Initial import of Sam Leffler's `Fast-IPsec' from FreeBSD 4.

Fast-IPsec is a rework of the OpenBSD and KAME IPsec code, using the
OpenCryptoFramework (and thus hardware crypto accelerators) and
numerous detailed performance improvements.

This import is (aside from SPL-level names) the FreeBSD source,
imported ``as-is'' as a historical snapshot, for future maintenance
and comparison against the FreeBSD source.  For now, several minor
kernel-API differences are hidden by macros a shim file, ipsec_osdep.h,
which (aside from SPL names) can be targeted at either NetBSD or FreeBSD.
This commit is contained in:
jonathan 2003-08-13 20:06:49 +00:00
parent 2d11b64df0
commit 740290313e
28 changed files with 18699 additions and 0 deletions

57
sys/netipsec/ah.h Normal file
View File

@ -0,0 +1,57 @@
/* $NetBSD: ah.h,v 1.1 2003/08/13 20:06:49 jonathan Exp $ */
/* $FreeBSD: src/sys/netipsec/ah.h,v 1.1.4.1 2003/01/24 05:11:35 sam Exp $ */
/* $KAME: ah.h,v 1.13 2000/10/18 21:28:00 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.
*/
/*
* RFC1826/2402 authentication header.
*/
#ifndef _NETIPSEC_AH_H_
#define _NETIPSEC_AH_H_
struct ah {
u_int8_t ah_nxt; /* Next Header */
u_int8_t ah_len; /* Length of data, in 32bit */
u_int16_t ah_reserve; /* Reserved for future use */
u_int32_t ah_spi; /* Security parameter index */
/* variable size, 32bit bound*/ /* Authentication data */
};
struct newah {
u_int8_t ah_nxt; /* Next Header */
u_int8_t ah_len; /* Length of data + 1, in 32bit */
u_int16_t ah_reserve; /* Reserved for future use */
u_int32_t ah_spi; /* Security parameter index */
u_int32_t ah_seq; /* Sequence number field */
/* variable size, 32bit bound*/ /* Authentication data */
};
#endif /*_NETIPSEC_AH_H_*/

79
sys/netipsec/ah_var.h Normal file
View File

@ -0,0 +1,79 @@
/* $NetBSD: ah_var.h,v 1.1 2003/08/13 20:06:49 jonathan Exp $ */
/* $FreeBSD: src/sys/netipsec/ah_var.h,v 1.1.4.1 2003/01/24 05:11:35 sam Exp $ */
/* $OpenBSD: ip_ah.h,v 1.29 2002/06/09 16:26:10 itojun Exp $ */
/*
* The authors of this code are John Ioannidis (ji@tla.org),
* Angelos D. Keromytis (kermit@csd.uch.gr) and
* Niels Provos (provos@physnet.uni-hamburg.de).
*
* The original version of this code was written by John Ioannidis
* for BSD/OS in Athens, Greece, in November 1995.
*
* Ported to OpenBSD and NetBSD, with additional transforms, in December 1996,
* by Angelos D. Keromytis.
*
* Additional transforms and features in 1997 and 1998 by Angelos D. Keromytis
* and Niels Provos.
*
* Additional features in 1999 by Angelos D. Keromytis.
*
* Copyright (C) 1995, 1996, 1997, 1998, 1999 John Ioannidis,
* Angelos D. Keromytis and Niels Provos.
* Copyright (c) 2001 Angelos D. Keromytis.
*
* Permission to use, copy, and modify this software with or without fee
* is hereby granted, provided that this entire notice is included in
* all copies of any software which is or includes a copy or
* modification of this software.
* You may use this code under the GNU public license if you so wish. Please
* contribute changes back to the authors under this freer than GPL license
* so that we may further the use of strong encryption without limitations to
* all.
*
* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY
* REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE
* MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR
* PURPOSE.
*/
#ifndef _NETIPSEC_AH_VAR_H_
#define _NETIPSEC_AH_VAR_H_
/*
* These define the algorithm indices into the histogram. They're
* presently based on the PF_KEY v2 protocol values which is bogus;
* they should be decoupled from the protocol at which time we can
* pack them and reduce the size of the array to a minimum.
*/
#define AH_ALG_MAX 16
struct ahstat {
u_int32_t ahs_hdrops; /* Packet shorter than header shows */
u_int32_t ahs_nopf; /* Protocol family not supported */
u_int32_t ahs_notdb;
u_int32_t ahs_badkcr;
u_int32_t ahs_badauth;
u_int32_t ahs_noxform;
u_int32_t ahs_qfull;
u_int32_t ahs_wrap;
u_int32_t ahs_replay;
u_int32_t ahs_badauthl; /* Bad authenticator length */
u_int32_t ahs_input; /* Input AH packets */
u_int32_t ahs_output; /* Output AH packets */
u_int32_t ahs_invalid; /* Trying to use an invalid TDB */
u_int64_t ahs_ibytes; /* Input bytes */
u_int64_t ahs_obytes; /* Output bytes */
u_int32_t ahs_toobig; /* Packet got larger than IP_MAXPACKET */
u_int32_t ahs_pdrops; /* Packet blocked due to policy */
u_int32_t ahs_crypto; /* Crypto processing failure */
u_int32_t ahs_tunnel; /* Tunnel sanity check failure */
u_int32_t ahs_hist[AH_ALG_MAX]; /* Per-algorithm op count */
};
#ifdef _KERNEL
extern int ah_enable;
extern int ah_cleartos;
extern struct ahstat ahstat;
#endif /* _KERNEL */
#endif /*_NETIPSEC_AH_VAR_H_*/

70
sys/netipsec/esp.h Normal file
View File

@ -0,0 +1,70 @@
/* $NetBSD: esp.h,v 1.1 2003/08/13 20:06:49 jonathan Exp $ */
/* $FreeBSD: src/sys/netipsec/esp.h,v 1.1.4.1 2003/01/24 05:11:35 sam Exp $ */
/* $KAME: esp.h,v 1.16 2000/10/18 21:28:00 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.
*/
/*
* RFC1827/2406 Encapsulated Security Payload.
*/
#ifndef _NETIPSEC_ESP_H_
#define _NETIPSEC_ESP_H_
struct esp {
u_int32_t esp_spi; /* ESP */
/*variable size, 32bit bound*/ /* Initialization Vector */
/*variable size*/ /* Payload data */
/*variable size*/ /* padding */
/*8bit*/ /* pad size */
/*8bit*/ /* next header */
/*8bit*/ /* next header */
/*variable size, 32bit bound*/ /* Authentication data (new IPsec) */
};
struct newesp {
u_int32_t esp_spi; /* ESP */
u_int32_t esp_seq; /* Sequence number */
/*variable size*/ /* (IV and) Payload data */
/*variable size*/ /* padding */
/*8bit*/ /* pad size */
/*8bit*/ /* next header */
/*8bit*/ /* next header */
/*variable size, 32bit bound*/ /* Authentication data */
};
struct esptail {
u_int8_t esp_padlen; /* pad length */
u_int8_t esp_nxt; /* Next header */
/*variable size, 32bit bound*/ /* Authentication data (new IPsec)*/
};
#define ESP_ALEN 12 /* 96-bit authenticator */
#endif /*_NETIPSEC_ESP_H_*/

79
sys/netipsec/esp_var.h Normal file
View File

@ -0,0 +1,79 @@
/* $NetBSD: esp_var.h,v 1.1 2003/08/13 20:06:49 jonathan Exp $ */
/* $FreeBSD: src/sys/netipsec/esp_var.h,v 1.1.4.1 2003/01/24 05:11:35 sam Exp $ */
/* $OpenBSD: ip_esp.h,v 1.37 2002/06/09 16:26:10 itojun Exp $ */
/*
* The authors of this code are John Ioannidis (ji@tla.org),
* Angelos D. Keromytis (kermit@csd.uch.gr) and
* Niels Provos (provos@physnet.uni-hamburg.de).
*
* The original version of this code was written by John Ioannidis
* for BSD/OS in Athens, Greece, in November 1995.
*
* Ported to OpenBSD and NetBSD, with additional transforms, in December 1996,
* by Angelos D. Keromytis.
*
* Additional transforms and features in 1997 and 1998 by Angelos D. Keromytis
* and Niels Provos.
*
* Additional features in 1999 by Angelos D. Keromytis.
*
* Copyright (C) 1995, 1996, 1997, 1998, 1999 by John Ioannidis,
* Angelos D. Keromytis and Niels Provos.
* Copyright (c) 2001 Angelos D. Keromytis.
*
* Permission to use, copy, and modify this software with or without fee
* is hereby granted, provided that this entire notice is included in
* all copies of any software which is or includes a copy or
* modification of this software.
* You may use this code under the GNU public license if you so wish. Please
* contribute changes back to the authors under this freer than GPL license
* so that we may further the use of strong encryption without limitations to
* all.
*
* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY
* REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE
* MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR
* PURPOSE.
*/
#ifndef _NETIPSEC_ESP_VAR_H_
#define _NETIPSEC_ESP_VAR_H_
/*
* These define the algorithm indices into the histogram. They're
* presently based on the PF_KEY v2 protocol values which is bogus;
* they should be decoupled from the protocol at which time we can
* pack them and reduce the size of the array to a reasonable value.
*/
#define ESP_ALG_MAX 256 /* NB: could be < but skipjack is 249 */
struct espstat {
u_int32_t esps_hdrops; /* Packet shorter than header shows */
u_int32_t esps_nopf; /* Protocol family not supported */
u_int32_t esps_notdb;
u_int32_t esps_badkcr;
u_int32_t esps_qfull;
u_int32_t esps_noxform;
u_int32_t esps_badilen;
u_int32_t esps_wrap; /* Replay counter wrapped around */
u_int32_t esps_badenc; /* Bad encryption detected */
u_int32_t esps_badauth; /* Only valid for transforms with auth */
u_int32_t esps_replay; /* Possible packet replay detected */
u_int32_t esps_input; /* Input ESP packets */
u_int32_t esps_output; /* Output ESP packets */
u_int32_t esps_invalid; /* Trying to use an invalid TDB */
u_int64_t esps_ibytes; /* Input bytes */
u_int64_t esps_obytes; /* Output bytes */
u_int32_t esps_toobig; /* Packet got larger than IP_MAXPACKET */
u_int32_t esps_pdrops; /* Packet blocked due to policy */
u_int32_t esps_crypto; /* Crypto processing failure */
u_int32_t esps_tunnel; /* Tunnel sanity check failure */
u_int32_t esps_hist[ESP_ALG_MAX]; /* Per-algorithm op count */
};
#ifdef _KERNEL
extern int esp_enable;
extern struct espstat espstat;
#endif /* _KERNEL */
#endif /*_NETIPSEC_ESP_VAR_H_*/

56
sys/netipsec/ipcomp.h Normal file
View File

@ -0,0 +1,56 @@
/* $NetBSD: ipcomp.h,v 1.1 2003/08/13 20:06:50 jonathan Exp $ */
/* $FreeBSD: src/sys/netipsec/ipcomp.h,v 1.1.4.1 2003/01/24 05:11:35 sam Exp $ */
/* $KAME: ipcomp.h,v 1.8 2000/09/26 07:55:14 itojun Exp $ */
/*
* Copyright (C) 1999 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.
*/
/*
* RFC2393 IP payload compression protocol (IPComp).
*/
#ifndef _NETIPSEC_IPCOMP_H_
#define _NETIPSEC_IPCOMP_H_
struct ipcomp {
u_int8_t comp_nxt; /* Next Header */
u_int8_t comp_flags; /* reserved, must be zero */
u_int16_t comp_cpi; /* Compression parameter index */
};
#define IPCOMP_HLENGTH 4 /* Length of IPCOMP header */
/* well-known algorithm number (in CPI), from RFC2409 */
#define IPCOMP_OUI 1 /* vendor specific */
#define IPCOMP_DEFLATE 2 /* RFC2394 */
#define IPCOMP_LZS 3 /* RFC2395 */
#define IPCOMP_MAX 4
#define IPCOMP_CPI_NEGOTIATE_MIN 256
#endif /*_NETIPSEC_IPCOMP_H_*/

68
sys/netipsec/ipcomp_var.h Normal file
View File

@ -0,0 +1,68 @@
/* $NetBSD: ipcomp_var.h,v 1.1 2003/08/13 20:06:50 jonathan Exp $ */
/* $FreeBSD: src/sys/netipsec/ipcomp_var.h,v 1.1.4.1 2003/01/24 05:11:35 sam Exp $ */
/* $KAME: ipcomp.h,v 1.8 2000/09/26 07:55:14 itojun Exp $ */
/*
* Copyright (C) 1999 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.
*/
#ifndef _NETIPSEC_IPCOMP_VAR_H_
#define _NETIPSEC_IPCOMP_VAR_H_
/*
* These define the algorithm indices into the histogram. They're
* presently based on the PF_KEY v2 protocol values which is bogus;
* they should be decoupled from the protocol at which time we can
* pack them and reduce the size of the array to a minimum.
*/
#define IPCOMP_ALG_MAX 8
struct ipcompstat {
u_int32_t ipcomps_hdrops; /* Packet shorter than header shows */
u_int32_t ipcomps_nopf; /* Protocol family not supported */
u_int32_t ipcomps_notdb;
u_int32_t ipcomps_badkcr;
u_int32_t ipcomps_qfull;
u_int32_t ipcomps_noxform;
u_int32_t ipcomps_wrap;
u_int32_t ipcomps_input; /* Input IPcomp packets */
u_int32_t ipcomps_output; /* Output IPcomp packets */
u_int32_t ipcomps_invalid;/* Trying to use an invalid TDB */
u_int64_t ipcomps_ibytes; /* Input bytes */
u_int64_t ipcomps_obytes; /* Output bytes */
u_int32_t ipcomps_toobig; /* Packet got > IP_MAXPACKET */
u_int32_t ipcomps_pdrops; /* Packet blocked due to policy */
u_int32_t ipcomps_crypto; /* "Crypto" processing failure */
u_int32_t ipcomps_hist[IPCOMP_ALG_MAX];/* Per-algorithm op count */
};
#ifdef _KERNEL
extern int ipcomp_enable;
extern struct ipcompstat ipcompstat;
#endif /* _KERNEL */
#endif /*_NETIPSEC_IPCOMP_VAR_H_*/

66
sys/netipsec/ipip_var.h Normal file
View File

@ -0,0 +1,66 @@
/* $NetBSD: ipip_var.h,v 1.1 2003/08/13 20:06:50 jonathan Exp $ */
/* $FreeBSD: src/sys/netipsec/ipip_var.h,v 1.1.4.1 2003/01/24 05:11:35 sam Exp $ */
/* $OpenBSD: ip_ipip.h,v 1.5 2002/06/09 16:26:10 itojun Exp $ */
/*
* The authors of this code are John Ioannidis (ji@tla.org),
* Angelos D. Keromytis (kermit@csd.uch.gr) and
* Niels Provos (provos@physnet.uni-hamburg.de).
*
* The original version of this code was written by John Ioannidis
* for BSD/OS in Athens, Greece, in November 1995.
*
* Ported to OpenBSD and NetBSD, with additional transforms, in December 1996,
* by Angelos D. Keromytis.
*
* Additional transforms and features in 1997 and 1998 by Angelos D. Keromytis
* and Niels Provos.
*
* Additional features in 1999 by Angelos D. Keromytis.
*
* Copyright (C) 1995, 1996, 1997, 1998, 1999 by John Ioannidis,
* Angelos D. Keromytis and Niels Provos.
* Copyright (c) 2001, Angelos D. Keromytis.
*
* Permission to use, copy, and modify this software with or without fee
* is hereby granted, provided that this entire notice is included in
* all copies of any software which is or includes a copy or
* modification of this software.
* You may use this code under the GNU public license if you so wish. Please
* contribute changes back to the authors under this freer than GPL license
* so that we may further the use of strong encryption without limitations to
* all.
*
* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY
* REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE
* MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR
* PURPOSE.
*/
#ifndef _NETINET_IPIP_H_
#define _NETINET_IPIP_H_
/*
* IP-inside-IP processing.
* Not quite all the functionality of RFC-1853, but the main idea is there.
*/
struct ipipstat
{
u_int32_t ipips_ipackets; /* total input packets */
u_int32_t ipips_opackets; /* total output packets */
u_int32_t ipips_hdrops; /* packet shorter than header shows */
u_int32_t ipips_qfull;
u_int64_t ipips_ibytes;
u_int64_t ipips_obytes;
u_int32_t ipips_pdrops; /* packet dropped due to policy */
u_int32_t ipips_spoof; /* IP spoofing attempts */
u_int32_t ipips_family; /* Protocol family mismatch */
u_int32_t ipips_unspec; /* Missing tunnel endpoint address */
};
#ifdef _KERNEL
extern int ipip_allow;
extern struct ipipstat ipipstat;
#endif /* _KERNEL */
#endif /* _NETINET_IPIP_H_ */

1965
sys/netipsec/ipsec.c Normal file

File diff suppressed because it is too large Load Diff

460
sys/netipsec/ipsec.h Normal file
View File

@ -0,0 +1,460 @@
/* $NetBSD: ipsec.h,v 1.1 2003/08/13 20:06:50 jonathan Exp $ */
/* $FreeBSD: src/sys/netipsec/ipsec.h,v 1.2.4.1 2003/01/24 05:11:35 sam Exp $ */
/* $KAME: ipsec.h,v 1.53 2001/11/20 08:32:38 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.
*/
/*
* IPsec controller part.
*/
#ifndef _NETIPSEC_IPSEC_H_
#define _NETIPSEC_IPSEC_H_
#if defined(_KERNEL) && !defined(_LKM) && !defined(KLD_MODULE)
#include "opt_inet.h"
#include "opt_ipsec.h"
#endif
#include <net/pfkeyv2.h>
#include <netipsec/keydb.h>
#ifdef _KERNEL
/*
* Security Policy Index
* Ensure that both address families in the "src" and "dst" are same.
* When the value of the ul_proto is ICMPv6, the port field in "src"
* specifies ICMPv6 type, and the port field in "dst" specifies ICMPv6 code.
*/
struct secpolicyindex {
u_int8_t dir; /* direction of packet flow, see blow */
union sockaddr_union src; /* IP src address for SP */
union sockaddr_union dst; /* IP dst address for SP */
u_int8_t prefs; /* prefix length in bits for src */
u_int8_t prefd; /* prefix length in bits for dst */
u_int16_t ul_proto; /* upper layer Protocol */
#ifdef notyet
uid_t uids;
uid_t uidd;
gid_t gids;
gid_t gidd;
#endif
};
/* Security Policy Data Base */
struct secpolicy {
LIST_ENTRY(secpolicy) chain;
u_int refcnt; /* reference count */
struct secpolicyindex spidx; /* selector */
u_int32_t id; /* It's unique number on the system. */
u_int state; /* 0: dead, others: alive */
#define IPSEC_SPSTATE_DEAD 0
#define IPSEC_SPSTATE_ALIVE 1
u_int policy; /* DISCARD, NONE or IPSEC, see keyv2.h */
struct ipsecrequest *req;
/* pointer to the ipsec request tree, */
/* if policy == IPSEC else this value == NULL.*/
/*
* lifetime handler.
* the policy can be used without limitiation if both lifetime and
* validtime are zero.
* "lifetime" is passed by sadb_lifetime.sadb_lifetime_addtime.
* "validtime" is passed by sadb_lifetime.sadb_lifetime_usetime.
*/
long created; /* time created the policy */
long lastused; /* updated every when kernel sends a packet */
long lifetime; /* duration of the lifetime of this policy */
long validtime; /* duration this policy is valid without use */
};
/* Request for IPsec */
struct ipsecrequest {
struct ipsecrequest *next;
/* pointer to next structure */
/* If NULL, it means the end of chain. */
struct secasindex saidx;/* hint for search proper SA */
/* if __ss_len == 0 then no address specified.*/
u_int level; /* IPsec level defined below. */
struct secasvar *sav; /* place holder of SA for use */
struct secpolicy *sp; /* back pointer to SP */
};
/* security policy in PCB */
struct inpcbpolicy {
struct secpolicy *sp_in;
struct secpolicy *sp_out;
int priv; /* privileged socket ? */
};
/* SP acquiring list table. */
struct secspacq {
LIST_ENTRY(secspacq) chain;
struct secpolicyindex spidx;
long created; /* for lifetime */
int count; /* for lifetime */
/* XXX: here is mbuf place holder to be sent ? */
};
#endif /* _KERNEL */
/* according to IANA assignment, port 0x0000 and proto 0xff are reserved. */
#define IPSEC_PORT_ANY 0
#define IPSEC_ULPROTO_ANY 255
#define IPSEC_PROTO_ANY 255
/* mode of security protocol */
/* NOTE: DON'T use IPSEC_MODE_ANY at SPD. It's only use in SAD */
#define IPSEC_MODE_ANY 0 /* i.e. wildcard. */
#define IPSEC_MODE_TRANSPORT 1
#define IPSEC_MODE_TUNNEL 2
/*
* Direction of security policy.
* NOTE: Since INVALID is used just as flag.
* The other are used for loop counter too.
*/
#define IPSEC_DIR_ANY 0
#define IPSEC_DIR_INBOUND 1
#define IPSEC_DIR_OUTBOUND 2
#define IPSEC_DIR_MAX 3
#define IPSEC_DIR_INVALID 4
/* Policy level */
/*
* IPSEC, ENTRUST and BYPASS are allowed for setsockopt() in PCB,
* DISCARD, IPSEC and NONE are allowed for setkey() in SPD.
* DISCARD and NONE are allowed for system default.
*/
#define IPSEC_POLICY_DISCARD 0 /* discarding packet */
#define IPSEC_POLICY_NONE 1 /* through IPsec engine */
#define IPSEC_POLICY_IPSEC 2 /* do IPsec */
#define IPSEC_POLICY_ENTRUST 3 /* consulting SPD if present. */
#define IPSEC_POLICY_BYPASS 4 /* only for privileged socket. */
/* Security protocol level */
#define IPSEC_LEVEL_DEFAULT 0 /* reference to system default */
#define IPSEC_LEVEL_USE 1 /* use SA if present. */
#define IPSEC_LEVEL_REQUIRE 2 /* require SA. */
#define IPSEC_LEVEL_UNIQUE 3 /* unique SA. */
#define IPSEC_MANUAL_REQID_MAX 0x3fff
/*
* if security policy level == unique, this id
* indicate to a relative SA for use, else is
* zero.
* 1 - 0x3fff are reserved for manual keying.
* 0 are reserved for above reason. Others is
* for kernel use.
* Note that this id doesn't identify SA
* by only itself.
*/
#define IPSEC_REPLAYWSIZE 32
/* old statistics for ipsec processing */
struct ipsecstat {
u_quad_t in_success; /* succeeded inbound process */
u_quad_t in_polvio;
/* security policy violation for inbound process */
u_quad_t in_nosa; /* inbound SA is unavailable */
u_quad_t in_inval; /* inbound processing failed due to EINVAL */
u_quad_t in_nomem; /* inbound processing failed due to ENOBUFS */
u_quad_t in_badspi; /* failed getting a SPI */
u_quad_t in_ahreplay; /* AH replay check failed */
u_quad_t in_espreplay; /* ESP replay check failed */
u_quad_t in_ahauthsucc; /* AH authentication success */
u_quad_t in_ahauthfail; /* AH authentication failure */
u_quad_t in_espauthsucc; /* ESP authentication success */
u_quad_t in_espauthfail; /* ESP authentication failure */
u_quad_t in_esphist[256];
u_quad_t in_ahhist[256];
u_quad_t in_comphist[256];
u_quad_t out_success; /* succeeded outbound process */
u_quad_t out_polvio;
/* security policy violation for outbound process */
u_quad_t out_nosa; /* outbound SA is unavailable */
u_quad_t out_inval; /* outbound process failed due to EINVAL */
u_quad_t out_nomem; /* inbound processing failed due to ENOBUFS */
u_quad_t out_noroute; /* there is no route */
u_quad_t out_esphist[256];
u_quad_t out_ahhist[256];
u_quad_t out_comphist[256];
};
/* statistics for ipsec processing */
struct newipsecstat {
u_int32_t ips_in_polvio; /* input: sec policy violation */
u_int32_t ips_out_polvio; /* output: sec policy violation */
u_int32_t ips_out_nosa; /* output: SA unavailable */
u_int32_t ips_out_nomem; /* output: no memory available */
u_int32_t ips_out_noroute; /* output: no route available */
u_int32_t ips_out_inval; /* output: generic error */
u_int32_t ips_out_bundlesa; /* output: bundled SA processed */
u_int32_t ips_mbcoalesced; /* mbufs coalesced during clone */
u_int32_t ips_clcoalesced; /* clusters coalesced during clone */
u_int32_t ips_clcopied; /* clusters copied during clone */
u_int32_t ips_mbinserted; /* mbufs inserted during makespace */
/*
* Temporary statistics for performance analysis.
*/
/* See where ESP/AH/IPCOMP header land in mbuf on input */
u_int32_t ips_input_front;
u_int32_t ips_input_middle;
u_int32_t ips_input_end;
};
/*
* XXX JRS FIXME: later replace NetBSD sourcecode with an IPSECSTAT_POLVIO() macro.
* for now, map the old fields to the new fields. */
#define ipsecstat newipsecstat
#define in_polvio ips_in_polvio
#define out_polvio ips_out_polvio
#define out_inval ips_out_inval
/*
* Definitions for IPsec & Key sysctl operations.
*/
/*
* Names for IPsec & Key sysctl objects
*/
#define IPSECCTL_STATS 1 /* stats */
#define IPSECCTL_DEF_POLICY 2
#define IPSECCTL_DEF_ESP_TRANSLEV 3 /* int; ESP transport mode */
#define IPSECCTL_DEF_ESP_NETLEV 4 /* int; ESP tunnel mode */
#define IPSECCTL_DEF_AH_TRANSLEV 5 /* int; AH transport mode */
#define IPSECCTL_DEF_AH_NETLEV 6 /* int; AH tunnel mode */
#if 0 /* obsolete, do not reuse */
#define IPSECCTL_INBOUND_CALL_IKE 7
#endif
#define IPSECCTL_AH_CLEARTOS 8
#define IPSECCTL_AH_OFFSETMASK 9
#define IPSECCTL_DFBIT 10
#define IPSECCTL_ECN 11
#define IPSECCTL_DEBUG 12
#define IPSECCTL_ESP_RANDPAD 13
#define IPSECCTL_MAXID 14
#define IPSECCTL_NAMES { \
{ 0, 0 }, \
{ 0, 0 }, \
{ "def_policy", CTLTYPE_INT }, \
{ "esp_trans_deflev", CTLTYPE_INT }, \
{ "esp_net_deflev", CTLTYPE_INT }, \
{ "ah_trans_deflev", CTLTYPE_INT }, \
{ "ah_net_deflev", CTLTYPE_INT }, \
{ 0, 0 }, \
{ "ah_cleartos", CTLTYPE_INT }, \
{ "ah_offsetmask", CTLTYPE_INT }, \
{ "dfbit", CTLTYPE_INT }, \
{ "ecn", CTLTYPE_INT }, \
{ "debug", CTLTYPE_INT }, \
{ "esp_randpad", CTLTYPE_INT }, \
}
#define IPSEC6CTL_NAMES { \
{ 0, 0 }, \
{ 0, 0 }, \
{ "def_policy", CTLTYPE_INT }, \
{ "esp_trans_deflev", CTLTYPE_INT }, \
{ "esp_net_deflev", CTLTYPE_INT }, \
{ "ah_trans_deflev", CTLTYPE_INT }, \
{ "ah_net_deflev", CTLTYPE_INT }, \
{ 0, 0 }, \
{ 0, 0 }, \
{ 0, 0 }, \
{ 0, 0 }, \
{ "ecn", CTLTYPE_INT }, \
{ "debug", CTLTYPE_INT }, \
{ "esp_randpad", CTLTYPE_INT }, \
}
#ifdef _KERNEL
struct ipsec_output_state {
struct mbuf *m;
struct route *ro;
struct sockaddr *dst;
};
struct ipsec_history {
int ih_proto;
u_int32_t ih_spi;
};
extern int ipsec_debug;
extern struct newipsecstat newipsecstat;
extern struct secpolicy ip4_def_policy;
extern int ip4_esp_trans_deflev;
extern int ip4_esp_net_deflev;
extern int ip4_ah_trans_deflev;
extern int ip4_ah_net_deflev;
extern int ip4_ah_cleartos;
extern int ip4_ah_offsetmask;
extern int ip4_ipsec_dfbit;
extern int ip4_ipsec_ecn;
extern int ip4_esp_randpad;
extern int crypto_support;
#define ipseclog(x) do { if (ipsec_debug) log x; } while (0)
/* for openbsd compatibility */
#define DPRINTF(x) do { if (ipsec_debug) printf x; } while (0)
struct tdb_ident;
extern struct secpolicy *ipsec_getpolicy __P((struct tdb_ident*, u_int));
struct inpcb;
extern struct secpolicy *ipsec4_checkpolicy __P((struct mbuf *, u_int, u_int,
int *, struct inpcb *));
extern struct secpolicy *ipsec_getpolicybysock(struct mbuf *, u_int,
struct inpcb *, int *);
extern struct secpolicy * ipsec_getpolicybyaddr(struct mbuf *, u_int,
int, int *);
static __inline struct secpolicy*
ipsec4_getpolicybysock(struct mbuf *m, u_int dir, const struct socket *so, int *err)
{
panic("ipsec4_getpolicybysock");
}
static __inline int
ipsec_copy_pcbpolicy(struct inpcbpolicy *old, struct inpcbpolicy *new)
{
/*XXX do nothing */
return (0);
}
struct inpcb;
#define ipsec_init_pcbpolicy ipsec_init_policy
extern int ipsec_init_policy __P((struct socket *so, struct inpcbpolicy **));
extern int ipsec_copy_policy
__P((struct inpcbpolicy *, struct inpcbpolicy *));
extern u_int ipsec_get_reqlevel __P((struct ipsecrequest *));
extern int ipsec_in_reject __P((struct secpolicy *, struct mbuf *));
extern int ipsec4_set_policy __P((struct inpcb *inp, int optname,
caddr_t request, size_t len, int priv));
extern int ipsec4_get_policy __P((struct inpcb *inpcb, caddr_t request,
size_t len, struct mbuf **mp));
extern int ipsec4_delete_pcbpolicy __P((struct inpcb *));
extern int ipsec4_in_reject __P((struct mbuf *, struct inpcb *));
/*
* KAME ipsec4_in_reject_so(struct mbuf*, struct so) compatibility shim
*/
#define ipsec4_in_reject_so(m, _so) \
ipsec4_in_reject(m, ((_so) == NULL? NULL : sotoinpcb(_so)))
struct secas;
struct tcpcb;
extern int ipsec_chkreplay __P((u_int32_t, struct secasvar *));
extern int ipsec_updatereplay __P((u_int32_t, struct secasvar *));
extern size_t ipsec4_hdrsiz __P((struct mbuf *, u_int, struct inpcb *));
#ifdef __FreeBSD__
extern size_t ipsec_hdrsiz_tcp __P((struct tcpcb *));
#else
extern size_t ipsec4_hdrsiz_tcp __P((struct tcpcb *));
#define ipsec4_getpolicybyaddr ipsec_getpolicybyaddr
#endif
union sockaddr_union;
extern char * ipsec_address(union sockaddr_union* sa);
extern const char *ipsec_logsastr __P((struct secasvar *));
extern void ipsec_dumpmbuf __P((struct mbuf *));
/* NetBSD protosw ctlin entrypoint */
extern void *esp4_ctlinput __P((int, struct sockaddr *, void *));
extern void *ah4_ctlinput __P((int, struct sockaddr *, void *));
extern int ipsec_sysctl __P((int *, u_int, void *, size_t *, void *, size_t));
struct m_tag;
extern int ipsec4_common_input(struct mbuf *m, ...);
extern int ipsec4_common_input_cb(struct mbuf *m, struct secasvar *sav,
int skip, int protoff, struct m_tag *mt);
extern int ipsec4_process_packet __P((struct mbuf *, struct ipsecrequest *,
int, int));
extern int ipsec_process_done __P((struct mbuf *, struct ipsecrequest *));
extern struct mbuf *ipsec_copypkt __P((struct mbuf *));
extern void m_checkalignment(const char* where, struct mbuf *m0,
int off, int len);
extern struct mbuf *m_clone(struct mbuf *m0);
extern struct mbuf *m_makespace(struct mbuf *m0, int skip, int hlen, int *off);
extern caddr_t m_pad(struct mbuf *m, int n);
extern int m_striphdr(struct mbuf *m, int skip, int hlen);
/* Per-socket caching of IPsec output policy */
static __inline int ipsec_clear_socket_cache(struct mbuf *m)
{
return 0;
}
#endif /* _KERNEL */
#ifndef _KERNEL
extern caddr_t ipsec_set_policy __P((char *, int));
extern int ipsec_get_policylen __P((caddr_t));
extern char *ipsec_dump_policy __P((caddr_t, char *));
extern const char *ipsec_strerror __P((void));
#endif /* !_KERNEL */
/*
* Porting glue for impedance mismatches between {free,net,open}bsd
*/
#ifdef __FreeBSD__
#define IPSEC_ASSERT(x, y) KASSERT(x, y)
#define INITFN static
#endif
#ifdef __NetBSD__
#define IPSEC_ASSERT(x, y) KASSERT(x)
#define INITFN extern
/* External declarations of per-file init functions */
INITFN void ah_attach(void);
INITFN void esp_attach(void);
INITFN void ipcomp_attach(void);
INITFN void ipe4_attach(void);
INITFN void ipsec_attach(void);
#endif /* __NetBSD __ */
#endif /* _NETIPSEC_IPSEC_H_ */

90
sys/netipsec/ipsec6.h Normal file
View File

@ -0,0 +1,90 @@
/* $NetBSD: ipsec6.h,v 1.1 2003/08/13 20:06:50 jonathan Exp $ */
/* $FreeBSD: src/sys/netipsec/ipsec6.h,v 1.1.4.1 2003/01/24 05:11:35 sam Exp $ */
/* $KAME: ipsec.h,v 1.44 2001/03/23 08:08:47 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.
*/
/*
* IPsec controller part.
*/
#ifndef _NETIPSEC_IPSEC6_H_
#define _NETIPSEC_IPSEC6_H_
#include <net/pfkeyv2.h>
#include <netipsec/keydb.h>
#ifdef _KERNEL
extern int ip6_esp_trans_deflev;
extern int ip6_esp_net_deflev;
extern int ip6_ah_trans_deflev;
extern int ip6_ah_net_deflev;
extern int ip6_ipsec_ecn;
extern int ip6_esp_randpad;
struct inpcb;
/* KAME compatibility shims */
#define ipsec6_getpolicybyaddr ipsec_getpolicybyaddr
#define ipsec6_getpolicybysock ipsec_getpolicybysock
#define ipsec6stat newipsecstat
#define out_inval ips_out_inval
#define in_polvio ips_in_polvio
#define out_polvio ips_out_polvio
#define key_freesp(_x) KEY_FREESP(&_x)
extern int ipsec6_delete_pcbpolicy __P((struct inpcb *));
extern int ipsec6_set_policy __P((struct inpcb *inp, int optname,
caddr_t request, size_t len, int priv));
extern int ipsec6_get_policy
__P((struct inpcb *inp, caddr_t request, size_t len, struct mbuf **mp));
extern int ipsec6_in_reject __P((struct mbuf *, struct inpcb *));
struct tcp6cb;
extern size_t ipsec6_hdrsiz __P((struct mbuf *, u_int, struct inpcb *));
struct ip6_hdr;
extern const char *ipsec6_logpacketstr __P((struct ip6_hdr *, u_int32_t));
struct m_tag;
extern int ipsec6_common_input(struct mbuf **mp, int *offp, int proto);
extern int ipsec6_common_input_cb(struct mbuf *m, struct secasvar *sav,
int skip, int protoff, struct m_tag *mt);
extern void esp6_ctlinput(int, struct sockaddr *, void *);
struct ipsec_output_state;
extern int ipsec6_output_trans __P((struct ipsec_output_state *, u_char *,
struct mbuf *, struct secpolicy *, int, int *));
extern int ipsec6_output_tunnel __P((struct ipsec_output_state *,
struct secpolicy *, int));
#endif /*_KERNEL*/
#endif /*_NETIPSEC_IPSEC6_H_*/

734
sys/netipsec/ipsec_input.c Normal file
View File

@ -0,0 +1,734 @@
/* $NetBSD: ipsec_input.c,v 1.1 2003/08/13 20:06:50 jonathan Exp $ */
/* $FreeBSD: src/sys/netipsec/ipsec_input.c,v 1.2.4.1 2003/01/24 05:11:35 sam Exp $ */
/* $KAME: ipsec.c,v 1.103 2001/05/24 07:14:18 sakane Exp $ */
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: ipsec_input.c,v 1.1 2003/08/13 20:06:50 jonathan Exp $");
/*
* IPsec input processing.
*/
#include "opt_inet.h"
#include "opt_inet6.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/syslog.h>
#include <net/if.h>
#include <net/route.h>
#include <net/netisr.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/ip_var.h>
#include <netinet/in_var.h>
#include <netinet/ip6.h>
#ifdef INET6
#include <netinet6/ip6_var.h>
#endif
#include <netinet/in_pcb.h>
#ifdef INET6
#include <netinet/icmp6.h>
#endif
#include <netipsec/ipsec.h>
#ifdef INET6
#include <netipsec/ipsec6.h>
#endif
#include <netipsec/ah_var.h>
#include <netipsec/esp.h>
#include <netipsec/esp_var.h>
#include <netipsec/ipcomp_var.h>
#include <netipsec/key.h>
#include <netipsec/keydb.h>
#include <netipsec/xform.h>
#include <netinet6/ip6protosw.h>
#include <netipsec/ipsec_osdep.h>
#include <machine/stdarg.h>
#include <net/net_osdep.h>
#define IPSEC_ISTAT(p,x,y,z) ((p) == IPPROTO_ESP ? (x)++ : \
(p) == IPPROTO_AH ? (y)++ : (z)++)
/*
* ipsec_common_input gets called when an IPsec-protected packet
* is received by IPv4 or IPv6. It's job is to find the right SA
# and call the appropriate transform. The transform callback
* takes care of further processing (like ingress filtering).
*/
static int
ipsec_common_input(struct mbuf *m, int skip, int protoff, int af, int sproto)
{
union sockaddr_union dst_address;
struct secasvar *sav;
u_int32_t spi;
int s, error;
IPSEC_ISTAT(sproto, espstat.esps_input, ahstat.ahs_input,
ipcompstat.ipcomps_input);
IPSEC_ASSERT(m != NULL, ("ipsec_common_input: null packet"));
if ((sproto == IPPROTO_ESP && !esp_enable) ||
(sproto == IPPROTO_AH && !ah_enable) ||
(sproto == IPPROTO_IPCOMP && !ipcomp_enable)) {
m_freem(m);
IPSEC_ISTAT(sproto, espstat.esps_pdrops, ahstat.ahs_pdrops,
ipcompstat.ipcomps_pdrops);
return EOPNOTSUPP;
}
if (m->m_pkthdr.len - skip < 2 * sizeof (u_int32_t)) {
m_freem(m);
IPSEC_ISTAT(sproto, espstat.esps_hdrops, ahstat.ahs_hdrops,
ipcompstat.ipcomps_hdrops);
DPRINTF(("ipsec_common_input: packet too small\n"));
return EINVAL;
}
/* Retrieve the SPI from the relevant IPsec header */
if (sproto == IPPROTO_ESP)
m_copydata(m, skip, sizeof(u_int32_t), (caddr_t) &spi);
else if (sproto == IPPROTO_AH)
m_copydata(m, skip + sizeof(u_int32_t), sizeof(u_int32_t),
(caddr_t) &spi);
else if (sproto == IPPROTO_IPCOMP) {
u_int16_t cpi;
m_copydata(m, skip + sizeof(u_int16_t), sizeof(u_int16_t),
(caddr_t) &cpi);
spi = ntohl(htons(cpi));
}
/*
* Find the SA and (indirectly) call the appropriate
* kernel crypto routine. The resulting mbuf chain is a valid
* IP packet ready to go through input processing.
*/
bzero(&dst_address, sizeof (dst_address));
dst_address.sa.sa_family = af;
switch (af) {
#ifdef INET
case AF_INET:
dst_address.sin.sin_len = sizeof(struct sockaddr_in);
m_copydata(m, offsetof(struct ip, ip_dst),
sizeof(struct in_addr),
(caddr_t) &dst_address.sin.sin_addr);
break;
#endif /* INET */
#ifdef INET6
case AF_INET6:
dst_address.sin6.sin6_len = sizeof(struct sockaddr_in6);
m_copydata(m, offsetof(struct ip6_hdr, ip6_dst),
sizeof(struct in6_addr),
(caddr_t) &dst_address.sin6.sin6_addr);
break;
#endif /* INET6 */
default:
DPRINTF(("ipsec_common_input: unsupported protocol "
"family %u\n", af));
m_freem(m);
IPSEC_ISTAT(sproto, espstat.esps_nopf, ahstat.ahs_nopf,
ipcompstat.ipcomps_nopf);
return EPFNOSUPPORT;
}
s = splsoftnet();
/* NB: only pass dst since key_allocsa follows RFC2401 */
sav = KEY_ALLOCSA(&dst_address, sproto, spi);
if (sav == NULL) {
DPRINTF(("ipsec_common_input: no key association found for"
" SA %s/%08lx/%u\n",
ipsec_address(&dst_address),
(u_long) ntohl(spi), sproto));
IPSEC_ISTAT(sproto, espstat.esps_notdb, ahstat.ahs_notdb,
ipcompstat.ipcomps_notdb);
splx(s);
m_freem(m);
return ENOENT;
}
if (sav->tdb_xform == NULL) {
DPRINTF(("ipsec_common_input: attempted to use uninitialized"
" SA %s/%08lx/%u\n",
ipsec_address(&dst_address),
(u_long) ntohl(spi), sproto));
IPSEC_ISTAT(sproto, espstat.esps_noxform, ahstat.ahs_noxform,
ipcompstat.ipcomps_noxform);
KEY_FREESAV(&sav);
splx(s);
m_freem(m);
return ENXIO;
}
/*
* Call appropriate transform and return -- callback takes care of
* everything else.
*/
error = (*sav->tdb_xform->xf_input)(m, sav, skip, protoff);
KEY_FREESAV(&sav);
splx(s);
return error;
}
#ifdef INET
/*
* Common input handler for IPv4 AH, ESP, and IPCOMP.
*/
int
ipsec4_common_input(struct mbuf *m, ...)
{
va_list ap;
int off, nxt;
va_start(ap, m);
off = va_arg(ap, int);
nxt = va_arg(ap, int);
va_end(ap);
return ipsec_common_input(m, off, offsetof(struct ip, ip_p),
AF_INET, nxt);
}
/*
* IPsec input callback for INET protocols.
* This routine is called as the transform callback.
* Takes care of filtering and other sanity checks on
* the processed packet.
*/
int
ipsec4_common_input_cb(struct mbuf *m, struct secasvar *sav,
int skip, int protoff, struct m_tag *mt)
{
int prot, af, sproto;
struct ip *ip;
struct m_tag *mtag;
struct tdb_ident *tdbi;
struct secasindex *saidx;
int error;
IPSEC_SPLASSERT_SOFTNET("ipsec4_common_input_cb");
IPSEC_ASSERT(m != NULL, ("ipsec4_common_input_cb: null mbuf"));
IPSEC_ASSERT(sav != NULL, ("ipsec4_common_input_cb: null SA"));
IPSEC_ASSERT(sav->sah != NULL, ("ipsec4_common_input_cb: null SAH"));
saidx = &sav->sah->saidx;
af = saidx->dst.sa.sa_family;
IPSEC_ASSERT(af == AF_INET, ("ipsec4_common_input_cb: unexpected af %u",af));
sproto = saidx->proto;
IPSEC_ASSERT(sproto == IPPROTO_ESP || sproto == IPPROTO_AH ||
sproto == IPPROTO_IPCOMP,
("ipsec4_common_input_cb: unexpected security protocol %u",
sproto));
/* Sanity check */
if (m == NULL) {
DPRINTF(("ipsec4_common_input_cb: null mbuf"));
IPSEC_ISTAT(sproto, espstat.esps_badkcr, ahstat.ahs_badkcr,
ipcompstat.ipcomps_badkcr);
KEY_FREESAV(&sav);
return EINVAL;
}
if (skip != 0) {
/* Fix IPv4 header */
if (m->m_len < skip && (m = m_pullup(m, skip)) == NULL) {
DPRINTF(("ipsec4_common_input_cb: processing failed "
"for SA %s/%08lx\n",
ipsec_address(&sav->sah->saidx.dst),
(u_long) ntohl(sav->spi)));
IPSEC_ISTAT(sproto, espstat.esps_hdrops, ahstat.ahs_hdrops,
ipcompstat.ipcomps_hdrops);
error = ENOBUFS;
goto bad;
}
ip = mtod(m, struct ip *);
ip->ip_len = htons(m->m_pkthdr.len);
ip->ip_off = htons(ip->ip_off);
ip->ip_sum = 0;
ip->ip_sum = in_cksum(m, ip->ip_hl << 2);
} else {
ip = mtod(m, struct ip *);
}
prot = ip->ip_p;
/* IP-in-IP encapsulation */
if (prot == IPPROTO_IPIP) {
struct ip ipn;
/* ipn will now contain the inner IPv4 header */
m_copydata(m, ip->ip_hl << 2, sizeof(struct ip),
(caddr_t) &ipn);
#ifdef notyet
/* XXX PROXY address isn't recorded in SAH */
/*
* Check that the inner source address is the same as
* the proxy address, if available.
*/
if ((saidx->proxy.sa.sa_family == AF_INET &&
saidx->proxy.sin.sin_addr.s_addr !=
INADDR_ANY &&
ipn.ip_src.s_addr !=
saidx->proxy.sin.sin_addr.s_addr) ||
(saidx->proxy.sa.sa_family != AF_INET &&
saidx->proxy.sa.sa_family != 0)) {
DPRINTF(("ipsec4_common_input_cb: inner "
"source address %s doesn't correspond to "
"expected proxy source %s, SA %s/%08lx\n",
inet_ntoa4(ipn.ip_src),
ipsp_address(saidx->proxy),
ipsp_address(saidx->dst),
(u_long) ntohl(sav->spi)));
IPSEC_ISTAT(sproto, espstat.esps_pdrops,
ahstat.ahs_pdrops,
ipcompstat.ipcomps_pdrops);
error = EACCES;
goto bad;
}
#endif /*XXX*/
}
#if INET6
/* IPv6-in-IP encapsulation. */
if (prot == IPPROTO_IPV6) {
struct ip6_hdr ip6n;
/* ip6n will now contain the inner IPv6 header. */
m_copydata(m, ip->ip_hl << 2, sizeof(struct ip6_hdr),
(caddr_t) &ip6n);
#ifdef notyet
/*
* Check that the inner source address is the same as
* the proxy address, if available.
*/
if ((saidx->proxy.sa.sa_family == AF_INET6 &&
!IN6_IS_ADDR_UNSPECIFIED(&saidx->proxy.sin6.sin6_addr) &&
!IN6_ARE_ADDR_EQUAL(&ip6n.ip6_src,
&saidx->proxy.sin6.sin6_addr)) ||
(saidx->proxy.sa.sa_family != AF_INET6 &&
saidx->proxy.sa.sa_family != 0)) {
DPRINTF(("ipsec4_common_input_cb: inner "
"source address %s doesn't correspond to "
"expected proxy source %s, SA %s/%08lx\n",
ip6_sprintf(&ip6n.ip6_src),
ipsec_address(&saidx->proxy),
ipsec_address(&saidx->dst),
(u_long) ntohl(sav->spi)));
IPSEC_ISTAT(sproto, espstat.esps_pdrops,
ahstat.ahs_pdrops,
ipcompstat.ipcomps_pdrops);
error = EACCES;
goto bad;
}
#endif /*XXX*/
}
#endif /* INET6 */
/*
* Record what we've done to the packet (under what SA it was
* processed). If we've been passed an mtag, it means the packet
* was already processed by an ethernet/crypto combo card and
* thus has a tag attached with all the right information, but
* with a PACKET_TAG_IPSEC_IN_CRYPTO_DONE as opposed to
* PACKET_TAG_IPSEC_IN_DONE type; in that case, just change the type.
*/
if (mt == NULL && sproto != IPPROTO_IPCOMP) {
mtag = m_tag_get(PACKET_TAG_IPSEC_IN_DONE,
sizeof(struct tdb_ident), M_NOWAIT);
if (mtag == NULL) {
DPRINTF(("ipsec4_common_input_cb: failed to get tag\n"));
IPSEC_ISTAT(sproto, espstat.esps_hdrops,
ahstat.ahs_hdrops, ipcompstat.ipcomps_hdrops);
error = ENOMEM;
goto bad;
}
tdbi = (struct tdb_ident *)(mtag + 1);
bcopy(&saidx->dst, &tdbi->dst, saidx->dst.sa.sa_len);
tdbi->proto = sproto;
tdbi->spi = sav->spi;
m_tag_prepend(m, mtag);
} else {
mt->m_tag_id = PACKET_TAG_IPSEC_IN_DONE;
/* XXX do we need to mark m_flags??? */
}
key_sa_recordxfer(sav, m); /* record data transfer */
/*
* Re-dispatch via software interrupt.
*/
if (!IF_HANDOFF(&ipintrq, m, NULL)) {
IPSEC_ISTAT(sproto, espstat.esps_qfull, ahstat.ahs_qfull,
ipcompstat.ipcomps_qfull);
DPRINTF(("ipsec4_common_input_cb: queue full; "
"proto %u packet dropped\n", sproto));
return ENOBUFS;
}
schednetisr(NETISR_IP);
return 0;
bad:
m_freem(m);
return error;
}
#endif /* INET */
#ifdef INET6
/* IPv6 AH wrapper. */
int
ipsec6_common_input(struct mbuf **mp, int *offp, int proto)
{
int l = 0;
int protoff;
struct ip6_ext ip6e;
if (*offp < sizeof(struct ip6_hdr)) {
DPRINTF(("ipsec6_common_input: bad offset %u\n", *offp));
return IPPROTO_DONE;
} else if (*offp == sizeof(struct ip6_hdr)) {
protoff = offsetof(struct ip6_hdr, ip6_nxt);
} else {
/* Chase down the header chain... */
protoff = sizeof(struct ip6_hdr);
do {
protoff += l;
m_copydata(*mp, protoff, sizeof(ip6e),
(caddr_t) &ip6e);
if (ip6e.ip6e_nxt == IPPROTO_AH)
l = (ip6e.ip6e_len + 2) << 2;
else
l = (ip6e.ip6e_len + 1) << 3;
IPSEC_ASSERT(l > 0, ("ah6_input: l went zero or negative"));
} while (protoff + l < *offp);
/* Malformed packet check */
if (protoff + l != *offp) {
DPRINTF(("ipsec6_common_input: bad packet header chain, "
"protoff %u, l %u, off %u\n", protoff, l, *offp));
IPSEC_ISTAT(proto, espstat.esps_hdrops,
ahstat.ahs_hdrops,
ipcompstat.ipcomps_hdrops);
m_freem(*mp);
*mp = NULL;
return IPPROTO_DONE;
}
protoff += offsetof(struct ip6_ext, ip6e_nxt);
}
(void) ipsec_common_input(*mp, *offp, protoff, AF_INET6, proto);
return IPPROTO_DONE;
}
void
esp6_ctlinput(int cmd, struct sockaddr *sa, void *d)
{
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) {
struct ip6ctlparam *ip6cp = (struct ip6ctlparam *)d;
struct mbuf *m = ip6cp->ip6c_m;
int off = ip6cp->ip6c_off;
struct ip6ctlparam ip6cp1;
/*
* 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.
*/
if (cmd == PRC_MSGSIZE) {
struct secasvar *sav;
u_int32_t spi;
int valid;
/* check header length before using m_copydata */
if (m->m_pkthdr.len < off + sizeof (struct esp))
return;
m_copydata(m, off + offsetof(struct esp, esp_spi),
sizeof(u_int32_t), (caddr_t) &spi);
/*
* Check to see if we have a valid SA corresponding to
* the address in the ICMP message payload.
*/
sav = KEY_ALLOCSA((union sockaddr_union *)sa,
IPPROTO_ESP, spi);
valid = (sav != NULL);
if (sav)
KEY_FREESAV(&sav);
/* XXX Further validation? */
/*
* Depending on whether the SA is "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(ip6cp, valid);
}
} else {
/* we normally notify any pcb here */
}
}
extern struct ip6protosw inet6sw[];
extern u_char ip6_protox[];
/*
* IPsec input callback, called by the transform callback. Takes care of
* filtering and other sanity checks on the processed packet.
*/
int
ipsec6_common_input_cb(struct mbuf *m, struct secasvar *sav, int skip, int protoff,
struct m_tag *mt)
{
int prot, af, sproto;
struct ip6_hdr *ip6;
struct m_tag *mtag;
struct tdb_ident *tdbi;
struct secasindex *saidx;
int nxt;
u_int8_t nxt8;
int error, nest;
IPSEC_ASSERT(m != NULL, ("ipsec6_common_input_cb: null mbuf"));
IPSEC_ASSERT(sav != NULL, ("ipsec6_common_input_cb: null SA"));
IPSEC_ASSERT(sav->sah != NULL, ("ipsec6_common_input_cb: null SAH"));
saidx = &sav->sah->saidx;
af = saidx->dst.sa.sa_family;
IPSEC_ASSERT(af == AF_INET6,
("ipsec6_common_input_cb: unexpected af %u", af));
sproto = saidx->proto;
IPSEC_ASSERT(sproto == IPPROTO_ESP || sproto == IPPROTO_AH ||
sproto == IPPROTO_IPCOMP,
("ipsec6_common_input_cb: unexpected security protocol %u",
sproto));
/* Sanity check */
if (m == NULL) {
DPRINTF(("ipsec4_common_input_cb: null mbuf"));
IPSEC_ISTAT(sproto, espstat.esps_badkcr, ahstat.ahs_badkcr,
ipcompstat.ipcomps_badkcr);
error = EINVAL;
goto bad;
}
/* Fix IPv6 header */
if (m->m_len < sizeof(struct ip6_hdr) &&
(m = m_pullup(m, sizeof(struct ip6_hdr))) == NULL) {
DPRINTF(("ipsec_common_input_cb: processing failed "
"for SA %s/%08lx\n", ipsec_address(&sav->sah->saidx.dst),
(u_long) ntohl(sav->spi)));
IPSEC_ISTAT(sproto, espstat.esps_hdrops, ahstat.ahs_hdrops,
ipcompstat.ipcomps_hdrops);
error = EACCES;
goto bad;
}
ip6 = mtod(m, struct ip6_hdr *);
ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(struct ip6_hdr));
/* Save protocol */
m_copydata(m, protoff, 1, (unsigned char *) &prot);
#ifdef INET
/* IP-in-IP encapsulation */
if (prot == IPPROTO_IPIP) {
struct ip ipn;
/* ipn will now contain the inner IPv4 header */
m_copydata(m, skip, sizeof(struct ip), (caddr_t) &ipn);
#ifdef notyet
/*
* Check that the inner source address is the same as
* the proxy address, if available.
*/
if ((saidx->proxy.sa.sa_family == AF_INET &&
saidx->proxy.sin.sin_addr.s_addr != INADDR_ANY &&
ipn.ip_src.s_addr != saidx->proxy.sin.sin_addr.s_addr) ||
(saidx->proxy.sa.sa_family != AF_INET &&
saidx->proxy.sa.sa_family != 0)) {
DPRINTF(("ipsec_common_input_cb: inner "
"source address %s doesn't correspond to "
"expected proxy source %s, SA %s/%08lx\n",
inet_ntoa4(ipn.ip_src),
ipsec_address(&saidx->proxy),
ipsec_address(&saidx->dst),
(u_long) ntohl(sav->spi)));
IPSEC_ISTATsproto, (espstat.esps_pdrops,
ahstat.ahs_pdrops, ipcompstat.ipcomps_pdrops);
error = EACCES;
goto bad;
}
#endif /*XXX*/
}
#endif /* INET */
/* IPv6-in-IP encapsulation */
if (prot == IPPROTO_IPV6) {
struct ip6_hdr ip6n;
/* ip6n will now contain the inner IPv6 header. */
m_copydata(m, skip, sizeof(struct ip6_hdr),
(caddr_t) &ip6n);
#ifdef notyet
/*
* Check that the inner source address is the same as
* the proxy address, if available.
*/
if ((saidx->proxy.sa.sa_family == AF_INET6 &&
!IN6_IS_ADDR_UNSPECIFIED(&saidx->proxy.sin6.sin6_addr) &&
!IN6_ARE_ADDR_EQUAL(&ip6n.ip6_src,
&saidx->proxy.sin6.sin6_addr)) ||
(saidx->proxy.sa.sa_family != AF_INET6 &&
saidx->proxy.sa.sa_family != 0)) {
DPRINTF(("ipsec_common_input_cb: inner "
"source address %s doesn't correspond to "
"expected proxy source %s, SA %s/%08lx\n",
ip6_sprintf(&ip6n.ip6_src),
ipsec_address(&saidx->proxy),
ipsec_address(&saidx->dst),
(u_long) ntohl(sav->spi)));
IPSEC_ISTAT(sproto, espstat.esps_pdrops,
ahstat.ahs_pdrops, ipcompstat.ipcomps_pdrops);
error = EACCES;
goto bad;
}
#endif /*XXX*/
}
/*
* Record what we've done to the packet (under what SA it was
* processed). If we've been passed an mtag, it means the packet
* was already processed by an ethernet/crypto combo card and
* thus has a tag attached with all the right information, but
* with a PACKET_TAG_IPSEC_IN_CRYPTO_DONE as opposed to
* PACKET_TAG_IPSEC_IN_DONE type; in that case, just change the type.
*/
if (mt == NULL && sproto != IPPROTO_IPCOMP) {
mtag = m_tag_get(PACKET_TAG_IPSEC_IN_DONE,
sizeof(struct tdb_ident), M_NOWAIT);
if (mtag == NULL) {
DPRINTF(("ipsec_common_input_cb: failed to "
"get tag\n"));
IPSEC_ISTAT(sproto, espstat.esps_hdrops,
ahstat.ahs_hdrops, ipcompstat.ipcomps_hdrops);
error = ENOMEM;
goto bad;
}
tdbi = (struct tdb_ident *)(mtag + 1);
bcopy(&saidx->dst, &tdbi->dst, sizeof(union sockaddr_union));
tdbi->proto = sproto;
tdbi->spi = sav->spi;
m_tag_prepend(m, mtag);
} else {
mt->m_tag_id = PACKET_TAG_IPSEC_IN_DONE;
/* XXX do we need to mark m_flags??? */
}
key_sa_recordxfer(sav, m);
/* Retrieve new protocol */
m_copydata(m, protoff, sizeof(u_int8_t), (caddr_t) &nxt8);
/*
* See the end of ip6_input for this logic.
* IPPROTO_IPV[46] case will be processed just like other ones
*/
nest = 0;
nxt = nxt8;
while (nxt != IPPROTO_DONE) {
if (ip6_hdrnestlimit && (++nest > ip6_hdrnestlimit)) {
ip6stat.ip6s_toomanyhdr++;
error = EINVAL;
goto bad;
}
/*
* Protection against faulty packet - there should be
* more sanity checks in header chain processing.
*/
if (m->m_pkthdr.len < skip) {
ip6stat.ip6s_tooshort++;
in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_truncated);
error = EINVAL;
goto bad;
}
/*
* Enforce IPsec policy checking if we are seeing last header.
* note that we do not visit this with protocols with pcb layer
* code - like udp/tcp/raw ip.
*/
if ((inet6sw[ip6_protox[nxt]].pr_flags & PR_LASTHDR) != 0 &&
ipsec6_in_reject(m, NULL)) {
error = EINVAL;
goto bad;
}
nxt = (*inet6sw[ip6_protox[nxt]].pr_input)(&m, &skip, nxt);
}
return 0;
bad:
if (m)
m_freem(m);
return error;
}
#endif /* INET6 */

462
sys/netipsec/ipsec_mbuf.c Normal file
View File

