Annotation of embedaddon/dnsmasq/src/dhcp6.c, revision 1.1.1.5

1.1.1.5 ! misho       1: /* dnsmasq is Copyright (c) 2000-2022 Simon Kelley
1.1       misho       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_DHCP6
                     20: 
1.1.1.2   misho      21: #include <netinet/icmp6.h>
                     22: 
1.1       misho      23: struct iface_param {
                     24:   struct dhcp_context *current;
1.1.1.5 ! misho      25:   struct in6_addr fallback, ll_addr, ula_addr;
1.1       misho      26:   int ind, addr_match;
                     27: };
                     28: 
1.1.1.2   misho      29: 
1.1       misho      30: static int complete_context6(struct in6_addr *local,  int prefix,
                     31:                             int scope, int if_index, int flags, 
                     32:                             unsigned int preferred, unsigned int valid, void *vparam);
                     33: static int make_duid1(int index, unsigned int type, char *mac, size_t maclen, void *parm); 
                     34: 
                     35: void dhcp6_init(void)
                     36: {
                     37:   int fd;
                     38:   struct sockaddr_in6 saddr;
                     39: #if defined(IPV6_TCLASS) && defined(IPTOS_CLASS_CS6)
                     40:   int class = IPTOS_CLASS_CS6;
                     41: #endif
                     42:   int oneopt = 1;
                     43: 
                     44:   if ((fd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP)) == -1 ||
                     45: #if defined(IPV6_TCLASS) && defined(IPTOS_CLASS_CS6)
                     46:       setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &class, sizeof(class)) == -1 ||
                     47: #endif
                     48:       setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &oneopt, sizeof(oneopt)) == -1 ||
                     49:       !fix_fd(fd) ||
                     50:       !set_ipv6pktinfo(fd))
                     51:     die (_("cannot create DHCPv6 socket: %s"), NULL, EC_BADNET);
                     52:   
1.1.1.4   misho      53:  /* When bind-interfaces is set, there might be more than one dnsmasq
1.1       misho      54:      instance binding port 547. That's OK if they serve different networks.
1.1.1.4   misho      55:      Need to set REUSEADDR|REUSEPORT to make this possible.
1.1       misho      56:      Handle the case that REUSEPORT is defined, but the kernel doesn't 
                     57:      support it. This handles the introduction of REUSEPORT on Linux. */
                     58:   if (option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND))
                     59:     {
1.1.1.2   misho      60:       int rc = 0;
1.1       misho      61: 
                     62: #ifdef SO_REUSEPORT
                     63:       if ((rc = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &oneopt, sizeof(oneopt))) == -1 &&
1.1.1.2   misho      64:          errno == ENOPROTOOPT)
                     65:        rc = 0;
1.1       misho      66: #endif
                     67:       
1.1.1.2   misho      68:       if (rc != -1)
1.1       misho      69:        rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &oneopt, sizeof(oneopt));
                     70:       
                     71:       if (rc == -1)
                     72:        die(_("failed to set SO_REUSE{ADDR|PORT} on DHCPv6 socket: %s"), NULL, EC_BADNET);
                     73:     }
                     74:   
                     75:   memset(&saddr, 0, sizeof(saddr));
                     76: #ifdef HAVE_SOCKADDR_SA_LEN
                     77:   saddr.sin6_len = sizeof(struct sockaddr_in6);
                     78: #endif
                     79:   saddr.sin6_family = AF_INET6;
                     80:   saddr.sin6_addr = in6addr_any;
                     81:   saddr.sin6_port = htons(DHCPV6_SERVER_PORT);
                     82:   
                     83:   if (bind(fd, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in6)))
                     84:     die(_("failed to bind DHCPv6 server socket: %s"), NULL, EC_BADNET);
                     85:   
                     86:   daemon->dhcp6fd = fd;
                     87: }
                     88: 
                     89: void dhcp6_packet(time_t now)
                     90: {
                     91:   struct dhcp_context *context;
                     92:   struct iface_param parm;
                     93:   struct cmsghdr *cmptr;
                     94:   struct msghdr msg;
                     95:   int if_index = 0;
                     96:   union {
                     97:     struct cmsghdr align; /* this ensures alignment */
                     98:     char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
                     99:   } control_u;
                    100:   struct sockaddr_in6 from;
                    101:   ssize_t sz; 
                    102:   struct ifreq ifr;
                    103:   struct iname *tmp;
                    104:   unsigned short port;
1.1.1.2   misho     105:   struct in6_addr dst_addr;
1.1.1.5 ! misho     106:   struct in6_addr all_servers;
        !           107:   
1.1.1.2   misho     108:   memset(&dst_addr, 0, sizeof(dst_addr));
1.1       misho     109: 
                    110:   msg.msg_control = control_u.control6;
                    111:   msg.msg_controllen = sizeof(control_u);
                    112:   msg.msg_flags = 0;
                    113:   msg.msg_name = &from;
                    114:   msg.msg_namelen = sizeof(from);
                    115:   msg.msg_iov =  &daemon->dhcp_packet;
                    116:   msg.msg_iovlen = 1;
                    117:   
                    118:   if ((sz = recv_dhcp_packet(daemon->dhcp6fd, &msg)) == -1)
                    119:     return;
                    120:   
1.1.1.5 ! misho     121: #ifdef HAVE_DUMPFILE
        !           122:   dump_packet_udp(DUMP_DHCPV6, (void *)daemon->dhcp_packet.iov_base, sz,
        !           123:                  (union mysockaddr *)&from, NULL, daemon->dhcp6fd);
        !           124: #endif
        !           125:   
1.1       misho     126:   for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
                    127:     if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo)
                    128:       {
                    129:        union {
                    130:          unsigned char *c;
                    131:          struct in6_pktinfo *p;
                    132:        } p;
                    133:        p.c = CMSG_DATA(cmptr);
                    134:         
                    135:        if_index = p.p->ipi6_ifindex;
1.1.1.2   misho     136:        dst_addr = p.p->ipi6_addr;
1.1       misho     137:       }
                    138: 
                    139:   if (!indextoname(daemon->dhcp6fd, if_index, ifr.ifr_name))
                    140:     return;
                    141: 
