File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / dnsmasq / src / slaac.c
Revision 1.1: download - view: text, annotated - select for diffs - revision graph
Mon Jul 29 19:37:40 2013 UTC (10 years, 11 months ago) by misho
CVS tags: MAIN, HEAD
Initial revision

    1: /* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
    2: 
    3:    This program is free software; you can redistribute it and/or modify
    4:    it under the terms of the GNU General Public License as published by
    5:    the Free Software Foundation; version 2 dated June, 1991, or
    6:    (at your option) version 3 dated 29 June, 2007.
    7:  
    8:    This program is distributed in the hope that it will be useful,
    9:    but WITHOUT ANY WARRANTY; without even the implied warranty of
   10:    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   11:    GNU General Public License for more details.
   12:      
   13:    You should have received a copy of the GNU General Public License
   14:    along with this program.  If not, see <http://www.gnu.org/licenses/>.
   15: */
   16: 
   17: #include "dnsmasq.h"
   18: 
   19: #ifdef HAVE_DHCP6
   20: 
   21: #include <netinet/icmp6.h>
   22: 
   23: static int ping_id = 0;
   24: 
   25: void slaac_add_addrs(struct dhcp_lease *lease, time_t now, int force)
   26: {
   27:   struct slaac_address *slaac, *old, **up;
   28:   struct dhcp_context *context;
   29:   int dns_dirty = 0;
   30:   
   31:   if (!(lease->flags & LEASE_HAVE_HWADDR) || 
   32:       (lease->flags & (LEASE_TA | LEASE_NA)) ||
   33:       lease->last_interface == 0 ||
   34:       !lease->hostname)
   35:     return ;
   36:   
   37:   old = lease->slaac_address;
   38:   lease->slaac_address = NULL;
   39: 
   40:   for (context = daemon->dhcp6; context; context = context->next) 
   41:     if ((context->flags & CONTEXT_RA_NAME) && lease->last_interface == context->if_index)
   42:       {
   43: 	struct in6_addr addr = context->start6;
   44: 	if (lease->hwaddr_len == 6 &&
   45: 	    (lease->hwaddr_type == ARPHRD_ETHER || lease->hwaddr_type == ARPHRD_IEEE802))
   46: 	  {
   47: 	    /* convert MAC address to EUI-64 */
   48: 	    memcpy(&addr.s6_addr[8], lease->hwaddr, 3);
   49: 	    memcpy(&addr.s6_addr[13], &lease->hwaddr[3], 3);
   50: 	    addr.s6_addr[11] = 0xff;
   51: 	    addr.s6_addr[12] = 0xfe;
   52: 	  }
   53: #if defined(ARPHRD_EUI64)
   54: 	else if (lease->hwaddr_len == 8 &&
   55: 		 lease->hwaddr_type == ARPHRD_EUI64)
   56: 	  memcpy(&addr.s6_addr[8], lease->hwaddr, 8);
   57: #endif
   58: #if defined(ARPHRD_IEEE1394) && defined(ARPHRD_EUI64)
   59: 	else if (lease->clid_len == 9 && 
   60: 		 lease->clid[0] ==  ARPHRD_EUI64 &&
   61: 		 lease->hwaddr_type == ARPHRD_IEEE1394)
   62: 	  /* firewire has EUI-64 identifier as clid */
   63: 	  memcpy(&addr.s6_addr[8], &lease->clid[1], 8);
   64: #endif
   65: 	else
   66: 	  continue;
   67: 	
   68: 	addr.s6_addr[8] ^= 0x02;
   69: 	
   70: 	/* check if we already have this one */
   71: 	for (up = &old, slaac = old; slaac; slaac = slaac->next)
   72: 	  {
   73: 	    if (IN6_ARE_ADDR_EQUAL(&addr, &slaac->addr))
   74: 	      {
   75: 		*up = slaac->next;
   76: 		/* recheck when DHCPv4 goes through init-reboot */
   77: 		if (force)
   78: 		  {
   79: 		    slaac->ping_time = now;
   80: 		    slaac->backoff = 1;
   81: 		    dns_dirty = 1;
   82: 		  }
   83: 		break;
   84: 	      }
   85: 	    up = &slaac->next;
   86: 	  }
   87: 	    
   88: 	/* No, make new one */
   89: 	if (!slaac && (slaac = whine_malloc(sizeof(struct slaac_address))))
   90: 	  {
   91: 	    slaac->ping_time = now;
   92: 	    slaac->backoff = 1;
   93: 	    slaac->addr = addr;
   94: 	    slaac->local = context->local6;
   95: 	    /* Do RA's to prod it */
   96: 	    ra_start_unsolicted(now, context);
   97: 	  }
   98: 	
   99: 	if (slaac)
  100: 	  {
  101: 	    slaac->next = lease->slaac_address;
  102: 	    lease->slaac_address = slaac;
  103: 	  }
  104:       }
  105:   
  106:   if (old || dns_dirty)
  107:     lease_update_dns(1);
  108:   
  109:   /* Free any no reused */
  110:   for (; old; old = slaac)
  111:     {
  112:       slaac = old->next;
  113:       free(old);
  114:     }
  115: }
  116: 
  117: 
  118: time_t periodic_slaac(time_t now, struct dhcp_lease *leases)
  119: {
  120:   struct dhcp_context *context;
  121:   struct dhcp_lease *lease;
  122:   struct slaac_address *slaac;
  123:   time_t next_event = 0;
  124:   
  125:   for (context = daemon->dhcp6; context; context = context->next)
  126:     if ((context->flags & CONTEXT_RA_NAME))
  127:       break;
  128: 
  129:   /* nothing configured */
  130:   if (!context)
  131:     return 0;
  132: 
  133:   while (ping_id == 0)
  134:     ping_id = rand16();
  135: 
  136:   for (lease = leases; lease; lease = lease->next)
  137:     for (slaac = lease->slaac_address; slaac; slaac = slaac->next)
  138:       {
  139: 	/* confirmed or given up? */
  140: 	if (slaac->backoff == 0 || slaac->ping_time == 0)
  141: 	  continue;
  142: 	
  143: 	if (difftime(slaac->ping_time, now) <= 0.0)
  144: 	  {
  145: 	    struct ping_packet *ping;
  146: 	    struct sockaddr_in6 addr;
  147:  
  148: 	    save_counter(0);
  149: 	    ping = expand(sizeof(struct ping_packet));
  150: 	    ping->type = ICMP6_ECHO_REQUEST;
  151: 	    ping->code = 0;
  152: 	    ping->identifier = ping_id;
  153: 	    ping->sequence_no = slaac->backoff;
  154: 	    
  155: 	    memset(&addr, 0, sizeof(addr));
  156: #ifdef HAVE_SOCKADDR_SA_LEN
  157: 	    addr.sin6_len = sizeof(struct sockaddr_in6);
  158: #endif
  159: 	    addr.sin6_family = AF_INET6;
  160: 	    addr.sin6_port = htons(IPPROTO_ICMPV6);
  161: 	    addr.sin6_addr = slaac->addr;
  162: 	    
  163: 	    if (sendto(daemon->icmp6fd, daemon->outpacket.iov_base, save_counter(0), 0,
  164: 		       (struct sockaddr *)&addr,  sizeof(addr)) == -1 &&
  165: 		errno == EHOSTUNREACH)
  166: 	      slaac->ping_time = 0; /* Give up */ 
  167: 	    else
  168: 	      {
  169: 		slaac->ping_time += (1 << (slaac->backoff - 1)) + (rand16()/21785); /* 0 - 3 */
  170: 		if (slaac->backoff > 4)
  171: 		  slaac->ping_time += rand16()/4000; /* 0 - 15 */
  172: 		if (slaac->backoff < 12)
  173: 		  slaac->backoff++;
  174: 	      }
  175: 	  }
  176: 	
  177: 	if (slaac->ping_time != 0 &&
  178: 	    (next_event == 0 || difftime(next_event, slaac->ping_time) >= 0.0))
  179: 	  next_event = slaac->ping_time;
  180:       }
  181: 
  182:   return next_event;
  183: }
  184: 
  185: 
  186: void slaac_ping_reply(struct in6_addr *sender, unsigned char *packet, char *interface, struct dhcp_lease *leases)
  187: {
  188:   struct dhcp_lease *lease;
  189:   struct slaac_address *slaac;
  190:   struct ping_packet *ping = (struct ping_packet *)packet;
  191:   int gotone = 0;
  192:   
  193:   if (ping->identifier == ping_id)
  194:     for (lease = leases; lease; lease = lease->next)
  195:       for (slaac = lease->slaac_address; slaac; slaac = slaac->next)
  196: 	if (slaac->backoff != 0 && IN6_ARE_ADDR_EQUAL(sender, &slaac->addr))
  197: 	  {
  198: 	    slaac->backoff = 0;
  199: 	    gotone = 1;
  200: 	    inet_ntop(AF_INET6, sender, daemon->addrbuff, ADDRSTRLEN);
  201: 	    my_syslog(MS_DHCP | LOG_INFO, "SLAAC-CONFIRM(%s) %s %s", interface, daemon->addrbuff, lease->hostname); 
  202: 	  }
  203:   
  204:   lease_update_dns(gotone);
  205: }
  206: 	
  207: #endif

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