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, 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.
 241static 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.
 257static 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 */
 303static 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 */
 321static 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.
 347static 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 */
 389static 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
 405static 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
 442void 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