Page MenuHomeFreeBSD

D54948.diff
No OneTemporary

D54948.diff

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 October 13, 2025
+.Dd January 29, 2026
.Dt IF_BRIDGE 4
.Os
.Sh NAME
@@ -144,7 +144,7 @@
If the MTU of a bridge is changed after its creation, the MTU of all member
interfaces is also changed to match.
.Pp
-The TOE, TSO, TXCSUM and TXCSUM6 capabilities on all interfaces added to the
+The TOE and TSO capabilities on all interfaces added to the
bridge are disabled if any of the interfaces do not support/enable them.
The LRO capability is always disabled.
All the capabilities are restored when the interface is removed from the bridge.
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
@@ -78,6 +78,7 @@
#include "opt_inet.h"
#include "opt_inet6.h"
+#include "opt_sctp.h"
#define EXTERR_CATEGORY EXTERR_CAT_BRIDGE
@@ -137,6 +138,10 @@
#include <net/route.h>
+#if defined(SCTP) || defined(SCTP_SUPPORT)
+#include <netinet/sctp_crc32.h>
+#endif
+
/*
* At various points in the code we need to know if we're hooked into the INET
* and/or INET6 pfil. Define some macros to do that based on which IP versions
@@ -195,8 +200,7 @@
/*
* List of capabilities to possibly mask on the member interface.
*/
-#define BRIDGE_IFCAPS_MASK (IFCAP_TOE|IFCAP_TSO|IFCAP_TXCSUM|\
- IFCAP_TXCSUM_IPV6|IFCAP_MEXTPG)
+#define BRIDGE_IFCAPS_MASK (IFCAP_TOE|IFCAP_TSO|IFCAP_MEXTPG)
/*
* List of capabilities to strip
@@ -841,6 +845,10 @@
return (bifa->bif_sc == bifb->bif_sc);
}
+#define BRIDGE_CSUM_IP4_ALL (CSUM_IP | CSUM_IP_SCTP | CSUM_IP_TCP | CSUM_IP_UDP)
+#define BRIDGE_CSUM_IP6_ALL (CSUM_IP6_SCTP | CSUM_IP6_TCP | CSUM_IP6_UDP)
+#define BRIDGE_CSUM_ALL (BRIDGE_CSUM_IP4_ALL | BRIDGE_CSUM_IP6_ALL)
+
/*
* bridge_clone_create:
*
@@ -871,7 +879,9 @@
ifp->if_softc = sc;
if_initname(ifp, bridge_name, ifd->unit);
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
- ifp->if_capabilities = ifp->if_capenable = IFCAP_VLAN_HWTAGGING;
+ ifp->if_capabilities = ifp->if_capenable = IFCAP_VLAN_HWTAGGING |
+ IFCAP_TXCSUM | IFCAP_TXCSUM_IPV6 | IFCAP_VLAN_HWCSUM;
+ ifp->if_hwassist = BRIDGE_CSUM_ALL;
ifp->if_ioctl = bridge_ioctl;
#ifdef ALTQ
ifp->if_start = bridge_altq_start;
@@ -998,7 +1008,7 @@
} args;
struct ifdrv *ifd = (struct ifdrv *) data;
const struct bridge_control *bc;
- int error = 0, oldmtu;
+ int error = 0, oldmtu, mask;
BRIDGE_LOCK(sc);
@@ -1125,6 +1135,20 @@
sc->sc_ifp->if_mtu = ifr->ifr_mtu;
}
break;
+ case SIOCSIFCAP:
+ mask = ifr->ifr_reqcap ^ if_getcapenable(ifp);
+ if (mask & IFCAP_TXCSUM) {
+ if_togglecapenable(ifp, IFCAP_TXCSUM);
+ if_togglehwassist(ifp, BRIDGE_CSUM_IP4_ALL);
+ }
+ if (mask & IFCAP_TXCSUM_IPV6) {
+ if_togglecapenable(ifp, IFCAP_TXCSUM_IPV6);
+ if_togglehwassist(ifp, BRIDGE_CSUM_IP6_ALL);
+ }
+ if (mask & IFCAP_VLAN_HWCSUM) {
+ if_togglecapenable(ifp, IFCAP_VLAN_HWCSUM);
+ }
+ break;
default:
/*
* drop the lock as ether_ioctl() will call bridge_start() and
@@ -2373,6 +2397,134 @@
ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
}
+#if defined(INET) || defined(INET6)
+
+/*
+ * Depending on what is set in the given csum_req, compute and insert the IP,
+ * SCTP, TCP, or UDP checksum for the given mbuf chain m.
+ * Return the modified mbuf chain, or NULL if the mbuf chain was destroyed.
+ */
+static struct mbuf *
+bridge_delayed_cksum(struct mbuf *m, uint32_t csum_req)
+{
+ struct ether_header *eh;
+ uint16_t ether_type, inner_ether_type, iph_offset;
+
+ KASSERT((csum_req & BRIDGE_CSUM_ALL) != 0,
+ ("%s: got an mbuf without csum offloading flag set.", __func__));
+
+ m = m_pullup(m, ETHER_HDR_LEN + 2 * ETHER_VLAN_ENCAP_LEN);
+ if (m == NULL) {
+ return (NULL);
+ }
+ /* determine ethernet header length */
+ eh = mtod(m, struct ether_header *);
+ ether_type = ntohs(eh->ether_type);
+ switch (ether_type) {
+ case ETHERTYPE_IP:
+ case ETHERTYPE_IPV6:
+ iph_offset = ETHER_HDR_LEN;
+ inner_ether_type = ether_type;
+ break;
+ case ETHERTYPE_VLAN:
+ iph_offset = ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
+ inner_ether_type =
+ ntohs(((struct ether_vlan_header *)eh)->evl_proto);
+ break;
+ case ETHERTYPE_QINQ:
+ iph_offset = ETHER_HDR_LEN + 2 * ETHER_VLAN_ENCAP_LEN;
+ /* QINQ header adds four 2-byte fields before the ether_type */
+ inner_ether_type = ntohs(*(&(eh->ether_type) + 4));
+ break;
+ default:
+ KASSERT(0, ("%s: got the unexpected ether_type %d.\n",
+ __func__, ether_type));
+ return (m);
+ }
+
+#if defined(INET)
+ if (csum_req & BRIDGE_CSUM_IP4_ALL) {
+ struct ip *ip;
+
+ if (inner_ether_type != ETHERTYPE_IP) {
+ KASSERT(0, ("%s: got an mbuf with an IPv4 csum offloading flag set that contains no IPv4 packet.",
+ __func__));
+ return (m);
+ }
+ /*
+ * pullup enough to access the ihl, total length, and checksum
+ * fields in the IP header.
+ */
+ m = m_pullup(m, iph_offset + offsetof(struct ip, ip_src));
+ if (m == NULL) {
+ return (NULL);
+ }
+ ip = (struct ip *)mtodo(m, iph_offset);
+ if (csum_req & CSUM_IP) {
+ ip->ip_sum = 0;
+ ip->ip_sum = in_cksum_skip(m,
+ iph_offset + (ip->ip_hl << 2), iph_offset);
+ if (m->m_flags & M_PKTHDR) {
+ m->m_pkthdr.csum_flags &= ~CSUM_IP;
+ }
+ }
+ if (csum_req & (CSUM_IP_TCP | CSUM_IP_UDP)) {
+ in_delayed_cksum_o(m, iph_offset);
+ if (m->m_flags & M_PKTHDR) {
+ m->m_pkthdr.csum_flags &=
+ ~(CSUM_IP_TCP | CSUM_IP_UDP);
+ }
+ }
+#if defined(SCTP) || defined(SCTP_SUPPORT)
+ else if (csum_req & CSUM_IP_SCTP) {
+ sctp_delayed_cksum(m, (uint32_t)(iph_offset +
+ (ip->ip_hl << 2)));
+ if (m->m_flags & M_PKTHDR) {
+ m->m_pkthdr.csum_flags &= ~CSUM_IP_SCTP;
+ }
+ }
+#endif /* defined(SCTP) || defined(SCTP_SUPPORT) */
+ }
+#endif /* defined(INET) */
+#if defined(INET6)
+ if (csum_req & BRIDGE_CSUM_IP6_ALL) {
+ int ip6p_offset;
+
+ if (inner_ether_type != ETHERTYPE_IPV6) {
+ KASSERT(0, ("%s: got an mbuf with an IPv6 csum offloading flag set that contains no IPv6 packet.",
+ __func__));
+ return (m);
+ }
+ ip6p_offset = ip6_lasthdr(m, iph_offset, IPPROTO_IPV6, NULL);
+ if ((ip6p_offset < iph_offset + sizeof(struct ip6_hdr)) ||
+ (m->m_flags & M_PKTHDR && ip6p_offset > m->m_pkthdr.len)) {
+ KASSERT(0, ("%s: offset to the IPv6 payload seems incorrect.",
+ __func__));
+ return (m);
+ }
+
+ if (csum_req & (CSUM_IP6_TCP | CSUM_IP6_UDP)) {
+ in6_delayed_cksum(m, m->m_pkthdr.len - ip6p_offset,
+ ip6p_offset);
+ if (m->m_flags & M_PKTHDR) {
+ m->m_pkthdr.csum_flags &=
+ ~(CSUM_IP6_TCP | CSUM_IP6_UDP);
+ }
+ }
+#if defined(SCTP) || defined(SCTP_SUPPORT)
+ else { /* csum_req & CSUM_IP6_SCTP */
+ sctp_delayed_cksum(m, ip6p_offset);
+ if (m->m_flags & M_PKTHDR) {
+ m->m_pkthdr.csum_flags &= ~CSUM_IP6_SCTP;
+ }
+ }
+#endif /* defined(SCTP) || defined(SCTP_SUPPORT) */
+ }
+#endif /* defined(INET6) */
+ return (m);
+}
+#endif /* defined(INET) || defined(INET6) */
+
/*
* bridge_enqueue:
*
@@ -2442,6 +2594,32 @@
}
M_ASSERTPKTHDR(m); /* We shouldn't transmit mbuf without pkthdr */
+
+#if defined(INET) || defined(INET6)
+ uint32_t csum_req;
+
+ /*
+ * If IP, SCTP, TCP, or UDP header still needs a valid checksum
+ * and the outgoing interface will not compute it for us,
+ * do it here.
+ */
+ csum_req = m->m_pkthdr.csum_flags & BRIDGE_CSUM_ALL &
+ ~dst_ifp->if_hwassist;
+ if (__predict_false(csum_req != 0)) {
+ m = bridge_delayed_cksum(m, csum_req);
+ if (m == NULL) {
+ /*
+ * The mbuf was destroyed during the attempt to
+ * compute and insert the checksum.
+ */
+ if_printf(dst_ifp,
+ "error while computing and inserting IP/SCTP/TCP/UDP checksum\n");
+ if_inc_counter(dst_ifp, IFCOUNTER_OERRORS, 1);
+ continue;
+ }
+ }
+#endif /* defined(INET) || defined(INET6) */
+
/*
* XXXZL: gif(4) requires the af to be saved in csum_data field
* so that gif_transmit() routine can pull it back.

File Metadata

Mime Type
text/plain
Expires
Fri, May 1, 3:21 AM (5 h, 17 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
32552053
Default Alt Text
D54948.diff (7 KB)

Event Timeline