| 
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; |