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