mirror of
https://github.com/rd-stuffs/msm-4.14.git
synced 2025-02-20 11:45:48 +08:00
Merge android-4.14 (e709f59) into msm-4.14
* refs/heads/tmp-e709f59: Revert "ANDROID: sched/fair: prevent possible infinite loop in sched_group_energy" Revert "UPSTREAM: zram: set BDI_CAP_STABLE_WRITES once" Revert "FROMLIST: ANDROID: binder: Add BINDER_GET_NODE_INFO_FOR_REF ioctl." Revert "ANDROID: sched: Add support for frequency/power energy model" ANDROID: restrict store of prefer_idle as boolean FROMLIST: ANDROID: binder: Add BINDER_GET_NODE_INFO_FOR_REF ioctl. ANDROID: squashfs: resolve merge conflict with 4.14.68 UPSTREAM: xfrm: fix ptr_ret.cocci warnings UPSTREAM: xfrm: Return detailed errors from xfrmi_newlink UPSTREAM: xfrm: Allow xfrmi if_id to be updated by UPDSA UPSTREAM: xfrm: Remove xfrmi interface ID from flowi UPSTREAM: xfrm: Allow Set Mark to be Updated Using UPDSA UPSTREAM: xfrm: Add virtual xfrm interfaces UPSTREAM: xfrm: Add a new lookup key to match xfrm interfaces. UPSTREAM: flow: Extend flow informations with xfrm interface id. UPSTREAM: xfrm: Extend the output_mark to support input direction and masking. UPSTREAM: xfrm: fix XFRMA_OUTPUT_MARK policy entry BACKPORT: zram: drop max_zpage_size and use zs_huge_class_size() UPSTREAM: zsmalloc: introduce zs_huge_class_size() ANDROID: x86_64_cuttlefish_defconfig: Enable lz4 compression for zram UPSTREAM: drivers/block/zram/zram_drv.c: fix bug storing backing_dev UPSTREAM: zram: introduce zram memory tracking UPSTREAM: zram: record accessed second BACKPORT: zram: mark incompressible page as ZRAM_HUGE UPSTREAM: zram: correct flag name of ZRAM_ACCESS UPSTREAM: zram: Delete gendisk before cleaning up the request queue UPSTREAM: drivers/block/zram/zram_drv.c: make zram_page_end_io() static UPSTREAM: zram: set BDI_CAP_STABLE_WRITES once ANDROID: x86_64_cuttlefish_defconfig: Enable zram and zstd UPSTREAM: crypto: zstd - Add zstd support UPSTREAM: zram: add zstd to the supported algorithms list ANDROID: FIXUP: sched/fair: Fix hang during suspend in compute_energy ANDROID: sched/fair: prevent possible infinite loop in sched_group_energy ANDROID: sched: Add support for frequency/power energy model ANDROID: AVB error handler to invalidate vbmeta partition. ANDROID: remove android config fragments ANDROID: ftrace: fix function type mismatches Conflicts: Documentation/blockdev/zram.txt drivers/block/zram/zram_drv.c kernel/configs/android-base.config kernel/configs/android-recommended.config Change-Id: I2b4d018d19d4e2bddfdf5a7a58110d823bf1b274 Signed-off-by: Blagovest Kolenichev <bkolenichev@codeaurora.org>
This commit is contained in:
commit
3773b3a557
@ -220,6 +220,7 @@ line of text and contains the following stats separated by whitespace:
|
||||
pages_compacted the number of pages freed during compaction
|
||||
dup_data_size deduplicated data size
|
||||
meta_data_size the amount of metadata allocated for deduplication feature
|
||||
huge_pages the number of incompressible pages
|
||||
|
||||
9) Deactivate:
|
||||
swapoff /dev/zram0
|
||||
@ -244,5 +245,29 @@ to backing storage rather than keeping it in memory.
|
||||
User should set up backing device via /sys/block/zramX/backing_dev
|
||||
before disksize setting.
|
||||
|
||||
= memory tracking
|
||||
|
||||
With CONFIG_ZRAM_MEMORY_TRACKING, user can know information of the
|
||||
zram block. It could be useful to catch cold or incompressible
|
||||
pages of the process with*pagemap.
|
||||
If you enable the feature, you could see block state via
|
||||
/sys/kernel/debug/zram/zram0/block_state". The output is as follows,
|
||||
|
||||
300 75.033841 .wh
|
||||
301 63.806904 s..
|
||||
302 63.806919 ..h
|
||||
|
||||
First column is zram's block index.
|
||||
Second column is access time since the system was booted
|
||||
Third column is state of the block.
|
||||
(s: same page
|
||||
w: written page to backing store
|
||||
h: huge page)
|
||||
|
||||
First line of above example says 300th block is accessed at 75.033841sec
|
||||
and the block's state is huge so it is written back to the backing
|
||||
storage. It's a debugging feature so anyone shouldn't rely on it to work
|
||||
properly.
|
||||
|
||||
Nitin Gupta
|
||||
ngupta@vflare.org
|
||||
|
@ -52,6 +52,7 @@ CONFIG_X86_CPUID=y
|
||||
CONFIG_KSM=y
|
||||
CONFIG_DEFAULT_MMAP_MIN_ADDR=65536
|
||||
CONFIG_TRANSPARENT_HUGEPAGE=y
|
||||
CONFIG_ZSMALLOC=y
|
||||
# CONFIG_MTRR is not set
|
||||
CONFIG_HZ_100=y
|
||||
CONFIG_KEXEC=y
|
||||
@ -198,6 +199,7 @@ CONFIG_DEBUG_DEVRES=y
|
||||
CONFIG_OF=y
|
||||
CONFIG_OF_UNITTEST=y
|
||||
# CONFIG_PNP_DEBUG_MESSAGES is not set
|
||||
CONFIG_ZRAM=y
|
||||
CONFIG_BLK_DEV_LOOP=y
|
||||
CONFIG_BLK_DEV_RAM=y
|
||||
CONFIG_BLK_DEV_RAM_SIZE=8192
|
||||
@ -452,6 +454,8 @@ CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE=1
|
||||
CONFIG_CRYPTO_RSA=y
|
||||
# CONFIG_CRYPTO_MANAGER_DISABLE_TESTS is not set
|
||||
CONFIG_CRYPTO_SHA512=y
|
||||
CONFIG_CRYPTO_LZ4=y
|
||||
CONFIG_CRYPTO_ZSTD=y
|
||||
CONFIG_CRYPTO_DEV_VIRTIO=y
|
||||
CONFIG_ASYMMETRIC_KEY_TYPE=y
|
||||
CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE=y
|
||||
|
@ -1652,6 +1652,15 @@ config CRYPTO_LZ4HC
|
||||
help
|
||||
This is the LZ4 high compression mode algorithm.
|
||||
|
||||
config CRYPTO_ZSTD
|
||||
tristate "Zstd compression algorithm"
|
||||
select CRYPTO_ALGAPI
|
||||
select CRYPTO_ACOMP2
|
||||
select ZSTD_COMPRESS
|
||||
select ZSTD_DECOMPRESS
|
||||
help
|
||||
This is the zstd algorithm.
|
||||
|
||||
comment "Random Number Generation"
|
||||
|
||||
config CRYPTO_ANSI_CPRNG
|
||||
|
@ -136,6 +136,7 @@ obj-$(CONFIG_CRYPTO_USER_API_HASH) += algif_hash.o
|
||||
obj-$(CONFIG_CRYPTO_USER_API_SKCIPHER) += algif_skcipher.o
|
||||
obj-$(CONFIG_CRYPTO_USER_API_RNG) += algif_rng.o
|
||||
obj-$(CONFIG_CRYPTO_USER_API_AEAD) += algif_aead.o
|
||||
obj-$(CONFIG_CRYPTO_ZSTD) += zstd.o
|
||||
|
||||
ecdh_generic-y := ecc.o
|
||||
ecdh_generic-y += ecdh.o
|
||||
|
@ -3639,6 +3639,16 @@ static const struct alg_test_desc alg_test_descs[] = {
|
||||
.decomp = __VECS(zlib_deflate_decomp_tv_template)
|
||||
}
|
||||
}
|
||||
}, {
|
||||
.alg = "zstd",
|
||||
.test = alg_test_comp,
|
||||
.fips_allowed = 1,
|
||||
.suite = {
|
||||
.comp = {
|
||||
.comp = __VECS(zstd_comp_tv_template),
|
||||
.decomp = __VECS(zstd_decomp_tv_template)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -36124,4 +36124,75 @@ static const struct comp_testvec lz4hc_decomp_tv_template[] = {
|
||||
},
|
||||
};
|
||||
|
||||
static const struct comp_testvec zstd_comp_tv_template[] = {
|
||||
{
|
||||
.inlen = 68,
|
||||
.outlen = 39,
|
||||
.input = "The algorithm is zstd. "
|
||||
"The algorithm is zstd. "
|
||||
"The algorithm is zstd.",
|
||||
.output = "\x28\xb5\x2f\xfd\x00\x50\xf5\x00\x00\xb8\x54\x68\x65"
|
||||
"\x20\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x20\x69\x73"
|
||||
"\x20\x7a\x73\x74\x64\x2e\x20\x01\x00\x55\x73\x36\x01"
|
||||
,
|
||||
},
|
||||
{
|
||||
.inlen = 244,
|
||||
.outlen = 151,
|
||||
.input = "zstd, short for Zstandard, is a fast lossless "
|
||||
"compression algorithm, targeting real-time "
|
||||
"compression scenarios at zlib-level and better "
|
||||
"compression ratios. The zstd compression library "
|
||||
"provides in-memory compression and decompression "
|
||||
"functions.",
|
||||
.output = "\x28\xb5\x2f\xfd\x00\x50\x75\x04\x00\x42\x4b\x1e\x17"
|
||||
"\x90\x81\x31\x00\xf2\x2f\xe4\x36\xc9\xef\x92\x88\x32"
|
||||
"\xc9\xf2\x24\x94\xd8\x68\x9a\x0f\x00\x0c\xc4\x31\x6f"
|
||||
"\x0d\x0c\x38\xac\x5c\x48\x03\xcd\x63\x67\xc0\xf3\xad"
|
||||
"\x4e\x90\xaa\x78\xa0\xa4\xc5\x99\xda\x2f\xb6\x24\x60"
|
||||
"\xe2\x79\x4b\xaa\xb6\x6b\x85\x0b\xc9\xc6\x04\x66\x86"
|
||||
"\xe2\xcc\xe2\x25\x3f\x4f\x09\xcd\xb8\x9d\xdb\xc1\x90"
|
||||
"\xa9\x11\xbc\x35\x44\x69\x2d\x9c\x64\x4f\x13\x31\x64"
|
||||
"\xcc\xfb\x4d\x95\x93\x86\x7f\x33\x7f\x1a\xef\xe9\x30"
|
||||
"\xf9\x67\xa1\x94\x0a\x69\x0f\x60\xcd\xc3\xab\x99\xdc"
|
||||
"\x42\xed\x97\x05\x00\x33\xc3\x15\x95\x3a\x06\xa0\x0e"
|
||||
"\x20\xa9\x0e\x82\xb9\x43\x45\x01",
|
||||
},
|
||||
};
|
||||
|
||||
static const struct comp_testvec zstd_decomp_tv_template[] = {
|
||||
{
|
||||
.inlen = 43,
|
||||
.outlen = 68,
|
||||
.input = "\x28\xb5\x2f\xfd\x04\x50\xf5\x00\x00\xb8\x54\x68\x65"
|
||||
"\x20\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x20\x69\x73"
|
||||
"\x20\x7a\x73\x74\x64\x2e\x20\x01\x00\x55\x73\x36\x01"
|
||||
"\x6b\xf4\x13\x35",
|
||||
.output = "The algorithm is zstd. "
|
||||
"The algorithm is zstd. "
|
||||
"The algorithm is zstd.",
|
||||
},
|
||||
{
|
||||
.inlen = 155,
|
||||
.outlen = 244,
|
||||
.input = "\x28\xb5\x2f\xfd\x04\x50\x75\x04\x00\x42\x4b\x1e\x17"
|
||||
"\x90\x81\x31\x00\xf2\x2f\xe4\x36\xc9\xef\x92\x88\x32"
|
||||
"\xc9\xf2\x24\x94\xd8\x68\x9a\x0f\x00\x0c\xc4\x31\x6f"
|
||||
"\x0d\x0c\x38\xac\x5c\x48\x03\xcd\x63\x67\xc0\xf3\xad"
|
||||
"\x4e\x90\xaa\x78\xa0\xa4\xc5\x99\xda\x2f\xb6\x24\x60"
|
||||
"\xe2\x79\x4b\xaa\xb6\x6b\x85\x0b\xc9\xc6\x04\x66\x86"
|
||||
"\xe2\xcc\xe2\x25\x3f\x4f\x09\xcd\xb8\x9d\xdb\xc1\x90"
|
||||
"\xa9\x11\xbc\x35\x44\x69\x2d\x9c\x64\x4f\x13\x31\x64"
|
||||
"\xcc\xfb\x4d\x95\x93\x86\x7f\x33\x7f\x1a\xef\xe9\x30"
|
||||
"\xf9\x67\xa1\x94\x0a\x69\x0f\x60\xcd\xc3\xab\x99\xdc"
|
||||
"\x42\xed\x97\x05\x00\x33\xc3\x15\x95\x3a\x06\xa0\x0e"
|
||||
"\x20\xa9\x0e\x82\xb9\x43\x45\x01\xaa\x6d\xda\x0d",
|
||||
.output = "zstd, short for Zstandard, is a fast lossless "
|
||||
"compression algorithm, targeting real-time "
|
||||
"compression scenarios at zlib-level and better "
|
||||
"compression ratios. The zstd compression library "
|
||||
"provides in-memory compression and decompression "
|
||||
"functions.",
|
||||
},
|
||||
};
|
||||
#endif /* _CRYPTO_TESTMGR_H */
|
||||
|
265
crypto/zstd.c
Normal file
265
crypto/zstd.c
Normal file
@ -0,0 +1,265 @@
|
||||
/*
|
||||
* Cryptographic API.
|
||||
*
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that 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.
|
||||
*/
|
||||
#include <linux/crypto.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/net.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/zstd.h>
|
||||
#include <crypto/internal/scompress.h>
|
||||
|
||||
|
||||
#define ZSTD_DEF_LEVEL 3
|
||||
|
||||
struct zstd_ctx {
|
||||
ZSTD_CCtx *cctx;
|
||||
ZSTD_DCtx *dctx;
|
||||
void *cwksp;
|
||||
void *dwksp;
|
||||
};
|
||||
|
||||
static ZSTD_parameters zstd_params(void)
|
||||
{
|
||||
return ZSTD_getParams(ZSTD_DEF_LEVEL, 0, 0);
|
||||
}
|
||||
|
||||
static int zstd_comp_init(struct zstd_ctx *ctx)
|
||||
{
|
||||
int ret = 0;
|
||||
const ZSTD_parameters params = zstd_params();
|
||||
const size_t wksp_size = ZSTD_CCtxWorkspaceBound(params.cParams);
|
||||
|
||||
ctx->cwksp = vzalloc(wksp_size);
|
||||
if (!ctx->cwksp) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ctx->cctx = ZSTD_initCCtx(ctx->cwksp, wksp_size);
|
||||
if (!ctx->cctx) {
|
||||
ret = -EINVAL;
|
||||
goto out_free;
|
||||
}
|
||||
out:
|
||||
return ret;
|
||||
out_free:
|
||||
vfree(ctx->cwksp);
|
||||
goto out;
|
||||
}
|
||||
|
||||
static int zstd_decomp_init(struct zstd_ctx *ctx)
|
||||
{
|
||||
int ret = 0;
|
||||
const size_t wksp_size = ZSTD_DCtxWorkspaceBound();
|
||||
|
||||
ctx->dwksp = vzalloc(wksp_size);
|
||||
if (!ctx->dwksp) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ctx->dctx = ZSTD_initDCtx(ctx->dwksp, wksp_size);
|
||||
if (!ctx->dctx) {
|
||||
ret = -EINVAL;
|
||||
goto out_free;
|
||||
}
|
||||
out:
|
||||
return ret;
|
||||
out_free:
|
||||
vfree(ctx->dwksp);
|
||||
goto out;
|
||||
}
|
||||
|
||||
static void zstd_comp_exit(struct zstd_ctx *ctx)
|
||||
{
|
||||
vfree(ctx->cwksp);
|
||||
ctx->cwksp = NULL;
|
||||
ctx->cctx = NULL;
|
||||
}
|
||||
|
||||
static void zstd_decomp_exit(struct zstd_ctx *ctx)
|
||||
{
|
||||
vfree(ctx->dwksp);
|
||||
ctx->dwksp = NULL;
|
||||
ctx->dctx = NULL;
|
||||
}
|
||||
|
||||
static int __zstd_init(void *ctx)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = zstd_comp_init(ctx);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = zstd_decomp_init(ctx);
|
||||
if (ret)
|
||||
zstd_comp_exit(ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void *zstd_alloc_ctx(struct crypto_scomp *tfm)
|
||||
{
|
||||
int ret;
|
||||
struct zstd_ctx *ctx;
|
||||
|
||||
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
|
||||
if (!ctx)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
ret = __zstd_init(ctx);
|
||||
if (ret) {
|
||||
kfree(ctx);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
static int zstd_init(struct crypto_tfm *tfm)
|
||||
{
|
||||
struct zstd_ctx *ctx = crypto_tfm_ctx(tfm);
|
||||
|
||||
return __zstd_init(ctx);
|
||||
}
|
||||
|
||||
static void __zstd_exit(void *ctx)
|
||||
{
|
||||
zstd_comp_exit(ctx);
|
||||
zstd_decomp_exit(ctx);
|
||||
}
|
||||
|
||||
static void zstd_free_ctx(struct crypto_scomp *tfm, void *ctx)
|
||||
{
|
||||
__zstd_exit(ctx);
|
||||
kzfree(ctx);
|
||||
}
|
||||
|
||||
static void zstd_exit(struct crypto_tfm *tfm)
|
||||
{
|
||||
struct zstd_ctx *ctx = crypto_tfm_ctx(tfm);
|
||||
|
||||
__zstd_exit(ctx);
|
||||
}
|
||||
|
||||
static int __zstd_compress(const u8 *src, unsigned int slen,
|
||||
u8 *dst, unsigned int *dlen, void *ctx)
|
||||
{
|
||||
size_t out_len;
|
||||
struct zstd_ctx *zctx = ctx;
|
||||
const ZSTD_parameters params = zstd_params();
|
||||
|
||||
out_len = ZSTD_compressCCtx(zctx->cctx, dst, *dlen, src, slen, params);
|
||||
if (ZSTD_isError(out_len))
|
||||
return -EINVAL;
|
||||
*dlen = out_len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int zstd_compress(struct crypto_tfm *tfm, const u8 *src,
|
||||
unsigned int slen, u8 *dst, unsigned int *dlen)
|
||||
{
|
||||
struct zstd_ctx *ctx = crypto_tfm_ctx(tfm);
|
||||
|
||||
return __zstd_compress(src, slen, dst, dlen, ctx);
|
||||
}
|
||||
|
||||
static int zstd_scompress(struct crypto_scomp *tfm, const u8 *src,
|
||||
unsigned int slen, u8 *dst, unsigned int *dlen,
|
||||
void *ctx)
|
||||
{
|
||||
return __zstd_compress(src, slen, dst, dlen, ctx);
|
||||
}
|
||||
|
||||
static int __zstd_decompress(const u8 *src, unsigned int slen,
|
||||
u8 *dst, unsigned int *dlen, void *ctx)
|
||||
{
|
||||
size_t out_len;
|
||||
struct zstd_ctx *zctx = ctx;
|
||||
|
||||
out_len = ZSTD_decompressDCtx(zctx->dctx, dst, *dlen, src, slen);
|
||||
if (ZSTD_isError(out_len))
|
||||
return -EINVAL;
|
||||
*dlen = out_len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int zstd_decompress(struct crypto_tfm *tfm, const u8 *src,
|
||||
unsigned int slen, u8 *dst, unsigned int *dlen)
|
||||
{
|
||||
struct zstd_ctx *ctx = crypto_tfm_ctx(tfm);
|
||||
|
||||
return __zstd_decompress(src, slen, dst, dlen, ctx);
|
||||
}
|
||||
|
||||
static int zstd_sdecompress(struct crypto_scomp *tfm, const u8 *src,
|
||||
unsigned int slen, u8 *dst, unsigned int *dlen,
|
||||
void *ctx)
|
||||
{
|
||||
return __zstd_decompress(src, slen, dst, dlen, ctx);
|
||||
}
|
||||
|
||||
static struct crypto_alg alg = {
|
||||
.cra_name = "zstd",
|
||||
.cra_flags = CRYPTO_ALG_TYPE_COMPRESS,
|
||||
.cra_ctxsize = sizeof(struct zstd_ctx),
|
||||
.cra_module = THIS_MODULE,
|
||||
.cra_init = zstd_init,
|
||||
.cra_exit = zstd_exit,
|
||||
.cra_u = { .compress = {
|
||||
.coa_compress = zstd_compress,
|
||||
.coa_decompress = zstd_decompress } }
|
||||
};
|
||||
|
||||
static struct scomp_alg scomp = {
|
||||
.alloc_ctx = zstd_alloc_ctx,
|
||||
.free_ctx = zstd_free_ctx,
|
||||
.compress = zstd_scompress,
|
||||
.decompress = zstd_sdecompress,
|
||||
.base = {
|
||||
.cra_name = "zstd",
|
||||
.cra_driver_name = "zstd-scomp",
|
||||
.cra_module = THIS_MODULE,
|
||||
}
|
||||
};
|
||||
|
||||
static int __init zstd_mod_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = crypto_register_alg(&alg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = crypto_register_scomp(&scomp);
|
||||
if (ret)
|
||||
crypto_unregister_alg(&alg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit zstd_mod_fini(void)
|
||||
{
|
||||
crypto_unregister_alg(&alg);
|
||||
crypto_unregister_scomp(&scomp);
|
||||
}
|
||||
|
||||
module_init(zstd_mod_init);
|
||||
module_exit(zstd_mod_fini);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Zstd Compression Algorithm");
|
||||
MODULE_ALIAS_CRYPTO("zstd");
|
@ -13,7 +13,7 @@ config ZRAM
|
||||
It has several use cases, for example: /tmp storage, use as swap
|
||||
disks and maybe many more.
|
||||
|
||||
See zram.txt for more information.
|
||||
See Documentation/blockdev/zram.txt for more information.
|
||||
|
||||
config ZRAM_DEDUP
|
||||
bool "Deduplication support for ZRAM data"
|
||||
@ -39,4 +39,14 @@ config ZRAM_WRITEBACK
|
||||
For this feature, admin should set up backing device via
|
||||
/sys/block/zramX/backing_dev.
|
||||
|
||||
See zram.txt for more infomration.
|
||||
See Documentation/blockdev/zram.txt for more information.
|
||||
|
||||
config ZRAM_MEMORY_TRACKING
|
||||
bool "Track zRam block status"
|
||||
depends on ZRAM && DEBUG_FS
|
||||
help
|
||||
With this feature, admin can track the state of allocated blocks
|
||||
of zRAM. Admin could see the information via
|
||||
/sys/kernel/debug/zram/zramX/block_state.
|
||||
|
||||
See Documentation/blockdev/zram.txt for more information.
|
||||
|
@ -31,6 +31,9 @@ static const char * const backends[] = {
|
||||
#endif
|
||||
#if IS_ENABLED(CONFIG_CRYPTO_842)
|
||||
"842",
|
||||
#endif
|
||||
#if IS_ENABLED(CONFIG_CRYPTO_ZSTD)
|
||||
"zstd",
|
||||
#endif
|
||||
NULL
|
||||
};
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/cpuhotplug.h>
|
||||
|
||||
#include "zram_drv.h"
|
||||
@ -44,14 +45,36 @@ static const char *default_compressor = "lzo";
|
||||
|
||||
/* Module params (documentation at end) */
|
||||
static unsigned int num_devices = 1;
|
||||
/*
|
||||
* Pages that compress to sizes equals or greater than this are stored
|
||||
* uncompressed in memory.
|
||||
*/
|
||||
static size_t huge_class_size;
|
||||
|
||||
static void zram_free_page(struct zram *zram, size_t index);
|
||||
|
||||
static void zram_slot_lock(struct zram *zram, u32 index)
|
||||
{
|
||||
bit_spin_lock(ZRAM_LOCK, &zram->table[index].value);
|
||||
}
|
||||
|
||||
static void zram_slot_unlock(struct zram *zram, u32 index)
|
||||
{
|
||||
bit_spin_unlock(ZRAM_LOCK, &zram->table[index].value);
|
||||
}
|
||||
|
||||
static inline bool init_done(struct zram *zram)
|
||||
{
|
||||
return zram->disksize;
|
||||
}
|
||||
|
||||
static inline bool zram_allocated(struct zram *zram, u32 index)
|
||||
{
|
||||
|
||||
return (zram->table[index].value >> (ZRAM_FLAG_SHIFT + 1)) ||
|
||||
zram->table[index].entry->handle;
|
||||
}
|
||||
|
||||
static inline struct zram *dev_to_zram(struct device *dev)
|
||||
{
|
||||
return (struct zram *)dev_to_disk(dev)->private_data;
|
||||
@ -69,7 +92,7 @@ static void zram_set_entry(struct zram *zram, u32 index,
|
||||
}
|
||||
|
||||
/* flag operations require table entry bit_spin_lock() being held */
|
||||
static int zram_test_flag(struct zram *zram, u32 index,
|
||||
static bool zram_test_flag(struct zram *zram, u32 index,
|
||||
enum zram_pageflags flag)
|
||||
{
|
||||
return zram->table[index].value & BIT(flag);
|
||||
@ -436,7 +459,7 @@ static void put_entry_bdev(struct zram *zram, unsigned long entry)
|
||||
WARN_ON_ONCE(!was_set);
|
||||
}
|
||||
|
||||
void zram_page_end_io(struct bio *bio)
|
||||
static void zram_page_end_io(struct bio *bio)
|
||||
{
|
||||
struct page *page = bio->bi_io_vec[0].bv_page;
|
||||
|
||||
@ -603,6 +626,114 @@ static int read_from_bdev(struct zram *zram, struct bio_vec *bvec,
|
||||
static void zram_wb_clear(struct zram *zram, u32 index) {}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ZRAM_MEMORY_TRACKING
|
||||
|
||||
static struct dentry *zram_debugfs_root;
|
||||
|
||||
static void zram_debugfs_create(void)
|
||||
{
|
||||
zram_debugfs_root = debugfs_create_dir("zram", NULL);
|
||||
}
|
||||
|
||||
static void zram_debugfs_destroy(void)
|
||||
{
|
||||
debugfs_remove_recursive(zram_debugfs_root);
|
||||
}
|
||||
|
||||
static void zram_accessed(struct zram *zram, u32 index)
|
||||
{
|
||||
zram->table[index].ac_time = ktime_get_boottime();
|
||||
}
|
||||
|
||||
static void zram_reset_access(struct zram *zram, u32 index)
|
||||
{
|
||||
zram->table[index].ac_time = 0;
|
||||
}
|
||||
|
||||
static ssize_t read_block_state(struct file *file, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
char *kbuf;
|
||||
ssize_t index, written = 0;
|
||||
struct zram *zram = file->private_data;
|
||||
unsigned long nr_pages = zram->disksize >> PAGE_SHIFT;
|
||||
struct timespec64 ts;
|
||||
|
||||
kbuf = kvmalloc(count, GFP_KERNEL);
|
||||
if (!kbuf)
|
||||
return -ENOMEM;
|
||||
|
||||
down_read(&zram->init_lock);
|
||||
if (!init_done(zram)) {
|
||||
up_read(&zram->init_lock);
|
||||
kvfree(kbuf);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (index = *ppos; index < nr_pages; index++) {
|
||||
int copied;
|
||||
|
||||
zram_slot_lock(zram, index);
|
||||
if (!zram_allocated(zram, index))
|
||||
goto next;
|
||||
|
||||
ts = ktime_to_timespec64(zram->table[index].ac_time);
|
||||
copied = snprintf(kbuf + written, count,
|
||||
"%12zd %12lld.%06lu %c%c%c\n",
|
||||
index, (s64)ts.tv_sec,
|
||||
ts.tv_nsec / NSEC_PER_USEC,
|
||||
zram_test_flag(zram, index, ZRAM_SAME) ? 's' : '.',
|
||||
zram_test_flag(zram, index, ZRAM_WB) ? 'w' : '.',
|
||||
zram_test_flag(zram, index, ZRAM_HUGE) ? 'h' : '.');
|
||||
|
||||
if (count < copied) {
|
||||
zram_slot_unlock(zram, index);
|
||||
break;
|
||||
}
|
||||
written += copied;
|
||||
count -= copied;
|
||||
next:
|
||||
zram_slot_unlock(zram, index);
|
||||
*ppos += 1;
|
||||
}
|
||||
|
||||
up_read(&zram->init_lock);
|
||||
if (copy_to_user(buf, kbuf, written))
|
||||
written = -EFAULT;
|
||||
kvfree(kbuf);
|
||||
|
||||
return written;
|
||||
}
|
||||
|
||||
static const struct file_operations proc_zram_block_state_op = {
|
||||
.open = simple_open,
|
||||
.read = read_block_state,
|
||||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
static void zram_debugfs_register(struct zram *zram)
|
||||
{
|
||||
if (!zram_debugfs_root)
|
||||
return;
|
||||
|
||||
zram->debugfs_dir = debugfs_create_dir(zram->disk->disk_name,
|
||||
zram_debugfs_root);
|
||||
debugfs_create_file("block_state", 0400, zram->debugfs_dir,
|
||||
zram, &proc_zram_block_state_op);
|
||||
}
|
||||
|
||||
static void zram_debugfs_unregister(struct zram *zram)
|
||||
{
|
||||
debugfs_remove_recursive(zram->debugfs_dir);
|
||||
}
|
||||
#else
|
||||
static void zram_debugfs_create(void) {};
|
||||
static void zram_debugfs_destroy(void) {};
|
||||
static void zram_accessed(struct zram *zram, u32 index) {};
|
||||
static void zram_reset_access(struct zram *zram, u32 index) {};
|
||||
static void zram_debugfs_register(struct zram *zram) {};
|
||||
static void zram_debugfs_unregister(struct zram *zram) {};
|
||||
#endif
|
||||
|
||||
/*
|
||||
* We switched to per-cpu streams and this attr is not needed anymore.
|
||||
@ -757,7 +888,7 @@ static ssize_t mm_stat_show(struct device *dev,
|
||||
max_used = atomic_long_read(&zram->stats.max_used_pages);
|
||||
|
||||
ret = scnprintf(buf, PAGE_SIZE,
|
||||
"%8llu %8llu %8llu %8lu %8ld %8llu %8lu %8llu %8llu\n",
|
||||
"%8llu %8llu %8llu %8lu %8ld %8llu %8lu %8llu %8llu %8llu\n",
|
||||
orig_size << PAGE_SHIFT,
|
||||
(u64)atomic64_read(&zram->stats.compr_data_size),
|
||||
mem_used << PAGE_SHIFT,
|
||||
@ -766,7 +897,8 @@ static ssize_t mm_stat_show(struct device *dev,
|
||||
(u64)atomic64_read(&zram->stats.same_pages),
|
||||
pool_stats.pages_compacted,
|
||||
zram_dedup_dup_size(zram),
|
||||
zram_dedup_meta_size(zram));
|
||||
zram_dedup_meta_size(zram),
|
||||
(u64)atomic64_read(&zram->stats.huge_pages));
|
||||
up_read(&zram->init_lock);
|
||||
|
||||
return ret;
|
||||
@ -802,16 +934,6 @@ static unsigned long zram_entry_handle(struct zram *zram,
|
||||
return (unsigned long)entry;
|
||||
}
|
||||
|
||||
static void zram_slot_lock(struct zram *zram, u32 index)
|
||||
{
|
||||
bit_spin_lock(ZRAM_ACCESS, &zram->table[index].value);
|
||||
}
|
||||
|
||||
static void zram_slot_unlock(struct zram *zram, u32 index)
|
||||
{
|
||||
bit_spin_unlock(ZRAM_ACCESS, &zram->table[index].value);
|
||||
}
|
||||
|
||||
static struct zram_entry *zram_entry_alloc(struct zram *zram,
|
||||
unsigned int len, gfp_t flags)
|
||||
{
|
||||
@ -882,6 +1004,9 @@ static bool zram_meta_alloc(struct zram *zram, u64 disksize)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!huge_class_size)
|
||||
huge_class_size = zs_huge_class_size(zram->mem_pool);
|
||||
|
||||
if (zram_dedup_init(zram, num_pages)) {
|
||||
vfree(zram->table);
|
||||
zs_destroy_pool(zram->mem_pool);
|
||||
@ -900,6 +1025,13 @@ static void zram_free_page(struct zram *zram, size_t index)
|
||||
{
|
||||
struct zram_entry *entry;
|
||||
|
||||
zram_reset_access(zram, index);
|
||||
|
||||
if (zram_test_flag(zram, index, ZRAM_HUGE)) {
|
||||
zram_clear_flag(zram, index, ZRAM_HUGE);
|
||||
atomic64_dec(&zram->stats.huge_pages);
|
||||
}
|
||||
|
||||
if (zram_wb_enabled(zram) && zram_test_flag(zram, index, ZRAM_WB)) {
|
||||
zram_wb_clear(zram, index);
|
||||
atomic64_dec(&zram->stats.pages_stored);
|
||||
@ -1076,7 +1208,7 @@ compress_again:
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (unlikely(comp_len > max_zpage_size)) {
|
||||
if (unlikely(comp_len >= huge_class_size)) {
|
||||
if (zram_wb_enabled(zram) && allow_wb) {
|
||||
zcomp_stream_put(zram->comp);
|
||||
ret = write_to_bdev(zram, bvec, index, bio, &element);
|
||||
@ -1088,7 +1220,6 @@ compress_again:
|
||||
allow_wb = false;
|
||||
goto compress_again;
|
||||
}
|
||||
comp_len = PAGE_SIZE;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1153,6 +1284,11 @@ out:
|
||||
zram_slot_lock(zram, index);
|
||||
zram_free_page(zram, index);
|
||||
|
||||
if (comp_len == PAGE_SIZE) {
|
||||
zram_set_flag(zram, index, ZRAM_HUGE);
|
||||
atomic64_inc(&zram->stats.huge_pages);
|
||||
}
|
||||
|
||||
if (flags) {
|
||||
zram_set_flag(zram, index, flags);
|
||||
zram_set_element(zram, index, element);
|
||||
@ -1273,6 +1409,10 @@ static int zram_bvec_rw(struct zram *zram, struct bio_vec *bvec, u32 index,
|
||||
|
||||
generic_end_io_acct(q, rw_acct, &zram->disk->part0, start_time);
|
||||
|
||||
zram_slot_lock(zram, index);
|
||||
zram_accessed(zram, index);
|
||||
zram_slot_unlock(zram, index);
|
||||
|
||||
if (unlikely(ret < 0)) {
|
||||
if (!is_write)
|
||||
atomic64_inc(&zram->stats.failed_reads);
|
||||
@ -1689,6 +1829,7 @@ static int zram_add(void)
|
||||
|
||||
strlcpy(zram->compressor, default_compressor, sizeof(zram->compressor));
|
||||
|
||||
zram_debugfs_register(zram);
|
||||
pr_info("Added device: %s\n", zram->disk->disk_name);
|
||||
return device_id;
|
||||
|
||||
@ -1719,6 +1860,7 @@ static int zram_remove(struct zram *zram)
|
||||
zram->claim = true;
|
||||
mutex_unlock(&bdev->bd_mutex);
|
||||
|
||||
zram_debugfs_unregister(zram);
|
||||
/* Make sure all the pending I/O are finished */
|
||||
fsync_bdev(bdev);
|
||||
zram_reset_device(zram);
|
||||
@ -1726,8 +1868,8 @@ static int zram_remove(struct zram *zram)
|
||||
|
||||
pr_info("Removed device: %s\n", zram->disk->disk_name);
|
||||
|
||||
blk_cleanup_queue(zram->disk->queue);
|
||||
del_gendisk(zram->disk);
|
||||
blk_cleanup_queue(zram->disk->queue);
|
||||
put_disk(zram->disk);
|
||||
kfree(zram);
|
||||
return 0;
|
||||
@ -1811,6 +1953,7 @@ static void destroy_devices(void)
|
||||
{
|
||||
class_unregister(&zram_control_class);
|
||||
idr_for_each(&zram_index_idr, &zram_remove_cb, NULL);
|
||||
zram_debugfs_destroy();
|
||||
idr_destroy(&zram_index_idr);
|
||||
unregister_blkdev(zram_major, "zram");
|
||||
cpuhp_remove_multi_state(CPUHP_ZCOMP_PREPARE);
|
||||
@ -1832,6 +1975,7 @@ static int __init zram_init(void)
|
||||
return ret;
|
||||
}
|
||||
|
||||
zram_debugfs_create();
|
||||
zram_major = register_blkdev(0, "zram");
|
||||
if (zram_major <= 0) {
|
||||
pr_err("Unable to get major number\n");
|
||||
|
@ -23,22 +23,6 @@
|
||||
#include "zcomp.h"
|
||||
#include "zram_dedup.h"
|
||||
|
||||
/*-- Configurable parameters */
|
||||
|
||||
/*
|
||||
* Pages that compress to size greater than this are stored
|
||||
* uncompressed in memory.
|
||||
*/
|
||||
static const size_t max_zpage_size = PAGE_SIZE / 4 * 3;
|
||||
|
||||
/*
|
||||
* NOTE: max_zpage_size must be less than or equal to:
|
||||
* ZS_MAX_ALLOC_SIZE. Otherwise, zs_malloc() would
|
||||
* always return failure.
|
||||
*/
|
||||
|
||||
/*-- End of configurable params */
|
||||
|
||||
#define SECTOR_SHIFT 9
|
||||
#define SECTORS_PER_PAGE_SHIFT (PAGE_SHIFT - SECTOR_SHIFT)
|
||||
#define SECTORS_PER_PAGE (1 << SECTORS_PER_PAGE_SHIFT)
|
||||
@ -62,10 +46,11 @@ static const size_t max_zpage_size = PAGE_SIZE / 4 * 3;
|
||||
|
||||
/* Flags for zram pages (table[page_no].value) */
|
||||
enum zram_pageflags {
|
||||
/* Page consists the same element */
|
||||
ZRAM_SAME = ZRAM_FLAG_SHIFT,
|
||||
ZRAM_ACCESS, /* page is now accessed */
|
||||
/* zram slot is locked */
|
||||
ZRAM_LOCK = ZRAM_FLAG_SHIFT,
|
||||
ZRAM_SAME, /* Page consists the same element */
|
||||
ZRAM_WB, /* page is stored on backing_device */
|
||||
ZRAM_HUGE, /* Incompressible page */
|
||||
|
||||
__NR_ZRAM_PAGEFLAGS,
|
||||
};
|
||||
@ -87,6 +72,9 @@ struct zram_table_entry {
|
||||
unsigned long element;
|
||||
};
|
||||
unsigned long value;
|
||||
#ifdef CONFIG_ZRAM_MEMORY_TRACKING
|
||||
ktime_t ac_time;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct zram_stats {
|
||||
@ -98,6 +86,7 @@ struct zram_stats {
|
||||
atomic64_t invalid_io; /* non-page-aligned I/O requests */
|
||||
atomic64_t notify_free; /* no. of swap slot free notifications */
|
||||
atomic64_t same_pages; /* no. of same element filled pages */
|
||||
atomic64_t huge_pages; /* no. of huge pages */
|
||||
atomic64_t pages_stored; /* no. of pages currently stored */
|
||||
atomic_long_t max_used_pages; /* no. of maximum pages stored */
|
||||
atomic64_t writestall; /* no. of write slow paths */
|
||||
@ -147,6 +136,9 @@ struct zram {
|
||||
unsigned long nr_pages;
|
||||
spinlock_t bitmap_lock;
|
||||
#endif
|
||||
#ifdef CONFIG_ZRAM_MEMORY_TRACKING
|
||||
struct dentry *debugfs_dir;
|
||||
#endif
|
||||
};
|
||||
|
||||
static inline bool zram_dedup_enabled(struct zram *zram)
|
||||
|
@ -254,8 +254,16 @@ static inline int ftrace_function_local_disabled(struct ftrace_ops *ops)
|
||||
return *this_cpu_ptr(ops->disabled);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CFI_CLANG
|
||||
/* Use a C stub with the correct type for CFI */
|
||||
static inline void ftrace_stub(unsigned long a0, unsigned long a1,
|
||||
struct ftrace_ops *op, struct pt_regs *regs)
|
||||
{
|
||||
}
|
||||
#else
|
||||
extern void ftrace_stub(unsigned long a0, unsigned long a1,
|
||||
struct ftrace_ops *op, struct pt_regs *regs);
|
||||
#endif
|
||||
|
||||
#else /* !CONFIG_FUNCTION_TRACER */
|
||||
/*
|
||||
|
@ -47,6 +47,8 @@ void zs_destroy_pool(struct zs_pool *pool);
|
||||
unsigned long zs_malloc(struct zs_pool *pool, size_t size, gfp_t flags);
|
||||
void zs_free(struct zs_pool *pool, unsigned long obj);
|
||||
|
||||
size_t zs_huge_class_size(struct zs_pool *pool);
|
||||
|
||||
void *zs_map_object(struct zs_pool *pool, unsigned long handle,
|
||||
enum zs_mapmode mm);
|
||||
void zs_unmap_object(struct zs_pool *pool, unsigned long handle);
|
||||
|
@ -490,6 +490,14 @@ static inline struct dst_entry *xfrm_lookup(struct net *net,
|
||||
return dst_orig;
|
||||
}
|
||||
|
||||
static inline struct dst_entry *
|
||||
xfrm_lookup_with_ifid(struct net *net, struct dst_entry *dst_orig,
|
||||
const struct flowi *fl, const struct sock *sk,
|
||||
int flags, u32 if_id)
|
||||
{
|
||||
return dst_orig;
|
||||
}
|
||||
|
||||
static inline struct dst_entry *xfrm_lookup_route(struct net *net,
|
||||
struct dst_entry *dst_orig,
|
||||
const struct flowi *fl,
|
||||
@ -509,6 +517,12 @@ struct dst_entry *xfrm_lookup(struct net *net, struct dst_entry *dst_orig,
|
||||
const struct flowi *fl, const struct sock *sk,
|
||||
int flags);
|
||||
|
||||
struct dst_entry *xfrm_lookup_with_ifid(struct net *net,
|
||||
struct dst_entry *dst_orig,
|
||||
const struct flowi *fl,
|
||||
const struct sock *sk, int flags,
|
||||
u32 if_id);
|
||||
|
||||
struct dst_entry *xfrm_lookup_route(struct net *net, struct dst_entry *dst_orig,
|
||||
const struct flowi *fl, const struct sock *sk,
|
||||
int flags);
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include <net/ipv6.h>
|
||||
#include <net/ip6_fib.h>
|
||||
#include <net/flow.h>
|
||||
#include <net/gro_cells.h>
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
@ -147,6 +148,7 @@ struct xfrm_state {
|
||||
struct xfrm_id id;
|
||||
struct xfrm_selector sel;
|
||||
struct xfrm_mark mark;
|
||||
u32 if_id;
|
||||
u32 tfcpad;
|
||||
|
||||
u32 genid;
|
||||
@ -166,7 +168,7 @@ struct xfrm_state {
|
||||
int header_len;
|
||||
int trailer_len;
|
||||
u32 extra_flags;
|
||||
u32 output_mark;
|
||||
struct xfrm_mark smark;
|
||||
} props;
|
||||
|
||||
struct xfrm_lifetime_cfg lft;
|
||||
@ -292,6 +294,13 @@ struct xfrm_replay {
|
||||
int (*overflow)(struct xfrm_state *x, struct sk_buff *skb);
|
||||
};
|
||||
|
||||
struct xfrm_if_cb {
|
||||
struct xfrm_if *(*decode_session)(struct sk_buff *skb);
|
||||
};
|
||||
|
||||
void xfrm_if_register_cb(const struct xfrm_if_cb *ifcb);
|
||||
void xfrm_if_unregister_cb(void);
|
||||
|
||||
struct net_device;
|
||||
struct xfrm_type;
|
||||
struct xfrm_dst;
|
||||
@ -573,6 +582,7 @@ struct xfrm_policy {
|
||||
atomic_t genid;
|
||||
u32 priority;
|
||||
u32 index;
|
||||
u32 if_id;
|
||||
struct xfrm_mark mark;
|
||||
struct xfrm_selector selector;
|
||||
struct xfrm_lifetime_cfg lft;
|
||||
@ -1006,6 +1016,22 @@ static inline void xfrm_dst_destroy(struct xfrm_dst *xdst)
|
||||
|
||||
void xfrm_dst_ifdown(struct dst_entry *dst, struct net_device *dev);
|
||||
|
||||
struct xfrm_if_parms {
|
||||
char name[IFNAMSIZ]; /* name of XFRM device */
|
||||
int link; /* ifindex of underlying L2 interface */
|
||||
u32 if_id; /* interface identifyer */
|
||||
};
|
||||
|
||||
struct xfrm_if {
|
||||
struct xfrm_if __rcu *next; /* next interface in list */
|
||||
struct net_device *dev; /* virtual device associated with interface */
|
||||
struct net_device *phydev; /* physical device */
|
||||
struct net *net; /* netns for packet i/o */
|
||||
struct xfrm_if_parms p; /* interface parms */
|
||||
|
||||
struct gro_cells gro_cells;
|
||||
};
|
||||
|
||||
struct xfrm_offload {
|
||||
/* Output sequence number for replay protection on offloading. */
|
||||
struct {
|
||||
@ -1500,8 +1526,8 @@ struct xfrm_state *xfrm_state_find(const xfrm_address_t *daddr,
|
||||
const struct flowi *fl,
|
||||
struct xfrm_tmpl *tmpl,
|
||||
struct xfrm_policy *pol, int *err,
|
||||
unsigned short family);
|
||||
struct xfrm_state *xfrm_stateonly_find(struct net *net, u32 mark,
|
||||
unsigned short family, u32 if_id);
|
||||
struct xfrm_state *xfrm_stateonly_find(struct net *net, u32 mark, u32 if_id,
|
||||
xfrm_address_t *daddr,
|
||||
xfrm_address_t *saddr,
|
||||
unsigned short family,
|
||||
@ -1658,20 +1684,20 @@ int xfrm_policy_walk(struct net *net, struct xfrm_policy_walk *walk,
|
||||
void *);
|
||||
void xfrm_policy_walk_done(struct xfrm_policy_walk *walk, struct net *net);
|
||||
int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl);
|
||||
struct xfrm_policy *xfrm_policy_bysel_ctx(struct net *net, u32 mark,
|
||||
struct xfrm_policy *xfrm_policy_bysel_ctx(struct net *net, u32 mark, u32 if_id,
|
||||
u8 type, int dir,
|
||||
struct xfrm_selector *sel,
|
||||
struct xfrm_sec_ctx *ctx, int delete,
|
||||
int *err);
|
||||
struct xfrm_policy *xfrm_policy_byid(struct net *net, u32 mark, u8, int dir,
|
||||
u32 id, int delete, int *err);
|
||||
struct xfrm_policy *xfrm_policy_byid(struct net *net, u32 mark, u32 if_id, u8,
|
||||
int dir, u32 id, int delete, int *err);
|
||||
int xfrm_policy_flush(struct net *net, u8 type, bool task_valid);
|
||||
void xfrm_policy_hash_rebuild(struct net *net);
|
||||
u32 xfrm_get_acqseq(void);
|
||||
int verify_spi_info(u8 proto, u32 min, u32 max);
|
||||
int xfrm_alloc_spi(struct xfrm_state *x, u32 minspi, u32 maxspi);
|
||||
struct xfrm_state *xfrm_find_acq(struct net *net, const struct xfrm_mark *mark,
|
||||
u8 mode, u32 reqid, u8 proto,
|
||||
u8 mode, u32 reqid, u32 if_id, u8 proto,
|
||||
const xfrm_address_t *daddr,
|
||||
const xfrm_address_t *saddr, int create,
|
||||
unsigned short family);
|
||||
@ -1948,6 +1974,22 @@ static inline int xfrm_mark_put(struct sk_buff *skb, const struct xfrm_mark *m)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline __u32 xfrm_smark_get(__u32 mark, struct xfrm_state *x)
|
||||
{
|
||||
struct xfrm_mark *m = &x->props.smark;
|
||||
|
||||
return (m->v & m->m) | (mark & ~m->m);
|
||||
}
|
||||
|
||||
static inline int xfrm_if_id_put(struct sk_buff *skb, __u32 if_id)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (if_id)
|
||||
ret = nla_put_u32(skb, XFRMA_IF_ID, if_id);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int xfrm_tunnel_check(struct sk_buff *skb, struct xfrm_state *x,
|
||||
unsigned int family)
|
||||
{
|
||||
|
@ -451,6 +451,16 @@ enum {
|
||||
|
||||
#define IFLA_MACSEC_MAX (__IFLA_MACSEC_MAX - 1)
|
||||
|
||||
/* XFRM section */
|
||||
enum {
|
||||
IFLA_XFRM_UNSPEC,
|
||||
IFLA_XFRM_LINK,
|
||||
IFLA_XFRM_IF_ID,
|
||||
__IFLA_XFRM_MAX
|
||||
};
|
||||
|
||||
#define IFLA_XFRM_MAX (__IFLA_XFRM_MAX - 1)
|
||||
|
||||
enum macsec_validation_type {
|
||||
MACSEC_VALIDATE_DISABLED = 0,
|
||||
MACSEC_VALIDATE_CHECK = 1,
|
||||
|
@ -305,9 +305,12 @@ enum xfrm_attr_type_t {
|
||||
XFRMA_ADDRESS_FILTER, /* struct xfrm_address_filter */
|
||||
XFRMA_PAD,
|
||||
XFRMA_OFFLOAD_DEV, /* struct xfrm_state_offload */
|
||||
XFRMA_OUTPUT_MARK, /* __u32 */
|
||||
XFRMA_SET_MARK, /* __u32 */
|
||||
XFRMA_SET_MARK_MASK, /* __u32 */
|
||||
XFRMA_IF_ID, /* __u32 */
|
||||
__XFRMA_MAX
|
||||
|
||||
#define XFRMA_OUTPUT_MARK XFRMA_SET_MARK /* Compatibility */
|
||||
#define XFRMA_MAX (__XFRMA_MAX - 1)
|
||||
};
|
||||
|
||||
|
@ -1,167 +0,0 @@
|
||||
# KEEP ALPHABETICALLY SORTED
|
||||
# CONFIG_DEVKMEM is not set
|
||||
# CONFIG_DEVMEM is not set
|
||||
# CONFIG_FHANDLE is not set
|
||||
# CONFIG_INET_LRO is not set
|
||||
# CONFIG_NFSD is not set
|
||||
# CONFIG_NFS_FS is not set
|
||||
# CONFIG_OABI_COMPAT is not set
|
||||
# CONFIG_SYSVIPC is not set
|
||||
# CONFIG_USELIB is not set
|
||||
CONFIG_ANDROID=y
|
||||
CONFIG_ANDROID_BINDER_DEVICES=binder,hwbinder,vndbinder
|
||||
CONFIG_ANDROID_BINDER_IPC=y
|
||||
CONFIG_ANDROID_BINDER_DEVICES=binder,hwbinder,vndbinder
|
||||
CONFIG_ANDROID_LOW_MEMORY_KILLER=y
|
||||
CONFIG_ASHMEM=y
|
||||
CONFIG_AUDIT=y
|
||||
CONFIG_BLK_DEV_INITRD=y
|
||||
CONFIG_CGROUPS=y
|
||||
CONFIG_CGROUP_BPF=y
|
||||
CONFIG_CGROUP_CPUACCT=y
|
||||
CONFIG_CGROUP_FREEZER=y
|
||||
CONFIG_CGROUP_SCHED=y
|
||||
CONFIG_CGROUP_BPF=y
|
||||
CONFIG_DEFAULT_SECURITY_SELINUX=y
|
||||
CONFIG_EMBEDDED=y
|
||||
CONFIG_FB=y
|
||||
CONFIG_HARDENED_USERCOPY=y
|
||||
CONFIG_HIGH_RES_TIMERS=y
|
||||
CONFIG_IKCONFIG=y
|
||||
CONFIG_IKCONFIG_PROC=y
|
||||
CONFIG_INET6_AH=y
|
||||
CONFIG_INET6_ESP=y
|
||||
CONFIG_INET6_IPCOMP=y
|
||||
CONFIG_INET=y
|
||||
CONFIG_INET_DIAG_DESTROY=y
|
||||
CONFIG_INET_ESP=y
|
||||
CONFIG_INET_XFRM_MODE_TUNNEL=y
|
||||
CONFIG_IP6_NF_FILTER=y
|
||||
CONFIG_IP6_NF_IPTABLES=y
|
||||
CONFIG_IP6_NF_MANGLE=y
|
||||
CONFIG_IP6_NF_RAW=y
|
||||
CONFIG_IP6_NF_TARGET_REJECT=y
|
||||
CONFIG_IPV6=y
|
||||
CONFIG_IPV6_MIP6=y
|
||||
CONFIG_IPV6_MULTIPLE_TABLES=y
|
||||
CONFIG_IPV6_OPTIMISTIC_DAD=y
|
||||
CONFIG_IPV6_ROUTER_PREF=y
|
||||
CONFIG_IPV6_ROUTE_INFO=y
|
||||
CONFIG_IP_ADVANCED_ROUTER=y
|
||||
CONFIG_IP_MULTICAST=y
|
||||
CONFIG_IP_MULTIPLE_TABLES=y
|
||||
CONFIG_IP_NF_ARPFILTER=y
|
||||
CONFIG_IP_NF_ARPTABLES=y
|
||||
CONFIG_IP_NF_ARP_MANGLE=y
|
||||
CONFIG_IP_NF_FILTER=y
|
||||
CONFIG_IP_NF_IPTABLES=y
|
||||
CONFIG_IP_NF_MANGLE=y
|
||||
CONFIG_IP_NF_MATCH_AH=y
|
||||
CONFIG_IP_NF_MATCH_ECN=y
|
||||
CONFIG_IP_NF_MATCH_TTL=y
|
||||
CONFIG_IP_NF_NAT=y
|
||||
CONFIG_IP_NF_RAW=y
|
||||
CONFIG_IP_NF_SECURITY=y
|
||||
CONFIG_IP_NF_TARGET_MASQUERADE=y
|
||||
CONFIG_IP_NF_TARGET_NETMAP=y
|
||||
CONFIG_IP_NF_TARGET_REDIRECT=y
|
||||
CONFIG_IP_NF_TARGET_REJECT=y
|
||||
CONFIG_MODULES=y
|
||||
CONFIG_MODULE_UNLOAD=y
|
||||
CONFIG_MODVERSIONS=y
|
||||
CONFIG_NET=y
|
||||
CONFIG_NETDEVICES=y
|
||||
CONFIG_NETFILTER=y
|
||||
CONFIG_NETFILTER_XT_MATCH_COMMENT=y
|
||||
CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y
|
||||
CONFIG_NETFILTER_XT_MATCH_CONNMARK=y
|
||||
CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y
|
||||
CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y
|
||||
CONFIG_NETFILTER_XT_MATCH_HELPER=y
|
||||
CONFIG_NETFILTER_XT_MATCH_IPRANGE=y
|
||||
CONFIG_NETFILTER_XT_MATCH_LENGTH=y
|
||||
CONFIG_NETFILTER_XT_MATCH_LIMIT=y
|
||||
CONFIG_NETFILTER_XT_MATCH_MAC=y
|
||||
CONFIG_NETFILTER_XT_MATCH_MARK=y
|
||||
CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y
|
||||
CONFIG_NETFILTER_XT_MATCH_POLICY=y
|
||||
CONFIG_NETFILTER_XT_MATCH_QTAGUID=y
|
||||
CONFIG_NETFILTER_XT_MATCH_QUOTA2=y
|
||||
CONFIG_NETFILTER_XT_MATCH_QUOTA=y
|
||||
CONFIG_NETFILTER_XT_MATCH_SOCKET=y
|
||||
CONFIG_NETFILTER_XT_MATCH_STATE=y
|
||||
CONFIG_NETFILTER_XT_MATCH_STATISTIC=y
|
||||
CONFIG_NETFILTER_XT_MATCH_STRING=y
|
||||
CONFIG_NETFILTER_XT_MATCH_TIME=y
|
||||
CONFIG_NETFILTER_XT_MATCH_U32=y
|
||||
CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y
|
||||
CONFIG_NETFILTER_XT_TARGET_CONNMARK=y
|
||||
CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y
|
||||
CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y
|
||||
CONFIG_NETFILTER_XT_TARGET_MARK=y
|
||||
CONFIG_NETFILTER_XT_TARGET_NFLOG=y
|
||||
CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y
|
||||
CONFIG_NETFILTER_XT_TARGET_SECMARK=y
|
||||
CONFIG_NETFILTER_XT_TARGET_TCPMSS=y
|
||||
CONFIG_NETFILTER_XT_TARGET_TPROXY=y
|
||||
CONFIG_NETFILTER_XT_TARGET_TRACE=y
|
||||
CONFIG_NET_CLS_ACT=y
|
||||
CONFIG_NET_CLS_U32=y
|
||||
CONFIG_NET_EMATCH=y
|
||||
CONFIG_NET_EMATCH_U32=y
|
||||
CONFIG_NET_KEY=y
|
||||
CONFIG_NET_SCHED=y
|
||||
CONFIG_NET_SCH_HTB=y
|
||||
CONFIG_NF_CONNTRACK=y
|
||||
CONFIG_NF_CONNTRACK_AMANDA=y
|
||||
CONFIG_NF_CONNTRACK_EVENTS=y
|
||||
CONFIG_NF_CONNTRACK_FTP=y
|
||||
CONFIG_NF_CONNTRACK_H323=y
|
||||
CONFIG_NF_CONNTRACK_IPV4=y
|
||||
CONFIG_NF_CONNTRACK_IPV6=y
|
||||
CONFIG_NF_CONNTRACK_IRC=y
|
||||
CONFIG_NF_CONNTRACK_NETBIOS_NS=y
|
||||
CONFIG_NF_CONNTRACK_PPTP=y
|
||||
CONFIG_NF_CONNTRACK_SANE=y
|
||||
CONFIG_NF_CONNTRACK_SECMARK=y
|
||||
CONFIG_NF_CONNTRACK_TFTP=y
|
||||
CONFIG_NF_CT_NETLINK=y
|
||||
CONFIG_NF_CT_PROTO_DCCP=y
|
||||
CONFIG_NF_CT_PROTO_SCTP=y
|
||||
CONFIG_NF_CT_PROTO_UDPLITE=y
|
||||
CONFIG_NF_NAT=y
|
||||
CONFIG_NO_HZ=y
|
||||
CONFIG_PACKET=y
|
||||
CONFIG_PM_AUTOSLEEP=y
|
||||
CONFIG_PM_WAKELOCKS=y
|
||||
CONFIG_PPP=y
|
||||
CONFIG_PPPOLAC=y
|
||||
CONFIG_PPPOPNS=y
|
||||
CONFIG_PPP_BSDCOMP=y
|
||||
CONFIG_PPP_DEFLATE=y
|
||||
CONFIG_PPP_MPPE=y
|
||||
CONFIG_PREEMPT=y
|
||||
CONFIG_PROFILING=y
|
||||
CONFIG_RANDOMIZE_BASE=y
|
||||
CONFIG_RTC_CLASS=y
|
||||
CONFIG_RT_GROUP_SCHED=y
|
||||
CONFIG_SECCOMP=y
|
||||
CONFIG_SECURITY=y
|
||||
CONFIG_SECURITY_NETWORK=y
|
||||
CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y
|
||||
CONFIG_SECURITY_SELINUX=y
|
||||
CONFIG_STAGING=y
|
||||
CONFIG_SYNC=y
|
||||
CONFIG_TUN=y
|
||||
CONFIG_UID_SYS_STATS=y
|
||||
CONFIG_UNIX=y
|
||||
CONFIG_USB_CONFIGFS=y
|
||||
CONFIG_USB_CONFIGFS_F_ACC=y
|
||||
CONFIG_USB_CONFIGFS_F_AUDIO_SRC=y
|
||||
CONFIG_USB_CONFIGFS_F_FS=y
|
||||
CONFIG_USB_CONFIGFS_F_MIDI=y
|
||||
CONFIG_USB_CONFIGFS_F_MTP=y
|
||||
CONFIG_USB_CONFIGFS_F_PTP=y
|
||||
CONFIG_USB_CONFIGFS_UEVENT=y
|
||||
CONFIG_USB_GADGET=y
|
||||
CONFIG_XFRM_USER=y
|
@ -1,137 +0,0 @@
|
||||
# KEEP ALPHABETICALLY SORTED
|
||||
# CONFIG_AIO is not set
|
||||
# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
|
||||
# CONFIG_INPUT_MOUSE is not set
|
||||
# CONFIG_LEGACY_PTYS is not set
|
||||
# CONFIG_NF_CONNTRACK_SIP is not set
|
||||
# CONFIG_PM_WAKELOCKS_GC is not set
|
||||
# CONFIG_VT is not set
|
||||
CONFIG_ARM64_SW_TTBR0_PAN=y
|
||||
CONFIG_BACKLIGHT_LCD_SUPPORT=y
|
||||
CONFIG_BLK_DEV_DM=y
|
||||
CONFIG_BLK_DEV_LOOP=y
|
||||
CONFIG_BLK_DEV_RAM=y
|
||||
CONFIG_BLK_DEV_RAM_SIZE=8192
|
||||
CONFIG_CC_STACKPROTECTOR_STRONG=y
|
||||
CONFIG_COMPACTION=y
|
||||
CONFIG_CPU_SW_DOMAIN_PAN=y
|
||||
CONFIG_DM_CRYPT=y
|
||||
CONFIG_DM_UEVENT=y
|
||||
CONFIG_DM_VERITY=y
|
||||
CONFIG_DM_VERITY_FEC=y
|
||||
CONFIG_DRAGONRISE_FF=y
|
||||
CONFIG_ENABLE_DEFAULT_TRACERS=y
|
||||
CONFIG_EXT4_FS=y
|
||||
CONFIG_EXT4_FS_SECURITY=y
|
||||
CONFIG_FUSE_FS=y
|
||||
CONFIG_GREENASIA_FF=y
|
||||
CONFIG_HIDRAW=y
|
||||
CONFIG_HID_A4TECH=y
|
||||
CONFIG_HID_ACRUX=y
|
||||
CONFIG_HID_ACRUX_FF=y
|
||||
CONFIG_HID_APPLE=y
|
||||
CONFIG_HID_BELKIN=y
|
||||
CONFIG_HID_CHERRY=y
|
||||
CONFIG_HID_CHICONY=y
|
||||
CONFIG_HID_CYPRESS=y
|
||||
CONFIG_HID_DRAGONRISE=y
|
||||
CONFIG_HID_ELECOM=y
|
||||
CONFIG_HID_EMS_FF=y
|
||||
CONFIG_HID_EZKEY=y
|
||||
CONFIG_HID_GREENASIA=y
|
||||
CONFIG_HID_GYRATION=y
|
||||
CONFIG_HID_HOLTEK=y
|
||||
CONFIG_HID_KENSINGTON=y
|
||||
CONFIG_HID_KEYTOUCH=y
|
||||
CONFIG_HID_KYE=y
|
||||
CONFIG_HID_LCPOWER=y
|
||||
CONFIG_HID_LOGITECH=y
|
||||
CONFIG_HID_LOGITECH_DJ=y
|
||||
CONFIG_HID_MAGICMOUSE=y
|
||||
CONFIG_HID_MICROSOFT=y
|
||||
CONFIG_HID_MONTEREY=y
|
||||
CONFIG_HID_MULTITOUCH=y
|
||||
CONFIG_HID_NTRIG=y
|
||||
CONFIG_HID_ORTEK=y
|
||||
CONFIG_HID_PANTHERLORD=y
|
||||
CONFIG_HID_PETALYNX=y
|
||||
CONFIG_HID_PICOLCD=y
|
||||
CONFIG_HID_PRIMAX=y
|
||||
CONFIG_HID_PRODIKEYS=y
|
||||
CONFIG_HID_ROCCAT=y
|
||||
CONFIG_HID_SAITEK=y
|
||||
CONFIG_HID_SAMSUNG=y
|
||||
CONFIG_HID_SMARTJOYPLUS=y
|
||||
CONFIG_HID_SONY=y
|
||||
CONFIG_HID_SPEEDLINK=y
|
||||
CONFIG_HID_SUNPLUS=y
|
||||
CONFIG_HID_THRUSTMASTER=y
|
||||
CONFIG_HID_TIVO=y
|
||||
CONFIG_HID_TOPSEED=y
|
||||
CONFIG_HID_TWINHAN=y
|
||||
CONFIG_HID_UCLOGIC=y
|
||||
CONFIG_HID_WACOM=y
|
||||
CONFIG_HID_WALTOP=y
|
||||
CONFIG_HID_WIIMOTE=y
|
||||
CONFIG_HID_ZEROPLUS=y
|
||||
CONFIG_HID_ZYDACRON=y
|
||||
CONFIG_INPUT_EVDEV=y
|
||||
CONFIG_INPUT_GPIO=y
|
||||
CONFIG_INPUT_JOYSTICK=y
|
||||
CONFIG_INPUT_KEYCHORD=y
|
||||
CONFIG_INPUT_KEYRESET=y
|
||||
CONFIG_INPUT_MISC=y
|
||||
CONFIG_INPUT_TABLET=y
|
||||
CONFIG_INPUT_UINPUT=y
|
||||
CONFIG_ION=y
|
||||
CONFIG_JOYSTICK_XPAD=y
|
||||
CONFIG_JOYSTICK_XPAD_FF=y
|
||||
CONFIG_JOYSTICK_XPAD_LEDS=y
|
||||
CONFIG_KALLSYMS_ALL=y
|
||||
CONFIG_KSM=y
|
||||
CONFIG_LOGIG940_FF=y
|
||||
CONFIG_LOGIRUMBLEPAD2_FF=y
|
||||
CONFIG_LOGITECH_FF=y
|
||||
CONFIG_MD=y
|
||||
CONFIG_MEDIA_SUPPORT=y
|
||||
CONFIG_MEMORY_STATE_TIME=y
|
||||
CONFIG_MSDOS_FS=y
|
||||
CONFIG_PANIC_TIMEOUT=5
|
||||
CONFIG_PANTHERLORD_FF=y
|
||||
CONFIG_PERF_EVENTS=y
|
||||
CONFIG_PM_DEBUG=y
|
||||
CONFIG_PM_RUNTIME=y
|
||||
CONFIG_PM_WAKELOCKS_LIMIT=0
|
||||
CONFIG_POWER_SUPPLY=y
|
||||
CONFIG_PSTORE=y
|
||||
CONFIG_PSTORE_CONSOLE=y
|
||||
CONFIG_PSTORE_RAM=y
|
||||
CONFIG_QFMT_V2=y
|
||||
CONFIG_QUOTA=y
|
||||
CONFIG_QUOTACTL=y
|
||||
CONFIG_QUOTA_NETLINK_INTERFACE=y
|
||||
CONFIG_QUOTA_TREE=y
|
||||
CONFIG_SCHEDSTATS=y
|
||||
CONFIG_SMARTJOYPLUS_FF=y
|
||||
CONFIG_SND=y
|
||||
CONFIG_SOUND=y
|
||||
CONFIG_STRICT_KERNEL_RWX=y
|
||||
CONFIG_SUSPEND_TIME=y
|
||||
CONFIG_TABLET_USB_ACECAD=y
|
||||
CONFIG_TABLET_USB_AIPTEK=y
|
||||
CONFIG_TABLET_USB_GTCO=y
|
||||
CONFIG_TABLET_USB_HANWANG=y
|
||||
CONFIG_TABLET_USB_KBTAB=y
|
||||
CONFIG_TASKSTATS=y
|
||||
CONFIG_TASK_DELAY_ACCT=y
|
||||
CONFIG_TASK_IO_ACCOUNTING=y
|
||||
CONFIG_TASK_XACCT=y
|
||||
CONFIG_TIMER_STATS=y
|
||||
CONFIG_TMPFS=y
|
||||
CONFIG_TMPFS_POSIX_ACL=y
|
||||
CONFIG_UHID=y
|
||||
CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
|
||||
CONFIG_USB_EHCI_HCD=y
|
||||
CONFIG_USB_HIDDEV=y
|
||||
CONFIG_USB_USBNET=y
|
||||
CONFIG_VFAT_FS=y
|
@ -579,7 +579,7 @@ prefer_idle_write(struct cgroup_subsys_state *css, struct cftype *cft,
|
||||
u64 prefer_idle)
|
||||
{
|
||||
struct schedtune *st = css_st(css);
|
||||
st->prefer_idle = prefer_idle;
|
||||
st->prefer_idle = !!prefer_idle;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -122,8 +122,9 @@ static void ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip,
|
||||
struct ftrace_ops *op, struct pt_regs *regs);
|
||||
#else
|
||||
/* See comment below, where ftrace_ops_list_func is defined */
|
||||
static void ftrace_ops_no_ops(unsigned long ip, unsigned long parent_ip);
|
||||
#define ftrace_ops_list_func ((ftrace_func_t)ftrace_ops_no_ops)
|
||||
static void ftrace_ops_no_ops(unsigned long ip, unsigned long parent_ip,
|
||||
struct ftrace_ops *op, struct pt_regs *regs);
|
||||
#define ftrace_ops_list_func ftrace_ops_no_ops
|
||||
#endif
|
||||
|
||||
/*
|
||||
@ -6099,7 +6100,8 @@ static void ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip,
|
||||
__ftrace_ops_list_func(ip, parent_ip, NULL, regs);
|
||||
}
|
||||
#else
|
||||
static void ftrace_ops_no_ops(unsigned long ip, unsigned long parent_ip)
|
||||
static void ftrace_ops_no_ops(unsigned long ip, unsigned long parent_ip,
|
||||
struct ftrace_ops *op, struct pt_regs *regs)
|
||||
{
|
||||
__ftrace_ops_list_func(ip, parent_ip, NULL, NULL);
|
||||
}
|
||||
@ -6563,14 +6565,17 @@ void ftrace_graph_graph_time_control(bool enable)
|
||||
fgraph_graph_time = enable;
|
||||
}
|
||||
|
||||
void ftrace_graph_return_stub(struct ftrace_graph_ret *trace)
|
||||
{
|
||||
}
|
||||
|
||||
int ftrace_graph_entry_stub(struct ftrace_graph_ent *trace)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* The callbacks that hook a function */
|
||||
trace_func_graph_ret_t ftrace_graph_return =
|
||||
(trace_func_graph_ret_t)ftrace_stub;
|
||||
trace_func_graph_ret_t ftrace_graph_return = ftrace_graph_return_stub;
|
||||
trace_func_graph_ent_t ftrace_graph_entry = ftrace_graph_entry_stub;
|
||||
static trace_func_graph_ent_t __ftrace_graph_entry = ftrace_graph_entry_stub;
|
||||
|
||||
@ -6798,7 +6803,7 @@ void unregister_ftrace_graph(void)
|
||||
goto out;
|
||||
|
||||
ftrace_graph_active--;
|
||||
ftrace_graph_return = (trace_func_graph_ret_t)ftrace_stub;
|
||||
ftrace_graph_return = ftrace_graph_return_stub;
|
||||
ftrace_graph_entry = ftrace_graph_entry_stub;
|
||||
__ftrace_graph_entry = ftrace_graph_entry_stub;
|
||||
ftrace_shutdown(&graph_ops, FTRACE_STOP_FUNC_RET);
|
||||
|
@ -190,6 +190,7 @@ static struct vfsmount *zsmalloc_mnt;
|
||||
* (see: fix_fullness_group())
|
||||
*/
|
||||
static const int fullness_threshold_frac = 4;
|
||||
static size_t huge_class_size;
|
||||
|
||||
struct size_class {
|
||||
spinlock_t lock;
|
||||
@ -1426,6 +1427,25 @@ void zs_unmap_object(struct zs_pool *pool, unsigned long handle)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(zs_unmap_object);
|
||||
|
||||
/**
|
||||
* zs_huge_class_size() - Returns the size (in bytes) of the first huge
|
||||
* zsmalloc &size_class.
|
||||
* @pool: zsmalloc pool to use
|
||||
*
|
||||
* The function returns the size of the first huge class - any object of equal
|
||||
* or bigger size will be stored in zspage consisting of a single physical
|
||||
* page.
|
||||
*
|
||||
* Context: Any context.
|
||||
*
|
||||
* Return: the size (in bytes) of the first huge zsmalloc &size_class.
|
||||
*/
|
||||
size_t zs_huge_class_size(struct zs_pool *pool)
|
||||
{
|
||||
return huge_class_size;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(zs_huge_class_size);
|
||||
|
||||
static unsigned long obj_malloc(struct size_class *class,
|
||||
struct zspage *zspage, unsigned long handle)
|
||||
{
|
||||
@ -2385,6 +2405,27 @@ struct zs_pool *zs_create_pool(const char *name)
|
||||
pages_per_zspage = get_pages_per_zspage(size);
|
||||
objs_per_zspage = pages_per_zspage * PAGE_SIZE / size;
|
||||
|
||||
/*
|
||||
* We iterate from biggest down to smallest classes,
|
||||
* so huge_class_size holds the size of the first huge
|
||||
* class. Any object bigger than or equal to that will
|
||||
* endup in the huge class.
|
||||
*/
|
||||
if (pages_per_zspage != 1 && objs_per_zspage != 1 &&
|
||||
!huge_class_size) {
|
||||
huge_class_size = size;
|
||||
/*
|
||||
* The object uses ZS_HANDLE_SIZE bytes to store the
|
||||
* handle. We need to subtract it, because zs_malloc()
|
||||
* unconditionally adds handle size before it performs
|
||||
* size class search - so object may be smaller than
|
||||
* huge class size, yet it still can end up in the huge
|
||||
* class because it grows by ZS_HANDLE_SIZE extra bytes
|
||||
* right before class lookup.
|
||||
*/
|
||||
huge_class_size -= (ZS_HANDLE_SIZE - 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* size_class is used for normal zsmalloc operation such
|
||||
* as alloc/free for that size. Although it is natural that we
|
||||
|
@ -2345,7 +2345,7 @@ static void get_ipsec_sa(struct pktgen_dev *pkt_dev, int flow)
|
||||
x = xfrm_state_lookup_byspi(pn->net, htonl(pkt_dev->spi), AF_INET);
|
||||
} else {
|
||||
/* slow path: we dont already have xfrm_state */
|
||||
x = xfrm_stateonly_find(pn->net, DUMMY_MARK,
|
||||
x = xfrm_stateonly_find(pn->net, DUMMY_MARK, 0,
|
||||
(xfrm_address_t *)&pkt_dev->cur_daddr,
|
||||
(xfrm_address_t *)&pkt_dev->cur_saddr,
|
||||
AF_INET,
|
||||
|
@ -1373,7 +1373,7 @@ static int pfkey_getspi(struct sock *sk, struct sk_buff *skb, const struct sadb_
|
||||
}
|
||||
|
||||
if (!x)
|
||||
x = xfrm_find_acq(net, &dummy_mark, mode, reqid, proto, xdaddr, xsaddr, 1, family);
|
||||
x = xfrm_find_acq(net, &dummy_mark, mode, reqid, 0, proto, xdaddr, xsaddr, 1, family);
|
||||
|
||||
if (x == NULL)
|
||||
return -ENOENT;
|
||||
@ -2402,7 +2402,7 @@ static int pfkey_spddelete(struct sock *sk, struct sk_buff *skb, const struct sa
|
||||
return err;
|
||||
}
|
||||
|
||||
xp = xfrm_policy_bysel_ctx(net, DUMMY_MARK, XFRM_POLICY_TYPE_MAIN,
|
||||
xp = xfrm_policy_bysel_ctx(net, DUMMY_MARK, 0, XFRM_POLICY_TYPE_MAIN,
|
||||
pol->sadb_x_policy_dir - 1, &sel, pol_ctx,
|
||||
1, &err);
|
||||
security_xfrm_policy_free(pol_ctx);
|
||||
@ -2651,7 +2651,7 @@ static int pfkey_spdget(struct sock *sk, struct sk_buff *skb, const struct sadb_
|
||||
return -EINVAL;
|
||||
|
||||
delete = (hdr->sadb_msg_type == SADB_X_SPDDELETE2);
|
||||
xp = xfrm_policy_byid(net, DUMMY_MARK, XFRM_POLICY_TYPE_MAIN,
|
||||
xp = xfrm_policy_byid(net, DUMMY_MARK, 0, XFRM_POLICY_TYPE_MAIN,
|
||||
dir, pol->sadb_x_policy_id, delete, &err);
|
||||
if (xp == NULL)
|
||||
return -ENOENT;
|
||||
|
@ -25,6 +25,14 @@ config XFRM_USER
|
||||
|
||||
If unsure, say Y.
|
||||
|
||||
config XFRM_INTERFACE
|
||||
tristate "Transformation virtual interface"
|
||||
depends on XFRM && IPV6
|
||||
---help---
|
||||
This provides a virtual interface to route IPsec traffic.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config XFRM_SUB_POLICY
|
||||
bool "Transformation sub policy support"
|
||||
depends on XFRM
|
||||
|
@ -10,3 +10,4 @@ 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
|
||||
obj-$(CONFIG_XFRM_INTERFACE) += xfrm_interface.o
|
||||
|
@ -80,7 +80,8 @@ int xfrm_dev_state_add(struct net *net, struct xfrm_state *x,
|
||||
}
|
||||
|
||||
dst = __xfrm_dst_lookup(net, 0, 0, saddr, daddr,
|
||||
x->props.family, x->props.output_mark);
|
||||
x->props.family,
|
||||
xfrm_smark_get(0, x));
|
||||
if (IS_ERR(dst))
|
||||
return 0;
|
||||
|
||||
|
@ -320,6 +320,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
|
||||
|
||||
seq = 0;
|
||||
if (!spi && (err = xfrm_parse_spi(skb, nexthdr, &spi, &seq)) != 0) {
|
||||
secpath_reset(skb);
|
||||
XFRM_INC_STATS(net, LINUX_MIB_XFRMINHDRERROR);
|
||||
goto drop;
|
||||
}
|
||||
@ -328,17 +329,21 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
|
||||
XFRM_SPI_SKB_CB(skb)->daddroff);
|
||||
do {
|
||||
if (skb->sp->len == XFRM_MAX_DEPTH) {
|
||||
secpath_reset(skb);
|
||||
XFRM_INC_STATS(net, LINUX_MIB_XFRMINBUFFERERROR);
|
||||
goto drop;
|
||||
}
|
||||
|
||||
x = xfrm_state_lookup(net, mark, daddr, spi, nexthdr, family);
|
||||
if (x == NULL) {
|
||||
secpath_reset(skb);
|
||||
XFRM_INC_STATS(net, LINUX_MIB_XFRMINNOSTATES);
|
||||
xfrm_audit_state_notfound(skb, family, spi, seq);
|
||||
goto drop;
|
||||
}
|
||||
|
||||
skb->mark = xfrm_smark_get(skb->mark, x);
|
||||
|
||||
skb->sp->xvec[skb->sp->len++] = x;
|
||||
|
||||
skb_dst_force(skb);
|
||||
|
975
net/xfrm/xfrm_interface.c
Normal file
975
net/xfrm/xfrm_interface.c
Normal file
@ -0,0 +1,975 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* XFRM virtual interface
|
||||
*
|
||||
* Copyright (C) 2018 secunet Security Networks AG
|
||||
*
|
||||
* Author:
|
||||
* Steffen Klassert <steffen.klassert@secunet.com>
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/capability.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/sockios.h>
|
||||
#include <linux/icmp.h>
|
||||
#include <linux/if.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/net.h>
|
||||
#include <linux/in6.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/if_link.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/icmpv6.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/route.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <linux/netfilter_ipv6.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/hash.h>
|
||||
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/atomic.h>
|
||||
|
||||
#include <net/icmp.h>
|
||||
#include <net/ip.h>
|
||||
#include <net/ipv6.h>
|
||||
#include <net/ip6_route.h>
|
||||
#include <net/addrconf.h>
|
||||
#include <net/xfrm.h>
|
||||
#include <net/net_namespace.h>
|
||||
#include <net/netns/generic.h>
|
||||
#include <linux/etherdevice.h>
|
||||
|
||||
static int xfrmi_dev_init(struct net_device *dev);
|
||||
static void xfrmi_dev_setup(struct net_device *dev);
|
||||
static struct rtnl_link_ops xfrmi_link_ops __read_mostly;
|
||||
static unsigned int xfrmi_net_id __read_mostly;
|
||||
|
||||
struct xfrmi_net {
|
||||
/* lists for storing interfaces in use */
|
||||
struct xfrm_if __rcu *xfrmi[1];
|
||||
};
|
||||
|
||||
#define for_each_xfrmi_rcu(start, xi) \
|
||||
for (xi = rcu_dereference(start); xi; xi = rcu_dereference(xi->next))
|
||||
|
||||
static struct xfrm_if *xfrmi_lookup(struct net *net, struct xfrm_state *x)
|
||||
{
|
||||
struct xfrmi_net *xfrmn = net_generic(net, xfrmi_net_id);
|
||||
struct xfrm_if *xi;
|
||||
|
||||
for_each_xfrmi_rcu(xfrmn->xfrmi[0], xi) {
|
||||
if (x->if_id == xi->p.if_id &&
|
||||
(xi->dev->flags & IFF_UP))
|
||||
return xi;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct xfrm_if *xfrmi_decode_session(struct sk_buff *skb)
|
||||
{
|
||||
struct xfrmi_net *xfrmn;
|
||||
int ifindex;
|
||||
struct xfrm_if *xi;
|
||||
|
||||
if (!skb->dev)
|
||||
return NULL;
|
||||
|
||||
xfrmn = net_generic(dev_net(skb->dev), xfrmi_net_id);
|
||||
ifindex = skb->dev->ifindex;
|
||||
|
||||
for_each_xfrmi_rcu(xfrmn->xfrmi[0], xi) {
|
||||
if (ifindex == xi->dev->ifindex &&
|
||||
(xi->dev->flags & IFF_UP))
|
||||
return xi;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void xfrmi_link(struct xfrmi_net *xfrmn, struct xfrm_if *xi)
|
||||
{
|
||||
struct xfrm_if __rcu **xip = &xfrmn->xfrmi[0];
|
||||
|
||||
rcu_assign_pointer(xi->next , rtnl_dereference(*xip));
|
||||
rcu_assign_pointer(*xip, xi);
|
||||
}
|
||||
|
||||
static void xfrmi_unlink(struct xfrmi_net *xfrmn, struct xfrm_if *xi)
|
||||
{
|
||||
struct xfrm_if __rcu **xip;
|
||||
struct xfrm_if *iter;
|
||||
|
||||
for (xip = &xfrmn->xfrmi[0];
|
||||
(iter = rtnl_dereference(*xip)) != NULL;
|
||||
xip = &iter->next) {
|
||||
if (xi == iter) {
|
||||
rcu_assign_pointer(*xip, xi->next);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void xfrmi_dev_free(struct net_device *dev)
|
||||
{
|
||||
free_percpu(dev->tstats);
|
||||
}
|
||||
|
||||
static int xfrmi_create2(struct net_device *dev)
|
||||
{
|
||||
struct xfrm_if *xi = netdev_priv(dev);
|
||||
struct net *net = dev_net(dev);
|
||||
struct xfrmi_net *xfrmn = net_generic(net, xfrmi_net_id);
|
||||
int err;
|
||||
|
||||
dev->rtnl_link_ops = &xfrmi_link_ops;
|
||||
err = register_netdevice(dev);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
strcpy(xi->p.name, dev->name);
|
||||
|
||||
dev_hold(dev);
|
||||
xfrmi_link(xfrmn, xi);
|
||||
|
||||
return 0;
|
||||
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct xfrm_if *xfrmi_create(struct net *net, struct xfrm_if_parms *p)
|
||||
{
|
||||
struct net_device *dev;
|
||||
struct xfrm_if *xi;
|
||||
char name[IFNAMSIZ];
|
||||
int err;
|
||||
|
||||
if (p->name[0]) {
|
||||
strlcpy(name, p->name, IFNAMSIZ);
|
||||
} else {
|
||||
err = -EINVAL;
|
||||
goto failed;
|
||||
}
|
||||
|
||||
dev = alloc_netdev(sizeof(*xi), name, NET_NAME_UNKNOWN, xfrmi_dev_setup);
|
||||
if (!dev) {
|
||||
err = -EAGAIN;
|
||||
goto failed;
|
||||
}
|
||||
|
||||
dev_net_set(dev, net);
|
||||
|
||||
xi = netdev_priv(dev);
|
||||
xi->p = *p;
|
||||
xi->net = net;
|
||||
xi->dev = dev;
|
||||
xi->phydev = dev_get_by_index(net, p->link);
|
||||
if (!xi->phydev) {
|
||||
err = -ENODEV;
|
||||
goto failed_free;
|
||||
}
|
||||
|
||||
err = xfrmi_create2(dev);
|
||||
if (err < 0)
|
||||
goto failed_dev_put;
|
||||
|
||||
return xi;
|
||||
|
||||
failed_dev_put:
|
||||
dev_put(xi->phydev);
|
||||
failed_free:
|
||||
free_netdev(dev);
|
||||
failed:
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
static struct xfrm_if *xfrmi_locate(struct net *net, struct xfrm_if_parms *p,
|
||||
int create)
|
||||
{
|
||||
struct xfrm_if __rcu **xip;
|
||||
struct xfrm_if *xi;
|
||||
struct xfrmi_net *xfrmn = net_generic(net, xfrmi_net_id);
|
||||
|
||||
for (xip = &xfrmn->xfrmi[0];
|
||||
(xi = rtnl_dereference(*xip)) != NULL;
|
||||
xip = &xi->next) {
|
||||
if (xi->p.if_id == p->if_id) {
|
||||
if (create)
|
||||
return ERR_PTR(-EEXIST);
|
||||
|
||||
return xi;
|
||||
}
|
||||
}
|
||||
if (!create)
|
||||
return ERR_PTR(-ENODEV);
|
||||
return xfrmi_create(net, p);
|
||||
}
|
||||
|
||||
static void xfrmi_dev_uninit(struct net_device *dev)
|
||||
{
|
||||
struct xfrm_if *xi = netdev_priv(dev);
|
||||
struct xfrmi_net *xfrmn = net_generic(xi->net, xfrmi_net_id);
|
||||
|
||||
xfrmi_unlink(xfrmn, xi);
|
||||
dev_put(xi->phydev);
|
||||
dev_put(dev);
|
||||
}
|
||||
|
||||
static void xfrmi_scrub_packet(struct sk_buff *skb, bool xnet)
|
||||
{
|
||||
skb->tstamp = 0;
|
||||
skb->pkt_type = PACKET_HOST;
|
||||
skb->skb_iif = 0;
|
||||
skb->ignore_df = 0;
|
||||
skb_dst_drop(skb);
|
||||
nf_reset(skb);
|
||||
nf_reset_trace(skb);
|
||||
|
||||
if (!xnet)
|
||||
return;
|
||||
|
||||
ipvs_reset(skb);
|
||||
secpath_reset(skb);
|
||||
skb_orphan(skb);
|
||||
skb->mark = 0;
|
||||
}
|
||||
|
||||
static int xfrmi_rcv_cb(struct sk_buff *skb, int err)
|
||||
{
|
||||
struct pcpu_sw_netstats *tstats;
|
||||
struct xfrm_mode *inner_mode;
|
||||
struct net_device *dev;
|
||||
struct xfrm_state *x;
|
||||
struct xfrm_if *xi;
|
||||
bool xnet;
|
||||
|
||||
if (err && !skb->sp)
|
||||
return 0;
|
||||
|
||||
x = xfrm_input_state(skb);
|
||||
|
||||
xi = xfrmi_lookup(xs_net(x), x);
|
||||
if (!xi)
|
||||
return 1;
|
||||
|
||||
dev = xi->dev;
|
||||
skb->dev = dev;
|
||||
|
||||
if (err) {
|
||||
dev->stats.rx_errors++;
|
||||
dev->stats.rx_dropped++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
xnet = !net_eq(xi->net, dev_net(skb->dev));
|
||||
|
||||
if (xnet) {
|
||||
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) {
|
||||
XFRM_INC_STATS(dev_net(skb->dev),
|
||||
LINUX_MIB_XFRMINSTATEMODEERROR);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!xfrm_policy_check(NULL, XFRM_POLICY_IN, skb,
|
||||
inner_mode->afinfo->family))
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
xfrmi_scrub_packet(skb, xnet);
|
||||
|
||||
tstats = this_cpu_ptr(dev->tstats);
|
||||
|
||||
u64_stats_update_begin(&tstats->syncp);
|
||||
tstats->rx_packets++;
|
||||
tstats->rx_bytes += skb->len;
|
||||
u64_stats_update_end(&tstats->syncp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
xfrmi_xmit2(struct sk_buff *skb, struct net_device *dev, struct flowi *fl)
|
||||
{
|
||||
struct xfrm_if *xi = netdev_priv(dev);
|
||||
struct net_device_stats *stats = &xi->dev->stats;
|
||||
struct dst_entry *dst = skb_dst(skb);
|
||||
unsigned int length = skb->len;
|
||||
struct net_device *tdev;
|
||||
struct xfrm_state *x;
|
||||
int err = -1;
|
||||
int mtu;
|
||||
|
||||
if (!dst)
|
||||
goto tx_err_link_failure;
|
||||
|
||||
dst_hold(dst);
|
||||
dst = xfrm_lookup_with_ifid(xi->net, dst, fl, NULL, 0, xi->p.if_id);
|
||||
if (IS_ERR(dst)) {
|
||||
err = PTR_ERR(dst);
|
||||
dst = NULL;
|
||||
goto tx_err_link_failure;
|
||||
}
|
||||
|
||||
x = dst->xfrm;
|
||||
if (!x)
|
||||
goto tx_err_link_failure;
|
||||
|
||||
if (x->if_id != xi->p.if_id)
|
||||
goto tx_err_link_failure;
|
||||
|
||||
tdev = dst->dev;
|
||||
|
||||
if (tdev == dev) {
|
||||
stats->collisions++;
|
||||
net_warn_ratelimited("%s: Local routing loop detected!\n",
|
||||
xi->p.name);
|
||||
goto tx_err_dst_release;
|
||||
}
|
||||
|
||||
mtu = dst_mtu(dst);
|
||||
if (!skb->ignore_df && skb->len > mtu) {
|
||||
skb_dst_update_pmtu(skb, mtu);
|
||||
|
||||
if (skb->protocol == htons(ETH_P_IPV6)) {
|
||||
if (mtu < IPV6_MIN_MTU)
|
||||
mtu = IPV6_MIN_MTU;
|
||||
|
||||
icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
|
||||
} else {
|
||||
icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
|
||||
htonl(mtu));
|
||||
}
|
||||
|
||||
dst_release(dst);
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
xfrmi_scrub_packet(skb, !net_eq(xi->net, dev_net(dev)));
|
||||
skb_dst_set(skb, dst);
|
||||
skb->dev = tdev;
|
||||
|
||||
err = dst_output(xi->net, skb->sk, skb);
|
||||
if (net_xmit_eval(err) == 0) {
|
||||
struct pcpu_sw_netstats *tstats = this_cpu_ptr(dev->tstats);
|
||||
|
||||
u64_stats_update_begin(&tstats->syncp);
|
||||
tstats->tx_bytes += length;
|
||||
tstats->tx_packets++;
|
||||
u64_stats_update_end(&tstats->syncp);
|
||||
} else {
|
||||
stats->tx_errors++;
|
||||
stats->tx_aborted_errors++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
tx_err_link_failure:
|
||||
stats->tx_carrier_errors++;
|
||||
dst_link_failure(skb);
|
||||
tx_err_dst_release:
|
||||
dst_release(dst);
|
||||
return err;
|
||||
}
|
||||
|
||||
static netdev_tx_t xfrmi_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
{
|
||||
struct xfrm_if *xi = netdev_priv(dev);
|
||||
struct net_device_stats *stats = &xi->dev->stats;
|
||||
struct flowi fl;
|
||||
int ret;
|
||||
|
||||
memset(&fl, 0, sizeof(fl));
|
||||
|
||||
switch (skb->protocol) {
|
||||
case htons(ETH_P_IPV6):
|
||||
xfrm_decode_session(skb, &fl, AF_INET6);
|
||||
memset(IP6CB(skb), 0, sizeof(*IP6CB(skb)));
|
||||
break;
|
||||
case htons(ETH_P_IP):
|
||||
xfrm_decode_session(skb, &fl, AF_INET);
|
||||
memset(IPCB(skb), 0, sizeof(*IPCB(skb)));
|
||||
break;
|
||||
default:
|
||||
goto tx_err;
|
||||
}
|
||||
|
||||
fl.flowi_oif = xi->phydev->ifindex;
|
||||
|
||||
ret = xfrmi_xmit2(skb, dev, &fl);
|
||||
if (ret < 0)
|
||||
goto tx_err;
|
||||
|
||||
return NETDEV_TX_OK;
|
||||
|
||||
tx_err:
|
||||
stats->tx_errors++;
|
||||
stats->tx_dropped++;
|
||||
kfree_skb(skb);
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
||||
static int xfrmi4_err(struct sk_buff *skb, u32 info)
|
||||
{
|
||||
const struct iphdr *iph = (const struct iphdr *)skb->data;
|
||||
struct net *net = dev_net(skb->dev);
|
||||
int protocol = iph->protocol;
|
||||
struct ip_comp_hdr *ipch;
|
||||
struct ip_esp_hdr *esph;
|
||||
struct ip_auth_hdr *ah ;
|
||||
struct xfrm_state *x;
|
||||
struct xfrm_if *xi;
|
||||
__be32 spi;
|
||||
|
||||
switch (protocol) {
|
||||
case IPPROTO_ESP:
|
||||
esph = (struct ip_esp_hdr *)(skb->data+(iph->ihl<<2));
|
||||
spi = esph->spi;
|
||||
break;
|
||||
case IPPROTO_AH:
|
||||
ah = (struct ip_auth_hdr *)(skb->data+(iph->ihl<<2));
|
||||
spi = ah->spi;
|
||||
break;
|
||||
case IPPROTO_COMP:
|
||||
ipch = (struct ip_comp_hdr *)(skb->data+(iph->ihl<<2));
|
||||
spi = htonl(ntohs(ipch->cpi));
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (icmp_hdr(skb)->type) {
|
||||
case ICMP_DEST_UNREACH:
|
||||
if (icmp_hdr(skb)->code != ICMP_FRAG_NEEDED)
|
||||
return 0;
|
||||
case ICMP_REDIRECT:
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
x = xfrm_state_lookup(net, skb->mark, (const xfrm_address_t *)&iph->daddr,
|
||||
spi, protocol, AF_INET);
|
||||
if (!x)
|
||||
return 0;
|
||||
|
||||
xi = xfrmi_lookup(net, x);
|
||||
if (!xi) {
|
||||
xfrm_state_put(x);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (icmp_hdr(skb)->type == ICMP_DEST_UNREACH)
|
||||
ipv4_update_pmtu(skb, net, info, 0, 0, protocol, 0);
|
||||
else
|
||||
ipv4_redirect(skb, net, 0, 0, protocol, 0);
|
||||
xfrm_state_put(x);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xfrmi6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
|
||||
u8 type, u8 code, int offset, __be32 info)
|
||||
{
|
||||
const struct ipv6hdr *iph = (const struct ipv6hdr *)skb->data;
|
||||
struct net *net = dev_net(skb->dev);
|
||||
int protocol = iph->nexthdr;
|
||||
struct ip_comp_hdr *ipch;
|
||||
struct ip_esp_hdr *esph;
|
||||
struct ip_auth_hdr *ah;
|
||||
struct xfrm_state *x;
|
||||
struct xfrm_if *xi;
|
||||
__be32 spi;
|
||||
|
||||
switch (protocol) {
|
||||
case IPPROTO_ESP:
|
||||
esph = (struct ip_esp_hdr *)(skb->data + offset);
|
||||
spi = esph->spi;
|
||||
break;
|
||||
case IPPROTO_AH:
|
||||
ah = (struct ip_auth_hdr *)(skb->data + offset);
|
||||
spi = ah->spi;
|
||||
break;
|
||||
case IPPROTO_COMP:
|
||||
ipch = (struct ip_comp_hdr *)(skb->data + offset);
|
||||
spi = htonl(ntohs(ipch->cpi));
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (type != ICMPV6_PKT_TOOBIG &&
|
||||
type != NDISC_REDIRECT)
|
||||
return 0;
|
||||
|
||||
x = xfrm_state_lookup(net, skb->mark, (const xfrm_address_t *)&iph->daddr,
|
||||
spi, protocol, AF_INET6);
|
||||
if (!x)
|
||||
return 0;
|
||||
|
||||
xi = xfrmi_lookup(net, x);
|
||||
if (!xi) {
|
||||
xfrm_state_put(x);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (type == NDISC_REDIRECT)
|
||||
ip6_redirect(skb, net, skb->dev->ifindex, 0,
|
||||
sock_net_uid(net, NULL));
|
||||
else
|
||||
ip6_update_pmtu(skb, net, info, 0, 0, sock_net_uid(net, NULL));
|
||||
xfrm_state_put(x);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xfrmi_change(struct xfrm_if *xi, const struct xfrm_if_parms *p)
|
||||
{
|
||||
if (xi->p.link != p->link)
|
||||
return -EINVAL;
|
||||
|
||||
xi->p.if_id = p->if_id;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xfrmi_update(struct xfrm_if *xi, struct xfrm_if_parms *p)
|
||||
{
|
||||
struct net *net = dev_net(xi->dev);
|
||||
struct xfrmi_net *xfrmn = net_generic(net, xfrmi_net_id);
|
||||
int err;
|
||||
|
||||
xfrmi_unlink(xfrmn, xi);
|
||||
synchronize_net();
|
||||
err = xfrmi_change(xi, p);
|
||||
xfrmi_link(xfrmn, xi);
|
||||
netdev_state_change(xi->dev);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void xfrmi_get_stats64(struct net_device *dev,
|
||||
struct rtnl_link_stats64 *s)
|
||||
{
|
||||
int cpu;
|
||||
|
||||
if (!dev->tstats)
|
||||
return;
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
struct pcpu_sw_netstats *stats;
|
||||
struct pcpu_sw_netstats tmp;
|
||||
int start;
|
||||
|
||||
stats = per_cpu_ptr(dev->tstats, cpu);
|
||||
do {
|
||||
start = u64_stats_fetch_begin_irq(&stats->syncp);
|
||||
tmp.rx_packets = stats->rx_packets;
|
||||
tmp.rx_bytes = stats->rx_bytes;
|
||||
tmp.tx_packets = stats->tx_packets;
|
||||
tmp.tx_bytes = stats->tx_bytes;
|
||||
} while (u64_stats_fetch_retry_irq(&stats->syncp, start));
|
||||
|
||||
s->rx_packets += tmp.rx_packets;
|
||||
s->rx_bytes += tmp.rx_bytes;
|
||||
s->tx_packets += tmp.tx_packets;
|
||||
s->tx_bytes += tmp.tx_bytes;
|
||||
}
|
||||
|
||||
s->rx_dropped = dev->stats.rx_dropped;
|
||||
s->tx_dropped = dev->stats.tx_dropped;
|
||||
}
|
||||
|
||||
static int xfrmi_get_iflink(const struct net_device *dev)
|
||||
{
|
||||
struct xfrm_if *xi = netdev_priv(dev);
|
||||
|
||||
return xi->phydev->ifindex;
|
||||
}
|
||||
|
||||
|
||||
static const struct net_device_ops xfrmi_netdev_ops = {
|
||||
.ndo_init = xfrmi_dev_init,
|
||||
.ndo_uninit = xfrmi_dev_uninit,
|
||||
.ndo_start_xmit = xfrmi_xmit,
|
||||
.ndo_get_stats64 = xfrmi_get_stats64,
|
||||
.ndo_get_iflink = xfrmi_get_iflink,
|
||||
};
|
||||
|
||||
static void xfrmi_dev_setup(struct net_device *dev)
|
||||
{
|
||||
dev->netdev_ops = &xfrmi_netdev_ops;
|
||||
dev->type = ARPHRD_NONE;
|
||||
dev->hard_header_len = ETH_HLEN;
|
||||
dev->min_header_len = ETH_HLEN;
|
||||
dev->mtu = ETH_DATA_LEN;
|
||||
dev->min_mtu = ETH_MIN_MTU;
|
||||
dev->max_mtu = ETH_DATA_LEN;
|
||||
dev->addr_len = ETH_ALEN;
|
||||
dev->flags = IFF_NOARP;
|
||||
dev->needs_free_netdev = true;
|
||||
dev->priv_destructor = xfrmi_dev_free;
|
||||
netif_keep_dst(dev);
|
||||
}
|
||||
|
||||
static int xfrmi_dev_init(struct net_device *dev)
|
||||
{
|
||||
struct xfrm_if *xi = netdev_priv(dev);
|
||||
struct net_device *phydev = xi->phydev;
|
||||
int err;
|
||||
|
||||
dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
|
||||
if (!dev->tstats)
|
||||
return -ENOMEM;
|
||||
|
||||
err = gro_cells_init(&xi->gro_cells, dev);
|
||||
if (err) {
|
||||
free_percpu(dev->tstats);
|
||||
return err;
|
||||
}
|
||||
|
||||
dev->features |= NETIF_F_LLTX;
|
||||
|
||||
dev->needed_headroom = phydev->needed_headroom;
|
||||
dev->needed_tailroom = phydev->needed_tailroom;
|
||||
|
||||
if (is_zero_ether_addr(dev->dev_addr))
|
||||
eth_hw_addr_inherit(dev, phydev);
|
||||
if (is_zero_ether_addr(dev->broadcast))
|
||||
memcpy(dev->broadcast, phydev->broadcast, dev->addr_len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xfrmi_validate(struct nlattr *tb[], struct nlattr *data[],
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void xfrmi_netlink_parms(struct nlattr *data[],
|
||||
struct xfrm_if_parms *parms)
|
||||
{
|
||||
memset(parms, 0, sizeof(*parms));
|
||||
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
if (data[IFLA_XFRM_LINK])
|
||||
parms->link = nla_get_u32(data[IFLA_XFRM_LINK]);
|
||||
|
||||
if (data[IFLA_XFRM_IF_ID])
|
||||
parms->if_id = nla_get_u32(data[IFLA_XFRM_IF_ID]);
|
||||
}
|
||||
|
||||
static int xfrmi_newlink(struct net *src_net, struct net_device *dev,
|
||||
struct nlattr *tb[], struct nlattr *data[],
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct net *net = dev_net(dev);
|
||||
struct xfrm_if_parms *p;
|
||||
struct xfrm_if *xi;
|
||||
|
||||
xi = netdev_priv(dev);
|
||||
p = &xi->p;
|
||||
|
||||
xfrmi_netlink_parms(data, p);
|
||||
|
||||
if (!tb[IFLA_IFNAME])
|
||||
return -EINVAL;
|
||||
|
||||
nla_strlcpy(p->name, tb[IFLA_IFNAME], IFNAMSIZ);
|
||||
|
||||
xi = xfrmi_locate(net, p, 1);
|
||||
return PTR_ERR_OR_ZERO(xi);
|
||||
}
|
||||
|
||||
static void xfrmi_dellink(struct net_device *dev, struct list_head *head)
|
||||
{
|
||||
unregister_netdevice_queue(dev, head);
|
||||
}
|
||||
|
||||
static int xfrmi_changelink(struct net_device *dev, struct nlattr *tb[],
|
||||
struct nlattr *data[],
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct xfrm_if *xi = netdev_priv(dev);
|
||||
struct net *net = dev_net(dev);
|
||||
|
||||
xfrmi_netlink_parms(data, &xi->p);
|
||||
|
||||
xi = xfrmi_locate(net, &xi->p, 0);
|
||||
|
||||
if (IS_ERR_OR_NULL(xi)) {
|
||||
xi = netdev_priv(dev);
|
||||
} else {
|
||||
if (xi->dev != dev)
|
||||
return -EEXIST;
|
||||
}
|
||||
|
||||
return xfrmi_update(xi, &xi->p);
|
||||
}
|
||||
|
||||
static size_t xfrmi_get_size(const struct net_device *dev)
|
||||
{
|
||||
return
|
||||
/* IFLA_XFRM_LINK */
|
||||
nla_total_size(4) +
|
||||
/* IFLA_XFRM_IF_ID */
|
||||
nla_total_size(4) +
|
||||
0;
|
||||
}
|
||||
|
||||
static int xfrmi_fill_info(struct sk_buff *skb, const struct net_device *dev)
|
||||
{
|
||||
struct xfrm_if *xi = netdev_priv(dev);
|
||||
struct xfrm_if_parms *parm = &xi->p;
|
||||
|
||||
if (nla_put_u32(skb, IFLA_XFRM_LINK, parm->link) ||
|
||||
nla_put_u32(skb, IFLA_XFRM_IF_ID, parm->if_id))
|
||||
goto nla_put_failure;
|
||||
return 0;
|
||||
|
||||
nla_put_failure:
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
struct net *xfrmi_get_link_net(const struct net_device *dev)
|
||||
{
|
||||
struct xfrm_if *xi = netdev_priv(dev);
|
||||
|
||||
return dev_net(xi->phydev);
|
||||
}
|
||||
|
||||
static const struct nla_policy xfrmi_policy[IFLA_XFRM_MAX + 1] = {
|
||||
[IFLA_XFRM_LINK] = { .type = NLA_U32 },
|
||||
[IFLA_XFRM_IF_ID] = { .type = NLA_U32 },
|
||||
};
|
||||
|
||||
static struct rtnl_link_ops xfrmi_link_ops __read_mostly = {
|
||||
.kind = "xfrm",
|
||||
.maxtype = IFLA_XFRM_MAX,
|
||||
.policy = xfrmi_policy,
|
||||
.priv_size = sizeof(struct xfrm_if),
|
||||
.setup = xfrmi_dev_setup,
|
||||
.validate = xfrmi_validate,
|
||||
.newlink = xfrmi_newlink,
|
||||
.dellink = xfrmi_dellink,
|
||||
.changelink = xfrmi_changelink,
|
||||
.get_size = xfrmi_get_size,
|
||||
.fill_info = xfrmi_fill_info,
|
||||
.get_link_net = xfrmi_get_link_net,
|
||||
};
|
||||
|
||||
static void __net_exit xfrmi_destroy_interfaces(struct xfrmi_net *xfrmn)
|
||||
{
|
||||
struct xfrm_if *xi;
|
||||
LIST_HEAD(list);
|
||||
|
||||
xi = rtnl_dereference(xfrmn->xfrmi[0]);
|
||||
if (!xi)
|
||||
return;
|
||||
|
||||
unregister_netdevice_queue(xi->dev, &list);
|
||||
unregister_netdevice_many(&list);
|
||||
}
|
||||
|
||||
static int __net_init xfrmi_init_net(struct net *net)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __net_exit xfrmi_exit_net(struct net *net)
|
||||
{
|
||||
struct xfrmi_net *xfrmn = net_generic(net, xfrmi_net_id);
|
||||
|
||||
rtnl_lock();
|
||||
xfrmi_destroy_interfaces(xfrmn);
|
||||
rtnl_unlock();
|
||||
}
|
||||
|
||||
static struct pernet_operations xfrmi_net_ops = {
|
||||
.init = xfrmi_init_net,
|
||||
.exit = xfrmi_exit_net,
|
||||
.id = &xfrmi_net_id,
|
||||
.size = sizeof(struct xfrmi_net),
|
||||
};
|
||||
|
||||
static struct xfrm6_protocol xfrmi_esp6_protocol __read_mostly = {
|
||||
.handler = xfrm6_rcv,
|
||||
.cb_handler = xfrmi_rcv_cb,
|
||||
.err_handler = xfrmi6_err,
|
||||
.priority = 10,
|
||||
};
|
||||
|
||||
static struct xfrm6_protocol xfrmi_ah6_protocol __read_mostly = {
|
||||
.handler = xfrm6_rcv,
|
||||
.cb_handler = xfrmi_rcv_cb,
|
||||
.err_handler = xfrmi6_err,
|
||||
.priority = 10,
|
||||
};
|
||||
|
||||
static struct xfrm6_protocol xfrmi_ipcomp6_protocol __read_mostly = {
|
||||
.handler = xfrm6_rcv,
|
||||
.cb_handler = xfrmi_rcv_cb,
|
||||
.err_handler = xfrmi6_err,
|
||||
.priority = 10,
|
||||
};
|
||||
|
||||
static struct xfrm4_protocol xfrmi_esp4_protocol __read_mostly = {
|
||||
.handler = xfrm4_rcv,
|
||||
.input_handler = xfrm_input,
|
||||
.cb_handler = xfrmi_rcv_cb,
|
||||
.err_handler = xfrmi4_err,
|
||||
.priority = 10,
|
||||
};
|
||||
|
||||
static struct xfrm4_protocol xfrmi_ah4_protocol __read_mostly = {
|
||||
.handler = xfrm4_rcv,
|
||||
.input_handler = xfrm_input,
|
||||
.cb_handler = xfrmi_rcv_cb,
|
||||
.err_handler = xfrmi4_err,
|
||||
.priority = 10,
|
||||
};
|
||||
|
||||
static struct xfrm4_protocol xfrmi_ipcomp4_protocol __read_mostly = {
|
||||
.handler = xfrm4_rcv,
|
||||
.input_handler = xfrm_input,
|
||||
.cb_handler = xfrmi_rcv_cb,
|
||||
.err_handler = xfrmi4_err,
|
||||
.priority = 10,
|
||||
};
|
||||
|
||||
static int __init xfrmi4_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = xfrm4_protocol_register(&xfrmi_esp4_protocol, IPPROTO_ESP);
|
||||
if (err < 0)
|
||||
goto xfrm_proto_esp_failed;
|
||||
err = xfrm4_protocol_register(&xfrmi_ah4_protocol, IPPROTO_AH);
|
||||
if (err < 0)
|
||||
goto xfrm_proto_ah_failed;
|
||||
err = xfrm4_protocol_register(&xfrmi_ipcomp4_protocol, IPPROTO_COMP);
|
||||
if (err < 0)
|
||||
goto xfrm_proto_comp_failed;
|
||||
|
||||
return 0;
|
||||
|
||||
xfrm_proto_comp_failed:
|
||||
xfrm4_protocol_deregister(&xfrmi_ah4_protocol, IPPROTO_AH);
|
||||
xfrm_proto_ah_failed:
|
||||
xfrm4_protocol_deregister(&xfrmi_esp4_protocol, IPPROTO_ESP);
|
||||
xfrm_proto_esp_failed:
|
||||
return err;
|
||||
}
|
||||
|
||||
static void xfrmi4_fini(void)
|
||||
{
|
||||
xfrm4_protocol_deregister(&xfrmi_ipcomp4_protocol, IPPROTO_COMP);
|
||||
xfrm4_protocol_deregister(&xfrmi_ah4_protocol, IPPROTO_AH);
|
||||
xfrm4_protocol_deregister(&xfrmi_esp4_protocol, IPPROTO_ESP);
|
||||
}
|
||||
|
||||
static int __init xfrmi6_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = xfrm6_protocol_register(&xfrmi_esp6_protocol, IPPROTO_ESP);
|
||||
if (err < 0)
|
||||
goto xfrm_proto_esp_failed;
|
||||
err = xfrm6_protocol_register(&xfrmi_ah6_protocol, IPPROTO_AH);
|
||||
if (err < 0)
|
||||
goto xfrm_proto_ah_failed;
|
||||
err = xfrm6_protocol_register(&xfrmi_ipcomp6_protocol, IPPROTO_COMP);
|
||||
if (err < 0)
|
||||
goto xfrm_proto_comp_failed;
|
||||
|
||||
return 0;
|
||||
|
||||
xfrm_proto_comp_failed:
|
||||
xfrm6_protocol_deregister(&xfrmi_ah6_protocol, IPPROTO_AH);
|
||||
xfrm_proto_ah_failed:
|
||||
xfrm6_protocol_deregister(&xfrmi_esp6_protocol, IPPROTO_ESP);
|
||||
xfrm_proto_esp_failed:
|
||||
return err;
|
||||
}
|
||||
|
||||
static void xfrmi6_fini(void)
|
||||
{
|
||||
xfrm6_protocol_deregister(&xfrmi_ipcomp6_protocol, IPPROTO_COMP);
|
||||
xfrm6_protocol_deregister(&xfrmi_ah6_protocol, IPPROTO_AH);
|
||||
xfrm6_protocol_deregister(&xfrmi_esp6_protocol, IPPROTO_ESP);
|
||||
}
|
||||
|
||||
static const struct xfrm_if_cb xfrm_if_cb = {
|
||||
.decode_session = xfrmi_decode_session,
|
||||
};
|
||||
|
||||
static int __init xfrmi_init(void)
|
||||
{
|
||||
const char *msg;
|
||||
int err;
|
||||
|
||||
pr_info("IPsec XFRM device driver\n");
|
||||
|
||||
msg = "tunnel device";
|
||||
err = register_pernet_device(&xfrmi_net_ops);
|
||||
if (err < 0)
|
||||
goto pernet_dev_failed;
|
||||
|
||||
msg = "xfrm4 protocols";
|
||||
err = xfrmi4_init();
|
||||
if (err < 0)
|
||||
goto xfrmi4_failed;
|
||||
|
||||
msg = "xfrm6 protocols";
|
||||
err = xfrmi6_init();
|
||||
if (err < 0)
|
||||
goto xfrmi6_failed;
|
||||
|
||||
|
||||
msg = "netlink interface";
|
||||
err = rtnl_link_register(&xfrmi_link_ops);
|
||||
if (err < 0)
|
||||
goto rtnl_link_failed;
|
||||
|
||||
xfrm_if_register_cb(&xfrm_if_cb);
|
||||
|
||||
return err;
|
||||
|
||||
rtnl_link_failed:
|
||||
xfrmi6_fini();
|
||||
xfrmi6_failed:
|
||||
xfrmi4_fini();
|
||||
xfrmi4_failed:
|
||||
unregister_pernet_device(&xfrmi_net_ops);
|
||||
pernet_dev_failed:
|
||||
pr_err("xfrmi init: failed to register %s\n", msg);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __exit xfrmi_fini(void)
|
||||
{
|
||||
xfrm_if_unregister_cb();
|
||||
rtnl_link_unregister(&xfrmi_link_ops);
|
||||
xfrmi4_fini();
|
||||
xfrmi6_fini();
|
||||
unregister_pernet_device(&xfrmi_net_ops);
|
||||
}
|
||||
|
||||
module_init(xfrmi_init);
|
||||
module_exit(xfrmi_fini);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS_RTNL_LINK("xfrm");
|
||||
MODULE_ALIAS_NETDEV("xfrm0");
|
||||
MODULE_AUTHOR("Steffen Klassert");
|
||||
MODULE_DESCRIPTION("XFRM virtual interface");
|
@ -66,8 +66,7 @@ static int xfrm_output_one(struct sk_buff *skb, int err)
|
||||
goto error_nolock;
|
||||
}
|
||||
|
||||
if (x->props.output_mark)
|
||||
skb->mark = x->props.output_mark;
|
||||
skb->mark = xfrm_smark_get(skb->mark, x);
|
||||
|
||||
err = x->outer_mode->output(x, skb);
|
||||
if (err) {
|
||||
|
@ -47,6 +47,9 @@ struct xfrm_flo {
|
||||
|
||||
static DEFINE_PER_CPU(struct xfrm_dst *, xfrm_last_dst);
|
||||
static struct work_struct *xfrm_pcpu_work __read_mostly;
|
||||
static DEFINE_SPINLOCK(xfrm_if_cb_lock);
|
||||
static struct xfrm_if_cb const __rcu *xfrm_if_cb __read_mostly;
|
||||
|
||||
static DEFINE_SPINLOCK(xfrm_policy_afinfo_lock);
|
||||
static struct xfrm_policy_afinfo const __rcu *xfrm_policy_afinfo[AF_INET6 + 1]
|
||||
__read_mostly;
|
||||
@ -119,6 +122,12 @@ static const struct xfrm_policy_afinfo *xfrm_policy_get_afinfo(unsigned short fa
|
||||
return afinfo;
|
||||
}
|
||||
|
||||
/* Called with rcu_read_lock(). */
|
||||
static const struct xfrm_if_cb *xfrm_if_get_cb(void)
|
||||
{
|
||||
return rcu_dereference(xfrm_if_cb);
|
||||
}
|
||||
|
||||
struct dst_entry *__xfrm_dst_lookup(struct net *net, int tos, int oif,
|
||||
const xfrm_address_t *saddr,
|
||||
const xfrm_address_t *daddr,
|
||||
@ -748,6 +757,7 @@ int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl)
|
||||
newpos = NULL;
|
||||
hlist_for_each_entry(pol, chain, bydst) {
|
||||
if (pol->type == policy->type &&
|
||||
pol->if_id == policy->if_id &&
|
||||
!selector_cmp(&pol->selector, &policy->selector) &&
|
||||
xfrm_policy_mark_match(policy, pol) &&
|
||||
xfrm_sec_ctx_match(pol->security, policy->security) &&
|
||||
@ -799,8 +809,9 @@ int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl)
|
||||
}
|
||||
EXPORT_SYMBOL(xfrm_policy_insert);
|
||||
|
||||
struct xfrm_policy *xfrm_policy_bysel_ctx(struct net *net, u32 mark, u8 type,
|
||||
int dir, struct xfrm_selector *sel,
|
||||
struct xfrm_policy *xfrm_policy_bysel_ctx(struct net *net, u32 mark, u32 if_id,
|
||||
u8 type, int dir,
|
||||
struct xfrm_selector *sel,
|
||||
struct xfrm_sec_ctx *ctx, int delete,
|
||||
int *err)
|
||||
{
|
||||
@ -813,6 +824,7 @@ struct xfrm_policy *xfrm_policy_bysel_ctx(struct net *net, u32 mark, u8 type,
|
||||
ret = NULL;
|
||||
hlist_for_each_entry(pol, chain, bydst) {
|
||||
if (pol->type == type &&
|
||||
pol->if_id == if_id &&
|
||||
(mark & pol->mark.m) == pol->mark.v &&
|
||||
!selector_cmp(sel, &pol->selector) &&
|
||||
xfrm_sec_ctx_match(ctx, pol->security)) {
|
||||
@ -838,8 +850,9 @@ struct xfrm_policy *xfrm_policy_bysel_ctx(struct net *net, u32 mark, u8 type,
|
||||
}
|
||||
EXPORT_SYMBOL(xfrm_policy_bysel_ctx);
|
||||
|
||||
struct xfrm_policy *xfrm_policy_byid(struct net *net, u32 mark, u8 type,
|
||||
int dir, u32 id, int delete, int *err)
|
||||
struct xfrm_policy *xfrm_policy_byid(struct net *net, u32 mark, u32 if_id,
|
||||
u8 type, int dir, u32 id, int delete,
|
||||
int *err)
|
||||
{
|
||||
struct xfrm_policy *pol, *ret;
|
||||
struct hlist_head *chain;
|
||||
@ -854,6 +867,7 @@ struct xfrm_policy *xfrm_policy_byid(struct net *net, u32 mark, u8 type,
|
||||
ret = NULL;
|
||||
hlist_for_each_entry(pol, chain, byidx) {
|
||||
if (pol->type == type && pol->index == id &&
|
||||
pol->if_id == if_id &&
|
||||
(mark & pol->mark.m) == pol->mark.v) {
|
||||
xfrm_pol_hold(pol);
|
||||
if (delete) {
|
||||
@ -1057,13 +1071,14 @@ EXPORT_SYMBOL(xfrm_policy_walk_done);
|
||||
*/
|
||||
static int xfrm_policy_match(const struct xfrm_policy *pol,
|
||||
const struct flowi *fl,
|
||||
u8 type, u16 family, int dir)
|
||||
u8 type, u16 family, int dir, u32 if_id)
|
||||
{
|
||||
const struct xfrm_selector *sel = &pol->selector;
|
||||
int ret = -ESRCH;
|
||||
bool match;
|
||||
|
||||
if (pol->family != family ||
|
||||
pol->if_id != if_id ||
|
||||
(fl->flowi_mark & pol->mark.m) != pol->mark.v ||
|
||||
pol->type != type)
|
||||
return ret;
|
||||
@ -1078,7 +1093,8 @@ static int xfrm_policy_match(const struct xfrm_policy *pol,
|
||||
|
||||
static struct xfrm_policy *xfrm_policy_lookup_bytype(struct net *net, u8 type,
|
||||
const struct flowi *fl,
|
||||
u16 family, u8 dir)
|
||||
u16 family, u8 dir,
|
||||
u32 if_id)
|
||||
{
|
||||
int err;
|
||||
struct xfrm_policy *pol, *ret;
|
||||
@ -1102,7 +1118,7 @@ static struct xfrm_policy *xfrm_policy_lookup_bytype(struct net *net, u8 type,
|
||||
priority = ~0U;
|
||||
ret = NULL;
|
||||
hlist_for_each_entry_rcu(pol, chain, bydst) {
|
||||
err = xfrm_policy_match(pol, fl, type, family, dir);
|
||||
err = xfrm_policy_match(pol, fl, type, family, dir, if_id);
|
||||
if (err) {
|
||||
if (err == -ESRCH)
|
||||
continue;
|
||||
@ -1121,7 +1137,7 @@ static struct xfrm_policy *xfrm_policy_lookup_bytype(struct net *net, u8 type,
|
||||
if ((pol->priority >= priority) && ret)
|
||||
break;
|
||||
|
||||
err = xfrm_policy_match(pol, fl, type, family, dir);
|
||||
err = xfrm_policy_match(pol, fl, type, family, dir, if_id);
|
||||
if (err) {
|
||||
if (err == -ESRCH)
|
||||
continue;
|
||||
@ -1146,21 +1162,25 @@ fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct xfrm_policy *
|
||||
xfrm_policy_lookup(struct net *net, const struct flowi *fl, u16 family, u8 dir)
|
||||
static struct xfrm_policy *xfrm_policy_lookup(struct net *net,
|
||||
const struct flowi *fl,
|
||||
u16 family, u8 dir, u32 if_id)
|
||||
{
|
||||
#ifdef CONFIG_XFRM_SUB_POLICY
|
||||
struct xfrm_policy *pol;
|
||||
|
||||
pol = xfrm_policy_lookup_bytype(net, XFRM_POLICY_TYPE_SUB, fl, family, dir);
|
||||
pol = xfrm_policy_lookup_bytype(net, XFRM_POLICY_TYPE_SUB, fl, family,
|
||||
dir, if_id);
|
||||
if (pol != NULL)
|
||||
return pol;
|
||||
#endif
|
||||
return xfrm_policy_lookup_bytype(net, XFRM_POLICY_TYPE_MAIN, fl, family, dir);
|
||||
return xfrm_policy_lookup_bytype(net, XFRM_POLICY_TYPE_MAIN, fl, family,
|
||||
dir, if_id);
|
||||
}
|
||||
|
||||
static struct xfrm_policy *xfrm_sk_policy_lookup(const struct sock *sk, int dir,
|
||||
const struct flowi *fl, u16 family)
|
||||
const struct flowi *fl,
|
||||
u16 family, u32 if_id)
|
||||
{
|
||||
struct xfrm_policy *pol;
|
||||
|
||||
@ -1178,7 +1198,8 @@ static struct xfrm_policy *xfrm_sk_policy_lookup(const struct sock *sk, int dir,
|
||||
|
||||
match = xfrm_selector_match(&pol->selector, fl, family);
|
||||
if (match) {
|
||||
if ((sk->sk_mark & pol->mark.m) != pol->mark.v) {
|
||||
if ((sk->sk_mark & pol->mark.m) != pol->mark.v ||
|
||||
pol->if_id != if_id) {
|
||||
pol = NULL;
|
||||
goto out;
|
||||
}
|
||||
@ -1306,6 +1327,7 @@ static struct xfrm_policy *clone_policy(const struct xfrm_policy *old, int dir)
|
||||
newp->lft = old->lft;
|
||||
newp->curlft = old->curlft;
|
||||
newp->mark = old->mark;
|
||||
newp->if_id = old->if_id;
|
||||
newp->action = old->action;
|
||||
newp->flags = old->flags;
|
||||
newp->xfrm_nr = old->xfrm_nr;
|
||||
@ -1391,7 +1413,8 @@ xfrm_tmpl_resolve_one(struct xfrm_policy *policy, const struct flowi *fl,
|
||||
}
|
||||
}
|
||||
|
||||
x = xfrm_state_find(remote, local, fl, tmpl, policy, &error, family);
|
||||
x = xfrm_state_find(remote, local, fl, tmpl, policy, &error,
|
||||
family, policy->if_id);
|
||||
|
||||
if (x && x->km.state == XFRM_STATE_VALID) {
|
||||
xfrm[nx++] = x;
|
||||
@ -1605,10 +1628,11 @@ static struct dst_entry *xfrm_bundle_create(struct xfrm_policy *policy,
|
||||
dst_copy_metrics(dst1, dst);
|
||||
|
||||
if (xfrm[i]->props.mode != XFRM_MODE_TRANSPORT) {
|
||||
__u32 mark = xfrm_smark_get(fl->flowi_mark, xfrm[i]);
|
||||
|
||||
family = xfrm[i]->props.family;
|
||||
dst = xfrm_dst_lookup(xfrm[i], tos, fl->flowi_oif,
|
||||
&saddr, &daddr, family,
|
||||
xfrm[i]->props.output_mark);
|
||||
&saddr, &daddr, family, mark);
|
||||
err = PTR_ERR(dst);
|
||||
if (IS_ERR(dst))
|
||||
goto put_states;
|
||||
@ -1693,7 +1717,8 @@ static int xfrm_expand_policies(const struct flowi *fl, u16 family,
|
||||
pols[1] = xfrm_policy_lookup_bytype(xp_net(pols[0]),
|
||||
XFRM_POLICY_TYPE_MAIN,
|
||||
fl, family,
|
||||
XFRM_POLICY_OUT);
|
||||
XFRM_POLICY_OUT,
|
||||
pols[0]->if_id);
|
||||
if (pols[1]) {
|
||||
if (IS_ERR(pols[1])) {
|
||||
xfrm_pols_put(pols, *num_pols);
|
||||
@ -2049,8 +2074,10 @@ free_dst:
|
||||
goto out;
|
||||
}
|
||||
|
||||
static struct xfrm_dst *
|
||||
xfrm_bundle_lookup(struct net *net, const struct flowi *fl, u16 family, u8 dir, struct xfrm_flo *xflo)
|
||||
static struct xfrm_dst *xfrm_bundle_lookup(struct net *net,
|
||||
const struct flowi *fl,
|
||||
u16 family, u8 dir,
|
||||
struct xfrm_flo *xflo, u32 if_id)
|
||||
{
|
||||
struct xfrm_policy *pols[XFRM_POLICY_TYPE_MAX];
|
||||
int num_pols = 0, num_xfrms = 0, err;
|
||||
@ -2059,7 +2086,7 @@ xfrm_bundle_lookup(struct net *net, const struct flowi *fl, u16 family, u8 dir,
|
||||
/* Resolve policies to use if we couldn't get them from
|
||||
* previous cache entry */
|
||||
num_pols = 1;
|
||||
pols[0] = xfrm_policy_lookup(net, fl, family, dir);
|
||||
pols[0] = xfrm_policy_lookup(net, fl, family, dir, if_id);
|
||||
err = xfrm_expand_policies(fl, family, pols,
|
||||
&num_pols, &num_xfrms);
|
||||
if (err < 0)
|
||||
@ -2076,6 +2103,11 @@ xfrm_bundle_lookup(struct net *net, const struct flowi *fl, u16 family, u8 dir,
|
||||
|
||||
if (IS_ERR(xdst)) {
|
||||
err = PTR_ERR(xdst);
|
||||
if (err == -EREMOTE) {
|
||||
xfrm_pols_put(pols, num_pols);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (err != -EAGAIN)
|
||||
goto error;
|
||||
goto make_dummy_bundle;
|
||||
@ -2125,14 +2157,19 @@ static struct dst_entry *make_blackhole(struct net *net, u16 family,
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Main function: finds/creates a bundle for given flow.
|
||||
/* Finds/creates a bundle for given flow and if_id
|
||||
*
|
||||
* At the moment we eat a raw IP route. Mostly to speed up lookups
|
||||
* on interfaces with disabled IPsec.
|
||||
*
|
||||
* xfrm_lookup uses an if_id of 0 by default, and is provided for
|
||||
* compatibility
|
||||
*/
|
||||
struct dst_entry *xfrm_lookup(struct net *net, struct dst_entry *dst_orig,
|
||||
struct dst_entry *xfrm_lookup_with_ifid(struct net *net,
|
||||
struct dst_entry *dst_orig,
|
||||
const struct flowi *fl,
|
||||
const struct sock *sk, int flags)
|
||||
const struct sock *sk,
|
||||
int flags, u32 if_id)
|
||||
{
|
||||
struct xfrm_policy *pols[XFRM_POLICY_TYPE_MAX];
|
||||
struct xfrm_dst *xdst;
|
||||
@ -2148,7 +2185,8 @@ struct dst_entry *xfrm_lookup(struct net *net, struct dst_entry *dst_orig,
|
||||
sk = sk_const_to_full_sk(sk);
|
||||
if (sk && sk->sk_policy[XFRM_POLICY_OUT]) {
|
||||
num_pols = 1;
|
||||
pols[0] = xfrm_sk_policy_lookup(sk, XFRM_POLICY_OUT, fl, family);
|
||||
pols[0] = xfrm_sk_policy_lookup(sk, XFRM_POLICY_OUT, fl, family,
|
||||
if_id);
|
||||
err = xfrm_expand_policies(fl, family, pols,
|
||||
&num_pols, &num_xfrms);
|
||||
if (err < 0)
|
||||
@ -2169,6 +2207,9 @@ struct dst_entry *xfrm_lookup(struct net *net, struct dst_entry *dst_orig,
|
||||
if (IS_ERR(xdst)) {
|
||||
xfrm_pols_put(pols, num_pols);
|
||||
err = PTR_ERR(xdst);
|
||||
if (err == -EREMOTE)
|
||||
goto nopol;
|
||||
|
||||
goto dropdst;
|
||||
} else if (xdst == NULL) {
|
||||
num_xfrms = 0;
|
||||
@ -2191,7 +2232,7 @@ struct dst_entry *xfrm_lookup(struct net *net, struct dst_entry *dst_orig,
|
||||
!net->xfrm.policy_count[XFRM_POLICY_OUT])
|
||||
goto nopol;
|
||||
|
||||
xdst = xfrm_bundle_lookup(net, fl, family, dir, &xflo);
|
||||
xdst = xfrm_bundle_lookup(net, fl, family, dir, &xflo, if_id);
|
||||
if (xdst == NULL)
|
||||
goto nopol;
|
||||
if (IS_ERR(xdst)) {
|
||||
@ -2272,6 +2313,19 @@ dropdst:
|
||||
xfrm_pols_put(pols, drop_pols);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
EXPORT_SYMBOL(xfrm_lookup_with_ifid);
|
||||
|
||||
/* Main function: finds/creates a bundle for given flow.
|
||||
*
|
||||
* At the moment we eat a raw IP route. Mostly to speed up lookups
|
||||
* on interfaces with disabled IPsec.
|
||||
*/
|
||||
struct dst_entry *xfrm_lookup(struct net *net, struct dst_entry *dst_orig,
|
||||
const struct flowi *fl, const struct sock *sk,
|
||||
int flags)
|
||||
{
|
||||
return xfrm_lookup_with_ifid(net, dst_orig, fl, sk, flags, 0);
|
||||
}
|
||||
EXPORT_SYMBOL(xfrm_lookup);
|
||||
|
||||
/* Callers of xfrm_lookup_route() must ensure a call to dst_output().
|
||||
@ -2370,6 +2424,7 @@ int __xfrm_decode_session(struct sk_buff *skb, struct flowi *fl,
|
||||
return -EAFNOSUPPORT;
|
||||
|
||||
afinfo->decode_session(skb, fl, reverse);
|
||||
|
||||
err = security_xfrm_decode_session(skb, &fl->flowi_secid);
|
||||
rcu_read_unlock();
|
||||
return err;
|
||||
@ -2400,6 +2455,19 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
|
||||
int reverse;
|
||||
struct flowi fl;
|
||||
int xerr_idx = -1;
|
||||
const struct xfrm_if_cb *ifcb;
|
||||
struct xfrm_if *xi;
|
||||
u32 if_id = 0;
|
||||
|
||||
rcu_read_lock();
|
||||
ifcb = xfrm_if_get_cb();
|
||||
|
||||
if (ifcb) {
|
||||
xi = ifcb->decode_session(skb);
|
||||
if (xi)
|
||||
if_id = xi->p.if_id;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
reverse = dir & ~XFRM_POLICY_MASK;
|
||||
dir &= XFRM_POLICY_MASK;
|
||||
@ -2427,7 +2495,7 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
|
||||
pol = NULL;
|
||||
sk = sk_to_full_sk(sk);
|
||||
if (sk && sk->sk_policy[dir]) {
|
||||
pol = xfrm_sk_policy_lookup(sk, dir, &fl, family);
|
||||
pol = xfrm_sk_policy_lookup(sk, dir, &fl, family, if_id);
|
||||
if (IS_ERR(pol)) {
|
||||
XFRM_INC_STATS(net, LINUX_MIB_XFRMINPOLERROR);
|
||||
return 0;
|
||||
@ -2435,7 +2503,7 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
|
||||
}
|
||||
|
||||
if (!pol)
|
||||
pol = xfrm_policy_lookup(net, &fl, family, dir);
|
||||
pol = xfrm_policy_lookup(net, &fl, family, dir, if_id);
|
||||
|
||||
if (IS_ERR(pol)) {
|
||||
XFRM_INC_STATS(net, LINUX_MIB_XFRMINPOLERROR);
|
||||
@ -2459,7 +2527,7 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
|
||||
if (pols[0]->type != XFRM_POLICY_TYPE_MAIN) {
|
||||
pols[1] = xfrm_policy_lookup_bytype(net, XFRM_POLICY_TYPE_MAIN,
|
||||
&fl, family,
|
||||
XFRM_POLICY_IN);
|
||||
XFRM_POLICY_IN, if_id);
|
||||
if (pols[1]) {
|
||||
if (IS_ERR(pols[1])) {
|
||||
XFRM_INC_STATS(net, LINUX_MIB_XFRMINPOLERROR);
|
||||
@ -2823,6 +2891,21 @@ void xfrm_policy_unregister_afinfo(const struct xfrm_policy_afinfo *afinfo)
|
||||
}
|
||||
EXPORT_SYMBOL(xfrm_policy_unregister_afinfo);
|
||||
|
||||
void xfrm_if_register_cb(const struct xfrm_if_cb *ifcb)
|
||||
{
|
||||
spin_lock(&xfrm_if_cb_lock);
|
||||
rcu_assign_pointer(xfrm_if_cb, ifcb);
|
||||
spin_unlock(&xfrm_if_cb_lock);
|
||||
}
|
||||
EXPORT_SYMBOL(xfrm_if_register_cb);
|
||||
|
||||
void xfrm_if_unregister_cb(void)
|
||||
{
|
||||
RCU_INIT_POINTER(xfrm_if_cb, NULL);
|
||||
synchronize_rcu();
|
||||
}
|
||||
EXPORT_SYMBOL(xfrm_if_unregister_cb);
|
||||
|
||||
#ifdef CONFIG_XFRM_STATISTICS
|
||||
static int __net_init xfrm_statistics_init(struct net *net)
|
||||
{
|
||||
@ -3004,6 +3087,9 @@ void __init xfrm_init(void)
|
||||
register_pernet_subsys(&xfrm_net_ops);
|
||||
seqcount_init(&xfrm_policy_hash_generation);
|
||||
xfrm_input_init();
|
||||
|
||||
RCU_INIT_POINTER(xfrm_if_cb, NULL);
|
||||
synchronize_rcu();
|
||||
}
|
||||
|
||||
#ifdef CONFIG_AUDITSYSCALL
|
||||
|
@ -931,7 +931,7 @@ struct xfrm_state *
|
||||
xfrm_state_find(const xfrm_address_t *daddr, const xfrm_address_t *saddr,
|
||||
const struct flowi *fl, struct xfrm_tmpl *tmpl,
|
||||
struct xfrm_policy *pol, int *err,
|
||||
unsigned short family)
|
||||
unsigned short family, u32 if_id)
|
||||
{
|
||||
static xfrm_address_t saddr_wildcard = { };
|
||||
struct net *net = xp_net(pol);
|
||||
@ -955,6 +955,7 @@ xfrm_state_find(const xfrm_address_t *daddr, const xfrm_address_t *saddr,
|
||||
if (x->props.family == encap_family &&
|
||||
x->props.reqid == tmpl->reqid &&
|
||||
(mark & x->mark.m) == x->mark.v &&
|
||||
x->if_id == if_id &&
|
||||
!(x->props.flags & XFRM_STATE_WILDRECV) &&
|
||||
xfrm_state_addr_check(x, daddr, saddr, encap_family) &&
|
||||
tmpl->mode == x->props.mode &&
|
||||
@ -971,6 +972,7 @@ xfrm_state_find(const xfrm_address_t *daddr, const xfrm_address_t *saddr,
|
||||
if (x->props.family == encap_family &&
|
||||
x->props.reqid == tmpl->reqid &&
|
||||
(mark & x->mark.m) == x->mark.v &&
|
||||
x->if_id == if_id &&
|
||||
!(x->props.flags & XFRM_STATE_WILDRECV) &&
|
||||
xfrm_addr_equal(&x->id.daddr, daddr, encap_family) &&
|
||||
tmpl->mode == x->props.mode &&
|
||||
@ -1010,6 +1012,7 @@ found:
|
||||
* to current session. */
|
||||
xfrm_init_tempstate(x, fl, tmpl, daddr, saddr, family);
|
||||
memcpy(&x->mark, &pol->mark, sizeof(x->mark));
|
||||
x->if_id = if_id;
|
||||
|
||||
error = security_xfrm_state_alloc_acquire(x, pol->security, fl->flowi_secid);
|
||||
if (error) {
|
||||
@ -1067,7 +1070,7 @@ out:
|
||||
}
|
||||
|
||||
struct xfrm_state *
|
||||
xfrm_stateonly_find(struct net *net, u32 mark,
|
||||
xfrm_stateonly_find(struct net *net, u32 mark, u32 if_id,
|
||||
xfrm_address_t *daddr, xfrm_address_t *saddr,
|
||||
unsigned short family, u8 mode, u8 proto, u32 reqid)
|
||||
{
|
||||
@ -1080,6 +1083,7 @@ xfrm_stateonly_find(struct net *net, u32 mark,
|
||||
if (x->props.family == family &&
|
||||
x->props.reqid == reqid &&
|
||||
(mark & x->mark.m) == x->mark.v &&
|
||||
x->if_id == if_id &&
|
||||
!(x->props.flags & XFRM_STATE_WILDRECV) &&
|
||||
xfrm_state_addr_check(x, daddr, saddr, family) &&
|
||||
mode == x->props.mode &&
|
||||
@ -1160,11 +1164,13 @@ static void __xfrm_state_bump_genids(struct xfrm_state *xnew)
|
||||
struct xfrm_state *x;
|
||||
unsigned int h;
|
||||
u32 mark = xnew->mark.v & xnew->mark.m;
|
||||
u32 if_id = xnew->if_id;
|
||||
|
||||
h = xfrm_dst_hash(net, &xnew->id.daddr, &xnew->props.saddr, reqid, family);
|
||||
hlist_for_each_entry(x, net->xfrm.state_bydst+h, bydst) {
|
||||
if (x->props.family == family &&
|
||||
x->props.reqid == reqid &&
|
||||
x->if_id == if_id &&
|
||||
(mark & x->mark.m) == x->mark.v &&
|
||||
xfrm_addr_equal(&x->id.daddr, &xnew->id.daddr, family) &&
|
||||
xfrm_addr_equal(&x->props.saddr, &xnew->props.saddr, family))
|
||||
@ -1187,7 +1193,7 @@ EXPORT_SYMBOL(xfrm_state_insert);
|
||||
static struct xfrm_state *__find_acq_core(struct net *net,
|
||||
const struct xfrm_mark *m,
|
||||
unsigned short family, u8 mode,
|
||||
u32 reqid, u8 proto,
|
||||
u32 reqid, u32 if_id, u8 proto,
|
||||
const xfrm_address_t *daddr,
|
||||
const xfrm_address_t *saddr,
|
||||
int create)
|
||||
@ -1242,6 +1248,7 @@ static struct xfrm_state *__find_acq_core(struct net *net,
|
||||
x->props.family = family;
|
||||
x->props.mode = mode;
|
||||
x->props.reqid = reqid;
|
||||
x->if_id = if_id;
|
||||
x->mark.v = m->v;
|
||||
x->mark.m = m->m;
|
||||
x->lft.hard_add_expires_seconds = net->xfrm.sysctl_acq_expires;
|
||||
@ -1296,7 +1303,7 @@ int xfrm_state_add(struct xfrm_state *x)
|
||||
|
||||
if (use_spi && !x1)
|
||||
x1 = __find_acq_core(net, &x->mark, family, x->props.mode,
|
||||
x->props.reqid, x->id.proto,
|
||||
x->props.reqid, x->if_id, x->id.proto,
|
||||
&x->id.daddr, &x->props.saddr, 0);
|
||||
|
||||
__xfrm_state_bump_genids(x);
|
||||
@ -1395,6 +1402,7 @@ static struct xfrm_state *xfrm_state_clone(struct xfrm_state *orig,
|
||||
x->props.flags = orig->props.flags;
|
||||
x->props.extra_flags = orig->props.extra_flags;
|
||||
|
||||
x->if_id = orig->if_id;
|
||||
x->tfcpad = orig->tfcpad;
|
||||
x->replay_maxdiff = orig->replay_maxdiff;
|
||||
x->replay_maxage = orig->replay_maxage;
|
||||
@ -1550,6 +1558,19 @@ out:
|
||||
if (x1->curlft.use_time)
|
||||
xfrm_state_check_expire(x1);
|
||||
|
||||
if (x->props.smark.m || x->props.smark.v || x->if_id) {
|
||||
spin_lock_bh(&net->xfrm.xfrm_state_lock);
|
||||
|
||||
if (x->props.smark.m || x->props.smark.v)
|
||||
x1->props.smark = x->props.smark;
|
||||
|
||||
if (x->if_id)
|
||||
x1->if_id = x->if_id;
|
||||
|
||||
__xfrm_state_bump_genids(x1);
|
||||
spin_unlock_bh(&net->xfrm.xfrm_state_lock);
|
||||
}
|
||||
|
||||
err = 0;
|
||||
x->km.state = XFRM_STATE_DEAD;
|
||||
__xfrm_state_put(x);
|
||||
@ -1613,13 +1634,13 @@ EXPORT_SYMBOL(xfrm_state_lookup_byaddr);
|
||||
|
||||
struct xfrm_state *
|
||||
xfrm_find_acq(struct net *net, const struct xfrm_mark *mark, u8 mode, u32 reqid,
|
||||
u8 proto, const xfrm_address_t *daddr,
|
||||
u32 if_id, u8 proto, const xfrm_address_t *daddr,
|
||||
const xfrm_address_t *saddr, int create, unsigned short family)
|
||||
{
|
||||
struct xfrm_state *x;
|
||||
|
||||
spin_lock_bh(&net->xfrm.xfrm_state_lock);
|
||||
x = __find_acq_core(net, mark, family, mode, reqid, proto, daddr, saddr, create);
|
||||
x = __find_acq_core(net, mark, family, mode, reqid, if_id, proto, daddr, saddr, create);
|
||||
spin_unlock_bh(&net->xfrm.xfrm_state_lock);
|
||||
|
||||
return x;
|
||||
|
@ -533,6 +533,19 @@ static void xfrm_update_ae_params(struct xfrm_state *x, struct nlattr **attrs,
|
||||
x->replay_maxdiff = nla_get_u32(rt);
|
||||
}
|
||||
|
||||
static void xfrm_smark_init(struct nlattr **attrs, struct xfrm_mark *m)
|
||||
{
|
||||
if (attrs[XFRMA_SET_MARK]) {
|
||||
m->v = nla_get_u32(attrs[XFRMA_SET_MARK]);
|
||||
if (attrs[XFRMA_SET_MARK_MASK])
|
||||
m->m = nla_get_u32(attrs[XFRMA_SET_MARK_MASK]);
|
||||
else
|
||||
m->m = 0xffffffff;
|
||||
} else {
|
||||
m->v = m->m = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static struct xfrm_state *xfrm_state_construct(struct net *net,
|
||||
struct xfrm_usersa_info *p,
|
||||
struct nlattr **attrs,
|
||||
@ -585,8 +598,10 @@ static struct xfrm_state *xfrm_state_construct(struct net *net,
|
||||
|
||||
xfrm_mark_get(attrs, &x->mark);
|
||||
|
||||
if (attrs[XFRMA_OUTPUT_MARK])
|
||||
x->props.output_mark = nla_get_u32(attrs[XFRMA_OUTPUT_MARK]);
|
||||
xfrm_smark_init(attrs, &x->props.smark);
|
||||
|
||||
if (attrs[XFRMA_IF_ID])
|
||||
x->if_id = nla_get_u32(attrs[XFRMA_IF_ID]);
|
||||
|
||||
err = __xfrm_init_state(x, false, attrs[XFRMA_OFFLOAD_DEV]);
|
||||
if (err)
|
||||
@ -826,6 +841,18 @@ static int copy_to_user_auth(struct xfrm_algo_auth *auth, struct sk_buff *skb)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xfrm_smark_put(struct sk_buff *skb, struct xfrm_mark *m)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (m->v | m->m) {
|
||||
ret = nla_put_u32(skb, XFRMA_SET_MARK, m->v);
|
||||
if (!ret)
|
||||
ret = nla_put_u32(skb, XFRMA_SET_MARK_MASK, m->m);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Don't change this without updating xfrm_sa_len! */
|
||||
static int copy_to_user_state_extra(struct xfrm_state *x,
|
||||
struct xfrm_usersa_info *p,
|
||||
@ -889,6 +916,11 @@ static int copy_to_user_state_extra(struct xfrm_state *x,
|
||||
ret = xfrm_mark_put(skb, &x->mark);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = xfrm_smark_put(skb, &x->props.smark);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (x->replay_esn)
|
||||
ret = nla_put(skb, XFRMA_REPLAY_ESN_VAL,
|
||||
xfrm_replay_state_esn_len(x->replay_esn),
|
||||
@ -902,8 +934,8 @@ static int copy_to_user_state_extra(struct xfrm_state *x,
|
||||
ret = copy_user_offload(&x->xso, skb);
|
||||
if (ret)
|
||||
goto out;
|
||||
if (x->props.output_mark) {
|
||||
ret = nla_put_u32(skb, XFRMA_OUTPUT_MARK, x->props.output_mark);
|
||||
if (x->if_id) {
|
||||
ret = nla_put_u32(skb, XFRMA_IF_ID, x->if_id);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
@ -1255,6 +1287,7 @@ static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh,
|
||||
int err;
|
||||
u32 mark;
|
||||
struct xfrm_mark m;
|
||||
u32 if_id = 0;
|
||||
|
||||
p = nlmsg_data(nlh);
|
||||
err = verify_spi_info(p->info.id.proto, p->min, p->max);
|
||||
@ -1267,6 +1300,10 @@ static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh,
|
||||
x = NULL;
|
||||
|
||||
mark = xfrm_mark_get(attrs, &m);
|
||||
|
||||
if (attrs[XFRMA_IF_ID])
|
||||
if_id = nla_get_u32(attrs[XFRMA_IF_ID]);
|
||||
|
||||
if (p->info.seq) {
|
||||
x = xfrm_find_acq_byseq(net, mark, p->info.seq);
|
||||
if (x && !xfrm_addr_equal(&x->id.daddr, daddr, family)) {
|
||||
@ -1277,7 +1314,7 @@ static int xfrm_alloc_userspi(struct sk_buff *skb, struct nlmsghdr *nlh,
|
||||
|
||||
if (!x)
|
||||
x = xfrm_find_acq(net, &m, p->info.mode, p->info.reqid,
|
||||
p->info.id.proto, daddr,
|
||||
if_id, p->info.id.proto, daddr,
|
||||
&p->info.saddr, 1,
|
||||
family);
|
||||
err = -ENOENT;
|
||||
@ -1579,6 +1616,9 @@ static struct xfrm_policy *xfrm_policy_construct(struct net *net, struct xfrm_us
|
||||
|
||||
xfrm_mark_get(attrs, &xp->mark);
|
||||
|
||||
if (attrs[XFRMA_IF_ID])
|
||||
xp->if_id = nla_get_u32(attrs[XFRMA_IF_ID]);
|
||||
|
||||
return xp;
|
||||
error:
|
||||
*errp = err;
|
||||
@ -1726,6 +1766,8 @@ static int dump_one_policy(struct xfrm_policy *xp, int dir, int count, void *ptr
|
||||
err = copy_to_user_policy_type(xp->type, skb);
|
||||
if (!err)
|
||||
err = xfrm_mark_put(skb, &xp->mark);
|
||||
if (!err)
|
||||
err = xfrm_if_id_put(skb, xp->if_id);
|
||||
if (err) {
|
||||
nlmsg_cancel(skb, nlh);
|
||||
return err;
|
||||
@ -1807,6 +1849,7 @@ static int xfrm_get_policy(struct sk_buff *skb, struct nlmsghdr *nlh,
|
||||
int delete;
|
||||
struct xfrm_mark m;
|
||||
u32 mark = xfrm_mark_get(attrs, &m);
|
||||
u32 if_id = 0;
|
||||
|
||||
p = nlmsg_data(nlh);
|
||||
delete = nlh->nlmsg_type == XFRM_MSG_DELPOLICY;
|
||||
@ -1819,8 +1862,11 @@ static int xfrm_get_policy(struct sk_buff *skb, struct nlmsghdr *nlh,
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (attrs[XFRMA_IF_ID])
|
||||
if_id = nla_get_u32(attrs[XFRMA_IF_ID]);
|
||||
|
||||
if (p->index)
|
||||
xp = xfrm_policy_byid(net, mark, type, p->dir, p->index, delete, &err);
|
||||
xp = xfrm_policy_byid(net, mark, if_id, type, p->dir, p->index, delete, &err);
|
||||
else {
|
||||
struct nlattr *rt = attrs[XFRMA_SEC_CTX];
|
||||
struct xfrm_sec_ctx *ctx;
|
||||
@ -1837,7 +1883,7 @@ static int xfrm_get_policy(struct sk_buff *skb, struct nlmsghdr *nlh,
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
xp = xfrm_policy_bysel_ctx(net, mark, type, p->dir, &p->sel,
|
||||
xp = xfrm_policy_bysel_ctx(net, mark, if_id, type, p->dir, &p->sel,
|
||||
ctx, delete, &err);
|
||||
security_xfrm_policy_free(ctx);
|
||||
}
|
||||
@ -1960,6 +2006,10 @@ static int build_aevent(struct sk_buff *skb, struct xfrm_state *x, const struct
|
||||
if (err)
|
||||
goto out_cancel;
|
||||
|
||||
err = xfrm_if_id_put(skb, x->if_id);
|
||||
if (err)
|
||||
goto out_cancel;
|
||||
|
||||
nlmsg_end(skb, nlh);
|
||||
return 0;
|
||||
|
||||
@ -2101,6 +2151,7 @@ static int xfrm_add_pol_expire(struct sk_buff *skb, struct nlmsghdr *nlh,
|
||||
int err = -ENOENT;
|
||||
struct xfrm_mark m;
|
||||
u32 mark = xfrm_mark_get(attrs, &m);
|
||||
u32 if_id = 0;
|
||||
|
||||
err = copy_from_user_policy_type(&type, attrs);
|
||||
if (err)
|
||||
@ -2110,8 +2161,11 @@ static int xfrm_add_pol_expire(struct sk_buff *skb, struct nlmsghdr *nlh,
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (attrs[XFRMA_IF_ID])
|
||||
if_id = nla_get_u32(attrs[XFRMA_IF_ID]);
|
||||
|
||||
if (p->index)
|
||||
xp = xfrm_policy_byid(net, mark, type, p->dir, p->index, 0, &err);
|
||||
xp = xfrm_policy_byid(net, mark, if_id, type, p->dir, p->index, 0, &err);
|
||||
else {
|
||||
struct nlattr *rt = attrs[XFRMA_SEC_CTX];
|
||||
struct xfrm_sec_ctx *ctx;
|
||||
@ -2128,7 +2182,7 @@ static int xfrm_add_pol_expire(struct sk_buff *skb, struct nlmsghdr *nlh,
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
xp = xfrm_policy_bysel_ctx(net, mark, type, p->dir,
|
||||
xp = xfrm_policy_bysel_ctx(net, mark, if_id, type, p->dir,
|
||||
&p->sel, ctx, 0, &err);
|
||||
security_xfrm_policy_free(ctx);
|
||||
}
|
||||
@ -2509,7 +2563,9 @@ static const struct nla_policy xfrma_policy[XFRMA_MAX+1] = {
|
||||
[XFRMA_PROTO] = { .type = NLA_U8 },
|
||||
[XFRMA_ADDRESS_FILTER] = { .len = sizeof(struct xfrm_address_filter) },
|
||||
[XFRMA_OFFLOAD_DEV] = { .len = sizeof(struct xfrm_user_offload) },
|
||||
[XFRMA_OUTPUT_MARK] = { .len = NLA_U32 },
|
||||
[XFRMA_SET_MARK] = { .type = NLA_U32 },
|
||||
[XFRMA_SET_MARK_MASK] = { .type = NLA_U32 },
|
||||
[XFRMA_IF_ID] = { .type = NLA_U32 },
|
||||
};
|
||||
|
||||
static const struct nla_policy xfrma_spd_policy[XFRMA_SPD_MAX+1] = {
|
||||
@ -2641,6 +2697,10 @@ static int build_expire(struct sk_buff *skb, struct xfrm_state *x, const struct
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = xfrm_if_id_put(skb, x->if_id);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
nlmsg_end(skb, nlh);
|
||||
return 0;
|
||||
}
|
||||
@ -2734,8 +2794,12 @@ static inline size_t xfrm_sa_len(struct xfrm_state *x)
|
||||
l += nla_total_size(sizeof(x->props.extra_flags));
|
||||
if (x->xso.dev)
|
||||
l += nla_total_size(sizeof(x->xso));
|
||||
if (x->props.output_mark)
|
||||
l += nla_total_size(sizeof(x->props.output_mark));
|
||||
if (x->props.smark.v | x->props.smark.m) {
|
||||
l += nla_total_size(sizeof(x->props.smark.v));
|
||||
l += nla_total_size(sizeof(x->props.smark.m));
|
||||
}
|
||||
if (x->if_id)
|
||||
l += nla_total_size(sizeof(x->if_id));
|
||||
|
||||
/* Must count x->lastused as it may become non-zero behind our back. */
|
||||
l += nla_total_size_64bit(sizeof(u64));
|
||||
@ -2864,6 +2928,8 @@ static int build_acquire(struct sk_buff *skb, struct xfrm_state *x,
|
||||
err = copy_to_user_policy_type(xp->type, skb);
|
||||
if (!err)
|
||||
err = xfrm_mark_put(skb, &xp->mark);
|
||||
if (!err)
|
||||
err = xfrm_if_id_put(skb, xp->if_id);
|
||||
if (err) {
|
||||
nlmsg_cancel(skb, nlh);
|
||||
return err;
|
||||
@ -2979,6 +3045,8 @@ static int build_polexpire(struct sk_buff *skb, struct xfrm_policy *xp,
|
||||
err = copy_to_user_policy_type(xp->type, skb);
|
||||
if (!err)
|
||||
err = xfrm_mark_put(skb, &xp->mark);
|
||||
if (!err)
|
||||
err = xfrm_if_id_put(skb, xp->if_id);
|
||||
if (err) {
|
||||
nlmsg_cancel(skb, nlh);
|
||||
return err;
|
||||
@ -3058,6 +3126,8 @@ static int xfrm_notify_policy(struct xfrm_policy *xp, int dir, const struct km_e
|
||||
err = copy_to_user_policy_type(xp->type, skb);
|
||||
if (!err)
|
||||
err = xfrm_mark_put(skb, &xp->mark);
|
||||
if (!err)
|
||||
err = xfrm_if_id_put(skb, xp->if_id);
|
||||
if (err)
|
||||
goto out_free_skb;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user