arpping() no longer blocks and address verification via arp is now performed
asynchronously in the main event loop.
This commit is contained in:
parent
a154f96538
commit
e37ed0e16b
113
ndhc/arpping.c
113
ndhc/arpping.c
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Shamelessly ripped off from busybox's udhcpc variant, which in turn was...
|
* Derived from busybox's udhcpc variant, which in turn was...
|
||||||
* Mostly stolen from: dhcpcd - DHCP client daemon
|
* Mostly stolen from: dhcpcd - DHCP client daemon
|
||||||
* by Yoichi Hariguchi <yoichi@fore.com>
|
* by Yoichi Hariguchi <yoichi@fore.com>
|
||||||
* Licensed under GPLv2, see file LICENSE in this source tree.
|
* Licensed under GPLv2, see file LICENSE in this source tree.
|
||||||
@ -9,59 +9,14 @@
|
|||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <netinet/if_ether.h>
|
#include <netinet/if_ether.h>
|
||||||
#include <net/if_arp.h>
|
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <poll.h>
|
#include "arpping.h"
|
||||||
#include "dhcpd.h"
|
#include "dhcpd.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "strl.h"
|
#include "strl.h"
|
||||||
#include "io.h"
|
#include "io.h"
|
||||||
|
|
||||||
struct arpMsg {
|
|
||||||
/* Ethernet header */
|
|
||||||
uint8_t h_dest[6]; /* 00 destination ether addr */
|
|
||||||
uint8_t h_source[6]; /* 06 source ether addr */
|
|
||||||
uint16_t h_proto; /* 0c packet type ID field */
|
|
||||||
|
|
||||||
/* ARP packet */
|
|
||||||
uint16_t htype; /* 0e hardware type (must be ARPHRD_ETHER) */
|
|
||||||
uint16_t ptype; /* 10 protocol type (must be ETH_P_IP) */
|
|
||||||
uint8_t hlen; /* 12 hardware address length (must be 6) */
|
|
||||||
uint8_t plen; /* 13 protocol address length (must be 4) */
|
|
||||||
uint16_t operation; /* 14 ARP opcode */
|
|
||||||
uint8_t sHaddr[6]; /* 16 sender's hardware address */
|
|
||||||
uint8_t sInaddr[4]; /* 1c sender's IP address */
|
|
||||||
uint8_t tHaddr[6]; /* 20 target's hardware address */
|
|
||||||
uint8_t tInaddr[4]; /* 26 target's IP address */
|
|
||||||
uint8_t pad[18]; /* 2a pad for min. ethernet payload (60 bytes) */
|
|
||||||
};
|
|
||||||
|
|
||||||
enum {
|
|
||||||
ARP_MSG_SIZE = 0x2a
|
|
||||||
};
|
|
||||||
|
|
||||||
static int safe_poll(struct pollfd *ufds, nfds_t nfds, int timeout)
|
|
||||||
{
|
|
||||||
while (1) {
|
|
||||||
int n = poll(ufds, nfds, timeout);
|
|
||||||
if (n >= 0)
|
|
||||||
return n;
|
|
||||||
/* Make sure we inch towards completion */
|
|
||||||
if (timeout > 0)
|
|
||||||
timeout--;
|
|
||||||
/* E.g. strace causes poll to return this */
|
|
||||||
if (errno == EINTR)
|
|
||||||
continue;
|
|
||||||
/* Kernel is very low on memory. Retry. */
|
|
||||||
/* I doubt many callers would handle this correctly! */
|
|
||||||
if (errno == ENOMEM)
|
|
||||||
continue;
|
|
||||||
log_warning("poll error: %s", strerror(errno));
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static unsigned long long curms()
|
static unsigned long long curms()
|
||||||
{
|
{
|
||||||
struct timeval tv;
|
struct timeval tv;
|
||||||
@ -69,27 +24,26 @@ static unsigned long long curms()
|
|||||||
return tv.tv_sec * 1000ULL + tv.tv_usec / 1000ULL;
|
return tv.tv_sec * 1000ULL + tv.tv_usec / 1000ULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Returns 1 if no reply received */
|
/* Returns fd of the arp socket, or -1 on failure. */
|
||||||
int arpping(uint32_t test_nip, const uint8_t *safe_mac, uint32_t from_ip,
|
int arpping(uint32_t test_nip, const uint8_t *safe_mac, uint32_t from_ip,
|
||||||
uint8_t *from_mac, const char *interface)
|
uint8_t *from_mac, const char *interface)
|
||||||
{
|
{
|
||||||
int timeout_ms;
|
int arpfd;
|
||||||
struct pollfd pfd[1];
|
|
||||||
int rv = 1; /* "no reply received" yet */
|
|
||||||
int opt = 1;
|
int opt = 1;
|
||||||
struct sockaddr addr; /* for interface name */
|
struct sockaddr addr; /* for interface name */
|
||||||
struct arpMsg arp;
|
struct arpMsg arp;
|
||||||
|
|
||||||
pfd[0].fd = socket(PF_PACKET, SOCK_PACKET, htons(ETH_P_ARP));
|
arpfd = socket(PF_PACKET, SOCK_PACKET, htons(ETH_P_ARP));
|
||||||
if (pfd[0].fd == -1) {
|
if (arpfd == -1) {
|
||||||
log_warning("arpping: failed to create socket: %s", strerror(errno));
|
log_warning("arpping: failed to create socket: %s", strerror(errno));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (setsockopt(pfd[0].fd, SOL_SOCKET, SO_BROADCAST,
|
if (setsockopt(arpfd, SOL_SOCKET, SO_BROADCAST,
|
||||||
&opt, sizeof opt) == -1) {
|
&opt, sizeof opt) == -1) {
|
||||||
log_warning("arpping: failed to set broadcast: %s", strerror(errno));
|
log_warning("arpping: failed to set broadcast: %s", strerror(errno));
|
||||||
goto ret;
|
close(arpfd);
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* send arp request */
|
/* send arp request */
|
||||||
@ -109,52 +63,11 @@ int arpping(uint32_t test_nip, const uint8_t *safe_mac, uint32_t from_ip,
|
|||||||
|
|
||||||
memset(&addr, 0, sizeof addr);
|
memset(&addr, 0, sizeof addr);
|
||||||
strlcpy(addr.sa_data, interface, sizeof addr.sa_data);
|
strlcpy(addr.sa_data, interface, sizeof addr.sa_data);
|
||||||
if (safe_sendto(pfd[0].fd, (const char *)&arp, sizeof arp,
|
if (safe_sendto(arpfd, (const char *)&arp, sizeof arp,
|
||||||
0, &addr, sizeof addr) < 0) {
|
0, &addr, sizeof addr) < 0) {
|
||||||
log_error("arpping: sendto failed: %s", strerror(errno));
|
log_error("arpping: sendto failed: %s", strerror(errno));
|
||||||
goto ret;
|
close(arpfd);
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
return arpfd;
|
||||||
/* wait for arp reply, and check it */
|
|
||||||
timeout_ms = 2000;
|
|
||||||
do {
|
|
||||||
typedef uint32_t aliased_uint32_t __attribute__((__may_alias__));
|
|
||||||
int r;
|
|
||||||
unsigned long long prevTime = curms();
|
|
||||||
|
|
||||||
pfd[0].events = POLLIN;
|
|
||||||
r = safe_poll(pfd, 1, timeout_ms);
|
|
||||||
if (r < 0)
|
|
||||||
break;
|
|
||||||
if (r) {
|
|
||||||
r = safe_read(pfd[0].fd, (char *)&arp, sizeof arp);
|
|
||||||
if (r < 0)
|
|
||||||
break;
|
|
||||||
|
|
||||||
//log3("sHaddr %02x:%02x:%02x:%02x:%02x:%02x",
|
|
||||||
//arp.sHaddr[0], arp.sHaddr[1], arp.sHaddr[2],
|
|
||||||
//arp.sHaddr[3], arp.sHaddr[4], arp.sHaddr[5]);
|
|
||||||
|
|
||||||
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 */
|
|
||||||
&& *(aliased_uint32_t*)arp.sInaddr == test_nip
|
|
||||||
) {
|
|
||||||
/* if ARP source MAC matches safe_mac
|
|
||||||
* (which is client's MAC), then it's not a conflict
|
|
||||||
* (client simply already has this IP and replies to ARPs!)
|
|
||||||
*/
|
|
||||||
if (!safe_mac || memcmp(safe_mac, arp.sHaddr, 6) != 0)
|
|
||||||
rv = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
timeout_ms -= (int)(curms() - prevTime);
|
|
||||||
} while (timeout_ms > 0);
|
|
||||||
|
|
||||||
ret:
|
|
||||||
close(pfd[0].fd);
|
|
||||||
log_line("%srp reply received for this address", rv ? "No a" : "A");
|
|
||||||
return rv;
|
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,33 @@
|
|||||||
#ifndef ARPPING_H_
|
#ifndef ARPPING_H_
|
||||||
#define ARPPING_H_
|
#define ARPPING_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <net/if_arp.h>
|
||||||
|
|
||||||
|
struct arpMsg {
|
||||||
|
/* Ethernet header */
|
||||||
|
uint8_t h_dest[6]; /* 00 destination ether addr */
|
||||||
|
uint8_t h_source[6]; /* 06 source ether addr */
|
||||||
|
uint16_t h_proto; /* 0c packet type ID field */
|
||||||
|
|
||||||
|
/* ARP packet */
|
||||||
|
uint16_t htype; /* 0e hardware type (must be ARPHRD_ETHER) */
|
||||||
|
uint16_t ptype; /* 10 protocol type (must be ETH_P_IP) */
|
||||||
|
uint8_t hlen; /* 12 hardware address length (must be 6) */
|
||||||
|
uint8_t plen; /* 13 protocol address length (must be 4) */
|
||||||
|
uint16_t operation; /* 14 ARP opcode */
|
||||||
|
uint8_t sHaddr[6]; /* 16 sender's hardware address */
|
||||||
|
uint8_t sInaddr[4]; /* 1c sender's IP address */
|
||||||
|
uint8_t tHaddr[6]; /* 20 target's hardware address */
|
||||||
|
uint8_t tInaddr[4]; /* 26 target's IP address */
|
||||||
|
uint8_t pad[18]; /* 2a pad for min. ethernet payload (60 bytes) */
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
ARP_MSG_SIZE = 0x2a
|
||||||
|
};
|
||||||
|
|
||||||
int arpping(uint32_t test_nip, const uint8_t *safe_mac, uint32_t from_ip,
|
int arpping(uint32_t test_nip, const uint8_t *safe_mac, uint32_t from_ip,
|
||||||
uint8_t *from_mac, const char *interface);
|
uint8_t *from_mac, const char *interface);
|
||||||
|
|
||||||
|
|
||||||
#endif /* ARPPING_H_ */
|
#endif /* ARPPING_H_ */
|
||||||
|
@ -7,6 +7,7 @@ enum {
|
|||||||
BOUND,
|
BOUND,
|
||||||
RENEWING,
|
RENEWING,
|
||||||
REBINDING,
|
REBINDING,
|
||||||
|
ARP_CHECK,
|
||||||
INIT_REBOOT,
|
INIT_REBOOT,
|
||||||
RENEW_REQUESTED,
|
RENEW_REQUESTED,
|
||||||
RELEASED
|
RELEASED
|
||||||
|
196
ndhc/ndhc.c
196
ndhc/ndhc.c
@ -56,19 +56,20 @@
|
|||||||
#include "strl.h"
|
#include "strl.h"
|
||||||
#include "pidfile.h"
|
#include "pidfile.h"
|
||||||
#include "malloc.h"
|
#include "malloc.h"
|
||||||
|
#include "io.h"
|
||||||
|
|
||||||
#define VERSION "1.0"
|
#define VERSION "1.0"
|
||||||
#define NUMPACKETS 3 /* number of packets to send before delay */
|
#define NUMPACKETS 3 /* number of packets to send before delay */
|
||||||
#define RETRY_DELAY 30 /* time in seconds to delay after sending NUMPACKETS */
|
#define RETRY_DELAY 30 /* time in seconds to delay after sending NUMPACKETS */
|
||||||
|
|
||||||
static int epollfd, signalFd;
|
static int epollfd, signalFd, arpFd = -1;
|
||||||
static struct epoll_event events[3];
|
static struct epoll_event events[3];
|
||||||
|
|
||||||
static char pidfile[MAX_PATH_LENGTH] = PID_FILE_DEFAULT;
|
static char pidfile[MAX_PATH_LENGTH] = PID_FILE_DEFAULT;
|
||||||
|
|
||||||
static uint32_t requested_ip, server_addr, timeout;
|
static uint32_t requested_ip, server_addr, timeout;
|
||||||
static uint32_t lease, t1, t2, xid, start;
|
static uint32_t lease, t1, t2, xid, start;
|
||||||
static int state, packet_num, listenFd, listen_mode;
|
static int dhcp_state, arp_prev_dhcp_state, packet_num, listenFd, listen_mode;
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
LISTEN_NONE,
|
LISTEN_NONE,
|
||||||
@ -167,19 +168,26 @@ static void change_listen_mode(int new_mode)
|
|||||||
static void perform_renew(void)
|
static void perform_renew(void)
|
||||||
{
|
{
|
||||||
log_line("Performing a DHCP renew...");
|
log_line("Performing a DHCP renew...");
|
||||||
switch (state) {
|
retry:
|
||||||
|
switch (dhcp_state) {
|
||||||
case BOUND:
|
case BOUND:
|
||||||
change_listen_mode(LISTEN_KERNEL);
|
change_listen_mode(LISTEN_KERNEL);
|
||||||
|
case ARP_CHECK:
|
||||||
|
// Cancel arp ping in progress and treat as previous state.
|
||||||
|
epoll_del(arpFd);
|
||||||
|
arpFd = -1;
|
||||||
|
dhcp_state = arp_prev_dhcp_state;
|
||||||
|
goto retry;
|
||||||
case RENEWING:
|
case RENEWING:
|
||||||
case REBINDING:
|
case REBINDING:
|
||||||
state = RENEW_REQUESTED;
|
dhcp_state = RENEW_REQUESTED;
|
||||||
break;
|
break;
|
||||||
case RENEW_REQUESTED: /* impatient are we? fine, square 1 */
|
case RENEW_REQUESTED: /* impatient are we? fine, square 1 */
|
||||||
run_script(NULL, SCRIPT_DECONFIG);
|
run_script(NULL, SCRIPT_DECONFIG);
|
||||||
case REQUESTING:
|
case REQUESTING:
|
||||||
case RELEASED:
|
case RELEASED:
|
||||||
change_listen_mode(LISTEN_RAW);
|
change_listen_mode(LISTEN_RAW);
|
||||||
state = INIT_SELECTING;
|
dhcp_state = INIT_SELECTING;
|
||||||
break;
|
break;
|
||||||
case INIT_SELECTING:
|
case INIT_SELECTING:
|
||||||
break;
|
break;
|
||||||
@ -199,7 +207,8 @@ static void perform_release(void)
|
|||||||
struct in_addr temp_saddr, temp_raddr;
|
struct in_addr temp_saddr, temp_raddr;
|
||||||
|
|
||||||
/* send release packet */
|
/* send release packet */
|
||||||
if (state == BOUND || state == RENEWING || state == REBINDING) {
|
if (dhcp_state == BOUND || dhcp_state == RENEWING ||
|
||||||
|
dhcp_state == REBINDING || dhcp_state == ARP_CHECK) {
|
||||||
temp_saddr.s_addr = server_addr;
|
temp_saddr.s_addr = server_addr;
|
||||||
temp_raddr.s_addr = requested_ip;
|
temp_raddr.s_addr = requested_ip;
|
||||||
log_line("Unicasting a release of %s to %s.",
|
log_line("Unicasting a release of %s to %s.",
|
||||||
@ -209,8 +218,12 @@ static void perform_release(void)
|
|||||||
}
|
}
|
||||||
log_line("Entering released state.");
|
log_line("Entering released state.");
|
||||||
|
|
||||||
|
if (dhcp_state == ARP_CHECK) {
|
||||||
|
epoll_del(arpFd);
|
||||||
|
arpFd = -1;
|
||||||
|
}
|
||||||
change_listen_mode(LISTEN_NONE);
|
change_listen_mode(LISTEN_NONE);
|
||||||
state = RELEASED;
|
dhcp_state = RELEASED;
|
||||||
timeout = 0x7fffffff;
|
timeout = 0x7fffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -229,12 +242,63 @@ static void background(void)
|
|||||||
write_pid(pidfile);
|
write_pid(pidfile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct arpMsg arpreply;
|
||||||
|
static int arpreply_offset;
|
||||||
|
static struct dhcpMessage arp_dhcp_packet;
|
||||||
|
|
||||||
|
static void arp_failed(void)
|
||||||
|
{
|
||||||
|
log_line("Offered address is in use: declining.");
|
||||||
|
epoll_del(arpFd);
|
||||||
|
arpFd = -1;
|
||||||
|
send_decline(xid, server_addr, arp_dhcp_packet.yiaddr);
|
||||||
|
|
||||||
|
if (arp_prev_dhcp_state != REQUESTING)
|
||||||
|
run_script(NULL, SCRIPT_DECONFIG);
|
||||||
|
dhcp_state = INIT_SELECTING;
|
||||||
|
requested_ip = 0;
|
||||||
|
timeout = time(0);
|
||||||
|
packet_num = 0;
|
||||||
|
change_listen_mode(LISTEN_RAW);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void arp_success(void)
|
||||||
|
{
|
||||||
|
struct in_addr temp_addr;
|
||||||
|
|
||||||
|
epoll_del(arpFd);
|
||||||
|
arpFd = -1;
|
||||||
|
|
||||||
|
/* enter bound state */
|
||||||
|
t1 = lease >> 1;
|
||||||
|
|
||||||
|
/* little fixed point for n * .875 */
|
||||||
|
t2 = (lease * 0x7) >> 3;
|
||||||
|
temp_addr.s_addr = arp_dhcp_packet.yiaddr;
|
||||||
|
log_line("Lease of %s obtained, lease time %ld.",
|
||||||
|
inet_ntoa(temp_addr), lease);
|
||||||
|
start = time(0);
|
||||||
|
timeout = t1 + start;
|
||||||
|
requested_ip = arp_dhcp_packet.yiaddr;
|
||||||
|
run_script(&arp_dhcp_packet,
|
||||||
|
((arp_prev_dhcp_state == RENEWING ||
|
||||||
|
arp_prev_dhcp_state == REBINDING)
|
||||||
|
? SCRIPT_RENEW : SCRIPT_BOUND));
|
||||||
|
|
||||||
|
dhcp_state = BOUND;
|
||||||
|
change_listen_mode(LISTEN_NONE);
|
||||||
|
if (client_config.quit_after_lease)
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
if (!client_config.foreground)
|
||||||
|
background();
|
||||||
|
}
|
||||||
|
|
||||||
/* Handle select timeout dropping to zero */
|
/* Handle select timeout dropping to zero */
|
||||||
static void handle_timeout(void)
|
static void handle_timeout(void)
|
||||||
{
|
{
|
||||||
time_t now = time(0);
|
time_t now = time(0);
|
||||||
|
|
||||||
switch (state) {
|
switch (dhcp_state) {
|
||||||
case INIT_SELECTING:
|
case INIT_SELECTING:
|
||||||
if (packet_num < NUMPACKETS) {
|
if (packet_num < NUMPACKETS) {
|
||||||
if (packet_num == 0)
|
if (packet_num == 0)
|
||||||
@ -261,7 +325,7 @@ static void handle_timeout(void)
|
|||||||
case REQUESTING:
|
case REQUESTING:
|
||||||
if (packet_num < NUMPACKETS) {
|
if (packet_num < NUMPACKETS) {
|
||||||
/* send request packet */
|
/* send request packet */
|
||||||
if (state == RENEW_REQUESTED)
|
if (dhcp_state == RENEW_REQUESTED)
|
||||||
/* unicast */
|
/* unicast */
|
||||||
send_renew(xid, server_addr, requested_ip);
|
send_renew(xid, server_addr, requested_ip);
|
||||||
else
|
else
|
||||||
@ -271,9 +335,9 @@ static void handle_timeout(void)
|
|||||||
packet_num++;
|
packet_num++;
|
||||||
} else {
|
} else {
|
||||||
/* timed out, go back to init state */
|
/* timed out, go back to init state */
|
||||||
if (state == RENEW_REQUESTED)
|
if (dhcp_state == RENEW_REQUESTED)
|
||||||
run_script(NULL, SCRIPT_DECONFIG);
|
run_script(NULL, SCRIPT_DECONFIG);
|
||||||
state = INIT_SELECTING;
|
dhcp_state = INIT_SELECTING;
|
||||||
timeout = now;
|
timeout = now;
|
||||||
packet_num = 0;
|
packet_num = 0;
|
||||||
change_listen_mode(LISTEN_RAW);
|
change_listen_mode(LISTEN_RAW);
|
||||||
@ -281,7 +345,7 @@ static void handle_timeout(void)
|
|||||||
break;
|
break;
|
||||||
case BOUND:
|
case BOUND:
|
||||||
/* Lease is starting to run out, time to enter renewing state */
|
/* Lease is starting to run out, time to enter renewing state */
|
||||||
state = RENEWING;
|
dhcp_state = RENEWING;
|
||||||
change_listen_mode(LISTEN_KERNEL);
|
change_listen_mode(LISTEN_KERNEL);
|
||||||
log_line("Entering renew state.");
|
log_line("Entering renew state.");
|
||||||
/* fall right through */
|
/* fall right through */
|
||||||
@ -289,7 +353,7 @@ static void handle_timeout(void)
|
|||||||
/* Either set a new T1, or enter REBINDING state */
|
/* Either set a new T1, or enter REBINDING state */
|
||||||
if ((t2 - t1) <= (lease / 14400 + 1)) {
|
if ((t2 - t1) <= (lease / 14400 + 1)) {
|
||||||
/* timed out, enter rebinding state */
|
/* timed out, enter rebinding state */
|
||||||
state = REBINDING;
|
dhcp_state = REBINDING;
|
||||||
timeout = now + (t2 - t1);
|
timeout = now + (t2 - t1);
|
||||||
log_line("Entering rebinding state.");
|
log_line("Entering rebinding state.");
|
||||||
} else {
|
} else {
|
||||||
@ -304,7 +368,7 @@ static void handle_timeout(void)
|
|||||||
/* Either set a new T2, or enter INIT state */
|
/* Either set a new T2, or enter INIT state */
|
||||||
if ((lease - t2) <= (lease / 14400 + 1)) {
|
if ((lease - t2) <= (lease / 14400 + 1)) {
|
||||||
/* timed out, enter init state */
|
/* timed out, enter init state */
|
||||||
state = INIT_SELECTING;
|
dhcp_state = INIT_SELECTING;
|
||||||
log_line("Lease lost, entering init state.");
|
log_line("Lease lost, entering init state.");
|
||||||
run_script(NULL, SCRIPT_DECONFIG);
|
run_script(NULL, SCRIPT_DECONFIG);
|
||||||
timeout = now;
|
timeout = now;
|
||||||
@ -322,6 +386,45 @@ static void handle_timeout(void)
|
|||||||
/* yah, I know, *you* say it would never happen */
|
/* yah, I know, *you* say it would never happen */
|
||||||
timeout = 0x7fffffff;
|
timeout = 0x7fffffff;
|
||||||
break;
|
break;
|
||||||
|
case ARP_CHECK:
|
||||||
|
arp_failed();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef uint32_t aliased_uint32_t __attribute__((__may_alias__));
|
||||||
|
static void handle_arp_response(void)
|
||||||
|
{
|
||||||
|
if (arpreply_offset < sizeof arpreply) {
|
||||||
|
int r = safe_read(arpFd, (char *)&arpreply + arpreply_offset,
|
||||||
|
sizeof arpreply - arpreply_offset);
|
||||||
|
if (r < 0) {
|
||||||
|
arp_failed();
|
||||||
|
return;
|
||||||
|
} else
|
||||||
|
arpreply_offset += r;
|
||||||
|
}
|
||||||
|
|
||||||
|
//log3("sHaddr %02x:%02x:%02x:%02x:%02x:%02x",
|
||||||
|
//arp.sHaddr[0], arp.sHaddr[1], arp.sHaddr[2],
|
||||||
|
//arp.sHaddr[3], arp.sHaddr[4], arp.sHaddr[5]);
|
||||||
|
|
||||||
|
if (arpreply_offset >= ARP_MSG_SIZE) {
|
||||||
|
if (arpreply.operation == htons(ARPOP_REPLY)
|
||||||
|
/* don't check: Linux returns invalid tHaddr (fixed in 2.6.24?) */
|
||||||
|
/* && memcmp(arp.tHaddr, from_mac, 6) == 0 */
|
||||||
|
&& *(aliased_uint32_t*)arpreply.sInaddr == arp_dhcp_packet.yiaddr)
|
||||||
|
{
|
||||||
|
/* if ARP source MAC matches safe_mac
|
||||||
|
* (which is client's MAC), then it's not a conflict
|
||||||
|
* (client simply already has this IP and replies to ARPs!)
|
||||||
|
*/
|
||||||
|
/* if (memcmp(safe_mac, arp.sHaddr, 6) == 0) */
|
||||||
|
arp_success();
|
||||||
|
} else {
|
||||||
|
memset(&arpreply, 0, sizeof arpreply);
|
||||||
|
arpreply_offset = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -329,7 +432,6 @@ static void handle_packet(void)
|
|||||||
{
|
{
|
||||||
unsigned char *temp = NULL, *message = NULL;
|
unsigned char *temp = NULL, *message = NULL;
|
||||||
int len;
|
int len;
|
||||||
struct in_addr temp_addr;
|
|
||||||
struct dhcpMessage packet;
|
struct dhcpMessage packet;
|
||||||
|
|
||||||
if (listen_mode == LISTEN_KERNEL)
|
if (listen_mode == LISTEN_KERNEL)
|
||||||
@ -359,7 +461,7 @@ static void handle_packet(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
time_t now = time(0);
|
time_t now = time(0);
|
||||||
switch (state) {
|
switch (dhcp_state) {
|
||||||
case INIT_SELECTING:
|
case INIT_SELECTING:
|
||||||
/* Must be a DHCPOFFER to one of our xid's */
|
/* Must be a DHCPOFFER to one of our xid's */
|
||||||
if (*message == DHCPOFFER) {
|
if (*message == DHCPOFFER) {
|
||||||
@ -370,7 +472,7 @@ static void handle_packet(void)
|
|||||||
requested_ip = packet.yiaddr;
|
requested_ip = packet.yiaddr;
|
||||||
|
|
||||||
/* enter requesting state */
|
/* enter requesting state */
|
||||||
state = REQUESTING;
|
dhcp_state = REQUESTING;
|
||||||
timeout = now;
|
timeout = now;
|
||||||
packet_num = 0;
|
packet_num = 0;
|
||||||
} else {
|
} else {
|
||||||
@ -378,6 +480,11 @@ static void handle_packet(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case ARP_CHECK:
|
||||||
|
/* We ignore dhcp packets for now. This state will
|
||||||
|
* be changed by the callback for arp ping.
|
||||||
|
*/
|
||||||
|
break;
|
||||||
case RENEW_REQUESTED:
|
case RENEW_REQUESTED:
|
||||||
case REQUESTING:
|
case REQUESTING:
|
||||||
case RENEWING:
|
case RENEWING:
|
||||||
@ -396,54 +503,29 @@ static void handle_packet(void)
|
|||||||
lease = RETRY_DELAY;
|
lease = RETRY_DELAY;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!arpping(packet.yiaddr, NULL, 0, client_config.arp,
|
arp_prev_dhcp_state = dhcp_state;
|
||||||
client_config.interface)) {
|
dhcp_state = ARP_CHECK;
|
||||||
log_line("Offered address is in use: declining.");
|
memcpy(&arp_dhcp_packet, &packet, sizeof packet);
|
||||||
send_decline(xid, server_addr, packet.yiaddr);
|
arpFd = arpping(arp_dhcp_packet.yiaddr, NULL, 0,
|
||||||
|
client_config.arp, client_config.interface);
|
||||||
if (state != REQUESTING)
|
epoll_add(arpFd);
|
||||||
run_script(NULL, SCRIPT_DECONFIG);
|
timeout = now + 2;
|
||||||
state = INIT_SELECTING;
|
memset(&arpreply, 0, sizeof arpreply);
|
||||||
requested_ip = 0;
|
arpreply_offset = 0;
|
||||||
timeout = now;
|
// Can transition to BOUND or INIT_SELECTING.
|
||||||
packet_num = 0;
|
|
||||||
change_listen_mode(LISTEN_RAW);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* enter bound state */
|
|
||||||
t1 = lease >> 1;
|
|
||||||
|
|
||||||
/* little fixed point for n * .875 */
|
|
||||||
t2 = (lease * 0x7) >> 3;
|
|
||||||
temp_addr.s_addr = packet.yiaddr;
|
|
||||||
log_line("Lease of %s obtained, lease time %ld.",
|
|
||||||
inet_ntoa(temp_addr), lease);
|
|
||||||
start = now;
|
|
||||||
timeout = t1 + start;
|
|
||||||
requested_ip = packet.yiaddr;
|
|
||||||
run_script(&packet,
|
|
||||||
((state == RENEWING || state == REBINDING)
|
|
||||||
? SCRIPT_RENEW : SCRIPT_BOUND));
|
|
||||||
|
|
||||||
state = BOUND;
|
|
||||||
change_listen_mode(LISTEN_NONE);
|
|
||||||
if (client_config.quit_after_lease)
|
|
||||||
exit(EXIT_SUCCESS);
|
|
||||||
if (!client_config.foreground)
|
|
||||||
background();
|
|
||||||
|
|
||||||
} else if (*message == DHCPNAK) {
|
} else if (*message == DHCPNAK) {
|
||||||
/* return to init state */
|
/* return to init state */
|
||||||
log_line("Received DHCP NAK.");
|
log_line("Received DHCP NAK.");
|
||||||
run_script(&packet, SCRIPT_NAK);
|
run_script(&packet, SCRIPT_NAK);
|
||||||
if (state != REQUESTING)
|
if (dhcp_state != REQUESTING)
|
||||||
run_script(NULL, SCRIPT_DECONFIG);
|
run_script(NULL, SCRIPT_DECONFIG);
|
||||||
state = INIT_SELECTING;
|
dhcp_state = INIT_SELECTING;
|
||||||
timeout = now;
|
timeout = now;
|
||||||
requested_ip = 0;
|
requested_ip = 0;
|
||||||
packet_num = 0;
|
packet_num = 0;
|
||||||
change_listen_mode(LISTEN_RAW);
|
change_listen_mode(LISTEN_RAW);
|
||||||
|
// XXX: this isn't rfc compliant: should be exp backoff
|
||||||
sleep(3); /* avoid excessive network traffic */
|
sleep(3); /* avoid excessive network traffic */
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -528,6 +610,8 @@ static void do_work(void)
|
|||||||
signal_dispatch();
|
signal_dispatch();
|
||||||
else if (fd == listenFd)
|
else if (fd == listenFd)
|
||||||
handle_packet();
|
handle_packet();
|
||||||
|
else if (fd == arpFd)
|
||||||
|
handle_arp_response();
|
||||||
else
|
else
|
||||||
suicide("epoll_wait: unknown fd");
|
suicide("epoll_wait: unknown fd");
|
||||||
}
|
}
|
||||||
@ -669,7 +753,7 @@ int main(int argc, char **argv)
|
|||||||
"cap_net_bind_service,cap_net_broadcast,cap_net_raw=ep");
|
"cap_net_bind_service,cap_net_broadcast,cap_net_raw=ep");
|
||||||
drop_root(uid, gid);
|
drop_root(uid, gid);
|
||||||
|
|
||||||
state = INIT_SELECTING;
|
dhcp_state = INIT_SELECTING;
|
||||||
run_script(NULL, SCRIPT_DECONFIG);
|
run_script(NULL, SCRIPT_DECONFIG);
|
||||||
|
|
||||||
do_work();
|
do_work();
|
||||||
|
Loading…
Reference in New Issue
Block a user