* [PATCH 04/17] netconfig: Create routes from Router Advertisements
@ 2022-05-13 22:47 Andrew Zaborowski
0 siblings, 0 replies; 3+ messages in thread
From: Andrew Zaborowski @ 2022-05-13 22:47 UTC (permalink / raw)
To: ell
[-- Attachment #1: Type: text/plain, Size: 13944 bytes --]
If IPv6 is enabled, create the ICMP6 client and handle Router
Advertisements received by creating, updating and removing routes per
the data in the Router Advertisements. Timeouts aren't handled yet.
---
ell/netconfig.c | 293 +++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 278 insertions(+), 15 deletions(-)
diff --git a/ell/netconfig.c b/ell/netconfig.c
index 9375d13..6ad6394 100644
--- a/ell/netconfig.c
+++ b/ell/netconfig.c
@@ -27,14 +27,19 @@
#include <linux/types.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
+#include <netinet/icmp6.h>
#include "private.h"
#include "useful.h"
#include "log.h"
#include "dhcp.h"
#include "dhcp-private.h"
+#include "icmp6.h"
+#include "icmp6-private.h"
+#include "dhcp6.h"
#include "netlink.h"
#include "rtnl.h"
+#include "rtnl-private.h"
#include "queue.h"
#include "time.h"
#include "idle.h"
@@ -64,6 +69,8 @@ struct l_netconfig {
bool v4_configured;
struct l_dhcp_client *dhcp_client;
bool v6_configured;
+ struct l_icmp6_client *icmp6_client;
+ struct l_dhcp6_client *dhcp6_client;
/* These objects, if not NULL, are owned by @addresses and @routes */
struct l_rtnl_address *v4_address;
@@ -128,6 +135,33 @@ static void netconfig_emit_event(struct l_netconfig *nc, uint8_t family,
netconfig_update_cleanup(nc);
}
+static struct l_rtnl_route *netconfig_route_new(struct l_netconfig *nc,
+ uint8_t family,
+ const void *dst,
+ uint8_t prefix_len,
+ const void *gw,
+ uint8_t protocol)
+{
+ struct l_rtnl_route *rt = l_new(struct l_rtnl_route, 1);
+
+ rt->family = family;
+ rt->scope = (family == AF_INET && dst) ?
+ RT_SCOPE_LINK : RT_SCOPE_UNIVERSE;
+ rt->protocol = protocol;
+ rt->lifetime = 0xffffffff;
+ rt->priority = nc->route_priority;
+
+ if (dst) {
+ memcpy(&rt->dst, dst, family == AF_INET ? 4 : 16);
+ rt->dst_prefix_len = prefix_len;
+ }
+
+ if (gw)
+ memcpy(&rt->gw, gw, family == AF_INET ? 4 : 16);
+
+ return rt;
+}
+
static void netconfig_add_v4_routes(struct l_netconfig *nc, const char *ip,
uint8_t prefix_len, const char *gateway,
uint8_t rtm_protocol)
@@ -141,12 +175,9 @@ static void netconfig_add_v4_routes(struct l_netconfig *nc, const char *ip,
return;
in_addr.s_addr &= htonl(0xfffffffflu << (32 - prefix_len));
- if (L_WARN_ON(!inet_ntop(AF_INET, &in_addr, network, INET_ADDRSTRLEN)))
- return;
-
- nc->v4_subnet_route = l_rtnl_route_new_prefix(network, prefix_len);
- l_rtnl_route_set_protocol(nc->v4_subnet_route, rtm_protocol);
- l_rtnl_route_set_priority(nc->v4_subnet_route, nc->route_priority);
+ nc->v4_subnet_route = netconfig_route_new(nc, AF_INET, network,
+ prefix_len, NULL,
+ rtm_protocol);
l_queue_push_tail(nc->routes.current, nc->v4_subnet_route);
l_queue_push_tail(nc->routes.added, nc->v4_subnet_route);
@@ -163,6 +194,7 @@ static void netconfig_add_v4_routes(struct l_netconfig *nc, const char *ip,
nc->v4_default_route = l_rtnl_route_new_gateway(gateway);
l_rtnl_route_set_protocol(nc->v4_default_route, rtm_protocol);
L_WARN_ON(!l_rtnl_route_set_prefsrc(nc->v4_default_route, ip));
+ l_rtnl_route_set_priority(nc->v4_default_route, nc->route_priority);
l_queue_push_tail(nc->routes.current, nc->v4_default_route);
l_queue_push_tail(nc->routes.added, nc->v4_default_route);
}
@@ -173,7 +205,6 @@ static void netconfig_add_v6_static_routes(struct l_netconfig *nc,
{
struct in6_addr in6_addr;
const void *prefix;
- char network[INET6_ADDRSTRLEN];
struct l_rtnl_route *v6_subnet_route;
struct l_rtnl_route *v6_default_route;
@@ -188,17 +219,13 @@ static void netconfig_add_v6_static_routes(struct l_netconfig *nc,
*/
prefix = net_prefix_from_ipv6(in6_addr.s6_addr, prefix_len);
- if (L_WARN_ON(!inet_ntop(AF_INET6, prefix, network, INET6_ADDRSTRLEN)))
- return;
-
/*
* One reason we add a subnet route instead of letting the kernel
* do it, by not specifying IFA_F_NOPREFIXROUTE for the address,
* is that that would force a 0 metric for the route.
*/
- v6_subnet_route = l_rtnl_route_new_prefix(network, prefix_len);
- l_rtnl_route_set_protocol(v6_subnet_route, RTPROT_STATIC);
- l_rtnl_route_set_priority(v6_subnet_route, nc->route_priority);
+ v6_subnet_route = netconfig_route_new(nc, AF_INET6, prefix, prefix_len,
+ NULL, RTPROT_STATIC);
l_queue_push_tail(nc->routes.current, v6_subnet_route);
l_queue_push_tail(nc->routes.added, v6_subnet_route);
@@ -214,6 +241,19 @@ static void netconfig_add_v6_static_routes(struct l_netconfig *nc,
l_queue_push_tail(nc->routes.added, v6_default_route);
}
+static bool netconfig_route_exists(struct l_queue *list,
+ const struct l_rtnl_route *route)
+{
+ const struct l_queue_entry *entry;
+
+ for (entry = l_queue_get_entries(list); entry;
+ entry = entry->next)
+ if ((const struct l_rtnl_route *) entry->data == route)
+ return true;
+
+ return false;
+}
+
static void netconfig_add_dhcp_address_routes(struct l_netconfig *nc)
{
const struct l_dhcp_lease *lease =
@@ -360,6 +400,204 @@ static void netconfig_dhcp_event_handler(struct l_dhcp_client *client,
}
}
+static struct l_rtnl_route *netconfig_find_icmp6_route(
+ struct l_netconfig *nc,
+ const uint8_t *gateway,
+ const struct route_info *dst)
+{
+ const struct l_queue_entry *entry;
+
+ for (entry = l_queue_get_entries(nc->routes.current); entry;
+ entry = entry->next) {
+ struct l_rtnl_route *route = entry->data;
+ const uint8_t *route_gateway;
+ const uint8_t *route_dst;
+ uint8_t route_prefix_len = 0;
+
+ if (l_rtnl_route_get_family(route) != AF_INET6 ||
+ l_rtnl_route_get_protocol(route) != RTPROT_RA)
+ continue;
+
+ route_gateway = l_rtnl_route_get_gateway_in_addr(route);
+ if ((gateway || route_gateway) &&
+ (!gateway || !route_gateway ||
+ memcmp(gateway, route_gateway, 16)))
+ continue;
+
+ route_dst = l_rtnl_route_get_dst_in_addr(route,
+ &route_prefix_len);
+ if ((dst || route_prefix_len) &&
+ (!dst || !route_prefix_len ||
+ dst->prefix_len != route_prefix_len ||
+ memcmp(dst->address, route_dst, 16)))
+ continue;
+
+ return route;
+ }
+
+ return NULL;
+}
+
+static struct l_rtnl_route *netconfig_add_icmp6_route(struct l_netconfig *nc,
+ const uint8_t *gateway,
+ const struct route_info *dst,
+ uint8_t preference)
+{
+ struct l_rtnl_route *rt;
+
+ rt = netconfig_route_new(nc, AF_INET6, dst->address, dst->prefix_len,
+ gateway, RTPROT_RA);
+ if (L_WARN_ON(!rt))
+ return NULL;
+
+ l_rtnl_route_set_preference(rt, preference);
+ l_queue_push_tail(nc->routes.current, rt);
+ l_queue_push_tail(nc->routes.added, rt);
+ return rt;
+}
+
+static void netconfig_set_icmp6_route_data(struct l_netconfig *nc,
+ struct l_rtnl_route *rt,
+ uint64_t start_time,
+ uint32_t preferred_lifetime,
+ uint32_t valid_lifetime,
+ uint32_t mtu, bool updated)
+{
+ uint64_t expiry = start_time + valid_lifetime * L_USEC_PER_SEC;
+ uint64_t old_expiry = l_rtnl_route_get_expiry(rt);
+ bool differs = false;
+
+ if (mtu != l_rtnl_route_get_mtu(rt)) {
+ l_rtnl_route_set_mtu(rt, mtu);
+ differs = true;
+ }
+
+ /*
+ * valid_lifetime of 0 from a route_info means the route is being
+ * removed so we wouldn't be here. valid_lifetime of 0xffffffff
+ * means no timeout. Check if the lifetime is changing between
+ * finite and infinite, or two finite values that result in expiry
+ * time difference of more than a second -- to avoid emitting
+ * updates for changes resulting only from the valid_lifetime one
+ * second resolution and RA transmission jitter. As RFC4861
+ * Section 6.2.7 puts it: "Due to link propagation delays and
+ * potentially poorly synchronized clocks between the routers such
+ * comparison SHOULD allow some time skew." The RFC talks about
+ * routers processing one another's RAs but the same logic applies
+ * here.
+ */
+ if (valid_lifetime == 0xffffffff)
+ expiry = 0;
+
+ if ((expiry || old_expiry) &&
+ (!expiry || !old_expiry ||
+ l_time_diff(expiry, old_expiry) > L_USEC_PER_SEC)) {
+ l_rtnl_route_set_lifetime(rt, valid_lifetime);
+ l_rtnl_route_set_expiry(rt, expiry);
+ differs = true;
+ }
+
+ if (updated && differs && !netconfig_route_exists(nc->routes.added, rt))
+ l_queue_push_tail(nc->routes.updated, rt);
+}
+
+static void netconfig_remove_icmp6_route(struct l_netconfig *nc,
+ struct l_rtnl_route *route)
+{
+ l_queue_remove(nc->routes.current, route);
+ l_queue_push_tail(nc->routes.removed, route);
+}
+
+static void netconfig_icmp6_event_handler(struct l_icmp6_client *client,
+ enum l_icmp6_client_event event,
+ void *event_data,
+ void *user_data)
+{
+ struct l_netconfig *nc = user_data;
+ const struct l_icmp6_router *r;
+ struct l_rtnl_route *default_route;
+ unsigned int i;
+
+ if (event != L_ICMP6_CLIENT_EVENT_ROUTER_FOUND)
+ return;
+
+ r = event_data;
+
+ /*
+ * Note: If this is the first RA received, the l_dhcp6_client
+ * will have received the event before us and will be acting
+ * on it by now.
+ */
+
+ if (nc->v4_gateway_override)
+ return;
+
+ /* Process the default gateway information */
+ default_route = netconfig_find_icmp6_route(nc, r->address, NULL);
+
+ if (!default_route && r->lifetime) {
+ default_route = netconfig_add_icmp6_route(nc, r->address, NULL,
+ r->pref);
+ if (unlikely(!default_route))
+ return;
+
+ /*
+ * r->lifetime is 16-bit only so there's no risk it gets
+ * confused for the special 0xffffffff value in
+ * netconfig_set_icmp6_route_data.
+ */
+ netconfig_set_icmp6_route_data(nc, default_route, r->start_time,
+ r->lifetime, r->lifetime,
+ r->mtu, false);
+ } else if (default_route && r->lifetime)
+ netconfig_set_icmp6_route_data(nc, default_route, r->start_time,
+ r->lifetime, r->lifetime,
+ r->mtu, true);
+ else if (default_route && !r->lifetime)
+ netconfig_remove_icmp6_route(nc, default_route);
+
+ /*
+ * Process the onlink and offlink routes, from the Router
+ * Advertisement's Prefix Information options and Route
+ * Information options respectively.
+ */
+ for (i = 0; i < r->n_routes; i++) {
+ const struct route_info *info = &r->routes[i];
+ const uint8_t *gateway = info->onlink ? NULL : r->address;
+ struct l_rtnl_route *route =
+ netconfig_find_icmp6_route(nc, gateway, info);
+
+ if (!route && info->valid_lifetime) {
+ route = netconfig_add_icmp6_route(nc, gateway, info,
+ info->preference);
+ if (unlikely(!route))
+ continue;
+
+ netconfig_set_icmp6_route_data(nc, route, r->start_time,
+ info->preferred_lifetime,
+ info->valid_lifetime,
+ gateway ? r->mtu : 0, false);
+ } else if (route && info->valid_lifetime)
+ netconfig_set_icmp6_route_data(nc, route, r->start_time,
+ info->preferred_lifetime,
+ info->valid_lifetime,
+ gateway ? r->mtu : 0, true);
+ else if (route && !info->valid_lifetime)
+ netconfig_remove_icmp6_route(nc, route);
+ }
+
+ /*
+ * Note: we may be emitting this before L_NETCONFIG_EVENT_CONFIGURE.
+ * We should probably instead save the affected routes in separate
+ * lists and add them to the _CONFIGURE event, suppressing any _UPDATE
+ * events while nc->v6_configured is false.
+ */
+ if (!l_queue_isempty(nc->routes.added) ||
+ !l_queue_isempty(nc->routes.updated) ||
+ !l_queue_isempty(nc->routes.removed))
+ netconfig_emit_event(nc, AF_INET6, L_NETCONFIG_EVENT_UPDATE);
+}
+
LIB_EXPORT struct l_netconfig *l_netconfig_new(uint32_t ifindex)
{
struct l_netconfig *nc;
@@ -382,6 +620,13 @@ LIB_EXPORT struct l_netconfig *l_netconfig_new(uint32_t ifindex)
netconfig_dhcp_event_handler,
nc, NULL);
+ nc->dhcp6_client = l_dhcp6_client_new(ifindex);
+
+ nc->icmp6_client = l_dhcp6_client_get_icmp6(nc->dhcp6_client);
+ l_icmp6_client_add_event_handler(nc->icmp6_client,
+ netconfig_icmp6_event_handler,
+ nc, NULL);
+
return nc;
}
@@ -402,6 +647,7 @@ LIB_EXPORT void l_netconfig_destroy(struct l_netconfig *netconfig)
l_netconfig_set_domain_names_override(netconfig, AF_INET6, NULL);
l_dhcp_client_destroy(netconfig->dhcp_client);
+ l_dhcp6_client_destroy(netconfig->dhcp6_client);
l_netconfig_set_event_handler(netconfig, NULL, NULL, NULL);
l_queue_destroy(netconfig->addresses.current, NULL);
l_queue_destroy(netconfig->addresses.added, NULL);
@@ -735,19 +981,35 @@ configure_ipv6:
if (!netconfig->v6_enabled)
goto done;
- if (netconfig->v6_static_addr && !netconfig->do_static_work) {
+ if (netconfig->v6_static_addr) {
/*
* We're basically ready to configure the interface
* but do this in an idle callback.
*/
- netconfig->do_static_work = l_idle_create(
+ if (!netconfig->do_static_work)
+ netconfig->do_static_work = l_idle_create(
netconfig_do_static_config,
netconfig, NULL);
+
+ goto done;
}
+ if (!l_dhcp6_client_start(netconfig->dhcp6_client))
+ goto stop_ipv4;
+
done:
netconfig->started = true;
return true;
+
+stop_ipv4:
+ if (netconfig->v4_enabled) {
+ if (netconfig->v4_static_addr)
+ l_idle_remove(l_steal_ptr(netconfig->do_static_work));
+ else
+ l_dhcp_client_stop(netconfig->dhcp_client);
+ }
+
+ return false;
}
LIB_EXPORT void l_netconfig_stop(struct l_netconfig *netconfig)
@@ -771,6 +1033,7 @@ LIB_EXPORT void l_netconfig_stop(struct l_netconfig *netconfig)
netconfig->v6_address = NULL;
l_dhcp_client_stop(netconfig->dhcp_client);
+ l_dhcp6_client_stop(netconfig->dhcp6_client);
}
LIB_EXPORT struct l_dhcp_client *l_netconfig_get_dhcp_client(
--
2.32.0
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH 04/17] netconfig: Create routes from Router Advertisements
@ 2022-05-18 18:42 Denis Kenzior
0 siblings, 0 replies; 3+ messages in thread
From: Denis Kenzior @ 2022-05-18 18:42 UTC (permalink / raw)
To: ell
[-- Attachment #1: Type: text/plain, Size: 1248 bytes --]
Hi Andrew,
On 5/13/22 17:47, Andrew Zaborowski wrote:
> If IPv6 is enabled, create the ICMP6 client and handle Router
> Advertisements received by creating, updating and removing routes per
> the data in the Router Advertisements. Timeouts aren't handled yet.
> ---
> ell/netconfig.c | 293 +++++++++++++++++++++++++++++++++++++++++++++---
> 1 file changed, 278 insertions(+), 15 deletions(-)
>
<snip>
> +static void netconfig_icmp6_event_handler(struct l_icmp6_client *client,
> + enum l_icmp6_client_event event,
> + void *event_data,
> + void *user_data)
> +{
> + struct l_netconfig *nc = user_data;
> + const struct l_icmp6_router *r;
> + struct l_rtnl_route *default_route;
> + unsigned int i;
> +
> + if (event != L_ICMP6_CLIENT_EVENT_ROUTER_FOUND)
> + return;
> +
> + r = event_data;
> +
> + /*
> + * Note: If this is the first RA received, the l_dhcp6_client
> + * will have received the event before us and will be acting
> + * on it by now.
> + */
> +
> + if (nc->v4_gateway_override)
> + return;
> +
So I think you meant v6_gateway_override here and I fixed this up accordingly.
Send a fix if this wasn't correct.
Patches 1-4 applied, thanks.
Regards,
-Denis
^ permalink raw reply [flat|nested] 3+ messages in thread
* [PATCH 04/17] netconfig: Create routes from Router Advertisements
@ 2022-05-13 14:54 Andrew Zaborowski
0 siblings, 0 replies; 3+ messages in thread
From: Andrew Zaborowski @ 2022-05-13 14:54 UTC (permalink / raw)
To: ell
[-- Attachment #1: Type: text/plain, Size: 14010 bytes --]
If IPv6 is enabled, create the ICMP6 client and handle Router
Advertisements received by creating, updating and removing routes per
the data in the Router Advertisements. Timeouts aren't handled yet.
---
ell/netconfig.c | 294 +++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 278 insertions(+), 16 deletions(-)
diff --git a/ell/netconfig.c b/ell/netconfig.c
index 9ede190..243ecd8 100644
--- a/ell/netconfig.c
+++ b/ell/netconfig.c
@@ -27,14 +27,19 @@
#include <linux/types.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
+#include <netinet/icmp6.h>
#include "private.h"
#include "useful.h"
#include "log.h"
#include "dhcp.h"
#include "dhcp-private.h"
+#include "icmp6.h"
+#include "icmp6-private.h"
+#include "dhcp6.h"
#include "netlink.h"
#include "rtnl.h"
+#include "rtnl-private.h"
#include "queue.h"
#include "time.h"
#include "idle.h"
@@ -63,6 +68,8 @@ struct l_netconfig {
bool v4_configured;
struct l_dhcp_client *dhcp_client;
bool v6_configured;
+ struct l_icmp6_client *icmp6_client;
+ struct l_dhcp6_client *dhcp6_client;
/* These objects, if not NULL, are owned by @addresses and @routes */
struct l_rtnl_address *v4_address;
@@ -127,6 +134,33 @@ static void netconfig_emit_event(struct l_netconfig *nc, uint8_t family,
netconfig_update_cleanup(nc);
}
+static struct l_rtnl_route *netconfig_route_new(struct l_netconfig *nc,
+ uint8_t family,
+ const void *dst,
+ uint8_t prefix_len,
+ const void *gw,
+ uint8_t protocol)
+{
+ struct l_rtnl_route *rt = l_new(struct l_rtnl_route, 1);
+
+ rt->family = family;
+ rt->scope = (family == AF_INET && dst) ?
+ RT_SCOPE_LINK : RT_SCOPE_UNIVERSE;
+ rt->protocol = protocol;
+ rt->lifetime = 0xffffffff;
+ rt->priority = nc->route_priority;
+
+ if (dst) {
+ memcpy(&rt->dst, dst, family == AF_INET ? 4 : 16);
+ rt->dst_prefix_len = prefix_len;
+ }
+
+ if (gw)
+ memcpy(&rt->gw, gw, family == AF_INET ? 4 : 16);
+
+ return rt;
+}
+
static void netconfig_add_v4_routes(struct l_netconfig *nc, const char *ip,
uint8_t prefix_len, const char *gateway,
uint8_t rtm_protocol)
@@ -140,12 +174,9 @@ static void netconfig_add_v4_routes(struct l_netconfig *nc, const char *ip,
return;
in_addr.s_addr &= htonl(0xfffffffflu << (32 - prefix_len));
- if (L_WARN_ON(!inet_ntop(AF_INET, &in_addr, network, INET_ADDRSTRLEN)))
- return;
-
- nc->v4_subnet_route = l_rtnl_route_new_prefix(network, prefix_len);
- l_rtnl_route_set_protocol(nc->v4_subnet_route, rtm_protocol);
- l_rtnl_route_set_priority(nc->v4_subnet_route, nc->route_priority);
+ nc->v4_subnet_route = netconfig_route_new(nc, AF_INET, network,
+ prefix_len, NULL,
+ rtm_protocol);
l_queue_push_tail(nc->routes.current, nc->v4_subnet_route);
l_queue_push_tail(nc->routes.added, nc->v4_subnet_route);
@@ -162,6 +193,7 @@ static void netconfig_add_v4_routes(struct l_netconfig *nc, const char *ip,
nc->v4_default_route = l_rtnl_route_new_gateway(gateway);
l_rtnl_route_set_protocol(nc->v4_default_route, rtm_protocol);
L_WARN_ON(!l_rtnl_route_set_prefsrc(nc->v4_default_route, ip));
+ l_rtnl_route_set_priority(nc->v4_default_route, nc->route_priority);
l_queue_push_tail(nc->routes.current, nc->v4_default_route);
l_queue_push_tail(nc->routes.added, nc->v4_default_route);
}
@@ -171,7 +203,6 @@ static void netconfig_add_v6_static_routes(struct l_netconfig *nc,
uint8_t prefix_len)
{
struct in6_addr in6_addr;
- char network[INET6_ADDRSTRLEN];
struct l_rtnl_route *v6_subnet_route;
struct l_rtnl_route *v6_default_route;
@@ -183,18 +214,13 @@ static void netconfig_add_v6_static_routes(struct l_netconfig *nc,
/* Zero out host address bits to produce network address */
l_net_mask_prefix(in6_addr.s6_addr, prefix_len, 16);
- if (L_WARN_ON(!inet_ntop(AF_INET6, &in6_addr, network,
- INET6_ADDRSTRLEN)))
- return;
-
/*
* One reason we add a subnet route instead of letting the kernel
* do it, by not specifying IFA_F_NOPREFIXROUTE for the address,
* is that that would force a 0 metric for the route.
*/
- v6_subnet_route = l_rtnl_route_new_prefix(network, prefix_len);
- l_rtnl_route_set_protocol(v6_subnet_route, RTPROT_STATIC);
- l_rtnl_route_set_priority(v6_subnet_route, nc->route_priority);
+ v6_subnet_route = netconfig_route_new(nc, AF_INET6, &in6_addr,
+ prefix_len, NULL, RTPROT_STATIC);
l_queue_push_tail(nc->routes.current, v6_subnet_route);
l_queue_push_tail(nc->routes.added, v6_subnet_route);
@@ -210,6 +236,19 @@ static void netconfig_add_v6_static_routes(struct l_netconfig *nc,
l_queue_push_tail(nc->routes.added, v6_default_route);
}
+static bool netconfig_route_exists(struct l_queue *list,
+ const struct l_rtnl_route *route)
+{
+ const struct l_queue_entry *entry;
+
+ for (entry = l_queue_get_entries(list); entry;
+ entry = entry->next)
+ if ((const struct l_rtnl_route *) entry->data == route)
+ return true;
+
+ return false;
+}
+
static void netconfig_add_dhcp_address_routes(struct l_netconfig *nc)
{
const struct l_dhcp_lease *lease =
@@ -356,6 +395,204 @@ static void netconfig_dhcp_event_handler(struct l_dhcp_client *client,
}
}
+static struct l_rtnl_route *netconfig_find_icmp6_route(
+ struct l_netconfig *nc,
+ const uint8_t *gateway,
+ const struct route_info *dst)
+{
+ const struct l_queue_entry *entry;
+
+ for (entry = l_queue_get_entries(nc->routes.current); entry;
+ entry = entry->next) {
+ struct l_rtnl_route *route = entry->data;
+ const uint8_t *route_gateway;
+ const uint8_t *route_dst;
+ uint8_t route_prefix_len = 0;
+
+ if (l_rtnl_route_get_family(route) != AF_INET6 ||
+ l_rtnl_route_get_protocol(route) != RTPROT_RA)
+ continue;
+
+ route_gateway = l_rtnl_route_get_gateway_in_addr(route);
+ if ((gateway || route_gateway) &&
+ (!gateway || !route_gateway ||
+ memcmp(gateway, route_gateway, 16)))
+ continue;
+
+ route_dst = l_rtnl_route_get_dst_in_addr(route,
+ &route_prefix_len);
+ if ((dst || route_prefix_len) &&
+ (!dst || !route_prefix_len ||
+ dst->prefix_len != route_prefix_len ||
+ memcmp(dst->address, route_dst, 16)))
+ continue;
+
+ return route;
+ }
+
+ return NULL;
+}
+
+static struct l_rtnl_route *netconfig_add_icmp6_route(struct l_netconfig *nc,
+ const uint8_t *gateway,
+ const struct route_info *dst,
+ uint8_t preference)
+{
+ struct l_rtnl_route *rt;
+
+ rt = netconfig_route_new(nc, AF_INET6, dst->address, dst->prefix_len,
+ gateway, RTPROT_RA);
+ if (L_WARN_ON(!rt))
+ return NULL;
+
+ l_rtnl_route_set_preference(rt, preference);
+ l_queue_push_tail(nc->routes.current, rt);
+ l_queue_push_tail(nc->routes.added, rt);
+ return rt;
+}
+
+static void netconfig_set_icmp6_route_data(struct l_netconfig *nc,
+ struct l_rtnl_route *rt,
+ uint64_t start_time,
+ uint32_t preferred_lifetime,
+ uint32_t valid_lifetime,
+ uint32_t mtu, bool updated)
+{
+ uint64_t expiry = start_time + valid_lifetime * L_USEC_PER_SEC;
+ uint64_t old_expiry = l_rtnl_route_get_expiry(rt);
+ bool differs = false;
+
+ if (mtu != l_rtnl_route_get_mtu(rt)) {
+ l_rtnl_route_set_mtu(rt, mtu);
+ differs = true;
+ }
+
+ /*
+ * valid_lifetime of 0 from a route_info means the route is being
+ * removed so we wouldn't be here. valid_lifetime of 0xffffffff
+ * means no timeout. Check if the lifetime is changing between
+ * finite and infinite, or two finite values that result in expiry
+ * time difference of more than a second -- to avoid emitting
+ * updates for changes resulting only from the valid_lifetime one
+ * second resolution and RA transmission jitter. As RFC4861
+ * Section 6.2.7 puts it: "Due to link propagation delays and
+ * potentially poorly synchronized clocks between the routers such
+ * comparison SHOULD allow some time skew." The RFC talks about
+ * routers processing one another's RAs but the same logic applies
+ * here.
+ */
+ if (valid_lifetime == 0xffffffff)
+ expiry = 0;
+
+ if ((expiry || old_expiry) &&
+ (!expiry || !old_expiry ||
+ l_time_diff(expiry, old_expiry) > L_USEC_PER_SEC)) {
+ l_rtnl_route_set_lifetime(rt, valid_lifetime);
+ l_rtnl_route_set_expiry(rt, expiry);
+ differs = true;
+ }
+
+ if (updated && differs && !netconfig_route_exists(nc->routes.added, rt))
+ l_queue_push_tail(nc->routes.updated, rt);
+}
+
+static void netconfig_remove_icmp6_route(struct l_netconfig *nc,
+ struct l_rtnl_route *route)
+{
+ l_queue_remove(nc->routes.current, route);
+ l_queue_push_tail(nc->routes.removed, route);
+}
+
+static void netconfig_icmp6_event_handler(struct l_icmp6_client *client,
+ enum l_icmp6_client_event event,
+ void *event_data,
+ void *user_data)
+{
+ struct l_netconfig *nc = user_data;
+ const struct l_icmp6_router *r;
+ struct l_rtnl_route *default_route;
+ unsigned int i;
+
+ if (event != L_ICMP6_CLIENT_EVENT_ROUTER_FOUND)
+ return;
+
+ r = event_data;
+
+ /*
+ * Note: If this is the first RA received, the l_dhcp6_client
+ * will have received the event before us and will be acting
+ * on it by now.
+ */
+
+ if (nc->v4_gateway_override)
+ return;
+
+ /* Process the default gateway information */
+ default_route = netconfig_find_icmp6_route(nc, r->address, NULL);
+
+ if (!default_route && r->lifetime) {
+ default_route = netconfig_add_icmp6_route(nc, r->address, NULL,
+ r->pref);
+ if (unlikely(!default_route))
+ return;
+
+ /*
+ * r->lifetime is 16-bit only so there's no risk it gets
+ * confused for the special 0xffffffff value in
+ * netconfig_set_icmp6_route_data.
+ */
+ netconfig_set_icmp6_route_data(nc, default_route, r->start_time,
+ r->lifetime, r->lifetime,
+ r->mtu, false);
+ } else if (default_route && r->lifetime)
+ netconfig_set_icmp6_route_data(nc, default_route, r->start_time,
+ r->lifetime, r->lifetime,
+ r->mtu, true);
+ else if (default_route && !r->lifetime)
+ netconfig_remove_icmp6_route(nc, default_route);
+
+ /*
+ * Process the onlink and offlink routes, from the Router
+ * Advertisement's Prefix Information options and Route
+ * Information options respectively.
+ */
+ for (i = 0; i < r->n_routes; i++) {
+ const struct route_info *info = &r->routes[i];
+ const uint8_t *gateway = info->onlink ? NULL : r->address;
+ struct l_rtnl_route *route =
+ netconfig_find_icmp6_route(nc, gateway, info);
+
+ if (!route && info->valid_lifetime) {
+ route = netconfig_add_icmp6_route(nc, gateway, info,
+ info->preference);
+ if (unlikely(!route))
+ continue;
+
+ netconfig_set_icmp6_route_data(nc, route, r->start_time,
+ info->preferred_lifetime,
+ info->valid_lifetime,
+ gateway ? r->mtu : 0, false);
+ } else if (route && info->valid_lifetime)
+ netconfig_set_icmp6_route_data(nc, route, r->start_time,
+ info->preferred_lifetime,
+ info->valid_lifetime,
+ gateway ? r->mtu : 0, true);
+ else if (route && !info->valid_lifetime)
+ netconfig_remove_icmp6_route(nc, route);
+ }
+
+ /*
+ * Note: we may be emitting this before L_NETCONFIG_EVENT_CONFIGURE.
+ * We should probably instead save the affected routes in separate
+ * lists and add them to the _CONFIGURE event, suppressing any _UPDATE
+ * events while nc->v6_configured is false.
+ */
+ if (!l_queue_isempty(nc->routes.added) ||
+ !l_queue_isempty(nc->routes.updated) ||
+ !l_queue_isempty(nc->routes.removed))
+ netconfig_emit_event(nc, AF_INET6, L_NETCONFIG_EVENT_UPDATE);
+}
+
LIB_EXPORT struct l_netconfig *l_netconfig_new(uint32_t ifindex)
{
struct l_netconfig *nc;
@@ -378,6 +615,13 @@ LIB_EXPORT struct l_netconfig *l_netconfig_new(uint32_t ifindex)
netconfig_dhcp_event_handler,
nc, NULL);
+ nc->dhcp6_client = l_dhcp6_client_new(ifindex);
+
+ nc->icmp6_client = l_dhcp6_client_get_icmp6(nc->dhcp6_client);
+ l_icmp6_client_add_event_handler(nc->icmp6_client,
+ netconfig_icmp6_event_handler,
+ nc, NULL);
+
return nc;
}
@@ -398,6 +642,7 @@ LIB_EXPORT void l_netconfig_destroy(struct l_netconfig *netconfig)
l_netconfig_set_domain_names_override(netconfig, AF_INET6, NULL);
l_dhcp_client_destroy(netconfig->dhcp_client);
+ l_dhcp6_client_destroy(netconfig->dhcp6_client);
l_netconfig_set_event_handler(netconfig, NULL, NULL, NULL);
l_queue_destroy(netconfig->addresses.current, NULL);
l_queue_destroy(netconfig->addresses.added, NULL);
@@ -731,19 +976,35 @@ configure_ipv6:
if (!netconfig->v6_enabled)
goto done;
- if (netconfig->v6_static_addr && !netconfig->do_static_work) {
+ if (netconfig->v6_static_addr) {
/*
* We're basically ready to configure the interface
* but do this in an idle callback.
*/
- netconfig->do_static_work = l_idle_create(
+ if (!netconfig->do_static_work)
+ netconfig->do_static_work = l_idle_create(
netconfig_do_static_config,
netconfig, NULL);
+
+ goto done;
}
+ if (!l_dhcp6_client_start(netconfig->dhcp6_client))
+ goto stop_ipv4;
+
done:
netconfig->started = true;
return true;
+
+stop_ipv4:
+ if (netconfig->v4_enabled) {
+ if (netconfig->v4_static_addr)
+ l_idle_remove(l_steal_ptr(netconfig->do_static_work));
+ else
+ l_dhcp_client_stop(netconfig->dhcp_client);
+ }
+
+ return false;
}
LIB_EXPORT void l_netconfig_stop(struct l_netconfig *netconfig)
@@ -767,6 +1028,7 @@ LIB_EXPORT void l_netconfig_stop(struct l_netconfig *netconfig)
netconfig->v6_address = NULL;
l_dhcp_client_stop(netconfig->dhcp_client);
+ l_dhcp6_client_stop(netconfig->dhcp6_client);
}
LIB_EXPORT struct l_dhcp_client *l_netconfig_get_dhcp_client(
--
2.32.0
^ permalink raw reply related [flat|nested] 3+ messages in thread
end of thread, other threads:[~2022-05-18 18:42 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-05-13 22:47 [PATCH 04/17] netconfig: Create routes from Router Advertisements Andrew Zaborowski
-- strict thread matches above, loose matches on Subject: below --
2022-05-18 18:42 Denis Kenzior
2022-05-13 14:54 Andrew Zaborowski
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.