]> Pileus Git - ~andy/linux/blobdiff - net/netfilter/nf_conntrack_netlink.c
Merge branch 'for-davem' of git://git.kernel.org/pub/scm/linux/kernel/git/linville...
[~andy/linux] / net / netfilter / nf_conntrack_netlink.c
index 6f4b00a8fc737a60f9f2ff5fa3f41d92ff9553a2..31d1d8f3a6ce4a7eaf47a6c8b4e093480e308d90 100644 (file)
@@ -46,6 +46,7 @@
 #ifdef CONFIG_NF_NAT_NEEDED
 #include <net/netfilter/nf_nat_core.h>
 #include <net/netfilter/nf_nat_protocol.h>
+#include <net/netfilter/nf_nat_helper.h>
 #endif
 
 #include <linux/netfilter/nfnetlink.h>
@@ -901,7 +902,8 @@ static const struct nla_policy help_nla_policy[CTA_HELP_MAX+1] = {
 };
 
 static inline int
-ctnetlink_parse_help(const struct nlattr *attr, char **helper_name)
+ctnetlink_parse_help(const struct nlattr *attr, char **helper_name,
+                    struct nlattr **helpinfo)
 {
        struct nlattr *tb[CTA_HELP_MAX+1];
 
@@ -912,6 +914,9 @@ ctnetlink_parse_help(const struct nlattr *attr, char **helper_name)
 
        *helper_name = nla_data(tb[CTA_HELP_NAME]);
 
+       if (tb[CTA_HELP_INFO])
+               *helpinfo = tb[CTA_HELP_INFO];
+
        return 0;
 }
 
@@ -1172,13 +1177,14 @@ ctnetlink_change_helper(struct nf_conn *ct, const struct nlattr * const cda[])
        struct nf_conntrack_helper *helper;
        struct nf_conn_help *help = nfct_help(ct);
        char *helpname = NULL;
+       struct nlattr *helpinfo = NULL;
        int err;
 
        /* don't change helper of sibling connections */
        if (ct->master)
                return -EBUSY;
 
-       err = ctnetlink_parse_help(cda[CTA_HELP], &helpname);
+       err = ctnetlink_parse_help(cda[CTA_HELP], &helpname, &helpinfo);
        if (err < 0)
                return err;
 
@@ -1213,20 +1219,17 @@ ctnetlink_change_helper(struct nf_conn *ct, const struct nlattr * const cda[])
        }
 
        if (help) {
-               if (help->helper == helper)
+               if (help->helper == helper) {
+                       /* update private helper data if allowed. */
+                       if (helper->from_nlattr && helpinfo)
+                               helper->from_nlattr(helpinfo, ct);
                        return 0;
-               if (help->helper)
+               } else
                        return -EBUSY;
-               /* need to zero data of old helper */
-               memset(&help->help, 0, sizeof(help->help));
-       } else {
-               /* we cannot set a helper for an existing conntrack */
-               return -EOPNOTSUPP;
        }
 
-       rcu_assign_pointer(help->helper, helper);
-
-       return 0;
+       /* we cannot set a helper for an existing conntrack */
+       return -EOPNOTSUPP;
 }
 
 static inline int