1.1.1.5 ! misho     142:   if (relay_reply6(&from, sz, ifr.ifr_name))
1.1.1.4   misho     143:     {
1.1.1.5 ! misho     144: #ifdef HAVE_DUMPFILE
        !           145:       dump_packet_udp(DUMP_DHCPV6, (void *)daemon->outpacket.iov_base, save_counter(-1), NULL,
        !           146:                      (union mysockaddr *)&from, daemon->dhcp6fd);
        !           147: #endif
        !           148:       
1.1.1.4   misho     149:       while (retry_send(sendto(daemon->dhcp6fd, daemon->outpacket.iov_base, 
                    150:                               save_counter(-1), 0, (struct sockaddr *)&from, 
                    151:                               sizeof(from))));
                    152:     }
                    153:   else
1.1       misho     154:     {
1.1.1.3   misho     155:       struct dhcp_bridge *bridge, *alias;
1.1.1.5 ! misho     156:       
1.1.1.2   misho     157:       for (tmp = daemon->if_except; tmp; tmp = tmp->next)
                    158:        if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
                    159:          return;
1.1       misho     160:       
1.1.1.2   misho     161:       for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
1.1       misho     162:        if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
1.1.1.2   misho     163:          return;
                    164:       
                    165:       parm.current = NULL;
                    166:       parm.ind = if_index;
                    167:       parm.addr_match = 0;
                    168:       memset(&parm.fallback, 0, IN6ADDRSZ);
                    169:       memset(&parm.ll_addr, 0, IN6ADDRSZ);
                    170:       memset(&parm.ula_addr, 0, IN6ADDRSZ);
1.1.1.5 ! misho     171:       
1.1.1.3   misho     172:       /* If the interface on which the DHCPv6 request was received is
                    173:          an alias of some other interface (as specified by the
                    174:          --bridge-interface option), change parm.ind so that we look
                    175:          for DHCPv6 contexts associated with the aliased interface
                    176:          instead of with the aliasing one. */
                    177:       for (bridge = daemon->bridges; bridge; bridge = bridge->next)
                    178:        {
                    179:          for (alias = bridge->alias; alias; alias = alias->next)
                    180:            if (wildcard_matchn(alias->iface, ifr.ifr_name, IF_NAMESIZE))
                    181:              {
                    182:                parm.ind = if_nametoindex(bridge->iface);
                    183:                if (!parm.ind)
                    184:                  {
                    185:                    my_syslog(MS_DHCP | LOG_WARNING,
                    186:                              _("unknown interface %s in bridge-interface"),
                    187:                              bridge->iface);
                    188:                    return;
                    189:                  }
                    190:                break;
                    191:              }
                    192:          if (alias)
                    193:            break;
                    194:        }
1.1.1.2   misho     195:       
                    196:       for (context = daemon->dhcp6; context; context = context->next)
                    197:        if (IN6_IS_ADDR_UNSPECIFIED(&context->start6) && context->prefix == 0)
                    198:          {
                    199:            /* wildcard context for DHCP-stateless only */
                    200:            parm.current = context;
                    201:            context->current = NULL;
                    202:          }
                    203:        else
                    204:          {
                    205:            /* unlinked contexts are marked by context->current == context */
                    206:            context->current = context;
                    207:            memset(&context->local6, 0, IN6ADDRSZ);
                    208:          }
1.1.1.5 ! misho     209:       
        !           210:       /* Ignore requests sent to the ALL_SERVERS multicast address for relay when
        !           211:         we're listening there for DHCPv6 server reasons. */
        !           212:       inet_pton(AF_INET6, ALL_SERVERS, &all_servers);
        !           213:       
        !           214:       if (!IN6_ARE_ADDR_EQUAL(&dst_addr, &all_servers) &&
        !           215:          relay_upstream6(if_index, (size_t)sz, &from.sin6_addr, from.sin6_scope_id, now))
        !           216:        return;
1.1.1.2   misho     217:       
                    218:       if (!iface_enumerate(AF_INET6, &parm, complete_context6))
1.1       misho     219:        return;
1.1.1.5 ! misho     220:       
        !           221:       /* Check for a relay again after iface_enumerate/complete_context has had
        !           222:         chance to fill in relay->iface_index fields. This handles first time through
        !           223:         and any changes in interface config. */
        !           224:       if (!IN6_ARE_ADDR_EQUAL(&dst_addr, &all_servers) &&
        !           225:          relay_upstream6(if_index, (size_t)sz, &from.sin6_addr, from.sin6_scope_id, now))
        !           226:        return;
        !           227:       
1.1.1.2   misho     228:       if (daemon->if_names || daemon->if_addrs)
                    229:        {
                    230:          
                    231:          for (tmp = daemon->if_names; tmp; tmp = tmp->next)
                    232:            if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
                    233:              break;
                    234:          
                    235:          if (!tmp && !parm.addr_match)
                    236:            return;
                    237:        }
                    238:       
                    239:       /* May have configured relay, but not DHCP server */
                    240:       if (!daemon->doing_dhcp6)
                    241:        return;
1.1.1.5 ! misho     242:       
1.1.1.2   misho     243:       lease_prune(NULL, now); /* lose any expired leases */
                    244:       
                    245:       port = dhcp6_reply(parm.current, if_index, ifr.ifr_name, &parm.fallback, 
                    246:                         &parm.ll_addr, &parm.ula_addr, sz, &from.sin6_addr, now);
                    247:       
1.1.1.4   misho     248:       /* The port in the source address of the original request should
                    249:         be correct, but at least once client sends from the server port,
                    250:         so we explicitly send to the client port to a client, and the
                    251:         server port to a relay. */
                    252:       if (port != 0)
                    253:        {
                    254:          from.sin6_port = htons(port);
1.1.1.5 ! misho     255:          
        !           256: #ifdef HAVE_DUMPFILE
        !           257:          dump_packet_udp(DUMP_DHCPV6, (void *)daemon->outpacket.iov_base, save_counter(-1),
        !           258:                          NULL, (union mysockaddr *)&from, daemon->dhcp6fd);
        !           259: #endif 
        !           260:          
        !           261:          while (retry_send(sendto(daemon->dhcp6fd, daemon->outpacket.iov_base,
        !           262:                                   save_counter(-1), 0, (struct sockaddr *)&from, sizeof(from))));
1.1.1.4   misho     263:        }
1.1.1.5 ! misho     264:       
1.1.1.4   misho     265:       /* These need to be called _after_ we send DHCPv6 packet, since lease_update_file()
                    266:         may trigger sending an RA packet, which overwrites our buffer. */
1.1.1.2   misho     267:       lease_update_file(now);
                    268:       lease_update_dns(0);
                    269:     }
