toybox/toys/pending/route.c
<<
>>
Prefs
   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
  15USE_ROUTE(NEWTOY(route, "?neA:", TOYFLAG_BIN))
  16config 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
  53GLOBALS(
  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
  61struct _arglist {
  62  char *arg;
  63
  64  int action;
  65};
  66
  67static struct _arglist arglist1[] = {
  68  { "add", 1 }, { "del", 2 },
  69  { "delete", 2 }, { NULL, 0 }
  70};
  71
  72static struct _arglist arglist2[] = {
  73  { "-net", 1 }, { "-host", 2 },
  74  { NULL, 0 }
  75};
  76
  77// to get the host name from the given ip.
  78static 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.
  98static 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
 116static 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.
 130static 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 */
 174static 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 */
 192static 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.
 242static 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.
 258static 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 */
 304static 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 */
 322static 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.
 349static 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 */
 391static 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
 407static 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
 444void 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