"Fast IPsec": this is an experimental IPsec implementation that is derived

from the KAME IPsec implementation, but with heavy borrowing and influence
of openbsd.  A key feature of this implementation is that it uses the kernel
crypto framework to do all crypto work so when h/w crypto support is present
IPsec operation is automatically accelerated.  Otherwise the protocol
implementations are rather differet while the SADB and policy management
code is very similar to KAME (for the moment).

Note that this implementation is enabled with a FAST_IPSEC option.  With this
you get all protocols; i.e. there is no FAST_IPSEC_ESP option.

FAST_IPSEC and IPSEC are mutually exclusive; you cannot build both into a
single system.

This software is well tested with IPv4 but should be considered very
experimental (i.e. do not deploy in production environments).  This software
does NOT currently support IPv6.  In fact do not configure FAST_IPSEC and
INET6 in the same system.

Obtained from:	KAME + openbsd
Supported by:	Vernier Networks
This commit is contained in:
Sam Leffler 2002-10-16 02:10:08 +00:00
parent 0aedf5d7d0
commit 88768458d2
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=105197
26 changed files with 17530 additions and 0 deletions

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

@ -0,0 +1,56 @@
/* $FreeBSD$ */
/* $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_*/

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

@ -0,0 +1,78 @@
/* $FreeBSD$ */
/* $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_*/

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

@ -0,0 +1,69 @@
/* $FreeBSD$ */
/* $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_*/

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

@ -0,0 +1,78 @@
/* $FreeBSD$ */
/* $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_*/

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

@ -0,0 +1,55 @@
/* $FreeBSD$ */
/* $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_*/

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

@ -0,0 +1,67 @@
/* $FreeBSD$ */
/* $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_*/

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

@ -0,0 +1,65 @@
/* $FreeBSD$ */
/* $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_ */

1941
sys/netipsec/ipsec.c Normal file

File diff suppressed because it is too large Load Diff

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

@ -0,0 +1,389 @@
/* $FreeBSD$ */
/* $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;
};
/*
* 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 *);
struct inpcb;
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 *));
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 *));
extern size_t ipsec_hdrsiz_tcp __P((struct tcpcb *));
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 *));
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);
#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 */
#endif /* _NETIPSEC_IPSEC_H_ */

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

@ -0,0 +1,89 @@
/* $FreeBSD$ */
/* $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_*/

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

@ -0,0 +1,728 @@
/* $FreeBSD$ */
/* $KAME: ipsec.c,v 1.103 2001/05/24 07:14:18 sakane 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 <machine/in_cksum.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);
KASSERT(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 = splnet();
/* 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;
#if 0
SPLASSERT(net, "ipsec4_common_input_cb");
#endif
KASSERT(m != NULL, ("ipsec4_common_input_cb: null mbuf"));
KASSERT(sav != NULL, ("ipsec4_common_input_cb: null SA"));
KASSERT(sav->sah != NULL, ("ipsec4_common_input_cb: null SAH"));
saidx = &sav->sah->saidx;
af = saidx->dst.sa.sa_family;
KASSERT(af == AF_INET, ("ipsec4_common_input_cb: unexpected af %u",af));
sproto = saidx->proto;
KASSERT(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;
KASSERT(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 */
}
}
/*
* 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;
KASSERT(m != NULL, ("ipsec6_common_input_cb: null mbuf"));
KASSERT(sav != NULL, ("ipsec6_common_input_cb: null SA"));
KASSERT(sav->sah != NULL, ("ipsec6_common_input_cb: null SAH"));
saidx = &sav->sah->saidx;
af = saidx->dst.sa.sa_family;
KASSERT(af == AF_INET6,
("ipsec6_common_input_cb: unexpected af %u", af));
sproto = saidx->proto;
KASSERT(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 */

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

