linux/net/netfilter/nft_redir.c
<<
>>
Prefs
   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};
 138
 139static struct nft_expr_type nft_redir_ipv4_type __read_mostly = {
 140        .family         = NFPROTO_IPV4,
 141        .name           = "redir",
 142        .ops            = &nft_redir_ipv4_ops,
 143        .policy         = nft_redir_policy,
 144        .maxattr        = NFTA_REDIR_MAX,
 145        .owner          = THIS_MODULE,
 146};
 147
 148#ifdef CONFIG_NF_TABLES_IPV6
 149static void nft_redir_ipv6_eval(const struct nft_expr *expr,
 150                                struct nft_regs *regs,
 151                                const struct nft_pktinfo *pkt)
 152{
 153        struct nft_redir *priv = nft_expr_priv(expr);
 154        struct nf_nat_range2 range;
 155
 156        memset(&range, 0, sizeof(range));
 157        if (priv->sreg_proto_min) {
 158                range.min_proto.all = (__force __be16)nft_reg_load16(
 159                        &regs->data[priv->sreg_proto_min]);
 160                range.max_proto.all = (__force __be16)nft_reg_load16(
 161                        &regs->data[priv->sreg_proto_max]);
 162                range.flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
 163        }
 164
 165        range.flags |= priv->flags;
 166
 167        regs->verdict.code =
 168                nf_nat_redirect_ipv6(pkt->skb, &range, nft_hook(pkt));
 169}
 170
 171static void
 172nft_redir_ipv6_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr)
 173{
 174        nf_ct_netns_put(ctx->net, NFPROTO_IPV6);
 175}
 176
 177static struct nft_expr_type nft_redir_ipv6_type;
 178static const struct nft_expr_ops nft_redir_ipv6_ops = {
 179        .type           = &nft_redir_ipv6_type,
 180        .size           = NFT_EXPR_SIZE(sizeof(struct nft_redir)),
 181        .eval           = nft_redir_ipv6_eval,
 182        .init           = nft_redir_init,
 183        .destroy        = nft_redir_ipv6_destroy,
 184        .dump           = nft_redir_dump,
 185        .validate       = nft_redir_validate,
 186};
 187
 188static struct nft_expr_type nft_redir_ipv6_type __read_mostly = {
 189        .family         = NFPROTO_IPV6,
 190        .name           = "redir",
 191        .ops            = &nft_redir_ipv6_ops,
 192        .policy         = nft_redir_policy,
 193        .maxattr        = NFTA_REDIR_MAX,
 194        .owner          = THIS_MODULE,
 195};
 196#endif
 197
 198#ifdef CONFIG_NF_TABLES_INET
 199static void nft_redir_inet_eval(const struct nft_expr *expr,
 200                                struct nft_regs *regs,
 201                                const struct nft_pktinfo *pkt)
 202{
 203        switch (nft_pf(pkt)) {
 204        case NFPROTO_IPV4:
 205                return nft_redir_ipv4_eval(expr, regs, pkt);
 206        case NFPROTO_IPV6:
 207                return nft_redir_ipv6_eval(expr, regs, pkt);
 208        }
 209
 210        WARN_ON_ONCE(1);
 211}
 212
 213static void
 214nft_redir_inet_destroy(const struct nft_ctx *ctx, const struct nft_expr *expr)
 215{
 216        nf_ct_netns_put(ctx->net, NFPROTO_INET);
 217}
 218
 219static struct nft_expr_type nft_redir_inet_type;
 220static const struct nft_expr_ops nft_redir_inet_ops = {
 221        .type           = &nft_redir_inet_type,
 222        .size           = NFT_EXPR_SIZE(sizeof(struct nft_redir)),
 223        .eval           = nft_redir_inet_eval,
 224        .init           = nft_redir_init,
 225        .destroy        = nft_redir_inet_destroy,
 226        .dump           = nft_redir_dump,
 227        .validate       = nft_redir_validate,
 228};
 229
 230static struct nft_expr_type nft_redir_inet_type __read_mostly = {
 231        .family         = NFPROTO_INET,
 232        .name           = "redir",
 233        .ops            = &nft_redir_inet_ops,
 234        .policy         = nft_redir_policy,
 235        .maxattr        = NFTA_MASQ_MAX,
 236        .owner          = THIS_MODULE,
 237};
 238
 239static int __init nft_redir_module_init_inet(void)
 240{
 241        return nft_register_expr(&nft_redir_inet_type);
 242}
 243#else
 244static inline int nft_redir_module_init_inet(void) { return 0; }
 245#endif
 246
 247static int __init nft_redir_module_init(void)
 248{
 249        int ret = nft_register_expr(&nft_redir_ipv4_type);
 250
 251        if (ret)
 252                return ret;
 253
 254#ifdef CONFIG_NF_TABLES_IPV6
 255        ret = nft_register_expr(&nft_redir_ipv6_type);
 256        if (ret) {
 257                nft_unregister_expr(&nft_redir_ipv4_type);
 258                return ret;
 259        }
 260#endif
 261
 262        ret = nft_redir_module_init_inet();
 263        if (ret < 0) {
 264                nft_unregister_expr(&nft_redir_ipv4_type);
 265#ifdef CONFIG_NF_TABLES_IPV6
 266                nft_unregister_expr(&nft_redir_ipv6_type);
 267#endif
 268                return ret;
 269        }
 270
 271        return ret;
 272}
 273
 274static void __exit nft_redir_module_exit(void)
 275{
 276        nft_unregister_expr(&nft_redir_ipv4_type);
 277#ifdef CONFIG_NF_TABLES_IPV6
 278        nft_unregister_expr(&nft_redir_ipv6_type);
 279#endif
 280#ifdef CONFIG_NF_TABLES_INET
 281        nft_unregister_expr(&nft_redir_inet_type);
 282#endif
 283}
 284
 285module_init(nft_redir_module_init);
 286module_exit(nft_redir_module_exit);
 287
 288MODULE_LICENSE("GPL");
 289MODULE_AUTHOR("Arturo Borrero Gonzalez <arturo@debian.org>");
 290MODULE_ALIAS_NFT_EXPR("redir");
 291MODULE_DESCRIPTION("Netfilter nftables redirect support");
 292