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