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

1.1.1.2 ! misho       1: /* dnsmasq is Copyright (c) 2000-2014 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: 
                     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;
1.1.1.2 ! misho      34:   struct in6_addr link_local, link_global, ula;
        !            35:   unsigned int glob_pref_time, link_pref_time, ula_pref_time, adv_interval;
1.1       misho      36: };
                     37: 
                     38: struct search_param {
                     39:   time_t now; int iface;
1.1.1.2 ! misho      40:   char name[IF_NAMESIZE+1];
1.1       misho      41: };
                     42: 
                     43: static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *dest);
                     44: static int add_prefixes(struct in6_addr *local,  int prefix,
                     45:                        int scope, int if_index, int flags, 
                     46:                        unsigned int preferred, unsigned int valid, void *vparam);
                     47: static int iface_search(struct in6_addr *local,  int prefix,
                     48:                        int scope, int if_index, int flags, 
                     49:                        int prefered, int valid, void *vparam);
                     50: static int add_lla(int index, unsigned int type, char *mac, size_t maclen, void *parm);
1.1.1.2 ! misho      51: static void new_timeout(struct dhcp_context *context, char *iface_name, time_t now);
        !            52: static unsigned int calc_lifetime(struct ra_interface *ra);
        !            53: static unsigned int calc_interval(struct ra_interface *ra);
        !            54: static unsigned int calc_prio(struct ra_interface *ra);
        !            55: static struct ra_interface *find_iface_param(char *iface);
1.1       misho      56: 
                     57: static int hop_limit;
                     58: 
                     59: void ra_init(time_t now)
                     60: {
                     61:   struct icmp6_filter filter;
                     62:   int fd;
                     63: #if defined(IPV6_TCLASS) && defined(IPTOS_CLASS_CS6)
                     64:   int class = IPTOS_CLASS_CS6;
                     65: #endif
                     66:   int val = 255; /* radvd uses this value */
                     67:   socklen_t len = sizeof(int);
                     68:   struct dhcp_context *context;
                     69:   
                     70:   /* ensure this is around even if we're not doing DHCPv6 */
                     71:   expand_buf(&daemon->outpacket, sizeof(struct dhcp_packet));
                     72:  
                     73:   /* See if we're guessing SLAAC addresses, if so we need to recieve ping replies */
                     74:   for (context = daemon->dhcp6; context; context = context->next)
                     75:     if ((context->flags & CONTEXT_RA_NAME))
                     76:       break;
                     77:   
1.1.1.2 ! misho      78:   /* Need ICMP6 socket for transmission for DHCPv6 even when not doing RA. */
        !            79: 
1.1       misho      80:   ICMP6_FILTER_SETBLOCKALL(&filter);
1.1.1.2 ! misho      81:   if (daemon->doing_ra)
        !            82:     {
        !            83:       ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &filter);
        !            84:       if (context)
        !            85:        ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &filter);
        !            86:     }
1.1       misho      87:   
                     88:   if ((fd = socket(PF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) == -1 ||
                     89:       getsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &hop_limit, &len) ||
                     90: #if defined(IPV6_TCLASS) && defined(IPTOS_CLASS_CS6)
                     91:       setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &class, sizeof(class)) == -1 ||
                     92: #endif
                     93:       !fix_fd(fd) ||
                     94:       !set_ipv6pktinfo(fd) ||
                     95:       setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &val, sizeof(val)) ||
                     96:       setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &val, sizeof(val)) ||
                     97:       setsockopt(fd, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, sizeof(filter)) == -1)
                     98:     die (_("cannot create ICMPv6 socket: %s"), NULL, EC_BADNET);
                     99:   
                    100:    daemon->icmp6fd = fd;
                    101:    
1.1.1.2 ! misho     102:    if (daemon->doing_ra)
        !           103:      ra_start_unsolicted(now, NULL);
1.1       misho     104: }
                    105: 
                    106: void ra_start_unsolicted(time_t now, struct dhcp_context *context)
                    107: {   
                    108:    /* init timers so that we do ra's for some/all soon. some ra_times will end up zeroed
                    109:      if it's not appropriate to advertise those contexts.
                    110:      This gets re-called on a netlink route-change to re-do the advertisement
                    111:      and pick up new interfaces */
                    112:   
                    113:   if (context)
                    114:     context->ra_short_period_start = context->ra_time = now;
                    115:   else
                    116:     for (context = daemon->dhcp6; context; context = context->next)
                    117:       if (!(context->flags & CONTEXT_TEMPLATE))
                    118:        {
                    119:          context->ra_time = now + (rand16()/13000); /* range 0 - 5 */
                    120:          /* re-do frequently for a minute or so, in case the first gets lost. */
                    121:          context->ra_short_period_start = now;
                    122:        }
                    123: }
                    124: 
                    125: void icmp6_packet(time_t now)
                    126: {
                    127:   char interface[IF_NAMESIZE+1];
                    128:   ssize_t sz; 
                    129:   int if_index = 0;
                    130:   struct cmsghdr *cmptr;
                    131:   struct msghdr msg;
                    132:   union {
                    133:     struct cmsghdr align; /* this ensures alignment */
                    134:     char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
                    135:   } control_u;
                    136:   struct sockaddr_in6 from;
                    137:   unsigned char *packet;
                    138:   struct iname *tmp;
                    139: 
                    140:   /* Note: use outpacket for input buffer */
                    141:   msg.msg_control = control_u.control6;
                    142:   msg.msg_controllen = sizeof(control_u);
                    143:   msg.msg_flags = 0;
                    144:   msg.msg_name = &from;
                    145:   msg.msg_namelen = sizeof(from);
                    146:   msg.msg_iov = &daemon->outpacket;
                    147:   msg.msg_iovlen = 1;
                    148:   
                    149:   if ((sz = recv_dhcp_packet(daemon->icmp6fd, &msg)) == -1 || sz < 8)
                    150:     return;
                    151:    
                    152:   packet = (unsigned char *)daemon->outpacket.iov_base;
                    153:   
                    154:   for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
                    155:     if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo)
                    156:       {
                    157:        union {
                    158:          unsigned char *c;
                    159:          struct in6_pktinfo *p;
                    160:        } p;
                    161:        p.c = CMSG_DATA(cmptr);
                    162:         
                    163:        if_index = p.p->ipi6_ifindex;
                    164:       }
                    165:   
                    166:   if (!indextoname(daemon->icmp6fd, if_index, interface))
                    167:     return;
                    168:     
                    169:   if (!iface_check(AF_LOCAL, NULL, interface, NULL))
                    170:     return;
                    171:   
                    172:   for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
                    173:     if (tmp->name && wildcard_match(tmp->name, interface))
                    174:       return;
                    175:  
                    176:   if (packet[1] != 0)
                    177:     return;
                    178:   
                    179:   if (packet[0] == ICMP6_ECHO_REPLY)
                    180:     lease_ping_reply(&from.sin6_addr, packet, interface); 
                    181:   else if (packet[0] == ND_ROUTER_SOLICIT)
                    182:     {
                    183:       char *mac = "";
                    184:       
                    185:       /* look for link-layer address option for logging */
                    186:       if (sz >= 16 && packet[8] == ICMP6_OPT_SOURCE_MAC && (packet[9] * 8) + 8 <= sz)
                    187:        {
                    188:          print_mac(daemon->namebuff, &packet[10], (packet[9] * 8) - 2);
                    189:          mac = daemon->namebuff;
                    190:        }
                    191:          
1.1.1.2 ! misho     192:       if (!option_bool(OPT_QUIET_RA))
        !           193:        my_syslog(MS_DHCP | LOG_INFO, "RTR-SOLICIT(%s) %s", interface, mac);
1.1       misho     194:       /* source address may not be valid in solicit request. */
                    195:       send_ra(now, if_index, interface, !IN6_IS_ADDR_UNSPECIFIED(&from.sin6_addr) ? &from.sin6_addr : NULL);
                    196:     }
                    197: }
                    198: 
                    199: static void send_ra(time_t now, int iface, char *iface_name, struct in6_addr *dest)
                    200: {
                    201:   struct ra_packet *ra;
                    202:   struct ra_param parm;
                    203:   struct sockaddr_in6 addr;
1.1.1.2 ! misho     204:   struct dhcp_context *context, *tmp,  **up;
1.1       misho     205:   struct dhcp_netid iface_id;
                    206:   struct dhcp_opt *opt_cfg;
1.1.1.2 ! misho     207:   struct ra_interface *ra_param = find_iface_param(iface_name);
        !           208:   int done_dns = 0, old_prefix = 0;
        !           209:   unsigned int min_pref_time;
1.1       misho     210: #ifdef HAVE_LINUX_NETWORK
                    211:   FILE *f;
                    212: #endif
1.1.1.2 ! misho     213:  
1.1       misho     214:   save_counter(0);
                    215:   ra = expand(sizeof(struct ra_packet));
                    216:   
                    217:   ra->type = ND_ROUTER_ADVERT;
                    218:   ra->code = 0;
                    219:   ra->hop_limit = hop_limit;
1.1.1.2 ! misho     220:   ra->flags = calc_prio(ra_param);
        !           221:   ra->lifetime = htons(calc_lifetime(ra_param));
1.1       misho     222:   ra->reachable_time = 0;
                    223:   ra->retrans_time = 0;
                    224: 
                    225:   parm.ind = iface;
                    226:   parm.managed = 0;
                    227:   parm.other = 0;
                    228:   parm.found_context = 0;
                    229:   parm.if_name = iface_name;
                    230:   parm.first = 1;
                    231:   parm.now = now;
1.1.1.2 ! misho     232:   parm.glob_pref_time = parm.link_pref_time = parm.ula_pref_time = 0;
        !           233:   parm.adv_interval = calc_interval(ra_param);
1.1       misho     234:   
                    235:   /* set tag with name == interface */
                    236:   iface_id.net = iface_name;
                    237:   iface_id.next = NULL;
                    238:   parm.tags = &iface_id; 
                    239:   
                    240:   for (context = daemon->dhcp6; context; context = context->next)
                    241:     {
                    242:       context->flags &= ~CONTEXT_RA_DONE;
                    243:       context->netid.next = &context->netid;
                    244:     }
                    245: 
1.1.1.2 ! misho     246:   if (!iface_enumerate(AF_INET6, &parm, add_prefixes))
1.1       misho     247:     return;
                    248: 
1.1.1.2 ! misho     249:   /* Find smallest preferred time within address classes,
        !           250:      to use as lifetime for options. This is a rather arbitrary choice. */
        !           251:   min_pref_time = 0xffffffff;
        !           252:   if (parm.glob_pref_time != 0 && parm.glob_pref_time < min_pref_time)
        !           253:     min_pref_time = parm.glob_pref_time;
        !           254:   
        !           255:   if (parm.ula_pref_time != 0 && parm.ula_pref_time < min_pref_time)
        !           256:     min_pref_time = parm.ula_pref_time;
        !           257: 
        !           258:   if (parm.link_pref_time != 0 && parm.link_pref_time < min_pref_time)
        !           259:     min_pref_time = parm.link_pref_time;
        !           260: 
        !           261:   /* Look for constructed contexts associated with addresses which have gone, 
        !           262:      and advertise them with preferred_time == 0  RFC 6204 4.3 L-13 */
        !           263:   for (up = &daemon->dhcp6, context = daemon->dhcp6; context; context = tmp)
        !           264:     {
        !           265:       tmp = context->next;
1.1       misho     266: 
1.1.1.2 ! misho     267:       if (context->if_index == iface && (context->flags & CONTEXT_OLD))
        !           268:        {
        !           269:          unsigned int old = difftime(now, context->address_lost_time);
        !           270:          
        !           271:          if (old > context->saved_valid)
        !           272:            {
        !           273:              /* We've advertised this enough, time to go */
        !           274:              *up = context->next;
        !           275:              free(context);
        !           276:            }
        !           277:          else
        !           278:            {
        !           279:              struct prefix_opt *opt;
        !           280:              struct in6_addr local = context->start6;
        !           281:              int do_slaac = 0;
        !           282: 
        !           283:              old_prefix = 1;
        !           284: 
        !           285:              /* zero net part of address */
        !           286:              setaddr6part(&local, addr6part(&local) & ~((context->prefix == 64) ? (u64)-1LL : (1LLU << (128 - context->prefix)) - 1LLU));
        !           287:             
        !           288:              
        !           289:              if ((context->flags & 
        !           290:                   (CONTEXT_RA_ONLY | CONTEXT_RA_NAME | CONTEXT_RA_STATELESS)))
        !           291:                {
        !           292:                  do_slaac = 1;
        !           293:                  if (context->flags & CONTEXT_DHCP)
        !           294:                    {
        !           295:                      parm.other = 1; 
        !           296:                      if (!(context->flags & CONTEXT_RA_STATELESS))
        !           297:                        parm.managed = 1;
        !           298:                    }
        !           299:                }
        !           300:              else
        !           301:                {
        !           302:                  /* don't do RA for non-ra-only unless --enable-ra is set */
        !           303:                  if (option_bool(OPT_RA))
        !           304:                    {
        !           305:                      parm.managed = 1;
        !           306:                      parm.other = 1;
        !           307:                    }
        !           308:                }
        !           309: 
        !           310:              if ((opt = expand(sizeof(struct prefix_opt))))
        !           311:                {
        !           312:                  opt->type = ICMP6_OPT_PREFIX;
        !           313:                  opt->len = 4;
        !           314:                  opt->prefix_len = context->prefix;
        !           315:                  /* autonomous only if we're not doing dhcp, always set "on-link" */
        !           316:                  opt->flags = do_slaac ? 0xC0 : 0x80;
        !           317:                  opt->valid_lifetime = htonl(context->saved_valid - old);
        !           318:                  opt->preferred_lifetime = htonl(0);
        !           319:                  opt->reserved = 0; 
        !           320:                  opt->prefix = local;
        !           321:                  
        !           322:                  inet_ntop(AF_INET6, &local, daemon->addrbuff, ADDRSTRLEN);
        !           323:                  if (!option_bool(OPT_QUIET_RA))
        !           324:                    my_syslog(MS_DHCP | LOG_INFO, "RTR-ADVERT(%s) %s old prefix", iface_name, daemon->addrbuff);                    
        !           325:                }
        !           326:           
        !           327:              up = &context->next;
        !           328:            }
        !           329:        }
        !           330:       else
        !           331:        up = &context->next;
        !           332:     }
        !           333:     
        !           334:   /* If we're advertising only old prefixes, set router lifetime to zero. */
        !           335:   if (old_prefix && !parm.found_context)
        !           336:     ra->lifetime = htons(0);
        !           337: 
        !           338:   /* No prefixes to advertise. */
        !           339:   if (!old_prefix && !parm.found_context)
        !           340:     return; 
        !           341:   
1.1       misho     342: #ifdef HAVE_LINUX_NETWORK
                    343:   /* Note that IPv6 MTU is not necessarilly the same as the IPv4 MTU
                    344:      available from SIOCGIFMTU */
                    345:   sprintf(daemon->namebuff, "/proc/sys/net/ipv6/conf/%s/mtu", iface_name);
                    346:   if ((f = fopen(daemon->namebuff, "r")))
                    347:     {
                    348:       if (fgets(daemon->namebuff, MAXDNAME, f))
                    349:        {
                    350:          put_opt6_char(ICMP6_OPT_MTU);
                    351:          put_opt6_char(1);
                    352:          put_opt6_short(0);
                    353:          put_opt6_long(atoi(daemon->namebuff));
                    354:        }
                    355:       fclose(f);
                    356:     }
                    357: #endif
                    358:      
                    359:   iface_enumerate(AF_LOCAL, &iface, add_lla);
                    360:  
                    361:   /* RDNSS, RFC 6106, use relevant DHCP6 options */
                    362:   (void)option_filter(parm.tags, NULL, daemon->dhcp_opts6);
                    363:   
                    364:   for (opt_cfg = daemon->dhcp_opts6; opt_cfg; opt_cfg = opt_cfg->next)
                    365:     {
                    366:       int i;
                    367:       
                    368:       /* netids match and not encapsulated? */
                    369:       if (!(opt_cfg->flags & DHOPT_TAGOK))
                    370:         continue;
                    371:       
                    372:       if (opt_cfg->opt == OPTION6_DNS_SERVER)
                    373:         {
1.1.1.2 ! misho     374:          struct in6_addr *a;
        !           375:          int len;
1.1       misho     376: 
                    377:          done_dns = 1;
1.1.1.2 ! misho     378: 
1.1       misho     379:           if (opt_cfg->len == 0)
1.1.1.2 ! misho     380:            continue;
1.1       misho     381:          
1.1.1.2 ! misho     382:          /* reduce len for any addresses we can't substitute */
        !           383:          for (a = (struct in6_addr *)opt_cfg->val, len = opt_cfg->len, i = 0; 
        !           384:               i < opt_cfg->len; i += IN6ADDRSZ, a++)
        !           385:            if ((IN6_IS_ADDR_UNSPECIFIED(a) && parm.glob_pref_time == 0) ||
        !           386:                (IN6_IS_ADDR_ULA_ZERO(a) && parm.ula_pref_time == 0) ||
        !           387:                (IN6_IS_ADDR_LINK_LOCAL_ZERO(a) && parm.link_pref_time == 0))
        !           388:              len -= IN6ADDRSZ;
        !           389: 
        !           390:          if (len != 0)
        !           391:            {
        !           392:              put_opt6_char(ICMP6_OPT_RDNSS);
        !           393:              put_opt6_char((len/8) + 1);
        !           394:              put_opt6_short(0);
        !           395:              put_opt6_long(min_pref_time);
        !           396:         
        !           397:              for (a = (struct in6_addr *)opt_cfg->val, i = 0; i <  opt_cfg->len; i += IN6ADDRSZ, a++)
        !           398:                if (IN6_IS_ADDR_UNSPECIFIED(a))
        !           399:                  {
        !           400:                    if (parm.glob_pref_time != 0)
        !           401:                      put_opt6(&parm.link_global, IN6ADDRSZ);
        !           402:                  }
        !           403:                else if (IN6_IS_ADDR_ULA_ZERO(a))
        !           404:                  {
        !           405:                    if (parm.ula_pref_time != 0)
        !           406:                    put_opt6(&parm.ula, IN6ADDRSZ);
        !           407:                  }
        !           408:                else if (IN6_IS_ADDR_LINK_LOCAL_ZERO(a))
        !           409:                  {
        !           410:                    if (parm.link_pref_time != 0)
        !           411:                      put_opt6(&parm.link_local, IN6ADDRSZ);
        !           412:                  }
        !           413:                else
        !           414:                  put_opt6(a, IN6ADDRSZ);
        !           415:            }
1.1       misho     416:        }
                    417:       
                    418:       if (opt_cfg->opt == OPTION6_DOMAIN_SEARCH && opt_cfg->len != 0)
                    419:        {
                    420:          int len = ((opt_cfg->len+7)/8);
                    421:          
                    422:          put_opt6_char(ICMP6_OPT_DNSSL);
                    423:          put_opt6_char(len + 1);
                    424:          put_opt6_short(0);
1.1.1.2 ! misho     425:          put_opt6_long(min_pref_time); 
1.1       misho     426:          put_opt6(opt_cfg->val, opt_cfg->len);
                    427:          
                    428:          /* pad */
                    429:          for (i = opt_cfg->len; i < len * 8; i++)
                    430:            put_opt6_char(0);
                    431:        }
                    432:     }
                    433:        
1.1.1.2 ! misho     434:   if (daemon->port == NAMESERVER_PORT && !done_dns && parm.link_pref_time != 0)
1.1       misho     435:     {
1.1.1.2 ! misho     436:       /* default == us, as long as we are supplying DNS service. */
1.1       misho     437:       put_opt6_char(ICMP6_OPT_RDNSS);
                    438:       put_opt6_char(3);
                    439:       put_opt6_short(0);
1.1.1.2 ! misho     440:       put_opt6_long(min_pref_time); 
        !           441:       put_opt6(&parm.link_local, IN6ADDRSZ);
1.1       misho     442:     }
                    443: 
                    444:   /* set managed bits unless we're providing only RA on this link */
                    445:   if (parm.managed)
                    446:     ra->flags |= 0x80; /* M flag, managed, */
                    447:    if (parm.other)
                    448:     ra->flags |= 0x40; /* O flag, other */ 
                    449:                        
                    450:   /* decide where we're sending */
                    451:   memset(&addr, 0, sizeof(addr));
                    452: #ifdef HAVE_SOCKADDR_SA_LEN
                    453:   addr.sin6_len = sizeof(struct sockaddr_in6);
                    454: #endif
                    455:   addr.sin6_family = AF_INET6;
                    456:   addr.sin6_port = htons(IPPROTO_ICMPV6);
                    457:   if (dest)
                    458:     {
                    459:       addr.sin6_addr = *dest;
                    460:       if (IN6_IS_ADDR_LINKLOCAL(dest) ||
                    461:          IN6_IS_ADDR_MC_LINKLOCAL(dest))
                    462:        addr.sin6_scope_id = iface;
                    463:     }
                    464:   else
1.1.1.2 ! misho     465:     {
        !           466:       inet_pton(AF_INET6, ALL_NODES, &addr.sin6_addr); 
        !           467:       setsockopt(daemon->icmp6fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &iface, sizeof(iface));
        !           468:     }
1.1       misho     469:   
1.1.1.2 ! misho     470:   while (sendto(daemon->icmp6fd, daemon->outpacket.iov_base, save_counter(0), 0, 
        !           471:                (struct sockaddr *)&addr, sizeof(addr)) == -1 && retry_send());
1.1       misho     472:   
                    473: }
                    474: 
                    475: static int add_prefixes(struct in6_addr *local,  int prefix,
                    476:                        int scope, int if_index, int flags, 
                    477:                        unsigned int preferred, unsigned int valid, void *vparam)
                    478: {
                    479:   struct ra_param *param = vparam;
                    480: 
                    481:   (void)scope; /* warning */
                    482:   
                    483:   if (if_index == param->ind)
                    484:     {
                    485:       if (IN6_IS_ADDR_LINKLOCAL(local))
1.1.1.2 ! misho     486:        {
        !           487:          /* Can there be more than one LL address?
        !           488:             Select the one with the longest preferred time 
        !           489:             if there is. */
        !           490:          if (preferred > param->link_pref_time)
        !           491:            {
        !           492:              param->link_pref_time = preferred;
        !           493:              param->link_local = *local;
        !           494:            }
        !           495:        }
1.1       misho     496:       else if (!IN6_IS_ADDR_LOOPBACK(local) &&
                    497:               !IN6_IS_ADDR_MULTICAST(local))
                    498:        {
1.1.1.2 ! misho     499:          int real_prefix = 0;
1.1       misho     500:          int do_slaac = 0;
                    501:          int deprecate  = 0;
                    502:          int constructed = 0;
                    503:          unsigned int time = 0xffffffff;
                    504:          struct dhcp_context *context;
                    505:          
                    506:          for (context = daemon->dhcp6; context; context = context->next)
1.1.1.2 ! misho     507:            if (!(context->flags & (CONTEXT_TEMPLATE | CONTEXT_OLD)) &&
        !           508:                prefix <= context->prefix &&
        !           509:                is_same_net6(local, &context->start6, context->prefix) &&
        !           510:                is_same_net6(local, &context->end6, context->prefix))
1.1       misho     511:              {
1.1.1.2 ! misho     512:                context->saved_valid = valid;
        !           513: 
1.1       misho     514:                if ((context->flags & 
                    515:                     (CONTEXT_RA_ONLY | CONTEXT_RA_NAME | CONTEXT_RA_STATELESS)))
                    516:                  {
                    517:                    do_slaac = 1;
                    518:                    if (context->flags & CONTEXT_DHCP)
                    519:                      {
                    520:                        param->other = 1; 
                    521:                        if (!(context->flags & CONTEXT_RA_STATELESS))
                    522:                          param->managed = 1;
                    523:                      }
                    524:                  }
                    525:                else
                    526:                  {
                    527:                    /* don't do RA for non-ra-only unless --enable-ra is set */
                    528:                    if (!option_bool(OPT_RA))
                    529:                      continue;
                    530:                    param->managed = 1;
                    531:                    param->other = 1;
                    532:                  }
                    533:                
1.1.1.2 ! misho     534:                /* find floor time, don't reduce below 3 * RA interval. */
1.1       misho     535:                if (time > context->lease_time)
                    536:                  {
                    537:                    time = context->lease_time;
1.1.1.2 ! misho     538:                    if (time < ((unsigned int)(3 * param->adv_interval)))
        !           539:                      time = 3 * param->adv_interval;
1.1       misho     540:                  }
                    541: 
                    542:                if (context->flags & CONTEXT_DEPRECATE)
                    543:                  deprecate = 1;
                    544:                
                    545:                if (context->flags & CONTEXT_CONSTRUCTED)
                    546:                  constructed = 1;
                    547: 
                    548: 
                    549:                /* collect dhcp-range tags */
                    550:                if (context->netid.next == &context->netid && context->netid.net)
                    551:                  {
                    552:                    context->netid.next = param->tags;
                    553:                    param->tags = &context->netid;
                    554:                  }
                    555:                  
                    556:                /* subsequent prefixes on the same interface 
                    557:                   and subsequent instances of this prefix don't need timers.
                    558:                   Be careful not to find the same prefix twice with different
                    559:                   addresses. */
                    560:                if (!(context->flags & CONTEXT_RA_DONE))
                    561:                  {
                    562:                    if (!param->first)
                    563:                      context->ra_time = 0;
                    564:                    context->flags |= CONTEXT_RA_DONE;
1.1.1.2 ! misho     565:                    real_prefix = context->prefix;
1.1       misho     566:                  }
                    567: 
                    568:                param->first = 0;       
                    569:                param->found_context = 1;
                    570:              }
                    571: 
                    572:          /* configured time is ceiling */
                    573:          if (!constructed || valid > time)
                    574:            valid = time;
                    575:          
                    576:          if (flags & IFACE_DEPRECATED)
                    577:            preferred = 0;
                    578:          
                    579:          if (deprecate)
                    580:            time = 0;
                    581:          
                    582:          /* configured time is ceiling */
                    583:          if (!constructed || preferred > time)
                    584:            preferred = time;
1.1.1.2 ! misho     585:          
        !           586:          if (IN6_IS_ADDR_ULA(local))
1.1       misho     587:            {
1.1.1.2 ! misho     588:              if (preferred > param->ula_pref_time)
        !           589:                {
        !           590:                  param->ula_pref_time = preferred;
        !           591:                  param->ula = *local;
        !           592:                }
        !           593:            }
        !           594:          else 
        !           595:            {
        !           596:              if (preferred > param->glob_pref_time)
        !           597:                {
        !           598:                  param->glob_pref_time = preferred;
        !           599:                  param->link_global = *local;
        !           600:                }
1.1       misho     601:            }
                    602:          
1.1.1.2 ! misho     603:          if (real_prefix != 0)
1.1       misho     604:            {
                    605:              struct prefix_opt *opt;
                    606:                      
                    607:              if ((opt = expand(sizeof(struct prefix_opt))))
                    608:                {
                    609:                  /* zero net part of address */
1.1.1.2 ! misho     610:                  setaddr6part(local, addr6part(local) & ~((real_prefix == 64) ? (u64)-1LL : (1LLU << (128 - real_prefix)) - 1LLU));
1.1       misho     611:                  
                    612:                  opt->type = ICMP6_OPT_PREFIX;
                    613:                  opt->len = 4;
1.1.1.2 ! misho     614:                  opt->prefix_len = real_prefix;
1.1       misho     615:                  /* autonomous only if we're not doing dhcp, always set "on-link" */
                    616:                  opt->flags = do_slaac ? 0xC0 : 0x80;
                    617:                  opt->valid_lifetime = htonl(valid);
                    618:                  opt->preferred_lifetime = htonl(preferred);
                    619:                  opt->reserved = 0; 
                    620:                  opt->prefix = *local;
                    621:                  
                    622:                  inet_ntop(AF_INET6, local, daemon->addrbuff, ADDRSTRLEN);
1.1.1.2 ! misho     623:                  if (!option_bool(OPT_QUIET_RA))
        !           624:                    my_syslog(MS_DHCP | LOG_INFO, "RTR-ADVERT(%s) %s", param->if_name, daemon->addrbuff);                   
1.1       misho     625:                }
                    626:            }
                    627:        }
                    628:     }          
                    629:   return 1;
                    630: }
                    631: 
                    632: static int add_lla(int index, unsigned int type, char *mac, size_t maclen, void *parm)
                    633: {
                    634:   (void)type;
                    635: 
                    636:   if (index == *((int *)parm))
                    637:     {
                    638:       /* size is in units of 8 octets and includes type and length (2 bytes)
                    639:         add 7 to round up */
                    640:       int len = (maclen + 9) >> 3;
                    641:       unsigned char *p = expand(len << 3);
                    642:       memset(p, 0, len << 3);
                    643:       *p++ = ICMP6_OPT_SOURCE_MAC;
                    644:       *p++ = len;
                    645:       memcpy(p, mac, maclen);
                    646: 
                    647:       return 0;
                    648:     }
                    649: 
                    650:   return 1;
                    651: }
                    652: 
                    653: time_t periodic_ra(time_t now)
                    654: {
                    655:   struct search_param param;
                    656:   struct dhcp_context *context;
                    657:   time_t next_event;
1.1.1.2 ! misho     658:     
1.1       misho     659:   param.now = now;
                    660:   param.iface = 0;
                    661: 
                    662:   while (1)
                    663:     {
                    664:       /* find overdue events, and time of first future event */
                    665:       for (next_event = 0, context = daemon->dhcp6; context; context = context->next)
                    666:        if (context->ra_time != 0)
                    667:          {
                    668:            if (difftime(context->ra_time, now) <= 0.0)
                    669:              break; /* overdue */
                    670:            
                    671:            if (next_event == 0 || difftime(next_event, context->ra_time) > 0.0)
                    672:              next_event = context->ra_time;
                    673:          }
                    674:       
                    675:       /* none overdue */
                    676:       if (!context)
                    677:        break;
                    678:       
1.1.1.2 ! misho     679:       if ((context->flags & CONTEXT_OLD) && 
        !           680:          context->if_index != 0 && 
        !           681:          indextoname(daemon->icmp6fd, context->if_index, param.name))
        !           682:        {
        !           683:          /* A context for an old address. We'll not find the interface by 
        !           684:             looking for addresses, but we know it anyway, since the context is
        !           685:             constructed */
        !           686:          param.iface = context->if_index;
        !           687:          new_timeout(context, param.name, now);
        !           688:        }
        !           689:       else if (iface_enumerate(AF_INET6, &param, iface_search))
        !           690:        /* There's a context overdue, but we can't find an interface
        !           691:           associated with it, because it's for a subnet we dont 
        !           692:           have an interface on. Probably we're doing DHCP on
        !           693:           a remote subnet via a relay. Zero the timer, since we won't
        !           694:           ever be able to send ra's and satistfy it. */
1.1       misho     695:        context->ra_time = 0;
1.1.1.2 ! misho     696:       
        !           697:       if (param.iface != 0 &&
        !           698:          iface_check(AF_LOCAL, NULL, param.name, NULL))
1.1       misho     699:        {
                    700:          struct iname *tmp;
                    701:          for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
1.1.1.2 ! misho     702:            if (tmp->name && wildcard_match(tmp->name, param.name))
1.1       misho     703:              break;
                    704:          if (!tmp)
1.1.1.2 ! misho     705:            send_ra(now, param.iface, param.name, NULL); 
1.1       misho     706:        }
                    707:     }      
                    708:   return next_event;
                    709: }
                    710:   
                    711: static int iface_search(struct in6_addr *local,  int prefix,
                    712:                        int scope, int if_index, int flags, 
                    713:                        int preferred, int valid, void *vparam)
                    714: {
                    715:   struct search_param *param = vparam;
                    716:   struct dhcp_context *context;
                    717: 
                    718:   (void)scope;
                    719:   (void)preferred;
                    720:   (void)valid;
                    721:  
                    722:   for (context = daemon->dhcp6; context; context = context->next)
1.1.1.2 ! misho     723:     if (!(context->flags & (CONTEXT_TEMPLATE | CONTEXT_OLD)) &&
        !           724:        prefix <= context->prefix &&
        !           725:        is_same_net6(local, &context->start6, context->prefix) &&
        !           726:        is_same_net6(local, &context->end6, context->prefix) &&
1.1       misho     727:        context->ra_time != 0 && 
                    728:        difftime(context->ra_time, param->now) <= 0.0)
                    729:       {
                    730:        /* found an interface that's overdue for RA determine new 
                    731:           timeout value and arrange for RA to be sent unless interface is
                    732:           still doing DAD.*/
                    733:        
                    734:        if (!(flags & IFACE_TENTATIVE))
                    735:          param->iface = if_index;
                    736:        
1.1.1.2 ! misho     737:        /* should never fail */
        !           738:        if (!indextoname(daemon->icmp6fd, if_index, param->name))
        !           739:          {
        !           740:            param->iface = 0;
        !           741:            return 0;
        !           742:          }
        !           743:        
        !           744:        new_timeout(context, param->name, param->now);
1.1       misho     745:        
                    746:        /* zero timers for other contexts on the same subnet, so they don't timeout 
                    747:           independently */
                    748:        for (context = context->next; context; context = context->next)
1.1.1.2 ! misho     749:          if (prefix <= context->prefix &&
        !           750:              is_same_net6(local, &context->start6, context->prefix) &&
        !           751:              is_same_net6(local, &context->end6, context->prefix))
1.1       misho     752:            context->ra_time = 0;
                    753:        
                    754:        return 0; /* found, abort */
                    755:       }
                    756:   
                    757:   return 1; /* keep searching */
                    758: }
