aboutsummaryrefslogtreecommitdiff
path: root/sys/netipsec
diff options
context:
space:
mode:
authorKonstantin Belousov <kib@FreeBSD.org>2024-12-29 07:12:26 +0000
committerKonstantin Belousov <kib@FreeBSD.org>2025-01-13 19:29:32 +0000
commit0ff2d00d2aa37cd883ffd8c7363dddef9cba267e (patch)
treef2907311f4ad3bf405f3549722286463ab6b760a /sys/netipsec
parentb0e020764aae970545357b0f146dcba7b4b55864 (diff)
Diffstat (limited to 'sys/netipsec')
-rw-r--r--sys/netipsec/ipsec.c64
-rw-r--r--sys/netipsec/ipsec.h14
-rw-r--r--sys/netipsec/ipsec_output.c96
-rw-r--r--sys/netipsec/ipsec_support.h2
-rw-r--r--sys/netipsec/subr_ipsec.c18
-rw-r--r--sys/netipsec/xform_tcp.c2
6 files changed, 115 insertions, 81 deletions
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