1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
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 #include "opt_inet.h"
30 #include "opt_inet6.h"
31
32 #include <sys/ctype.h>
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/malloc.h>
36 #include <sys/mbuf.h>
37 #include <sys/socket.h>
38 #include <sys/sysctl.h>
39 #include <sys/syslog.h>
40 #include <sys/kernel.h>
41 #include <sys/lock.h>
42 #include <sys/rmlock.h>
43
44 #include <ddb/ddb.h>
45 #include <ddb/db_lex.h>
46
47 #include <net/if.h>
48 #include <net/if_var.h>
49 #include <net/if_private.h>
50 #include <net/if_dl.h>
51 #include <net/route.h>
52 #include <net/route/nhop.h>
53 #include <net/route/route_ctl.h>
54 #include <net/route/route_var.h>
55
56 /*
57 * Unfortunately, RTF_ values are expressed as raw masks rather than powers of
58 * 2, so we cannot use them as nice C99 initializer indices below.
59 */
60 static const char * const rtf_flag_strings[] = {
61 "UP",
62 "GATEWAY",
63 "HOST",
64 "REJECT",
65 "DYNAMIC",
66 "MODIFIED",
67 "DONE",
68 "UNUSED_0x80",
69 "UNUSED_0x100",
70 "XRESOLVE",
71 "LLDATA",
72 "STATIC",
73 "BLACKHOLE",
74 "UNUSED_0x2000",
75 "PROTO2",
76 "PROTO1",
77 "UNUSED_0x10000",
78 "UNUSED_0x20000",
79 "PROTO3",
80 "FIXEDMTU",
81 "PINNED",
82 "LOCAL",
83 "BROADCAST",
84 "MULTICAST",
85 /* Big gap. */
86 [28] = "STICKY",
87 [30] = "RNH_LOCKED",
88 [31] = "GWFLAG_COMPAT",
89 };
90
91 static const char * __pure
rt_flag_name(unsigned idx)92 rt_flag_name(unsigned idx)
93 {
94 if (idx >= nitems(rtf_flag_strings))
95 return ("INVALID_FLAG");
96 if (rtf_flag_strings[idx] == NULL)
97 return ("UNKNOWN");
98 return (rtf_flag_strings[idx]);
99 }
100
101 static void
rt_dumpaddr_ddb(const char * name,const struct sockaddr * sa)102 rt_dumpaddr_ddb(const char *name, const struct sockaddr *sa)
103 {
104 char buf[INET6_ADDRSTRLEN], *res;
105
106 res = NULL;
107 if (sa == NULL)
108 res = "NULL";
109 else if (sa->sa_family == AF_INET) {
110 res = inet_ntop(AF_INET,
111 &((const struct sockaddr_in *)sa)->sin_addr,
112 buf, sizeof(buf));
113 } else if (sa->sa_family == AF_INET6) {
114 res = inet_ntop(AF_INET6,
115 &((const struct sockaddr_in6 *)sa)->sin6_addr,
116 buf, sizeof(buf));
117 } else if (sa->sa_family == AF_LINK) {
118 res = "on link";
119 }
120
121 if (res != NULL) {
122 db_printf("%s <%s> ", name, res);
123 return;
124 }
125
126 db_printf("%s <af:%d> ", name, sa->sa_family);
127 }
128
129 static int
rt_dumpentry_ddb(struct radix_node * rn,void * arg __unused)130 rt_dumpentry_ddb(struct radix_node *rn, void *arg __unused)
131 {
132 struct sockaddr_storage ss;
133 struct rtentry *rt;
134 struct nhop_object *nh;
135 int flags, idx;
136
137 /* If RNTORT is important, put it in a header. */
138 rt = (void *)rn;
139 nh = (struct nhop_object *)rt->rt_nhop;
140
141 rt_dumpaddr_ddb("dst", rt_key(rt));
142 rt_dumpaddr_ddb("gateway", &rt->rt_nhop->gw_sa);
143 rt_dumpaddr_ddb("netmask", rtsock_fix_netmask(rt_key(rt), rt_mask(rt),
144 &ss));
145 if ((nh->nh_ifp->if_flags & IFF_DYING) == 0) {
146 rt_dumpaddr_ddb("ifp", nh->nh_ifp->if_addr->ifa_addr);
147 rt_dumpaddr_ddb("ifa", nh->nh_ifa->ifa_addr);
148 }
149
150 db_printf("flags ");
151 flags = rt->rte_flags | nhop_get_rtflags(nh);
152 if (flags == 0)
153 db_printf("none");
154
155 while ((idx = ffs(flags)) > 0) {
156 idx--;
157
158 db_printf("%s", rt_flag_name(idx));
159 flags &= ~(1ul << idx);
160 if (flags != 0)
161 db_printf(",");
162 }
163
164 db_printf("\n");
165 return (0);
166 }
167
DB_SHOW_COMMAND(routetable,db_show_routetable)168 DB_SHOW_COMMAND(routetable, db_show_routetable)
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
DB_SHOW_COMMAND_FLAGS(route,db_show_route,CS_OWN)206 DB_SHOW_COMMAND_FLAGS(route, db_show_route, CS_OWN)
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