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