]>
Commit | Line | Data |
---|---|---|
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 | ||
23 | static int map_rebuild = 0; | |
24 | static int ping_id = 0; | |
25 | ||
a9ab732e | 26 | void 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 | ||
119 | time_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 | ||
193 | void 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 | */ | |
218 | static 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 | ||
240 | void 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 | ||
257 | void schedule_subnet_map(void) | |
258 | { | |
259 | map_rebuild = 1; | |
260 | } | |
261 | #endif |