diff options
| -rw-r--r-- | lib/lib.h | 2 | ||||
| -rw-r--r-- | lib/pending.c | 2 | ||||
| -rw-r--r-- | toys/pending/route.c | 499 | 
3 files changed, 501 insertions, 2 deletions
| @@ -190,7 +190,7 @@ int read_password(char * buff, int buflen, char* mesg);  int update_password(char *filename, char* username, char* encrypted);  // cut helper functions -unsigned long get_int_value(const char *numstr, unsigned lowrange, unsigned highrange); +unsigned long get_int_value(const char *numstr, unsigned long lowrange, unsigned long highrange);  // grep helper functions  char  *astrcat (char *, char *); diff --git a/lib/pending.c b/lib/pending.c index 9c92f2ce..9e250854 100644 --- a/lib/pending.c +++ b/lib/pending.c @@ -39,7 +39,7 @@ done:  /*   * used to get the interger value.   */ -unsigned long get_int_value(const char *numstr, unsigned lowrange, unsigned highrange) +unsigned long get_int_value(const char *numstr, unsigned long lowrange, unsigned long highrange)  {    unsigned long rvalue = 0;    char *ptr; diff --git a/toys/pending/route.c b/toys/pending/route.c new file mode 100644 index 00000000..63370882 --- /dev/null +++ b/toys/pending/route.c @@ -0,0 +1,499 @@ +/* route.c - Display routing table. + * + * Copyright 2012 Ranjan Kumar <ranjankumar.bth@gmail.com> + * Copyright 2013 Kyungwan Han <asura321@gmail.com> + * + * No Standard + * +USE_ROUTE(NEWTOY(route, "?neA:", TOYFLAG_BIN)) +config ROUTE +  bool "route" +  default n +  help +    usage: route -neA inet{6} / [{add|del}] + +    Display/Edit kernel routing tables. + +    -n  Don't resolve names +    -e  Display other/more information +    -A  inet{6} Select Address Family +*/ + +#define FOR_route +#include "toys.h" +#include "toynet.h" +#include <net/route.h> +#include <sys/param.h> + +GLOBALS( +  char *family; +) + +#define DEFAULT_PREFIXLEN 128 +#define INVALID_ADDR 0xffffffffUL +#define IPV6_ADDR_LEN 40 //32 + 7 (':') + 1 ('\0') + +#define TEST_ARGV(argv) if (!*argv) show_route_help() + +struct _arglist { +  char *arg; +  int action; +}; + +static struct _arglist arglist1[] = { +  { "add", 1 }, { "del", 2 }, +  { "delete", 2 }, { NULL, 0 } +}; + +static struct _arglist arglist2[] = { +  { "-net", 1 }, { "-host", 2 }, +  { NULL, 0 } +}; + +// display help info and exit. +static void show_route_help(void) +{ +  toys.exithelp = 1; +  error_exit("Invalid Argument"); +} + +// to get the host name from the given ip. +static int get_hostname(char *ipstr, struct sockaddr_in *sockin) +{ +  struct hostent *host; + +  sockin->sin_family = AF_INET; +  sockin->sin_port = 0; + +  if (!strcmp(ipstr, "default")) { +    sockin->sin_addr.s_addr = INADDR_ANY; +    return 1; +  } + +  if (inet_aton(ipstr, &sockin->sin_addr)) return 0; +  if (!(host = gethostbyname(ipstr))) return -1; +  memcpy(&sockin->sin_addr, host->h_addr_list[0], sizeof(struct in_addr)); +  return 0; +} + +// used to extract the address info from the given ip. +static int get_addrinfo(char *ip, struct sockaddr_in6 *sock_in6) +{ +  struct addrinfo hints, *result; +  int status = 0; + +  memset(&hints, 0, sizeof(struct addrinfo)); +  hints.ai_family = AF_INET6; +  if ((status = getaddrinfo(ip, NULL, &hints, &result))) { +    perror_msg("getaddrinfo: %s", gai_strerror(status)); +    return -1; +  } +  if (result) { +    memcpy(sock_in6, result->ai_addr, sizeof(*sock_in6)); +    freeaddrinfo(result); +  } +  return 0; +} + +//get the flag values for route command. +static void get_flag_value(char *str, int flags) +{ +  int i = 0; +  static unsigned flagarray[] = { +    RTF_GATEWAY, RTF_HOST, RTF_REINSTATE, RTF_DYNAMIC, +    RTF_MODIFIED, RTF_DEFAULT, RTF_ADDRCONF, RTF_CACHE +  }; + +  *str++ = 'U'; +  while ((*str = "GHRDMDAC"[i])) +    if (flags & flagarray[i++]) ++str; +} + +// extract inet4 route info from /proc/net/route file and display it. +static void display_routes(void) +{ +  unsigned long dest, gate, mask; +  int flags, ref, use, metric, mss, win, irtt, items; +  char iface[64] = {0,}, flag_val[10]; //there are 9 flags "UGHRDMDAC" for route. + +  FILE *fp = xfopen("/proc/net/route", "r"); + +  xprintf("Kernel IP routing table\n" +      "Destination     Gateway         Genmask         Flags %s Iface\n", +      (toys.optflags & FLAG_e)? "  MSS Window  irtt" : "Metric Ref    Use"); + +  if (fscanf(fp, "%*[^\n]\n") < 0) perror_exit("fscanf"); //skip 1st line +  while ((items = fscanf(fp, "%63s%lx%lx%X%d%d%d%lx%d%d%d\n", iface, &dest,  +          &gate, &flags, &ref, &use, &metric, &mask, &mss, &win, &irtt)) == 11) { + +    char *destip = toybuf, *gateip = toybuf+32, *maskip = toybuf+64; //ip string 16 +    memset(flag_val, 0, 10); + +    if (!(flags & RTF_UP)) continue; //skip down interfaces. + +    if (!dest && !(toys.optflags & FLAG_n)) strcpy( destip, "default"); +    else if (!inet_ntop(AF_INET, &dest, destip, 32)) perror_exit("inet"); + +    if (!gate && !(toys.optflags & FLAG_n)) strcpy( gateip, "*"); +    else if (!inet_ntop(AF_INET, &gate, gateip, 32)) perror_exit("inet"); + +    if (!inet_ntop(AF_INET, &mask, maskip, 32)) perror_exit("inet"); + +    //Get flag Values +    get_flag_value(flag_val, (flags & (RTF_GATEWAY|RTF_HOST|RTF_REINSTATE +            |RTF_DYNAMIC|RTF_MODIFIED))); +    if (flags & RTF_REJECT) flag_val[0] = '!'; +    xprintf("%-15.15s %-15.15s %-16s%-6s", destip, gateip, maskip, flag_val); +    if (toys.optflags & FLAG_e) xprintf("%5d %-5d %6d %s\n", mss, win, irtt, iface); +    else xprintf("%-6d %-2d %7d %s\n", metric, ref, use, iface); +  } + +  if (items > 0 && feof(fp)) perror_exit("fscanf %d", items); +  fclose(fp); +} + + +/* + * find the given parameter in list like add/del/net/host. + * and if match found return the appropriate action. + */ +static int get_action(char ***argv, struct _arglist *list) +{ +  struct _arglist *alist; + +  if (!**argv) return 0; +  for (alist = list; alist->arg; alist++) { //find the given parameter in list +    if (!strcmp(**argv, alist->arg)) { +      *argv += 1; +      return alist->action; +    } +  } +  return 0; +} + +/* + * get prefix len (if any) and remove the prefix from target ip. + * if no prefix then set netmask as default. + */ +static void is_prefix(char **tip, char **netmask, struct rtentry *rt) +{ +  char *prefix = strchr(*tip, '/'); +  if (prefix) { +    unsigned long plen; +    plen = get_int_value(prefix + 1, 0, 32); +    //used to verify the netmask and route conflict. +    (((struct sockaddr_in *)&((rt)->rt_genmask))->sin_addr.s_addr) +      = htonl( ~(INVALID_ADDR >> plen)); +    *prefix = '\0'; +    rt->rt_genmask.sa_family = AF_INET; +  } else *netmask = "default"; //default netmask. +} + +/* + * used to get the params like: metric, netmask, gw, mss, window, irtt, dev and their values. + * additionally set the flag values for reject, mod, dyn and reinstate. + */ +static void get_next_params(char **argv, struct rtentry *rt, char **netmask) +{ +  while (*argv) { +    //set the metric field in the routing table. +    if (!strcmp(*argv, "metric")) { +      argv++; +      TEST_ARGV(argv); +      rt->rt_metric = get_int_value(*argv, 0, ULONG_MAX) + 1; +      argv++; +    } else if (!strcmp(*argv, "netmask")) { +      //when adding a network route, the netmask to be used. +      struct sockaddr sock; +      unsigned int addr_mask = (((struct sockaddr_in *)&((rt)->rt_genmask))->sin_addr.s_addr); +      if (addr_mask) show_route_help(); +      argv++; +      TEST_ARGV(argv); +      *netmask = *argv; +      if (get_hostname(*netmask, (struct sockaddr_in *) &sock) < 0) +        perror_exit("resolving '%s'", *netmask); +      rt->rt_genmask = sock; +      argv++; +    } else if (!strcmp(*argv, "gw")) {  +      //route packets via a gateway. +      if (!(rt->rt_flags & RTF_GATEWAY)) { +        int ishost; +        argv++; +        TEST_ARGV(argv); +        if ((ishost = get_hostname(*argv, (struct sockaddr_in *) &rt->rt_gateway)) == 0) { +          rt->rt_flags |= RTF_GATEWAY; +          argv++; +        } else if (ishost < 0) perror_exit("resolving '%s'", *argv); +        else perror_exit("gateway '%s' is a NETWORK", *argv); +      } else show_route_help(); +    } else if (!strcmp(*argv, "mss")) { +      //set the TCP Maximum Segment Size for connections over this route. +      argv++; +      TEST_ARGV(argv); +      rt->rt_mss = get_int_value(*argv, 64, 32768); //MSS low and max +      rt->rt_flags |= RTF_MSS; +      argv++; +    } else if (!strcmp(*argv, "window")) { +      //set the TCP window size for connections over this route to W bytes. +      argv++; +      TEST_ARGV(argv); +      rt->rt_window = get_int_value(*argv, 128, INT_MAX); //win low +      rt->rt_flags |= RTF_WINDOW; +      argv++; +    } else if (!strcmp(*argv, "irtt")) { +      long nclock_ticks = sysconf(_SC_CLK_TCK); //number of clock ticks per second. +      argv++; +      TEST_ARGV(argv); +      nclock_ticks /= 100; +      rt->rt_irtt = strtoul(*argv, NULL, 10); +      if (nclock_ticks > 0) rt->rt_irtt *= nclock_ticks; +      rt->rt_flags |= 0x0100; //RTF_IRTT +      argv++; +    } else if (!strcmp(*argv, "dev")) { +      argv++; +      TEST_ARGV(argv); +      if ((!rt->rt_dev)) rt->rt_dev = *argv; +      argv++; +    } else if (!strcmp(*argv, "reject")) { +      rt->rt_flags |= RTF_REJECT; +      argv++; +    } else if (!strcmp(*argv, "mod")) { +      rt->rt_flags |= RTF_MODIFIED; +      argv++; +    } else if (!strcmp(*argv, "dyn")) { +      rt->rt_flags |= RTF_DYNAMIC; +      argv++; +    } else if (!strcmp(*argv, "reinstate")) { +      rt->rt_flags |= RTF_REINSTATE; +      argv++; +    } else show_route_help();  //No match found; exit form the application. +  }//end of while loop. + +  if (!rt->rt_dev && (rt->rt_flags & RTF_REJECT)) rt->rt_dev = (char *)"lo"; +} + +// verify the netmask and conflict in netmask and route address. +static void verify_netmask(struct rtentry *rt, char *netmask) +{ +  unsigned int addr_mask = (((struct sockaddr_in *)&((rt)->rt_genmask))->sin_addr.s_addr); +  unsigned int router_addr = ~(unsigned int)(((struct sockaddr_in *)&((rt)->rt_dst))->sin_addr.s_addr); + +  if (addr_mask) { +    addr_mask = ~ntohl(addr_mask); +    if ((rt->rt_flags & RTF_HOST) && addr_mask != INVALID_ADDR) +      perror_exit("conflicting netmask and host route"); +    if (addr_mask & (addr_mask + 1)) perror_exit("wrong netmask '%s'", netmask); +    addr_mask = ((struct sockaddr_in *) &rt->rt_dst)->sin_addr.s_addr; +    if (addr_mask & router_addr) perror_exit("conflicting netmask and route address"); +  } +} + +// add/del a route. +static void setroute(char **argv) +{ +  struct rtentry rt; +  char *netmask = NULL, *targetip; +  int is_net_or_host = 0, sokfd, arg2_action; +  int action = get_action(&argv, arglist1); //verify the arg for add/del. + +  if (!action || !*argv) show_route_help(); + +  arg2_action = get_action(&argv, arglist2); //verify the arg for -net or -host +  if (!*argv) show_route_help();  + +  memset(&rt, 0, sizeof(struct rtentry)); +  targetip = *argv++; + +  is_prefix((char **)&targetip, (char **)&netmask, &rt); +  if ((is_net_or_host = get_hostname(targetip,  +          (struct sockaddr_in *) &rt.rt_dst)) < 0) +    perror_exit("resolving '%s'", targetip); + +  if (arg2_action) is_net_or_host = arg2_action & 1; +  rt.rt_flags = ((is_net_or_host) ? RTF_UP : (RTF_UP | RTF_HOST)); + +  get_next_params(argv, &rt, (char **)&netmask); +  verify_netmask(&rt, (char *)netmask); + +  if ((action == 1) && (rt.rt_flags & RTF_HOST)) +    (((struct sockaddr_in *)&((rt).rt_genmask))->sin_addr.s_addr) = INVALID_ADDR; + +  sokfd = xsocket(AF_INET, SOCK_DGRAM, 0); +  if (action == 1) xioctl(sokfd, SIOCADDRT, &rt); +  else xioctl(sokfd, SIOCDELRT, &rt); +  xclose(sokfd); +} + +/* + * get prefix len (if any) and remove the prefix from target ip. + * if no prefix then set default prefix len. + */ +static void is_prefix_inet6(char **tip, struct in6_rtmsg *rt) +{ +  unsigned long plen; +  char *prefix = strchr(*tip, '/'); + +  if (prefix) { +    *prefix = '\0'; +    plen = get_int_value(prefix + 1, 0, 128); //DEFAULT_PREFIXLEN); +  } else plen = DEFAULT_PREFIXLEN; + +  rt->rtmsg_flags = (plen == DEFAULT_PREFIXLEN) ? (RTF_UP | RTF_HOST) : RTF_UP; +  rt->rtmsg_dst_len = plen; +} + +/* + * used to get the params like: metric, gw, dev and their values. + * additionally set the flag values for mod and dyn. + */ +static void get_next_params_inet6(char **argv, struct sockaddr_in6 *sock_in6, struct in6_rtmsg *rt, char **dev_name) +{ +  while (*argv) { +    if (!strcmp(*argv, "metric")) { +      //set the metric field in the routing table. +      argv++; +      TEST_ARGV(argv); +      rt->rtmsg_metric = get_int_value(*argv, 0, ULONG_MAX); +      argv++; +    } else if (!strcmp(*argv, "gw")) { +      //route packets via a gateway. +      if (!(rt->rtmsg_flags & RTF_GATEWAY)) { +        argv++; +        TEST_ARGV(argv); +        if (!get_addrinfo(*argv, (struct sockaddr_in6 *) &sock_in6)) { +          memcpy(&rt->rtmsg_gateway, sock_in6->sin6_addr.s6_addr, sizeof(struct in6_addr)); +          rt->rtmsg_flags |= RTF_GATEWAY; +          argv++; +        } else perror_exit("resolving '%s'", *argv); +      } else show_route_help(); +    } else if (!strcmp(*argv, "dev")) { +      argv++; +      TEST_ARGV(argv); +      if (!*dev_name) *dev_name = *argv; +      argv++; +    } else if (!strcmp(*argv, "mod")) { +      rt->rtmsg_flags |= RTF_MODIFIED; +      argv++; +    } else if (!strcmp(*argv, "dyn")) { +      rt->rtmsg_flags |= RTF_DYNAMIC; +      argv++; +    } else show_route_help(); +  }//end of while loop. +} + +// add/del a route. +static void setroute_inet6(char **argv) +{ +  struct sockaddr_in6 sock_in6; +  struct in6_rtmsg rt; +  char *targetip; +  int sockfd, action = get_action(&argv, arglist1); + +  if (!action || !*argv) show_route_help(); +  memset(&sock_in6, 0, sizeof(struct sockaddr_in6)); +  memset(&rt, 0, sizeof(struct in6_rtmsg)); +  targetip = *argv++; +  if (*argv) { +    unsigned long plen = 0; +    char *dev_name = NULL; +    if (!strcmp(targetip, "default")) { +      rt.rtmsg_flags = RTF_UP; +      rt.rtmsg_dst_len = plen; +    } else { +      is_prefix_inet6((char **)&targetip, &rt); +      if (get_addrinfo(targetip, (struct sockaddr_in6 *) &sock_in6)) +        perror_exit("resolving '%s'", targetip); +    } +    rt.rtmsg_metric = 1; //default metric. +    memcpy(&rt.rtmsg_dst, sock_in6.sin6_addr.s6_addr, sizeof(struct in6_addr)); +    get_next_params_inet6(argv, &sock_in6, &rt, (char **)&dev_name); + +    sockfd = xsocket(AF_INET6, SOCK_DGRAM, 0); +    if (dev_name) { +      char ifre_buf[sizeof(struct ifreq)] = {0,}; +      struct ifreq *ifre = (struct ifreq*)ifre_buf; +      strncpy(ifre->ifr_name, dev_name, IFNAMSIZ-1); +      xioctl(sockfd, SIOGIFINDEX, ifre); +      rt.rtmsg_ifindex = ifre->ifr_ifindex; +    }           +    if (action == 1) xioctl(sockfd, SIOCADDRT, &rt); +    else xioctl(sockfd, SIOCDELRT, &rt); +    xclose(sockfd); +  } else show_route_help(); +} + +/* + * format the dest and src address in ipv6 format. + * e.g. 2002:6b6d:26c8:d:ea03:9aff:fe65:9d62 + */ +static void ipv6_addr_formating(char *ptr, char *addr) +{ +  int i = 0; +  while (i <= IPV6_ADDR_LEN) { +    if (!*ptr) { +      if (i == IPV6_ADDR_LEN) { +        addr[IPV6_ADDR_LEN - 1] = 0; //NULL terminating the ':' seperated address. +        break; +      } +      error_exit("IPv6 ip format error"); +    } +    addr[i++] = *ptr++; +    if (!((i+1) % 5)) addr[i++] = ':'; //put ':' after 4th bit +  } +} + +static void display_routes6(void) +{ +  char iface[16] = {0,}, ipv6_dest_addr[41];  +  char ipv6_src_addr[41], flag_val[10], buf2[INET6_ADDRSTRLEN]; +  int prefixlen, metric, use, refcount, flag, items = 0; +  unsigned char buf[sizeof(struct in6_addr)]; + +  FILE *fp = xfopen("/proc/net/ipv6_route", "r"); + +  xprintf("Kernel IPv6 routing table\n" +      "%-43s%-40s Flags Metric Ref    Use Iface\n", "Destination", "Next Hop"); + +  while ((items = fscanf(fp, "%32s%x%*s%*x%32s%x%x%x%x%10s\n", ipv6_dest_addr+8, +          &prefixlen, ipv6_src_addr+8, &metric, &use, &refcount, &flag,  +          iface)) == 8) { + +    memset(flag_val, 0, 10); + +    if (!(flag & RTF_UP)) continue; //skip down interfaces. + +    //ipv6_dest_addr+8: as the values are filled from the 8th location of the array. +    ipv6_addr_formating(ipv6_dest_addr+8, ipv6_dest_addr); +    ipv6_addr_formating(ipv6_src_addr+8, ipv6_src_addr); + +    get_flag_value(flag_val, (flag & (RTF_GATEWAY|RTF_HOST|RTF_DEFAULT| +            RTF_ADDRCONF|RTF_CACHE))); +    if (inet_pton(AF_INET6, ipv6_dest_addr, buf) <= 0) perror_exit("inet"); +    if (inet_ntop(AF_INET6, buf, buf2, INET6_ADDRSTRLEN)) { +      sprintf(toybuf, "%s/%d", buf2, prefixlen); +    } + +    if (inet_pton(AF_INET6, ipv6_src_addr, buf) <= 0) perror_exit("inet"); +    if (inet_ntop(AF_INET6, buf, buf2, INET6_ADDRSTRLEN)) { +      xprintf("%-43s %-39s %-5s %-6d %-4d %5d %-8s\n", +          toybuf, buf2, flag_val, metric, refcount, use, iface); +    } +  } +  if ((items > 0) && feof(fp)) perror_exit("fscanf"); + +  fclose(fp); +} + +void route_main(void) +{ +  if (!(toys.optflags & FLAG_A)) TT.family = "inet"; +  if (!*toys.optargs) { +    if (!strcmp(TT.family, "inet")) display_routes(); +    else if (!strcmp(TT.family, "inet6")) display_routes6(); +    else show_route_help(); +    return; +  }//End of if statement. + +  if (!strcmp(TT.family, "inet6")) setroute_inet6(toys.optargs); +  else setroute(toys.optargs); +} | 
