This is the first step to teach tcpdump(8) how to read OpenFlow protocol
packets. I've implemented support for most messages switchd(8) uses and
currently only for OpenFlow 1.3.5, but we might reuse some functions for
OpenFlow 1.0 as well. For now we don't support showing strings of
definitions instead of numbers.

Right now we might not be able to tcpdump local switchd(8) <-> switch(4),
however for remote switchd(8) using TCP it is possible (remote switch(4),
HP device, Mininet etc...).

Supported messages:
 * Hello (with bitmap, there is a diff for switchd(8) as well);
 * Error (with encapsulated ofp header printing);
 * Echo Request/Reply;
 * Features Request/Reply;
 * Set Config;
 * Packet-in (with encapsulated ethernet printing);
 * Packet-out (with encapsulated ethernet printing);
 * Flow removed;

Missing:
 * Multipart request/reply;
 * Flow mod;

I'll show some packet capture output examples and you can find the diff
in the end of this e-mail.

Normal packet capture:
---snip---
12:07:18.700340 172.23.1.203.63996 > 172.23.1.210.6633: P 1:9(8) ack 1 win 
33304 <nop,nop,timestamp 9696690 2782880257> OpenFlow(ver 0x4 type 0 len 8 xid 
2711) HELLO (DF)
12:07:18.701825 172.23.1.210.6633 > 172.23.1.203.63996: P 1:9(8) ack 9 win 271 
<nop,nop,timestamp 2782880257 9696690> OpenFlow(ver 0x4 type 0 len 8 xid 0) 
HELLO (DF)
12:07:18.701986 172.23.1.210.6633 > 172.23.1.203.63996: P 9:17(8) ack 9 win 271 
<nop,nop,timestamp 2782880257 9696690> OpenFlow(ver 0x4 type 5 len 8 xid 1) 
FEATURES REQUEST (DF)
12:07:18.704463 172.23.1.203.63996 > 172.23.1.210.6633: P 9:41(32) ack 17 win 
33296 <nop,nop,timestamp 9696690 2782880257> OpenFlow(ver 0x4 type 6 len 32 xid 
1) FEATURES REPLY <datapath_id 0x0138eaa7711440 nbuffers 0 ntables 3 aux_id 0 
capabilities 0x00010f actions 00000000> (DF)
12:07:18.705252 172.23.1.210.6633 > 172.23.1.203.63996: P 17:29(12) ack 41 win 
271 <nop,nop,timestamp 2782880257 9696690> OpenFlow(ver 0x4 type 9 len 12 xid 
2) SET CONFIG <flags 0000 miss_send_len 65535> (DF)
12:07:18.705452 172.23.1.210.6633 > 172.23.1.203.63996: P 29:109(80) ack 41 win 
271 <nop,nop,timestamp 2782880257 9696690> OpenFlow(ver 0x4 type 14 len 80 xid 
3) (DF)
12:07:18.778120 172.23.1.203.63996 > 172.23.1.210.6633: P 41:169(128) ack 109 
win 33250 <nop,nop,timestamp 9696770 2782880257> OpenFlow(ver 0x4 type 10 len 
128 xid 2712) PACKET-IN <buffer_id 4294967295 total_len 70 reason 0 table_id 
100 cookie 0000000000000000 match type 1 length 26> OXM <class 32768 field 0 
hasmask 0 length 4 value 3> OXM <class 32768 field 1 hasmask 0 length 4 value 
3> OXM <class 32768 field 6 hasmask 0 length 2 value (VLAN 1) 4097> 
fe80::b283:feff:fe70:7e7a > ff02::2: icmp6: router solicitation  (DF)
12:07:18.780401 172.23.1.210.6633 > 172.23.1.203.63996: P 109:219(110) ack 169 
win 271 <nop,nop,timestamp 2782880257 9696770> OpenFlow(ver 0x4 type 13 len 110 
xid 4) PACKET-OUT <buffer_id 4294967295 in_port 3 actions_len 16> ACTION <type 
0 length 16 port 4294967291 max_len 65535> fe80::b283:feff:fe70:7e7a > ff02::2: 
icmp6: router solicitation  (DF)
12:07:26.446497 172.23.1.203.63996 > 172.23.1.210.6633: P 463:603(140) ack 495 
win 33057 <nop,nop,timestamp 9704430 2782880261> OpenFlow(ver 0x4 type 10 len 
140 xid 2714) PACKET-IN <buffer_id 4294967295 total_len 82 reason 0 table_id 
100 cookie 0000000000000000 match type 1 length 26> OXM <class 32768 field 0 
hasmask 0 length 4 value 3> OXM <class 32768 field 1 hasmask 0 length 4 value 
3> OXM <class 32768 field 6 hasmask 0 length 2 value (VLAN 1) 4097> 
172.23.1.206.61780 > 224.0.0.253.3544: udp 40 [ttl 1] (DF)
12:07:26.448460 172.23.1.210.6633 > 172.23.1.203.63996: P 495:617(122) ack 603 
win 271 <nop,nop,timestamp 2782880272 9704430> OpenFlow(ver 0x4 type 13 len 122 
xid 6) PACKET-OUT <buffer_id 4294967295 in_port 3 actions_len 16> ACTION <type 
0 length 16 port 4294967291 max_len 65535> 172.23.1.206.61780 > 
224.0.0.253.3544: udp 40 [ttl 1] (DF)
12:07:26.658565 172.23.1.203.63996 > 172.23.1.210.6633: P 603:1013(410) ack 617 
win 32996 <nop,nop,timestamp 9704650 2782880272> OpenFlow(ver 0x4 type 10 len 
410 xid 2715) PACKET-IN <buffer_id 4294967295 total_len 352 reason 0 table_id 
100 cookie 0000000000000000 match type 1 length 26> OXM <class 32768 field 0 
hasmask 0 length 4 value 3> OXM <class 32768 field 1 hasmask 0 length 4 value 
3> OXM <class 32768 field 6 hasmask 0 length 2 value (VLAN 1) 4097> 
172.23.1.62.1900 > 239.255.255.250.1900: udp 310 (DF)
12:07:26.662728 172.23.1.210.6633 > 172.23.1.203.63996: P 617:1009(392) ack 
1013 win 271 <nop,nop,timestamp 2782880273 9704650> OpenFlow(ver 0x4 type 13 
len 392 xid 7) PACKET-OUT <buffer_id 4294967295 in_port 3 actions_len 16> 
ACTION <type 0 length 16 port 4294967291 max_len 65535> 172.23.1.62.1900 > 
239.255.255.250.1900: udp 310 (DF)
12:07:26.709177 172.23.1.203.63996 > 172.23.1.210.6633: P 1013:1423(410) ack 
1009 win 32800 <nop,nop,timestamp 9704700 2782880273> OpenFlow(ver 0x4 type 10 
len 410 xid 2716)PACKET-IN <buffer_id 4294967295 total_len 352 reason 0 
table_id 100 cookie 0000000000000000 match type 1 length 26> OXM <class 32768 
field 0 hasmask 0 length 4 value 3> OXM <class 32768 field 1 hasmask 0 length 4 
value 3> OXM <class 32768 field 6 hasmask 0 length 2 value (VLAN 1) 4097> 
172.23.1.62.1900 > 239.255.255.250.1900: udp 310 (DF)
12:07:26.713609 172.23.1.210.6633 > 172.23.1.203.63996: P 1009:1401(392) ack 
1423 win 271 <nop,nop,timestamp 2782880273 9704700> OpenFlow(ver 0x4 type 13 
len 392 xid 8) PACKET-OUT <buffer_id 4294967295 in_port 3 actions_len 16> 
ACTION <type 0 length 16 port 4294967291 max_len 65535> 172.23.1.62.1900 > 
239.255.255.250.1900: udp 310 (DF)
12:07:26.759321 172.23.1.203.63996 > 172.23.1.210.6633: P 1423:1842(419) ack 
1401 win 32604 <nop,nop,timestamp 9704750 2782880273> OpenFlow(ver 0x4 type 10 
len 419 xid 2717)PACKET-IN <buffer_id 4294967295 total_len 361 reason 0 
table_id 100 cookie 0000000000000000 match type 1 length 26> OXM <class 32768 
field 0 hasmask 0 length 4 value 3> OXM <class 32768 field 1 hasmask 0 length 4 
value 3> OXM <class 32768 field 6 hasmask 0 length 2 value (VLAN 1) 4097> 
172.23.1.62.1900 > 239.255.255.250.1900: udp 319 (DF)
12:07:26.761087 172.23.1.210.6633 > 172.23.1.203.63996: P 1401:1802(401) ack 
1842 win 271 <nop,nop,timestamp 2782880273 9704750> OpenFlow(ver 0x4 type 13 
len 401 xid 9) PACKET-OUT <buffer_id 4294967295 in_port 3 actions_len 16> 
ACTION <type 0 length 16 port 4294967291 max_len 65535> 172.23.1.62.1900 > 
239.255.255.250.1900: udp 319 (DF)
12:07:26.810621 172.23.1.203.63996 > 172.23.1.210.6633: P 1842:2261(419) ack 
1802 win 33304 <nop,nop,timestamp 9704800 2782880273> OpenFlow(ver 0x4 type 10 
len 419 xid 2718)PACKET-IN <buffer_id 4294967295 total_len 361 reason 0 
table_id 100 cookie 0000000000000000 match type 1 length 26> OXM <class 32768 
field 0 hasmask 0 length 4 value 3> OXM <class 32768 field 1 hasmask 0 length 4 
value 3> OXM <class 32768 field 6 hasmask 0 length 2 value (VLAN 1) 4097> 
172.23.1.62.1900 > 239.255.255.250.1900: udp 319 (DF)
12:07:26.812693 172.23.1.210.6633 > 172.23.1.203.63996: P 1802:2203(401) ack 
2261 win 271 <nop,nop,timestamp 2782880273 9704800> OpenFlow(ver 0x4 type 13 
len 401 xid 10) PACKET-OUT <buffer_id 4294967295 in_port 3 actions_len 16> 
ACTION <type 0 length 16 port 4294967291 max_len 65535> 172.23.1.62.1900 > 
239.255.255.250.1900: udp 319 (DF)
12:07:26.860610 172.23.1.203.63996 > 172.23.1.210.6633: P 2261:2674(413) ack 
2203 win 33103 <nop,nop,timestamp 9704850 2782880273> OpenFlow(ver 0x4 type 10 
len 413 xid 2719)PACKET-IN <buffer_id 4294967295 total_len 355 reason 0 
table_id 100 cookie 0000000000000000 match type 1 length 26> OXM <class 32768 
field 0 hasmask 0 length 4 value 3> OXM <class 32768 field 1 hasmask 0 length 4 
value 3> OXM <class 32768 field 6 hasmask 0 length 2 value (VLAN 1) 4097> 
172.23.1.62.1900 > 239.255.255.250.1900: udp 313 (DF)
12:07:26.862536 172.23.1.210.6633 > 172.23.1.203.63996: P 2203:2598(395) ack 
2674 win 271 <nop,nop,timestamp 2782880273 9704850> OpenFlow(ver 0x4 type 13 
len 395 xid 11) PACKET-OUT <buffer_id 4294967295 in_port 3 actions_len 16> 
ACTION <type 0 length 16 port 4294967291 max_len 65535> 172.23.1.62.1900 > 
239.255.255.250.1900: udp 313 (DF)
12:07:26.911666 172.23.1.203.63996 > 172.23.1.210.6633: P 2674:3087(413) ack 
2598 win 32906 <nop,nop,timestamp 9704900 2782880273> OpenFlow(ver 0x4 type 10 
len 413 xid 2720)PACKET-IN <buffer_id 4294967295 total_len 355 reason 0 
table_id 100 cookie 0000000000000000 match type 1 length 26> OXM <class 32768 
field 0 hasmask 0 length 4 value 3> OXM <class 32768 field 1 hasmask 0 length 4 
value 3> OXM <class 32768 field 6 hasmask 0 length 2 value (VLAN 1) 4097> 
172.23.1.62.1900 > 239.255.255.250.1900: udp 313 (DF)
12:07:26.913615 172.23.1.210.6633 > 172.23.1.203.63996: P 2598:2993(395) ack 
3087 win 271 <nop,nop,timestamp 2782880273 9704900> OpenFlow(ver 0x4 type 13 
len 395 xid 12) PACKET-OUT <buffer_id 4294967295 in_port 3 actions_len 16> 
ACTION <type 0 length 16 port 4294967291 max_len 65535> 172.23.1.62.1900 > 
239.255.255.250.1900: udp 313 (DF)
---snip---

