]>
Commit | Line | Data |
---|---|---|
aff33962 | 1 | /* dnsmasq is Copyright (c) 2000-2015 Simon Kelley |
353ae4d2 SK |
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 | ||
353ae4d2 SK |
23 | static int ping_id = 0; |
24 | ||
a9ab732e | 25 | void slaac_add_addrs(struct dhcp_lease *lease, time_t now, int force) |
353ae4d2 SK |
26 | { |
27 | struct slaac_address *slaac, *old, **up; | |
28 | struct dhcp_context *context; | |
7f61b3ad | 29 | int dns_dirty = 0; |
353ae4d2 SK |
30 | |
31 | if (!(lease->flags & LEASE_HAVE_HWADDR) || | |
a9ab732e | 32 | (lease->flags & (LEASE_TA | LEASE_NA)) || |
353ae4d2 SK |
33 | lease->last_interface == 0 || |
34 | !lease->hostname) | |
35 | return ; | |
36 | ||
37 | old = lease->slaac_address; | |
38 | lease->slaac_address = NULL; | |
39 | ||
1f776932 | 40 | for (context = daemon->dhcp6; context; context = context->next) |
ef1a94ab SK |
41 | if ((context->flags & CONTEXT_RA_NAME) && |
42 | !(context->flags & CONTEXT_OLD) && | |
43 | lease->last_interface == context->if_index) | |
353ae4d2 SK |
44 | { |
45 | struct in6_addr addr = context->start6; | |
46 | if (lease->hwaddr_len == 6 && | |
47 | (lease->hwaddr_type == ARPHRD_ETHER || lease->hwaddr_type == ARPHRD_IEEE802)) | |
48 | { | |
49 | /* convert MAC address to EUI-64 */ | |
50 | memcpy(&addr.s6_addr[8], lease->hwaddr, 3); | |
51 | memcpy(&addr.s6_addr[13], &lease->hwaddr[3], 3); | |
52 | addr.s6_addr[11] = 0xff; | |
53 | addr.s6_addr[12] = 0xfe; | |
54 | } | |
55 | #if defined(ARPHRD_EUI64) | |
56 | else if (lease->hwaddr_len == 8 && | |
57 | lease->hwaddr_type == ARPHRD_EUI64) | |
58 | memcpy(&addr.s6_addr[8], lease->hwaddr, 8); | |
59 | #endif | |
60 | #if defined(ARPHRD_IEEE1394) && defined(ARPHRD_EUI64) | |
61 | else if (lease->clid_len == 9 && | |
62 | lease->clid[0] == ARPHRD_EUI64 && | |
63 | lease->hwaddr_type == ARPHRD_IEEE1394) | |
64 | /* firewire has EUI-64 identifier as clid */ | |
65 | memcpy(&addr.s6_addr[8], &lease->clid[1], 8); | |
66 | #endif | |
67 | else | |
68 | continue; | |
69 | ||
70 | addr.s6_addr[8] ^= 0x02; | |
71 | ||
72 | /* check if we already have this one */ | |
73 | for (up = &old, slaac = old; slaac; slaac = slaac->next) | |
74 | { | |
75 | if (IN6_ARE_ADDR_EQUAL(&addr, &slaac->addr)) | |
76 | { | |
77 | *up = slaac->next; | |
a9ab732e SK |
78 | /* recheck when DHCPv4 goes through init-reboot */ |
79 | if (force) | |
80 | { | |
81 | slaac->ping_time = now; | |
82 | slaac->backoff = 1; | |
7f61b3ad | 83 | dns_dirty = 1; |
a9ab732e | 84 | } |
353ae4d2 SK |
85 | break; |
86 | } | |
87 | up = &slaac->next; | |
88 | } | |
89 | ||
90 | /* No, make new one */ | |
91 | if (!slaac && (slaac = whine_malloc(sizeof(struct slaac_address)))) | |
92 | { | |
93 | slaac->ping_time = now; | |
94 | slaac->backoff = 1; | |
95 | slaac->addr = addr; | |
353ae4d2 SK |
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 | 125 | |
1f776932 | 126 | for (context = daemon->dhcp6; context; context = context->next) |
ef1a94ab | 127 | if ((context->flags & CONTEXT_RA_NAME) && !(context->flags & CONTEXT_OLD)) |
353ae4d2 SK |
128 | break; |
129 | ||
130 | /* nothing configured */ | |
131 | if (!context) | |
132 | return 0; | |
133 | ||
134 | while (ping_id == 0) | |
135 | ping_id = rand16(); | |
136 | ||
353ae4d2 SK |
137 | for (lease = leases; lease; lease = lease->next) |
138 | for (slaac = lease->slaac_address; slaac; slaac = slaac->next) | |
139 | { | |
29689cfa SK |
140 | /* confirmed or given up? */ |
141 | if (slaac->backoff == 0 || slaac->ping_time == 0) | |
353ae4d2 SK |
142 | continue; |
143 | ||
144 | if (difftime(slaac->ping_time, now) <= 0.0) | |
145 | { | |
146 | struct ping_packet *ping; | |
147 | struct sockaddr_in6 addr; | |
89382bac | 148 | |
353ae4d2 SK |
149 | save_counter(0); |
150 | ping = expand(sizeof(struct ping_packet)); | |
151 | ping->type = ICMP6_ECHO_REQUEST; | |
152 | ping->code = 0; | |
153 | ping->identifier = ping_id; | |
154 | ping->sequence_no = slaac->backoff; | |
155 | ||
156 | memset(&addr, 0, sizeof(addr)); | |
157 | #ifdef HAVE_SOCKADDR_SA_LEN | |
158 | addr.sin6_len = sizeof(struct sockaddr_in6); | |
159 | #endif | |
160 | addr.sin6_family = AF_INET6; | |
161 | addr.sin6_port = htons(IPPROTO_ICMPV6); | |
162 | addr.sin6_addr = slaac->addr; | |
163 | ||
89382bac SK |
164 | if (sendto(daemon->icmp6fd, daemon->outpacket.iov_base, save_counter(0), 0, |
165 | (struct sockaddr *)&addr, sizeof(addr)) == -1 && | |
166 | errno == EHOSTUNREACH) | |
50303b19 | 167 | slaac->ping_time = 0; /* Give up */ |
89382bac | 168 | else |
29689cfa SK |
169 | { |
170 | slaac->ping_time += (1 << (slaac->backoff - 1)) + (rand16()/21785); /* 0 - 3 */ | |
171 | if (slaac->backoff > 4) | |
172 | slaac->ping_time += rand16()/4000; /* 0 - 15 */ | |
173 | if (slaac->backoff < 12) | |
174 | slaac->backoff++; | |
175 | } | |
353ae4d2 SK |
176 | } |
177 | ||
29689cfa SK |
178 | if (slaac->ping_time != 0 && |
179 | (next_event == 0 || difftime(next_event, slaac->ping_time) >= 0.0)) | |
353ae4d2 SK |
180 | next_event = slaac->ping_time; |
181 | } | |
182 | ||
183 | return next_event; | |
184 | } | |
185 | ||
186 | ||
187 | void slaac_ping_reply(struct in6_addr *sender, unsigned char *packet, char *interface, struct dhcp_lease *leases) | |
188 | { | |
189 | struct dhcp_lease *lease; | |
190 | struct slaac_address *slaac; | |
191 | struct ping_packet *ping = (struct ping_packet *)packet; | |
192 | int gotone = 0; | |
193 | ||
194 | if (ping->identifier == ping_id) | |
195 | for (lease = leases; lease; lease = lease->next) | |
196 | for (slaac = lease->slaac_address; slaac; slaac = slaac->next) | |
197 | if (slaac->backoff != 0 && IN6_ARE_ADDR_EQUAL(sender, &slaac->addr)) | |
198 | { | |
199 | slaac->backoff = 0; | |
200 | gotone = 1; | |
201 | inet_ntop(AF_INET6, sender, daemon->addrbuff, ADDRSTRLEN); | |
8c0b73d3 KDB |
202 | if (!option_bool(OPT_QUIET_DHCP6)) |
203 | my_syslog(MS_DHCP | LOG_INFO, "SLAAC-CONFIRM(%s) %s %s", interface, daemon->addrbuff, lease->hostname); | |
353ae4d2 SK |
204 | } |
205 | ||
206 | lease_update_dns(gotone); | |
207 | } | |
208 | ||
353ae4d2 | 209 | #endif |