git: 3200a274272d - main - sctp: Allow to ignore iface type when determining address scope

From: Pouria Mousavizadeh Tehrani <pouria_at_FreeBSD.org>
Date: Thu, 22 Oct 2026 15:14:39 UTC
The branch main has been updated by pouria:

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

commit 3200a274272d28cf6d2385a8ea46959f313866de
Author:     Pouria Mousavizadeh Tehrani <pouria@FreeBSD.org>
AuthorDate: 2026-10-21 15:53:51 +0000
Commit:     Pouria Mousavizadeh Tehrani <pouria@FreeBSD.org>
CommitDate: 2026-10-22 15:13:42 +0000

    sctp: Allow to ignore iface type when determining address scope
    
    Scope restriction by loopback interface was not specified in
    the draft-stewart-tsvwg-sctp-ipv4 draft and having global address
    on loopback interface is common.
    Therefore, allow to skip loopback interface type check in sctp.
    
    Reviewed by:    tuexen
    Differential Revision:  https://reviews.freebsd.org/D60938
---
 share/man/man4/sctp.4     |  4 +++-
 sys/netinet/sctp_asconf.c | 15 +++++++++++++++
 sys/netinet/sctp_os_bsd.h |  3 ++-
 sys/netinet/sctp_output.c | 10 ++++++++++
 sys/netinet/sctp_pcb.c    | 16 +++++++++++++++-
 sys/netinet/sctp_sysctl.c | 10 ++++++++++
 sys/netinet/sctp_sysctl.h |  6 ++++++
 sys/netinet/sctp_usrreq.c |  8 ++++++++
 sys/netinet/sctputil.c    |  8 ++++++++
 9 files changed, 77 insertions(+), 3 deletions(-)

diff --git a/share/man/man4/sctp.4 b/share/man/man4/sctp.4
index 287e16f3b599..89d58a0e207f 100644
--- a/share/man/man4/sctp.4
+++ b/share/man/man4/sctp.4
@@ -24,7 +24,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd June 21, 2023
+.Dd October 21, 2026
 .Dt SCTP 4
 .Os
 .Sh NAME
@@ -605,6 +605,8 @@ Enable SCTP Auto-ASCONF.
 Maximum incoming SCTP buffer size.
 .It Va sendspace
 Maximum outgoing SCTP buffer size.
+.It Va ignore_loopback_iftype
+Ignore interface type when determining loopback address scope.
 .El
 .El
 .Sh SEE ALSO
diff --git a/sys/netinet/sctp_asconf.c b/sys/netinet/sctp_asconf.c
index 6b98557c45a6..061e4ae0f271 100644
--- a/sys/netinet/sctp_asconf.c
+++ b/sys/netinet/sctp_asconf.c
@@ -2456,6 +2456,11 @@ sctp_find_valid_localaddr(struct sctp_tcb *stcb, int addr_locked)
 					    &sin->sin_addr) != 0) {
 						continue;
 					}
+
+					if (stcb->asoc.scope.loopback_scope == 0 &&
+					    IN4_ISLOOPBACK_ADDRESS(&sin->sin_addr))
+						continue;
+
 					if (stcb->asoc.scope.ipv4_local_scope == 0 &&
 					    IN4_ISPRIVATE_ADDRESS(&sin->sin_addr))
 						continue;
@@ -2494,6 +2499,9 @@ sctp_find_valid_localaddr(struct sctp_tcb *stcb, int addr_locked)
 					    &sin6->sin6_addr) != 0) {
 						continue;
 					}
+					if (stcb->asoc.scope.loopback_scope == 0 &&
+					    IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr))
+						continue;
 					if (stcb->asoc.scope.local_scope == 0 &&
 					    IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))
 						continue;
@@ -3095,6 +3103,9 @@ sctp_check_address_list_all(struct sctp_tcb *stcb, struct mbuf *m, int offset,
 				    &sin->sin_addr) != 0) {
 					continue;
 				}
+				if (loopback_scope == 0 &&
+				    (IN4_ISLOOPBACK_ADDRESS(&sin->sin_addr)))
+					continue;
 				if ((ipv4_scope == 0) &&
 				    (IN4_ISPRIVATE_ADDRESS(&sin->sin_addr))) {
 					/* private address not in scope */
@@ -3109,6 +3120,10 @@ sctp_check_address_list_all(struct sctp_tcb *stcb, struct mbuf *m, int offset,
 				    &sin6->sin6_addr) != 0) {
 					continue;
 				}
