1*1da177e4SLinus Torvalds /* 2*1da177e4SLinus Torvalds * IPv6 fragment reassembly 3*1da177e4SLinus Torvalds * Linux INET6 implementation 4*1da177e4SLinus Torvalds * 5*1da177e4SLinus Torvalds * Authors: 6*1da177e4SLinus Torvalds * Pedro Roque <roque@di.fc.ul.pt> 7*1da177e4SLinus Torvalds * 8*1da177e4SLinus Torvalds * $Id: reassembly.c,v 1.26 2001/03/07 22:00:57 davem Exp $ 9*1da177e4SLinus Torvalds * 10*1da177e4SLinus Torvalds * Based on: net/ipv4/ip_fragment.c 11*1da177e4SLinus Torvalds * 12*1da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or 13*1da177e4SLinus Torvalds * modify it under the terms of the GNU General Public License 14*1da177e4SLinus Torvalds * as published by the Free Software Foundation; either version 15*1da177e4SLinus Torvalds * 2 of the License, or (at your option) any later version. 16*1da177e4SLinus Torvalds */ 17*1da177e4SLinus Torvalds 18*1da177e4SLinus Torvalds /* 19*1da177e4SLinus Torvalds * Fixes: 20*1da177e4SLinus Torvalds * Andi Kleen Make it work with multiple hosts. 21*1da177e4SLinus Torvalds * More RFC compliance. 22*1da177e4SLinus Torvalds * 23*1da177e4SLinus Torvalds * Horst von Brand Add missing #include <linux/string.h> 24*1da177e4SLinus Torvalds * Alexey Kuznetsov SMP races, threading, cleanup. 25*1da177e4SLinus Torvalds * Patrick McHardy LRU queue of frag heads for evictor. 26*1da177e4SLinus Torvalds * Mitsuru KANDA @USAGI Register inet6_protocol{}. 27*1da177e4SLinus Torvalds * David Stevens and 28*1da177e4SLinus Torvalds * YOSHIFUJI,H. @USAGI Always remove fragment header to 29*1da177e4SLinus Torvalds * calculate ICV correctly. 30*1da177e4SLinus Torvalds */ 31*1da177e4SLinus Torvalds #include <linux/config.h> 32*1da177e4SLinus Torvalds #include <linux/errno.h> 33*1da177e4SLinus Torvalds #include <linux/types.h> 34*1da177e4SLinus Torvalds #include <linux/string.h> 35*1da177e4SLinus Torvalds #include <linux/socket.h> 36*1da177e4SLinus Torvalds #include <linux/sockios.h> 37*1da177e4SLinus Torvalds #include <linux/jiffies.h> 38*1da177e4SLinus Torvalds #include <linux/net.h> 39*1da177e4SLinus Torvalds #include <linux/list.h> 40*1da177e4SLinus Torvalds #include <linux/netdevice.h> 41*1da177e4SLinus Torvalds #include <linux/in6.h> 42*1da177e4SLinus Torvalds #include <linux/ipv6.h> 43*1da177e4SLinus Torvalds #include <linux/icmpv6.h> 44*1da177e4SLinus Torvalds #include <linux/random.h> 45*1da177e4SLinus Torvalds #include <linux/jhash.h> 46*1da177e4SLinus Torvalds 47*1da177e4SLinus Torvalds #include <net/sock.h> 48*1da177e4SLinus Torvalds #include <net/snmp.h> 49*1da177e4SLinus Torvalds 50*1da177e4SLinus Torvalds #include <net/ipv6.h> 51*1da177e4SLinus Torvalds #include <net/protocol.h> 52*1da177e4SLinus Torvalds #include <net/transp_v6.h> 53*1da177e4SLinus Torvalds #include <net/rawv6.h> 54*1da177e4SLinus Torvalds #include <net/ndisc.h> 55*1da177e4SLinus Torvalds #include <net/addrconf.h> 56*1da177e4SLinus Torvalds 57*1da177e4SLinus Torvalds int sysctl_ip6frag_high_thresh = 256*1024; 58*1da177e4SLinus Torvalds int sysctl_ip6frag_low_thresh = 192*1024; 59*1da177e4SLinus Torvalds 60*1da177e4SLinus Torvalds int sysctl_ip6frag_time = IPV6_FRAG_TIMEOUT; 61*1da177e4SLinus Torvalds 62*1da177e4SLinus Torvalds struct ip6frag_skb_cb 63*1da177e4SLinus Torvalds { 64*1da177e4SLinus Torvalds struct inet6_skb_parm h; 65*1da177e4SLinus Torvalds int offset; 66*1da177e4SLinus Torvalds }; 67*1da177e4SLinus Torvalds 68*1da177e4SLinus Torvalds #define FRAG6_CB(skb) ((struct ip6frag_skb_cb*)((skb)->cb)) 69*1da177e4SLinus Torvalds 70*1da177e4SLinus Torvalds 71*1da177e4SLinus Torvalds /* 72*1da177e4SLinus Torvalds * Equivalent of ipv4 struct ipq 73*1da177e4SLinus Torvalds */ 74*1da177e4SLinus Torvalds 75*1da177e4SLinus Torvalds struct frag_queue 76*1da177e4SLinus Torvalds { 77*1da177e4SLinus Torvalds struct frag_queue *next; 78*1da177e4SLinus Torvalds struct list_head lru_list; /* lru list member */ 79*1da177e4SLinus Torvalds 80*1da177e4SLinus Torvalds __u32 id; /* fragment id */ 81*1da177e4SLinus Torvalds struct in6_addr saddr; 82*1da177e4SLinus Torvalds struct in6_addr daddr; 83*1da177e4SLinus Torvalds 84*1da177e4SLinus Torvalds spinlock_t lock; 85*1da177e4SLinus Torvalds atomic_t refcnt; 86*1da177e4SLinus Torvalds struct timer_list timer; /* expire timer */ 87*1da177e4SLinus Torvalds struct sk_buff *fragments; 88*1da177e4SLinus Torvalds int len; 89*1da177e4SLinus Torvalds int meat; 90*1da177e4SLinus Torvalds int iif; 91*1da177e4SLinus Torvalds struct timeval stamp; 92*1da177e4SLinus Torvalds unsigned int csum; 93*1da177e4SLinus Torvalds __u8 last_in; /* has first/last segment arrived? */ 94*1da177e4SLinus Torvalds #define COMPLETE 4 95*1da177e4SLinus Torvalds #define FIRST_IN 2 96*1da177e4SLinus Torvalds #define LAST_IN 1 97*1da177e4SLinus Torvalds __u16 nhoffset; 98*1da177e4SLinus Torvalds struct frag_queue **pprev; 99*1da177e4SLinus Torvalds }; 100*1da177e4SLinus Torvalds 101*1da177e4SLinus Torvalds /* Hash table. */ 102*1da177e4SLinus Torvalds 103*1da177e4SLinus Torvalds #define IP6Q_HASHSZ 64 104*1da177e4SLinus Torvalds 105*1da177e4SLinus Torvalds static struct frag_queue *ip6_frag_hash[IP6Q_HASHSZ]; 106*1da177e4SLinus Torvalds static DEFINE_RWLOCK(ip6_frag_lock); 107*1da177e4SLinus Torvalds static u32 ip6_frag_hash_rnd; 108*1da177e4SLinus Torvalds static LIST_HEAD(ip6_frag_lru_list); 109*1da177e4SLinus Torvalds int ip6_frag_nqueues = 0; 110*1da177e4SLinus Torvalds 111*1da177e4SLinus Torvalds static __inline__ void __fq_unlink(struct frag_queue *fq) 112*1da177e4SLinus Torvalds { 113*1da177e4SLinus Torvalds if(fq->next) 114*1da177e4SLinus Torvalds fq->next->pprev = fq->pprev; 115*1da177e4SLinus Torvalds *fq->pprev = fq->next; 116*1da177e4SLinus Torvalds list_del(&fq->lru_list); 117*1da177e4SLinus Torvalds ip6_frag_nqueues--; 118*1da177e4SLinus Torvalds } 119*1da177e4SLinus Torvalds 120*1da177e4SLinus Torvalds static __inline__ void fq_unlink(struct frag_queue *fq) 121*1da177e4SLinus Torvalds { 122*1da177e4SLinus Torvalds write_lock(&ip6_frag_lock); 123*1da177e4SLinus Torvalds __fq_unlink(fq); 124*1da177e4SLinus Torvalds write_unlock(&ip6_frag_lock); 125*1da177e4SLinus Torvalds } 126*1da177e4SLinus Torvalds 127*1da177e4SLinus Torvalds static unsigned int ip6qhashfn(u32 id, struct in6_addr *saddr, 128*1da177e4SLinus Torvalds struct in6_addr *daddr) 129*1da177e4SLinus Torvalds { 130*1da177e4SLinus Torvalds u32 a, b, c; 131*1da177e4SLinus Torvalds 132*1da177e4SLinus Torvalds a = saddr->s6_addr32[0]; 133*1da177e4SLinus Torvalds b = saddr->s6_addr32[1]; 134*1da177e4SLinus Torvalds c = saddr->s6_addr32[2]; 135*1da177e4SLinus Torvalds 136*1da177e4SLinus Torvalds a += JHASH_GOLDEN_RATIO; 137*1da177e4SLinus Torvalds b += JHASH_GOLDEN_RATIO; 138*1da177e4SLinus Torvalds c += ip6_frag_hash_rnd; 139*1da177e4SLinus Torvalds __jhash_mix(a, b, c); 140*1da177e4SLinus Torvalds 141*1da177e4SLinus Torvalds a += saddr->s6_addr32[3]; 142*1da177e4SLinus Torvalds b += daddr->s6_addr32[0]; 143*1da177e4SLinus Torvalds c += daddr->s6_addr32[1]; 144*1da177e4SLinus Torvalds __jhash_mix(a, b, c); 145*1da177e4SLinus Torvalds 146*1da177e4SLinus Torvalds a += daddr->s6_addr32[2]; 147*1da177e4SLinus Torvalds b += daddr->s6_addr32[3]; 148*1da177e4SLinus Torvalds c += id; 149*1da177e4SLinus Torvalds __jhash_mix(a, b, c); 150*1da177e4SLinus Torvalds 151*1da177e4SLinus Torvalds return c & (IP6Q_HASHSZ - 1); 152*1da177e4SLinus Torvalds } 153*1da177e4SLinus Torvalds 154*1da177e4SLinus Torvalds static struct timer_list ip6_frag_secret_timer; 155*1da177e4SLinus Torvalds int sysctl_ip6frag_secret_interval = 10 * 60 * HZ; 156*1da177e4SLinus Torvalds 157*1da177e4SLinus Torvalds static void ip6_frag_secret_rebuild(unsigned long dummy) 158*1da177e4SLinus Torvalds { 159*1da177e4SLinus Torvalds unsigned long now = jiffies; 160*1da177e4SLinus Torvalds int i; 161*1da177e4SLinus Torvalds 162*1da177e4SLinus Torvalds write_lock(&ip6_frag_lock); 163*1da177e4SLinus Torvalds get_random_bytes(&ip6_frag_hash_rnd, sizeof(u32)); 164*1da177e4SLinus Torvalds for (i = 0; i < IP6Q_HASHSZ; i++) { 165*1da177e4SLinus Torvalds struct frag_queue *q; 166*1da177e4SLinus Torvalds 167*1da177e4SLinus Torvalds q = ip6_frag_hash[i]; 168*1da177e4SLinus Torvalds while (q) { 169*1da177e4SLinus Torvalds struct frag_queue *next = q->next; 170*1da177e4SLinus Torvalds unsigned int hval = ip6qhashfn(q->id, 171*1da177e4SLinus Torvalds &q->saddr, 172*1da177e4SLinus Torvalds &q->daddr); 173*1da177e4SLinus Torvalds 174*1da177e4SLinus Torvalds if (hval != i) { 175*1da177e4SLinus Torvalds /* Unlink. */ 176*1da177e4SLinus Torvalds if (q->next) 177*1da177e4SLinus Torvalds q->next->pprev = q->pprev; 178*1da177e4SLinus Torvalds *q->pprev = q->next; 179*1da177e4SLinus Torvalds 180*1da177e4SLinus Torvalds /* Relink to new hash chain. */ 181*1da177e4SLinus Torvalds if ((q->next = ip6_frag_hash[hval]) != NULL) 182*1da177e4SLinus Torvalds q->next->pprev = &q->next; 183*1da177e4SLinus Torvalds ip6_frag_hash[hval] = q; 184*1da177e4SLinus Torvalds q->pprev = &ip6_frag_hash[hval]; 185*1da177e4SLinus Torvalds } 186*1da177e4SLinus Torvalds 187*1da177e4SLinus Torvalds q = next; 188*1da177e4SLinus Torvalds } 189*1da177e4SLinus Torvalds } 190*1da177e4SLinus Torvalds write_unlock(&ip6_frag_lock); 191*1da177e4SLinus Torvalds 192*1da177e4SLinus Torvalds mod_timer(&ip6_frag_secret_timer, now + sysctl_ip6frag_secret_interval); 193*1da177e4SLinus Torvalds } 194*1da177e4SLinus Torvalds 195*1da177e4SLinus Torvalds atomic_t ip6_frag_mem = ATOMIC_INIT(0); 196*1da177e4SLinus Torvalds 197*1da177e4SLinus Torvalds /* Memory Tracking Functions. */ 198*1da177e4SLinus Torvalds static inline void frag_kfree_skb(struct sk_buff *skb, int *work) 199*1da177e4SLinus Torvalds { 200*1da177e4SLinus Torvalds if (work) 201*1da177e4SLinus Torvalds *work -= skb->truesize; 202*1da177e4SLinus Torvalds atomic_sub(skb->truesize, &ip6_frag_mem); 203*1da177e4SLinus Torvalds kfree_skb(skb); 204*1da177e4SLinus Torvalds } 205*1da177e4SLinus Torvalds 206*1da177e4SLinus Torvalds static inline void frag_free_queue(struct frag_queue *fq, int *work) 207*1da177e4SLinus Torvalds { 208*1da177e4SLinus Torvalds if (work) 209*1da177e4SLinus Torvalds *work -= sizeof(struct frag_queue); 210*1da177e4SLinus Torvalds atomic_sub(sizeof(struct frag_queue), &ip6_frag_mem); 211*1da177e4SLinus Torvalds kfree(fq); 212*1da177e4SLinus Torvalds } 213*1da177e4SLinus Torvalds 214*1da177e4SLinus Torvalds static inline struct frag_queue *frag_alloc_queue(void) 215*1da177e4SLinus Torvalds { 216*1da177e4SLinus Torvalds struct frag_queue *fq = kmalloc(sizeof(struct frag_queue), GFP_ATOMIC); 217*1da177e4SLinus Torvalds 218*1da177e4SLinus Torvalds if(!fq) 219*1da177e4SLinus Torvalds return NULL; 220*1da177e4SLinus Torvalds atomic_add(sizeof(struct frag_queue), &ip6_frag_mem); 221*1da177e4SLinus Torvalds return fq; 222*1da177e4SLinus Torvalds } 223*1da177e4SLinus Torvalds 224*1da177e4SLinus Torvalds /* Destruction primitives. */ 225*1da177e4SLinus Torvalds 226*1da177e4SLinus Torvalds /* Complete destruction of fq. */ 227*1da177e4SLinus Torvalds static void ip6_frag_destroy(struct frag_queue *fq, int *work) 228*1da177e4SLinus Torvalds { 229*1da177e4SLinus Torvalds struct sk_buff *fp; 230*1da177e4SLinus Torvalds 231*1da177e4SLinus Torvalds BUG_TRAP(fq->last_in&COMPLETE); 232*1da177e4SLinus Torvalds BUG_TRAP(del_timer(&fq->timer) == 0); 233*1da177e4SLinus Torvalds 234*1da177e4SLinus Torvalds /* Release all fragment data. */ 235*1da177e4SLinus Torvalds fp = fq->fragments; 236*1da177e4SLinus Torvalds while (fp) { 237*1da177e4SLinus Torvalds struct sk_buff *xp = fp->next; 238*1da177e4SLinus Torvalds 239*1da177e4SLinus Torvalds frag_kfree_skb(fp, work); 240*1da177e4SLinus Torvalds fp = xp; 241*1da177e4SLinus Torvalds } 242*1da177e4SLinus Torvalds 243*1da177e4SLinus Torvalds frag_free_queue(fq, work); 244*1da177e4SLinus Torvalds } 245*1da177e4SLinus Torvalds 246*1da177e4SLinus Torvalds static __inline__ void fq_put(struct frag_queue *fq, int *work) 247*1da177e4SLinus Torvalds { 248*1da177e4SLinus Torvalds if (atomic_dec_and_test(&fq->refcnt)) 249*1da177e4SLinus Torvalds ip6_frag_destroy(fq, work); 250*1da177e4SLinus Torvalds } 251*1da177e4SLinus Torvalds 252*1da177e4SLinus Torvalds /* Kill fq entry. It is not destroyed immediately, 253*1da177e4SLinus Torvalds * because caller (and someone more) holds reference count. 254*1da177e4SLinus Torvalds */ 255*1da177e4SLinus Torvalds static __inline__ void fq_kill(struct frag_queue *fq) 256*1da177e4SLinus Torvalds { 257*1da177e4SLinus Torvalds if (del_timer(&fq->timer)) 258*1da177e4SLinus Torvalds atomic_dec(&fq->refcnt); 259*1da177e4SLinus Torvalds 260*1da177e4SLinus Torvalds if (!(fq->last_in & COMPLETE)) { 261*1da177e4SLinus Torvalds fq_unlink(fq); 262*1da177e4SLinus Torvalds atomic_dec(&fq->refcnt); 263*1da177e4SLinus Torvalds fq->last_in |= COMPLETE; 264*1da177e4SLinus Torvalds } 265*1da177e4SLinus Torvalds } 266*1da177e4SLinus Torvalds 267*1da177e4SLinus Torvalds static void ip6_evictor(void) 268*1da177e4SLinus Torvalds { 269*1da177e4SLinus Torvalds struct frag_queue *fq; 270*1da177e4SLinus Torvalds struct list_head *tmp; 271*1da177e4SLinus Torvalds int work; 272*1da177e4SLinus Torvalds 273*1da177e4SLinus Torvalds work = atomic_read(&ip6_frag_mem) - sysctl_ip6frag_low_thresh; 274*1da177e4SLinus Torvalds if (work <= 0) 275*1da177e4SLinus Torvalds return; 276*1da177e4SLinus Torvalds 277*1da177e4SLinus Torvalds while(work > 0) { 278*1da177e4SLinus Torvalds read_lock(&ip6_frag_lock); 279*1da177e4SLinus Torvalds if (list_empty(&ip6_frag_lru_list)) { 280*1da177e4SLinus Torvalds read_unlock(&ip6_frag_lock); 281*1da177e4SLinus Torvalds return; 282*1da177e4SLinus Torvalds } 283*1da177e4SLinus Torvalds tmp = ip6_frag_lru_list.next; 284*1da177e4SLinus Torvalds fq = list_entry(tmp, struct frag_queue, lru_list); 285*1da177e4SLinus Torvalds atomic_inc(&fq->refcnt); 286*1da177e4SLinus Torvalds read_unlock(&ip6_frag_lock); 287*1da177e4SLinus Torvalds 288*1da177e4SLinus Torvalds spin_lock(&fq->lock); 289*1da177e4SLinus Torvalds if (!(fq->last_in&COMPLETE)) 290*1da177e4SLinus Torvalds fq_kill(fq); 291*1da177e4SLinus Torvalds spin_unlock(&fq->lock); 292*1da177e4SLinus Torvalds 293*1da177e4SLinus Torvalds fq_put(fq, &work); 294*1da177e4SLinus Torvalds IP6_INC_STATS_BH(IPSTATS_MIB_REASMFAILS); 295*1da177e4SLinus Torvalds } 296*1da177e4SLinus Torvalds } 297*1da177e4SLinus Torvalds 298*1da177e4SLinus Torvalds static void ip6_frag_expire(unsigned long data) 299*1da177e4SLinus Torvalds { 300*1da177e4SLinus Torvalds struct frag_queue *fq = (struct frag_queue *) data; 301*1da177e4SLinus Torvalds 302*1da177e4SLinus Torvalds spin_lock(&fq->lock); 303*1da177e4SLinus Torvalds 304*1da177e4SLinus Torvalds if (fq->last_in & COMPLETE) 305*1da177e4SLinus Torvalds goto out; 306*1da177e4SLinus Torvalds 307*1da177e4SLinus Torvalds fq_kill(fq); 308*1da177e4SLinus Torvalds 309*1da177e4SLinus Torvalds IP6_INC_STATS_BH(IPSTATS_MIB_REASMTIMEOUT); 310*1da177e4SLinus Torvalds IP6_INC_STATS_BH(IPSTATS_MIB_REASMFAILS); 311*1da177e4SLinus Torvalds 312*1da177e4SLinus Torvalds /* Send error only if the first segment arrived. */ 313*1da177e4SLinus Torvalds if (fq->last_in&FIRST_IN && fq->fragments) { 314*1da177e4SLinus Torvalds struct net_device *dev = dev_get_by_index(fq->iif); 315*1da177e4SLinus Torvalds 316*1da177e4SLinus Torvalds /* 317*1da177e4SLinus Torvalds But use as source device on which LAST ARRIVED 318*1da177e4SLinus Torvalds segment was received. And do not use fq->dev 319*1da177e4SLinus Torvalds pointer directly, device might already disappeared. 320*1da177e4SLinus Torvalds */ 321*1da177e4SLinus Torvalds if (dev) { 322*1da177e4SLinus Torvalds fq->fragments->dev = dev; 323*1da177e4SLinus Torvalds icmpv6_send(fq->fragments, ICMPV6_TIME_EXCEED, ICMPV6_EXC_FRAGTIME, 0, 324*1da177e4SLinus Torvalds dev); 325*1da177e4SLinus Torvalds dev_put(dev); 326*1da177e4SLinus Torvalds } 327*1da177e4SLinus Torvalds } 328*1da177e4SLinus Torvalds out: 329*1da177e4SLinus Torvalds spin_unlock(&fq->lock); 330*1da177e4SLinus Torvalds fq_put(fq, NULL); 331*1da177e4SLinus Torvalds } 332*1da177e4SLinus Torvalds 333*1da177e4SLinus Torvalds /* Creation primitives. */ 334*1da177e4SLinus Torvalds 335*1da177e4SLinus Torvalds 336*1da177e4SLinus Torvalds static struct frag_queue *ip6_frag_intern(unsigned int hash, 337*1da177e4SLinus Torvalds struct frag_queue *fq_in) 338*1da177e4SLinus Torvalds { 339*1da177e4SLinus Torvalds struct frag_queue *fq; 340*1da177e4SLinus Torvalds 341*1da177e4SLinus Torvalds write_lock(&ip6_frag_lock); 342*1da177e4SLinus Torvalds #ifdef CONFIG_SMP 343*1da177e4SLinus Torvalds for (fq = ip6_frag_hash[hash]; fq; fq = fq->next) { 344*1da177e4SLinus Torvalds if (fq->id == fq_in->id && 345*1da177e4SLinus Torvalds ipv6_addr_equal(&fq_in->saddr, &fq->saddr) && 346*1da177e4SLinus Torvalds ipv6_addr_equal(&fq_in->daddr, &fq->daddr)) { 347*1da177e4SLinus Torvalds atomic_inc(&fq->refcnt); 348*1da177e4SLinus Torvalds write_unlock(&ip6_frag_lock); 349*1da177e4SLinus Torvalds fq_in->last_in |= COMPLETE; 350*1da177e4SLinus Torvalds fq_put(fq_in, NULL); 351*1da177e4SLinus Torvalds return fq; 352*1da177e4SLinus Torvalds } 353*1da177e4SLinus Torvalds } 354*1da177e4SLinus Torvalds #endif 355*1da177e4SLinus Torvalds fq = fq_in; 356*1da177e4SLinus Torvalds 357*1da177e4SLinus Torvalds if (!mod_timer(&fq->timer, jiffies + sysctl_ip6frag_time)) 358*1da177e4SLinus Torvalds atomic_inc(&fq->refcnt); 359*1da177e4SLinus Torvalds 360*1da177e4SLinus Torvalds atomic_inc(&fq->refcnt); 361*1da177e4SLinus Torvalds if((fq->next = ip6_frag_hash[hash]) != NULL) 362*1da177e4SLinus Torvalds fq->next->pprev = &fq->next; 363*1da177e4SLinus Torvalds ip6_frag_hash[hash] = fq; 364*1da177e4SLinus Torvalds fq->pprev = &ip6_frag_hash[hash]; 365*1da177e4SLinus Torvalds INIT_LIST_HEAD(&fq->lru_list); 366*1da177e4SLinus Torvalds list_add_tail(&fq->lru_list, &ip6_frag_lru_list); 367*1da177e4SLinus Torvalds ip6_frag_nqueues++; 368*1da177e4SLinus Torvalds write_unlock(&ip6_frag_lock); 369*1da177e4SLinus Torvalds return fq; 370*1da177e4SLinus Torvalds } 371*1da177e4SLinus Torvalds 372*1da177e4SLinus Torvalds 373*1da177e4SLinus Torvalds static struct frag_queue * 374*1da177e4SLinus Torvalds ip6_frag_create(unsigned int hash, u32 id, struct in6_addr *src, struct in6_addr *dst) 375*1da177e4SLinus Torvalds { 376*1da177e4SLinus Torvalds struct frag_queue *fq; 377*1da177e4SLinus Torvalds 378*1da177e4SLinus Torvalds if ((fq = frag_alloc_queue()) == NULL) 379*1da177e4SLinus Torvalds goto oom; 380*1da177e4SLinus Torvalds 381*1da177e4SLinus Torvalds memset(fq, 0, sizeof(struct frag_queue)); 382*1da177e4SLinus Torvalds 383*1da177e4SLinus Torvalds fq->id = id; 384*1da177e4SLinus Torvalds ipv6_addr_copy(&fq->saddr, src); 385*1da177e4SLinus Torvalds ipv6_addr_copy(&fq->daddr, dst); 386*1da177e4SLinus Torvalds 387*1da177e4SLinus Torvalds init_timer(&fq->timer); 388*1da177e4SLinus Torvalds fq->timer.function = ip6_frag_expire; 389*1da177e4SLinus Torvalds fq->timer.data = (long) fq; 390*1da177e4SLinus Torvalds spin_lock_init(&fq->lock); 391*1da177e4SLinus Torvalds atomic_set(&fq->refcnt, 1); 392*1da177e4SLinus Torvalds 393*1da177e4SLinus Torvalds return ip6_frag_intern(hash, fq); 394*1da177e4SLinus Torvalds 395*1da177e4SLinus Torvalds oom: 396*1da177e4SLinus Torvalds IP6_INC_STATS_BH(IPSTATS_MIB_REASMFAILS); 397*1da177e4SLinus Torvalds return NULL; 398*1da177e4SLinus Torvalds } 399*1da177e4SLinus Torvalds 400*1da177e4SLinus Torvalds static __inline__ struct frag_queue * 401*1da177e4SLinus Torvalds fq_find(u32 id, struct in6_addr *src, struct in6_addr *dst) 402*1da177e4SLinus Torvalds { 403*1da177e4SLinus Torvalds struct frag_queue *fq; 404*1da177e4SLinus Torvalds unsigned int hash = ip6qhashfn(id, src, dst); 405*1da177e4SLinus Torvalds 406*1da177e4SLinus Torvalds read_lock(&ip6_frag_lock); 407*1da177e4SLinus Torvalds for(fq = ip6_frag_hash[hash]; fq; fq = fq->next) { 408*1da177e4SLinus Torvalds if (fq->id == id && 409*1da177e4SLinus Torvalds ipv6_addr_equal(src, &fq->saddr) && 410*1da177e4SLinus Torvalds ipv6_addr_equal(dst, &fq->daddr)) { 411*1da177e4SLinus Torvalds atomic_inc(&fq->refcnt); 412*1da177e4SLinus Torvalds read_unlock(&ip6_frag_lock); 413*1da177e4SLinus Torvalds return fq; 414*1da177e4SLinus Torvalds } 415*1da177e4SLinus Torvalds } 416*1da177e4SLinus Torvalds read_unlock(&ip6_frag_lock); 417*1da177e4SLinus Torvalds 418*1da177e4SLinus Torvalds return ip6_frag_create(hash, id, src, dst); 419*1da177e4SLinus Torvalds } 420*1da177e4SLinus Torvalds 421*1da177e4SLinus Torvalds 422*1da177e4SLinus Torvalds static void ip6_frag_queue(struct frag_queue *fq, struct sk_buff *skb, 423*1da177e4SLinus Torvalds struct frag_hdr *fhdr, int nhoff) 424*1da177e4SLinus Torvalds { 425*1da177e4SLinus Torvalds struct sk_buff *prev, *next; 426*1da177e4SLinus Torvalds int offset, end; 427*1da177e4SLinus Torvalds 428*1da177e4SLinus Torvalds if (fq->last_in & COMPLETE) 429*1da177e4SLinus Torvalds goto err; 430*1da177e4SLinus Torvalds 431*1da177e4SLinus Torvalds offset = ntohs(fhdr->frag_off) & ~0x7; 432*1da177e4SLinus Torvalds end = offset + (ntohs(skb->nh.ipv6h->payload_len) - 433*1da177e4SLinus Torvalds ((u8 *) (fhdr + 1) - (u8 *) (skb->nh.ipv6h + 1))); 434*1da177e4SLinus Torvalds 435*1da177e4SLinus Torvalds if ((unsigned int)end > IPV6_MAXPLEN) { 436*1da177e4SLinus Torvalds IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS); 437*1da177e4SLinus Torvalds icmpv6_param_prob(skb,ICMPV6_HDR_FIELD, (u8*)&fhdr->frag_off - skb->nh.raw); 438*1da177e4SLinus Torvalds return; 439*1da177e4SLinus Torvalds } 440*1da177e4SLinus Torvalds 441*1da177e4SLinus Torvalds if (skb->ip_summed == CHECKSUM_HW) 442*1da177e4SLinus Torvalds skb->csum = csum_sub(skb->csum, 443*1da177e4SLinus Torvalds csum_partial(skb->nh.raw, (u8*)(fhdr+1)-skb->nh.raw, 0)); 444*1da177e4SLinus Torvalds 445*1da177e4SLinus Torvalds /* Is this the final fragment? */ 446*1da177e4SLinus Torvalds if (!(fhdr->frag_off & htons(IP6_MF))) { 447*1da177e4SLinus Torvalds /* If we already have some bits beyond end 448*1da177e4SLinus Torvalds * or have different end, the segment is corrupted. 449*1da177e4SLinus Torvalds */ 450*1da177e4SLinus Torvalds if (end < fq->len || 451*1da177e4SLinus Torvalds ((fq->last_in & LAST_IN) && end != fq->len)) 452*1da177e4SLinus Torvalds goto err; 453*1da177e4SLinus Torvalds fq->last_in |= LAST_IN; 454*1da177e4SLinus Torvalds fq->len = end; 455*1da177e4SLinus Torvalds } else { 456*1da177e4SLinus Torvalds /* Check if the fragment is rounded to 8 bytes. 457*1da177e4SLinus Torvalds * Required by the RFC. 458*1da177e4SLinus Torvalds */ 459*1da177e4SLinus Torvalds if (end & 0x7) { 460*1da177e4SLinus Torvalds /* RFC2460 says always send parameter problem in 461*1da177e4SLinus Torvalds * this case. -DaveM 462*1da177e4SLinus Torvalds */ 463*1da177e4SLinus Torvalds IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS); 464*1da177e4SLinus Torvalds icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, 465*1da177e4SLinus Torvalds offsetof(struct ipv6hdr, payload_len)); 466*1da177e4SLinus Torvalds return; 467*1da177e4SLinus Torvalds } 468*1da177e4SLinus Torvalds if (end > fq->len) { 469*1da177e4SLinus Torvalds /* Some bits beyond end -> corruption. */ 470*1da177e4SLinus Torvalds if (fq->last_in & LAST_IN) 471*1da177e4SLinus Torvalds goto err; 472*1da177e4SLinus Torvalds fq->len = end; 473*1da177e4SLinus Torvalds } 474*1da177e4SLinus Torvalds } 475*1da177e4SLinus Torvalds 476*1da177e4SLinus Torvalds if (end == offset) 477*1da177e4SLinus Torvalds goto err; 478*1da177e4SLinus Torvalds 479*1da177e4SLinus Torvalds /* Point into the IP datagram 'data' part. */ 480*1da177e4SLinus Torvalds if (!pskb_pull(skb, (u8 *) (fhdr + 1) - skb->data)) 481*1da177e4SLinus Torvalds goto err; 482*1da177e4SLinus Torvalds if (end-offset < skb->len) { 483*1da177e4SLinus Torvalds if (pskb_trim(skb, end - offset)) 484*1da177e4SLinus Torvalds goto err; 485*1da177e4SLinus Torvalds if (skb->ip_summed != CHECKSUM_UNNECESSARY) 486*1da177e4SLinus Torvalds skb->ip_summed = CHECKSUM_NONE; 487*1da177e4SLinus Torvalds } 488*1da177e4SLinus Torvalds 489*1da177e4SLinus Torvalds /* Find out which fragments are in front and at the back of us 490*1da177e4SLinus Torvalds * in the chain of fragments so far. We must know where to put 491*1da177e4SLinus Torvalds * this fragment, right? 492*1da177e4SLinus Torvalds */ 493*1da177e4SLinus Torvalds prev = NULL; 494*1da177e4SLinus Torvalds for(next = fq->fragments; next != NULL; next = next->next) { 495*1da177e4SLinus Torvalds if (FRAG6_CB(next)->offset >= offset) 496*1da177e4SLinus Torvalds break; /* bingo! */ 497*1da177e4SLinus Torvalds prev = next; 498*1da177e4SLinus Torvalds } 499*1da177e4SLinus Torvalds 500*1da177e4SLinus Torvalds /* We found where to put this one. Check for overlap with 501*1da177e4SLinus Torvalds * preceding fragment, and, if needed, align things so that 502*1da177e4SLinus Torvalds * any overlaps are eliminated. 503*1da177e4SLinus Torvalds */ 504*1da177e4SLinus Torvalds if (prev) { 505*1da177e4SLinus Torvalds int i = (FRAG6_CB(prev)->offset + prev->len) - offset; 506*1da177e4SLinus Torvalds 507*1da177e4SLinus Torvalds if (i > 0) { 508*1da177e4SLinus Torvalds offset += i; 509*1da177e4SLinus Torvalds if (end <= offset) 510*1da177e4SLinus Torvalds goto err; 511*1da177e4SLinus Torvalds if (!pskb_pull(skb, i)) 512*1da177e4SLinus Torvalds goto err; 513*1da177e4SLinus Torvalds if (skb->ip_summed != CHECKSUM_UNNECESSARY) 514*1da177e4SLinus Torvalds skb->ip_summed = CHECKSUM_NONE; 515*1da177e4SLinus Torvalds } 516*1da177e4SLinus Torvalds } 517*1da177e4SLinus Torvalds 518*1da177e4SLinus Torvalds /* Look for overlap with succeeding segments. 519*1da177e4SLinus Torvalds * If we can merge fragments, do it. 520*1da177e4SLinus Torvalds */ 521*1da177e4SLinus Torvalds while (next && FRAG6_CB(next)->offset < end) { 522*1da177e4SLinus Torvalds int i = end - FRAG6_CB(next)->offset; /* overlap is 'i' bytes */ 523*1da177e4SLinus Torvalds 524*1da177e4SLinus Torvalds if (i < next->len) { 525*1da177e4SLinus Torvalds /* Eat head of the next overlapped fragment 526*1da177e4SLinus Torvalds * and leave the loop. The next ones cannot overlap. 527*1da177e4SLinus Torvalds */ 528*1da177e4SLinus Torvalds if (!pskb_pull(next, i)) 529*1da177e4SLinus Torvalds goto err; 530*1da177e4SLinus Torvalds FRAG6_CB(next)->offset += i; /* next fragment */ 531*1da177e4SLinus Torvalds fq->meat -= i; 532*1da177e4SLinus Torvalds if (next->ip_summed != CHECKSUM_UNNECESSARY) 533*1da177e4SLinus Torvalds next->ip_summed = CHECKSUM_NONE; 534*1da177e4SLinus Torvalds break; 535*1da177e4SLinus Torvalds } else { 536*1da177e4SLinus Torvalds struct sk_buff *free_it = next; 537*1da177e4SLinus Torvalds 538*1da177e4SLinus Torvalds /* Old fragment is completely overridden with 539*1da177e4SLinus Torvalds * new one drop it. 540*1da177e4SLinus Torvalds */ 541*1da177e4SLinus Torvalds next = next->next; 542*1da177e4SLinus Torvalds 543*1da177e4SLinus Torvalds if (prev) 544*1da177e4SLinus Torvalds prev->next = next; 545*1da177e4SLinus Torvalds else 546*1da177e4SLinus Torvalds fq->fragments = next; 547*1da177e4SLinus Torvalds 548*1da177e4SLinus Torvalds fq->meat -= free_it->len; 549*1da177e4SLinus Torvalds frag_kfree_skb(free_it, NULL); 550*1da177e4SLinus Torvalds } 551*1da177e4SLinus Torvalds } 552*1da177e4SLinus Torvalds 553*1da177e4SLinus Torvalds FRAG6_CB(skb)->offset = offset; 554*1da177e4SLinus Torvalds 555*1da177e4SLinus Torvalds /* Insert this fragment in the chain of fragments. */ 556*1da177e4SLinus Torvalds skb->next = next; 557*1da177e4SLinus Torvalds if (prev) 558*1da177e4SLinus Torvalds prev->next = skb; 559*1da177e4SLinus Torvalds else 560*1da177e4SLinus Torvalds fq->fragments = skb; 561*1da177e4SLinus Torvalds 562*1da177e4SLinus Torvalds if (skb->dev) 563*1da177e4SLinus Torvalds fq->iif = skb->dev->ifindex; 564*1da177e4SLinus Torvalds skb->dev = NULL; 565*1da177e4SLinus Torvalds fq->stamp = skb->stamp; 566*1da177e4SLinus Torvalds fq->meat += skb->len; 567*1da177e4SLinus Torvalds atomic_add(skb->truesize, &ip6_frag_mem); 568*1da177e4SLinus Torvalds 569*1da177e4SLinus Torvalds /* The first fragment. 570*1da177e4SLinus Torvalds * nhoffset is obtained from the first fragment, of course. 571*1da177e4SLinus Torvalds */ 572*1da177e4SLinus Torvalds if (offset == 0) { 573*1da177e4SLinus Torvalds fq->nhoffset = nhoff; 574*1da177e4SLinus Torvalds fq->last_in |= FIRST_IN; 575*1da177e4SLinus Torvalds } 576*1da177e4SLinus Torvalds write_lock(&ip6_frag_lock); 577*1da177e4SLinus Torvalds list_move_tail(&fq->lru_list, &ip6_frag_lru_list); 578*1da177e4SLinus Torvalds write_unlock(&ip6_frag_lock); 579*1da177e4SLinus Torvalds return; 580*1da177e4SLinus Torvalds 581*1da177e4SLinus Torvalds err: 582*1da177e4SLinus Torvalds IP6_INC_STATS(IPSTATS_MIB_REASMFAILS); 583*1da177e4SLinus Torvalds kfree_skb(skb); 584*1da177e4SLinus Torvalds } 585*1da177e4SLinus Torvalds 586*1da177e4SLinus Torvalds /* 587*1da177e4SLinus Torvalds * Check if this packet is complete. 588*1da177e4SLinus Torvalds * Returns NULL on failure by any reason, and pointer 589*1da177e4SLinus Torvalds * to current nexthdr field in reassembled frame. 590*1da177e4SLinus Torvalds * 591*1da177e4SLinus Torvalds * It is called with locked fq, and caller must check that 592*1da177e4SLinus Torvalds * queue is eligible for reassembly i.e. it is not COMPLETE, 593*1da177e4SLinus Torvalds * the last and the first frames arrived and all the bits are here. 594*1da177e4SLinus Torvalds */ 595*1da177e4SLinus Torvalds static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff **skb_in, 596*1da177e4SLinus Torvalds unsigned int *nhoffp, 597*1da177e4SLinus Torvalds struct net_device *dev) 598*1da177e4SLinus Torvalds { 599*1da177e4SLinus Torvalds struct sk_buff *fp, *head = fq->fragments; 600*1da177e4SLinus Torvalds int payload_len; 601*1da177e4SLinus Torvalds unsigned int nhoff; 602*1da177e4SLinus Torvalds 603*1da177e4SLinus Torvalds fq_kill(fq); 604*1da177e4SLinus Torvalds 605*1da177e4SLinus Torvalds BUG_TRAP(head != NULL); 606*1da177e4SLinus Torvalds BUG_TRAP(FRAG6_CB(head)->offset == 0); 607*1da177e4SLinus Torvalds 608*1da177e4SLinus Torvalds /* Unfragmented part is taken from the first segment. */ 609*1da177e4SLinus Torvalds payload_len = (head->data - head->nh.raw) - sizeof(struct ipv6hdr) + fq->len - sizeof(struct frag_hdr); 610*1da177e4SLinus Torvalds if (payload_len > IPV6_MAXPLEN) 611*1da177e4SLinus Torvalds goto out_oversize; 612*1da177e4SLinus Torvalds 613*1da177e4SLinus Torvalds /* Head of list must not be cloned. */ 614*1da177e4SLinus Torvalds if (skb_cloned(head) && pskb_expand_head(head, 0, 0, GFP_ATOMIC)) 615*1da177e4SLinus Torvalds goto out_oom; 616*1da177e4SLinus Torvalds 617*1da177e4SLinus Torvalds /* If the first fragment is fragmented itself, we split 618*1da177e4SLinus Torvalds * it to two chunks: the first with data and paged part 619*1da177e4SLinus Torvalds * and the second, holding only fragments. */ 620*1da177e4SLinus Torvalds if (skb_shinfo(head)->frag_list) { 621*1da177e4SLinus Torvalds struct sk_buff *clone; 622*1da177e4SLinus Torvalds int i, plen = 0; 623*1da177e4SLinus Torvalds 624*1da177e4SLinus Torvalds if ((clone = alloc_skb(0, GFP_ATOMIC)) == NULL) 625*1da177e4SLinus Torvalds goto out_oom; 626*1da177e4SLinus Torvalds clone->next = head->next; 627*1da177e4SLinus Torvalds head->next = clone; 628*1da177e4SLinus Torvalds skb_shinfo(clone)->frag_list = skb_shinfo(head)->frag_list; 629*1da177e4SLinus Torvalds skb_shinfo(head)->frag_list = NULL; 630*1da177e4SLinus Torvalds for (i=0; i<skb_shinfo(head)->nr_frags; i++) 631*1da177e4SLinus Torvalds plen += skb_shinfo(head)->frags[i].size; 632*1da177e4SLinus Torvalds clone->len = clone->data_len = head->data_len - plen; 633*1da177e4SLinus Torvalds head->data_len -= clone->len; 634*1da177e4SLinus Torvalds head->len -= clone->len; 635*1da177e4SLinus Torvalds clone->csum = 0; 636*1da177e4SLinus Torvalds clone->ip_summed = head->ip_summed; 637*1da177e4SLinus Torvalds atomic_add(clone->truesize, &ip6_frag_mem); 638*1da177e4SLinus Torvalds } 639*1da177e4SLinus Torvalds 640*1da177e4SLinus Torvalds /* We have to remove fragment header from datagram and to relocate 641*1da177e4SLinus Torvalds * header in order to calculate ICV correctly. */ 642*1da177e4SLinus Torvalds nhoff = fq->nhoffset; 643*1da177e4SLinus Torvalds head->nh.raw[nhoff] = head->h.raw[0]; 644*1da177e4SLinus Torvalds memmove(head->head + sizeof(struct frag_hdr), head->head, 645*1da177e4SLinus Torvalds (head->data - head->head) - sizeof(struct frag_hdr)); 646*1da177e4SLinus Torvalds head->mac.raw += sizeof(struct frag_hdr); 647*1da177e4SLinus Torvalds head->nh.raw += sizeof(struct frag_hdr); 648*1da177e4SLinus Torvalds 649*1da177e4SLinus Torvalds skb_shinfo(head)->frag_list = head->next; 650*1da177e4SLinus Torvalds head->h.raw = head->data; 651*1da177e4SLinus Torvalds skb_push(head, head->data - head->nh.raw); 652*1da177e4SLinus Torvalds atomic_sub(head->truesize, &ip6_frag_mem); 653*1da177e4SLinus Torvalds 654*1da177e4SLinus Torvalds for (fp=head->next; fp; fp = fp->next) { 655*1da177e4SLinus Torvalds head->data_len += fp->len; 656*1da177e4SLinus Torvalds head->len += fp->len; 657*1da177e4SLinus Torvalds if (head->ip_summed != fp->ip_summed) 658*1da177e4SLinus Torvalds head->ip_summed = CHECKSUM_NONE; 659*1da177e4SLinus Torvalds else if (head->ip_summed == CHECKSUM_HW) 660*1da177e4SLinus Torvalds head->csum = csum_add(head->csum, fp->csum); 661*1da177e4SLinus Torvalds head->truesize += fp->truesize; 662*1da177e4SLinus Torvalds atomic_sub(fp->truesize, &ip6_frag_mem); 663*1da177e4SLinus Torvalds } 664*1da177e4SLinus Torvalds 665*1da177e4SLinus Torvalds head->next = NULL; 666*1da177e4SLinus Torvalds head->dev = dev; 667*1da177e4SLinus Torvalds head->stamp = fq->stamp; 668*1da177e4SLinus Torvalds head->nh.ipv6h->payload_len = htons(payload_len); 669*1da177e4SLinus Torvalds 670*1da177e4SLinus Torvalds *skb_in = head; 671*1da177e4SLinus Torvalds 672*1da177e4SLinus Torvalds /* Yes, and fold redundant checksum back. 8) */ 673*1da177e4SLinus Torvalds if (head->ip_summed == CHECKSUM_HW) 674*1da177e4SLinus Torvalds head->csum = csum_partial(head->nh.raw, head->h.raw-head->nh.raw, head->csum); 675*1da177e4SLinus Torvalds 676*1da177e4SLinus Torvalds IP6_INC_STATS_BH(IPSTATS_MIB_REASMOKS); 677*1da177e4SLinus Torvalds fq->fragments = NULL; 678*1da177e4SLinus Torvalds *nhoffp = nhoff; 679*1da177e4SLinus Torvalds return 1; 680*1da177e4SLinus Torvalds 681*1da177e4SLinus Torvalds out_oversize: 682*1da177e4SLinus Torvalds if (net_ratelimit()) 683*1da177e4SLinus Torvalds printk(KERN_DEBUG "ip6_frag_reasm: payload len = %d\n", payload_len); 684*1da177e4SLinus Torvalds goto out_fail; 685*1da177e4SLinus Torvalds out_oom: 686*1da177e4SLinus Torvalds if (net_ratelimit()) 687*1da177e4SLinus Torvalds printk(KERN_DEBUG "ip6_frag_reasm: no memory for reassembly\n"); 688*1da177e4SLinus Torvalds out_fail: 689*1da177e4SLinus Torvalds IP6_INC_STATS_BH(IPSTATS_MIB_REASMFAILS); 690*1da177e4SLinus Torvalds return -1; 691*1da177e4SLinus Torvalds } 692*1da177e4SLinus Torvalds 693*1da177e4SLinus Torvalds static int ipv6_frag_rcv(struct sk_buff **skbp, unsigned int *nhoffp) 694*1da177e4SLinus Torvalds { 695*1da177e4SLinus Torvalds struct sk_buff *skb = *skbp; 696*1da177e4SLinus Torvalds struct net_device *dev = skb->dev; 697*1da177e4SLinus Torvalds struct frag_hdr *fhdr; 698*1da177e4SLinus Torvalds struct frag_queue *fq; 699*1da177e4SLinus Torvalds struct ipv6hdr *hdr; 700*1da177e4SLinus Torvalds 701*1da177e4SLinus Torvalds hdr = skb->nh.ipv6h; 702*1da177e4SLinus Torvalds 703*1da177e4SLinus Torvalds IP6_INC_STATS_BH(IPSTATS_MIB_REASMREQDS); 704*1da177e4SLinus Torvalds 705*1da177e4SLinus Torvalds /* Jumbo payload inhibits frag. header */ 706*1da177e4SLinus Torvalds if (hdr->payload_len==0) { 707*1da177e4SLinus Torvalds IP6_INC_STATS(IPSTATS_MIB_INHDRERRORS); 708*1da177e4SLinus Torvalds icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, skb->h.raw-skb->nh.raw); 709*1da177e4SLinus Torvalds return -1; 710*1da177e4SLinus Torvalds } 711*1da177e4SLinus Torvalds if (!pskb_may_pull(skb, (skb->h.raw-skb->data)+sizeof(struct frag_hdr))) { 712*1da177e4SLinus Torvalds IP6_INC_STATS(IPSTATS_MIB_INHDRERRORS); 713*1da177e4SLinus Torvalds icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, skb->h.raw-skb->nh.raw); 714*1da177e4SLinus Torvalds return -1; 715*1da177e4SLinus Torvalds } 716*1da177e4SLinus Torvalds 717*1da177e4SLinus Torvalds hdr = skb->nh.ipv6h; 718*1da177e4SLinus Torvalds fhdr = (struct frag_hdr *)skb->h.raw; 719*1da177e4SLinus Torvalds 720*1da177e4SLinus Torvalds if (!(fhdr->frag_off & htons(0xFFF9))) { 721*1da177e4SLinus Torvalds /* It is not a fragmented frame */ 722*1da177e4SLinus Torvalds skb->h.raw += sizeof(struct frag_hdr); 723*1da177e4SLinus Torvalds IP6_INC_STATS_BH(IPSTATS_MIB_REASMOKS); 724*1da177e4SLinus Torvalds 725*1da177e4SLinus Torvalds *nhoffp = (u8*)fhdr - skb->nh.raw; 726*1da177e4SLinus Torvalds return 1; 727*1da177e4SLinus Torvalds } 728*1da177e4SLinus Torvalds 729*1da177e4SLinus Torvalds if (atomic_read(&ip6_frag_mem) > sysctl_ip6frag_high_thresh) 730*1da177e4SLinus Torvalds ip6_evictor(); 731*1da177e4SLinus Torvalds 732*1da177e4SLinus Torvalds if ((fq = fq_find(fhdr->identification, &hdr->saddr, &hdr->daddr)) != NULL) { 733*1da177e4SLinus Torvalds int ret = -1; 734*1da177e4SLinus Torvalds 735*1da177e4SLinus Torvalds spin_lock(&fq->lock); 736*1da177e4SLinus Torvalds 737*1da177e4SLinus Torvalds ip6_frag_queue(fq, skb, fhdr, *nhoffp); 738*1da177e4SLinus Torvalds 739*1da177e4SLinus Torvalds if (fq->last_in == (FIRST_IN|LAST_IN) && 740*1da177e4SLinus Torvalds fq->meat == fq->len) 741*1da177e4SLinus Torvalds ret = ip6_frag_reasm(fq, skbp, nhoffp, dev); 742*1da177e4SLinus Torvalds 743*1da177e4SLinus Torvalds spin_unlock(&fq->lock); 744*1da177e4SLinus Torvalds fq_put(fq, NULL); 745*1da177e4SLinus Torvalds return ret; 746*1da177e4SLinus Torvalds } 747*1da177e4SLinus Torvalds 748*1da177e4SLinus Torvalds IP6_INC_STATS_BH(IPSTATS_MIB_REASMFAILS); 749*1da177e4SLinus Torvalds kfree_skb(skb); 750*1da177e4SLinus Torvalds return -1; 751*1da177e4SLinus Torvalds } 752*1da177e4SLinus Torvalds 753*1da177e4SLinus Torvalds static struct inet6_protocol frag_protocol = 754*1da177e4SLinus Torvalds { 755*1da177e4SLinus Torvalds .handler = ipv6_frag_rcv, 756*1da177e4SLinus Torvalds .flags = INET6_PROTO_NOPOLICY, 757*1da177e4SLinus Torvalds }; 758*1da177e4SLinus Torvalds 759*1da177e4SLinus Torvalds void __init ipv6_frag_init(void) 760*1da177e4SLinus Torvalds { 761*1da177e4SLinus Torvalds if (inet6_add_protocol(&frag_protocol, IPPROTO_FRAGMENT) < 0) 762*1da177e4SLinus Torvalds printk(KERN_ERR "ipv6_frag_init: Could not register protocol\n"); 763*1da177e4SLinus Torvalds 764*1da177e4SLinus Torvalds ip6_frag_hash_rnd = (u32) ((num_physpages ^ (num_physpages>>7)) ^ 765*1da177e4SLinus Torvalds (jiffies ^ (jiffies >> 6))); 766*1da177e4SLinus Torvalds 767*1da177e4SLinus Torvalds init_timer(&ip6_frag_secret_timer); 768*1da177e4SLinus Torvalds ip6_frag_secret_timer.function = ip6_frag_secret_rebuild; 769*1da177e4SLinus Torvalds ip6_frag_secret_timer.expires = jiffies + sysctl_ip6frag_secret_interval; 770*1da177e4SLinus Torvalds add_timer(&ip6_frag_secret_timer); 771*1da177e4SLinus Torvalds } 772