mirror of
https://github.com/FRRouting/frr.git
synced 2026-01-11 20:07:27 +00:00
gdb: Move the bgp macros to their own file
Not everyone wants to load the bgp macros when debugging a different daemon Signed-off-by: Donald Sharp <sharpd@nvidia.com>
This commit is contained in:
parent
d6a266deeb
commit
9eb855ff6b
2 changed files with 482 additions and 467 deletions
482
gdb/bgp.txt
Normal file
482
gdb/bgp.txt
Normal file
|
|
@ -0,0 +1,482 @@
|
|||
# GDB macros for use with FRR.
|
||||
#
|
||||
# Macros in this file are specific to bgpd/. Definitions here depend on the
|
||||
# lib.txt macros file, which must also be loaded.
|
||||
#
|
||||
# The macro files can be loaded with 'source <filename>'. They can then be
|
||||
# called by the user. Macros that explore more complicated structs generally
|
||||
# take pointer arguments.
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# (gdb) source gdb/lib.txt
|
||||
# (gdb) source gdb/bgp.txt
|
||||
# (gdb) walk_bgp_table bgp->rib[0][0]
|
||||
# (gdb) dump_bgp_path_info pi
|
||||
|
||||
define dump_bgp_path_flags
|
||||
set $flags = $arg0
|
||||
|
||||
if ($flags & (1 << 0))
|
||||
printf " IGP_CHANGED"
|
||||
end
|
||||
if ($flags & (1 << 1))
|
||||
printf " DAMPED"
|
||||
end
|
||||
if ($flags & (1 << 2))
|
||||
printf " HISTORY"
|
||||
end
|
||||
if ($flags & (1 << 3))
|
||||
printf " SELECTED"
|
||||
end
|
||||
if ($flags & (1 << 4))
|
||||
printf " VALID"
|
||||
end
|
||||
if ($flags & (1 << 5))
|
||||
printf " ATTR_CHANGED"
|
||||
end
|
||||
if ($flags & (1 << 6))
|
||||
printf " DMED_CHECK"
|
||||
end
|
||||
if ($flags & (1 << 7))
|
||||
printf " DMED_SELECTED"
|
||||
end
|
||||
if ($flags & (1 << 8))
|
||||
printf " STALE"
|
||||
end
|
||||
if ($flags & (1 << 9))
|
||||
printf " REMOVED"
|
||||
end
|
||||
if ($flags & (1 << 10))
|
||||
printf " COUNTED"
|
||||
end
|
||||
if ($flags & (1 << 11))
|
||||
printf " MULTIPATH"
|
||||
end
|
||||
if ($flags & (1 << 12))
|
||||
printf " MULTIPATH_CHG"
|
||||
end
|
||||
if ($flags & (1 << 13))
|
||||
printf " RIB_ATTR_CHG"
|
||||
end
|
||||
if ($flags & (1 << 14))
|
||||
printf " ANNC_NH_SELF"
|
||||
end
|
||||
if ($flags & (1 << 15))
|
||||
printf " LINK_BW_CHG"
|
||||
end
|
||||
if ($flags & (1 << 16))
|
||||
printf " ACCEPT_OWN"
|
||||
end
|
||||
if ($flags & (1 << 17))
|
||||
printf " MPLSVPN_LABEL_NH"
|
||||
end
|
||||
if ($flags & (1 << 18))
|
||||
printf " MPLSVPN_NH_LABEL_BIND"
|
||||
end
|
||||
if ($flags & (1 << 19))
|
||||
printf " UNSORTED"
|
||||
end
|
||||
if ($flags & (1 << 20))
|
||||
printf " MULTIPATH_NEW"
|
||||
end
|
||||
end
|
||||
document dump_bgp_path_flags
|
||||
Print human-readable BGP path info flags.
|
||||
|
||||
Argument: uint32_t flags value from bgp_path_info->flags
|
||||
end
|
||||
|
||||
define dump_bgp_path_info
|
||||
set $pi = (struct bgp_path_info *)$arg0
|
||||
set $indent = ""
|
||||
if ($argc > 1)
|
||||
set $indent = $arg1
|
||||
end
|
||||
|
||||
printf "%s bgp_path_info: 0x%lx\n", $indent, $pi
|
||||
printf "%s peer: 0x%lx", $indent, $pi->peer
|
||||
if ($pi->peer != 0)
|
||||
printf " (%s)", $pi->peer->host
|
||||
end
|
||||
printf "\n"
|
||||
|
||||
printf "%s type: %d, sub_type: %d", $indent, $pi->type, $pi->sub_type
|
||||
if ($pi->sub_type == 0)
|
||||
printf " (NORMAL)"
|
||||
end
|
||||
if ($pi->sub_type == 1)
|
||||
printf " (STATIC)"
|
||||
end
|
||||
if ($pi->sub_type == 2)
|
||||
printf " (AGGREGATE)"
|
||||
end
|
||||
if ($pi->sub_type == 3)
|
||||
printf " (REDISTRIBUTE)"
|
||||
end
|
||||
if ($pi->sub_type == 5)
|
||||
printf " (IMPORTED)"
|
||||
end
|
||||
printf "\n"
|
||||
|
||||
printf "%s flags: 0x%x", $indent, $pi->flags
|
||||
dump_bgp_path_flags $pi->flags
|
||||
printf "\n"
|
||||
|
||||
printf "%s uptime: %ld", $indent, $pi->uptime
|
||||
printf ", lock: %d\n", $pi->lock
|
||||
|
||||
if ($pi->attr != 0)
|
||||
printf "%s attr: 0x%lx", $indent, $pi->attr
|
||||
printf " (nexthop: "
|
||||
dump_s_addr &($pi->attr->nexthop)
|
||||
printf ")\n"
|
||||
end
|
||||
|
||||
if ($pi->extra != 0)
|
||||
printf "%s extra: 0x%lx", $indent, $pi->extra
|
||||
if ($pi->extra->labels != 0)
|
||||
printf " [has labels]"
|
||||
end
|
||||
if ($pi->extra->evpn != 0)
|
||||
printf " [has evpn]"
|
||||
end
|
||||
if ($pi->extra->vrfleak != 0)
|
||||
printf " [has vrfleak]"
|
||||
end
|
||||
printf "\n"
|
||||
end
|
||||
|
||||
printf "%s next: 0x%lx, prev: 0x%lx\n", $indent, $pi->next, $pi->prev
|
||||
end
|
||||
document dump_bgp_path_info
|
||||
Dump detailed information about a single bgp_path_info structure.
|
||||
|
||||
Arguments:
|
||||
1st: (struct bgp_path_info *) pointer to the path info to dump
|
||||
2nd: (optional) string for indentation
|
||||
end
|
||||
|
||||
define walk_bgp_table_subtree
|
||||
# Internal helper to walk a single BGP table
|
||||
# Args: bgp_table, indent_level, is_two_level
|
||||
set $_btable = (struct bgp_table *)$arg0
|
||||
set $_indent_level = $arg1
|
||||
set $_is_two_level = $arg2
|
||||
|
||||
if ($_btable == 0)
|
||||
return
|
||||
end
|
||||
|
||||
set $_rt = $_btable->route_table
|
||||
|
||||
if ($_rt == 0)
|
||||
return
|
||||
end
|
||||
|
||||
set $_rn = ((struct route_table *)$_rt)->top
|
||||
set $_top_node = $_rn
|
||||
|
||||
if ($_rn == 0)
|
||||
return
|
||||
end
|
||||
|
||||
# Iterate through all nodes using the standard route table walker
|
||||
while ($_rn != 0)
|
||||
set $_dest = 0
|
||||
if ($_rn->info != 0)
|
||||
set $_dest = (struct bgp_dest *)$_rn->info
|
||||
end
|
||||
|
||||
if ($_dest != 0)
|
||||
if ($_dest->info != 0)
|
||||
# Check if this is a two-level table (EVPN, MPLS VPN, ENCAP)
|
||||
# In two-level tables, dest->info points to another bgp_table
|
||||
if ($_is_two_level == 1)
|
||||
set $_subtable = (struct bgp_table *)$_dest->info
|
||||
printf "\n=== RD: "
|
||||
|
||||
# RD is stored as a prefix, extract and display it
|
||||
set $_rd_p = &((struct route_node *)$_rn)->p
|
||||
set $_rd_bytes = (unsigned char *)&$_rd_p->u
|
||||
|
||||
# RD format: 2 bytes type + 6 bytes value
|
||||
# Type 0: 2-byte AS : 4-byte number
|
||||
# Type 1: 4-byte IPv4 : 2-byte number (most common for EVPN)
|
||||
# Type 2: 4-byte AS : 2-byte number
|
||||
|
||||
set $_rd_type = ($_rd_bytes[0] << 8) | $_rd_bytes[1]
|
||||
|
||||
if ($_rd_type == 1)
|
||||
# Type 1: IPv4:number
|
||||
printf "%d.%d.%d.%d:%d", $_rd_bytes[2], $_rd_bytes[3], $_rd_bytes[4], $_rd_bytes[5], ($_rd_bytes[6] << 8) | $_rd_bytes[7]
|
||||
else
|
||||
if ($_rd_type == 0)
|
||||
# Type 0: AS:number
|
||||
set $_rd_as = ($_rd_bytes[2] << 8) | $_rd_bytes[3]
|
||||
set $_rd_num = ($_rd_bytes[4] << 24) | ($_rd_bytes[5] << 16) | ($_rd_bytes[6] << 8) | $_rd_bytes[7]
|
||||
printf "%d:%d", $_rd_as, $_rd_num
|
||||
else
|
||||
if ($_rd_type == 2)
|
||||
# Type 2: 4-byte AS:number
|
||||
set $_rd_as = ($_rd_bytes[2] << 24) | ($_rd_bytes[3] << 16) | ($_rd_bytes[4] << 8) | $_rd_bytes[5]
|
||||
set $_rd_num = ($_rd_bytes[6] << 8) | $_rd_bytes[7]
|
||||
printf "%d:%d", $_rd_as, $_rd_num
|
||||
else
|
||||
# Unknown type, display raw
|
||||
printf "%02x%02x:%02x%02x:%02x%02x:%02x%02x", $_rd_bytes[0], $_rd_bytes[1], $_rd_bytes[2], $_rd_bytes[3], $_rd_bytes[4], $_rd_bytes[5], $_rd_bytes[6], $_rd_bytes[7]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
printf " ===\n"
|
||||
|
||||
# Recursively walk the sub-table
|
||||
# Save parent state since GDB macros don't have local scope
|
||||
if ($_subtable != 0)
|
||||
set $_saved_rn = $_rn
|
||||
set $_saved_top = $_top_node
|
||||
set $_saved_is_two_level = $_is_two_level
|
||||
set $_saved_indent = $_indent_level
|
||||
walk_bgp_table_subtree $_subtable 1 0
|
||||
# Restore parent state
|
||||
set $_rn = $_saved_rn
|
||||
set $_top_node = $_saved_top
|
||||
set $_is_two_level = $_saved_is_two_level
|
||||
set $_indent_level = $_saved_indent
|
||||
end
|
||||
else
|
||||
# This is a regular table, dest->info points to bgp_path_info
|
||||
set $_pi = (struct bgp_path_info *)$_dest->info
|
||||
|
||||
set $_dest_count = $_dest_count + 1
|
||||
|
||||
if ($_indent_level == 0)
|
||||
printf "\n=== Dest #%d: 0x%lx ===\n", $_dest_count, $_dest
|
||||
else
|
||||
printf "\n === Dest #%d: 0x%lx ===\n", $_dest_count, $_dest
|
||||
end
|
||||
|
||||
if ($_indent_level == 0)
|
||||
printf "Prefix: "
|
||||
else
|
||||
printf " Prefix: "
|
||||
end
|
||||
|
||||
# Check if this is an EVPN prefix (AF_EVPN = 47 on this system)
|
||||
set $_p = &((struct route_node *)$_rn)->p
|
||||
if ($_p->family == 47)
|
||||
# EVPN prefix - decode the route type
|
||||
set $_evpn_bytes = (unsigned char *)&$_p->u
|
||||
set $_evpn_route_type = $_evpn_bytes[0]
|
||||
|
||||
if ($_evpn_route_type == 2)
|
||||
# Type 2: MAC/IP route - format: [type]:[eth_tag]:[mac_len]:[mac]:[ip_len]:[ip]
|
||||
set $_evpn_p = (struct prefix_evpn *)$_p
|
||||
set $_eth_tag = $_evpn_p->prefix.macip_addr.eth_tag
|
||||
set $_mac_bytes = (unsigned char *)&$_evpn_p->prefix.macip_addr.mac
|
||||
|
||||
printf "[%d]:[%u]:[%d]:%02x:%02x:%02x:%02x:%02x:%02x", $_evpn_route_type, $_eth_tag, 48, $_mac_bytes[0], $_mac_bytes[1], $_mac_bytes[2], $_mac_bytes[3], $_mac_bytes[4], $_mac_bytes[5]
|
||||
|
||||
set $_ip_type = $_evpn_p->prefix.macip_addr.ip.ipa_type
|
||||
if ($_ip_type == 2)
|
||||
# IPv4 (AF_INET = 2)
|
||||
printf ":[32]:"
|
||||
dump_s_addr &$_evpn_p->prefix.macip_addr.ip.ipaddr_v4
|
||||
else
|
||||
if ($_ip_type == 10)
|
||||
# IPv6 (AF_INET6 = 10)
|
||||
printf ":[128]:"
|
||||
dump_s6_addr &$_evpn_p->prefix.macip_addr.ip.ipaddr_v6
|
||||
end
|
||||
end
|
||||
else
|
||||
if ($_evpn_route_type == 3)
|
||||
# Type 3: IMET route - format: [type]:[eth_tag]:[ip_len]:[ip]
|
||||
set $_evpn_p = (struct prefix_evpn *)$_p
|
||||
set $_eth_tag = $_evpn_p->prefix.imet_addr.eth_tag
|
||||
set $_ip_type = $_evpn_p->prefix.imet_addr.ip.ipa_type
|
||||
|
||||
if ($_ip_type == 2)
|
||||
# IPv4 (AF_INET = 2)
|
||||
printf "[%d]:[%u]:[32]:", $_evpn_route_type, $_eth_tag
|
||||
dump_s_addr &$_evpn_p->prefix.imet_addr.ip.ipaddr_v4
|
||||
else
|
||||
if ($_ip_type == 10)
|
||||
# IPv6 (AF_INET6 = 10)
|
||||
printf "[%d]:[%u]:[128]:", $_evpn_route_type, $_eth_tag
|
||||
dump_s6_addr &$_evpn_p->prefix.imet_addr.ip.ipaddr_v6
|
||||
else
|
||||
printf "[%d]:[%u]:[?]", $_evpn_route_type, $_eth_tag
|
||||
end
|
||||
end
|
||||
else
|
||||
if ($_evpn_route_type == 5)
|
||||
# Type 5: IP Prefix route - format: [type]:[eth_tag]:[prefix_len]:[ip]
|
||||
set $_evpn_p = (struct prefix_evpn *)$_p
|
||||
set $_eth_tag = $_evpn_p->prefix.prefix_addr.eth_tag
|
||||
set $_prefix_len = $_evpn_p->prefix.prefix_addr.ip_prefix_length
|
||||
set $_ip_type = $_evpn_p->prefix.prefix_addr.ip.ipa_type
|
||||
|
||||
printf "[%d]:[%u]:[%d]:", $_evpn_route_type, $_eth_tag, $_prefix_len
|
||||
|
||||
if ($_ip_type == 2)
|
||||
# IPv4 (AF_INET = 2)
|
||||
dump_s_addr &$_evpn_p->prefix.prefix_addr.ip.ipaddr_v4
|
||||
else
|
||||
if ($_ip_type == 10)
|
||||
# IPv6 (AF_INET6 = 10)
|
||||
dump_s6_addr &$_evpn_p->prefix.prefix_addr.ip.ipaddr_v6
|
||||
else
|
||||
printf "Unknown-IP-Type-%d", $_ip_type
|
||||
end
|
||||
end
|
||||
else
|
||||
# Other types
|
||||
printf "[%d]:[Type-%d]", $_evpn_route_type, $_evpn_route_type
|
||||
end
|
||||
end
|
||||
end
|
||||
printf "\n"
|
||||
else
|
||||
dump_prefix $_p
|
||||
end
|
||||
|
||||
if ($_indent_level == 0)
|
||||
printf " dest->flags: 0x%x", $_dest->flags
|
||||
else
|
||||
printf " dest->flags: 0x%x", $_dest->flags
|
||||
end
|
||||
if ($_dest->flags & (1 << 0))
|
||||
printf " PROCESS_SCHEDULED"
|
||||
end
|
||||
if ($_dest->flags & (1 << 1))
|
||||
printf " USER_CLEAR"
|
||||
end
|
||||
if ($_dest->flags & (1 << 3))
|
||||
printf " SELECTED"
|
||||
end
|
||||
printf "\n"
|
||||
|
||||
# Walk through all path_info structures for this destination
|
||||
set $_pi_num = 0
|
||||
while ($_pi != 0)
|
||||
set $_pi_num = $_pi_num + 1
|
||||
set $_path_count = $_path_count + 1
|
||||
|
||||
if ($_indent_level == 0)
|
||||
printf " --- Path #%d ---\n", $_pi_num
|
||||
dump_bgp_path_info $_pi " "
|
||||
else
|
||||
printf " --- Path #%d ---\n", $_pi_num
|
||||
dump_bgp_path_info $_pi " "
|
||||
end
|
||||
|
||||
set $_pi = $_pi->next
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Move to next route node - simple left-right-up traversal
|
||||
set $_next = 0
|
||||
|
||||
# Try going left first
|
||||
if ($_rn != 0)
|
||||
set $_left = ((struct route_node *)$_rn)->link[0]
|
||||
if ($_left != 0)
|
||||
set $_next = $_left
|
||||
end
|
||||
end
|
||||
|
||||
# If no left, try right
|
||||
if ($_next == 0 && $_rn != 0)
|
||||
set $_right = ((struct route_node *)$_rn)->link[1]
|
||||
if ($_right != 0)
|
||||
set $_next = $_right
|
||||
end
|
||||
end
|
||||
|
||||
# If no left or right, go up until we find an unvisited right branch
|
||||
if ($_next == 0 && $_rn != 0)
|
||||
set $_current = $_rn
|
||||
set $_parent = ((struct route_node *)$_current)->parent
|
||||
|
||||
while ($_parent != 0 && $_next == 0)
|
||||
# Check if we came from the left child
|
||||
set $_parent_left = ((struct route_node *)$_parent)->link[0]
|
||||
if ($_parent_left == $_current)
|
||||
# Try the right child
|
||||
set $_parent_right = ((struct route_node *)$_parent)->link[1]
|
||||
if ($_parent_right != 0)
|
||||
set $_next = $_parent_right
|
||||
end
|
||||
end
|
||||
|
||||
# Move up
|
||||
if ($_next == 0)
|
||||
set $_current = $_parent
|
||||
if ($_parent != 0)
|
||||
set $_parent = ((struct route_node *)$_current)->parent
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
set $_rn = $_next
|
||||
end
|
||||
end
|
||||
document walk_bgp_table_subtree
|
||||
Internal helper for walk_bgp_table - walks a route_table recursively.
|
||||
end
|
||||
|
||||
define walk_bgp_table
|
||||
set $_table = (struct bgp_table *)$arg0
|
||||
set $_verbose = 0
|
||||
if ($argc > 1)
|
||||
set $_verbose = $arg1
|
||||
end
|
||||
|
||||
if ($_table == 0)
|
||||
printf "Error: NULL table pointer\n"
|
||||
else
|
||||
printf "Walking BGP table at 0x%lx\n", $_table
|
||||
printf " AFI: %d, SAFI: %d\n", $_table->afi, $_table->safi
|
||||
printf " Version: %lu\n", $_table->version
|
||||
|
||||
set $_dest_count = 0
|
||||
set $_path_count = 0
|
||||
|
||||
# Check if this is a two-level table (EVPN=5, MPLS_VPN=128, ENCAP=7)
|
||||
set $_is_two_level = 0
|
||||
if ($_table->safi == 5 || $_table->safi == 128 || $_table->safi == 7)
|
||||
set $_is_two_level = 1
|
||||
printf " (Two-level table: RD -> Routes)\n"
|
||||
end
|
||||
|
||||
walk_bgp_table_subtree $_table 0 $_is_two_level
|
||||
|
||||
printf "\n=== Summary ===\n"
|
||||
printf "Total destinations with paths: %d\n", $_dest_count
|
||||
printf "Total paths: %d\n", $_path_count
|
||||
end
|
||||
end
|
||||
document walk_bgp_table
|
||||
Walk through a BGP table and dump all bgp_path_info structures with their state.
|
||||
|
||||
This macro iterates through all route nodes in a BGP table, and for each
|
||||
destination that has path information, it dumps all the bgp_path_info
|
||||
structures in the linked list along with their key state information including:
|
||||
- Peer information
|
||||
- Route type and sub-type
|
||||
- Flags (SELECTED, VALID, MULTIPATH, etc.)
|
||||
- Attributes (nexthop, etc.)
|
||||
- Extra information (EVPN, labels, vrfleak)
|
||||
|
||||
Arguments:
|
||||
(struct bgp_table *) pointer to the BGP table to walk
|
||||
|
||||
Example usage:
|
||||
(gdb) p bgp->rib[0][0]
|
||||
(gdb) walk_bgp_table bgp->rib[0][0]
|
||||
end
|
||||
467
gdb/lib.txt
467
gdb/lib.txt
|
|
@ -364,470 +364,3 @@ Print the capacity of the given dynamic array, and store in $_.
|
|||
Argument: a pointer to a darr dynamic array.
|
||||
Returns: capacity of the array.
|
||||
end
|
||||
|
||||
define dump_bgp_path_flags
|
||||
set $flags = $arg0
|
||||
|
||||
if ($flags & (1 << 0))
|
||||
printf " IGP_CHANGED"
|
||||
end
|
||||
if ($flags & (1 << 1))
|
||||
printf " DAMPED"
|
||||
end
|
||||
if ($flags & (1 << 2))
|
||||
printf " HISTORY"
|
||||
end
|
||||
if ($flags & (1 << 3))
|
||||
printf " SELECTED"
|
||||
end
|
||||
if ($flags & (1 << 4))
|
||||
printf " VALID"
|
||||
end
|
||||
if ($flags & (1 << 5))
|
||||
printf " ATTR_CHANGED"
|
||||
end
|
||||
if ($flags & (1 << 6))
|
||||
printf " DMED_CHECK"
|
||||
end
|
||||
if ($flags & (1 << 7))
|
||||
printf " DMED_SELECTED"
|
||||
end
|
||||
if ($flags & (1 << 8))
|
||||
printf " STALE"
|
||||
end
|
||||
if ($flags & (1 << 9))
|
||||
printf " REMOVED"
|
||||
end
|
||||
if ($flags & (1 << 10))
|
||||
printf " COUNTED"
|
||||
end
|
||||
if ($flags & (1 << 11))
|
||||
printf " MULTIPATH"
|
||||
end
|
||||
if ($flags & (1 << 12))
|
||||
printf " MULTIPATH_CHG"
|
||||
end
|
||||
if ($flags & (1 << 13))
|
||||
printf " RIB_ATTR_CHG"
|
||||
end
|
||||
if ($flags & (1 << 14))
|
||||
printf " ANNC_NH_SELF"
|
||||
end
|
||||
if ($flags & (1 << 15))
|
||||
printf " LINK_BW_CHG"
|
||||
end
|
||||
if ($flags & (1 << 16))
|
||||
printf " ACCEPT_OWN"
|
||||
end
|
||||
if ($flags & (1 << 17))
|
||||
printf " MPLSVPN_LABEL_NH"
|
||||
end
|
||||
if ($flags & (1 << 18))
|
||||
printf " MPLSVPN_NH_LABEL_BIND"
|
||||
end
|
||||
if ($flags & (1 << 19))
|
||||
printf " UNSORTED"
|
||||
end
|
||||
if ($flags & (1 << 20))
|
||||
printf " MULTIPATH_NEW"
|
||||
end
|
||||
end
|
||||
document dump_bgp_path_flags
|
||||
Print human-readable BGP path info flags.
|
||||
|
||||
Argument: uint32_t flags value from bgp_path_info->flags
|
||||
end
|
||||
|
||||
define dump_bgp_path_info
|
||||
set $pi = (struct bgp_path_info *)$arg0
|
||||
set $indent = ""
|
||||
if ($argc > 1)
|
||||
set $indent = $arg1
|
||||
end
|
||||
|
||||
printf "%s bgp_path_info: 0x%lx\n", $indent, $pi
|
||||
printf "%s peer: 0x%lx", $indent, $pi->peer
|
||||
if ($pi->peer != 0)
|
||||
printf " (%s)", $pi->peer->host
|
||||
end
|
||||
printf "\n"
|
||||
|
||||
printf "%s type: %d, sub_type: %d", $indent, $pi->type, $pi->sub_type
|
||||
if ($pi->sub_type == 0)
|
||||
printf " (NORMAL)"
|
||||
end
|
||||
if ($pi->sub_type == 1)
|
||||
printf " (STATIC)"
|
||||
end
|
||||
if ($pi->sub_type == 2)
|
||||
printf " (AGGREGATE)"
|
||||
end
|
||||
if ($pi->sub_type == 3)
|
||||
printf " (REDISTRIBUTE)"
|
||||
end
|
||||
if ($pi->sub_type == 5)
|
||||
printf " (IMPORTED)"
|
||||
end
|
||||
printf "\n"
|
||||
|
||||
printf "%s flags: 0x%x", $indent, $pi->flags
|
||||
dump_bgp_path_flags $pi->flags
|
||||
printf "\n"
|
||||
|
||||
printf "%s uptime: %ld", $indent, $pi->uptime
|
||||
printf ", lock: %d\n", $pi->lock
|
||||
|
||||
if ($pi->attr != 0)
|
||||
printf "%s attr: 0x%lx", $indent, $pi->attr
|
||||
printf " (nexthop: "
|
||||
dump_s_addr &($pi->attr->nexthop)
|
||||
printf ")\n"
|
||||
end
|
||||
|
||||
if ($pi->extra != 0)
|
||||
printf "%s extra: 0x%lx", $indent, $pi->extra
|
||||
if ($pi->extra->labels != 0)
|
||||
printf " [has labels]"
|
||||
end
|
||||
if ($pi->extra->evpn != 0)
|
||||
printf " [has evpn]"
|
||||
end
|
||||
if ($pi->extra->vrfleak != 0)
|
||||
printf " [has vrfleak]"
|
||||
end
|
||||
printf "\n"
|
||||
end
|
||||
|
||||
printf "%s next: 0x%lx, prev: 0x%lx\n", $indent, $pi->next, $pi->prev
|
||||
end
|
||||
document dump_bgp_path_info
|
||||
Dump detailed information about a single bgp_path_info structure.
|
||||
|
||||
Arguments:
|
||||
1st: (struct bgp_path_info *) pointer to the path info to dump
|
||||
2nd: (optional) string for indentation
|
||||
end
|
||||
|
||||
define walk_bgp_table_subtree
|
||||
# Internal helper to walk a single BGP table
|
||||
# Args: bgp_table, indent_level, is_two_level
|
||||
set $_btable = (struct bgp_table *)$arg0
|
||||
set $_indent_level = $arg1
|
||||
set $_is_two_level = $arg2
|
||||
|
||||
if ($_btable == 0)
|
||||
return
|
||||
end
|
||||
|
||||
set $_rt = $_btable->route_table
|
||||
|
||||
if ($_rt == 0)
|
||||
return
|
||||
end
|
||||
|
||||
set $_rn = ((struct route_table *)$_rt)->top
|
||||
set $_top_node = $_rn
|
||||
|
||||
if ($_rn == 0)
|
||||
return
|
||||
end
|
||||
|
||||
# Iterate through all nodes using the standard route table walker
|
||||
while ($_rn != 0)
|
||||
set $_dest = 0
|
||||
if ($_rn->info != 0)
|
||||
set $_dest = (struct bgp_dest *)$_rn->info
|
||||
end
|
||||
|
||||
if ($_dest != 0)
|
||||
if ($_dest->info != 0)
|
||||
# Check if this is a two-level table (EVPN, MPLS VPN, ENCAP)
|
||||
# In two-level tables, dest->info points to another bgp_table
|
||||
if ($_is_two_level == 1)
|
||||
set $_subtable = (struct bgp_table *)$_dest->info
|
||||
printf "\n=== RD: "
|
||||
|
||||
# RD is stored as a prefix, extract and display it
|
||||
set $_rd_p = &((struct route_node *)$_rn)->p
|
||||
set $_rd_bytes = (unsigned char *)&$_rd_p->u
|
||||
|
||||
# RD format: 2 bytes type + 6 bytes value
|
||||
# Type 0: 2-byte AS : 4-byte number
|
||||
# Type 1: 4-byte IPv4 : 2-byte number (most common for EVPN)
|
||||
# Type 2: 4-byte AS : 2-byte number
|
||||
|
||||
set $_rd_type = ($_rd_bytes[0] << 8) | $_rd_bytes[1]
|
||||
|
||||
if ($_rd_type == 1)
|
||||
# Type 1: IPv4:number
|
||||
printf "%d.%d.%d.%d:%d", $_rd_bytes[2], $_rd_bytes[3], $_rd_bytes[4], $_rd_bytes[5], ($_rd_bytes[6] << 8) | $_rd_bytes[7]
|
||||
else
|
||||
if ($_rd_type == 0)
|
||||
# Type 0: AS:number
|
||||
set $_rd_as = ($_rd_bytes[2] << 8) | $_rd_bytes[3]
|
||||
set $_rd_num = ($_rd_bytes[4] << 24) | ($_rd_bytes[5] << 16) | ($_rd_bytes[6] << 8) | $_rd_bytes[7]
|
||||
printf "%d:%d", $_rd_as, $_rd_num
|
||||
else
|
||||
if ($_rd_type == 2)
|
||||
# Type 2: 4-byte AS:number
|
||||
set $_rd_as = ($_rd_bytes[2] << 24) | ($_rd_bytes[3] << 16) | ($_rd_bytes[4] << 8) | $_rd_bytes[5]
|
||||
set $_rd_num = ($_rd_bytes[6] << 8) | $_rd_bytes[7]
|
||||
printf "%d:%d", $_rd_as, $_rd_num
|
||||
else
|
||||
# Unknown type, display raw
|
||||
printf "%02x%02x:%02x%02x:%02x%02x:%02x%02x", $_rd_bytes[0], $_rd_bytes[1], $_rd_bytes[2], $_rd_bytes[3], $_rd_bytes[4], $_rd_bytes[5], $_rd_bytes[6], $_rd_bytes[7]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
printf " ===\n"
|
||||
|
||||
# Recursively walk the sub-table
|
||||
# Save parent state since GDB macros don't have local scope
|
||||
if ($_subtable != 0)
|
||||
set $_saved_rn = $_rn
|
||||
set $_saved_top = $_top_node
|
||||
set $_saved_is_two_level = $_is_two_level
|
||||
set $_saved_indent = $_indent_level
|
||||
walk_bgp_table_subtree $_subtable 1 0
|
||||
# Restore parent state
|
||||
set $_rn = $_saved_rn
|
||||
set $_top_node = $_saved_top
|
||||
set $_is_two_level = $_saved_is_two_level
|
||||
set $_indent_level = $_saved_indent
|
||||
end
|
||||
else
|
||||
# This is a regular table, dest->info points to bgp_path_info
|
||||
set $_pi = (struct bgp_path_info *)$_dest->info
|
||||
|
||||
set $_dest_count = $_dest_count + 1
|
||||
|
||||
if ($_indent_level == 0)
|
||||
printf "\n=== Dest #%d: 0x%lx ===\n", $_dest_count, $_dest
|
||||
else
|
||||
printf "\n === Dest #%d: 0x%lx ===\n", $_dest_count, $_dest
|
||||
end
|
||||
|
||||
if ($_indent_level == 0)
|
||||
printf "Prefix: "
|
||||
else
|
||||
printf " Prefix: "
|
||||
end
|
||||
|
||||
# Check if this is an EVPN prefix (AF_EVPN = 47 on this system)
|
||||
set $_p = &((struct route_node *)$_rn)->p
|
||||
if ($_p->family == 47)
|
||||
# EVPN prefix - decode the route type
|
||||
set $_evpn_bytes = (unsigned char *)&$_p->u
|
||||
set $_evpn_route_type = $_evpn_bytes[0]
|
||||
|
||||
if ($_evpn_route_type == 2)
|
||||
# Type 2: MAC/IP route - format: [type]:[eth_tag]:[mac_len]:[mac]:[ip_len]:[ip]
|
||||
set $_evpn_p = (struct prefix_evpn *)$_p
|
||||
set $_eth_tag = $_evpn_p->prefix.macip_addr.eth_tag
|
||||
set $_mac_bytes = (unsigned char *)&$_evpn_p->prefix.macip_addr.mac
|
||||
|
||||
printf "[%d]:[%u]:[%d]:%02x:%02x:%02x:%02x:%02x:%02x", $_evpn_route_type, $_eth_tag, 48, $_mac_bytes[0], $_mac_bytes[1], $_mac_bytes[2], $_mac_bytes[3], $_mac_bytes[4], $_mac_bytes[5]
|
||||
|
||||
set $_ip_type = $_evpn_p->prefix.macip_addr.ip.ipa_type
|
||||
if ($_ip_type == 2)
|
||||
# IPv4 (AF_INET = 2)
|
||||
printf ":[32]:"
|
||||
dump_s_addr &$_evpn_p->prefix.macip_addr.ip.ipaddr_v4
|
||||
else
|
||||
if ($_ip_type == 10)
|
||||
# IPv6 (AF_INET6 = 10)
|
||||
printf ":[128]:"
|
||||
dump_s6_addr &$_evpn_p->prefix.macip_addr.ip.ipaddr_v6
|
||||
end
|
||||
end
|
||||
else
|
||||
if ($_evpn_route_type == 3)
|
||||
# Type 3: IMET route - format: [type]:[eth_tag]:[ip_len]:[ip]
|
||||
set $_evpn_p = (struct prefix_evpn *)$_p
|
||||
set $_eth_tag = $_evpn_p->prefix.imet_addr.eth_tag
|
||||
set $_ip_type = $_evpn_p->prefix.imet_addr.ip.ipa_type
|
||||
|
||||
if ($_ip_type == 2)
|
||||
# IPv4 (AF_INET = 2)
|
||||
printf "[%d]:[%u]:[32]:", $_evpn_route_type, $_eth_tag
|
||||
dump_s_addr &$_evpn_p->prefix.imet_addr.ip.ipaddr_v4
|
||||
else
|
||||
if ($_ip_type == 10)
|
||||
# IPv6 (AF_INET6 = 10)
|
||||
printf "[%d]:[%u]:[128]:", $_evpn_route_type, $_eth_tag
|
||||
dump_s6_addr &$_evpn_p->prefix.imet_addr.ip.ipaddr_v6
|
||||
else
|
||||
printf "[%d]:[%u]:[?]", $_evpn_route_type, $_eth_tag
|
||||
end
|
||||
end
|
||||
else
|
||||
if ($_evpn_route_type == 5)
|
||||
# Type 5: IP Prefix route - format: [type]:[eth_tag]:[prefix_len]:[ip]
|
||||
set $_evpn_p = (struct prefix_evpn *)$_p
|
||||
set $_eth_tag = $_evpn_p->prefix.prefix_addr.eth_tag
|
||||
set $_prefix_len = $_evpn_p->prefix.prefix_addr.ip_prefix_length
|
||||
set $_ip_type = $_evpn_p->prefix.prefix_addr.ip.ipa_type
|
||||
|
||||
printf "[%d]:[%u]:[%d]:", $_evpn_route_type, $_eth_tag, $_prefix_len
|
||||
|
||||
if ($_ip_type == 2)
|
||||
# IPv4 (AF_INET = 2)
|
||||
dump_s_addr &$_evpn_p->prefix.prefix_addr.ip.ipaddr_v4
|
||||
else
|
||||
if ($_ip_type == 10)
|
||||
# IPv6 (AF_INET6 = 10)
|
||||
dump_s6_addr &$_evpn_p->prefix.prefix_addr.ip.ipaddr_v6
|
||||
else
|
||||
printf "Unknown-IP-Type-%d", $_ip_type
|
||||
end
|
||||
end
|
||||
else
|
||||
# Other types
|
||||
printf "[%d]:[Type-%d]", $_evpn_route_type, $_evpn_route_type
|
||||
end
|
||||
end
|
||||
end
|
||||
printf "\n"
|
||||
else
|
||||
dump_prefix $_p
|
||||
end
|
||||
|
||||
if ($_indent_level == 0)
|
||||
printf " dest->flags: 0x%x", $_dest->flags
|
||||
else
|
||||
printf " dest->flags: 0x%x", $_dest->flags
|
||||
end
|
||||
if ($_dest->flags & (1 << 0))
|
||||
printf " PROCESS_SCHEDULED"
|
||||
end
|
||||
if ($_dest->flags & (1 << 1))
|
||||
printf " USER_CLEAR"
|
||||
end
|
||||
if ($_dest->flags & (1 << 3))
|
||||
printf " SELECTED"
|
||||
end
|
||||
printf "\n"
|
||||
|
||||
# Walk through all path_info structures for this destination
|
||||
set $_pi_num = 0
|
||||
while ($_pi != 0)
|
||||
set $_pi_num = $_pi_num + 1
|
||||
set $_path_count = $_path_count + 1
|
||||
|
||||
if ($_indent_level == 0)
|
||||
printf " --- Path #%d ---\n", $_pi_num
|
||||
dump_bgp_path_info $_pi " "
|
||||
else
|
||||
printf " --- Path #%d ---\n", $_pi_num
|
||||
dump_bgp_path_info $_pi " "
|
||||
end
|
||||
|
||||
set $_pi = $_pi->next
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Move to next route node - simple left-right-up traversal
|
||||
set $_next = 0
|
||||
|
||||
# Try going left first
|
||||
if ($_rn != 0)
|
||||
set $_left = ((struct route_node *)$_rn)->link[0]
|
||||
if ($_left != 0)
|
||||
set $_next = $_left
|
||||
end
|
||||
end
|
||||
|
||||
# If no left, try right
|
||||
if ($_next == 0 && $_rn != 0)
|
||||
set $_right = ((struct route_node *)$_rn)->link[1]
|
||||
if ($_right != 0)
|
||||
set $_next = $_right
|
||||
end
|
||||
end
|
||||
|
||||
# If no left or right, go up until we find an unvisited right branch
|
||||
if ($_next == 0 && $_rn != 0)
|
||||
set $_current = $_rn
|
||||
set $_parent = ((struct route_node *)$_current)->parent
|
||||
|
||||
while ($_parent != 0 && $_next == 0)
|
||||
# Check if we came from the left child
|
||||
set $_parent_left = ((struct route_node *)$_parent)->link[0]
|
||||
if ($_parent_left == $_current)
|
||||
# Try the right child
|
||||
set $_parent_right = ((struct route_node *)$_parent)->link[1]
|
||||
if ($_parent_right != 0)
|
||||
set $_next = $_parent_right
|
||||
end
|
||||
end
|
||||
|
||||
# Move up
|
||||
if ($_next == 0)
|
||||
set $_current = $_parent
|
||||
if ($_parent != 0)
|
||||
set $_parent = ((struct route_node *)$_current)->parent
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
set $_rn = $_next
|
||||
end
|
||||
end
|
||||
document walk_bgp_table_subtree
|
||||
Internal helper for walk_bgp_table - walks a route_table recursively.
|
||||
end
|
||||
|
||||
define walk_bgp_table
|
||||
set $_table = (struct bgp_table *)$arg0
|
||||
set $_verbose = 0
|
||||
if ($argc > 1)
|
||||
set $_verbose = $arg1
|
||||
end
|
||||
|
||||
if ($_table == 0)
|
||||
printf "Error: NULL table pointer\n"
|
||||
else
|
||||
printf "Walking BGP table at 0x%lx\n", $_table
|
||||
printf " AFI: %d, SAFI: %d\n", $_table->afi, $_table->safi
|
||||
printf " Version: %lu\n", $_table->version
|
||||
|
||||
set $_dest_count = 0
|
||||
set $_path_count = 0
|
||||
|
||||
# Check if this is a two-level table (EVPN=5, MPLS_VPN=128, ENCAP=7)
|
||||
set $_is_two_level = 0
|
||||
if ($_table->safi == 5 || $_table->safi == 128 || $_table->safi == 7)
|
||||
set $_is_two_level = 1
|
||||
printf " (Two-level table: RD -> Routes)\n"
|
||||
end
|
||||
|
||||
walk_bgp_table_subtree $_table 0 $_is_two_level
|
||||
|
||||
printf "\n=== Summary ===\n"
|
||||
printf "Total destinations with paths: %d\n", $_dest_count
|
||||
printf "Total paths: %d\n", $_path_count
|
||||
end
|
||||
end
|
||||
document walk_bgp_table
|
||||
Walk through a BGP table and dump all bgp_path_info structures with their state.
|
||||
|
||||
This macro iterates through all route nodes in a BGP table, and for each
|
||||
destination that has path information, it dumps all the bgp_path_info
|
||||
structures in the linked list along with their key state information including:
|
||||
- Peer information
|
||||
- Route type and sub-type
|
||||
- Flags (SELECTED, VALID, MULTIPATH, etc.)
|
||||
- Attributes (nexthop, etc.)
|
||||
- Extra information (EVPN, labels, vrfleak)
|
||||
|
||||
Arguments:
|
||||
(struct bgp_table *) pointer to the BGP table to walk
|
||||
|
||||
Example usage:
|
||||
(gdb) p bgp->rib[0][0]
|
||||
(gdb) walk_bgp_table bgp->rib[0][0]
|
||||
end
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue