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, 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.
    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.
    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  */
    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  */
    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.
    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  */
    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 
    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 
    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