Home | History | Annotate | Download | only in pending
      1 /* route.c - Display/edit network routing table.
      2  *
      3  * Copyright 2012 Ranjan Kumar <ranjankumar.bth (at) gmail.com>
      4  * Copyright 2013 Kyungwan Han <asura321 (at) 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.
     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.
     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 
    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.
    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  */
    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  */
    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.
    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.
    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  */
    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  */
    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.
    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  */
    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 
    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 
    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