diff options
Diffstat (limited to 'print-openflow.c')
-rw-r--r-- | print-openflow.c | 228 |
1 files changed, 228 insertions, 0 deletions
diff --git a/print-openflow.c b/print-openflow.c new file mode 100644 index 0000000..6024a21 --- /dev/null +++ b/print-openflow.c @@ -0,0 +1,228 @@ +/* + * This module implements printing of the very basic (version-independent) + * OpenFlow header and iteration over OpenFlow messages. It is intended for + * dispatching of version-specific OpenFlow message decoding. + * + * + * Copyright (c) 2013 The TCPDUMP project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* \summary: version-independent OpenFlow printer */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "netdissect-stdinc.h" + +#define ND_LONGJMP_FROM_TCHECK +#include "netdissect.h" +#include "extract.h" +#include "openflow.h" +#include "oui.h" + + +static const struct tok ofver_str[] = { + { OF_VER_1_0, "1.0" }, + { OF_VER_1_1, "1.1" }, + { OF_VER_1_2, "1.2" }, + { OF_VER_1_3, "1.3" }, + { OF_VER_1_4, "1.4" }, + { OF_VER_1_5, "1.5" }, + { 0, NULL } +}; + +const struct tok onf_exp_str[] = { + { ONF_EXP_ONF, "ONF Extensions" }, + { ONF_EXP_BUTE, "Budapest University of Technology and Economics" }, + { ONF_EXP_NOVIFLOW, "NoviFlow" }, + { ONF_EXP_L3, "L3+ Extensions, Vendor Neutral" }, + { ONF_EXP_L4L7, "L4-L7 Extensions" }, + { ONF_EXP_WMOB, "Wireless and Mobility Extensions" }, + { ONF_EXP_FABS, "Forwarding Abstractions Extensions" }, + { ONF_EXP_OTRANS, "Optical Transport Extensions" }, + { ONF_EXP_NBLNCTU, "Network Benchmarking Lab, NCTU" }, + { ONF_EXP_MPCE, "Mobile Packet Core Extensions" }, + { ONF_EXP_MPLSTPSPTN, "MPLS-TP OpenFlow Extensions for SPTN" }, + { 0, NULL } +}; + +const char * +of_vendor_name(const uint32_t vendor) +{ + const struct tok *table = (vendor & 0xff000000) == 0 ? oui_values : onf_exp_str; + return tok2str(table, "unknown", vendor); +} + +void +of_bitmap_print(netdissect_options *ndo, + const struct tok *t, const uint32_t v, const uint32_t u) +{ + /* Assigned bits? */ + if (v & ~u) + ND_PRINT(" (%s)", bittok2str(t, "", v)); + /* Unassigned bits? */ + if (v & u) + ND_PRINT(" (bogus)"); +} + +void +of_data_print(netdissect_options *ndo, + const u_char *cp, const u_int len) +{ + if (len == 0) + return; + /* data */ + ND_PRINT("\n\t data (%u octets)", len); + if (ndo->ndo_vflag >= 2) + hex_and_ascii_print(ndo, "\n\t ", cp, len); + else + ND_TCHECK_LEN(cp, len); +} + +static void +of_message_print(netdissect_options *ndo, + const u_char *cp, uint16_t len, + const struct of_msgtypeinfo *mti) +{ + /* + * Here "cp" and "len" stand for the message part beyond the common + * OpenFlow 1.0 header, if any. + * + * Most message types are longer than just the header, and the length + * constraints may be complex. When possible, validate the constraint + * completely here (REQ_FIXLEN), otherwise check that the message is + * long enough to begin the decoding (REQ_MINLEN) and have the + * type-specific function do any remaining validation. + */ + + if (!mti) + goto tcheck_remainder; + + if ((mti->req_what == REQ_FIXLEN && len != mti->req_value) || + (mti->req_what == REQ_MINLEN && len < mti->req_value)) + goto invalid; + + if (!ndo->ndo_vflag || !mti->decoder) + goto tcheck_remainder; + + mti->decoder(ndo, cp, len); + return; + +invalid: + nd_print_invalid(ndo); +tcheck_remainder: + ND_TCHECK_LEN(cp, len); +} + +/* Print a TCP segment worth of OpenFlow messages presuming the segment begins + * on a message boundary. */ +void +openflow_print(netdissect_options *ndo, const u_char *cp, u_int len) +{ + ndo->ndo_protocol = "openflow"; + ND_PRINT(": OpenFlow"); + while (len) { + /* Print a single OpenFlow message. */ + uint8_t version, type; + uint16_t length; + const struct of_msgtypeinfo *mti; + + /* version */ + version = GET_U_1(cp); + OF_FWD(1); + ND_PRINT("\n\tversion %s", + tok2str(ofver_str, "unknown (0x%02x)", version)); + /* type */ + if (len < 1) + goto partial_header; + type = GET_U_1(cp); + OF_FWD(1); + mti = + version == OF_VER_1_0 ? of10_identify_msgtype(type) : + version == OF_VER_1_3 ? of13_identify_msgtype(type) : + NULL; + if (mti && mti->name) + ND_PRINT(", type %s", mti->name); + else + ND_PRINT(", type unknown (0x%02x)", type); + /* length */ + if (len < 2) + goto partial_header; + length = GET_BE_U_2(cp); + OF_FWD(2); + ND_PRINT(", length %u%s", length, + length < OF_HEADER_FIXLEN ? " (too short!)" : ""); + /* xid */ + if (len < 4) + goto partial_header; + ND_PRINT(", xid 0x%08x", GET_BE_U_4(cp)); + OF_FWD(4); + + /* + * When a TCP packet can contain several protocol messages, + * and at the same time a protocol message can span several + * TCP packets, decoding an incomplete message at the end of + * a TCP packet requires attention to detail in this loop. + * + * Message length includes the header length and a message + * always includes the basic header. A message length underrun + * fails decoding of the rest of the current packet. At the + * same time, try decoding as much of the current message as + * possible even when it does not end within the current TCP + * segment. + * + * Specifically, to try to process the message body in this + * iteration do NOT require the header "length" to be small + * enough for the full declared OpenFlow message to fit into + * the remainder of the declared TCP segment, same as the full + * declared TCP segment is not required to fit into the + * captured packet buffer. + * + * But DO require the same at the end of this iteration to + * decrement "len" and to proceed to the next iteration. + * (Ideally the declared TCP payload end will be at or after + * the captured packet buffer end, but stay safe even when + * that's somehow not the case.) + */ + if (length < OF_HEADER_FIXLEN) + goto invalid; + + of_message_print(ndo, cp, length - OF_HEADER_FIXLEN, mti); + if (length - OF_HEADER_FIXLEN > len) + break; + OF_FWD(length - OF_HEADER_FIXLEN); + } /* while (len) */ + return; + +partial_header: + ND_PRINT(" (end of TCP payload)"); + ND_TCHECK_LEN(cp, len); + return; +invalid: /* fail the current packet */ + nd_print_invalid(ndo); + ND_TCHECK_LEN(cp, len); +} |