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