1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
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
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
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
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
119
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
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];
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");
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;
147
148 if (!(flags & RTF_UP)) continue;
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
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
172
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++) {
180 if (!strcmp(**argv, alist->arg)) {
181 *argv += 1;
182 return alist->action;
183 }
184 }
185 return 0;
186}
187
188
189
190
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
203 if (!strcmp(*argv, "metric"))
204 rt->rt_metric = atolx_range(argv[1], 0, ULONG_MAX) + 1;
205 else if (!strcmp(*argv, "netmask")) {
206
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
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
223 rt->rt_mtu = atolx_range(argv[1], 64, 65536);
224 rt->rt_flags |= RTF_MSS;
225 } else if (!strcmp(*argv, "window")) {
226
227 rt->rt_window = atolx_range(argv[1], 128, INT_MAX);
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
242static 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
258static 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);
264
265 if (!action || !*argv) help_exit("setroute");
266
267 arg2_action = get_action(&argv, arglist2);
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
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
302
303
304static 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);
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
320
321
322static 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
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
349static 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;
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
389
390
391static 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;
398 break;
399 }
400 error_exit("IPv6 ip format error");
401 }
402 addr[i++] = *ptr++;
403 if (!((i+1) % 5)) addr[i++] = ':';
404 }
405}
406
407static 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;
424
425
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
444void 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