NetBSD/sys/netipsec/ipsec_netbsd.c
atatat 13f8d2ce5f Dynamic sysctl.
Gone are the old kern_sysctl(), cpu_sysctl(), hw_sysctl(),
vfs_sysctl(), etc, routines, along with sysctl_int() et al.  Now all
nodes are registered with the tree, and nodes can be added (or
removed) easily, and I/O to and from the tree is handled generically.

Since the nodes are registered with the tree, the mapping from name to
number (and back again) can now be discovered, instead of having to be
hard coded.  Adding new nodes to the tree is likewise much simpler --
the new infrastructure handles almost all the work for simple types,
and just about anything else can be done with a small helper function.

All existing nodes are where they were before (numerically speaking),
so all existing consumers of sysctl information should notice no
difference.

PS - I'm sorry, but there's a distinct lack of documentation at the
moment.  I'm working on sysctl(3/8/9) right now, and I promise to
watch out for buses.
2003-12-04 19:38:21 +00:00

447 lines
13 KiB
C

/* $NetBSD: ipsec_netbsd.c,v 1.4 2003/12/04 19:38:25 atatat Exp $ */
/* $KAME: esp_input.c,v 1.60 2001/09/04 08:43:19 itojun Exp $ */
/* $KAME: ah_input.c,v 1.64 2001/09/04 08:43:19 itojun Exp $ */
/*
* 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 <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: ipsec_netbsd.c,v 1.4 2003/12/04 19:38:25 atatat Exp $");
#include "opt_inet.h"
#include "opt_ipsec.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/domain.h>
#include <sys/protosw.h>
#include <sys/socket.h>
#include <sys/errno.h>
#include <sys/time.h>
#include <sys/kernel.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <net/route.h>
#include <net/netisr.h>
#include <machine/cpu.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/in_var.h>
#include <netinet/ip.h>
#include <netinet/ip_var.h>
#include <netinet/ip_ecn.h>
#include <netinet/ip_icmp.h>
#ifdef IPSEC
#include <netkey/key.h>
#include <netkey/keydb.h>
#include <netkey/key_debug.h>
#endif
#ifdef FAST_IPSEC
#include <netipsec/ipsec.h>
#include <netipsec/key.h>
#include <netipsec/keydb.h>
#include <netipsec/key_debug.h>
#include <netipsec/ah_var.h>
#endif
#include <machine/stdarg.h>
#include <netipsec/key.h>
/* assumes that ip header and ah header are contiguous on mbuf */
void *
ah4_ctlinput(cmd, sa, v)
int cmd;
struct sockaddr *sa;
void *v;
{
struct ip *ip = v;
struct ah *ah;
struct icmp *icp;
struct secasvar *sav;
if (sa->sa_family != AF_INET ||
sa->sa_len != sizeof(struct sockaddr_in))
return NULL;
if ((unsigned)cmd >= PRC_NCMDS)
return NULL;
#ifndef notyet
(void) ip; (void) ah; (void) icp; (void) sav;
#else
if (cmd == PRC_MSGSIZE && ip_mtudisc && ip && ip->ip_v == 4) {
/*
* Check to see if we have a valid SA corresponding to
* the address in the ICMP message payload.
*/
ah = (struct ah *)((caddr_t)ip + (ip->ip_hl << 2));
if ((sav = key_allocsa(AF_INET,
(caddr_t) &ip->ip_src,
(caddr_t) &ip->ip_dst,
IPPROTO_AH, ah->ah_spi)) == NULL)
return NULL;
if (sav->state != SADB_SASTATE_MATURE &&
sav->state != SADB_SASTATE_DYING) {
key_freesav(sav);
return NULL;
}
/* XXX Further validation? */
key_freesav(sav);
/*
* Now that we've validated that we are actually communicating
* with the host indicated in the ICMP message, locate the
* ICMP header, recalculate the new MTU, and create the
* corresponding routing entry.
*/
icp = (struct icmp *)((caddr_t)ip -
offsetof(struct icmp, icmp_ip));
icmp_mtudisc(icp, ip->ip_dst);
return NULL;
}
#endif
return NULL;
}
/* assumes that ip header and esp header are contiguous on mbuf */
void *
esp4_ctlinput(cmd, sa, v)
int cmd;
struct sockaddr *sa;
void *v;
{
struct ip *ip = v;
struct esp *esp;
struct icmp *icp;
struct secasvar *sav;
if (sa->sa_family != AF_INET ||
sa->sa_len != sizeof(struct sockaddr_in))
return NULL;
if ((unsigned)cmd >= PRC_NCMDS)
return NULL;
#ifndef notyet
(void) ip; (void) esp; (void) icp; (void) sav;
#else
if (cmd == PRC_MSGSIZE && ip_mtudisc && ip && ip->ip_v == 4) {
/*
* Check to see if we have a valid SA corresponding to
* the address in the ICMP message payload.
*/
esp = (struct esp *)((caddr_t)ip + (ip->ip_hl << 2));
if ((sav = key_allocsa(AF_INET,
(caddr_t) &ip->ip_src,
(caddr_t) &ip->ip_dst,
IPPROTO_ESP, esp->esp_spi)) == NULL)
return NULL;
if (sav->state != SADB_SASTATE_MATURE &&
sav->state != SADB_SASTATE_DYING) {
key_freesav(sav);
return NULL;
}
/* XXX Further validation? */
key_freesav(sav);
/*
* Now that we've validated that we are actually communicating
* with the host indicated in the ICMP message, locate the
* ICMP header, recalculate the new MTU, and create the
* corresponding routing entry.
*/
icp = (struct icmp *)((caddr_t)ip -
offsetof(struct icmp, icmp_ip));
icmp_mtudisc(icp, ip->ip_dst);
return NULL;
}
#endif
return NULL;
}
#ifdef INET6
void
esp6_ctlinput(cmd, sa, d)
int cmd;
struct sockaddr *sa;
void *d;
{
const struct newesp *espp;
struct newesp esp;
struct ip6ctlparam *ip6cp = NULL, ip6cp1;
struct secasvar *sav;
struct ip6_hdr *ip6;
struct mbuf *m;
int off;
struct sockaddr_in6 *sa6_src, *sa6_dst;
if (sa->sa_family != AF_INET6 ||
sa->sa_len != sizeof(struct sockaddr_in6))
return;
if ((unsigned)cmd >= PRC_NCMDS)
return;
/* if the parameter is from icmp6, decode it. */
if (d != NULL) {
ip6cp = (struct ip6ctlparam *)d;
m = ip6cp->ip6c_m;
ip6 = ip6cp->ip6c_ip6;
off = ip6cp->ip6c_off;
} else {
m = NULL;
ip6 = NULL;
}
if (ip6) {
/*
* Notify the error to all possible sockets via pfctlinput2.
* Since the upper layer information (such as protocol type,
* source and destination ports) is embedded in the encrypted
* data and might have been cut, we can't directly call
* an upper layer ctlinput function. However, the pcbnotify
* function will consider source and destination addresses
* as well as the flow info value, and may be able to find
* some PCB that should be notified.
* Although pfctlinput2 will call esp6_ctlinput(), there is
* no possibility of an infinite loop of function calls,
* because we don't pass the inner IPv6 header.
*/
bzero(&ip6cp1, sizeof(ip6cp1));
ip6cp1.ip6c_src = ip6cp->ip6c_src;
pfctlinput2(cmd, sa, (void *)&ip6cp1);
/*
* Then go to special cases that need ESP header information.
* XXX: We assume that when ip6 is non NULL,
* M and OFF are valid.
*/
/* check if we can safely examine src and dst ports */
if (m->m_pkthdr.len < off + sizeof(esp))
return;
if (m->m_len < off + sizeof(esp)) {
/*
* this should be rare case,
* so we compromise on this copy...
*/
m_copydata(m, off, sizeof(esp), (caddr_t)&esp);
espp = &esp;
} else
espp = (struct newesp*)(mtod(m, caddr_t) + off);
if (cmd == PRC_MSGSIZE) {
int valid = 0;
/*
* Check to see if we have a valid SA corresponding to
* the address in the ICMP message payload.
*/
sa6_src = ip6cp->ip6c_src;
sa6_dst = (struct sockaddr_in6 *)sa;
sav = key_allocsa(AF_INET6,
(caddr_t)&sa6_src->sin6_addr,
(caddr_t)&sa6_dst->sin6_addr,
IPPROTO_ESP, espp->esp_spi);
if (sav) {
if (sav->state == SADB_SASTATE_MATURE ||
sav->state == SADB_SASTATE_DYING)
valid++;
key_freesav(sav);
}
/* XXX Further validation? */
/*
* Depending on the value of "valid" and routing table
* size (mtudisc_{hi,lo}wat), we will:
* - recalcurate the new MTU and create the
* corresponding routing entry, or
* - ignore the MTU change notification.
*/
icmp6_mtudisc_update((struct ip6ctlparam *)d, valid);
}
} else {
/* we normally notify any pcb here */
}
}
#endif /* INET6 */
/*FIXME: placebo for invalpcbcacheall. Fast-IPsec has no pcb cache? */
void ipsec_invalpcbcacheall(void);
void
ipsec_invalpcbcacheall(void)
{
}
static int
sysctl_fast_ipsec(SYSCTLFN_ARGS)
{
int error, t;
struct sysctlnode node;
node = *rnode;
t = *(int*)rnode->sysctl_data;
node.sysctl_data = &t;
error = sysctl_lookup(SYSCTLFN_CALL(&node));
if (error || newp == NULL)
return (error);
switch (rnode->sysctl_num) {
case IPSECCTL_DEF_ESP_TRANSLEV:
case IPSECCTL_DEF_ESP_NETLEV:
case IPSECCTL_DEF_AH_TRANSLEV:
case IPSECCTL_DEF_AH_NETLEV:
if (t != IPSEC_LEVEL_USE &&
t != IPSEC_LEVEL_REQUIRE)
return (EINVAL);
ipsec_invalpcbcacheall();
break;
case IPSECCTL_DEF_POLICY:
if (t != IPSEC_POLICY_DISCARD &&
t != IPSEC_POLICY_NONE)
return (EINVAL);
ipsec_invalpcbcacheall();
break;
default:
return (EINVAL);
}
*(int*)rnode->sysctl_data = t;
return (0);
}
/* XXX will need a different oid at parent */
/* @@@ i have called it "fast_ipsec" instead of "ipsec" */
SYSCTL_SETUP(sysctl_net_inet_fast_ipsec_setup, "sysctl net.inet.fast_ipsec subtree setup")
{
sysctl_createv(SYSCTL_PERMANENT,
CTLTYPE_NODE, "net", NULL,
NULL, 0, NULL, 0,
CTL_NET, CTL_EOL);
sysctl_createv(SYSCTL_PERMANENT,
CTLTYPE_NODE, "inet", NULL,
NULL, 0, NULL, 0,
CTL_NET, PF_INET, CTL_EOL);
sysctl_createv(SYSCTL_PERMANENT,
CTLTYPE_NODE, "fast_ipsec", NULL,
NULL, 0, NULL, 0,
CTL_NET, PF_INET, IPPROTO_AH, CTL_EOL);
sysctl_createv(SYSCTL_PERMANENT|SYSCTL_READWRITE,
CTLTYPE_STRUCT, "stats", NULL,
NULL, 0, &ipsecstat, sizeof(ipsecstat),
CTL_NET, PF_INET, IPPROTO_AH,
IPSECCTL_STATS, CTL_EOL);
sysctl_createv(SYSCTL_PERMANENT|SYSCTL_READWRITE,
CTLTYPE_INT, "def_policy", NULL,
sysctl_fast_ipsec, 0, &ip4_def_policy.policy, 0,
CTL_NET, PF_INET, IPPROTO_AH,
IPSECCTL_DEF_POLICY, CTL_EOL);
sysctl_createv(SYSCTL_PERMANENT|SYSCTL_READWRITE,
CTLTYPE_INT, "esp_trans_deflev", NULL,
sysctl_fast_ipsec, 0, &ip4_esp_trans_deflev, 0,
CTL_NET, PF_INET, IPPROTO_AH,
IPSECCTL_DEF_ESP_TRANSLEV, CTL_EOL);
sysctl_createv(SYSCTL_PERMANENT|SYSCTL_READWRITE,
CTLTYPE_INT, "esp_net_deflev", NULL,
sysctl_fast_ipsec, 0, &ip4_esp_net_deflev, 0,
CTL_NET, PF_INET, IPPROTO_AH,
IPSECCTL_DEF_ESP_NETLEV, CTL_EOL);
sysctl_createv(SYSCTL_PERMANENT|SYSCTL_READWRITE,
CTLTYPE_INT, "ah_trans_deflev", NULL,
sysctl_fast_ipsec, 0, &ip4_ah_trans_deflev, 0,
CTL_NET, PF_INET, IPPROTO_AH,
IPSECCTL_DEF_AH_TRANSLEV, CTL_EOL);
sysctl_createv(SYSCTL_PERMANENT|SYSCTL_READWRITE,
CTLTYPE_INT, "ah_net_deflev", NULL,
sysctl_fast_ipsec, 0, &ip4_ah_net_deflev, 0,
CTL_NET, PF_INET, IPPROTO_AH,
IPSECCTL_DEF_AH_NETLEV, CTL_EOL);
sysctl_createv(SYSCTL_PERMANENT|SYSCTL_READWRITE,
CTLTYPE_INT, "ah_cleartos", NULL,
NULL, 0, &/*ip4_*/ah_cleartos, 0,
CTL_NET, PF_INET, IPPROTO_AH,
IPSECCTL_AH_CLEARTOS, CTL_EOL);
sysctl_createv(SYSCTL_PERMANENT|SYSCTL_READWRITE,
CTLTYPE_INT, "ah_offsetmask", NULL,
NULL, 0, &ip4_ah_offsetmask, 0,
CTL_NET, PF_INET, IPPROTO_AH,
IPSECCTL_AH_OFFSETMASK, CTL_EOL);
sysctl_createv(SYSCTL_PERMANENT|SYSCTL_READWRITE,
CTLTYPE_INT, "dfbit", NULL,
NULL, 0, &ip4_ipsec_dfbit, 0,
CTL_NET, PF_INET, IPPROTO_AH,
IPSECCTL_DFBIT, CTL_EOL);
sysctl_createv(SYSCTL_PERMANENT|SYSCTL_READWRITE,
CTLTYPE_INT, "ecn", NULL,
NULL, 0, &ip4_ipsec_ecn, 0,
CTL_NET, PF_INET, IPPROTO_AH,
IPSECCTL_ECN, CTL_EOL);
sysctl_createv(SYSCTL_PERMANENT|SYSCTL_READWRITE,
CTLTYPE_INT, "debug", NULL,
NULL, 0, &ipsec_debug, 0,
CTL_NET, PF_INET, IPPROTO_AH,
IPSECCTL_DEBUG, CTL_EOL);
/*
* "aliases" for the fast ipsec subtree
*/
sysctl_createv(SYSCTL_PERMANENT|SYSCTL_ALIAS,
CTLTYPE_NODE, "fast_esp", NULL,
NULL, IPPROTO_AH, NULL, 0,
CTL_NET, PF_INET, IPPROTO_ESP, CTL_EOL);
sysctl_createv(SYSCTL_PERMANENT|SYSCTL_ALIAS,
CTLTYPE_NODE, "fast_ipcomp", NULL,
NULL, IPPROTO_AH, NULL, 0,
CTL_NET, PF_INET, IPPROTO_IPCOMP, CTL_EOL);
sysctl_createv(SYSCTL_PERMANENT|SYSCTL_ALIAS,
CTLTYPE_NODE, "fast_ah", NULL,
NULL, IPPROTO_AH, NULL, 0,
CTL_NET, PF_INET, CTL_CREATE, CTL_EOL);
}