Just use raw sockets for listening to DHCP requests. A UDP SO_BROADCAST
socket was previously used only for receiving RENEWING packets, and it added needless complexity and was somewhat fragile.
This commit is contained in:
parent
ca85a6ba9f
commit
07cbd88049
@ -447,7 +447,7 @@ void arp_success(struct client_state_t *cs)
|
|||||||
cs->routerAddr = get_option_router(&garp.dhcp_packet);
|
cs->routerAddr = get_option_router(&garp.dhcp_packet);
|
||||||
arp_get_gw_hwaddr(cs);
|
arp_get_gw_hwaddr(cs);
|
||||||
}
|
}
|
||||||
set_listen_none(cs);
|
stop_dhcp_listen(cs);
|
||||||
write_leasefile(temp_addr);
|
write_leasefile(temp_addr);
|
||||||
arp_announcement(cs);
|
arp_announcement(cs);
|
||||||
if (client_config.quit_after_lease)
|
if (client_config.quit_after_lease)
|
||||||
|
83
src/dhcp.c
83
src/dhcp.c
@ -48,12 +48,6 @@
|
|||||||
#include "options.h"
|
#include "options.h"
|
||||||
#include "sockd.h"
|
#include "sockd.h"
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
LM_NONE = 0,
|
|
||||||
LM_COOKED,
|
|
||||||
LM_RAW
|
|
||||||
} listen_mode_t;
|
|
||||||
|
|
||||||
static int get_udp_unicast_socket(struct client_state_t *cs)
|
static int get_udp_unicast_socket(struct client_state_t *cs)
|
||||||
{
|
{
|
||||||
char buf[32];
|
char buf[32];
|
||||||
@ -67,11 +61,6 @@ static int get_raw_broadcast_socket(void)
|
|||||||
return request_sockd_fd("s", 1, NULL);
|
return request_sockd_fd("s", 1, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int get_udp_listen_socket(void)
|
|
||||||
{
|
|
||||||
return request_sockd_fd("U", 1, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int get_raw_listen_socket(struct client_state_t *cs)
|
static int get_raw_listen_socket(struct client_state_t *cs)
|
||||||
{
|
{
|
||||||
char resp;
|
char resp;
|
||||||
@ -85,8 +74,8 @@ static int get_raw_listen_socket(struct client_state_t *cs)
|
|||||||
return fd;
|
return fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Broadcast a DHCP message using a UDP socket.
|
// Unicast a DHCP message using a UDP socket.
|
||||||
static ssize_t send_dhcp_cooked(struct client_state_t *cs,
|
static ssize_t send_dhcp_unicast(struct client_state_t *cs,
|
||||||
struct dhcpmsg *payload)
|
struct dhcpmsg *payload)
|
||||||
{
|
{
|
||||||
ssize_t ret = -1;
|
ssize_t ret = -1;
|
||||||
@ -127,23 +116,6 @@ static ssize_t send_dhcp_cooked(struct client_state_t *cs,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read a packet from a cooked socket. Returns -1 on fatal error, -2 on
|
|
||||||
// transient error.
|
|
||||||
static ssize_t get_cooked_packet(struct client_state_t *cs,
|
|
||||||
struct dhcpmsg *packet)
|
|
||||||
{
|
|
||||||
memset(packet, 0, sizeof *packet);
|
|
||||||
ssize_t bytes = safe_read(cs->listenFd, (char *)packet, sizeof *packet);
|
|
||||||
if (bytes < 0) {
|
|
||||||
if (errno == EAGAIN || errno == EWOULDBLOCK)
|
|
||||||
return -2;
|
|
||||||
log_warning("%s: (%s) read error %s", client_config.interface,
|
|
||||||
__func__, strerror(errno));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
// When summing ones-complement 16-bit values using a 32-bit unsigned
|
// When summing ones-complement 16-bit values using a 32-bit unsigned
|
||||||
// representation, fold the carry bits that have spilled into the upper
|
// representation, fold the carry bits that have spilled into the upper
|
||||||
// 16-bits of the 32-bit unsigned value back into the 16-bit ones-complement
|
// 16-bits of the 32-bit unsigned value back into the 16-bit ones-complement
|
||||||
@ -352,39 +324,24 @@ static ssize_t send_dhcp_raw(struct dhcpmsg *payload)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Switch listen socket between raw (if-bound), kernel (ip-bound), and none
|
void start_dhcp_listen(struct client_state_t *cs)
|
||||||
static void change_listen_mode(struct client_state_t *cs, int new_mode)
|
|
||||||
{
|
{
|
||||||
cs->listenMode = new_mode;
|
if (cs->listenFd >= 0)
|
||||||
if (cs->listenFd >= 0) {
|
return;
|
||||||
epoll_del(cs->epollFd, cs->listenFd);
|
cs->listenFd = get_raw_listen_socket(cs);
|
||||||
close(cs->listenFd);
|
|
||||||
cs->listenFd = -1;
|
|
||||||
}
|
|
||||||
switch (cs->listenMode) {
|
|
||||||
default: return;
|
|
||||||
case LM_RAW: cs->listenFd = get_raw_listen_socket(cs); break;
|
|
||||||
case LM_COOKED: cs->listenFd = get_udp_listen_socket(); break;
|
|
||||||
}
|
|
||||||
if (cs->listenFd < 0)
|
if (cs->listenFd < 0)
|
||||||
suicide("%s: FATAL: Couldn't listen on socket: %s",
|
suicide("%s: FATAL: Couldn't listen on socket: %s",
|
||||||
client_config.interface, strerror(errno));
|
client_config.interface, strerror(errno));
|
||||||
epoll_add(cs->epollFd, cs->listenFd);
|
epoll_add(cs->epollFd, cs->listenFd);
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_listen_raw(struct client_state_t *cs)
|
void stop_dhcp_listen(struct client_state_t *cs)
|
||||||
{
|
{
|
||||||
change_listen_mode(cs, LM_RAW);
|
if (cs->listenFd < 0)
|
||||||
}
|
return;
|
||||||
|
epoll_del(cs->epollFd, cs->listenFd);
|
||||||
void set_listen_cooked(struct client_state_t *cs)
|
close(cs->listenFd);
|
||||||
{
|
cs->listenFd = -1;
|
||||||
change_listen_mode(cs, LM_COOKED);
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_listen_none(struct client_state_t *cs)
|
|
||||||
{
|
|
||||||
change_listen_mode(cs, LM_NONE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int validate_dhcp_packet(struct client_state_t *cs, size_t len,
|
static int validate_dhcp_packet(struct client_state_t *cs, size_t len,
|
||||||
@ -436,19 +393,17 @@ static int validate_dhcp_packet(struct client_state_t *cs, size_t len,
|
|||||||
|
|
||||||
void handle_packet(struct client_state_t *cs)
|
void handle_packet(struct client_state_t *cs)
|
||||||
{
|
{
|
||||||
|
if (cs->listenFd < 0)
|
||||||
|
return;
|
||||||
struct dhcpmsg packet;
|
struct dhcpmsg packet;
|
||||||
ssize_t r;
|
ssize_t r = get_raw_packet(cs, &packet);
|
||||||
switch (cs->listenMode) {
|
|
||||||
case LM_RAW: r = get_raw_packet(cs, &packet); break;
|
|
||||||
case LM_COOKED: r = get_cooked_packet(cs, &packet); break;
|
|
||||||
default: return;
|
|
||||||
}
|
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
// Not a transient issue handled by packet collection functions.
|
// Not a transient issue handled by packet collection functions.
|
||||||
if (r != -2) {
|
if (r != -2) {
|
||||||
log_error("%s: Error reading from listening socket: %s. Reopening.",
|
log_error("%s: Error reading from listening socket: %s. Reopening.",
|
||||||
client_config.interface, strerror(errno));
|
client_config.interface, strerror(errno));
|
||||||
change_listen_mode(cs, cs->listenMode);
|
stop_dhcp_listen(cs);
|
||||||
|
start_dhcp_listen(cs);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -515,7 +470,7 @@ ssize_t send_renew(struct client_state_t *cs)
|
|||||||
add_option_vendor(&packet);
|
add_option_vendor(&packet);
|
||||||
add_option_hostname(&packet);
|
add_option_hostname(&packet);
|
||||||
log_line("%s: Sending a renew request...", client_config.interface);
|
log_line("%s: Sending a renew request...", client_config.interface);
|
||||||
return send_dhcp_cooked(cs, &packet);
|
return send_dhcp_unicast(cs, &packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t send_rebind(struct client_state_t *cs)
|
ssize_t send_rebind(struct client_state_t *cs)
|
||||||
@ -548,6 +503,6 @@ ssize_t send_release(struct client_state_t *cs)
|
|||||||
add_option_reqip(&packet, cs->clientAddr);
|
add_option_reqip(&packet, cs->clientAddr);
|
||||||
add_option_serverid(&packet, cs->serverAddr);
|
add_option_serverid(&packet, cs->serverAddr);
|
||||||
log_line("%s: Sending a release message...", client_config.interface);
|
log_line("%s: Sending a release message...", client_config.interface);
|
||||||
return send_dhcp_cooked(cs, &packet);
|
return send_dhcp_unicast(cs, &packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,9 +81,8 @@ struct udp_dhcp_packet {
|
|||||||
struct dhcpmsg data;
|
struct dhcpmsg data;
|
||||||
};
|
};
|
||||||
|
|
||||||
void set_listen_raw(struct client_state_t *cs);
|
void start_dhcp_listen(struct client_state_t *cs);
|
||||||
void set_listen_cooked(struct client_state_t *cs);
|
void stop_dhcp_listen(struct client_state_t *cs);
|
||||||
void set_listen_none(struct client_state_t *cs);
|
|
||||||
void handle_packet(struct client_state_t *cs);
|
void handle_packet(struct client_state_t *cs);
|
||||||
ssize_t send_discover(struct client_state_t *cs);
|
ssize_t send_discover(struct client_state_t *cs);
|
||||||
ssize_t send_selecting(struct client_state_t *cs);
|
ssize_t send_selecting(struct client_state_t *cs);
|
||||||
|
@ -295,7 +295,7 @@ static void do_ndhc_work(void)
|
|||||||
epoll_add(cs.epollFd, ifchSock[0]);
|
epoll_add(cs.epollFd, ifchSock[0]);
|
||||||
epoll_add(cs.epollFd, ifchStream[0]);
|
epoll_add(cs.epollFd, ifchStream[0]);
|
||||||
epoll_add(cs.epollFd, sockdStream[0]);
|
epoll_add(cs.epollFd, sockdStream[0]);
|
||||||
set_listen_raw(&cs);
|
start_dhcp_listen(&cs);
|
||||||
nowts = curms();
|
nowts = curms();
|
||||||
goto jumpstart;
|
goto jumpstart;
|
||||||
|
|
||||||
|
@ -40,7 +40,6 @@ struct client_state_t {
|
|||||||
int ifsPrevState;
|
int ifsPrevState;
|
||||||
int ifchWorking; // ifch is performing interface changes.
|
int ifchWorking; // ifch is performing interface changes.
|
||||||
int ifDeconfig; // Set if the interface has already been deconfigured.
|
int ifDeconfig; // Set if the interface has already been deconfigured.
|
||||||
int listenMode;
|
|
||||||
int epollFd, signalFd, listenFd, arpFd, nlFd;
|
int epollFd, signalFd, listenFd, arpFd, nlFd;
|
||||||
int nlPortId;
|
int nlPortId;
|
||||||
uint32_t clientAddr, serverAddr, routerAddr;
|
uint32_t clientAddr, serverAddr, routerAddr;
|
||||||
|
28
src/sockd.c
28
src/sockd.c
@ -245,30 +245,6 @@ out:
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns fd of new listen socket bound to 0.0.0.0:@68 on interface @inf
|
|
||||||
// on success, or -1 on failure.
|
|
||||||
static int create_udp_listen_socket(void)
|
|
||||||
{
|
|
||||||
int fd = create_udp_socket(INADDR_ANY, DHCP_CLIENT_PORT,
|
|
||||||
client_config.interface);
|
|
||||||
if (fd < 0)
|
|
||||||
return -1;
|
|
||||||
int opt = 1;
|
|
||||||
if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &opt, sizeof opt) < 0) {
|
|
||||||
log_error("%s: (%s) Set broadcast failed: %s",
|
|
||||||
client_config.interface, __func__, strerror(errno));
|
|
||||||
close(fd);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return fd;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int create_udp_send_socket(uint32_t client_addr)
|
|
||||||
{
|
|
||||||
return create_udp_socket(client_addr, DHCP_CLIENT_PORT,
|
|
||||||
client_config.interface);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int create_raw_listen_socket(bool *using_bpf)
|
static int create_raw_listen_socket(bool *using_bpf)
|
||||||
{
|
{
|
||||||
static const struct sock_filter sf_dhcp[] = {
|
static const struct sock_filter sf_dhcp[] = {
|
||||||
@ -478,7 +454,6 @@ static size_t execute_sockd(char *buf, size_t buflen)
|
|||||||
xfer_fd(fd, using_bpf ? 'L' : 'l');
|
xfer_fd(fd, using_bpf ? 'L' : 'l');
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
case 'U': xfer_fd(create_udp_listen_socket(), 'U'); return 1;
|
|
||||||
case 'a': {
|
case 'a': {
|
||||||
bool using_bpf;
|
bool using_bpf;
|
||||||
int fd = create_arp_basic_socket(&using_bpf);
|
int fd = create_arp_basic_socket(&using_bpf);
|
||||||
@ -505,7 +480,8 @@ static size_t execute_sockd(char *buf, size_t buflen)
|
|||||||
suicide("%s: (%s) 'u' does not have necessary arguments: %zu",
|
suicide("%s: (%s) 'u' does not have necessary arguments: %zu",
|
||||||
client_config.interface, __func__, buflen);
|
client_config.interface, __func__, buflen);
|
||||||
memcpy(&client_addr, buf + 1, sizeof client_addr);
|
memcpy(&client_addr, buf + 1, sizeof client_addr);
|
||||||
xfer_fd(create_udp_send_socket(client_addr), 'u');
|
xfer_fd(create_udp_socket(client_addr, DHCP_CLIENT_PORT,
|
||||||
|
client_config.interface), 'u');
|
||||||
return 5;
|
return 5;
|
||||||
}
|
}
|
||||||
default: suicide("%s: (%s) received invalid commands: '%c'",
|
default: suicide("%s: (%s) received invalid commands: '%c'",
|
||||||
|
10
src/state.c
10
src/state.c
@ -106,7 +106,7 @@ void reinit_selecting(struct client_state_t *cs, int timeout)
|
|||||||
reinit_shared_deconfig(cs);
|
reinit_shared_deconfig(cs);
|
||||||
cs->dhcpState = DS_SELECTING;
|
cs->dhcpState = DS_SELECTING;
|
||||||
dhcp_wake_ts = curms() + timeout;
|
dhcp_wake_ts = curms() + timeout;
|
||||||
set_listen_raw(cs);
|
start_dhcp_listen(cs);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void set_released(struct client_state_t *cs)
|
static void set_released(struct client_state_t *cs)
|
||||||
@ -114,7 +114,7 @@ static void set_released(struct client_state_t *cs)
|
|||||||
reinit_shared_deconfig(cs);
|
reinit_shared_deconfig(cs);
|
||||||
cs->dhcpState = DS_RELEASED;
|
cs->dhcpState = DS_RELEASED;
|
||||||
dhcp_wake_ts = -1;
|
dhcp_wake_ts = -1;
|
||||||
set_listen_none(cs);
|
stop_dhcp_listen(cs);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Triggered after a DHCP lease request packet has been sent and no reply has
|
// Triggered after a DHCP lease request packet has been sent and no reply has
|
||||||
@ -143,7 +143,7 @@ static void bound_timeout(struct client_state_t *cs, long long nowts)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
cs->dhcpState = DS_RENEWING;
|
cs->dhcpState = DS_RENEWING;
|
||||||
set_listen_cooked(cs);
|
start_dhcp_listen(cs);
|
||||||
renewing_timeout(cs, nowts);
|
renewing_timeout(cs, nowts);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -267,7 +267,7 @@ static void an_packet(struct client_state_t *cs, struct dhcpmsg *packet,
|
|||||||
client_config.interface, cs->lease);
|
client_config.interface, cs->lease);
|
||||||
cs->dhcpState = DS_BOUND;
|
cs->dhcpState = DS_BOUND;
|
||||||
arp_set_defense_mode(cs);
|
arp_set_defense_mode(cs);
|
||||||
set_listen_none(cs);
|
stop_dhcp_listen(cs);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (msgtype == DHCPNAK) {
|
} else if (msgtype == DHCPNAK) {
|
||||||
@ -358,7 +358,7 @@ static void frenew(struct client_state_t *cs)
|
|||||||
if (cs->dhcpState == DS_BOUND) {
|
if (cs->dhcpState == DS_BOUND) {
|
||||||
log_line("%s: Forcing a DHCP renew...", client_config.interface);
|
log_line("%s: Forcing a DHCP renew...", client_config.interface);
|
||||||
cs->dhcpState = DS_RENEWING;
|
cs->dhcpState = DS_RENEWING;
|
||||||
set_listen_cooked(cs);
|
start_dhcp_listen(cs);
|
||||||
if (send_renew(cs) < 0)
|
if (send_renew(cs) < 0)
|
||||||
log_warning("%s: Failed to send a renew request packet.",
|
log_warning("%s: Failed to send a renew request packet.",
|
||||||
client_config.interface);
|
client_config.interface);
|
||||||
|
Loading…
Reference in New Issue
Block a user