From nobody@FreeBSD.ORG Mon Mar 29 11:26:45 1999
Return-Path: <nobody@FreeBSD.ORG>
Received: by hub.freebsd.org (Postfix, from userid 32767)
	id 8552714BD5; Mon, 29 Mar 1999 11:26:45 -0800 (PST)
Message-Id: <19990329192645.8552714BD5@hub.freebsd.org>
Date: Mon, 29 Mar 1999 11:26:45 -0800 (PST)
From: cmsedore@maxwell.syr.edu
Sender: nobody@FreeBSD.ORG
To: freebsd-gnats-submit@freebsd.org
Subject: bpf overwrites source ethernet addresses when writing to a bpf descriptor
X-Send-Pr-Version: www-1.0

>Number:         10860
>Category:       kern
>Synopsis:       bpf overwrites source ethernet addresses when writing to a bpf descriptor
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-bugs
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Mon Mar 29 11:30:00 PST 1999
>Closed-Date:    Thu Oct 14 22:07:52 PDT 1999
>Last-Modified:  Thu Oct 14 22:08:26 PDT 1999
>Originator:     Chris Sedore
>Release:        3.1-RELEASE
>Organization:
>Environment:
FreeBSD fleegin.maxwell.syr.edu 3.1-RELEASE FreeBSD 3.1-RELEASE #10: Fri Mar 26 19:24:58 GMT 1999
>Description:
Writes to BPF descriptors bound to ethernet interfaces silently overwrite the source ethernet address field in packets.  BPF should reproduce the packets on the wire exactly as they are passed into write().  
>How-To-Repeat:
Write a packet to a bpf descriptor and observe the ethernet address with tcpdump or a similar tool
>Fix:
bpf routines need to use a different address family (or another methodology) to communicate with ether_output so ether_output doesn't mess with the packet.

My solution is attached.  Basically, rather than setting the dst->sa_family to AF_UNSPEC in the bpfwrite call, I set it to AF_LINK instead.  I added glue to if_ethersubr.c to handle AF_LINK, implying that this usage would not conflict with anything else since it wasn't handled previously.  

It works as expected in my testing.

-Chris

diff -c -r sys/net/bpf.c /usr/src/sys/net/bpf.c
*** sys/net/bpf.c       Mon Dec  7 21:58:36 1998
--- /usr/src/sys/net/bpf.c      Fri Mar 26 19:52:37 1999
***************
*** 177,185 ****
                break;

        case DLT_EN10MB:
!               sockp->sa_family = AF_UNSPEC;
!               /* XXX Would MAXLINKHDR be better? */
!               hlen = sizeof(struct ether_header);
                break;

        case DLT_FDDI:
--- 177,184 ----
                break;

        case DLT_EN10MB:
!               sockp->sa_family = AF_LINK;
!               hlen = 0;
                break;

        case DLT_FDDI:
diff -c -r sys/net/if_ethersubr.c /usr/src/sys/net/if_ethersubr.c
*** sys/net/if_ethersubr.c      Tue Jan 12 12:07:00 1999
--- /usr/src/sys/net/if_ethersubr.c     Fri Mar 26 18:58:58 1999
***************
*** 336,360 ****
                type = eh->ether_type;
                break;

        default:
                printf("%s%d: can't handle af%d\n", ifp->if_name, ifp->if_unit,
                        dst->sa_family);
                senderr(EAFNOSUPPORT);
        }

