source: branches/3.2/mindi-busybox/networking/libiproute/iproute.c @ 3232

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