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

1.1     ! misho       1: /* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
        !             2: 
        !             3:    This program is free software; you can redistribute it and/or modify
        !             4:    it under the terms of the GNU General Public License as published by
        !             5:    the Free Software Foundation; version 2 dated June, 1991, or
        !             6:    (at your option) version 3 dated 29 June, 2007.
        !             7:  
        !             8:    This program is distributed in the hope that it will be useful,
        !             9:    but WITHOUT ANY WARRANTY; without even the implied warranty of
        !            10:    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
        !            11:    GNU General Public License for more details.
        !            12:      
        !            13:    You should have received a copy of the GNU General Public License
        !            14:    along with this program.  If not, see <http://www.gnu.org/licenses/>.
        !            15: */
        !            16: 
        !            17: 
        !            18: /* NB. This code may be called during a DHCPv4 or transaction which is in ping-wait
        !            19:    It therefore cannot use any DHCP buffer resources except outpacket, which is
        !            20:    not used by DHCPv4 code. This code may also be called when DHCP 4 or 6 isn't
        !            21:    active, so we ensure that outpacket is allocated here too */
        !            22: 
        !            23: #include "dnsmasq.h"
        !            24: 
        !            25: #ifdef HAVE_DHCP6
        !            26: 
        !            27: #include <netinet/icmp6.h>
        !            28: 
        !            29: struct ra_param {
        !            30:   time_t now;
        !            31:   int ind, managed, other, found_context, first;
        !            32:   char *if_name;
        !            33:   struct dhcp_netid *tags;
        !            34:   struct in6_addr link_local, link_global;
        !            35:   unsigned int pref_time;
        !            36: };
        !            37: 
        !            38: struct search_param {
        !            39:   time_t now; int iface;
        !            40: };
        !            41: 
        !            42: static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *dest);
        !            43: static int add_prefixes(struct in6_addr *local,  int prefix,
        !            44:                        int scope, int if_index, int flags, 
        !            45:                        unsigned int preferred, unsigned int valid, void *vparam);
        !            46: static int iface_search(struct in6_addr *local,  int prefix,
        !            47:                        int scope, int if_index, int flags, 
        !            48:                        int prefered, int valid, void *vparam);
        !            49: static int add_lla(int index, unsigned int type, char *mac, size_t maclen, void *parm);
        !            50: 
        !            51: static int hop_limit;
        !            52: 
        !            53: void ra_init(time_t now)
        !            54: {
        !            55:   struct icmp6_filter filter;
        !            56:   int fd;
        !            57: #if defined(IPV6_TCLASS) && defined(IPTOS_CLASS_CS6)
        !            58:   int class = IPTOS_CLASS_CS6;
        !            59: #endif
        !            60:   int val = 255; /* radvd uses this value */
        !            61:   socklen_t len = sizeof(int);
        !            62:   struct dhcp_context *context;
        !            63:   
        !            64:   /* ensure this is around even if we're not doing DHCPv6 */
        !            65:   expand_buf(&daemon->outpacket, sizeof(struct dhcp_packet));
        !            66:  
        !            67:   /* See if we're guessing SLAAC addresses, if so we need to recieve ping replies */
        !            68:   for (context = daemon->dhcp6; context; context = context->next)
        !            69:     if ((context->flags & CONTEXT_RA_NAME))
        !            70:       break;
        !            71:   
        !            72:   ICMP6_FILTER_SETBLOCKALL(&filter);
        !            73:   ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &filter);
        !            74:   if (context)
        !            75:     ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &filter);
        !            76:   
        !            77:   if ((fd = socket(PF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) == -1 ||
        !            78:       getsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &hop_limit, &len) ||
        !            79: #if defined(IPV6_TCLASS) && defined(IPTOS_CLASS_CS6)
        !            80:       setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &class, sizeof(class)) == -1 ||
        !            81: #endif
        !            82:       !fix_fd(fd) ||
        !            83:       !set_ipv6pktinfo(fd) ||
        !            84:       setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &val, sizeof(val)) ||
        !            85:       setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &val, sizeof(val)) ||
        !            86:       setsockopt(fd, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, sizeof(filter)) == -1)
        !            87:     die (_("cannot create ICMPv6 socket: %s"), NULL, EC_BADNET);
        !            88:   
        !            89:    daemon->icmp6fd = fd;
        !            90:    
        !            91:    ra_start_unsolicted(now, NULL);
        !            92: }
        !            93: 
        !            94: void ra_start_unsolicted(time_t now, struct dhcp_context *context)
        !            95: {   
        !            96:    /* init timers so that we do ra's for some/all soon. some ra_times will end up zeroed
        !            97:      if it's not appropriate to advertise those contexts.
        !            98:      This gets re-called on a netlink route-change to re-do the advertisement
        !            99:      and pick up new interfaces */
        !           100:   
        !           101:   if (context)
        !           102:     context->ra_short_period_start = context->ra_time = now;
        !           103:   else
        !           104:     for (context = daemon->dhcp6; context; context = context->next)
        !           105:       if (!(context->flags & CONTEXT_TEMPLATE))
        !           106:        {
        !           107:          context->ra_time = now + (rand16()/13000); /* range 0 - 5 */
        !           108:          /* re-do frequently for a minute or so, in case the first gets lost. */
        !           109:          context->ra_short_period_start = now;
        !           110:        }
        !           111: }
        !           112: 
        !           113: void icmp6_packet(time_t now)
        !           114: {
        !           115:   char interface[IF_NAMESIZE+1];
        !           116:   ssize_t sz; 
        !           117:   int if_index = 0;
        !           118:   struct cmsghdr *cmptr;
        !           119:   struct msghdr msg;
        !           120:   union {
        !           121:     struct cmsghdr align; /* this ensures alignment */
        !           122:     char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
        !           123:   } control_u;
        !           124:   struct sockaddr_in6 from;
        !           125:   unsigned char *packet;
        !           126:   struct iname *tmp;
        !           127: 
        !           128:   /* Note: use outpacket for input buffer */
        !           129:   msg.msg_control = control_u.control6;
        !           130:   msg.msg_controllen = sizeof(control_u);
        !           131:   msg.msg_flags = 0;
        !           132:   msg.msg_name = &from;
        !           133:   msg.msg_namelen = sizeof(from);
        !           134:   msg.msg_iov = &daemon->outpacket;
        !           135:   msg.msg_iovlen = 1;
        !           136:   
        !           137:   if ((sz = recv_dhcp_packet(daemon->icmp6fd, &msg)) == -1 || sz < 8)
        !           138:     return;
        !           139:    
        !           140:   packet = (unsigned char *)daemon->outpacket.iov_base;
        !           141:   
        !           142:   for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
        !           143:     if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo)
        !           144:       {
        !           145:        union {
        !           146:          unsigned char *c;
        !           147:          struct in6_pktinfo *p;
        !           148:        } p;
        !           149:        p.c = CMSG_DATA(cmptr);
        !           150:         
        !           151:        if_index = p.p->ipi6_ifindex;
        !           152:       }
        !           153:   
        !           154:   if (!indextoname(daemon->icmp6fd, if_index, interface))
        !           155:     return;
        !           156:     
        !           157:   if (!iface_check(AF_LOCAL, NULL, interface, NULL))
        !           158:     return;
        !           159:   
        !           160:   for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
        !           161:     if (tmp->name && wildcard_match(tmp->name, interface))
        !           162:       return;
        !           163:  
        !           164:   if (packet[1] != 0)
        !           165:     return;
        !           166:   
        !           167:   if (packet[0] == ICMP6_ECHO_REPLY)
        !           168:     lease_ping_reply(&from.sin6_addr, packet, interface); 
        !           169:   else if (packet[0] == ND_ROUTER_SOLICIT)
        !           170:     {
        !           171:       char *mac = "";
        !           172:       
        !           173:       /* look for link-layer address option for logging */
        !           174:       if (sz >= 16 && packet[8] == ICMP6_OPT_SOURCE_MAC && (packet[9] * 8) + 8 <= sz)
        !           175:        {
        !           176:          print_mac(daemon->namebuff, &packet[10], (packet[9] * 8) - 2);
        !           177:          mac = daemon->namebuff;
        !           178:        }
        !           179:          
        !           180:       my_syslog(MS_DHCP | LOG_INFO, "RTR-SOLICIT(%s) %s", interface, mac);
        !           181:       /* source address may not be valid in solicit request. */
        !           182:       send_ra(now, if_index, interface, !IN6_IS_ADDR_UNSPECIFIED(&from.sin6_addr) ? &from.sin6_addr : NULL);
        !           183:     }
        !           184: }
        !           185: 
        !           186: static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *dest)
        !           187: {
        !           188:   struct ra_packet *ra;
        !           189:   struct ra_param parm;
        !           190:   struct ifreq ifr;
        !           191:   struct sockaddr_in6 addr;
        !           192:   struct dhcp_context *context;
        !           193:   struct dhcp_netid iface_id;
        !           194:   struct dhcp_opt *opt_cfg;
        !           195:   int done_dns = 0;
        !           196: #ifdef HAVE_LINUX_NETWORK
        !           197:   FILE *f;
        !           198: #endif
        !           199: 
        !           200:   save_counter(0);
        !           201:   ra = expand(sizeof(struct ra_packet));
        !           202:   
        !           203:   ra->type = ND_ROUTER_ADVERT;
        !           204:   ra->code = 0;
        !           205:   ra->hop_limit = hop_limit;
        !           206:   ra->flags = 0x00;
        !           207:   ra->lifetime = htons(RA_INTERVAL * 3); /* AdvDefaultLifetime * 3 */
        !           208:   ra->reachable_time = 0;
        !           209:   ra->retrans_time = 0;
        !           210: 
        !           211:   parm.ind = iface;
        !           212:   parm.managed = 0;
        !           213:   parm.other = 0;
        !           214:   parm.found_context = 0;
        !           215:   parm.if_name = iface_name;
        !           216:   parm.first = 1;
        !           217:   parm.now = now;
        !           218:   parm.pref_time = 0;
        !           219:   
        !           220:   /* set tag with name == interface */
        !           221:   iface_id.net = iface_name;
        !           222:   iface_id.next = NULL;
        !           223:   parm.tags = &iface_id; 
        !           224:   
        !           225:   for (context = daemon->dhcp6; context; context = context->next)
        !           226:     {
        !           227:       context->flags &= ~CONTEXT_RA_DONE;
        !           228:       context->netid.next = &context->netid;
        !           229:     }
        !           230: 
        !           231:   if (!iface_enumerate(AF_INET6, &parm, add_prefixes) ||
        !           232:       !parm.found_context)
        !           233:     return;
        !           234: 
        !           235:   strncpy(ifr.ifr_name, iface_name, IF_NAMESIZE);
        !           236: 
        !           237: #ifdef HAVE_LINUX_NETWORK
        !           238:   /* Note that IPv6 MTU is not necessarilly the same as the IPv4 MTU
        !           239:      available from SIOCGIFMTU */
        !           240:   sprintf(daemon->namebuff, "/proc/sys/net/ipv6/conf/%s/mtu", iface_name);
        !           241:   if ((f = fopen(daemon->namebuff, "r")))
        !           242:     {
        !           243:       if (fgets(daemon->namebuff, MAXDNAME, f))
        !           244:        {
        !           245:          put_opt6_char(ICMP6_OPT_MTU);
        !           246:          put_opt6_char(1);
        !           247:          put_opt6_short(0);
        !           248:          put_opt6_long(atoi(daemon->namebuff));
        !           249:        }
        !           250:       fclose(f);
        !           251:     }
        !           252: #endif
        !           253:      
        !           254:   iface_enumerate(AF_LOCAL, &iface, add_lla);
        !           255:  
        !           256:   /* RDNSS, RFC 6106, use relevant DHCP6 options */
        !           257:   (void)option_filter(parm.tags, NULL, daemon->dhcp_opts6);
        !           258:   
        !           259:   for (opt_cfg = daemon->dhcp_opts6; opt_cfg; opt_cfg = opt_cfg->next)
        !           260:     {
        !           261:       int i;
        !           262:       
        !           263:       /* netids match and not encapsulated? */
        !           264:       if (!(opt_cfg->flags & DHOPT_TAGOK))
        !           265:         continue;
        !           266:       
        !           267:       if (opt_cfg->opt == OPTION6_DNS_SERVER)
        !           268:         {
        !           269:          struct in6_addr *a = (struct in6_addr *)opt_cfg->val;
        !           270: 
        !           271:          done_dns = 1;
        !           272:           if (opt_cfg->len == 0)
        !           273:             continue;
        !           274:          
        !           275:          put_opt6_char(ICMP6_OPT_RDNSS);
        !           276:          put_opt6_char((opt_cfg->len/8) + 1);
        !           277:          put_opt6_short(0);
        !           278:          put_opt6_long(RA_INTERVAL * 2); /* lifetime - twice RA retransmit */
        !           279:          /* zero means "self" */
        !           280:          for (i = 0; i < opt_cfg->len; i += IN6ADDRSZ, a++)
        !           281:            if (IN6_IS_ADDR_UNSPECIFIED(a))
        !           282:              put_opt6(&parm.link_global, IN6ADDRSZ);
        !           283:            else
        !           284:              put_opt6(a, IN6ADDRSZ);
        !           285:        }
        !           286:       
        !           287:       if (opt_cfg->opt == OPTION6_DOMAIN_SEARCH && opt_cfg->len != 0)
        !           288:        {
        !           289:          int len = ((opt_cfg->len+7)/8);
        !           290:          
        !           291:          put_opt6_char(ICMP6_OPT_DNSSL);
        !           292:          put_opt6_char(len + 1);
        !           293:          put_opt6_short(0);
        !           294:          put_opt6_long(1800); /* lifetime - twice RA retransmit */
        !           295:          put_opt6(opt_cfg->val, opt_cfg->len);
        !           296:          
        !           297:          /* pad */
        !           298:          for (i = opt_cfg->len; i < len * 8; i++)
        !           299:            put_opt6_char(0);
        !           300:        }
        !           301:     }
        !           302:        
        !           303:   if (!done_dns)
        !           304:     {
        !           305:       /* default == us. */
        !           306:       put_opt6_char(ICMP6_OPT_RDNSS);
        !           307:       put_opt6_char(3);
        !           308:       put_opt6_short(0);
        !           309:       put_opt6_long(RA_INTERVAL * 2); /* lifetime - twice RA retransmit */
        !           310:       put_opt6(&parm.link_global, IN6ADDRSZ);
        !           311:     }
        !           312: 
        !           313:   /* set managed bits unless we're providing only RA on this link */
        !           314:   if (parm.managed)
        !           315:     ra->flags |= 0x80; /* M flag, managed, */
        !           316:    if (parm.other)
        !           317:     ra->flags |= 0x40; /* O flag, other */ 
        !           318:                        
        !           319:   /* decide where we're sending */
        !           320:   memset(&addr, 0, sizeof(addr));
        !           321: #ifdef HAVE_SOCKADDR_SA_LEN
        !           322:   addr.sin6_len = sizeof(struct sockaddr_in6);
        !           323: #endif
        !           324:   addr.sin6_family = AF_INET6;
        !           325:   addr.sin6_port = htons(IPPROTO_ICMPV6);
        !           326:   if (dest)
        !           327:     {
        !           328:       addr.sin6_addr = *dest;
        !           329:       if (IN6_IS_ADDR_LINKLOCAL(dest) ||
        !           330:          IN6_IS_ADDR_MC_LINKLOCAL(dest))
        !           331:        addr.sin6_scope_id = iface;
        !           332:     }
        !           333:   else
        !           334:     inet_pton(AF_INET6, ALL_NODES, &addr.sin6_addr); 
        !           335:   
        !           336:   send_from(daemon->icmp6fd, 0, daemon->outpacket.iov_base, save_counter(0),
        !           337:            (union mysockaddr *)&addr, (struct all_addr *)&parm.link_local, iface); 
        !           338:   
        !           339: }
        !           340: 
        !           341: static int add_prefixes(struct in6_addr *local,  int prefix,
        !           342:                        int scope, int if_index, int flags, 
        !           343:                        unsigned int preferred, unsigned int valid, void *vparam)
        !           344: {
        !           345:   struct ra_param *param = vparam;
        !           346: 
        !           347:   (void)scope; /* warning */
        !           348:   
        !           349:   if (if_index == param->ind)
        !           350:     {
        !           351:       if (IN6_IS_ADDR_LINKLOCAL(local))
        !           352:        param->link_local = *local;
        !           353:       else if (!IN6_IS_ADDR_LOOPBACK(local) &&
        !           354:               !IN6_IS_ADDR_MULTICAST(local))
        !           355:        {
        !           356:          int do_prefix = 0;
        !           357:          int do_slaac = 0;
        !           358:          int deprecate  = 0;
        !           359:          int constructed = 0;
        !           360:          unsigned int time = 0xffffffff;
        !           361:          struct dhcp_context *context;
        !           362:          
        !           363:          for (context = daemon->dhcp6; context; context = context->next)
        !           364:            if (!(context->flags & CONTEXT_TEMPLATE) &&
        !           365:                prefix == context->prefix &&
        !           366:                is_same_net6(local, &context->start6, prefix) &&
        !           367:                is_same_net6(local, &context->end6, prefix))
        !           368:              {
        !           369:                if ((context->flags & 
        !           370:                     (CONTEXT_RA_ONLY | CONTEXT_RA_NAME | CONTEXT_RA_STATELESS)))
        !           371:                  {
        !           372:                    do_slaac = 1;
        !           373:                    if (context->flags & CONTEXT_DHCP)
        !           374:                      {
        !           375:                        param->other = 1; 
        !           376:                        if (!(context->flags & CONTEXT_RA_STATELESS))
        !           377:                          param->managed = 1;
        !           378:                      }
        !           379:                  }
        !           380:                else
        !           381:                  {
        !           382:                    /* don't do RA for non-ra-only unless --enable-ra is set */
        !           383:                    if (!option_bool(OPT_RA))
        !           384:                      continue;
        !           385:                    param->managed = 1;
        !           386:                    param->other = 1;
        !           387:                  }
        !           388:                
        !           389:                /* find floor time, don't reduce below RA interval. */
        !           390:                if (time > context->lease_time)
        !           391:                  {
        !           392:                    time = context->lease_time;
        !           393:                    if (time < ((unsigned int)RA_INTERVAL))
        !           394:                      time = RA_INTERVAL;
        !           395:                  }
        !           396: 
        !           397:                if (context->flags & CONTEXT_DEPRECATE)
        !           398:                  deprecate = 1;
        !           399:                
        !           400:                if (context->flags & CONTEXT_CONSTRUCTED)
        !           401:                  constructed = 1;
        !           402: 
        !           403: 
        !           404:                /* collect dhcp-range tags */
        !           405:                if (context->netid.next == &context->netid && context->netid.net)
        !           406:                  {
        !           407:                    context->netid.next = param->tags;
        !           408:                    param->tags = &context->netid;
        !           409:                  }
        !           410:                  
        !           411:                /* subsequent prefixes on the same interface 
        !           412:                   and subsequent instances of this prefix don't need timers.
        !           413:                   Be careful not to find the same prefix twice with different
        !           414:                   addresses. */
        !           415:                if (!(context->flags & CONTEXT_RA_DONE))
        !           416:                  {
        !           417:                    if (!param->first)
        !           418:                      context->ra_time = 0;
        !           419:                    context->flags |= CONTEXT_RA_DONE;
        !           420:                    do_prefix = 1;
        !           421:                  }
        !           422: 
        !           423:                param->first = 0;       
        !           424:                param->found_context = 1;
        !           425:              }
        !           426: 
        !           427:          /* configured time is ceiling */
        !           428:          if (!constructed || valid > time)
        !           429:            valid = time;
        !           430:          
        !           431:          if (flags & IFACE_DEPRECATED)
        !           432:            preferred = 0;
        !           433:          
        !           434:          if (deprecate)
        !           435:            time = 0;
        !           436:          
        !           437:          /* configured time is ceiling */
        !           438:          if (!constructed || preferred > time)
        !           439:            preferred = time;
        !           440:                  
        !           441:          if (preferred > param->pref_time)
        !           442:            {
        !           443:              param->pref_time = preferred;
        !           444:              param->link_global = *local;
        !           445:            }
        !           446:          
        !           447:          if (do_prefix)
        !           448:            {
        !           449:              struct prefix_opt *opt;
        !           450:                      
        !           451:              if ((opt = expand(sizeof(struct prefix_opt))))
        !           452:                {
        !           453:                  /* zero net part of address */
        !           454:                  setaddr6part(local, addr6part(local) & ~((prefix == 64) ? (u64)-1LL : (1LLU << (128 - prefix)) - 1LLU));
        !           455:                  
        !           456:                  opt->type = ICMP6_OPT_PREFIX;
        !           457:                  opt->len = 4;
        !           458:                  opt->prefix_len = prefix;
        !           459:                  /* autonomous only if we're not doing dhcp, always set "on-link" */
        !           460:                  opt->flags = do_slaac ? 0xC0 : 0x80;
        !           461:                  opt->valid_lifetime = htonl(valid);
        !           462:                  opt->preferred_lifetime = htonl(preferred);
        !           463:                  opt->reserved = 0; 
        !           464:                  opt->prefix = *local;
        !           465:                  
        !           466:                  inet_ntop(AF_INET6, local, daemon->addrbuff, ADDRSTRLEN);
        !           467:                  my_syslog(MS_DHCP | LOG_INFO, "RTR-ADVERT(%s) %s", param->if_name, daemon->addrbuff);                     
        !           468:                }
        !           469:            }
        !           470:        }
        !           471:     }          
        !           472:   return 1;
        !           473: }
        !           474: 
        !           475: static int add_lla(int index, unsigned int type, char *mac, size_t maclen, void *parm)
        !           476: {
        !           477:   (void)type;
        !           478: 
        !           479:   if (index == *((int *)parm))
        !           480:     {
        !           481:       /* size is in units of 8 octets and includes type and length (2 bytes)
        !           482:         add 7 to round up */
        !           483:       int len = (maclen + 9) >> 3;
        !           484:       unsigned char *p = expand(len << 3);
        !           485:       memset(p, 0, len << 3);
        !           486:       *p++ = ICMP6_OPT_SOURCE_MAC;
        !           487:       *p++ = len;
        !           488:       memcpy(p, mac, maclen);
        !           489: 
        !           490:       return 0;
        !           491:     }
        !           492: 
        !           493:   return 1;
        !           494: }
        !           495: 
        !           496: time_t periodic_ra(time_t now)
        !           497: {
        !           498:   struct search_param param;
        !           499:   struct dhcp_context *context;
        !           500:   time_t next_event;
        !           501:   char interface[IF_NAMESIZE+1];
        !           502:   
        !           503:   param.now = now;
        !           504:   param.iface = 0;
        !           505: 
        !           506:   while (1)
        !           507:     {
        !           508:       /* find overdue events, and time of first future event */
        !           509:       for (next_event = 0, context = daemon->dhcp6; context; context = context->next)
        !           510:        if (context->ra_time != 0)
        !           511:          {
        !           512:            if (difftime(context->ra_time, now) <= 0.0)
        !           513:              break; /* overdue */
        !           514:            
        !           515:            if (next_event == 0 || difftime(next_event, context->ra_time) > 0.0)
        !           516:              next_event = context->ra_time;
        !           517:          }
        !           518:       
        !           519:       /* none overdue */
        !           520:       if (!context)
        !           521:        break;
        !           522:       
        !           523:       /* There's a context overdue, but we can't find an interface
        !           524:         associated with it, because it's for a subnet we dont 
        !           525:         have an interface on. Probably we're doing DHCP on
        !           526:         a remote subnet via a relay. Zero the timer, since we won't
        !           527:         ever be able to send ra's and satistfy it. */
        !           528:       if (iface_enumerate(AF_INET6, &param, iface_search))
        !           529:        context->ra_time = 0;
        !           530:       else if (param.iface != 0 &&
        !           531:               indextoname(daemon->icmp6fd, param.iface, interface) &&
        !           532:               iface_check(AF_LOCAL, NULL, interface, NULL))
        !           533:        {
        !           534:          struct iname *tmp;
        !           535:          for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
        !           536:            if (tmp->name && wildcard_match(tmp->name, interface))
        !           537:              break;
        !           538:          if (!tmp)
        !           539:            send_ra(now, param.iface, interface, NULL); 
        !           540:        }
        !           541:     }      
        !           542:   return next_event;
        !           543: }
        !           544:   
        !           545: static int iface_search(struct in6_addr *local,  int prefix,
        !           546:                        int scope, int if_index, int flags, 
        !           547:                        int preferred, int valid, void *vparam)
        !           548: {
        !           549:   struct search_param *param = vparam;
        !           550:   struct dhcp_context *context;
        !           551: 
        !           552:   (void)scope;
        !           553:   (void)preferred;
        !           554:   (void)valid;
        !           555:  
        !           556:   for (context = daemon->dhcp6; context; context = context->next)
        !           557:     if (!(context->flags & CONTEXT_TEMPLATE) &&
        !           558:        prefix == context->prefix &&
        !           559:        is_same_net6(local, &context->start6, prefix) &&
        !           560:        is_same_net6(local, &context->end6, prefix) &&
        !           561:        context->ra_time != 0 && 
        !           562:        difftime(context->ra_time, param->now) <= 0.0)
        !           563:       {
        !           564:        /* found an interface that's overdue for RA determine new 
        !           565:           timeout value and arrange for RA to be sent unless interface is
        !           566:           still doing DAD.*/
        !           567:        
        !           568:        if (!(flags & IFACE_TENTATIVE))
        !           569:          param->iface = if_index;
        !           570:        
        !           571:        if (difftime(param->now, context->ra_short_period_start) < 60.0)
        !           572:          /* range 5 - 20 */
        !           573:          context->ra_time = param->now + 5 + (rand16()/4400);
        !           574:        else
        !           575:          /* range 3/4 - 1 times RA_INTERVAL */
        !           576:          context->ra_time = param->now + (3 * RA_INTERVAL)/4 + ((RA_INTERVAL * (unsigned int)rand16()) >> 18);
        !           577:        
        !           578:        /* zero timers for other contexts on the same subnet, so they don't timeout 
        !           579:           independently */
        !           580:        for (context = context->next; context; context = context->next)
        !           581:          if (prefix == context->prefix &&
        !           582:              is_same_net6(local, &context->start6, prefix) &&
        !           583:              is_same_net6(local, &context->end6, prefix))
        !           584:            context->ra_time = 0;
        !           585:        
        !           586:        return 0; /* found, abort */
        !           587:       }
        !           588:   
        !           589:   return 1; /* keep searching */
        !           590: }
        !           591: 
        !           592: 
        !           593: #endif

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