On Wed, Jul 29, 2026 at 09:07:30PM +0900, Yuyang Huang wrote: > Add RTA_DEL_REASON and enum rta_del_reason to the rtnetlink uAPI, and > add ip6_del_rt_reason(), which takes the reason a route is being > deleted. ip6_del_rt() becomes a wrapper that passes > RTA_DEL_REASON_UNSPEC, so its callers do not change. > > The reason is unused for now. Subsequent patches propagate it to the > deletion path and report it on RTM_DELROUTE. > > Signed-off-by: Yuyang Huang <[email protected]> > --- > include/net/ip6_route.h | 9 +++++++++ > include/uapi/linux/rtnetlink.h | 17 +++++++++++++++++ > net/ipv6/route.c | 8 +++++++- > 3 files changed, 33 insertions(+), 1 deletion(-) > > diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h > index 09ffe0f13ce7..92ad5a0d03a2 100644 > --- a/include/net/ip6_route.h > +++ b/include/net/ip6_route.h > @@ -128,12 +128,21 @@ 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, > + enum rta_del_reason del_reason);
>From Sashiko: " This isn't a bug, but was carrying the reason in struct nl_info considered instead of adding a parallel argument? " I did consider it, but it doesn't look like a good fit to carry a route deletion reason in a generic netlink structure (also used by nexthops). It later says: " One counter-point worth noting: rt6_fill_node() is also reached from dump, GET and hw-flags paths that have no struct nl_info at all, so an explicit parameter there is unavoidable either way, and nl_info is shared with IPv4, MPLS and nexthop code. " https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260729120739.47880-1-sigefriedhyy%40gmail.com > #else > static inline int ip6_del_rt(struct net *net, struct fib6_info *f6i, > bool skip_notify) > { > return -EAFNOSUPPORT; > } > + > +static inline int ip6_del_rt_reason(struct net *net, struct fib6_info *f6i, > + bool skip_notify, > + enum rta_del_reason del_reason) > +{ > + return -EAFNOSUPPORT; > +} > #endif > > void rt6_flush_exceptions(struct fib6_info *f6i); > 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) Valid comments from Sashiko: " This isn't a bug, but since these names become uAPI on first release, is the RTA_ prefix the right choice for the payload values? RTA_ is the established prefix for route attribute ids in enum rtattr_type_t, and RTA_DEL_REASON is added to that very enum a few lines above. So RTA_DEL_REASON_EXPIRED reads like an attribute id, and RTA_DEL_REASON_MAX visually parallels RTA_MAX while meaning something completely different. Elsewhere the payload value space of an attribute gets its own prefix, for instance LWTUNNEL_ENCAP_* for RTA_ENCAP_TYPE. [....] RTA_DEL_REASON_RA_WITHDRAWN is an IPv6-specific cause placed in the family-agnostic route attribute value space, and RTA_DEL_REASON_MAX has no in-tree user by the end of the series (rtm_ipv6_policy has no RTA_DEL_REASON entry). Would it be worth stating in the comment that the value space must never be reinterpreted per family, so a later family cannot reuse the numbers for its own causes? " And be aware of: https://lore.kernel.org/all/83360de7addb13a3b5f4d5e722148f248fdb2ae0.1784884817.git.pab...@redhat.com/ > + > /* RTM_MULTIPATH --- array of struct rtnexthop. > * > * "struct rtnexthop" describes all necessary nexthop information, > diff --git a/net/ipv6/route.c b/net/ipv6/route.c > index a1301334da48..9f82829923ab 100644 > --- a/net/ipv6/route.c > +++ b/net/ipv6/route.c > @@ -3994,7 +3994,8 @@ 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, > + enum rta_del_reason del_reason) It's weird to have 'skip_notify' in a function that is supposed to notify the deletion reason to user space. All the callers except ip6_del_rt() pass 'false'. Can ip6_del_rt() call __ip6_del_rt() with 'RTA_DEL_REASON_UNSPEC' ? > { > struct nl_info info = { > .nl_net = net, > @@ -4004,6 +4005,11 @@ int ip6_del_rt(struct net *net, struct fib6_info *rt, > bool skip_notify) > return __ip6_del_rt(rt, &info); > } > > +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) > { > struct nl_info *info = &cfg->fc_nlinfo; > -- > 2.43.0 >

