1 /* vi: set sw=4 ts=4: */
3 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
5 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
9 * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
10 * Kunihiro Ishiguro <kunihiro@zebra.org> 001102: rtnh_ifindex was not initialized
13 #include "ip_common.h" /* #include "libbb.h" is inside */
18 #define RTAX_RTTVAR RTAX_HOPS
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
36 //int realm, realmmask; - unused
37 //inet_prefix rprefsrc; - read-only
44 typedef struct filter_t filter_t
;
46 #define G_filter (*(filter_t*)&bb_common_bufsiz1)
48 static int flush_update(void)
50 if (rtnl_send(G_filter
.rth
, G_filter
.flushb
, G_filter
.flushp
) < 0) {
51 bb_perror_msg("can't send flush request");
58 static unsigned get_hz(void)
60 static unsigned hz_internal
;
66 fp
= fopen_for_read("/proc/net/psched");
70 if (fscanf(fp
, "%*08x%*08x%08x%08x", &nom
, &denom
) == 2)
76 hz_internal
= sysconf(_SC_CLK_TCK
);
80 static int FAST_FUNC
print_route(const struct sockaddr_nl
*who UNUSED_PARAM
,
81 struct nlmsghdr
*n
, void *arg UNUSED_PARAM
)
83 struct rtmsg
*r
= NLMSG_DATA(n
);
84 int len
= n
->nlmsg_len
;
85 struct rtattr
*tb
[RTA_MAX
+1];
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
);
97 if (G_filter
.flushb
&& n
->nlmsg_type
!= RTM_NEWROUTE
)
99 len
-= NLMSG_LENGTH(sizeof(*r
));
101 bb_error_msg_and_die("wrong nlmsg len %d", len
);
103 if (r
->rtm_family
== AF_INET6
)
105 else if (r
->rtm_family
== AF_INET
)
108 if (r
->rtm_family
== AF_INET6
) {
110 if (G_filter
.tb
< 0) {
111 if (!(r
->rtm_flags
& RTM_F_CLONED
)) {
115 if (r
->rtm_flags
& RTM_F_CLONED
) {
118 if (G_filter
.tb
== RT_TABLE_LOCAL
) {
119 if (r
->rtm_type
!= RTN_LOCAL
) {
122 } else if (G_filter
.tb
== RT_TABLE_MAIN
) {
123 if (r
->rtm_type
== RTN_LOCAL
) {
132 if (G_filter
.tb
> 0 && G_filter
.tb
!= r
->rtm_table
) {
136 if (G_filter
.rdst
.family
137 && (r
->rtm_family
!= G_filter
.rdst
.family
|| G_filter
.rdst
.bitlen
> r
->rtm_dst_len
)
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
)
148 if (G_filter
.rsrc
.family
149 && (r
->rtm_family
!= G_filter
.rsrc
.family
|| G_filter
.rsrc
.bitlen
> r
->rtm_src_len
)
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
)
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
);
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
);
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
);
177 if (G_filter
.rdst
.family
178 && inet_addr_match(&dst
, &G_filter
.rdst
, G_filter
.rdst
.bitlen
)
182 if (G_filter
.mdst
.family
183 && G_filter
.mdst
.bitlen
>= 0
184 && inet_addr_match(&dst
, &G_filter
.mdst
, r
->rtm_dst_len
)
188 if (G_filter
.rsrc
.family
189 && inet_addr_match(&src
, &G_filter
.rsrc
, G_filter
.rsrc
.bitlen
)
193 if (G_filter
.msrc
.family
&& G_filter
.msrc
.bitlen
>= 0
194 && inet_addr_match(&src
, &G_filter
.msrc
, r
->rtm_src_len
)
198 if (G_filter
.oif
!= 0) {
201 if (G_filter
.oif
!= *(int*)RTA_DATA(tb
[RTA_OIF
]))
205 if (G_filter
.flushb
) {
208 /* We are creating route flush commands */
210 if (r
->rtm_family
== AF_INET6
211 && r
->rtm_dst_len
== 0
212 && r
->rtm_type
== RTN_UNREACHABLE
214 && *(int*)RTA_DATA(tb
[RTA_PRIORITY
]) == -1
219 if (NLMSG_ALIGN(G_filter
.flushp
) + n
->nlmsg_len
> G_filter
.flushe
) {
221 bb_error_msg_and_die("flush");
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;
233 /* We are printing routes */
235 if (n
->nlmsg_type
== RTM_DELROUTE
) {
238 if (r
->rtm_type
!= RTN_UNICAST
/* && !G_filter.type - always 0 */) {
239 printf("%s ", rtnl_rtntype_n2a(r
->rtm_type
, b1
));
243 if (r
->rtm_dst_len
!= host_len
) {
244 printf("%s/%u ", rt_addr_n2a(r
->rtm_family
,
245 RTA_DATA(tb
[RTA_DST
]),
250 printf("%s ", format_host(r
->rtm_family
,
251 RTA_PAYLOAD(tb
[RTA_DST
]),
252 RTA_DATA(tb
[RTA_DST
]),
256 } else if (r
->rtm_dst_len
) {
257 printf("0/%d ", r
->rtm_dst_len
);
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
]),
269 printf("from %s ", format_host(r
->rtm_family
,
270 RTA_PAYLOAD(tb
[RTA_SRC
]),
271 RTA_DATA(tb
[RTA_SRC
]),
275 } else if (r
->rtm_src_len
) {
276 printf("from 0/%u ", r
->rtm_src_len
);
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
)));
285 printf("dev %s ", ll_index_to_name(*(int*)RTA_DATA(tb
[RTA_OIF
])));
288 /* Todo: parse & show "proto kernel", "scope link" here */
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.
294 printf(" src %s ", rt_addr_n2a(r
->rtm_family
,
295 RTA_DATA(tb
[RTA_PREFSRC
]),
296 abuf
, sizeof(abuf
)));
298 if (tb
[RTA_PRIORITY
]) {
299 printf(" metric %d ", *(uint32_t*)RTA_DATA(tb
[RTA_PRIORITY
]));
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
]);
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_
);
310 if (ci
->rta_expires
) {
311 printf(" expires %dsec", ci
->rta_expires
/ get_hz());
313 if (ci
->rta_error
!= 0) {
314 printf(" error %d", ci
->rta_error
);
317 if (ci
->rta_error
!= 0)
318 printf(" error %d", ci
->rta_error
);
321 if (tb
[RTA_IIF
] && G_filter
.iif
== 0) {
322 printf(" iif %s", ll_index_to_name(*(int*)RTA_DATA(tb
[RTA_IIF
])));
328 /* Return value becomes exitcode. It's okay to not return at all */
329 static int iproute_modify(int cmd
, unsigned flags
, char **argv
)
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";
339 IF_FEATURE_IP_RULE(ARG_table
,)
351 struct rtnl_handle rth
;
358 struct rtattr
* mxrta
= (void*)mxbuf
;
364 memset(&req
, 0, sizeof(req
));
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
;
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
;
381 mxrta
->rta_type
= RTA_METRICS
;
382 mxrta
->rta_len
= RTA_LENGTH(0);
385 arg
= index_in_substrings(keywords
, *argv
);
386 if (arg
== ARG_src
) {
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
) {
397 get_addr(&addr
, *argv
, req
.r
.rtm_family
);
398 if (req
.r
.rtm_family
== AF_UNSPEC
) {
399 req
.r
.rtm_family
= addr
.family
;
401 addattr_l(&req
.n
, sizeof(req
), RTA_GATEWAY
, &addr
.data
, addr
.bytelen
);
402 } else if (arg
== ARG_mtu
) {
405 if (index_in_strings(keywords
, *argv
) == PARM_lock
) {
406 mxlock
|= (1 << RTAX_MTU
);
409 mtu
= get_unsigned(*argv
, "mtu");
410 rta_addattr32(mxrta
, sizeof(mxbuf
), RTAX_MTU
, mtu
);
411 } else if (arg
== ARG_protocol
) {
414 if (rtnl_rtprot_a2n(&prot
, *argv
))
415 invarg(*argv
, "protocol");
416 req
.r
.rtm_protocol
= prot
;
418 #if ENABLE_FEATURE_IP_RULE
419 } else if (arg
== ARG_table
) {
422 if (rtnl_rttable_a2n(&tid
, *argv
))
423 invarg(*argv
, "table");
424 req
.r
.rtm_table
= tid
;
426 } else if (arg
== ARG_dev
|| arg
== ARG_oif
) {
429 } else if (arg
== ARG_metric
) {
432 metric
= get_u32(*argv
, "metric");
433 addattr32(&req
.n
, sizeof(req
), RTA_PRIORITY
, metric
);
441 if ((**argv
< '0' || **argv
> '9')
442 && rtnl_rtntype_a2n(&type
, *argv
) == 0
445 req
.r
.rtm_type
= type
;
450 duparg2("to", *argv
);
452 get_prefix(&dst
, *argv
, req
.r
.rtm_family
);
453 if (req
.r
.rtm_family
== AF_UNSPEC
) {
454 req
.r
.rtm_family
= dst
.family
;
456 req
.r
.rtm_dst_len
= dst
.bitlen
;
459 addattr_l(&req
.n
, sizeof(req
), RTA_DST
, &dst
.data
, dst
.bytelen
);
473 idx
= xll_name_to_index(d
);
474 addattr32(&req
.n
, sizeof(req
), RTA_OIF
, idx
);
478 if (mxrta
->rta_len
> RTA_LENGTH(0)) {
480 rta_addattr32(mxrta
, sizeof(mxbuf
), RTAX_LOCK
, mxlock
);
482 addattr_l(&req
.n
, sizeof(req
), RTA_METRICS
, RTA_DATA(mxrta
), RTA_PAYLOAD(mxrta
));
485 if (req
.r
.rtm_type
== RTN_LOCAL
|| req
.r
.rtm_type
== RTN_NAT
)
486 req
.r
.rtm_scope
= RT_SCOPE_HOST
;
488 if (req
.r
.rtm_type
== RTN_BROADCAST
489 || req
.r
.rtm_type
== RTN_MULTICAST
490 || req
.r
.rtm_type
== RTN_ANYCAST
492 req
.r
.rtm_scope
= RT_SCOPE_LINK
;
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
;
501 if (req
.r
.rtm_family
== AF_UNSPEC
) {
502 req
.r
.rtm_family
= AF_INET
;
505 if (rtnl_talk(&rth
, &req
.n
, 0, 0, NULL
, NULL
, NULL
) < 0) {
512 static int rtnl_rtcache_request(struct rtnl_handle
*rth
, int family
)
518 struct sockaddr_nl nladdr
;
520 memset(&nladdr
, 0, sizeof(nladdr
));
521 memset(&req
, 0, sizeof(req
));
522 nladdr
.nl_family
= AF_NETLINK
;
524 req
.nlh
.nlmsg_len
= sizeof(req
);
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
;
533 req
.rtm
.rtm_flags
= RTM_F_CLONED
;
535 return xsendto(rth
->fd
, (void*)&req
, sizeof(req
), (struct sockaddr
*)&nladdr
, sizeof(nladdr
));
538 static void iproute_flush_cache(void)
540 static const char fn
[] ALIGN1
= "/proc/sys/net/ipv4/route/flush";
541 int flush_fd
= open_or_warn(fn
, O_WRONLY
);
547 if (write(flush_fd
, "-1", 2) < 2) {
548 bb_perror_msg("can't flush routing cache");
554 static void iproute_reset_filter(void)
556 memset(&G_filter
, 0, sizeof(G_filter
));
557 G_filter
.mdst
.bitlen
= -1;
558 G_filter
.msrc
.bitlen
= -1;
561 /* Return value becomes exitcode. It's okay to not return at all */
562 static int iproute_list_or_flush(char **argv
, int flush
)
564 int do_ipv6
= preferred_family
;
565 struct rtnl_handle rth
;
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"
573 /* and possible further keywords */
581 KW_proto
, KW_dev
, KW_oif
, KW_iif
,
582 KW_via
, KW_table
, KW_cache
,
593 iproute_reset_filter();
594 G_filter
.tb
= RT_TABLE_MAIN
;
597 bb_error_msg_and_die(bb_msg_requires_arg
, "\"ip route flush\"");
600 arg
= index_in_substrings(keywords
, *argv
);
601 if (arg
== KW_proto
) {
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");
609 //G_filter.protocolmask = 0;
611 //G_filter.protocol = prot;
612 } else if (arg
== KW_dev
|| arg
== KW_oif
) {
615 } else if (arg
== KW_iif
) {
618 } else if (arg
== KW_via
) {
620 get_prefix(&G_filter
.rvia
, *argv
, do_ipv6
);
621 } else if (arg
== KW_table
) { /* table all/cache/main */
623 parm
= index_in_substrings(keywords
, *argv
);
624 if (parm
== KW_cache
)
626 else if (parm
== KW_all
)
628 else if (parm
!= KW_main
) {
629 #if ENABLE_FEATURE_IP_RULE
631 if (rtnl_rttable_a2n(&tid
, *argv
))
632 invarg(*argv
, "table");
635 invarg(*argv
, "table");
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' */
642 } else if (arg
== KW_from
) {
644 parm
= index_in_substrings(keywords
, *argv
);
645 if (parm
== KW_root
) {
647 get_prefix(&G_filter
.rsrc
, *argv
, do_ipv6
);
648 } else if (parm
== KW_match
) {
650 get_prefix(&G_filter
.msrc
, *argv
, do_ipv6
);
652 if (parm
== KW_exact
)
654 get_prefix(&G_filter
.msrc
, *argv
, do_ipv6
);
655 G_filter
.rsrc
= G_filter
.msrc
;
657 } else { /* "to" is the default parameter */
660 arg
= index_in_substrings(keywords
, *argv
);
662 /* parm = arg; - would be more plausible, but we reuse 'arg' here */
663 if (arg
== KW_root
) {
665 get_prefix(&G_filter
.rdst
, *argv
, do_ipv6
);
666 } else if (arg
== KW_match
) {
668 get_prefix(&G_filter
.mdst
, *argv
, do_ipv6
);
669 } else { /* "to exact" is the default */
672 get_prefix(&G_filter
.mdst
, *argv
, do_ipv6
);
673 G_filter
.rdst
= G_filter
.mdst
;
679 if (do_ipv6
== AF_UNSPEC
&& G_filter
.tb
) {
690 idx
= xll_name_to_index(id
);
694 idx
= xll_name_to_index(od
);
700 char flushb
[4096-512];
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
)
709 G_filter
.flushb
= flushb
;
711 G_filter
.flushe
= sizeof(flushb
);
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)
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");
730 xrtnl_dump_filter(&rth
, print_route
, NULL
);
736 /* Return value becomes exitcode. It's okay to not return at all */
737 static int iproute_get(char **argv
)
739 struct rtnl_handle rth
;
749 static const char options
[] ALIGN1
=
750 "from\0""iif\0""oif\0""dev\0""notify\0""connected\0""to\0";
752 memset(&req
, 0, sizeof(req
));
754 iproute_reset_filter();
756 req
.n
.nlmsg_len
= NLMSG_LENGTH(sizeof(struct rtmsg
));
758 req
.n
.nlmsg_flags
= NLM_F_REQUEST
;
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;*/
771 switch (index_in_strings(options
, *argv
)) {
777 get_prefix(&addr
, *argv
, req
.r
.rtm_family
);
778 if (req
.r
.rtm_family
== AF_UNSPEC
) {
779 req
.r
.rtm_family
= addr
.family
;
782 addattr_l(&req
.n
, sizeof(req
), RTA_SRC
, &addr
.data
, addr
.bytelen
);
784 req
.r
.rtm_src_len
= addr
.bitlen
;
797 req
.r
.rtm_flags
|= RTM_F_NOTIFY
;
799 case 5: /* connected */
807 get_prefix(&addr
, *argv
, req
.r
.rtm_family
);
808 if (req
.r
.rtm_family
== AF_UNSPEC
) {
809 req
.r
.rtm_family
= addr
.family
;
812 addattr_l(&req
.n
, sizeof(req
), RTA_DST
, &addr
.data
, addr
.bytelen
);
814 req
.r
.rtm_dst_len
= addr
.bitlen
;
820 if (req
.r
.rtm_dst_len
== 0) {
821 bb_error_msg_and_die("need at least destination address");
832 idx
= xll_name_to_index(idev
);
833 addattr32(&req
.n
, sizeof(req
), RTA_IIF
, idx
);
836 idx
= xll_name_to_index(odev
);
837 addattr32(&req
.n
, sizeof(req
), RTA_OIF
, idx
);
841 if (req
.r
.rtm_family
== AF_UNSPEC
) {
842 req
.r
.rtm_family
= AF_INET
;
845 if (rtnl_talk(&rth
, &req
.n
, 0, 0, &req
.n
, NULL
, NULL
) < 0) {
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];
854 print_route(NULL
, &req
.n
, NULL
);
856 if (req
.n
.nlmsg_type
!= RTM_NEWROUTE
) {
857 bb_error_msg_and_die("not a route?");
859 len
-= NLMSG_LENGTH(sizeof(*r
));
861 bb_error_msg_and_die("wrong len %d", len
);
864 memset(tb
, 0, sizeof(tb
));
865 parse_rtattr(tb
, RTA_MAX
, RTM_RTA(r
), len
);
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");
873 if (!odev
&& tb
[RTA_OIF
]) {
874 tb
[RTA_OIF
]->rta_type
= 0;
876 if (tb
[RTA_GATEWAY
]) {
877 tb
[RTA_GATEWAY
]->rta_type
= 0;
879 if (!idev
&& tb
[RTA_IIF
]) {
880 tb
[RTA_IIF
]->rta_type
= 0;
882 req
.n
.nlmsg_flags
= NLM_F_REQUEST
;
883 req
.n
.nlmsg_type
= RTM_GETROUTE
;
885 if (rtnl_talk(&rth
, &req
.n
, 0, 0, &req
.n
, NULL
, NULL
) < 0) {
889 print_route(NULL
, &req
.n
, NULL
);
893 /* Return value becomes exitcode. It's okay to not return at all */
894 int FAST_FUNC
do_iproute(char **argv
)
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";
902 int cmd
= RTM_NEWROUTE
;
905 return iproute_list_or_flush(argv
, 0);
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
);
911 switch (command_num
) {
913 flags
= NLM_F_CREATE
|NLM_F_EXCL
;
916 flags
= NLM_F_CREATE
|NLM_F_APPEND
;
920 flags
= NLM_F_REPLACE
;
926 return iproute_get(argv
+1);
929 return iproute_list_or_flush(argv
+1, 0);
930 case 8: /* prepend */
931 flags
= NLM_F_CREATE
;
933 case 9: /* replace */
934 flags
= NLM_F_CREATE
|NLM_F_REPLACE
;
940 return iproute_list_or_flush(argv
+1, 1);
942 bb_error_msg_and_die("unknown command %s", *argv
);
945 return iproute_modify(cmd
, flags
, argv
+1);