mirror of
https://git.FreeBSD.org/src.git
synced 2024-12-18 10:35:55 +00:00
ddb(4): Add 'show route <dest>' and 'show routetable [<af>]'
These commands show the route resolved for a specified destination, or print out the entire routing table for a given address family (or all families, if none is explicitly provided). Discussed with: emaste Differential Revision: https://reviews.freebsd.org/D21510
This commit is contained in:
parent
576649b30a
commit
8b6acd2b51
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=352112
@ -60,7 +60,7 @@
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd November 30, 2018
|
||||
.Dd September 9, 2019
|
||||
.Dt DDB 4
|
||||
.Os
|
||||
.Sh NAME
|
||||
@ -955,6 +955,20 @@ Addresses of particular pointers can be gathered with "show allrman"
|
||||
command.
|
||||
.\"
|
||||
.Pp
|
||||
.It Ic show Cm route Ar addr
|
||||
Show route table result for destination
|
||||
.Ar addr .
|
||||
At this time, INET and INET6 formatted addresses are supported.
|
||||
.\"
|
||||
.Pp
|
||||
.It Ic show Cm routetable Oo Ar af Oc
|
||||
Show full route table or tables.
|
||||
If
|
||||
.Ar af
|
||||
is specified, show only routes for the given numeric address family.
|
||||
If no argument is specified, dump the route table for all address families.
|
||||
.\"
|
||||
.Pp
|
||||
.It Ic show Cm rtc
|
||||
Show real time clock value.
|
||||
Useful for long debugging sessions.
|
||||
|
407
sys/net/rtsock.c
407
sys/net/rtsock.c
@ -31,6 +31,7 @@
|
||||
* @(#)rtsock.c 8.7 (Berkeley) 10/12/95
|
||||
* $FreeBSD$
|
||||
*/
|
||||
#include "opt_ddb.h"
|
||||
#include "opt_mpath.h"
|
||||
#include "opt_inet.h"
|
||||
#include "opt_inet6.h"
|
||||
@ -53,6 +54,11 @@
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/systm.h>
|
||||
|
||||
#ifdef DDB
|
||||
#include <ddb/ddb.h>
|
||||
#include <ddb/db_lex.h>
|
||||
#endif
|
||||
|
||||
#include <net/if.h>
|
||||
#include <net/if_var.h>
|
||||
#include <net/if_dl.h>
|
||||
@ -1995,3 +2001,404 @@ static struct domain routedomain = {
|
||||
};
|
||||
|
||||
VNET_DOMAIN_SET(route);
|
||||
|
||||
#ifdef DDB
|
||||
/*
|
||||
* Unfortunately, RTF_ values are expressed as raw masks rather than powers of
|
||||
* 2, so we cannot use them as nice C99 initializer indices below.
|
||||
*/
|
||||
static const char * const rtf_flag_strings[] = {
|
||||
"UP",
|
||||
"GATEWAY",
|
||||
"HOST",
|
||||
"REJECT",
|
||||
"DYNAMIC",
|
||||
"MODIFIED",
|
||||
"DONE",
|
||||
"UNUSED_0x80",
|
||||
"UNUSED_0x100",
|
||||
"XRESOLVE",
|
||||
"LLDATA",
|
||||
"STATIC",
|
||||
"BLACKHOLE",
|
||||
"UNUSED_0x2000",
|
||||
"PROTO2",
|
||||
"PROTO1",
|
||||
"UNUSED_0x10000",
|
||||
"UNUSED_0x20000",
|
||||
"PROTO3",
|
||||
"FIXEDMTU",
|
||||
"PINNED",
|
||||
"LOCAL",
|
||||
"BROADCAST",
|
||||
"MULTICAST",
|
||||
/* Big gap. */
|
||||
[28] = "STICKY",
|
||||
[30] = "RNH_LOCKED",
|
||||
[31] = "GWFLAG_COMPAT",
|
||||
};
|
||||
|
||||
static const char * __pure
|
||||
rt_flag_name(unsigned idx)
|
||||
{
|
||||
if (idx >= nitems(rtf_flag_strings))
|
||||
return ("INVALID_FLAG");
|
||||
if (rtf_flag_strings[idx] == NULL)
|
||||
return ("UNKNOWN");
|
||||
return (rtf_flag_strings[idx]);
|
||||
}
|
||||
|
||||
static void
|
||||
rt_dumpaddr_ddb(const char *name, const struct sockaddr *sa)
|
||||
{
|
||||
char buf[INET6_ADDRSTRLEN], *res;
|
||||
|
||||
res = NULL;
|
||||
if (sa == NULL)
|
||||
res = "NULL";
|
||||
else if (sa->sa_family == AF_INET) {
|
||||
res = inet_ntop(AF_INET,
|
||||
&((const struct sockaddr_in *)sa)->sin_addr,
|
||||
buf, sizeof(buf));
|
||||
} else if (sa->sa_family == AF_INET6) {
|
||||
res = inet_ntop(AF_INET6,
|
||||
&((const struct sockaddr_in6 *)sa)->sin6_addr,
|
||||
buf, sizeof(buf));
|
||||
} else if (sa->sa_family == AF_LINK) {
|
||||
res = "on link";
|
||||
}
|
||||
|
||||
if (res != NULL) {
|
||||
db_printf("%s <%s> ", name, res);
|
||||
return;
|
||||
}
|
||||
|
||||
db_printf("%s <af:%d> ", name, sa->sa_family);
|
||||
}
|
||||
|
||||
static int
|
||||
rt_dumpentry_ddb(struct radix_node *rn, void *arg __unused)
|
||||
{
|
||||
struct sockaddr_storage ss;
|
||||
struct rtentry *rt;
|
||||
int flags, idx;
|
||||
|
||||
/* If RNTORT is important, put it in a header. */
|
||||
rt = (void *)rn;
|
||||
|
||||
rt_dumpaddr_ddb("dst", rt_key(rt));
|
||||
rt_dumpaddr_ddb("gateway", rt->rt_gateway);
|
||||
rt_dumpaddr_ddb("netmask", rtsock_fix_netmask(rt_key(rt), rt_mask(rt),
|
||||
&ss));
|
||||
if (rt->rt_ifp != NULL && (rt->rt_ifp->if_flags & IFF_DYING) == 0) {
|
||||
rt_dumpaddr_ddb("ifp", rt->rt_ifp->if_addr->ifa_addr);
|
||||
rt_dumpaddr_ddb("ifa", rt->rt_ifa->ifa_addr);
|
||||
}
|
||||
|
||||
db_printf("flags ");
|
||||
flags = rt->rt_flags;
|
||||
if (flags == 0)
|
||||
db_printf("none");
|
||||
|
||||
while ((idx = ffs(flags)) > 0) {
|
||||
idx--;
|
||||
|
||||
if (flags != rt->rt_flags)
|
||||
db_printf(",");
|
||||
db_printf(rt_flag_name(idx));
|
||||
|
||||
flags &= ~(1ul << idx);
|
||||
}
|
||||
|
||||
db_printf("\n");
|
||||
return (0);
|
||||
}
|
||||
|
||||
DB_SHOW_COMMAND(routetable, db_show_routetable_cmd)
|
||||
{
|
||||
struct rib_head *rnh;
|
||||
int error, i, lim;
|
||||
|
||||
if (have_addr)
|
||||
i = lim = addr;
|
||||
else {
|
||||
i = 1;
|
||||
lim = AF_MAX;
|
||||
}
|
||||
|
||||
for (; i <= lim; i++) {
|
||||
rnh = rt_tables_get_rnh(0, i);
|
||||
if (rnh == NULL) {
|
||||
if (have_addr) {
|
||||
db_printf("%s: AF %d not supported?\n",
|
||||
__func__, i);
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!have_addr && i > 1)
|
||||
db_printf("\n");
|
||||
|
||||
db_printf("Route table for AF %d%s%s%s:\n", i,
|
||||
(i == AF_INET || i == AF_INET6) ? " (" : "",
|
||||
(i == AF_INET) ? "INET" : (i == AF_INET6) ? "INET6" : "",
|
||||
(i == AF_INET || i == AF_INET6) ? ")" : "");
|
||||
|
||||
error = rnh->rnh_walktree(&rnh->head, rt_dumpentry_ddb, NULL);
|
||||
if (error != 0)
|
||||
db_printf("%s: walktree(%d): %d\n", __func__, i,
|
||||
error);
|
||||
}
|
||||
}
|
||||
|
||||
_DB_FUNC(_show, route, db_show_route_cmd, db_show_table, CS_OWN, NULL)
|
||||
{
|
||||
char buf[INET6_ADDRSTRLEN], *bp;
|
||||
const void *dst_addrp;
|
||||
struct sockaddr *dstp;
|
||||
struct rtentry *rt;
|
||||
union {
|
||||
struct sockaddr_in dest_sin;
|
||||
struct sockaddr_in6 dest_sin6;
|
||||
} u;
|
||||
uint16_t hextets[8];
|
||||
unsigned i, tets;
|
||||
int t, af, exp, tokflags;
|
||||
|
||||
/*
|
||||
* Undecoded address family. No double-colon expansion seen yet.
|
||||
*/
|
||||
af = -1;
|
||||
exp = -1;
|
||||
/* Assume INET6 to start; we can work back if guess was wrong. */
|
||||
tokflags = DRT_WSPACE | DRT_HEX | DRT_HEXADECIMAL;
|
||||
|
||||
/*
|
||||
* db_command has lexed 'show route' for us.
|
||||
*/
|
||||
t = db_read_token_flags(tokflags);
|
||||
if (t == tWSPACE)
|
||||
t = db_read_token_flags(tokflags);
|
||||
|
||||
/*
|
||||
* tEOL: Just 'show route' isn't a valid mode.
|
||||
* tMINUS: It's either '-h' or some invalid option. Regardless, usage.
|
||||
*/
|
||||
if (t == tEOL || t == tMINUS)
|
||||
goto usage;
|
||||
|
||||
db_unread_token(t);
|
||||
|
||||
tets = nitems(hextets);
|
||||
|
||||
/*
|
||||
* Each loop iteration, we expect to read one octet (v4) or hextet
|
||||
* (v6), followed by an appropriate field separator ('.' or ':' or
|
||||
* '::').
|
||||
*
|
||||
* At the start of each loop, we're looking for a number (octet or
|
||||
* hextet).
|
||||
*
|
||||
* INET6 addresses have a special case where they may begin with '::'.
|
||||
*/
|
||||
for (i = 0; i < tets; i++) {
|
||||
t = db_read_token_flags(tokflags);
|
||||
|
||||
if (t == tCOLONCOLON) {
|
||||
/* INET6 with leading '::' or invalid. */
|
||||
if (i != 0) {
|
||||
db_printf("Parse error: unexpected extra "
|
||||
"colons.\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
af = AF_INET6;
|
||||
exp = i;
|
||||
hextets[i] = 0;
|
||||
continue;
|
||||
} else if (t == tNUMBER) {
|
||||
/*
|
||||
* Lexer separates out '-' as tMINUS, but make the
|
||||
* assumption explicit here.
|
||||
*/
|
||||
MPASS(db_tok_number >= 0);
|
||||
|
||||
if (af == AF_INET && db_tok_number > UINT8_MAX) {
|
||||
db_printf("Not a valid v4 octet: %ld\n",
|
||||
db_tok_number);
|
||||
goto exit;
|
||||
}
|
||||
hextets[i] = db_tok_number;
|
||||
} else if (t == tEOL) {
|
||||
/*
|
||||
* We can only detect the end of an IPv6 address in
|
||||
* compact representation with EOL.
|
||||
*/
|
||||
if (af != AF_INET6 || exp < 0) {
|
||||
db_printf("Parse failed. Got unexpected EOF "
|
||||
"when the address is not a compact-"
|
||||
"representation IPv6 address.\n");
|
||||
goto exit;
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
db_printf("Parse failed. Unexpected token %d.\n", t);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Next, look for a separator, if appropriate. */
|
||||
if (i == tets - 1)
|
||||
continue;
|
||||
|
||||
t = db_read_token_flags(tokflags);
|
||||
if (af < 0) {
|
||||
if (t == tCOLON) {
|
||||
af = AF_INET6;
|
||||
continue;
|
||||
}
|
||||
if (t == tCOLONCOLON) {
|
||||
af = AF_INET6;
|
||||
i++;
|
||||
hextets[i] = 0;
|
||||
exp = i;
|
||||
continue;
|
||||
}
|
||||
if (t == tDOT) {
|
||||
unsigned hn, dn;
|
||||
|
||||
af = AF_INET;
|
||||
/* Need to fixup the first parsed number. */
|
||||
if (hextets[0] > 0x255 ||
|
||||
(hextets[0] & 0xf0) > 0x90 ||
|
||||
(hextets[0] & 0xf) > 9) {
|
||||
db_printf("Not a valid v4 octet: %x\n",
|
||||
hextets[0]);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
hn = hextets[0];
|
||||
dn = (hn >> 8) * 100 +
|
||||
((hn >> 4) & 0xf) * 10 +
|
||||
(hn & 0xf);
|
||||
|
||||
hextets[0] = dn;
|
||||
|
||||
/* Switch to decimal for remaining octets. */
|
||||
tokflags &= ~DRT_RADIX_MASK;
|
||||
tokflags |= DRT_DECIMAL;
|
||||
|
||||
tets = 4;
|
||||
continue;
|
||||
}
|
||||
|
||||
db_printf("Parse error. Unexpected token %d.\n", t);
|
||||
goto exit;
|
||||
} else if (af == AF_INET) {
|
||||
if (t == tDOT)
|
||||
continue;
|
||||
db_printf("Expected '.' (%d) between octets but got "
|
||||
"(%d).\n", tDOT, t);
|
||||
goto exit;
|
||||
|
||||
} else if (af == AF_INET6) {
|
||||
if (t == tCOLON)
|
||||
continue;
|
||||
if (t == tCOLONCOLON) {
|
||||
if (exp < 0) {
|
||||
i++;
|
||||
hextets[i] = 0;
|
||||
exp = i;
|
||||
continue;
|
||||
}
|
||||
db_printf("Got bogus second '::' in v6 "
|
||||
"address.\n");
|
||||
goto exit;
|
||||
}
|
||||
if (t == tEOL) {
|
||||
/*
|
||||
* Handle in the earlier part of the loop
|
||||
* because we need to handle trailing :: too.
|
||||
*/
|
||||
db_unread_token(t);
|
||||
continue;
|
||||
}
|
||||
|
||||
db_printf("Expected ':' (%d) or '::' (%d) between "
|
||||
"hextets but got (%d).\n", tCOLON, tCOLONCOLON, t);
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check for trailing garbage. */
|
||||
if (i == tets) {
|
||||
t = db_read_token_flags(tokflags);
|
||||
if (t != tEOL) {
|
||||
db_printf("Got unexpected garbage after address "
|
||||
"(%d).\n", t);
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Need to expand compact INET6 addresses.
|
||||
*
|
||||
* Technically '::' for a single ':0:' is MUST NOT but just in case,
|
||||
* don't bother expanding that form (exp >= 0 && i == tets case).
|
||||
*/
|
||||
if (af == AF_INET6 && exp >= 0 && i < tets) {
|
||||
if (exp + 1 < i) {
|
||||
memmove(&hextets[exp + 1 + (nitems(hextets) - i)],
|
||||
&hextets[exp + 1],
|
||||
(i - (exp + 1)) * sizeof(hextets[0]));
|
||||
}
|
||||
memset(&hextets[exp + 1], 0, (nitems(hextets) - i) *
|
||||
sizeof(hextets[0]));
|
||||
}
|
||||
|
||||
memset(&u, 0, sizeof(u));
|
||||
if (af == AF_INET) {
|
||||
u.dest_sin.sin_family = AF_INET;
|
||||
u.dest_sin.sin_len = sizeof(u.dest_sin);
|
||||
u.dest_sin.sin_addr.s_addr = htonl(
|
||||
((uint32_t)hextets[0] << 24) |
|
||||
((uint32_t)hextets[1] << 16) |
|
||||
((uint32_t)hextets[2] << 8) |
|
||||
(uint32_t)hextets[3]);
|
||||
dstp = (void *)&u.dest_sin;
|
||||
dst_addrp = &u.dest_sin.sin_addr;
|
||||
} else if (af == AF_INET6) {
|
||||
u.dest_sin6.sin6_family = AF_INET6;
|
||||
u.dest_sin6.sin6_len = sizeof(u.dest_sin6);
|
||||
for (i = 0; i < nitems(hextets); i++)
|
||||
u.dest_sin6.sin6_addr.s6_addr16[i] = htons(hextets[i]);
|
||||
dstp = (void *)&u.dest_sin6;
|
||||
dst_addrp = &u.dest_sin6.sin6_addr;
|
||||
} else
|
||||
MPASS(false);
|
||||
|
||||
bp = inet_ntop(af, dst_addrp, buf, sizeof(buf));
|
||||
if (bp != NULL)
|
||||
db_printf("Looking up route to destination '%s'\n", bp);
|
||||
|
||||
CURVNET_SET(vnet0);
|
||||
rt = rtalloc1(dstp, 0, RTF_RNH_LOCKED);
|
||||
CURVNET_RESTORE();
|
||||
|
||||
if (rt == NULL) {
|
||||
db_printf("Could not get route for that server.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
rt_dumpentry_ddb((void *)rt, NULL);
|
||||
RTFREE_LOCKED(rt);
|
||||
|
||||
return;
|
||||
usage:
|
||||
db_printf("Usage: 'show route <address>'\n"
|
||||
" Currently accepts only dotted-decimal INET or colon-separated\n"
|
||||
" hextet INET6 addresses.\n");
|
||||
exit:
|
||||
db_skip_to_eol();
|
||||
}
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user