1
2
3
4
5
6
7
8
9
10
11
12#include "ip_common.h"
13#include "common_bufsiz.h"
14#include "rt_names.h"
15#include "utils.h"
16
17#include <linux/version.h>
18
19
20#define HAVE_RTA_TABLE (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19))
21
22#ifndef RTAX_RTTVAR
23#define RTAX_RTTVAR RTAX_HOPS
24#endif
25
26
27struct filter_t {
28 int tb;
29 smallint flushed;
30 char *flushb;
31 int flushp;
32 int flushe;
33 struct rtnl_handle *rth;
34
35 int scope, scopemask;
36
37
38
39 int iif;
40 int oif;
41
42
43 inet_prefix rvia;
44 inet_prefix rdst;
45 inet_prefix mdst;
46 inet_prefix rsrc;
47 inet_prefix msrc;
48} FIX_ALIASING;
49typedef struct filter_t filter_t;
50
51#define G_filter (*(filter_t*)bb_common_bufsiz1)
52#define INIT_G() do { setup_common_bufsiz(); } while (0)
53
54static int flush_update(void)
55{
56 if (rtnl_send(G_filter.rth, G_filter.flushb, G_filter.flushp) < 0) {
57 bb_perror_msg("can't send flush request");
58 return -1;
59 }
60 G_filter.flushp = 0;
61 return 0;
62}
63
64static int FAST_FUNC print_route(const struct sockaddr_nl *who UNUSED_PARAM,
65 struct nlmsghdr *n, void *arg UNUSED_PARAM)
66{
67 struct rtmsg *r = NLMSG_DATA(n);
68 int len = n->nlmsg_len;
69 struct rtattr *tb[RTA_MAX+1];
70 inet_prefix dst;
71 inet_prefix src;
72 int host_len = -1;
73 uint32_t tid;
74
75 if (n->nlmsg_type != RTM_NEWROUTE && n->nlmsg_type != RTM_DELROUTE) {
76 fprintf(stderr, "Not a route: %08x %08x %08x\n",
77 n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
78 return 0;
79 }
80 if (G_filter.flushb && n->nlmsg_type != RTM_NEWROUTE)
81 return 0;
82 len -= NLMSG_LENGTH(sizeof(*r));
83 if (len < 0)
84 bb_error_msg_and_die("wrong nlmsg len %d", len);
85
86
87 parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
88
89#if HAVE_RTA_TABLE
90 if (tb[RTA_TABLE])
91 tid = *(uint32_t *)RTA_DATA(tb[RTA_TABLE]);
92 else
93#endif
94 tid = r->rtm_table;
95
96 if (r->rtm_family == AF_INET6)
97 host_len = 128;
98 else if (r->rtm_family == AF_INET)
99 host_len = 32;
100
101 if (r->rtm_family == AF_INET6) {
102 if (G_filter.tb) {
103 if (G_filter.tb < 0) {
104 if (!(r->rtm_flags & RTM_F_CLONED)) {
105 return 0;
106 }
107 } else {
108 if (r->rtm_flags & RTM_F_CLONED) {
109 return 0;
110 }
111 if (G_filter.tb == RT_TABLE_LOCAL) {
112 if (r->rtm_type != RTN_LOCAL) {
113 return 0;
114 }
115 } else if (G_filter.tb == RT_TABLE_MAIN) {
116 if (r->rtm_type == RTN_LOCAL) {
117 return 0;
118 }
119 } else {
120 return 0;
121 }
122 }
123 }
124 } else {
125 if (G_filter.tb > 0 && G_filter.tb != tid) {
126 return 0;
127 }
128 }
129 if ((G_filter.scope ^ r->rtm_scope) & G_filter.scopemask)
130 return 0;
131 if (G_filter.rdst.family
132 && (r->rtm_family != G_filter.rdst.family || G_filter.rdst.bitlen > r->rtm_dst_len)
133 ) {
134 return 0;
135 }
136 if (G_filter.mdst.family
137 && (r->rtm_family != G_filter.mdst.family
138 || (G_filter.mdst.bitlen >= 0 && G_filter.mdst.bitlen < r->rtm_dst_len)
139 )
140 ) {
141 return 0;
142 }
143 if (G_filter.rsrc.family
144 && (r->rtm_family != G_filter.rsrc.family || G_filter.rsrc.bitlen > r->rtm_src_len)
145 ) {
146 return 0;
147 }
148 if (G_filter.msrc.family
149 && (r->rtm_family != G_filter.msrc.family
150 || (G_filter.msrc.bitlen >= 0 && G_filter.msrc.bitlen < r->rtm_src_len)
151 )
152 ) {
153 return 0;
154 }
155
156 memset(&src, 0, sizeof(src));
157 memset(&dst, 0, sizeof(dst));
158
159 if (tb[RTA_SRC]) {
160 src.bitlen = r->rtm_src_len;
161 src.bytelen = (r->rtm_family == AF_INET6 ? 16 : 4);
162 memcpy(src.data, RTA_DATA(tb[RTA_SRC]), src.bytelen);
163 }
164 if (tb[RTA_DST]) {
165 dst.bitlen = r->rtm_dst_len;
166 dst.bytelen = (r->rtm_family == AF_INET6 ? 16 : 4);
167 memcpy(dst.data, RTA_DATA(tb[RTA_DST]), dst.bytelen);
168 }
169
170 if (G_filter.rdst.family
171 && inet_addr_match(&dst, &G_filter.rdst, G_filter.rdst.bitlen)
172 ) {
173 return 0;
174 }
175 if (G_filter.mdst.family
176 && G_filter.mdst.bitlen >= 0
177 && inet_addr_match(&dst, &G_filter.mdst, r->rtm_dst_len)
178 ) {
179 return 0;
180 }
181 if (G_filter.rsrc.family
182 && inet_addr_match(&src, &G_filter.rsrc, G_filter.rsrc.bitlen)
183 ) {
184 return 0;
185 }
186 if (G_filter.msrc.family && G_filter.msrc.bitlen >= 0
187 && inet_addr_match(&src, &G_filter.msrc, r->rtm_src_len)
188 ) {
189 return 0;
190 }
191 if (G_filter.oif != 0) {
192 if (!tb[RTA_OIF])
193 return 0;
194 if (G_filter.oif != *(int*)RTA_DATA(tb[RTA_OIF]))
195 return 0;
196 }
197
198 if (G_filter.flushb) {
199 struct nlmsghdr *fn;
200
201
202
203 if (r->rtm_family == AF_INET6
204 && r->rtm_dst_len == 0
205 && r->rtm_type == RTN_UNREACHABLE
206 && tb[RTA_PRIORITY]
207 && *(int*)RTA_DATA(tb[RTA_PRIORITY]) == -1
208 ) {
209 return 0;
210 }
211
212 if (NLMSG_ALIGN(G_filter.flushp) + n->nlmsg_len > G_filter.flushe) {
213 if (flush_update())
214 xfunc_die();
215 }
216 fn = (void*)(G_filter.flushb + NLMSG_ALIGN(G_filter.flushp));
217 memcpy(fn, n, n->nlmsg_len);
218 fn->nlmsg_type = RTM_DELROUTE;
219 fn->nlmsg_flags = NLM_F_REQUEST;
220 fn->nlmsg_seq = ++G_filter.rth->seq;
221 G_filter.flushp = (((char*)fn) + n->nlmsg_len) - G_filter.flushb;
222 G_filter.flushed = 1;
223 return 0;
224 }
225
226
227
228 if (n->nlmsg_type == RTM_DELROUTE) {
229 printf("Deleted ");
230 }
231 if (r->rtm_type != RTN_UNICAST ) {
232 printf("%s ", rtnl_rtntype_n2a(r->rtm_type));
233 }
234
235 if (tb[RTA_DST]) {
236 if (r->rtm_dst_len != host_len) {
237 printf("%s/%u ",
238 rt_addr_n2a(r->rtm_family, RTA_DATA(tb[RTA_DST])),
239 r->rtm_dst_len
240 );
241 } else {
242 printf("%s ", format_host(r->rtm_family,
243 RTA_PAYLOAD(tb[RTA_DST]),
244 RTA_DATA(tb[RTA_DST]))
245 );
246 }
247 } else if (r->rtm_dst_len) {
248 printf("0/%d ", r->rtm_dst_len);
249 } else {
250 printf("default ");
251 }
252 if (tb[RTA_SRC]) {
253 if (r->rtm_src_len != host_len) {
254 printf("from %s/%u ",
255 rt_addr_n2a(r->rtm_family, RTA_DATA(tb[RTA_SRC])),
256 r->rtm_src_len
257 );
258 } else {
259 printf("from %s ", format_host(r->rtm_family,
260 RTA_PAYLOAD(tb[RTA_SRC]),
261 RTA_DATA(tb[RTA_SRC]))
262 );
263 }
264 } else if (r->rtm_src_len) {
265 printf("from 0/%u ", r->rtm_src_len);
266 }
267 if (tb[RTA_GATEWAY] && G_filter.rvia.bitlen != host_len) {
268 printf("via %s ", format_host(r->rtm_family,
269 RTA_PAYLOAD(tb[RTA_GATEWAY]),
270 RTA_DATA(tb[RTA_GATEWAY]))
271 );
272 }
273 if (tb[RTA_OIF]) {
274 printf("dev %s ", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_OIF])));
275 }
276#if ENABLE_FEATURE_IP_RULE
277 if (tid && tid != RT_TABLE_MAIN && !G_filter.tb)
278 printf("table %s ", rtnl_rttable_n2a(tid));
279#endif
280
281
282 if (!(r->rtm_flags & RTM_F_CLONED)) {
283 if ((r->rtm_scope != RT_SCOPE_UNIVERSE) && G_filter.scopemask != -1)
284 printf("scope %s ", rtnl_rtscope_n2a(r->rtm_scope));
285 }
286
287 if (tb[RTA_PREFSRC] && 0 != host_len) {
288
289
290
291 printf(" src %s ", rt_addr_n2a(r->rtm_family,
292 RTA_DATA(tb[RTA_PREFSRC])));
293 }
294 if (tb[RTA_PRIORITY]) {
295 printf(" metric %d ", *(uint32_t*)RTA_DATA(tb[RTA_PRIORITY]));
296 }
297 if (r->rtm_flags & RTNH_F_DEAD) {
298 printf("dead ");
299 }
300 if (r->rtm_flags & RTNH_F_ONLINK) {
301 printf("onlink ");
302 }
303 if (r->rtm_flags & RTNH_F_PERVASIVE) {
304 printf("pervasive ");
305 }
306 if (r->rtm_flags & RTM_F_NOTIFY) {
307 printf("notify ");
308 }
309
310 if (r->rtm_family == AF_INET6) {
311 struct rta_cacheinfo *ci = NULL;
312 if (tb[RTA_CACHEINFO]) {
313 ci = RTA_DATA(tb[RTA_CACHEINFO]);
314 }
315 if ((r->rtm_flags & RTM_F_CLONED) || (ci && ci->rta_expires)) {
316 if (r->rtm_flags & RTM_F_CLONED) {
317 printf("%c cache ", _SL_);
318 }
319 if (ci->rta_expires) {
320 printf(" expires %dsec", ci->rta_expires / get_hz());
321 }
322 if (ci->rta_error != 0) {
323 printf(" error %d", ci->rta_error);
324 }
325 } else if (ci) {
326 if (ci->rta_error != 0)
327 printf(" error %d", ci->rta_error);
328 }
329 }
330 if (tb[RTA_IIF] && G_filter.iif == 0) {
331 printf(" iif %s", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_IIF])));
332 }
333 bb_putchar('\n');
334 return 0;
335}
336
337static int str_is_lock(const char *str)
338{
339 return strcmp(str, "lock") == 0;
340}
341
342
343static int iproute_modify(int cmd, unsigned flags, char **argv)
344{
345
346 static const char keywords[] ALIGN1 =
347 "src\0""via\0"
348 "mtu\0""advmss\0"
349 "scope\0""protocol\0"IF_FEATURE_IP_RULE("table\0")
350 "dev\0""oif\0""to\0""metric\0""onlink\0";
351#define keyword_via (keywords + sizeof("src"))
352#define keyword_mtu (keyword_via + sizeof("via"))
353#define keyword_advmss (keyword_mtu + sizeof("mtu"))
354#define keyword_scope (keyword_advmss + sizeof("advmss"))
355#define keyword_proto (keyword_scope + sizeof("scope"))
356#define keyword_table (keyword_proto + sizeof("protocol"))
357 enum {
358 ARG_src,
359 ARG_via,
360 ARG_mtu,
361 ARG_advmss,
362 ARG_scope,
363 ARG_protocol,
364IF_FEATURE_IP_RULE(ARG_table,)
365 ARG_dev,
366 ARG_oif,
367 ARG_to,
368 ARG_metric,
369 ARG_onlink,
370 };
371 enum {
372 gw_ok = 1 << 0,
373 dst_ok = 1 << 1,
374 proto_ok = 1 << 2,
375 type_ok = 1 << 3
376 };
377 struct rtnl_handle rth;
378 struct {
379 struct nlmsghdr n;
380 struct rtmsg r;
381 char buf[1024];
382 } req;
383 char mxbuf[256];
384 struct rtattr * mxrta = (void*)mxbuf;
385 unsigned mxlock = 0;
386 char *d = NULL;
387 smalluint ok = 0;
388 smalluint scope_ok = 0;
389 int arg;
390
391 memset(&req, 0, sizeof(req));
392
393 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
394 req.n.nlmsg_flags = NLM_F_REQUEST | flags;
395 req.n.nlmsg_type = cmd;
396 req.r.rtm_family = preferred_family;
397 if (RT_TABLE_MAIN != 0)
398 req.r.rtm_table = RT_TABLE_MAIN;
399 if (RT_SCOPE_NOWHERE != 0)
400 req.r.rtm_scope = RT_SCOPE_NOWHERE;
401
402 if (cmd != RTM_DELROUTE) {
403 req.r.rtm_scope = RT_SCOPE_UNIVERSE;
404 if (RTPROT_BOOT != 0)
405 req.r.rtm_protocol = RTPROT_BOOT;
406 if (RTN_UNICAST != 0)
407 req.r.rtm_type = RTN_UNICAST;
408 }
409
410 mxrta->rta_type = RTA_METRICS;
411 mxrta->rta_len = RTA_LENGTH(0);
412
413 while (*argv) {
414 arg = index_in_substrings(keywords, *argv);
415 if (arg == ARG_src) {
416 inet_prefix addr;
417 NEXT_ARG();
418 get_addr(&addr, *argv, req.r.rtm_family);
419 if (req.r.rtm_family == AF_UNSPEC)
420 req.r.rtm_family = addr.family;
421 addattr_l(&req.n, sizeof(req), RTA_PREFSRC, &addr.data, addr.bytelen);
422 } else if (arg == ARG_via) {
423 inet_prefix addr;
424 ok |= gw_ok;
425 NEXT_ARG();
426 get_addr(&addr, *argv, req.r.rtm_family);
427 if (req.r.rtm_family == AF_UNSPEC) {
428 req.r.rtm_family = addr.family;
429 }
430 addattr_l(&req.n, sizeof(req), RTA_GATEWAY, &addr.data, addr.bytelen);
431 } else if (arg == ARG_mtu) {
432 unsigned mtu;
433 NEXT_ARG();
434 if (str_is_lock(*argv)) {
435 mxlock |= (1 << RTAX_MTU);
436 NEXT_ARG();
437 }
438 mtu = get_unsigned(*argv, keyword_mtu);
439 rta_addattr32(mxrta, sizeof(mxbuf), RTAX_MTU, mtu);
440 } else if (arg == ARG_advmss) {
441 unsigned mss;
442 NEXT_ARG();
443 if (str_is_lock(*argv)) {
444 mxlock |= (1 << RTAX_ADVMSS);
445 NEXT_ARG();
446 }
447 mss = get_unsigned(*argv, keyword_advmss);
448 rta_addattr32(mxrta, sizeof(mxbuf), RTAX_ADVMSS, mss);
449 } else if (arg == ARG_scope) {
450 uint32_t scope;
451 NEXT_ARG();
452 if (rtnl_rtscope_a2n(&scope, *argv))
453 invarg_1_to_2(*argv, keyword_scope);
454 req.r.rtm_scope = scope;
455 scope_ok = 1;
456 } else if (arg == ARG_protocol) {
457 uint32_t prot;
458 NEXT_ARG();
459 if (rtnl_rtprot_a2n(&prot, *argv))
460 invarg_1_to_2(*argv, keyword_proto);
461 req.r.rtm_protocol = prot;
462 ok |= proto_ok;
463#if ENABLE_FEATURE_IP_RULE
464 } else if (arg == ARG_table) {
465 uint32_t tid;
466 NEXT_ARG();
467 if (rtnl_rttable_a2n(&tid, *argv))
468 invarg_1_to_2(*argv, keyword_table);
469#if HAVE_RTA_TABLE
470 if (tid > 255) {
471 req.r.rtm_table = RT_TABLE_UNSPEC;
472 addattr32(&req.n, sizeof(req), RTA_TABLE, tid);
473 } else
474#endif
475 req.r.rtm_table = tid;
476#endif
477 } else if (arg == ARG_dev || arg == ARG_oif) {
478 NEXT_ARG();
479 d = *argv;
480 } else if (arg == ARG_metric) {
481
482 uint32_t metric;
483 NEXT_ARG();
484 metric = get_u32(*argv, "metric");
485 addattr32(&req.n, sizeof(req), RTA_PRIORITY, metric);
486 } else if (arg == ARG_onlink) {
487 req.r.rtm_flags |= RTNH_F_ONLINK;
488 } else {
489 int type;
490 inet_prefix dst;
491
492 if (arg == ARG_to) {
493 NEXT_ARG();
494 }
495 if ((**argv < '0' || **argv > '9')
496 && rtnl_rtntype_a2n(&type, *argv) == 0
497 ) {
498 NEXT_ARG();
499 req.r.rtm_type = type;
500 ok |= type_ok;
501 }
502
503 if (ok & dst_ok) {
504 duparg2("to", *argv);
505 }
506 get_prefix(&dst, *argv, req.r.rtm_family);
507 if (req.r.rtm_family == AF_UNSPEC) {
508 req.r.rtm_family = dst.family;
509 }
510 req.r.rtm_dst_len = dst.bitlen;
511 ok |= dst_ok;
512 if (dst.bytelen) {
513 addattr_l(&req.n, sizeof(req), RTA_DST, &dst.data, dst.bytelen);
514 }
515 }
516
517#if 0
518 } else if (strcmp(*argv, "from") == 0) {
519 inet_prefix addr;
520 NEXT_ARG();
521 get_prefix(&addr, *argv, req.r.rtm_family);
522 if (req.r.rtm_family == AF_UNSPEC)
523 req.r.rtm_family = addr.family;
524 if (addr.bytelen)
525 addattr_l(&req.n, sizeof(req), RTA_SRC, &addr.data, addr.bytelen);
526 req.r.rtm_src_len = addr.bitlen;
527 } else if (strcmp(*argv, "tos") == 0 ||
528 matches(*argv, "dsfield") == 0) {
529 __u32 tos;
530 NEXT_ARG();
531 if (rtnl_dsfield_a2n(&tos, *argv))
532 invarg("\"tos\" value is invalid\n", *argv);
533 req.r.rtm_tos = tos;
534 } else if (strcmp(*argv, "hoplimit") == 0) {
535 unsigned hoplimit;
536 NEXT_ARG();
537 if (strcmp(*argv, "lock") == 0) {
538 mxlock |= (1<<RTAX_HOPLIMIT);
539 NEXT_ARG();
540 }
541 if (get_unsigned(&hoplimit, *argv, 0))
542 invarg("\"hoplimit\" value is invalid\n", *argv);
543 rta_addattr32(mxrta, sizeof(mxbuf), RTAX_HOPLIMIT, hoplimit);
544 } else if (matches(*argv, "reordering") == 0) {
545 unsigned reord;
546 NEXT_ARG();
547 if (strcmp(*argv, "lock") == 0) {
548 mxlock |= (1<<RTAX_REORDERING);
549 NEXT_ARG();
550 }
551 if (get_unsigned(&reord, *argv, 0))
552 invarg("\"reordering\" value is invalid\n", *argv);
553 rta_addattr32(mxrta, sizeof(mxbuf), RTAX_REORDERING, reord);
554 } else if (strcmp(*argv, "rtt") == 0) {
555 unsigned rtt;
556 NEXT_ARG();
557 if (strcmp(*argv, "lock") == 0) {
558 mxlock |= (1<<RTAX_RTT);
559 NEXT_ARG();
560 }
561 if (get_time_rtt(&rtt, *argv, &raw))
562 invarg("\"rtt\" value is invalid\n", *argv);
563 rta_addattr32(mxrta, sizeof(mxbuf), RTAX_RTT,
564 (raw) ? rtt : rtt * 8);
565 } else if (strcmp(*argv, "rto_min") == 0) {
566 unsigned rto_min;
567 NEXT_ARG();
568 mxlock |= (1<<RTAX_RTO_MIN);
569 if (get_time_rtt(&rto_min, *argv, &raw))
570 invarg("\"rto_min\" value is invalid\n",
571 *argv);
572 rta_addattr32(mxrta, sizeof(mxbuf), RTAX_RTO_MIN,
573 rto_min);
574 } else if (matches(*argv, "window") == 0) {
575 unsigned win;
576 NEXT_ARG();
577 if (strcmp(*argv, "lock") == 0) {
578 mxlock |= (1<<RTAX_WINDOW);
579 NEXT_ARG();
580 }
581 if (get_unsigned(&win, *argv, 0))
582 invarg("\"window\" value is invalid\n", *argv);
583 rta_addattr32(mxrta, sizeof(mxbuf), RTAX_WINDOW, win);
584 } else if (matches(*argv, "cwnd") == 0) {
585 unsigned win;
586 NEXT_ARG();
587 if (strcmp(*argv, "lock") == 0) {
588 mxlock |= (1<<RTAX_CWND);
589 NEXT_ARG();
590 }
591 if (get_unsigned(&win, *argv, 0))
592 invarg("\"cwnd\" value is invalid\n", *argv);
593 rta_addattr32(mxrta, sizeof(mxbuf), RTAX_CWND, win);
594 } else if (matches(*argv, "initcwnd") == 0) {
595 unsigned win;
596 NEXT_ARG();
597 if (strcmp(*argv, "lock") == 0) {
598 mxlock |= (1<<RTAX_INITCWND);
599 NEXT_ARG();
600 }
601 if (get_unsigned(&win, *argv, 0))
602 invarg("\"initcwnd\" value is invalid\n", *argv);
603 rta_addattr32(mxrta, sizeof(mxbuf), RTAX_INITCWND, win);
604 } else if (matches(*argv, "initrwnd") == 0) {
605 unsigned win;
606 NEXT_ARG();
607 if (strcmp(*argv, "lock") == 0) {
608 mxlock |= (1<<RTAX_INITRWND);
609 NEXT_ARG();
610 }
611 if (get_unsigned(&win, *argv, 0))
612 invarg("\"initrwnd\" value is invalid\n", *argv);
613 rta_addattr32(mxrta, sizeof(mxbuf), RTAX_INITRWND, win);
614 } else if (matches(*argv, "features") == 0) {
615 unsigned int features = 0;
616
617 while (argc > 0) {
618 NEXT_ARG();
619
620 if (strcmp(*argv, "ecn") == 0)
621 features |= RTAX_FEATURE_ECN;
622 else
623 invarg("\"features\" value not valid\n", *argv);
624 break;
625 }
626
627 rta_addattr32(mxrta, sizeof(mxbuf), RTAX_FEATURES, features);
628 } else if (matches(*argv, "quickack") == 0) {
629 unsigned quickack;
630 NEXT_ARG();
631 if (get_unsigned(&quickack, *argv, 0))
632 invarg("\"quickack\" value is invalid\n", *argv);
633 if (quickack != 1 && quickack != 0)
634 invarg("\"quickack\" value should be 0 or 1\n", *argv);
635 rta_addattr32(mxrta, sizeof(mxbuf), RTAX_QUICKACK, quickack);
636 } else if (matches(*argv, "rttvar") == 0) {
637 unsigned win;
638 NEXT_ARG();
639 if (strcmp(*argv, "lock") == 0) {
640 mxlock |= (1<<RTAX_RTTVAR);
641 NEXT_ARG();
642 }
643 if (get_time_rtt(&win, *argv, &raw))
644 invarg("\"rttvar\" value is invalid\n", *argv);
645 rta_addattr32(mxrta, sizeof(mxbuf), RTAX_RTTVAR,
646 (raw) ? win : win * 4);
647 } else if (matches(*argv, "ssthresh") == 0) {
648 unsigned win;
649 NEXT_ARG();
650 if (strcmp(*argv, "lock") == 0) {
651 mxlock |= (1<<RTAX_SSTHRESH);
652 NEXT_ARG();
653 }
654 if (get_unsigned(&win, *argv, 0))
655 invarg("\"ssthresh\" value is invalid\n", *argv);
656 rta_addattr32(mxrta, sizeof(mxbuf), RTAX_SSTHRESH, win);
657 } else if (matches(*argv, "realms") == 0) {
658 __u32 realm;
659 NEXT_ARG();
660 if (get_rt_realms(&realm, *argv))
661 invarg("\"realm\" value is invalid\n", *argv);
662 addattr32(&req.n, sizeof(req), RTA_FLOW, realm);
663 } else if (strcmp(*argv, "nexthop") == 0) {
664 nhs_ok = 1;
665 break;
666 }
667#endif
668 argv++;
669 }
670
671 xrtnl_open(&rth);
672
673 if (d) {
674 int idx;
675
676 ll_init_map(&rth);
677
678 if (d) {
679 idx = xll_name_to_index(d);
680 addattr32(&req.n, sizeof(req), RTA_OIF, idx);
681 }
682 }
683
684 if (mxrta->rta_len > RTA_LENGTH(0)) {
685 if (mxlock) {
686 rta_addattr32(mxrta, sizeof(mxbuf), RTAX_LOCK, mxlock);
687 }
688 addattr_l(&req.n, sizeof(req), RTA_METRICS, RTA_DATA(mxrta), RTA_PAYLOAD(mxrta));
689 }
690
691 if (!scope_ok) {
692 if (req.r.rtm_type == RTN_LOCAL || req.r.rtm_type == RTN_NAT)
693 req.r.rtm_scope = RT_SCOPE_HOST;
694 else
695 if (req.r.rtm_type == RTN_BROADCAST
696 || req.r.rtm_type == RTN_MULTICAST
697 || req.r.rtm_type == RTN_ANYCAST
698 ) {
699 req.r.rtm_scope = RT_SCOPE_LINK;
700 }
701 else if (req.r.rtm_type == RTN_UNICAST || req.r.rtm_type == RTN_UNSPEC) {
702 if (cmd == RTM_DELROUTE)
703 req.r.rtm_scope = RT_SCOPE_NOWHERE;
704 else if (!(ok & gw_ok))
705 req.r.rtm_scope = RT_SCOPE_LINK;
706 }
707 }
708
709 if (req.r.rtm_family == AF_UNSPEC) {
710 req.r.rtm_family = AF_INET;
711 }
712
713 if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) {
714 return 2;
715 }
716
717 return 0;
718}
719
720static int rtnl_rtcache_request(struct rtnl_handle *rth, int family)
721{
722 struct {
723 struct nlmsghdr nlh;
724 struct rtmsg rtm;
725 } req;
726 struct sockaddr_nl nladdr;
727
728 memset(&nladdr, 0, sizeof(nladdr));
729 memset(&req, 0, sizeof(req));
730 nladdr.nl_family = AF_NETLINK;
731
732 req.nlh.nlmsg_len = sizeof(req);
733 if (RTM_GETROUTE)
734 req.nlh.nlmsg_type = RTM_GETROUTE;
735 if (NLM_F_ROOT | NLM_F_REQUEST)
736 req.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_REQUEST;
737
738 req.nlh.nlmsg_seq = rth->dump = ++rth->seq;
739 req.rtm.rtm_family = family;
740 if (RTM_F_CLONED)
741 req.rtm.rtm_flags = RTM_F_CLONED;
742
743 return xsendto(rth->fd, (void*)&req, sizeof(req), (struct sockaddr*)&nladdr, sizeof(nladdr));
744}
745
746static void iproute_flush_cache(void)
747{
748 static const char fn[] ALIGN1 = "/proc/sys/net/ipv4/route/flush";
749 int flush_fd = open_or_warn(fn, O_WRONLY);
750
751 if (flush_fd < 0) {
752 return;
753 }
754
755 if (write(flush_fd, "-1", 2) < 2) {
756 bb_perror_msg("can't flush routing cache");
757 return;
758 }
759 close(flush_fd);
760}
761
762static void iproute_reset_filter(void)
763{
764 memset(&G_filter, 0, sizeof(G_filter));
765 G_filter.mdst.bitlen = -1;
766 G_filter.msrc.bitlen = -1;
767}
768
769
770static int iproute_list_or_flush(char **argv, int flush)
771{
772 int do_ipv6 = preferred_family;
773 struct rtnl_handle rth;
774 char *id = NULL;
775 char *od = NULL;
776 static const char keywords[] ALIGN1 =
777
778
779 "protocol\0" "dev\0" "oif\0" "iif\0"
780 "via\0" "table\0" "cache\0"
781 "from\0" "to\0" "scope\0"
782
783 "all\0"
784 "root\0"
785 "match\0"
786 "exact\0"
787 "main\0"
788 ;
789 enum {
790 KW_proto, KW_dev, KW_oif, KW_iif,
791 KW_via, KW_table, KW_cache,
792 KW_from, KW_to, KW_scope,
793
794 KW_all,
795 KW_root,
796 KW_match,
797 KW_exact,
798 KW_main,
799 };
800 int arg, parm;
801
802 iproute_reset_filter();
803 G_filter.tb = RT_TABLE_MAIN;
804
805 if (flush && !*argv)
806 bb_error_msg_and_die(bb_msg_requires_arg, "\"ip route flush\"");
807
808 while (*argv) {
809 arg = index_in_substrings(keywords, *argv);
810 if (arg == KW_proto) {
811 uint32_t prot = 0;
812 NEXT_ARG();
813
814 if (rtnl_rtprot_a2n(&prot, *argv)) {
815 if (index_in_strings(keywords, *argv) != KW_all)
816 invarg_1_to_2(*argv, "protocol");
817 prot = 0;
818
819 }
820
821 } else if (arg == KW_dev || arg == KW_oif) {
822 NEXT_ARG();
823 od = *argv;
824 } else if (arg == KW_iif) {
825 NEXT_ARG();
826 id = *argv;
827 } else if (arg == KW_via) {
828 NEXT_ARG();
829 get_prefix(&G_filter.rvia, *argv, do_ipv6);
830 } else if (arg == KW_table) {
831 NEXT_ARG();
832 parm = index_in_substrings(keywords, *argv);
833 if (parm == KW_cache)
834 G_filter.tb = -1;
835 else if (parm == KW_all)
836 G_filter.tb = 0;
837 else if (parm != KW_main) {
838#if ENABLE_FEATURE_IP_RULE
839 uint32_t tid;
840 if (rtnl_rttable_a2n(&tid, *argv))
841 invarg_1_to_2(*argv, "table");
842 G_filter.tb = tid;
843#else
844 invarg_1_to_2(*argv, "table");
845#endif
846 }
847 } else if (arg == KW_cache) {
848
849
850 G_filter.tb = -1;
851 } else if (arg == KW_scope) {
852 uint32_t scope;
853 NEXT_ARG();
854 G_filter.scopemask = -1;
855 if (rtnl_rtscope_a2n(&scope, *argv)) {
856 if (strcmp(*argv, "all") != 0)
857 invarg_1_to_2(*argv, "scope");
858 scope = RT_SCOPE_NOWHERE;
859 G_filter.scopemask = 0;
860 }
861 G_filter.scope = scope;
862 } else if (arg == KW_from) {
863 NEXT_ARG();
864 parm = index_in_substrings(keywords, *argv);
865 if (parm == KW_root) {
866 NEXT_ARG();
867 get_prefix(&G_filter.rsrc, *argv, do_ipv6);
868 } else if (parm == KW_match) {
869 NEXT_ARG();
870 get_prefix(&G_filter.msrc, *argv, do_ipv6);
871 } else {
872 if (parm == KW_exact)
873 NEXT_ARG();
874 get_prefix(&G_filter.msrc, *argv, do_ipv6);
875 G_filter.rsrc = G_filter.msrc;
876 }
877 } else {
878 if (arg == KW_to) {
879 NEXT_ARG();
880 arg = index_in_substrings(keywords, *argv);
881 }
882
883 if (arg == KW_root) {
884 NEXT_ARG();
885 get_prefix(&G_filter.rdst, *argv, do_ipv6);
886 } else if (arg == KW_match) {
887 NEXT_ARG();
888 get_prefix(&G_filter.mdst, *argv, do_ipv6);
889 } else {
890 if (arg == KW_exact)
891 NEXT_ARG();
892 get_prefix(&G_filter.mdst, *argv, do_ipv6);
893 G_filter.rdst = G_filter.mdst;
894 }
895 }
896 argv++;
897 }
898
899 if (do_ipv6 == AF_UNSPEC && G_filter.tb) {
900 do_ipv6 = AF_INET;
901 }
902
903 xrtnl_open(&rth);
904 ll_init_map(&rth);
905
906 if (id || od) {
907 int idx;
908
909 if (id) {
910 idx = xll_name_to_index(id);
911 G_filter.iif = idx;
912 }
913 if (od) {
914 idx = xll_name_to_index(od);
915 G_filter.oif = idx;
916 }
917 }
918
919 if (flush) {
920 char flushb[4096-512];
921
922 if (G_filter.tb == -1) {
923 if (do_ipv6 != AF_INET6)
924 iproute_flush_cache();
925 if (do_ipv6 == AF_INET)
926 return 0;
927 }
928
929 G_filter.flushb = flushb;
930 G_filter.flushp = 0;
931 G_filter.flushe = sizeof(flushb);
932 G_filter.rth = &rth;
933
934 for (;;) {
935 xrtnl_wilddump_request(&rth, do_ipv6, RTM_GETROUTE);
936 G_filter.flushed = 0;
937 xrtnl_dump_filter(&rth, print_route, NULL);
938 if (G_filter.flushed == 0)
939 return 0;
940 if (flush_update())
941 return 1;
942 }
943 }
944
945 if (G_filter.tb != -1) {
946 xrtnl_wilddump_request(&rth, do_ipv6, RTM_GETROUTE);
947 } else if (rtnl_rtcache_request(&rth, do_ipv6) < 0) {
948 bb_perror_msg_and_die("can't send dump request");
949 }
950 xrtnl_dump_filter(&rth, print_route, NULL);
951
952 return 0;
953}
954
955
956
957static int iproute_get(char **argv)
958{
959 struct rtnl_handle rth;
960 struct {
961 struct nlmsghdr n;
962 struct rtmsg r;
963 char buf[1024];
964 } req;
965 char *idev = NULL;
966 char *odev = NULL;
967 bool connected = 0;
968 bool from_ok = 0;
969 static const char options[] ALIGN1 =
970 "from\0""iif\0""oif\0""dev\0""notify\0""connected\0""to\0";
971
972 memset(&req, 0, sizeof(req));
973
974 iproute_reset_filter();
975
976 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
977 if (NLM_F_REQUEST)
978 req.n.nlmsg_flags = NLM_F_REQUEST;
979 if (RTM_GETROUTE)
980 req.n.nlmsg_type = RTM_GETROUTE;
981 req.r.rtm_family = preferred_family;
982
983
984
985
986
987
988
989
990 while (*argv) {
991 switch (index_in_strings(options, *argv)) {
992 case 0:
993 {
994 inet_prefix addr;
995 NEXT_ARG();
996 from_ok = 1;
997 get_prefix(&addr, *argv, req.r.rtm_family);
998 if (req.r.rtm_family == AF_UNSPEC) {
999 req.r.rtm_family = addr.family;
1000 }
1001 if (addr.bytelen) {
1002 addattr_l(&req.n, sizeof(req), RTA_SRC, &addr.data, addr.bytelen);
1003 }
1004 req.r.rtm_src_len = addr.bitlen;
1005 break;
1006 }
1007 case 1:
1008 NEXT_ARG();
1009 idev = *argv;
1010 break;
1011 case 2:
1012 case 3:
1013 NEXT_ARG();
1014 odev = *argv;
1015 break;
1016 case 4:
1017 req.r.rtm_flags |= RTM_F_NOTIFY;
1018 break;
1019 case 5:
1020 connected = 1;
1021 break;
1022 case 6:
1023 NEXT_ARG();
1024 default:
1025 {
1026 inet_prefix addr;
1027 get_prefix(&addr, *argv, req.r.rtm_family);
1028 if (req.r.rtm_family == AF_UNSPEC) {
1029 req.r.rtm_family = addr.family;
1030 }
1031 if (addr.bytelen) {
1032 addattr_l(&req.n, sizeof(req), RTA_DST, &addr.data, addr.bytelen);
1033 }
1034 req.r.rtm_dst_len = addr.bitlen;
1035 }
1036 }
1037 argv++;
1038 }
1039
1040 if (req.r.rtm_dst_len == 0) {
1041 bb_error_msg_and_die("need at least destination address");
1042 }
1043
1044 xrtnl_open(&rth);
1045
1046 ll_init_map(&rth);
1047
1048 if (idev || odev) {
1049 int idx;
1050
1051 if (idev) {
1052 idx = xll_name_to_index(idev);
1053 addattr32(&req.n, sizeof(req), RTA_IIF, idx);
1054 }
1055 if (odev) {
1056 idx = xll_name_to_index(odev);
1057 addattr32(&req.n, sizeof(req), RTA_OIF, idx);
1058 }
1059 }
1060
1061 if (req.r.rtm_family == AF_UNSPEC) {
1062 req.r.rtm_family = AF_INET;
1063 }
1064
1065 if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0) {
1066 return 2;
1067 }
1068
1069 if (connected && !from_ok) {
1070 struct rtmsg *r = NLMSG_DATA(&req.n);
1071 int len = req.n.nlmsg_len;
1072 struct rtattr * tb[RTA_MAX+1];
1073
1074 print_route(NULL, &req.n, NULL);
1075
1076 if (req.n.nlmsg_type != RTM_NEWROUTE) {
1077 bb_error_msg_and_die("not a route?");
1078 }
1079 len -= NLMSG_LENGTH(sizeof(*r));
1080 if (len < 0) {
1081 bb_error_msg_and_die("wrong len %d", len);
1082 }
1083
1084
1085 parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
1086
1087 if (tb[RTA_PREFSRC]) {
1088 tb[RTA_PREFSRC]->rta_type = RTA_SRC;
1089 r->rtm_src_len = 8*RTA_PAYLOAD(tb[RTA_PREFSRC]);
1090 } else if (!tb[RTA_SRC]) {
1091 bb_error_msg_and_die("can't connect the route");
1092 }
1093 if (!odev && tb[RTA_OIF]) {
1094 tb[RTA_OIF]->rta_type = 0;
1095 }
1096 if (tb[RTA_GATEWAY]) {
1097 tb[RTA_GATEWAY]->rta_type = 0;
1098 }
1099 if (!idev && tb[RTA_IIF]) {
1100 tb[RTA_IIF]->rta_type = 0;
1101 }
1102 req.n.nlmsg_flags = NLM_F_REQUEST;
1103 req.n.nlmsg_type = RTM_GETROUTE;
1104
1105 if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0) {
1106 return 2;
1107 }
1108 }
1109 print_route(NULL, &req.n, NULL);
1110 return 0;
1111}
1112
1113
1114int FAST_FUNC do_iproute(char **argv)
1115{
1116 static const char ip_route_commands[] ALIGN1 =
1117 "a\0""add\0""append\0""change\0""chg\0"
1118 "delete\0""get\0""list\0""show\0"
1119 "prepend\0""replace\0""test\0""flush\0"
1120 ;
1121 enum {
1122 CMD_a = 0, CMD_add, CMD_append, CMD_change, CMD_chg,
1123 CMD_delete, CMD_get, CMD_list, CMD_show,
1124 CMD_prepend, CMD_replace, CMD_test, CMD_flush,
1125 };
1126 int command_num;
1127 unsigned flags = 0;
1128 int cmd = RTM_NEWROUTE;
1129
1130 INIT_G();
1131
1132 if (!*argv)
1133 return iproute_list_or_flush(argv, 0);
1134
1135
1136
1137 command_num = index_in_substrings(ip_route_commands, *argv);
1138
1139 switch (command_num) {
1140 case CMD_a:
1141 case CMD_add:
1142 flags = NLM_F_CREATE|NLM_F_EXCL;
1143 break;
1144 case CMD_append:
1145 flags = NLM_F_CREATE|NLM_F_APPEND;
1146 break;
1147 case CMD_change:
1148 case CMD_chg:
1149 flags = NLM_F_REPLACE;
1150 break;
1151 case CMD_delete:
1152 cmd = RTM_DELROUTE;
1153 break;
1154 case CMD_get:
1155 return iproute_get(argv + 1);
1156 case CMD_list:
1157 case CMD_show:
1158 return iproute_list_or_flush(argv + 1, 0);
1159 case CMD_prepend:
1160 flags = NLM_F_CREATE;
1161 break;
1162 case CMD_replace:
1163 flags = NLM_F_CREATE|NLM_F_REPLACE;
1164 break;
1165 case CMD_test:
1166 flags = NLM_F_EXCL;
1167 break;
1168 case CMD_flush:
1169 return iproute_list_or_flush(argv + 1, 1);
1170 default:
1171 invarg_1_to_2(*argv, applet_name);
1172 }
1173
1174 return iproute_modify(cmd, flags, argv + 1);
1175}
1176