!       /*
!        * Add local net header.  If no space in first mbuf,
!        * allocate another.
!        */
!       M_PREPEND(m, sizeof (struct ether_header), M_DONTWAIT);
!       if (m == 0)
!               senderr(ENOBUFS);
!       eh = mtod(m, struct ether_header *);
!       (void)memcpy(&eh->ether_type, &type,
!               sizeof(eh->ether_type));
!       (void)memcpy(eh->ether_dhost, edst, sizeof (edst));
!       (void)memcpy(eh->ether_shost, ac->ac_enaddr,
!           sizeof(eh->ether_shost));

        /*
         * If a simplex interface, and the packet is being sent to our
--- 336,369 ----
                type = eh->ether_type;
                break;

+       case AF_LINK:
+               loop_copy = -1;
+               break;
+
        default:
                printf("%s%d: can't handle af%d\n", ifp->if_name, ifp->if_unit,
                        dst->sa_family);
                senderr(EAFNOSUPPORT);
        }

!       if (dst->sa_family!=AF_LINK) {
!
!               /*
!                * Add local net header.  If no space in first mbuf,
!                * allocate another.
!                */
!               M_PREPEND(m, sizeof (struct ether_header), M_DONTWAIT);
!               if (m == 0)
!                       senderr(ENOBUFS);
!               eh = mtod(m, struct ether_header *);
!               (void)memcpy(&eh->ether_type, &type,
!                       sizeof(eh->ether_type));
!               (void)memcpy(eh->ether_dhost, edst, sizeof (edst));
!               (void)memcpy(eh->ether_shost, ac->ac_enaddr,
!                   sizeof(eh->ether_shost));
!       } else {
!               eh = mtod(m, struct ether_header *);
!       }

        /*
         * If a simplex interface, and the packet is being sent to our

>Release-Note:
>Audit-Trail:

From: "Christopher N . Harrell" <cnh@mindspring.net>
To: freebsd-gnats-submit@freebsd.org
Cc: imp@village.org, dima@rdy.com, security-officer@freebsd.org,
	pst@juniper.net, cmsedore@maxwell.syr.edu
Subject: Re: kern/10860: bpf overwrites source ethernet addresses when writing to a bpf descriptor
Date: Tue, 5 Oct 1999 05:04:55 -0400

 I just ported the netbsd solution and added a manpage update.  This solution
 works, and is already a bsd-ish standard.
 
 cheers,
 Christopher
 
 
 *** /usr/src/sys/net/bpf.c	Sat Sep 25 14:24:29 1999
 --- /usr/modsrc/net/bpf.c	Tue Oct  5 03:06:24 1999
 ***************
 *** 579,584 ****
 --- 579,587 ----
   	if (datlen > ifp->if_mtu)
   		return (EMSGSIZE);
   
 + 	if (d->bd_hdrcmplt)
 + 		dst.sa_family = pseudo_AF_HDRCMPLT;
 + 
   	s = splnet();
   #if BSD >= 199103
   	error = (*ifp->if_output)(ifp, m, &dst, (struct rtentry *)0);
 ***************
 *** 626,631 ****
 --- 629,636 ----
    *  BIOCGSTATS		Get packet stats.
    *  BIOCIMMEDIATE	Set immediate mode.
    *  BIOCVERSION		Get filter language version.
 +  *  BIOCGHDRCMPLT	Get "header already complete" flag
 +  *  BIOCSHDRCMPLT	Set "header already complete" flag
    */
   /* ARGSUSED */
   static	int
 ***************
 *** 821,826 ****
 --- 826,845 ----
   			bv->bv_minor = BPF_MINOR_VERSION;
   			break;
   		}
 + 
 + 	/*
 + 	 * Get "header already complete" flag
 + 	 */
 + 	case BIOCGHDRCMPLT:
 + 		*(u_int *)addr = d->bd_hdrcmplt;
 + 		break;
 + 
 + 	/*
 + 	 * Set "header already complete" flag
 + 	 */
 + 	case BIOCSHDRCMPLT:
 + 		d->bd_hdrcmplt = *(u_int *)addr ? 1 : 0;
 + 		break;
   
   	case FIONBIO:		/* Non-blocking I/O */
   		break;
 *** /usr/src/sys/net/bpf.h	Fri Aug 27 20:48:13 1999
 --- /usr/modsrc/net/bpf.h	Tue Oct  5 03:44:32 1999
 ***************
 *** 111,116 ****
 --- 111,118 ----
   #define BIOCVERSION	_IOR('B',113, struct bpf_version)
   #define BIOCGRSIG	_IOR('B',114, u_int)
   #define BIOCSRSIG	_IOW('B',115, u_int)
 + #define BIOCGHDRCMPLT	_IOR('B',116, u_int)
 + #define BIOCSHDRCMPLT	_IOW('B',117, u_int)
   
   /*
    * Structure prepended to each packet.
 *** /usr/src/sys/net/bpfdesc.h	Fri Aug 27 20:48:14 1999
 --- /usr/modsrc/net/bpfdesc.h	Tue Oct  5 03:02:44 1999
 ***************
 *** 76,81 ****
 --- 76,82 ----
   	u_char		bd_promisc;	/* true if listening promiscuously */
   	u_char		bd_state;	/* idle, waiting, or timed out */
   	u_char		bd_immediate;	/* true to return on packet arrival */
 + 	int		bd_hdrcmplt;	/* false to fill in src lladdr automatically */
   	int		bd_async;	/* non-zero if packet reception should generate signal */
   	int		bd_sig;		/* signal to send upon packet reception */
   	struct sigio *	bd_sigio;	/* information for async I/O */
 *** /usr/src/sys/net/if_ethersubr.c	Fri Aug 27 20:48:17 1999
 --- /usr/modsrc/net/if_ethersubr.c	Tue Oct  5 02:53:04 1999
 ***************
 *** 132,139 ****
   	struct rtentry *rt0;
   {
   	short type;
 ! 	int s, error = 0;
 !  	u_char edst[6];
   	register struct mbuf *m = m0;
   	register struct rtentry *rt;
   	register struct ether_header *eh;
 --- 132,139 ----
   	struct rtentry *rt0;
   {
   	short type;
 ! 	int s, error = 0, hdrcmplt = 0;
 !  	u_char esrc[6], edst[6];
   	register struct mbuf *m = m0;
   	register struct rtentry *rt;
   	register struct ether_header *eh;
 ***************
 *** 326,331 ****
 --- 326,337 ----
   		} break;
   #endif /* LLC */
   
 + 	case pseudo_AF_HDRCMPLT:
 + 		hdrcmplt = 1;
 + 		eh = (struct ether_header *)dst->sa_data;
 + 		(void)memcpy(esrc, eh->ether_shost, sizeof (esrc));
 + 		/* FALLTHROUGH */
 + 
   	case AF_UNSPEC:
   		loop_copy = -1; /* if this is for us, don't do it */
   		eh = (struct ether_header *)dst->sa_data;
 ***************
 *** 333,338 ****
 --- 339,345 ----
   		type = eh->ether_type;
   		break;
   
 + 
   	default:
   		printf("%s%d: can't handle af%d\n", ifp->if_name, ifp->if_unit,
   			dst->sa_family);
 ***************
 *** 350,357 ****
   	(void)memcpy(&eh->ether_type, &type,
   		sizeof(eh->ether_type));
    	(void)memcpy(eh->ether_dhost, edst, sizeof (edst));
 !  	(void)memcpy(eh->ether_shost, ac->ac_enaddr,
 ! 	    sizeof(eh->ether_shost));
   
   	/*
   	 * If a simplex interface, and the packet is being sent to our
 --- 357,368 ----
   	(void)memcpy(&eh->ether_type, &type,
   		sizeof(eh->ether_type));
    	(void)memcpy(eh->ether_dhost, edst, sizeof (edst));
 ! 	if (hdrcmplt)
 ! 		(void)memcpy(eh->ether_shost, esrc,
 ! 			sizeof(eh->ether_shost));
 ! 	else
 ! 		(void)memcpy(eh->ether_shost, ac->ac_enaddr,
 ! 			sizeof(eh->ether_shost));
   
   	/*
   	 * If a simplex interface, and the packet is being sent to our
 *** /usr/src/sys/net/if_fddisubr.c	Sat Sep 25 08:05:56 1999
 --- /usr/modsrc/net/if_fddisubr.c	Tue Oct  5 03:22:39 1999
 ***************
 *** 136,143 ****
   	struct rtentry *rt0;
   {
   	u_int16_t type;
 ! 	int s, loop_copy = 0, error = 0;
 !  	u_char edst[6];
   	register struct mbuf *m = m0;
   	register struct rtentry *rt;
   	register struct fddi_header *fh;
 --- 136,143 ----
   	struct rtentry *rt0;
   {
   	u_int16_t type;
 ! 	int s, loop_copy = 0, error = 0, hdrcmplt = 0;
 !  	u_char esrc[6], edst[6];
   	register struct mbuf *m = m0;
   	register struct rtentry *rt;
   	register struct fddi_header *fh;
 ***************
 *** 295,300 ****
 --- 295,309 ----
   		} break;
   #endif /* LLC */	
   
 + 	case pseudo_AF_HDRCMPLT:
 + 	{
 + 		struct ether_header *eh;
 + 		hdrcmplt = 1;
 + 		eh = (struct ether_header *)dst->sa_data;
 +  		(void)memcpy((caddr_t)esrc, (caddr_t)eh->ether_shost, sizeof (esrc));
 + 		/* FALLTHROUGH */
 + 	}
 + 
   	case AF_UNSPEC:
   	{
   		struct ether_header *eh;
 ***************
 *** 370,378 ****
   	fh->fddi_fc = FDDIFC_LLC_ASYNC|FDDIFC_LLC_PRIO4;
    	(void)memcpy((caddr_t)fh->fddi_dhost, (caddr_t)edst, sizeof (edst));
     queue_it:
 !  	(void)memcpy((caddr_t)fh->fddi_shost, (caddr_t)ac->ac_enaddr,
 ! 	    sizeof(fh->fddi_shost));
 ! 
   	/*
   	 * If a simplex interface, and the packet is being sent to our
   	 * Ethernet address or a broadcast address, loopback a copy.
 --- 379,390 ----
   	fh->fddi_fc = FDDIFC_LLC_ASYNC|FDDIFC_LLC_PRIO4;
    	(void)memcpy((caddr_t)fh->fddi_dhost, (caddr_t)edst, sizeof (edst));
     queue_it:
 ! 	if (hdrcmplt)
 ! 		(void)memcpy((caddr_t)fh->fddi_shost, (caddr_t)esrc,
 ! 			sizeof(fh->fddi_shost));
 ! 	else
 ! 		(void)memcpy((caddr_t)fh->fddi_shost, (caddr_t)ac->ac_enaddr,
 ! 			sizeof(fh->fddi_shost));
   	/*
   	 * If a simplex interface, and the packet is being sent to our
   	 * Ethernet address or a broadcast address, loopback a copy.
 *** /usr/src/sys/sys/mount.h	Wed Sep 29 16:05:30 1999
 --- /usr/modsrc/sys/mount.h	Tue Oct  5 03:29:56 1999
 ***************
 *** 355,361 ****
   
   #include <net/radix.h>
   
 ! #define	AF_MAX		31	/* XXX */
   
   /*
    * Network address lookup element
 --- 355,361 ----
   
   #include <net/radix.h>
   
 ! #define	AF_MAX		32	/* XXX */
   
   /*
    * Network address lookup element
 *** /usr/src/sys/sys/socket.h	Fri Aug 27 20:52:01 1999
 --- /usr/modsrc/sys/socket.h	Tue Oct  5 03:15:55 1999
 ***************
 *** 128,135 ****
   #define	AF_INET6	28		/* IPv6 */
   #define	AF_NATM		29		/* native ATM access */
   #define	AF_ATM		30		/* ATM */
   
 ! #define	AF_MAX		31
   
   /*
    * Structure used by kernel to store most
 --- 128,138 ----
   #define	AF_INET6	28		/* IPv6 */
   #define	AF_NATM		29		/* native ATM access */
   #define	AF_ATM		30		/* ATM */
 + #define pseudo_AF_HDRCMPLT 31		/* Used by BPF to not rewrite headers
 + 					 * in interface output routine
 + 					 */
   
 ! #define	AF_MAX		32
   
   /*
    * Structure used by kernel to store most
 *** /usr/src/share/man/man4/bpf.4	Fri Aug 27 20:19:46 1999
 --- share/man/man4/bpf.4	Tue Oct  5 04:21:42 1999
 ***************
 *** 276,281 ****
 --- 276,289 ----
   may result in undefined behavior (most likely, an error returned by
   .Fn ioctl
   or haphazard packet matching).
 + .It Dv BIOCSHDRCMPLT
 + .It Dv BIOCGHDRCMPLT
 + .Pq Li u_int
 + Set or get the status of the ``header complete'' flag.
 + Set to zero if the link level source address should be filled in automatically
 + by the the interface output routine.  Set to one if the link level source
 + address will be written, as provided, to the wire.  This flag is initialized
 + to zero by default.
   .Sh BPF HEADER
   The following structure is prepended to each packet returned by
   .Xr read 2 :
 

From: Warner Losh <imp@village.org>
To: "Christopher N . Harrell" <cnh@mindspring.net>
Cc: freebsd-gnats-submit@freebsd.org, dima@rdy.com,
	security-officer@freebsd.org, pst@juniper.net,
	cmsedore@maxwell.syr.edu
Subject: Re: kern/10860: bpf overwrites source ethernet addresses when writing to a bpf descriptor 
Date: Wed, 06 Oct 1999 10:24:58 -0600

 In message <19991005050455.A2780@foo.ems.mindspring.net> "Christopher N . Harrell" writes:
 : I just ported the netbsd solution and added a manpage update.  This solution
 : works, and is already a bsd-ish standard.
 
 This looks OK to me.  The security implications are something that I'm
 not sure of.  However given that it can be done in a kld, I think that
 this is ok.
 
 Warner
 

From: "Christopher N . Harrell" <cnh@mindspring.net>
To: freebsd-gnats-submit@freebsd.org
Cc:  
Subject: Re: kern/10860: bpf overwrites source ethernet addresses when writing to a bpf descriptor
Date: Wed, 6 Oct 1999 20:30:21 -0400

 There's a superfluous newline in that patch near "default:"... sorry about that.
 
 cheers,
 Christopher
 
State-Changed-From-To: open->closed 
State-Changed-By: msmith 
State-Changed-When: Thu Oct 14 22:07:52 PDT 1999 
State-Changed-Why:  
Supplied patch verified and committed. 
>Unformatted:
