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_dl.h>
50 #include <net/route.h>
51 #include <net/route/nhop.h>
52 #include <net/route/route_ctl.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
rt_flag_name(unsigned idx)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
rt_dumpaddr_ddb(const char * name,const struct sockaddr * sa)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
rt_dumpentry_ddb(struct radix_node * rn,void * arg __unused)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->rte_flags | nhop_get_rtflags(nh);
151 if (flags == 0)
152 db_printf("none");
153
154 while ((idx = ffs(flags)) > 0) {
155 idx--;
156
157 db_printf("%s", rt_flag_name(idx));
158 flags &= ~(1ul << idx);
159 if (flags != 0)
160 db_printf(",");
161 }
162
163 db_printf("\n");
164 return (0);
165 }
166
DB_SHOW_COMMAND(routetable,db_show_routetable_cmd)167 DB_SHOW_COMMAND(routetable, db_show_routetable_cmd)
168 {
169 struct rib_head *rnh;
170 int error, i, lim;
171
172 if (have_addr)
173 i = lim = addr;
174 else {
175 i = 1;
176 lim = AF_MAX;
177 }
178
179 for (; i <= lim; i++) {
180 rnh = rt_tables_get_rnh(0, i);
181 if (rnh == NULL) {
182 if (have_addr) {
183 db_printf("%s: AF %d not supported?\n",
184 __func__, i);
185 break;
186 }
187 continue;
188 }
189
190 if (!have_addr && i > 1)
191 db_printf("\n");
192
193 db_printf("Route table for AF %d%s%s%s:\n", i,
194 (i == AF_INET || i == AF_INET6) ? " (" : "",
195 (i == AF_INET) ? "INET" : (i == AF_INET6) ? "INET6" : "",
196 (i == AF_INET || i == AF_INET6) ? ")" : "");
197
198 error = rnh->rnh_walktree(&rnh->head, rt_dumpentry_ddb, NULL);
199 if (error != 0)
200 db_printf("%s: walktree(%d): %d\n", __func__, i,
201 error);
202 }
203 }
204
DB_SHOW_COMMAND_FLAGS(route,db_show_route_cmd,CS_OWN)205 DB_SHOW_COMMAND_FLAGS(route, db_show_route_cmd, CS_OWN)
206 {
207 char abuf[INET6_ADDRSTRLEN], *buf, *end;
208 struct rib_head *rh;
209 struct radix_node *rn;
210 void *dst_addrp;
211 struct rtentry *rt;
212 union {
213 struct sockaddr_in dest_sin;
214 struct sockaddr_in6 dest_sin6;
215 } u;
216 int af;
217
218 buf = db_get_line();
219
220 /* Remove whitespaces from both ends */
221 end = buf + strlen(buf) - 1;
222 for (; (end >= buf) && (*end=='\n' || isspace(*end)); end--)
223 *end = '\0';
224 while (isspace(*buf))
225 buf++;
226
227 /* Determine AF */
228 if (strchr(buf, ':') != NULL) {
229 af = AF_INET6;
230 u.dest_sin6.sin6_family = af;
231 u.dest_sin6.sin6_len = sizeof(struct sockaddr_in6);
232 dst_addrp = &u.dest_sin6.sin6_addr;
233 } else {
234 af = AF_INET;
235 u.dest_sin.sin_family = af;
236 u.dest_sin.sin_len = sizeof(struct sockaddr_in);
237 dst_addrp = &u.dest_sin.sin_addr;
238 }
239
240 if (inet_pton(af, buf, dst_addrp) != 1)
241 goto usage;
242
243 if (inet_ntop(af, dst_addrp, abuf, sizeof(abuf)) != NULL)
244 db_printf("Looking up route to destination '%s'\n", abuf);
245
246 rt = NULL;
247 CURVNET_SET(vnet0);
248
249 rh = rt_tables_get_rnh(RT_DEFAULT_FIB, af);
250
251 rn = rh->rnh_matchaddr(&u, &rh->head);
252 if (rn && ((rn->rn_flags & RNF_ROOT) == 0))
253 rt = (struct rtentry *)rn;
254
255 CURVNET_RESTORE();
256
257 if (rt == NULL) {
258 db_printf("Could not get route for that server.\n");
259 return;
260 }
261
262 rt_dumpentry_ddb((void *)rt, NULL);
263
264 return;
265 usage:
266 db_printf("Usage: 'show route <address>'\n"
267 " Currently accepts only IPv4 and IPv6 addresses\n");
268 db_skip_to_eol();
269 }
270