Packet capture with '-X':
---snip---
12:14:01.050756 172.23.1.203.53258 > 172.23.1.210.6633: P 1:9(8) ack 1 win 
33304 <nop,nop,timestamp 10099040 2636559980> OpenFlow(ver 0x4 type 0 len 8 xid 
2738) HELLO (DF)
  0000: 4500 003c 1560 4000 4006 c990 ac17 01cb  E..<.`@.@.......
  0010: ac17 01d2 d00a 19e9 7807 402d 5188 f72d  ........x.@-Q..-
  0020: 8018 8218 31a3 0000 0101 080a 009a 1960  ....1..........`
  0030: 9d26 b66c 0400 0008 0000 0ab2            .&.l........

12:14:01.052197 172.23.1.210.6633 > 172.23.1.203.53258: P 1:9(8) ack 9 win 271 
<nop,nop,timestamp 2636559980 10099040> OpenFlow(ver 0x4 type 0 len 8 xid 0) 
HELLO (DF)
  0000: 4500 003c 9911 4000 4006 45df ac17 01d2  E..<..@.@.E.....
  0010: ac17 01cb 19e9 d00a 5188 f72d 7807 4035  ........Q..-x.@5
  0020: 8018 010f bd56 0000 0101 080a 9d26 b66c  .....V.......&.l
  0030: 009a 1960 0400 0008 0000 0000            ...`........

12:14:01.052325 172.23.1.210.6633 > 172.23.1.203.53258: P 9:17(8) ack 9 win 271 
<nop,nop,timestamp 2636559980 10099040> OpenFlow(ver 0x4 type 5 len 8 xid 1) 
FEATURES REQUEST(DF)
  0000: 4500 003c 53db 4000 4006 8b15 ac17 01d2  E..<S.@.@.......
  0010: ac17 01cb 19e9 d00a 5188 f735 7807 4035  ........Q..5x.@5
  0020: 8018 010f bd48 0000 0101 080a 9d26 b66c  .....H.......&.l
  0030: 009a 1960 0405 0008 0000 0001            ...`........

