blob: e74e5e9fb0ba9aad7b0f0cae9cf32367a5585f5f [file] [log] [blame]
Simon Kelley353ae4d2012-03-19 20:07:51 +00001/* dnsmasq is Copyright (c) 2000-2012 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
23static int map_rebuild = 0;
24static int ping_id = 0;
25
Simon Kelleya9ab7322012-04-28 11:29:37 +010026void slaac_add_addrs(struct dhcp_lease *lease, time_t now, int force)
Simon Kelley353ae4d2012-03-19 20:07:51 +000027{
28 struct slaac_address *slaac, *old, **up;
29 struct dhcp_context *context;
30
31 if (!(lease->flags & LEASE_HAVE_HWADDR) ||
Simon Kelleya9ab7322012-04-28 11:29:37 +010032 (lease->flags & (LEASE_TA | LEASE_NA)) ||
Simon Kelley353ae4d2012-03-19 20:07:51 +000033 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->ra_contexts; 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;
Simon Kelleya9ab7322012-04-28 11:29:37 +010076 /* recheck when DHCPv4 goes through init-reboot */
77 if (force)
78 {
79 slaac->ping_time = now;
80 slaac->backoff = 1;
81 lease_update_dns(1);
82 }
Simon Kelley353ae4d2012-03-19 20:07:51 +000083 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 /* Free any no reused */
107 for (; old; old = slaac)
108 {
109 slaac = old->next;
110 free(old);
111 }
112}
113
114
115time_t periodic_slaac(time_t now, struct dhcp_lease *leases)
116{
117 struct dhcp_context *context;
118 struct dhcp_lease *lease;
Simon Kelley89382ba2012-04-04 20:48:16 +0100119 struct slaac_address *slaac;
120 time_t next_event = 0;
Simon Kelley353ae4d2012-03-19 20:07:51 +0000121
122 for (context = daemon->ra_contexts; context; context = context->next)
123 if ((context->flags & CONTEXT_RA_NAME))
124 break;
125
126 /* nothing configured */
127 if (!context)
128 return 0;
129
130 while (ping_id == 0)
131 ping_id = rand16();
132
133 if (map_rebuild)
134 {
135 map_rebuild = 0;
136 build_subnet_map();
137 }
138
139 for (lease = leases; lease; lease = lease->next)
140 for (slaac = lease->slaac_address; slaac; slaac = slaac->next)
141 {
Simon Kelley29689cf2012-03-22 14:01:00 +0000142 /* confirmed or given up? */
143 if (slaac->backoff == 0 || slaac->ping_time == 0)
Simon Kelley353ae4d2012-03-19 20:07:51 +0000144 continue;
145
146 if (difftime(slaac->ping_time, now) <= 0.0)
147 {
148 struct ping_packet *ping;
149 struct sockaddr_in6 addr;
Simon Kelley89382ba2012-04-04 20:48:16 +0100150
Simon Kelley353ae4d2012-03-19 20:07:51 +0000151 save_counter(0);
152 ping = expand(sizeof(struct ping_packet));
153 ping->type = ICMP6_ECHO_REQUEST;
154 ping->code = 0;
155 ping->identifier = ping_id;
156 ping->sequence_no = slaac->backoff;
157
158 memset(&addr, 0, sizeof(addr));
159#ifdef HAVE_SOCKADDR_SA_LEN
160 addr.sin6_len = sizeof(struct sockaddr_in6);
161#endif
162 addr.sin6_family = AF_INET6;
163 addr.sin6_port = htons(IPPROTO_ICMPV6);
164 addr.sin6_addr = slaac->addr;
165
Simon Kelley89382ba2012-04-04 20:48:16 +0100166 if (sendto(daemon->icmp6fd, daemon->outpacket.iov_base, save_counter(0), 0,
167 (struct sockaddr *)&addr, sizeof(addr)) == -1 &&
168 errno == EHOSTUNREACH)
Simon Kelley50303b12012-04-04 22:13:17 +0100169 slaac->ping_time = 0; /* Give up */
Simon Kelley89382ba2012-04-04 20:48:16 +0100170 else
Simon Kelley29689cf2012-03-22 14:01:00 +0000171 {
172 slaac->ping_time += (1 << (slaac->backoff - 1)) + (rand16()/21785); /* 0 - 3 */
173 if (slaac->backoff > 4)
174 slaac->ping_time += rand16()/4000; /* 0 - 15 */
175 if (slaac->backoff < 12)
176 slaac->backoff++;
177 }
Simon Kelley353ae4d2012-03-19 20:07:51 +0000178 }
179
Simon Kelley29689cf2012-03-22 14:01:00 +0000180 if (slaac->ping_time != 0 &&
181 (next_event == 0 || difftime(next_event, slaac->ping_time) >= 0.0))
Simon Kelley353ae4d2012-03-19 20:07:51 +0000182 next_event = slaac->ping_time;
183 }
184
185 return next_event;
186}
187
188
189void slaac_ping_reply(struct in6_addr *sender, unsigned char *packet, char *interface, struct dhcp_lease *leases)
190{
191 struct dhcp_lease *lease;
192 struct slaac_address *slaac;
193 struct ping_packet *ping = (struct ping_packet *)packet;
194 int gotone = 0;
195
196 if (ping->identifier == ping_id)
197 for (lease = leases; lease; lease = lease->next)
198 for (slaac = lease->slaac_address; slaac; slaac = slaac->next)
199 if (slaac->backoff != 0 && IN6_ARE_ADDR_EQUAL(sender, &slaac->addr))
200 {
201 slaac->backoff = 0;
202 gotone = 1;
203 inet_ntop(AF_INET6, sender, daemon->addrbuff, ADDRSTRLEN);
204 my_syslog(MS_DHCP | LOG_INFO, "SLAAC-CONFIRM(%s) %s %s", interface, daemon->addrbuff, lease->hostname);
205 }
206
207 lease_update_dns(gotone);
208}
209
210/* Build a map from ra-names subnets to corresponding interfaces. This
211 is used to go from DHCPv4 leases to SLAAC addresses,
212 interface->IPv6-subnet, IPv6-subnet + MAC address -> SLAAC.
213*/
214static int add_subnet(struct in6_addr *local, int prefix,
215 int scope, int if_index, int dad, void *vparam)
216{
217 struct dhcp_context *context;
218
219 (void)scope;
220 (void)dad;
221 (void)vparam;
222
223 for (context = daemon->ra_contexts; context; context = context->next)
224 if ((context->flags & CONTEXT_RA_NAME) &&
225 prefix == context->prefix &&
226 is_same_net6(local, &context->start6, prefix) &&
227 is_same_net6(local, &context->end6, prefix))
228 {
229 context->if_index = if_index;
230 context->local6 = *local;
231 }
232
233 return 1;
234}
235
236void build_subnet_map(void)
237{
238 struct dhcp_context *context;
239 int ok = 0;
240
241 for (context = daemon->ra_contexts; context; context = context->next)
242 {
243 context->if_index = 0;
244 if ((context->flags & CONTEXT_RA_NAME))
245 ok = 1;
246 }
247
248 /* ra-names configured */
249 if (ok)
250 iface_enumerate(AF_INET6, NULL, add_subnet);
251}
252
253void schedule_subnet_map(void)
254{
255 map_rebuild = 1;
256}
257#endif