mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-08 01:08:03 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
85
net/xfrm/Kconfig
Normal file
85
net/xfrm/Kconfig
Normal file
|
@ -0,0 +1,85 @@
|
|||
#
|
||||
# XFRM configuration
|
||||
#
|
||||
config XFRM
|
||||
bool
|
||||
depends on NET
|
||||
|
||||
config XFRM_ALGO
|
||||
tristate
|
||||
select XFRM
|
||||
select CRYPTO
|
||||
|
||||
config XFRM_USER
|
||||
tristate "Transformation user configuration interface"
|
||||
depends on INET
|
||||
select XFRM_ALGO
|
||||
---help---
|
||||
Support for Transformation(XFRM) user configuration interface
|
||||
like IPsec used by native Linux tools.
|
||||
|
||||
If unsure, say Y.
|
||||
|
||||
config XFRM_SUB_POLICY
|
||||
bool "Transformation sub policy support"
|
||||
depends on XFRM
|
||||
---help---
|
||||
Support sub policy for developers. By using sub policy with main
|
||||
one, two policies can be applied to the same packet at once.
|
||||
Policy which lives shorter time in kernel should be a sub.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config XFRM_MIGRATE
|
||||
bool "Transformation migrate database"
|
||||
depends on XFRM
|
||||
---help---
|
||||
A feature to update locator(s) of a given IPsec security
|
||||
association dynamically. This feature is required, for
|
||||
instance, in a Mobile IPv6 environment with IPsec configuration
|
||||
where mobile nodes change their attachment point to the Internet.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config XFRM_STATISTICS
|
||||
bool "Transformation statistics"
|
||||
depends on INET && XFRM && PROC_FS
|
||||
---help---
|
||||
This statistics is not a SNMP/MIB specification but shows
|
||||
statistics about transformation error (or almost error) factor
|
||||
at packet processing for developer.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config XFRM_IPCOMP
|
||||
tristate
|
||||
select XFRM_ALGO
|
||||
select CRYPTO
|
||||
select CRYPTO_DEFLATE
|
||||
|
||||
config NET_KEY
|
||||
tristate "PF_KEY sockets"
|
||||
select XFRM_ALGO
|
||||
---help---
|
||||
PF_KEYv2 socket family, compatible to KAME ones.
|
||||
They are required if you are going to use IPsec tools ported
|
||||
from KAME.
|
||||
|
||||
Say Y unless you know what you are doing.
|
||||
|
||||
config NET_KEY_MIGRATE
|
||||
bool "PF_KEY MIGRATE"
|
||||
depends on NET_KEY
|
||||
select XFRM_MIGRATE
|
||||
---help---
|
||||
Add a PF_KEY MIGRATE message to PF_KEYv2 socket family.
|
||||
The PF_KEY MIGRATE message is used to dynamically update
|
||||
locator(s) of a given IPsec security association.
|
||||
This feature is required, for instance, in a Mobile IPv6
|
||||
environment with IPsec configuration where mobile nodes
|
||||
change their attachment point to the Internet. Detail
|
||||
information can be found in the internet-draft
|
||||
<draft-sugimoto-mip6-pfkey-migrate>.
|
||||
|
||||
If unsure, say N.
|
||||
|
11
net/xfrm/Makefile
Normal file
11
net/xfrm/Makefile
Normal file
|
@ -0,0 +1,11 @@
|
|||
#
|
||||
# Makefile for the XFRM subsystem.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_XFRM) := xfrm_policy.o xfrm_state.o xfrm_hash.o \
|
||||
xfrm_input.o xfrm_output.o \
|
||||
xfrm_sysctl.o xfrm_replay.o
|
||||
obj-$(CONFIG_XFRM_STATISTICS) += xfrm_proc.o
|
||||
obj-$(CONFIG_XFRM_ALGO) += xfrm_algo.o
|
||||
obj-$(CONFIG_XFRM_USER) += xfrm_user.o
|
||||
obj-$(CONFIG_XFRM_IPCOMP) += xfrm_ipcomp.o
|
805
net/xfrm/xfrm_algo.c
Normal file
805
net/xfrm/xfrm_algo.c
Normal file
|
@ -0,0 +1,805 @@
|
|||
/*
|
||||
* xfrm algorithm interface
|
||||
*
|
||||
* Copyright (c) 2002 James Morris <jmorris@intercode.com.au>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/pfkeyv2.h>
|
||||
#include <linux/crypto.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <net/xfrm.h>
|
||||
#if defined(CONFIG_INET_ESP) || defined(CONFIG_INET_ESP_MODULE) || defined(CONFIG_INET6_ESP) || defined(CONFIG_INET6_ESP_MODULE)
|
||||
#include <net/esp.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Algorithms supported by IPsec. These entries contain properties which
|
||||
* are used in key negotiation and xfrm processing, and are used to verify
|
||||
* that instantiated crypto transforms have correct parameters for IPsec
|
||||
* purposes.
|
||||
*/
|
||||
static struct xfrm_algo_desc aead_list[] = {
|
||||
{
|
||||
.name = "rfc4106(gcm(aes))",
|
||||
|
||||
.uinfo = {
|
||||
.aead = {
|
||||
.icv_truncbits = 64,
|
||||
}
|
||||
},
|
||||
|
||||
.pfkey_supported = 1,
|
||||
|
||||
.desc = {
|
||||
.sadb_alg_id = SADB_X_EALG_AES_GCM_ICV8,
|
||||
.sadb_alg_ivlen = 8,
|
||||
.sadb_alg_minbits = 128,
|
||||
.sadb_alg_maxbits = 256
|
||||
}
|
||||
},
|
||||
{
|
||||
.name = "rfc4106(gcm(aes))",
|
||||
|
||||
.uinfo = {
|
||||
.aead = {
|
||||
.icv_truncbits = 96,
|
||||
}
|
||||
},
|
||||
|
||||
.pfkey_supported = 1,
|
||||
|
||||
.desc = {
|
||||
.sadb_alg_id = SADB_X_EALG_AES_GCM_ICV12,
|
||||
.sadb_alg_ivlen = 8,
|
||||
.sadb_alg_minbits = 128,
|
||||
.sadb_alg_maxbits = 256
|
||||
}
|
||||
},
|
||||
{
|
||||
.name = "rfc4106(gcm(aes))",
|
||||
|
||||
.uinfo = {
|
||||
.aead = {
|
||||
.icv_truncbits = 128,
|
||||
}
|
||||
},
|
||||
|
||||
.pfkey_supported = 1,
|
||||
|
||||
.desc = {
|
||||
.sadb_alg_id = SADB_X_EALG_AES_GCM_ICV16,
|
||||
.sadb_alg_ivlen = 8,
|
||||
.sadb_alg_minbits = 128,
|
||||
.sadb_alg_maxbits = 256
|
||||
}
|
||||
},
|
||||
{
|
||||
.name = "rfc4309(ccm(aes))",
|
||||
|
||||
.uinfo = {
|
||||
.aead = {
|
||||
.icv_truncbits = 64,
|
||||
}
|
||||
},
|
||||
|
||||
.pfkey_supported = 1,
|
||||
|
||||
.desc = {
|
||||
.sadb_alg_id = SADB_X_EALG_AES_CCM_ICV8,
|
||||
.sadb_alg_ivlen = 8,
|
||||
.sadb_alg_minbits = 128,
|
||||
.sadb_alg_maxbits = 256
|
||||
}
|
||||
},
|
||||
{
|
||||
.name = "rfc4309(ccm(aes))",
|
||||
|
||||
.uinfo = {
|
||||
.aead = {
|
||||
.icv_truncbits = 96,
|
||||
}
|
||||
},
|
||||
|
||||
.pfkey_supported = 1,
|
||||
|
||||
.desc = {
|
||||
.sadb_alg_id = SADB_X_EALG_AES_CCM_ICV12,
|
||||
.sadb_alg_ivlen = 8,
|
||||
.sadb_alg_minbits = 128,
|
||||
.sadb_alg_maxbits = 256
|
||||
}
|
||||
},
|
||||
{
|
||||
.name = "rfc4309(ccm(aes))",
|
||||
|
||||
.uinfo = {
|
||||
.aead = {
|
||||
.icv_truncbits = 128,
|
||||
}
|
||||
},
|
||||
|
||||
.pfkey_supported = 1,
|
||||
|
||||
.desc = {
|
||||
.sadb_alg_id = SADB_X_EALG_AES_CCM_ICV16,
|
||||
.sadb_alg_ivlen = 8,
|
||||
.sadb_alg_minbits = 128,
|
||||
.sadb_alg_maxbits = 256
|
||||
}
|
||||
},
|
||||
{
|
||||
.name = "rfc4543(gcm(aes))",
|
||||
|
||||
.uinfo = {
|
||||
.aead = {
|
||||
.icv_truncbits = 128,
|
||||
}
|
||||
},
|
||||
|
||||
.pfkey_supported = 1,
|
||||
|
||||
.desc = {
|
||||
.sadb_alg_id = SADB_X_EALG_NULL_AES_GMAC,
|
||||
.sadb_alg_ivlen = 8,
|
||||
.sadb_alg_minbits = 128,
|
||||
.sadb_alg_maxbits = 256
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
static struct xfrm_algo_desc aalg_list[] = {
|
||||
{
|
||||
.name = "digest_null",
|
||||
|
||||
.uinfo = {
|
||||
.auth = {
|
||||
.icv_truncbits = 0,
|
||||
.icv_fullbits = 0,
|
||||
}
|
||||
},
|
||||
|
||||
.pfkey_supported = 1,
|
||||
|
||||
.desc = {
|
||||
.sadb_alg_id = SADB_X_AALG_NULL,
|
||||
.sadb_alg_ivlen = 0,
|
||||
.sadb_alg_minbits = 0,
|
||||
.sadb_alg_maxbits = 0
|
||||
}
|
||||
},
|
||||
{
|
||||
.name = "hmac(md5)",
|
||||
.compat = "md5",
|
||||
|
||||
.uinfo = {
|
||||
.auth = {
|
||||
.icv_truncbits = 96,
|
||||
.icv_fullbits = 128,
|
||||
}
|
||||
},
|
||||
|
||||
.pfkey_supported = 1,
|
||||
|
||||
.desc = {
|
||||
.sadb_alg_id = SADB_AALG_MD5HMAC,
|
||||
.sadb_alg_ivlen = 0,
|
||||
.sadb_alg_minbits = 128,
|
||||
.sadb_alg_maxbits = 128
|
||||
}
|
||||
},
|
||||
{
|
||||
.name = "hmac(sha1)",
|
||||
.compat = "sha1",
|
||||
|
||||
.uinfo = {
|
||||
.auth = {
|
||||
.icv_truncbits = 96,
|
||||
.icv_fullbits = 160,
|
||||
}
|
||||
},
|
||||
|
||||
.pfkey_supported = 1,
|
||||
|
||||
.desc = {
|
||||
.sadb_alg_id = SADB_AALG_SHA1HMAC,
|
||||
.sadb_alg_ivlen = 0,
|
||||
.sadb_alg_minbits = 160,
|
||||
.sadb_alg_maxbits = 160
|
||||
}
|
||||
},
|
||||
{
|
||||
.name = "hmac(sha256)",
|
||||
.compat = "sha256",
|
||||
|
||||
.uinfo = {
|
||||
.auth = {
|
||||
.icv_truncbits = 96,
|
||||
.icv_fullbits = 256,
|
||||
}
|
||||
},
|
||||
|
||||
.pfkey_supported = 1,
|
||||
|
||||
.desc = {
|
||||
.sadb_alg_id = SADB_X_AALG_SHA2_256HMAC,
|
||||
.sadb_alg_ivlen = 0,
|
||||
.sadb_alg_minbits = 256,
|
||||
.sadb_alg_maxbits = 256
|
||||
}
|
||||
},
|
||||
{
|
||||
.name = "hmac(sha384)",
|
||||
|
||||
.uinfo = {
|
||||
.auth = {
|
||||
.icv_truncbits = 192,
|
||||
.icv_fullbits = 384,
|
||||
}
|
||||
},
|
||||
|
||||
.pfkey_supported = 1,
|
||||
|
||||
.desc = {
|
||||
.sadb_alg_id = SADB_X_AALG_SHA2_384HMAC,
|
||||
.sadb_alg_ivlen = 0,
|
||||
.sadb_alg_minbits = 384,
|
||||
.sadb_alg_maxbits = 384
|
||||
}
|
||||
},
|
||||
{
|
||||
.name = "hmac(sha512)",
|
||||
|
||||
.uinfo = {
|
||||
.auth = {
|
||||
.icv_truncbits = 256,
|
||||
.icv_fullbits = 512,
|
||||
}
|
||||
},
|
||||
|
||||
.pfkey_supported = 1,
|
||||
|
||||
.desc = {
|
||||
.sadb_alg_id = SADB_X_AALG_SHA2_512HMAC,
|
||||
.sadb_alg_ivlen = 0,
|
||||
.sadb_alg_minbits = 512,
|
||||
.sadb_alg_maxbits = 512
|
||||
}
|
||||
},
|
||||
{
|
||||
.name = "hmac(rmd160)",
|
||||
.compat = "rmd160",
|
||||
|
||||
.uinfo = {
|
||||
.auth = {
|
||||
.icv_truncbits = 96,
|
||||
.icv_fullbits = 160,
|
||||
}
|
||||
},
|
||||
|
||||
.pfkey_supported = 1,
|
||||
|
||||
.desc = {
|
||||
.sadb_alg_id = SADB_X_AALG_RIPEMD160HMAC,
|
||||
.sadb_alg_ivlen = 0,
|
||||
.sadb_alg_minbits = 160,
|
||||
.sadb_alg_maxbits = 160
|
||||
}
|
||||
},
|
||||
{
|
||||
.name = "xcbc(aes)",
|
||||
|
||||
.uinfo = {
|
||||
.auth = {
|
||||
.icv_truncbits = 96,
|
||||
.icv_fullbits = 128,
|
||||
}
|
||||
},
|
||||
|
||||
.pfkey_supported = 1,
|
||||
|
||||
.desc = {
|
||||
.sadb_alg_id = SADB_X_AALG_AES_XCBC_MAC,
|
||||
.sadb_alg_ivlen = 0,
|
||||
.sadb_alg_minbits = 128,
|
||||
.sadb_alg_maxbits = 128
|
||||
}
|
||||
},
|
||||
{
|
||||
/* rfc4494 */
|
||||
.name = "cmac(aes)",
|
||||
|
||||
.uinfo = {
|
||||
.auth = {
|
||||
.icv_truncbits = 96,
|
||||
.icv_fullbits = 128,
|
||||
}
|
||||
},
|
||||
|
||||
.pfkey_supported = 0,
|
||||
},
|
||||
};
|
||||
|
||||
static struct xfrm_algo_desc ealg_list[] = {
|
||||
{
|
||||
.name = "ecb(cipher_null)",
|
||||
.compat = "cipher_null",
|
||||
|
||||
.uinfo = {
|
||||
.encr = {
|
||||
.blockbits = 8,
|
||||
.defkeybits = 0,
|
||||
}
|
||||
},
|
||||
|
||||
.pfkey_supported = 1,
|
||||
|
||||
.desc = {
|
||||
.sadb_alg_id = SADB_EALG_NULL,
|
||||
.sadb_alg_ivlen = 0,
|
||||
.sadb_alg_minbits = 0,
|
||||
.sadb_alg_maxbits = 0
|
||||
}
|
||||
},
|
||||
{
|
||||
.name = "cbc(des)",
|
||||
.compat = "des",
|
||||
|
||||
.uinfo = {
|
||||
.encr = {
|
||||
.blockbits = 64,
|
||||
.defkeybits = 64,
|
||||
}
|
||||
},
|
||||
|
||||
.pfkey_supported = 1,
|
||||
|
||||
.desc = {
|
||||
.sadb_alg_id = SADB_EALG_DESCBC,
|
||||
.sadb_alg_ivlen = 8,
|
||||
.sadb_alg_minbits = 64,
|
||||
.sadb_alg_maxbits = 64
|
||||
}
|
||||
},
|
||||
{
|
||||
.name = "cbc(des3_ede)",
|
||||
.compat = "des3_ede",
|
||||
|
||||
.uinfo = {
|
||||
.encr = {
|
||||
.blockbits = 64,
|
||||
.defkeybits = 192,
|
||||
}
|
||||
},
|
||||
|
||||
.pfkey_supported = 1,
|
||||
|
||||
.desc = {
|
||||
.sadb_alg_id = SADB_EALG_3DESCBC,
|
||||
.sadb_alg_ivlen = 8,
|
||||
.sadb_alg_minbits = 192,
|
||||
.sadb_alg_maxbits = 192
|
||||
}
|
||||
},
|
||||
{
|
||||
.name = "cbc(cast5)",
|
||||
.compat = "cast5",
|
||||
|
||||
.uinfo = {
|
||||
.encr = {
|
||||
.blockbits = 64,
|
||||
.defkeybits = 128,
|
||||
}
|
||||
},
|
||||
|
||||
.pfkey_supported = 1,
|
||||
|
||||
.desc = {
|
||||
.sadb_alg_id = SADB_X_EALG_CASTCBC,
|
||||
.sadb_alg_ivlen = 8,
|
||||
.sadb_alg_minbits = 40,
|
||||
.sadb_alg_maxbits = 128
|
||||
}
|
||||
},
|
||||
{
|
||||
.name = "cbc(blowfish)",
|
||||
.compat = "blowfish",
|
||||
|
||||
.uinfo = {
|
||||
.encr = {
|
||||
.blockbits = 64,
|
||||
.defkeybits = 128,
|
||||
}
|
||||
},
|
||||
|
||||
.pfkey_supported = 1,
|
||||
|
||||
.desc = {
|
||||
.sadb_alg_id = SADB_X_EALG_BLOWFISHCBC,
|
||||
.sadb_alg_ivlen = 8,
|
||||
.sadb_alg_minbits = 40,
|
||||
.sadb_alg_maxbits = 448
|
||||
}
|
||||
},
|
||||
{
|
||||
.name = "cbc(aes)",
|
||||
.compat = "aes",
|
||||
|
||||
.uinfo = {
|
||||
.encr = {
|
||||
.blockbits = 128,
|
||||
.defkeybits = 128,
|
||||
}
|
||||
},
|
||||
|
||||
.pfkey_supported = 1,
|
||||
|
||||
.desc = {
|
||||
.sadb_alg_id = SADB_X_EALG_AESCBC,
|
||||
.sadb_alg_ivlen = 8,
|
||||
.sadb_alg_minbits = 128,
|
||||
.sadb_alg_maxbits = 256
|
||||
}
|
||||
},
|
||||
{
|
||||
.name = "cbc(serpent)",
|
||||
.compat = "serpent",
|
||||
|
||||
.uinfo = {
|
||||
.encr = {
|
||||
.blockbits = 128,
|
||||
.defkeybits = 128,
|
||||
}
|
||||
},
|
||||
|
||||
.pfkey_supported = 1,
|
||||
|
||||
.desc = {
|
||||
.sadb_alg_id = SADB_X_EALG_SERPENTCBC,
|
||||
.sadb_alg_ivlen = 8,
|
||||
.sadb_alg_minbits = 128,
|
||||
.sadb_alg_maxbits = 256,
|
||||
}
|
||||
},
|
||||
{
|
||||
.name = "cbc(camellia)",
|
||||
.compat = "camellia",
|
||||
|
||||
.uinfo = {
|
||||
.encr = {
|
||||
.blockbits = 128,
|
||||
.defkeybits = 128,
|
||||
}
|
||||
},
|
||||
|
||||
.pfkey_supported = 1,
|
||||
|
||||
.desc = {
|
||||
.sadb_alg_id = SADB_X_EALG_CAMELLIACBC,
|
||||
.sadb_alg_ivlen = 8,
|
||||
.sadb_alg_minbits = 128,
|
||||
.sadb_alg_maxbits = 256
|
||||
}
|
||||
},
|
||||
{
|
||||
.name = "cbc(twofish)",
|
||||
.compat = "twofish",
|
||||
|
||||
.uinfo = {
|
||||
.encr = {
|
||||
.blockbits = 128,
|
||||
.defkeybits = 128,
|
||||
}
|
||||
},
|
||||
|
||||
.pfkey_supported = 1,
|
||||
|
||||
.desc = {
|
||||
.sadb_alg_id = SADB_X_EALG_TWOFISHCBC,
|
||||
.sadb_alg_ivlen = 8,
|
||||
.sadb_alg_minbits = 128,
|
||||
.sadb_alg_maxbits = 256
|
||||
}
|
||||
},
|
||||
{
|
||||
.name = "rfc3686(ctr(aes))",
|
||||
|
||||
.uinfo = {
|
||||
.encr = {
|
||||
.blockbits = 128,
|
||||
.defkeybits = 160, /* 128-bit key + 32-bit nonce */
|
||||
}
|
||||
},
|
||||
|
||||
.pfkey_supported = 1,
|
||||
|
||||
.desc = {
|
||||
.sadb_alg_id = SADB_X_EALG_AESCTR,
|
||||
.sadb_alg_ivlen = 8,
|
||||
.sadb_alg_minbits = 160,
|
||||
.sadb_alg_maxbits = 288
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
static struct xfrm_algo_desc calg_list[] = {
|
||||
{
|
||||
.name = "deflate",
|
||||
.uinfo = {
|
||||
.comp = {
|
||||
.threshold = 90,
|
||||
}
|
||||
},
|
||||
.pfkey_supported = 1,
|
||||
.desc = { .sadb_alg_id = SADB_X_CALG_DEFLATE }
|
||||
},
|
||||
{
|
||||
.name = "lzs",
|
||||
.uinfo = {
|
||||
.comp = {
|
||||
.threshold = 90,
|
||||
}
|
||||
},
|
||||
.pfkey_supported = 1,
|
||||
.desc = { .sadb_alg_id = SADB_X_CALG_LZS }
|
||||
},
|
||||
{
|
||||
.name = "lzjh",
|
||||
.uinfo = {
|
||||
.comp = {
|
||||
.threshold = 50,
|
||||
}
|
||||
},
|
||||
.pfkey_supported = 1,
|
||||
.desc = { .sadb_alg_id = SADB_X_CALG_LZJH }
|
||||
},
|
||||
};
|
||||
|
||||
static inline int aead_entries(void)
|
||||
{
|
||||
return ARRAY_SIZE(aead_list);
|
||||
}
|
||||
|
||||
static inline int aalg_entries(void)
|
||||
{
|
||||
return ARRAY_SIZE(aalg_list);
|
||||
}
|
||||
|
||||
static inline int ealg_entries(void)
|
||||
{
|
||||
return ARRAY_SIZE(ealg_list);
|
||||
}
|
||||
|
||||
static inline int calg_entries(void)
|
||||
{
|
||||
return ARRAY_SIZE(calg_list);
|
||||
}
|
||||
|
||||
struct xfrm_algo_list {
|
||||
struct xfrm_algo_desc *algs;
|
||||
int entries;
|
||||
u32 type;
|
||||
u32 mask;
|
||||
};
|
||||
|
||||
static const struct xfrm_algo_list xfrm_aead_list = {
|
||||
.algs = aead_list,
|
||||
.entries = ARRAY_SIZE(aead_list),
|
||||
.type = CRYPTO_ALG_TYPE_AEAD,
|
||||
.mask = CRYPTO_ALG_TYPE_MASK,
|
||||
};
|
||||
|
||||
static const struct xfrm_algo_list xfrm_aalg_list = {
|
||||
.algs = aalg_list,
|
||||
.entries = ARRAY_SIZE(aalg_list),
|
||||
.type = CRYPTO_ALG_TYPE_HASH,
|
||||
.mask = CRYPTO_ALG_TYPE_HASH_MASK,
|
||||
};
|
||||
|
||||
static const struct xfrm_algo_list xfrm_ealg_list = {
|
||||
.algs = ealg_list,
|
||||
.entries = ARRAY_SIZE(ealg_list),
|
||||
.type = CRYPTO_ALG_TYPE_BLKCIPHER,
|
||||
.mask = CRYPTO_ALG_TYPE_BLKCIPHER_MASK,
|
||||
};
|
||||
|
||||
static const struct xfrm_algo_list xfrm_calg_list = {
|
||||
.algs = calg_list,
|
||||
.entries = ARRAY_SIZE(calg_list),
|
||||
.type = CRYPTO_ALG_TYPE_COMPRESS,
|
||||
.mask = CRYPTO_ALG_TYPE_MASK,
|
||||
};
|
||||
|
||||
static struct xfrm_algo_desc *xfrm_find_algo(
|
||||
const struct xfrm_algo_list *algo_list,
|
||||
int match(const struct xfrm_algo_desc *entry, const void *data),
|
||||
const void *data, int probe)
|
||||
{
|
||||
struct xfrm_algo_desc *list = algo_list->algs;
|
||||
int i, status;
|
||||
|
||||
for (i = 0; i < algo_list->entries; i++) {
|
||||
if (!match(list + i, data))
|
||||
continue;
|
||||
|
||||
if (list[i].available)
|
||||
return &list[i];
|
||||
|
||||
if (!probe)
|
||||
break;
|
||||
|
||||
status = crypto_has_alg(list[i].name, algo_list->type,
|
||||
algo_list->mask);
|
||||
if (!status)
|
||||
break;
|
||||
|
||||
list[i].available = status;
|
||||
return &list[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int xfrm_alg_id_match(const struct xfrm_algo_desc *entry,
|
||||
const void *data)
|
||||
{
|
||||
return entry->desc.sadb_alg_id == (unsigned long)data;
|
||||
}
|
||||
|
||||
struct xfrm_algo_desc *xfrm_aalg_get_byid(int alg_id)
|
||||
{
|
||||
return xfrm_find_algo(&xfrm_aalg_list, xfrm_alg_id_match,
|
||||
(void *)(unsigned long)alg_id, 1);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xfrm_aalg_get_byid);
|
||||
|
||||
struct xfrm_algo_desc *xfrm_ealg_get_byid(int alg_id)
|
||||
{
|
||||
return xfrm_find_algo(&xfrm_ealg_list, xfrm_alg_id_match,
|
||||
(void *)(unsigned long)alg_id, 1);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xfrm_ealg_get_byid);
|
||||
|
||||
struct xfrm_algo_desc *xfrm_calg_get_byid(int alg_id)
|
||||
{
|
||||
return xfrm_find_algo(&xfrm_calg_list, xfrm_alg_id_match,
|
||||
(void *)(unsigned long)alg_id, 1);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xfrm_calg_get_byid);
|
||||
|
||||
static int xfrm_alg_name_match(const struct xfrm_algo_desc *entry,
|
||||
const void *data)
|
||||
{
|
||||
const char *name = data;
|
||||
|
||||
return name && (!strcmp(name, entry->name) ||
|
||||
(entry->compat && !strcmp(name, entry->compat)));
|
||||
}
|
||||
|
||||
struct xfrm_algo_desc *xfrm_aalg_get_byname(const char *name, int probe)
|
||||
{
|
||||
return xfrm_find_algo(&xfrm_aalg_list, xfrm_alg_name_match, name,
|
||||
probe);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xfrm_aalg_get_byname);
|
||||
|
||||
struct xfrm_algo_desc *xfrm_ealg_get_byname(const char *name, int probe)
|
||||
{
|
||||
return xfrm_find_algo(&xfrm_ealg_list, xfrm_alg_name_match, name,
|
||||
probe);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xfrm_ealg_get_byname);
|
||||
|
||||
struct xfrm_algo_desc *xfrm_calg_get_byname(const char *name, int probe)
|
||||
{
|
||||
return xfrm_find_algo(&xfrm_calg_list, xfrm_alg_name_match, name,
|
||||
probe);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xfrm_calg_get_byname);
|
||||
|
||||
struct xfrm_aead_name {
|
||||
const char *name;
|
||||
int icvbits;
|
||||
};
|
||||
|
||||
static int xfrm_aead_name_match(const struct xfrm_algo_desc *entry,
|
||||
const void *data)
|
||||
{
|
||||
const struct xfrm_aead_name *aead = data;
|
||||
const char *name = aead->name;
|
||||
|
||||
return aead->icvbits == entry->uinfo.aead.icv_truncbits && name &&
|
||||
!strcmp(name, entry->name);
|
||||
}
|
||||
|
||||
struct xfrm_algo_desc *xfrm_aead_get_byname(const char *name, int icv_len, int probe)
|
||||
{
|
||||
struct xfrm_aead_name data = {
|
||||
.name = name,
|
||||
.icvbits = icv_len,
|
||||
};
|
||||
|
||||
return xfrm_find_algo(&xfrm_aead_list, xfrm_aead_name_match, &data,
|
||||
probe);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xfrm_aead_get_byname);
|
||||
|
||||
struct xfrm_algo_desc *xfrm_aalg_get_byidx(unsigned int idx)
|
||||
{
|
||||
if (idx >= aalg_entries())
|
||||
return NULL;
|
||||
|
||||
return &aalg_list[idx];
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xfrm_aalg_get_byidx);
|
||||
|
||||
struct xfrm_algo_desc *xfrm_ealg_get_byidx(unsigned int idx)
|
||||
{
|
||||
if (idx >= ealg_entries())
|
||||
return NULL;
|
||||
|
||||
return &ealg_list[idx];
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xfrm_ealg_get_byidx);
|
||||
|
||||
/*
|
||||
* Probe for the availability of crypto algorithms, and set the available
|
||||
* flag for any algorithms found on the system. This is typically called by
|
||||
* pfkey during userspace SA add, update or register.
|
||||
*/
|
||||
void xfrm_probe_algs(void)
|
||||
{
|
||||
int i, status;
|
||||
|
||||
BUG_ON(in_softirq());
|
||||
|
||||
for (i = 0; i < aalg_entries(); i++) {
|
||||
status = crypto_has_hash(aalg_list[i].name, 0,
|
||||
CRYPTO_ALG_ASYNC);
|
||||
if (aalg_list[i].available != status)
|
||||
aalg_list[i].available = status;
|
||||
}
|
||||
|
||||
for (i = 0; i < ealg_entries(); i++) {
|
||||
status = crypto_has_ablkcipher(ealg_list[i].name, 0, 0);
|
||||
if (ealg_list[i].available != status)
|
||||
ealg_list[i].available = status;
|
||||
}
|
||||
|
||||
for (i = 0; i < calg_entries(); i++) {
|
||||
status = crypto_has_comp(calg_list[i].name, 0,
|
||||
CRYPTO_ALG_ASYNC);
|
||||
if (calg_list[i].available != status)
|
||||
calg_list[i].available = status;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xfrm_probe_algs);
|
||||
|
||||
int xfrm_count_pfkey_auth_supported(void)
|
||||
{
|
||||
int i, n;
|
||||
|
||||
for (i = 0, n = 0; i < aalg_entries(); i++)
|
||||
if (aalg_list[i].available && aalg_list[i].pfkey_supported)
|
||||
n++;
|
||||
return n;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xfrm_count_pfkey_auth_supported);
|
||||
|
||||
int xfrm_count_pfkey_enc_supported(void)
|
||||
{
|
||||
int i, n;
|
||||
|
||||
for (i = 0, n = 0; i < ealg_entries(); i++)
|
||||
if (ealg_list[i].available && ealg_list[i].pfkey_supported)
|
||||
n++;
|
||||
return n;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xfrm_count_pfkey_enc_supported);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
39
net/xfrm/xfrm_hash.c
Normal file
39
net/xfrm/xfrm_hash.c
Normal file
|
@ -0,0 +1,39 @@
|
|||
/* xfrm_hash.c: Common hash table code.
|
||||
*
|
||||
* Copyright (C) 2006 David S. Miller (davem@davemloft.net)
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/bootmem.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/xfrm.h>
|
||||
|
||||
#include "xfrm_hash.h"
|
||||
|
||||
struct hlist_head *xfrm_hash_alloc(unsigned int sz)
|
||||
{
|
||||
struct hlist_head *n;
|
||||
|
||||
if (sz <= PAGE_SIZE)
|
||||
n = kzalloc(sz, GFP_KERNEL);
|
||||
else if (hashdist)
|
||||
n = vzalloc(sz);
|
||||
else
|
||||
n = (struct hlist_head *)
|
||||
__get_free_pages(GFP_KERNEL | __GFP_NOWARN | __GFP_ZERO,
|
||||
get_order(sz));
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
void xfrm_hash_free(struct hlist_head *n, unsigned int sz)
|
||||
{
|
||||
if (sz <= PAGE_SIZE)
|
||||
kfree(n);
|
||||
else if (hashdist)
|
||||
vfree(n);
|
||||
else
|
||||
free_pages((unsigned long)n, get_order(sz));
|
||||
}
|
192
net/xfrm/xfrm_hash.h
Normal file
192
net/xfrm/xfrm_hash.h
Normal file
|
@ -0,0 +1,192 @@
|
|||
#ifndef _XFRM_HASH_H
|
||||
#define _XFRM_HASH_H
|
||||
|
||||
#include <linux/xfrm.h>
|
||||
#include <linux/socket.h>
|
||||
#include <linux/jhash.h>
|
||||
|
||||
static inline unsigned int __xfrm4_addr_hash(const xfrm_address_t *addr)
|
||||
{
|
||||
return ntohl(addr->a4);
|
||||
}
|
||||
|
||||
static inline unsigned int __xfrm6_addr_hash(const xfrm_address_t *addr)
|
||||
{
|
||||
return ntohl(addr->a6[2] ^ addr->a6[3]);
|
||||
}
|
||||
|
||||
static inline unsigned int __xfrm4_daddr_saddr_hash(const xfrm_address_t *daddr,
|
||||
const xfrm_address_t *saddr)
|
||||
{
|
||||
u32 sum = (__force u32)daddr->a4 + (__force u32)saddr->a4;
|
||||
return ntohl((__force __be32)sum);
|
||||
}
|
||||
|
||||
static inline unsigned int __xfrm6_daddr_saddr_hash(const xfrm_address_t *daddr,
|
||||
const xfrm_address_t *saddr)
|
||||
{
|
||||
return ntohl(daddr->a6[2] ^ daddr->a6[3] ^
|
||||
saddr->a6[2] ^ saddr->a6[3]);
|
||||
}
|
||||
|
||||
static inline u32 __bits2mask32(__u8 bits)
|
||||
{
|
||||
u32 mask32 = 0xffffffff;
|
||||
|
||||
if (bits == 0)
|
||||
mask32 = 0;
|
||||
else if (bits < 32)
|
||||
mask32 <<= (32 - bits);
|
||||
|
||||
return mask32;
|
||||
}
|
||||
|
||||
static inline unsigned int __xfrm4_dpref_spref_hash(const xfrm_address_t *daddr,
|
||||
const xfrm_address_t *saddr,
|
||||
__u8 dbits,
|
||||
__u8 sbits)
|
||||
{
|
||||
return jhash_2words(ntohl(daddr->a4) & __bits2mask32(dbits),
|
||||
ntohl(saddr->a4) & __bits2mask32(sbits),
|
||||
0);
|
||||
}
|
||||
|
||||
static inline unsigned int __xfrm6_pref_hash(const xfrm_address_t *addr,
|
||||
__u8 prefixlen)
|
||||
{
|
||||
int pdw;
|
||||
int pbi;
|
||||
u32 initval = 0;
|
||||
|
||||
pdw = prefixlen >> 5; /* num of whole u32 in prefix */
|
||||
pbi = prefixlen & 0x1f; /* num of bits in incomplete u32 in prefix */
|
||||
|
||||
if (pbi) {
|
||||
__be32 mask;
|
||||
|
||||
mask = htonl((0xffffffff) << (32 - pbi));
|
||||
|
||||
initval = (__force u32)(addr->a6[pdw] & mask);
|
||||
}
|
||||
|
||||
return jhash2((__force u32 *)addr->a6, pdw, initval);
|
||||
}
|
||||
|
||||
static inline unsigned int __xfrm6_dpref_spref_hash(const xfrm_address_t *daddr,
|
||||
const xfrm_address_t *saddr,
|
||||
__u8 dbits,
|
||||
__u8 sbits)
|
||||
{
|
||||
return __xfrm6_pref_hash(daddr, dbits) ^
|
||||
__xfrm6_pref_hash(saddr, sbits);
|
||||
}
|
||||
|
||||
static inline unsigned int __xfrm_dst_hash(const xfrm_address_t *daddr,
|
||||
const xfrm_address_t *saddr,
|
||||
u32 reqid, unsigned short family,
|
||||
unsigned int hmask)
|
||||
{
|
||||
unsigned int h = family ^ reqid;
|
||||
switch (family) {
|
||||
case AF_INET:
|
||||
h ^= __xfrm4_daddr_saddr_hash(daddr, saddr);
|
||||
break;
|
||||
case AF_INET6:
|
||||
h ^= __xfrm6_daddr_saddr_hash(daddr, saddr);
|
||||
break;
|
||||
}
|
||||
return (h ^ (h >> 16)) & hmask;
|
||||
}
|
||||
|
||||
static inline unsigned int __xfrm_src_hash(const xfrm_address_t *daddr,
|
||||
const xfrm_address_t *saddr,
|
||||
unsigned short family,
|
||||
unsigned int hmask)
|
||||
{
|
||||
unsigned int h = family;
|
||||
switch (family) {
|
||||
case AF_INET:
|
||||
h ^= __xfrm4_daddr_saddr_hash(daddr, saddr);
|
||||
break;
|
||||
case AF_INET6:
|
||||
h ^= __xfrm6_daddr_saddr_hash(daddr, saddr);
|
||||
break;
|
||||
}
|
||||
return (h ^ (h >> 16)) & hmask;
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
__xfrm_spi_hash(const xfrm_address_t *daddr, __be32 spi, u8 proto,
|
||||
unsigned short family, unsigned int hmask)
|
||||
{
|
||||
unsigned int h = (__force u32)spi ^ proto;
|
||||
switch (family) {
|
||||
case AF_INET:
|
||||
h ^= __xfrm4_addr_hash(daddr);
|
||||
break;
|
||||
case AF_INET6:
|
||||
h ^= __xfrm6_addr_hash(daddr);
|
||||
break;
|
||||
}
|
||||
return (h ^ (h >> 10) ^ (h >> 20)) & hmask;
|
||||
}
|
||||
|
||||
static inline unsigned int __idx_hash(u32 index, unsigned int hmask)
|
||||
{
|
||||
return (index ^ (index >> 8)) & hmask;
|
||||
}
|
||||
|
||||
static inline unsigned int __sel_hash(const struct xfrm_selector *sel,
|
||||
unsigned short family, unsigned int hmask,
|
||||
u8 dbits, u8 sbits)
|
||||
{
|
||||
const xfrm_address_t *daddr = &sel->daddr;
|
||||
const xfrm_address_t *saddr = &sel->saddr;
|
||||
unsigned int h = 0;
|
||||
|
||||
switch (family) {
|
||||
case AF_INET:
|
||||
if (sel->prefixlen_d < dbits ||
|
||||
sel->prefixlen_s < sbits)
|
||||
return hmask + 1;
|
||||
|
||||
h = __xfrm4_dpref_spref_hash(daddr, saddr, dbits, sbits);
|
||||
break;
|
||||
|
||||
case AF_INET6:
|
||||
if (sel->prefixlen_d < dbits ||
|
||||
sel->prefixlen_s < sbits)
|
||||
return hmask + 1;
|
||||
|
||||
h = __xfrm6_dpref_spref_hash(daddr, saddr, dbits, sbits);
|
||||
break;
|
||||
}
|
||||
h ^= (h >> 16);
|
||||
return h & hmask;
|
||||
}
|
||||
|
||||
static inline unsigned int __addr_hash(const xfrm_address_t *daddr,
|
||||
const xfrm_address_t *saddr,
|
||||
unsigned short family,
|
||||
unsigned int hmask,
|
||||
u8 dbits, u8 sbits)
|
||||
{
|
||||
unsigned int h = 0;
|
||||
|
||||
switch (family) {
|
||||
case AF_INET:
|
||||
h = __xfrm4_dpref_spref_hash(daddr, saddr, dbits, sbits);
|
||||
break;
|
||||
|
||||
case AF_INET6:
|
||||
h = __xfrm6_dpref_spref_hash(daddr, saddr, dbits, sbits);
|
||||
break;
|
||||
}
|
||||
h ^= (h >> 16);
|
||||
return h & hmask;
|
||||
}
|
||||
|
||||
struct hlist_head *xfrm_hash_alloc(unsigned int sz);
|
||||
void xfrm_hash_free(struct hlist_head *n, unsigned int sz);
|
||||
|
||||
#endif /* _XFRM_HASH_H */
|
381
net/xfrm/xfrm_input.c
Normal file
381
net/xfrm/xfrm_input.c
Normal file
|
@ -0,0 +1,381 @@
|
|||
/*
|
||||
* xfrm_input.c
|
||||
*
|
||||
* Changes:
|
||||
* YOSHIFUJI Hideaki @USAGI
|
||||
* Split up af-specific portion
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <net/dst.h>
|
||||
#include <net/ip.h>
|
||||
#include <net/xfrm.h>
|
||||
|
||||
static struct kmem_cache *secpath_cachep __read_mostly;
|
||||
|
||||
static DEFINE_SPINLOCK(xfrm_input_afinfo_lock);
|
||||
static struct xfrm_input_afinfo __rcu *xfrm_input_afinfo[NPROTO];
|
||||
|
||||
int xfrm_input_register_afinfo(struct xfrm_input_afinfo *afinfo)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
if (unlikely(afinfo == NULL))
|
||||
return -EINVAL;
|
||||
if (unlikely(afinfo->family >= NPROTO))
|
||||
return -EAFNOSUPPORT;
|
||||
spin_lock_bh(&xfrm_input_afinfo_lock);
|
||||
if (unlikely(xfrm_input_afinfo[afinfo->family] != NULL))
|
||||
err = -ENOBUFS;
|
||||
else
|
||||
rcu_assign_pointer(xfrm_input_afinfo[afinfo->family], afinfo);
|
||||
spin_unlock_bh(&xfrm_input_afinfo_lock);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(xfrm_input_register_afinfo);
|
||||
|
||||
int xfrm_input_unregister_afinfo(struct xfrm_input_afinfo *afinfo)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
if (unlikely(afinfo == NULL))
|
||||
return -EINVAL;
|
||||
if (unlikely(afinfo->family >= NPROTO))
|
||||
return -EAFNOSUPPORT;
|
||||
spin_lock_bh(&xfrm_input_afinfo_lock);
|
||||
if (likely(xfrm_input_afinfo[afinfo->family] != NULL)) {
|
||||
if (unlikely(xfrm_input_afinfo[afinfo->family] != afinfo))
|
||||
err = -EINVAL;
|
||||
else
|
||||
RCU_INIT_POINTER(xfrm_input_afinfo[afinfo->family], NULL);
|
||||
}
|
||||
spin_unlock_bh(&xfrm_input_afinfo_lock);
|
||||
synchronize_rcu();
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(xfrm_input_unregister_afinfo);
|
||||
|
||||
static struct xfrm_input_afinfo *xfrm_input_get_afinfo(unsigned int family)
|
||||
{
|
||||
struct xfrm_input_afinfo *afinfo;
|
||||
|
||||
if (unlikely(family >= NPROTO))
|
||||
return NULL;
|
||||
rcu_read_lock();
|
||||
afinfo = rcu_dereference(xfrm_input_afinfo[family]);
|
||||
if (unlikely(!afinfo))
|
||||
rcu_read_unlock();
|
||||
return afinfo;
|
||||
}
|
||||
|
||||
static void xfrm_input_put_afinfo(struct xfrm_input_afinfo *afinfo)
|
||||
{
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
static int xfrm_rcv_cb(struct sk_buff *skb, unsigned int family, u8 protocol,
|
||||
int err)
|
||||
{
|
||||
int ret;
|
||||
struct xfrm_input_afinfo *afinfo = xfrm_input_get_afinfo(family);
|
||||
|
||||
if (!afinfo)
|
||||
return -EAFNOSUPPORT;
|
||||
|
||||
ret = afinfo->callback(skb, protocol, err);
|
||||
xfrm_input_put_afinfo(afinfo);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void __secpath_destroy(struct sec_path *sp)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < sp->len; i++)
|
||||
xfrm_state_put(sp->xvec[i]);
|
||||
kmem_cache_free(secpath_cachep, sp);
|
||||
}
|
||||
EXPORT_SYMBOL(__secpath_destroy);
|
||||
|
||||
struct sec_path *secpath_dup(struct sec_path *src)
|
||||
{
|
||||
struct sec_path *sp;
|
||||
|
||||
sp = kmem_cache_alloc(secpath_cachep, GFP_ATOMIC);
|
||||
if (!sp)
|
||||
return NULL;
|
||||
|
||||
sp->len = 0;
|
||||
if (src) {
|
||||
int i;
|
||||
|
||||
memcpy(sp, src, sizeof(*sp));
|
||||
for (i = 0; i < sp->len; i++)
|
||||
xfrm_state_hold(sp->xvec[i]);
|
||||
}
|
||||
atomic_set(&sp->refcnt, 1);
|
||||
return sp;
|
||||
}
|
||||
EXPORT_SYMBOL(secpath_dup);
|
||||
|
||||
/* Fetch spi and seq from ipsec header */
|
||||
|
||||
int xfrm_parse_spi(struct sk_buff *skb, u8 nexthdr, __be32 *spi, __be32 *seq)
|
||||
{
|
||||
int offset, offset_seq;
|
||||
int hlen;
|
||||
|
||||
switch (nexthdr) {
|
||||
case IPPROTO_AH:
|
||||
hlen = sizeof(struct ip_auth_hdr);
|
||||
offset = offsetof(struct ip_auth_hdr, spi);
|
||||
offset_seq = offsetof(struct ip_auth_hdr, seq_no);
|
||||
break;
|
||||
case IPPROTO_ESP:
|
||||
hlen = sizeof(struct ip_esp_hdr);
|
||||
offset = offsetof(struct ip_esp_hdr, spi);
|
||||
offset_seq = offsetof(struct ip_esp_hdr, seq_no);
|
||||
break;
|
||||
case IPPROTO_COMP:
|
||||
if (!pskb_may_pull(skb, sizeof(struct ip_comp_hdr)))
|
||||
return -EINVAL;
|
||||
*spi = htonl(ntohs(*(__be16 *)(skb_transport_header(skb) + 2)));
|
||||
*seq = 0;
|
||||
return 0;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!pskb_may_pull(skb, hlen))
|
||||
return -EINVAL;
|
||||
|
||||
*spi = *(__be32 *)(skb_transport_header(skb) + offset);
|
||||
*seq = *(__be32 *)(skb_transport_header(skb) + offset_seq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int xfrm_prepare_input(struct xfrm_state *x, struct sk_buff *skb)
|
||||
{
|
||||
struct xfrm_mode *inner_mode = x->inner_mode;
|
||||
int err;
|
||||
|
||||
err = x->outer_mode->afinfo->extract_input(x, skb);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (x->sel.family == AF_UNSPEC) {
|
||||
inner_mode = xfrm_ip2inner_mode(x, XFRM_MODE_SKB_CB(skb)->protocol);
|
||||
if (inner_mode == NULL)
|
||||
return -EAFNOSUPPORT;
|
||||
}
|
||||
|
||||
skb->protocol = inner_mode->afinfo->eth_proto;
|
||||
return inner_mode->input2(x, skb);
|
||||
}
|
||||
EXPORT_SYMBOL(xfrm_prepare_input);
|
||||
|
||||
int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
|
||||
{
|
||||
struct net *net = dev_net(skb->dev);
|
||||
int err;
|
||||
__be32 seq;
|
||||
__be32 seq_hi;
|
||||
struct xfrm_state *x = NULL;
|
||||
xfrm_address_t *daddr;
|
||||
struct xfrm_mode *inner_mode;
|
||||
unsigned int family;
|
||||
int decaps = 0;
|
||||
int async = 0;
|
||||
|
||||
/* A negative encap_type indicates async resumption. */
|
||||
if (encap_type < 0) {
|
||||
async = 1;
|
||||
x = xfrm_input_state(skb);
|
||||
seq = XFRM_SKB_CB(skb)->seq.input.low;
|
||||
family = x->outer_mode->afinfo->family;
|
||||
goto resume;
|
||||
}
|
||||
|
||||
daddr = (xfrm_address_t *)(skb_network_header(skb) +
|
||||
XFRM_SPI_SKB_CB(skb)->daddroff);
|
||||
family = XFRM_SPI_SKB_CB(skb)->family;
|
||||
|
||||
/* Allocate new secpath or COW existing one. */
|
||||
if (!skb->sp || atomic_read(&skb->sp->refcnt) != 1) {
|
||||
struct sec_path *sp;
|
||||
|
||||
sp = secpath_dup(skb->sp);
|
||||
if (!sp) {
|
||||
XFRM_INC_STATS(net, LINUX_MIB_XFRMINERROR);
|
||||
goto drop;
|
||||
}
|
||||
if (skb->sp)
|
||||
secpath_put(skb->sp);
|
||||
skb->sp = sp;
|
||||
}
|
||||
|
||||
seq = 0;
|
||||
if (!spi && (err = xfrm_parse_spi(skb, nexthdr, &spi, &seq)) != 0) {
|
||||
XFRM_INC_STATS(net, LINUX_MIB_XFRMINHDRERROR);
|
||||
goto drop;
|
||||
}
|
||||
|
||||
do {
|
||||
if (skb->sp->len == XFRM_MAX_DEPTH) {
|
||||
XFRM_INC_STATS(net, LINUX_MIB_XFRMINBUFFERERROR);
|
||||
goto drop;
|
||||
}
|
||||
|
||||
x = xfrm_state_lookup(net, skb->mark, daddr, spi, nexthdr, family);
|
||||
if (x == NULL) {
|
||||
XFRM_INC_STATS(net, LINUX_MIB_XFRMINNOSTATES);
|
||||
xfrm_audit_state_notfound(skb, family, spi, seq);
|
||||
goto drop;
|
||||
}
|
||||
|
||||
skb->sp->xvec[skb->sp->len++] = x;
|
||||
|
||||
if (xfrm_tunnel_check(skb, x, family)) {
|
||||
XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEMODEERROR);
|
||||
goto drop;
|
||||
}
|
||||
|
||||
spin_lock(&x->lock);
|
||||
if (unlikely(x->km.state == XFRM_STATE_ACQ)) {
|
||||
XFRM_INC_STATS(net, LINUX_MIB_XFRMACQUIREERROR);
|
||||
goto drop_unlock;
|
||||
}
|
||||
|
||||
if (unlikely(x->km.state != XFRM_STATE_VALID)) {
|
||||
XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEINVALID);
|
||||
goto drop_unlock;
|
||||
}
|
||||
|
||||
if ((x->encap ? x->encap->encap_type : 0) != encap_type) {
|
||||
XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEMISMATCH);
|
||||
goto drop_unlock;
|
||||
}
|
||||
|
||||
if (x->repl->check(x, skb, seq)) {
|
||||
XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATESEQERROR);
|
||||
goto drop_unlock;
|
||||
}
|
||||
|
||||
if (xfrm_state_check_expire(x)) {
|
||||
XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEEXPIRED);
|
||||
goto drop_unlock;
|
||||
}
|
||||
|
||||
spin_unlock(&x->lock);
|
||||
|
||||
seq_hi = htonl(xfrm_replay_seqhi(x, seq));
|
||||
|
||||
XFRM_SKB_CB(skb)->seq.input.low = seq;
|
||||
XFRM_SKB_CB(skb)->seq.input.hi = seq_hi;
|
||||
|
||||
skb_dst_force(skb);
|
||||
|
||||
nexthdr = x->type->input(x, skb);
|
||||
|
||||
if (nexthdr == -EINPROGRESS)
|
||||
return 0;
|
||||
resume:
|
||||
spin_lock(&x->lock);
|
||||
if (nexthdr <= 0) {
|
||||
if (nexthdr == -EBADMSG) {
|
||||
xfrm_audit_state_icvfail(x, skb,
|
||||
x->type->proto);
|
||||
x->stats.integrity_failed++;
|
||||
}
|
||||
XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEPROTOERROR);
|
||||
goto drop_unlock;
|
||||
}
|
||||
|
||||
/* only the first xfrm gets the encap type */
|
||||
encap_type = 0;
|
||||
|
||||
if (async && x->repl->recheck(x, skb, seq)) {
|
||||
XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATESEQERROR);
|
||||
goto drop_unlock;
|
||||
}
|
||||
|
||||
x->repl->advance(x, seq);
|
||||
|
||||
x->curlft.bytes += skb->len;
|
||||
x->curlft.packets++;
|
||||
|
||||
spin_unlock(&x->lock);
|
||||
|
||||
XFRM_MODE_SKB_CB(skb)->protocol = nexthdr;
|
||||
|
||||
inner_mode = x->inner_mode;
|
||||
|
||||
if (x->sel.family == AF_UNSPEC) {
|
||||
inner_mode = xfrm_ip2inner_mode(x, XFRM_MODE_SKB_CB(skb)->protocol);
|
||||
if (inner_mode == NULL)
|
||||
goto drop;
|
||||
}
|
||||
|
||||
if (inner_mode->input(x, skb)) {
|
||||
XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEMODEERROR);
|
||||
goto drop;
|
||||
}
|
||||
|
||||
if (x->outer_mode->flags & XFRM_MODE_FLAG_TUNNEL) {
|
||||
decaps = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* We need the inner address. However, we only get here for
|
||||
* transport mode so the outer address is identical.
|
||||
*/
|
||||
daddr = &x->id.daddr;
|
||||
family = x->outer_mode->afinfo->family;
|
||||
|
||||
err = xfrm_parse_spi(skb, nexthdr, &spi, &seq);
|
||||
if (err < 0) {
|
||||
XFRM_INC_STATS(net, LINUX_MIB_XFRMINHDRERROR);
|
||||
goto drop;
|
||||
}
|
||||
} while (!err);
|
||||
|
||||
err = xfrm_rcv_cb(skb, family, x->type->proto, 0);
|
||||
if (err)
|
||||
goto drop;
|
||||
|
||||
nf_reset(skb);
|
||||
|
||||
if (decaps) {
|
||||
skb_dst_drop(skb);
|
||||
netif_rx(skb);
|
||||
return 0;
|
||||
} else {
|
||||
return x->inner_mode->afinfo->transport_finish(skb, async);
|
||||
}
|
||||
|
||||
drop_unlock:
|
||||
spin_unlock(&x->lock);
|
||||
drop:
|
||||
xfrm_rcv_cb(skb, family, x && x->type ? x->type->proto : nexthdr, -1);
|
||||
kfree_skb(skb);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(xfrm_input);
|
||||
|
||||
int xfrm_input_resume(struct sk_buff *skb, int nexthdr)
|
||||
{
|
||||
return xfrm_input(skb, nexthdr, 0, -1);
|
||||
}
|
||||
EXPORT_SYMBOL(xfrm_input_resume);
|
||||
|
||||
void __init xfrm_input_init(void)
|
||||
{
|
||||
secpath_cachep = kmem_cache_create("secpath_cache",
|
||||
sizeof(struct sec_path),
|
||||
0, SLAB_HWCACHE_ALIGN|SLAB_PANIC,
|
||||
NULL);
|
||||
}
|
386
net/xfrm/xfrm_ipcomp.c
Normal file
386
net/xfrm/xfrm_ipcomp.c
Normal file
|
@ -0,0 +1,386 @@
|
|||
/*
|
||||
* IP Payload Compression Protocol (IPComp) - RFC3173.
|
||||
*
|
||||
* Copyright (c) 2003 James Morris <jmorris@intercode.com.au>
|
||||
* Copyright (c) 2003-2008 Herbert Xu <herbert@gondor.apana.org.au>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* Todo:
|
||||
* - Tunable compression parameters.
|
||||
* - Compression stats.
|
||||
* - Adaptive compression.
|
||||
*/
|
||||
|
||||
#include <linux/crypto.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/percpu.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <net/ip.h>
|
||||
#include <net/ipcomp.h>
|
||||
#include <net/xfrm.h>
|
||||
|
||||
struct ipcomp_tfms {
|
||||
struct list_head list;
|
||||
struct crypto_comp * __percpu *tfms;
|
||||
int users;
|
||||
};
|
||||
|
||||
static DEFINE_MUTEX(ipcomp_resource_mutex);
|
||||
static void * __percpu *ipcomp_scratches;
|
||||
static int ipcomp_scratch_users;
|
||||
static LIST_HEAD(ipcomp_tfms_list);
|
||||
|
||||
static int ipcomp_decompress(struct xfrm_state *x, struct sk_buff *skb)
|
||||
{
|
||||
struct ipcomp_data *ipcd = x->data;
|
||||
const int plen = skb->len;
|
||||
int dlen = IPCOMP_SCRATCH_SIZE;
|
||||
const u8 *start = skb->data;
|
||||
const int cpu = get_cpu();
|
||||
u8 *scratch = *per_cpu_ptr(ipcomp_scratches, cpu);
|
||||
struct crypto_comp *tfm = *per_cpu_ptr(ipcd->tfms, cpu);
|
||||
int err = crypto_comp_decompress(tfm, start, plen, scratch, &dlen);
|
||||
int len;
|
||||
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
if (dlen < (plen + sizeof(struct ip_comp_hdr))) {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
len = dlen - plen;
|
||||
if (len > skb_tailroom(skb))
|
||||
len = skb_tailroom(skb);
|
||||
|
||||
__skb_put(skb, len);
|
||||
|
||||
len += plen;
|
||||
skb_copy_to_linear_data(skb, scratch, len);
|
||||
|
||||
while ((scratch += len, dlen -= len) > 0) {
|
||||
skb_frag_t *frag;
|
||||
struct page *page;
|
||||
|
||||
err = -EMSGSIZE;
|
||||
if (WARN_ON(skb_shinfo(skb)->nr_frags >= MAX_SKB_FRAGS))
|
||||
goto out;
|
||||
|
||||
frag = skb_shinfo(skb)->frags + skb_shinfo(skb)->nr_frags;
|
||||
page = alloc_page(GFP_ATOMIC);
|
||||
|
||||
err = -ENOMEM;
|
||||
if (!page)
|
||||
goto out;
|
||||
|
||||
__skb_frag_set_page(frag, page);
|
||||
|
||||
len = PAGE_SIZE;
|
||||
if (dlen < len)
|
||||
len = dlen;
|
||||
|
||||
frag->page_offset = 0;
|
||||
skb_frag_size_set(frag, len);
|
||||
memcpy(skb_frag_address(frag), scratch, len);
|
||||
|
||||
skb->truesize += len;
|
||||
skb->data_len += len;
|
||||
skb->len += len;
|
||||
|
||||
skb_shinfo(skb)->nr_frags++;
|
||||
}
|
||||
|
||||
err = 0;
|
||||
|
||||
out:
|
||||
put_cpu();
|
||||
return err;
|
||||
}
|
||||
|
||||
int ipcomp_input(struct xfrm_state *x, struct sk_buff *skb)
|
||||
{
|
||||
int nexthdr;
|
||||
int err = -ENOMEM;
|
||||
struct ip_comp_hdr *ipch;
|
||||
|
||||
if (skb_linearize_cow(skb))
|
||||
goto out;
|
||||
|
||||
skb->ip_summed = CHECKSUM_NONE;
|
||||
|
||||
/* Remove ipcomp header and decompress original payload */
|
||||
ipch = (void *)skb->data;
|
||||
nexthdr = ipch->nexthdr;
|
||||
|
||||
skb->transport_header = skb->network_header + sizeof(*ipch);
|
||||
__skb_pull(skb, sizeof(*ipch));
|
||||
err = ipcomp_decompress(x, skb);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = nexthdr;
|
||||
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ipcomp_input);
|
||||
|
||||
static int ipcomp_compress(struct xfrm_state *x, struct sk_buff *skb)
|
||||
{
|
||||
struct ipcomp_data *ipcd = x->data;
|
||||
const int plen = skb->len;
|
||||
int dlen = IPCOMP_SCRATCH_SIZE;
|
||||
u8 *start = skb->data;
|
||||
struct crypto_comp *tfm;
|
||||
u8 *scratch;
|
||||
int err;
|
||||
|
||||
local_bh_disable();
|
||||
scratch = *this_cpu_ptr(ipcomp_scratches);
|
||||
tfm = *this_cpu_ptr(ipcd->tfms);
|
||||
err = crypto_comp_compress(tfm, start, plen, scratch, &dlen);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
if ((dlen + sizeof(struct ip_comp_hdr)) >= plen) {
|
||||
err = -EMSGSIZE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
memcpy(start + sizeof(struct ip_comp_hdr), scratch, dlen);
|
||||
local_bh_enable();
|
||||
|
||||
pskb_trim(skb, dlen + sizeof(struct ip_comp_hdr));
|
||||
return 0;
|
||||
|
||||
out:
|
||||
local_bh_enable();
|
||||
return err;
|
||||
}
|
||||
|
||||
int ipcomp_output(struct xfrm_state *x, struct sk_buff *skb)
|
||||
{
|
||||
int err;
|
||||
struct ip_comp_hdr *ipch;
|
||||
struct ipcomp_data *ipcd = x->data;
|
||||
|
||||
if (skb->len < ipcd->threshold) {
|
||||
/* Don't bother compressing */
|
||||
goto out_ok;
|
||||
}
|
||||
|
||||
if (skb_linearize_cow(skb))
|
||||
goto out_ok;
|
||||
|
||||
err = ipcomp_compress(x, skb);
|
||||
|
||||
if (err) {
|
||||
goto out_ok;
|
||||
}
|
||||
|
||||
/* Install ipcomp header, convert into ipcomp datagram. */
|
||||
ipch = ip_comp_hdr(skb);
|
||||
ipch->nexthdr = *skb_mac_header(skb);
|
||||
ipch->flags = 0;
|
||||
ipch->cpi = htons((u16 )ntohl(x->id.spi));
|
||||
*skb_mac_header(skb) = IPPROTO_COMP;
|
||||
out_ok:
|
||||
skb_push(skb, -skb_network_offset(skb));
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ipcomp_output);
|
||||
|
||||
static void ipcomp_free_scratches(void)
|
||||
{
|
||||
int i;
|
||||
void * __percpu *scratches;
|
||||
|
||||
if (--ipcomp_scratch_users)
|
||||
return;
|
||||
|
||||
scratches = ipcomp_scratches;
|
||||
if (!scratches)
|
||||
return;
|
||||
|
||||
for_each_possible_cpu(i)
|
||||
vfree(*per_cpu_ptr(scratches, i));
|
||||
|
||||
free_percpu(scratches);
|
||||
}
|
||||
|
||||
static void * __percpu *ipcomp_alloc_scratches(void)
|
||||
{
|
||||
void * __percpu *scratches;
|
||||
int i;
|
||||
|
||||
if (ipcomp_scratch_users++)
|
||||
return ipcomp_scratches;
|
||||
|
||||
scratches = alloc_percpu(void *);
|
||||
if (!scratches)
|
||||
return NULL;
|
||||
|
||||
ipcomp_scratches = scratches;
|
||||
|
||||
for_each_possible_cpu(i) {
|
||||
void *scratch;
|
||||
|
||||
scratch = vmalloc_node(IPCOMP_SCRATCH_SIZE, cpu_to_node(i));
|
||||
if (!scratch)
|
||||
return NULL;
|
||||
*per_cpu_ptr(scratches, i) = scratch;
|
||||
}
|
||||
|
||||
return scratches;
|
||||
}
|
||||
|
||||
static void ipcomp_free_tfms(struct crypto_comp * __percpu *tfms)
|
||||
{
|
||||
struct ipcomp_tfms *pos;
|
||||
int cpu;
|
||||
|
||||
list_for_each_entry(pos, &ipcomp_tfms_list, list) {
|
||||
if (pos->tfms == tfms)
|
||||
break;
|
||||
}
|
||||
|
||||
WARN_ON(!pos);
|
||||
|
||||
if (--pos->users)
|
||||
return;
|
||||
|
||||
list_del(&pos->list);
|
||||
kfree(pos);
|
||||
|
||||
if (!tfms)
|
||||
return;
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
struct crypto_comp *tfm = *per_cpu_ptr(tfms, cpu);
|
||||
crypto_free_comp(tfm);
|
||||
}
|
||||
free_percpu(tfms);
|
||||
}
|
||||
|
||||
static struct crypto_comp * __percpu *ipcomp_alloc_tfms(const char *alg_name)
|
||||
{
|
||||
struct ipcomp_tfms *pos;
|
||||
struct crypto_comp * __percpu *tfms;
|
||||
int cpu;
|
||||
|
||||
|
||||
list_for_each_entry(pos, &ipcomp_tfms_list, list) {
|
||||
struct crypto_comp *tfm;
|
||||
|
||||
/* This can be any valid CPU ID so we don't need locking. */
|
||||
tfm = __this_cpu_read(*pos->tfms);
|
||||
|
||||
if (!strcmp(crypto_comp_name(tfm), alg_name)) {
|
||||
pos->users++;
|
||||
return pos->tfms;
|
||||
}
|
||||
}
|
||||
|
||||
pos = kmalloc(sizeof(*pos), GFP_KERNEL);
|
||||
if (!pos)
|
||||
return NULL;
|
||||
|
||||
pos->users = 1;
|
||||
INIT_LIST_HEAD(&pos->list);
|
||||
list_add(&pos->list, &ipcomp_tfms_list);
|
||||
|
||||
pos->tfms = tfms = alloc_percpu(struct crypto_comp *);
|
||||
if (!tfms)
|
||||
goto error;
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
struct crypto_comp *tfm = crypto_alloc_comp(alg_name, 0,
|
||||
CRYPTO_ALG_ASYNC);
|
||||
if (IS_ERR(tfm))
|
||||
goto error;
|
||||
*per_cpu_ptr(tfms, cpu) = tfm;
|
||||
}
|
||||
|
||||
return tfms;
|
||||
|
||||
error:
|
||||
ipcomp_free_tfms(tfms);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void ipcomp_free_data(struct ipcomp_data *ipcd)
|
||||
{
|
||||
if (ipcd->tfms)
|
||||
ipcomp_free_tfms(ipcd->tfms);
|
||||
ipcomp_free_scratches();
|
||||
}
|
||||
|
||||
void ipcomp_destroy(struct xfrm_state *x)
|
||||
{
|
||||
struct ipcomp_data *ipcd = x->data;
|
||||
if (!ipcd)
|
||||
return;
|
||||
xfrm_state_delete_tunnel(x);
|
||||
mutex_lock(&ipcomp_resource_mutex);
|
||||
ipcomp_free_data(ipcd);
|
||||
mutex_unlock(&ipcomp_resource_mutex);
|
||||
kfree(ipcd);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ipcomp_destroy);
|
||||
|
||||
int ipcomp_init_state(struct xfrm_state *x)
|
||||
{
|
||||
int err;
|
||||
struct ipcomp_data *ipcd;
|
||||
struct xfrm_algo_desc *calg_desc;
|
||||
|
||||
err = -EINVAL;
|
||||
if (!x->calg)
|
||||
goto out;
|
||||
|
||||
if (x->encap)
|
||||
goto out;
|
||||
|
||||
err = -ENOMEM;
|
||||
ipcd = kzalloc(sizeof(*ipcd), GFP_KERNEL);
|
||||
if (!ipcd)
|
||||
goto out;
|
||||
|
||||
mutex_lock(&ipcomp_resource_mutex);
|
||||
if (!ipcomp_alloc_scratches())
|
||||
goto error;
|
||||
|
||||
ipcd->tfms = ipcomp_alloc_tfms(x->calg->alg_name);
|
||||
if (!ipcd->tfms)
|
||||
goto error;
|
||||
mutex_unlock(&ipcomp_resource_mutex);
|
||||
|
||||
calg_desc = xfrm_calg_get_byname(x->calg->alg_name, 0);
|
||||
BUG_ON(!calg_desc);
|
||||
ipcd->threshold = calg_desc->uinfo.comp.threshold;
|
||||
x->data = ipcd;
|
||||
err = 0;
|
||||
out:
|
||||
return err;
|
||||
|
||||
error:
|
||||
ipcomp_free_data(ipcd);
|
||||
mutex_unlock(&ipcomp_resource_mutex);
|
||||
kfree(ipcd);
|
||||
goto out;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ipcomp_init_state);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("IP Payload Compression Protocol (IPComp) - RFC3173");
|
||||
MODULE_AUTHOR("James Morris <jmorris@intercode.com.au>");
|
236
net/xfrm/xfrm_output.c
Normal file
236
net/xfrm/xfrm_output.c
Normal file
|
@ -0,0 +1,236 @@
|
|||
/*
|
||||
* xfrm_output.c - Common IPsec encapsulation code.
|
||||
*
|
||||
* Copyright (c) 2007 Herbert Xu <herbert@gondor.apana.org.au>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/netfilter.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <net/dst.h>
|
||||
#include <net/xfrm.h>
|
||||
|
||||
static int xfrm_output2(struct sk_buff *skb);
|
||||
|
||||
static int xfrm_skb_check_space(struct sk_buff *skb)
|
||||
{
|
||||
struct dst_entry *dst = skb_dst(skb);
|
||||
int nhead = dst->header_len + LL_RESERVED_SPACE(dst->dev)
|
||||
- skb_headroom(skb);
|
||||
int ntail = dst->dev->needed_tailroom - skb_tailroom(skb);
|
||||
|
||||
if (nhead <= 0) {
|
||||
if (ntail <= 0)
|
||||
return 0;
|
||||
nhead = 0;
|
||||
} else if (ntail < 0)
|
||||
ntail = 0;
|
||||
|
||||
return pskb_expand_head(skb, nhead, ntail, GFP_ATOMIC);
|
||||
}
|
||||
|
||||
static int xfrm_output_one(struct sk_buff *skb, int err)
|
||||
{
|
||||
struct dst_entry *dst = skb_dst(skb);
|
||||
struct xfrm_state *x = dst->xfrm;
|
||||
struct net *net = xs_net(x);
|
||||
|
||||
if (err <= 0)
|
||||
goto resume;
|
||||
|
||||
do {
|
||||
err = xfrm_skb_check_space(skb);
|
||||
if (err) {
|
||||
XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTERROR);
|
||||
goto error_nolock;
|
||||
}
|
||||
|
||||
err = x->outer_mode->output(x, skb);
|
||||
if (err) {
|
||||
XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTSTATEMODEERROR);
|
||||
goto error_nolock;
|
||||
}
|
||||
|
||||
spin_lock_bh(&x->lock);
|
||||
|
||||
if (unlikely(x->km.state != XFRM_STATE_VALID)) {
|
||||
XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTSTATEINVALID);
|
||||
err = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
err = xfrm_state_check_expire(x);
|
||||
if (err) {
|
||||
XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTSTATEEXPIRED);
|
||||
goto error;
|
||||
}
|
||||
|
||||
err = x->repl->overflow(x, skb);
|
||||
if (err) {
|
||||
XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTSTATESEQERROR);
|
||||
goto error;
|
||||
}
|
||||
|
||||
x->curlft.bytes += skb->len;
|
||||
x->curlft.packets++;
|
||||
|
||||
spin_unlock_bh(&x->lock);
|
||||
|
||||
skb_dst_force(skb);
|
||||
|
||||
err = x->type->output(x, skb);
|
||||
if (err == -EINPROGRESS)
|
||||
goto out;
|
||||
|
||||
resume:
|
||||
if (err) {
|
||||
XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTSTATEPROTOERROR);
|
||||
goto error_nolock;
|
||||
}
|
||||
|
||||
dst = skb_dst_pop(skb);
|
||||
if (!dst) {
|
||||
XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTERROR);
|
||||
err = -EHOSTUNREACH;
|
||||
goto error_nolock;
|
||||
}
|
||||
skb_dst_set(skb, dst);
|
||||
x = dst->xfrm;
|
||||
} while (x && !(x->outer_mode->flags & XFRM_MODE_FLAG_TUNNEL));
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
spin_unlock_bh(&x->lock);
|
||||
error_nolock:
|
||||
kfree_skb(skb);
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
int xfrm_output_resume(struct sk_buff *skb, int err)
|
||||
{
|
||||
while (likely((err = xfrm_output_one(skb, err)) == 0)) {
|
||||
nf_reset(skb);
|
||||
|
||||
err = skb_dst(skb)->ops->local_out(skb);
|
||||
if (unlikely(err != 1))
|
||||
goto out;
|
||||
|
||||
if (!skb_dst(skb)->xfrm)
|
||||
return dst_output(skb);
|
||||
|
||||
err = nf_hook(skb_dst(skb)->ops->family,
|
||||
NF_INET_POST_ROUTING, skb,
|
||||
NULL, skb_dst(skb)->dev, xfrm_output2);
|
||||
if (unlikely(err != 1))
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (err == -EINPROGRESS)
|
||||
err = 0;
|
||||
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xfrm_output_resume);
|
||||
|
||||
static int xfrm_output2(struct sk_buff *skb)
|
||||
{
|
||||
return xfrm_output_resume(skb, 1);
|
||||
}
|
||||
|
||||
static int xfrm_output_gso(struct sk_buff *skb)
|
||||
{
|
||||
struct sk_buff *segs;
|
||||
|
||||
segs = skb_gso_segment(skb, 0);
|
||||
kfree_skb(skb);
|
||||
if (IS_ERR(segs))
|
||||
return PTR_ERR(segs);
|
||||
if (segs == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
do {
|
||||
struct sk_buff *nskb = segs->next;
|
||||
int err;
|
||||
|
||||
segs->next = NULL;
|
||||
err = xfrm_output2(segs);
|
||||
|
||||
if (unlikely(err)) {
|
||||
kfree_skb_list(nskb);
|
||||
return err;
|
||||
}
|
||||
|
||||
segs = nskb;
|
||||
} while (segs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int xfrm_output(struct sk_buff *skb)
|
||||
{
|
||||
struct net *net = dev_net(skb_dst(skb)->dev);
|
||||
int err;
|
||||
|
||||
if (skb_is_gso(skb))
|
||||
return xfrm_output_gso(skb);
|
||||
|
||||
if (skb->ip_summed == CHECKSUM_PARTIAL) {
|
||||
err = skb_checksum_help(skb);
|
||||
if (err) {
|
||||
XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTERROR);
|
||||
kfree_skb(skb);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return xfrm_output2(skb);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xfrm_output);
|
||||
|
||||
int xfrm_inner_extract_output(struct xfrm_state *x, struct sk_buff *skb)
|
||||
{
|
||||
struct xfrm_mode *inner_mode;
|
||||
if (x->sel.family == AF_UNSPEC)
|
||||
inner_mode = xfrm_ip2inner_mode(x,
|
||||
xfrm_af2proto(skb_dst(skb)->ops->family));
|
||||
else
|
||||
inner_mode = x->inner_mode;
|
||||
|
||||
if (inner_mode == NULL)
|
||||
return -EAFNOSUPPORT;
|
||||
return inner_mode->afinfo->extract_output(x, skb);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xfrm_inner_extract_output);
|
||||
|
||||
void xfrm_local_error(struct sk_buff *skb, int mtu)
|
||||
{
|
||||
unsigned int proto;
|
||||
struct xfrm_state_afinfo *afinfo;
|
||||
|
||||
if (skb->protocol == htons(ETH_P_IP))
|
||||
proto = AF_INET;
|
||||
else if (skb->protocol == htons(ETH_P_IPV6))
|
||||
proto = AF_INET6;
|
||||
else
|
||||
return;
|
||||
|
||||
afinfo = xfrm_state_get_afinfo(proto);
|
||||
if (!afinfo)
|
||||
return;
|
||||
|
||||
afinfo->local_error(skb, mtu);
|
||||
xfrm_state_put_afinfo(afinfo);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xfrm_local_error);
|
3391
net/xfrm/xfrm_policy.c
Normal file
3391
net/xfrm/xfrm_policy.c
Normal file
File diff suppressed because it is too large
Load diff
86
net/xfrm/xfrm_proc.c
Normal file
86
net/xfrm/xfrm_proc.c
Normal file
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* xfrm_proc.c
|
||||
*
|
||||
* Copyright (C)2006-2007 USAGI/WIDE Project
|
||||
*
|
||||
* Authors: Masahide NAKAMURA <nakam@linux-ipv6.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*/
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/export.h>
|
||||
#include <net/snmp.h>
|
||||
#include <net/xfrm.h>
|
||||
|
||||
static const struct snmp_mib xfrm_mib_list[] = {
|
||||
SNMP_MIB_ITEM("XfrmInError", LINUX_MIB_XFRMINERROR),
|
||||
SNMP_MIB_ITEM("XfrmInBufferError", LINUX_MIB_XFRMINBUFFERERROR),
|
||||
SNMP_MIB_ITEM("XfrmInHdrError", LINUX_MIB_XFRMINHDRERROR),
|
||||
SNMP_MIB_ITEM("XfrmInNoStates", LINUX_MIB_XFRMINNOSTATES),
|
||||
SNMP_MIB_ITEM("XfrmInStateProtoError", LINUX_MIB_XFRMINSTATEPROTOERROR),
|
||||
SNMP_MIB_ITEM("XfrmInStateModeError", LINUX_MIB_XFRMINSTATEMODEERROR),
|
||||
SNMP_MIB_ITEM("XfrmInStateSeqError", LINUX_MIB_XFRMINSTATESEQERROR),
|
||||
SNMP_MIB_ITEM("XfrmInStateExpired", LINUX_MIB_XFRMINSTATEEXPIRED),
|
||||
SNMP_MIB_ITEM("XfrmInStateMismatch", LINUX_MIB_XFRMINSTATEMISMATCH),
|
||||
SNMP_MIB_ITEM("XfrmInStateInvalid", LINUX_MIB_XFRMINSTATEINVALID),
|
||||
SNMP_MIB_ITEM("XfrmInTmplMismatch", LINUX_MIB_XFRMINTMPLMISMATCH),
|
||||
SNMP_MIB_ITEM("XfrmInNoPols", LINUX_MIB_XFRMINNOPOLS),
|
||||
SNMP_MIB_ITEM("XfrmInPolBlock", LINUX_MIB_XFRMINPOLBLOCK),
|
||||
SNMP_MIB_ITEM("XfrmInPolError", LINUX_MIB_XFRMINPOLERROR),
|
||||
SNMP_MIB_ITEM("XfrmOutError", LINUX_MIB_XFRMOUTERROR),
|
||||
SNMP_MIB_ITEM("XfrmOutBundleGenError", LINUX_MIB_XFRMOUTBUNDLEGENERROR),
|
||||
SNMP_MIB_ITEM("XfrmOutBundleCheckError", LINUX_MIB_XFRMOUTBUNDLECHECKERROR),
|
||||
SNMP_MIB_ITEM("XfrmOutNoStates", LINUX_MIB_XFRMOUTNOSTATES),
|
||||
SNMP_MIB_ITEM("XfrmOutStateProtoError", LINUX_MIB_XFRMOUTSTATEPROTOERROR),
|
||||
SNMP_MIB_ITEM("XfrmOutStateModeError", LINUX_MIB_XFRMOUTSTATEMODEERROR),
|
||||
SNMP_MIB_ITEM("XfrmOutStateSeqError", LINUX_MIB_XFRMOUTSTATESEQERROR),
|
||||
SNMP_MIB_ITEM("XfrmOutStateExpired", LINUX_MIB_XFRMOUTSTATEEXPIRED),
|
||||
SNMP_MIB_ITEM("XfrmOutPolBlock", LINUX_MIB_XFRMOUTPOLBLOCK),
|
||||
SNMP_MIB_ITEM("XfrmOutPolDead", LINUX_MIB_XFRMOUTPOLDEAD),
|
||||
SNMP_MIB_ITEM("XfrmOutPolError", LINUX_MIB_XFRMOUTPOLERROR),
|
||||
SNMP_MIB_ITEM("XfrmFwdHdrError", LINUX_MIB_XFRMFWDHDRERROR),
|
||||
SNMP_MIB_ITEM("XfrmOutStateInvalid", LINUX_MIB_XFRMOUTSTATEINVALID),
|
||||
SNMP_MIB_ITEM("XfrmAcquireError", LINUX_MIB_XFRMACQUIREERROR),
|
||||
SNMP_MIB_SENTINEL
|
||||
};
|
||||
|
||||
static int xfrm_statistics_seq_show(struct seq_file *seq, void *v)
|
||||
{
|
||||
struct net *net = seq->private;
|
||||
int i;
|
||||
for (i = 0; xfrm_mib_list[i].name; i++)
|
||||
seq_printf(seq, "%-24s\t%lu\n", xfrm_mib_list[i].name,
|
||||
snmp_fold_field(net->mib.xfrm_statistics,
|
||||
xfrm_mib_list[i].entry));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xfrm_statistics_seq_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open_net(inode, file, xfrm_statistics_seq_show);
|
||||
}
|
||||
|
||||
static const struct file_operations xfrm_statistics_seq_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = xfrm_statistics_seq_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release_net,
|
||||
};
|
||||
|
||||
int __net_init xfrm_proc_init(struct net *net)
|
||||
{
|
||||
if (!proc_create("xfrm_stat", S_IRUGO, net->proc_net,
|
||||
&xfrm_statistics_seq_fops))
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void xfrm_proc_fini(struct net *net)
|
||||
{
|
||||
remove_proc_entry("xfrm_stat", net->proc_net);
|
||||
}
|
603
net/xfrm/xfrm_replay.c
Normal file
603
net/xfrm/xfrm_replay.c
Normal file
|
@ -0,0 +1,603 @@
|
|||
/*
|
||||
* xfrm_replay.c - xfrm replay detection, derived from xfrm_state.c.
|
||||
*
|
||||
* Copyright (C) 2010 secunet Security Networks AG
|
||||
* Copyright (C) 2010 Steffen Klassert <steffen.klassert@secunet.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <linux/export.h>
|
||||
#include <net/xfrm.h>
|
||||
|
||||
u32 xfrm_replay_seqhi(struct xfrm_state *x, __be32 net_seq)
|
||||
{
|
||||
u32 seq, seq_hi, bottom;
|
||||
struct xfrm_replay_state_esn *replay_esn = x->replay_esn;
|
||||
|
||||
if (!(x->props.flags & XFRM_STATE_ESN))
|
||||
return 0;
|
||||
|
||||
seq = ntohl(net_seq);
|
||||
seq_hi = replay_esn->seq_hi;
|
||||
bottom = replay_esn->seq - replay_esn->replay_window + 1;
|
||||
|
||||
if (likely(replay_esn->seq >= replay_esn->replay_window - 1)) {
|
||||
/* A. same subspace */
|
||||
if (unlikely(seq < bottom))
|
||||
seq_hi++;
|
||||
} else {
|
||||
/* B. window spans two subspaces */
|
||||
if (unlikely(seq >= bottom))
|
||||
seq_hi--;
|
||||
}
|
||||
|
||||
return seq_hi;
|
||||
}
|
||||
|
||||
static void xfrm_replay_notify(struct xfrm_state *x, int event)
|
||||
{
|
||||
struct km_event c;
|
||||
/* we send notify messages in case
|
||||
* 1. we updated on of the sequence numbers, and the seqno difference
|
||||
* is at least x->replay_maxdiff, in this case we also update the
|
||||
* timeout of our timer function
|
||||
* 2. if x->replay_maxage has elapsed since last update,
|
||||
* and there were changes
|
||||
*
|
||||
* The state structure must be locked!
|
||||
*/
|
||||
|
||||
switch (event) {
|
||||
case XFRM_REPLAY_UPDATE:
|
||||
if (!x->replay_maxdiff ||
|
||||
((x->replay.seq - x->preplay.seq < x->replay_maxdiff) &&
|
||||
(x->replay.oseq - x->preplay.oseq < x->replay_maxdiff))) {
|
||||
if (x->xflags & XFRM_TIME_DEFER)
|
||||
event = XFRM_REPLAY_TIMEOUT;
|
||||
else
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case XFRM_REPLAY_TIMEOUT:
|
||||
if (memcmp(&x->replay, &x->preplay,
|
||||
sizeof(struct xfrm_replay_state)) == 0) {
|
||||
x->xflags |= XFRM_TIME_DEFER;
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
memcpy(&x->preplay, &x->replay, sizeof(struct xfrm_replay_state));
|
||||
c.event = XFRM_MSG_NEWAE;
|
||||
c.data.aevent = event;
|
||||
km_state_notify(x, &c);
|
||||
|
||||
if (x->replay_maxage &&
|
||||
!mod_timer(&x->rtimer, jiffies + x->replay_maxage))
|
||||
x->xflags &= ~XFRM_TIME_DEFER;
|
||||
}
|
||||
|
||||
static int xfrm_replay_overflow(struct xfrm_state *x, struct sk_buff *skb)
|
||||
{
|
||||
int err = 0;
|
||||
struct net *net = xs_net(x);
|
||||
|
||||
if (x->type->flags & XFRM_TYPE_REPLAY_PROT) {
|
||||
XFRM_SKB_CB(skb)->seq.output.low = ++x->replay.oseq;
|
||||
if (unlikely(x->replay.oseq == 0)) {
|
||||
x->replay.oseq--;
|
||||
xfrm_audit_state_replay_overflow(x, skb);
|
||||
err = -EOVERFLOW;
|
||||
|
||||
return err;
|
||||
}
|
||||
if (xfrm_aevent_is_on(net))
|
||||
x->repl->notify(x, XFRM_REPLAY_UPDATE);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int xfrm_replay_check(struct xfrm_state *x,
|
||||
struct sk_buff *skb, __be32 net_seq)
|
||||
{
|
||||
u32 diff;
|
||||
u32 seq = ntohl(net_seq);
|
||||
|
||||
if (!x->props.replay_window)
|
||||
return 0;
|
||||
|
||||
if (unlikely(seq == 0))
|
||||
goto err;
|
||||
|
||||
if (likely(seq > x->replay.seq))
|
||||
return 0;
|
||||
|
||||
diff = x->replay.seq - seq;
|
||||
if (diff >= x->props.replay_window) {
|
||||
x->stats.replay_window++;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (x->replay.bitmap & (1U << diff)) {
|
||||
x->stats.replay++;
|
||||
goto err;
|
||||
}
|
||||
return 0;
|
||||
|
||||
err:
|
||||
xfrm_audit_state_replay(x, skb, net_seq);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static void xfrm_replay_advance(struct xfrm_state *x, __be32 net_seq)
|
||||
{
|
||||
u32 diff;
|
||||
u32 seq = ntohl(net_seq);
|
||||
|
||||
if (!x->props.replay_window)
|
||||
return;
|
||||
|
||||
if (seq > x->replay.seq) {
|
||||
diff = seq - x->replay.seq;
|
||||
if (diff < x->props.replay_window)
|
||||
x->replay.bitmap = ((x->replay.bitmap) << diff) | 1;
|
||||
else
|
||||
x->replay.bitmap = 1;
|
||||
x->replay.seq = seq;
|
||||
} else {
|
||||
diff = x->replay.seq - seq;
|
||||
x->replay.bitmap |= (1U << diff);
|
||||
}
|
||||
|
||||
if (xfrm_aevent_is_on(xs_net(x)))
|
||||
x->repl->notify(x, XFRM_REPLAY_UPDATE);
|
||||
}
|
||||
|
||||
static int xfrm_replay_overflow_bmp(struct xfrm_state *x, struct sk_buff *skb)
|
||||
{
|
||||
int err = 0;
|
||||
struct xfrm_replay_state_esn *replay_esn = x->replay_esn;
|
||||
struct net *net = xs_net(x);
|
||||
|
||||
if (x->type->flags & XFRM_TYPE_REPLAY_PROT) {
|
||||
XFRM_SKB_CB(skb)->seq.output.low = ++replay_esn->oseq;
|
||||
if (unlikely(replay_esn->oseq == 0)) {
|
||||
replay_esn->oseq--;
|
||||
xfrm_audit_state_replay_overflow(x, skb);
|
||||
err = -EOVERFLOW;
|
||||
|
||||
return err;
|
||||
}
|
||||
if (xfrm_aevent_is_on(net))
|
||||
x->repl->notify(x, XFRM_REPLAY_UPDATE);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int xfrm_replay_check_bmp(struct xfrm_state *x,
|
||||
struct sk_buff *skb, __be32 net_seq)
|
||||
{
|
||||
unsigned int bitnr, nr;
|
||||
struct xfrm_replay_state_esn *replay_esn = x->replay_esn;
|
||||
u32 pos;
|
||||
u32 seq = ntohl(net_seq);
|
||||
u32 diff = replay_esn->seq - seq;
|
||||
|
||||
if (!replay_esn->replay_window)
|
||||
return 0;
|
||||
|
||||
if (unlikely(seq == 0))
|
||||
goto err;
|
||||
|
||||
if (likely(seq > replay_esn->seq))
|
||||
return 0;
|
||||
|
||||
if (diff >= replay_esn->replay_window) {
|
||||
x->stats.replay_window++;
|
||||
goto err;
|
||||
}
|
||||
|
||||
pos = (replay_esn->seq - 1) % replay_esn->replay_window;
|
||||
|
||||
if (pos >= diff)
|
||||
bitnr = (pos - diff) % replay_esn->replay_window;
|
||||
else
|
||||
bitnr = replay_esn->replay_window - (diff - pos);
|
||||
|
||||
nr = bitnr >> 5;
|
||||
bitnr = bitnr & 0x1F;
|
||||
if (replay_esn->bmp[nr] & (1U << bitnr))
|
||||
goto err_replay;
|
||||
|
||||
return 0;
|
||||
|
||||
err_replay:
|
||||
x->stats.replay++;
|
||||
err:
|
||||
xfrm_audit_state_replay(x, skb, net_seq);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static void xfrm_replay_advance_bmp(struct xfrm_state *x, __be32 net_seq)
|
||||
{
|
||||
unsigned int bitnr, nr, i;
|
||||
u32 diff;
|
||||
struct xfrm_replay_state_esn *replay_esn = x->replay_esn;
|
||||
u32 seq = ntohl(net_seq);
|
||||
u32 pos;
|
||||
|
||||
if (!replay_esn->replay_window)
|
||||
return;
|
||||
|
||||
pos = (replay_esn->seq - 1) % replay_esn->replay_window;
|
||||
|
||||
if (seq > replay_esn->seq) {
|
||||
diff = seq - replay_esn->seq;
|
||||
|
||||
if (diff < replay_esn->replay_window) {
|
||||
for (i = 1; i < diff; i++) {
|
||||
bitnr = (pos + i) % replay_esn->replay_window;
|
||||
nr = bitnr >> 5;
|
||||
bitnr = bitnr & 0x1F;
|
||||
replay_esn->bmp[nr] &= ~(1U << bitnr);
|
||||
}
|
||||
} else {
|
||||
nr = (replay_esn->replay_window - 1) >> 5;
|
||||
for (i = 0; i <= nr; i++)
|
||||
replay_esn->bmp[i] = 0;
|
||||
}
|
||||
|
||||
bitnr = (pos + diff) % replay_esn->replay_window;
|
||||
replay_esn->seq = seq;
|
||||
} else {
|
||||
diff = replay_esn->seq - seq;
|
||||
|
||||
if (pos >= diff)
|
||||
bitnr = (pos - diff) % replay_esn->replay_window;
|
||||
else
|
||||
bitnr = replay_esn->replay_window - (diff - pos);
|
||||
}
|
||||
|
||||
nr = bitnr >> 5;
|
||||
bitnr = bitnr & 0x1F;
|
||||
replay_esn->bmp[nr] |= (1U << bitnr);
|
||||
|
||||
if (xfrm_aevent_is_on(xs_net(x)))
|
||||
x->repl->notify(x, XFRM_REPLAY_UPDATE);
|
||||
}
|
||||
|
||||
static void xfrm_replay_notify_bmp(struct xfrm_state *x, int event)
|
||||
{
|
||||
struct km_event c;
|
||||
struct xfrm_replay_state_esn *replay_esn = x->replay_esn;
|
||||
struct xfrm_replay_state_esn *preplay_esn = x->preplay_esn;
|
||||
|
||||
/* we send notify messages in case
|
||||
* 1. we updated on of the sequence numbers, and the seqno difference
|
||||
* is at least x->replay_maxdiff, in this case we also update the
|
||||
* timeout of our timer function
|
||||
* 2. if x->replay_maxage has elapsed since last update,
|
||||
* and there were changes
|
||||
*
|
||||
* The state structure must be locked!
|
||||
*/
|
||||
|
||||
switch (event) {
|
||||
case XFRM_REPLAY_UPDATE:
|
||||
if (!x->replay_maxdiff ||
|
||||
((replay_esn->seq - preplay_esn->seq < x->replay_maxdiff) &&
|
||||
(replay_esn->oseq - preplay_esn->oseq
|
||||
< x->replay_maxdiff))) {
|
||||
if (x->xflags & XFRM_TIME_DEFER)
|
||||
event = XFRM_REPLAY_TIMEOUT;
|
||||
else
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case XFRM_REPLAY_TIMEOUT:
|
||||
if (memcmp(x->replay_esn, x->preplay_esn,
|
||||
xfrm_replay_state_esn_len(replay_esn)) == 0) {
|
||||
x->xflags |= XFRM_TIME_DEFER;
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
memcpy(x->preplay_esn, x->replay_esn,
|
||||
xfrm_replay_state_esn_len(replay_esn));
|
||||
c.event = XFRM_MSG_NEWAE;
|
||||
c.data.aevent = event;
|
||||
km_state_notify(x, &c);
|
||||
|
||||
if (x->replay_maxage &&
|
||||
!mod_timer(&x->rtimer, jiffies + x->replay_maxage))
|
||||
x->xflags &= ~XFRM_TIME_DEFER;
|
||||
}
|
||||
|
||||
static void xfrm_replay_notify_esn(struct xfrm_state *x, int event)
|
||||
{
|
||||
u32 seq_diff, oseq_diff;
|
||||
struct km_event c;
|
||||
struct xfrm_replay_state_esn *replay_esn = x->replay_esn;
|
||||
struct xfrm_replay_state_esn *preplay_esn = x->preplay_esn;
|
||||
|
||||
/* we send notify messages in case
|
||||
* 1. we updated on of the sequence numbers, and the seqno difference
|
||||
* is at least x->replay_maxdiff, in this case we also update the
|
||||
* timeout of our timer function
|
||||
* 2. if x->replay_maxage has elapsed since last update,
|
||||
* and there were changes
|
||||
*
|
||||
* The state structure must be locked!
|
||||
*/
|
||||
|
||||
switch (event) {
|
||||
case XFRM_REPLAY_UPDATE:
|
||||
if (x->replay_maxdiff) {
|
||||
if (replay_esn->seq_hi == preplay_esn->seq_hi)
|
||||
seq_diff = replay_esn->seq - preplay_esn->seq;
|
||||
else
|
||||
seq_diff = ~preplay_esn->seq + replay_esn->seq
|
||||
+ 1;
|
||||
|
||||
if (replay_esn->oseq_hi == preplay_esn->oseq_hi)
|
||||
oseq_diff = replay_esn->oseq
|
||||
- preplay_esn->oseq;
|
||||
else
|
||||
oseq_diff = ~preplay_esn->oseq
|
||||
+ replay_esn->oseq + 1;
|
||||
|
||||
if (seq_diff >= x->replay_maxdiff ||
|
||||
oseq_diff >= x->replay_maxdiff)
|
||||
break;
|
||||
}
|
||||
|
||||
if (x->xflags & XFRM_TIME_DEFER)
|
||||
event = XFRM_REPLAY_TIMEOUT;
|
||||
else
|
||||
return;
|
||||
|
||||
break;
|
||||
|
||||
case XFRM_REPLAY_TIMEOUT:
|
||||
if (memcmp(x->replay_esn, x->preplay_esn,
|
||||
xfrm_replay_state_esn_len(replay_esn)) == 0) {
|
||||
x->xflags |= XFRM_TIME_DEFER;
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
memcpy(x->preplay_esn, x->replay_esn,
|
||||
xfrm_replay_state_esn_len(replay_esn));
|
||||
c.event = XFRM_MSG_NEWAE;
|
||||
c.data.aevent = event;
|
||||
km_state_notify(x, &c);
|
||||
|
||||
if (x->replay_maxage &&
|
||||
!mod_timer(&x->rtimer, jiffies + x->replay_maxage))
|
||||
x->xflags &= ~XFRM_TIME_DEFER;
|
||||
}
|
||||
|
||||
static int xfrm_replay_overflow_esn(struct xfrm_state *x, struct sk_buff *skb)
|
||||
{
|
||||
int err = 0;
|
||||
struct xfrm_replay_state_esn *replay_esn = x->replay_esn;
|
||||
struct net *net = xs_net(x);
|
||||
|
||||
if (x->type->flags & XFRM_TYPE_REPLAY_PROT) {
|
||||
XFRM_SKB_CB(skb)->seq.output.low = ++replay_esn->oseq;
|
||||
XFRM_SKB_CB(skb)->seq.output.hi = replay_esn->oseq_hi;
|
||||
|
||||
if (unlikely(replay_esn->oseq == 0)) {
|
||||
XFRM_SKB_CB(skb)->seq.output.hi = ++replay_esn->oseq_hi;
|
||||
|
||||
if (replay_esn->oseq_hi == 0) {
|
||||
replay_esn->oseq--;
|
||||
replay_esn->oseq_hi--;
|
||||
xfrm_audit_state_replay_overflow(x, skb);
|
||||
err = -EOVERFLOW;
|
||||
|
||||
return err;
|
||||
}
|
||||
}
|
||||
if (xfrm_aevent_is_on(net))
|
||||
x->repl->notify(x, XFRM_REPLAY_UPDATE);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int xfrm_replay_check_esn(struct xfrm_state *x,
|
||||
struct sk_buff *skb, __be32 net_seq)
|
||||
{
|
||||
unsigned int bitnr, nr;
|
||||
u32 diff;
|
||||
struct xfrm_replay_state_esn *replay_esn = x->replay_esn;
|
||||
u32 pos;
|
||||
u32 seq = ntohl(net_seq);
|
||||
u32 wsize = replay_esn->replay_window;
|
||||
u32 top = replay_esn->seq;
|
||||
u32 bottom = top - wsize + 1;
|
||||
|
||||
if (!wsize)
|
||||
return 0;
|
||||
|
||||
if (unlikely(seq == 0 && replay_esn->seq_hi == 0 &&
|
||||
(replay_esn->seq < replay_esn->replay_window - 1)))
|
||||
goto err;
|
||||
|
||||
diff = top - seq;
|
||||
|
||||
if (likely(top >= wsize - 1)) {
|
||||
/* A. same subspace */
|
||||
if (likely(seq > top) || seq < bottom)
|
||||
return 0;
|
||||
} else {
|
||||
/* B. window spans two subspaces */
|
||||
if (likely(seq > top && seq < bottom))
|
||||
return 0;
|
||||
if (seq >= bottom)
|
||||
diff = ~seq + top + 1;
|
||||
}
|
||||
|
||||
if (diff >= replay_esn->replay_window) {
|
||||
x->stats.replay_window++;
|
||||
goto err;
|
||||
}
|
||||
|
||||
pos = (replay_esn->seq - 1) % replay_esn->replay_window;
|
||||
|
||||
if (pos >= diff)
|
||||
bitnr = (pos - diff) % replay_esn->replay_window;
|
||||
else
|
||||
bitnr = replay_esn->replay_window - (diff - pos);
|
||||
|
||||
nr = bitnr >> 5;
|
||||
bitnr = bitnr & 0x1F;
|
||||
if (replay_esn->bmp[nr] & (1U << bitnr))
|
||||
goto err_replay;
|
||||
|
||||
return 0;
|
||||
|
||||
err_replay:
|
||||
x->stats.replay++;
|
||||
err:
|
||||
xfrm_audit_state_replay(x, skb, net_seq);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int xfrm_replay_recheck_esn(struct xfrm_state *x,
|
||||
struct sk_buff *skb, __be32 net_seq)
|
||||
{
|
||||
if (unlikely(XFRM_SKB_CB(skb)->seq.input.hi !=
|
||||
htonl(xfrm_replay_seqhi(x, net_seq)))) {
|
||||
x->stats.replay_window++;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return xfrm_replay_check_esn(x, skb, net_seq);
|
||||
}
|
||||
|
||||
static void xfrm_replay_advance_esn(struct xfrm_state *x, __be32 net_seq)
|
||||
{
|
||||
unsigned int bitnr, nr, i;
|
||||
int wrap;
|
||||
u32 diff, pos, seq, seq_hi;
|
||||
struct xfrm_replay_state_esn *replay_esn = x->replay_esn;
|
||||
|
||||
if (!replay_esn->replay_window)
|
||||
return;
|
||||
|
||||
seq = ntohl(net_seq);
|
||||
pos = (replay_esn->seq - 1) % replay_esn->replay_window;
|
||||
seq_hi = xfrm_replay_seqhi(x, net_seq);
|
||||
wrap = seq_hi - replay_esn->seq_hi;
|
||||
|
||||
if ((!wrap && seq > replay_esn->seq) || wrap > 0) {
|
||||
if (likely(!wrap))
|
||||
diff = seq - replay_esn->seq;
|
||||
else
|
||||
diff = ~replay_esn->seq + seq + 1;
|
||||
|
||||
if (diff < replay_esn->replay_window) {
|
||||
for (i = 1; i < diff; i++) {
|
||||
bitnr = (pos + i) % replay_esn->replay_window;
|
||||
nr = bitnr >> 5;
|
||||
bitnr = bitnr & 0x1F;
|
||||
replay_esn->bmp[nr] &= ~(1U << bitnr);
|
||||
}
|
||||
} else {
|
||||
nr = (replay_esn->replay_window - 1) >> 5;
|
||||
for (i = 0; i <= nr; i++)
|
||||
replay_esn->bmp[i] = 0;
|
||||
}
|
||||
|
||||
bitnr = (pos + diff) % replay_esn->replay_window;
|
||||
replay_esn->seq = seq;
|
||||
|
||||
if (unlikely(wrap > 0))
|
||||
replay_esn->seq_hi++;
|
||||
} else {
|
||||
diff = replay_esn->seq - seq;
|
||||
|
||||
if (pos >= diff)
|
||||
bitnr = (pos - diff) % replay_esn->replay_window;
|
||||
else
|
||||
bitnr = replay_esn->replay_window - (diff - pos);
|
||||
}
|
||||
|
||||
nr = bitnr >> 5;
|
||||
bitnr = bitnr & 0x1F;
|
||||
replay_esn->bmp[nr] |= (1U << bitnr);
|
||||
|
||||
if (xfrm_aevent_is_on(xs_net(x)))
|
||||
x->repl->notify(x, XFRM_REPLAY_UPDATE);
|
||||
}
|
||||
|
||||
static struct xfrm_replay xfrm_replay_legacy = {
|
||||
.advance = xfrm_replay_advance,
|
||||
.check = xfrm_replay_check,
|
||||
.recheck = xfrm_replay_check,
|
||||
.notify = xfrm_replay_notify,
|
||||
.overflow = xfrm_replay_overflow,
|
||||
};
|
||||
|
||||
static struct xfrm_replay xfrm_replay_bmp = {
|
||||
.advance = xfrm_replay_advance_bmp,
|
||||
.check = xfrm_replay_check_bmp,
|
||||
.recheck = xfrm_replay_check_bmp,
|
||||
.notify = xfrm_replay_notify_bmp,
|
||||
.overflow = xfrm_replay_overflow_bmp,
|
||||
};
|
||||
|
||||
static struct xfrm_replay xfrm_replay_esn = {
|
||||
.advance = xfrm_replay_advance_esn,
|
||||
.check = xfrm_replay_check_esn,
|
||||
.recheck = xfrm_replay_recheck_esn,
|
||||
.notify = xfrm_replay_notify_esn,
|
||||
.overflow = xfrm_replay_overflow_esn,
|
||||
};
|
||||
|
||||
int xfrm_init_replay(struct xfrm_state *x)
|
||||
{
|
||||
struct xfrm_replay_state_esn *replay_esn = x->replay_esn;
|
||||
|
||||
if (replay_esn) {
|
||||
if (replay_esn->replay_window >
|
||||
replay_esn->bmp_len * sizeof(__u32) * 8)
|
||||
return -EINVAL;
|
||||
|
||||
if (x->props.flags & XFRM_STATE_ESN) {
|
||||
if (replay_esn->replay_window == 0)
|
||||
return -EINVAL;
|
||||
x->repl = &xfrm_replay_esn;
|
||||
} else
|
||||
x->repl = &xfrm_replay_bmp;
|
||||
} else
|
||||
x->repl = &xfrm_replay_legacy;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(xfrm_init_replay);
|
2294
net/xfrm/xfrm_state.c
Normal file
2294
net/xfrm/xfrm_state.c
Normal file
File diff suppressed because it is too large
Load diff
86
net/xfrm/xfrm_sysctl.c
Normal file
86
net/xfrm/xfrm_sysctl.c
Normal file
|
@ -0,0 +1,86 @@
|
|||
#include <linux/sysctl.h>
|
||||
#include <linux/slab.h>
|
||||
#include <net/net_namespace.h>
|
||||
#include <net/xfrm.h>
|
||||
|
||||
static void __net_init __xfrm_sysctl_init(struct net *net)
|
||||
{
|
||||
net->xfrm.sysctl_aevent_etime = XFRM_AE_ETIME;
|
||||
net->xfrm.sysctl_aevent_rseqth = XFRM_AE_SEQT_SIZE;
|
||||
net->xfrm.sysctl_larval_drop = 1;
|
||||
net->xfrm.sysctl_acq_expires = 30;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SYSCTL
|
||||
static struct ctl_table xfrm_table[] = {
|
||||
{
|
||||
.procname = "xfrm_aevent_etime",
|
||||
.maxlen = sizeof(u32),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_dointvec
|
||||
},
|
||||
{
|
||||
.procname = "xfrm_aevent_rseqth",
|
||||
.maxlen = sizeof(u32),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_dointvec
|
||||
},
|
||||
{
|
||||
.procname = "xfrm_larval_drop",
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_dointvec
|
||||
},
|
||||
{
|
||||
.procname = "xfrm_acq_expires",
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_dointvec
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
int __net_init xfrm_sysctl_init(struct net *net)
|
||||
{
|
||||
struct ctl_table *table;
|
||||
|
||||
__xfrm_sysctl_init(net);
|
||||
|
||||
table = kmemdup(xfrm_table, sizeof(xfrm_table), GFP_KERNEL);
|
||||
if (!table)
|
||||
goto out_kmemdup;
|
||||
table[0].data = &net->xfrm.sysctl_aevent_etime;
|
||||
table[1].data = &net->xfrm.sysctl_aevent_rseqth;
|
||||
table[2].data = &net->xfrm.sysctl_larval_drop;
|
||||
table[3].data = &net->xfrm.sysctl_acq_expires;
|
||||
|
||||
/* Don't export sysctls to unprivileged users */
|
||||
if (net->user_ns != &init_user_ns)
|
||||
table[0].procname = NULL;
|
||||
|
||||
net->xfrm.sysctl_hdr = register_net_sysctl(net, "net/core", table);
|
||||
if (!net->xfrm.sysctl_hdr)
|
||||
goto out_register;
|
||||
return 0;
|
||||
|
||||
out_register:
|
||||
kfree(table);
|
||||
out_kmemdup:
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
void __net_exit xfrm_sysctl_fini(struct net *net)
|
||||
{
|
||||
struct ctl_table *table;
|
||||
|
||||
table = net->xfrm.sysctl_hdr->ctl_table_arg;
|
||||
unregister_net_sysctl_table(net->xfrm.sysctl_hdr);
|
||||
kfree(table);
|
||||
}
|
||||
#else
|
||||
int __net_init xfrm_sysctl_init(struct net *net)
|
||||
{
|
||||
__xfrm_sysctl_init(net);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
3128
net/xfrm/xfrm_user.c
Normal file
3128
net/xfrm/xfrm_user.c
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue