diff --git a/share/man/man4/bridge.4 b/share/man/man4/bridge.4 --- a/share/man/man4/bridge.4 +++ b/share/man/man4/bridge.4 @@ -36,7 +36,7 @@ .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE .\" POSSIBILITY OF SUCH DAMAGE. .\" -.Dd April 10, 2024 +.Dd May 5, 2025 .Dt IF_BRIDGE 4 .Os .Sh NAME @@ -158,6 +158,19 @@ stream. This is useful for reconstructing the traffic for network taps that transmit the RX/TX signals out through two separate interfaces. +.Pp +To allow the host to communicate with bridge members, IP addresses +should be assigned to the +.Nm +interface itself, not to the bridge's member interfaces. +Assigning IP addresses to bridge member interfaces is unsupported, but +for backward compatibility, it is permitted if the +.Xr sysctl 8 +variable +.Va net.link.bridge.member_ifaddrs +is set to 1, which is the default. +In a future release, this sysctl may be set to 0 by default, or may be +removed entirely. .Sh IPV6 SUPPORT .Nm supports the diff --git a/sys/net/if_bridge.c b/sys/net/if_bridge.c --- a/sys/net/if_bridge.c +++ b/sys/net/if_bridge.c @@ -336,6 +336,7 @@ static void bridge_forward(struct bridge_softc *, struct bridge_iflist *, struct mbuf *m); +static bool bridge_member_ifaddrs(void); static void bridge_timer(void *); @@ -502,6 +503,19 @@ CTLFLAG_RW | CTLFLAG_VNET, &VNET_NAME(log_mac_flap), true, "Log MAC address port flapping"); +/* allow IP addresses on bridge members */ +VNET_DEFINE_STATIC(bool, member_ifaddrs) = true; +#define V_member_ifaddrs VNET(member_ifaddrs) +SYSCTL_BOOL(_net_link_bridge, OID_AUTO, member_ifaddrs, + CTLFLAG_RW | CTLFLAG_VNET, &VNET_NAME(member_ifaddrs), true, + "Allow layer 3 addresses on bridge members"); + +static bool +bridge_member_ifaddrs(void) +{ + return (V_member_ifaddrs); +} + VNET_DEFINE_STATIC(int, log_interval) = 5; VNET_DEFINE_STATIC(int, log_count) = 0; VNET_DEFINE_STATIC(struct timeval, log_last) = { 0 }; @@ -665,6 +679,7 @@ bridge_dn_p = bridge_dummynet; bridge_same_p = bridge_same; bridge_get_softc_p = bridge_get_softc; + bridge_member_ifaddrs_p = bridge_member_ifaddrs; bridge_detach_cookie = EVENTHANDLER_REGISTER( ifnet_departure_event, bridge_ifdetach, NULL, EVENTHANDLER_PRI_ANY); @@ -675,6 +690,7 @@ bridge_dn_p = NULL; bridge_same_p = NULL; bridge_get_softc_p = NULL; + bridge_member_ifaddrs_p = NULL; break; default: return (EOPNOTSUPP); @@ -1313,6 +1329,25 @@ return (EINVAL); } + /* + * If member_ifaddrs is disabled, do not allow an interface with + * assigned IP addresses to be added to a bridge. + */ + if (!V_member_ifaddrs) { + struct ifaddr *ifa; + + CK_STAILQ_FOREACH(ifa, &ifs->if_addrhead, ifa_link) { +#ifdef INET + if (ifa->ifa_addr->sa_family == AF_INET) + return (EINVAL); +#endif +#ifdef INET6 + if (ifa->ifa_addr->sa_family == AF_INET6) + return (EINVAL); +#endif + } + } + #ifdef INET6 /* * Two valid inet6 addresses with link-local scope must not be @@ -2742,17 +2777,25 @@ do { GRAB_OUR_PACKETS(bifp) } while (0); /* - * Give a chance for ifp at first priority. This will help when the - * packet comes through the interface like VLAN's with the same MACs - * on several interfaces from the same bridge. This also will save - * some CPU cycles in case the destination interface and the input - * interface (eq ifp) are the same. + * We only need to check members interfaces if member_ifaddrs is + * enabled; otherwise we should have never traffic destined for a + * member's lladdr. */ - do { GRAB_OUR_PACKETS(ifp) } while (0); - /* Now check the all bridge members. */ - CK_LIST_FOREACH(bif2, &sc->sc_iflist, bif_next) { - GRAB_OUR_PACKETS(bif2->bif_ifp) + if (V_member_ifaddrs) { + /* + * Give a chance for ifp at first priority. This will help when + * the packet comes through the interface like VLAN's with the + * same MACs on several interfaces from the same bridge. This + * also will save some CPU cycles in case the destination + * interface and the input interface (eq ifp) are the same. + */ + do { GRAB_OUR_PACKETS(ifp) } while (0); + + /* Now check the all bridge members. */ + CK_LIST_FOREACH(bif2, &sc->sc_iflist, bif_next) { + GRAB_OUR_PACKETS(bif2->bif_ifp) + } } #undef CARP_CHECK_WE_ARE_DST diff --git a/sys/net/if_bridgevar.h b/sys/net/if_bridgevar.h --- a/sys/net/if_bridgevar.h +++ b/sys/net/if_bridgevar.h @@ -325,6 +325,7 @@ extern void (*bridge_dn_p)(struct mbuf *, struct ifnet *); extern bool (*bridge_same_p)(const void *, const void *); extern void *(*bridge_get_softc_p)(struct ifnet *); +extern bool (*bridge_member_ifaddrs_p)(void); #endif /* _KERNEL */ diff --git a/sys/net/if_ethersubr.c b/sys/net/if_ethersubr.c --- a/sys/net/if_ethersubr.c +++ b/sys/net/if_ethersubr.c @@ -112,6 +112,7 @@ void (*bridge_dn_p)(struct mbuf *, struct ifnet *); bool (*bridge_same_p)(const void *, const void *); void *(*bridge_get_softc_p)(struct ifnet *); +bool (*bridge_member_ifaddrs_p)(void); /* if_lagg(4) support */ struct mbuf *(*lagg_input_ethernet_p)(struct ifnet *, struct mbuf *); diff --git a/sys/netinet/in.c b/sys/netinet/in.c --- a/sys/netinet/in.c +++ b/sys/netinet/in.c @@ -57,6 +57,7 @@ #include #include #include +#include #include #include #include @@ -518,6 +519,13 @@ return (error); #endif + /* + * Check if bridge wants to allow adding addrs to member interfaces. + */ + if (ifp->if_bridge && bridge_member_ifaddrs_p && + !bridge_member_ifaddrs_p()) + return (EINVAL); + /* * See whether address already exist. */ diff --git a/sys/netinet6/in6.c b/sys/netinet6/in6.c --- a/sys/netinet6/in6.c +++ b/sys/netinet6/in6.c @@ -86,6 +86,7 @@ #include #include #include +#include #include #include #include @@ -1234,6 +1235,13 @@ int carp_attached = 0; int error; + /* Check if this interface is a bridge member */ + if (ifp->if_bridge && bridge_member_ifaddrs_p && + !bridge_member_ifaddrs_p()) { + error = EINVAL; + goto out; + } + /* * first, make or update the interface address structure, * and link it to the list. diff --git a/tests/sys/net/if_bridge_test.sh b/tests/sys/net/if_bridge_test.sh --- a/tests/sys/net/if_bridge_test.sh +++ b/tests/sys/net/if_bridge_test.sh @@ -703,6 +703,82 @@ vnet_cleanup } +atf_test_case "member_ifaddrs_enabled" "cleanup" +member_ifaddrs_enabled_head() +{ + atf_set descr 'bridge with member_ifaddrs=1' + atf_set require.user root +} + +member_ifaddrs_enabled_body() +{ + vnet_init + vnet_init_bridge + + ep=$(vnet_mkepair) + ifconfig ${ep}a inet 192.0.2.1/24 up + + vnet_mkjail one ${ep}b + jexec one sysctl net.link.bridge.member_ifaddrs=1 + jexec one ifconfig ${ep}b inet 192.0.2.2/24 up + jexec one ifconfig bridge0 create addm ${ep}b + + atf_check -s exit:0 -o ignore ping -c3 -t1 192.0.2.2 +} + +member_ifaddrs_enabled_cleanup() +{ + vnet_cleanup +} + +atf_test_case "member_ifaddrs_disabled" "cleanup" +member_ifaddrs_disabled_head() +{ + atf_set descr 'bridge with member_ifaddrs=0' + atf_set require.user root +} + +member_ifaddrs_disabled_body() +{ + vnet_init + vnet_init_bridge + + vnet_mkjail one + jexec one sysctl net.link.bridge.member_ifaddrs=0 + + bridge=$(jexec one ifconfig bridge create) + + # adding an interface with an IPv4 address + ep=$(jexec one ifconfig epair create) + jexec one ifconfig ${ep} 192.0.2.1/32 + atf_check -s exit:1 -e ignore jexec one ifconfig ${bridge} addm ${ep} + + # adding an interface with an IPv6 address + ep=$(jexec one ifconfig epair create) + jexec one ifconfig ${ep} inet6 2001:db8::1/128 + atf_check -s exit:1 -e ignore jexec one ifconfig ${bridge} addm ${ep} + + # adding an interface with an IPv6 link-local address + ep=$(jexec one ifconfig epair create) + jexec one ifconfig ${ep} inet6 -ifdisabled auto_linklocal up + atf_check -s exit:1 -e ignore jexec one ifconfig ${bridge} addm ${ep} + + # adding an IPv4 address to a member + ep=$(jexec one ifconfig epair create) + jexec one ifconfig ${bridge} addm ${ep} + atf_check -s exit:1 -e ignore jexec one ifconfig ${ep} inet 192.0.2.2/32 + + # adding an IPv6 address to a member + ep=$(jexec one ifconfig epair create) + jexec one ifconfig ${bridge} addm ${ep} + atf_check -s exit:1 -e ignore jexec one ifconfig ${ep} inet6 2001:db8::1/128 +} + +member_ifaddrs_disabled_cleanup() +{ + vnet_cleanup +} + atf_init_test_cases() { atf_add_test_case "bridge_transmit_ipv4_unicast" @@ -718,4 +794,6 @@ atf_add_test_case "mtu" atf_add_test_case "vlan" atf_add_test_case "many_bridge_members" + atf_add_test_case "member_ifaddrs_enabled" + atf_add_test_case "member_ifaddrs_disabled" }