From 9b0838d07c2fce7eb9dd348fea1f34776bdacccc Mon Sep 17 00:00:00 2001 From: Tim Kourt Date: Tue, 25 Jun 2019 15:20:54 -0700 Subject: [PATCH] netconfig: Subscribe for IPv4 link address notifications iwd keeps track of the addresses assigned to the managed interfaces. The list of assigned IPv4/IPv6 addresses is stored in ifaddr_list inside of netconfig. The tracking of the IP addresses will help to remove them from an interface once they are no longer valid. --- src/netconfig.c | 147 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) diff --git a/src/netconfig.c b/src/netconfig.c index 64d1f9fd..5c699796 100644 --- a/src/netconfig.c +++ b/src/netconfig.c @@ -30,18 +30,47 @@ #include #include "src/iwd.h" +#include "src/rtnlutil.h" #include "src/netconfig.h" struct netconfig { uint32_t ifindex; + struct l_queue *ifaddr_list; }; +struct netconfig_ifaddr { + uint8_t family; + uint8_t prefix_len; + char *ip; + char *broadcast; +}; + +static struct l_netlink *rtnl; static struct l_queue *netconfig_list; +static void do_debug(const char *str, void *user_data) +{ + const char *prefix = user_data; + + l_info("%s%s", prefix, str); +} + +static void netconfig_ifaddr_destroy(void *data) +{ + struct netconfig_ifaddr *ifaddr = data; + + l_free(ifaddr->ip); + l_free(ifaddr->broadcast); + + l_free(ifaddr); +} + static void netconfig_destroy(void *data) { struct netconfig *netconfig = data; + l_queue_destroy(netconfig->ifaddr_list, netconfig_ifaddr_destroy); + l_free(netconfig); } @@ -73,6 +102,99 @@ static struct netconfig *netconfig_find(uint32_t ifindex) return NULL; } +static struct netconfig_ifaddr *netconfig_ifaddr_find( + const struct netconfig *netconfig, + uint8_t family, uint8_t prefix_len, + const char *ip) +{ + const struct l_queue_entry *entry; + + for (entry = l_queue_get_entries(netconfig->ifaddr_list); entry; + entry = entry->next) { + struct netconfig_ifaddr *ifaddr = entry->data; + + if (ifaddr->family != family) + continue; + + if (ifaddr->prefix_len != prefix_len) + continue; + + if (strcmp(ifaddr->ip, ip)) + continue; + + return ifaddr; + } + + return NULL; +} + +static void netconfig_ifaddr_added(struct netconfig *netconfig, + const struct ifaddrmsg *ifa, int bytes) +{ + struct netconfig_ifaddr *ifaddr; + char *label; + + ifaddr = l_new(struct netconfig_ifaddr, 1); + ifaddr->family = ifa->ifa_family; + ifaddr->prefix_len = ifa->ifa_prefixlen; + + rtnl_ifaddr_extract(ifa, bytes, &label, &ifaddr->ip, + &ifaddr->broadcast); + + l_debug("%s: ifaddr %s/%u broadcast %s", label, ifaddr->ip, + ifaddr->prefix_len, ifaddr->broadcast); + l_free(label); + + l_queue_push_tail(netconfig->ifaddr_list, ifaddr); +} + +static void netconfig_ifaddr_deleted(struct netconfig *netconfig, + const struct ifaddrmsg *ifa, int bytes) +{ + struct netconfig_ifaddr *ifaddr; + char *ip; + + rtnl_ifaddr_extract(ifa, bytes, NULL, &ip, NULL); + + ifaddr = netconfig_ifaddr_find(netconfig, ifa->ifa_family, + ifa->ifa_prefixlen, ip); + + l_free(ip); + + if (!ifaddr) + return; + + l_debug("ifaddr %s/%u", ifaddr->ip, ifaddr->prefix_len); + + l_queue_remove(netconfig->ifaddr_list, ifaddr); + + netconfig_ifaddr_destroy(ifaddr); +} + +static void netconfig_ifaddr_notify(uint16_t type, const void *data, + uint32_t len, void *user_data) +{ + const struct ifaddrmsg *ifa = data; + struct netconfig *netconfig; + unsigned int bytes; + + netconfig = netconfig_find(ifa->ifa_index); + if (!netconfig) + /* Ignore the interfaces which aren't managed by iwd. */ + return; + + bytes = len - NLMSG_ALIGN(sizeof(struct ifaddrmsg)); + + switch (type) { + case RTM_NEWADDR: + netconfig_ifaddr_added(netconfig, ifa, bytes); + break; + case RTM_DELADDR: + netconfig_ifaddr_deleted(netconfig, ifa, bytes); + break; + } +} + bool netconfig_ifindex_add(uint32_t ifindex) { struct netconfig *netconfig; @@ -88,6 +210,7 @@ bool netconfig_ifindex_add(uint32_t ifindex) netconfig = l_new(struct netconfig, 1); netconfig->ifindex = ifindex; + netconfig->ifaddr_list = l_queue_new(); l_queue_push_tail(netconfig_list, netconfig); @@ -116,6 +239,7 @@ bool netconfig_ifindex_remove(uint32_t ifindex) static int netconfig_init(void) { bool enabled; + uint32_t r; if (netconfig_list) return -EALREADY; @@ -128,6 +252,26 @@ static int netconfig_init(void) return false; } + rtnl = l_netlink_new(NETLINK_ROUTE); + if (!rtnl) { + l_error("netconfig: Failed to open route netlink socket"); + return -EPERM; + } + + if (getenv("IWD_RTNL_DEBUG")) + l_netlink_set_debug(rtnl, do_debug, "[NETCONFIG RTNL] ", NULL); + + r = l_netlink_register(rtnl, RTNLGRP_IPV4_IFADDR, + netconfig_ifaddr_notify, NULL, NULL); + if (!r) { + l_error("netconfig: Failed to register for RTNL link address" + " notifications."); + l_netlink_destroy(rtnl); + rtnl = NULL; + + return r; + } + netconfig_list = l_queue_new(); return 0; @@ -138,6 +282,9 @@ static void netconfig_exit(void) if (!netconfig_list) return; + l_netlink_destroy(rtnl); + rtnl = NULL; + l_queue_destroy(netconfig_list, netconfig_destroy); }