cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

nft_redir.c (7536B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Copyright (c) 2014 Arturo Borrero Gonzalez <arturo@debian.org>
      4 */
      5
      6#include <linux/kernel.h>
      7#include <linux/init.h>
      8#include <linux/module.h>
      9#include <linux/netlink.h>
     10#include <linux/netfilter.h>
     11#include <linux/netfilter/nf_tables.h>
     12#include <net/netfilter/nf_nat.h>
     13#include <net/netfilter/nf_nat_redirect.h>
     14#include <net/netfilter/nf_tables.h>
     15
     16struct nft_redir {
     17	u8			sreg_proto_min;
     18	u8			sreg_proto_max;
     19	u16			flags;
     20};
     21
     22static const struct nla_policy nft_redir_policy[NFTA_REDIR_MAX + 1] = {
     23	[NFTA_REDIR_REG_PROTO_MIN]	= { .type = NLA_U32 },
     24	[NFTA_REDIR_REG_PROTO_MAX]	= { .type = NLA_U32 },
     25	[NFTA_REDIR_FLAGS]		= { .type = NLA_U32 },
     26};
     27
     28static int nft_redir_validate(const struct nft_ctx *ctx,
     29			      const struct nft_expr *expr,
     30			      const struct nft_data **data)
     31{
     32	int err;
     33
     34	err = nft_chain_validate_dependency(ctx->chain, NFT_CHAIN_T_NAT);
     35	if (err < 0)
     36		return err;
     37
     38	return nft_chain_validate_hooks(ctx->chain,
     39					(1 << NF_INET_PRE_ROUTING) |
     40					(1 << NF_INET_LOCAL_OUT));
     41}
     42
     43static int nft_redir_init(const struct nft_ctx *ctx,
     44			  const struct nft_expr *expr,
     45			  const struct nlattr * const tb[])
     46{
     47	struct nft_redir *priv = nft_expr_priv(expr);
     48	unsigned int plen;
     49	int err;
     50
     51	plen = sizeof_field(struct nf_nat_range, min_addr.all);
     52	if (tb[NFTA_REDIR_REG_PROTO_MIN]) {
     53		err = nft_parse_register_load(tb[NFTA_REDIR_REG_PROTO_MIN],
     54					      &priv->sreg_proto_min, plen);
     55		if (err < 0)
     56			return err;
     57
     58		if (tb[NFTA_REDIR_REG_PROTO_MAX]) {
     59			err = nft_parse_register_load(tb[NFTA_REDIR_REG_PROTO_MAX],
     60						      &priv->sreg_proto_max,
     61						      plen);
     62			if (err < 0)
     63				return err;
     64		} else {
     65			priv->sreg_proto_max = priv->sreg_proto_min;
     66		}
     67	}
     68
     69	if (tb[NFTA_REDIR_FLAGS]) {
     70		priv->flags = ntohl(nla_get_be32(tb[NFTA_REDIR_FLAGS]));
     71		if (priv->flags & ~NF_NAT_RANGE_MASK)
     72			return -EINVAL;
     73	}
     74
     75	return nf_ct_netns_get(ctx->net, ctx->family);
     76}
     77
     78static int nft_redir_dump(struct sk_buff *skb, const struct nft_expr *expr)
     79{
     80	const struct nft_redir *priv = nft_expr_priv(expr);
     81
     82	if (priv->sreg_proto_min) {
     83		if (nft_dump_register(skb, NFTA_REDIR_REG_PROTO_MIN,
     84				      priv->sreg_proto_min))
     85			goto nla_put_failure;
     86		if (nft_dump_register(skb, NFTA_REDIR_REG_PROTO_MAX,
     87				      priv->sreg_proto_max))
     88			goto nla_put_failure;
     89	}
     90
     91	if (priv->flags != 0 &&
     92	    nla_put_be32(skb, NFTA_REDIR_FLAGS, htonl(priv->flags)))
     93			goto nla_put_failure;
     94
     95	return 0;
     96
     97nla_put_failure:
     98	return -1;
     99}
    100
    101static void nft_redir_ipv4_eval(const struct nft_expr *expr,
    102				struct nft_regs *regs,
    103				const struct nft_pktinfo *pkt)
    104{
    105	struct nft_redir *priv = nft_expr_priv(expr);
    106	struct nf_nat_ipv4_multi_range_compat mr;
    107
    108	memset(&mr, 0, sizeof(mr));
    109	if (priv->sreg_proto_min) {
    110		mr.range[0].min.all = (__force __be16)nft_reg_load16(
    111			&regs->data[priv->sreg_proto_min]);
    112		mr.range[0].max.all = (__force __be16)nft_reg_load16(
    113			&regs->data[priv->sreg_proto_max]);
    114		mr.range[0].flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
    115	}
    116
    117	mr.range[0].flags |= priv->flags;
    118
    119	regs->verdict.code = nf_nat_redirect_ipv4(pkt->skb, &mr, nft_hook(pkt));
    120}
    121
    122static void
    123nft_redir_ipv4_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr)
    124{
    125	nf_ct_netns_put(ctx->net, NFPROTO_IPV4);
    126}
    127
    128static struct nft_expr_type nft_redir_ipv4_type;
    129static const struct nft_expr_ops nft_redir_ipv4_ops = {
    130	.type		= &nft_redir_ipv4_type,
    131	.size		= NFT_EXPR_SIZE(sizeof(struct nft_redir)),
    132	.eval		= nft_redir_ipv4_eval,
    133	.init		= nft_redir_init,
    134	.destroy	= nft_redir_ipv4_destroy,
    135	.dump		= nft_redir_dump,
    136	.validate	= nft_redir_validate,
    137	.reduce		= NFT_REDUCE_READONLY,
    138};
    139
    140static struct nft_expr_type nft_redir_ipv4_type __read_mostly = {
    141	.family		= NFPROTO_IPV4,
    142	.name		= "redir",
    143	.ops		= &nft_redir_ipv4_ops,
    144	.policy		= nft_redir_policy,
    145	.maxattr	= NFTA_REDIR_MAX,
    146	.owner		= THIS_MODULE,
    147};
    148
    149#ifdef CONFIG_NF_TABLES_IPV6
    150static void nft_redir_ipv6_eval(const struct nft_expr *expr,
    151				struct nft_regs *regs,
    152				const struct nft_pktinfo *pkt)
    153{
    154	struct nft_redir *priv = nft_expr_priv(expr);
    155	struct nf_nat_range2 range;
    156
    157	memset(&range, 0, sizeof(range));
    158	if (priv->sreg_proto_min) {
    159		range.min_proto.all = (__force __be16)nft_reg_load16(
    160			&regs->data[priv->sreg_proto_min]);
    161		range.max_proto.all = (__force __be16)nft_reg_load16(
    162			&regs->data[priv->sreg_proto_max]);
    163		range.flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
    164	}
    165
    166	range.flags |= priv->flags;
    167
    168	regs->verdict.code =
    169		nf_nat_redirect_ipv6(pkt->skb, &range, nft_hook(pkt));
    170}
    171
    172static void
    173nft_redir_ipv6_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr)
    174{
    175	nf_ct_netns_put(ctx->net, NFPROTO_IPV6);
    176}
    177
    178static struct nft_expr_type nft_redir_ipv6_type;
    179static const struct nft_expr_ops nft_redir_ipv6_ops = {
    180	.type		= &nft_redir_ipv6_type,
    181	.size		= NFT_EXPR_SIZE(sizeof(struct nft_redir)),
    182	.eval		= nft_redir_ipv6_eval,
    183	.init		= nft_redir_init,
    184	.destroy	= nft_redir_ipv6_destroy,
    185	.dump		= nft_redir_dump,
    186	.validate	= nft_redir_validate,
    187	.reduce		= NFT_REDUCE_READONLY,
    188};
    189
    190static struct nft_expr_type nft_redir_ipv6_type __read_mostly = {
    191	.family		= NFPROTO_IPV6,
    192	.name		= "redir",
    193	.ops		= &nft_redir_ipv6_ops,
    194	.policy		= nft_redir_policy,
    195	.maxattr	= NFTA_REDIR_MAX,
    196	.owner		= THIS_MODULE,
    197};
    198#endif
    199
    200#ifdef CONFIG_NF_TABLES_INET
    201static void nft_redir_inet_eval(const struct nft_expr *expr,
    202				struct nft_regs *regs,
    203				const struct nft_pktinfo *pkt)
    204{
    205	switch (nft_pf(pkt)) {
    206	case NFPROTO_IPV4:
    207		return nft_redir_ipv4_eval(expr, regs, pkt);
    208	case NFPROTO_IPV6:
    209		return nft_redir_ipv6_eval(expr, regs, pkt);
    210	}
    211
    212	WARN_ON_ONCE(1);
    213}
    214
    215static void
    216nft_redir_inet_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr)
    217{
    218	nf_ct_netns_put(ctx->net, NFPROTO_INET);
    219}
    220
    221static struct nft_expr_type nft_redir_inet_type;
    222static const struct nft_expr_ops nft_redir_inet_ops = {
    223	.type		= &nft_redir_inet_type,
    224	.size		= NFT_EXPR_SIZE(sizeof(struct nft_redir)),
    225	.eval		= nft_redir_inet_eval,
    226	.init		= nft_redir_init,
    227	.destroy	= nft_redir_inet_destroy,
    228	.dump		= nft_redir_dump,
    229	.validate	= nft_redir_validate,
    230	.reduce		= NFT_REDUCE_READONLY,
    231};
    232
    233static struct nft_expr_type nft_redir_inet_type __read_mostly = {
    234	.family		= NFPROTO_INET,
    235	.name		= "redir",
    236	.ops		= &nft_redir_inet_ops,
    237	.policy		= nft_redir_policy,
    238	.maxattr	= NFTA_MASQ_MAX,
    239	.owner		= THIS_MODULE,
    240};
    241
    242static int __init nft_redir_module_init_inet(void)
    243{
    244	return nft_register_expr(&nft_redir_inet_type);
    245}
    246#else
    247static inline int nft_redir_module_init_inet(void) { return 0; }
    248#endif
    249
    250static int __init nft_redir_module_init(void)
    251{
    252	int ret = nft_register_expr(&nft_redir_ipv4_type);
    253
    254	if (ret)
    255		return ret;
    256
    257#ifdef CONFIG_NF_TABLES_IPV6
    258	ret = nft_register_expr(&nft_redir_ipv6_type);
    259	if (ret) {
    260		nft_unregister_expr(&nft_redir_ipv4_type);
    261		return ret;
    262	}
    263#endif
    264
    265	ret = nft_redir_module_init_inet();
    266	if (ret < 0) {
    267		nft_unregister_expr(&nft_redir_ipv4_type);
    268#ifdef CONFIG_NF_TABLES_IPV6
    269		nft_unregister_expr(&nft_redir_ipv6_type);
    270#endif
    271		return ret;
    272	}
    273
    274	return ret;
    275}
    276
    277static void __exit nft_redir_module_exit(void)
    278{
    279	nft_unregister_expr(&nft_redir_ipv4_type);
    280#ifdef CONFIG_NF_TABLES_IPV6
    281	nft_unregister_expr(&nft_redir_ipv6_type);
    282#endif
    283#ifdef CONFIG_NF_TABLES_INET
    284	nft_unregister_expr(&nft_redir_inet_type);
    285#endif
    286}
    287
    288module_init(nft_redir_module_init);
    289module_exit(nft_redir_module_exit);
    290
    291MODULE_LICENSE("GPL");
    292MODULE_AUTHOR("Arturo Borrero Gonzalez <arturo@debian.org>");
    293MODULE_ALIAS_NFT_EXPR("redir");
    294MODULE_DESCRIPTION("Netfilter nftables redirect support");