Diff for /embedaddon/dnsmasq/src/dhcp.c between versions 1.1.1.3 and 1.1.1.5

version 1.1.1.3, 2016/11/02 09:57:01 version 1.1.1.5, 2023/09/27 11:02:07
Line 1 Line 1
/* dnsmasq is Copyright (c) 2000-2016 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 20 Line 20
   
 struct iface_param {  struct iface_param {
   struct dhcp_context *current;    struct dhcp_context *current;
   struct dhcp_relay *relay;  
   struct in_addr relay_local;  
   int ind;    int ind;
 };  };
   
Line 34  static int complete_context(struct in_addr local, int  Line 32  static int complete_context(struct in_addr local, int 
                             struct in_addr netmask, struct in_addr broadcast, void *vparam);                              struct in_addr netmask, struct in_addr broadcast, void *vparam);
 static int check_listen_addrs(struct in_addr local, int if_index, char *label,  static int check_listen_addrs(struct in_addr local, int if_index, char *label,
                               struct in_addr netmask, struct in_addr broadcast, void *vparam);                                struct in_addr netmask, struct in_addr broadcast, void *vparam);
static int relay_upstream4(struct dhcp_relay *relay, struct dhcp_packet *mess, size_t sz, int iface_index);static int relay_upstream4(int iface_index, struct dhcp_packet *mess, size_t sz);
 static struct dhcp_relay *relay_reply4(struct dhcp_packet *mess, char *arrival_interface);  static struct dhcp_relay *relay_reply4(struct dhcp_packet *mess, char *arrival_interface);
   
 static int make_fd(int port)  static int make_fd(int port)
Line 67  static int make_fd(int port) Line 65  static int make_fd(int port)
       setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &oneopt, sizeof(oneopt)) == -1)          setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &oneopt, sizeof(oneopt)) == -1)  
     die(_("failed to set options on DHCP socket: %s"), NULL, EC_BADNET);      die(_("failed to set options on DHCP socket: %s"), NULL, EC_BADNET);
       
  /* When bind-interfaces is set, there might be more than one dnmsasq  /* When bind-interfaces is set, there might be more than one dnsmasq
      instance binding port 67. That's OK if they serve different networks.       instance binding port 67. That's OK if they serve different networks.
     Need to set REUSEADDR|REUSEPORT to make this posible.     Need to set REUSEADDR|REUSEPORT to make this possible.
      Handle the case that REUSEPORT is defined, but the kernel doesn't        Handle the case that REUSEPORT is defined, but the kernel doesn't 
      support it. This handles the introduction of REUSEPORT on Linux. */       support it. This handles the introduction of REUSEPORT on Linux. */
   if (option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND))    if (option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND))
Line 145  void dhcp_packet(time_t now, int pxe_fd) Line 143  void dhcp_packet(time_t now, int pxe_fd)
   struct cmsghdr *cmptr;    struct cmsghdr *cmptr;
   struct iovec iov;    struct iovec iov;
   ssize_t sz;     ssize_t sz; 
  int iface_index = 0, unicast_dest = 0, is_inform = 0;  int iface_index = 0, unicast_dest = 0, is_inform = 0, loopback = 0;
   int rcvd_iface_index;    int rcvd_iface_index;
   struct in_addr iface_addr;    struct in_addr iface_addr;
   struct iface_param parm;    struct iface_param parm;
     time_t recvtime = now;
 #ifdef HAVE_LINUX_NETWORK  #ifdef HAVE_LINUX_NETWORK
   struct arpreq arp_req;    struct arpreq arp_req;
     struct timeval tv;
 #endif  #endif
       
   union {    union {
Line 175  void dhcp_packet(time_t now, int pxe_fd) Line 175  void dhcp_packet(time_t now, int pxe_fd)
   if ((sz = recv_dhcp_packet(fd, &msg)) == -1 ||     if ((sz = recv_dhcp_packet(fd, &msg)) == -1 || 
       (sz < (ssize_t)(sizeof(*mess) - sizeof(mess->options))))         (sz < (ssize_t)(sizeof(*mess) - sizeof(mess->options)))) 
     return;      return;
      
  #if defined (HAVE_LINUX_NETWORK)#ifdef HAVE_DUMPFILE
   dump_packet_udp(DUMP_DHCP, (void *)daemon->dhcp_packet.iov_base, sz, (union mysockaddr *)&dest, NULL, fd);
 #endif
   
 #if defined (HAVE_LINUX_NETWORK)
   if (ioctl(fd, SIOCGSTAMP, &tv) == 0)
     recvtime = tv.tv_sec;
   
   if (msg.msg_controllen >= sizeof(struct cmsghdr))    if (msg.msg_controllen >= sizeof(struct cmsghdr))
     for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))      for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
       if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_PKTINFO)        if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_PKTINFO)
