]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blobdiff - net/netfilter/nf_conntrack_netlink.c
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
[mirror_ubuntu-artful-kernel.git] / net / netfilter / nf_conntrack_netlink.c
index 5f6f2f388928130a2717911fef15c72325ebe2f3..dcf561b5c97a47e627ee00649d756635db0e6fb3 100644 (file)
@@ -417,8 +417,7 @@ nla_put_failure:
        return -1;
 }
 
-static int ctnetlink_dump_ct_seq_adj(struct sk_buff *skb,
-                                    const struct nf_conn *ct)
+static int ctnetlink_dump_ct_seq_adj(struct sk_buff *skb, struct nf_conn *ct)
 {
        struct nf_conn_seqadj *seqadj = nfct_seqadj(ct);
        struct nf_ct_seqadj *seq;
@@ -426,15 +425,20 @@ static int ctnetlink_dump_ct_seq_adj(struct sk_buff *skb,
        if (!(ct->status & IPS_SEQ_ADJUST) || !seqadj)
                return 0;
 
+       spin_lock_bh(&ct->lock);
        seq = &seqadj->seq[IP_CT_DIR_ORIGINAL];
        if (dump_ct_seq_adj(skb, seq, CTA_SEQ_ADJ_ORIG) == -1)
-               return -1;
+               goto err;
 
        seq = &seqadj->seq[IP_CT_DIR_REPLY];
        if (dump_ct_seq_adj(skb, seq, CTA_SEQ_ADJ_REPLY) == -1)
-               return -1;
+               goto err;
 
+       spin_unlock_bh(&ct->lock);
        return 0;
+err:
+       spin_unlock_bh(&ct->lock);
+       return -1;
 }
 
 static int ctnetlink_dump_id(struct sk_buff *skb, const struct nf_conn *ct)
@@ -1417,6 +1421,24 @@ ctnetlink_parse_nat_setup(struct nf_conn *ct,
 }
 #endif
 
+static void
+__ctnetlink_change_status(struct nf_conn *ct, unsigned long on,
+                         unsigned long off)
+{
+       unsigned int bit;
+
+       /* Ignore these unchangable bits */
+       on &= ~IPS_UNCHANGEABLE_MASK;
+       off &= ~IPS_UNCHANGEABLE_MASK;
+
+       for (bit = 0; bit < __IPS_MAX_BIT; bit++) {
+               if (on & (1 << bit))
+                       set_bit(bit, &ct->status);
+               else if (off & (1 << bit))
+                       clear_bit(bit, &ct->status);
+       }
+}
+
 static int
 ctnetlink_change_status(struct nf_conn *ct, const struct nlattr * const cda[])
 {
@@ -1436,10 +1458,7 @@ ctnetlink_change_status(struct nf_conn *ct, const struct nlattr * const cda[])
                /* ASSURED bit can only be set */
                return -EBUSY;
 
-       /* Be careful here, modifying NAT bits can screw up things,
-        * so don't let users modify them directly if they don't pass
-        * nf_nat_range. */
-       ct->status |= status & ~(IPS_NAT_DONE_MASK | IPS_NAT_MASK);
+       __ctnetlink_change_status(ct, status, 0);
        return 0;
 }
 
@@ -1508,23 +1527,11 @@ static int ctnetlink_change_helper(struct nf_conn *ct,
                return 0;
        }
 
+       rcu_read_lock();
        helper = __nf_conntrack_helper_find(helpname, nf_ct_l3num(ct),
                                            nf_ct_protonum(ct));
        if (helper == NULL) {
-#ifdef CONFIG_MODULES
-               spin_unlock_bh(&nf_conntrack_expect_lock);
-
-               if (request_module("nfct-helper-%s", helpname) < 0) {
-                       spin_lock_bh(&nf_conntrack_expect_lock);
-                       return -EOPNOTSUPP;
-               }
-
-               spin_lock_bh(&nf_conntrack_expect_lock);
-               helper = __nf_conntrack_helper_find(helpname, nf_ct_l3num(ct),
-                                                   nf_ct_protonum(ct));
-               if (helper)
-                       return -EAGAIN;
-#endif
+               rcu_read_unlock();
                return -EOPNOTSUPP;
        }
 
@@ -1533,13 +1540,16 @@ static int ctnetlink_change_helper(struct nf_conn *ct,
                        /* update private helper data if allowed. */
                        if (helper->from_nlattr)
                                helper->from_nlattr(helpinfo, ct);
-                       return 0;
+                       err = 0;
                } else
-                       return -EBUSY;
+                       err = -EBUSY;
+       } else {
+               /* we cannot set a helper for an existing conntrack */
+               err = -EOPNOTSUPP;
        }
 