1.1       misho     270: }
                    271: 
1.1.1.3   misho     272: void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac, unsigned int *maclenp, unsigned int *mactypep, time_t now)
1.1.1.2   misho     273: {
1.1.1.4   misho     274:   /* Receiving a packet from a host does not populate the neighbour
1.1.1.2   misho     275:      cache, so we send a neighbour discovery request if we can't 
                    276:      find the sender. Repeat a few times in case of packet loss. */
                    277:   
                    278:   struct neigh_packet neigh;
1.1.1.3   misho     279:   union mysockaddr addr;
                    280:   int i, maclen;
1.1.1.2   misho     281: 
                    282:   neigh.type = ND_NEIGHBOR_SOLICIT;
                    283:   neigh.code = 0;
                    284:   neigh.reserved = 0;
                    285:   neigh.target = *client;
1.1.1.3   misho     286:   /* RFC4443 section-2.3: checksum has to be zero to be calculated */
                    287:   neigh.checksum = 0;
                    288:    
1.1.1.2   misho     289:   memset(&addr, 0, sizeof(addr));
                    290: #ifdef HAVE_SOCKADDR_SA_LEN
1.1.1.3   misho     291:   addr.in6.sin6_len = sizeof(struct sockaddr_in6);
1.1.1.2   misho     292: #endif
1.1.1.3   misho     293:   addr.in6.sin6_family = AF_INET6;
                    294:   addr.in6.sin6_port = htons(IPPROTO_ICMPV6);
                    295:   addr.in6.sin6_addr = *client;
                    296:   addr.in6.sin6_scope_id = iface;
1.1.1.2   misho     297:   
                    298:   for (i = 0; i < 5; i++)
                    299:     {
                    300:       struct timespec ts;
                    301:       
1.1.1.3   misho     302:       if ((maclen = find_mac(&addr, mac, 0, now)) != 0)
1.1.1.2   misho     303:        break;
1.1.1.3   misho     304:          
1.1.1.5 ! misho     305:       while(retry_send(sendto(daemon->icmp6fd, &neigh, sizeof(neigh), 0, &addr.sa, sizeof(addr))));
1.1.1.2   misho     306:       
                    307:       ts.tv_sec = 0;
                    308:       ts.tv_nsec = 100000000; /* 100ms */
                    309:       nanosleep(&ts, NULL);
                    310:     }
                    311: 
1.1.1.3   misho     312:   *maclenp = maclen;
1.1.1.2   misho     313:   *mactypep = ARPHRD_ETHER;
                    314: }
                    315:     
