source: MondoRescue/branches/stable/mindi-busybox/networking/zcip.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: 13.7 KB
Line 
1/* vi: set sw=4 ts=4: */
2/*
3 * RFC3927 ZeroConf IPv4 Link-Local addressing
4 * (see <http://www.zeroconf.org/>)
5 *
6 * Copyright (C) 2003 by Arthur van Hoff (avh@strangeberry.com)
7 * Copyright (C) 2004 by David Brownell
8 *
9 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
10 */
11
12/*
13 * ZCIP just manages the 169.254.*.* addresses. That network is not
14 * routed at the IP level, though various proxies or bridges can
15 * certainly be used. Its naming is built over multicast DNS.
16 */
17
18//#define DEBUG
19
20// TODO:
21// - more real-world usage/testing, especially daemon mode
22// - kernel packet filters to reduce scheduling noise
23// - avoid silent script failures, especially under load...
24// - link status monitoring (restart on link-up; stop on link-down)
25
26#include <syslog.h>
27#include <poll.h>
28#include <sys/wait.h>
29#include <netinet/ether.h>
30#include <net/ethernet.h>
31#include <net/if.h>
32#include <net/if_arp.h>
33#include <linux/if_packet.h>
34#include <linux/sockios.h>
35
36#include "libbb.h"
37
38/* We don't need more than 32 bits of the counter */
39#define MONOTONIC_US() ((unsigned)monotonic_us())
40
41struct arp_packet {
42 struct ether_header hdr;
43 struct ether_arp arp;
44} ATTRIBUTE_PACKED;
45
46enum {
47/* 169.254.0.0 */
48 LINKLOCAL_ADDR = 0xa9fe0000,
49
50/* protocol timeout parameters, specified in seconds */
51 PROBE_WAIT = 1,
52 PROBE_MIN = 1,
53 PROBE_MAX = 2,
54 PROBE_NUM = 3,
55 MAX_CONFLICTS = 10,
56 RATE_LIMIT_INTERVAL = 60,
57 ANNOUNCE_WAIT = 2,
58 ANNOUNCE_NUM = 2,
59 ANNOUNCE_INTERVAL = 2,
60 DEFEND_INTERVAL = 10
61};
62
63/* States during the configuration process. */
64enum {
65 PROBE = 0,
66 RATE_LIMIT_PROBE,
67 ANNOUNCE,
68 MONITOR,
69 DEFEND
70};
71
72#define VDBG(fmt,args...) \
73 do { } while (0)
74
75/**
76 * Pick a random link local IP address on 169.254/16, except that
77 * the first and last 256 addresses are reserved.
78 */
79static void pick(struct in_addr *ip)
80{
81 unsigned tmp;
82
83 do {
84 tmp = rand() & IN_CLASSB_HOST;
85 } while (tmp > (IN_CLASSB_HOST - 0x0200));
86 ip->s_addr = htonl((LINKLOCAL_ADDR + 0x0100) + tmp);
87}
88
89/**
90 * Broadcast an ARP packet.
91 */
92static void arp(int fd, struct sockaddr *saddr, int op,
93 const struct ether_addr *source_addr, struct in_addr source_ip,
94 const struct ether_addr *target_addr, struct in_addr target_ip)
95{
96 struct arp_packet p;
97 memset(&p, 0, sizeof(p));
98
99 // ether header
100 p.hdr.ether_type = htons(ETHERTYPE_ARP);
101 memcpy(p.hdr.ether_shost, source_addr, ETH_ALEN);
102 memset(p.hdr.ether_dhost, 0xff, ETH_ALEN);
103
104 // arp request
105 p.arp.arp_hrd = htons(ARPHRD_ETHER);
106 p.arp.arp_pro = htons(ETHERTYPE_IP);
107 p.arp.arp_hln = ETH_ALEN;
108 p.arp.arp_pln = 4;
109 p.arp.arp_op = htons(op);
110 memcpy(&p.arp.arp_sha, source_addr, ETH_ALEN);
111 memcpy(&p.arp.arp_spa, &source_ip, sizeof(p.arp.arp_spa));
112 memcpy(&p.arp.arp_tha, target_addr, ETH_ALEN);
113 memcpy(&p.arp.arp_tpa, &target_ip, sizeof(p.arp.arp_tpa));
114
115 // send it
116 xsendto(fd, &p, sizeof(p), saddr, sizeof(*saddr));
117
118 // Currently all callers ignore errors, that's why returns are
119 // commented out...
120 //return 0;
121}
122
123/**
124 * Run a script. argv[2] is already NULL.
125 */
126static int run(char *argv[3], const char *intf, struct in_addr *ip)
127{
128 int status;
129
130 VDBG("%s run %s %s\n", intf, argv[0], argv[1]);
131
132 if (ip) {
133 char *addr = inet_ntoa(*ip);
134 setenv("ip", addr, 1);
135 bb_info_msg("%s %s %s", argv[1], intf, addr);
136 }
137
138 status = wait4pid(spawn(argv));
139 if (status < 0) {
140 bb_perror_msg("%s %s", argv[1], intf);
141 return -errno;
142 }
143 if (status != 0)
144 bb_error_msg("script %s %s failed, exitcode=%d", argv[0], argv[1], status);
145 return status;
146}
147
148/**
149 * Return milliseconds of random delay, up to "secs" seconds.
150 */
151static unsigned ALWAYS_INLINE ms_rdelay(unsigned secs)
152{
153 return rand() % (secs * 1000);
154}
155
156/**
157 * main program
158 */
159int zcip_main(int argc, char **argv);
160int zcip_main(int argc, char **argv)
161{
162 int state = PROBE;
163 struct ether_addr eth_addr;
164 const char *why;
165 int fd;
166 char *r_opt;
167 unsigned opts;
168
169 /* Ugly trick, but I want these zeroed in one go */
170 struct {
171 const struct in_addr null_ip;
172 const struct ether_addr null_addr;
173 struct sockaddr saddr;
174 struct in_addr ip;
175 struct ifreq ifr;
176 char *intf;
177 char *script_av[3];
178 int timeout_ms; /* must be signed */
179 unsigned conflicts;
180 unsigned nprobes;
181 unsigned nclaims;
182 int ready;
183 int verbose;
184 } L;
185#define null_ip (L.null_ip )
186#define null_addr (L.null_addr )
187#define saddr (L.saddr )
188#define ip (L.ip )
189#define ifr (L.ifr )
190#define intf (L.intf )
191#define script_av (L.script_av )
192#define timeout_ms (L.timeout_ms)
193#define conflicts (L.conflicts )
194#define nprobes (L.nprobes )
195#define nclaims (L.nclaims )
196#define ready (L.ready )
197#define verbose (L.verbose )
198
199 memset(&L, 0, sizeof(L));
200
201#define FOREGROUND (opts & 1)
202#define QUIT (opts & 2)
203 // parse commandline: prog [options] ifname script
204 // exactly 2 args; -v accumulates and implies -f
205 opt_complementary = "=2:vv:vf";
206 opts = getopt32(argv, "fqr:v", &r_opt, &verbose);
207 if (!FOREGROUND) {
208 /* Do it early, before all bb_xx_msg calls */
209 openlog(applet_name, 0, LOG_DAEMON);
210 logmode |= LOGMODE_SYSLOG;
211 }
212 if (opts & 4) { // -r n.n.n.n
213 if (inet_aton(r_opt, &ip) == 0
214 || (ntohl(ip.s_addr) & IN_CLASSB_NET) != LINKLOCAL_ADDR
215 ) {
216 bb_error_msg_and_die("invalid link address");
217 }
218 }
219 // On NOMMU reexec early (or else we will rerun things twice)
220#if !BB_MMU
221 if (!FOREGROUND)
222 bb_daemonize_or_rexec(DAEMON_CHDIR_ROOT, argv);
223#endif
224 argc -= optind;
225 argv += optind;
226
227 intf = argv[0];
228 script_av[0] = argv[1];
229 setenv("interface", intf, 1);
230
231 // initialize the interface (modprobe, ifup, etc)
232 script_av[1] = (char*)"init";
233 if (run(script_av, intf, NULL))
234 return EXIT_FAILURE;
235
236 // initialize saddr
237 //memset(&saddr, 0, sizeof(saddr));
238 safe_strncpy(saddr.sa_data, intf, sizeof(saddr.sa_data));
239
240 // open an ARP socket
241 fd = xsocket(PF_PACKET, SOCK_PACKET, htons(ETH_P_ARP));
242 // bind to the interface's ARP socket
243 xbind(fd, &saddr, sizeof(saddr));
244
245 // get the interface's ethernet address
246 //memset(&ifr, 0, sizeof(ifr));
247 strncpy(ifr.ifr_name, intf, sizeof(ifr.ifr_name));
248 xioctl(fd, SIOCGIFHWADDR, &ifr);
249 memcpy(&eth_addr, &ifr.ifr_hwaddr.sa_data, ETH_ALEN);
250
251 // start with some stable ip address, either a function of
252 // the hardware address or else the last address we used.
253 // NOTE: the sequence of addresses we try changes only
254 // depending on when we detect conflicts.
255 srand(*(unsigned*)&ifr.ifr_hwaddr.sa_data);
256 if (ip.s_addr == 0)
257 pick(&ip);
258
259 // FIXME cases to handle:
260 // - zcip already running!
261 // - link already has local address... just defend/update
262
263 // daemonize now; don't delay system startup
264 if (!FOREGROUND) {
265#if BB_MMU
266 bb_daemonize(DAEMON_CHDIR_ROOT);
267#endif
268 bb_info_msg("start, interface %s", intf);
269 }
270
271 // run the dynamic address negotiation protocol,
272 // restarting after address conflicts:
273 // - start with some address we want to try
274 // - short random delay
275 // - arp probes to see if another host else uses it
276 // - arp announcements that we're claiming it
277 // - use it
278 // - defend it, within limits
279 while (1) {
280 struct pollfd fds[1];
281 unsigned deadline_us;
282 struct arp_packet p;
283
284 int source_ip_conflict = 0;
285 int target_ip_conflict = 0;
286
287 fds[0].fd = fd;
288 fds[0].events = POLLIN;
289 fds[0].revents = 0;
290
291 // poll, being ready to adjust current timeout
292 if (!timeout_ms) {
293 timeout_ms = ms_rdelay(PROBE_WAIT);
294 // FIXME setsockopt(fd, SO_ATTACH_FILTER, ...) to
295 // make the kernel filter out all packets except
296 // ones we'd care about.
297 }
298 // set deadline_us to the point in time when we timeout
299 deadline_us = MONOTONIC_US() + timeout_ms * 1000;
300
301 VDBG("...wait %d %s nprobes=%u, nclaims=%u\n",
302 timeout_ms, intf, nprobes, nclaims);
303 switch (poll(fds, 1, timeout_ms)) {
304
305 // timeout
306 case 0:
307 VDBG("state = %d\n", state);
308 switch (state) {
309 case PROBE:
310 // timeouts in the PROBE state mean no conflicting ARP packets
311 // have been received, so we can progress through the states
312 if (nprobes < PROBE_NUM) {
313 nprobes++;
314 VDBG("probe/%u %s@%s\n",
315 nprobes, intf, inet_ntoa(ip));
316 arp(fd, &saddr, ARPOP_REQUEST,
317 &eth_addr, null_ip,
318 &null_addr, ip);
319 timeout_ms = PROBE_MIN * 1000;
320 timeout_ms += ms_rdelay(PROBE_MAX - PROBE_MIN);
321 }
322 else {
323 // Switch to announce state.
324 state = ANNOUNCE;
325 nclaims = 0;
326 VDBG("announce/%u %s@%s\n",
327 nclaims, intf, inet_ntoa(ip));
328 arp(fd, &saddr, ARPOP_REQUEST,
329 &eth_addr, ip,
330 &eth_addr, ip);
331 timeout_ms = ANNOUNCE_INTERVAL * 1000;
332 }
333 break;
334 case RATE_LIMIT_PROBE:
335 // timeouts in the RATE_LIMIT_PROBE state mean no conflicting ARP packets
336 // have been received, so we can move immediately to the announce state
337 state = ANNOUNCE;
338 nclaims = 0;
339 VDBG("announce/%u %s@%s\n",
340 nclaims, intf, inet_ntoa(ip));
341 arp(fd, &saddr, ARPOP_REQUEST,
342 &eth_addr, ip,
343 &eth_addr, ip);
344 timeout_ms = ANNOUNCE_INTERVAL * 1000;
345 break;
346 case ANNOUNCE:
347 // timeouts in the ANNOUNCE state mean no conflicting ARP packets
348 // have been received, so we can progress through the states
349 if (nclaims < ANNOUNCE_NUM) {
350 nclaims++;
351 VDBG("announce/%u %s@%s\n",
352 nclaims, intf, inet_ntoa(ip));
353 arp(fd, &saddr, ARPOP_REQUEST,
354 &eth_addr, ip,
355 &eth_addr, ip);
356 timeout_ms = ANNOUNCE_INTERVAL * 1000;
357 }
358 else {
359 // Switch to monitor state.
360 state = MONITOR;
361 // link is ok to use earlier
362 // FIXME update filters
363 script_av[1] = (char*)"config";
364 run(script_av, intf, &ip);
365 ready = 1;
366 conflicts = 0;
367 timeout_ms = -1; // Never timeout in the monitor state.
368
369 // NOTE: all other exit paths
370 // should deconfig ...
371 if (QUIT)
372 return EXIT_SUCCESS;
373 }
374 break;
375 case DEFEND:
376 // We won! No ARP replies, so just go back to monitor.
377 state = MONITOR;
378 timeout_ms = -1;
379 conflicts = 0;
380 break;
381 default:
382 // Invalid, should never happen. Restart the whole protocol.
383 state = PROBE;
384 pick(&ip);
385 timeout_ms = 0;
386 nprobes = 0;
387 nclaims = 0;
388 break;
389 } // switch (state)
390 break; // case 0 (timeout)
391 // packets arriving
392 case 1:
393 // We need to adjust the timeout in case we didn't receive
394 // a conflicting packet.
395 if (timeout_ms > 0) {
396 unsigned diff = deadline_us - MONOTONIC_US();
397 if ((int)(diff) < 0) {
398 // Current time is greater than the expected timeout time.
399 // Should never happen.
400 VDBG("missed an expected timeout\n");
401 timeout_ms = 0;
402 } else {
403 VDBG("adjusting timeout\n");
404 timeout_ms = diff / 1000;
405 if (!timeout_ms) timeout_ms = 1;
406 }
407 }
408
409 if ((fds[0].revents & POLLIN) == 0) {
410 if (fds[0].revents & POLLERR) {
411 // FIXME: links routinely go down;
412 // this shouldn't necessarily exit.
413 bb_error_msg("%s: poll error", intf);
414 if (ready) {
415 script_av[1] = (char*)"deconfig";
416 run(script_av, intf, &ip);
417 }
418 return EXIT_FAILURE;
419 }
420 continue;
421 }
422
423 // read ARP packet
424 if (recv(fd, &p, sizeof(p), 0) < 0) {
425 why = "recv";
426 goto bad;
427 }
428 if (p.hdr.ether_type != htons(ETHERTYPE_ARP))
429 continue;
430
431#ifdef DEBUG
432 {
433 struct ether_addr * sha = (struct ether_addr *) p.arp.arp_sha;
434 struct ether_addr * tha = (struct ether_addr *) p.arp.arp_tha;
435 struct in_addr * spa = (struct in_addr *) p.arp.arp_spa;
436 struct in_addr * tpa = (struct in_addr *) p.arp.arp_tpa;
437 VDBG("%s recv arp type=%d, op=%d,\n",
438 intf, ntohs(p.hdr.ether_type),
439 ntohs(p.arp.arp_op));
440 VDBG("\tsource=%s %s\n",
441 ether_ntoa(sha),
442 inet_ntoa(*spa));
443 VDBG("\ttarget=%s %s\n",
444 ether_ntoa(tha),
445 inet_ntoa(*tpa));
446 }
447#endif
448 if (p.arp.arp_op != htons(ARPOP_REQUEST)
449 && p.arp.arp_op != htons(ARPOP_REPLY))
450 continue;
451
452 if (memcmp(p.arp.arp_spa, &ip.s_addr, sizeof(struct in_addr)) == 0 &&
453 memcmp(&eth_addr, &p.arp.arp_sha, ETH_ALEN) != 0) {
454 source_ip_conflict = 1;
455 }
456 if (memcmp(p.arp.arp_tpa, &ip.s_addr, sizeof(struct in_addr)) == 0 &&
457 p.arp.arp_op == htons(ARPOP_REQUEST) &&
458 memcmp(&eth_addr, &p.arp.arp_tha, ETH_ALEN) != 0) {
459 target_ip_conflict = 1;
460 }
461
462 VDBG("state = %d, source ip conflict = %d, target ip conflict = %d\n",
463 state, source_ip_conflict, target_ip_conflict);
464 switch (state) {
465 case PROBE:
466 case ANNOUNCE:
467 // When probing or announcing, check for source IP conflicts
468 // and other hosts doing ARP probes (target IP conflicts).
469 if (source_ip_conflict || target_ip_conflict) {
470 conflicts++;
471 if (conflicts >= MAX_CONFLICTS) {
472 VDBG("%s ratelimit\n", intf);
473 timeout_ms = RATE_LIMIT_INTERVAL * 1000;
474 state = RATE_LIMIT_PROBE;
475 }
476
477 // restart the whole protocol
478 pick(&ip);
479 timeout_ms = 0;
480 nprobes = 0;
481 nclaims = 0;
482 }
483 break;
484 case MONITOR:
485 // If a conflict, we try to defend with a single ARP probe.
486 if (source_ip_conflict) {
487 VDBG("monitor conflict -- defending\n");
488 state = DEFEND;
489 timeout_ms = DEFEND_INTERVAL * 1000;
490 arp(fd, &saddr,
491 ARPOP_REQUEST,
492 &eth_addr, ip,
493 &eth_addr, ip);
494 }
495 break;
496 case DEFEND:
497 // Well, we tried. Start over (on conflict).
498 if (source_ip_conflict) {
499 state = PROBE;
500 VDBG("defend conflict -- starting over\n");
501 ready = 0;
502 script_av[1] = (char*)"deconfig";
503 run(script_av, intf, &ip);
504
505 // restart the whole protocol
506 pick(&ip);
507 timeout_ms = 0;
508 nprobes = 0;
509 nclaims = 0;
510 }
511 break;
512 default:
513 // Invalid, should never happen. Restart the whole protocol.
514 VDBG("invalid state -- starting over\n");
515 state = PROBE;
516 pick(&ip);
517 timeout_ms = 0;
518 nprobes = 0;
519 nclaims = 0;
520 break;
521 } // switch state
522
523 break; // case 1 (packets arriving)
524 default:
525 why = "poll";
526 goto bad;
527 } // switch poll
528 }
529 bad:
530 bb_perror_msg("%s, %s", intf, why);
531 return EXIT_FAILURE;
532}
Note: See TracBrowser for help on using the repository browser.