1fcf59617SAndrey V. Elsukov /*-
22c87fdf0SAndrey V. Elsukov * Copyright (c) 2016-2018 Yandex LLC
32c87fdf0SAndrey V. Elsukov * Copyright (c) 2016-2018 Andrey V. Elsukov <ae@FreeBSD.org>
4fcf59617SAndrey V. Elsukov * All rights reserved.
5fcf59617SAndrey V. Elsukov *
6fcf59617SAndrey V. Elsukov * Redistribution and use in source and binary forms, with or without
7fcf59617SAndrey V. Elsukov * modification, are permitted provided that the following conditions
8fcf59617SAndrey V. Elsukov * are met:
9fcf59617SAndrey V. Elsukov *
10fcf59617SAndrey V. Elsukov * 1. Redistributions of source code must retain the above copyright
11fcf59617SAndrey V. Elsukov * notice, this list of conditions and the following disclaimer.
12fcf59617SAndrey V. Elsukov * 2. Redistributions in binary form must reproduce the above copyright
13fcf59617SAndrey V. Elsukov * notice, this list of conditions and the following disclaimer in the
14fcf59617SAndrey V. Elsukov * documentation and/or other materials provided with the distribution.
15fcf59617SAndrey V. Elsukov *
16fcf59617SAndrey V. Elsukov * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17fcf59617SAndrey V. Elsukov * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18fcf59617SAndrey V. Elsukov * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19fcf59617SAndrey V. Elsukov * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20fcf59617SAndrey V. Elsukov * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21fcf59617SAndrey V. Elsukov * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22fcf59617SAndrey V. Elsukov * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23fcf59617SAndrey V. Elsukov * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24fcf59617SAndrey V. Elsukov * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25fcf59617SAndrey V. Elsukov * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26fcf59617SAndrey V. Elsukov */
27fcf59617SAndrey V. Elsukov
28fcf59617SAndrey V. Elsukov #include <sys/cdefs.h>
29fcf59617SAndrey V. Elsukov #include "opt_inet.h"
30fcf59617SAndrey V. Elsukov #include "opt_inet6.h"
3128d2a72bSJohn Baldwin #include "opt_ipsec.h"
32fcf59617SAndrey V. Elsukov
33fcf59617SAndrey V. Elsukov #include <sys/param.h>
34fcf59617SAndrey V. Elsukov #include <sys/systm.h>
35fcf59617SAndrey V. Elsukov #include <sys/kernel.h>
36fcf59617SAndrey V. Elsukov #include <sys/fnv_hash.h>
37fcf59617SAndrey V. Elsukov #include <sys/jail.h>
38fcf59617SAndrey V. Elsukov #include <sys/lock.h>
39fcf59617SAndrey V. Elsukov #include <sys/malloc.h>
40fcf59617SAndrey V. Elsukov #include <sys/mbuf.h>
41fcf59617SAndrey V. Elsukov #include <sys/module.h>
42fcf59617SAndrey V. Elsukov #include <sys/socket.h>
43fcf59617SAndrey V. Elsukov #include <sys/sockio.h>
44fcf59617SAndrey V. Elsukov #include <sys/sx.h>
45fcf59617SAndrey V. Elsukov #include <sys/errno.h>
46fcf59617SAndrey V. Elsukov #include <sys/sysctl.h>
47fcf59617SAndrey V. Elsukov #include <sys/priv.h>
48fcf59617SAndrey V. Elsukov #include <sys/proc.h>
49fcf59617SAndrey V. Elsukov #include <sys/conf.h>
50fcf59617SAndrey V. Elsukov
51fcf59617SAndrey V. Elsukov #include <net/if.h>
52fcf59617SAndrey V. Elsukov #include <net/if_var.h>
532c2b37adSJustin Hibbits #include <net/if_private.h>
54fcf59617SAndrey V. Elsukov #include <net/if_clone.h>
55fcf59617SAndrey V. Elsukov #include <net/if_types.h>
56fcf59617SAndrey V. Elsukov #include <net/bpf.h>
57fcf59617SAndrey V. Elsukov #include <net/route.h>
58fcf59617SAndrey V. Elsukov #include <net/vnet.h>
59fcf59617SAndrey V. Elsukov
60fcf59617SAndrey V. Elsukov #include <netinet/in.h>
61fcf59617SAndrey V. Elsukov #include <netinet/in_var.h>
62fcf59617SAndrey V. Elsukov #include <netinet/ip.h>
632c87fdf0SAndrey V. Elsukov #include <netinet/ip_encap.h>
64fcf59617SAndrey V. Elsukov
65fcf59617SAndrey V. Elsukov #include <netinet/ip6.h>
66fcf59617SAndrey V. Elsukov #include <netinet6/in6_var.h>
67fcf59617SAndrey V. Elsukov #include <netinet6/scope6_var.h>
68fcf59617SAndrey V. Elsukov
69fcf59617SAndrey V. Elsukov #include <netipsec/ipsec.h>
70fcf59617SAndrey V. Elsukov #ifdef INET6
71fcf59617SAndrey V. Elsukov #include <netipsec/ipsec6.h>
72fcf59617SAndrey V. Elsukov #endif
73fcf59617SAndrey V. Elsukov
74fcf59617SAndrey V. Elsukov #include <net/if_ipsec.h>
75fcf59617SAndrey V. Elsukov #include <netipsec/key.h>
76fcf59617SAndrey V. Elsukov
77fcf59617SAndrey V. Elsukov #include <security/mac/mac_framework.h>
78fcf59617SAndrey V. Elsukov
79fcf59617SAndrey V. Elsukov static MALLOC_DEFINE(M_IPSEC, "ipsec", "IPsec Virtual Tunnel Interface");
80fcf59617SAndrey V. Elsukov static const char ipsecname[] = "ipsec";
81fcf59617SAndrey V. Elsukov
82fcf59617SAndrey V. Elsukov #if defined(INET) && defined(INET6)
83fcf59617SAndrey V. Elsukov #define IPSEC_SPCOUNT 4
84fcf59617SAndrey V. Elsukov #else
85fcf59617SAndrey V. Elsukov #define IPSEC_SPCOUNT 2
86fcf59617SAndrey V. Elsukov #endif
87fcf59617SAndrey V. Elsukov
88fcf59617SAndrey V. Elsukov struct ipsec_softc {
89fcf59617SAndrey V. Elsukov struct ifnet *ifp;
90fcf59617SAndrey V. Elsukov struct secpolicy *sp[IPSEC_SPCOUNT];
91fcf59617SAndrey V. Elsukov uint32_t reqid;
92fcf59617SAndrey V. Elsukov u_int family;
93fcf59617SAndrey V. Elsukov u_int fibnum;
942c87fdf0SAndrey V. Elsukov
952c87fdf0SAndrey V. Elsukov CK_LIST_ENTRY(ipsec_softc) idhash;
962c87fdf0SAndrey V. Elsukov CK_LIST_ENTRY(ipsec_softc) srchash;
97fcf59617SAndrey V. Elsukov };
98fcf59617SAndrey V. Elsukov
992c87fdf0SAndrey V. Elsukov #define IPSEC_RLOCK_TRACKER struct epoch_tracker ipsec_et
1002c87fdf0SAndrey V. Elsukov #define IPSEC_RLOCK() epoch_enter_preempt(net_epoch_preempt, &ipsec_et)
1012c87fdf0SAndrey V. Elsukov #define IPSEC_RUNLOCK() epoch_exit_preempt(net_epoch_preempt, &ipsec_et)
1022c87fdf0SAndrey V. Elsukov #define IPSEC_WAIT() epoch_wait_preempt(net_epoch_preempt)
103fcf59617SAndrey V. Elsukov
1042c87fdf0SAndrey V. Elsukov #ifndef IPSEC_HASH_SIZE
1052c87fdf0SAndrey V. Elsukov #define IPSEC_HASH_SIZE (1 << 5)
1062c87fdf0SAndrey V. Elsukov #endif
107fcf59617SAndrey V. Elsukov
1082c87fdf0SAndrey V. Elsukov CK_LIST_HEAD(ipsec_iflist, ipsec_softc);
1092c87fdf0SAndrey V. Elsukov VNET_DEFINE_STATIC(struct ipsec_iflist *, ipsec_idhtbl) = NULL;
1102c87fdf0SAndrey V. Elsukov #define V_ipsec_idhtbl VNET(ipsec_idhtbl)
111fcf59617SAndrey V. Elsukov
1122c87fdf0SAndrey V. Elsukov #ifdef INET
1132c87fdf0SAndrey V. Elsukov VNET_DEFINE_STATIC(struct ipsec_iflist *, ipsec4_srchtbl) = NULL;
1142c87fdf0SAndrey V. Elsukov #define V_ipsec4_srchtbl VNET(ipsec4_srchtbl)
1152c87fdf0SAndrey V. Elsukov static const struct srcaddrtab *ipsec4_srctab = NULL;
1162c87fdf0SAndrey V. Elsukov #endif
117fcf59617SAndrey V. Elsukov
1182c87fdf0SAndrey V. Elsukov #ifdef INET6
1192c87fdf0SAndrey V. Elsukov VNET_DEFINE_STATIC(struct ipsec_iflist *, ipsec6_srchtbl) = NULL;
1202c87fdf0SAndrey V. Elsukov #define V_ipsec6_srchtbl VNET(ipsec6_srchtbl)
1212c87fdf0SAndrey V. Elsukov static const struct srcaddrtab *ipsec6_srctab = NULL;
1222c87fdf0SAndrey V. Elsukov #endif
1232c87fdf0SAndrey V. Elsukov
1242c87fdf0SAndrey V. Elsukov static struct ipsec_iflist *
ipsec_idhash(uint32_t id)1252c87fdf0SAndrey V. Elsukov ipsec_idhash(uint32_t id)
126fcf59617SAndrey V. Elsukov {
127fcf59617SAndrey V. Elsukov
1282c87fdf0SAndrey V. Elsukov return (&V_ipsec_idhtbl[fnv_32_buf(&id, sizeof(id),
1292c87fdf0SAndrey V. Elsukov FNV1_32_INIT) & (IPSEC_HASH_SIZE - 1)]);
130fcf59617SAndrey V. Elsukov }
131fcf59617SAndrey V. Elsukov
1322c87fdf0SAndrey V. Elsukov static struct ipsec_iflist *
ipsec_srchash(const struct sockaddr * sa)1332c87fdf0SAndrey V. Elsukov ipsec_srchash(const struct sockaddr *sa)
1342c87fdf0SAndrey V. Elsukov {
1352c87fdf0SAndrey V. Elsukov uint32_t hval;
1362c87fdf0SAndrey V. Elsukov
1372c87fdf0SAndrey V. Elsukov switch (sa->sa_family) {
1382c87fdf0SAndrey V. Elsukov #ifdef INET
1392c87fdf0SAndrey V. Elsukov case AF_INET:
1402c87fdf0SAndrey V. Elsukov hval = fnv_32_buf(
1412c87fdf0SAndrey V. Elsukov &((const struct sockaddr_in *)sa)->sin_addr.s_addr,
1422c87fdf0SAndrey V. Elsukov sizeof(in_addr_t), FNV1_32_INIT);
1432c87fdf0SAndrey V. Elsukov return (&V_ipsec4_srchtbl[hval & (IPSEC_HASH_SIZE - 1)]);
1442c87fdf0SAndrey V. Elsukov #endif
1452c87fdf0SAndrey V. Elsukov #ifdef INET6
1462c87fdf0SAndrey V. Elsukov case AF_INET6:
1472c87fdf0SAndrey V. Elsukov hval = fnv_32_buf(
1482c87fdf0SAndrey V. Elsukov &((const struct sockaddr_in6 *)sa)->sin6_addr,
1492c87fdf0SAndrey V. Elsukov sizeof(struct in6_addr), FNV1_32_INIT);
1502c87fdf0SAndrey V. Elsukov return (&V_ipsec6_srchtbl[hval & (IPSEC_HASH_SIZE - 1)]);
1512c87fdf0SAndrey V. Elsukov #endif
1522c87fdf0SAndrey V. Elsukov }
1532c87fdf0SAndrey V. Elsukov return (NULL);
1542c87fdf0SAndrey V. Elsukov }
155fcf59617SAndrey V. Elsukov
156fcf59617SAndrey V. Elsukov /*
157fcf59617SAndrey V. Elsukov * ipsec_ioctl_sx protects from concurrent ioctls.
158fcf59617SAndrey V. Elsukov */
159fcf59617SAndrey V. Elsukov static struct sx ipsec_ioctl_sx;
160fcf59617SAndrey V. Elsukov SX_SYSINIT(ipsec_ioctl_sx, &ipsec_ioctl_sx, "ipsec_ioctl");
161fcf59617SAndrey V. Elsukov
162fcf59617SAndrey V. Elsukov static int ipsec_init_reqid(struct ipsec_softc *);
163fcf59617SAndrey V. Elsukov static int ipsec_set_tunnel(struct ipsec_softc *, struct sockaddr *,
164fcf59617SAndrey V. Elsukov struct sockaddr *, uint32_t);
1652c87fdf0SAndrey V. Elsukov static void ipsec_delete_tunnel(struct ipsec_softc *);
166fcf59617SAndrey V. Elsukov
167fcf59617SAndrey V. Elsukov static int ipsec_set_addresses(struct ifnet *, struct sockaddr *,
168fcf59617SAndrey V. Elsukov struct sockaddr *);
1692c87fdf0SAndrey V. Elsukov static int ipsec_set_reqid(struct ipsec_softc *, uint32_t);
1702c87fdf0SAndrey V. Elsukov static void ipsec_set_running(struct ipsec_softc *);
171fcf59617SAndrey V. Elsukov
172dd4490fdSAndrey V. Elsukov #ifdef VIMAGE
173dd4490fdSAndrey V. Elsukov static void ipsec_reassign(struct ifnet *, struct vnet *, char *);
174dd4490fdSAndrey V. Elsukov #endif
1752c87fdf0SAndrey V. Elsukov static void ipsec_srcaddr(void *, const struct sockaddr *, int);
176fcf59617SAndrey V. Elsukov static int ipsec_ioctl(struct ifnet *, u_long, caddr_t);
177fcf59617SAndrey V. Elsukov static int ipsec_transmit(struct ifnet *, struct mbuf *);
178fcf59617SAndrey V. Elsukov static int ipsec_output(struct ifnet *, struct mbuf *,
179fcf59617SAndrey V. Elsukov const struct sockaddr *, struct route *);
180fcf59617SAndrey V. Elsukov static void ipsec_qflush(struct ifnet *);
181fcf59617SAndrey V. Elsukov static int ipsec_clone_create(struct if_clone *, int, caddr_t);
182fcf59617SAndrey V. Elsukov static void ipsec_clone_destroy(struct ifnet *);
183fcf59617SAndrey V. Elsukov
1845f901c92SAndrew Turner VNET_DEFINE_STATIC(struct if_clone *, ipsec_cloner);
185fcf59617SAndrey V. Elsukov #define V_ipsec_cloner VNET(ipsec_cloner)
186fcf59617SAndrey V. Elsukov
187fcf59617SAndrey V. Elsukov static int
ipsec_clone_create(struct if_clone * ifc,int unit,caddr_t params)188fcf59617SAndrey V. Elsukov ipsec_clone_create(struct if_clone *ifc, int unit, caddr_t params)
189fcf59617SAndrey V. Elsukov {
190fcf59617SAndrey V. Elsukov struct ipsec_softc *sc;
191fcf59617SAndrey V. Elsukov struct ifnet *ifp;
192fcf59617SAndrey V. Elsukov
193fcf59617SAndrey V. Elsukov sc = malloc(sizeof(*sc), M_IPSEC, M_WAITOK | M_ZERO);
194fcf59617SAndrey V. Elsukov sc->fibnum = curthread->td_proc->p_fibnum;
195fcf59617SAndrey V. Elsukov sc->ifp = ifp = if_alloc(IFT_TUNNEL);
196fcf59617SAndrey V. Elsukov ifp->if_softc = sc;
197fcf59617SAndrey V. Elsukov if_initname(ifp, ipsecname, unit);
198fcf59617SAndrey V. Elsukov
199fcf59617SAndrey V. Elsukov ifp->if_addrlen = 0;
200fcf59617SAndrey V. Elsukov ifp->if_mtu = IPSEC_MTU;
201fcf59617SAndrey V. Elsukov ifp->if_flags = IFF_POINTOPOINT | IFF_MULTICAST;
202fcf59617SAndrey V. Elsukov ifp->if_ioctl = ipsec_ioctl;
203fcf59617SAndrey V. Elsukov ifp->if_transmit = ipsec_transmit;
204fcf59617SAndrey V. Elsukov ifp->if_qflush = ipsec_qflush;
205fcf59617SAndrey V. Elsukov ifp->if_output = ipsec_output;
206dd4490fdSAndrey V. Elsukov #ifdef VIMAGE
207dd4490fdSAndrey V. Elsukov ifp->if_reassign = ipsec_reassign;
208dd4490fdSAndrey V. Elsukov #endif
209fcf59617SAndrey V. Elsukov if_attach(ifp);
210fcf59617SAndrey V. Elsukov bpfattach(ifp, DLT_NULL, sizeof(uint32_t));
211fcf59617SAndrey V. Elsukov
212fcf59617SAndrey V. Elsukov return (0);
213fcf59617SAndrey V. Elsukov }
214fcf59617SAndrey V. Elsukov
215dd4490fdSAndrey V. Elsukov #ifdef VIMAGE
216dd4490fdSAndrey V. Elsukov static void
ipsec_reassign(struct ifnet * ifp,struct vnet * new_vnet __unused,char * unused __unused)217dd4490fdSAndrey V. Elsukov ipsec_reassign(struct ifnet *ifp, struct vnet *new_vnet __unused,
218dd4490fdSAndrey V. Elsukov char *unused __unused)
219dd4490fdSAndrey V. Elsukov {
220dd4490fdSAndrey V. Elsukov struct ipsec_softc *sc;
221dd4490fdSAndrey V. Elsukov
222dd4490fdSAndrey V. Elsukov sx_xlock(&ipsec_ioctl_sx);
223dd4490fdSAndrey V. Elsukov sc = ifp->if_softc;
224dd4490fdSAndrey V. Elsukov if (sc != NULL)
225dd4490fdSAndrey V. Elsukov ipsec_delete_tunnel(sc);
226dd4490fdSAndrey V. Elsukov sx_xunlock(&ipsec_ioctl_sx);
227dd4490fdSAndrey V. Elsukov }
228dd4490fdSAndrey V. Elsukov #endif /* VIMAGE */
229dd4490fdSAndrey V. Elsukov
230fcf59617SAndrey V. Elsukov static void
ipsec_clone_destroy(struct ifnet * ifp)231fcf59617SAndrey V. Elsukov ipsec_clone_destroy(struct ifnet *ifp)
232fcf59617SAndrey V. Elsukov {
233fcf59617SAndrey V. Elsukov struct ipsec_softc *sc;
234fcf59617SAndrey V. Elsukov
235fcf59617SAndrey V. Elsukov sx_xlock(&ipsec_ioctl_sx);
236fcf59617SAndrey V. Elsukov sc = ifp->if_softc;
2372c87fdf0SAndrey V. Elsukov ipsec_delete_tunnel(sc);
238e6b383b2SAndrey V. Elsukov /*
239e6b383b2SAndrey V. Elsukov * Delete softc from idhash on interface destroy, since
240e6b383b2SAndrey V. Elsukov * ipsec_delete_tunnel() keeps reqid unchanged.
241e6b383b2SAndrey V. Elsukov */
242e6b383b2SAndrey V. Elsukov if (sc->reqid != 0)
243e6b383b2SAndrey V. Elsukov CK_LIST_REMOVE(sc, idhash);
244fcf59617SAndrey V. Elsukov bpfdetach(ifp);
245fcf59617SAndrey V. Elsukov if_detach(ifp);
246fcf59617SAndrey V. Elsukov ifp->if_softc = NULL;
247fcf59617SAndrey V. Elsukov sx_xunlock(&ipsec_ioctl_sx);
248fcf59617SAndrey V. Elsukov
2492c87fdf0SAndrey V. Elsukov IPSEC_WAIT();
250fcf59617SAndrey V. Elsukov if_free(ifp);
251fcf59617SAndrey V. Elsukov free(sc, M_IPSEC);
252fcf59617SAndrey V. Elsukov }
253fcf59617SAndrey V. Elsukov
2542c87fdf0SAndrey V. Elsukov static struct ipsec_iflist *
ipsec_hashinit(void)2552c87fdf0SAndrey V. Elsukov ipsec_hashinit(void)
2562c87fdf0SAndrey V. Elsukov {
2572c87fdf0SAndrey V. Elsukov struct ipsec_iflist *hash;
2582c87fdf0SAndrey V. Elsukov int i;
2592c87fdf0SAndrey V. Elsukov
2602c87fdf0SAndrey V. Elsukov hash = malloc(sizeof(struct ipsec_iflist) * IPSEC_HASH_SIZE,
2612c87fdf0SAndrey V. Elsukov M_IPSEC, M_WAITOK);
2622c87fdf0SAndrey V. Elsukov for (i = 0; i < IPSEC_HASH_SIZE; i++)
2632c87fdf0SAndrey V. Elsukov CK_LIST_INIT(&hash[i]);
2642c87fdf0SAndrey V. Elsukov
2652c87fdf0SAndrey V. Elsukov return (hash);
2662c87fdf0SAndrey V. Elsukov }
2672c87fdf0SAndrey V. Elsukov
268fcf59617SAndrey V. Elsukov static void
vnet_ipsec_init(const void * unused __unused)269fcf59617SAndrey V. Elsukov vnet_ipsec_init(const void *unused __unused)
270fcf59617SAndrey V. Elsukov {
271fcf59617SAndrey V. Elsukov
2722c87fdf0SAndrey V. Elsukov V_ipsec_idhtbl = ipsec_hashinit();
2732c87fdf0SAndrey V. Elsukov #ifdef INET
2742c87fdf0SAndrey V. Elsukov V_ipsec4_srchtbl = ipsec_hashinit();
2752c87fdf0SAndrey V. Elsukov if (IS_DEFAULT_VNET(curvnet))
2762c87fdf0SAndrey V. Elsukov ipsec4_srctab = ip_encap_register_srcaddr(ipsec_srcaddr,
2772c87fdf0SAndrey V. Elsukov NULL, M_WAITOK);
2782c87fdf0SAndrey V. Elsukov #endif
2792c87fdf0SAndrey V. Elsukov #ifdef INET6
2802c87fdf0SAndrey V. Elsukov V_ipsec6_srchtbl = ipsec_hashinit();
2812c87fdf0SAndrey V. Elsukov if (IS_DEFAULT_VNET(curvnet))
2822c87fdf0SAndrey V. Elsukov ipsec6_srctab = ip6_encap_register_srcaddr(ipsec_srcaddr,
2832c87fdf0SAndrey V. Elsukov NULL, M_WAITOK);
2842c87fdf0SAndrey V. Elsukov #endif
285fcf59617SAndrey V. Elsukov V_ipsec_cloner = if_clone_simple(ipsecname, ipsec_clone_create,
286fcf59617SAndrey V. Elsukov ipsec_clone_destroy, 0);
287fcf59617SAndrey V. Elsukov }
288fcf59617SAndrey V. Elsukov VNET_SYSINIT(vnet_ipsec_init, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY,
289fcf59617SAndrey V. Elsukov vnet_ipsec_init, NULL);
290fcf59617SAndrey V. Elsukov
291fcf59617SAndrey V. Elsukov static void
vnet_ipsec_uninit(const void * unused __unused)292fcf59617SAndrey V. Elsukov vnet_ipsec_uninit(const void *unused __unused)
293fcf59617SAndrey V. Elsukov {
294fcf59617SAndrey V. Elsukov
295fcf59617SAndrey V. Elsukov if_clone_detach(V_ipsec_cloner);
2962c87fdf0SAndrey V. Elsukov free(V_ipsec_idhtbl, M_IPSEC);
297221022e1SAndrey V. Elsukov /*
298221022e1SAndrey V. Elsukov * Use V_ipsec_idhtbl pointer as indicator that VNET is going to be
299221022e1SAndrey V. Elsukov * destroyed, it is used by ipsec_srcaddr() callback.
300221022e1SAndrey V. Elsukov */
301221022e1SAndrey V. Elsukov V_ipsec_idhtbl = NULL;
302221022e1SAndrey V. Elsukov IPSEC_WAIT();
303221022e1SAndrey V. Elsukov
3042c87fdf0SAndrey V. Elsukov #ifdef INET
3052c87fdf0SAndrey V. Elsukov if (IS_DEFAULT_VNET(curvnet))
3062c87fdf0SAndrey V. Elsukov ip_encap_unregister_srcaddr(ipsec4_srctab);
3072c87fdf0SAndrey V. Elsukov free(V_ipsec4_srchtbl, M_IPSEC);
3082c87fdf0SAndrey V. Elsukov #endif
3092c87fdf0SAndrey V. Elsukov #ifdef INET6
3102c87fdf0SAndrey V. Elsukov if (IS_DEFAULT_VNET(curvnet))
3112c87fdf0SAndrey V. Elsukov ip6_encap_unregister_srcaddr(ipsec6_srctab);
3122c87fdf0SAndrey V. Elsukov free(V_ipsec6_srchtbl, M_IPSEC);
3132c87fdf0SAndrey V. Elsukov #endif
314fcf59617SAndrey V. Elsukov }
315fcf59617SAndrey V. Elsukov VNET_SYSUNINIT(vnet_ipsec_uninit, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY,
316fcf59617SAndrey V. Elsukov vnet_ipsec_uninit, NULL);
317fcf59617SAndrey V. Elsukov
318fcf59617SAndrey V. Elsukov static struct secpolicy *
ipsec_getpolicy(struct ipsec_softc * sc,int dir,sa_family_t af)319fcf59617SAndrey V. Elsukov ipsec_getpolicy(struct ipsec_softc *sc, int dir, sa_family_t af)
320fcf59617SAndrey V. Elsukov {
321fcf59617SAndrey V. Elsukov
322fcf59617SAndrey V. Elsukov switch (af) {
323fcf59617SAndrey V. Elsukov #ifdef INET
324fcf59617SAndrey V. Elsukov case AF_INET:
325fcf59617SAndrey V. Elsukov return (sc->sp[(dir == IPSEC_DIR_INBOUND ? 0: 1)]);
326fcf59617SAndrey V. Elsukov #endif
327fcf59617SAndrey V. Elsukov #ifdef INET6
328fcf59617SAndrey V. Elsukov case AF_INET6:
329fcf59617SAndrey V. Elsukov return (sc->sp[(dir == IPSEC_DIR_INBOUND ? 0: 1)
330fcf59617SAndrey V. Elsukov #ifdef INET
331fcf59617SAndrey V. Elsukov + 2
332fcf59617SAndrey V. Elsukov #endif
333fcf59617SAndrey V. Elsukov ]);
334fcf59617SAndrey V. Elsukov #endif
335fcf59617SAndrey V. Elsukov }
336fcf59617SAndrey V. Elsukov return (NULL);
337fcf59617SAndrey V. Elsukov }
338fcf59617SAndrey V. Elsukov
339fcf59617SAndrey V. Elsukov static struct secasindex *
ipsec_getsaidx(struct ipsec_softc * sc,int dir,sa_family_t af)340fcf59617SAndrey V. Elsukov ipsec_getsaidx(struct ipsec_softc *sc, int dir, sa_family_t af)
341fcf59617SAndrey V. Elsukov {
342fcf59617SAndrey V. Elsukov struct secpolicy *sp;
343fcf59617SAndrey V. Elsukov
344fcf59617SAndrey V. Elsukov sp = ipsec_getpolicy(sc, dir, af);
345fcf59617SAndrey V. Elsukov if (sp == NULL)
346fcf59617SAndrey V. Elsukov return (NULL);
347fcf59617SAndrey V. Elsukov return (&sp->req[0]->saidx);
348fcf59617SAndrey V. Elsukov }
349fcf59617SAndrey V. Elsukov
350fcf59617SAndrey V. Elsukov static int
ipsec_transmit(struct ifnet * ifp,struct mbuf * m)351fcf59617SAndrey V. Elsukov ipsec_transmit(struct ifnet *ifp, struct mbuf *m)
352fcf59617SAndrey V. Elsukov {
353fcf59617SAndrey V. Elsukov IPSEC_RLOCK_TRACKER;
354fcf59617SAndrey V. Elsukov struct ipsec_softc *sc;
355fcf59617SAndrey V. Elsukov struct secpolicy *sp;
356*0ff2d00dSKonstantin Belousov struct ip *ip, iph;
357fcf59617SAndrey V. Elsukov uint32_t af;
358fcf59617SAndrey V. Elsukov int error;
359fcf59617SAndrey V. Elsukov
360cc958ed2SAndrey V. Elsukov IPSEC_RLOCK();
361fcf59617SAndrey V. Elsukov #ifdef MAC
362fcf59617SAndrey V. Elsukov error = mac_ifnet_check_transmit(ifp, m);
363fcf59617SAndrey V. Elsukov if (error) {
364fcf59617SAndrey V. Elsukov m_freem(m);
365fcf59617SAndrey V. Elsukov goto err;
366fcf59617SAndrey V. Elsukov }
367fcf59617SAndrey V. Elsukov #endif
368fcf59617SAndrey V. Elsukov error = ENETDOWN;
369fcf59617SAndrey V. Elsukov sc = ifp->if_softc;
370fcf59617SAndrey V. Elsukov if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 ||
371fcf59617SAndrey V. Elsukov (ifp->if_flags & IFF_MONITOR) != 0 ||
3722c87fdf0SAndrey V. Elsukov (ifp->if_flags & IFF_UP) == 0 || sc->family == 0) {
373fcf59617SAndrey V. Elsukov m_freem(m);
374fcf59617SAndrey V. Elsukov goto err;
375fcf59617SAndrey V. Elsukov }
376fcf59617SAndrey V. Elsukov
377fcf59617SAndrey V. Elsukov /* Determine address family to correctly handle packet in BPF */
378*0ff2d00dSKonstantin Belousov ip = &iph;
379*0ff2d00dSKonstantin Belousov m_copydata(m, 0, sizeof(*ip), (char *)ip);
380fcf59617SAndrey V. Elsukov switch (ip->ip_v) {
381fcf59617SAndrey V. Elsukov #ifdef INET
382fcf59617SAndrey V. Elsukov case IPVERSION:
383fcf59617SAndrey V. Elsukov af = AF_INET;
384fcf59617SAndrey V. Elsukov break;
385fcf59617SAndrey V. Elsukov #endif
386fcf59617SAndrey V. Elsukov #ifdef INET6
387fcf59617SAndrey V. Elsukov case (IPV6_VERSION >> 4):
388fcf59617SAndrey V. Elsukov af = AF_INET6;
389fcf59617SAndrey V. Elsukov break;
390fcf59617SAndrey V. Elsukov #endif
391fcf59617SAndrey V. Elsukov default:
392fcf59617SAndrey V. Elsukov error = EAFNOSUPPORT;
393fcf59617SAndrey V. Elsukov m_freem(m);
394fcf59617SAndrey V. Elsukov goto err;
395fcf59617SAndrey V. Elsukov }
396fcf59617SAndrey V. Elsukov
397fcf59617SAndrey V. Elsukov /*
398fcf59617SAndrey V. Elsukov * Loop prevention.
399fcf59617SAndrey V. Elsukov * XXX: for now just check presence of IPSEC_OUT_DONE mbuf tag.
400fcf59617SAndrey V. Elsukov * We can read full chain and compare destination address,
401fcf59617SAndrey V. Elsukov * proto and mode from xform_history with values from softc.
402fcf59617SAndrey V. Elsukov */
403fcf59617SAndrey V. Elsukov if (m_tag_find(m, PACKET_TAG_IPSEC_OUT_DONE, NULL) != NULL) {
404fcf59617SAndrey V. Elsukov m_freem(m);
405fcf59617SAndrey V. Elsukov goto err;
406fcf59617SAndrey V. Elsukov }
407fcf59617SAndrey V. Elsukov
408fcf59617SAndrey V. Elsukov sp = ipsec_getpolicy(sc, IPSEC_DIR_OUTBOUND, af);
409fcf59617SAndrey V. Elsukov key_addref(sp);
410fcf59617SAndrey V. Elsukov M_SETFIB(m, sc->fibnum);
411fcf59617SAndrey V. Elsukov
412fcf59617SAndrey V. Elsukov BPF_MTAP2(ifp, &af, sizeof(af), m);
413fcf59617SAndrey V. Elsukov if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
414fcf59617SAndrey V. Elsukov if_inc_counter(ifp, IFCOUNTER_OBYTES, m->m_pkthdr.len);
415fcf59617SAndrey V. Elsukov
416fcf59617SAndrey V. Elsukov switch (af) {
417fcf59617SAndrey V. Elsukov #ifdef INET
418fcf59617SAndrey V. Elsukov case AF_INET:
419*0ff2d00dSKonstantin Belousov error = ipsec4_process_packet(ifp, m, ip, sp, NULL,
420*0ff2d00dSKonstantin Belousov ifp->if_mtu);
421fcf59617SAndrey V. Elsukov break;
422fcf59617SAndrey V. Elsukov #endif
423fcf59617SAndrey V. Elsukov #ifdef INET6
424fcf59617SAndrey V. Elsukov case AF_INET6:
42500524fd4SKonstantin Belousov error = ipsec6_process_packet(ifp, m, sp, NULL, ifp->if_mtu);
426fcf59617SAndrey V. Elsukov break;
427fcf59617SAndrey V. Elsukov #endif
428fcf59617SAndrey V. Elsukov default:
429fcf59617SAndrey V. Elsukov panic("%s: unknown address family\n", __func__);
430fcf59617SAndrey V. Elsukov }
431fcf59617SAndrey V. Elsukov err:
432fcf59617SAndrey V. Elsukov if (error != 0)
433fcf59617SAndrey V. Elsukov if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
4342c87fdf0SAndrey V. Elsukov IPSEC_RUNLOCK();
435fcf59617SAndrey V. Elsukov return (error);
436fcf59617SAndrey V. Elsukov }
437fcf59617SAndrey V. Elsukov
438fcf59617SAndrey V. Elsukov static void
ipsec_qflush(struct ifnet * ifp __unused)439fcf59617SAndrey V. Elsukov ipsec_qflush(struct ifnet *ifp __unused)
440fcf59617SAndrey V. Elsukov {
441fcf59617SAndrey V. Elsukov
442fcf59617SAndrey V. Elsukov }
443fcf59617SAndrey V. Elsukov
444fcf59617SAndrey V. Elsukov static int
ipsec_output(struct ifnet * ifp,struct mbuf * m,const struct sockaddr * dst,struct route * ro)445fcf59617SAndrey V. Elsukov ipsec_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst,
446fcf59617SAndrey V. Elsukov struct route *ro)
447fcf59617SAndrey V. Elsukov {
448fcf59617SAndrey V. Elsukov
449fcf59617SAndrey V. Elsukov return (ifp->if_transmit(ifp, m));
450fcf59617SAndrey V. Elsukov }
451fcf59617SAndrey V. Elsukov
452fcf59617SAndrey V. Elsukov int
ipsec_if_input(struct mbuf * m,struct secasvar * sav,uint32_t af)453fcf59617SAndrey V. Elsukov ipsec_if_input(struct mbuf *m, struct secasvar *sav, uint32_t af)
454fcf59617SAndrey V. Elsukov {
4552c87fdf0SAndrey V. Elsukov IPSEC_RLOCK_TRACKER;
456fcf59617SAndrey V. Elsukov struct secasindex *saidx;
457fcf59617SAndrey V. Elsukov struct ipsec_softc *sc;
458fcf59617SAndrey V. Elsukov struct ifnet *ifp;
459fcf59617SAndrey V. Elsukov
460fcf59617SAndrey V. Elsukov if (sav->state != SADB_SASTATE_MATURE &&
461fcf59617SAndrey V. Elsukov sav->state != SADB_SASTATE_DYING) {
462fcf59617SAndrey V. Elsukov m_freem(m);
463fcf59617SAndrey V. Elsukov return (ENETDOWN);
464fcf59617SAndrey V. Elsukov }
465fcf59617SAndrey V. Elsukov
466fcf59617SAndrey V. Elsukov if (sav->sah->saidx.mode != IPSEC_MODE_TUNNEL ||
467fcf59617SAndrey V. Elsukov sav->sah->saidx.proto != IPPROTO_ESP)
468fcf59617SAndrey V. Elsukov return (0);
469fcf59617SAndrey V. Elsukov
4702c87fdf0SAndrey V. Elsukov IPSEC_RLOCK();
4712c87fdf0SAndrey V. Elsukov CK_LIST_FOREACH(sc, ipsec_idhash(sav->sah->saidx.reqid), idhash) {
4722c87fdf0SAndrey V. Elsukov if (sc->family == 0)
4732c87fdf0SAndrey V. Elsukov continue;
474fcf59617SAndrey V. Elsukov saidx = ipsec_getsaidx(sc, IPSEC_DIR_INBOUND,
475fcf59617SAndrey V. Elsukov sav->sah->saidx.src.sa.sa_family);
476fcf59617SAndrey V. Elsukov /* SA's reqid should match reqid in SP */
477fcf59617SAndrey V. Elsukov if (saidx == NULL ||
478fcf59617SAndrey V. Elsukov sav->sah->saidx.reqid != saidx->reqid)
479fcf59617SAndrey V. Elsukov continue;
480fcf59617SAndrey V. Elsukov /* SAH's addresses should match tunnel endpoints. */
481fcf59617SAndrey V. Elsukov if (key_sockaddrcmp(&sav->sah->saidx.dst.sa,
482fcf59617SAndrey V. Elsukov &saidx->dst.sa, 0) != 0)
483fcf59617SAndrey V. Elsukov continue;
484fcf59617SAndrey V. Elsukov if (key_sockaddrcmp(&sav->sah->saidx.src.sa,
485fcf59617SAndrey V. Elsukov &saidx->src.sa, 0) == 0)
486fcf59617SAndrey V. Elsukov break;
487fcf59617SAndrey V. Elsukov }
488fcf59617SAndrey V. Elsukov if (sc == NULL) {
4892c87fdf0SAndrey V. Elsukov IPSEC_RUNLOCK();
490fcf59617SAndrey V. Elsukov /* Tunnel was not found. Nothing to do. */
491fcf59617SAndrey V. Elsukov return (0);
492fcf59617SAndrey V. Elsukov }
493fcf59617SAndrey V. Elsukov ifp = sc->ifp;
494fcf59617SAndrey V. Elsukov if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 ||
495fcf59617SAndrey V. Elsukov (ifp->if_flags & IFF_UP) == 0) {
4962c87fdf0SAndrey V. Elsukov IPSEC_RUNLOCK();
497fcf59617SAndrey V. Elsukov m_freem(m);
498fcf59617SAndrey V. Elsukov return (ENETDOWN);
499fcf59617SAndrey V. Elsukov }
500fcf59617SAndrey V. Elsukov /*
501fcf59617SAndrey V. Elsukov * We found matching and working tunnel.
502fcf59617SAndrey V. Elsukov * Set its ifnet as receiving interface.
503fcf59617SAndrey V. Elsukov */
504fcf59617SAndrey V. Elsukov m->m_pkthdr.rcvif = ifp;
505fcf59617SAndrey V. Elsukov
506e287c474SAndrey V. Elsukov m_clrprotoflags(m);
507fcf59617SAndrey V. Elsukov M_SETFIB(m, ifp->if_fib);
508fcf59617SAndrey V. Elsukov BPF_MTAP2(ifp, &af, sizeof(af), m);
509fcf59617SAndrey V. Elsukov if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1);
510fcf59617SAndrey V. Elsukov if_inc_counter(ifp, IFCOUNTER_IBYTES, m->m_pkthdr.len);
511fcf59617SAndrey V. Elsukov if ((ifp->if_flags & IFF_MONITOR) != 0) {
5122c87fdf0SAndrey V. Elsukov IPSEC_RUNLOCK();
513fcf59617SAndrey V. Elsukov m_freem(m);
514fcf59617SAndrey V. Elsukov return (ENETDOWN);
515fcf59617SAndrey V. Elsukov }
5162c87fdf0SAndrey V. Elsukov IPSEC_RUNLOCK();
517fcf59617SAndrey V. Elsukov return (0);
518fcf59617SAndrey V. Elsukov }
519fcf59617SAndrey V. Elsukov
5202c87fdf0SAndrey V. Elsukov static int
ipsec_ioctl(struct ifnet * ifp,u_long cmd,caddr_t data)521fcf59617SAndrey V. Elsukov ipsec_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
522fcf59617SAndrey V. Elsukov {
523fcf59617SAndrey V. Elsukov struct ifreq *ifr = (struct ifreq*)data;
524fcf59617SAndrey V. Elsukov struct sockaddr *dst, *src;
525fcf59617SAndrey V. Elsukov struct ipsec_softc *sc;
526fcf59617SAndrey V. Elsukov struct secasindex *saidx;
527fcf59617SAndrey V. Elsukov #ifdef INET
528fcf59617SAndrey V. Elsukov struct sockaddr_in *sin = NULL;
529fcf59617SAndrey V. Elsukov #endif
530fcf59617SAndrey V. Elsukov #ifdef INET6
531fcf59617SAndrey V. Elsukov struct sockaddr_in6 *sin6 = NULL;
532fcf59617SAndrey V. Elsukov #endif
533fcf59617SAndrey V. Elsukov uint32_t reqid;
534fcf59617SAndrey V. Elsukov int error;
535fcf59617SAndrey V. Elsukov
536fcf59617SAndrey V. Elsukov switch (cmd) {
537fcf59617SAndrey V. Elsukov case SIOCSIFADDR:
538fcf59617SAndrey V. Elsukov ifp->if_flags |= IFF_UP;
539fcf59617SAndrey V. Elsukov case SIOCADDMULTI:
540fcf59617SAndrey V. Elsukov case SIOCDELMULTI:
541fcf59617SAndrey V. Elsukov case SIOCGIFMTU:
542fcf59617SAndrey V. Elsukov case SIOCSIFFLAGS:
543fcf59617SAndrey V. Elsukov return (0);
544fcf59617SAndrey V. Elsukov case SIOCSIFMTU:
545fcf59617SAndrey V. Elsukov if (ifr->ifr_mtu < IPSEC_MTU_MIN ||
546fcf59617SAndrey V. Elsukov ifr->ifr_mtu > IPSEC_MTU_MAX)
547fcf59617SAndrey V. Elsukov return (EINVAL);
548fcf59617SAndrey V. Elsukov else
549fcf59617SAndrey V. Elsukov ifp->if_mtu = ifr->ifr_mtu;
550fcf59617SAndrey V. Elsukov return (0);
551fcf59617SAndrey V. Elsukov }
552fcf59617SAndrey V. Elsukov sx_xlock(&ipsec_ioctl_sx);
553fcf59617SAndrey V. Elsukov sc = ifp->if_softc;
554fcf59617SAndrey V. Elsukov /* Check that softc is still here */
555fcf59617SAndrey V. Elsukov if (sc == NULL) {
556fcf59617SAndrey V. Elsukov error = ENXIO;
557fcf59617SAndrey V. Elsukov goto bad;
558fcf59617SAndrey V. Elsukov }
559fcf59617SAndrey V. Elsukov error = 0;
560fcf59617SAndrey V. Elsukov switch (cmd) {
561fcf59617SAndrey V. Elsukov case SIOCSIFPHYADDR:
562fcf59617SAndrey V. Elsukov #ifdef INET6
563fcf59617SAndrey V. Elsukov case SIOCSIFPHYADDR_IN6:
564fcf59617SAndrey V. Elsukov #endif
565fcf59617SAndrey V. Elsukov error = EINVAL;
566fcf59617SAndrey V. Elsukov switch (cmd) {
567fcf59617SAndrey V. Elsukov #ifdef INET
568fcf59617SAndrey V. Elsukov case SIOCSIFPHYADDR:
569fcf59617SAndrey V. Elsukov src = (struct sockaddr *)
570fcf59617SAndrey V. Elsukov &(((struct in_aliasreq *)data)->ifra_addr);
571fcf59617SAndrey V. Elsukov dst = (struct sockaddr *)
572fcf59617SAndrey V. Elsukov &(((struct in_aliasreq *)data)->ifra_dstaddr);
573fcf59617SAndrey V. Elsukov break;
574fcf59617SAndrey V. Elsukov #endif
575fcf59617SAndrey V. Elsukov #ifdef INET6
576fcf59617SAndrey V. Elsukov case SIOCSIFPHYADDR_IN6:
577fcf59617SAndrey V. Elsukov src = (struct sockaddr *)
578fcf59617SAndrey V. Elsukov &(((struct in6_aliasreq *)data)->ifra_addr);
579fcf59617SAndrey V. Elsukov dst = (struct sockaddr *)
580fcf59617SAndrey V. Elsukov &(((struct in6_aliasreq *)data)->ifra_dstaddr);
581fcf59617SAndrey V. Elsukov break;
582fcf59617SAndrey V. Elsukov #endif
583fcf59617SAndrey V. Elsukov default:
584fcf59617SAndrey V. Elsukov goto bad;
585fcf59617SAndrey V. Elsukov }
586fcf59617SAndrey V. Elsukov /* sa_family must be equal */
587fcf59617SAndrey V. Elsukov if (src->sa_family != dst->sa_family ||
588fcf59617SAndrey V. Elsukov src->sa_len != dst->sa_len)
589fcf59617SAndrey V. Elsukov goto bad;
590fcf59617SAndrey V. Elsukov
591fcf59617SAndrey V. Elsukov /* validate sa_len */
592fcf59617SAndrey V. Elsukov switch (src->sa_family) {
593fcf59617SAndrey V. Elsukov #ifdef INET
594fcf59617SAndrey V. Elsukov case AF_INET:
595fcf59617SAndrey V. Elsukov if (src->sa_len != sizeof(struct sockaddr_in))
596fcf59617SAndrey V. Elsukov goto bad;
597fcf59617SAndrey V. Elsukov break;
598fcf59617SAndrey V. Elsukov #endif
599fcf59617SAndrey V. Elsukov #ifdef INET6
600fcf59617SAndrey V. Elsukov case AF_INET6:
601fcf59617SAndrey V. Elsukov if (src->sa_len != sizeof(struct sockaddr_in6))
602fcf59617SAndrey V. Elsukov goto bad;
603fcf59617SAndrey V. Elsukov break;
604fcf59617SAndrey V. Elsukov #endif
605fcf59617SAndrey V. Elsukov default:
606fcf59617SAndrey V. Elsukov error = EAFNOSUPPORT;
607fcf59617SAndrey V. Elsukov goto bad;
608fcf59617SAndrey V. Elsukov }
609fcf59617SAndrey V. Elsukov /* check sa_family looks sane for the cmd */
610fcf59617SAndrey V. Elsukov error = EAFNOSUPPORT;
611fcf59617SAndrey V. Elsukov switch (cmd) {
612fcf59617SAndrey V. Elsukov #ifdef INET
613fcf59617SAndrey V. Elsukov case SIOCSIFPHYADDR:
614fcf59617SAndrey V. Elsukov if (src->sa_family == AF_INET)
615fcf59617SAndrey V. Elsukov break;
616fcf59617SAndrey V. Elsukov goto bad;
617fcf59617SAndrey V. Elsukov #endif
618fcf59617SAndrey V. Elsukov #ifdef INET6
619fcf59617SAndrey V. Elsukov case SIOCSIFPHYADDR_IN6:
620fcf59617SAndrey V. Elsukov if (src->sa_family == AF_INET6)
621fcf59617SAndrey V. Elsukov break;
622fcf59617SAndrey V. Elsukov goto bad;
623fcf59617SAndrey V. Elsukov #endif
624fcf59617SAndrey V. Elsukov }
625fcf59617SAndrey V. Elsukov error = EADDRNOTAVAIL;
626fcf59617SAndrey V. Elsukov switch (src->sa_family) {
627fcf59617SAndrey V. Elsukov #ifdef INET
628fcf59617SAndrey V. Elsukov case AF_INET:
629fcf59617SAndrey V. Elsukov if (satosin(src)->sin_addr.s_addr == INADDR_ANY ||
630fcf59617SAndrey V. Elsukov satosin(dst)->sin_addr.s_addr == INADDR_ANY)
631fcf59617SAndrey V. Elsukov goto bad;
632fcf59617SAndrey V. Elsukov break;
633fcf59617SAndrey V. Elsukov #endif
634fcf59617SAndrey V. Elsukov #ifdef INET6
635fcf59617SAndrey V. Elsukov case AF_INET6:
6362c87fdf0SAndrey V. Elsukov if (IN6_IS_ADDR_UNSPECIFIED(
6372c87fdf0SAndrey V. Elsukov &satosin6(src)->sin6_addr) ||
6382c87fdf0SAndrey V. Elsukov IN6_IS_ADDR_UNSPECIFIED(
6392c87fdf0SAndrey V. Elsukov &satosin6(dst)->sin6_addr))
640fcf59617SAndrey V. Elsukov goto bad;
641fcf59617SAndrey V. Elsukov /*
642fcf59617SAndrey V. Elsukov * Check validity of the scope zone ID of the
643fcf59617SAndrey V. Elsukov * addresses, and convert it into the kernel
644fcf59617SAndrey V. Elsukov * internal form if necessary.
645fcf59617SAndrey V. Elsukov */
646fcf59617SAndrey V. Elsukov error = sa6_embedscope(satosin6(src), 0);
647fcf59617SAndrey V. Elsukov if (error != 0)
648fcf59617SAndrey V. Elsukov goto bad;
649fcf59617SAndrey V. Elsukov error = sa6_embedscope(satosin6(dst), 0);
650fcf59617SAndrey V. Elsukov if (error != 0)
651fcf59617SAndrey V. Elsukov goto bad;
652fcf59617SAndrey V. Elsukov #endif
653fcf59617SAndrey V. Elsukov };
654fcf59617SAndrey V. Elsukov error = ipsec_set_addresses(ifp, src, dst);
655fcf59617SAndrey V. Elsukov break;
656fcf59617SAndrey V. Elsukov case SIOCDIFPHYADDR:
6572c87fdf0SAndrey V. Elsukov ipsec_delete_tunnel(sc);
658fcf59617SAndrey V. Elsukov break;
659fcf59617SAndrey V. Elsukov case SIOCGIFPSRCADDR:
660fcf59617SAndrey V. Elsukov case SIOCGIFPDSTADDR:
661fcf59617SAndrey V. Elsukov #ifdef INET6
662fcf59617SAndrey V. Elsukov case SIOCGIFPSRCADDR_IN6:
663fcf59617SAndrey V. Elsukov case SIOCGIFPDSTADDR_IN6:
664fcf59617SAndrey V. Elsukov #endif
665fcf59617SAndrey V. Elsukov if (sc->family == 0) {
666fcf59617SAndrey V. Elsukov error = EADDRNOTAVAIL;
667fcf59617SAndrey V. Elsukov break;
668fcf59617SAndrey V. Elsukov }
669fcf59617SAndrey V. Elsukov saidx = ipsec_getsaidx(sc, IPSEC_DIR_OUTBOUND, sc->family);
670b1d10b49SKonstantin Belousov if (saidx == NULL) {
671b1d10b49SKonstantin Belousov error = ENXIO;
672b1d10b49SKonstantin Belousov break;
673b1d10b49SKonstantin Belousov }
674fcf59617SAndrey V. Elsukov switch (cmd) {
675fcf59617SAndrey V. Elsukov #ifdef INET
676fcf59617SAndrey V. Elsukov case SIOCGIFPSRCADDR:
677fcf59617SAndrey V. Elsukov case SIOCGIFPDSTADDR:
678fcf59617SAndrey V. Elsukov if (saidx->src.sa.sa_family != AF_INET) {
679fcf59617SAndrey V. Elsukov error = EADDRNOTAVAIL;
680fcf59617SAndrey V. Elsukov break;
681fcf59617SAndrey V. Elsukov }
682fcf59617SAndrey V. Elsukov sin = (struct sockaddr_in *)&ifr->ifr_addr;
683fcf59617SAndrey V. Elsukov memset(sin, 0, sizeof(*sin));
684fcf59617SAndrey V. Elsukov sin->sin_family = AF_INET;
685fcf59617SAndrey V. Elsukov sin->sin_len = sizeof(*sin);
686fcf59617SAndrey V. Elsukov break;
687fcf59617SAndrey V. Elsukov #endif
688fcf59617SAndrey V. Elsukov #ifdef INET6
689fcf59617SAndrey V. Elsukov case SIOCGIFPSRCADDR_IN6:
690fcf59617SAndrey V. Elsukov case SIOCGIFPDSTADDR_IN6:
691fcf59617SAndrey V. Elsukov if (saidx->src.sa.sa_family != AF_INET6) {
692fcf59617SAndrey V. Elsukov error = EADDRNOTAVAIL;
693fcf59617SAndrey V. Elsukov break;
694fcf59617SAndrey V. Elsukov }
695fcf59617SAndrey V. Elsukov sin6 = (struct sockaddr_in6 *)
696fcf59617SAndrey V. Elsukov &(((struct in6_ifreq *)data)->ifr_addr);
697fcf59617SAndrey V. Elsukov memset(sin6, 0, sizeof(*sin6));
698fcf59617SAndrey V. Elsukov sin6->sin6_family = AF_INET6;
699fcf59617SAndrey V. Elsukov sin6->sin6_len = sizeof(*sin6);
700fcf59617SAndrey V. Elsukov break;
701fcf59617SAndrey V. Elsukov #endif
702fcf59617SAndrey V. Elsukov default:
703fcf59617SAndrey V. Elsukov error = EAFNOSUPPORT;
704fcf59617SAndrey V. Elsukov }
705fcf59617SAndrey V. Elsukov if (error == 0) {
706fcf59617SAndrey V. Elsukov switch (cmd) {
707fcf59617SAndrey V. Elsukov #ifdef INET
708fcf59617SAndrey V. Elsukov case SIOCGIFPSRCADDR:
709fcf59617SAndrey V. Elsukov sin->sin_addr = saidx->src.sin.sin_addr;
710fcf59617SAndrey V. Elsukov break;
711fcf59617SAndrey V. Elsukov case SIOCGIFPDSTADDR:
712fcf59617SAndrey V. Elsukov sin->sin_addr = saidx->dst.sin.sin_addr;
713fcf59617SAndrey V. Elsukov break;
714fcf59617SAndrey V. Elsukov #endif
715fcf59617SAndrey V. Elsukov #ifdef INET6
716fcf59617SAndrey V. Elsukov case SIOCGIFPSRCADDR_IN6:
717fcf59617SAndrey V. Elsukov sin6->sin6_addr = saidx->src.sin6.sin6_addr;
718fcf59617SAndrey V. Elsukov break;
719fcf59617SAndrey V. Elsukov case SIOCGIFPDSTADDR_IN6:
720fcf59617SAndrey V. Elsukov sin6->sin6_addr = saidx->dst.sin6.sin6_addr;
721fcf59617SAndrey V. Elsukov break;
722fcf59617SAndrey V. Elsukov #endif
723fcf59617SAndrey V. Elsukov }
724fcf59617SAndrey V. Elsukov }
725fcf59617SAndrey V. Elsukov if (error != 0)
726fcf59617SAndrey V. Elsukov break;
727fcf59617SAndrey V. Elsukov switch (cmd) {
728fcf59617SAndrey V. Elsukov #ifdef INET
729fcf59617SAndrey V. Elsukov case SIOCGIFPSRCADDR:
730fcf59617SAndrey V. Elsukov case SIOCGIFPDSTADDR:
731fcf59617SAndrey V. Elsukov error = prison_if(curthread->td_ucred,
732fcf59617SAndrey V. Elsukov (struct sockaddr *)sin);
733fcf59617SAndrey V. Elsukov if (error != 0)
734fcf59617SAndrey V. Elsukov memset(sin, 0, sizeof(*sin));
735fcf59617SAndrey V. Elsukov break;
736fcf59617SAndrey V. Elsukov #endif
737fcf59617SAndrey V. Elsukov #ifdef INET6
738fcf59617SAndrey V. Elsukov case SIOCGIFPSRCADDR_IN6:
739fcf59617SAndrey V. Elsukov case SIOCGIFPDSTADDR_IN6:
740fcf59617SAndrey V. Elsukov error = prison_if(curthread->td_ucred,
741fcf59617SAndrey V. Elsukov (struct sockaddr *)sin6);
742fcf59617SAndrey V. Elsukov if (error == 0)
743fcf59617SAndrey V. Elsukov error = sa6_recoverscope(sin6);
744fcf59617SAndrey V. Elsukov if (error != 0)
745fcf59617SAndrey V. Elsukov memset(sin6, 0, sizeof(*sin6));
746fcf59617SAndrey V. Elsukov #endif
747fcf59617SAndrey V. Elsukov }
748fcf59617SAndrey V. Elsukov break;
749fcf59617SAndrey V. Elsukov case SIOCGTUNFIB:
750fcf59617SAndrey V. Elsukov ifr->ifr_fib = sc->fibnum;
751fcf59617SAndrey V. Elsukov break;
752fcf59617SAndrey V. Elsukov case SIOCSTUNFIB:
753fcf59617SAndrey V. Elsukov if ((error = priv_check(curthread, PRIV_NET_SETIFFIB)) != 0)
754fcf59617SAndrey V. Elsukov break;
755fcf59617SAndrey V. Elsukov if (ifr->ifr_fib >= rt_numfibs)
756fcf59617SAndrey V. Elsukov error = EINVAL;
757fcf59617SAndrey V. Elsukov else
758fcf59617SAndrey V. Elsukov sc->fibnum = ifr->ifr_fib;
759fcf59617SAndrey V. Elsukov break;
760fcf59617SAndrey V. Elsukov case IPSECGREQID:
761fcf59617SAndrey V. Elsukov reqid = sc->reqid;
762541d96aaSBrooks Davis error = copyout(&reqid, ifr_data_get_ptr(ifr), sizeof(reqid));
763fcf59617SAndrey V. Elsukov break;
764fcf59617SAndrey V. Elsukov case IPSECSREQID:
765fcf59617SAndrey V. Elsukov if ((error = priv_check(curthread, PRIV_NET_SETIFCAP)) != 0)
766fcf59617SAndrey V. Elsukov break;
767541d96aaSBrooks Davis error = copyin(ifr_data_get_ptr(ifr), &reqid, sizeof(reqid));
768fcf59617SAndrey V. Elsukov if (error != 0)
769fcf59617SAndrey V. Elsukov break;
7702c87fdf0SAndrey V. Elsukov error = ipsec_set_reqid(sc, reqid);
771fcf59617SAndrey V. Elsukov break;
772fcf59617SAndrey V. Elsukov default:
773fcf59617SAndrey V. Elsukov error = EINVAL;
774fcf59617SAndrey V. Elsukov break;
775fcf59617SAndrey V. Elsukov }
776fcf59617SAndrey V. Elsukov bad:
777fcf59617SAndrey V. Elsukov sx_xunlock(&ipsec_ioctl_sx);
778fcf59617SAndrey V. Elsukov return (error);
779fcf59617SAndrey V. Elsukov }
780fcf59617SAndrey V. Elsukov
781fcf59617SAndrey V. Elsukov /*
7822c87fdf0SAndrey V. Elsukov * Check that ingress address belongs to local host.
7832c87fdf0SAndrey V. Elsukov */
7842c87fdf0SAndrey V. Elsukov static void
ipsec_set_running(struct ipsec_softc * sc)7852c87fdf0SAndrey V. Elsukov ipsec_set_running(struct ipsec_softc *sc)
7862c87fdf0SAndrey V. Elsukov {
7872c87fdf0SAndrey V. Elsukov struct secasindex *saidx;
7882c87fdf0SAndrey V. Elsukov int localip;
7892c87fdf0SAndrey V. Elsukov
7902c87fdf0SAndrey V. Elsukov saidx = ipsec_getsaidx(sc, IPSEC_DIR_OUTBOUND, sc->family);
791b1d10b49SKonstantin Belousov if (saidx == NULL)
792b1d10b49SKonstantin Belousov return;
7932c87fdf0SAndrey V. Elsukov localip = 0;
7942c87fdf0SAndrey V. Elsukov switch (sc->family) {
7952c87fdf0SAndrey V. Elsukov #ifdef INET
7962c87fdf0SAndrey V. Elsukov case AF_INET:
7972c87fdf0SAndrey V. Elsukov localip = in_localip(saidx->src.sin.sin_addr);
7982c87fdf0SAndrey V. Elsukov break;
7992c87fdf0SAndrey V. Elsukov #endif
8002c87fdf0SAndrey V. Elsukov #ifdef INET6
8012c87fdf0SAndrey V. Elsukov case AF_INET6:
8022c87fdf0SAndrey V. Elsukov localip = in6_localip(&saidx->src.sin6.sin6_addr);
8032c87fdf0SAndrey V. Elsukov break;
8042c87fdf0SAndrey V. Elsukov #endif
8052c87fdf0SAndrey V. Elsukov }
8062c87fdf0SAndrey V. Elsukov if (localip != 0)
8072c87fdf0SAndrey V. Elsukov sc->ifp->if_drv_flags |= IFF_DRV_RUNNING;
8082c87fdf0SAndrey V. Elsukov else
8092c87fdf0SAndrey V. Elsukov sc->ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
8102c87fdf0SAndrey V. Elsukov }
8112c87fdf0SAndrey V. Elsukov
8122c87fdf0SAndrey V. Elsukov /*
8132c87fdf0SAndrey V. Elsukov * ifaddr_event handler.
8142c87fdf0SAndrey V. Elsukov * Clear IFF_DRV_RUNNING flag when ingress address disappears to prevent
8152c87fdf0SAndrey V. Elsukov * source address spoofing.
8162c87fdf0SAndrey V. Elsukov */
8172c87fdf0SAndrey V. Elsukov static void
ipsec_srcaddr(void * arg __unused,const struct sockaddr * sa,int event __unused)8182c87fdf0SAndrey V. Elsukov ipsec_srcaddr(void *arg __unused, const struct sockaddr *sa,
8192c87fdf0SAndrey V. Elsukov int event __unused)
8202c87fdf0SAndrey V. Elsukov {
8212c87fdf0SAndrey V. Elsukov struct ipsec_softc *sc;
8222c87fdf0SAndrey V. Elsukov struct secasindex *saidx;
823eac97154SKonstantin Belousov struct ipsec_iflist *iflist;
8242c87fdf0SAndrey V. Elsukov
825221022e1SAndrey V. Elsukov /* Check that VNET is ready */
826221022e1SAndrey V. Elsukov if (V_ipsec_idhtbl == NULL)
827221022e1SAndrey V. Elsukov return;
828221022e1SAndrey V. Elsukov
82997168be8SGleb Smirnoff NET_EPOCH_ASSERT();
830eac97154SKonstantin Belousov iflist = ipsec_srchash(sa);
831eac97154SKonstantin Belousov if (iflist == NULL)
832eac97154SKonstantin Belousov return;
833eac97154SKonstantin Belousov CK_LIST_FOREACH(sc, iflist, srchash) {
8342c87fdf0SAndrey V. Elsukov if (sc->family == 0)
8352c87fdf0SAndrey V. Elsukov continue;
8362c87fdf0SAndrey V. Elsukov saidx = ipsec_getsaidx(sc, IPSEC_DIR_OUTBOUND, sa->sa_family);
8372c87fdf0SAndrey V. Elsukov if (saidx == NULL ||
8382c87fdf0SAndrey V. Elsukov key_sockaddrcmp(&saidx->src.sa, sa, 0) != 0)
8392c87fdf0SAndrey V. Elsukov continue;
8402c87fdf0SAndrey V. Elsukov ipsec_set_running(sc);
8412c87fdf0SAndrey V. Elsukov }
8422c87fdf0SAndrey V. Elsukov }
8432c87fdf0SAndrey V. Elsukov
8442c87fdf0SAndrey V. Elsukov /*
845fcf59617SAndrey V. Elsukov * Allocate new private security policies for tunneling interface.
846fcf59617SAndrey V. Elsukov * Each tunneling interface has following security policies for
847fcf59617SAndrey V. Elsukov * both AF:
848fcf59617SAndrey V. Elsukov * 0.0.0.0/0[any] 0.0.0.0/0[any] -P in \
849fcf59617SAndrey V. Elsukov * ipsec esp/tunnel/RemoteIP-LocalIP/unique:reqid
850fcf59617SAndrey V. Elsukov * 0.0.0.0/0[any] 0.0.0.0/0[any] -P out \
851fcf59617SAndrey V. Elsukov * ipsec esp/tunnel/LocalIP-RemoteIP/unique:reqid
852fcf59617SAndrey V. Elsukov */
853fcf59617SAndrey V. Elsukov static int
ipsec_newpolicies(struct ipsec_softc * sc,struct secpolicy * sp[IPSEC_SPCOUNT],const struct sockaddr * src,const struct sockaddr * dst,uint32_t reqid)85422986c67SAndrey V. Elsukov ipsec_newpolicies(struct ipsec_softc *sc, struct secpolicy *sp[IPSEC_SPCOUNT],
855fcf59617SAndrey V. Elsukov const struct sockaddr *src, const struct sockaddr *dst, uint32_t reqid)
856fcf59617SAndrey V. Elsukov {
857fcf59617SAndrey V. Elsukov struct ipsecrequest *isr;
858fcf59617SAndrey V. Elsukov int i;
859fcf59617SAndrey V. Elsukov
860fcf59617SAndrey V. Elsukov memset(sp, 0, sizeof(struct secpolicy *) * IPSEC_SPCOUNT);
861fcf59617SAndrey V. Elsukov for (i = 0; i < IPSEC_SPCOUNT; i++) {
862fcf59617SAndrey V. Elsukov if ((sp[i] = key_newsp()) == NULL)
863fcf59617SAndrey V. Elsukov goto fail;
864fcf59617SAndrey V. Elsukov if ((isr = ipsec_newisr()) == NULL)
865fcf59617SAndrey V. Elsukov goto fail;
866fcf59617SAndrey V. Elsukov
867fcf59617SAndrey V. Elsukov sp[i]->policy = IPSEC_POLICY_IPSEC;
868fcf59617SAndrey V. Elsukov sp[i]->state = IPSEC_SPSTATE_DEAD;
869fcf59617SAndrey V. Elsukov sp[i]->req[sp[i]->tcount++] = isr;
870fcf59617SAndrey V. Elsukov sp[i]->created = time_second;
87122986c67SAndrey V. Elsukov /* Use priority field to store if_index */
87222986c67SAndrey V. Elsukov sp[i]->priority = sc->ifp->if_index;
873fcf59617SAndrey V. Elsukov isr->level = IPSEC_LEVEL_UNIQUE;
874fcf59617SAndrey V. Elsukov isr->saidx.proto = IPPROTO_ESP;
875fcf59617SAndrey V. Elsukov isr->saidx.mode = IPSEC_MODE_TUNNEL;
876fcf59617SAndrey V. Elsukov isr->saidx.reqid = reqid;
877fcf59617SAndrey V. Elsukov if (i % 2 == 0) {
878fcf59617SAndrey V. Elsukov sp[i]->spidx.dir = IPSEC_DIR_INBOUND;
879fcf59617SAndrey V. Elsukov bcopy(src, &isr->saidx.dst, src->sa_len);
880fcf59617SAndrey V. Elsukov bcopy(dst, &isr->saidx.src, dst->sa_len);
881fcf59617SAndrey V. Elsukov } else {
882fcf59617SAndrey V. Elsukov sp[i]->spidx.dir = IPSEC_DIR_OUTBOUND;
883fcf59617SAndrey V. Elsukov bcopy(src, &isr->saidx.src, src->sa_len);
884fcf59617SAndrey V. Elsukov bcopy(dst, &isr->saidx.dst, dst->sa_len);
885fcf59617SAndrey V. Elsukov }
886fcf59617SAndrey V. Elsukov sp[i]->spidx.ul_proto = IPSEC_ULPROTO_ANY;
887fcf59617SAndrey V. Elsukov #ifdef INET
888fcf59617SAndrey V. Elsukov if (i < 2) {
889fcf59617SAndrey V. Elsukov sp[i]->spidx.src.sa.sa_family =
890fcf59617SAndrey V. Elsukov sp[i]->spidx.dst.sa.sa_family = AF_INET;
891fcf59617SAndrey V. Elsukov sp[i]->spidx.src.sa.sa_len =
892fcf59617SAndrey V. Elsukov sp[i]->spidx.dst.sa.sa_len =
893fcf59617SAndrey V. Elsukov sizeof(struct sockaddr_in);
894fcf59617SAndrey V. Elsukov continue;
895fcf59617SAndrey V. Elsukov }
896fcf59617SAndrey V. Elsukov #endif
897fcf59617SAndrey V. Elsukov #ifdef INET6
898fcf59617SAndrey V. Elsukov sp[i]->spidx.src.sa.sa_family =
899fcf59617SAndrey V. Elsukov sp[i]->spidx.dst.sa.sa_family = AF_INET6;
900fcf59617SAndrey V. Elsukov sp[i]->spidx.src.sa.sa_len =
901fcf59617SAndrey V. Elsukov sp[i]->spidx.dst.sa.sa_len = sizeof(struct sockaddr_in6);
902fcf59617SAndrey V. Elsukov #endif
903fcf59617SAndrey V. Elsukov }
904fcf59617SAndrey V. Elsukov return (0);
905fcf59617SAndrey V. Elsukov fail:
9067539b04eSKonstantin Belousov for (i = 0; i < IPSEC_SPCOUNT; i++) {
9077539b04eSKonstantin Belousov if (sp[i] != NULL)
908fcf59617SAndrey V. Elsukov key_freesp(&sp[i]);
9097539b04eSKonstantin Belousov }
910fcf59617SAndrey V. Elsukov return (ENOMEM);
911fcf59617SAndrey V. Elsukov }
912fcf59617SAndrey V. Elsukov
913fcf59617SAndrey V. Elsukov static int
ipsec_check_reqid(uint32_t reqid)914fcf59617SAndrey V. Elsukov ipsec_check_reqid(uint32_t reqid)
915fcf59617SAndrey V. Elsukov {
916fcf59617SAndrey V. Elsukov struct ipsec_softc *sc;
917fcf59617SAndrey V. Elsukov
9182c87fdf0SAndrey V. Elsukov sx_assert(&ipsec_ioctl_sx, SA_XLOCKED);
9192c87fdf0SAndrey V. Elsukov CK_LIST_FOREACH(sc, ipsec_idhash(reqid), idhash) {
920fcf59617SAndrey V. Elsukov if (sc->reqid == reqid)
921fcf59617SAndrey V. Elsukov return (EEXIST);
922fcf59617SAndrey V. Elsukov }
923fcf59617SAndrey V. Elsukov return (0);
924fcf59617SAndrey V. Elsukov }
925fcf59617SAndrey V. Elsukov
926fcf59617SAndrey V. Elsukov /*
927fcf59617SAndrey V. Elsukov * We use key_newreqid() to automatically obtain unique reqid.
928fcf59617SAndrey V. Elsukov * Then we check that given id is unique, i.e. it is not used by
929fcf59617SAndrey V. Elsukov * another if_ipsec(4) interface. This macro limits the number of
930fcf59617SAndrey V. Elsukov * tries to get unique id.
931fcf59617SAndrey V. Elsukov */
932fcf59617SAndrey V. Elsukov #define IPSEC_REQID_TRYCNT 64
933fcf59617SAndrey V. Elsukov static int
ipsec_init_reqid(struct ipsec_softc * sc)934fcf59617SAndrey V. Elsukov ipsec_init_reqid(struct ipsec_softc *sc)
935fcf59617SAndrey V. Elsukov {
936fcf59617SAndrey V. Elsukov uint32_t reqid;
937fcf59617SAndrey V. Elsukov int trycount;
938fcf59617SAndrey V. Elsukov
9392c87fdf0SAndrey V. Elsukov sx_assert(&ipsec_ioctl_sx, SA_XLOCKED);
940fcf59617SAndrey V. Elsukov if (sc->reqid != 0) /* already initialized */
941fcf59617SAndrey V. Elsukov return (0);
942fcf59617SAndrey V. Elsukov
943fcf59617SAndrey V. Elsukov trycount = IPSEC_REQID_TRYCNT;
944fcf59617SAndrey V. Elsukov while (--trycount > 0) {
945fcf59617SAndrey V. Elsukov reqid = key_newreqid();
946fcf59617SAndrey V. Elsukov if (ipsec_check_reqid(reqid) == 0)
947fcf59617SAndrey V. Elsukov break;
948fcf59617SAndrey V. Elsukov }
949fcf59617SAndrey V. Elsukov if (trycount == 0)
950fcf59617SAndrey V. Elsukov return (EEXIST);
951fcf59617SAndrey V. Elsukov sc->reqid = reqid;
9522c87fdf0SAndrey V. Elsukov CK_LIST_INSERT_HEAD(ipsec_idhash(reqid), sc, idhash);
953fcf59617SAndrey V. Elsukov return (0);
954fcf59617SAndrey V. Elsukov }
955fcf59617SAndrey V. Elsukov
956fcf59617SAndrey V. Elsukov /*
957fcf59617SAndrey V. Elsukov * Set or update reqid for given tunneling interface.
958fcf59617SAndrey V. Elsukov * When specified reqid is zero, generate new one.
959fcf59617SAndrey V. Elsukov * We are protected by ioctl_sx lock from concurrent id generation.
960fcf59617SAndrey V. Elsukov * Also softc would not disappear while we hold ioctl_sx lock.
961fcf59617SAndrey V. Elsukov */
962fcf59617SAndrey V. Elsukov static int
ipsec_set_reqid(struct ipsec_softc * sc,uint32_t reqid)9632c87fdf0SAndrey V. Elsukov ipsec_set_reqid(struct ipsec_softc *sc, uint32_t reqid)
964fcf59617SAndrey V. Elsukov {
965fcf59617SAndrey V. Elsukov struct secasindex *saidx;
966fcf59617SAndrey V. Elsukov
967fcf59617SAndrey V. Elsukov sx_assert(&ipsec_ioctl_sx, SA_XLOCKED);
968fcf59617SAndrey V. Elsukov
969fcf59617SAndrey V. Elsukov if (sc->reqid == reqid && reqid != 0)
970fcf59617SAndrey V. Elsukov return (0);
971fcf59617SAndrey V. Elsukov
972fcf59617SAndrey V. Elsukov if (reqid != 0) {
973fcf59617SAndrey V. Elsukov /* Check that specified reqid doesn't exist */
9742c87fdf0SAndrey V. Elsukov if (ipsec_check_reqid(reqid) != 0)
975fcf59617SAndrey V. Elsukov return (EEXIST);
9762c87fdf0SAndrey V. Elsukov if (sc->reqid != 0) {
9772c87fdf0SAndrey V. Elsukov CK_LIST_REMOVE(sc, idhash);
9782c87fdf0SAndrey V. Elsukov IPSEC_WAIT();
979fcf59617SAndrey V. Elsukov }
980fcf59617SAndrey V. Elsukov sc->reqid = reqid;
9812c87fdf0SAndrey V. Elsukov CK_LIST_INSERT_HEAD(ipsec_idhash(reqid), sc, idhash);
982fcf59617SAndrey V. Elsukov } else {
983fcf59617SAndrey V. Elsukov /* Generate new reqid */
9842c87fdf0SAndrey V. Elsukov if (ipsec_init_reqid(sc) != 0)
985fcf59617SAndrey V. Elsukov return (EEXIST);
986fcf59617SAndrey V. Elsukov }
987fcf59617SAndrey V. Elsukov
988fcf59617SAndrey V. Elsukov /* Tunnel isn't fully configured, just return. */
989fcf59617SAndrey V. Elsukov if (sc->family == 0)
990fcf59617SAndrey V. Elsukov return (0);
991fcf59617SAndrey V. Elsukov
992fcf59617SAndrey V. Elsukov saidx = ipsec_getsaidx(sc, IPSEC_DIR_OUTBOUND, sc->family);
993fcf59617SAndrey V. Elsukov KASSERT(saidx != NULL,
994fcf59617SAndrey V. Elsukov ("saidx is NULL, but family is %d", sc->family));
995fcf59617SAndrey V. Elsukov return (ipsec_set_tunnel(sc, &saidx->src.sa, &saidx->dst.sa,
996fcf59617SAndrey V. Elsukov sc->reqid));
997fcf59617SAndrey V. Elsukov }
998fcf59617SAndrey V. Elsukov
999fcf59617SAndrey V. Elsukov /*
1000fcf59617SAndrey V. Elsukov * Set tunnel endpoints addresses.
1001fcf59617SAndrey V. Elsukov */
1002fcf59617SAndrey V. Elsukov static int
ipsec_set_addresses(struct ifnet * ifp,struct sockaddr * src,struct sockaddr * dst)1003fcf59617SAndrey V. Elsukov ipsec_set_addresses(struct ifnet *ifp, struct sockaddr *src,
1004fcf59617SAndrey V. Elsukov struct sockaddr *dst)
1005fcf59617SAndrey V. Elsukov {
1006ad43bf34SAndrey V. Elsukov struct ipsec_softc *sc;
1007fcf59617SAndrey V. Elsukov struct secasindex *saidx;
1008fcf59617SAndrey V. Elsukov
1009fcf59617SAndrey V. Elsukov sx_assert(&ipsec_ioctl_sx, SA_XLOCKED);
1010fcf59617SAndrey V. Elsukov
1011fcf59617SAndrey V. Elsukov sc = ifp->if_softc;
1012fcf59617SAndrey V. Elsukov if (sc->family != 0) {
1013fcf59617SAndrey V. Elsukov saidx = ipsec_getsaidx(sc, IPSEC_DIR_OUTBOUND,
1014fcf59617SAndrey V. Elsukov src->sa_family);
1015fcf59617SAndrey V. Elsukov if (saidx != NULL && saidx->reqid == sc->reqid &&
1016fcf59617SAndrey V. Elsukov key_sockaddrcmp(&saidx->src.sa, src, 0) == 0 &&
1017fcf59617SAndrey V. Elsukov key_sockaddrcmp(&saidx->dst.sa, dst, 0) == 0)
1018fcf59617SAndrey V. Elsukov return (0); /* Nothing has been changed. */
1019fcf59617SAndrey V. Elsukov }
1020fcf59617SAndrey V. Elsukov /* If reqid is not set, generate new one. */
10212c87fdf0SAndrey V. Elsukov if (ipsec_init_reqid(sc) != 0)
1022fcf59617SAndrey V. Elsukov return (EEXIST);
1023fcf59617SAndrey V. Elsukov return (ipsec_set_tunnel(sc, src, dst, sc->reqid));
1024fcf59617SAndrey V. Elsukov }
1025fcf59617SAndrey V. Elsukov
1026fcf59617SAndrey V. Elsukov static int
ipsec_set_tunnel(struct ipsec_softc * sc,struct sockaddr * src,struct sockaddr * dst,uint32_t reqid)1027fcf59617SAndrey V. Elsukov ipsec_set_tunnel(struct ipsec_softc *sc, struct sockaddr *src,
1028fcf59617SAndrey V. Elsukov struct sockaddr *dst, uint32_t reqid)
1029fcf59617SAndrey V. Elsukov {
1030c8ee75f2SGleb Smirnoff struct epoch_tracker et;
1031eac97154SKonstantin Belousov struct ipsec_iflist *iflist;
1032fcf59617SAndrey V. Elsukov struct secpolicy *sp[IPSEC_SPCOUNT];
10332c87fdf0SAndrey V. Elsukov int i;
1034fcf59617SAndrey V. Elsukov
1035fcf59617SAndrey V. Elsukov sx_assert(&ipsec_ioctl_sx, SA_XLOCKED);
1036fcf59617SAndrey V. Elsukov
1037fcf59617SAndrey V. Elsukov /* Allocate SP with new addresses. */
1038eac97154SKonstantin Belousov iflist = ipsec_srchash(src);
1039eac97154SKonstantin Belousov if (iflist == NULL) {
1040eac97154SKonstantin Belousov sc->ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
1041eac97154SKonstantin Belousov return (EAFNOSUPPORT);
1042eac97154SKonstantin Belousov }
104322986c67SAndrey V. Elsukov if (ipsec_newpolicies(sc, sp, src, dst, reqid) == 0) {
1044fcf59617SAndrey V. Elsukov /* Add new policies to SPDB */
1045fcf59617SAndrey V. Elsukov if (key_register_ifnet(sp, IPSEC_SPCOUNT) != 0) {
1046fcf59617SAndrey V. Elsukov for (i = 0; i < IPSEC_SPCOUNT; i++)
1047fcf59617SAndrey V. Elsukov key_freesp(&sp[i]);
1048fcf59617SAndrey V. Elsukov return (EAGAIN);
1049fcf59617SAndrey V. Elsukov }
10502c87fdf0SAndrey V. Elsukov if (sc->family != 0)
10512c87fdf0SAndrey V. Elsukov ipsec_delete_tunnel(sc);
10522c87fdf0SAndrey V. Elsukov for (i = 0; i < IPSEC_SPCOUNT; i++)
1053fcf59617SAndrey V. Elsukov sc->sp[i] = sp[i];
1054fcf59617SAndrey V. Elsukov sc->family = src->sa_family;
1055eac97154SKonstantin Belousov CK_LIST_INSERT_HEAD(iflist, sc, srchash);
1056fcf59617SAndrey V. Elsukov } else {
1057fcf59617SAndrey V. Elsukov sc->ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
1058fcf59617SAndrey V. Elsukov return (ENOMEM);
1059fcf59617SAndrey V. Elsukov }
1060c8ee75f2SGleb Smirnoff NET_EPOCH_ENTER(et);
10612c87fdf0SAndrey V. Elsukov ipsec_set_running(sc);
1062c8ee75f2SGleb Smirnoff NET_EPOCH_EXIT(et);
1063fcf59617SAndrey V. Elsukov return (0);
1064fcf59617SAndrey V. Elsukov }
1065fcf59617SAndrey V. Elsukov
1066fcf59617SAndrey V. Elsukov static void
ipsec_delete_tunnel(struct ipsec_softc * sc)10672c87fdf0SAndrey V. Elsukov ipsec_delete_tunnel(struct ipsec_softc *sc)
1068fcf59617SAndrey V. Elsukov {
1069fcf59617SAndrey V. Elsukov int i;
1070fcf59617SAndrey V. Elsukov
1071fcf59617SAndrey V. Elsukov sx_assert(&ipsec_ioctl_sx, SA_XLOCKED);
1072fcf59617SAndrey V. Elsukov
10732c87fdf0SAndrey V. Elsukov sc->ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
1074fcf59617SAndrey V. Elsukov if (sc->family != 0) {
10752c87fdf0SAndrey V. Elsukov CK_LIST_REMOVE(sc, srchash);
1076e6b383b2SAndrey V. Elsukov sc->family = 0;
10772c87fdf0SAndrey V. Elsukov /*
10782c87fdf0SAndrey V. Elsukov * Make sure that ipsec_if_input() will not do access
10792c87fdf0SAndrey V. Elsukov * to softc's policies.
10802c87fdf0SAndrey V. Elsukov */
10812c87fdf0SAndrey V. Elsukov IPSEC_WAIT();
10822c87fdf0SAndrey V. Elsukov
10832c87fdf0SAndrey V. Elsukov key_unregister_ifnet(sc->sp, IPSEC_SPCOUNT);
1084fcf59617SAndrey V. Elsukov for (i = 0; i < IPSEC_SPCOUNT; i++)
10852c87fdf0SAndrey V. Elsukov key_freesp(&sc->sp[i]);
1086fcf59617SAndrey V. Elsukov }
1087fcf59617SAndrey V. Elsukov }
1088