This commit adds a basic packet metadata macro to the already existing macros in ovs_gdb.py, ovs_dump_packets will print out information about one or more packets. Currently, it will only extract some basic L2-4 information from Ethernet frames. However, it could easily be extended as needed for future tasks.
Example usage: (gdb) break fast_path_processing (gdb) commands >ovs_dump_packets packets_ >continue >end (gdb) continue Thread 1 "ovs-vswitchd" hit Breakpoint 1, fast_path_processing ... 9e:11:bb:29:f3:1b -> 5a:e3:39:9b:b7:e0 ARP: Who has 10.1.1.2, tell 9e:11:bb:29:f3:1b Thread 1 "ovs-vswitchd" hit Breakpoint 1, fast_path_processing ... 5a:e3:39:9b:b7:e0 -> 9e:11:bb:29:f3:1b ARP: 10.1.1.2 is at 5a:e3:39:9b:b7:e0 Signed-off-by: Mike Pattrick <m...@redhat.com> --- utilities/gdb/ovs_gdb.py | 161 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 161 insertions(+) diff --git a/utilities/gdb/ovs_gdb.py b/utilities/gdb/ovs_gdb.py index 0b2ecb81b..9e3c59f0e 100644 --- a/utilities/gdb/ovs_gdb.py +++ b/utilities/gdb/ovs_gdb.py @@ -34,6 +34,7 @@ # - ovs_dump_udpif_keys {<udpif_name>|<udpif_address>} {short} # - ovs_show_fdb {[<bridge_name>] {dbg} {hash}} # - ovs_show_upcall {dbg} +# - ovs_dump_dp_packet <struct dp_packet_batch|struct dp_packet> # # Example: # $ gdb $(which ovs-vswitchd) $(pidof ovs-vswitchd) @@ -58,6 +59,8 @@ import gdb import sys import uuid +import socket +import struct # @@ -151,6 +154,29 @@ def eth_addr_to_string(eth_addr): int(eth_addr['ea'][5])) +def ipv4_to_string(ip): + return socket.inet_ntop( + socket.AF_INET, + struct.pack( + "I", + ip.cast(gdb.lookup_type('uint32_t')) + ) + ) + + +def ipv6_to_string(ip): + ip_array = ip.cast( gdb.lookup_type('uint64_t').array(1) ) + + return socket.inet_ntop( + socket.AF_INET6, + struct.pack( + "QQ", + ip_array[0], + ip_array[1] + ) + ) + + # # Simple class to print a spinner on the console # @@ -1306,6 +1332,140 @@ class CmdDumpOfpacts(gdb.Command): print("(struct ofpact *) {}: {}".format(node, node.dereference())) +# +# Implements the GDB "ovs_dump_packets" command +# +class CmdDumpPackets(gdb.Command): + """Dump metadata about dp_packets + Usage: ovs_dump_packets <struct dp_packet_batch|struct dp_packet> + + This command can take either a dp_packet_batch struct and print out metadata + about all packets in this batch, or a single dp_packet struct and print out + metadata about a single packet. + + (gdb) ovs_dump_packets packets_ + 9e:11:bb:29:f3:1b -> ff:ff:ff:ff:ff:ff ARP: Who has 00:00:00:00:00:00, tell 9e:11:bb:29:f3:1b + """ + def __init__(self): + super().__init__("ovs_dump_packets", gdb.COMMAND_DATA) + + def invoke(self, arg, from_tty): + arg_list = gdb.string_to_argv(arg) + if len(arg_list) != 1: + print("Usage: ovs_dump_packets <dp_packet_struct>") + + val = gdb.parse_and_eval(arg_list[0]) + while val.type.code == gdb.TYPE_CODE_PTR: + val = val.dereference() + + if str(val.type).startswith("struct dp_packet_batch"): + for idx in range(val['count']): + pkt = val['packets'][idx].dereference() + parsed = self.fmt_pkt(pkt) + if parsed is not None: + print(parsed['format'] % parsed) + elif str(val.type) == "struct dp_packet": + parsed = self.fmt_pkt(val) + if parsed is not None: + print(parsed['format'] % parsed) + else: + print("Unhandled type", str(val.type)) + + def fmt_pkt(self, pkt): + eth = gdb.parse_and_eval('dp_packet_eth(%s)' % pkt.address) + # todo, check for is NULL + if eth == 0: + return None + + res_d = { + "e_src": None, + "e_dst": None, + "e_proto": None, + "i_src": None, + "i_dst": None, + "i_proto": None, + "t_src": "", + "t_dst": "", + "proto_msg": None, + "format": "Can not parse ethernet protocol %(e_proto)s" + } + + eth_hdr = eth.cast(gdb.lookup_type('struct eth_header').pointer()).dereference() + res_d['e_dst'] = eth_addr_to_string(eth_hdr['eth_dst']) + res_d['e_src'] = eth_addr_to_string(eth_hdr['eth_src']) + eth_type = socket.ntohs(eth_hdr['eth_type']) + res_d['e_proto'] = eth_type + + l3 = gdb.parse_and_eval('dp_packet_l3(%s)' % pkt.address) + if eth_type == 0x0806: + res_d['e_proto'] = "ARP" + + arp_pkt = l3.cast(gdb.lookup_type('struct arp_eth_header').pointer()).dereference() + ar_op = socket.ntohs(arp_pkt['ar_op']) + if ar_op == 1: + res_d['proto_msg'] = "Who has %s, tell %s" % ( + ipv4_to_string(arp_pkt['ar_tpa']), + eth_addr_to_string(arp_pkt['ar_sha']), + ) + elif ar_op == 2: + res_d['proto_msg'] = "%s is at %s" % ( + ipv4_to_string(arp_pkt['ar_spa']), + eth_addr_to_string(arp_pkt['ar_sha']), + ) + + res_d['format'] = "%(e_src)s -> %(e_dst)s ARP: %(proto_msg)s" + + elif eth_type == 0x0800: + ip_pkt = l3.cast(gdb.lookup_type('struct ip_header').pointer()).dereference() + res_d['e_proto'] = "IP" + res_d['i_src'] = ipv4_to_string(ip_pkt['ip_src']) + res_d['i_dst'] = ipv4_to_string(ip_pkt['ip_dst']) + + ip_proto = ip_pkt['ip_proto'] + l4 = gdb.parse_and_eval('dp_packet_l4(%s)' % pkt.address) + self.parse_l4(ip_proto, l4, res_d) + if res_d['t_dst']: + res_d['format'] = "%(e_src)s -> %(e_dst)s IP/%(i_proto)s: %(i_src)s:%(t_src)s -> %(i_dst)s:%(t_dst)s" + else: + res_d['format'] = "%(e_src)s -> %(e_dst)s IP/%(i_proto)s: %(i_src)s -> %(i_dst)s" + + elif eth_type == 0x86dd: + res_d['e_proto'] = "IPv6" + + ip_pkt = l3.cast(gdb.lookup_type('struct ovs_16aligned_ip6_hdr').pointer()).dereference() + res_d['i_src'] = ipv6_to_string(ip_pkt['ip6_src']) + res_d['i_dst'] = ipv6_to_string(ip_pkt['ip6_dst']) + + l4 = gdb.parse_and_eval('dp_packet_l4(%s)' % pkt.address) + ip_proto = ip_pkt['ip6_ctlun']['ip6_un1']['ip6_un1_nxt'] + self.parse_l4(ip_proto, l4, res_d) + if res_d['t_dst']: + res_d['format'] = "%(e_src)s -> %(e_dst)s IP6/%(i_proto)s: [%(i_src)s]:%(t_src)s -> [%(i_dst)s]:%(t_dst)s" + else: + res_d['format'] = "%(e_src)s -> %(e_dst)s IP6/%(i_proto)s: [%(i_src)s] -> [%(i_dst)s]" + + return res_d + + @staticmethod + def parse_l4(ip_proto, l4, res_d): + uh = l4.cast(gdb.lookup_type('struct udp_header').pointer()).dereference() + th = l4.cast(gdb.lookup_type('struct tcp_header').pointer()).dereference() + if ip_proto == 58: + res_d['i_proto'] = "ICMPv6" + elif ip_proto == 11: + res_d['i_proto'] = "UDP" + res_d['t_src'] = socket.ntohs(th['udp_src']) + res_d['t_dst'] = socket.ntohs(th['udp_dst']) + elif ip_proto == 6: + res_d['i_proto'] = "TCP" + res_d['t_src'] = socket.ntohs(th['tcp_src']) + res_d['t_dst'] = socket.ntohs(th['tcp_dst']) + elif ip_proto == 1: + res_d['i_proto'] = "ICMP" + else: + res_d['i_proto'] = str(ip_proto) + + # # Initialize all GDB commands # @@ -1324,3 +1484,4 @@ CmdDumpSmap() CmdDumpUdpifKeys() CmdShowFDB() CmdShowUpcall() +CmdDumpPackets() -- 2.30.2 _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev