You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The opal/mca/if framework enumerates the IPv4 loopback interface (127.0.0.1) inconsistently across platforms, and the MCA parameter that is supposed to control this (if_base_retain_loopback) is silently ignored on Linux and macOS.
On the BSDs (FreeBSD/NetBSD/OpenBSD/DragonFly), the IPv4 component bsdx_ipv4 honors if_base_retain_loopback (default false) and drops the loopback interface from opal_if_list.
On Linux and macOS, the IPv4 component posix_ipv4 has the equivalent loopback check #if 0'd out, so it always keeps loopback regardless of the parameter.
Net effect: for an identical Open MPI configuration, 127.0.0.1/lo is in opal_if_list on Linux/macOS but absent on the BSDs.
This was found while adding an opal_if unit test in #13976: the test failed only on the FreeBSD CI job because 127.0.0.1/lo0 was not enumerated there. (That PR works around it test-side by setting OMPI_MCA_if_base_retain_loopback=1; this issue is about the underlying production behavior.)
Scope: this is IPv4-only. IPv6 loopback (::1) is already dropped on all platforms — linux_ipv6 keeps only global-scope (0x00) addresses and so skips ::1, and bsdx_ipv6 drops it via the same retain_loopback check. So the only live disparity is the IPv4 127.0.0.1 case described here.
Where the disparity lives
if_base_retain_loopback is registered (default false) in opal/mca/if/base/if_base_components.c (~L50-55), declared in opal/mca/if/if.h.
The IPv4 components diverge:
opal/mca/if/posix_ipv4/if_posix.c (~L197-201) — used on Linux and macOS — loopback filter compiled out:
opal/mca/if/bsdx_ipv4/if_bsdx.c (~L96-99) — used on the BSDs — filter is live:
/* skip interface if it is a loopback device (IFF_LOOPBACK set) */if (!opal_if_retain_loopback&&0!= (cur_ifaddrs->ifa_flags&IFF_LOOPBACK)) {
continue;
}
History
The #if 0 in posix_ipv4 is not recent. It was carried into the framework when the interface code was reorganized in 2010 (commit e96b5f486f, "Reorganize the opal interface code in opal/util/if.c ... Move the interface discovery code into a framework"), i.e. it predates the framework split and has been inert ever since. A 2010 follow-up (c74ce1632a) even renamed the variable inside the already-#if 0'd block, so the dead code has been maintained but never re-enabled. So: ~16 years of posix_ipv4 ignoring if_base_retain_loopback.
Who actually consumes opal_if_* (blast radius)
A whole-tree audit of the opal_if* API shows the impact is far narrower than it looks:
The TCP BTL (opal/mca/btl/tcp/) is the only production consumer of the opal_if_* API in the entire Open MPI source tree.
ompi/ and oshmem/ contain zero references to opal_if_* (not even opal_if_list/opal_if_t). The MPI/OpenSHMEM layers never enumerate interfaces directly.
The TCP BTL only calls the enumeration/index/name/address functions (opal_ifbegin, opal_ifnext, opal_ifcount, opal_ifindextoaddr, opal_ifindextoname, opal_ifindextokindex, opal_ifkindextoname, opal_ifnametokindex). The contract-sensitive query functions have zero production callers: opal_ifislocal, opal_ifaddrtoname, and opal_ifisloopback are referenced only by the new unit test and by a stale contrib/build-mca-comps-outside-of-tree/btl_tcp2_* sample (an out-of-tree-build teaching copy of an older TCP BTL).
The bundled 3rd-party/ libraries are not consumers: they ship their own interface code (e.g. pmix_if_* in 3rd-party/openpmix/src/util/pmix_if.{c,h}), independent of OPAL's copy.
Consequence of the blast-radius finding: because opal_ifislocal() & friends have no live callers, and the one real consumer (TCP BTL) self-filters loopback (see below), the contract violation has no current runtime impact in Open MPI — it is dormant. That should weigh heavily in deciding how aggressively to fix it.
Observable consequences
if_base_retain_loopback does not work on Linux/macOS. Setting if_base_retain_loopback=0 (its documented default) does not remove loopback there, because posix_ipv4 ignores the knob. The parameter only actually does anything on the BSDs.
opal_ifislocal()/opal_ifaddrtoname() give different answers across platforms — a documented-contract violation (but dormant).opal/util/if.h documents opal_ifislocal() as returning "true if hostname is local." Because these lookups are backed by opal_if_list:
BSD (default config): they return false — even though 127.0.0.1/localhost are unquestionably local.
This is the same class of localhost-correctness bug as opal_net_islocalhost() (fixed in Add OPAL class/util/datatype unit tests #13976), reached via the interface list rather than address math. It is dormant only because nothing in production calls these functions today.
Internal interface indices differ.opal_if_t::if_index is assigned by enumeration order, so on Linux/macOS loopback occupies an index (shifting every other interface's OPAL index relative to the BSDs), and opal_ifcount() differs by one for the same host.
Consequences for the TCP BTL (the lone consumer)
The TCP BTL iterates opal_if_list (mca_btl_tcp_component_create_instances() in opal/mca/btl/tcp/btl_tcp_component.c, ~L840-950) and ships a default btl_tcp_if_exclude = "127.0.0.1/8,sppp" (~L308). That default exclude exists essentially to compensate for this inconsistency — it is what keeps loopback out of the BTL set on Linux/macOS, where loopback is present in opal_if_list. With stock settings both platform families therefore end up not using loopback for TCP, so the bug is masked in the common case. It leaks through once the defaults are touched:
Overriding btl_tcp_if_exclude without re-listing 127.0.0.1/8 is a cross-platform footgun. E.g. btl_tcp_if_exclude=ib0 to steer TCP off IPoIB: on Linux/macOS, loopback is in opal_if_list, no longer covered by the (replaced) exclude, so the create loop (~L927-949) builds a TCP BTL module for lo/127.0.0.1. The node then advertises a 127.0.0.1 endpoint in its modex; peers on other hosts see a 127.0.0.0/8 address, opal_net_samenetwork() matches it against their own loopback, and connections go to the local machine instead of the intended peer — spurious connect failures/retries/timeouts during wire-up. (The btl_tcp.c ~L95 "Do not create loopback TCP connections" guard only blocks a proc connecting to itself; it does not filter the loopback interface.) On the BSDs the identical setting is harmless, since loopback was never in opal_if_list.
btl_tcp_if_include referencing loopback differs:btl_tcp_if_include=lo resolves via opal_ifnametokindex() (~L903-913) and builds a loopback BTL on Linux/macOS, while on the BSDs opal_ifnametokindex("lo0") fails and the TCP BTL aborts init with "Unknown interface name".
Spurious diagnostics on the BSDs: the default btl_tcp_if_exclude=127.0.0.1/8 resolves against opal_if_list; on the BSDs nothing matches it (loopback absent), which can emit "Did not find interface matching this subnet" when btl_tcp_report_all_unfound_interfaces is enabled — for a default setting.
Options and consequences
"Loopback in list" = whether 127.0.0.1 is in opal_if_list by default (Linux/macOS · BSD).
#
Option
Loopback in list (Lin/mac · BSD)
Consistent?
opal_ifislocal("127.0.0.1")
Param honored on all OSes?
Risk
—
Current state
in · out
no
true · false
no (posix ignores)
—
A
Document only (no code change)
in · out
no
true · false
no
none
B
Remove the #if 0 in posix_ipv4; keep default false
out · out
yes
false · false ¹
yes
med
C
Default → true (leave #if 0)
in · in
default only ²
true · true
no
med
D
Remove #if 0 + default → true
in · in
all values
true · true
yes
med
E
Special-case 127/8+::1 as local in opal_ifislocal/opal_ifaddrtoname
in · out (unchanged)
enum: no ³
true · true
n/a
low
F
B + E (drop loopback everywhere and fix query fns)
out · out
yes
true · true
yes
med
G
Harden the TCP BTL to filter loopback internally
in · out (unchanged)
enum: no ³
true · false (unchanged)
n/a
low
¹ B makes opal_ifislocal("127.0.0.1") consistently false — consistent, but the documented "is local" contract is then uniformly violated (still dormant: no production callers).
² C is consistent only at the default value. Set retain_loopback=0 and the split returns, because posix_ipv4 still ignores the parameter.
³ E/G leave interface enumeration platform-dependent, but that no longer affects the locality contract (E) and is already tolerated by the TCP BTL's default exclude.
Cross-cutting consequences:
Footgun (override of btl_tcp_if_exclude):B, F eliminate it on all platforms (loopback never in the list); C, Dspread it to the BSDs (they would now include loopback by default); G removes it everywhere without touching the framework.
B/F side effect: with loopback gone from the list on Linux/macOS too, the TCP BTL's default 127.0.0.1/8 exclude matches nothing → the "Did not find interface matching this subnet" diagnostic (today BSD-only) could fire on all platforms → that default exclude should be dropped/adjusted as part of the change.
C/D semantics flip: changes the 16-year-old default meaning of retain_loopback from "drop" to "retain"; anything assuming loopback is excluded by default gets it back.
Not tabulated: a larger cleanup — consolidating posix_ipv4 + bsdx_ipv4 (both getifaddrs-based) into one component that honors the parameter — would subsume B/D but is a bigger, riskier diff.
Recommendation
Lowest-risk correctness fix: option E — special-case 127.0.0.0/8 and ::1 in opal_ifislocal()/opal_ifaddrtoname() so locality is correct on every platform, with no change to enumeration or interface selection (and therefore no TCP BTL impact). This directly fixes the documented contract.
If retain_loopback should also actually work, with full enumeration consistency: option F (= B + E) — accept the Linux/macOS enumeration change and adjust the TCP BTL's default exclude.
Avoid C alone (leaves the parameter broken and spreads the footgun to the BSDs).
G is a worthwhile orthogonal hardening if the override-btl_tcp_if_exclude footgun is a concern, regardless of which of A/E/F is chosen.
Given the dormancy (no production callers; TCP BTL self-filters), A (document) or E are the proportionate responses; a disruptive change (C/D/F) is not justified by urgency.
Summary
The
opal/mca/ifframework enumerates the IPv4 loopback interface (127.0.0.1) inconsistently across platforms, and the MCA parameter that is supposed to control this (if_base_retain_loopback) is silently ignored on Linux and macOS.bsdx_ipv4honorsif_base_retain_loopback(defaultfalse) and drops the loopback interface fromopal_if_list.posix_ipv4has the equivalent loopback check#if 0'd out, so it always keeps loopback regardless of the parameter.Net effect: for an identical Open MPI configuration,
127.0.0.1/lois inopal_if_liston Linux/macOS but absent on the BSDs.This was found while adding an
opal_ifunit test in #13976: the test failed only on the FreeBSD CI job because127.0.0.1/lo0was not enumerated there. (That PR works around it test-side by settingOMPI_MCA_if_base_retain_loopback=1; this issue is about the underlying production behavior.)Scope: this is IPv4-only. IPv6 loopback (
::1) is already dropped on all platforms —linux_ipv6keeps only global-scope (0x00) addresses and so skips::1, andbsdx_ipv6drops it via the sameretain_loopbackcheck. So the only live disparity is the IPv4127.0.0.1case described here.Where the disparity lives
if_base_retain_loopbackis registered (defaultfalse) inopal/mca/if/base/if_base_components.c(~L50-55), declared inopal/mca/if/if.h.The IPv4 components diverge:
opal/mca/if/posix_ipv4/if_posix.c(~L197-201) — used on Linux and macOS — loopback filter compiled out:opal/mca/if/bsdx_ipv4/if_bsdx.c(~L96-99) — used on the BSDs — filter is live:History
The
#if 0inposix_ipv4is not recent. It was carried into the framework when the interface code was reorganized in 2010 (commite96b5f486f, "Reorganize the opal interface code in opal/util/if.c ... Move the interface discovery code into a framework"), i.e. it predates the framework split and has been inert ever since. A 2010 follow-up (c74ce1632a) even renamed the variable inside the already-#if 0'd block, so the dead code has been maintained but never re-enabled. So: ~16 years ofposix_ipv4ignoringif_base_retain_loopback.Who actually consumes
opal_if_*(blast radius)A whole-tree audit of the
opal_if*API shows the impact is far narrower than it looks:opal/mca/btl/tcp/) is the only production consumer of theopal_if_*API in the entire Open MPI source tree.ompi/andoshmem/contain zero references toopal_if_*(not evenopal_if_list/opal_if_t). The MPI/OpenSHMEM layers never enumerate interfaces directly.opal_ifbegin,opal_ifnext,opal_ifcount,opal_ifindextoaddr,opal_ifindextoname,opal_ifindextokindex,opal_ifkindextoname,opal_ifnametokindex). The contract-sensitive query functions have zero production callers:opal_ifislocal,opal_ifaddrtoname, andopal_ifisloopbackare referenced only by the new unit test and by a stalecontrib/build-mca-comps-outside-of-tree/btl_tcp2_*sample (an out-of-tree-build teaching copy of an older TCP BTL).3rd-party/libraries are not consumers: they ship their own interface code (e.g.pmix_if_*in3rd-party/openpmix/src/util/pmix_if.{c,h}), independent of OPAL's copy.Consequence of the blast-radius finding: because
opal_ifislocal()& friends have no live callers, and the one real consumer (TCP BTL) self-filters loopback (see below), the contract violation has no current runtime impact in Open MPI — it is dormant. That should weigh heavily in deciding how aggressively to fix it.Observable consequences
if_base_retain_loopbackdoes not work on Linux/macOS. Settingif_base_retain_loopback=0(its documented default) does not remove loopback there, becauseposix_ipv4ignores the knob. The parameter only actually does anything on the BSDs.opal_ifislocal()/opal_ifaddrtoname()give different answers across platforms — a documented-contract violation (but dormant).opal/util/if.hdocumentsopal_ifislocal()as returning "true ifhostnameis local." Because these lookups are backed byopal_if_list:opal_ifislocal("127.0.0.1")/opal_ifislocal("localhost")return true.127.0.0.1/localhostare unquestionably local.This is the same class of localhost-correctness bug as
opal_net_islocalhost()(fixed in Add OPAL class/util/datatype unit tests #13976), reached via the interface list rather than address math. It is dormant only because nothing in production calls these functions today.Internal interface indices differ.
opal_if_t::if_indexis assigned by enumeration order, so on Linux/macOS loopback occupies an index (shifting every other interface's OPAL index relative to the BSDs), andopal_ifcount()differs by one for the same host.Consequences for the TCP BTL (the lone consumer)
The TCP BTL iterates
opal_if_list(mca_btl_tcp_component_create_instances()inopal/mca/btl/tcp/btl_tcp_component.c, ~L840-950) and ships a defaultbtl_tcp_if_exclude = "127.0.0.1/8,sppp"(~L308). That default exclude exists essentially to compensate for this inconsistency — it is what keeps loopback out of the BTL set on Linux/macOS, where loopback is present inopal_if_list. With stock settings both platform families therefore end up not using loopback for TCP, so the bug is masked in the common case. It leaks through once the defaults are touched:btl_tcp_if_excludewithout re-listing127.0.0.1/8is a cross-platform footgun. E.g.btl_tcp_if_exclude=ib0to steer TCP off IPoIB: on Linux/macOS, loopback is inopal_if_list, no longer covered by the (replaced) exclude, so the create loop (~L927-949) builds a TCP BTL module forlo/127.0.0.1. The node then advertises a127.0.0.1endpoint in its modex; peers on other hosts see a127.0.0.0/8address,opal_net_samenetwork()matches it against their own loopback, and connections go to the local machine instead of the intended peer — spurious connect failures/retries/timeouts during wire-up. (Thebtl_tcp.c~L95 "Do not create loopback TCP connections" guard only blocks a proc connecting to itself; it does not filter the loopback interface.) On the BSDs the identical setting is harmless, since loopback was never inopal_if_list.btl_tcp_if_includereferencing loopback differs:btl_tcp_if_include=loresolves viaopal_ifnametokindex()(~L903-913) and builds a loopback BTL on Linux/macOS, while on the BSDsopal_ifnametokindex("lo0")fails and the TCP BTL aborts init with "Unknown interface name".btl_tcp_if_exclude=127.0.0.1/8resolves againstopal_if_list; on the BSDs nothing matches it (loopback absent), which can emit "Did not find interface matching this subnet" whenbtl_tcp_report_all_unfound_interfacesis enabled — for a default setting.Options and consequences
"Loopback in list" = whether
127.0.0.1is inopal_if_listby default (Linux/macOS · BSD).opal_ifislocal("127.0.0.1")#if 0inposix_ipv4; keep defaultfalsetrue(leave#if 0)#if 0+ default →true127/8+::1as local inopal_ifislocal/opal_ifaddrtoname¹ B makes
opal_ifislocal("127.0.0.1")consistentlyfalse— consistent, but the documented "is local" contract is then uniformly violated (still dormant: no production callers).² C is consistent only at the default value. Set
retain_loopback=0and the split returns, becauseposix_ipv4still ignores the parameter.³ E/G leave interface enumeration platform-dependent, but that no longer affects the locality contract (E) and is already tolerated by the TCP BTL's default exclude.
Cross-cutting consequences:
btl_tcp_if_exclude): B, F eliminate it on all platforms (loopback never in the list); C, D spread it to the BSDs (they would now include loopback by default); G removes it everywhere without touching the framework.127.0.0.1/8exclude matches nothing → the "Did not find interface matching this subnet" diagnostic (today BSD-only) could fire on all platforms → that default exclude should be dropped/adjusted as part of the change.retain_loopbackfrom "drop" to "retain"; anything assuming loopback is excluded by default gets it back.posix_ipv4+bsdx_ipv4(bothgetifaddrs-based) into one component that honors the parameter — would subsume B/D but is a bigger, riskier diff.Recommendation
127.0.0.0/8and::1inopal_ifislocal()/opal_ifaddrtoname()so locality is correct on every platform, with no change to enumeration or interface selection (and therefore no TCP BTL impact). This directly fixes the documented contract.retain_loopbackshould also actually work, with full enumeration consistency: option F (= B + E) — accept the Linux/macOS enumeration change and adjust the TCP BTL's default exclude.btl_tcp_if_excludefootgun is a concern, regardless of which of A/E/F is chosen.References
opal_ifunit test added in Add OPAL class/util/datatype unit tests #13976 (which works around this test-side viaOMPI_MCA_if_base_retain_loopback=1).opal/mca/if/{posix_ipv4,bsdx_ipv4,bsdx_ipv6,linux_ipv6,base},opal/util/if.{h,c},opal/mca/btl/tcp/btl_tcp_component.c,opal/mca/btl/tcp/btl_tcp.c.