Subject: Lance Rev C ether bug
To: None <D.K.Brownlee@city.ac.uk, gilbertd@cs.man.ac.uk>
From: Gordon W. Ross <gwr@mc.com>
List: port-sun3
Date: 10/27/1995 10:51:32
> Date: Thu, 26 Oct 1995 21:38:18 +0000 (GMT)
> From: David Brownlee <D.K.Brownlee@city.ac.uk>
> 
> If -current as of yesterday is ok...
> 
> I've put a GENERIC kernel up with the LANCE_REVC_BUG undef'd on
> ftp://ftp.city.ac.uk/incoming/netbsd/netbsd_sun3_lance.gz

OK, but be warned this bug can cause NFS file corruption.
I recommend the patch below instead.  (This one works! 8^)

> (I'd just compiled up a normal generic kernel and made a binary
> snapshot from yesterdays sup for my own use :)

Thanks.  I'm still waiting for access to the NetBSD FTP server...

> While I'm at it I'll put yesterdays snapshot there as well if you 
> want the entire system!
> 	
> 	David/abs
> 
> david@city.ac.uk +44 171 477 8186 (MIME) david@southern.com +44 0181 88 8949
> Network Analyst, UCS, City University   System Manager, Southern Studios Ltd
> Northampton Square, London EC1V 0HB                PO Box 59, London N22 1AR


Here is a patch for the "le" driver.
This one has been tested too!
(Sorry about the previous two...)

Gordon Ross

*** if_le.c.~12~	Wed Jun 28 03:11:11 1995
--- if_le.c	Thu Oct 26 23:01:17 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,694 ----
  	struct mbuf *m;
  	struct ether_header *eh;
  
! 	ifp = &sc->sc_if;
  
! 	if ((len < ETHERMIN) || (len > ETHER_MAX_LEN)) {
! 		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 ****
--- 698,726 ----
  	 * 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);
  }
  
--- 728,744 ----
  		 */
  		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);
  }