udhcpc: an option to perform ARP check (Jonas Danielsson <jonas.danielsson@axis.com>)
configurable, ~+300 bytes when on.
This commit is contained in:
parent
c881c733bb
commit
223bc97f61
@ -2183,8 +2183,8 @@
|
||||
"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" \
|
||||
) \
|
||||
"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"\
|
||||
"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" \
|
||||
"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]"
|
||||
#define udhcpc_full_usage \
|
||||
USE_GETOPT_LONG( \
|
||||
" -V,--vendorclass=CLASSID Set vendor class identifier" \
|
||||
"\n -i,--interface=INTERFACE Interface to use (default: eth0)" \
|
||||
" -V,--vendorclass=CLASSID Vendor class identifier" \
|
||||
"\n -i,--interface=INTERFACE Interface to use (default eth0)" \
|
||||
"\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 -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 -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,--timeout=N Try to get a lease for N seconds (default: 3)"\
|
||||
"\n -A,--tryagain=N Wait N seconds (default: 60) after failure"\
|
||||
"\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 -A,--tryagain=N Wait N seconds (default 60) after failure" \
|
||||
"\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 -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 -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( \
|
||||
" -V CLASSID Set vendor class identifier" \
|
||||
" -V CLASSID Vendor class identifier" \
|
||||
"\n -i INTERFACE Interface to use (default: eth0)" \
|
||||
"\n -H,-h HOSTNAME Client hostname" \
|
||||
"\n -c CLIENTID Set client identifier" \
|
||||
"\n -c CLIENTID 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 -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 Try to get a lease for N seconds (default: 3)"\
|
||||
"\n -A N Wait N seconds (default: 60) after failure"\
|
||||
"\n -t N Send up to N request packets" \
|
||||
"\n -T N Try to get a lease for N seconds (default 3)" \
|
||||
"\n -A N Wait N seconds (default 60) after failure" \
|
||||
"\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 -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 -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 \
|
||||
|
@ -54,6 +54,16 @@ config APP_UDHCPC
|
||||
|
||||
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
|
||||
bool "Compile udhcp with noisy debugging messages"
|
||||
default n
|
||||
|
@ -10,10 +10,16 @@ lib-$(CONFIG_APP_UDHCPC) += common.o options.o packet.o \
|
||||
signalpipe.o socket.o
|
||||
lib-$(CONFIG_APP_UDHCPD) += common.o options.o packet.o \
|
||||
signalpipe.o socket.o
|
||||
|
||||
lib-$(CONFIG_APP_UDHCPC) += dhcpc.o clientpacket.o clientsocket.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 \
|
||||
serverpacket.o static_leases.o
|
||||
|
||||
lib-$(CONFIG_APP_DUMPLEASES) += dumpleases.o
|
||||
lib-$(CONFIG_APP_DHCPRELAY) += dhcprelay.o
|
||||
lib-$(CONFIG_FEATURE_RFC3397) += domain_codec.o
|
||||
|
@ -32,12 +32,16 @@ struct arpMsg {
|
||||
uint8_t pad[18]; /* 2a pad for min. ethernet payload (60 bytes) */
|
||||
} ATTRIBUTE_PACKED;
|
||||
|
||||
enum {
|
||||
ARP_MSG_SIZE = 0x2a
|
||||
};
|
||||
|
||||
|
||||
/* Returns 1 if no reply received */
|
||||
|
||||
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];
|
||||
#define s (pfd[0].fd) /* socket */
|
||||
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) {
|
||||
bb_perror_msg("cannot setsocketopt on raw socket");
|
||||
bb_perror_msg("cannot enable bcast on raw socket");
|
||||
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 */
|
||||
memcpy(arp.sHaddr, from_mac, 6); /* source hardware 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 */
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
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;
|
||||
}
|
||||
|
||||
/* wait for arp reply, and check it */
|
||||
timeout_ms = 2000;
|
||||
do {
|
||||
int r;
|
||||
unsigned prevTime = monotonic_us();
|
||||
|
||||
pfd[0].events = POLLIN;
|
||||
r = safe_poll(pfd, 1, timeout_ms);
|
||||
if (r < 0) {
|
||||
if (r < 0)
|
||||
break;
|
||||
} else if (r) {
|
||||
if (read(s, &arp, sizeof(arp)) < 0)
|
||||
if (r) {
|
||||
r = read(s, &arp, sizeof(arp));
|
||||
if (r < 0)
|
||||
break;
|
||||
if (arp.operation == htons(ARPOP_REPLY)
|
||||
&& memcmp(arp.tHaddr, from_mac, 6) == 0
|
||||
if (r >= ARP_MSG_SIZE
|
||||
&& 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
|
||||
) {
|
||||
rv = 0;
|
||||
|
@ -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 */
|
||||
int send_discover(uint32_t xid, uint32_t requested)
|
||||
|
@ -76,7 +76,7 @@ void udhcp_run_script(struct dhcpMessage *packet, const char *name);
|
||||
|
||||
void udhcp_sp_setup(void);
|
||||
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 read_interface(const char *interface, int *ifindex, uint32_t *addr, uint8_t *arp);
|
||||
int listen_socket(/*uint32_t ip,*/ int port, const char *inf);
|
||||
|
@ -145,6 +145,13 @@ int udhcpc_main(int argc, char **argv)
|
||||
{
|
||||
uint8_t *temp, *message;
|
||||
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 lease = 0; /* can be given as 32-bit quantity */
|
||||
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_S = 1 << 18,
|
||||
OPT_A = 1 << 19,
|
||||
#if ENABLE_FEATURE_UDHCPC_ARPING
|
||||
OPT_a = 1 << 20,
|
||||
OPT_W = 1 << 21,
|
||||
#endif
|
||||
};
|
||||
#if ENABLE_GETOPT_LONG
|
||||
static const char udhcpc_longopts[] ALIGN1 =
|
||||
@ -203,14 +214,15 @@ int udhcpc_main(int argc, char **argv)
|
||||
"retries\0" Required_argument "t"
|
||||
"tryagain\0" Required_argument "A"
|
||||
"syslog\0" No_argument "S"
|
||||
#if ENABLE_FEATURE_UDHCPC_ARPING
|
||||
"arping\0" No_argument "a"
|
||||
"wait\0" Required_argument "W"
|
||||
#endif
|
||||
;
|
||||
#endif
|
||||
/* Default options. */
|
||||
client_config.interface = "eth0";
|
||||
client_config.script = DEFAULT_SCRIPT;
|
||||
client_config.retries = 3;
|
||||
client_config.timeout = 3;
|
||||
client_config.tryagain = 60;
|
||||
|
||||
/* Parse command line */
|
||||
opt_complementary = "c--C:C--c" // mutually exclusive
|
||||
@ -218,10 +230,12 @@ int udhcpc_main(int argc, char **argv)
|
||||
#if ENABLE_GETOPT_LONG
|
||||
applet_long_options = udhcpc_longopts;
|
||||
#endif
|
||||
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,
|
||||
opt = getopt32(argv, "c:CV:fbH:h:F:i:np:qRr:s:T:t:vSA:"
|
||||
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.script, &str_T, &str_t, &str_A
|
||||
USE_FEATURE_UDHCPC_ARPING(, &str_W)
|
||||
);
|
||||
|
||||
if (opt & OPT_c)
|
||||
@ -259,11 +273,11 @@ int udhcpc_main(int argc, char **argv)
|
||||
requested_ip = inet_addr(str_r);
|
||||
// if (opt & OPT_s) client_config.script = ...
|
||||
if (opt & OPT_T)
|
||||
client_config.timeout = xatoi_u(str_T);
|
||||
discover_timeout = xatoi_u(str_T);
|
||||
if (opt & OPT_t)
|
||||
client_config.retries = xatoi_u(str_t);
|
||||
discover_retries = xatoi_u(str_t);
|
||||
if (opt & OPT_A)
|
||||
client_config.tryagain = xatoi_u(str_A);
|
||||
tryagain_timeout = xatoi_u(str_A);
|
||||
if (opt & OPT_v) {
|
||||
puts("version "BB_VER);
|
||||
return 0;
|
||||
@ -274,6 +288,11 @@ int udhcpc_main(int argc, char **argv)
|
||||
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,
|
||||
NULL, client_config.arp))
|
||||
return 1;
|
||||
@ -339,14 +358,14 @@ int udhcpc_main(int argc, char **argv)
|
||||
/* timeout dropped to zero */
|
||||
switch (state) {
|
||||
case INIT_SELECTING:
|
||||
if (packet_num < client_config.retries) {
|
||||
if (packet_num < discover_retries) {
|
||||
if (packet_num == 0)
|
||||
xid = random_xid();
|
||||
|
||||
/* send discover packet */
|
||||
send_discover(xid, requested_ip); /* broadcast */
|
||||
|
||||
timeout = now + client_config.timeout;
|
||||
timeout = now + discover_timeout;
|
||||
packet_num++;
|
||||
} else {
|
||||
udhcp_run_script(NULL, "leasefail");
|
||||
@ -360,12 +379,12 @@ int udhcpc_main(int argc, char **argv)
|
||||
}
|
||||
/* wait to try again */
|
||||
packet_num = 0;
|
||||
timeout = now + client_config.tryagain;
|
||||
timeout = now + tryagain_timeout;
|
||||
}
|
||||
break;
|
||||
case RENEW_REQUESTED:
|
||||
case REQUESTING:
|
||||
if (packet_num < client_config.retries) {
|
||||
if (packet_num < discover_retries) {
|
||||
/* send request packet */
|
||||
if (state == RENEW_REQUESTED)
|
||||
send_renew(xid, server_addr, requested_ip); /* unicast */
|
||||
@ -491,6 +510,28 @@ int udhcpc_main(int argc, char **argv)
|
||||
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 */
|
||||
t1 = lease / 2;
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
/* vi: set sw=4 ts=4: */
|
||||
/* dhcpc.h */
|
||||
|
||||
#ifndef _DHCPC_H
|
||||
#define _DHCPC_H
|
||||
|
||||
@ -28,9 +29,6 @@ struct client_config_t {
|
||||
uint8_t *hostname; /* Optional hostname to use */
|
||||
uint8_t *fqdn; /* Optional fully qualified domain name 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 */
|
||||
};
|
||||
|
||||
@ -42,6 +40,9 @@ struct client_config_t {
|
||||
uint32_t random_xid(void);
|
||||
int send_discover(uint32_t xid, 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_release(uint32_t server, uint32_t ciaddr);
|
||||
|
@ -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
|
||||
* no signal, -1 on error (and sets errno appropriately), and
|
||||
* your signal on success */
|
||||
int udhcp_sp_read(fd_set *rfds)
|
||||
int udhcp_sp_read(const fd_set *rfds)
|
||||
{
|
||||
unsigned char sig;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user