From f39d1b4ac2af36f8fa6ebb4e00351bfe91f4611a Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Thu, 10 Dec 2020 13:22:09 -0800 Subject: [PATCH] netconfig: add ACD client for static configuration When the IP is configured to be static we can now use ACD in order to check that the IP is available and not already in use. If a conflict is found netconfig will be reset and no IP will be set on the interface. The ACD client is left with the default 'defend once' policy, and probes are not turned off. This will increase connection time, but for static IP's it is the best approach. --- src/netconfig.c | 100 ++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 79 insertions(+), 21 deletions(-) diff --git a/src/netconfig.c b/src/netconfig.c index f48a789a..e7a6a402 100644 --- a/src/netconfig.c +++ b/src/netconfig.c @@ -64,6 +64,8 @@ struct netconfig { void *user_data; struct resolve *resolve; + + struct l_acd *acd; }; static struct l_netlink *rtnl; @@ -804,16 +806,88 @@ static void netconfig_dhcp6_event_handler(struct l_dhcp6_client *client, } } -static void netconfig_ipv4_select_and_install(struct netconfig *netconfig) +static void netconfig_reset_v4(struct netconfig *netconfig) { - netconfig->v4_address = netconfig_get_static4_address(netconfig); - if (netconfig->v4_address) { - netconfig->rtm_protocol = RTPROT_STATIC; + if (netconfig->rtm_protocol) { + if (netconfig->v4_address) { + L_WARN_ON(!l_rtnl_ifaddr_delete(rtnl, + netconfig->ifindex, + netconfig->v4_address, + netconfig_ifaddr_del_cmd_cb, + netconfig, NULL)); + l_rtnl_address_free(netconfig->v4_address); + netconfig->v4_address = NULL; + } + + l_strfreev(netconfig->dns4_overrides); + netconfig->dns4_overrides = NULL; + + l_dhcp_client_stop(netconfig->dhcp_client); + netconfig->rtm_protocol = 0; + } +} + +static void netconfig_ipv4_acd_event(enum l_acd_event event, void *user_data) +{ + struct netconfig *netconfig = user_data; + + switch (event) { + case L_ACD_EVENT_AVAILABLE: L_WARN_ON(!l_rtnl_ifaddr_add(rtnl, netconfig->ifindex, netconfig->v4_address, netconfig_ipv4_ifaddr_add_cmd_cb, netconfig, NULL)); return; + case L_ACD_EVENT_CONFLICT: + /* + * Conflict found, no IP was actually set so just free/unset + * anything we set prior to starting ACD. + */ + l_error("netconfig: statically configured address conflict!"); + l_rtnl_address_free(netconfig->v4_address); + netconfig->v4_address = NULL; + netconfig->rtm_protocol = 0; + break; + case L_ACD_EVENT_LOST: + /* + * Set IP but lost it some time after. Full (IPv4) reset in this + * case. + */ + l_error("netconfig: statically configured address was lost"); + netconfig_reset_v4(netconfig); + break; + } +} + +static void netconfig_ipv4_select_and_install(struct netconfig *netconfig) +{ + netconfig->v4_address = netconfig_get_static4_address(netconfig); + if (netconfig->v4_address) { + char ip[INET6_ADDRSTRLEN]; + + netconfig->rtm_protocol = RTPROT_STATIC; + netconfig->acd = l_acd_new(netconfig->ifindex); + l_acd_set_event_handler(netconfig->acd, + netconfig_ipv4_acd_event, netconfig, + NULL); + if (getenv("IWD_ACD_DEBUG")) + l_acd_set_debug(netconfig->acd, do_debug, + "[ACD] ", NULL); + + l_rtnl_address_get_address(netconfig->v4_address, ip); + + if (!l_acd_start(netconfig->acd, ip)) { + l_error("failed to start ACD, continuing anyways"); + l_acd_destroy(netconfig->acd); + netconfig->acd = NULL; + + L_WARN_ON(!l_rtnl_ifaddr_add(rtnl, netconfig->ifindex, + netconfig->v4_address, + netconfig_ipv4_ifaddr_add_cmd_cb, + netconfig, NULL)); + } + + return; } netconfig->rtm_protocol = RTPROT_DHCP; @@ -955,23 +1029,7 @@ bool netconfig_reset(struct netconfig *netconfig) if (netconfig->rtm_protocol || netconfig->rtm_v6_protocol) resolve_revert(netconfig->resolve); - if (netconfig->rtm_protocol) { - if (netconfig->v4_address) { - L_WARN_ON(!l_rtnl_ifaddr_delete(rtnl, - netconfig->ifindex, - netconfig->v4_address, - netconfig_ifaddr_del_cmd_cb, - netconfig, NULL)); - l_rtnl_address_free(netconfig->v4_address); - netconfig->v4_address = NULL; - } - - l_strfreev(netconfig->dns4_overrides); - netconfig->dns4_overrides = NULL; - - l_dhcp_client_stop(netconfig->dhcp_client); - netconfig->rtm_protocol = 0; - } + netconfig_reset_v4(netconfig); if (netconfig->rtm_v6_protocol) { l_strfreev(netconfig->dns6_overrides);