Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 28 Jul 2026 20:57:40 +0000
From:      Pouria Mousavizadeh Tehrani <pouria@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Subject:   git: be5994cdfbe2 - main - netlink: Add RTA_PREFSRC support
Message-ID:  <6a6917c4.1e429.2c2ba4ad@gitrepo.freebsd.org>

index | next in thread | raw e-mail

The branch main has been updated by pouria:

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

commit be5994cdfbe2fab4c038a534fbff27e49bbb7a7b
Author:     Pouria Mousavizadeh Tehrani <pouria@FreeBSD.org>
AuthorDate: 2026-07-17 12:02:13 +0000
Commit:     Pouria Mousavizadeh Tehrani <pouria@FreeBSD.org>
CommitDate: 2026-07-28 20:54:24 +0000

    netlink: Add RTA_PREFSRC support
    
    Add the ability to select source ip address of outgoing packets
    even when the source ip address is configured on another interface.
    Also add this new rtnetlink attribute to manual.
    
    PR:             285422
    Reviewed by:    glebius, ziaee (manpages)
    Tested by:      ivy, Marek Zarychta <zarychtam@plan-b.pwste.edu.pl>
    Relnotes:       yes
    Differential Revision: https://reviews.freebsd.org/D58294
---
 share/man/man4/rtnetlink.4              |  5 +++-
 sys/net/route.h                         |  1 +
 sys/net/route/nhop_ctl.c                |  7 ++---
 sys/netlink/netlink_snl_route_parsers.h |  2 ++
 sys/netlink/route/nexthop.c             | 22 +++++++++++++++
 sys/netlink/route/route.h               |  5 ++--
 sys/netlink/route/route_var.h           |  1 +
 sys/netlink/route/rt.c                  | 47 +++++++++++++++++++++++++++++++++
 8 files changed, 84 insertions(+), 6 deletions(-)

diff --git a/share/man/man4/rtnetlink.4 b/share/man/man4/rtnetlink.4
index d756cd031145..3d76c66c1917 100644
--- a/share/man/man4/rtnetlink.4
+++ b/share/man/man4/rtnetlink.4
@@ -22,7 +22,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd June 15, 2026
+.Dd July 28, 2026
 .Dt RTNETLINK 4
 .Os
 .Sh NAME
@@ -126,6 +126,9 @@ RTA_TABLE	fib number or RT_TABLE_UNSPEC to return all fibs
 .It Dv RTA_GATEWAY
 (binary) IPv4/IPv6 gateway address, depending on the
 .Va rtm_family .
+.It Dv RTA_PREFSRC
+(binary) IPv4/IPv6 preferred address, depending on the
+.Va rtm_family .
 .It Dv RTA_METRICS
 (nested) Container attribute, listing route properties.
 The only supported sub-attribute is
diff --git a/sys/net/route.h b/sys/net/route.h
index b4440de19d96..c2052dc9cde8 100644
--- a/sys/net/route.h
+++ b/sys/net/route.h
@@ -206,6 +206,7 @@ EVENTHANDLER_DECLARE(rtnumfibs_change, rtnumfibs_change_t);
 #define	NHF_GATEWAY		0x0200	/* RTF_GATEWAY */
 #define	NHF_HOST		0x0400	/* RTF_HOST */
 #define	NHF_INVALID		0x0800	/* Nexthop is unreachable */
+#define	NHF_PREFSRC		0x1000	/* Nexthop IFA is static */
 
 /* Nexthop request flags */
 #define	NHR_NONE		0x00	/* empty flags field */
