]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
netfilter: nat: add inet family nat support
authorFlorian Westphal <fw@strlen.de>
Wed, 27 Mar 2019 08:22:24 +0000 (09:22 +0100)
committerPablo Neira Ayuso <pablo@netfilter.org>
Mon, 8 Apr 2019 21:01:39 +0000 (23:01 +0200)
We need minimal support from the nat core for this, as we do not
want to register additional base hooks.

When an inet hook is registered, interally register ipv4 and ipv6
hooks for them and unregister those when inet hooks are removed.

Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
include/net/netfilter/nf_nat.h
net/netfilter/nf_nat_core.c
net/netfilter/nf_nat_proto.c
net/netfilter/nft_chain_nat.c
net/netfilter/nft_nat.c

index cf332c4e0b325bc06e7926fb5f886b0a3dd07e42..423cda2c65425e43659f2558f67ef4c0fe94dea2 100644 (file)
@@ -69,9 +69,9 @@ static inline bool nf_nat_oif_changed(unsigned int hooknum,
 #endif
 }
 
-int nf_nat_register_fn(struct net *net, const struct nf_hook_ops *ops,
+int nf_nat_register_fn(struct net *net, u8 pf, const struct nf_hook_ops *ops,
                       const struct nf_hook_ops *nat_ops, unsigned int ops_count);
-void nf_nat_unregister_fn(struct net *net, const struct nf_hook_ops *ops,
+void nf_nat_unregister_fn(struct net *net, u8 pf, const struct nf_hook_ops *ops,
                          unsigned int ops_count);
 
 unsigned int nf_nat_packet(struct nf_conn *ct, enum ip_conntrack_info ctinfo,
@@ -98,6 +98,9 @@ void nf_nat_ipv4_unregister_fn(struct net *net, const struct nf_hook_ops *ops);
 int nf_nat_ipv6_register_fn(struct net *net, const struct nf_hook_ops *ops);
 void nf_nat_ipv6_unregister_fn(struct net *net, const struct nf_hook_ops *ops);
 
+int nf_nat_inet_register_fn(struct net *net, const struct nf_hook_ops *ops);
+void nf_nat_inet_unregister_fn(struct net *net, const struct nf_hook_ops *ops);
+
 unsigned int
 nf_nat_inet_fn(void *priv, struct sk_buff *skb,
               const struct nf_hook_state *state);
index af7dc65377584d26f4b5d98ef55dd06f93d8107d..a9ec49edd7f43bc8a3ed7707cd8e3e0ee4bd8681 100644 (file)
@@ -1009,7 +1009,7 @@ static struct nf_ct_helper_expectfn follow_master_nat = {
        .expectfn       = nf_nat_follow_master,
 };
 
-int nf_nat_register_fn(struct net *net, const struct nf_hook_ops *ops,
+int nf_nat_register_fn(struct net *net, u8 pf, const struct nf_hook_ops *ops,
                       const struct nf_hook_ops *orig_nat_ops, unsigned int ops_count)
 {
        struct nat_net *nat_net = net_generic(net, nat_net_id);
@@ -1019,14 +1019,12 @@ int nf_nat_register_fn(struct net *net, const struct nf_hook_ops *ops,
        struct nf_hook_ops *nat_ops;
        int i, ret;
 
-       if (WARN_ON_ONCE(ops->pf >= ARRAY_SIZE(nat_net->nat_proto_net)))
+       if (WARN_ON_ONCE(pf >= ARRAY_SIZE(nat_net->nat_proto_net)))
                return -EINVAL;
 
-       nat_proto_net = &nat_net->nat_proto_net[ops->pf];
+       nat_proto_net = &nat_net->nat_proto_net[pf];
 
        for (i = 0; i < ops_count; i++) {
-               if (WARN_ON(orig_nat_ops[i].pf != ops->pf))
-                       return -EINVAL;
                if (orig_nat_ops[i].hooknum == hooknum) {
                        hooknum = i;
                        break;
@@ -1086,8 +1084,8 @@ int nf_nat_register_fn(struct net *net, const struct nf_hook_ops *ops,
        return ret;
 }
 
-void nf_nat_unregister_fn(struct net *net, const struct nf_hook_ops *ops,
-                         unsigned int ops_count)
+void nf_nat_unregister_fn(struct net *net, u8 pf, const struct nf_hook_ops *ops,
+                         unsigned int ops_count)
 {
        struct nat_net *nat_net = net_generic(net, nat_net_id);
        struct nf_nat_hooks_net *nat_proto_net;
@@ -1096,10 +1094,10 @@ void nf_nat_unregister_fn(struct net *net, const struct nf_hook_ops *ops,
        int hooknum = ops->hooknum;
        int i;
 
-       if (ops->pf >= ARRAY_SIZE(nat_net->nat_proto_net))
+       if (pf >= ARRAY_SIZE(nat_net->nat_proto_net))
                return;
 
-       nat_proto_net = &nat_net->nat_proto_net[ops->pf];
+       nat_proto_net = &nat_net->nat_proto_net[pf];
 
        mutex_lock(&nf_nat_proto_mutex);
        if (WARN_ON(nat_proto_net->users == 0))
index 62743da3004fa631e6034dc6eb2ff98fcc1b9cd5..606d0a740615d38ff93eeee28bcd19e4f1f655a2 100644 (file)
@@ -725,7 +725,7 @@ nf_nat_ipv4_local_fn(void *priv, struct sk_buff *skb,
        return ret;
 }
 
-static const struct nf_hook_ops nf_nat_ipv4_ops[] = {
+const struct nf_hook_ops nf_nat_ipv4_ops[] = {
        /* Before packet filtering, change destination */
        {
                .hook           = nf_nat_ipv4_in,
@@ -758,13 +758,14 @@ static const struct nf_hook_ops nf_nat_ipv4_ops[] = {
 
 int nf_nat_ipv4_register_fn(struct net *net, const struct nf_hook_ops *ops)
 {
-       return nf_nat_register_fn(net, ops, nf_nat_ipv4_ops, ARRAY_SIZE(nf_nat_ipv4_ops));
+       return nf_nat_register_fn(net, ops->pf, ops, nf_nat_ipv4_ops,
+                                 ARRAY_SIZE(nf_nat_ipv4_ops));
 }
 EXPORT_SYMBOL_GPL(nf_nat_ipv4_register_fn);
 
 void nf_nat_ipv4_unregister_fn(struct net *net, const struct nf_hook_ops *ops)
 {
-       nf_nat_unregister_fn(net, ops, ARRAY_SIZE(nf_nat_ipv4_ops));
+       nf_nat_unregister_fn(net, ops->pf, ops, ARRAY_SIZE(nf_nat_ipv4_ops));
 }
 EXPORT_SYMBOL_GPL(nf_nat_ipv4_unregister_fn);
 
@@ -977,7 +978,7 @@ nf_nat_ipv6_local_fn(void *priv, struct sk_buff *skb,
        return ret;
 }
 
-static const struct nf_hook_ops nf_nat_ipv6_ops[] = {
+const struct nf_hook_ops nf_nat_ipv6_ops[] = {
        /* Before packet filtering, change destination */
        {
                .hook           = nf_nat_ipv6_in,
@@ -1010,14 +1011,44 @@ static const struct nf_hook_ops nf_nat_ipv6_ops[] = {
 
 int nf_nat_ipv6_register_fn(struct net *net, const struct nf_hook_ops *ops)
 {
-       return nf_nat_register_fn(net, ops, nf_nat_ipv6_ops,
+       return nf_nat_register_fn(net, ops->pf, ops, nf_nat_ipv6_ops,
                                  ARRAY_SIZE(nf_nat_ipv6_ops));
 }
 EXPORT_SYMBOL_GPL(nf_nat_ipv6_register_fn);
 
 void nf_nat_ipv6_unregister_fn(struct net *net, const struct nf_hook_ops *ops)
 {
-       nf_nat_unregister_fn(net, ops, ARRAY_SIZE(nf_nat_ipv6_ops));
+       nf_nat_unregister_fn(net, ops->pf, ops, ARRAY_SIZE(nf_nat_ipv6_ops));
 }
 EXPORT_SYMBOL_GPL(nf_nat_ipv6_unregister_fn);
 #endif /* CONFIG_IPV6 */
+
+#if defined(CONFIG_NF_TABLES_INET) && IS_ENABLED(CONFIG_NFT_NAT)
+int nf_nat_inet_register_fn(struct net *net, const struct nf_hook_ops *ops)
+{
+       int ret;
+
+       if (WARN_ON_ONCE(ops->pf != NFPROTO_INET))
+               return -EINVAL;
+
+       ret = nf_nat_register_fn(net, NFPROTO_IPV6, ops, nf_nat_ipv6_ops,
+                                ARRAY_SIZE(nf_nat_ipv6_ops));
+       if (ret)
+               return ret;
+
+       ret = nf_nat_register_fn(net, NFPROTO_IPV4, ops, nf_nat_ipv4_ops,
+                                ARRAY_SIZE(nf_nat_ipv4_ops));
+       if (ret)
+               nf_nat_ipv6_unregister_fn(net, ops);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(nf_nat_inet_register_fn);
+
+void nf_nat_inet_unregister_fn(struct net *net, const struct nf_hook_ops *ops)
+{
+       nf_nat_unregister_fn(net, NFPROTO_IPV4, ops, ARRAY_SIZE(nf_nat_ipv4_ops));
+       nf_nat_unregister_fn(net, NFPROTO_IPV6, ops, ARRAY_SIZE(nf_nat_ipv6_ops));
+}
+EXPORT_SYMBOL_GPL(nf_nat_inet_unregister_fn);
+#endif /* NFT INET NAT */
index ee4852088d509a154091f41e213d3c5a91c094c4..2f89bde3c61cb93690238c8c53ccad3556806417 100644 (file)
@@ -74,6 +74,36 @@ static const struct nft_chain_type nft_chain_nat_ipv6 = {
 };
 #endif
 
+#ifdef CONFIG_NF_TABLES_INET
+static int nft_nat_inet_reg(struct net *net, const struct nf_hook_ops *ops)
+{
+       return nf_nat_inet_register_fn(net, ops);
+}
+
+static void nft_nat_inet_unreg(struct net *net, const struct nf_hook_ops *ops)
+{
+       nf_nat_inet_unregister_fn(net, ops);
+}
+
+static const struct nft_chain_type nft_chain_nat_inet = {
+       .name           = "nat",
+       .type           = NFT_CHAIN_T_NAT,
+       .family         = NFPROTO_INET,
+       .hook_mask      = (1 << NF_INET_PRE_ROUTING) |
+                         (1 << NF_INET_LOCAL_IN) |
+                         (1 << NF_INET_LOCAL_OUT) |
+                         (1 << NF_INET_POST_ROUTING),
+       .hooks          = {
+               [NF_INET_PRE_ROUTING]   = nft_nat_do_chain,
+               [NF_INET_LOCAL_IN]      = nft_nat_do_chain,
+               [NF_INET_LOCAL_OUT]     = nft_nat_do_chain,
+               [NF_INET_POST_ROUTING]  = nft_nat_do_chain,
+       },
+       .ops_register           = nft_nat_inet_reg,
+       .ops_unregister         = nft_nat_inet_unreg,
+};
+#endif
+
 static int __init nft_chain_nat_init(void)
 {
 #ifdef CONFIG_NF_TABLES_IPV6
@@ -82,6 +112,9 @@ static int __init nft_chain_nat_init(void)
 #ifdef CONFIG_NF_TABLES_IPV4
        nft_register_chain_type(&nft_chain_nat_ipv4);
 #endif
+#ifdef CONFIG_NF_TABLES_INET
+       nft_register_chain_type(&nft_chain_nat_inet);
+#endif
 
        return 0;
 }
@@ -94,6 +127,9 @@ static void __exit nft_chain_nat_exit(void)
 #ifdef CONFIG_NF_TABLES_IPV6
        nft_unregister_chain_type(&nft_chain_nat_ipv6);
 #endif
+#ifdef CONFIG_NF_TABLES_INET
+       nft_unregister_chain_type(&nft_chain_nat_inet);
+#endif
 }
 
 module_init(nft_chain_nat_init);
index e93aed9bda887dff7db5c0312aa6ad66d388fdcf..d90d421826aa28546d3c6f2a1b0e1b92f9b1caca 100644 (file)
@@ -140,7 +140,7 @@ static int nft_nat_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
                return -EINVAL;
 
        family = ntohl(nla_get_be32(tb[NFTA_NAT_FAMILY]));
-       if (family != ctx->family)
+       if (ctx->family != NFPROTO_INET && ctx->family != family)
                return -EOPNOTSUPP;
 
        switch (family) {
@@ -278,13 +278,67 @@ static struct nft_expr_type nft_nat_type __read_mostly = {
        .owner          = THIS_MODULE,
 };
 
+#ifdef CONFIG_NF_TABLES_INET
+static void nft_nat_inet_eval(const struct nft_expr *expr,
+                             struct nft_regs *regs,
+                             const struct nft_pktinfo *pkt)
+{
+       const struct nft_nat *priv = nft_expr_priv(expr);
+
+       if (priv->family == nft_pf(pkt))
+               nft_nat_eval(expr, regs, pkt);
+}
+
+static const struct nft_expr_ops nft_nat_inet_ops = {
+       .type           = &nft_nat_type,
+       .size           = NFT_EXPR_SIZE(sizeof(struct nft_nat)),
+       .eval           = nft_nat_inet_eval,
+       .init           = nft_nat_init,
+       .destroy        = nft_nat_destroy,
+       .dump           = nft_nat_dump,
+       .validate       = nft_nat_validate,
+};
+
+static struct nft_expr_type nft_inet_nat_type __read_mostly = {
+       .name           = "nat",
+       .family         = NFPROTO_INET,
+       .ops            = &nft_nat_inet_ops,
+       .policy         = nft_nat_policy,
+       .maxattr        = NFTA_NAT_MAX,
+       .owner          = THIS_MODULE,
+};
+
+static int nft_nat_inet_module_init(void)
+{
+       return nft_register_expr(&nft_inet_nat_type);
+}
+
+static void nft_nat_inet_module_exit(void)
+{
+       nft_unregister_expr(&nft_inet_nat_type);
+}
+#else
+static int nft_nat_inet_module_init(void) { return 0; }
+static void nft_nat_inet_module_exit(void) { }
+#endif
+
 static int __init nft_nat_module_init(void)
 {
-       return nft_register_expr(&nft_nat_type);
+       int ret = nft_nat_inet_module_init();
+
+       if (ret)
+               return ret;
+
+       ret = nft_register_expr(&nft_nat_type);
+       if (ret)
+               nft_nat_inet_module_exit();
+
+       return ret;
 }
 
 static void __exit nft_nat_module_exit(void)
 {
+       nft_nat_inet_module_exit();
        nft_unregister_expr(&nft_nat_type);
 }