+				if (loopback_scope == 0 &&
+				    (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr))) {
+					continue;
+				}
 				if ((local_scope == 0) &&
 				    (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))) {
 					continue;
diff --git a/sys/netinet/sctp_os_bsd.h b/sys/netinet/sctp_os_bsd.h
index 9cec02aa6a07..01f003c1ef4b 100644
--- a/sys/netinet/sctp_os_bsd.h
+++ b/sys/netinet/sctp_os_bsd.h
@@ -198,7 +198,8 @@ MALLOC_DECLARE(SCTP_M_MCORE);
 #define SCTP_VRF_IFN_HASH_SIZE	3
 #define	SCTP_INIT_VRF_TABLEID(vrf)
 
-#define SCTP_IFN_IS_IFT_LOOP(ifn) ((ifn)->ifn_type == IFT_LOOP)
+#define SCTP_IFN_IS_IFT_LOOP(ifn)	\
+	((SCTP_BASE_SYSCTL(sctp_ignore_loopback_iftype) == 0) && ((ifn)->ifn_type == IFT_LOOP))
 #define SCTP_ROUTE_IS_REAL_LOOP(ro) ((ro)->ro_nh && (ro)->ro_nh->nh_ifa && (ro)->ro_nh->nh_ifa->ifa_ifp && (ro)->ro_nh->nh_ifa->ifa_ifp->if_type == IFT_LOOP)
 
 /*
diff --git a/sys/netinet/sctp_output.c b/sys/netinet/sctp_output.c
index 85513d32fedf..2021a31ca601 100644
--- a/sys/netinet/sctp_output.c
+++ b/sys/netinet/sctp_output.c
@@ -1883,6 +1883,11 @@ sctp_is_address_in_scope(struct sctp_ifa *ifa,
 				/* not in scope , unspecified */
 				return (0);
 			}
+			if (scope->loopback_scope == 0 &&
+			    ifa->src_is_loop == 1) {
+				/* not in scope , loopback */
+				return (0);
+			}
 			if ((scope->ipv4_local_scope == 0) &&
 			    (IN4_ISPRIVATE_ADDRESS(&sin->sin_addr))) {
 				/* private address not in scope */
@@ -1914,6 +1919,11 @@ sctp_is_address_in_scope(struct sctp_ifa *ifa,
 				/* skip unspecified addresses */
 				return (0);
 			}
+			if (scope->loopback_scope == 0 &&
+			    ifa->src_is_loop == 1) {
+				/* not in scope , loopback */
+				return (0);
+			}
 			if (	/* (local_scope == 0) && */
 			    (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))) {
 				return (0);
diff --git a/sys/netinet/sctp_pcb.c b/sys/netinet/sctp_pcb.c
index 2092f20e3c22..536f4aa80c60 100644
--- a/sys/netinet/sctp_pcb.c
+++ b/sys/netinet/sctp_pcb.c
@@ -768,6 +768,10 @@ sctp_does_stcb_own_this_addr(struct sctp_tcb *stcb, struct sockaddr *to)
 
 						sin = &sctp_ifa->address.sin;
 						rsin = (struct sockaddr_in *)to;
+						if (loopback_scope == 0 &&
+						    IN4_ISLOOPBACK_ADDRESS(&sin->sin_addr)) {
+							continue;
+						}
 						if ((ipv4_local_scope == 0) &&
 						    IN4_ISPRIVATE_ADDRESS(&sin->sin_addr)) {
 							continue;
@@ -795,6 +799,10 @@ sctp_does_stcb_own_this_addr(struct sctp_tcb *stcb, struct sockaddr *to)
 						    &sin6->sin6_addr) != 0) {
 							continue;
 						}
+						if (loopback_scope == 0 &&
+						    IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr)) {
+							continue;
+						}
 						if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
 							if (local_scope == 0)
 								continue;
