diff options
| author | Konstantin Belousov <kib@FreeBSD.org> | 2024-12-29 07:12:26 +0000 |
|---|---|---|
| committer | Konstantin Belousov <kib@FreeBSD.org> | 2025-01-13 19:29:32 +0000 |
| commit | 0ff2d00d2aa37cd883ffd8c7363dddef9cba267e (patch) | |
| tree | f2907311f4ad3bf405f3549722286463ab6b760a /sys/netipsec | |
| parent | b0e020764aae970545357b0f146dcba7b4b55864 (diff) | |
Diffstat (limited to 'sys/netipsec')
| -rw-r--r-- | sys/netipsec/ipsec.c | 64 | ||||
| -rw-r--r-- | sys/netipsec/ipsec.h | 14 | ||||
| -rw-r--r-- | sys/netipsec/ipsec_output.c | 96 | ||||
| -rw-r--r-- | sys/netipsec/ipsec_support.h | 2 | ||||
| -rw-r--r-- | sys/netipsec/subr_ipsec.c | 18 | ||||
| -rw-r--r-- | sys/netipsec/xform_tcp.c | 2 |
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 |
