]> Pileus Git - ~andy/linux/blobdiff - net/netfilter/nf_nat_core.c
netfilter: ctnetlink: force null nat binding on insert
[~andy/linux] / net / netfilter / nf_nat_core.c
index d3f5cd6dd962b195ea85775cd0432cf204e697b2..52ca952b802c5e3ea41c83823b90f74365b8fc76 100644 (file)
@@ -432,15 +432,15 @@ nf_nat_setup_info(struct nf_conn *ct,
 }
 EXPORT_SYMBOL(nf_nat_setup_info);
 
-unsigned int
-nf_nat_alloc_null_binding(struct nf_conn *ct, unsigned int hooknum)
+static unsigned int
+__nf_nat_alloc_null_binding(struct nf_conn *ct, enum nf_nat_manip_type manip)
 {
        /* Force range to this IP; let proto decide mapping for
         * per-proto parts (hence not IP_NAT_RANGE_PROTO_SPECIFIED).
         * Use reply in case it's already been mangled (eg local packet).
         */
        union nf_inet_addr ip =
-               (HOOK2MANIP(hooknum) == NF_NAT_MANIP_SRC ?
+               (manip == NF_NAT_MANIP_SRC ?
                ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3 :
                ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u3);
        struct nf_nat_range range = {
@@ -448,7 +448,13 @@ nf_nat_alloc_null_binding(struct nf_conn *ct, unsigned int hooknum)
                .min_addr       = ip,
                .max_addr       = ip,
        };
-       return nf_nat_setup_info(ct, &range, HOOK2MANIP(hooknum));
+       return nf_nat_setup_info(ct, &range, manip);
+}
+
+unsigned int
+nf_nat_alloc_null_binding(struct nf_conn *ct, unsigned int hooknum)
+{
+       return __nf_nat_alloc_null_binding(ct, HOOK2MANIP(hooknum));
 }
 EXPORT_SYMBOL_GPL(nf_nat_alloc_null_binding);
 
@@ -702,9 +708,9 @@ static const struct nla_policy nat_nla_policy[CTA_NAT_MAX+1] = {
 
 static int
 nfnetlink_parse_nat(const struct nlattr *nat,
-                   const struct nf_conn *ct, struct nf_nat_range *range)
+                   const struct nf_conn *ct, struct nf_nat_range *range,
+                   const struct nf_nat_l3proto *l3proto)
 {
-       const struct nf_nat_l3proto *l3proto;
        struct nlattr *tb[CTA_NAT_MAX+1];
        int err;
 
@@ -714,38 +720,46 @@ nfnetlink_parse_nat(const struct nlattr *nat,
        if (err < 0)
                return err;
 
-       rcu_read_lock();
-       l3proto = __nf_nat_l3proto_find(nf_ct_l3num(ct));
-       if (l3proto == NULL) {
-               err = -EAGAIN;
-               goto out;
-       }
        err = l3proto->nlattr_to_range(tb, range);
        if (err < 0)
-               goto out;
+               return err;
 
        if (!tb[CTA_NAT_PROTO])
-               goto out;
+               return 0;
 
-       err = nfnetlink_parse_nat_proto(tb[CTA_NAT_PROTO], ct, range);
-out:
-       rcu_read_unlock();
-       return err;
+       return nfnetlink_parse_nat_proto(tb[CTA_NAT_PROTO], ct, range);
 }
 
+/* This function is called under rcu_read_lock() */
 static int
 nfnetlink_parse_nat_setup(struct nf_conn *ct,
                          enum nf_nat_manip_type manip,
                          const struct nlattr *attr)
 {
        struct nf_nat_range range;
+       const struct nf_nat_l3proto *l3proto;
        int err;
 
-       err = nfnetlink_parse_nat(attr, ct, &range);
+       /* Should not happen, restricted to creating new conntracks
+        * via ctnetlink.
+        */
+       if (WARN_ON_ONCE(nf_nat_initialized(ct, manip)))
+               return -EEXIST;
+
+       /* Make sure that L3 NAT is there by when we call nf_nat_setup_info to
+        * attach the null binding, otherwise this may oops.
+        */
+       l3proto = __nf_nat_l3proto_find(nf_ct_l3num(ct));
+       if (l3proto == NULL)
+               return -EAGAIN;
+
+       /* No NAT information has been passed, allocate the null-binding */
+       if (attr == NULL)
+               return __nf_nat_alloc_null_binding(ct, manip);
+
+       err = nfnetlink_parse_nat(attr, ct, &range, l3proto);
        if (err < 0)
                return err;
-       if (nf_nat_initialized(ct, manip))
-               return -EEXIST;
 
        return nf_nat_setup_info(ct, &range, manip);
 }