@@ -3659,7 +3667,10 @@ sctp_add_remote_addr(struct sctp_tcb *stcb, struct sockaddr *newaddr,
 				}
 			} else {
 				/* Validate the address is in scope */
-				if ((IN4_ISPRIVATE_ADDRESS(&sin->sin_addr)) &&
+				if (IN4_ISLOOPBACK_ADDRESS(&sin->sin_addr) &&
+				    (stcb->asoc.scope.loopback_scope == 0)) {
+					addr_inscope = 0;
+				} else if ((IN4_ISPRIVATE_ADDRESS(&sin->sin_addr)) &&
 				    (stcb->asoc.scope.ipv4_local_scope == 0)) {
 					addr_inscope = 0;
 				}
@@ -5607,9 +5618,12 @@ sctp_pcb_init(void)
 	TUNABLE_INT_FETCH("net.inet.sctp.tcbhashsize", &SCTP_BASE_SYSCTL(sctp_hashtblsize));
 	TUNABLE_INT_FETCH("net.inet.sctp.pcbhashsize", &SCTP_BASE_SYSCTL(sctp_pcbtblsize));
 	TUNABLE_INT_FETCH("net.inet.sctp.chunkscale", &SCTP_BASE_SYSCTL(sctp_chunkscale));
+	TUNABLE_INT_FETCH("net.inet.sctp.ignore_loopback_iftype",
+	    &SCTP_BASE_SYSCTL(sctp_ignore_loopback_iftype));
 	VALIDATE_LOADER_TUNABLE(sctp_hashtblsize, SCTPCTL_TCBHASHSIZE);
 	VALIDATE_LOADER_TUNABLE(sctp_pcbtblsize, SCTPCTL_PCBHASHSIZE);
 	VALIDATE_LOADER_TUNABLE(sctp_chunkscale, SCTPCTL_CHUNKSCALE);
+	VALIDATE_LOADER_TUNABLE(sctp_ignore_loopback_iftype, SCTPCTL_IGNORE_LOOPBACK_IFTYPE);
 	SCTP_BASE_INFO(sctp_asochash) = SCTP_HASH_INIT((SCTP_BASE_SYSCTL(sctp_hashtblsize) * 31),
 	    &SCTP_BASE_INFO(hashasocmark));
 	SCTP_BASE_INFO(sctp_ephash) = SCTP_HASH_INIT(SCTP_BASE_SYSCTL(sctp_hashtblsize),
diff --git a/sys/netinet/sctp_sysctl.c b/sys/netinet/sctp_sysctl.c
index bd2f23f40727..27448533329b 100644
--- a/sys/netinet/sctp_sysctl.c
+++ b/sys/netinet/sctp_sysctl.c
@@ -128,6 +128,7 @@ sctp_init_sysctls(void)
 #if defined(SCTP_DEBUG)
 	SCTP_BASE_SYSCTL(sctp_debug_on) = SCTPCTL_DEBUG_DEFAULT;
 #endif
+	SCTP_BASE_SYSCTL(sctp_ignore_loopback_iftype) = SCTPCTL_IGNORE_LOOPBACK_IFTYPE_DEFAULT;
 }
 
 /* It returns an upper limit. No filtering is done here */
@@ -284,6 +285,10 @@ sctp_sysctl_copy_out_local_addresses(struct sctp_inpcb *inp, struct sctp_tcb *st
 						    &sin->sin_addr) != 0) {
 							continue;
 						}
+						if (loopback_scope == 0 &&
+						    IN4_ISLOOPBACK_ADDRESS(&sin->sin_addr)) {
+							continue;
+						}
 						if ((ipv4_local_scope == 0) && (IN4_ISPRIVATE_ADDRESS(&sin->sin_addr))) {
 							continue;
 						}
@@ -305,6 +310,10 @@ sctp_sysctl_copy_out_local_addresses(struct sctp_inpcb *inp, struct sctp_tcb *st
 						    &sin6->sin6_addr) != 0) {
 							continue;
 						}
