Subject: Re: patch for pre/post-IPSEC traffic with IPv4
To: None <itojun@iijlab.net>
From: Darren Reed <avalon@caligula.anu.edu.au>
List: tech-net
Date: 05/05/2003 12:16:27
After some further discussion with Itojun on how and where to
"correctly" try and patch the networking code for tcpdump/ipfilter
hooks, I've arrived at the following set of patches.

Feedback welcome.

Darren

--- usr/src/sys/net/if.h.orig	2003-04-29 09:16:25.000000000 +1000
+++ usr/src/sys/net/if.h	2003-04-30 19:38:30.000000000 +1000
@@ -290,6 +290,7 @@
 
 	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
--- usr/src/sys/net/if.c.orig	2003-02-01 17:23:46.000000000 +1100
+++ usr/src/sys/net/if.c	2003-04-30 20:06:22.000000000 +1000
@@ -109,7 +109,9 @@
 #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,6 +144,12 @@
 #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,6 +162,9 @@
 
 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,7 +476,42 @@
 
 	/* 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_RAW, 0);
+# endif
+	pifp->if_ipsec = ifp;
 }
+#endif
 
 void
 if_attachdomain()
@@ -570,6 +616,16 @@
 #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);
 
@@ -1157,6 +1213,10 @@
 		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,6 +1244,10 @@
 #ifdef INET6
 	in6_if_up(ifp);
 #endif
+#ifdef IPSEC
+	if (ifp->if_ipsec != NULL)
+		ifp->if_ipsec->if_flags &= ~IFF_UP;
+#endif
 }
 
 /*
@@ -1294,12 +1358,24 @@
 		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, name) == 0)
+	 	if (strcmp(ifp->if_xname, cp) == 0) {
+#ifdef IPSEC
+			if (cp != name)
+				return ifp->if_ipsec;
+#endif
 			return (ifp);
+		}
 	}
 	return (NULL);
 }
--- usr/src/sys/netinet6/ipsec.c.orig	2003-01-17 19:11:57.000000000 +1100
+++ usr/src/sys/netinet6/ipsec.c	2003-05-01 17:54:02.000000000 +1000
@@ -39,6 +39,8 @@
 
 #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,6 +58,12 @@
 
 #include <net/if.h>
 #include <net/route.h>
+#if NBPFILTER > 0
+#include <net/bpf.h>
+#endif
+#ifdef PFIL_HOOKS
+#include <net/pfil.h>
+#endif
 
 #include <netinet/in.h>
 #include <netinet/in_systm.h>
@@ -90,6 +98,15 @@
 
 #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
@@ -1992,6 +2009,9 @@
 	struct ip *ip;
 	size_t hlen;
 	size_t plen;
+#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
@@ -2034,6 +2054,24 @@
 
 	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_mtap(ifp->if_bpf, m);
+#endif /* NBPFILTER > 0 */
+#ifdef PFIL_HOOKS
+	if (pfil_run_hooks(&inet_pfil_hook, &m, ifp, PFIL_OUT) != 0)
+		return EHOSTUNREACH;
+	if (m == NULL)
+		return EHOSTUNREACH;
+#endif /* PFIL_HOOKS */
+
 	/*
 	 * grow the mbuf to accomodate the new IPv4 header.
 	 * NOTE: IPv4 options will never be copied.
@@ -2110,6 +2148,9 @@
 	struct ip6_hdr *oip6;
 	struct ip6_hdr *ip6;
 	size_t plen;
+#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
@@ -2128,6 +2169,24 @@
 
 	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_mtap(ifp->if_bpf, m);
+#endif /* NBPFILTER > 0 */
+#ifdef PFIL_HOOKS
+	if (pfil_run_hooks(&inet6_pfil_hook, &m, ifp, PFIL_OUT) != 0)
+		return EHOSTUNREACH;
+	if (m == NULL)
+		return EHOSTUNREACH;
+#endif /* PFIL_HOOKS */
+
 	/*
 	 * grow the mbuf to accomodate the new IPv6 header.
 	 */
@@ -2541,6 +2600,9 @@
 	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");
@@ -2668,14 +2730,35 @@
 			}
 
 			state->encap++;
-		} else
+		} 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_mtap(ifp->if_bpf, state->m);
+#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 /* PFIL_HOOKS */
+		}
+
 		state->m = ipsec4_splithdr(state->m);
 		if (!state->m) {
 			error = ENOMEM;
 			goto bad;
 		}