1.1.1.2 ! misho     759:  
        !           760: static void new_timeout(struct dhcp_context *context, char *iface_name, time_t now)
        !           761: {
        !           762:   if (difftime(now, context->ra_short_period_start) < 60.0)
        !           763:     /* range 5 - 20 */
        !           764:     context->ra_time = now + 5 + (rand16()/4400);
        !           765:   else
        !           766:     {
        !           767:       /* range 3/4 - 1 times MaxRtrAdvInterval */
        !           768:       unsigned int adv_interval = calc_interval(find_iface_param(iface_name));
        !           769:       context->ra_time = now + (3 * adv_interval)/4 + ((adv_interval * (unsigned int)rand16()) >> 18);
        !           770:     }
        !           771: }
        !           772: 
        !           773: static struct ra_interface *find_iface_param(char *iface)
        !           774: {
        !           775:   struct ra_interface *ra;
        !           776:   
        !           777:   for (ra = daemon->ra_interfaces; ra; ra = ra->next)
        !           778:     if (wildcard_match(ra->name, iface))
        !           779:       return ra;
        !           780: 
        !           781:   return NULL;
        !           782: }
        !           783: 
        !           784: static unsigned int calc_interval(struct ra_interface *ra)
        !           785: {
        !           786:   int interval = 600;
        !           787:   
        !           788:   if (ra && ra->interval != 0)
        !           789:     {
        !           790:       interval = ra->interval;
        !           791:       if (interval > 1800)
        !           792:        interval = 1800;
        !           793:       else if (interval < 4)
        !           794:        interval = 4;
        !           795:     }
        !           796:   
        !           797:   return (unsigned int)interval;
        !           798: }
        !           799: 
        !           800: static unsigned int calc_lifetime(struct ra_interface *ra)
        !           801: {
        !           802:   int lifetime, interval = (int)calc_interval(ra);
        !           803:   
        !           804:   if (!ra || ra->lifetime == -1) /* not specified */
        !           805:     lifetime = 3 * interval;
        !           806:   else
        !           807:     {
        !           808:       lifetime = ra->lifetime;
        !           809:       if (lifetime < interval && lifetime != 0)
        !           810:        lifetime = interval;
        !           811:       else if (lifetime > 9000)
        !           812:        lifetime = 9000;
        !           813:     }
        !           814:   
        !           815:   return (unsigned int)lifetime;
        !           816: }
1.1       misho     817: 
1.1.1.2 ! misho     818: static unsigned int calc_prio(struct ra_interface *ra)
        !           819: {
        !           820:   if (ra)
        !           821:     return ra->prio;
        !           822:   
        !           823:   return 0;
        !           824: }
1.1       misho     825: 
                    826: #endif

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