@ -0,0 +1,401 @@
/* $FreeBSD$ */
/*
* 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>
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;
KASSERT(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 MCBYTES 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;
}
/*
* Cluster'd mbufs are left alone (for now).
*/
if (!MEXT_IS_REF(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).
*/
/* XXX why can M_PKTHDR be set past the first mbuf? */
KASSERT(m->m_flags & M_EXT,
("m_clone: m_flags 0x%x", m->m_flags));
/* NB: we only coalesce into a cluster */
if (mprev == NULL || (mprev->m_flags & M_EXT) == 0 ||
m->m_len > M_TRAILINGSPACE(mprev)) {
struct mbuf *n;
/*
* Allocate a new page, copy the data to the front
* and release the reference to the old page.
*/
n = m_getcl(M_DONTWAIT, m->m_type, m->m_flags);
if (n == NULL) {
m_freem(m0);
return (NULL);
}
if (mprev == NULL && (m->m_flags & M_PKTHDR))
M_COPY_PKTHDR(n, m);
memcpy(mtod(n, caddr_t), mtod(m, caddr_t), m->m_len);
n->m_len = m->m_len;
n->m_next = m->m_next;
if (mprev == NULL)
m0 = n; /* new head of chain */
else
mprev->m_next = n; /* replace old mbuf */
m_free(m); /* release old mbuf */
mprev = n;
newipsecstat.ips_clcopied++;
} else {
/* 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++;
}
}
return (m0);
}
/*
* Make space for a new header of length hlen at offset off
* in 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;
KASSERT(m0 != NULL, ("m_dmakespace: null mbuf"));
KASSERT(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 */
/* XXX code doesn't handle clusters XXX */
KASSERT(remain < MLEN, ("m_makespace: remainder too big: %u", remain));
if (hlen > M_TRAILINGSPACE(m)) {
struct mbuf *n;
/*
* 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) {
KASSERT(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");
}

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

@ -0,0 +1,737 @@
/* $FreeBSD$ */
/* $KAME: ipsec.c,v 1.103 2001/05/24 07:14:18 sakane 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 <machine/in_cksum.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;
#if 0
SPLASSERT(net, "ipsec_process_done");
#endif
KASSERT(m != NULL, ("ipsec_process_done: null mbuf"));
KASSERT(isr != NULL, ("ipsec_process_done: null ISR"));
sav = isr->sav;
KASSERT(sav != NULL, ("ipsec_process_done: null SA"));
KASSERT(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 *);
ip->ip_len = ntohs(ip->ip_len);
ip->ip_off = ntohs(ip->ip_off);
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;
#if 0
SPLASSERT(net, "ipsec_nextisr");
#endif
KASSERT(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 */
KASSERT(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:
KASSERT(*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;
KASSERT(m != NULL, ("ipsec4_process_packet: null mbuf"));
KASSERT(isr != NULL, ("ipsec4_process_packet: null isr"));
s = splnet(); /* 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;
KASSERT(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_COPY_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;
KASSERT(state != NULL, ("ipsec6_output: null state"));
KASSERT(state->m != NULL, ("ipsec6_output: null m"));
KASSERT(nexthdrp != NULL, ("ipsec6_output: null nexthdrp"));
KASSERT(mprev != NULL, ("ipsec6_output: null mprev"));
KASSERT(sp != NULL, ("ipsec6_output: null sp"));
KASSERT(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;
}
KASSERT(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;
KASSERT(state != NULL, ("ipsec6_output: null state"));
KASSERT(state->m != NULL, ("ipsec6_output: null m"));
KASSERT(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=%u\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*/

7287
sys/netipsec/key.c Normal file

File diff suppressed because it is too large Load Diff

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

@ -0,0 +1,107 @@
/* $FreeBSD$ */
/* $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);
#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_ */

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

@ -0,0 +1,747 @@
/* $FreeBSD$ */
/* $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 "opt_inet.h"
#include "opt_inet6.h"
#include "opt_ipsec.h"
#endif
#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) {
printf(" m_ext{ ext_buf:%p ext_free:%p "
"ext_size:%u ref_cnt:%p }\n",
m->m_ext.ext_buf, m->m_ext.ext_free,
m->m_ext.ext_size, m->m_ext.ref_cnt);
}
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;
}

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

@ -0,0 +1,88 @@
/* $FreeBSD$ */
/* $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 (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_ */

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

@ -0,0 +1,74 @@
/* $FreeBSD$ */
/* $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 }, \
}
#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) ((caddr_t)((caddr_t)(key) + sizeof(struct sadb_key)))
#endif /*_KERNEL*/
#endif /* _NETIPSEC_KEY_VAR_H_ */

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

@ -0,0 +1,181 @@
/* $FreeBSD$ */
/* $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>
/*
* 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_ */

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

@ -0,0 +1,603 @@
/* $FreeBSD$ */
/* $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 "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 <machine/stdarg.h>
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 = splnet();
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_NOWAIT);
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, 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;
}
/*
* key_abort()
* derived from net/rtsock.c:rts_abort()
*/
static int
key_abort(struct socket *so)
{
int s, error;
s = splnet();
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 thread *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 splnet() 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();
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 thread *td)
{
int s, error;
s = splnet();
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 thread *td)
{
int s, error;
s = splnet();
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();
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();
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();
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 thread *td)
{
int s, error;
s = splnet();
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();
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();
error = raw_usrreqs.pru_sockaddr(so, nam);
splx(s);
return error;
}
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
};
/* sysctl */
SYSCTL_NODE(_net, PF_KEY, key, CTLFLAG_RW, 0, "Key Family");
/*
* Definitions of protocols supported in the KEY domain.
*/
extern struct domain keydomain;
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);

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

@ -0,0 +1,82 @@
/* $FreeBSD$ */
/* $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 *, ...));
extern int key_usrreq __P((struct socket *,
int, struct mbuf *, struct mbuf *, struct mbuf *));
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_*/

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

@ -0,0 +1,126 @@
/* $FreeBSD$ */
/* $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_ */

1209
sys/netipsec/xform_ah.c Normal file

File diff suppressed because it is too large Load Diff

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

@ -0,0 +1,966 @@
/* $FreeBSD$ */
/* $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 "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 <opencrypto/cryptodev.h>
#include <opencrypto/xform.h>
int esp_enable = 1;
struct espstat espstat;
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, "");
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??*/
KASSERT(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;
#if 0
SPLASSERT(net, "esp_input");
#endif
KASSERT(sav != NULL, ("esp_input: null SA"));
KASSERT(sav->tdb_encalgxform != NULL,
("esp_input: null encoding xform"));
KASSERT((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;
KASSERT(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 | CRYPTO_F_NODELAY;
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) {
KASSERT(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;
KASSERT(crd != NULL, ("esp_input_cb: null crypto descriptor!"));
tc = (struct tdb_crypto *) crp->crp_opaque;
KASSERT(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 = splnet();
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;
KASSERT(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;
#if 0
SPLASSERT(net, "esp_output");
#endif
sav = isr->sav;
KASSERT(sav != NULL, ("esp_output: null SA"));
esph = sav->tdb_authalgxform;
espx = sav->tdb_encalgxform;
KASSERT(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;
KASSERT(tc != NULL, ("esp_output_cb: null opaque data area!"));
m = (struct mbuf *) crp->crp_buf;
s = splnet();
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;
}
KASSERT(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
};
static 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
}
SYSINIT(esp_xform_init, SI_SUB_DRIVERS, SI_ORDER_FIRST, esp_attach, NULL)

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

@ -0,0 +1,608 @@
/* $FreeBSD$ */
/* $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.
*/
/* 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 <opencrypto/cryptodev.h>
#include <opencrypto/deflate.h>
#include <opencrypto/xform.h>
int ipcomp_enable = 0;
struct ipcompstat ipcompstat;
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, "");
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;
#if 0
SPLASSERT(net, "ipcomp_input");
#endif
/* 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 | CRYPTO_F_NODELAY;
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;
KASSERT(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 = splnet();
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;
KASSERT(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;
#if 0
SPLASSERT(net, "ipcomp_output");
#endif
sav = isr->sav;
KASSERT(sav != NULL, ("ipcomp_output: null SA"));
ipcompx = sav->tdb_compalgxform;
KASSERT(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;
KASSERT(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 = splnet();
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;
}
KASSERT(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
};
static void
ipcomp_attach(void)
{
xform_register(&ipcomp_xformsw);
}
SYSINIT(ipcomp_xform_init, SI_SUB_DRIVERS, SI_ORDER_FIRST, ipcomp_attach, NULL)

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

@ -0,0 +1,699 @@
/* $FreeBSD$ */
/* $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.
*/
/*
* IP-inside-IP processing
*/
#include "opt_inet.h"
#include "opt_inet6.h"
#include "opt_random_ip_id.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 <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>
#include <netinet/ipprotosw.h>
#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 <machine/stdarg.h>
/*
* 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;
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, "");
/* 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:
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:
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 */
#if 0
SPLASSERT(net, "ipip_output");
#endif
sav = isr->sav;
KASSERT(sav != NULL, ("ipip_output: null SA"));
KASSERT(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,
(pr_in_input_t*) ip4_input,
0, 0, rip_ctloutput,
0,
0, 0, 0, 0,
&rip_usrreqs
},
#ifdef INET6
{ SOCK_RAW, &inetdomain, IPPROTO_IPV6, PR_ATOMIC|PR_ADDR|PR_LASTHDR,
(pr_in_input_t*) ip4_input,
0, 0, rip_ctloutput,
0,
0, 0, 0, 0,
&rip_usrreqs
}
#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);
}
static 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
}
SYSINIT(ipe4_xform_init, SI_SUB_PROTO_DOMAIN, SI_ORDER_MIDDLE, ipe4_attach, NULL);
#endif /* FAST_IPSEC */