]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - net/netfilter/nf_conntrack_netlink.c
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rric/oprofile
[linux-2.6-omap-h63xx.git] / net / netfilter / nf_conntrack_netlink.c
index 5f4a6516b3b6b6f935e677f774c22de1c038dccb..00e8c27130ff6ad6fa345b860948ddb8dca2bf4e 100644 (file)
@@ -105,16 +105,14 @@ ctnetlink_dump_tuples(struct sk_buff *skb,
        struct nf_conntrack_l3proto *l3proto;
        struct nf_conntrack_l4proto *l4proto;
 
-       l3proto = nf_ct_l3proto_find_get(tuple->src.l3num);
+       l3proto = __nf_ct_l3proto_find(tuple->src.l3num);
        ret = ctnetlink_dump_tuples_ip(skb, tuple, l3proto);
-       nf_ct_l3proto_put(l3proto);
 
        if (unlikely(ret < 0))
                return ret;
 
-       l4proto = nf_ct_l4proto_find_get(tuple->src.l3num, tuple->dst.protonum);
+       l4proto = __nf_ct_l4proto_find(tuple->src.l3num, tuple->dst.protonum);
        ret = ctnetlink_dump_tuples_proto(skb, tuple, l4proto);
-       nf_ct_l4proto_put(l4proto);
 
        return ret;
 }
@@ -151,11 +149,9 @@ ctnetlink_dump_protoinfo(struct sk_buff *skb, const struct nf_conn *ct)
        struct nlattr *nest_proto;
        int ret;
 
-       l4proto = nf_ct_l4proto_find_get(nf_ct_l3num(ct), nf_ct_protonum(ct));
-       if (!l4proto->to_nlattr) {
-               nf_ct_l4proto_put(l4proto);
+       l4proto = __nf_ct_l4proto_find(nf_ct_l3num(ct), nf_ct_protonum(ct));
+       if (!l4proto->to_nlattr)
                return 0;
-       }
 
        nest_proto = nla_nest_start(skb, CTA_PROTOINFO | NLA_F_NESTED);
        if (!nest_proto)
@@ -163,14 +159,11 @@ ctnetlink_dump_protoinfo(struct sk_buff *skb, const struct nf_conn *ct)
 
        ret = l4proto->to_nlattr(skb, nest_proto, ct);
 
-       nf_ct_l4proto_put(l4proto);
-
        nla_nest_end(skb, nest_proto);
 
        return ret;
 
 nla_put_failure:
-       nf_ct_l4proto_put(l4proto);
        return -1;
 }
 
@@ -184,7 +177,6 @@ ctnetlink_dump_helpinfo(struct sk_buff *skb, const struct nf_conn *ct)
        if (!help)
                return 0;
 
-       rcu_read_lock();
        helper = rcu_dereference(help->helper);
        if (!helper)
                goto out;
@@ -199,11 +191,9 @@ ctnetlink_dump_helpinfo(struct sk_buff *skb, const struct nf_conn *ct)
 
        nla_nest_end(skb, nest_helper);
 out:
-       rcu_read_unlock();
        return 0;
 
 nla_put_failure:
-       rcu_read_unlock();
        return -1;
 }
 
