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