Subject: Lance Rev C ethernet bug
To: None <thorpej@nas.nasa.gov>
From: Gordon W. Ross <gwr@mc.com>
List: port-sun3
Date: 10/26/1995 16:03:49
> From: Jason Thorpe <thorpej@nas.nasa.gov>
> Date: Thu, 26 Oct 1995 11:01:04 -0700

[ Using BPF causes endless "le0: Lance revision C..." warnings. ]

> Basically, rbootd sets the multicast address filter of the interface if 
> possible, or places it in promiscuous mode, because the HP bootrom uses 
> ethernet multicast rather than ethernet broadcast.  If you look around line 
> 681 of sys/arch/sun3/dev/if_le.c, you'll see that packets that aren't 
> sent to a) "our" ethernet addr or b) the broadcast addr, are blamed on a 
> bug that existed in the LANCE chip Rev C, and thrown away.  If you don't 
> have a Rev C LANCE in your system, it's safe to not do this check.  To 
> make rbootd work on my Sun 3 at home, I had to undef the LANCE_REVC_BUG 
> macro in if_le.c.  I've always thought that this definition should be 
> moved out to the kernel config file, and better documented.
> 
> --------------------------------------------------------------------------
> Jason R. Thorpe                                       thorpej@nas.nasa.gov

OK, here is a version of the patch that lets you turn off the
work-aound for this bug.  Note that BPF will now work even with
the work-around enabled, because the dest. address check was moved
later.  BPF will get the bad packets if they come in, but the rest
of the network stack will not see them.

Therefore, the only remaining reason to turn off the LANCE_REVC_BUG
work-around is if you want to use multicast.  (New patch below.)

Gordon Ross



*** if_le.c.~12~	Wed Jun 28 03:11:11 1995
--- if_le.c	Thu Oct 26 15:53:02 1995
***************
*** 51,58 ****
  #include <machine/autoconf.h>
  #include <machine/cpu.h>
  
! /* XXX - Yes, we DO have to deal with this bug. */
  #define	LANCE_REVC_BUG 1
  
  /* #define	LEDEBUG	1 */
  
--- 51,67 ----
  #include <machine/autoconf.h>
  #include <machine/cpu.h>
  
! /*
!  * XXX - Be warned: Most Sun3/50 and many Sun3/60 machines have
!  * the LANCE Rev. C bug, which we MUST avoid or suffer likely
!  * NFS file corruption and worse!  That said, if you are SURE
!  * your LANCE is OK, you can remove this work-around using:
!  *  	options LANCE_REVC_BUG=0
!  * in your kernel config file.
!  */
! #ifndef	LANCE_REVC_BUG
  #define	LANCE_REVC_BUG 1
+ #endif
  
  /* #define	LEDEBUG	1 */
  
***************
*** 168,175 ****
  	ifp->if_watchdog = lewatchdog;
  	ifp->if_flags =
  	    IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS;
! #ifndef	LANCE_REVC_BUG
! 	/* XXX - Must be a better way... */
  	ifp->if_flags |= IFF_MULTICAST;
  #endif
  
--- 177,185 ----
  	ifp->if_watchdog = lewatchdog;
  	ifp->if_flags =
  	    IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS;
! 
! #if LANCE_REVC_BUG == 0
! 	/* The work-around precludes multicast... */
  	ifp->if_flags |= IFF_MULTICAST;
  #endif
  
***************
*** 176,181 ****
--- 186,192 ----
  	/* Attach the interface. */
  	if_attach(ifp);
  	ether_ifattach(ifp);
+ 
  #if NBPFILTER > 0
  	bpfattach(&ifp->if_bpf, ifp, DLT_EN10MB, sizeof(struct ether_header));
  #endif
***************
*** 631,637 ****
  #endif
  			leread(sc, sc->sc_rbuf + (BUFSIZE * rmd),
  			    (int)cdm->mcnt);
- 			sc->sc_if.if_ipackets++;
  		}
  
  		cdm->bcnt = -BUFSIZE;
--- 642,647 ----
***************
*** 661,703 ****
  	struct mbuf *m;
  	struct ether_header *eh;
  
! 	len -= 4;
! 	if (len <= 0)
! 		return;
  
