blob: 9b100630b673537733ee3e7625e8ed30731f986d [file] [log] [blame]
Simon Kelleyc8e8f5c2021-01-24 21:59:37 +00001/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley
Simon Kelley353ae4d2012-03-19 20:07:51 +00002
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
Simon Kelley353ae4d2012-03-19 20:07:51 +000023static int ping_id = 0;
24
Simon Kelleya9ab7322012-04-28 11:29:37 +010025void slaac_add_addrs(struct dhcp_lease *lease, time_t now, int force)
Simon Kelley353ae4d2012-03-19 20:07:51 +000026{
27 struct slaac_address *slaac, *old, **up;
28 struct dhcp_context *context;
Simon Kelley7f61b3a2012-04-28 17:42:31 +010029 int dns_dirty = 0;
Simon Kelley353ae4d2012-03-19 20:07:51 +000030
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
Simon Kelley1f776932012-12-16 19:46:08 +000040 for (context = daemon->dhcp6; context; context = context->next)
Simon Kelleyef1a94a2013-07-26 13:59:03 +010041 if ((context->flags & CONTEXT_RA_NAME) &&
42 !(context->flags & CONTEXT_OLD) &&
43 lease->last_interface == context->if_index)
Simon Kelley353ae4d2012-03-19 20:07:51 +000044 {
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;
Simon Kelleya9ab7322012-04-28 11:29:37 +010078 /* recheck when DHCPv4 goes through init-reboot */
79 if (force)
80 {
81 slaac->ping_time = now;
82 slaac->backoff = 1;
Simon Kelley7f61b3a2012-04-28 17:42:31 +010083 dns_dirty = 1;
Simon Kelleya9ab7322012-04-28 11:29:37 +010084 }
Simon Kelley353ae4d2012-03-19 20:07:51 +000085 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;
Simon Kelley353ae4d2012-03-19 20:07:51 +000096 /* Do RA's to prod it */
Josh Soref730c6742017-02-06 16:14:04 +000097 ra_start_unsolicited(now, context);
Simon Kelley353ae4d2012-03-19 20:07:51 +000098 }
99
100 if (slaac)
101 {
102 slaac->next = lease->slaac_address;
103 lease->slaac_address = slaac;
104 }
105 }
106
Simon Kelley7f61b3a2012-04-28 17:42:31 +0100107 if (old || dns_dirty)
108 lease_update_dns(1);
109
Simon Kelley353ae4d2012-03-19 20:07:51 +0000110 /* 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;
Simon Kelley89382ba2012-04-04 20:48:16 +0100123 struct slaac_address *slaac;
124 time_t next_event = 0;
Simon Kelley353ae4d2012-03-19 20:07:51 +0000125
Simon Kelley1f776932012-12-16 19:46:08 +0000126 for (context = daemon->dhcp6; context; context = context->next)
Simon Kelleyef1a94a2013-07-26 13:59:03 +0100127 if ((context->flags & CONTEXT_RA_NAME) && !(context->flags & CONTEXT_OLD))
Simon Kelley353ae4d2012-03-19 20:07:51 +0000128 break;
129
130 /* nothing configured */
131 if (!context)
132 return 0;
133
134 while (ping_id == 0)
135 ping_id = rand16();
136
Simon Kelley353ae4d2012-03-19 20:07:51 +0000137 for (lease = leases; lease; lease = lease->next)
138 for (slaac = lease->slaac_address; slaac; slaac = slaac->next)
139 {
Simon Kelley29689cf2012-03-22 14:01:00 +0000140 /* confirmed or given up? */
141 if (slaac->backoff == 0 || slaac->ping_time == 0)
Simon Kelley353ae4d2012-03-19 20:07:51 +0000142 continue;
143
144 if (difftime(slaac->ping_time, now) <= 0.0)
145 {
146 struct ping_packet *ping;
147 struct sockaddr_in6 addr;
Simon Kelley89382ba2012-04-04 20:48:16 +0100148
Simon Kelleyfa785732016-07-22 20:56:01 +0100149 reset_counter();
Simon Kelleyce7845b2016-07-06 21:42:27 +0100150
151 if (!(ping = expand(sizeof(struct ping_packet))))
152 continue;
153
Simon Kelley353ae4d2012-03-19 20:07:51 +0000154 ping->type = ICMP6_ECHO_REQUEST;
155 ping->code = 0;
156 ping->identifier = ping_id;
157 ping->sequence_no = slaac->backoff;
158
159 memset(&addr, 0, sizeof(addr));
160#ifdef HAVE_SOCKADDR_SA_LEN
161 addr.sin6_len = sizeof(struct sockaddr_in6);
162#endif
163 addr.sin6_family = AF_INET6;
164 addr.sin6_port = htons(IPPROTO_ICMPV6);
165 addr.sin6_addr = slaac->addr;
166
Simon Kelley6b1c4642016-07-22 20:59:16 +0100167 if (sendto(daemon->icmp6fd, daemon->outpacket.iov_base, save_counter(-1), 0,
Simon Kelley89382ba2012-04-04 20:48:16 +0100168 (struct sockaddr *)&addr, sizeof(addr)) == -1 &&
Simon Kelleyee1df062018-10-05 22:22:41 +0100169 errno == EHOSTUNREACH &&
170 slaac->backoff == 12)
Simon Kelley50303b12012-04-04 22:13:17 +0100171 slaac->ping_time = 0; /* Give up */
Simon Kelley89382ba2012-04-04 20:48:16 +0100172 else
Simon Kelley29689cf2012-03-22 14:01:00 +0000173 {
174 slaac->ping_time += (1 << (slaac->backoff - 1)) + (rand16()/21785); /* 0 - 3 */
175 if (slaac->backoff > 4)
176 slaac->ping_time += rand16()/4000; /* 0 - 15 */
177 if (slaac->backoff < 12)
178 slaac->backoff++;
179 }
Simon Kelley353ae4d2012-03-19 20:07:51 +0000180 }
181
Simon Kelley29689cf2012-03-22 14:01:00 +0000182 if (slaac->ping_time != 0 &&
183 (next_event == 0 || difftime(next_event, slaac->ping_time) >= 0.0))
Simon Kelley353ae4d2012-03-19 20:07:51 +0000184 next_event = slaac->ping_time;
185 }
186
187 return next_event;
188}
189
190
191void slaac_ping_reply(struct in6_addr *sender, unsigned char *packet, char *interface, struct dhcp_lease *leases)
192{
193 struct dhcp_lease *lease;
194 struct slaac_address *slaac;
195 struct ping_packet *ping = (struct ping_packet *)packet;
196 int gotone = 0;
197
198 if (ping->identifier == ping_id)
199 for (lease = leases; lease; lease = lease->next)
200 for (slaac = lease->slaac_address; slaac; slaac = slaac->next)
201 if (slaac->backoff != 0 && IN6_ARE_ADDR_EQUAL(sender, &slaac->addr))
202 {
203 slaac->backoff = 0;
204 gotone = 1;
205 inet_ntop(AF_INET6, sender, daemon->addrbuff, ADDRSTRLEN);
Kevin Darbyshire-Bryant8c0b73d2013-10-11 11:56:33 +0100206 if (!option_bool(OPT_QUIET_DHCP6))
207 my_syslog(MS_DHCP | LOG_INFO, "SLAAC-CONFIRM(%s) %s %s", interface, daemon->addrbuff, lease->hostname);
Simon Kelley353ae4d2012-03-19 20:07:51 +0000208 }
209
210 lease_update_dns(gotone);
211}
212
Simon Kelley353ae4d2012-03-19 20:07:51 +0000213#endif