spotfilter: discover ip address via ARP

Signed-off-by: Felix Fietkau <nbd@nbd.name>
This commit is contained in:
Felix Fietkau
2024-04-30 14:36:08 +02:00
committed by John Crispin
parent de425e8f17
commit ae9d1b64c3
5 changed files with 86 additions and 14 deletions

View File

@@ -12,6 +12,7 @@
struct cache_entry {
struct avl_node node;
uint8_t macaddr[ETH_ALEN];
uint32_t arp_ip4addr;
uint32_t ip4addr;
uint32_t ip6addr[4];
uint32_t time;
@@ -144,6 +145,8 @@ int client_set(struct interface *iface, const void *addr, const char *id,
if (c) {
if (!cl->data.ip4addr)
cl->data.ip4addr = c->ip4addr;
if (!cl->arp_ip4addr)
cl->arp_ip4addr = c->arp_ip4addr;
if (!cl->data.ip6addr[0])
memcpy(cl->data.ip6addr, c->ip6addr, sizeof(cl->data.ip6addr));
}
@@ -186,25 +189,38 @@ int client_set(struct interface *iface, const void *addr, const char *id,
return 0;
}
void client_set_ipaddr(const void *mac, const void *addr, bool ipv6)
static struct cache_entry *
client_get_cache_entry(const void *mac)
{
static struct uloop_timeout gc_timer = {
.cb = client_gc
};
struct interface *iface;
struct cache_entry *c;
struct client *cl;
c = avl_find_element(&cache, mac, c, node);
if (!c) {
if (c)
goto out;
c = calloc(1, sizeof(*c));
memcpy(c->macaddr, mac, ETH_ALEN);
c->node.key = c->macaddr;
avl_insert(&cache, &c->node);
if (!gc_timer.pending)
uloop_timeout_set(&gc_timer, CACHE_TIMEOUT * 1000);
}
out:
c->time = client_gettime();
return c;
}
void client_set_ipaddr(const void *mac, const void *addr, bool ipv6)
{
struct interface *iface;
struct cache_entry *c;
struct client *cl;
c = client_get_cache_entry(mac);
if (!ipv6 && !c->ip4addr)
memcpy(&c->ip4addr, addr, sizeof(c->ip4addr));
else if (ipv6 && !c->ip6addr[0])
@@ -212,8 +228,6 @@ void client_set_ipaddr(const void *mac, const void *addr, bool ipv6)
else
return;
c->time = client_gettime();
avl_for_each_element(&interfaces, iface, node) {
cl = avl_find_element(&iface->clients, mac, cl, node);
if (!cl)
@@ -231,3 +245,21 @@ void client_set_ipaddr(const void *mac, const void *addr, bool ipv6)
spotfilter_bpf_set_client(iface, &cl->key, &cl->data);
}
}
void client_set_arp_ipaddr(const void *mac, const void *addr)
{
struct interface *iface;
struct cache_entry *c;
struct client *cl;
c = client_get_cache_entry(mac);
memcpy(&c->arp_ip4addr, addr, sizeof(c->arp_ip4addr));
avl_for_each_element(&interfaces, iface, node) {
cl = avl_find_element(&iface->clients, mac, cl, node);
if (!cl)
continue;
cl->arp_ip4addr = c->arp_ip4addr;
}
}

View File

@@ -15,6 +15,7 @@ struct client {
struct kvlist kvdata;
int idle;
uint32_t arp_ip4addr;
struct spotfilter_client_key key;
struct spotfilter_client_data data;
char *device;
@@ -25,6 +26,7 @@ int client_set(struct interface *iface, const void *addr, const char *id,
const char *device, bool flush);
void client_free(struct interface *iface, struct client *cl);
void client_set_ipaddr(const void *mac, const void *addr, bool ipv6);
void client_set_arp_ipaddr(const void *mac, const void *addr);
void client_init_interface(struct interface *iface);
#endif

View File

@@ -9,6 +9,7 @@
#include <netinet/udp.h>
#include <netpacket/packet.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/types.h>
@@ -48,6 +49,20 @@ int spotfilter_ifb_ifindex;
static struct uloop_fd ufd;
static struct uloop_timeout cname_gc_timer;
struct arp_packet {
uint16_t hwtype;
uint16_t proto;
uint8_t h_size;
uint8_t p_size;
uint16_t op;
uint8_t src_mac[ETH_ALEN];
uint8_t src_ip[4];
uint8_t dest_mac[ETH_ALEN];
uint8_t dest_ip[4];
};
struct vlan_hdr {
uint16_t tci;
uint16_t proto;
@@ -395,6 +410,7 @@ spotfilter_packet_cb(struct packet *pkt)
struct ip6_hdr *ip6;
struct ip *ip;
struct udphdr *udp;
struct arp_packet *arp;
bool ipv4;
eth = pkt_pull(pkt, sizeof(*eth));
@@ -413,6 +429,19 @@ spotfilter_packet_cb(struct packet *pkt)
}
switch (proto) {
case ETH_P_ARP:
arp = pkt_peek(pkt, sizeof(*arp));
if (!arp)
break;
if (arp->hwtype != cpu_to_be16(1) ||
arp->proto != cpu_to_be16(0x800) ||
arp->h_size != 6 || arp->p_size != 4 ||
arp->op != cpu_to_be16(1))
break;
client_set_arp_ipaddr(eth->h_source, &arp->src_ip);
break;
case ETH_P_IP:
ip = pkt_peek(pkt, sizeof(struct ip));
if (!ip)

View File

@@ -227,6 +227,9 @@ int spotfilter_in(struct __sk_buff *skb)
if (!is_control)
wl_val = bpf_map_lookup_elem(&whitelist_ipv6, &ip6h->daddr);
} else if (info.proto == bpf_htons(ETH_P_ARP)) {
bpf_clone_redirect(skb, config.snoop_ifindex, BPF_F_INGRESS);
return TC_ACT_UNSPEC;
} else {
return TC_ACT_UNSPEC;
}

View File

@@ -257,6 +257,12 @@ static void client_dump(struct interface *iface, struct client *cl)
if (cl->id_node.key)
blobmsg_add_string(&b, "id", (const char *)cl->id_node.key);
if (cl->arp_ip4addr) {
buf = blobmsg_alloc_string_buffer(&b, "arp_ip4addr", INET6_ADDRSTRLEN);
inet_ntop(AF_INET, (const void *)&cl->arp_ip4addr, buf, INET6_ADDRSTRLEN);
blobmsg_add_string_buffer(&b);
}
if (cl->data.ip4addr) {
buf = blobmsg_alloc_string_buffer(&b, "ip4addr", INET6_ADDRSTRLEN);
inet_ntop(AF_INET, (const void *)&cl->data.ip4addr, buf, INET6_ADDRSTRLEN);