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