1 /* route.c - Display/edit network routing table.
2 *
3 * Copyright 2012 Ranjan Kumar <ranjankumar.bth@gmail.com>
4 * Copyright 2013 Kyungwan Han <asura321@gmail.com>
5 * Copyright 2020 Eric Molitor <eric@molitor.org>
6 *
7 * No Standard
8 *
9 * TODO: autodetect -net -host target dev -A (but complain)
10 * route add -net target 10.0.0.0 netmask 255.0.0.0 dev eth0
11 * route del delete
12 * delete net route, must match netmask, informative error message
13 *
14 * mod dyn reinstate metric netmask gw mss window irtt dev
15
16 USE_ROUTE(NEWTOY(route, "?neA:", TOYFLAG_BIN))
17 config ROUTE
18 bool "route"
19 default n
20 help
21 usage: route [-ne] [-A [46]] [add|del TARGET [OPTIONS]]
22
23 Display, add or delete network routes in the "Forwarding Information Base".
24
25 -n Show numerical addresses (no DNS lookups)
26 -e display netstat fields
27
28 Routing means sending packets out a network interface to an address.
29 The kernel can tell where to send packets one hop away by examining each
30 interface's address and netmask, so the most common use of this command
31 is to identify a "gateway" that forwards other traffic.
32
33 Assigning an address to an interface automatically creates an appropriate
34 network route ("ifconfig eth0 10.0.2.15/8" does "route add 10.0.0.0/8 eth0"
35 for you), although some devices (such as loopback) won't show it in the
36 table. For machines more than one hop away, you need to specify a gateway
37 (ala "route add default gw 10.0.2.2").
38
39 The address "default" is a wildcard address (0.0.0.0/0) matching all
40 packets without a more specific route.
41
42 Available OPTIONS include:
43 reject - blocking route (force match failure)
44 dev NAME - force packets out this interface (ala "eth0")
45 netmask - old way of saying things like ADDR/24
46 gw ADDR - forward packets to gateway ADDR
47
48 */
49
50 #define FOR_route
51 #include "toys.h"
52 #include <net/route.h>
53
54 GLOBALS(
55 char *family;
56 )
57
58 #define DEFAULT_PREFIXLEN 128
59 #define INVALID_ADDR 0xffffffffUL
60 #define IPV6_ADDR_LEN 40 //32 + 7 (':') + 1 ('\0')
61
62 struct _arglist {
63 char *arg;
64
65 int action;
66 };
67
68 static struct _arglist arglist1[] = {
69 { "add", 1 }, { "del", 2 },
70 { "delete", 2 }, { NULL, 0 }
71 };
72
73 static struct _arglist arglist2[] = {
74 { "-net", 1 }, { "-host", 2 },
75 { NULL, 0 }
76 };
77
78 // to get the host name from the given ip.
get_hostname(char * ipstr,struct sockaddr_in * sockin)79 static int get_hostname(char *ipstr, struct sockaddr_in *sockin)
80 {
81 struct hostent *host;
82
83 sockin->sin_family = AF_INET;
84 sockin->sin_port = 0;
85
86 if (!strcmp(ipstr, "default")) {
87 sockin->sin_addr.s_addr = INADDR_ANY;
88 return 1;
89 }
90
91 if (inet_aton(ipstr, &sockin->sin_addr)) return 0;
92 if (!(host = gethostbyname(ipstr))) perror_exit("resolving '%s'", ipstr);
93 memcpy(&sockin->sin_addr, host->h_addr_list[0], sizeof(struct in_addr));
94
95 return 0;
96 }
97
98 // used to extract the address info from the given ip.
get_addrinfo(char * ip,struct sockaddr_in6 * sock_in6)99 static int get_addrinfo(char *ip, struct sockaddr_in6 *sock_in6)
100 {
101 struct addrinfo hints, *result;
102 int status = 0;
103
104 memset(&hints, 0, sizeof(struct addrinfo));
105 hints.ai_family = AF_INET6;
106 if ((status = getaddrinfo(ip, NULL, &hints, &result))) {
107 perror_msg("getaddrinfo: %s", gai_strerror(status));
108 return -1;
109 }
110 if (result) {
111 memcpy(sock_in6, result->ai_addr, sizeof(*sock_in6));
112 freeaddrinfo(result);
113 }
114 return 0;
115 }
116
get_flag_value(char * str,int flags)117 static void get_flag_value(char *str, int flags)
118 {
119 // RTF_* bits in order:
120 // UP, GATEWAY, HOST, REINSTATE, DYNAMIC, MODIFIED, DEFAULT, ADDRCONF, CACHE
121 int i = 0, mask = 0x105003f;
122
123 for (; mask; mask>>=1) if (mask&1) {
124 if (flags&(1<<i)) *str++ = "UGHRDMDAC"[i];
125 i++;
126 }
127 *str = 0;
128 }
129
130 // extract inet4 route info from /proc/net/route file and display it.
display_routes(void)131 static void display_routes(void)
132 {
133 unsigned long dest, gate, mask;
134 int flags, ref, use, metric, mss, win, irtt, items;
135 char iface[64] = {0,}, flag_val[10]; //there are 9 flags "UGHRDMDAC" for route.
136
137 FILE *fp = xfopen("/proc/net/route", "r");
138
139 xprintf("Kernel IP routing table\n"
140 "Destination Gateway Genmask Flags %s Iface\n",
141 (toys.optflags & FLAG_e)? " MSS Window irtt" : "Metric Ref Use");
142
143 if (fscanf(fp, "%*[^\n]\n") < 0) perror_exit("fscanf"); //skip 1st line
144 while ((items = fscanf(fp, "%63s%lx%lx%X%d%d%d%lx%d%d%d\n", iface, &dest,
145 &gate, &flags, &ref, &use, &metric, &mask, &mss, &win, &irtt)) == 11)
146 {
147 char *destip = toybuf, *gateip = toybuf+32, *maskip = toybuf+64; //ip string 16
148
149 if (!(flags & RTF_UP)) continue; //skip down interfaces.
150
151 if (!dest && !(toys.optflags & FLAG_n)) strcpy( destip, "default");
152 else if (!inet_ntop(AF_INET, &dest, destip, 32)) perror_exit("inet");
153
154 if (!gate && !(toys.optflags & FLAG_n)) strcpy( gateip, "*");
155 else if (!inet_ntop(AF_INET, &gate, gateip, 32)) perror_exit("inet");
156
157 if (!inet_ntop(AF_INET, &mask, maskip, 32)) perror_exit("inet");
158
159 //Get flag Values
160 get_flag_value(flag_val, flags);
161 if (flags & RTF_REJECT) flag_val[0] = '!';
162 xprintf("%-15.15s %-15.15s %-16s%-6s", destip, gateip, maskip, flag_val);
163 if (toys.optflags & FLAG_e) xprintf("%5d %-5d %6d %s\n", mss, win, irtt, iface);
164 else xprintf("%-6d %-2d %7d %s\n", metric, ref, use, iface);
165 }
166
167 if (items > 0 && feof(fp)) perror_exit("fscanf %d", items);
168 fclose(fp);
169 }
170
171 /*
172 * find the given parameter in list like add/del/net/host.
173 * and if match found return the appropriate action.
174 */
get_action(char *** argv,struct _arglist * list)175 static int get_action(char ***argv, struct _arglist *list)
176 {
177 struct _arglist *alist;
178
179 if (!**argv) return 0;
180 for (alist = list; alist->arg; alist++) { //find the given parameter in list
181 if (!strcmp(**argv, alist->arg)) {
182 *argv += 1;
183 return alist->action;
184 }
185 }
186 return 0;
187 }
188
189 /*
190 * used to get the params like: metric, netmask, gw, mss, window, irtt, dev and their values.
191 * additionally set the flag values for reject, mod, dyn and reinstate.
192 */
get_next_params(char ** argv,struct rtentry * rt,char ** netmask)193 static void get_next_params(char **argv, struct rtentry *rt, char **netmask)
194 {
195 for (;*argv;argv++) {
196 if (!strcmp(*argv, "reject")) rt->rt_flags |= RTF_REJECT;
197 else if (!strcmp(*argv, "mod")) rt->rt_flags |= RTF_MODIFIED;
198 else if (!strcmp(*argv, "dyn")) rt->rt_flags |= RTF_DYNAMIC;
199 else if (!strcmp(*argv, "reinstate")) rt->rt_flags |= RTF_REINSTATE;
200 else {
201 if (!argv[1]) help_exit(0);
202
203 //set the metric field in the routing table.
204 if (!strcmp(*argv, "metric"))
205 rt->rt_metric = atolx_range(argv[1], 0, ULONG_MAX) + 1;
206 else if (!strcmp(*argv, "netmask")) {
207 //when adding a network route, the netmask to be used.
208 struct sockaddr sock;
209 unsigned int addr_mask = (((struct sockaddr_in *)&((rt)->rt_genmask))->sin_addr.s_addr);
210
211 if (addr_mask) help_exit("dup netmask");
212 *netmask = argv[1];
213 get_hostname(*netmask, (struct sockaddr_in *) &sock);
214 rt->rt_genmask = sock;
215 } else if (!strcmp(*argv, "gw")) {
216 //route packets via a gateway.
217 if (!(rt->rt_flags & RTF_GATEWAY)) {
218 if (!get_hostname(argv[1], (struct sockaddr_in *) &rt->rt_gateway))
219 rt->rt_flags |= RTF_GATEWAY;
220 else perror_exit("gateway '%s' is a NETWORK", argv[1]);
221 } else help_exit("dup gw");
222 } else if (!strcmp(*argv, "mss")) {
223 //set the TCP Maximum Segment Size for connections over this route.
224 rt->rt_mtu = atolx_range(argv[1], 64, 65536);
225 rt->rt_flags |= RTF_MSS;
226 } else if (!strcmp(*argv, "window")) {
227 //set the TCP window size for connections over this route to W bytes.
228 rt->rt_window = atolx_range(argv[1], 128, INT_MAX); //win low
229 rt->rt_flags |= RTF_WINDOW;
230 } else if (!strcmp(*argv, "irtt")) {
231 rt->rt_irtt = atolx_range(argv[1], 0, INT_MAX);
232 rt->rt_flags |= RTF_IRTT;
233 } else if (!strcmp(*argv, "dev") && !rt->rt_dev) rt->rt_dev = argv[1];
234 else help_exit("no '%s'", *argv);
235 argv++;
236 }
237 }
238
239 if (!rt->rt_dev && (rt->rt_flags & RTF_REJECT)) rt->rt_dev = (char *)"lo";
240 }
241
242 // verify the netmask and conflict in netmask and route address.
verify_netmask(struct rtentry * rt,char * netmask)243 static void verify_netmask(struct rtentry *rt, char *netmask)
244 {
245 unsigned int addr_mask = (((struct sockaddr_in *)&((rt)->rt_genmask))->sin_addr.s_addr);
246 unsigned int router_addr = ~(unsigned int)(((struct sockaddr_in *)&((rt)->rt_dst))->sin_addr.s_addr);
247
248 if (addr_mask) {
249 addr_mask = ~ntohl(addr_mask);
250 if ((rt->rt_flags & RTF_HOST) && addr_mask != INVALID_ADDR)
251 perror_exit("conflicting netmask and host route");
252 if (addr_mask & (addr_mask + 1)) perror_exit("wrong netmask '%s'", netmask);
253 addr_mask = ((struct sockaddr_in *) &rt->rt_dst)->sin_addr.s_addr;
254 if (addr_mask & router_addr) perror_exit("conflicting netmask and route address");
255 }
256 }
257
258 // add/del a route.
setroute(char ** argv)259 static void setroute(char **argv)
260 {
261 struct rtentry rt;
262 char *netmask, *targetip;
263 int is_net_or_host = 0, sokfd, arg2_action;
264 int action = get_action(&argv, arglist1); //verify the arg for add/del.
265
266 if (!action || !*argv) help_exit("setroute");
267
268 arg2_action = get_action(&argv, arglist2); //verify the arg for -net or -host
269 if (!*argv) help_exit("setroute");
270
271 memset(&rt, 0, sizeof(struct rtentry));
272 targetip = *argv++;
273
274 netmask = strchr(targetip, '/');
275 if (netmask) {
276 *netmask++ = 0;
277 //used to verify the netmask and route conflict.
278 (((struct sockaddr_in *)&rt.rt_genmask)->sin_addr.s_addr)
279 = htonl((1<<(32-atolx_range(netmask, 0, 32)))-1);
280 rt.rt_genmask.sa_family = AF_INET;
281 netmask = 0;
282 } else netmask = "default";
283
284 is_net_or_host = get_hostname(targetip, (void *)&rt.rt_dst);
285
286 if (arg2_action) is_net_or_host = arg2_action & 1;
287 rt.rt_flags = ((is_net_or_host) ? RTF_UP : (RTF_UP | RTF_HOST));
288
289 get_next_params(argv, &rt, (char **)&netmask);
290 verify_netmask(&rt, (char *)netmask);
291
292 if ((action == 1) && (rt.rt_flags & RTF_HOST))
293 (((struct sockaddr_in *)&((rt).rt_genmask))->sin_addr.s_addr) = INVALID_ADDR;
294
295 sokfd = xsocket(AF_INET, SOCK_DGRAM, 0);
296 if (action == 1) xioctl(sokfd, SIOCADDRT, &rt);
297 else xioctl(sokfd, SIOCDELRT, &rt);
298 xclose(sokfd);
299 }
300
301 /*
302 * get prefix len (if any) and remove the prefix from target ip.
303 * if no prefix then set default prefix len.
304 */
is_prefix_inet6(char ** tip,struct in6_rtmsg * rt)305 static void is_prefix_inet6(char **tip, struct in6_rtmsg *rt)
306 {
307 unsigned long plen;
308 char *prefix = strchr(*tip, '/');
309
310 if (prefix) {
311 *prefix = '\0';
312 plen = atolx_range(prefix + 1, 0, 128); //DEFAULT_PREFIXLEN);
313 } else plen = DEFAULT_PREFIXLEN;
314
315 rt->rtmsg_flags = (plen == DEFAULT_PREFIXLEN) ? (RTF_UP | RTF_HOST) : RTF_UP;
316 rt->rtmsg_dst_len = plen;
317 }
318
319 /*
320 * used to get the params like: metric, gw, dev and their values.
321 * additionally set the flag values for mod and dyn.
322 */
get_next_params_inet6(char ** argv,struct sockaddr_in6 * sock_in6,struct in6_rtmsg * rt,char ** dev_name)323 static void get_next_params_inet6(char **argv, struct sockaddr_in6 *sock_in6, struct in6_rtmsg *rt, char **dev_name)
324 {
325 for (;*argv;argv++) {
326 if (!strcmp(*argv, "mod")) rt->rtmsg_flags |= RTF_MODIFIED;
327 else if (!strcmp(*argv, "dyn")) rt->rtmsg_flags |= RTF_DYNAMIC;
328 else {
329 if (!argv[1]) help_exit(0);
330
331 if (!strcmp(*argv, "metric"))
332 rt->rtmsg_metric = atolx_range(argv[1], 0, ULONG_MAX);
333 else if (!strcmp(*argv, "gw")) {
334 //route packets via a gateway.
335 if (!(rt->rtmsg_flags & RTF_GATEWAY)) {
336 if (!get_addrinfo(argv[1], (struct sockaddr_in6 *) &sock_in6)) {
337 memcpy(&rt->rtmsg_gateway, sock_in6->sin6_addr.s6_addr, sizeof(struct in6_addr));
338 rt->rtmsg_flags |= RTF_GATEWAY;
339 } else perror_exit("resolving '%s'", argv[1]);
340 } else help_exit(0);
341 } else if (!strcmp(*argv, "dev")) {
342 if (!*dev_name) *dev_name = argv[1];
343 } else help_exit(0);
344 argv++;
345 }
346 }
347 }
348
349 // add/del a route.
setroute_inet6(char ** argv)350 static void setroute_inet6(char **argv)
351 {
352 struct sockaddr_in6 sock_in6;
353 struct in6_rtmsg rt;
354 char *targetip, *dev_name = 0;
355 int sockfd, action = get_action(&argv, arglist1);
356
357 if (!action || !*argv) help_exit(0);
358 memset(&sock_in6, 0, sizeof(struct sockaddr_in6));
359 memset(&rt, 0, sizeof(struct in6_rtmsg));
360 targetip = *argv++;
361 if (!*argv) help_exit(0);
362
363 if (!strcmp(targetip, "default")) {
364 rt.rtmsg_flags = RTF_UP;
365 rt.rtmsg_dst_len = 0;
366 } else {
367 is_prefix_inet6((char **)&targetip, &rt);
368 if (get_addrinfo(targetip, (struct sockaddr_in6 *) &sock_in6))
369 perror_exit("resolving '%s'", targetip);
370 }
371 rt.rtmsg_metric = 1; //default metric.
372 memcpy(&rt.rtmsg_dst, sock_in6.sin6_addr.s6_addr, sizeof(struct in6_addr));
373 get_next_params_inet6(argv, &sock_in6, &rt, (char **)&dev_name);
374
375 sockfd = xsocket(AF_INET6, SOCK_DGRAM, 0);
376 if (dev_name) {
377 char ifre_buf[sizeof(struct ifreq)] = {0,};
378 struct ifreq *ifre = (struct ifreq*)ifre_buf;
379 xstrncpy(ifre->ifr_name, dev_name, IFNAMSIZ);
380 xioctl(sockfd, SIOGIFINDEX, ifre);
381 rt.rtmsg_ifindex = ifre->ifr_ifindex;
382 }
383 if (action == 1) xioctl(sockfd, SIOCADDRT, &rt);
384 else xioctl(sockfd, SIOCDELRT, &rt);
385 xclose(sockfd);
386 }
387
388 /*
389 * format the dest and src address in ipv6 format.
390 * e.g. 2002:6b6d:26c8:d:ea03:9aff:fe65:9d62
391 */
ipv6_addr_formating(char * ptr,char * addr)392 static void ipv6_addr_formating(char *ptr, char *addr)
393 {
394 int i = 0;
395 while (i <= IPV6_ADDR_LEN) {
396 if (!*ptr) {
397 if (i == IPV6_ADDR_LEN) {
398 addr[IPV6_ADDR_LEN - 1] = 0; //NULL terminating the ':' separated address.
399 break;
400 }
401 error_exit("IPv6 ip format error");
402 }
403 addr[i++] = *ptr++;
404 if (!((i+1) % 5)) addr[i++] = ':'; //put ':' after 4th bit
405 }
406 }
407
display_routes6(void)408 static void display_routes6(void)
409 {
410 char iface[16] = {0,}, ipv6_dest_addr[41];
411 char ipv6_src_addr[41], flag_val[10], buf2[INET6_ADDRSTRLEN];
412 int prefixlen, metric, use, refcount, flag, items = 0;
413 unsigned char buf[sizeof(struct in6_addr)];
414
415 FILE *fp = xfopen("/proc/net/ipv6_route", "r");
416
417 xprintf("Kernel IPv6 routing table\n"
418 "%-43s%-40s Flags Metric Ref Use Iface\n", "Destination", "Next Hop");
419
420 while ((items = fscanf(fp, "%32s%x%*s%*x%32s%x%x%x%x%15s\n", ipv6_dest_addr+8,
421 &prefixlen, ipv6_src_addr+8, &metric, &use, &refcount, &flag,
422 iface)) == 8)
423 {
424 if (!(flag & RTF_UP)) continue; //skip down interfaces.
425
426 //ipv6_dest_addr+8: as the values are filled from the 8th location of the array.
427 ipv6_addr_formating(ipv6_dest_addr+8, ipv6_dest_addr);
428 ipv6_addr_formating(ipv6_src_addr+8, ipv6_src_addr);
429
430 get_flag_value(flag_val, flag);
431 if (inet_pton(AF_INET6, ipv6_dest_addr, buf) <= 0) perror_exit("inet");
432 if (inet_ntop(AF_INET6, buf, buf2, INET6_ADDRSTRLEN))
433 sprintf(toybuf, "%s/%d", buf2, prefixlen);
434
435 if (inet_pton(AF_INET6, ipv6_src_addr, buf) <= 0) perror_exit("inet");
436 if (inet_ntop(AF_INET6, buf, buf2, INET6_ADDRSTRLEN))
437 xprintf("%-43s %-39s %-5s %-6d %-4d %5d %-8s\n",
438 toybuf, buf2, flag_val, metric, refcount, use, iface);
439 }
440 if ((items > 0) && feof(fp)) perror_exit("fscanf");
441
442 fclose(fp);
443 }
444
route_main(void)445 void route_main(void)
446 {
447 if (!TT.family) TT.family = "inet";
448 if (!*toys.optargs) {
449 if (!strcmp(TT.family, "inet")) display_routes();
450 else if (!strcmp(TT.family, "inet6")) display_routes6();
451 else help_exit(0);
452 } else {
453 if (!strcmp(TT.family, "inet6")) setroute_inet6(toys.optargs);
454 else setroute(toys.optargs);
455 }
456 }
457