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