@ -0,0 +1,462 @@
/* $NetBSD: ipsec_mbuf.c,v 1.1 2003/08/13 20:06:50 jonathan Exp $ */
/* $FreeBSD: src/sys/netipsec/ipsec_mbuf.c,v 1.5.2.1 2003/01/24 05:11:35 sam Exp $ */
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: ipsec_mbuf.c,v 1.1 2003/08/13 20:06:50 jonathan Exp $");
/*
* IPsec-specific mbuf routines.
*/
#include "opt_param.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netipsec/ipsec.h>
#include <netipsec/ipsec_osdep.h>
#include <net/net_osdep.h>
extern struct mbuf *m_getptr(struct mbuf *, int, int *);
/*
* Create a writable copy of the mbuf chain. While doing this
* we compact the chain with a goal of producing a chain with
* at most two mbufs. The second mbuf in this chain is likely
* to be a cluster. The primary purpose of this work is to create
* a writable packet for encryption, compression, etc. The
* secondary goal is to linearize the data so the data can be
* passed to crypto hardware in the most efficient manner possible.
*/
struct mbuf *
m_clone(struct mbuf *m0)
{
struct mbuf *m, *mprev;
struct mbuf *n, *mfirst, *mlast;
int len, off;
IPSEC_ASSERT(m0 != NULL, ("m_clone: null mbuf"));
mprev = NULL;
for (m = m0; m != NULL; m = mprev->m_next) {
/*
* Regular mbufs are ignored unless there's a cluster
* in front of it that we can use to coalesce. We do
* the latter mainly so later clusters can be coalesced
* also w/o having to handle them specially (i.e. convert
* mbuf+cluster -> cluster). This optimization is heavily
* influenced by the assumption that we're running over
* Ethernet where MCLBYTES is large enough that the max
* packet size will permit lots of coalescing into a
* single cluster. This in turn permits efficient
* crypto operations, especially when using hardware.
*/
if ((m->m_flags & M_EXT) == 0) {
if (mprev && (mprev->m_flags & M_EXT) &&
m->m_len <= M_TRAILINGSPACE(mprev)) {
/* XXX: this ignores mbuf types */
memcpy(mtod(mprev, caddr_t) + mprev->m_len,
mtod(m, caddr_t), m->m_len);
mprev->m_len += m->m_len;
mprev->m_next = m->m_next; /* unlink from chain */
m_free(m); /* reclaim mbuf */
newipsecstat.ips_mbcoalesced++;
} else {
mprev = m;
}
continue;
}
/*
* Writable mbufs are left alone (for now). Note
* that for 4.x systems it's not possible to identify
* whether or not mbufs with external buffers are
* writable unless they use clusters.
*/
if (M_EXT_WRITABLE(m)) {
mprev = m;
continue;
}
/*
* Not writable, replace with a copy or coalesce with
* the previous mbuf if possible (since we have to copy
* it anyway, we try to reduce the number of mbufs and
* clusters so that future work is easier).
*/
IPSEC_ASSERT(m->m_flags & M_EXT,
("m_clone: m_flags 0x%x", m->m_flags));
/* NB: we only coalesce into a cluster or larger */
if (mprev != NULL && (mprev->m_flags & M_EXT) &&
m->m_len <= M_TRAILINGSPACE(mprev)) {
/* XXX: this ignores mbuf types */
memcpy(mtod(mprev, caddr_t) + mprev->m_len,
mtod(m, caddr_t), m->m_len);
mprev->m_len += m->m_len;
mprev->m_next = m->m_next; /* unlink from chain */
m_free(m); /* reclaim mbuf */
newipsecstat.ips_clcoalesced++;
continue;
}
/*
* Allocate new space to hold the copy...
*/
/* XXX why can M_PKTHDR be set past the first mbuf? */
if (mprev == NULL && (m->m_flags & M_PKTHDR)) {
/*
* NB: if a packet header is present we must
* allocate the mbuf separately from any cluster
* because M_MOVE_PKTHDR will smash the data
* pointer and drop the M_EXT marker.
*/
MGETHDR(n, M_DONTWAIT, m->m_type);
if (n == NULL) {
m_freem(m0);
return (NULL);
}
M_MOVE_PKTHDR(n, m);
MCLGET(n, M_DONTWAIT);
if ((n->m_flags & M_EXT) == 0) {
m_free(n);
m_freem(m0);
return (NULL);
}
} else {
n = m_getcl(M_DONTWAIT, m->m_type, m->m_flags);
if (n == NULL) {
m_freem(m0);
return (NULL);
}
}
/*
* ... and copy the data. We deal with jumbo mbufs
* (i.e. m_len > MCLBYTES) by splitting them into
* clusters. We could just malloc a buffer and make
* it external but too many device drivers don't know
* how to break up the non-contiguous memory when
* doing DMA.
*/
len = m->m_len;
off = 0;
mfirst = n;
mlast = NULL;
for (;;) {
int cc = min(len, MCLBYTES);
memcpy(mtod(n, caddr_t), mtod(m, caddr_t) + off, cc);
n->m_len = cc;
if (mlast != NULL)
mlast->m_next = n;
mlast = n;
newipsecstat.ips_clcopied++;
len -= cc;
if (len <= 0)
break;
off += cc;
n = m_getcl(M_DONTWAIT, m->m_type, m->m_flags);
if (n == NULL) {
m_freem(mfirst);
m_freem(m0);
return (NULL);
}
}
n->m_next = m->m_next;
if (mprev == NULL)
m0 = mfirst; /* new head of chain */
else
mprev->m_next = mfirst; /* replace old mbuf */
m_free(m); /* release old mbuf */
mprev = mfirst;
}
return (m0);
}
/*
* Make space for a new header of length hlen at skip bytes
* into the packet. When doing this we allocate new mbufs only
* when absolutely necessary. The mbuf where the new header
* is to go is returned together with an offset into the mbuf.
* If NULL is returned then the mbuf chain may have been modified;
* the caller is assumed to always free the chain.
*/
struct mbuf *
m_makespace(struct mbuf *m0, int skip, int hlen, int *off)
{
struct mbuf *m;
unsigned remain;
IPSEC_ASSERT(m0 != NULL, ("m_dmakespace: null mbuf"));
IPSEC_ASSERT(hlen < MHLEN, ("m_makespace: hlen too big: %u", hlen));
for (m = m0; m && skip > m->m_len; m = m->m_next)
skip -= m->m_len;
if (m == NULL)
return (NULL);
/*
* At this point skip is the offset into the mbuf m
* where the new header should be placed. Figure out
* if there's space to insert the new header. If so,
* and copying the remainder makese sense then do so.
* Otherwise insert a new mbuf in the chain, splitting
* the contents of m as needed.
*/
remain = m->m_len - skip; /* data to move */
if (hlen > M_TRAILINGSPACE(m)) {
struct mbuf *n;
/* XXX code doesn't handle clusters XXX */
IPSEC_ASSERT(remain < MLEN,
("m_makespace: remainder too big: %u", remain));
/*
* Not enough space in m, split the contents
* of m, inserting new mbufs as required.
*
* NB: this ignores mbuf types.
*/
MGET(n, M_DONTWAIT, MT_DATA);
if (n == NULL)
return (NULL);
n->m_next = m->m_next; /* splice new mbuf */
m->m_next = n;
newipsecstat.ips_mbinserted++;
if (hlen <= M_TRAILINGSPACE(m) + remain) {
/*
* New header fits in the old mbuf if we copy
* the remainder; just do the copy to the new
* mbuf and we're good to go.
*/
memcpy(mtod(n, caddr_t),
mtod(m, caddr_t) + skip, remain);
n->m_len = remain;
m->m_len = skip + hlen;
*off = skip;
} else {
/*
* No space in the old mbuf for the new header.
* Make space in the new mbuf and check the
* remainder'd data fits too. If not then we
* must allocate an additional mbuf (yech).
*/
n->m_len = 0;
if (remain + hlen > M_TRAILINGSPACE(n)) {
struct mbuf *n2;
MGET(n2, M_DONTWAIT, MT_DATA);
/* NB: new mbuf is on chain, let caller free */
if (n2 == NULL)
return (NULL);
n2->m_len = 0;
memcpy(mtod(n2, caddr_t),
mtod(m, caddr_t) + skip, remain);
n2->m_len = remain;
/* splice in second mbuf */
n2->m_next = n->m_next;
n->m_next = n2;
newipsecstat.ips_mbinserted++;
} else {
memcpy(mtod(n, caddr_t) + hlen,
mtod(m, caddr_t) + skip, remain);
n->m_len += remain;
}
m->m_len -= remain;
n->m_len += hlen;
m = n; /* header is at front ... */
*off = 0; /* ... of new mbuf */
}
} else {
/*
* Copy the remainder to the back of the mbuf
* so there's space to write the new header.
*/
/* XXX can this be memcpy? does it handle overlap? */
ovbcopy(mtod(m, caddr_t) + skip,
mtod(m, caddr_t) + skip + hlen, remain);
m->m_len += hlen;
*off = skip;
}
m0->m_pkthdr.len += hlen; /* adjust packet length */
return m;
}
/*
* m_pad(m, n) pads <m> with <n> bytes at the end. The packet header
* length is updated, and a pointer to the first byte of the padding
* (which is guaranteed to be all in one mbuf) is returned.
*/
caddr_t
m_pad(struct mbuf *m, int n)
{
register struct mbuf *m0, *m1;
register int len, pad;
caddr_t retval;
if (n <= 0) { /* No stupid arguments. */
DPRINTF(("m_pad: pad length invalid (%d)\n", n));
m_freem(m);
return NULL;
}
len = m->m_pkthdr.len;
pad = n;
m0 = m;
while (m0->m_len < len) {
IPSEC_ASSERT(m0->m_next != NULL, ("m_pad: m0 null, len %u m_len %u", len, m0->m_len));/*XXX*/
len -= m0->m_len;
m0 = m0->m_next;
}
if (m0->m_len != len) {
DPRINTF(("m_pad: length mismatch (should be %d instead of %d)\n",
m->m_pkthdr.len, m->m_pkthdr.len + m0->m_len - len));
m_freem(m);
return NULL;
}
/* Check for zero-length trailing mbufs, and find the last one. */
for (m1 = m0; m1->m_next; m1 = m1->m_next) {
if (m1->m_next->m_len != 0) {
DPRINTF(("m_pad: length mismatch (should be %d "
"instead of %d)\n",
m->m_pkthdr.len,
m->m_pkthdr.len + m1->m_next->m_len));
m_freem(m);
return NULL;
}
m0 = m1->m_next;
}
if (pad > M_TRAILINGSPACE(m0)) {
/* Add an mbuf to the chain. */
MGET(m1, M_DONTWAIT, MT_DATA);
if (m1 == 0) {
m_freem(m0);
DPRINTF(("m_pad: unable to get extra mbuf\n"));
return NULL;
}
m0->m_next = m1;
m0 = m1;
m0->m_len = 0;
}
retval = m0->m_data + m0->m_len;
m0->m_len += pad;
m->m_pkthdr.len += pad;
return retval;
}
/*
* Remove hlen data at offset skip in the packet. This is used by
* the protocols strip protocol headers and associated data (e.g. IV,
* authenticator) on input.
*/
int
m_striphdr(struct mbuf *m, int skip, int hlen)
{
struct mbuf *m1;
int roff;
/* Find beginning of header */
m1 = m_getptr(m, skip, &roff);
if (m1 == NULL)
return (EINVAL);
/* Remove the header and associated data from the mbuf. */
if (roff == 0) {
/* The header was at the beginning of the mbuf */
newipsecstat.ips_input_front++;
m_adj(m1, hlen);
if ((m1->m_flags & M_PKTHDR) == 0)
m->m_pkthdr.len -= hlen;
} else if (roff + hlen >= m1->m_len) {
struct mbuf *mo;
/*
* Part or all of the header is at the end of this mbuf,
* so first let's remove the remainder of the header from
* the beginning of the remainder of the mbuf chain, if any.
*/
newipsecstat.ips_input_end++;
if (roff + hlen > m1->m_len) {
/* Adjust the next mbuf by the remainder */
m_adj(m1->m_next, roff + hlen - m1->m_len);
/* The second mbuf is guaranteed not to have a pkthdr... */
m->m_pkthdr.len -= (roff + hlen - m1->m_len);
}
/* Now, let's unlink the mbuf chain for a second...*/
mo = m1->m_next;
m1->m_next = NULL;
/* ...and trim the end of the first part of the chain...sick */
m_adj(m1, -(m1->m_len - roff));
if ((m1->m_flags & M_PKTHDR) == 0)
m->m_pkthdr.len -= (m1->m_len - roff);
/* Finally, let's relink */
m1->m_next = mo;
} else {
/*
* The header lies in the "middle" of the mbuf; copy
* the remainder of the mbuf down over the header.
*/
newipsecstat.ips_input_middle++;
bcopy(mtod(m1, u_char *) + roff + hlen,
mtod(m1, u_char *) + roff,
m1->m_len - (roff + hlen));
m1->m_len -= hlen;
m->m_pkthdr.len -= hlen;
}
return (0);
}
/*
* Diagnostic routine to check mbuf alignment as required by the
* crypto device drivers (that use DMA).
*/
void
m_checkalignment(const char* where, struct mbuf *m0, int off, int len)
{
int roff;
struct mbuf *m = m_getptr(m0, off, &roff);
caddr_t addr;
if (m == NULL)
return;
printf("%s (off %u len %u): ", where, off, len);
addr = mtod(m, caddr_t) + roff;
do {
int mlen;
if (((uintptr_t) addr) & 3) {
printf("addr misaligned %p,", addr);
break;
}
mlen = m->m_len;
if (mlen > len)
mlen = len;
len -= mlen;
if (len && (mlen & 3)) {
printf("len mismatch %u,", mlen);
break;
}
m = m->m_next;
addr = m ? mtod(m, caddr_t) : NULL;
} while (m && len > 0);
for (m = m0; m; m = m->m_next)
printf(" [%p:%u]", mtod(m, caddr_t), m->m_len);
printf("\n");
}

407
sys/netipsec/ipsec_netbsd.c Normal file
View File

@ -0,0 +1,407 @@
/* $NetBSD: ipsec_netbsd.c,v 1.1 2003/08/13 20:06:50 jonathan 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.1 2003/08/13 20:06:50 jonathan 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)
{
}
/* XXX will need a different oid at parent */
int
ipsec_sysctl(name, namelen, oldp, oldlenp, newp, newlen)
int *name;
u_int namelen;
void *oldp;
size_t *oldlenp;
void *newp;
size_t newlen;
{
/* All sysctl names at this level are terminal. */
if (namelen != 1)
return ENOTDIR;
/* common sanity checks */
switch (name[0]) {
case IPSECCTL_DEF_ESP_TRANSLEV:
case IPSECCTL_DEF_ESP_NETLEV:
case IPSECCTL_DEF_AH_TRANSLEV:
case IPSECCTL_DEF_AH_NETLEV:
if (newp != NULL && newlen == sizeof(int)) {
switch (*(int *)newp) {
case IPSEC_LEVEL_USE:
case IPSEC_LEVEL_REQUIRE:
break;
default:
return EINVAL;
}
}
break;
case IPSECCTL_DEF_POLICY:
if (newp != NULL && newlen == sizeof(int)) {
switch (*(int *)newp) {
case IPSEC_POLICY_DISCARD:
case IPSEC_POLICY_NONE:
break;
default:
return EINVAL;
}
ipsec_invalpcbcacheall();
}
break;
}
switch (name[0]) {
case IPSECCTL_STATS:
return sysctl_struct(oldp, oldlenp, newp, newlen,
&ipsecstat, sizeof(ipsecstat));
case IPSECCTL_DEF_POLICY:
return sysctl_int(oldp, oldlenp, newp, newlen,
&ip4_def_policy.policy);
case IPSECCTL_DEF_ESP_TRANSLEV:
if (newp != NULL)
ipsec_invalpcbcacheall();
return sysctl_int(oldp, oldlenp, newp, newlen,
&ip4_esp_trans_deflev);
case IPSECCTL_DEF_ESP_NETLEV:
if (newp != NULL)
ipsec_invalpcbcacheall();
return sysctl_int(oldp, oldlenp, newp, newlen,
&ip4_esp_net_deflev);
case IPSECCTL_DEF_AH_TRANSLEV:
if (newp != NULL)
ipsec_invalpcbcacheall();
return sysctl_int(oldp, oldlenp, newp, newlen,
&ip4_ah_trans_deflev);
case IPSECCTL_DEF_AH_NETLEV:
if (newp != NULL)
ipsec_invalpcbcacheall();
return sysctl_int(oldp, oldlenp, newp, newlen,
&ip4_ah_net_deflev);
case IPSECCTL_AH_CLEARTOS:
return sysctl_int(oldp, oldlenp, newp, newlen,
&/*ip4_*/ah_cleartos);
case IPSECCTL_AH_OFFSETMASK:
return sysctl_int(oldp, oldlenp, newp, newlen,
&ip4_ah_offsetmask);
case IPSECCTL_DFBIT:
return sysctl_int(oldp, oldlenp, newp, newlen,
&ip4_ipsec_dfbit);
case IPSECCTL_ECN:
return sysctl_int(oldp, oldlenp, newp, newlen, &ip4_ipsec_ecn);
case IPSECCTL_DEBUG:
return sysctl_int(oldp, oldlenp, newp, newlen, &ipsec_debug);
default:
return EOPNOTSUPP;
}
/* NOTREACHED */
}

277
sys/netipsec/ipsec_osdep.h Normal file
View File

@ -0,0 +1,277 @@
/* $NetBSD: ipsec_osdep.h,v 1.1 2003/08/13 20:06:51 jonathan Exp $ */
#ifndef NETIPSEC_OSDEP_H
#define NETIPSEC_OSDEP_H
/*
* Hide porting differences across different 4.4BSD-derived platforms.
*
* 1. KASSERT() differences:
* 2. Kernel Random-number API differences.
* 3. Is packet data in an mbuf object writeable?
* 4. Packet-header semantics.
* 5. Fast mbuf-cluster allocation.
* 6. Network packet-output macros.
* 7. Elased time, in seconds.
* 8. Test if a socket object opened by a privileged (super) user.
* 9. Global SLIST of all open raw sockets.
* 10. Global SLIST of known interface addresses.
*/
/*
* 1. KASSERT and spl differences
*
* FreeBSD takes an expression and parenthesized printf() argument-list.
* NetBSD takes one arg: the expression being asserted.
* FreeBSD's SPLASSERT() takes an SPL level as 1st arg and a
* parenthesized printf-format argument list as the second argument.
*
* This difference is hidden by two 2-argument macros and one 1-arg macro:
* IPSEC_ASSERT(expr, msg)
* IPSEC_SPLASSERT(spl, msg)
* One further difference is the spl names:
* NetBSD splsoftnet equates to FreeBSD splnet;
* NetBSD splnet equates to FreeBSD splimp.
* which is hidden by the macro IPSEC_SPLASSERT_SOFTNET(msg).
*/
#ifdef __FreeBSD__
#define IPSEC_SPLASSERT(x,y) SPLASSERT(x, y)
#define IPSEC_ASSERT(c,m) KASSERT(c, m)
#define IPSEC_SPLASSERT_SOFTNET(m) SPLASSERT(splnet, m)
#endif /* __FreeBSD__ */
#ifdef __NetBSD__
#define IPSEC_SPLASSERT(x,y) (void)0
#define IPSEC_ASSERT(c,m) KASSERT(c)
#define IPSEC_SPLASSERT_SOFTNET(m) IPSEC_SPLASSERT(softnet, m)
#endif /* __NetBSD__ */
/*
* 2. Kernel Randomness API.
* FreeBSD uses:
* u_int read_random(void *outbuf, int nbytes).
*/
#ifdef __FreeBSD__
#include <sys/random.h>
/* do nothing, use native random code. */
#endif /* __FreeBSD__ */
#ifdef __NetBSD__
#include <sys/rnd.h>
static __inline u_int read_random(void *p, u_int len);
static __inline u_int
read_random(void *bufp, u_int len)
{
return rnd_extract_data(bufp, len, RND_EXTRACT_ANY /*XXX FIXME */);
}
#endif /* __NetBSD__ */
/*
* 3. Test for mbuf mutability
* FreeBSD 4.x uses: M_EXT_WRITABLE
* NetBSD has M_READONLY(). Use !M_READONLY().
* Not an exact match to FreeBSD semantics, but adequate for IPsec purposes.
*
*/
#ifdef __NetBSD__
/* XXX wrong, but close enough for restricted ipsec usage. */
#define M_EXT_WRITABLE(m) (!M_READONLY(m))
#endif /* __NetBSD__ */
/*
* 4. mbuf packet-header/packet-tag semantics.
* Sam Leffler explains, in private email, some problems with
* M_COPY_PKTHDR(), and why FreeBSD deprecated it and replaced it
* with new, explicit macros M_MOVE_PKTHDR()/M_DUP_PKTHDR().
* he original fast-ipsec source uses M_MOVE_PKTHDR.
* NetBSD currently still uses M_COPY_PKTHDR(), so we define
* M_MOVE_PKTHDR in terms of M_COPY_PKTHDR(). Fast-IPsec
* will delete the source mbuf shortly after copying packet tags,
* so we are safe for fast-ipsec but not in general..
*/
#ifdef __NetBSD__
#define M_MOVE_PKTHDR(_f, _t) M_COPY_PKTHDR(_f, _t)
#endif /* __NetBSD__ */
/*
* 5. Fast mbuf-cluster allocation.
* FreeBSD 4.6 introduce m_getcl(), which performs `fast' allocation
* mbuf clusters from a cache of recently-freed clusters. (If the cache
* is empty, new clusters are allocated en-masse).
* On NetBSD, for now, implement the `cache' as an inline function
*using normal NetBSD mbuf/cluster allocation macros. Replace this
* with fast-cache code, if and when netBSD implements one.
*/
#ifdef __NetBSD__
static __inline struct mbuf *
m_getcl(int how, short type, int flags)
{
struct mbuf *mp;
if (flags & M_PKTHDR)
MGETHDR(mp, how, type);
else
MGET(mp, how, type);
if (mp == NULL)
return NULL;
MCLGET(mp, how);
return mp;
}
#endif /* __NetBSD__ */
/*
* 6. Network output macros
* FreeBSD uses the IF_HANDOFF(), which raises SPL, enqueues
* a packet, and updates interface counters. NetBSD has IFQ_ENQUE(),
* which leaves SPL changes up to the caller.
* For now, we provide an emulation of IF_HANOOFF() which works
* for protocol input queues.
*/
#ifdef __FreeBSD__
/* nothing to do */
#endif /* __FreeBSD__ */
#ifdef __NetBSD__
#define IF_HANDOFF(ifq, m, f) if_handoff(ifq, m, f, 0)
#include <net/if.h>
static __inline int
if_handoff(struct ifqueue *ifq, struct mbuf *m, struct ifnet *ifp, int adjust)
{
int need_if_start = 0;
int s = splnet();
if (IF_QFULL(ifq)) {
IF_DROP(ifq);
splx(s);
m_freem(m);
return (0);
}
if (ifp != NULL) {
ifp->if_obytes += m->m_pkthdr.len + adjust;
if (m->m_flags & M_MCAST)
ifp->if_omcasts++;
need_if_start = !(ifp->if_flags & IFF_OACTIVE);
}
IF_ENQUEUE(ifq, m);
if (need_if_start)
(*ifp->if_start)(ifp);
splx(s);
return (1);
}
#endif /* __NetBSD__ */
/*
* 7. Elapsed Time: time_second as time in seconds.
* Original FreeBSD fast-ipsec code references a FreeBSD kernel global,
* time_second(). NetBSD: kludge #define to use time_mono_time.tv_sec.
*/
#ifdef __NetBSD__
#include <sys/kernel.h>
#define time_second mono_time.tv_sec
#endif /* __NetBSD__ */
/* protosw glue */
#ifdef __NetBSD__
#include <sys/protosw.h>
#define ipprotosw protosw
#endif /* __NetBSD__ */
/*
* 8. Test for "privileged" socket opened by superuser.
* FreeBSD tests ((so)->so_cred && (so)->so_cred.cr_uid == 0),
* NetBSD (1.6N) tests (so)->so_uid == 0).
* This difference is wrapped inside the IPSEC_PRIVILEGED_SO() macro.
*
*/
#ifdef __FreeBSD__
#define IPSEC_PRIVILEGED_SO(so) ((so)->so_cred && (so)->so_cred.cr_uid == 0)
#endif /* __FreeBSD__ */
#ifdef __NetBSD__
/* superuser opened socket? */
#define IPSEC_PRIVILEGED_SO(so) ((so)->so_uid == 0)
#endif /* __NetBSD__ */
/*
* 9. Raw socket list
* FreeBSD uses: listhead = rawcb_list, SLIST()-next field "list".
* NetBSD uses: listhead = rawcb, SLIST()-next field "list"
*
* This version of fast-ipsec source code uses rawcb_list as the head,
* and (to avoid namespace collisions) uses rcb_list as the "next" field.
*/
#ifdef __FreeBSD__
#define rcb_list list
#endif /* __FreeBSD__ */
#ifdef __NetBSD__
#define rawcb_list rawcb
#endif /* __NetBSD__ */
/*
* 10. List of all known network interfaces.
* FreeBSD has listhead in_ifaddread, with ia_link as link.
* NetBSD has listhead in_ifaddr, with ia_list as link.
* No name-clahses, so just #define the appropriate names on NetBSD.
* NB: Is it worth introducing iterator (find-first-list/find-next-list)
* functions or macros to encapsulate these?
*/
#ifdef __FreeBSD__
/* nothing to do for raw interface list */
#endif /* FreeBSD */
#ifdef __NetBSD__
/* For now, use FreeBSD-compatible names for raw interface list. */
#define in_ifaddrhead in_ifaddr
#define ia_link ia_list
#endif /* __NetBSD__ */
/*
* Differences that we don't attempt to hide:
*
* A. Initialization code. This is the largest difference of all.
*
* FreeBSD uses compile/link-time perl hackery to generate special
* .o files with linker sections that give the moral equivalent of
* C++ file-level-object constructors. NetBSD has no such facility.
*
* Either we implement it (ideally, in a way that can emulate
* FreeBSD's SYSINIT() macros), or we must take other means
* to have the per-file init functions called at some appropriate time.
*
* In the absence of SYSINIT(), all the file-level init functions
* now have "extern" linkage. There is a new fast-ipsec init()
* function which calls each of the per-file in an appropriate order.
* init_main will arrange to call the fast-ipsec init function
* after the crypto framework has registered its transforms (including
* any autoconfigured hardware crypto accelerators) but before
* initializing the network stack to send or receive packet.
*
* B. Protosw() differences.
* CSRG-style BSD TCP/IP uses a generic protocol-dispatch-function
* where the specific request is identified by an enum argument.
* FreeBSD replaced that with an array of request-specific
* function pointers.
*
* These differences affect the handlers for key-protocol user requests
* so pervasively that I gave up on the fast-ipsec code, and re-worked the
* NetBSD KAME code to match the (relative few) API differences
* between NetBSD and FreeBSD's KAME netkey, and Fast-IPsec netkey.
*
* C. Timeout() versus callout(9):
* The FreeBSD 4.x netipsec/ code still uses timeout().
* FreeBSD 4.7 has callout(9), so I just replaced
* timeout_*() with the nearest callout_*() equivalents,
* and added a callout handle to the ipsec context.
*
* D. SPL name differences.
* FreeBSD splnet() equates directly to NetBSD's splsoftnet();
* FreeBSD uses splimp() where (for networking) NetBSD would use splnet().
*/
#endif NETIPSEC_OSDEP_H

738
sys/netipsec/ipsec_output.c Normal file
View File

@ -0,0 +1,738 @@
/* $NetBSD: ipsec_output.c,v 1.1 2003/08/13 20:06:51 jonathan Exp $ */
/* $FreeBSD: src/sys/netipsec/ipsec_output.c,v 1.3.2.1 2003/01/24 05:11:35 sam Exp $ */
/* $KAME: ipsec.c,v 1.103 2001/05/24 07:14:18 sakane Exp $ */
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: ipsec_output.c,v 1.1 2003/08/13 20:06:51 jonathan Exp $");
/*
* IPsec output processing.
*/
#include "opt_inet.h"
#include "opt_inet6.h"
#include "opt_ipsec.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/domain.h>
#include <sys/protosw.h>
#include <sys/socket.h>
#include <sys/errno.h>
#include <sys/syslog.h>
#include <net/if.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/ip_var.h>
#include <netinet/in_var.h>
#include <netinet/ip_ecn.h>
#ifdef INET6
#include <netinet6/ip6_ecn.h>
#endif
#include <netinet/ip6.h>
#ifdef INET6
#include <netinet6/ip6_var.h>
#endif
#include <netinet/in_pcb.h>
#ifdef INET6
#include <netinet/icmp6.h>
#endif
#include <netipsec/ipsec.h>
#ifdef INET6
#include <netipsec/ipsec6.h>
#endif
#include <netipsec/ah_var.h>
#include <netipsec/esp_var.h>
#include <netipsec/ipcomp_var.h>
#include <netipsec/xform.h>
#include <netipsec/key.h>
#include <netipsec/keydb.h>
#include <netipsec/key_debug.h>
#include <netipsec/ipsec_osdep.h>
int
ipsec_process_done(struct mbuf *m, struct ipsecrequest *isr)
{
struct tdb_ident *tdbi;
struct m_tag *mtag;
struct secasvar *sav;
struct secasindex *saidx;
int error;
IPSEC_SPLASSERT_SOFTNET("ipsec_process_done");
IPSEC_ASSERT(m != NULL, ("ipsec_process_done: null mbuf"));
IPSEC_ASSERT(isr != NULL, ("ipsec_process_done: null ISR"));
sav = isr->sav;
IPSEC_ASSERT(sav != NULL, ("ipsec_process_done: null SA"));
IPSEC_ASSERT(sav->sah != NULL, ("ipsec_process_done: null SAH"));
saidx = &sav->sah->saidx;
switch (saidx->dst.sa.sa_family) {
#ifdef INET
case AF_INET:
/* Fix the header length, for AH processing. */
mtod(m, struct ip *)->ip_len = htons(m->m_pkthdr.len);
break;
#endif /* INET */
#ifdef INET6
case AF_INET6:
/* Fix the header length, for AH processing. */
if (m->m_pkthdr.len < sizeof (struct ip6_hdr)) {
error = ENXIO;
goto bad;
}
if (m->m_pkthdr.len - sizeof (struct ip6_hdr) > IPV6_MAXPACKET) {
/* No jumbogram support. */
error = ENXIO; /*?*/
goto bad;
}
mtod(m, struct ip6_hdr *)->ip6_plen =
htons(m->m_pkthdr.len - sizeof(struct ip6_hdr));
break;
#endif /* INET6 */
default:
DPRINTF(("ipsec_process_done: unknown protocol family %u\n",
saidx->dst.sa.sa_family));
error = ENXIO;
goto bad;
}
/*
* Add a record of what we've done or what needs to be done to the
* packet.
*/
mtag = m_tag_get(PACKET_TAG_IPSEC_OUT_DONE,
sizeof(struct tdb_ident), M_NOWAIT);
if (mtag == NULL) {
DPRINTF(("ipsec_process_done: could not get packet tag\n"));
error = ENOMEM;
goto bad;
}
tdbi = (struct tdb_ident *)(mtag + 1);
tdbi->dst = saidx->dst;
tdbi->proto = saidx->proto;
tdbi->spi = sav->spi;
m_tag_prepend(m, mtag);
/*
* If there's another (bundled) SA to apply, do so.
* Note that this puts a burden on the kernel stack size.
* If this is a problem we'll need to introduce a queue
* to set the packet on so we can unwind the stack before
* doing further processing.
*/
if (isr->next) {
newipsecstat.ips_out_bundlesa++;
return ipsec4_process_packet(m, isr->next, 0, 0);
}
/*
* We're done with IPsec processing, transmit the packet using the
* appropriate network protocol (IP or IPv6). SPD lookup will be
* performed again there.
*/
switch (saidx->dst.sa.sa_family) {
#ifdef INET
struct ip *ip;
case AF_INET:
ip = mtod(m, struct ip *);
#ifdef __FreeBSD__
/* FreeBSD ip_output() expects ip_len, ip_off in host endian */
ip->ip_len = ntohs(ip->ip_len);
ip->ip_off = ntohs(ip->ip_off);
#endif /* __FreeBSD_ */
return ip_output(m, NULL, NULL, IP_RAWOUTPUT, NULL, NULL);
#endif /* INET */
#ifdef INET6
case AF_INET6:
/*
* We don't need massage, IPv6 header fields are always in
* net endian.
*/
return ip6_output(m, NULL, NULL, 0, NULL, NULL, NULL);
#endif /* INET6 */
}
panic("ipsec_process_done");
bad:
m_freem(m);
KEY_FREESAV(&sav);
return (error);
}
static struct ipsecrequest *
ipsec_nextisr(
struct mbuf *m,
struct ipsecrequest *isr,
int af,
struct secasindex *saidx,
int *error
)
{
#define IPSEC_OSTAT(x,y,z) (isr->saidx.proto == IPPROTO_ESP ? (x)++ : \
isr->saidx.proto == IPPROTO_AH ? (y)++ : (z)++)
struct secasvar *sav;
IPSEC_SPLASSERT_SOFTNET("ipsec_nextisr");
IPSEC_ASSERT(af == AF_INET || af == AF_INET6,
("ipsec_nextisr: invalid address family %u", af));
again:
/*
* Craft SA index to search for proper SA. Note that
* we only fillin unspecified SA peers for transport
* mode; for tunnel mode they must already be filled in.
*/
*saidx = isr->saidx;
if (isr->saidx.mode == IPSEC_MODE_TRANSPORT) {
/* Fillin unspecified SA peers only for transport mode */
if (af == AF_INET) {
struct sockaddr_in *sin;
struct ip *ip = mtod(m, struct ip *);
if (saidx->src.sa.sa_len == 0) {
sin = &saidx->src.sin;
sin->sin_len = sizeof(*sin);
sin->sin_family = AF_INET;
sin->sin_port = IPSEC_PORT_ANY;
sin->sin_addr = ip->ip_src;
}
if (saidx->dst.sa.sa_len == 0) {
sin = &saidx->dst.sin;
sin->sin_len = sizeof(*sin);
sin->sin_family = AF_INET;
sin->sin_port = IPSEC_PORT_ANY;
sin->sin_addr = ip->ip_dst;
}
} else {
struct sockaddr_in6 *sin6;
struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
if (saidx->src.sin6.sin6_len == 0) {
sin6 = (struct sockaddr_in6 *)&saidx->src;
sin6->sin6_len = sizeof(*sin6);
sin6->sin6_family = AF_INET6;
sin6->sin6_port = IPSEC_PORT_ANY;
sin6->sin6_addr = ip6->ip6_src;
if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) {
/* fix scope id for comparing SPD */
sin6->sin6_addr.s6_addr16[1] = 0;
sin6->sin6_scope_id =
ntohs(ip6->ip6_src.s6_addr16[1]);
}
}
if (saidx->dst.sin6.sin6_len == 0) {
sin6 = (struct sockaddr_in6 *)&saidx->dst;
sin6->sin6_len = sizeof(*sin6);
sin6->sin6_family = AF_INET6;
sin6->sin6_port = IPSEC_PORT_ANY;
sin6->sin6_addr = ip6->ip6_dst;
if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) {
/* fix scope id for comparing SPD */
sin6->sin6_addr.s6_addr16[1] = 0;
sin6->sin6_scope_id =
ntohs(ip6->ip6_dst.s6_addr16[1]);
}
}
}
}
/*
* Lookup SA and validate it.
*/
*error = key_checkrequest(isr, saidx);
if (*error != 0) {
/*
* IPsec processing is required, but no SA found.
* I assume that key_acquire() had been called
* to get/establish the SA. Here I discard
* this packet because it is responsibility for
* upper layer to retransmit the packet.
*/
newipsecstat.ips_out_nosa++;
goto bad;
}
sav = isr->sav;
if (sav == NULL) { /* XXX valid return */
IPSEC_ASSERT(ipsec_get_reqlevel(isr) == IPSEC_LEVEL_USE,
("ipsec_nextisr: no SA found, but required; level %u",
ipsec_get_reqlevel(isr)));
isr = isr->next;
if (isr == NULL) {
/*XXXstatistic??*/
*error = EINVAL; /*XXX*/
return isr;
}
goto again;
}
/*
* Check system global policy controls.
*/
if ((isr->saidx.proto == IPPROTO_ESP && !esp_enable) ||
(isr->saidx.proto == IPPROTO_AH && !ah_enable) ||
(isr->saidx.proto == IPPROTO_IPCOMP && !ipcomp_enable)) {
DPRINTF(("ipsec_nextisr: IPsec outbound packet dropped due"
" to policy (check your sysctls)\n"));
IPSEC_OSTAT(espstat.esps_pdrops, ahstat.ahs_pdrops,
ipcompstat.ipcomps_pdrops);
*error = EHOSTUNREACH;
goto bad;
}
/*
* Sanity check the SA contents for the caller
* before they invoke the xform output method.
*/
if (sav->tdb_xform == NULL) {
DPRINTF(("ipsec_nextisr: no transform for SA\n"));
IPSEC_OSTAT(espstat.esps_noxform, ahstat.ahs_noxform,
ipcompstat.ipcomps_noxform);
*error = EHOSTUNREACH;
goto bad;
}
return isr;
bad:
IPSEC_ASSERT(*error != 0, ("ipsec_nextisr: error return w/ no error code"));
return NULL;
#undef IPSEC_OSTAT
}
#ifdef INET
/*
* IPsec output logic for IPv4.
*/
int
ipsec4_process_packet(
struct mbuf *m,
struct ipsecrequest *isr,
int flags,
int tunalready)
{
struct secasindex saidx;
struct secasvar *sav;
struct ip *ip;
int s, error, i, off;
IPSEC_ASSERT(m != NULL, ("ipsec4_process_packet: null mbuf"));
IPSEC_ASSERT(isr != NULL, ("ipsec4_process_packet: null isr"));
s = splsoftnet(); /* insure SA contents don't change */
isr = ipsec_nextisr(m, isr, AF_INET, &saidx, &error);
if (isr == NULL)
goto bad;
sav = isr->sav;
if (!tunalready) {
union sockaddr_union *dst = &sav->sah->saidx.dst;
int setdf;
/*
* Collect IP_DF state from the outer header.
*/
if (dst->sa.sa_family == AF_INET) {
if (m->m_len < sizeof (struct ip) &&
(m = m_pullup(m, sizeof (struct ip))) == NULL) {
error = ENOBUFS;
goto bad;
}
ip = mtod(m, struct ip *);
/* Honor system-wide control of how to handle IP_DF */
switch (ip4_ipsec_dfbit) {
case 0: /* clear in outer header */
case 1: /* set in outer header */
setdf = ip4_ipsec_dfbit;
break;
default: /* propagate to outer header */
setdf = ntohs(ip->ip_off & IP_DF);
break;
}
} else {
ip = NULL; /* keep compiler happy */
setdf = 0;
}
/* Do the appropriate encapsulation, if necessary */
if (isr->saidx.mode == IPSEC_MODE_TUNNEL || /* Tunnel requ'd */
dst->sa.sa_family != AF_INET || /* PF mismatch */
#if 0
(sav->flags & SADB_X_SAFLAGS_TUNNEL) || /* Tunnel requ'd */
sav->tdb_xform->xf_type == XF_IP4 || /* ditto */
#endif
(dst->sa.sa_family == AF_INET && /* Proxy */
dst->sin.sin_addr.s_addr != INADDR_ANY &&
dst->sin.sin_addr.s_addr != ip->ip_dst.s_addr)) {
struct mbuf *mp;
/* Fix IPv4 header checksum and length */
if (m->m_len < sizeof (struct ip) &&
(m = m_pullup(m, sizeof (struct ip))) == NULL) {
error = ENOBUFS;
goto bad;
}
ip = mtod(m, struct ip *);
ip->ip_len = htons(m->m_pkthdr.len);
ip->ip_sum = 0;
#ifdef _IP_VHL
if (ip->ip_vhl == IP_VHL_BORING)
ip->ip_sum = in_cksum_hdr(ip);
else
ip->ip_sum = in_cksum(m,
_IP_VHL_HL(ip->ip_vhl) << 2);
#else
ip->ip_sum = in_cksum(m, ip->ip_hl << 2);
#endif
/* Encapsulate the packet */
error = ipip_output(m, isr, &mp, 0, 0);
if (mp == NULL && !error) {
/* Should never happen. */
DPRINTF(("ipsec4_process_packet: ipip_output "
"returns no mbuf and no error!"));
error = EFAULT;
}
if (error) {
if (mp)
m_freem(mp);
goto bad;
}
m = mp, mp = NULL;
/*
* ipip_output clears IP_DF in the new header. If
* we need to propagate IP_DF from the outer header,
* then we have to do it here.
*
* XXX shouldn't assume what ipip_output does.
*/
if (dst->sa.sa_family == AF_INET && setdf) {
if (m->m_len < sizeof (struct ip) &&
(m = m_pullup(m, sizeof (struct ip))) == NULL) {
error = ENOBUFS;
goto bad;
}
ip = mtod(m, struct ip *);
ip->ip_off = ntohs(ip->ip_off);
ip->ip_off |= IP_DF;
ip->ip_off = htons(ip->ip_off);
}
}
}
/*
* Dispatch to the appropriate IPsec transform logic. The
* packet will be returned for transmission after crypto
* processing, etc. are completed. For encapsulation we
* bypass this call because of the explicit call done above
* (necessary to deal with IP_DF handling for IPv4).
*
* NB: m & sav are ``passed to caller'' who's reponsible for
* for reclaiming their resources.
*/
if (sav->tdb_xform->xf_type != XF_IP4) {
ip = mtod(m, struct ip *);
i = ip->ip_hl << 2;
off = offsetof(struct ip, ip_p);
error = (*sav->tdb_xform->xf_output)(m, isr, NULL, i, off);
} else {
error = ipsec_process_done(m, isr);
}
splx(s);
return error;
bad:
splx(s);
if (m)
m_freem(m);
return error;
}
#endif
#ifdef INET6
/*
* Chop IP6 header from the payload.
*/
static struct mbuf *
ipsec6_splithdr(struct mbuf *m)
{
struct mbuf *mh;
struct ip6_hdr *ip6;
int hlen;
IPSEC_ASSERT(m->m_len >= sizeof (struct ip6_hdr),
("ipsec6_splithdr: first mbuf too short, len %u", m->m_len));
ip6 = mtod(m, struct ip6_hdr *);
hlen = sizeof(struct ip6_hdr);
if (m->m_len > hlen) {
MGETHDR(mh, M_DONTWAIT, MT_HEADER);
if (!mh) {
m_freem(m);
return NULL;
}
M_MOVE_PKTHDR(mh, m);
MH_ALIGN(mh, hlen);
m->m_len -= hlen;
m->m_data += hlen;
mh->m_next = m;
m = mh;
m->m_len = hlen;
bcopy((caddr_t)ip6, mtod(m, caddr_t), hlen);
} else if (m->m_len < hlen) {
m = m_pullup(m, hlen);
if (!m)
return NULL;
}
return m;
}
/*
* IPsec output logic for IPv6, transport mode.
*/
int
ipsec6_output_trans(
struct ipsec_output_state *state,
u_char *nexthdrp,
struct mbuf *mprev,
struct secpolicy *sp,
int flags,
int *tun)
{
struct ipsecrequest *isr;
struct secasindex saidx;
int error = 0;
struct mbuf *m;
IPSEC_ASSERT(state != NULL, ("ipsec6_output: null state"));
IPSEC_ASSERT(state->m != NULL, ("ipsec6_output: null m"));
IPSEC_ASSERT(nexthdrp != NULL, ("ipsec6_output: null nexthdrp"));
IPSEC_ASSERT(mprev != NULL, ("ipsec6_output: null mprev"));
IPSEC_ASSERT(sp != NULL, ("ipsec6_output: null sp"));
IPSEC_ASSERT(tun != NULL, ("ipsec6_output: null tun"));
KEYDEBUG(KEYDEBUG_IPSEC_DATA,
printf("ipsec6_output_trans: applyed SP\n");
kdebug_secpolicy(sp));
isr = sp->req;
if (isr->saidx.mode == IPSEC_MODE_TUNNEL) {
/* the rest will be handled by ipsec6_output_tunnel() */
*tun = 1; /* need tunnel-mode processing */
return 0;
}
*tun = 0;
m = state->m;
isr = ipsec_nextisr(m, isr, AF_INET6, &saidx, &error);
if (isr == NULL) {
#ifdef notdef
/* XXX should notification be done for all errors ? */
/*
* Notify the fact that the packet is discarded
* to ourselves. I believe this is better than
* just silently discarding. (jinmei@kame.net)
* XXX: should we restrict the error to TCP packets?
* XXX: should we directly notify sockets via
* pfctlinputs?
*/
icmp6_error(m, ICMP6_DST_UNREACH,
ICMP6_DST_UNREACH_ADMIN, 0);
m = NULL; /* NB: icmp6_error frees mbuf */
#endif
goto bad;
}
return (*isr->sav->tdb_xform->xf_output)(m, isr, NULL,
sizeof (struct ip6_hdr),
offsetof(struct ip6_hdr, ip6_nxt));
bad:
if (m)
m_freem(m);
state->m = NULL;
return error;
}
static int
ipsec6_encapsulate(struct mbuf *m, struct secasvar *sav)
{
struct ip6_hdr *oip6;
struct ip6_hdr *ip6;
size_t plen;
/* can't tunnel between different AFs */
if (sav->sah->saidx.src.sa.sa_family != AF_INET6 ||
sav->sah->saidx.dst.sa.sa_family != AF_INET6) {
m_freem(m);
return EINVAL;
}
IPSEC_ASSERT(m->m_len != sizeof (struct ip6_hdr),
("ipsec6_encapsulate: mbuf wrong size; len %u", m->m_len));
/*
* grow the mbuf to accomodate the new IPv6 header.
*/
plen = m->m_pkthdr.len;
if (M_LEADINGSPACE(m->m_next) < sizeof(struct ip6_hdr)) {
struct mbuf *n;
MGET(n, M_DONTWAIT, MT_DATA);
if (!n) {
m_freem(m);
return ENOBUFS;
}
n->m_len = sizeof(struct ip6_hdr);
n->m_next = m->m_next;
m->m_next = n;
m->m_pkthdr.len += sizeof(struct ip6_hdr);
oip6 = mtod(n, struct ip6_hdr *);
} else {
m->m_next->m_len += sizeof(struct ip6_hdr);
m->m_next->m_data -= sizeof(struct ip6_hdr);
m->m_pkthdr.len += sizeof(struct ip6_hdr);
oip6 = mtod(m->m_next, struct ip6_hdr *);
}
ip6 = mtod(m, struct ip6_hdr *);
ovbcopy((caddr_t)ip6, (caddr_t)oip6, sizeof(struct ip6_hdr));
/* Fake link-local scope-class addresses */
if (IN6_IS_SCOPE_LINKLOCAL(&oip6->ip6_src))
oip6->ip6_src.s6_addr16[1] = 0;
if (IN6_IS_SCOPE_LINKLOCAL(&oip6->ip6_dst))
oip6->ip6_dst.s6_addr16[1] = 0;
/* construct new IPv6 header. see RFC 2401 5.1.2.2 */
/* ECN consideration. */
ip6_ecn_ingress(ip6_ipsec_ecn, &ip6->ip6_flow, &oip6->ip6_flow);
if (plen < IPV6_MAXPACKET - sizeof(struct ip6_hdr))
ip6->ip6_plen = htons(plen);
else {
/* ip6->ip6_plen will be updated in ip6_output() */
}
ip6->ip6_nxt = IPPROTO_IPV6;
sav->sah->saidx.src.sin6.sin6_addr = ip6->ip6_src;
sav->sah->saidx.dst.sin6.sin6_addr = ip6->ip6_dst;
ip6->ip6_hlim = IPV6_DEFHLIM;
/* XXX Should ip6_src be updated later ? */
return 0;
}
/*
* IPsec output logic for IPv6, tunnel mode.
*/
int
ipsec6_output_tunnel(struct ipsec_output_state *state, struct secpolicy *sp, int flags)
{
struct ip6_hdr *ip6;
struct ipsecrequest *isr;
struct secasindex saidx;
int error;
struct sockaddr_in6* dst6;
struct mbuf *m;
IPSEC_ASSERT(state != NULL, ("ipsec6_output: null state"));
IPSEC_ASSERT(state->m != NULL, ("ipsec6_output: null m"));
IPSEC_ASSERT(sp != NULL, ("ipsec6_output: null sp"));
KEYDEBUG(KEYDEBUG_IPSEC_DATA,
printf("ipsec6_output_tunnel: applyed SP\n");
kdebug_secpolicy(sp));
m = state->m;
/*
* transport mode ipsec (before the 1st tunnel mode) is already
* processed by ipsec6_output_trans().
*/
for (isr = sp->req; isr; isr = isr->next) {
if (isr->saidx.mode == IPSEC_MODE_TUNNEL)
break;
}
isr = ipsec_nextisr(m, isr, AF_INET6, &saidx, &error);
if (isr == NULL)
goto bad;
/*
* There may be the case that SA status will be changed when
* we are refering to one. So calling splsoftnet().
*/
if (isr->saidx.mode == IPSEC_MODE_TUNNEL) {
/*
* build IPsec tunnel.
*/
/* XXX should be processed with other familiy */
if (isr->sav->sah->saidx.src.sa.sa_family != AF_INET6) {
ipseclog((LOG_ERR, "ipsec6_output_tunnel: "
"family mismatched between inner and outer, spi=%lu\n",
ntohl(isr->sav->spi)));
newipsecstat.ips_out_inval++;
error = EAFNOSUPPORT;
goto bad;
}
m = ipsec6_splithdr(m);
if (!m) {
newipsecstat.ips_out_nomem++;
error = ENOMEM;
goto bad;
}
error = ipsec6_encapsulate(m, isr->sav);
if (error) {
m = NULL;
goto bad;
}
ip6 = mtod(m, struct ip6_hdr *);
state->ro = &isr->sav->sah->sa_route;
state->dst = (struct sockaddr *)&state->ro->ro_dst;
dst6 = (struct sockaddr_in6 *)state->dst;
if (state->ro->ro_rt
&& ((state->ro->ro_rt->rt_flags & RTF_UP) == 0
|| !IN6_ARE_ADDR_EQUAL(&dst6->sin6_addr, &ip6->ip6_dst))) {
RTFREE(state->ro->ro_rt);
state->ro->ro_rt = NULL;
}
if (state->ro->ro_rt == 0) {
bzero(dst6, sizeof(*dst6));
dst6->sin6_family = AF_INET6;
dst6->sin6_len = sizeof(*dst6);
dst6->sin6_addr = ip6->ip6_dst;
rtalloc(state->ro);
}
if (state->ro->ro_rt == 0) {
ip6stat.ip6s_noroute++;
newipsecstat.ips_out_noroute++;
error = EHOSTUNREACH;
goto bad;
}
/* adjust state->dst if tunnel endpoint is offlink */
if (state->ro->ro_rt->rt_flags & RTF_GATEWAY) {
state->dst = (struct sockaddr *)state->ro->ro_rt->rt_gateway;
dst6 = (struct sockaddr_in6 *)state->dst;
}
}
m = ipsec6_splithdr(m);
if (!m) {
newipsecstat.ips_out_nomem++;
error = ENOMEM;
goto bad;
}
ip6 = mtod(m, struct ip6_hdr *);
return (*isr->sav->tdb_xform->xf_output)(m, isr, NULL,
sizeof (struct ip6_hdr),
offsetof(struct ip6_hdr, ip6_nxt));
bad:
if (m)
m_freem(m);
state->m = NULL;
return error;
}
#endif /*INET6*/

7365
sys/netipsec/key.c Normal file

File diff suppressed because it is too large Load Diff

115
sys/netipsec/key.h Normal file
View File

@ -0,0 +1,115 @@
/* $NetBSD: key.h,v 1.1 2003/08/13 20:06:51 jonathan Exp $ */
/* $FreeBSD: src/sys/netipsec/key.h,v 1.1.4.1 2003/01/24 05:11:36 sam Exp $ */
/* $KAME: key.h,v 1.21 2001/07/27 03:51:30 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.
*/
#ifndef _NETIPSEC_KEY_H_
#define _NETIPSEC_KEY_H_
#ifdef _KERNEL
struct secpolicy;
struct secpolicyindex;
struct ipsecrequest;
struct secasvar;
struct sockaddr;
struct socket;
struct sadb_msg;
struct sadb_x_policy;
struct secasindex;
union sockaddr_union;
extern int key_havesp(u_int dir);
extern struct secpolicy *key_allocsp(struct secpolicyindex *, u_int,
const char*, int);
extern struct secpolicy *key_allocsp2(u_int32_t spi, union sockaddr_union *dst,
u_int8_t proto, u_int dir, const char*, int);
extern struct secpolicy *key_newsp(const char*, int);
extern struct secpolicy *key_gettunnel(const struct sockaddr *,
const struct sockaddr *, const struct sockaddr *,
const struct sockaddr *, const char*, int);
/* NB: prepend with _ for KAME IPv6 compatbility */
extern void _key_freesp(struct secpolicy **, const char*, int);
/*
* Access to the SADB are interlocked with splsoftnet. In particular,
* holders of SA's use this to block accesses by protocol processing
* that can happen either by network swi's or by continuations that
* occur on crypto callbacks. Much of this could go away if
* key_checkrequest were redone.
*/
#define KEY_ALLOCSP(spidx, dir) \
key_allocsp(spidx, dir, __FILE__, __LINE__)
#define KEY_ALLOCSP2(spi, dst, proto, dir) \
key_allocsp2(spi, dst, proto, dir, __FILE__, __LINE__)
#define KEY_NEWSP() \
key_newsp(__FILE__, __LINE__)
#define KEY_GETTUNNEL(osrc, odst, isrc, idst) \
key_gettunnel(osrc, odst, isrc, idst, __FILE__, __LINE__)
#define KEY_FREESP(spp) \
_key_freesp(spp, __FILE__, __LINE__)
extern struct secasvar *key_allocsa(union sockaddr_union *, u_int, u_int32_t,
const char*, int);
extern void key_freesav(struct secasvar **, const char*, int);
#define KEY_ALLOCSA(dst, proto, spi) \
key_allocsa(dst, proto, spi, __FILE__, __LINE__)
#define KEY_FREESAV(psav) \
key_freesav(psav, __FILE__, __LINE__)
extern void key_freeso __P((struct socket *));
extern int key_checktunnelsanity __P((struct secasvar *, u_int,
caddr_t, caddr_t));
extern int key_checkrequest
__P((struct ipsecrequest *isr, const struct secasindex *));
extern struct secpolicy *key_msg2sp __P((struct sadb_x_policy *,
size_t, int *));
extern struct mbuf *key_sp2msg __P((struct secpolicy *));
extern int key_ismyaddr __P((struct sockaddr *));
extern int key_spdacquire __P((struct secpolicy *));
extern void key_timehandler __P((void*));
extern u_long key_random __P((void));
extern void key_randomfill __P((void *, size_t));
extern void key_freereg __P((struct socket *));
extern int key_parse __P((struct mbuf *, struct socket *));
extern void key_init __P((void));
extern void key_sa_recordxfer __P((struct secasvar *, struct mbuf *));
extern void key_sa_routechange __P((struct sockaddr *));
extern void key_sa_stir_iv __P((struct secasvar *));
#ifdef MALLOC_DECLARE
MALLOC_DECLARE(M_SECA);
#endif /* MALLOC_DECLARE */
#endif /* defined(_KERNEL) */
#endif /* _NETIPSEC_KEY_H_ */

753
sys/netipsec/key_debug.c Normal file
View File

@ -0,0 +1,753 @@
/* $NetBSD: key_debug.c,v 1.1 2003/08/13 20:06:51 jonathan Exp $ */
/* $FreeBSD: src/sys/netipsec/key_debug.c,v 1.1.4.1 2003/01/24 05:11:36 sam Exp $ */
/* $KAME: key_debug.c,v 1.26 2001/06/27 10:46:50 sakane 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.
*/
#ifdef _KERNEL
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: key_debug.c,v 1.1 2003/08/13 20:06:51 jonathan Exp $");
#endif
#include "opt_inet.h"
#include "opt_inet6.h"
#include "opt_ipsec.h"
#include <sys/types.h>
#include <sys/param.h>
#ifdef _KERNEL
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/queue.h>
#endif
#include <sys/socket.h>
#include <net/route.h>
#include <netipsec/key_var.h>
#include <netipsec/key_debug.h>
#include <netinet/in.h>
#include <netipsec/ipsec.h>
#ifndef _KERNEL
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#endif /* !_KERNEL */
static void kdebug_sadb_prop __P((struct sadb_ext *));
static void kdebug_sadb_identity __P((struct sadb_ext *));
static void kdebug_sadb_supported __P((struct sadb_ext *));
static void kdebug_sadb_lifetime __P((struct sadb_ext *));
static void kdebug_sadb_sa __P((struct sadb_ext *));
static void kdebug_sadb_address __P((struct sadb_ext *));
static void kdebug_sadb_key __P((struct sadb_ext *));
static void kdebug_sadb_x_sa2 __P((struct sadb_ext *));
#ifdef _KERNEL
static void kdebug_secreplay __P((struct secreplay *));
#endif
#ifndef _KERNEL
#define panic(param) { printf(param); exit(-1); }
#endif
/* NOTE: host byte order */
/* %%%: about struct sadb_msg */
void
kdebug_sadb(base)
struct sadb_msg *base;
{
struct sadb_ext *ext;
int tlen, extlen;
/* sanity check */
if (base == NULL)
panic("kdebug_sadb: NULL pointer was passed.\n");
printf("sadb_msg{ version=%u type=%u errno=%u satype=%u\n",
base->sadb_msg_version, base->sadb_msg_type,
base->sadb_msg_errno, base->sadb_msg_satype);
printf(" len=%u reserved=%u seq=%u pid=%u\n",
base->sadb_msg_len, base->sadb_msg_reserved,
base->sadb_msg_seq, base->sadb_msg_pid);
tlen = PFKEY_UNUNIT64(base->sadb_msg_len) - sizeof(struct sadb_msg);
ext = (struct sadb_ext *)((caddr_t)base + sizeof(struct sadb_msg));
while (tlen > 0) {
printf("sadb_ext{ len=%u type=%u }\n",
ext->sadb_ext_len, ext->sadb_ext_type);
if (ext->sadb_ext_len == 0) {
printf("kdebug_sadb: invalid ext_len=0 was passed.\n");
return;
}
if (ext->sadb_ext_len > tlen) {
printf("kdebug_sadb: ext_len exceeds end of buffer.\n");
return;
}
switch (ext->sadb_ext_type) {
case SADB_EXT_SA:
kdebug_sadb_sa(ext);
break;
case SADB_EXT_LIFETIME_CURRENT:
case SADB_EXT_LIFETIME_HARD:
case SADB_EXT_LIFETIME_SOFT:
kdebug_sadb_lifetime(ext);
break;
case SADB_EXT_ADDRESS_SRC:
case SADB_EXT_ADDRESS_DST:
case SADB_EXT_ADDRESS_PROXY:
kdebug_sadb_address(ext);
break;
case SADB_EXT_KEY_AUTH:
case SADB_EXT_KEY_ENCRYPT:
kdebug_sadb_key(ext);
break;
case SADB_EXT_IDENTITY_SRC:
case SADB_EXT_IDENTITY_DST:
kdebug_sadb_identity(ext);
break;
case SADB_EXT_SENSITIVITY:
break;
case SADB_EXT_PROPOSAL:
kdebug_sadb_prop(ext);
break;
case SADB_EXT_SUPPORTED_AUTH:
case SADB_EXT_SUPPORTED_ENCRYPT:
kdebug_sadb_supported(ext);
break;
case SADB_EXT_SPIRANGE:
case SADB_X_EXT_KMPRIVATE:
break;
case SADB_X_EXT_POLICY:
kdebug_sadb_x_policy(ext);
break;
case SADB_X_EXT_SA2:
kdebug_sadb_x_sa2(ext);
break;
default:
printf("kdebug_sadb: invalid ext_type %u was passed.\n",
ext->sadb_ext_type);
return;
}
extlen = PFKEY_UNUNIT64(ext->sadb_ext_len);
tlen -= extlen;
ext = (struct sadb_ext *)((caddr_t)ext + extlen);
}
return;
}
static void
kdebug_sadb_prop(ext)
struct sadb_ext *ext;
{
struct sadb_prop *prop = (struct sadb_prop *)ext;
struct sadb_comb *comb;
int len;
/* sanity check */
if (ext == NULL)
panic("kdebug_sadb_prop: NULL pointer was passed.\n");
len = (PFKEY_UNUNIT64(prop->sadb_prop_len) - sizeof(*prop))
/ sizeof(*comb);
comb = (struct sadb_comb *)(prop + 1);
printf("sadb_prop{ replay=%u\n", prop->sadb_prop_replay);
while (len--) {
printf("sadb_comb{ auth=%u encrypt=%u "
"flags=0x%04x reserved=0x%08x\n",
comb->sadb_comb_auth, comb->sadb_comb_encrypt,
comb->sadb_comb_flags, comb->sadb_comb_reserved);
printf(" auth_minbits=%u auth_maxbits=%u "
"encrypt_minbits=%u encrypt_maxbits=%u\n",
comb->sadb_comb_auth_minbits,
comb->sadb_comb_auth_maxbits,
comb->sadb_comb_encrypt_minbits,
comb->sadb_comb_encrypt_maxbits);
printf(" soft_alloc=%u hard_alloc=%u "
"soft_bytes=%lu hard_bytes=%lu\n",
comb->sadb_comb_soft_allocations,
comb->sadb_comb_hard_allocations,
(unsigned long)comb->sadb_comb_soft_bytes,
(unsigned long)comb->sadb_comb_hard_bytes);
printf(" soft_alloc=%lu hard_alloc=%lu "
"soft_bytes=%lu hard_bytes=%lu }\n",
(unsigned long)comb->sadb_comb_soft_addtime,
(unsigned long)comb->sadb_comb_hard_addtime,
(unsigned long)comb->sadb_comb_soft_usetime,
(unsigned long)comb->sadb_comb_hard_usetime);
comb++;
}
printf("}\n");
return;
}
static void
kdebug_sadb_identity(ext)
struct sadb_ext *ext;
{
struct sadb_ident *id = (struct sadb_ident *)ext;
int len;
/* sanity check */
if (ext == NULL)
panic("kdebug_sadb_identity: NULL pointer was passed.\n");
len = PFKEY_UNUNIT64(id->sadb_ident_len) - sizeof(*id);
printf("sadb_ident_%s{",
id->sadb_ident_exttype == SADB_EXT_IDENTITY_SRC ? "src" : "dst");
switch (id->sadb_ident_type) {
default:
printf(" type=%d id=%lu",
id->sadb_ident_type, (u_long)id->sadb_ident_id);
if (len) {
#ifdef _KERNEL
ipsec_hexdump((caddr_t)(id + 1), len); /*XXX cast ?*/
#else
char *p, *ep;
printf("\n str=\"");
p = (char *)(id + 1);
ep = p + len;
for (/*nothing*/; *p && p < ep; p++) {
if (isprint(*p))
printf("%c", *p & 0xff);
else
printf("\\%03o", *p & 0xff);
}
#endif
printf("\"");
}
break;
}
printf(" }\n");
return;
}
static void
kdebug_sadb_supported(ext)
struct sadb_ext *ext;
{
struct sadb_supported *sup = (struct sadb_supported *)ext;
struct sadb_alg *alg;
int len;
/* sanity check */
if (ext == NULL)
panic("kdebug_sadb_supported: NULL pointer was passed.\n");
len = (PFKEY_UNUNIT64(sup->sadb_supported_len) - sizeof(*sup))
/ sizeof(*alg);
alg = (struct sadb_alg *)(sup + 1);
printf("sadb_sup{\n");
while (len--) {
printf(" { id=%d ivlen=%d min=%d max=%d }\n",
alg->sadb_alg_id, alg->sadb_alg_ivlen,
alg->sadb_alg_minbits, alg->sadb_alg_maxbits);
alg++;
}
printf("}\n");
return;
}
static void
kdebug_sadb_lifetime(ext)
struct sadb_ext *ext;
{
struct sadb_lifetime *lft = (struct sadb_lifetime *)ext;
/* sanity check */
if (ext == NULL)
printf("kdebug_sadb_lifetime: NULL pointer was passed.\n");
printf("sadb_lifetime{ alloc=%u, bytes=%u\n",
lft->sadb_lifetime_allocations,
(u_int32_t)lft->sadb_lifetime_bytes);
printf(" addtime=%u, usetime=%u }\n",
(u_int32_t)lft->sadb_lifetime_addtime,
(u_int32_t)lft->sadb_lifetime_usetime);
return;
}
static void
kdebug_sadb_sa(ext)
struct sadb_ext *ext;
{
struct sadb_sa *sa = (struct sadb_sa *)ext;
/* sanity check */
if (ext == NULL)
panic("kdebug_sadb_sa: NULL pointer was passed.\n");
printf("sadb_sa{ spi=%u replay=%u state=%u\n",
(u_int32_t)ntohl(sa->sadb_sa_spi), sa->sadb_sa_replay,
sa->sadb_sa_state);
printf(" auth=%u encrypt=%u flags=0x%08x }\n",
sa->sadb_sa_auth, sa->sadb_sa_encrypt, sa->sadb_sa_flags);
return;
}
static void
kdebug_sadb_address(ext)
struct sadb_ext *ext;
{
struct sadb_address *addr = (struct sadb_address *)ext;
/* sanity check */
if (ext == NULL)
panic("kdebug_sadb_address: NULL pointer was passed.\n");
printf("sadb_address{ proto=%u prefixlen=%u reserved=0x%02x%02x }\n",
addr->sadb_address_proto, addr->sadb_address_prefixlen,
((u_char *)&addr->sadb_address_reserved)[0],
((u_char *)&addr->sadb_address_reserved)[1]);
kdebug_sockaddr((struct sockaddr *)((caddr_t)ext + sizeof(*addr)));
return;
}
static void
kdebug_sadb_key(ext)
struct sadb_ext *ext;
{
struct sadb_key *key = (struct sadb_key *)ext;
/* sanity check */
if (ext == NULL)
panic("kdebug_sadb_key: NULL pointer was passed.\n");
printf("sadb_key{ bits=%u reserved=%u\n",
key->sadb_key_bits, key->sadb_key_reserved);
printf(" key=");
/* sanity check 2 */
if ((key->sadb_key_bits >> 3) >
(PFKEY_UNUNIT64(key->sadb_key_len) - sizeof(struct sadb_key))) {
printf("kdebug_sadb_key: key length mismatch, bit:%d len:%ld.\n",
key->sadb_key_bits >> 3,
(long)PFKEY_UNUNIT64(key->sadb_key_len) - sizeof(struct sadb_key));
}
ipsec_hexdump((caddr_t)key + sizeof(struct sadb_key),
key->sadb_key_bits >> 3);
printf(" }\n");
return;
}
static void
kdebug_sadb_x_sa2(ext)
struct sadb_ext *ext;
{
struct sadb_x_sa2 *sa2 = (struct sadb_x_sa2 *)ext;
/* sanity check */
if (ext == NULL)
panic("kdebug_sadb_x_sa2: NULL pointer was passed.\n");
printf("sadb_x_sa2{ mode=%u reqid=%u\n",
sa2->sadb_x_sa2_mode, sa2->sadb_x_sa2_reqid);
printf(" reserved1=%u reserved2=%u sequence=%u }\n",
sa2->sadb_x_sa2_reserved1, sa2->sadb_x_sa2_reserved2,
sa2->sadb_x_sa2_sequence);
return;
}
void
kdebug_sadb_x_policy(ext)
struct sadb_ext *ext;
{
struct sadb_x_policy *xpl = (struct sadb_x_policy *)ext;
struct sockaddr *addr;
/* sanity check */
if (ext == NULL)
panic("kdebug_sadb_x_policy: NULL pointer was passed.\n");
printf("sadb_x_policy{ type=%u dir=%u id=%x }\n",
xpl->sadb_x_policy_type, xpl->sadb_x_policy_dir,
xpl->sadb_x_policy_id);
if (xpl->sadb_x_policy_type == IPSEC_POLICY_IPSEC) {
int tlen;
struct sadb_x_ipsecrequest *xisr;
tlen = PFKEY_UNUNIT64(xpl->sadb_x_policy_len) - sizeof(*xpl);
xisr = (struct sadb_x_ipsecrequest *)(xpl + 1);
while (tlen > 0) {
printf(" { len=%u proto=%u mode=%u level=%u reqid=%u\n",
xisr->sadb_x_ipsecrequest_len,
xisr->sadb_x_ipsecrequest_proto,
xisr->sadb_x_ipsecrequest_mode,
xisr->sadb_x_ipsecrequest_level,
xisr->sadb_x_ipsecrequest_reqid);
if (xisr->sadb_x_ipsecrequest_len > sizeof(*xisr)) {
addr = (struct sockaddr *)(xisr + 1);
kdebug_sockaddr(addr);
addr = (struct sockaddr *)((caddr_t)addr
+ addr->sa_len);
kdebug_sockaddr(addr);
}
printf(" }\n");
/* prevent infinite loop */
if (xisr->sadb_x_ipsecrequest_len <= 0) {
printf("kdebug_sadb_x_policy: wrong policy struct.\n");
return;
}
/* prevent overflow */
if (xisr->sadb_x_ipsecrequest_len > tlen) {
printf("invalid ipsec policy length\n");
return;
}
tlen -= xisr->sadb_x_ipsecrequest_len;
xisr = (struct sadb_x_ipsecrequest *)((caddr_t)xisr
+ xisr->sadb_x_ipsecrequest_len);
}
if (tlen != 0)
panic("kdebug_sadb_x_policy: wrong policy struct.\n");
}
return;
}
#ifdef _KERNEL
/* %%%: about SPD and SAD */
void
kdebug_secpolicy(sp)
struct secpolicy *sp;
{
/* sanity check */
if (sp == NULL)
panic("kdebug_secpolicy: NULL pointer was passed.\n");
printf("secpolicy{ refcnt=%u state=%u policy=%u\n",
sp->refcnt, sp->state, sp->policy);
kdebug_secpolicyindex(&sp->spidx);
switch (sp->policy) {
case IPSEC_POLICY_DISCARD:
printf(" type=discard }\n");
break;
case IPSEC_POLICY_NONE:
printf(" type=none }\n");
break;
case IPSEC_POLICY_IPSEC:
{
struct ipsecrequest *isr;
for (isr = sp->req; isr != NULL; isr = isr->next) {
printf(" level=%u\n", isr->level);
kdebug_secasindex(&isr->saidx);
if (isr->sav != NULL)
kdebug_secasv(isr->sav);
}
printf(" }\n");
}
break;
case IPSEC_POLICY_BYPASS:
printf(" type=bypass }\n");
break;
case IPSEC_POLICY_ENTRUST:
printf(" type=entrust }\n");
break;
default:
printf("kdebug_secpolicy: Invalid policy found. %d\n",
sp->policy);
break;
}
return;
}
void
kdebug_secpolicyindex(spidx)
struct secpolicyindex *spidx;
{
/* sanity check */
if (spidx == NULL)
panic("kdebug_secpolicyindex: NULL pointer was passed.\n");
printf("secpolicyindex{ dir=%u prefs=%u prefd=%u ul_proto=%u\n",
spidx->dir, spidx->prefs, spidx->prefd, spidx->ul_proto);
ipsec_hexdump((caddr_t)&spidx->src,
((struct sockaddr *)&spidx->src)->sa_len);
printf("\n");
ipsec_hexdump((caddr_t)&spidx->dst,
((struct sockaddr *)&spidx->dst)->sa_len);
printf("}\n");
return;
}
void
kdebug_secasindex(saidx)
struct secasindex *saidx;
{
/* sanity check */
if (saidx == NULL)
panic("kdebug_secpolicyindex: NULL pointer was passed.\n");
printf("secasindex{ mode=%u proto=%u\n",
saidx->mode, saidx->proto);
ipsec_hexdump((caddr_t)&saidx->src,
((struct sockaddr *)&saidx->src)->sa_len);
printf("\n");
ipsec_hexdump((caddr_t)&saidx->dst,
((struct sockaddr *)&saidx->dst)->sa_len);
printf("\n");
return;
}
void
kdebug_secasv(sav)
struct secasvar *sav;
{
/* sanity check */
if (sav == NULL)
panic("kdebug_secasv: NULL pointer was passed.\n");
printf("secas{");
kdebug_secasindex(&sav->sah->saidx);
printf(" refcnt=%u state=%u auth=%u enc=%u\n",
sav->refcnt, sav->state, sav->alg_auth, sav->alg_enc);
printf(" spi=%u flags=%u\n",
(u_int32_t)ntohl(sav->spi), sav->flags);
if (sav->key_auth != NULL)
kdebug_sadb_key((struct sadb_ext *)sav->key_auth);
if (sav->key_enc != NULL)
kdebug_sadb_key((struct sadb_ext *)sav->key_enc);
if (sav->iv != NULL) {
printf(" iv=");
ipsec_hexdump(sav->iv, sav->ivlen ? sav->ivlen : 8);
printf("\n");
}
if (sav->replay != NULL)
kdebug_secreplay(sav->replay);
if (sav->lft_c != NULL)
kdebug_sadb_lifetime((struct sadb_ext *)sav->lft_c);
if (sav->lft_h != NULL)
kdebug_sadb_lifetime((struct sadb_ext *)sav->lft_h);
if (sav->lft_s != NULL)
kdebug_sadb_lifetime((struct sadb_ext *)sav->lft_s);
#if notyet
/* XXX: misc[123] ? */
#endif
return;
}
static void
kdebug_secreplay(rpl)
struct secreplay *rpl;
{
int len, l;
/* sanity check */
if (rpl == NULL)
panic("kdebug_secreplay: NULL pointer was passed.\n");
printf(" secreplay{ count=%u wsize=%u seq=%u lastseq=%u",
rpl->count, rpl->wsize, rpl->seq, rpl->lastseq);
if (rpl->bitmap == NULL) {
printf(" }\n");
return;
}
printf("\n bitmap { ");
for (len = 0; len < rpl->wsize; len++) {
for (l = 7; l >= 0; l--)
printf("%u", (((rpl->bitmap)[len] >> l) & 1) ? 1 : 0);
}
printf(" }\n");
return;
}
void
kdebug_mbufhdr(m)
struct mbuf *m;
{
/* sanity check */
if (m == NULL)
return;
printf("mbuf(%p){ m_next:%p m_nextpkt:%p m_data:%p "
"m_len:%d m_type:0x%02x m_flags:0x%02x }\n",
m, m->m_next, m->m_nextpkt, m->m_data,
m->m_len, m->m_type, m->m_flags);
if (m->m_flags & M_PKTHDR) {
printf(" m_pkthdr{ len:%d rcvif:%p }\n",
m->m_pkthdr.len, m->m_pkthdr.rcvif);
}
if (m->m_flags & M_EXT) {
#ifdef __FreeBSD__ /* mbuf differences */
printf(" m_ext{ ext_buf:%p ext_free:%p "
"ext_size:%u ext_ref:%p }\n",
m->m_ext.ext_buf, m->m_ext.ext_free,
m->m_ext.ext_size, m->m_ext.ext_ref);
#endif __FreeBSD__
}
return;
}
void
kdebug_mbuf(m0)
struct mbuf *m0;
{
struct mbuf *m = m0;
int i, j;
for (j = 0; m; m = m->m_next) {
kdebug_mbufhdr(m);
printf(" m_data:\n");
for (i = 0; i < m->m_len; i++) {
if (i && i % 32 == 0)
printf("\n");
if (i % 4 == 0)
printf(" ");
printf("%02x", mtod(m, u_char *)[i]);
j++;
}
printf("\n");
}
return;
}
#endif /* _KERNEL */
void
kdebug_sockaddr(addr)
struct sockaddr *addr;
{
struct sockaddr_in *sin4;
#ifdef INET6
struct sockaddr_in6 *sin6;
#endif
/* sanity check */
if (addr == NULL)
panic("kdebug_sockaddr: NULL pointer was passed.\n");
/* NOTE: We deal with port number as host byte order. */
printf("sockaddr{ len=%u family=%u", addr->sa_len, addr->sa_family);
switch (addr->sa_family) {
case AF_INET:
sin4 = (struct sockaddr_in *)addr;
printf(" port=%u\n", ntohs(sin4->sin_port));
ipsec_hexdump((caddr_t)&sin4->sin_addr, sizeof(sin4->sin_addr));
break;
#ifdef INET6
case AF_INET6:
sin6 = (struct sockaddr_in6 *)addr;
printf(" port=%u\n", ntohs(sin6->sin6_port));
printf(" flowinfo=0x%08x, scope_id=0x%08x\n",
sin6->sin6_flowinfo, sin6->sin6_scope_id);
ipsec_hexdump((caddr_t)&sin6->sin6_addr,
sizeof(sin6->sin6_addr));
break;
#endif
}
printf(" }\n");
return;
}
void
ipsec_bindump(buf, len)
caddr_t buf;
int len;
{
int i;
for (i = 0; i < len; i++)
printf("%c", (unsigned char)buf[i]);
return;
}
void
ipsec_hexdump(buf, len)
caddr_t buf;
int len;
{
int i;
for (i = 0; i < len; i++) {
if (i != 0 && i % 32 == 0) printf("\n");
if (i % 4 == 0) printf(" ");
printf("%02x", (unsigned char)buf[i]);
}
#if 0
if (i % 32 != 0) printf("\n");
#endif
return;
}

89
sys/netipsec/key_debug.h Normal file
View File

@ -0,0 +1,89 @@
/* $NetBSD: key_debug.h,v 1.1 2003/08/13 20:06:51 jonathan Exp $ */
/* $FreeBSD: src/sys/netipsec/key_debug.h,v 1.1.4.1 2003/01/24 05:11:36 sam Exp $ */
/* $KAME: key_debug.h,v 1.10 2001/08/05 08:37:52 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.
*/
#ifndef _NETIPSEC_KEY_DEBUG_H_
#define _NETIPSEC_KEY_DEBUG_H_
#ifdef _KERNEL
/* debug flags */
#define KEYDEBUG_STAMP 0x00000001 /* path */
#define KEYDEBUG_DATA 0x00000002 /* data */
#define KEYDEBUG_DUMP 0x00000004 /* dump */
#define KEYDEBUG_KEY 0x00000010 /* key processing */
#define KEYDEBUG_ALG 0x00000020 /* ciph & auth algorithm */
#define KEYDEBUG_IPSEC 0x00000040 /* ipsec processing */
#define KEYDEBUG_KEY_STAMP (KEYDEBUG_KEY | KEYDEBUG_STAMP)
#define KEYDEBUG_KEY_DATA (KEYDEBUG_KEY | KEYDEBUG_DATA)
#define KEYDEBUG_KEY_DUMP (KEYDEBUG_KEY | KEYDEBUG_DUMP)
#define KEYDEBUG_ALG_STAMP (KEYDEBUG_ALG | KEYDEBUG_STAMP)
#define KEYDEBUG_ALG_DATA (KEYDEBUG_ALG | KEYDEBUG_DATA)
#define KEYDEBUG_ALG_DUMP (KEYDEBUG_ALG | KEYDEBUG_DUMP)
#define KEYDEBUG_IPSEC_STAMP (KEYDEBUG_IPSEC | KEYDEBUG_STAMP)
#define KEYDEBUG_IPSEC_DATA (KEYDEBUG_IPSEC | KEYDEBUG_DATA)
#define KEYDEBUG_IPSEC_DUMP (KEYDEBUG_IPSEC | KEYDEBUG_DUMP)
#define KEYDEBUG(lev,arg) \
do { if ((key_debug_level & (lev)) == (lev)) { arg; } } while (/*CONSTCOND*/ 0)
extern u_int32_t key_debug_level;
#endif /*_KERNEL*/
struct sadb_msg;
struct sadb_ext;
extern void kdebug_sadb __P((struct sadb_msg *));
extern void kdebug_sadb_x_policy __P((struct sadb_ext *));
#ifdef _KERNEL
struct secpolicy;
struct secpolicyindex;
struct secasindex;
struct secasvar;
struct secreplay;
struct mbuf;
extern void kdebug_secpolicy __P((struct secpolicy *));
extern void kdebug_secpolicyindex __P((struct secpolicyindex *));
extern void kdebug_secasindex __P((struct secasindex *));
extern void kdebug_secasv __P((struct secasvar *));
extern void kdebug_mbufhdr __P((struct mbuf *));
extern void kdebug_mbuf __P((struct mbuf *));
#endif /*_KERNEL*/
struct sockaddr;
extern void kdebug_sockaddr __P((struct sockaddr *));
extern void ipsec_hexdump __P((caddr_t, int));
extern void ipsec_bindump __P((caddr_t, int));
#endif /* _NETIPSEC_KEY_DEBUG_H_ */

91
sys/netipsec/key_var.h Normal file
View File

@ -0,0 +1,91 @@
/* $NetBSD: key_var.h,v 1.1 2003/08/13 20:06:51 jonathan Exp $ */
/* $FreeBSD: src/sys/netipsec/key_var.h,v 1.1.4.1 2003/01/24 05:11:36 sam Exp $ */
/* $KAME: key_var.h,v 1.11 2001/09/12 23:05:07 sakane 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.
*/
#ifndef _NETIPSEC_KEY_VAR_H_
#define _NETIPSEC_KEY_VAR_H_
/* sysctl */
#define KEYCTL_DEBUG_LEVEL 1
#define KEYCTL_SPI_TRY 2
#define KEYCTL_SPI_MIN_VALUE 3
#define KEYCTL_SPI_MAX_VALUE 4
#define KEYCTL_RANDOM_INT 5
#define KEYCTL_LARVAL_LIFETIME 6
#define KEYCTL_BLOCKACQ_COUNT 7
#define KEYCTL_BLOCKACQ_LIFETIME 8
#define KEYCTL_ESP_KEYMIN 9
#define KEYCTL_ESP_AUTH 10
#define KEYCTL_AH_KEYMIN 11
#define KEYCTL_PREFERED_OLDSA 12
#define KEYCTL_MAXID 13
#define KEYCTL_NAMES { \
{ 0, 0 }, \
{ "debug", CTLTYPE_INT }, \
{ "spi_try", CTLTYPE_INT }, \
{ "spi_min_value", CTLTYPE_INT }, \
{ "spi_max_value", CTLTYPE_INT }, \
{ "random_int", CTLTYPE_INT }, \
{ "larval_lifetime", CTLTYPE_INT }, \
{ "blockacq_count", CTLTYPE_INT }, \
{ "blockacq_lifetime", CTLTYPE_INT }, \
{ "esp_keymin", CTLTYPE_INT }, \
{ "esp_auth", CTLTYPE_INT }, \
{ "ah_keymin", CTLTYPE_INT }, \
{ "prefered_oldsa", CTLTYPE_INT }, \
}
#define KEYCTL_VARS { \
0, \
0, \
&key_spi_trycnt, \
&key_spi_minval, \
&key_spi_maxval, \
&key_int_random, \
&key_larval_lifetime, \
&key_blockacq_count, \
&key_blockacq_lifetime, \
&ipsec_esp_keymin, \
&ipsec_esp_auth, \
&ipsec_ah_keymin, \
&key_prefered_oldsa, \
}
#ifdef _KERNEL
#define _ARRAYLEN(p) (sizeof(p)/sizeof(p[0]))
#define _KEYLEN(key) ((u_int)((key)->sadb_key_bits >> 3))
#define _KEYBITS(key) ((u_int)((key)->sadb_key_bits))
#define _KEYBUF(key) ((u_int8_t *)((u_int8_t*)(key) + sizeof(struct sadb_key)))
#endif /*_KERNEL*/
#endif /* _NETIPSEC_KEY_VAR_H_ */

184
sys/netipsec/keydb.h Normal file
View File

@ -0,0 +1,184 @@
/* $NetBSD: keydb.h,v 1.1 2003/08/13 20:06:51 jonathan Exp $ */
/* $FreeBSD: src/sys/netipsec/keydb.h,v 1.1.4.1 2003/01/24 05:11:36 sam Exp $ */
/* $KAME: keydb.h,v 1.14 2000/08/02 17:58:26 sakane 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.
*/
#ifndef _NETIPSEC_KEYDB_H_
#define _NETIPSEC_KEYDB_H_
#ifdef _KERNEL
#include <netipsec/key_var.h>
#include <net/route.h>
#include <netinet/in.h>
/*
* The union of all possible address formats we handle.
*/
union sockaddr_union {
struct sockaddr sa;
struct sockaddr_in sin;
struct sockaddr_in6 sin6;
};
/* Security Assocciation Index */
/* NOTE: Ensure to be same address family */
struct secasindex {
union sockaddr_union src; /* srouce address for SA */
union sockaddr_union dst; /* destination address for SA */
u_int16_t proto; /* IPPROTO_ESP or IPPROTO_AH */
u_int8_t mode; /* mode of protocol, see ipsec.h */
u_int32_t reqid; /* reqid id who owned this SA */
/* see IPSEC_MANUAL_REQID_MAX. */
};
/* Security Association Data Base */
struct secashead {
LIST_ENTRY(secashead) chain;
struct secasindex saidx;
struct sadb_ident *idents; /* source identity */
struct sadb_ident *identd; /* destination identity */
/* XXX I don't know how to use them. */
u_int8_t state; /* MATURE or DEAD. */
LIST_HEAD(_satree, secasvar) savtree[SADB_SASTATE_MAX+1];
/* SA chain */
/* The first of this list is newer SA */
struct route sa_route; /* route cache */
};
struct xformsw;
struct enc_xform;
struct auth_hash;
struct comp_algo;
/* Security Association */
struct secasvar {
LIST_ENTRY(secasvar) chain;
u_int refcnt; /* reference count */
u_int8_t state; /* Status of this Association */
u_int8_t alg_auth; /* Authentication Algorithm Identifier*/
u_int8_t alg_enc; /* Cipher Algorithm Identifier */
u_int8_t alg_comp; /* Compression Algorithm Identifier */
u_int32_t spi; /* SPI Value, network byte order */
u_int32_t flags; /* holder for SADB_KEY_FLAGS */
struct sadb_key *key_auth; /* Key for Authentication */
struct sadb_key *key_enc; /* Key for Encryption */
caddr_t iv; /* Initilization Vector */
u_int ivlen; /* length of IV */
void *sched; /* intermediate encryption key */
size_t schedlen;
struct secreplay *replay; /* replay prevention */
long created; /* for lifetime */
struct sadb_lifetime *lft_c; /* CURRENT lifetime, it's constant. */
struct sadb_lifetime *lft_h; /* HARD lifetime */
struct sadb_lifetime *lft_s; /* SOFT lifetime */
u_int32_t seq; /* sequence number */
pid_t pid; /* message's pid */
struct secashead *sah; /* back pointer to the secashead */
/*
* NB: Fields with a tdb_ prefix are part of the "glue" used
* to interface to the OpenBSD crypto support. This was done
* to distinguish this code from the mainline KAME code.
*/
struct xformsw *tdb_xform; /* transform */
struct enc_xform *tdb_encalgxform; /* encoding algorithm */
struct auth_hash *tdb_authalgxform; /* authentication algorithm */
struct comp_algo *tdb_compalgxform; /* compression algorithm */
u_int64_t tdb_cryptoid; /* crypto session id */
};
/* replay prevention */
struct secreplay {
u_int32_t count;
u_int wsize; /* window size, i.g. 4 bytes */
u_int32_t seq; /* used by sender */
u_int32_t lastseq; /* used by receiver */
caddr_t bitmap; /* used by receiver */
int overflow; /* overflow flag */
};
/* socket table due to send PF_KEY messages. */
struct secreg {
LIST_ENTRY(secreg) chain;
struct socket *so;
};
#ifndef IPSEC_NONBLOCK_ACQUIRE
/* acquiring list table. */
struct secacq {
LIST_ENTRY(secacq) chain;
struct secasindex saidx;
u_int32_t seq; /* sequence number */
long created; /* for lifetime */
int count; /* for lifetime */
};
#endif
/* Sensitivity Level Specification */
/* nothing */
#define SADB_KILL_INTERVAL 600 /* six seconds */
/* secpolicy */
extern struct secpolicy *keydb_newsecpolicy __P((void));
extern void keydb_delsecpolicy __P((struct secpolicy *));
/* secashead */
extern struct secashead *keydb_newsecashead __P((void));
extern void keydb_delsecashead __P((struct secashead *));
/* secasvar */
extern struct secasvar *keydb_newsecasvar __P((void));
extern void keydb_refsecasvar __P((struct secasvar *));
extern void keydb_freesecasvar __P((struct secasvar *));
/* secreplay */
extern struct secreplay *keydb_newsecreplay __P((size_t));
extern void keydb_delsecreplay __P((struct secreplay *));
/* secreg */
extern struct secreg *keydb_newsecreg __P((void));
extern void keydb_delsecreg __P((struct secreg *));
#endif /* _KERNEL */
#endif /* _NETIPSEC_KEYDB_H_ */

695
sys/netipsec/keysock.c Normal file
View File

@ -0,0 +1,695 @@
/* $NetBSD: keysock.c,v 1.1 2003/08/13 20:06:51 jonathan Exp $ */
/* $FreeBSD: src/sys/netipsec/keysock.c,v 1.3.2.1 2003/01/24 05:11:36 sam Exp $ */
/* $KAME: keysock.c,v 1.25 2001/08/13 20:07:41 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: keysock.c,v 1.1 2003/08/13 20:06:51 jonathan Exp $");
#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/malloc.h>
#include <sys/mbuf.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/raw_cb.h>
#include <net/route.h>
#include <net/pfkeyv2.h>
#include <netipsec/key.h>
#include <netipsec/keysock.h>
#include <netipsec/key_debug.h>
#include <netipsec/ipsec_osdep.h>
#include <machine/stdarg.h>
typedef int pr_output_t (struct mbuf *, struct socket *);
struct key_cb {
int key_count;
int any_count;
};
static struct key_cb key_cb;
static struct sockaddr key_dst = { 2, PF_KEY, };
static struct sockaddr key_src = { 2, PF_KEY, };
static int key_sendup0 __P((struct rawcb *, struct mbuf *, int));
struct pfkeystat pfkeystat;
/*
* key_output()
*/
int
#if __STDC__
key_output(struct mbuf *m, ...)
#else
key_output(m, va_alist)
struct mbuf *m;
va_dcl
#endif
{
struct sadb_msg *msg;
int len, error = 0;
int s;
struct socket *so;
va_list ap;
va_start(ap, m);
so = va_arg(ap, struct socket *);
va_end(ap);
if (m == 0)
panic("key_output: NULL pointer was passed.\n");
pfkeystat.out_total++;
pfkeystat.out_bytes += m->m_pkthdr.len;
len = m->m_pkthdr.len;
if (len < sizeof(struct sadb_msg)) {
pfkeystat.out_tooshort++;
error = EINVAL;
goto end;
}
if (m->m_len < sizeof(struct sadb_msg)) {
if ((m = m_pullup(m, sizeof(struct sadb_msg))) == 0) {
pfkeystat.out_nomem++;
error = ENOBUFS;
goto end;
}
}
if ((m->m_flags & M_PKTHDR) == 0)
panic("key_output: not M_PKTHDR ??");
KEYDEBUG(KEYDEBUG_KEY_DUMP, kdebug_mbuf(m));
msg = mtod(m, struct sadb_msg *);
pfkeystat.out_msgtype[msg->sadb_msg_type]++;
if (len != PFKEY_UNUNIT64(msg->sadb_msg_len)) {
pfkeystat.out_invlen++;
error = EINVAL;
goto end;
}
/*XXX giant lock*/
s = splsoftnet();
error = key_parse(m, so);
m = NULL;
splx(s);
end:
if (m)
m_freem(m);
return error;
}
/*
* send message to the socket.
*/
static int
key_sendup0(rp, m, promisc)
struct rawcb *rp;
struct mbuf *m;
int promisc;
{
int error;
if (promisc) {
struct sadb_msg *pmsg;
M_PREPEND(m, sizeof(struct sadb_msg), M_DONTWAIT);
if (m && m->m_len < sizeof(struct sadb_msg))
m = m_pullup(m, sizeof(struct sadb_msg));
if (!m) {
pfkeystat.in_nomem++;
m_freem(m);
return ENOBUFS;
}
m->m_pkthdr.len += sizeof(*pmsg);
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.in_msgtype[pmsg->sadb_msg_type]++;
}
if (!sbappendaddr(&rp->rcb_socket->so_rcv, (struct sockaddr *)&key_src,
m, NULL)) {
pfkeystat.in_nomem++;
m_freem(m);
error = ENOBUFS;
} else
error = 0;
sorwakeup(rp->rcb_socket);
return error;
}
/* XXX this interface should be obsoleted. */
int
key_sendup(so, msg, len, target)
struct socket *so;
struct sadb_msg *msg;
u_int len;
int target; /*target of the resulting message*/
{
struct mbuf *m, *n, *mprev;
int tlen;
/* sanity check */
if (so == 0 || msg == 0)
panic("key_sendup: NULL pointer was passed.\n");
KEYDEBUG(KEYDEBUG_KEY_DUMP,
printf("key_sendup: \n");
kdebug_sadb(msg));
/*
* we increment statistics here, just in case we have ENOBUFS
* in this function.
*/
pfkeystat.in_total++;
pfkeystat.in_bytes += len;
pfkeystat.in_msgtype[msg->sadb_msg_type]++;
/*
* Get mbuf chain whenever possible (not clusters),
* to save socket buffer. We'll be generating many SADB_ACQUIRE
* messages to listening key sockets. If we simply allocate clusters,
* sbappendaddr() will raise ENOBUFS due to too little sbspace().
* sbspace() computes # of actual data bytes AND mbuf region.
*
* TODO: SADB_ACQUIRE filters should be implemented.
*/
tlen = len;
m = mprev = NULL;
while (tlen > 0) {
if (tlen == len) {
MGETHDR(n, M_DONTWAIT, MT_DATA);
n->m_len = MHLEN;
} else {
MGET(n, M_DONTWAIT, MT_DATA);
n->m_len = MLEN;
}
if (!n) {
pfkeystat.in_nomem++;
return ENOBUFS;
}
if (tlen >= MCLBYTES) { /*XXX better threshold? */
MCLGET(n, M_DONTWAIT);
if ((n->m_flags & M_EXT) == 0) {
m_free(n);
m_freem(m);
pfkeystat.in_nomem++;
return ENOBUFS;
}
n->m_len = MCLBYTES;
}
if (tlen < n->m_len)
n->m_len = tlen;
n->m_next = NULL;
if (m == NULL)
m = mprev = n;
else {
mprev->m_next = n;
mprev = n;
}
tlen -= n->m_len;
n = NULL;
}
m->m_pkthdr.len = len;
m->m_pkthdr.rcvif = NULL;
m_copyback(m, 0, len, (caddr_t)msg);
/* avoid duplicated statistics */
pfkeystat.in_total--;
pfkeystat.in_bytes -= len;
pfkeystat.in_msgtype[msg->sadb_msg_type]--;
return key_sendup_mbuf(so, m, target);
}
/* so can be NULL if target != KEY_SENDUP_ONE */
int
key_sendup_mbuf(so, m, target)
struct socket *so;
struct mbuf *m;
int target;
{
struct mbuf *n;
struct keycb *kp;
int sendup;
struct rawcb *rp;
int error = 0;
if (m == NULL)
panic("key_sendup_mbuf: NULL pointer was passed.\n");
if (so == NULL && target == KEY_SENDUP_ONE)
panic("key_sendup_mbuf: NULL pointer was passed.\n");
pfkeystat.in_total++;
pfkeystat.in_bytes += m->m_pkthdr.len;
if (m->m_len < sizeof(struct sadb_msg)) {
#if 1
m = m_pullup(m, sizeof(struct sadb_msg));
if (m == NULL) {
pfkeystat.in_nomem++;
return ENOBUFS;
}
#else
/* don't bother pulling it up just for stats */
#endif
}
if (m->m_len >= sizeof(struct sadb_msg)) {
struct sadb_msg *msg;
msg = mtod(m, struct sadb_msg *);
pfkeystat.in_msgtype[msg->sadb_msg_type]++;
}
LIST_FOREACH(rp, &rawcb_list, rcb_list)
{
if (rp->rcb_proto.sp_family != PF_KEY)
continue;
if (rp->rcb_proto.sp_protocol
&& rp->rcb_proto.sp_protocol != PF_KEY_V2) {
continue;
}
kp = (struct keycb *)rp;
/*
* 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 (((struct keycb *)rp)->kp_promisc) {
if ((n = m_copy(m, 0, (int)M_COPYALL)) != NULL) {
(void)key_sendup0(rp, n, 1);
n = NULL;
}
}
/* the exact target will be processed later */
if (so && sotorawcb(so) == rp)
continue;
sendup = 0;
switch (target) {
case KEY_SENDUP_ONE:
/* the statement has no effect */
if (so && sotorawcb(so) == rp)
sendup++;
break;
case KEY_SENDUP_ALL:
sendup++;
break;
case KEY_SENDUP_REGISTERED:
if (kp->kp_registered)
sendup++;
break;
}
pfkeystat.in_msgtarget[target]++;
if (!sendup)
continue;
if ((n = m_copy(m, 0, (int)M_COPYALL)) == NULL) {
m_freem(m);
pfkeystat.in_nomem++;
return ENOBUFS;
}
if ((error = key_sendup0(rp, n, 0)) != 0) {
m_freem(m);
return error;
}
n = NULL;
}
if (so) {
error = key_sendup0(sotorawcb(so), m, 0);
m = NULL;
} else {
error = 0;
m_freem(m);
}
return error;
}
#ifdef __FreeBSD__
/*
* key_abort()
* derived from net/rtsock.c:rts_abort()
*/
static int
key_abort(struct socket *so)
{
int s, error;
s = splnet(); /* FreeBSD */
error = raw_usrreqs.pru_abort(so);
splx(s);
return error;
}
/*
* key_attach()
* derived from net/rtsock.c:rts_attach()
*/
static int
key_attach(struct socket *so, int proto, struct proc *td)
{
struct keycb *kp;
int s, error;
if (sotorawcb(so) != 0)
return EISCONN; /* XXX panic? */
kp = (struct keycb *)malloc(sizeof *kp, M_PCB, M_WAITOK|M_ZERO); /* XXX */
if (kp == 0)
return ENOBUFS;
/*
* The spl[soft]net() is necessary to block protocols from sending
* error notifications (like RTM_REDIRECT or RTM_LOSING) while
* this PCB is extant but incompletely initialized.
* Probably we should try to do more of this work beforehand and
* eliminate the spl.
*/
s = splnet(); /* FreeBSD */
so->so_pcb = (caddr_t)kp;
error = raw_usrreqs.pru_attach(so, proto, td);
kp = (struct keycb *)sotorawcb(so);
if (error) {
free(kp, M_PCB);
so->so_pcb = (caddr_t) 0;
splx(s);
return error;
}
kp->kp_promisc = kp->kp_registered = 0;
if (kp->kp_raw.rcb_proto.sp_protocol == PF_KEY) /* XXX: AF_KEY */
key_cb.key_count++;
key_cb.any_count++;
kp->kp_raw.rcb_laddr = &key_src;
kp->kp_raw.rcb_faddr = &key_dst;
soisconnected(so);
so->so_options |= SO_USELOOPBACK;
splx(s);
return 0;
}
/*
* key_bind()
* derived from net/rtsock.c:rts_bind()
*/
static int
key_bind(struct socket *so, struct sockaddr *nam, struct proc *td)
{
int s, error;
s = splnet(); /* FreeBSD */
error = raw_usrreqs.pru_bind(so, nam, td); /* xxx just EINVAL */
splx(s);
return error;
}
/*
* key_connect()
* derived from net/rtsock.c:rts_connect()
*/
static int
key_connect(struct socket *so, struct sockaddr *nam, struct proc *td)
{
int s, error;
s = splnet(); /* FreeBSD */
error = raw_usrreqs.pru_connect(so, nam, td); /* XXX just EINVAL */
splx(s);
return error;
}
/*
* key_detach()
* derived from net/rtsock.c:rts_detach()
*/
static int
key_detach(struct socket *so)
{
struct keycb *kp = (struct keycb *)sotorawcb(so);
int s, error;
s = splnet(); /* FreeBSD */
if (kp != 0) {
if (kp->kp_raw.rcb_proto.sp_protocol
== PF_KEY) /* XXX: AF_KEY */
key_cb.key_count--;
key_cb.any_count--;
key_freereg(so);
}
error = raw_usrreqs.pru_detach(so);
splx(s);
return error;
}
/*
* key_disconnect()
* derived from net/rtsock.c:key_disconnect()
*/
static int
key_disconnect(struct socket *so)
{
int s, error;
s = splnet(); /* FreeBSD */
error = raw_usrreqs.pru_disconnect(so);
splx(s);
return error;
}
/*
* key_peeraddr()
* derived from net/rtsock.c:rts_peeraddr()
*/
static int
key_peeraddr(struct socket *so, struct sockaddr **nam)
{
int s, error;
s = splnet(); /* FreeBSD */
error = raw_usrreqs.pru_peeraddr(so, nam);
splx(s);
return error;
}
/*
* key_send()
* derived from net/rtsock.c:rts_send()
*/
static int
key_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam,
struct mbuf *control, struct proc *td)
{
int s, error;
s = splnet(); /* FreeBSD */
error = raw_usrreqs.pru_send(so, flags, m, nam, control, td);
splx(s);
return error;
}
/*
* key_shutdown()
* derived from net/rtsock.c:rts_shutdown()
*/
static int
key_shutdown(struct socket *so)
{
int s, error;
s = splnet(); /* FreeBSD */
error = raw_usrreqs.pru_shutdown(so);
splx(s);
return error;
}
/*
* key_sockaddr()
* derived from net/rtsock.c:rts_sockaddr()
*/
static int
key_sockaddr(struct socket *so, struct sockaddr **nam)
{
int s, error;
s = splnet(); /* FreeBSD */
error = raw_usrreqs.pru_sockaddr(so, nam);
splx(s);
return error;
}
#else /*!__FreeBSD__ -- traditional proto_usrreq() switch */
/*
* key_usrreq()
* derived from net/rtsock.c:route_usrreq()
*/
int
key_usrreq(so, req, m, nam, control, p)
struct socket *so;
int req;
struct mbuf *m, *nam, *control;
struct proc *p;
{
int error = 0;
struct keycb *kp = (struct keycb *)sotorawcb(so);
int s;
s = splsoftnet();
if (req == PRU_ATTACH) {
kp = (struct keycb *)malloc(sizeof(*kp), M_PCB, M_WAITOK);
so->so_pcb = (caddr_t)kp;
if (so->so_pcb)
bzero(so->so_pcb, sizeof(*kp));
}
if (req == PRU_DETACH && kp) {
int af = kp->kp_raw.rcb_proto.sp_protocol;
if (af == PF_KEY) /* XXX: AF_KEY */
key_cb.key_count--;
key_cb.any_count--;
key_freereg(so);
}
error = raw_usrreq(so, req, m, nam, control, p);
m = control = NULL; /* reclaimed in raw_usrreq */
kp = (struct keycb *)sotorawcb(so);
if (req == PRU_ATTACH && kp) {
int af = kp->kp_raw.rcb_proto.sp_protocol;
if (error) {
pfkeystat.sockerr++;
free((caddr_t)kp, M_PCB);
so->so_pcb = (caddr_t) 0;
splx(s);
return (error);
}
kp->kp_promisc = kp->kp_registered = 0;
if (af == PF_KEY) /* XXX: AF_KEY */
key_cb.key_count++;
key_cb.any_count++;
kp->kp_raw.rcb_laddr = &key_src;
kp->kp_raw.rcb_faddr = &key_dst;
soisconnected(so);
so->so_options |= SO_USELOOPBACK;
}
splx(s);
return (error);
}
#endif /*!__FreeBSD__*/
/* sysctl */
#ifdef SYSCTL_NODE
SYSCTL_NODE(_net, PF_KEY, key, CTLFLAG_RW, 0, "Key Family");
#endif SYSCTL_NODE
/*
* Definitions of protocols supported in the KEY domain.
*/
/* This extern declaration is all that's common... */
extern struct domain keydomain;
#ifdef __FreeBSD__
struct pr_usrreqs key_usrreqs = {
key_abort, pru_accept_notsupp, key_attach, key_bind,
key_connect,
pru_connect2_notsupp, pru_control_notsupp, key_detach,
key_disconnect, pru_listen_notsupp, key_peeraddr,
pru_rcvd_notsupp,
pru_rcvoob_notsupp, key_send, pru_sense_null, key_shutdown,
key_sockaddr, sosend, soreceive, sopoll
};
struct protosw keysw[] = {
{ SOCK_RAW, &keydomain, PF_KEY_V2, PR_ATOMIC|PR_ADDR,
0, (pr_output_t *)key_output, raw_ctlinput, 0,
0,
raw_init, 0, 0, 0,
&key_usrreqs
}
};
static void
key_init0(void)
{
bzero((caddr_t)&key_cb, sizeof(key_cb));
key_init();
}
struct domain keydomain =
{ PF_KEY, "key", key_init0, 0, 0,
keysw, &keysw[sizeof(keysw)/sizeof(keysw[0])] };
DOMAIN_SET(key);
#else /* !__FreeBSD__ */
struct protosw keysw[] = {
{ SOCK_RAW, &keydomain, PF_KEY_V2, PR_ATOMIC|PR_ADDR,
0, key_output, raw_ctlinput, 0,
key_usrreq,
raw_init, 0, 0, 0,
key_sysctl,
}
};
struct domain keydomain =
{ PF_KEY, "key", key_init, 0, 0,
keysw, &keysw[sizeof(keysw)/sizeof(keysw[0])] };
#endif

89
sys/netipsec/keysock.h Normal file
View File

@ -0,0 +1,89 @@
/* $NetBSD: keysock.h,v 1.1 2003/08/13 20:06:51 jonathan Exp $ */
/* $FreeBSD: src/sys/netipsec/keysock.h,v 1.1.4.1 2003/01/24 05:11:36 sam Exp $ */
/* $KAME: keysock.h,v 1.8 2000/03/27 05:11:06 sumikawa 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.
*/
#ifndef _NETIPSEC_KEYSOCK_H_
#define _NETIPSEC_KEYSOCK_H_
/* statistics for pfkey socket */
struct pfkeystat {
/* kernel -> userland */
u_quad_t out_total; /* # of total calls */
u_quad_t out_bytes; /* total bytecount */
u_quad_t out_msgtype[256]; /* message type histogram */
u_quad_t out_invlen; /* invalid length field */
u_quad_t out_invver; /* invalid version field */
u_quad_t out_invmsgtype; /* invalid message type field */
u_quad_t out_tooshort; /* msg too short */
u_quad_t out_nomem; /* memory allocation failure */
u_quad_t out_dupext; /* duplicate extension */
u_quad_t out_invexttype; /* invalid extension type */
u_quad_t out_invsatype; /* invalid sa type */
u_quad_t out_invaddr; /* invalid address extension */
/* userland -> kernel */
u_quad_t in_total; /* # of total calls */
u_quad_t in_bytes; /* total bytecount */
u_quad_t in_msgtype[256]; /* message type histogram */
u_quad_t in_msgtarget[3]; /* one/all/registered */
u_quad_t in_nomem; /* memory allocation failure */
/* others */
u_quad_t sockerr; /* # of socket related errors */
};
#define KEY_SENDUP_ONE 0
#define KEY_SENDUP_ALL 1
#define KEY_SENDUP_REGISTERED 2
#ifdef _KERNEL
struct keycb {
struct rawcb kp_raw; /* rawcb */
int kp_promisc; /* promiscuous mode */
int kp_registered; /* registered socket */
};
extern struct pfkeystat pfkeystat;
extern int key_output __P((struct mbuf *, ...));
#ifndef __NetBSD__
extern int key_usrreq __P((struct socket *,
int, struct mbuf *, struct mbuf *, struct mbuf *));
#else
extern int key_usrreq __P((struct socket *,
int, struct mbuf *, struct mbuf *, struct mbuf *, struct proc *));
extern int key_sysctl __P((int *, u_int, void *, size_t *, void *, size_t));
#endif
extern int key_sendup __P((struct socket *, struct sadb_msg *, u_int, int));
extern int key_sendup_mbuf __P((struct socket *, struct mbuf *, int));
#endif /* _KERNEL */
#endif /*_NETIPSEC_KEYSOCK_H_*/

127
sys/netipsec/xform.h Normal file
View File

@ -0,0 +1,127 @@
/* $NetBSD: xform.h,v 1.1 2003/08/13 20:06:51 jonathan Exp $ */
/* $FreeBSD: src/sys/netipsec/xform.h,v 1.1.4.1 2003/01/24 05:11:36 sam Exp $ */
/* $OpenBSD: ip_ipsp.h,v 1.119 2002/03/14 01:27:11 millert Exp $ */
/*
* The authors of this code are John Ioannidis (ji@tla.org),
* Angelos D. Keromytis (kermit@csd.uch.gr),
* Niels Provos (provos@physnet.uni-hamburg.de) and
* Niklas Hallqvist (niklas@appli.se).
*
* The original version of this code was written by John Ioannidis
* for BSD/OS in Athens, Greece, in November 1995.
*
* Ported to OpenBSD and NetBSD, with additional transforms, in December 1996,
* by Angelos D. Keromytis.
*
* Additional transforms and features in 1997 and 1998 by Angelos D. Keromytis
* and Niels Provos.
*
* Additional features in 1999 by Angelos D. Keromytis and Niklas Hallqvist.
*
* Copyright (c) 1995, 1996, 1997, 1998, 1999 by John Ioannidis,
* Angelos D. Keromytis and Niels Provos.
* Copyright (c) 1999 Niklas Hallqvist.
* Copyright (c) 2001, Angelos D. Keromytis.
*
* Permission to use, copy, and modify this software with or without fee
* is hereby granted, provided that this entire notice is included in
* all copies of any software which is or includes a copy or
* modification of this software.
* You may use this code under the GNU public license if you so wish. Please
* contribute changes back to the authors under this freer than GPL license
* so that we may further the use of strong encryption without limitations to
* all.
*
* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY
* REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE
* MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR
* PURPOSE.
*/
#ifndef _NETIPSEC_XFORM_H_
#define _NETIPSEC_XFORM_H_
#include <sys/types.h>
#include <netinet/in.h>
#include <opencrypto/xform.h>
#define AH_HMAC_HASHLEN 12 /* 96 bits of authenticator */
#define AH_HMAC_INITIAL_RPL 1 /* replay counter initial value */
/*
* Packet tag assigned on completion of IPsec processing; used
* to speedup processing when/if the packet comes back for more
* processing.
*/
struct tdb_ident {
u_int32_t spi;
union sockaddr_union dst;
u_int8_t proto;
};
/*
* Opaque data structure hung off a crypto operation descriptor.
*/
struct tdb_crypto {
struct ipsecrequest *tc_isr; /* ipsec request state */
u_int32_t tc_spi; /* associated SPI */
union sockaddr_union tc_dst; /* dst addr of packet */
u_int8_t tc_proto; /* current protocol, e.g. AH */
u_int8_t tc_nxt; /* next protocol, e.g. IPV4 */
int tc_protoff; /* current protocol offset */
int tc_skip; /* data offset */
caddr_t tc_ptr; /* associated crypto data */
};
struct secasvar;
struct ipescrequest;
struct xformsw {
u_short xf_type; /* xform ID */
#define XF_IP4 1 /* IP inside IP */
#define XF_AH 2 /* AH */
#define XF_ESP 3 /* ESP */
#define XF_TCPSIGNATURE 5 /* TCP MD5 Signature option, RFC 2358 */
#define XF_IPCOMP 6 /* IPCOMP */
u_short xf_flags;
#define XFT_AUTH 0x0001
#define XFT_CONF 0x0100
#define XFT_COMP 0x1000
char *xf_name; /* human-readable name */
int (*xf_init)(struct secasvar*, struct xformsw*); /* setup */
int (*xf_zeroize)(struct secasvar*); /* cleanup */
int (*xf_input)(struct mbuf*, struct secasvar*, /* input */
int, int);
int (*xf_output)(struct mbuf*, /* output */
struct ipsecrequest *, struct mbuf **, int, int);
struct xformsw *xf_next; /* list of registered xforms */
};
#ifdef _KERNEL
extern void xform_register(struct xformsw*);
extern int xform_init(struct secasvar *sav, int xftype);
struct cryptoini;
/* XF_IP4 */
extern int ip4_input6(struct mbuf **m, int *offp, int proto);
extern void ip4_input(struct mbuf *m, ...);
extern int ipip_output(struct mbuf *, struct ipsecrequest *,
struct mbuf **, int, int);
/* XF_AH */
extern int ah_init0(struct secasvar *, struct xformsw *, struct cryptoini *);
extern int ah_zeroize(struct secasvar *sav);
extern struct auth_hash *ah_algorithm_lookup(int alg);
extern size_t ah_hdrsiz(struct secasvar *);
/* XF_ESP */
extern struct enc_xform *esp_algorithm_lookup(int alg);
extern size_t esp_hdrsiz(struct secasvar *sav);
/* XF_COMP */
extern struct comp_algo *ipcomp_algorithm_lookup(int alg);
#endif /* _KERNEL */
#endif /* _NETIPSEC_XFORM_H_ */

1266
sys/netipsec/xform_ah.c Normal file

File diff suppressed because it is too large Load Diff

975
sys/netipsec/xform_esp.c Normal file
View File

