Subject: Take #3 - final proposed patch for ipsec/bpf/ipfilter integration
To: None <tech-net@netbsd.org>
From: Darren Reed <avalon@caligula.anu.edu.au>
List: tech-net
Date: 05/11/2003 17:36:17
Hi All,

At this pass on the ipsec/tcpdump/ipfilter problem, I think I'm
approaching something very close to 100% working.  I'd really
like some feedback from someone at KAME on the changes below as
well as from people who can test it or just review the patches.
I've also not yet tested IPv6, so if someone could give that a
go, it'd be most welcome.

There is one major outstanding problem: IP header checksums are
incorrect for inbound packets when picked up for transport mode
IPSEC processing.  I suppose one solution is just to recalculate
it there and then as required but is that the only solution ?

I'd really like to get this patch out of the way and committed
within a week's time.

Anyone who has been busting to do firewalling (or NAT) on packets
before or after they go through an IPSec tunnel should give this
patch a go.

Oh, included in the patch is a new BPF routine - bpf_nullmtap().
When this set is committed, I plan to hunt around and covert all
of the "inline" versions of that to just call this function,
which should be able to provide better BPF performance for some
number of DLT_NULL packets.

Cheers,
Darren

# ipf -E
# ifconfig ipsec_pcn0 up
 ipf -f -
pass out log from any to 192.168.1.254
pass in log from 192.168.1.254 to any
^D
# tcpdump -nvi ipsec_pcn0 &
tcpdump: WARNING: ipsec_pcn0: no IPv4 address assigned
tcpdump: listening on ipsec_pcn0

# ping -c 1 192.168.1.254
PING firewall.reed.wattle.id.au (192.168.1.254): 56 data bytes
64 bytes from 192.168.1.254: icmp_seq=0 ttl=255 time=3.891 ms

----firewall.reed.wattle.id.au PING Statistics----
1 packets transmitted, 1 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 3.891/3.891/3.891/0.000 ms

========== TRANSPORT MODE IPSEC OUTPUT ==========
04:36:11.061455 192.168.1.187 > 192.168.1.254: icmp: echo request seq 0 (ttl 255, id 1187, len 84, bad cksum 0 (->ffff03cf)!)
04:36:11.063675 192.168.1.254 > 192.168.1.187: icmp: echo reply seq 0 (ttl 255, id 8770, len 84, bad cksum 1408 (->ffffbf08)!)

# ipmon
12/05/2003 04:36:11.061959 ipsec_pcn0 @0:1 p 192.168.1.187 -> 192.168.1.254 PR icmp len 20 84 icmp echo/0 OUT
12/05/2003 04:36:11.062492 pcn0 @0:1 p 192.168.1.187 -> 192.168.1.254 PR esp len 20 (120) OUT
12/05/2003 04:36:11.063329 pcn0 @0:1 p 192.168.1.254 -> 192.168.1.187 PR esp len 20 (120) IN
12/05/2003 04:36:11.063803 ipsec_pcn0 @0:1 p 192.168.1.254 -> 192.168.1.187 PR icmp len 20 84 icmp echoreply/0 IN

========== TUNNEL MODE IPSEC OUTPUT ==========
04:39:14.735927 192.168.1.187 > 192.168.1.254: icmp: echo request seq 0 (ttl 255, id 2401, len 84)
04:39:14.739423 192.168.1.254 > 192.168.1.187: icmp: echo reply seq 0 (ttl 255, id 9046, len 84)

# ipmon
12/05/2003 04:39:14.736142 ipsec_pcn0 @0:1 p 192.168.1.187 -> 192.168.1.254 PR icmp len 20 84 icmp echo/0 OUT
12/05/2003 04:39:14.737429 pcn0 @0:1 p 192.168.1.187 -> 192.168.1.254 PR esp len 20 (136) OUT
12/05/2003 04:39:14.738377 pcn0 @0:1 p 192.168.1.254 -> 192.168.1.187 PR esp len 20 (136) IN
12/05/2003 04:39:14.739629 ipsec_pcn0 @0:1 p 192.168.1.254 -> 192.168.1.187 PR icmp len 20 84 icmp echoreply/0 IN

Index: sys/net/bpf.c
===================================================================
RCS file: /cvsroot/src/sys/net/bpf.c,v
retrieving revision 1.78
diff -c -r1.78 bpf.c
*** sys/net/bpf.c	2003/03/13 10:18:35	1.78
--- sys/net/bpf.c	2003/05/11 07:17:19
***************
*** 93,98 ****
--- 93,99 ----
   * The default read buffer size is patchable.
   */
  int bpf_bufsize = BPF_BUFSIZE;
