kern/67823: hardware checksums are to be disabled when using bridge(4)

Eygene Ryabinkin rea at mbslab.kiae.ru
Fri Jun 11 09:30:27 GMT 2004


>Number:         67823
>Category:       kern
>Synopsis:       hardware checksums are to be disabled when using bridge(4)
>Confidential:   no
>Severity:       serious
>Priority:       high
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Fri Jun 11 09:30:09 GMT 2004
>Closed-Date:
>Last-Modified:
>Originator:     Eygene Ryabinkin
>Release:        FreeBSD 5.2.1-RELEASE i386
>Organization:
>Environment:
System: FreeBSD pretty.mbslab.kiae.ru 5.2.1-RELEASE FreeBSD 5.2.1-RELEASE #10: Fri Jun 11 12:37:21 MSD 2004 rea at pretty.mbslab.kiae.ru:/usr/src/sys/i386/compile/PRETTY i386

>Description:
 When one is using non-transparent bridge(4) with multiple NICs and some of
NIC's (drivers) are capable of hardware checksumming and some are not,
one will expirience problems with connecting to bridging host from the
"inside" network. "External" network will be fine.
 Diagram:

	+------------------+        +---------------+        +---------------+
  + External network +------=-+ Bridging host +-=------+ Inner network +
	+------------------+      | +---------------+ |      +---------------+
                            |                   |
                    External interface  Internal interface

 The symptoms of this bug are:
  1) You can not reach "Bridging host" from "Inner network" and vice versa.
  2) You CAN reach "External network" from both "Bridging host" and
     "Inner network".
	3) If you will run of "Inner network" host tcpdump(1) with flags '-vp'
     and simultaneously try to connect to "Bridging host" you will see
     "bad cksum" or "bad tcp cksum" messages for packets that are coming from
     "Bridging host".

 If you have same NICs on all bridging interfaces you probably will not suffer
from this bug, but I have not done such tests.

 This problem was already discovered in 4.8-RELEASE by [pak at cns.utoronto.ca]
and we have PR kern/57100 for it.
>How-To-Repeat:
 Get two cards with distinct features, or the cards whose FreeBSD drivers have
distinct hardware checksumming features and install it to some host. Turn this
host into bridge and create "Inner network". Try to ping/telnet/slogin/etc and
expirience this bug.
 I myself have one 3COM Boomerang NIC and two D-Link DGE-530T cards.
>Fix:
 Patch net/bridge.c and net/bridge.h. The following is a quick fix, because
IMHO bridge implementation has to be corrected more seriously to take into
account distinct features of bridging cards. OK, here we go: added code is
embraced by #ifdef HWASSIST_PATCH ... #endif. Essentially we turn off
hardware checksumming when bridging is enabled and turn it on (if it was)
when bridging is disabled.

 net/bridge.h
--------------------&<-------------------
/*
 * Copyright (c) 1998-2002 Luigi Rizzo
 *
 * Work partly supported by: Cisco Systems, Inc. - NSITE lab, RTP, NC
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * $FreeBSD: src/sys/net/bridge.h,v 1.12 2002/11/15 00:00:14 sam Exp $
 */
#define HWASSIST_PATCH

extern int do_bridge;

/*
 * We need additional per-interface info for the bridge, which is
 * stored in a struct bdg_softc. The ifp2sc[] array provides a pointer
 * to this struct using the if_index as a mapping key.
 * bdg_softc has a backpointer to the struct ifnet, the bridge
 * flags, and a cluster (bridging occurs only between port of the
 * same cluster).
 */

struct cluster_softc;	/* opaque here, defined in bridge.c */

struct bdg_softc {
    struct ifnet *ifp ;
    /* also ((struct arpcom *)ifp)->ac_enaddr is the eth. addr */
    int flags ;
#define IFF_BDG_PROMISC 0x0001  /* set promisc mode on this if.	*/
#define IFF_MUTE        0x0002  /* mute this if for bridging.   */
#define IFF_USED        0x0004  /* use this if for bridging.    */
#ifdef HWASSIST_PATCH
    u_long hwassist;
#endif
    struct cluster_softc *cluster;
} ;
-------------------->&-------------------
 The rest of net/bridge.c is unmodified.

 net/bridge.c, only modified functions are bridge_off() and bridge_on().
--------------------&<-------------------
/*
 * Turn off bridging, by clearing promisc mode on the interface,
 * marking the interface as unused, and clearing the name in the
 * stats entry.
 * Also dispose the hash tables associated with the clusters.
 */
static void
bridge_off(void)
{
    struct ifnet *ifp ;
    int i;

    BDG_LOCK_ASSERT();

    DPRINTF(("%s: n_clusters %d\n", __func__, n_clusters));

    IFNET_RLOCK();
    TAILQ_FOREACH(ifp, &ifnet, if_link) {
	struct bdg_softc *b;

	if (ifp->if_index >= BDG_MAX_PORTS)
	    continue;	/* make sure we do not go beyond the end */
	b = &ifp2sc[ifp->if_index];

	if ( b->flags & IFF_BDG_PROMISC ) {
	    ifpromisc(ifp, 0);
#ifdef HWASSIST_PATCH
	    ifp->if_hwassist = b->hwassist;
#endif
	    b->flags &= ~(IFF_BDG_PROMISC|IFF_MUTE) ;
	    DPRINTF(("%s: %s promisc OFF if_flags 0x%x "
		"bdg_flags 0x%x\n", __func__, ifp->if_xname,
		ifp->if_flags, b->flags));
	}
	b->flags &= ~(IFF_USED) ;
	b->cluster = NULL;
	bdg_stats.s[ifp->if_index].name[0] = '\0';
    }
    IFNET_RUNLOCK();
    /* flush_tables */

    for (i=0; i < n_clusters; i++) {
	free(clusters[i].ht, M_IFADDR);
	free(clusters[i].my_macs, M_IFADDR);
    }
    if (clusters != NULL)
	free(clusters, M_IFADDR);
    clusters = NULL;
    n_clusters =0;
}

/*
 * set promisc mode on the interfaces we use.
 */
static void
bridge_on(void)
{
    struct ifnet *ifp ;

    BDG_LOCK_ASSERT();

    IFNET_RLOCK();
    TAILQ_FOREACH(ifp, &ifnet, if_link) {
	struct bdg_softc *b = &ifp2sc[ifp->if_index];

	if ( !(b->flags & IFF_USED) )
	    continue ;
	if ( !( ifp->if_flags & IFF_UP) ) {
	    if_up(ifp);
	}
	if ( !(b->flags & IFF_BDG_PROMISC) ) {
#ifdef HWASSIST_PATCH
	    b->hwassist = ifp->if_hwassist;
	    ifp->if_hwassist = 0;
#endif
	    (void) ifpromisc(ifp, 1);
	    b->flags |= IFF_BDG_PROMISC ;
	    DPRINTF(("%s: %s promisc ON if_flags 0x%x bdg_flags 0x%x\n",
		__func__, ifp->if_xname, ifp->if_flags, b->flags));
	}
	if (b->flags & IFF_MUTE) {
	    DPRINTF(("%s: unmuting %s\n", __func__, ifp->if_xname));
	    b->flags &= ~IFF_MUTE;
	}
    }
    IFNET_RUNLOCK();
}
-------------------->&-------------------
>Release-Note:
>Audit-Trail:
>Unformatted:


More information about the freebsd-bugs mailing list