Fedora kernel-2.6.17-1.2142_FC4 patched with stable patch-2.6.17.4-vs2.0.2-rc26.diff
[linux-2.6.git] / net / ipv4 / netfilter / ip_nat_helper.c
index a49c722..5d506e0 100644 (file)
@@ -28,8 +28,8 @@
 #include <net/tcp.h>
 #include <net/udp.h>
 
-#define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ip_nat_lock)
-#define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&ip_nat_lock)
+#define ASSERT_READ_LOCK(x)
+#define ASSERT_WRITE_LOCK(x)
 
 #include <linux/netfilter_ipv4/ip_conntrack.h>
 #include <linux/netfilter_ipv4/ip_conntrack_helper.h>
@@ -47,7 +47,7 @@
 #define DUMP_OFFSET(x)
 #endif
 
-DECLARE_LOCK(ip_nat_seqofs_lock);
+static DEFINE_SPINLOCK(ip_nat_seqofs_lock);
 
 /* Setup TCP sequence correction given this change at this sequence */
 static inline void 
@@ -70,9 +70,9 @@ adjust_tcp_sequence(u32 seq,
        DEBUGP("ip_nat_resize_packet: Seq_offset before: ");
        DUMP_OFFSET(this_way);
 
-       LOCK_BH(&ip_nat_seqofs_lock);
+       spin_lock_bh(&ip_nat_seqofs_lock);
 
-       /* SYN adjust. If it's uninitialized, of this is after last
+       /* SYN adjust. If it's uninitialized, or this is after last
         * correction, record it: we don't handle more than one
         * adjustment in the window, but do deal with common case of a
         * retransmit */
@@ -82,7 +82,7 @@ adjust_tcp_sequence(u32 seq,
                    this_way->offset_before = this_way->offset_after;
                    this_way->offset_after += sizediff;
        }
-       UNLOCK_BH(&ip_nat_seqofs_lock);
+       spin_unlock_bh(&ip_nat_seqofs_lock);
 
        DEBUGP("ip_nat_resize_packet: Seq_offset after: ");
        DUMP_OFFSET(this_way);
@@ -142,9 +142,6 @@ static int enlarge_skb(struct sk_buff **pskb, unsigned int extra)
        /* Transfer socket to new skb. */
        if ((*pskb)->sk)
                skb_set_owner_w(nskb, (*pskb)->sk);
-#ifdef CONFIG_NETFILTER_DEBUG
-       nskb->nf_debug = (*pskb)->nf_debug;
-#endif
        kfree_skb(*pskb);
        *pskb = nskb;
        return 1;
@@ -171,7 +168,7 @@ ip_nat_mangle_tcp_packet(struct sk_buff **pskb,
        struct tcphdr *tcph;
        int datalen;
 
-       if (!skb_ip_make_writable(pskb, (*pskb)->len))
+       if (!skb_make_writable(pskb, (*pskb)->len))
                return 0;
 
        if (rep_len > match_len
@@ -192,11 +189,17 @@ ip_nat_mangle_tcp_packet(struct sk_buff **pskb,
        tcph->check = tcp_v4_check(tcph, datalen, iph->saddr, iph->daddr,
                                   csum_partial((char *)tcph, datalen, 0));
 
-       adjust_tcp_sequence(ntohl(tcph->seq),
-                           (int)rep_len - (int)match_len,
-                           ct, ctinfo);
+       if (rep_len != match_len) {
+               set_bit(IPS_SEQ_ADJUST_BIT, &ct->status);
+               adjust_tcp_sequence(ntohl(tcph->seq),
+                                   (int)rep_len - (int)match_len,
+                                   ct, ctinfo);
+               /* Tell TCP window tracking about seq change */
+               ip_conntrack_tcp_update(*pskb, ct, CTINFO2DIR(ctinfo));
+       }
        return 1;
 }
+EXPORT_SYMBOL(ip_nat_mangle_tcp_packet);
                        
 /* Generic function for mangling variable-length address changes inside
  * NATed UDP connections (like the CONNECT DATA XXXXX MESG XXXXX INDEX XXXXX
@@ -226,7 +229,7 @@ ip_nat_mangle_udp_packet(struct sk_buff **pskb,
                               match_offset + match_len)
                return 0;
 
-       if (!skb_ip_make_writable(pskb, (*pskb)->len))
+       if (!skb_make_writable(pskb, (*pskb)->len))
                return 0;
 
        if (rep_len > match_len
@@ -254,6 +257,7 @@ ip_nat_mangle_udp_packet(struct sk_buff **pskb,
 
        return 1;
 }
+EXPORT_SYMBOL(ip_nat_mangle_udp_packet);
 
 /* Adjust one found SACK option including checksum correction */
 static void
@@ -313,7 +317,7 @@ ip_nat_sack_adjust(struct sk_buff **pskb,
        optoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct tcphdr);
        optend = (*pskb)->nh.iph->ihl*4 + tcph->doff*4;
 
-       if (!skb_ip_make_writable(pskb, optend))
+       if (!skb_make_writable(pskb, optend))
                return 0;
 
        dir = CTINFO2DIR(ctinfo);
@@ -346,7 +350,7 @@ ip_nat_sack_adjust(struct sk_buff **pskb,
        return 1;
 }
 
-/* TCP sequence number adjustment.  Returns true or false.  */
+/* TCP sequence number adjustment.  Returns 1 on success, 0 on failure */
 int
 ip_nat_seq_adjust(struct sk_buff **pskb, 
                  struct ip_conntrack *ct, 
@@ -361,12 +365,7 @@ ip_nat_seq_adjust(struct sk_buff **pskb,
        this_way = &ct->nat.info.seq[dir];
        other_way = &ct->nat.info.seq[!dir];
 
-       /* No adjustments to make?  Very common case. */
-       if (!this_way->offset_before && !this_way->offset_after
-           && !other_way->offset_before && !other_way->offset_after)
-               return 1;
-
-       if (!skb_ip_make_writable(pskb, (*pskb)->nh.iph->ihl*4+sizeof(*tcph)))
+       if (!skb_make_writable(pskb, (*pskb)->nh.iph->ihl*4+sizeof(*tcph)))
                return 0;
 
        tcph = (void *)(*pskb)->data + (*pskb)->nh.iph->ihl*4;
@@ -395,60 +394,38 @@ ip_nat_seq_adjust(struct sk_buff **pskb,
        tcph->seq = newseq;
        tcph->ack_seq = newack;
 
-       return ip_nat_sack_adjust(pskb, tcph, ct, ctinfo);
-}
-
-static inline int
-helper_cmp(const struct ip_nat_helper *helper,
-          const struct ip_conntrack_tuple *tuple)
-{
-       return ip_ct_tuple_mask_cmp(tuple, &helper->tuple, &helper->mask);
-}
-
-int ip_nat_helper_register(struct ip_nat_helper *me)
-{
-       int ret = 0;
-
-       WRITE_LOCK(&ip_nat_lock);
-       if (LIST_FIND(&helpers, helper_cmp, struct ip_nat_helper *,&me->tuple))
-               ret = -EBUSY;
-       else
-               list_prepend(&helpers, me);
-       WRITE_UNLOCK(&ip_nat_lock);
-
-       return ret;
-}
-
-static int
-kill_helper(const struct ip_conntrack *i, void *helper)
-{
-       int ret;
+       if (!ip_nat_sack_adjust(pskb, tcph, ct, ctinfo))
+               return 0;
 
-       READ_LOCK(&ip_nat_lock);
-       ret = (i->nat.info.helper == helper);
-       READ_UNLOCK(&ip_nat_lock);
+       ip_conntrack_tcp_update(*pskb, ct, dir);
 
-       return ret;
+       return 1;
 }
+EXPORT_SYMBOL(ip_nat_seq_adjust);
 
-void ip_nat_helper_unregister(struct ip_nat_helper *me)
+/* Setup NAT on this expected conntrack so it follows master. */
+/* If we fail to get a free NAT slot, we'll get dropped on confirm */
+void ip_nat_follow_master(struct ip_conntrack *ct,
+                         struct ip_conntrack_expect *exp)
 {
-       WRITE_LOCK(&ip_nat_lock);
-       /* Autoloading conntrack helper might have failed */
-       if (LIST_FIND(&helpers, helper_cmp, struct ip_nat_helper *,&me->tuple)) {
-               LIST_DELETE(&helpers, me);
-       }
-       WRITE_UNLOCK(&ip_nat_lock);
-
-       /* Someone could be still looking at the helper in a bh. */
-       synchronize_net();
-
-       /* Find anything using it, and umm, kill them.  We can't turn
-          them into normal connections: if we've adjusted SYNs, then
-          they'll ackstorm.  So we just drop it.  We used to just
-          bump module count when a connection existed, but that
-          forces admins to gen fake RSTs or bounce box, either of
-          which is just a long-winded way of making things
-          worse. --RR */
-       ip_ct_selective_cleanup(kill_helper, me);
+       struct ip_nat_range range;
+
+       /* This must be a fresh one. */
+       BUG_ON(ct->status & IPS_NAT_DONE_MASK);
+
+       /* Change src to where master sends to */
+       range.flags = IP_NAT_RANGE_MAP_IPS;
+       range.min_ip = range.max_ip
+               = ct->master->tuplehash[!exp->dir].tuple.dst.ip;
+       /* hook doesn't matter, but it has to do source manip */
+       ip_nat_setup_info(ct, &range, NF_IP_POST_ROUTING);
+
+       /* For DST manip, map port here to where it's expected. */
+       range.flags = (IP_NAT_RANGE_MAP_IPS | IP_NAT_RANGE_PROTO_SPECIFIED);
+       range.min = range.max = exp->saved_proto;
+       range.min_ip = range.max_ip
+               = ct->master->tuplehash[!exp->dir].tuple.src.ip;
+       /* hook doesn't matter, but it has to do destination manip */
+       ip_nat_setup_info(ct, &range, NF_IP_PRE_ROUTING);
 }
+EXPORT_SYMBOL(ip_nat_follow_master);