+ u_long bpf_nullfitted[2] = {0,0};
  
  /*
   *  bpf_iflist is the list of interfaces; each corresponds to an ifnet
***************
*** 1198,1203 ****
--- 1199,1250 ----
  			catchpacket(d, (u_char *)m, pktlen, slen, bpf_mcpy);
  	}
  }
+ 
+ /*
+  * Incoming linkage from DLT_NULL device drivers.
+  */
+ void
+ bpf_nullmtap(arg, m, family)
+ 	caddr_t arg;
+ 	struct mbuf *m;
+ 	sa_family_t family;
+ {
+ 	u_int32_t mfamily = family;
+ 	struct mbuf m0;
+ 
+ 	/*
+ 	 * We need to prepend the address family as a four byte field.
+ 	 * Cons up a dummy header to pacify bpf.  This is safe because bpf
+ 	 * will only read from the mbuf (i.e., it won't try to free it or
+ 	 * keep a pointer to it).  If possible, prepend the family before
+ 	 * the header, in the same mbuf, for better BPF performance.
+ 	 */
+ 
+ 	if (M_LEADINGSPACE(m) >= sizeof(sa_family_t)) {
+ 		bpf_nullfitted[0]++;
+ 		m->m_data -= sizeof(mfamily);
+ 		bcopy(&family, mtod(m, char *), sizeof(mfamily));
+ 		m->m_len += sizeof(mfamily);
+ 		m->m_pkthdr.len += sizeof(mfamily);
+ 
+ 		bpf_mtap(arg, m);
+ 
+ 		m->m_pkthdr.len -= sizeof(mfamily);
+ 		m->m_len -= sizeof(mfamily);
+ 		m->m_data += sizeof(mfamily);
+ 	} else {
+ 		bpf_nullfitted[1]++;
+ 		M_COPY_PKTHDR(&m0, m);
+ 		m0.m_next = m;
+ 		m0.m_len = sizeof(mfamily);
+ 		m0.m_data = (char *)&mfamily;
+ 		m0.m_pkthdr.len += m0.m_len;
+ 
+ 		bpf_mtap(arg, &m0);
+ 	}
+ 
+ }
+ 
  
  /*
   * Move the packet data from interface memory (pkt) into the
Index: sys/net/bpf.h
===================================================================
RCS file: /cvsroot/src/sys/net/bpf.h,v
retrieving revision 1.31
diff -c -r1.31 bpf.h
*** sys/net/bpf.h	2002/09/21 17:02:46	1.31
--- sys/net/bpf.h	2003/05/11 07:17:20
***************
*** 240,245 ****
--- 240,246 ----
  int	 bpf_validate __P((struct bpf_insn *, int));
  void	 bpf_tap __P((caddr_t, u_char *, u_int));
  void	 bpf_mtap __P((caddr_t, struct mbuf *));
+ void	 bpf_nullmtap __P((caddr_t, struct mbuf *, sa_family_t));
  void	 bpfattach __P((struct ifnet *, u_int, u_int));
  void	 bpfattach2 __P((struct ifnet *, u_int, u_int, caddr_t *));
  void	 bpfdetach __P((struct ifnet *));
Index: sys/net/bpf_filter.c
===================================================================
RCS file: /cvsroot/src/sys/net/bpf_filter.c,v
retrieving revision 1.19
diff -c -r1.19 bpf_filter.c
*** sys/net/bpf_filter.c	2001/11/15 09:48:25	1.19
--- sys/net/bpf_filter.c	2003/05/11 07:17:20
***************
*** 52,57 ****
--- 52,58 ----
  
  #include <sys/param.h>
  #include <sys/time.h>
+ #include <sys/socket.h>
  
  #if !defined(UNALIGNED_ACCESS)
  #define BPF_ALIGN
Index: sys/net/if.c
===================================================================
RCS file: /cvsroot/src/sys/net/if.c,v
retrieving revision 1.117
diff -c -r1.117 if.c
*** sys/net/if.c	2003/02/01 06:23:46	1.117
--- sys/net/if.c	2003/05/11 07:17:24
***************
*** 109,115 ****
--- 109,117 ----
  #include "opt_compat_svr4.h"
  #include "opt_compat_43.h"
  #include "opt_atalk.h"
+ #include "opt_ipsec.h"
  #include "opt_pfil_hooks.h"
+ #include "bpfilter.h"
  
  #include <sys/param.h>
  #include <sys/mbuf.h>
***************
*** 142,147 ****
--- 144,155 ----
  #include <netinet6/nd6.h>
  #endif
  
+ #if NBPFILTER > 0
+ #include <net/bpf.h>
+ #endif 
+ 
+ 
+ MALLOC_DEFINE(M_IFNET, "ifnet", "interfaces");
  MALLOC_DEFINE(M_IFADDR, "ifaddr", "interface address");
  MALLOC_DEFINE(M_IFMADDR, "ether_multi", "link-level multicast address");
  
***************
*** 154,159 ****
--- 162,170 ----
  
  struct if_clone *if_clone_lookup __P((const char *, int *));
  int if_clone_list __P((struct if_clonereq *));
+ #ifdef IPSEC
+ void if_attachipsec __P((struct ifnet *));
+ #endif
  
  LIST_HEAD(, if_clone) if_cloners = LIST_HEAD_INITIALIZER(if_cloners);
  int if_cloners_count;
***************
*** 465,471 ****
--- 476,517 ----
  
  	/* Announce the interface. */
  	rt_ifannouncemsg(ifp, IFAN_ARRIVAL);
+ 
+ #ifdef IPSEC
+ 	if_attachipsec(ifp);
+ #endif
+ }
+ 
+ 
+ #ifdef IPSEC
+ void
+ if_attachipsec(struct ifnet *pifp)
+ {
+ 	struct ifnet *ifp;
+ 
+ 	ifp = (struct ifnet *)malloc(sizeof(*ifp), M_IFNET, M_WAITOK);
+ 	bzero((char *)ifp, sizeof(*ifp));
+ 	snprintf(ifp->if_xname, sizeof(ifp->if_xname),
+ 		 "ipsec_%s", pifp->if_xname);
+ 
+ 	TAILQ_INIT(&ifp->if_addrlist);
+ 	ifp->if_link_state = LINK_STATE_UNKNOWN;
+ 	if_deactivate(ifp);
+ 
+ # ifdef PFIL_HOOKS
+ 	ifp->if_pfil.ph_type = PFIL_TYPE_IFNET;
+ 	ifp->if_pfil.ph_ifnet = ifp;
+ 	if (pfil_head_register(&ifp->if_pfil) != 0)
+ 		printf("%s: WARNING: unable to register pfil hook\n",
+ 		    ifp->if_xname);
+ # endif
+ 
+ # if NBPFILTER > 0
+ 	bpfattach(ifp, DLT_NULL, sizeof(sa_family_t));
+ # endif
+ 	pifp->if_ipsec = ifp;
  }
+ #endif
  
  void
  if_attachdomain()
***************
*** 570,575 ****
--- 616,631 ----
  #ifdef PFIL_HOOKS
  	(void) pfil_head_unregister(&ifp->if_pfil);
  #endif
