freebsd-src/sys/netipsec/keysock.c

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

356 lines
8.6 KiB
C
Raw Normal View History

/* $KAME: keysock.c,v 1.25 2001/08/13 20:07:41 itojun Exp $ */
/*-
* SPDX-License-Identifier: BSD-3-Clause
*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "opt_ipsec.h"
/* This code has derived from sys/net/rtsock.c on FreeBSD2.2.5 */
#include <sys/types.h>
#include <sys/param.h>
#include <sys/domain.h>
#include <sys/errno.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/mutex.h>
#include <sys/priv.h>
#include <sys/protosw.h>
#include <sys/signalvar.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/sysctl.h>
#include <sys/systm.h>
#include <net/if.h>
#include <net/vnet.h>
#include <netinet/in.h>
#include <net/pfkeyv2.h>
#include <netipsec/key.h>
#include <netipsec/keysock.h>
#include <netipsec/key_debug.h>
#include <netipsec/ipsec.h>
#include <machine/stdarg.h>
static struct mtx keysock_mtx;
MTX_SYSINIT(keysock, &keysock_mtx, "key socket pcb list", MTX_DEF);
#define KEYSOCK_LOCK() mtx_lock(&keysock_mtx)
#define KEYSOCK_UNLOCK() mtx_unlock(&keysock_mtx)
VNET_DEFINE_STATIC(LIST_HEAD(, keycb), keycb_list) =
LIST_HEAD_INITIALIZER(keycb_list);
#define V_keycb_list VNET(keycb_list)
Build on Jeff Roberson's linker-set based dynamic per-CPU allocator (DPCPU), as suggested by Peter Wemm, and implement a new per-virtual network stack memory allocator. Modify vnet to use the allocator instead of monolithic global container structures (vinet, ...). This change solves many binary compatibility problems associated with VIMAGE, and restores ELF symbols for virtualized global variables. Each virtualized global variable exists as a "reference copy", and also once per virtual network stack. Virtualized global variables are tagged at compile-time, placing the in a special linker set, which is loaded into a contiguous region of kernel memory. Virtualized global variables in the base kernel are linked as normal, but those in modules are copied and relocated to a reserved portion of the kernel's vnet region with the help of a the kernel linker. Virtualized global variables exist in per-vnet memory set up when the network stack instance is created, and are initialized statically from the reference copy. Run-time access occurs via an accessor macro, which converts from the current vnet and requested symbol to a per-vnet address. When "options VIMAGE" is not compiled into the kernel, normal global ELF symbols will be used instead and indirection is avoided. This change restores static initialization for network stack global variables, restores support for non-global symbols and types, eliminates the need for many subsystem constructors, eliminates large per-subsystem structures that caused many binary compatibility issues both for monitoring applications (netstat) and kernel modules, removes the per-function INIT_VNET_*() macros throughout the stack, eliminates the need for vnet_symmap ksym(2) munging, and eliminates duplicate definitions of virtualized globals under VIMAGE_GLOBALS. Bump __FreeBSD_version and update UPDATING. Portions submitted by: bz Reviewed by: bz, zec Discussed with: gnn, jamie, jeff, jhb, julian, sam Suggested by: peter Approved by: re (kensmith)
2009-07-14 22:48:30 +00:00
static struct sockaddr key_src = { 2, PF_KEY, };
static int key_sendup0(struct keycb *, struct mbuf *, int);
VNET_PCPUSTAT_DEFINE(struct pfkeystat, pfkeystat);
VNET_PCPUSTAT_SYSINIT(pfkeystat);
#ifdef VIMAGE
VNET_PCPUSTAT_SYSUNINIT(pfkeystat);
#endif /* VIMAGE */
Build on Jeff Roberson's linker-set based dynamic per-CPU allocator (DPCPU), as suggested by Peter Wemm, and implement a new per-virtual network stack memory allocator. Modify vnet to use the allocator instead of monolithic global container structures (vinet, ...). This change solves many binary compatibility problems associated with VIMAGE, and restores ELF symbols for virtualized global variables. Each virtualized global variable exists as a "reference copy", and also once per virtual network stack. Virtualized global variables are tagged at compile-time, placing the in a special linker set, which is loaded into a contiguous region of kernel memory. Virtualized global variables in the base kernel are linked as normal, but those in modules are copied and relocated to a reserved portion of the kernel's vnet region with the help of a the kernel linker. Virtualized global variables exist in per-vnet memory set up when the network stack instance is created, and are initialized statically from the reference copy. Run-time access occurs via an accessor macro, which converts from the current vnet and requested symbol to a per-vnet address. When "options VIMAGE" is not compiled into the kernel, normal global ELF symbols will be used instead and indirection is avoided. This change restores static initialization for network stack global variables, restores support for non-global symbols and types, eliminates the need for many subsystem constructors, eliminates large per-subsystem structures that caused many binary compatibility issues both for monitoring applications (netstat) and kernel modules, removes the per-function INIT_VNET_*() macros throughout the stack, eliminates the need for vnet_symmap ksym(2) munging, and eliminates duplicate definitions of virtualized globals under VIMAGE_GLOBALS. Bump __FreeBSD_version and update UPDATING. Portions submitted by: bz Reviewed by: bz, zec Discussed with: gnn, jamie, jeff, jhb, julian, sam Suggested by: peter Approved by: re (kensmith)
2009-07-14 22:48:30 +00:00
static int
key_send(struct socket *so, int flags, struct mbuf *m,
struct sockaddr *nam, struct mbuf *control, struct thread *td)
{
struct sadb_msg *msg;
int len, error = 0;
if ((flags & PRUS_OOB) || control != NULL) {
m_freem(m);
if (control != NULL)
m_freem(control);
return (EOPNOTSUPP);
}
PFKEYSTAT_INC(out_total);
PFKEYSTAT_ADD(out_bytes, m->m_pkthdr.len);
len = m->m_pkthdr.len;
if (len < sizeof(struct sadb_msg)) {
PFKEYSTAT_INC(out_tooshort);
error = EINVAL;
goto end;
}
if (m->m_len < sizeof(struct sadb_msg)) {
if ((m = m_pullup(m, sizeof(struct sadb_msg))) == NULL) {
PFKEYSTAT_INC(out_nomem);
error = ENOBUFS;
goto end;
}
}
M_ASSERTPKTHDR(m);
Merge projects/ipsec into head/. Small summary ------------- o Almost all IPsec releated code was moved into sys/netipsec. o New kernel modules added: ipsec.ko and tcpmd5.ko. New kernel option IPSEC_SUPPORT added. It enables support for loading and unloading of ipsec.ko and tcpmd5.ko kernel modules. o IPSEC_NAT_T option was removed. Now NAT-T support is enabled by default. The UDP_ENCAP_ESPINUDP_NON_IKE encapsulation type support was removed. Added TCP/UDP checksum handling for inbound packets that were decapsulated by transport mode SAs. setkey(8) modified to show run-time NAT-T configuration of SA. o New network pseudo interface if_ipsec(4) added. For now it is build as part of ipsec.ko module (or with IPSEC kernel). It implements IPsec virtual tunnels to create route-based VPNs. o The network stack now invokes IPsec functions using special methods. The only one header file <netipsec/ipsec_support.h> should be included to declare all the needed things to work with IPsec. o All IPsec protocols handlers (ESP/AH/IPCOMP protosw) were removed. Now these protocols are handled directly via IPsec methods. o TCP_SIGNATURE support was reworked to be more close to RFC. o PF_KEY SADB was reworked: - now all security associations stored in the single SPI namespace, and all SAs MUST have unique SPI. - several hash tables added to speed up lookups in SADB. - SADB now uses rmlock to protect access, and concurrent threads can do SA lookups in the same time. - many PF_KEY message handlers were reworked to reflect changes in SADB. - SADB_UPDATE message was extended to support new PF_KEY headers: SADB_X_EXT_NEW_ADDRESS_SRC and SADB_X_EXT_NEW_ADDRESS_DST. They can be used by IKE daemon to change SA addresses. o ipsecrequest and secpolicy structures were cardinally changed to avoid locking protection for ipsecrequest. Now we support only limited number (4) of bundled SAs, but they are supported for both INET and INET6. o INPCB security policy cache was introduced. Each PCB now caches used security policies to avoid SP lookup for each packet. o For inbound security policies added the mode, when the kernel does check for full history of applied IPsec transforms. o References counting rules for security policies and security associations were changed. The proper SA locking added into xform code. o xform code was also changed. Now it is possible to unregister xforms. tdb_xxx structures were changed and renamed to reflect changes in SADB/SPDB, and changed rules for locking and refcounting. Reviewed by: gnn, wblock Obtained from: Yandex LLC Relnotes: yes Sponsored by: Yandex LLC Differential Revision: https://reviews.freebsd.org/D9352
2017-02-06 08:49:57 +00:00
KEYDBG(KEY_DUMP, kdebug_mbuf(m));
msg = mtod(m, struct sadb_msg *);
PFKEYSTAT_INC(out_msgtype[msg->sadb_msg_type]);
if (len != PFKEY_UNUNIT64(msg->sadb_msg_len)) {
PFKEYSTAT_INC(out_invlen);
error = EINVAL;
goto end;
}
error = key_parse(m, so);
m = NULL;
end:
if (m != NULL)
m_freem(m);
return (error);
}
/*
* send message to the socket.
*/
static int
key_sendup0(struct keycb *kp, struct mbuf *m, int promisc)
{
if (promisc) {
struct sadb_msg *pmsg;
M_PREPEND(m, sizeof(struct sadb_msg), M_NOWAIT);
if (m == NULL) {
PFKEYSTAT_INC(in_nomem);
return (ENOBUFS);
}
pmsg = mtod(m, struct sadb_msg *);
bzero(pmsg, sizeof(*pmsg));
pmsg->sadb_msg_version = PF_KEY_V2;
pmsg->sadb_msg_type = SADB_X_PROMISC;
pmsg->sadb_msg_len = PFKEY_UNIT64(m->m_pkthdr.len);
/* pid and seq? */
PFKEYSTAT_INC(in_msgtype[pmsg->sadb_msg_type]);
}
if (!sbappendaddr(&kp->kp_socket->so_rcv, &key_src, m, NULL)) {
PFKEYSTAT_INC(in_nomem);
m_freem(m);
soroverflow(kp->kp_socket);
return (ENOBUFS);
}
sorwakeup(kp->kp_socket);
return (0);
}
/* so can be NULL if target != KEY_SENDUP_ONE */
int
key_sendup_mbuf(struct socket *so, struct mbuf *m, int target)
{
struct mbuf *n;
struct keycb *kp;
int error = 0;
KASSERT(m != NULL, ("NULL mbuf pointer was passed."));
KASSERT(so != NULL || target != KEY_SENDUP_ONE,
("NULL socket pointer was passed."));
KASSERT(target == KEY_SENDUP_ONE || target == KEY_SENDUP_ALL ||
target == KEY_SENDUP_REGISTERED, ("Wrong target %d", target));
PFKEYSTAT_INC(in_total);
PFKEYSTAT_ADD(in_bytes, m->m_pkthdr.len);
if (m->m_len < sizeof(struct sadb_msg)) {
m = m_pullup(m, sizeof(struct sadb_msg));
if (m == NULL) {
PFKEYSTAT_INC(in_nomem);
return (ENOBUFS);
}
}
if (m->m_len >= sizeof(struct sadb_msg)) {
struct sadb_msg *msg;
msg = mtod(m, struct sadb_msg *);
PFKEYSTAT_INC(in_msgtype[msg->sadb_msg_type]);
}
KEYSOCK_LOCK();
LIST_FOREACH(kp, &V_keycb_list, kp_next) {
/*
* If you are in promiscuous mode, and when you get broadcasted
* reply, you'll get two PF_KEY messages.
* (based on pf_key@inner.net message on 14 Oct 1998)
*/
if (kp->kp_promisc) {
n = m_copym(m, 0, M_COPYALL, M_NOWAIT);
if (n != NULL)
key_sendup0(kp, n, 1);
else
PFKEYSTAT_INC(in_nomem);
}
/* the exact target will be processed later */
if (so != NULL && so->so_pcb == kp)
continue;
if (target == KEY_SENDUP_ONE || (target ==
KEY_SENDUP_REGISTERED && kp->kp_registered == 0))
continue;
/* KEY_SENDUP_ALL + KEY_SENDUP_REGISTERED */
n = m_copym(m, 0, M_COPYALL, M_NOWAIT);
if (n == NULL) {
PFKEYSTAT_INC(in_nomem);
/* Try send to another socket */
continue;
}
if (key_sendup0(kp, n, 0) == 0)
PFKEYSTAT_INC(in_msgtarget[target]);
}
if (so) { /* KEY_SENDUP_ONE */
error = key_sendup0(so->so_pcb, m, 0);
if (error == 0)
PFKEYSTAT_INC(in_msgtarget[KEY_SENDUP_ONE]);
} else {
error = 0;
m_freem(m);
}
KEYSOCK_UNLOCK();
return (error);
}
static u_long key_sendspace = 8192;
SYSCTL_ULONG(_net_key, OID_AUTO, sendspace, CTLFLAG_RW, &key_sendspace, 0,
"Default key socket send space");
static u_long key_recvspace = 8192;
SYSCTL_ULONG(_net_key, OID_AUTO, recvspace, CTLFLAG_RW, &key_recvspace, 0,
"Default key socket receive space");
static int
key_attach(struct socket *so, int proto, struct thread *td)
{
struct keycb *kp;
int error;
KASSERT(so->so_pcb == NULL, ("key_attach: so_pcb != NULL"));
if (td != NULL) {
error = priv_check(td, PRIV_NET_RAW);
if (error != 0)
return (error);
}
error = soreserve(so, key_sendspace, key_recvspace);
if (error != 0)
return (error);
kp = malloc(sizeof(*kp), M_PCB, M_WAITOK);
kp->kp_socket = so;
kp->kp_promisc = kp->kp_registered = 0;
so->so_pcb = kp;
so->so_options |= SO_USELOOPBACK;
KEYSOCK_LOCK();
LIST_INSERT_HEAD(&V_keycb_list, kp, kp_next);
KEYSOCK_UNLOCK();
soisconnected(so);
return (0);
}
static void
key_close(struct socket *so)
{
soisdisconnected(so);
}
static void
key_detach(struct socket *so)
{
struct keycb *kp = so->so_pcb;
key_freereg(so);
KEYSOCK_LOCK();
LIST_REMOVE(kp, kp_next);
KEYSOCK_UNLOCK();
free(kp, M_PCB);
so->so_pcb = NULL;
}
static int
key_shutdown(struct socket *so, enum shutdown_how how)
{
/*
* Note: key socket marks itself as connected through its lifetime.
*/
switch (how) {
case SHUT_RD:
socantrcvmore(so);
sbrelease(so, SO_RCV);
break;
case SHUT_RDWR:
socantrcvmore(so);
sbrelease(so, SO_RCV);
/* FALLTHROUGH */
case SHUT_WR:
socantsendmore(so);
}
return (0);
}
SYSCTL_NODE(_net, PF_KEY, key, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
"Key Family");
static struct protosw keysw = {
.pr_type = SOCK_RAW,
.pr_protocol = PF_KEY_V2,
.pr_flags = PR_ATOMIC | PR_ADDR,
.pr_abort = key_close,
.pr_attach = key_attach,
.pr_detach = key_detach,
.pr_send = key_send,
.pr_shutdown = key_shutdown,
.pr_close = key_close,
};
static struct domain keydomain = {
.dom_family = PF_KEY,
.dom_name = "key",
.dom_nprotosw = 1,
.dom_protosw = { &keysw },
};
DOMAIN_SET(key);