+						if (loopback_scope == 0 &&
+						    IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr)) {
+							continue;
+						}
 						if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
 							if (local_scope == 0) {
 								continue;
@@ -989,6 +998,7 @@ SCTP_UINT_SYSCTL(blackhole, sctp_blackhole, SCTPCTL_BLACKHOLE);
 SCTP_UINT_SYSCTL(sendall_limit, sctp_sendall_limit, SCTPCTL_SENDALL_LIMIT);
 SCTP_UINT_SYSCTL(diag_info_code, sctp_diag_info_code, SCTPCTL_DIAG_INFO_CODE);
 SCTP_UINT_SYSCTL(ootb_with_zero_cksum, sctp_ootb_with_zero_cksum, SCTPCTL_OOTB_WITH_ZERO_CKSUM);
+SCTP_UINT_SYSCTL_RDTUN(ignore_loopback_iftype, sctp_ignore_loopback_iftype, SCTPCTL_IGNORE_LOOPBACK_IFTYPE);
 #ifdef SCTP_DEBUG
 SCTP_UINT_SYSCTL(debug, sctp_debug_on, SCTPCTL_DEBUG);
 #endif
diff --git a/sys/netinet/sctp_sysctl.h b/sys/netinet/sctp_sysctl.h
index 46c015b0b854..950f53b62051 100644
--- a/sys/netinet/sctp_sysctl.h
+++ b/sys/netinet/sctp_sysctl.h
@@ -118,6 +118,7 @@ struct sctp_sysctl {
 #if defined(SCTP_DEBUG)
 	uint32_t sctp_debug_on;
 #endif
+	uint32_t sctp_ignore_loopback_iftype;
 };
 
 /*
@@ -549,6 +550,11 @@ struct sctp_sysctl {
 #define SCTPCTL_OOTB_WITH_ZERO_CKSUM_MAX	1
 #define SCTPCTL_OOTB_WITH_ZERO_CKSUM_DEFAULT	0
 
+#define SCTPCTL_IGNORE_LOOPBACK_IFTYPE_DESC	"Ignore interface type when determining loopback address scope"
+#define SCTPCTL_IGNORE_LOOPBACK_IFTYPE_MIN		0
+#define SCTPCTL_IGNORE_LOOPBACK_IFTYPE_MAX		1
+#define SCTPCTL_IGNORE_LOOPBACK_IFTYPE_DEFAULT	1
+
 #if defined(SCTP_DEBUG)
 /* debug: Configure debug output */
 #define SCTPCTL_DEBUG_DESC	"Configure debug output"
diff --git a/sys/netinet/sctp_usrreq.c b/sys/netinet/sctp_usrreq.c
index 7c615b4cd02c..4345c0cc6326 100644
--- a/sys/netinet/sctp_usrreq.c
+++ b/sys/netinet/sctp_usrreq.c
@@ -1099,6 +1099,10 @@ sctp_fill_up_addresses_vrf(struct sctp_inpcb *inp,
 						    &sin->sin_addr) != 0) {
 							continue;
 						}
+						if (loopback_scope == 0 &&
+						    (IN4_ISLOOPBACK_ADDRESS(&sin->sin_addr))) {
+							continue;
+						}
 						if ((ipv4_local_scope == 0) &&
 						    (IN4_ISPRIVATE_ADDRESS(&sin->sin_addr))) {
 							continue;
@@ -1147,6 +1151,10 @@ sctp_fill_up_addresses_vrf(struct sctp_inpcb *inp,
 						    &sin6->sin6_addr) != 0) {
 							continue;
 						}
+						if (loopback_scope == 0 &&
+						    (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr))) {
+							continue;
+						}
 						if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
 							if (local_scope == 0)
 								continue;
diff --git a/sys/netinet/sctputil.c b/sys/netinet/sctputil.c
index 119fad13c3f0..05fef2a7fad3 100644
--- a/sys/netinet/sctputil.c
+++ b/sys/netinet/sctputil.c
@@ -7016,6 +7016,10 @@ sctp_local_addr_count(struct sctp_tcb *stcb)
 						    &sin->sin_addr) != 0) {
 							continue;
 						}
+						if (loopback_scope == 0 &&
+						    IN4_ISLOOPBACK_ADDRESS(&sin->sin_addr)) {
+							continue;
+						}
 						if ((ipv4_local_scope == 0) &&
 						    (IN4_ISPRIVATE_ADDRESS(&sin->sin_addr))) {
 							continue;
@@ -7040,6 +7044,10 @@ sctp_local_addr_count(struct sctp_tcb *stcb)
 						    &sin6->sin6_addr) != 0) {
 							continue;
 						}
+						if (loopback_scope == 0 &&
+						    IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr)) {
+							continue;
+						}
 						if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
 							if (local_scope == 0)
 								continue;