ipsec.c revision 105197
1105197Ssam/* $FreeBSD: head/sys/netipsec/ipsec.c 105197 2002-10-16 02:10:08Z sam $ */ 2105197Ssam/* $KAME: ipsec.c,v 1.103 2001/05/24 07:14:18 sakane Exp $ */ 3105197Ssam 4105197Ssam/* 5105197Ssam * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 6105197Ssam * All rights reserved. 7105197Ssam * 8105197Ssam * Redistribution and use in source and binary forms, with or without 9105197Ssam * modification, are permitted provided that the following conditions 10105197Ssam * are met: 11105197Ssam * 1. Redistributions of source code must retain the above copyright 12105197Ssam * notice, this list of conditions and the following disclaimer. 13105197Ssam * 2. Redistributions in binary form must reproduce the above copyright 14105197Ssam * notice, this list of conditions and the following disclaimer in the 15105197Ssam * documentation and/or other materials provided with the distribution. 16105197Ssam * 3. Neither the name of the project nor the names of its contributors 17105197Ssam * may be used to endorse or promote products derived from this software 18105197Ssam * without specific prior written permission. 19105197Ssam * 20105197Ssam * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 21105197Ssam * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22105197Ssam * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23105197Ssam * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 24105197Ssam * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25105197Ssam * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26105197Ssam * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27105197Ssam * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28105197Ssam * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29105197Ssam * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30105197Ssam * SUCH DAMAGE. 31105197Ssam */ 32105197Ssam 33105197Ssam/* 34105197Ssam * IPsec controller part. 35105197Ssam */ 36105197Ssam 37105197Ssam#include "opt_inet.h" 38105197Ssam#include "opt_inet6.h" 39105197Ssam#include "opt_ipsec.h" 40105197Ssam 41105197Ssam#include <sys/param.h> 42105197Ssam#include <sys/systm.h> 43105197Ssam#include <sys/malloc.h> 44105197Ssam#include <sys/mbuf.h> 45105197Ssam#include <sys/domain.h> 46105197Ssam#include <sys/protosw.h> 47105197Ssam#include <sys/socket.h> 48105197Ssam#include <sys/socketvar.h> 49105197Ssam#include <sys/errno.h> 50105197Ssam#include <sys/time.h> 51105197Ssam#include <sys/kernel.h> 52105197Ssam#include <sys/syslog.h> 53105197Ssam#include <sys/sysctl.h> 54105197Ssam#include <sys/proc.h> 55105197Ssam 56105197Ssam#include <net/if.h> 57105197Ssam#include <net/route.h> 58105197Ssam 59105197Ssam#include <netinet/in.h> 60105197Ssam#include <netinet/in_systm.h> 61105197Ssam#include <netinet/ip.h> 62105197Ssam#include <netinet/ip_var.h> 63105197Ssam#include <netinet/in_var.h> 64105197Ssam#include <netinet/udp.h> 65105197Ssam#include <netinet/udp_var.h> 66105197Ssam#include <netinet/tcp.h> 67105197Ssam#include <netinet/udp.h> 68105197Ssam 69105197Ssam#include <netinet/ip6.h> 70105197Ssam#ifdef INET6 71105197Ssam#include <netinet6/ip6_var.h> 72105197Ssam#endif 73105197Ssam#include <netinet/in_pcb.h> 74105197Ssam#ifdef INET6 75105197Ssam#include <netinet/icmp6.h> 76105197Ssam#endif 77105197Ssam 78105197Ssam#include <netipsec/ipsec.h> 79105197Ssam#ifdef INET6 80105197Ssam#include <netipsec/ipsec6.h> 81105197Ssam#endif 82105197Ssam#include <netipsec/ah_var.h> 83105197Ssam#include <netipsec/esp_var.h> 84105197Ssam#include <netipsec/ipcomp.h> /*XXX*/ 85105197Ssam#include <netipsec/ipcomp_var.h> 86105197Ssam 87105197Ssam#include <netipsec/key.h> 88105197Ssam#include <netipsec/keydb.h> 89105197Ssam#include <netipsec/key_debug.h> 90105197Ssam 91105197Ssam#include <netipsec/xform.h> 92105197Ssam 93105197Ssam#include <machine/in_cksum.h> 94105197Ssam 95105197Ssam#include <net/net_osdep.h> 96105197Ssam 97105197Ssam#ifdef IPSEC_DEBUG 98105197Ssamint ipsec_debug = 1; 99105197Ssam#else 100105197Ssamint ipsec_debug = 0; 101105197Ssam#endif 102105197Ssam 103105197Ssam/* NB: name changed so netstat doesn't use it */ 104105197Ssamstruct newipsecstat newipsecstat; 105105197Ssamint ip4_ah_offsetmask = 0; /* maybe IP_DF? */ 106105197Ssamint ip4_ipsec_dfbit = 0; /* DF bit on encap. 0: clear 1: set 2: copy */ 107105197Ssamint ip4_esp_trans_deflev = IPSEC_LEVEL_USE; 108105197Ssamint ip4_esp_net_deflev = IPSEC_LEVEL_USE; 109105197Ssamint ip4_ah_trans_deflev = IPSEC_LEVEL_USE; 110105197Ssamint ip4_ah_net_deflev = IPSEC_LEVEL_USE; 111105197Ssamstruct secpolicy ip4_def_policy; 112105197Ssamint ip4_ipsec_ecn = 0; /* ECN ignore(-1)/forbidden(0)/allowed(1) */ 113105197Ssamint ip4_esp_randpad = -1; 114105197Ssam/* 115105197Ssam * Crypto support requirements: 116105197Ssam * 117105197Ssam * 1 require hardware support 118105197Ssam * -1 require software support 119105197Ssam * 0 take anything 120105197Ssam */ 121105197Ssamint crypto_support = 0; 122105197Ssam 123105197SsamSYSCTL_DECL(_net_inet_ipsec); 124105197Ssam 125105197Ssam/* net.inet.ipsec */ 126105197SsamSYSCTL_INT(_net_inet_ipsec, IPSECCTL_DEF_POLICY, 127105197Ssam def_policy, CTLFLAG_RW, &ip4_def_policy.policy, 0, ""); 128105197SsamSYSCTL_INT(_net_inet_ipsec, IPSECCTL_DEF_ESP_TRANSLEV, esp_trans_deflev, 129105197Ssam CTLFLAG_RW, &ip4_esp_trans_deflev, 0, ""); 130105197SsamSYSCTL_INT(_net_inet_ipsec, IPSECCTL_DEF_ESP_NETLEV, esp_net_deflev, 131105197Ssam CTLFLAG_RW, &ip4_esp_net_deflev, 0, ""); 132105197SsamSYSCTL_INT(_net_inet_ipsec, IPSECCTL_DEF_AH_TRANSLEV, ah_trans_deflev, 133105197Ssam CTLFLAG_RW, &ip4_ah_trans_deflev, 0, ""); 134105197SsamSYSCTL_INT(_net_inet_ipsec, IPSECCTL_DEF_AH_NETLEV, ah_net_deflev, 135105197Ssam CTLFLAG_RW, &ip4_ah_net_deflev, 0, ""); 136105197SsamSYSCTL_INT(_net_inet_ipsec, IPSECCTL_AH_CLEARTOS, 137105197Ssam ah_cleartos, CTLFLAG_RW, &ah_cleartos, 0, ""); 138105197SsamSYSCTL_INT(_net_inet_ipsec, IPSECCTL_AH_OFFSETMASK, 139105197Ssam ah_offsetmask, CTLFLAG_RW, &ip4_ah_offsetmask, 0, ""); 140105197SsamSYSCTL_INT(_net_inet_ipsec, IPSECCTL_DFBIT, 141105197Ssam dfbit, CTLFLAG_RW, &ip4_ipsec_dfbit, 0, ""); 142105197SsamSYSCTL_INT(_net_inet_ipsec, IPSECCTL_ECN, 143105197Ssam ecn, CTLFLAG_RW, &ip4_ipsec_ecn, 0, ""); 144105197SsamSYSCTL_INT(_net_inet_ipsec, IPSECCTL_DEBUG, 145105197Ssam debug, CTLFLAG_RW, &ipsec_debug, 0, ""); 146105197SsamSYSCTL_INT(_net_inet_ipsec, IPSECCTL_ESP_RANDPAD, 147105197Ssam esp_randpad, CTLFLAG_RW, &ip4_esp_randpad, 0, ""); 148105197SsamSYSCTL_INT(_net_inet_ipsec, OID_AUTO, 149105197Ssam crypto_support, CTLFLAG_RW, &crypto_support,0, ""); 150105197SsamSYSCTL_STRUCT(_net_inet_ipsec, OID_AUTO, 151105197Ssam ipsecstats, CTLFLAG_RD, &newipsecstat, newipsecstat, ""); 152105197Ssam 153105197Ssam#ifdef INET6 154105197Ssamint ip6_esp_trans_deflev = IPSEC_LEVEL_USE; 155105197Ssamint ip6_esp_net_deflev = IPSEC_LEVEL_USE; 156105197Ssamint ip6_ah_trans_deflev = IPSEC_LEVEL_USE; 157105197Ssamint ip6_ah_net_deflev = IPSEC_LEVEL_USE; 158105197Ssamint ip6_ipsec_ecn = 0; /* ECN ignore(-1)/forbidden(0)/allowed(1) */ 159105197Ssamint ip6_esp_randpad = -1; 160105197Ssam 161105197SsamSYSCTL_DECL(_net_inet6_ipsec6); 162105197Ssam 163105197Ssam/* net.inet6.ipsec6 */ 164105197Ssam#ifdef COMPAT_KAME 165105197SsamSYSCTL_OID(_net_inet6_ipsec6, IPSECCTL_STATS, stats, CTLFLAG_RD, 166105197Ssam 0,0, compat_ipsecstats_sysctl, "S", ""); 167105197Ssam#endif /* COMPAT_KAME */ 168105197SsamSYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_DEF_POLICY, 169105197Ssam def_policy, CTLFLAG_RW, &ip4_def_policy.policy, 0, ""); 170105197SsamSYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_DEF_ESP_TRANSLEV, esp_trans_deflev, 171105197Ssam CTLFLAG_RW, &ip6_esp_trans_deflev, 0, ""); 172105197SsamSYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_DEF_ESP_NETLEV, esp_net_deflev, 173105197Ssam CTLFLAG_RW, &ip6_esp_net_deflev, 0, ""); 174105197SsamSYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_DEF_AH_TRANSLEV, ah_trans_deflev, 175105197Ssam CTLFLAG_RW, &ip6_ah_trans_deflev, 0, ""); 176105197SsamSYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_DEF_AH_NETLEV, ah_net_deflev, 177105197Ssam CTLFLAG_RW, &ip6_ah_net_deflev, 0, ""); 178105197SsamSYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_ECN, 179105197Ssam ecn, CTLFLAG_RW, &ip6_ipsec_ecn, 0, ""); 180105197SsamSYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_DEBUG, 181105197Ssam debug, CTLFLAG_RW, &ipsec_debug, 0, ""); 182105197SsamSYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_ESP_RANDPAD, 183105197Ssam esp_randpad, CTLFLAG_RW, &ip6_esp_randpad, 0, ""); 184105197Ssam#endif /* INET6 */ 185105197Ssam 186105197Ssamstatic int ipsec4_setspidx_inpcb __P((struct mbuf *, struct inpcb *pcb)); 187105197Ssam#ifdef INET6 188105197Ssamstatic int ipsec6_setspidx_in6pcb __P((struct mbuf *, struct in6pcb *pcb)); 189105197Ssam#endif 190105197Ssamstatic int ipsec_setspidx __P((struct mbuf *, struct secpolicyindex *, int)); 191105197Ssamstatic void ipsec4_get_ulp __P((struct mbuf *m, struct secpolicyindex *, int)); 192105197Ssamstatic int ipsec4_setspidx_ipaddr __P((struct mbuf *, struct secpolicyindex *)); 193105197Ssam#ifdef INET6 194105197Ssamstatic void ipsec6_get_ulp __P((struct mbuf *m, struct secpolicyindex *, int)); 195105197Ssamstatic int ipsec6_setspidx_ipaddr __P((struct mbuf *, struct secpolicyindex *)); 196105197Ssam#endif 197105197Ssamstatic void ipsec_delpcbpolicy __P((struct inpcbpolicy *)); 198105197Ssamstatic struct secpolicy *ipsec_deepcopy_policy __P((struct secpolicy *src)); 199105197Ssamstatic int ipsec_set_policy __P((struct secpolicy **pcb_sp, 200105197Ssam int optname, caddr_t request, size_t len, int priv)); 201105197Ssamstatic int ipsec_get_policy __P((struct secpolicy *pcb_sp, struct mbuf **mp)); 202105197Ssamstatic void vshiftl __P((unsigned char *, int, int)); 203105197Ssamstatic size_t ipsec_hdrsiz __P((struct secpolicy *)); 204105197Ssam 205105197Ssam/* 206105197Ssam * Return a held reference to the default SP. 207105197Ssam */ 208105197Ssamstatic struct secpolicy * 209105197Ssamkey_allocsp_default(const char* where, int tag) 210105197Ssam{ 211105197Ssam struct secpolicy *sp; 212105197Ssam 213105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_STAMP, 214105197Ssam printf("DP key_allocsp_default from %s:%u\n", where, tag)); 215105197Ssam 216105197Ssam sp = &ip4_def_policy; 217105197Ssam if (sp->policy != IPSEC_POLICY_DISCARD && 218105197Ssam sp->policy != IPSEC_POLICY_NONE) { 219105197Ssam ipseclog((LOG_INFO, "fixed system default policy: %d->%d\n", 220105197Ssam sp->policy, IPSEC_POLICY_NONE)); 221105197Ssam sp->policy = IPSEC_POLICY_NONE; 222105197Ssam } 223105197Ssam sp->refcnt++; 224105197Ssam 225105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_STAMP, 226105197Ssam printf("DP key_allocsp_default returns SP:%p (%u)\n", 227105197Ssam sp, sp->refcnt)); 228105197Ssam return sp; 229105197Ssam} 230105197Ssam#define KEY_ALLOCSP_DEFAULT() \ 231105197Ssam key_allocsp_default(__FILE__, __LINE__) 232105197Ssam 233105197Ssam/* 234105197Ssam * For OUTBOUND packet having a socket. Searching SPD for packet, 235105197Ssam * and return a pointer to SP. 236105197Ssam * OUT: NULL: no apropreate SP found, the following value is set to error. 237105197Ssam * 0 : bypass 238105197Ssam * EACCES : discard packet. 239105197Ssam * ENOENT : ipsec_acquire() in progress, maybe. 240105197Ssam * others : error occured. 241105197Ssam * others: a pointer to SP 242105197Ssam * 243105197Ssam * NOTE: IPv6 mapped adddress concern is implemented here. 244105197Ssam */ 245105197Ssamstruct secpolicy * 246105197Ssamipsec_getpolicy(struct tdb_ident *tdbi, u_int dir) 247105197Ssam{ 248105197Ssam struct secpolicy *sp; 249105197Ssam 250105197Ssam KASSERT(tdbi != NULL, ("ipsec_getpolicy: null tdbi")); 251105197Ssam KASSERT(dir == IPSEC_DIR_INBOUND || dir == IPSEC_DIR_OUTBOUND, 252105197Ssam ("ipsec_getpolicy: invalid direction %u", dir)); 253105197Ssam 254105197Ssam sp = KEY_ALLOCSP2(tdbi->spi, &tdbi->dst, tdbi->proto, dir); 255105197Ssam if (sp == NULL) /*XXX????*/ 256105197Ssam sp = KEY_ALLOCSP_DEFAULT(); 257105197Ssam KASSERT(sp != NULL, ("ipsec_getpolicy: null SP")); 258105197Ssam return sp; 259105197Ssam} 260105197Ssam 261105197Ssam/* 262105197Ssam * For OUTBOUND packet having a socket. Searching SPD for packet, 263105197Ssam * and return a pointer to SP. 264105197Ssam * OUT: NULL: no apropreate SP found, the following value is set to error. 265105197Ssam * 0 : bypass 266105197Ssam * EACCES : discard packet. 267105197Ssam * ENOENT : ipsec_acquire() in progress, maybe. 268105197Ssam * others : error occured. 269105197Ssam * others: a pointer to SP 270105197Ssam * 271105197Ssam * NOTE: IPv6 mapped adddress concern is implemented here. 272105197Ssam */ 273105197Ssamstruct secpolicy * 274105197Ssamipsec_getpolicybysock(m, dir, inp, error) 275105197Ssam struct mbuf *m; 276105197Ssam u_int dir; 277105197Ssam struct inpcb *inp; 278105197Ssam int *error; 279105197Ssam{ 280105197Ssam struct inpcbpolicy *pcbsp = NULL; 281105197Ssam struct secpolicy *currsp = NULL; /* policy on socket */ 282105197Ssam struct secpolicy *sp; 283105197Ssam int af; 284105197Ssam 285105197Ssam KASSERT(m != NULL, ("ipsec_getpolicybysock: null mbuf")); 286105197Ssam KASSERT(inp != NULL, ("ipsec_getpolicybysock: null inpcb")); 287105197Ssam KASSERT(error != NULL, ("ipsec_getpolicybysock: null error")); 288105197Ssam KASSERT(dir == IPSEC_DIR_INBOUND || dir == IPSEC_DIR_OUTBOUND, 289105197Ssam ("ipsec_getpolicybysock: invalid direction %u", dir)); 290105197Ssam 291105197Ssam af = inp->inp_socket->so_proto->pr_domain->dom_family; 292105197Ssam KASSERT(af == AF_INET || af == AF_INET6, 293105197Ssam ("ipsec_getpolicybysock: unexpected protocol family %u", af)); 294105197Ssam 295105197Ssam switch (af) { 296105197Ssam case AF_INET: 297105197Ssam /* set spidx in pcb */ 298105197Ssam *error = ipsec4_setspidx_inpcb(m, inp); 299105197Ssam pcbsp = inp->inp_sp; 300105197Ssam break; 301105197Ssam#ifdef INET6 302105197Ssam case AF_INET6: 303105197Ssam /* set spidx in pcb */ 304105197Ssam *error = ipsec6_setspidx_in6pcb(m, inp); 305105197Ssam pcbsp = inp->in6p_sp; 306105197Ssam break; 307105197Ssam#endif 308105197Ssam default: 309105197Ssam *error = EPFNOSUPPORT; 310105197Ssam break; 311105197Ssam } 312105197Ssam if (*error) 313105197Ssam return NULL; 314105197Ssam 315105197Ssam KASSERT(pcbsp != NULL, ("ipsec_getpolicybysock: null pcbsp")); 316105197Ssam switch (dir) { 317105197Ssam case IPSEC_DIR_INBOUND: 318105197Ssam currsp = pcbsp->sp_in; 319105197Ssam break; 320105197Ssam case IPSEC_DIR_OUTBOUND: 321105197Ssam currsp = pcbsp->sp_out; 322105197Ssam break; 323105197Ssam } 324105197Ssam KASSERT(currsp != NULL, ("ipsec_getpolicybysock: null currsp")); 325105197Ssam 326105197Ssam if (pcbsp->priv) { /* when privilieged socket */ 327105197Ssam switch (currsp->policy) { 328105197Ssam case IPSEC_POLICY_BYPASS: 329105197Ssam case IPSEC_POLICY_IPSEC: 330105197Ssam currsp->refcnt++; 331105197Ssam sp = currsp; 332105197Ssam break; 333105197Ssam 334105197Ssam case IPSEC_POLICY_ENTRUST: 335105197Ssam /* look for a policy in SPD */ 336105197Ssam sp = KEY_ALLOCSP(&currsp->spidx, dir); 337105197Ssam if (sp == NULL) /* no SP found */ 338105197Ssam sp = KEY_ALLOCSP_DEFAULT(); 339105197Ssam break; 340105197Ssam 341105197Ssam default: 342105197Ssam ipseclog((LOG_ERR, "ipsec_getpolicybysock: " 343105197Ssam "Invalid policy for PCB %d\n", currsp->policy)); 344105197Ssam *error = EINVAL; 345105197Ssam return NULL; 346105197Ssam } 347105197Ssam } else { /* unpriv, SPD has policy */ 348105197Ssam sp = KEY_ALLOCSP(&currsp->spidx, dir); 349105197Ssam if (sp == NULL) { /* no SP found */ 350105197Ssam switch (currsp->policy) { 351105197Ssam case IPSEC_POLICY_BYPASS: 352105197Ssam ipseclog((LOG_ERR, "ipsec_getpolicybysock: " 353105197Ssam "Illegal policy for non-priviliged defined %d\n", 354105197Ssam currsp->policy)); 355105197Ssam *error = EINVAL; 356105197Ssam return NULL; 357105197Ssam 358105197Ssam case IPSEC_POLICY_ENTRUST: 359105197Ssam sp = KEY_ALLOCSP_DEFAULT(); 360105197Ssam break; 361105197Ssam 362105197Ssam case IPSEC_POLICY_IPSEC: 363105197Ssam currsp->refcnt++; 364105197Ssam sp = currsp; 365105197Ssam break; 366105197Ssam 367105197Ssam default: 368105197Ssam ipseclog((LOG_ERR, "ipsec_getpolicybysock: " 369105197Ssam "Invalid policy for PCB %d\n", currsp->policy)); 370105197Ssam *error = EINVAL; 371105197Ssam return NULL; 372105197Ssam } 373105197Ssam } 374105197Ssam } 375105197Ssam KASSERT(sp != NULL, 376105197Ssam ("ipsec_getpolicybysock: null SP (priv %u policy %u", 377105197Ssam pcbsp->priv, currsp->policy)); 378105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_STAMP, 379105197Ssam printf("DP ipsec_getpolicybysock (priv %u policy %u) allocates " 380105197Ssam "SP:%p (refcnt %u)\n", pcbsp->priv, currsp->policy, 381105197Ssam sp, sp->refcnt)); 382105197Ssam return sp; 383105197Ssam} 384105197Ssam 385105197Ssam/* 386105197Ssam * For FORWADING packet or OUTBOUND without a socket. Searching SPD for packet, 387105197Ssam * and return a pointer to SP. 388105197Ssam * OUT: positive: a pointer to the entry for security policy leaf matched. 389105197Ssam * NULL: no apropreate SP found, the following value is set to error. 390105197Ssam * 0 : bypass 391105197Ssam * EACCES : discard packet. 392105197Ssam * ENOENT : ipsec_acquire() in progress, maybe. 393105197Ssam * others : error occured. 394105197Ssam */ 395105197Ssamstruct secpolicy * 396105197Ssamipsec_getpolicybyaddr(m, dir, flag, error) 397105197Ssam struct mbuf *m; 398105197Ssam u_int dir; 399105197Ssam int flag; 400105197Ssam int *error; 401105197Ssam{ 402105197Ssam struct secpolicyindex spidx; 403105197Ssam struct secpolicy *sp; 404105197Ssam 405105197Ssam KASSERT(m != NULL, ("ipsec_getpolicybyaddr: null mbuf")); 406105197Ssam KASSERT(error != NULL, ("ipsec_getpolicybyaddr: null error")); 407105197Ssam KASSERT(dir == IPSEC_DIR_INBOUND || dir == IPSEC_DIR_OUTBOUND, 408105197Ssam ("ipsec4_getpolicybaddr: invalid direction %u", dir)); 409105197Ssam 410105197Ssam sp = NULL; 411105197Ssam if (key_havesp(dir)) { 412105197Ssam /* make a index to look for a policy */ 413105197Ssam *error = ipsec_setspidx(m, &spidx, 414105197Ssam (flag & IP_FORWARDING) ? 0 : 1); 415105197Ssam if (*error != 0) { 416105197Ssam DPRINTF(("ipsec_getpolicybyaddr: setpidx failed," 417105197Ssam " dir %u flag %u\n", dir, flag)); 418105197Ssam bzero(&spidx, sizeof (spidx)); 419105197Ssam return NULL; 420105197Ssam } 421105197Ssam spidx.dir = dir; 422105197Ssam 423105197Ssam sp = KEY_ALLOCSP(&spidx, dir); 424105197Ssam } 425105197Ssam if (sp == NULL) /* no SP found, use system default */ 426105197Ssam sp = KEY_ALLOCSP_DEFAULT(); 427105197Ssam KASSERT(sp != NULL, ("ipsec_getpolicybyaddr: null SP")); 428105197Ssam return sp; 429105197Ssam} 430105197Ssam 431105197Ssamstruct secpolicy * 432105197Ssamipsec4_checkpolicy(m, dir, flag, error, inp) 433105197Ssam struct mbuf *m; 434105197Ssam u_int dir, flag; 435105197Ssam int *error; 436105197Ssam struct inpcb *inp; 437105197Ssam{ 438105197Ssam struct secpolicy *sp; 439105197Ssam 440105197Ssam *error = 0; 441105197Ssam if (inp == NULL) 442105197Ssam sp = ipsec_getpolicybyaddr(m, dir, flag, error); 443105197Ssam else 444105197Ssam sp = ipsec_getpolicybysock(m, dir, inp, error); 445105197Ssam if (sp == NULL) { 446105197Ssam KASSERT(*error != 0, 447105197Ssam ("ipsec4_checkpolicy: getpolicy failed w/o error")); 448105197Ssam newipsecstat.ips_out_inval++; 449105197Ssam return NULL; 450105197Ssam } 451105197Ssam KASSERT(*error == 0, 452105197Ssam ("ipsec4_checkpolicy: sp w/ error set to %u", *error)); 453105197Ssam switch (sp->policy) { 454105197Ssam case IPSEC_POLICY_ENTRUST: 455105197Ssam default: 456105197Ssam printf("ipsec4_checkpolicy: invalid policy %u\n", sp->policy); 457105197Ssam /* fall thru... */ 458105197Ssam case IPSEC_POLICY_DISCARD: 459105197Ssam newipsecstat.ips_out_polvio++; 460105197Ssam *error = -EINVAL; /* packet is discarded by caller */ 461105197Ssam break; 462105197Ssam case IPSEC_POLICY_BYPASS: 463105197Ssam case IPSEC_POLICY_NONE: 464105197Ssam KEY_FREESP(&sp); 465105197Ssam sp = NULL; /* NB: force NULL result */ 466105197Ssam break; 467105197Ssam case IPSEC_POLICY_IPSEC: 468105197Ssam if (sp->req == NULL) /* acquire an SA */ 469105197Ssam *error = key_spdacquire(sp); 470105197Ssam break; 471105197Ssam } 472105197Ssam if (*error != 0) { 473105197Ssam KEY_FREESP(&sp); 474105197Ssam sp = NULL; 475105197Ssam } 476105197Ssam return sp; 477105197Ssam} 478105197Ssam 479105197Ssamstatic int 480105197Ssamipsec4_setspidx_inpcb(m, pcb) 481105197Ssam struct mbuf *m; 482105197Ssam struct inpcb *pcb; 483105197Ssam{ 484105197Ssam int error; 485105197Ssam 486105197Ssam KASSERT(pcb != NULL, ("ipsec4_setspidx_inpcb: null pcb")); 487105197Ssam KASSERT(pcb->inp_sp != NULL, ("ipsec4_setspidx_inpcb: null inp_sp")); 488105197Ssam KASSERT(pcb->inp_sp->sp_out != NULL && pcb->inp_sp->sp_in != NULL, 489105197Ssam ("ipsec4_setspidx_inpcb: null sp_in || sp_out")); 490105197Ssam 491105197Ssam error = ipsec_setspidx(m, &pcb->inp_sp->sp_in->spidx, 1); 492105197Ssam if (error == 0) { 493105197Ssam pcb->inp_sp->sp_in->spidx.dir = IPSEC_DIR_INBOUND; 494105197Ssam pcb->inp_sp->sp_out->spidx = pcb->inp_sp->sp_in->spidx; 495105197Ssam pcb->inp_sp->sp_out->spidx.dir = IPSEC_DIR_OUTBOUND; 496105197Ssam } else { 497105197Ssam bzero(&pcb->inp_sp->sp_in->spidx, 498105197Ssam sizeof (pcb->inp_sp->sp_in->spidx)); 499105197Ssam bzero(&pcb->inp_sp->sp_out->spidx, 500105197Ssam sizeof (pcb->inp_sp->sp_in->spidx)); 501105197Ssam } 502105197Ssam return error; 503105197Ssam} 504105197Ssam 505105197Ssam#ifdef INET6 506105197Ssamstatic int 507105197Ssamipsec6_setspidx_in6pcb(m, pcb) 508105197Ssam struct mbuf *m; 509105197Ssam struct in6pcb *pcb; 510105197Ssam{ 511105197Ssam struct secpolicyindex *spidx; 512105197Ssam int error; 513105197Ssam 514105197Ssam KASSERT(pcb != NULL, ("ipsec6_setspidx_in6pcb: null pcb")); 515105197Ssam KASSERT(pcb->in6p_sp != NULL, ("ipsec6_setspidx_in6pcb: null inp_sp")); 516105197Ssam KASSERT(pcb->in6p_sp->sp_out != NULL && pcb->in6p_sp->sp_in != NULL, 517105197Ssam ("ipsec6_setspidx_in6pcb: null sp_in || sp_out")); 518105197Ssam 519105197Ssam bzero(&pcb->in6p_sp->sp_in->spidx, sizeof(*spidx)); 520105197Ssam bzero(&pcb->in6p_sp->sp_out->spidx, sizeof(*spidx)); 521105197Ssam 522105197Ssam spidx = &pcb->in6p_sp->sp_in->spidx; 523105197Ssam error = ipsec_setspidx(m, spidx, 1); 524105197Ssam if (error) 525105197Ssam goto bad; 526105197Ssam spidx->dir = IPSEC_DIR_INBOUND; 527105197Ssam 528105197Ssam spidx = &pcb->in6p_sp->sp_out->spidx; 529105197Ssam error = ipsec_setspidx(m, spidx, 1); 530105197Ssam if (error) 531105197Ssam goto bad; 532105197Ssam spidx->dir = IPSEC_DIR_OUTBOUND; 533105197Ssam 534105197Ssam return 0; 535105197Ssam 536105197Ssambad: 537105197Ssam bzero(&pcb->in6p_sp->sp_in->spidx, sizeof(*spidx)); 538105197Ssam bzero(&pcb->in6p_sp->sp_out->spidx, sizeof(*spidx)); 539105197Ssam return error; 540105197Ssam} 541105197Ssam#endif 542105197Ssam 543105197Ssam/* 544105197Ssam * configure security policy index (src/dst/proto/sport/dport) 545105197Ssam * by looking at the content of mbuf. 546105197Ssam * the caller is responsible for error recovery (like clearing up spidx). 547105197Ssam */ 548105197Ssamstatic int 549105197Ssamipsec_setspidx(m, spidx, needport) 550105197Ssam struct mbuf *m; 551105197Ssam struct secpolicyindex *spidx; 552105197Ssam int needport; 553105197Ssam{ 554105197Ssam struct ip *ip = NULL; 555105197Ssam struct ip ipbuf; 556105197Ssam u_int v; 557105197Ssam struct mbuf *n; 558105197Ssam int len; 559105197Ssam int error; 560105197Ssam 561105197Ssam KASSERT(m != NULL, ("ipsec_setspidx: null mbuf")); 562105197Ssam 563105197Ssam /* 564105197Ssam * validate m->m_pkthdr.len. we see incorrect length if we 565105197Ssam * mistakenly call this function with inconsistent mbuf chain 566105197Ssam * (like 4.4BSD tcp/udp processing). XXX should we panic here? 567105197Ssam */ 568105197Ssam len = 0; 569105197Ssam for (n = m; n; n = n->m_next) 570105197Ssam len += n->m_len; 571105197Ssam if (m->m_pkthdr.len != len) { 572105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DUMP, 573105197Ssam printf("ipsec_setspidx: " 574105197Ssam "total of m_len(%d) != pkthdr.len(%d), " 575105197Ssam "ignored.\n", 576105197Ssam len, m->m_pkthdr.len)); 577105197Ssam return EINVAL; 578105197Ssam } 579105197Ssam 580105197Ssam if (m->m_pkthdr.len < sizeof(struct ip)) { 581105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DUMP, 582105197Ssam printf("ipsec_setspidx: " 583105197Ssam "pkthdr.len(%d) < sizeof(struct ip), ignored.\n", 584105197Ssam m->m_pkthdr.len)); 585105197Ssam return EINVAL; 586105197Ssam } 587105197Ssam 588105197Ssam if (m->m_len >= sizeof(*ip)) 589105197Ssam ip = mtod(m, struct ip *); 590105197Ssam else { 591105197Ssam m_copydata(m, 0, sizeof(ipbuf), (caddr_t)&ipbuf); 592105197Ssam ip = &ipbuf; 593105197Ssam } 594105197Ssam#ifdef _IP_VHL 595105197Ssam v = _IP_VHL_V(ip->ip_vhl); 596105197Ssam#else 597105197Ssam v = ip->ip_v; 598105197Ssam#endif 599105197Ssam switch (v) { 600105197Ssam case 4: 601105197Ssam error = ipsec4_setspidx_ipaddr(m, spidx); 602105197Ssam if (error) 603105197Ssam return error; 604105197Ssam ipsec4_get_ulp(m, spidx, needport); 605105197Ssam return 0; 606105197Ssam#ifdef INET6 607105197Ssam case 6: 608105197Ssam if (m->m_pkthdr.len < sizeof(struct ip6_hdr)) { 609105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DUMP, 610105197Ssam printf("ipsec_setspidx: " 611105197Ssam "pkthdr.len(%d) < sizeof(struct ip6_hdr), " 612105197Ssam "ignored.\n", m->m_pkthdr.len)); 613105197Ssam return EINVAL; 614105197Ssam } 615105197Ssam error = ipsec6_setspidx_ipaddr(m, spidx); 616105197Ssam if (error) 617105197Ssam return error; 618105197Ssam ipsec6_get_ulp(m, spidx, needport); 619105197Ssam return 0; 620105197Ssam#endif 621105197Ssam default: 622105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DUMP, 623105197Ssam printf("ipsec_setspidx: " 624105197Ssam "unknown IP version %u, ignored.\n", v)); 625105197Ssam return EINVAL; 626105197Ssam } 627105197Ssam} 628105197Ssam 629105197Ssamstatic void 630105197Ssamipsec4_get_ulp(struct mbuf *m, struct secpolicyindex *spidx, int needport) 631105197Ssam{ 632105197Ssam u_int8_t nxt; 633105197Ssam int off; 634105197Ssam 635105197Ssam /* sanity check */ 636105197Ssam KASSERT(m != NULL, ("ipsec4_get_ulp: null mbuf")); 637105197Ssam KASSERT(m->m_pkthdr.len >= sizeof(struct ip), 638105197Ssam ("ipsec4_get_ulp: packet too short")); 639105197Ssam 640105197Ssam /* NB: ip_input() flips it into host endian XXX need more checking */ 641105197Ssam if (m->m_len < sizeof (struct ip)) { 642105197Ssam struct ip *ip = mtod(m, struct ip *); 643105197Ssam if (ip->ip_off & (IP_MF | IP_OFFMASK)) 644105197Ssam goto done; 645105197Ssam#ifdef _IP_VHL 646105197Ssam off = _IP_VHL_HL(ip->ip_vhl) << 2; 647105197Ssam#else 648105197Ssam off = ip->ip_hl << 2; 649105197Ssam#endif 650105197Ssam nxt = ip->ip_p; 651105197Ssam } else { 652105197Ssam struct ip ih; 653105197Ssam 654105197Ssam m_copydata(m, 0, sizeof (struct ip), (caddr_t) &ih); 655105197Ssam if (ih.ip_off & (IP_MF | IP_OFFMASK)) 656105197Ssam goto done; 657105197Ssam#ifdef _IP_VHL 658105197Ssam off = _IP_VHL_HL(ih.ip_vhl) << 2; 659105197Ssam#else 660105197Ssam off = ih.ip_hl << 2; 661105197Ssam#endif 662105197Ssam nxt = ih.ip_p; 663105197Ssam } 664105197Ssam 665105197Ssam while (off < m->m_pkthdr.len) { 666105197Ssam struct ip6_ext ip6e; 667105197Ssam struct tcphdr th; 668105197Ssam struct udphdr uh; 669105197Ssam 670105197Ssam switch (nxt) { 671105197Ssam case IPPROTO_TCP: 672105197Ssam spidx->ul_proto = nxt; 673105197Ssam if (!needport) 674105197Ssam goto done_proto; 675105197Ssam if (off + sizeof(struct tcphdr) > m->m_pkthdr.len) 676105197Ssam goto done; 677105197Ssam m_copydata(m, off, sizeof (th), (caddr_t) &th); 678105197Ssam spidx->src.sin.sin_port = th.th_sport; 679105197Ssam spidx->dst.sin.sin_port = th.th_dport; 680105197Ssam return; 681105197Ssam case IPPROTO_UDP: 682105197Ssam spidx->ul_proto = nxt; 683105197Ssam if (!needport) 684105197Ssam goto done_proto; 685105197Ssam if (off + sizeof(struct udphdr) > m->m_pkthdr.len) 686105197Ssam goto done; 687105197Ssam m_copydata(m, off, sizeof (uh), (caddr_t) &uh); 688105197Ssam spidx->src.sin.sin_port = uh.uh_sport; 689105197Ssam spidx->dst.sin.sin_port = uh.uh_dport; 690105197Ssam return; 691105197Ssam case IPPROTO_AH: 692105197Ssam if (m->m_pkthdr.len > off + sizeof(ip6e)) 693105197Ssam goto done; 694105197Ssam /* XXX sigh, this works but is totally bogus */ 695105197Ssam m_copydata(m, off, sizeof(ip6e), (caddr_t) &ip6e); 696105197Ssam off += (ip6e.ip6e_len + 2) << 2; 697105197Ssam nxt = ip6e.ip6e_nxt; 698105197Ssam break; 699105197Ssam case IPPROTO_ICMP: 700105197Ssam default: 701105197Ssam /* XXX intermediate headers??? */ 702105197Ssam spidx->ul_proto = nxt; 703105197Ssam goto done_proto; 704105197Ssam } 705105197Ssam } 706105197Ssamdone: 707105197Ssam spidx->ul_proto = IPSEC_ULPROTO_ANY; 708105197Ssamdone_proto: 709105197Ssam spidx->src.sin.sin_port = IPSEC_PORT_ANY; 710105197Ssam spidx->dst.sin.sin_port = IPSEC_PORT_ANY; 711105197Ssam} 712105197Ssam 713105197Ssam/* assumes that m is sane */ 714105197Ssamstatic int 715105197Ssamipsec4_setspidx_ipaddr(struct mbuf *m, struct secpolicyindex *spidx) 716105197Ssam{ 717105197Ssam static const struct sockaddr_in template = { 718105197Ssam sizeof (struct sockaddr_in), 719105197Ssam AF_INET, 720105197Ssam 0, { 0 }, { 0, 0, 0, 0, 0, 0, 0, 0 } 721105197Ssam }; 722105197Ssam 723105197Ssam spidx->src.sin = template; 724105197Ssam spidx->dst.sin = template; 725105197Ssam 726105197Ssam if (m->m_len < sizeof (struct ip)) { 727105197Ssam m_copydata(m, offsetof(struct ip, ip_src), 728105197Ssam sizeof (struct in_addr), 729105197Ssam (caddr_t) &spidx->src.sin.sin_addr); 730105197Ssam m_copydata(m, offsetof(struct ip, ip_dst), 731105197Ssam sizeof (struct in_addr), 732105197Ssam (caddr_t) &spidx->dst.sin.sin_addr); 733105197Ssam } else { 734105197Ssam struct ip *ip = mtod(m, struct ip *); 735105197Ssam spidx->src.sin.sin_addr = ip->ip_src; 736105197Ssam spidx->dst.sin.sin_addr = ip->ip_dst; 737105197Ssam } 738105197Ssam 739105197Ssam spidx->prefs = sizeof(struct in_addr) << 3; 740105197Ssam spidx->prefd = sizeof(struct in_addr) << 3; 741105197Ssam 742105197Ssam return 0; 743105197Ssam} 744105197Ssam 745105197Ssam#ifdef INET6 746105197Ssamstatic void 747105197Ssamipsec6_get_ulp(m, spidx, needport) 748105197Ssam struct mbuf *m; 749105197Ssam struct secpolicyindex *spidx; 750105197Ssam int needport; 751105197Ssam{ 752105197Ssam int off, nxt; 753105197Ssam struct tcphdr th; 754105197Ssam struct udphdr uh; 755105197Ssam 756105197Ssam /* sanity check */ 757105197Ssam if (m == NULL) 758105197Ssam panic("ipsec6_get_ulp: NULL pointer was passed.\n"); 759105197Ssam 760105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DUMP, 761105197Ssam printf("ipsec6_get_ulp:\n"); kdebug_mbuf(m)); 762105197Ssam 763105197Ssam /* set default */ 764105197Ssam spidx->ul_proto = IPSEC_ULPROTO_ANY; 765105197Ssam ((struct sockaddr_in6 *)&spidx->src)->sin6_port = IPSEC_PORT_ANY; 766105197Ssam ((struct sockaddr_in6 *)&spidx->dst)->sin6_port = IPSEC_PORT_ANY; 767105197Ssam 768105197Ssam nxt = -1; 769105197Ssam off = ip6_lasthdr(m, 0, IPPROTO_IPV6, &nxt); 770105197Ssam if (off < 0 || m->m_pkthdr.len < off) 771105197Ssam return; 772105197Ssam 773105197Ssam switch (nxt) { 774105197Ssam case IPPROTO_TCP: 775105197Ssam spidx->ul_proto = nxt; 776105197Ssam if (!needport) 777105197Ssam break; 778105197Ssam if (off + sizeof(struct tcphdr) > m->m_pkthdr.len) 779105197Ssam break; 780105197Ssam m_copydata(m, off, sizeof(th), (caddr_t)&th); 781105197Ssam ((struct sockaddr_in6 *)&spidx->src)->sin6_port = th.th_sport; 782105197Ssam ((struct sockaddr_in6 *)&spidx->dst)->sin6_port = th.th_dport; 783105197Ssam break; 784105197Ssam case IPPROTO_UDP: 785105197Ssam spidx->ul_proto = nxt; 786105197Ssam if (!needport) 787105197Ssam break; 788105197Ssam if (off + sizeof(struct udphdr) > m->m_pkthdr.len) 789105197Ssam break; 790105197Ssam m_copydata(m, off, sizeof(uh), (caddr_t)&uh); 791105197Ssam ((struct sockaddr_in6 *)&spidx->src)->sin6_port = uh.uh_sport; 792105197Ssam ((struct sockaddr_in6 *)&spidx->dst)->sin6_port = uh.uh_dport; 793105197Ssam break; 794105197Ssam case IPPROTO_ICMPV6: 795105197Ssam default: 796105197Ssam /* XXX intermediate headers??? */ 797105197Ssam spidx->ul_proto = nxt; 798105197Ssam break; 799105197Ssam } 800105197Ssam} 801105197Ssam 802105197Ssam/* assumes that m is sane */ 803105197Ssamstatic int 804105197Ssamipsec6_setspidx_ipaddr(m, spidx) 805105197Ssam struct mbuf *m; 806105197Ssam struct secpolicyindex *spidx; 807105197Ssam{ 808105197Ssam struct ip6_hdr *ip6 = NULL; 809105197Ssam struct ip6_hdr ip6buf; 810105197Ssam struct sockaddr_in6 *sin6; 811105197Ssam 812105197Ssam if (m->m_len >= sizeof(*ip6)) 813105197Ssam ip6 = mtod(m, struct ip6_hdr *); 814105197Ssam else { 815105197Ssam m_copydata(m, 0, sizeof(ip6buf), (caddr_t)&ip6buf); 816105197Ssam ip6 = &ip6buf; 817105197Ssam } 818105197Ssam 819105197Ssam sin6 = (struct sockaddr_in6 *)&spidx->src; 820105197Ssam bzero(sin6, sizeof(*sin6)); 821105197Ssam sin6->sin6_family = AF_INET6; 822105197Ssam sin6->sin6_len = sizeof(struct sockaddr_in6); 823105197Ssam bcopy(&ip6->ip6_src, &sin6->sin6_addr, sizeof(ip6->ip6_src)); 824105197Ssam if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) { 825105197Ssam sin6->sin6_addr.s6_addr16[1] = 0; 826105197Ssam sin6->sin6_scope_id = ntohs(ip6->ip6_src.s6_addr16[1]); 827105197Ssam } 828105197Ssam spidx->prefs = sizeof(struct in6_addr) << 3; 829105197Ssam 830105197Ssam sin6 = (struct sockaddr_in6 *)&spidx->dst; 831105197Ssam bzero(sin6, sizeof(*sin6)); 832105197Ssam sin6->sin6_family = AF_INET6; 833105197Ssam sin6->sin6_len = sizeof(struct sockaddr_in6); 834105197Ssam bcopy(&ip6->ip6_dst, &sin6->sin6_addr, sizeof(ip6->ip6_dst)); 835105197Ssam if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) { 836105197Ssam sin6->sin6_addr.s6_addr16[1] = 0; 837105197Ssam sin6->sin6_scope_id = ntohs(ip6->ip6_dst.s6_addr16[1]); 838105197Ssam } 839105197Ssam spidx->prefd = sizeof(struct in6_addr) << 3; 840105197Ssam 841105197Ssam return 0; 842105197Ssam} 843105197Ssam#endif 844105197Ssam 845105197Ssamstatic void 846105197Ssamipsec_delpcbpolicy(p) 847105197Ssam struct inpcbpolicy *p; 848105197Ssam{ 849105197Ssam free(p, M_SECA); 850105197Ssam} 851105197Ssam 852105197Ssam/* initialize policy in PCB */ 853105197Ssamint 854105197Ssamipsec_init_policy(so, pcb_sp) 855105197Ssam struct socket *so; 856105197Ssam struct inpcbpolicy **pcb_sp; 857105197Ssam{ 858105197Ssam struct inpcbpolicy *new; 859105197Ssam 860105197Ssam /* sanity check. */ 861105197Ssam if (so == NULL || pcb_sp == NULL) 862105197Ssam panic("ipsec_init_policy: NULL pointer was passed.\n"); 863105197Ssam 864105197Ssam new = (struct inpcbpolicy *) malloc(sizeof(struct inpcbpolicy), 865105197Ssam M_SECA, M_NOWAIT|M_ZERO); 866105197Ssam if (new == NULL) { 867105197Ssam ipseclog((LOG_DEBUG, "ipsec_init_policy: No more memory.\n")); 868105197Ssam return ENOBUFS; 869105197Ssam } 870105197Ssam 871105197Ssam if (so->so_cred != 0 && so->so_cred->cr_uid == 0) 872105197Ssam new->priv = 1; 873105197Ssam else 874105197Ssam new->priv = 0; 875105197Ssam 876105197Ssam if ((new->sp_in = KEY_NEWSP()) == NULL) { 877105197Ssam ipsec_delpcbpolicy(new); 878105197Ssam return ENOBUFS; 879105197Ssam } 880105197Ssam new->sp_in->state = IPSEC_SPSTATE_ALIVE; 881105197Ssam new->sp_in->policy = IPSEC_POLICY_ENTRUST; 882105197Ssam 883105197Ssam if ((new->sp_out = KEY_NEWSP()) == NULL) { 884105197Ssam KEY_FREESP(&new->sp_in); 885105197Ssam ipsec_delpcbpolicy(new); 886105197Ssam return ENOBUFS; 887105197Ssam } 888105197Ssam new->sp_out->state = IPSEC_SPSTATE_ALIVE; 889105197Ssam new->sp_out->policy = IPSEC_POLICY_ENTRUST; 890105197Ssam 891105197Ssam *pcb_sp = new; 892105197Ssam 893105197Ssam return 0; 894105197Ssam} 895105197Ssam 896105197Ssam/* copy old ipsec policy into new */ 897105197Ssamint 898105197Ssamipsec_copy_policy(old, new) 899105197Ssam struct inpcbpolicy *old, *new; 900105197Ssam{ 901105197Ssam struct secpolicy *sp; 902105197Ssam 903105197Ssam sp = ipsec_deepcopy_policy(old->sp_in); 904105197Ssam if (sp) { 905105197Ssam KEY_FREESP(&new->sp_in); 906105197Ssam new->sp_in = sp; 907105197Ssam } else 908105197Ssam return ENOBUFS; 909105197Ssam 910105197Ssam sp = ipsec_deepcopy_policy(old->sp_out); 911105197Ssam if (sp) { 912105197Ssam KEY_FREESP(&new->sp_out); 913105197Ssam new->sp_out = sp; 914105197Ssam } else 915105197Ssam return ENOBUFS; 916105197Ssam 917105197Ssam new->priv = old->priv; 918105197Ssam 919105197Ssam return 0; 920105197Ssam} 921105197Ssam 922105197Ssam/* deep-copy a policy in PCB */ 923105197Ssamstatic struct secpolicy * 924105197Ssamipsec_deepcopy_policy(src) 925105197Ssam struct secpolicy *src; 926105197Ssam{ 927105197Ssam struct ipsecrequest *newchain = NULL; 928105197Ssam struct ipsecrequest *p; 929105197Ssam struct ipsecrequest **q; 930105197Ssam struct ipsecrequest *r; 931105197Ssam struct secpolicy *dst; 932105197Ssam 933105197Ssam if (src == NULL) 934105197Ssam return NULL; 935105197Ssam dst = KEY_NEWSP(); 936105197Ssam if (dst == NULL) 937105197Ssam return NULL; 938105197Ssam 939105197Ssam /* 940105197Ssam * deep-copy IPsec request chain. This is required since struct 941105197Ssam * ipsecrequest is not reference counted. 942105197Ssam */ 943105197Ssam q = &newchain; 944105197Ssam for (p = src->req; p; p = p->next) { 945105197Ssam *q = (struct ipsecrequest *)malloc(sizeof(struct ipsecrequest), 946105197Ssam M_SECA, M_NOWAIT); 947105197Ssam if (*q == NULL) 948105197Ssam goto fail; 949105197Ssam bzero(*q, sizeof(**q)); 950105197Ssam (*q)->next = NULL; 951105197Ssam 952105197Ssam (*q)->saidx.proto = p->saidx.proto; 953105197Ssam (*q)->saidx.mode = p->saidx.mode; 954105197Ssam (*q)->level = p->level; 955105197Ssam (*q)->saidx.reqid = p->saidx.reqid; 956105197Ssam 957105197Ssam bcopy(&p->saidx.src, &(*q)->saidx.src, sizeof((*q)->saidx.src)); 958105197Ssam bcopy(&p->saidx.dst, &(*q)->saidx.dst, sizeof((*q)->saidx.dst)); 959105197Ssam 960105197Ssam (*q)->sav = NULL; 961105197Ssam (*q)->sp = dst; 962105197Ssam 963105197Ssam q = &((*q)->next); 964105197Ssam } 965105197Ssam 966105197Ssam dst->req = newchain; 967105197Ssam dst->state = src->state; 968105197Ssam dst->policy = src->policy; 969105197Ssam /* do not touch the refcnt fields */ 970105197Ssam 971105197Ssam return dst; 972105197Ssam 973105197Ssamfail: 974105197Ssam for (p = newchain; p; p = r) { 975105197Ssam r = p->next; 976105197Ssam free(p, M_SECA); 977105197Ssam p = NULL; 978105197Ssam } 979105197Ssam return NULL; 980105197Ssam} 981105197Ssam 982105197Ssam/* set policy and ipsec request if present. */ 983105197Ssamstatic int 984105197Ssamipsec_set_policy(pcb_sp, optname, request, len, priv) 985105197Ssam struct secpolicy **pcb_sp; 986105197Ssam int optname; 987105197Ssam caddr_t request; 988105197Ssam size_t len; 989105197Ssam int priv; 990105197Ssam{ 991105197Ssam struct sadb_x_policy *xpl; 992105197Ssam struct secpolicy *newsp = NULL; 993105197Ssam int error; 994105197Ssam 995105197Ssam /* sanity check. */ 996105197Ssam if (pcb_sp == NULL || *pcb_sp == NULL || request == NULL) 997105197Ssam return EINVAL; 998105197Ssam if (len < sizeof(*xpl)) 999105197Ssam return EINVAL; 1000105197Ssam xpl = (struct sadb_x_policy *)request; 1001105197Ssam 1002105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DUMP, 1003105197Ssam printf("ipsec_set_policy: passed policy\n"); 1004105197Ssam kdebug_sadb_x_policy((struct sadb_ext *)xpl)); 1005105197Ssam 1006105197Ssam /* check policy type */ 1007105197Ssam /* ipsec_set_policy() accepts IPSEC, ENTRUST and BYPASS. */ 1008105197Ssam if (xpl->sadb_x_policy_type == IPSEC_POLICY_DISCARD 1009105197Ssam || xpl->sadb_x_policy_type == IPSEC_POLICY_NONE) 1010105197Ssam return EINVAL; 1011105197Ssam 1012105197Ssam /* check privileged socket */ 1013105197Ssam if (priv == 0 && xpl->sadb_x_policy_type == IPSEC_POLICY_BYPASS) 1014105197Ssam return EACCES; 1015105197Ssam 1016105197Ssam /* allocation new SP entry */ 1017105197Ssam if ((newsp = key_msg2sp(xpl, len, &error)) == NULL) 1018105197Ssam return error; 1019105197Ssam 1020105197Ssam newsp->state = IPSEC_SPSTATE_ALIVE; 1021105197Ssam 1022105197Ssam /* clear old SP and set new SP */ 1023105197Ssam KEY_FREESP(pcb_sp); 1024105197Ssam *pcb_sp = newsp; 1025105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DUMP, 1026105197Ssam printf("ipsec_set_policy: new policy\n"); 1027105197Ssam kdebug_secpolicy(newsp)); 1028105197Ssam 1029105197Ssam return 0; 1030105197Ssam} 1031105197Ssam 1032105197Ssamstatic int 1033105197Ssamipsec_get_policy(pcb_sp, mp) 1034105197Ssam struct secpolicy *pcb_sp; 1035105197Ssam struct mbuf **mp; 1036105197Ssam{ 1037105197Ssam 1038105197Ssam /* sanity check. */ 1039105197Ssam if (pcb_sp == NULL || mp == NULL) 1040105197Ssam return EINVAL; 1041105197Ssam 1042105197Ssam *mp = key_sp2msg(pcb_sp); 1043105197Ssam if (!*mp) { 1044105197Ssam ipseclog((LOG_DEBUG, "ipsec_get_policy: No more memory.\n")); 1045105197Ssam return ENOBUFS; 1046105197Ssam } 1047105197Ssam 1048105197Ssam (*mp)->m_type = MT_DATA; 1049105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DUMP, 1050105197Ssam printf("ipsec_get_policy:\n"); 1051105197Ssam kdebug_mbuf(*mp)); 1052105197Ssam 1053105197Ssam return 0; 1054105197Ssam} 1055105197Ssam 1056105197Ssamint 1057105197Ssamipsec4_set_policy(inp, optname, request, len, priv) 1058105197Ssam struct inpcb *inp; 1059105197Ssam int optname; 1060105197Ssam caddr_t request; 1061105197Ssam size_t len; 1062105197Ssam int priv; 1063105197Ssam{ 1064105197Ssam struct sadb_x_policy *xpl; 1065105197Ssam struct secpolicy **pcb_sp; 1066105197Ssam 1067105197Ssam /* sanity check. */ 1068105197Ssam if (inp == NULL || request == NULL) 1069105197Ssam return EINVAL; 1070105197Ssam if (len < sizeof(*xpl)) 1071105197Ssam return EINVAL; 1072105197Ssam xpl = (struct sadb_x_policy *)request; 1073105197Ssam 1074105197Ssam /* select direction */ 1075105197Ssam switch (xpl->sadb_x_policy_dir) { 1076105197Ssam case IPSEC_DIR_INBOUND: 1077105197Ssam pcb_sp = &inp->inp_sp->sp_in; 1078105197Ssam break; 1079105197Ssam case IPSEC_DIR_OUTBOUND: 1080105197Ssam pcb_sp = &inp->inp_sp->sp_out; 1081105197Ssam break; 1082105197Ssam default: 1083105197Ssam ipseclog((LOG_ERR, "ipsec4_set_policy: invalid direction=%u\n", 1084105197Ssam xpl->sadb_x_policy_dir)); 1085105197Ssam return EINVAL; 1086105197Ssam } 1087105197Ssam 1088105197Ssam return ipsec_set_policy(pcb_sp, optname, request, len, priv); 1089105197Ssam} 1090105197Ssam 1091105197Ssamint 1092105197Ssamipsec4_get_policy(inp, request, len, mp) 1093105197Ssam struct inpcb *inp; 1094105197Ssam caddr_t request; 1095105197Ssam size_t len; 1096105197Ssam struct mbuf **mp; 1097105197Ssam{ 1098105197Ssam struct sadb_x_policy *xpl; 1099105197Ssam struct secpolicy *pcb_sp; 1100105197Ssam 1101105197Ssam /* sanity check. */ 1102105197Ssam if (inp == NULL || request == NULL || mp == NULL) 1103105197Ssam return EINVAL; 1104105197Ssam KASSERT(inp->inp_sp != NULL, ("ipsec4_get_policy: null inp_sp")); 1105105197Ssam if (len < sizeof(*xpl)) 1106105197Ssam return EINVAL; 1107105197Ssam xpl = (struct sadb_x_policy *)request; 1108105197Ssam 1109105197Ssam /* select direction */ 1110105197Ssam switch (xpl->sadb_x_policy_dir) { 1111105197Ssam case IPSEC_DIR_INBOUND: 1112105197Ssam pcb_sp = inp->inp_sp->sp_in; 1113105197Ssam break; 1114105197Ssam case IPSEC_DIR_OUTBOUND: 1115105197Ssam pcb_sp = inp->inp_sp->sp_out; 1116105197Ssam break; 1117105197Ssam default: 1118105197Ssam ipseclog((LOG_ERR, "ipsec4_set_policy: invalid direction=%u\n", 1119105197Ssam xpl->sadb_x_policy_dir)); 1120105197Ssam return EINVAL; 1121105197Ssam } 1122105197Ssam 1123105197Ssam return ipsec_get_policy(pcb_sp, mp); 1124105197Ssam} 1125105197Ssam 1126105197Ssam/* delete policy in PCB */ 1127105197Ssamint 1128105197Ssamipsec4_delete_pcbpolicy(inp) 1129105197Ssam struct inpcb *inp; 1130105197Ssam{ 1131105197Ssam KASSERT(inp != NULL, ("ipsec4_delete_pcbpolicy: null inp")); 1132105197Ssam 1133105197Ssam if (inp->inp_sp == NULL) 1134105197Ssam return 0; 1135105197Ssam 1136105197Ssam if (inp->inp_sp->sp_in != NULL) 1137105197Ssam KEY_FREESP(&inp->inp_sp->sp_in); 1138105197Ssam 1139105197Ssam if (inp->inp_sp->sp_out != NULL) 1140105197Ssam KEY_FREESP(&inp->inp_sp->sp_out); 1141105197Ssam 1142105197Ssam ipsec_delpcbpolicy(inp->inp_sp); 1143105197Ssam inp->inp_sp = NULL; 1144105197Ssam 1145105197Ssam return 0; 1146105197Ssam} 1147105197Ssam 1148105197Ssam#ifdef INET6 1149105197Ssamint 1150105197Ssamipsec6_set_policy(in6p, optname, request, len, priv) 1151105197Ssam struct in6pcb *in6p; 1152105197Ssam int optname; 1153105197Ssam caddr_t request; 1154105197Ssam size_t len; 1155105197Ssam int priv; 1156105197Ssam{ 1157105197Ssam struct sadb_x_policy *xpl; 1158105197Ssam struct secpolicy **pcb_sp; 1159105197Ssam 1160105197Ssam /* sanity check. */ 1161105197Ssam if (in6p == NULL || request == NULL) 1162105197Ssam return EINVAL; 1163105197Ssam if (len < sizeof(*xpl)) 1164105197Ssam return EINVAL; 1165105197Ssam xpl = (struct sadb_x_policy *)request; 1166105197Ssam 1167105197Ssam /* select direction */ 1168105197Ssam switch (xpl->sadb_x_policy_dir) { 1169105197Ssam case IPSEC_DIR_INBOUND: 1170105197Ssam pcb_sp = &in6p->in6p_sp->sp_in; 1171105197Ssam break; 1172105197Ssam case IPSEC_DIR_OUTBOUND: 1173105197Ssam pcb_sp = &in6p->in6p_sp->sp_out; 1174105197Ssam break; 1175105197Ssam default: 1176105197Ssam ipseclog((LOG_ERR, "ipsec6_set_policy: invalid direction=%u\n", 1177105197Ssam xpl->sadb_x_policy_dir)); 1178105197Ssam return EINVAL; 1179105197Ssam } 1180105197Ssam 1181105197Ssam return ipsec_set_policy(pcb_sp, optname, request, len, priv); 1182105197Ssam} 1183105197Ssam 1184105197Ssamint 1185105197Ssamipsec6_get_policy(in6p, request, len, mp) 1186105197Ssam struct in6pcb *in6p; 1187105197Ssam caddr_t request; 1188105197Ssam size_t len; 1189105197Ssam struct mbuf **mp; 1190105197Ssam{ 1191105197Ssam struct sadb_x_policy *xpl; 1192105197Ssam struct secpolicy *pcb_sp; 1193105197Ssam 1194105197Ssam /* sanity check. */ 1195105197Ssam if (in6p == NULL || request == NULL || mp == NULL) 1196105197Ssam return EINVAL; 1197105197Ssam KASSERT(in6p->in6p_sp != NULL, ("ipsec6_get_policy: null in6p_sp")); 1198105197Ssam if (len < sizeof(*xpl)) 1199105197Ssam return EINVAL; 1200105197Ssam xpl = (struct sadb_x_policy *)request; 1201105197Ssam 1202105197Ssam /* select direction */ 1203105197Ssam switch (xpl->sadb_x_policy_dir) { 1204105197Ssam case IPSEC_DIR_INBOUND: 1205105197Ssam pcb_sp = in6p->in6p_sp->sp_in; 1206105197Ssam break; 1207105197Ssam case IPSEC_DIR_OUTBOUND: 1208105197Ssam pcb_sp = in6p->in6p_sp->sp_out; 1209105197Ssam break; 1210105197Ssam default: 1211105197Ssam ipseclog((LOG_ERR, "ipsec6_set_policy: invalid direction=%u\n", 1212105197Ssam xpl->sadb_x_policy_dir)); 1213105197Ssam return EINVAL; 1214105197Ssam } 1215105197Ssam 1216105197Ssam return ipsec_get_policy(pcb_sp, mp); 1217105197Ssam} 1218105197Ssam 1219105197Ssamint 1220105197Ssamipsec6_delete_pcbpolicy(in6p) 1221105197Ssam struct in6pcb *in6p; 1222105197Ssam{ 1223105197Ssam KASSERT(in6p != NULL, ("ipsec6_delete_pcbpolicy: null in6p")); 1224105197Ssam 1225105197Ssam if (in6p->in6p_sp == NULL) 1226105197Ssam return 0; 1227105197Ssam 1228105197Ssam if (in6p->in6p_sp->sp_in != NULL) 1229105197Ssam KEY_FREESP(&in6p->in6p_sp->sp_in); 1230105197Ssam 1231105197Ssam if (in6p->in6p_sp->sp_out != NULL) 1232105197Ssam KEY_FREESP(&in6p->in6p_sp->sp_out); 1233105197Ssam 1234105197Ssam ipsec_delpcbpolicy(in6p->in6p_sp); 1235105197Ssam in6p->in6p_sp = NULL; 1236105197Ssam 1237105197Ssam return 0; 1238105197Ssam} 1239105197Ssam#endif 1240105197Ssam 1241105197Ssam/* 1242105197Ssam * return current level. 1243105197Ssam * Either IPSEC_LEVEL_USE or IPSEC_LEVEL_REQUIRE are always returned. 1244105197Ssam */ 1245105197Ssamu_int 1246105197Ssamipsec_get_reqlevel(isr) 1247105197Ssam struct ipsecrequest *isr; 1248105197Ssam{ 1249105197Ssam u_int level = 0; 1250105197Ssam u_int esp_trans_deflev, esp_net_deflev; 1251105197Ssam u_int ah_trans_deflev, ah_net_deflev; 1252105197Ssam 1253105197Ssam KASSERT(isr != NULL && isr->sp != NULL, 1254105197Ssam ("ipsec_get_reqlevel: null argument")); 1255105197Ssam KASSERT(isr->sp->spidx.src.sa.sa_family == isr->sp->spidx.dst.sa.sa_family, 1256105197Ssam ("ipsec_get_reqlevel: af family mismatch, src %u, dst %u", 1257105197Ssam isr->sp->spidx.src.sa.sa_family, 1258105197Ssam isr->sp->spidx.dst.sa.sa_family)); 1259105197Ssam 1260105197Ssam/* XXX note that we have ipseclog() expanded here - code sync issue */ 1261105197Ssam#define IPSEC_CHECK_DEFAULT(lev) \ 1262105197Ssam (((lev) != IPSEC_LEVEL_USE && (lev) != IPSEC_LEVEL_REQUIRE \ 1263105197Ssam && (lev) != IPSEC_LEVEL_UNIQUE) \ 1264105197Ssam ? (ipsec_debug \ 1265105197Ssam ? log(LOG_INFO, "fixed system default level " #lev ":%d->%d\n",\ 1266105197Ssam (lev), IPSEC_LEVEL_REQUIRE) \ 1267105197Ssam : 0), \ 1268105197Ssam (lev) = IPSEC_LEVEL_REQUIRE, \ 1269105197Ssam (lev) \ 1270105197Ssam : (lev)) 1271105197Ssam 1272105197Ssam /* set default level */ 1273105197Ssam switch (((struct sockaddr *)&isr->sp->spidx.src)->sa_family) { 1274105197Ssam#ifdef INET 1275105197Ssam case AF_INET: 1276105197Ssam esp_trans_deflev = IPSEC_CHECK_DEFAULT(ip4_esp_trans_deflev); 1277105197Ssam esp_net_deflev = IPSEC_CHECK_DEFAULT(ip4_esp_net_deflev); 1278105197Ssam ah_trans_deflev = IPSEC_CHECK_DEFAULT(ip4_ah_trans_deflev); 1279105197Ssam ah_net_deflev = IPSEC_CHECK_DEFAULT(ip4_ah_net_deflev); 1280105197Ssam break; 1281105197Ssam#endif 1282105197Ssam#ifdef INET6 1283105197Ssam case AF_INET6: 1284105197Ssam esp_trans_deflev = IPSEC_CHECK_DEFAULT(ip6_esp_trans_deflev); 1285105197Ssam esp_net_deflev = IPSEC_CHECK_DEFAULT(ip6_esp_net_deflev); 1286105197Ssam ah_trans_deflev = IPSEC_CHECK_DEFAULT(ip6_ah_trans_deflev); 1287105197Ssam ah_net_deflev = IPSEC_CHECK_DEFAULT(ip6_ah_net_deflev); 1288105197Ssam break; 1289105197Ssam#endif /* INET6 */ 1290105197Ssam default: 1291105197Ssam panic("key_get_reqlevel: unknown af %u", 1292105197Ssam isr->sp->spidx.src.sa.sa_family); 1293105197Ssam } 1294105197Ssam 1295105197Ssam#undef IPSEC_CHECK_DEFAULT 1296105197Ssam 1297105197Ssam /* set level */ 1298105197Ssam switch (isr->level) { 1299105197Ssam case IPSEC_LEVEL_DEFAULT: 1300105197Ssam switch (isr->saidx.proto) { 1301105197Ssam case IPPROTO_ESP: 1302105197Ssam if (isr->saidx.mode == IPSEC_MODE_TUNNEL) 1303105197Ssam level = esp_net_deflev; 1304105197Ssam else 1305105197Ssam level = esp_trans_deflev; 1306105197Ssam break; 1307105197Ssam case IPPROTO_AH: 1308105197Ssam if (isr->saidx.mode == IPSEC_MODE_TUNNEL) 1309105197Ssam level = ah_net_deflev; 1310105197Ssam else 1311105197Ssam level = ah_trans_deflev; 1312105197Ssam case IPPROTO_IPCOMP: 1313105197Ssam /* 1314105197Ssam * we don't really care, as IPcomp document says that 1315105197Ssam * we shouldn't compress small packets 1316105197Ssam */ 1317105197Ssam level = IPSEC_LEVEL_USE; 1318105197Ssam break; 1319105197Ssam default: 1320105197Ssam panic("ipsec_get_reqlevel: " 1321105197Ssam "Illegal protocol defined %u\n", 1322105197Ssam isr->saidx.proto); 1323105197Ssam } 1324105197Ssam break; 1325105197Ssam 1326105197Ssam case IPSEC_LEVEL_USE: 1327105197Ssam case IPSEC_LEVEL_REQUIRE: 1328105197Ssam level = isr->level; 1329105197Ssam break; 1330105197Ssam case IPSEC_LEVEL_UNIQUE: 1331105197Ssam level = IPSEC_LEVEL_REQUIRE; 1332105197Ssam break; 1333105197Ssam 1334105197Ssam default: 1335105197Ssam panic("ipsec_get_reqlevel: Illegal IPsec level %u\n", 1336105197Ssam isr->level); 1337105197Ssam } 1338105197Ssam 1339105197Ssam return level; 1340105197Ssam} 1341105197Ssam 1342105197Ssam/* 1343105197Ssam * Check security policy requirements against the actual 1344105197Ssam * packet contents. Return one if the packet should be 1345105197Ssam * reject as "invalid"; otherwiser return zero to have the 1346105197Ssam * packet treated as "valid". 1347105197Ssam * 1348105197Ssam * OUT: 1349105197Ssam * 0: valid 1350105197Ssam * 1: invalid 1351105197Ssam */ 1352105197Ssamint 1353105197Ssamipsec_in_reject(struct secpolicy *sp, struct mbuf *m) 1354105197Ssam{ 1355105197Ssam struct ipsecrequest *isr; 1356105197Ssam int need_auth; 1357105197Ssam 1358105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DATA, 1359105197Ssam printf("ipsec_in_reject: using SP\n"); 1360105197Ssam kdebug_secpolicy(sp)); 1361105197Ssam 1362105197Ssam /* check policy */ 1363105197Ssam switch (sp->policy) { 1364105197Ssam case IPSEC_POLICY_DISCARD: 1365105197Ssam return 1; 1366105197Ssam case IPSEC_POLICY_BYPASS: 1367105197Ssam case IPSEC_POLICY_NONE: 1368105197Ssam return 0; 1369105197Ssam } 1370105197Ssam 1371105197Ssam KASSERT(sp->policy == IPSEC_POLICY_IPSEC, 1372105197Ssam ("ipsec_in_reject: invalid policy %u", sp->policy)); 1373105197Ssam 1374105197Ssam /* XXX should compare policy against ipsec header history */ 1375105197Ssam 1376105197Ssam need_auth = 0; 1377105197Ssam for (isr = sp->req; isr != NULL; isr = isr->next) { 1378105197Ssam if (ipsec_get_reqlevel(isr) != IPSEC_LEVEL_REQUIRE) 1379105197Ssam continue; 1380105197Ssam switch (isr->saidx.proto) { 1381105197Ssam case IPPROTO_ESP: 1382105197Ssam if ((m->m_flags & M_DECRYPTED) == 0) { 1383105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DUMP, 1384105197Ssam printf("ipsec_in_reject: ESP m_flags:%x\n", 1385105197Ssam m->m_flags)); 1386105197Ssam return 1; 1387105197Ssam } 1388105197Ssam 1389105197Ssam if (!need_auth && 1390105197Ssam isr->sav != NULL && 1391105197Ssam isr->sav->tdb_authalgxform != NULL && 1392105197Ssam (m->m_flags & M_AUTHIPDGM) == 0) { 1393105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DUMP, 1394105197Ssam printf("ipsec_in_reject: ESP/AH m_flags:%x\n", 1395105197Ssam m->m_flags)); 1396105197Ssam return 1; 1397105197Ssam } 1398105197Ssam break; 1399105197Ssam case IPPROTO_AH: 1400105197Ssam need_auth = 1; 1401105197Ssam if ((m->m_flags & M_AUTHIPHDR) == 0) { 1402105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DUMP, 1403105197Ssam printf("ipsec_in_reject: AH m_flags:%x\n", 1404105197Ssam m->m_flags)); 1405105197Ssam return 1; 1406105197Ssam } 1407105197Ssam break; 1408105197Ssam case IPPROTO_IPCOMP: 1409105197Ssam /* 1410105197Ssam * we don't really care, as IPcomp document 1411105197Ssam * says that we shouldn't compress small 1412105197Ssam * packets, IPComp policy should always be 1413105197Ssam * treated as being in "use" level. 1414105197Ssam */ 1415105197Ssam break; 1416105197Ssam } 1417105197Ssam } 1418105197Ssam return 0; /* valid */ 1419105197Ssam} 1420105197Ssam 1421105197Ssam/* 1422105197Ssam * Check AH/ESP integrity. 1423105197Ssam * This function is called from tcp_input(), udp_input(), 1424105197Ssam * and {ah,esp}4_input for tunnel mode 1425105197Ssam */ 1426105197Ssamint 1427105197Ssamipsec4_in_reject(m, inp) 1428105197Ssam struct mbuf *m; 1429105197Ssam struct inpcb *inp; 1430105197Ssam{ 1431105197Ssam struct secpolicy *sp; 1432105197Ssam int error; 1433105197Ssam int result; 1434105197Ssam 1435105197Ssam KASSERT(m != NULL, ("ipsec4_in_reject_so: null mbuf")); 1436105197Ssam 1437105197Ssam /* get SP for this packet. 1438105197Ssam * When we are called from ip_forward(), we call 1439105197Ssam * ipsec_getpolicybyaddr() with IP_FORWARDING flag. 1440105197Ssam */ 1441105197Ssam if (inp == NULL) 1442105197Ssam sp = ipsec_getpolicybyaddr(m, IPSEC_DIR_INBOUND, IP_FORWARDING, &error); 1443105197Ssam else 1444105197Ssam sp = ipsec_getpolicybysock(m, IPSEC_DIR_INBOUND, inp, &error); 1445105197Ssam 1446105197Ssam if (sp != NULL) { 1447105197Ssam result = ipsec_in_reject(sp, m); 1448105197Ssam if (result) 1449105197Ssam newipsecstat.ips_in_polvio++; 1450105197Ssam KEY_FREESP(&sp); 1451105197Ssam } else { 1452105197Ssam result = 0; /* XXX should be panic ? 1453105197Ssam * -> No, there may be error. */ 1454105197Ssam } 1455105197Ssam return result; 1456105197Ssam} 1457105197Ssam 1458105197Ssam#ifdef INET6 1459105197Ssam/* 1460105197Ssam * Check AH/ESP integrity. 1461105197Ssam * This function is called from tcp6_input(), udp6_input(), 1462105197Ssam * and {ah,esp}6_input for tunnel mode 1463105197Ssam */ 1464105197Ssamint 1465105197Ssamipsec6_in_reject(m, inp) 1466105197Ssam struct mbuf *m; 1467105197Ssam struct inpcb *inp; 1468105197Ssam{ 1469105197Ssam struct secpolicy *sp = NULL; 1470105197Ssam int error; 1471105197Ssam int result; 1472105197Ssam 1473105197Ssam /* sanity check */ 1474105197Ssam if (m == NULL) 1475105197Ssam return 0; /* XXX should be panic ? */ 1476105197Ssam 1477105197Ssam /* get SP for this packet. 1478105197Ssam * When we are called from ip_forward(), we call 1479105197Ssam * ipsec_getpolicybyaddr() with IP_FORWARDING flag. 1480105197Ssam */ 1481105197Ssam if (inp == NULL) 1482105197Ssam sp = ipsec_getpolicybyaddr(m, IPSEC_DIR_INBOUND, IP_FORWARDING, &error); 1483105197Ssam else 1484105197Ssam sp = ipsec_getpolicybysock(m, IPSEC_DIR_INBOUND, inp, &error); 1485105197Ssam 1486105197Ssam if (sp != NULL) { 1487105197Ssam result = ipsec_in_reject(sp, m); 1488105197Ssam if (result) 1489105197Ssam newipsecstat.ips_in_polvio++; 1490105197Ssam KEY_FREESP(&sp); 1491105197Ssam } else { 1492105197Ssam result = 0; 1493105197Ssam } 1494105197Ssam return result; 1495105197Ssam} 1496105197Ssam#endif 1497105197Ssam 1498105197Ssam/* 1499105197Ssam * compute the byte size to be occupied by IPsec header. 1500105197Ssam * in case it is tunneled, it includes the size of outer IP header. 1501105197Ssam * NOTE: SP passed is free in this function. 1502105197Ssam */ 1503105197Ssamstatic size_t 1504105197Ssamipsec_hdrsiz(struct secpolicy *sp) 1505105197Ssam{ 1506105197Ssam struct ipsecrequest *isr; 1507105197Ssam size_t siz; 1508105197Ssam 1509105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DATA, 1510105197Ssam printf("ipsec_hdrsiz: using SP\n"); 1511105197Ssam kdebug_secpolicy(sp)); 1512105197Ssam 1513105197Ssam switch (sp->policy) { 1514105197Ssam case IPSEC_POLICY_DISCARD: 1515105197Ssam case IPSEC_POLICY_BYPASS: 1516105197Ssam case IPSEC_POLICY_NONE: 1517105197Ssam return 0; 1518105197Ssam } 1519105197Ssam 1520105197Ssam KASSERT(sp->policy == IPSEC_POLICY_IPSEC, 1521105197Ssam ("ipsec_hdrsiz: invalid policy %u", sp->policy)); 1522105197Ssam 1523105197Ssam siz = 0; 1524105197Ssam for (isr = sp->req; isr != NULL; isr = isr->next) { 1525105197Ssam size_t clen = 0; 1526105197Ssam 1527105197Ssam switch (isr->saidx.proto) { 1528105197Ssam case IPPROTO_ESP: 1529105197Ssam clen = esp_hdrsiz(isr->sav); 1530105197Ssam break; 1531105197Ssam case IPPROTO_AH: 1532105197Ssam clen = ah_hdrsiz(isr->sav); 1533105197Ssam break; 1534105197Ssam case IPPROTO_IPCOMP: 1535105197Ssam clen = sizeof(struct ipcomp); 1536105197Ssam break; 1537105197Ssam } 1538105197Ssam 1539105197Ssam if (isr->saidx.mode == IPSEC_MODE_TUNNEL) { 1540105197Ssam switch (isr->saidx.dst.sa.sa_family) { 1541105197Ssam case AF_INET: 1542105197Ssam clen += sizeof(struct ip); 1543105197Ssam break; 1544105197Ssam#ifdef INET6 1545105197Ssam case AF_INET6: 1546105197Ssam clen += sizeof(struct ip6_hdr); 1547105197Ssam break; 1548105197Ssam#endif 1549105197Ssam default: 1550105197Ssam ipseclog((LOG_ERR, "ipsec_hdrsiz: " 1551105197Ssam "unknown AF %d in IPsec tunnel SA\n", 1552105197Ssam ((struct sockaddr *)&isr->saidx.dst)->sa_family)); 1553105197Ssam break; 1554105197Ssam } 1555105197Ssam } 1556105197Ssam siz += clen; 1557105197Ssam } 1558105197Ssam 1559105197Ssam return siz; 1560105197Ssam} 1561105197Ssam 1562105197Ssam/* This function is called from ip_forward() and ipsec4_hdrsize_tcp(). */ 1563105197Ssamsize_t 1564105197Ssamipsec4_hdrsiz(m, dir, inp) 1565105197Ssam struct mbuf *m; 1566105197Ssam u_int dir; 1567105197Ssam struct inpcb *inp; 1568105197Ssam{ 1569105197Ssam struct secpolicy *sp; 1570105197Ssam int error; 1571105197Ssam size_t size; 1572105197Ssam 1573105197Ssam KASSERT(m != NULL, ("ipsec4_hdrsiz: null mbuf")); 1574105197Ssam KASSERT(inp == NULL || inp->inp_socket != NULL, 1575105197Ssam ("ipsec4_hdrsize: socket w/o inpcb")); 1576105197Ssam 1577105197Ssam /* get SP for this packet. 1578105197Ssam * When we are called from ip_forward(), we call 1579105197Ssam * ipsec_getpolicybyaddr() with IP_FORWARDING flag. 1580105197Ssam */ 1581105197Ssam if (inp == NULL) 1582105197Ssam sp = ipsec_getpolicybyaddr(m, dir, IP_FORWARDING, &error); 1583105197Ssam else 1584105197Ssam sp = ipsec_getpolicybysock(m, dir, inp, &error); 1585105197Ssam 1586105197Ssam if (sp != NULL) { 1587105197Ssam size = ipsec_hdrsiz(sp); 1588105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DATA, 1589105197Ssam printf("ipsec4_hdrsiz: size:%lu.\n", 1590105197Ssam (unsigned long)size)); 1591105197Ssam 1592105197Ssam KEY_FREESP(&sp); 1593105197Ssam } else { 1594105197Ssam size = 0; /* XXX should be panic ? */ 1595105197Ssam } 1596105197Ssam return size; 1597105197Ssam} 1598105197Ssam 1599105197Ssam#ifdef INET6 1600105197Ssam/* This function is called from ipsec6_hdrsize_tcp(), 1601105197Ssam * and maybe from ip6_forward.() 1602105197Ssam */ 1603105197Ssamsize_t 1604105197Ssamipsec6_hdrsiz(m, dir, in6p) 1605105197Ssam struct mbuf *m; 1606105197Ssam u_int dir; 1607105197Ssam struct in6pcb *in6p; 1608105197Ssam{ 1609105197Ssam struct secpolicy *sp; 1610105197Ssam int error; 1611105197Ssam size_t size; 1612105197Ssam 1613105197Ssam KASSERT(m != NULL, ("ipsec6_hdrsiz: null mbuf")); 1614105197Ssam KASSERT(in6p == NULL || in6p->in6p_socket != NULL, 1615105197Ssam ("ipsec6_hdrsize: socket w/o inpcb")); 1616105197Ssam 1617105197Ssam /* get SP for this packet */ 1618105197Ssam /* XXX Is it right to call with IP_FORWARDING. */ 1619105197Ssam if (in6p == NULL) 1620105197Ssam sp = ipsec_getpolicybyaddr(m, dir, IP_FORWARDING, &error); 1621105197Ssam else 1622105197Ssam sp = ipsec_getpolicybysock(m, dir, in6p, &error); 1623105197Ssam 1624105197Ssam if (sp == NULL) 1625105197Ssam return 0; 1626105197Ssam size = ipsec_hdrsiz(sp); 1627105197Ssam KEYDEBUG(KEYDEBUG_IPSEC_DATA, 1628105197Ssam printf("ipsec6_hdrsiz: size:%lu.\n", (unsigned long)size)); 1629105197Ssam KEY_FREESP(&sp); 1630105197Ssam 1631105197Ssam return size; 1632105197Ssam} 1633105197Ssam#endif /*INET6*/ 1634105197Ssam 1635105197Ssam/* 1636105197Ssam * Check the variable replay window. 1637105197Ssam * ipsec_chkreplay() performs replay check before ICV verification. 1638105197Ssam * ipsec_updatereplay() updates replay bitmap. This must be called after 1639105197Ssam * ICV verification (it also performs replay check, which is usually done 1640105197Ssam * beforehand). 1641105197Ssam * 0 (zero) is returned if packet disallowed, 1 if packet permitted. 1642105197Ssam * 1643105197Ssam * based on RFC 2401. 1644105197Ssam */ 1645105197Ssamint 1646105197Ssamipsec_chkreplay(seq, sav) 1647105197Ssam u_int32_t seq; 1648105197Ssam struct secasvar *sav; 1649105197Ssam{ 1650105197Ssam const struct secreplay *replay; 1651105197Ssam u_int32_t diff; 1652105197Ssam int fr; 1653105197Ssam u_int32_t wsizeb; /* constant: bits of window size */ 1654105197Ssam int frlast; /* constant: last frame */ 1655105197Ssam 1656105197Ssam#if 0 1657105197Ssam SPLASSERT(net, "ipsec_chkreplay"); 1658105197Ssam#endif 1659105197Ssam 1660105197Ssam KASSERT(sav != NULL, ("ipsec_chkreplay: Null SA")); 1661105197Ssam KASSERT(sav->replay != NULL, ("ipsec_chkreplay: Null replay state")); 1662105197Ssam 1663105197Ssam replay = sav->replay; 1664105197Ssam 1665105197Ssam if (replay->wsize == 0) 1666105197Ssam return 1; /* no need to check replay. */ 1667105197Ssam 1668105197Ssam /* constant */ 1669105197Ssam frlast = replay->wsize - 1; 1670105197Ssam wsizeb = replay->wsize << 3; 1671105197Ssam 1672105197Ssam /* sequence number of 0 is invalid */ 1673105197Ssam if (seq == 0) 1674105197Ssam return 0; 1675105197Ssam 1676105197Ssam /* first time is always okay */ 1677105197Ssam if (replay->count == 0) 1678105197Ssam return 1; 1679105197Ssam 1680105197Ssam if (seq > replay->lastseq) { 1681105197Ssam /* larger sequences are okay */ 1682105197Ssam return 1; 1683105197Ssam } else { 1684105197Ssam /* seq is equal or less than lastseq. */ 1685105197Ssam diff = replay->lastseq - seq; 1686105197Ssam 1687105197Ssam /* over range to check, i.e. too old or wrapped */ 1688105197Ssam if (diff >= wsizeb) 1689105197Ssam return 0; 1690105197Ssam 1691105197Ssam fr = frlast - diff / 8; 1692105197Ssam 1693105197Ssam /* this packet already seen ? */ 1694105197Ssam if ((replay->bitmap)[fr] & (1 << (diff % 8))) 1695105197Ssam return 0; 1696105197Ssam 1697105197Ssam /* out of order but good */ 1698105197Ssam return 1; 1699105197Ssam } 1700105197Ssam} 1701105197Ssam 1702105197Ssam/* 1703105197Ssam * check replay counter whether to update or not. 1704105197Ssam * OUT: 0: OK 1705105197Ssam * 1: NG 1706105197Ssam */ 1707105197Ssamint 1708105197Ssamipsec_updatereplay(seq, sav) 1709105197Ssam u_int32_t seq; 1710105197Ssam struct secasvar *sav; 1711105197Ssam{ 1712105197Ssam struct secreplay *replay; 1713105197Ssam u_int32_t diff; 1714105197Ssam int fr; 1715105197Ssam u_int32_t wsizeb; /* constant: bits of window size */ 1716105197Ssam int frlast; /* constant: last frame */ 1717105197Ssam 1718105197Ssam#if 0 1719105197Ssam SPLASSERT(net, "ipsec_updatereplay"); 1720105197Ssam#endif 1721105197Ssam 1722105197Ssam KASSERT(sav != NULL, ("ipsec_updatereplay: Null SA")); 1723105197Ssam KASSERT(sav->replay != NULL, ("ipsec_updatereplay: Null replay state")); 1724105197Ssam 1725105197Ssam replay = sav->replay; 1726105197Ssam 1727105197Ssam if (replay->wsize == 0) 1728105197Ssam goto ok; /* no need to check replay. */ 1729105197Ssam 1730105197Ssam /* constant */ 1731105197Ssam frlast = replay->wsize - 1; 1732105197Ssam wsizeb = replay->wsize << 3; 1733105197Ssam 1734105197Ssam /* sequence number of 0 is invalid */ 1735105197Ssam if (seq == 0) 1736105197Ssam return 1; 1737105197Ssam 1738105197Ssam /* first time */ 1739105197Ssam if (replay->count == 0) { 1740105197Ssam replay->lastseq = seq; 1741105197Ssam bzero(replay->bitmap, replay->wsize); 1742105197Ssam (replay->bitmap)[frlast] = 1; 1743105197Ssam goto ok; 1744105197Ssam } 1745105197Ssam 1746105197Ssam if (seq > replay->lastseq) { 1747105197Ssam /* seq is larger than lastseq. */ 1748105197Ssam diff = seq - replay->lastseq; 1749105197Ssam 1750105197Ssam /* new larger sequence number */ 1751105197Ssam if (diff < wsizeb) { 1752105197Ssam /* In window */ 1753105197Ssam /* set bit for this packet */ 1754105197Ssam vshiftl(replay->bitmap, diff, replay->wsize); 1755105197Ssam (replay->bitmap)[frlast] |= 1; 1756105197Ssam } else { 1757105197Ssam /* this packet has a "way larger" */ 1758105197Ssam bzero(replay->bitmap, replay->wsize); 1759105197Ssam (replay->bitmap)[frlast] = 1; 1760105197Ssam } 1761105197Ssam replay->lastseq = seq; 1762105197Ssam 1763105197Ssam /* larger is good */ 1764105197Ssam } else { 1765105197Ssam /* seq is equal or less than lastseq. */ 1766105197Ssam diff = replay->lastseq - seq; 1767105197Ssam 1768105197Ssam /* over range to check, i.e. too old or wrapped */ 1769105197Ssam if (diff >= wsizeb) 1770105197Ssam return 1; 1771105197Ssam 1772105197Ssam fr = frlast - diff / 8; 1773105197Ssam 1774105197Ssam /* this packet already seen ? */ 1775105197Ssam if ((replay->bitmap)[fr] & (1 << (diff % 8))) 1776105197Ssam return 1; 1777105197Ssam 1778105197Ssam /* mark as seen */ 1779105197Ssam (replay->bitmap)[fr] |= (1 << (diff % 8)); 1780105197Ssam 1781105197Ssam /* out of order but good */ 1782105197Ssam } 1783105197Ssam 1784105197Ssamok: 1785105197Ssam if (replay->count == ~0) { 1786105197Ssam 1787105197Ssam /* set overflow flag */ 1788105197Ssam replay->overflow++; 1789105197Ssam 1790105197Ssam /* don't increment, no more packets accepted */ 1791105197Ssam if ((sav->flags & SADB_X_EXT_CYCSEQ) == 0) 1792105197Ssam return 1; 1793105197Ssam 1794105197Ssam ipseclog((LOG_WARNING, "replay counter made %d cycle. %s\n", 1795105197Ssam replay->overflow, ipsec_logsastr(sav))); 1796105197Ssam } 1797105197Ssam 1798105197Ssam replay->count++; 1799105197Ssam 1800105197Ssam return 0; 1801105197Ssam} 1802105197Ssam 1803105197Ssam/* 1804105197Ssam * shift variable length bunffer to left. 1805105197Ssam * IN: bitmap: pointer to the buffer 1806105197Ssam * nbit: the number of to shift. 1807105197Ssam * wsize: buffer size (bytes). 1808105197Ssam */ 1809105197Ssamstatic void 1810105197Ssamvshiftl(bitmap, nbit, wsize) 1811105197Ssam unsigned char *bitmap; 1812105197Ssam int nbit, wsize; 1813105197Ssam{ 1814105197Ssam int s, j, i; 1815105197Ssam unsigned char over; 1816105197Ssam 1817105197Ssam for (j = 0; j < nbit; j += 8) { 1818105197Ssam s = (nbit - j < 8) ? (nbit - j): 8; 1819105197Ssam bitmap[0] <<= s; 1820105197Ssam for (i = 1; i < wsize; i++) { 1821105197Ssam over = (bitmap[i] >> (8 - s)); 1822105197Ssam bitmap[i] <<= s; 1823105197Ssam bitmap[i-1] |= over; 1824105197Ssam } 1825105197Ssam } 1826105197Ssam 1827105197Ssam return; 1828105197Ssam} 1829105197Ssam 1830105197Ssam/* Return a printable string for the IPv4 address. */ 1831105197Ssamstatic char * 1832105197Ssaminet_ntoa4(struct in_addr ina) 1833105197Ssam{ 1834105197Ssam static char buf[4][4 * sizeof "123" + 4]; 1835105197Ssam unsigned char *ucp = (unsigned char *) &ina; 1836105197Ssam static int i = 3; 1837105197Ssam 1838105197Ssam i = (i + 1) % 4; 1839105197Ssam sprintf(buf[i], "%d.%d.%d.%d", ucp[0] & 0xff, ucp[1] & 0xff, 1840105197Ssam ucp[2] & 0xff, ucp[3] & 0xff); 1841105197Ssam return (buf[i]); 1842105197Ssam} 1843105197Ssam 1844105197Ssam/* Return a printable string for the address. */ 1845105197Ssamchar * 1846105197Ssamipsec_address(union sockaddr_union* sa) 1847105197Ssam{ 1848105197Ssam switch (sa->sa.sa_family) { 1849105197Ssam#if INET 1850105197Ssam case AF_INET: 1851105197Ssam return inet_ntoa4(sa->sin.sin_addr); 1852105197Ssam#endif /* INET */ 1853105197Ssam 1854105197Ssam#if INET6 1855105197Ssam case AF_INET6: 1856105197Ssam return ip6_sprintf(&sa->sin6.sin6_addr); 1857105197Ssam#endif /* INET6 */ 1858105197Ssam 1859105197Ssam default: 1860105197Ssam return "(unknown address family)"; 1861105197Ssam } 1862105197Ssam} 1863105197Ssam 1864105197Ssamconst char * 1865105197Ssamipsec_logsastr(sav) 1866105197Ssam struct secasvar *sav; 1867105197Ssam{ 1868105197Ssam static char buf[256]; 1869105197Ssam char *p; 1870105197Ssam struct secasindex *saidx = &sav->sah->saidx; 1871105197Ssam 1872105197Ssam KASSERT(saidx->src.sa.sa_family == saidx->dst.sa.sa_family, 1873105197Ssam ("ipsec_logsastr: address family mismatch")); 1874105197Ssam 1875105197Ssam p = buf; 1876105197Ssam snprintf(buf, sizeof(buf), "SA(SPI=%u ", (u_int32_t)ntohl(sav->spi)); 1877105197Ssam while (p && *p) 1878105197Ssam p++; 1879105197Ssam /* NB: only use ipsec_address on one address at a time */ 1880105197Ssam snprintf(p, sizeof (buf) - (p - buf), "src=%s ", 1881105197Ssam ipsec_address(&saidx->src)); 1882105197Ssam while (p && *p) 1883105197Ssam p++; 1884105197Ssam snprintf(p, sizeof (buf) - (p - buf), "dst=%s)", 1885105197Ssam ipsec_address(&saidx->dst)); 1886105197Ssam 1887105197Ssam return buf; 1888105197Ssam} 1889105197Ssam 1890105197Ssamvoid 1891105197Ssamipsec_dumpmbuf(m) 1892105197Ssam struct mbuf *m; 1893105197Ssam{ 1894105197Ssam int totlen; 1895105197Ssam int i; 1896105197Ssam u_char *p; 1897105197Ssam 1898105197Ssam totlen = 0; 1899105197Ssam printf("---\n"); 1900105197Ssam while (m) { 1901105197Ssam p = mtod(m, u_char *); 1902105197Ssam for (i = 0; i < m->m_len; i++) { 1903105197Ssam printf("%02x ", p[i]); 1904105197Ssam totlen++; 1905105197Ssam if (totlen % 16 == 0) 1906105197Ssam printf("\n"); 1907105197Ssam } 1908105197Ssam m = m->m_next; 1909105197Ssam } 1910105197Ssam if (totlen % 16 != 0) 1911105197Ssam printf("\n"); 1912105197Ssam printf("---\n"); 1913105197Ssam} 1914105197Ssam 1915105197Ssam/* XXX this stuff doesn't belong here... */ 1916105197Ssam 1917105197Ssamstatic struct xformsw* xforms = NULL; 1918105197Ssam 1919105197Ssam/* 1920105197Ssam * Register a transform; typically at system startup. 1921105197Ssam */ 1922105197Ssamvoid 1923105197Ssamxform_register(struct xformsw* xsp) 1924105197Ssam{ 1925105197Ssam xsp->xf_next = xforms; 1926105197Ssam xforms = xsp; 1927105197Ssam} 1928105197Ssam 1929105197Ssam/* 1930105197Ssam * Initialize transform support in an sav. 1931105197Ssam */ 1932105197Ssamint 1933105197Ssamxform_init(struct secasvar *sav, int xftype) 1934105197Ssam{ 1935105197Ssam struct xformsw *xsp; 1936105197Ssam 1937105197Ssam for (xsp = xforms; xsp; xsp = xsp->xf_next) 1938105197Ssam if (xsp->xf_type == xftype) 1939105197Ssam return (*xsp->xf_init)(sav, xsp); 1940105197Ssam return EINVAL; 1941105197Ssam} 1942