1.1       misho     316: static int complete_context6(struct in6_addr *local,  int prefix,
                    317:                             int scope, int if_index, int flags, unsigned int preferred, 
                    318:                             unsigned int valid, void *vparam)
                    319: {
                    320:   struct dhcp_context *context;
1.1.1.4   misho     321:   struct shared_network *share;
1.1.1.2   misho     322:   struct dhcp_relay *relay;
1.1       misho     323:   struct iface_param *param = vparam;
                    324:   struct iname *tmp;
1.1.1.5 ! misho     325:   int match = !daemon->if_addrs;
1.1       misho     326:  
                    327:   (void)scope; /* warning */
                    328:   
1.1.1.4   misho     329:   if (if_index != param->ind)
                    330:     return 1;
                    331:   
                    332:   if (IN6_IS_ADDR_LINKLOCAL(local))
                    333:     param->ll_addr = *local;
                    334:   else if (IN6_IS_ADDR_ULA(local))
                    335:     param->ula_addr = *local;
                    336:       
                    337:   if (IN6_IS_ADDR_LOOPBACK(local) ||
                    338:       IN6_IS_ADDR_LINKLOCAL(local) ||
                    339:       IN6_IS_ADDR_MULTICAST(local))
                    340:     return 1;
                    341:   
                    342:   /* if we have --listen-address config, see if the 
                    343:      arrival interface has a matching address. */
                    344:   for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
                    345:     if (tmp->addr.sa.sa_family == AF_INET6 &&
                    346:        IN6_ARE_ADDR_EQUAL(&tmp->addr.in6.sin6_addr, local))
1.1.1.5 ! misho     347:       match = param->addr_match = 1;
1.1.1.4   misho     348:   
                    349:   /* Determine a globally address on the arrival interface, even
                    350:      if we have no matching dhcp-context, because we're only
                    351:      allocating on remote subnets via relays. This
                    352:      is used as a default for the DNS server option. */
                    353:   param->fallback = *local;
                    354:   
                    355:   for (context = daemon->dhcp6; context; context = context->next)
                    356:     if ((context->flags & CONTEXT_DHCP) &&
                    357:        !(context->flags & (CONTEXT_TEMPLATE | CONTEXT_OLD)) &&
                    358:        prefix <= context->prefix &&
                    359:        context->current == context)
                    360:       {
                    361:        if (is_same_net6(local, &context->start6, context->prefix) &&
                    362:            is_same_net6(local, &context->end6, context->prefix))
1.1.1.2   misho     363:          {
1.1.1.4   misho     364:            struct dhcp_context *tmp, **up;
                    365:            
                    366:            /* use interface values only for constructed contexts */
                    367:            if (!(context->flags & CONTEXT_CONSTRUCTED))
                    368:              preferred = valid = 0xffffffff;
                    369:            else if (flags & IFACE_DEPRECATED)
                    370:              preferred = 0;
                    371:                    
                    372:            if (context->flags & CONTEXT_DEPRECATE)
                    373:              preferred = 0;
                    374:            
                    375:            /* order chain, longest preferred time first */
                    376:            for (up = &param->current, tmp = param->current; tmp; tmp = tmp->current)
                    377:              if (tmp->preferred <= preferred)
                    378:                break;
                    379:              else
                    380:                up = &tmp->current;
                    381:            
                    382:            context->current = *up;
                    383:            *up = context;
                    384:            context->local6 = *local;
                    385:            context->preferred = preferred;
                    386:            context->valid = valid;
1.1.1.2   misho     387:          }
1.1.1.4   misho     388:        else
                    389:          {
                    390:            for (share = daemon->shared_networks; share; share = share->next)
                    391:              {
                    392:                /* IPv4 shared_address - ignore */
                    393:                if (share->shared_addr.s_addr != 0)
                    394:                  continue;
                    395:                        
                    396:                if (share->if_index != 0)
                    397:                  {
                    398:                    if (share->if_index != if_index)
                    399:                      continue;
                    400:                  }
                    401:                else
                    402:                  {
                    403:                    if (!IN6_ARE_ADDR_EQUAL(&share->match_addr6, local))
                    404:                      continue;
                    405:                  }
                    406:                
                    407:                if (is_same_net6(&share->shared_addr6, &context->start6, context->prefix) &&
                    408:                    is_same_net6(&share->shared_addr6, &context->end6, context->prefix))
                    409:                  {
                    410:                    context->current = param->current;
                    411:                    param->current = context;
                    412:                    context->local6 = *local;
                    413:                    context->preferred = context->flags & CONTEXT_DEPRECATE ? 0 :0xffffffff;
                    414:                    context->valid = 0xffffffff;
                    415:                  }
                    416:              }
                    417:          }      
                    418:       }
1.1.1.5 ! misho     419:   
        !           420:   if (match)
        !           421:     for (relay = daemon->relay6; relay; relay = relay->next)
        !           422:       if (IN6_ARE_ADDR_EQUAL(local, &relay->local.addr6))
        !           423:        relay->iface_index = if_index;
        !           424:   
1.1.1.4   misho     425:   return 1;
1.1       misho     426: }
                    427: 