@ -0,0 +1,975 @@
/* $NetBSD: xform_esp.c,v 1.1 2003/08/13 20:06:51 jonathan Exp $ */
/* $FreeBSD: src/sys/netipsec/xform_esp.c,v 1.2.2.1 2003/01/24 05:11:36 sam Exp $ */
/* $OpenBSD: ip_esp.c,v 1.69 2001/06/26 06:18:59 angelos Exp $ */
/*
* The authors of this code are John Ioannidis (ji@tla.org),
* Angelos D. Keromytis (kermit@csd.uch.gr) and
* Niels Provos (provos@physnet.uni-hamburg.de).
*
* The original version of this code was written by John Ioannidis
* for BSD/OS in Athens, Greece, in November 1995.
*
* Ported to OpenBSD and NetBSD, with additional transforms, in December 1996,
* by Angelos D. Keromytis.
*
* Additional transforms and features in 1997 and 1998 by Angelos D. Keromytis
* and Niels Provos.
*
* Additional features in 1999 by Angelos D. Keromytis.
*
* Copyright (C) 1995, 1996, 1997, 1998, 1999 by John Ioannidis,
* Angelos D. Keromytis and Niels Provos.
* Copyright (c) 2001 Angelos D. Keromytis.
*
* Permission to use, copy, and modify this software with or without fee
* is hereby granted, provided that this entire notice is included in
* all copies of any software which is or includes a copy or
* modification of this software.
* You may use this code under the GNU public license if you so wish. Please
* contribute changes back to the authors under this freer than GPL license
* so that we may further the use of strong encryption without limitations to
* all.
*
* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY
* REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE
* MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR
* PURPOSE.
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: xform_esp.c,v 1.1 2003/08/13 20:06:51 jonathan Exp $");
#include "opt_inet.h"
#include "opt_inet6.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/syslog.h>
#include <sys/kernel.h>
/*#include <sys/random.h>*/
#include <sys/sysctl.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/ip_ecn.h>
#include <netinet/ip6.h>
#include <net/route.h>
#include <netipsec/ipsec.h>
#include <netipsec/ah.h>
#include <netipsec/ah_var.h>
#include <netipsec/esp.h>
#include <netipsec/esp_var.h>
#include <netipsec/xform.h>
#ifdef INET6
#include <netinet6/ip6_var.h>
#include <netipsec/ipsec6.h>
#include <netinet6/ip6_ecn.h>
#endif
#include <netipsec/key.h>
#include <netipsec/key_debug.h>
#include <netipsec/ipsec_osdep.h>
#include <opencrypto/cryptodev.h>
#include <opencrypto/xform.h>
int esp_enable = 1;
struct espstat espstat;
#ifdef __FreeBSD__
SYSCTL_DECL(_net_inet_esp);
SYSCTL_INT(_net_inet_esp, OID_AUTO,
esp_enable, CTLFLAG_RW, &esp_enable, 0, "");
SYSCTL_STRUCT(_net_inet_esp, IPSECCTL_STATS,
stats, CTLFLAG_RD, &espstat, espstat, "");
#endif __FreeBSD__
static int esp_max_ivlen; /* max iv length over all algorithms */
static int esp_input_cb(struct cryptop *op);
static int esp_output_cb(struct cryptop *crp);
/*
* NB: this is public for use by the PF_KEY support.
* NB: if you add support here; be sure to add code to esp_attach below!
*/
struct enc_xform *
esp_algorithm_lookup(int alg)
{
if (alg >= ESP_ALG_MAX)
return NULL;
switch (alg) {
case SADB_EALG_DESCBC:
return &enc_xform_des;
case SADB_EALG_3DESCBC:
return &enc_xform_3des;
case SADB_X_EALG_AES:
return &enc_xform_rijndael128;
case SADB_X_EALG_BLOWFISHCBC:
return &enc_xform_blf;
case SADB_X_EALG_CAST128CBC:
return &enc_xform_cast5;
case SADB_X_EALG_SKIPJACK:
return &enc_xform_skipjack;
case SADB_EALG_NULL:
return &enc_xform_null;
}
return NULL;
}
size_t
esp_hdrsiz(struct secasvar *sav)
{
size_t size;
if (sav != NULL) {
/*XXX not right for null algorithm--does it matter??*/
IPSEC_ASSERT(sav->tdb_encalgxform != NULL,
("esp_hdrsiz: SA with null xform"));
if (sav->flags & SADB_X_EXT_OLD)
size = sizeof (struct esp);
else
size = sizeof (struct newesp);
size += sav->tdb_encalgxform->blocksize + 9;
/*XXX need alg check???*/
if (sav->tdb_authalgxform != NULL && sav->replay)
size += ah_hdrsiz(sav);
} else {
/*
* base header size
* + max iv length for CBC mode
* + max pad length
* + sizeof (pad length field)
* + sizeof (next header field)
* + max icv supported.
*/
size = sizeof (struct newesp) + esp_max_ivlen + 9 + 16;
}
return size;
}
/*
* esp_init() is called when an SPI is being set up.
*/
static int
esp_init(struct secasvar *sav, struct xformsw *xsp)
{
struct enc_xform *txform;
struct cryptoini cria, crie;
int keylen;
int error;
txform = esp_algorithm_lookup(sav->alg_enc);
if (txform == NULL) {
DPRINTF(("esp_init: unsupported encryption algorithm %d\n",
sav->alg_enc));
return EINVAL;
}
if (sav->key_enc == NULL) {
DPRINTF(("esp_init: no encoding key for %s algorithm\n",
txform->name));
return EINVAL;
}
if ((sav->flags&(SADB_X_EXT_OLD|SADB_X_EXT_IV4B)) == SADB_X_EXT_IV4B) {
DPRINTF(("esp_init: 4-byte IV not supported with protocol\n"));
return EINVAL;
}
keylen = _KEYLEN(sav->key_enc);
if (txform->minkey > keylen || keylen > txform->maxkey) {
DPRINTF(("esp_init: invalid key length %u, must be in "
"the range [%u..%u] for algorithm %s\n",
keylen, txform->minkey, txform->maxkey,
txform->name));
return EINVAL;
}
/*
* NB: The null xform needs a non-zero blocksize to keep the
* crypto code happy but if we use it to set ivlen then
* the ESP header will be processed incorrectly. The
* compromise is to force it to zero here.
*/
sav->ivlen = (txform == &enc_xform_null ? 0 : txform->blocksize);
sav->iv = (caddr_t) malloc(sav->ivlen, M_XDATA, M_WAITOK);
if (sav->iv == NULL) {
DPRINTF(("esp_init: no memory for IV\n"));
return EINVAL;
}
key_randomfill(sav->iv, sav->ivlen); /*XXX*/
/*
* Setup AH-related state.
*/
if (sav->alg_auth != 0) {
error = ah_init0(sav, xsp, &cria);
if (error)
return error;
}
/* NB: override anything set in ah_init0 */
sav->tdb_xform = xsp;
sav->tdb_encalgxform = txform;
/* Initialize crypto session. */
bzero(&crie, sizeof (crie));
crie.cri_alg = sav->tdb_encalgxform->type;
crie.cri_klen = _KEYBITS(sav->key_enc);
crie.cri_key = _KEYBUF(sav->key_enc);
/* XXX Rounds ? */
if (sav->tdb_authalgxform && sav->tdb_encalgxform) {
/* init both auth & enc */
crie.cri_next = &cria;
error = crypto_newsession(&sav->tdb_cryptoid,
&crie, crypto_support);
} else if (sav->tdb_encalgxform) {
error = crypto_newsession(&sav->tdb_cryptoid,
&crie, crypto_support);
} else if (sav->tdb_authalgxform) {
error = crypto_newsession(&sav->tdb_cryptoid,
&cria, crypto_support);
} else {
/* XXX cannot happen? */
DPRINTF(("esp_init: no encoding OR authentication xform!\n"));
error = EINVAL;
}
return error;
}
/*
* Paranoia.
*/
static int
esp_zeroize(struct secasvar *sav)
{
/* NB: ah_zerorize free's the crypto session state */
int error = ah_zeroize(sav);
if (sav->key_enc)
bzero(_KEYBUF(sav->key_enc), _KEYLEN(sav->key_enc));
/* NB: sav->iv is freed elsewhere, even though we malloc it! */
sav->tdb_encalgxform = NULL;
sav->tdb_xform = NULL;
return error;
}
/*
* ESP input processing, called (eventually) through the protocol switch.
*/
static int
esp_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
{
struct auth_hash *esph;
struct enc_xform *espx;
struct tdb_ident *tdbi;
struct tdb_crypto *tc;
int plen, alen, hlen;
struct m_tag *mtag;
struct newesp *esp;
struct cryptodesc *crde;
struct cryptop *crp;
IPSEC_SPLASSERT_SOFTNET("esp_input");
IPSEC_ASSERT(sav != NULL, ("esp_input: null SA"));
IPSEC_ASSERT(sav->tdb_encalgxform != NULL,
("esp_input: null encoding xform"));
IPSEC_ASSERT((skip&3) == 0 && (m->m_pkthdr.len&3) == 0,
("esp_input: misaligned packet, skip %u pkt len %u",
skip, m->m_pkthdr.len));
/* XXX don't pullup, just copy header */
IP6_EXTHDR_GET(esp, struct newesp *, m, skip, sizeof (struct newesp));
esph = sav->tdb_authalgxform;
espx = sav->tdb_encalgxform;
/* Determine the ESP header length */
if (sav->flags & SADB_X_EXT_OLD)
hlen = sizeof (struct esp) + sav->ivlen;
else
hlen = sizeof (struct newesp) + sav->ivlen;
/* Authenticator hash size */
alen = esph ? AH_HMAC_HASHLEN : 0;
/*
* Verify payload length is multiple of encryption algorithm
* block size.
*
* NB: This works for the null algorithm because the blocksize
* is 4 and all packets must be 4-byte aligned regardless
* of the algorithm.
*/
plen = m->m_pkthdr.len - (skip + hlen + alen);
if ((plen & (espx->blocksize - 1)) || (plen <= 0)) {
DPRINTF(("esp_input: "
"payload of %d octets not a multiple of %d octets,"
" SA %s/%08lx\n",
plen, espx->blocksize,
ipsec_address(&sav->sah->saidx.dst),
(u_long) ntohl(sav->spi)));
espstat.esps_badilen++;
m_freem(m);
return EINVAL;
}
/*
* Check sequence number.
*/
if (esph && sav->replay && !ipsec_chkreplay(ntohl(esp->esp_seq), sav)) {
DPRINTF(("esp_input: packet replay check for %s\n",
ipsec_logsastr(sav))); /*XXX*/
espstat.esps_replay++;
m_freem(m);
return ENOBUFS; /*XXX*/
}
/* Update the counters */
espstat.esps_ibytes += m->m_pkthdr.len - skip - hlen - alen;
/* Find out if we've already done crypto */
for (mtag = m_tag_find(m, PACKET_TAG_IPSEC_IN_CRYPTO_DONE, NULL);
mtag != NULL;
mtag = m_tag_find(m, PACKET_TAG_IPSEC_IN_CRYPTO_DONE, mtag)) {
tdbi = (struct tdb_ident *) (mtag + 1);
if (tdbi->proto == sav->sah->saidx.proto &&
tdbi->spi == sav->spi &&
!bcmp(&tdbi->dst, &sav->sah->saidx.dst,
sizeof(union sockaddr_union)))
break;
}
/* Get crypto descriptors */
crp = crypto_getreq(esph && espx ? 2 : 1);
if (crp == NULL) {
DPRINTF(("esp_input: failed to acquire crypto descriptors\n"));
espstat.esps_crypto++;
m_freem(m);
return ENOBUFS;
}
/* Get IPsec-specific opaque pointer */
if (esph == NULL || mtag != NULL)
tc = (struct tdb_crypto *) malloc(sizeof(struct tdb_crypto),
M_XDATA, M_NOWAIT|M_ZERO);
else
tc = (struct tdb_crypto *) malloc(sizeof(struct tdb_crypto) + alen,
M_XDATA, M_NOWAIT|M_ZERO);
if (tc == NULL) {
crypto_freereq(crp);
DPRINTF(("esp_input: failed to allocate tdb_crypto\n"));
espstat.esps_crypto++;
m_freem(m);
return ENOBUFS;
}
tc->tc_ptr = (caddr_t) mtag;
if (esph) {
struct cryptodesc *crda = crp->crp_desc;
IPSEC_ASSERT(crda != NULL, ("esp_input: null ah crypto descriptor"));
/* Authentication descriptor */
crda->crd_skip = skip;
crda->crd_len = m->m_pkthdr.len - (skip + alen);
crda->crd_inject = m->m_pkthdr.len - alen;
crda->crd_alg = esph->type;
crda->crd_key = _KEYBUF(sav->key_auth);
crda->crd_klen = _KEYBITS(sav->key_auth);
/* Copy the authenticator */
if (mtag == NULL)
m_copydata(m, m->m_pkthdr.len - alen, alen,
(caddr_t) (tc + 1));
/* Chain authentication request */
crde = crda->crd_next;
} else {
crde = crp->crp_desc;
}
/* Crypto operation descriptor */
crp->crp_ilen = m->m_pkthdr.len; /* Total input length */
crp->crp_flags = CRYPTO_F_IMBUF;
crp->crp_buf = (caddr_t) m;
crp->crp_callback = esp_input_cb;
crp->crp_sid = sav->tdb_cryptoid;
crp->crp_opaque = (caddr_t) tc;
/* These are passed as-is to the callback */
tc->tc_spi = sav->spi;
tc->tc_dst = sav->sah->saidx.dst;
tc->tc_proto = sav->sah->saidx.proto;
tc->tc_protoff = protoff;
tc->tc_skip = skip;
/* Decryption descriptor */
if (espx) {
IPSEC_ASSERT(crde != NULL, ("esp_input: null esp crypto descriptor"));
crde->crd_skip = skip + hlen;
crde->crd_len = m->m_pkthdr.len - (skip + hlen + alen);
crde->crd_inject = skip + hlen - sav->ivlen;
crde->crd_alg = espx->type;
crde->crd_key = _KEYBUF(sav->key_enc);
crde->crd_klen = _KEYBITS(sav->key_enc);
/* XXX Rounds ? */
}
if (mtag == NULL)
return crypto_dispatch(crp);
else
return esp_input_cb(crp);
}
#ifdef INET6
#define IPSEC_COMMON_INPUT_CB(m, sav, skip, protoff, mtag) do { \
if (saidx->dst.sa.sa_family == AF_INET6) { \
error = ipsec6_common_input_cb(m, sav, skip, protoff, mtag); \
} else { \
error = ipsec4_common_input_cb(m, sav, skip, protoff, mtag); \
} \
} while (0)
#else
#define IPSEC_COMMON_INPUT_CB(m, sav, skip, protoff, mtag) \
(error = ipsec4_common_input_cb(m, sav, skip, protoff, mtag))
#endif
/*
* ESP input callback from the crypto driver.
*/
static int
esp_input_cb(struct cryptop *crp)
{
u_int8_t lastthree[3], aalg[AH_HMAC_HASHLEN];
int s, hlen, skip, protoff, error;
struct mbuf *m;
struct cryptodesc *crd;
struct auth_hash *esph;
struct enc_xform *espx;
struct tdb_crypto *tc;
struct m_tag *mtag;
struct secasvar *sav;
struct secasindex *saidx;
caddr_t ptr;
crd = crp->crp_desc;
IPSEC_ASSERT(crd != NULL, ("esp_input_cb: null crypto descriptor!"));
tc = (struct tdb_crypto *) crp->crp_opaque;
IPSEC_ASSERT(tc != NULL, ("esp_input_cb: null opaque crypto data area!"));
skip = tc->tc_skip;
protoff = tc->tc_protoff;
mtag = (struct m_tag *) tc->tc_ptr;
m = (struct mbuf *) crp->crp_buf;
s = splsoftnet();
sav = KEY_ALLOCSA(&tc->tc_dst, tc->tc_proto, tc->tc_spi);
if (sav == NULL) {
espstat.esps_notdb++;
DPRINTF(("esp_input_cb: SA expired while in crypto "
"(SA %s/%08lx proto %u)\n", ipsec_address(&tc->tc_dst),
(u_long) ntohl(tc->tc_spi), tc->tc_proto));
error = ENOBUFS; /*XXX*/
goto bad;
}
saidx = &sav->sah->saidx;
IPSEC_ASSERT(saidx->dst.sa.sa_family == AF_INET ||
saidx->dst.sa.sa_family == AF_INET6,
("ah_input_cb: unexpected protocol family %u",
saidx->dst.sa.sa_family));
esph = sav->tdb_authalgxform;
espx = sav->tdb_encalgxform;
/* Check for crypto errors */
if (crp->crp_etype) {
/* Reset the session ID */
if (sav->tdb_cryptoid != 0)
sav->tdb_cryptoid = crp->crp_sid;
if (crp->crp_etype == EAGAIN) {
KEY_FREESAV(&sav);
splx(s);
return crypto_dispatch(crp);
}
espstat.esps_noxform++;
DPRINTF(("esp_input_cb: crypto error %d\n", crp->crp_etype));
error = crp->crp_etype;
goto bad;
}
/* Shouldn't happen... */
if (m == NULL) {
espstat.esps_crypto++;
DPRINTF(("esp_input_cb: bogus returned buffer from crypto\n"));
error = EINVAL;
goto bad;
}
espstat.esps_hist[sav->alg_enc]++;
/* If authentication was performed, check now. */
if (esph != NULL) {
/*
* If we have a tag, it means an IPsec-aware NIC did
* the verification for us. Otherwise we need to
* check the authentication calculation.
*/
ahstat.ahs_hist[sav->alg_auth]++;
if (mtag == NULL) {
/* Copy the authenticator from the packet */
m_copydata(m, m->m_pkthdr.len - esph->authsize,
esph->authsize, aalg);
ptr = (caddr_t) (tc + 1);
/* Verify authenticator */
if (bcmp(ptr, aalg, esph->authsize) != 0) {
DPRINTF(("esp_input_cb: "
"authentication hash mismatch for packet in SA %s/%08lx\n",
ipsec_address(&saidx->dst),
(u_long) ntohl(sav->spi)));
espstat.esps_badauth++;
error = EACCES;
goto bad;
}
}
/* Remove trailing authenticator */
m_adj(m, -(esph->authsize));
}
/* Release the crypto descriptors */
free(tc, M_XDATA), tc = NULL;
crypto_freereq(crp), crp = NULL;
/*
* Packet is now decrypted.
*/
m->m_flags |= M_DECRYPTED;
/* Determine the ESP header length */
if (sav->flags & SADB_X_EXT_OLD)
hlen = sizeof (struct esp) + sav->ivlen;
else
hlen = sizeof (struct newesp) + sav->ivlen;
/* Remove the ESP header and IV from the mbuf. */
error = m_striphdr(m, skip, hlen);
if (error) {
espstat.esps_hdrops++;
DPRINTF(("esp_input_cb: bad mbuf chain, SA %s/%08lx\n",
ipsec_address(&sav->sah->saidx.dst),
(u_long) ntohl(sav->spi)));
goto bad;
}
/* Save the last three bytes of decrypted data */
m_copydata(m, m->m_pkthdr.len - 3, 3, lastthree);
/* Verify pad length */
if (lastthree[1] + 2 > m->m_pkthdr.len - skip) {
espstat.esps_badilen++;
DPRINTF(("esp_input_cb: invalid padding length %d "
"for %u byte packet in SA %s/%08lx\n",
lastthree[1], m->m_pkthdr.len - skip,
ipsec_address(&sav->sah->saidx.dst),
(u_long) ntohl(sav->spi)));
error = EINVAL;
goto bad;
}
/* Verify correct decryption by checking the last padding bytes */
if ((sav->flags & SADB_X_EXT_PMASK) != SADB_X_EXT_PRAND) {
if (lastthree[1] != lastthree[0] && lastthree[1] != 0) {
espstat.esps_badenc++;
DPRINTF(("esp_input_cb: decryption failed "
"for packet in SA %s/%08lx\n",
ipsec_address(&sav->sah->saidx.dst),
(u_long) ntohl(sav->spi)));
DPRINTF(("esp_input_cb: %x %x\n", lastthree[0], lastthree[1]));
error = EINVAL;
goto bad;
}
}
/* Trim the mbuf chain to remove trailing authenticator and padding */
m_adj(m, -(lastthree[1] + 2));
/* Restore the Next Protocol field */
m_copyback(m, protoff, sizeof (u_int8_t), lastthree + 2);
IPSEC_COMMON_INPUT_CB(m, sav, skip, protoff, mtag);
KEY_FREESAV(&sav);
splx(s);
return error;
bad:
if (sav)
KEY_FREESAV(&sav);
splx(s);
if (m != NULL)
m_freem(m);
if (tc != NULL)
free(tc, M_XDATA);
if (crp != NULL)
crypto_freereq(crp);
return error;
}
/*
* ESP output routine, called by ipsec[46]_process_packet().
*/
static int
esp_output(
struct mbuf *m,
struct ipsecrequest *isr,
struct mbuf **mp,
int skip,
int protoff
)
{
struct enc_xform *espx;
struct auth_hash *esph;
int hlen, rlen, plen, padding, blks, alen, i, roff;
struct mbuf *mo = (struct mbuf *) NULL;
struct tdb_crypto *tc;
struct secasvar *sav;
struct secasindex *saidx;
unsigned char *pad;
u_int8_t prot;
int error, maxpacketsize;
struct cryptodesc *crde = NULL, *crda = NULL;
struct cryptop *crp;
IPSEC_SPLASSERT_SOFTNET("esp_output");
sav = isr->sav;
IPSEC_ASSERT(sav != NULL, ("esp_output: null SA"));
esph = sav->tdb_authalgxform;
espx = sav->tdb_encalgxform;
IPSEC_ASSERT(espx != NULL, ("esp_output: null encoding xform"));
if (sav->flags & SADB_X_EXT_OLD)
hlen = sizeof (struct esp) + sav->ivlen;
else
hlen = sizeof (struct newesp) + sav->ivlen;
rlen = m->m_pkthdr.len - skip; /* Raw payload length. */
/*
* NB: The null encoding transform has a blocksize of 4
* so that headers are properly aligned.
*/
blks = espx->blocksize; /* IV blocksize */
/* XXX clamp padding length a la KAME??? */
padding = ((blks - ((rlen + 2) % blks)) % blks) + 2;
plen = rlen + padding; /* Padded payload length. */
if (esph)
alen = AH_HMAC_HASHLEN;
else
alen = 0;
espstat.esps_output++;
saidx = &sav->sah->saidx;
/* Check for maximum packet size violations. */
switch (saidx->dst.sa.sa_family) {
#ifdef INET
case AF_INET:
maxpacketsize = IP_MAXPACKET;
break;
#endif /* INET */
#ifdef INET6
case AF_INET6:
maxpacketsize = IPV6_MAXPACKET;
break;
#endif /* INET6 */
default:
DPRINTF(("esp_output: unknown/unsupported protocol "
"family %d, SA %s/%08lx\n",
saidx->dst.sa.sa_family, ipsec_address(&saidx->dst),
(u_long) ntohl(sav->spi)));
espstat.esps_nopf++;
error = EPFNOSUPPORT;
goto bad;
}
if (skip + hlen + rlen + padding + alen > maxpacketsize) {
DPRINTF(("esp_output: packet in SA %s/%08lx got too big "
"(len %u, max len %u)\n",
ipsec_address(&saidx->dst), (u_long) ntohl(sav->spi),
skip + hlen + rlen + padding + alen, maxpacketsize));
espstat.esps_toobig++;
error = EMSGSIZE;
goto bad;
}
/* Update the counters. */
espstat.esps_obytes += m->m_pkthdr.len - skip;
m = m_clone(m);
if (m == NULL) {
DPRINTF(("esp_output: cannot clone mbuf chain, SA %s/%08lx\n",
ipsec_address(&saidx->dst), (u_long) ntohl(sav->spi)));
espstat.esps_hdrops++;
error = ENOBUFS;
goto bad;
}
/* Inject ESP header. */
mo = m_makespace(m, skip, hlen, &roff);
if (mo == NULL) {
DPRINTF(("esp_output: failed to inject %u byte ESP hdr for SA "
"%s/%08lx\n",
hlen, ipsec_address(&saidx->dst),
(u_long) ntohl(sav->spi)));
espstat.esps_hdrops++; /* XXX diffs from openbsd */
error = ENOBUFS;
goto bad;
}
/* Initialize ESP header. */
bcopy((caddr_t) &sav->spi, mtod(mo, caddr_t) + roff, sizeof(u_int32_t));
if (sav->replay) {
u_int32_t replay = htonl(++(sav->replay->count));
bcopy((caddr_t) &replay,
mtod(mo, caddr_t) + roff + sizeof(u_int32_t),
sizeof(u_int32_t));
}
/*
* Add padding -- better to do it ourselves than use the crypto engine,
* although if/when we support compression, we'd have to do that.
*/
pad = (u_char *) m_pad(m, padding + alen);
if (pad == NULL) {
DPRINTF(("esp_output: m_pad failed for SA %s/%08lx\n",
ipsec_address(&saidx->dst), (u_long) ntohl(sav->spi)));
m = NULL; /* NB: free'd by m_pad */
error = ENOBUFS;
goto bad;
}
/*
* Add padding: random, zero, or self-describing.
* XXX catch unexpected setting
*/
switch (sav->flags & SADB_X_EXT_PMASK) {
case SADB_X_EXT_PRAND:
(void) read_random(pad, padding - 2);
break;
case SADB_X_EXT_PZERO:
bzero(pad, padding - 2);
break;
case SADB_X_EXT_PSEQ:
for (i = 0; i < padding - 2; i++)
pad[i] = i+1;
break;
}
/* Fix padding length and Next Protocol in padding itself. */
pad[padding - 2] = padding - 2;
m_copydata(m, protoff, sizeof(u_int8_t), pad + padding - 1);
/* Fix Next Protocol in IPv4/IPv6 header. */
prot = IPPROTO_ESP;
m_copyback(m, protoff, sizeof(u_int8_t), (u_char *) &prot);
/* Get crypto descriptors. */
crp = crypto_getreq(esph && espx ? 2 : 1);
if (crp == NULL) {
DPRINTF(("esp_output: failed to acquire crypto descriptors\n"));
espstat.esps_crypto++;
error = ENOBUFS;
goto bad;
}
if (espx) {
crde = crp->crp_desc;
crda = crde->crd_next;
/* Encryption descriptor. */
crde->crd_skip = skip + hlen;
crde->crd_len = m->m_pkthdr.len - (skip + hlen + alen);
crde->crd_flags = CRD_F_ENCRYPT;
crde->crd_inject = skip + hlen - sav->ivlen;
/* Encryption operation. */
crde->crd_alg = espx->type;
crde->crd_key = _KEYBUF(sav->key_enc);
crde->crd_klen = _KEYBITS(sav->key_enc);
/* XXX Rounds ? */
} else
crda = crp->crp_desc;
/* IPsec-specific opaque crypto info. */
tc = (struct tdb_crypto *) malloc(sizeof(struct tdb_crypto),
M_XDATA, M_NOWAIT|M_ZERO);
if (tc == NULL) {
crypto_freereq(crp);
DPRINTF(("esp_output: failed to allocate tdb_crypto\n"));
espstat.esps_crypto++;
error = ENOBUFS;
goto bad;
}
/* Callback parameters */
tc->tc_isr = isr;
tc->tc_spi = sav->spi;
tc->tc_dst = saidx->dst;
tc->tc_proto = saidx->proto;
/* Crypto operation descriptor. */
crp->crp_ilen = m->m_pkthdr.len; /* Total input length. */
crp->crp_flags = CRYPTO_F_IMBUF;
crp->crp_buf = (caddr_t) m;
crp->crp_callback = esp_output_cb;
crp->crp_opaque = (caddr_t) tc;
crp->crp_sid = sav->tdb_cryptoid;
if (esph) {
/* Authentication descriptor. */
crda->crd_skip = skip;
crda->crd_len = m->m_pkthdr.len - (skip + alen);
crda->crd_inject = m->m_pkthdr.len - alen;
/* Authentication operation. */
crda->crd_alg = esph->type;
crda->crd_key = _KEYBUF(sav->key_auth);
crda->crd_klen = _KEYBITS(sav->key_auth);
}
return crypto_dispatch(crp);
bad:
if (m)
m_freem(m);
return (error);
}
/*
* ESP output callback from the crypto driver.
*/
static int
esp_output_cb(struct cryptop *crp)
{
struct tdb_crypto *tc;
struct ipsecrequest *isr;
struct secasvar *sav;
struct mbuf *m;
int s, err, error;
tc = (struct tdb_crypto *) crp->crp_opaque;
IPSEC_ASSERT(tc != NULL, ("esp_output_cb: null opaque data area!"));
m = (struct mbuf *) crp->crp_buf;
s = splsoftnet();
isr = tc->tc_isr;
sav = KEY_ALLOCSA(&tc->tc_dst, tc->tc_proto, tc->tc_spi);
if (sav == NULL) {
espstat.esps_notdb++;
DPRINTF(("esp_output_cb: SA expired while in crypto "
"(SA %s/%08lx proto %u)\n", ipsec_address(&tc->tc_dst),
(u_long) ntohl(tc->tc_spi), tc->tc_proto));
error = ENOBUFS; /*XXX*/
goto bad;
}
IPSEC_ASSERT(isr->sav == sav,
("esp_output_cb: SA changed was %p now %p\n", isr->sav, sav));
/* Check for crypto errors. */
if (crp->crp_etype) {
/* Reset session ID. */
if (sav->tdb_cryptoid != 0)
sav->tdb_cryptoid = crp->crp_sid;
if (crp->crp_etype == EAGAIN) {
KEY_FREESAV(&sav);
splx(s);
return crypto_dispatch(crp);
}
espstat.esps_noxform++;
DPRINTF(("esp_output_cb: crypto error %d\n", crp->crp_etype));
error = crp->crp_etype;
goto bad;
}
/* Shouldn't happen... */
if (m == NULL) {
espstat.esps_crypto++;
DPRINTF(("esp_output_cb: bogus returned buffer from crypto\n"));
error = EINVAL;
goto bad;
}
espstat.esps_hist[sav->alg_enc]++;
if (sav->tdb_authalgxform != NULL)
ahstat.ahs_hist[sav->alg_auth]++;
/* Release crypto descriptors. */
free(tc, M_XDATA);
crypto_freereq(crp);
/* NB: m is reclaimed by ipsec_process_done. */
err = ipsec_process_done(m, isr);
KEY_FREESAV(&sav);
splx(s);
return err;
bad:
if (sav)
KEY_FREESAV(&sav);
splx(s);
if (m)
m_freem(m);
free(tc, M_XDATA);
crypto_freereq(crp);
return error;
}
static struct xformsw esp_xformsw = {
XF_ESP, XFT_CONF|XFT_AUTH, "IPsec ESP",
esp_init, esp_zeroize, esp_input,
esp_output
};
INITFN void
esp_attach(void)
{
#define MAXIV(xform) \
if (xform.blocksize > esp_max_ivlen) \
esp_max_ivlen = xform.blocksize \
esp_max_ivlen = 0;
MAXIV(enc_xform_des); /* SADB_EALG_DESCBC */
MAXIV(enc_xform_3des); /* SADB_EALG_3DESCBC */
MAXIV(enc_xform_rijndael128); /* SADB_X_EALG_AES */
MAXIV(enc_xform_blf); /* SADB_X_EALG_BLOWFISHCBC */
MAXIV(enc_xform_cast5); /* SADB_X_EALG_CAST128CBC */
MAXIV(enc_xform_skipjack); /* SADB_X_EALG_SKIPJACK */
MAXIV(enc_xform_null); /* SADB_EALG_NULL */
xform_register(&esp_xformsw);
#undef MAXIV
}
#ifdef __FreeBSD__
SYSINIT(esp_xform_init, SI_SUB_DRIVERS, SI_ORDER_FIRST, esp_attach, NULL)
#else
#endif

614
sys/netipsec/xform_ipcomp.c Normal file
View File

@ -0,0 +1,614 @@
/* $NetBSD: xform_ipcomp.c,v 1.1 2003/08/13 20:06:52 jonathan Exp $ */
/* $FreeBSD: src/sys/netipsec/xform_ipcomp.c,v 1.1.4.1 2003/01/24 05:11:36 sam Exp $ */
/* $OpenBSD: ip_ipcomp.c,v 1.1 2001/07/05 12:08:52 jjbg Exp $ */
/*
* Copyright (c) 2001 Jean-Jacques Bernard-Gundol (jj@wabbitt.org)
*
* 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. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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: xform_ipcomp.c,v 1.1 2003/08/13 20:06:52 jonathan Exp $");
/* IP payload compression protocol (IPComp), see RFC 2393 */
#include "opt_inet.h"
#include "opt_inet6.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/kernel.h>
#include <sys/protosw.h>
#include <sys/sysctl.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/ip_var.h>
#include <net/route.h>
#include <netipsec/ipsec.h>
#include <netipsec/xform.h>
#ifdef INET6
#include <netinet/ip6.h>
#include <netipsec/ipsec6.h>
#endif
#include <netipsec/ipcomp.h>
#include <netipsec/ipcomp_var.h>
#include <netipsec/key.h>
#include <netipsec/key_debug.h>
#include <netipsec/ipsec_osdep.h>
#include <opencrypto/cryptodev.h>
#include <opencrypto/deflate.h>
#include <opencrypto/xform.h>
int ipcomp_enable = 0;
struct ipcompstat ipcompstat;
#ifdef __freeBSD__
SYSCTL_DECL(_net_inet_ipcomp);
SYSCTL_INT(_net_inet_ipcomp, OID_AUTO,
ipcomp_enable, CTLFLAG_RW, &ipcomp_enable, 0, "");
SYSCTL_STRUCT(_net_inet_ipcomp, IPSECCTL_STATS,
stats, CTLFLAG_RD, &ipcompstat, ipcompstat, "");
#endif __FreeBSD__
static int ipcomp_input_cb(struct cryptop *crp);
static int ipcomp_output_cb(struct cryptop *crp);
struct comp_algo *
ipcomp_algorithm_lookup(int alg)
{
if (alg >= IPCOMP_ALG_MAX)
return NULL;
switch (alg) {
case SADB_X_CALG_DEFLATE:
return &comp_algo_deflate;
}
return NULL;
}
/*
* ipcomp_init() is called when an CPI is being set up.
*/
static int
ipcomp_init(struct secasvar *sav, struct xformsw *xsp)
{
struct comp_algo *tcomp;
struct cryptoini cric;
/* NB: algorithm really comes in alg_enc and not alg_comp! */
tcomp = ipcomp_algorithm_lookup(sav->alg_enc);
if (tcomp == NULL) {
DPRINTF(("ipcomp_init: unsupported compression algorithm %d\n",
sav->alg_comp));
return EINVAL;
}
sav->alg_comp = sav->alg_enc; /* set for doing histogram */
sav->tdb_xform = xsp;
sav->tdb_compalgxform = tcomp;
/* Initialize crypto session */
bzero(&cric, sizeof (cric));
cric.cri_alg = sav->tdb_compalgxform->type;
return crypto_newsession(&sav->tdb_cryptoid, &cric, crypto_support);
}
/*
* ipcomp_zeroize() used when IPCA is deleted
*/
static int
ipcomp_zeroize(struct secasvar *sav)
{
int err;
err = crypto_freesession(sav->tdb_cryptoid);
sav->tdb_cryptoid = 0;
return err;
}
/*
* ipcomp_input() gets called to uncompress an input packet
*/
static int
ipcomp_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
{
struct tdb_crypto *tc;
struct cryptodesc *crdc;
struct cryptop *crp;
int hlen = IPCOMP_HLENGTH;
IPSEC_SPLASSERT_SOFTNET("ipcomp_input");
/* Get crypto descriptors */
crp = crypto_getreq(1);
if (crp == NULL) {
m_freem(m);
DPRINTF(("ipcomp_input: no crypto descriptors\n"));
ipcompstat.ipcomps_crypto++;
return ENOBUFS;
}
/* Get IPsec-specific opaque pointer */
tc = (struct tdb_crypto *) malloc(sizeof (*tc), M_XDATA, M_NOWAIT|M_ZERO);
if (tc == NULL) {
m_freem(m);
crypto_freereq(crp);
DPRINTF(("ipcomp_input: cannot allocate tdb_crypto\n"));
ipcompstat.ipcomps_crypto++;
return ENOBUFS;
}
crdc = crp->crp_desc;
crdc->crd_skip = skip + hlen;
crdc->crd_len = m->m_pkthdr.len - (skip + hlen);
crdc->crd_inject = skip;
tc->tc_ptr = 0;
/* Decompression operation */
crdc->crd_alg = sav->tdb_compalgxform->type;
/* Crypto operation descriptor */
crp->crp_ilen = m->m_pkthdr.len - (skip + hlen);
crp->crp_flags = CRYPTO_F_IMBUF;
crp->crp_buf = (caddr_t) m;
crp->crp_callback = ipcomp_input_cb;
crp->crp_sid = sav->tdb_cryptoid;
crp->crp_opaque = (caddr_t) tc;
/* These are passed as-is to the callback */
tc->tc_spi = sav->spi;
tc->tc_dst = sav->sah->saidx.dst;
tc->tc_proto = sav->sah->saidx.proto;
tc->tc_protoff = protoff;
tc->tc_skip = skip;
return crypto_dispatch(crp);
}
#ifdef INET6
#define IPSEC_COMMON_INPUT_CB(m, sav, skip, protoff, mtag) do { \
if (saidx->dst.sa.sa_family == AF_INET6) { \
error = ipsec6_common_input_cb(m, sav, skip, protoff, mtag); \
} else { \
error = ipsec4_common_input_cb(m, sav, skip, protoff, mtag); \
} \
} while (0)
#else
#define IPSEC_COMMON_INPUT_CB(m, sav, skip, protoff, mtag) \
(error = ipsec4_common_input_cb(m, sav, skip, protoff, mtag))
#endif
/*
* IPComp input callback from the crypto driver.
*/
static int
ipcomp_input_cb(struct cryptop *crp)
{
struct cryptodesc *crd;
struct tdb_crypto *tc;
int skip, protoff;
struct mtag *mtag;
struct mbuf *m;
struct secasvar *sav;
struct secasindex *saidx;
int s, hlen = IPCOMP_HLENGTH, error, clen;
u_int8_t nproto;
caddr_t addr;
crd = crp->crp_desc;
tc = (struct tdb_crypto *) crp->crp_opaque;
IPSEC_ASSERT(tc != NULL, ("ipcomp_input_cb: null opaque crypto data area!"));
skip = tc->tc_skip;
protoff = tc->tc_protoff;
mtag = (struct mtag *) tc->tc_ptr;
m = (struct mbuf *) crp->crp_buf;
s = splsoftnet();
sav = KEY_ALLOCSA(&tc->tc_dst, tc->tc_proto, tc->tc_spi);
if (sav == NULL) {
ipcompstat.ipcomps_notdb++;
DPRINTF(("ipcomp_input_cb: SA expired while in crypto\n"));
error = ENOBUFS; /*XXX*/
goto bad;
}
saidx = &sav->sah->saidx;
IPSEC_ASSERT(saidx->dst.sa.sa_family == AF_INET ||
saidx->dst.sa.sa_family == AF_INET6,
("ah_input_cb: unexpected protocol family %u",
saidx->dst.sa.sa_family));
/* Check for crypto errors */
if (crp->crp_etype) {
/* Reset the session ID */
if (sav->tdb_cryptoid != 0)
sav->tdb_cryptoid = crp->crp_sid;
if (crp->crp_etype == EAGAIN) {
KEY_FREESAV(&sav);
splx(s);
return crypto_dispatch(crp);
}
ipcompstat.ipcomps_noxform++;
DPRINTF(("ipcomp_input_cb: crypto error %d\n", crp->crp_etype));
error = crp->crp_etype;
goto bad;
}
/* Shouldn't happen... */
if (m == NULL) {
ipcompstat.ipcomps_crypto++;
DPRINTF(("ipcomp_input_cb: null mbuf returned from crypto\n"));
error = EINVAL;
goto bad;
}
ipcompstat.ipcomps_hist[sav->alg_comp]++;
clen = crp->crp_olen; /* Length of data after processing */
/* Release the crypto descriptors */
free(tc, M_XDATA), tc = NULL;
crypto_freereq(crp), crp = NULL;
/* In case it's not done already, adjust the size of the mbuf chain */
m->m_pkthdr.len = clen + hlen + skip;
if (m->m_len < skip + hlen && (m = m_pullup(m, skip + hlen)) == 0) {
ipcompstat.ipcomps_hdrops++; /*XXX*/
DPRINTF(("ipcomp_input_cb: m_pullup failed\n"));
error = EINVAL; /*XXX*/
goto bad;
}
/* Keep the next protocol field */
addr = (caddr_t) mtod(m, struct ip *) + skip;
nproto = ((struct ipcomp *) addr)->comp_nxt;
/* Remove the IPCOMP header */
error = m_striphdr(m, skip, hlen);
if (error) {
ipcompstat.ipcomps_hdrops++;
DPRINTF(("ipcomp_input_cb: bad mbuf chain, IPCA %s/%08lx\n",
ipsec_address(&sav->sah->saidx.dst),
(u_long) ntohl(sav->spi)));
goto bad;
}
/* Restore the Next Protocol field */
m_copyback(m, protoff, sizeof (u_int8_t), (u_int8_t *) &nproto);
IPSEC_COMMON_INPUT_CB(m, sav, skip, protoff, NULL);
KEY_FREESAV(&sav);
splx(s);
return error;
bad:
if (sav)
KEY_FREESAV(&sav);
splx(s);
if (m)
m_freem(m);
if (tc != NULL)
free(tc, M_XDATA);
if (crp)
crypto_freereq(crp);
return error;
}
/*
* IPComp output routine, called by ipsec[46]_process_packet()
*/
static int
ipcomp_output(
struct mbuf *m,
struct ipsecrequest *isr,
struct mbuf **mp,
int skip,
int protoff
)
{
struct secasvar *sav;
struct comp_algo *ipcompx;
int error, ralen, hlen, maxpacketsize, roff;
u_int8_t prot;
struct cryptodesc *crdc;
struct cryptop *crp;
struct tdb_crypto *tc;
struct mbuf *mo;
struct ipcomp *ipcomp;
IPSEC_SPLASSERT_SOFTNET("ipcomp_output");
sav = isr->sav;
IPSEC_ASSERT(sav != NULL, ("ipcomp_output: null SA"));
ipcompx = sav->tdb_compalgxform;
IPSEC_ASSERT(ipcompx != NULL, ("ipcomp_output: null compression xform"));
ralen = m->m_pkthdr.len - skip; /* Raw payload length before comp. */
hlen = IPCOMP_HLENGTH;
ipcompstat.ipcomps_output++;
/* Check for maximum packet size violations. */
switch (sav->sah->saidx.dst.sa.sa_family) {
#ifdef INET
case AF_INET:
maxpacketsize = IP_MAXPACKET;
break;
#endif /* INET */
#ifdef INET6
case AF_INET6:
maxpacketsize = IPV6_MAXPACKET;
break;
#endif /* INET6 */
default:
ipcompstat.ipcomps_nopf++;
DPRINTF(("ipcomp_output: unknown/unsupported protocol family %d"
", IPCA %s/%08lx\n",
sav->sah->saidx.dst.sa.sa_family,
ipsec_address(&sav->sah->saidx.dst),
(u_long) ntohl(sav->spi)));
error = EPFNOSUPPORT;
goto bad;
}
if (skip + hlen + ralen > maxpacketsize) {
ipcompstat.ipcomps_toobig++;
DPRINTF(("ipcomp_output: packet in IPCA %s/%08lx got too big "
"(len %u, max len %u)\n",
ipsec_address(&sav->sah->saidx.dst),
(u_long) ntohl(sav->spi),
skip + hlen + ralen, maxpacketsize));
error = EMSGSIZE;
goto bad;
}
/* Update the counters */
ipcompstat.ipcomps_obytes += m->m_pkthdr.len - skip;
m = m_clone(m);
if (m == NULL) {
ipcompstat.ipcomps_hdrops++;
DPRINTF(("ipcomp_output: cannot clone mbuf chain, IPCA %s/%08lx\n",
ipsec_address(&sav->sah->saidx.dst),
(u_long) ntohl(sav->spi)));
error = ENOBUFS;
goto bad;
}
/* Inject IPCOMP header */
mo = m_makespace(m, skip, hlen, &roff);
if (mo == NULL) {
ipcompstat.ipcomps_wrap++;
DPRINTF(("ipcomp_output: failed to inject IPCOMP header for "
"IPCA %s/%08lx\n",
ipsec_address(&sav->sah->saidx.dst),
(u_long) ntohl(sav->spi)));
error = ENOBUFS;
goto bad;
}
ipcomp = (struct ipcomp *)(mtod(mo, caddr_t) + roff);
/* Initialize the IPCOMP header */
/* XXX alignment always correct? */
switch (sav->sah->saidx.dst.sa.sa_family) {
#ifdef INET
case AF_INET:
ipcomp->comp_nxt = mtod(m, struct ip *)->ip_p;
break;
#endif /* INET */
#ifdef INET6
case AF_INET6:
ipcomp->comp_nxt = mtod(m, struct ip6_hdr *)->ip6_nxt;
break;
#endif
}
ipcomp->comp_flags = 0;
ipcomp->comp_cpi = htons((u_int16_t) ntohl(sav->spi));
/* Fix Next Protocol in IPv4/IPv6 header */
prot = IPPROTO_IPCOMP;
m_copyback(m, protoff, sizeof(u_int8_t), (u_char *) &prot);
/* Ok now, we can pass to the crypto processing */
/* Get crypto descriptors */
crp = crypto_getreq(1);
if (crp == NULL) {
ipcompstat.ipcomps_crypto++;
DPRINTF(("ipcomp_output: failed to acquire crypto descriptor\n"));
error = ENOBUFS;
goto bad;
}
crdc = crp->crp_desc;
/* Compression descriptor */
crdc->crd_skip = skip + hlen;
crdc->crd_len = m->m_pkthdr.len - (skip + hlen);
crdc->crd_flags = CRD_F_COMP;
crdc->crd_inject = skip + hlen;
/* Compression operation */
crdc->crd_alg = ipcompx->type;
/* IPsec-specific opaque crypto info */
tc = (struct tdb_crypto *) malloc(sizeof(struct tdb_crypto),
M_XDATA, M_NOWAIT|M_ZERO);
if (tc == NULL) {
ipcompstat.ipcomps_crypto++;
DPRINTF(("ipcomp_output: failed to allocate tdb_crypto\n"));
crypto_freereq(crp);
error = ENOBUFS;
goto bad;
}
tc->tc_isr = isr;
tc->tc_spi = sav->spi;
tc->tc_dst = sav->sah->saidx.dst;
tc->tc_proto = sav->sah->saidx.proto;
tc->tc_skip = skip + hlen;
/* Crypto operation descriptor */
crp->crp_ilen = m->m_pkthdr.len; /* Total input length */
crp->crp_flags = CRYPTO_F_IMBUF;
crp->crp_buf = (caddr_t) m;
crp->crp_callback = ipcomp_output_cb;
crp->crp_opaque = (caddr_t) tc;
crp->crp_sid = sav->tdb_cryptoid;
return crypto_dispatch(crp);
bad:
if (m)
m_freem(m);
return (error);
}
/*
* IPComp output callback from the crypto driver.
*/
static int
ipcomp_output_cb(struct cryptop *crp)
{
struct tdb_crypto *tc;
struct ipsecrequest *isr;
struct secasvar *sav;
struct mbuf *m;
int s, error, skip, rlen;
tc = (struct tdb_crypto *) crp->crp_opaque;
IPSEC_ASSERT(tc != NULL, ("ipcomp_output_cb: null opaque data area!"));
m = (struct mbuf *) crp->crp_buf;
skip = tc->tc_skip;
rlen = crp->crp_ilen - skip;
s = splsoftnet();
isr = tc->tc_isr;
sav = KEY_ALLOCSA(&tc->tc_dst, tc->tc_proto, tc->tc_spi);
if (sav == NULL) {
ipcompstat.ipcomps_notdb++;
DPRINTF(("ipcomp_output_cb: SA expired while in crypto\n"));
error = ENOBUFS; /*XXX*/
goto bad;
}
IPSEC_ASSERT(isr->sav == sav, ("ipcomp_output_cb: SA changed\n"));
/* Check for crypto errors */
if (crp->crp_etype) {
/* Reset session ID */
if (sav->tdb_cryptoid != 0)
sav->tdb_cryptoid = crp->crp_sid;
if (crp->crp_etype == EAGAIN) {
KEY_FREESAV(&sav);
splx(s);
return crypto_dispatch(crp);
}
ipcompstat.ipcomps_noxform++;
DPRINTF(("ipcomp_output_cb: crypto error %d\n", crp->crp_etype));
error = crp->crp_etype;
goto bad;
}
/* Shouldn't happen... */
if (m == NULL) {
ipcompstat.ipcomps_crypto++;
DPRINTF(("ipcomp_output_cb: bogus return buffer from crypto\n"));
error = EINVAL;
goto bad;
}
ipcompstat.ipcomps_hist[sav->alg_comp]++;
if (rlen > crp->crp_olen) {
/* Adjust the length in the IP header */
switch (sav->sah->saidx.dst.sa.sa_family) {
#ifdef INET
case AF_INET:
mtod(m, struct ip *)->ip_len = htons(m->m_pkthdr.len);
break;
#endif /* INET */
#ifdef INET6
case AF_INET6:
mtod(m, struct ip6_hdr *)->ip6_plen =
htons(m->m_pkthdr.len) - sizeof(struct ip6_hdr);
break;
#endif /* INET6 */
default:
ipcompstat.ipcomps_nopf++;
DPRINTF(("ipcomp_output: unknown/unsupported protocol "
"family %d, IPCA %s/%08lx\n",
sav->sah->saidx.dst.sa.sa_family,
ipsec_address(&sav->sah->saidx.dst),
(u_long) ntohl(sav->spi)));
error = EPFNOSUPPORT;
goto bad;
}
} else {
/* compression was useless, we have lost time */
/* XXX add statistic */
}
/* Release the crypto descriptor */
free(tc, M_XDATA);
crypto_freereq(crp);
/* NB: m is reclaimed by ipsec_process_done. */
error = ipsec_process_done(m, isr);
KEY_FREESAV(&sav);
splx(s);
return error;
bad:
if (sav)
KEY_FREESAV(&sav);
splx(s);
if (m)
m_freem(m);
free(tc, M_XDATA);
crypto_freereq(crp);
return error;
}
static struct xformsw ipcomp_xformsw = {
XF_IPCOMP, XFT_COMP, "IPcomp",
ipcomp_init, ipcomp_zeroize, ipcomp_input,
ipcomp_output
};
INITFN void
ipcomp_attach(void)
{
xform_register(&ipcomp_xformsw);
}
#ifdef __FreeBSD__
SYSINIT(ipcomp_xform_init, SI_SUB_DRIVERS, SI_ORDER_FIRST, ipcomp_attach, NULL)
#endif __FreeBSD__

