git: 0ff2d00d2aa3 - main - ipsec: allow it to work with unmapped mbufs

From: Konstantin Belousov <kib_at_FreeBSD.org>
Date: Mon, 13 Jan 2025 19:30:13 UTC
The branch main has been updated by kib:

URL: https://cgit.FreeBSD.org/src/commit/?id=0ff2d00d2aa37cd883ffd8c7363dddef9cba267e

commit 0ff2d00d2aa37cd883ffd8c7363dddef9cba267e
Author:     Konstantin Belousov <kib@FreeBSD.org>
AuthorDate: 2024-12-29 07:12:26 +0000
Commit:     Konstantin Belousov <kib@FreeBSD.org>
CommitDate: 2025-01-13 19:29:32 +0000

    ipsec: allow it to work with unmapped mbufs
    
    Only map mbuf when a policy is looked up and indicates that IPSEC needs
    to transform the packet.  If IPSEC is inline offloaded, it is up to the
    interface driver to request remap if needed.
    
    Fetch the IP header using m_copydata() instead of using mtod() to select
    policy/SA.
    
    Reviewed by:    markj
    Sponsored by:   NVidia networking
    Differential revision:  https://reviews.freebsd.org/D48265
---
 sys/net/if_ipsec.c           |  8 ++--
 sys/netinet/ip_output.c      | 22 +++-------
 sys/netinet6/ip6_output.c    | 14 -------
 sys/netipsec/ipsec.c         | 64 +++++++++++++++--------------
 sys/netipsec/ipsec.h         | 14 ++++---
 sys/netipsec/ipsec_output.c  | 96 ++++++++++++++++++++++++++++++--------------
 sys/netipsec/ipsec_support.h |  2 +
 sys/netipsec/subr_ipsec.c    | 18 ++-------
 sys/netipsec/xform_tcp.c     |  2 +-
 9 files changed, 126 insertions(+), 114 deletions(-)

diff --git a/sys/net/if_ipsec.c b/sys/net/if_ipsec.c
index 849127f75de6..df10af66471c 100644
--- a/sys/net/if_ipsec.c
+++ b/sys/net/if_ipsec.c
@@ -353,7 +353,7 @@ ipsec_transmit(struct ifnet *ifp, struct mbuf *m)
 	IPSEC_RLOCK_TRACKER;
 	struct ipsec_softc *sc;
 	struct secpolicy *sp;
-	struct ip *ip;
+	struct ip *ip, iph;
 	uint32_t af;
 	int error;
 
@@ -375,7 +375,8 @@ ipsec_transmit(struct ifnet *ifp, struct mbuf *m)
 	}
 
 	/* Determine address family to correctly handle packet in BPF */
