source: MondoRescue/branches/3.3/mindi-busybox/networking/zcip.c@ 3734

Last change on this file since 3734 was 3621, checked in by Bruno Cornec, 10 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

File size: 15.0 KB
RevLine 
[1765]1/* vi: set sw=4 ts=4: */
[821]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 *
[2725]9 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
[821]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
[902]18//#define DEBUG
[821]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
[3232]26//usage:#define zcip_trivial_usage
27//usage: "[OPTIONS] IFACE SCRIPT"
28//usage:#define zcip_full_usage "\n\n"
29//usage: "Manage a ZeroConf IPv4 link-local address\n"
30//usage: "\n -f Run in foreground"
31//usage: "\n -q Quit after obtaining address"
32//usage: "\n -r 169.254.x.x Request this address first"
[3621]33//usage: "\n -l x.x.0.0 Use this range instead of 169.254"
[3232]34//usage: "\n -v Verbose"
35//usage: "\n"
[3621]36//usage: "\n$LOGGING=none Suppress logging"
37//usage: "\n$LOGGING=syslog Log to syslog"
38//usage: "\n"
[3232]39//usage: "\nWith no -q, runs continuously monitoring for ARP conflicts,"
40//usage: "\nexits only on I/O errors (link down etc)"
41
42#include "libbb.h"
[3621]43#include "common_bufsiz.h"
[821]44#include <netinet/ether.h>
45#include <net/if.h>
46#include <net/if_arp.h>
47#include <linux/sockios.h>
48
[2725]49#include <syslog.h>
[821]50
[1765]51/* We don't need more than 32 bits of the counter */
52#define MONOTONIC_US() ((unsigned)monotonic_us())
53
[821]54struct arp_packet {
[2725]55 struct ether_header eth;
[902]56 struct ether_arp arp;
[2725]57} PACKED;
[821]58
59enum {
[3621]60 /* 0-1 seconds before sending 1st probe */
[821]61 PROBE_WAIT = 1,
[3621]62 /* 1-2 seconds between probes */
[821]63 PROBE_MIN = 1,
64 PROBE_MAX = 2,
[3621]65 PROBE_NUM = 3, /* total probes to send */
66 ANNOUNCE_INTERVAL = 2, /* 2 seconds between announces */
67 ANNOUNCE_NUM = 3, /* announces to send */
68 /* if probe/announce sees a conflict, multiply RANDOM(NUM_CONFLICT) by... */
69 CONFLICT_MULTIPLIER = 2,
70 /* if we monitor and see a conflict, how long is defend state? */
71 DEFEND_INTERVAL = 10,
[821]72};
73
[902]74/* States during the configuration process. */
75enum {
76 PROBE = 0,
77 ANNOUNCE,
78 MONITOR,
79 DEFEND
80};
[821]81
[2725]82#define VDBG(...) do { } while (0)
[821]83
[2725]84
85enum {
86 sock_fd = 3
87};
88
89struct globals {
[3621]90 struct sockaddr iface_sockaddr;
91 struct ether_addr our_ethaddr;
92 uint32_t localnet_ip;
[2725]93} FIX_ALIASING;
[3621]94#define G (*(struct globals*)bb_common_bufsiz1)
95#define INIT_G() do { setup_common_bufsiz(); } while (0)
[2725]96
97
[821]98/**
99 * Pick a random link local IP address on 169.254/16, except that
100 * the first and last 256 addresses are reserved.
101 */
[3621]102static uint32_t pick_nip(void)
[821]103{
[1765]104 unsigned tmp;
[821]105
106 do {
[1765]107 tmp = rand() & IN_CLASSB_HOST;
[821]108 } while (tmp > (IN_CLASSB_HOST - 0x0200));
[3621]109 return htonl((G.localnet_ip + 0x0100) + tmp);
[821]110}
111
[3621]112static const char *nip_to_a(uint32_t nip)
113{
114 struct in_addr in;
115 in.s_addr = nip;
116 return inet_ntoa(in);
117}
118
[821]119/**
120 * Broadcast an ARP packet.
121 */
[3621]122static void send_arp_request(
[2725]123 /* int op, - always ARPOP_REQUEST */
[3621]124 /* const struct ether_addr *source_eth, - always &G.our_ethaddr */
125 uint32_t source_nip,
126 const struct ether_addr *target_eth, uint32_t target_nip)
[821]127{
[2725]128 enum { op = ARPOP_REQUEST };
[3621]129#define source_eth (&G.our_ethaddr)
[2725]130
[821]131 struct arp_packet p;
[902]132 memset(&p, 0, sizeof(p));
[821]133
134 // ether header
[2725]135 p.eth.ether_type = htons(ETHERTYPE_ARP);
136 memcpy(p.eth.ether_shost, source_eth, ETH_ALEN);
137 memset(p.eth.ether_dhost, 0xff, ETH_ALEN);
[821]138
139 // arp request
[902]140 p.arp.arp_hrd = htons(ARPHRD_ETHER);
141 p.arp.arp_pro = htons(ETHERTYPE_IP);
142 p.arp.arp_hln = ETH_ALEN;
143 p.arp.arp_pln = 4;
144 p.arp.arp_op = htons(op);
[2725]145 memcpy(&p.arp.arp_sha, source_eth, ETH_ALEN);
[3621]146 memcpy(&p.arp.arp_spa, &source_nip, 4);
[2725]147 memcpy(&p.arp.arp_tha, target_eth, ETH_ALEN);
[3621]148 memcpy(&p.arp.arp_tpa, &target_nip, 4);
[821]149
150 // send it
[3621]151 // Even though sock_fd is already bound to G.iface_sockaddr, just send()
[2725]152 // won't work, because "socket is not connected"
153 // (and connect() won't fix that, "operation not supported").
[3621]154 // Thus we sendto() to G.iface_sockaddr. I wonder which sockaddr
[2725]155 // (from bind() or from sendto()?) kernel actually uses
156 // to determine iface to emit the packet from...
[3621]157 xsendto(sock_fd, &p, sizeof(p), &G.iface_sockaddr, sizeof(G.iface_sockaddr));
[2725]158#undef source_eth
[821]159}
160
161/**
[2725]162 * Run a script.
163 * argv[0]:intf argv[1]:script_name argv[2]:junk argv[3]:NULL
[821]164 */
[3621]165static int run(char *argv[3], const char *param, uint32_t nip)
[821]166{
[1765]167 int status;
[3621]168 const char *addr = addr; /* for gcc */
[2725]169 const char *fmt = "%s %s %s" + 3;
[821]170
[2725]171 argv[2] = (char*)param;
[821]172
[2725]173 VDBG("%s run %s %s\n", argv[0], argv[1], argv[2]);
174
[3621]175 if (nip != 0) {
176 addr = nip_to_a(nip);
[2725]177 xsetenv("ip", addr);
178 fmt -= 3;
[1765]179 }
[3621]180 bb_error_msg(fmt, argv[2], argv[0], addr);
[821]181
[2725]182 status = spawn_and_wait(argv + 1);
[1765]183 if (status < 0) {
[2725]184 bb_perror_msg("%s %s %s" + 3, argv[2], argv[0]);
[1765]185 return -errno;
[821]186 }
[1765]187 if (status != 0)
[2725]188 bb_error_msg("script %s %s failed, exitcode=%d", argv[1], argv[2], status & 0xff);
[821]189 return status;
190}
191
192/**
193 * Return milliseconds of random delay, up to "secs" seconds.
194 */
[2725]195static ALWAYS_INLINE unsigned random_delay_ms(unsigned secs)
[821]196{
[3621]197 return (unsigned)rand() % (secs * 1000);
[821]198}
199
200/**
201 * main program
202 */
[2725]203int zcip_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
204int zcip_main(int argc UNUSED_PARAM, char **argv)
[821]205{
[3621]206 char *r_opt;
207 const char *l_opt = "169.254.0.0";
[2725]208 int state;
[3621]209 int nsent;
[1765]210 unsigned opts;
[821]211
[3621]212 // Ugly trick, but I want these zeroed in one go
[1765]213 struct {
[3621]214 const struct ether_addr null_ethaddr;
[1765]215 struct ifreq ifr;
[3621]216 uint32_t chosen_nip;
217 int conflicts;
218 int timeout_ms; // must be signed
[1765]219 int verbose;
220 } L;
[3621]221#define null_ethaddr (L.null_ethaddr)
222#define ifr (L.ifr )
223#define chosen_nip (L.chosen_nip )
224#define conflicts (L.conflicts )
225#define timeout_ms (L.timeout_ms )
226#define verbose (L.verbose )
[1765]227
228 memset(&L, 0, sizeof(L));
[3232]229 INIT_G();
[1765]230
231#define FOREGROUND (opts & 1)
232#define QUIT (opts & 2)
[3621]233 // Parse commandline: prog [options] ifname script
[1765]234 // exactly 2 args; -v accumulates and implies -f
235 opt_complementary = "=2:vv:vf";
[3621]236 opts = getopt32(argv, "fqr:l:v", &r_opt, &l_opt, &verbose);
[2725]237#if !BB_MMU
238 // on NOMMU reexec early (or else we will rerun things twice)
239 if (!FOREGROUND)
240 bb_daemonize_or_rexec(0 /*was: DAEMON_CHDIR_ROOT*/, argv);
241#endif
[3621]242 // Open an ARP socket
[2725]243 // (need to do it before openlog to prevent openlog from taking
244 // fd 3 (sock_fd==3))
245 xmove_fd(xsocket(AF_PACKET, SOCK_PACKET, htons(ETH_P_ARP)), sock_fd);
[1765]246 if (!FOREGROUND) {
[2725]247 // do it before all bb_xx_msg calls
[1765]248 openlog(applet_name, 0, LOG_DAEMON);
249 logmode |= LOGMODE_SYSLOG;
250 }
[3621]251 bb_logenv_override();
252
253 { // -l n.n.n.n
254 struct in_addr net;
255 if (inet_aton(l_opt, &net) == 0
256 || (net.s_addr & htonl(IN_CLASSB_NET)) != net.s_addr
257 ) {
258 bb_error_msg_and_die("invalid network address");
259 }
260 G.localnet_ip = ntohl(net.s_addr);
261 }
[1765]262 if (opts & 4) { // -r n.n.n.n
[3621]263 struct in_addr ip;
[1765]264 if (inet_aton(r_opt, &ip) == 0
[3621]265 || (ntohl(ip.s_addr) & IN_CLASSB_NET) != G.localnet_ip
[1765]266 ) {
267 bb_error_msg_and_die("invalid link address");
[821]268 }
[3621]269 chosen_nip = ip.s_addr;
[821]270 }
[2725]271 argv += optind - 1;
[821]272
[2725]273 /* Now: argv[0]:junk argv[1]:intf argv[2]:script argv[3]:NULL */
274 /* We need to make space for script argument: */
275 argv[0] = argv[1];
276 argv[1] = argv[2];
277 /* Now: argv[0]:intf argv[1]:script argv[2]:junk argv[3]:NULL */
278#define argv_intf (argv[0])
[1765]279
[2725]280 xsetenv("interface", argv_intf);
281
[3621]282 // Initialize the interface (modprobe, ifup, etc)
283 if (run(argv, "init", 0))
[821]284 return EXIT_FAILURE;
285
[3621]286 // Initialize G.iface_sockaddr
287 // G.iface_sockaddr is: { u16 sa_family; u8 sa_data[14]; }
288 //memset(&G.iface_sockaddr, 0, sizeof(G.iface_sockaddr));
[2725]289 //TODO: are we leaving sa_family == 0 (AF_UNSPEC)?!
[3621]290 safe_strncpy(G.iface_sockaddr.sa_data, argv_intf, sizeof(G.iface_sockaddr.sa_data));
[821]291
[3621]292 // Bind to the interface's ARP socket
293 xbind(sock_fd, &G.iface_sockaddr, sizeof(G.iface_sockaddr));
[821]294
[3621]295 // Get the interface's ethernet address
[1765]296 //memset(&ifr, 0, sizeof(ifr));
[2725]297 strncpy_IFNAMSIZ(ifr.ifr_name, argv_intf);
298 xioctl(sock_fd, SIOCGIFHWADDR, &ifr);
[3621]299 memcpy(&G.our_ethaddr, &ifr.ifr_hwaddr.sa_data, ETH_ALEN);
[821]300
[3621]301 // Start with some stable ip address, either a function of
[1765]302 // the hardware address or else the last address we used.
[2725]303 // we are taking low-order four bytes, as top-order ones
304 // aren't random enough.
[1765]305 // NOTE: the sequence of addresses we try changes only
306 // depending on when we detect conflicts.
[2725]307 {
308 uint32_t t;
[3621]309 move_from_unaligned32(t, ((char *)&G.our_ethaddr + 2));
[2725]310 srand(t);
311 }
[821]312 // FIXME cases to handle:
313 // - zcip already running!
314 // - link already has local address... just defend/update
315
[3621]316 // Daemonize now; don't delay system startup
[1765]317 if (!FOREGROUND) {
318#if BB_MMU
[2725]319 bb_daemonize(0 /*was: DAEMON_CHDIR_ROOT*/);
[1765]320#endif
[3621]321 bb_error_msg("start, interface %s", argv_intf);
[821]322 }
323
[3621]324 // Run the dynamic address negotiation protocol,
[821]325 // restarting after address conflicts:
326 // - start with some address we want to try
327 // - short random delay
[2725]328 // - arp probes to see if another host uses it
[3621]329 // 00:04:e2:64:23:c2 > ff:ff:ff:ff:ff:ff arp who-has 169.254.194.171 tell 0.0.0.0
[821]330 // - arp announcements that we're claiming it
[3621]331 // 00:04:e2:64:23:c2 > ff:ff:ff:ff:ff:ff arp who-has 169.254.194.171 (00:04:e2:64:23:c2) tell 169.254.194.171
[821]332 // - use it
333 // - defend it, within limits
[2725]334 // exit if:
335 // - address is successfully obtained and -q was given:
336 // run "<script> config", then exit with exitcode 0
337 // - poll error (when does this happen?)
338 // - read error (when does this happen?)
[3621]339 // - sendto error (in send_arp_request()) (when does this happen?)
[2725]340 // - revents & POLLERR (link down). run "<script> deconfig" first
[3621]341 if (chosen_nip == 0) {
342 new_nip_and_PROBE:
343 chosen_nip = pick_nip();
344 }
345 nsent = 0;
[2725]346 state = PROBE;
[821]347 while (1) {
348 struct pollfd fds[1];
[3621]349 unsigned deadline_us = deadline_us;
[821]350 struct arp_packet p;
[3621]351 int ip_conflict;
352 int n;
[821]353
[2725]354 fds[0].fd = sock_fd;
[821]355 fds[0].events = POLLIN;
356 fds[0].revents = 0;
357
[3621]358 // Poll, being ready to adjust current timeout
[1765]359 if (!timeout_ms) {
[2725]360 timeout_ms = random_delay_ms(PROBE_WAIT);
361 // FIXME setsockopt(sock_fd, SO_ATTACH_FILTER, ...) to
[821]362 // make the kernel filter out all packets except
363 // ones we'd care about.
364 }
[3621]365 if (timeout_ms >= 0) {
366 // Set deadline_us to the point in time when we timeout
367 deadline_us = MONOTONIC_US() + timeout_ms * 1000;
368 }
[821]369
[3621]370 VDBG("...wait %d %s nsent=%u\n",
371 timeout_ms, argv_intf, nsent);
[1765]372
[3621]373 n = safe_poll(fds, 1, timeout_ms);
374 if (n < 0) {
[2725]375 //bb_perror_msg("poll"); - done in safe_poll
376 return EXIT_FAILURE;
[3621]377 }
378 if (n == 0) { // timed out?
379 VDBG("state:%d\n", state);
[902]380 switch (state) {
381 case PROBE:
[3621]382 // No conflicting ARP packets were seen:
383 // we can progress through the states
384 if (nsent < PROBE_NUM) {
385 nsent++;
[1765]386 VDBG("probe/%u %s@%s\n",
[3621]387 nsent, argv_intf, nip_to_a(chosen_nip));
[1765]388 timeout_ms = PROBE_MIN * 1000;
[2725]389 timeout_ms += random_delay_ms(PROBE_MAX - PROBE_MIN);
[3621]390 send_arp_request(0, &null_ethaddr, chosen_nip);
391 continue;
[902]392 }
[3621]393 // Switch to announce state
394 nsent = 0;
[902]395 state = ANNOUNCE;
[3621]396 goto send_announce;
[902]397 case ANNOUNCE:
[3621]398 // No conflicting ARP packets were seen:
399 // we can progress through the states
400 if (nsent < ANNOUNCE_NUM) {
401 send_announce:
402 nsent++;
[1765]403 VDBG("announce/%u %s@%s\n",
[3621]404 nsent, argv_intf, nip_to_a(chosen_nip));
[1765]405 timeout_ms = ANNOUNCE_INTERVAL * 1000;
[3621]406 send_arp_request(chosen_nip, &G.our_ethaddr, chosen_nip);
407 continue;
[902]408 }
[3621]409 // Switch to monitor state
410 // FIXME update filters
411 run(argv, "config", chosen_nip);
412 // NOTE: all other exit paths should deconfig...
413 if (QUIT)
414 return EXIT_SUCCESS;
415 // fall through: switch to MONITOR
416 default:
417 // case DEFEND:
418 // case MONITOR: (shouldn't happen, MONITOR timeout is infinite)
419 // Defend period ended with no ARP replies - we won
420 timeout_ms = -1; // never timeout in monitor state
[902]421 state = MONITOR;
[3621]422 continue;
423 }
424 }
[2725]425
[3621]426 // Packet arrived, or link went down.
427 // We need to adjust the timeout in case we didn't receive
428 // a conflicting packet.
429 if (timeout_ms > 0) {
430 unsigned diff = deadline_us - MONOTONIC_US();
431 if ((int)(diff) < 0) {
432 // Current time is greater than the expected timeout time.
433 diff = 0;
[821]434 }
[3621]435 VDBG("adjusting timeout\n");
436 timeout_ms = (diff / 1000) | 1; // never 0
437 }
[902]438
[3621]439 if ((fds[0].revents & POLLIN) == 0) {
440 if (fds[0].revents & POLLERR) {
441 // FIXME: links routinely go down;
442 // this shouldn't necessarily exit.
443 bb_error_msg("iface %s is down", argv_intf);
444 if (state >= MONITOR) {
445 // Only if we are in MONITOR or DEFEND
446 run(argv, "deconfig", chosen_nip);
[821]447 }
[3621]448 return EXIT_FAILURE;
[821]449 }
[3621]450 continue;
451 }
[902]452
[3621]453 // Read ARP packet
454 if (safe_read(sock_fd, &p, sizeof(p)) < 0) {
455 bb_perror_msg_and_die(bb_msg_read_error);
456 }
457
458 if (p.eth.ether_type != htons(ETHERTYPE_ARP))
459 continue;
460 if (p.arp.arp_op != htons(ARPOP_REQUEST)
461 && p.arp.arp_op != htons(ARPOP_REPLY)
462 ) {
463 continue;
464 }
[902]465#ifdef DEBUG
[3621]466 {
467 struct ether_addr *sha = (struct ether_addr *) p.arp.arp_sha;
468 struct ether_addr *tha = (struct ether_addr *) p.arp.arp_tha;
469 struct in_addr *spa = (struct in_addr *) p.arp.arp_spa;
470 struct in_addr *tpa = (struct in_addr *) p.arp.arp_tpa;
471 VDBG("source=%s %s\n", ether_ntoa(sha), inet_ntoa(*spa));
472 VDBG("target=%s %s\n", ether_ntoa(tha), inet_ntoa(*tpa));
473 }
[902]474#endif
[3621]475 ip_conflict = 0;
476 if (memcmp(&p.arp.arp_sha, &G.our_ethaddr, ETH_ALEN) != 0) {
477 if (memcmp(p.arp.arp_spa, &chosen_nip, 4) == 0) {
478 // A probe or reply with source_ip == chosen ip
479 ip_conflict = 1;
[902]480 }
[2725]481 if (p.arp.arp_op == htons(ARPOP_REQUEST)
[3621]482 && memcmp(p.arp.arp_spa, &const_int_0, 4) == 0
483 && memcmp(p.arp.arp_tpa, &chosen_nip, 4) == 0
[2725]484 ) {
[3621]485 // A probe with source_ip == 0.0.0.0, target_ip == chosen ip:
486 // another host trying to claim this ip!
487 ip_conflict |= 2;
[902]488 }
[3621]489 }
490 VDBG("state:%d ip_conflict:%d\n", state, ip_conflict);
491 if (!ip_conflict)
492 continue;
[821]493
[3621]494 // Either src or target IP conflict exists
495 if (state <= ANNOUNCE) {
496 // PROBE or ANNOUNCE
497 conflicts++;
498 timeout_ms = PROBE_MIN * 1000
499 + CONFLICT_MULTIPLIER * random_delay_ms(conflicts);
500 goto new_nip_and_PROBE;
501 }
[902]502
[3621]503 // MONITOR or DEFEND: only src IP conflict is a problem
504 if (ip_conflict & 1) {
505 if (state == MONITOR) {
506 // Src IP conflict, defend with a single ARP probe
507 VDBG("monitor conflict - defending\n");
508 timeout_ms = DEFEND_INTERVAL * 1000;
509 state = DEFEND;
510 send_arp_request(chosen_nip, &G.our_ethaddr, chosen_nip);
511 continue;
512 }
513 // state == DEFEND
514 // Another src IP conflict, start over
515 VDBG("defend conflict - starting over\n");
516 run(argv, "deconfig", chosen_nip);
517 conflicts = 0;
518 timeout_ms = 0;
519 goto new_nip_and_PROBE;
520 }
521 // Note: if we only have a target IP conflict here (ip_conflict & 2),
522 // IOW: if we just saw this sort of ARP packet:
523 // aa:bb:cc:dd:ee:ff > xx:xx:xx:xx:xx:xx arp who-has <chosen_nip> tell 0.0.0.0
524 // we expect _kernel_ to respond to that, because <chosen_nip>
525 // is (expected to be) configured on this iface.
[2725]526 } // while (1)
527#undef argv_intf
[821]528}
Note: See TracBrowser for help on using the repository browser.