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
15 USE_ROUTE(NEWTOY(route, "?neA:", TOYFLAG_BIN))
16 config 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
53 GLOBALS(
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
61 struct _arglist {
62 char *arg;
63
64 int action;
65 };
66
67 static struct _arglist arglist1[] = {
68 { "add", 1 }, { "del", 2 },
69 { "delete", 2 }, { NULL, 0 }
70 };
71
72 static struct _arglist arglist2[] = {
73 { "-net", 1 }, { "-host", 2 },
74 { NULL, 0 }
75 };
76
77 // to get the host name from the given ip.
get_hostname(char * ipstr,struct sockaddr_in * sockin)78 static 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.
get_addrinfo(char * ip,struct sockaddr_in6 * sock_in6)98 static 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
get_flag_value(char * str,int flags)116 static 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.
display_routes(void)130 static 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 */
get_action(char *** argv,struct _arglist * list)174 static 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 */
get_next_params(char ** argv,struct rtentry * rt,char ** netmask)192 static 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[1], 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[1];
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[1], (struct sockaddr_in *) &rt->rt_gateway))
218 rt->rt_flags |= RTF_GATEWAY;
219 else perror_exit("gateway '%s' is a NETWORK", argv[1]);
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[1], 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[1], 128, INT_MAX); //win low
228 rt->rt_flags |= RTF_WINDOW;
229 } else if (!strcmp(*argv, "irtt")) {
230 rt->rt_irtt = atolx_range(argv[1], 0, INT_MAX);
231 rt->rt_flags |= RTF_IRTT;
232 } else if (!strcmp(*argv, "dev") && !rt->rt_dev) rt->rt_dev = argv[1];
233 else help_exit("no '%s'", *argv);
234 argv++;
235 }
236 }
237
238 if (!rt->rt_dev && (rt->rt_flags & RTF_REJECT)) rt->rt_dev = (char *)"lo";
239 }
240
241 // verify the netmask and conflict in netmask and route address.
verify_netmask(struct rtentry * rt,char * netmask)242 static void verify_netmask(struct rtentry *rt, char *netmask)
243 {
244 unsigned int addr_mask = (((struct sockaddr_in *)&((rt)->rt_genmask))->sin_addr.s_addr);
245 unsigned int router_addr = ~(unsigned int)(((struct sockaddr_in *)&((rt)->rt_dst))->sin_addr.s_addr);
246
247 if (addr_mask) {
248 addr_mask = ~ntohl(addr_mask);
249 if ((rt->rt_flags & RTF_HOST) && addr_mask != INVALID_ADDR)
250 perror_exit("conflicting netmask and host route");
251 if (addr_mask & (addr_mask + 1)) perror_exit("wrong netmask '%s'", netmask);
252 addr_mask = ((struct sockaddr_in *) &rt->rt_dst)->sin_addr.s_addr;
253 if (addr_mask & router_addr) perror_exit("conflicting netmask and route address");
254 }
255 }
256
257 // add/del a route.
setroute(char ** argv)258 static void setroute(char **argv)
259 {
260 struct rtentry rt;
261 char *netmask, *targetip;
262 int is_net_or_host = 0, sokfd, arg2_action;
263 int action = get_action(&argv, arglist1); //verify the arg for add/del.
264
265 if (!action || !*argv) help_exit("setroute");
266
267 arg2_action = get_action(&argv, arglist2); //verify the arg for -net or -host
268 if (!*argv) help_exit("setroute");
269
270 memset(&rt, 0, sizeof(struct rtentry));
271 targetip = *argv++;
272
273 netmask = strchr(targetip, '/');
274 if (netmask) {
275 *netmask++ = 0;
276 //used to verify the netmask and route conflict.
277 (((struct sockaddr_in *)&rt.rt_genmask)->sin_addr.s_addr)
278 = htonl((1<<(32-atolx_range(netmask, 0, 32)))-1);
279 rt.rt_genmask.sa_family = AF_INET;
280 netmask = 0;
281 } else netmask = "default";
282
283 is_net_or_host = get_hostname(targetip, (void *)&rt.rt_dst);
284
285 if (arg2_action) is_net_or_host = arg2_action & 1;
286 rt.rt_flags = ((is_net_or_host) ? RTF_UP : (RTF_UP | RTF_HOST));
287
288 get_next_params(argv, &rt, (char **)&netmask);
289 verify_netmask(&rt, (char *)netmask);
290
291 if ((action == 1) && (rt.rt_flags & RTF_HOST))
292 (((struct sockaddr_in *)&((rt).rt_genmask))->sin_addr.s_addr) = INVALID_ADDR;
293
294 sokfd = xsocket(AF_INET, SOCK_DGRAM, 0);
295 if (action == 1) xioctl(sokfd, SIOCADDRT, &rt);
296 else xioctl(sokfd, SIOCDELRT, &rt);
297 xclose(sokfd);
298 }
299
300 /*
301 * get prefix len (if any) and remove the prefix from target ip.
302 * if no prefix then set default prefix len.
303 */
is_prefix_inet6(char ** tip,struct in6_rtmsg * rt)304 static void is_prefix_inet6(char **tip, struct in6_rtmsg *rt)
305 {
306 unsigned long plen;
307 char *prefix = strchr(*tip, '/');
308
309 if (prefix) {
310 *prefix = '\0';
311 plen = atolx_range(prefix + 1, 0, 128); //DEFAULT_PREFIXLEN);
312 } else plen = DEFAULT_PREFIXLEN;
313
314 rt->rtmsg_flags = (plen == DEFAULT_PREFIXLEN) ? (RTF_UP | RTF_HOST) : RTF_UP;
315 rt->rtmsg_dst_len = plen;
316 }
317
318 /*
319 * used to get the params like: metric, gw, dev and their values.
320 * additionally set the flag values for mod and dyn.
321 */
get_next_params_inet6(char ** argv,struct sockaddr_in6 * sock_in6,struct in6_rtmsg * rt,char ** dev_name)322 static void get_next_params_inet6(char **argv, struct sockaddr_in6 *sock_in6, struct in6_rtmsg *rt, char **dev_name)
323 {
324 for (;*argv;argv++) {
325 if (!strcmp(*argv, "mod")) rt->rtmsg_flags |= RTF_MODIFIED;
326 else if (!strcmp(*argv, "dyn")) rt->rtmsg_flags |= RTF_DYNAMIC;
327 else {
328 if (!argv[1]) help_exit(0);
329
330 if (!strcmp(*argv, "metric"))
331 rt->rtmsg_metric = atolx_range(argv[1], 0, ULONG_MAX);
332 else if (!strcmp(*argv, "gw")) {
333 //route packets via a gateway.
334 if (!(rt->rtmsg_flags & RTF_GATEWAY)) {
335 if (!get_addrinfo(argv[1], (struct sockaddr_in6 *) &sock_in6)) {
336 memcpy(&rt->rtmsg_gateway, sock_in6->sin6_addr.s6_addr, sizeof(struct in6_addr));
337 rt->rtmsg_flags |= RTF_GATEWAY;
338 } else perror_exit("resolving '%s'", argv[1]);
339 } else help_exit(0);
340 } else if (!strcmp(*argv, "dev")) {
341 if (!*dev_name) *dev_name = argv[1];
342 } else help_exit(0);
343 argv++;
344 }
345 }
346 }
347
348 // add/del a route.
setroute_inet6(char ** argv)349 static void setroute_inet6(char **argv)
350 {
351 struct sockaddr_in6 sock_in6;
352 struct in6_rtmsg rt;
353 char *targetip, *dev_name = 0;
354 int sockfd, action = get_action(&argv, arglist1);
355
356 if (!action || !*argv) help_exit(0);
357 memset(&sock_in6, 0, sizeof(struct sockaddr_in6));
358 memset(&rt, 0, sizeof(struct in6_rtmsg));
359 targetip = *argv++;
360 if (!*argv) help_exit(0);
361
362 if (!strcmp(targetip, "default")) {
363 rt.rtmsg_flags = RTF_UP;
364 rt.rtmsg_dst_len = 0;
365 } else {
366 is_prefix_inet6((char **)&targetip, &rt);
367 if (get_addrinfo(targetip, (struct sockaddr_in6 *) &sock_in6))
368 perror_exit("resolving '%s'", targetip);
369 }
370 rt.rtmsg_metric = 1; //default metric.
371 memcpy(&rt.rtmsg_dst, sock_in6.sin6_addr.s6_addr, sizeof(struct in6_addr));
372 get_next_params_inet6(argv, &sock_in6, &rt, (char **)&dev_name);
373
374 sockfd = xsocket(AF_INET6, SOCK_DGRAM, 0);
375 if (dev_name) {
376 char ifre_buf[sizeof(struct ifreq)] = {0,};
377 struct ifreq *ifre = (struct ifreq*)ifre_buf;
378 xstrncpy(ifre->ifr_name, dev_name, IFNAMSIZ);
379 xioctl(sockfd, SIOGIFINDEX, ifre);
380 rt.rtmsg_ifindex = ifre->ifr_ifindex;
381 }
382 if (action == 1) xioctl(sockfd, SIOCADDRT, &rt);
383 else xioctl(sockfd, SIOCDELRT, &rt);
384 xclose(sockfd);
385 }
386
387 /*
388 * format the dest and src address in ipv6 format.
389 * e.g. 2002:6b6d:26c8:d:ea03:9aff:fe65:9d62
390 */
ipv6_addr_formating(char * ptr,char * addr)391 static void ipv6_addr_formating(char *ptr, char *addr)
392 {
393 int i = 0;
394 while (i <= IPV6_ADDR_LEN) {
395 if (!*ptr) {
396 if (i == IPV6_ADDR_LEN) {
397 addr[IPV6_ADDR_LEN - 1] = 0; //NULL terminating the ':' separated address.
398 break;
399 }
400 error_exit("IPv6 ip format error");
401 }
402 addr[i++] = *ptr++;
403 if (!((i+1) % 5)) addr[i++] = ':'; //put ':' after 4th bit
404 }
405 }
406
display_routes6(void)407 static void display_routes6(void)
408 {
409 char iface[16] = {0,}, ipv6_dest_addr[41];
410 char ipv6_src_addr[41], flag_val[10], buf2[INET6_ADDRSTRLEN];
411 int prefixlen, metric, use, refcount, flag, items = 0;
412 unsigned char buf[sizeof(struct in6_addr)];
413
414 FILE *fp = xfopen("/proc/net/ipv6_route", "r");
415
416 xprintf("Kernel IPv6 routing table\n"
417 "%-43s%-40s Flags Metric Ref Use Iface\n", "Destination", "Next Hop");
418
419 while ((items = fscanf(fp, "%32s%x%*s%*x%32s%x%x%x%x%15s\n", ipv6_dest_addr+8,
420 &prefixlen, ipv6_src_addr+8, &metric, &use, &refcount, &flag,
421 iface)) == 8)
422 {
423 if (!(flag & RTF_UP)) continue; //skip down interfaces.
424
425 //ipv6_dest_addr+8: as the values are filled from the 8th location of the array.
426 ipv6_addr_formating(ipv6_dest_addr+8, ipv6_dest_addr);
427 ipv6_addr_formating(ipv6_src_addr+8, ipv6_src_addr);
428
429 get_flag_value(flag_val, flag);
430 if (inet_pton(AF_INET6, ipv6_dest_addr, buf) <= 0) perror_exit("inet");
431 if (inet_ntop(AF_INET6, buf, buf2, INET6_ADDRSTRLEN))
432 sprintf(toybuf, "%s/%d", buf2, prefixlen);
433
434 if (inet_pton(AF_INET6, ipv6_src_addr, buf) <= 0) perror_exit("inet");
435 if (inet_ntop(AF_INET6, buf, buf2, INET6_ADDRSTRLEN))
436 xprintf("%-43s %-39s %-5s %-6d %-4d %5d %-8s\n",
437 toybuf, buf2, flag_val, metric, refcount, use, iface);
438 }
439 if ((items > 0) && feof(fp)) perror_exit("fscanf");
440
441 fclose(fp);
442 }
443
route_main(void)444 void route_main(void)
445 {
446 if (!TT.family) TT.family = "inet";
447 if (!*toys.optargs) {
448 if (!strcmp(TT.family, "inet")) display_routes();
449 else if (!strcmp(TT.family, "inet6")) display_routes6();
450 else help_exit(0);
451 } else {
452 if (!strcmp(TT.family, "inet6")) setroute_inet6(toys.optargs);
453 else setroute(toys.optargs);
454 }
455 }
456