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

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

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