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 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.
get_hostname(char * ipstr,struct sockaddr_in * sockin)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.
get_addrinfo(char * ip,struct sockaddr_in6 * sock_in6)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
get_flag_value(char * str,int flags)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.
display_routes(void)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 */
get_action(char *** argv,struct _arglist * list)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 */
is_prefix(char ** tip,char ** netmask,struct rtentry * rt)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 */
get_next_params(char ** argv,struct rtentry * rt,char ** netmask)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.
verify_netmask(struct rtentry * rt,char * netmask)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.
setroute(char ** argv)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 */
is_prefix_inet6(char ** tip,struct in6_rtmsg * rt)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 */
get_next_params_inet6(char ** argv,struct sockaddr_in6 * sock_in6,struct in6_rtmsg * rt,char ** dev_name)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.
setroute_inet6(char ** argv)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 */
ipv6_addr_formating(char * ptr,char * addr)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
display_routes6(void)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
route_main(void)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