Diff for /embedaddon/dnsmasq/src/netlink.c between versions 1.1.1.2 and 1.1.1.5

version 1.1.1.2, 2014/06/15 16:31:38 version 1.1.1.5, 2023/09/27 11:02:07
Line 1 Line 1
/* dnsmasq is Copyright (c) 2000-2014 Simon Kelley/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley
   
    This program is free software; you can redistribute it and/or modify     This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by     it under the terms of the GNU General Public License as published by
Line 22 Line 22
 #include <linux/netlink.h>  #include <linux/netlink.h>
 #include <linux/rtnetlink.h>  #include <linux/rtnetlink.h>
   
   /* Blergh. Radv does this, so that's our excuse. */
   #ifndef SOL_NETLINK
   #define SOL_NETLINK 270
   #endif
   
   #ifndef NETLINK_NO_ENOBUFS
   #define NETLINK_NO_ENOBUFS 5
   #endif
   
 /* linux 2.6.19 buggers up the headers, patch it up here. */   /* linux 2.6.19 buggers up the headers, patch it up here. */ 
 #ifndef IFA_RTA  #ifndef IFA_RTA
 #  define IFA_RTA(r)  \  #  define IFA_RTA(r)  \
Line 32 Line 41
   
 #ifndef NDA_RTA  #ifndef NDA_RTA
 #  define NDA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndmsg))))   #  define NDA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndmsg)))) 
#endif #endif
   
   /* Used to request refresh of addresses or routes just once,
    * when multiple changes might be announced. */
   enum async_states {
     STATE_NEWADDR = (1 << 0),
     STATE_NEWROUTE = (1 << 1),
   };
   
   
 static struct iovec iov;  static struct iovec iov;
 static u32 netlink_pid;  static u32 netlink_pid;
   
static int nl_async(struct nlmsghdr *h);static unsigned nl_async(struct nlmsghdr *h, unsigned state);
 static void nl_multicast_state(unsigned state);
   
void netlink_init(void)char *netlink_init(void)
 {  {
   struct sockaddr_nl addr;    struct sockaddr_nl addr;
   socklen_t slen = sizeof(addr);    socklen_t slen = sizeof(addr);
Line 49  void netlink_init(void) Line 66  void netlink_init(void)
   addr.nl_pad = 0;    addr.nl_pad = 0;
   addr.nl_pid = 0; /* autobind */    addr.nl_pid = 0; /* autobind */
   addr.nl_groups = RTMGRP_IPV4_ROUTE;    addr.nl_groups = RTMGRP_IPV4_ROUTE;
  if (option_bool(OPT_CLEVERBIND))  addr.nl_groups |= RTMGRP_IPV4_IFADDR;  
    addr.nl_groups |= RTMGRP_IPV4_IFADDR;   
#ifdef HAVE_IPV6 
   addr.nl_groups |= RTMGRP_IPV6_ROUTE;    addr.nl_groups |= RTMGRP_IPV6_ROUTE;
  if (option_bool(OPT_CLEVERBIND))  addr.nl_groups |= RTMGRP_IPV6_IFADDR;
    addr.nl_groups |= RTMGRP_IPV6_IFADDR;
#endif 
#ifdef HAVE_DHCP6 
  if (daemon->doing_ra || daemon->doing_dhcp6) 
    addr.nl_groups |= RTMGRP_IPV6_IFADDR; 
#endif 
   
   /* May not be able to have permission to set multicast groups don't die in that case */    /* May not be able to have permission to set multicast groups don't die in that case */
   if ((daemon->netlinkfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) != -1)    if ((daemon->netlinkfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) != -1)
     {      {
Line 73  void netlink_init(void) Line 82  void netlink_init(void)
     }      }
       
   if (daemon->netlinkfd == -1 ||     if (daemon->netlinkfd == -1 || 
      getsockname(daemon->netlinkfd, (struct sockaddr *)&addr, &slen) == 1)      getsockname(daemon->netlinkfd, (struct sockaddr *)&addr, &slen) == -1)
     die(_("cannot create netlink socket: %s"), NULL, EC_MISC);      die(_("cannot create netlink socket: %s"), NULL, EC_MISC);
     
   
   /* save pid assigned by bind() and retrieved by getsockname() */     /* save pid assigned by bind() and retrieved by getsockname() */ 
   netlink_pid = addr.nl_pid;    netlink_pid = addr.nl_pid;
       
   iov.iov_len = 100;    iov.iov_len = 100;
   iov.iov_base = safe_malloc(iov.iov_len);    iov.iov_base = safe_malloc(iov.iov_len);
     
     return NULL;
 }  }
   
