Coverage Report

Created: 2025-07-12 07:01

/src/dnsmasq/src/arp.c
Line
Count
Source (jump to first uncovered line)
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
/* Time between forced re-loads from kernel. */
20
0
#define INTERVAL 90
21
22
0
#define ARP_MARK  0
23
0
#define ARP_FOUND 1  /* Confirmed */
24
0
#define ARP_NEW   2  /* Newly created */
25
0
#define ARP_EMPTY 3  /* No MAC addr */
26
27
struct arp_record {
28
  unsigned short hwlen, status;
29
  int family;
30
  unsigned char hwaddr[DHCP_CHADDR_MAX]; 
31
  union all_addr addr;
32
  struct arp_record *next;
33
};
34
35
static struct arp_record *arps = NULL, *old = NULL, *freelist = NULL;
36
static time_t last = 0;
37
38
static int filter_mac(int family, void *addrp, char *mac, size_t maclen, void *parmv)
39
0
{
40
0
  struct arp_record *arp;
41
42
0
  (void)parmv;
43
44
0
  if (maclen > DHCP_CHADDR_MAX)
45
0
    return 1;
46
47
  /* Look for existing entry */
48
0
  for (arp = arps; arp; arp = arp->next)
49
0
    {
50
0
      if (family != arp->family || arp->status == ARP_NEW)
51
0
  continue;
52
      
53
0
      if (family == AF_INET)
54
0
  {
55
0
    if (arp->addr.addr4.s_addr != ((struct in_addr *)addrp)->s_addr)
56
0
      continue;
57
0
  }
58
0
      else
59
0
  {
60
0
    if (!IN6_ARE_ADDR_EQUAL(&arp->addr.addr6, (struct in6_addr *)addrp))
61
0
      continue;
62
0
  }
63
64
0
      if (arp->status == ARP_EMPTY)
65
0
  {
66
    /* existing address, was negative. */
67
0
    arp->status = ARP_NEW;
68
0
    arp->hwlen = maclen;
69
0
    memcpy(arp->hwaddr, mac, maclen);
70
0
  }
71
0
      else if (arp->hwlen == maclen && memcmp(arp->hwaddr, mac, maclen) == 0)
72
  /* Existing entry matches - confirm. */
73
0
  arp->status = ARP_FOUND;
74
0
      else
75
0
  continue;
76
      
77
0
      break;
78
0
    }
79
80
0
  if (!arp)
81
0
    {
82
      /* New entry */
83
0
      if (freelist)
84
0
  {
85
0
    arp = freelist;
86
0
    freelist = freelist->next;
87
0
  }
88
0
      else if (!(arp = whine_malloc(sizeof(struct arp_record))))
89
0
  return 1;
90
      
91
0
      arp->next = arps;
92
0
      arps = arp;
93
0
      arp->status = ARP_NEW;
94
0
      arp->hwlen = maclen;
95
0
      arp->family = family;
96
0
      memcpy(arp->hwaddr, mac, maclen);
97
0
      if (family == AF_INET)
98
0
  arp->addr.addr4.s_addr = ((struct in_addr *)addrp)->s_addr;
99
0
      else
100
0
  memcpy(&arp->addr.addr6, addrp, IN6ADDRSZ);
101
0
    }
102
  
103
0
  return 1;
104
0
}
105
106
/* If in lazy mode, we cache absence of ARP entries. */
107
int find_mac(union mysockaddr *addr, unsigned char *mac, int lazy, time_t now)
108
0
{
109
0
  struct arp_record *arp, *tmp, **up;
110
0
  int updated = 0;
111
112
0
 again:
113
  
114
  /* If the database is less then INTERVAL old, look in there */
115
0
  if (difftime(now, last) < INTERVAL)
116
0
    {
117
      /* addr == NULL -> just make cache up-to-date */
118
0
      if (!addr)
119
0
  return 0;
120
121
0
      for (arp = arps; arp; arp = arp->next)
122
0
  {
123
0
    if (addr->sa.sa_family != arp->family)
124
0
      continue;
125
      
126
0
    if (arp->family == AF_INET &&
127
0
        arp->addr.addr4.s_addr != addr->in.sin_addr.s_addr)
128
0
      continue;
129
      
130
0
    if (arp->family == AF_INET6 && 
131
0
        !IN6_ARE_ADDR_EQUAL(&arp->addr.addr6, &addr->in6.sin6_addr))
132
0
      continue;
133
    
134
    /* Only accept positive entries unless in lazy mode. */
135
0
    if (arp->status != ARP_EMPTY || lazy || updated)
136
0
      {
137
0
        if (mac && arp->hwlen != 0)
138
0
    memcpy(mac, arp->hwaddr, arp->hwlen);
139
0
        return arp->hwlen;
140
0
      }
141
0
  }
142
0
    }
143
144
  /* Not found, try the kernel */
145
0
  if (!updated)
146
0
     {
147
0
       updated = 1;
148
0
       last = now;
149
150
       /* Mark all non-negative entries */
151
0
       for (arp = arps; arp; arp = arp->next)
152
0
   if (arp->status != ARP_EMPTY)
153
0
     arp->status = ARP_MARK;
154
       
155
0
       iface_enumerate(AF_UNSPEC, NULL, (callback_t){.af_unspec=filter_mac});
156
       
157
       /* Remove all unconfirmed entries to old list. */
158
0
       for (arp = arps, up = &arps; arp; arp = tmp)
159
0
   {
160
0
     tmp = arp->next;
161
     
162
0
     if (arp->status == ARP_MARK)
163
0
       {
164
0
         *up = arp->next;
165
0
         arp->next = old;
166
0
         old = arp;
167
0
       }
168
0
     else
169
0
       up = &arp->next;
170
0
   }
171
172
0
       goto again;
173
0
     }
174
175
  /* record failure, so we don't consult the kernel each time
176
     we're asked for this address */
177
0
  if (freelist)
178
0
    {
179
0
      arp = freelist;
180
0
      freelist = freelist->next;
181
0
    }
182
0
  else
183
0
    arp = whine_malloc(sizeof(struct arp_record));
184
  
185
0
  if (arp)
186
0
    {      
187
0
      arp->next = arps;
188
0
      arps = arp;
189
0
      arp->status = ARP_EMPTY;
190
0
      arp->family = addr->sa.sa_family;
191
0
      arp->hwlen = 0;
192
193
0
      if (addr->sa.sa_family == AF_INET)
194
0
  arp->addr.addr4.s_addr = addr->in.sin_addr.s_addr;
195
0
      else
196
0
  memcpy(&arp->addr.addr6, &addr->in6.sin6_addr, IN6ADDRSZ);
197
0
    }
198
    
199
0
   return 0;
200
0
}
201
202
int do_arp_script_run(void)
203
0
{
204
0
  struct arp_record *arp;
205
  
206
  /* Notify any which went, then move to free list */
207
0
  if (old)
208
0
    {
209
0
#ifdef HAVE_SCRIPT
210
0
      if (option_bool(OPT_SCRIPT_ARP))
211
0
  queue_arp(ACTION_ARP_DEL, old->hwaddr, old->hwlen, old->family, &old->addr);
212
0
#endif
213
0
      arp = old;
214
0
      old = arp->next;
215
0
      arp->next = freelist;
216
0
      freelist = arp;
217
0
      return 1;
218
0
    }
219
220
0
  for (arp = arps; arp; arp = arp->next)
221
0
    if (arp->status == ARP_NEW)
222
0
      {
223
0
#ifdef HAVE_SCRIPT
224
0
  if (option_bool(OPT_SCRIPT_ARP))
225
0
    queue_arp(ACTION_ARP, arp->hwaddr, arp->hwlen, arp->family, &arp->addr);
226
0
#endif
227
0
  arp->status = ARP_FOUND;
228
0
  return 1;
229
0
      }
230
231
0
  return 0;
232
0
}