* [PATCH 03/13] netconfig: Create routes from Router Advertisements
@ 2022-05-05 23:15 Andrew Zaborowski
0 siblings, 0 replies; 4+ messages in thread
From: Andrew Zaborowski @ 2022-05-05 23:15 UTC (permalink / raw
To: ell
[-- Attachment #1: Type: text/plain, Size: 10532 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 | 248 +++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 246 insertions(+), 2 deletions(-)
diff --git a/ell/netconfig.c b/ell/netconfig.c
index 6736913..81cca82 100644
--- a/ell/netconfig.c
+++ b/ell/netconfig.c
@@ -27,12 +27,16 @@
#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 "queue.h"
@@ -63,6 +67,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;
@@ -101,6 +107,11 @@ union netconfig_addr {
struct in6_addr v6;
};
+static bool netconfig_match_ptr(const void *a, const void *b)
+{
+ return a == b;
+}
+
static void netconfig_update_cleanup(struct l_netconfig *nc)
{
l_queue_clear(nc->addresses.added, NULL);
@@ -356,6 +367,224 @@ 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,
+ (dst->prefix_len + 7) / 8)))
+ 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;
+ char buf1[INET6_ADDRSTRLEN];
+ char buf2[INET6_ADDRSTRLEN];
+
+ if (gateway && !dst)
+ rt = l_rtnl_route_new_gateway(inet_ntop(AF_INET6, gateway,
+ buf1, sizeof(buf1)));
+ else if (dst && !gateway)
+ rt = l_rtnl_route_new_prefix(inet_ntop(AF_INET6, dst->address,
+ buf2, sizeof(buf2)),
+ dst->prefix_len);
+ else
+ rt = l_rtnl_route_new_static(inet_ntop(AF_INET6, gateway,
+ buf1, sizeof(buf1)),
+ inet_ntop(AF_INET6,
+ dst->address,
+ buf2, sizeof(buf2)),
+ dst->prefix_len);
+
+ if (L_WARN_ON(!rt))
+ return NULL;
+
+ l_rtnl_route_set_preference(rt, preference);
+ l_rtnl_route_set_protocol(rt, RTPROT_RA);
+ l_rtnl_route_set_priority(rt, nc->route_priority);
+ 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 FC4861
+ * 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 &&
+ !l_queue_find(nc->routes.added, netconfig_match_ptr,
+ 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 +607,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 +634,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,16 +968,22 @@ 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))
+ return false;
+
done:
netconfig->started = true;
return true;
@@ -767,6 +1010,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] 4+ messages in thread
* Re: [PATCH 03/13] netconfig: Create routes from Router Advertisements
@ 2022-05-12 16:14 Denis Kenzior
0 siblings, 0 replies; 4+ messages in thread
From: Denis Kenzior @ 2022-05-12 16:14 UTC (permalink / raw
To: ell
[-- Attachment #1: Type: text/plain, Size: 4622 bytes --]
Hi Andrew,
On 5/5/22 18:15, 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 | 248 +++++++++++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 246 insertions(+), 2 deletions(-)
>
<snip>
>
> +static bool netconfig_match_ptr(const void *a, const void *b)
> +{
> + return a == b;
> +}
> +
Ugh, can we use l_queue_get_entries loop directly or a helper function instead?
> static void netconfig_update_cleanup(struct l_netconfig *nc)
> {
> l_queue_clear(nc->addresses.added, NULL);
> @@ -356,6 +367,224 @@ 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,
> + (dst->prefix_len + 7) / 8)))
This might need a utility function, something like l_net_prefix_matches?
> + 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;
> + char buf1[INET6_ADDRSTRLEN];
> + char buf2[INET6_ADDRSTRLEN];
> +
> + if (gateway && !dst)
> + rt = l_rtnl_route_new_gateway(inet_ntop(AF_INET6, gateway,
> + buf1, sizeof(buf1)));
> + else if (dst && !gateway)
> + rt = l_rtnl_route_new_prefix(inet_ntop(AF_INET6, dst->address,
> + buf2, sizeof(buf2)),
> + dst->prefix_len);
> + else
> + rt = l_rtnl_route_new_static(inet_ntop(AF_INET6, gateway,
> + buf1, sizeof(buf1)),
> + inet_ntop(AF_INET6,
> + dst->address,
> + buf2, sizeof(buf2)),
> + dst->prefix_len);
We can move l_rtnl_route structure definition into rtnl-private.h if you prefer
to fill these in directly?
> +
> + if (L_WARN_ON(!rt))
> + return NULL;
> +
> + l_rtnl_route_set_preference(rt, preference);
> + l_rtnl_route_set_protocol(rt, RTPROT_RA);
> + l_rtnl_route_set_priority(rt, nc->route_priority);
> + l_queue_push_tail(nc->routes.current, rt);
> + l_queue_push_tail(nc->routes.added, rt);
> + return rt;
> +}
> +
<snip>
> @@ -398,6 +634,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,16 +968,22 @@ 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))
> + return false;
What about the dhcp4 client started earlier?
> +
> done:
> netconfig->started = true;
> return true;
<snip>
Regards,
-Denis
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH 03/13] netconfig: Create routes from Router Advertisements
@ 2022-05-13 12:46 Andrew Zaborowski
0 siblings, 0 replies; 4+ messages in thread
From: Andrew Zaborowski @ 2022-05-13 12:46 UTC (permalink / raw
To: ell
[-- Attachment #1: Type: text/plain, Size: 6136 bytes --]
On Thu, 12 May 2022 at 18:14, Denis Kenzior <denkenz(a)gmail.com> wrote:
> On 5/5/22 18:15, Andrew Zaborowski wrote:
> > +static bool netconfig_match_ptr(const void *a, const void *b)
> > +{
> > + return a == b;
> > +}
> > +
>
> Ugh, can we use l_queue_get_entries loop directly or a helper function instead?
Ok. We could also add a l_queue_match_ptr in queue.h since IIRC we
also have a copy in IWD.
>
> > static void netconfig_update_cleanup(struct l_netconfig *nc)
> > {
> > l_queue_clear(nc->addresses.added, NULL);
> > @@ -356,6 +367,224 @@ 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,
> > + (dst->prefix_len + 7) / 8)))
>
> This might need a utility function, something like l_net_prefix_matches?
Well this could just be switched to l_net_prefix_matches() but since
we know the host bits of the address (interface ID) are zero, let me
just memcmp() the 16 bytes.
>
> > + 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;
> > + char buf1[INET6_ADDRSTRLEN];
> > + char buf2[INET6_ADDRSTRLEN];
> > +
> > + if (gateway && !dst)
> > + rt = l_rtnl_route_new_gateway(inet_ntop(AF_INET6, gateway,
> > + buf1, sizeof(buf1)));
> > + else if (dst && !gateway)
> > + rt = l_rtnl_route_new_prefix(inet_ntop(AF_INET6, dst->address,
> > + buf2, sizeof(buf2)),
> > + dst->prefix_len);
> > + else
> > + rt = l_rtnl_route_new_static(inet_ntop(AF_INET6, gateway,
> > + buf1, sizeof(buf1)),
> > + inet_ntop(AF_INET6,
> > + dst->address,
> > + buf2, sizeof(buf2)),
> > + dst->prefix_len);
>
> We can move l_rtnl_route structure definition into rtnl-private.h if you prefer
> to fill these in directly?
Ok.
>
> > +
> > + if (L_WARN_ON(!rt))
> > + return NULL;
> > +
> > + l_rtnl_route_set_preference(rt, preference);
> > + l_rtnl_route_set_protocol(rt, RTPROT_RA);
> > + l_rtnl_route_set_priority(rt, nc->route_priority);
> > + l_queue_push_tail(nc->routes.current, rt);
> > + l_queue_push_tail(nc->routes.added, rt);
> > + return rt;
> > +}
> > +
>
> <snip>
>
> > @@ -398,6 +634,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,16 +968,22 @@ 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))
> > + return false;
>
> What about the dhcp4 client started earlier?
Good point, I'll add proper cleanup for this.
Best regards
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH 03/13] netconfig: Create routes from Router Advertisements
@ 2022-05-13 13:39 Denis Kenzior
0 siblings, 0 replies; 4+ messages in thread
From: Denis Kenzior @ 2022-05-13 13:39 UTC (permalink / raw
To: ell
[-- Attachment #1: Type: text/plain, Size: 343 bytes --]
Hi Andrew,
>> Ugh, can we use l_queue_get_entries loop directly or a helper function instead?
>
> Ok. We could also add a l_queue_match_ptr in queue.h since IIRC we
> also have a copy in IWD.
>
I don't like the idea of adding a matching function for comparing pointers. How
about l_queue_find_ptr() instead?
Regards,
-Denis
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2022-05-13 13:39 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2022-05-12 16:14 [PATCH 03/13] netconfig: Create routes from Router Advertisements Denis Kenzior
-- strict thread matches above, loose matches on Subject: below --
2022-05-13 13:39 Denis Kenzior
2022-05-13 12:46 Andrew Zaborowski
2022-05-05 23:15 Andrew Zaborowski
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).