1 /* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
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.
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.
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/>.
21 #include <netinet/icmp6.h>
23 static int ping_id
= 0;
25 void slaac_add_addrs(struct dhcp_lease
*lease
, time_t now
, int force
)
27 struct slaac_address
*slaac
, *old
, **up
;
28 struct dhcp_context
*context
;
31 if (!(lease
->flags
& LEASE_HAVE_HWADDR
) ||
32 (lease
->flags
& (LEASE_TA
| LEASE_NA
)) ||
33 lease
->last_interface
== 0 ||
37 old
= lease
->slaac_address
;
38 lease
->slaac_address
= NULL
;
40 for (context
= daemon
->dhcp6
; context
; context
= context
->next
)
41 if ((context
->flags
& CONTEXT_RA_NAME
) && lease
->last_interface
== context
->if_index
)
43 struct in6_addr addr
= context
->start6
;
44 if (lease
->hwaddr_len
== 6 &&
45 (lease
->hwaddr_type
== ARPHRD_ETHER
|| lease
->hwaddr_type
== ARPHRD_IEEE802
))
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;
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);
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);
68 addr
.s6_addr
[8] ^= 0x02;
70 /* check if we already have this one */
71 for (up
= &old
, slaac
= old
; slaac
; slaac
= slaac
->next
)
73 if (IN6_ARE_ADDR_EQUAL(&addr
, &slaac
->addr
))
76 /* recheck when DHCPv4 goes through init-reboot */
79 slaac
->ping_time
= now
;
88 /* No, make new one */
89 if (!slaac
&& (slaac
= whine_malloc(sizeof(struct slaac_address
))))
91 slaac
->ping_time
= now
;
94 slaac
->local
= context
->local6
;
95 /* Do RA's to prod it */
96 ra_start_unsolicted(now
, context
);
101 slaac
->next
= lease
->slaac_address
;
102 lease
->slaac_address
= slaac
;
106 if (old
|| dns_dirty
)
109 /* Free any no reused */
110 for (; old
; old
= slaac
)
118 time_t periodic_slaac(time_t now
, struct dhcp_lease
*leases
)
120 struct dhcp_context
*context
;
121 struct dhcp_lease
*lease
;
122 struct slaac_address
*slaac
;
123 time_t next_event
= 0;
125 for (context
= daemon
->dhcp6
; context
; context
= context
->next
)
126 if ((context
->flags
& CONTEXT_RA_NAME
))
129 /* nothing configured */
136 for (lease
= leases
; lease
; lease
= lease
->next
)
137 for (slaac
= lease
->slaac_address
; slaac
; slaac
= slaac
->next
)
139 /* confirmed or given up? */
140 if (slaac
->backoff
== 0 || slaac
->ping_time
== 0)
143 if (difftime(slaac
->ping_time
, now
) <= 0.0)
145 struct ping_packet
*ping
;
146 struct sockaddr_in6 addr
;
149 ping
= expand(sizeof(struct ping_packet
));
150 ping
->type
= ICMP6_ECHO_REQUEST
;
152 ping
->identifier
= ping_id
;
153 ping
->sequence_no
= slaac
->backoff
;
155 memset(&addr
, 0, sizeof(addr
));
156 #ifdef HAVE_SOCKADDR_SA_LEN
157 addr
.sin6_len
= sizeof(struct sockaddr_in6
);
159 addr
.sin6_family
= AF_INET6
;
160 addr
.sin6_port
= htons(IPPROTO_ICMPV6
);
161 addr
.sin6_addr
= slaac
->addr
;
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 */
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)
177 if (slaac
->ping_time
!= 0 &&
178 (next_event
== 0 || difftime(next_event
, slaac
->ping_time
) >= 0.0))
179 next_event
= slaac
->ping_time
;
186 void slaac_ping_reply(struct in6_addr
*sender
, unsigned char *packet
, char *interface
, struct dhcp_lease
*leases
)
188 struct dhcp_lease
*lease
;
189 struct slaac_address
*slaac
;
190 struct ping_packet
*ping
= (struct ping_packet
*)packet
;
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
))
200 inet_ntop(AF_INET6
, sender
, daemon
->addrbuff
, ADDRSTRLEN
);
201 #ifdef HAVE_QUIET_DHCP
202 if (!option_bool(OPT_QUIET_DHCP6
))
204 my_syslog(MS_DHCP
| LOG_INFO
, "SLAAC-CONFIRM(%s) %s %s", interface
, daemon
->addrbuff
, lease
->hostname
);
207 lease_update_dns(gotone
);