3
0
mirror of https://git.kernel.org/pub/scm/network/wireless/iwd.git synced 2024-10-28 07:39:23 +01:00
iwd/src/netconfig.c
Andrew Zaborowski bafd604834 netconfig: Implement netconfig_get_dhcp_server_ipv4
This uses l_dhcp_lease_get_server_id to get the IP of the server that
offered us our current lease.  l_dhcp_lease_get_server_id returns the
vaue of the L_DHCP_OPTION_SERVER_IDENTIFIER option, which is the address
that any unicast DHCP frames are supposed to be sent to so it seems to
be the best way to get the P2P group owner's IP address as a P2P-client.
2020-07-13 14:52:02 -05:00

1273 lines
28 KiB
C

/*
*
* Wireless daemon for Linux
*
* Copyright (C) 2019 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 <arpa/inet.h>
#include <net/if_arp.h>
#include <netinet/if_ether.h>
#include <netinet/in.h>
#include <linux/rtnetlink.h>
#include <ell/ell.h>
#include "src/iwd.h"
#include "src/module.h"
#include "src/netdev.h"
#include "src/station.h"
#include "src/common.h"
#include "src/network.h"
#include "src/resolve.h"
#include "src/netconfig.h"
struct netconfig {
uint32_t ifindex;
struct l_dhcp_client *dhcp_client;
struct l_queue *ifaddr_list;
uint8_t rtm_protocol;
uint8_t rtm_v6_protocol;
const struct l_settings *active_settings;
netconfig_notify_func_t notify;
void *user_data;
};
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;
/*
* Routing priority offset, configurable in main.conf. The route with lower
* priority offset is preferred.
*/
static uint32_t ROUTE_PRIORITY_OFFSET;
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_free(void *data)
{
struct netconfig *netconfig = data;
l_dhcp_client_destroy(netconfig->dhcp_client);
l_queue_destroy(netconfig->ifaddr_list, netconfig_ifaddr_destroy);
l_free(netconfig);
}
static struct netconfig *netconfig_find(uint32_t ifindex)
{
const struct l_queue_entry *entry;
for (entry = l_queue_get_entries(netconfig_list); entry;
entry = entry->next) {
struct netconfig *netconfig = entry->data;
if (netconfig->ifindex != ifindex)
continue;
return netconfig;
}
return NULL;
}
static struct netconfig_ifaddr *netconfig_ipv4_get_ifaddr(
struct netconfig *netconfig,
uint8_t proto)
{
const struct l_dhcp_lease *lease;
struct netconfig_ifaddr *ifaddr;
struct in_addr in_addr;
char *netmask;
char *ip;
switch (proto) {
case RTPROT_STATIC:
ip = l_settings_get_string(netconfig->active_settings, "IPv4",
"Address");
if (!ip) {
ip = l_settings_get_string(netconfig->active_settings,
"IPv4", "ip");
if (!ip)
return NULL;
}
ifaddr = l_new(struct netconfig_ifaddr, 1);
ifaddr->ip = ip;
netmask = l_settings_get_string(netconfig->active_settings,
"IPv4", "Netmask");
if (!netmask)
netmask = l_settings_get_string(
netconfig->active_settings,
"IPv4", "netmask");
if (netmask && inet_pton(AF_INET, netmask, &in_addr) > 0)
ifaddr->prefix_len = __builtin_popcountl(
L_BE32_TO_CPU(in_addr.s_addr));
else
ifaddr->prefix_len = 24;
l_free(netmask);
ifaddr->broadcast =
l_settings_get_string(netconfig->active_settings,
"IPv4", "Broadcast");
if (!ifaddr->broadcast)
ifaddr->broadcast =
l_settings_get_string(
netconfig->active_settings,
"IPv4", "broadcast");
ifaddr->family = AF_INET;
return ifaddr;
case RTPROT_DHCP:
lease = l_dhcp_client_get_lease(netconfig->dhcp_client);
if (!lease)
return NULL;
ip = l_dhcp_lease_get_address(lease);
if (!ip)
return NULL;
ifaddr = l_new(struct netconfig_ifaddr, 1);
ifaddr->ip = ip;
netmask = l_dhcp_lease_get_netmask(lease);
if (netmask && inet_pton(AF_INET, netmask, &in_addr) > 0)
ifaddr->prefix_len = __builtin_popcountl(
L_BE32_TO_CPU(in_addr.s_addr));
else
ifaddr->prefix_len = 24;
l_free(netmask);
ifaddr->broadcast = l_dhcp_lease_get_broadcast(lease);
ifaddr->family = AF_INET;
return ifaddr;
}
return NULL;
}
static char *netconfig_ipv4_get_gateway(struct netconfig *netconfig)
{
const struct l_dhcp_lease *lease;
char *gateway;
switch (netconfig->rtm_protocol) {
case RTPROT_STATIC:
gateway = l_settings_get_string(netconfig->active_settings,
"IPv4", "Gateway");
if (!gateway)
gateway = l_settings_get_string(
netconfig->active_settings,
"IPv4", "gateway");
return gateway;
case RTPROT_DHCP:
lease = l_dhcp_client_get_lease(netconfig->dhcp_client);
if (!lease)
return NULL;
return l_dhcp_lease_get_gateway(lease);
}
return NULL;
}
static char **netconfig_ipv4_get_dns(struct netconfig *netconfig, uint8_t proto)
{
const struct l_dhcp_lease *lease;
struct in_addr in_addr;
char **dns_list;
dns_list = l_settings_get_string_list(netconfig->active_settings,
"IPv4", "DNS", ' ');
if (!dns_list)
dns_list = l_settings_get_string_list(
netconfig->active_settings,
"IPv4", "dns", ' ');
if (dns_list && *dns_list) {
char **p;
for (p = dns_list; *p; p++) {
if (inet_pton(AF_INET, *p, &in_addr) == 1)
continue;
l_error("netconfig: Invalid IPv4 DNS address '%s' is "
"provided in network configuration file.", *p);
l_strv_free(dns_list);
return NULL;
}
/* Allow to override the DHCP DNSs with static addressing. */
return dns_list;
} else if (dns_list) {
l_error("netconfig: No IPv4 DNS address is provided in network "
"configuration file.");
l_strv_free(dns_list);
return NULL;
}
if (proto == RTPROT_DHCP) {
lease = l_dhcp_client_get_lease(netconfig->dhcp_client);
if (!lease)
return NULL;
return l_dhcp_lease_get_dns(lease);
}
return NULL;
}
static char *netconfig_ipv4_get_domain_name(struct netconfig *netconfig,
uint8_t proto)
{
const struct l_dhcp_lease *lease;
char *domain_name;
domain_name = l_settings_get_string(netconfig->active_settings,
"IPv4", "DomainName");
if (domain_name)
/* Allow to override the DHCP domain name with setting entry. */
return domain_name;
if (proto != RTPROT_DHCP)
return NULL;
lease = l_dhcp_client_get_lease(netconfig->dhcp_client);
if (!lease)
return NULL;
return l_dhcp_lease_get_domain_name(lease);
}
static struct netconfig_ifaddr *netconfig_ipv6_get_ifaddr(
struct netconfig *netconfig,
uint8_t proto)
{
struct in6_addr in6_addr;
struct netconfig_ifaddr *ifaddr;
char *ip;
char *p;
switch (proto) {
case RTPROT_STATIC:
ip = l_settings_get_string(netconfig->active_settings, "IPv6",
"Address");
if (!ip) {
ip = l_settings_get_string(netconfig->active_settings,
"IPv6", "ip");
if (!ip)
return NULL;
}
ifaddr = l_new(struct netconfig_ifaddr, 1);
ifaddr->ip = ip;
p = strrchr(ifaddr->ip, '/');
if (!p)
goto no_prefix_len;
*p = '\0';
if (inet_pton(AF_INET6, ifaddr->ip, &in6_addr) < 1) {
l_error("netconfig: Invalid IPv6 address %s is "
"provided in network configuration file.",
ifaddr->ip);
netconfig_ifaddr_destroy(ifaddr);
return NULL;
}
if (*++p == '\0')
goto no_prefix_len;
ifaddr->prefix_len = strtoul(p, NULL, 10);
if (!unlikely(errno == EINVAL || errno == ERANGE ||
!ifaddr->prefix_len ||
ifaddr->prefix_len > 128))
goto proceed;
no_prefix_len:
ifaddr->prefix_len = 128;
proceed:
ifaddr->family = AF_INET6;
return ifaddr;
case RTPROT_DHCP:
/* TODO */
return NULL;
}
return NULL;
}
static char *netconfig_ipv6_get_gateway(struct netconfig *netconfig)
{
struct in6_addr in6_addr;
char *gateway;
switch (netconfig->rtm_v6_protocol) {
case RTPROT_STATIC:
gateway = l_settings_get_string(netconfig->active_settings,
"IPv6", "Gateway");
if (!gateway)
gateway = l_settings_get_string(
netconfig->active_settings,
"IPv6", "gateway");
if (inet_pton(AF_INET6, gateway, &in6_addr) < 1) {
l_error("netconfig: Invalid IPv6 gateway address %s is "
"provided in network configuration file.",
gateway);
l_free(gateway);
return NULL;
}
return gateway;
case RTPROT_DHCP:
/* TODO */
return NULL;
}
return NULL;
}
static char **netconfig_ipv6_get_dns(struct netconfig *netconfig, uint8_t proto)
{
struct in6_addr in6_addr;
char **dns_list;
dns_list = l_settings_get_string_list(netconfig->active_settings,
"IPv6", "DNS", ' ');
if (!dns_list)
dns_list = l_settings_get_string_list(
netconfig->active_settings,
"IPv6", "dns", ' ');
if (dns_list && *dns_list) {
char **p;
for (p = dns_list; *p; p++) {
if (inet_pton(AF_INET6, *p, &in6_addr) == 1)
continue;
l_error("netconfig: Invalid IPv6 DNS address '%s' is "
"provided in network configuration file.", *p);
l_strv_free(dns_list);
return NULL;
}
/* Allow to override the DHCP DNSs with static addressing. */
return dns_list;
} else if (dns_list) {
l_error("netconfig: No IPv6 DNS address is provided in network "
"configuration file.");
l_strv_free(dns_list);
return NULL;
}
if (proto == RTPROT_DHCP) {
/* TODO */
return NULL;
}
return NULL;
}
static bool netconfig_ifaddr_match(const void *a, const void *b)
{
const struct netconfig_ifaddr *entry = a;
const struct netconfig_ifaddr *query = b;
if (entry->family != query->family)
return false;
if (entry->prefix_len != query->prefix_len)
return false;
if (strcmp(entry->ip, query->ip))
return false;
return true;
}
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,
uint32_t len)
{
struct netconfig_ifaddr *ifaddr;
char *label;
ifaddr = l_new(struct netconfig_ifaddr, 1);
ifaddr->family = ifa->ifa_family;
ifaddr->prefix_len = ifa->ifa_prefixlen;
l_rtnl_ifaddr4_extract(ifa, len, &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,
uint32_t len)
{
struct netconfig_ifaddr *ifaddr;
struct netconfig_ifaddr query;
l_rtnl_ifaddr4_extract(ifa, len, NULL, &query.ip, NULL);
query.family = ifa->ifa_family;
query.prefix_len = ifa->ifa_prefixlen;
ifaddr = l_queue_remove_if(netconfig->ifaddr_list,
netconfig_ifaddr_match, &query);
l_free(query.ip);
if (!ifaddr)
return;
l_debug("ifaddr %s/%u", ifaddr->ip, ifaddr->prefix_len);
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;
uint32_t 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;
}
}
static void netconfig_ifaddr_cmd_cb(int error, uint16_t type,
const void *data, uint32_t len,
void *user_data)
{
if (error) {
l_error("netconfig: ifaddr command failure. "
"Error %d: %s", error, strerror(-error));
return;
}
if (type != RTM_NEWADDR)
return;
netconfig_ifaddr_notify(type, data, len, user_data);
}
static void netconfig_ifaddr_ipv6_added(struct netconfig *netconfig,
const struct ifaddrmsg *ifa,
uint32_t len)
{
struct netconfig_ifaddr *ifaddr;
ifaddr = l_new(struct netconfig_ifaddr, 1);
ifaddr->family = ifa->ifa_family;
ifaddr->prefix_len = ifa->ifa_prefixlen;
l_rtnl_ifaddr6_extract(ifa, len, &ifaddr->ip);
l_debug("ifindex %u: ifaddr %s/%u", netconfig->ifindex, ifaddr->ip,
ifaddr->prefix_len);
l_queue_push_tail(netconfig->ifaddr_list, ifaddr);
}
static void netconfig_ifaddr_ipv6_deleted(struct netconfig *netconfig,
const struct ifaddrmsg *ifa,
uint32_t len)
{
struct netconfig_ifaddr *ifaddr;
struct netconfig_ifaddr query;
l_rtnl_ifaddr6_extract(ifa, len, &query.ip);
query.family = ifa->ifa_family;
query.prefix_len = ifa->ifa_prefixlen;
ifaddr = l_queue_remove_if(netconfig->ifaddr_list,
netconfig_ifaddr_match, &query);
l_free(query.ip);
if (!ifaddr)
return;
l_debug("ifaddr %s/%u", ifaddr->ip, ifaddr->prefix_len);
netconfig_ifaddr_destroy(ifaddr);
}
static void netconfig_ifaddr_ipv6_notify(uint16_t type, const void *data,
uint32_t len, void *user_data)
{
const struct ifaddrmsg *ifa = data;
struct netconfig *netconfig;
uint32_t 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_ipv6_added(netconfig, ifa, bytes);
break;
case RTM_DELADDR:
netconfig_ifaddr_ipv6_deleted(netconfig, ifa, bytes);
break;
}
}
static void netconfig_ifaddr_ipv6_cmd_cb(int error, uint16_t type,
const void *data, uint32_t len,
void *user_data)
{
if (error) {
l_error("netconfig: ifaddr IPv6 command failure. "
"Error %d: %s", error, strerror(-error));
return;
}
if (type != RTM_NEWADDR)
return;
netconfig_ifaddr_ipv6_notify(type, data, len, user_data);
}
static void netconfig_route_add_cmd_cb(int error, uint16_t type,
const void *data, uint32_t len,
void *user_data)
{
struct netconfig *netconfig = user_data;
if (error) {
l_error("netconfig: Failed to add route. Error %d: %s",
error, strerror(-error));
return;
}
if (!netconfig->notify)
return;
netconfig->notify(NETCONFIG_EVENT_CONNECTED, netconfig->user_data);
netconfig->notify = NULL;
}
static void netconfig_route_del_cmd_cb(int error, uint16_t type,
const void *data, uint32_t len,
void *user_data)
{
if (!error)
return;
l_error("netconfig: Failed to delete route. Error %d: %s",
error, strerror(-error));
}
static bool netconfig_ipv4_routes_install(struct netconfig *netconfig,
struct netconfig_ifaddr *ifaddr)
{
L_AUTO_FREE_VAR(char *, gateway) = NULL;
struct in_addr in_addr;
char *network;
if (inet_pton(AF_INET, ifaddr->ip, &in_addr) < 1)
return false;
in_addr.s_addr = in_addr.s_addr &
htonl(0xFFFFFFFFLU << (32 - ifaddr->prefix_len));
network = inet_ntoa(in_addr);
if (!network)
return false;
if (!l_rtnl_route4_add_connected(rtnl, netconfig->ifindex,
ifaddr->prefix_len, network,
ifaddr->ip,
netconfig->rtm_protocol,
netconfig_route_add_cmd_cb,
netconfig, NULL)) {
l_error("netconfig: Failed to add subnet route.");
return false;
}
gateway = netconfig_ipv4_get_gateway(netconfig);
if (!gateway) {
l_error("netconfig: Failed to obtain gateway from %s.",
netconfig->rtm_protocol == RTPROT_STATIC ?
"setting file" : "DHCPv4 lease");
return false;
}
if (!l_rtnl_route4_add_gateway(rtnl, netconfig->ifindex, gateway,
ifaddr->ip,
ROUTE_PRIORITY_OFFSET,
netconfig->rtm_protocol,
netconfig_route_add_cmd_cb,
netconfig, NULL)) {
l_error("netconfig: Failed to add route for: %s gateway.",
gateway);
return false;
}
return true;
}
static void netconfig_ipv4_ifaddr_add_cmd_cb(int error, uint16_t type,
const void *data, uint32_t len,
void *user_data)
{
struct netconfig *netconfig = user_data;
struct netconfig_ifaddr *ifaddr;
char **dns;
char *domain_name;
if (error && error != -EEXIST) {
l_error("netconfig: Failed to add IP address. "
"Error %d: %s", error, strerror(-error));
return;
}
ifaddr = netconfig_ipv4_get_ifaddr(netconfig, netconfig->rtm_protocol);
if (!ifaddr) {
l_error("netconfig: Failed to obtain IP address from %s.",
netconfig->rtm_protocol == RTPROT_STATIC ?
"setting file" : "DHCPv4 lease");
return;
}
if (!netconfig_ipv4_routes_install(netconfig, ifaddr)) {
l_error("netconfig: Failed to install IPv4 routes.");
goto done;
}
dns = netconfig_ipv4_get_dns(netconfig, netconfig->rtm_protocol);
if (!dns) {
l_error("netconfig: Failed to obtain DNS addresses.");
goto domain_name;
}
resolve_add_dns(netconfig->ifindex, ifaddr->family, dns);
l_strv_free(dns);
domain_name:
domain_name = netconfig_ipv4_get_domain_name(netconfig,
netconfig->rtm_protocol);
if (!domain_name)
goto done;
resolve_add_domain_name(netconfig->ifindex, domain_name);
l_free(domain_name);
done:
netconfig_ifaddr_destroy(ifaddr);
}
static bool netconfig_ipv6_routes_install(struct netconfig *netconfig)
{
L_AUTO_FREE_VAR(char *, gateway) = NULL;
gateway = netconfig_ipv6_get_gateway(netconfig);
if (!gateway) {
l_error("netconfig: Failed to obtain gateway from %s.",
netconfig->rtm_v6_protocol == RTPROT_STATIC ?
"settings file" : "DHCPv6 lease");
return false;
}
if (!l_rtnl_route6_add_gateway(rtnl, netconfig->ifindex, gateway,
ROUTE_PRIORITY_OFFSET,
netconfig->rtm_v6_protocol,
netconfig_route_add_cmd_cb,
netconfig, NULL)) {
l_error("netconfig: Failed to add route for: %s gateway.",
gateway);
return false;
}
return true;
}
static void netconfig_ipv6_ifaddr_add_cmd_cb(int error, uint16_t type,
const void *data, uint32_t len,
void *user_data)
{
struct netconfig *netconfig = user_data;
char **dns;
if (error && error != -EEXIST) {
l_error("netconfig: Failed to add IPv6 address. "
"Error %d: %s", error, strerror(-error));
return;
}
if (!netconfig_ipv6_routes_install(netconfig)) {
l_error("netconfig: Failed to install IPv6 routes.");
return;
}
dns = netconfig_ipv6_get_dns(netconfig, netconfig->rtm_v6_protocol);
if (!dns) {
l_error("netconfig: Failed to obtain the DNS addresses from "
"%s.", netconfig->rtm_v6_protocol == RTPROT_STATIC ?
"setting file" : "DHCPv6 lease");
return;
}
resolve_add_dns(netconfig->ifindex, AF_INET6, dns);
l_strv_free(dns);
}
static void netconfig_install_address(struct netconfig *netconfig,
struct netconfig_ifaddr *ifaddr)
{
if (netconfig_ifaddr_find(netconfig, ifaddr->family, ifaddr->prefix_len,
ifaddr->ip))
return;
switch (ifaddr->family) {
case AF_INET:
if (l_rtnl_ifaddr4_add(rtnl, netconfig->ifindex,
ifaddr->prefix_len, ifaddr->ip,
ifaddr->broadcast,
netconfig_ipv4_ifaddr_add_cmd_cb,
netconfig, NULL))
return;
l_error("netconfig: Failed to set IP %s/%u.", ifaddr->ip,
ifaddr->prefix_len);
break;
case AF_INET6:
if (l_rtnl_ifaddr6_add(rtnl, netconfig->ifindex,
ifaddr->prefix_len, ifaddr->ip,
netconfig_ipv6_ifaddr_add_cmd_cb,
netconfig, NULL))
return;
l_error("netconfig: Failed to set IPv6 address %s/%u.",
ifaddr->ip, ifaddr->prefix_len);
break;
default:
l_error("netconfig: Unsupported address family: %u",
ifaddr->family);
break;
}
}
static void netconfig_ifaddr_del_cmd_cb(int error, uint16_t type,
const void *data, uint32_t len,
void *user_data)
{
if (error == -ENODEV)
/* The device is unplugged, we are done. */
return;
if (!error)
/*
* The kernel removes all of the routes associated with the
* deleted IP on its own. There is no need to explicitly remove
* them.
*/
return;
l_error("netconfig: Failed to delete IP address. "
"Error %d: %s", error, strerror(-error));
}
static void netconfig_uninstall_address(struct netconfig *netconfig,
struct netconfig_ifaddr *ifaddr)
{
if (!netconfig_ifaddr_find(netconfig, ifaddr->family,
ifaddr->prefix_len, ifaddr->ip))
return;
switch (ifaddr->family) {
case AF_INET:
if (l_rtnl_ifaddr4_delete(rtnl, netconfig->ifindex,
ifaddr->prefix_len, ifaddr->ip,
ifaddr->broadcast,
netconfig_ifaddr_del_cmd_cb, netconfig,
NULL))
return;
l_error("netconfig: Failed to delete IP %s/%u.",
ifaddr->ip, ifaddr->prefix_len);
break;
case AF_INET6:
if (l_rtnl_ifaddr6_delete(rtnl, netconfig->ifindex,
ifaddr->prefix_len, ifaddr->ip,
netconfig_ifaddr_del_cmd_cb, netconfig,
NULL))
return;
l_error("netconfig: Failed to delete IPv6 address %s/%u.",
ifaddr->ip, ifaddr->prefix_len);
break;
default:
l_error("netconfig: Unsupported address family: %u",
ifaddr->family);
break;
}
}
static void netconfig_ipv4_dhcp_event_handler(struct l_dhcp_client *client,
enum l_dhcp_client_event event,
void *userdata)
{
struct netconfig *netconfig = userdata;
struct netconfig_ifaddr *ifaddr;
l_debug("DHCPv4 event %d", event);
switch (event) {
case L_DHCP_CLIENT_EVENT_LEASE_RENEWED:
case L_DHCP_CLIENT_EVENT_LEASE_OBTAINED:
case L_DHCP_CLIENT_EVENT_IP_CHANGED:
ifaddr = netconfig_ipv4_get_ifaddr(netconfig, RTPROT_DHCP);
if (!ifaddr) {
l_error("netconfig: Failed to obtain IP addresses from "
"DHCPv4 lease.");
return;
}
netconfig_install_address(netconfig, ifaddr);
netconfig_ifaddr_destroy(ifaddr);
break;
case L_DHCP_CLIENT_EVENT_LEASE_EXPIRED:
ifaddr = netconfig_ipv4_get_ifaddr(netconfig, RTPROT_DHCP);
if (!ifaddr) {
l_error("netconfig: Failed to obtain IP addresses from "
"DHCPv4 lease.");
return;
}
netconfig_uninstall_address(netconfig, ifaddr);
netconfig_ifaddr_destroy(ifaddr);
/* Fall through. */
case L_DHCP_CLIENT_EVENT_NO_LEASE:
/*
* The requested address is no longer available, try to restart
* the client.
*/
if (!l_dhcp_client_start(client))
l_error("netconfig: Failed to re-start DHCPv4 client "
"for interface %u", netconfig->ifindex);
break;
default:
l_error("netconfig: Received unsupported DHCPv4 event: %d",
event);
}
}
static bool netconfig_ipv4_dhcp_create(struct netconfig *netconfig)
{
netconfig->dhcp_client = l_dhcp_client_new(netconfig->ifindex);
l_dhcp_client_set_event_handler(netconfig->dhcp_client,
netconfig_ipv4_dhcp_event_handler,
netconfig, NULL);
if (getenv("IWD_DHCP_DEBUG"))
l_dhcp_client_set_debug(netconfig->dhcp_client, do_debug,
"[DHCPv4] ", NULL);
return true;
}
static void netconfig_ipv4_select_and_install(struct netconfig *netconfig)
{
struct netconfig_ifaddr *ifaddr;
ifaddr = netconfig_ipv4_get_ifaddr(netconfig, RTPROT_STATIC);
if (ifaddr) {
netconfig->rtm_protocol = RTPROT_STATIC;
netconfig_install_address(netconfig, ifaddr);
netconfig_ifaddr_destroy(ifaddr);
return;
}
netconfig->rtm_protocol = RTPROT_DHCP;
if (l_dhcp_client_start(netconfig->dhcp_client))
return;
l_error("netconfig: Failed to start DHCPv4 client for interface %u",
netconfig->ifindex);
}
static void netconfig_ipv4_select_and_uninstall(struct netconfig *netconfig)
{
struct netconfig_ifaddr *ifaddr;
ifaddr = netconfig_ipv4_get_ifaddr(netconfig, netconfig->rtm_protocol);
if (ifaddr) {
netconfig_uninstall_address(netconfig, ifaddr);
netconfig_ifaddr_destroy(ifaddr);
}
l_dhcp_client_stop(netconfig->dhcp_client);
}
static void netconfig_ipv6_select_and_install(struct netconfig *netconfig)
{
struct netconfig_ifaddr *ifaddr;
ifaddr = netconfig_ipv6_get_ifaddr(netconfig, RTPROT_STATIC);
if (ifaddr) {
netconfig->rtm_v6_protocol = RTPROT_STATIC;
netconfig_install_address(netconfig, ifaddr);
netconfig_ifaddr_destroy(ifaddr);
return;
}
/*
* TODO
*
* netconfig->rtm_v6_protocol = RTPROT_DHCP;
*
* if (l_dhcp_v6_client_start(netconfig->l_dhcp_v6_client))
* return;
*
* l_error("netconfig: Failed to start DHCPv6 client for "
* "interface %u", netconfig->ifindex);
*/
}
static void netconfig_ipv6_select_and_uninstall(struct netconfig *netconfig)
{
struct netconfig_ifaddr *ifaddr;
char *gateway;
ifaddr = netconfig_ipv6_get_ifaddr(netconfig,
netconfig->rtm_v6_protocol);
if (ifaddr) {
netconfig_uninstall_address(netconfig, ifaddr);
netconfig_ifaddr_destroy(ifaddr);
}
/*
* TODO
* l_dhcp_v6_client_stop(netconfig->l_dhcp_v6_client);
*/
gateway = netconfig_ipv6_get_gateway(netconfig);
if (!gateway)
return;
if (!l_rtnl_route6_delete_gateway(rtnl, netconfig->ifindex,
gateway, ROUTE_PRIORITY_OFFSET,
netconfig->rtm_v6_protocol,
netconfig_route_del_cmd_cb, NULL, NULL)) {
l_error("netconfig: Failed to delete route for: %s gateway.",
gateway);
}
l_free(gateway);
}
bool netconfig_configure(struct netconfig *netconfig,
const struct l_settings *active_settings,
const uint8_t *mac_address,
netconfig_notify_func_t notify, void *user_data)
{
netconfig->active_settings = active_settings;
netconfig->notify = notify;
netconfig->user_data = user_data;
l_dhcp_client_set_address(netconfig->dhcp_client, ARPHRD_ETHER,
mac_address, ETH_ALEN);
netconfig_ipv4_select_and_install(netconfig);
netconfig_ipv6_select_and_install(netconfig);
return true;
}
bool netconfig_reconfigure(struct netconfig *netconfig)
{
if (netconfig->rtm_protocol == RTPROT_DHCP) {
/* TODO l_dhcp_client sending a DHCP inform request */
}
if (netconfig->rtm_v6_protocol == RTPROT_DHCP) {
/* TODO l_dhcp_v6_client sending a DHCP inform request */
}
return true;
}
bool netconfig_reset(struct netconfig *netconfig)
{
netconfig_ipv4_select_and_uninstall(netconfig);
netconfig->rtm_protocol = 0;
netconfig_ipv6_select_and_uninstall(netconfig);
netconfig->rtm_v6_protocol = 0;
resolve_remove(netconfig->ifindex);
return true;
}
char *netconfig_get_dhcp_server_ipv4(struct netconfig *netconfig)
{
const struct l_dhcp_lease *lease;
if (!netconfig->dhcp_client)
return NULL;
lease = l_dhcp_client_get_lease(netconfig->dhcp_client);
if (!lease)
return NULL;
return l_dhcp_lease_get_server_id(lease);
}
struct netconfig *netconfig_new(uint32_t ifindex)
{
struct netconfig *netconfig;
if (!netconfig_list)
return NULL;
l_debug("Starting netconfig for interface: %d", ifindex);
netconfig = netconfig_find(ifindex);
if (netconfig)
return netconfig;
netconfig = l_new(struct netconfig, 1);
netconfig->ifindex = ifindex;
netconfig->ifaddr_list = l_queue_new();
netconfig_ipv4_dhcp_create(netconfig);
l_queue_push_tail(netconfig_list, netconfig);
return netconfig;
}
void netconfig_destroy(struct netconfig *netconfig)
{
if (!netconfig_list)
return;
l_debug("");
l_queue_remove(netconfig_list, netconfig);
if (netconfig->rtm_protocol)
netconfig_ipv4_select_and_uninstall(netconfig);
if (netconfig->rtm_protocol || netconfig->rtm_v6_protocol)
resolve_remove(netconfig->ifindex);
netconfig_free(netconfig);
}
static int netconfig_init(void)
{
uint32_t r;
if (netconfig_list)
return -EALREADY;
rtnl = iwd_get_rtnl();
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.");
goto error;
}
r = l_rtnl_ifaddr4_dump(rtnl, netconfig_ifaddr_cmd_cb, NULL, NULL);
if (!r) {
l_error("netconfig: Failed to get addresses from RTNL link.");
goto error;
}
r = l_netlink_register(rtnl, RTNLGRP_IPV6_IFADDR,
netconfig_ifaddr_ipv6_notify, NULL, NULL);
if (!r) {
l_error("netconfig: Failed to register for RTNL link IPv6 "
"address notifications.");
goto error;
}
r = l_rtnl_ifaddr6_dump(rtnl, netconfig_ifaddr_ipv6_cmd_cb, NULL,
NULL);
if (!r) {
l_error("netconfig: Failed to get IPv6 addresses from RTNL"
" link.");
goto error;
}
if (!l_settings_get_uint(iwd_get_config(), "Network",
"RoutePriorityOffset",
&ROUTE_PRIORITY_OFFSET))
ROUTE_PRIORITY_OFFSET = 300;
netconfig_list = l_queue_new();
return 0;
error:
rtnl = NULL;
return r;
}
static void netconfig_exit(void)
{
if (!netconfig_list)
return;
rtnl = NULL;
l_queue_destroy(netconfig_list, netconfig_free);
}
IWD_MODULE(netconfig, netconfig_init, netconfig_exit)