12:14:01.054805 172.23.1.203.53258 > 172.23.1.210.6633: P 9:41(32) ack 17 win 
33296 <nop,nop,timestamp 10099040 2636559980> OpenFlow(ver 0x4 type 6 len 32 
xid 1) FEATURES REPLY <datapath_id 0x0138eaa7711440 nbuffers 0 ntables 3 aux_id 
0 capabilities 0x00010f actions 00000000> (DF)
  0000: 4500 0054 1561 4000 4006 c977 ac17 01cb  E..T.a@.@..w....
  0010: ac17 01d2 d00a 19e9 7807 4035 5188 f73d  ........x.@5Q..=
  0020: 8018 8210 4362 0000 0101 080a 009a 1960  ....Cb.........`
  0030: 9d26 b66c 0406 0020 0000 0001 0001 38ea  .&.l... ......8.
  0040: a771 1440 0000 0000 0300 0000 0000 010f  .q.@............
  0050: 0000 0000                                ....

12:14:01.055520 172.23.1.210.6633 > 172.23.1.203.53258: P 17:29(12) ack 41 win 
271 <nop,nop,timestamp 2636559980 10099040> OpenFlow(ver 0x4 type 9 len 12 xid 
2) SET CONFIG <flags 0000 miss_send_len 65535> (DF)
  0000: 4500 0040 4964 4000 4006 9588 ac17 01d2  E..@Id@.@.......
  0010: ac17 01cb 19e9 d00a 5188 f73d 7807 4055  ........Q..=x.@U
  0020: 8018 010f bd13 0000 0101 080a 9d26 b66c  .............&.l
  0030: 009a 1960 0409 000c 0000 0002 0000 ffff  ...`............

12:14:11.098895 172.23.1.203.53258 > 172.23.1.210.6633: P 41:49(8) ack 109 win 
33250 <nop,nop,timestamp 10109080 2636559980> OpenFlow(ver 0x4 type 2 len 8 xid 
0) ECHO REQUEST (DF)
  0000: 4500 003c 1563 4000 4006 c98d ac17 01cb  E..<.c@.@.......
  0010: ac17 01d2 d00a 19e9 7807 4055 5188 f799  ........x.@UQ...
  0020: 8018 81e2 14bd 0000 0101 080a 009a 4098  ..............@.
  0030: 9d26 b66c 0402 0008 0000 0000            .&.l........

12:14:11.099316 172.23.1.210.6633 > 172.23.1.203.53258: P 109:117(8) ack 49 win 
271 <nop,nop,timestamp 2636560000 10109080> OpenFlow(ver 0x4 type 3 len 8 xid 
0) ECHO REPLY (DF)
  0000: 4500 003c 7274 4000 4006 6c7c ac17 01d2  E..<rt@.@.l|....
  0010: ac17 01cb 19e9 d00a 5188 f799 7807 405d  ........Q...x.@]
  0020: 8018 010f 9573 0000 0101 080a 9d26 b680  .....s.......&..
  0030: 009a 4098 0403 0008 0000 0000            ..@.........

12:14:14.098750 172.23.1.203.53258 > 172.23.1.210.6633: P 49:167(118) ack 117 
win 33246 <nop,nop,timestamp 10112080 2636560000> OpenFlow(ver 0x4 type 10 len 
118 xid 2739) PACKET-IN <buffer_id 4294967295 total_len 60 reason 0 table_id 
100 cookie 0000000000000000 match type 1 length 26> OXM <class 32768 field 0 
hasmask 0 length 4 value 3> OXM <class 32768 field 1 hasmask 0 length 4 value 
3> OXM <class 32768 field 6 hasmask 0 length 2 value (VLAN 1) 4097> arp who-has 
172.23.1.60 tell 172.23.1.203 (DF)
  0000: 4500 00aa 1566 4000 4006 c91c ac17 01cb  E....f@.@.......
  0010: ac17 01d2 d00a 19e9 7807 405d 5188 f7a1  ........x.@]Q...
  0020: 8018 81de 0404 0000 0101 080a 009a 4c50  ..............LP
  0030: 9d26 b680 040a 0076 0000 0ab3 ffff ffff  .&.....v........
  0040: 003c 0064 0000 0000 0000 0000 0001 001a  .<.d............
  0050: 8000 0004 0000 0003 8000 0204 0000 0003  ................
  0060: 8000 0c02 1001 0000 0000 0000 0000 ffff  ................
  0070: ffff ffff 38ea a771 1441 0806 0001 0800  ....8..q.A......
  0080: 0604 0001 38ea a771 1441 ac17 01cb 0000  ....8..q.A......
  0090: 0000 0000 ac17 013c 0000 0000 0000 0000  .......<........
  00a0: 0000 0000 0000 0000 0000                 ..........