diff --git a/sys/net/route/nhop_ctl.c b/sys/net/route/nhop_ctl.c
index 628a1da41862..f34721c888b4 100644
--- a/sys/net/route/nhop_ctl.c
+++ b/sys/net/route/nhop_ctl.c
@@ -120,8 +120,8 @@ nhops_init(void)
 
 /*
  * Fetches the interface of source address used by the route.
- * In all cases except interface-address-route it would be the
- * same as the transmit interfaces.
+ * In all cases except interface-address-route and prefsrc it
+ * would be the same as the transmit interfaces.
  * However, for the interface address this function will return
  * this interface ifp instead of loopback. This is needed to support
  * link-local IPv6 loopback communications.
@@ -147,7 +147,8 @@ get_aifp(const struct nhop_object *nh)
 			FIB_NH_LOG(LOG_WARNING, nh, "unable to get aifp for %s index %d",
 				if_name(nh->nh_ifp), nh->gwl_sa.sdl_index);
 		}
-	}
+	} else if ((nh->nh_flags & NHF_PREFSRC) != 0)
+		aifp = nh->nh_ifa->ifa_ifp;
 
 	if (aifp == NULL)
 		aifp = nh->nh_ifp;
diff --git a/sys/netlink/netlink_snl_route_parsers.h b/sys/netlink/netlink_snl_route_parsers.h
index 438ed820262b..f0849884ec97 100644
--- a/sys/netlink/netlink_snl_route_parsers.h
+++ b/sys/netlink/netlink_snl_route_parsers.h
@@ -113,6 +113,7 @@ nlattr_get_multipath(struct snl_state *ss, struct nlattr *nla,
 struct snl_parsed_route {
 	struct sockaddr		*rta_dst;
 	struct sockaddr		*rta_gw;
+	struct sockaddr		*rta_pref_src;
 	struct nlattr		*rta_metrics;
 	struct rta_mpath	rta_multipath;
 	uint32_t		rta_oif;
@@ -142,6 +143,7 @@ static const struct snl_attr_parser _nla_p_route[] = {
 	{ .type = NL_RTA_OIF, .off = _OUT(rta_oif), .cb = snl_attr_get_uint32 },
 	{ .type = NL_RTA_GATEWAY, .off = _OUT(rta_gw), .cb = snl_attr_get_ip },
 	{ .type = NL_RTA_PRIORITY, .off = _OUT(rta_metric), .cb = snl_attr_get_uint32 },
+	{ .type = NL_RTA_PREFSRC, .off = _OUT(rta_pref_src), .cb = snl_attr_get_ip },
 	{ .type = NL_RTA_METRICS, .arg = &_metrics_parser, .cb = snl_attr_get_nested },
 	{ .type = NL_RTA_MULTIPATH, .off = _OUT(rta_multipath), .cb = nlattr_get_multipath },
 	{ .type = NL_RTA_KNH_ID, .off = _OUT(rta_knh_id), .cb = snl_attr_get_uint32 },
diff --git a/sys/netlink/route/nexthop.c b/sys/netlink/route/nexthop.c
index 0b4a929f65a6..e5f776e9d052 100644
--- a/sys/netlink/route/nexthop.c
+++ b/sys/netlink/route/nexthop.c
@@ -811,6 +811,28 @@ nl_set_nexthop_gw(struct nhop_object *nh, struct sockaddr *gw, if_t ifp,
 	return (0);
 }
 
+/*
+ * Sets nexthop @nh prefsrc specified by @src.
+ * Returns 0 on success or errno.
+ */
+int
+nl_set_nexthop_prefsrc(struct nhop_object *nh, struct sockaddr *src)
+{
+	struct ifaddr *ifa = NULL;
+	int fibnum = nhop_get_fibnum(nh);
+
+	MPASS(src != NULL);
+
+	ifa = ifa_ifwithaddr_fib(src, fibnum);
+	if (ifa == NULL)
+		return (EINVAL);
+
+	nhop_set_src(nh, ifa);
+	nh->nh_flags |= NHF_PREFSRC;
+
+	return (0);
+}
+
 static int
 newnhop(struct nl_parsed_nhop *attrs, struct user_nhop *unhop, struct nl_pstate *npt)
 {
diff --git a/sys/netlink/route/route.h b/sys/netlink/route/route.h
index 592b978b4745..3c037a47e3a9 100644
--- a/sys/netlink/route/route.h
+++ b/sys/netlink/route/route.h
@@ -145,12 +145,12 @@ enum rt_scope_t {
 enum rtattr_type_t {
 	NL_RTA_UNSPEC,
 	NL_RTA_DST		= 1, /* binary, IPv4/IPv6 destination */
-	NL_RTA_SRC		= 2, /* binary, preferred source address */
+	NL_RTA_SRC		= 2, /* not supported */
 	NL_RTA_IIF		= 3, /* not supported */
 	NL_RTA_OIF		= 4, /* u32, transmit ifindex */
 	NL_RTA_GATEWAY		= 5, /* binary: IPv4/IPv6 gateway */
 	NL_RTA_PRIORITY		= 6, /* u32, path metric */
-	NL_RTA_PREFSRC		= 7, /* not supported */
+	NL_RTA_PREFSRC		= 7, /* binary, preferred source address */
 	NL_RTA_METRICS		= 8, /* nested, list of NL_RTAX* attrs */
 	NL_RTA_MULTIPATH	= 9, /* binary, array of struct rtnexthop */
 	NL_RTA_PROTOINFO	= 10, /* not supported / deprecated */
@@ -200,6 +200,7 @@ enum rtattr_type_t {
 #define RTA_GATEWAY		NL_RTA_GATEWAY
 #define RTA_PRIORITY		NL_RTA_PRIORITY
 #define RTA_PREFSRC		NL_RTA_PREFSRC
+#define RTA_IFA			NL_RTA_PREFSRC
 #define RTA_METRICS		NL_RTA_METRICS
 #define RTA_MULTIPATH		NL_RTA_MULTIPATH
 #define	RTA_PROTOINFO		NL_RTA_PROTOINFO
diff --git a/sys/netlink/route/route_var.h b/sys/netlink/route/route_var.h
index 41f110038b54..012dc5ad0594 100644
--- a/sys/netlink/route/route_var.h
+++ b/sys/netlink/route/route_var.h
@@ -136,6 +136,7 @@ struct nhop_object *nl_find_nhop(uint32_t fibnum, int family,
     uint32_t uidx, int nh_flags, int *perror);
 int nl_set_nexthop_gw(struct nhop_object *nh, struct sockaddr *gw,
     struct ifnet *ifp, struct nl_pstate *npt);
+int nl_set_nexthop_prefsrc(struct nhop_object *nh, struct sockaddr *src);
 
 
 #endif
diff --git a/sys/netlink/route/rt.c b/sys/netlink/route/rt.c
index 39ecb537d365..fbd96bb9fdc6 100644
--- a/sys/netlink/route/rt.c
+++ b/sys/netlink/route/rt.c
@@ -158,6 +158,34 @@ dump_rc_nhop_gw(struct nl_writer *nw, const struct nhop_object *nh)
 	}
 }
 
+static void
+dump_rc_nhop_prefsrc(struct nl_writer *nw, const struct nhop_object *nh)
+{
+	const struct ifaddr *ifa = nh->nh_ifa;
+#ifdef INET6
+	struct in6_addr in6;
+#endif
+
+	if ((nh->nh_flags & NHF_PREFSRC) == 0)
+		return;
+
+	switch (ifa->ifa_addr->sa_family) {
+#ifdef INET
+	case AF_INET:
+		nlattr_add(nw, NL_RTA_PREFSRC, 4,
+		    &((struct sockaddr_in *)((ifa)->ifa_addr))->sin_addr);
+		break;
+#endif
+#ifdef INET6
+	case AF_INET6:
+		in6 = ((struct sockaddr_in6 *)((ifa)->ifa_addr))->sin6_addr;
+		in6_clearscope(&in6);
+		nlattr_add(nw, NL_RTA_PREFSRC, 16, &in6);
+		break;
+#endif
+	}
+}
+
 static void
 dump_rc_nhop_mtu(struct nl_writer *nw, const struct nhop_object *nh)
 {
@@ -193,6 +221,7 @@ dump_rc_nhg(struct nl_writer *nw, const struct route_nhop_data *rnd, struct rtms
 	if (nh->nh_flags & NHF_GATEWAY)
 		dump_rc_nhop_gw(nw, nh);
 
+	dump_rc_nhop_prefsrc(nw, nh);
 	wn = nhgrp_get_nhops(nhg, &num_nhops);
 	base_rtflags = nhop_get_rtflags(wn[0].nh);
 	uidx = nhgrp_get_uidx(nhg);
@@ -269,6 +298,7 @@ dump_rc_nhop(struct nl_writer *nw, const struct route_nhop_data *rnd, struct rtm
 	if (nh->nh_flags & NHF_GATEWAY)
 		dump_rc_nhop_gw(nw, nh);
 
+	dump_rc_nhop_prefsrc(nw, nh);
 	uidx = nhop_get_uidx(nh);
 	if (uidx != 0)
 		nlattr_add_u32(nw, NL_RTA_NH_ID, uidx);
@@ -518,6 +548,7 @@ nlattr_get_multipath(struct nlattr *nla, struct nl_pstate *npt,
 struct nl_parsed_route {
 	struct sockaddr		*rta_dst;
 	struct sockaddr		*rta_gw;
+	struct sockaddr		*rta_pref_src;
 	struct ifnet		*rta_oif;
 	struct rta_mpath	*rta_multipath;
 	uint32_t		rta_table;
@@ -547,6 +578,7 @@ static const struct nlattr_parser nla_p_rtmsg[] = {
 	{ .type = NL_RTA_OIF, .off = _OUT(rta_oif), .cb = nlattr_get_ifp },
 	{ .type = NL_RTA_GATEWAY, .off = _OUT(rta_gw), .cb = nlattr_get_ip },
 	{ .type = NL_RTA_PRIORITY, .off = _OUT(rta_metric), .cb = nlattr_get_uint32 },
+	{ .type = NL_RTA_PREFSRC, .off = _OUT(rta_pref_src), .cb = nlattr_get_ip },
 	{ .type = NL_RTA_METRICS, .arg = &metrics_parser, .cb = nlattr_get_nested },
 	{ .type = NL_RTA_MULTIPATH, .off = _OUT(rta_multipath), .cb = nlattr_get_multipath },
 	{ .type = NL_RTA_WEIGHT, .off = _OUT(rta_weight), .cb = nlattr_get_uint32 },
@@ -575,6 +607,7 @@ post_p_rtmsg(void *_attrs, struct nl_pstate *npt __unused)
 
 	set_scope6(attrs->rta_dst, attrs->rta_oif);
 	set_scope6(attrs->rta_gw, attrs->rta_oif);
+	set_scope6(attrs->rta_pref_src, attrs->rta_oif);
 	return (true);
 }
 NL_DECLARE_PARSER_EXT(rtm_parser, struct rtmsg, NULL, nlf_p_rtmsg, nla_p_rtmsg, post_p_rtmsg);
@@ -873,6 +906,13 @@ create_nexthop_one(struct nl_parsed_route *attrs, struct rta_mpath_nh *mpnh,
 	}
 	if (mpnh->ifp != NULL)
 		nhop_set_transmit_ifp(nh, mpnh->ifp);
+	if (attrs->rta_pref_src != NULL) {
+		error = nl_set_nexthop_prefsrc(nh, attrs->rta_pref_src);
+		if (error != 0) {
+			nhop_free(nh);
+			return (error);
+		}
+	}
 	nhop_set_pxtype_flag(nh, get_pxflag(attrs));
 	nhop_set_rtflags(nh, attrs->rta_rtflags);
 	nhop_set_metric(nh, attrs->rta_metric);
@@ -945,6 +985,13 @@ create_nexthop_from_attrs(struct nl_parsed_route *attrs,
 		}
 		if (attrs->rta_oif != NULL)
 			nhop_set_transmit_ifp(nh, attrs->rta_oif);
+		if (attrs->rta_pref_src != NULL) {
+			*perror = nl_set_nexthop_prefsrc(nh, attrs->rta_pref_src);
+			if (*perror != 0) {
+				nhop_free(nh);
+				return (NULL);
+			}
+		}
 		if (attrs->rtax_mtu != 0)
 			nhop_set_mtu(nh, attrs->rtax_mtu, true);
 		if (attrs->rta_expire > 0) {


home | help

Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?6a6917c4.1e429.2c2ba4ad>