Line 218  void dhcp_packet(time_t now, int pxe_fd) Line 225  void dhcp_packet(time_t now, int pxe_fd)
         }          }
 #endif  #endif
                   
  if (!indextoname(daemon->dhcpfd, iface_index, ifr.ifr_name))  if (!indextoname(daemon->dhcpfd, iface_index, ifr.ifr_name) ||
       ioctl(daemon->dhcpfd, SIOCGIFFLAGS, &ifr) != 0)
     return;      return;
  
   mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
   loopback = !mess->giaddr.s_addr && (ifr.ifr_flags & IFF_LOOPBACK);
   
 #ifdef HAVE_LINUX_NETWORK  #ifdef HAVE_LINUX_NETWORK
   /* ARP fiddling uses original interface even if we pretend to use a different one. */    /* ARP fiddling uses original interface even if we pretend to use a different one. */
  strncpy(arp_req.arp_dev, ifr.ifr_name, 16);  safe_strncpy(arp_req.arp_dev, ifr.ifr_name, sizeof(arp_req.arp_dev));
 #endif   #endif 
   
   /* If the interface on which the DHCP request was received is an    /* If the interface on which the DHCP request was received is an
Line 246  void dhcp_packet(time_t now, int pxe_fd) Line 257  void dhcp_packet(time_t now, int pxe_fd)
               }                }
             else               else 
               {                {
                strncpy(ifr.ifr_name,  bridge->iface, IF_NAMESIZE);                safe_strncpy(ifr.ifr_name,  bridge->iface, sizeof(ifr.ifr_name));
                 break;                  break;
               }                }
           }            }
Line 264  void dhcp_packet(time_t now, int pxe_fd) Line 275  void dhcp_packet(time_t now, int pxe_fd)
   if ((relay = relay_reply4((struct dhcp_packet *)daemon->dhcp_packet.iov_base, ifr.ifr_name)))    if ((relay = relay_reply4((struct dhcp_packet *)daemon->dhcp_packet.iov_base, ifr.ifr_name)))
     {      {
       /* Reply from server, using us as relay. */        /* Reply from server, using us as relay. */
      iface_index = relay->iface_index;      rcvd_iface_index = relay->iface_index;
      if (!indextoname(daemon->dhcpfd, iface_index, ifr.ifr_name))      if (!indextoname(daemon->dhcpfd, rcvd_iface_index, ifr.ifr_name))
         return;          return;
       is_relay_reply = 1;         is_relay_reply = 1; 
       iov.iov_len = sz;        iov.iov_len = sz;
 #ifdef HAVE_LINUX_NETWORK  #ifdef HAVE_LINUX_NETWORK
      strncpy(arp_req.arp_dev, ifr.ifr_name, 16);      safe_strncpy(arp_req.arp_dev, ifr.ifr_name, sizeof(arp_req.arp_dev));
 #endif   #endif 
     }      }
   else    else
Line 280  void dhcp_packet(time_t now, int pxe_fd) Line 291  void dhcp_packet(time_t now, int pxe_fd)
         iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;          iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
       else        else
         {          {
          my_syslog(MS_DHCP | LOG_WARNING, _("DHCP packet received on %s which has no address"), ifr.ifr_name);          if (iface_check(AF_INET, NULL, ifr.ifr_name, NULL))
             my_syslog(MS_DHCP | LOG_WARNING, _("DHCP packet received on %s which has no address"), ifr.ifr_name);
           return;            return;
         }          }
               
