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