• 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  *
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, 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;
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, (struct sockaddr_in *) &rt->rt_gateway))
218             rt->rt_flags |= RTF_GATEWAY;
219           else perror_exit("gateway '%s' is a NETWORK", *argv);
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, 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, 128, INT_MAX); //win low
228         rt->rt_flags |= RTF_WINDOW;
229       } else if (!strcmp(*argv, "irtt")) {
230         rt->rt_irtt = atolx_range(*argv, 0, INT_MAX);
231         rt->rt_flags |= RTF_IRTT;
232       } else if (!strcmp(*argv, "dev") && !rt->rt_dev) rt->rt_dev = *argv;
233       else help_exit("no '%s'", *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, 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, (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);
338         } else help_exit(0);
339       } else if (!strcmp(*argv, "dev")) {
340         if (!*dev_name) *dev_name = *argv;
341       } else help_exit(0);
342     }
343   }
344 }
345 
346 // add/del a route.
setroute_inet6(char ** argv)347 static void setroute_inet6(char **argv)
348 {
349   struct sockaddr_in6 sock_in6;
350   struct in6_rtmsg rt;
351   char *targetip, *dev_name = 0;
352   int sockfd, action = get_action(&argv, arglist1);
353 
354   if (!action || !*argv) help_exit(0);
355   memset(&sock_in6, 0, sizeof(struct sockaddr_in6));
356   memset(&rt, 0, sizeof(struct in6_rtmsg));
357   targetip = *argv++;
358   if (!*argv) help_exit(0);
359 
360   if (!strcmp(targetip, "default")) {
361     rt.rtmsg_flags = RTF_UP;
362     rt.rtmsg_dst_len = 0;
363   } else {
364     is_prefix_inet6((char **)&targetip, &rt);
365     if (get_addrinfo(targetip, (struct sockaddr_in6 *) &sock_in6))
366       perror_exit("resolving '%s'", targetip);
367   }
368   rt.rtmsg_metric = 1; //default metric.
369   memcpy(&rt.rtmsg_dst, sock_in6.sin6_addr.s6_addr, sizeof(struct in6_addr));
370   get_next_params_inet6(argv, &sock_in6, &rt, (char **)&dev_name);
371 
372   sockfd = xsocket(AF_INET6, SOCK_DGRAM, 0);
373   if (dev_name) {
374     char ifre_buf[sizeof(struct ifreq)] = {0,};
375     struct ifreq *ifre = (struct ifreq*)ifre_buf;
376     xstrncpy(ifre->ifr_name, dev_name, IFNAMSIZ);
377     xioctl(sockfd, SIOGIFINDEX, ifre);
378     rt.rtmsg_ifindex = ifre->ifr_ifindex;
379   }
380   if (action == 1) xioctl(sockfd, SIOCADDRT, &rt);
381   else xioctl(sockfd, SIOCDELRT, &rt);
382   xclose(sockfd);
383 }
384 
385 /*
386  * format the dest and src address in ipv6 format.
387  * e.g. 2002:6b6d:26c8:d:ea03:9aff:fe65:9d62
388  */
ipv6_addr_formating(char * ptr,char * addr)389 static void ipv6_addr_formating(char *ptr, char *addr)
390 {
391   int i = 0;
392   while (i <= IPV6_ADDR_LEN) {
393     if (!*ptr) {
394       if (i == IPV6_ADDR_LEN) {
395         addr[IPV6_ADDR_LEN - 1] = 0; //NULL terminating the ':' seperated address.
396         break;
397       }
398       error_exit("IPv6 ip format error");
399     }
400     addr[i++] = *ptr++;
401     if (!((i+1) % 5)) addr[i++] = ':'; //put ':' after 4th bit
402   }
403 }
404 
display_routes6(void)405 static void display_routes6(void)
406 {
407   char iface[16] = {0,}, ipv6_dest_addr[41];
408   char ipv6_src_addr[41], flag_val[10], buf2[INET6_ADDRSTRLEN];
409   int prefixlen, metric, use, refcount, flag, items = 0;
410   unsigned char buf[sizeof(struct in6_addr)];
411 
412   FILE *fp = xfopen("/proc/net/ipv6_route", "r");
413 
414   xprintf("Kernel IPv6 routing table\n"
415       "%-43s%-40s Flags Metric Ref    Use Iface\n", "Destination", "Next Hop");
416 
417   while ((items = fscanf(fp, "%32s%x%*s%*x%32s%x%x%x%x%15s\n", ipv6_dest_addr+8,
418           &prefixlen, ipv6_src_addr+8, &metric, &use, &refcount, &flag,
419           iface)) == 8)
420   {
421     if (!(flag & RTF_UP)) continue; //skip down interfaces.
422 
423     //ipv6_dest_addr+8: as the values are filled from the 8th location of the array.
424     ipv6_addr_formating(ipv6_dest_addr+8, ipv6_dest_addr);
425     ipv6_addr_formating(ipv6_src_addr+8, ipv6_src_addr);
426 
427     get_flag_value(flag_val, flag);
428     if (inet_pton(AF_INET6, ipv6_dest_addr, buf) <= 0) perror_exit("inet");
429     if (inet_ntop(AF_INET6, buf, buf2, INET6_ADDRSTRLEN))
430       sprintf(toybuf, "%s/%d", buf2, prefixlen);
431 
432     if (inet_pton(AF_INET6, ipv6_src_addr, buf) <= 0) perror_exit("inet");
433     if (inet_ntop(AF_INET6, buf, buf2, INET6_ADDRSTRLEN))
434       xprintf("%-43s %-39s %-5s %-6d %-4d %5d %-8s\n",
435           toybuf, buf2, flag_val, metric, refcount, use, iface);
436   }
437   if ((items > 0) && feof(fp)) perror_exit("fscanf");
438 
439   fclose(fp);
440 }
441 
route_main(void)442 void route_main(void)
443 {
444   if (!TT.family) TT.family = "inet";
445   if (!*toys.optargs) {
446     if (!strcmp(TT.family, "inet")) display_routes();
447     else if (!strcmp(TT.family, "inet6")) display_routes6();
448     else help_exit(0);
449   } else {
450     if (!strcmp(TT.family, "inet6")) setroute_inet6(toys.optargs);
451     else setroute(toys.optargs);
452   }
453 }
454