12:14:14.100922 172.23.1.210.6633 > 172.23.1.203.53258: P 117:217(100) ack 167 
win 271 <nop,nop,timestamp 2636560006 10112080> OpenFlow(ver 0x4 type 13 len 
100 xid 4) PACKET-OUT <buffer_id 4294967295 in_port 3 actions_len 16> ACTION 
<type 0 length 16 port 4294967291 max_len 65535> arp who-has 172.23.1.60 tell 
172.23.1.203 (DF)
  0000: 4500 0098 d934 4000 4006 0560 ac17 01d2  E....4@.@..`....
  0010: ac17 01cb 19e9 d00a 5188 f7a1 7807 40d3  ........Q...x.@.
  0020: 8018 010f 2dd6 0000 0101 080a 9d26 b686  ....-........&..
  0030: 009a 4c50 040d 0064 0000 0004 ffff ffff  ..LP...d........
  0040: 0000 0003 0010 0000 0000 0000 0000 0010  ................
  0050: ffff fffb ffff 0000 0000 0000 ffff ffff  ................
  0060: ffff 38ea a771 1441 0806 0001 0800 0604  ..8..q.A........
  0070: 0001 38ea a771 1441 ac17 01cb 0000 0000  ..8..q.A........
  0080: 0000 ac17 013c 0000 0000 0000 0000 0000  .....<..........
  0090: 0000 0000 0000 0000                      ........

12:14:15.092623 172.23.1.203.53258 > 172.23.1.210.6633: P 167:285(118) ack 217 
win 33196 <nop,nop,timestamp 10113080 2636560006> OpenFlow(ver 0x4 type 10 len 
118 xid 2740) PACKET-IN <buffer_id 4294967295 total_len 60 reason 0 table_id 
100 cookie 0000000000000000 match type 1 length 26> OXM <class 32768 field 0 
hasmask 0 length 4 value 3> OXM <class 32768 field 1 hasmask 0 length 4 value 
3> OXM <class 32768 field 6 hasmask 0 length 2 value (VLAN 1) 4097> arp who-has 
172.23.1.60 tell 172.23.1.203 (DF)
  0000: 4500 00aa 1568 4000 4006 c91a ac17 01cb  E....h@.@.......
  0010: ac17 01d2 d00a 19e9 7807 40d3 5188 f805  ........x.@.Q...
  0020: 8018 81ac ff6c 0000 0101 080a 009a 5038  .....l........P8
  0030: 9d26 b686 040a 0076 0000 0ab4 ffff ffff  .&.....v........
  0040: 003c 0064 0000 0000 0000 0000 0001 001a  .<.d............
  0050: 8000 0004 0000 0003 8000 0204 0000 0003  ................
  0060: 8000 0c02 1001 0000 0000 0000 0000 ffff  ................
  0070: ffff ffff 38ea a771 1441 0806 0001 0800  ....8..q.A......
  0080: 0604 0001 38ea a771 1441 ac17 01cb 0000  ....8..q.A......
  0090: 0000 0000 ac17 013c 0000 0000 0000 0000  .......<........
  00a0: 0000 0000 0000 0000 0000                 ..........

12:14:15.094468 172.23.1.210.6633 > 172.23.1.203.53258: P 217:317(100) ack 285 
win 271 <nop,nop,timestamp 2636560008 10113080> OpenFlow(ver 0x4 type 13 len 
100 xid 5) PACKET-OUT <buffer_id 4294967295 in_port 3 actions_len 16> ACTION 
<type 0 length 16 port 4294967291 max_len 65535> arp who-has 172.23.1.60 tell 
172.23.1.203 (DF)
  0000: 4500 0098 6ff7 4000 4006 6e9d ac17 01d2  E...o.@.@.n.....
  0010: ac17 01cb 19e9 d00a 5188 f805 7807 4149  ........Q...x.AI
  0020: 8018 010f 2911 0000 0101 080a 9d26 b688  ....)........&..
  0030: 009a 5038 040d 0064 0000 0005 ffff ffff  ..P8...d........
  0040: 0000 0003 0010 0000 0000 0000 0000 0010  ................
  0050: ffff fffb ffff 0000 0000 0000 ffff ffff  ................
  0060: ffff 38ea a771 1441 0806 0001 0800 0604  ..8..q.A........
  0070: 0001 38ea a771 1441 ac17 01cb 0000 0000  ..8..q.A........
  0080: 0000 ac17 013c 0000 0000 0000 0000 0000  .....<..........
  0090: 0000 0000 0000 0000                      ........
12:14:16.092553 172.23.1.203.53258 > 172.23.1.210.6633: P 285:403(118) ack 317 
win 33146 <nop,nop,timestamp 10114080 2636560008> OpenFlow(ver 0x4 type 10 len 
118 xid 2741) PACKET-IN <buffer_id 4294967295 total_len 60 reason 0 table_id 
100 cookie 0000000000000000 match type 1 length 26> OXM <class 32768 field 0 
hasmask 0 length 4 value 3> OXM <class 32768 field 1 hasmask 0 length 4 value 
3> OXM <class 32768 field 6 hasmask 0 length 2 value (VLAN 1) 4097> arp who-has 
172.23.1.60 tell 172.23.1.203 (DF)
  0000: 4500 00aa 156a 4000 4006 c918 ac17 01cb  E....j@.@.......
  0010: ac17 01d2 d00a 19e9 7807 4149 5188 f869  ........x.AIQ..i
  0020: 8018 817a fad9 0000 0101 080a 009a 5420  ...z..........T
  0030: 9d26 b688 040a 0076 0000 0ab5 ffff ffff  .&.....v........
  0040: 003c 0064 0000 0000 0000 0000 0001 001a  .<.d............
  0050: 8000 0004 0000 0003 8000 0204 0000 0003  ................
  0060: 8000 0c02 1001 0000 0000 0000 0000 ffff  ................
  0070: ffff ffff 38ea a771 1441 0806 0001 0800  ....8..q.A......
  0080: 0604 0001 38ea a771 1441 ac17 01cb 0000  ....8..q.A......
  0090: 0000 0000 ac17 013c 0000 0000 0000 0000  .......<........
  00a0: 0000 0000 0000 0000 0000                 ..........

12:14:16.094336 172.23.1.210.6633 > 172.23.1.203.53258: P 317:417(100) ack 403 
win 271 <nop,nop,timestamp 2636560010 10114080> OpenFlow(ver 0x4 type 13 len 
100 xid 6) PACKET-OUT <buffer_id 4294967295 in_port 3 actions_len 16> ACTION 
<type 0 length 16 port 4294967291 max_len 65535> arp who-has 172.23.1.60 tell 
172.23.1.203 (DF)
  0000: 4500 0098 10b0 4000 4006 cde4 ac17 01d2  E.....@.@.......
  0010: ac17 01cb 19e9 d00a 5188 f869 7807 41bf  ........Q..ix.A.
  0020: 8018 010f 244c 0000 0101 080a 9d26 b68a  ....$L.......&..
  0030: 009a 5420 040d 0064 0000 0006 ffff ffff  ..T ...d........
  0040: 0000 0003 0010 0000 0000 0000 0000 0010  ................
  0050: ffff fffb ffff 0000 0000 0000 ffff ffff  ................
  0060: ffff 38ea a771 1441 0806 0001 0800 0604  ..8..q.A........
  0070: 0001 38ea a771 1441 ac17 01cb 0000 0000  ..8..q.A........
  0080: 0000 ac17 013c 0000 0000 0000 0000 0000  .....<..........
  0090: 0000 0000 0000 0000                      ........

---snip---

And here is the diff.

ok?

Index: Makefile
===================================================================
RCS file: /home/obsdcvs/src/usr.sbin/tcpdump/Makefile,v
retrieving revision 1.59
diff -u -p -r1.59 Makefile
--- Makefile    14 Oct 2015 04:55:17 -0000      1.59
+++ Makefile    18 Oct 2016 10:19:50 -0000
@@ -47,7 +47,7 @@ SRCS= tcpdump.c addrtoname.c privsep.c p
        print-ip6.c print-ip6opts.c print-icmp6.c print-dhcp6.c print-frag6.c \
        print-bgp.c print-ospf6.c print-ripng.c print-rt6.c print-stp.c \
        print-etherip.c print-lwres.c print-lldp.c print-cdp.c print-pflog.c \
-       print-pfsync.c pf_print_state.c \
+       print-pfsync.c pf_print_state.c print-ofp.c \
        print-udpencap.c print-carp.c \
        print-802_11.c print-iapp.c print-mpls.c print-slow.c \
        gmt2local.c savestr.c setsignal.c in_cksum.c
Index: interface.h
===================================================================
RCS file: /home/obsdcvs/src/usr.sbin/tcpdump/interface.h,v
retrieving revision 1.67
diff -u -p -r1.67 interface.h
--- interface.h 11 Jul 2016 00:27:50 -0000      1.67
+++ interface.h 18 Oct 2016 10:18:54 -0000
@@ -274,6 +274,7 @@ extern void mpls_print(const u_char *, u
 extern void lldp_print(const u_char *, u_int);
 extern void slow_print(const u_char *, u_int);
 extern void gtp_print(const u_char *, u_int, u_short, u_short);
+extern void ofp_print(const u_char *, u_int);
 
 #ifdef INET6
 extern void ip6_print(const u_char *, u_int);
Index: print-tcp.c
===================================================================
RCS file: /home/obsdcvs/src/usr.sbin/tcpdump/print-tcp.c,v
retrieving revision 1.35
diff -u -p -r1.35 print-tcp.c
--- print-tcp.c 16 Nov 2015 00:16:39 -0000      1.35
+++ print-tcp.c 18 Oct 2016 16:11:37 -0000
@@ -123,6 +123,10 @@ static struct tcp_seq_hash tcp_seq_hash[
 #endif
 #define NETBIOS_SSN_PORT 139
 
+/* OpenFlow TCP ports. */
+#define OLD_OFP_PORT   6633
+#define OFP_PORT       6653
+
 static int tcp_cksum(const struct ip *ip, const struct tcphdr *tp, int len)
 {
        union phu {
@@ -665,6 +669,9 @@ tcp_print(const u_char *bp, u_int length
        } else {
                if (sport == BGP_PORT || dport == BGP_PORT)
                        bgp_print(bp, length);
+               else if (sport == OLD_OFP_PORT || dport == OLD_OFP_PORT ||
+                   sport == OFP_PORT || dport == OFP_PORT)
+                       ofp_print(bp, length);
 #if 0
                else if (sport == NETBIOS_SSN_PORT || dport == NETBIOS_SSN_PORT)
                        nbt_tcp_print(bp, length);
--- /dev/null   Wed Oct 19 12:02:31 2016
+++ print-ofp.c Wed Oct 19 12:00:39 2016
@@ -0,0 +1,912 @@
+/*     $$      */
+
+/*
+ * Copyright (c) 2016 Rafael Zalamena <rzalam...@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <net/ofp.h>
+
+#include <endian.h>
+#include <stddef.h>
+#include <stdio.h>
+
+#include "interface.h"
+
+/* Size of action header without the padding. */
+#define AH_UNPADDED    (offsetof(struct ofp_action_header, ah_pad))
+
+void            ofp_print_hello(const u_char *, u_int, u_int);
+void            ofp_print_featuresreply(const u_char *, u_int);
+void            ofp_print_setconfig(const u_char *, u_int);
+void            ofp_print_packetin(const u_char *, u_int);
+void            ofp_print_packetout(const u_char *, u_int);
+void            ofp_print_flowremoved(const u_char *, u_int);
+
+void            oxm_print_halfword(const u_char *, u_int, int, int);
+void            oxm_print_word(const u_char *, u_int, int, int);
+void            oxm_print_quad(const u_char *, u_int, int, int);
+void            oxm_print_ether(const u_char *, u_int, int);
+void            ofp_print_oxm(struct ofp_ox_match *, const u_char *, u_int);
+
+void            action_print_output(const u_char *, u_int);
+void            action_print_group(const u_char *, u_int);
+void            action_print_setmplsttl(const u_char *, u_int);
+void            action_print_setnwttl(const u_char *, u_int);
+void            action_print_push(const u_char *, u_int);
+void            action_print_popmpls(const u_char *, u_int);
+void            action_print_setfield(const u_char *, u_int);
+void            ofp_print_action(struct ofp_action_header *, const u_char *, 
u_int);
+
+void
+ofp_print_hello(const u_char *bp, u_int length, u_int ohlen)
+{
+       struct ofp_hello_element_header         *he;
+       int                                      hetype, helen, ver, i;
+       int                                      vmajor, vminor;
+       uint32_t                                 bmp;
+
+       /* Skip the OFP header. */
+       bp += sizeof(struct ofp_header);
+       length -= sizeof(struct ofp_header);
+
+       printf(" HELLO");
+
+       /* Check for header truncation. */
+       if (ohlen > sizeof(struct ofp_header) &&
+           length < sizeof(*he)) {
+               printf(" truncated header");
+               return;
+       }
+
+ next_header:
+       /* Check for hello element headers. */
+       if (length < sizeof(*he))
+               return;
+
+       he = (struct ofp_hello_element_header *)bp;
+       hetype = ntohs(he->he_type);
+       helen = ntohs(he->he_length);
+
+       bp += sizeof(*he);
+       length -= sizeof(*he);
+       helen -= sizeof(*he);
+
+       switch (hetype) {
+       case OPF_HELLO_T_VERSION_BITMAP:
+               printf(" version bitmap <");
+               if (helen < sizeof(bmp)) {
+                       printf("invalid header>");
+                       break;
+               }
+
+ next_bitmap:
+               if (length < sizeof(bmp)) {
+                       printf("truncated>");
+                       break;
+               }
+
+               bmp = ntohl(*(uint32_t *)bp);
+               for (i = 0, ver = 9; i < 32; i++, ver++) {
+                       if ((bmp & (1 << i)) == 0)
+                               continue;
+
+                       vmajor = (ver / 10);
+                       vminor = ver % 10;
+                       printf("v%d.%d ", vmajor, vminor);
+               }
+               helen -= min(sizeof(bmp), helen);
+               length -= sizeof(bmp);
+               bp += sizeof(bmp);
+               if (helen)
+                       goto next_bitmap;
+
+               printf("\b>");
+               break;
+
+       default:
+               printf(" element header[type %d length %d]", hetype, helen);
+               break;
+       }
+
+       length -= min(helen, length);
+       bp += helen;
+       if (length)
+               goto next_header;
+}
+
+void
+ofp_print_error(const u_char *bp, u_int length)
+{
+       struct ofp_error                        *err;
+
+       printf(" ERROR");
+       if (length < sizeof(*err)) {
+               printf(" [truncated]");
+               return;
+       }
+
+       err = (struct ofp_error *)bp;
+       printf(" <type %d code %d>", ntohs(err->err_type),
+           ntohs(err->err_code));
+
+       length -= min(sizeof(*err), length);
+       bp += sizeof(*err);
+       /* If there are still bytes left, print the optional error data. */
+       if (length) {
+               printf(" error data:");
+               ofp_print(bp, length);
+       }
+}
+
+void
+ofp_print_featuresreply(const u_char *bp, u_int length)
+{
+       struct ofp_switch_features              *swf;
+
+       printf(" FEATURES REPLY");
+       if (length < sizeof(*swf)) {
+               printf(" [trucanted]");
+               return;
+       }
+
+       swf = (struct ofp_switch_features *)bp;
+       printf(" <datapath_id %#016llx nbuffers %u ntables %d aux_id %d "
+           "capabilities %#08x actions %#08x>",
+           be64toh(swf->swf_datapath_id), ntohl(swf->swf_nbuffers),
+           swf->swf_ntables, swf->swf_aux_id,
+           ntohl(swf->swf_capabilities), ntohl(swf->swf_actions));
+}
+
+void
+ofp_print_setconfig(const u_char *bp, u_int length)
+{
+       struct ofp_switch_config                *cfg;
+
+       printf(" SET CONFIG");
+       if (length < sizeof(*cfg)) {
+               printf(" [truncated]");
+               return;
+       }
+
+       cfg = (struct ofp_switch_config *)bp;
+       printf(" <flags %#04x miss_send_len %d>",
+           ntohs(cfg->cfg_flags), ntohs(cfg->cfg_miss_send_len));
+}
+
+void
+ofp_print_packetin(const u_char *bp, u_int length)
+{
+       struct ofp_packet_in            *pin;
+       struct ofp_ox_match             *oxm;
+       int                              omtype, omlen;
+       int                              haspacket = 0;
+       const u_char                    *pktptr;
+
+       printf(" PACKET-IN");
+       if (length < sizeof(*pin)) {
+               printf(" [truncated]");
+               return;
+       }
+
+       pin = (struct ofp_packet_in *)bp;
+       omtype = ntohs(pin->pin_match.om_type);
+       omlen = ntohs(pin->pin_match.om_length);
+       printf(" <buffer_id %u total_len %d reason %d table_id %d "
+           "cookie %#016llx match type %d length %d>",
+           ntohl(pin->pin_buffer_id), ntohs(pin->pin_total_len),
+           pin->pin_reason, pin->pin_table_id, be64toh(pin->pin_cookie),
+           omtype, omlen);
+
+       if (pin->pin_buffer_id == OFP_PKTOUT_NO_BUFFER)
+               haspacket = 1;
+
+       /* We only support OXM. */
+       if (omtype != OFP_MATCH_OXM)
+               return;
+
+       bp += sizeof(*pin);
+       length -= sizeof(*pin);
+
+       /* Get packet start address. */
+       pktptr = (bp - sizeof(pin->pin_match)) +
+           OFP_ALIGN(omlen) + ETHER_ALIGN;
+
+       /* Don't count the header for the OXM fields. */
+       omlen -= min(sizeof(pin->pin_match), omlen);
+       if (omlen == 0)
+               goto print_packet;
+
+ parse_next_oxm:
+       if (length < sizeof(*oxm)) {
+               printf("[truncated]");
+               return;
+       }
+
+       oxm = (struct ofp_ox_match *)bp;
+       bp += sizeof(*oxm);
+       length -= sizeof(*oxm);
+       if (length < oxm->oxm_length) {
+               printf("[truncated]");
+               return;
+       }
+
+       ofp_print_oxm(oxm, bp, length);
+
+       bp += oxm->oxm_length;
+       length -= oxm->oxm_length;
+       omlen -= min(sizeof(*oxm) + oxm->oxm_length, omlen);
+       if (omlen)
+               goto parse_next_oxm;
+
+ print_packet:
+       if (haspacket == 0)
+               return;
+
+       /*
+        * Recalculate length:
+        * pktptr skipped the omlen + padding and the ETHER_ALIGN, so
+        * instead of keeping track of that we just recalculate length
+        * using the encapsulated packet begin and snapend.
+        */
+       length = max(snapend - pktptr, 0);
+       if (length < ETHER_ADDR_LEN) {
+               printf(" [|ether]");
+               return;
+       }
+
+       printf(" ");
+       ether_tryprint(pktptr, length, 0);
+}
+
+void
+ofp_print_flowremoved(const u_char *bp, u_int length)
+{
+       struct ofp_flow_removed                 *fr;
+       struct ofp_ox_match                     *oxm;
+       int                                      omtype, omlen;
+
+       printf(" FLOW REMOVED");
+       if (length < sizeof(*fr)) {
+               printf("[truncated]");
+               return;
+       }
+
+       fr = (struct ofp_flow_removed *)bp;
+       omtype = ntohs(fr->fr_match.om_type);
+       omlen = ntohs(fr->fr_match.om_length);
+       printf(" cookie %#016llx priority %d reason %d table_id %d "
+           "duration sec %u nsec %u timeout idle %d hard %d "
+           "packet count %llu byte count %llu match type %d length %d",
+           be64toh(fr->fr_cookie), ntohs(fr->fr_priority), fr->fr_reason,
+           fr->fr_table_id, ntohl(fr->fr_duration_sec),
+           ntohl(fr->fr_duration_nsec), ntohs(fr->fr_idle_timeout),
+           ntohs(fr->fr_hard_timeout), be64toh(fr->fr_packet_count),
+           be64toh(fr->fr_byte_count), omtype, omlen);
+
+       /* We only support OXM. */
+       if (omtype != OFP_MATCH_OXM)
+               return;
+
+       omlen -= min(sizeof(fr->fr_match), omlen);
+       if (omlen == 0)
+               return;
+
+       bp += sizeof(*fr);
+       length -= sizeof(*fr);
+
+ parse_next_oxm:
+       if (length < sizeof(*oxm)) {
+               printf("[truncated]");
+               return;
+       }
+
+       oxm = (struct ofp_ox_match *)bp;
+       bp += sizeof(*oxm);
+       length -= sizeof(*oxm);
+       if (length < oxm->oxm_length) {
+               printf("[truncated]");
+               return;
+       }
+
+       ofp_print_oxm(oxm, bp, length);
+
+       bp += oxm->oxm_length;
+       length -= oxm->oxm_length;
+       omlen -= min(sizeof(*oxm) + oxm->oxm_length, omlen);
+       if (omlen)
+               goto parse_next_oxm;
+}
+
+void
+ofp_print_packetout(const u_char *bp, u_int length)
+{
+       struct ofp_packet_out                   *pout;
+       struct ofp_action_header                *ah;
+       const u_char                            *pktptr;
+       int                                      actionslen, haspacket = 0;
+       int                                      ahlen;
+
+       printf(" PACKET-OUT");
+       if (length < sizeof(*pout)) {
+               printf("[truncated]");
+               return;
+       }
+
+       pout = (struct ofp_packet_out *)bp;
+       actionslen = ntohs(pout->pout_actions_len);
+       printf(" <buffer_id %u in_port %u actions_len %d>",
+           ntohl(pout->pout_buffer_id), ntohl(pout->pout_in_port),
+           actionslen);
+
+       if (pout->pout_buffer_id == OFP_PKTOUT_NO_BUFFER)
+               haspacket = 1;
+
+       bp += sizeof(*pout);
+       length -= sizeof(*pout);
+       pktptr = bp + actionslen;
+
+       /* No actions or unpadded header. */
+       if (actionslen < sizeof(*ah))
+               goto print_packet;
+
+ parse_next_action:
+       if (length < sizeof(*ah)) {
+               printf("[truncated]");
+               return;
+       }
+
+       ah = (struct ofp_action_header *)bp;
+       bp += AH_UNPADDED;
+       length -= AH_UNPADDED;
+       actionslen -= AH_UNPADDED;
+       ahlen = ntohs(ah->ah_len) - AH_UNPADDED;
+       if (length < ahlen) {
+               printf("[truncated]");
+               return;
+       }
+
+       ofp_print_action(ah, bp, length);
+
+       bp += ahlen;
+       length -= ahlen;
+       actionslen -= min(ahlen, actionslen);
+       if (actionslen)
+               goto parse_next_action;
+
+ print_packet:
+       if (haspacket == 0)
+               return;
+
+       /* Recalculate length using packet boundaries. */
+       length = max(snapend - pktptr, 0);
+       if (length < ETHER_ADDR_LEN) {
+               printf(" [|ether]");
+               return;
+       }
+
+       printf(" ");
+       ether_tryprint(pktptr, length, 0);
+}
+
+void
+ofp_print(const u_char *bp, u_int length)
+{
+       struct ofp_header                       *oh;
+       unsigned int                             ohlen;
+
+       if (length < sizeof(*oh)) {
+               printf("OpenFlow(truncated)");
+               return;
+       }
+
+       oh = (struct ofp_header *)bp;
+       ohlen = ntohs(oh->oh_length);
+       printf(" OpenFlow(ver %#02x type %d len %d xid %u)",
+           oh->oh_version, oh->oh_type, ohlen, ntohl(oh->oh_xid));
+
+       switch (oh->oh_version) {
+       case OFP_V_1_3:
+               break;
+
+       default:
+               return;
+       }
+
+       switch (oh->oh_type) {
+       case OFP_T_HELLO:
+               ofp_print_hello(bp, length, ohlen);
+               break;
+       case OFP_T_ERROR:
+               ofp_print_error(bp, length);
+               break;
+       case OFP_T_ECHO_REQUEST:
+               printf(" ECHO REQUEST");
+               break;
+       case OFP_T_ECHO_REPLY:
+               printf(" ECHO REPLY");
+               break;
+       case OFP_T_FEATURES_REQUEST:
+               printf(" FEATURES REQUEST");
+               break;
+       case OFP_T_FEATURES_REPLY:
+               ofp_print_featuresreply(bp, length);
+               break;
+       case OFP_T_SET_CONFIG:
+               ofp_print_setconfig(bp, length);
+               break;
+       case OFP_T_PACKET_IN:
+               ofp_print_packetin(bp, length);
+               break;
+       case OFP_T_FLOW_REMOVED:
+               ofp_print_flowremoved(bp, length);
+               break;
+       case OFP_T_PACKET_OUT:
+               ofp_print_packetout(bp, length);
+               break;
+       }
+}
+
+void
+oxm_print_byte(const u_char *bp, u_int length, int hasmask, int hex)
+{
+       uint8_t         *b;
+
+       if (length < sizeof(*b)) {
+               printf("[truncated]");
+               return;
+       }
+
+       b = (uint8_t *)bp;
+       if (hex)
+               printf("%#02x", ntohs(*b));
+       else
+               printf("%u", ntohs(*b));
+
+       if (hasmask) {
+               bp += sizeof(*b);
+               length -= sizeof(*b);
+               printf(" mask ");
+               oxm_print_word(bp, length, 0, 1);
+       }
+}
+
+void
+oxm_print_halfword(const u_char *bp, u_int length, int hasmask, int hex)
+{
+       uint16_t        *h;
+
+       if (length < sizeof(*h)) {
+               printf("[truncated]");
+               return;
+       }
+
+       h = (uint16_t *)bp;
+       if (hex)
+               printf("%#04x", ntohs(*h));
+       else
+               printf("%u", ntohs(*h));
+
+       if (hasmask) {
+               bp += sizeof(*h);
+               length -= sizeof(*h);
+               printf(" mask ");
+               oxm_print_word(bp, length, 0, 1);
+       }
+}
+
+void
+oxm_print_word(const u_char *bp, u_int length, int hasmask, int hex)
+{
+       uint32_t        *w;
+
+       if (length < sizeof(*w)) {
+               printf("[truncated]");
+               return;
+       }
+
+       w = (uint32_t *)bp;
+       if (hex)
+               printf("%#08x", ntohl(*w));
+       else
+               printf("%u", ntohl(*w));
+
+       if (hasmask) {
+               bp += sizeof(*w);
+               length -= sizeof(*w);
+               printf(" mask ");
+               oxm_print_word(bp, length, 0, 1);
+       }
+}
+
+void
+oxm_print_quad(const u_char *bp, u_int length, int hasmask, int hex)
+{
+       uint64_t        *q;
+
+       if (length < sizeof(*q)) {
+               printf("[truncated]");
+               return;
+       }
+
+       q = (uint64_t *)bp;
+       if (hex)
+               printf("%#016llx", be64toh(*q));
+       else
+               printf("%llu", be64toh(*q));
+
+       if (hasmask) {
+               bp += sizeof(*q);
+               length -= sizeof(*q);
+               printf(" mask ");
+               oxm_print_quad(bp, length, 0, 1);
+       }
+}
+
+void
+oxm_print_ether(const u_char *bp, u_int length, int hasmask)
+{
+       uint8_t         *ptr;
+       int              i;
+       char             hex[8];
+
+       if (length < ETHER_HDR_LEN) {
+               printf("[truncated]");
+               return;
+       }
+
+       ptr = (uint8_t *)bp;
+       for (i = 0; i < ETHER_ADDR_LEN; i++) {
+               if (i)
+                       printf(":");
+
+               snprintf(hex, sizeof(hex), "%02x", ptr[i]);
+               printf("%s", hex);
+       }
+
+       if (hasmask) {
+               bp += ETHER_ADDR_LEN;
+               length -= ETHER_ADDR_LEN;
+               printf(" mask ");
+               oxm_print_ether(bp, length, 0);
+       }
+}
+
+void
+oxm_print_data(const u_char *bp, u_int length, int hasmask, size_t datalen)
+{
+       uint8_t         *ptr;
+       int              i;
+       char             hex[8];
+
+       if (length < datalen) {
+               printf("[truncated]");
+               return;
+       }
+
+       ptr = (uint8_t *)bp;
+       for (i = 0; i < datalen; i++) {
+               snprintf(hex, sizeof(hex), "%02x", ptr[i]);
+               printf("%s", hex);
+       }
+
+       if (hasmask) {
+               bp += datalen;
+               length -= datalen;
+               printf(" mask ");
+               oxm_print_data(bp, length, 0, datalen);
+       }
+}
+
+void
+ofp_print_oxm(struct ofp_ox_match *oxm, const u_char *bp, u_int length)
+{
+       int                              class, field, mask, len;
+       uint16_t                        *vlan;
+
+       class = ntohs(oxm->oxm_class);
+       field = OFP_OXM_GET_FIELD(oxm);
+       mask = OFP_OXM_GET_HASMASK(oxm);
+       len = oxm->oxm_length;
+       printf(" OXM <class %d field %d hasmask %d length %d",
+           class, field, mask, len);
+
+       switch (class) {
+       case OFP_OXM_C_OPENFLOW_BASIC:
+               break;
+
+       case OFP_OXM_C_NXM_0:
+       case OFP_OXM_C_NXM_1:
+       case OFP_OXM_C_OPENFLOW_EXPERIMENTER:
+       default:
+               printf(">");
+               return;
+       }
+
+       printf(" value ");
+
+       switch (field) {
+       case OFP_XM_T_IN_PORT:
+       case OFP_XM_T_IN_PHY_PORT:
+       case OFP_XM_T_MPLS_LABEL:
+               oxm_print_word(bp, length, mask, 0);
+               break;
+
+       case OFP_XM_T_META:
+       case OFP_XM_T_TUNNEL_ID:
+               oxm_print_quad(bp, length, mask, 1);
+               break;
+
+       case OFP_XM_T_ETH_DST:
+       case OFP_XM_T_ETH_SRC:
+       case OFP_XM_T_ARP_SHA:
+       case OFP_XM_T_ARP_THA:
+       case OFP_XM_T_IPV6_ND_SLL:
+       case OFP_XM_T_IPV6_ND_TLL:
+               oxm_print_ether(bp, length, mask);
+               break;
+
+       case OFP_XM_T_ETH_TYPE:
+               oxm_print_halfword(bp, length, mask, 1);
+               break;
+
+       case OFP_XM_T_VLAN_VID:
+               /*
+                * VLAN has an exception: it uses the higher bits to signal
+                * the presence of the VLAN.
+                */
+               if (length < sizeof(*vlan)) {
+                       printf("[truncated]");
+                       break;
+               }
+
+               vlan = (uint16_t *)bp;
+               if (ntohs(*vlan) & OFP_XM_VID_PRESENT)
+                       printf("(VLAN %d) ",
+                           ntohs(*vlan) & (~OFP_XM_VID_PRESENT));
+               else
+                       printf("(no VLAN) ");
+               /* FALLTHROUGH */               
+       case OFP_XM_T_TCP_SRC:
+       case OFP_XM_T_TCP_DST:
+       case OFP_XM_T_UDP_SRC:
+       case OFP_XM_T_UDP_DST:
+       case OFP_XM_T_SCTP_SRC:
+       case OFP_XM_T_SCTP_DST:
+       case OFP_XM_T_ARP_OP:
+       case OFP_XM_T_IPV6_EXTHDR:
+               oxm_print_halfword(bp, length, mask, 0);
+               break;
+
+       case OFP_XM_T_VLAN_PCP:
+       case OFP_XM_T_IP_DSCP:
+       case OFP_XM_T_IP_ECN:
+       case OFP_XM_T_MPLS_TC:
+       case OFP_XM_T_MPLS_BOS:
+               oxm_print_byte(bp, length, mask, 1);
+               break;
+
+       case OFP_XM_T_IPV4_SRC:
+       case OFP_XM_T_IPV4_DST:
+       case OFP_XM_T_ARP_SPA:
+       case OFP_XM_T_ARP_TPA:
+       case OFP_XM_T_IPV6_FLABEL:
+               oxm_print_word(bp, length, mask, 1);
+               break;
+
+       case OFP_XM_T_IP_PROTO:
+       case OFP_XM_T_ICMPV4_TYPE:
+       case OFP_XM_T_ICMPV4_CODE:
+       case OFP_XM_T_ICMPV6_TYPE:
+       case OFP_XM_T_ICMPV6_CODE:
+               oxm_print_byte(bp, length, mask, 0);
+               break;
+
+       case OFP_XM_T_IPV6_SRC:
+       case OFP_XM_T_IPV6_DST:
+       case OFP_XM_T_IPV6_ND_TARGET:
+               oxm_print_data(bp, length, mask, sizeof(struct in6_addr));
+               break;
+
+       case OFP_XM_T_PBB_ISID:
+               oxm_print_data(bp, length, mask, 3);
+               break;
+
+       default:
+               printf("unknown");
+               break;
+       }
+
+       printf(">");
+}
+
+void
+action_print_output(const u_char *bp, u_int length)
+{
+       struct ofp_action_output                *ao;
+
+       if (length < (sizeof(*ao) - AH_UNPADDED)) {
+               printf("[truncated]");
+               return;
+       }
+
+       ao = (struct ofp_action_output *)(bp - AH_UNPADDED);
+       printf(" port %u max_len %d",
+           ntohl(ao->ao_port), ntohs(ao->ao_max_len));
+}
+
+void
+action_print_group(const u_char *bp, u_int length)
+{
+       struct ofp_action_group         *ag;
+       
+       if (length < (sizeof(*ag) - AH_UNPADDED)) {
+               printf("[truncated]");
+               return;
+       }
+
+       ag = (struct ofp_action_group *)(bp - AH_UNPADDED);
+       printf(" group_id %u", ntohl(ag->ag_group_id));
+}
+
+void
+action_print_setmplsttl(const u_char *bp, u_int length)
+{
+       struct ofp_action_mpls_ttl      *amt;
+
+       if (length < (sizeof(*amt) - AH_UNPADDED)) {
+               printf("[truncated]");
+               return;
+       }
+
+       amt = (struct ofp_action_mpls_ttl *)(bp - AH_UNPADDED);
+       printf(" ttl %d", amt->amt_ttl);
+}
+
+void
+action_print_setnwttl(const u_char *bp, u_int length)
+{
+       struct ofp_action_nw_ttl        *ant;
+
+       if (length < (sizeof(*ant) - AH_UNPADDED)) {
+               printf("[truncated]");
+               return;
+       }
+
+       ant = (struct ofp_action_nw_ttl *)(bp - AH_UNPADDED);
+       printf(" ttl %d", ant->ant_ttl);
+}
+
+void
+action_print_push(const u_char *bp, u_int length)
+{
+       struct ofp_action_push          *ap;
+
+       if (length < (sizeof(*ap) - AH_UNPADDED)) {
+               printf("[truncated]");
+               return;
+       }
+
+       ap = (struct ofp_action_push *)(bp - AH_UNPADDED);
+       printf(" ethertype %#04x", ntohs(ap->ap_ethertype));
+}
+
+void
+action_print_popmpls(const u_char *bp, u_int length)
+{
+       struct ofp_action_pop_mpls      *apm;
+
+       if (length < (sizeof(*apm) - AH_UNPADDED)) {
+               printf("[truncated]");
+               return;
+       }
+
+       apm = (struct ofp_action_pop_mpls *)(bp - AH_UNPADDED);
+       printf(" ethertype %#04x", ntohs(apm->apm_ethertype));
+}
+
+void
+action_print_setfield(const u_char *bp, u_int length)
+{
+       struct ofp_action_set_field     *asf;
+       struct ofp_ox_match             *oxm;
+       int                              omlen;
+
+       if (length < (sizeof(*asf) - AH_UNPADDED)) {
+               printf("[truncated]");
+               return;
+       }
+
+       asf = (struct ofp_action_set_field *)(bp - AH_UNPADDED);
+       omlen = ntohs(asf->asf_len) - AH_UNPADDED;
+
+ parse_next_oxm:
+       if (length < sizeof(*oxm)) {
+               printf("[truncated]");
+               return;
+       }
+
+       oxm = (struct ofp_ox_match *)bp;
+       bp += sizeof(*oxm);
+       length -= sizeof(*oxm);
+       if (length < oxm->oxm_length) {
+               printf("[truncated]");
+               return;
+       }
+
+       ofp_print_oxm(oxm, bp, length);
+
+       bp += oxm->oxm_length;
+       length -= oxm->oxm_length;
+       omlen -= min(sizeof(*oxm) + oxm->oxm_length, omlen);
+       if (omlen)
+               goto parse_next_oxm;
+}
+
+void
+ofp_print_action(struct ofp_action_header *ah, const u_char *bp, u_int length)
+{
+       int                     ahtype;
+
+       ahtype = ntohs(ah->ah_type);
+       printf(" ACTION <type %d length %d", ahtype, ntohs(ah->ah_len));
+
+       switch (ahtype) {
+       case OFP_ACTION_OUTPUT:
+               action_print_output(bp, length);
+               break;
+
+       case OFP_ACTION_GROUP:
+               action_print_group(bp, length);
+               break;
+
+       case OFP_ACTION_SET_QUEUE:
+               /* TODO missing struct in ofp.h header. */
+               break;
+
+       case OFP_ACTION_SET_MPLS_TTL:
+               action_print_setmplsttl(bp, length);
+               break;
+
+       case OFP_ACTION_SET_NW_TTL:
+               action_print_setnwttl(bp, length);
+               break;
+
+       case OFP_ACTION_PUSH_VLAN:
+       case OFP_ACTION_PUSH_MPLS:
+       case OFP_ACTION_PUSH_PBB:
+               action_print_push(bp, length);
+               break;
+
+       case OFP_ACTION_POP_MPLS:
+               action_print_popmpls(bp, length);
+               break;
+
+       case OFP_ACTION_SET_FIELD:
+               break;
+
+       case OFP_ACTION_COPY_TTL_OUT:
+       case OFP_ACTION_COPY_TTL_IN:
+       case OFP_ACTION_DEC_NW_TTL:
+       case OFP_ACTION_DEC_MPLS_TTL:
+       case OFP_ACTION_POP_VLAN:
+       case OFP_ACTION_POP_PBB:
+       case OFP_ACTION_EXPERIMENTER:
+       default:
+               /* Generic header, nothing to show here. */
+               break;
+       }
+
+       printf(">");
+}

Reply via email to