Move route-specific ddb commands to route/route_ddb.c

Currently functionality resides in rtsock.c, which is a controlling
 interface, partially external to the routing subsystem.
Additionally, DDB-supporting functionality is > 100SLOC, which deserves
 a separate file.

Given that, move this functionality to a newly-created net/route/ subdir.

Reviewed by:	cem
Differential Revision:	https://reviews.freebsd.org/D24561
This commit is contained in:
melifaro 2020-04-28 20:00:17 +00:00
parent 7c1be85b66
commit ba5c497845
4 changed files with 466 additions and 413 deletions

View File

@ -4095,6 +4095,7 @@ net/route/nhop.c standard
net/route/nhop_ctl.c standard
net/route/nhop_utils.c standard
net/route/route_ctl.c standard
net/route/route_ddb.c optional ddb
net/route/route_helpers.c standard
net/route/route_temporal.c standard
net/rss_config.c optional inet rss | inet6 rss

View File

@ -388,6 +388,8 @@ int rtsock_addrmsg(int, struct ifaddr *, int);
int rtsock_routemsg(int, struct rtentry *, struct ifnet *ifp, int, int);
int rtsock_routemsg_info(int, struct rt_addrinfo *, int);
struct sockaddr *rtsock_fix_netmask(const struct sockaddr *dst,
const struct sockaddr *smask, struct sockaddr_storage *dmask);
/*
* Note the following locking behavior:
*

458
sys/net/route/route_ddb.c Normal file
View File

@ -0,0 +1,458 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright 2019 Conrad Meyer <cem@FreeBSD.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include "opt_inet.h"
#include "opt_inet6.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <sys/syslog.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/rmlock.h>
#include <ddb/ddb.h>
#include <ddb/db_lex.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/if_dl.h>
#include <net/vnet.h>
#include <net/route.h>
#include <net/route/route_var.h>
#include <net/route/nhop.h>
#include <netinet/in.h>
/*
* 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_nhop->gw_sa);
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("%s", 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",
(long)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);
/* UNREACHABLE */
/* Appease Clang false positive: */
dstp = NULL;
}
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();
}

View File

@ -54,11 +54,6 @@
#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>
@ -68,6 +63,9 @@
#include <net/raw_cb.h>
#include <net/route.h>
#include <net/route/route_var.h>
#ifdef RADIX_MPATH
#include <net/radix_mpath.h>
#endif
#include <net/vnet.h>
#include <netinet/in.h>
@ -182,8 +180,6 @@ static int sysctl_ifmalist(int af, struct walkarg *w);
static int route_output(struct mbuf *m, struct socket *so, ...);
static void rt_getmetrics(const struct rtentry *rt, struct rt_metrics *out);
static void rt_dispatch(struct mbuf *, sa_family_t);
static struct sockaddr *rtsock_fix_netmask(struct sockaddr *dst,
struct sockaddr *smask, struct sockaddr_storage *dmask);
static int handle_rtm_get(struct rt_addrinfo *info, u_int fibnum,
struct rt_msghdr *rtm, struct rtentry **ret_nrt);
static int update_rtm_from_rte(struct rt_addrinfo *info,
@ -1136,8 +1132,8 @@ rt_xaddrs(caddr_t cp, caddr_t cplim, struct rt_addrinfo *rtinfo)
* Fill in @dmask with valid netmask leaving original @smask
* intact. Mostly used with radix netmasks.
*/
static struct sockaddr *
rtsock_fix_netmask(struct sockaddr *dst, struct sockaddr *smask,
struct sockaddr *
rtsock_fix_netmask(const struct sockaddr *dst, const struct sockaddr *smask,
struct sockaddr_storage *dmask)
{
if (dst == NULL || smask == NULL)
@ -2164,407 +2160,3 @@ 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_nhop->gw_sa);
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("%s", 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",
(long)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);
/* UNREACHABLE */
/* Appease Clang false positive: */
dstp = NULL;
}
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