1aaea26efSSam Leffler /*-
24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
3fe267a55SPedro F. Giffuni *
4aaea26efSSam Leffler * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
5fcf59617SAndrey V. Elsukov * Copyright (c) 2016 Andrey V. Elsukov <ae@FreeBSD.org>
6aaea26efSSam Leffler * All rights reserved.
7aaea26efSSam Leffler *
8aaea26efSSam Leffler * Redistribution and use in source and binary forms, with or without
9aaea26efSSam Leffler * modification, are permitted provided that the following conditions
10aaea26efSSam Leffler * are met:
11aaea26efSSam Leffler * 1. Redistributions of source code must retain the above copyright
12aaea26efSSam Leffler * notice, this list of conditions and the following disclaimer.
13aaea26efSSam Leffler * 2. Redistributions in binary form must reproduce the above copyright
14aaea26efSSam Leffler * notice, this list of conditions and the following disclaimer in the
15aaea26efSSam Leffler * documentation and/or other materials provided with the distribution.
16aaea26efSSam Leffler *
17aaea26efSSam Leffler * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18aaea26efSSam Leffler * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19aaea26efSSam Leffler * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20aaea26efSSam Leffler * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21aaea26efSSam Leffler * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22aaea26efSSam Leffler * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23aaea26efSSam Leffler * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24aaea26efSSam Leffler * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25aaea26efSSam Leffler * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26aaea26efSSam Leffler * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27aaea26efSSam Leffler * SUCH DAMAGE.
28aaea26efSSam Leffler */
2988768458SSam Leffler
3088768458SSam Leffler /*
3188768458SSam Leffler * IPsec output processing.
3288768458SSam Leffler */
3388768458SSam Leffler #include "opt_inet.h"
3488768458SSam Leffler #include "opt_inet6.h"
3588768458SSam Leffler #include "opt_ipsec.h"
36fcf59617SAndrey V. Elsukov #include "opt_sctp.h"
3788768458SSam Leffler
3888768458SSam Leffler #include <sys/param.h>
3988768458SSam Leffler #include <sys/systm.h>
4088768458SSam Leffler #include <sys/mbuf.h>
4188768458SSam Leffler #include <sys/domain.h>
4288768458SSam Leffler #include <sys/protosw.h>
4388768458SSam Leffler #include <sys/socket.h>
4488768458SSam Leffler #include <sys/errno.h>
45ef91a976SAndrey V. Elsukov #include <sys/hhook.h>
4688768458SSam Leffler #include <sys/syslog.h>
4788768458SSam Leffler
4888768458SSam Leffler #include <net/if.h>
49ef91a976SAndrey V. Elsukov #include <net/if_enc.h>
5076039bc8SGleb Smirnoff #include <net/if_var.h>
51eddfbb76SRobert Watson #include <net/vnet.h>
5288768458SSam Leffler
5388768458SSam Leffler #include <netinet/in.h>
54e68b3792SGleb Smirnoff #include <netinet/in_pcb.h>
5588768458SSam Leffler #include <netinet/in_systm.h>
5688768458SSam Leffler #include <netinet/ip.h>
5788768458SSam Leffler #include <netinet/ip_var.h>
5888768458SSam Leffler #include <netinet/in_var.h>
5988768458SSam Leffler #include <netinet/ip_ecn.h>
6088768458SSam Leffler #ifdef INET6
6188768458SSam Leffler #include <netinet6/ip6_ecn.h>
6288768458SSam Leffler #endif
636b66194bSKornel Duleba #include <netinet/ip_icmp.h>
646b66194bSKornel Duleba #include <netinet/tcp_var.h>
6588768458SSam Leffler
6688768458SSam Leffler #include <netinet/ip6.h>
6788768458SSam Leffler #ifdef INET6
6888768458SSam Leffler #include <netinet6/ip6_var.h>
6961f37615SAndrey V. Elsukov #include <netinet6/scope6_var.h>
7088768458SSam Leffler #endif
7188768458SSam Leffler #include <netinet/in_pcb.h>
7288768458SSam Leffler #ifdef INET6
7388768458SSam Leffler #include <netinet/icmp6.h>
7488768458SSam Leffler #endif
7595033af9SMark Johnston #if defined(SCTP) || defined(SCTP_SUPPORT)
76fcf59617SAndrey V. Elsukov #include <netinet/sctp_crc32.h>
77fcf59617SAndrey V. Elsukov #endif
7888768458SSam Leffler
79fcf59617SAndrey V. Elsukov #include <netinet/udp.h>
80fcf59617SAndrey V. Elsukov #include <netipsec/ah.h>
81fcf59617SAndrey V. Elsukov #include <netipsec/esp.h>
8288768458SSam Leffler #include <netipsec/ipsec.h>
8388768458SSam Leffler #ifdef INET6
8488768458SSam Leffler #include <netipsec/ipsec6.h>
8588768458SSam Leffler #endif
86809fef29SGleb Smirnoff #include <netipsec/ipsec_support.h>
87ef2a572bSKonstantin Belousov #include <netipsec/ipsec_offload.h>
8888768458SSam Leffler #include <netipsec/ah_var.h>
8988768458SSam Leffler #include <netipsec/esp_var.h>
9088768458SSam Leffler #include <netipsec/ipcomp_var.h>
9188768458SSam Leffler
9288768458SSam Leffler #include <netipsec/xform.h>
9388768458SSam Leffler
9488768458SSam Leffler #include <netipsec/key.h>
9588768458SSam Leffler #include <netipsec/keydb.h>
9688768458SSam Leffler #include <netipsec/key_debug.h>
9788768458SSam Leffler
9888768458SSam Leffler #include <machine/in_cksum.h>
9988768458SSam Leffler
100fcf59617SAndrey V. Elsukov #define IPSEC_OSTAT_INC(proto, name) do { \
101fcf59617SAndrey V. Elsukov if ((proto) == IPPROTO_ESP) \
102fcf59617SAndrey V. Elsukov ESPSTAT_INC(esps_##name); \
103fcf59617SAndrey V. Elsukov else if ((proto) == IPPROTO_AH)\
104fcf59617SAndrey V. Elsukov AHSTAT_INC(ahs_##name); \
105fcf59617SAndrey V. Elsukov else \
106fcf59617SAndrey V. Elsukov IPCOMPSTAT_INC(ipcomps_##name); \
107fcf59617SAndrey V. Elsukov } while (0)
108fcf59617SAndrey V. Elsukov
109fcf59617SAndrey V. Elsukov static int ipsec_encap(struct mbuf **mp, struct secasindex *saidx);
1109dfc8606SBartlomiej Grzesik static size_t ipsec_get_pmtu(struct secasvar *sav);
111fcf59617SAndrey V. Elsukov
112fcf59617SAndrey V. Elsukov #ifdef INET
113fcf59617SAndrey V. Elsukov static struct secasvar *
ipsec4_allocsa(struct ifnet * ifp,struct mbuf * m,const struct ip * ip,struct secpolicy * sp,u_int * pidx,int * error)1140ff2d00dSKonstantin Belousov ipsec4_allocsa(struct ifnet *ifp, struct mbuf *m, const struct ip *ip,
1150ff2d00dSKonstantin Belousov struct secpolicy *sp, u_int *pidx, int *error)
116fcf59617SAndrey V. Elsukov {
117fcf59617SAndrey V. Elsukov struct secasindex *saidx, tmpsaidx;
118fcf59617SAndrey V. Elsukov struct ipsecrequest *isr;
119fcf59617SAndrey V. Elsukov struct sockaddr_in *sin;
120fcf59617SAndrey V. Elsukov struct secasvar *sav;
121fcf59617SAndrey V. Elsukov
122fcf59617SAndrey V. Elsukov /*
123fcf59617SAndrey V. Elsukov * Check system global policy controls.
124fcf59617SAndrey V. Elsukov */
125fcf59617SAndrey V. Elsukov next:
126fcf59617SAndrey V. Elsukov isr = sp->req[*pidx];
127fcf59617SAndrey V. Elsukov if ((isr->saidx.proto == IPPROTO_ESP && !V_esp_enable) ||
128fcf59617SAndrey V. Elsukov (isr->saidx.proto == IPPROTO_AH && !V_ah_enable) ||
129fcf59617SAndrey V. Elsukov (isr->saidx.proto == IPPROTO_IPCOMP && !V_ipcomp_enable)) {
130fcf59617SAndrey V. Elsukov DPRINTF(("%s: IPsec outbound packet dropped due"
131fcf59617SAndrey V. Elsukov " to policy (check your sysctls)\n", __func__));
132fcf59617SAndrey V. Elsukov IPSEC_OSTAT_INC(isr->saidx.proto, pdrops);
133fcf59617SAndrey V. Elsukov *error = EHOSTUNREACH;
134fcf59617SAndrey V. Elsukov return (NULL);
135fcf59617SAndrey V. Elsukov }
136fcf59617SAndrey V. Elsukov /*
137fcf59617SAndrey V. Elsukov * Craft SA index to search for proper SA. Note that
138fcf59617SAndrey V. Elsukov * we only initialize unspecified SA peers for transport
139fcf59617SAndrey V. Elsukov * mode; for tunnel mode they must already be filled in.
140fcf59617SAndrey V. Elsukov */
141fcf59617SAndrey V. Elsukov if (isr->saidx.mode == IPSEC_MODE_TRANSPORT) {
142fcf59617SAndrey V. Elsukov saidx = &tmpsaidx;
143fcf59617SAndrey V. Elsukov *saidx = isr->saidx;
144fcf59617SAndrey V. Elsukov if (saidx->src.sa.sa_len == 0) {
145fcf59617SAndrey V. Elsukov sin = &saidx->src.sin;
146fcf59617SAndrey V. Elsukov sin->sin_len = sizeof(*sin);
147fcf59617SAndrey V. Elsukov sin->sin_family = AF_INET;
148fcf59617SAndrey V. Elsukov sin->sin_port = IPSEC_PORT_ANY;
149fcf59617SAndrey V. Elsukov sin->sin_addr = ip->ip_src;
150fcf59617SAndrey V. Elsukov }
151fcf59617SAndrey V. Elsukov if (saidx->dst.sa.sa_len == 0) {
152fcf59617SAndrey V. Elsukov sin = &saidx->dst.sin;
153fcf59617SAndrey V. Elsukov sin->sin_len = sizeof(*sin);
154fcf59617SAndrey V. Elsukov sin->sin_family = AF_INET;
155fcf59617SAndrey V. Elsukov sin->sin_port = IPSEC_PORT_ANY;
156fcf59617SAndrey V. Elsukov sin->sin_addr = ip->ip_dst;
157fcf59617SAndrey V. Elsukov }
158fcf59617SAndrey V. Elsukov } else
159fcf59617SAndrey V. Elsukov saidx = &sp->req[*pidx]->saidx;
160fcf59617SAndrey V. Elsukov /*
161fcf59617SAndrey V. Elsukov * Lookup SA and validate it.
162fcf59617SAndrey V. Elsukov */
163fcf59617SAndrey V. Elsukov sav = key_allocsa_policy(sp, saidx, error);
164fcf59617SAndrey V. Elsukov if (sav == NULL) {
165fcf59617SAndrey V. Elsukov IPSECSTAT_INC(ips_out_nosa);
166fcf59617SAndrey V. Elsukov if (*error != 0)
167fcf59617SAndrey V. Elsukov return (NULL);
168fcf59617SAndrey V. Elsukov if (ipsec_get_reqlevel(sp, *pidx) != IPSEC_LEVEL_REQUIRE) {
169fcf59617SAndrey V. Elsukov /*
170fcf59617SAndrey V. Elsukov * We have no SA and policy that doesn't require
171fcf59617SAndrey V. Elsukov * this IPsec transform, thus we can continue w/o
172fcf59617SAndrey V. Elsukov * IPsec processing, i.e. return EJUSTRETURN.
173fcf59617SAndrey V. Elsukov * But first check if there is some bundled transform.
174fcf59617SAndrey V. Elsukov */
175fcf59617SAndrey V. Elsukov if (sp->tcount > ++(*pidx))
176fcf59617SAndrey V. Elsukov goto next;
177fcf59617SAndrey V. Elsukov *error = EJUSTRETURN;
178fcf59617SAndrey V. Elsukov }
179fcf59617SAndrey V. Elsukov return (NULL);
180fcf59617SAndrey V. Elsukov }
181fcf59617SAndrey V. Elsukov IPSEC_ASSERT(sav->tdb_xform != NULL, ("SA with NULL tdb_xform"));
182fcf59617SAndrey V. Elsukov return (sav);
183fcf59617SAndrey V. Elsukov }
184fcf59617SAndrey V. Elsukov
185fcf59617SAndrey V. Elsukov /*
186fcf59617SAndrey V. Elsukov * IPsec output logic for IPv4.
187fcf59617SAndrey V. Elsukov */
188fcf59617SAndrey V. Elsukov static int
ipsec4_perform_request(struct ifnet * ifp,struct mbuf * m,struct ip * ip1,struct secpolicy * sp,struct inpcb * inp,u_int idx,u_long mtu)1890ff2d00dSKonstantin Belousov ipsec4_perform_request(struct ifnet *ifp, struct mbuf *m, struct ip *ip1,
1900ff2d00dSKonstantin Belousov struct secpolicy *sp, struct inpcb *inp, u_int idx, u_long mtu)
191fcf59617SAndrey V. Elsukov {
192fcf59617SAndrey V. Elsukov struct ipsec_ctx_data ctx;
193fcf59617SAndrey V. Elsukov union sockaddr_union *dst;
194fcf59617SAndrey V. Elsukov struct secasvar *sav;
195fcf59617SAndrey V. Elsukov struct ip *ip;
1960ff2d00dSKonstantin Belousov struct mbuf *m1;
197240b7bfeSKonstantin Belousov int error, hwassist, i, off;
198240b7bfeSKonstantin Belousov bool accel;
199fcf59617SAndrey V. Elsukov
200fcf59617SAndrey V. Elsukov IPSEC_ASSERT(idx < sp->tcount, ("Wrong IPsec request index %d", idx));
201fcf59617SAndrey V. Elsukov
202fcf59617SAndrey V. Elsukov /*
203fcf59617SAndrey V. Elsukov * We hold the reference to SP. Content of SP couldn't be changed.
204fcf59617SAndrey V. Elsukov * Craft secasindex and do lookup for suitable SA.
205fcf59617SAndrey V. Elsukov * Then do encapsulation if needed and call xform's output.
206fcf59617SAndrey V. Elsukov * We need to store SP in the xform callback parameters.
207fcf59617SAndrey V. Elsukov * In xform callback we will extract SP and it can be used to
208fcf59617SAndrey V. Elsukov * determine next transform. At the end of transform we can
209fcf59617SAndrey V. Elsukov * release reference to SP.
210fcf59617SAndrey V. Elsukov */
2110ff2d00dSKonstantin Belousov sav = ipsec4_allocsa(ifp, m, ip1, sp, &idx, &error);
212fcf59617SAndrey V. Elsukov if (sav == NULL) {
213fcf59617SAndrey V. Elsukov if (error == EJUSTRETURN) { /* No IPsec required */
214ef2a572bSKonstantin Belousov (void)ipsec_accel_output(ifp, m, inp, sp, NULL,
215240b7bfeSKonstantin Belousov AF_INET, mtu, &hwassist);
216fcf59617SAndrey V. Elsukov key_freesp(&sp);
217fcf59617SAndrey V. Elsukov return (error);
218fcf59617SAndrey V. Elsukov }
219fcf59617SAndrey V. Elsukov goto bad;
220fcf59617SAndrey V. Elsukov }
221fcf59617SAndrey V. Elsukov /*
222fcf59617SAndrey V. Elsukov * XXXAE: most likely ip_sum at this point is wrong.
223fcf59617SAndrey V. Elsukov */
2241a01e0e7SAndrey V. Elsukov IPSEC_INIT_CTX(&ctx, &m, inp, sav, AF_INET, IPSEC_ENC_BEFORE);
225fcf59617SAndrey V. Elsukov if ((error = ipsec_run_hhooks(&ctx, HHOOK_TYPE_IPSEC_OUT)) != 0)
226fcf59617SAndrey V. Elsukov goto bad;
2270ff2d00dSKonstantin Belousov /* Re-calculate *ip1 after potential change of m in the hook. */
2280ff2d00dSKonstantin Belousov m_copydata(m, 0, sizeof(*ip1), (char *)ip1);
229fcf59617SAndrey V. Elsukov
230240b7bfeSKonstantin Belousov hwassist = 0;
231240b7bfeSKonstantin Belousov accel = ipsec_accel_output(ifp, m, inp, sp, sav, AF_INET, mtu,
232240b7bfeSKonstantin Belousov &hwassist);
233240b7bfeSKonstantin Belousov
234240b7bfeSKonstantin Belousov /*
235240b7bfeSKonstantin Belousov * Do delayed checksums now because we send before
236240b7bfeSKonstantin Belousov * this is done in the normal processing path.
237240b7bfeSKonstantin Belousov */
238240b7bfeSKonstantin Belousov if ((m->m_pkthdr.csum_flags & CSUM_DELAY_DATA & ~hwassist) != 0) {
239240b7bfeSKonstantin Belousov in_delayed_cksum(m);
240240b7bfeSKonstantin Belousov m->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA;
241240b7bfeSKonstantin Belousov }
242240b7bfeSKonstantin Belousov #if defined(SCTP) || defined(SCTP_SUPPORT)
243240b7bfeSKonstantin Belousov if ((m->m_pkthdr.csum_flags & CSUM_SCTP & ~hwassist) != 0) {
2440ff2d00dSKonstantin Belousov sctp_delayed_cksum(m, (uint32_t)(ip1->ip_hl << 2));
245240b7bfeSKonstantin Belousov m->m_pkthdr.csum_flags &= ~CSUM_SCTP;
246240b7bfeSKonstantin Belousov }
247240b7bfeSKonstantin Belousov #endif
248240b7bfeSKonstantin Belousov if (accel)
249ef2a572bSKonstantin Belousov return (EJUSTRETURN);
250ef2a572bSKonstantin Belousov
2510ff2d00dSKonstantin Belousov error = mb_unmapped_to_ext(m, &m1);
2520ff2d00dSKonstantin Belousov if (error != 0) {
2530ff2d00dSKonstantin Belousov if (error == EINVAL) {
2540ff2d00dSKonstantin Belousov if (bootverbose)
2550ff2d00dSKonstantin Belousov if_printf(ifp, "Tx TLS+IPSEC packet\n");
2560ff2d00dSKonstantin Belousov }
2570ff2d00dSKonstantin Belousov return (error);
2580ff2d00dSKonstantin Belousov }
2590ff2d00dSKonstantin Belousov m = m1;
2600ff2d00dSKonstantin Belousov
261fcf59617SAndrey V. Elsukov ip = mtod(m, struct ip *);
262fcf59617SAndrey V. Elsukov dst = &sav->sah->saidx.dst;
263fcf59617SAndrey V. Elsukov /* Do the appropriate encapsulation, if necessary */
264fcf59617SAndrey V. Elsukov if (sp->req[idx]->saidx.mode == IPSEC_MODE_TUNNEL || /* Tunnel requ'd */
265fcf59617SAndrey V. Elsukov dst->sa.sa_family != AF_INET || /* PF mismatch */
266fcf59617SAndrey V. Elsukov (dst->sa.sa_family == AF_INET && /* Proxy */
267fcf59617SAndrey V. Elsukov dst->sin.sin_addr.s_addr != INADDR_ANY &&
268fcf59617SAndrey V. Elsukov dst->sin.sin_addr.s_addr != ip->ip_dst.s_addr)) {
269fcf59617SAndrey V. Elsukov /* Fix IPv4 header checksum and length */
270fcf59617SAndrey V. Elsukov ip->ip_len = htons(m->m_pkthdr.len);
271fcf59617SAndrey V. Elsukov ip->ip_sum = 0;
272fcf59617SAndrey V. Elsukov ip->ip_sum = in_cksum(m, ip->ip_hl << 2);
273fcf59617SAndrey V. Elsukov error = ipsec_encap(&m, &sav->sah->saidx);
274fcf59617SAndrey V. Elsukov if (error != 0) {
2757f1f6591SAndrey V. Elsukov DPRINTF(("%s: encapsulation for SPI 0x%08x failed "
2767f1f6591SAndrey V. Elsukov "with error %d\n", __func__, ntohl(sav->spi),
2777f1f6591SAndrey V. Elsukov error));
278fcf59617SAndrey V. Elsukov /* XXXAE: IPSEC_OSTAT_INC(tunnel); */
279fcf59617SAndrey V. Elsukov goto bad;
280fcf59617SAndrey V. Elsukov }
2811a01e0e7SAndrey V. Elsukov inp = NULL;
282fcf59617SAndrey V. Elsukov }
283fcf59617SAndrey V. Elsukov
2841a01e0e7SAndrey V. Elsukov IPSEC_INIT_CTX(&ctx, &m, inp, sav, dst->sa.sa_family, IPSEC_ENC_AFTER);
285fcf59617SAndrey V. Elsukov if ((error = ipsec_run_hhooks(&ctx, HHOOK_TYPE_IPSEC_OUT)) != 0)
286fcf59617SAndrey V. Elsukov goto bad;
287fcf59617SAndrey V. Elsukov
288fcf59617SAndrey V. Elsukov /*
289fcf59617SAndrey V. Elsukov * Dispatch to the appropriate IPsec transform logic. The
290fcf59617SAndrey V. Elsukov * packet will be returned for transmission after crypto
291fcf59617SAndrey V. Elsukov * processing, etc. are completed.
292fcf59617SAndrey V. Elsukov *
293fcf59617SAndrey V. Elsukov * NB: m & sav are ``passed to caller'' who's responsible for
294fcf59617SAndrey V. Elsukov * reclaiming their resources.
295fcf59617SAndrey V. Elsukov */
296fcf59617SAndrey V. Elsukov switch(dst->sa.sa_family) {
297fcf59617SAndrey V. Elsukov case AF_INET:
298fcf59617SAndrey V. Elsukov ip = mtod(m, struct ip *);
299fcf59617SAndrey V. Elsukov i = ip->ip_hl << 2;
300fcf59617SAndrey V. Elsukov off = offsetof(struct ip, ip_p);
301fcf59617SAndrey V. Elsukov break;
302fcf59617SAndrey V. Elsukov #ifdef INET6
303fcf59617SAndrey V. Elsukov case AF_INET6:
304fcf59617SAndrey V. Elsukov i = sizeof(struct ip6_hdr);
305fcf59617SAndrey V. Elsukov off = offsetof(struct ip6_hdr, ip6_nxt);
306fcf59617SAndrey V. Elsukov break;
307fcf59617SAndrey V. Elsukov #endif /* INET6 */
308fcf59617SAndrey V. Elsukov default:
309fcf59617SAndrey V. Elsukov DPRINTF(("%s: unsupported protocol family %u\n",
310fcf59617SAndrey V. Elsukov __func__, dst->sa.sa_family));
311fcf59617SAndrey V. Elsukov error = EPFNOSUPPORT;
312fcf59617SAndrey V. Elsukov IPSEC_OSTAT_INC(sav->sah->saidx.proto, nopf);
313fcf59617SAndrey V. Elsukov goto bad;
314fcf59617SAndrey V. Elsukov }
315fcf59617SAndrey V. Elsukov error = (*sav->tdb_xform->xf_output)(m, sp, sav, idx, i, off);
316fcf59617SAndrey V. Elsukov return (error);
317fcf59617SAndrey V. Elsukov bad:
318fcf59617SAndrey V. Elsukov IPSECSTAT_INC(ips_out_inval);
319fcf59617SAndrey V. Elsukov if (m != NULL)
320fcf59617SAndrey V. Elsukov m_freem(m);
321fcf59617SAndrey V. Elsukov if (sav != NULL)
322fcf59617SAndrey V. Elsukov key_freesav(&sav);
323fcf59617SAndrey V. Elsukov key_freesp(&sp);
324fcf59617SAndrey V. Elsukov return (error);
325fcf59617SAndrey V. Elsukov }
3267b495c44SVANHULLEBUS Yvan
32788768458SSam Leffler int
ipsec4_process_packet(struct ifnet * ifp,struct mbuf * m,struct ip * ip1,struct secpolicy * sp,struct inpcb * inp,u_long mtu)3280ff2d00dSKonstantin Belousov ipsec4_process_packet(struct ifnet *ifp, struct mbuf *m, struct ip *ip1,
3290ff2d00dSKonstantin Belousov struct secpolicy *sp, struct inpcb *inp, u_long mtu)
33088768458SSam Leffler {
331fcf59617SAndrey V. Elsukov
3320ff2d00dSKonstantin Belousov return (ipsec4_perform_request(ifp, m, ip1, sp, inp, 0, mtu));
333fcf59617SAndrey V. Elsukov }
334fcf59617SAndrey V. Elsukov
3356b66194bSKornel Duleba int
ipsec4_check_pmtu(struct ifnet * ifp,struct mbuf * m,struct ip * ip1,struct secpolicy * sp,int forwarding)3360ff2d00dSKonstantin Belousov ipsec4_check_pmtu(struct ifnet *ifp, struct mbuf *m, struct ip *ip1,
3370ff2d00dSKonstantin Belousov struct secpolicy *sp, int forwarding)
3386b66194bSKornel Duleba {
3396b66194bSKornel Duleba struct secasvar *sav;
3406b66194bSKornel Duleba size_t hlen, pmtu;
3416b66194bSKornel Duleba uint32_t idx;
3426b66194bSKornel Duleba int error;
3436b66194bSKornel Duleba
3446b66194bSKornel Duleba /* Don't check PMTU if the frame won't have DF bit set. */
3456b66194bSKornel Duleba if (!V_ip4_ipsec_dfbit)
3466b66194bSKornel Duleba return (0);
3476b66194bSKornel Duleba if (V_ip4_ipsec_dfbit == 1)
3486b66194bSKornel Duleba goto setdf;
3496b66194bSKornel Duleba
3506b66194bSKornel Duleba /* V_ip4_ipsec_dfbit > 1 - we will copy it from inner header. */
3510ff2d00dSKonstantin Belousov if ((ip1->ip_off & htons(IP_DF)) == 0)
3526b66194bSKornel Duleba return (0);
3536b66194bSKornel Duleba
3546b66194bSKornel Duleba setdf:
3556b66194bSKornel Duleba idx = sp->tcount - 1;
3560ff2d00dSKonstantin Belousov sav = ipsec4_allocsa(ifp, m, ip1, sp, &idx, &error);
3576b66194bSKornel Duleba if (sav == NULL) {
3586b66194bSKornel Duleba key_freesp(&sp);
359a16771deSKornel Duleba /*
360a16771deSKornel Duleba * No matching SA was found and SADB_ACQUIRE message was generated.
361a16771deSKornel Duleba * Since we have matched a SP to this packet drop it silently.
362a16771deSKornel Duleba */
363a16771deSKornel Duleba if (error == 0)
364a16771deSKornel Duleba error = EINPROGRESS;
3656b66194bSKornel Duleba if (error != EJUSTRETURN)
3666b66194bSKornel Duleba m_freem(m);
3676b66194bSKornel Duleba
3686b66194bSKornel Duleba return (error);
3696b66194bSKornel Duleba }
3706b66194bSKornel Duleba
3719dfc8606SBartlomiej Grzesik pmtu = ipsec_get_pmtu(sav);
372b4220bf3SBartlomiej Grzesik if (pmtu == 0) {
3739dfc8606SBartlomiej Grzesik key_freesav(&sav);
3746b66194bSKornel Duleba return (0);
375b4220bf3SBartlomiej Grzesik }
376b4220bf3SBartlomiej Grzesik
3776b66194bSKornel Duleba hlen = ipsec_hdrsiz_internal(sp);
3789dfc8606SBartlomiej Grzesik key_freesav(&sav);
3799dfc8606SBartlomiej Grzesik
3806b66194bSKornel Duleba if (m_length(m, NULL) + hlen > pmtu) {
3816b66194bSKornel Duleba /*
3826b66194bSKornel Duleba * If we're forwarding generate ICMP message here,
3838deba29cSWojciech Macek * so that it contains pmtu subtracted by header size.
3846b66194bSKornel Duleba * Set error to EINPROGRESS, in order for the frame
3856b66194bSKornel Duleba * to be dropped silently.
3866b66194bSKornel Duleba */
3876b66194bSKornel Duleba if (forwarding) {
3886b66194bSKornel Duleba if (pmtu > hlen)
3896b66194bSKornel Duleba icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_NEEDFRAG,
3906b66194bSKornel Duleba 0, pmtu - hlen);
3916b66194bSKornel Duleba else
3926b66194bSKornel Duleba m_freem(m);
3936b66194bSKornel Duleba
3946b66194bSKornel Duleba key_freesp(&sp);
3956b66194bSKornel Duleba return (EINPROGRESS); /* Pretend that we consumed it. */
3966b66194bSKornel Duleba } else {
3976b66194bSKornel Duleba m_freem(m);
3986b66194bSKornel Duleba key_freesp(&sp);
3996b66194bSKornel Duleba return (EMSGSIZE);
4006b66194bSKornel Duleba }
4016b66194bSKornel Duleba }
4026b66194bSKornel Duleba
4036b66194bSKornel Duleba return (0);
4046b66194bSKornel Duleba }
4056b66194bSKornel Duleba
406fcf59617SAndrey V. Elsukov static int
ipsec4_common_output1(struct ifnet * ifp,struct mbuf * m,struct inpcb * inp,struct ip * ip1,int forwarding,u_long mtu)4070ff2d00dSKonstantin Belousov ipsec4_common_output1(struct ifnet *ifp, struct mbuf *m, struct inpcb *inp,
4080ff2d00dSKonstantin Belousov struct ip *ip1, int forwarding, u_long mtu)
409fcf59617SAndrey V. Elsukov {
410fcf59617SAndrey V. Elsukov struct secpolicy *sp;
41188768458SSam Leffler int error;
41288768458SSam Leffler
413fcf59617SAndrey V. Elsukov /* Lookup for the corresponding outbound security policy */
4140ff2d00dSKonstantin Belousov sp = ipsec4_checkpolicy(m, inp, ip1, &error, !forwarding);
415fcf59617SAndrey V. Elsukov if (sp == NULL) {
416fcf59617SAndrey V. Elsukov if (error == -EINVAL) {
417fcf59617SAndrey V. Elsukov /* Discarded by policy. */
418fcf59617SAndrey V. Elsukov m_freem(m);
419fcf59617SAndrey V. Elsukov return (EACCES);
420fcf59617SAndrey V. Elsukov }
421fcf59617SAndrey V. Elsukov return (0); /* No IPsec required. */
422fcf59617SAndrey V. Elsukov }
423fcf59617SAndrey V. Elsukov
424fcf59617SAndrey V. Elsukov /*
425fcf59617SAndrey V. Elsukov * Usually we have to have tunnel mode IPsec security policy
426fcf59617SAndrey V. Elsukov * when we are forwarding a packet. Otherwise we could not handle
427fcf59617SAndrey V. Elsukov * encrypted replies, because they are not destined for us. But
428fcf59617SAndrey V. Elsukov * some users are doing source address translation for forwarded
429fcf59617SAndrey V. Elsukov * packets, and thus, even if they are forwarded, the replies will
430fcf59617SAndrey V. Elsukov * return back to us.
431fcf59617SAndrey V. Elsukov */
432fcf59617SAndrey V. Elsukov
433fcf59617SAndrey V. Elsukov /* NB: callee frees mbuf and releases reference to SP */
4340ff2d00dSKonstantin Belousov error = ipsec4_check_pmtu(ifp, m, ip1, sp, forwarding);
4356b66194bSKornel Duleba if (error != 0) {
4366b66194bSKornel Duleba if (error == EJUSTRETURN)
4376b66194bSKornel Duleba return (0);
4386b66194bSKornel Duleba
4396b66194bSKornel Duleba return (error);
4406b66194bSKornel Duleba }
4416b66194bSKornel Duleba
4420ff2d00dSKonstantin Belousov error = ipsec4_process_packet(ifp, m, ip1, sp, inp, mtu);
443fcf59617SAndrey V. Elsukov if (error == EJUSTRETURN) {
444fcf59617SAndrey V. Elsukov /*
445fcf59617SAndrey V. Elsukov * We had a SP with a level of 'use' and no SA. We
446fcf59617SAndrey V. Elsukov * will just continue to process the packet without
447fcf59617SAndrey V. Elsukov * IPsec processing and return without error.
448fcf59617SAndrey V. Elsukov */
449fcf59617SAndrey V. Elsukov return (0);
450fcf59617SAndrey V. Elsukov }
451fcf59617SAndrey V. Elsukov if (error == 0)
452fcf59617SAndrey V. Elsukov return (EINPROGRESS); /* consumed by IPsec */
453fcf59617SAndrey V. Elsukov return (error);
454fcf59617SAndrey V. Elsukov }
455fcf59617SAndrey V. Elsukov
4560ff2d00dSKonstantin Belousov static int
ipsec4_common_output(struct ifnet * ifp,struct mbuf * m,struct inpcb * inp,struct ip * ip1,int forwarding,u_long mtu)4570ff2d00dSKonstantin Belousov ipsec4_common_output(struct ifnet *ifp, struct mbuf *m, struct inpcb *inp,
4580ff2d00dSKonstantin Belousov struct ip *ip1, int forwarding, u_long mtu)
4590ff2d00dSKonstantin Belousov {
4600ff2d00dSKonstantin Belousov struct ip ip_hdr;
4610ff2d00dSKonstantin Belousov struct ip *ip;
4620ff2d00dSKonstantin Belousov
4630ff2d00dSKonstantin Belousov if (((m->m_flags & M_PKTHDR) != 0 && m->m_pkthdr.len < sizeof(*ip)) ||
4640ff2d00dSKonstantin Belousov ((m->m_flags & M_PKTHDR) == 0 && m->m_len < sizeof(*ip))) {
4650ff2d00dSKonstantin Belousov m_free(m);
4660ff2d00dSKonstantin Belousov return (EACCES);
4670ff2d00dSKonstantin Belousov }
4680ff2d00dSKonstantin Belousov if (ip1 != NULL) {
4690ff2d00dSKonstantin Belousov ip = ip1;
4700ff2d00dSKonstantin Belousov } else {
4710ff2d00dSKonstantin Belousov ip = &ip_hdr;
4720ff2d00dSKonstantin Belousov m_copydata(m, 0, sizeof(*ip), (char *)ip);
4730ff2d00dSKonstantin Belousov }
4740ff2d00dSKonstantin Belousov
4750ff2d00dSKonstantin Belousov return (ipsec4_common_output1(ifp, m, inp, ip, forwarding, mtu));
4760ff2d00dSKonstantin Belousov }
4770ff2d00dSKonstantin Belousov
478fcf59617SAndrey V. Elsukov /*
479fcf59617SAndrey V. Elsukov * IPSEC_OUTPUT() method implementation for IPv4.
480fcf59617SAndrey V. Elsukov * 0 - no IPsec handling needed
481fcf59617SAndrey V. Elsukov * other values - mbuf consumed by IPsec.
482fcf59617SAndrey V. Elsukov */
483fcf59617SAndrey V. Elsukov int
ipsec4_output(struct ifnet * ifp,struct mbuf * m,struct inpcb * inp,u_long mtu)48400524fd4SKonstantin Belousov ipsec4_output(struct ifnet *ifp, struct mbuf *m, struct inpcb *inp, u_long mtu)
485fcf59617SAndrey V. Elsukov {
486fcf59617SAndrey V. Elsukov
487fcf59617SAndrey V. Elsukov /*
488fcf59617SAndrey V. Elsukov * If the packet is resubmitted to ip_output (e.g. after
489fcf59617SAndrey V. Elsukov * AH, ESP, etc. processing), there will be a tag to bypass
490fcf59617SAndrey V. Elsukov * the lookup and related policy checking.
491fcf59617SAndrey V. Elsukov */
492fcf59617SAndrey V. Elsukov if (m_tag_find(m, PACKET_TAG_IPSEC_OUT_DONE, NULL) != NULL)
493fcf59617SAndrey V. Elsukov return (0);
494fcf59617SAndrey V. Elsukov
4950ff2d00dSKonstantin Belousov return (ipsec4_common_output(ifp, m, inp, NULL, 0, mtu));
496fcf59617SAndrey V. Elsukov }
497fcf59617SAndrey V. Elsukov
498fcf59617SAndrey V. Elsukov /*
499fcf59617SAndrey V. Elsukov * IPSEC_FORWARD() method implementation for IPv4.
500fcf59617SAndrey V. Elsukov * 0 - no IPsec handling needed
501fcf59617SAndrey V. Elsukov * other values - mbuf consumed by IPsec.
502fcf59617SAndrey V. Elsukov */
503fcf59617SAndrey V. Elsukov int
ipsec4_forward(struct mbuf * m)504fcf59617SAndrey V. Elsukov ipsec4_forward(struct mbuf *m)
505fcf59617SAndrey V. Elsukov {
5060ff2d00dSKonstantin Belousov struct ip ip_hdr;
5070ff2d00dSKonstantin Belousov
5080ff2d00dSKonstantin Belousov m_copydata(m, 0, sizeof(ip_hdr), (char *)&ip_hdr);
509fcf59617SAndrey V. Elsukov
510fcf59617SAndrey V. Elsukov /*
511fcf59617SAndrey V. Elsukov * Check if this packet has an active inbound SP and needs to be
512fcf59617SAndrey V. Elsukov * dropped instead of forwarded.
513fcf59617SAndrey V. Elsukov */
5140ff2d00dSKonstantin Belousov if (ipsec4_in_reject1(m, &ip_hdr, NULL) != 0) {
515fcf59617SAndrey V. Elsukov m_freem(m);
516fcf59617SAndrey V. Elsukov return (EACCES);
517fcf59617SAndrey V. Elsukov }
5180ff2d00dSKonstantin Belousov return (ipsec4_common_output(NULL /* XXXKIB */, m, NULL, &ip_hdr,
5190ff2d00dSKonstantin Belousov 1, 0));
520fcf59617SAndrey V. Elsukov }
521fcf59617SAndrey V. Elsukov #endif
522fcf59617SAndrey V. Elsukov
523fcf59617SAndrey V. Elsukov #ifdef INET6
524fcf59617SAndrey V. Elsukov static int
in6_sa_equal_addrwithscope(const struct sockaddr_in6 * sa,const struct in6_addr * ia)525fcf59617SAndrey V. Elsukov in6_sa_equal_addrwithscope(const struct sockaddr_in6 *sa,
526fcf59617SAndrey V. Elsukov const struct in6_addr *ia)
527fcf59617SAndrey V. Elsukov {
528fcf59617SAndrey V. Elsukov struct in6_addr ia2;
529fcf59617SAndrey V. Elsukov
530fcf59617SAndrey V. Elsukov if (IN6_IS_SCOPE_LINKLOCAL(&sa->sin6_addr)) {
531fcf59617SAndrey V. Elsukov memcpy(&ia2, &sa->sin6_addr, sizeof(ia2));
532fcf59617SAndrey V. Elsukov ia2.s6_addr16[1] = htons(sa->sin6_scope_id);
533fcf59617SAndrey V. Elsukov return (IN6_ARE_ADDR_EQUAL(ia, &ia2));
534fcf59617SAndrey V. Elsukov }
535fcf59617SAndrey V. Elsukov return (IN6_ARE_ADDR_EQUAL(&sa->sin6_addr, ia));
536fcf59617SAndrey V. Elsukov }
537fcf59617SAndrey V. Elsukov
538fcf59617SAndrey V. Elsukov static struct secasvar *
ipsec6_allocsa(struct ifnet * ifp,struct mbuf * m,struct secpolicy * sp,u_int * pidx,int * error)539de1da299SKonstantin Belousov ipsec6_allocsa(struct ifnet *ifp, struct mbuf *m, struct secpolicy *sp,
540de1da299SKonstantin Belousov u_int *pidx, int *error)
541fcf59617SAndrey V. Elsukov {
542fcf59617SAndrey V. Elsukov struct secasindex *saidx, tmpsaidx;
543fcf59617SAndrey V. Elsukov struct ipsecrequest *isr;
544fcf59617SAndrey V. Elsukov struct sockaddr_in6 *sin6;
545fcf59617SAndrey V. Elsukov struct secasvar *sav;
546fcf59617SAndrey V. Elsukov struct ip6_hdr *ip6;
547fcf59617SAndrey V. Elsukov
548fcf59617SAndrey V. Elsukov /*
549fcf59617SAndrey V. Elsukov * Check system global policy controls.
550fcf59617SAndrey V. Elsukov */
551fcf59617SAndrey V. Elsukov next:
552fcf59617SAndrey V. Elsukov isr = sp->req[*pidx];
553fcf59617SAndrey V. Elsukov if ((isr->saidx.proto == IPPROTO_ESP && !V_esp_enable) ||
554fcf59617SAndrey V. Elsukov (isr->saidx.proto == IPPROTO_AH && !V_ah_enable) ||
555fcf59617SAndrey V. Elsukov (isr->saidx.proto == IPPROTO_IPCOMP && !V_ipcomp_enable)) {
556fcf59617SAndrey V. Elsukov DPRINTF(("%s: IPsec outbound packet dropped due"
557fcf59617SAndrey V. Elsukov " to policy (check your sysctls)\n", __func__));
558fcf59617SAndrey V. Elsukov IPSEC_OSTAT_INC(isr->saidx.proto, pdrops);
559fcf59617SAndrey V. Elsukov *error = EHOSTUNREACH;
560fcf59617SAndrey V. Elsukov return (NULL);
561fcf59617SAndrey V. Elsukov }
562fcf59617SAndrey V. Elsukov /*
563fcf59617SAndrey V. Elsukov * Craft SA index to search for proper SA. Note that
564fcf59617SAndrey V. Elsukov * we only fillin unspecified SA peers for transport
565fcf59617SAndrey V. Elsukov * mode; for tunnel mode they must already be filled in.
566fcf59617SAndrey V. Elsukov */
567fcf59617SAndrey V. Elsukov if (isr->saidx.mode == IPSEC_MODE_TRANSPORT) {
568fcf59617SAndrey V. Elsukov saidx = &tmpsaidx;
569fcf59617SAndrey V. Elsukov *saidx = isr->saidx;
570fcf59617SAndrey V. Elsukov ip6 = mtod(m, struct ip6_hdr *);
571fcf59617SAndrey V. Elsukov if (saidx->src.sin6.sin6_len == 0) {
572fcf59617SAndrey V. Elsukov sin6 = (struct sockaddr_in6 *)&saidx->src;
573fcf59617SAndrey V. Elsukov sin6->sin6_len = sizeof(*sin6);
574fcf59617SAndrey V. Elsukov sin6->sin6_family = AF_INET6;
575fcf59617SAndrey V. Elsukov sin6->sin6_port = IPSEC_PORT_ANY;
576fcf59617SAndrey V. Elsukov sin6->sin6_addr = ip6->ip6_src;
577fcf59617SAndrey V. Elsukov if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) {
578fcf59617SAndrey V. Elsukov /* fix scope id for comparing SPD */
579fcf59617SAndrey V. Elsukov sin6->sin6_addr.s6_addr16[1] = 0;
580fcf59617SAndrey V. Elsukov sin6->sin6_scope_id =
581fcf59617SAndrey V. Elsukov ntohs(ip6->ip6_src.s6_addr16[1]);
582fcf59617SAndrey V. Elsukov }
583fcf59617SAndrey V. Elsukov }
584fcf59617SAndrey V. Elsukov if (saidx->dst.sin6.sin6_len == 0) {
585fcf59617SAndrey V. Elsukov sin6 = (struct sockaddr_in6 *)&saidx->dst;
586fcf59617SAndrey V. Elsukov sin6->sin6_len = sizeof(*sin6);
587fcf59617SAndrey V. Elsukov sin6->sin6_family = AF_INET6;
588fcf59617SAndrey V. Elsukov sin6->sin6_port = IPSEC_PORT_ANY;
589fcf59617SAndrey V. Elsukov sin6->sin6_addr = ip6->ip6_dst;
590fcf59617SAndrey V. Elsukov if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) {
591fcf59617SAndrey V. Elsukov /* fix scope id for comparing SPD */
592fcf59617SAndrey V. Elsukov sin6->sin6_addr.s6_addr16[1] = 0;
593fcf59617SAndrey V. Elsukov sin6->sin6_scope_id =
594fcf59617SAndrey V. Elsukov ntohs(ip6->ip6_dst.s6_addr16[1]);
595fcf59617SAndrey V. Elsukov }
596fcf59617SAndrey V. Elsukov }
597fcf59617SAndrey V. Elsukov } else
598fcf59617SAndrey V. Elsukov saidx = &sp->req[*pidx]->saidx;
599fcf59617SAndrey V. Elsukov /*
600fcf59617SAndrey V. Elsukov * Lookup SA and validate it.
601fcf59617SAndrey V. Elsukov */
602fcf59617SAndrey V. Elsukov sav = key_allocsa_policy(sp, saidx, error);
603fcf59617SAndrey V. Elsukov if (sav == NULL) {
604fcf59617SAndrey V. Elsukov IPSEC6STAT_INC(ips_out_nosa);
605fcf59617SAndrey V. Elsukov if (*error != 0)
606fcf59617SAndrey V. Elsukov return (NULL);
607fcf59617SAndrey V. Elsukov if (ipsec_get_reqlevel(sp, *pidx) != IPSEC_LEVEL_REQUIRE) {
608fcf59617SAndrey V. Elsukov /*
609fcf59617SAndrey V. Elsukov * We have no SA and policy that doesn't require
610fcf59617SAndrey V. Elsukov * this IPsec transform, thus we can continue w/o
611fcf59617SAndrey V. Elsukov * IPsec processing, i.e. return EJUSTRETURN.
612fcf59617SAndrey V. Elsukov * But first check if there is some bundled transform.
613fcf59617SAndrey V. Elsukov */
614fcf59617SAndrey V. Elsukov if (sp->tcount > ++(*pidx))
615fcf59617SAndrey V. Elsukov goto next;
616fcf59617SAndrey V. Elsukov *error = EJUSTRETURN;
617fcf59617SAndrey V. Elsukov }
618fcf59617SAndrey V. Elsukov return (NULL);
619fcf59617SAndrey V. Elsukov }
620fcf59617SAndrey V. Elsukov IPSEC_ASSERT(sav->tdb_xform != NULL, ("SA with NULL tdb_xform"));
621fcf59617SAndrey V. Elsukov return (sav);
622fcf59617SAndrey V. Elsukov }
623fcf59617SAndrey V. Elsukov
624fcf59617SAndrey V. Elsukov /*
625fcf59617SAndrey V. Elsukov * IPsec output logic for IPv6.
626fcf59617SAndrey V. Elsukov */
627fcf59617SAndrey V. Elsukov static int
ipsec6_perform_request(struct ifnet * ifp,struct mbuf * m,struct secpolicy * sp,struct inpcb * inp,u_int idx,u_long mtu)628de1da299SKonstantin Belousov ipsec6_perform_request(struct ifnet *ifp, struct mbuf *m, struct secpolicy *sp,
62900524fd4SKonstantin Belousov struct inpcb *inp, u_int idx, u_long mtu)
630fcf59617SAndrey V. Elsukov {
631fcf59617SAndrey V. Elsukov struct ipsec_ctx_data ctx;
632fcf59617SAndrey V. Elsukov union sockaddr_union *dst;
633fcf59617SAndrey V. Elsukov struct secasvar *sav;
634fcf59617SAndrey V. Elsukov struct ip6_hdr *ip6;
635240b7bfeSKonstantin Belousov int error, hwassist, i, off;
636240b7bfeSKonstantin Belousov bool accel;
637fcf59617SAndrey V. Elsukov
638fcf59617SAndrey V. Elsukov IPSEC_ASSERT(idx < sp->tcount, ("Wrong IPsec request index %d", idx));
639fcf59617SAndrey V. Elsukov
640de1da299SKonstantin Belousov sav = ipsec6_allocsa(ifp, m, sp, &idx, &error);
641fcf59617SAndrey V. Elsukov if (sav == NULL) {
642fcf59617SAndrey V. Elsukov if (error == EJUSTRETURN) { /* No IPsec required */
643ef2a572bSKonstantin Belousov (void)ipsec_accel_output(ifp, m, inp, sp, NULL,
644240b7bfeSKonstantin Belousov AF_INET6, mtu, &hwassist);
645fcf59617SAndrey V. Elsukov key_freesp(&sp);
646fcf59617SAndrey V. Elsukov return (error);
647fcf59617SAndrey V. Elsukov }
648fcf59617SAndrey V. Elsukov goto bad;
649fcf59617SAndrey V. Elsukov }
650fcf59617SAndrey V. Elsukov
651fcf59617SAndrey V. Elsukov /* Fix IP length in case if it is not set yet. */
652fcf59617SAndrey V. Elsukov ip6 = mtod(m, struct ip6_hdr *);
653fcf59617SAndrey V. Elsukov ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(*ip6));
654fcf59617SAndrey V. Elsukov
6551a01e0e7SAndrey V. Elsukov IPSEC_INIT_CTX(&ctx, &m, inp, sav, AF_INET6, IPSEC_ENC_BEFORE);
656fcf59617SAndrey V. Elsukov if ((error = ipsec_run_hhooks(&ctx, HHOOK_TYPE_IPSEC_OUT)) != 0)
657fcf59617SAndrey V. Elsukov goto bad;
658fcf59617SAndrey V. Elsukov
659240b7bfeSKonstantin Belousov hwassist = 0;
660240b7bfeSKonstantin Belousov accel = ipsec_accel_output(ifp, m, inp, sp, sav, AF_INET6, mtu,
661240b7bfeSKonstantin Belousov &hwassist);
662240b7bfeSKonstantin Belousov
663240b7bfeSKonstantin Belousov /*
664240b7bfeSKonstantin Belousov * Do delayed checksums now because we send before
665240b7bfeSKonstantin Belousov * this is done in the normal processing path.
666240b7bfeSKonstantin Belousov */
667240b7bfeSKonstantin Belousov if ((m->m_pkthdr.csum_flags & CSUM_DELAY_DATA_IPV6 & ~hwassist) != 0) {
668240b7bfeSKonstantin Belousov in6_delayed_cksum(m, m->m_pkthdr.len -
669240b7bfeSKonstantin Belousov sizeof(struct ip6_hdr), sizeof(struct ip6_hdr));
670240b7bfeSKonstantin Belousov m->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA_IPV6;
671240b7bfeSKonstantin Belousov }
672240b7bfeSKonstantin Belousov #if defined(SCTP) || defined(SCTP_SUPPORT)
673240b7bfeSKonstantin Belousov if ((m->m_pkthdr.csum_flags & CSUM_SCTP_IPV6 & ~hwassist) != 0) {
674240b7bfeSKonstantin Belousov sctp_delayed_cksum(m, sizeof(struct ip6_hdr));
675240b7bfeSKonstantin Belousov m->m_pkthdr.csum_flags &= ~CSUM_SCTP_IPV6;
676240b7bfeSKonstantin Belousov }
677240b7bfeSKonstantin Belousov #endif
678240b7bfeSKonstantin Belousov if (accel)
679ef2a572bSKonstantin Belousov return (EJUSTRETURN);
680ef2a572bSKonstantin Belousov
681fcf59617SAndrey V. Elsukov ip6 = mtod(m, struct ip6_hdr *); /* pfil can change mbuf */
682fcf59617SAndrey V. Elsukov dst = &sav->sah->saidx.dst;
683fcf59617SAndrey V. Elsukov
684fcf59617SAndrey V. Elsukov /* Do the appropriate encapsulation, if necessary */
685fcf59617SAndrey V. Elsukov if (sp->req[idx]->saidx.mode == IPSEC_MODE_TUNNEL || /* Tunnel requ'd */
686fcf59617SAndrey V. Elsukov dst->sa.sa_family != AF_INET6 || /* PF mismatch */
687fcf59617SAndrey V. Elsukov ((dst->sa.sa_family == AF_INET6) &&
688fcf59617SAndrey V. Elsukov (!IN6_IS_ADDR_UNSPECIFIED(&dst->sin6.sin6_addr)) &&
689fcf59617SAndrey V. Elsukov (!in6_sa_equal_addrwithscope(&dst->sin6, &ip6->ip6_dst)))) {
690fcf59617SAndrey V. Elsukov if (m->m_pkthdr.len - sizeof(*ip6) > IPV6_MAXPACKET) {
691fcf59617SAndrey V. Elsukov /* No jumbogram support. */
692fcf59617SAndrey V. Elsukov error = ENXIO; /*XXX*/
693fcf59617SAndrey V. Elsukov goto bad;
694fcf59617SAndrey V. Elsukov }
695fcf59617SAndrey V. Elsukov error = ipsec_encap(&m, &sav->sah->saidx);
696fcf59617SAndrey V. Elsukov if (error != 0) {
6977f1f6591SAndrey V. Elsukov DPRINTF(("%s: encapsulation for SPI 0x%08x failed "
6987f1f6591SAndrey V. Elsukov "with error %d\n", __func__, ntohl(sav->spi),
6997f1f6591SAndrey V. Elsukov error));
700fcf59617SAndrey V. Elsukov /* XXXAE: IPSEC_OSTAT_INC(tunnel); */
701fcf59617SAndrey V. Elsukov goto bad;
702fcf59617SAndrey V. Elsukov }
7031a01e0e7SAndrey V. Elsukov inp = NULL;
704fcf59617SAndrey V. Elsukov }
705fcf59617SAndrey V. Elsukov
7061a01e0e7SAndrey V. Elsukov IPSEC_INIT_CTX(&ctx, &m, inp, sav, dst->sa.sa_family, IPSEC_ENC_AFTER);
707fcf59617SAndrey V. Elsukov if ((error = ipsec_run_hhooks(&ctx, HHOOK_TYPE_IPSEC_OUT)) != 0)
708fcf59617SAndrey V. Elsukov goto bad;
709fcf59617SAndrey V. Elsukov
710fcf59617SAndrey V. Elsukov switch(dst->sa.sa_family) {
711fcf59617SAndrey V. Elsukov #ifdef INET
712fcf59617SAndrey V. Elsukov case AF_INET:
713fcf59617SAndrey V. Elsukov {
714fcf59617SAndrey V. Elsukov struct ip *ip;
715fcf59617SAndrey V. Elsukov ip = mtod(m, struct ip *);
716fcf59617SAndrey V. Elsukov i = ip->ip_hl << 2;
717fcf59617SAndrey V. Elsukov off = offsetof(struct ip, ip_p);
718fcf59617SAndrey V. Elsukov }
719fcf59617SAndrey V. Elsukov break;
720fcf59617SAndrey V. Elsukov #endif /* AF_INET */
721fcf59617SAndrey V. Elsukov case AF_INET6:
722fcf59617SAndrey V. Elsukov i = sizeof(struct ip6_hdr);
723fcf59617SAndrey V. Elsukov off = offsetof(struct ip6_hdr, ip6_nxt);
724fcf59617SAndrey V. Elsukov break;
725fcf59617SAndrey V. Elsukov default:
726fcf59617SAndrey V. Elsukov DPRINTF(("%s: unsupported protocol family %u\n",
727fcf59617SAndrey V. Elsukov __func__, dst->sa.sa_family));
728fcf59617SAndrey V. Elsukov error = EPFNOSUPPORT;
729fcf59617SAndrey V. Elsukov IPSEC_OSTAT_INC(sav->sah->saidx.proto, nopf);
730fcf59617SAndrey V. Elsukov goto bad;
731fcf59617SAndrey V. Elsukov }
732fcf59617SAndrey V. Elsukov error = (*sav->tdb_xform->xf_output)(m, sp, sav, idx, i, off);
733fcf59617SAndrey V. Elsukov return (error);
734fcf59617SAndrey V. Elsukov bad:
735fcf59617SAndrey V. Elsukov IPSEC6STAT_INC(ips_out_inval);
736fcf59617SAndrey V. Elsukov if (m != NULL)
737fcf59617SAndrey V. Elsukov m_freem(m);
738fcf59617SAndrey V. Elsukov if (sav != NULL)
739fcf59617SAndrey V. Elsukov key_freesav(&sav);
740fcf59617SAndrey V. Elsukov key_freesp(&sp);
741fcf59617SAndrey V. Elsukov return (error);
742fcf59617SAndrey V. Elsukov }
743fcf59617SAndrey V. Elsukov
744fcf59617SAndrey V. Elsukov int
ipsec6_process_packet(struct ifnet * ifp,struct mbuf * m,struct secpolicy * sp,struct inpcb * inp,u_long mtu)745de1da299SKonstantin Belousov ipsec6_process_packet(struct ifnet *ifp, struct mbuf *m, struct secpolicy *sp,
74600524fd4SKonstantin Belousov struct inpcb *inp, u_long mtu)
747fcf59617SAndrey V. Elsukov {
748fcf59617SAndrey V. Elsukov
74900524fd4SKonstantin Belousov return (ipsec6_perform_request(ifp, m, sp, inp, 0, mtu));
750fcf59617SAndrey V. Elsukov }
751fcf59617SAndrey V. Elsukov
7529dfc8606SBartlomiej Grzesik /*
7539dfc8606SBartlomiej Grzesik * IPv6 implementation is based on IPv4 implementation.
7549dfc8606SBartlomiej Grzesik */
7559dfc8606SBartlomiej Grzesik int
ipsec6_check_pmtu(struct ifnet * ifp,struct mbuf * m,struct secpolicy * sp,int forwarding)756de1da299SKonstantin Belousov ipsec6_check_pmtu(struct ifnet *ifp, struct mbuf *m, struct secpolicy *sp,
757de1da299SKonstantin Belousov int forwarding)
7589dfc8606SBartlomiej Grzesik {
7599dfc8606SBartlomiej Grzesik struct secasvar *sav;
7609dfc8606SBartlomiej Grzesik size_t hlen, pmtu;
7619dfc8606SBartlomiej Grzesik uint32_t idx;
7629dfc8606SBartlomiej Grzesik int error;
7639dfc8606SBartlomiej Grzesik
7649dfc8606SBartlomiej Grzesik /*
7659dfc8606SBartlomiej Grzesik * According to RFC8200 L3 fragmentation is supposed to be done only on
7669dfc8606SBartlomiej Grzesik * locally generated packets. During L3 forwarding packets that are too
7679dfc8606SBartlomiej Grzesik * big are always supposed to be dropped, with an ICMPv6 packet being
7689dfc8606SBartlomiej Grzesik * sent back.
7699dfc8606SBartlomiej Grzesik */
7709dfc8606SBartlomiej Grzesik if (!forwarding)
7719dfc8606SBartlomiej Grzesik return (0);
7729dfc8606SBartlomiej Grzesik
7739dfc8606SBartlomiej Grzesik idx = sp->tcount - 1;
774de1da299SKonstantin Belousov sav = ipsec6_allocsa(ifp, m, sp, &idx, &error);
7759dfc8606SBartlomiej Grzesik if (sav == NULL) {
7769dfc8606SBartlomiej Grzesik key_freesp(&sp);
7779dfc8606SBartlomiej Grzesik /*
7789dfc8606SBartlomiej Grzesik * No matching SA was found and SADB_ACQUIRE message was generated.
7799dfc8606SBartlomiej Grzesik * Since we have matched a SP to this packet drop it silently.
7809dfc8606SBartlomiej Grzesik */
7819dfc8606SBartlomiej Grzesik if (error == 0)
7829dfc8606SBartlomiej Grzesik error = EINPROGRESS;
7839dfc8606SBartlomiej Grzesik if (error != EJUSTRETURN)
7849dfc8606SBartlomiej Grzesik m_freem(m);
7859dfc8606SBartlomiej Grzesik
7869dfc8606SBartlomiej Grzesik return (error);
7879dfc8606SBartlomiej Grzesik }
7889dfc8606SBartlomiej Grzesik
7899dfc8606SBartlomiej Grzesik pmtu = ipsec_get_pmtu(sav);
7909dfc8606SBartlomiej Grzesik if (pmtu == 0) {
7919dfc8606SBartlomiej Grzesik key_freesav(&sav);
7929dfc8606SBartlomiej Grzesik return (0);
7939dfc8606SBartlomiej Grzesik }
7949dfc8606SBartlomiej Grzesik
7959dfc8606SBartlomiej Grzesik hlen = ipsec_hdrsiz_internal(sp);
7969dfc8606SBartlomiej Grzesik key_freesav(&sav);
7979dfc8606SBartlomiej Grzesik
7989dfc8606SBartlomiej Grzesik if (m_length(m, NULL) + hlen > pmtu) {
7999dfc8606SBartlomiej Grzesik /*
8009dfc8606SBartlomiej Grzesik * If we're forwarding generate ICMPv6 message here,
801c27214f0SWojciech Macek * so that it contains pmtu subtracted by header size.
8029dfc8606SBartlomiej Grzesik * Set error to EINPROGRESS, in order for the frame
8039dfc8606SBartlomiej Grzesik * to be dropped silently.
8049dfc8606SBartlomiej Grzesik */
8059dfc8606SBartlomiej Grzesik if (forwarding) {
8069dfc8606SBartlomiej Grzesik if (pmtu > hlen)
8079dfc8606SBartlomiej Grzesik icmp6_error(m, ICMP6_PACKET_TOO_BIG, 0, pmtu - hlen);
8089dfc8606SBartlomiej Grzesik else
8099dfc8606SBartlomiej Grzesik m_freem(m);
8109dfc8606SBartlomiej Grzesik
8119dfc8606SBartlomiej Grzesik key_freesp(&sp);
8129dfc8606SBartlomiej Grzesik return (EINPROGRESS); /* Pretend that we consumed it. */
8139dfc8606SBartlomiej Grzesik }
8149dfc8606SBartlomiej Grzesik }
8159dfc8606SBartlomiej Grzesik
8169dfc8606SBartlomiej Grzesik return (0);
8179dfc8606SBartlomiej Grzesik }
8189dfc8606SBartlomiej Grzesik
819fcf59617SAndrey V. Elsukov static int
ipsec6_common_output(struct ifnet * ifp,struct mbuf * m,struct inpcb * inp,int forwarding,u_long mtu)820de1da299SKonstantin Belousov ipsec6_common_output(struct ifnet *ifp, struct mbuf *m, struct inpcb *inp,
82100524fd4SKonstantin Belousov int forwarding, u_long mtu)
822fcf59617SAndrey V. Elsukov {
823fcf59617SAndrey V. Elsukov struct secpolicy *sp;
824fcf59617SAndrey V. Elsukov int error;
825fcf59617SAndrey V. Elsukov
826fcf59617SAndrey V. Elsukov /* Lookup for the corresponding outbound security policy */
82722bbefb2SAndrey V. Elsukov sp = ipsec6_checkpolicy(m, inp, &error, !forwarding);
828fcf59617SAndrey V. Elsukov if (sp == NULL) {
829fcf59617SAndrey V. Elsukov if (error == -EINVAL) {
830fcf59617SAndrey V. Elsukov /* Discarded by policy. */
831fcf59617SAndrey V. Elsukov m_freem(m);
832fcf59617SAndrey V. Elsukov return (EACCES);
833fcf59617SAndrey V. Elsukov }
834fcf59617SAndrey V. Elsukov return (0); /* No IPsec required. */
835fcf59617SAndrey V. Elsukov }
836fcf59617SAndrey V. Elsukov
837de1da299SKonstantin Belousov error = ipsec6_check_pmtu(ifp, m, sp, forwarding);
8389dfc8606SBartlomiej Grzesik if (error != 0) {
8399dfc8606SBartlomiej Grzesik if (error == EJUSTRETURN)
8409dfc8606SBartlomiej Grzesik return (0);
8419dfc8606SBartlomiej Grzesik
8429dfc8606SBartlomiej Grzesik return (error);
8439dfc8606SBartlomiej Grzesik }
8449dfc8606SBartlomiej Grzesik
845fcf59617SAndrey V. Elsukov /* NB: callee frees mbuf and releases reference to SP */
84600524fd4SKonstantin Belousov error = ipsec6_process_packet(ifp, m, sp, inp, mtu);
847fcf59617SAndrey V. Elsukov if (error == EJUSTRETURN) {
848fcf59617SAndrey V. Elsukov /*
849fcf59617SAndrey V. Elsukov * We had a SP with a level of 'use' and no SA. We
850fcf59617SAndrey V. Elsukov * will just continue to process the packet without
851fcf59617SAndrey V. Elsukov * IPsec processing and return without error.
852fcf59617SAndrey V. Elsukov */
853fcf59617SAndrey V. Elsukov return (0);
854fcf59617SAndrey V. Elsukov }
855fcf59617SAndrey V. Elsukov if (error == 0)
856fcf59617SAndrey V. Elsukov return (EINPROGRESS); /* consumed by IPsec */
857fcf59617SAndrey V. Elsukov return (error);
858fcf59617SAndrey V. Elsukov }
859fcf59617SAndrey V. Elsukov
860fcf59617SAndrey V. Elsukov /*
861fcf59617SAndrey V. Elsukov * IPSEC_OUTPUT() method implementation for IPv6.
862fcf59617SAndrey V. Elsukov * 0 - no IPsec handling needed
863fcf59617SAndrey V. Elsukov * other values - mbuf consumed by IPsec.
864fcf59617SAndrey V. Elsukov */
865fcf59617SAndrey V. Elsukov int
ipsec6_output(struct ifnet * ifp,struct mbuf * m,struct inpcb * inp,u_long mtu)86600524fd4SKonstantin Belousov ipsec6_output(struct ifnet *ifp, struct mbuf *m, struct inpcb *inp, u_long mtu)
867fcf59617SAndrey V. Elsukov {
868fcf59617SAndrey V. Elsukov
869fcf59617SAndrey V. Elsukov /*
870fcf59617SAndrey V. Elsukov * If the packet is resubmitted to ip_output (e.g. after
871fcf59617SAndrey V. Elsukov * AH, ESP, etc. processing), there will be a tag to bypass
872fcf59617SAndrey V. Elsukov * the lookup and related policy checking.
873fcf59617SAndrey V. Elsukov */
874fcf59617SAndrey V. Elsukov if (m_tag_find(m, PACKET_TAG_IPSEC_OUT_DONE, NULL) != NULL)
875fcf59617SAndrey V. Elsukov return (0);
876fcf59617SAndrey V. Elsukov
87700524fd4SKonstantin Belousov return (ipsec6_common_output(ifp, m, inp, 0, mtu));
878fcf59617SAndrey V. Elsukov }
879fcf59617SAndrey V. Elsukov
880fcf59617SAndrey V. Elsukov /*
881fcf59617SAndrey V. Elsukov * IPSEC_FORWARD() method implementation for IPv6.
882fcf59617SAndrey V. Elsukov * 0 - no IPsec handling needed
883fcf59617SAndrey V. Elsukov * other values - mbuf consumed by IPsec.
884fcf59617SAndrey V. Elsukov */
885fcf59617SAndrey V. Elsukov int
ipsec6_forward(struct mbuf * m)886fcf59617SAndrey V. Elsukov ipsec6_forward(struct mbuf *m)
887fcf59617SAndrey V. Elsukov {
888fcf59617SAndrey V. Elsukov
889fcf59617SAndrey V. Elsukov /*
890fcf59617SAndrey V. Elsukov * Check if this packet has an active inbound SP and needs to be
891fcf59617SAndrey V. Elsukov * dropped instead of forwarded.
892fcf59617SAndrey V. Elsukov */
893fcf59617SAndrey V. Elsukov if (ipsec6_in_reject(m, NULL) != 0) {
894fcf59617SAndrey V. Elsukov m_freem(m);
895fcf59617SAndrey V. Elsukov return (EACCES);
896fcf59617SAndrey V. Elsukov }
89700524fd4SKonstantin Belousov return (ipsec6_common_output(NULL /* XXXKIB */, m, NULL, 1, 0));
898fcf59617SAndrey V. Elsukov }
899fcf59617SAndrey V. Elsukov #endif /* INET6 */
900fcf59617SAndrey V. Elsukov
901fcf59617SAndrey V. Elsukov int
ipsec_process_done(struct mbuf * m,struct secpolicy * sp,struct secasvar * sav,u_int idx)902fcf59617SAndrey V. Elsukov ipsec_process_done(struct mbuf *m, struct secpolicy *sp, struct secasvar *sav,
903fcf59617SAndrey V. Elsukov u_int idx)
904fcf59617SAndrey V. Elsukov {
905f82eb2a6SJohn Baldwin struct epoch_tracker et;
906fcf59617SAndrey V. Elsukov struct xform_history *xh;
907fcf59617SAndrey V. Elsukov struct secasindex *saidx;
908fcf59617SAndrey V. Elsukov struct m_tag *mtag;
9090ff2d00dSKonstantin Belousov #ifdef INET
9100ff2d00dSKonstantin Belousov struct ip *ip;
9110ff2d00dSKonstantin Belousov #endif
912fcf59617SAndrey V. Elsukov int error;
91388768458SSam Leffler
914ef2a572bSKonstantin Belousov if (sav->state >= SADB_SASTATE_DEAD) {
915ef2a572bSKonstantin Belousov error = ESRCH;
916ef2a572bSKonstantin Belousov goto bad;
917ef2a572bSKonstantin Belousov }
91888768458SSam Leffler saidx = &sav->sah->saidx;
91988768458SSam Leffler switch (saidx->dst.sa.sa_family) {
92088768458SSam Leffler #ifdef INET
92188768458SSam Leffler case AF_INET:
9220ff2d00dSKonstantin Belousov ip = mtod(m, struct ip *);
92388768458SSam Leffler /* Fix the header length, for AH processing. */
9240ff2d00dSKonstantin Belousov ip->ip_len = htons(m->m_pkthdr.len);
92588768458SSam Leffler break;
92688768458SSam Leffler #endif /* INET */
92788768458SSam Leffler #ifdef INET6
92888768458SSam Leffler case AF_INET6:
92988768458SSam Leffler /* Fix the header length, for AH processing. */
93088768458SSam Leffler if (m->m_pkthdr.len < sizeof (struct ip6_hdr)) {
93188768458SSam Leffler error = ENXIO;
93288768458SSam Leffler goto bad;
93388768458SSam Leffler }
93488768458SSam Leffler if (m->m_pkthdr.len - sizeof (struct ip6_hdr) > IPV6_MAXPACKET) {
93588768458SSam Leffler /* No jumbogram support. */
93688768458SSam Leffler error = ENXIO; /*?*/
93788768458SSam Leffler goto bad;
93888768458SSam Leffler }
93988768458SSam Leffler mtod(m, struct ip6_hdr *)->ip6_plen =
94088768458SSam Leffler htons(m->m_pkthdr.len - sizeof(struct ip6_hdr));
94188768458SSam Leffler break;
94288768458SSam Leffler #endif /* INET6 */
94388768458SSam Leffler default:
9449ffa9677SSam Leffler DPRINTF(("%s: unknown protocol family %u\n", __func__,
94588768458SSam Leffler saidx->dst.sa.sa_family));
94688768458SSam Leffler error = ENXIO;
94788768458SSam Leffler goto bad;
94888768458SSam Leffler }
94988768458SSam Leffler
95088768458SSam Leffler /*
951fcf59617SAndrey V. Elsukov * Add a record of what we've done to the packet.
95288768458SSam Leffler */
953fcf59617SAndrey V. Elsukov mtag = m_tag_get(PACKET_TAG_IPSEC_OUT_DONE, sizeof(*xh), M_NOWAIT);
95488768458SSam Leffler if (mtag == NULL) {
9559ffa9677SSam Leffler DPRINTF(("%s: could not get packet tag\n", __func__));
95688768458SSam Leffler error = ENOMEM;
95788768458SSam Leffler goto bad;
95888768458SSam Leffler }
95988768458SSam Leffler
960fcf59617SAndrey V. Elsukov xh = (struct xform_history *)(mtag + 1);
961fcf59617SAndrey V. Elsukov xh->dst = saidx->dst;
962fcf59617SAndrey V. Elsukov xh->proto = saidx->proto;
963fcf59617SAndrey V. Elsukov xh->mode = saidx->mode;
964fcf59617SAndrey V. Elsukov xh->spi = sav->spi;
96588768458SSam Leffler m_tag_prepend(m, mtag);
96688768458SSam Leffler
96759959de5SErmal Luçi key_sa_recordxfer(sav, m); /* record data transfer */
96859959de5SErmal Luçi
96988768458SSam Leffler /*
97088768458SSam Leffler * If there's another (bundled) SA to apply, do so.
97188768458SSam Leffler * Note that this puts a burden on the kernel stack size.
97288768458SSam Leffler * If this is a problem we'll need to introduce a queue
97388768458SSam Leffler * to set the packet on so we can unwind the stack before
97488768458SSam Leffler * doing further processing.
97588768458SSam Leffler */
976fcf59617SAndrey V. Elsukov if (++idx < sp->tcount) {
977db178eb8SBjoern A. Zeeb switch (saidx->dst.sa.sa_family) {
978db178eb8SBjoern A. Zeeb #ifdef INET
979db178eb8SBjoern A. Zeeb case AF_INET:
980fcf59617SAndrey V. Elsukov key_freesav(&sav);
981f9d8f665SAndrey V. Elsukov IPSECSTAT_INC(ips_out_bundlesa);
9820ff2d00dSKonstantin Belousov return (ipsec4_perform_request(NULL, m, ip, sp, NULL,
98300524fd4SKonstantin Belousov idx, 0));
984db178eb8SBjoern A. Zeeb /* NOTREACHED */
985db178eb8SBjoern A. Zeeb #endif
986db178eb8SBjoern A. Zeeb #ifdef INET6
987db178eb8SBjoern A. Zeeb case AF_INET6:
988fcf59617SAndrey V. Elsukov key_freesav(&sav);
989f9d8f665SAndrey V. Elsukov IPSEC6STAT_INC(ips_out_bundlesa);
990de1da299SKonstantin Belousov return (ipsec6_perform_request(NULL, m, sp, NULL,
99100524fd4SKonstantin Belousov idx, 0));
992db178eb8SBjoern A. Zeeb /* NOTREACHED */
993db178eb8SBjoern A. Zeeb #endif /* INET6 */
994db178eb8SBjoern A. Zeeb default:
995db178eb8SBjoern A. Zeeb DPRINTF(("%s: unknown protocol family %u\n", __func__,
996db178eb8SBjoern A. Zeeb saidx->dst.sa.sa_family));
997fcf59617SAndrey V. Elsukov error = EPFNOSUPPORT;
998db178eb8SBjoern A. Zeeb goto bad;
999db178eb8SBjoern A. Zeeb }
100088768458SSam Leffler }
100188768458SSam Leffler
1002fcf59617SAndrey V. Elsukov key_freesp(&sp), sp = NULL; /* Release reference to SP */
100380044c78SXavier Beaudouin #if defined(INET) || defined(INET6)
1004fcf59617SAndrey V. Elsukov /*
1005fcf59617SAndrey V. Elsukov * Do UDP encapsulation if SA requires it.
1006fcf59617SAndrey V. Elsukov */
1007fcf59617SAndrey V. Elsukov if (sav->natt != NULL) {
1008fcf59617SAndrey V. Elsukov error = udp_ipsec_output(m, sav);
1009fcf59617SAndrey V. Elsukov if (error != 0)
1010fcf59617SAndrey V. Elsukov goto bad;
1011fcf59617SAndrey V. Elsukov }
101280044c78SXavier Beaudouin #endif /* INET || INET6 */
101388768458SSam Leffler /*
101488768458SSam Leffler * We're done with IPsec processing, transmit the packet using the
10156508929bSAndrey V. Elsukov * appropriate network protocol (IP or IPv6).
101688768458SSam Leffler */
1017f82eb2a6SJohn Baldwin NET_EPOCH_ENTER(et);
101888768458SSam Leffler switch (saidx->dst.sa.sa_family) {
101988768458SSam Leffler #ifdef INET
102088768458SSam Leffler case AF_INET:
1021fcf59617SAndrey V. Elsukov key_freesav(&sav);
1022f82eb2a6SJohn Baldwin error = ip_output(m, NULL, NULL, IP_RAWOUTPUT, NULL, NULL);
1023f82eb2a6SJohn Baldwin break;
102488768458SSam Leffler #endif /* INET */
102588768458SSam Leffler #ifdef INET6
102688768458SSam Leffler case AF_INET6:
1027fcf59617SAndrey V. Elsukov key_freesav(&sav);
1028f82eb2a6SJohn Baldwin error = ip6_output(m, NULL, NULL, 0, NULL, NULL, NULL);
1029f82eb2a6SJohn Baldwin break;
103088768458SSam Leffler #endif /* INET6 */
1031f82eb2a6SJohn Baldwin default:
103288768458SSam Leffler panic("ipsec_process_done");
1033f82eb2a6SJohn Baldwin }
1034f82eb2a6SJohn Baldwin NET_EPOCH_EXIT(et);
1035f82eb2a6SJohn Baldwin return (error);
103688768458SSam Leffler bad:
103788768458SSam Leffler m_freem(m);
1038fcf59617SAndrey V. Elsukov key_freesav(&sav);
1039fcf59617SAndrey V. Elsukov if (sp != NULL)
1040fcf59617SAndrey V. Elsukov key_freesp(&sp);
104188768458SSam Leffler return (error);
104288768458SSam Leffler }
104388768458SSam Leffler
1044fcf59617SAndrey V. Elsukov /*
1045fcf59617SAndrey V. Elsukov * ipsec_prepend() is optimized version of M_PREPEND().
1046fcf59617SAndrey V. Elsukov * ipsec_encap() is called by IPsec output routine for tunnel mode SA.
1047fcf59617SAndrey V. Elsukov * It is expected that after IP encapsulation some IPsec transform will
1048fcf59617SAndrey V. Elsukov * be performed. Each IPsec transform inserts its variable length header
1049fcf59617SAndrey V. Elsukov * just after outer IP header using m_makespace(). If given mbuf has not
1050fcf59617SAndrey V. Elsukov * enough free space at the beginning, we allocate new mbuf and reserve
1051fcf59617SAndrey V. Elsukov * some space at the beginning and at the end.
1052fcf59617SAndrey V. Elsukov * This helps avoid allocating of new mbuf and data copying in m_makespace(),
1053fcf59617SAndrey V. Elsukov * we place outer header in the middle of mbuf's data with reserved leading
1054fcf59617SAndrey V. Elsukov * and trailing space:
1055fcf59617SAndrey V. Elsukov * [ LEADINGSPACE ][ Outer IP header ][ TRAILINGSPACE ]
1056fcf59617SAndrey V. Elsukov * LEADINGSPACE will be used to add ethernet header, TRAILINGSPACE will
1057fcf59617SAndrey V. Elsukov * be used to inject AH/ESP/IPCOMP header.
1058fcf59617SAndrey V. Elsukov */
1059fcf59617SAndrey V. Elsukov #define IPSEC_TRAILINGSPACE (sizeof(struct udphdr) +/* NAT-T */ \
1060fcf59617SAndrey V. Elsukov max(sizeof(struct newesp) + EALG_MAX_BLOCK_LEN, /* ESP + IV */ \
1061fcf59617SAndrey V. Elsukov sizeof(struct newah) + HASH_MAX_LEN /* AH + ICV */))
1062fcf59617SAndrey V. Elsukov static struct mbuf *
ipsec_prepend(struct mbuf * m,int len,int how)1063fcf59617SAndrey V. Elsukov ipsec_prepend(struct mbuf *m, int len, int how)
106488768458SSam Leffler {
1065fcf59617SAndrey V. Elsukov struct mbuf *n;
106688768458SSam Leffler
1067fcf59617SAndrey V. Elsukov M_ASSERTPKTHDR(m);
1068fcf59617SAndrey V. Elsukov IPSEC_ASSERT(len < MHLEN, ("wrong length"));
1069fcf59617SAndrey V. Elsukov if (M_LEADINGSPACE(m) >= len) {
1070fcf59617SAndrey V. Elsukov /* No need to allocate new mbuf. */
1071fcf59617SAndrey V. Elsukov m->m_data -= len;
1072fcf59617SAndrey V. Elsukov m->m_len += len;
1073fcf59617SAndrey V. Elsukov m->m_pkthdr.len += len;
1074fcf59617SAndrey V. Elsukov return (m);
107588768458SSam Leffler }
1076fcf59617SAndrey V. Elsukov n = m_gethdr(how, m->m_type);
1077fcf59617SAndrey V. Elsukov if (n == NULL) {
1078fcf59617SAndrey V. Elsukov m_freem(m);
1079fcf59617SAndrey V. Elsukov return (NULL);
108088768458SSam Leffler }
1081fcf59617SAndrey V. Elsukov m_move_pkthdr(n, m);
1082fcf59617SAndrey V. Elsukov n->m_next = m;
1083fcf59617SAndrey V. Elsukov if (len + IPSEC_TRAILINGSPACE < M_SIZE(n))
1084fcf59617SAndrey V. Elsukov m_align(n, len + IPSEC_TRAILINGSPACE);
1085fcf59617SAndrey V. Elsukov n->m_len = len;
1086fcf59617SAndrey V. Elsukov n->m_pkthdr.len += len;
1087fcf59617SAndrey V. Elsukov return (n);
108888768458SSam Leffler }
108988768458SSam Leffler
10909dfc8606SBartlomiej Grzesik static size_t
ipsec_get_pmtu(struct secasvar * sav)10919dfc8606SBartlomiej Grzesik ipsec_get_pmtu(struct secasvar *sav)
10929dfc8606SBartlomiej Grzesik {
10939dfc8606SBartlomiej Grzesik union sockaddr_union *dst;
10949dfc8606SBartlomiej Grzesik struct in_conninfo inc;
10959dfc8606SBartlomiej Grzesik size_t pmtu;
10969dfc8606SBartlomiej Grzesik
10979dfc8606SBartlomiej Grzesik dst = &sav->sah->saidx.dst;
10989dfc8606SBartlomiej Grzesik memset(&inc, 0, sizeof(inc));
10999dfc8606SBartlomiej Grzesik
11009dfc8606SBartlomiej Grzesik switch (dst->sa.sa_family) {
11019dfc8606SBartlomiej Grzesik #ifdef INET
11029dfc8606SBartlomiej Grzesik case AF_INET:
11039dfc8606SBartlomiej Grzesik inc.inc_faddr = satosin(&dst->sa)->sin_addr;
11049dfc8606SBartlomiej Grzesik break;
11059dfc8606SBartlomiej Grzesik #endif
11069dfc8606SBartlomiej Grzesik #ifdef INET6
11079dfc8606SBartlomiej Grzesik case AF_INET6:
11089dfc8606SBartlomiej Grzesik inc.inc6_faddr = satosin6(&dst->sa)->sin6_addr;
11099dfc8606SBartlomiej Grzesik inc.inc_flags |= INC_ISIPV6;
11109dfc8606SBartlomiej Grzesik break;
11119dfc8606SBartlomiej Grzesik #endif
11129dfc8606SBartlomiej Grzesik default:
11139dfc8606SBartlomiej Grzesik return (0);
11149dfc8606SBartlomiej Grzesik }
11159dfc8606SBartlomiej Grzesik
11169dfc8606SBartlomiej Grzesik pmtu = tcp_hc_getmtu(&inc);
11179dfc8606SBartlomiej Grzesik if (pmtu != 0)
11189dfc8606SBartlomiej Grzesik return (pmtu);
11199dfc8606SBartlomiej Grzesik
11209dfc8606SBartlomiej Grzesik /* No entry in hostcache. Assume that PMTU is equal to link's MTU */
11219dfc8606SBartlomiej Grzesik switch (dst->sa.sa_family) {
11229dfc8606SBartlomiej Grzesik #ifdef INET
11239dfc8606SBartlomiej Grzesik case AF_INET:
11249dfc8606SBartlomiej Grzesik pmtu = tcp_maxmtu(&inc, NULL);
11259dfc8606SBartlomiej Grzesik break;
11269dfc8606SBartlomiej Grzesik #endif
11279dfc8606SBartlomiej Grzesik #ifdef INET6
11289dfc8606SBartlomiej Grzesik case AF_INET6:
11299dfc8606SBartlomiej Grzesik pmtu = tcp_maxmtu6(&inc, NULL);
11309dfc8606SBartlomiej Grzesik break;
11319dfc8606SBartlomiej Grzesik #endif
11329dfc8606SBartlomiej Grzesik default:
11339dfc8606SBartlomiej Grzesik return (0);
11349dfc8606SBartlomiej Grzesik }
11359dfc8606SBartlomiej Grzesik if (pmtu == 0)
11369dfc8606SBartlomiej Grzesik return (0);
11379dfc8606SBartlomiej Grzesik
11389dfc8606SBartlomiej Grzesik tcp_hc_updatemtu(&inc, pmtu);
11399dfc8606SBartlomiej Grzesik
11409dfc8606SBartlomiej Grzesik return (pmtu);
11419dfc8606SBartlomiej Grzesik }
11429dfc8606SBartlomiej Grzesik
114361f37615SAndrey V. Elsukov static int
ipsec_encap(struct mbuf ** mp,struct secasindex * saidx)114461f37615SAndrey V. Elsukov ipsec_encap(struct mbuf **mp, struct secasindex *saidx)
114561f37615SAndrey V. Elsukov {
114661f37615SAndrey V. Elsukov #ifdef INET6
114761f37615SAndrey V. Elsukov struct ip6_hdr *ip6;
114861f37615SAndrey V. Elsukov #endif
114961f37615SAndrey V. Elsukov struct ip *ip;
115040461785SJohn Baldwin #ifdef INET
1151c94d6389SAndrey V. Elsukov int setdf = V_ip4_ipsec_dfbit == 1 ? 1: 0;
115240461785SJohn Baldwin #endif
115361f37615SAndrey V. Elsukov uint8_t itos, proto;
115461f37615SAndrey V. Elsukov
115561f37615SAndrey V. Elsukov ip = mtod(*mp, struct ip *);
115661f37615SAndrey V. Elsukov switch (ip->ip_v) {
115761f37615SAndrey V. Elsukov #ifdef INET
115861f37615SAndrey V. Elsukov case IPVERSION:
115961f37615SAndrey V. Elsukov proto = IPPROTO_IPIP;
116061f37615SAndrey V. Elsukov /*
1161c94d6389SAndrey V. Elsukov * Copy IP_DF flag from the inner header if
1162c94d6389SAndrey V. Elsukov * system-wide control variable is greater than 1.
116361f37615SAndrey V. Elsukov */
1164c94d6389SAndrey V. Elsukov if (V_ip4_ipsec_dfbit > 1)
11656f814d0eSAndrey V. Elsukov setdf = (ip->ip_off & htons(IP_DF)) != 0;
116661f37615SAndrey V. Elsukov itos = ip->ip_tos;
116761f37615SAndrey V. Elsukov break;
116861f37615SAndrey V. Elsukov #endif
116961f37615SAndrey V. Elsukov #ifdef INET6
117061f37615SAndrey V. Elsukov case (IPV6_VERSION >> 4):
117161f37615SAndrey V. Elsukov proto = IPPROTO_IPV6;
117261f37615SAndrey V. Elsukov ip6 = mtod(*mp, struct ip6_hdr *);
117361f37615SAndrey V. Elsukov itos = (ntohl(ip6->ip6_flow) >> 20) & 0xff;
117461f37615SAndrey V. Elsukov /* scoped address handling */
117561f37615SAndrey V. Elsukov in6_clearscope(&ip6->ip6_src);
117661f37615SAndrey V. Elsukov in6_clearscope(&ip6->ip6_dst);
117761f37615SAndrey V. Elsukov break;
117861f37615SAndrey V. Elsukov #endif
117961f37615SAndrey V. Elsukov default:
118061f37615SAndrey V. Elsukov return (EAFNOSUPPORT);
118161f37615SAndrey V. Elsukov }
118261f37615SAndrey V. Elsukov switch (saidx->dst.sa.sa_family) {
118361f37615SAndrey V. Elsukov #ifdef INET
118461f37615SAndrey V. Elsukov case AF_INET:
118561f37615SAndrey V. Elsukov if (saidx->src.sa.sa_family != AF_INET ||
118661f37615SAndrey V. Elsukov saidx->src.sin.sin_addr.s_addr == INADDR_ANY ||
118761f37615SAndrey V. Elsukov saidx->dst.sin.sin_addr.s_addr == INADDR_ANY)
118861f37615SAndrey V. Elsukov return (EINVAL);
1189fcf59617SAndrey V. Elsukov *mp = ipsec_prepend(*mp, sizeof(struct ip), M_NOWAIT);
119061f37615SAndrey V. Elsukov if (*mp == NULL)
119161f37615SAndrey V. Elsukov return (ENOBUFS);
119261f37615SAndrey V. Elsukov ip = mtod(*mp, struct ip *);
119361f37615SAndrey V. Elsukov ip->ip_v = IPVERSION;
119461f37615SAndrey V. Elsukov ip->ip_hl = sizeof(struct ip) >> 2;
119561f37615SAndrey V. Elsukov ip->ip_p = proto;
119661f37615SAndrey V. Elsukov ip->ip_len = htons((*mp)->m_pkthdr.len);
119761f37615SAndrey V. Elsukov ip->ip_ttl = V_ip_defttl;
119861f37615SAndrey V. Elsukov ip->ip_sum = 0;
119961f37615SAndrey V. Elsukov ip->ip_off = setdf ? htons(IP_DF): 0;
120061f37615SAndrey V. Elsukov ip->ip_src = saidx->src.sin.sin_addr;
120161f37615SAndrey V. Elsukov ip->ip_dst = saidx->dst.sin.sin_addr;
120261f37615SAndrey V. Elsukov ip_ecn_ingress(V_ip4_ipsec_ecn, &ip->ip_tos, &itos);
1203*70703aa9Sacazuc ip_fillid(ip, V_ip4_ipsec_random_id);
120461f37615SAndrey V. Elsukov break;
120561f37615SAndrey V. Elsukov #endif /* INET */
120661f37615SAndrey V. Elsukov #ifdef INET6
120761f37615SAndrey V. Elsukov case AF_INET6:
120861f37615SAndrey V. Elsukov if (saidx->src.sa.sa_family != AF_INET6 ||
120961f37615SAndrey V. Elsukov IN6_IS_ADDR_UNSPECIFIED(&saidx->src.sin6.sin6_addr) ||
121061f37615SAndrey V. Elsukov IN6_IS_ADDR_UNSPECIFIED(&saidx->dst.sin6.sin6_addr))
121161f37615SAndrey V. Elsukov return (EINVAL);
1212fcf59617SAndrey V. Elsukov *mp = ipsec_prepend(*mp, sizeof(struct ip6_hdr), M_NOWAIT);
121361f37615SAndrey V. Elsukov if (*mp == NULL)
121461f37615SAndrey V. Elsukov return (ENOBUFS);
121561f37615SAndrey V. Elsukov ip6 = mtod(*mp, struct ip6_hdr *);
121661f37615SAndrey V. Elsukov ip6->ip6_flow = 0;
121761f37615SAndrey V. Elsukov ip6->ip6_vfc = IPV6_VERSION;
121861f37615SAndrey V. Elsukov ip6->ip6_hlim = V_ip6_defhlim;
121961f37615SAndrey V. Elsukov ip6->ip6_nxt = proto;
122061f37615SAndrey V. Elsukov ip6->ip6_dst = saidx->dst.sin6.sin6_addr;
12211ae800e7SAndrey V. Elsukov /* For link-local address embed scope zone id */
12221ae800e7SAndrey V. Elsukov if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst))
12231ae800e7SAndrey V. Elsukov ip6->ip6_dst.s6_addr16[1] =
12241ae800e7SAndrey V. Elsukov htons(saidx->dst.sin6.sin6_scope_id & 0xffff);
122561f37615SAndrey V. Elsukov ip6->ip6_src = saidx->src.sin6.sin6_addr;
12261ae800e7SAndrey V. Elsukov if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src))
12271ae800e7SAndrey V. Elsukov ip6->ip6_src.s6_addr16[1] =
12281ae800e7SAndrey V. Elsukov htons(saidx->src.sin6.sin6_scope_id & 0xffff);
122961f37615SAndrey V. Elsukov ip6->ip6_plen = htons((*mp)->m_pkthdr.len - sizeof(*ip6));
123061f37615SAndrey V. Elsukov ip_ecn_ingress(V_ip6_ipsec_ecn, &proto, &itos);
123161f37615SAndrey V. Elsukov ip6->ip6_flow |= htonl((uint32_t)proto << 20);
123261f37615SAndrey V. Elsukov break;
123361f37615SAndrey V. Elsukov #endif /* INET6 */
123461f37615SAndrey V. Elsukov default:
123561f37615SAndrey V. Elsukov return (EAFNOSUPPORT);
123661f37615SAndrey V. Elsukov }
1237fcf59617SAndrey V. Elsukov (*mp)->m_flags &= ~(M_BCAST | M_MCAST);
123861f37615SAndrey V. Elsukov return (0);
123961f37615SAndrey V. Elsukov }
1240