udhcpc: an option to perform ARP check (Jonas Danielsson <jonas.danielsson@axis.com>)

configurable, ~+300 bytes when on.
This commit is contained in:
Denis Vlasenko 2007-11-22 00:58:49 +00:00
parent c881c733bb
commit 223bc97f61
9 changed files with 138 additions and 47 deletions

View File

@ -2183,8 +2183,8 @@
"and * (run both after creating and before deleting). The commands run in\n" \ "and * (run both after creating and before deleting). The commands run in\n" \
"the /dev directory, and use system() which calls /bin/sh.\n\n" \ "the /dev directory, and use system() which calls /bin/sh.\n\n" \
) \ ) \
"Config file parsing stops on the first matching line. If no config\n"\ "Config file parsing stops on the first matching line. If no config\n" \
"entry is matched, devices are created with default 0:0 660. (Make\n"\ "entry is matched, devices are created with default 0:0 660. (Make\n" \
"the last line match .* to override this.)\n\n" \ "the last line match .* to override this.)\n\n" \
) )
@ -3843,44 +3843,50 @@ USE_FEATURE_RUN_PARTS_FANCY("\n -l Prints names of all matching files even when
" [-p pidfile] [-r IP] [-s script]" " [-p pidfile] [-r IP] [-s script]"
#define udhcpc_full_usage \ #define udhcpc_full_usage \
USE_GETOPT_LONG( \ USE_GETOPT_LONG( \
" -V,--vendorclass=CLASSID Set vendor class identifier" \ " -V,--vendorclass=CLASSID Vendor class identifier" \
"\n -i,--interface=INTERFACE Interface to use (default: eth0)" \ "\n -i,--interface=INTERFACE Interface to use (default eth0)" \
"\n -H,-h,--hostname=HOSTNAME Client hostname" \ "\n -H,-h,--hostname=HOSTNAME Client hostname" \
"\n -c,--clientid=CLIENTID Set client identifier" \ "\n -c,--clientid=CLIENTID Client identifier" \
"\n -C,--clientid-none Suppress default client identifier" \ "\n -C,--clientid-none Suppress default client identifier" \
"\n -p,--pidfile=file Store process ID of daemon in file" \ "\n -p,--pidfile=file Create pidfile" \
"\n -r,--request=IP IP address to request" \ "\n -r,--request=IP IP address to request" \
"\n -s,--script=file Run file at dhcp events (default: /usr/share/udhcpc/default.script)" \ "\n -s,--script=file Run file at dhcp events (default: /usr/share/udhcpc/default.script)" \
"\n -t,--retries=N Send up to N request packets"\ "\n -t,--retries=N Send up to N request packets" \
"\n -T,--timeout=N Try to get a lease for N seconds (default: 3)"\ "\n -T,--timeout=N Try to get a lease for N seconds (default 3)" \
"\n -A,--tryagain=N Wait N seconds (default: 60) after failure"\ "\n -A,--tryagain=N Wait N seconds (default 60) after failure" \
"\n -f,--foreground Run in foreground" \ "\n -f,--foreground Run in foreground" \
"\n -b,--background Background if lease cannot be immediately negotiated" \ "\n -b,--background Background if lease is not immediately obtained" \
"\n -S,--syslog Log to syslog too" \ "\n -S,--syslog Log to syslog too" \
"\n -n,--now Exit with failure if lease cannot be immediately negotiated" \ "\n -n,--now Exit with failure if lease is not immediately obtained" \
"\n -q,--quit Quit after obtaining lease" \ "\n -q,--quit Quit after obtaining lease" \
"\n -R,--release Release IP on quit" \ "\n -R,--release Release IP on quit" \
"\n -v,--version Display version" \ USE_FEATURE_UDHCPC_ARPING( \
"\n -a,--arping Use arping to validate offered address" \
"\n -W,--wait=N Wait N seconds after declining (default 10)" \
) \
) \ ) \
SKIP_GETOPT_LONG( \ SKIP_GETOPT_LONG( \
" -V CLASSID Set vendor class identifier" \ " -V CLASSID Vendor class identifier" \
"\n -i INTERFACE Interface to use (default: eth0)" \ "\n -i INTERFACE Interface to use (default: eth0)" \
"\n -H,-h HOSTNAME Client hostname" \ "\n -H,-h HOSTNAME Client hostname" \
"\n -c CLIENTID Set client identifier" \ "\n -c CLIENTID Client identifier" \
"\n -C Suppress default client identifier" \ "\n -C Suppress default client identifier" \
"\n -p file Store process ID of daemon in file" \ "\n -p file Create pidfile" \
"\n -r IP IP address to request" \ "\n -r IP IP address to request" \
"\n -s file Run file at dhcp events (default: /usr/share/udhcpc/default.script)" \ "\n -s file Run file at dhcp events (default: /usr/share/udhcpc/default.script)" \
"\n -t N Send up to N request packets"\ "\n -t N Send up to N request packets" \
"\n -T N Try to get a lease for N seconds (default: 3)"\ "\n -T N Try to get a lease for N seconds (default 3)" \
"\n -A N Wait N seconds (default: 60) after failure"\ "\n -A N Wait N seconds (default 60) after failure" \
"\n -f Run in foreground" \ "\n -f Run in foreground" \
"\n -b Background if lease cannot be immediately negotiated" \ "\n -b Background if lease is not immediately obtained" \
"\n -S Log to syslog too" \ "\n -S Log to syslog too" \
"\n -n Exit with failure if lease cannot be immediately negotiated" \ "\n -n Exit with failure if lease is not immediately obtained" \
"\n -q Quit after obtaining lease" \ "\n -q Quit after obtaining lease" \
"\n -R Release IP on quit" \ "\n -R Release IP on quit" \
"\n -v Display version" \ USE_FEATURE_UDHCPC_ARPING( \
"\n -a Use arping to validate offered address" \
"\n -W N Wait N seconds after declining (default 10)" \
) \
) )
#define udhcpd_trivial_usage \ #define udhcpd_trivial_usage \

View File

@ -54,6 +54,16 @@ config APP_UDHCPC
See http://udhcp.busybox.net for further details. See http://udhcp.busybox.net for further details.
config FEATURE_UDHCPC_ARPING
bool "Ask udhcpc to verify that the offered address is free, using arpping"
default y
depends on APP_UDHCPC
help
If selected, udhcpc will use arpping to make sure the offered address
is really available. The client will DHCPDECLINE the offer if the
address is in use, and restart the discover process.
config FEATURE_UDHCP_DEBUG config FEATURE_UDHCP_DEBUG
bool "Compile udhcp with noisy debugging messages" bool "Compile udhcp with noisy debugging messages"
default n default n

View File

@ -10,10 +10,16 @@ lib-$(CONFIG_APP_UDHCPC) += common.o options.o packet.o \
signalpipe.o socket.o signalpipe.o socket.o
lib-$(CONFIG_APP_UDHCPD) += common.o options.o packet.o \ lib-$(CONFIG_APP_UDHCPD) += common.o options.o packet.o \
signalpipe.o socket.o signalpipe.o socket.o
lib-$(CONFIG_APP_UDHCPC) += dhcpc.o clientpacket.o clientsocket.o \ lib-$(CONFIG_APP_UDHCPC) += dhcpc.o clientpacket.o clientsocket.o \
script.o script.o
UDHCPC_NEEDS_ARPING-$(CONFIG_FEATURE_UDHCPC_ARPING) = y
lib-$(UDHCPC_NEEDS_ARPING) += arpping.o
lib-$(CONFIG_APP_UDHCPD) += dhcpd.o arpping.o files.o leases.o \ lib-$(CONFIG_APP_UDHCPD) += dhcpd.o arpping.o files.o leases.o \
serverpacket.o static_leases.o serverpacket.o static_leases.o
lib-$(CONFIG_APP_DUMPLEASES) += dumpleases.o lib-$(CONFIG_APP_DUMPLEASES) += dumpleases.o
lib-$(CONFIG_APP_DHCPRELAY) += dhcprelay.o lib-$(CONFIG_APP_DHCPRELAY) += dhcprelay.o
lib-$(CONFIG_FEATURE_RFC3397) += domain_codec.o lib-$(CONFIG_FEATURE_RFC3397) += domain_codec.o

View File

@ -32,12 +32,16 @@ struct arpMsg {
uint8_t pad[18]; /* 2a pad for min. ethernet payload (60 bytes) */ uint8_t pad[18]; /* 2a pad for min. ethernet payload (60 bytes) */
} ATTRIBUTE_PACKED; } ATTRIBUTE_PACKED;
enum {
ARP_MSG_SIZE = 0x2a
};
/* Returns 1 if no reply received */ /* Returns 1 if no reply received */
int arpping(uint32_t test_ip, uint32_t from_ip, uint8_t *from_mac, const char *interface) int arpping(uint32_t test_ip, uint32_t from_ip, uint8_t *from_mac, const char *interface)
{ {
int timeout_ms = 2000; int timeout_ms;
struct pollfd pfd[1]; struct pollfd pfd[1];
#define s (pfd[0].fd) /* socket */ #define s (pfd[0].fd) /* socket */
int rv = 1; /* "no reply received" yet */ int rv = 1; /* "no reply received" yet */
@ -51,7 +55,7 @@ int arpping(uint32_t test_ip, uint32_t from_ip, uint8_t *from_mac, const char *i
} }
if (setsockopt_broadcast(s) == -1) { if (setsockopt_broadcast(s) == -1) {
bb_perror_msg("cannot setsocketopt on raw socket"); bb_perror_msg("cannot enable bcast on raw socket");
goto ret; goto ret;
} }
@ -67,28 +71,35 @@ int arpping(uint32_t test_ip, uint32_t from_ip, uint8_t *from_mac, const char *i
arp.operation = htons(ARPOP_REQUEST); /* ARP op code */ arp.operation = htons(ARPOP_REQUEST); /* ARP op code */
memcpy(arp.sHaddr, from_mac, 6); /* source hardware address */ memcpy(arp.sHaddr, from_mac, 6); /* source hardware address */
memcpy(arp.sInaddr, &from_ip, sizeof(from_ip)); /* source IP address */ memcpy(arp.sInaddr, &from_ip, sizeof(from_ip)); /* source IP address */
/* tHaddr */ /* target hardware address */ /* tHaddr is zero-fiiled */ /* target hardware address */
memcpy(arp.tInaddr, &test_ip, sizeof(test_ip)); /* target IP address */ memcpy(arp.tInaddr, &test_ip, sizeof(test_ip)); /* target IP address */
memset(&addr, 0, sizeof(addr)); memset(&addr, 0, sizeof(addr));
safe_strncpy(addr.sa_data, interface, sizeof(addr.sa_data)); safe_strncpy(addr.sa_data, interface, sizeof(addr.sa_data));
if (sendto(s, &arp, sizeof(arp), 0, &addr, sizeof(addr)) < 0) if (sendto(s, &arp, sizeof(arp), 0, &addr, sizeof(addr)) < 0) {
// TODO: error message? caller didn't expect us to fail,
// just returning 1 "no reply received" misleads it.
goto ret; goto ret;
}
/* wait for arp reply, and check it */ /* wait for arp reply, and check it */
timeout_ms = 2000;
do { do {
int r; int r;
unsigned prevTime = monotonic_us(); unsigned prevTime = monotonic_us();
pfd[0].events = POLLIN; pfd[0].events = POLLIN;
r = safe_poll(pfd, 1, timeout_ms); r = safe_poll(pfd, 1, timeout_ms);
if (r < 0) { if (r < 0)
break; break;
} else if (r) { if (r) {
if (read(s, &arp, sizeof(arp)) < 0) r = read(s, &arp, sizeof(arp));
if (r < 0)
break; break;
if (arp.operation == htons(ARPOP_REPLY) if (r >= ARP_MSG_SIZE
&& memcmp(arp.tHaddr, from_mac, 6) == 0 && arp.operation == htons(ARPOP_REPLY)
/* don't check it: Linux doesn't return proper tHaddr (fixed in 2.6.24?) */
/* && memcmp(arp.tHaddr, from_mac, 6) == 0 */
&& *((uint32_t *) arp.sInaddr) == test_ip && *((uint32_t *) arp.sInaddr) == test_ip
) { ) {
rv = 0; rv = 0;

View File

@ -69,6 +69,22 @@ static void add_requests(struct dhcpMessage *packet)
} }
#if ENABLE_FEATURE_UDHCPC_ARPING
/* Unicast a DHCP decline message */
int send_decline(uint32_t xid, uint32_t server)
{
struct dhcpMessage packet;
init_packet(&packet, DHCPDECLINE);
packet.xid = xid;
add_requests(&packet);
bb_info_msg("Sending decline...");
return udhcp_raw_packet(&packet, INADDR_ANY, CLIENT_PORT, INADDR_BROADCAST,
SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex);
}
#endif
/* Broadcast a DHCP discover packet to the network, with an optionally requested IP */ /* Broadcast a DHCP discover packet to the network, with an optionally requested IP */
int send_discover(uint32_t xid, uint32_t requested) int send_discover(uint32_t xid, uint32_t requested)

View File

@ -76,7 +76,7 @@ void udhcp_run_script(struct dhcpMessage *packet, const char *name);
void udhcp_sp_setup(void); void udhcp_sp_setup(void);
int udhcp_sp_fd_set(fd_set *rfds, int extra_fd); int udhcp_sp_fd_set(fd_set *rfds, int extra_fd);
int udhcp_sp_read(fd_set *rfds); int udhcp_sp_read(const fd_set *rfds);
int raw_socket(int ifindex); int raw_socket(int ifindex);
int read_interface(const char *interface, int *ifindex, uint32_t *addr, uint8_t *arp); int read_interface(const char *interface, int *ifindex, uint32_t *addr, uint8_t *arp);
int listen_socket(/*uint32_t ip,*/ int port, const char *inf); int listen_socket(/*uint32_t ip,*/ int port, const char *inf);

View File

@ -145,6 +145,13 @@ int udhcpc_main(int argc, char **argv)
{ {
uint8_t *temp, *message; uint8_t *temp, *message;
char *str_c, *str_V, *str_h, *str_F, *str_r, *str_T, *str_A, *str_t; char *str_c, *str_V, *str_h, *str_F, *str_r, *str_T, *str_A, *str_t;
int tryagain_timeout = 60;
int discover_timeout = 3;
int discover_retries = 3;
#if ENABLE_FEATURE_UDHCPC_ARPING
int decline_wait = 10;
char *str_W;
#endif
uint32_t xid = 0; uint32_t xid = 0;
uint32_t lease = 0; /* can be given as 32-bit quantity */ uint32_t lease = 0; /* can be given as 32-bit quantity */
unsigned t1 = 0, t2 = 0; /* what a wonderful names */ unsigned t1 = 0, t2 = 0; /* what a wonderful names */
@ -180,6 +187,10 @@ int udhcpc_main(int argc, char **argv)
OPT_v = 1 << 17, OPT_v = 1 << 17,
OPT_S = 1 << 18, OPT_S = 1 << 18,
OPT_A = 1 << 19, OPT_A = 1 << 19,
#if ENABLE_FEATURE_UDHCPC_ARPING
OPT_a = 1 << 20,
OPT_W = 1 << 21,
#endif
}; };
#if ENABLE_GETOPT_LONG #if ENABLE_GETOPT_LONG
static const char udhcpc_longopts[] ALIGN1 = static const char udhcpc_longopts[] ALIGN1 =
@ -203,14 +214,15 @@ int udhcpc_main(int argc, char **argv)
"retries\0" Required_argument "t" "retries\0" Required_argument "t"
"tryagain\0" Required_argument "A" "tryagain\0" Required_argument "A"
"syslog\0" No_argument "S" "syslog\0" No_argument "S"
#if ENABLE_FEATURE_UDHCPC_ARPING
"arping\0" No_argument "a"
"wait\0" Required_argument "W"
#endif
; ;
#endif #endif
/* Default options. */ /* Default options. */
client_config.interface = "eth0"; client_config.interface = "eth0";
client_config.script = DEFAULT_SCRIPT; client_config.script = DEFAULT_SCRIPT;
client_config.retries = 3;
client_config.timeout = 3;
client_config.tryagain = 60;
/* Parse command line */ /* Parse command line */
opt_complementary = "c--C:C--c" // mutually exclusive opt_complementary = "c--C:C--c" // mutually exclusive
@ -218,10 +230,12 @@ int udhcpc_main(int argc, char **argv)
#if ENABLE_GETOPT_LONG #if ENABLE_GETOPT_LONG
applet_long_options = udhcpc_longopts; applet_long_options = udhcpc_longopts;
#endif #endif
opt = getopt32(argv, "c:CV:fbH:h:F:i:np:qRr:s:T:t:vSA:", opt = getopt32(argv, "c:CV:fbH:h:F:i:np:qRr:s:T:t:vSA:"
&str_c, &str_V, &str_h, &str_h, &str_F, USE_FEATURE_UDHCPC_ARPING("aW:")
, &str_c, &str_V, &str_h, &str_h, &str_F,
&client_config.interface, &client_config.pidfile, &str_r, &client_config.interface, &client_config.pidfile, &str_r,
&client_config.script, &str_T, &str_t, &str_A &client_config.script, &str_T, &str_t, &str_A
USE_FEATURE_UDHCPC_ARPING(, &str_W)
); );
if (opt & OPT_c) if (opt & OPT_c)
@ -259,11 +273,11 @@ int udhcpc_main(int argc, char **argv)
requested_ip = inet_addr(str_r); requested_ip = inet_addr(str_r);
// if (opt & OPT_s) client_config.script = ... // if (opt & OPT_s) client_config.script = ...
if (opt & OPT_T) if (opt & OPT_T)
client_config.timeout = xatoi_u(str_T); discover_timeout = xatoi_u(str_T);
if (opt & OPT_t) if (opt & OPT_t)
client_config.retries = xatoi_u(str_t); discover_retries = xatoi_u(str_t);
if (opt & OPT_A) if (opt & OPT_A)
client_config.tryagain = xatoi_u(str_A); tryagain_timeout = xatoi_u(str_A);
if (opt & OPT_v) { if (opt & OPT_v) {
puts("version "BB_VER); puts("version "BB_VER);
return 0; return 0;
@ -274,6 +288,11 @@ int udhcpc_main(int argc, char **argv)
logmode |= LOGMODE_SYSLOG; logmode |= LOGMODE_SYSLOG;
} }
#if ENABLE_FEATURE_UDHCPC_ARPING
if (opt & OPT_W)
decline_wait = xatou_range(str_W, 10, INT_MAX);
#endif
if (read_interface(client_config.interface, &client_config.ifindex, if (read_interface(client_config.interface, &client_config.ifindex,
NULL, client_config.arp)) NULL, client_config.arp))
return 1; return 1;
@ -339,14 +358,14 @@ int udhcpc_main(int argc, char **argv)
/* timeout dropped to zero */ /* timeout dropped to zero */
switch (state) { switch (state) {
case INIT_SELECTING: case INIT_SELECTING:
if (packet_num < client_config.retries) { if (packet_num < discover_retries) {
if (packet_num == 0) if (packet_num == 0)
xid = random_xid(); xid = random_xid();
/* send discover packet */ /* send discover packet */
send_discover(xid, requested_ip); /* broadcast */ send_discover(xid, requested_ip); /* broadcast */
timeout = now + client_config.timeout; timeout = now + discover_timeout;
packet_num++; packet_num++;
} else { } else {
udhcp_run_script(NULL, "leasefail"); udhcp_run_script(NULL, "leasefail");
@ -360,12 +379,12 @@ int udhcpc_main(int argc, char **argv)
} }
/* wait to try again */ /* wait to try again */
packet_num = 0; packet_num = 0;
timeout = now + client_config.tryagain; timeout = now + tryagain_timeout;
} }
break; break;
case RENEW_REQUESTED: case RENEW_REQUESTED:
case REQUESTING: case REQUESTING:
if (packet_num < client_config.retries) { if (packet_num < discover_retries) {
/* send request packet */ /* send request packet */
if (state == RENEW_REQUESTED) if (state == RENEW_REQUESTED)
send_renew(xid, server_addr, requested_ip); /* unicast */ send_renew(xid, server_addr, requested_ip); /* unicast */
@ -491,6 +510,28 @@ int udhcpc_main(int argc, char **argv)
lease = ntohl(lease); lease = ntohl(lease);
} }
#if ENABLE_FEATURE_UDHCPC_ARPING
if (opt & OPT_a) {
if (!arpping(packet.yiaddr,
(uint32_t) 0,
client_config.arp,
client_config.interface)
) {
bb_info_msg("offered address is in use,"
" declining");
send_decline(xid, server_addr);
if (state != REQUESTING)
udhcp_run_script(NULL, "deconfig");
state = INIT_SELECTING;
requested_ip = 0;
packet_num = 0;
change_mode(LISTEN_RAW);
timeout = now + decline_wait;
break;
}
}
#endif
/* enter bound state */ /* enter bound state */
t1 = lease / 2; t1 = lease / 2;

View File

@ -1,5 +1,6 @@
/* vi: set sw=4 ts=4: */ /* vi: set sw=4 ts=4: */
/* dhcpc.h */ /* dhcpc.h */
#ifndef _DHCPC_H #ifndef _DHCPC_H
#define _DHCPC_H #define _DHCPC_H
@ -28,9 +29,6 @@ struct client_config_t {
uint8_t *hostname; /* Optional hostname to use */ uint8_t *hostname; /* Optional hostname to use */
uint8_t *fqdn; /* Optional fully qualified domain name to use */ uint8_t *fqdn; /* Optional fully qualified domain name to use */
int ifindex; /* Index number of the interface to use */ int ifindex; /* Index number of the interface to use */
int retries; /* Max number of request packets */
int timeout; /* Number of seconds to try to get a lease */
int tryagain; /* Number of seconds to try to get a lease */
uint8_t arp[6]; /* Our arp address */ uint8_t arp[6]; /* Our arp address */
}; };
@ -42,6 +40,9 @@ struct client_config_t {
uint32_t random_xid(void); uint32_t random_xid(void);
int send_discover(uint32_t xid, uint32_t requested); int send_discover(uint32_t xid, uint32_t requested);
int send_selecting(uint32_t xid, uint32_t server, uint32_t requested); int send_selecting(uint32_t xid, uint32_t server, uint32_t requested);
#if ENABLE_FEATURE_UDHCPC_ARPING
int send_decline(uint32_t xid, uint32_t server);
#endif
int send_renew(uint32_t xid, uint32_t server, uint32_t ciaddr); int send_renew(uint32_t xid, uint32_t server, uint32_t ciaddr);
int send_renew(uint32_t xid, uint32_t server, uint32_t ciaddr); int send_renew(uint32_t xid, uint32_t server, uint32_t ciaddr);
int send_release(uint32_t server, uint32_t ciaddr); int send_release(uint32_t server, uint32_t ciaddr);

View File

@ -66,7 +66,7 @@ int udhcp_sp_fd_set(fd_set *rfds, int extra_fd)
/* Read a signal from the signal pipe. Returns 0 if there is /* Read a signal from the signal pipe. Returns 0 if there is
* no signal, -1 on error (and sets errno appropriately), and * no signal, -1 on error (and sets errno appropriately), and
* your signal on success */ * your signal on success */
int udhcp_sp_read(fd_set *rfds) int udhcp_sp_read(const fd_set *rfds)
{ {
unsigned char sig; unsigned char sig;