git: 7b3440fc3007 - main - Revert "routing: install prefix and loopback routes using new nhop-based KPI."

From: Alexander V. Chernikov <melifaro_at_FreeBSD.org>
Date: Mon, 29 Aug 2022 16:22:20 UTC
The branch main has been updated by melifaro:

URL: https://cgit.FreeBSD.org/src/commit/?id=7b3440fc3007ca6ece4fb3c77b423f941e11b2a0

commit 7b3440fc3007ca6ece4fb3c77b423f941e11b2a0
Author:     Alexander V. Chernikov <melifaro@FreeBSD.org>
AuthorDate: 2022-08-29 16:12:51 +0000
Commit:     Alexander V. Chernikov <melifaro@FreeBSD.org>
CommitDate: 2022-08-29 16:20:42 +0000

    Revert "routing: install prefix and loopback routes using new nhop-based KPI."
    
    Temporarily revert the commit to unblock testing.
    
    This reverts commit a1b59379db7d879551118b921f6e9692b4bf200c.
---
 sys/net/route/route_ctl.h     |   6 +-
 sys/net/route/route_ifaddrs.c | 190 +++++++++++++++++++-----------------------
 sys/netinet/in.c              |  62 ++++++++------
 sys/netinet6/in6.c            |  54 +++++-------
 sys/netinet6/nd6_rtr.c        |  70 +++++++---------
 5 files changed, 174 insertions(+), 208 deletions(-)

diff --git a/sys/net/route/route_ctl.h b/sys/net/route/route_ctl.h
index e65e6be4efda..e73a6dcf8a93 100644
--- a/sys/net/route/route_ctl.h
+++ b/sys/net/route/route_ctl.h
@@ -75,13 +75,9 @@ int rib_change_route(uint32_t fibnum, struct rt_addrinfo *info,
   struct rib_cmd_info *rc);
 int rib_action(uint32_t fibnum, int action, struct rt_addrinfo *info,
   struct rib_cmd_info *rc);
-int rib_add_kernel_px(uint32_t fibnum, struct sockaddr *dst, int plen,
-    struct route_nhop_data *rnd, int op_flags);
-int rib_del_kernel_px(uint32_t fibnum, struct sockaddr *dst, int plen,
-    rib_filter_f_t *filter_func, void *filter_arg, int op_flags);
-
 int rib_match_gw(const struct rtentry *rt, const struct nhop_object *nh,
     void *_data);
+int rib_handle_ifaddr_info(uint32_t fibnum, int cmd, struct rt_addrinfo *info);
 
 int rib_add_default_route(uint32_t fibnum, int family, struct ifnet *ifp,
     struct sockaddr *gw, struct rib_cmd_info *rc);
diff --git a/sys/net/route/route_ifaddrs.c b/sys/net/route/route_ifaddrs.c
index d88284c2e008..a456ffa28696 100644
--- a/sys/net/route/route_ifaddrs.c
+++ b/sys/net/route/route_ifaddrs.c
@@ -64,103 +64,112 @@ VNET_DEFINE(u_int, rt_add_addr_allfibs) = 0;
 SYSCTL_UINT(_net, OID_AUTO, add_addr_allfibs, CTLFLAG_RWTUN | CTLFLAG_VNET,
     &VNET_NAME(rt_add_addr_allfibs), 0, "");
 
