Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 9 Sep 2019 22:54:27 +0000 (UTC)
From:      Conrad Meyer <cem@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r352112 - in head: share/man/man4 sys/net
Message-ID:  <201909092254.x89MsRaX081398@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: cem
Date: Mon Sep  9 22:54:27 2019
New Revision: 352112
URL: https://svnweb.freebsd.org/changeset/base/352112

Log:
  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

Modified:
  head/share/man/man4/ddb.4
  head/sys/net/rtsock.c

Modified: head/share/man/man4/ddb.4
==============================================================================
--- head/share/man/man4/ddb.4	Mon Sep  9 22:08:22 2019	(r352111)
+++ head/share/man/man4/ddb.4	Mon Sep  9 22:54:27 2019	(r352112)
@@ -60,7 +60,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd November 30, 2018
+.Dd September 9, 2019
 .Dt DDB 4
 .Os
 .Sh NAME
@@ -953,6 +953,20 @@ at address
 .Ar addr .
 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

Modified: head/sys/net/rtsock.c
==============================================================================
--- head/sys/net/rtsock.c	Mon Sep  9 22:08:22 2019	(r352111)
+++ head/sys/net/rtsock.c	Mon Sep  9 22:54:27 2019	(r352112)
@@ -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



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201909092254.x89MsRaX081398>