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