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