]> git.ipfire.org Git - people/ms/dnsmasq.git/blame - src/slaac.c
Make authoritative stuff a compile-time option.
[people/ms/dnsmasq.git] / src / slaac.c
CommitLineData
353ae4d2
SK
1/* 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
a9ab732e 26void slaac_add_addrs(struct dhcp_lease *lease, time_t now, int force)
353ae4d2
SK
27{
28 struct slaac_address *slaac, *old, **up;
29 struct dhcp_context *context;
7f61b3ad 30 int dns_dirty = 0;
353ae4d2
SK
31
32 if (!(lease->flags & LEASE_HAVE_HWADDR) ||
a9ab732e 33 (lease->flags & (LEASE_TA | LEASE_NA)) ||
353ae4d2
SK
34 lease->last_interface == 0 ||
35 !lease->hostname)
36 return ;
37
38 old = lease->slaac_address;
39 lease->slaac_address = NULL;
40
41 for (context = daemon->ra_contexts; context; context = context->next)
42 if ((context->flags & CONTEXT_RA_NAME) && lease->last_interface == context->if_index)
43 {
44 struct in6_addr addr = context->start6;
45 if (lease->hwaddr_len == 6 &&
46 (lease->hwaddr_type == ARPHRD_ETHER || lease->hwaddr_type == ARPHRD_IEEE802))
47 {
48 /* convert MAC address to EUI-64 */
49 memcpy(&addr.s6_addr[8], lease->hwaddr, 3);
50 memcpy(&addr.s6_addr[13], &lease->hwaddr[3], 3);
51 addr.s6_addr[11] = 0xff;
52 addr.s6_addr[12] = 0xfe;
53 }
54#if defined(ARPHRD_EUI64)
55 else if (lease->hwaddr_len == 8 &&
56 lease->hwaddr_type == ARPHRD_EUI64)
57 memcpy(&addr.s6_addr[8], lease->hwaddr, 8);
58#endif
59#if defined(ARPHRD_IEEE1394) && defined(ARPHRD_EUI64)
60 else if (lease->clid_len == 9 &&
61 lease->clid[0] == ARPHRD_EUI64 &&
62 lease->hwaddr_type == ARPHRD_IEEE1394)
63 /* firewire has EUI-64 identifier as clid */
64 memcpy(&addr.s6_addr[8], &lease->clid[1], 8);
65#endif
66 else
67 continue;
68
69 addr.s6_addr[8] ^= 0x02;
70
71 /* check if we already have this one */
72 for (up = &old, slaac = old; slaac; slaac = slaac->next)
73 {
74 if (IN6_ARE_ADDR_EQUAL(&addr, &slaac->addr))
75 {
76 *up = slaac->next;
a9ab732e
SK
77 /* recheck when DHCPv4 goes through init-reboot */
78 if (force)
79 {
80 slaac->ping_time = now;
81 slaac->backoff = 1;
7f61b3ad 82 dns_dirty = 1;
a9ab732e 83 }
353ae4d2
SK
84 break;
85 }
86 up = &slaac->next;
87 }
88
89 /* No, make new one */
90 if (!slaac && (slaac = whine_malloc(sizeof(struct slaac_address))))
91 {
92 slaac->ping_time = now;
93 slaac->backoff = 1;
94 slaac->addr = addr;
95 slaac->local = context->local6;
96 /* Do RA's to prod it */
97 ra_start_unsolicted(now, context);
98 }
99
100 if (slaac)
101 {
102 slaac->next = lease->slaac_address;
103 lease->slaac_address = slaac;
104 }
105 }
106
7f61b3ad
SK
107 if (old || dns_dirty)
108 lease_update_dns(1);
109
353ae4d2
SK
110 /* Free any no reused */
111 for (; old; old = slaac)
112 {
113 slaac = old->next;
114 free(old);
115 }
116}
117
118
119time_t periodic_slaac(time_t now, struct dhcp_lease *leases)
120{
121 struct dhcp_context *context;
122 struct dhcp_lease *lease;
89382bac
SK
123 struct slaac_address *slaac;
124 time_t next_event = 0;
353ae4d2
SK
125
126 for (context = daemon->ra_contexts; context; context = context->next)
127 if ((context->flags & CONTEXT_RA_NAME))
128 break;
129
130 /* nothing configured */
131 if (!context)
132 return 0;
133
134 while (ping_id == 0)
135 ping_id = rand16();
136
137 if (map_rebuild)
138 {
139 map_rebuild = 0;
140 build_subnet_map();
141 }
142
143 for (lease = leases; lease; lease = lease->next)
144 for (slaac = lease->slaac_address; slaac; slaac = slaac->next)
145 {
29689cfa
SK
146 /* confirmed or given up? */
147 if (slaac->backoff == 0 || slaac->ping_time == 0)
353ae4d2
SK
148 continue;
149
150 if (difftime(slaac->ping_time, now) <= 0.0)
151 {
152 struct ping_packet *ping;
153 struct sockaddr_in6 addr;
89382bac 154
353ae4d2
SK
155 save_counter(0);
156 ping = expand(sizeof(struct ping_packet));
157 ping->type = ICMP6_ECHO_REQUEST;
158 ping->code = 0;
159 ping->identifier = ping_id;
160 ping->sequence_no = slaac->backoff;
161
162 memset(&addr, 0, sizeof(addr));
163#ifdef HAVE_SOCKADDR_SA_LEN
164 addr.sin6_len = sizeof(struct sockaddr_in6);
165#endif
166 addr.sin6_family = AF_INET6;
167 addr.sin6_port = htons(IPPROTO_ICMPV6);
168 addr.sin6_addr = slaac->addr;
169
89382bac
SK
170 if (sendto(daemon->icmp6fd, daemon->outpacket.iov_base, save_counter(0), 0,
171 (struct sockaddr *)&addr, sizeof(addr)) == -1 &&
172 errno == EHOSTUNREACH)
50303b19 173 slaac->ping_time = 0; /* Give up */
89382bac 174 else
29689cfa
SK
175 {
176 slaac->ping_time += (1 << (slaac->backoff - 1)) + (rand16()/21785); /* 0 - 3 */
177 if (slaac->backoff > 4)
178 slaac->ping_time += rand16()/4000; /* 0 - 15 */
179 if (slaac->backoff < 12)
180 slaac->backoff++;
181 }
353ae4d2
SK
182 }
183
29689cfa
SK
184 if (slaac->ping_time != 0 &&
185 (next_event == 0 || difftime(next_event, slaac->ping_time) >= 0.0))
353ae4d2
SK
186 next_event = slaac->ping_time;
187 }
188
189 return next_event;
190}
191
192
193void slaac_ping_reply(struct in6_addr *sender, unsigned char *packet, char *interface, struct dhcp_lease *leases)
194{
195 struct dhcp_lease *lease;
196 struct slaac_address *slaac;
197 struct ping_packet *ping = (struct ping_packet *)packet;
198 int gotone = 0;
199
200 if (ping->identifier == ping_id)
201 for (lease = leases; lease; lease = lease->next)
202 for (slaac = lease->slaac_address; slaac; slaac = slaac->next)
203 if (slaac->backoff != 0 && IN6_ARE_ADDR_EQUAL(sender, &slaac->addr))
204 {
205 slaac->backoff = 0;
206 gotone = 1;
207 inet_ntop(AF_INET6, sender, daemon->addrbuff, ADDRSTRLEN);
208 my_syslog(MS_DHCP | LOG_INFO, "SLAAC-CONFIRM(%s) %s %s", interface, daemon->addrbuff, lease->hostname);
209 }
210
211 lease_update_dns(gotone);
212}
213
214/* Build a map from ra-names subnets to corresponding interfaces. This
215 is used to go from DHCPv4 leases to SLAAC addresses,
216 interface->IPv6-subnet, IPv6-subnet + MAC address -> SLAAC.
217*/
218static int add_subnet(struct in6_addr *local, int prefix,
219 int scope, int if_index, int dad, void *vparam)
220{
221 struct dhcp_context *context;
222
223 (void)scope;
224 (void)dad;
225 (void)vparam;
226
227 for (context = daemon->ra_contexts; context; context = context->next)
228 if ((context->flags & CONTEXT_RA_NAME) &&
229 prefix == context->prefix &&
230 is_same_net6(local, &context->start6, prefix) &&
231 is_same_net6(local, &context->end6, prefix))
232 {
233 context->if_index = if_index;
234 context->local6 = *local;
235 }
236
237 return 1;
238}
239
240void build_subnet_map(void)
241{
242 struct dhcp_context *context;
243 int ok = 0;
244
245 for (context = daemon->ra_contexts; context; context = context->next)
246 {
247 context->if_index = 0;
248 if ((context->flags & CONTEXT_RA_NAME))
249 ok = 1;
250 }
251
252 /* ra-names configured */
253 if (ok)
254 iface_enumerate(AF_INET6, NULL, add_subnet);
255}
256
257void schedule_subnet_map(void)
258{
259 map_rebuild = 1;
260}
261#endif