source: MondoRescue/branches/3.3/mindi-busybox/networking/libiproute/iproute.c@ 3667

Last change on this file since 3667 was 3621, checked in by Bruno Cornec, 8 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

File size: 22.1 KB
RevLine 
[821]1/* vi: set sw=4 ts=4: */
2/*
[2725]3 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
[821]4 *
[2725]5 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
[821]6 *
7 * Changes:
8 *
[2725]9 * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
[821]10 * Kunihiro Ishiguro <kunihiro@zebra.org> 001102: rtnh_ifindex was not initialized
11 */
12
[2725]13#include "ip_common.h" /* #include "libbb.h" is inside */
[3621]14#include "common_bufsiz.h"
[821]15#include "rt_names.h"
16#include "utils.h"
17
18#ifndef RTAX_RTTVAR
19#define RTAX_RTTVAR RTAX_HOPS
20#endif
21
22
[2725]23struct filter_t {
[821]24 int tb;
[2725]25 smallint flushed;
[821]26 char *flushb;
27 int flushp;
28 int flushe;
29 struct rtnl_handle *rth;
[2725]30 //int protocol, protocolmask; - write-only fields?!
31 //int scope, scopemask; - unused
32 //int type; - read-only
33 //int typemask; - unused
34 //int tos, tosmask; - unused
[3232]35 int iif;
36 int oif;
[2725]37 //int realm, realmmask; - unused
38 //inet_prefix rprefsrc; - read-only
[821]39 inet_prefix rvia;
40 inet_prefix rdst;
41 inet_prefix mdst;
42 inet_prefix rsrc;
43 inet_prefix msrc;
[2725]44} FIX_ALIASING;
45typedef struct filter_t filter_t;
[821]46
[3621]47#define G_filter (*(filter_t*)bb_common_bufsiz1)
48#define INIT_G() do { setup_common_bufsiz(); } while (0)
[1765]49
[821]50static int flush_update(void)
51{
[2725]52 if (rtnl_send(G_filter.rth, G_filter.flushb, G_filter.flushp) < 0) {
53 bb_perror_msg("can't send flush request");
[821]54 return -1;
55 }
[2725]56 G_filter.flushp = 0;
[821]57 return 0;
58}
59
[2725]60static int FAST_FUNC print_route(const struct sockaddr_nl *who UNUSED_PARAM,
61 struct nlmsghdr *n, void *arg UNUSED_PARAM)
[821]62{
63 struct rtmsg *r = NLMSG_DATA(n);
64 int len = n->nlmsg_len;
[3232]65 struct rtattr *tb[RTA_MAX+1];
[821]66 inet_prefix dst;
67 inet_prefix src;
68 int host_len = -1;
69
70 if (n->nlmsg_type != RTM_NEWROUTE && n->nlmsg_type != RTM_DELROUTE) {
71 fprintf(stderr, "Not a route: %08x %08x %08x\n",
72 n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
73 return 0;
74 }
[2725]75 if (G_filter.flushb && n->nlmsg_type != RTM_NEWROUTE)
[821]76 return 0;
77 len -= NLMSG_LENGTH(sizeof(*r));
[1765]78 if (len < 0)
79 bb_error_msg_and_die("wrong nlmsg len %d", len);
[821]80
81 if (r->rtm_family == AF_INET6)
82 host_len = 128;
83 else if (r->rtm_family == AF_INET)
84 host_len = 32;
85
86 if (r->rtm_family == AF_INET6) {
[2725]87 if (G_filter.tb) {
88 if (G_filter.tb < 0) {
89 if (!(r->rtm_flags & RTM_F_CLONED)) {
[821]90 return 0;
91 }
92 } else {
[2725]93 if (r->rtm_flags & RTM_F_CLONED) {
[821]94 return 0;
95 }
[2725]96 if (G_filter.tb == RT_TABLE_LOCAL) {
[821]97 if (r->rtm_type != RTN_LOCAL) {
98 return 0;
99 }
[2725]100 } else if (G_filter.tb == RT_TABLE_MAIN) {
[821]101 if (r->rtm_type == RTN_LOCAL) {
102 return 0;
103 }
104 } else {
105 return 0;
106 }
107 }
108 }
109 } else {
[2725]110 if (G_filter.tb > 0 && G_filter.tb != r->rtm_table) {
[821]111 return 0;
112 }
113 }
[2725]114 if (G_filter.rdst.family
115 && (r->rtm_family != G_filter.rdst.family || G_filter.rdst.bitlen > r->rtm_dst_len)
116 ) {
[821]117 return 0;
118 }
[2725]119 if (G_filter.mdst.family
120 && (r->rtm_family != G_filter.mdst.family
121 || (G_filter.mdst.bitlen >= 0 && G_filter.mdst.bitlen < r->rtm_dst_len)
122 )
123 ) {
[821]124 return 0;
125 }
[2725]126 if (G_filter.rsrc.family
127 && (r->rtm_family != G_filter.rsrc.family || G_filter.rsrc.bitlen > r->rtm_src_len)
128 ) {
[821]129 return 0;
130 }
[2725]131 if (G_filter.msrc.family
132 && (r->rtm_family != G_filter.msrc.family
133 || (G_filter.msrc.bitlen >= 0 && G_filter.msrc.bitlen < r->rtm_src_len)
134 )
135 ) {
[821]136 return 0;
137 }
138
139 memset(tb, 0, sizeof(tb));
[3232]140 memset(&src, 0, sizeof(src));
141 memset(&dst, 0, sizeof(dst));
[821]142 parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
143
[3232]144 if (tb[RTA_SRC]) {
145 src.bitlen = r->rtm_src_len;
146 src.bytelen = (r->rtm_family == AF_INET6 ? 16 : 4);
147 memcpy(src.data, RTA_DATA(tb[RTA_SRC]), src.bytelen);
148 }
149 if (tb[RTA_DST]) {
150 dst.bitlen = r->rtm_dst_len;
151 dst.bytelen = (r->rtm_family == AF_INET6 ? 16 : 4);
152 memcpy(dst.data, RTA_DATA(tb[RTA_DST]), dst.bytelen);
153 }
154
[2725]155 if (G_filter.rdst.family
156 && inet_addr_match(&dst, &G_filter.rdst, G_filter.rdst.bitlen)
157 ) {
[821]158 return 0;
[2725]159 }
160 if (G_filter.mdst.family
161 && G_filter.mdst.bitlen >= 0
162 && inet_addr_match(&dst, &G_filter.mdst, r->rtm_dst_len)
163 ) {
[821]164 return 0;
[2725]165 }
166 if (G_filter.rsrc.family
167 && inet_addr_match(&src, &G_filter.rsrc, G_filter.rsrc.bitlen)
168 ) {
[821]169 return 0;
[2725]170 }
171 if (G_filter.msrc.family && G_filter.msrc.bitlen >= 0
172 && inet_addr_match(&src, &G_filter.msrc, r->rtm_src_len)
173 ) {
[821]174 return 0;
[2725]175 }
[3232]176 if (G_filter.oif != 0) {
177 if (!tb[RTA_OIF])
178 return 0;
179 if (G_filter.oif != *(int*)RTA_DATA(tb[RTA_OIF]))
180 return 0;
[2725]181 }
[821]182
[2725]183 if (G_filter.flushb) {
[821]184 struct nlmsghdr *fn;
[3232]185
186 /* We are creating route flush commands */
187
188 if (r->rtm_family == AF_INET6
189 && r->rtm_dst_len == 0
190 && r->rtm_type == RTN_UNREACHABLE
191 && tb[RTA_PRIORITY]
192 && *(int*)RTA_DATA(tb[RTA_PRIORITY]) == -1
193 ) {
194 return 0;
195 }
196
[2725]197 if (NLMSG_ALIGN(G_filter.flushp) + n->nlmsg_len > G_filter.flushe) {
[821]198 if (flush_update())
[3621]199 xfunc_die();
[821]200 }
[3232]201 fn = (void*)(G_filter.flushb + NLMSG_ALIGN(G_filter.flushp));
[821]202 memcpy(fn, n, n->nlmsg_len);
203 fn->nlmsg_type = RTM_DELROUTE;
204 fn->nlmsg_flags = NLM_F_REQUEST;
[2725]205 fn->nlmsg_seq = ++G_filter.rth->seq;
206 G_filter.flushp = (((char*)fn) + n->nlmsg_len) - G_filter.flushb;
207 G_filter.flushed = 1;
[821]208 return 0;
209 }
210
[3232]211 /* We are printing routes */
212
[821]213 if (n->nlmsg_type == RTM_DELROUTE) {
[2725]214 printf("Deleted ");
[821]215 }
[2725]216 if (r->rtm_type != RTN_UNICAST /* && !G_filter.type - always 0 */) {
[3621]217 printf("%s ", rtnl_rtntype_n2a(r->rtm_type));
[821]218 }
219
220 if (tb[RTA_DST]) {
221 if (r->rtm_dst_len != host_len) {
[3621]222 printf("%s/%u ",
223 rt_addr_n2a(r->rtm_family, RTA_DATA(tb[RTA_DST])),
224 r->rtm_dst_len
225 );
[821]226 } else {
[2725]227 printf("%s ", format_host(r->rtm_family,
228 RTA_PAYLOAD(tb[RTA_DST]),
[3621]229 RTA_DATA(tb[RTA_DST]))
230 );
[821]231 }
232 } else if (r->rtm_dst_len) {
[2725]233 printf("0/%d ", r->rtm_dst_len);
[821]234 } else {
[2725]235 printf("default ");
[821]236 }
237 if (tb[RTA_SRC]) {
238 if (r->rtm_src_len != host_len) {
[3621]239 printf("from %s/%u ",
240 rt_addr_n2a(r->rtm_family, RTA_DATA(tb[RTA_SRC])),
241 r->rtm_src_len
242 );
[821]243 } else {
[2725]244 printf("from %s ", format_host(r->rtm_family,
245 RTA_PAYLOAD(tb[RTA_SRC]),
[3621]246 RTA_DATA(tb[RTA_SRC]))
247 );
[821]248 }
249 } else if (r->rtm_src_len) {
[2725]250 printf("from 0/%u ", r->rtm_src_len);
[821]251 }
[2725]252 if (tb[RTA_GATEWAY] && G_filter.rvia.bitlen != host_len) {
253 printf("via %s ", format_host(r->rtm_family,
254 RTA_PAYLOAD(tb[RTA_GATEWAY]),
[3621]255 RTA_DATA(tb[RTA_GATEWAY]))
256 );
[821]257 }
[3232]258 if (tb[RTA_OIF]) {
[2725]259 printf("dev %s ", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_OIF])));
[821]260 }
261
[3232]262 /* Todo: parse & show "proto kernel", "scope link" here */
263
[2725]264 if (tb[RTA_PREFSRC] && /*G_filter.rprefsrc.bitlen - always 0*/ 0 != host_len) {
[821]265 /* Do not use format_host(). It is our local addr
266 and symbolic name will not be useful.
267 */
[2725]268 printf(" src %s ", rt_addr_n2a(r->rtm_family,
[3621]269 RTA_DATA(tb[RTA_PREFSRC])));
[821]270 }
271 if (tb[RTA_PRIORITY]) {
[2725]272 printf(" metric %d ", *(uint32_t*)RTA_DATA(tb[RTA_PRIORITY]));
[821]273 }
[3621]274 if (r->rtm_flags & RTNH_F_DEAD) {
275 printf("dead ");
276 }
277 if (r->rtm_flags & RTNH_F_ONLINK) {
278 printf("onlink ");
279 }
280 if (r->rtm_flags & RTNH_F_PERVASIVE) {
281 printf("pervasive ");
282 }
283 if (r->rtm_flags & RTM_F_NOTIFY) {
284 printf("notify ");
285 }
286
[821]287 if (r->rtm_family == AF_INET6) {
288 struct rta_cacheinfo *ci = NULL;
289 if (tb[RTA_CACHEINFO]) {
290 ci = RTA_DATA(tb[RTA_CACHEINFO]);
291 }
292 if ((r->rtm_flags & RTM_F_CLONED) || (ci && ci->rta_expires)) {
293 if (r->rtm_flags & RTM_F_CLONED) {
[2725]294 printf("%c cache ", _SL_);
[821]295 }
296 if (ci->rta_expires) {
[2725]297 printf(" expires %dsec", ci->rta_expires / get_hz());
[821]298 }
299 if (ci->rta_error != 0) {
[2725]300 printf(" error %d", ci->rta_error);
[821]301 }
302 } else if (ci) {
303 if (ci->rta_error != 0)
[2725]304 printf(" error %d", ci->rta_error);
[821]305 }
306 }
[3232]307 if (tb[RTA_IIF] && G_filter.iif == 0) {
[2725]308 printf(" iif %s", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_IIF])));
[821]309 }
[2725]310 bb_putchar('\n');
[821]311 return 0;
312}
313
[1765]314/* Return value becomes exitcode. It's okay to not return at all */
[2725]315static int iproute_modify(int cmd, unsigned flags, char **argv)
[821]316{
[1765]317 static const char keywords[] ALIGN1 =
[3621]318 "src\0""via\0""mtu\0""lock\0""scope\0""protocol\0"IF_FEATURE_IP_RULE("table\0")
319 "dev\0""oif\0""to\0""metric\0""onlink\0";
[1765]320 enum {
321 ARG_src,
322 ARG_via,
323 ARG_mtu, PARM_lock,
[3621]324 ARG_scope,
[1765]325 ARG_protocol,
[2725]326IF_FEATURE_IP_RULE(ARG_table,)
[1765]327 ARG_dev,
328 ARG_oif,
[2725]329 ARG_to,
330 ARG_metric,
[3621]331 ARG_onlink,
[1765]332 };
333 enum {
334 gw_ok = 1 << 0,
335 dst_ok = 1 << 1,
336 proto_ok = 1 << 2,
337 type_ok = 1 << 3
338 };
[821]339 struct rtnl_handle rth;
340 struct {
[2725]341 struct nlmsghdr n;
342 struct rtmsg r;
343 char buf[1024];
[821]344 } req;
[1765]345 char mxbuf[256];
[821]346 struct rtattr * mxrta = (void*)mxbuf;
347 unsigned mxlock = 0;
[1765]348 char *d = NULL;
349 smalluint ok = 0;
[3621]350 smalluint scope_ok = 0;
[1765]351 int arg;
[821]352
353 memset(&req, 0, sizeof(req));
354
355 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
[2725]356 req.n.nlmsg_flags = NLM_F_REQUEST | flags;
[821]357 req.n.nlmsg_type = cmd;
358 req.r.rtm_family = preferred_family;
[3621]359 if (RT_TABLE_MAIN != 0) /* if it is zero, memset already did it */
[2725]360 req.r.rtm_table = RT_TABLE_MAIN;
[3621]361 if (RT_SCOPE_NOWHERE != 0)
[2725]362 req.r.rtm_scope = RT_SCOPE_NOWHERE;
[821]363
364 if (cmd != RTM_DELROUTE) {
365 req.r.rtm_scope = RT_SCOPE_UNIVERSE;
[3621]366 if (RTPROT_BOOT != 0)
367 req.r.rtm_protocol = RTPROT_BOOT;
368 if (RTN_UNICAST != 0)
369 req.r.rtm_type = RTN_UNICAST;
[821]370 }
371
372 mxrta->rta_type = RTA_METRICS;
373 mxrta->rta_len = RTA_LENGTH(0);
374
[2725]375 while (*argv) {
[1765]376 arg = index_in_substrings(keywords, *argv);
377 if (arg == ARG_src) {
[821]378 inet_prefix addr;
379 NEXT_ARG();
380 get_addr(&addr, *argv, req.r.rtm_family);
[1765]381 if (req.r.rtm_family == AF_UNSPEC)
[821]382 req.r.rtm_family = addr.family;
383 addattr_l(&req.n, sizeof(req), RTA_PREFSRC, &addr.data, addr.bytelen);
[1765]384 } else if (arg == ARG_via) {
[821]385 inet_prefix addr;
[1765]386 ok |= gw_ok;
[821]387 NEXT_ARG();
388 get_addr(&addr, *argv, req.r.rtm_family);
389 if (req.r.rtm_family == AF_UNSPEC) {
390 req.r.rtm_family = addr.family;
391 }
392 addattr_l(&req.n, sizeof(req), RTA_GATEWAY, &addr.data, addr.bytelen);
[1765]393 } else if (arg == ARG_mtu) {
[821]394 unsigned mtu;
395 NEXT_ARG();
[1765]396 if (index_in_strings(keywords, *argv) == PARM_lock) {
[2725]397 mxlock |= (1 << RTAX_MTU);
[821]398 NEXT_ARG();
399 }
[2725]400 mtu = get_unsigned(*argv, "mtu");
[821]401 rta_addattr32(mxrta, sizeof(mxbuf), RTAX_MTU, mtu);
[3621]402 } else if (arg == ARG_scope) {
403 uint32_t scope;
404 NEXT_ARG();
405 if (rtnl_rtscope_a2n(&scope, *argv))
406 invarg_1_to_2(*argv, "scope");
407 req.r.rtm_scope = scope;
408 scope_ok = 1;
[1765]409 } else if (arg == ARG_protocol) {
[821]410 uint32_t prot;
411 NEXT_ARG();
412 if (rtnl_rtprot_a2n(&prot, *argv))
[3621]413 invarg_1_to_2(*argv, "protocol");
[821]414 req.r.rtm_protocol = prot;
[1765]415 ok |= proto_ok;
416#if ENABLE_FEATURE_IP_RULE
417 } else if (arg == ARG_table) {
418 uint32_t tid;
[821]419 NEXT_ARG();
[1765]420 if (rtnl_rttable_a2n(&tid, *argv))
[3621]421 invarg_1_to_2(*argv, "table");
[1765]422 req.r.rtm_table = tid;
423#endif
424 } else if (arg == ARG_dev || arg == ARG_oif) {
425 NEXT_ARG();
[821]426 d = *argv;
[2725]427 } else if (arg == ARG_metric) {
428 uint32_t metric;
429 NEXT_ARG();
430 metric = get_u32(*argv, "metric");
431 addattr32(&req.n, sizeof(req), RTA_PRIORITY, metric);
[3621]432 } else if (arg == ARG_onlink) {
433 req.r.rtm_flags |= RTNH_F_ONLINK;
[821]434 } else {
435 int type;
436 inet_prefix dst;
437
[1765]438 if (arg == ARG_to) {
[821]439 NEXT_ARG();
440 }
[1765]441 if ((**argv < '0' || **argv > '9')
[3232]442 && rtnl_rtntype_a2n(&type, *argv) == 0
443 ) {
[821]444 NEXT_ARG();
445 req.r.rtm_type = type;
[1765]446 ok |= type_ok;
[821]447 }
448
[1765]449 if (ok & dst_ok) {
[821]450 duparg2("to", *argv);
451 }
452 get_prefix(&dst, *argv, req.r.rtm_family);
453 if (req.r.rtm_family == AF_UNSPEC) {
454 req.r.rtm_family = dst.family;
455 }
456 req.r.rtm_dst_len = dst.bitlen;
[1765]457 ok |= dst_ok;
[821]458 if (dst.bytelen) {
459 addattr_l(&req.n, sizeof(req), RTA_DST, &dst.data, dst.bytelen);
460 }
461 }
[2725]462 argv++;
[821]463 }
464
[1765]465 xrtnl_open(&rth);
[821]466
467 if (d) {
468 int idx;
469
470 ll_init_map(&rth);
471
472 if (d) {
[1765]473 idx = xll_name_to_index(d);
[821]474 addattr32(&req.n, sizeof(req), RTA_OIF, idx);
475 }
476 }
477
478 if (mxrta->rta_len > RTA_LENGTH(0)) {
479 if (mxlock) {
480 rta_addattr32(mxrta, sizeof(mxbuf), RTAX_LOCK, mxlock);
481 }
482 addattr_l(&req.n, sizeof(req), RTA_METRICS, RTA_DATA(mxrta), RTA_PAYLOAD(mxrta));
483 }
484
[3621]485 if (!scope_ok) {
486 if (req.r.rtm_type == RTN_LOCAL || req.r.rtm_type == RTN_NAT)
487 req.r.rtm_scope = RT_SCOPE_HOST;
488 else
489 if (req.r.rtm_type == RTN_BROADCAST
490 || req.r.rtm_type == RTN_MULTICAST
491 || req.r.rtm_type == RTN_ANYCAST
492 ) {
[1765]493 req.r.rtm_scope = RT_SCOPE_LINK;
[3621]494 }
495 else if (req.r.rtm_type == RTN_UNICAST || req.r.rtm_type == RTN_UNSPEC) {
496 if (cmd == RTM_DELROUTE)
497 req.r.rtm_scope = RT_SCOPE_NOWHERE;
498 else if (!(ok & gw_ok))
499 req.r.rtm_scope = RT_SCOPE_LINK;
500 }
[1765]501 }
502
[821]503 if (req.r.rtm_family == AF_UNSPEC) {
504 req.r.rtm_family = AF_INET;
505 }
506
507 if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) {
[1765]508 return 2;
[821]509 }
510
511 return 0;
512}
513
514static int rtnl_rtcache_request(struct rtnl_handle *rth, int family)
515{
516 struct {
517 struct nlmsghdr nlh;
518 struct rtmsg rtm;
519 } req;
520 struct sockaddr_nl nladdr;
521
522 memset(&nladdr, 0, sizeof(nladdr));
523 memset(&req, 0, sizeof(req));
524 nladdr.nl_family = AF_NETLINK;
525
526 req.nlh.nlmsg_len = sizeof(req);
[2725]527 if (RTM_GETROUTE)
528 req.nlh.nlmsg_type = RTM_GETROUTE;
529 if (NLM_F_ROOT | NLM_F_REQUEST)
530 req.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_REQUEST;
531 /*req.nlh.nlmsg_pid = 0; - memset did it already */
[821]532 req.nlh.nlmsg_seq = rth->dump = ++rth->seq;
533 req.rtm.rtm_family = family;
[2725]534 if (RTM_F_CLONED)
535 req.rtm.rtm_flags = RTM_F_CLONED;
[821]536
[1765]537 return xsendto(rth->fd, (void*)&req, sizeof(req), (struct sockaddr*)&nladdr, sizeof(nladdr));
[821]538}
539
[1765]540static void iproute_flush_cache(void)
[821]541{
[1765]542 static const char fn[] ALIGN1 = "/proc/sys/net/ipv4/route/flush";
543 int flush_fd = open_or_warn(fn, O_WRONLY);
[821]544
545 if (flush_fd < 0) {
[1765]546 return;
[821]547 }
548
[1765]549 if (write(flush_fd, "-1", 2) < 2) {
[2725]550 bb_perror_msg("can't flush routing cache");
[1765]551 return;
[821]552 }
553 close(flush_fd);
554}
555
556static void iproute_reset_filter(void)
557{
[2725]558 memset(&G_filter, 0, sizeof(G_filter));
559 G_filter.mdst.bitlen = -1;
560 G_filter.msrc.bitlen = -1;
[821]561}
562
[1765]563/* Return value becomes exitcode. It's okay to not return at all */
[2725]564static int iproute_list_or_flush(char **argv, int flush)
[821]565{
566 int do_ipv6 = preferred_family;
567 struct rtnl_handle rth;
568 char *id = NULL;
569 char *od = NULL;
[1765]570 static const char keywords[] ALIGN1 =
[2725]571 /* "ip route list/flush" parameters: */
572 "protocol\0" "dev\0" "oif\0" "iif\0"
573 "via\0" "table\0" "cache\0"
574 "from\0" "to\0"
575 /* and possible further keywords */
576 "all\0"
577 "root\0"
578 "match\0"
579 "exact\0"
580 "main\0"
581 ;
[1765]582 enum {
[2725]583 KW_proto, KW_dev, KW_oif, KW_iif,
584 KW_via, KW_table, KW_cache,
585 KW_from, KW_to,
586 /* */
587 KW_all,
588 KW_root,
589 KW_match,
590 KW_exact,
591 KW_main,
[1765]592 };
593 int arg, parm;
[2725]594
[821]595 iproute_reset_filter();
[2725]596 G_filter.tb = RT_TABLE_MAIN;
[821]597
[2725]598 if (flush && !*argv)
[1765]599 bb_error_msg_and_die(bb_msg_requires_arg, "\"ip route flush\"");
[821]600
[2725]601 while (*argv) {
[1765]602 arg = index_in_substrings(keywords, *argv);
[2725]603 if (arg == KW_proto) {
[821]604 uint32_t prot = 0;
605 NEXT_ARG();
[2725]606 //G_filter.protocolmask = -1;
[821]607 if (rtnl_rtprot_a2n(&prot, *argv)) {
[2725]608 if (index_in_strings(keywords, *argv) != KW_all)
[3621]609 invarg_1_to_2(*argv, "protocol");
[821]610 prot = 0;
[2725]611 //G_filter.protocolmask = 0;
[821]612 }
[2725]613 //G_filter.protocol = prot;
614 } else if (arg == KW_dev || arg == KW_oif) {
[821]615 NEXT_ARG();
616 od = *argv;
[2725]617 } else if (arg == KW_iif) {
[821]618 NEXT_ARG();
619 id = *argv;
[2725]620 } else if (arg == KW_via) {
[821]621 NEXT_ARG();
[2725]622 get_prefix(&G_filter.rvia, *argv, do_ipv6);
623 } else if (arg == KW_table) { /* table all/cache/main */
[1765]624 NEXT_ARG();
625 parm = index_in_substrings(keywords, *argv);
[2725]626 if (parm == KW_cache)
627 G_filter.tb = -1;
628 else if (parm == KW_all)
629 G_filter.tb = 0;
630 else if (parm != KW_main) {
631#if ENABLE_FEATURE_IP_RULE
632 uint32_t tid;
633 if (rtnl_rttable_a2n(&tid, *argv))
[3621]634 invarg_1_to_2(*argv, "table");
[2725]635 G_filter.tb = tid;
636#else
[3621]637 invarg_1_to_2(*argv, "table");
[2725]638#endif
639 }
640 } else if (arg == KW_cache) {
641 /* The command 'ip route flush cache' is used by OpenSWAN.
642 * Assuming it's a synonym for 'ip route flush table cache' */
643 G_filter.tb = -1;
644 } else if (arg == KW_from) {
[1765]645 NEXT_ARG();
646 parm = index_in_substrings(keywords, *argv);
[2725]647 if (parm == KW_root) {
[821]648 NEXT_ARG();
[2725]649 get_prefix(&G_filter.rsrc, *argv, do_ipv6);
650 } else if (parm == KW_match) {
[821]651 NEXT_ARG();
[2725]652 get_prefix(&G_filter.msrc, *argv, do_ipv6);
[821]653 } else {
[2725]654 if (parm == KW_exact)
[821]655 NEXT_ARG();
[2725]656 get_prefix(&G_filter.msrc, *argv, do_ipv6);
657 G_filter.rsrc = G_filter.msrc;
[821]658 }
[2725]659 } else { /* "to" is the default parameter */
660 if (arg == KW_to) {
[821]661 NEXT_ARG();
[1765]662 arg = index_in_substrings(keywords, *argv);
[821]663 }
[2725]664 /* parm = arg; - would be more plausible, but we reuse 'arg' here */
665 if (arg == KW_root) {
[821]666 NEXT_ARG();
[2725]667 get_prefix(&G_filter.rdst, *argv, do_ipv6);
668 } else if (arg == KW_match) {
[821]669 NEXT_ARG();
[2725]670 get_prefix(&G_filter.mdst, *argv, do_ipv6);
671 } else { /* "to exact" is the default */
672 if (arg == KW_exact)
[821]673 NEXT_ARG();
[2725]674 get_prefix(&G_filter.mdst, *argv, do_ipv6);
675 G_filter.rdst = G_filter.mdst;
[821]676 }
677 }
[1765]678 argv++;
[821]679 }
680
[2725]681 if (do_ipv6 == AF_UNSPEC && G_filter.tb) {
[821]682 do_ipv6 = AF_INET;
683 }
684
[1765]685 xrtnl_open(&rth);
[821]686 ll_init_map(&rth);
687
688 if (id || od) {
689 int idx;
690
691 if (id) {
[1765]692 idx = xll_name_to_index(id);
[2725]693 G_filter.iif = idx;
[821]694 }
695 if (od) {
[1765]696 idx = xll_name_to_index(od);
[2725]697 G_filter.oif = idx;
[821]698 }
699 }
700
701 if (flush) {
702 char flushb[4096-512];
703
[2725]704 if (G_filter.tb == -1) { /* "flush table cache" */
[821]705 if (do_ipv6 != AF_INET6)
706 iproute_flush_cache();
707 if (do_ipv6 == AF_INET)
708 return 0;
709 }
710
[2725]711 G_filter.flushb = flushb;
712 G_filter.flushp = 0;
713 G_filter.flushe = sizeof(flushb);
714 G_filter.rth = &rth;
[821]715
716 for (;;) {
[1765]717 xrtnl_wilddump_request(&rth, do_ipv6, RTM_GETROUTE);
[2725]718 G_filter.flushed = 0;
719 xrtnl_dump_filter(&rth, print_route, NULL);
720 if (G_filter.flushed == 0)
[821]721 return 0;
[1765]722 if (flush_update())
723 return 1;
[821]724 }
725 }
726
[2725]727 if (G_filter.tb != -1) {
[1765]728 xrtnl_wilddump_request(&rth, do_ipv6, RTM_GETROUTE);
729 } else if (rtnl_rtcache_request(&rth, do_ipv6) < 0) {
[2725]730 bb_perror_msg_and_die("can't send dump request");
[821]731 }
[2725]732 xrtnl_dump_filter(&rth, print_route, NULL);
[821]733
[1765]734 return 0;
[821]735}
736
737
[1765]738/* Return value becomes exitcode. It's okay to not return at all */
[2725]739static int iproute_get(char **argv)
[821]740{
741 struct rtnl_handle rth;
742 struct {
[1765]743 struct nlmsghdr n;
744 struct rtmsg r;
745 char buf[1024];
[821]746 } req;
[1765]747 char *idev = NULL;
748 char *odev = NULL;
749 bool connected = 0;
750 bool from_ok = 0;
751 static const char options[] ALIGN1 =
752 "from\0""iif\0""oif\0""dev\0""notify\0""connected\0""to\0";
[821]753
754 memset(&req, 0, sizeof(req));
755
756 iproute_reset_filter();
757
758 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
[2725]759 if (NLM_F_REQUEST)
760 req.n.nlmsg_flags = NLM_F_REQUEST;
761 if (RTM_GETROUTE)
762 req.n.nlmsg_type = RTM_GETROUTE;
[821]763 req.r.rtm_family = preferred_family;
[2725]764 /*req.r.rtm_table = 0; - memset did this already */
765 /*req.r.rtm_protocol = 0;*/
766 /*req.r.rtm_scope = 0;*/
767 /*req.r.rtm_type = 0;*/
768 /*req.r.rtm_src_len = 0;*/
769 /*req.r.rtm_dst_len = 0;*/
770 /*req.r.rtm_tos = 0;*/
[821]771
[2725]772 while (*argv) {
[1765]773 switch (index_in_strings(options, *argv)) {
[821]774 case 0: /* from */
775 {
776 inet_prefix addr;
777 NEXT_ARG();
778 from_ok = 1;
779 get_prefix(&addr, *argv, req.r.rtm_family);
780 if (req.r.rtm_family == AF_UNSPEC) {
781 req.r.rtm_family = addr.family;
782 }
783 if (addr.bytelen) {
784 addattr_l(&req.n, sizeof(req), RTA_SRC, &addr.data, addr.bytelen);
785 }
786 req.r.rtm_src_len = addr.bitlen;
787 break;
788 }
789 case 1: /* iif */
790 NEXT_ARG();
791 idev = *argv;
792 break;
793 case 2: /* oif */
794 case 3: /* dev */
795 NEXT_ARG();
796 odev = *argv;
797 break;
798 case 4: /* notify */
799 req.r.rtm_flags |= RTM_F_NOTIFY;
800 break;
801 case 5: /* connected */
802 connected = 1;
803 break;
804 case 6: /* to */
805 NEXT_ARG();
806 default:
807 {
808 inet_prefix addr;
809 get_prefix(&addr, *argv, req.r.rtm_family);
810 if (req.r.rtm_family == AF_UNSPEC) {
811 req.r.rtm_family = addr.family;
812 }
813 if (addr.bytelen) {
814 addattr_l(&req.n, sizeof(req), RTA_DST, &addr.data, addr.bytelen);
815 }
816 req.r.rtm_dst_len = addr.bitlen;
817 }
818 }
[2725]819 argv++;
[821]820 }
821
822 if (req.r.rtm_dst_len == 0) {
823 bb_error_msg_and_die("need at least destination address");
824 }
825
[1765]826 xrtnl_open(&rth);
[821]827
828 ll_init_map(&rth);
829
830 if (idev || odev) {
831 int idx;
832
833 if (idev) {
[1765]834 idx = xll_name_to_index(idev);
[821]835 addattr32(&req.n, sizeof(req), RTA_IIF, idx);
836 }
837 if (odev) {
[1765]838 idx = xll_name_to_index(odev);
[821]839 addattr32(&req.n, sizeof(req), RTA_OIF, idx);
840 }
841 }
842
843 if (req.r.rtm_family == AF_UNSPEC) {
844 req.r.rtm_family = AF_INET;
845 }
846
847 if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0) {
[1765]848 return 2;
[821]849 }
850
851 if (connected && !from_ok) {
852 struct rtmsg *r = NLMSG_DATA(&req.n);
853 int len = req.n.nlmsg_len;
854 struct rtattr * tb[RTA_MAX+1];
855
[2725]856 print_route(NULL, &req.n, NULL);
[821]857
858 if (req.n.nlmsg_type != RTM_NEWROUTE) {
[1765]859 bb_error_msg_and_die("not a route?");
[821]860 }
861 len -= NLMSG_LENGTH(sizeof(*r));
862 if (len < 0) {
[1765]863 bb_error_msg_and_die("wrong len %d", len);
[821]864 }
865
866 memset(tb, 0, sizeof(tb));
867 parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
868
869 if (tb[RTA_PREFSRC]) {
870 tb[RTA_PREFSRC]->rta_type = RTA_SRC;
871 r->rtm_src_len = 8*RTA_PAYLOAD(tb[RTA_PREFSRC]);
872 } else if (!tb[RTA_SRC]) {
[2725]873 bb_error_msg_and_die("can't connect the route");
[821]874 }
875 if (!odev && tb[RTA_OIF]) {
876 tb[RTA_OIF]->rta_type = 0;
877 }
878 if (tb[RTA_GATEWAY]) {
879 tb[RTA_GATEWAY]->rta_type = 0;
880 }
881 if (!idev && tb[RTA_IIF]) {
882 tb[RTA_IIF]->rta_type = 0;
883 }
884 req.n.nlmsg_flags = NLM_F_REQUEST;
885 req.n.nlmsg_type = RTM_GETROUTE;
886
887 if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0) {
[1765]888 return 2;
[821]889 }
890 }
[2725]891 print_route(NULL, &req.n, NULL);
[1765]892 return 0;
[821]893}
894
[1765]895/* Return value becomes exitcode. It's okay to not return at all */
[2725]896int FAST_FUNC do_iproute(char **argv)
[821]897{
[1765]898 static const char ip_route_commands[] ALIGN1 =
899 /*0-3*/ "add\0""append\0""change\0""chg\0"
900 /*4-7*/ "delete\0""get\0""list\0""show\0"
901 /*8..*/ "prepend\0""replace\0""test\0""flush\0";
[2725]902 int command_num;
[1765]903 unsigned flags = 0;
[821]904 int cmd = RTM_NEWROUTE;
905
[3621]906 INIT_G();
907
[2725]908 if (!*argv)
909 return iproute_list_or_flush(argv, 0);
910
[1765]911 /* "Standard" 'ip r a' treats 'a' as 'add', not 'append' */
912 /* It probably means that it is using "first match" rule */
[2725]913 command_num = index_in_substrings(ip_route_commands, *argv);
914
[1765]915 switch (command_num) {
916 case 0: /* add */
[821]917 flags = NLM_F_CREATE|NLM_F_EXCL;
918 break;
919 case 1: /* append */
920 flags = NLM_F_CREATE|NLM_F_APPEND;
921 break;
922 case 2: /* change */
923 case 3: /* chg */
924 flags = NLM_F_REPLACE;
925 break;
926 case 4: /* delete */
927 cmd = RTM_DELROUTE;
928 break;
[1765]929 case 5: /* get */
[2725]930 return iproute_get(argv+1);
[1765]931 case 6: /* list */
932 case 7: /* show */
[2725]933 return iproute_list_or_flush(argv+1, 0);
[1765]934 case 8: /* prepend */
[821]935 flags = NLM_F_CREATE;
[2725]936 break;
[1765]937 case 9: /* replace */
[821]938 flags = NLM_F_CREATE|NLM_F_REPLACE;
[2725]939 break;
[1765]940 case 10: /* test */
[821]941 flags = NLM_F_EXCL;
[2725]942 break;
[1765]943 case 11: /* flush */
[2725]944 return iproute_list_or_flush(argv+1, 1);
[821]945 default:
[3621]946 invarg_1_to_2(*argv, applet_name);
[821]947 }
948
[2725]949 return iproute_modify(cmd, flags, argv+1);
[821]950}
Note: See TracBrowser for help on using the repository browser.