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