Re: [PATCH net-next v2 1/2] ipv6: report why a route was deleted in RTM_DELROUTE

From: Yuyang Huang

Date: Fri Jul 24 2026 - 22:49:05 EST


review feedback from sashiko is correct

https://sashiko.dev/#/patchset/20260723104403.7970-1-sigefriedhyy%40gmail.com

I will send another revision to fix them.

On Thu, Jul 23, 2026 at 7:44 PM Yuyang Huang <sigefriedhyy@xxxxxxxxx> wrote:
>
> Add RTA_DEL_REASON (u8) to RTM_DELROUTE notifications to report the
> cause of kernel-initiated IPv6 route deletions, so user space can tell
> routes that expired from routes explicitly withdrawn by Router
> Advertisements.
>
> The two causes call for different treatment in user space. An expired
> RA route means the router failed to refresh it before its lifetime ran
> out, which points at a misconfigured or unreliable router; user space
> may want to react, for example by disabling IPv6 on that network
> entirely. An RA withdrawal (zero lifetime) is valid, RFC-compliant
> operation and needs no such recovery. Today both surface as an
> identical RTM_DELROUTE.
>
> Plumb the deletion reason through the IPv6 FIB and notification paths:
> - Update `fib6_del`, `fib6_del_route`, and `inet6_rt_notify` signatures
> to explicitly pass the `del_reason` (u8) down the call chain.
> - Update `rt6_fill_node()` to emit `RTA_DEL_REASON` attribute when the
> reason is not UNSPEC.
> - Record the cause in the kernel-initiated IPv6 deletion paths:
> - RTA_DEL_REASON_EXPIRED: for routes cleaned up by the FIB6 garbage
> collector.
> - RTA_DEL_REASON_RA_WITHDRAWN: for default routes, prefix routes, or
> route information option routes withdrawn by Router Advertisements.
> - Update the `rt-route` Netlink YAML spec to define the new attribute,
> notifications, and multicast groups.
>
> Signed-off-by: Yuyang Huang <sigefriedhyy@xxxxxxxxx>
> ---
> Documentation/netlink/specs/rt-route.yaml | 33 ++++++++++++
> include/net/ip6_fib.h | 4 +-
> include/net/ip6_route.h | 2 +
> include/uapi/linux/rtnetlink.h | 17 ++++++
> net/ipv6/addrconf.c | 3 +-
> net/ipv6/ip6_fib.c | 19 ++++---
> net/ipv6/ndisc.c | 7 ++-
> net/ipv6/route.c | 66 +++++++++++++++--------
> 8 files changed, 116 insertions(+), 35 deletions(-)
>
> diff --git a/Documentation/netlink/specs/rt-route.yaml b/Documentation/netlink/specs/rt-route.yaml
> index 33195db96746..234f502fb35d 100644
> --- a/Documentation/netlink/specs/rt-route.yaml
> +++ b/Documentation/netlink/specs/rt-route.yaml
> @@ -78,6 +78,15 @@ definitions:
> -
> name: rta-used
> type: u32
> + -
> + name: del-reason
> + type: enum
> + name-prefix: rta-del-reason-
> + enum-name: rta-del-reason
> + entries:
> + - unspec
> + - expired
> + - ra-withdrawn
>
> attribute-sets:
> -
> @@ -185,6 +194,10 @@ attribute-sets:
> type: u32
> byte-order: big-endian
> display-hint: hex
> + -
> + name: del-reason
> + type: u32
> + enum: del-reason
> -
> name: metrics
> name-prefix: rtax-
> @@ -299,6 +312,7 @@ operations:
> - dport
> - nh-id
> - flowlabel
> + - del-reason
> dump:
> request:
> value: 26
> @@ -322,3 +336,22 @@ operations:
> request:
> value: 25
> attributes: *all-route-attrs
> + -
> + name: newroute-ntf
> + doc: Notification about a created route.
> + value: 24
> + notify: getroute
> + -
> + name: delroute-ntf
> + doc: Notification about a deleted route.
> + value: 25
> + notify: getroute
> +
> +mcast-groups:
> + list:
> + -
> + name: rtnlgrp-ipv4-route
> + value: 7
> + -
> + name: rtnlgrp-ipv6-route
> + value: 11
> diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h
> index 9cd27e1b9b69..7ca0e2124e79 100644
> --- a/include/net/ip6_fib.h
> +++ b/include/net/ip6_fib.h
> @@ -468,7 +468,7 @@ void fib6_clean_all_skip_notify(struct net *net,
>
> int fib6_add(struct fib6_node *root, struct fib6_info *rt,
> struct nl_info *info, struct netlink_ext_ack *extack);
> -int fib6_del(struct fib6_info *rt, struct nl_info *info);
> +int fib6_del(struct fib6_info *rt, struct nl_info *info, u32 del_reason);
>
> static inline
> void rt6_get_prefsrc(const struct rt6_info *rt, struct in6_addr *addr)
> @@ -531,7 +531,7 @@ static inline void fib6_rt_update(struct net *net, struct fib6_info *rt,
> }
> #endif
> void inet6_rt_notify(int event, struct fib6_info *rt, struct nl_info *info,
> - unsigned int flags);
> + unsigned int flags, u32 del_reason);
>
> void fib6_age_exceptions(struct fib6_info *rt, struct fib6_gc_args *gc_args,
> unsigned long now);
> diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h
> index 09ffe0f13ce7..98f1684fe378 100644
> --- a/include/net/ip6_route.h
> +++ b/include/net/ip6_route.h
> @@ -128,6 +128,8 @@ int ip6_route_add(struct fib6_config *cfg, gfp_t gfp_flags,
> int ip6_ins_rt(struct net *net, struct fib6_info *f6i);
> #if IS_ENABLED(CONFIG_IPV6)
> int ip6_del_rt(struct net *net, struct fib6_info *f6i, bool skip_notify);
> +int ip6_del_rt_reason(struct net *net, struct fib6_info *f6i, bool skip_notify,
> + u32 del_reason);
> #else
> static inline int ip6_del_rt(struct net *net, struct fib6_info *f6i,
> bool skip_notify)
> diff --git a/include/uapi/linux/rtnetlink.h b/include/uapi/linux/rtnetlink.h
> index 27265fd31e5f..fe00e624f3c3 100644
> --- a/include/uapi/linux/rtnetlink.h
> +++ b/include/uapi/linux/rtnetlink.h
> @@ -399,6 +399,7 @@ enum rtattr_type_t {
> RTA_DPORT,
> RTA_NH_ID,
> RTA_FLOWLABEL,
> + RTA_DEL_REASON,
> __RTA_MAX
> };
>
> @@ -407,6 +408,22 @@ enum rtattr_type_t {
> #define RTM_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct rtmsg))))
> #define RTM_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct rtmsg))
>
> +/* RTA_DEL_REASON: why the kernel deleted the route. u32.
> + * Emitted only on RTM_DELROUTE notifications, and only when the deletion
> + * path records a cause. Absence means either an older kernel or a
> + * deletion path that does not (yet) record its cause - consumers must
> + * treat "absent" and "unspec" identically. New causes may be appended.
> + * Currently only IPv6 deletion paths record a cause.
> + */
> +enum rta_del_reason {
> + RTA_DEL_REASON_UNSPEC, /* cause not recorded */
> + RTA_DEL_REASON_EXPIRED, /* RTF_EXPIRES lifetime ran out (GC) */
> + RTA_DEL_REASON_RA_WITHDRAWN, /* zero-lifetime RA / PIO / RIO */
> + __RTA_DEL_REASON_MAX
> +};
> +
> +#define RTA_DEL_REASON_MAX (__RTA_DEL_REASON_MAX - 1)
> +
> /* RTM_MULTIPATH --- array of struct rtnexthop.
> *
> * "struct rtnexthop" describes all necessary nexthop information,
> diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
> index f1fe9ede1edb..1ca9d82b5721 100644
> --- a/net/ipv6/addrconf.c
> +++ b/net/ipv6/addrconf.c
> @@ -2874,7 +2874,8 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
> if (rt) {
> /* Autoconf prefix route */
> if (valid_lft == 0) {
> - ip6_del_rt(net, rt, false);
> + ip6_del_rt_reason(net, rt, false,
> + RTA_DEL_REASON_RA_WITHDRAWN);
> rt = NULL;
> } else {
> table = rt->fib6_table;
> diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c
> index a130cdfaebfb..32b9a00956a0 100644
> --- a/net/ipv6/ip6_fib.c
> +++ b/net/ipv6/ip6_fib.c
> @@ -1286,7 +1286,8 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct fib6_info *rt,
> rcu_assign_pointer(rt->fib6_node, fn);
> rcu_assign_pointer(*ins, rt);
> if (!info->skip_notify)
> - inet6_rt_notify(RTM_NEWROUTE, rt, info, nlflags);
> + inet6_rt_notify(RTM_NEWROUTE, rt, info, nlflags,
> + RTA_DEL_REASON_UNSPEC);
> info->nl_net->ipv6.rt6_stats->fib_rt_entries++;
>
> if (!(fn->fn_flags & RTN_RTINFO)) {
> @@ -1317,7 +1318,8 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct fib6_info *rt,
> rt->fib6_next = iter->fib6_next;
> rcu_assign_pointer(*ins, rt);
> if (!info->skip_notify)
> - inet6_rt_notify(RTM_NEWROUTE, rt, info, NLM_F_REPLACE);
> + inet6_rt_notify(RTM_NEWROUTE, rt, info, NLM_F_REPLACE,
> + RTA_DEL_REASON_UNSPEC);
> if (!(fn->fn_flags & RTN_RTINFO)) {
> info->nl_net->ipv6.rt6_stats->fib_route_nodes++;
> fn->fn_flags |= RTN_RTINFO;
> @@ -1966,7 +1968,8 @@ static struct fib6_node *fib6_repair_tree(struct net *net,
> }
>
> static void fib6_del_route(struct fib6_table *table, struct fib6_node *fn,
> - struct fib6_info __rcu **rtp, struct nl_info *info)
> + struct fib6_info __rcu **rtp, struct nl_info *info,
> + u32 del_reason)
> {
> struct fib6_info *leaf, *replace_rt = NULL;
> struct fib6_walker *w;
> @@ -2055,13 +2058,13 @@ static void fib6_del_route(struct fib6_table *table, struct fib6_node *fn,
> call_fib6_entry_notifiers_replace(net, replace_rt);
> }
> if (!info->skip_notify)
> - inet6_rt_notify(RTM_DELROUTE, rt, info, 0);
> + inet6_rt_notify(RTM_DELROUTE, rt, info, 0, del_reason);
>
> fib6_info_release(rt);
> }
>
> /* Need to own table->tb6_lock */
> -int fib6_del(struct fib6_info *rt, struct nl_info *info)
> +int fib6_del(struct fib6_info *rt, struct nl_info *info, u32 del_reason)
> {
> struct net *net = info->nl_net;
> struct fib6_info __rcu **rtp;
> @@ -2090,7 +2093,7 @@ int fib6_del(struct fib6_info *rt, struct nl_info *info)
> if (rt == cur) {
> if (fib6_requires_src(cur))
> fib6_routes_require_src_dec(info->nl_net);
> - fib6_del_route(table, fn, rtp, info);
> + fib6_del_route(table, fn, rtp, info, del_reason);
> return 0;
> }
> rtp_next = &cur->fib6_next;
> @@ -2252,7 +2255,7 @@ static int fib6_clean_node(struct fib6_walker *w)
> res = c->func(rt, c->arg);
> if (res == -1) {
> w->leaf = rt;
> - res = fib6_del(rt, &info);
> + res = fib6_del(rt, &info, RTA_DEL_REASON_UNSPEC);
> if (res) {
> #if RT6_DEBUG >= 2
> pr_debug("%s: del failed: rt=%p@%p err=%d\n",
> @@ -2400,7 +2403,7 @@ static void fib6_gc_table(struct net *net,
>
> hlist_for_each_entry_safe(rt, n, &tb6->tb6_gc_hlist, gc_link)
> if (fib6_age(rt, gc_args) == -1)
> - fib6_del(rt, &info);
> + fib6_del(rt, &info, RTA_DEL_REASON_EXPIRED);
> }
>
> static void fib6_gc_all(struct net *net, struct fib6_gc_args *gc_args)
> diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
> index f867ec8d3d90..c3537fc0ec63 100644
> --- a/net/ipv6/ndisc.c
> +++ b/net/ipv6/ndisc.c
> @@ -1361,7 +1361,9 @@ static enum skb_drop_reason ndisc_router_discovery(struct sk_buff *skb)
> defrtr_usr_metric = in6_dev->cnf.ra_defrtr_metric;
> /* delete the route if lifetime is 0 or if metric needs change */
> if (rt && (lifetime == 0 || rt->fib6_metric != defrtr_usr_metric)) {
> - ip6_del_rt(net, rt, false);
> + ip6_del_rt_reason(net, rt, false,
> + lifetime == 0 ? RTA_DEL_REASON_RA_WITHDRAWN :
> + RTA_DEL_REASON_UNSPEC);
> rt = NULL;
> }
>
> @@ -1396,7 +1398,8 @@ static enum skb_drop_reason ndisc_router_discovery(struct sk_buff *skb)
> .nl_net = net,
> };
> rt->fib6_flags = (rt->fib6_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);
> - inet6_rt_notify(RTM_NEWROUTE, rt, &nlinfo, NLM_F_REPLACE);
> + inet6_rt_notify(RTM_NEWROUTE, rt, &nlinfo, NLM_F_REPLACE,
> + RTA_DEL_REASON_UNSPEC);
> }
>
> if (rt) {
> diff --git a/net/ipv6/route.c b/net/ipv6/route.c
> index a1301334da48..e8ed95dc5665 100644
> --- a/net/ipv6/route.c
> +++ b/net/ipv6/route.c
> @@ -111,7 +111,7 @@ static int rt6_fill_node(struct net *net, struct sk_buff *skb,
> struct fib6_info *rt, struct dst_entry *dst,
> struct in6_addr *dest, struct in6_addr *src,
> int iif, int type, u32 portid, u32 seq,
> - unsigned int flags);
> + unsigned int flags, u32 del_reason);
> static struct rt6_info *rt6_find_cached_rt(const struct fib6_result *res,
> const struct in6_addr *daddr,
> const struct in6_addr *saddr);
> @@ -1020,7 +1020,7 @@ int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
> gwaddr, dev);
>
> if (rt && !lifetime) {
> - ip6_del_rt(net, rt, false);
> + ip6_del_rt_reason(net, rt, false, RTA_DEL_REASON_RA_WITHDRAWN);
> rt = NULL;
> }
>
> @@ -3973,7 +3973,8 @@ int ip6_route_add(struct fib6_config *cfg, gfp_t gfp_flags,
> return err;
> }
>
> -static int __ip6_del_rt(struct fib6_info *rt, struct nl_info *info)
> +static int __ip6_del_rt(struct fib6_info *rt, struct nl_info *info,
> + u32 del_reason)
> {
> struct net *net = info->nl_net;
> struct fib6_table *table;
> @@ -3986,7 +3987,7 @@ static int __ip6_del_rt(struct fib6_info *rt, struct nl_info *info)
>
> table = rt->fib6_table;
> spin_lock_bh(&table->tb6_lock);
> - err = fib6_del(rt, info);
> + err = fib6_del(rt, info, del_reason);
> spin_unlock_bh(&table->tb6_lock);
>
> out:
> @@ -3994,14 +3995,20 @@ static int __ip6_del_rt(struct fib6_info *rt, struct nl_info *info)
> return err;
> }
>
> -int ip6_del_rt(struct net *net, struct fib6_info *rt, bool skip_notify)
> +int ip6_del_rt_reason(struct net *net, struct fib6_info *rt, bool skip_notify,
> + u32 del_reason)
> {
> struct nl_info info = {
> .nl_net = net,
> - .skip_notify = skip_notify
> + .skip_notify = skip_notify,
> };
>
> - return __ip6_del_rt(rt, &info);
> + return __ip6_del_rt(rt, &info, del_reason);
> +}
> +
> +int ip6_del_rt(struct net *net, struct fib6_info *rt, bool skip_notify)
> +{
> + return ip6_del_rt_reason(net, rt, skip_notify, RTA_DEL_REASON_UNSPEC);
> }
>
> static int __ip6_del_rt_siblings(struct fib6_info *rt, struct fib6_config *cfg)
> @@ -4028,7 +4035,8 @@ static int __ip6_del_rt_siblings(struct fib6_info *rt, struct fib6_config *cfg)
>
> if (rt6_fill_node(net, skb, rt, NULL,
> NULL, NULL, 0, RTM_DELROUTE,
> - info->portid, seq, 0) < 0) {
> + info->portid, seq, 0,
> + RTA_DEL_REASON_UNSPEC) < 0) {
> kfree_skb(skb);
> skb = NULL;
> } else
> @@ -4064,13 +4072,13 @@ static int __ip6_del_rt_siblings(struct fib6_info *rt, struct fib6_config *cfg)
> list_for_each_entry_safe(sibling, next_sibling,
> &rt->fib6_siblings,
> fib6_siblings) {
> - err = fib6_del(sibling, info);
> + err = fib6_del(sibling, info, RTA_DEL_REASON_UNSPEC);
> if (err)
> goto out_unlock;
> }
> }
>
> - err = fib6_del(rt, info);
> + err = fib6_del(rt, info, RTA_DEL_REASON_UNSPEC);
> out_unlock:
> spin_unlock_bh(&table->tb6_lock);
> out_put:
> @@ -4196,7 +4204,8 @@ static int ip6_route_del(struct fib6_config *cfg,
> if (!fib6_info_hold_safe(rt))
> continue;
>
> - err = __ip6_del_rt(rt, &cfg->fc_nlinfo);
> + err = __ip6_del_rt(rt, &cfg->fc_nlinfo,
> + RTA_DEL_REASON_UNSPEC);
> break;
> }
> if (cfg->fc_nh_id)
> @@ -4215,7 +4224,8 @@ static int ip6_route_del(struct fib6_config *cfg,
>
> /* if gateway was specified only delete the one hop */
> if (cfg->fc_flags & RTF_GATEWAY)
> - err = __ip6_del_rt(rt, &cfg->fc_nlinfo);
> + err = __ip6_del_rt(rt, &cfg->fc_nlinfo,
> + RTA_DEL_REASON_UNSPEC);
> else
> err = __ip6_del_rt_siblings(rt, cfg);
> break;
> @@ -5381,7 +5391,8 @@ static void ip6_route_mpath_notify(struct fib6_info *rt,
> }
>
> if (rt)
> - inet6_rt_notify(RTM_NEWROUTE, rt, info, nlflags);
> + inet6_rt_notify(RTM_NEWROUTE, rt, info, nlflags,
> + RTA_DEL_REASON_UNSPEC);
>
> rcu_read_unlock();
> }
> @@ -5739,6 +5750,7 @@ static size_t rt6_nlmsg_size(struct fib6_info *f6i)
> + nla_total_size(sizeof(struct rta_cacheinfo))
> + nla_total_size(TCP_CA_NAME_MAX) /* RTAX_CC_ALGO */
> + nla_total_size(1) /* RTA_PREF */
> + + nla_total_size(4) /* RTA_DEL_REASON */
> + nexthop_len;
> }
>
> @@ -5775,7 +5787,7 @@ static int rt6_fill_node(struct net *net, struct sk_buff *skb,
> struct fib6_info *rt, struct dst_entry *dst,
> struct in6_addr *dest, struct in6_addr *src,
> int iif, int type, u32 portid, u32 seq,
> - unsigned int flags)
> + unsigned int flags, u32 del_reason)
> {
> struct rt6_info *rt6 = dst_rt6_info(dst);
> struct rt6key *rt6_dst, *rt6_src;
> @@ -5954,6 +5966,10 @@ static int rt6_fill_node(struct net *net, struct sk_buff *skb,
> if (rtnl_put_cacheinfo(skb, dst, 0, expires, dst ? dst->error : 0) < 0)
> goto nla_put_failure;
>
> + if (type == RTM_DELROUTE && del_reason != RTA_DEL_REASON_UNSPEC &&
> + nla_put_u32(skb, RTA_DEL_REASON, del_reason))
> + goto nla_put_failure;
> +
> if (nla_put_u8(skb, RTA_PREF, IPV6_EXTRACT_PREF(rt6_flags)))
> goto nla_put_failure;
>
> @@ -6055,7 +6071,8 @@ static int rt6_nh_dump_exceptions(struct fib6_nh *nh, void *arg)
> &rt6_ex->rt6i->dst, NULL, NULL, 0,
> RTM_NEWROUTE,
> NETLINK_CB(dump->cb->skb).portid,
> - dump->cb->nlh->nlmsg_seq, w->flags);
> + dump->cb->nlh->nlmsg_seq, w->flags,
> + RTA_DEL_REASON_UNSPEC);
> if (err)
> return err;
>
> @@ -6103,7 +6120,8 @@ int rt6_dump_route(struct fib6_info *rt, void *p_arg, unsigned int skip)
> if (rt6_fill_node(net, arg->skb, rt, NULL, NULL, NULL,
> 0, RTM_NEWROUTE,
> NETLINK_CB(arg->cb->skb).portid,
> - arg->cb->nlh->nlmsg_seq, flags)) {
> + arg->cb->nlh->nlmsg_seq, flags,
> + RTA_DEL_REASON_UNSPEC)) {
> return 0;
> }
> count++;
> @@ -6336,12 +6354,14 @@ static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
> err = rt6_fill_node(net, skb, from, NULL, NULL, NULL,
> iif, RTM_NEWROUTE,
> NETLINK_CB(in_skb).portid,
> - nlh->nlmsg_seq, 0);
> + nlh->nlmsg_seq, 0,
> + RTA_DEL_REASON_UNSPEC);
> else
> err = rt6_fill_node(net, skb, from, dst, &fl6.daddr,
> &fl6.saddr, iif, RTM_NEWROUTE,
> NETLINK_CB(in_skb).portid,
> - nlh->nlmsg_seq, 0);
> + nlh->nlmsg_seq, 0,
> + RTA_DEL_REASON_UNSPEC);
> } else {
> err = -ENETUNREACH;
> }
> @@ -6358,7 +6378,7 @@ static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
> }
>
> void inet6_rt_notify(int event, struct fib6_info *rt, struct nl_info *info,
> - unsigned int nlm_flags)
> + unsigned int nlm_flags, u32 del_reason)
> {
> struct net *net = info->nl_net;
> struct sk_buff *skb;
> @@ -6377,7 +6397,8 @@ void inet6_rt_notify(int event, struct fib6_info *rt, struct nl_info *info,
> goto errout;
>
> err = rt6_fill_node(net, skb, rt, NULL, NULL, NULL, 0,
> - event, info->portid, seq, nlm_flags);
> + event, info->portid, seq, nlm_flags,
> + del_reason);
> if (err < 0) {
> kfree_skb(skb);
> /* -EMSGSIZE implies needed space grew under us. */
> @@ -6410,7 +6431,8 @@ void fib6_rt_update(struct net *net, struct fib6_info *rt,
> goto errout;
>
> err = rt6_fill_node(net, skb, rt, NULL, NULL, NULL, 0,
> - RTM_NEWROUTE, info->portid, seq, NLM_F_REPLACE);
> + RTM_NEWROUTE, info->portid, seq, NLM_F_REPLACE,
> + RTA_DEL_REASON_UNSPEC);
> if (err < 0) {
> /* -EMSGSIZE implies BUG in rt6_nlmsg_size() */
> WARN_ON(err == -EMSGSIZE);
> @@ -6463,7 +6485,7 @@ void fib6_info_hw_flags_set(struct net *net, struct fib6_info *f6i,
> }
>
> err = rt6_fill_node(net, skb, f6i, NULL, NULL, NULL, 0, RTM_NEWROUTE, 0,
> - 0, 0);
> + 0, 0, RTA_DEL_REASON_UNSPEC);
> if (err < 0) {
> /* -EMSGSIZE implies BUG in rt6_nlmsg_size() */
> WARN_ON(err == -EMSGSIZE);
> --
> 2.43.0
>