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