1313330Sae/*- 2313330Sae * Copyright (c) 2016 Yandex LLC 3313330Sae * Copyright (c) 2016 Andrey V. Elsukov <ae@FreeBSD.org> 4313330Sae * All rights reserved. 5313330Sae * 6313330Sae * Redistribution and use in source and binary forms, with or without 7313330Sae * modification, are permitted provided that the following conditions 8313330Sae * are met: 9313330Sae * 10313330Sae * 1. Redistributions of source code must retain the above copyright 11313330Sae * notice, this list of conditions and the following disclaimer. 12313330Sae * 2. Redistributions in binary form must reproduce the above copyright 13313330Sae * notice, this list of conditions and the following disclaimer in the 14313330Sae * documentation and/or other materials provided with the distribution. 15313330Sae * 16313330Sae * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17313330Sae * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18313330Sae * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19313330Sae * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20313330Sae * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21313330Sae * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22313330Sae * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23313330Sae * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24313330Sae * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25313330Sae * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26313330Sae */ 27313330Sae 28313330Sae#include <sys/cdefs.h> 29313330Sae__FBSDID("$FreeBSD: stable/11/sys/net/if_ipsec.c 365277 2020-09-02 20:36:33Z jhb $"); 30313330Sae 31313330Sae#include "opt_inet.h" 32313330Sae#include "opt_inet6.h" 33365277Sjhb#include "opt_ipsec.h" 34313330Sae 35313330Sae#include <sys/param.h> 36313330Sae#include <sys/systm.h> 37313330Sae#include <sys/kernel.h> 38313330Sae#include <sys/fnv_hash.h> 39313330Sae#include <sys/jail.h> 40313330Sae#include <sys/lock.h> 41313330Sae#include <sys/malloc.h> 42313330Sae#include <sys/mbuf.h> 43313330Sae#include <sys/module.h> 44313330Sae#include <sys/rmlock.h> 45313330Sae#include <sys/socket.h> 46313330Sae#include <sys/sockio.h> 47313330Sae#include <sys/sx.h> 48313330Sae#include <sys/errno.h> 49313330Sae#include <sys/sysctl.h> 50313330Sae#include <sys/priv.h> 51313330Sae#include <sys/proc.h> 52313330Sae#include <sys/conf.h> 53313330Sae 54313330Sae#include <net/if.h> 55313330Sae#include <net/if_var.h> 56313330Sae#include <net/if_clone.h> 57313330Sae#include <net/if_types.h> 58313330Sae#include <net/bpf.h> 59313330Sae#include <net/route.h> 60313330Sae#include <net/vnet.h> 61313330Sae 62313330Sae#include <netinet/in.h> 63313330Sae#include <netinet/in_var.h> 64313330Sae#include <netinet/ip.h> 65313330Sae 66313330Sae#include <netinet/ip6.h> 67313330Sae#include <netinet6/in6_var.h> 68313330Sae#include <netinet6/scope6_var.h> 69313330Sae 70313330Sae#include <netipsec/ipsec.h> 71313330Sae#ifdef INET6 72313330Sae#include <netipsec/ipsec6.h> 73313330Sae#endif 74313330Sae 75313330Sae#include <net/if_ipsec.h> 76313330Sae#include <netipsec/key.h> 77313330Sae 78313330Sae#include <security/mac/mac_framework.h> 79313330Sae 80313330Saestatic MALLOC_DEFINE(M_IPSEC, "ipsec", "IPsec Virtual Tunnel Interface"); 81313330Saestatic const char ipsecname[] = "ipsec"; 82313330Sae 83313330Sae#if defined(INET) && defined(INET6) 84313330Sae#define IPSEC_SPCOUNT 4 85313330Sae#else 86313330Sae#define IPSEC_SPCOUNT 2 87313330Sae#endif 88313330Sae 89313330Saestruct ipsec_softc { 90313330Sae struct ifnet *ifp; 91313330Sae 92313330Sae struct rmlock lock; 93313330Sae struct secpolicy *sp[IPSEC_SPCOUNT]; 94313330Sae 95313330Sae uint32_t reqid; 96313330Sae u_int family; 97313330Sae u_int fibnum; 98313330Sae LIST_ENTRY(ipsec_softc) chain; 99313330Sae LIST_ENTRY(ipsec_softc) hash; 100313330Sae}; 101313330Sae 102313330Sae#define IPSEC_LOCK_INIT(sc) rm_init(&(sc)->lock, "if_ipsec softc") 103313330Sae#define IPSEC_LOCK_DESTROY(sc) rm_destroy(&(sc)->lock) 104313330Sae#define IPSEC_RLOCK_TRACKER struct rm_priotracker ipsec_tracker 105313330Sae#define IPSEC_RLOCK(sc) rm_rlock(&(sc)->lock, &ipsec_tracker) 106313330Sae#define IPSEC_RUNLOCK(sc) rm_runlock(&(sc)->lock, &ipsec_tracker) 107313330Sae#define IPSEC_RLOCK_ASSERT(sc) rm_assert(&(sc)->lock, RA_RLOCKED) 108313330Sae#define IPSEC_WLOCK(sc) rm_wlock(&(sc)->lock) 109313330Sae#define IPSEC_WUNLOCK(sc) rm_wunlock(&(sc)->lock) 110313330Sae#define IPSEC_WLOCK_ASSERT(sc) rm_assert(&(sc)->lock, RA_WLOCKED) 111313330Sae 112313330Saestatic struct rmlock ipsec_sc_lock; 113313330SaeRM_SYSINIT(ipsec_sc_lock, &ipsec_sc_lock, "if_ipsec softc list"); 114313330Sae 115313330Sae#define IPSEC_SC_RLOCK_TRACKER struct rm_priotracker ipsec_sc_tracker 116313330Sae#define IPSEC_SC_RLOCK() rm_rlock(&ipsec_sc_lock, &ipsec_sc_tracker) 117313330Sae#define IPSEC_SC_RUNLOCK() rm_runlock(&ipsec_sc_lock, &ipsec_sc_tracker) 118313330Sae#define IPSEC_SC_RLOCK_ASSERT() rm_assert(&ipsec_sc_lock, RA_RLOCKED) 119313330Sae#define IPSEC_SC_WLOCK() rm_wlock(&ipsec_sc_lock) 120313330Sae#define IPSEC_SC_WUNLOCK() rm_wunlock(&ipsec_sc_lock) 121313330Sae#define IPSEC_SC_WLOCK_ASSERT() rm_assert(&ipsec_sc_lock, RA_WLOCKED) 122313330Sae 123313330SaeLIST_HEAD(ipsec_iflist, ipsec_softc); 124313330Saestatic VNET_DEFINE(struct ipsec_iflist, ipsec_sc_list); 125313330Saestatic VNET_DEFINE(struct ipsec_iflist *, ipsec_sc_htbl); 126313330Saestatic VNET_DEFINE(u_long, ipsec_sc_hmask); 127313330Sae#define V_ipsec_sc_list VNET(ipsec_sc_list) 128313330Sae#define V_ipsec_sc_htbl VNET(ipsec_sc_htbl) 129313330Sae#define V_ipsec_sc_hmask VNET(ipsec_sc_hmask) 130313330Sae 131313330Saestatic uint32_t 132313330Saeipsec_hash(uint32_t id) 133313330Sae{ 134313330Sae 135313330Sae return (fnv_32_buf(&id, sizeof(id), FNV1_32_INIT)); 136313330Sae} 137313330Sae 138313330Sae#define SCHASH_NHASH_LOG2 5 139313330Sae#define SCHASH_NHASH (1 << SCHASH_NHASH_LOG2) 140313330Sae#define SCHASH_HASHVAL(id) (ipsec_hash((id)) & V_ipsec_sc_hmask) 141313330Sae#define SCHASH_HASH(id) &V_ipsec_sc_htbl[SCHASH_HASHVAL(id)] 142313330Sae 143313330Sae/* 144313330Sae * ipsec_ioctl_sx protects from concurrent ioctls. 145313330Sae */ 146313330Saestatic struct sx ipsec_ioctl_sx; 147313330SaeSX_SYSINIT(ipsec_ioctl_sx, &ipsec_ioctl_sx, "ipsec_ioctl"); 148313330Sae 149313330Saestatic int ipsec_init_reqid(struct ipsec_softc *); 150313330Saestatic int ipsec_set_tunnel(struct ipsec_softc *, struct sockaddr *, 151313330Sae struct sockaddr *, uint32_t); 152313330Saestatic void ipsec_delete_tunnel(struct ifnet *, int); 153313330Sae 154313330Saestatic int ipsec_set_addresses(struct ifnet *, struct sockaddr *, 155313330Sae struct sockaddr *); 156313330Saestatic int ipsec_set_reqid(struct ifnet *, uint32_t); 157313330Sae 158313330Saestatic int ipsec_ioctl(struct ifnet *, u_long, caddr_t); 159313330Saestatic int ipsec_transmit(struct ifnet *, struct mbuf *); 160313330Saestatic int ipsec_output(struct ifnet *, struct mbuf *, 161313330Sae const struct sockaddr *, struct route *); 162313330Saestatic void ipsec_qflush(struct ifnet *); 163313330Saestatic int ipsec_clone_create(struct if_clone *, int, caddr_t); 164313330Saestatic void ipsec_clone_destroy(struct ifnet *); 165313330Sae 166313330Saestatic VNET_DEFINE(struct if_clone *, ipsec_cloner); 167313330Sae#define V_ipsec_cloner VNET(ipsec_cloner) 168313330Sae 169313330Saestatic int 170313330Saeipsec_clone_create(struct if_clone *ifc, int unit, caddr_t params) 171313330Sae{ 172313330Sae struct ipsec_softc *sc; 173313330Sae struct ifnet *ifp; 174313330Sae 175313330Sae sc = malloc(sizeof(*sc), M_IPSEC, M_WAITOK | M_ZERO); 176313330Sae sc->fibnum = curthread->td_proc->p_fibnum; 177313330Sae sc->ifp = ifp = if_alloc(IFT_TUNNEL); 178313330Sae IPSEC_LOCK_INIT(sc); 179313330Sae ifp->if_softc = sc; 180313330Sae if_initname(ifp, ipsecname, unit); 181313330Sae 182313330Sae ifp->if_addrlen = 0; 183313330Sae ifp->if_mtu = IPSEC_MTU; 184313330Sae ifp->if_flags = IFF_POINTOPOINT | IFF_MULTICAST; 185313330Sae ifp->if_ioctl = ipsec_ioctl; 186313330Sae ifp->if_transmit = ipsec_transmit; 187313330Sae ifp->if_qflush = ipsec_qflush; 188313330Sae ifp->if_output = ipsec_output; 189313330Sae if_attach(ifp); 190313330Sae bpfattach(ifp, DLT_NULL, sizeof(uint32_t)); 191313330Sae 192313330Sae IPSEC_SC_WLOCK(); 193313330Sae LIST_INSERT_HEAD(&V_ipsec_sc_list, sc, chain); 194313330Sae IPSEC_SC_WUNLOCK(); 195313330Sae return (0); 196313330Sae} 197313330Sae 198313330Saestatic void 199313330Saeipsec_clone_destroy(struct ifnet *ifp) 200313330Sae{ 201313330Sae struct ipsec_softc *sc; 202313330Sae 203313330Sae sx_xlock(&ipsec_ioctl_sx); 204313330Sae sc = ifp->if_softc; 205313330Sae 206313330Sae IPSEC_SC_WLOCK(); 207313330Sae ipsec_delete_tunnel(ifp, 1); 208313330Sae LIST_REMOVE(sc, chain); 209313330Sae IPSEC_SC_WUNLOCK(); 210313330Sae 211313330Sae bpfdetach(ifp); 212313330Sae if_detach(ifp); 213313330Sae ifp->if_softc = NULL; 214313330Sae sx_xunlock(&ipsec_ioctl_sx); 215313330Sae 216313330Sae if_free(ifp); 217313330Sae IPSEC_LOCK_DESTROY(sc); 218313330Sae free(sc, M_IPSEC); 219313330Sae} 220313330Sae 221313330Saestatic void 222313330Saevnet_ipsec_init(const void *unused __unused) 223313330Sae{ 224313330Sae 225313330Sae LIST_INIT(&V_ipsec_sc_list); 226313330Sae V_ipsec_sc_htbl = hashinit(SCHASH_NHASH, M_IPSEC, &V_ipsec_sc_hmask); 227313330Sae V_ipsec_cloner = if_clone_simple(ipsecname, ipsec_clone_create, 228313330Sae ipsec_clone_destroy, 0); 229313330Sae} 230313330SaeVNET_SYSINIT(vnet_ipsec_init, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY, 231313330Sae vnet_ipsec_init, NULL); 232313330Sae 233313330Saestatic void 234313330Saevnet_ipsec_uninit(const void *unused __unused) 235313330Sae{ 236313330Sae 237313330Sae if_clone_detach(V_ipsec_cloner); 238313330Sae hashdestroy(V_ipsec_sc_htbl, M_IPSEC, V_ipsec_sc_hmask); 239313330Sae} 240313330SaeVNET_SYSUNINIT(vnet_ipsec_uninit, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY, 241313330Sae vnet_ipsec_uninit, NULL); 242313330Sae 243313330Saestatic struct secpolicy * 244313330Saeipsec_getpolicy(struct ipsec_softc *sc, int dir, sa_family_t af) 245313330Sae{ 246313330Sae 247313330Sae switch (af) { 248313330Sae#ifdef INET 249313330Sae case AF_INET: 250313330Sae return (sc->sp[(dir == IPSEC_DIR_INBOUND ? 0: 1)]); 251313330Sae#endif 252313330Sae#ifdef INET6 253313330Sae case AF_INET6: 254313330Sae return (sc->sp[(dir == IPSEC_DIR_INBOUND ? 0: 1) 255313330Sae#ifdef INET 256313330Sae + 2 257313330Sae#endif 258313330Sae ]); 259313330Sae#endif 260313330Sae } 261313330Sae return (NULL); 262313330Sae} 263313330Sae 264313330Saestatic struct secasindex * 265313330Saeipsec_getsaidx(struct ipsec_softc *sc, int dir, sa_family_t af) 266313330Sae{ 267313330Sae struct secpolicy *sp; 268313330Sae 269313330Sae sp = ipsec_getpolicy(sc, dir, af); 270313330Sae if (sp == NULL) 271313330Sae return (NULL); 272313330Sae return (&sp->req[0]->saidx); 273313330Sae} 274313330Sae 275313330Saestatic int 276313330Saeipsec_transmit(struct ifnet *ifp, struct mbuf *m) 277313330Sae{ 278313330Sae IPSEC_RLOCK_TRACKER; 279313330Sae struct ipsec_softc *sc; 280313330Sae struct secpolicy *sp; 281313330Sae struct ip *ip; 282313330Sae uint32_t af; 283313330Sae int error; 284313330Sae 285313330Sae#ifdef MAC 286313330Sae error = mac_ifnet_check_transmit(ifp, m); 287313330Sae if (error) { 288313330Sae m_freem(m); 289313330Sae goto err; 290313330Sae } 291313330Sae#endif 292313330Sae error = ENETDOWN; 293313330Sae sc = ifp->if_softc; 294313330Sae if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || 295313330Sae (ifp->if_flags & IFF_MONITOR) != 0 || 296313330Sae (ifp->if_flags & IFF_UP) == 0) { 297313330Sae m_freem(m); 298313330Sae goto err; 299313330Sae } 300313330Sae 301313330Sae /* Determine address family to correctly handle packet in BPF */ 302313330Sae ip = mtod(m, struct ip *); 303313330Sae switch (ip->ip_v) { 304313330Sae#ifdef INET 305313330Sae case IPVERSION: 306313330Sae af = AF_INET; 307313330Sae break; 308313330Sae#endif 309313330Sae#ifdef INET6 310313330Sae case (IPV6_VERSION >> 4): 311313330Sae af = AF_INET6; 312313330Sae break; 313313330Sae#endif 314313330Sae default: 315313330Sae error = EAFNOSUPPORT; 316313330Sae m_freem(m); 317313330Sae goto err; 318313330Sae } 319313330Sae 320313330Sae /* 321313330Sae * Loop prevention. 322313330Sae * XXX: for now just check presence of IPSEC_OUT_DONE mbuf tag. 323313330Sae * We can read full chain and compare destination address, 324313330Sae * proto and mode from xform_history with values from softc. 325313330Sae */ 326313330Sae if (m_tag_find(m, PACKET_TAG_IPSEC_OUT_DONE, NULL) != NULL) { 327313330Sae m_freem(m); 328313330Sae goto err; 329313330Sae } 330313330Sae 331313330Sae IPSEC_RLOCK(sc); 332313330Sae if (sc->family == 0) { 333313330Sae IPSEC_RUNLOCK(sc); 334313330Sae m_freem(m); 335313330Sae goto err; 336313330Sae } 337313330Sae sp = ipsec_getpolicy(sc, IPSEC_DIR_OUTBOUND, af); 338313330Sae key_addref(sp); 339313330Sae M_SETFIB(m, sc->fibnum); 340313330Sae IPSEC_RUNLOCK(sc); 341313330Sae 342313330Sae BPF_MTAP2(ifp, &af, sizeof(af), m); 343313330Sae if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); 344313330Sae if_inc_counter(ifp, IFCOUNTER_OBYTES, m->m_pkthdr.len); 345313330Sae 346313330Sae switch (af) { 347313330Sae#ifdef INET 348313330Sae case AF_INET: 349313330Sae error = ipsec4_process_packet(m, sp, NULL); 350313330Sae break; 351313330Sae#endif 352313330Sae#ifdef INET6 353313330Sae case AF_INET6: 354313330Sae error = ipsec6_process_packet(m, sp, NULL); 355313330Sae break; 356313330Sae#endif 357313330Sae default: 358313330Sae panic("%s: unknown address family\n", __func__); 359313330Sae } 360313330Saeerr: 361313330Sae if (error != 0) 362313330Sae if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 363313330Sae return (error); 364313330Sae} 365313330Sae 366313330Saestatic void 367313330Saeipsec_qflush(struct ifnet *ifp __unused) 368313330Sae{ 369313330Sae 370313330Sae} 371313330Sae 372313330Saestatic int 373313330Saeipsec_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst, 374313330Sae struct route *ro) 375313330Sae{ 376313330Sae 377313330Sae return (ifp->if_transmit(ifp, m)); 378313330Sae} 379313330Sae 380313330Saeint 381313330Saeipsec_if_input(struct mbuf *m, struct secasvar *sav, uint32_t af) 382313330Sae{ 383313330Sae IPSEC_SC_RLOCK_TRACKER; 384313330Sae struct secasindex *saidx; 385313330Sae struct ipsec_softc *sc; 386313330Sae struct ifnet *ifp; 387313330Sae 388313330Sae if (sav->state != SADB_SASTATE_MATURE && 389313330Sae sav->state != SADB_SASTATE_DYING) { 390313330Sae m_freem(m); 391313330Sae return (ENETDOWN); 392313330Sae } 393313330Sae 394313330Sae if (sav->sah->saidx.mode != IPSEC_MODE_TUNNEL || 395313330Sae sav->sah->saidx.proto != IPPROTO_ESP) 396313330Sae return (0); 397313330Sae 398313330Sae IPSEC_SC_RLOCK(); 399313330Sae /* 400313330Sae * We only acquire SC_RLOCK() while we are doing search in 401313330Sae * ipsec_sc_htbl. It is safe, because removing softc or changing 402313330Sae * of reqid/addresses requires removing from hash table. 403313330Sae */ 404313330Sae LIST_FOREACH(sc, SCHASH_HASH(sav->sah->saidx.reqid), hash) { 405313330Sae saidx = ipsec_getsaidx(sc, IPSEC_DIR_INBOUND, 406313330Sae sav->sah->saidx.src.sa.sa_family); 407313330Sae /* SA's reqid should match reqid in SP */ 408313330Sae if (saidx == NULL || 409313330Sae sav->sah->saidx.reqid != saidx->reqid) 410313330Sae continue; 411313330Sae /* SAH's addresses should match tunnel endpoints. */ 412313330Sae if (key_sockaddrcmp(&sav->sah->saidx.dst.sa, 413313330Sae &saidx->dst.sa, 0) != 0) 414313330Sae continue; 415313330Sae if (key_sockaddrcmp(&sav->sah->saidx.src.sa, 416313330Sae &saidx->src.sa, 0) == 0) 417313330Sae break; 418313330Sae } 419313330Sae if (sc == NULL) { 420313330Sae IPSEC_SC_RUNLOCK(); 421313330Sae /* Tunnel was not found. Nothing to do. */ 422313330Sae return (0); 423313330Sae } 424313330Sae ifp = sc->ifp; 425313330Sae if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || 426313330Sae (ifp->if_flags & IFF_UP) == 0) { 427313330Sae IPSEC_SC_RUNLOCK(); 428313330Sae m_freem(m); 429313330Sae return (ENETDOWN); 430313330Sae } 431313330Sae /* 432313330Sae * We found matching and working tunnel. 433313330Sae * Set its ifnet as receiving interface. 434313330Sae */ 435313330Sae m->m_pkthdr.rcvif = ifp; 436313330Sae IPSEC_SC_RUNLOCK(); 437313330Sae 438333785Sae m_clrprotoflags(m); 439313330Sae M_SETFIB(m, ifp->if_fib); 440313330Sae BPF_MTAP2(ifp, &af, sizeof(af), m); 441313330Sae if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); 442313330Sae if_inc_counter(ifp, IFCOUNTER_IBYTES, m->m_pkthdr.len); 443313330Sae if ((ifp->if_flags & IFF_MONITOR) != 0) { 444313330Sae m_freem(m); 445313330Sae return (ENETDOWN); 446313330Sae } 447313330Sae return (0); 448313330Sae} 449313330Sae 450313330Sae/* XXX how should we handle IPv6 scope on SIOC[GS]IFPHYADDR? */ 451313330Saeint 452313330Saeipsec_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 453313330Sae{ 454313330Sae IPSEC_RLOCK_TRACKER; 455313330Sae struct ifreq *ifr = (struct ifreq*)data; 456313330Sae struct sockaddr *dst, *src; 457313330Sae struct ipsec_softc *sc; 458313330Sae struct secasindex *saidx; 459313330Sae#ifdef INET 460313330Sae struct sockaddr_in *sin = NULL; 461313330Sae#endif 462313330Sae#ifdef INET6 463313330Sae struct sockaddr_in6 *sin6 = NULL; 464313330Sae#endif 465313330Sae uint32_t reqid; 466313330Sae int error; 467313330Sae 468313330Sae switch (cmd) { 469313330Sae case SIOCSIFADDR: 470313330Sae ifp->if_flags |= IFF_UP; 471313330Sae case SIOCADDMULTI: 472313330Sae case SIOCDELMULTI: 473313330Sae case SIOCGIFMTU: 474313330Sae case SIOCSIFFLAGS: 475313330Sae return (0); 476313330Sae case SIOCSIFMTU: 477313330Sae if (ifr->ifr_mtu < IPSEC_MTU_MIN || 478313330Sae ifr->ifr_mtu > IPSEC_MTU_MAX) 479313330Sae return (EINVAL); 480313330Sae else 481313330Sae ifp->if_mtu = ifr->ifr_mtu; 482313330Sae return (0); 483313330Sae } 484313330Sae sx_xlock(&ipsec_ioctl_sx); 485313330Sae sc = ifp->if_softc; 486313330Sae /* Check that softc is still here */ 487313330Sae if (sc == NULL) { 488313330Sae error = ENXIO; 489313330Sae goto bad; 490313330Sae } 491313330Sae error = 0; 492313330Sae switch (cmd) { 493313330Sae case SIOCSIFPHYADDR: 494313330Sae#ifdef INET6 495313330Sae case SIOCSIFPHYADDR_IN6: 496313330Sae#endif 497313330Sae error = EINVAL; 498313330Sae switch (cmd) { 499313330Sae#ifdef INET 500313330Sae case SIOCSIFPHYADDR: 501313330Sae src = (struct sockaddr *) 502313330Sae &(((struct in_aliasreq *)data)->ifra_addr); 503313330Sae dst = (struct sockaddr *) 504313330Sae &(((struct in_aliasreq *)data)->ifra_dstaddr); 505313330Sae break; 506313330Sae#endif 507313330Sae#ifdef INET6 508313330Sae case SIOCSIFPHYADDR_IN6: 509313330Sae src = (struct sockaddr *) 510313330Sae &(((struct in6_aliasreq *)data)->ifra_addr); 511313330Sae dst = (struct sockaddr *) 512313330Sae &(((struct in6_aliasreq *)data)->ifra_dstaddr); 513313330Sae break; 514313330Sae#endif 515313330Sae default: 516313330Sae goto bad; 517313330Sae } 518313330Sae /* sa_family must be equal */ 519313330Sae if (src->sa_family != dst->sa_family || 520313330Sae src->sa_len != dst->sa_len) 521313330Sae goto bad; 522313330Sae 523313330Sae /* validate sa_len */ 524313330Sae switch (src->sa_family) { 525313330Sae#ifdef INET 526313330Sae case AF_INET: 527313330Sae if (src->sa_len != sizeof(struct sockaddr_in)) 528313330Sae goto bad; 529313330Sae break; 530313330Sae#endif 531313330Sae#ifdef INET6 532313330Sae case AF_INET6: 533313330Sae if (src->sa_len != sizeof(struct sockaddr_in6)) 534313330Sae goto bad; 535313330Sae break; 536313330Sae#endif 537313330Sae default: 538313330Sae error = EAFNOSUPPORT; 539313330Sae goto bad; 540313330Sae } 541313330Sae /* check sa_family looks sane for the cmd */ 542313330Sae error = EAFNOSUPPORT; 543313330Sae switch (cmd) { 544313330Sae#ifdef INET 545313330Sae case SIOCSIFPHYADDR: 546313330Sae if (src->sa_family == AF_INET) 547313330Sae break; 548313330Sae goto bad; 549313330Sae#endif 550313330Sae#ifdef INET6 551313330Sae case SIOCSIFPHYADDR_IN6: 552313330Sae if (src->sa_family == AF_INET6) 553313330Sae break; 554313330Sae goto bad; 555313330Sae#endif 556313330Sae } 557313330Sae error = EADDRNOTAVAIL; 558313330Sae switch (src->sa_family) { 559313330Sae#ifdef INET 560313330Sae case AF_INET: 561313330Sae if (satosin(src)->sin_addr.s_addr == INADDR_ANY || 562313330Sae satosin(dst)->sin_addr.s_addr == INADDR_ANY) 563313330Sae goto bad; 564313330Sae break; 565313330Sae#endif 566313330Sae#ifdef INET6 567313330Sae case AF_INET6: 568313330Sae if (IN6_IS_ADDR_UNSPECIFIED(&satosin6(src)->sin6_addr) 569313330Sae || 570313330Sae IN6_IS_ADDR_UNSPECIFIED(&satosin6(dst)->sin6_addr)) 571313330Sae goto bad; 572313330Sae /* 573313330Sae * Check validity of the scope zone ID of the 574313330Sae * addresses, and convert it into the kernel 575313330Sae * internal form if necessary. 576313330Sae */ 577313330Sae error = sa6_embedscope(satosin6(src), 0); 578313330Sae if (error != 0) 579313330Sae goto bad; 580313330Sae error = sa6_embedscope(satosin6(dst), 0); 581313330Sae if (error != 0) 582313330Sae goto bad; 583313330Sae#endif 584313330Sae }; 585313330Sae error = ipsec_set_addresses(ifp, src, dst); 586313330Sae break; 587313330Sae case SIOCDIFPHYADDR: 588313330Sae ipsec_delete_tunnel(ifp, 0); 589313330Sae break; 590313330Sae case SIOCGIFPSRCADDR: 591313330Sae case SIOCGIFPDSTADDR: 592313330Sae#ifdef INET6 593313330Sae case SIOCGIFPSRCADDR_IN6: 594313330Sae case SIOCGIFPDSTADDR_IN6: 595313330Sae#endif 596313330Sae IPSEC_RLOCK(sc); 597313330Sae if (sc->family == 0) { 598313330Sae IPSEC_RUNLOCK(sc); 599313330Sae error = EADDRNOTAVAIL; 600313330Sae break; 601313330Sae } 602313330Sae saidx = ipsec_getsaidx(sc, IPSEC_DIR_OUTBOUND, sc->family); 603313330Sae switch (cmd) { 604313330Sae#ifdef INET 605313330Sae case SIOCGIFPSRCADDR: 606313330Sae case SIOCGIFPDSTADDR: 607313330Sae if (saidx->src.sa.sa_family != AF_INET) { 608313330Sae error = EADDRNOTAVAIL; 609313330Sae break; 610313330Sae } 611313330Sae sin = (struct sockaddr_in *)&ifr->ifr_addr; 612313330Sae memset(sin, 0, sizeof(*sin)); 613313330Sae sin->sin_family = AF_INET; 614313330Sae sin->sin_len = sizeof(*sin); 615313330Sae break; 616313330Sae#endif 617313330Sae#ifdef INET6 618313330Sae case SIOCGIFPSRCADDR_IN6: 619313330Sae case SIOCGIFPDSTADDR_IN6: 620313330Sae if (saidx->src.sa.sa_family != AF_INET6) { 621313330Sae error = EADDRNOTAVAIL; 622313330Sae break; 623313330Sae } 624313330Sae sin6 = (struct sockaddr_in6 *) 625313330Sae &(((struct in6_ifreq *)data)->ifr_addr); 626313330Sae memset(sin6, 0, sizeof(*sin6)); 627313330Sae sin6->sin6_family = AF_INET6; 628313330Sae sin6->sin6_len = sizeof(*sin6); 629313330Sae break; 630313330Sae#endif 631313330Sae default: 632313330Sae error = EAFNOSUPPORT; 633313330Sae } 634313330Sae if (error == 0) { 635313330Sae switch (cmd) { 636313330Sae#ifdef INET 637313330Sae case SIOCGIFPSRCADDR: 638313330Sae sin->sin_addr = saidx->src.sin.sin_addr; 639313330Sae break; 640313330Sae case SIOCGIFPDSTADDR: 641313330Sae sin->sin_addr = saidx->dst.sin.sin_addr; 642313330Sae break; 643313330Sae#endif 644313330Sae#ifdef INET6 645313330Sae case SIOCGIFPSRCADDR_IN6: 646313330Sae sin6->sin6_addr = saidx->src.sin6.sin6_addr; 647313330Sae break; 648313330Sae case SIOCGIFPDSTADDR_IN6: 649313330Sae sin6->sin6_addr = saidx->dst.sin6.sin6_addr; 650313330Sae break; 651313330Sae#endif 652313330Sae } 653313330Sae } 654313330Sae IPSEC_RUNLOCK(sc); 655313330Sae if (error != 0) 656313330Sae break; 657313330Sae switch (cmd) { 658313330Sae#ifdef INET 659313330Sae case SIOCGIFPSRCADDR: 660313330Sae case SIOCGIFPDSTADDR: 661313330Sae error = prison_if(curthread->td_ucred, 662313330Sae (struct sockaddr *)sin); 663313330Sae if (error != 0) 664313330Sae memset(sin, 0, sizeof(*sin)); 665313330Sae break; 666313330Sae#endif 667313330Sae#ifdef INET6 668313330Sae case SIOCGIFPSRCADDR_IN6: 669313330Sae case SIOCGIFPDSTADDR_IN6: 670313330Sae error = prison_if(curthread->td_ucred, 671313330Sae (struct sockaddr *)sin6); 672313330Sae if (error == 0) 673313330Sae error = sa6_recoverscope(sin6); 674313330Sae if (error != 0) 675313330Sae memset(sin6, 0, sizeof(*sin6)); 676313330Sae#endif 677313330Sae } 678313330Sae break; 679313330Sae case SIOCGTUNFIB: 680313330Sae ifr->ifr_fib = sc->fibnum; 681313330Sae break; 682313330Sae case SIOCSTUNFIB: 683313330Sae if ((error = priv_check(curthread, PRIV_NET_SETIFFIB)) != 0) 684313330Sae break; 685313330Sae if (ifr->ifr_fib >= rt_numfibs) 686313330Sae error = EINVAL; 687313330Sae else 688313330Sae sc->fibnum = ifr->ifr_fib; 689313330Sae break; 690313330Sae case IPSECGREQID: 691313330Sae reqid = sc->reqid; 692332288Sbrooks error = copyout(&reqid, ifr_data_get_ptr(ifr), sizeof(reqid)); 693313330Sae break; 694313330Sae case IPSECSREQID: 695313330Sae if ((error = priv_check(curthread, PRIV_NET_SETIFCAP)) != 0) 696313330Sae break; 697332288Sbrooks error = copyin(ifr_data_get_ptr(ifr), &reqid, sizeof(reqid)); 698313330Sae if (error != 0) 699313330Sae break; 700313330Sae error = ipsec_set_reqid(ifp, reqid); 701313330Sae break; 702313330Sae default: 703313330Sae error = EINVAL; 704313330Sae break; 705313330Sae } 706313330Saebad: 707313330Sae sx_xunlock(&ipsec_ioctl_sx); 708313330Sae return (error); 709313330Sae} 710313330Sae 711313330Sae/* 712313330Sae * Allocate new private security policies for tunneling interface. 713313330Sae * Each tunneling interface has following security policies for 714313330Sae * both AF: 715313330Sae * 0.0.0.0/0[any] 0.0.0.0/0[any] -P in \ 716313330Sae * ipsec esp/tunnel/RemoteIP-LocalIP/unique:reqid 717313330Sae * 0.0.0.0/0[any] 0.0.0.0/0[any] -P out \ 718313330Sae * ipsec esp/tunnel/LocalIP-RemoteIP/unique:reqid 719313330Sae */ 720313330Saestatic int 721315514Saeipsec_newpolicies(struct ipsec_softc *sc, struct secpolicy *sp[IPSEC_SPCOUNT], 722313330Sae const struct sockaddr *src, const struct sockaddr *dst, uint32_t reqid) 723313330Sae{ 724313330Sae struct ipsecrequest *isr; 725313330Sae int i; 726313330Sae 727313330Sae memset(sp, 0, sizeof(struct secpolicy *) * IPSEC_SPCOUNT); 728313330Sae for (i = 0; i < IPSEC_SPCOUNT; i++) { 729313330Sae if ((sp[i] = key_newsp()) == NULL) 730313330Sae goto fail; 731313330Sae if ((isr = ipsec_newisr()) == NULL) 732313330Sae goto fail; 733313330Sae 734313330Sae sp[i]->policy = IPSEC_POLICY_IPSEC; 735313330Sae sp[i]->state = IPSEC_SPSTATE_DEAD; 736313330Sae sp[i]->req[sp[i]->tcount++] = isr; 737313330Sae sp[i]->created = time_second; 738315514Sae /* Use priority field to store if_index */ 739315514Sae sp[i]->priority = sc->ifp->if_index; 740313330Sae isr->level = IPSEC_LEVEL_UNIQUE; 741313330Sae isr->saidx.proto = IPPROTO_ESP; 742313330Sae isr->saidx.mode = IPSEC_MODE_TUNNEL; 743313330Sae isr->saidx.reqid = reqid; 744313330Sae if (i % 2 == 0) { 745313330Sae sp[i]->spidx.dir = IPSEC_DIR_INBOUND; 746313330Sae bcopy(src, &isr->saidx.dst, src->sa_len); 747313330Sae bcopy(dst, &isr->saidx.src, dst->sa_len); 748313330Sae } else { 749313330Sae sp[i]->spidx.dir = IPSEC_DIR_OUTBOUND; 750313330Sae bcopy(src, &isr->saidx.src, src->sa_len); 751313330Sae bcopy(dst, &isr->saidx.dst, dst->sa_len); 752313330Sae } 753313330Sae sp[i]->spidx.ul_proto = IPSEC_ULPROTO_ANY; 754313330Sae#ifdef INET 755313330Sae if (i < 2) { 756313330Sae sp[i]->spidx.src.sa.sa_family = 757313330Sae sp[i]->spidx.dst.sa.sa_family = AF_INET; 758313330Sae sp[i]->spidx.src.sa.sa_len = 759313330Sae sp[i]->spidx.dst.sa.sa_len = 760313330Sae sizeof(struct sockaddr_in); 761313330Sae continue; 762313330Sae } 763313330Sae#endif 764313330Sae#ifdef INET6 765313330Sae sp[i]->spidx.src.sa.sa_family = 766313330Sae sp[i]->spidx.dst.sa.sa_family = AF_INET6; 767313330Sae sp[i]->spidx.src.sa.sa_len = 768313330Sae sp[i]->spidx.dst.sa.sa_len = sizeof(struct sockaddr_in6); 769313330Sae#endif 770313330Sae } 771313330Sae return (0); 772313330Saefail: 773313330Sae for (i = 0; i < IPSEC_SPCOUNT; i++) 774313330Sae key_freesp(&sp[i]); 775313330Sae return (ENOMEM); 776313330Sae} 777313330Sae 778313330Saestatic int 779313330Saeipsec_check_reqid(uint32_t reqid) 780313330Sae{ 781313330Sae struct ipsec_softc *sc; 782313330Sae 783313330Sae IPSEC_SC_RLOCK_ASSERT(); 784313330Sae LIST_FOREACH(sc, &V_ipsec_sc_list, chain) { 785313330Sae if (sc->reqid == reqid) 786313330Sae return (EEXIST); 787313330Sae } 788313330Sae return (0); 789313330Sae} 790313330Sae 791313330Sae/* 792313330Sae * We use key_newreqid() to automatically obtain unique reqid. 793313330Sae * Then we check that given id is unique, i.e. it is not used by 794313330Sae * another if_ipsec(4) interface. This macro limits the number of 795313330Sae * tries to get unique id. 796313330Sae */ 797313330Sae#define IPSEC_REQID_TRYCNT 64 798313330Saestatic int 799313330Saeipsec_init_reqid(struct ipsec_softc *sc) 800313330Sae{ 801313330Sae uint32_t reqid; 802313330Sae int trycount; 803313330Sae 804313330Sae IPSEC_SC_RLOCK_ASSERT(); 805313330Sae 806313330Sae if (sc->reqid != 0) /* already initialized */ 807313330Sae return (0); 808313330Sae 809313330Sae trycount = IPSEC_REQID_TRYCNT; 810313330Sae while (--trycount > 0) { 811313330Sae reqid = key_newreqid(); 812313330Sae if (ipsec_check_reqid(reqid) == 0) 813313330Sae break; 814313330Sae } 815313330Sae if (trycount == 0) 816313330Sae return (EEXIST); 817313330Sae sc->reqid = reqid; 818313330Sae return (0); 819313330Sae} 820313330Sae 821313330Sae/* 822313330Sae * Set or update reqid for given tunneling interface. 823313330Sae * When specified reqid is zero, generate new one. 824313330Sae * We are protected by ioctl_sx lock from concurrent id generation. 825313330Sae * Also softc would not disappear while we hold ioctl_sx lock. 826313330Sae */ 827313330Saestatic int 828313330Saeipsec_set_reqid(struct ifnet *ifp, uint32_t reqid) 829313330Sae{ 830313330Sae IPSEC_SC_RLOCK_TRACKER; 831313330Sae struct ipsec_softc *sc; 832313330Sae struct secasindex *saidx; 833313330Sae 834313330Sae sx_assert(&ipsec_ioctl_sx, SA_XLOCKED); 835313330Sae 836313330Sae sc = ifp->if_softc; 837313330Sae if (sc->reqid == reqid && reqid != 0) 838313330Sae return (0); 839313330Sae 840313330Sae IPSEC_SC_RLOCK(); 841313330Sae if (reqid != 0) { 842313330Sae /* Check that specified reqid doesn't exist */ 843313330Sae if (ipsec_check_reqid(reqid) != 0) { 844313330Sae IPSEC_SC_RUNLOCK(); 845313330Sae return (EEXIST); 846313330Sae } 847313330Sae sc->reqid = reqid; 848313330Sae } else { 849313330Sae /* Generate new reqid */ 850313330Sae if (ipsec_init_reqid(sc) != 0) { 851313330Sae IPSEC_SC_RUNLOCK(); 852313330Sae return (EEXIST); 853313330Sae } 854313330Sae } 855313330Sae IPSEC_SC_RUNLOCK(); 856313330Sae 857313330Sae /* Tunnel isn't fully configured, just return. */ 858313330Sae if (sc->family == 0) 859313330Sae return (0); 860313330Sae 861313330Sae saidx = ipsec_getsaidx(sc, IPSEC_DIR_OUTBOUND, sc->family); 862313330Sae KASSERT(saidx != NULL, 863313330Sae ("saidx is NULL, but family is %d", sc->family)); 864313330Sae return (ipsec_set_tunnel(sc, &saidx->src.sa, &saidx->dst.sa, 865313330Sae sc->reqid)); 866313330Sae} 867313330Sae 868313330Sae/* 869313330Sae * Set tunnel endpoints addresses. 870313330Sae */ 871313330Saestatic int 872313330Saeipsec_set_addresses(struct ifnet *ifp, struct sockaddr *src, 873313330Sae struct sockaddr *dst) 874313330Sae{ 875313330Sae IPSEC_SC_RLOCK_TRACKER; 876313330Sae struct ipsec_softc *sc, *tsc; 877313330Sae struct secasindex *saidx; 878313330Sae 879313330Sae sx_assert(&ipsec_ioctl_sx, SA_XLOCKED); 880313330Sae 881313330Sae sc = ifp->if_softc; 882313330Sae if (sc->family != 0) { 883313330Sae saidx = ipsec_getsaidx(sc, IPSEC_DIR_OUTBOUND, 884313330Sae src->sa_family); 885313330Sae if (saidx != NULL && saidx->reqid == sc->reqid && 886313330Sae key_sockaddrcmp(&saidx->src.sa, src, 0) == 0 && 887313330Sae key_sockaddrcmp(&saidx->dst.sa, dst, 0) == 0) 888313330Sae return (0); /* Nothing has been changed. */ 889313330Sae 890313330Sae } 891313330Sae /* 892313330Sae * We cannot service IPsec tunnel when source address is 893313330Sae * not our own. 894313330Sae */ 895313330Sae#ifdef INET 896313330Sae if (src->sa_family == AF_INET && 897313330Sae in_localip(satosin(src)->sin_addr) == 0) 898313330Sae return (EADDRNOTAVAIL); 899313330Sae#endif 900313330Sae#ifdef INET6 901313330Sae /* 902313330Sae * NOTE: IPv6 addresses are in kernel internal form with 903313330Sae * embedded scope zone id. 904313330Sae */ 905313330Sae if (src->sa_family == AF_INET6 && 906313330Sae in6_localip(&satosin6(src)->sin6_addr) == 0) 907313330Sae return (EADDRNOTAVAIL); 908313330Sae#endif 909313330Sae /* Check that given addresses aren't already configured */ 910313330Sae IPSEC_SC_RLOCK(); 911313330Sae LIST_FOREACH(tsc, &V_ipsec_sc_list, chain) { 912313330Sae if (tsc == sc || tsc->family != src->sa_family) 913313330Sae continue; 914313330Sae saidx = ipsec_getsaidx(tsc, IPSEC_DIR_OUTBOUND, tsc->family); 915313330Sae if (key_sockaddrcmp(&saidx->src.sa, src, 0) == 0 && 916313330Sae key_sockaddrcmp(&saidx->dst.sa, dst, 0) == 0) { 917313330Sae /* We already have tunnel with such addresses */ 918313330Sae IPSEC_SC_RUNLOCK(); 919313330Sae return (EADDRNOTAVAIL); 920313330Sae } 921313330Sae } 922313330Sae /* If reqid is not set, generate new one. */ 923313330Sae if (ipsec_init_reqid(sc) != 0) { 924313330Sae IPSEC_SC_RUNLOCK(); 925313330Sae return (EEXIST); 926313330Sae } 927313330Sae IPSEC_SC_RUNLOCK(); 928313330Sae return (ipsec_set_tunnel(sc, src, dst, sc->reqid)); 929313330Sae} 930313330Sae 931313330Saestatic int 932313330Saeipsec_set_tunnel(struct ipsec_softc *sc, struct sockaddr *src, 933313330Sae struct sockaddr *dst, uint32_t reqid) 934313330Sae{ 935313330Sae struct secpolicy *sp[IPSEC_SPCOUNT]; 936313330Sae struct secpolicy *oldsp[IPSEC_SPCOUNT]; 937313330Sae int i, f; 938313330Sae 939313330Sae sx_assert(&ipsec_ioctl_sx, SA_XLOCKED); 940313330Sae 941313330Sae /* Allocate SP with new addresses. */ 942315514Sae if (ipsec_newpolicies(sc, sp, src, dst, reqid) == 0) { 943313330Sae /* Add new policies to SPDB */ 944313330Sae if (key_register_ifnet(sp, IPSEC_SPCOUNT) != 0) { 945313330Sae for (i = 0; i < IPSEC_SPCOUNT; i++) 946313330Sae key_freesp(&sp[i]); 947313330Sae return (EAGAIN); 948313330Sae } 949313330Sae IPSEC_SC_WLOCK(); 950313330Sae if ((f = sc->family) != 0) 951313330Sae LIST_REMOVE(sc, hash); 952313330Sae IPSEC_WLOCK(sc); 953313330Sae for (i = 0; i < IPSEC_SPCOUNT; i++) { 954313330Sae oldsp[i] = sc->sp[i]; 955313330Sae sc->sp[i] = sp[i]; 956313330Sae } 957313330Sae sc->family = src->sa_family; 958313330Sae IPSEC_WUNLOCK(sc); 959313330Sae LIST_INSERT_HEAD(SCHASH_HASH(sc->reqid), sc, hash); 960313330Sae IPSEC_SC_WUNLOCK(); 961313330Sae } else { 962313330Sae sc->ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 963313330Sae return (ENOMEM); 964313330Sae } 965313330Sae 966313330Sae sc->ifp->if_drv_flags |= IFF_DRV_RUNNING; 967313330Sae if (f != 0) { 968313330Sae key_unregister_ifnet(oldsp, IPSEC_SPCOUNT); 969313330Sae for (i = 0; i < IPSEC_SPCOUNT; i++) 970313330Sae key_freesp(&oldsp[i]); 971313330Sae } 972313330Sae return (0); 973313330Sae} 974313330Sae 975313330Saestatic void 976313330Saeipsec_delete_tunnel(struct ifnet *ifp, int locked) 977313330Sae{ 978313330Sae struct ipsec_softc *sc = ifp->if_softc; 979313330Sae struct secpolicy *oldsp[IPSEC_SPCOUNT]; 980313330Sae int i; 981313330Sae 982313330Sae sx_assert(&ipsec_ioctl_sx, SA_XLOCKED); 983313330Sae 984313330Sae ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 985313330Sae if (sc->family != 0) { 986313330Sae if (!locked) 987313330Sae IPSEC_SC_WLOCK(); 988313330Sae /* Remove from hash table */ 989313330Sae LIST_REMOVE(sc, hash); 990313330Sae IPSEC_WLOCK(sc); 991313330Sae for (i = 0; i < IPSEC_SPCOUNT; i++) { 992313330Sae oldsp[i] = sc->sp[i]; 993313330Sae sc->sp[i] = NULL; 994313330Sae } 995313330Sae sc->family = 0; 996313330Sae IPSEC_WUNLOCK(sc); 997313330Sae if (!locked) 998313330Sae IPSEC_SC_WUNLOCK(); 999313330Sae key_unregister_ifnet(oldsp, IPSEC_SPCOUNT); 1000313330Sae for (i = 0; i < IPSEC_SPCOUNT; i++) 1001313330Sae key_freesp(&oldsp[i]); 1002313330Sae } 1003313330Sae} 1004