From 07cbd88049515ba0a3498baf099ec4f25422fb0a Mon Sep 17 00:00:00 2001 From: "Nicholas J. Kain" Date: Wed, 16 Apr 2014 01:00:36 -0400 Subject: [PATCH] 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. --- src/arp.c | 2 +- src/dhcp.c | 85 +++++++++++++---------------------------------------- src/dhcp.h | 5 ++-- src/ndhc.c | 2 +- src/ndhc.h | 1 - src/sockd.c | 28 ++---------------- src/state.c | 10 +++---- 7 files changed, 31 insertions(+), 102 deletions(-) diff --git a/src/arp.c b/src/arp.c index ae82811..41525bd 100644 --- a/src/arp.c +++ b/src/arp.c @@ -447,7 +447,7 @@ void arp_success(struct client_state_t *cs) cs->routerAddr = get_option_router(&garp.dhcp_packet); arp_get_gw_hwaddr(cs); } - set_listen_none(cs); + stop_dhcp_listen(cs); write_leasefile(temp_addr); arp_announcement(cs); if (client_config.quit_after_lease) diff --git a/src/dhcp.c b/src/dhcp.c index 1b21a24..f3c5796 100644 --- a/src/dhcp.c +++ b/src/dhcp.c @@ -48,12 +48,6 @@ #include "options.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) { char buf[32]; @@ -67,11 +61,6 @@ static int get_raw_broadcast_socket(void) 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) { char resp; @@ -85,9 +74,9 @@ static int get_raw_listen_socket(struct client_state_t *cs) return fd; } -// Broadcast a DHCP message using a UDP socket. -static ssize_t send_dhcp_cooked(struct client_state_t *cs, - struct dhcpmsg *payload) +// Unicast a DHCP message using a UDP socket. +static ssize_t send_dhcp_unicast(struct client_state_t *cs, + struct dhcpmsg *payload) { ssize_t ret = -1; int fd = get_udp_unicast_socket(cs); @@ -127,23 +116,6 @@ static ssize_t send_dhcp_cooked(struct client_state_t *cs, 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 // 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 @@ -352,39 +324,24 @@ static ssize_t send_dhcp_raw(struct dhcpmsg *payload) return ret; } -// Switch listen socket between raw (if-bound), kernel (ip-bound), and none -static void change_listen_mode(struct client_state_t *cs, int new_mode) +void start_dhcp_listen(struct client_state_t *cs) { - cs->listenMode = new_mode; - if (cs->listenFd >= 0) { - epoll_del(cs->epollFd, cs->listenFd); - 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) + return; + cs->listenFd = get_raw_listen_socket(cs); if (cs->listenFd < 0) suicide("%s: FATAL: Couldn't listen on socket: %s", client_config.interface, strerror(errno)); 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); -} - -void set_listen_cooked(struct client_state_t *cs) -{ - change_listen_mode(cs, LM_COOKED); -} - -void set_listen_none(struct client_state_t *cs) -{ - change_listen_mode(cs, LM_NONE); + if (cs->listenFd < 0) + return; + epoll_del(cs->epollFd, cs->listenFd); + close(cs->listenFd); + cs->listenFd = -1; } 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) { + if (cs->listenFd < 0) + return; struct dhcpmsg packet; - ssize_t r; - 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; - } + ssize_t r = get_raw_packet(cs, &packet); if (r < 0) { // Not a transient issue handled by packet collection functions. if (r != -2) { log_error("%s: Error reading from listening socket: %s. Reopening.", client_config.interface, strerror(errno)); - change_listen_mode(cs, cs->listenMode); + stop_dhcp_listen(cs); + start_dhcp_listen(cs); } return; } @@ -515,7 +470,7 @@ ssize_t send_renew(struct client_state_t *cs) add_option_vendor(&packet); add_option_hostname(&packet); 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) @@ -548,6 +503,6 @@ ssize_t send_release(struct client_state_t *cs) add_option_reqip(&packet, cs->clientAddr); add_option_serverid(&packet, cs->serverAddr); log_line("%s: Sending a release message...", client_config.interface); - return send_dhcp_cooked(cs, &packet); + return send_dhcp_unicast(cs, &packet); } diff --git a/src/dhcp.h b/src/dhcp.h index b674386..6fbabb2 100644 --- a/src/dhcp.h +++ b/src/dhcp.h @@ -81,9 +81,8 @@ struct udp_dhcp_packet { struct dhcpmsg data; }; -void set_listen_raw(struct client_state_t *cs); -void set_listen_cooked(struct client_state_t *cs); -void set_listen_none(struct client_state_t *cs); +void start_dhcp_listen(struct client_state_t *cs); +void stop_dhcp_listen(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_selecting(struct client_state_t *cs); diff --git a/src/ndhc.c b/src/ndhc.c index b95790e..2c4b6c6 100644 --- a/src/ndhc.c +++ b/src/ndhc.c @@ -295,7 +295,7 @@ static void do_ndhc_work(void) epoll_add(cs.epollFd, ifchSock[0]); epoll_add(cs.epollFd, ifchStream[0]); epoll_add(cs.epollFd, sockdStream[0]); - set_listen_raw(&cs); + start_dhcp_listen(&cs); nowts = curms(); goto jumpstart; diff --git a/src/ndhc.h b/src/ndhc.h index f1cdde4..d9ad0c4 100644 --- a/src/ndhc.h +++ b/src/ndhc.h @@ -40,7 +40,6 @@ struct client_state_t { int ifsPrevState; int ifchWorking; // ifch is performing interface changes. int ifDeconfig; // Set if the interface has already been deconfigured. - int listenMode; int epollFd, signalFd, listenFd, arpFd, nlFd; int nlPortId; uint32_t clientAddr, serverAddr, routerAddr; diff --git a/src/sockd.c b/src/sockd.c index fe7f8a5..96039c7 100644 --- a/src/sockd.c +++ b/src/sockd.c @@ -245,30 +245,6 @@ out: 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 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'); return 1; } - case 'U': xfer_fd(create_udp_listen_socket(), 'U'); return 1; case 'a': { bool 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", client_config.interface, __func__, buflen); 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; } default: suicide("%s: (%s) received invalid commands: '%c'", diff --git a/src/state.c b/src/state.c index 6e3c9cb..93289e1 100644 --- a/src/state.c +++ b/src/state.c @@ -106,7 +106,7 @@ void reinit_selecting(struct client_state_t *cs, int timeout) reinit_shared_deconfig(cs); cs->dhcpState = DS_SELECTING; dhcp_wake_ts = curms() + timeout; - set_listen_raw(cs); + start_dhcp_listen(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); cs->dhcpState = DS_RELEASED; 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 @@ -143,7 +143,7 @@ static void bound_timeout(struct client_state_t *cs, long long nowts) return; } cs->dhcpState = DS_RENEWING; - set_listen_cooked(cs); + start_dhcp_listen(cs); 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); cs->dhcpState = DS_BOUND; arp_set_defense_mode(cs); - set_listen_none(cs); + stop_dhcp_listen(cs); } } else if (msgtype == DHCPNAK) { @@ -358,7 +358,7 @@ static void frenew(struct client_state_t *cs) if (cs->dhcpState == DS_BOUND) { log_line("%s: Forcing a DHCP renew...", client_config.interface); cs->dhcpState = DS_RENEWING; - set_listen_cooked(cs); + start_dhcp_listen(cs); if (send_renew(cs) < 0) log_warning("%s: Failed to send a renew request packet.", client_config.interface);