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