Subject: Re: IPsec, NAT, and Firewalling
To: Dave Burgess <burgess@neonramp.com>
From: None <itojun@iijlab.net>
List: current-users
Date: 01/23/2001 13:55:40
	try the following diff.  it should make ipfilter look only at
	packet on the wire (not the IPsec-decapsulated ones).

itojun


? netinet/tcp_subr.c-
Index: netinet/ip_input.c
===================================================================
RCS file: /cvsroot/syssrc/sys/netinet/ip_input.c,v
retrieving revision 1.126
diff -u -r1.126 ip_input.c
--- netinet/ip_input.c	2000/12/28 21:40:59	1.126
+++ netinet/ip_input.c	2001/01/23 04:54:04
@@ -471,12 +471,23 @@
 	 * Note that filters must _never_ set this flag, as another filter
 	 * in the list may have previously cleared it.
 	 */
-	if (pfil_run_hooks(&inet_pfil_hook, &m, m->m_pkthdr.rcvif,
-			   PFIL_IN) != 0)
+	/*
+	 * let ipfilter look at packet on the wire,
+	 * not the decapsulated packet.
+	 */
+#ifdef IPSEC
+	if (!ipsec_gethist(m, NULL))
+#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 *);
+		if (m == NULL)
+			return;
+		ip = mtod(m, struct ip *);
+	}
 #endif /* PFIL_HOOKS */
 
 #ifdef ALTQ
Index: netinet/ip_output.c
===================================================================
RCS file: /cvsroot/syssrc/sys/netinet/ip_output.c,v
retrieving revision 1.81
diff -u -r1.81 ip_output.c
--- netinet/ip_output.c	2001/01/13 07:19:33	1.81
+++ netinet/ip_output.c	2001/01/23 04:54:04
@@ -430,19 +430,6 @@
 	HTONS(ip->ip_len);
 	HTONS(ip->ip_off);
 
-#ifdef PFIL_HOOKS
-	/*
-	 * Run through list of hooks for output packets.
-	 */
-	if ((error = pfil_run_hooks(&inet_pfil_hook, &m, ifp,
-				    PFIL_OUT)) != 0)
-		goto done;
-	if (m == NULL)
-		goto done;
-
-	ip = mtod(m, struct ip *);
-#endif /* PFIL_HOOKS */
-
 #ifdef IPSEC
 	/* get SP for this packet */
 	if (so == NULL)
@@ -562,6 +549,19 @@
 skip_ipsec:
 #endif /*IPSEC*/
 
+#ifdef PFIL_HOOKS
+	/*
+	 * Run through list of hooks for output packets.
+	 */
+	if ((error = pfil_run_hooks(&inet_pfil_hook, &m, ifp,
+				    PFIL_OUT)) != 0)
+		goto done;
+	if (m == NULL)
+		goto done;
+
+	ip = mtod(m, struct ip *);
+#endif /* PFIL_HOOKS */
+
 	/*
 	 * If small enough for mtu of path, can just send directly.
 	 */
@@ -577,6 +577,10 @@
 #endif
 		ip->ip_sum = 0;
 		ip->ip_sum = in_cksum(m, hlen);
+#ifdef IPSEC
+		/* clean ipsec history once it goes out of the node */
+		ipsec_delaux(m);
+#endif
 		error = (*ifp->if_output)(ifp, m, sintosa(dst), ro->ro_rt);
 		goto done;
 	}
@@ -703,6 +707,10 @@
 				ia->ia_ifa.ifa_data.ifad_outbytes +=
 					ntohs(ip->ip_len);
 			}
+#endif
+#ifdef IPSEC
+			/* clean ipsec history once it goes out of the node */
+			ipsec_delaux(m);
 #endif
 			error = (*ifp->if_output)(ifp, m, sintosa(dst),
 			    ro->ro_rt);
