1*5142f029Smsaitoh /* $NetBSD: ipsec.c,v 1.179 2024/05/13 00:12:33 msaitoh Exp $ */
2e2c8a664Smaxv /* $FreeBSD: ipsec.c,v 1.2.2.2 2003/07/01 01:38:13 sam Exp $ */
374029031Sjonathan /* $KAME: ipsec.c,v 1.103 2001/05/24 07:14:18 sakane Exp $ */
474029031Sjonathan
574029031Sjonathan /*
674029031Sjonathan * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
774029031Sjonathan * All rights reserved.
874029031Sjonathan *
974029031Sjonathan * Redistribution and use in source and binary forms, with or without
1074029031Sjonathan * modification, are permitted provided that the following conditions
1174029031Sjonathan * are met:
1274029031Sjonathan * 1. Redistributions of source code must retain the above copyright
1374029031Sjonathan * notice, this list of conditions and the following disclaimer.
1474029031Sjonathan * 2. Redistributions in binary form must reproduce the above copyright
1574029031Sjonathan * notice, this list of conditions and the following disclaimer in the
1674029031Sjonathan * documentation and/or other materials provided with the distribution.
1774029031Sjonathan * 3. Neither the name of the project nor the names of its contributors
1874029031Sjonathan * may be used to endorse or promote products derived from this software
1974029031Sjonathan * without specific prior written permission.
2074029031Sjonathan *
2174029031Sjonathan * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
2274029031Sjonathan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2374029031Sjonathan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2474029031Sjonathan * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
2574029031Sjonathan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2674029031Sjonathan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2774029031Sjonathan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2874029031Sjonathan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2974029031Sjonathan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3074029031Sjonathan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3174029031Sjonathan * SUCH DAMAGE.
3274029031Sjonathan */
3374029031Sjonathan
3474029031Sjonathan #include <sys/cdefs.h>
35*5142f029Smsaitoh __KERNEL_RCSID(0, "$NetBSD: ipsec.c,v 1.179 2024/05/13 00:12:33 msaitoh Exp $");
3674029031Sjonathan
3774029031Sjonathan /*
3874029031Sjonathan * IPsec controller part.
3974029031Sjonathan */
4074029031Sjonathan
4180d40a78Sozaki-r #if defined(_KERNEL_OPT)
4274029031Sjonathan #include "opt_inet.h"
4374029031Sjonathan #include "opt_ipsec.h"
4480d40a78Sozaki-r #endif
4574029031Sjonathan
4674029031Sjonathan #include <sys/param.h>
4774029031Sjonathan #include <sys/systm.h>
4874029031Sjonathan #include <sys/mbuf.h>
4974029031Sjonathan #include <sys/domain.h>
5074029031Sjonathan #include <sys/protosw.h>
5174029031Sjonathan #include <sys/socket.h>
5274029031Sjonathan #include <sys/socketvar.h>
5374029031Sjonathan #include <sys/errno.h>
5474029031Sjonathan #include <sys/time.h>
5574029031Sjonathan #include <sys/kernel.h>
5674029031Sjonathan #include <sys/syslog.h>
5774029031Sjonathan #include <sys/sysctl.h>
5874029031Sjonathan #include <sys/proc.h>
59efeb620eSelad #include <sys/kauth.h>
6020448372Sozaki-r #include <sys/cpu.h>
6120448372Sozaki-r #include <sys/kmem.h>
620c084e85Sozaki-r #include <sys/pserialize.h>
6374029031Sjonathan
6474029031Sjonathan #include <net/if.h>
6574029031Sjonathan #include <net/route.h>
6674029031Sjonathan
6774029031Sjonathan #include <netinet/in.h>
6874029031Sjonathan #include <netinet/in_systm.h>
6974029031Sjonathan #include <netinet/ip.h>
7074029031Sjonathan #include <netinet/ip_var.h>
7174029031Sjonathan #include <netinet/in_var.h>
7274029031Sjonathan #include <netinet/udp.h>
7374029031Sjonathan #include <netinet/udp_var.h>
7474029031Sjonathan #include <netinet/tcp.h>
7574029031Sjonathan #include <netinet/udp.h>
76fa014c63Smlelstv #include <netinet/ip_icmp.h>
777cb08cfdSrmind #include <netinet/ip_private.h>
7874029031Sjonathan
7974029031Sjonathan #include <netinet/ip6.h>
8074029031Sjonathan #ifdef INET6
8174029031Sjonathan #include <netinet6/ip6_var.h>
8274029031Sjonathan #endif
8374029031Sjonathan #include <netinet/in_pcb.h>
843574e990Smaxv #include <netinet/in_offload.h>
8574029031Sjonathan #ifdef INET6
86e139b206Sjonathan #include <netinet6/in6_pcb.h>
8774029031Sjonathan #include <netinet/icmp6.h>
8874029031Sjonathan #endif
8974029031Sjonathan
9074029031Sjonathan #include <netipsec/ipsec.h>
9185b3ba5bSjonathan #include <netipsec/ipsec_var.h>
92caf49ea5Sthorpej #include <netipsec/ipsec_private.h>
9374029031Sjonathan #ifdef INET6
9474029031Sjonathan #include <netipsec/ipsec6.h>
9574029031Sjonathan #endif
9674029031Sjonathan #include <netipsec/ah_var.h>
9774029031Sjonathan #include <netipsec/esp_var.h>
9874029031Sjonathan #include <netipsec/ipcomp.h> /*XXX*/
9974029031Sjonathan #include <netipsec/ipcomp_var.h>
10074029031Sjonathan
1019355900eStls #include <netipsec/key.h>
1029355900eStls #include <netipsec/keydb.h>
1039355900eStls #include <netipsec/key_debug.h>
10474029031Sjonathan
10574029031Sjonathan #include <netipsec/xform.h>
10674029031Sjonathan
1075d61e6c0Schristos int ipsec_used = 0;
1085d61e6c0Schristos int ipsec_enabled = 1;
1095d61e6c0Schristos
11074029031Sjonathan #ifdef IPSEC_DEBUG
11174029031Sjonathan int ipsec_debug = 1;
11268699904Srpaulo
11368699904Srpaulo /*
11468699904Srpaulo * When set to 1, IPsec will send packets with the same sequence number.
11568699904Srpaulo * This allows to verify if the other side has proper replay attacks detection.
11668699904Srpaulo */
11768699904Srpaulo int ipsec_replay = 0;
11868699904Srpaulo
11968699904Srpaulo /*
12068699904Srpaulo * When set 1, IPsec will send packets with corrupted HMAC.
12168699904Srpaulo * This allows to verify if the other side properly detects modified packets.
12268699904Srpaulo */
12368699904Srpaulo int ipsec_integrity = 0;
12474029031Sjonathan #else
12574029031Sjonathan int ipsec_debug = 0;
12674029031Sjonathan #endif
12774029031Sjonathan
128caf49ea5Sthorpej percpu_t *ipsecstat_percpu;
1299a3c5d51Smaxv
13074029031Sjonathan int ip4_ah_offsetmask = 0; /* maybe IP_DF? */
1319152d0e1Schristos int ip4_ipsec_dfbit = 2; /* DF bit on encap. 0: clear 1: set 2: copy */
13274029031Sjonathan int ip4_esp_trans_deflev = IPSEC_LEVEL_USE;
13374029031Sjonathan int ip4_esp_net_deflev = IPSEC_LEVEL_USE;
13474029031Sjonathan int ip4_ah_trans_deflev = IPSEC_LEVEL_USE;
13574029031Sjonathan int ip4_ah_net_deflev = IPSEC_LEVEL_USE;
13674029031Sjonathan struct secpolicy ip4_def_policy;
13774029031Sjonathan int ip4_ipsec_ecn = 0; /* ECN ignore(-1)/forbidden(0)/allowed(1) */
138ce5ecc33Sthorpej
139ce5ecc33Sthorpej u_int ipsec_spdgen = 1; /* SPD generation # */
140ce5ecc33Sthorpej
141603defb9Sozaki-r static struct secpolicy ipsec_dummy_sp __read_mostly = {
142603defb9Sozaki-r .state = IPSEC_SPSTATE_ALIVE,
143603defb9Sozaki-r /* If ENTRUST, the dummy SP never be used. See ipsec_getpolicybysock. */
144603defb9Sozaki-r .policy = IPSEC_POLICY_ENTRUST,
145603defb9Sozaki-r };
146603defb9Sozaki-r
147a382db0aSdegroote static struct secpolicy *ipsec_checkpcbcache(struct mbuf *,
148a382db0aSdegroote struct inpcbpolicy *, int);
149a382db0aSdegroote static int ipsec_fillpcbcache(struct inpcbpolicy *, struct mbuf *,
150a382db0aSdegroote struct secpolicy *, int);
151a382db0aSdegroote static int ipsec_invalpcbcache(struct inpcbpolicy *, int);
152ce5ecc33Sthorpej
15374029031Sjonathan /*
15474029031Sjonathan * Crypto support requirements:
15574029031Sjonathan *
15674029031Sjonathan * 1 require hardware support
15774029031Sjonathan * -1 require software support
15874029031Sjonathan * 0 take anything
15974029031Sjonathan */
16074029031Sjonathan int crypto_support = 0;
16174029031Sjonathan
162e139b206Sjonathan static struct secpolicy *ipsec_getpolicybysock(struct mbuf *, u_int,
1630e390eeeSozaki-r struct inpcb *, int *);
164e139b206Sjonathan
16574029031Sjonathan #ifdef INET6
16674029031Sjonathan int ip6_esp_trans_deflev = IPSEC_LEVEL_USE;
16774029031Sjonathan int ip6_esp_net_deflev = IPSEC_LEVEL_USE;
16874029031Sjonathan int ip6_ah_trans_deflev = IPSEC_LEVEL_USE;
16974029031Sjonathan int ip6_ah_net_deflev = IPSEC_LEVEL_USE;
170e139b206Sjonathan struct secpolicy ip6_def_policy;
17174029031Sjonathan int ip6_ipsec_ecn = 0; /* ECN ignore(-1)/forbidden(0)/allowed(1) */
1729a3c5d51Smaxv #endif
17374029031Sjonathan
1740e390eeeSozaki-r static int ipsec_setspidx_inpcb(struct mbuf *, struct inpcb *);
175e201bd44Smaxv static int ipsec_setspidx(struct mbuf *, struct secpolicyindex *, int, int);
176a382db0aSdegroote static void ipsec4_get_ulp(struct mbuf *m, struct secpolicyindex *, int);
177a382db0aSdegroote static int ipsec4_setspidx_ipaddr(struct mbuf *, struct secpolicyindex *);
17874029031Sjonathan #ifdef INET6
179a382db0aSdegroote static void ipsec6_get_ulp(struct mbuf *m, struct secpolicyindex *, int);
180a382db0aSdegroote static int ipsec6_setspidx_ipaddr(struct mbuf *, struct secpolicyindex *);
18174029031Sjonathan #endif
182a382db0aSdegroote static void ipsec_delpcbpolicy(struct inpcbpolicy *);
1830c084e85Sozaki-r static void ipsec_destroy_policy(struct secpolicy *);
184af69f639Smaxv static int ipsec_sp_reject(const struct secpolicy *, const struct mbuf *);
185a382db0aSdegroote static void vshiftl(unsigned char *, int, int);
186af69f639Smaxv static size_t ipsec_sp_hdrsiz(const struct secpolicy *, const struct mbuf *);
18774029031Sjonathan
188ce5ecc33Sthorpej /*
189ce5ecc33Sthorpej * Try to validate and use cached policy on a PCB.
190ce5ecc33Sthorpej */
191ce5ecc33Sthorpej static struct secpolicy *
ipsec_checkpcbcache(struct mbuf * m,struct inpcbpolicy * pcbsp,int dir)192ce5ecc33Sthorpej ipsec_checkpcbcache(struct mbuf *m, struct inpcbpolicy *pcbsp, int dir)
193ce5ecc33Sthorpej {
194ce5ecc33Sthorpej struct secpolicyindex spidx;
1950c084e85Sozaki-r struct secpolicy *sp = NULL;
1960c084e85Sozaki-r int s;
197ce5ecc33Sthorpej
1980df8f574Sozaki-r KASSERT(IPSEC_DIR_IS_VALID(dir));
19950d790a0Sozaki-r KASSERT(pcbsp != NULL);
2008db5286dSozaki-r KASSERT(dir < __arraycount(pcbsp->sp_cache));
2010e390eeeSozaki-r KASSERT(inp_locked(pcbsp->sp_inp));
20250d790a0Sozaki-r
2030c084e85Sozaki-r /*
2040c084e85Sozaki-r * Checking the generation and sp->state and taking a reference to an SP
2050c084e85Sozaki-r * must be in a critical section of pserialize. See key_unlink_sp.
2060c084e85Sozaki-r */
2070c084e85Sozaki-r s = pserialize_read_enter();
208ce5ecc33Sthorpej /* SPD table change invalidate all the caches. */
209ce5ecc33Sthorpej if (ipsec_spdgen != pcbsp->sp_cache[dir].cachegen) {
210ce5ecc33Sthorpej ipsec_invalpcbcache(pcbsp, dir);
2110c084e85Sozaki-r goto out;
212ce5ecc33Sthorpej }
2130c084e85Sozaki-r sp = pcbsp->sp_cache[dir].cachesp;
2140c084e85Sozaki-r if (sp == NULL)
2150c084e85Sozaki-r goto out;
2160c084e85Sozaki-r if (sp->state != IPSEC_SPSTATE_ALIVE) {
2170c084e85Sozaki-r sp = NULL;
218ce5ecc33Sthorpej ipsec_invalpcbcache(pcbsp, dir);
2190c084e85Sozaki-r goto out;
220ce5ecc33Sthorpej }
221ce5ecc33Sthorpej if ((pcbsp->sp_cacheflags & IPSEC_PCBSP_CONNECTED) == 0) {
2220c084e85Sozaki-r /* NB: assume ipsec_setspidx never sleep */
223e201bd44Smaxv if (ipsec_setspidx(m, &spidx, dir, 1) != 0) {
2240c084e85Sozaki-r sp = NULL;
2250c084e85Sozaki-r goto out;
2260c084e85Sozaki-r }
22740cf3d18Sdegroote
22840cf3d18Sdegroote /*
22940cf3d18Sdegroote * We have to make an exact match here since the cached rule
23040cf3d18Sdegroote * might have lower priority than a rule that would otherwise
23140cf3d18Sdegroote * have matched the packet.
23240cf3d18Sdegroote */
2330c084e85Sozaki-r if (memcmp(&pcbsp->sp_cache[dir].cacheidx, &spidx,
2340c084e85Sozaki-r sizeof(spidx))) {
2350c084e85Sozaki-r sp = NULL;
2360c084e85Sozaki-r goto out;
2370c084e85Sozaki-r }
238ce5ecc33Sthorpej } else {
239ce5ecc33Sthorpej /*
240ce5ecc33Sthorpej * The pcb is connected, and the L4 code is sure that:
241ce5ecc33Sthorpej * - outgoing side uses inp_[lf]addr
242ce5ecc33Sthorpej * - incoming side looks up policy after inpcb lookup
243ce5ecc33Sthorpej * and address pair is know to be stable. We do not need
244ce5ecc33Sthorpej * to generate spidx again, nor check the address match again.
245ce5ecc33Sthorpej *
246ce5ecc33Sthorpej * For IPv4/v6 SOCK_STREAM sockets, this assumptions holds
2472ba9f052Sozaki-r * and there are calls to ipsec_pcbconn() from inpcb_connect().
248ce5ecc33Sthorpej */
249ce5ecc33Sthorpej }
250ce5ecc33Sthorpej
251fea168e1Sknakahara key_sp_touch(sp);
2520c084e85Sozaki-r KEY_SP_REF(sp);
2536208c225Sozaki-r KEYDEBUG_PRINTF(KEYDEBUG_IPSEC_STAMP,
2546208c225Sozaki-r "DP cause refcnt++:%d SP:%p\n",
2550c084e85Sozaki-r key_sp_refcnt(sp), pcbsp->sp_cache[dir].cachesp);
2560c084e85Sozaki-r out:
2570c084e85Sozaki-r pserialize_read_exit(s);
2580c084e85Sozaki-r return sp;
259ce5ecc33Sthorpej }
260ce5ecc33Sthorpej
261ce5ecc33Sthorpej static int
ipsec_fillpcbcache(struct inpcbpolicy * pcbsp,struct mbuf * m,struct secpolicy * sp,int dir)262ce5ecc33Sthorpej ipsec_fillpcbcache(struct inpcbpolicy *pcbsp, struct mbuf *m,
263ce5ecc33Sthorpej struct secpolicy *sp, int dir)
264ce5ecc33Sthorpej {
265ce5ecc33Sthorpej
2660df8f574Sozaki-r KASSERT(IPSEC_DIR_IS_INOROUT(dir));
2678db5286dSozaki-r KASSERT(dir < __arraycount(pcbsp->sp_cache));
2680e390eeeSozaki-r KASSERT(inp_locked(pcbsp->sp_inp));
269ce5ecc33Sthorpej
270ce5ecc33Sthorpej pcbsp->sp_cache[dir].cachesp = NULL;
271a11fe343Sozaki-r pcbsp->sp_cache[dir].cachehint = IPSEC_PCBHINT_UNKNOWN;
272e201bd44Smaxv if (ipsec_setspidx(m, &pcbsp->sp_cache[dir].cacheidx, dir, 1) != 0) {
273ce5ecc33Sthorpej return EINVAL;
274ce5ecc33Sthorpej }
275ce5ecc33Sthorpej pcbsp->sp_cache[dir].cachesp = sp;
276ce5ecc33Sthorpej if (pcbsp->sp_cache[dir].cachesp) {
277ce5ecc33Sthorpej /*
278ce5ecc33Sthorpej * If the PCB is connected, we can remember a hint to
279ce5ecc33Sthorpej * possibly short-circuit IPsec processing in other places.
280ce5ecc33Sthorpej */
281ce5ecc33Sthorpej if (pcbsp->sp_cacheflags & IPSEC_PCBSP_CONNECTED) {
282ce5ecc33Sthorpej switch (pcbsp->sp_cache[dir].cachesp->policy) {
283ce5ecc33Sthorpej case IPSEC_POLICY_NONE:
284ce5ecc33Sthorpej case IPSEC_POLICY_BYPASS:
285ce5ecc33Sthorpej pcbsp->sp_cache[dir].cachehint =
286ce5ecc33Sthorpej IPSEC_PCBHINT_NO;
287ce5ecc33Sthorpej break;
288ce5ecc33Sthorpej default:
289ce5ecc33Sthorpej pcbsp->sp_cache[dir].cachehint =
290ce5ecc33Sthorpej IPSEC_PCBHINT_YES;
291ce5ecc33Sthorpej }
292ce5ecc33Sthorpej }
293ce5ecc33Sthorpej }
294ce5ecc33Sthorpej pcbsp->sp_cache[dir].cachegen = ipsec_spdgen;
295ce5ecc33Sthorpej
296ce5ecc33Sthorpej return 0;
297ce5ecc33Sthorpej }
298ce5ecc33Sthorpej
299ce5ecc33Sthorpej static int
ipsec_invalpcbcache(struct inpcbpolicy * pcbsp,int dir)300ce5ecc33Sthorpej ipsec_invalpcbcache(struct inpcbpolicy *pcbsp, int dir)
301ce5ecc33Sthorpej {
302ce5ecc33Sthorpej int i;
303ce5ecc33Sthorpej
3040e390eeeSozaki-r KASSERT(inp_locked(pcbsp->sp_inp));
3055cfcce1fSozaki-r
306ce5ecc33Sthorpej for (i = IPSEC_DIR_INBOUND; i <= IPSEC_DIR_OUTBOUND; i++) {
307ce5ecc33Sthorpej if (dir != IPSEC_DIR_ANY && i != dir)
308ce5ecc33Sthorpej continue;
309ce5ecc33Sthorpej pcbsp->sp_cache[i].cachesp = NULL;
310a11fe343Sozaki-r pcbsp->sp_cache[i].cachehint = IPSEC_PCBHINT_UNKNOWN;
311ce5ecc33Sthorpej pcbsp->sp_cache[i].cachegen = 0;
312c363a9cbScegger memset(&pcbsp->sp_cache[i].cacheidx, 0,
313ce5ecc33Sthorpej sizeof(pcbsp->sp_cache[i].cacheidx));
314ce5ecc33Sthorpej }
315ce5ecc33Sthorpej return 0;
316ce5ecc33Sthorpej }
317ce5ecc33Sthorpej
318ce5ecc33Sthorpej void
ipsec_pcbconn(struct inpcbpolicy * pcbsp)319ce5ecc33Sthorpej ipsec_pcbconn(struct inpcbpolicy *pcbsp)
320ce5ecc33Sthorpej {
321ce5ecc33Sthorpej
3220e390eeeSozaki-r KASSERT(inp_locked(pcbsp->sp_inp));
3235cfcce1fSozaki-r
324ce5ecc33Sthorpej pcbsp->sp_cacheflags |= IPSEC_PCBSP_CONNECTED;
325ce5ecc33Sthorpej ipsec_invalpcbcache(pcbsp, IPSEC_DIR_ANY);
326ce5ecc33Sthorpej }
327ce5ecc33Sthorpej
328ce5ecc33Sthorpej void
ipsec_pcbdisconn(struct inpcbpolicy * pcbsp)329ce5ecc33Sthorpej ipsec_pcbdisconn(struct inpcbpolicy *pcbsp)
330ce5ecc33Sthorpej {
331ce5ecc33Sthorpej
3320e390eeeSozaki-r KASSERT(inp_locked(pcbsp->sp_inp));
3335cfcce1fSozaki-r
334ce5ecc33Sthorpej pcbsp->sp_cacheflags &= ~IPSEC_PCBSP_CONNECTED;
335ce5ecc33Sthorpej ipsec_invalpcbcache(pcbsp, IPSEC_DIR_ANY);
336ce5ecc33Sthorpej }
337ce5ecc33Sthorpej
338ce5ecc33Sthorpej void
ipsec_invalpcbcacheall(void)339ce5ecc33Sthorpej ipsec_invalpcbcacheall(void)
340ce5ecc33Sthorpej {
341ce5ecc33Sthorpej
342ce5ecc33Sthorpej if (ipsec_spdgen == UINT_MAX)
343ce5ecc33Sthorpej ipsec_spdgen = 1;
344ce5ecc33Sthorpej else
345ce5ecc33Sthorpej ipsec_spdgen++;
346ce5ecc33Sthorpej }
347ce5ecc33Sthorpej
34874029031Sjonathan /*
34974029031Sjonathan * Return a held reference to the default SP.
35074029031Sjonathan */
35174029031Sjonathan static struct secpolicy *
key_get_default_sp(int af,const char * where,int tag)3526f13f59fSozaki-r key_get_default_sp(int af, const char *where, int tag)
35374029031Sjonathan {
35474029031Sjonathan struct secpolicy *sp;
35574029031Sjonathan
3566208c225Sozaki-r KEYDEBUG_PRINTF(KEYDEBUG_IPSEC_STAMP, "DP from %s:%u\n", where, tag);
35774029031Sjonathan
3586997fa5fSdegroote switch(af) {
3596997fa5fSdegroote case AF_INET:
36074029031Sjonathan sp = &ip4_def_policy;
3616997fa5fSdegroote break;
3626997fa5fSdegroote #ifdef INET6
3636997fa5fSdegroote case AF_INET6:
3646997fa5fSdegroote sp = &ip6_def_policy;
3656997fa5fSdegroote break;
3666997fa5fSdegroote #endif
3676997fa5fSdegroote default:
3686208c225Sozaki-r KEYDEBUG_PRINTF(KEYDEBUG_IPSEC_STAMP,
3696208c225Sozaki-r "unexpected protocol family %u\n", af);
3706997fa5fSdegroote return NULL;
3716997fa5fSdegroote }
3726997fa5fSdegroote
37374029031Sjonathan if (sp->policy != IPSEC_POLICY_DISCARD &&
37474029031Sjonathan sp->policy != IPSEC_POLICY_NONE) {
375290dc492Sozaki-r IPSECLOG(LOG_INFO, "fixed system default policy: %d->%d\n",
376290dc492Sozaki-r sp->policy, IPSEC_POLICY_NONE);
37774029031Sjonathan sp->policy = IPSEC_POLICY_NONE;
37874029031Sjonathan }
37990bba4b2Sozaki-r KEY_SP_REF(sp);
38074029031Sjonathan
3816208c225Sozaki-r KEYDEBUG_PRINTF(KEYDEBUG_IPSEC_STAMP, "DP returns SP:%p (%u)\n",
382a0f7ae36Sozaki-r sp, key_sp_refcnt(sp));
38374029031Sjonathan return sp;
38474029031Sjonathan }
3859a3c5d51Smaxv
3866f13f59fSozaki-r #define KEY_GET_DEFAULT_SP(af) \
3876f13f59fSozaki-r key_get_default_sp((af), __func__, __LINE__)
38874029031Sjonathan
38974029031Sjonathan /*
39074029031Sjonathan * For OUTBOUND packet having a socket. Searching SPD for packet,
39174029031Sjonathan * and return a pointer to SP.
39265b46b60Smaxv * OUT: NULL: no appropriate SP found, the following value is set to error.
39374029031Sjonathan * 0 : bypass
39474029031Sjonathan * EACCES : discard packet.
39574029031Sjonathan * ENOENT : ipsec_acquire() in progress, maybe.
396f05e6f1aSwiz * others : error occurred.
39774029031Sjonathan * others: a pointer to SP
39874029031Sjonathan *
3995d1e8b27Swiz * NOTE: IPv6 mapped address concern is implemented here.
40074029031Sjonathan */
401e139b206Sjonathan static struct secpolicy *
ipsec_getpolicybysock(struct mbuf * m,u_int dir,struct inpcb * inp,int * error)4020e390eeeSozaki-r ipsec_getpolicybysock(struct mbuf *m, u_int dir, struct inpcb *inp,
403ef67739aSozaki-r int *error)
40474029031Sjonathan {
40574029031Sjonathan struct inpcbpolicy *pcbsp = NULL;
40674029031Sjonathan struct secpolicy *currsp = NULL; /* policy on socket */
40774029031Sjonathan struct secpolicy *sp;
40874029031Sjonathan int af;
40974029031Sjonathan
4102620e166Sozaki-r KASSERT(m != NULL);
4110e390eeeSozaki-r KASSERT(inp != NULL);
4122620e166Sozaki-r KASSERT(error != NULL);
4130df8f574Sozaki-r KASSERTMSG(IPSEC_DIR_IS_INOROUT(dir), "invalid direction %u", dir);
41474029031Sjonathan
4150e390eeeSozaki-r KASSERT(inp->inp_socket != NULL);
4160e390eeeSozaki-r KASSERT(inp_locked(inp));
417e139b206Sjonathan
4180e390eeeSozaki-r /* XXX FIXME inpcb vs socket*/
4190e390eeeSozaki-r af = inp->inp_af;
4202620e166Sozaki-r KASSERTMSG(af == AF_INET || af == AF_INET6,
4212620e166Sozaki-r "unexpected protocol family %u", af);
42274029031Sjonathan
4230e390eeeSozaki-r KASSERT(inp->inp_sp != NULL);
424ce5ecc33Sthorpej /* If we have a cached entry, and if it is still valid, use it. */
425caf49ea5Sthorpej IPSEC_STATINC(IPSEC_STAT_SPDCACHELOOKUP);
4260e390eeeSozaki-r currsp = ipsec_checkpcbcache(m, inp->inp_sp, dir);
427ce5ecc33Sthorpej if (currsp) {
428ce5ecc33Sthorpej *error = 0;
429ce5ecc33Sthorpej return currsp;
430ce5ecc33Sthorpej }
431caf49ea5Sthorpej IPSEC_STATINC(IPSEC_STAT_SPDCACHEMISS);
432ce5ecc33Sthorpej
43374029031Sjonathan switch (af) {
434d219e3b4Smaxv case AF_INET:
435e139b206Sjonathan #if defined(INET6)
436d219e3b4Smaxv case AF_INET6:
43774029031Sjonathan #endif
4380e390eeeSozaki-r *error = ipsec_setspidx_inpcb(m, inp);
4390e390eeeSozaki-r pcbsp = inp->inp_sp;
440d219e3b4Smaxv break;
44174029031Sjonathan default:
44274029031Sjonathan *error = EPFNOSUPPORT;
44374029031Sjonathan break;
44474029031Sjonathan }
44574029031Sjonathan if (*error)
44674029031Sjonathan return NULL;
44774029031Sjonathan
4482620e166Sozaki-r KASSERT(pcbsp != NULL);
44974029031Sjonathan switch (dir) {
45074029031Sjonathan case IPSEC_DIR_INBOUND:
45174029031Sjonathan currsp = pcbsp->sp_in;
45274029031Sjonathan break;
45374029031Sjonathan case IPSEC_DIR_OUTBOUND:
45474029031Sjonathan currsp = pcbsp->sp_out;
45574029031Sjonathan break;
45674029031Sjonathan }
4572620e166Sozaki-r KASSERT(currsp != NULL);
45874029031Sjonathan
45952489f2bSmaxv if (pcbsp->priv) { /* when privileged socket */
46074029031Sjonathan switch (currsp->policy) {
46174029031Sjonathan case IPSEC_POLICY_BYPASS:
46274029031Sjonathan case IPSEC_POLICY_IPSEC:
46390bba4b2Sozaki-r KEY_SP_REF(currsp);
46474029031Sjonathan sp = currsp;
46574029031Sjonathan break;
46674029031Sjonathan
46774029031Sjonathan case IPSEC_POLICY_ENTRUST:
46874029031Sjonathan /* look for a policy in SPD */
469683fe570Sknakahara if (key_havesp(dir))
4706f13f59fSozaki-r sp = KEY_LOOKUP_SP_BYSPIDX(&currsp->spidx, dir);
471683fe570Sknakahara else
472683fe570Sknakahara sp = NULL;
47374029031Sjonathan if (sp == NULL) /* no SP found */
4746f13f59fSozaki-r sp = KEY_GET_DEFAULT_SP(af);
47574029031Sjonathan break;
47674029031Sjonathan
47774029031Sjonathan default:
478290dc492Sozaki-r IPSECLOG(LOG_ERR, "Invalid policy for PCB %d\n",
479290dc492Sozaki-r currsp->policy);
48074029031Sjonathan *error = EINVAL;
48174029031Sjonathan return NULL;
48274029031Sjonathan }
48374029031Sjonathan } else { /* unpriv, SPD has policy */
484683fe570Sknakahara if (key_havesp(dir))
4856f13f59fSozaki-r sp = KEY_LOOKUP_SP_BYSPIDX(&currsp->spidx, dir);
486683fe570Sknakahara else
487683fe570Sknakahara sp = NULL;
48874029031Sjonathan if (sp == NULL) { /* no SP found */
48974029031Sjonathan switch (currsp->policy) {
49074029031Sjonathan case IPSEC_POLICY_BYPASS:
491290dc492Sozaki-r IPSECLOG(LOG_ERR, "Illegal policy for "
492*5142f029Smsaitoh "non-privileged defined %d\n",
493290dc492Sozaki-r currsp->policy);
49474029031Sjonathan *error = EINVAL;
49574029031Sjonathan return NULL;
49674029031Sjonathan
49774029031Sjonathan case IPSEC_POLICY_ENTRUST:
4986f13f59fSozaki-r sp = KEY_GET_DEFAULT_SP(af);
49974029031Sjonathan break;
50074029031Sjonathan
50174029031Sjonathan case IPSEC_POLICY_IPSEC:
50290bba4b2Sozaki-r KEY_SP_REF(currsp);
50374029031Sjonathan sp = currsp;
50474029031Sjonathan break;
50574029031Sjonathan
50674029031Sjonathan default:
507290dc492Sozaki-r IPSECLOG(LOG_ERR, "Invalid policy for "
508290dc492Sozaki-r "PCB %d\n", currsp->policy);
50974029031Sjonathan *error = EINVAL;
51074029031Sjonathan return NULL;
51174029031Sjonathan }
51274029031Sjonathan }
51374029031Sjonathan }
5142620e166Sozaki-r KASSERTMSG(sp != NULL, "null SP (priv %u policy %u", pcbsp->priv,
5152620e166Sozaki-r currsp->policy);
5166208c225Sozaki-r KEYDEBUG_PRINTF(KEYDEBUG_IPSEC_STAMP,
5176208c225Sozaki-r "DP (priv %u policy %u) allocates SP:%p (refcnt %u)\n",
518a0f7ae36Sozaki-r pcbsp->priv, currsp->policy, sp, key_sp_refcnt(sp));
519ce5ecc33Sthorpej ipsec_fillpcbcache(pcbsp, m, sp, dir);
52074029031Sjonathan return sp;
52174029031Sjonathan }
52274029031Sjonathan
52374029031Sjonathan /*
524af69f639Smaxv * For FORWARDING packet or OUTBOUND without a socket. Searching SPD for packet,
52574029031Sjonathan * and return a pointer to SP.
52674029031Sjonathan * OUT: positive: a pointer to the entry for security policy leaf matched.
52765b46b60Smaxv * NULL: no appropriate SP found, the following value is set to error.
52874029031Sjonathan * 0 : bypass
52974029031Sjonathan * EACCES : discard packet.
53074029031Sjonathan * ENOENT : ipsec_acquire() in progress, maybe.
531f05e6f1aSwiz * others : error occurred.
53274029031Sjonathan */
53322e66c74Smaxv static struct secpolicy *
ipsec_getpolicybyaddr(struct mbuf * m,u_int dir,int flag,int * error)534a382db0aSdegroote ipsec_getpolicybyaddr(struct mbuf *m, u_int dir, int flag, int *error)
53574029031Sjonathan {
53674029031Sjonathan struct secpolicyindex spidx;
53774029031Sjonathan struct secpolicy *sp;
53874029031Sjonathan
5392620e166Sozaki-r KASSERT(m != NULL);
5402620e166Sozaki-r KASSERT(error != NULL);
5410df8f574Sozaki-r KASSERTMSG(IPSEC_DIR_IS_INOROUT(dir), "invalid direction %u", dir);
54274029031Sjonathan
54374029031Sjonathan sp = NULL;
5445f72dadbSdegroote
54574029031Sjonathan /* Make an index to look for a policy. */
5464a727625Sknakahara *error = ipsec_setspidx(m, &spidx, dir, 1);
54774029031Sjonathan if (*error != 0) {
548290dc492Sozaki-r IPSECLOG(LOG_DEBUG, "setpidx failed, dir %u flag %u\n", dir, flag);
549c363a9cbScegger memset(&spidx, 0, sizeof(spidx));
55074029031Sjonathan return NULL;
55174029031Sjonathan }
5525f72dadbSdegroote
55374029031Sjonathan spidx.dir = dir;
55474029031Sjonathan
5555f72dadbSdegroote if (key_havesp(dir)) {
5566f13f59fSozaki-r sp = KEY_LOOKUP_SP_BYSPIDX(&spidx, dir);
55774029031Sjonathan }
55825be83d5Smaxv if (sp == NULL) {
55925be83d5Smaxv /* no SP found, use system default */
5606f13f59fSozaki-r sp = KEY_GET_DEFAULT_SP(spidx.dst.sa.sa_family);
56125be83d5Smaxv }
56225be83d5Smaxv
5632620e166Sozaki-r KASSERT(sp != NULL);
56474029031Sjonathan return sp;
56574029031Sjonathan }
56674029031Sjonathan
56722e66c74Smaxv static struct secpolicy *
ipsec_checkpolicy(struct mbuf * m,u_int dir,u_int flag,int * error,struct inpcb * inp)5682953bb25Smaxv ipsec_checkpolicy(struct mbuf *m, u_int dir, u_int flag, int *error,
5690e390eeeSozaki-r struct inpcb *inp)
57074029031Sjonathan {
57174029031Sjonathan struct secpolicy *sp;
57274029031Sjonathan
57374029031Sjonathan *error = 0;
574e139b206Sjonathan
575ed8b1986Sozaki-r if (inp == NULL) {
57674029031Sjonathan sp = ipsec_getpolicybyaddr(m, dir, flag, error);
577ed8b1986Sozaki-r } else {
5780e390eeeSozaki-r KASSERT(inp->inp_socket != NULL);
5790e390eeeSozaki-r sp = ipsec_getpolicybysock(m, dir, inp, error);
580ed8b1986Sozaki-r }
58174029031Sjonathan if (sp == NULL) {
5822620e166Sozaki-r KASSERTMSG(*error != 0, "getpolicy failed w/o error");
583caf49ea5Sthorpej IPSEC_STATINC(IPSEC_STAT_OUT_INVAL);
58474029031Sjonathan return NULL;
58574029031Sjonathan }
5862620e166Sozaki-r KASSERTMSG(*error == 0, "sp w/ error set to %u", *error);
58716a6b570Smaxv
58874029031Sjonathan switch (sp->policy) {
58974029031Sjonathan case IPSEC_POLICY_ENTRUST:
59074029031Sjonathan default:
59136443d4aSchristos printf("%s: invalid policy %u\n", __func__, sp->policy);
59274029031Sjonathan /* fall thru... */
59374029031Sjonathan case IPSEC_POLICY_DISCARD:
594caf49ea5Sthorpej IPSEC_STATINC(IPSEC_STAT_OUT_POLVIO);
59574029031Sjonathan *error = -EINVAL; /* packet is discarded by caller */
59674029031Sjonathan break;
59774029031Sjonathan case IPSEC_POLICY_BYPASS:
59874029031Sjonathan case IPSEC_POLICY_NONE:
5990c084e85Sozaki-r KEY_SP_UNREF(&sp);
60074029031Sjonathan sp = NULL; /* NB: force NULL result */
60174029031Sjonathan break;
60274029031Sjonathan case IPSEC_POLICY_IPSEC:
60318db854eSozaki-r KASSERT(sp->req != NULL);
60474029031Sjonathan break;
60574029031Sjonathan }
60616a6b570Smaxv
60774029031Sjonathan if (*error != 0) {
6080c084e85Sozaki-r KEY_SP_UNREF(&sp);
60974029031Sjonathan sp = NULL;
610290dc492Sozaki-r IPSECLOG(LOG_DEBUG, "done, error %d\n", *error);
61174029031Sjonathan }
61216a6b570Smaxv
61374029031Sjonathan return sp;
61474029031Sjonathan }
61574029031Sjonathan
616e4506547Srmind int
ipsec4_output(struct mbuf * m,struct inpcb * inp,int flags,u_long * mtu,bool * natt_frag,bool * done,bool * count_drop)6172495e7a0Sozaki-r ipsec4_output(struct mbuf *m, struct inpcb *inp, int flags,
618c1e00d7dSozaki-r u_long *mtu, bool *natt_frag, bool *done, bool *count_drop)
619e4506547Srmind {
620e4506547Srmind struct secpolicy *sp = NULL;
62152489f2bSmaxv u_long _mtu = 0;
622986909fbSozaki-r int error;
623e4506547Srmind
624e4506547Srmind /*
625e4506547Srmind * Check the security policy (SP) for the packet and, if required,
626e4506547Srmind * do IPsec-related processing. There are two cases here; the first
627e4506547Srmind * time a packet is sent through it will be untagged and handled by
6282953bb25Smaxv * ipsec_checkpolicy(). If the packet is resubmitted to ip_output
629e4506547Srmind * (e.g. after AH, ESP, etc. processing), there will be a tag to
630e4506547Srmind * bypass the lookup and related policy checking.
631e4506547Srmind */
632e4506547Srmind if (ipsec_outdone(m)) {
633e4506547Srmind return 0;
634e4506547Srmind }
6354ce45a79Sozaki-r if (inp && ipsec_pcb_skip_ipsec(inp->inp_sp, IPSEC_DIR_OUTBOUND)) {
636e4506547Srmind return 0;
637e4506547Srmind }
6382953bb25Smaxv sp = ipsec_checkpolicy(m, IPSEC_DIR_OUTBOUND, flags, &error, inp);
639e4506547Srmind
640e4506547Srmind /*
641e4506547Srmind * There are four return cases:
642e4506547Srmind * sp != NULL apply IPsec policy
643e4506547Srmind * sp == NULL, error == 0 no IPsec handling needed
644e4506547Srmind * sp == NULL, error == -EINVAL discard packet w/o error
645e4506547Srmind * sp == NULL, error != 0 discard packet, report error
646e4506547Srmind */
647e4506547Srmind if (sp == NULL) {
648e4506547Srmind if (error) {
649e4506547Srmind /*
650e4506547Srmind * Hack: -EINVAL is used to signal that a packet
651e4506547Srmind * should be silently discarded. This is typically
652e4506547Srmind * because we asked key management for an SA and
653e4506547Srmind * it was delayed (e.g. kicked up to IKE).
654e4506547Srmind */
655e4506547Srmind if (error == -EINVAL)
656e4506547Srmind error = 0;
657e4506547Srmind m_freem(m);
658e4506547Srmind *done = true;
659c1e00d7dSozaki-r *count_drop = true;
660e4506547Srmind return error;
661e4506547Srmind }
662e4506547Srmind /* No IPsec processing for this packet. */
663e4506547Srmind return 0;
664e4506547Srmind }
665e4506547Srmind
666e4506547Srmind /*
667e4506547Srmind * Do delayed checksums now because we send before
668e4506547Srmind * this is done in the normal processing path.
669e4506547Srmind */
670e4506547Srmind if (m->m_pkthdr.csum_flags & (M_CSUM_TCPv4|M_CSUM_UDPv4)) {
6713574e990Smaxv in_undefer_cksum_tcpudp(m);
672e4506547Srmind m->m_pkthdr.csum_flags &= ~(M_CSUM_TCPv4|M_CSUM_UDPv4);
673e4506547Srmind }
674e4506547Srmind
675ea86b0a9Sozaki-r error = ipsec4_process_packet(m, sp->req, &_mtu);
676ea86b0a9Sozaki-r if (error == 0 && _mtu != 0) {
677ea86b0a9Sozaki-r /*
678ea86b0a9Sozaki-r * NAT-T ESP fragmentation: do not do IPSec processing
679ea86b0a9Sozaki-r * now, we will do it on each fragmented packet.
680ea86b0a9Sozaki-r */
681ea86b0a9Sozaki-r *mtu = _mtu;
682ea86b0a9Sozaki-r *natt_frag = true;
6830c084e85Sozaki-r KEY_SP_UNREF(&sp);
684ea86b0a9Sozaki-r return 0;
685ea86b0a9Sozaki-r }
68616a6b570Smaxv
687e4506547Srmind /*
688e4506547Srmind * Preserve KAME behaviour: ENOENT can be returned
689e4506547Srmind * when an SA acquire is in progress. Don't propagate
690e4506547Srmind * this to user-level; it confuses applications.
691e4506547Srmind *
692e4506547Srmind * XXX this will go away when the SADB is redone.
693e4506547Srmind */
694e4506547Srmind if (error == ENOENT)
695e4506547Srmind error = 0;
6960c084e85Sozaki-r KEY_SP_UNREF(&sp);
697e4506547Srmind *done = true;
698e4506547Srmind return error;
699e4506547Srmind }
700e4506547Srmind
7017cb08cfdSrmind int
ipsec_ip_input_checkpolicy(struct mbuf * m,bool forward)702b494441dSozaki-r ipsec_ip_input_checkpolicy(struct mbuf *m, bool forward)
7037cb08cfdSrmind {
7047cb08cfdSrmind struct secpolicy *sp;
705986909fbSozaki-r int error;
7067cb08cfdSrmind
7070695fe90Smaxv error = ipsec_in_reject(m, NULL);
7087cb08cfdSrmind if (error) {
7090695fe90Smaxv return EINVAL;
7107cb08cfdSrmind }
7117cb08cfdSrmind
71200ff305aSmaxv if (!forward || !(m->m_flags & M_CANFASTFWD)) {
7137cb08cfdSrmind return 0;
7147cb08cfdSrmind }
7157cb08cfdSrmind
71685265168Sozaki-r /*
71785265168Sozaki-r * Peek at the outbound SP for this packet to determine if
71885265168Sozaki-r * it is a Fast Forward candidate.
71985265168Sozaki-r */
72000ff305aSmaxv sp = ipsec_checkpolicy(m, IPSEC_DIR_OUTBOUND, IP_FORWARDING,
72100ff305aSmaxv &error, NULL);
7227cb08cfdSrmind if (sp != NULL) {
7237cb08cfdSrmind m->m_flags &= ~M_CANFASTFWD;
7240c084e85Sozaki-r KEY_SP_UNREF(&sp);
7257cb08cfdSrmind }
72600ff305aSmaxv
7277cb08cfdSrmind return 0;
7287cb08cfdSrmind }
7297cb08cfdSrmind
73025be83d5Smaxv /*
73125be83d5Smaxv * If the packet is routed over IPsec tunnel, tell the originator the
73225be83d5Smaxv * tunnel MTU.
73325be83d5Smaxv * tunnel MTU = if MTU - sizeof(IP) - ESP/AH hdrsiz
73425be83d5Smaxv *
73525be83d5Smaxv * XXX: Quick hack!!!
73625be83d5Smaxv *
73725be83d5Smaxv * XXX: And what if the MTU goes negative?
73825be83d5Smaxv */
739f813c44dSmaxv void
ipsec_mtu(struct mbuf * m,int * destmtu)740f813c44dSmaxv ipsec_mtu(struct mbuf *m, int *destmtu)
7417cb08cfdSrmind {
7427cb08cfdSrmind struct secpolicy *sp;
7437cb08cfdSrmind size_t ipsechdr;
7447cb08cfdSrmind int error;
7457cb08cfdSrmind
74616a6b570Smaxv sp = ipsec_getpolicybyaddr(m, IPSEC_DIR_OUTBOUND, IP_FORWARDING,
74716a6b570Smaxv &error);
7487cb08cfdSrmind if (sp == NULL) {
749f813c44dSmaxv return;
7507cb08cfdSrmind }
7517cb08cfdSrmind
7527cb08cfdSrmind /* Count IPsec header size. */
753215e1b41Smaxv ipsechdr = ipsec_sp_hdrsiz(sp, m);
7547cb08cfdSrmind
7557cb08cfdSrmind /*
756f813c44dSmaxv * Find the correct route for outer IP header, compute tunnel MTU.
7577cb08cfdSrmind */
7583bd6a580Sozaki-r if (sp->req) {
75913270c39Sozaki-r struct secasvar *sav;
76013270c39Sozaki-r
76113270c39Sozaki-r sav = ipsec_lookup_sa(sp->req, m);
76213270c39Sozaki-r if (sav != NULL) {
7637cb08cfdSrmind struct route *ro;
7647cb08cfdSrmind struct rtentry *rt;
7657cb08cfdSrmind
7663bd6a580Sozaki-r ro = &sav->sah->sa_route;
7677cb08cfdSrmind rt = rtcache_validate(ro);
7687cb08cfdSrmind if (rt && rt->rt_ifp) {
7697cb08cfdSrmind *destmtu = rt->rt_rmx.rmx_mtu ?
7707cb08cfdSrmind rt->rt_rmx.rmx_mtu : rt->rt_ifp->if_mtu;
7717cb08cfdSrmind *destmtu -= ipsechdr;
7727cb08cfdSrmind }
7734c25fb2fSozaki-r rtcache_unref(rt, ro);
7748be5cabcSozaki-r KEY_SA_UNREF(&sav);
7757cb08cfdSrmind }
77613270c39Sozaki-r }
7770c084e85Sozaki-r KEY_SP_UNREF(&sp);
7787cb08cfdSrmind }
7797cb08cfdSrmind
78074029031Sjonathan static int
ipsec_setspidx_inpcb(struct mbuf * m,struct inpcb * inp)7810e390eeeSozaki-r ipsec_setspidx_inpcb(struct mbuf *m, struct inpcb *inp)
78274029031Sjonathan {
78374029031Sjonathan int error;
78474029031Sjonathan
7850e390eeeSozaki-r KASSERT(inp != NULL);
7860e390eeeSozaki-r KASSERT(inp->inp_sp != NULL);
7870e390eeeSozaki-r KASSERT(inp->inp_sp->sp_out != NULL);
7880e390eeeSozaki-r KASSERT(inp->inp_sp->sp_in != NULL);
78974029031Sjonathan
7900e390eeeSozaki-r error = ipsec_setspidx(m, &inp->inp_sp->sp_in->spidx,
791e201bd44Smaxv IPSEC_DIR_INBOUND, 1);
79274029031Sjonathan if (error == 0) {
7930e390eeeSozaki-r inp->inp_sp->sp_out->spidx = inp->inp_sp->sp_in->spidx;
7940e390eeeSozaki-r inp->inp_sp->sp_out->spidx.dir = IPSEC_DIR_OUTBOUND;
79574029031Sjonathan } else {
7960e390eeeSozaki-r memset(&inp->inp_sp->sp_in->spidx, 0,
7970e390eeeSozaki-r sizeof(inp->inp_sp->sp_in->spidx));
7980e390eeeSozaki-r memset(&inp->inp_sp->sp_out->spidx, 0,
7990e390eeeSozaki-r sizeof(inp->inp_sp->sp_out->spidx));
80074029031Sjonathan }
80174029031Sjonathan return error;
80274029031Sjonathan }
80374029031Sjonathan
80474029031Sjonathan /*
80574029031Sjonathan * configure security policy index (src/dst/proto/sport/dport)
80674029031Sjonathan * by looking at the content of mbuf.
80774029031Sjonathan * the caller is responsible for error recovery (like clearing up spidx).
80874029031Sjonathan */
80974029031Sjonathan static int
ipsec_setspidx(struct mbuf * m,struct secpolicyindex * spidx,int dir,int needport)810e201bd44Smaxv ipsec_setspidx(struct mbuf *m, struct secpolicyindex *spidx, int dir,
811e201bd44Smaxv int needport)
81274029031Sjonathan {
81374029031Sjonathan struct ip *ip = NULL;
81474029031Sjonathan struct ip ipbuf;
81574029031Sjonathan u_int v;
81674029031Sjonathan int error;
81774029031Sjonathan
8182620e166Sozaki-r KASSERT(m != NULL);
81959275f73Smaxv M_VERIFY_PACKET(m);
82074029031Sjonathan
82174029031Sjonathan if (m->m_pkthdr.len < sizeof(struct ip)) {
8226208c225Sozaki-r KEYDEBUG_PRINTF(KEYDEBUG_IPSEC_DUMP,
8236208c225Sozaki-r "pkthdr.len(%d) < sizeof(struct ip), ignored.\n",
8246208c225Sozaki-r m->m_pkthdr.len);
82574029031Sjonathan return EINVAL;
82674029031Sjonathan }
82774029031Sjonathan
828e201bd44Smaxv memset(spidx, 0, sizeof(*spidx));
829e201bd44Smaxv spidx->dir = dir;
830e201bd44Smaxv
8319a3c5d51Smaxv if (m->m_len >= sizeof(*ip)) {
83274029031Sjonathan ip = mtod(m, struct ip *);
8339a3c5d51Smaxv } else {
834dd86ba72Sdegroote m_copydata(m, 0, sizeof(ipbuf), &ipbuf);
83574029031Sjonathan ip = &ipbuf;
83674029031Sjonathan }
83774029031Sjonathan v = ip->ip_v;
83874029031Sjonathan switch (v) {
83974029031Sjonathan case 4:
84074029031Sjonathan error = ipsec4_setspidx_ipaddr(m, spidx);
84174029031Sjonathan if (error)
84274029031Sjonathan return error;
84374029031Sjonathan ipsec4_get_ulp(m, spidx, needport);
84474029031Sjonathan return 0;
84574029031Sjonathan #ifdef INET6
84674029031Sjonathan case 6:
84774029031Sjonathan if (m->m_pkthdr.len < sizeof(struct ip6_hdr)) {
8486208c225Sozaki-r KEYDEBUG_PRINTF(KEYDEBUG_IPSEC_DUMP,
84974029031Sjonathan "pkthdr.len(%d) < sizeof(struct ip6_hdr), "
8506208c225Sozaki-r "ignored.\n", m->m_pkthdr.len);
85174029031Sjonathan return EINVAL;
85274029031Sjonathan }
85374029031Sjonathan error = ipsec6_setspidx_ipaddr(m, spidx);
85474029031Sjonathan if (error)
85574029031Sjonathan return error;
85674029031Sjonathan ipsec6_get_ulp(m, spidx, needport);
85774029031Sjonathan return 0;
85874029031Sjonathan #endif
85974029031Sjonathan default:
8606208c225Sozaki-r KEYDEBUG_PRINTF(KEYDEBUG_IPSEC_DUMP,
8616208c225Sozaki-r "unknown IP version %u, ignored.\n", v);
86274029031Sjonathan return EINVAL;
86374029031Sjonathan }
86474029031Sjonathan }
86574029031Sjonathan
86674029031Sjonathan static void
ipsec4_get_ulp(struct mbuf * m,struct secpolicyindex * spidx,int needport)86774029031Sjonathan ipsec4_get_ulp(struct mbuf *m, struct secpolicyindex *spidx, int needport)
86874029031Sjonathan {
86974029031Sjonathan u_int8_t nxt;
87074029031Sjonathan int off;
87174029031Sjonathan
8722620e166Sozaki-r KASSERT(m != NULL);
8732620e166Sozaki-r KASSERTMSG(m->m_pkthdr.len >= sizeof(struct ip), "packet too short");
87474029031Sjonathan
87574029031Sjonathan /* NB: ip_input() flips it into host endian XXX need more checking */
87668020cebSthorpej if (m->m_len >= sizeof(struct ip)) {
87774029031Sjonathan struct ip *ip = mtod(m, struct ip *);
878ef67739aSozaki-r if (ip->ip_off & htons(IP_MF | IP_OFFMASK))
87974029031Sjonathan goto done;
88074029031Sjonathan off = ip->ip_hl << 2;
88174029031Sjonathan nxt = ip->ip_p;
88274029031Sjonathan } else {
88374029031Sjonathan struct ip ih;
88474029031Sjonathan
885dd86ba72Sdegroote m_copydata(m, 0, sizeof(struct ip), &ih);
886ef67739aSozaki-r if (ih.ip_off & htons(IP_MF | IP_OFFMASK))
88774029031Sjonathan goto done;
88874029031Sjonathan off = ih.ip_hl << 2;
88974029031Sjonathan nxt = ih.ip_p;
89074029031Sjonathan }
89174029031Sjonathan
89274029031Sjonathan while (off < m->m_pkthdr.len) {
89374029031Sjonathan struct ip6_ext ip6e;
89474029031Sjonathan struct tcphdr th;
89574029031Sjonathan struct udphdr uh;
896fa014c63Smlelstv struct icmp icmph;
89774029031Sjonathan
89874029031Sjonathan switch (nxt) {
89974029031Sjonathan case IPPROTO_TCP:
90074029031Sjonathan spidx->ul_proto = nxt;
90174029031Sjonathan if (!needport)
90274029031Sjonathan goto done_proto;
90374029031Sjonathan if (off + sizeof(struct tcphdr) > m->m_pkthdr.len)
90474029031Sjonathan goto done;
905dd86ba72Sdegroote m_copydata(m, off, sizeof(th), &th);
90674029031Sjonathan spidx->src.sin.sin_port = th.th_sport;
90774029031Sjonathan spidx->dst.sin.sin_port = th.th_dport;
90874029031Sjonathan return;
90974029031Sjonathan case IPPROTO_UDP:
91074029031Sjonathan spidx->ul_proto = nxt;
91174029031Sjonathan if (!needport)
91274029031Sjonathan goto done_proto;
91374029031Sjonathan if (off + sizeof(struct udphdr) > m->m_pkthdr.len)
91474029031Sjonathan goto done;
915dd86ba72Sdegroote m_copydata(m, off, sizeof(uh), &uh);
91674029031Sjonathan spidx->src.sin.sin_port = uh.uh_sport;
91774029031Sjonathan spidx->dst.sin.sin_port = uh.uh_dport;
91874029031Sjonathan return;
91974029031Sjonathan case IPPROTO_AH:
9202603d1d6Smaxv if (off + sizeof(ip6e) > m->m_pkthdr.len)
92174029031Sjonathan goto done;
92274029031Sjonathan /* XXX sigh, this works but is totally bogus */
923dd86ba72Sdegroote m_copydata(m, off, sizeof(ip6e), &ip6e);
92474029031Sjonathan off += (ip6e.ip6e_len + 2) << 2;
92574029031Sjonathan nxt = ip6e.ip6e_nxt;
92674029031Sjonathan break;
92774029031Sjonathan case IPPROTO_ICMP:
928fa014c63Smlelstv spidx->ul_proto = nxt;
929fa014c63Smlelstv if (off + sizeof(struct icmp) > m->m_pkthdr.len)
930b9badffcSmaxv goto done;
931ba4ebf7eSdegroote m_copydata(m, off, sizeof(icmph), &icmph);
932fa014c63Smlelstv ((struct sockaddr_in *)&spidx->src)->sin_port =
933fa014c63Smlelstv htons((uint16_t)icmph.icmp_type);
934fa014c63Smlelstv ((struct sockaddr_in *)&spidx->dst)->sin_port =
935fa014c63Smlelstv htons((uint16_t)icmph.icmp_code);
936fa014c63Smlelstv return;
93774029031Sjonathan default:
93874029031Sjonathan /* XXX intermediate headers??? */
93974029031Sjonathan spidx->ul_proto = nxt;
94074029031Sjonathan goto done_proto;
94174029031Sjonathan }
94274029031Sjonathan }
94374029031Sjonathan done:
94474029031Sjonathan spidx->ul_proto = IPSEC_ULPROTO_ANY;
94574029031Sjonathan done_proto:
94674029031Sjonathan spidx->src.sin.sin_port = IPSEC_PORT_ANY;
94774029031Sjonathan spidx->dst.sin.sin_port = IPSEC_PORT_ANY;
94874029031Sjonathan }
94974029031Sjonathan
95074029031Sjonathan static int
ipsec4_setspidx_ipaddr(struct mbuf * m,struct secpolicyindex * spidx)95174029031Sjonathan ipsec4_setspidx_ipaddr(struct mbuf *m, struct secpolicyindex *spidx)
95274029031Sjonathan {
95374029031Sjonathan static const struct sockaddr_in template = {
95474029031Sjonathan sizeof(struct sockaddr_in),
95574029031Sjonathan AF_INET,
95674029031Sjonathan 0, { 0 }, { 0, 0, 0, 0, 0, 0, 0, 0 }
95774029031Sjonathan };
95874029031Sjonathan
95974029031Sjonathan spidx->src.sin = template;
96074029031Sjonathan spidx->dst.sin = template;
96174029031Sjonathan
96274029031Sjonathan if (m->m_len < sizeof(struct ip)) {
96374029031Sjonathan m_copydata(m, offsetof(struct ip, ip_src),
96492b56eb3Sozaki-r sizeof(struct in_addr), &spidx->src.sin.sin_addr);
96574029031Sjonathan m_copydata(m, offsetof(struct ip, ip_dst),
96692b56eb3Sozaki-r sizeof(struct in_addr), &spidx->dst.sin.sin_addr);
96774029031Sjonathan } else {
96874029031Sjonathan struct ip *ip = mtod(m, struct ip *);
96974029031Sjonathan spidx->src.sin.sin_addr = ip->ip_src;
97074029031Sjonathan spidx->dst.sin.sin_addr = ip->ip_dst;
97174029031Sjonathan }
97274029031Sjonathan
97374029031Sjonathan spidx->prefs = sizeof(struct in_addr) << 3;
97474029031Sjonathan spidx->prefd = sizeof(struct in_addr) << 3;
97574029031Sjonathan
97674029031Sjonathan return 0;
97774029031Sjonathan }
97874029031Sjonathan
97974029031Sjonathan #ifdef INET6
98074029031Sjonathan static void
ipsec6_get_ulp(struct mbuf * m,struct secpolicyindex * spidx,int needport)9819a3c5d51Smaxv ipsec6_get_ulp(struct mbuf *m, struct secpolicyindex *spidx, int needport)
98274029031Sjonathan {
98374029031Sjonathan int off, nxt;
98474029031Sjonathan struct tcphdr th;
98574029031Sjonathan struct udphdr uh;
986fa014c63Smlelstv struct icmp6_hdr icmph;
98774029031Sjonathan
9881d3af2e6Sozaki-r KASSERT(m != NULL);
98974029031Sjonathan
9906208c225Sozaki-r if (KEYDEBUG_ON(KEYDEBUG_IPSEC_DUMP)) {
991d6d612dcSchristos kdebug_mbuf(__func__, m);
9926208c225Sozaki-r }
99374029031Sjonathan
99474029031Sjonathan /* set default */
99574029031Sjonathan spidx->ul_proto = IPSEC_ULPROTO_ANY;
99674029031Sjonathan ((struct sockaddr_in6 *)&spidx->src)->sin6_port = IPSEC_PORT_ANY;
99774029031Sjonathan ((struct sockaddr_in6 *)&spidx->dst)->sin6_port = IPSEC_PORT_ANY;
99874029031Sjonathan
99974029031Sjonathan nxt = -1;
100074029031Sjonathan off = ip6_lasthdr(m, 0, IPPROTO_IPV6, &nxt);
100174029031Sjonathan if (off < 0 || m->m_pkthdr.len < off)
100274029031Sjonathan return;
100374029031Sjonathan
100474029031Sjonathan switch (nxt) {
100574029031Sjonathan case IPPROTO_TCP:
100674029031Sjonathan spidx->ul_proto = nxt;
100774029031Sjonathan if (!needport)
100874029031Sjonathan break;
100974029031Sjonathan if (off + sizeof(struct tcphdr) > m->m_pkthdr.len)
101074029031Sjonathan break;
1011dd86ba72Sdegroote m_copydata(m, off, sizeof(th), &th);
101274029031Sjonathan ((struct sockaddr_in6 *)&spidx->src)->sin6_port = th.th_sport;
101374029031Sjonathan ((struct sockaddr_in6 *)&spidx->dst)->sin6_port = th.th_dport;
101474029031Sjonathan break;
101574029031Sjonathan case IPPROTO_UDP:
101674029031Sjonathan spidx->ul_proto = nxt;
101774029031Sjonathan if (!needport)
101874029031Sjonathan break;
101974029031Sjonathan if (off + sizeof(struct udphdr) > m->m_pkthdr.len)
102074029031Sjonathan break;
1021dd86ba72Sdegroote m_copydata(m, off, sizeof(uh), &uh);
102274029031Sjonathan ((struct sockaddr_in6 *)&spidx->src)->sin6_port = uh.uh_sport;
102374029031Sjonathan ((struct sockaddr_in6 *)&spidx->dst)->sin6_port = uh.uh_dport;
102474029031Sjonathan break;
102574029031Sjonathan case IPPROTO_ICMPV6:
1026fa014c63Smlelstv spidx->ul_proto = nxt;
1027fa014c63Smlelstv if (off + sizeof(struct icmp6_hdr) > m->m_pkthdr.len)
1028fa014c63Smlelstv break;
1029ba4ebf7eSdegroote m_copydata(m, off, sizeof(icmph), &icmph);
1030fa014c63Smlelstv ((struct sockaddr_in6 *)&spidx->src)->sin6_port =
1031fa014c63Smlelstv htons((uint16_t)icmph.icmp6_type);
1032fa014c63Smlelstv ((struct sockaddr_in6 *)&spidx->dst)->sin6_port =
1033fa014c63Smlelstv htons((uint16_t)icmph.icmp6_code);
1034fa014c63Smlelstv break;
103574029031Sjonathan default:
103674029031Sjonathan /* XXX intermediate headers??? */
103774029031Sjonathan spidx->ul_proto = nxt;
103874029031Sjonathan break;
103974029031Sjonathan }
104074029031Sjonathan }
104174029031Sjonathan
104274029031Sjonathan static int
ipsec6_setspidx_ipaddr(struct mbuf * m,struct secpolicyindex * spidx)1043a382db0aSdegroote ipsec6_setspidx_ipaddr(struct mbuf *m, struct secpolicyindex *spidx)
104474029031Sjonathan {
104574029031Sjonathan struct ip6_hdr *ip6 = NULL;
104674029031Sjonathan struct ip6_hdr ip6buf;
104774029031Sjonathan struct sockaddr_in6 *sin6;
104874029031Sjonathan
104925be83d5Smaxv if (m->m_len >= sizeof(*ip6)) {
105074029031Sjonathan ip6 = mtod(m, struct ip6_hdr *);
105125be83d5Smaxv } else {
1052dd86ba72Sdegroote m_copydata(m, 0, sizeof(ip6buf), &ip6buf);
105374029031Sjonathan ip6 = &ip6buf;
105474029031Sjonathan }
105574029031Sjonathan
105674029031Sjonathan sin6 = (struct sockaddr_in6 *)&spidx->src;
1057c363a9cbScegger memset(sin6, 0, sizeof(*sin6));
105874029031Sjonathan sin6->sin6_family = AF_INET6;
105974029031Sjonathan sin6->sin6_len = sizeof(struct sockaddr_in6);
1060e2cb8590Scegger memcpy(&sin6->sin6_addr, &ip6->ip6_src, sizeof(ip6->ip6_src));
106174029031Sjonathan if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) {
106274029031Sjonathan sin6->sin6_addr.s6_addr16[1] = 0;
106374029031Sjonathan sin6->sin6_scope_id = ntohs(ip6->ip6_src.s6_addr16[1]);
106474029031Sjonathan }
106574029031Sjonathan spidx->prefs = sizeof(struct in6_addr) << 3;
106674029031Sjonathan
106774029031Sjonathan sin6 = (struct sockaddr_in6 *)&spidx->dst;
1068c363a9cbScegger memset(sin6, 0, sizeof(*sin6));
106974029031Sjonathan sin6->sin6_family = AF_INET6;
107074029031Sjonathan sin6->sin6_len = sizeof(struct sockaddr_in6);
1071e2cb8590Scegger memcpy(&sin6->sin6_addr, &ip6->ip6_dst, sizeof(ip6->ip6_dst));
107274029031Sjonathan if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) {
107374029031Sjonathan sin6->sin6_addr.s6_addr16[1] = 0;
107474029031Sjonathan sin6->sin6_scope_id = ntohs(ip6->ip6_dst.s6_addr16[1]);
107574029031Sjonathan }
107674029031Sjonathan spidx->prefd = sizeof(struct in6_addr) << 3;
107774029031Sjonathan
107874029031Sjonathan return 0;
107974029031Sjonathan }
108074029031Sjonathan #endif
108174029031Sjonathan
108274029031Sjonathan static void
ipsec_delpcbpolicy(struct inpcbpolicy * p)1083a382db0aSdegroote ipsec_delpcbpolicy(struct inpcbpolicy *p)
108474029031Sjonathan {
108520448372Sozaki-r
1086b5d03f64Sozaki-r kmem_intr_free(p, sizeof(*p));
108774029031Sjonathan }
108874029031Sjonathan
108974029031Sjonathan int
ipsec_init_pcbpolicy(struct socket * so,struct inpcbpolicy ** policy)109019f9cae6Smaxv ipsec_init_pcbpolicy(struct socket *so, struct inpcbpolicy **policy)
109174029031Sjonathan {
109274029031Sjonathan struct inpcbpolicy *new;
109374029031Sjonathan
10941d3af2e6Sozaki-r KASSERT(so != NULL);
10951d3af2e6Sozaki-r KASSERT(policy != NULL);
109674029031Sjonathan
1097b5d03f64Sozaki-r new = kmem_intr_zalloc(sizeof(*new), KM_NOSLEEP);
1098b5d03f64Sozaki-r if (new == NULL) {
1099290dc492Sozaki-r IPSECLOG(LOG_DEBUG, "No more memory.\n");
1100b5d03f64Sozaki-r return ENOBUFS;
1101b5d03f64Sozaki-r }
110274029031Sjonathan
110374029031Sjonathan if (IPSEC_PRIVILEGED_SO(so))
110474029031Sjonathan new->priv = 1;
110574029031Sjonathan else
110674029031Sjonathan new->priv = 0;
110774029031Sjonathan
11080c084e85Sozaki-r /*
1109603defb9Sozaki-r * Set dummy SPs. Actual SPs will be allocated later if needed.
11100c084e85Sozaki-r */
1111603defb9Sozaki-r new->sp_in = &ipsec_dummy_sp;
1112603defb9Sozaki-r new->sp_out = &ipsec_dummy_sp;
111374029031Sjonathan
111462903782Schristos *policy = new;
111574029031Sjonathan
111674029031Sjonathan return 0;
111774029031Sjonathan }
111874029031Sjonathan
11190c084e85Sozaki-r static void
ipsec_destroy_policy(struct secpolicy * sp)11200c084e85Sozaki-r ipsec_destroy_policy(struct secpolicy *sp)
11210c084e85Sozaki-r {
11220c084e85Sozaki-r
112325be83d5Smaxv if (sp == &ipsec_dummy_sp) {
1124603defb9Sozaki-r ; /* It's dummy. No need to free it. */
112525be83d5Smaxv } else {
11260c084e85Sozaki-r /*
11270c084e85Sozaki-r * We cannot destroy here because it can be called in
11280c084e85Sozaki-r * softint. So mark the SP as DEAD and let the timer
11290c084e85Sozaki-r * destroy it. See key_timehandler_spd.
11300c084e85Sozaki-r */
11310c084e85Sozaki-r sp->state = IPSEC_SPSTATE_DEAD;
11320c084e85Sozaki-r }
11330c084e85Sozaki-r }
11340c084e85Sozaki-r
1135faad9849Smaxv int
ipsec_set_policy(struct inpcb * inp,const void * request,size_t len,kauth_cred_t cred)11360e390eeeSozaki-r ipsec_set_policy(struct inpcb *inp, const void *request, size_t len,
1137faad9849Smaxv kauth_cred_t cred)
113874029031Sjonathan {
11392cd69bbbSdrochner const struct sadb_x_policy *xpl;
11406fd7a9a7Smaxv struct secpolicy *newsp, *oldsp;
1141faad9849Smaxv struct secpolicy **policy;
114274029031Sjonathan int error;
114374029031Sjonathan
114420448372Sozaki-r KASSERT(!cpu_softintr_p());
11450e390eeeSozaki-r KASSERT(inp != NULL);
11460e390eeeSozaki-r KASSERT(inp_locked(inp));
1147faad9849Smaxv KASSERT(request != NULL);
1148faad9849Smaxv
1149faad9849Smaxv if (len < sizeof(*xpl))
1150faad9849Smaxv return EINVAL;
1151faad9849Smaxv xpl = (const struct sadb_x_policy *)request;
1152faad9849Smaxv
11530e390eeeSozaki-r KASSERT(inp->inp_sp != NULL);
1154faad9849Smaxv
1155faad9849Smaxv /* select direction */
1156faad9849Smaxv switch (xpl->sadb_x_policy_dir) {
1157faad9849Smaxv case IPSEC_DIR_INBOUND:
11580e390eeeSozaki-r policy = &inp->inp_sp->sp_in;
1159faad9849Smaxv break;
1160faad9849Smaxv case IPSEC_DIR_OUTBOUND:
11610e390eeeSozaki-r policy = &inp->inp_sp->sp_out;
1162faad9849Smaxv break;
1163faad9849Smaxv default:
1164faad9849Smaxv IPSECLOG(LOG_ERR, "invalid direction=%u\n",
1165faad9849Smaxv xpl->sadb_x_policy_dir);
1166faad9849Smaxv return EINVAL;
1167faad9849Smaxv }
116820448372Sozaki-r
116974029031Sjonathan /* sanity check. */
11706fd7a9a7Smaxv if (policy == NULL || *policy == NULL)
117174029031Sjonathan return EINVAL;
117274029031Sjonathan
11736208c225Sozaki-r if (KEYDEBUG_ON(KEYDEBUG_IPSEC_DUMP)) {
1174d6d612dcSchristos kdebug_sadb_xpolicy("set passed policy", request);
11756208c225Sozaki-r }
117674029031Sjonathan
117774029031Sjonathan /* check policy type */
117874029031Sjonathan /* ipsec_set_policy() accepts IPSEC, ENTRUST and BYPASS. */
11799a3c5d51Smaxv if (xpl->sadb_x_policy_type == IPSEC_POLICY_DISCARD ||
11809a3c5d51Smaxv xpl->sadb_x_policy_type == IPSEC_POLICY_NONE)
118174029031Sjonathan return EINVAL;
118274029031Sjonathan
118374029031Sjonathan /* check privileged socket */
1184efeb620eSelad if (xpl->sadb_x_policy_type == IPSEC_POLICY_BYPASS) {
11850c9d8d15Selad error = kauth_authorize_network(cred, KAUTH_NETWORK_IPSEC,
11860c9d8d15Selad KAUTH_REQ_NETWORK_IPSEC_BYPASS, NULL, NULL, NULL);
1187efeb620eSelad if (error)
11889a3c5d51Smaxv return error;
1189efeb620eSelad }
119074029031Sjonathan
119174029031Sjonathan /* allocation new SP entry */
119274029031Sjonathan if ((newsp = key_msg2sp(xpl, len, &error)) == NULL)
119374029031Sjonathan return error;
119474029031Sjonathan
11950c084e85Sozaki-r key_init_sp(newsp);
11960c084e85Sozaki-r newsp->created = time_uptime;
11970c084e85Sozaki-r /* Insert the global list for SPs for sockets */
11980c084e85Sozaki-r key_socksplist_add(newsp);
119974029031Sjonathan
120074029031Sjonathan /* clear old SP and set new SP */
12010c084e85Sozaki-r oldsp = *policy;
120262903782Schristos *policy = newsp;
12030c084e85Sozaki-r ipsec_destroy_policy(oldsp);
12040c084e85Sozaki-r
12056208c225Sozaki-r if (KEYDEBUG_ON(KEYDEBUG_IPSEC_DUMP)) {
12066208c225Sozaki-r printf("%s: new policy\n", __func__);
12076208c225Sozaki-r kdebug_secpolicy(newsp);
12086208c225Sozaki-r }
120974029031Sjonathan
121074029031Sjonathan return 0;
121174029031Sjonathan }
121274029031Sjonathan
121374029031Sjonathan int
ipsec_get_policy(struct inpcb * inp,const void * request,size_t len,struct mbuf ** mp)12140e390eeeSozaki-r ipsec_get_policy(struct inpcb *inp, const void *request, size_t len,
1215a382db0aSdegroote struct mbuf **mp)
121674029031Sjonathan {
12172cd69bbbSdrochner const struct sadb_x_policy *xpl;
121862903782Schristos struct secpolicy *policy;
121974029031Sjonathan
122074029031Sjonathan /* sanity check. */
12210e390eeeSozaki-r if (inp == NULL || request == NULL || mp == NULL)
122274029031Sjonathan return EINVAL;
12230e390eeeSozaki-r KASSERT(inp->inp_sp != NULL);
122474029031Sjonathan if (len < sizeof(*xpl))
122574029031Sjonathan return EINVAL;
12262cd69bbbSdrochner xpl = (const struct sadb_x_policy *)request;
122774029031Sjonathan
122874029031Sjonathan /* select direction */
122974029031Sjonathan switch (xpl->sadb_x_policy_dir) {
123074029031Sjonathan case IPSEC_DIR_INBOUND:
12310e390eeeSozaki-r policy = inp->inp_sp->sp_in;
123274029031Sjonathan break;
123374029031Sjonathan case IPSEC_DIR_OUTBOUND:
12340e390eeeSozaki-r policy = inp->inp_sp->sp_out;
123574029031Sjonathan break;
123674029031Sjonathan default:
1237290dc492Sozaki-r IPSECLOG(LOG_ERR, "invalid direction=%u\n",
1238290dc492Sozaki-r xpl->sadb_x_policy_dir);
123974029031Sjonathan return EINVAL;
124074029031Sjonathan }
124174029031Sjonathan
12426fd7a9a7Smaxv if (policy == NULL)
1243580ac4beSmaxv return EINVAL;
1244580ac4beSmaxv
1245580ac4beSmaxv *mp = key_sp2msg(policy, M_NOWAIT);
1246580ac4beSmaxv if (!*mp) {
1247580ac4beSmaxv IPSECLOG(LOG_DEBUG, "No more memory.\n");
1248580ac4beSmaxv return ENOBUFS;
1249580ac4beSmaxv }
1250580ac4beSmaxv
1251580ac4beSmaxv if (KEYDEBUG_ON(KEYDEBUG_IPSEC_DUMP)) {
1252580ac4beSmaxv kdebug_mbuf(__func__, *mp);
1253580ac4beSmaxv }
1254580ac4beSmaxv
1255580ac4beSmaxv return 0;
125674029031Sjonathan }
125774029031Sjonathan
125874029031Sjonathan int
ipsec_delete_pcbpolicy(struct inpcb * inp)12590e390eeeSozaki-r ipsec_delete_pcbpolicy(struct inpcb *inp)
126074029031Sjonathan {
12612620e166Sozaki-r
12620e390eeeSozaki-r KASSERT(inp != NULL);
126374029031Sjonathan
12640e390eeeSozaki-r if (inp->inp_sp == NULL)
126574029031Sjonathan return 0;
126674029031Sjonathan
12670e390eeeSozaki-r if (inp->inp_sp->sp_in != NULL)
12680e390eeeSozaki-r ipsec_destroy_policy(inp->inp_sp->sp_in);
126974029031Sjonathan
12700e390eeeSozaki-r if (inp->inp_sp->sp_out != NULL)
12710e390eeeSozaki-r ipsec_destroy_policy(inp->inp_sp->sp_out);
127274029031Sjonathan
12730e390eeeSozaki-r ipsec_invalpcbcache(inp->inp_sp, IPSEC_DIR_ANY);
12747d0822f8Sdrochner
12750e390eeeSozaki-r ipsec_delpcbpolicy(inp->inp_sp);
12760e390eeeSozaki-r inp->inp_sp = NULL;
127774029031Sjonathan
127874029031Sjonathan return 0;
127974029031Sjonathan }
128074029031Sjonathan
128174029031Sjonathan /*
128216a6b570Smaxv * Return the current level (either IPSEC_LEVEL_USE or IPSEC_LEVEL_REQUIRE).
128374029031Sjonathan */
128474029031Sjonathan u_int
ipsec_get_reqlevel(const struct ipsecrequest * isr)1285dd877261Schristos ipsec_get_reqlevel(const struct ipsecrequest *isr)
128674029031Sjonathan {
128774029031Sjonathan u_int level = 0;
128874029031Sjonathan u_int esp_trans_deflev, esp_net_deflev;
128974029031Sjonathan u_int ah_trans_deflev, ah_net_deflev;
129074029031Sjonathan
12912620e166Sozaki-r KASSERT(isr != NULL);
12922620e166Sozaki-r KASSERT(isr->sp != NULL);
12932620e166Sozaki-r KASSERTMSG(
12942620e166Sozaki-r isr->sp->spidx.src.sa.sa_family == isr->sp->spidx.dst.sa.sa_family,
12952620e166Sozaki-r "af family mismatch, src %u, dst %u",
12962620e166Sozaki-r isr->sp->spidx.src.sa.sa_family, isr->sp->spidx.dst.sa.sa_family);
129774029031Sjonathan
129874029031Sjonathan /* XXX note that we have ipseclog() expanded here - code sync issue */
129974029031Sjonathan #define IPSEC_CHECK_DEFAULT(lev) \
130074029031Sjonathan (((lev) != IPSEC_LEVEL_USE && (lev) != IPSEC_LEVEL_REQUIRE \
130136443d4aSchristos && (lev) != IPSEC_LEVEL_UNIQUE) ? \
130236443d4aSchristos (ipsec_debug ? log(LOG_INFO, "fixed system default level " #lev \
13037300f5c7Splunky ":%d->%d\n", (lev), IPSEC_LEVEL_REQUIRE) : (void)0), \
130436443d4aSchristos (lev) = IPSEC_LEVEL_REQUIRE, (lev) \
130574029031Sjonathan : (lev))
130674029031Sjonathan
130774029031Sjonathan /* set default level */
130874029031Sjonathan switch (((struct sockaddr *)&isr->sp->spidx.src)->sa_family) {
130974029031Sjonathan #ifdef INET
131074029031Sjonathan case AF_INET:
131174029031Sjonathan esp_trans_deflev = IPSEC_CHECK_DEFAULT(ip4_esp_trans_deflev);
131274029031Sjonathan esp_net_deflev = IPSEC_CHECK_DEFAULT(ip4_esp_net_deflev);
131374029031Sjonathan ah_trans_deflev = IPSEC_CHECK_DEFAULT(ip4_ah_trans_deflev);
131474029031Sjonathan ah_net_deflev = IPSEC_CHECK_DEFAULT(ip4_ah_net_deflev);
131574029031Sjonathan break;
131674029031Sjonathan #endif
131774029031Sjonathan #ifdef INET6
131874029031Sjonathan case AF_INET6:
131974029031Sjonathan esp_trans_deflev = IPSEC_CHECK_DEFAULT(ip6_esp_trans_deflev);
132074029031Sjonathan esp_net_deflev = IPSEC_CHECK_DEFAULT(ip6_esp_net_deflev);
132174029031Sjonathan ah_trans_deflev = IPSEC_CHECK_DEFAULT(ip6_ah_trans_deflev);
132274029031Sjonathan ah_net_deflev = IPSEC_CHECK_DEFAULT(ip6_ah_net_deflev);
132374029031Sjonathan break;
132425be83d5Smaxv #endif
132574029031Sjonathan default:
132636443d4aSchristos panic("%s: unknown af %u", __func__,
132774029031Sjonathan isr->sp->spidx.src.sa.sa_family);
132874029031Sjonathan }
132974029031Sjonathan
133074029031Sjonathan #undef IPSEC_CHECK_DEFAULT
133174029031Sjonathan
133274029031Sjonathan /* set level */
133374029031Sjonathan switch (isr->level) {
133474029031Sjonathan case IPSEC_LEVEL_DEFAULT:
133574029031Sjonathan switch (isr->saidx.proto) {
133674029031Sjonathan case IPPROTO_ESP:
133774029031Sjonathan if (isr->saidx.mode == IPSEC_MODE_TUNNEL)
133874029031Sjonathan level = esp_net_deflev;
133974029031Sjonathan else
134074029031Sjonathan level = esp_trans_deflev;
134174029031Sjonathan break;
134274029031Sjonathan case IPPROTO_AH:
134374029031Sjonathan if (isr->saidx.mode == IPSEC_MODE_TUNNEL)
134474029031Sjonathan level = ah_net_deflev;
134574029031Sjonathan else
134674029031Sjonathan level = ah_trans_deflev;
13472bee2ad8Sjonathan break;
134874029031Sjonathan case IPPROTO_IPCOMP:
134974029031Sjonathan /*
135074029031Sjonathan * we don't really care, as IPcomp document says that
135174029031Sjonathan * we shouldn't compress small packets
135274029031Sjonathan */
135374029031Sjonathan level = IPSEC_LEVEL_USE;
135474029031Sjonathan break;
135574029031Sjonathan default:
135636443d4aSchristos panic("%s: Illegal protocol defined %u", __func__,
135774029031Sjonathan isr->saidx.proto);
135874029031Sjonathan }
135974029031Sjonathan break;
136074029031Sjonathan
136174029031Sjonathan case IPSEC_LEVEL_USE:
136274029031Sjonathan case IPSEC_LEVEL_REQUIRE:
136374029031Sjonathan level = isr->level;
136474029031Sjonathan break;
136574029031Sjonathan case IPSEC_LEVEL_UNIQUE:
136674029031Sjonathan level = IPSEC_LEVEL_REQUIRE;
136774029031Sjonathan break;
136874029031Sjonathan
136974029031Sjonathan default:
137036443d4aSchristos panic("%s: Illegal IPsec level %u", __func__, isr->level);
137174029031Sjonathan }
137274029031Sjonathan
137374029031Sjonathan return level;
137474029031Sjonathan }
137574029031Sjonathan
137674029031Sjonathan /*
137722e66c74Smaxv * Check security policy requirements against the actual packet contents.
137874029031Sjonathan *
137922e66c74Smaxv * If the SP requires an IPsec packet, and the packet was neither AH nor ESP,
138022e66c74Smaxv * then kick it.
138174029031Sjonathan */
1382f3215192Smaxv static int
ipsec_sp_reject(const struct secpolicy * sp,const struct mbuf * m)1383af69f639Smaxv ipsec_sp_reject(const struct secpolicy *sp, const struct mbuf *m)
138474029031Sjonathan {
138574029031Sjonathan struct ipsecrequest *isr;
138674029031Sjonathan
13876208c225Sozaki-r if (KEYDEBUG_ON(KEYDEBUG_IPSEC_DATA)) {
13886208c225Sozaki-r printf("%s: using SP\n", __func__);
13896208c225Sozaki-r kdebug_secpolicy(sp);
13906208c225Sozaki-r }
139174029031Sjonathan
139274029031Sjonathan /* check policy */
139374029031Sjonathan switch (sp->policy) {
139474029031Sjonathan case IPSEC_POLICY_DISCARD:
139574029031Sjonathan return 1;
139674029031Sjonathan case IPSEC_POLICY_BYPASS:
139774029031Sjonathan case IPSEC_POLICY_NONE:
139874029031Sjonathan return 0;
139974029031Sjonathan }
140074029031Sjonathan
14012620e166Sozaki-r KASSERTMSG(sp->policy == IPSEC_POLICY_IPSEC,
14022620e166Sozaki-r "invalid policy %u", sp->policy);
140374029031Sjonathan
140474029031Sjonathan /* XXX should compare policy against ipsec header history */
140574029031Sjonathan
140674029031Sjonathan for (isr = sp->req; isr != NULL; isr = isr->next) {
140774029031Sjonathan if (ipsec_get_reqlevel(isr) != IPSEC_LEVEL_REQUIRE)
140874029031Sjonathan continue;
140974029031Sjonathan switch (isr->saidx.proto) {
141074029031Sjonathan case IPPROTO_ESP:
141174029031Sjonathan if ((m->m_flags & M_DECRYPTED) == 0) {
14126208c225Sozaki-r KEYDEBUG_PRINTF(KEYDEBUG_IPSEC_DUMP,
14136208c225Sozaki-r "ESP m_flags:%x\n", m->m_flags);
141474029031Sjonathan return 1;
141574029031Sjonathan }
141674029031Sjonathan break;
141774029031Sjonathan case IPPROTO_AH:
141874029031Sjonathan if ((m->m_flags & M_AUTHIPHDR) == 0) {
14196208c225Sozaki-r KEYDEBUG_PRINTF(KEYDEBUG_IPSEC_DUMP,
14206208c225Sozaki-r "AH m_flags:%x\n", m->m_flags);
142174029031Sjonathan return 1;
142274029031Sjonathan }
142374029031Sjonathan break;
142474029031Sjonathan case IPPROTO_IPCOMP:
142574029031Sjonathan /*
142622e66c74Smaxv * We don't really care, as IPcomp document
142774029031Sjonathan * says that we shouldn't compress small
142874029031Sjonathan * packets, IPComp policy should always be
142974029031Sjonathan * treated as being in "use" level.
143074029031Sjonathan */
143174029031Sjonathan break;
143274029031Sjonathan }
143374029031Sjonathan }
1434af69f639Smaxv
1435af69f639Smaxv return 0;
143674029031Sjonathan }
143774029031Sjonathan
143874029031Sjonathan /*
1439b39a0dd1Smaxv * Check security policy requirements.
144074029031Sjonathan */
144174029031Sjonathan int
ipsec_in_reject(struct mbuf * m,struct inpcb * inp)14420e390eeeSozaki-r ipsec_in_reject(struct mbuf *m, struct inpcb *inp)
144374029031Sjonathan {
144474029031Sjonathan struct secpolicy *sp;
144574029031Sjonathan int error;
144674029031Sjonathan int result;
144774029031Sjonathan
14482620e166Sozaki-r KASSERT(m != NULL);
144974029031Sjonathan
14500e390eeeSozaki-r if (inp == NULL)
145122e66c74Smaxv sp = ipsec_getpolicybyaddr(m, IPSEC_DIR_INBOUND,
145222e66c74Smaxv IP_FORWARDING, &error);
145374029031Sjonathan else
1454e139b206Sjonathan sp = ipsec_getpolicybysock(m, IPSEC_DIR_INBOUND,
14550e390eeeSozaki-r inp, &error);
145674029031Sjonathan
145774029031Sjonathan if (sp != NULL) {
1458af69f639Smaxv result = ipsec_sp_reject(sp, m);
145974029031Sjonathan if (result)
1460caf49ea5Sthorpej IPSEC_STATINC(IPSEC_STAT_IN_POLVIO);
14610c084e85Sozaki-r KEY_SP_UNREF(&sp);
146274029031Sjonathan } else {
146322e66c74Smaxv result = 0;
146474029031Sjonathan }
146574029031Sjonathan return result;
146674029031Sjonathan }
146774029031Sjonathan
146874029031Sjonathan /*
1469af69f639Smaxv * Compute the byte size to be occupied by the IPsec header. If it is
1470af69f639Smaxv * tunneled, it includes the size of outer IP header.
147174029031Sjonathan */
147274029031Sjonathan static size_t
ipsec_sp_hdrsiz(const struct secpolicy * sp,const struct mbuf * m)1473af69f639Smaxv ipsec_sp_hdrsiz(const struct secpolicy *sp, const struct mbuf *m)
147474029031Sjonathan {
14755e860e54Sozaki-r struct ipsecrequest *isr;
147674029031Sjonathan size_t siz;
147774029031Sjonathan
14786208c225Sozaki-r if (KEYDEBUG_ON(KEYDEBUG_IPSEC_DATA)) {
14796208c225Sozaki-r printf("%s: using SP\n", __func__);
14806208c225Sozaki-r kdebug_secpolicy(sp);
14816208c225Sozaki-r }
148274029031Sjonathan
148374029031Sjonathan switch (sp->policy) {
148474029031Sjonathan case IPSEC_POLICY_DISCARD:
148574029031Sjonathan case IPSEC_POLICY_BYPASS:
148674029031Sjonathan case IPSEC_POLICY_NONE:
148774029031Sjonathan return 0;
148874029031Sjonathan }
148974029031Sjonathan
14902620e166Sozaki-r KASSERTMSG(sp->policy == IPSEC_POLICY_IPSEC,
14912620e166Sozaki-r "invalid policy %u", sp->policy);
149274029031Sjonathan
149374029031Sjonathan siz = 0;
149474029031Sjonathan for (isr = sp->req; isr != NULL; isr = isr->next) {
149574029031Sjonathan size_t clen = 0;
149613270c39Sozaki-r struct secasvar *sav;
149774029031Sjonathan
149874029031Sjonathan switch (isr->saidx.proto) {
149974029031Sjonathan case IPPROTO_ESP:
150013270c39Sozaki-r sav = ipsec_lookup_sa(isr, m);
150113270c39Sozaki-r if (sav != NULL) {
15025e860e54Sozaki-r clen = esp_hdrsiz(sav);
15038be5cabcSozaki-r KEY_SA_UNREF(&sav);
15045e860e54Sozaki-r } else
15055e860e54Sozaki-r clen = esp_hdrsiz(NULL);
150674029031Sjonathan break;
150774029031Sjonathan case IPPROTO_AH:
150813270c39Sozaki-r sav = ipsec_lookup_sa(isr, m);
150913270c39Sozaki-r if (sav != NULL) {
15105e860e54Sozaki-r clen = ah_hdrsiz(sav);
15118be5cabcSozaki-r KEY_SA_UNREF(&sav);
15125e860e54Sozaki-r } else
15135e860e54Sozaki-r clen = ah_hdrsiz(NULL);
151474029031Sjonathan break;
151574029031Sjonathan case IPPROTO_IPCOMP:
151674029031Sjonathan clen = sizeof(struct ipcomp);
151774029031Sjonathan break;
151874029031Sjonathan }
151974029031Sjonathan
152074029031Sjonathan if (isr->saidx.mode == IPSEC_MODE_TUNNEL) {
152174029031Sjonathan switch (isr->saidx.dst.sa.sa_family) {
152274029031Sjonathan case AF_INET:
152374029031Sjonathan clen += sizeof(struct ip);
152474029031Sjonathan break;
152574029031Sjonathan #ifdef INET6
152674029031Sjonathan case AF_INET6:
152774029031Sjonathan clen += sizeof(struct ip6_hdr);
152874029031Sjonathan break;
152974029031Sjonathan #endif
153074029031Sjonathan default:
1531290dc492Sozaki-r IPSECLOG(LOG_ERR, "unknown AF %d in "
1532290dc492Sozaki-r "IPsec tunnel SA\n",
153336443d4aSchristos ((const struct sockaddr *)&isr->saidx.dst)
1534290dc492Sozaki-r ->sa_family);
153574029031Sjonathan break;
153674029031Sjonathan }
153774029031Sjonathan }
153874029031Sjonathan siz += clen;
153974029031Sjonathan }
154074029031Sjonathan
154174029031Sjonathan return siz;
154274029031Sjonathan }
154374029031Sjonathan
154474029031Sjonathan size_t
ipsec_hdrsiz(struct mbuf * m,u_int dir,struct inpcb * inp)15450e390eeeSozaki-r ipsec_hdrsiz(struct mbuf *m, u_int dir, struct inpcb *inp)
154674029031Sjonathan {
154774029031Sjonathan struct secpolicy *sp;
154874029031Sjonathan int error;
154974029031Sjonathan size_t size;
155074029031Sjonathan
15512620e166Sozaki-r KASSERT(m != NULL);
15520e390eeeSozaki-r KASSERTMSG(inp == NULL || inp->inp_socket != NULL,
15538df55a52Smaxv "socket w/o inpcb");
155474029031Sjonathan
15550e390eeeSozaki-r if (inp == NULL)
155674029031Sjonathan sp = ipsec_getpolicybyaddr(m, dir, IP_FORWARDING, &error);
155774029031Sjonathan else
15580e390eeeSozaki-r sp = ipsec_getpolicybysock(m, dir, inp, &error);
155974029031Sjonathan
156074029031Sjonathan if (sp != NULL) {
1561af69f639Smaxv size = ipsec_sp_hdrsiz(sp, m);
156222e66c74Smaxv KEYDEBUG_PRINTF(KEYDEBUG_IPSEC_DATA, "size:%zu.\n", size);
15630c084e85Sozaki-r KEY_SP_UNREF(&sp);
156474029031Sjonathan } else {
15658df55a52Smaxv size = 0;
156674029031Sjonathan }
156722e66c74Smaxv
156874029031Sjonathan return size;
156974029031Sjonathan }
157074029031Sjonathan
157174029031Sjonathan /*
157274029031Sjonathan * Check the variable replay window.
157374029031Sjonathan * ipsec_chkreplay() performs replay check before ICV verification.
157474029031Sjonathan * ipsec_updatereplay() updates replay bitmap. This must be called after
157574029031Sjonathan * ICV verification (it also performs replay check, which is usually done
157674029031Sjonathan * beforehand).
157774029031Sjonathan * 0 (zero) is returned if packet disallowed, 1 if packet permitted.
157874029031Sjonathan *
157974029031Sjonathan * based on RFC 2401.
158074029031Sjonathan */
158174029031Sjonathan int
ipsec_chkreplay(u_int32_t seq,const struct secasvar * sav)1582bbd82ed1Sdrochner ipsec_chkreplay(u_int32_t seq, const struct secasvar *sav)
158374029031Sjonathan {
158474029031Sjonathan const struct secreplay *replay;
158574029031Sjonathan u_int32_t diff;
158674029031Sjonathan int fr;
158774029031Sjonathan u_int32_t wsizeb; /* constant: bits of window size */
158874029031Sjonathan int frlast; /* constant: last frame */
158974029031Sjonathan
15902620e166Sozaki-r KASSERT(sav != NULL);
15912620e166Sozaki-r KASSERT(sav->replay != NULL);
159274029031Sjonathan
159374029031Sjonathan replay = sav->replay;
159474029031Sjonathan
159574029031Sjonathan if (replay->wsize == 0)
159674029031Sjonathan return 1; /* no need to check replay. */
159774029031Sjonathan
159874029031Sjonathan /* constant */
159974029031Sjonathan frlast = replay->wsize - 1;
160074029031Sjonathan wsizeb = replay->wsize << 3;
160174029031Sjonathan
160274029031Sjonathan /* sequence number of 0 is invalid */
160374029031Sjonathan if (seq == 0)
160474029031Sjonathan return 0;
160574029031Sjonathan
160674029031Sjonathan /* first time is always okay */
160774029031Sjonathan if (replay->count == 0)
160874029031Sjonathan return 1;
160974029031Sjonathan
161074029031Sjonathan if (seq > replay->lastseq) {
161174029031Sjonathan /* larger sequences are okay */
161274029031Sjonathan return 1;
161374029031Sjonathan } else {
161474029031Sjonathan /* seq is equal or less than lastseq. */
161574029031Sjonathan diff = replay->lastseq - seq;
161674029031Sjonathan
161774029031Sjonathan /* over range to check, i.e. too old or wrapped */
161874029031Sjonathan if (diff >= wsizeb)
161974029031Sjonathan return 0;
162074029031Sjonathan
162174029031Sjonathan fr = frlast - diff / 8;
162274029031Sjonathan
162374029031Sjonathan /* this packet already seen ? */
162474029031Sjonathan if ((replay->bitmap)[fr] & (1 << (diff % 8)))
162574029031Sjonathan return 0;
162674029031Sjonathan
162774029031Sjonathan /* out of order but good */
162874029031Sjonathan return 1;
162974029031Sjonathan }
163074029031Sjonathan }
163174029031Sjonathan
163274029031Sjonathan /*
163374029031Sjonathan * check replay counter whether to update or not.
163474029031Sjonathan * OUT: 0: OK
163574029031Sjonathan * 1: NG
163674029031Sjonathan */
163774029031Sjonathan int
ipsec_updatereplay(u_int32_t seq,const struct secasvar * sav)1638bbd82ed1Sdrochner ipsec_updatereplay(u_int32_t seq, const struct secasvar *sav)
163974029031Sjonathan {
164074029031Sjonathan struct secreplay *replay;
164174029031Sjonathan u_int32_t diff;
164274029031Sjonathan int fr;
164374029031Sjonathan u_int32_t wsizeb; /* constant: bits of window size */
164474029031Sjonathan int frlast; /* constant: last frame */
164574029031Sjonathan
16462620e166Sozaki-r KASSERT(sav != NULL);
16472620e166Sozaki-r KASSERT(sav->replay != NULL);
164874029031Sjonathan
164974029031Sjonathan replay = sav->replay;
165074029031Sjonathan
165174029031Sjonathan if (replay->wsize == 0)
165274029031Sjonathan goto ok; /* no need to check replay. */
165374029031Sjonathan
165474029031Sjonathan /* constant */
165574029031Sjonathan frlast = replay->wsize - 1;
165674029031Sjonathan wsizeb = replay->wsize << 3;
165774029031Sjonathan
165874029031Sjonathan /* sequence number of 0 is invalid */
165974029031Sjonathan if (seq == 0)
166074029031Sjonathan return 1;
166174029031Sjonathan
166274029031Sjonathan /* first time */
166374029031Sjonathan if (replay->count == 0) {
166474029031Sjonathan replay->lastseq = seq;
1665c363a9cbScegger memset(replay->bitmap, 0, replay->wsize);
166674029031Sjonathan (replay->bitmap)[frlast] = 1;
166774029031Sjonathan goto ok;
166874029031Sjonathan }
166974029031Sjonathan
167074029031Sjonathan if (seq > replay->lastseq) {
167174029031Sjonathan /* seq is larger than lastseq. */
167274029031Sjonathan diff = seq - replay->lastseq;
167374029031Sjonathan
167474029031Sjonathan /* new larger sequence number */
167574029031Sjonathan if (diff < wsizeb) {
167674029031Sjonathan /* In window */
167774029031Sjonathan /* set bit for this packet */
167874029031Sjonathan vshiftl(replay->bitmap, diff, replay->wsize);
167974029031Sjonathan (replay->bitmap)[frlast] |= 1;
168074029031Sjonathan } else {
168174029031Sjonathan /* this packet has a "way larger" */
1682c363a9cbScegger memset(replay->bitmap, 0, replay->wsize);
168374029031Sjonathan (replay->bitmap)[frlast] = 1;
168474029031Sjonathan }
168574029031Sjonathan replay->lastseq = seq;
168674029031Sjonathan
168774029031Sjonathan /* larger is good */
168874029031Sjonathan } else {
168974029031Sjonathan /* seq is equal or less than lastseq. */
169074029031Sjonathan diff = replay->lastseq - seq;
169174029031Sjonathan
169274029031Sjonathan /* over range to check, i.e. too old or wrapped */
169374029031Sjonathan if (diff >= wsizeb)
169474029031Sjonathan return 1;
169574029031Sjonathan
169674029031Sjonathan fr = frlast - diff / 8;
169774029031Sjonathan
169874029031Sjonathan /* this packet already seen ? */
169974029031Sjonathan if ((replay->bitmap)[fr] & (1 << (diff % 8)))
170074029031Sjonathan return 1;
170174029031Sjonathan
170274029031Sjonathan /* mark as seen */
170374029031Sjonathan (replay->bitmap)[fr] |= (1 << (diff % 8));
170474029031Sjonathan
170574029031Sjonathan /* out of order but good */
170674029031Sjonathan }
170774029031Sjonathan
170874029031Sjonathan ok:
170974029031Sjonathan if (replay->count == ~0) {
1710a05a27d7Sozaki-r char buf[IPSEC_LOGSASTRLEN];
171174029031Sjonathan
171274029031Sjonathan /* set overflow flag */
171374029031Sjonathan replay->overflow++;
171474029031Sjonathan
171574029031Sjonathan /* don't increment, no more packets accepted */
171674029031Sjonathan if ((sav->flags & SADB_X_EXT_CYCSEQ) == 0)
171774029031Sjonathan return 1;
171874029031Sjonathan
1719290dc492Sozaki-r IPSECLOG(LOG_WARNING, "replay counter made %d cycle. %s\n",
1720290dc492Sozaki-r replay->overflow, ipsec_logsastr(sav, buf, sizeof(buf)));
172174029031Sjonathan }
172274029031Sjonathan
172374029031Sjonathan replay->count++;
172474029031Sjonathan
172574029031Sjonathan return 0;
172674029031Sjonathan }
172774029031Sjonathan
172874029031Sjonathan /*
1729af69f639Smaxv * shift variable length buffer to left.
173074029031Sjonathan * IN: bitmap: pointer to the buffer
173174029031Sjonathan * nbit: the number of to shift.
173274029031Sjonathan * wsize: buffer size (bytes).
173374029031Sjonathan */
173474029031Sjonathan static void
vshiftl(unsigned char * bitmap,int nbit,int wsize)1735a382db0aSdegroote vshiftl(unsigned char *bitmap, int nbit, int wsize)
173674029031Sjonathan {
173774029031Sjonathan int s, j, i;
173874029031Sjonathan unsigned char over;
173974029031Sjonathan
174074029031Sjonathan for (j = 0; j < nbit; j += 8) {
174174029031Sjonathan s = (nbit - j < 8) ? (nbit - j): 8;
174274029031Sjonathan bitmap[0] <<= s;
174374029031Sjonathan for (i = 1; i < wsize; i++) {
174474029031Sjonathan over = (bitmap[i] >> (8 - s));
174574029031Sjonathan bitmap[i] <<= s;
174674029031Sjonathan bitmap[i-1] |= over;
174774029031Sjonathan }
174874029031Sjonathan }
174974029031Sjonathan
175074029031Sjonathan return;
175174029031Sjonathan }
175274029031Sjonathan
175374029031Sjonathan /* Return a printable string for the address. */
1754f1171a42Schristos const char *
ipsec_address(const union sockaddr_union * sa,char * buf,size_t size)1755dd8c81f5Sryo ipsec_address(const union sockaddr_union *sa, char *buf, size_t size)
175674029031Sjonathan {
175774029031Sjonathan switch (sa->sa.sa_family) {
175874029031Sjonathan case AF_INET:
1759dd8c81f5Sryo in_print(buf, size, &sa->sin.sin_addr);
1760dd8c81f5Sryo return buf;
176174029031Sjonathan #if INET6
176274029031Sjonathan case AF_INET6:
1763dd8c81f5Sryo in6_print(buf, size, &sa->sin6.sin6_addr);
1764dd8c81f5Sryo return buf;
17659a3c5d51Smaxv #endif
176674029031Sjonathan default:
176774029031Sjonathan return "(unknown address family)";
176874029031Sjonathan }
176974029031Sjonathan }
177074029031Sjonathan
177174029031Sjonathan const char *
ipsec_logsastr(const struct secasvar * sav,char * buf,size_t size)1772dd8c81f5Sryo ipsec_logsastr(const struct secasvar *sav, char *buf, size_t size)
177374029031Sjonathan {
1774bbd82ed1Sdrochner const struct secasindex *saidx = &sav->sah->saidx;
1775dd8c81f5Sryo char sbuf[IPSEC_ADDRSTRLEN], dbuf[IPSEC_ADDRSTRLEN];
177674029031Sjonathan
177743d1c237Sozaki-r KASSERTMSG(saidx->src.sa.sa_family == saidx->dst.sa.sa_family,
177843d1c237Sozaki-r "af family mismatch, src %u, dst %u",
177943d1c237Sozaki-r saidx->src.sa.sa_family, saidx->dst.sa.sa_family);
178074029031Sjonathan
1781dd8c81f5Sryo snprintf(buf, size, "SA(SPI=%u src=%s dst=%s)",
1782dd8c81f5Sryo (u_int32_t)ntohl(sav->spi),
1783dd8c81f5Sryo ipsec_address(&saidx->src, sbuf, sizeof(sbuf)),
1784dd8c81f5Sryo ipsec_address(&saidx->dst, dbuf, sizeof(dbuf)));
178574029031Sjonathan
178674029031Sjonathan return buf;
178774029031Sjonathan }
178874029031Sjonathan
1789e2211411Sdegroote #ifdef INET6
1790e2211411Sdegroote struct secpolicy *
ipsec6_check_policy(struct mbuf * m,struct inpcb * inp,int flags,int * needipsecp,int * errorp)17910e390eeeSozaki-r ipsec6_check_policy(struct mbuf *m, struct inpcb *inp, int flags,
17929a3c5d51Smaxv int *needipsecp, int *errorp)
1793e2211411Sdegroote {
1794e2211411Sdegroote struct secpolicy *sp = NULL;
1795e2211411Sdegroote int error = 0;
1796e2211411Sdegroote int needipsec = 0;
1797e2211411Sdegroote
179852489f2bSmaxv if (ipsec_outdone(m)) {
179952489f2bSmaxv goto skippolicycheck;
180052489f2bSmaxv }
18010e390eeeSozaki-r if (inp && ipsec_pcb_skip_ipsec(inp->inp_sp, IPSEC_DIR_OUTBOUND)) {
1802e2211411Sdegroote goto skippolicycheck;
18037d100c18Sjakllsch }
18040e390eeeSozaki-r sp = ipsec_checkpolicy(m, IPSEC_DIR_OUTBOUND, flags, &error, inp);
1805e2211411Sdegroote
1806e2211411Sdegroote /*
1807e2211411Sdegroote * There are four return cases:
1808e2211411Sdegroote * sp != NULL apply IPsec policy
1809e2211411Sdegroote * sp == NULL, error == 0 no IPsec handling needed
1810e2211411Sdegroote * sp == NULL, error == -EINVAL discard packet w/o error
1811e2211411Sdegroote * sp == NULL, error != 0 discard packet, report error
1812e2211411Sdegroote */
1813e2211411Sdegroote if (sp == NULL) {
1814e2211411Sdegroote needipsec = 0;
1815e2211411Sdegroote } else {
1816e2211411Sdegroote needipsec = 1;
1817e2211411Sdegroote }
1818e2211411Sdegroote
181952489f2bSmaxv skippolicycheck:
1820e2211411Sdegroote *errorp = error;
1821e2211411Sdegroote *needipsecp = needipsec;
1822e2211411Sdegroote return sp;
1823e2211411Sdegroote }
1824890dda53Sknakahara
1825890dda53Sknakahara /*
1826890dda53Sknakahara * calculate UDP checksum for UDP encapsulated ESP for IPv6.
1827890dda53Sknakahara *
1828890dda53Sknakahara * RFC2460(Internet Protocol, Version 6 Specification) says:
1829890dda53Sknakahara *
1830890dda53Sknakahara * IPv6 receivers MUST discard UDP packets with a zero checksum.
1831890dda53Sknakahara *
1832ee19b386Sandvar * There is more relaxed specification RFC6935(IPv6 and UDP Checksums for
1833890dda53Sknakahara * Tunneled Packets). The document allows zero checksum. It's too
1834890dda53Sknakahara * late to publish, there are a lot of interoperability problems...
1835890dda53Sknakahara */
1836890dda53Sknakahara void
ipsec6_udp_cksum(struct mbuf * m)1837890dda53Sknakahara ipsec6_udp_cksum(struct mbuf *m)
1838890dda53Sknakahara {
1839890dda53Sknakahara struct ip6_hdr *ip6;
1840890dda53Sknakahara uint16_t plen, uh_sum;
1841890dda53Sknakahara int off;
1842890dda53Sknakahara
1843890dda53Sknakahara /* must called after m_pullup() */
1844890dda53Sknakahara KASSERT(m->m_len >= sizeof(struct ip6_hdr));
1845890dda53Sknakahara
1846890dda53Sknakahara ip6 = mtod(m, struct ip6_hdr *);
1847890dda53Sknakahara KASSERT(ip6->ip6_nxt == IPPROTO_UDP);
1848890dda53Sknakahara
1849890dda53Sknakahara /* ip6->ip6_plen can not be updated before ip6_output() */
1850890dda53Sknakahara plen = m->m_pkthdr.len - sizeof(*ip6);
1851890dda53Sknakahara KASSERT(plen >= sizeof(struct udphdr));
1852890dda53Sknakahara
1853890dda53Sknakahara uh_sum = in6_cksum(m, IPPROTO_UDP, sizeof(*ip6), plen);
1854890dda53Sknakahara if (uh_sum == 0)
1855890dda53Sknakahara uh_sum = 0xffff;
1856890dda53Sknakahara
1857890dda53Sknakahara off = sizeof(*ip6) + offsetof(struct udphdr, uh_sum);
1858890dda53Sknakahara m_copyback(m, off, sizeof(uh_sum), (void *)&uh_sum);
1859890dda53Sknakahara }
1860eefc30d5Sozaki-r #endif /* INET6 */
1861e2211411Sdegroote
186216a6b570Smaxv /*
186316a6b570Smaxv * -----------------------------------------------------------------------------
186416a6b570Smaxv */
1865e2211411Sdegroote
186674029031Sjonathan /* XXX this stuff doesn't belong here... */
186774029031Sjonathan
186874029031Sjonathan static struct xformsw *xforms = NULL;
186974029031Sjonathan
187074029031Sjonathan /*
187174029031Sjonathan * Register a transform; typically at system startup.
187274029031Sjonathan */
187374029031Sjonathan void
xform_register(struct xformsw * xsp)187474029031Sjonathan xform_register(struct xformsw *xsp)
187574029031Sjonathan {
187674029031Sjonathan xsp->xf_next = xforms;
187774029031Sjonathan xforms = xsp;
187874029031Sjonathan }
187974029031Sjonathan
188074029031Sjonathan /*
188174029031Sjonathan * Initialize transform support in an sav.
188274029031Sjonathan */
188374029031Sjonathan int
xform_init(struct secasvar * sav,int xftype)188474029031Sjonathan xform_init(struct secasvar *sav, int xftype)
188574029031Sjonathan {
188674029031Sjonathan struct xformsw *xsp;
188774029031Sjonathan
188874029031Sjonathan if (sav->tdb_xform != NULL) /* previously initialized */
188974029031Sjonathan return 0;
189074029031Sjonathan for (xsp = xforms; xsp; xsp = xsp->xf_next)
189174029031Sjonathan if (xsp->xf_type == xftype)
189274029031Sjonathan return (*xsp->xf_init)(sav, xsp);
189374029031Sjonathan
1894290dc492Sozaki-r IPSECLOG(LOG_DEBUG, "no match for xform type %d\n", xftype);
189574029031Sjonathan return EINVAL;
189674029031Sjonathan }
189774029031Sjonathan
1898caf49ea5Sthorpej /*
1899caf49ea5Sthorpej * XXXJRT This should be done as a protosw init call.
1900caf49ea5Sthorpej */
190174029031Sjonathan void
ipsec_attach(void)190274029031Sjonathan ipsec_attach(void)
190374029031Sjonathan {
1904caf49ea5Sthorpej
190564e64168Sozaki-r ipsec_output_init();
190664e64168Sozaki-r
1907caf49ea5Sthorpej ipsecstat_percpu = percpu_alloc(sizeof(uint64_t) * IPSEC_NSTATS);
1908caf49ea5Sthorpej
190980d40a78Sozaki-r sysctl_net_inet_ipsec_setup(NULL);
191080d40a78Sozaki-r #ifdef INET6
191180d40a78Sozaki-r sysctl_net_inet6_ipsec6_setup(NULL);
191280d40a78Sozaki-r #endif
191380d40a78Sozaki-r
191474029031Sjonathan ah_attach();
191574029031Sjonathan esp_attach();
191674029031Sjonathan ipcomp_attach();
191774029031Sjonathan ipe4_attach();
1918887b782bSjonathan #ifdef TCP_SIGNATURE
1919887b782bSjonathan tcpsignature_attach();
1920887b782bSjonathan #endif
192174029031Sjonathan }
1922