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