+ #ifdef IPSEC
+ #ifdef PFIL_HOOKS
+ 	(void) pfil_head_unregister(&ifp->if_ipsec->if_pfil);
+ #endif
+ #if BPFILTER > 0
+ 	bpfdetach(ifp->if_ipsec);
+ #endif
+ 	free(ifp->if_ipsec, M_IFNET);
+ 	ifp->if_ipsec = NULL;
+ #endif
  
  	if_free_sadl(ifp);
  
***************
*** 608,614 ****
  				if (pr->pr_usrreq != NULL) {
  					(void) (*pr->pr_usrreq)(&so,
  					    PRU_PURGEIF, NULL, NULL,
! 					    (struct mbuf *) ifp, curproc);
  					purged = 1;
  				}
  			}
--- 664,670 ----
  				if (pr->pr_usrreq != NULL) {
  					(void) (*pr->pr_usrreq)(&so,
  					    PRU_PURGEIF, NULL, NULL,
! 					    (struct mbuf *) ifp, curlwp);
  					purged = 1;
  				}
  			}
***************
*** 1157,1162 ****
--- 1213,1222 ----
  		pfctlinput(PRC_IFDOWN, ifa->ifa_addr);
  	IFQ_PURGE(&ifp->if_snd);
  	rt_ifmsg(ifp);
+ #ifdef IPSEC
+ 	if (ifp->if_ipsec != NULL)
+ 		ifp->if_ipsec->if_flags &= ~IFF_UP;
+ #endif
  }
  
  /*
***************
*** 1184,1189 ****
--- 1244,1253 ----
  #ifdef INET6
  	in6_if_up(ifp);
  #endif
+ #ifdef IPSEC
+ 	if (ifp->if_ipsec != NULL)
+ 		ifp->if_ipsec->if_flags &= ~IFF_UP;
+ #endif
  }
  
  /*
***************
*** 1294,1305 ****
  		return (ifp);
  	}
  
  	for (ifp = TAILQ_FIRST(&ifnet); ifp != NULL;
  	     ifp = TAILQ_NEXT(ifp, if_list)) {
  		if (ifp->if_output == if_nulloutput)
  			continue;
! 	 	if (strcmp(ifp->if_xname, name) == 0)
  			return (ifp);
  	}
  	return (NULL);
  }
--- 1358,1381 ----
  		return (ifp);
  	}
  
+ #ifdef IPSEC
+ 	if (strncmp(name, "ipsec_", 6) == 0) {
+ 		cp = name + 6;
+ 	} else
+ #endif
+ 		cp = name;
+ 
  	for (ifp = TAILQ_FIRST(&ifnet); ifp != NULL;
  	     ifp = TAILQ_NEXT(ifp, if_list)) {
  		if (ifp->if_output == if_nulloutput)
  			continue;
! 	 	if (strcmp(ifp->if_xname, cp) == 0) {
! #ifdef IPSEC
! 			if (cp != name)
! 				return ifp->if_ipsec;
! #endif
  			return (ifp);
+ 		}
  	}
  	return (NULL);
  }
Index: sys/net/if.h
===================================================================
RCS file: /cvsroot/src/sys/net/if.h,v
retrieving revision 1.88
diff -c -r1.88 if.h
*** sys/net/if.h	2003/04/30 18:50:26	1.88
--- sys/net/if.h	2003/05/11 07:17:26
***************
*** 292,297 ****
--- 292,298 ----
  
  	void	*if_afdata[AF_MAX];
  	struct	mowner *if_mowner;	/* who owns mbufs for this interface */
+ 	struct ifnet	*if_ipsec;
  };
  #define	if_mtu		if_data.ifi_mtu
  #define	if_type		if_data.ifi_type
Index: sys/netinet/ip_input.c
===================================================================
RCS file: /cvsroot/src/sys/netinet/ip_input.c,v
retrieving revision 1.165
diff -c -r1.165 ip_input.c
*** sys/netinet/ip_input.c	2003/04/11 19:41:37	1.165
--- sys/netinet/ip_input.c	2003/05/11 07:17:45
***************
*** 415,420 ****
--- 415,423 ----
  void
  ip_input(struct mbuf *m)
  {
+ #ifdef PFIL_HOOKS
+ 	struct ifnet *ifp;
+ #endif
  	struct ip *ip = NULL;
  	struct ipq *fp;
  	struct in_ifaddr *ia;
***************
*** 569,593 ****
  	 * not fast-forwarded, they must clear the M_CANFASTFWD flag.
  	 * Note that filters must _never_ set this flag, as another filter
  	 * in the list may have previously cleared it.
- 	 */
- 	/*
- 	 * let ipfilter look at packet on the wire,
- 	 * not the decapsulated packet.
  	 */
  #ifdef IPSEC
! 	if (!ipsec_getnhist(m))
! #else
! 	if (1)
  #endif
! 	{
! 		if (pfil_run_hooks(&inet_pfil_hook, &m, m->m_pkthdr.rcvif,
! 				   PFIL_IN) != 0)
  		return;
! 		if (m == NULL)
! 			return;
! 		ip = mtod(m, struct ip *);
! 		hlen = ip->ip_hl << 2;
! 	}
  #endif /* PFIL_HOOKS */
  
  #ifdef ALTQ
--- 572,589 ----
  	 * not fast-forwarded, they must clear the M_CANFASTFWD flag.
  	 * Note that filters must _never_ set this flag, as another filter
  	 * in the list may have previously cleared it.
  	 */
+ 	ifp = m->m_pkthdr.rcvif;
  #ifdef IPSEC
! 	if (ipsec_getnhist(m))
! 		ifp = ifp->if_ipsec;
  #endif
! 	if (pfil_run_hooks(&inet_pfil_hook, &m, ifp, PFIL_IN) != 0)
  		return;
! 	if (m == NULL)
! 		return;
! 	ip = mtod(m, struct ip *);
! 	hlen = ip->ip_hl << 2;
  #endif /* PFIL_HOOKS */
  
  #ifdef ALTQ
Index: sys/netinet/ip_output.c
===================================================================
RCS file: /cvsroot/src/sys/netinet/ip_output.c,v
retrieving revision 1.103
diff -c -r1.103 ip_output.c
*** sys/netinet/ip_output.c	2003/02/26 06:31:15	1.103
--- sys/netinet/ip_output.c	2003/05/11 07:17:48
***************
*** 490,495 ****
--- 490,497 ----
  		printf("ip_output: Invalid policy found. %d\n", sp->policy);
  	}
  
