mirror of
http://thekelleys.org.uk/git/dnsmasq.git
synced 2026-11-04 13:48:30 +00:00
I am attaching an incremental git-am ready patch to go on top your Git HEAD, to fix all sorts of issues and make this conforming C99 with default options set, and fix another load of warnings you receive when setting the compiler to pick the nits, -pedantic-errors -std=c99 (or c11, c18, c2x). It changes many void * to uint8_t * to make the "increment by bytes" explicit. You can't do: void *foo; // ... foo += 2.
233 lines
5.4 KiB
C
233 lines
5.4 KiB
C
/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; version 2 dated June, 1991, or
|
|
(at your option) version 3 dated 29 June, 2007.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "dnsmasq.h"
|
|
|
|
/* Time between forced re-loads from kernel. */
|
|
#define INTERVAL 90
|
|
|
|
#define ARP_MARK 0
|
|
#define ARP_FOUND 1 /* Confirmed */
|
|
#define ARP_NEW 2 /* Newly created */
|
|
#define ARP_EMPTY 3 /* No MAC addr */
|
|
|
|
struct arp_record {
|
|
unsigned short hwlen, status;
|
|
int family;
|
|
unsigned char hwaddr[DHCP_CHADDR_MAX];
|
|
union all_addr addr;
|
|
struct arp_record *next;
|
|
};
|
|
|
|
static struct arp_record *arps = NULL, *old = NULL, *freelist = NULL;
|
|
static time_t last = 0;
|
|
|
|
static int filter_mac(int family, void *addrp, char *mac, size_t maclen, void *parmv)
|
|
{
|
|
struct arp_record *arp;
|
|
|
|
(void)parmv;
|
|
|
|
if (maclen > DHCP_CHADDR_MAX)
|
|
return 1;
|
|
|
|
/* Look for existing entry */
|
|
for (arp = arps; arp; arp = arp->next)
|
|
{
|
|
if (family != arp->family || arp->status == ARP_NEW)
|
|
continue;
|
|
|
|
if (family == AF_INET)
|
|
{
|
|
if (arp->addr.addr4.s_addr != ((struct in_addr *)addrp)->s_addr)
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
if (!IN6_ARE_ADDR_EQUAL(&arp->addr.addr6, (struct in6_addr *)addrp))
|
|
continue;
|
|
}
|
|
|
|
if (arp->status == ARP_EMPTY)
|
|
{
|
|
/* existing address, was negative. */
|
|
arp->status = ARP_NEW;
|
|
arp->hwlen = maclen;
|
|
memcpy(arp->hwaddr, mac, maclen);
|
|
}
|
|
else if (arp->hwlen == maclen && memcmp(arp->hwaddr, mac, maclen) == 0)
|
|
/* Existing entry matches - confirm. */
|
|
arp->status = ARP_FOUND;
|
|
else
|
|
continue;
|
|
|
|
break;
|
|
}
|
|
|
|
if (!arp)
|
|
{
|
|
/* New entry */
|
|
if (freelist)
|
|
{
|
|
arp = freelist;
|
|
freelist = freelist->next;
|
|
}
|
|
else if (!(arp = whine_malloc(sizeof(struct arp_record))))
|
|
return 1;
|
|
|
|
arp->next = arps;
|
|
arps = arp;
|
|
arp->status = ARP_NEW;
|
|
arp->hwlen = maclen;
|
|
arp->family = family;
|
|
memcpy(arp->hwaddr, mac, maclen);
|
|
if (family == AF_INET)
|
|
arp->addr.addr4.s_addr = ((struct in_addr *)addrp)->s_addr;
|
|
else
|
|
memcpy(&arp->addr.addr6, addrp, IN6ADDRSZ);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* If in lazy mode, we cache absence of ARP entries. */
|
|
int find_mac(union mysockaddr *addr, unsigned char *mac, int lazy, time_t now)
|
|
{
|
|
struct arp_record *arp, *tmp, **up;
|
|
int updated = 0;
|
|
|
|
again:
|
|
|
|
/* If the database is less then INTERVAL old, look in there */
|
|
if (difftime(now, last) < INTERVAL)
|
|
{
|
|
/* addr == NULL -> just make cache up-to-date */
|
|
if (!addr)
|
|
return 0;
|
|
|
|
for (arp = arps; arp; arp = arp->next)
|
|
{
|
|
if (addr->sa.sa_family != arp->family)
|
|
continue;
|
|
|
|
if (arp->family == AF_INET &&
|
|
arp->addr.addr4.s_addr != addr->in.sin_addr.s_addr)
|
|
continue;
|
|
|
|
if (arp->family == AF_INET6 &&
|
|
!IN6_ARE_ADDR_EQUAL(&arp->addr.addr6, &addr->in6.sin6_addr))
|
|
continue;
|
|
|
|
/* Only accept positive entries unless in lazy mode. */
|
|
if (arp->status != ARP_EMPTY || lazy || updated)
|
|
{
|
|
if (mac && arp->hwlen != 0)
|
|
memcpy(mac, arp->hwaddr, arp->hwlen);
|
|
return arp->hwlen;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Not found, try the kernel */
|
|
if (!updated)
|
|
{
|
|
updated = 1;
|
|
last = now;
|
|
|
|
/* Mark all non-negative entries */
|
|
for (arp = arps; arp; arp = arp->next)
|
|
if (arp->status != ARP_EMPTY)
|
|
arp->status = ARP_MARK;
|
|
|
|
iface_enumerate(AF_UNSPEC, NULL, (callback_t){.af_unspec=filter_mac});
|
|
|
|
/* Remove all unconfirmed entries to old list. */
|
|
for (arp = arps, up = &arps; arp; arp = tmp)
|
|
{
|
|
tmp = arp->next;
|
|
|
|
if (arp->status == ARP_MARK)
|
|
{
|
|
*up = arp->next;
|
|
arp->next = old;
|
|
old = arp;
|
|
}
|
|
else
|
|
up = &arp->next;
|
|
}
|
|
|
|
goto again;
|
|
}
|
|
|
|
/* record failure, so we don't consult the kernel each time
|
|
we're asked for this address */
|
|
if (freelist)
|
|
{
|
|
arp = freelist;
|
|
freelist = freelist->next;
|
|
}
|
|
else
|
|
arp = whine_malloc(sizeof(struct arp_record));
|
|
|
|
if (arp)
|
|
{
|
|
arp->next = arps;
|
|
arps = arp;
|
|
arp->status = ARP_EMPTY;
|
|
arp->family = addr->sa.sa_family;
|
|
arp->hwlen = 0;
|
|
|
|
if (addr->sa.sa_family == AF_INET)
|
|
arp->addr.addr4.s_addr = addr->in.sin_addr.s_addr;
|
|
else
|
|
memcpy(&arp->addr.addr6, &addr->in6.sin6_addr, IN6ADDRSZ);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int do_arp_script_run(void)
|
|
{
|
|
struct arp_record *arp;
|
|
|
|
/* Notify any which went, then move to free list */
|
|
if (old)
|
|
{
|
|
#ifdef HAVE_SCRIPT
|
|
if (option_bool(OPT_SCRIPT_ARP))
|
|
queue_arp(ACTION_ARP_DEL, old->hwaddr, old->hwlen, old->family, &old->addr);
|
|
#endif
|
|
arp = old;
|
|
old = arp->next;
|
|
arp->next = freelist;
|
|
freelist = arp;
|
|
return 1;
|
|
}
|
|
|
|
for (arp = arps; arp; arp = arp->next)
|
|
if (arp->status == ARP_NEW)
|
|
{
|
|
#ifdef HAVE_SCRIPT
|
|
if (option_bool(OPT_SCRIPT_ARP))
|
|
queue_arp(ACTION_ARP, arp->hwaddr, arp->hwlen, arp->family, &arp->addr);
|
|
#endif
|
|
arp->status = ARP_FOUND;
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|