• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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