From ae9d1b64c3356119b56eb30f36769ac0bd12b4ee Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Tue, 30 Apr 2024 14:36:08 +0200 Subject: [PATCH] spotfilter: discover ip address via ARP Signed-off-by: Felix Fietkau --- feeds/ucentral/spotfilter/src/client.c | 58 ++++++++++++++----- feeds/ucentral/spotfilter/src/client.h | 2 + feeds/ucentral/spotfilter/src/snoop.c | 29 ++++++++++ .../ucentral/spotfilter/src/spotfilter-bpf.c | 5 +- feeds/ucentral/spotfilter/src/ubus.c | 6 ++ 5 files changed, 86 insertions(+), 14 deletions(-) diff --git a/feeds/ucentral/spotfilter/src/client.c b/feeds/ucentral/spotfilter/src/client.c index ee35316e8..591afc86b 100644 --- a/feeds/ucentral/spotfilter/src/client.c +++ b/feeds/ucentral/spotfilter/src/client.c @@ -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 cache_entry *c; + + c = avl_find_element(&cache, mac, c, node); + 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 = avl_find_element(&cache, mac, c, node); - if (!c) { - 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); - } - + 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; + } +} diff --git a/feeds/ucentral/spotfilter/src/client.h b/feeds/ucentral/spotfilter/src/client.h index f9e6cc6f9..c22acff5c 100644 --- a/feeds/ucentral/spotfilter/src/client.h +++ b/feeds/ucentral/spotfilter/src/client.h @@ -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 diff --git a/feeds/ucentral/spotfilter/src/snoop.c b/feeds/ucentral/spotfilter/src/snoop.c index 266de5236..c378aecd1 100644 --- a/feeds/ucentral/spotfilter/src/snoop.c +++ b/feeds/ucentral/spotfilter/src/snoop.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -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) diff --git a/feeds/ucentral/spotfilter/src/spotfilter-bpf.c b/feeds/ucentral/spotfilter/src/spotfilter-bpf.c index 26b04919b..45b2d5480 100644 --- a/feeds/ucentral/spotfilter/src/spotfilter-bpf.c +++ b/feeds/ucentral/spotfilter/src/spotfilter-bpf.c @@ -227,8 +227,11 @@ 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; + return TC_ACT_UNSPEC; } if (wl_val) { diff --git a/feeds/ucentral/spotfilter/src/ubus.c b/feeds/ucentral/spotfilter/src/ubus.c index 29ea0fa9f..35ed087d8 100644 --- a/feeds/ucentral/spotfilter/src/ubus.c +++ b/feeds/ucentral/spotfilter/src/ubus.c @@ -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);