10f66f451Sopenharmony_ci/* route.c - Display/edit network routing table.
20f66f451Sopenharmony_ci *
30f66f451Sopenharmony_ci * Copyright 2012 Ranjan Kumar <ranjankumar.bth@gmail.com>
40f66f451Sopenharmony_ci * Copyright 2013 Kyungwan Han <asura321@gmail.com>
50f66f451Sopenharmony_ci * Copyright 2020 Eric Molitor <eric@molitor.org>
60f66f451Sopenharmony_ci *
70f66f451Sopenharmony_ci * No Standard
80f66f451Sopenharmony_ci *
90f66f451Sopenharmony_ci * TODO: autodetect -net -host target dev -A (but complain)
100f66f451Sopenharmony_ci * route add -net target 10.0.0.0 netmask 255.0.0.0 dev eth0
110f66f451Sopenharmony_ci * route del delete
120f66f451Sopenharmony_ci * delete net route, must match netmask, informative error message
130f66f451Sopenharmony_ci *
140f66f451Sopenharmony_ci * mod dyn reinstate metric netmask gw mss window irtt dev
150f66f451Sopenharmony_ci
160f66f451Sopenharmony_ciUSE_ROUTE(NEWTOY(route, "?neA:", TOYFLAG_BIN))
170f66f451Sopenharmony_ciconfig ROUTE
180f66f451Sopenharmony_ci  bool "route"
190f66f451Sopenharmony_ci  default n
200f66f451Sopenharmony_ci  help
210f66f451Sopenharmony_ci    usage: route [-ne] [-A [46]] [add|del TARGET [OPTIONS]]
220f66f451Sopenharmony_ci
230f66f451Sopenharmony_ci    Display, add or delete network routes in the "Forwarding Information Base".
240f66f451Sopenharmony_ci
250f66f451Sopenharmony_ci    -n	Show numerical addresses (no DNS lookups)
260f66f451Sopenharmony_ci    -e	display netstat fields
270f66f451Sopenharmony_ci
280f66f451Sopenharmony_ci    Routing means sending packets out a network interface to an address.
290f66f451Sopenharmony_ci    The kernel can tell where to send packets one hop away by examining each
300f66f451Sopenharmony_ci    interface's address and netmask, so the most common use of this command
310f66f451Sopenharmony_ci    is to identify a "gateway" that forwards other traffic.
320f66f451Sopenharmony_ci
330f66f451Sopenharmony_ci    Assigning an address to an interface automatically creates an appropriate
340f66f451Sopenharmony_ci    network route ("ifconfig eth0 10.0.2.15/8" does "route add 10.0.0.0/8 eth0"
350f66f451Sopenharmony_ci    for you), although some devices (such as loopback) won't show it in the
360f66f451Sopenharmony_ci    table. For machines more than one hop away, you need to specify a gateway
370f66f451Sopenharmony_ci    (ala "route add default gw 10.0.2.2").
380f66f451Sopenharmony_ci
390f66f451Sopenharmony_ci    The address "default" is a wildcard address (0.0.0.0/0) matching all
400f66f451Sopenharmony_ci    packets without a more specific route.
410f66f451Sopenharmony_ci
420f66f451Sopenharmony_ci    Available OPTIONS include:
430f66f451Sopenharmony_ci    reject   - blocking route (force match failure)
440f66f451Sopenharmony_ci    dev NAME - force matching packets out this interface (ala "eth0")
450f66f451Sopenharmony_ci    netmask  - old way of saying things like ADDR/24
460f66f451Sopenharmony_ci    gw ADDR  - forward packets to gateway ADDR
470f66f451Sopenharmony_ci*/
480f66f451Sopenharmony_ci
490f66f451Sopenharmony_ci#define FOR_route
500f66f451Sopenharmony_ci#include "toys.h"
510f66f451Sopenharmony_ci#include <net/route.h>
520f66f451Sopenharmony_ci
530f66f451Sopenharmony_ciGLOBALS(
540f66f451Sopenharmony_ci  char *family;
550f66f451Sopenharmony_ci)
560f66f451Sopenharmony_ci
570f66f451Sopenharmony_ci#define DEFAULT_PREFIXLEN 128
580f66f451Sopenharmony_ci#define INVALID_ADDR 0xffffffffUL
590f66f451Sopenharmony_ci#define IPV6_ADDR_LEN 40 //32 + 7 (':') + 1 ('\0')
600f66f451Sopenharmony_ci
610f66f451Sopenharmony_cistruct _arglist {
620f66f451Sopenharmony_ci  char *arg;
630f66f451Sopenharmony_ci  int action;
640f66f451Sopenharmony_ci};
650f66f451Sopenharmony_ci
660f66f451Sopenharmony_cistatic struct _arglist arglist1[] = {
670f66f451Sopenharmony_ci  { "add", 1 }, { "del", 2 },
680f66f451Sopenharmony_ci  { "delete", 2 }, { NULL, 0 }
690f66f451Sopenharmony_ci};
700f66f451Sopenharmony_ci
710f66f451Sopenharmony_cistatic struct _arglist arglist2[] = {
720f66f451Sopenharmony_ci  { "-net", 1 }, { "-host", 2 },
730f66f451Sopenharmony_ci  { NULL, 0 }
740f66f451Sopenharmony_ci};
750f66f451Sopenharmony_ci
760f66f451Sopenharmony_ci// to get the host name from the given ip.
770f66f451Sopenharmony_cistatic int get_hostname(char *ipstr, struct sockaddr_in *sockin)
780f66f451Sopenharmony_ci{
790f66f451Sopenharmony_ci  struct hostent *host;
800f66f451Sopenharmony_ci
810f66f451Sopenharmony_ci  sockin->sin_family = AF_INET;
820f66f451Sopenharmony_ci  sockin->sin_port = 0;
830f66f451Sopenharmony_ci
840f66f451Sopenharmony_ci  if (!strcmp(ipstr, "default")) {
850f66f451Sopenharmony_ci    sockin->sin_addr.s_addr = INADDR_ANY;
860f66f451Sopenharmony_ci    return 1;
870f66f451Sopenharmony_ci  }
880f66f451Sopenharmony_ci
890f66f451Sopenharmony_ci  if (inet_aton(ipstr, &sockin->sin_addr)) return 0;
900f66f451Sopenharmony_ci  if (!(host = gethostbyname(ipstr))) perror_exit("resolving '%s'", ipstr);
910f66f451Sopenharmony_ci  memcpy(&sockin->sin_addr, host->h_addr_list[0], sizeof(struct in_addr));
920f66f451Sopenharmony_ci
930f66f451Sopenharmony_ci  return 0;
940f66f451Sopenharmony_ci}
950f66f451Sopenharmony_ci
960f66f451Sopenharmony_ci// used to extract the address info from the given ip.
970f66f451Sopenharmony_cistatic int get_addrinfo(char *ip, struct sockaddr_in6 *sock_in6)
980f66f451Sopenharmony_ci{
990f66f451Sopenharmony_ci  struct addrinfo hints, *result;
1000f66f451Sopenharmony_ci  int status = 0;
1010f66f451Sopenharmony_ci
1020f66f451Sopenharmony_ci  memset(&hints, 0, sizeof(struct addrinfo));
1030f66f451Sopenharmony_ci  hints.ai_family = AF_INET6;
1040f66f451Sopenharmony_ci  if ((status = getaddrinfo(ip, NULL, &hints, &result))) {
1050f66f451Sopenharmony_ci    perror_msg("getaddrinfo: %s", gai_strerror(status));
1060f66f451Sopenharmony_ci    return -1;
1070f66f451Sopenharmony_ci  }
1080f66f451Sopenharmony_ci  if (result) {
1090f66f451Sopenharmony_ci    memcpy(sock_in6, result->ai_addr, sizeof(*sock_in6));
1100f66f451Sopenharmony_ci    freeaddrinfo(result);
1110f66f451Sopenharmony_ci  }
1120f66f451Sopenharmony_ci  return 0;
1130f66f451Sopenharmony_ci}
1140f66f451Sopenharmony_ci
1150f66f451Sopenharmony_cistatic void get_flag_value(char *str, int flags)
1160f66f451Sopenharmony_ci{
1170f66f451Sopenharmony_ci  // RTF_* bits in order:
1180f66f451Sopenharmony_ci  // UP, GATEWAY, HOST, REINSTATE, DYNAMIC, MODIFIED, DEFAULT, ADDRCONF, CACHE
1190f66f451Sopenharmony_ci  int i = 0, mask = 0x105003f;
1200f66f451Sopenharmony_ci
1210f66f451Sopenharmony_ci  for (; mask; mask>>=1) if (mask&1) {
1220f66f451Sopenharmony_ci    if (flags&(1<<i)) *str++ = "UGHRDMDAC"[i];
1230f66f451Sopenharmony_ci    i++;
1240f66f451Sopenharmony_ci  }
1250f66f451Sopenharmony_ci  *str = 0;
1260f66f451Sopenharmony_ci}
1270f66f451Sopenharmony_ci
1280f66f451Sopenharmony_ci// extract inet4 route info from /proc/net/route file and display it.
1290f66f451Sopenharmony_cistatic void display_routes(void)
1300f66f451Sopenharmony_ci{
1310f66f451Sopenharmony_ci  unsigned long dest, gate, mask;
1320f66f451Sopenharmony_ci  int flags, ref, use, metric, mss, win, irtt, items;
1330f66f451Sopenharmony_ci  char iface[64] = {0,}, flag_val[10]; //there are 9 flags "UGHRDMDAC" for route.
1340f66f451Sopenharmony_ci
1350f66f451Sopenharmony_ci  FILE *fp = xfopen("/proc/net/route", "r");
1360f66f451Sopenharmony_ci
1370f66f451Sopenharmony_ci  xprintf("Kernel IP routing table\n"
1380f66f451Sopenharmony_ci      "Destination     Gateway         Genmask         Flags %s Iface\n",
1390f66f451Sopenharmony_ci      (toys.optflags & FLAG_e)? "  MSS Window  irtt" : "Metric Ref    Use");
1400f66f451Sopenharmony_ci
1410f66f451Sopenharmony_ci  if (fscanf(fp, "%*[^\n]\n") < 0) perror_exit("fscanf"); //skip 1st line
1420f66f451Sopenharmony_ci  while ((items = fscanf(fp, "%63s%lx%lx%X%d%d%d%lx%d%d%d\n", iface, &dest,
1430f66f451Sopenharmony_ci          &gate, &flags, &ref, &use, &metric, &mask, &mss, &win, &irtt)) == 11)
1440f66f451Sopenharmony_ci  {
1450f66f451Sopenharmony_ci    char *destip = toybuf, *gateip = toybuf+32, *maskip = toybuf+64; //ip string 16
1460f66f451Sopenharmony_ci
1470f66f451Sopenharmony_ci    if (!(flags & RTF_UP)) continue; //skip down interfaces.
1480f66f451Sopenharmony_ci
1490f66f451Sopenharmony_ci    if (!dest && !(toys.optflags & FLAG_n)) strcpy( destip, "default");
1500f66f451Sopenharmony_ci    else if (!inet_ntop(AF_INET, &dest, destip, 32)) perror_exit("inet");
1510f66f451Sopenharmony_ci
1520f66f451Sopenharmony_ci    if (!gate && !(toys.optflags & FLAG_n)) strcpy( gateip, "*");
1530f66f451Sopenharmony_ci    else if (!inet_ntop(AF_INET, &gate, gateip, 32)) perror_exit("inet");
1540f66f451Sopenharmony_ci
1550f66f451Sopenharmony_ci    if (!inet_ntop(AF_INET, &mask, maskip, 32)) perror_exit("inet");
1560f66f451Sopenharmony_ci
1570f66f451Sopenharmony_ci    //Get flag Values
1580f66f451Sopenharmony_ci    get_flag_value(flag_val, flags);
1590f66f451Sopenharmony_ci    if (flags & RTF_REJECT) flag_val[0] = '!';
1600f66f451Sopenharmony_ci    xprintf("%-15.15s %-15.15s %-16s%-6s", destip, gateip, maskip, flag_val);
1610f66f451Sopenharmony_ci    if (toys.optflags & FLAG_e) xprintf("%5d %-5d %6d %s\n", mss, win, irtt, iface);
1620f66f451Sopenharmony_ci    else xprintf("%-6d %-2d %7d %s\n", metric, ref, use, iface);
1630f66f451Sopenharmony_ci  }
1640f66f451Sopenharmony_ci
1650f66f451Sopenharmony_ci  if (items > 0 && feof(fp)) perror_exit("fscanf %d", items);
1660f66f451Sopenharmony_ci  fclose(fp);
1670f66f451Sopenharmony_ci}
1680f66f451Sopenharmony_ci
1690f66f451Sopenharmony_ci/*
1700f66f451Sopenharmony_ci * find the given parameter in list like add/del/net/host.
1710f66f451Sopenharmony_ci * and if match found return the appropriate action.
1720f66f451Sopenharmony_ci */
1730f66f451Sopenharmony_cistatic int get_action(char ***argv, struct _arglist *list)
1740f66f451Sopenharmony_ci{
1750f66f451Sopenharmony_ci  struct _arglist *alist;
1760f66f451Sopenharmony_ci
1770f66f451Sopenharmony_ci  if (!**argv) return 0;
1780f66f451Sopenharmony_ci  for (alist = list; alist->arg; alist++) { //find the given parameter in list
1790f66f451Sopenharmony_ci    if (!strcmp(**argv, alist->arg)) {
1800f66f451Sopenharmony_ci      *argv += 1;
1810f66f451Sopenharmony_ci      return alist->action;
1820f66f451Sopenharmony_ci    }
1830f66f451Sopenharmony_ci  }
1840f66f451Sopenharmony_ci  return 0;
1850f66f451Sopenharmony_ci}
1860f66f451Sopenharmony_ci
1870f66f451Sopenharmony_ci/*
1880f66f451Sopenharmony_ci * used to get the params like: metric, netmask, gw, mss, window, irtt, dev and their values.
1890f66f451Sopenharmony_ci * additionally set the flag values for reject, mod, dyn and reinstate.
1900f66f451Sopenharmony_ci */
1910f66f451Sopenharmony_cistatic void get_next_params(char **argv, struct rtentry *rt, char **netmask)
1920f66f451Sopenharmony_ci{
1930f66f451Sopenharmony_ci  for (;*argv;argv++) {
1940f66f451Sopenharmony_ci    if (!strcmp(*argv, "reject")) rt->rt_flags |= RTF_REJECT;
1950f66f451Sopenharmony_ci    else if (!strcmp(*argv, "mod")) rt->rt_flags |= RTF_MODIFIED;
1960f66f451Sopenharmony_ci    else if (!strcmp(*argv, "dyn")) rt->rt_flags |= RTF_DYNAMIC;
1970f66f451Sopenharmony_ci    else if (!strcmp(*argv, "reinstate")) rt->rt_flags |= RTF_REINSTATE;
1980f66f451Sopenharmony_ci    else {
1990f66f451Sopenharmony_ci      if (!argv[1]) help_exit(0);
2000f66f451Sopenharmony_ci
2010f66f451Sopenharmony_ci      //set the metric field in the routing table.
2020f66f451Sopenharmony_ci      if (!strcmp(*argv, "metric"))
2030f66f451Sopenharmony_ci        rt->rt_metric = atolx_range(argv[1], 0, ULONG_MAX) + 1;
2040f66f451Sopenharmony_ci      else if (!strcmp(*argv, "netmask")) {
2050f66f451Sopenharmony_ci        //when adding a network route, the netmask to be used.
2060f66f451Sopenharmony_ci        struct sockaddr sock;
2070f66f451Sopenharmony_ci        unsigned int addr_mask = (((struct sockaddr_in *)&((rt)->rt_genmask))->sin_addr.s_addr);
2080f66f451Sopenharmony_ci
2090f66f451Sopenharmony_ci        if (addr_mask) help_exit("dup netmask");
2100f66f451Sopenharmony_ci        *netmask = argv[1];
2110f66f451Sopenharmony_ci        get_hostname(*netmask, (struct sockaddr_in *) &sock);
2120f66f451Sopenharmony_ci        rt->rt_genmask = sock;
2130f66f451Sopenharmony_ci      } else if (!strcmp(*argv, "gw")) {
2140f66f451Sopenharmony_ci        //route packets via a gateway.
2150f66f451Sopenharmony_ci        if (!(rt->rt_flags & RTF_GATEWAY)) {
2160f66f451Sopenharmony_ci          if (!get_hostname(argv[1], (struct sockaddr_in *) &rt->rt_gateway))
2170f66f451Sopenharmony_ci            rt->rt_flags |= RTF_GATEWAY;
2180f66f451Sopenharmony_ci          else perror_exit("gateway '%s' is a NETWORK", argv[1]);
2190f66f451Sopenharmony_ci        } else help_exit("dup gw");
2200f66f451Sopenharmony_ci      } else if (!strcmp(*argv, "mss")) {
2210f66f451Sopenharmony_ci        //set the TCP Maximum Segment Size for connections over this route.
2220f66f451Sopenharmony_ci        rt->rt_mtu = atolx_range(argv[1], 64, 65536);
2230f66f451Sopenharmony_ci        rt->rt_flags |= RTF_MSS;
2240f66f451Sopenharmony_ci      } else if (!strcmp(*argv, "window")) {
2250f66f451Sopenharmony_ci        //set the TCP window size for connections over this route to W bytes.
2260f66f451Sopenharmony_ci        rt->rt_window = atolx_range(argv[1], 128, INT_MAX); //win low
2270f66f451Sopenharmony_ci        rt->rt_flags |= RTF_WINDOW;
2280f66f451Sopenharmony_ci      } else if (!strcmp(*argv, "irtt")) {
2290f66f451Sopenharmony_ci        rt->rt_irtt = atolx_range(argv[1], 0, INT_MAX);
2300f66f451Sopenharmony_ci        rt->rt_flags |= RTF_IRTT;
2310f66f451Sopenharmony_ci      } else if (!strcmp(*argv, "dev") && !rt->rt_dev) rt->rt_dev = argv[1];
2320f66f451Sopenharmony_ci      else help_exit("no '%s'", *argv);
2330f66f451Sopenharmony_ci      argv++;
2340f66f451Sopenharmony_ci    }
2350f66f451Sopenharmony_ci  }
2360f66f451Sopenharmony_ci
2370f66f451Sopenharmony_ci  if (!rt->rt_dev && (rt->rt_flags & RTF_REJECT)) rt->rt_dev = (char *)"lo";
2380f66f451Sopenharmony_ci}
2390f66f451Sopenharmony_ci
2400f66f451Sopenharmony_ci// verify the netmask and conflict in netmask and route address.
2410f66f451Sopenharmony_cistatic void verify_netmask(struct rtentry *rt, char *netmask)
2420f66f451Sopenharmony_ci{
2430f66f451Sopenharmony_ci  unsigned int addr_mask = (((struct sockaddr_in *)&((rt)->rt_genmask))->sin_addr.s_addr);
2440f66f451Sopenharmony_ci  unsigned int router_addr = ~(unsigned int)(((struct sockaddr_in *)&((rt)->rt_dst))->sin_addr.s_addr);
2450f66f451Sopenharmony_ci
2460f66f451Sopenharmony_ci  if (addr_mask) {
2470f66f451Sopenharmony_ci    addr_mask = ~ntohl(addr_mask);
2480f66f451Sopenharmony_ci    if ((rt->rt_flags & RTF_HOST) && addr_mask != INVALID_ADDR)
2490f66f451Sopenharmony_ci      perror_exit("conflicting netmask and host route");
2500f66f451Sopenharmony_ci    if (addr_mask & (addr_mask + 1)) perror_exit("wrong netmask '%s'", netmask);
2510f66f451Sopenharmony_ci    addr_mask = ((struct sockaddr_in *) &rt->rt_dst)->sin_addr.s_addr;
2520f66f451Sopenharmony_ci    if (addr_mask & router_addr) perror_exit("conflicting netmask and route address");
2530f66f451Sopenharmony_ci  }
2540f66f451Sopenharmony_ci}
2550f66f451Sopenharmony_ci
2560f66f451Sopenharmony_ci// add/del a route.
2570f66f451Sopenharmony_cistatic void setroute(char **argv)
2580f66f451Sopenharmony_ci{
2590f66f451Sopenharmony_ci  struct rtentry rt;
2600f66f451Sopenharmony_ci  char *netmask, *targetip;
2610f66f451Sopenharmony_ci  int is_net_or_host = 0, sokfd, arg2_action;
2620f66f451Sopenharmony_ci  int action = get_action(&argv, arglist1); //verify the arg for add/del.
2630f66f451Sopenharmony_ci
2640f66f451Sopenharmony_ci  if (!action || !*argv) help_exit("setroute");
2650f66f451Sopenharmony_ci
2660f66f451Sopenharmony_ci  arg2_action = get_action(&argv, arglist2); //verify the arg for -net or -host
2670f66f451Sopenharmony_ci  if (!*argv) help_exit("setroute");
2680f66f451Sopenharmony_ci
2690f66f451Sopenharmony_ci  memset(&rt, 0, sizeof(struct rtentry));
2700f66f451Sopenharmony_ci  targetip = *argv++;
2710f66f451Sopenharmony_ci
2720f66f451Sopenharmony_ci  netmask = strchr(targetip, '/');
2730f66f451Sopenharmony_ci  if (netmask) {
2740f66f451Sopenharmony_ci    *netmask++ = 0;
2750f66f451Sopenharmony_ci    //used to verify the netmask and route conflict.
2760f66f451Sopenharmony_ci    (((struct sockaddr_in *)&rt.rt_genmask)->sin_addr.s_addr)
2770f66f451Sopenharmony_ci      = htonl((1<<(32-atolx_range(netmask, 0, 32)))-1);
2780f66f451Sopenharmony_ci    rt.rt_genmask.sa_family = AF_INET;
2790f66f451Sopenharmony_ci    netmask = 0;
2800f66f451Sopenharmony_ci  } else netmask = "default";
2810f66f451Sopenharmony_ci
2820f66f451Sopenharmony_ci  is_net_or_host = get_hostname(targetip, (void *)&rt.rt_dst);
2830f66f451Sopenharmony_ci
2840f66f451Sopenharmony_ci  if (arg2_action) is_net_or_host = arg2_action & 1;
2850f66f451Sopenharmony_ci  rt.rt_flags = ((is_net_or_host) ? RTF_UP : (RTF_UP | RTF_HOST));
2860f66f451Sopenharmony_ci
2870f66f451Sopenharmony_ci  get_next_params(argv, &rt, (char **)&netmask);
2880f66f451Sopenharmony_ci  verify_netmask(&rt, (char *)netmask);
2890f66f451Sopenharmony_ci
2900f66f451Sopenharmony_ci  if ((action == 1) && (rt.rt_flags & RTF_HOST))
2910f66f451Sopenharmony_ci    (((struct sockaddr_in *)&((rt).rt_genmask))->sin_addr.s_addr) = INVALID_ADDR;
2920f66f451Sopenharmony_ci
2930f66f451Sopenharmony_ci  sokfd = xsocket(AF_INET, SOCK_DGRAM, 0);
2940f66f451Sopenharmony_ci  if (action == 1) xioctl(sokfd, SIOCADDRT, &rt);
2950f66f451Sopenharmony_ci  else xioctl(sokfd, SIOCDELRT, &rt);
2960f66f451Sopenharmony_ci  xclose(sokfd);
2970f66f451Sopenharmony_ci}
2980f66f451Sopenharmony_ci
2990f66f451Sopenharmony_ci/*
3000f66f451Sopenharmony_ci * get prefix len (if any) and remove the prefix from target ip.
3010f66f451Sopenharmony_ci * if no prefix then set default prefix len.
3020f66f451Sopenharmony_ci */
3030f66f451Sopenharmony_cistatic void is_prefix_inet6(char **tip, struct in6_rtmsg *rt)
3040f66f451Sopenharmony_ci{
3050f66f451Sopenharmony_ci  unsigned long plen;
3060f66f451Sopenharmony_ci  char *prefix = strchr(*tip, '/');
3070f66f451Sopenharmony_ci
3080f66f451Sopenharmony_ci  if (prefix) {
3090f66f451Sopenharmony_ci    *prefix = '\0';
3100f66f451Sopenharmony_ci    plen = atolx_range(prefix + 1, 0, 128); //DEFAULT_PREFIXLEN);
3110f66f451Sopenharmony_ci  } else plen = DEFAULT_PREFIXLEN;
3120f66f451Sopenharmony_ci
3130f66f451Sopenharmony_ci  rt->rtmsg_flags = (plen == DEFAULT_PREFIXLEN) ? (RTF_UP | RTF_HOST) : RTF_UP;
3140f66f451Sopenharmony_ci  rt->rtmsg_dst_len = plen;
3150f66f451Sopenharmony_ci}
3160f66f451Sopenharmony_ci
3170f66f451Sopenharmony_ci/*
3180f66f451Sopenharmony_ci * used to get the params like: metric, gw, dev and their values.
3190f66f451Sopenharmony_ci * additionally set the flag values for mod and dyn.
3200f66f451Sopenharmony_ci */
3210f66f451Sopenharmony_cistatic void get_next_params_inet6(char **argv, struct sockaddr_in6 *sock_in6, struct in6_rtmsg *rt, char **dev_name)
3220f66f451Sopenharmony_ci{
3230f66f451Sopenharmony_ci  for (;*argv;argv++) {
3240f66f451Sopenharmony_ci    if (!strcmp(*argv, "mod")) rt->rtmsg_flags |= RTF_MODIFIED;
3250f66f451Sopenharmony_ci    else if (!strcmp(*argv, "dyn")) rt->rtmsg_flags |= RTF_DYNAMIC;
3260f66f451Sopenharmony_ci    else {
3270f66f451Sopenharmony_ci      if (!argv[1]) help_exit(0);
3280f66f451Sopenharmony_ci
3290f66f451Sopenharmony_ci      if (!strcmp(*argv, "metric"))
3300f66f451Sopenharmony_ci        rt->rtmsg_metric = atolx_range(argv[1], 0, ULONG_MAX);
3310f66f451Sopenharmony_ci      else if (!strcmp(*argv, "gw")) {
3320f66f451Sopenharmony_ci        //route packets via a gateway.
3330f66f451Sopenharmony_ci        if (!(rt->rtmsg_flags & RTF_GATEWAY)) {
3340f66f451Sopenharmony_ci          if (!get_addrinfo(argv[1], (struct sockaddr_in6 *) &sock_in6)) {
3350f66f451Sopenharmony_ci            memcpy(&rt->rtmsg_gateway, sock_in6->sin6_addr.s6_addr, sizeof(struct in6_addr));
3360f66f451Sopenharmony_ci            rt->rtmsg_flags |= RTF_GATEWAY;
3370f66f451Sopenharmony_ci          } else perror_exit("resolving '%s'", argv[1]);
3380f66f451Sopenharmony_ci        } else help_exit(0);
3390f66f451Sopenharmony_ci      } else if (!strcmp(*argv, "dev")) {
3400f66f451Sopenharmony_ci        if (!*dev_name) *dev_name = argv[1];
3410f66f451Sopenharmony_ci      } else help_exit(0);
3420f66f451Sopenharmony_ci      argv++;
3430f66f451Sopenharmony_ci    }
3440f66f451Sopenharmony_ci  }
3450f66f451Sopenharmony_ci}
3460f66f451Sopenharmony_ci
3470f66f451Sopenharmony_ci// add/del a route.
3480f66f451Sopenharmony_cistatic void setroute_inet6(char **argv)
3490f66f451Sopenharmony_ci{
3500f66f451Sopenharmony_ci  struct sockaddr_in6 sock_in6;
3510f66f451Sopenharmony_ci  struct in6_rtmsg rt;
3520f66f451Sopenharmony_ci  char *targetip, *dev_name = 0;
3530f66f451Sopenharmony_ci  int sockfd, action = get_action(&argv, arglist1);
3540f66f451Sopenharmony_ci
3550f66f451Sopenharmony_ci  if (!action || !*argv) help_exit(0);
3560f66f451Sopenharmony_ci  memset(&sock_in6, 0, sizeof(struct sockaddr_in6));
3570f66f451Sopenharmony_ci  memset(&rt, 0, sizeof(struct in6_rtmsg));
3580f66f451Sopenharmony_ci  targetip = *argv++;
3590f66f451Sopenharmony_ci  if (!*argv) help_exit(0);
3600f66f451Sopenharmony_ci
3610f66f451Sopenharmony_ci  if (!strcmp(targetip, "default")) {
3620f66f451Sopenharmony_ci    rt.rtmsg_flags = RTF_UP;
3630f66f451Sopenharmony_ci    rt.rtmsg_dst_len = 0;
3640f66f451Sopenharmony_ci  } else {
3650f66f451Sopenharmony_ci    is_prefix_inet6((char **)&targetip, &rt);
3660f66f451Sopenharmony_ci    if (get_addrinfo(targetip, (struct sockaddr_in6 *) &sock_in6))
3670f66f451Sopenharmony_ci      perror_exit("resolving '%s'", targetip);
3680f66f451Sopenharmony_ci  }
3690f66f451Sopenharmony_ci  rt.rtmsg_metric = 1; //default metric.
3700f66f451Sopenharmony_ci  memcpy(&rt.rtmsg_dst, sock_in6.sin6_addr.s6_addr, sizeof(struct in6_addr));
3710f66f451Sopenharmony_ci  get_next_params_inet6(argv, &sock_in6, &rt, (char **)&dev_name);
3720f66f451Sopenharmony_ci
3730f66f451Sopenharmony_ci  sockfd = xsocket(AF_INET6, SOCK_DGRAM, 0);
3740f66f451Sopenharmony_ci  if (dev_name) {
3750f66f451Sopenharmony_ci    char ifre_buf[sizeof(struct ifreq)] = {0,};
3760f66f451Sopenharmony_ci    struct ifreq *ifre = (struct ifreq*)ifre_buf;
3770f66f451Sopenharmony_ci    xstrncpy(ifre->ifr_name, dev_name, IFNAMSIZ);
3780f66f451Sopenharmony_ci    xioctl(sockfd, SIOGIFINDEX, ifre);
3790f66f451Sopenharmony_ci    rt.rtmsg_ifindex = ifre->ifr_ifindex;
3800f66f451Sopenharmony_ci  }
3810f66f451Sopenharmony_ci  if (action == 1) xioctl(sockfd, SIOCADDRT, &rt);
3820f66f451Sopenharmony_ci  else xioctl(sockfd, SIOCDELRT, &rt);
3830f66f451Sopenharmony_ci  xclose(sockfd);
3840f66f451Sopenharmony_ci}
3850f66f451Sopenharmony_ci
3860f66f451Sopenharmony_ci/*
3870f66f451Sopenharmony_ci * format the dest and src address in ipv6 format.
3880f66f451Sopenharmony_ci * e.g. 2002:6b6d:26c8:d:ea03:9aff:fe65:9d62
3890f66f451Sopenharmony_ci */
3900f66f451Sopenharmony_cistatic void ipv6_addr_formating(char *ptr, char *addr)
3910f66f451Sopenharmony_ci{
3920f66f451Sopenharmony_ci  int i = 0;
3930f66f451Sopenharmony_ci  while (i <= IPV6_ADDR_LEN) {
3940f66f451Sopenharmony_ci    if (!*ptr) {
3950f66f451Sopenharmony_ci      if (i == IPV6_ADDR_LEN) {
3960f66f451Sopenharmony_ci        addr[IPV6_ADDR_LEN - 1] = 0; //NULL terminating the ':' separated address.
3970f66f451Sopenharmony_ci        break;
3980f66f451Sopenharmony_ci      }
3990f66f451Sopenharmony_ci      error_exit("IPv6 ip format error");
4000f66f451Sopenharmony_ci    }
4010f66f451Sopenharmony_ci    addr[i++] = *ptr++;
4020f66f451Sopenharmony_ci    if (!((i+1) % 5)) addr[i++] = ':'; //put ':' after 4th bit
4030f66f451Sopenharmony_ci  }
4040f66f451Sopenharmony_ci}
4050f66f451Sopenharmony_ci
4060f66f451Sopenharmony_cistatic void display_routes6(void)
4070f66f451Sopenharmony_ci{
4080f66f451Sopenharmony_ci  char iface[16] = {0,}, ipv6_dest_addr[41];
4090f66f451Sopenharmony_ci  char ipv6_src_addr[41], flag_val[10], buf2[INET6_ADDRSTRLEN];
4100f66f451Sopenharmony_ci  int prefixlen, metric, use, refcount, flag, items = 0;
4110f66f451Sopenharmony_ci  unsigned char buf[sizeof(struct in6_addr)];
4120f66f451Sopenharmony_ci
4130f66f451Sopenharmony_ci  FILE *fp = xfopen("/proc/net/ipv6_route", "r");
4140f66f451Sopenharmony_ci
4150f66f451Sopenharmony_ci  xprintf("Kernel IPv6 routing table\n"
4160f66f451Sopenharmony_ci      "%-43s%-40s Flags Metric Ref    Use Iface\n", "Destination", "Next Hop");
4170f66f451Sopenharmony_ci
4180f66f451Sopenharmony_ci  while ((items = fscanf(fp, "%32s%x%*s%*x%32s%x%x%x%x%15s\n", ipv6_dest_addr+8,
4190f66f451Sopenharmony_ci          &prefixlen, ipv6_src_addr+8, &metric, &use, &refcount, &flag,
4200f66f451Sopenharmony_ci          iface)) == 8)
4210f66f451Sopenharmony_ci  {
4220f66f451Sopenharmony_ci    if (!(flag & RTF_UP)) continue; //skip down interfaces.
4230f66f451Sopenharmony_ci
4240f66f451Sopenharmony_ci    //ipv6_dest_addr+8: as the values are filled from the 8th location of the array.
4250f66f451Sopenharmony_ci    ipv6_addr_formating(ipv6_dest_addr+8, ipv6_dest_addr);
4260f66f451Sopenharmony_ci    ipv6_addr_formating(ipv6_src_addr+8, ipv6_src_addr);
4270f66f451Sopenharmony_ci
4280f66f451Sopenharmony_ci    get_flag_value(flag_val, flag);
4290f66f451Sopenharmony_ci    if (inet_pton(AF_INET6, ipv6_dest_addr, buf) <= 0) perror_exit("inet");
4300f66f451Sopenharmony_ci    if (inet_ntop(AF_INET6, buf, buf2, INET6_ADDRSTRLEN))
4310f66f451Sopenharmony_ci      sprintf(toybuf, "%s/%d", buf2, prefixlen);
4320f66f451Sopenharmony_ci
4330f66f451Sopenharmony_ci    if (inet_pton(AF_INET6, ipv6_src_addr, buf) <= 0) perror_exit("inet");
4340f66f451Sopenharmony_ci    if (inet_ntop(AF_INET6, buf, buf2, INET6_ADDRSTRLEN))
4350f66f451Sopenharmony_ci      xprintf("%-43s %-39s %-5s %-6d %-4d %5d %-8s\n",
4360f66f451Sopenharmony_ci          toybuf, buf2, flag_val, metric, refcount, use, iface);
4370f66f451Sopenharmony_ci  }
4380f66f451Sopenharmony_ci  if ((items > 0) && feof(fp)) perror_exit("fscanf");
4390f66f451Sopenharmony_ci
4400f66f451Sopenharmony_ci  fclose(fp);
4410f66f451Sopenharmony_ci}
4420f66f451Sopenharmony_ci
4430f66f451Sopenharmony_civoid route_main(void)
4440f66f451Sopenharmony_ci{
4450f66f451Sopenharmony_ci  if (!TT.family) TT.family = "inet";
4460f66f451Sopenharmony_ci  if (!*toys.optargs) {
4470f66f451Sopenharmony_ci    if (!strcmp(TT.family, "inet")) display_routes();
4480f66f451Sopenharmony_ci    else if (!strcmp(TT.family, "inet6")) display_routes6();
4490f66f451Sopenharmony_ci    else help_exit(0);
4500f66f451Sopenharmony_ci  } else {
4510f66f451Sopenharmony_ci    if (!strcmp(TT.family, "inet6")) setroute_inet6(toys.optargs);
4520f66f451Sopenharmony_ci    else setroute(toys.optargs);
4530f66f451Sopenharmony_ci  }
4540f66f451Sopenharmony_ci}
455