Files
wlan-ap/feeds/ucentral/udevmand/src/netdev.c
John Crispin cbf0e536df udevmand: move the code directly into the repo
Signed-off-by: John Crispin <john@phrozen.org>
2025-09-07 10:04:06 +02:00

236 lines
5.9 KiB
C

#include "udevmand.h"
struct avl_tree iface_tree = AVL_TREE_INIT(iface_tree, avl_strcmp, false, NULL);
struct iface_ip {
struct list_head list;
char ip[];
};
struct iface {
struct avl_node avl;
int alive;
char name[IF_NAMESIZE];
struct rtnl_link_stats stats;
struct rtnl_link_stats stats_old;
struct list_head ipv6, ipv4;
uint8_t addr[ETH_ALEN];
};
static struct iface*
iface_find(char *name)
{
struct iface *iface = avl_find_element(&iface_tree, name, iface, avl);
if (iface) {
iface->alive = 1;
return iface;
}
iface = malloc(sizeof(*iface));
if (!iface)
return NULL;
memset(iface, 0, sizeof(*iface));
iface->alive = 1;
iface->avl.key = strcpy(iface->name, name);
INIT_LIST_HEAD(&iface->ipv4);
INIT_LIST_HEAD(&iface->ipv6);
avl_insert(&iface_tree, &iface->avl);
return iface;
}
void
iface_done(void)
{
struct iface *i, *t;
avl_for_each_element_safe(&iface_tree, i, avl, t) {
avl_delete(&iface_tree, &i->avl);
free(i);
}
}
static void
iface_get(void)
{
int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
struct ifaddrs *ifaddr, *ifa;
int n;
if (getifaddrs(&ifaddr) == -1) {
ULOG_ERR("failed to getifaddrs\n");
return;
}
for (ifa = ifaddr, n = 0; ifa != NULL; ifa = ifa->ifa_next, n++) {
char host[NI_MAXHOST];
struct iface *iface;
int family, s;
if (ifa->ifa_addr == NULL)
continue;
if ((ifa->ifa_flags & IFF_UP) == 0)
continue;
if (ifa->ifa_flags & IFF_LOOPBACK)
continue;
family = ifa->ifa_addr->sa_family;
iface = iface_find(ifa->ifa_name);
if (!iface)
continue;
if (family == AF_INET || family == AF_INET6) {
struct iface_ip *ip;
s = getnameinfo(ifa->ifa_addr,
(family == AF_INET) ? sizeof(struct sockaddr_in) :
sizeof(struct sockaddr_in6),
host, NI_MAXHOST,
NULL, 0, NI_NUMERICHOST);
if (s)
continue;
ip = malloc(sizeof(*ip) + strlen(host) + 1);
if (!ip)
continue;
strcpy(ip->ip, host);
if (family == AF_INET)
list_add(&ip->list, &iface->ipv4);
else
list_add(&ip->list, &iface->ipv6);
} else if (family == AF_PACKET && ifa->ifa_data != NULL) {
struct rtnl_link_stats *stats = ifa->ifa_data;
struct ifreq s;
memcpy(&iface->stats, stats, sizeof(*stats));
memset(iface->addr, 0, ETH_ALEN);
if (fd) {
strcpy(s.ifr_name, ifa->ifa_name);
if (!ioctl(fd, SIOCGIFHWADDR, &s))
memcpy(iface->addr, s.ifr_addr.sa_data, ETH_ALEN);
}
}
}
freeifaddrs(ifaddr);
if (fd)
close(fd);
}
static void
iface_flush(void)
{
struct iface *iface, *tmp;
avl_for_each_element_safe(&iface_tree, iface, avl, tmp) {
struct iface_ip *ip, *tmp;
list_for_each_entry_safe(ip, tmp, &iface->ipv4, list) {
list_del(&ip->list);
free(ip);
}
list_for_each_entry_safe(ip, tmp, &iface->ipv6, list) {
list_del(&ip->list);
free(ip);
}
if (!iface->alive) {
avl_delete(&iface_tree, &iface->avl);
free(iface);
} else {
iface->alive = 0;
}
}
}
static int get_counter_delta(__u32 new, __u32 old)
{
uint32_t delta;
if (new < old)
delta = UINT32_MAX - old + new;
else
delta = new - old;
return delta > 0 ? delta : 0;
}
void
iface_dump(int delta)
{
struct iface *iface;
iface_get();
blob_buf_init(&b, 0);
avl_for_each_element(&iface_tree, iface, avl) {
struct iface_ip *ip;
void *c, *d;
if (!iface->alive)
continue;
c = blobmsg_open_table(&b, interface_resolve(iface->name));
blobmsg_add_mac(&b, "hwaddr", iface->addr);
if (!list_empty(&iface->ipv4)) {
d = blobmsg_open_array(&b, "ipv4");
list_for_each_entry(ip, &iface->ipv4, list)
blobmsg_add_string(&b, NULL, ip->ip);
blobmsg_close_array(&b, d);
}
if (!list_empty(&iface->ipv4)) {
d = blobmsg_open_array(&b, "ipv6");
list_for_each_entry(ip, &iface->ipv6, list)
blobmsg_add_string(&b, NULL, ip->ip);
blobmsg_close_array(&b, d);
}
d = blobmsg_open_table(&b, "counters");
blobmsg_add_u64(&b, "rx_packets", iface->stats.rx_packets);
blobmsg_add_u64(&b, "tx_packets", iface->stats.tx_packets);
blobmsg_add_u64(&b, "rx_bytes", iface->stats.rx_bytes);
blobmsg_add_u64(&b, "tx_bytes", iface->stats.tx_bytes);
blobmsg_add_u64(&b, "rx_errors", iface->stats.rx_errors);
blobmsg_add_u64(&b, "tx_errors", iface->stats.tx_errors);
blobmsg_add_u64(&b, "rx_dropped", iface->stats.rx_dropped);
blobmsg_add_u64(&b, "tx_dropped", iface->stats.tx_dropped);
blobmsg_add_u64(&b, "multicast", iface->stats.multicast);
blobmsg_add_u64(&b, "collisions", iface->stats.collisions);
blobmsg_close_table(&b, d);
if (delta) {
d = blobmsg_open_table(&b, "deltas");
blobmsg_add_u32(&b, "rx_packets",
get_counter_delta(iface->stats.rx_packets, iface->stats_old.rx_packets));
blobmsg_add_u32(&b, "tx_packets",
get_counter_delta(iface->stats.tx_packets, iface->stats_old.tx_packets));
blobmsg_add_u32(&b, "rx_bytes",
get_counter_delta(iface->stats.rx_bytes, iface->stats_old.rx_bytes));
blobmsg_add_u32(&b, "tx_bytes",
get_counter_delta(iface->stats.tx_bytes, iface->stats_old.tx_bytes));
blobmsg_add_u32(&b, "rx_errors",
get_counter_delta(iface->stats.rx_errors, iface->stats_old.rx_errors));
blobmsg_add_u32(&b, "tx_errors",
get_counter_delta(iface->stats.tx_errors, iface->stats_old.tx_errors));
blobmsg_add_u32(&b, "rx_dropped",
get_counter_delta(iface->stats.rx_dropped, iface->stats_old.rx_dropped));
blobmsg_add_u32(&b, "tx_dropped",
get_counter_delta(iface->stats.tx_dropped, iface->stats_old.tx_dropped));
blobmsg_add_u32(&b, "multicast",
get_counter_delta(iface->stats.multicast, iface->stats_old.multicast));
blobmsg_add_u32(&b, "collisions",
get_counter_delta(iface->stats.collisions, iface->stats_old.collisions));
blobmsg_close_table(&b, d);
}
memcpy(&iface->stats_old, &iface->stats, sizeof(iface->stats));
bridge_dump_if(iface->name);
blobmsg_close_table(&b, c);
}
iface_flush();
}