busybox/networking/zcip.c

549 lines
16 KiB
C
Raw Normal View History

/* vi: set sw=4 ts=4: */
/*
* RFC3927 ZeroConf IPv4 Link-Local addressing
* (see <http://www.zeroconf.org/>)
*
* Copyright (C) 2003 by Arthur van Hoff (avh@strangeberry.com)
* Copyright (C) 2004 by David Brownell
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
/*
* ZCIP just manages the 169.254.*.* addresses. That network is not
* routed at the IP level, though various proxies or bridges can
* certainly be used. Its naming is built over multicast DNS.
*/
//config:config ZCIP
//config: bool "zcip (8.4 kb)"
//config: default y
//config: select FEATURE_SYSLOG
//config: help
//config: ZCIP provides ZeroConf IPv4 address selection, according to RFC 3927.
//config: It's a daemon that allocates and defends a dynamically assigned
//config: address on the 169.254/16 network, requiring no system administrator.
//config:
//config: See http://www.zeroconf.org for further details, and "zcip.script"
//config: in the busybox examples.
//applet:IF_ZCIP(APPLET(zcip, BB_DIR_SBIN, BB_SUID_DROP))
//kbuild:lib-$(CONFIG_ZCIP) += zcip.o
//#define DEBUG
// TODO:
// - more real-world usage/testing, especially daemon mode
// - kernel packet filters to reduce scheduling noise
// - avoid silent script failures, especially under load...
// - link status monitoring (restart on link-up; stop on link-down)
//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 -l x.x.0.0 Use this range instead of 169.254"
//usage: "\n -v Verbose"
//usage: "\n"
//usage: "\n$LOGGING=none Suppress logging"
//usage: "\n$LOGGING=syslog Log to syslog"
//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 "common_bufsiz.h"
#include <netinet/ether.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <linux/sockios.h>
#include <syslog.h>
/* We don't need more than 32 bits of the counter */
#define MONOTONIC_US() ((unsigned)monotonic_us())
2005-12-13 15:58:25 +05:30
struct arp_packet {
struct ether_header eth;
struct ether_arp arp;
2008-07-05 14:48:54 +05:30
} PACKED;
enum {
/* 0-1 seconds before sending 1st probe */
PROBE_WAIT = 1,
/* 1-2 seconds between probes */
PROBE_MIN = 1,
PROBE_MAX = 2,
PROBE_NUM = 3, /* total probes to send */
ANNOUNCE_INTERVAL = 2, /* 2 seconds between announces */
ANNOUNCE_NUM = 3, /* announces to send */
/* if probe/announce sees a conflict, multiply RANDOM(NUM_CONFLICT) by... */
CONFLICT_MULTIPLIER = 2,
/* if we monitor and see a conflict, how long is defend state? */
DEFEND_INTERVAL = 10,
};
/* States during the configuration process. */
enum {
PROBE = 0,
ANNOUNCE,
MONITOR,
DEFEND
};
#define VDBG(...) do { } while (0)
enum {
sock_fd = 3
};
struct globals {
struct sockaddr iface_sockaddr;
struct ether_addr our_ethaddr;
uint32_t localnet_ip;
} FIX_ALIASING;
#define G (*(struct globals*)bb_common_bufsiz1)
#define INIT_G() do { setup_common_bufsiz(); } while (0)
/**
* Pick a random link local IP address on 169.254/16, except that
* the first and last 256 addresses are reserved.
*/
static uint32_t pick_nip(void)
{
2006-09-03 17:59:53 +05:30
unsigned tmp;
do {
tmp = rand() & IN_CLASSB_HOST;
} while (tmp > (IN_CLASSB_HOST - 0x0200));
return htonl((G.localnet_ip + 0x0100) + tmp);
}
static const char *nip_to_a(uint32_t nip)
{
struct in_addr in;
in.s_addr = nip;
return inet_ntoa(in);
}
/**
* Broadcast an ARP packet.
*/
static void send_arp_request(
/* int op, - always ARPOP_REQUEST */
/* const struct ether_addr *source_eth, - always &G.our_ethaddr */
uint32_t source_nip,
const struct ether_addr *target_eth, uint32_t target_nip)
{
enum { op = ARPOP_REQUEST };
#define source_eth (&G.our_ethaddr)
struct arp_packet p;
memset(&p, 0, sizeof(p));
// ether header
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);
p.arp.arp_pro = htons(ETHERTYPE_IP);
p.arp.arp_hln = ETH_ALEN;
p.arp.arp_pln = 4;
p.arp.arp_op = htons(op);
memcpy(&p.arp.arp_sha, source_eth, ETH_ALEN);
memcpy(&p.arp.arp_spa, &source_nip, 4);
memcpy(&p.arp.arp_tha, target_eth, ETH_ALEN);
memcpy(&p.arp.arp_tpa, &target_nip, 4);
// send it
// Even though sock_fd is already bound to G.iface_sockaddr, just send()
// won't work, because "socket is not connected"
// (and connect() won't fix that, "operation not supported").
// Thus we sendto() to G.iface_sockaddr. 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), &G.iface_sockaddr, sizeof(G.iface_sockaddr));
#undef source_eth
}
/**
* Run a script.
* argv[0]:intf argv[1]:script_name argv[2]:junk argv[3]:NULL
*/
static int run(char *argv[3], const char *param, uint32_t nip)
{
int status;
const char *addr = addr; /* for gcc */
const char *fmt = "%s %s %s" + 3;
char *env_ip = env_ip;
argv[2] = (char*)param;
VDBG("%s run %s %s\n", argv[0], argv[1], argv[2]);
if (nip != 0) {
addr = nip_to_a(nip);
/* Must not use setenv() repeatedly, it leaks memory. Use putenv() */
env_ip = xasprintf("ip=%s", addr);
putenv(env_ip);
fmt -= 3;
}
Optionally re-introduce bb_info_msg() Between Busybox 1.24.2 and 1.25.0 the bb_info_msg() function was eliminated and calls to it changed to be bb_error_msg(). The downside of this is that daemons now log all messages to syslog at the LOG_ERR level which makes it hard to filter errors from informational messages. This change optionally re-introduces bb_info_msg(), controlled by a new option FEATURE_SYSLOG_INFO, restores all the calls to bb_info_msg() that were removed (only in applets that set logmode to LOGMODE_SYSLOG or LOGMODE_BOTH), and also changes informational messages in ifplugd and ntpd. The code size change of this is as follows (using 'defconfig' on x86_64 with gcc 7.3.0-27ubuntu1~18.04) function old new delta bb_info_msg - 182 +182 bb_vinfo_msg - 27 +27 static.log7 194 198 +4 log8 190 191 +1 log5 190 191 +1 crondlog 45 - -45 ------------------------------------------------------------------------------ (add/remove: 2/1 grow/shrink: 3/0 up/down: 215/-45) Total: 170 bytes If you don't care about everything being logged at LOG_ERR level then when FEATURE_SYSLOG_INFO is disabled Busybox actually gets smaller: function old new delta static.log7 194 200 +6 log8 190 193 +3 log5 190 193 +3 syslog_level 1 - -1 bb_verror_msg 583 581 -2 crondlog 45 - -45 ------------------------------------------------------------------------------ (add/remove: 0/2 grow/shrink: 3/1 up/down: 12/-48) Total: -36 bytes Signed-off-by: James Byrne <james.byrne@origamienergy.com> Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2019-04-12 22:31:51 +05:30
bb_info_msg(fmt, argv[2], argv[0], addr);
status = spawn_and_wait(argv + 1);
if (nip != 0)
bb_unsetenv_and_free(env_ip);
if (status < 0) {
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[1], argv[2], status & 0xff);
return status;
}
/**
* Return milliseconds of random delay, up to "secs" seconds.
*/
static ALWAYS_INLINE unsigned random_delay_ms(unsigned secs)
{
return (unsigned)rand() % (secs * 1000);
}
/**
* main program
*/
int zcip_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int zcip_main(int argc UNUSED_PARAM, char **argv)
{
char *r_opt;
const char *l_opt = "169.254.0.0";
int state;
int nsent;
unsigned opts;
// Ugly trick, but I want these zeroed in one go
struct {
const struct ether_addr null_ethaddr;
struct ifreq ifr;
uint32_t chosen_nip;
int conflicts;
int timeout_ms; // must be signed
int verbose;
} L;
#define null_ethaddr (L.null_ethaddr)
#define ifr (L.ifr )
#define chosen_nip (L.chosen_nip )
#define conflicts (L.conflicts )
#define timeout_ms (L.timeout_ms )
#define verbose (L.verbose )
memset(&L, 0, sizeof(L));
INIT_G();
2006-09-03 17:53:56 +05:30
#define FOREGROUND (opts & 1)
#define QUIT (opts & 2)
// Parse commandline: prog [options] ifname script
// exactly 2 args; -v accumulates and implies -f
getopt32: remove opt_complementary function old new delta vgetopt32 1318 1392 +74 runsvdir_main 703 713 +10 bb_make_directory 423 425 +2 collect_cpu 546 545 -1 opt_chars 3 - -3 opt_complementary 4 - -4 tftpd_main 567 562 -5 ntp_init 476 471 -5 zcip_main 1266 1256 -10 xxd_main 428 418 -10 whois_main 140 130 -10 who_main 463 453 -10 which_main 212 202 -10 wget_main 2535 2525 -10 watchdog_main 291 281 -10 watch_main 222 212 -10 vlock_main 399 389 -10 uuencode_main 332 322 -10 uudecode_main 316 306 -10 unlink_main 45 35 -10 udhcpd_main 1482 1472 -10 udhcpc_main 2762 2752 -10 tune2fs_main 290 280 -10 tunctl_main 366 356 -10 truncate_main 218 208 -10 tr_main 518 508 -10 time_main 1134 1124 -10 tftp_main 286 276 -10 telnetd_main 1873 1863 -10 tcpudpsvd_main 1785 1775 -10 taskset_main 521 511 -10 tar_main 1009 999 -10 tail_main 1644 1634 -10 syslogd_main 1967 1957 -10 switch_root_main 368 358 -10 svlogd_main 1454 1444 -10 sv 1296 1286 -10 stat_main 104 94 -10 start_stop_daemon_main 1028 1018 -10 split_main 542 532 -10 sort_main 796 786 -10 slattach_main 624 614 -10 shuf_main 504 494 -10 setsid_main 96 86 -10 setserial_main 1132 1122 -10 setfont_main 388 378 -10 setconsole_main 78 68 -10 sendmail_main 1209 1199 -10 sed_main 677 667 -10 script_main 1077 1067 -10 run_parts_main 325 315 -10 rtcwake_main 454 444 -10 rm_main 175 165 -10 reformime_main 119 109 -10 readlink_main 123 113 -10 rdate_main 246 236 -10 pwdx_main 189 179 -10 pstree_main 317 307 -10 pscan_main 663 653 -10 popmaildir_main 818 808 -10 pmap_main 80 70 -10 nc_main 1042 1032 -10 mv_main 558 548 -10 mountpoint_main 477 467 -10 mount_main 1264 1254 -10 modprobe_main 768 758 -10 modinfo_main 333 323 -10 mktemp_main 200 190 -10 mkswap_main 324 314 -10 mkfs_vfat_main 1489 1479 -10 microcom_main 715 705 -10 md5_sha1_sum_main 521 511 -10 man_main 867 857 -10 makedevs_main 1052 1042 -10 ls_main 563 553 -10 losetup_main 432 422 -10 loadfont_main 89 79 -10 ln_main 524 514 -10 link_main 75 65 -10 ipcalc_main 544 534 -10 iostat_main 2397 2387 -10 install_main 768 758 -10 id_main 480 470 -10 i2cset_main 1239 1229 -10 i2cget_main 380 370 -10 i2cdump_main 1482 1472 -10 i2cdetect_main 682 672 -10 hwclock_main 406 396 -10 httpd_main 741 731 -10 grep_main 837 827 -10 getty_main 1559 1549 -10 fuser_main 297 287 -10 ftpgetput_main 345 335 -10 ftpd_main 2232 2222 -10 fstrim_main 251 241 -10 fsfreeze_main 77 67 -10 fsck_minix_main 2921 2911 -10 flock_main 314 304 -10 flashcp_main 740 730 -10 flash_eraseall_main 833 823 -10 fdformat_main 532 522 -10 expand_main 680 670 -10 eject_main 335 325 -10 dumpleases_main 630 620 -10 du_main 314 304 -10 dos2unix_main 441 431 -10 diff_main 1350 1340 -10 df_main 1064 1054 -10 date_main 1095 1085 -10 cut_main 961 951 -10 cryptpw_main 228 218 -10 crontab_main 575 565 -10 crond_main 1149 1139 -10 cp_main 370 360 -10 common_traceroute_main 3834 3824 -10 common_ping_main 1767 1757 -10 comm_main 239 229 -10 cmp_main 655 645 -10 chrt_main 379 369 -10 chpst_main 704 694 -10 chpasswd_main 308 298 -10 chown_main 171 161 -10 chmod_main 158 148 -10 cat_main 428 418 -10 bzip2_main 120 110 -10 blkdiscard_main 264 254 -10 base64_main 221 211 -10 arping_main 1665 1655 -10 ar_main 556 546 -10 adjtimex_main 406 396 -10 adduser_main 882 872 -10 addgroup_main 411 401 -10 acpid_main 1198 1188 -10 optstring 11 - -11 opt_string 18 - -18 OPT_STR 25 - -25 ubi_tools_main 1288 1258 -30 ls_options 31 - -31 ------------------------------------------------------------------------------ (add/remove: 0/6 grow/shrink: 3/129 up/down: 86/-1383) Total: -1297 bytes text data bss dec hex filename 915428 485 6876 922789 e14a5 busybox_old 914629 485 6872 921986 e1182 busybox_unstripped Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2017-08-09 01:25:02 +05:30
opts = getopt32(argv, "^" "fqr:l:v" "\0" "=2:vv:vf",
&r_opt, &l_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 before all bb_xx_msg calls
2006-10-04 02:30:43 +05:30
openlog(applet_name, 0, LOG_DAEMON);
logmode |= LOGMODE_SYSLOG;
}
bb_logenv_override();
{ // -l n.n.n.n
struct in_addr net;
if (inet_aton(l_opt, &net) == 0
|| (net.s_addr & htonl(IN_CLASSB_NET)) != net.s_addr
) {
libbb: reduce the overhead of single parameter bb_error_msg() calls Back in 2007, commit 0c97c9d43707 ("'simple' error message functions by Loic Grenie") introduced bb_simple_perror_msg() to allow for a lower overhead call to bb_perror_msg() when only a string was being printed with no parameters. This saves space for some CPU architectures because it avoids the overhead of a call to a variadic function. However there has never been a simple version of bb_error_msg(), and since 2007 many new calls to bb_perror_msg() have been added that only take a single parameter and so could have been using bb_simple_perror_message(). This changeset introduces 'simple' versions of bb_info_msg(), bb_error_msg(), bb_error_msg_and_die(), bb_herror_msg() and bb_herror_msg_and_die(), and replaces all calls that only take a single parameter, or use something like ("%s", arg), with calls to the corresponding 'simple' version. Since it is likely that single parameter calls to the variadic functions may be accidentally reintroduced in the future a new debugging config option WARN_SIMPLE_MSG has been introduced. This uses some macro magic which will cause any such calls to generate a warning, but this is turned off by default to avoid use of the unpleasant macros in normal circumstances. This is a large changeset due to the number of calls that have been replaced. The only files that contain changes other than simple substitution of function calls are libbb.h, libbb/herror_msg.c, libbb/verror_msg.c and libbb/xfuncs_printf.c. In miscutils/devfsd.c, networking/udhcp/common.h and util-linux/mdev.c additonal macros have been added for logging so that single parameter and multiple parameter logging variants exist. The amount of space saved varies considerably by architecture, and was found to be as follows (for 'defconfig' using GCC 7.4): Arm: -92 bytes MIPS: -52 bytes PPC: -1836 bytes x86_64: -938 bytes Note that for the MIPS architecture only an exception had to be made disabling the 'simple' calls for 'udhcp' (in networking/udhcp/common.h) because it made these files larger on MIPS. Signed-off-by: James Byrne <james.byrne@origamienergy.com> Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2019-07-02 15:05:03 +05:30
bb_simple_error_msg_and_die("invalid network address");
}
G.localnet_ip = ntohl(net.s_addr);
}
2006-09-03 17:59:53 +05:30
if (opts & 4) { // -r n.n.n.n
struct in_addr ip;
2006-09-03 17:57:25 +05:30
if (inet_aton(r_opt, &ip) == 0
|| (ntohl(ip.s_addr) & IN_CLASSB_NET) != G.localnet_ip
) {
libbb: reduce the overhead of single parameter bb_error_msg() calls Back in 2007, commit 0c97c9d43707 ("'simple' error message functions by Loic Grenie") introduced bb_simple_perror_msg() to allow for a lower overhead call to bb_perror_msg() when only a string was being printed with no parameters. This saves space for some CPU architectures because it avoids the overhead of a call to a variadic function. However there has never been a simple version of bb_error_msg(), and since 2007 many new calls to bb_perror_msg() have been added that only take a single parameter and so could have been using bb_simple_perror_message(). This changeset introduces 'simple' versions of bb_info_msg(), bb_error_msg(), bb_error_msg_and_die(), bb_herror_msg() and bb_herror_msg_and_die(), and replaces all calls that only take a single parameter, or use something like ("%s", arg), with calls to the corresponding 'simple' version. Since it is likely that single parameter calls to the variadic functions may be accidentally reintroduced in the future a new debugging config option WARN_SIMPLE_MSG has been introduced. This uses some macro magic which will cause any such calls to generate a warning, but this is turned off by default to avoid use of the unpleasant macros in normal circumstances. This is a large changeset due to the number of calls that have been replaced. The only files that contain changes other than simple substitution of function calls are libbb.h, libbb/herror_msg.c, libbb/verror_msg.c and libbb/xfuncs_printf.c. In miscutils/devfsd.c, networking/udhcp/common.h and util-linux/mdev.c additonal macros have been added for logging so that single parameter and multiple parameter logging variants exist. The amount of space saved varies considerably by architecture, and was found to be as follows (for 'defconfig' using GCC 7.4): Arm: -92 bytes MIPS: -52 bytes PPC: -1836 bytes x86_64: -938 bytes Note that for the MIPS architecture only an exception had to be made disabling the 'simple' calls for 'udhcp' (in networking/udhcp/common.h) because it made these files larger on MIPS. Signed-off-by: James Byrne <james.byrne@origamienergy.com> Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2019-07-02 15:05:03 +05:30
bb_simple_error_msg_and_die("invalid link address");
}
chosen_nip = ip.s_addr;
}
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])
xsetenv("interface", argv_intf);
// Initialize the interface (modprobe, ifup, etc)
if (run(argv, "init", 0))
return EXIT_FAILURE;
// Initialize G.iface_sockaddr
// G.iface_sockaddr is: { u16 sa_family; u8 sa_data[14]; }
//memset(&G.iface_sockaddr, 0, sizeof(G.iface_sockaddr));
//TODO: are we leaving sa_family == 0 (AF_UNSPEC)?!
safe_strncpy(G.iface_sockaddr.sa_data, argv_intf, sizeof(G.iface_sockaddr.sa_data));
// Bind to the interface's ARP socket
xbind(sock_fd, &G.iface_sockaddr, sizeof(G.iface_sockaddr));
2006-09-03 17:53:56 +05:30
// Get the interface's ethernet address
2007-02-15 02:20:22 +05:30
//memset(&ifr, 0, sizeof(ifr));
strncpy_IFNAMSIZ(ifr.ifr_name, argv_intf);
xioctl(sock_fd, SIOCGIFHWADDR, &ifr);
memcpy(&G.our_ethaddr, &ifr.ifr_hwaddr.sa_data, ETH_ALEN);
2006-09-03 17:53:56 +05:30
// Start with some stable ip address, either a function of
2006-09-03 17:53:56 +05:30
// 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.
2006-09-03 17:53:56 +05:30
// NOTE: the sequence of addresses we try changes only
// depending on when we detect conflicts.
{
uint32_t t;
move_from_unaligned32(t, ((char *)&G.our_ethaddr + 2));
srand(t);
}
// FIXME cases to handle:
// - zcip already running!
// - link already has local address... just defend/update
// Daemonize now; don't delay system startup
2006-09-03 17:57:25 +05:30
if (!FOREGROUND) {
#if BB_MMU
bb_daemonize(0 /*was: DAEMON_CHDIR_ROOT*/);
#endif
Optionally re-introduce bb_info_msg() Between Busybox 1.24.2 and 1.25.0 the bb_info_msg() function was eliminated and calls to it changed to be bb_error_msg(). The downside of this is that daemons now log all messages to syslog at the LOG_ERR level which makes it hard to filter errors from informational messages. This change optionally re-introduces bb_info_msg(), controlled by a new option FEATURE_SYSLOG_INFO, restores all the calls to bb_info_msg() that were removed (only in applets that set logmode to LOGMODE_SYSLOG or LOGMODE_BOTH), and also changes informational messages in ifplugd and ntpd. The code size change of this is as follows (using 'defconfig' on x86_64 with gcc 7.3.0-27ubuntu1~18.04) function old new delta bb_info_msg - 182 +182 bb_vinfo_msg - 27 +27 static.log7 194 198 +4 log8 190 191 +1 log5 190 191 +1 crondlog 45 - -45 ------------------------------------------------------------------------------ (add/remove: 2/1 grow/shrink: 3/0 up/down: 215/-45) Total: 170 bytes If you don't care about everything being logged at LOG_ERR level then when FEATURE_SYSLOG_INFO is disabled Busybox actually gets smaller: function old new delta static.log7 194 200 +6 log8 190 193 +3 log5 190 193 +3 syslog_level 1 - -1 bb_verror_msg 583 581 -2 crondlog 45 - -45 ------------------------------------------------------------------------------ (add/remove: 0/2 grow/shrink: 3/1 up/down: 12/-48) Total: -36 bytes Signed-off-by: James Byrne <james.byrne@origamienergy.com> Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2019-04-12 22:31:51 +05:30
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 uses it
// 00:04:e2:64:23:c2 > ff:ff:ff:ff:ff:ff arp who-has 169.254.194.171 tell 0.0.0.0
// - arp announcements that we're claiming it
// 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
// - use it
// - defend it, within limits
// exit if:
// - address is successfully obtained and -q was given:
// run "<script> config", then exit with exitcode 0
// - poll error (when does this happen?)
// - read error (when does this happen?)
// - sendto error (in send_arp_request()) (when does this happen?)
// - revents & POLLERR (link down). run "<script> deconfig" first
if (chosen_nip == 0) {
new_nip_and_PROBE:
chosen_nip = pick_nip();
}
nsent = 0;
state = PROBE;
while (1) {
struct pollfd fds[1];
unsigned deadline_us = deadline_us;
struct arp_packet p;
int ip_conflict;
int n;
fds[0].fd = sock_fd;
fds[0].events = POLLIN;
fds[0].revents = 0;
// Poll, being ready to adjust current timeout
if (!timeout_ms) {
timeout_ms = random_delay_ms(PROBE_WAIT);
// FIXME setsockopt(sock_fd, SO_ATTACH_FILTER, ...) to
// make the kernel filter out all packets except
// ones we'd care about.
}
if (timeout_ms >= 0) {
// Set deadline_us to the point in time when we timeout
deadline_us = MONOTONIC_US() + timeout_ms * 1000;
}
2006-09-17 21:58:10 +05:30
VDBG("...wait %d %s nsent=%u\n",
timeout_ms, argv_intf, nsent);
n = safe_poll(fds, 1, timeout_ms);
if (n < 0) {
//bb_perror_msg("poll"); - done in safe_poll
return EXIT_FAILURE;
}
if (n == 0) { // timed out?
VDBG("state:%d\n", state);
switch (state) {
case PROBE:
// No conflicting ARP packets were seen:
// we can progress through the states
if (nsent < PROBE_NUM) {
nsent++;
VDBG("probe/%u %s@%s\n",
nsent, argv_intf, nip_to_a(chosen_nip));
timeout_ms = PROBE_MIN * 1000;
timeout_ms += random_delay_ms(PROBE_MAX - PROBE_MIN);
send_arp_request(0, &null_ethaddr, chosen_nip);
continue;
}
// Switch to announce state
nsent = 0;
state = ANNOUNCE;
goto send_announce;
case ANNOUNCE:
// No conflicting ARP packets were seen:
// we can progress through the states
if (nsent < ANNOUNCE_NUM) {
send_announce:
nsent++;
VDBG("announce/%u %s@%s\n",
nsent, argv_intf, nip_to_a(chosen_nip));
timeout_ms = ANNOUNCE_INTERVAL * 1000;
send_arp_request(chosen_nip, &G.our_ethaddr, chosen_nip);
continue;
}
// Switch to monitor state
// FIXME update filters
run(argv, "config", chosen_nip);
// NOTE: all other exit paths should deconfig...
if (QUIT)
return EXIT_SUCCESS;
// fall through: switch to MONITOR
default:
// case DEFEND:
// case MONITOR: (shouldn't happen, MONITOR timeout is infinite)
// Defend period ended with no ARP replies - we won
timeout_ms = -1; // never timeout in monitor state
state = MONITOR;
continue;
}
}
// Packet arrived, or link went down.
// We need to adjust the timeout in case we didn't receive
// a conflicting packet.
if (timeout_ms > 0) {
unsigned diff = deadline_us - MONOTONIC_US();
if ((int)(diff) < 0) {
// Current time is greater than the expected timeout time.
diff = 0;
}
VDBG("adjusting timeout\n");
timeout_ms = (diff / 1000) | 1; // never 0
}
if ((fds[0].revents & POLLIN) == 0) {
if (fds[0].revents & POLLERR) {
// FIXME: links routinely go down;
// this shouldn't necessarily exit.
bb_error_msg("iface %s is down", argv_intf);
if (state >= MONITOR) {
// Only if we are in MONITOR or DEFEND
run(argv, "deconfig", chosen_nip);
}
return EXIT_FAILURE;
}
continue;
}
// Read ARP packet
if (safe_read(sock_fd, &p, sizeof(p)) < 0) {
libbb: reduce the overhead of single parameter bb_error_msg() calls Back in 2007, commit 0c97c9d43707 ("'simple' error message functions by Loic Grenie") introduced bb_simple_perror_msg() to allow for a lower overhead call to bb_perror_msg() when only a string was being printed with no parameters. This saves space for some CPU architectures because it avoids the overhead of a call to a variadic function. However there has never been a simple version of bb_error_msg(), and since 2007 many new calls to bb_perror_msg() have been added that only take a single parameter and so could have been using bb_simple_perror_message(). This changeset introduces 'simple' versions of bb_info_msg(), bb_error_msg(), bb_error_msg_and_die(), bb_herror_msg() and bb_herror_msg_and_die(), and replaces all calls that only take a single parameter, or use something like ("%s", arg), with calls to the corresponding 'simple' version. Since it is likely that single parameter calls to the variadic functions may be accidentally reintroduced in the future a new debugging config option WARN_SIMPLE_MSG has been introduced. This uses some macro magic which will cause any such calls to generate a warning, but this is turned off by default to avoid use of the unpleasant macros in normal circumstances. This is a large changeset due to the number of calls that have been replaced. The only files that contain changes other than simple substitution of function calls are libbb.h, libbb/herror_msg.c, libbb/verror_msg.c and libbb/xfuncs_printf.c. In miscutils/devfsd.c, networking/udhcp/common.h and util-linux/mdev.c additonal macros have been added for logging so that single parameter and multiple parameter logging variants exist. The amount of space saved varies considerably by architecture, and was found to be as follows (for 'defconfig' using GCC 7.4): Arm: -92 bytes MIPS: -52 bytes PPC: -1836 bytes x86_64: -938 bytes Note that for the MIPS architecture only an exception had to be made disabling the 'simple' calls for 'udhcp' (in networking/udhcp/common.h) because it made these files larger on MIPS. Signed-off-by: James Byrne <james.byrne@origamienergy.com> Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2019-07-02 15:05:03 +05:30
bb_simple_perror_msg_and_die(bb_msg_read_error);
}
if (p.eth.ether_type != htons(ETHERTYPE_ARP))
continue;
if (p.arp.arp_op != htons(ARPOP_REQUEST)
&& p.arp.arp_op != htons(ARPOP_REPLY)
) {
continue;
}
#ifdef DEBUG
{
struct ether_addr *sha = (struct ether_addr *) p.arp.arp_sha;
struct ether_addr *tha = (struct ether_addr *) p.arp.arp_tha;
struct in_addr *spa = (struct in_addr *) p.arp.arp_spa;
struct in_addr *tpa = (struct in_addr *) p.arp.arp_tpa;
VDBG("source=%s %s\n", ether_ntoa(sha), inet_ntoa(*spa));
VDBG("target=%s %s\n", ether_ntoa(tha), inet_ntoa(*tpa));
}
#endif
ip_conflict = 0;
if (memcmp(&p.arp.arp_sha, &G.our_ethaddr, ETH_ALEN) != 0) {
if (memcmp(p.arp.arp_spa, &chosen_nip, 4) == 0) {
// A probe or reply with source_ip == chosen ip
ip_conflict = 1;
}
if (p.arp.arp_op == htons(ARPOP_REQUEST)
&& memcmp(p.arp.arp_spa, &const_int_0, 4) == 0
&& memcmp(p.arp.arp_tpa, &chosen_nip, 4) == 0
) {
// A probe with source_ip == 0.0.0.0, target_ip == chosen ip:
// another host trying to claim this ip!
ip_conflict |= 2;
}
}
VDBG("state:%d ip_conflict:%d\n", state, ip_conflict);
if (!ip_conflict)
continue;
// Either src or target IP conflict exists
if (state <= ANNOUNCE) {
// PROBE or ANNOUNCE
conflicts++;
timeout_ms = PROBE_MIN * 1000
+ CONFLICT_MULTIPLIER * random_delay_ms(conflicts);
goto new_nip_and_PROBE;
}
// MONITOR or DEFEND: only src IP conflict is a problem
if (ip_conflict & 1) {
if (state == MONITOR) {
// Src IP conflict, defend with a single ARP probe
VDBG("monitor conflict - defending\n");
timeout_ms = DEFEND_INTERVAL * 1000;
state = DEFEND;
send_arp_request(chosen_nip, &G.our_ethaddr, chosen_nip);
continue;
}
// state == DEFEND
// Another src IP conflict, start over
VDBG("defend conflict - starting over\n");
run(argv, "deconfig", chosen_nip);
conflicts = 0;
timeout_ms = 0;
goto new_nip_and_PROBE;
}
// Note: if we only have a target IP conflict here (ip_conflict & 2),
// IOW: if we just saw this sort of ARP packet:
// aa:bb:cc:dd:ee:ff > xx:xx:xx:xx:xx:xx arp who-has <chosen_nip> tell 0.0.0.0
// we expect _kernel_ to respond to that, because <chosen_nip>
// is (expected to be) configured on this iface.
} // while (1)
#undef argv_intf
}