diff --git a/CHANGELOG b/CHANGELOG index e7a5d4a..82835ee 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -39,6 +39,14 @@ version 2.87 a local NODATA answer. The pre-2.86 behaviour is still available, by configuring --address=/example.com/1.2.3.4 --local=/example.com/ + Fix problem with binding DHCP sockets to an individual interface. + Despite the fact that the system call tales the interface _name_ as + a parameter, it actually, binds the socket to interface _index_. + Deleting the interface and creating a new one with the same name + leaves the socket bound to the old index. (Creating new sockets + always allocates a fresh index, they are not reused). We now + take this behaviour into account and keep up with changing indexes. + version 2.86 Handle DHCPREBIND requests in the DHCPv6 server code. diff --git a/src/dhcp-common.c b/src/dhcp-common.c index 611b5cb..95d41da 100644 --- a/src/dhcp-common.c +++ b/src/dhcp-common.c @@ -566,12 +566,16 @@ char *whichdevice(void) } if (found) - return found->name; - + { + char *ret = safe_malloc(strlen(found->name)+1); + strcpy(ret, found->name); + return ret; + } + return NULL; } -void bindtodevice(char *device, int fd) +static int bindtodevice(char *device, int fd) { size_t len = strlen(device)+1; if (len > IFNAMSIZ) @@ -579,7 +583,33 @@ void bindtodevice(char *device, int fd) /* only allowed by root. */ if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, device, len) == -1 && errno != EPERM) - die(_("failed to set SO_BINDTODEVICE on DHCP socket: %s"), NULL, EC_BADNET); + return 2; + + return 1; +} + +int bind_dhcp_devices(char *bound_device) +{ + int ret = 0; + + if (bound_device) + { + if (daemon->dhcp) + { + if (!daemon->relay4) + ret |= bindtodevice(bound_device, daemon->dhcpfd); + + if (daemon->enable_pxe && daemon->pxefd != -1) + ret |= bindtodevice(bound_device, daemon->pxefd); + } + +#if defined(HAVE_DHCP6) + if (daemon->doing_dhcp6 && !daemon->relay6) + ret |= bindtodevice(bound_device, daemon->dhcp6fd); +#endif + } + + return ret; } #endif diff --git a/src/dnsmasq.c b/src/dnsmasq.c index d112a7e..7cfb493 100644 --- a/src/dnsmasq.c +++ b/src/dnsmasq.c @@ -387,28 +387,9 @@ int main (int argc, char **argv) #if defined(HAVE_LINUX_NETWORK) && defined(HAVE_DHCP) /* after enumerate_interfaces() */ bound_device = whichdevice(); - - if (daemon->dhcp) - { - if (!daemon->relay4 && bound_device) - { - bindtodevice(bound_device, daemon->dhcpfd); - did_bind = 1; - } - if (daemon->enable_pxe && bound_device && daemon->pxefd != -1) - { - bindtodevice(bound_device, daemon->pxefd); - did_bind = 1; - } - } -#endif -#if defined(HAVE_LINUX_NETWORK) && defined(HAVE_DHCP6) - if (daemon->doing_dhcp6 && !daemon->relay6 && bound_device) - { - bindtodevice(bound_device, daemon->dhcp6fd); - did_bind = 1; - } + if ((did_bind = bind_dhcp_devices(bound_device)) & 2) + die(_("failed to set SO_BINDTODEVICE on DHCP socket: %s"), NULL, EC_BADNET); #endif } else @@ -1100,6 +1081,17 @@ int main (int argc, char **argv) #endif #ifdef HAVE_DHCP +# if defined(HAVE_LINUX_NETWORK) + if (bind_dhcp_devices(bound_device) & 2) + { + static int warned = 0; + if (!warned) + { + my_syslog(LOG_ERR, _("error binding DHCP socket to device %s"), bound_device); + warned = 1; + } + } +# endif if (daemon->dhcp || daemon->relay4) { poll_listen(daemon->dhcpfd, POLLIN); diff --git a/src/dnsmasq.h b/src/dnsmasq.h index 2ffe808..51a1aa6 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -1715,7 +1715,7 @@ struct dhcp_config *find_config(struct dhcp_config *configs, int config_has_mac(struct dhcp_config *config, unsigned char *hwaddr, int len, int type); #ifdef HAVE_LINUX_NETWORK char *whichdevice(void); -void bindtodevice(char *device, int fd); +int bind_dhcp_devices(char *bound_device); #endif # ifdef HAVE_DHCP6 void display_opts6(void);