Index: netinet6/ah_input.c
===================================================================
RCS file: /cvsroot/syssrc/sys/netinet6/ah_input.c,v
retrieving revision 1.23
diff -u -r1.23 ah_input.c
--- netinet6/ah_input.c	2000/12/09 01:29:50	1.23
+++ netinet6/ah_input.c	2001/01/23 04:54:05
@@ -456,6 +456,8 @@
 #endif
 
 		key_sa_recordxfer(sav, m);
+		ipsec_addhist(m, IPPROTO_AH, spi);
+		ipsec_addhist(m, IPPROTO_IPV4, 0);
 
 		s = splimp();
 		if (IF_QFULL(&ipintrq)) {
@@ -538,6 +540,7 @@
 		/* forget about IP hdr checksum, the check has already been passed */
 
 		key_sa_recordxfer(sav, m);
+		ipsec_addhist(m, IPPROTO_AH, spi);
 
 		if (nxt != IPPROTO_DONE)
 			(*inetsw[ip_protox[nxt]].pr_input)(m, off, nxt);
@@ -915,6 +918,8 @@
 #endif
 
 		key_sa_recordxfer(sav, m);
+		ipsec_addhist(m, IPPROTO_AH, spi);
+		ipsec_addhist(m, IPPROTO_IPV6, 0);
 
 		s = splimp();
 		if (IF_QFULL(&ip6intrq)) {
@@ -993,6 +998,7 @@
 		ip6->ip6_plen = htons(ntohs(ip6->ip6_plen) - stripsiz);
 
 		key_sa_recordxfer(sav, m);
+		ipsec_addhist(m, IPPROTO_AH, spi);
 	}
 
 	*offp = off;
Index: netinet6/esp_input.c
===================================================================
RCS file: /cvsroot/syssrc/sys/netinet6/esp_input.c,v
retrieving revision 1.13
diff -u -r1.13 esp_input.c
--- netinet6/esp_input.c	2000/12/09 01:29:50	1.13
+++ netinet6/esp_input.c	2001/01/23 04:54:05
@@ -382,6 +382,8 @@
 #endif
 
 		key_sa_recordxfer(sav, m);
+		ipsec_addhist(m, IPPROTO_ESP, spi);
+		ipsec_addhist(m, IPPROTO_IPV4, 0);
 
 		s = splimp();
 		if (IF_QFULL(&ipintrq)) {
@@ -419,6 +421,7 @@
 		ip->ip_p = nxt;
 
 		key_sa_recordxfer(sav, m);
+		ipsec_addhist(m, IPPROTO_ESP, spi);
 
 		if (nxt != IPPROTO_DONE)
 			(*inetsw[ip_protox[nxt]].pr_input)(m, off, nxt);
@@ -793,6 +796,8 @@
 #endif
 
 		key_sa_recordxfer(sav, m);
+		ipsec_addhist(m, IPPROTO_ESP, spi);
+		ipsec_addhist(m, IPPROTO_IPV6, 0);
 
 		s = splimp();
 		if (IF_QFULL(&ip6intrq)) {
@@ -896,6 +901,7 @@
 		ip6->ip6_plen = htons(ntohs(ip6->ip6_plen) - stripsiz);
 
 		key_sa_recordxfer(sav, m);
+		ipsec_addhist(m, IPPROTO_ESP, spi);
 	}
 
 	*offp = off;
Index: netinet6/ip6_output.c
===================================================================
RCS file: /cvsroot/syssrc/sys/netinet6/ip6_output.c,v
retrieving revision 1.27
diff -u -r1.27 ip6_output.c
--- netinet6/ip6_output.c	2000/11/11 00:52:39	1.27
+++ netinet6/ip6_output.c	2001/01/23 04:54:06
@@ -862,6 +862,10 @@
 				m->m_pkthdr.len;
 		}
 #endif
+#ifdef IPSEC
+		/* clean ipsec history once it goes out of the node */
+		ipsec_delaux(m);
+#endif
 #ifdef OLDIP6OUTPUT
 		error = (*ifp->if_output)(ifp, m, (struct sockaddr *)dst,
 					  ro->ro_rt);
@@ -991,6 +995,10 @@
 				ia6->ia_ifa.ifa_data.ifad_outbytes +=
 					m->m_pkthdr.len;
 			}
