1 |
|
|
/* $OpenBSD: print-ofp.c,v 1.11 2016/11/28 17:47:15 jca Exp $ */ |
2 |
|
|
|
3 |
|
|
/* |
4 |
|
|
* Copyright (c) 2016 Rafael Zalamena <rzalamena@openbsd.org> |
5 |
|
|
* |
6 |
|
|
* Permission to use, copy, modify, and distribute this software for any |
7 |
|
|
* purpose with or without fee is hereby granted, provided that the above |
8 |
|
|
* copyright notice and this permission notice appear in all copies. |
9 |
|
|
* |
10 |
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
11 |
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
12 |
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
13 |
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
14 |
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
15 |
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
16 |
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
17 |
|
|
*/ |
18 |
|
|
|
19 |
|
|
#include <net/ofp.h> |
20 |
|
|
|
21 |
|
|
#include <endian.h> |
22 |
|
|
#include <stddef.h> |
23 |
|
|
#include <stdio.h> |
24 |
|
|
#include <string.h> |
25 |
|
|
#include <pcap.h> |
26 |
|
|
|
27 |
|
|
#include "addrtoname.h" |
28 |
|
|
#include "extract.h" |
29 |
|
|
#include "interface.h" |
30 |
|
|
#include "ofp_map.h" |
31 |
|
|
|
32 |
|
|
/* Size of action header without the padding. */ |
33 |
|
|
#define AH_UNPADDED (offsetof(struct ofp_action_header, ah_pad)) |
34 |
|
|
|
35 |
|
|
const char * |
36 |
|
|
print_map(unsigned int, struct constmap *); |
37 |
|
|
|
38 |
|
|
void ofp_print_hello(const u_char *, u_int, u_int); |
39 |
|
|
void ofp_print_featuresreply(const u_char *, u_int); |
40 |
|
|
void ofp_print_setconfig(const u_char *, u_int); |
41 |
|
|
void ofp_print_packetin(const u_char *, u_int); |
42 |
|
|
void ofp_print_packetout(const u_char *, u_int); |
43 |
|
|
void ofp_print_flowremoved(const u_char *, u_int); |
44 |
|
|
void ofp_print_flowmod(const u_char *, u_int); |
45 |
|
|
|
46 |
|
|
void oxm_print_halfword(const u_char *, u_int, int, int); |
47 |
|
|
void oxm_print_word(const u_char *, u_int, int, int); |
48 |
|
|
void oxm_print_quad(const u_char *, u_int, int, int); |
49 |
|
|
void oxm_print_ether(const u_char *, u_int, int); |
50 |
|
|
void ofp_print_oxm(struct ofp_ox_match *, const u_char *, u_int); |
51 |
|
|
|
52 |
|
|
void action_print_output(const u_char *, u_int); |
53 |
|
|
void action_print_group(const u_char *, u_int); |
54 |
|
|
void action_print_setqueue(const u_char *, u_int); |
55 |
|
|
void action_print_setmplsttl(const u_char *, u_int); |
56 |
|
|
void action_print_setnwttl(const u_char *, u_int); |
57 |
|
|
void action_print_push(const u_char *, u_int); |
58 |
|
|
void action_print_popmpls(const u_char *, u_int); |
59 |
|
|
void action_print_setfield(const u_char *, u_int); |
60 |
|
|
void ofp_print_action(struct ofp_action_header *, const u_char *, |
61 |
|
|
u_int); |
62 |
|
|
|
63 |
|
|
void instruction_print_gototable(const char *, u_int); |
64 |
|
|
void instruction_print_meta(const char *, u_int); |
65 |
|
|
void instruction_print_actions(const char *, u_int); |
66 |
|
|
void instruction_print_meter(const char *, u_int); |
67 |
|
|
void instruction_print_experimenter(const char *, u_int); |
68 |
|
|
void ofp_print_instruction(struct ofp_instruction *, const char *, u_int); |
69 |
|
|
|
70 |
|
|
const char * |
71 |
|
|
print_map(unsigned int type, struct constmap *map) |
72 |
|
|
{ |
73 |
|
|
unsigned int i; |
74 |
|
|
#define CYCLE_BUFFERS 8 |
75 |
|
|
static char buf[CYCLE_BUFFERS][32]; |
76 |
|
|
static int idx = 0; |
77 |
|
|
const char *name = NULL; |
78 |
|
|
|
79 |
|
|
if (idx >= CYCLE_BUFFERS) |
80 |
|
|
idx = 0; |
81 |
|
|
memset(buf[idx], 0, sizeof(buf[idx])); |
82 |
|
|
|
83 |
|
|
for (i = 0; map[i].cm_name != NULL; i++) { |
84 |
|
|
if (map[i].cm_type == type) { |
85 |
|
|
name = map[i].cm_name; |
86 |
|
|
break; |
87 |
|
|
} |
88 |
|
|
} |
89 |
|
|
|
90 |
|
|
if (name == NULL) |
91 |
|
|
snprintf(buf[idx], sizeof(buf[idx]), "%u", type); |
92 |
|
|
else if (vflag > 1) |
93 |
|
|
snprintf(buf[idx], sizeof(buf[idx]), "%s[%u]", name, type); |
94 |
|
|
else |
95 |
|
|
strlcpy(buf[idx], name, sizeof(buf[idx])); |
96 |
|
|
|
97 |
|
|
return (buf[idx++]); |
98 |
|
|
} |
99 |
|
|
|
100 |
|
|
void |
101 |
|
|
ofp_print_hello(const u_char *bp, u_int length, u_int ohlen) |
102 |
|
|
{ |
103 |
|
|
struct ofp_hello_element_header *he; |
104 |
|
|
int hetype, helen, ver, i; |
105 |
|
|
int vmajor, vminor; |
106 |
|
|
uint32_t bmp; |
107 |
|
|
|
108 |
|
|
/* Skip the OFP header. */ |
109 |
|
|
bp += sizeof(struct ofp_header); |
110 |
|
|
length -= sizeof(struct ofp_header); |
111 |
|
|
|
112 |
|
|
/* Check for header truncation. */ |
113 |
|
|
if (ohlen > sizeof(struct ofp_header) && |
114 |
|
|
length < sizeof(*he)) { |
115 |
|
|
printf(" [|OpenFlow]"); |
116 |
|
|
return; |
117 |
|
|
} |
118 |
|
|
|
119 |
|
|
next_header: |
120 |
|
|
/* Check for hello element headers. */ |
121 |
|
|
if (length < sizeof(*he)) |
122 |
|
|
return; |
123 |
|
|
|
124 |
|
|
he = (struct ofp_hello_element_header *)bp; |
125 |
|
|
hetype = ntohs(he->he_type); |
126 |
|
|
helen = ntohs(he->he_length); |
127 |
|
|
|
128 |
|
|
bp += sizeof(*he); |
129 |
|
|
length -= sizeof(*he); |
130 |
|
|
helen -= sizeof(*he); |
131 |
|
|
|
132 |
|
|
switch (hetype) { |
133 |
|
|
case OFP_HELLO_T_VERSION_BITMAP: |
134 |
|
|
printf(" version bitmap <"); |
135 |
|
|
if (helen < sizeof(bmp)) { |
136 |
|
|
printf("invalid header>"); |
137 |
|
|
break; |
138 |
|
|
} |
139 |
|
|
|
140 |
|
|
next_bitmap: |
141 |
|
|
if (length < sizeof(bmp)) { |
142 |
|
|
printf("[|OpenFlow]>"); |
143 |
|
|
break; |
144 |
|
|
} |
145 |
|
|
|
146 |
|
|
bmp = ntohl(*(uint32_t *)bp); |
147 |
|
|
for (i = 0, ver = 9; i < 32; i++, ver++) { |
148 |
|
|
if ((bmp & (1 << i)) == 0) |
149 |
|
|
continue; |
150 |
|
|
|
151 |
|
|
vmajor = (ver / 10); |
152 |
|
|
vminor = ver % 10; |
153 |
|
|
printf("v%d.%d ", vmajor, vminor); |
154 |
|
|
} |
155 |
|
|
helen -= min(sizeof(bmp), helen); |
156 |
|
|
length -= sizeof(bmp); |
157 |
|
|
bp += sizeof(bmp); |
158 |
|
|
if (helen) |
159 |
|
|
goto next_bitmap; |
160 |
|
|
|
161 |
|
|
printf("\b>"); |
162 |
|
|
break; |
163 |
|
|
|
164 |
|
|
default: |
165 |
|
|
printf(" element header[type %d length %d]", hetype, helen); |
166 |
|
|
break; |
167 |
|
|
} |
168 |
|
|
|
169 |
|
|
length -= min(helen, length); |
170 |
|
|
bp += helen; |
171 |
|
|
if (length) |
172 |
|
|
goto next_header; |
173 |
|
|
} |
174 |
|
|
|
175 |
|
|
void |
176 |
|
|
ofp_print_error(const u_char *bp, u_int length) |
177 |
|
|
{ |
178 |
|
|
struct ofp_error *err; |
179 |
|
|
|
180 |
|
|
if (length < sizeof(*err)) { |
181 |
|
|
printf(" [|OpenFlow]"); |
182 |
|
|
return; |
183 |
|
|
} |
184 |
|
|
|
185 |
|
|
err = (struct ofp_error *)bp; |
186 |
|
|
printf(" <type %s code %d>", |
187 |
|
|
print_map(ntohs(err->err_type), ofp_errtype_map), |
188 |
|
|
ntohs(err->err_code)); |
189 |
|
|
|
190 |
|
|
length -= min(sizeof(*err), length); |
191 |
|
|
bp += sizeof(*err); |
192 |
|
|
/* If there are still bytes left, print the optional error data. */ |
193 |
|
|
if (length) { |
194 |
|
|
printf(" error data"); |
195 |
|
|
ofp_print(bp, length); |
196 |
|
|
} |
197 |
|
|
} |
198 |
|
|
|
199 |
|
|
void |
200 |
|
|
ofp_print_featuresreply(const u_char *bp, u_int length) |
201 |
|
|
{ |
202 |
|
|
struct ofp_switch_features *swf; |
203 |
|
|
|
204 |
|
|
if (length < sizeof(*swf)) { |
205 |
|
|
printf(" [trucanted]"); |
206 |
|
|
return; |
207 |
|
|
} |
208 |
|
|
|
209 |
|
|
swf = (struct ofp_switch_features *)bp; |
210 |
|
|
printf(" <datapath_id %#016llx nbuffers %u ntables %d aux_id %d " |
211 |
|
|
"capabilities %#08x>", |
212 |
|
|
be64toh(swf->swf_datapath_id), ntohl(swf->swf_nbuffers), |
213 |
|
|
swf->swf_ntables, swf->swf_aux_id, |
214 |
|
|
ntohl(swf->swf_capabilities)); |
215 |
|
|
} |
216 |
|
|
|
217 |
|
|
void |
218 |
|
|
ofp_print_setconfig(const u_char *bp, u_int length) |
219 |
|
|
{ |
220 |
|
|
struct ofp_switch_config *cfg; |
221 |
|
|
|
222 |
|
|
if (length < sizeof(*cfg)) { |
223 |
|
|
printf(" [|OpenFlow]"); |
224 |
|
|
return; |
225 |
|
|
} |
226 |
|
|
|
227 |
|
|
cfg = (struct ofp_switch_config *)bp; |
228 |
|
|
printf(" <flags %#04x miss_send_len %s>", |
229 |
|
|
ntohs(cfg->cfg_flags), |
230 |
|
|
print_map(ntohs(cfg->cfg_miss_send_len), |
231 |
|
|
ofp_controller_maxlen_map)); |
232 |
|
|
} |
233 |
|
|
|
234 |
|
|
void |
235 |
|
|
ofp_print_packetin(const u_char *bp, u_int length) |
236 |
|
|
{ |
237 |
|
|
struct ofp_packet_in *pin; |
238 |
|
|
struct ofp_ox_match *oxm; |
239 |
|
|
int omtype, omlen; |
240 |
|
|
int haspacket = 0; |
241 |
|
|
const u_char *pktptr; |
242 |
|
|
|
243 |
|
|
if (length < sizeof(*pin)) { |
244 |
|
|
printf(" [|OpenFlow]"); |
245 |
|
|
return; |
246 |
|
|
} |
247 |
|
|
|
248 |
|
|
pin = (struct ofp_packet_in *)bp; |
249 |
|
|
omtype = ntohs(pin->pin_match.om_type); |
250 |
|
|
omlen = ntohs(pin->pin_match.om_length); |
251 |
|
|
printf(" <buffer_id %s total_len %d reason %s table_id %s " |
252 |
|
|
"cookie %#016llx match type %s length %d>", |
253 |
|
|
print_map(ntohl(pin->pin_buffer_id), ofp_pktout_map), |
254 |
|
|
ntohs(pin->pin_total_len), |
255 |
|
|
print_map(pin->pin_reason, ofp_pktin_reason_map), |
256 |
|
|
print_map(pin->pin_table_id, ofp_table_id_map), |
257 |
|
|
be64toh(pin->pin_cookie), |
258 |
|
|
print_map(omtype, ofp_match_map), omlen); |
259 |
|
|
|
260 |
|
|
if (pin->pin_buffer_id == OFP_PKTOUT_NO_BUFFER) |
261 |
|
|
haspacket = 1; |
262 |
|
|
|
263 |
|
|
/* We only support OXM. */ |
264 |
|
|
if (omtype != OFP_MATCH_OXM) |
265 |
|
|
return; |
266 |
|
|
|
267 |
|
|
bp += sizeof(*pin); |
268 |
|
|
length -= sizeof(*pin); |
269 |
|
|
|
270 |
|
|
/* Get packet start address. */ |
271 |
|
|
pktptr = (bp - sizeof(pin->pin_match)) + |
272 |
|
|
OFP_ALIGN(omlen) + ETHER_ALIGN; |
273 |
|
|
|
274 |
|
|
/* Don't count the header for the OXM fields. */ |
275 |
|
|
omlen -= min(sizeof(pin->pin_match), omlen); |
276 |
|
|
if (omlen == 0) |
277 |
|
|
goto print_packet; |
278 |
|
|
|
279 |
|
|
parse_next_oxm: |
280 |
|
|
if (length < sizeof(*oxm)) { |
281 |
|
|
printf(" [|OpenFlow]"); |
282 |
|
|
return; |
283 |
|
|
} |
284 |
|
|
|
285 |
|
|
oxm = (struct ofp_ox_match *)bp; |
286 |
|
|
bp += sizeof(*oxm); |
287 |
|
|
length -= sizeof(*oxm); |
288 |
|
|
if (length < oxm->oxm_length) { |
289 |
|
|
printf(" [|OpenFlow]"); |
290 |
|
|
return; |
291 |
|
|
} |
292 |
|
|
|
293 |
|
|
ofp_print_oxm(oxm, bp, length); |
294 |
|
|
|
295 |
|
|
bp += oxm->oxm_length; |
296 |
|
|
length -= oxm->oxm_length; |
297 |
|
|
omlen -= min(sizeof(*oxm) + oxm->oxm_length, omlen); |
298 |
|
|
if (omlen) |
299 |
|
|
goto parse_next_oxm; |
300 |
|
|
|
301 |
|
|
print_packet: |
302 |
|
|
if (haspacket == 0) |
303 |
|
|
return; |
304 |
|
|
|
305 |
|
|
/* |
306 |
|
|
* Recalculate length: |
307 |
|
|
* pktptr skipped the omlen + padding and the ETHER_ALIGN, so |
308 |
|
|
* instead of keeping track of that we just recalculate length |
309 |
|
|
* using the encapsulated packet begin and snapend. |
310 |
|
|
*/ |
311 |
|
|
length = max(snapend - pktptr, 0); |
312 |
|
|
if (length < ETHER_ADDR_LEN) { |
313 |
|
|
printf(" [|ether]"); |
314 |
|
|
return; |
315 |
|
|
} |
316 |
|
|
|
317 |
|
|
printf(" "); |
318 |
|
|
ether_tryprint(pktptr, length, 0); |
319 |
|
|
} |
320 |
|
|
|
321 |
|
|
void |
322 |
|
|
ofp_print_flowremoved(const u_char *bp, u_int length) |
323 |
|
|
{ |
324 |
|
|
struct ofp_flow_removed *fr; |
325 |
|
|
struct ofp_ox_match *oxm; |
326 |
|
|
int omtype, omlen; |
327 |
|
|
|
328 |
|
|
if (length < sizeof(*fr)) { |
329 |
|
|
printf(" [|OpenFlow]"); |
330 |
|
|
return; |
331 |
|
|
} |
332 |
|
|
|
333 |
|
|
fr = (struct ofp_flow_removed *)bp; |
334 |
|
|
omtype = ntohs(fr->fr_match.om_type); |
335 |
|
|
omlen = ntohs(fr->fr_match.om_length); |
336 |
|
|
printf(" <cookie %#016llx priority %d reason %s table_id %s " |
337 |
|
|
"duration sec %u nsec %u timeout idle %d hard %d " |
338 |
|
|
"packet count %llu byte count %llu match type %s length %d>", |
339 |
|
|
be64toh(fr->fr_cookie), ntohs(fr->fr_priority), |
340 |
|
|
print_map(fr->fr_reason, ofp_flowrem_reason_map), |
341 |
|
|
print_map(fr->fr_table_id, ofp_table_id_map), |
342 |
|
|
ntohl(fr->fr_duration_sec), ntohl(fr->fr_duration_nsec), |
343 |
|
|
ntohs(fr->fr_idle_timeout), ntohs(fr->fr_hard_timeout), |
344 |
|
|
be64toh(fr->fr_packet_count), be64toh(fr->fr_byte_count), |
345 |
|
|
print_map(omtype, ofp_match_map), omlen); |
346 |
|
|
|
347 |
|
|
/* We only support OXM. */ |
348 |
|
|
if (omtype != OFP_MATCH_OXM) |
349 |
|
|
return; |
350 |
|
|
|
351 |
|
|
omlen -= min(sizeof(fr->fr_match), omlen); |
352 |
|
|
if (omlen == 0) |
353 |
|
|
return; |
354 |
|
|
|
355 |
|
|
bp += sizeof(*fr); |
356 |
|
|
length -= sizeof(*fr); |
357 |
|
|
|
358 |
|
|
parse_next_oxm: |
359 |
|
|
if (length < sizeof(*oxm)) { |
360 |
|
|
printf(" [|OpenFlow]"); |
361 |
|
|
return; |
362 |
|
|
} |
363 |
|
|
|
364 |
|
|
oxm = (struct ofp_ox_match *)bp; |
365 |
|
|
bp += sizeof(*oxm); |
366 |
|
|
length -= sizeof(*oxm); |
367 |
|
|
if (length < oxm->oxm_length) { |
368 |
|
|
printf(" [|OpenFlow]"); |
369 |
|
|
return; |
370 |
|
|
} |
371 |
|
|
|
372 |
|
|
ofp_print_oxm(oxm, bp, length); |
373 |
|
|
|
374 |
|
|
bp += oxm->oxm_length; |
375 |
|
|
length -= oxm->oxm_length; |
376 |
|
|
omlen -= min(sizeof(*oxm) + oxm->oxm_length, omlen); |
377 |
|
|
if (omlen) |
378 |
|
|
goto parse_next_oxm; |
379 |
|
|
} |
380 |
|
|
|
381 |
|
|
void |
382 |
|
|
ofp_print_packetout(const u_char *bp, u_int length) |
383 |
|
|
{ |
384 |
|
|
struct ofp_packet_out *pout; |
385 |
|
|
struct ofp_action_header *ah; |
386 |
|
|
const u_char *pktptr; |
387 |
|
|
int actionslen, haspacket = 0; |
388 |
|
|
int ahlen; |
389 |
|
|
|
390 |
|
|
if (length < sizeof(*pout)) { |
391 |
|
|
printf(" [|OpenFlow]"); |
392 |
|
|
return; |
393 |
|
|
} |
394 |
|
|
|
395 |
|
|
pout = (struct ofp_packet_out *)bp; |
396 |
|
|
actionslen = ntohs(pout->pout_actions_len); |
397 |
|
|
printf(" <buffer_id %s in_port %s actions_len %d>", |
398 |
|
|
print_map(ntohl(pout->pout_buffer_id), ofp_pktout_map), |
399 |
|
|
print_map(ntohl(pout->pout_in_port), ofp_port_map), |
400 |
|
|
actionslen); |
401 |
|
|
|
402 |
|
|
if (pout->pout_buffer_id == OFP_PKTOUT_NO_BUFFER) |
403 |
|
|
haspacket = 1; |
404 |
|
|
|
405 |
|
|
bp += sizeof(*pout); |
406 |
|
|
length -= sizeof(*pout); |
407 |
|
|
pktptr = bp + actionslen; |
408 |
|
|
|
409 |
|
|
/* No actions or unpadded header. */ |
410 |
|
|
if (actionslen < sizeof(*ah)) |
411 |
|
|
goto print_packet; |
412 |
|
|
|
413 |
|
|
parse_next_action: |
414 |
|
|
if (length < sizeof(*ah)) { |
415 |
|
|
printf(" [|OpenFlow]"); |
416 |
|
|
return; |
417 |
|
|
} |
418 |
|
|
|
419 |
|
|
ah = (struct ofp_action_header *)bp; |
420 |
|
|
bp += AH_UNPADDED; |
421 |
|
|
length -= AH_UNPADDED; |
422 |
|
|
actionslen -= AH_UNPADDED; |
423 |
|
|
ahlen = ntohs(ah->ah_len) - AH_UNPADDED; |
424 |
|
|
if (length < ahlen) { |
425 |
|
|
printf(" [|OpenFlow]"); |
426 |
|
|
return; |
427 |
|
|
} |
428 |
|
|
|
429 |
|
|
ofp_print_action(ah, bp, length); |
430 |
|
|
|
431 |
|
|
bp += ahlen; |
432 |
|
|
length -= ahlen; |
433 |
|
|
actionslen -= min(ahlen, actionslen); |
434 |
|
|
if (actionslen) |
435 |
|
|
goto parse_next_action; |
436 |
|
|
|
437 |
|
|
print_packet: |
438 |
|
|
if (haspacket == 0) |
439 |
|
|
return; |
440 |
|
|
|
441 |
|
|
/* Recalculate length using packet boundaries. */ |
442 |
|
|
length = max(snapend - pktptr, 0); |
443 |
|
|
if (length < ETHER_ADDR_LEN) { |
444 |
|
|
printf(" [|ether]"); |
445 |
|
|
return; |
446 |
|
|
} |
447 |
|
|
|
448 |
|
|
printf(" "); |
449 |
|
|
ether_tryprint(pktptr, length, 0); |
450 |
|
|
} |
451 |
|
|
|
452 |
|
|
void |
453 |
|
|
ofp_print_flowmod(const u_char *bp, u_int length) |
454 |
|
|
{ |
455 |
|
|
struct ofp_flow_mod *fm; |
456 |
|
|
struct ofp_ox_match *oxm; |
457 |
|
|
struct ofp_instruction *i; |
458 |
|
|
int omtype, omlen, ilen; |
459 |
|
|
int instructionslen, padsize; |
460 |
|
|
|
461 |
|
|
if (length < sizeof(*fm)) { |
462 |
|
|
printf(" [|OpenFlow]"); |
463 |
|
|
return; |
464 |
|
|
} |
465 |
|
|
|
466 |
|
|
fm = (struct ofp_flow_mod *)bp; |
467 |
|
|
omtype = ntohs(fm->fm_match.om_type); |
468 |
|
|
omlen = ntohs(fm->fm_match.om_length); |
469 |
|
|
printf(" <cookie %llu cookie_mask %#016llx table_id %s command %s " |
470 |
|
|
"timeout idle %d hard %d priority %d buffer_id %s out_port %s " |
471 |
|
|
"out_group %s flags %#04x match type %s length %d>", |
472 |
|
|
be64toh(fm->fm_cookie), be64toh(fm->fm_cookie_mask), |
473 |
|
|
print_map(fm->fm_table_id, ofp_table_id_map), |
474 |
|
|
print_map(fm->fm_command, ofp_flowcmd_map), |
475 |
|
|
ntohs(fm->fm_idle_timeout), ntohs(fm->fm_hard_timeout), |
476 |
|
|
fm->fm_priority, |
477 |
|
|
print_map(ntohl(fm->fm_buffer_id), ofp_pktout_map), |
478 |
|
|
print_map(ntohl(fm->fm_out_port), ofp_port_map), |
479 |
|
|
print_map(ntohl(fm->fm_out_group), ofp_group_id_map), |
480 |
|
|
ntohs(fm->fm_flags), |
481 |
|
|
print_map(omtype, ofp_match_map), omlen); |
482 |
|
|
|
483 |
|
|
bp += sizeof(*fm); |
484 |
|
|
length -= sizeof(*fm); |
485 |
|
|
|
486 |
|
|
padsize = OFP_ALIGN(omlen) - omlen; |
487 |
|
|
omlen -= min(sizeof(fm->fm_match), omlen); |
488 |
|
|
instructionslen = length - (omlen + padsize); |
489 |
|
|
if (omtype != OFP_MATCH_OXM || omlen == 0) { |
490 |
|
|
if (instructionslen <= 0) |
491 |
|
|
return; |
492 |
|
|
|
493 |
|
|
/* Skip padding if any. */ |
494 |
|
|
if (padsize) { |
495 |
|
|
bp += padsize; |
496 |
|
|
length -= padsize; |
497 |
|
|
} |
498 |
|
|
goto parse_next_instruction; |
499 |
|
|
} |
500 |
|
|
|
501 |
|
|
parse_next_oxm: |
502 |
|
|
if (length < sizeof(*oxm)) { |
503 |
|
|
printf(" [|OpenFlow]"); |
504 |
|
|
return; |
505 |
|
|
} |
506 |
|
|
|
507 |
|
|
oxm = (struct ofp_ox_match *)bp; |
508 |
|
|
bp += sizeof(*oxm); |
509 |
|
|
length -= sizeof(*oxm); |
510 |
|
|
if (length < oxm->oxm_length) { |
511 |
|
|
printf(" [|OpenFlow]"); |
512 |
|
|
return; |
513 |
|
|
} |
514 |
|
|
|
515 |
|
|
ofp_print_oxm(oxm, bp, length); |
516 |
|
|
|
517 |
|
|
bp += oxm->oxm_length; |
518 |
|
|
length -= oxm->oxm_length; |
519 |
|
|
omlen -= min(sizeof(*oxm) + oxm->oxm_length, omlen); |
520 |
|
|
if (omlen) |
521 |
|
|
goto parse_next_oxm; |
522 |
|
|
|
523 |
|
|
/* Skip padding if any. */ |
524 |
|
|
if (padsize) { |
525 |
|
|
bp += padsize; |
526 |
|
|
length -= padsize; |
527 |
|
|
} |
528 |
|
|
|
529 |
|
|
parse_next_instruction: |
530 |
|
|
if (length < sizeof(*i)) { |
531 |
|
|
printf(" [|OpenFlow]"); |
532 |
|
|
return; |
533 |
|
|
} |
534 |
|
|
|
535 |
|
|
i = (struct ofp_instruction *)bp; |
536 |
|
|
bp += sizeof(*i); |
537 |
|
|
length -= sizeof(*i); |
538 |
|
|
instructionslen -= sizeof(*i); |
539 |
|
|
ilen = ntohs(i->i_len) - sizeof(*i); |
540 |
|
|
if (length < ilen) { |
541 |
|
|
printf(" [|OpenFlow]"); |
542 |
|
|
return; |
543 |
|
|
} |
544 |
|
|
|
545 |
|
|
ofp_print_instruction(i, bp, length); |
546 |
|
|
|
547 |
|
|
bp += ilen; |
548 |
|
|
length -= ilen; |
549 |
|
|
instructionslen -= ilen; |
550 |
|
|
if (instructionslen > 0) |
551 |
|
|
goto parse_next_instruction; |
552 |
|
|
} |
553 |
|
|
|
554 |
|
|
void |
555 |
|
|
ofp_if_print(u_char *user, const struct pcap_pkthdr *h, const u_char *p) |
556 |
|
|
{ |
557 |
|
|
struct dlt_openflow_hdr of; |
558 |
|
|
unsigned int length; |
559 |
|
|
|
560 |
|
|
ts_print(&h->ts); |
561 |
|
|
|
562 |
|
|
packetp = p; |
563 |
|
|
snapend = p + h->caplen; |
564 |
|
|
length = snapend - p; |
565 |
|
|
|
566 |
|
|
TCHECK2(*p, sizeof(of)); |
567 |
|
|
memcpy(&of, p, sizeof(of)); |
568 |
|
|
|
569 |
|
|
if (ntohl(of.of_direction) == DLT_OPENFLOW_TO_SWITCH) |
570 |
|
|
printf("controller -> %s", device); |
571 |
|
|
else |
572 |
|
|
printf("%s -> controller", device); |
573 |
|
|
if (eflag) |
574 |
|
|
printf(", datapath %#016llx", be64toh(of.of_datapath_id)); |
575 |
|
|
|
576 |
|
|
ofp_print(p + sizeof(of), length - sizeof(of)); |
577 |
|
|
goto out; |
578 |
|
|
|
579 |
|
|
trunc: |
580 |
|
|
printf("[|OpenFlow]"); |
581 |
|
|
|
582 |
|
|
out: |
583 |
|
|
if (xflag) |
584 |
|
|
default_print(p, (u_int)h->len); |
585 |
|
|
putchar('\n'); |
586 |
|
|
} |
587 |
|
|
|
588 |
|
|
void |
589 |
|
|
ofp_print(const u_char *bp, u_int length) |
590 |
|
|
{ |
591 |
|
|
struct ofp_header *oh; |
592 |
|
|
unsigned int ohlen, snaplen; |
593 |
|
|
|
594 |
|
|
/* The captured data might be smaller than indicated */ |
595 |
|
|
snaplen = snapend - bp; |
596 |
|
|
length = min(snaplen, length); |
597 |
|
|
if (length < sizeof(*oh)) { |
598 |
|
|
printf("[|OpenFlow]"); |
599 |
|
|
return; |
600 |
|
|
} |
601 |
|
|
|
602 |
|
|
oh = (struct ofp_header *)bp; |
603 |
|
|
ohlen = ntohs(oh->oh_length); |
604 |
|
|
|
605 |
|
|
printf(": OpenFlow %s type %u length %u xid %u", |
606 |
|
|
print_map(oh->oh_version, ofp_v_map), |
607 |
|
|
oh->oh_type, ntohs(oh->oh_length), ntohl(oh->oh_xid)); |
608 |
|
|
|
609 |
|
|
switch (oh->oh_version) { |
610 |
|
|
case OFP_V_1_3: |
611 |
|
|
break; |
612 |
|
|
|
613 |
|
|
default: |
614 |
|
|
return; |
615 |
|
|
} |
616 |
|
|
|
617 |
|
|
printf(": %s", print_map(oh->oh_type, ofp_t_map)); |
618 |
|
|
|
619 |
|
|
switch (oh->oh_type) { |
620 |
|
|
case OFP_T_HELLO: |
621 |
|
|
ofp_print_hello(bp, length, ohlen); |
622 |
|
|
break; |
623 |
|
|
case OFP_T_ERROR: |
624 |
|
|
ofp_print_error(bp, length); |
625 |
|
|
break; |
626 |
|
|
case OFP_T_ECHO_REQUEST: |
627 |
|
|
case OFP_T_ECHO_REPLY: |
628 |
|
|
break; |
629 |
|
|
case OFP_T_FEATURES_REQUEST: |
630 |
|
|
break; |
631 |
|
|
case OFP_T_FEATURES_REPLY: |
632 |
|
|
ofp_print_featuresreply(bp, length); |
633 |
|
|
break; |
634 |
|
|
case OFP_T_SET_CONFIG: |
635 |
|
|
ofp_print_setconfig(bp, length); |
636 |
|
|
break; |
637 |
|
|
case OFP_T_PACKET_IN: |
638 |
|
|
ofp_print_packetin(bp, length); |
639 |
|
|
break; |
640 |
|
|
case OFP_T_FLOW_REMOVED: |
641 |
|
|
ofp_print_flowremoved(bp, length); |
642 |
|
|
break; |
643 |
|
|
case OFP_T_PACKET_OUT: |
644 |
|
|
ofp_print_packetout(bp, length); |
645 |
|
|
break; |
646 |
|
|
case OFP_T_FLOW_MOD: |
647 |
|
|
ofp_print_flowmod(bp, length); |
648 |
|
|
break; |
649 |
|
|
} |
650 |
|
|
} |
651 |
|
|
|
652 |
|
|
void |
653 |
|
|
oxm_print_byte(const u_char *bp, u_int length, int hasmask, int hex) |
654 |
|
|
{ |
655 |
|
|
uint8_t *b; |
656 |
|
|
|
657 |
|
|
if (length < sizeof(*b)) { |
658 |
|
|
printf("[|OpenFlow]"); |
659 |
|
|
return; |
660 |
|
|
} |
661 |
|
|
|
662 |
|
|
b = (uint8_t *)bp; |
663 |
|
|
if (hex) |
664 |
|
|
printf("%#02x", ntohs(*b)); |
665 |
|
|
else |
666 |
|
|
printf("%u", ntohs(*b)); |
667 |
|
|
|
668 |
|
|
if (hasmask) { |
669 |
|
|
bp += sizeof(*b); |
670 |
|
|
length -= sizeof(*b); |
671 |
|
|
printf(" mask "); |
672 |
|
|
oxm_print_byte(bp, length, 0, 1); |
673 |
|
|
} |
674 |
|
|
} |
675 |
|
|
|
676 |
|
|
void |
677 |
|
|
oxm_print_halfword(const u_char *bp, u_int length, int hasmask, int hex) |
678 |
|
|
{ |
679 |
|
|
uint16_t *h; |
680 |
|
|
|
681 |
|
|
if (length < sizeof(*h)) { |
682 |
|
|
printf("[|OpenFlow]"); |
683 |
|
|
return; |
684 |
|
|
} |
685 |
|
|
|
686 |
|
|
h = (uint16_t *)bp; |
687 |
|
|
if (hex) |
688 |
|
|
printf("%#04x", ntohs(*h)); |
689 |
|
|
else |
690 |
|
|
printf("%u", ntohs(*h)); |
691 |
|
|
|
692 |
|
|
if (hasmask) { |
693 |
|
|
bp += sizeof(*h); |
694 |
|
|
length -= sizeof(*h); |
695 |
|
|
printf(" mask "); |
696 |
|
|
oxm_print_halfword(bp, length, 0, 1); |
697 |
|
|
} |
698 |
|
|
} |
699 |
|
|
|
700 |
|
|
void |
701 |
|
|
oxm_print_word(const u_char *bp, u_int length, int hasmask, int hex) |
702 |
|
|
{ |
703 |
|
|
uint32_t *w; |
704 |
|
|
|
705 |
|
|
if (length < sizeof(*w)) { |
706 |
|
|
printf("[|OpenFlow]"); |
707 |
|
|
return; |
708 |
|
|
} |
709 |
|
|
|
710 |
|
|
w = (uint32_t *)bp; |
711 |
|
|
if (hex) |
712 |
|
|
printf("%#08x", ntohl(*w)); |
713 |
|
|
else |
714 |
|
|
printf("%u", ntohl(*w)); |
715 |
|
|
|
716 |
|
|
if (hasmask) { |
717 |
|
|
bp += sizeof(*w); |
718 |
|
|
length -= sizeof(*w); |
719 |
|
|
printf(" mask "); |
720 |
|
|
oxm_print_word(bp, length, 0, 1); |
721 |
|
|
} |
722 |
|
|
} |
723 |
|
|
|
724 |
|
|
void |
725 |
|
|
oxm_print_quad(const u_char *bp, u_int length, int hasmask, int hex) |
726 |
|
|
{ |
727 |
|
|
uint64_t *q; |
728 |
|
|
|
729 |
|
|
if (length < sizeof(*q)) { |
730 |
|
|
printf("[|OpenFlow]"); |
731 |
|
|
return; |
732 |
|
|
} |
733 |
|
|
|
734 |
|
|
q = (uint64_t *)bp; |
735 |
|
|
if (hex) |
736 |
|
|
printf("%#016llx", be64toh(*q)); |
737 |
|
|
else |
738 |
|
|
printf("%llu", be64toh(*q)); |
739 |
|
|
|
740 |
|
|
if (hasmask) { |
741 |
|
|
bp += sizeof(*q); |
742 |
|
|
length -= sizeof(*q); |
743 |
|
|
printf(" mask "); |
744 |
|
|
oxm_print_quad(bp, length, 0, 1); |
745 |
|
|
} |
746 |
|
|
} |
747 |
|
|
|
748 |
|
|
void |
749 |
|
|
oxm_print_ether(const u_char *bp, u_int length, int hasmask) |
750 |
|
|
{ |
751 |
|
|
if (length < ETHER_HDR_LEN) { |
752 |
|
|
printf("[|OpenFlow]"); |
753 |
|
|
return; |
754 |
|
|
} |
755 |
|
|
|
756 |
|
|
printf("%s", etheraddr_string(bp)); |
757 |
|
|
|
758 |
|
|
if (hasmask) { |
759 |
|
|
bp += ETHER_ADDR_LEN; |
760 |
|
|
length -= ETHER_ADDR_LEN; |
761 |
|
|
printf(" mask "); |
762 |
|
|
oxm_print_ether(bp, length, 0); |
763 |
|
|
} |
764 |
|
|
} |
765 |
|
|
|
766 |
|
|
void |
767 |
|
|
oxm_print_data(const u_char *bp, u_int length, int hasmask, size_t datalen) |
768 |
|
|
{ |
769 |
|
|
uint8_t *ptr; |
770 |
|
|
int i; |
771 |
|
|
char hex[8]; |
772 |
|
|
|
773 |
|
|
if (length < datalen) { |
774 |
|
|
printf("[|OpenFlow]"); |
775 |
|
|
return; |
776 |
|
|
} |
777 |
|
|
|
778 |
|
|
ptr = (uint8_t *)bp; |
779 |
|
|
for (i = 0; i < datalen; i++) { |
780 |
|
|
snprintf(hex, sizeof(hex), "%02x", ptr[i]); |
781 |
|
|
printf("%s", hex); |
782 |
|
|
} |
783 |
|
|
|
784 |
|
|
if (hasmask) { |
785 |
|
|
bp += datalen; |
786 |
|
|
length -= datalen; |
787 |
|
|
printf(" mask "); |
788 |
|
|
oxm_print_data(bp, length, 0, datalen); |
789 |
|
|
} |
790 |
|
|
} |
791 |
|
|
|
792 |
|
|
void |
793 |
|
|
ofp_print_oxm(struct ofp_ox_match *oxm, const u_char *bp, u_int length) |
794 |
|
|
{ |
795 |
|
|
int class, field, mask, len; |
796 |
|
|
uint16_t *vlan; |
797 |
|
|
|
798 |
|
|
class = ntohs(oxm->oxm_class); |
799 |
|
|
field = OFP_OXM_GET_FIELD(oxm); |
800 |
|
|
mask = OFP_OXM_GET_HASMASK(oxm); |
801 |
|
|
len = oxm->oxm_length; |
802 |
|
|
printf(" oxm <class %s field %s hasmask %d length %d", |
803 |
|
|
print_map(class, ofp_oxm_c_map), |
804 |
|
|
print_map(field, ofp_xm_t_map), mask, len); |
805 |
|
|
|
806 |
|
|
switch (class) { |
807 |
|
|
case OFP_OXM_C_OPENFLOW_BASIC: |
808 |
|
|
break; |
809 |
|
|
|
810 |
|
|
case OFP_OXM_C_NXM_0: |
811 |
|
|
case OFP_OXM_C_NXM_1: |
812 |
|
|
case OFP_OXM_C_OPENFLOW_EXPERIMENTER: |
813 |
|
|
default: |
814 |
|
|
printf(">"); |
815 |
|
|
return; |
816 |
|
|
} |
817 |
|
|
|
818 |
|
|
printf(" value "); |
819 |
|
|
|
820 |
|
|
switch (field) { |
821 |
|
|
case OFP_XM_T_IN_PORT: |
822 |
|
|
case OFP_XM_T_IN_PHY_PORT: |
823 |
|
|
case OFP_XM_T_MPLS_LABEL: |
824 |
|
|
oxm_print_word(bp, length, mask, 0); |
825 |
|
|
break; |
826 |
|
|
|
827 |
|
|
case OFP_XM_T_META: |
828 |
|
|
case OFP_XM_T_TUNNEL_ID: |
829 |
|
|
oxm_print_quad(bp, length, mask, 1); |
830 |
|
|
break; |
831 |
|
|
|
832 |
|
|
case OFP_XM_T_ETH_DST: |
833 |
|
|
case OFP_XM_T_ETH_SRC: |
834 |
|
|
case OFP_XM_T_ARP_SHA: |
835 |
|
|
case OFP_XM_T_ARP_THA: |
836 |
|
|
case OFP_XM_T_IPV6_ND_SLL: |
837 |
|
|
case OFP_XM_T_IPV6_ND_TLL: |
838 |
|
|
oxm_print_ether(bp, length, mask); |
839 |
|
|
break; |
840 |
|
|
|
841 |
|
|
case OFP_XM_T_ETH_TYPE: |
842 |
|
|
oxm_print_halfword(bp, length, mask, 1); |
843 |
|
|
break; |
844 |
|
|
|
845 |
|
|
case OFP_XM_T_VLAN_VID: |
846 |
|
|
/* |
847 |
|
|
* VLAN has an exception: it uses the higher bits to signal |
848 |
|
|
* the presence of the VLAN. |
849 |
|
|
*/ |
850 |
|
|
if (length < sizeof(*vlan)) { |
851 |
|
|
printf("[|OpenFlow]"); |
852 |
|
|
break; |
853 |
|
|
} |
854 |
|
|
|
855 |
|
|
vlan = (uint16_t *)bp; |
856 |
|
|
if (ntohs(*vlan) & OFP_XM_VID_PRESENT) |
857 |
|
|
printf("(VLAN %d) ", |
858 |
|
|
ntohs(*vlan) & (~OFP_XM_VID_PRESENT)); |
859 |
|
|
else |
860 |
|
|
printf("(no VLAN) "); |
861 |
|
|
/* FALLTHROUGH */ |
862 |
|
|
case OFP_XM_T_TCP_SRC: |
863 |
|
|
case OFP_XM_T_TCP_DST: |
864 |
|
|
case OFP_XM_T_UDP_SRC: |
865 |
|
|
case OFP_XM_T_UDP_DST: |
866 |
|
|
case OFP_XM_T_SCTP_SRC: |
867 |
|
|
case OFP_XM_T_SCTP_DST: |
868 |
|
|
case OFP_XM_T_ARP_OP: |
869 |
|
|
case OFP_XM_T_IPV6_EXTHDR: |
870 |
|
|
oxm_print_halfword(bp, length, mask, 0); |
871 |
|
|
break; |
872 |
|
|
|
873 |
|
|
case OFP_XM_T_VLAN_PCP: |
874 |
|
|
case OFP_XM_T_IP_DSCP: |
875 |
|
|
case OFP_XM_T_IP_ECN: |
876 |
|
|
case OFP_XM_T_MPLS_TC: |
877 |
|
|
case OFP_XM_T_MPLS_BOS: |
878 |
|
|
oxm_print_byte(bp, length, mask, 1); |
879 |
|
|
break; |
880 |
|
|
|
881 |
|
|
case OFP_XM_T_IPV4_SRC: |
882 |
|
|
case OFP_XM_T_IPV4_DST: |
883 |
|
|
case OFP_XM_T_ARP_SPA: |
884 |
|
|
case OFP_XM_T_ARP_TPA: |
885 |
|
|
case OFP_XM_T_IPV6_FLABEL: |
886 |
|
|
oxm_print_word(bp, length, mask, 1); |
887 |
|
|
break; |
888 |
|
|
|
889 |
|
|
case OFP_XM_T_IP_PROTO: |
890 |
|
|
case OFP_XM_T_ICMPV4_TYPE: |
891 |
|
|
case OFP_XM_T_ICMPV4_CODE: |
892 |
|
|
case OFP_XM_T_ICMPV6_TYPE: |
893 |
|
|
case OFP_XM_T_ICMPV6_CODE: |
894 |
|
|
oxm_print_byte(bp, length, mask, 0); |
895 |
|
|
break; |
896 |
|
|
|
897 |
|
|
case OFP_XM_T_IPV6_SRC: |
898 |
|
|
case OFP_XM_T_IPV6_DST: |
899 |
|
|
case OFP_XM_T_IPV6_ND_TARGET: |
900 |
|
|
oxm_print_data(bp, length, mask, sizeof(struct in6_addr)); |
901 |
|
|
break; |
902 |
|
|
|
903 |
|
|
case OFP_XM_T_PBB_ISID: |
904 |
|
|
oxm_print_data(bp, length, mask, 3); |
905 |
|
|
break; |
906 |
|
|
|
907 |
|
|
default: |
908 |
|
|
printf("unknown"); |
909 |
|
|
break; |
910 |
|
|
} |
911 |
|
|
|
912 |
|
|
printf(">"); |
913 |
|
|
} |
914 |
|
|
|
915 |
|
|
void |
916 |
|
|
action_print_output(const u_char *bp, u_int length) |
917 |
|
|
{ |
918 |
|
|
struct ofp_action_output *ao; |
919 |
|
|
|
920 |
|
|
if (length < (sizeof(*ao) - AH_UNPADDED)) { |
921 |
|
|
printf(" [|OpenFlow]"); |
922 |
|
|
return; |
923 |
|
|
} |
924 |
|
|
|
925 |
|
|
ao = (struct ofp_action_output *)(bp - AH_UNPADDED); |
926 |
|
|
printf(" port %s max_len %s", |
927 |
|
|
print_map(ntohl(ao->ao_port), ofp_port_map), |
928 |
|
|
print_map(ntohs(ao->ao_max_len), ofp_controller_maxlen_map)); |
929 |
|
|
} |
930 |
|
|
|
931 |
|
|
void |
932 |
|
|
action_print_group(const u_char *bp, u_int length) |
933 |
|
|
{ |
934 |
|
|
struct ofp_action_group *ag; |
935 |
|
|
|
936 |
|
|
if (length < (sizeof(*ag) - AH_UNPADDED)) { |
937 |
|
|
printf(" [|OpenFlow]"); |
938 |
|
|
return; |
939 |
|
|
} |
940 |
|
|
|
941 |
|
|
ag = (struct ofp_action_group *)(bp - AH_UNPADDED); |
942 |
|
|
printf(" group_id %s", |
943 |
|
|
print_map(ntohl(ag->ag_group_id), ofp_group_id_map)); |
944 |
|
|
} |
945 |
|
|
|
946 |
|
|
void |
947 |
|
|
action_print_setqueue(const u_char *bp, u_int length) |
948 |
|
|
{ |
949 |
|
|
struct ofp_action_set_queue *asq; |
950 |
|
|
|
951 |
|
|
if (length < (sizeof(*asq) - AH_UNPADDED)) { |
952 |
|
|
printf(" [|OpenFlow]"); |
953 |
|
|
return; |
954 |
|
|
} |
955 |
|
|
|
956 |
|
|
asq = (struct ofp_action_set_queue *)(bp - AH_UNPADDED); |
957 |
|
|
printf(" queue_id %u", ntohl(asq->asq_queue_id)); |
958 |
|
|
} |
959 |
|
|
|
960 |
|
|
void |
961 |
|
|
action_print_setmplsttl(const u_char *bp, u_int length) |
962 |
|
|
{ |
963 |
|
|
struct ofp_action_mpls_ttl *amt; |
964 |
|
|
|
965 |
|
|
if (length < (sizeof(*amt) - AH_UNPADDED)) { |
966 |
|
|
printf(" [|OpenFlow]"); |
967 |
|
|
return; |
968 |
|
|
} |
969 |
|
|
|
970 |
|
|
amt = (struct ofp_action_mpls_ttl *)(bp - AH_UNPADDED); |
971 |
|
|
printf(" ttl %d", amt->amt_ttl); |
972 |
|
|
} |
973 |
|
|
|
974 |
|
|
void |
975 |
|
|
action_print_setnwttl(const u_char *bp, u_int length) |
976 |
|
|
{ |
977 |
|
|
struct ofp_action_nw_ttl *ant; |
978 |
|
|
|
979 |
|
|
if (length < (sizeof(*ant) - AH_UNPADDED)) { |
980 |
|
|
printf(" [|OpenFlow]"); |
981 |
|
|
return; |
982 |
|
|
} |
983 |
|
|
|
984 |
|
|
ant = (struct ofp_action_nw_ttl *)(bp - AH_UNPADDED); |
985 |
|
|
printf(" ttl %d", ant->ant_ttl); |
986 |
|
|
} |
987 |
|
|
|
988 |
|
|
void |
989 |
|
|
action_print_push(const u_char *bp, u_int length) |
990 |
|
|
{ |
991 |
|
|
struct ofp_action_push *ap; |
992 |
|
|
|
993 |
|
|
if (length < (sizeof(*ap) - AH_UNPADDED)) { |
994 |
|
|
printf(" [|OpenFlow]"); |
995 |
|
|
return; |
996 |
|
|
} |
997 |
|
|
|
998 |
|
|
ap = (struct ofp_action_push *)(bp - AH_UNPADDED); |
999 |
|
|
printf(" ethertype %#04x", ntohs(ap->ap_ethertype)); |
1000 |
|
|
} |
1001 |
|
|
|
1002 |
|
|
void |
1003 |
|
|
action_print_popmpls(const u_char *bp, u_int length) |
1004 |
|
|
{ |
1005 |
|
|
struct ofp_action_pop_mpls *apm; |
1006 |
|
|
|
1007 |
|
|
if (length < (sizeof(*apm) - AH_UNPADDED)) { |
1008 |
|
|
printf(" [|OpenFlow]"); |
1009 |
|
|
return; |
1010 |
|
|
} |
1011 |
|
|
|
1012 |
|
|
apm = (struct ofp_action_pop_mpls *)(bp - AH_UNPADDED); |
1013 |
|
|
printf(" ethertype %#04x", ntohs(apm->apm_ethertype)); |
1014 |
|
|
} |
1015 |
|
|
|
1016 |
|
|
void |
1017 |
|
|
action_print_setfield(const u_char *bp, u_int length) |
1018 |
|
|
{ |
1019 |
|
|
struct ofp_action_set_field *asf; |
1020 |
|
|
struct ofp_ox_match *oxm; |
1021 |
|
|
int omlen; |
1022 |
|
|
|
1023 |
|
|
if (length < (sizeof(*asf) - AH_UNPADDED)) { |
1024 |
|
|
printf(" [|OpenFlow]"); |
1025 |
|
|
return; |
1026 |
|
|
} |
1027 |
|
|
|
1028 |
|
|
asf = (struct ofp_action_set_field *)(bp - AH_UNPADDED); |
1029 |
|
|
omlen = ntohs(asf->asf_len) - AH_UNPADDED; |
1030 |
|
|
if (omlen == 0) |
1031 |
|
|
return; |
1032 |
|
|
|
1033 |
|
|
parse_next_oxm: |
1034 |
|
|
if (length < sizeof(*oxm)) { |
1035 |
|
|
printf(" [|OpenFlow]"); |
1036 |
|
|
return; |
1037 |
|
|
} |
1038 |
|
|
|
1039 |
|
|
oxm = (struct ofp_ox_match *)bp; |
1040 |
|
|
bp += sizeof(*oxm); |
1041 |
|
|
length -= sizeof(*oxm); |
1042 |
|
|
if (length < oxm->oxm_length) { |
1043 |
|
|
printf(" [|OpenFlow]"); |
1044 |
|
|
return; |
1045 |
|
|
} |
1046 |
|
|
|
1047 |
|
|
ofp_print_oxm(oxm, bp, length); |
1048 |
|
|
|
1049 |
|
|
bp += oxm->oxm_length; |
1050 |
|
|
length -= oxm->oxm_length; |
1051 |
|
|
omlen -= min(sizeof(*oxm) + oxm->oxm_length, omlen); |
1052 |
|
|
if (omlen) |
1053 |
|
|
goto parse_next_oxm; |
1054 |
|
|
} |
1055 |
|
|
|
1056 |
|
|
void |
1057 |
|
|
ofp_print_action(struct ofp_action_header *ah, const u_char *bp, u_int length) |
1058 |
|
|
{ |
1059 |
|
|
int ahtype; |
1060 |
|
|
|
1061 |
|
|
ahtype = ntohs(ah->ah_type); |
1062 |
|
|
printf(" action <type %s length %d", |
1063 |
|
|
print_map(ahtype, ofp_action_map), ntohs(ah->ah_len)); |
1064 |
|
|
|
1065 |
|
|
switch (ahtype) { |
1066 |
|
|
case OFP_ACTION_OUTPUT: |
1067 |
|
|
action_print_output(bp, length); |
1068 |
|
|
break; |
1069 |
|
|
|
1070 |
|
|
case OFP_ACTION_GROUP: |
1071 |
|
|
action_print_group(bp, length); |
1072 |
|
|
break; |
1073 |
|
|
|
1074 |
|
|
case OFP_ACTION_SET_QUEUE: |
1075 |
|
|
action_print_setqueue(bp, length); |
1076 |
|
|
break; |
1077 |
|
|
|
1078 |
|
|
case OFP_ACTION_SET_MPLS_TTL: |
1079 |
|
|
action_print_setmplsttl(bp, length); |
1080 |
|
|
break; |
1081 |
|
|
|
1082 |
|
|
case OFP_ACTION_SET_NW_TTL: |
1083 |
|
|
action_print_setnwttl(bp, length); |
1084 |
|
|
break; |
1085 |
|
|
|
1086 |
|
|
case OFP_ACTION_PUSH_VLAN: |
1087 |
|
|
case OFP_ACTION_PUSH_MPLS: |
1088 |
|
|
case OFP_ACTION_PUSH_PBB: |
1089 |
|
|
action_print_push(bp, length); |
1090 |
|
|
break; |
1091 |
|
|
|
1092 |
|
|
case OFP_ACTION_POP_MPLS: |
1093 |
|
|
action_print_popmpls(bp, length); |
1094 |
|
|
break; |
1095 |
|
|
|
1096 |
|
|
case OFP_ACTION_SET_FIELD: |
1097 |
|
|
break; |
1098 |
|
|
|
1099 |
|
|
case OFP_ACTION_COPY_TTL_OUT: |
1100 |
|
|
case OFP_ACTION_COPY_TTL_IN: |
1101 |
|
|
case OFP_ACTION_DEC_NW_TTL: |
1102 |
|
|
case OFP_ACTION_DEC_MPLS_TTL: |
1103 |
|
|
case OFP_ACTION_POP_VLAN: |
1104 |
|
|
case OFP_ACTION_POP_PBB: |
1105 |
|
|
case OFP_ACTION_EXPERIMENTER: |
1106 |
|
|
default: |
1107 |
|
|
/* Generic header, nothing to show here. */ |
1108 |
|
|
break; |
1109 |
|
|
} |
1110 |
|
|
|
1111 |
|
|
printf(">"); |
1112 |
|
|
} |
1113 |
|
|
|
1114 |
|
|
void |
1115 |
|
|
instruction_print_gototable(const char *bp, u_int length) |
1116 |
|
|
{ |
1117 |
|
|
struct ofp_instruction_goto_table *igt; |
1118 |
|
|
|
1119 |
|
|
if (length < (sizeof(*igt) - sizeof(struct ofp_instruction))) { |
1120 |
|
|
printf(" [|OpenFlow]"); |
1121 |
|
|
return; |
1122 |
|
|
} |
1123 |
|
|
|
1124 |
|
|
igt = (struct ofp_instruction_goto_table *) |
1125 |
|
|
(bp - sizeof(struct ofp_instruction)); |
1126 |
|
|
printf(" table_id %d", igt->igt_table_id); |
1127 |
|
|
} |
1128 |
|
|
|
1129 |
|
|
void |
1130 |
|
|
instruction_print_meta(const char *bp, u_int length) |
1131 |
|
|
{ |
1132 |
|
|
struct ofp_instruction_write_metadata *iwm; |
1133 |
|
|
|
1134 |
|
|
if (length < (sizeof(*iwm) - sizeof(struct ofp_instruction))) { |
1135 |
|
|
printf(" [|OpenFlow]"); |
1136 |
|
|
return; |
1137 |
|
|
} |
1138 |
|
|
|
1139 |
|
|
iwm = (struct ofp_instruction_write_metadata *) |
1140 |
|
|
(bp - sizeof(struct ofp_instruction)); |
1141 |
|
|
printf(" metadata %llu metadata_mask %llu", |
1142 |
|
|
be64toh(iwm->iwm_metadata), be64toh(iwm->iwm_metadata_mask)); |
1143 |
|
|
} |
1144 |
|
|
|
1145 |
|
|
void |
1146 |
|
|
instruction_print_actions(const char *bp, u_int length) |
1147 |
|
|
{ |
1148 |
|
|
struct ofp_instruction_actions *ia; |
1149 |
|
|
struct ofp_action_header *ah; |
1150 |
|
|
int actionslen; |
1151 |
|
|
unsigned int ahlen; |
1152 |
|
|
|
1153 |
|
|
if (length < (sizeof(*ia) - sizeof(struct ofp_instruction))) { |
1154 |
|
|
printf(" [|OpenFlow]"); |
1155 |
|
|
return; |
1156 |
|
|
} |
1157 |
|
|
|
1158 |
|
|
ia = (struct ofp_instruction_actions *) |
1159 |
|
|
(bp - sizeof(struct ofp_instruction)); |
1160 |
|
|
|
1161 |
|
|
actionslen = ntohs(ia->ia_len) - sizeof(*ia); |
1162 |
|
|
if (actionslen <= 0) |
1163 |
|
|
return; |
1164 |
|
|
|
1165 |
|
|
bp += sizeof(*ia) - sizeof(struct ofp_instruction); |
1166 |
|
|
length -= sizeof(*ia) - sizeof(struct ofp_instruction); |
1167 |
|
|
|
1168 |
|
|
parse_next_action: |
1169 |
|
|
if (length < sizeof(*ah)) { |
1170 |
|
|
printf(" [|OpenFlow]"); |
1171 |
|
|
return; |
1172 |
|
|
} |
1173 |
|
|
|
1174 |
|
|
ah = (struct ofp_action_header *)bp; |
1175 |
|
|
bp += AH_UNPADDED; |
1176 |
|
|
length -= AH_UNPADDED; |
1177 |
|
|
actionslen -= AH_UNPADDED; |
1178 |
|
|
ahlen = ntohs(ah->ah_len) - AH_UNPADDED; |
1179 |
|
|
if (length < ahlen) { |
1180 |
|
|
printf(" [|OpenFlow]"); |
1181 |
|
|
return; |
1182 |
|
|
} |
1183 |
|
|
|
1184 |
|
|
ofp_print_action(ah, bp, length); |
1185 |
|
|
|
1186 |
|
|
bp += ahlen; |
1187 |
|
|
length -= ahlen; |
1188 |
|
|
actionslen -= min(ahlen, actionslen); |
1189 |
|
|
if (actionslen) |
1190 |
|
|
goto parse_next_action; |
1191 |
|
|
} |
1192 |
|
|
|
1193 |
|
|
void |
1194 |
|
|
instruction_print_meter(const char *bp, u_int length) |
1195 |
|
|
{ |
1196 |
|
|
struct ofp_instruction_meter *im; |
1197 |
|
|
|
1198 |
|
|
if (length < (sizeof(*im) - sizeof(struct ofp_instruction))) { |
1199 |
|
|
printf(" [|OpenFlow]"); |
1200 |
|
|
return; |
1201 |
|
|
} |
1202 |
|
|
|
1203 |
|
|
im = (struct ofp_instruction_meter *) |
1204 |
|
|
(bp - sizeof(struct ofp_instruction)); |
1205 |
|
|
printf(" meter_id %u", ntohl(im->im_meter_id)); |
1206 |
|
|
} |
1207 |
|
|
|
1208 |
|
|
void |
1209 |
|
|
instruction_print_experimenter(const char *bp, u_int length) |
1210 |
|
|
{ |
1211 |
|
|
struct ofp_instruction_experimenter *ie; |
1212 |
|
|
|
1213 |
|
|
if (length < (sizeof(*ie) - sizeof(struct ofp_instruction))) { |
1214 |
|
|
printf(" [|OpenFlow]"); |
1215 |
|
|
return; |
1216 |
|
|
} |
1217 |
|
|
|
1218 |
|
|
ie = (struct ofp_instruction_experimenter *) |
1219 |
|
|
(bp - sizeof(struct ofp_instruction)); |
1220 |
|
|
printf(" experimenter %u", ntohl(ie->ie_experimenter)); |
1221 |
|
|
} |
1222 |
|
|
|
1223 |
|
|
void |
1224 |
|
|
ofp_print_instruction(struct ofp_instruction *i, const char *bp, u_int length) |
1225 |
|
|
{ |
1226 |
|
|
int itype; |
1227 |
|
|
|
1228 |
|
|
itype = ntohs(i->i_type); |
1229 |
|
|
printf(" instruction <type %s length %d", |
1230 |
|
|
print_map(itype, ofp_instruction_t_map), ntohs(i->i_len)); |
1231 |
|
|
|
1232 |
|
|
switch (itype) { |
1233 |
|
|
case OFP_INSTRUCTION_T_GOTO_TABLE: |
1234 |
|
|
instruction_print_gototable(bp, length); |
1235 |
|
|
break; |
1236 |
|
|
case OFP_INSTRUCTION_T_WRITE_META: |
1237 |
|
|
instruction_print_meta(bp, length); |
1238 |
|
|
break; |
1239 |
|
|
case OFP_INSTRUCTION_T_WRITE_ACTIONS: |
1240 |
|
|
case OFP_INSTRUCTION_T_APPLY_ACTIONS: |
1241 |
|
|
case OFP_INSTRUCTION_T_CLEAR_ACTIONS: |
1242 |
|
|
instruction_print_actions(bp, length); |
1243 |
|
|
break; |
1244 |
|
|
case OFP_INSTRUCTION_T_METER: |
1245 |
|
|
instruction_print_meter(bp, length); |
1246 |
|
|
break; |
1247 |
|
|
case OFP_INSTRUCTION_T_EXPERIMENTER: |
1248 |
|
|
instruction_print_meter(bp, length); |
1249 |
|
|
break; |
1250 |
|
|
} |
1251 |
|
|
|
1252 |
|
|
printf(">"); |
1253 |
|
|
} |