[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