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
33/* \summary: version-independent OpenFlow printer */
34
35#ifdef HAVE_CONFIG_H
36#include <config.h>
37#endif
38
39#include "netdissect-stdinc.h"
40
41#define ND_LONGJMP_FROM_TCHECK
42#include "netdissect.h"
43#include "extract.h"
44#include "openflow.h"
45#include "oui.h"
46
47
48static 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};
57
58const 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"                    },
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"            },
70	{ 0, NULL }
71};
72
73const char *
74of_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
80void
81of_bitmap_print(netdissect_options *ndo,
82                const struct tok *t, const uint32_t v, const uint32_t u)
83{
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)");
90}
91
92void
93of_data_print(netdissect_options *ndo,
94              const u_char *cp, const u_int len)
95{
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}
105
106static void
107of_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	 */
121
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
135invalid:
136	nd_print_invalid(ndo);
137tcheck_remainder:
138	ND_TCHECK_LEN(cp, len);
139}
140
141/* Print a TCP segment worth of OpenFlow messages presuming the segment begins
142 * on a message boundary. */
143void
144openflow_print(netdissect_options *ndo, const u_char *cp, u_int len)
145{
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;
153
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
221partial_header:
222	ND_PRINT(" (end of TCP payload)");
223	ND_TCHECK_LEN(cp, len);
224	return;
225invalid: /* fail the current packet */
226	nd_print_invalid(ndo);
227	ND_TCHECK_LEN(cp, len);
228}
229