xref: /netbsd-src/sys/net/if_ipsec.c (revision 1cd43426d582b6650b153797f2db305dcd93c554)
1*1cd43426Sandvar /*	$NetBSD: if_ipsec.c,v 1.36 2024/02/10 18:43:53 andvar Exp $  */
24ab3af3eSknakahara 
34ab3af3eSknakahara /*
44ab3af3eSknakahara  * Copyright (c) 2017 Internet Initiative Japan Inc.
54ab3af3eSknakahara  * All rights reserved.
64ab3af3eSknakahara  *
74ab3af3eSknakahara  * Redistribution and use in source and binary forms, with or without
84ab3af3eSknakahara  * modification, are permitted provided that the following conditions
94ab3af3eSknakahara  * are met:
104ab3af3eSknakahara  * 1. Redistributions of source code must retain the above copyright
114ab3af3eSknakahara  *    notice, this list of conditions and the following disclaimer.
124ab3af3eSknakahara  * 2. Redistributions in binary form must reproduce the above copyright
134ab3af3eSknakahara  *    notice, this list of conditions and the following disclaimer in the
144ab3af3eSknakahara  *    documentation and/or other materials provided with the distribution.
154ab3af3eSknakahara  *
164ab3af3eSknakahara  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
174ab3af3eSknakahara  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
184ab3af3eSknakahara  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
194ab3af3eSknakahara  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
204ab3af3eSknakahara  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
214ab3af3eSknakahara  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
224ab3af3eSknakahara  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
234ab3af3eSknakahara  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
244ab3af3eSknakahara  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
254ab3af3eSknakahara  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
264ab3af3eSknakahara  * POSSIBILITY OF SUCH DAMAGE.
274ab3af3eSknakahara  */
284ab3af3eSknakahara 
294ab3af3eSknakahara #include <sys/cdefs.h>
30*1cd43426Sandvar __KERNEL_RCSID(0, "$NetBSD: if_ipsec.c,v 1.36 2024/02/10 18:43:53 andvar Exp $");
314ab3af3eSknakahara 
324ab3af3eSknakahara #ifdef _KERNEL_OPT
334ab3af3eSknakahara #include "opt_inet.h"
344ab3af3eSknakahara #endif
354ab3af3eSknakahara 
364ab3af3eSknakahara #include <sys/param.h>
3747880c13Sriastradh #include <sys/atomic.h>
384ab3af3eSknakahara #include <sys/systm.h>
394ab3af3eSknakahara #include <sys/kernel.h>
404ab3af3eSknakahara #include <sys/mbuf.h>
414ab3af3eSknakahara #include <sys/socket.h>
424ab3af3eSknakahara #include <sys/sockio.h>
434ab3af3eSknakahara #include <sys/errno.h>
444ab3af3eSknakahara #include <sys/ioctl.h>
454ab3af3eSknakahara #include <sys/time.h>
464ab3af3eSknakahara #include <sys/syslog.h>
474ab3af3eSknakahara #include <sys/cpu.h>
484ab3af3eSknakahara #include <sys/kmem.h>
494ab3af3eSknakahara #include <sys/mutex.h>
504ab3af3eSknakahara #include <sys/pserialize.h>
514ab3af3eSknakahara #include <sys/psref.h>
5293a28c82Sknakahara #include <sys/sysctl.h>
534ab3af3eSknakahara 
544ab3af3eSknakahara #include <net/if.h>
554ab3af3eSknakahara #include <net/if_types.h>
564ab3af3eSknakahara #include <net/route.h>
574ab3af3eSknakahara #include <net/bpf.h>
584ab3af3eSknakahara #include <net/pfkeyv2.h>
594ab3af3eSknakahara 
604ab3af3eSknakahara #include <netinet/in.h>
614ab3af3eSknakahara #include <netinet/in_systm.h>
624ab3af3eSknakahara #include <netinet/ip.h>
634ab3af3eSknakahara #ifdef	INET
644ab3af3eSknakahara #include <netinet/in_var.h>
654ab3af3eSknakahara #endif	/* INET */
664ab3af3eSknakahara 
674ab3af3eSknakahara #ifdef INET6
684ab3af3eSknakahara #include <netinet6/in6_var.h>
694ab3af3eSknakahara #include <netinet/ip6.h>
704ab3af3eSknakahara #include <netinet6/ip6_var.h>
714ab3af3eSknakahara #endif /* INET6 */
724ab3af3eSknakahara 
734ab3af3eSknakahara #include <netinet/ip_encap.h>
744ab3af3eSknakahara 
754ab3af3eSknakahara #include <net/if_ipsec.h>
764ab3af3eSknakahara 
774ab3af3eSknakahara #include <net/raw_cb.h>
784ab3af3eSknakahara #include <net/pfkeyv2.h>
794ab3af3eSknakahara 
804ab3af3eSknakahara #include <netipsec/key.h>
81c6bcca39Sknakahara #include <netipsec/keydb.h> /* for union sockaddr_union */
824ab3af3eSknakahara #include <netipsec/ipsec.h>
834ab3af3eSknakahara #include <netipsec/ipsecif.h>
844ab3af3eSknakahara 
854ab3af3eSknakahara static int if_ipsec_clone_create(struct if_clone *, int);
864ab3af3eSknakahara static int if_ipsec_clone_destroy(struct ifnet *);
874ab3af3eSknakahara 
884ab3af3eSknakahara static inline int if_ipsec_out_direct(struct ipsec_variant *, struct mbuf *, int);
894ab3af3eSknakahara static inline void if_ipsec_in_enqueue(struct mbuf *, int, struct ifnet *);
904ab3af3eSknakahara 
914ab3af3eSknakahara static int if_ipsec_encap_attach(struct ipsec_variant *);
924ab3af3eSknakahara static int if_ipsec_encap_detach(struct ipsec_variant *);
934ab3af3eSknakahara static int if_ipsec_set_tunnel(struct ifnet *,
944ab3af3eSknakahara     struct sockaddr *, struct sockaddr *);
954ab3af3eSknakahara static void if_ipsec_delete_tunnel(struct ifnet *);
9670b25bc9Smsaitoh static int if_ipsec_ensure_flags(struct ifnet *, u_short);
974ab3af3eSknakahara static void if_ipsec_attach0(struct ipsec_softc *);
984ab3af3eSknakahara 
994ab3af3eSknakahara static int if_ipsec_update_variant(struct ipsec_softc *,
1004ab3af3eSknakahara     struct ipsec_variant *, struct ipsec_variant *);
1014ab3af3eSknakahara 
1024ab3af3eSknakahara /* sadb_msg */
1034ab3af3eSknakahara static inline void if_ipsec_add_mbuf(struct mbuf *, void *, size_t);
1044ab3af3eSknakahara static inline void if_ipsec_add_pad(struct mbuf *, size_t);
1054ab3af3eSknakahara static inline size_t if_ipsec_set_sadb_addr(struct sadb_address *,
1064ab3af3eSknakahara     struct sockaddr *, int, uint16_t);
1074ab3af3eSknakahara static inline size_t if_ipsec_set_sadb_src(struct sadb_address *,
1084ab3af3eSknakahara     struct sockaddr *, int);
1094ab3af3eSknakahara static inline size_t if_ipsec_set_sadb_dst(struct sadb_address *,
1104ab3af3eSknakahara     struct sockaddr *, int);
1114ab3af3eSknakahara static inline size_t if_ipsec_set_sadb_x_policy(struct sadb_x_policy *,
1126be8939aSknakahara     struct sadb_x_ipsecrequest *, uint16_t, uint8_t, uint32_t, uint8_t,
11331414b24Sknakahara     struct sockaddr *, struct sockaddr *, uint16_t);
1144ab3af3eSknakahara static inline void if_ipsec_set_sadb_msg(struct sadb_msg *, uint16_t, uint8_t);
1154ab3af3eSknakahara static inline void if_ipsec_set_sadb_msg_add(struct sadb_msg *, uint16_t);
1164ab3af3eSknakahara static inline void if_ipsec_set_sadb_msg_del(struct sadb_msg *, uint16_t);
1174ab3af3eSknakahara /* SPD */
1184ab3af3eSknakahara static int if_ipsec_share_sp(struct ipsec_variant *);
1194ab3af3eSknakahara static int if_ipsec_unshare_sp(struct ipsec_variant *);
1204ab3af3eSknakahara static inline struct secpolicy *if_ipsec_add_sp0(struct sockaddr *,
12131414b24Sknakahara     in_port_t, struct sockaddr *, in_port_t, int, int, int, u_int, uint16_t);
1224ab3af3eSknakahara static inline int if_ipsec_del_sp0(struct secpolicy *);
1234ab3af3eSknakahara static int if_ipsec_add_sp(struct ipsec_variant *,
1244ab3af3eSknakahara     struct sockaddr *, in_port_t, struct sockaddr *, in_port_t);
1254ab3af3eSknakahara static void if_ipsec_del_sp(struct ipsec_variant *);
1264ab3af3eSknakahara static int if_ipsec_replace_sp(struct ipsec_softc *, struct ipsec_variant *,
1274ab3af3eSknakahara     struct ipsec_variant *);
1284ab3af3eSknakahara 
1294ab3af3eSknakahara static int if_ipsec_set_addr_port(struct sockaddr *, struct sockaddr *,
1304ab3af3eSknakahara     in_port_t);
1314ab3af3eSknakahara #define IF_IPSEC_GATHER_PSRC_ADDR_PORT(var, target)			\
1324ab3af3eSknakahara 	if_ipsec_set_addr_port(target, (var)->iv_psrc, (var)->iv_sport)
1334ab3af3eSknakahara #define IF_IPSEC_GATHER_PDST_ADDR_PORT(var, target)			\
1344ab3af3eSknakahara 	if_ipsec_set_addr_port(target, (var)->iv_pdst, (var)->iv_dport)
1354ab3af3eSknakahara 
1364ab3af3eSknakahara /*
1374ab3af3eSknakahara  * ipsec global variable definitions
1384ab3af3eSknakahara  */
1394ab3af3eSknakahara 
1404ab3af3eSknakahara /* This list is used in ioctl context only. */
1414ab3af3eSknakahara static struct {
142b801416bSmsaitoh 	LIST_HEAD(ipsec_sclist, ipsec_softc) list;
14331414b24Sknakahara 	bool use_fixed_reqid;
14431414b24Sknakahara #define REQID_BASE_DEFAULT	0x2000
14531414b24Sknakahara #define REQID_LAST_DEFAULT	0x2fff
14631414b24Sknakahara 	u_int16_t reqid_base;
14731414b24Sknakahara 	u_int16_t reqid_last;
1484ab3af3eSknakahara 	kmutex_t lock;
14931414b24Sknakahara } ipsec_softcs __cacheline_aligned = {
15031414b24Sknakahara 	.use_fixed_reqid = false,
15131414b24Sknakahara 	.reqid_base = REQID_BASE_DEFAULT,
15231414b24Sknakahara 	.reqid_last = REQID_LAST_DEFAULT,
15331414b24Sknakahara };
1544ab3af3eSknakahara 
1554ab3af3eSknakahara struct psref_class *iv_psref_class __read_mostly;
1564ab3af3eSknakahara 
1574ab3af3eSknakahara struct if_clone ipsec_cloner =
1584ab3af3eSknakahara     IF_CLONE_INITIALIZER("ipsec", if_ipsec_clone_create, if_ipsec_clone_destroy);
1594ab3af3eSknakahara static int max_ipsec_nesting = MAX_IPSEC_NEST;
1604ab3af3eSknakahara 
16193a28c82Sknakahara static struct sysctllog *if_ipsec_sysctl;
16293a28c82Sknakahara 
1637128a14dSknakahara static pktq_rps_hash_func_t if_ipsec_pktq_rps_hash_p;
1647128a14dSknakahara 
16531414b24Sknakahara enum {
16631414b24Sknakahara 	REQID_INDEX_IPV4IN = 0,
16731414b24Sknakahara 	REQID_INDEX_IPV4OUT,
16831414b24Sknakahara 	REQID_INDEX_IPV6IN,
16931414b24Sknakahara 	REQID_INDEX_IPV6OUT,
17031414b24Sknakahara 	REQID_INDEX_NUM,
17131414b24Sknakahara };
17231414b24Sknakahara 
17393a28c82Sknakahara #ifdef INET6
17493a28c82Sknakahara static int
sysctl_if_ipsec_pmtu_global(SYSCTLFN_ARGS)17593a28c82Sknakahara sysctl_if_ipsec_pmtu_global(SYSCTLFN_ARGS)
17693a28c82Sknakahara {
17793a28c82Sknakahara 	int error, pmtu;
17893a28c82Sknakahara 	struct sysctlnode node = *rnode;
17993a28c82Sknakahara 
18093a28c82Sknakahara 	pmtu = ip6_ipsec_pmtu;
18193a28c82Sknakahara 	node.sysctl_data = &pmtu;
18293a28c82Sknakahara 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
18393a28c82Sknakahara 	if (error || newp == NULL)
18493a28c82Sknakahara 		return error;
18593a28c82Sknakahara 
18693a28c82Sknakahara 	switch (pmtu) {
18793a28c82Sknakahara 	case IPSEC_PMTU_MINMTU:
18893a28c82Sknakahara 	case IPSEC_PMTU_OUTERMTU:
18993a28c82Sknakahara 		ip6_ipsec_pmtu = pmtu;
19093a28c82Sknakahara 		break;
19193a28c82Sknakahara 	default:
19293a28c82Sknakahara 		return EINVAL;
19393a28c82Sknakahara 	}
19493a28c82Sknakahara 
19593a28c82Sknakahara 	return 0;
19693a28c82Sknakahara }
19793a28c82Sknakahara 
19893a28c82Sknakahara static int
sysctl_if_ipsec_pmtu_perif(SYSCTLFN_ARGS)19993a28c82Sknakahara sysctl_if_ipsec_pmtu_perif(SYSCTLFN_ARGS)
20093a28c82Sknakahara {
20193a28c82Sknakahara 	int error, pmtu;
20293a28c82Sknakahara 	struct sysctlnode node = *rnode;
20393a28c82Sknakahara 	struct ipsec_softc *sc = (struct ipsec_softc *)node.sysctl_data;
20493a28c82Sknakahara 
20593a28c82Sknakahara 	pmtu = sc->ipsec_pmtu;
20693a28c82Sknakahara 	node.sysctl_data = &pmtu;
20793a28c82Sknakahara 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
20893a28c82Sknakahara 	if (error || newp == NULL)
20993a28c82Sknakahara 		return error;
21093a28c82Sknakahara 
21193a28c82Sknakahara 	switch (pmtu) {
21293a28c82Sknakahara 	case IPSEC_PMTU_SYSDEFAULT:
21393a28c82Sknakahara 	case IPSEC_PMTU_MINMTU:
21493a28c82Sknakahara 	case IPSEC_PMTU_OUTERMTU:
21593a28c82Sknakahara 		sc->ipsec_pmtu = pmtu;
21693a28c82Sknakahara 		break;
21793a28c82Sknakahara 	default:
21893a28c82Sknakahara 		return EINVAL;
21993a28c82Sknakahara 	}
22093a28c82Sknakahara 
22193a28c82Sknakahara 	return 0;
22293a28c82Sknakahara }
22393a28c82Sknakahara #endif
22493a28c82Sknakahara 
22531414b24Sknakahara static int
sysctl_if_ipsec_use_fixed_reqid(SYSCTLFN_ARGS)22631414b24Sknakahara sysctl_if_ipsec_use_fixed_reqid(SYSCTLFN_ARGS)
22731414b24Sknakahara {
22831414b24Sknakahara 	bool fixed;
22931414b24Sknakahara 	int error;
23031414b24Sknakahara 	struct sysctlnode node = *rnode;
23131414b24Sknakahara 
23231414b24Sknakahara 	mutex_enter(&ipsec_softcs.lock);
23331414b24Sknakahara 	fixed = ipsec_softcs.use_fixed_reqid;
23431414b24Sknakahara 	node.sysctl_data = &fixed;
23531414b24Sknakahara 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
23631414b24Sknakahara 	if (error || newp == NULL) {
23731414b24Sknakahara 		mutex_exit(&ipsec_softcs.lock);
23831414b24Sknakahara 		return error;
23931414b24Sknakahara 	}
24031414b24Sknakahara 
24131414b24Sknakahara 	if (!LIST_EMPTY(&ipsec_softcs.list)) {
24231414b24Sknakahara 		mutex_exit(&ipsec_softcs.lock);
24331414b24Sknakahara 		return EBUSY;
24431414b24Sknakahara 	}
24531414b24Sknakahara 	ipsec_softcs.use_fixed_reqid = fixed;
24631414b24Sknakahara 	mutex_exit(&ipsec_softcs.lock);
24731414b24Sknakahara 
24831414b24Sknakahara 	return 0;
24931414b24Sknakahara }
25031414b24Sknakahara 
25131414b24Sknakahara static int
sysctl_if_ipsec_reqid_base(SYSCTLFN_ARGS)25231414b24Sknakahara sysctl_if_ipsec_reqid_base(SYSCTLFN_ARGS)
25331414b24Sknakahara {
25431414b24Sknakahara 	int base;
25531414b24Sknakahara 	int error;
25631414b24Sknakahara 	struct sysctlnode node = *rnode;
25731414b24Sknakahara 
25831414b24Sknakahara 	mutex_enter(&ipsec_softcs.lock);
25931414b24Sknakahara 	base = ipsec_softcs.reqid_base;
26031414b24Sknakahara 	node.sysctl_data = &base;
26131414b24Sknakahara 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
26231414b24Sknakahara 	if (error || newp == NULL) {
26331414b24Sknakahara 		mutex_exit(&ipsec_softcs.lock);
26431414b24Sknakahara 		return error;
26531414b24Sknakahara 	}
26631414b24Sknakahara 
26731414b24Sknakahara 	if (!LIST_EMPTY(&ipsec_softcs.list)) {
26831414b24Sknakahara 		mutex_exit(&ipsec_softcs.lock);
26931414b24Sknakahara 		return EBUSY;
27031414b24Sknakahara 	}
27131414b24Sknakahara 	ipsec_softcs.reqid_base = base;
27231414b24Sknakahara 	mutex_exit(&ipsec_softcs.lock);
27331414b24Sknakahara 
27431414b24Sknakahara 	return 0;
27531414b24Sknakahara }
27631414b24Sknakahara 
27731414b24Sknakahara static int
sysctl_if_ipsec_reqid_last(SYSCTLFN_ARGS)27831414b24Sknakahara sysctl_if_ipsec_reqid_last(SYSCTLFN_ARGS)
27931414b24Sknakahara {
28031414b24Sknakahara 	int last;
28131414b24Sknakahara 	int error;
28231414b24Sknakahara 	struct sysctlnode node = *rnode;
28331414b24Sknakahara 
28431414b24Sknakahara 	mutex_enter(&ipsec_softcs.lock);
28531414b24Sknakahara 	last = ipsec_softcs.reqid_last;
28631414b24Sknakahara 	node.sysctl_data = &last;
28731414b24Sknakahara 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
28831414b24Sknakahara 	if (error || newp == NULL) {
28931414b24Sknakahara 		mutex_exit(&ipsec_softcs.lock);
29031414b24Sknakahara 		return error;
29131414b24Sknakahara 	}
29231414b24Sknakahara 
29331414b24Sknakahara 	if (!LIST_EMPTY(&ipsec_softcs.list)) {
29431414b24Sknakahara 		mutex_exit(&ipsec_softcs.lock);
29531414b24Sknakahara 		return EBUSY;
29631414b24Sknakahara 	}
29731414b24Sknakahara 	ipsec_softcs.reqid_last = last;
29831414b24Sknakahara 	mutex_exit(&ipsec_softcs.lock);
29931414b24Sknakahara 
30031414b24Sknakahara 	return 0;
30131414b24Sknakahara }
30231414b24Sknakahara 
30393a28c82Sknakahara static void
if_ipsec_sysctl_setup(void)30493a28c82Sknakahara if_ipsec_sysctl_setup(void)
30593a28c82Sknakahara {
3067128a14dSknakahara 	const struct sysctlnode *node = NULL;
3077128a14dSknakahara 
30893a28c82Sknakahara 	if_ipsec_sysctl = NULL;
30993a28c82Sknakahara 
31093a28c82Sknakahara #ifdef INET6
31193a28c82Sknakahara 	/*
31293a28c82Sknakahara 	 * Previously create "net.inet6.ip6" entry to avoid sysctl_createv error.
31393a28c82Sknakahara 	 */
31493a28c82Sknakahara 	sysctl_createv(NULL, 0, NULL, NULL,
31593a28c82Sknakahara 		       CTLFLAG_PERMANENT,
31693a28c82Sknakahara 		       CTLTYPE_NODE, "inet6",
31793a28c82Sknakahara 		       SYSCTL_DESCR("PF_INET6 related settings"),
31893a28c82Sknakahara 		       NULL, 0, NULL, 0,
31993a28c82Sknakahara 		       CTL_NET, PF_INET6, CTL_EOL);
32093a28c82Sknakahara 	sysctl_createv(NULL, 0, NULL, NULL,
32193a28c82Sknakahara 		       CTLFLAG_PERMANENT,
32293a28c82Sknakahara 		       CTLTYPE_NODE, "ip6",
32393a28c82Sknakahara 		       SYSCTL_DESCR("IPv6 related settings"),
32493a28c82Sknakahara 		       NULL, 0, NULL, 0,
32593a28c82Sknakahara 		       CTL_NET, PF_INET6, IPPROTO_IPV6, CTL_EOL);
32693a28c82Sknakahara 
32793a28c82Sknakahara 	sysctl_createv(&if_ipsec_sysctl, 0, NULL, NULL,
32893a28c82Sknakahara 		       CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
32993a28c82Sknakahara 		       CTLTYPE_INT, "ipsecifhlim",
33093a28c82Sknakahara 		       SYSCTL_DESCR("Default hop limit for a ipsec tunnel datagram"),
33193a28c82Sknakahara 		       NULL, 0, &ip6_ipsec_hlim, 0,
33293a28c82Sknakahara 		       CTL_NET, PF_INET6, IPPROTO_IPV6,
33393a28c82Sknakahara 		       IPV6CTL_IPSEC_HLIM, CTL_EOL);
33493a28c82Sknakahara 
33593a28c82Sknakahara 	sysctl_createv(&if_ipsec_sysctl, 0, NULL, NULL,
33693a28c82Sknakahara 		       CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
33793a28c82Sknakahara 		       CTLTYPE_INT, "ipsecifpmtu",
33893a28c82Sknakahara 		       SYSCTL_DESCR("Default Path MTU setting for ipsec tunnels"),
33993a28c82Sknakahara 		       sysctl_if_ipsec_pmtu_global, 0, NULL, 0,
34093a28c82Sknakahara 		       CTL_NET, PF_INET6, IPPROTO_IPV6,
34193a28c82Sknakahara 		       IPV6CTL_IPSEC_PMTU, CTL_EOL);
34293a28c82Sknakahara #endif
3437128a14dSknakahara 
3447128a14dSknakahara 	sysctl_createv(&if_ipsec_sysctl, 0, NULL, &node,
3457128a14dSknakahara 	    CTLFLAG_PERMANENT,
3467128a14dSknakahara 	    CTLTYPE_NODE, "ipsecif",
3477128a14dSknakahara 	    SYSCTL_DESCR("ipsecif global control"),
3487128a14dSknakahara 	    NULL, 0, NULL, 0,
3497128a14dSknakahara 	    CTL_NET, CTL_CREATE, CTL_EOL);
3507128a14dSknakahara 
3517128a14dSknakahara 	sysctl_createv(&if_ipsec_sysctl, 0, &node, NULL,
3527128a14dSknakahara 	    CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
3537128a14dSknakahara 	    CTLTYPE_STRING, "rps_hash",
3547128a14dSknakahara 	    SYSCTL_DESCR("Interface rps hash function control"),
3557128a14dSknakahara 	    sysctl_pktq_rps_hash_handler, 0, (void *)&if_ipsec_pktq_rps_hash_p,
3567128a14dSknakahara 	    PKTQ_RPS_HASH_NAME_LEN,
3577128a14dSknakahara 	    CTL_CREATE, CTL_EOL);
35831414b24Sknakahara 
35931414b24Sknakahara 	sysctl_createv(&if_ipsec_sysctl, 0, &node, NULL,
36031414b24Sknakahara 	    CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
36131414b24Sknakahara 	    CTLTYPE_BOOL, "use_fixed_reqid",
36231414b24Sknakahara 	    SYSCTL_DESCR("use fixed reqid for SP"),
36331414b24Sknakahara 	    sysctl_if_ipsec_use_fixed_reqid, 0, NULL, 0,
36431414b24Sknakahara 	    CTL_CREATE, CTL_EOL);
36531414b24Sknakahara 	sysctl_createv(&if_ipsec_sysctl, 0, &node, NULL,
36631414b24Sknakahara 	    CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
36731414b24Sknakahara 	    CTLTYPE_INT, "reqid_base",
36831414b24Sknakahara 	    SYSCTL_DESCR("base value of fixed reqid"),
36931414b24Sknakahara 	    sysctl_if_ipsec_reqid_base, 0, NULL, 0,
37031414b24Sknakahara 	    CTL_CREATE, CTL_EOL);
37131414b24Sknakahara 	sysctl_createv(&if_ipsec_sysctl, 0, &node, NULL,
37231414b24Sknakahara 	    CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
37331414b24Sknakahara 	    CTLTYPE_INT, "reqid_last",
37431414b24Sknakahara 	    SYSCTL_DESCR("last value of fixed reqid"),
37531414b24Sknakahara 	    sysctl_if_ipsec_reqid_last, 0, NULL, 0,
37631414b24Sknakahara 	    CTL_CREATE, CTL_EOL);
37731414b24Sknakahara 
37893a28c82Sknakahara }
37993a28c82Sknakahara 
38093a28c82Sknakahara static void
if_ipsec_perif_sysctl_setup(struct sysctllog ** clog,struct ipsec_softc * sc)38193a28c82Sknakahara if_ipsec_perif_sysctl_setup(struct sysctllog **clog, struct ipsec_softc *sc)
38293a28c82Sknakahara {
38393a28c82Sknakahara #ifdef INET6
38493a28c82Sknakahara 	const struct sysctlnode *cnode, *rnode;
38593a28c82Sknakahara 	struct ifnet *ifp = &sc->ipsec_if;
38693a28c82Sknakahara 	const char *ifname = ifp->if_xname;
38793a28c82Sknakahara 	int rv;
38893a28c82Sknakahara 
38993a28c82Sknakahara 	/*
39093a28c82Sknakahara 	 * Already created in sysctl_sndq_setup().
39193a28c82Sknakahara 	 */
39293a28c82Sknakahara 	sysctl_createv(clog, 0, NULL, &rnode,
39393a28c82Sknakahara 		       CTLFLAG_PERMANENT,
39493a28c82Sknakahara 		       CTLTYPE_NODE, "interfaces",
39593a28c82Sknakahara 		       SYSCTL_DESCR("Per-interface controls"),
39693a28c82Sknakahara 		       NULL, 0, NULL, 0,
39793a28c82Sknakahara 		       CTL_NET, CTL_CREATE, CTL_EOL);
39893a28c82Sknakahara 	sysctl_createv(clog, 0, &rnode, &rnode,
39993a28c82Sknakahara 		       CTLFLAG_PERMANENT,
40093a28c82Sknakahara 		       CTLTYPE_NODE, ifname,
40193a28c82Sknakahara 		       SYSCTL_DESCR("Interface controls"),
40293a28c82Sknakahara 		       NULL, 0, NULL, 0,
40393a28c82Sknakahara 		       CTL_CREATE, CTL_EOL);
40493a28c82Sknakahara 
40593a28c82Sknakahara 	rv = sysctl_createv(clog, 0, &rnode, &cnode,
40693a28c82Sknakahara 			    CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
40793a28c82Sknakahara 			    CTLTYPE_INT, "pmtu",
40893a28c82Sknakahara 			    SYSCTL_DESCR("Path MTU setting for this ipsec tunnel"),
40993a28c82Sknakahara 			    sysctl_if_ipsec_pmtu_perif, 0, (void *)sc, 0,
41093a28c82Sknakahara 			    CTL_CREATE, CTL_EOL);
41193a28c82Sknakahara 	if (rv != 0)
41293a28c82Sknakahara 		log(LOG_WARNING, "%s: could not attach sysctl node pmtu\n", ifname);
41393a28c82Sknakahara 
41493a28c82Sknakahara 	sc->ipsec_pmtu = IPSEC_PMTU_SYSDEFAULT;
41593a28c82Sknakahara #endif
41693a28c82Sknakahara }
41793a28c82Sknakahara 
4184ab3af3eSknakahara /* ARGSUSED */
4194ab3af3eSknakahara void
ipsecifattach(int count)4204ab3af3eSknakahara ipsecifattach(int count)
4214ab3af3eSknakahara {
4224ab3af3eSknakahara 
4234ab3af3eSknakahara 	mutex_init(&ipsec_softcs.lock, MUTEX_DEFAULT, IPL_NONE);
4244ab3af3eSknakahara 	LIST_INIT(&ipsec_softcs.list);
4254ab3af3eSknakahara 
4264ab3af3eSknakahara 	iv_psref_class = psref_class_create("ipsecvar", IPL_SOFTNET);
4274ab3af3eSknakahara 
4287128a14dSknakahara 	if_ipsec_pktq_rps_hash_p = pktq_rps_hash_default;
42993a28c82Sknakahara 	if_ipsec_sysctl_setup();
43093a28c82Sknakahara 
4314ab3af3eSknakahara 	if_clone_attach(&ipsec_cloner);
4324ab3af3eSknakahara }
4334ab3af3eSknakahara 
4344ab3af3eSknakahara static int
if_ipsec_clone_create(struct if_clone * ifc,int unit)4354ab3af3eSknakahara if_ipsec_clone_create(struct if_clone *ifc, int unit)
4364ab3af3eSknakahara {
4374ab3af3eSknakahara 	struct ipsec_softc *sc;
4384ab3af3eSknakahara 	struct ipsec_variant *var;
43993a28c82Sknakahara 	struct ifnet *ifp;
4404ab3af3eSknakahara 
4414ab3af3eSknakahara 	sc = kmem_zalloc(sizeof(*sc), KM_SLEEP);
4424ab3af3eSknakahara 
4434ab3af3eSknakahara 	if_initname(&sc->ipsec_if, ifc->ifc_name, unit);
4444ab3af3eSknakahara 
4454ab3af3eSknakahara 	if_ipsec_attach0(sc);
4464ab3af3eSknakahara 
44793a28c82Sknakahara 	ifp = &sc->ipsec_if;
44893a28c82Sknakahara 	if_ipsec_perif_sysctl_setup(&ifp->if_sysctl_log, sc);
44993a28c82Sknakahara 
4504ab3af3eSknakahara 	var = kmem_zalloc(sizeof(*var), KM_SLEEP);
4514ab3af3eSknakahara 	var->iv_softc = sc;
4524ab3af3eSknakahara 	psref_target_init(&var->iv_psref, iv_psref_class);
4534ab3af3eSknakahara 
4544ab3af3eSknakahara 	sc->ipsec_var = var;
4554ab3af3eSknakahara 	mutex_init(&sc->ipsec_lock, MUTEX_DEFAULT, IPL_NONE);
456ebac3c72Sknakahara 	sc->ipsec_psz = pserialize_create();
4572da350beSknakahara 	sc->ipsec_ro_percpu = if_tunnel_alloc_ro_percpu();
4584ab3af3eSknakahara 
4594ab3af3eSknakahara 	mutex_enter(&ipsec_softcs.lock);
4604ab3af3eSknakahara 	LIST_INSERT_HEAD(&ipsec_softcs.list, sc, ipsec_list);
4614ab3af3eSknakahara 	mutex_exit(&ipsec_softcs.lock);
4624ab3af3eSknakahara 	return 0;
4634ab3af3eSknakahara }
4644ab3af3eSknakahara 
4654ab3af3eSknakahara static void
if_ipsec_attach0(struct ipsec_softc * sc)4664ab3af3eSknakahara if_ipsec_attach0(struct ipsec_softc *sc)
4674ab3af3eSknakahara {
4684ab3af3eSknakahara 
4694ab3af3eSknakahara 	sc->ipsec_if.if_addrlen = 0;
4704ab3af3eSknakahara 	sc->ipsec_if.if_mtu    = IPSEC_MTU;
4714ab3af3eSknakahara 	sc->ipsec_if.if_flags  = IFF_POINTOPOINT | IFF_MULTICAST;
4724ab3af3eSknakahara 	/* set ipsec(4) specific default flags. */
4734ab3af3eSknakahara 	sc->ipsec_if.if_flags  |= IFF_FWD_IPV6;
474120b194aSroy 	sc->ipsec_if.if_extflags = IFEF_MPSAFE;
4754ab3af3eSknakahara 	sc->ipsec_if.if_ioctl  = if_ipsec_ioctl;
4764ab3af3eSknakahara 	sc->ipsec_if.if_output = if_ipsec_output;
4774ab3af3eSknakahara 	sc->ipsec_if.if_type   = IFT_IPSEC;
4784ab3af3eSknakahara 	sc->ipsec_if.if_dlt    = DLT_NULL;
4794ab3af3eSknakahara 	sc->ipsec_if.if_softc  = sc;
4804ab3af3eSknakahara 	IFQ_SET_READY(&sc->ipsec_if.if_snd);
4814ab3af3eSknakahara 	if_initialize(&sc->ipsec_if);
482120b194aSroy 	sc->ipsec_if.if_link_state = LINK_STATE_DOWN;
4834ab3af3eSknakahara 	if_alloc_sadl(&sc->ipsec_if);
4844ab3af3eSknakahara 	bpf_attach(&sc->ipsec_if, DLT_NULL, sizeof(u_int));
4854ab3af3eSknakahara 	if_register(&sc->ipsec_if);
4864ab3af3eSknakahara }
4874ab3af3eSknakahara 
4884ab3af3eSknakahara static int
if_ipsec_clone_destroy(struct ifnet * ifp)4894ab3af3eSknakahara if_ipsec_clone_destroy(struct ifnet *ifp)
4904ab3af3eSknakahara {
4914ab3af3eSknakahara 	struct ipsec_softc *sc = ifp->if_softc;
4924ab3af3eSknakahara 	struct ipsec_variant *var;
4934ab3af3eSknakahara 	int bound;
4944ab3af3eSknakahara 
4954ab3af3eSknakahara 	mutex_enter(&ipsec_softcs.lock);
4964ab3af3eSknakahara 	LIST_REMOVE(sc, ipsec_list);
4974ab3af3eSknakahara 	mutex_exit(&ipsec_softcs.lock);
4984ab3af3eSknakahara 
4994ab3af3eSknakahara 	bound = curlwp_bind();
5004ab3af3eSknakahara 	if_ipsec_delete_tunnel(&sc->ipsec_if);
5014ab3af3eSknakahara 	curlwp_bindx(bound);
5024ab3af3eSknakahara 
5034ab3af3eSknakahara 	bpf_detach(ifp);
5044ab3af3eSknakahara 	if_detach(ifp);
5054ab3af3eSknakahara 
5062da350beSknakahara 	if_tunnel_free_ro_percpu(sc->ipsec_ro_percpu);
5074ab3af3eSknakahara 
508ebac3c72Sknakahara 	pserialize_destroy(sc->ipsec_psz);
5094ab3af3eSknakahara 	mutex_destroy(&sc->ipsec_lock);
5104ab3af3eSknakahara 
5114ab3af3eSknakahara 	var = sc->ipsec_var;
5124ab3af3eSknakahara 	kmem_free(var, sizeof(*var));
5134ab3af3eSknakahara 	kmem_free(sc, sizeof(*sc));
5144ab3af3eSknakahara 
5154ab3af3eSknakahara 	return 0;
5164ab3af3eSknakahara }
5174ab3af3eSknakahara 
5184ab3af3eSknakahara static inline bool
if_ipsec_nat_t(struct ipsec_softc * sc)5194ab3af3eSknakahara if_ipsec_nat_t(struct ipsec_softc *sc)
5204ab3af3eSknakahara {
5214ab3af3eSknakahara 
5224ab3af3eSknakahara 	return (sc->ipsec_if.if_flags & IFF_NAT_T) != 0;
5234ab3af3eSknakahara }
5244ab3af3eSknakahara 
5254ab3af3eSknakahara static inline bool
if_ipsec_fwd_ipv6(struct ipsec_softc * sc)5264ab3af3eSknakahara if_ipsec_fwd_ipv6(struct ipsec_softc *sc)
5274ab3af3eSknakahara {
5284ab3af3eSknakahara 
5294ab3af3eSknakahara 	return (sc->ipsec_if.if_flags & IFF_FWD_IPV6) != 0;
5304ab3af3eSknakahara }
5314ab3af3eSknakahara 
5324ab3af3eSknakahara int
if_ipsec_encap_func(struct mbuf * m,int off,int proto,void * arg)5334ab3af3eSknakahara if_ipsec_encap_func(struct mbuf *m, int off, int proto, void *arg)
5344ab3af3eSknakahara {
535d83c566fSknakahara 	uint8_t v;
5364ab3af3eSknakahara 	struct ipsec_softc *sc;
5374ab3af3eSknakahara 	struct ipsec_variant *var = NULL;
5384ab3af3eSknakahara 	struct psref psref;
5394ab3af3eSknakahara 	int ret = 0;
5404ab3af3eSknakahara 
5414ab3af3eSknakahara 	sc = arg;
5424ab3af3eSknakahara 	KASSERT(sc != NULL);
5434ab3af3eSknakahara 
5444ab3af3eSknakahara 	if ((sc->ipsec_if.if_flags & IFF_UP) == 0)
5454ab3af3eSknakahara 		goto out;
5464ab3af3eSknakahara 
5474ab3af3eSknakahara 	var = if_ipsec_getref_variant(sc, &psref);
5484ab3af3eSknakahara 	if (if_ipsec_variant_is_unconfigured(var))
5494ab3af3eSknakahara 		goto out;
5504ab3af3eSknakahara 
5514ab3af3eSknakahara 	switch (proto) {
5524ab3af3eSknakahara 	case IPPROTO_IPV4:
5534ab3af3eSknakahara 	case IPPROTO_IPV6:
5544ab3af3eSknakahara 		break;
5554ab3af3eSknakahara 	default:
5564ab3af3eSknakahara 		goto out;
5574ab3af3eSknakahara 	}
5584ab3af3eSknakahara 
559d83c566fSknakahara 	m_copydata(m, 0, sizeof(v), &v);
560d83c566fSknakahara 	v = (v >> 4) & 0xff;  /* Get the IP version number. */
561d83c566fSknakahara 
562d83c566fSknakahara 	switch (v) {
563d83c566fSknakahara #ifdef INET
564d83c566fSknakahara 	case IPVERSION: {
565d83c566fSknakahara 		struct ip ip;
566d83c566fSknakahara 
5674ab3af3eSknakahara 		if (m->m_pkthdr.len < sizeof(ip))
5684ab3af3eSknakahara 			goto out;
5694ab3af3eSknakahara 
5704ab3af3eSknakahara 		m_copydata(m, 0, sizeof(ip), &ip);
5714ab3af3eSknakahara 		if (var->iv_psrc->sa_family != AF_INET ||
5724ab3af3eSknakahara 		    var->iv_pdst->sa_family != AF_INET)
5734ab3af3eSknakahara 			goto out;
5744ab3af3eSknakahara 		ret = ipsecif4_encap_func(m, &ip, var);
5754ab3af3eSknakahara 		break;
576d83c566fSknakahara 	}
577d83c566fSknakahara #endif
578d83c566fSknakahara #ifdef INET6
579d83c566fSknakahara 	case (IPV6_VERSION >> 4): {
580d83c566fSknakahara 		struct ip6_hdr ip6;
581d83c566fSknakahara 
582d83c566fSknakahara 		if (m->m_pkthdr.len < sizeof(ip6))
583d83c566fSknakahara 			goto out;
584d83c566fSknakahara 
585d83c566fSknakahara 		m_copydata(m, 0, sizeof(ip6), &ip6);
586d83c566fSknakahara 		if (var->iv_psrc->sa_family != AF_INET6 ||
587d83c566fSknakahara 		    var->iv_pdst->sa_family != AF_INET6)
588d83c566fSknakahara 			goto out;
589d83c566fSknakahara 		ret = ipsecif6_encap_func(m, &ip6, var);
590d83c566fSknakahara 		break;
591d83c566fSknakahara 	}
5924ab3af3eSknakahara #endif
5934ab3af3eSknakahara 	default:
5944ab3af3eSknakahara 		goto out;
5954ab3af3eSknakahara 	}
5964ab3af3eSknakahara 
5974ab3af3eSknakahara out:
5984ab3af3eSknakahara 	if (var != NULL)
5994ab3af3eSknakahara 		if_ipsec_putref_variant(var, &psref);
6004ab3af3eSknakahara 	return ret;
6014ab3af3eSknakahara }
6024ab3af3eSknakahara 
6034ab3af3eSknakahara /*
6044ab3af3eSknakahara  * ipsec(4) I/F may cause infinite recursion calls when misconfigured.
6054ab3af3eSknakahara  * We'll prevent this by introducing upper limit.
6064ab3af3eSknakahara  */
6074ab3af3eSknakahara static int
if_ipsec_check_nesting(struct ifnet * ifp,struct mbuf * m)6084ab3af3eSknakahara if_ipsec_check_nesting(struct ifnet *ifp, struct mbuf *m)
6094ab3af3eSknakahara {
6104ab3af3eSknakahara 
6114ab3af3eSknakahara 	return if_tunnel_check_nesting(ifp, m, max_ipsec_nesting);
6124ab3af3eSknakahara }
6134ab3af3eSknakahara 
6144ab3af3eSknakahara int
if_ipsec_output(struct ifnet * ifp,struct mbuf * m,const struct sockaddr * dst,const struct rtentry * rt)6154ab3af3eSknakahara if_ipsec_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst,
6164ab3af3eSknakahara     const struct rtentry *rt)
6174ab3af3eSknakahara {
6184ab3af3eSknakahara 	struct ipsec_softc *sc = ifp->if_softc;
6194ab3af3eSknakahara 	struct ipsec_variant *var;
6204ab3af3eSknakahara 	struct psref psref;
6214ab3af3eSknakahara 	int error;
6224ab3af3eSknakahara 	int bound;
6234ab3af3eSknakahara 
6244ab3af3eSknakahara 	IFQ_CLASSIFY(&ifp->if_snd, m, dst->sa_family);
6254ab3af3eSknakahara 
6264ab3af3eSknakahara 	error = if_ipsec_check_nesting(ifp, m);
6274ab3af3eSknakahara 	if (error) {
6284ab3af3eSknakahara 		m_freem(m);
6294ab3af3eSknakahara 		goto noref_end;
6304ab3af3eSknakahara 	}
6314ab3af3eSknakahara 
6324ab3af3eSknakahara 	if ((ifp->if_flags & IFF_UP) == 0) {
6334ab3af3eSknakahara 		m_freem(m);
6344ab3af3eSknakahara 		error = ENETDOWN;
6354ab3af3eSknakahara 		goto noref_end;
6364ab3af3eSknakahara 	}
6374ab3af3eSknakahara 
6384ab3af3eSknakahara 
6394ab3af3eSknakahara 	bound = curlwp_bind();
6404ab3af3eSknakahara 	var = if_ipsec_getref_variant(sc, &psref);
6414ab3af3eSknakahara 	if (if_ipsec_variant_is_unconfigured(var)) {
6424ab3af3eSknakahara 		m_freem(m);
6434ab3af3eSknakahara 		error = ENETDOWN;
6444ab3af3eSknakahara 		goto end;
6454ab3af3eSknakahara 	}
6464ab3af3eSknakahara 
6474ab3af3eSknakahara 	m->m_flags &= ~(M_BCAST|M_MCAST);
6484ab3af3eSknakahara 
6494ab3af3eSknakahara 	/* use DLT_NULL encapsulation here to pass inner af type */
6504ab3af3eSknakahara 	M_PREPEND(m, sizeof(int), M_DONTWAIT);
6514ab3af3eSknakahara 	if (!m) {
6524ab3af3eSknakahara 		error = ENOBUFS;
6534ab3af3eSknakahara 		goto end;
6544ab3af3eSknakahara 	}
6554ab3af3eSknakahara 	*mtod(m, int *) = dst->sa_family;
6564ab3af3eSknakahara 
6574ab3af3eSknakahara #if INET6
6584ab3af3eSknakahara 	/* drop IPv6 packet if IFF_FWD_IPV6 is not set */
6594ab3af3eSknakahara 	if (dst->sa_family == AF_INET6 &&
6604ab3af3eSknakahara 	    !if_ipsec_fwd_ipv6(sc)) {
6614ab3af3eSknakahara 		/*
6624ab3af3eSknakahara 		 * IPv6 packet is not allowed to forward,that is not error.
6634ab3af3eSknakahara 		 */
6644ab3af3eSknakahara 		error = 0;
6654ab3af3eSknakahara 		IF_DROP(&ifp->if_snd);
6664ab3af3eSknakahara 		m_freem(m);
6674ab3af3eSknakahara 		goto end;
6684ab3af3eSknakahara 	}
6694ab3af3eSknakahara #endif
6704ab3af3eSknakahara 
6714ab3af3eSknakahara 	error = if_ipsec_out_direct(var, m, dst->sa_family);
6724ab3af3eSknakahara 
6734ab3af3eSknakahara end:
6744ab3af3eSknakahara 	if_ipsec_putref_variant(var, &psref);
6754ab3af3eSknakahara 	curlwp_bindx(bound);
6764ab3af3eSknakahara noref_end:
6774ab3af3eSknakahara 	if (error)
678d99f8f36Sthorpej 		if_statinc(ifp, if_oerrors);
6794ab3af3eSknakahara 
6804ab3af3eSknakahara 	return error;
6814ab3af3eSknakahara }
6824ab3af3eSknakahara 
6834ab3af3eSknakahara static inline int
if_ipsec_out_direct(struct ipsec_variant * var,struct mbuf * m,int family)6844ab3af3eSknakahara if_ipsec_out_direct(struct ipsec_variant *var, struct mbuf *m, int family)
6854ab3af3eSknakahara {
6864ab3af3eSknakahara 	struct ifnet *ifp = &var->iv_softc->ipsec_if;
6874ab3af3eSknakahara 	int error;
6884ab3af3eSknakahara 	int len;
6894ab3af3eSknakahara 
6904ab3af3eSknakahara 	KASSERT(if_ipsec_heldref_variant(var));
6914ab3af3eSknakahara 	KASSERT(var->iv_output != NULL);
6924ab3af3eSknakahara 
6934ab3af3eSknakahara 	len = m->m_pkthdr.len;
6944ab3af3eSknakahara 
6954ab3af3eSknakahara 	/* input DLT_NULL frame to BPF */
6963cd62456Smsaitoh 	bpf_mtap(ifp, m, BPF_D_OUT);
6974ab3af3eSknakahara 
6984ab3af3eSknakahara 	/* grab and chop off inner af type */
6994ab3af3eSknakahara 	/* XXX need pullup? */
7004ab3af3eSknakahara 	m_adj(m, sizeof(int));
7014ab3af3eSknakahara 
7024ab3af3eSknakahara 	error = var->iv_output(var, family, m);
7034ab3af3eSknakahara 	if (error)
7044ab3af3eSknakahara 		return error;
7054ab3af3eSknakahara 
706d99f8f36Sthorpej 	if_statadd2(ifp, if_opackets, 1, if_obytes, len);
7074ab3af3eSknakahara 
7084ab3af3eSknakahara 	return 0;
7094ab3af3eSknakahara }
7104ab3af3eSknakahara 
7114ab3af3eSknakahara void
if_ipsec_input(struct mbuf * m,int af,struct ifnet * ifp)7124ab3af3eSknakahara if_ipsec_input(struct mbuf *m, int af, struct ifnet *ifp)
7134ab3af3eSknakahara {
7144ab3af3eSknakahara 
7154ab3af3eSknakahara 	KASSERT(ifp != NULL);
7164ab3af3eSknakahara 
7174ab3af3eSknakahara 	m_set_rcvif(m, ifp);
7184ab3af3eSknakahara 
7193cd62456Smsaitoh 	bpf_mtap_af(ifp, af, m, BPF_D_IN);
7204ab3af3eSknakahara 
7214ab3af3eSknakahara 	if_ipsec_in_enqueue(m, af, ifp);
7224ab3af3eSknakahara 
7234ab3af3eSknakahara 	return;
7244ab3af3eSknakahara }
7254ab3af3eSknakahara 
7264ab3af3eSknakahara static inline void
if_ipsec_in_enqueue(struct mbuf * m,int af,struct ifnet * ifp)7274ab3af3eSknakahara if_ipsec_in_enqueue(struct mbuf *m, int af, struct ifnet *ifp)
7284ab3af3eSknakahara {
7294ab3af3eSknakahara 	pktqueue_t *pktq;
7304ab3af3eSknakahara 	int pktlen;
7314ab3af3eSknakahara 
7324ab3af3eSknakahara 	/*
7334ab3af3eSknakahara 	 * Put the packet to the network layer input queue according to the
7344ab3af3eSknakahara 	 * specified address family.
7354ab3af3eSknakahara 	 */
7364ab3af3eSknakahara 	switch (af) {
7374ab3af3eSknakahara #ifdef INET
7384ab3af3eSknakahara 	case AF_INET:
7394ab3af3eSknakahara 		pktq = ip_pktq;
7404ab3af3eSknakahara 		break;
7414ab3af3eSknakahara #endif
7424ab3af3eSknakahara #ifdef INET6
7434ab3af3eSknakahara 	case AF_INET6:
7444ab3af3eSknakahara 		pktq = ip6_pktq;
7454ab3af3eSknakahara 		break;
7464ab3af3eSknakahara #endif
7474ab3af3eSknakahara 	default:
748d99f8f36Sthorpej 		if_statinc(ifp, if_ierrors);
7494ab3af3eSknakahara 		m_freem(m);
7504ab3af3eSknakahara 		return;
7514ab3af3eSknakahara 	}
7524ab3af3eSknakahara 
7537128a14dSknakahara 	const uint32_t h = pktq_rps_hash(&if_ipsec_pktq_rps_hash_p, m);
7544ab3af3eSknakahara 	pktlen = m->m_pkthdr.len;
7554ab3af3eSknakahara 	if (__predict_true(pktq_enqueue(pktq, m, h))) {
756d99f8f36Sthorpej 		if_statadd2(ifp, if_ibytes, pktlen, if_ipackets, 1);
7574ab3af3eSknakahara 	} else {
758d99f8f36Sthorpej 		if_statinc(ifp, if_iqdrops);
7594ab3af3eSknakahara 		m_freem(m);
7604ab3af3eSknakahara 	}
7614ab3af3eSknakahara 
7624ab3af3eSknakahara 	return;
7634ab3af3eSknakahara }
7644ab3af3eSknakahara 
7654ab3af3eSknakahara static inline int
if_ipsec_check_salen(struct sockaddr * addr)7664ab3af3eSknakahara if_ipsec_check_salen(struct sockaddr *addr)
7674ab3af3eSknakahara {
7684ab3af3eSknakahara 
7694ab3af3eSknakahara 	switch (addr->sa_family) {
7704ab3af3eSknakahara #ifdef INET
7714ab3af3eSknakahara 	case AF_INET:
7724ab3af3eSknakahara 		if (addr->sa_len != sizeof(struct sockaddr_in))
7734ab3af3eSknakahara 			return EINVAL;
7744ab3af3eSknakahara 		break;
7754ab3af3eSknakahara #endif /* INET */
7764ab3af3eSknakahara #ifdef INET6
7774ab3af3eSknakahara 	case AF_INET6:
7784ab3af3eSknakahara 		if (addr->sa_len != sizeof(struct sockaddr_in6))
7794ab3af3eSknakahara 			return EINVAL;
7804ab3af3eSknakahara 		break;
7814ab3af3eSknakahara #endif /* INET6 */
7824ab3af3eSknakahara 	default:
7834ab3af3eSknakahara 		return EAFNOSUPPORT;
7844ab3af3eSknakahara 	}
7854ab3af3eSknakahara 
7864ab3af3eSknakahara 	return 0;
7874ab3af3eSknakahara }
7884ab3af3eSknakahara 
7894ab3af3eSknakahara /* XXX how should we handle IPv6 scope on SIOC[GS]IFPHYADDR? */
7904ab3af3eSknakahara int
if_ipsec_ioctl(struct ifnet * ifp,u_long cmd,void * data)7914ab3af3eSknakahara if_ipsec_ioctl(struct ifnet *ifp, u_long cmd, void *data)
7924ab3af3eSknakahara {
7934ab3af3eSknakahara 	struct ipsec_softc *sc  = ifp->if_softc;
7944ab3af3eSknakahara 	struct ipsec_variant *var = NULL;
7954ab3af3eSknakahara 	struct ifreq     *ifr = (struct ifreq*)data;
7964ab3af3eSknakahara 	struct ifaddr    *ifa = (struct ifaddr*)data;
7974ab3af3eSknakahara 	int error = 0, size;
7984ab3af3eSknakahara 	struct sockaddr *dst, *src;
7994ab3af3eSknakahara 	u_long mtu;
80070b25bc9Smsaitoh 	u_short oflags = ifp->if_flags;
8014ab3af3eSknakahara 	int bound;
8024ab3af3eSknakahara 	struct psref psref;
8034ab3af3eSknakahara 
8044ab3af3eSknakahara 	switch (cmd) {
8054ab3af3eSknakahara 	case SIOCINITIFADDR:
8064ab3af3eSknakahara 		ifp->if_flags |= IFF_UP;
8074ab3af3eSknakahara 		ifa->ifa_rtrequest = p2p_rtrequest;
8084ab3af3eSknakahara 		break;
8094ab3af3eSknakahara 
8104ab3af3eSknakahara 	case SIOCSIFDSTADDR:
8114ab3af3eSknakahara 		break;
8124ab3af3eSknakahara 
8134ab3af3eSknakahara 	case SIOCADDMULTI:
8144ab3af3eSknakahara 	case SIOCDELMULTI:
8154ab3af3eSknakahara 		switch (ifr->ifr_addr.sa_family) {
8164ab3af3eSknakahara #ifdef INET
8174ab3af3eSknakahara 		case AF_INET:	/* IP supports Multicast */
8184ab3af3eSknakahara 			break;
8194ab3af3eSknakahara #endif /* INET */
8204ab3af3eSknakahara #ifdef INET6
8214ab3af3eSknakahara 		case AF_INET6:	/* IP6 supports Multicast */
8224ab3af3eSknakahara 			break;
8234ab3af3eSknakahara #endif /* INET6 */
8244ab3af3eSknakahara 		default:  /* Other protocols doesn't support Multicast */
8254ab3af3eSknakahara 			error = EAFNOSUPPORT;
8264ab3af3eSknakahara 			break;
8274ab3af3eSknakahara 		}
8284ab3af3eSknakahara 		break;
8294ab3af3eSknakahara 
8304ab3af3eSknakahara 	case SIOCSIFMTU:
8314ab3af3eSknakahara 		mtu = ifr->ifr_mtu;
8324ab3af3eSknakahara 		if (mtu < IPSEC_MTU_MIN || mtu > IPSEC_MTU_MAX)
8334ab3af3eSknakahara 			return EINVAL;
8344ab3af3eSknakahara 		else if ((error = ifioctl_common(ifp, cmd, data)) == ENETRESET)
8354ab3af3eSknakahara 			error = 0;
8364ab3af3eSknakahara 		break;
8374ab3af3eSknakahara 
8384ab3af3eSknakahara #ifdef INET
8394ab3af3eSknakahara 	case SIOCSIFPHYADDR:
8404ab3af3eSknakahara #endif
8414ab3af3eSknakahara #ifdef INET6
8424ab3af3eSknakahara 	case SIOCSIFPHYADDR_IN6:
8434ab3af3eSknakahara #endif /* INET6 */
8444ab3af3eSknakahara 	case SIOCSLIFPHYADDR:
8454ab3af3eSknakahara 		switch (cmd) {
8464ab3af3eSknakahara #ifdef INET
8474ab3af3eSknakahara 		case SIOCSIFPHYADDR:
8484ab3af3eSknakahara 			src = (struct sockaddr *)
8494ab3af3eSknakahara 				&(((struct in_aliasreq *)data)->ifra_addr);
8504ab3af3eSknakahara 			dst = (struct sockaddr *)
8514ab3af3eSknakahara 				&(((struct in_aliasreq *)data)->ifra_dstaddr);
8524ab3af3eSknakahara 			break;
8534ab3af3eSknakahara #endif /* INET */
8544ab3af3eSknakahara #ifdef INET6
8554ab3af3eSknakahara 		case SIOCSIFPHYADDR_IN6:
8564ab3af3eSknakahara 			src = (struct sockaddr *)
8574ab3af3eSknakahara 				&(((struct in6_aliasreq *)data)->ifra_addr);
8584ab3af3eSknakahara 			dst = (struct sockaddr *)
8594ab3af3eSknakahara 				&(((struct in6_aliasreq *)data)->ifra_dstaddr);
8604ab3af3eSknakahara 			break;
8614ab3af3eSknakahara #endif /* INET6 */
8624ab3af3eSknakahara 		case SIOCSLIFPHYADDR:
8634ab3af3eSknakahara 			src = (struct sockaddr *)
8644ab3af3eSknakahara 				&(((struct if_laddrreq *)data)->addr);
8654ab3af3eSknakahara 			dst = (struct sockaddr *)
8664ab3af3eSknakahara 				&(((struct if_laddrreq *)data)->dstaddr);
8674ab3af3eSknakahara 			break;
8684ab3af3eSknakahara 		default:
8694ab3af3eSknakahara 			return EINVAL;
8704ab3af3eSknakahara 		}
8714ab3af3eSknakahara 
8724ab3af3eSknakahara 		/* sa_family must be equal */
8734ab3af3eSknakahara 		if (src->sa_family != dst->sa_family)
8744ab3af3eSknakahara 			return EINVAL;
8754ab3af3eSknakahara 
8764ab3af3eSknakahara 		error = if_ipsec_check_salen(src);
8774ab3af3eSknakahara 		if (error)
8784ab3af3eSknakahara 			return error;
8794ab3af3eSknakahara 		error = if_ipsec_check_salen(dst);
8804ab3af3eSknakahara 		if (error)
8814ab3af3eSknakahara 			return error;
8824ab3af3eSknakahara 
8834ab3af3eSknakahara 		/* check sa_family looks sane for the cmd */
8844ab3af3eSknakahara 		switch (cmd) {
8854ab3af3eSknakahara #ifdef INET
8864ab3af3eSknakahara 		case SIOCSIFPHYADDR:
8874ab3af3eSknakahara 			if (src->sa_family == AF_INET)
8884ab3af3eSknakahara 				break;
8894ab3af3eSknakahara 			return EAFNOSUPPORT;
8904ab3af3eSknakahara #endif /* INET */
8914ab3af3eSknakahara #ifdef INET6
8924ab3af3eSknakahara 		case SIOCSIFPHYADDR_IN6:
8934ab3af3eSknakahara 			if (src->sa_family == AF_INET6)
8944ab3af3eSknakahara 				break;
8954ab3af3eSknakahara 			return EAFNOSUPPORT;
8964ab3af3eSknakahara #endif /* INET6 */
8974ab3af3eSknakahara 		case SIOCSLIFPHYADDR:
8984ab3af3eSknakahara 			/* checks done in the above */
8994ab3af3eSknakahara 			break;
9004ab3af3eSknakahara 		}
9014ab3af3eSknakahara 		/*
9024ab3af3eSknakahara 		 * calls if_ipsec_getref_variant() for other softcs to check
903*1cd43426Sandvar 		 * address pair duplication
9044ab3af3eSknakahara 		 */
9054ab3af3eSknakahara 		bound = curlwp_bind();
9064ab3af3eSknakahara 		error = if_ipsec_set_tunnel(&sc->ipsec_if, src, dst);
9074ab3af3eSknakahara 		if (error)
9084ab3af3eSknakahara 			goto bad;
909120b194aSroy 		if_link_state_change(&sc->ipsec_if, LINK_STATE_UP);
9107b815c42Sknakahara 		curlwp_bindx(bound);
9114ab3af3eSknakahara 		break;
9124ab3af3eSknakahara 
9134ab3af3eSknakahara 	case SIOCDIFPHYADDR:
9144ab3af3eSknakahara 		bound = curlwp_bind();
9154ab3af3eSknakahara 		if_ipsec_delete_tunnel(&sc->ipsec_if);
916120b194aSroy 		if_link_state_change(&sc->ipsec_if, LINK_STATE_DOWN);
9174ab3af3eSknakahara 		curlwp_bindx(bound);
9184ab3af3eSknakahara 		break;
9194ab3af3eSknakahara 
9204ab3af3eSknakahara 	case SIOCGIFPSRCADDR:
9214ab3af3eSknakahara #ifdef INET6
9224ab3af3eSknakahara 	case SIOCGIFPSRCADDR_IN6:
9234ab3af3eSknakahara #endif /* INET6 */
9244ab3af3eSknakahara 		bound = curlwp_bind();
9254ab3af3eSknakahara 		var = if_ipsec_getref_variant(sc, &psref);
9264ab3af3eSknakahara 		if (var->iv_psrc == NULL) {
9274ab3af3eSknakahara 			error = EADDRNOTAVAIL;
9284ab3af3eSknakahara 			goto bad;
9294ab3af3eSknakahara 		}
9304ab3af3eSknakahara 		src = var->iv_psrc;
9314ab3af3eSknakahara 		switch (cmd) {
9324ab3af3eSknakahara #ifdef INET
9334ab3af3eSknakahara 		case SIOCGIFPSRCADDR:
9344ab3af3eSknakahara 			dst = &ifr->ifr_addr;
9354ab3af3eSknakahara 			size = sizeof(ifr->ifr_addr);
9364ab3af3eSknakahara 			break;
9374ab3af3eSknakahara #endif /* INET */
9384ab3af3eSknakahara #ifdef INET6
9394ab3af3eSknakahara 		case SIOCGIFPSRCADDR_IN6:
9404ab3af3eSknakahara 			dst = (struct sockaddr *)
9414ab3af3eSknakahara 				&(((struct in6_ifreq *)data)->ifr_addr);
9424ab3af3eSknakahara 			size = sizeof(((struct in6_ifreq *)data)->ifr_addr);
9434ab3af3eSknakahara 			break;
9444ab3af3eSknakahara #endif /* INET6 */
9454ab3af3eSknakahara 		default:
9464ab3af3eSknakahara 			error = EADDRNOTAVAIL;
9474ab3af3eSknakahara 			goto bad;
9484ab3af3eSknakahara 		}
9494ab3af3eSknakahara 		if (src->sa_len > size) {
9504ab3af3eSknakahara 			error = EINVAL;
9514ab3af3eSknakahara 			goto bad;
9524ab3af3eSknakahara 		}
9534ab3af3eSknakahara 		error = IF_IPSEC_GATHER_PSRC_ADDR_PORT(var, dst);
9544ab3af3eSknakahara 		if (error)
9554ab3af3eSknakahara 			goto bad;
9564ab3af3eSknakahara 		if_ipsec_putref_variant(var, &psref);
9574ab3af3eSknakahara 		curlwp_bindx(bound);
9584ab3af3eSknakahara 		break;
9594ab3af3eSknakahara 
9604ab3af3eSknakahara 	case SIOCGIFPDSTADDR:
9614ab3af3eSknakahara #ifdef INET6
9624ab3af3eSknakahara 	case SIOCGIFPDSTADDR_IN6:
9634ab3af3eSknakahara #endif /* INET6 */
9644ab3af3eSknakahara 		bound = curlwp_bind();
9654ab3af3eSknakahara 		var = if_ipsec_getref_variant(sc, &psref);
9664ab3af3eSknakahara 		if (var->iv_pdst == NULL) {
9674ab3af3eSknakahara 			error = EADDRNOTAVAIL;
9684ab3af3eSknakahara 			goto bad;
9694ab3af3eSknakahara 		}
9704ab3af3eSknakahara 		src = var->iv_pdst;
9714ab3af3eSknakahara 		switch (cmd) {
9724ab3af3eSknakahara #ifdef INET
9734ab3af3eSknakahara 		case SIOCGIFPDSTADDR:
9744ab3af3eSknakahara 			dst = &ifr->ifr_addr;
9754ab3af3eSknakahara 			size = sizeof(ifr->ifr_addr);
9764ab3af3eSknakahara 			break;
9774ab3af3eSknakahara #endif /* INET */
9784ab3af3eSknakahara #ifdef INET6
9794ab3af3eSknakahara 		case SIOCGIFPDSTADDR_IN6:
9804ab3af3eSknakahara 			dst = (struct sockaddr *)
9814ab3af3eSknakahara 				&(((struct in6_ifreq *)data)->ifr_addr);
9824ab3af3eSknakahara 			size = sizeof(((struct in6_ifreq *)data)->ifr_addr);
9834ab3af3eSknakahara 			break;
9844ab3af3eSknakahara #endif /* INET6 */
9854ab3af3eSknakahara 		default:
9864ab3af3eSknakahara 			error = EADDRNOTAVAIL;
9874ab3af3eSknakahara 			goto bad;
9884ab3af3eSknakahara 		}
9894ab3af3eSknakahara 		if (src->sa_len > size) {
9904ab3af3eSknakahara 			error = EINVAL;
9914ab3af3eSknakahara 			goto bad;
9924ab3af3eSknakahara 		}
9934ab3af3eSknakahara 		error = IF_IPSEC_GATHER_PDST_ADDR_PORT(var, dst);
9944ab3af3eSknakahara 		if (error)
9954ab3af3eSknakahara 			goto bad;
9964ab3af3eSknakahara 		if_ipsec_putref_variant(var, &psref);
9974ab3af3eSknakahara 		curlwp_bindx(bound);
9984ab3af3eSknakahara 		break;
9994ab3af3eSknakahara 
10004ab3af3eSknakahara 	case SIOCGLIFPHYADDR:
10014ab3af3eSknakahara 		bound = curlwp_bind();
10024ab3af3eSknakahara 		var = if_ipsec_getref_variant(sc, &psref);
10034ab3af3eSknakahara 		if (if_ipsec_variant_is_unconfigured(var)) {
10044ab3af3eSknakahara 			error = EADDRNOTAVAIL;
10054ab3af3eSknakahara 			goto bad;
10064ab3af3eSknakahara 		}
10074ab3af3eSknakahara 
10084ab3af3eSknakahara 		/* copy src */
10094ab3af3eSknakahara 		src = var->iv_psrc;
10104ab3af3eSknakahara 		dst = (struct sockaddr *)
10114ab3af3eSknakahara 			&(((struct if_laddrreq *)data)->addr);
10124ab3af3eSknakahara 		size = sizeof(((struct if_laddrreq *)data)->addr);
10134ab3af3eSknakahara 		if (src->sa_len > size) {
10144ab3af3eSknakahara 			error = EINVAL;
10154ab3af3eSknakahara 			goto bad;
10164ab3af3eSknakahara 		}
10174ab3af3eSknakahara 		error = IF_IPSEC_GATHER_PSRC_ADDR_PORT(var, dst);
10184ab3af3eSknakahara 		if (error)
10194ab3af3eSknakahara 			goto bad;
10204ab3af3eSknakahara 
10214ab3af3eSknakahara 		/* copy dst */
10224ab3af3eSknakahara 		src = var->iv_pdst;
10234ab3af3eSknakahara 		dst = (struct sockaddr *)
10244ab3af3eSknakahara 			&(((struct if_laddrreq *)data)->dstaddr);
10254ab3af3eSknakahara 		size = sizeof(((struct if_laddrreq *)data)->dstaddr);
10264ab3af3eSknakahara 		if (src->sa_len > size) {
10274ab3af3eSknakahara 			error = EINVAL;
10284ab3af3eSknakahara 			goto bad;
10294ab3af3eSknakahara 		}
10304ab3af3eSknakahara 		error = IF_IPSEC_GATHER_PDST_ADDR_PORT(var, dst);
10314ab3af3eSknakahara 		if (error)
10324ab3af3eSknakahara 			goto bad;
10334ab3af3eSknakahara 		if_ipsec_putref_variant(var, &psref);
10344ab3af3eSknakahara 		curlwp_bindx(bound);
10354ab3af3eSknakahara 		break;
10364ab3af3eSknakahara 
10374ab3af3eSknakahara 	default:
10384ab3af3eSknakahara 		error = ifioctl_common(ifp, cmd, data);
10394ab3af3eSknakahara 		if (!error) {
10404ab3af3eSknakahara 			bound = curlwp_bind();
10414ab3af3eSknakahara 			error = if_ipsec_ensure_flags(&sc->ipsec_if, oflags);
10424ab3af3eSknakahara 			if (error)
10434ab3af3eSknakahara 				goto bad;
10447b815c42Sknakahara 			curlwp_bindx(bound);
10454ab3af3eSknakahara 		}
10464ab3af3eSknakahara 		break;
10474ab3af3eSknakahara 	}
10484ab3af3eSknakahara 	return error;
10494ab3af3eSknakahara 
10504ab3af3eSknakahara bad:
10514ab3af3eSknakahara 	if (var != NULL)
10524ab3af3eSknakahara 		if_ipsec_putref_variant(var, &psref);
10534ab3af3eSknakahara 	curlwp_bindx(bound);
10544ab3af3eSknakahara 
10554ab3af3eSknakahara 	return error;
10564ab3af3eSknakahara }
10574ab3af3eSknakahara 
10584ab3af3eSknakahara struct encap_funcs {
105911a84560Smrg #ifdef INET
10604ab3af3eSknakahara 	int (*ef_inet)(struct ipsec_variant *);
106111a84560Smrg #endif
106211a84560Smrg #ifdef INET6
10634ab3af3eSknakahara 	int (*ef_inet6)(struct ipsec_variant *);
106411a84560Smrg #endif
10654ab3af3eSknakahara };
10664ab3af3eSknakahara 
10674ab3af3eSknakahara static struct encap_funcs ipsec_encap_attach = {
106811a84560Smrg #ifdef INET
10694ab3af3eSknakahara 	.ef_inet = ipsecif4_attach,
107011a84560Smrg #endif
107111a84560Smrg #ifdef INET6
10724ab3af3eSknakahara 	.ef_inet6 = &ipsecif6_attach,
107311a84560Smrg #endif
10744ab3af3eSknakahara };
10754ab3af3eSknakahara 
10764ab3af3eSknakahara static struct encap_funcs ipsec_encap_detach = {
107711a84560Smrg #ifdef INET
10784ab3af3eSknakahara 	.ef_inet = ipsecif4_detach,
107911a84560Smrg #endif
108011a84560Smrg #ifdef INET6
10814ab3af3eSknakahara 	.ef_inet6 = &ipsecif6_detach,
108211a84560Smrg #endif
10834ab3af3eSknakahara };
10844ab3af3eSknakahara 
10854ab3af3eSknakahara static int
if_ipsec_encap_common(struct ipsec_variant * var,struct encap_funcs * funcs)10864ab3af3eSknakahara if_ipsec_encap_common(struct ipsec_variant *var, struct encap_funcs *funcs)
10874ab3af3eSknakahara {
10884ab3af3eSknakahara 	int error;
10894ab3af3eSknakahara 
10904ab3af3eSknakahara 	KASSERT(var != NULL);
10914ab3af3eSknakahara 	KASSERT(if_ipsec_variant_is_configured(var));
10924ab3af3eSknakahara 
10934ab3af3eSknakahara 	switch (var->iv_psrc->sa_family) {
10944ab3af3eSknakahara #ifdef INET
10954ab3af3eSknakahara 	case AF_INET:
10964ab3af3eSknakahara 		error = (funcs->ef_inet)(var);
10974ab3af3eSknakahara 		break;
10984ab3af3eSknakahara #endif /* INET */
10994ab3af3eSknakahara #ifdef INET6
11004ab3af3eSknakahara 	case AF_INET6:
11014ab3af3eSknakahara 		error = (funcs->ef_inet6)(var);
11024ab3af3eSknakahara 		break;
11034ab3af3eSknakahara #endif /* INET6 */
11044ab3af3eSknakahara 	default:
11054ab3af3eSknakahara 		error = EINVAL;
11064ab3af3eSknakahara 		break;
11074ab3af3eSknakahara 	}
11084ab3af3eSknakahara 
11094ab3af3eSknakahara 	return error;
11104ab3af3eSknakahara }
11114ab3af3eSknakahara 
11124ab3af3eSknakahara static int
if_ipsec_encap_attach(struct ipsec_variant * var)11134ab3af3eSknakahara if_ipsec_encap_attach(struct ipsec_variant *var)
11144ab3af3eSknakahara {
11154ab3af3eSknakahara 
11164ab3af3eSknakahara 	return if_ipsec_encap_common(var, &ipsec_encap_attach);
11174ab3af3eSknakahara }
11184ab3af3eSknakahara 
11194ab3af3eSknakahara static int
if_ipsec_encap_detach(struct ipsec_variant * var)11204ab3af3eSknakahara if_ipsec_encap_detach(struct ipsec_variant *var)
11214ab3af3eSknakahara {
11224ab3af3eSknakahara 
11234ab3af3eSknakahara 	return if_ipsec_encap_common(var, &ipsec_encap_detach);
11244ab3af3eSknakahara }
11254ab3af3eSknakahara 
11264ab3af3eSknakahara /*
11274ab3af3eSknakahara  * Validate and set ipsec(4) I/F configurations.
11284ab3af3eSknakahara  *     (1) validate
11294ab3af3eSknakahara  *         (1-1) Check the argument src and dst address pair will change
11304ab3af3eSknakahara  *               configuration from current src and dst address pair.
11314ab3af3eSknakahara  *         (1-2) Check any ipsec(4) I/F uses duplicated src and dst address pair
11324ab3af3eSknakahara  *               with argument src and dst address pair, except for NAT-T shared
11334ab3af3eSknakahara  *               tunnels.
11344ab3af3eSknakahara  *     (2) set
11354ab3af3eSknakahara  *         (2-1) Create variant for new configuration.
11364ab3af3eSknakahara  *         (2-2) Create temporary "null" variant used to avoid to access
11374ab3af3eSknakahara  *               dangling variant while SPs are deleted and added.
11384ab3af3eSknakahara  *         (2-3) Swap variant include its SPs.
11394ab3af3eSknakahara  *         (2-4) Cleanup last configurations.
11404ab3af3eSknakahara  */
11414ab3af3eSknakahara static int
if_ipsec_set_tunnel(struct ifnet * ifp,struct sockaddr * src,struct sockaddr * dst)11424ab3af3eSknakahara if_ipsec_set_tunnel(struct ifnet *ifp,
11434ab3af3eSknakahara     struct sockaddr *src, struct sockaddr *dst)
11444ab3af3eSknakahara {
11454ab3af3eSknakahara 	struct ipsec_softc *sc = ifp->if_softc;
11464ab3af3eSknakahara 	struct ipsec_softc *sc2;
11474ab3af3eSknakahara 	struct ipsec_variant *ovar, *nvar, *nullvar;
11484ab3af3eSknakahara 	struct sockaddr *osrc, *odst;
11494ab3af3eSknakahara 	struct sockaddr *nsrc, *ndst;
11504ab3af3eSknakahara 	in_port_t nsport = 0, ndport = 0;
11514ab3af3eSknakahara 	int error;
11524ab3af3eSknakahara 
11534ab3af3eSknakahara 	error = encap_lock_enter();
11544ab3af3eSknakahara 	if (error)
11554ab3af3eSknakahara 		return error;
11564ab3af3eSknakahara 
11574ab3af3eSknakahara 	nsrc = sockaddr_dup(src, M_WAITOK);
11584ab3af3eSknakahara 	ndst = sockaddr_dup(dst, M_WAITOK);
11594ab3af3eSknakahara 	nvar = kmem_zalloc(sizeof(*nvar), KM_SLEEP);
11604ab3af3eSknakahara 	nullvar = kmem_zalloc(sizeof(*nullvar), KM_SLEEP);
11614ab3af3eSknakahara 
11624ab3af3eSknakahara 	mutex_enter(&sc->ipsec_lock);
11634ab3af3eSknakahara 
11644ab3af3eSknakahara 	ovar = sc->ipsec_var;
11654ab3af3eSknakahara 
11664ab3af3eSknakahara 	switch(nsrc->sa_family) {
11674ab3af3eSknakahara #ifdef INET
11684ab3af3eSknakahara 	case AF_INET:
1169fceccdc8Sknakahara 		nsport = satosin(src)->sin_port;
11704ab3af3eSknakahara 		/*
11714ab3af3eSknakahara 		 * avoid confuse SP when NAT-T disabled,
11724ab3af3eSknakahara 		 * e.g.
11734ab3af3eSknakahara 		 *     expected: 10.0.1.2[any] 10.0.1.1[any] 4(ipv4)
11744ab3af3eSknakahara 		 *     confuse : 10.0.1.2[600] 10.0.1.1[600] 4(ipv4)
11754ab3af3eSknakahara 		 */
11764ab3af3eSknakahara 		satosin(nsrc)->sin_port = 0;
1177fceccdc8Sknakahara 		ndport = satosin(dst)->sin_port;
11784ab3af3eSknakahara 		satosin(ndst)->sin_port = 0;
11794ab3af3eSknakahara 		break;
11804ab3af3eSknakahara #endif /* INET */
11814ab3af3eSknakahara #ifdef INET6
11824ab3af3eSknakahara 	case AF_INET6:
1183fceccdc8Sknakahara 		nsport = satosin6(src)->sin6_port;
11844ab3af3eSknakahara 		satosin6(nsrc)->sin6_port = 0;
1185fceccdc8Sknakahara 		ndport = satosin6(dst)->sin6_port;
11864ab3af3eSknakahara 		satosin6(ndst)->sin6_port = 0;
11874ab3af3eSknakahara 		break;
11884ab3af3eSknakahara #endif /* INET6 */
11894ab3af3eSknakahara 	default:
11904ab3af3eSknakahara 		log(LOG_DEBUG,
11914ab3af3eSknakahara 		    "%s: Invalid address family: %d.\n",
11924ab3af3eSknakahara 		    __func__, src->sa_family);
11934ab3af3eSknakahara 		error = EINVAL;
11944ab3af3eSknakahara 		goto out;
11954ab3af3eSknakahara 	}
11964ab3af3eSknakahara 
11974ab3af3eSknakahara 	/*
11984ab3af3eSknakahara 	 * (1-1) Check the argument src and dst address pair will change
11994ab3af3eSknakahara 	 *       configuration from current src and dst address pair.
12004ab3af3eSknakahara 	 */
12014ab3af3eSknakahara 	if ((ovar->iv_pdst && sockaddr_cmp(ovar->iv_pdst, dst) == 0) &&
12024ab3af3eSknakahara 	    (ovar->iv_psrc && sockaddr_cmp(ovar->iv_psrc, src) == 0) &&
12034ab3af3eSknakahara 	    (ovar->iv_sport == nsport && ovar->iv_dport == ndport)) {
12044ab3af3eSknakahara 		/* address and port pair not changed. */
12054ab3af3eSknakahara 		error = 0;
12064ab3af3eSknakahara 		goto out;
12074ab3af3eSknakahara 	}
12084ab3af3eSknakahara 
12094ab3af3eSknakahara 	/*
12104ab3af3eSknakahara 	 * (1-2) Check any ipsec(4) I/F uses duplicated src and dst address pair
12114ab3af3eSknakahara 	 *       with argument src and dst address pair, except for NAT-T shared
12124ab3af3eSknakahara 	 *       tunnels.
12134ab3af3eSknakahara 	 */
12144ab3af3eSknakahara 	mutex_enter(&ipsec_softcs.lock);
12154ab3af3eSknakahara 	LIST_FOREACH(sc2, &ipsec_softcs.list, ipsec_list) {
12164ab3af3eSknakahara 		struct ipsec_variant *var2;
12174ab3af3eSknakahara 		struct psref psref;
12184ab3af3eSknakahara 
12194ab3af3eSknakahara 		if (sc2 == sc)
12204ab3af3eSknakahara 			continue;
12214ab3af3eSknakahara 		var2 = if_ipsec_getref_variant(sc2, &psref);
12224ab3af3eSknakahara 		if (if_ipsec_variant_is_unconfigured(var2)) {
12234ab3af3eSknakahara 			if_ipsec_putref_variant(var2, &psref);
12244ab3af3eSknakahara 			continue;
12254ab3af3eSknakahara 		}
12264ab3af3eSknakahara 		if (if_ipsec_nat_t(sc) || if_ipsec_nat_t(sc2)) {
12274ab3af3eSknakahara 			if_ipsec_putref_variant(var2, &psref);
12284ab3af3eSknakahara 			continue; /* NAT-T shared tunnel */
12294ab3af3eSknakahara 		}
12304ab3af3eSknakahara 		if (sockaddr_cmp(var2->iv_pdst, dst) == 0 &&
12314ab3af3eSknakahara 		    sockaddr_cmp(var2->iv_psrc, src) == 0) {
12324ab3af3eSknakahara 			if_ipsec_putref_variant(var2, &psref);
12334ab3af3eSknakahara 			mutex_exit(&ipsec_softcs.lock);
12344ab3af3eSknakahara 			error = EADDRNOTAVAIL;
12354ab3af3eSknakahara 			goto out;
12364ab3af3eSknakahara 		}
12374ab3af3eSknakahara 
12384ab3af3eSknakahara 		if_ipsec_putref_variant(var2, &psref);
12394ab3af3eSknakahara 		/* XXX both end must be valid? (I mean, not 0.0.0.0) */
12404ab3af3eSknakahara 	}
12414ab3af3eSknakahara 	mutex_exit(&ipsec_softcs.lock);
12424ab3af3eSknakahara 
12434ab3af3eSknakahara 
12444ab3af3eSknakahara 	osrc = ovar->iv_psrc;
12454ab3af3eSknakahara 	odst = ovar->iv_pdst;
12464ab3af3eSknakahara 
12474ab3af3eSknakahara 	/*
12484ab3af3eSknakahara 	 * (2-1) Create ipsec_variant for new configuration.
12494ab3af3eSknakahara 	 */
12504ab3af3eSknakahara 	if_ipsec_copy_variant(nvar, ovar);
12514ab3af3eSknakahara 	nvar->iv_psrc = nsrc;
12524ab3af3eSknakahara 	nvar->iv_pdst = ndst;
12534ab3af3eSknakahara 	nvar->iv_sport = nsport;
12544ab3af3eSknakahara 	nvar->iv_dport = ndport;
12554ab3af3eSknakahara 	nvar->iv_encap_cookie4 = NULL;
12564ab3af3eSknakahara 	nvar->iv_encap_cookie6 = NULL;
12574ab3af3eSknakahara 	psref_target_init(&nvar->iv_psref, iv_psref_class);
12584ab3af3eSknakahara 	error = if_ipsec_encap_attach(nvar);
12594ab3af3eSknakahara 	if (error)
12604ab3af3eSknakahara 		goto out;
12614ab3af3eSknakahara 
12624ab3af3eSknakahara 	/*
12634ab3af3eSknakahara 	 * (2-2) Create temporary "null" variant.
12644ab3af3eSknakahara 	 */
12654ab3af3eSknakahara 	if_ipsec_copy_variant(nullvar, ovar);
12664ab3af3eSknakahara 	if_ipsec_clear_config(nullvar);
12674ab3af3eSknakahara 	psref_target_init(&nullvar->iv_psref, iv_psref_class);
12684ab3af3eSknakahara 	/*
12694ab3af3eSknakahara 	 * (2-3) Swap variant include its SPs.
12704ab3af3eSknakahara 	 */
12714ab3af3eSknakahara 	error = if_ipsec_update_variant(sc, nvar, nullvar);
12724ab3af3eSknakahara 	if (error) {
12734ab3af3eSknakahara 		if_ipsec_encap_detach(nvar);
12744ab3af3eSknakahara 		goto out;
12754ab3af3eSknakahara 	}
12764ab3af3eSknakahara 
12774ab3af3eSknakahara 	mutex_exit(&sc->ipsec_lock);
12784ab3af3eSknakahara 
12794ab3af3eSknakahara 	/*
12804ab3af3eSknakahara 	 * (2-4) Cleanup last configurations.
12814ab3af3eSknakahara 	 */
12824ab3af3eSknakahara 	if (if_ipsec_variant_is_configured(ovar))
12834ab3af3eSknakahara 		if_ipsec_encap_detach(ovar);
12844ab3af3eSknakahara 	encap_lock_exit();
12854ab3af3eSknakahara 
12864ab3af3eSknakahara 	if (osrc != NULL)
12874ab3af3eSknakahara 		sockaddr_free(osrc);
12884ab3af3eSknakahara 	if (odst != NULL)
12894ab3af3eSknakahara 		sockaddr_free(odst);
12904ab3af3eSknakahara 	kmem_free(ovar, sizeof(*ovar));
12914ab3af3eSknakahara 	kmem_free(nullvar, sizeof(*nullvar));
12924ab3af3eSknakahara 
12934ab3af3eSknakahara 	return 0;
12944ab3af3eSknakahara 
12954ab3af3eSknakahara out:
12964ab3af3eSknakahara 	mutex_exit(&sc->ipsec_lock);
12974ab3af3eSknakahara 	encap_lock_exit();
12984ab3af3eSknakahara 
12994ab3af3eSknakahara 	sockaddr_free(nsrc);
13004ab3af3eSknakahara 	sockaddr_free(ndst);
13014ab3af3eSknakahara 	kmem_free(nvar, sizeof(*nvar));
13024ab3af3eSknakahara 	kmem_free(nullvar, sizeof(*nullvar));
13034ab3af3eSknakahara 
13044ab3af3eSknakahara 	return error;
13054ab3af3eSknakahara }
13064ab3af3eSknakahara 
13074ab3af3eSknakahara /*
13084ab3af3eSknakahara  * Validate and delete ipsec(4) I/F configurations.
13094ab3af3eSknakahara  *     (1) validate
13104ab3af3eSknakahara  *         (1-1) Check current src and dst address pair are null,
13114ab3af3eSknakahara  *               which means the ipsec(4) I/F is already done deletetunnel.
13124ab3af3eSknakahara  *     (2) delete
13134ab3af3eSknakahara  *         (2-1) Create variant for deleted status.
13144ab3af3eSknakahara  *         (2-2) Create temporary "null" variant used to avoid to access
13154ab3af3eSknakahara  *               dangling variant while SPs are deleted and added.
13164ab3af3eSknakahara  *               NOTE:
13174ab3af3eSknakahara  *               The contents of temporary "null" variant equal to the variant
13184ab3af3eSknakahara  *               of (2-1), however two psref_target_destroy() synchronization
13194ab3af3eSknakahara  *               points are necessary to avoid to access dangling variant
13204ab3af3eSknakahara  *               while SPs are deleted and added. To implement that simply,
13214ab3af3eSknakahara  *               we use the same manner as if_ipsec_set_tunnel(), that is,
13224ab3af3eSknakahara  *               create extra "null" variant and use it temporarily.
13234ab3af3eSknakahara  *         (2-3) Swap variant include its SPs.
13244ab3af3eSknakahara  *         (2-4) Cleanup last configurations.
13254ab3af3eSknakahara  */
13264ab3af3eSknakahara static void
if_ipsec_delete_tunnel(struct ifnet * ifp)13274ab3af3eSknakahara if_ipsec_delete_tunnel(struct ifnet *ifp)
13284ab3af3eSknakahara {
13294ab3af3eSknakahara 	struct ipsec_softc *sc = ifp->if_softc;
13304ab3af3eSknakahara 	struct ipsec_variant *ovar, *nvar, *nullvar;
13314ab3af3eSknakahara 	struct sockaddr *osrc, *odst;
13324ab3af3eSknakahara 	int error;
13334ab3af3eSknakahara 
13344ab3af3eSknakahara 	error = encap_lock_enter();
13354ab3af3eSknakahara 	if (error)
13364ab3af3eSknakahara 		return;
13374ab3af3eSknakahara 
13384ab3af3eSknakahara 	nvar = kmem_zalloc(sizeof(*nvar), KM_SLEEP);
13394ab3af3eSknakahara 	nullvar = kmem_zalloc(sizeof(*nullvar), KM_SLEEP);
13404ab3af3eSknakahara 
13414ab3af3eSknakahara 	mutex_enter(&sc->ipsec_lock);
13424ab3af3eSknakahara 
13434ab3af3eSknakahara 	ovar = sc->ipsec_var;
13444ab3af3eSknakahara 	osrc = ovar->iv_psrc;
13454ab3af3eSknakahara 	odst = ovar->iv_pdst;
13464ab3af3eSknakahara 	/*
13474ab3af3eSknakahara 	 * (1-1) Check current src and dst address pair are null,
13484ab3af3eSknakahara 	 *       which means the ipsec(4) I/F is already done deletetunnel.
13494ab3af3eSknakahara 	 */
13504ab3af3eSknakahara 	if (osrc == NULL || odst == NULL) {
13514ab3af3eSknakahara 		/* address pair not changed. */
13524ab3af3eSknakahara 		mutex_exit(&sc->ipsec_lock);
13534ab3af3eSknakahara 		encap_lock_exit();
13544ab3af3eSknakahara 		kmem_free(nvar, sizeof(*nvar));
1355d97e48c2Sknakahara 		kmem_free(nullvar, sizeof(*nullvar));
13564ab3af3eSknakahara 		return;
13574ab3af3eSknakahara 	}
13584ab3af3eSknakahara 
13594ab3af3eSknakahara 	/*
13604ab3af3eSknakahara 	 * (2-1) Create variant for deleted status.
13614ab3af3eSknakahara 	 */
13624ab3af3eSknakahara 	if_ipsec_copy_variant(nvar, ovar);
13634ab3af3eSknakahara 	if_ipsec_clear_config(nvar);
13644ab3af3eSknakahara 	psref_target_init(&nvar->iv_psref, iv_psref_class);
13654ab3af3eSknakahara 
13664ab3af3eSknakahara 	/*
13674ab3af3eSknakahara 	 * (2-2) Create temporary "null" variant used to avoid to access
13684ab3af3eSknakahara 	 *       dangling variant while SPs are deleted and added.
13694ab3af3eSknakahara 	 */
13704ab3af3eSknakahara 	if_ipsec_copy_variant(nullvar, ovar);
13714ab3af3eSknakahara 	if_ipsec_clear_config(nullvar);
13724ab3af3eSknakahara 	psref_target_init(&nullvar->iv_psref, iv_psref_class);
13734ab3af3eSknakahara 	/*
13744ab3af3eSknakahara 	 * (2-3) Swap variant include its SPs.
13754ab3af3eSknakahara 	 */
13764ab3af3eSknakahara 	/* if_ipsec_update_variant() does not fail when delete SP only. */
13774ab3af3eSknakahara 	(void)if_ipsec_update_variant(sc, nvar, nullvar);
13784ab3af3eSknakahara 
13794ab3af3eSknakahara 	mutex_exit(&sc->ipsec_lock);
13804ab3af3eSknakahara 
13814ab3af3eSknakahara 	/*
13824ab3af3eSknakahara 	 * (2-4) Cleanup last configurations.
13834ab3af3eSknakahara 	 */
13844ab3af3eSknakahara 	if (if_ipsec_variant_is_configured(ovar))
13854ab3af3eSknakahara 		if_ipsec_encap_detach(ovar);
13864ab3af3eSknakahara 	encap_lock_exit();
13874ab3af3eSknakahara 
13884ab3af3eSknakahara 	sockaddr_free(osrc);
13894ab3af3eSknakahara 	sockaddr_free(odst);
13904ab3af3eSknakahara 	kmem_free(ovar, sizeof(*ovar));
13914ab3af3eSknakahara 	kmem_free(nullvar, sizeof(*nullvar));
13924ab3af3eSknakahara }
13934ab3af3eSknakahara 
13944ab3af3eSknakahara /*
13954ab3af3eSknakahara  * Check IFF_NAT_T and IFF_FWD_IPV6 flags, therefore update SPs if needed.
13964ab3af3eSknakahara  *     (1) check
13974ab3af3eSknakahara  *         (1-1) Check flags are changed.
13984ab3af3eSknakahara  *         (1-2) Check current src and dst address pair. If they are null,
13994ab3af3eSknakahara  *               that means the ipsec(4) I/F is deletetunnel'ed, so it is
14004ab3af3eSknakahara  *               not needed to update.
14014ab3af3eSknakahara  *     (2) update
14024ab3af3eSknakahara  *         (2-1) Create variant for new SPs.
14034ab3af3eSknakahara  *         (2-2) Create temporary "null" variant used to avoid to access
14044ab3af3eSknakahara  *               dangling variant while SPs are deleted and added.
14054ab3af3eSknakahara  *               NOTE:
14064ab3af3eSknakahara  *               There is the same problem as if_ipsec_delete_tunnel().
14074ab3af3eSknakahara  *         (2-3) Swap variant include its SPs.
14084ab3af3eSknakahara  *         (2-4) Cleanup unused configurations.
14094ab3af3eSknakahara  *               NOTE: use the same encap_cookies.
14104ab3af3eSknakahara  */
14114ab3af3eSknakahara static int
if_ipsec_ensure_flags(struct ifnet * ifp,u_short oflags)141270b25bc9Smsaitoh if_ipsec_ensure_flags(struct ifnet *ifp, u_short oflags)
14134ab3af3eSknakahara {
14144ab3af3eSknakahara 	struct ipsec_softc *sc = ifp->if_softc;
14154ab3af3eSknakahara 	struct ipsec_variant *ovar, *nvar, *nullvar;
14164ab3af3eSknakahara 	int error;
14174ab3af3eSknakahara 
14184ab3af3eSknakahara 	/*
14194ab3af3eSknakahara 	 * (1) Check flags are changed.
14204ab3af3eSknakahara 	 */
14214ab3af3eSknakahara 	if ((oflags & (IFF_NAT_T|IFF_FWD_IPV6)) ==
14224ab3af3eSknakahara 	    (ifp->if_flags & (IFF_NAT_T|IFF_FWD_IPV6)))
14234ab3af3eSknakahara 		return 0; /* flags not changed. */
14244ab3af3eSknakahara 
14254ab3af3eSknakahara 	error = encap_lock_enter();
14264ab3af3eSknakahara 	if (error)
14274ab3af3eSknakahara 		return error;
14284ab3af3eSknakahara 
14294ab3af3eSknakahara 	nvar = kmem_zalloc(sizeof(*nvar), KM_SLEEP);
14304ab3af3eSknakahara 	nullvar = kmem_zalloc(sizeof(*nullvar), KM_SLEEP);
14314ab3af3eSknakahara 
14324ab3af3eSknakahara 	mutex_enter(&sc->ipsec_lock);
14334ab3af3eSknakahara 
14344ab3af3eSknakahara 	ovar = sc->ipsec_var;
14354ab3af3eSknakahara 	/*
14364ab3af3eSknakahara 	 * (1-2) Check current src and dst address pair.
14374ab3af3eSknakahara 	 */
14384ab3af3eSknakahara 	if (if_ipsec_variant_is_unconfigured(ovar)) {
14394ab3af3eSknakahara 		/* nothing to do */
14404ab3af3eSknakahara 		mutex_exit(&sc->ipsec_lock);
1441cc1df90aSknakahara 		encap_lock_exit();
1442d97e48c2Sknakahara 		kmem_free(nvar, sizeof(*nvar));
1443d97e48c2Sknakahara 		kmem_free(nullvar, sizeof(*nullvar));
14444ab3af3eSknakahara 		return 0;
14454ab3af3eSknakahara 	}
14464ab3af3eSknakahara 
14474ab3af3eSknakahara 	/*
14484ab3af3eSknakahara 	 * (2-1) Create variant for new SPs.
14494ab3af3eSknakahara 	 */
14504ab3af3eSknakahara 	if_ipsec_copy_variant(nvar, ovar);
14514ab3af3eSknakahara 	psref_target_init(&nvar->iv_psref, iv_psref_class);
14524ab3af3eSknakahara 	/*
14534ab3af3eSknakahara 	 * (2-2) Create temporary "null" variant used to avoid to access
14544ab3af3eSknakahara 	 *       dangling variant while SPs are deleted and added.
14554ab3af3eSknakahara 	 */
14564ab3af3eSknakahara 	if_ipsec_copy_variant(nullvar, ovar);
14574ab3af3eSknakahara 	if_ipsec_clear_config(nullvar);
14584ab3af3eSknakahara 	psref_target_init(&nullvar->iv_psref, iv_psref_class);
14594ab3af3eSknakahara 	/*
14604ab3af3eSknakahara 	 * (2-3) Swap variant include its SPs.
14614ab3af3eSknakahara 	 */
14624ab3af3eSknakahara 	error = if_ipsec_update_variant(sc, nvar, nullvar);
14634ab3af3eSknakahara 
14644ab3af3eSknakahara 	mutex_exit(&sc->ipsec_lock);
14654ab3af3eSknakahara 	encap_lock_exit();
14664ab3af3eSknakahara 
14674ab3af3eSknakahara 	/*
14684ab3af3eSknakahara 	 * (2-4) Cleanup unused configurations.
14694ab3af3eSknakahara 	 */
14704ab3af3eSknakahara 	if (!error)
14714ab3af3eSknakahara 		kmem_free(ovar, sizeof(*ovar));
14724ab3af3eSknakahara 	else
14734ab3af3eSknakahara 		kmem_free(nvar, sizeof(*ovar));
14744ab3af3eSknakahara 	kmem_free(nullvar, sizeof(*nullvar));
14754ab3af3eSknakahara 
14764ab3af3eSknakahara 	return error;
14774ab3af3eSknakahara }
14784ab3af3eSknakahara 
14794ab3af3eSknakahara /*
14804ab3af3eSknakahara  * SPD management
14814ab3af3eSknakahara  */
14824ab3af3eSknakahara 
14834ab3af3eSknakahara /*
14844ab3af3eSknakahara  * Share SP set with other NAT-T ipsec(4) I/F(s).
14854ab3af3eSknakahara  *     Return 1, when "var" shares SP set.
14864ab3af3eSknakahara  *     Return 0, when "var" cannot share SP set.
14874ab3af3eSknakahara  *
14884ab3af3eSknakahara  * NOTE:
14894ab3af3eSknakahara  * if_ipsec_share_sp() and if_ipsec_unshare_sp() would require global lock
14904ab3af3eSknakahara  * to exclude other ipsec(4) I/Fs set_tunnel/delete_tunnel. E.g. when ipsec0
14914ab3af3eSknakahara  * and ipsec1 can share SP set, running ipsec0's set_tunnel and ipsec1's
14924ab3af3eSknakahara  * set_tunnel causes race.
14934ab3af3eSknakahara  * Currently, (fortunately) encap_lock works as this global lock.
14944ab3af3eSknakahara  */
14954ab3af3eSknakahara static int
if_ipsec_share_sp(struct ipsec_variant * var)14964ab3af3eSknakahara if_ipsec_share_sp(struct ipsec_variant *var)
14974ab3af3eSknakahara {
14984ab3af3eSknakahara 	struct ipsec_softc *sc = var->iv_softc;
14994ab3af3eSknakahara 	struct ipsec_softc *sc2;
15004ab3af3eSknakahara 	struct ipsec_variant *var2;
15014ab3af3eSknakahara 	struct psref psref;
15024ab3af3eSknakahara 
15034ab3af3eSknakahara 	KASSERT(encap_lock_held());
150442796cd7Sknakahara 	KASSERT(var->iv_psrc != NULL && var->iv_pdst != NULL);
15054ab3af3eSknakahara 
15064ab3af3eSknakahara 	mutex_enter(&ipsec_softcs.lock);
15074ab3af3eSknakahara 	LIST_FOREACH(sc2, &ipsec_softcs.list, ipsec_list) {
15084ab3af3eSknakahara 		if (sc2 == sc)
15094ab3af3eSknakahara 			continue;
15104ab3af3eSknakahara 		var2 = if_ipsec_getref_variant(sc2, &psref);
15114ab3af3eSknakahara 		if (if_ipsec_variant_is_unconfigured(var2)) {
15124ab3af3eSknakahara 			if_ipsec_putref_variant(var2, &psref);
15134ab3af3eSknakahara 			continue;
15144ab3af3eSknakahara 		}
15154ab3af3eSknakahara 		if (sockaddr_cmp(var2->iv_pdst, var->iv_pdst) != 0 ||
15164ab3af3eSknakahara 		    sockaddr_cmp(var2->iv_psrc, var->iv_psrc) != 0) {
15174ab3af3eSknakahara 			if_ipsec_putref_variant(var2, &psref);
15184ab3af3eSknakahara 			continue;
15194ab3af3eSknakahara 		}
15204ab3af3eSknakahara 
15214ab3af3eSknakahara 		break;
15224ab3af3eSknakahara 	}
15234ab3af3eSknakahara 	mutex_exit(&ipsec_softcs.lock);
15244ab3af3eSknakahara 	if (sc2 == NULL)
15254ab3af3eSknakahara 		return 0; /* not shared */
15264ab3af3eSknakahara 
15274ab3af3eSknakahara 	IV_SP_IN(var) = IV_SP_IN(var2);
15284ab3af3eSknakahara 	IV_SP_IN6(var) = IV_SP_IN6(var2);
15294ab3af3eSknakahara 	IV_SP_OUT(var) = IV_SP_OUT(var2);
15304ab3af3eSknakahara 	IV_SP_OUT6(var) = IV_SP_OUT6(var2);
15314ab3af3eSknakahara 
15324ab3af3eSknakahara 	if_ipsec_putref_variant(var2, &psref);
15334ab3af3eSknakahara 	return 1; /* shared */
15344ab3af3eSknakahara }
15354ab3af3eSknakahara 
15364ab3af3eSknakahara /*
15374ab3af3eSknakahara  * Unshare SP set with other NAT-T ipsec(4) I/F(s).
15384ab3af3eSknakahara  *     Return 1, when "var" shared SP set, and then unshare them.
15394ab3af3eSknakahara  *     Return 0, when "var" did not share SP set.
15404ab3af3eSknakahara  *
15414ab3af3eSknakahara  * NOTE:
15424ab3af3eSknakahara  * See if_ipsec_share_sp()'s note.
15434ab3af3eSknakahara  */
15444ab3af3eSknakahara static int
if_ipsec_unshare_sp(struct ipsec_variant * var)15454ab3af3eSknakahara if_ipsec_unshare_sp(struct ipsec_variant *var)
15464ab3af3eSknakahara {
15474ab3af3eSknakahara 	struct ipsec_softc *sc = var->iv_softc;
15484ab3af3eSknakahara 	struct ipsec_softc *sc2;
15494ab3af3eSknakahara 	struct ipsec_variant *var2;
15504ab3af3eSknakahara 	struct psref psref;
15514ab3af3eSknakahara 
15524ab3af3eSknakahara 	KASSERT(encap_lock_held());
15534ab3af3eSknakahara 
15544ab3af3eSknakahara 	if (!var->iv_pdst || !var->iv_psrc)
15554ab3af3eSknakahara 		return 0;
15564ab3af3eSknakahara 
15574ab3af3eSknakahara 	mutex_enter(&ipsec_softcs.lock);
15584ab3af3eSknakahara 	LIST_FOREACH(sc2, &ipsec_softcs.list, ipsec_list) {
15594ab3af3eSknakahara 		if (sc2 == sc)
15604ab3af3eSknakahara 			continue;
15614ab3af3eSknakahara 		var2 = if_ipsec_getref_variant(sc2, &psref);
15624ab3af3eSknakahara 		if (!var2->iv_pdst || !var2->iv_psrc) {
15634ab3af3eSknakahara 			if_ipsec_putref_variant(var2, &psref);
15644ab3af3eSknakahara 			continue;
15654ab3af3eSknakahara 		}
15664ab3af3eSknakahara 		if (sockaddr_cmp(var2->iv_pdst, var->iv_pdst) != 0 ||
15674ab3af3eSknakahara 		    sockaddr_cmp(var2->iv_psrc, var->iv_psrc) != 0) {
15684ab3af3eSknakahara 			if_ipsec_putref_variant(var2, &psref);
15694ab3af3eSknakahara 			continue;
15704ab3af3eSknakahara 		}
15714ab3af3eSknakahara 
15724ab3af3eSknakahara 		break;
15734ab3af3eSknakahara 	}
15744ab3af3eSknakahara 	mutex_exit(&ipsec_softcs.lock);
15754ab3af3eSknakahara 	if (sc2 == NULL)
15764ab3af3eSknakahara 		return 0; /* not shared */
15774ab3af3eSknakahara 
15784ab3af3eSknakahara 	IV_SP_IN(var) = NULL;
15794ab3af3eSknakahara 	IV_SP_IN6(var) = NULL;
15804ab3af3eSknakahara 	IV_SP_OUT(var) = NULL;
15814ab3af3eSknakahara 	IV_SP_OUT6(var) = NULL;
15824ab3af3eSknakahara 	if_ipsec_putref_variant(var2, &psref);
15834ab3af3eSknakahara 	return 1; /* shared */
15844ab3af3eSknakahara }
15854ab3af3eSknakahara 
15864ab3af3eSknakahara static inline void
if_ipsec_add_mbuf_optalign(struct mbuf * m0,void * data,size_t len,bool align)158751a1e9f4Sknakahara if_ipsec_add_mbuf_optalign(struct mbuf *m0, void *data, size_t len, bool align)
15884ab3af3eSknakahara {
15894ab3af3eSknakahara 	struct mbuf *m;
15904ab3af3eSknakahara 
1591f6621122Sknakahara 	MGET(m, M_WAIT, MT_DATA);
1592f6621122Sknakahara 	if (align) {
15934ab3af3eSknakahara 		m->m_len = PFKEY_ALIGN8(len);
1594f6621122Sknakahara 		memset(mtod(m, void *), 0, m->m_len);
1595f6621122Sknakahara 	} else
159651a1e9f4Sknakahara 		m->m_len = len;
15974ab3af3eSknakahara 	m_copyback(m, 0, len, data);
15984ab3af3eSknakahara 	m_cat(m0, m);
15994ab3af3eSknakahara }
16004ab3af3eSknakahara 
16014ab3af3eSknakahara static inline void
if_ipsec_add_mbuf(struct mbuf * m0,void * data,size_t len)160251a1e9f4Sknakahara if_ipsec_add_mbuf(struct mbuf *m0, void *data, size_t len)
160351a1e9f4Sknakahara {
160451a1e9f4Sknakahara 
160551a1e9f4Sknakahara 	if_ipsec_add_mbuf_optalign(m0, data, len, true);
160651a1e9f4Sknakahara }
160751a1e9f4Sknakahara 
160851a1e9f4Sknakahara static inline void
if_ipsec_add_mbuf_addr_port(struct mbuf * m0,struct sockaddr * addr,in_port_t port,bool align)160951a1e9f4Sknakahara if_ipsec_add_mbuf_addr_port(struct mbuf *m0, struct sockaddr *addr, in_port_t port, bool align)
1610130a306cSknakahara {
1611130a306cSknakahara 
1612130a306cSknakahara 	if (port == 0) {
161351a1e9f4Sknakahara 		if_ipsec_add_mbuf_optalign(m0, addr, addr->sa_len, align);
1614130a306cSknakahara 	} else {
1615c6bcca39Sknakahara 		union sockaddr_union addrport_u;
1616c6bcca39Sknakahara 		struct sockaddr *addrport = &addrport_u.sa;
1617130a306cSknakahara 
1618c6bcca39Sknakahara 		if_ipsec_set_addr_port(addrport, addr, port);
1619c6bcca39Sknakahara 		if_ipsec_add_mbuf_optalign(m0, addrport, addrport->sa_len, align);
1620130a306cSknakahara 	}
1621130a306cSknakahara }
1622130a306cSknakahara 
1623130a306cSknakahara static inline void
if_ipsec_add_pad(struct mbuf * m0,size_t len)16244ab3af3eSknakahara if_ipsec_add_pad(struct mbuf *m0, size_t len)
16254ab3af3eSknakahara {
16264ab3af3eSknakahara 	struct mbuf *m;
16274ab3af3eSknakahara 
16284ab3af3eSknakahara 	if (len == 0)
16294ab3af3eSknakahara 		return;
16304ab3af3eSknakahara 
1631f6621122Sknakahara 	MGET(m, M_WAIT, MT_DATA);
16324ab3af3eSknakahara 	m->m_len = len;
1633f6621122Sknakahara 	memset(mtod(m, void *), 0, m->m_len);
16344ab3af3eSknakahara 	m_cat(m0, m);
16354ab3af3eSknakahara }
16364ab3af3eSknakahara 
16374ab3af3eSknakahara static inline size_t
if_ipsec_set_sadb_addr(struct sadb_address * saaddr,struct sockaddr * addr,int proto,uint16_t exttype)16384ab3af3eSknakahara if_ipsec_set_sadb_addr(struct sadb_address *saaddr, struct sockaddr *addr,
16394ab3af3eSknakahara     int proto, uint16_t exttype)
16404ab3af3eSknakahara {
16414ab3af3eSknakahara 	size_t size;
16424ab3af3eSknakahara 
16434ab3af3eSknakahara 	KASSERT(saaddr != NULL);
16444ab3af3eSknakahara 	KASSERT(addr != NULL);
16454ab3af3eSknakahara 
16464ab3af3eSknakahara 	size = sizeof(*saaddr) + PFKEY_ALIGN8(addr->sa_len);
16474ab3af3eSknakahara 	saaddr->sadb_address_len = PFKEY_UNIT64(size);
16484ab3af3eSknakahara 	saaddr->sadb_address_exttype = exttype;
16494ab3af3eSknakahara 	saaddr->sadb_address_proto = proto;
16504ab3af3eSknakahara 	switch (addr->sa_family) {
16514ab3af3eSknakahara #ifdef INET
16524ab3af3eSknakahara 	case AF_INET:
16534ab3af3eSknakahara 		saaddr->sadb_address_prefixlen = sizeof(struct in_addr) << 3;
16544ab3af3eSknakahara 		break;
16554ab3af3eSknakahara #endif /* INET */
16564ab3af3eSknakahara #ifdef INET6
16574ab3af3eSknakahara 	case AF_INET6:
16584ab3af3eSknakahara 		saaddr->sadb_address_prefixlen = sizeof(struct in6_addr) << 3;
16594ab3af3eSknakahara 		break;
16604ab3af3eSknakahara #endif /* INET6 */
16614ab3af3eSknakahara 	default:
16624ab3af3eSknakahara 		log(LOG_DEBUG,
16634ab3af3eSknakahara 		    "%s: Invalid address family: %d.\n",
16644ab3af3eSknakahara 		    __func__, addr->sa_family);
16654ab3af3eSknakahara 		break;
16664ab3af3eSknakahara 	}
16674ab3af3eSknakahara 	saaddr->sadb_address_reserved = 0;
16684ab3af3eSknakahara 
16694ab3af3eSknakahara 	return size;
16704ab3af3eSknakahara }
16714ab3af3eSknakahara 
16724ab3af3eSknakahara static inline size_t
if_ipsec_set_sadb_src(struct sadb_address * sasrc,struct sockaddr * src,int proto)16734ab3af3eSknakahara if_ipsec_set_sadb_src(struct sadb_address *sasrc, struct sockaddr *src,
16744ab3af3eSknakahara     int proto)
16754ab3af3eSknakahara {
16764ab3af3eSknakahara 
16774ab3af3eSknakahara 	return if_ipsec_set_sadb_addr(sasrc, src, proto,
16784ab3af3eSknakahara 	    SADB_EXT_ADDRESS_SRC);
16794ab3af3eSknakahara }
16804ab3af3eSknakahara 
16814ab3af3eSknakahara static inline size_t
if_ipsec_set_sadb_dst(struct sadb_address * sadst,struct sockaddr * dst,int proto)16824ab3af3eSknakahara if_ipsec_set_sadb_dst(struct sadb_address *sadst, struct sockaddr *dst,
16834ab3af3eSknakahara     int proto)
16844ab3af3eSknakahara {
16854ab3af3eSknakahara 
16864ab3af3eSknakahara 	return if_ipsec_set_sadb_addr(sadst, dst, proto,
16874ab3af3eSknakahara 	    SADB_EXT_ADDRESS_DST);
16884ab3af3eSknakahara }
16894ab3af3eSknakahara 
16904ab3af3eSknakahara static inline size_t
if_ipsec_set_sadb_x_policy(struct sadb_x_policy * xpl,struct sadb_x_ipsecrequest * xisr,uint16_t policy,uint8_t dir,uint32_t id,uint8_t level,struct sockaddr * src,struct sockaddr * dst,uint16_t reqid)16914ab3af3eSknakahara if_ipsec_set_sadb_x_policy(struct sadb_x_policy *xpl,
16924ab3af3eSknakahara     struct sadb_x_ipsecrequest *xisr, uint16_t policy, uint8_t dir, uint32_t id,
169331414b24Sknakahara     uint8_t level, struct sockaddr *src, struct sockaddr *dst, uint16_t reqid)
16944ab3af3eSknakahara {
16954ab3af3eSknakahara 	size_t size;
16964ab3af3eSknakahara 
16974ab3af3eSknakahara 	KASSERT(policy != IPSEC_POLICY_IPSEC || xisr != NULL);
16984ab3af3eSknakahara 
16994ab3af3eSknakahara 	size = sizeof(*xpl);
17004ab3af3eSknakahara 	if (policy == IPSEC_POLICY_IPSEC) {
17014ab3af3eSknakahara 		size += PFKEY_ALIGN8(sizeof(*xisr));
170251a1e9f4Sknakahara 		if (src != NULL && dst != NULL)
170351a1e9f4Sknakahara 			size += PFKEY_ALIGN8(src->sa_len + dst->sa_len);
17044ab3af3eSknakahara 	}
17054ab3af3eSknakahara 	xpl->sadb_x_policy_len = PFKEY_UNIT64(size);
17064ab3af3eSknakahara 	xpl->sadb_x_policy_exttype = SADB_X_EXT_POLICY;
17074ab3af3eSknakahara 	xpl->sadb_x_policy_type = policy;
17084ab3af3eSknakahara 	xpl->sadb_x_policy_dir = dir;
170913ced16cSknakahara 	xpl->sadb_x_policy_flags = 0;
17104ab3af3eSknakahara 	xpl->sadb_x_policy_id = id;
17114ab3af3eSknakahara 	xpl->sadb_x_policy_reserved2 = 0;
17124ab3af3eSknakahara 
17134ab3af3eSknakahara 	if (policy == IPSEC_POLICY_IPSEC) {
17144ab3af3eSknakahara 		xisr->sadb_x_ipsecrequest_len = PFKEY_ALIGN8(sizeof(*xisr));
171551a1e9f4Sknakahara 		if (src != NULL && dst != NULL)
171651a1e9f4Sknakahara 			xisr->sadb_x_ipsecrequest_len +=
171751a1e9f4Sknakahara 				PFKEY_ALIGN8(src->sa_len + dst->sa_len);
17184ab3af3eSknakahara 		xisr->sadb_x_ipsecrequest_proto = IPPROTO_ESP;
17194ab3af3eSknakahara 		xisr->sadb_x_ipsecrequest_mode = IPSEC_MODE_TRANSPORT;
17204ab3af3eSknakahara 		xisr->sadb_x_ipsecrequest_level = level;
1721e8a0215cSknakahara 		if (level == IPSEC_LEVEL_UNIQUE)
172231414b24Sknakahara 			xisr->sadb_x_ipsecrequest_reqid = reqid;
1723e8a0215cSknakahara 		else
1724e8a0215cSknakahara 			xisr->sadb_x_ipsecrequest_reqid = 0;
17254ab3af3eSknakahara 	}
17264ab3af3eSknakahara 
17274ab3af3eSknakahara 	return size;
17284ab3af3eSknakahara }
17294ab3af3eSknakahara 
17304ab3af3eSknakahara static inline void
if_ipsec_set_sadb_msg(struct sadb_msg * msg,uint16_t extlen,uint8_t msgtype)17314ab3af3eSknakahara if_ipsec_set_sadb_msg(struct sadb_msg *msg, uint16_t extlen, uint8_t msgtype)
17324ab3af3eSknakahara {
17334ab3af3eSknakahara 
17344ab3af3eSknakahara 	KASSERT(msg != NULL);
17354ab3af3eSknakahara 
17364ab3af3eSknakahara 	msg->sadb_msg_version = PF_KEY_V2;
17374ab3af3eSknakahara 	msg->sadb_msg_type = msgtype;
17384ab3af3eSknakahara 	msg->sadb_msg_errno = 0;
17394ab3af3eSknakahara 	msg->sadb_msg_satype = SADB_SATYPE_UNSPEC;
17404ab3af3eSknakahara 	msg->sadb_msg_len = PFKEY_UNIT64(sizeof(*msg)) + extlen;
17414ab3af3eSknakahara 	msg->sadb_msg_reserved = 0;
17424ab3af3eSknakahara 	msg->sadb_msg_seq = 0; /* XXXX */
17434ab3af3eSknakahara 	msg->sadb_msg_pid = 0; /* XXXX */
17444ab3af3eSknakahara }
17454ab3af3eSknakahara 
17464ab3af3eSknakahara static inline void
if_ipsec_set_sadb_msg_add(struct sadb_msg * msg,uint16_t extlen)17474ab3af3eSknakahara if_ipsec_set_sadb_msg_add(struct sadb_msg *msg, uint16_t extlen)
17484ab3af3eSknakahara {
17494ab3af3eSknakahara 
17504ab3af3eSknakahara 	if_ipsec_set_sadb_msg(msg, extlen, SADB_X_SPDADD);
17514ab3af3eSknakahara }
17524ab3af3eSknakahara 
17534ab3af3eSknakahara static inline void
if_ipsec_set_sadb_msg_del(struct sadb_msg * msg,uint16_t extlen)17544ab3af3eSknakahara if_ipsec_set_sadb_msg_del(struct sadb_msg *msg, uint16_t extlen)
17554ab3af3eSknakahara {
17564ab3af3eSknakahara 
17574ab3af3eSknakahara 	if_ipsec_set_sadb_msg(msg, extlen, SADB_X_SPDDELETE2);
17584ab3af3eSknakahara }
17594ab3af3eSknakahara 
17604ab3af3eSknakahara static int
if_ipsec_set_addr_port(struct sockaddr * addrport,struct sockaddr * addr,in_port_t port)17614ab3af3eSknakahara if_ipsec_set_addr_port(struct sockaddr *addrport, struct sockaddr *addr,
17624ab3af3eSknakahara     in_port_t port)
17634ab3af3eSknakahara {
17644ab3af3eSknakahara 	int error = 0;
17654ab3af3eSknakahara 
17664ab3af3eSknakahara 	sockaddr_copy(addrport, addr->sa_len, addr);
17674ab3af3eSknakahara 
17684ab3af3eSknakahara 	switch (addr->sa_family) {
17694ab3af3eSknakahara #ifdef INET
17704ab3af3eSknakahara 	case AF_INET: {
17714ab3af3eSknakahara 		struct sockaddr_in *sin = satosin(addrport);
1772fceccdc8Sknakahara 		sin->sin_port = port;
17734ab3af3eSknakahara 		break;
17744ab3af3eSknakahara 	}
17754ab3af3eSknakahara #endif /* INET */
17764ab3af3eSknakahara #ifdef INET6
17774ab3af3eSknakahara 	case AF_INET6: {
17784ab3af3eSknakahara 		struct sockaddr_in6 *sin6 = satosin6(addrport);
1779fceccdc8Sknakahara 		sin6->sin6_port = port;
17804ab3af3eSknakahara 		break;
17814ab3af3eSknakahara 	}
17824ab3af3eSknakahara #endif /* INET6 */
17834ab3af3eSknakahara 	default:
17844ab3af3eSknakahara 		log(LOG_DEBUG,
17854ab3af3eSknakahara 		    "%s: Invalid address family: %d.\n",
17864ab3af3eSknakahara 		    __func__, addr->sa_family);
17874ab3af3eSknakahara 		error = EINVAL;
17884ab3af3eSknakahara 	}
17894ab3af3eSknakahara 
17904ab3af3eSknakahara 	return error;
17914ab3af3eSknakahara }
17924ab3af3eSknakahara 
179331414b24Sknakahara static int
if_ipsec_get_reqids(struct ipsec_variant * var,u_int16_t reqids[REQID_INDEX_NUM])179431414b24Sknakahara if_ipsec_get_reqids(struct ipsec_variant *var, u_int16_t reqids[REQID_INDEX_NUM])
179531414b24Sknakahara {
179631414b24Sknakahara 	struct ipsec_softc *sc = var->iv_softc;
179731414b24Sknakahara 	struct ifnet *ifp = &sc->ipsec_if;
179831414b24Sknakahara 
179931414b24Sknakahara 	mutex_enter(&ipsec_softcs.lock);
180031414b24Sknakahara 	if (ipsec_softcs.use_fixed_reqid) {
180187f7d099Sknakahara 		uint32_t unit, reqid_base;
180231414b24Sknakahara 
180387f7d099Sknakahara 		unit = strtoul(ifp->if_xname + sizeof("ipsec") - 1, NULL, 10);
180487f7d099Sknakahara 		reqid_base = ipsec_softcs.reqid_base + unit * 2;
180531414b24Sknakahara 		if (reqid_base + 1 > ipsec_softcs.reqid_last) {
180631414b24Sknakahara 			log(LOG_ERR,
1807814970e5Sknakahara 			    "%s: invalid fixed reqid(%"PRIu32"), "
180831414b24Sknakahara 			    "current range %"PRIu16" <= reqid <= %"PRIu16"\n",
180931414b24Sknakahara 			    ifp->if_xname, reqid_base + 1,
181031414b24Sknakahara 			    ipsec_softcs.reqid_base, ipsec_softcs.reqid_last);
181131414b24Sknakahara 			mutex_exit(&ipsec_softcs.lock);
181231414b24Sknakahara 			return ENOSPC;
181331414b24Sknakahara 		}
181431414b24Sknakahara 
181531414b24Sknakahara 		/*
181631414b24Sknakahara 		 * Use same reqid both inbound and outbound to reduce reqid.
181731414b24Sknakahara 		 */
181831414b24Sknakahara 		reqids[REQID_INDEX_IPV4IN] = reqid_base;
181931414b24Sknakahara 		reqids[REQID_INDEX_IPV4OUT] = reqid_base;
182031414b24Sknakahara 		reqids[REQID_INDEX_IPV6IN] = reqid_base + 1;
182131414b24Sknakahara 		reqids[REQID_INDEX_IPV6OUT] = reqid_base + 1;
182231414b24Sknakahara 	} else {
182331414b24Sknakahara 		for (int i = 0; i < REQID_INDEX_NUM; i++)
182431414b24Sknakahara 			reqids[i] = key_newreqid();
182531414b24Sknakahara 	}
182631414b24Sknakahara 	mutex_exit(&ipsec_softcs.lock);
182731414b24Sknakahara 
182831414b24Sknakahara 	return 0;
182931414b24Sknakahara }
183031414b24Sknakahara 
18314ab3af3eSknakahara static struct secpolicy *
if_ipsec_add_sp0(struct sockaddr * src,in_port_t sport,struct sockaddr * dst,in_port_t dport,int dir,int proto,int level,u_int policy,uint16_t reqid)18324ab3af3eSknakahara if_ipsec_add_sp0(struct sockaddr *src, in_port_t sport,
18334ab3af3eSknakahara     struct sockaddr *dst, in_port_t dport,
183431414b24Sknakahara     int dir, int proto, int level, u_int policy, uint16_t reqid)
18354ab3af3eSknakahara {
18364ab3af3eSknakahara 	struct sadb_msg msg;
18374ab3af3eSknakahara 	struct sadb_address xsrc, xdst;
18384ab3af3eSknakahara 	struct sadb_x_policy xpl;
18394ab3af3eSknakahara 	struct sadb_x_ipsecrequest xisr;
18404ab3af3eSknakahara 	size_t size;
18414ab3af3eSknakahara 	size_t padlen;
18424ab3af3eSknakahara 	uint16_t ext_msg_len = 0;
18434ab3af3eSknakahara 	struct mbuf *m;
18444ab3af3eSknakahara 
18454ab3af3eSknakahara 	memset(&msg, 0, sizeof(msg));
18464ab3af3eSknakahara 	memset(&xsrc, 0, sizeof(xsrc));
18474ab3af3eSknakahara 	memset(&xdst, 0, sizeof(xdst));
18484ab3af3eSknakahara 	memset(&xpl, 0, sizeof(xpl));
18494ab3af3eSknakahara 	memset(&xisr, 0, sizeof(xisr));
18504ab3af3eSknakahara 
1851f6621122Sknakahara 	MGETHDR(m, M_WAIT, MT_DATA);
18524ab3af3eSknakahara 
18534ab3af3eSknakahara 	size = if_ipsec_set_sadb_src(&xsrc, src, proto);
18544ab3af3eSknakahara 	ext_msg_len += PFKEY_UNIT64(size);
18554ab3af3eSknakahara 	size = if_ipsec_set_sadb_dst(&xdst, dst, proto);
18564ab3af3eSknakahara 	ext_msg_len += PFKEY_UNIT64(size);
185731414b24Sknakahara 	size = if_ipsec_set_sadb_x_policy(&xpl, &xisr, policy, dir, 0, level,
185831414b24Sknakahara 	    NULL, NULL, reqid);
18594ab3af3eSknakahara 	ext_msg_len += PFKEY_UNIT64(size);
18604ab3af3eSknakahara 	if_ipsec_set_sadb_msg_add(&msg, ext_msg_len);
18614ab3af3eSknakahara 
18624ab3af3eSknakahara 	/* build PF_KEY message */
18634ab3af3eSknakahara 
18644ab3af3eSknakahara 	m->m_len = sizeof(msg);
18654ab3af3eSknakahara 	m_copyback(m, 0, sizeof(msg), &msg);
18664ab3af3eSknakahara 
18674ab3af3eSknakahara 	if_ipsec_add_mbuf(m, &xsrc, sizeof(xsrc));
1868c6109391Sknakahara 	/*
1869c6109391Sknakahara 	 * secpolicy.spidx.{src, dst} must not be set port number,
1870c6109391Sknakahara 	 * even if it is used for NAT-T.
1871c6109391Sknakahara 	 */
1872c6109391Sknakahara 	if_ipsec_add_mbuf_addr_port(m, src, 0, true);
18734ab3af3eSknakahara 	padlen = PFKEY_UNUNIT64(xsrc.sadb_address_len)
18744ab3af3eSknakahara 		- (sizeof(xsrc) + PFKEY_ALIGN8(src->sa_len));
18754ab3af3eSknakahara 	if_ipsec_add_pad(m, padlen);
18764ab3af3eSknakahara 
18774ab3af3eSknakahara 	if_ipsec_add_mbuf(m, &xdst, sizeof(xdst));
1878c6109391Sknakahara 	/* ditto */
1879c6109391Sknakahara 	if_ipsec_add_mbuf_addr_port(m, dst, 0, true);
18804ab3af3eSknakahara 	padlen = PFKEY_UNUNIT64(xdst.sadb_address_len)
18814ab3af3eSknakahara 		- (sizeof(xdst) + PFKEY_ALIGN8(dst->sa_len));
18824ab3af3eSknakahara 	if_ipsec_add_pad(m, padlen);
18834ab3af3eSknakahara 
18844ab3af3eSknakahara 	if_ipsec_add_mbuf(m, &xpl, sizeof(xpl));
1885223e9d00Sknakahara 	padlen = PFKEY_UNUNIT64(xpl.sadb_x_policy_len) - sizeof(xpl);
18866be8939aSknakahara 	if (policy == IPSEC_POLICY_IPSEC) {
18874ab3af3eSknakahara 		if_ipsec_add_mbuf(m, &xisr, sizeof(xisr));
1888223e9d00Sknakahara 		padlen -= PFKEY_ALIGN8(sizeof(xisr));
18893fb44db7Sknakahara 	}
1890223e9d00Sknakahara 	if_ipsec_add_pad(m, padlen);
18914ab3af3eSknakahara 
18924ab3af3eSknakahara 	/* key_kpi_spdadd() has already done KEY_SP_REF(). */
18934ab3af3eSknakahara 	return key_kpi_spdadd(m);
18944ab3af3eSknakahara }
18954ab3af3eSknakahara 
18964ab3af3eSknakahara static int
if_ipsec_add_sp(struct ipsec_variant * var,struct sockaddr * src,in_port_t sport,struct sockaddr * dst,in_port_t dport)18974ab3af3eSknakahara if_ipsec_add_sp(struct ipsec_variant *var,
18984ab3af3eSknakahara     struct sockaddr *src, in_port_t sport,
18994ab3af3eSknakahara     struct sockaddr *dst, in_port_t dport)
19004ab3af3eSknakahara {
19014ab3af3eSknakahara 	struct ipsec_softc *sc = var->iv_softc;
19024ab3af3eSknakahara 	int level;
190331414b24Sknakahara 	int error;
19044ab3af3eSknakahara 	u_int v6policy;
190531414b24Sknakahara 	u_int16_t reqids[REQID_INDEX_NUM];
19064ab3af3eSknakahara 
19074ab3af3eSknakahara 	/*
19084ab3af3eSknakahara 	 * must delete sp before add it.
19094ab3af3eSknakahara 	 */
19104ab3af3eSknakahara 	KASSERT(IV_SP_IN(var) == NULL);
19114ab3af3eSknakahara 	KASSERT(IV_SP_OUT(var) == NULL);
19124ab3af3eSknakahara 	KASSERT(IV_SP_IN6(var) == NULL);
19134ab3af3eSknakahara 	KASSERT(IV_SP_OUT6(var) == NULL);
19144ab3af3eSknakahara 
19154ab3af3eSknakahara 	/*
19164ab3af3eSknakahara 	 * can be shared?
19174ab3af3eSknakahara 	 */
19184ab3af3eSknakahara 	if (if_ipsec_share_sp(var))
19194ab3af3eSknakahara 		return 0;
19204ab3af3eSknakahara 
19214ab3af3eSknakahara 	if (if_ipsec_nat_t(sc))
19224ab3af3eSknakahara 		level = IPSEC_LEVEL_REQUIRE;
19234ab3af3eSknakahara 	else
19244ab3af3eSknakahara 		level = IPSEC_LEVEL_UNIQUE;
19254ab3af3eSknakahara 
19264ab3af3eSknakahara 	if (if_ipsec_fwd_ipv6(sc))
19274ab3af3eSknakahara 		v6policy = IPSEC_POLICY_IPSEC;
19284ab3af3eSknakahara 	else
19294ab3af3eSknakahara 		v6policy = IPSEC_POLICY_DISCARD;
19304ab3af3eSknakahara 
193131414b24Sknakahara 	error = if_ipsec_get_reqids(var, reqids);
193231414b24Sknakahara 	if (error)
193331414b24Sknakahara 		goto fail;
193431414b24Sknakahara 
19354ab3af3eSknakahara 	IV_SP_IN(var) = if_ipsec_add_sp0(dst, dport, src, sport,
193631414b24Sknakahara 	    IPSEC_DIR_INBOUND, IPPROTO_IPIP, level, IPSEC_POLICY_IPSEC,
193731414b24Sknakahara 	    reqids[REQID_INDEX_IPV4IN]);
193831414b24Sknakahara 	if (IV_SP_IN(var) == NULL) {
193931414b24Sknakahara 		error = EEXIST;
19404ab3af3eSknakahara 		goto fail;
194131414b24Sknakahara 	}
19424ab3af3eSknakahara 	IV_SP_OUT(var) = if_ipsec_add_sp0(src, sport, dst, dport,
194331414b24Sknakahara 	    IPSEC_DIR_OUTBOUND, IPPROTO_IPIP, level, IPSEC_POLICY_IPSEC,
194431414b24Sknakahara 	    reqids[REQID_INDEX_IPV4OUT]);
194531414b24Sknakahara 	if (IV_SP_OUT(var) == NULL) {
194631414b24Sknakahara 		error = EEXIST;
19474ab3af3eSknakahara 		goto fail;
194831414b24Sknakahara 	}
19494ab3af3eSknakahara 	IV_SP_IN6(var) = if_ipsec_add_sp0(dst, dport, src, sport,
195031414b24Sknakahara 	    IPSEC_DIR_INBOUND, IPPROTO_IPV6, level, v6policy,
195131414b24Sknakahara 	    reqids[REQID_INDEX_IPV6IN]);
195231414b24Sknakahara 	if (IV_SP_IN6(var) == NULL) {
195331414b24Sknakahara 		error = EEXIST;
19544ab3af3eSknakahara 		goto fail;
195531414b24Sknakahara 	}
19564ab3af3eSknakahara 	IV_SP_OUT6(var) = if_ipsec_add_sp0(src, sport, dst, dport,
195731414b24Sknakahara 	    IPSEC_DIR_OUTBOUND, IPPROTO_IPV6, level, v6policy,
195831414b24Sknakahara 	    reqids[REQID_INDEX_IPV6OUT]);
195931414b24Sknakahara 	if (IV_SP_OUT6(var) == NULL) {
196031414b24Sknakahara 		error = EEXIST;
19614ab3af3eSknakahara 		goto fail;
196231414b24Sknakahara 	}
19634ab3af3eSknakahara 
19644ab3af3eSknakahara 	return 0;
19654ab3af3eSknakahara 
19664ab3af3eSknakahara fail:
19674ab3af3eSknakahara 	if (IV_SP_IN6(var) != NULL) {
19684ab3af3eSknakahara 		if_ipsec_del_sp0(IV_SP_IN6(var));
19694ab3af3eSknakahara 		IV_SP_IN6(var) = NULL;
19704ab3af3eSknakahara 	}
19714ab3af3eSknakahara 	if (IV_SP_OUT(var) != NULL) {
19724ab3af3eSknakahara 		if_ipsec_del_sp0(IV_SP_OUT(var));
19734ab3af3eSknakahara 		IV_SP_OUT(var) = NULL;
19744ab3af3eSknakahara 	}
19754ab3af3eSknakahara 	if (IV_SP_IN(var) != NULL) {
19764ab3af3eSknakahara 		if_ipsec_del_sp0(IV_SP_IN(var));
19774ab3af3eSknakahara 		IV_SP_IN(var) = NULL;
19784ab3af3eSknakahara 	}
19794ab3af3eSknakahara 
198031414b24Sknakahara 	return error;
19814ab3af3eSknakahara }
19824ab3af3eSknakahara 
19834ab3af3eSknakahara static int
if_ipsec_del_sp0(struct secpolicy * sp)19844ab3af3eSknakahara if_ipsec_del_sp0(struct secpolicy *sp)
19854ab3af3eSknakahara {
19864ab3af3eSknakahara 	struct sadb_msg msg;
19874ab3af3eSknakahara 	struct sadb_x_policy xpl;
19884ab3af3eSknakahara 	size_t size;
19894ab3af3eSknakahara 	uint16_t ext_msg_len = 0;
19904ab3af3eSknakahara 	int error;
19914ab3af3eSknakahara 	struct mbuf *m;
19924ab3af3eSknakahara 
19934ab3af3eSknakahara 	if (sp == NULL)
19944ab3af3eSknakahara 		return 0;
19954ab3af3eSknakahara 
19964ab3af3eSknakahara 	memset(&msg, 0, sizeof(msg));
19974ab3af3eSknakahara 	memset(&xpl, 0, sizeof(xpl));
19984ab3af3eSknakahara 
1999f6621122Sknakahara 	MGETHDR(m, M_WAIT, MT_DATA);
20004ab3af3eSknakahara 
200131414b24Sknakahara 	size = if_ipsec_set_sadb_x_policy(&xpl, NULL, 0, 0, sp->id, 0, NULL, NULL, 0);
20024ab3af3eSknakahara 	ext_msg_len += PFKEY_UNIT64(size);
20034ab3af3eSknakahara 
20044ab3af3eSknakahara 	if_ipsec_set_sadb_msg_del(&msg, ext_msg_len);
20054ab3af3eSknakahara 
20064ab3af3eSknakahara 	m->m_len = sizeof(msg);
20074ab3af3eSknakahara 	m_copyback(m, 0, sizeof(msg), &msg);
20084ab3af3eSknakahara 
20094ab3af3eSknakahara 	if_ipsec_add_mbuf(m, &xpl, sizeof(xpl));
20104ab3af3eSknakahara 
20114ab3af3eSknakahara 	/*  unreference correspond to key_kpi_spdadd(). */
20124ab3af3eSknakahara 	KEY_SP_UNREF(&sp);
20134ab3af3eSknakahara 	error = key_kpi_spddelete2(m);
20144ab3af3eSknakahara 	if (error != 0) {
20154ab3af3eSknakahara 		log(LOG_ERR, "%s: cannot delete SP(ID=%u) (error=%d).\n",
20164ab3af3eSknakahara 		    __func__, sp->id, error);
20174ab3af3eSknakahara 	}
20184ab3af3eSknakahara 	return error;
20194ab3af3eSknakahara }
20204ab3af3eSknakahara 
20214ab3af3eSknakahara static void
if_ipsec_del_sp(struct ipsec_variant * var)20224ab3af3eSknakahara if_ipsec_del_sp(struct ipsec_variant *var)
20234ab3af3eSknakahara {
20244ab3af3eSknakahara 
20254ab3af3eSknakahara 	/* are the SPs shared? */
20264ab3af3eSknakahara 	if (if_ipsec_unshare_sp(var))
20274ab3af3eSknakahara 		return;
20284ab3af3eSknakahara 
20294ab3af3eSknakahara 	(void)if_ipsec_del_sp0(IV_SP_OUT(var));
20304ab3af3eSknakahara 	(void)if_ipsec_del_sp0(IV_SP_IN(var));
20314ab3af3eSknakahara 	(void)if_ipsec_del_sp0(IV_SP_OUT6(var));
20324ab3af3eSknakahara 	(void)if_ipsec_del_sp0(IV_SP_IN6(var));
20334ab3af3eSknakahara 	IV_SP_IN(var) = NULL;
20344ab3af3eSknakahara 	IV_SP_IN6(var) = NULL;
20354ab3af3eSknakahara 	IV_SP_OUT(var) = NULL;
20364ab3af3eSknakahara 	IV_SP_OUT6(var) = NULL;
20374ab3af3eSknakahara }
20384ab3af3eSknakahara 
20394ab3af3eSknakahara static int
if_ipsec_replace_sp(struct ipsec_softc * sc,struct ipsec_variant * ovar,struct ipsec_variant * nvar)20404ab3af3eSknakahara if_ipsec_replace_sp(struct ipsec_softc *sc, struct ipsec_variant *ovar,
20414ab3af3eSknakahara     struct ipsec_variant *nvar)
20424ab3af3eSknakahara {
20434ab3af3eSknakahara 	in_port_t src_port = 0;
20444ab3af3eSknakahara 	in_port_t dst_port = 0;
20454ab3af3eSknakahara 	struct sockaddr *src;
20464ab3af3eSknakahara 	struct sockaddr *dst;
20474ab3af3eSknakahara 	int error = 0;
20484ab3af3eSknakahara 
20494ab3af3eSknakahara 	KASSERT(mutex_owned(&sc->ipsec_lock));
20504ab3af3eSknakahara 
20514ab3af3eSknakahara 	if_ipsec_del_sp(ovar);
20524ab3af3eSknakahara 
20534ab3af3eSknakahara 	src = nvar->iv_psrc;
20544ab3af3eSknakahara 	dst = nvar->iv_pdst;
20554ab3af3eSknakahara 	if (if_ipsec_nat_t(sc)) {
20564ab3af3eSknakahara 		/* NAT-T enabled */
20574ab3af3eSknakahara 		src_port = nvar->iv_sport;
20584ab3af3eSknakahara 		dst_port = nvar->iv_dport;
20594ab3af3eSknakahara 	}
20604ab3af3eSknakahara 	if (src && dst)
20614ab3af3eSknakahara 		error = if_ipsec_add_sp(nvar, src, src_port, dst, dst_port);
20624ab3af3eSknakahara 
20634ab3af3eSknakahara 	return error;
20644ab3af3eSknakahara }
20654ab3af3eSknakahara 
20664ab3af3eSknakahara /*
20674ab3af3eSknakahara  * ipsec_variant and its SPs update API.
20684ab3af3eSknakahara  *
20694ab3af3eSknakahara  * Assumption:
20704ab3af3eSknakahara  * reader side dereferences sc->ipsec_var in reader critical section only,
20714ab3af3eSknakahara  * that is, all of reader sides do not reader the sc->ipsec_var after
20724ab3af3eSknakahara  * pserialize_perform().
20734ab3af3eSknakahara  */
20744ab3af3eSknakahara static int
if_ipsec_update_variant(struct ipsec_softc * sc,struct ipsec_variant * nvar,struct ipsec_variant * nullvar)20754ab3af3eSknakahara if_ipsec_update_variant(struct ipsec_softc *sc, struct ipsec_variant *nvar,
20764ab3af3eSknakahara     struct ipsec_variant *nullvar)
20774ab3af3eSknakahara {
20784ab3af3eSknakahara 	struct ifnet *ifp = &sc->ipsec_if;
20794ab3af3eSknakahara 	struct ipsec_variant *ovar = sc->ipsec_var;
20804ab3af3eSknakahara 	int error;
20814ab3af3eSknakahara 
20824ab3af3eSknakahara 	KASSERT(mutex_owned(&sc->ipsec_lock));
20834ab3af3eSknakahara 
20844ab3af3eSknakahara 	/*
20854ab3af3eSknakahara 	 * To keep consistency between ipsec(4) I/F settings and SPs,
20864ab3af3eSknakahara 	 * we stop packet processing while replacing SPs, that is, we set
20874ab3af3eSknakahara 	 * "null" config variant to sc->ipsec_var.
20884ab3af3eSknakahara 	 */
208947880c13Sriastradh 	atomic_store_release(&sc->ipsec_var, nullvar);
2090ebac3c72Sknakahara 	pserialize_perform(sc->ipsec_psz);
20914ab3af3eSknakahara 	psref_target_destroy(&ovar->iv_psref, iv_psref_class);
20924ab3af3eSknakahara 
20934ab3af3eSknakahara 	error = if_ipsec_replace_sp(sc, ovar, nvar);
20944ab3af3eSknakahara 	if (!error)
209547880c13Sriastradh 		atomic_store_release(&sc->ipsec_var, nvar);
20964ab3af3eSknakahara 	else {
20974ab3af3eSknakahara 		psref_target_init(&ovar->iv_psref, iv_psref_class);
209847880c13Sriastradh 		atomic_store_release(&sc->ipsec_var, ovar); /* rollback */
20994ab3af3eSknakahara 	}
21004ab3af3eSknakahara 
2101ebac3c72Sknakahara 	pserialize_perform(sc->ipsec_psz);
21024ab3af3eSknakahara 	psref_target_destroy(&nullvar->iv_psref, iv_psref_class);
21034ab3af3eSknakahara 
21044ab3af3eSknakahara 	if (if_ipsec_variant_is_configured(sc->ipsec_var))
21054ab3af3eSknakahara 		ifp->if_flags |= IFF_RUNNING;
21064ab3af3eSknakahara 	else
21074ab3af3eSknakahara 		ifp->if_flags &= ~IFF_RUNNING;
21084ab3af3eSknakahara 
21094ab3af3eSknakahara 	return error;
21104ab3af3eSknakahara }
2111