root/net/netfilter/nft_redir.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. nft_redir_validate
  2. nft_redir_init
  3. nft_redir_dump
  4. nft_redir_ipv4_eval
  5. nft_redir_ipv4_destroy
  6. nft_redir_ipv6_eval
  7. nft_redir_ipv6_destroy
  8. nft_redir_inet_eval
  9. nft_redir_inet_destroy
  10. nft_redir_module_init_inet
  11. nft_redir_module_init_inet
  12. nft_redir_module_init
  13. nft_redir_module_exit

   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 
  16 struct nft_redir {
  17         enum nft_registers      sreg_proto_min:8;
  18         enum nft_registers      sreg_proto_max:8;
  19         u16                     flags;
  20 };
  21 
  22 static 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 
  28 static 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 
  43 static 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 
  82 static 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 
 101 nla_put_failure:
 102         return -1;
 103 }
 104 
 105 static 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 
 126 static void
 127 nft_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 
 132 static struct nft_expr_type nft_redir_ipv4_type;
 133 static 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 
 143 static 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
 153 static 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 
 175 static void
 176 nft_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 
 181 static struct nft_expr_type nft_redir_ipv6_type;
 182 static 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 
 192 static 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
 203 static 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 
 217 static void
 218 nft_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 
 223 static struct nft_expr_type nft_redir_inet_type;
 224 static 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 
 234 static 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 
 243 static int __init nft_redir_module_init_inet(void)
 244 {
 245         return nft_register_expr(&nft_redir_inet_type);
 246 }
 247 #else
 248 static inline int nft_redir_module_init_inet(void) { return 0; }
 249 #endif
 250 
 251 static 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 
 278 static 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 
 289 module_init(nft_redir_module_init);
 290 module_exit(nft_redir_module_exit);
 291 
 292 MODULE_LICENSE("GPL");
 293 MODULE_AUTHOR("Arturo Borrero Gonzalez <arturo@debian.org>");
 294 MODULE_ALIAS_NFT_EXPR("redir");

/* [<][>][^][v][top][bottom][index][help] */