From a619356b5f21bfe3c13f1576eb1d16c015532ceb Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 25 Jul 2023 10:21:40 +0200 Subject: [PATCH] kernel-pfroute: Maintain virtual flag when repopulating interface addrs When adding a virtual IP on a TUN interface, the interface might get activated (in terms of receiving the event) after we've already set the virtual flag for the added address. As the activation repopulates the addresses on the interface, this cleared the flag and the address would no longer be treated as virtual IP when installing routes for CHILD_SAs that reference it in their local traffic selectors. Closes strongswan/strongswan#1807 --- .../kernel_pfroute/kernel_pfroute_net.c | 57 +++++++++++++++---- 1 file changed, 47 insertions(+), 10 deletions(-) diff --git a/src/libcharon/plugins/kernel_pfroute/kernel_pfroute_net.c b/src/libcharon/plugins/kernel_pfroute/kernel_pfroute_net.c index b933b7c92ed..02a8b59253c 100644 --- a/src/libcharon/plugins/kernel_pfroute/kernel_pfroute_net.c +++ b/src/libcharon/plugins/kernel_pfroute/kernel_pfroute_net.c @@ -780,20 +780,46 @@ static void process_addr(private_kernel_pfroute_net_t *this, } } +/** + * Check if the address is already known so we can keep it as-is with the + * virtual flag. Destroys the given host_t object if it is found. + */ +static bool existing_addr_entry(iface_entry_t *iface, linked_list_t *addrs, + host_t *ip) +{ + enumerator_t *enumerator; + addr_entry_t *addr; + bool found = FALSE; + + enumerator = addrs->create_enumerator(addrs); + while (enumerator->enumerate(enumerator, &addr)) + { + if (ip->ip_equals(ip, addr->ip)) + { + ip->destroy(ip); + addrs->remove_at(addrs, enumerator); + iface->addrs->insert_last(iface->addrs, addr); + found = TRUE; + break; + } + } + enumerator->destroy(enumerator); + return found; +} + /** * Re-initialize address list of an interface if it changes state */ static void repopulate_iface(private_kernel_pfroute_net_t *this, iface_entry_t *iface) { + linked_list_t *addrs; struct ifaddrs *ifap, *ifa; addr_entry_t *addr; + host_t *ip; - while (iface->addrs->remove_last(iface->addrs, (void**)&addr) == SUCCESS) - { - addr_map_entry_remove(addr, iface, this); - addr_entry_destroy(addr); - } + addrs = iface->addrs; + iface->addrs = linked_list_create(); if (getifaddrs(&ifap) == 0) { @@ -805,11 +831,15 @@ static void repopulate_iface(private_kernel_pfroute_net_t *this, { case AF_INET: case AF_INET6: - INIT(addr, - .ip = host_create_from_sockaddr(ifa->ifa_addr), - ); - iface->addrs->insert_last(iface->addrs, addr); - addr_map_entry_add(this, addr, iface); + ip = host_create_from_sockaddr(ifa->ifa_addr); + if (!existing_addr_entry(iface, addrs, ip)) + { + INIT(addr, + .ip = ip, + ); + iface->addrs->insert_last(iface->addrs, addr); + addr_map_entry_add(this, addr, iface); + } break; default: break; @@ -818,6 +848,13 @@ static void repopulate_iface(private_kernel_pfroute_net_t *this, } freeifaddrs(ifap); } + + while (addrs->remove_last(addrs, (void**)&addr) == SUCCESS) + { + addr_map_entry_remove(addr, iface, this); + addr_entry_destroy(addr); + } + addrs->destroy(addrs); } /**