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