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); | ||||
|         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) | ||||
|   | ||||
							
								
								
									
										83
									
								
								src/dhcp.c
									
									
									
									
									
								
							
							
						
						
									
										83
									
								
								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,8 +74,8 @@ 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, | ||||
| // 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; | ||||
| @@ -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); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -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); | ||||
|   | ||||
| @@ -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; | ||||
|  | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
							
								
								
									
										28
									
								
								src/sockd.c
									
									
									
									
									
								
							
							
						
						
									
										28
									
								
								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'", | ||||
|   | ||||
							
								
								
									
										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); | ||||
|     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); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user