[odhcpd PATCH 2/2] router: skip RA and wait for LINK-LOCAL to be assigned
Hans Dedecker
dedeckeh at gmail.com
Sat Mar 18 13:41:45 PDT 2023
On Fri, Mar 17, 2023 at 1:54 AM Christian Marangi <ansuelsmth at gmail.com> wrote:
>
> This fix a specific and corner case when the following error and similar
> is printed in the log:
>
> Failed to send to ff02::1%br-lan (Address not available)
>
> The cause for this was tracked down to the lack of the interface of a
> configured LINK-LOCAL IPV6 address resulting in odhcpd_send() always
> failing.
>
> A LINK-LOCAL IPV6 address is assigned only after the interface has
> carrier and is set to IFF_RUNNING and require some time for the address
> to be assigned due to DAD logic.
>
> In the case where an interface was just UP, odhcpd RA may fail since the
> LINK-LOCAL IPV6 address still needs to be assigned as it still need to
> be "trained". From the kernel view this is flagged in the IPV6 interface
> address with the flag IFA_F_TENTATIVE, that means the address still
> needs to be checked and follow DAD process.
>
> This is only a transient problem and the DAD process is required only
> once till the interface is not set DOWN.
>
> To handle this, add some check to verify if the address has to be
> checked and add an additional bool to flag if the interface have a
> LINK-LOCAL assigned.
>
> Skip sending RA if the interface still doesn't have finished the DAD
> process and retry at the next RA.
> A notice log is added to track this special case to track problematic
> case and even more corner case.
>
> Logic to check if interface have LINK-LOCAL are:
> - When interface is setup, on scanning for the interface ipv6 address
> check if at least one address is NOT in IFA_F_TENTATIVE state.
> - With interface already up but with still no LINK-LOCAL react on the
> RTM_NEWADDR event and set LINK-LOCAL if the addrs added by the event
> is a LINK-LOCAL reflecting that the interface finally ended the DAD
> process and have a correct address.
>
> Signed-off-by: Christian Marangi <ansuelsmth at gmail.com>
> ---
> src/config.c | 9 +++++++++
> src/netlink.c | 11 ++++++++++-
> src/odhcpd.h | 2 ++
> src/router.c | 6 ++++++
> 4 files changed, 27 insertions(+), 1 deletion(-)
>
> diff --git a/src/config.c b/src/config.c
> index 30da879..ee7219f 100644
> --- a/src/config.c
> +++ b/src/config.c
> @@ -594,6 +594,15 @@ int config_parse_interface(void *data, size_t len, const char *name, bool overwr
> if (len > 0)
> iface->addr6_len = len;
>
> + for (size_t i = 0; i < iface->addr6_len; i++) {
> + struct odhcpd_ipaddr *addr = &iface->addr6[i];
> +
> + if (!addr->tentative) {
> + iface->have_link_local = true;
> + break;
> + }
> + }
> +
> len = netlink_get_interface_addrs(iface->ifindex,
> false, &iface->addr4);
> if (len > 0)
> diff --git a/src/netlink.c b/src/netlink.c
> index 4a352a6..0a2da03 100644
> --- a/src/netlink.c
> +++ b/src/netlink.c
> @@ -386,7 +386,7 @@ static int handle_rtm_addr(struct nlmsghdr *hdr, bool add)
>
> nla_memcpy(&event_info.addr, nla[IFA_ADDRESS], sizeof(event_info.addr));
>
> - if (IN6_IS_ADDR_LINKLOCAL(&event_info.addr) || IN6_IS_ADDR_MULTICAST(&event_info.addr))
> + if (IN6_IS_ADDR_MULTICAST(&event_info.addr))
> return NL_SKIP;
>
> inet_ntop(AF_INET6, &event_info.addr, buf, sizeof(buf));
> @@ -395,6 +395,11 @@ static int handle_rtm_addr(struct nlmsghdr *hdr, bool add)
> if (iface->ifindex != (int)ifa->ifa_index)
> continue;
>
> + if (add && IN6_IS_ADDR_LINKLOCAL(&event_info.addr)) {
> + iface->have_link_local = true;
> + return NL_SKIP;
> + }
> +
> syslog(LOG_DEBUG, "Netlink %s %s on %s", add ? "newaddr" : "deladdr",
> buf, iface->name);
>
> @@ -625,6 +630,10 @@ static int cb_addr_valid(struct nl_msg *msg, void *arg)
> if (ifa->ifa_flags & IFA_F_DEPRECATED)
> addrs[ctxt->ret].preferred = 0;
>
> + if (ifa->ifa_family == AF_INET6 &&
> + ifa->ifa_flags & IFA_F_TENTATIVE)
> + addrs[ctxt->ret].tentative = true;
> +
> ctxt->ret++;
> *(ctxt->addrs) = addrs;
>
> diff --git a/src/odhcpd.h b/src/odhcpd.h
> index d829033..0550bc2 100644
> --- a/src/odhcpd.h
> +++ b/src/odhcpd.h
> @@ -131,6 +131,7 @@ struct odhcpd_ipaddr {
> struct {
> uint8_t dprefix;
> uint8_t invalid_advertisements;
> + bool tentative;
> };
>
> /* ipv4 only */
> @@ -300,6 +301,7 @@ struct interface {
> bool ra_useleasetime;
> bool ra_dns;
> bool no_dynamic_dhcp;
> + bool have_link_local;
> uint8_t pio_filter_length;
> struct in6_addr pio_filter_addr;
> int default_router;
> diff --git a/src/router.c b/src/router.c
> index 7e66e3c..5c518b1 100644
> --- a/src/router.c
> +++ b/src/router.c
> @@ -621,6 +621,11 @@ static int send_router_advert(struct interface *iface, const struct in6_addr *fr
> msecs = calc_adv_interval(iface, minvalid, &maxival);
> lifetime = calc_ra_lifetime(iface, maxival);
>
> + if (!iface->have_link_local) {
> + syslog(LOG_NOTICE, "Skip sending a RA on %s as still not ready", iface->name);
Log the real reason of the failure by replacing "still not ready" by
"no link local address is available"
> + goto out;
> + }
> +
> if (default_route && valid_prefix) {
> adv.h.nd_ra_router_lifetime = htons(lifetime < UINT16_MAX ? lifetime : UINT16_MAX);
> } else {
> @@ -782,6 +787,7 @@ static int send_router_advert(struct interface *iface, const struct in6_addr *fr
> if (odhcpd_send(iface->router_event.uloop.fd, &dest, iov, ARRAY_SIZE(iov), iface) > 0)
> iface->ra_sent++;
>
> +out:
> free(pfxs);
> free(routes);
>
> --
> 2.39.2
>
>
> _______________________________________________
> openwrt-devel mailing list
> openwrt-devel at lists.openwrt.org
> https://lists.openwrt.org/mailman/listinfo/openwrt-devel
More information about the openwrt-devel
mailing list