mirror of
https://github.com/openvswitch/ovs
synced 2025-08-23 10:28:00 +00:00
Currently, ovs_dump_packets will break the formatting of the GDB terminal UI, resulting in artifacts displayed on the screen that may make packets difficult to read. This patch suppresses stderr output from tcpdump and feeds tcpdumps stdout into the paginated output stream. Signed-off-by: Mike Pattrick <mkp@redhat.com> Acked-by: Paolo Valerio <pvalerio@redhat.com> Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
1457 lines
47 KiB
Python
1457 lines
47 KiB
Python
#
|
|
# Copyright (c) 2018 Eelco Chaudron
|
|
#
|
|
# This program is free software; you can redistribute it and/or
|
|
# modify it under the terms of the GNU General Public License
|
|
# as published by the Free Software Foundation; either version
|
|
# 2 of the License, or (at your option) any later version.
|
|
#
|
|
# Files name:
|
|
# ovs_gdb.py
|
|
#
|
|
# Description:
|
|
# GDB commands and functions for Open vSwitch debugging
|
|
#
|
|
# Author:
|
|
# Eelco Chaudron
|
|
#
|
|
# Initial Created:
|
|
# 23 April 2018
|
|
#
|
|
# Notes:
|
|
# It implements the following GDB commands:
|
|
# - ovs_dump_bridge {ports|wanted}
|
|
# - ovs_dump_bridge_ports <struct bridge *>
|
|
# - ovs_dump_dp_netdev [ports]
|
|
# - ovs_dump_dp_netdev_poll_threads <struct dp_netdev *>
|
|
# - ovs_dump_dp_netdev_ports <struct dp_netdev *>
|
|
# - ovs_dump_dp_provider
|
|
# - ovs_dump_netdev
|
|
# - ovs_dump_netdev_provider
|
|
# - ovs_dump_ovs_list <struct ovs_list *> {[<structure>] [<member>] {dump}]}
|
|
# - ovs_dump_packets <struct dp_packet_batch|dp_packet> [tcpdump options]
|
|
# - ovs_dump_simap <struct simap *>
|
|
# - ovs_dump_smap <struct smap *>
|
|
# - ovs_dump_udpif_keys {<udpif_name>|<udpif_address>} {short}
|
|
# - ovs_show_fdb {[<bridge_name>] {dbg} {hash}}
|
|
# - ovs_show_upcall {dbg}
|
|
#
|
|
# Example:
|
|
# $ gdb $(which ovs-vswitchd) $(pidof ovs-vswitchd)
|
|
# (gdb) source ./utilities/gdb/ovs_gdb.py
|
|
#
|
|
# (gdb) ovs_dump_<TAB>
|
|
# ovs_dump_bridge ovs_dump_bridge_ports ovs_dump_dp_netdev
|
|
# ovs_dump_dp_netdev_ports ovs_dump_netdev
|
|
#
|
|
# (gdb) ovs_dump_bridge
|
|
# (struct bridge *) 0x5615471ed2e0: name = br2, type = system
|
|
# (struct bridge *) 0x561547166350: name = br0, type = system
|
|
# (struct bridge *) 0x561547216de0: name = ovs_pvp_br0, type = netdev
|
|
# (struct bridge *) 0x5615471d0420: name = br1, type = system
|
|
#
|
|
# (gdb) p *(struct bridge *) 0x5615471d0420
|
|
# $1 = {node = {hash = 24776443, next = 0x0}, name = 0x5615471cca90 "br1",
|
|
# type = 0x561547163bb0 "system",
|
|
# ...
|
|
# ...
|
|
#
|
|
import gdb
|
|
import struct
|
|
import sys
|
|
import uuid
|
|
try:
|
|
from scapy.layers.l2 import Ether
|
|
from scapy.utils import tcpdump
|
|
except ModuleNotFoundError:
|
|
Ether = None
|
|
tcpdump = None
|
|
|
|
|
|
#
|
|
# Global #define's from OVS which might need updating based on a version.
|
|
#
|
|
N_UMAPS = 512
|
|
|
|
|
|
#
|
|
# The container_of code below is a copied from the Linux kernel project file,
|
|
# scripts/gdb/linux/utils.py. It has the following copyright header:
|
|
#
|
|
# # gdb helper commands and functions for Linux kernel debugging
|
|
# #
|
|
# # common utilities
|
|
# #
|
|
# # Copyright (c) Siemens AG, 2011-2013
|
|
# #
|
|
# # Authors:
|
|
# # Jan Kiszka <jan.kiszka@siemens.com>
|
|
# #
|
|
# # This work is licensed under the terms of the GNU GPL version 2.
|
|
#
|
|
class CachedType(object):
|
|
def __init__(self, name):
|
|
self._type = None
|
|
self._name = name
|
|
|
|
def _new_objfile_handler(self, event):
|
|
self._type = None
|
|
gdb.events.new_objfile.disconnect(self._new_objfile_handler)
|
|
|
|
def get_type(self):
|
|
if self._type is None:
|
|
self._type = gdb.lookup_type(self._name)
|
|
if self._type is None:
|
|
raise gdb.GdbError(
|
|
"cannot resolve type '{0}'".format(self._name))
|
|
if hasattr(gdb, 'events') and hasattr(gdb.events, 'new_objfile'):
|
|
gdb.events.new_objfile.connect(self._new_objfile_handler)
|
|
return self._type
|
|
|
|
|
|
long_type = CachedType("long")
|
|
|
|
|
|
def get_long_type():
|
|
global long_type
|
|
return long_type.get_type()
|
|
|
|
|
|
def offset_of(typeobj, field):
|
|
element = gdb.Value(0).cast(typeobj)
|
|
return int(str(element[field].address).split()[0], 16)
|
|
|
|
|
|
def container_of(ptr, typeobj, member):
|
|
return (ptr.cast(get_long_type()) -
|
|
offset_of(typeobj, member)).cast(typeobj)
|
|
|
|
|
|
def get_global_variable(name):
|
|
var = gdb.lookup_symbol(name)[0]
|
|
if var is None or not var.is_variable:
|
|
print("Can't find {} global variable, are you sure "
|
|
"you are debugging OVS?".format(name))
|
|
return None
|
|
return gdb.parse_and_eval(name)
|
|
|
|
|
|
def get_time_msec():
|
|
# There is no variable that stores the current time each iteration,
|
|
# to get a decent time time_now() value. For now we take the global
|
|
# "coverage_run_time" value, which is the current time + max 5 seconds
|
|
# (COVERAGE_RUN_INTERVAL)
|
|
return int(get_global_variable("coverage_run_time")), -5000
|
|
|
|
|
|
def get_time_now():
|
|
# See get_time_msec() above
|
|
return int(get_global_variable("coverage_run_time")) / 1000, -5
|
|
|
|
|
|
def eth_addr_to_string(eth_addr):
|
|
return "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}".format(
|
|
int(eth_addr['ea'][0]),
|
|
int(eth_addr['ea'][1]),
|
|
int(eth_addr['ea'][2]),
|
|
int(eth_addr['ea'][3]),
|
|
int(eth_addr['ea'][4]),
|
|
int(eth_addr['ea'][5]))
|
|
|
|
|
|
#
|
|
# Simple class to print a spinner on the console
|
|
#
|
|
class ProgressIndicator(object):
|
|
def __init__(self, message=None):
|
|
self.spinner = "/-\\|"
|
|
self.spinner_index = 0
|
|
self.message = message
|
|
|
|
if self.message is not None:
|
|
print(self.message, end='')
|
|
|
|
def update(self):
|
|
print("{}\b".format(self.spinner[self.spinner_index]), end='')
|
|
sys.stdout.flush()
|
|
self.spinner_index += 1
|
|
if self.spinner_index >= len(self.spinner):
|
|
self.spinner_index = 0
|
|
|
|
def pause(self):
|
|
print("\r\033[K", end='')
|
|
|
|
def resume(self):
|
|
if self.message is not None:
|
|
print(self.message, end='')
|
|
self.update()
|
|
|
|
def done(self):
|
|
self.pause()
|
|
|
|
|
|
#
|
|
# Class that will provide an iterator over an OVS cmap.
|
|
#
|
|
class ForEachCMAP(object):
|
|
def __init__(self, cmap, typeobj=None, member='node'):
|
|
self.cmap = cmap
|
|
self.first = True
|
|
self.typeobj = typeobj
|
|
self.member = member
|
|
# Cursor values
|
|
self.node = 0
|
|
self.bucket_idx = 0
|
|
self.entry_idx = 0
|
|
|
|
def __iter__(self):
|
|
return self
|
|
|
|
def __get_CMAP_K(self):
|
|
ptr_type = gdb.lookup_type("void").pointer()
|
|
return (64 - 4) / (4 + ptr_type.sizeof)
|
|
|
|
def __next(self):
|
|
ipml = self.cmap['impl']['p']
|
|
|
|
if self.node != 0:
|
|
self.node = self.node['next']['p']
|
|
if self.node != 0:
|
|
return
|
|
|
|
while self.bucket_idx <= ipml['mask']:
|
|
buckets = ipml['buckets'][self.bucket_idx]
|
|
while self.entry_idx < self.__get_CMAP_K():
|
|
self.node = buckets['nodes'][self.entry_idx]['next']['p']
|
|
self.entry_idx += 1
|
|
if self.node != 0:
|
|
return
|
|
|
|
self.bucket_idx += 1
|
|
self.entry_idx = 0
|
|
|
|
raise StopIteration
|
|
|
|
def __next__(self):
|
|
ipml = self.cmap['impl']['p']
|
|
if ipml['n'] == 0:
|
|
raise StopIteration
|
|
|
|
self.__next()
|
|
|
|
if self.typeobj is None:
|
|
return self.node
|
|
|
|
return container_of(self.node,
|
|
gdb.lookup_type(self.typeobj).pointer(),
|
|
self.member)
|
|
|
|
def next(self):
|
|
return self.__next__()
|
|
|
|
|
|
#
|
|
# Class that will provide an iterator over an OVS hmap.
|
|
#
|
|
class ForEachHMAP(object):
|
|
def __init__(self, hmap, typeobj=None, member='node'):
|
|
self.hmap = hmap
|
|
self.node = None
|
|
self.first = True
|
|
self.typeobj = typeobj
|
|
self.member = member
|
|
|
|
def __iter__(self):
|
|
return self
|
|
|
|
def __next(self, start):
|
|
for i in range(start, (self.hmap['mask'] + 1)):
|
|
self.node = self.hmap['buckets'][i]
|
|
if self.node != 0:
|
|
return
|
|
|
|
raise StopIteration
|
|
|
|
def __next__(self):
|
|
#
|
|
# In the real implementation the n values is never checked,
|
|
# however when debugging we do, as we might try to access
|
|
# a hmap that has been cleared/hmap_destroy().
|
|
#
|
|
if self.hmap['n'] <= 0:
|
|
raise StopIteration
|
|
|
|
if self.first:
|
|
self.first = False
|
|
self.__next(0)
|
|
elif self.node['next'] != 0:
|
|
self.node = self.node['next']
|
|
else:
|
|
self.__next((self.node['hash'] & self.hmap['mask']) + 1)
|
|
|
|
if self.typeobj is None:
|
|
return self.node
|
|
|
|
return container_of(self.node,
|
|
gdb.lookup_type(self.typeobj).pointer(),
|
|
self.member)
|
|
|
|
def next(self):
|
|
return self.__next__()
|
|
|
|
|
|
#
|
|
# Class that will provide an iterator over an Netlink attributes
|
|
#
|
|
class ForEachNL(object):
|
|
def __init__(self, nlattrs, nlattrs_len):
|
|
self.attr = nlattrs.cast(gdb.lookup_type('struct nlattr').pointer())
|
|
self.attr_len = int(nlattrs_len)
|
|
|
|
def __iter__(self):
|
|
return self
|
|
|
|
def round_up(self, val, round_to):
|
|
return int(val) + (round_to - int(val)) % round_to
|
|
|
|
def __next__(self):
|
|
if self.attr is None or \
|
|
self.attr_len < 4 or self.attr['nla_len'] < 4 or \
|
|
self.attr['nla_len'] > self.attr_len:
|
|
#
|
|
# Invalid attr set, maybe we should raise an exception?
|
|
#
|
|
raise StopIteration
|
|
|
|
attr = self.attr
|
|
self.attr_len -= self.round_up(attr['nla_len'], 4)
|
|
|
|
self.attr = self.attr.cast(gdb.lookup_type('void').pointer()) \
|
|
+ self.round_up(attr['nla_len'], 4)
|
|
self.attr = self.attr.cast(gdb.lookup_type('struct nlattr').pointer())
|
|
|
|
return attr
|
|
|
|
def next(self):
|
|
return self.__next__()
|
|
|
|
|
|
#
|
|
# Class that will provide an iterator over an OVS shash.
|
|
#
|
|
class ForEachSHASH(ForEachHMAP):
|
|
def __init__(self, shash, typeobj=None):
|
|
|
|
self.data_typeobj = typeobj
|
|
|
|
super(ForEachSHASH, self).__init__(shash['map'],
|
|
"struct shash_node", "node")
|
|
|
|
def __next__(self):
|
|
node = super(ForEachSHASH, self).__next__()
|
|
|
|
if self.data_typeobj is None:
|
|
return node
|
|
|
|
return node['data'].cast(gdb.lookup_type(self.data_typeobj).pointer())
|
|
|
|
def next(self):
|
|
return self.__next__()
|
|
|
|
|
|
#
|
|
# Class that will provide an iterator over an OVS simap.
|
|
#
|
|
class ForEachSIMAP(ForEachHMAP):
|
|
def __init__(self, shash):
|
|
super(ForEachSIMAP, self).__init__(shash['map'],
|
|
"struct simap_node", "node")
|
|
|
|
def __next__(self):
|
|
node = super(ForEachSIMAP, self).__next__()
|
|
return node['name'], node['data']
|
|
|
|
def next(self):
|
|
return self.__next__()
|
|
|
|
|
|
#
|
|
# Class that will provide an iterator over an OVS smap.
|
|
#
|
|
class ForEachSMAP(ForEachHMAP):
|
|
def __init__(self, shash):
|
|
super(ForEachSMAP, self).__init__(shash['map'],
|
|
"struct smap_node", "node")
|
|
|
|
def __next__(self):
|
|
node = super(ForEachSMAP, self).__next__()
|
|
return node['key'], node['value']
|
|
|
|
def next(self):
|
|
return self.__next__()
|
|
|
|
|
|
#
|
|
# Class that will provide an iterator over an OVS list.
|
|
#
|
|
class ForEachLIST(object):
|
|
def __init__(self, list, typeobj=None, member='node'):
|
|
self.list = list
|
|
self.node = list
|
|
self.typeobj = typeobj
|
|
self.member = member
|
|
|
|
def __iter__(self):
|
|
return self
|
|
|
|
def __next__(self):
|
|
if self.list.address == self.node['next']:
|
|
raise StopIteration
|
|
|
|
self.node = self.node['next']
|
|
|
|
if self.typeobj is None:
|
|
return self.node
|
|
|
|
return container_of(self.node,
|
|
gdb.lookup_type(self.typeobj).pointer(),
|
|
self.member)
|
|
|
|
def next(self):
|
|
return self.__next__()
|
|
|
|
|
|
#
|
|
# Class that will provide an iterator over an OFPACTS.
|
|
#
|
|
class ForEachOFPACTS(object):
|
|
def __init__(self, ofpacts, ofpacts_len):
|
|
self.ofpact = ofpacts.cast(gdb.lookup_type('struct ofpact').pointer())
|
|
self.length = int(ofpacts_len)
|
|
|
|
def __round_up(self, val, round_to):
|
|
return int(val) + (round_to - int(val)) % round_to
|
|
|
|
def __iter__(self):
|
|
return self
|
|
|
|
def __next__(self):
|
|
if self.ofpact is None or self.length <= 0:
|
|
raise StopIteration
|
|
|
|
ofpact = self.ofpact
|
|
length = self.__round_up(ofpact['len'], 8)
|
|
|
|
self.length -= length
|
|
self.ofpact = self.ofpact.cast(
|
|
gdb.lookup_type('void').pointer()) + length
|
|
self.ofpact = self.ofpact.cast(
|
|
gdb.lookup_type('struct ofpact').pointer())
|
|
|
|
return ofpact
|
|
|
|
def next(self):
|
|
return self.__next__()
|
|
|
|
|
|
#
|
|
# Implements the GDB "ovs_dump_bridges" command
|
|
#
|
|
class CmdDumpBridge(gdb.Command):
|
|
"""Dump all configured bridges.
|
|
Usage:
|
|
ovs_dump_bridge {ports|wanted}
|
|
"""
|
|
def __init__(self):
|
|
super(CmdDumpBridge, self).__init__("ovs_dump_bridge",
|
|
gdb.COMMAND_DATA)
|
|
|
|
def invoke(self, arg, from_tty):
|
|
ports = False
|
|
wanted = False
|
|
arg_list = gdb.string_to_argv(arg)
|
|
if len(arg_list) > 1 or \
|
|
(len(arg_list) == 1 and arg_list[0] != "ports" and
|
|
arg_list[0] != "wanted"):
|
|
print("usage: ovs_dump_bridge {ports|wanted}")
|
|
return
|
|
elif len(arg_list) == 1:
|
|
if arg_list[0] == "ports":
|
|
ports = True
|
|
else:
|
|
wanted = True
|
|
|
|
all_bridges = get_global_variable('all_bridges')
|
|
if all_bridges is None:
|
|
return
|
|
|
|
for node in ForEachHMAP(all_bridges,
|
|
"struct bridge", "node"):
|
|
print("(struct bridge *) {}: name = {}, type = {}".
|
|
format(node, node['name'].string(),
|
|
node['type'].string()))
|
|
|
|
if ports:
|
|
for port in ForEachHMAP(node['ports'],
|
|
"struct port", "hmap_node"):
|
|
CmdDumpBridgePorts.display_single_port(port, 4)
|
|
|
|
if wanted:
|
|
for port in ForEachSHASH(node['wanted_ports'],
|
|
typeobj="struct ovsrec_port"):
|
|
print(" (struct ovsrec_port *) {}: name = {}".
|
|
format(port, port['name'].string()))
|
|
# print port.dereference()
|
|
|
|
|
|
#
|
|
# Implements the GDB "ovs_dump_bridge_ports" command
|
|
#
|
|
class CmdDumpBridgePorts(gdb.Command):
|
|
"""Dump all ports added to a specific struct bridge*.
|
|
Usage:
|
|
ovs_dump_bridge_ports <struct bridge *>
|
|
"""
|
|
def __init__(self):
|
|
super(CmdDumpBridgePorts, self).__init__("ovs_dump_bridge_ports",
|
|
gdb.COMMAND_DATA)
|
|
|
|
@staticmethod
|
|
def display_single_port(port, indent=0):
|
|
indent = " " * indent
|
|
port = port.cast(gdb.lookup_type('struct port').pointer())
|
|
print("{}(struct port *) {}: name = {}, bridge = (struct bridge *) {}".
|
|
format(indent, port, port['name'].string(),
|
|
port['bridge']))
|
|
|
|
indent += " " * 4
|
|
for iface in ForEachLIST(port['ifaces'], "struct iface", "port_elem"):
|
|
print("{}(struct iface *) {}: name = {}, ofp_port = {}, "
|
|
"netdev = (struct netdev *) {}".
|
|
format(indent, iface, iface['name'],
|
|
iface['ofp_port'], iface['netdev']))
|
|
|
|
def invoke(self, arg, from_tty):
|
|
arg_list = gdb.string_to_argv(arg)
|
|
if len(arg_list) != 1:
|
|
print("usage: ovs_dump_bridge_ports <struct bridge *>")
|
|
return
|
|
bridge = gdb.parse_and_eval(arg_list[0]).cast(
|
|
gdb.lookup_type('struct bridge').pointer())
|
|
for node in ForEachHMAP(bridge['ports'],
|
|
"struct port", "hmap_node"):
|
|
self.display_single_port(node)
|
|
|
|
|
|
#
|
|
# Implements the GDB "ovs_dump_dp_netdev" command
|
|
#
|
|
class CmdDumpDpNetdev(gdb.Command):
|
|
"""Dump all registered dp_netdev structures.
|
|
Usage:
|
|
ovs_dump_dp_netdev [ports]
|
|
"""
|
|
def __init__(self):
|
|
super(CmdDumpDpNetdev, self).__init__("ovs_dump_dp_netdev",
|
|
gdb.COMMAND_DATA)
|
|
|
|
def invoke(self, arg, from_tty):
|
|
ports = False
|
|
arg_list = gdb.string_to_argv(arg)
|
|
if len(arg_list) > 1 or \
|
|
(len(arg_list) == 1 and arg_list[0] != "ports"):
|
|
print("usage: ovs_dump_dp_netdev [ports]")
|
|
return
|
|
elif len(arg_list) == 1:
|
|
ports = True
|
|
|
|
dp_netdevs = get_global_variable('dp_netdevs')
|
|
if dp_netdevs is None:
|
|
return
|
|
|
|
for dp in ForEachSHASH(dp_netdevs, typeobj=('struct dp_netdev')):
|
|
|
|
print("(struct dp_netdev *) {}: name = {}, class = "
|
|
"(struct dpif_class *) {}".
|
|
format(dp, dp['name'].string(), dp['class']))
|
|
|
|
if ports:
|
|
for node in ForEachHMAP(dp['ports'],
|
|
"struct dp_netdev_port", "node"):
|
|
CmdDumpDpNetdevPorts.display_single_port(node, 4)
|
|
|
|
|
|
#
|
|
# Implements the GDB "ovs_dump_dp_netdev_poll_threads" command
|
|
#
|
|
class CmdDumpDpNetdevPollThreads(gdb.Command):
|
|
"""Dump all poll_thread info added to a specific struct dp_netdev*.
|
|
Usage:
|
|
ovs_dump_dp_netdev_poll_threads <struct dp_netdev *>
|
|
"""
|
|
def __init__(self):
|
|
super(CmdDumpDpNetdevPollThreads, self).__init__(
|
|
"ovs_dump_dp_netdev_poll_threads",
|
|
gdb.COMMAND_DATA)
|
|
|
|
@staticmethod
|
|
def display_single_poll_thread(pmd_thread, indent=0):
|
|
indent = " " * indent
|
|
print("{}(struct dp_netdev_pmd_thread *) {}: core_id = {}, "
|
|
"numa_id {}".format(indent,
|
|
pmd_thread, pmd_thread['core_id'],
|
|
pmd_thread['numa_id']))
|
|
|
|
def invoke(self, arg, from_tty):
|
|
arg_list = gdb.string_to_argv(arg)
|
|
if len(arg_list) != 1:
|
|
print("usage: ovs_dump_dp_netdev_poll_threads "
|
|
"<struct dp_netdev *>")
|
|
return
|
|
dp_netdev = gdb.parse_and_eval(arg_list[0]).cast(
|
|
gdb.lookup_type('struct dp_netdev').pointer())
|
|
for node in ForEachCMAP(dp_netdev['poll_threads'],
|
|
"struct dp_netdev_pmd_thread", "node"):
|
|
self.display_single_poll_thread(node)
|
|
|
|
|
|
#
|
|
# Implements the GDB "ovs_dump_dp_netdev_ports" command
|
|
#
|
|
class CmdDumpDpNetdevPorts(gdb.Command):
|
|
"""Dump all ports added to a specific struct dp_netdev*.
|
|
Usage:
|
|
ovs_dump_dp_netdev_ports <struct dp_netdev *>
|
|
"""
|
|
def __init__(self):
|
|
super(CmdDumpDpNetdevPorts, self).__init__("ovs_dump_dp_netdev_ports",
|
|
gdb.COMMAND_DATA)
|
|
|
|
@staticmethod
|
|
def display_single_port(port, indent=0):
|
|
indent = " " * indent
|
|
print("{}(struct dp_netdev_port *) {}:".format(indent, port))
|
|
print("{} port_no = {}, n_rxq = {}, type = {}".
|
|
format(indent, port['port_no'], port['n_rxq'],
|
|
port['type'].string()))
|
|
print("{} netdev = (struct netdev *) {}: name = {}, "
|
|
"n_txq/rxq = {}/{}".
|
|
format(indent, port['netdev'],
|
|
port['netdev']['name'].string(),
|
|
port['netdev']['n_txq'],
|
|
port['netdev']['n_rxq']))
|
|
|
|
def invoke(self, arg, from_tty):
|
|
arg_list = gdb.string_to_argv(arg)
|
|
if len(arg_list) != 1:
|
|
print("usage: ovs_dump_dp_netdev_ports <struct dp_netdev *>")
|
|
return
|
|
dp_netdev = gdb.parse_and_eval(arg_list[0]).cast(
|
|
gdb.lookup_type('struct dp_netdev').pointer())
|
|
for node in ForEachHMAP(dp_netdev['ports'],
|
|
"struct dp_netdev_port", "node"):
|
|
# print node.dereference()
|
|
self.display_single_port(node)
|
|
|
|
|
|
#
|
|
# Implements the GDB "ovs_dump_dp_provider" command
|
|
#
|
|
class CmdDumpDpProvider(gdb.Command):
|
|
"""Dump all registered registered_dpif_class structures.
|
|
Usage:
|
|
ovs_dump_dp_provider
|
|
"""
|
|
def __init__(self):
|
|
super(CmdDumpDpProvider, self).__init__("ovs_dump_dp_provider",
|
|
gdb.COMMAND_DATA)
|
|
|
|
def invoke(self, arg, from_tty):
|
|
dp_providers = get_global_variable('dpif_classes')
|
|
if dp_providers is None:
|
|
return
|
|
|
|
for dp_class in ForEachSHASH(dp_providers,
|
|
typeobj="struct registered_dpif_class"):
|
|
|
|
print("(struct registered_dpif_class *) {}: "
|
|
"(struct dpif_class *) 0x{:x} = {{type = {}, ...}}, "
|
|
"refcount = {}".
|
|
format(dp_class,
|
|
int(dp_class['dpif_class']),
|
|
dp_class['dpif_class']['type'].string(),
|
|
dp_class['refcount']))
|
|
|
|
|
|
#
|
|
# Implements the GDB "ovs_dump_netdev" command
|
|
#
|
|
class CmdDumpNetdev(gdb.Command):
|
|
"""Dump all registered netdev structures.
|
|
Usage:
|
|
ovs_dump_netdev
|
|
"""
|
|
def __init__(self):
|
|
super(CmdDumpNetdev, self).__init__("ovs_dump_netdev",
|
|
gdb.COMMAND_DATA)
|
|
|
|
@staticmethod
|
|
def display_single_netdev(netdev, indent=0):
|
|
indent = " " * indent
|
|
print("{}(struct netdev *) {}: name = {:15}, auto_classified = {}, "
|
|
"netdev_class = {}".
|
|
format(indent, netdev, netdev['name'].string(),
|
|
netdev['auto_classified'], netdev['netdev_class']))
|
|
|
|
def invoke(self, arg, from_tty):
|
|
netdev_shash = get_global_variable('netdev_shash')
|
|
if netdev_shash is None:
|
|
return
|
|
|
|
for netdev in ForEachSHASH(netdev_shash, "struct netdev"):
|
|
self.display_single_netdev(netdev)
|
|
|
|
|
|
#
|
|
# Implements the GDB "ovs_dump_netdev_provider" command
|
|
#
|
|
class CmdDumpNetdevProvider(gdb.Command):
|
|
"""Dump all registered netdev providers.
|
|
Usage:
|
|
ovs_dump_netdev_provider
|
|
"""
|
|
def __init__(self):
|
|
super(CmdDumpNetdevProvider, self).__init__("ovs_dump_netdev_provider",
|
|
gdb.COMMAND_DATA)
|
|
|
|
@staticmethod
|
|
def is_class_vport_class(netdev_class):
|
|
netdev_class = netdev_class.cast(
|
|
gdb.lookup_type('struct netdev_class').pointer())
|
|
|
|
vport_construct = gdb.lookup_symbol('netdev_vport_construct')[0]
|
|
|
|
if netdev_class['construct'] == vport_construct.value():
|
|
return True
|
|
return False
|
|
|
|
@staticmethod
|
|
def display_single_netdev_provider(reg_class, indent=0):
|
|
indent = " " * indent
|
|
print("{}(struct netdev_registered_class *) {}: refcnt = {},".
|
|
format(indent, reg_class, reg_class['refcnt']))
|
|
|
|
print("{} (struct netdev_class *) 0x{:x} = {{type = {}, "
|
|
"is_pmd = {}, ...}}, ".
|
|
format(indent, int(reg_class['class']),
|
|
reg_class['class']['type'].string(),
|
|
reg_class['class']['is_pmd']))
|
|
|
|
if CmdDumpNetdevProvider.is_class_vport_class(reg_class['class']):
|
|
vport = container_of(
|
|
reg_class['class'],
|
|
gdb.lookup_type('struct vport_class').pointer(),
|
|
'netdev_class')
|
|
|
|
if vport['dpif_port'] != 0:
|
|
dpif_port = vport['dpif_port'].string()
|
|
else:
|
|
dpif_port = "\"\""
|
|
|
|
print("{} (struct vport_class *) 0x{:x} = "
|
|
"{{ dpif_port = {}, ... }}".
|
|
format(indent, int(vport), dpif_port))
|
|
|
|
def invoke(self, arg, from_tty):
|
|
netdev_classes = get_global_variable('netdev_classes')
|
|
if netdev_classes is None:
|
|
return
|
|
|
|
for reg_class in ForEachCMAP(netdev_classes,
|
|
"struct netdev_registered_class",
|
|
"cmap_node"):
|
|
self.display_single_netdev_provider(reg_class)
|
|
|
|
|
|
#
|
|
# Implements the GDB "ovs_dump_ovs_list" command
|
|
#
|
|
class CmdDumpOvsList(gdb.Command):
|
|
"""Dump all nodes of an ovs_list give
|
|
Usage:
|
|
ovs_dump_ovs_list <struct ovs_list *> {[<structure>] [<member>] {dump}]}
|
|
|
|
For example dump all the none quiescent OvS RCU threads:
|
|
|
|
(gdb) ovs_dump_ovs_list &ovsrcu_threads
|
|
(struct ovs_list *) 0x1400
|
|
(struct ovs_list *) 0xcc00
|
|
(struct ovs_list *) 0x6806
|
|
|
|
This is not very useful, so please use this with the container_of mode:
|
|
|
|
(gdb) set $threads = &ovsrcu_threads
|
|
(gdb) ovs_dump_ovs_list $threads 'struct ovsrcu_perthread' list_node
|
|
(struct ovsrcu_perthread *) 0x1400
|
|
(struct ovsrcu_perthread *) 0xcc00
|
|
(struct ovsrcu_perthread *) 0x6806
|
|
|
|
Now you can manually use the print command to show the content, or use the
|
|
dump option to dump the structure for all nodes:
|
|
|
|
(gdb) ovs_dump_ovs_list $threads 'struct ovsrcu_perthread' list_node dump
|
|
(struct ovsrcu_perthread *) 0x1400 =
|
|
{list_node = {prev = 0x48e80 <ovsrcu_threads>, next = 0xcc00}, mutex...
|
|
|
|
(struct ovsrcu_perthread *) 0xcc00 =
|
|
{list_node = {prev = 0x1400, next = 0x6806}, mutex ...
|
|
|
|
(struct ovsrcu_perthread *) 0x6806 =
|
|
{list_node = {prev = 0xcc00, next = 0x48e80 <ovsrcu_threads>}, ...
|
|
"""
|
|
def __init__(self):
|
|
super(CmdDumpOvsList, self).__init__("ovs_dump_ovs_list",
|
|
gdb.COMMAND_DATA)
|
|
|
|
def invoke(self, arg, from_tty):
|
|
arg_list = gdb.string_to_argv(arg)
|
|
typeobj = None
|
|
member = None
|
|
dump = False
|
|
|
|
if len(arg_list) != 1 and len(arg_list) != 3 and len(arg_list) != 4:
|
|
print("usage: ovs_dump_ovs_list <struct ovs_list *> "
|
|
"{[<structure>] [<member>] {dump}]}")
|
|
return
|
|
|
|
header = gdb.parse_and_eval(arg_list[0]).cast(
|
|
gdb.lookup_type('struct ovs_list').pointer())
|
|
|
|
if len(arg_list) >= 3:
|
|
typeobj = arg_list[1]
|
|
member = arg_list[2]
|
|
if len(arg_list) == 4 and arg_list[3] == "dump":
|
|
dump = True
|
|
|
|
for node in ForEachLIST(header.dereference()):
|
|
if typeobj is None or member is None:
|
|
print("(struct ovs_list *) {}".format(node))
|
|
else:
|
|
print("({} *) {} {}".format(
|
|
typeobj,
|
|
container_of(node,
|
|
gdb.lookup_type(typeobj).pointer(), member),
|
|
"=" if dump else ""))
|
|
if dump:
|
|
print(" {}\n".format(container_of(
|
|
node,
|
|
gdb.lookup_type(typeobj).pointer(),
|
|
member).dereference()))
|
|
|
|
|
|
#
|
|
# Implements the GDB "ovs_dump_simap" command
|
|
#
|
|
class CmdDumpSimap(gdb.Command):
|
|
"""Dump all key, value entries of a simap
|
|
Usage:
|
|
ovs_dump_simap <struct simap *>
|
|
"""
|
|
|
|
def __init__(self):
|
|
super(CmdDumpSimap, self).__init__("ovs_dump_simap",
|
|
gdb.COMMAND_DATA)
|
|
|
|
def invoke(self, arg, from_tty):
|
|
arg_list = gdb.string_to_argv(arg)
|
|
|
|
if len(arg_list) != 1:
|
|
print("ERROR: Missing argument!\n")
|
|
print(self.__doc__)
|
|
return
|
|
|
|
simap = gdb.parse_and_eval(arg_list[0]).cast(
|
|
gdb.lookup_type('struct simap').pointer())
|
|
|
|
values = dict()
|
|
max_name_len = 0
|
|
for name, value in ForEachSIMAP(simap.dereference()):
|
|
values[name.string()] = int(value)
|
|
if len(name.string()) > max_name_len:
|
|
max_name_len = len(name.string())
|
|
|
|
for name in sorted(values.keys()):
|
|
print("{}: {} / 0x{:x}".format(name.ljust(max_name_len),
|
|
values[name], values[name]))
|
|
|
|
|
|
#
|
|
# Implements the GDB "ovs_dump_smap" command
|
|
#
|
|
class CmdDumpSmap(gdb.Command):
|
|
"""Dump all key, value pairs of a smap
|
|
Usage:
|
|
ovs_dump_smap <struct smap *>
|
|
"""
|
|
|
|
def __init__(self):
|
|
super(CmdDumpSmap, self).__init__("ovs_dump_smap",
|
|
gdb.COMMAND_DATA)
|
|
|
|
def invoke(self, arg, from_tty):
|
|
arg_list = gdb.string_to_argv(arg)
|
|
|
|
if len(arg_list) != 1:
|
|
print("ERROR: Missing argument!\n")
|
|
print(self.__doc__)
|
|
return
|
|
|
|
smap = gdb.parse_and_eval(arg_list[0]).cast(
|
|
gdb.lookup_type('struct smap').pointer())
|
|
|
|
values = dict()
|
|
max_key_len = 0
|
|
for key, value in ForEachSMAP(smap.dereference()):
|
|
values[key.string()] = value.string()
|
|
if len(key.string()) > max_key_len:
|
|
max_key_len = len(key.string())
|
|
|
|
for key in sorted(values.keys()):
|
|
print("{}: {}".format(key.ljust(max_key_len),
|
|
values[key]))
|
|
|
|
|
|
#
|
|
# Implements the GDB "ovs_dump_udpif_keys" command
|
|
#
|
|
class CmdDumpUdpifKeys(gdb.Command):
|
|
"""Dump all nodes of an ovs_list give
|
|
Usage:
|
|
ovs_dump_udpif_keys {<udpif_name>|<udpif_address>} {short}
|
|
|
|
<udpif_name> : Full name of the udpif's dpif to dump
|
|
<udpif_address> : Address of the udpif structure to dump. If both the
|
|
<udpif_name> and <udpif_address> are omitted the
|
|
available udpif structures are displayed.
|
|
short : Only dump ukey structure addresses, no content details
|
|
no_count : Do not count the number of ukeys, as it might be slow
|
|
"""
|
|
|
|
def __init__(self):
|
|
super(CmdDumpUdpifKeys, self).__init__("ovs_dump_udpif_keys",
|
|
gdb.COMMAND_DATA)
|
|
|
|
def count_all_ukeys(self, udpif):
|
|
count = 0
|
|
spinner = ProgressIndicator("Counting all ukeys: ")
|
|
|
|
for j in range(0, N_UMAPS):
|
|
spinner.update()
|
|
count += udpif['ukeys'][j]['cmap']['impl']['p']['n']
|
|
|
|
spinner.done()
|
|
return count
|
|
|
|
def dump_all_ukeys(self, udpif, indent=0, short=False):
|
|
indent = " " * indent
|
|
spinner = ProgressIndicator("Walking ukeys: ")
|
|
for j in range(0, N_UMAPS):
|
|
spinner.update()
|
|
if udpif['ukeys'][j]['cmap']['impl']['p']['n'] != 0:
|
|
spinner.pause()
|
|
print("{}(struct umap *) {}:".
|
|
format(indent, udpif['ukeys'][j].address))
|
|
for ukey in ForEachCMAP(udpif['ukeys'][j]['cmap'],
|
|
"struct udpif_key", "cmap_node"):
|
|
|
|
base_str = "{} (struct udpif_key *) {}: ". \
|
|
format(indent, ukey)
|
|
if short:
|
|
print(base_str)
|
|
continue
|
|
|
|
print("{}key_len = {}, mask_len = {}".
|
|
format(base_str, ukey['key_len'], ukey['mask_len']))
|
|
|
|
indent_b = " " * len(base_str)
|
|
if ukey['ufid_present']:
|
|
print("{}ufid = {}".
|
|
format(
|
|
indent_b, str(uuid.UUID(
|
|
"{:08x}{:08x}{:08x}{:08x}".
|
|
format(int(ukey['ufid']['u32'][3]),
|
|
int(ukey['ufid']['u32'][2]),
|
|
int(ukey['ufid']['u32'][1]),
|
|
int(ukey['ufid']['u32'][0]))))))
|
|
|
|
print("{}hash = 0x{:8x}, pmd_id = {}".
|
|
format(indent_b, int(ukey['hash']), ukey['pmd_id']))
|
|
print("{}state = {}".format(indent_b, ukey['state']))
|
|
print("{}n_packets = {}, n_bytes = {}".
|
|
format(indent_b,
|
|
ukey['stats']['n_packets'],
|
|
ukey['stats']['n_bytes']))
|
|
print("{}used = {}, tcp_flags = 0x{:04x}".
|
|
format(indent_b,
|
|
ukey['stats']['used'],
|
|
int(ukey['stats']['tcp_flags'])))
|
|
|
|
#
|
|
# TODO: Would like to add support for dumping key, mask
|
|
# actions, and xlate_cache
|
|
#
|
|
# key = ""
|
|
# for nlattr in ForEachNL(ukey['key'], ukey['key_len']):
|
|
# key += "{}{}".format(
|
|
# "" if len(key) == 0 else ", ",
|
|
# nlattr['nla_type'].cast(
|
|
# gdb.lookup_type('enum ovs_key_attr')))
|
|
# print("{}key attributes = {}".format(indent_b, key))
|
|
spinner.resume()
|
|
spinner.done()
|
|
|
|
def invoke(self, arg, from_tty):
|
|
arg_list = gdb.string_to_argv(arg)
|
|
all_udpifs = get_global_variable('all_udpifs')
|
|
no_count = "no_count" in arg_list
|
|
|
|
if all_udpifs is None:
|
|
return
|
|
|
|
udpifs = dict()
|
|
for udpif in ForEachLIST(all_udpifs, "struct udpif", "list_node"):
|
|
udpifs[udpif['dpif']['full_name'].string()] = udpif
|
|
|
|
if len(arg_list) == 0 or (
|
|
len(arg_list) == 1 and arg_list[0] == "no_count"):
|
|
print("(struct udpif *) {}: name = {}, total keys = {}".
|
|
format(udpif, udpif['dpif']['full_name'].string(),
|
|
self.count_all_ukeys(udpif) if not no_count
|
|
else "<not counted!>"))
|
|
|
|
if len(arg_list) == 0 or (
|
|
len(arg_list) == 1 and arg_list[0] == "no_count"):
|
|
return
|
|
|
|
if arg_list[0] in udpifs:
|
|
udpif = udpifs[arg_list[0]]
|
|
else:
|
|
try:
|
|
udpif = gdb.parse_and_eval(arg_list[0]).cast(
|
|
gdb.lookup_type('struct udpif').pointer())
|
|
except Exception:
|
|
udpif = None
|
|
|
|
if udpif is None:
|
|
print("Can't find provided udpif address!")
|
|
return
|
|
|
|
self.dump_all_ukeys(udpif, 0, "short" in arg_list[1:])
|
|
|
|
|
|
#
|
|
# Implements the GDB "ovs_show_fdb" command
|
|
#
|
|
class CmdShowFDB(gdb.Command):
|
|
"""Show FDB information
|
|
Usage:
|
|
ovs_show_fdb {<bridge_name> {dbg} {hash}}
|
|
|
|
<bridge_name> : Optional bridge name, if not supplied FDB summary
|
|
information is displayed for all bridges.
|
|
dbg : Will show structure address information
|
|
hash : Will display the forwarding table using the hash
|
|
table, rather than the rlu list.
|
|
"""
|
|
|
|
def __init__(self):
|
|
super(CmdShowFDB, self).__init__("ovs_show_fdb",
|
|
gdb.COMMAND_DATA)
|
|
|
|
@staticmethod
|
|
def __get_port_name_num(mac_entry):
|
|
if mac_entry['mlport'] is not None:
|
|
port = mac_entry['mlport']['port'].cast(
|
|
gdb.lookup_type('struct ofbundle').pointer())
|
|
|
|
port_name = port['name'].string()
|
|
port_no = int(container_of(
|
|
port['ports']['next'],
|
|
gdb.lookup_type('struct ofport_dpif').pointer(),
|
|
'bundle_node')['up']['ofp_port'])
|
|
|
|
if port_no == 0xfff7:
|
|
port_no = "UNSET"
|
|
elif port_no == 0xfff8:
|
|
port_no = "IN_PORT"
|
|
elif port_no == 0xfff9:
|
|
port_no = "TABLE"
|
|
elif port_no == 0xfffa:
|
|
port_no = "NORMAL"
|
|
elif port_no == 0xfffb:
|
|
port_no = "FLOOD"
|
|
elif port_no == 0xfffc:
|
|
port_no = "ALL"
|
|
elif port_no == 0xfffd:
|
|
port_no = "CONTROLLER"
|
|
elif port_no == 0xfffe:
|
|
port_no = "LOCAL"
|
|
elif port_no == 0xffff:
|
|
port_no = "NONE"
|
|
else:
|
|
port_no = str(port_no)
|
|
else:
|
|
port_name = "-"
|
|
port_no = "?"
|
|
|
|
return port_name, port_no
|
|
|
|
@staticmethod
|
|
def display_ml_summary(ml, indent=0, dbg=False):
|
|
indent = " " * indent
|
|
if ml is None:
|
|
return
|
|
|
|
if dbg:
|
|
print("[(struct mac_learning *) {}]".format(ml))
|
|
|
|
print("{}table.n : {}".format(indent, ml['table']['n']))
|
|
print("{}secret : 0x{:x}".format(indent, int(ml['secret'])))
|
|
print("{}idle_time : {}".format(indent, ml['idle_time']))
|
|
print("{}max_entries : {}".format(indent, ml['max_entries']))
|
|
print("{}ref_count : {}".format(indent, ml['ref_cnt']['count']))
|
|
print("{}need_revalidate : {}".format(indent, ml['need_revalidate']))
|
|
print("{}ports_by_ptr.n : {}".format(indent, ml['ports_by_ptr']['n']))
|
|
print("{}ports_by_usage.n: {}".format(indent,
|
|
ml['ports_by_usage']['n']))
|
|
print("{}total_learned : {}".format(indent, ml['total_learned']))
|
|
print("{}total_expired : {}".format(indent, ml['total_expired']))
|
|
print("{}total_evicted : {}".format(indent, ml['total_evicted']))
|
|
print("{}total_moved : {}".format(indent, ml['total_moved']))
|
|
|
|
@staticmethod
|
|
def display_mac_entry(mac_entry, indent=0, dbg=False):
|
|
port_name, port_no = CmdShowFDB.__get_port_name_num(mac_entry)
|
|
|
|
line = "{}{:16.16} {:-4} {} {:-9}".format(
|
|
indent,
|
|
"{}[{}]".format(port_no, port_name),
|
|
int(mac_entry['vlan']),
|
|
eth_addr_to_string(mac_entry['mac']),
|
|
int(mac_entry['expires']))
|
|
|
|
if dbg:
|
|
line += " [(struct mac_entry *) {}]".format(mac_entry)
|
|
|
|
print(line)
|
|
|
|
@staticmethod
|
|
def display_ml_entries(ml, indent=0, hash=False, dbg=False):
|
|
indent = " " * indent
|
|
if ml is None:
|
|
return
|
|
|
|
print("\n{}FDB \"{}\" table:".format(indent,
|
|
"lrus" if not hash else "hash"))
|
|
print("{}port VLAN MAC Age out @".
|
|
format(indent))
|
|
print("{}----------------- ---- ----------------- ---------".
|
|
format(indent))
|
|
|
|
mac_entries = 0
|
|
|
|
if hash:
|
|
for mac_entry in ForEachHMAP(ml['table'],
|
|
"struct mac_entry",
|
|
"hmap_node"):
|
|
CmdShowFDB.display_mac_entry(mac_entry, len(indent), dbg)
|
|
mac_entries += 1
|
|
else:
|
|
for mac_entry in ForEachLIST(ml['lrus'],
|
|
"struct mac_entry",
|
|
"lru_node"):
|
|
CmdShowFDB.display_mac_entry(mac_entry, len(indent), dbg)
|
|
mac_entries += 1
|
|
|
|
print("\nTotal MAC entries: {}".format(mac_entries))
|
|
time_now = list(get_time_now())
|
|
time_now[1] = time_now[0] + time_now[1]
|
|
print("\n{}Current time is between {} and {} seconds.\n".
|
|
format(indent, min(time_now[0], time_now[1]),
|
|
max(time_now[0], time_now[1])))
|
|
|
|
def invoke(self, arg, from_tty):
|
|
arg_list = gdb.string_to_argv(arg)
|
|
|
|
all_ofproto_dpifs_by_name = get_global_variable(
|
|
'all_ofproto_dpifs_by_name')
|
|
if all_ofproto_dpifs_by_name is None:
|
|
return
|
|
|
|
all_name = dict()
|
|
max_name_len = 0
|
|
for node in ForEachHMAP(all_ofproto_dpifs_by_name,
|
|
"struct ofproto_dpif",
|
|
"all_ofproto_dpifs_by_name_node"):
|
|
|
|
all_name[node['up']['name'].string()] = node
|
|
if len(node['up']['name'].string()) > max_name_len:
|
|
max_name_len = len(node['up']['name'].string())
|
|
|
|
if len(arg_list) == 0:
|
|
for name in sorted(all_name.keys()):
|
|
print("{}: (struct mac_learning *) {}".
|
|
format(name.ljust(max_name_len),
|
|
all_name[name]['ml']))
|
|
|
|
self.display_ml_summary(all_name[name]['ml'], 4)
|
|
else:
|
|
if not arg_list[0] in all_name:
|
|
print("ERROR: Given bridge name is not known!")
|
|
return
|
|
|
|
ml = all_name[arg_list[0]]['ml']
|
|
self.display_ml_summary(ml, 0, "dbg" in arg_list[1:])
|
|
self.display_ml_entries(ml, 0, "hash" in arg_list[1:],
|
|
"dbg" in arg_list[1:])
|
|
|
|
|
|
#
|
|
# Implements the GDB "ovs_show_fdb" command
|
|
#
|
|
class CmdShowUpcall(gdb.Command):
|
|
"""Show upcall information
|
|
Usage:
|
|
ovs_show_upcall {dbg}
|
|
|
|
dbg : Will show structure address information
|
|
"""
|
|
|
|
def __init__(self):
|
|
super(CmdShowUpcall, self).__init__("ovs_show_upcall",
|
|
gdb.COMMAND_DATA)
|
|
|
|
@staticmethod
|
|
def display_udpif_upcall(udpif, indent=0, dbg=False):
|
|
indent = " " * indent
|
|
|
|
enable_ufid = get_global_variable('enable_ufid')
|
|
if enable_ufid is None:
|
|
return
|
|
|
|
dbg_str = ""
|
|
if dbg:
|
|
dbg_str = ", ((struct udpif *) {})".format(udpif)
|
|
|
|
print("{}{}{}:".format(
|
|
indent, udpif['dpif']['full_name'].string(),
|
|
dbg_str))
|
|
|
|
print("{} flows : (current {}) (avg {}) (max {}) (limit {})".
|
|
format(indent, udpif['n_flows'], udpif['avg_n_flows'],
|
|
udpif['max_n_flows'], udpif['flow_limit']))
|
|
print("{} dump duration : {}ms".
|
|
format(indent, udpif['dump_duration']))
|
|
print("{} ufid enabled : {}\n".
|
|
format(indent, enable_ufid &
|
|
udpif['backer']['rt_support']['ufid']))
|
|
|
|
for i in range(0, int(udpif['n_revalidators'])):
|
|
revalidator = udpif['revalidators'][i]
|
|
|
|
dbg_str = ""
|
|
if dbg:
|
|
dbg_str = ", ((struct revalidator *) {})".\
|
|
format(revalidator.address)
|
|
|
|
count = 0
|
|
j = i
|
|
spinner = ProgressIndicator("Counting all ukeys: ")
|
|
while j < N_UMAPS:
|
|
spinner.update()
|
|
count += udpif['ukeys'][j]['cmap']['impl']['p']['n']
|
|
j += int(udpif['n_revalidators'])
|
|
|
|
spinner.done()
|
|
print("{} {}: (keys {}){}".
|
|
format(indent, revalidator['id'], count, dbg_str))
|
|
|
|
print("")
|
|
|
|
def invoke(self, arg, from_tty):
|
|
arg_list = gdb.string_to_argv(arg)
|
|
|
|
all_udpifs = get_global_variable('all_udpifs')
|
|
if all_udpifs is None:
|
|
return
|
|
|
|
for udpif in ForEachLIST(all_udpifs, "struct udpif", "list_node"):
|
|
self.display_udpif_upcall(udpif, 0, "dbg" in arg_list)
|
|
|
|
|
|
#
|
|
# Implements the GDB "ovs_dump_ofpacts" command
|
|
#
|
|
class CmdDumpOfpacts(gdb.Command):
|
|
"""Dump all actions in an ofpacts set
|
|
Usage:
|
|
ovs_dump_ofpacts <struct ofpact *> <ofpacts_len>
|
|
|
|
<struct ofpact *> : Pointer to set of ofpact structures.
|
|
<ofpacts_len> : Total length of the set.
|
|
|
|
Example dumping all actions when in the clone_xlate_actions() function:
|
|
|
|
(gdb) ovs_dump_ofpacts actions actions_len
|
|
(struct ofpact *) 0x87c8: {type = OFPACT_SET_FIELD, raw = 255 '', len = 24}
|
|
(struct ofpact *) 0x87e0: {type = OFPACT_SET_FIELD, raw = 255 '', len = 24}
|
|
(struct ofpact *) 0x87f8: {type = OFPACT_SET_FIELD, raw = 255 '', len = 24}
|
|
(struct ofpact *) 0x8810: {type = OFPACT_SET_FIELD, raw = 255 '', len = 32}
|
|
(struct ofpact *) 0x8830: {type = OFPACT_SET_FIELD, raw = 255 '', len = 24}
|
|
(struct ofpact *) 0x8848: {type = OFPACT_RESUBMIT, raw = 38 '&', len = 16}
|
|
"""
|
|
def __init__(self):
|
|
super(CmdDumpOfpacts, self).__init__("ovs_dump_ofpacts",
|
|
gdb.COMMAND_DATA)
|
|
|
|
def invoke(self, arg, from_tty):
|
|
arg_list = gdb.string_to_argv(arg)
|
|
|
|
if len(arg_list) != 2:
|
|
print("usage: ovs_dump_ofpacts <struct ofpact *> <ofpacts_len>")
|
|
return
|
|
|
|
ofpacts = gdb.parse_and_eval(arg_list[0]).cast(
|
|
gdb.lookup_type('struct ofpact').pointer())
|
|
|
|
length = gdb.parse_and_eval(arg_list[1])
|
|
|
|
for node in ForEachOFPACTS(ofpacts, length):
|
|
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|dp_packet> [tcpdump options]
|
|
|
|
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.
|
|
|
|
Everything after the struct reference is passed into tcpdump. If nothing
|
|
is passed in as a tcpdump option, the default is "-n".
|
|
|
|
(gdb) ovs_dump_packets packets_
|
|
12:01:05.981214 ARP, Ethernet (len 6), IPv4 (len 4), Reply 10.1.1.1 is-at
|
|
a6:0f:c3:f0:5f:bd (oui Unknown), length 28
|
|
"""
|
|
def __init__(self):
|
|
super().__init__("ovs_dump_packets", gdb.COMMAND_DATA)
|
|
|
|
def invoke(self, arg, from_tty):
|
|
if Ether is None:
|
|
print("ERROR: This command requires scapy to be installed.")
|
|
return
|
|
|
|
arg_list = gdb.string_to_argv(arg)
|
|
if len(arg_list) == 0:
|
|
print("Usage: ovs_dump_packets <struct dp_packet_batch|"
|
|
"dp_packet> [tcpdump options]")
|
|
return
|
|
|
|
symb_name = arg_list[0]
|
|
tcpdump_args = arg_list[1:]
|
|
|
|
if not tcpdump_args:
|
|
# Add a sane default
|
|
tcpdump_args = ["-n"]
|
|
|
|
val = gdb.parse_and_eval(symb_name)
|
|
while val.type.code == gdb.TYPE_CODE_PTR:
|
|
val = val.dereference()
|
|
|
|
pkt_list = []
|
|
if str(val.type).startswith("struct dp_packet_batch"):
|
|
for idx in range(val['count']):
|
|
pkt_struct = val['packets'][idx].dereference()
|
|
pkt = self.extract_pkt(pkt_struct)
|
|
if pkt is None:
|
|
continue
|
|
pkt_list.append(pkt)
|
|
elif str(val.type) == "struct dp_packet":
|
|
pkt = self.extract_pkt(val)
|
|
if pkt is None:
|
|
return
|
|
pkt_list.append(pkt)
|
|
else:
|
|
print("Error, unsupported argument type: {}".format(str(val.type)))
|
|
return
|
|
|
|
stdout = tcpdump(pkt_list, args=tcpdump_args, getfd=True, quiet=True)
|
|
gdb.write(stdout.read().decode("utf8", "replace"))
|
|
|
|
def extract_pkt(self, pkt):
|
|
pkt_fields = pkt.type.keys()
|
|
if pkt['packet_type'] != 0:
|
|
return
|
|
if pkt['l3_ofs'] == 0xFFFF:
|
|
return
|
|
|
|
if "mbuf" in pkt_fields:
|
|
if pkt['mbuf']['data_off'] == 0xFFFF:
|
|
return
|
|
eth_ptr = pkt['mbuf']['buf_addr']
|
|
eth_off = int(pkt['mbuf']['data_off'])
|
|
eth_len = int(pkt['mbuf']['pkt_len'])
|
|
else:
|
|
if pkt['data_ofs'] == 0xFFFF:
|
|
return
|
|
eth_ptr = pkt['base_']
|
|
eth_off = int(pkt['data_ofs'])
|
|
eth_len = int(pkt['size_'])
|
|
|
|
if eth_ptr == 0 or eth_len < 1:
|
|
return
|
|
|
|
# Extract packet
|
|
pkt_ptr = eth_ptr.cast(
|
|
gdb.lookup_type('uint8_t').pointer()
|
|
)
|
|
pkt_ptr += eth_off
|
|
|
|
pkt_data = []
|
|
for idx in range(eth_len):
|
|
pkt_data.append(int(pkt_ptr[idx]))
|
|
|
|
pkt_data = struct.pack("{}B".format(eth_len), *pkt_data)
|
|
|
|
packet = Ether(pkt_data)
|
|
packet.len = int(eth_len)
|
|
|
|
return packet
|
|
|
|
|
|
#
|
|
# Initialize all GDB commands
|
|
#
|
|
CmdDumpBridge()
|
|
CmdDumpBridgePorts()
|
|
CmdDumpDpNetdev()
|
|
CmdDumpDpNetdevPollThreads()
|
|
CmdDumpDpNetdevPorts()
|
|
CmdDumpDpProvider()
|
|
CmdDumpNetdev()
|
|
CmdDumpNetdevProvider()
|
|
CmdDumpOfpacts()
|
|
CmdDumpOvsList()
|
|
CmdDumpPackets()
|
|
CmdDumpSimap()
|
|
CmdDumpSmap()
|
|
CmdDumpUdpifKeys()
|
|
CmdShowFDB()
|
|
CmdShowUpcall()
|