+#endif
+#ifdef IPSEC
+			/* clean ipsec history once it goes out of the node */
+			ipsec_delaux(m);
 #endif
 #ifdef OLDIP6OUTPUT
 			error = (*ifp->if_output)(ifp, m,
Index: netinet6/ipcomp_input.c
===================================================================
RCS file: /cvsroot/syssrc/sys/netinet6/ipcomp_input.c,v
retrieving revision 1.14
diff -u -r1.14 ipcomp_input.c
--- netinet6/ipcomp_input.c	2000/10/02 03:55:43	1.14
+++ netinet6/ipcomp_input.c	2001/01/23 04:54:06
@@ -209,6 +209,7 @@
 
 	if (sav) {
 		key_sa_recordxfer(sav, m);
+		ipsec_addhist(m, IPPROTO_IPCOMP, (u_int32_t)cpi);
 		key_freesav(sav);
 		sav = NULL;
 	}
@@ -320,6 +321,7 @@
 
 	if (sav) {
 		key_sa_recordxfer(sav, m);
+		ipsec_addhist(m, IPPROTO_IPCOMP, (u_int32_t)cpi);
 		key_freesav(sav);
 		sav = NULL;
 	}
Index: netinet6/ipsec.c
===================================================================
RCS file: /cvsroot/syssrc/sys/netinet6/ipsec.c,v
retrieving revision 1.31
diff -u -r1.31 ipsec.c
--- netinet6/ipsec.c	2000/11/10 01:10:36	1.31
+++ netinet6/ipsec.c	2001/01/23 04:54:08
@@ -156,6 +156,9 @@
 #ifdef INET6
 static int ipsec6_encapsulate __P((struct mbuf *, struct secasvar *));
 #endif
+static struct mbuf *ipsec_addaux __P((struct mbuf *));
+static struct mbuf *ipsec_findaux __P((struct mbuf *));
+static void ipsec_optaux __P((struct mbuf *, struct mbuf *));
 
 /*
  * For OUTBOUND packet having a socket. Searching SPD for packet,
@@ -3266,27 +3269,74 @@
 	return(NULL);
 }
 
-void
-ipsec_setsocket(m, so)
+static struct mbuf *
+ipsec_addaux(m)
 	struct mbuf *m;
-	struct socket *so;
 {
 	struct mbuf *n;
 
 	n = m_aux_find(m, AF_INET, IPPROTO_ESP);
-	if (so && !n)
+	if (!n) {
 		n = m_aux_add(m, AF_INET, IPPROTO_ESP);
-	if (n) {
-		if (so) {
-			*mtod(n, struct socket **) = so;
-			/*
-			 * XXX think again about it when we put decryption
-			 * histrory into aux mbuf
-			 */
-			n->m_len = sizeof(struct socket *);
-		} else
-			m_aux_delete(m, n);
+		n->m_len = sizeof(struct socket *);
+		bzero(mtod(n, void *), n->m_len);
 	}
+	return n;
+}
+
+static struct mbuf *
+ipsec_findaux(m)
+	struct mbuf *m;
+{
+	struct mbuf *n;
+
+	n = m_aux_find(m, AF_INET, IPPROTO_ESP);
+#ifdef DIAGNOSTIC
+	if (n && n->m_len < sizeof(struct socket *))
+		panic("invalid ipsec m_aux");
+#endif
+	return n;
+}
+
+void
+ipsec_delaux(m)
+	struct mbuf *m;
+{
+	struct mbuf *n;
+
+	n = m_aux_find(m, AF_INET, IPPROTO_ESP);
+	if (n)
+		m_aux_delete(m, n);
+}
+
+/* if the aux buffer is unnecessary, nuke it. */
+static void
+ipsec_optaux(m, n)
+	struct mbuf *m;
+	struct mbuf *n;
+{
+
+	if (!n)
+		return;
+	if (n->m_len == sizeof(struct socket *) && !*mtod(n, struct socket **))
+		ipsec_delaux(m);
+}
+
+void
+ipsec_setsocket(m, so)
+	struct mbuf *m;
+	struct socket *so;
+{
+	struct mbuf *n;
+
+	/* if so == NULL, don't insist on getting the aux mbuf */
+	if (so)
+		n = ipsec_addaux(m);
+	else
+		n = ipsec_findaux(m);
+	if (n && n->m_len >= sizeof(struct socket *))
+		*mtod(n, struct socket **) = so;
+	ipsec_optaux(m, n);
 }
 
 struct socket *