static ssize_t netlink_recv(void)static ssize_t netlink_recv(int flags)
 {  {
   struct msghdr msg;    struct msghdr msg;
   struct sockaddr_nl nladdr;    struct sockaddr_nl nladdr;
Line 99  static ssize_t netlink_recv(void) Line 111  static ssize_t netlink_recv(void)
       msg.msg_iovlen = 1;        msg.msg_iovlen = 1;
       msg.msg_flags = 0;        msg.msg_flags = 0;
               
      while ((rc = recvmsg(daemon->netlinkfd, &msg, MSG_PEEK | MSG_TRUNC)) == -1 && errno == EINTR);      while ((rc = recvmsg(daemon->netlinkfd, &msg, flags | MSG_PEEK | MSG_TRUNC)) == -1 &&
              errno == EINTR);
               
       /* make buffer big enough */        /* make buffer big enough */
       if (rc != -1 && (msg.msg_flags & MSG_TRUNC))        if (rc != -1 && (msg.msg_flags & MSG_TRUNC))
Line 116  static ssize_t netlink_recv(void) Line 129  static ssize_t netlink_recv(void)
   
       /* read it for real */        /* read it for real */
       msg.msg_flags = 0;        msg.msg_flags = 0;
      while ((rc = recvmsg(daemon->netlinkfd, &msg, 0)) == -1 && errno == EINTR);      while ((rc = recvmsg(daemon->netlinkfd, &msg, flags)) == -1 && errno == EINTR);
               
       /* Make sure this is from the kernel */        /* Make sure this is from the kernel */
       if (rc == -1 || nladdr.nl_pid == 0)        if (rc == -1 || nladdr.nl_pid == 0)
Line 135  static ssize_t netlink_recv(void) Line 148  static ssize_t netlink_recv(void)
       
   
 /* family = AF_UNSPEC finds ARP table entries.  /* family = AF_UNSPEC finds ARP table entries.
   family = AF_LOCAL finds MAC addresses. */   family = AF_LOCAL finds MAC addresses.
    returns 0 on failure, 1 on success, -1 when restart is required
 */
 int iface_enumerate(int family, void *parm, int (*callback)())  int iface_enumerate(int family, void *parm, int (*callback)())
 {  {
   struct sockaddr_nl addr;    struct sockaddr_nl addr;
   struct nlmsghdr *h;    struct nlmsghdr *h;
   ssize_t len;    ssize_t len;
   static unsigned int seq = 0;    static unsigned int seq = 0;
  int callback_ok = 1, newaddr = 0;  int callback_ok = 1;
   unsigned state = 0;
   
   struct {    struct {
     struct nlmsghdr nlh;      struct nlmsghdr nlh;
     struct rtgenmsg g;       struct rtgenmsg g; 
   } req;    } req;
   
     memset(&req, 0, sizeof(req));
     memset(&addr, 0, sizeof(addr));
   
   addr.nl_family = AF_NETLINK;    addr.nl_family = AF_NETLINK;
   addr.nl_pad = 0;  
   addr.nl_groups = 0;  
   addr.nl_pid = 0; /* address to kernel */  
     
  again:   
   if (family == AF_UNSPEC)    if (family == AF_UNSPEC)
     req.nlh.nlmsg_type = RTM_GETNEIGH;      req.nlh.nlmsg_type = RTM_GETNEIGH;
   else if (family == AF_LOCAL)    else if (family == AF_LOCAL)
Line 169  int iface_enumerate(int family, void *parm, int (*call Line 184  int iface_enumerate(int family, void *parm, int (*call
   req.g.rtgen_family = family;     req.g.rtgen_family = family; 
   
   /* Don't block in recvfrom if send fails */    /* Don't block in recvfrom if send fails */
  while((len = sendto(daemon->netlinkfd, (void *)&req, sizeof(req), 0,   while(retry_send(sendto(daemon->netlinkfd, (void *)&req, sizeof(req), 0, 
                      (struct sockaddr *)&addr, sizeof(addr))) == -1 && retry_send());                          (struct sockaddr *)&addr, sizeof(addr))));
  
  if (len == -1)  if (errno != 0)
     return 0;      return 0;
           
   while (1)    while (1)
     {      {
      if ((len = netlink_recv()) == -1)      if ((len = netlink_recv(0)) == -1)
         {          {
           if (errno == ENOBUFS)            if (errno == ENOBUFS)
             {              {
              sleep(1);              nl_multicast_state(state);
              goto again;              return -1;
             }              }
           return 0;            return 0;
         }          }
   
       for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len))        for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len))
        if (h->nlmsg_seq != seq || h->nlmsg_pid != netlink_pid || h->nlmsg_type == NLMSG_ERROR)        if (h->nlmsg_pid != netlink_pid || h->nlmsg_type == NLMSG_ERROR)
           {            {
             /* May be multicast arriving async */              /* May be multicast arriving async */
            if (nl_async(h))            state = nl_async(h, state);
              { 
                newaddr = 1;  
                enumerate_interfaces(1); /* reset */ 
              } 
           }            }
        else if (h->nlmsg_type == NLMSG_DONE)        else if (h->nlmsg_seq != seq)
           {            {
            /* handle async new interface address arrivals, these have to be done            /* May be part of incomplete response to previous request after
               after we complete as we're not re-entrant */               ENOBUFS. Drop it. */
            if (newaddr)             continue;
              newaddress(dnsmasq_time()); 
                 
            return callback_ok; 
           }            }
           else if (h->nlmsg_type == NLMSG_DONE)
             return callback_ok;
         else if (h->nlmsg_type == RTM_NEWADDR && family != AF_UNSPEC && family != AF_LOCAL)          else if (h->nlmsg_type == RTM_NEWADDR && family != AF_UNSPEC && family != AF_LOCAL)
           {            {
             struct ifaddrmsg *ifa = NLMSG_DATA(h);                struct ifaddrmsg *ifa = NLMSG_DATA(h);  
Line 219  int iface_enumerate(int family, void *parm, int (*call Line 229  int iface_enumerate(int family, void *parm, int (*call
                     struct in_addr netmask, addr, broadcast;                      struct in_addr netmask, addr, broadcast;
                     char *label = NULL;                      char *label = NULL;
   
                    netmask.s_addr = htonl(0xffffffff << (32 - ifa->ifa_prefixlen));                    netmask.s_addr = htonl(~(in_addr_t)0 << (32 - ifa->ifa_prefixlen));
 
                     addr.s_addr = 0;                      addr.s_addr = 0;
                     broadcast.s_addr = 0;                      broadcast.s_addr = 0;
                                           
Line 239  int iface_enumerate(int family, void *parm, int (*call Line 250  int iface_enumerate(int family, void *parm, int (*call
                       if (!((*callback)(addr, ifa->ifa_index, label,  netmask, broadcast, parm)))                        if (!((*callback)(addr, ifa->ifa_index, label,  netmask, broadcast, parm)))
                         callback_ok = 0;                          callback_ok = 0;
                   }                    }
 #ifdef HAVE_IPV6  
                 else if (ifa->ifa_family == AF_INET6)                  else if (ifa->ifa_family == AF_INET6)
                   {                    {
                     struct in6_addr *addrp = NULL;                      struct in6_addr *addrp = NULL;
Line 248  int iface_enumerate(int family, void *parm, int (*call Line 258  int iface_enumerate(int family, void *parm, int (*call
                                           
                     while (RTA_OK(rta, len1))                      while (RTA_OK(rta, len1))
                       {                        {
                        if (rta->rta_type == IFA_ADDRESS)                        /*
                          * Important comment: (from if_addr.h)
                          * IFA_ADDRESS is prefix address, rather than local interface address.
                          * It makes no difference for normally configured broadcast interfaces,
                          * but for point-to-point IFA_ADDRESS is DESTINATION address,
                          * local address is supplied in IFA_LOCAL attribute.
                          */
                         if (rta->rta_type == IFA_LOCAL)
                           addrp = ((struct in6_addr *)(rta+1));
                         else if (rta->rta_type == IFA_ADDRESS && !addrp)
                           addrp = ((struct in6_addr *)(rta+1));                             addrp = ((struct in6_addr *)(rta+1)); 
                         else if (rta->rta_type == IFA_CACHEINFO)                          else if (rta->rta_type == IFA_CACHEINFO)
                           {                            {
Line 274  int iface_enumerate(int family, void *parm, int (*call Line 293  int iface_enumerate(int family, void *parm, int (*call
                                         (int) preferred, (int)valid, parm)))                                          (int) preferred, (int)valid, parm)))
                         callback_ok = 0;                          callback_ok = 0;
                   }                    }
 #endif  
               }                }
           }            }
         else if (h->nlmsg_type == RTM_NEWNEIGH && family == AF_UNSPEC)          else if (h->nlmsg_type == RTM_NEWNEIGH && family == AF_UNSPEC)
Line 298  int iface_enumerate(int family, void *parm, int (*call Line 316  int iface_enumerate(int family, void *parm, int (*call
                 rta = RTA_NEXT(rta, len1);                  rta = RTA_NEXT(rta, len1);
               }                }
   
            if (inaddr && mac && callback_ok)            if (!(neigh->ndm_state & (NUD_NOARP | NUD_INCOMPLETE | NUD_FAILED)) &&
                 inaddr && mac && callback_ok)
               if (!((*callback)(neigh->ndm_family, inaddr, mac, maclen, parm)))                if (!((*callback)(neigh->ndm_family, inaddr, mac, maclen, parm)))
                 callback_ok = 0;                  callback_ok = 0;
           }            }
Line 330  int iface_enumerate(int family, void *parm, int (*call Line 349  int iface_enumerate(int family, void *parm, int (*call
     }      }
 }  }
   
void netlink_multicast(time_t now)static void nl_multicast_state(unsigned state)
 {  {
   ssize_t len;    ssize_t len;
   struct nlmsghdr *h;    struct nlmsghdr *h;
  int flags, newaddr = 0;
   do {
     /* don't risk blocking reading netlink messages here. */
     while ((len = netlink_recv(MSG_DONTWAIT)) != -1)
       
  /* don't risk blocking reading netlink messages here. */      for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len))
  if ((flags = fcntl(daemon->netlinkfd, F_GETFL)) == -1 ||        state = nl_async(h, state);
      fcntl(daemon->netlinkfd, F_SETFL, flags | O_NONBLOCK) == -1)   } while (errno == ENOBUFS);
    return; 
   
  if ((len = netlink_recv()) != -1) 
    for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len)) 
      if (nl_async(h)) 
        newaddr = 1; 
   
  /* restore non-blocking status */ 
  fcntl(daemon->netlinkfd, F_SETFL, flags); 
   
  if (newaddr)  
    newaddress(now); 
 }  }
   
static int nl_async(struct nlmsghdr *h)void netlink_multicast(void)
 {  {
     unsigned state = 0;
     nl_multicast_state(state);
   }
   
   
   static unsigned nl_async(struct nlmsghdr *h, unsigned state)
   {
   if (h->nlmsg_type == NLMSG_ERROR)    if (h->nlmsg_type == NLMSG_ERROR)
     {      {
       struct nlmsgerr *err = NLMSG_DATA(h);        struct nlmsgerr *err = NLMSG_DATA(h);
       if (err->error != 0)        if (err->error != 0)
         my_syslog(LOG_ERR, _("netlink returns error: %s"), strerror(-(err->error)));          my_syslog(LOG_ERR, _("netlink returns error: %s"), strerror(-(err->error)));
       return 0;  
     }      }
  else if (h->nlmsg_pid == 0 && h->nlmsg_type == RTM_NEWROUTE  else if (h->nlmsg_pid == 0 && h->nlmsg_type == RTM_NEWROUTE &&
            (state & STATE_NEWROUTE)==0)
     {      {
       /* We arrange to receive netlink multicast messages whenever the network route is added.        /* We arrange to receive netlink multicast messages whenever the network route is added.
          If this happens and we still have a DNS packet in the buffer, we re-send it.           If this happens and we still have a DNS packet in the buffer, we re-send it.
Line 371  static int nl_async(struct nlmsghdr *h) Line 388  static int nl_async(struct nlmsghdr *h)
          failing. */            failing. */ 
       struct rtmsg *rtm = NLMSG_DATA(h);        struct rtmsg *rtm = NLMSG_DATA(h);
               
      if (rtm->rtm_type == RTN_UNICAST && rtm->rtm_scope == RT_SCOPE_LINK)      if (rtm->rtm_type == RTN_UNICAST && rtm->rtm_scope == RT_SCOPE_LINK &&
           (rtm->rtm_table == RT_TABLE_MAIN ||
            rtm->rtm_table == RT_TABLE_LOCAL))
         {          {
          /* Force re-reading resolv file right now, for luck. */          queue_event(EVENT_NEWROUTE);
          daemon->last_resolv = 0;          state |= STATE_NEWROUTE;
           
          if (daemon->srv_save) 
            { 
              int fd; 
 
              if (daemon->srv_save->sfd) 
                fd = daemon->srv_save->sfd->fd; 
              else if (daemon->rfd_save && daemon->rfd_save->refcount != 0) 
                fd = daemon->rfd_save->fd; 
              else 
                return 0; 
               
              while(sendto(fd, daemon->packet, daemon->packet_len, 0, 
                           &daemon->srv_save->addr.sa, sa_len(&daemon->srv_save->addr)) == -1 && retry_send());  
            } 
         }          }
       return 0;  
     }      }
  else if (h->nlmsg_type == RTM_NEWADDR || h->nlmsg_type == RTM_DELADDR)   else if ((h->nlmsg_type == RTM_NEWADDR || h->nlmsg_type == RTM_DELADDR) &&
    return 1; /* clever bind mode - rescan */           (state & STATE_NEWADDR)==0)
      {
  return 0;      queue_event(EVENT_NEWADDR);
       state |= STATE_NEWADDR;
     }
   return state;
 }  }
#endif#endif /* HAVE_LINUX_NETWORK */
 
       

Removed from v.1.1.1.2  
changed lines
  Added in v.1.1.1.5


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>