3
0
mirror of https://git.kernel.org/pub/scm/network/wireless/iwd.git synced 2024-11-07 12:39:23 +01:00
iwd/src/ip-pool.c
Andrew Zaborowski 6e5b26ba64 ip-pool: Track IPv4 addresses in use
Add the ip-pool submodule that tracks IPv4 addresses in use on the
system for use when selecting the address for a new AP.  l_rtnl_address
is used internally because if we're going to return l_rtnl_address
objects it would be misleading if we didn't fill in all of their
properties like flags etc.
2021-06-01 10:03:00 -05:00

176 lines
4.2 KiB
C

/*
*
* Wireless daemon for Linux
*
* Copyright (C) 2021 Intel Corporation. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <errno.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <ell/ell.h>
#include "src/util.h"
#include "src/iwd.h"
#include "src/module.h"
#include "src/netdev.h"
#include "src/ip-pool.h"
struct ip_pool_addr4_record {
uint32_t ifindex;
struct l_rtnl_address *addr;
};
static struct l_queue *used_addr4_list;
static struct l_netlink *rtnl;
static void ip_pool_addr4_record_free(void *data)
{
struct ip_pool_addr4_record *rec = data;
l_rtnl_address_free(rec->addr);
l_free(rec);
}
static bool ip_pool_addr4_match_ifindex(const void *a, const void *b)
{
const struct ip_pool_addr4_record *addr = a;
return addr->ifindex == L_PTR_TO_UINT(b);
}
struct l_rtnl_address *ip_pool_get_addr4(uint32_t ifindex)
{
const struct ip_pool_addr4_record *rec =
l_queue_find(used_addr4_list, ip_pool_addr4_match_ifindex,
L_UINT_TO_PTR(ifindex));
return rec ? l_rtnl_address_clone(rec->addr) : 0;
}
static bool ip_pool_addr4_match_free(void *data, void *user_data)
{
const struct ip_pool_addr4_record *a = data;
const struct ip_pool_addr4_record *b = user_data;
char a_addr_str[INET_ADDRSTRLEN];
char b_addr_str[INET_ADDRSTRLEN];
if (a->ifindex != b->ifindex ||
l_rtnl_address_get_prefix_length(a->addr) !=
l_rtnl_address_get_prefix_length(b->addr))
return false;
if (!l_rtnl_address_get_address(a->addr, a_addr_str) ||
!l_rtnl_address_get_address(b->addr, b_addr_str) ||
strcmp(a_addr_str, b_addr_str))
return false;
ip_pool_addr4_record_free(data);
return true;
}
static void ip_pool_addr_notify(uint16_t type, const void *data, uint32_t len,
void *user_data)
{
const struct ifaddrmsg *ifa = data;
struct l_rtnl_address *addr;
if (ifa->ifa_family != AF_INET || ifa->ifa_prefixlen < 1)
return;
len -= NLMSG_ALIGN(sizeof(struct ifaddrmsg));
addr = l_rtnl_ifaddr_extract(ifa, len);
if (!addr)
return;
if (type == RTM_NEWADDR) {
struct ip_pool_addr4_record *rec;
rec = l_new(struct ip_pool_addr4_record, 1);
rec->ifindex = ifa->ifa_index;
rec->addr = l_steal_ptr(addr);
l_queue_push_tail(used_addr4_list, rec);
} else if (type == RTM_DELADDR) {
struct ip_pool_addr4_record rec;
rec.ifindex = ifa->ifa_index;
rec.addr = addr;
l_queue_foreach_remove(used_addr4_list,
ip_pool_addr4_match_free, &rec);
}
l_rtnl_address_free(addr);
}
static void ip_pool_addr4_dump_cb(int error,
uint16_t type, const void *data,
uint32_t len, void *user_data)
{
if (error) {
l_error("addr4_dump_cb: %s (%i)", strerror(-error), -error);
return;
}
ip_pool_addr_notify(type, data, len, user_data);
}
static int ip_pool_init(void)
{
const struct l_settings *settings = iwd_get_config();
bool netconfig_enabled;
if (!l_settings_get_bool(settings, "General",
"EnableNetworkConfiguration",
&netconfig_enabled))
netconfig_enabled = false;
if (!netconfig_enabled)
return 0;
rtnl = iwd_get_rtnl();
if (!l_netlink_register(rtnl, RTNLGRP_IPV4_IFADDR,
ip_pool_addr_notify, NULL, NULL)) {
l_error("Failed to register for RTNL link notifications");
return -EIO;
}
if (!l_rtnl_ifaddr4_dump(rtnl, ip_pool_addr4_dump_cb, NULL, NULL)) {
l_error("Sending the IPv4 addr dump req failed");
return -EIO;
}
used_addr4_list = l_queue_new();
return 0;
}
static void ip_pool_exit(void)
{
l_queue_destroy(used_addr4_list, ip_pool_addr4_record_free);
used_addr4_list = NULL;
}
IWD_MODULE(ip_pool, ip_pool_init, ip_pool_exit)