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