+ 	m->m_pkthdr.rcvif = ifp;
+ 
  	/*
  	 * ipsec4_output() expects ip_len and ip_off in network
  	 * order.  They have been set to network order above.
Index: sys/netinet6/ah_input.c
===================================================================
RCS file: /cvsroot/src/sys/netinet6/ah_input.c,v
retrieving revision 1.37
diff -c -r1.37 ah_input.c
*** sys/netinet6/ah_input.c	2002/09/11 03:45:44	1.37
--- sys/netinet6/ah_input.c	2003/05/11 07:17:59
***************
*** 38,43 ****
--- 38,45 ----
  __KERNEL_RCSID(0, "$NetBSD: ah_input.c,v 1.37 2002/09/11 03:45:44 itojun Exp $");
  
  #include "opt_inet.h"
+ #include "bpfilter.h"
+ #include "opt_pfil_hooks.h"
  
  #include <sys/param.h>
  #include <sys/systm.h>
***************
*** 82,87 ****
--- 84,105 ----
  
  #include <net/net_osdep.h>
  
+ #if NBPFILTER > 0
+ #include <net/bpf.h>
+ #endif
+ #ifdef PFIL_HOOKS
+ #include <net/pfil.h>
+ #endif
+ 
+ #ifdef PFIL_HOOKS
+ #ifdef INET
+ extern struct pfil_head inet_pfil_hook; /* XXX */
+ #endif
+ #ifdef INET6
+ extern struct pfil_head inet6_pfil_hook;        /* XXX */
+ #endif
+ #endif
+ 
  /*#define IPLEN_FLIPPED*/
  
  #ifdef INET
***************
*** 94,99 ****
--- 112,120 ----
  	va_dcl
  #endif
  {
+ #if (NBPFILTER > 0) || defined(PFIL_HOOKS)
+ 	struct ifnet *ifp;
+ #endif
  	struct ip *ip;
  	struct ah *ah;
  	u_int32_t spi;
***************
*** 458,463 ****
--- 479,495 ----
  			splx(s);
  			goto fail;
  		}
+ 
+ #if NBPFILTER > 0
+                 ifp = m->m_pkthdr.rcvif->if_ipsec;
+ 		/*
+ 		 * Pass this up to any BPF listeners, but only
+ 		 * pass if up the stack if it's for us.
+ 		 */
+ 		if (ifp->if_bpf)
+ 			bpf_nullmtap(ifp->if_bpf, m, AF_INET);
+ #endif /* NBPFILTER > 0 */
+ 
  		IF_ENQUEUE(&ipintrq, m);
  		m = NULL;
  		schednetisr(NETISR_IP);	/* can be skipped but to make sure */
***************
*** 530,535 ****
--- 562,586 ----
  		}
  
  		if (nxt != IPPROTO_DONE) {
+ #if (NBPFILTER > 0) || defined(PFIL_HOOKS)
+ 			ifp = m->m_pkthdr.rcvif->if_ipsec;
+ #endif
+ #if NBPFILTER > 0
+ 			/*
+ 			 * Pass this up to any BPF listeners, but only
+ 			 * pass if up the stack if it's for us.
+ 			 */
+ 			if (ifp->if_bpf)
+ 				bpf_nullmtap(ifp->if_bpf, m, AF_INET);
+ #endif /* NBPFILTER > 0 */
+ #ifdef PFIL_HOOKS
+ 			if (pfil_run_hooks(&inet_pfil_hook, &m,
+ 			    ifp, PFIL_IN) != 0)
+ 				goto fail;
+ 			if (m == NULL)
+ 				goto fail;
+ #endif
+ 
  			if ((inetsw[ip_protox[nxt]].pr_flags & PR_LASTHDR) != 0 &&
  			    ipsec4_in_reject(m, NULL)) {
  				ipsecstat.in_polvio++;
***************
*** 621,626 ****
--- 672,680 ----
  	struct mbuf **mp;
  	int *offp, proto;
  {
+ #if (NBPFILTER > 0) || defined(PFIL_HOOKS)
+ 	struct ifnet *ifp;
+ #endif
  	struct mbuf *m = *mp;
  	int off = *offp;
  	struct ip6_hdr *ip6;
***************
*** 910,915 ****
--- 964,980 ----
  			splx(s);
  			goto fail;
  		}
+ 
+ #if NBPFILTER > 0
+                 ifp = m->m_pkthdr.rcvif->if_ipsec;
+ 		/*
+ 		 * Pass this up to any BPF listeners, but only
+ 		 * pass if up the stack if it's for us.
+ 		 */
+ 		if (ifp->if_bpf)
+ 			bpf_nullmtap(ifp->if_bpf, m, AF_INET6);
+ #endif /* NBPFILTER > 0 */
+ 
  		IF_ENQUEUE(&ip6intrq, m);
  		m = NULL;
  		schednetisr(NETISR_IPV6); /* can be skipped but to make sure */
***************
*** 976,981 ****
--- 1041,1063 ----
  			ipsec6stat.in_nomem++;
  			goto fail;
  		}
+ #if (NBPFILTER > 0) || defined(PFIL_HOOKS)
+ 		ifp = m->m_pkthdr.rcvif->if_ipsec;
+ #endif
+ #if NBPFILTER > 0
+ 		/*
+ 		 * Pass this up to any BPF listeners, but only
+ 		 * pass if up the stack if it's for us.
+ 		 */
+ 		if (ifp->if_bpf)
+ 			bpf_nullmtap(ifp->if_bpf, m, AF_INET6);
+ #endif /* NBPFILTER > 0 */
+ #ifdef PFIL_HOOKS
+ 		if (pfil_run_hooks(&inet6_pfil_hook, &m, ifp, PFIL_IN) != 0)
+ 			goto fail;
+ 		if (m == NULL)
+ 			goto fail;
+ #endif
  	}
  
  	*offp = off;
