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