udhcpd: add option for tweaking arpping

Some clients have a very short timeout for sending the DHCP
DISCOVER, shorter than the arpping timeout of 2000 milliseconds
that udhcpd uses by default.

This patch allows tweaking the timeout, or disabling of arpping
altogether, at the risk of handing out addresses which are
already in use.

function                                             old     new   delta
udhcpd_main                                         1460    1501     +41
udhcpc_main                                         2814    2851     +37
packed_usage                                       29957   29974     +17
arpping                                              477     493     +16
find_free_or_expired_nip                             161     174     +13
send_offer                                           285     292      +7
nobody_responds_to_arp                                85      89      +4
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 7/0 up/down: 135/0)             Total: 135 bytes

Signed-off-by: Michel Stam <m.stam@fugro.nl>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
Michel Stam 2014-10-30 11:59:04 +01:00 committed by Denys Vlasenko
parent 760d035699
commit 9f41271f3c
6 changed files with 39 additions and 21 deletions

View File

@ -39,7 +39,8 @@ int FAST_FUNC arpping(uint32_t test_nip,
const uint8_t *safe_mac, const uint8_t *safe_mac,
uint32_t from_ip, uint32_t from_ip,
uint8_t *from_mac, uint8_t *from_mac,
const char *interface) const char *interface,
unsigned timeo)
{ {
int timeout_ms; int timeout_ms;
struct pollfd pfd[1]; struct pollfd pfd[1];
@ -48,6 +49,9 @@ int FAST_FUNC arpping(uint32_t test_nip,
struct sockaddr addr; /* for interface name */ struct sockaddr addr; /* for interface name */
struct arpMsg arp; struct arpMsg arp;
if (!timeo)
return 1;
s = socket(PF_PACKET, SOCK_PACKET, htons(ETH_P_ARP)); s = socket(PF_PACKET, SOCK_PACKET, htons(ETH_P_ARP));
if (s == -1) { if (s == -1) {
bb_perror_msg(bb_msg_can_not_create_raw_socket); bb_perror_msg(bb_msg_can_not_create_raw_socket);
@ -83,7 +87,7 @@ int FAST_FUNC arpping(uint32_t test_nip,
} }
/* wait for arp reply, and check it */ /* wait for arp reply, and check it */
timeout_ms = 2000; timeout_ms = (int)timeo;
do { do {
typedef uint32_t aliased_uint32_t FIX_ALIASING; typedef uint32_t aliased_uint32_t FIX_ALIASING;
int r; int r;
@ -124,7 +128,7 @@ int FAST_FUNC arpping(uint32_t test_nip,
* this is more under/overflow-resistant * this is more under/overflow-resistant
* (people did see overflows here when system time jumps): * (people did see overflows here when system time jumps):
*/ */
} while ((unsigned)timeout_ms <= 2000); } while ((unsigned)timeout_ms <= timeo);
ret: ret:
close(s); close(s);

View File

@ -311,7 +311,8 @@ int arpping(uint32_t test_nip,
const uint8_t *safe_mac, const uint8_t *safe_mac,
uint32_t from_ip, uint32_t from_ip,
uint8_t *from_mac, uint8_t *from_mac,
const char *interface) FAST_FUNC; const char *interface,
unsigned timeo) FAST_FUNC;
/* note: ip is a pointer to an IPv6 in network order, possibly misaliged */ /* note: ip is a pointer to an IPv6 in network order, possibly misaliged */
int sprint_nip6(char *dest, /*const char *pre,*/ const uint8_t *ip) FAST_FUNC; int sprint_nip6(char *dest, /*const char *pre,*/ const uint8_t *ip) FAST_FUNC;

View File

@ -54,7 +54,7 @@ static const char udhcpc_longopts[] ALIGN1 =
"foreground\0" No_argument "f" "foreground\0" No_argument "f"
"background\0" No_argument "b" "background\0" No_argument "b"
"broadcast\0" No_argument "B" "broadcast\0" No_argument "B"
IF_FEATURE_UDHCPC_ARPING("arping\0" No_argument "a") IF_FEATURE_UDHCPC_ARPING("arping\0" Optional_argument "a")
IF_FEATURE_UDHCP_PORT("client-port\0" Required_argument "P") IF_FEATURE_UDHCP_PORT("client-port\0" Required_argument "P")
; ;
#endif #endif
@ -1150,7 +1150,7 @@ static void client_background(void)
//usage:# define IF_UDHCP_VERBOSE(...) //usage:# define IF_UDHCP_VERBOSE(...)
//usage:#endif //usage:#endif
//usage:#define udhcpc_trivial_usage //usage:#define udhcpc_trivial_usage
//usage: "[-fbq"IF_UDHCP_VERBOSE("v")IF_FEATURE_UDHCPC_ARPING("a")"RB] [-t N] [-T SEC] [-A SEC/-n]\n" //usage: "[-fbq"IF_UDHCP_VERBOSE("v")"RB]"IF_FEATURE_UDHCPC_ARPING(" [-a[MSEC]]")" [-t N] [-T SEC] [-A SEC/-n]\n"
//usage: " [-i IFACE]"IF_FEATURE_UDHCP_PORT(" [-P PORT]")" [-s PROG] [-p PIDFILE]\n" //usage: " [-i IFACE]"IF_FEATURE_UDHCP_PORT(" [-P PORT]")" [-s PROG] [-p PIDFILE]\n"
//usage: " [-oC] [-r IP] [-V VENDOR] [-F NAME] [-x OPT:VAL]... [-O OPT]..." //usage: " [-oC] [-r IP] [-V VENDOR] [-F NAME] [-x OPT:VAL]... [-O OPT]..."
//usage:#define udhcpc_full_usage "\n" //usage:#define udhcpc_full_usage "\n"
@ -1174,7 +1174,7 @@ static void client_background(void)
//usage: ) //usage: )
//usage: "\n -S,--syslog Log to syslog too" //usage: "\n -S,--syslog Log to syslog too"
//usage: IF_FEATURE_UDHCPC_ARPING( //usage: IF_FEATURE_UDHCPC_ARPING(
//usage: "\n -a,--arping Use arping to validate offered address" //usage: "\n -a[MSEC],--arping[=MSEC] Validate offered address with ARP ping"
//usage: ) //usage: )
//usage: "\n -r,--request IP Request this IP address" //usage: "\n -r,--request IP Request this IP address"
//usage: "\n -o,--no-default-options Don't request any options (unless -O is given)" //usage: "\n -o,--no-default-options Don't request any options (unless -O is given)"
@ -1211,7 +1211,7 @@ static void client_background(void)
//usage: ) //usage: )
//usage: "\n -S Log to syslog too" //usage: "\n -S Log to syslog too"
//usage: IF_FEATURE_UDHCPC_ARPING( //usage: IF_FEATURE_UDHCPC_ARPING(
//usage: "\n -a Use arping to validate offered address" //usage: "\n -a[MSEC] Validate offered address with ARP ping"
//usage: ) //usage: )
//usage: "\n -r IP Request this IP address" //usage: "\n -r IP Request this IP address"
//usage: "\n -o Don't request any options (unless -O is given)" //usage: "\n -o Don't request any options (unless -O is given)"
@ -1238,6 +1238,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
{ {
uint8_t *message; uint8_t *message;
const char *str_V, *str_h, *str_F, *str_r; const char *str_V, *str_h, *str_F, *str_r;
IF_FEATURE_UDHCPC_ARPING(const char *str_a = "2000";)
IF_FEATURE_UDHCP_PORT(char *str_P;) IF_FEATURE_UDHCP_PORT(char *str_P;)
void *clientid_mac_ptr; void *clientid_mac_ptr;
llist_t *list_O = NULL; llist_t *list_O = NULL;
@ -1252,6 +1253,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
int timeout; /* must be signed */ int timeout; /* must be signed */
unsigned already_waited_sec; unsigned already_waited_sec;
unsigned opt; unsigned opt;
IF_FEATURE_UDHCPC_ARPING(unsigned arpping_ms;)
int max_fd; int max_fd;
int retval; int retval;
fd_set rfds; fd_set rfds;
@ -1269,7 +1271,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
IF_LONG_OPTS(applet_long_options = udhcpc_longopts;) IF_LONG_OPTS(applet_long_options = udhcpc_longopts;)
opt = getopt32(argv, "CV:H:h:F:i:np:qRr:s:T:t:SA:O:ox:fB" opt = getopt32(argv, "CV:H:h:F:i:np:qRr:s:T:t:SA:O:ox:fB"
USE_FOR_MMU("b") USE_FOR_MMU("b")
IF_FEATURE_UDHCPC_ARPING("a") IF_FEATURE_UDHCPC_ARPING("a::")
IF_FEATURE_UDHCP_PORT("P:") IF_FEATURE_UDHCP_PORT("P:")
"v" "v"
, &str_V, &str_h, &str_h, &str_F , &str_V, &str_h, &str_h, &str_F
@ -1278,6 +1280,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
, &discover_timeout, &discover_retries, &tryagain_timeout /* T,t,A */ , &discover_timeout, &discover_retries, &tryagain_timeout /* T,t,A */
, &list_O , &list_O
, &list_x , &list_x
IF_FEATURE_UDHCPC_ARPING(, &str_a)
IF_FEATURE_UDHCP_PORT(, &str_P) IF_FEATURE_UDHCP_PORT(, &str_P)
IF_UDHCP_VERBOSE(, &dhcp_verbose) IF_UDHCP_VERBOSE(, &dhcp_verbose)
); );
@ -1309,6 +1312,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
SERVER_PORT = CLIENT_PORT - 1; SERVER_PORT = CLIENT_PORT - 1;
} }
#endif #endif
IF_FEATURE_UDHCPC_ARPING(arpping_ms = xatou(str_a);)
while (list_O) { while (list_O) {
char *optstr = llist_pop(&list_O); char *optstr = llist_pop(&list_O);
unsigned n = bb_strtou(optstr, NULL, 0); unsigned n = bb_strtou(optstr, NULL, 0);
@ -1726,7 +1730,8 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv)
NULL, NULL,
(uint32_t) 0, (uint32_t) 0,
client_config.client_mac, client_config.client_mac,
client_config.interface) client_config.interface,
arpping_ms)
) { ) {
bb_info_msg("Offered address is in use " bb_info_msg("Offered address is in use "
"(got ARP reply), declining"); "(got ARP reply), declining");

View File

@ -28,6 +28,7 @@
//usage: "\n -f Run in foreground" //usage: "\n -f Run in foreground"
//usage: "\n -S Log to syslog too" //usage: "\n -S Log to syslog too"
//usage: "\n -I ADDR Local address" //usage: "\n -I ADDR Local address"
//usage: "\n -a MSEC Timeout for ARP ping (default 2000)"
//usage: IF_FEATURE_UDHCP_PORT( //usage: IF_FEATURE_UDHCP_PORT(
//usage: "\n -P N Use port N (default 67)" //usage: "\n -P N Use port N (default 67)"
//usage: ) //usage: )
@ -148,7 +149,8 @@ static uint32_t select_lease_time(struct dhcp_packet *packet)
static NOINLINE void send_offer(struct dhcp_packet *oldpacket, static NOINLINE void send_offer(struct dhcp_packet *oldpacket,
uint32_t static_lease_nip, uint32_t static_lease_nip,
struct dyn_lease *lease, struct dyn_lease *lease,
uint8_t *requested_ip_opt) uint8_t *requested_ip_opt,
unsigned arpping_ms)
{ {
struct dhcp_packet packet; struct dhcp_packet packet;
uint32_t lease_time_sec; uint32_t lease_time_sec;
@ -187,7 +189,7 @@ static NOINLINE void send_offer(struct dhcp_packet *oldpacket,
} }
else { else {
/* Otherwise, find a free IP */ /* Otherwise, find a free IP */
packet.yiaddr = find_free_or_expired_nip(oldpacket->chaddr); packet.yiaddr = find_free_or_expired_nip(oldpacket->chaddr, arpping_ms);
} }
if (!packet.yiaddr) { if (!packet.yiaddr) {
@ -304,6 +306,8 @@ int udhcpd_main(int argc UNUSED_PARAM, char **argv)
unsigned opt; unsigned opt;
struct option_set *option; struct option_set *option;
char *str_I = str_I; char *str_I = str_I;
const char *str_a = "2000";
unsigned arpping_ms;
IF_FEATURE_UDHCP_PORT(char *str_P;) IF_FEATURE_UDHCP_PORT(char *str_P;)
#if ENABLE_FEATURE_UDHCP_PORT #if ENABLE_FEATURE_UDHCP_PORT
@ -314,9 +318,10 @@ int udhcpd_main(int argc UNUSED_PARAM, char **argv)
#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1 #if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1
opt_complementary = "vv"; opt_complementary = "vv";
#endif #endif
opt = getopt32(argv, "fSI:v" opt = getopt32(argv, "fSI:va:"
IF_FEATURE_UDHCP_PORT("P:") IF_FEATURE_UDHCP_PORT("P:")
, &str_I , &str_I
, &str_a
IF_FEATURE_UDHCP_PORT(, &str_P) IF_FEATURE_UDHCP_PORT(, &str_P)
IF_UDHCP_VERBOSE(, &dhcp_verbose) IF_UDHCP_VERBOSE(, &dhcp_verbose)
); );
@ -336,11 +341,13 @@ int udhcpd_main(int argc UNUSED_PARAM, char **argv)
free(lsa); free(lsa);
} }
#if ENABLE_FEATURE_UDHCP_PORT #if ENABLE_FEATURE_UDHCP_PORT
if (opt & 16) { /* -P */ if (opt & 32) { /* -P */
SERVER_PORT = xatou16(str_P); SERVER_PORT = xatou16(str_P);
CLIENT_PORT = SERVER_PORT + 1; CLIENT_PORT = SERVER_PORT + 1;
} }
#endif #endif
arpping_ms = xatou(str_a);
/* Would rather not do read_config before daemonization - /* Would rather not do read_config before daemonization -
* otherwise NOMMU machines will parse config twice */ * otherwise NOMMU machines will parse config twice */
read_config(argv[0] ? argv[0] : DHCPD_CONF_FILE); read_config(argv[0] ? argv[0] : DHCPD_CONF_FILE);
@ -498,7 +505,7 @@ int udhcpd_main(int argc UNUSED_PARAM, char **argv)
case DHCPDISCOVER: case DHCPDISCOVER:
log1("Received DISCOVER"); log1("Received DISCOVER");
send_offer(&packet, static_lease_nip, lease, requested_ip_opt); send_offer(&packet, static_lease_nip, lease, requested_ip_opt, arpping_ms);
break; break;
case DHCPREQUEST: case DHCPREQUEST:

View File

@ -100,7 +100,7 @@ struct dyn_lease *add_lease(
int is_expired_lease(struct dyn_lease *lease) FAST_FUNC; int is_expired_lease(struct dyn_lease *lease) FAST_FUNC;
struct dyn_lease *find_lease_by_mac(const uint8_t *mac) FAST_FUNC; struct dyn_lease *find_lease_by_mac(const uint8_t *mac) FAST_FUNC;
struct dyn_lease *find_lease_by_nip(uint32_t nip) FAST_FUNC; struct dyn_lease *find_lease_by_nip(uint32_t nip) FAST_FUNC;
uint32_t find_free_or_expired_nip(const uint8_t *safe_mac) FAST_FUNC; uint32_t find_free_or_expired_nip(const uint8_t *safe_mac, unsigned arpping_ms) FAST_FUNC;
/* Config file parser will pass static lease info to this function /* Config file parser will pass static lease info to this function

View File

@ -112,7 +112,7 @@ struct dyn_lease* FAST_FUNC find_lease_by_nip(uint32_t nip)
} }
/* Check if the IP is taken; if it is, add it to the lease table */ /* Check if the IP is taken; if it is, add it to the lease table */
static int nobody_responds_to_arp(uint32_t nip, const uint8_t *safe_mac) static int nobody_responds_to_arp(uint32_t nip, const uint8_t *safe_mac, unsigned arpping_ms)
{ {
struct in_addr temp; struct in_addr temp;
int r; int r;
@ -120,7 +120,8 @@ static int nobody_responds_to_arp(uint32_t nip, const uint8_t *safe_mac)
r = arpping(nip, safe_mac, r = arpping(nip, safe_mac,
server_config.server_nip, server_config.server_nip,
server_config.server_mac, server_config.server_mac,
server_config.interface); server_config.interface,
arpping_ms);
if (r) if (r)
return r; return r;
@ -132,7 +133,7 @@ static int nobody_responds_to_arp(uint32_t nip, const uint8_t *safe_mac)
} }
/* Find a new usable (we think) address */ /* Find a new usable (we think) address */
uint32_t FAST_FUNC find_free_or_expired_nip(const uint8_t *safe_mac) uint32_t FAST_FUNC find_free_or_expired_nip(const uint8_t *safe_mac, unsigned arpping_ms)
{ {
uint32_t addr; uint32_t addr;
struct dyn_lease *oldest_lease = NULL; struct dyn_lease *oldest_lease = NULL;
@ -177,7 +178,7 @@ uint32_t FAST_FUNC find_free_or_expired_nip(const uint8_t *safe_mac)
lease = find_lease_by_nip(nip); lease = find_lease_by_nip(nip);
if (!lease) { if (!lease) {
//TODO: DHCP servers do not always sit on the same subnet as clients: should *ping*, not arp-ping! //TODO: DHCP servers do not always sit on the same subnet as clients: should *ping*, not arp-ping!
if (nobody_responds_to_arp(nip, safe_mac)) if (nobody_responds_to_arp(nip, safe_mac, arpping_ms))
return nip; return nip;
} else { } else {
if (!oldest_lease || lease->expires < oldest_lease->expires) if (!oldest_lease || lease->expires < oldest_lease->expires)
@ -194,7 +195,7 @@ uint32_t FAST_FUNC find_free_or_expired_nip(const uint8_t *safe_mac)
if (oldest_lease if (oldest_lease
&& is_expired_lease(oldest_lease) && is_expired_lease(oldest_lease)
&& nobody_responds_to_arp(oldest_lease->lease_nip, safe_mac) && nobody_responds_to_arp(oldest_lease->lease_nip, safe_mac, arpping_ms)
) { ) {
return oldest_lease->lease_nip; return oldest_lease->lease_nip;
} }