@@ -3295,11 +3345,65 @@
 {
 	struct mbuf *n;
 
-	n = m_aux_find(m, AF_INET, IPPROTO_ESP);
+	n = ipsec_findaux(m);
 	if (n && n->m_len >= sizeof(struct socket *))
 		return *mtod(n, struct socket **);
 	else
 		return NULL;
+}
+
+void
+ipsec_addhist(m, proto, spi)
+	struct mbuf *m;
+	int proto;
+	u_int32_t spi;
+{
+	struct mbuf *n;
+	struct ipsec_history *p;
+
+	n = ipsec_addaux(m);
+	if (n && M_TRAILINGSPACE(n) > sizeof(*p)) {
+		p = (struct ipsec_history *)(mtod(n, caddr_t) + n->m_len);
+		n->m_len += sizeof(*p);
+		bzero(p, sizeof(*p));
+		p->ih_proto = proto;
+		p->ih_spi = spi;
+	}
+}
+
+struct ipsec_history *
+ipsec_gethist(m, lenp)
+	struct mbuf *m;
+	int *lenp;
+{
+	struct mbuf *n;
+	int l;
+
+	n = ipsec_findaux(m);
+	if (!n)
+		return NULL;
+	l = n->m_len;
+	if (sizeof(struct socket *) > l)
+		return NULL;
+	if ((l - sizeof(struct socket *)) % sizeof(struct ipsec_history))
+		return NULL;
+	/* XXX does it make more sense to divide by sizeof(ipsec_history)? */
+	if (lenp)
+		*lenp = l - sizeof(struct socket *);
+	return (struct ipsec_history *)
+	    (mtod(n, caddr_t) + sizeof(struct socket *));
+}
+
+void
+ipsec_clearhist(m)
+	struct mbuf *m;
+{
+	struct mbuf *n;
+
+	n = ipsec_findaux(m);
+	if ((n) && n->m_len > sizeof(struct socket *))
+		n->m_len = sizeof(struct socket *);
+	ipsec_optaux(m, n);
 }
 
 /*
Index: netinet6/ipsec.h
===================================================================
RCS file: /cvsroot/syssrc/sys/netinet6/ipsec.h,v
retrieving revision 1.18
diff -u -r1.18 ipsec.h
--- netinet6/ipsec.h	2001/01/04 11:48:44	1.18
+++ netinet6/ipsec.h	2001/01/23 04:54:08
@@ -261,6 +261,11 @@
 	struct sockaddr *dst;
 };
 
+struct ipsec_history {
+	int ih_proto;
+	u_int32_t ih_spi;
+};
+
 extern int ipsec_debug;
 
 #ifdef INET
@@ -366,8 +371,12 @@
 	struct secasvar *));
 #endif
 extern struct mbuf *ipsec_copypkt __P((struct mbuf *));
+extern void ipsec_delaux __P((struct mbuf *));
 extern void ipsec_setsocket __P((struct mbuf *, struct socket *));
 extern struct socket *ipsec_getsocket __P((struct mbuf *));
+extern void ipsec_addhist __P((struct mbuf *, int, u_int32_t)); 
+extern struct ipsec_history *ipsec_gethist __P((struct mbuf *, int *));
+extern void ipsec_clearhist __P((struct mbuf *));
 
 extern int ipsec_sysctl __P((int *, u_int, void *, size_t *, void *, size_t));
 extern int ipsec6_sysctl __P((int *, u_int, void *, size_t *, void *, size_t));