728
sys/netipsec/xform_ipip.c Normal file
View File

@ -0,0 +1,728 @@
/* $NetBSD: xform_ipip.c,v 1.1 2003/08/13 20:06:52 jonathan Exp $ */
/* $FreeBSD: src/sys/netipsec/xform_ipip.c,v 1.3.2.1 2003/01/24 05:11:36 sam Exp $ */
/* $OpenBSD: ip_ipip.c,v 1.25 2002/06/10 18:04:55 itojun Exp $ */
/*
* The authors of this code are John Ioannidis (ji@tla.org),
* Angelos D. Keromytis (kermit@csd.uch.gr) and
* Niels Provos (provos@physnet.uni-hamburg.de).
*
* The original version of this code was written by John Ioannidis
* for BSD/OS in Athens, Greece, in November 1995.
*
* Ported to OpenBSD and NetBSD, with additional transforms, in December 1996,
* by Angelos D. Keromytis.
*
* Additional transforms and features in 1997 and 1998 by Angelos D. Keromytis
* and Niels Provos.
*
* Additional features in 1999 by Angelos D. Keromytis.
*
* Copyright (C) 1995, 1996, 1997, 1998, 1999 by John Ioannidis,
* Angelos D. Keromytis and Niels Provos.
* Copyright (c) 2001, Angelos D. Keromytis.
*
* Permission to use, copy, and modify this software with or without fee
* is hereby granted, provided that this entire notice is included in
* all copies of any software which is or includes a copy or
* modification of this software.
* You may use this code under the GNU public license if you so wish. Please
* contribute changes back to the authors under this freer than GPL license
* so that we may further the use of strong encryption without limitations to
* all.
*
* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY
* REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE
* MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR
* PURPOSE.
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: xform_ipip.c,v 1.1 2003/08/13 20:06:52 jonathan Exp $");
/*
* IP-inside-IP processing
*/
#include "opt_inet.h"
#include "opt_inet6.h"
#ifdef __FreeBSD__
#include "opt_random_ip_id.h"
#endif /* __FreeBSD__ */
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/kernel.h>
#include <sys/protosw.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <net/route.h>
#include <net/netisr.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/in_var.h>
#include <netinet/ip.h>
#include <netinet/ip_ecn.h>
#include <netinet/ip_var.h>
#include <netinet/ip_encap.h>
#ifdef __FreeBSD__
#include <netinet/ipprotosw.h>
#endif
#include <netipsec/ipsec.h>
#include <netipsec/xform.h>
#include <netipsec/ipip_var.h>
#ifdef MROUTING
#include <netinet/ip_mroute.h>
#endif
#ifdef INET6
#include <netinet/ip6.h>
#include <netipsec/ipsec6.h>
#include <netinet6/ip6_ecn.h>
#include <netinet6/in6_var.h>
#include <netinet6/ip6protosw.h>
#endif
#include <netipsec/key.h>
#include <netipsec/key_debug.h>
#include <netipsec/ipsec_osdep.h>
#include <machine/stdarg.h>
#ifdef __FreeBSD__
typedef void pr_in_input_t (struct mbuf *, int, int); /* XXX FIX THIS */
#else
typedef void pr_in_input_t (struct mbuf *m, ...);
#endif
/*
* We can control the acceptance of IP4 packets by altering the sysctl
* net.inet.ipip.allow value. Zero means drop them, all else is acceptance.
*/
int ipip_allow = 0;
struct ipipstat ipipstat;
#ifdef SYSCTL_DECL
SYSCTL_DECL(_net_inet_ipip);
SYSCTL_INT(_net_inet_ipip, OID_AUTO,
ipip_allow, CTLFLAG_RW, &ipip_allow, 0, "");
SYSCTL_STRUCT(_net_inet_ipip, IPSECCTL_STATS,
stats, CTLFLAG_RD, &ipipstat, ipipstat, "");
#endif
#ifdef __FreeBSD__
static
#endif
void ipe4_attach(void);
/* XXX IPCOMP */
#define M_IPSEC (M_AUTHIPHDR|M_AUTHIPDGM|M_DECRYPTED)
static void _ipip_input(struct mbuf *m, int iphlen, struct ifnet *gifp);
#ifdef INET6
/*
* Really only a wrapper for ipip_input(), for use with IPv6.
*/
int
ip4_input6(struct mbuf **m, int *offp, int proto)
{
#if 0
/* If we do not accept IP-in-IP explicitly, drop. */
if (!ipip_allow && ((*m)->m_flags & M_IPSEC) == 0) {
DPRINTF(("ip4_input6: dropped due to policy\n"));
ipipstat.ipips_pdrops++;
m_freem(*m);
return IPPROTO_DONE;
}
#endif
_ipip_input(*m, *offp, NULL);
return IPPROTO_DONE;
}
#endif /* INET6 */
#ifdef INET
/*
* Really only a wrapper for ipip_input(), for use with IPv4.
*/
void
ip4_input(struct mbuf *m, ...)
{
va_list ap;
int iphlen;
#if 0
/* If we do not accept IP-in-IP explicitly, drop. */
if (!ipip_allow && (m->m_flags & M_IPSEC) == 0) {
DPRINTF(("ip4_input: dropped due to policy\n"));
ipipstat.ipips_pdrops++;
m_freem(m);
return;
}
#endif
va_start(ap, m);
iphlen = va_arg(ap, int);
va_end(ap);
_ipip_input(m, iphlen, NULL);
}
#endif /* INET */
/*
* ipip_input gets called when we receive an IP{46} encapsulated packet,
* either because we got it at a real interface, or because AH or ESP
* were being used in tunnel mode (in which case the rcvif element will
* contain the address of the encX interface associated with the tunnel.
*/
static void
_ipip_input(struct mbuf *m, int iphlen, struct ifnet *gifp)
{
register struct sockaddr_in *sin;
register struct ifnet *ifp;
register struct ifaddr *ifa;
struct ifqueue *ifq = NULL;
struct ip *ipo;
#ifdef INET6
register struct sockaddr_in6 *sin6;
struct ip6_hdr *ip6 = NULL;
u_int8_t itos;
#endif
u_int8_t nxt;
int isr;
u_int8_t otos;
u_int8_t v;
int hlen;
ipipstat.ipips_ipackets++;
m_copydata(m, 0, 1, &v);
switch (v >> 4) {
#ifdef INET
case 4:
hlen = sizeof(struct ip);
break;
#endif /* INET */
#ifdef INET6
case 6:
hlen = sizeof(struct ip6_hdr);
break;
#endif
default:
DPRINTF(("_ipip_input: bad protocol version 0x%x (%u) "
"for outer header\n", v, v>>4));
ipipstat.ipips_family++;
m_freem(m);
return /* EAFNOSUPPORT */;
}
/* Bring the IP header in the first mbuf, if not there already */
if (m->m_len < hlen) {
if ((m = m_pullup(m, hlen)) == NULL) {
DPRINTF(("ipip_input: m_pullup (1) failed\n"));
ipipstat.ipips_hdrops++;
return;
}
}
ipo = mtod(m, struct ip *);
#ifdef MROUTING
if (ipo->ip_v == IPVERSION && ipo->ip_p == IPPROTO_IPV4) {
if (IN_MULTICAST(((struct ip *)((char *) ipo + iphlen))->ip_dst.s_addr)) {
ipip_mroute_input (m, iphlen);
return;
}
}
#endif /* MROUTING */
/* Keep outer ecn field. */
switch (v >> 4) {
#ifdef INET
case 4:
otos = ipo->ip_tos;
break;
#endif /* INET */
#ifdef INET6
case 6:
otos = (ntohl(mtod(m, struct ip6_hdr *)->ip6_flow) >> 20) & 0xff;
break;
#endif
default:
panic("ipip_input: unknown ip version %u (outer)", v>>4);
}
/* Remove outer IP header */
m_adj(m, iphlen);
/* Sanity check */
if (m->m_pkthdr.len < sizeof(struct ip)) {
ipipstat.ipips_hdrops++;
m_freem(m);
return;
}
m_copydata(m, 0, 1, &v);
switch (v >> 4) {
#ifdef INET
case 4:
hlen = sizeof(struct ip);
break;
#endif /* INET */
#ifdef INET6
case 6:
hlen = sizeof(struct ip6_hdr);
break;
#endif
default:
DPRINTF(("_ipip_input: bad protocol version 0x%x (%u) "
"for inner header\n", v, v>>4));
ipipstat.ipips_family++;
m_freem(m);
return; /* EAFNOSUPPORT */
}
/*
* Bring the inner IP header in the first mbuf, if not there already.
*/
if (m->m_len < hlen) {
if ((m = m_pullup(m, hlen)) == NULL) {
DPRINTF(("ipip_input: m_pullup (2) failed\n"));
ipipstat.ipips_hdrops++;
return;
}
}
/*
* RFC 1853 specifies that the inner TTL should not be touched on
* decapsulation. There's no reason this comment should be here, but
* this is as good as any a position.
*/
/* Some sanity checks in the inner IP header */
switch (v >> 4) {
#ifdef INET
case 4:
ipo = mtod(m, struct ip *);
nxt = ipo->ip_p;
ip_ecn_egress(ip4_ipsec_ecn, &otos, &ipo->ip_tos);
break;
#endif /* INET */
#ifdef INET6
case 6:
ip6 = (struct ip6_hdr *) ipo;
nxt = ip6->ip6_nxt;
itos = (ntohl(ip6->ip6_flow) >> 20) & 0xff;
ip_ecn_egress(ip6_ipsec_ecn, &otos, &itos);
ip6->ip6_flow &= ~htonl(0xff << 20);
ip6->ip6_flow |= htonl((u_int32_t) itos << 20);
break;
#endif
default:
panic("ipip_input: unknown ip version %u (inner)", v>>4);
}
/* Check for local address spoofing. */
if ((m->m_pkthdr.rcvif == NULL ||
!(m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK)) &&
ipip_allow != 2) {
for (ifp = ifnet.tqh_first; ifp != 0;
ifp = ifp->if_list.tqe_next) {
for (ifa = ifp->if_addrlist.tqh_first; ifa != 0;
ifa = ifa->ifa_list.tqe_next) {
#ifdef INET
if (ipo) {
if (ifa->ifa_addr->sa_family !=
AF_INET)
continue;
sin = (struct sockaddr_in *) ifa->ifa_addr;
if (sin->sin_addr.s_addr ==
ipo->ip_src.s_addr) {
ipipstat.ipips_spoof++;
m_freem(m);
return;
}
}
#endif /* INET */
#ifdef INET6
if (ip6) {
if (ifa->ifa_addr->sa_family !=
AF_INET6)
continue;
sin6 = (struct sockaddr_in6 *) ifa->ifa_addr;
if (IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr, &ip6->ip6_src)) {
ipipstat.ipips_spoof++;
m_freem(m);
return;
}
}
#endif /* INET6 */
}
}
}
/* Statistics */
ipipstat.ipips_ibytes += m->m_pkthdr.len - iphlen;
/*
* Interface pointer stays the same; if no IPsec processing has
* been done (or will be done), this will point to a normal
* interface. Otherwise, it'll point to an enc interface, which
* will allow a packet filter to distinguish between secure and
* untrusted packets.
*/
switch (v >> 4) {
#ifdef INET
case 4:
ifq = &ipintrq;
isr = NETISR_IP;
break;
#endif
#ifdef INET6
case 6:
ifq = &ip6intrq;
isr = NETISR_IPV6;
break;
#endif
default:
panic("ipip_input: should never reach here");
}
if (!IF_HANDOFF(ifq, m, NULL)) {
ipipstat.ipips_qfull++;
DPRINTF(("ipip_input: packet dropped because of full queue\n"));
} else {
schednetisr(isr);
}
}
int
ipip_output(
struct mbuf *m,
struct ipsecrequest *isr,
struct mbuf **mp,
int skip,
int protoff
)
{
struct secasvar *sav;
u_int8_t tp, otos;
struct secasindex *saidx;
int error;
#ifdef INET
u_int8_t itos;
struct ip *ipo;
#endif /* INET */
#ifdef INET6
struct ip6_hdr *ip6, *ip6o;
#endif /* INET6 */
IPSEC_SPLASSERT_SOFTNET("ipip_output");
sav = isr->sav;
IPSEC_ASSERT(sav != NULL, ("ipip_output: null SA"));
IPSEC_ASSERT(sav->sah != NULL, ("ipip_output: null SAH"));
/* XXX Deal with empty TDB source/destination addresses. */
m_copydata(m, 0, 1, &tp);
tp = (tp >> 4) & 0xff; /* Get the IP version number. */
saidx = &sav->sah->saidx;
switch (saidx->dst.sa.sa_family) {
#ifdef INET
case AF_INET:
if (saidx->src.sa.sa_family != AF_INET ||
saidx->src.sin.sin_addr.s_addr == INADDR_ANY ||
saidx->dst.sin.sin_addr.s_addr == INADDR_ANY) {
DPRINTF(("ipip_output: unspecified tunnel endpoint "
"address in SA %s/%08lx\n",
ipsec_address(&saidx->dst),
(u_long) ntohl(sav->spi)));
ipipstat.ipips_unspec++;
error = EINVAL;
goto bad;
}
M_PREPEND(m, sizeof(struct ip), M_DONTWAIT);
if (m == 0) {
DPRINTF(("ipip_output: M_PREPEND failed\n"));
ipipstat.ipips_hdrops++;
error = ENOBUFS;
goto bad;
}
ipo = mtod(m, struct ip *);
ipo->ip_v = IPVERSION;
ipo->ip_hl = 5;
ipo->ip_len = htons(m->m_pkthdr.len);
ipo->ip_ttl = ip_defttl;
ipo->ip_sum = 0;
ipo->ip_src = saidx->src.sin.sin_addr;
ipo->ip_dst = saidx->dst.sin.sin_addr;
#ifdef RANDOM_IP_ID
ipo->ip_id = ip_randomid();
#else
ipo->ip_id = htons(ip_id++);
#endif
/* If the inner protocol is IP... */
if (tp == IPVERSION) {
/* Save ECN notification */
m_copydata(m, sizeof(struct ip) +
offsetof(struct ip, ip_tos),
sizeof(u_int8_t), (caddr_t) &itos);
ipo->ip_p = IPPROTO_IPIP;
/*
* We should be keeping tunnel soft-state and
* send back ICMPs if needed.
*/
m_copydata(m, sizeof(struct ip) +
offsetof(struct ip, ip_off),
sizeof(u_int16_t), (caddr_t) &ipo->ip_off);
ipo->ip_off = ntohs(ipo->ip_off);
ipo->ip_off &= ~(IP_DF | IP_MF | IP_OFFMASK);
ipo->ip_off = htons(ipo->ip_off);
}
#ifdef INET6
else if (tp == (IPV6_VERSION >> 4)) {
u_int32_t itos32;
/* Save ECN notification. */
m_copydata(m, sizeof(struct ip) +
offsetof(struct ip6_hdr, ip6_flow),
sizeof(u_int32_t), (caddr_t) &itos32);
itos = ntohl(itos32) >> 20;
ipo->ip_p = IPPROTO_IPV6;
ipo->ip_off = 0;
}
#endif /* INET6 */
else {
goto nofamily;
}
otos = 0;
ip_ecn_ingress(ECN_ALLOWED, &otos, &itos);
ipo->ip_tos = otos;
break;
#endif /* INET */
#ifdef INET6
case AF_INET6:
if (IN6_IS_ADDR_UNSPECIFIED(&saidx->dst.sin6.sin6_addr) ||
saidx->src.sa.sa_family != AF_INET6 ||
IN6_IS_ADDR_UNSPECIFIED(&saidx->src.sin6.sin6_addr)) {
DPRINTF(("ipip_output: unspecified tunnel endpoint "
"address in SA %s/%08lx\n",
ipsec_address(&saidx->dst),
(u_long) ntohl(sav->spi)));
ipipstat.ipips_unspec++;
error = ENOBUFS;
goto bad;
}
/* scoped address handling */
ip6 = mtod(m, struct ip6_hdr *);
if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src))
ip6->ip6_src.s6_addr16[1] = 0;
if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst))
ip6->ip6_dst.s6_addr16[1] = 0;
M_PREPEND(m, sizeof(struct ip6_hdr), M_DONTWAIT);
if (m == 0) {
DPRINTF(("ipip_output: M_PREPEND failed\n"));
ipipstat.ipips_hdrops++;
*mp = NULL;
error = ENOBUFS;
goto bad;
}
/* Initialize IPv6 header */
ip6o = mtod(m, struct ip6_hdr *);
ip6o->ip6_flow = 0;
ip6o->ip6_vfc &= ~IPV6_VERSION_MASK;
ip6o->ip6_vfc |= IPV6_VERSION;
ip6o->ip6_plen = htons(m->m_pkthdr.len);
ip6o->ip6_hlim = ip_defttl;
ip6o->ip6_dst = saidx->dst.sin6.sin6_addr;
ip6o->ip6_src = saidx->src.sin6.sin6_addr;
#ifdef INET
if (tp == IPVERSION) {
/* Save ECN notification */
m_copydata(m, sizeof(struct ip6_hdr) +
offsetof(struct ip, ip_tos), sizeof(u_int8_t),
(caddr_t) &itos);
/* This is really IPVERSION. */
ip6o->ip6_nxt = IPPROTO_IPIP;
} else
#endif /* INET */
if (tp == (IPV6_VERSION >> 4)) {
u_int32_t itos32;
/* Save ECN notification. */
m_copydata(m, sizeof(struct ip6_hdr) +
offsetof(struct ip6_hdr, ip6_flow),
sizeof(u_int32_t), (caddr_t) &itos32);
itos = ntohl(itos32) >> 20;
ip6o->ip6_nxt = IPPROTO_IPV6;
} else {
goto nofamily;
}
otos = 0;
ip_ecn_ingress(ECN_ALLOWED, &otos, &itos);
ip6o->ip6_flow |= htonl((u_int32_t) otos << 20);
break;
#endif /* INET6 */
default:
nofamily:
DPRINTF(("ipip_output: unsupported protocol family %u\n",
saidx->dst.sa.sa_family));
ipipstat.ipips_family++;
error = EAFNOSUPPORT; /* XXX diffs from openbsd */
goto bad;
}
ipipstat.ipips_opackets++;
*mp = m;
#ifdef INET
if (saidx->dst.sa.sa_family == AF_INET) {
#if 0
if (sav->tdb_xform->xf_type == XF_IP4)
tdb->tdb_cur_bytes +=
m->m_pkthdr.len - sizeof(struct ip);
#endif
ipipstat.ipips_obytes += m->m_pkthdr.len - sizeof(struct ip);
}
#endif /* INET */
#ifdef INET6
if (saidx->dst.sa.sa_family == AF_INET6) {
#if 0
if (sav->tdb_xform->xf_type == XF_IP4)
tdb->tdb_cur_bytes +=
m->m_pkthdr.len - sizeof(struct ip6_hdr);
#endif
ipipstat.ipips_obytes +=
m->m_pkthdr.len - sizeof(struct ip6_hdr);
}
#endif /* INET6 */
return 0;
bad:
if (m)
m_freem(m), *mp = NULL;
return (error);
}
#ifdef FAST_IPSEC
static int
ipe4_init(struct secasvar *sav, struct xformsw *xsp)
{
sav->tdb_xform = xsp;
return 0;
}
static int
ipe4_zeroize(struct secasvar *sav)
{
sav->tdb_xform = NULL;
return 0;
}
static int
ipe4_input(struct mbuf *m, struct secasvar *sav, int skip, int protoff)
{
/* This is a rather serious mistake, so no conditional printing. */
printf("ipe4_input: should never be called\n");
if (m)
m_freem(m);
return EOPNOTSUPP;
}
static struct xformsw ipe4_xformsw = {
XF_IP4, 0, "IPv4 Simple Encapsulation",
ipe4_init, ipe4_zeroize, ipe4_input, ipip_output,
};
extern struct domain inetdomain;
static struct ipprotosw ipe4_protosw[] = {
{ SOCK_RAW, &inetdomain, IPPROTO_IPV4, PR_ATOMIC|PR_ADDR|PR_LASTHDR,
ip4_input, 0, 0, rip_ctloutput,
rip_usrreq,
0, 0, 0, 0,
},
#ifdef INET6
{ SOCK_RAW, &inetdomain, IPPROTO_IPV6, PR_ATOMIC|PR_ADDR|PR_LASTHDR,
ip4_input, 0, 0, rip_ctloutput,
rip_usrreq,
0, 0, 0, 0,
},
#endif
};
/*
* Check the encapsulated packet to see if we want it
*/
static int
ipe4_encapcheck(const struct mbuf *m, int off, int proto, void *arg)
{
/*
* Only take packets coming from IPSEC tunnels; the rest
* must be handled by the gif tunnel code. Note that we
* also return a minimum priority when we want the packet
* so any explicit gif tunnels take precedence.
*/
return ((m->m_flags & M_IPSEC) != 0 ? 1 : 0);
}
INITFN void
ipe4_attach(void)
{
xform_register(&ipe4_xformsw);
/* attach to encapsulation framework */
/* XXX save return cookie for detach on module remove */
(void) encap_attach_func(AF_INET, -1,
ipe4_encapcheck, (struct protosw*) &ipe4_protosw[0], NULL);
#ifdef INET6
(void) encap_attach_func(AF_INET6, -1,
ipe4_encapcheck, (struct protosw*) &ipe4_protosw[1], NULL);
#endif
}
#ifdef SYSINIT
SYSINIT(ipe4_xform_init, SI_SUB_PROTO_DOMAIN, SI_ORDER_MIDDLE, ipe4_attach, NULL);
#endif
#endif /* FAST_IPSEC */