source: MondoRescue/branches/stable/mindi-busybox/networking/libiproute/iproute.c

Last change on this file was 1770, checked in by Bruno Cornec, 16 years ago
  • Better output for mindi-busybox revision
  • Remove dummy file created on NFS - report from Arnaud Tiger <arnaud.tiger_at_hp.com>
  • strace useful for debug
  • fix new versions for pb (2.0.0 for mindi and 1.7.2 for mindi-busybox)
  • fix build process for mindi-busybox + options used in that version (dd for label-partitions-as-necessary)
  • fix typo in label-partitions-as-necessary which doesn't seem to work
  • Update to busybox 1.7.2
  • perl is now required at restore time to support uuid swap partitions (and will be used for many other thigs

in the future for sure)

  • next mindi version will be 2.0.0 due to all the changes made in it (udev may break working distros)
  • small optimization in mindi on keyboard handling (one single find instead of multiple)
  • better interaction for USB device when launching mindi manually
  • attempt to automatically guess block disk size for ramdisk
  • fix typos in bkphw
  • Fix the remaining problem with UUID support for swap partitions
  • Updates mondoarchive man page for USB support
  • Adds preliminary Hardware support to mindi (Proliant SSSTK)
  • Tries to add udev support also for rhel4
  • Fix UUID support which was still broken.
  • Be conservative in test for the start-nfs script
  • Update config file for mindi-busybox for 1.7.2 migration
  • Try to run around a busybox bug (1.2.2 pb on inexistant links)
  • Add build content for mindi-busybox in pb
  • Remove distributions content for mindi-busybox
  • Fix a warning on inexistant raidtab
  • Solve problem on tmpfs in restore init (Problem of inexistant symlink and busybox)
  • Create MONDO_CACHE and use it everywhere + creation at start
  • Really never try to eject a USB device
  • Fix a issue with &> usage (replaced with 1> and 2>)
  • Adds magic file to depllist in order to have file working + ldd which helps for debugging issues
  • tty modes correct to avoid sh error messages
  • Use ext3 normally and not ext2 instead
  • USB device should be corrected after reading (take 1st part)
  • Adds a mount_USB_here function derived from mount_CDROM_here
  • usb detection place before /dev detection in device name at restore time
  • Fix when restoring from USB: media is asked in interactive mode
  • Adds USB support for mondorestore
  • mount_cdrom => mount_media
  • elilo.efi is now searched throughout /boot/efi and not in a fixed place as there is no standard
  • untar-and-softlink => untar (+ interface change)
  • suppress useless softlinks creation/removal in boot process
  • avoids udevd messages on groups
  • Increase # of disks to 99 as in mindi at restore time (should be a conf file parameter)
  • skip existing big file creation
  • seems to work correctly for USB mindi boot
  • Adds group and tty link to udev conf
  • Always load usb-torage (even 2.6) to initiate USB bus discovery
  • Better printing of messages
  • Attempt to fix a bug in supporting OpenSusE 10.3 kernel for initramfs (mindi may now use multiple regex for kernel initrd detection)
  • Links were not correctly done as non relative for modules in mindi
  • exclusion of modules denied now works
  • Also create modules in their ordinary place, so that classical modprobe works + copy modules.dep
  • Fix bugs for DENY_MODS handling
  • Add device /dev/console for udev
  • ide-generic should now really be excluded
  • Fix a bug in major number for tty
  • If udev then adds modprobe/insmod to rootfs
  • tty0 is also cretaed with udev
  • ide-generic put rather in DENY_MODS
  • udevd remove from deplist s handled in mindi directly
  • better default for mindi when using --usb
  • Handles dynamically linked busybox (in case we want to use it soon ;-)
  • Adds fixed devices to create for udev
  • ide-generic should not be part of the initrd when using libata v2
  • support a dynamically linked udev (case on Ubuntu 7.10 and Mandriva 2008.0 so should be quite generic) This will give incitation to move to dyn. linked binaries in the initrd which will help for other tasks (ia6 4)
  • Improvement in udev support (do not use cl options not available in busybox)
  • Udev in mindi
    • auto creation of the right links at boot time with udev-links.conf(from Mandriva 2008.0)
    • rework startup of udev as current makes kernel crash (from Mandriva 2008.0)
    • add support for 64 bits udev
  • Try to render MyInsmod silent at boot time
  • Adds udev support (mandatory for newest distributions to avoid remapping of devices in a different way as on the original system)
  • We also need vaft format support for USB boot
  • Adds libusual support (Ubuntu 7.10 needs it for USB)
  • Improve Ubuntu/Debian keyboard detection and support
  • pbinit adapted to new pb (0.8.10). Filtering of docs done in it
  • Suppress some mondo warnings and errors on USB again
  • Tries to fix lack of files in deb mindi package
  • Verify should now work for USB devices
  • More log/mesages improvement for USB support
  • - Supress g_erase_tmpdir_and_scratchdir
  • Improve some log messages for USB support
  • Try to improve install in mindi to avoid issues with isolinux.cfg not installed vene if in the pkg :-(
  • Improve mindi-busybox build
  • In conformity with pb 0.8.9
  • Add support for Ubuntu 7.10 in build process
  • Add USB Key button to Menu UI (CD streamer removed)
  • Attempt to fix error messages on tmp/scratch files at the end by removing those dir at the latest possible.
  • Fix a bug linked to the size of the -E param which could be used (Arnaud Tiger/René Ribaud).
  • Integrate ~/.pbrc content into mondorescue.pb (required project-builder >= 0.8.7)
  • Put mondorescue in conformity with new pb filtering rules
  • Add USB support at restore time (no test done yet). New start-usb script PB varibale added where useful
  • Unmounting USB device before removal of temporary scratchdir
  • Stil refining USB copy back to mondo (one command was not executed)
  • No need to have the image subdor in the csratchdir when USB.
  • umount the USB partition before attempting to use it
  • Remove useless copy from mindi to mondo at end of USB handling

(risky merge, we are raising the limits of 2 diverging branches. The status of stable is not completely sure as such. Will need lots of tests, but it's not yet done :-()
(merge -r1692:1769 $SVN_M/branches/2.2.5)

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