1.1.1.4   misho     428: struct dhcp_config *config_find_by_address6(struct dhcp_config *configs, struct in6_addr *net, int prefix,  struct in6_addr *addr)
1.1       misho     429: {
                    430:   struct dhcp_config *config;
                    431:   
                    432:   for (config = configs; config; config = config->next)
1.1.1.4   misho     433:     if (config->flags & CONFIG_ADDR6)
                    434:       {
                    435:        struct addrlist *addr_list;
                    436:        
                    437:        for (addr_list = config->addr6; addr_list; addr_list = addr_list->next)
                    438:          if ((!net || is_same_net6(&addr_list->addr.addr6, net, prefix) || ((addr_list->flags & ADDRLIST_WILDCARD) && prefix == 64)) &&
                    439:              is_same_net6(&addr_list->addr.addr6, addr, (addr_list->flags & ADDRLIST_PREFIX) ? addr_list->prefixlen : 128))
                    440:            return config;
                    441:       }
1.1       misho     442:   
                    443:   return NULL;
                    444: }
                    445: 
1.1.1.2   misho     446: struct dhcp_context *address6_allocate(struct dhcp_context *context,  unsigned char *clid, int clid_len, int temp_addr,
1.1.1.4   misho     447:                                       unsigned int iaid, int serial, struct dhcp_netid *netids, int plain_range, struct in6_addr *ans)
1.1       misho     448: {
                    449:   /* Find a free address: exclude anything in use and anything allocated to
                    450:      a particular hwaddr/clientid/hostname in our configuration.
                    451:      Try to return from contexts which match netids first. 
                    452:      
                    453:      Note that we assume the address prefix lengths are 64 or greater, so we can
                    454:      get by with 64 bit arithmetic.
                    455: */
                    456: 
                    457:   u64 start, addr;
                    458:   struct dhcp_context *c, *d;
                    459:   int i, pass;
                    460:   u64 j; 
                    461: 
                    462:   /* hash hwaddr: use the SDBM hashing algorithm.  This works
1.1.1.2   misho     463:      for MAC addresses, let's see how it manages with client-ids! 
                    464:      For temporary addresses, we generate a new random one each time. */
                    465:   if (temp_addr)
                    466:     j = rand64();
                    467:   else
                    468:     for (j = iaid, i = 0; i < clid_len; i++)
1.1.1.3   misho     469:       j = clid[i] + (j << 6) + (j << 16) - j;
1.1       misho     470:   
                    471:   for (pass = 0; pass <= plain_range ? 1 : 0; pass++)
                    472:     for (c = context; c; c = c->current)
                    473:       if (c->flags & (CONTEXT_DEPRECATE | CONTEXT_STATIC | CONTEXT_RA_STATELESS | CONTEXT_USED))
                    474:        continue;
                    475:       else if (!match_netid(c->filter, netids, pass))
                    476:        continue;
                    477:       else
                    478:        { 
1.1.1.2   misho     479:          if (!temp_addr && option_bool(OPT_CONSEC_ADDR))
1.1.1.4   misho     480:            {
                    481:              /* seed is largest extant lease addr in this context,
                    482:                 skip addresses equal to the number of addresses rejected
                    483:                 by clients. This should avoid the same client being offered the same
                    484:                 address after it has rjected it. */
                    485:              start = lease_find_max_addr6(c) + 1 + serial + c->addr_epoch;
                    486:              if (c->addr_epoch)
                    487:                c->addr_epoch--;
                    488:            }
1.1       misho     489:          else
1.1.1.3   misho     490:            {
                    491:              u64 range = 1 + addr6part(&c->end6) - addr6part(&c->start6);
                    492:              u64 offset = j + c->addr_epoch;
                    493: 
                    494:              /* don't divide by zero if range is whole 2^64 */
                    495:              if (range != 0)
                    496:                offset = offset % range;
                    497: 
                    498:              start = addr6part(&c->start6) + offset;
                    499:            }
1.1       misho     500: 
                    501:          /* iterate until we find a free address. */
                    502:          addr = start;
                    503:          
                    504:          do {
                    505:            /* eliminate addresses in use by the server. */
                    506:            for (d = context; d; d = d->current)
                    507:              if (addr == addr6part(&d->local6))
                    508:                break;
1.1.1.4   misho     509:            
                    510:            *ans = c->start6;
                    511:            setaddr6part (ans, addr);
1.1       misho     512: 
                    513:            if (!d &&
                    514:                !lease6_find_by_addr(&c->start6, c->prefix, addr) && 
1.1.1.4   misho     515:                !config_find_by_address6(daemon->dhcp_conf, &c->start6, c->prefix, ans))
                    516:              return c;
                    517:            
1.1       misho     518:            addr++;
                    519:            
                    520:            if (addr  == addr6part(&c->end6) + 1)
                    521:              addr = addr6part(&c->start6);
                    522:            
                    523:          } while (addr != start);
                    524:        }
                    525:           
                    526:   return NULL;
                    527: }
                    528: 
                    529: /* can dynamically allocate addr */
                    530: struct dhcp_context *address6_available(struct dhcp_context *context, 
                    531:                                        struct in6_addr *taddr,
                    532:                                        struct dhcp_netid *netids,
                    533:                                        int plain_range)
                    534: {
                    535:   u64 start, end, addr = addr6part(taddr);
                    536:   struct dhcp_context *tmp;
                    537:  
                    538:   for (tmp = context; tmp; tmp = tmp->current)
                    539:     {
                    540:       start = addr6part(&tmp->start6);
                    541:       end = addr6part(&tmp->end6);
                    542: 
                    543:       if (!(tmp->flags & (CONTEXT_STATIC | CONTEXT_RA_STATELESS)) &&
                    544:           is_same_net6(&tmp->start6, taddr, tmp->prefix) &&
                    545:          is_same_net6(&tmp->end6, taddr, tmp->prefix) &&
                    546:          addr >= start &&
                    547:           addr <= end &&
                    548:           match_netid(tmp->filter, netids, plain_range))
                    549:         return tmp;
                    550:     }
                    551: 
                    552:   return NULL;
                    553: }
                    554: 
                    555: /* address OK if configured */
                    556: struct dhcp_context *address6_valid(struct dhcp_context *context, 
                    557:                                    struct in6_addr *taddr,
                    558:                                    struct dhcp_netid *netids,
                    559:                                    int plain_range)
                    560: {
                    561:   struct dhcp_context *tmp;
                    562:  
                    563:   for (tmp = context; tmp; tmp = tmp->current)
                    564:     if (is_same_net6(&tmp->start6, taddr, tmp->prefix) &&
                    565:        match_netid(tmp->filter, netids, plain_range))
                    566:       return tmp;
                    567: 
                    568:   return NULL;
                    569: }
                    570: 
                    571: void make_duid(time_t now)
                    572: {
1.1.1.2   misho     573:   (void)now;
                    574: 
1.1       misho     575:   if (daemon->duid_config)
                    576:     {
                    577:       unsigned char *p;
                    578:       
                    579:       daemon->duid = p = safe_malloc(daemon->duid_config_len + 6);
                    580:       daemon->duid_len = daemon->duid_config_len + 6;
                    581:       PUTSHORT(2, p); /* DUID_EN */
                    582:       PUTLONG(daemon->duid_enterprise, p);
                    583:       memcpy(p, daemon->duid_config, daemon->duid_config_len);
                    584:     }
                    585:   else
                    586:     {
1.1.1.2   misho     587:       time_t newnow = 0;
                    588:       
                    589:       /* If we have no persistent lease database, or a non-stable RTC, use DUID_LL (newnow == 0) */
                    590: #ifndef HAVE_BROKEN_RTC
1.1       misho     591:       /* rebase epoch to 1/1/2000 */
1.1.1.2   misho     592:       if (!option_bool(OPT_LEASE_RO) || daemon->lease_change_command)
                    593:        newnow = now - 946684800;
                    594: #endif      
1.1       misho     595:       
                    596:       iface_enumerate(AF_LOCAL, &newnow, make_duid1);
                    597:       
                    598:       if(!daemon->duid)
                    599:        die("Cannot create DHCPv6 server DUID: %s", NULL, EC_MISC);
                    600:     }
                    601: }
                    602: 
                    603: static int make_duid1(int index, unsigned int type, char *mac, size_t maclen, void *parm)
                    604: {
                    605:   /* create DUID as specified in RFC3315. We use the MAC of the
                    606:      first interface we find that isn't loopback or P-to-P and
                    607:      has address-type < 256. Address types above 256 are things like 
                    608:      tunnels which don't have usable MAC addresses. */
                    609:   
                    610:   unsigned char *p;
                    611:   (void)index;
1.1.1.2   misho     612:   (void)parm;
                    613:   time_t newnow = *((time_t *)parm);
                    614:   
1.1       misho     615:   if (type >= 256)
                    616:     return 1;
                    617: 
1.1.1.2   misho     618:   if (newnow == 0)
                    619:     {
                    620:       daemon->duid = p = safe_malloc(maclen + 4);
                    621:       daemon->duid_len = maclen + 4;
                    622:       PUTSHORT(3, p); /* DUID_LL */
                    623:       PUTSHORT(type, p); /* address type */
                    624:     }
                    625:   else
                    626:     {
                    627:       daemon->duid = p = safe_malloc(maclen + 8);
                    628:       daemon->duid_len = maclen + 8;
                    629:       PUTSHORT(1, p); /* DUID_LLT */
                    630:       PUTSHORT(type, p); /* address type */
                    631:       PUTLONG(*((time_t *)parm), p); /* time */
                    632:     }
                    633:   
1.1       misho     634:   memcpy(p, mac, maclen);
                    635: 
                    636:   return 0;
                    637: }
                    638: 
                    639: struct cparam {
                    640:   time_t now;
                    641:   int newone, newname;
                    642: };
                    643: 
                    644: static int construct_worker(struct in6_addr *local, int prefix, 
                    645:                            int scope, int if_index, int flags, 
                    646:                            int preferred, int valid, void *vparam)
                    647: {
                    648:   char ifrn_name[IFNAMSIZ];
                    649:   struct in6_addr start6, end6;
                    650:   struct dhcp_context *template, *context;
1.1.1.4   misho     651:   struct iname *tmp;
                    652:   
1.1       misho     653:   (void)scope;
                    654:   (void)flags;
                    655:   (void)valid;
                    656:   (void)preferred;
                    657: 
                    658:   struct cparam *param = vparam;
                    659: 
                    660:   if (IN6_IS_ADDR_LOOPBACK(local) ||
                    661:       IN6_IS_ADDR_LINKLOCAL(local) ||
                    662:       IN6_IS_ADDR_MULTICAST(local))
                    663:     return 1;
                    664: 
1.1.1.2   misho     665:   if (!(flags & IFACE_PERMANENT))
                    666:     return 1;
                    667: 
                    668:   if (flags & IFACE_DEPRECATED)
                    669:     return 1;
                    670: 
1.1.1.4   misho     671:   /* Ignore interfaces where we're not doing RA/DHCP6 */
                    672:   if (!indextoname(daemon->icmp6fd, if_index, ifrn_name) ||
                    673:       !iface_check(AF_LOCAL, NULL, ifrn_name, NULL))
                    674:     return 1;
1.1       misho     675:   
1.1.1.4   misho     676:   for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
                    677:     if (tmp->name && wildcard_match(tmp->name, ifrn_name))
                    678:       return 1;
                    679: 
1.1       misho     680:   for (template = daemon->dhcp6; template; template = template->next)
1.1.1.4   misho     681:     if (!(template->flags & (CONTEXT_TEMPLATE | CONTEXT_CONSTRUCTED)))
1.1       misho     682:       {
                    683:        /* non-template entries, just fill in interface and local addresses */
1.1.1.2   misho     684:        if (prefix <= template->prefix &&
                    685:            is_same_net6(local, &template->start6, template->prefix) &&
                    686:            is_same_net6(local, &template->end6, template->prefix))
1.1       misho     687:          {
1.1.1.4   misho     688:            /* First time found, do fast RA. */
                    689:            if (template->if_index == 0)
                    690:              {
                    691:                ra_start_unsolicited(param->now, template);
                    692:                param->newone = 1;
                    693:              }
                    694:            
1.1       misho     695:            template->if_index = if_index;
                    696:            template->local6 = *local;
                    697:          }
                    698:        
                    699:       }
1.1.1.2   misho     700:     else if (wildcard_match(template->template_interface, ifrn_name) &&
                    701:             template->prefix >= prefix)
1.1       misho     702:       {
                    703:        start6 = *local;
                    704:        setaddr6part(&start6, addr6part(&template->start6));
                    705:        end6 = *local;
                    706:        setaddr6part(&end6, addr6part(&template->end6));
                    707:        
                    708:        for (context = daemon->dhcp6; context; context = context->next)
1.1.1.4   misho     709:          if (!(context->flags & CONTEXT_TEMPLATE) &&
1.1       misho     710:              IN6_ARE_ADDR_EQUAL(&start6, &context->start6) &&
                    711:              IN6_ARE_ADDR_EQUAL(&end6, &context->end6))
                    712:            {
1.1.1.4   misho     713:              /* If there's an absolute address context covering this address
                    714:                 then don't construct one as well. */
                    715:              if (!(context->flags & CONTEXT_CONSTRUCTED))
                    716:                break;
                    717:              
                    718:              if (context->if_index == if_index)
1.1.1.2   misho     719:                {
1.1.1.4   misho     720:                  int cflags = context->flags;
                    721:                  context->flags &= ~(CONTEXT_GC | CONTEXT_OLD);
                    722:                  if (cflags & CONTEXT_OLD)
                    723:                    {
                    724:                      /* address went, now it's back, and on the same interface */
                    725:                      log_context(AF_INET6, context); 
                    726:                      /* fast RAs for a while */
                    727:                      ra_start_unsolicited(param->now, context);
                    728:                      param->newone = 1; 
                    729:                      /* Add address to name again */
                    730:                      if (context->flags & CONTEXT_RA_NAME)
                    731:                        param->newname = 1;
                    732:                    
                    733:                    }
                    734:                  break;
1.1.1.2   misho     735:                }
1.1       misho     736:            }
                    737:        
                    738:        if (!context && (context = whine_malloc(sizeof (struct dhcp_context))))
                    739:          {
                    740:            *context = *template;
                    741:            context->start6 = start6;
                    742:            context->end6 = end6;
                    743:            context->flags &= ~CONTEXT_TEMPLATE;
                    744:            context->flags |= CONTEXT_CONSTRUCTED;
                    745:            context->if_index = if_index;
                    746:            context->local6 = *local;
1.1.1.2   misho     747:            context->saved_valid = 0;
1.1       misho     748:            
                    749:            context->next = daemon->dhcp6;
                    750:            daemon->dhcp6 = context;
                    751: 
1.1.1.4   misho     752:            ra_start_unsolicited(param->now, context);
1.1       misho     753:            /* we created a new one, need to call
                    754:               lease_update_file to get periodic functions called */
                    755:            param->newone = 1; 
                    756: 
                    757:            /* Will need to add new putative SLAAC addresses to existing leases */
                    758:            if (context->flags & CONTEXT_RA_NAME)
                    759:              param->newname = 1;
                    760:            
                    761:            log_context(AF_INET6, context);
                    762:          } 
                    763:       }
                    764:   
                    765:   return 1;
                    766: }
                    767: 
                    768: void dhcp_construct_contexts(time_t now)
                    769: { 
1.1.1.2   misho     770:   struct dhcp_context *context, *tmp, **up;
1.1       misho     771:   struct cparam param;
                    772:   param.newone = 0;
                    773:   param.newname = 0;
                    774:   param.now = now;
                    775: 
                    776:   for (context = daemon->dhcp6; context; context = context->next)
1.1.1.2   misho     777:     if (context->flags & CONTEXT_CONSTRUCTED)
                    778:       context->flags |= CONTEXT_GC;
                    779:    
1.1       misho     780:   iface_enumerate(AF_INET6, &param, construct_worker);
                    781: 
                    782:   for (up = &daemon->dhcp6, context = daemon->dhcp6; context; context = tmp)
                    783:     {
                    784:       
1.1.1.2   misho     785:       tmp = context->next; 
                    786:      
                    787:       if (context->flags & CONTEXT_GC && !(context->flags & CONTEXT_OLD))
1.1       misho     788:        {
1.1.1.3   misho     789:          if ((context->flags & CONTEXT_RA) || option_bool(OPT_RA))
1.1.1.2   misho     790:            {
                    791:              /* previously constructed context has gone. advertise it's demise */
                    792:              context->flags |= CONTEXT_OLD;
                    793:              context->address_lost_time = now;
                    794:              /* Apply same ceiling of configured lease time as in radv.c */
                    795:              if (context->saved_valid > context->lease_time)
                    796:                context->saved_valid = context->lease_time;
                    797:              /* maximum time is 2 hours, from RFC */
                    798:              if (context->saved_valid > 7200) /* 2 hours */
                    799:                context->saved_valid = 7200;
1.1.1.4   misho     800:              ra_start_unsolicited(now, context);
1.1.1.2   misho     801:              param.newone = 1; /* include deletion */ 
                    802:              
                    803:              if (context->flags & CONTEXT_RA_NAME)
                    804:                param.newname = 1; 
                    805:                              
                    806:              log_context(AF_INET6, context);
                    807:              
                    808:              up = &context->next;
                    809:            }
                    810:          else
                    811:            {
                    812:              /* we were never doing RA for this, so free now */
                    813:              *up = context->next;
                    814:              free(context);
                    815:            }
1.1       misho     816:        }
                    817:       else
1.1.1.2   misho     818:         up = &context->next;
1.1       misho     819:     }
                    820:   
                    821:   if (param.newone)
                    822:     {
                    823:       if (daemon->dhcp || daemon->doing_dhcp6)
                    824:        {
                    825:          if (param.newname)
                    826:            lease_update_slaac(now);
                    827:          lease_update_file(now);
                    828:        }
                    829:       else 
                    830:        /* Not doing DHCP, so no lease system, manage alarms for ra only */
                    831:        send_alarm(periodic_ra(now), now);
                    832:     }
                    833: }
                    834: 
1.1.1.5 ! misho     835: #endif /* HAVE_DHCP6 */

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