Line 292  void dhcp_packet(time_t now, int pxe_fd) Line 304  void dhcp_packet(time_t now, int pxe_fd)
       for (context = daemon->dhcp; context; context = context->next)        for (context = daemon->dhcp; context; context = context->next)
         context->current = context;          context->current = context;
               
       for (relay = daemon->relay4; relay; relay = relay->next)  
         relay->current = relay;  
         
       parm.current = NULL;        parm.current = NULL;
       parm.relay = NULL;  
       parm.relay_local.s_addr = 0;  
       parm.ind = iface_index;        parm.ind = iface_index;
               
      if (!iface_check(AF_INET, (struct all_addr *)&iface_addr, ifr.ifr_name, NULL))      if (!iface_check(AF_INET, (union all_addr *)&iface_addr, ifr.ifr_name, NULL))
         {          {
           /* If we failed to match the primary address of the interface, see if we've got a --listen-address            /* If we failed to match the primary address of the interface, see if we've got a --listen-address
              for a secondary */               for a secondary */
Line 319  void dhcp_packet(time_t now, int pxe_fd) Line 326  void dhcp_packet(time_t now, int pxe_fd)
              there is more than one address on the interface in the same subnet */               there is more than one address on the interface in the same subnet */
           complete_context(match.addr, iface_index, NULL, match.netmask, match.broadcast, &parm);            complete_context(match.addr, iface_index, NULL, match.netmask, match.broadcast, &parm);
         }              }    
               
         if (relay_upstream4(iface_index, mess, (size_t)sz))
           return;
               
       if (!iface_enumerate(AF_INET, &parm, complete_context))        if (!iface_enumerate(AF_INET, &parm, complete_context))
         return;          return;
   
      /* We're relaying this request */      /* Check for a relay again after iface_enumerate/complete_context has had
      if  (parm.relay_local.s_addr != 0 &&         chance to fill in relay->iface_index fields. This handles first time through
           relay_upstream4(parm.relay, (struct dhcp_packet *)daemon->dhcp_packet.iov_base, (size_t)sz, iface_index))         and any changes in interface config. */
        if (relay_upstream4(iface_index, mess, (size_t)sz))
         return;          return;
       
       /* May have configured relay, but not DHCP server */        /* May have configured relay, but not DHCP server */
       if (!daemon->dhcp)        if (!daemon->dhcp)
         return;          return;
   
       lease_prune(NULL, now); /* lose any expired leases */        lease_prune(NULL, now); /* lose any expired leases */
       iov.iov_len = dhcp_reply(parm.current, ifr.ifr_name, iface_index, (size_t)sz,         iov.iov_len = dhcp_reply(parm.current, ifr.ifr_name, iface_index, (size_t)sz, 
                               now, unicast_dest, &is_inform, pxe_fd, iface_addr);                               now, unicast_dest, loopback, &is_inform, pxe_fd, iface_addr, recvtime);
       lease_update_file(now);        lease_update_file(now);
       lease_update_dns(0);        lease_update_dns(0);
               
       if (iov.iov_len == 0)        if (iov.iov_len == 0)
         return;          return;
     }      }
  
   msg.msg_name = &dest;    msg.msg_name = &dest;
   msg.msg_namelen = sizeof(dest);    msg.msg_namelen = sizeof(dest);
   msg.msg_control = NULL;    msg.msg_control = NULL;
Line 391  void dhcp_packet(time_t now, int pxe_fd) Line 402  void dhcp_packet(time_t now, int pxe_fd)
       pkt = (struct in_pktinfo *)CMSG_DATA(cmptr);        pkt = (struct in_pktinfo *)CMSG_DATA(cmptr);
       pkt->ipi_ifindex = rcvd_iface_index;        pkt->ipi_ifindex = rcvd_iface_index;
       pkt->ipi_spec_dst.s_addr = 0;        pkt->ipi_spec_dst.s_addr = 0;
      msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));      msg.msg_controllen = CMSG_SPACE(sizeof(struct in_pktinfo));
       cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
       cmptr->cmsg_level = IPPROTO_IP;        cmptr->cmsg_level = IPPROTO_IP;
       cmptr->cmsg_type = IP_PKTINFO;        cmptr->cmsg_type = IP_PKTINFO;
   
Line 444  void dhcp_packet(time_t now, int pxe_fd) Line 456  void dhcp_packet(time_t now, int pxe_fd)
 #elif defined(HAVE_BSD_NETWORK)  #elif defined(HAVE_BSD_NETWORK)
   else     else 
     {      {
   #ifdef HAVE_DUMPFILE
         if (ntohs(mess->flags) & 0x8000)
           dest.sin_addr.s_addr = INADDR_BROADCAST;
         else
           dest.sin_addr = mess->yiaddr;
         dest.sin_port = htons(daemon->dhcp_client_port);
         
         dump_packet_udp(DUMP_DHCP, (void *)iov.iov_base, iov.iov_len, NULL,
                         (union mysockaddr *)&dest, fd);
   #endif
         
       send_via_bpf(mess, iov.iov_len, iface_addr, &ifr);        send_via_bpf(mess, iov.iov_len, iface_addr, &ifr);
       return;        return;
     }      }
