xref: /freebsd/sys/net/route/route_ddb.c (revision 580744621f33383027108364dcadad718df46ffe)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright 2019 Conrad Meyer <cem@FreeBSD.org>
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30 #include "opt_inet.h"
31 #include "opt_inet6.h"
32 
33 #include <sys/ctype.h>
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/malloc.h>
37 #include <sys/mbuf.h>
38 #include <sys/socket.h>
39 #include <sys/sysctl.h>
40 #include <sys/syslog.h>
41 #include <sys/kernel.h>
42 #include <sys/lock.h>
43 #include <sys/rmlock.h>
44 
45 #include <ddb/ddb.h>
46 #include <ddb/db_lex.h>
47 
48 #include <net/if.h>
49 #include <net/if_var.h>
50 #include <net/if_dl.h>
51 #include <net/route.h>
52 #include <net/route/nhop.h>
53 #include <net/route/route_var.h>
54 
55 /*
56  * Unfortunately, RTF_ values are expressed as raw masks rather than powers of
57  * 2, so we cannot use them as nice C99 initializer indices below.
58  */
59 static const char * const rtf_flag_strings[] = {
60 	"UP",
61 	"GATEWAY",
62 	"HOST",
63 	"REJECT",
64 	"DYNAMIC",
65 	"MODIFIED",
66 	"DONE",
67 	"UNUSED_0x80",
68 	"UNUSED_0x100",
69 	"XRESOLVE",
70 	"LLDATA",
71 	"STATIC",
72 	"BLACKHOLE",
73 	"UNUSED_0x2000",
74 	"PROTO2",
75 	"PROTO1",
76 	"UNUSED_0x10000",
77 	"UNUSED_0x20000",
78 	"PROTO3",
79 	"FIXEDMTU",
80 	"PINNED",
81 	"LOCAL",
82 	"BROADCAST",
83 	"MULTICAST",
84 	/* Big gap. */
85 	[28] = "STICKY",
86 	[30] = "RNH_LOCKED",
87 	[31] = "GWFLAG_COMPAT",
88 };
89 
90 static const char * __pure
91 rt_flag_name(unsigned idx)
92 {
93 	if (idx >= nitems(rtf_flag_strings))
94 		return ("INVALID_FLAG");
95 	if (rtf_flag_strings[idx] == NULL)
96 		return ("UNKNOWN");
97 	return (rtf_flag_strings[idx]);
98 }
99 
100 static void
101 rt_dumpaddr_ddb(const char *name, const struct sockaddr *sa)
102 {
103 	char buf[INET6_ADDRSTRLEN], *res;
104 
105 	res = NULL;
106 	if (sa == NULL)
107 		res = "NULL";
108 	else if (sa->sa_family == AF_INET) {
109 		res = inet_ntop(AF_INET,
110 		    &((const struct sockaddr_in *)sa)->sin_addr,
111 		    buf, sizeof(buf));
112 	} else if (sa->sa_family == AF_INET6) {
113 		res = inet_ntop(AF_INET6,
114 		    &((const struct sockaddr_in6 *)sa)->sin6_addr,
115 		    buf, sizeof(buf));
116 	} else if (sa->sa_family == AF_LINK) {
117 		res = "on link";
118 	}
119 
120 	if (res != NULL) {
121 		db_printf("%s <%s> ", name, res);
122 		return;
123 	}
124 
125 	db_printf("%s <af:%d> ", name, sa->sa_family);
126 }
127 
128 static int
129 rt_dumpentry_ddb(struct radix_node *rn, void *arg __unused)
130 {
131 	struct sockaddr_storage ss;
132 	struct rtentry *rt;
133 	struct nhop_object *nh;
134 	int flags, idx;
135 
136 	/* If RNTORT is important, put it in a header. */
137 	rt = (void *)rn;
138 	nh = (struct nhop_object *)rt->rt_nhop;
139 
140 	rt_dumpaddr_ddb("dst", rt_key(rt));
141 	rt_dumpaddr_ddb("gateway", &rt->rt_nhop->gw_sa);
142 	rt_dumpaddr_ddb("netmask", rtsock_fix_netmask(rt_key(rt), rt_mask(rt),
143 	    &ss));
144 	if ((nh->nh_ifp->if_flags & IFF_DYING) == 0) {
145 		rt_dumpaddr_ddb("ifp", nh->nh_ifp->if_addr->ifa_addr);
146 		rt_dumpaddr_ddb("ifa", nh->nh_ifa->ifa_addr);
147 	}
148 
149 	db_printf("flags ");
150 	flags = rt->rt_flags;
151 	if (flags == 0)
152 		db_printf("none");
153 
154 	while ((idx = ffs(flags)) > 0) {
155 		idx--;
156 
157 		if (flags != rt->rt_flags)
158 			db_printf(",");
159 		db_printf("%s", rt_flag_name(idx));
160 
161 		flags &= ~(1ul << idx);
162 	}
163 
164 	db_printf("\n");
165 	return (0);
166 }
167 
168 DB_SHOW_COMMAND(routetable, db_show_routetable_cmd)
169 {
170 	struct rib_head *rnh;
171 	int error, i, lim;
172 
173 	if (have_addr)
174 		i = lim = addr;
175 	else {
176 		i = 1;
177 		lim = AF_MAX;
178 	}
179 
180 	for (; i <= lim; i++) {
181 		rnh = rt_tables_get_rnh(0, i);
182 		if (rnh == NULL) {
183 			if (have_addr) {
184 				db_printf("%s: AF %d not supported?\n",
185 				    __func__, i);
186 				break;
187 			}
188 			continue;
189 		}
190 
191 		if (!have_addr && i > 1)
192 			db_printf("\n");
193 
194 		db_printf("Route table for AF %d%s%s%s:\n", i,
195 		    (i == AF_INET || i == AF_INET6) ? " (" : "",
196 		    (i == AF_INET) ? "INET" : (i == AF_INET6) ? "INET6" : "",
197 		    (i == AF_INET || i == AF_INET6) ? ")" : "");
198 
199 		error = rnh->rnh_walktree(&rnh->head, rt_dumpentry_ddb, NULL);
200 		if (error != 0)
201 			db_printf("%s: walktree(%d): %d\n", __func__, i,
202 			    error);
203 	}
204 }
205 
206 _DB_FUNC(_show, route, db_show_route_cmd, db_show_table, CS_OWN, NULL)
207 {
208 	char abuf[INET6_ADDRSTRLEN], *buf, *end;
209 	struct rib_head *rh;
210 	struct radix_node *rn;
211 	void *dst_addrp;
212 	struct rtentry *rt;
213 	union {
214 		struct sockaddr_in dest_sin;
215 		struct sockaddr_in6 dest_sin6;
216 	} u;
217 	int af;
218 
219 	buf = db_get_line();
220 
221 	/* Remove whitespaces from both ends */
222 	end = buf + strlen(buf) - 1;
223 	for (; (end >= buf) && (*end=='\n' || isspace(*end)); end--)
224 		*end = '\0';
225 	while (isspace(*buf))
226 		buf++;
227 
228 	/* Determine AF */
229 	if (strchr(buf, ':') != NULL) {
230 		af = AF_INET6;
231 		u.dest_sin6.sin6_family = af;
232 		u.dest_sin6.sin6_len = sizeof(struct sockaddr_in6);
233 		dst_addrp = &u.dest_sin6.sin6_addr;
234 	} else {
235 		af = AF_INET;
236 		u.dest_sin.sin_family = af;
237 		u.dest_sin.sin_len = sizeof(struct sockaddr_in);
238 		dst_addrp = &u.dest_sin.sin_addr;
239 	}
240 
241 	if (inet_pton(af, buf, dst_addrp) != 1)
242 		goto usage;
243 
244 	if (inet_ntop(af, dst_addrp, abuf, sizeof(abuf)) != NULL)
245 		db_printf("Looking up route to destination '%s'\n", abuf);
246 
247 	rt = NULL;
248 	CURVNET_SET(vnet0);
249 
250 	rh = rt_tables_get_rnh(RT_DEFAULT_FIB, af);
251 
252 	rn = rh->rnh_matchaddr(&u, &rh->head);
253 	if (rn && ((rn->rn_flags & RNF_ROOT) == 0))
254 		rt = (struct rtentry *)rn;
255 
256 	CURVNET_RESTORE();
257 
258 	if (rt == NULL) {
259 		db_printf("Could not get route for that server.\n");
260 		return;
261 	}
262 
263 	rt_dumpentry_ddb((void *)rt, NULL);
264 
265 	return;
266 usage:
267 	db_printf("Usage: 'show route <address>'\n"
268 	    "  Currently accepts only IPv4 and IPv6 addresses\n");
269 	db_skip_to_eol();
270 }
271 
272