[Dnsmasq-discuss] [PATCH] Refresh cached socket fd if the interface index changed

Simon Kelley simon at thekelleys.org.uk
Sun Aug 28 20:47:00 BST 2016


Patch applied, with a few tweaks, mainly for style consistency, but one
to avoid passing strings of length zero to if_nametoindex().


Many thanks.


Cheers,

Simon.



On 25/08/16 16:10, Beniamino Galvani wrote:
> The socket bound to a specific interface in the daemon->sfds cache is
> reused also when the interface disappears and is created again,
> causing resolution problems.
> 
> This problem can be seen when connecting to VPNs with NetworkManager:
> when the VPN is connected NM pushes through D-Bus a configuration
> containing the upstream server '1.2.3.4 at tun0' and dnsmasq creates a
> socket bound to tun0. Later, the VPN is reconnected and tun0 reappears
> with a different ifindex; but even if the server list is updated again
> (still containing an upstream server on tun0), dnsmasq tries to use
> the old socket and any DNS request fails.
> 
> This patch adds a check on the ifindex in allocate_sfd() to prevent
> the reuse of a stale socket, and ensures that unused sockets are
> destroyed.
> ---
>  src/dnsmasq.h |  2 ++
>  src/network.c | 29 +++++++++++++++++++++++++++--
>  2 files changed, 29 insertions(+), 2 deletions(-)
> 
> diff --git a/src/dnsmasq.h b/src/dnsmasq.h
> index 27385a9..462aaf5 100644
> --- a/src/dnsmasq.h
> +++ b/src/dnsmasq.h
> @@ -487,8 +487,10 @@ union mysockaddr {
>  struct serverfd {
>    int fd;
>    union mysockaddr source_addr;
>    char interface[IF_NAMESIZE+1];
> +  unsigned int ifindex;
> +  unsigned int used;
>    struct serverfd *next;
>  };
>  
>  struct randfd {
> diff --git a/src/network.c b/src/network.c
> index e7722fd..bcb4d1f 100644
> --- a/src/network.c
> +++ b/src/network.c
> @@ -1203,8 +1203,9 @@ int local_bind(int fd, union mysockaddr *addr, char *intname, int is_tcp)
>  
>  static struct serverfd *allocate_sfd(union mysockaddr *addr, char *intname)
>  {
>    struct serverfd *sfd;
> +  unsigned int ifindex = 0;
>    int errsave;
>  
>    /* when using random ports, servers which would otherwise use
>       the INADDR_ANY/port0 socket have sfd set to NULL */
> @@ -1223,14 +1224,19 @@ static struct serverfd *allocate_sfd(union mysockaddr *addr, char *intname)
>  	  addr->in6.sin6_port == htons(0)) 
>  	return NULL;
>  #endif
>      }
> +
> +  if (intname)
> +    ifindex = if_nametoindex(intname);
>        
>    /* may have a suitable one already */
>    for (sfd = daemon->sfds; sfd; sfd = sfd->next )
>      if (sockaddr_isequal(&sfd->source_addr, addr) &&
> -	strcmp(intname, sfd->interface) == 0)
> +	strcmp(intname, sfd->interface) == 0 &&
> +	ifindex == sfd->ifindex) {
>        return sfd;
> +    }
>    
>    /* need to make a new one. */
>    errno = ENOMEM; /* in case malloc fails. */
>    if (!(sfd = whine_malloc(sizeof(struct serverfd))))
> @@ -1249,13 +1255,15 @@ static struct serverfd *allocate_sfd(union mysockaddr *addr, char *intname)
>        free(sfd);
>        errno = errsave;
>        return NULL;
>      }
> -    
> +
>    strcpy(sfd->interface, intname); 
>    sfd->source_addr = *addr;
>    sfd->next = daemon->sfds;
> +  sfd->ifindex = ifindex;
>    daemon->sfds = sfd;
> +
>    return sfd; 
>  }
>  
>  /* create upstream sockets during startup, before root is dropped which may be needed
> @@ -1428,14 +1436,18 @@ void add_update_server(int flags,
>  void check_servers(void)
>  {
>    struct irec *iface;
>    struct server *serv;
> +  struct serverfd *sfd, **ptr;
>    int port = 0, count;
>  
>    /* interface may be new since startup */
>    if (!option_bool(OPT_NOWILD))
>      enumerate_interfaces(0);
>    
> +  for (sfd = daemon->sfds; sfd; sfd = sfd->next)
> +    sfd->used = 0;
> +
>  #ifdef HAVE_DNSSEC
>   /* Disable DNSSEC validation when using server=/domain/.... servers
>      unless there's a configured trust anchor. */
>    for (serv = daemon->servers; serv; serv = serv->next)
> @@ -1504,8 +1516,10 @@ void check_servers(void)
>  			daemon->namebuff, strerror(errno));
>  	      serv->flags |= SERV_MARK;
>  	      continue;
>  	    }
> +
> +	  serv->sfd->used++;
>  	}
>        
>        if (!(serv->flags & SERV_NO_REBIND) && !(serv->flags & SERV_LITERAL_ADDRESS))
>  	{
> @@ -1546,8 +1560,19 @@ void check_servers(void)
>    
>    if (count - 1 > SERVERS_LOGGED)
>      my_syslog(LOG_INFO, _("using %d more nameservers"), count - SERVERS_LOGGED - 1);
>  
> +  /* Remove unused sfds */
> +  for (ptr = &daemon->sfds; *ptr; ) {
> +    sfd = *ptr;
> +    if (!sfd->used) {
> +      *ptr = sfd->next;
> +      close(sfd->fd);
> +      free(sfd);
> +    } else
> +      ptr = &sfd->next;
> +  }
> +
>    cleanup_servers();
>  }
>  
>  /* Return zero if no servers found, in that case we keep polling.
> 




More information about the Dnsmasq-discuss mailing list