-	ip = mtod(m, struct ip *);
+	ip = &iph;
+	m_copydata(m, 0, sizeof(*ip), (char *)ip);
 	switch (ip->ip_v) {
 #ifdef INET
 	case IPVERSION:
@@ -415,7 +416,8 @@ ipsec_transmit(struct ifnet *ifp, struct mbuf *m)
 	switch (af) {
 #ifdef INET
 	case AF_INET:
-		error = ipsec4_process_packet(ifp, m, sp, NULL, ifp->if_mtu);
+		error = ipsec4_process_packet(ifp, m, ip, sp, NULL,
+		    ifp->if_mtu);
 		break;
 #endif
 #ifdef INET6
diff --git a/sys/netinet/ip_output.c b/sys/netinet/ip_output.c
index 4f5d8b7279ba..9317d5d57e1d 100644
--- a/sys/netinet/ip_output.c
+++ b/sys/netinet/ip_output.c
@@ -667,26 +667,19 @@ again:
 sendit:
 #if defined(IPSEC) || defined(IPSEC_SUPPORT)
 	if (IPSEC_ENABLED(ipv4)) {
-		struct mbuf *m1;
+		struct ip ip_hdr;
 
-		error = mb_unmapped_to_ext(m, &m1);
-		if (error != 0) {
-			if (error == ENOMEM) {
-				IPSTAT_INC(ips_odropped);
-				error = ENOBUFS;
-				goto bad;
-			}
-			/* XXXKIB */
-			goto no_ipsec;
-		}
-		m = m1;
 		if ((error = IPSEC_OUTPUT(ipv4, ifp, m, inp, mtu)) != 0) {
 			if (error == EINPROGRESS)
 				error = 0;
 			goto done;
 		}
-no_ipsec:;
+
+		/* Update variables that are affected by ipsec4_output(). */
+		m_copydata(m, 0, sizeof(ip_hdr), (char *)&ip_hdr);
+		hlen = ip_hdr.ip_hl << 2;
 	}
+
 	/*
 	 * Check if there was a route for this packet; return error if not.
 	 */
@@ -695,9 +688,6 @@ no_ipsec:;
 		error = EHOSTUNREACH;
 		goto bad;
 	}
-	/* Update variables that are affected by ipsec4_output(). */
-	ip = mtod(m, struct ip *);
-	hlen = ip->ip_hl << 2;
 #endif /* IPSEC */
 
 	/* Jump over all PFIL processing if hooks are not active. */
diff --git a/sys/netinet6/ip6_output.c b/sys/netinet6/ip6_output.c
index c6907835bc67..c48101aa2990 100644
--- a/sys/netinet6/ip6_output.c
+++ b/sys/netinet6/ip6_output.c
@@ -792,26 +792,12 @@ nonh6lookup:
 	 * XXX: need scope argument.
 	 */
 	if (IPSEC_ENABLED(ipv6)) {
-		struct mbuf *m1;
-
-		error = mb_unmapped_to_ext(m, &m1);
-		if (error != 0) {
-			if (error == ENOMEM) {
-				IP6STAT_INC(ip6s_odropped);
-				error = ENOBUFS;
-				goto bad;
-			}
-			/* XXXKIB */
-			goto no_ipsec;
-		}
-		m = m1;
 		if ((error = IPSEC_OUTPUT(ipv6, ifp, m, inp, mtu == 0 ?
 		    ifp->if_mtu : mtu)) != 0) {
 			if (error == EINPROGRESS)
 				error = 0;
 			goto done;
 		}
-no_ipsec:;
 	}
 #endif /* IPSEC */
 
diff --git a/sys/netipsec/ipsec.c b/sys/netipsec/ipsec.c
index 9b18cedc7a80..8d604a24eeea 100644
--- a/sys/netipsec/ipsec.c
+++ b/sys/netipsec/ipsec.c
@@ -286,8 +286,9 @@ static int ipsec_in_reject(struct secpolicy *, struct inpcb *,
     const struct mbuf *);
 
 #ifdef INET
-static void ipsec4_get_ulp(const struct mbuf *, struct secpolicyindex *, int);
-static void ipsec4_setspidx_ipaddr(const struct mbuf *,
+static void ipsec4_get_ulp(const struct mbuf *, const struct ip *,
+    struct secpolicyindex *, int);
+static void ipsec4_setspidx_ipaddr(const struct mbuf *, struct ip *,
     struct secpolicyindex *);
 #endif
 #ifdef INET6
@@ -488,8 +489,8 @@ ipsec_getpcbpolicy(struct inpcb *inp, u_int dir)
 
 #ifdef INET
 static void
-ipsec4_get_ulp(const struct mbuf *m, struct secpolicyindex *spidx,
-    int needport)
+ipsec4_get_ulp(const struct mbuf *m, const struct ip *ip1,
+    struct secpolicyindex *spidx, int needport)
 {
 	uint8_t nxt;
 	int off;
@@ -498,21 +499,10 @@ ipsec4_get_ulp(const struct mbuf *m, struct secpolicyindex *spidx,
 	IPSEC_ASSERT(m->m_pkthdr.len >= sizeof(struct ip),
 	    ("packet too short"));
 
-	if (m->m_len >= sizeof (struct ip)) {
-		const struct ip *ip = mtod(m, const struct ip *);
-		if (ip->ip_off & htons(IP_MF | IP_OFFMASK))
-			goto done;
-		off = ip->ip_hl << 2;
-		nxt = ip->ip_p;
-	} else {
-		struct ip ih;
-
-		m_copydata(m, 0, sizeof (struct ip), (caddr_t) &ih);
-		if (ih.ip_off & htons(IP_MF | IP_OFFMASK))
-			goto done;
-		off = ih.ip_hl << 2;
-		nxt = ih.ip_p;
-	}
+	if (ip1->ip_off & htons(IP_MF | IP_OFFMASK))
+		goto done;
+	off = ip1->ip_hl << 2;
+	nxt = ip1->ip_p;
 
 	while (off < m->m_pkthdr.len) {
 		struct ip6_ext ip6e;
@@ -565,17 +555,18 @@ done_proto:
 }
 
 static void
-ipsec4_setspidx_ipaddr(const struct mbuf *m, struct secpolicyindex *spidx)
+ipsec4_setspidx_ipaddr(const struct mbuf *m, struct ip *ip1,
+    struct secpolicyindex *spidx)
 {
 
-	ipsec4_setsockaddrs(m, &spidx->src, &spidx->dst);
+	ipsec4_setsockaddrs(m, ip1, &spidx->src, &spidx->dst);
 	spidx->prefs = sizeof(struct in_addr) << 3;
 	spidx->prefd = sizeof(struct in_addr) << 3;
 }
 
 static struct secpolicy *
-ipsec4_getpolicy(const struct mbuf *m, struct inpcb *inp, u_int dir,
-    int needport)
+ipsec4_getpolicy(const struct mbuf *m, struct inpcb *inp, struct ip *ip1,
+    u_int dir, int needport)
 {
 	struct secpolicyindex spidx;
 	struct secpolicy *sp;
@@ -583,8 +574,8 @@ ipsec4_getpolicy(const struct mbuf *m, struct inpcb *inp, u_int dir,
 	sp = ipsec_getpcbpolicy(inp, dir);
 	if (sp == NULL && key_havesp(dir)) {
 		/* Make an index to look for a policy. */
-		ipsec4_setspidx_ipaddr(m, &spidx);
-		ipsec4_get_ulp(m, &spidx, needport);
+		ipsec4_setspidx_ipaddr(m, ip1, &spidx);
+		ipsec4_get_ulp(m, ip1, &spidx, needport);
 		spidx.dir = dir;
 		sp = key_allocsp(&spidx, dir);
 	}
@@ -597,13 +588,13 @@ ipsec4_getpolicy(const struct mbuf *m, struct inpcb *inp, u_int dir,
  * Check security policy for *OUTBOUND* IPv4 packet.
  */
 struct secpolicy *
-ipsec4_checkpolicy(const struct mbuf *m, struct inpcb *inp, int *error,
-    int needport)
+ipsec4_checkpolicy(const struct mbuf *m, struct inpcb *inp, struct ip *ip1,
+    int *error, int needport)
 {
 	struct secpolicy *sp;
 
 	*error = 0;
-	sp = ipsec4_getpolicy(m, inp, IPSEC_DIR_OUTBOUND, needport);
+	sp = ipsec4_getpolicy(m, inp, ip1, IPSEC_DIR_OUTBOUND, needport);
 	if (sp != NULL)
 		sp = ipsec_checkpolicy(sp, inp, error);
 	if (sp == NULL) {
@@ -630,12 +621,13 @@ ipsec4_checkpolicy(const struct mbuf *m, struct inpcb *inp, int *error,
  * rip_input() and sctp_input().
  */
 int
-ipsec4_in_reject(const struct mbuf *m, struct inpcb *inp)
+ipsec4_in_reject1(const struct mbuf *m, struct ip *ip1, struct inpcb *inp)
 {
 	struct secpolicy *sp;
 #ifdef IPSEC_OFFLOAD
 	struct ipsec_accel_in_tag *tag;
 #endif
+	struct ip ip_hdr;
 	int result;
 
 #ifdef IPSEC_OFFLOAD
@@ -643,7 +635,13 @@ ipsec4_in_reject(const struct mbuf *m, struct inpcb *inp)
 	if (tag != NULL)
 		return (0);
 #endif
-	sp = ipsec4_getpolicy(m, inp, IPSEC_DIR_INBOUND, 0);
+
+	if (ip1 == NULL) {
+		ip1 = &ip_hdr;
+		m_copydata(m, 0, sizeof(*ip1), (char *)ip1);
+	}
+
+	sp = ipsec4_getpolicy(m, inp, ip1, IPSEC_DIR_INBOUND, 0);
 	result = ipsec_in_reject(sp, inp, m);
 	key_freesp(&sp);
 	if (result != 0)
@@ -651,6 +649,12 @@ ipsec4_in_reject(const struct mbuf *m, struct inpcb *inp)
 	return (result);
 }
 
+int
+ipsec4_in_reject(const struct mbuf *m, struct inpcb *inp)
+{
+	return (ipsec4_in_reject1(m, NULL, inp));
+}
+
 /*
  * IPSEC_CAP() method implementation for IPv4.
  */
diff --git a/sys/netipsec/ipsec.h b/sys/netipsec/ipsec.h
index 8f3e8f02d88b..f8c5b10e7bd6 100644
--- a/sys/netipsec/ipsec.h
+++ b/sys/netipsec/ipsec.h
@@ -325,6 +325,7 @@ VNET_DECLARE(int, natt_cksum_policy);
 #endif
 
 struct inpcb;
+struct ip;
 struct m_tag;
 struct secasvar;
 struct sockopt;
@@ -336,7 +337,7 @@ int ipsec_if_input(struct mbuf *, struct secasvar *, uint32_t);
 struct ipsecrequest *ipsec_newisr(void);
 void ipsec_delisr(struct ipsecrequest *);
 struct secpolicy *ipsec4_checkpolicy(const struct mbuf *, struct inpcb *,
-    int *, int);
+    struct ip *, int *, int);
 
 u_int ipsec_get_reqlevel(struct secpolicy *, u_int);
 
@@ -351,12 +352,13 @@ size_t ipsec_hdrsiz_internal(struct secpolicy *);
 
 void ipsec_setspidx_inpcb(struct inpcb *, struct secpolicyindex *, u_int);
 
-void ipsec4_setsockaddrs(const struct mbuf *, union sockaddr_union *,
-    union sockaddr_union *);
+void ipsec4_setsockaddrs(const struct mbuf *, const struct ip *,
+    union sockaddr_union *, union sockaddr_union *);
 int ipsec4_common_input_cb(struct mbuf *, struct secasvar *, int, int);
-int ipsec4_check_pmtu(struct ifnet *, struct mbuf *, struct secpolicy *, int);
-int ipsec4_process_packet(struct ifnet *, struct mbuf *, struct secpolicy *,
-    struct inpcb *, u_long);
+int ipsec4_check_pmtu(struct ifnet *, struct mbuf *, struct ip *ip1,
+    struct secpolicy *, int);
+int ipsec4_process_packet(struct ifnet *, struct mbuf *, struct ip *ip1,
+    struct secpolicy *, struct inpcb *, u_long);
 int ipsec_process_done(struct mbuf *, struct secpolicy *, struct secasvar *,
     u_int);
 
diff --git a/sys/netipsec/ipsec_output.c b/sys/netipsec/ipsec_output.c
index 73a32c71fffc..8d8a304e7af4 100644
--- a/sys/netipsec/ipsec_output.c
+++ b/sys/netipsec/ipsec_output.c
@@ -111,14 +111,13 @@ static size_t ipsec_get_pmtu(struct secasvar *sav);
 
 #ifdef INET
 static struct secasvar *
-ipsec4_allocsa(struct ifnet *ifp, struct mbuf *m, struct secpolicy *sp,
-    u_int *pidx, int *error)
+ipsec4_allocsa(struct ifnet *ifp, struct mbuf *m, const struct ip *ip,
+    struct secpolicy *sp, u_int *pidx, int *error)
 {
 	struct secasindex *saidx, tmpsaidx;
 	struct ipsecrequest *isr;
 	struct sockaddr_in *sin;
 	struct secasvar *sav;
-	struct ip *ip;
 
 	/*
 	 * Check system global policy controls.
@@ -142,7 +141,6 @@ next:
 	if (isr->saidx.mode == IPSEC_MODE_TRANSPORT) {
 		saidx = &tmpsaidx;
 		*saidx = isr->saidx;
-		ip = mtod(m, struct ip *);
 		if (saidx->src.sa.sa_len == 0) {
 			sin = &saidx->src.sin;
 			sin->sin_len = sizeof(*sin);
@@ -188,13 +186,14 @@ next:
  * IPsec output logic for IPv4.
  */
 static int
-ipsec4_perform_request(struct ifnet *ifp, struct mbuf *m, struct secpolicy *sp,
-    struct inpcb *inp, u_int idx, u_long mtu)
+ipsec4_perform_request(struct ifnet *ifp, struct mbuf *m, struct ip *ip1,
+    struct secpolicy *sp, struct inpcb *inp, u_int idx, u_long mtu)
 {
 	struct ipsec_ctx_data ctx;
 	union sockaddr_union *dst;
 	struct secasvar *sav;
 	struct ip *ip;
+	struct mbuf *m1;
 	int error, hwassist, i, off;
 	bool accel;
 
@@ -209,7 +208,7 @@ ipsec4_perform_request(struct ifnet *ifp, struct mbuf *m, struct secpolicy *sp,
 	 * determine next transform. At the end of transform we can
 	 * release reference to SP.
 	 */
-	sav = ipsec4_allocsa(ifp, m, sp, &idx, &error);
+	sav = ipsec4_allocsa(ifp, m, ip1, sp, &idx, &error);
 	if (sav == NULL) {
 		if (error == EJUSTRETURN) { /* No IPsec required */
 			(void)ipsec_accel_output(ifp, m, inp, sp, NULL,
@@ -225,6 +224,8 @@ ipsec4_perform_request(struct ifnet *ifp, struct mbuf *m, struct secpolicy *sp,
 	IPSEC_INIT_CTX(&ctx, &m, inp, sav, AF_INET, IPSEC_ENC_BEFORE);
 	if ((error = ipsec_run_hhooks(&ctx, HHOOK_TYPE_IPSEC_OUT)) != 0)
 		goto bad;
+	/* Re-calculate *ip1 after potential change of m in the hook. */
+	m_copydata(m, 0, sizeof(*ip1), (char *)ip1);
 
 	hwassist = 0;
 	accel = ipsec_accel_output(ifp, m, inp, sp, sav, AF_INET, mtu,
@@ -240,16 +241,23 @@ ipsec4_perform_request(struct ifnet *ifp, struct mbuf *m, struct secpolicy *sp,
 	}
 #if defined(SCTP) || defined(SCTP_SUPPORT)
 	if ((m->m_pkthdr.csum_flags & CSUM_SCTP & ~hwassist) != 0) {
-		struct ip *ip;
-
-		ip = mtod(m, struct ip *);
-		sctp_delayed_cksum(m, (uint32_t)(ip->ip_hl << 2));
+		sctp_delayed_cksum(m, (uint32_t)(ip1->ip_hl << 2));
 		m->m_pkthdr.csum_flags &= ~CSUM_SCTP;
 	}
 #endif
 	if (accel)
 		return (EJUSTRETURN);
 
+	error = mb_unmapped_to_ext(m, &m1);
+	if (error != 0) {
+		if (error == EINVAL) {
+			if (bootverbose)
+				if_printf(ifp, "Tx TLS+IPSEC packet\n");
+		}
+		return (error);
+	}
+	m = m1;
+
 	ip = mtod(m, struct ip *);
 	dst = &sav->sah->saidx.dst;
 	/* Do the appropriate encapsulation, if necessary */
@@ -317,19 +325,18 @@ bad:
 }
 
 int
-ipsec4_process_packet(struct ifnet *ifp, struct mbuf *m, struct secpolicy *sp,
-    struct inpcb *inp, u_long mtu)
+ipsec4_process_packet(struct ifnet *ifp, struct mbuf *m, struct ip *ip1,
+    struct secpolicy *sp, struct inpcb *inp, u_long mtu)
 {
 
-	return (ipsec4_perform_request(ifp, m, sp, inp, 0, mtu));
+	return (ipsec4_perform_request(ifp, m, ip1, sp, inp, 0, mtu));
 }
 
 int
-ipsec4_check_pmtu(struct ifnet *ifp, struct mbuf *m, struct secpolicy *sp,
-    int forwarding)
+ipsec4_check_pmtu(struct ifnet *ifp, struct mbuf *m, struct ip *ip1,
+    struct secpolicy *sp, int forwarding)
 {
 	struct secasvar *sav;
-	struct ip *ip;
 	size_t hlen, pmtu;
 	uint32_t idx;
 	int error;
@@ -341,13 +348,12 @@ ipsec4_check_pmtu(struct ifnet *ifp, struct mbuf *m, struct secpolicy *sp,
 		goto setdf;
 
 	/* V_ip4_ipsec_dfbit > 1 - we will copy it from inner header. */
-	ip = mtod(m, struct ip *);
-	if (!(ip->ip_off & htons(IP_DF)))
+	if ((ip1->ip_off & htons(IP_DF)) == 0)
 		return (0);
 
 setdf:
 	idx = sp->tcount - 1;
-	sav = ipsec4_allocsa(ifp, m, sp, &idx, &error);
+	sav = ipsec4_allocsa(ifp, m, ip1, sp, &idx, &error);
 	if (sav == NULL) {
 		key_freesp(&sp);
 		/*
@@ -398,14 +404,14 @@ setdf:
 }
 
 static int
-ipsec4_common_output(struct ifnet *ifp, struct mbuf *m, struct inpcb *inp,
-    int forwarding, u_long mtu)
+ipsec4_common_output1(struct ifnet *ifp, struct mbuf *m, struct inpcb *inp,
+    struct ip *ip1, int forwarding, u_long mtu)
 {
 	struct secpolicy *sp;
 	int error;
 
 	/* Lookup for the corresponding outbound security policy */
-	sp = ipsec4_checkpolicy(m, inp, &error, !forwarding);
+	sp = ipsec4_checkpolicy(m, inp, ip1, &error, !forwarding);
 	if (sp == NULL) {
 		if (error == -EINVAL) {
 			/* Discarded by policy. */
@@ -425,7 +431,7 @@ ipsec4_common_output(struct ifnet *ifp, struct mbuf *m, struct inpcb *inp,
 	 */
 
 	/* NB: callee frees mbuf and releases reference to SP */
-	error = ipsec4_check_pmtu(ifp, m, sp, forwarding);
+	error = ipsec4_check_pmtu(ifp, m, ip1, sp, forwarding);
 	if (error != 0) {
 		if (error == EJUSTRETURN)
 			return (0);
@@ -433,7 +439,7 @@ ipsec4_common_output(struct ifnet *ifp, struct mbuf *m, struct inpcb *inp,
 		return (error);
 	}
 
-	error = ipsec4_process_packet(ifp, m, sp, inp, mtu);
+	error = ipsec4_process_packet(ifp, m, ip1, sp, inp, mtu);
 	if (error == EJUSTRETURN) {
 		/*
 		 * We had a SP with a level of 'use' and no SA. We
@@ -447,6 +453,28 @@ ipsec4_common_output(struct ifnet *ifp, struct mbuf *m, struct inpcb *inp,
 	return (error);
 }
 
+static int
+ipsec4_common_output(struct ifnet *ifp, struct mbuf *m, struct inpcb *inp,
+    struct ip *ip1, int forwarding, u_long mtu)
+{
+	struct ip ip_hdr;
+	struct ip *ip;
+
+	if (((m->m_flags & M_PKTHDR) != 0 && m->m_pkthdr.len < sizeof(*ip)) ||
+	    ((m->m_flags & M_PKTHDR) == 0 && m->m_len < sizeof(*ip))) {
+		m_free(m);
+		return (EACCES);
+	}
+	if (ip1 != NULL) {
+		ip = ip1;
+	} else {
+		ip = &ip_hdr;
+		m_copydata(m, 0, sizeof(*ip), (char *)ip);
+	}
+
+	return (ipsec4_common_output1(ifp, m, inp, ip, forwarding, mtu));
+}
+
 /*
  * IPSEC_OUTPUT() method implementation for IPv4.
  * 0 - no IPsec handling needed
@@ -464,7 +492,7 @@ ipsec4_output(struct ifnet *ifp, struct mbuf *m, struct inpcb *inp, u_long mtu)
 	if (m_tag_find(m, PACKET_TAG_IPSEC_OUT_DONE, NULL) != NULL)
 		return (0);
 
-	return (ipsec4_common_output(ifp, m, inp, 0, mtu));
+	return (ipsec4_common_output(ifp, m, inp, NULL, 0, mtu));
 }
 
 /*
@@ -475,16 +503,20 @@ ipsec4_output(struct ifnet *ifp, struct mbuf *m, struct inpcb *inp, u_long mtu)
 int
 ipsec4_forward(struct mbuf *m)
 {
+	struct ip ip_hdr;
+
+	m_copydata(m, 0, sizeof(ip_hdr), (char *)&ip_hdr);
 
 	/*
 	 * Check if this packet has an active inbound SP and needs to be
 	 * dropped instead of forwarded.
 	 */
-	if (ipsec4_in_reject(m, NULL) != 0) {
+	if (ipsec4_in_reject1(m, &ip_hdr, NULL) != 0) {
 		m_freem(m);
 		return (EACCES);
 	}
-	return (ipsec4_common_output(NULL /* XXXKIB */, m, NULL, 1, 0));
+	return (ipsec4_common_output(NULL /* XXXKIB */, m, NULL, &ip_hdr,
+	    1, 0));
 }
 #endif
 
@@ -874,6 +906,9 @@ ipsec_process_done(struct mbuf *m, struct secpolicy *sp, struct secasvar *sav,
 	struct xform_history *xh;
 	struct secasindex *saidx;
 	struct m_tag *mtag;
+#ifdef INET
+	struct ip *ip;
+#endif
 	int error;
 
 	if (sav->state >= SADB_SASTATE_DEAD) {
@@ -884,8 +919,9 @@ ipsec_process_done(struct mbuf *m, struct secpolicy *sp, struct secasvar *sav,
 	switch (saidx->dst.sa.sa_family) {
 #ifdef INET
 	case AF_INET:
+		ip = mtod(m, struct ip *);
 		/* Fix the header length, for AH processing. */
-		mtod(m, struct ip *)->ip_len = htons(m->m_pkthdr.len);
+		ip->ip_len = htons(m->m_pkthdr.len);
 		break;
 #endif /* INET */
 #ifdef INET6
@@ -943,7 +979,7 @@ ipsec_process_done(struct mbuf *m, struct secpolicy *sp, struct secasvar *sav,
 		case AF_INET:
 			key_freesav(&sav);
 			IPSECSTAT_INC(ips_out_bundlesa);
-			return (ipsec4_perform_request(NULL, m, sp, NULL,
+			return (ipsec4_perform_request(NULL, m, ip, sp, NULL,
 			    idx, 0));
 			/* NOTREACHED */
 #endif
diff --git a/sys/netipsec/ipsec_support.h b/sys/netipsec/ipsec_support.h
index 0cbfe6ddfee5..26f5e8a82d87 100644
--- a/sys/netipsec/ipsec_support.h
+++ b/sys/netipsec/ipsec_support.h
@@ -30,6 +30,7 @@
 #ifdef _KERNEL
 #if defined(IPSEC) || defined(IPSEC_SUPPORT)
 struct ifnet;
+struct ip;
 struct mbuf;
 struct inpcb;
 struct tcphdr;
@@ -56,6 +57,7 @@ int udp_ipsec_pcbctl(struct inpcb *, struct sockopt *);
 #endif
 #ifdef INET
 int ipsec4_in_reject(const struct mbuf *, struct inpcb *);
+int ipsec4_in_reject1(const struct mbuf *m, struct ip *ip1, struct inpcb *inp);
 int ipsec4_input(struct mbuf *, int, int);
 int ipsec4_forward(struct mbuf *);
 int ipsec4_pcbctl(struct inpcb *, struct sockopt *);
diff --git a/sys/netipsec/subr_ipsec.c b/sys/netipsec/subr_ipsec.c
index 2f7e0f09882e..bb8b62a841c6 100644
--- a/sys/netipsec/subr_ipsec.c
+++ b/sys/netipsec/subr_ipsec.c
@@ -61,8 +61,8 @@
 
 #ifdef INET
 void
-ipsec4_setsockaddrs(const struct mbuf *m, union sockaddr_union *src,
-    union sockaddr_union *dst)
+ipsec4_setsockaddrs(const struct mbuf *m, const struct ip *ip1,
+    union sockaddr_union *src, union sockaddr_union *dst)
 {
 	static const struct sockaddr_in template = {
 		sizeof (struct sockaddr_in),
@@ -73,18 +73,8 @@ ipsec4_setsockaddrs(const struct mbuf *m, union sockaddr_union *src,
 	src->sin = template;
 	dst->sin = template;
 
-	if (m->m_len < sizeof (struct ip)) {
-		m_copydata(m, offsetof(struct ip, ip_src),
-			   sizeof (struct  in_addr),
-			   (caddr_t) &src->sin.sin_addr);
-		m_copydata(m, offsetof(struct ip, ip_dst),
-			   sizeof (struct  in_addr),
-			   (caddr_t) &dst->sin.sin_addr);
-	} else {
-		const struct ip *ip = mtod(m, const struct ip *);
-		src->sin.sin_addr = ip->ip_src;
-		dst->sin.sin_addr = ip->ip_dst;
-	}
+	src->sin.sin_addr = ip1->ip_src;
+	dst->sin.sin_addr = ip1->ip_dst;
 }
 #endif
 #ifdef INET6
diff --git a/sys/netipsec/xform_tcp.c b/sys/netipsec/xform_tcp.c
index d52051d415a8..d3d4d6c4d734 100644
--- a/sys/netipsec/xform_tcp.c
+++ b/sys/netipsec/xform_tcp.c
@@ -240,7 +240,7 @@ setsockaddrs(const struct mbuf *m, union sockaddr_union *src,
 	switch (ip->ip_v) {
 #ifdef INET
 	case IPVERSION:
-		ipsec4_setsockaddrs(m, src, dst);
+		ipsec4_setsockaddrs(m, ip, src, dst);
 		break;
 #endif
 #ifdef INET6