diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index f96acad1857c341757275afc29280f6239881b06..7cd34f189f1e7b953226d71db5fff645c0c7dc6a 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -1122,6 +1122,7 @@ static int __init init_ipv4_mibs(void) } int ipv4_proc_init(void); +extern void ipfrag_init(void); static int __init inet_init(void) { @@ -1224,6 +1225,9 @@ static int __init inet_init(void) printk(KERN_CRIT "inet_init: Cannot init ipv4 mibs\n"); ; ipv4_proc_init(); + + ipfrag_init(); + return 0; } diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c index 4799d1e074c2130847be905d91211d37e820ddbd..8165e29b8f644a1d99a7e6ff0ea9c20cc8b02aed 100644 --- a/net/ipv4/ip_fragment.c +++ b/net/ipv4/ip_fragment.c @@ -31,6 +31,8 @@ #include <linux/ip.h> #include <linux/icmp.h> #include <linux/netdevice.h> +#include <linux/jhash.h> +#include <linux/random.h> #include <net/sock.h> #include <net/ip.h> #include <net/icmp.h> @@ -97,6 +99,7 @@ struct ipq { /* Per-bucket lock is easy to add now. */ static struct ipq *ipq_hash[IPQ_HASHSZ]; static rwlock_t ipfrag_lock = RW_LOCK_UNLOCKED; +static u32 ipfrag_hash_rnd; static LIST_HEAD(ipq_lru_list); int ip_frag_nqueues = 0; @@ -116,21 +119,51 @@ static __inline__ void ipq_unlink(struct ipq *ipq) write_unlock(&ipfrag_lock); } -/* - * Was: ((((id) >> 1) ^ (saddr) ^ (daddr) ^ (prot)) & (IPQ_HASHSZ - 1)) - * - * I see, I see evil hand of bigendian mafia. On Intel all the packets hit - * one hash bucket with this hash function. 8) - */ -static __inline__ unsigned int ipqhashfn(u16 id, u32 saddr, u32 daddr, u8 prot) +static unsigned int ipqhashfn(u16 id, u32 saddr, u32 daddr, u8 prot) { - unsigned int h = saddr ^ daddr; - - h ^= (h>>16)^id; - h ^= (h>>8)^prot; - return h & (IPQ_HASHSZ - 1); + return jhash_3words((u32)id << 16 | prot, saddr, daddr, + ipfrag_hash_rnd) & (IPQ_HASHSZ - 1); } +static struct timer_list ipfrag_secret_timer; +static int ipfrag_secret_interval = 10 * 60 * HZ; + +static void ipfrag_secret_rebuild(unsigned long dummy) +{ + unsigned long now = jiffies; + int i; + + write_lock(&ipfrag_lock); + get_random_bytes(&ipfrag_hash_rnd, sizeof(u32)); + for (i = 0; i < IPQ_HASHSZ; i++) { + struct ipq *q; + + q = ipq_hash[i]; + while (q) { + struct ipq *next = q->next; + unsigned int hval = ipqhashfn(q->id, q->saddr, + q->daddr, q->protocol); + + if (hval != i) { + /* Unlink. */ + if (q->next) + q->next->pprev = q->pprev; + *q->pprev = q->next; + + /* Relink to new hash chain. */ + if ((q->next = ipq_hash[hval]) != NULL) + q->next->pprev = &q->next; + ipq_hash[hval] = q; + q->pprev = &ipq_hash[hval]; + } + + q = next; + } + } + write_unlock(&ipfrag_lock); + + mod_timer(&ipfrag_secret_timer, now + ipfrag_secret_interval); +} atomic_t ip_frag_mem = ATOMIC_INIT(0); /* Memory used for fragments */ @@ -631,3 +664,14 @@ struct sk_buff *ip_defrag(struct sk_buff *skb) kfree_skb(skb); return NULL; } + +void ipfrag_init(void) +{ + ipfrag_hash_rnd = (u32) ((num_physpages ^ (num_physpages>>7)) ^ + (jiffies ^ (jiffies >> 6))); + + init_timer(&ipfrag_secret_timer); + ipfrag_secret_timer.function = ipfrag_secret_rebuild; + ipfrag_secret_timer.expires = jiffies + ipfrag_secret_interval; + add_timer(&ipfrag_secret_timer); +} diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c index 3bfd4b84fb9df6648aacc4d14500a9fbdda4b8a9..ce6df9cfbe7297c90184c4867fb29760daf81844 100644 --- a/net/ipv6/reassembly.c +++ b/net/ipv6/reassembly.c @@ -38,6 +38,8 @@ #include <linux/in6.h> #include <linux/ipv6.h> #include <linux/icmpv6.h> +#include <linux/random.h> +#include <linux/jhash.h> #include <net/sock.h> #include <net/snmp.h> @@ -99,6 +101,7 @@ struct frag_queue static struct frag_queue *ip6_frag_hash[IP6Q_HASHSZ]; static rwlock_t ip6_frag_lock = RW_LOCK_UNLOCKED; +static u32 ip6_frag_hash_rnd; static LIST_HEAD(ip6_frag_lru_list); int ip6_frag_nqueues = 0; @@ -118,16 +121,73 @@ static __inline__ void fq_unlink(struct frag_queue *fq) write_unlock(&ip6_frag_lock); } -static __inline__ unsigned int ip6qhashfn(u32 id, struct in6_addr *saddr, - struct in6_addr *daddr) +static unsigned int ip6qhashfn(u32 id, struct in6_addr *saddr, + struct in6_addr *daddr) { - unsigned int h = saddr->s6_addr32[3] ^ daddr->s6_addr32[3] ^ id; + u32 a, b, c; - h ^= (h>>16); - h ^= (h>>8); - return h & (IP6Q_HASHSZ - 1); + a = saddr->s6_addr32[0]; + b = saddr->s6_addr32[1]; + c = saddr->s6_addr32[2]; + + a += JHASH_GOLDEN_RATIO; + b += JHASH_GOLDEN_RATIO; + c += ip6_frag_hash_rnd; + __jhash_mix(a, b, c); + + a += saddr->s6_addr32[3]; + b += daddr->s6_addr32[0]; + c += daddr->s6_addr32[1]; + __jhash_mix(a, b, c); + + a += daddr->s6_addr32[2]; + b += daddr->s6_addr32[3]; + c += id; + __jhash_mix(a, b, c); + + return c & (IP6Q_HASHSZ - 1); } +static struct timer_list ip6_frag_secret_timer; +static int ip6_frag_secret_interval = 10 * 60 * HZ; + +static void ip6_frag_secret_rebuild(unsigned long dummy) +{ + unsigned long now = jiffies; + int i; + + write_lock(&ip6_frag_lock); + get_random_bytes(&ip6_frag_hash_rnd, sizeof(u32)); + for (i = 0; i < IP6Q_HASHSZ; i++) { + struct frag_queue *q; + + q = ip6_frag_hash[i]; + while (q) { + struct frag_queue *next = q->next; + unsigned int hval = ip6qhashfn(q->id, + &q->saddr, + &q->daddr); + + if (hval != i) { + /* Unlink. */ + if (q->next) + q->next->pprev = q->pprev; + *q->pprev = q->next; + + /* Relink to new hash chain. */ + if ((q->next = ip6_frag_hash[hval]) != NULL) + q->next->pprev = &q->next; + ip6_frag_hash[hval] = q; + q->pprev = &ip6_frag_hash[hval]; + } + + q = next; + } + } + write_unlock(&ip6_frag_lock); + + mod_timer(&ip6_frag_secret_timer, now + ip6_frag_secret_interval); +} atomic_t ip6_frag_mem = ATOMIC_INIT(0); @@ -696,4 +756,12 @@ void __init ipv6_frag_init(void) { if (inet6_add_protocol(&frag_protocol, IPPROTO_FRAGMENT) < 0) printk(KERN_ERR "ipv6_frag_init: Could not register protocol\n"); + + ip6_frag_hash_rnd = (u32) ((num_physpages ^ (num_physpages>>7)) ^ + (jiffies ^ (jiffies >> 6))); + + init_timer(&ip6_frag_secret_timer); + ip6_frag_secret_timer.function = ip6_frag_secret_rebuild; + ip6_frag_secret_timer.expires = jiffies + ip6_frag_secret_interval; + add_timer(&ip6_frag_secret_timer); }