: keep fragment queues private to each user
Subsystems (4)
PATCH 1/1 : keep fragment queues private to each user
Hi Dave,
This patch keeps fragment queues private to each ip_defrag user to avoid skbs jumping between different callers. It shouldn't change any wanted behaviour, the only questionable one was ip_call_ra_chain, but the RA option is included in each fragment. If you're fine with the patch I'm going to send a 2.4 version later.
Regards
Patrick
PS: The patch should apply in any order with the netfilter patches I just sent.
# This is a BitKeeper generated diff -Nru style patch. #
# ChangeSet
# 2005/01/25 05:35:12+01:00 [email protected]
# [IPV4]: keep fragment queues private to each user
#
# Signed-off-by: Patrick McHardy <[email protected]>
#
# net/ipv4/netfilter/ip_nat_standalone.c # 2005/01/25 05:35:04+01:00 [email protected] +1 -1 # [IPV4]: keep fragment queues private to each user # # Signed-off-by: Patrick McHardy <[email protected]> # # net/ipv4/netfilter/ip_conntrack_standalone.c # 2005/01/25 05:35:04+01:00 [email protected] +4 -7 # [IPV4]: keep fragment queues private to each user # # Signed-off-by: Patrick McHardy <[email protected]> # # net/ipv4/netfilter/ip_conntrack_core.c # 2005/01/25 05:35:04+01:00 [email protected] +2 -9 # [IPV4]: keep fragment queues private to each user # # Signed-off-by: Patrick McHardy <[email protected]> # # net/ipv4/ipvs/ip_vs_core.c # 2005/01/25 05:35:04+01:00 [email protected] +11 -8 # [IPV4]: keep fragment queues private to each user # # Signed-off-by: Patrick McHardy <[email protected]> # # net/ipv4/ip_input.c # 2005/01/25 05:35:04+01:00 [email protected] +2 -2 # [IPV4]: keep fragment queues private to each user # # Signed-off-by: Patrick McHardy <[email protected]> # # net/ipv4/ip_fragment.c # 2005/01/25 05:35:04+01:00 [email protected] +13 -20 # [IPV4]: keep fragment queues private to each user # # Signed-off-by: Patrick McHardy <[email protected]> # # include/net/ip.h # 2005/01/25 05:35:04+01:00 [email protected] +14 -3 # [IPV4]: keep fragment queues private to each user # # Signed-off-by: Patrick McHardy <[email protected]> # # include/linux/netfilter_ipv4/ip_conntrack.h # 2005/01/25 05:35:04+01:00 [email protected] +1 -2 # [IPV4]: keep fragment queues private to each user # # Signed-off-by: Patrick McHardy <[email protected]> # diff -Nru 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-01-25 05:35:30 +01:00 +++ b/include/linux/netfilter_ipv4/ip_conntrack.h 2005-01-25 05:35:30 +01: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
include/net/ip.h +14 -3
@@ -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;
net/ipv4/ip_fragment.c +13 -20
@@ -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 == 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);
net/ipv4/ip_input.c +2 -2
@@ -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 ; }
net/ipv4/ipvs/ip_vs_core.c +11 -8
@@ -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); }
net/ipv4/netfilter/ip_conntrack_core.c +2 -9
@@ -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) {
net/ipv4/netfilter/ip_conntrack_standalone.c +4 -7
@@ -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
net/ipv4/netfilter/ip_nat_standalone.c +1 -1
@@ -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 ;
Quoted 6 lines · Patrick McHardy
This patch keeps fragment queues private to each
ip_defraguser to avoid skbs jumping between different callers. It shouldn't change any wanted behaviour, the only questionable one wasip_call_ra_chain, but the RA option is included in each fragment. If you're fine with the patch I'm going to send a 2.4 version later.
Patch looks good.
However, I think this is still not enough. What about fragments that come from different interfaces? Fragments with different security paths?
Cheers,
Signature · 4 lines
Visit Openswan at http://www.openswan.org/
Email: Herbert Xu ~{PmV>HI~} <[email protected]>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txtQuoted 4 lines · Herbert Xu
However, I think this is still not enough. What about fragments that come from different interfaces? Fragments with different security paths?
Fragments with different security paths are indeed a problem, I already tried fixing them by keeping the secpaths of all fragments on a list in the head secpath and changing __xfrm_policy_check to deal with them, but it is pretty tricky to get right. For different
interfaces: I'm not exactly sure where this is a problem, except that any classification will only look at the head fragment interface.
Regards
Patrick
Quoted 4 lines · Patrick McHardy
deal with them, but it is pretty tricky to get right. For different
interfaces: I'm not exactly sure where this is a problem, except that any classification will only look at the head fragment interface.
Well consider an IPIP interface over a transport mode ESP SA...
Signature · 4 lines
Visit Openswan at http://www.openswan.org/
Email: Herbert Xu ~{PmV>HI~} <[email protected]>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txtDavid S. Miller
maintainerQuoted 5 lines · On Tue, 25 Jan 2005 05:57:47 +0100 Patrick McHardy <[email protected]> wrote:
This patch keeps fragment queues private to each
ip_defraguser to avoid skbs jumping between different callers. It shouldn't change any wanted behaviour, the only questionable one wasip_call_ra_chain, but the RA option is included in each fragment. If you're fine with the patch I'm going to send a 2.4 version later.
Please fix the CONFIG_SMP build. You use some variable "user" in ip_frag_intern(), yet don't add this as an argument or anything like that.
The "user" use you added is protected by CONFIG_SMP, so my guess is that you compile and tested this without SMP enabled.
Quoted 10 lines · David S. Miller
Quoted 6 lines · On Tue, 25 Jan 2005 05:57:47 +0100 Patrick McHardy <[email protected]> wrote:
This patch keeps fragment queues private to each
ip_defraguser to avoid skbs jumping between different callers. It shouldn't change any wanted behaviour, the only questionable one wasip_call_ra_chain, but the RA option is included in each fragment. If you're fine with the patch I'm going to send a 2.4 version later.
Quoted 4 lines
Please fix the
CONFIG_SMPbuild. You use some variable "user" inip_frag_intern(), yet don't add this as an argument or anything like that.
Sorry, new patch attached. I'll send the 2.4 patch later tonight.
Regards
Patrick
# This is a BitKeeper generated diff -Nru style patch. #
# ChangeSet
# 2005/01/27 00:20:54+01:00 [email protected]
# [IPV4]: Keep fragment queues private to each user
#
# Signed-off-by: Patrick McHardy <[email protected]>
#
# net/ipv4/netfilter/ip_nat_standalone.c # 2005/01/27 00:20:46+01:00 [email protected] +1 -1 # [IPV4]: Keep fragment queues private to each user # # Signed-off-by: Patrick McHardy <[email protected]> # # net/ipv4/netfilter/ip_conntrack_standalone.c # 2005/01/27 00:20:46+01:00 [email protected] +4 -7 # [IPV4]: Keep fragment queues private to each user # # Signed-off-by: Patrick McHardy <[email protected]> # # net/ipv4/netfilter/ip_conntrack_core.c # 2005/01/27 00:20:46+01:00 [email protected] +2 -9 # [IPV4]: Keep fragment queues private to each user # # Signed-off-by: Patrick McHardy <[email protected]> # # net/ipv4/ipvs/ip_vs_core.c # 2005/01/27 00:20:46+01:00 [email protected] +11 -8 # [IPV4]: Keep fragment queues private to each user # # Signed-off-by: Patrick McHardy <[email protected]> # # net/ipv4/ip_input.c # 2005/01/27 00:20:46+01:00 [email protected] +2 -2 # [IPV4]: Keep fragment queues private to each user # # Signed-off-by: Patrick McHardy <[email protected]> # # net/ipv4/ip_fragment.c # 2005/01/27 00:20:46+01:00 [email protected] +13 -20 # [IPV4]: Keep fragment queues private to each user # # Signed-off-by: Patrick McHardy <[email protected]> # # include/net/ip.h # 2005/01/27 00:20:46+01:00 [email protected] +14 -3 # [IPV4]: Keep fragment queues private to each user # # Signed-off-by: Patrick McHardy <[email protected]> # # include/linux/netfilter_ipv4/ip_conntrack.h # 2005/01/27 00:20:46+01:00 [email protected] +1 -2 # [IPV4]: Keep fragment queues private to each user # # Signed-off-by: Patrick McHardy <[email protected]> # diff -Nru 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-01-27 00:22:21 +01:00 +++ b/include/linux/netfilter_ipv4/ip_conntrack.h 2005-01-27 00:22:21 +01: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
include/net/ip.h +14 -3
@@ -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;
net/ipv4/ip_fragment.c +13 -20
@@ -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);
net/ipv4/ip_input.c +2 -2
@@ -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 ; }
net/ipv4/ipvs/ip_vs_core.c +11 -8
@@ -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); }
net/ipv4/netfilter/ip_conntrack_core.c +2 -9
@@ -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) {
net/ipv4/netfilter/ip_conntrack_standalone.c +4 -7
@@ -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
net/ipv4/netfilter/ip_nat_standalone.c +1 -1
@@ -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 ;
PATCH 1/1 : keep fragment queues private to each user
2.4 backport of the ip_defrag fix.
# This is a BitKeeper generated diff -Nru style patch. #
# ChangeSet
# 2005/01/27 03:42:30+01:00 [email protected]
# [IPV4]: Keep fragment queues private to each user
#
# Signed-off-by: Patrick McHardy <[email protected]>
#
# net/netsyms.c # 2005/01/27 03:42:28+01:00 [email protected] +0 -1 # [IPV4]: Keep fragment queues private to each user # # Signed-off-by: Patrick McHardy <[email protected]> # # net/ipv4/netfilter/ip_nat_standalone.c # 2005/01/27 03:42:28+01:00 [email protected] +1 -1 # [IPV4]: Keep fragment queues private to each user # # Signed-off-by: Patrick McHardy <[email protected]> # # net/ipv4/netfilter/ip_conntrack_standalone.c # 2005/01/27 03:42:28+01:00 [email protected] +0 -7 # [IPV4]: Keep fragment queues private to each user # # Signed-off-by: Patrick McHardy <[email protected]> # # net/ipv4/netfilter/ip_conntrack_core.c # 2005/01/27 03:42:28+01:00 [email protected] +6 -10 # [IPV4]: Keep fragment queues private to each user # # Signed-off-by: Patrick McHardy <[email protected]> # # net/ipv4/ipvs/ip_vs_core.c # 2005/01/27 03:42:28+01:00 [email protected] +3 -3 # [IPV4]: Keep fragment queues private to each user # # Signed-off-by: Patrick McHardy <[email protected]> # # net/ipv4/ip_input.c # 2005/01/27 03:42:28+01:00 [email protected] +2 -2 # [IPV4]: Keep fragment queues private to each user # # Signed-off-by: Patrick McHardy <[email protected]> # # net/ipv4/ip_fragment.c # 2005/01/27 03:42:28+01:00 [email protected] +13 -19 # [IPV4]: Keep fragment queues private to each user # # Signed-off-by: Patrick McHardy <[email protected]> # # include/net/ip.h # 2005/01/27 03:42:28+01:00 [email protected] +13 -3 # [IPV4]: Keep fragment queues private to each user # # Signed-off-by: Patrick McHardy <[email protected]> # # include/linux/netfilter_ipv4/ip_conntrack.h # 2005/01/27 03:42:28+01:00 [email protected] +1 -2 # [IPV4]: Keep fragment queues private to each user # # Signed-off-by: Patrick McHardy <[email protected]> # diff -Nru 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-01-27 03:42:45 +01:00 +++ b/include/linux/netfilter_ipv4/ip_conntrack.h 2005-01-27 03:42:45 +01:00 @@ -249,10 +249,9 @@ /* Call me when a conntrack is destroyed. */ extern void (*ip_conntrack_destroyed)(struct ip_conntrack *conntrack);
-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);
/* Delete all conntracks which match. */
extern void
include/net/ip.h +13 -3
@@ -227,9 +227,19 @@/* * 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_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;
net/ipv4/ip_fragment.c +13 -19
@@ -72,6 +72,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;
@@ -242,13 +243,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 ;
@@ -273,11 +274,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. */
@@ -324,7 +320,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 ;
@@ -351,7 +348,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;
@@ -363,6 +360,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 ;
@@ -385,7 +383,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;
@@ -399,7 +397,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;
@@ -407,7 +406,7 @@ }read_unlock (&ipfrag_lock); -return ip_frag_create (hash, iph); +return ip_frag_create (hash, iph, user); }/* Add new segment to existing queue. */
@@ -641,7 +640,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;
@@ -656,7 +655,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);
@@ -686,9 +685,4 @@ ipfrag_secret_timer.function = ipfrag_secret_rebuild; ipfrag_secret_timer.expires = jiffies + sysctl_ipfrag_secret_interval;add_timer (&ipfrag_secret_timer); -} - -void ipfrag_flush (void ) -{ -__ip_evictor (0 ); }
net/ipv4/ip_input.c +2 -2
@@ -170,7 +170,7 @@ && ((sk->bound_dev_if == 0) || (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 ;
@@ -291,7 +291,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 ; }
net/ipv4/ipvs/ip_vs_core.c +3 -3
@@ -506,7 +506,7 @@/* reassemble IP fragments, but will it happen in ICMP packets?? */ if (skb->nh.iph->frag_off &__constant_htons (IP_MF |IP_OFFSET )) { - skb =ip_defrag (skb); + skb =ip_defrag (skb,IP_DEFRAG_VS_OUT );if (!skb)return NF_STOLEN ; *skb_p = skb;
@@ -658,7 +658,7 @@/* reassemble IP fragments */ if (iph->frag_off &__constant_htons (IP_MF |IP_OFFSET )) { - skb =ip_defrag (skb); + skb =ip_defrag (skb,IP_DEFRAG_VS_OUT );if (!skb)return NF_STOLEN ; iph = skb->nh.iph;
@@ -1164,7 +1164,7 @@return NF_ACCEPT ;if (iph->frag_off &__constant_htons (IP_MF |IP_OFFSET )) { - skb =ip_defrag (skb); + skb =ip_defrag (skb,IP_DEFRAG_VS_FWD );if (!skb)return NF_STOLEN ; *skb_p = skb;
net/ipv4/netfilter/ip_conntrack_core.c +6 -10
@@ -834,7 +834,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 ; }
@@ -1183,29 +1186,22 @@WRITE_UNLOCK (&ip_conntrack_lock); } -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) {
net/ipv4/netfilter/ip_conntrack_standalone.c +0 -7
@@ -393,13 +393,6 @@ cleanup_inandlocalops:nf_unregister_hook (&ip_conntrack_local_out_ops); cleanup_inops: -/* Frag queues may hold fragments with skb->dst == NULL */ - ip_ct_no_defrag =1 ; -local_bh_disable (); -br_write_lock (BR_NETPROTO_LOCK ); -br_write_unlock (BR_NETPROTO_LOCK ); -ipfrag_flush (); -local_bh_enable ();nf_unregister_hook (&ip_conntrack_in_ops); cleanup_proc:proc_net_remove ("ip_conntrack" );
net/ipv4/netfilter/ip_nat_standalone.c +1 -1
@@ -201,7 +201,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 ;
net/netsyms.c +0 -1
@@ -287,7 +287,6 @@EXPORT_SYMBOL (inetdev_by_index);EXPORT_SYMBOL (in_dev_finish_destroy);EXPORT_SYMBOL (ip_defrag); -EXPORT_SYMBOL (ipfrag_flush);/* Route manipulation */ EXPORT_SYMBOL (ip_rt_ioctl);
David S. Miller
maintainerQuoted 1 line · On Thu, 27 Jan 2005 00:30:56 +0100 Patrick McHardy <[email protected]> wrote:
Sorry, new patch attached. I'll send the 2.4 patch later tonight.
Thanks. I'll apply this and the 2.4.x variant.
Although I have a theoretical objection to this thing. It is extending a namespace we have no such control over. The namespace is saddr/daddr/ID. But perhaps I'm being overly anal.