Line 452  void dhcp_packet(time_t now, int pxe_fd) Line 475  void dhcp_packet(time_t now, int pxe_fd)
 #ifdef HAVE_SOLARIS_NETWORK  #ifdef HAVE_SOLARIS_NETWORK
   setsockopt(fd, IPPROTO_IP, IP_BOUND_IF, &iface_index, sizeof(iface_index));    setsockopt(fd, IPPROTO_IP, IP_BOUND_IF, &iface_index, sizeof(iface_index));
 #endif  #endif
   
   #ifdef HAVE_DUMPFILE
     dump_packet_udp(DUMP_DHCP, (void *)iov.iov_base, iov.iov_len, NULL,
                     (union mysockaddr *)&dest, fd);
   #endif
       
   while(retry_send(sendmsg(fd, &msg, 0)));    while(retry_send(sendmsg(fd, &msg, 0)));
   
   /* This can fail when, eg, iptables DROPS destination 255.255.255.255 */    /* This can fail when, eg, iptables DROPS destination 255.255.255.255 */
   if (errno != 0)    if (errno != 0)
    my_syslog(MS_DHCP | LOG_WARNING, _("Error sending DHCP packet to %s: %s"),    {
              inet_ntoa(dest.sin_addr), strerror(errno));      inet_ntop(AF_INET, &dest.sin_addr, daemon->addrbuff, ADDRSTRLEN);
       my_syslog(MS_DHCP | LOG_WARNING, _("Error sending DHCP packet to %s: %s"),
                 daemon->addrbuff, strerror(errno));
     }
 }  }
   
 /* check against secondary interface addresses */  /* check against secondary interface addresses */
Line 495  static int check_listen_addrs(struct in_addr local, in Line 526  static int check_listen_addrs(struct in_addr local, in
    3) Fills in local (this host) and router (this host or relay) addresses.     3) Fills in local (this host) and router (this host or relay) addresses.
    4) Links contexts which are valid for hosts directly connected to the arrival interface on ->current.     4) Links contexts which are valid for hosts directly connected to the arrival interface on ->current.
   
   Note that the current chain may be superceded later for configured hosts or those coming via gateways. */   Note that the current chain may be superseded later for configured hosts or those coming via gateways. */
   
