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