-       /* we cannot set a helper for an existing conntrack */
-       return -EOPNOTSUPP;
+       rcu_read_unlock();
+       return err;
 }
 
 static int ctnetlink_change_timeout(struct nf_conn *ct,
@@ -1630,25 +1640,30 @@ ctnetlink_change_seq_adj(struct nf_conn *ct,
        if (!seqadj)
                return 0;
 
+       spin_lock_bh(&ct->lock);
        if (cda[CTA_SEQ_ADJ_ORIG]) {
                ret = change_seq_adj(&seqadj->seq[IP_CT_DIR_ORIGINAL],
                                     cda[CTA_SEQ_ADJ_ORIG]);
                if (ret < 0)
-                       return ret;
+                       goto err;
 
-               ct->status |= IPS_SEQ_ADJUST;
+               set_bit(IPS_SEQ_ADJUST_BIT, &ct->status);
        }
 
        if (cda[CTA_SEQ_ADJ_REPLY]) {
                ret = change_seq_adj(&seqadj->seq[IP_CT_DIR_REPLY],
                                     cda[CTA_SEQ_ADJ_REPLY]);
                if (ret < 0)
-                       return ret;
+                       goto err;
 
-               ct->status |= IPS_SEQ_ADJUST;
+               set_bit(IPS_SEQ_ADJUST_BIT, &ct->status);
        }
 
+       spin_unlock_bh(&ct->lock);
        return 0;
+err:
+       spin_unlock_bh(&ct->lock);
+       return ret;
 }
 
 static int
@@ -1959,9 +1974,7 @@ static int ctnetlink_new_conntrack(struct net *net, struct sock *ctnl,
        err = -EEXIST;
        ct = nf_ct_tuplehash_to_ctrack(h);
        if (!(nlh->nlmsg_flags & NLM_F_EXCL)) {
-               spin_lock_bh(&nf_conntrack_expect_lock);
                err = ctnetlink_change_conntrack(ct, cda);
-               spin_unlock_bh(&nf_conntrack_expect_lock);
                if (err == 0) {
                        nf_conntrack_eventmask_report((1 << IPCT_REPLY) |
                                                      (1 << IPCT_ASSURED) |
@@ -2294,10 +2307,10 @@ ctnetlink_update_status(struct nf_conn *ct, const struct nlattr * const cda[])
        /* This check is less strict than ctnetlink_change_status()
         * because callers often flip IPS_EXPECTED bits when sending
         * an NFQA_CT attribute to the kernel.  So ignore the
-        * unchangeable bits but do not error out.
+        * unchangeable bits but do not error out. Also user programs
+        * are allowed to clear the bits that they are allowed to change.
         */
-       ct->status = (status & ~IPS_UNCHANGEABLE_MASK) |
-                    (ct->status & IPS_UNCHANGEABLE_MASK);
+       __ctnetlink_change_status(ct, status, ~status);
        return 0;
 }
 
@@ -2351,11 +2364,7 @@ ctnetlink_glue_parse(const struct nlattr *attr, struct nf_conn *ct)
        if (ret < 0)
                return ret;
 
-       spin_lock_bh(&nf_conntrack_expect_lock);
-       ret = ctnetlink_glue_parse_ct((const struct nlattr **)cda, ct);
-       spin_unlock_bh(&nf_conntrack_expect_lock);
-
-       return ret;
+       return ctnetlink_glue_parse_ct((const struct nlattr **)cda, ct);
 }
 
 static int ctnetlink_glue_exp_parse(const struct nlattr * const *cda,