@@ -1410,8 +1413,9 @@ ctnetlink_create_conntrack(struct net *net, u16 zone,
        rcu_read_lock();
        if (cda[CTA_HELP]) {
                char *helpname = NULL;
-               err = ctnetlink_parse_help(cda[CTA_HELP], &helpname);
+               struct nlattr *helpinfo = NULL;
+
+               err = ctnetlink_parse_help(cda[CTA_HELP], &helpname, &helpinfo);
                if (err < 0)
                        goto err2;
 
@@ -1440,11 +1444,14 @@ ctnetlink_create_conntrack(struct net *net, u16 zone,
                } else {
                        struct nf_conn_help *help;
 
-                       help = nf_ct_helper_ext_add(ct, GFP_ATOMIC);
+                       help = nf_ct_helper_ext_add(ct, helper, GFP_ATOMIC);
                        if (help == NULL) {
                                err = -ENOMEM;
                                goto err2;
                        }
+                       /* set private helper data if allowed. */
+                       if (helper->from_nlattr && helpinfo)
+                               helper->from_nlattr(helpinfo, ct);
 
                        /* not in hash table yet so not strictly necessary */
                        RCU_INIT_POINTER(help->helper, helper);
@@ -1620,6 +1627,142 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb,
        return err;
 }
 
+#ifdef CONFIG_NETFILTER_NETLINK_QUEUE_CT
+static size_t
+ctnetlink_nfqueue_build_size(const struct nf_conn *ct)
+{
+       return 3 * nla_total_size(0) /* CTA_TUPLE_ORIG|REPL|MASTER */
+              + 3 * nla_total_size(0) /* CTA_TUPLE_IP */
+              + 3 * nla_total_size(0) /* CTA_TUPLE_PROTO */
+              + 3 * nla_total_size(sizeof(u_int8_t)) /* CTA_PROTO_NUM */
+              + nla_total_size(sizeof(u_int32_t)) /* CTA_ID */
+              + nla_total_size(sizeof(u_int32_t)) /* CTA_STATUS */
+              + nla_total_size(sizeof(u_int32_t)) /* CTA_TIMEOUT */
+              + nla_total_size(0) /* CTA_PROTOINFO */
+              + nla_total_size(0) /* CTA_HELP */
+              + nla_total_size(NF_CT_HELPER_NAME_LEN) /* CTA_HELP_NAME */
+              + ctnetlink_secctx_size(ct)
+#ifdef CONFIG_NF_NAT_NEEDED
+              + 2 * nla_total_size(0) /* CTA_NAT_SEQ_ADJ_ORIG|REPL */
+              + 6 * nla_total_size(sizeof(u_int32_t)) /* CTA_NAT_SEQ_OFFSET */
+#endif
+#ifdef CONFIG_NF_CONNTRACK_MARK
+              + nla_total_size(sizeof(u_int32_t)) /* CTA_MARK */
+#endif
+              + ctnetlink_proto_size(ct)
+              ;
+}
+
+static int
+ctnetlink_nfqueue_build(struct sk_buff *skb, struct nf_conn *ct)
+{
+       struct nlattr *nest_parms;
+
+       rcu_read_lock();
+       nest_parms = nla_nest_start(skb, CTA_TUPLE_ORIG | NLA_F_NESTED);
+       if (!nest_parms)
+               goto nla_put_failure;
+       if (ctnetlink_dump_tuples(skb, nf_ct_tuple(ct, IP_CT_DIR_ORIGINAL)) < 0)
+               goto nla_put_failure;
+       nla_nest_end(skb, nest_parms);
+
+       nest_parms = nla_nest_start(skb, CTA_TUPLE_REPLY | NLA_F_NESTED);
+       if (!nest_parms)
+               goto nla_put_failure;
+       if (ctnetlink_dump_tuples(skb, nf_ct_tuple(ct, IP_CT_DIR_REPLY)) < 0)
+               goto nla_put_failure;
+       nla_nest_end(skb, nest_parms);
+
+       if (nf_ct_zone(ct)) {
+               if (nla_put_be16(skb, CTA_ZONE, htons(nf_ct_zone(ct))))
+                       goto nla_put_failure;
+       }
+
+       if (ctnetlink_dump_id(skb, ct) < 0)
+               goto nla_put_failure;
+
+       if (ctnetlink_dump_status(skb, ct) < 0)
+               goto nla_put_failure;
+
+       if (ctnetlink_dump_timeout(skb, ct) < 0)
+               goto nla_put_failure;
+
+       if (ctnetlink_dump_protoinfo(skb, ct) < 0)
+               goto nla_put_failure;
+
+       if (ctnetlink_dump_helpinfo(skb, ct) < 0)
+               goto nla_put_failure;
+
+#ifdef CONFIG_NF_CONNTRACK_SECMARK
+       if (ct->secmark && ctnetlink_dump_secctx(skb, ct) < 0)
+               goto nla_put_failure;
+#endif
+       if (ct->master && ctnetlink_dump_master(skb, ct) < 0)
+               goto nla_put_failure;
+
+       if ((ct->status & IPS_SEQ_ADJUST) &&
+           ctnetlink_dump_nat_seq_adj(skb, ct) < 0)
+               goto nla_put_failure;
+
+#ifdef CONFIG_NF_CONNTRACK_MARK
+       if (ct->mark && ctnetlink_dump_mark(skb, ct) < 0)
+               goto nla_put_failure;
+#endif
+       rcu_read_unlock();
+       return 0;
+
+nla_put_failure:
+       rcu_read_unlock();
+       return -ENOSPC;
+}
+
+static int
+ctnetlink_nfqueue_parse_ct(const struct nlattr *cda[], struct nf_conn *ct)
+{
+       int err;
+
+       if (cda[CTA_TIMEOUT]) {
+               err = ctnetlink_change_timeout(ct, cda);
+               if (err < 0)
+                       return err;
+       }
+       if (cda[CTA_STATUS]) {
+               err = ctnetlink_change_status(ct, cda);
+               if (err < 0)
+                       return err;
+       }
+       if (cda[CTA_HELP]) {
+               err = ctnetlink_change_helper(ct, cda);
+               if (err < 0)
+                       return err;
+       }
+#if defined(CONFIG_NF_CONNTRACK_MARK)
+       if (cda[CTA_MARK])
+               ct->mark = ntohl(nla_get_be32(cda[CTA_MARK]));
+#endif
+       return 0;
+}
+
+static int
+ctnetlink_nfqueue_parse(const struct nlattr *attr, struct nf_conn *ct)
+{
+       struct nlattr *cda[CTA_MAX+1];
+
+       nla_parse_nested(cda, CTA_MAX, attr, ct_nla_policy);
+
+       return ctnetlink_nfqueue_parse_ct((const struct nlattr **)cda, ct);
+}
+
+static struct nfq_ct_hook ctnetlink_nfqueue_hook = {
+       .build_size     = ctnetlink_nfqueue_build_size,
+       .build          = ctnetlink_nfqueue_build,
+       .parse          = ctnetlink_nfqueue_parse,
+#ifdef CONFIG_NF_NAT_NEEDED
+       .seq_adjust     = nf_nat_tcp_seq_adjust,
+#endif
+};
+#endif /* CONFIG_NETFILTER_NETLINK_QUEUE_CT */
+
 /***********************************************************************
  * EXPECT
  ***********************************************************************/
@@ -2424,7 +2567,10 @@ static int __init ctnetlink_init(void)
                pr_err("ctnetlink_init: cannot register pernet operations\n");
                goto err_unreg_exp_subsys;
        }
-
+#ifdef CONFIG_NETFILTER_NETLINK_QUEUE_CT
+       /* setup interaction between nf_queue and nf_conntrack_netlink. */
+       RCU_INIT_POINTER(nfq_ct_hook, &ctnetlink_nfqueue_hook);
+#endif
        return 0;
 
 err_unreg_exp_subsys:
@@ -2442,6 +2588,9 @@ static void __exit ctnetlink_exit(void)
        unregister_pernet_subsys(&ctnetlink_net_ops);
        nfnetlink_subsys_unregister(&ctnl_exp_subsys);
        nfnetlink_subsys_unregister(&ctnl_subsys);
+#ifdef CONFIG_NETFILTER_NETLINK_QUEUE_CT
+       RCU_INIT_POINTER(nfq_ct_hook, NULL);
+#endif
 }
 
 module_init(ctnetlink_init);