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:
		| @@ -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) | ||||||
|   | |||||||
							
								
								
									
										85
									
								
								src/dhcp.c
									
									
									
									
									
								
							
							
						
						
									
										85
									
								
								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,9 +74,9 @@ 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; | ||||||
|     int fd = get_udp_unicast_socket(cs); |     int fd = get_udp_unicast_socket(cs); | ||||||
| @@ -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); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user