-static void
-report_operation(uint32_t fibnum, struct rib_cmd_info *rc)
+/*
+ * Executes routing tables change specified by @cmd and @info for the fib
+ * @fibnum. Generates routing message on success.
+ * Note: it assumes there is only single route (interface route) for the
+ * provided prefix.
+ * Returns 0 on success or errno.
+ */
+static int
+rib_handle_ifaddr_one(uint32_t fibnum, int cmd, struct rt_addrinfo *info)
 {
+	struct rib_cmd_info rc;
 	struct nhop_object *nh;
+	int error;
 
-	if (rc->rc_cmd == RTM_DELETE)
-		nh = nhop_select(rc->rc_nh_old, 0);
-	else
-		nh = nhop_select(rc->rc_nh_new, 0);
-	rt_routemsg(rc->rc_cmd, rc->rc_rt, nh, fibnum);
-}
-
-int
-rib_add_kernel_px(uint32_t fibnum, struct sockaddr *dst, int plen,
-    struct route_nhop_data *rnd, int op_flags)
-{
-	struct rib_cmd_info rc = {};
-
-	NET_EPOCH_ASSERT();
-
-	int error = rib_add_route_px(fibnum, dst, plen, rnd, op_flags, &rc);
-	if (error != 0)
-		return (error);
-	report_operation(fibnum, &rc);
-
-	if (V_rt_add_addr_allfibs != 0) {
-		for (int i = 0; i < V_rt_numfibs; i++) {
-			if (i == fibnum)
-				continue;
-			struct rib_head *rnh = rt_tables_get_rnh(fibnum, dst->sa_family);
-			/* Don't care much about the errors in non-primary fib */
-			if (rnh != NULL) {
-				if (rib_copy_route(rc.rc_rt, rnd, rnh, &rc) == 0)
-					report_operation(i, &rc);
-			}
-		}
+	error = rib_action(fibnum, cmd, info, &rc);
+	if (error == 0) {
+		if (cmd == RTM_ADD)
+			nh = nhop_select(rc.rc_nh_new, 0);
+		else
+			nh = nhop_select(rc.rc_nh_old, 0);
+		rt_routemsg(cmd, rc.rc_rt, nh, fibnum);
 	}
 
 	return (error);
 }
 
+/*
+ * Adds/deletes interface prefix specified by @info to the routing table.
+ * If V_rt_add_addr_allfibs is set, iterates over all existing routing
+ * tables, otherwise uses fib in @fibnum. Generates routing message for
+ *  each table.
+ * Returns 0 on success or errno.
+ */
 int
-rib_del_kernel_px(uint32_t fibnum, struct sockaddr *dst, int plen,
-    rib_filter_f_t *filter_func, void *filter_arg, int op_flags)
+rib_handle_ifaddr_info(uint32_t fibnum, int cmd, struct rt_addrinfo *info)
 {
-	struct rib_cmd_info rc = {};
-
-	NET_EPOCH_ASSERT();
-
-	int error = rib_del_route_px(fibnum, dst, plen, filter_func, filter_arg,
-	    op_flags, &rc);
-	if (error != 0)
-		return (error);
-	report_operation(fibnum, &rc);
-
-	if (V_rt_add_addr_allfibs != 0) {
-		for (int i = 0; i < V_rt_numfibs; i++) {
-			if (i == fibnum)
-				continue;
-			/* Don't care much about the errors in non-primary fib */
-			if (rib_del_route_px(fibnum, dst, plen, filter_func, filter_arg,
-			    op_flags, &rc) == 0)
-				report_operation(i, &rc);
+	int error = 0, last_error = 0;
+	bool didwork = false;
+
+	if (V_rt_add_addr_allfibs == 0) {
+		error = rib_handle_ifaddr_one(fibnum, cmd, info);
+		didwork = (error == 0);
+	} else {
+		for (fibnum = 0; fibnum < V_rt_numfibs; fibnum++) {
+			error = rib_handle_ifaddr_one(fibnum, cmd, info);
+			if (error == 0)
+				didwork = true;
+			else
+				last_error = error;
 		}
 	}
 
+	if (cmd == RTM_DELETE) {
+		if (didwork) {
+			error = 0;
+		} else {
+			/* we only give an error if it wasn't in any table */
+			error = ((info->rti_flags & RTF_HOST) ?
+			    EHOSTUNREACH : ENETUNREACH);
+		}
+	} else {
+		if (last_error != 0) {
+			/* return an error if any of them failed */
+			error = last_error;
+		}
+	}
 	return (error);
 }
 
 static int
-add_loopback_route_flags(struct ifaddr *ifa, struct sockaddr *ia, int op_flags)
+ifa_maintain_loopback_route(int cmd, const char *otype, struct ifaddr *ifa,
+    struct sockaddr *ia)
 {
 	struct rib_cmd_info rc;
+	struct epoch_tracker et;
 	int error;
+	struct rt_addrinfo info;
+	struct sockaddr_dl null_sdl;
+	struct ifnet *ifp;
+
+	ifp = ifa->ifa_ifp;
 
-	NET_EPOCH_ASSERT();
+	NET_EPOCH_ENTER(et);
+	bzero(&info, sizeof(info));
+	if (cmd != RTM_DELETE)
+		info.rti_ifp = V_loif;
+	if (cmd == RTM_ADD) {
+		/* explicitly specify (loopback) ifa */
+		if (info.rti_ifp != NULL)
+			info.rti_ifa = ifaof_ifpforaddr(ifa->ifa_addr, info.rti_ifp);
+	}
+	info.rti_flags = ifa->ifa_flags | RTF_HOST | RTF_STATIC | RTF_PINNED;
+	info.rti_info[RTAX_DST] = ia;
+	info.rti_info[RTAX_GATEWAY] = (struct sockaddr *)&null_sdl;
+	link_init_sdl(ifp, (struct sockaddr *)&null_sdl, ifp->if_type);
 
-	struct ifnet *ifp = ifa->ifa_ifp;
-	struct nhop_object *nh = nhop_alloc(ifp->if_fib, ia->sa_family);
-	struct route_nhop_data rnd = { .rnd_weight = RT_DEFAULT_WEIGHT };
-	if (nh == NULL)
-		return (ENOMEM);
+	error = rib_action(ifp->if_fib, cmd, &info, &rc);
+	NET_EPOCH_EXIT(et);
 
-	nhop_set_direct_gw(nh, ifp);
-	nhop_set_transmit_ifp(nh, V_loif);
-	nhop_set_src(nh, ifaof_ifpforaddr(ifa->ifa_addr, ifp));
-	nhop_set_pinned(nh, true);
-	nhop_set_rtflags(nh, RTF_STATIC);
-	nhop_set_pxtype_flag(nh, NHF_HOST);
-	rnd.rnd_nhop = nhop_get_nhop(nh, &error);
-	if (error != 0)
+	if (error == 0 ||
+	    (cmd == RTM_ADD && error == EEXIST) ||
+	    (cmd == RTM_DELETE && (error == ENOENT || error == ESRCH)))
 		return (error);
-	error = rib_add_route_px(ifp->if_fib, ia, -1, &rnd, op_flags, &rc);
 
-	if (error != 0)
-		log(LOG_DEBUG, "%s: failed to update interface %s route: %u\n",
-		    __func__, if_name(ifp), error);
+	log(LOG_DEBUG, "%s: %s failed for interface %s: %u\n",
+		__func__, otype, if_name(ifp), error);
 
 	return (error);
 }
@@ -168,49 +177,22 @@ add_loopback_route_flags(struct ifaddr *ifa, struct sockaddr *ia, int op_flags)
 int
 ifa_add_loopback_route(struct ifaddr *ifa, struct sockaddr *ia)
 {
-	struct epoch_tracker et;
 
-	NET_EPOCH_ENTER(et);
-	int error = add_loopback_route_flags(ifa, ia, RTM_F_CREATE | RTM_F_FORCE);
-	NET_EPOCH_EXIT(et);
-
-	return (error);
+	return (ifa_maintain_loopback_route(RTM_ADD, "insertion", ifa, ia));
 }
 
 int
-ifa_switch_loopback_route(struct ifaddr *ifa, struct sockaddr *ia)
+ifa_del_loopback_route(struct ifaddr *ifa, struct sockaddr *ia)
 {
-	struct epoch_tracker et;
-
-	NET_EPOCH_ENTER(et);
-	int error = add_loopback_route_flags(ifa, ia, RTM_F_REPLACE | RTM_F_FORCE);
-	NET_EPOCH_EXIT(et);
 
-	return (error);
+	return (ifa_maintain_loopback_route(RTM_DELETE, "deletion", ifa, ia));
 }
 
 int
-ifa_del_loopback_route(struct ifaddr *ifa, struct sockaddr *ia)
+ifa_switch_loopback_route(struct ifaddr *ifa, struct sockaddr *ia)
 {
-	struct ifnet *ifp = ifa->ifa_ifp;
-	struct sockaddr_dl link_sdl;
-	struct sockaddr *gw = (struct sockaddr *)&link_sdl;
-	struct rib_cmd_info rc;
-	struct epoch_tracker et;
-	int error;
 
-	NET_EPOCH_ENTER(et);
-
-	link_init_sdl(ifp, gw, ifp->if_type);
-	error = rib_del_route_px_gw(ifp->if_fib, ia, -1, gw, RTM_F_FORCE, &rc);
-
-	NET_EPOCH_EXIT(et);
-
-	if (error != 0)
-		log(LOG_DEBUG, "%s: failed to delete interface %s route: %u\n",
-		    __func__,  if_name(ifp), error);
-
-	return (error);
+	return (ifa_maintain_loopback_route(RTM_CHANGE, "switch", ifa, ia));
 }
 
 static bool
diff --git a/sys/netinet/in.c b/sys/netinet/in.c
index e097698c27dd..790740456160 100644
--- a/sys/netinet/in.c
+++ b/sys/netinet/in.c
@@ -821,34 +821,36 @@ in_match_ifaddr(const struct rtentry *rt, const struct nhop_object *nh, void *ar
 }
 
 static int
-in_handle_prefix_route(uint32_t fibnum, int cmd, struct sockaddr *dst,
-    int plen, struct ifaddr *ifa, struct ifnet *ifp)
+in_handle_prefix_route(uint32_t fibnum, int cmd,
+    struct sockaddr_in *dst, struct sockaddr_in *netmask, struct ifaddr *ifa,
+    struct ifnet *ifp)
 {
-	int error = 0;
 
 	NET_EPOCH_ASSERT();
 
-	if (cmd == RTM_DELETE) {
-		error = rib_del_kernel_px(fibnum, dst, plen, in_match_ifaddr, ifa,
-		    RTM_F_FORCE);
-	} else {
-		struct nhop_object *nh = nhop_alloc(fibnum, dst->sa_family);
-		struct route_nhop_data rnd = { .rnd_weight = RT_DEFAULT_WEIGHT };
-
-		if (nh == NULL)
-			return (ENOMEM);
-		nhop_set_direct_gw(nh, ifa->ifa_ifp);
-		nhop_set_transmit_ifp(nh, ifp);
-		nhop_set_src(nh, ifa);
-		nhop_set_pinned(nh, true);
-		nhop_set_pxtype_flag(nh, plen == 32 ? NHF_HOST : 0);
-		rnd.rnd_nhop = nhop_get_nhop(nh, &error);
-		if (error != 0)
-			return (error);
-		int op_flags = RTM_F_CREATE | RTM_F_REPLACE | RTM_F_FORCE;
-		error = rib_add_kernel_px(fibnum, dst, plen, &rnd, op_flags);
-	}
-	return (error);
+	/* Prepare gateway */
+	struct sockaddr_dl_short sdl = {
+		.sdl_family = AF_LINK,
+		.sdl_len = sizeof(struct sockaddr_dl_short),
+		.sdl_type = ifa->ifa_ifp->if_type,
+		.sdl_index = ifa->ifa_ifp->if_index,
+	};
+
+	struct rt_addrinfo info = {
+		.rti_ifa = ifa,
+		.rti_ifp = ifp,
+		.rti_flags = RTF_PINNED | ((netmask != NULL) ? 0 : RTF_HOST),
+		.rti_info = {
+			[RTAX_DST] = (struct sockaddr *)dst,
+			[RTAX_NETMASK] = (struct sockaddr *)netmask,
+			[RTAX_GATEWAY] = (struct sockaddr *)&sdl,
+		},
+		/* Ensure we delete the prefix IFF prefix ifa matches */
+		.rti_filter = in_match_ifaddr,
+		.rti_filterdata = ifa,
+	};
+
+	return (rib_handle_ifaddr_info(fibnum, cmd, &info));
 }
 
 /*
@@ -957,12 +959,19 @@ in_handle_ifaddr_route(int cmd, struct in_ifaddr *ia)
 {
 	struct ifaddr *ifa = &ia->ia_ifa;
 	struct in_addr daddr, maddr;
+	struct sockaddr_in *pmask;
 	struct epoch_tracker et;
 	int error;
 
 	ia_getrtprefix(ia, &daddr, &maddr);
 
-	int plen = bitcount32(maddr.s_addr);
+	struct sockaddr_in mask = {
+		.sin_family = AF_INET,
+		.sin_len = sizeof(struct sockaddr_in),
+		.sin_addr = maddr,
+	};
+
+	pmask = (maddr.s_addr != INADDR_BROADCAST) ? &mask : NULL;
 
 	struct sockaddr_in dst = {
 		.sin_family = AF_INET,
@@ -980,8 +989,7 @@ in_handle_ifaddr_route(int cmd, struct in_ifaddr *ia)
 
 	uint32_t fibnum = ifa->ifa_ifp->if_fib;
 	NET_EPOCH_ENTER(et);
-	error = in_handle_prefix_route(fibnum, cmd, (struct sockaddr *)&dst,
-	    plen, ifa, ifp);
+	error = in_handle_prefix_route(fibnum, cmd, &dst, pmask, ifa, ifp);
 	NET_EPOCH_EXIT(et);
 
 	return (error);
diff --git a/sys/netinet6/in6.c b/sys/netinet6/in6.c
index 4b3f30a7ee7c..ccdf71cfc01c 100644
--- a/sys/netinet6/in6.c
+++ b/sys/netinet6/in6.c
@@ -1267,29 +1267,6 @@ in6_broadcast_ifa(struct ifnet *ifp, struct in6_aliasreq *ifra,
 	return (error);
 }
 
-static int
-in6_handle_dstaddr_rtadd(uint32_t fibnum, struct sockaddr *dst, struct ifaddr *ifa)
-{
-	struct nhop_object *nh = nhop_alloc(fibnum, dst->sa_family);
-	struct route_nhop_data rnd = { .rnd_weight = RT_DEFAULT_WEIGHT };
-	int error = 0;
-
-	if (nh == NULL)
-		return (ENOMEM);
-	nhop_set_direct_gw(nh, ifa->ifa_ifp);
-	nhop_set_transmit_ifp(nh, ifa->ifa_ifp);
-	nhop_set_src(nh, ifa);
-	nhop_set_pinned(nh, true);
-	nhop_set_pxtype_flag(nh, NHF_HOST);
-	rnd.rnd_nhop = nhop_get_nhop(nh, &error);
-	if (error == 0) {
-		int op_flags = RTM_F_CREATE | RTM_F_REPLACE | RTM_F_FORCE;
-		error = rib_add_kernel_px(fibnum, dst, -1, &rnd, op_flags);
-	}
-
-	return (error);
-}
-
 /*
  * Adds or deletes interface route for p2p ifa.
  * Returns 0 on success or errno.
@@ -1299,26 +1276,35 @@ in6_handle_dstaddr_rtrequest(int cmd, struct in6_ifaddr *ia)
 {
 	struct epoch_tracker et;
 	struct ifaddr *ifa = &ia->ia_ifa;
-	uint32_t fibnum = ifa->ifa_ifp->if_fib;
 	int error;
 
+	/* Prepare gateway */
+	struct sockaddr_dl_short sdl = {
+		.sdl_family = AF_LINK,
+		.sdl_len = sizeof(struct sockaddr_dl_short),
+		.sdl_type = ifa->ifa_ifp->if_type,
+		.sdl_index = ifa->ifa_ifp->if_index,
+	};
+
 	struct sockaddr_in6 dst = {
 		.sin6_family = AF_INET6,
 		.sin6_len = sizeof(struct sockaddr_in6),
 		.sin6_addr = ia->ia_dstaddr.sin6_addr,
 	};
 
+	struct rt_addrinfo info = {
+		.rti_ifa = ifa,
+		.rti_ifp = ifa->ifa_ifp,
+		.rti_flags = RTF_PINNED | RTF_HOST,
+		.rti_info = {
+			[RTAX_DST] = (struct sockaddr *)&dst,
+			[RTAX_GATEWAY] = (struct sockaddr *)&sdl,
+		},
+	};
+	/* Don't set additional per-gw filters on removal */
+
 	NET_EPOCH_ENTER(et);
-	if (cmd == RTM_DELETE) {
-		struct sockaddr_dl link_sdl;
-		struct sockaddr *gw = (struct sockaddr *)&link_sdl;
-		struct ifnet *ifp = ifa->ifa_ifp;
-
-		link_init_sdl(ifp, gw, ifp->if_type);
-		error = rib_del_kernel_px(fibnum, (struct sockaddr *)&dst, 128,
-		    rib_match_gw, gw, RTM_F_FORCE);
-	} else
-		error = in6_handle_dstaddr_rtadd(fibnum, (struct sockaddr *)&dst, ifa);
+	error = rib_handle_ifaddr_info(ifa->ifa_ifp->if_fib, cmd, &info);
 	NET_EPOCH_EXIT(et);
 
 	return (error);
diff --git a/sys/netinet6/nd6_rtr.c b/sys/netinet6/nd6_rtr.c
index a8431e23b815..793befc47aa5 100644
--- a/sys/netinet6/nd6_rtr.c
+++ b/sys/netinet6/nd6_rtr.c
@@ -1999,55 +1999,41 @@ restart:
 	ND6_ONLINK_UNLOCK();
 }
 
-static int
-nd6_prefix_rtadd(uint32_t fibnum, struct sockaddr *dst, int plen,
-    struct ifnet *ifp, struct ifaddr *ifa)
-{
-	struct route_nhop_data rnd = { .rnd_weight = RT_DEFAULT_WEIGHT };
-	int error = 0;
-
-	struct nhop_object *nh = nhop_alloc(fibnum, dst->sa_family);
-	if (nh == NULL)
-		return (ENOMEM);
-	nhop_set_direct_gw(nh, ifp);
-	nhop_set_transmit_ifp(nh, ifp);
-	nhop_set_src(nh, ifa);
-	nhop_set_pinned(nh, true);
-	nhop_set_pxtype_flag(nh, plen == 128 ? NHF_HOST : 0);
-	rnd.rnd_nhop = nhop_get_nhop(nh, &error);
-	if (error == 0) {
-		int op_flags = RTM_F_CREATE | RTM_F_REPLACE | RTM_F_FORCE;
-		error = rib_add_kernel_px(fibnum, dst, plen, &rnd, op_flags);
-	}
-
-	return (error);
-}
-
 /*
  * Add or remove interface route specified by @dst, @netmask and @ifp.
  * ifa can be NULL.
  * Returns 0 on success
  */
 static int
-nd6_prefix_rtrequest(uint32_t fibnum, int cmd, struct sockaddr *dst,
-    int plen, struct ifnet *ifp, struct ifaddr *ifa)
+nd6_prefix_rtrequest(uint32_t fibnum, int cmd, struct sockaddr_in6 *dst,
+    struct sockaddr_in6 *netmask, struct ifnet *ifp, struct ifaddr *ifa)
 {
 	struct epoch_tracker et;
 	int error;
 
-	NET_EPOCH_ENTER(et);
-	if (cmd == RTM_DELETE) {
-		struct sockaddr_dl link_sdl;
-		struct sockaddr *gw = (struct sockaddr *)&link_sdl;
+	/* Prepare gateway */
+	struct sockaddr_dl_short sdl = {
+		.sdl_family = AF_LINK,
+		.sdl_len = sizeof(struct sockaddr_dl_short),
+		.sdl_type = ifp->if_type,
+		.sdl_index = ifp->if_index,
+	};
 
-		link_init_sdl(ifp, gw, ifp->if_type);
-		error = rib_del_kernel_px(fibnum, dst, plen,
-		    rib_match_gw, gw, RTM_F_FORCE);
-	} else
-		error = nd6_prefix_rtadd(fibnum, dst, plen, ifp, ifa);
+	struct rt_addrinfo info = {
+		.rti_ifa = ifa,
+		.rti_ifp = ifp,
+		.rti_flags = RTF_PINNED | ((netmask != NULL) ? 0 : RTF_HOST),
+		.rti_info = {
+			[RTAX_DST] = (struct sockaddr *)dst,
+			[RTAX_NETMASK] = (struct sockaddr *)netmask,
+			[RTAX_GATEWAY] = (struct sockaddr *)&sdl,
+		},
+	};
+	/* Don't set additional per-gw filters on removal */
 
+	NET_EPOCH_ENTER(et);
+	error = rib_handle_ifaddr_info(fibnum, cmd, &info);
 	NET_EPOCH_EXIT(et);
-
 	return (error);
 }
 
@@ -2056,8 +2042,15 @@ nd6_prefix_onlink_rtrequest(struct nd_prefix *pr, struct ifaddr *ifa)
 {
 	int error;
 
+	struct sockaddr_in6 mask6 = {
+		.sin6_family = AF_INET6,
+		.sin6_len = sizeof(struct sockaddr_in6),
+		.sin6_addr = pr->ndpr_mask,
+	};
+	struct sockaddr_in6 *pmask6 = (pr->ndpr_plen != 128) ? &mask6 : NULL;
+
 	error = nd6_prefix_rtrequest(pr->ndpr_ifp->if_fib, RTM_ADD,
-	    (struct sockaddr *)&pr->ndpr_prefix, pr->ndpr_plen, pr->ndpr_ifp, ifa);
+	    &pr->ndpr_prefix, pmask6, pr->ndpr_ifp, ifa);
 	if (error == 0)
 		pr->ndpr_stateflags |= NDPRF_ONLINK;
 
@@ -2168,9 +2161,10 @@ nd6_prefix_offlink(struct nd_prefix *pr)
 		.sin6_len = sizeof(struct sockaddr_in6),
 		.sin6_addr = pr->ndpr_mask,
 	};
+	struct sockaddr_in6 *pmask6 = (pr->ndpr_plen != 128) ? &mask6 : NULL;
 
 	error = nd6_prefix_rtrequest(ifp->if_fib, RTM_DELETE,
-	    (struct sockaddr *)&pr->ndpr_prefix, pr->ndpr_plen, ifp, NULL);
+	    &pr->ndpr_prefix, pmask6, ifp, NULL);
 
 	a_failure = 1;
 	if (error == 0) {