static int complete_context(struct in_addr local, int if_index, char *label,static void guess_range_netmask(struct in_addr addr, struct in_addr netmask)
                            struct in_addr netmask, struct in_addr broadcast, void *vparam) 
 {  {
   struct dhcp_context *context;    struct dhcp_context *context;
   struct dhcp_relay *relay;  
   struct iface_param *param = vparam;  
   
   (void)label;  
     
   for (context = daemon->dhcp; context; context = context->next)    for (context = daemon->dhcp; context; context = context->next)
    {    if (!(context->flags & CONTEXT_NETMASK) &&
      if (!(context->flags & CONTEXT_NETMASK) &&        (is_same_net(addr, context->start, netmask) ||
          (is_same_net(local, context->start, netmask) ||         is_same_net(addr, context->end, netmask)))
           is_same_net(local, context->end, netmask))) 
       {         { 
         if (context->netmask.s_addr != netmask.s_addr &&          if (context->netmask.s_addr != netmask.s_addr &&
            !(is_same_net(local, context->start, netmask) &&            !(is_same_net(addr, context->start, netmask) &&
              is_same_net(local, context->end, netmask)))              is_same_net(addr, context->end, netmask)))
           {            {
            strcpy(daemon->dhcp_buff, inet_ntoa(context->start));            inet_ntop(AF_INET, &context->start, daemon->dhcp_buff, DHCP_BUFF_SZ);
            strcpy(daemon->dhcp_buff2, inet_ntoa(context->end));            inet_ntop(AF_INET, &context->end, daemon->dhcp_buff2, DHCP_BUFF_SZ);
             inet_ntop(AF_INET, &netmask, daemon->addrbuff, ADDRSTRLEN);
             my_syslog(MS_DHCP | LOG_WARNING, _("DHCP range %s -- %s is not consistent with netmask %s"),              my_syslog(MS_DHCP | LOG_WARNING, _("DHCP range %s -- %s is not consistent with netmask %s"),
                      daemon->dhcp_buff, daemon->dhcp_buff2, inet_ntoa(netmask));                      daemon->dhcp_buff, daemon->dhcp_buff2, daemon->addrbuff);
           }                 }     
        context->netmask = netmask;        context->netmask = netmask;
       }        }
   }
   
   static int complete_context(struct in_addr local, int if_index, char *label,
                               struct in_addr netmask, struct in_addr broadcast, void *vparam)
   {
     struct dhcp_context *context;
     struct dhcp_relay *relay;
     struct iface_param *param = vparam;
     struct shared_network *share;
     
     (void)label;
   
     for (share = daemon->shared_networks; share; share = share->next)
       {
               
   #ifdef HAVE_DHCP6
         if (share->shared_addr.s_addr == 0)
           continue;
   #endif
         
         if (share->if_index != 0)
           {
             if (share->if_index != if_index)
               continue;
           }
         else
           {
             if (share->match_addr.s_addr != local.s_addr)
               continue;
           }
   
         for (context = daemon->dhcp; context; context = context->next)
           {
             if (context->netmask.s_addr != 0 &&
                 is_same_net(share->shared_addr, context->start, context->netmask) &&
                 is_same_net(share->shared_addr, context->end, context->netmask))
               {
                 /* link it onto the current chain if we've not seen it before */
                 if (context->current == context)
                   {
                     /* For a shared network, we have no way to guess what the default route should be. */
                     context->router.s_addr = 0;
                     context->local = local; /* Use configured address for Server Identifier */
                     context->current = param->current;
                     param->current = context;
                   }
                 
                 if (!(context->flags & CONTEXT_BRDCAST))
                   context->broadcast.s_addr  = context->start.s_addr | ~context->netmask.s_addr;
               }           
           }
       }
   
     guess_range_netmask(local, netmask);
     
     for (context = daemon->dhcp; context; context = context->next)
       {
       if (context->netmask.s_addr != 0 &&        if (context->netmask.s_addr != 0 &&
           is_same_net(local, context->start, context->netmask) &&            is_same_net(local, context->start, context->netmask) &&
           is_same_net(local, context->end, context->netmask))            is_same_net(local, context->end, context->netmask))
Line 548  static int complete_context(struct in_addr local, int  Line 630  static int complete_context(struct in_addr local, int 
     }      }
   
   for (relay = daemon->relay4; relay; relay = relay->next)    for (relay = daemon->relay4; relay; relay = relay->next)
    if (if_index == param->ind && relay->local.addr.addr4.s_addr == local.s_addr && relay->current == relay &&    if (relay->local.addr4.s_addr == local.s_addr)
        (param->relay_local.s_addr == 0 || param->relay_local.s_addr == local.s_addr))      relay->iface_index = if_index;
      {  
        relay->current = param->relay; 
        param->relay = relay; 
        param->relay_local = local;      
      } 
 
   return 1;    return 1;
 }  }
                       
Line 595  struct dhcp_context *narrow_context(struct dhcp_contex Line 672  struct dhcp_context *narrow_context(struct dhcp_contex
 {  {
   /* We start of with a set of possible contexts, all on the current physical interface.    /* We start of with a set of possible contexts, all on the current physical interface.
      These are chained on ->current.       These are chained on ->current.
     Here we have an address, and return the actual context correponding to that     Here we have an address, and return the actual context corresponding to that
      address. Note that none may fit, if the address came a dhcp-host and is outside       address. Note that none may fit, if the address came a dhcp-host and is outside
      any dhcp-range. In that case we return a static range if possible, or failing that,       any dhcp-range. In that case we return a static range if possible, or failing that,
      any context on the correct subnet. (If there's more than one, this is a dodgy        any context on the correct subnet. (If there's more than one, this is a dodgy 
Line 637  struct dhcp_config *config_find_by_address(struct dhcp Line 714  struct dhcp_config *config_find_by_address(struct dhcp
   return NULL;    return NULL;
 }  }
   
   /* Check if and address is in use by sending ICMP ping.
      This wrapper handles a cache and load-limiting.
      Return is NULL is address in use, or a pointer to a cache entry
      recording that it isn't. */
   struct ping_result *do_icmp_ping(time_t now, struct in_addr addr, unsigned int hash, int loopback)
   {
     static struct ping_result dummy;
     struct ping_result *r, *victim = NULL;
     int count, max = (int)(0.6 * (((float)PING_CACHE_TIME)/
                                   ((float)PING_WAIT)));
   
     /* check if we failed to ping addr sometime in the last
        PING_CACHE_TIME seconds. If so, assume the same situation still exists.
        This avoids problems when a stupid client bangs
        on us repeatedly. As a final check, if we did more
        than 60% of the possible ping checks in the last 
        PING_CACHE_TIME, we are in high-load mode, so don't do any more. */
     for (count = 0, r = daemon->ping_results; r; r = r->next)
       if (difftime(now, r->time) >  (float)PING_CACHE_TIME)
         victim = r; /* old record */
       else 
         {
           count++;
           if (r->addr.s_addr == addr.s_addr)
             return r;
         }
     
     /* didn't find cached entry */
     if ((count >= max) || option_bool(OPT_NO_PING) || loopback)
       {
         /* overloaded, or configured not to check, loopback interface, return "not in use" */
         dummy.hash = hash;
         return &dummy;
       }
     else if (icmp_ping(addr))
       return NULL; /* address in use. */
     else
       {
         /* at this point victim may hold an expired record */
         if (!victim)
           {
             if ((victim = whine_malloc(sizeof(struct ping_result))))
               {
                 victim->next = daemon->ping_results;
                 daemon->ping_results = victim;
               }
           }
         
         /* record that this address is OK for 30s 
            without more ping checks */
         if (victim)
           {
             victim->addr = addr;
             victim->time = now;
             victim->hash = hash;
           }
         return victim;
       }
   }
   
 int address_allocate(struct dhcp_context *context,  int address_allocate(struct dhcp_context *context,
                      struct in_addr *addrp, unsigned char *hwaddr, int hw_len,                        struct in_addr *addrp, unsigned char *hwaddr, int hw_len, 
                     struct dhcp_netid *netids, time_t now)                        struct dhcp_netid *netids, time_t now, int loopback)   
 {  {
   /* Find a free address: exclude anything in use and anything allocated to    /* Find a free address: exclude anything in use and anything allocated to
      a particular hwaddr/clientid/hostname in our configuration.       a particular hwaddr/clientid/hostname in our configuration.
Line 654  int address_allocate(struct dhcp_context *context, Line 791  int address_allocate(struct dhcp_context *context,
      dispersal even with similarly-valued "strings". */        dispersal even with similarly-valued "strings". */ 
   for (j = 0, i = 0; i < hw_len; i++)    for (j = 0, i = 0; i < hw_len; i++)
     j = hwaddr[i] + (j << 6) + (j << 16) - j;      j = hwaddr[i] + (j << 6) + (j << 16) - j;
   
     /* j == 0 is marker */
     if (j == 0)
       j = 1;
       
   for (pass = 0; pass <= 1; pass++)    for (pass = 0; pass <= 1; pass++)
     for (c = context; c; c = c->current)      for (c = context; c; c = c->current)
Line 691  int address_allocate(struct dhcp_context *context, Line 832  int address_allocate(struct dhcp_context *context,
                 (!IN_CLASSC(ntohl(addr.s_addr)) ||                   (!IN_CLASSC(ntohl(addr.s_addr)) || 
                  ((ntohl(addr.s_addr) & 0xff) != 0xff && ((ntohl(addr.s_addr) & 0xff) != 0x0))))                   ((ntohl(addr.s_addr) & 0xff) != 0xff && ((ntohl(addr.s_addr) & 0xff) != 0x0))))
               {                {
                struct ping_result *r, *victim = NULL;                /* in consec-ip mode, skip addresses equal to
                int count, max = (int)(0.6 * (((float)PING_CACHE_TIME)/                   the number of addresses rejected by clients. This
                                              ((float)PING_WAIT)));                   should avoid the same client being offered the same
                                   address after it has rjected it. */
                *addrp = addr;                if (option_bool(OPT_CONSEC_ADDR) && c->addr_epoch)
                  c->addr_epoch--;
                /* check if we failed to ping addr sometime in the last                else
                   PING_CACHE_TIME seconds. If so, assume the same situation still exists. 
                   This avoids problems when a stupid client bangs 
                   on us repeatedly. As a final check, if we did more 
                   than 60% of the possible ping checks in the last  
                   PING_CACHE_TIME, we are in high-load mode, so don't do any more. */ 
                for (count = 0, r = daemon->ping_results; r; r = r->next) 
                  if (difftime(now, r->time) >  (float)PING_CACHE_TIME) 
                    victim = r; /* old record */ 
                  else  
                    { 
                      count++; 
                      if (r->addr.s_addr == addr.s_addr) 
                        { 
                          /* consec-ip mode: we offered this address for another client 
                             (different hash) recently, don't offer it to this one. */ 
                          if (option_bool(OPT_CONSEC_ADDR) && r->hash != j) 
                            break; 
                           
                          return 1; 
                        } 
                    } 
 
                if (!r)  
                   {                    {
                    if ((count < max) && !option_bool(OPT_NO_PING) && icmp_ping(addr))                    struct ping_result *r;
                     
                     if ((r = do_icmp_ping(now, addr, j, loopback)))
                       {                        {
                           /* consec-ip mode: we offered this address for another client
                              (different hash) recently, don't offer it to this one. */
                           if (!option_bool(OPT_CONSEC_ADDR) || r->hash == j)
                             {
                               *addrp = addr;
                               return 1;
                             }
                         }
                       else
                         {
                         /* address in use: perturb address selection so that we are                          /* address in use: perturb address selection so that we are
                            less likely to try this address again. */                             less likely to try this address again. */
                         if (!option_bool(OPT_CONSEC_ADDR))                          if (!option_bool(OPT_CONSEC_ADDR))
                           c->addr_epoch++;                            c->addr_epoch++;
                       }                        }
                     else  
                       {  
                         /* at this point victim may hold an expired record */  
                         if (!victim)  
                           {  
                             if ((victim = whine_malloc(sizeof(struct ping_result))))  
                               {  
                                 victim->next = daemon->ping_results;  
                                 daemon->ping_results = victim;  
                               }  
                           }  
                           
                         /* record that this address is OK for 30s   
                            without more ping checks */  
                         if (victim)  
                           {  
                             victim->addr = addr;  
                             victim->time = now;  
                             victim->hash = j;  
                           }  
                         return 1;  
                       }  
                   }                    }
               }                }
            
             addr.s_addr = htonl(ntohl(addr.s_addr) + 1);              addr.s_addr = htonl(ntohl(addr.s_addr) + 1);
                           
             if (addr.s_addr == htonl(ntohl(c->end.s_addr) + 1))              if (addr.s_addr == htonl(ntohl(c->end.s_addr) + 1))
Line 808  void dhcp_read_ethers(void) Line 916  void dhcp_read_ethers(void)
               
       lineno++;        lineno++;
               
      while (strlen(buff) > 0 && isspace((int)buff[strlen(buff)-1]))      while (strlen(buff) > 0 && isspace((unsigned char)buff[strlen(buff)-1]))
         buff[strlen(buff)-1] = 0;          buff[strlen(buff)-1] = 0;
               
       if ((*buff == '#') || (*buff == '+') || (*buff == 0))        if ((*buff == '#') || (*buff == '+') || (*buff == 0))
         continue;          continue;
               
      for (ip = buff; *ip && !isspace((int)*ip); ip++);      for (ip = buff; *ip && !isspace((unsigned char)*ip); ip++);
      for(; *ip && isspace((int)*ip); ip++)      for(; *ip && isspace((unsigned char)*ip); ip++)
         *ip = 0;          *ip = 0;
       if (!*ip || parse_hex(buff, hwaddr, ETHER_ADDR_LEN, NULL, NULL) != ETHER_ADDR_LEN)        if (!*ip || parse_hex(buff, hwaddr, ETHER_ADDR_LEN, NULL, NULL) != ETHER_ADDR_LEN)
         {          {
Line 830  void dhcp_read_ethers(void) Line 938  void dhcp_read_ethers(void)
               
       if (!*cp)        if (!*cp)
         {          {
          if ((addr.s_addr = inet_addr(ip)) == (in_addr_t)-1)          if (inet_pton(AF_INET, ip, &addr.s_addr) < 1)
             {              {
               my_syslog(MS_DHCP | LOG_ERR, _("bad address at %s line %d"), ETHERSFILE, lineno);                 my_syslog(MS_DHCP | LOG_ERR, _("bad address at %s line %d"), ETHERSFILE, lineno); 
               continue;                continue;
Line 939  char *host_from_dns(struct in_addr addr) Line 1047  char *host_from_dns(struct in_addr addr)
   if (daemon->port == 0)    if (daemon->port == 0)
     return NULL; /* DNS disabled. */      return NULL; /* DNS disabled. */
       
  lookup = cache_find_by_addr(NULL, (struct all_addr *)&addr, 0, F_IPV4);  lookup = cache_find_by_addr(NULL, (union all_addr *)&addr, 0, F_IPV4);
   
   if (lookup && (lookup->flags & F_HOSTS))    if (lookup && (lookup->flags & F_HOSTS))
     {      {
Line 956  char *host_from_dns(struct in_addr addr) Line 1064  char *host_from_dns(struct in_addr addr)
       if (!legal_hostname(hostname))        if (!legal_hostname(hostname))
         return NULL;          return NULL;
               
      strncpy(daemon->dhcp_buff, hostname, 256);      safe_strncpy(daemon->dhcp_buff, hostname, 256);
      daemon->dhcp_buff[255] = 0; 
       strip_hostname(daemon->dhcp_buff);        strip_hostname(daemon->dhcp_buff);
   
       return daemon->dhcp_buff;        return daemon->dhcp_buff;
Line 966  char *host_from_dns(struct in_addr addr) Line 1073  char *host_from_dns(struct in_addr addr)
   return NULL;    return NULL;
 }  }
   
static int  relay_upstream4(struct dhcp_relay *relay, struct dhcp_packet *mess, size_t sz, int iface_index)static int relay_upstream4(int iface_index, struct dhcp_packet *mess, size_t sz)
 {  {
  /* ->local is same value for all relays on ->current chain */  struct in_addr giaddr = mess->giaddr;
  struct all_addr from;  u8 hops = mess->hops;
    struct dhcp_relay *relay;
 
   if (mess->op != BOOTREQUEST)    if (mess->op != BOOTREQUEST)
     return 0;      return 0;
   
  /* source address == relay address */  for (relay = daemon->relay4; relay; relay = relay->next)
  from.addr.addr4 = relay->local.addr.addr4;    if (relay->iface_index != 0 && relay->iface_index == iface_index)
       break;
 
   /* No relay config. */
   if (!relay)
     return 0;
       
  /* already gatewayed ? */  for (; relay; relay = relay->next)
  if (mess->giaddr.s_addr)    if (relay->iface_index != 0 && relay->iface_index == iface_index)
    {      {
      /* if so check if by us, to stomp on loops. */        union mysockaddr to;
      if (mess->giaddr.s_addr == relay->local.addr.addr4.s_addr)        union all_addr from;
        return 1; 
    } 
  else 
    { 
      /* plug in our address */ 
      mess->giaddr.s_addr = relay->local.addr.addr4.s_addr; 
    } 
   
  if ((mess->hops++) > 20)        mess->hops = hops;
    return 1;        mess->giaddr = giaddr;
         
         if ((mess->hops++) > 20)
           continue;
         
         /* source address == relay address */
         from.addr4 = relay->local.addr4;
   
  for (; relay; relay = relay->current)        /* already gatewayed ? */
    {        if (giaddr.s_addr)
      union mysockaddr to;          {
                  /* if so check if by us, to stomp on loops. */
      to.sa.sa_family = AF_INET;            if (giaddr.s_addr == relay->local.addr4.s_addr)
      to.in.sin_addr = relay->server.addr.addr4;              continue;
      to.in.sin_port = htons(daemon->dhcp_server_port);          }
              else
      send_from(daemon->dhcpfd, 0, (char *)mess, sz, &to, &from, 0);          {
                  /* plug in our address */
      if (option_bool(OPT_LOG_OPTS))            mess->giaddr.s_addr = relay->local.addr4.s_addr;
           }
         
         to.sa.sa_family = AF_INET;
         to.in.sin_addr = relay->server.addr4;
         to.in.sin_port = htons(relay->port);
         
         /* Broadcasting to server. */
         if (relay->server.addr4.s_addr == 0)
           {
             struct ifreq ifr;
             
             if (relay->interface)
               safe_strncpy(ifr.ifr_name, relay->interface, IF_NAMESIZE);
             
             if (!relay->interface || strchr(relay->interface, '*') ||
                 ioctl(daemon->dhcpfd, SIOCGIFBRDADDR, &ifr) == -1)
               {
                 my_syslog(MS_DHCP | LOG_ERR, _("Cannot broadcast DHCP relay via interface %s"), relay->interface);
                 continue;
               }
             
             to.in.sin_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
           }
         
 #ifdef HAVE_DUMPFILE
         {          {
          inet_ntop(AF_INET, &relay->local, daemon->addrbuff, ADDRSTRLEN);          union mysockaddr fromsock;
          my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay %s -> %s"), daemon->addrbuff, inet_ntoa(relay->server.addr.addr4));          fromsock.in.sin_port = htons(daemon->dhcp_server_port);
           fromsock.in.sin_addr = from.addr4;
           fromsock.sa.sa_family = AF_INET;
 
           dump_packet_udp(DUMP_DHCP, (void *)mess, sz, &fromsock, &to, -1);
         }          }
      #endif
      /* Save this for replies */        
      relay->iface_index = iface_index;         send_from(daemon->dhcpfd, 0, (char *)mess, sz, &to, &from, 0);
    }         
          if (option_bool(OPT_LOG_OPTS))
            {
              inet_ntop(AF_INET, &relay->local, daemon->addrbuff, ADDRSTRLEN);
              if (relay->server.addr4.s_addr == 0)
                snprintf(daemon->dhcp_buff2, DHCP_BUFF_SZ, _("broadcast via %s"), relay->interface);
              else
                inet_ntop(AF_INET, &relay->server.addr4, daemon->dhcp_buff2, DHCP_BUFF_SZ);
              my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay at %s -> %s"), daemon->addrbuff, daemon->dhcp_buff2);
            }
       }
       
   return 1;    return 1;
 }  }
Line 1026  static struct dhcp_relay *relay_reply4(struct dhcp_pac Line 1177  static struct dhcp_relay *relay_reply4(struct dhcp_pac
   
   for (relay = daemon->relay4; relay; relay = relay->next)    for (relay = daemon->relay4; relay; relay = relay->next)
     {      {
      if (mess->giaddr.s_addr == relay->local.addr.addr4.s_addr)      if (mess->giaddr.s_addr == relay->local.addr4.s_addr)
         {          {
           if (!relay->interface || wildcard_match(relay->interface, arrival_interface))            if (!relay->interface || wildcard_match(relay->interface, arrival_interface))
             return relay->iface_index != 0 ? relay : NULL;              return relay->iface_index != 0 ? relay : NULL;

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


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