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#define FOR_route
45#include "toys.h"
46#define _LINUX_SYSINFO_H
47#include <linux/rtnetlink.h>
48
49GLOBALS(
50 char *A;
51)
52
53struct _arglist {
54 char *arg;
55 int action;
56};
57
58static struct _arglist arglist1[] = {
59 { "add", 1 }, { "del", 2 },
60 { "delete", 2 }, { NULL, 0 }
61};
62
63static struct _arglist arglist2[] = {
64 { "-net", 1 }, { "-host", 2 },
65 { NULL, 0 }
66};
67
68void xsend(int sockfd, void *buf, size_t len)
69{
70 if (send(sockfd, buf, len, 0) != len) perror_exit("xsend");
71}
72
73int xrecv(int sockfd, void *buf, size_t len)
74{
75 int msg_len = recv(sockfd, buf, len, 0);
76 if (msg_len < 0) perror_exit("xrecv");
77
78 return msg_len;
79}
80
81void addAttr(struct nlmsghdr *nl, int maxlen, void *attr, int type, int len)
82{
83 struct rtattr *rt;
84 int rtlen = RTA_LENGTH(len);
85 if (NLMSG_ALIGN(nl->nlmsg_len) + rtlen > maxlen) perror_exit("addAttr");
86 rt = (struct rtattr*)((char *)nl + NLMSG_ALIGN(nl->nlmsg_len));
87 rt->rta_type = type;
88 rt->rta_len = rtlen;
89 memcpy(RTA_DATA(rt), attr, len);
90 nl->nlmsg_len = NLMSG_ALIGN(nl->nlmsg_len) + rtlen;
91}
92
93static void get_hostname(sa_family_t f, void *a, char *dst, size_t len) {
94 size_t a_len = (AF_INET6 == f) ? sizeof(struct in6_addr) : sizeof(struct in_addr);
95
96 struct hostent *host = gethostbyaddr(a, a_len, f);
97 if (host) xstrncpy(dst, host->h_name, len);
98}
99
100static void display_routes(sa_family_t f)
101{
102 int fd, msg_hdr_len, route_protocol;
103 struct {
104 struct nlmsghdr nl;
105 struct rtmsg rt;
106 } req;
107 struct nlmsghdr buf[8192 / sizeof(struct nlmsghdr)];
108 struct nlmsghdr *msg_hdr_ptr;
109 struct rtmsg *route_entry;
110 struct rtattr *rteattr;
111
112 fd = xsocket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
113
114 memset(&req, 0, sizeof(req));
115 req.nl.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
116 req.nl.nlmsg_type = RTM_GETROUTE;
117 req.nl.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
118 req.nl.nlmsg_pid = getpid();
119 req.nl.nlmsg_seq = 1;
120 req.rt.rtm_family = f;
121 req.rt.rtm_table = RT_TABLE_MAIN;
122 xsend(fd, &req, sizeof(req));
123
124 if (f == AF_INET) {
125 xprintf("Kernel IP routing table\n"
126 "Destination Gateway Genmask Flags %s Iface\n",
127 FLAG(e) ? " MSS Window irtt" : "Metric Ref Use");
128 } else {
129 xprintf("Kernel IPv6 routing table\n"
130 "%-31s%-26s Flag Metric Ref Use If\n", "Destination", "Next Hop");
131 }
132
133 msg_hdr_len = xrecv(fd, buf, sizeof(buf));
134 msg_hdr_ptr = buf;
135 while (msg_hdr_ptr->nlmsg_type != NLMSG_DONE) {
136 while (NLMSG_OK(msg_hdr_ptr, msg_hdr_len)) {
137 route_entry = NLMSG_DATA(msg_hdr_ptr);
138 route_protocol = route_entry->rtm_protocol;
139
140
141
142
143 if (route_entry->rtm_table == RT_TABLE_MAIN) {
144 int route_attribute_len;
145 char dest[INET6_ADDRSTRLEN], gate[INET6_ADDRSTRLEN], netmask[32],
146 flags[10] = "U", if_name[IF_NAMESIZE] = "-";
147 unsigned priority = 0, mss = 0, win = 0, irtt = 0, ref = 0, use = 0,
148 route_netmask, metric_len;
149 struct in_addr netmask_addr;
150 struct rtattr *metric;
151 struct rta_cacheinfo *cache_info;
152
153 if (f == AF_INET) {
154 strcpy(dest, FLAG(n) ? "0.0.0.0" : "default");
155 strcpy(gate, FLAG(n) ? "*" : "0.0.0.0");
156 strcpy(netmask, "0.0.0.0");
157 } else {
158 strcpy(dest, "::");
159 strcpy(gate, "::");
160 }
161
162 route_netmask = route_entry->rtm_dst_len;
163 if (route_netmask == 0) netmask_addr.s_addr = ~((in_addr_t) -1);
164 else netmask_addr.s_addr = htonl(~((1 << (32 - route_netmask)) - 1));
165 inet_ntop(AF_INET, &netmask_addr, netmask, sizeof(netmask));
166
167 rteattr = RTM_RTA(route_entry);
168 route_attribute_len = RTM_PAYLOAD(msg_hdr_ptr);
169 while (RTA_OK(rteattr, route_attribute_len)) {
170 switch (rteattr->rta_type) {
171 case RTA_DST:
172 if (FLAG(n)) inet_ntop(f, RTA_DATA(rteattr), dest, sizeof(dest));
173 else get_hostname(f, RTA_DATA(rteattr), dest, sizeof(dest));
174 break;
175
176 case RTA_GATEWAY:
177 if (FLAG(n)) inet_ntop(f, RTA_DATA(rteattr), gate, sizeof(dest));
178 else get_hostname(f, RTA_DATA(rteattr), gate, sizeof(dest));
179 strcat(flags, "G");
180 break;
181
182 case RTA_PRIORITY:
183 priority = *(unsigned *)RTA_DATA(rteattr);
184 break;
185
186 case RTA_OIF:
187 if_indextoname(*(int *)RTA_DATA(rteattr), if_name);
188 break;
189
190 case RTA_METRICS:
191 metric_len = RTA_PAYLOAD(rteattr);
192 for (metric = RTA_DATA(rteattr); RTA_OK(metric, metric_len);
193 metric = RTA_NEXT(metric, metric_len))
194 if (metric->rta_type == RTAX_ADVMSS)
195 mss = *(unsigned *)RTA_DATA(metric);
196 else if (metric->rta_type == RTAX_WINDOW)
197 win = *(unsigned *)RTA_DATA(metric);
198 else if (metric->rta_type == RTAX_RTT)
199 irtt = (*(unsigned *)RTA_DATA(metric))/8;
200 break;
201
202 case RTA_CACHEINFO:
203 cache_info = RTA_DATA(rteattr);
204 ref = cache_info->rta_clntref;
205 use = cache_info->rta_used;
206 break;
207 }
208
209 rteattr = RTA_NEXT(rteattr, route_attribute_len);
210 }
211
212 if (route_entry->rtm_type == RTN_UNREACHABLE) flags[0] = '!';
213 if (route_netmask == 32) strcat(flags, "H");
214 if (route_protocol == RTPROT_REDIRECT) strcat(flags, "D");
215
216 if (f == AF_INET) {
217 xprintf("%-15.15s %-15.15s %-16s%-6s", dest, gate, netmask, flags);
218 if (FLAG(e)) xprintf("%5d %-5d %6d %s\n", mss, win, irtt, if_name);
219 else xprintf("%-6d %-2d %7d %s\n", priority, ref, use, if_name);
220 } else {
221 char *dest_with_mask = xmprintf("%s/%u", dest, route_netmask);
222 xprintf("%-30s %-26s %-4s %-6d %-4d %2d %-8s\n",
223 dest_with_mask, gate, flags, priority, ref, use, if_name);
224 free(dest_with_mask);
225 }
226 }
227 msg_hdr_ptr = NLMSG_NEXT(msg_hdr_ptr, msg_hdr_len);
228 }
229
230 msg_hdr_len = xrecv(fd, buf, sizeof(buf));
231 msg_hdr_ptr = buf;
232 }
233
234 xclose(fd);
235}
236
237
238static int get_action(char ***argv, struct _arglist *list)
239{
240 struct _arglist *alist;
241
242 if (!**argv) return 0;
243 for (alist = list; alist->arg; alist++) {
244 if (!strcmp(**argv, alist->arg)) {
245 *argv += 1;
246 return alist->action;
247 }
248 }
249 return 0;
250}
251
252
253static void setroute(sa_family_t f, char **argv)
254{
255 char *tgtip;
256 int sockfd, arg2_action;
257 int action = get_action(&argv, arglist1);
258 struct nlmsghdr buf[8192 / sizeof(struct nlmsghdr)];
259 struct nlmsghdr *nlMsg;
260 struct rtmsg *rtMsg;
261
262 if (!action || !*argv) help_exit("setroute");
263 arg2_action = get_action(&argv, arglist2);
264 if (!*argv) help_exit("setroute");
265 tgtip = *argv++;
266 sockfd = xsocket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
267 memset(buf, 0, sizeof(buf));
268 nlMsg = (struct nlmsghdr *) buf;
269 rtMsg = (struct rtmsg *) NLMSG_DATA(nlMsg);
270
271 nlMsg->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
272
273
274 if (action == 1) {
275 nlMsg->nlmsg_type = RTM_NEWROUTE;
276 nlMsg->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL;
277 } else {
278 nlMsg->nlmsg_type = RTM_DELROUTE;
279 nlMsg->nlmsg_flags = NLM_F_REQUEST;
280 }
281
282 nlMsg->nlmsg_pid = getpid();
283 nlMsg->nlmsg_seq = 1;
284 rtMsg->rtm_family = f;
285 rtMsg->rtm_table = RT_TABLE_UNSPEC;
286 rtMsg->rtm_type = RTN_UNICAST;
287 rtMsg->rtm_protocol = RTPROT_UNSPEC;
288 rtMsg->rtm_flags = RTM_F_NOTIFY;
289 rtMsg->rtm_dst_len = rtMsg->rtm_src_len = (f == AF_INET) ? 32 : 128;
290
291 if (arg2_action == 2) rtMsg->rtm_scope = RT_SCOPE_HOST;
292
293 size_t addr_len = sizeof(struct in_addr);
294 if (f == AF_INET6) addr_len = sizeof(struct in6_addr);
295 unsigned char addr[sizeof(struct in6_addr)] = {0,};
296
297 for (; *argv; argv++) {
298 if (!strcmp(*argv, "mod")) continue;
299 else if (!strcmp(*argv, "dyn")) continue;
300 else if (!strcmp(*argv, "reinstate")) continue;
301 else if (!strcmp(*argv, "reject")) rtMsg->rtm_type = RTN_UNREACHABLE;
302 else {
303 if (!argv[1]) show_help(stdout, 1);
304
305 if (!strcmp(*argv, "metric")) {
306 unsigned int priority = atolx_range(argv[1], 0, UINT_MAX);
307 addAttr(nlMsg, sizeof(toybuf), &priority, RTA_PRIORITY, sizeof(unsigned int));
308 } else if (!strcmp(*argv, "netmask")) {
309 uint32_t netmask;
310 char *ptr;
311 uint32_t naddr[4] = {0,};
312 uint64_t plen;
313
314 netmask = (f == AF_INET6) ? 128 : 32;
315 plen = strtoul(argv[1], &ptr, 0);
316
317 if (!ptr || ptr == argv[1] || *ptr || !plen || plen > netmask) {
318 if (!inet_pton(f, argv[1], &naddr)) error_exit("invalid netmask");
319 if (f == AF_INET) {
320 uint32_t mask = htonl(*naddr), host = ~mask;
321 if (host & (host + 1)) error_exit("invalid netmask");
322 for (plen = 0; mask; mask <<= 1) ++plen;
323 if (plen > 32) error_exit("invalid netmask");
324 }
325 }
326 netmask = plen;
327 rtMsg->rtm_dst_len = netmask;
328 } else if (!strcmp(*argv, "gw")) {
329 if (!inet_pton(f, argv[1], &addr)) error_exit("invalid gw");
330 addAttr(nlMsg, sizeof(toybuf), &addr, RTA_GATEWAY, addr_len);
331 } else if (!strcmp(*argv, "mss")) {
332
333
334
335
336 } else if (!strcmp(*argv, "window")) {
337
338
339
340
341 } else if (!strcmp(*argv, "irtt")) {
342
343
344
345 } else if (!strcmp(*argv, "dev")) {
346 unsigned int if_idx = if_nametoindex(argv[1]);
347 if (!if_idx) perror_exit("dev");
348 addAttr(nlMsg, sizeof(toybuf), &if_idx, RTA_OIF, sizeof(unsigned int));
349 } else help_exit("no '%s'", *argv);
350 argv++;
351 }
352 }
353
354 if (strcmp(tgtip, "default") != 0) {
355 char *prefix = strtok(0, "/");
356
357 if (prefix) rtMsg->rtm_dst_len = strtoul(prefix, &prefix, 0);
358 if (!inet_pton(f, strtok(tgtip, "/"), &addr)) error_exit("invalid target");
359 addAttr(nlMsg, sizeof(toybuf), &addr, RTA_DST, addr_len);
360 } else rtMsg->rtm_dst_len = 0;
361
362 xsend(sockfd, nlMsg, nlMsg->nlmsg_len);
363 xclose(sockfd);
364}
365
366void route_main(void)
367{
368 if (!*toys.optargs) {
369 if (!TT.A || !strcmp(TT.A, "inet")) display_routes(AF_INET);
370 else if (!strcmp(TT.A, "inet6")) display_routes(AF_INET6);
371 else show_help(stdout, 1);
372 } else {
373 if (!TT.A) {
374 if (toys.optc>1 && strchr(toys.optargs[1], ':')) {
375 xprintf("WARNING: Implicit IPV6 address using -Ainet6\n");
376 TT.A = "inet6";
377 } else TT.A = "inet";
378 }
379
380 if (!strcmp(TT.A, "inet")) setroute(AF_INET, toys.optargs);
381 else setroute(AF_INET6, toys.optargs);
382 }
383}
384