|
|
struct ipq { | struct ipq { |
struct ipq *next; /* linked list pointers */ | struct ipq *next; /* linked list pointers */ |
struct list_head lru_list; /* lru list member */ | struct list_head lru_list; /* lru list member */ |
|
u32 user; |
u32 saddr; | u32 saddr; |
u32 daddr; | u32 daddr; |
u16 id; | u16 id; |
|
|
/* Memory limiting on fragments. Evictor trashes the oldest | /* Memory limiting on fragments. Evictor trashes the oldest |
* fragment queue until we are back under the threshold. | * fragment queue until we are back under the threshold. |
*/ | */ |
static void __ip_evictor(int threshold) |
static void ip_evictor(void) |
{ | { |
struct ipq *qp; | struct ipq *qp; |
struct list_head *tmp; | struct list_head *tmp; |
int work; | int work; |
| |
work = atomic_read(&ip_frag_mem) - threshold; |
work = atomic_read(&ip_frag_mem) - sysctl_ipfrag_low_thresh; |
if (work <= 0) | if (work <= 0) |
return; | return; |
| |
|
|
} | } |
} | } |
| |
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. | * Oops, a fragment queue timed out. Kill it and send an ICMP reply. |
*/ | */ |
|
|
if(qp->id == qp_in->id && | if(qp->id == qp_in->id && |
qp->saddr == qp_in->saddr && | qp->saddr == qp_in->saddr && |
qp->daddr == qp_in->daddr && | qp->daddr == qp_in->daddr && |
qp->protocol == qp_in->protocol) { |
qp->protocol == qp_in->protocol && |
|
qp->user == qp_in->user) { |
atomic_inc(&qp->refcnt); | atomic_inc(&qp->refcnt); |
write_unlock(&ipfrag_lock); | write_unlock(&ipfrag_lock); |
qp_in->last_in |= COMPLETE; | qp_in->last_in |= COMPLETE; |
|
|
} | } |
| |
/* Add an entry to the 'ipq' queue for a newly received IP datagram. */ | /* 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; | struct ipq *qp; |
| |
|
|
qp->id = iph->id; | qp->id = iph->id; |
qp->saddr = iph->saddr; | qp->saddr = iph->saddr; |
qp->daddr = iph->daddr; | qp->daddr = iph->daddr; |
|
qp->user = user; |
qp->len = 0; | qp->len = 0; |
qp->meat = 0; | qp->meat = 0; |
qp->fragments = NULL; | qp->fragments = NULL; |
|
|
/* Find the correct entry in the "incomplete datagrams" queue for | /* Find the correct entry in the "incomplete datagrams" queue for |
* this IP datagram, and create new one, if nothing is found. | * 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; | __u16 id = iph->id; |
__u32 saddr = iph->saddr; | __u32 saddr = iph->saddr; |
|
|
if(qp->id == id && | if(qp->id == id && |
qp->saddr == saddr && | qp->saddr == saddr && |
qp->daddr == daddr && | qp->daddr == daddr && |
qp->protocol == protocol) { |
qp->protocol == protocol && |
|
qp->user == user) { |
atomic_inc(&qp->refcnt); | atomic_inc(&qp->refcnt); |
read_unlock(&ipfrag_lock); | read_unlock(&ipfrag_lock); |
return qp; | return qp; |
|
|
} | } |
read_unlock(&ipfrag_lock); | read_unlock(&ipfrag_lock); |
| |
return ip_frag_create(hash, iph); |
return ip_frag_create(hash, iph, user); |
} | } |
| |
/* Add new segment to existing queue. */ | /* Add new segment to existing queue. */ |
|
|
} | } |
| |
/* Process an incoming IP datagram fragment. */ | /* 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 iphdr *iph = skb->nh.iph; |
struct ipq *qp; | struct ipq *qp; |
|
|
dev = skb->dev; | dev = skb->dev; |
| |
/* Lookup (or create) queue header */ | /* Lookup (or create) queue header */ |
if ((qp = ip_find(iph)) != NULL) { |
if ((qp = ip_find(iph, user)) != NULL) { |
struct sk_buff *ret = NULL; | struct sk_buff *ret = NULL; |
| |
spin_lock(&qp->lock); | spin_lock(&qp->lock); |
|
|
add_timer(&ipfrag_secret_timer); | add_timer(&ipfrag_secret_timer); |
} | } |
| |
void ipfrag_flush(void) |
|
{ |
|
__ip_evictor(0); |
|
} |
|
|
|
EXPORT_SYMBOL(ip_defrag); | EXPORT_SYMBOL(ip_defrag); |
EXPORT_SYMBOL(ipfrag_flush); |
|