X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=networking%2Fzcip.c;h=45d1f7c1c25cf148825052075a0d20ea15eb9751;hb=a4d564ad7c24df2da1d8e03a7dd016f0a3d5edbd;hp=0b5bebec4d052c1d76d3a0730070c8d91951c52f;hpb=3e64e98570b2748973a66876a8b010c5d63e23be;p=oweals%2Fbusybox.git diff --git a/networking/zcip.c b/networking/zcip.c index 0b5bebec4..45d1f7c1c 100644 --- a/networking/zcip.c +++ b/networking/zcip.c @@ -6,7 +6,7 @@ * Copyright (C) 2003 by Arthur van Hoff (avh@strangeberry.com) * Copyright (C) 2004 by David Brownell * - * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. + * Licensed under GPLv2 or later, see file LICENSE in this source tree. */ /* @@ -23,25 +23,33 @@ // - avoid silent script failures, especially under load... // - link status monitoring (restart on link-up; stop on link-down) -#include -#include -#include +//usage:#define zcip_trivial_usage +//usage: "[OPTIONS] IFACE SCRIPT" +//usage:#define zcip_full_usage "\n\n" +//usage: "Manage a ZeroConf IPv4 link-local address\n" +//usage: "\n -f Run in foreground" +//usage: "\n -q Quit after obtaining address" +//usage: "\n -r 169.254.x.x Request this address first" +//usage: "\n -v Verbose" +//usage: "\n" +//usage: "\nWith no -q, runs continuously monitoring for ARP conflicts," +//usage: "\nexits only on I/O errors (link down etc)" + +#include "libbb.h" #include -#include #include #include -#include #include -#include "libbb.h" +#include /* We don't need more than 32 bits of the counter */ #define MONOTONIC_US() ((unsigned)monotonic_us()) struct arp_packet { - struct ether_header hdr; + struct ether_header eth; struct ether_arp arp; -} ATTRIBUTE_PACKED; +} PACKED; enum { /* 169.254.0.0 */ @@ -69,37 +77,56 @@ enum { DEFEND }; -#define VDBG(fmt,args...) \ - do { } while (0) +#define VDBG(...) do { } while (0) + + +enum { + sock_fd = 3 +}; + +struct globals { + struct sockaddr saddr; + struct ether_addr eth_addr; +} FIX_ALIASING; +#define G (*(struct globals*)&bb_common_bufsiz1) +#define saddr (G.saddr ) +#define eth_addr (G.eth_addr) +#define INIT_G() do { } while (0) + /** * Pick a random link local IP address on 169.254/16, except that * the first and last 256 addresses are reserved. */ -static void pick(struct in_addr *ip) +static uint32_t pick(void) { unsigned tmp; do { tmp = rand() & IN_CLASSB_HOST; } while (tmp > (IN_CLASSB_HOST - 0x0200)); - ip->s_addr = htonl((LINKLOCAL_ADDR + 0x0100) + tmp); + return htonl((LINKLOCAL_ADDR + 0x0100) + tmp); } /** * Broadcast an ARP packet. */ -static void arp(int fd, struct sockaddr *saddr, int op, - const struct ether_addr *source_addr, struct in_addr source_ip, - const struct ether_addr *target_addr, struct in_addr target_ip) +static void arp( + /* int op, - always ARPOP_REQUEST */ + /* const struct ether_addr *source_eth, - always ð_addr */ + struct in_addr source_ip, + const struct ether_addr *target_eth, struct in_addr target_ip) { + enum { op = ARPOP_REQUEST }; +#define source_eth (ð_addr) + struct arp_packet p; memset(&p, 0, sizeof(p)); // ether header - p.hdr.ether_type = htons(ETHERTYPE_ARP); - memcpy(p.hdr.ether_shost, source_addr, ETH_ALEN); - memset(p.hdr.ether_dhost, 0xff, ETH_ALEN); + p.eth.ether_type = htons(ETHERTYPE_ARP); + memcpy(p.eth.ether_shost, source_eth, ETH_ALEN); + memset(p.eth.ether_dhost, 0xff, ETH_ALEN); // arp request p.arp.arp_hrd = htons(ARPHRD_ETHER); @@ -107,48 +134,57 @@ static void arp(int fd, struct sockaddr *saddr, int op, p.arp.arp_hln = ETH_ALEN; p.arp.arp_pln = 4; p.arp.arp_op = htons(op); - memcpy(&p.arp.arp_sha, source_addr, ETH_ALEN); + memcpy(&p.arp.arp_sha, source_eth, ETH_ALEN); memcpy(&p.arp.arp_spa, &source_ip, sizeof(p.arp.arp_spa)); - memcpy(&p.arp.arp_tha, target_addr, ETH_ALEN); + memcpy(&p.arp.arp_tha, target_eth, ETH_ALEN); memcpy(&p.arp.arp_tpa, &target_ip, sizeof(p.arp.arp_tpa)); // send it - xsendto(fd, &p, sizeof(p), saddr, sizeof(*saddr)); - - // Currently all callers ignore errors, that's why returns are - // commented out... - //return 0; + // Even though sock_fd is already bound to saddr, just send() + // won't work, because "socket is not connected" + // (and connect() won't fix that, "operation not supported"). + // Thus we sendto() to saddr. I wonder which sockaddr + // (from bind() or from sendto()?) kernel actually uses + // to determine iface to emit the packet from... + xsendto(sock_fd, &p, sizeof(p), &saddr, sizeof(saddr)); +#undef source_eth } /** - * Run a script. argv[2] is already NULL. + * Run a script. + * argv[0]:intf argv[1]:script_name argv[2]:junk argv[3]:NULL */ -static int run(char *argv[3], const char *intf, struct in_addr *ip) +static int run(char *argv[3], const char *param, struct in_addr *ip) { int status; + char *addr = addr; /* for gcc */ + const char *fmt = "%s %s %s" + 3; + + argv[2] = (char*)param; - VDBG("%s run %s %s\n", intf, argv[0], argv[1]); + VDBG("%s run %s %s\n", argv[0], argv[1], argv[2]); if (ip) { - char *addr = inet_ntoa(*ip); - setenv("ip", addr, 1); - bb_info_msg("%s %s %s", argv[1], intf, addr); + addr = inet_ntoa(*ip); + xsetenv("ip", addr); + fmt -= 3; } + bb_info_msg(fmt, argv[2], argv[0], addr); - status = wait4pid(spawn(argv)); + status = spawn_and_wait(argv + 1); if (status < 0) { - bb_perror_msg("%s %s", argv[1], intf); + bb_perror_msg("%s %s %s" + 3, argv[2], argv[0]); return -errno; } if (status != 0) - bb_error_msg("script %s %s failed, exitcode=%d", argv[0], argv[1], status); + bb_error_msg("script %s %s failed, exitcode=%d", argv[1], argv[2], status & 0xff); return status; } /** * Return milliseconds of random delay, up to "secs" seconds. */ -static unsigned ALWAYS_INLINE ms_rdelay(unsigned secs) +static ALWAYS_INLINE unsigned random_delay_ms(unsigned secs) { return rand() % (secs * 1000); } @@ -156,25 +192,19 @@ static unsigned ALWAYS_INLINE ms_rdelay(unsigned secs) /** * main program */ -int zcip_main(int argc, char **argv); -int zcip_main(int argc, char **argv) +int zcip_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int zcip_main(int argc UNUSED_PARAM, char **argv) { - int state = PROBE; - struct ether_addr eth_addr; - const char *why; - int fd; + int state; char *r_opt; unsigned opts; - /* Ugly trick, but I want these zeroed in one go */ + // ugly trick, but I want these zeroed in one go struct { const struct in_addr null_ip; const struct ether_addr null_addr; - struct sockaddr saddr; struct in_addr ip; struct ifreq ifr; - char *intf; - char *script_av[3]; int timeout_ms; /* must be signed */ unsigned conflicts; unsigned nprobes; @@ -184,11 +214,8 @@ int zcip_main(int argc, char **argv) } L; #define null_ip (L.null_ip ) #define null_addr (L.null_addr ) -#define saddr (L.saddr ) #define ip (L.ip ) #define ifr (L.ifr ) -#define intf (L.intf ) -#define script_av (L.script_av ) #define timeout_ms (L.timeout_ms) #define conflicts (L.conflicts ) #define nprobes (L.nprobes ) @@ -197,15 +224,25 @@ int zcip_main(int argc, char **argv) #define verbose (L.verbose ) memset(&L, 0, sizeof(L)); + INIT_G(); #define FOREGROUND (opts & 1) #define QUIT (opts & 2) // parse commandline: prog [options] ifname script // exactly 2 args; -v accumulates and implies -f opt_complementary = "=2:vv:vf"; - opts = getopt32(argc, argv, "fqr:v", &r_opt, &verbose); + opts = getopt32(argv, "fqr:v", &r_opt, &verbose); +#if !BB_MMU + // on NOMMU reexec early (or else we will rerun things twice) + if (!FOREGROUND) + bb_daemonize_or_rexec(0 /*was: DAEMON_CHDIR_ROOT*/, argv); +#endif + // open an ARP socket + // (need to do it before openlog to prevent openlog from taking + // fd 3 (sock_fd==3)) + xmove_fd(xsocket(AF_PACKET, SOCK_PACKET, htons(ETH_P_ARP)), sock_fd); if (!FOREGROUND) { - /* Do it early, before all bb_xx_msg calls */ + // do it before all bb_xx_msg calls openlog(applet_name, 0, LOG_DAEMON); logmode |= LOGMODE_SYSLOG; } @@ -216,45 +253,49 @@ int zcip_main(int argc, char **argv) bb_error_msg_and_die("invalid link address"); } } - // On NOMMU reexec early (or else we will rerun things twice) -#if !BB_MMU - if (!FOREGROUND) - bb_daemonize_or_rexec(DAEMON_CHDIR_ROOT, argv); -#endif - argc -= optind; - argv += optind; + argv += optind - 1; + + /* Now: argv[0]:junk argv[1]:intf argv[2]:script argv[3]:NULL */ + /* We need to make space for script argument: */ + argv[0] = argv[1]; + argv[1] = argv[2]; + /* Now: argv[0]:intf argv[1]:script argv[2]:junk argv[3]:NULL */ +#define argv_intf (argv[0]) - intf = argv[0]; - script_av[0] = argv[1]; - setenv("interface", intf, 1); + xsetenv("interface", argv_intf); // initialize the interface (modprobe, ifup, etc) - script_av[1] = (char*)"init"; - if (run(script_av, intf, NULL)) + if (run(argv, "init", NULL)) return EXIT_FAILURE; // initialize saddr + // saddr is: { u16 sa_family; u8 sa_data[14]; } //memset(&saddr, 0, sizeof(saddr)); - safe_strncpy(saddr.sa_data, intf, sizeof(saddr.sa_data)); + //TODO: are we leaving sa_family == 0 (AF_UNSPEC)?! + safe_strncpy(saddr.sa_data, argv_intf, sizeof(saddr.sa_data)); - // open an ARP socket - fd = xsocket(PF_PACKET, SOCK_PACKET, htons(ETH_P_ARP)); // bind to the interface's ARP socket - xbind(fd, &saddr, sizeof(saddr)); + xbind(sock_fd, &saddr, sizeof(saddr)); // get the interface's ethernet address //memset(&ifr, 0, sizeof(ifr)); - strncpy(ifr.ifr_name, intf, sizeof(ifr.ifr_name)); - xioctl(fd, SIOCGIFHWADDR, &ifr); + strncpy_IFNAMSIZ(ifr.ifr_name, argv_intf); + xioctl(sock_fd, SIOCGIFHWADDR, &ifr); memcpy(ð_addr, &ifr.ifr_hwaddr.sa_data, ETH_ALEN); // start with some stable ip address, either a function of // the hardware address or else the last address we used. + // we are taking low-order four bytes, as top-order ones + // aren't random enough. // NOTE: the sequence of addresses we try changes only // depending on when we detect conflicts. - srand(*(unsigned*)&ifr.ifr_hwaddr.sa_data); + { + uint32_t t; + move_from_unaligned32(t, ((char *)ð_addr + 2)); + srand(t); + } if (ip.s_addr == 0) - pick(&ip); + ip.s_addr = pick(); // FIXME cases to handle: // - zcip already running! @@ -263,35 +304,42 @@ int zcip_main(int argc, char **argv) // daemonize now; don't delay system startup if (!FOREGROUND) { #if BB_MMU - bb_daemonize(DAEMON_CHDIR_ROOT); + bb_daemonize(0 /*was: DAEMON_CHDIR_ROOT*/); #endif - bb_info_msg("start, interface %s", intf); + bb_info_msg("start, interface %s", argv_intf); } // run the dynamic address negotiation protocol, // restarting after address conflicts: // - start with some address we want to try // - short random delay - // - arp probes to see if another host else uses it + // - arp probes to see if another host uses it // - arp announcements that we're claiming it // - use it // - defend it, within limits + // exit if: + // - address is successfully obtained and -q was given: + // run "