+
 		switch (isr->saidx.proto) {
 		case IPPROTO_ESP:
 #ifdef IPSEC_ESP
@@ -2798,6 +2881,10 @@
 	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;
@@ -2879,6 +2966,26 @@
 			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_mtap(ifp->if_bpf, m);
+#endif /* NBPFILTER > 0 */
+#ifdef PFIL_HOOKS
+		if (pfil_run_hooks(&inet6_pfil_hook, &m, ifp, PFIL_OUT) != 0)
+			goto bad;
+		if (m == NULL)
+			goto bad;
+		state->m = m;
+#endif /* PFIL_HOOKS */
+
 		switch (isr->saidx.proto) {
 		case IPPROTO_ESP:
 #ifdef IPSEC_ESP
@@ -3031,6 +3138,7 @@
 				error = ENOMEM;
 				goto bad;
 			}
+
 			error = ipsec6_encapsulate(state->m, isr->sav);
 			splx(s);
 			if (error) {
--- usr/src/sys/netinet/ip_input.c.orig	2003-04-12 05:41:37.000000000 +1000
+++ usr/src/sys/netinet/ip_input.c	2003-05-04 14:45:08.000000000 +1000
@@ -417,6 +417,7 @@
 {
 	struct ip *ip = NULL;
 	struct ipq *fp;
+	struct ifnet *ifp;
 	struct in_ifaddr *ia;
 	struct ifaddr *ifa;
 	struct ipqent *ipqe;
@@ -570,24 +571,17 @@
 	 * 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.
-	 */
+	ifp = m->m_pkthdr.rcvif;
 #ifdef IPSEC
-	if (!ipsec_getnhist(m))
-#else
-	if (1)
+	if (ipsec_getnhist(m))
+		ifp = ifp->if_ipsec;
 #endif
-	{
-		if (pfil_run_hooks(&inet_pfil_hook, &m, m->m_pkthdr.rcvif,
-				   PFIL_IN) != 0)
+	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;
-	}
+	if (m == NULL)
+		return;
+	ip = mtod(m, struct ip *);
+	hlen = ip->ip_hl << 2;
 #endif /* PFIL_HOOKS */
 
 #ifdef ALTQ
--- usr/src/sys/netinet/ip_output.c.orig	2003-02-26 17:31:15.000000000 +1100
+++ usr/src/sys/netinet/ip_output.c	2003-05-04 14:47:12.000000000 +1000
@@ -490,6 +490,8 @@
 		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.
--- usr/src/sys/net/esp_input.c.orig	2003-01-20 11:39:30.000000000 +1100
+++ usr/src/sys/net/esp_input.c	2003-05-01 15:43:45.000000000 +1000
@@ -38,6 +38,8 @@
 __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,6 +84,20 @@
 
 #include <net/net_osdep.h>
 
+#if NBPFILTER > 0
+#include <net/bpf.h>
+#endif
+#ifdef PFIL_HOOKS
+#include <net/pfil.h>
+ 
+#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,6 +114,7 @@
 	va_dcl
 #endif
 {
+	struct ifnet *ifp;
 	struct ip *ip;
 	struct esp *esp;
 	struct esptail esptail;
@@ -393,6 +410,23 @@
 			splx(s);
 			goto bad;
 		}
+
+		ifp = m->m_pkthdr.rcvif->if_ipsec;
+#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_mtap(ifp->if_bpf, m);
+#endif /* NBPFILTER > 0 */
+#ifdef PFIL_HOOKS
+		if (pfil_run_hooks(&inet_pfil_hook, &m, ifp, PFIL_IN) != 0)
+			return; 
+		if (m == NULL)
+			return; 
+#endif /* PFIL_HOOKS */
+
 		IF_ENQUEUE(&ipintrq, m);
 		m = NULL;
 		schednetisr(NETISR_IP); /* can be skipped but to make sure */
@@ -521,6 +555,7 @@
 	struct mbuf **mp;
 	int *offp, proto;
 {
+	struct ifnet *ifp;
 	struct mbuf *m = *mp;
 	int off = *offp;
 	struct ip6_hdr *ip6;
@@ -814,6 +849,23 @@
 			splx(s);
 			goto bad;
 		}
+
+		ifp = m->m_pkthdr.rcvif->if_ipsec;
+#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_mtap(ifp->if_bpf, m);
+#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 /* PFIL_HOOKS */
+
 		IF_ENQUEUE(&ip6intrq, m);
 		m = NULL;
 		schednetisr(NETISR_IPV6); /* can be skipped but to make sure */
--- usr/src/sys/net/ah_input.c.orig	2003-05-04 14:58:04.000000000 +1000
+++ usr/src/sys/net/ah_input.c	2003-05-01 15:39:00.000000000 +1000
@@ -38,6 +38,8 @@
 __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,6 +84,20 @@
 
 #include <net/net_osdep.h>
 
+#if NBPFILTER > 0
+#include <net/bpf.h>
+#endif
+#ifdef PFIL_HOOKS
+#include <net/pfil.h>
+
+#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,6 +110,7 @@
 	va_dcl
 #endif
 {
+	struct ifnet *ifp;
 	struct ip *ip;
 	struct ah *ah;
 	u_int32_t spi;
@@ -458,6 +475,23 @@
 			splx(s);
 			goto fail;
 		}
+
+                ifp = m->m_pkthdr.rcvif->if_ipsec; 
+#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_mtap(ifp->if_bpf, m);
+#endif /* NBPFILTER > 0 */
+#ifdef PFIL_HOOKS   
+		if (pfil_run_hooks(&inet6_pfil_hook, &m, ifp, PFIL_IN) != 0)
+			return;
+		if (m == NULL)  
+			return;
+#endif /* PFIL_HOOKS */
+
 		IF_ENQUEUE(&ipintrq, m);
 		m = NULL;
 		schednetisr(NETISR_IP);	/* can be skipped but to make sure */
@@ -621,6 +655,7 @@
 	struct mbuf **mp;
 	int *offp, proto;
 {
+	struct ifnet *ifp;
 	struct mbuf *m = *mp;
 	int off = *offp;
 	struct ip6_hdr *ip6;
@@ -910,6 +945,23 @@
 			splx(s);
 			goto fail;
 		}
+
+                ifp = m->m_pkthdr.rcvif->if_ipsec; 
+#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_mtap(ifp->if_bpf, m);
+#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 /* PFIL_HOOKS */
+        
 		IF_ENQUEUE(&ip6intrq, m);
 		m = NULL;
 		schednetisr(NETISR_IPV6); /* can be skipped but to make sure */