@@ -420,7 +410,8 @@ static int ctnetlink_conntrack_event(struct notifier_block *this,
        struct nlmsghdr *nlh;
        struct nfgenmsg *nfmsg;
        struct nlattr *nest_parms;
-       struct nf_conn *ct = (struct nf_conn *)ptr;
+       struct nf_ct_event *item = (struct nf_ct_event *)ptr;
+       struct nf_conn *ct = item->ct;
        struct sk_buff *skb;
        unsigned int type;
        sk_buff_data_t b;
@@ -453,7 +444,7 @@ static int ctnetlink_conntrack_event(struct notifier_block *this,
        b = skb->tail;
 
        type |= NFNL_SUBSYS_CTNETLINK << 8;
-       nlh   = NLMSG_PUT(skb, 0, 0, type, sizeof(struct nfgenmsg));
+       nlh   = NLMSG_PUT(skb, item->pid, 0, type, sizeof(struct nfgenmsg));
        nfmsg = NLMSG_DATA(nlh);
 
        nlh->nlmsg_flags    = flags;
@@ -461,6 +452,7 @@ static int ctnetlink_conntrack_event(struct notifier_block *this,
        nfmsg->version  = NFNETLINK_V0;
        nfmsg->res_id   = 0;
 
+       rcu_read_lock();
        nest_parms = nla_nest_start(skb, CTA_TUPLE_ORIG | NLA_F_NESTED);
        if (!nest_parms)
                goto nla_put_failure;
@@ -517,13 +509,15 @@ static int ctnetlink_conntrack_event(struct notifier_block *this,
            && ctnetlink_dump_mark(skb, ct) < 0)
                goto nla_put_failure;
 #endif
+       rcu_read_unlock();
 
        nlh->nlmsg_len = skb->tail - b;
-       nfnetlink_send(skb, 0, group, 0);
+       nfnetlink_send(skb, item->pid, group, item->report);
        return NOTIFY_DONE;
 
-nlmsg_failure:
 nla_put_failure:
+       rcu_read_unlock();
+nlmsg_failure:
        kfree_skb(skb);
        return NOTIFY_DONE;
 }
@@ -729,7 +723,9 @@ ctnetlink_del_conntrack(struct sock *ctnl, struct sk_buff *skb,
                err = ctnetlink_parse_tuple(cda, &tuple, CTA_TUPLE_REPLY, u3);
        else {
                /* Flush the whole table */
-               nf_conntrack_flush(&init_net);
+               nf_conntrack_flush(&init_net, 
+                                  NETLINK_CB(skb).pid, 
+                                  nlmsg_report(nlh));
                return 0;
        }
 
@@ -750,6 +746,14 @@ ctnetlink_del_conntrack(struct sock *ctnl, struct sk_buff *skb,
                }
        }
 
+       nf_conntrack_event_report(IPCT_DESTROY,
+                                 ct,
+                                 NETLINK_CB(skb).pid,
+                                 nlmsg_report(nlh));
+
+       /* death_by_timeout would report the event again */
+       set_bit(IPS_DYING_BIT, &ct->status);
+
        nf_ct_kill(ct);
        nf_ct_put(ct);
 
@@ -795,8 +799,10 @@ ctnetlink_get_conntrack(struct sock *ctnl, struct sk_buff *skb,
                return -ENOMEM;
        }
 
+       rcu_read_lock();
        err = ctnetlink_fill_info(skb2, NETLINK_CB(skb).pid, nlh->nlmsg_seq,
                                  IPCTNL_MSG_CT_NEW, 1, ct);
+       rcu_read_unlock();
        nf_ct_put(ct);
        if (err <= 0)
                goto free;
@@ -922,8 +928,22 @@ ctnetlink_change_helper(struct nf_conn *ct, struct nlattr *cda[])
        }
 
        helper = __nf_conntrack_helper_find_byname(helpname);
-       if (helper == NULL)
+       if (helper == NULL) {
+#ifdef CONFIG_MODULES
+               spin_unlock_bh(&nf_conntrack_lock);
+
+               if (request_module("nfct-helper-%s", helpname) < 0) {
+                       spin_lock_bh(&nf_conntrack_lock);
+                       return -EOPNOTSUPP;
+               }
+
+               spin_lock_bh(&nf_conntrack_lock);
+               helper = __nf_conntrack_helper_find_byname(helpname);
+               if (helper)
+                       return -EAGAIN;
+#endif
                return -EOPNOTSUPP;
+       }
 
        if (help) {
                if (help->helper == helper)
@@ -1079,15 +1099,38 @@ ctnetlink_change_conntrack(struct nf_conn *ct, struct nlattr *cda[])
        return 0;
 }
 
+static inline void
+ctnetlink_event_report(struct nf_conn *ct, u32 pid, int report)
+{
+       unsigned int events = 0;
+
+       if (test_bit(IPS_EXPECTED_BIT, &ct->status))
+               events |= IPCT_RELATED;
+       else
+               events |= IPCT_NEW;
+
+       nf_conntrack_event_report(IPCT_STATUS |
+                                 IPCT_HELPER |
+                                 IPCT_REFRESH |
+                                 IPCT_PROTOINFO |
+                                 IPCT_NATSEQADJ |
+                                 IPCT_MARK |
+                                 events,
+                                 ct,
+                                 pid,
+                                 report);
+}
+
 static int
 ctnetlink_create_conntrack(struct nlattr *cda[],
                           struct nf_conntrack_tuple *otuple,
                           struct nf_conntrack_tuple *rtuple,
-                          struct nf_conn *master_ct)
+                          struct nf_conn *master_ct,
+                          u32 pid,
+                          int report)
 {
        struct nf_conn *ct;
        int err = -EINVAL;
-       struct nf_conn_help *help;
        struct nf_conntrack_helper *helper;
 
        ct = nf_conntrack_alloc(&init_net, otuple, rtuple, GFP_ATOMIC);
@@ -1102,16 +1145,55 @@ ctnetlink_create_conntrack(struct nlattr *cda[],
        ct->status |= IPS_CONFIRMED;
 
        rcu_read_lock();
-       helper = __nf_ct_helper_find(rtuple);
-       if (helper) {
-               help = nf_ct_helper_ext_add(ct, GFP_ATOMIC);
-               if (help == NULL) {
+       if (cda[CTA_HELP]) {
+               char *helpname;
+               err = ctnetlink_parse_help(cda[CTA_HELP], &helpname);
+               if (err < 0) {
+                       rcu_read_unlock();
+                       goto err;
+               }
+
+               helper = __nf_conntrack_helper_find_byname(helpname);
+               if (helper == NULL) {
+                       rcu_read_unlock();
+#ifdef CONFIG_MODULES
+                       if (request_module("nfct-helper-%s", helpname) < 0) {
+                               err = -EOPNOTSUPP;
+                               goto err;
+                       }
+
+                       rcu_read_lock();
+                       helper = __nf_conntrack_helper_find_byname(helpname);
+                       if (helper) {
+                               rcu_read_unlock();
+                               err = -EAGAIN;
+                               goto err;
+                       }
+                       rcu_read_unlock();
+#endif
+                       err = -EOPNOTSUPP;
+                       goto err;
+               } else {
+                       struct nf_conn_help *help;
+
+                       help = nf_ct_helper_ext_add(ct, GFP_ATOMIC);
+                       if (help == NULL) {
+                               rcu_read_unlock();
+                               err = -ENOMEM;
+                               goto err;
+                       }
+
+                       /* not in hash table yet so not strictly necessary */
+                       rcu_assign_pointer(help->helper, helper);
+               }
+       } else {
+               /* try an implicit helper assignation */
+               err = __nf_ct_try_assign_helper(ct, GFP_ATOMIC);
+               if (err < 0) {
                        rcu_read_unlock();
-                       err = -ENOMEM;
                        goto err;
                }
-               /* not in hash table yet so not strictly necessary */
-               rcu_assign_pointer(help->helper, helper);
        }
 
        if (cda[CTA_STATUS]) {
@@ -1151,9 +1233,12 @@ ctnetlink_create_conntrack(struct nlattr *cda[],
                ct->master = master_ct;
        }
 
+       nf_conntrack_get(&ct->ct_general);
        add_timer(&ct->timeout);
        nf_conntrack_hash_insert(ct);
        rcu_read_unlock();
+       ctnetlink_event_report(ct, pid, report);
+       nf_ct_put(ct);
 
        return 0;
 
@@ -1209,7 +1294,7 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb,
                                goto out_unlock;
                        }
                        master_ct = nf_ct_tuplehash_to_ctrack(master_h);
-                       atomic_inc(&master_ct->ct_general.use);
+                       nf_conntrack_get(&master_ct->ct_general);
                }
 
                err = -ENOENT;
@@ -1217,9 +1302,10 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb,
                        err = ctnetlink_create_conntrack(cda,
                                                         &otuple,
                                                         &rtuple,
-                                                        master_ct);
+                                                        master_ct,
+                                                        NETLINK_CB(skb).pid,
+                                                        nlmsg_report(nlh));
                spin_unlock_bh(&nf_conntrack_lock);
-
                if (err < 0 && master_ct)
                        nf_ct_put(master_ct);
 
@@ -1231,6 +1317,8 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb,
         * so there's no need to increase the refcount */
        err = -EEXIST;
        if (!(nlh->nlmsg_flags & NLM_F_EXCL)) {
+               struct nf_conn *ct = nf_ct_tuplehash_to_ctrack(h);
+
                /* we only allow nat config for new conntracks */
                if (cda[CTA_NAT_SRC] || cda[CTA_NAT_DST]) {
                        err = -EOPNOTSUPP;
@@ -1241,8 +1329,19 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb,
                        err = -EOPNOTSUPP;
                        goto out_unlock;
                }
-               err = ctnetlink_change_conntrack(nf_ct_tuplehash_to_ctrack(h),
-                                                cda);
+
+               err = ctnetlink_change_conntrack(ct, cda);
+               if (err == 0) {
+                       nf_conntrack_get(&ct->ct_general);
+                       spin_unlock_bh(&nf_conntrack_lock);
+                       ctnetlink_event_report(ct,
+                                              NETLINK_CB(skb).pid,
+                                              nlmsg_report(nlh));
+                       nf_ct_put(ct);
+               } else
+                       spin_unlock_bh(&nf_conntrack_lock);
+
+               return err;
        }
 
 out_unlock:
@@ -1293,16 +1392,14 @@ ctnetlink_exp_dump_mask(struct sk_buff *skb,
        if (!nest_parms)
                goto nla_put_failure;
 
-       l3proto = nf_ct_l3proto_find_get(tuple->src.l3num);
+       l3proto = __nf_ct_l3proto_find(tuple->src.l3num);
        ret = ctnetlink_dump_tuples_ip(skb, &m, l3proto);
-       nf_ct_l3proto_put(l3proto);
 
        if (unlikely(ret < 0))
                goto nla_put_failure;
 
-       l4proto = nf_ct_l4proto_find_get(tuple->src.l3num, tuple->dst.protonum);
+       l4proto = __nf_ct_l4proto_find(tuple->src.l3num, tuple->dst.protonum);
        ret = ctnetlink_dump_tuples_proto(skb, &m, l4proto);
-       nf_ct_l4proto_put(l4proto);
        if (unlikely(ret < 0))
                goto nla_put_failure;
 
@@ -1379,7 +1476,8 @@ static int ctnetlink_expect_event(struct notifier_block *this,
 {
        struct nlmsghdr *nlh;
        struct nfgenmsg *nfmsg;
-       struct nf_conntrack_expect *exp = (struct nf_conntrack_expect *)ptr;
+       struct nf_exp_event *item = (struct nf_exp_event *)ptr;
+       struct nf_conntrack_expect *exp = item->exp;
        struct sk_buff *skb;
        unsigned int type;
        sk_buff_data_t b;
@@ -1401,7 +1499,7 @@ static int ctnetlink_expect_event(struct notifier_block *this,
        b = skb->tail;
 
        type |= NFNL_SUBSYS_CTNETLINK_EXP << 8;
-       nlh   = NLMSG_PUT(skb, 0, 0, type, sizeof(struct nfgenmsg));
+       nlh   = NLMSG_PUT(skb, item->pid, 0, type, sizeof(struct nfgenmsg));
        nfmsg = NLMSG_DATA(nlh);
 
        nlh->nlmsg_flags    = flags;
@@ -1409,15 +1507,18 @@ static int ctnetlink_expect_event(struct notifier_block *this,
        nfmsg->version      = NFNETLINK_V0;
        nfmsg->res_id       = 0;
 
+       rcu_read_lock();
        if (ctnetlink_exp_dump_expect(skb, exp) < 0)
                goto nla_put_failure;
+       rcu_read_unlock();
 
        nlh->nlmsg_len = skb->tail - b;
-       nfnetlink_send(skb, 0, NFNLGRP_CONNTRACK_EXP_NEW, 0);
+       nfnetlink_send(skb, item->pid, NFNLGRP_CONNTRACK_EXP_NEW, item->report);
        return NOTIFY_DONE;
 
-nlmsg_failure:
 nla_put_failure:
+       rcu_read_unlock();
+nlmsg_failure:
        kfree_skb(skb);
        return NOTIFY_DONE;
 }
@@ -1521,9 +1622,11 @@ ctnetlink_get_expect(struct sock *ctnl, struct sk_buff *skb,
        if (!skb2)
                goto out;
 
+       rcu_read_lock();
        err = ctnetlink_exp_fill_info(skb2, NETLINK_CB(skb).pid,
                                      nlh->nlmsg_seq, IPCTNL_MSG_EXP_NEW,
                                      1, exp);
+       rcu_read_unlock();
        if (err <= 0)
                goto free;
 
@@ -1624,7 +1727,7 @@ ctnetlink_change_expect(struct nf_conntrack_expect *x, struct nlattr *cda[])
 }
 
 static int
-ctnetlink_create_expect(struct nlattr *cda[], u_int8_t u3)
+ctnetlink_create_expect(struct nlattr *cda[], u_int8_t u3, u32 pid, int report)
 {
        struct nf_conntrack_tuple tuple, mask, master_tuple;
        struct nf_conntrack_tuple_hash *h = NULL;
@@ -1653,7 +1756,7 @@ ctnetlink_create_expect(struct nlattr *cda[], u_int8_t u3)
 
        if (!help || !help->helper) {
                /* such conntrack hasn't got any helper, abort */
-               err = -EINVAL;
+               err = -EOPNOTSUPP;
                goto out;
        }
 
@@ -1671,7 +1774,7 @@ ctnetlink_create_expect(struct nlattr *cda[], u_int8_t u3)
        memcpy(&exp->mask.src.u3, &mask.src.u3, sizeof(exp->mask.src.u3));
        exp->mask.src.u.all = mask.src.u.all;
 
-       err = nf_ct_expect_related(exp);
+       err = nf_ct_expect_related_report(exp, pid, report);
        nf_ct_expect_put(exp);
 
 out:
@@ -1704,8 +1807,12 @@ ctnetlink_new_expect(struct sock *ctnl, struct sk_buff *skb,
        if (!exp) {
                spin_unlock_bh(&nf_conntrack_lock);
                err = -ENOENT;
-               if (nlh->nlmsg_flags & NLM_F_CREATE)
-                       err = ctnetlink_create_expect(cda, u3);
+               if (nlh->nlmsg_flags & NLM_F_CREATE) {
+                       err = ctnetlink_create_expect(cda,
+                                                     u3,
+                                                     NETLINK_CB(skb).pid,
+                                                     nlmsg_report(nlh));
+               }
                return err;
        }