# ChangeSet # 2005/01/24 16:40:33-08:00 herbert@gondor.apana.org.au # [IPV4/IPV6]: In ip_fragment(), reset ip_summed field on SKB sub-frags. # # If we forward a fragmented packet, we can have ip_summed # set to CHECKSUM_HW or similar. This is fine for local # protocol processing, but once if we are forwarding this # packet we want to reset ip_summed to CHECKSUM_NONE. # # Signed-off-by: Herbert Xu # Signed-off-by: David S. Miller # # ChangeSet # 2005/01/26 22:03:38-08:00 kaber@trash.net # [IPV4]: Keep fragment queues private to each user. # # Signed-off-by: Patrick McHardy # Signed-off-by: David S. Miller # diff -Naru a/include/linux/netfilter_ipv4/ip_conntrack.h b/include/linux/netfilter_ipv4/ip_conntrack.h --- a/include/linux/netfilter_ipv4/ip_conntrack.h 2005-03-29 07:19:50 -08:00 +++ b/include/linux/netfilter_ipv4/ip_conntrack.h 2005-03-29 07:19:50 -08:00 @@ -262,10 +262,9 @@ /* Fake conntrack entry for untracked connections */ extern struct ip_conntrack ip_conntrack_untracked; -extern int ip_ct_no_defrag; /* Returns new sk_buff, or NULL */ struct sk_buff * -ip_ct_gather_frags(struct sk_buff *skb); +ip_ct_gather_frags(struct sk_buff *skb, u_int32_t user); /* Iterate over all conntracks: if iter returns true, it's deleted. */ extern void diff -Naru a/include/net/ip.h b/include/net/ip.h --- a/include/net/ip.h 2005-03-29 07:19:50 -08:00 +++ b/include/net/ip.h 2005-03-29 07:19:50 -08:00 @@ -286,9 +286,20 @@ /* * Functions provided by ip_fragment.o */ - -struct sk_buff *ip_defrag(struct sk_buff *skb); -extern void ipfrag_flush(void); + +enum ip_defrag_users +{ + IP_DEFRAG_LOCAL_DELIVER, + IP_DEFRAG_CALL_RA_CHAIN, + IP_DEFRAG_CONNTRACK_IN, + IP_DEFRAG_CONNTRACK_OUT, + IP_DEFRAG_NAT_OUT, + IP_DEFRAG_VS_IN, + IP_DEFRAG_VS_OUT, + IP_DEFRAG_VS_FWD +}; + +struct sk_buff *ip_defrag(struct sk_buff *skb, u32 user); extern int ip_frag_nqueues; extern atomic_t ip_frag_mem; diff -Naru a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c --- a/net/ipv4/ip_fragment.c 2005-03-29 07:19:50 -08:00 +++ b/net/ipv4/ip_fragment.c 2005-03-29 07:19:50 -08:00 @@ -73,6 +73,7 @@ struct ipq { struct ipq *next; /* linked list pointers */ struct list_head lru_list; /* lru list member */ + u32 user; u32 saddr; u32 daddr; u16 id; @@ -243,13 +244,13 @@ /* Memory limiting on fragments. Evictor trashes the oldest * fragment queue until we are back under the threshold. */ -static void __ip_evictor(int threshold) +static void ip_evictor(void) { struct ipq *qp; struct list_head *tmp; int work; - work = atomic_read(&ip_frag_mem) - threshold; + work = atomic_read(&ip_frag_mem) - sysctl_ipfrag_low_thresh; if (work <= 0) return; @@ -274,11 +275,6 @@ } } -static inline void ip_evictor(void) -{ - __ip_evictor(sysctl_ipfrag_low_thresh); -} - /* * Oops, a fragment queue timed out. Kill it and send an ICMP reply. */ @@ -325,7 +321,8 @@ if(qp->id == qp_in->id && qp->saddr == qp_in->saddr && qp->daddr == qp_in->daddr && - qp->protocol == qp_in->protocol) { + qp->protocol == qp_in->protocol && + qp->user == qp_in->user) { atomic_inc(&qp->refcnt); write_unlock(&ipfrag_lock); qp_in->last_in |= COMPLETE; @@ -352,7 +349,7 @@ } /* Add an entry to the 'ipq' queue for a newly received IP datagram. */ -static struct ipq *ip_frag_create(unsigned hash, struct iphdr *iph) +static struct ipq *ip_frag_create(unsigned hash, struct iphdr *iph, u32 user) { struct ipq *qp; @@ -364,6 +361,7 @@ qp->id = iph->id; qp->saddr = iph->saddr; qp->daddr = iph->daddr; + qp->user = user; qp->len = 0; qp->meat = 0; qp->fragments = NULL; @@ -386,7 +384,7 @@ /* Find the correct entry in the "incomplete datagrams" queue for * this IP datagram, and create new one, if nothing is found. */ -static inline struct ipq *ip_find(struct iphdr *iph) +static inline struct ipq *ip_find(struct iphdr *iph, u32 user) { __u16 id = iph->id; __u32 saddr = iph->saddr; @@ -400,7 +398,8 @@ if(qp->id == id && qp->saddr == saddr && qp->daddr == daddr && - qp->protocol == protocol) { + qp->protocol == protocol && + qp->user == user) { atomic_inc(&qp->refcnt); read_unlock(&ipfrag_lock); return qp; @@ -408,7 +407,7 @@ } read_unlock(&ipfrag_lock); - return ip_frag_create(hash, iph); + return ip_frag_create(hash, iph, user); } /* Add new segment to existing queue. */ @@ -642,7 +641,7 @@ } /* Process an incoming IP datagram fragment. */ -struct sk_buff *ip_defrag(struct sk_buff *skb) +struct sk_buff *ip_defrag(struct sk_buff *skb, u32 user) { struct iphdr *iph = skb->nh.iph; struct ipq *qp; @@ -657,7 +656,7 @@ dev = skb->dev; /* Lookup (or create) queue header */ - if ((qp = ip_find(iph)) != NULL) { + if ((qp = ip_find(iph, user)) != NULL) { struct sk_buff *ret = NULL; spin_lock(&qp->lock); @@ -689,10 +688,4 @@ add_timer(&ipfrag_secret_timer); } -void ipfrag_flush(void) -{ - __ip_evictor(0); -} - EXPORT_SYMBOL(ip_defrag); -EXPORT_SYMBOL(ipfrag_flush); diff -Naru a/net/ipv4/ip_input.c b/net/ipv4/ip_input.c --- a/net/ipv4/ip_input.c 2005-03-29 07:19:50 -08:00 +++ b/net/ipv4/ip_input.c 2005-03-29 07:19:50 -08:00 @@ -172,7 +172,7 @@ (!sk->sk_bound_dev_if || sk->sk_bound_dev_if == skb->dev->ifindex)) { if (skb->nh.iph->frag_off & htons(IP_MF|IP_OFFSET)) { - skb = ip_defrag(skb); + skb = ip_defrag(skb, IP_DEFRAG_CALL_RA_CHAIN); if (skb == NULL) { read_unlock(&ip_ra_lock); return 1; @@ -273,7 +273,7 @@ */ if (skb->nh.iph->frag_off & htons(IP_MF|IP_OFFSET)) { - skb = ip_defrag(skb); + skb = ip_defrag(skb, IP_DEFRAG_LOCAL_DELIVER); if (!skb) return 0; } diff -Naru a/net/ipv4/ipvs/ip_vs_core.c b/net/ipv4/ipvs/ip_vs_core.c --- a/net/ipv4/ipvs/ip_vs_core.c 2005-03-29 07:19:50 -08:00 +++ b/net/ipv4/ipvs/ip_vs_core.c 2005-03-29 07:19:50 -08:00 @@ -544,9 +544,9 @@ } static inline struct sk_buff * -ip_vs_gather_frags(struct sk_buff *skb) +ip_vs_gather_frags(struct sk_buff *skb, u_int32_t user) { - skb = ip_defrag(skb); + skb = ip_defrag(skb, user); if (skb) ip_send_check(skb->nh.iph); return skb; @@ -620,7 +620,7 @@ /* reassemble IP fragments */ if (skb->nh.iph->frag_off & __constant_htons(IP_MF|IP_OFFSET)) { - skb = ip_vs_gather_frags(skb); + skb = ip_vs_gather_frags(skb, IP_DEFRAG_VS_OUT); if (!skb) return NF_STOLEN; *pskb = skb; @@ -759,7 +759,7 @@ /* reassemble IP fragments */ if (unlikely(iph->frag_off & __constant_htons(IP_MF|IP_OFFSET) && !pp->dont_defrag)) { - skb = ip_vs_gather_frags(skb); + skb = ip_vs_gather_frags(skb, IP_DEFRAG_VS_OUT); if (!skb) return NF_STOLEN; iph = skb->nh.iph; @@ -839,7 +839,8 @@ * forward to the right destination host if relevant. * Currently handles error types - unreachable, quench, ttl exceeded. */ -static int ip_vs_in_icmp(struct sk_buff **pskb, int *related) +static int +ip_vs_in_icmp(struct sk_buff **pskb, int *related, unsigned int hooknum) { struct sk_buff *skb = *pskb; struct iphdr *iph; @@ -853,7 +854,9 @@ /* reassemble IP fragments */ if (skb->nh.iph->frag_off & __constant_htons(IP_MF|IP_OFFSET)) { - skb = ip_vs_gather_frags(skb); + skb = ip_vs_gather_frags(skb, + hooknum == NF_IP_LOCAL_IN ? + IP_DEFRAG_VS_IN : IP_DEFRAG_VS_FWD); if (!skb) return NF_STOLEN; *pskb = skb; @@ -962,7 +965,7 @@ iph = skb->nh.iph; if (unlikely(iph->protocol == IPPROTO_ICMP)) { - int related, verdict = ip_vs_in_icmp(pskb, &related); + int related, verdict = ip_vs_in_icmp(pskb, &related, hooknum); if (related) return verdict; @@ -1057,7 +1060,7 @@ if ((*pskb)->nh.iph->protocol != IPPROTO_ICMP) return NF_ACCEPT; - return ip_vs_in_icmp(pskb, &r); + return ip_vs_in_icmp(pskb, &r, hooknum); } diff -Naru a/net/ipv4/netfilter/ip_conntrack_core.c b/net/ipv4/netfilter/ip_conntrack_core.c --- a/net/ipv4/netfilter/ip_conntrack_core.c 2005-03-29 07:19:50 -08:00 +++ b/net/ipv4/netfilter/ip_conntrack_core.c 2005-03-29 07:19:50 -08:00 @@ -936,29 +936,22 @@ } } -int ip_ct_no_defrag; - /* Returns new sk_buff, or NULL */ struct sk_buff * -ip_ct_gather_frags(struct sk_buff *skb) +ip_ct_gather_frags(struct sk_buff *skb, u_int32_t user) { struct sock *sk = skb->sk; #ifdef CONFIG_NETFILTER_DEBUG unsigned int olddebug = skb->nf_debug; #endif - if (unlikely(ip_ct_no_defrag)) { - kfree_skb(skb); - return NULL; - } - if (sk) { sock_hold(sk); skb_orphan(skb); } local_bh_disable(); - skb = ip_defrag(skb); + skb = ip_defrag(skb, user); local_bh_enable(); if (!skb) { diff -Naru a/net/ipv4/netfilter/ip_conntrack_standalone.c b/net/ipv4/netfilter/ip_conntrack_standalone.c --- a/net/ipv4/netfilter/ip_conntrack_standalone.c 2005-03-29 07:19:50 -08:00 +++ b/net/ipv4/netfilter/ip_conntrack_standalone.c 2005-03-29 07:19:50 -08:00 @@ -391,7 +391,10 @@ /* Gather fragments. */ if ((*pskb)->nh.iph->frag_off & htons(IP_MF|IP_OFFSET)) { - *pskb = ip_ct_gather_frags(*pskb); + *pskb = ip_ct_gather_frags(*pskb, + hooknum == NF_IP_PRE_ROUTING ? + IP_DEFRAG_CONNTRACK_IN : + IP_DEFRAG_CONNTRACK_OUT); if (!*pskb) return NF_STOLEN; } @@ -823,12 +826,6 @@ cleanup_defraglocalops: nf_unregister_hook(&ip_conntrack_defrag_local_out_ops); cleanup_defragops: - /* Frag queues may hold fragments with skb->dst == NULL */ - ip_ct_no_defrag = 1; - synchronize_net(); - local_bh_disable(); - ipfrag_flush(); - local_bh_enable(); nf_unregister_hook(&ip_conntrack_defrag_ops); cleanup_proc_stat: #ifdef CONFIG_PROC_FS diff -Naru a/net/ipv4/netfilter/ip_nat_standalone.c b/net/ipv4/netfilter/ip_nat_standalone.c --- a/net/ipv4/netfilter/ip_nat_standalone.c 2005-03-29 07:19:50 -08:00 +++ b/net/ipv4/netfilter/ip_nat_standalone.c 2005-03-29 07:19:50 -08:00 @@ -195,7 +195,7 @@ I'm starting to have nightmares about fragments. */ if ((*pskb)->nh.iph->frag_off & htons(IP_MF|IP_OFFSET)) { - *pskb = ip_ct_gather_frags(*pskb); + *pskb = ip_ct_gather_frags(*pskb, IP_DEFRAG_NAT_OUT); if (!*pskb) return NF_STOLEN; diff -Naru a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c --- a/net/ipv4/ip_output.c 2005-03-16 12:07:44 -08:00 +++ b/net/ipv4/ip_output.c 2005-03-16 12:07:44 -08:00 @@ -504,6 +504,7 @@ /* Prepare header of the next frame, * before previous one went down. */ if (frag) { + frag->ip_summed = CHECKSUM_NONE; frag->h.raw = frag->data; frag->nh.raw = __skb_push(frag, hlen); memcpy(frag->nh.raw, iph, hlen); diff -Naru a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c --- a/net/ipv6/ip6_output.c 2005-03-16 12:07:44 -08:00 +++ b/net/ipv6/ip6_output.c 2005-03-16 12:07:44 -08:00 @@ -592,6 +592,7 @@ /* Prepare header of the next frame, * before previous one went down. */ if (frag) { + frag->ip_summed = CHECKSUM_NONE; frag->h.raw = frag->data; fh = (struct frag_hdr*)__skb_push(frag, sizeof(struct frag_hdr)); frag->nh.raw = __skb_push(frag, hlen);