1 /* route.c - Display routing table. 2 * 3 * Copyright 2012 Ranjan Kumar <ranjankumar.bth (at) gmail.com> 4 * Copyright 2013 Kyungwan Han <asura321 (at) gmail.com> 5 * 6 * No Standard 7 * 8 USE_ROUTE(NEWTOY(route, "?neA:", TOYFLAG_BIN)) 9 config ROUTE 10 bool "route" 11 default n 12 help 13 usage: route -neA inet{6} / [{add|del}] 14 15 Display/Edit kernel routing tables. 16 17 -n Don't resolve names 18 -e Display other/more information 19 -A inet{6} Select Address Family 20 */ 21 22 #define FOR_route 23 #include "toys.h" 24 #include <net/route.h> 25 #include <sys/param.h> 26 27 GLOBALS( 28 char *family; 29 ) 30 31 #define DEFAULT_PREFIXLEN 128 32 #define INVALID_ADDR 0xffffffffUL 33 #define IPV6_ADDR_LEN 40 //32 + 7 (':') + 1 ('\0') 34 35 #define TEST_ARGV(argv) if (!*argv) show_route_help() 36 37 struct _arglist { 38 char *arg; 39 int action; 40 }; 41 42 static struct _arglist arglist1[] = { 43 { "add", 1 }, { "del", 2 }, 44 { "delete", 2 }, { NULL, 0 } 45 }; 46 47 static struct _arglist arglist2[] = { 48 { "-net", 1 }, { "-host", 2 }, 49 { NULL, 0 } 50 }; 51 52 // display help info and exit. 53 static void show_route_help(void) 54 { 55 toys.exithelp = 1; 56 error_exit("Invalid Argument"); 57 } 58 59 // to get the host name from the given ip. 60 static int get_hostname(char *ipstr, struct sockaddr_in *sockin) 61 { 62 struct hostent *host; 63 64 sockin->sin_family = AF_INET; 65 sockin->sin_port = 0; 66 67 if (!strcmp(ipstr, "default")) { 68 sockin->sin_addr.s_addr = INADDR_ANY; 69 return 1; 70 } 71 72 if (inet_aton(ipstr, &sockin->sin_addr)) return 0; 73 if (!(host = gethostbyname(ipstr))) return -1; 74 memcpy(&sockin->sin_addr, host->h_addr_list[0], sizeof(struct in_addr)); 75 return 0; 76 } 77 78 // used to extract the address info from the given ip. 79 static int get_addrinfo(char *ip, struct sockaddr_in6 *sock_in6) 80 { 81 struct addrinfo hints, *result; 82 int status = 0; 83 84 memset(&hints, 0, sizeof(struct addrinfo)); 85 hints.ai_family = AF_INET6; 86 if ((status = getaddrinfo(ip, NULL, &hints, &result))) { 87 perror_msg("getaddrinfo: %s", gai_strerror(status)); 88 return -1; 89 } 90 if (result) { 91 memcpy(sock_in6, result->ai_addr, sizeof(*sock_in6)); 92 freeaddrinfo(result); 93 } 94 return 0; 95 } 96 97 //get the flag values for route command. 98 static void get_flag_value(char *str, int flags) 99 { 100 int i = 0; 101 static unsigned flagarray[] = { 102 RTF_GATEWAY, RTF_HOST, RTF_REINSTATE, RTF_DYNAMIC, 103 RTF_MODIFIED, RTF_DEFAULT, RTF_ADDRCONF, RTF_CACHE 104 }; 105 106 *str++ = 'U'; 107 while ((*str = "GHRDMDAC"[i])) 108 if (flags & flagarray[i++]) ++str; 109 } 110 111 // extract inet4 route info from /proc/net/route file and display it. 112 static void display_routes(void) 113 { 114 unsigned long dest, gate, mask; 115 int flags, ref, use, metric, mss, win, irtt, items; 116 char iface[64] = {0,}, flag_val[10]; //there are 9 flags "UGHRDMDAC" for route. 117 118 FILE *fp = xfopen("/proc/net/route", "r"); 119 120 xprintf("Kernel IP routing table\n" 121 "Destination Gateway Genmask Flags %s Iface\n", 122 (toys.optflags & FLAG_e)? " MSS Window irtt" : "Metric Ref Use"); 123 124 if (fscanf(fp, "%*[^\n]\n") < 0) perror_exit("fscanf"); //skip 1st line 125 while ((items = fscanf(fp, "%63s%lx%lx%X%d%d%d%lx%d%d%d\n", iface, &dest, 126 &gate, &flags, &ref, &use, &metric, &mask, &mss, &win, &irtt)) == 11) { 127 128 char *destip = toybuf, *gateip = toybuf+32, *maskip = toybuf+64; //ip string 16 129 memset(flag_val, 0, 10); 130 131 if (!(flags & RTF_UP)) continue; //skip down interfaces. 132 133 if (!dest && !(toys.optflags & FLAG_n)) strcpy( destip, "default"); 134 else if (!inet_ntop(AF_INET, &dest, destip, 32)) perror_exit("inet"); 135 136 if (!gate && !(toys.optflags & FLAG_n)) strcpy( gateip, "*"); 137 else if (!inet_ntop(AF_INET, &gate, gateip, 32)) perror_exit("inet"); 138 139 if (!inet_ntop(AF_INET, &mask, maskip, 32)) perror_exit("inet"); 140 141 //Get flag Values 142 get_flag_value(flag_val, (flags & (RTF_GATEWAY|RTF_HOST|RTF_REINSTATE 143 |RTF_DYNAMIC|RTF_MODIFIED))); 144 if (flags & RTF_REJECT) flag_val[0] = '!'; 145 xprintf("%-15.15s %-15.15s %-16s%-6s", destip, gateip, maskip, flag_val); 146 if (toys.optflags & FLAG_e) xprintf("%5d %-5d %6d %s\n", mss, win, irtt, iface); 147 else xprintf("%-6d %-2d %7d %s\n", metric, ref, use, iface); 148 } 149 150 if (items > 0 && feof(fp)) perror_exit("fscanf %d", items); 151 fclose(fp); 152 } 153 154 155 /* 156 * find the given parameter in list like add/del/net/host. 157 * and if match found return the appropriate action. 158 */ 159 static int get_action(char ***argv, struct _arglist *list) 160 { 161 struct _arglist *alist; 162 163 if (!**argv) return 0; 164 for (alist = list; alist->arg; alist++) { //find the given parameter in list 165 if (!strcmp(**argv, alist->arg)) { 166 *argv += 1; 167 return alist->action; 168 } 169 } 170 return 0; 171 } 172 173 /* 174 * get prefix len (if any) and remove the prefix from target ip. 175 * if no prefix then set netmask as default. 176 */ 177 static void is_prefix(char **tip, char **netmask, struct rtentry *rt) 178 { 179 char *prefix = strchr(*tip, '/'); 180 if (prefix) { 181 unsigned long plen; 182 plen = atolx_range(prefix + 1, 0, 32); 183 //used to verify the netmask and route conflict. 184 (((struct sockaddr_in *)&((rt)->rt_genmask))->sin_addr.s_addr) 185 = htonl( ~(INVALID_ADDR >> plen)); 186 *prefix = '\0'; 187 rt->rt_genmask.sa_family = AF_INET; 188 } else *netmask = "default"; //default netmask. 189 } 190 191 /* 192 * used to get the params like: metric, netmask, gw, mss, window, irtt, dev and their values. 193 * additionally set the flag values for reject, mod, dyn and reinstate. 194 */ 195 static void get_next_params(char **argv, struct rtentry *rt, char **netmask) 196 { 197 while (*argv) { 198 //set the metric field in the routing table. 199 if (!strcmp(*argv, "metric")) { 200 argv++; 201 TEST_ARGV(argv); 202 rt->rt_metric = atolx_range(*argv, 0, ULONG_MAX) + 1; 203 argv++; 204 } else if (!strcmp(*argv, "netmask")) { 205 //when adding a network route, the netmask to be used. 206 struct sockaddr sock; 207 unsigned int addr_mask = (((struct sockaddr_in *)&((rt)->rt_genmask))->sin_addr.s_addr); 208 if (addr_mask) show_route_help(); 209 argv++; 210 TEST_ARGV(argv); 211 *netmask = *argv; 212 if (get_hostname(*netmask, (struct sockaddr_in *) &sock) < 0) 213 perror_exit("resolving '%s'", *netmask); 214 rt->rt_genmask = sock; 215 argv++; 216 } else if (!strcmp(*argv, "gw")) { 217 //route packets via a gateway. 218 if (!(rt->rt_flags & RTF_GATEWAY)) { 219 int ishost; 220 argv++; 221 TEST_ARGV(argv); 222 if ((ishost = get_hostname(*argv, (struct sockaddr_in *) &rt->rt_gateway)) == 0) { 223 rt->rt_flags |= RTF_GATEWAY; 224 argv++; 225 } else if (ishost < 0) perror_exit("resolving '%s'", *argv); 226 else perror_exit("gateway '%s' is a NETWORK", *argv); 227 } else show_route_help(); 228 } else if (!strcmp(*argv, "mss")) { 229 //set the TCP Maximum Segment Size for connections over this route. 230 argv++; 231 TEST_ARGV(argv); 232 rt->rt_mss = atolx_range(*argv, 64, 32768); //MSS low and max 233 rt->rt_flags |= RTF_MSS; 234 argv++; 235 } else if (!strcmp(*argv, "window")) { 236 //set the TCP window size for connections over this route to W bytes. 237 argv++; 238 TEST_ARGV(argv); 239 rt->rt_window = atolx_range(*argv, 128, INT_MAX); //win low 240 rt->rt_flags |= RTF_WINDOW; 241 argv++; 242 } else if (!strcmp(*argv, "irtt")) { 243 long nclock_ticks = sysconf(_SC_CLK_TCK); //number of clock ticks per second. 244 argv++; 245 TEST_ARGV(argv); 246 nclock_ticks /= 100; 247 rt->rt_irtt = strtoul(*argv, NULL, 10); 248 if (nclock_ticks > 0) rt->rt_irtt *= nclock_ticks; 249 rt->rt_flags |= 0x0100; //RTF_IRTT 250 argv++; 251 } else if (!strcmp(*argv, "dev")) { 252 argv++; 253 TEST_ARGV(argv); 254 if ((!rt->rt_dev)) rt->rt_dev = *argv; 255 argv++; 256 } else if (!strcmp(*argv, "reject")) { 257 rt->rt_flags |= RTF_REJECT; 258 argv++; 259 } else if (!strcmp(*argv, "mod")) { 260 rt->rt_flags |= RTF_MODIFIED; 261 argv++; 262 } else if (!strcmp(*argv, "dyn")) { 263 rt->rt_flags |= RTF_DYNAMIC; 264 argv++; 265 } else if (!strcmp(*argv, "reinstate")) { 266 rt->rt_flags |= RTF_REINSTATE; 267 argv++; 268 } else show_route_help(); //No match found; exit form the application. 269 }//end of while loop. 270 271 if (!rt->rt_dev && (rt->rt_flags & RTF_REJECT)) rt->rt_dev = (char *)"lo"; 272 } 273 274 // verify the netmask and conflict in netmask and route address. 275 static void verify_netmask(struct rtentry *rt, char *netmask) 276 { 277 unsigned int addr_mask = (((struct sockaddr_in *)&((rt)->rt_genmask))->sin_addr.s_addr); 278 unsigned int router_addr = ~(unsigned int)(((struct sockaddr_in *)&((rt)->rt_dst))->sin_addr.s_addr); 279 280 if (addr_mask) { 281 addr_mask = ~ntohl(addr_mask); 282 if ((rt->rt_flags & RTF_HOST) && addr_mask != INVALID_ADDR) 283 perror_exit("conflicting netmask and host route"); 284 if (addr_mask & (addr_mask + 1)) perror_exit("wrong netmask '%s'", netmask); 285 addr_mask = ((struct sockaddr_in *) &rt->rt_dst)->sin_addr.s_addr; 286 if (addr_mask & router_addr) perror_exit("conflicting netmask and route address"); 287 } 288 } 289 290 // add/del a route. 291 static void setroute(char **argv) 292 { 293 struct rtentry rt; 294 char *netmask = NULL, *targetip; 295 int is_net_or_host = 0, sokfd, arg2_action; 296 int action = get_action(&argv, arglist1); //verify the arg for add/del. 297 298 if (!action || !*argv) show_route_help(); 299 300 arg2_action = get_action(&argv, arglist2); //verify the arg for -net or -host 301 if (!*argv) show_route_help(); 302 303 memset(&rt, 0, sizeof(struct rtentry)); 304 targetip = *argv++; 305 306 is_prefix((char **)&targetip, (char **)&netmask, &rt); 307 if ((is_net_or_host = get_hostname(targetip, 308 (struct sockaddr_in *) &rt.rt_dst)) < 0) 309 perror_exit("resolving '%s'", targetip); 310 311 if (arg2_action) is_net_or_host = arg2_action & 1; 312 rt.rt_flags = ((is_net_or_host) ? RTF_UP : (RTF_UP | RTF_HOST)); 313 314 get_next_params(argv, &rt, (char **)&netmask); 315 verify_netmask(&rt, (char *)netmask); 316 317 if ((action == 1) && (rt.rt_flags & RTF_HOST)) 318 (((struct sockaddr_in *)&((rt).rt_genmask))->sin_addr.s_addr) = INVALID_ADDR; 319 320 sokfd = xsocket(AF_INET, SOCK_DGRAM, 0); 321 if (action == 1) xioctl(sokfd, SIOCADDRT, &rt); 322 else xioctl(sokfd, SIOCDELRT, &rt); 323 xclose(sokfd); 324 } 325 326 /* 327 * get prefix len (if any) and remove the prefix from target ip. 328 * if no prefix then set default prefix len. 329 */ 330 static void is_prefix_inet6(char **tip, struct in6_rtmsg *rt) 331 { 332 unsigned long plen; 333 char *prefix = strchr(*tip, '/'); 334 335 if (prefix) { 336 *prefix = '\0'; 337 plen = atolx_range(prefix + 1, 0, 128); //DEFAULT_PREFIXLEN); 338 } else plen = DEFAULT_PREFIXLEN; 339 340 rt->rtmsg_flags = (plen == DEFAULT_PREFIXLEN) ? (RTF_UP | RTF_HOST) : RTF_UP; 341 rt->rtmsg_dst_len = plen; 342 } 343 344 /* 345 * used to get the params like: metric, gw, dev and their values. 346 * additionally set the flag values for mod and dyn. 347 */ 348 static void get_next_params_inet6(char **argv, struct sockaddr_in6 *sock_in6, struct in6_rtmsg *rt, char **dev_name) 349 { 350 while (*argv) { 351 if (!strcmp(*argv, "metric")) { 352 //set the metric field in the routing table. 353 argv++; 354 TEST_ARGV(argv); 355 rt->rtmsg_metric = atolx_range(*argv, 0, ULONG_MAX); 356 argv++; 357 } else if (!strcmp(*argv, "gw")) { 358 //route packets via a gateway. 359 if (!(rt->rtmsg_flags & RTF_GATEWAY)) { 360 argv++; 361 TEST_ARGV(argv); 362 if (!get_addrinfo(*argv, (struct sockaddr_in6 *) &sock_in6)) { 363 memcpy(&rt->rtmsg_gateway, sock_in6->sin6_addr.s6_addr, sizeof(struct in6_addr)); 364 rt->rtmsg_flags |= RTF_GATEWAY; 365 argv++; 366 } else perror_exit("resolving '%s'", *argv); 367 } else show_route_help(); 368 } else if (!strcmp(*argv, "dev")) { 369 argv++; 370 TEST_ARGV(argv); 371 if (!*dev_name) *dev_name = *argv; 372 argv++; 373 } else if (!strcmp(*argv, "mod")) { 374 rt->rtmsg_flags |= RTF_MODIFIED; 375 argv++; 376 } else if (!strcmp(*argv, "dyn")) { 377 rt->rtmsg_flags |= RTF_DYNAMIC; 378 argv++; 379 } else show_route_help(); 380 }//end of while loop. 381 } 382 383 // add/del a route. 384 static void setroute_inet6(char **argv) 385 { 386 struct sockaddr_in6 sock_in6; 387 struct in6_rtmsg rt; 388 char *targetip; 389 int sockfd, action = get_action(&argv, arglist1); 390 391 if (!action || !*argv) show_route_help(); 392 memset(&sock_in6, 0, sizeof(struct sockaddr_in6)); 393 memset(&rt, 0, sizeof(struct in6_rtmsg)); 394 targetip = *argv++; 395 if (*argv) { 396 unsigned long plen = 0; 397 char *dev_name = NULL; 398 if (!strcmp(targetip, "default")) { 399 rt.rtmsg_flags = RTF_UP; 400 rt.rtmsg_dst_len = plen; 401 } else { 402 is_prefix_inet6((char **)&targetip, &rt); 403 if (get_addrinfo(targetip, (struct sockaddr_in6 *) &sock_in6)) 404 perror_exit("resolving '%s'", targetip); 405 } 406 rt.rtmsg_metric = 1; //default metric. 407 memcpy(&rt.rtmsg_dst, sock_in6.sin6_addr.s6_addr, sizeof(struct in6_addr)); 408 get_next_params_inet6(argv, &sock_in6, &rt, (char **)&dev_name); 409 410 sockfd = xsocket(AF_INET6, SOCK_DGRAM, 0); 411 if (dev_name) { 412 char ifre_buf[sizeof(struct ifreq)] = {0,}; 413 struct ifreq *ifre = (struct ifreq*)ifre_buf; 414 xstrncpy(ifre->ifr_name, dev_name, IFNAMSIZ); 415 xioctl(sockfd, SIOGIFINDEX, ifre); 416 rt.rtmsg_ifindex = ifre->ifr_ifindex; 417 } 418 if (action == 1) xioctl(sockfd, SIOCADDRT, &rt); 419 else xioctl(sockfd, SIOCDELRT, &rt); 420 xclose(sockfd); 421 } else show_route_help(); 422 } 423 424 /* 425 * format the dest and src address in ipv6 format. 426 * e.g. 2002:6b6d:26c8:d:ea03:9aff:fe65:9d62 427 */ 428 static void ipv6_addr_formating(char *ptr, char *addr) 429 { 430 int i = 0; 431 while (i <= IPV6_ADDR_LEN) { 432 if (!*ptr) { 433 if (i == IPV6_ADDR_LEN) { 434 addr[IPV6_ADDR_LEN - 1] = 0; //NULL terminating the ':' seperated address. 435 break; 436 } 437 error_exit("IPv6 ip format error"); 438 } 439 addr[i++] = *ptr++; 440 if (!((i+1) % 5)) addr[i++] = ':'; //put ':' after 4th bit 441 } 442 } 443 444 static void display_routes6(void) 445 { 446 char iface[16] = {0,}, ipv6_dest_addr[41]; 447 char ipv6_src_addr[41], flag_val[10], buf2[INET6_ADDRSTRLEN]; 448 int prefixlen, metric, use, refcount, flag, items = 0; 449 unsigned char buf[sizeof(struct in6_addr)]; 450 451 FILE *fp = xfopen("/proc/net/ipv6_route", "r"); 452 453 xprintf("Kernel IPv6 routing table\n" 454 "%-43s%-40s Flags Metric Ref Use Iface\n", "Destination", "Next Hop"); 455 456 while ((items = fscanf(fp, "%32s%x%*s%*x%32s%x%x%x%x%10s\n", ipv6_dest_addr+8, 457 &prefixlen, ipv6_src_addr+8, &metric, &use, &refcount, &flag, 458 iface)) == 8) { 459 460 memset(flag_val, 0, 10); 461 462 if (!(flag & RTF_UP)) continue; //skip down interfaces. 463 464 //ipv6_dest_addr+8: as the values are filled from the 8th location of the array. 465 ipv6_addr_formating(ipv6_dest_addr+8, ipv6_dest_addr); 466 ipv6_addr_formating(ipv6_src_addr+8, ipv6_src_addr); 467 468 get_flag_value(flag_val, (flag & (RTF_GATEWAY|RTF_HOST|RTF_DEFAULT| 469 RTF_ADDRCONF|RTF_CACHE))); 470 if (inet_pton(AF_INET6, ipv6_dest_addr, buf) <= 0) perror_exit("inet"); 471 if (inet_ntop(AF_INET6, buf, buf2, INET6_ADDRSTRLEN)) { 472 sprintf(toybuf, "%s/%d", buf2, prefixlen); 473 } 474 475 if (inet_pton(AF_INET6, ipv6_src_addr, buf) <= 0) perror_exit("inet"); 476 if (inet_ntop(AF_INET6, buf, buf2, INET6_ADDRSTRLEN)) { 477 xprintf("%-43s %-39s %-5s %-6d %-4d %5d %-8s\n", 478 toybuf, buf2, flag_val, metric, refcount, use, iface); 479 } 480 } 481 if ((items > 0) && feof(fp)) perror_exit("fscanf"); 482 483 fclose(fp); 484 } 485 486 void route_main(void) 487 { 488 if (!(toys.optflags & FLAG_A)) TT.family = "inet"; 489 if (!*toys.optargs) { 490 if (!strcmp(TT.family, "inet")) display_routes(); 491 else if (!strcmp(TT.family, "inet6")) display_routes6(); 492 else show_route_help(); 493 return; 494 }//End of if statement. 495 496 if (!strcmp(TT.family, "inet6")) setroute_inet6(toys.optargs); 497 else setroute(toys.optargs); 498 } 499