! #ifdef	LANCE_REVC_BUG	/* XXX - Must be a better way... */
! 	/*
! 	 * Check for unreported packet errors.  Rev C of the LANCE chip
! 	 * has a bug which can cause "random" bytes to be prepended to
! 	 * the start of the packet.  The work-around is to make sure that
! 	 * the Ethernet destination address in the packet matches our
! 	 * address (or the broadcast address).
! 	 */
! 	{
! 		register short *pp, *ea;
! 
! 		pp = (short *) buf;
! 		ea = (short *) &sc->sc_enaddr;
! 		if ((pp[0] == ea[0]) && (pp[1] == ea[1]) && (pp[2] == ea[2]))
! 			goto ok;
! 		if ((pp[0] == -1) && (pp[1] == -1) && (pp[2] == -1))
! 			goto ok;
! 		/* XXX - Multicast packets? */
! 
! 		sc->sc_if.if_ierrors++;
! 		log(LOG_ERR, "%s: LANCE Rev C Extra Byte(s) bug; Packet punted\n",
! 			   sc->sc_dev.dv_xname);
  		return;
- 	ok:
  	}
- #endif	/* LANCE_REVC_BUG */
  
  	/* Pull packet off interface. */
- 	ifp = &sc->sc_if;
  	m = leget(buf, len, ifp);
! 	if (m == 0)
  		return;
  
  	/* We assume that the header fit entirely in one mbuf. */
  	eh = mtod(m, struct ether_header *);
  
--- 671,695 ----
  	struct mbuf *m;
  	struct ether_header *eh;
  
! 	ifp = &sc->sc_if;
  
! 	if (len <= sizeof(struct ether_header) ||
! 	    len > ETHERMTU + sizeof(struct ether_header)) {
! 		printf("%s: invalid packet size %d; dropping\n",
! 		    sc->sc_dev.dv_xname, len);	
! 		ifp->if_ierrors++;
  		return;
  	}
  
  	/* Pull packet off interface. */
  	m = leget(buf, len, ifp);
! 	if (m == 0) {
! 		ifp->if_ierrors++;
  		return;
+ 	}
  
+ 	ifp->if_ipackets++;
+ 
  	/* We assume that the header fit entirely in one mbuf. */
  	eh = mtod(m, struct ether_header *);
  
***************
*** 707,714 ****
--- 699,727 ----
  	 * If so, hand off the raw packet to BPF.
  	 */
  	if (ifp->if_bpf) {
+ 		/* Note that BPF may see garbage! (LANCE_REVC_BUG) */
  		bpf_mtap(ifp->if_bpf, m);
+ 	}
+ #endif	/* NBPFILTER */
  
+ #if LANCE_REVC_BUG
+ 	/*
+ 	 * Check for unreported packet errors.  Rev C of the LANCE chip
+ 	 * has a bug which can cause "random" bytes to be prepended to
+ 	 * the start of the packet.  The work-around is to make sure that
+ 	 * the Ethernet destination address in the packet matches our
+ 	 * address (or the broadcast address).  Must ALWAYS check!
+ 	 */
+ 	if (bcmp(eh->ether_dhost, sc->sc_enaddr, 6) &&
+ 		bcmp(eh->ether_dhost, etherbroadcastaddr, 6))
+ 	{
+ 		/* Not for us. */
+ 		m_freem(m);
+ 		return;
+ 	}
+ #else	/* LANCE_REVC_BUG */
+ #if NBPFILTER > 0
+ 	if (ifp->if_bpf) {
  		/*
  		 * Note that the interface cannot be in promiscuous mode if
  		 * there are no BPF listeners.  And if we are in promiscuous
***************
*** 716,734 ****
  		 */
  		if ((ifp->if_flags & IFF_PROMISC) &&
  		    (eh->ether_dhost[0] & 1) == 0 && /* !mcast and !bcast */
! 		    bcmp(eh->ether_dhost, sc->sc_enaddr,
! 			    sizeof(eh->ether_dhost)) != 0) {
  			m_freem(m);
  			return;
  		}
  	}
! #endif
  
! 	/* We assume that the header fit entirely in one mbuf. */
! 	m->m_pkthdr.len -= sizeof(*eh);
! 	m->m_len -= sizeof(*eh);
! 	m->m_data += sizeof(*eh);
! 
  	ether_input(ifp, eh, m);
  }
  
--- 729,745 ----
  		 */
  		if ((ifp->if_flags & IFF_PROMISC) &&
  		    (eh->ether_dhost[0] & 1) == 0 && /* !mcast and !bcast */
! 		    bcmp(eh->ether_dhost, sc->sc_enaddr, 6) != 0)
! 		{
  			m_freem(m);
  			return;
  		}
  	}
! #endif	/* NBPFILTER */
! #endif	/* LANCE_REVC_BUG */
  
! 	/* Pass the packet up, with the ether header sort-of removed. */
! 	m_adj(m, sizeof(struct ether_header));
  	ether_input(ifp, eh, m);
  }