1 1.1 christos /* 2 1.1 christos * This module implements printing of the very basic (version-independent) 3 1.1 christos * OpenFlow header and iteration over OpenFlow messages. It is intended for 4 1.1 christos * dispatching of version-specific OpenFlow message decoding. 5 1.1 christos * 6 1.1 christos * 7 1.1 christos * Copyright (c) 2013 The TCPDUMP project 8 1.1 christos * All rights reserved. 9 1.1 christos * 10 1.1 christos * Redistribution and use in source and binary forms, with or without 11 1.1 christos * modification, are permitted provided that the following conditions 12 1.1 christos * are met: 13 1.1 christos * 1. Redistributions of source code must retain the above copyright 14 1.1 christos * notice, this list of conditions and the following disclaimer. 15 1.1 christos * 2. Redistributions in binary form must reproduce the above copyright 16 1.1 christos * notice, this list of conditions and the following disclaimer in the 17 1.1 christos * documentation and/or other materials provided with the distribution. 18 1.1 christos * 19 1.1 christos * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 1.1 christos * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 1.1 christos * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 22 1.1 christos * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 23 1.1 christos * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 24 1.1 christos * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 25 1.1 christos * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 26 1.1 christos * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 1.1 christos * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 1.1 christos * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 29 1.1 christos * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 1.1 christos * POSSIBILITY OF SUCH DAMAGE. 31 1.1 christos */ 32 1.1 christos 33 1.2 christos #include <sys/cdefs.h> 34 1.2 christos #ifndef lint 35 1.6 christos __RCSID("$NetBSD: print-openflow.c,v 1.6 2024/09/02 16:15:32 christos Exp $"); 36 1.2 christos #endif 37 1.2 christos 38 1.3 spz /* \summary: version-independent OpenFlow printer */ 39 1.3 spz 40 1.5 christos #include <config.h> 41 1.1 christos 42 1.5 christos #include "netdissect-stdinc.h" 43 1.1 christos 44 1.5 christos #define ND_LONGJMP_FROM_TCHECK 45 1.2 christos #include "netdissect.h" 46 1.1 christos #include "extract.h" 47 1.1 christos #include "openflow.h" 48 1.2 christos #include "oui.h" 49 1.2 christos 50 1.1 christos 51 1.5 christos static const struct tok ofver_str[] = { 52 1.5 christos { OF_VER_1_0, "1.0" }, 53 1.5 christos { OF_VER_1_1, "1.1" }, 54 1.5 christos { OF_VER_1_2, "1.2" }, 55 1.5 christos { OF_VER_1_3, "1.3" }, 56 1.5 christos { OF_VER_1_4, "1.4" }, 57 1.5 christos { OF_VER_1_5, "1.5" }, 58 1.5 christos { 0, NULL } 59 1.5 christos }; 60 1.1 christos 61 1.2 christos const struct tok onf_exp_str[] = { 62 1.2 christos { ONF_EXP_ONF, "ONF Extensions" }, 63 1.2 christos { ONF_EXP_BUTE, "Budapest University of Technology and Economics" }, 64 1.2 christos { ONF_EXP_NOVIFLOW, "NoviFlow" }, 65 1.2 christos { ONF_EXP_L3, "L3+ Extensions, Vendor Neutral" }, 66 1.2 christos { ONF_EXP_L4L7, "L4-L7 Extensions" }, 67 1.2 christos { ONF_EXP_WMOB, "Wireless and Mobility Extensions" }, 68 1.2 christos { ONF_EXP_FABS, "Forwarding Abstractions Extensions" }, 69 1.2 christos { ONF_EXP_OTRANS, "Optical Transport Extensions" }, 70 1.5 christos { ONF_EXP_NBLNCTU, "Network Benchmarking Lab, NCTU" }, 71 1.5 christos { ONF_EXP_MPCE, "Mobile Packet Core Extensions" }, 72 1.5 christos { ONF_EXP_MPLSTPSPTN, "MPLS-TP OpenFlow Extensions for SPTN" }, 73 1.2 christos { 0, NULL } 74 1.2 christos }; 75 1.2 christos 76 1.2 christos const char * 77 1.2 christos of_vendor_name(const uint32_t vendor) 78 1.2 christos { 79 1.2 christos const struct tok *table = (vendor & 0xff000000) == 0 ? oui_values : onf_exp_str; 80 1.2 christos return tok2str(table, "unknown", vendor); 81 1.2 christos } 82 1.2 christos 83 1.5 christos void 84 1.5 christos of_bitmap_print(netdissect_options *ndo, 85 1.5 christos const struct tok *t, const uint32_t v, const uint32_t u) 86 1.5 christos { 87 1.5 christos /* Assigned bits? */ 88 1.5 christos if (v & ~u) 89 1.5 christos ND_PRINT(" (%s)", bittok2str(t, "", v)); 90 1.5 christos /* Unassigned bits? */ 91 1.5 christos if (v & u) 92 1.5 christos ND_PRINT(" (bogus)"); 93 1.5 christos } 94 1.5 christos 95 1.5 christos void 96 1.5 christos of_data_print(netdissect_options *ndo, 97 1.5 christos const u_char *cp, const u_int len) 98 1.2 christos { 99 1.5 christos if (len == 0) 100 1.5 christos return; 101 1.5 christos /* data */ 102 1.5 christos ND_PRINT("\n\t data (%u octets)", len); 103 1.5 christos if (ndo->ndo_vflag >= 2) 104 1.5 christos hex_and_ascii_print(ndo, "\n\t ", cp, len); 105 1.5 christos else 106 1.5 christos ND_TCHECK_LEN(cp, len); 107 1.1 christos } 108 1.1 christos 109 1.5 christos static void 110 1.5 christos of_message_print(netdissect_options *ndo, 111 1.5 christos const u_char *cp, uint16_t len, 112 1.5 christos const struct of_msgtypeinfo *mti) 113 1.2 christos { 114 1.5 christos /* 115 1.5 christos * Here "cp" and "len" stand for the message part beyond the common 116 1.5 christos * OpenFlow 1.0 header, if any. 117 1.5 christos * 118 1.5 christos * Most message types are longer than just the header, and the length 119 1.5 christos * constraints may be complex. When possible, validate the constraint 120 1.5 christos * completely here (REQ_FIXLEN), otherwise check that the message is 121 1.5 christos * long enough to begin the decoding (REQ_MINLEN) and have the 122 1.5 christos * type-specific function do any remaining validation. 123 1.5 christos */ 124 1.1 christos 125 1.5 christos if (!mti) 126 1.5 christos goto tcheck_remainder; 127 1.5 christos 128 1.5 christos if ((mti->req_what == REQ_FIXLEN && len != mti->req_value) || 129 1.5 christos (mti->req_what == REQ_MINLEN && len < mti->req_value)) 130 1.2 christos goto invalid; 131 1.5 christos 132 1.5 christos if (!ndo->ndo_vflag || !mti->decoder) 133 1.5 christos goto tcheck_remainder; 134 1.5 christos 135 1.5 christos mti->decoder(ndo, cp, len); 136 1.5 christos return; 137 1.5 christos 138 1.5 christos invalid: 139 1.5 christos nd_print_invalid(ndo); 140 1.5 christos tcheck_remainder: 141 1.5 christos ND_TCHECK_LEN(cp, len); 142 1.1 christos } 143 1.1 christos 144 1.1 christos /* Print a TCP segment worth of OpenFlow messages presuming the segment begins 145 1.1 christos * on a message boundary. */ 146 1.1 christos void 147 1.5 christos openflow_print(netdissect_options *ndo, const u_char *cp, u_int len) 148 1.2 christos { 149 1.5 christos ndo->ndo_protocol = "openflow"; 150 1.5 christos ND_PRINT(": OpenFlow"); 151 1.5 christos while (len) { 152 1.5 christos /* Print a single OpenFlow message. */ 153 1.5 christos uint8_t version, type; 154 1.5 christos uint16_t length; 155 1.5 christos const struct of_msgtypeinfo *mti; 156 1.5 christos 157 1.5 christos /* version */ 158 1.5 christos version = GET_U_1(cp); 159 1.5 christos OF_FWD(1); 160 1.5 christos ND_PRINT("\n\tversion %s", 161 1.5 christos tok2str(ofver_str, "unknown (0x%02x)", version)); 162 1.5 christos /* type */ 163 1.5 christos if (len < 1) 164 1.5 christos goto partial_header; 165 1.5 christos type = GET_U_1(cp); 166 1.5 christos OF_FWD(1); 167 1.5 christos mti = 168 1.5 christos version == OF_VER_1_0 ? of10_identify_msgtype(type) : 169 1.5 christos version == OF_VER_1_3 ? of13_identify_msgtype(type) : 170 1.5 christos NULL; 171 1.5 christos if (mti && mti->name) 172 1.5 christos ND_PRINT(", type %s", mti->name); 173 1.5 christos else 174 1.5 christos ND_PRINT(", type unknown (0x%02x)", type); 175 1.5 christos /* length */ 176 1.5 christos if (len < 2) 177 1.5 christos goto partial_header; 178 1.5 christos length = GET_BE_U_2(cp); 179 1.5 christos OF_FWD(2); 180 1.5 christos ND_PRINT(", length %u%s", length, 181 1.5 christos length < OF_HEADER_FIXLEN ? " (too short!)" : ""); 182 1.5 christos /* xid */ 183 1.5 christos if (len < 4) 184 1.5 christos goto partial_header; 185 1.5 christos ND_PRINT(", xid 0x%08x", GET_BE_U_4(cp)); 186 1.5 christos OF_FWD(4); 187 1.5 christos 188 1.5 christos /* 189 1.5 christos * When a TCP packet can contain several protocol messages, 190 1.5 christos * and at the same time a protocol message can span several 191 1.5 christos * TCP packets, decoding an incomplete message at the end of 192 1.5 christos * a TCP packet requires attention to detail in this loop. 193 1.5 christos * 194 1.5 christos * Message length includes the header length and a message 195 1.5 christos * always includes the basic header. A message length underrun 196 1.5 christos * fails decoding of the rest of the current packet. At the 197 1.5 christos * same time, try decoding as much of the current message as 198 1.5 christos * possible even when it does not end within the current TCP 199 1.5 christos * segment. 200 1.5 christos * 201 1.5 christos * Specifically, to try to process the message body in this 202 1.5 christos * iteration do NOT require the header "length" to be small 203 1.5 christos * enough for the full declared OpenFlow message to fit into 204 1.5 christos * the remainder of the declared TCP segment, same as the full 205 1.5 christos * declared TCP segment is not required to fit into the 206 1.5 christos * captured packet buffer. 207 1.5 christos * 208 1.5 christos * But DO require the same at the end of this iteration to 209 1.5 christos * decrement "len" and to proceed to the next iteration. 210 1.5 christos * (Ideally the declared TCP payload end will be at or after 211 1.5 christos * the captured packet buffer end, but stay safe even when 212 1.5 christos * that's somehow not the case.) 213 1.5 christos */ 214 1.5 christos if (length < OF_HEADER_FIXLEN) 215 1.5 christos goto invalid; 216 1.5 christos 217 1.5 christos of_message_print(ndo, cp, length - OF_HEADER_FIXLEN, mti); 218 1.5 christos if (length - OF_HEADER_FIXLEN > len) 219 1.5 christos break; 220 1.5 christos OF_FWD(length - OF_HEADER_FIXLEN); 221 1.5 christos } /* while (len) */ 222 1.5 christos return; 223 1.5 christos 224 1.5 christos partial_header: 225 1.5 christos ND_PRINT(" (end of TCP payload)"); 226 1.5 christos ND_TCHECK_LEN(cp, len); 227 1.5 christos return; 228 1.5 christos invalid: /* fail the current packet */ 229 1.5 christos nd_print_invalid(ndo); 230 1.5 christos ND_TCHECK_LEN(cp, len); 231 1.1 christos } 232