Index: sys/netinet6/esp_core.c
===================================================================
RCS file: /cvsroot/src/sys/netinet6/esp_core.c,v
retrieving revision 1.25
diff -c -r1.25 esp_core.c
*** sys/netinet6/esp_core.c	2002/09/11 03:45:45	1.25
--- sys/netinet6/esp_core.c	2003/05/11 07:18:01
***************
*** 967,972 ****
--- 967,975 ----
  	while (s && s->m_len == 0)
  		s = s->m_next;
  
+ 	if (s == NULL)
+ 		panic("esp_cbc_encrypt: out of mbufs");
+ 
  	while (soff < m->m_pkthdr.len) {
  		/* source */
  		if (sn + blocklen <= s->m_len) {
Index: sys/netinet6/esp_input.c
===================================================================
RCS file: /cvsroot/src/sys/netinet6/esp_input.c,v
retrieving revision 1.28
diff -c -r1.28 esp_input.c
*** sys/netinet6/esp_input.c	2003/01/20 00:39:30	1.28
--- sys/netinet6/esp_input.c	2003/05/11 07:18:03
***************
*** 38,43 ****
--- 38,45 ----
  __KERNEL_RCSID(0, "$NetBSD: esp_input.c,v 1.28 2003/01/20 00:39:30 simonb Exp $");
  
  #include "opt_inet.h"
+ #include "bpfilter.h"
+ #include "opt_pfil_hooks.h"
  
  #include <sys/param.h>
  #include <sys/systm.h>
***************
*** 82,87 ****
--- 84,105 ----
  
  #include <net/net_osdep.h>
  
+ #if NBPFILTER > 0
+ #include <net/bpf.h>
+ #endif
+ #ifdef PFIL_HOOKS
+ #include <net/pfil.h>
+ #endif
+ 
+ #ifdef PFIL_HOOKS
+ #ifdef INET
+ extern struct pfil_head inet_pfil_hook; /* XXX */
+ #endif
+ #ifdef INET6
+ extern struct pfil_head inet6_pfil_hook;        /* XXX */
+ #endif
+ #endif
+ 
  /*#define IPLEN_FLIPPED*/
  
  #define ESPMAXLEN \
***************
*** 98,103 ****
--- 116,124 ----
  	va_dcl
  #endif
  {
+ #if (NBPFILTER > 0) || defined(PFIL_HOOKS)
+ 	struct ifnet *ifp;
+ #endif
  	struct ip *ip;
  	struct esp *esp;
  	struct esptail esptail;
***************
*** 393,398 ****
--- 414,430 ----
  			splx(s);
  			goto bad;
  		}
+ 
+ #if NBPFILTER > 0
+ 		ifp = m->m_pkthdr.rcvif->if_ipsec;
+ 		/*
+ 		 * Pass this up to any BPF listeners, but only
+ 		 * pass if up the stack if it's for us.
+ 		 */
+ 		if (ifp->if_bpf)
+ 			bpf_nullmtap(ifp->if_bpf, m, AF_INET);
+ #endif /* NBPFILTER > 0 */
+ 
  		IF_ENQUEUE(&ipintrq, m);
  		m = NULL;
  		schednetisr(NETISR_IP); /* can be skipped but to make sure */
***************
*** 429,434 ****
--- 461,485 ----
  		}
  
  		if (nxt != IPPROTO_DONE) {
+ #if (NBPFILTER > 0) || defined(PFIL_HOOKS)
+ 			ifp = m->m_pkthdr.rcvif->if_ipsec;
+ #endif
+ #if NBPFILTER > 0
+ 			/*
+ 			 * Pass this up to any BPF listeners, but only
+ 			 * pass if up the stack if it's for us.
+ 			 */
+ 			if (ifp->if_bpf)
+ 				bpf_nullmtap(ifp->if_bpf, m, AF_INET);
+ #endif /* NBPFILTER > 0 */
+ #ifdef PFIL_HOOKS
+ 			if (pfil_run_hooks(&inet_pfil_hook, &m,
+ 			    ifp, PFIL_IN) != 0)
+ 				goto bad;
+ 			if (m == NULL)
+ 				goto bad;
+ #endif
+ 
  			if ((inetsw[ip_protox[nxt]].pr_flags & PR_LASTHDR) != 0 &&
  			    ipsec4_in_reject(m, NULL)) {
  				ipsecstat.in_polvio++;
***************
*** 521,526 ****
--- 572,580 ----
  	struct mbuf **mp;
  	int *offp, proto;
  {
+ #if (NBPFILTER > 0) || defined(PFIL_HOOKS)
+ 	struct ifnet *ifp;
+ #endif
  	struct mbuf *m = *mp;
  	int off = *offp;
  	struct ip6_hdr *ip6;
***************
*** 814,819 ****
--- 868,884 ----
  			splx(s);
  			goto bad;
  		}
+ 
+ #if NBPFILTER > 0
+ 		ifp = m->m_pkthdr.rcvif->if_ipsec;
+ 		/*
+ 		 * Pass this up to any BPF listeners, but only
+ 		 * pass if up the stack if it's for us.
+ 		 */
+ 		if (ifp->if_bpf)
+ 			bpf_nullmtap(ifp->if_bpf, m, AF_INET6);
+ #endif /* NBPFILTER > 0 */
+ 
  		IF_ENQUEUE(&ip6intrq, m);
  		m = NULL;
  		schednetisr(NETISR_IPV6); /* can be skipped but to make sure */
***************
*** 914,919 ****
--- 979,1001 ----
  			ipsec6stat.in_nomem++;
  			goto bad;
  		}
+ #if (NBPFILTER > 0) || defined(PFIL_HOOKS)
+ 		ifp = m->m_pkthdr.rcvif->if_ipsec;
+ #endif
+ #if NBPFILTER > 0
+ 		/*
+ 		 * Pass this up to any BPF listeners, but only
+ 		 * pass if up the stack if it's for us.
+ 		 */
+ 		if (ifp->if_bpf)
+ 			bpf_nullmtap(ifp->if_bpf, m, AF_INET6);
+ #endif /* NBPFILTER > 0 */
+ #ifdef PFIL_HOOKS
+ 		if (pfil_run_hooks(&inet6_pfil_hook, &m, ifp, PFIL_IN) != 0)
+ 			goto bad;
+ 		if (m == NULL)
+ 			goto bad;
+ #endif
  	}
  
  	*offp = off;
Index: sys/netinet6/ip6_input.c
===================================================================
RCS file: /cvsroot/src/sys/netinet6/ip6_input.c,v
retrieving revision 1.60
diff -c -r1.60 ip6_input.c
*** sys/netinet6/ip6_input.c	2003/01/20 05:30:11	1.60
--- sys/netinet6/ip6_input.c	2003/05/11 07:18:06
***************
*** 229,234 ****
--- 229,237 ----
  	u_int32_t rtalert = ~0;
  	int nxt, ours = 0;
  	struct ifnet *deliverifp = NULL;
+ #ifdef PFIL_HOOKS
+ 	struct ifnet *ifp;
+ #endif
  
  #ifdef IPSEC
  	/*
***************
*** 301,307 ****
  		goto bad;
  	}
  
- #ifdef PFIL_HOOKS
  	/*
  	 * Run through list of hooks for input packets.  If there are any
  	 * filters which require that additional packets in the flow are
--- 304,309 ----
***************
*** 313,331 ****
  	 * let ipfilter look at packet on the wire,
  	 * not the decapsulated packet.
  	 */
  #ifdef IPSEC
! 	if (!ipsec_getnhist(m))
! #else
! 	if (1)
  #endif
! 	{
! 		if (pfil_run_hooks(&inet6_pfil_hook, &m, m->m_pkthdr.rcvif,
! 				   PFIL_IN) != 0)
! 			return;
! 		if (m == NULL)
! 			return;
! 		ip6 = mtod(m, struct ip6_hdr *);
! 	}
  #endif /* PFIL_HOOKS */
  
  	ip6stat.ip6s_nxthist[ip6->ip6_nxt]++;
--- 315,331 ----
  	 * let ipfilter look at packet on the wire,
  	 * not the decapsulated packet.
  	 */
+ #ifdef PFIL_HOOKS
+ 	ifp = m->m_pkthdr.rcvif;
  #ifdef IPSEC
! 	if (ipsec_getnhist(m))
! 		ifp = ifp->if_ipsec;
  #endif
! 	if (pfil_run_hooks(&inet6_pfil_hook, &m, ifp, PFIL_IN) != 0)
! 		return;
! 	if (m == NULL)
! 		return;
! 	ip6 = mtod(m, struct ip6_hdr *);
  #endif /* PFIL_HOOKS */
  
  	ip6stat.ip6s_nxthist[ip6->ip6_nxt]++;
Index: sys/netinet6/ipsec.c
===================================================================
RCS file: /cvsroot/src/sys/netinet6/ipsec.c,v
retrieving revision 1.70
diff -c -r1.70 ipsec.c
*** sys/netinet6/ipsec.c	2003/05/10 13:23:07	1.70
--- sys/netinet6/ipsec.c	2003/05/11 07:18:14
***************
*** 39,44 ****
--- 39,46 ----
  
  #include "opt_inet.h"
  #include "opt_ipsec.h"
+ #include "opt_pfil_hooks.h"
+ #include "bpfilter.h"
  
  #include <sys/param.h>
  #include <sys/systm.h>
***************
*** 56,61 ****
--- 58,66 ----
  
  #include <net/if.h>
  #include <net/route.h>
+ #if NBPFILTER > 0
+ #include <net/bpf.h>
+ #endif
  
  #include <netinet/in.h>
  #include <netinet/in_systm.h>
***************
*** 90,95 ****
--- 95,109 ----
  
  #include <net/net_osdep.h>
  
+ #ifdef PFIL_HOOKS
+ #ifdef INET
+ extern struct pfil_head inet_pfil_hook;	/* XXX */
+ #endif
+ #ifdef INET6
+ extern struct pfil_head inet6_pfil_hook;	/* XXX */
+ #endif
+ #endif
+ 
  #ifdef IPSEC_DEBUG
  int ipsec_debug = 1;
  #else
***************
*** 153,162 ****
  static struct mbuf *ipsec6_splithdr __P((struct mbuf *));
  #endif
  #ifdef INET
! static int ipsec4_encapsulate __P((struct mbuf *, struct secasvar *));
  #endif
  #ifdef INET6
! static int ipsec6_encapsulate __P((struct mbuf *, struct secasvar *));
  #endif
  static struct m_tag *ipsec_addaux __P((struct mbuf *));
  static struct m_tag *ipsec_findaux __P((struct mbuf *));
--- 167,176 ----
  static struct mbuf *ipsec6_splithdr __P((struct mbuf *));
  #endif
  #ifdef INET
! static int ipsec4_encapsulate __P((struct mbuf **, struct secasvar *));
  #endif
  #ifdef INET6
! static int ipsec6_encapsulate __P((struct mbuf **, struct secasvar *));
  #endif
  static struct m_tag *ipsec_addaux __P((struct mbuf *));
  static struct m_tag *ipsec_findaux __P((struct mbuf *));
***************
*** 1984,2015 ****
   * ip->ip_src must be fixed later on.
   */
  static int
! ipsec4_encapsulate(m, sav)
! 	struct mbuf *m;
  	struct secasvar *sav;
  {
  	struct ip *oip;
  	struct ip *ip;
  	size_t hlen;
  	size_t plen;
  
  	/* can't tunnel between different AFs */
  	if (((struct sockaddr *)&sav->sah->saidx.src)->sa_family
  		!= ((struct sockaddr *)&sav->sah->saidx.dst)->sa_family
  	 || ((struct sockaddr *)&sav->sah->saidx.src)->sa_family != AF_INET) {
  		m_freem(m);
  		return EINVAL;
  	}
  #if 0
  	/* XXX if the dst is myself, perform nothing. */
  	if (key_ismyaddr((struct sockaddr *)&sav->sah->saidx.dst)) {
  		m_freem(m);
  		return EINVAL;
  	}
  #endif
  
! 	if (m->m_len < sizeof(*ip))
  		panic("ipsec4_encapsulate: assumption failed (first mbuf length)");
  
  	ip = mtod(m, struct ip *);
  #ifdef _IP_VHL
--- 1998,2044 ----
   * ip->ip_src must be fixed later on.
   */
  static int
! ipsec4_encapsulate(mp, sav)
! 	struct mbuf **mp;
  	struct secasvar *sav;
  {
  	struct ip *oip;
  	struct ip *ip;
  	size_t hlen;
  	size_t plen;
+ 	struct mbuf *m = *mp;
+ #if (NBPFILTER > 0) || defined(PFIL_HOOKS)
+         struct ifnet *ifp;
+ #endif
  
  	/* can't tunnel between different AFs */
  	if (((struct sockaddr *)&sav->sah->saidx.src)->sa_family
  		!= ((struct sockaddr *)&sav->sah->saidx.dst)->sa_family
  	 || ((struct sockaddr *)&sav->sah->saidx.src)->sa_family != AF_INET) {
  		m_freem(m);
+ 		*mp = NULL;
  		return EINVAL;
  	}
  #if 0
  	/* XXX if the dst is myself, perform nothing. */
  	if (key_ismyaddr((struct sockaddr *)&sav->sah->saidx.dst)) {
  		m_freem(m);
+ 		*mp = NULL;
  		return EINVAL;
  	}
  #endif
  
! 	if (m->m_len < sizeof(*ip)) {
! #ifdef DIAGNOSTIC
  		panic("ipsec4_encapsulate: assumption failed (first mbuf length)");
+ #else
+ 		ipseclog((LOG_ERR, "ipsec4_encapsulate: %s %d < 20",
+ 			 "assumption failed (first mbuf length)", m->m_len));
+ 		m_freem(m);
+ 		*mp = NULL;
+ 		return EINVAL;
+ #endif
+ 	}
  
  	ip = mtod(m, struct ip *);
  #ifdef _IP_VHL
***************
*** 2018,2026 ****
  	hlen = ip->ip_hl << 2;
  #endif
  
- 	if (m->m_len != hlen)
- 		panic("ipsec4_encapsulate: assumption failed (first mbuf length)");
- 
  	/* generate header checksum */
  	ip->ip_sum = 0;
  #ifdef _IP_VHL
--- 2047,2052 ----
***************
*** 2034,2039 ****
--- 2060,2094 ----
  
  	plen = m->m_pkthdr.len;
  
+ #if (NBPFILTER > 0) || defined(PFIL_HOOKS)
+         ifp = m->m_pkthdr.rcvif->if_ipsec;
+ #endif
+ #if NBPFILTER > 0
+ 	/*
+ 	 * Pass this up to any BPF listeners, but only
+ 	 * pass if up the stack if it's for us.
+ 	 */
+ 	if (ifp->if_bpf)
+ 		bpf_nullmtap(ifp->if_bpf, m, AF_INET);
+ #endif /* NBPFILTER > 0 */
+ #ifdef PFIL_HOOKS
+ 	{
+ 	int error;
+ 
+ 	if ((error = pfil_run_hooks(&inet_pfil_hook, mp, ifp, PFIL_OUT)) != 0)
+ 		return error;
+ 	m = *mp;
+ 	if (m == NULL)
+ 		return error;
+ 	plen = m->m_pkthdr.len;
+ 	}
+ #endif
+ 
+ 	*mp = ipsec4_splithdr(m);
+ 	m = *mp;
+ 	if (m == NULL)
+ 		return ENOMEM;
+ 
  	/*
  	 * grow the mbuf to accomodate the new IPv4 header.
  	 * NOTE: IPv4 options will never be copied.
***************
*** 2101,2113 ****
  
  #ifdef INET6
  static int
! ipsec6_encapsulate(m, sav)
! 	struct mbuf *m;
  	struct secasvar *sav;
  {
  	struct ip6_hdr *oip6;
  	struct ip6_hdr *ip6;
  	size_t plen;
  
  	/* can't tunnel between different AFs */
  	if (((struct sockaddr *)&sav->sah->saidx.src)->sa_family
--- 2156,2172 ----
  
  #ifdef INET6
  static int
! ipsec6_encapsulate(mp, sav)
! 	struct mbuf **mp;
  	struct secasvar *sav;
  {
  	struct ip6_hdr *oip6;
  	struct ip6_hdr *ip6;
  	size_t plen;
+ 	struct mbuf *m = *mp;
+ #if (NBPFILTER > 0) || defined(PFIL_HOOKS)
+         struct ifnet *ifp;
+ #endif
  
  	/* can't tunnel between different AFs */
  	if (((struct sockaddr *)&sav->sah->saidx.src)->sa_family
***************
*** 2126,2132 ****
--- 2185,2215 ----
  
  	plen = m->m_pkthdr.len;
  
+ #if (NBPFILTER > 0) || defined(PFIL_HOOKS)
+         ifp = m->m_pkthdr.rcvif->if_ipsec;
+ #endif
+ #if NBPFILTER > 0
  	/*
+ 	 * Pass this up to any BPF listeners, but only
+ 	 * pass if up the stack if it's for us.
+ 	 */
+ 	if (ifp->if_bpf)
+ 		bpf_nullmtap(ifp->if_bpf, m, AF_INET6);
+ #endif /* NBPFILTER > 0 */
+ #ifdef PFIL_HOOKS
+ 	{
+ 	int error;
+ 
+ 	if ((error = pfil_run_hooks(&inet6_pfil_hook, mp, ifp, PFIL_OUT)) != 0)
+ 		return error;
+ 	m = *mp;
+ 	if (m == NULL)
+ 		return error;
+ 	ip6 = mtod(m, struct ip6_hdr *);
+ 	}
+ #endif
+ 
+ 	/*
  	 * grow the mbuf to accomodate the new IPv6 header.
  	 */
  	if (m->m_len != sizeof(struct ip6_hdr))
***************
*** 2539,2544 ****
--- 2622,2630 ----
  	int s;
  	int error;
  	struct sockaddr_in *dst4;
+ #if (NBPFILTER > 0) || defined(PFIL_HOOKS)
+ 	struct ifnet *ifp;
+ #endif
  
  	if (!state)
  		panic("state == NULL in ipsec4_output");
***************
*** 2624,2636 ****
  				goto bad;
  			}
  
! 			state->m = ipsec4_splithdr(state->m);
! 			if (!state->m) {
! 				splx(s);
! 				error = ENOMEM;
! 				goto bad;
! 			}
! 			error = ipsec4_encapsulate(state->m, isr->sav);
  			splx(s);
  			if (error) {
  				state->m = NULL;
--- 2710,2716 ----
  				goto bad;
  			}
  
! 			error = ipsec4_encapsulate(&state->m, isr->sav);
  			splx(s);
  			if (error) {
  				state->m = NULL;
***************
*** 2666,2679 ****
  			}
  
  			state->encap++;
! 		} else
  			splx(s);
  
  		state->m = ipsec4_splithdr(state->m);
  		if (!state->m) {
  			error = ENOMEM;
  			goto bad;
  		}
  		switch (isr->saidx.proto) {
  		case IPPROTO_ESP:
  #ifdef IPSEC_ESP
--- 2746,2781 ----
  			}
  
  			state->encap++;
! 		} else {
  			splx(s);
  
+ #if (NBPFILTER > 0) || defined(PFIL_HOOKS)
+ 			ifp = state->m->m_pkthdr.rcvif->if_ipsec;
+ #endif
+ #if NBPFILTER > 0
+ 			/*
+ 			 * Pass this up to any BPF listeners, but only
+ 			 * pass if up the stack if it's for us.
+ 			 */
+ 			if (ifp->if_bpf)
+ 				bpf_nullmtap(ifp->if_bpf, state->m, AF_INET);
+ #endif /* NBPFILTER > 0 */
+ #ifdef PFIL_HOOKS
+ 			if (pfil_run_hooks(&inet_pfil_hook, &state->m,
+ 			    ifp, PFIL_OUT) != 0)
+ 				goto bad;
+ 			if (state->m == NULL)
+ 				goto bad;
+ #endif
+ 
+ 		}
+ 
  		state->m = ipsec4_splithdr(state->m);
  		if (!state->m) {
  			error = ENOMEM;
  			goto bad;
  		}
+ 
  		switch (isr->saidx.proto) {
  		case IPPROTO_ESP:
  #ifdef IPSEC_ESP
***************
*** 2796,2801 ****
--- 2898,2907 ----
  	int flags;
  	int *tun;
  {
+ #if (NBPFILTER > 0) || defined(PFIL_HOOKS)
+ 	struct ifnet *ifp;
+ 	struct mbuf *m;
+ #endif
  	struct ip6_hdr *ip6;
  	struct ipsecrequest *isr = NULL;
  	int error = 0;
***************
*** 2877,2882 ****
--- 2983,3010 ----
  			goto bad;
  		}
  
+ #if (NBPFILTER > 0) || defined(PFIL_HOOKS)
+ 		m = state->m;
+ 		ifp = m->m_pkthdr.rcvif->if_ipsec;
+ #endif
+ #if NBPFILTER > 0
+ 		/*
+ 		 * Pass this up to any BPF listeners, but only
+ 		 * pass if up the stack if it's for us.
+ 		 */
+ 		if (ifp->if_bpf)
+ 			bpf_nullmtap(ifp->if_bpf, m, AF_INET6);
+ #endif /* NBPFILTER > 0 */
+ #ifdef PFIL_HOOKS
+ 		if ((error = pfil_run_hooks(&inet6_pfil_hook, &m,
+ 		    ifp, PFIL_OUT)) != 0)
+ 			goto bad;
+ 		if (m == NULL)
+ 			goto bad;
+ 		state->m = m;
+ 		ip6 = mtod(m, struct ip6_hdr *);
+ #endif
+ 
  		switch (isr->saidx.proto) {
  		case IPPROTO_ESP:
  #ifdef IPSEC_ESP
***************
*** 3029,3035 ****
  				error = ENOMEM;
  				goto bad;
  			}
! 			error = ipsec6_encapsulate(state->m, isr->sav);
  			splx(s);
  			if (error) {
  				state->m = 0;
--- 3157,3164 ----
  				error = ENOMEM;
  				goto bad;
  			}
! 
! 			error = ipsec6_encapsulate(&state->m, isr->sav);
  			splx(s);
  			if (error) {
  				state->m = 0;