Annotation of embedaddon/dnsmasq/src/dhcp.c, revision 1.1

1.1     ! misho       1: /* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
        !             2: 
        !             3:    This program is free software; you can redistribute it and/or modify
        !             4:    it under the terms of the GNU General Public License as published by
        !             5:    the Free Software Foundation; version 2 dated June, 1991, or
        !             6:    (at your option) version 3 dated 29 June, 2007.
        !             7:  
        !             8:    This program is distributed in the hope that it will be useful,
        !             9:    but WITHOUT ANY WARRANTY; without even the implied warranty of
        !            10:    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
        !            11:    GNU General Public License for more details.
        !            12:      
        !            13:    You should have received a copy of the GNU General Public License
        !            14:    along with this program.  If not, see <http://www.gnu.org/licenses/>.
        !            15: */
        !            16: 
        !            17: #include "dnsmasq.h"
        !            18: 
        !            19: #ifdef HAVE_DHCP
        !            20: 
        !            21: struct iface_param {
        !            22:   struct dhcp_context *current;
        !            23:   int ind;
        !            24: };
        !            25: 
        !            26: struct match_param {
        !            27:   int ind, matched;
        !            28:   struct in_addr netmask, broadcast, addr;
        !            29: };
        !            30: 
        !            31: static int complete_context(struct in_addr local, int if_index, 
        !            32:                            struct in_addr netmask, struct in_addr broadcast, void *vparam);
        !            33: static int check_listen_addrs(struct in_addr local, int if_index, 
        !            34:                              struct in_addr netmask, struct in_addr broadcast, void *vparam);
        !            35: 
        !            36: static int make_fd(int port)
        !            37: {
        !            38:   int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
        !            39:   struct sockaddr_in saddr;
        !            40:   int oneopt = 1;
        !            41: #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
        !            42:   int mtu = IP_PMTUDISC_DONT;
        !            43: #endif
        !            44: #if defined(IP_TOS) && defined(IPTOS_CLASS_CS6)
        !            45:   int tos = IPTOS_CLASS_CS6;
        !            46: #endif
        !            47: 
        !            48:   if (fd == -1)
        !            49:     die (_("cannot create DHCP socket: %s"), NULL, EC_BADNET);
        !            50:   
        !            51:   if (!fix_fd(fd) ||
        !            52: #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
        !            53:       setsockopt(fd, IPPROTO_IP, IP_MTU_DISCOVER, &mtu, sizeof(mtu)) == -1 ||
        !            54: #endif
        !            55: #if defined(IP_TOS) && defined(IPTOS_CLASS_CS6)
        !            56:       setsockopt(fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) == -1 ||
        !            57: #endif
        !            58: #if defined(HAVE_LINUX_NETWORK)
        !            59:       setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &oneopt, sizeof(oneopt)) == -1 ||
        !            60: #else
        !            61:       setsockopt(fd, IPPROTO_IP, IP_RECVIF, &oneopt, sizeof(oneopt)) == -1 ||
        !            62: #endif
        !            63:       setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &oneopt, sizeof(oneopt)) == -1)  
        !            64:     die(_("failed to set options on DHCP socket: %s"), NULL, EC_BADNET);
        !            65:   
        !            66:   /* When bind-interfaces is set, there might be more than one dnmsasq
        !            67:      instance binding port 67. That's OK if they serve different networks.
        !            68:      Need to set REUSEADDR|REUSEPORT to make this posible.
        !            69:      Handle the case that REUSEPORT is defined, but the kernel doesn't 
        !            70:      support it. This handles the introduction of REUSEPORT on Linux. */
        !            71:   if (option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND))
        !            72:     {
        !            73:       int rc = -1, porterr = 0;
        !            74: 
        !            75: #ifdef SO_REUSEPORT
        !            76:       if ((rc = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &oneopt, sizeof(oneopt))) == -1 && 
        !            77:          errno != ENOPROTOOPT)
        !            78:        porterr = 1;
        !            79: #endif
        !            80:       
        !            81:       if (rc == -1 && !porterr)
        !            82:        rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &oneopt, sizeof(oneopt));
        !            83:       
        !            84:       if (rc == -1)
        !            85:        die(_("failed to set SO_REUSE{ADDR|PORT} on DHCP socket: %s"), NULL, EC_BADNET);
        !            86:     }
        !            87:   
        !            88:   memset(&saddr, 0, sizeof(saddr));
        !            89:   saddr.sin_family = AF_INET;
        !            90:   saddr.sin_port = htons(port);
        !            91:   saddr.sin_addr.s_addr = INADDR_ANY;
        !            92: #ifdef HAVE_SOCKADDR_SA_LEN
        !            93:   saddr.sin_len = sizeof(struct sockaddr_in);
        !            94: #endif
        !            95: 
        !            96:   if (bind(fd, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in)))
        !            97:     die(_("failed to bind DHCP server socket: %s"), NULL, EC_BADNET);
        !            98: 
        !            99:   return fd;
        !           100: }
        !           101: 
        !           102: void dhcp_init(void)
        !           103: {
        !           104: #if defined(HAVE_BSD_NETWORK)
        !           105:   int oneopt = 1;
        !           106: #endif
        !           107: 
        !           108:   daemon->dhcpfd = make_fd(daemon->dhcp_server_port);
        !           109:   if (daemon->enable_pxe)
        !           110:     daemon->pxefd = make_fd(PXE_PORT);
        !           111:   else
        !           112:     daemon->pxefd = -1;
        !           113: 
        !           114: #if defined(HAVE_BSD_NETWORK)
        !           115:   /* When we're not using capabilities, we need to do this here before
        !           116:      we drop root. Also, set buffer size small, to avoid wasting
        !           117:      kernel buffers */
        !           118:   
        !           119:   if (option_bool(OPT_NO_PING))
        !           120:     daemon->dhcp_icmp_fd = -1;
        !           121:   else if ((daemon->dhcp_icmp_fd = make_icmp_sock()) == -1 ||
        !           122:           setsockopt(daemon->dhcp_icmp_fd, SOL_SOCKET, SO_RCVBUF, &oneopt, sizeof(oneopt)) == -1 )
        !           123:     die(_("cannot create ICMP raw socket: %s."), NULL, EC_BADNET);
        !           124:   
        !           125:   /* Make BPF raw send socket */
        !           126:   init_bpf();
        !           127: #endif  
        !           128: }
        !           129: 
        !           130: void dhcp_packet(time_t now, int pxe_fd)
        !           131: {
        !           132:   int fd = pxe_fd ? daemon->pxefd : daemon->dhcpfd;
        !           133:   struct dhcp_packet *mess;
        !           134:   struct dhcp_context *context;
        !           135:   struct iname *tmp;
        !           136:   struct ifreq ifr;
        !           137:   struct msghdr msg;
        !           138:   struct sockaddr_in dest;
        !           139:   struct cmsghdr *cmptr;
        !           140:   struct iovec iov;
        !           141:   ssize_t sz; 
        !           142:   int iface_index = 0, unicast_dest = 0, is_inform = 0;
        !           143:   struct in_addr iface_addr;
        !           144:   struct iface_param parm;
        !           145: #ifdef HAVE_LINUX_NETWORK
        !           146:   struct arpreq arp_req;
        !           147: #endif
        !           148:   
        !           149:   union {
        !           150:     struct cmsghdr align; /* this ensures alignment */
        !           151: #if defined(HAVE_LINUX_NETWORK)
        !           152:     char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
        !           153: #elif defined(HAVE_SOLARIS_NETWORK)
        !           154:     char control[CMSG_SPACE(sizeof(unsigned int))];
        !           155: #elif defined(HAVE_BSD_NETWORK) 
        !           156:     char control[CMSG_SPACE(sizeof(struct sockaddr_dl))];
        !           157: #endif
        !           158:   } control_u;
        !           159:   struct dhcp_bridge *bridge, *alias;
        !           160: 
        !           161:   msg.msg_controllen = sizeof(control_u);
        !           162:   msg.msg_control = control_u.control;
        !           163:   msg.msg_name = &dest;
        !           164:   msg.msg_namelen = sizeof(dest);
        !           165:   msg.msg_iov = &daemon->dhcp_packet;
        !           166:   msg.msg_iovlen = 1;
        !           167:   
        !           168:   if ((sz = recv_dhcp_packet(fd, &msg)) == -1 || 
        !           169:       (sz < (ssize_t)(sizeof(*mess) - sizeof(mess->options)))) 
        !           170:     return;
        !           171:     
        !           172:   #if defined (HAVE_LINUX_NETWORK)
        !           173:   if (msg.msg_controllen >= sizeof(struct cmsghdr))
        !           174:     for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
        !           175:       if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_PKTINFO)
        !           176:        {
        !           177:          union {
        !           178:            unsigned char *c;
        !           179:            struct in_pktinfo *p;
        !           180:          } p;
        !           181:          p.c = CMSG_DATA(cmptr);
        !           182:          iface_index = p.p->ipi_ifindex;
        !           183:          if (p.p->ipi_addr.s_addr != INADDR_BROADCAST)
        !           184:            unicast_dest = 1;
        !           185:        }
        !           186: 
        !           187: #elif defined(HAVE_BSD_NETWORK) 
        !           188:   if (msg.msg_controllen >= sizeof(struct cmsghdr))
        !           189:     for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
        !           190:       if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
        !           191:         {
        !           192:          union {
        !           193:             unsigned char *c;
        !           194:             struct sockaddr_dl *s;
        !           195:           } p;
        !           196:          p.c = CMSG_DATA(cmptr);
        !           197:          iface_index = p.s->sdl_index;
        !           198:        }
        !           199:   
        !           200: #elif defined(HAVE_SOLARIS_NETWORK) 
        !           201:   if (msg.msg_controllen >= sizeof(struct cmsghdr))
        !           202:     for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
        !           203:       if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
        !           204:        {
        !           205:          union {
        !           206:            unsigned char *c;
        !           207:            unsigned int *i;
        !           208:          } p;
        !           209:          p.c = CMSG_DATA(cmptr);
        !           210:          iface_index = *(p.i);
        !           211:        }
        !           212: #endif
        !           213:        
        !           214:   if (!indextoname(daemon->dhcpfd, iface_index, ifr.ifr_name))
        !           215:     return;
        !           216: 
        !           217: #ifdef HAVE_LINUX_NETWORK
        !           218:   /* ARP fiddling uses original interface even if we pretend to use a different one. */
        !           219:   strncpy(arp_req.arp_dev, ifr.ifr_name, 16);
        !           220: #endif 
        !           221: 
        !           222:    /* One form of bridging on BSD has the property that packets
        !           223:       can be recieved on bridge interfaces which do not have an IP address.
        !           224:       We allow these to be treated as aliases of another interface which does have
        !           225:       an IP address with --dhcp-bridge=interface,alias,alias */
        !           226:   for (bridge = daemon->bridges; bridge; bridge = bridge->next)
        !           227:     {
        !           228:       for (alias = bridge->alias; alias; alias = alias->next)
        !           229:        if (strncmp(ifr.ifr_name, alias->iface, IF_NAMESIZE) == 0)
        !           230:          {
        !           231:            if (!(iface_index = if_nametoindex(bridge->iface)))
        !           232:              {
        !           233:                my_syslog(LOG_WARNING, _("unknown interface %s in bridge-interface"), ifr.ifr_name);
        !           234:                return;
        !           235:              }
        !           236:            else 
        !           237:              {
        !           238:                strncpy(ifr.ifr_name,  bridge->iface, IF_NAMESIZE);
        !           239:                break;
        !           240:              }
        !           241:          }
        !           242:       
        !           243:       if (alias)
        !           244:        break;
        !           245:     }
        !           246: 
        !           247: #ifdef MSG_BCAST
        !           248:   /* OpenBSD tells us when a packet was broadcast */
        !           249:   if (!(msg.msg_flags & MSG_BCAST))
        !           250:     unicast_dest = 1;
        !           251: #endif
        !           252:   
        !           253:   ifr.ifr_addr.sa_family = AF_INET;
        !           254:   if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) != -1 )
        !           255:     iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
        !           256:   else
        !           257:     {
        !           258:       my_syslog(MS_DHCP | LOG_WARNING, _("DHCP packet received on %s which has no address"), ifr.ifr_name);
        !           259:       return;
        !           260:     }
        !           261:   
        !           262:   for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
        !           263:     if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
        !           264:       return;
        !           265:   
        !           266:   /* unlinked contexts are marked by context->current == context */
        !           267:   for (context = daemon->dhcp; context; context = context->next)
        !           268:     context->current = context;
        !           269:   
        !           270:   parm.current = NULL;
        !           271:   parm.ind = iface_index;
        !           272: 
        !           273:   if (!iface_check(AF_INET, (struct all_addr *)&iface_addr, ifr.ifr_name, NULL))
        !           274:     {
        !           275:       /* If we failed to match the primary address of the interface, see if we've got a --listen-address
        !           276:         for a secondary */
        !           277:       struct match_param match;
        !           278:       
        !           279:       match.matched = 0;
        !           280:       match.ind = iface_index;
        !           281:       
        !           282:       if (!daemon->if_addrs ||
        !           283:          !iface_enumerate(AF_INET, &match, check_listen_addrs) ||
        !           284:          !match.matched)
        !           285:        return;
        !           286: 
        !           287:       iface_addr = match.addr;
        !           288:       /* make sure secondary address gets priority in case
        !           289:         there is more than one address on the interface in the same subnet */
        !           290:       complete_context(match.addr, iface_index, match.netmask, match.broadcast, &parm);
        !           291:     }    
        !           292:       
        !           293:   if (!iface_enumerate(AF_INET, &parm, complete_context))
        !           294:     return;
        !           295:   
        !           296:   lease_prune(NULL, now); /* lose any expired leases */
        !           297:   iov.iov_len = dhcp_reply(parm.current, ifr.ifr_name, iface_index, (size_t)sz, 
        !           298:                           now, unicast_dest, &is_inform, pxe_fd, iface_addr);
        !           299:   lease_update_file(now);
        !           300:   lease_update_dns(0);
        !           301:     
        !           302:   if (iov.iov_len == 0)
        !           303:     return;
        !           304:   
        !           305:   msg.msg_name = &dest;
        !           306:   msg.msg_namelen = sizeof(dest);
        !           307:   msg.msg_control = NULL;
        !           308:   msg.msg_controllen = 0;
        !           309:   msg.msg_iov = &iov;
        !           310:   iov.iov_base = daemon->dhcp_packet.iov_base;
        !           311:   
        !           312:   /* packet buffer may have moved */
        !           313:   mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
        !           314:   
        !           315: #ifdef HAVE_SOCKADDR_SA_LEN
        !           316:   dest.sin_len = sizeof(struct sockaddr_in);
        !           317: #endif
        !           318:   
        !           319:   if (pxe_fd)
        !           320:     { 
        !           321:       if (mess->ciaddr.s_addr != 0)
        !           322:        dest.sin_addr = mess->ciaddr;
        !           323:     }
        !           324:   else if (mess->giaddr.s_addr)
        !           325:     {
        !           326:       /* Send to BOOTP relay  */
        !           327:       dest.sin_port = htons(daemon->dhcp_server_port);
        !           328:       dest.sin_addr = mess->giaddr; 
        !           329:     }
        !           330:   else if (mess->ciaddr.s_addr)
        !           331:     {
        !           332:       /* If the client's idea of its own address tallys with
        !           333:         the source address in the request packet, we believe the
        !           334:         source port too, and send back to that.  If we're replying 
        !           335:         to a DHCPINFORM, trust the source address always. */
        !           336:       if ((!is_inform && dest.sin_addr.s_addr != mess->ciaddr.s_addr) ||
        !           337:          dest.sin_port == 0 || dest.sin_addr.s_addr == 0)
        !           338:        {
        !           339:          dest.sin_port = htons(daemon->dhcp_client_port); 
        !           340:          dest.sin_addr = mess->ciaddr;
        !           341:        }
        !           342:     } 
        !           343: #if defined(HAVE_LINUX_NETWORK)
        !           344:   else if ((ntohs(mess->flags) & 0x8000) || mess->hlen == 0 ||
        !           345:           mess->hlen > sizeof(ifr.ifr_addr.sa_data) || mess->htype == 0)
        !           346:     {
        !           347:       /* broadcast to 255.255.255.255 (or mac address invalid) */
        !           348:       struct in_pktinfo *pkt;
        !           349:       msg.msg_control = control_u.control;
        !           350:       msg.msg_controllen = sizeof(control_u);
        !           351:       cmptr = CMSG_FIRSTHDR(&msg);
        !           352:       pkt = (struct in_pktinfo *)CMSG_DATA(cmptr);
        !           353:       pkt->ipi_ifindex = iface_index;
        !           354:       pkt->ipi_spec_dst.s_addr = 0;
        !           355:       msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
        !           356:       cmptr->cmsg_level = IPPROTO_IP;
        !           357:       cmptr->cmsg_type = IP_PKTINFO;  
        !           358:       dest.sin_addr.s_addr = INADDR_BROADCAST;
        !           359:       dest.sin_port = htons(daemon->dhcp_client_port);
        !           360:     }
        !           361:   else
        !           362:     {
        !           363:       /* unicast to unconfigured client. Inject mac address direct into ARP cache. 
        !           364:         struct sockaddr limits size to 14 bytes. */
        !           365:       dest.sin_addr = mess->yiaddr;
        !           366:       dest.sin_port = htons(daemon->dhcp_client_port);
        !           367:       memcpy(&arp_req.arp_pa, &dest, sizeof(struct sockaddr_in));
        !           368:       arp_req.arp_ha.sa_family = mess->htype;
        !           369:       memcpy(arp_req.arp_ha.sa_data, mess->chaddr, mess->hlen);
        !           370:       /* interface name already copied in */
        !           371:       arp_req.arp_flags = ATF_COM;
        !           372:       ioctl(daemon->dhcpfd, SIOCSARP, &arp_req);
        !           373:     }
        !           374: #elif defined(HAVE_SOLARIS_NETWORK)
        !           375:   else if ((ntohs(mess->flags) & 0x8000) || mess->hlen != ETHER_ADDR_LEN || mess->htype != ARPHRD_ETHER)
        !           376:     {
        !           377:       /* broadcast to 255.255.255.255 (or mac address invalid) */
        !           378:       dest.sin_addr.s_addr = INADDR_BROADCAST;
        !           379:       dest.sin_port = htons(daemon->dhcp_client_port);
        !           380:       /* note that we don't specify the interface here: that's done by the
        !           381:         IP_BOUND_IF sockopt lower down. */
        !           382:     }
        !           383:   else
        !           384:     {
        !           385:       /* unicast to unconfigured client. Inject mac address direct into ARP cache. 
        !           386:         Note that this only works for ethernet on solaris, because we use SIOCSARP
        !           387:         and not SIOCSXARP, which would be perfect, except that it returns ENXIO 
        !           388:         mysteriously. Bah. Fall back to broadcast for other net types. */
        !           389:       struct arpreq req;
        !           390:       dest.sin_addr = mess->yiaddr;
        !           391:       dest.sin_port = htons(daemon->dhcp_client_port);
        !           392:       *((struct sockaddr_in *)&req.arp_pa) = dest;
        !           393:       req.arp_ha.sa_family = AF_UNSPEC;
        !           394:       memcpy(req.arp_ha.sa_data, mess->chaddr, mess->hlen);
        !           395:       req.arp_flags = ATF_COM;
        !           396:       ioctl(daemon->dhcpfd, SIOCSARP, &req);
        !           397:     }
        !           398: #elif defined(HAVE_BSD_NETWORK)
        !           399:   else 
        !           400:     {
        !           401:       send_via_bpf(mess, iov.iov_len, iface_addr, &ifr);
        !           402:       return;
        !           403:     }
        !           404: #endif
        !           405:    
        !           406: #ifdef HAVE_SOLARIS_NETWORK
        !           407:   setsockopt(fd, IPPROTO_IP, IP_BOUND_IF, &iface_index, sizeof(iface_index));
        !           408: #endif
        !           409:   
        !           410:   while(sendmsg(fd, &msg, 0) == -1 && retry_send());
        !           411: }
        !           412:  
        !           413: /* check against secondary interface addresses */
        !           414: static int check_listen_addrs(struct in_addr local, int if_index, 
        !           415:                              struct in_addr netmask, struct in_addr broadcast, void *vparam)
        !           416: {
        !           417:   struct match_param *param = vparam;
        !           418:   struct iname *tmp;
        !           419: 
        !           420:   if (if_index == param->ind)
        !           421:     {
        !           422:       for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
        !           423:        if ( tmp->addr.sa.sa_family == AF_INET &&
        !           424:             tmp->addr.in.sin_addr.s_addr == local.s_addr)
        !           425:          {
        !           426:            param->matched = 1;
        !           427:            param->addr = local;
        !           428:            param->netmask = netmask;
        !           429:            param->broadcast = broadcast;
        !           430:            break;
        !           431:          }
        !           432:     }
        !           433:   
        !           434:   return 1;
        !           435: }
        !           436: 
        !           437: /* This is a complex routine: it gets called with each (address,netmask,broadcast) triple 
        !           438:    of each interface (and any relay address) and does the  following things:
        !           439: 
        !           440:    1) Discards stuff for interfaces other than the one on which a DHCP packet just arrived.
        !           441:    2) Fills in any netmask and broadcast addresses which have not been explicitly configured.
        !           442:    3) Fills in local (this host) and router (this host or relay) addresses.
        !           443:    4) Links contexts which are valid for hosts directly connected to the arrival interface on ->current.
        !           444: 
        !           445:    Note that the current chain may be superceded later for configured hosts or those coming via gateways. */
        !           446: 
        !           447: static int complete_context(struct in_addr local, int if_index, 
        !           448:                            struct in_addr netmask, struct in_addr broadcast, void *vparam)
        !           449: {
        !           450:   struct dhcp_context *context;
        !           451:   struct iface_param *param = vparam;
        !           452:   
        !           453:   for (context = daemon->dhcp; context; context = context->next)
        !           454:     {
        !           455:       if (!(context->flags & CONTEXT_NETMASK) &&
        !           456:          (is_same_net(local, context->start, netmask) ||
        !           457:           is_same_net(local, context->end, netmask)))
        !           458:       { 
        !           459:        if (context->netmask.s_addr != netmask.s_addr &&
        !           460:            !(is_same_net(local, context->start, netmask) &&
        !           461:              is_same_net(local, context->end, netmask)))
        !           462:          {
        !           463:            strcpy(daemon->dhcp_buff, inet_ntoa(context->start));
        !           464:            strcpy(daemon->dhcp_buff2, inet_ntoa(context->end));
        !           465:            my_syslog(MS_DHCP | LOG_WARNING, _("DHCP range %s -- %s is not consistent with netmask %s"),
        !           466:                      daemon->dhcp_buff, daemon->dhcp_buff2, inet_ntoa(netmask));
        !           467:          }     
        !           468:        context->netmask = netmask;
        !           469:       }
        !           470:       
        !           471:       if (context->netmask.s_addr != 0 &&
        !           472:          is_same_net(local, context->start, context->netmask) &&
        !           473:          is_same_net(local, context->end, context->netmask))
        !           474:        {
        !           475:          /* link it onto the current chain if we've not seen it before */
        !           476:          if (if_index == param->ind && context->current == context)
        !           477:            {
        !           478:              context->router = local;
        !           479:              context->local = local;
        !           480:              context->current = param->current;
        !           481:              param->current = context;
        !           482:            }
        !           483:          
        !           484:          if (!(context->flags & CONTEXT_BRDCAST))
        !           485:            {
        !           486:              if (is_same_net(broadcast, context->start, context->netmask))
        !           487:                context->broadcast = broadcast;
        !           488:              else 
        !           489:                context->broadcast.s_addr  = context->start.s_addr | ~context->netmask.s_addr;
        !           490:            }
        !           491:        }               
        !           492:     }
        !           493: 
        !           494:   return 1;
        !           495: }
        !           496:          
        !           497: struct dhcp_context *address_available(struct dhcp_context *context, 
        !           498:                                       struct in_addr taddr,
        !           499:                                       struct dhcp_netid *netids)
        !           500: {
        !           501:   /* Check is an address is OK for this network, check all
        !           502:      possible ranges. Make sure that the address isn't in use
        !           503:      by the server itself. */
        !           504:   
        !           505:   unsigned int start, end, addr = ntohl(taddr.s_addr);
        !           506:   struct dhcp_context *tmp;
        !           507: 
        !           508:   for (tmp = context; tmp; tmp = tmp->current)
        !           509:     if (taddr.s_addr == context->router.s_addr)
        !           510:       return NULL;
        !           511:   
        !           512:   for (tmp = context; tmp; tmp = tmp->current)
        !           513:     {
        !           514:       start = ntohl(tmp->start.s_addr);
        !           515:       end = ntohl(tmp->end.s_addr);
        !           516: 
        !           517:       if (!(tmp->flags & (CONTEXT_STATIC | CONTEXT_PROXY)) &&
        !           518:          addr >= start &&
        !           519:          addr <= end &&
        !           520:          match_netid(tmp->filter, netids, 1))
        !           521:        return tmp;
        !           522:     }
        !           523: 
        !           524:   return NULL;
        !           525: }
        !           526: 
        !           527: struct dhcp_context *narrow_context(struct dhcp_context *context, 
        !           528:                                    struct in_addr taddr,
        !           529:                                    struct dhcp_netid *netids)
        !           530: {
        !           531:   /* We start of with a set of possible contexts, all on the current physical interface.
        !           532:      These are chained on ->current.
        !           533:      Here we have an address, and return the actual context correponding to that
        !           534:      address. Note that none may fit, if the address came a dhcp-host and is outside
        !           535:      any dhcp-range. In that case we return a static range if possible, or failing that,
        !           536:      any context on the correct subnet. (If there's more than one, this is a dodgy 
        !           537:      configuration: maybe there should be a warning.) */
        !           538:   
        !           539:   struct dhcp_context *tmp;
        !           540: 
        !           541:   if (!(tmp = address_available(context, taddr, netids)))
        !           542:     {
        !           543:       for (tmp = context; tmp; tmp = tmp->current)
        !           544:        if (match_netid(tmp->filter, netids, 1) &&
        !           545:            is_same_net(taddr, tmp->start, tmp->netmask) && 
        !           546:            (tmp->flags & CONTEXT_STATIC))
        !           547:          break;
        !           548:       
        !           549:       if (!tmp)
        !           550:        for (tmp = context; tmp; tmp = tmp->current)
        !           551:          if (match_netid(tmp->filter, netids, 1) &&
        !           552:              is_same_net(taddr, tmp->start, tmp->netmask) &&
        !           553:              !(tmp->flags & CONTEXT_PROXY))
        !           554:            break;
        !           555:     }
        !           556:   
        !           557:   /* Only one context allowed now */
        !           558:   if (tmp)
        !           559:     tmp->current = NULL;
        !           560:   
        !           561:   return tmp;
        !           562: }
        !           563: 
        !           564: struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct in_addr addr)
        !           565: {
        !           566:   struct dhcp_config *config;
        !           567:   
        !           568:   for (config = configs; config; config = config->next)
        !           569:     if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr)
        !           570:       return config;
        !           571: 
        !           572:   return NULL;
        !           573: }
        !           574: 
        !           575: int address_allocate(struct dhcp_context *context,
        !           576:                     struct in_addr *addrp, unsigned char *hwaddr, int hw_len, 
        !           577:                     struct dhcp_netid *netids, time_t now)   
        !           578: {
        !           579:   /* Find a free address: exclude anything in use and anything allocated to
        !           580:      a particular hwaddr/clientid/hostname in our configuration.
        !           581:      Try to return from contexts which match netids first. */
        !           582: 
        !           583:   struct in_addr start, addr;
        !           584:   struct dhcp_context *c, *d;
        !           585:   int i, pass;
        !           586:   unsigned int j; 
        !           587: 
        !           588:   /* hash hwaddr: use the SDBM hashing algorithm.  Seems to give good
        !           589:      dispersal even with similarly-valued "strings". */ 
        !           590:   for (j = 0, i = 0; i < hw_len; i++)
        !           591:     j += hwaddr[i] + (j << 6) + (j << 16) - j;
        !           592:   
        !           593:   for (pass = 0; pass <= 1; pass++)
        !           594:     for (c = context; c; c = c->current)
        !           595:       if (c->flags & (CONTEXT_STATIC | CONTEXT_PROXY))
        !           596:        continue;
        !           597:       else if (!match_netid(c->filter, netids, pass))
        !           598:        continue;
        !           599:       else
        !           600:        {
        !           601:          if (option_bool(OPT_CONSEC_ADDR))
        !           602:            /* seed is largest extant lease addr in this context */
        !           603:            start = lease_find_max_addr(c);
        !           604:          else
        !           605:            /* pick a seed based on hwaddr */
        !           606:            start.s_addr = htonl(ntohl(c->start.s_addr) + 
        !           607:                                 ((j + c->addr_epoch) % (1 + ntohl(c->end.s_addr) - ntohl(c->start.s_addr))));
        !           608: 
        !           609:          /* iterate until we find a free address. */
        !           610:          addr = start;
        !           611:          
        !           612:          do {
        !           613:            /* eliminate addresses in use by the server. */
        !           614:            for (d = context; d; d = d->current)
        !           615:              if (addr.s_addr == d->router.s_addr)
        !           616:                break;
        !           617: 
        !           618:            /* Addresses which end in .255 and .0 are broken in Windows even when using 
        !           619:               supernetting. ie dhcp-range=192.168.0.1,192.168.1.254,255,255,254.0
        !           620:               then 192.168.0.255 is a valid IP address, but not for Windows as it's
        !           621:               in the class C range. See  KB281579. We therefore don't allocate these 
        !           622:               addresses to avoid hard-to-diagnose problems. Thanks Bill. */        
        !           623:            if (!d &&
        !           624:                !lease_find_by_addr(addr) && 
        !           625:                !config_find_by_address(daemon->dhcp_conf, addr) &&
        !           626:                (!IN_CLASSC(ntohl(addr.s_addr)) || 
        !           627:                 ((ntohl(addr.s_addr) & 0xff) != 0xff && ((ntohl(addr.s_addr) & 0xff) != 0x0))))
        !           628:              {
        !           629:                struct ping_result *r, *victim = NULL;
        !           630:                int count, max = (int)(0.6 * (((float)PING_CACHE_TIME)/
        !           631:                                              ((float)PING_WAIT)));
        !           632:                
        !           633:                *addrp = addr;
        !           634: 
        !           635:                /* check if we failed to ping addr sometime in the last
        !           636:                   PING_CACHE_TIME seconds. If so, assume the same situation still exists.
        !           637:                   This avoids problems when a stupid client bangs
        !           638:                   on us repeatedly. As a final check, if we did more
        !           639:                   than 60% of the possible ping checks in the last 
        !           640:                   PING_CACHE_TIME, we are in high-load mode, so don't do any more. */
        !           641:                for (count = 0, r = daemon->ping_results; r; r = r->next)
        !           642:                  if (difftime(now, r->time) >  (float)PING_CACHE_TIME)
        !           643:                    victim = r; /* old record */
        !           644:                  else 
        !           645:                    {
        !           646:                      count++;
        !           647:                      if (r->addr.s_addr == addr.s_addr)
        !           648:                        {
        !           649:                          /* consec-ip mode: we offered this address for another client
        !           650:                             (different hash) recently, don't offer it to this one. */
        !           651:                          if (option_bool(OPT_CONSEC_ADDR) && r->hash != j)
        !           652:                            break;
        !           653:                          
        !           654:                          return 1;
        !           655:                        }
        !           656:                    }
        !           657: 
        !           658:                if (!r) 
        !           659:                  {
        !           660:                    if ((count < max) && !option_bool(OPT_NO_PING) && icmp_ping(addr))
        !           661:                      {
        !           662:                        /* address in use: perturb address selection so that we are
        !           663:                           less likely to try this address again. */
        !           664:                        if (!option_bool(OPT_CONSEC_ADDR))
        !           665:                          c->addr_epoch++;
        !           666:                      }
        !           667:                    else
        !           668:                      {
        !           669:                        /* at this point victim may hold an expired record */
        !           670:                        if (!victim)
        !           671:                          {
        !           672:                            if ((victim = whine_malloc(sizeof(struct ping_result))))
        !           673:                              {
        !           674:                                victim->next = daemon->ping_results;
        !           675:                                daemon->ping_results = victim;
        !           676:                              }
        !           677:                          }
        !           678:                        
        !           679:                        /* record that this address is OK for 30s 
        !           680:                           without more ping checks */
        !           681:                        if (victim)
        !           682:                          {
        !           683:                            victim->addr = addr;
        !           684:                            victim->time = now;
        !           685:                            victim->hash = j;
        !           686:                          }
        !           687:                        return 1;
        !           688:                      }
        !           689:                  }
        !           690:              }
        !           691: 
        !           692:            addr.s_addr = htonl(ntohl(addr.s_addr) + 1);
        !           693:            
        !           694:            if (addr.s_addr == htonl(ntohl(c->end.s_addr) + 1))
        !           695:              addr = c->start;
        !           696:            
        !           697:          } while (addr.s_addr != start.s_addr);
        !           698:        }
        !           699: 
        !           700:   return 0;
        !           701: }
        !           702: 
        !           703: static int is_addr_in_context(struct dhcp_context *context, struct dhcp_config *config)
        !           704: {
        !           705:   if (!context) /* called via find_config() from lease_update_from_configs() */
        !           706:     return 1; 
        !           707:   if (!(config->flags & CONFIG_ADDR))
        !           708:     return 1;
        !           709:   for (; context; context = context->current)
        !           710:     if (is_same_net(config->addr, context->start, context->netmask))
        !           711:       return 1;
        !           712:   
        !           713:   return 0;
        !           714: }
        !           715: 
        !           716: int config_has_mac(struct dhcp_config *config, unsigned char *hwaddr, int len, int type)
        !           717: {
        !           718:   struct hwaddr_config *conf_addr;
        !           719:   
        !           720:   for (conf_addr = config->hwaddr; conf_addr; conf_addr = conf_addr->next)
        !           721:     if (conf_addr->wildcard_mask == 0 &&
        !           722:        conf_addr->hwaddr_len == len &&
        !           723:        (conf_addr->hwaddr_type == type || conf_addr->hwaddr_type == 0) &&
        !           724:        memcmp(conf_addr->hwaddr, hwaddr, len) == 0)
        !           725:       return 1;
        !           726:   
        !           727:   return 0;
        !           728: }
        !           729: 
        !           730: struct dhcp_config *find_config(struct dhcp_config *configs,
        !           731:                                struct dhcp_context *context,
        !           732:                                unsigned char *clid, int clid_len,
        !           733:                                unsigned char *hwaddr, int hw_len, 
        !           734:                                int hw_type, char *hostname)
        !           735: {
        !           736:   int count, new;
        !           737:   struct dhcp_config *config, *candidate; 
        !           738:   struct hwaddr_config *conf_addr;
        !           739: 
        !           740:   if (clid)
        !           741:     for (config = configs; config; config = config->next)
        !           742:       if (config->flags & CONFIG_CLID)
        !           743:        {
        !           744:          if (config->clid_len == clid_len && 
        !           745:              memcmp(config->clid, clid, clid_len) == 0 &&
        !           746:              is_addr_in_context(context, config))
        !           747:            return config;
        !           748:          
        !           749:          /* dhcpcd prefixes ASCII client IDs by zero which is wrong, but we try and
        !           750:             cope with that here */
        !           751:          if (*clid == 0 && config->clid_len == clid_len-1  &&
        !           752:              memcmp(config->clid, clid+1, clid_len-1) == 0 &&
        !           753:              is_addr_in_context(context, config))
        !           754:            return config;
        !           755:        }
        !           756:   
        !           757: 
        !           758:   for (config = configs; config; config = config->next)
        !           759:     if (config_has_mac(config, hwaddr, hw_len, hw_type) &&
        !           760:        is_addr_in_context(context, config))
        !           761:       return config;
        !           762:   
        !           763:   if (hostname && context)
        !           764:     for (config = configs; config; config = config->next)
        !           765:       if ((config->flags & CONFIG_NAME) && 
        !           766:          hostname_isequal(config->hostname, hostname) &&
        !           767:          is_addr_in_context(context, config))
        !           768:        return config;
        !           769: 
        !           770:   /* use match with fewest wildcard octets */
        !           771:   for (candidate = NULL, count = 0, config = configs; config; config = config->next)
        !           772:     if (is_addr_in_context(context, config))
        !           773:       for (conf_addr = config->hwaddr; conf_addr; conf_addr = conf_addr->next)
        !           774:        if (conf_addr->wildcard_mask != 0 &&
        !           775:            conf_addr->hwaddr_len == hw_len &&  
        !           776:            (conf_addr->hwaddr_type == hw_type || conf_addr->hwaddr_type == 0) &&
        !           777:            (new = memcmp_masked(conf_addr->hwaddr, hwaddr, hw_len, conf_addr->wildcard_mask)) > count)
        !           778:          {
        !           779:            count = new;
        !           780:            candidate = config;
        !           781:          }
        !           782: 
        !           783:   return candidate;
        !           784: }
        !           785: 
        !           786: void dhcp_read_ethers(void)
        !           787: {
        !           788:   FILE *f = fopen(ETHERSFILE, "r");
        !           789:   unsigned int flags;
        !           790:   char *buff = daemon->namebuff;
        !           791:   char *ip, *cp;
        !           792:   struct in_addr addr;
        !           793:   unsigned char hwaddr[ETHER_ADDR_LEN];
        !           794:   struct dhcp_config **up, *tmp;
        !           795:   struct dhcp_config *config;
        !           796:   int count = 0, lineno = 0;
        !           797: 
        !           798:   addr.s_addr = 0; /* eliminate warning */
        !           799:   
        !           800:   if (!f)
        !           801:     {
        !           802:       my_syslog(MS_DHCP | LOG_ERR, _("failed to read %s: %s"), ETHERSFILE, strerror(errno));
        !           803:       return;
        !           804:     }
        !           805: 
        !           806:   /* This can be called again on SIGHUP, so remove entries created last time round. */
        !           807:   for (up = &daemon->dhcp_conf, config = daemon->dhcp_conf; config; config = tmp)
        !           808:     {
        !           809:       tmp = config->next;
        !           810:       if (config->flags & CONFIG_FROM_ETHERS)
        !           811:        {
        !           812:          *up = tmp;
        !           813:          /* cannot have a clid */
        !           814:          if (config->flags & CONFIG_NAME)
        !           815:            free(config->hostname);
        !           816:          free(config->hwaddr);
        !           817:          free(config);
        !           818:        }
        !           819:       else
        !           820:        up = &config->next;
        !           821:     }
        !           822: 
        !           823:   while (fgets(buff, MAXDNAME, f))
        !           824:     {
        !           825:       char *host = NULL;
        !           826:       
        !           827:       lineno++;
        !           828:       
        !           829:       while (strlen(buff) > 0 && isspace((int)buff[strlen(buff)-1]))
        !           830:        buff[strlen(buff)-1] = 0;
        !           831:       
        !           832:       if ((*buff == '#') || (*buff == '+') || (*buff == 0))
        !           833:        continue;
        !           834:       
        !           835:       for (ip = buff; *ip && !isspace((int)*ip); ip++);
        !           836:       for(; *ip && isspace((int)*ip); ip++)
        !           837:        *ip = 0;
        !           838:       if (!*ip || parse_hex(buff, hwaddr, ETHER_ADDR_LEN, NULL, NULL) != ETHER_ADDR_LEN)
        !           839:        {
        !           840:          my_syslog(MS_DHCP | LOG_ERR, _("bad line at %s line %d"), ETHERSFILE, lineno); 
        !           841:          continue;
        !           842:        }
        !           843:       
        !           844:       /* check for name or dotted-quad */
        !           845:       for (cp = ip; *cp; cp++)
        !           846:        if (!(*cp == '.' || (*cp >='0' && *cp <= '9')))
        !           847:          break;
        !           848:       
        !           849:       if (!*cp)
        !           850:        {
        !           851:          if ((addr.s_addr = inet_addr(ip)) == (in_addr_t)-1)
        !           852:            {
        !           853:              my_syslog(MS_DHCP | LOG_ERR, _("bad address at %s line %d"), ETHERSFILE, lineno); 
        !           854:              continue;
        !           855:            }
        !           856: 
        !           857:          flags = CONFIG_ADDR;
        !           858:          
        !           859:          for (config = daemon->dhcp_conf; config; config = config->next)
        !           860:            if ((config->flags & CONFIG_ADDR) && config->addr.s_addr == addr.s_addr)
        !           861:              break;
        !           862:        }
        !           863:       else 
        !           864:        {
        !           865:          int nomem;
        !           866:          if (!(host = canonicalise(ip, &nomem)) || !legal_hostname(host))
        !           867:            {
        !           868:              if (!nomem)
        !           869:                my_syslog(MS_DHCP | LOG_ERR, _("bad name at %s line %d"), ETHERSFILE, lineno); 
        !           870:              free(host);
        !           871:              continue;
        !           872:            }
        !           873:              
        !           874:          flags = CONFIG_NAME;
        !           875: 
        !           876:          for (config = daemon->dhcp_conf; config; config = config->next)
        !           877:            if ((config->flags & CONFIG_NAME) && hostname_isequal(config->hostname, host))
        !           878:              break;
        !           879:        }
        !           880: 
        !           881:       if (config && (config->flags & CONFIG_FROM_ETHERS))
        !           882:        {
        !           883:          my_syslog(MS_DHCP | LOG_ERR, _("ignoring %s line %d, duplicate name or IP address"), ETHERSFILE, lineno); 
        !           884:          continue;
        !           885:        }
        !           886:        
        !           887:       if (!config)
        !           888:        { 
        !           889:          for (config = daemon->dhcp_conf; config; config = config->next)
        !           890:            {
        !           891:              struct hwaddr_config *conf_addr = config->hwaddr;
        !           892:              if (conf_addr && 
        !           893:                  conf_addr->next == NULL && 
        !           894:                  conf_addr->wildcard_mask == 0 &&
        !           895:                  conf_addr->hwaddr_len == ETHER_ADDR_LEN &&
        !           896:                  (conf_addr->hwaddr_type == ARPHRD_ETHER || conf_addr->hwaddr_type == 0) &&
        !           897:                  memcmp(conf_addr->hwaddr, hwaddr, ETHER_ADDR_LEN) == 0)
        !           898:                break;
        !           899:            }
        !           900:          
        !           901:          if (!config)
        !           902:            {
        !           903:              if (!(config = whine_malloc(sizeof(struct dhcp_config))))
        !           904:                continue;
        !           905:              config->flags = CONFIG_FROM_ETHERS;
        !           906:              config->hwaddr = NULL;
        !           907:              config->domain = NULL;
        !           908:              config->netid = NULL;
        !           909:              config->next = daemon->dhcp_conf;
        !           910:              daemon->dhcp_conf = config;
        !           911:            }
        !           912:          
        !           913:          config->flags |= flags;
        !           914:          
        !           915:          if (flags & CONFIG_NAME)
        !           916:            {
        !           917:              config->hostname = host;
        !           918:              host = NULL;
        !           919:            }
        !           920:          
        !           921:          if (flags & CONFIG_ADDR)
        !           922:            config->addr = addr;
        !           923:        }
        !           924:       
        !           925:       config->flags |= CONFIG_NOCLID;
        !           926:       if (!config->hwaddr)
        !           927:        config->hwaddr = whine_malloc(sizeof(struct hwaddr_config));
        !           928:       if (config->hwaddr)
        !           929:        {
        !           930:          memcpy(config->hwaddr->hwaddr, hwaddr, ETHER_ADDR_LEN);
        !           931:          config->hwaddr->hwaddr_len = ETHER_ADDR_LEN;
        !           932:          config->hwaddr->hwaddr_type = ARPHRD_ETHER;
        !           933:          config->hwaddr->wildcard_mask = 0;
        !           934:          config->hwaddr->next = NULL;
        !           935:        }
        !           936:       count++;
        !           937:       
        !           938:       free(host);
        !           939: 
        !           940:     }
        !           941:   
        !           942:   fclose(f);
        !           943: 
        !           944:   my_syslog(MS_DHCP | LOG_INFO, _("read %s - %d addresses"), ETHERSFILE, count);
        !           945: }
        !           946: 
        !           947: 
        !           948: /* If we've not found a hostname any other way, try and see if there's one in /etc/hosts
        !           949:    for this address. If it has a domain part, that must match the set domain and
        !           950:    it gets stripped. The set of legal domain names is bigger than the set of legal hostnames
        !           951:    so check here that the domain name is legal as a hostname. 
        !           952:    NOTE: we're only allowed to overwrite daemon->dhcp_buff if we succeed. */
        !           953: char *host_from_dns(struct in_addr addr)
        !           954: {
        !           955:   struct crec *lookup;
        !           956: 
        !           957:   if (daemon->port == 0)
        !           958:     return NULL; /* DNS disabled. */
        !           959:   
        !           960:   lookup = cache_find_by_addr(NULL, (struct all_addr *)&addr, 0, F_IPV4);
        !           961: 
        !           962:   if (lookup && (lookup->flags & F_HOSTS))
        !           963:     {
        !           964:       char *dot, *hostname = cache_get_name(lookup);
        !           965:       dot = strchr(hostname, '.');
        !           966:       
        !           967:       if (dot && strlen(dot+1) != 0)
        !           968:        {
        !           969:          char *d2 = get_domain(addr);
        !           970:          if (!d2 || !hostname_isequal(dot+1, d2))
        !           971:            return NULL; /* wrong domain */
        !           972:        }
        !           973: 
        !           974:       if (!legal_hostname(hostname))
        !           975:        return NULL;
        !           976:       
        !           977:       strncpy(daemon->dhcp_buff, hostname, 256);
        !           978:       daemon->dhcp_buff[255] = 0;
        !           979:       strip_hostname(daemon->dhcp_buff);
        !           980: 
        !           981:       return daemon->dhcp_buff;
        !           982:     }
        !           983:   
        !           984:   return NULL;
        !           985: }
        !           986: 
        !           987: #endif
        !           988: 

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