Coverage Report

Created: 2026-02-24 06:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/dnsmasq/src/slaac.c
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