X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=net%2Fipv4%2Fnetfilter%2Fip_nat_helper.c;h=1637b96d8c0110c155efd1fe7492570cfdb34650;hb=6a77f38946aaee1cd85eeec6cf4229b204c15071;hp=a49c722adbc195f8647f9b1228b272444774ffd8;hpb=5273a3df6485dc2ad6aa7ddd441b9a21970f003b;p=linux-2.6.git diff --git a/net/ipv4/netfilter/ip_nat_helper.c b/net/ipv4/netfilter/ip_nat_helper.c index a49c722ad..1637b96d8 100644 --- a/net/ipv4/netfilter/ip_nat_helper.c +++ b/net/ipv4/netfilter/ip_nat_helper.c @@ -47,7 +47,7 @@ #define DUMP_OFFSET(x) #endif -DECLARE_LOCK(ip_nat_seqofs_lock); +static DECLARE_LOCK(ip_nat_seqofs_lock); /* Setup TCP sequence correction given this change at this sequence */ static inline void @@ -72,7 +72,7 @@ adjust_tcp_sequence(u32 seq, 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 */ @@ -192,9 +192,14 @@ 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; } @@ -346,7 +351,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,11 +366,6 @@ 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))) return 0; @@ -395,60 +395,36 @@ 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; } -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); }