2005-04-17 02:20:36 +04:00
/*
* IPv6 fragment reassembly
2007-02-09 17:24:49 +03:00
* Linux INET6 implementation
2005-04-17 02:20:36 +04:00
*
* Authors :
2007-02-09 17:24:49 +03:00
* Pedro Roque < roque @ di . fc . ul . pt >
2005-04-17 02:20:36 +04:00
*
* Based on : net / ipv4 / ip_fragment . c
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version
* 2 of the License , or ( at your option ) any later version .
*/
2007-02-09 17:24:49 +03:00
/*
* Fixes :
2005-04-17 02:20:36 +04:00
* Andi Kleen Make it work with multiple hosts .
* More RFC compliance .
*
* Horst von Brand Add missing # include < linux / string . h >
* Alexey Kuznetsov SMP races , threading , cleanup .
* Patrick McHardy LRU queue of frag heads for evictor .
* Mitsuru KANDA @ USAGI Register inet6_protocol { } .
* David Stevens and
* YOSHIFUJI , H . @ USAGI Always remove fragment header to
* calculate ICV correctly .
*/
# include <linux/errno.h>
# include <linux/types.h>
# include <linux/string.h>
# include <linux/socket.h>
# include <linux/sockios.h>
# include <linux/jiffies.h>
# include <linux/net.h>
# include <linux/list.h>
# include <linux/netdevice.h>
# include <linux/in6.h>
# include <linux/ipv6.h>
# include <linux/icmpv6.h>
# include <linux/random.h>
# include <linux/jhash.h>
2007-10-15 12:28:47 +04:00
# include <linux/skbuff.h>
2005-04-17 02:20:36 +04:00
# include <net/sock.h>
# include <net/snmp.h>
# include <net/ipv6.h>
2006-11-04 14:11:37 +03:00
# include <net/ip6_route.h>
2005-04-17 02:20:36 +04:00
# include <net/protocol.h>
# include <net/transp_v6.h>
# include <net/rawv6.h>
# include <net/ndisc.h>
# include <net/addrconf.h>
2007-10-15 13:24:19 +04:00
# include <net/inet_frag.h>
2005-04-17 02:20:36 +04:00
struct ip6frag_skb_cb
{
struct inet6_skb_parm h ;
int offset ;
} ;
# define FRAG6_CB(skb) ((struct ip6frag_skb_cb*)((skb)->cb))
/*
* Equivalent of ipv4 struct ipq
*/
struct frag_queue
{
2007-10-15 13:24:19 +04:00
struct inet_frag_queue q ;
2005-04-17 02:20:36 +04:00
2006-11-15 07:56:00 +03:00
__be32 id ; /* fragment id */
2005-04-17 02:20:36 +04:00
struct in6_addr saddr ;
struct in6_addr daddr ;
int iif ;
unsigned int csum ;
__u16 nhoffset ;
} ;
2007-10-15 13:31:52 +04:00
static struct inet_frags ip6_frags ;
2005-04-17 02:20:36 +04:00
2008-01-22 17:06:23 +03:00
int ip6_frag_nqueues ( struct net * net )
2007-10-15 13:31:52 +04:00
{
2008-01-22 17:06:23 +03:00
return net - > ipv6 . frags . nqueues ;
2007-10-15 13:31:52 +04:00
}
2005-04-17 02:20:36 +04:00
2008-01-22 17:07:25 +03:00
int ip6_frag_mem ( struct net * net )
2007-10-15 13:31:52 +04:00
{
2008-01-22 17:07:25 +03:00
return atomic_read ( & net - > ipv6 . frags . mem ) ;
2007-10-15 13:31:52 +04:00
}
2005-04-17 02:20:36 +04:00
2007-10-15 12:28:47 +04:00
static int ip6_frag_reasm ( struct frag_queue * fq , struct sk_buff * prev ,
struct net_device * dev ) ;
2006-04-11 03:05:34 +04:00
/*
* callers should be careful not to use the hash value outside the ipfrag_lock
* as doing so could race with ipfrag_hash_rnd being recalculated .
*/
ipv6: almost identical frag hashing funcs combined
$ diff-funcs ip6qhashfn reassembly.c netfilter/nf_conntrack_reasm.c
--- reassembly.c:ip6qhashfn()
+++ netfilter/nf_conntrack_reasm.c:ip6qhashfn()
@@ -1,5 +1,5 @@
-static unsigned int ip6qhashfn(__be32 id, struct in6_addr *saddr,
- struct in6_addr *daddr)
+static unsigned int ip6qhashfn(__be32 id, const struct in6_addr *saddr,
+ const struct in6_addr *daddr)
{
u32 a, b, c;
@@ -9,7 +9,7 @@
a += JHASH_GOLDEN_RATIO;
b += JHASH_GOLDEN_RATIO;
- c += ip6_frags.rnd;
+ c += nf_frags.rnd;
__jhash_mix(a, b, c);
a += (__force u32)saddr->s6_addr32[3];
And codiff xx.o.old xx.o.new:
net/ipv6/netfilter/nf_conntrack_reasm.c:
ip6qhashfn | -512
nf_hashfn | +6
nf_ct_frag6_gather | +36
3 functions changed, 42 bytes added, 512 bytes removed, diff: -470
net/ipv6/reassembly.c:
ip6qhashfn | -512
ip6_hashfn | +7
ipv6_frag_rcv | +89
3 functions changed, 96 bytes added, 512 bytes removed, diff: -416
net/ipv6/reassembly.c:
inet6_hash_frag | +510
1 function changed, 510 bytes added, diff: +510
Total: -376
Compile tested.
Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@helsinki.fi>
Acked-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-10-01 13:48:31 +04:00
unsigned int inet6_hash_frag ( __be32 id , const struct in6_addr * saddr ,
const struct in6_addr * daddr , u32 rnd )
2005-04-17 02:20:36 +04:00
{
u32 a , b , c ;
2006-11-15 07:56:00 +03:00
a = ( __force u32 ) saddr - > s6_addr32 [ 0 ] ;
b = ( __force u32 ) saddr - > s6_addr32 [ 1 ] ;
c = ( __force u32 ) saddr - > s6_addr32 [ 2 ] ;
2005-04-17 02:20:36 +04:00
a + = JHASH_GOLDEN_RATIO ;
b + = JHASH_GOLDEN_RATIO ;
ipv6: almost identical frag hashing funcs combined
$ diff-funcs ip6qhashfn reassembly.c netfilter/nf_conntrack_reasm.c
--- reassembly.c:ip6qhashfn()
+++ netfilter/nf_conntrack_reasm.c:ip6qhashfn()
@@ -1,5 +1,5 @@
-static unsigned int ip6qhashfn(__be32 id, struct in6_addr *saddr,
- struct in6_addr *daddr)
+static unsigned int ip6qhashfn(__be32 id, const struct in6_addr *saddr,
+ const struct in6_addr *daddr)
{
u32 a, b, c;
@@ -9,7 +9,7 @@
a += JHASH_GOLDEN_RATIO;
b += JHASH_GOLDEN_RATIO;
- c += ip6_frags.rnd;
+ c += nf_frags.rnd;
__jhash_mix(a, b, c);
a += (__force u32)saddr->s6_addr32[3];
And codiff xx.o.old xx.o.new:
net/ipv6/netfilter/nf_conntrack_reasm.c:
ip6qhashfn | -512
nf_hashfn | +6
nf_ct_frag6_gather | +36
3 functions changed, 42 bytes added, 512 bytes removed, diff: -470
net/ipv6/reassembly.c:
ip6qhashfn | -512
ip6_hashfn | +7
ipv6_frag_rcv | +89
3 functions changed, 96 bytes added, 512 bytes removed, diff: -416
net/ipv6/reassembly.c:
inet6_hash_frag | +510
1 function changed, 510 bytes added, diff: +510
Total: -376
Compile tested.
Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@helsinki.fi>
Acked-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-10-01 13:48:31 +04:00
c + = rnd ;
2005-04-17 02:20:36 +04:00
__jhash_mix ( a , b , c ) ;
2006-11-15 07:56:00 +03:00
a + = ( __force u32 ) saddr - > s6_addr32 [ 3 ] ;
b + = ( __force u32 ) daddr - > s6_addr32 [ 0 ] ;
c + = ( __force u32 ) daddr - > s6_addr32 [ 1 ] ;
2005-04-17 02:20:36 +04:00
__jhash_mix ( a , b , c ) ;
2006-11-15 07:56:00 +03:00
a + = ( __force u32 ) daddr - > s6_addr32 [ 2 ] ;
b + = ( __force u32 ) daddr - > s6_addr32 [ 3 ] ;
c + = ( __force u32 ) id ;
2005-04-17 02:20:36 +04:00
__jhash_mix ( a , b , c ) ;
2007-10-15 13:31:52 +04:00
return c & ( INETFRAGS_HASHSZ - 1 ) ;
2005-04-17 02:20:36 +04:00
}
ipv6: almost identical frag hashing funcs combined
$ diff-funcs ip6qhashfn reassembly.c netfilter/nf_conntrack_reasm.c
--- reassembly.c:ip6qhashfn()
+++ netfilter/nf_conntrack_reasm.c:ip6qhashfn()
@@ -1,5 +1,5 @@
-static unsigned int ip6qhashfn(__be32 id, struct in6_addr *saddr,
- struct in6_addr *daddr)
+static unsigned int ip6qhashfn(__be32 id, const struct in6_addr *saddr,
+ const struct in6_addr *daddr)
{
u32 a, b, c;
@@ -9,7 +9,7 @@
a += JHASH_GOLDEN_RATIO;
b += JHASH_GOLDEN_RATIO;
- c += ip6_frags.rnd;
+ c += nf_frags.rnd;
__jhash_mix(a, b, c);
a += (__force u32)saddr->s6_addr32[3];
And codiff xx.o.old xx.o.new:
net/ipv6/netfilter/nf_conntrack_reasm.c:
ip6qhashfn | -512
nf_hashfn | +6
nf_ct_frag6_gather | +36
3 functions changed, 42 bytes added, 512 bytes removed, diff: -470
net/ipv6/reassembly.c:
ip6qhashfn | -512
ip6_hashfn | +7
ipv6_frag_rcv | +89
3 functions changed, 96 bytes added, 512 bytes removed, diff: -416
net/ipv6/reassembly.c:
inet6_hash_frag | +510
1 function changed, 510 bytes added, diff: +510
Total: -376
Compile tested.
Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@helsinki.fi>
Acked-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-10-01 13:48:31 +04:00
EXPORT_SYMBOL_GPL ( inet6_hash_frag ) ;
2005-04-17 02:20:36 +04:00
2007-10-15 13:38:08 +04:00
static unsigned int ip6_hashfn ( struct inet_frag_queue * q )
2005-04-17 02:20:36 +04:00
{
2007-10-15 13:38:08 +04:00
struct frag_queue * fq ;
2005-04-17 02:20:36 +04:00
2007-10-15 13:38:08 +04:00
fq = container_of ( q , struct frag_queue , q ) ;
ipv6: almost identical frag hashing funcs combined
$ diff-funcs ip6qhashfn reassembly.c netfilter/nf_conntrack_reasm.c
--- reassembly.c:ip6qhashfn()
+++ netfilter/nf_conntrack_reasm.c:ip6qhashfn()
@@ -1,5 +1,5 @@
-static unsigned int ip6qhashfn(__be32 id, struct in6_addr *saddr,
- struct in6_addr *daddr)
+static unsigned int ip6qhashfn(__be32 id, const struct in6_addr *saddr,
+ const struct in6_addr *daddr)
{
u32 a, b, c;
@@ -9,7 +9,7 @@
a += JHASH_GOLDEN_RATIO;
b += JHASH_GOLDEN_RATIO;
- c += ip6_frags.rnd;
+ c += nf_frags.rnd;
__jhash_mix(a, b, c);
a += (__force u32)saddr->s6_addr32[3];
And codiff xx.o.old xx.o.new:
net/ipv6/netfilter/nf_conntrack_reasm.c:
ip6qhashfn | -512
nf_hashfn | +6
nf_ct_frag6_gather | +36
3 functions changed, 42 bytes added, 512 bytes removed, diff: -470
net/ipv6/reassembly.c:
ip6qhashfn | -512
ip6_hashfn | +7
ipv6_frag_rcv | +89
3 functions changed, 96 bytes added, 512 bytes removed, diff: -416
net/ipv6/reassembly.c:
inet6_hash_frag | +510
1 function changed, 510 bytes added, diff: +510
Total: -376
Compile tested.
Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@helsinki.fi>
Acked-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-10-01 13:48:31 +04:00
return inet6_hash_frag ( fq - > id , & fq - > saddr , & fq - > daddr , ip6_frags . rnd ) ;
2005-04-17 02:20:36 +04:00
}
2007-10-18 06:47:21 +04:00
int ip6_frag_match ( struct inet_frag_queue * q , void * a )
{
struct frag_queue * fq ;
struct ip6_create_arg * arg = a ;
fq = container_of ( q , struct frag_queue , q ) ;
return ( fq - > id = = arg - > id & &
ipv6_addr_equal ( & fq - > saddr , arg - > src ) & &
ipv6_addr_equal ( & fq - > daddr , arg - > dst ) ) ;
}
EXPORT_SYMBOL ( ip6_frag_match ) ;
2005-04-17 02:20:36 +04:00
/* Memory Tracking Functions. */
2008-01-22 17:07:25 +03:00
static inline void frag_kfree_skb ( struct netns_frags * nf ,
struct sk_buff * skb , int * work )
2005-04-17 02:20:36 +04:00
{
if ( work )
* work - = skb - > truesize ;
2008-01-22 17:07:25 +03:00
atomic_sub ( skb - > truesize , & nf - > mem ) ;
2005-04-17 02:20:36 +04:00
kfree_skb ( skb ) ;
}
2007-10-18 06:46:47 +04:00
void ip6_frag_init ( struct inet_frag_queue * q , void * a )
2005-04-17 02:20:36 +04:00
{
2007-10-18 06:46:47 +04:00
struct frag_queue * fq = container_of ( q , struct frag_queue , q ) ;
struct ip6_create_arg * arg = a ;
fq - > id = arg - > id ;
ipv6_addr_copy ( & fq - > saddr , arg - > src ) ;
ipv6_addr_copy ( & fq - > daddr , arg - > dst ) ;
2005-04-17 02:20:36 +04:00
}
2007-10-18 06:46:47 +04:00
EXPORT_SYMBOL ( ip6_frag_init ) ;
2005-04-17 02:20:36 +04:00
/* Destruction primitives. */
2007-10-15 13:41:09 +04:00
static __inline__ void fq_put ( struct frag_queue * fq )
2005-04-17 02:20:36 +04:00
{
2007-10-15 13:41:56 +04:00
inet_frag_put ( & fq - > q , & ip6_frags ) ;
2005-04-17 02:20:36 +04:00
}
/* Kill fq entry. It is not destroyed immediately,
* because caller ( and someone more ) holds reference count .
*/
static __inline__ void fq_kill ( struct frag_queue * fq )
{
2007-10-15 13:37:18 +04:00
inet_frag_kill ( & fq - > q , & ip6_frags ) ;
2005-04-17 02:20:36 +04:00
}
2008-01-22 17:07:25 +03:00
static void ip6_evictor ( struct net * net , struct inet6_dev * idev )
2005-04-17 02:20:36 +04:00
{
2007-10-15 13:40:06 +04:00
int evicted ;
2008-01-22 17:07:25 +03:00
evicted = inet_frag_evictor ( & net - > ipv6 . frags , & ip6_frags ) ;
2007-10-15 13:40:06 +04:00
if ( evicted )
2008-10-08 21:32:43 +04:00
IP6_ADD_STATS_BH ( net , idev , IPSTATS_MIB_REASMFAILS , evicted ) ;
2005-04-17 02:20:36 +04:00
}
static void ip6_frag_expire ( unsigned long data )
{
2007-10-18 06:45:23 +04:00
struct frag_queue * fq ;
2006-11-04 14:11:37 +03:00
struct net_device * dev = NULL ;
2008-05-03 04:02:03 +04:00
struct net * net ;
2005-04-17 02:20:36 +04:00
2007-10-18 06:45:23 +04:00
fq = container_of ( ( struct inet_frag_queue * ) data , struct frag_queue , q ) ;
2007-10-15 13:24:19 +04:00
spin_lock ( & fq - > q . lock ) ;
2005-04-17 02:20:36 +04:00
2008-03-29 02:35:27 +03:00
if ( fq - > q . last_in & INET_FRAG_COMPLETE )
2005-04-17 02:20:36 +04:00
goto out ;
fq_kill ( fq ) ;
2008-05-03 04:02:03 +04:00
net = container_of ( fq - > q . net , struct net , ipv6 . frags ) ;
dev = dev_get_by_index ( net , fq - > iif ) ;
2006-11-04 14:11:37 +03:00
if ( ! dev )
goto out ;
rcu_read_lock ( ) ;
2008-10-08 22:09:27 +04:00
IP6_INC_STATS_BH ( net , __in6_dev_get ( dev ) , IPSTATS_MIB_REASMTIMEOUT ) ;
IP6_INC_STATS_BH ( net , __in6_dev_get ( dev ) , IPSTATS_MIB_REASMFAILS ) ;
2006-11-04 14:11:37 +03:00
rcu_read_unlock ( ) ;
2005-04-17 02:20:36 +04:00
2006-03-21 10:01:17 +03:00
/* Don't send error if the first segment did not arrive. */
2008-03-29 02:35:27 +03:00
if ( ! ( fq - > q . last_in & INET_FRAG_FIRST_IN ) | | ! fq - > q . fragments )
2006-03-21 10:01:17 +03:00
goto out ;
/*
But use as source device on which LAST ARRIVED
segment was received . And do not use fq - > dev
pointer directly , device might already disappeared .
*/
2007-10-15 13:24:19 +04:00
fq - > q . fragments - > dev = dev ;
icmpv6_send ( fq - > q . fragments , ICMPV6_TIME_EXCEED , ICMPV6_EXC_FRAGTIME , 0 , dev ) ;
2005-04-17 02:20:36 +04:00
out :
2006-11-04 14:11:37 +03:00
if ( dev )
dev_put ( dev ) ;
2007-10-15 13:24:19 +04:00
spin_unlock ( & fq - > q . lock ) ;
2007-10-15 13:41:09 +04:00
fq_put ( fq ) ;
2005-04-17 02:20:36 +04:00
}
2007-10-18 06:47:21 +04:00
static __inline__ struct frag_queue *
2008-01-22 17:02:14 +03:00
fq_find ( struct net * net , __be32 id , struct in6_addr * src , struct in6_addr * dst ,
2007-10-18 06:47:21 +04:00
struct inet6_dev * idev )
2005-04-17 02:20:36 +04:00
{
2007-10-18 06:46:47 +04:00
struct inet_frag_queue * q ;
struct ip6_create_arg arg ;
2007-10-18 06:47:21 +04:00
unsigned int hash ;
2005-04-17 02:20:36 +04:00
2007-10-18 06:46:47 +04:00
arg . id = id ;
arg . src = src ;
arg . dst = dst ;
2008-06-28 07:06:08 +04:00
read_lock ( & ip6_frags . lock ) ;
ipv6: almost identical frag hashing funcs combined
$ diff-funcs ip6qhashfn reassembly.c netfilter/nf_conntrack_reasm.c
--- reassembly.c:ip6qhashfn()
+++ netfilter/nf_conntrack_reasm.c:ip6qhashfn()
@@ -1,5 +1,5 @@
-static unsigned int ip6qhashfn(__be32 id, struct in6_addr *saddr,
- struct in6_addr *daddr)
+static unsigned int ip6qhashfn(__be32 id, const struct in6_addr *saddr,
+ const struct in6_addr *daddr)
{
u32 a, b, c;
@@ -9,7 +9,7 @@
a += JHASH_GOLDEN_RATIO;
b += JHASH_GOLDEN_RATIO;
- c += ip6_frags.rnd;
+ c += nf_frags.rnd;
__jhash_mix(a, b, c);
a += (__force u32)saddr->s6_addr32[3];
And codiff xx.o.old xx.o.new:
net/ipv6/netfilter/nf_conntrack_reasm.c:
ip6qhashfn | -512
nf_hashfn | +6
nf_ct_frag6_gather | +36
3 functions changed, 42 bytes added, 512 bytes removed, diff: -470
net/ipv6/reassembly.c:
ip6qhashfn | -512
ip6_hashfn | +7
ipv6_frag_rcv | +89
3 functions changed, 96 bytes added, 512 bytes removed, diff: -416
net/ipv6/reassembly.c:
inet6_hash_frag | +510
1 function changed, 510 bytes added, diff: +510
Total: -376
Compile tested.
Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@helsinki.fi>
Acked-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-10-01 13:48:31 +04:00
hash = inet6_hash_frag ( id , src , dst , ip6_frags . rnd ) ;
2005-04-17 02:20:36 +04:00
2008-01-22 17:02:14 +03:00
q = inet_frag_find ( & net - > ipv6 . frags , & ip6_frags , & arg , hash ) ;
2007-10-18 06:46:47 +04:00
if ( q = = NULL )
goto oom ;
2005-04-17 02:20:36 +04:00
2007-10-18 06:46:47 +04:00
return container_of ( q , struct frag_queue , q ) ;
2005-04-17 02:20:36 +04:00
oom :
2008-10-08 22:09:27 +04:00
IP6_INC_STATS_BH ( net , idev , IPSTATS_MIB_REASMFAILS ) ;
2005-04-17 02:20:36 +04:00
return NULL ;
}
2007-10-15 12:28:47 +04:00
static int ip6_frag_queue ( struct frag_queue * fq , struct sk_buff * skb ,
2005-04-17 02:20:36 +04:00
struct frag_hdr * fhdr , int nhoff )
{
struct sk_buff * prev , * next ;
2007-10-15 12:28:47 +04:00
struct net_device * dev ;
2005-04-17 02:20:36 +04:00
int offset , end ;
2008-10-08 22:09:27 +04:00
struct net * net = dev_net ( skb - > dst - > dev ) ;
2005-04-17 02:20:36 +04:00
2008-03-29 02:35:27 +03:00
if ( fq - > q . last_in & INET_FRAG_COMPLETE )
2005-04-17 02:20:36 +04:00
goto err ;
offset = ntohs ( fhdr - > frag_off ) & ~ 0x7 ;
2007-04-26 04:54:47 +04:00
end = offset + ( ntohs ( ipv6_hdr ( skb ) - > payload_len ) -
( ( u8 * ) ( fhdr + 1 ) - ( u8 * ) ( ipv6_hdr ( skb ) + 1 ) ) ) ;
2005-04-17 02:20:36 +04:00
if ( ( unsigned int ) end > IPV6_MAXPLEN ) {
2008-10-08 22:09:27 +04:00
IP6_INC_STATS_BH ( net , ip6_dst_idev ( skb - > dst ) ,
2006-11-04 14:11:37 +03:00
IPSTATS_MIB_INHDRERRORS ) ;
2007-04-11 07:50:43 +04:00
icmpv6_param_prob ( skb , ICMPV6_HDR_FIELD ,
( ( u8 * ) & fhdr - > frag_off -
skb_network_header ( skb ) ) ) ;
2007-10-15 12:28:47 +04:00
return - 1 ;
2005-04-17 02:20:36 +04:00
}
2007-04-11 07:50:43 +04:00
if ( skb - > ip_summed = = CHECKSUM_COMPLETE ) {
const unsigned char * nh = skb_network_header ( skb ) ;
2007-02-09 17:24:49 +03:00
skb - > csum = csum_sub ( skb - > csum ,
2007-04-11 07:50:43 +04:00
csum_partial ( nh , ( u8 * ) ( fhdr + 1 ) - nh ,
0 ) ) ;
}
2005-04-17 02:20:36 +04:00
/* Is this the final fragment? */
if ( ! ( fhdr - > frag_off & htons ( IP6_MF ) ) ) {
/* If we already have some bits beyond end
* or have different end , the segment is corrupted .
*/
2007-10-15 13:24:19 +04:00
if ( end < fq - > q . len | |
2008-03-29 02:35:27 +03:00
( ( fq - > q . last_in & INET_FRAG_LAST_IN ) & & end ! = fq - > q . len ) )
2005-04-17 02:20:36 +04:00
goto err ;
2008-03-29 02:35:27 +03:00
fq - > q . last_in | = INET_FRAG_LAST_IN ;
2007-10-15 13:24:19 +04:00
fq - > q . len = end ;
2005-04-17 02:20:36 +04:00
} else {
/* Check if the fragment is rounded to 8 bytes.
* Required by the RFC .
*/
if ( end & 0x7 ) {
/* RFC2460 says always send parameter problem in
* this case . - DaveM
*/
2008-10-08 22:09:27 +04:00
IP6_INC_STATS_BH ( net , ip6_dst_idev ( skb - > dst ) ,
2006-11-04 14:11:37 +03:00
IPSTATS_MIB_INHDRERRORS ) ;
2007-02-09 17:24:49 +03:00
icmpv6_param_prob ( skb , ICMPV6_HDR_FIELD ,
2005-04-17 02:20:36 +04:00
offsetof ( struct ipv6hdr , payload_len ) ) ;
2007-10-15 12:28:47 +04:00
return - 1 ;
2005-04-17 02:20:36 +04:00
}
2007-10-15 13:24:19 +04:00
if ( end > fq - > q . len ) {
2005-04-17 02:20:36 +04:00
/* Some bits beyond end -> corruption. */
2008-03-29 02:35:27 +03:00
if ( fq - > q . last_in & INET_FRAG_LAST_IN )
2005-04-17 02:20:36 +04:00
goto err ;
2007-10-15 13:24:19 +04:00
fq - > q . len = end ;
2005-04-17 02:20:36 +04:00
}
}
if ( end = = offset )
goto err ;
/* Point into the IP datagram 'data' part. */
if ( ! pskb_pull ( skb , ( u8 * ) ( fhdr + 1 ) - skb - > data ) )
goto err ;
2007-02-09 17:24:49 +03:00
2005-09-08 23:57:43 +04:00
if ( pskb_trim_rcsum ( skb , end - offset ) )
goto err ;
2005-04-17 02:20:36 +04:00
/* Find out which fragments are in front and at the back of us
* in the chain of fragments so far . We must know where to put
* this fragment , right ?
*/
prev = NULL ;
2007-10-15 13:24:19 +04:00
for ( next = fq - > q . fragments ; next ! = NULL ; next = next - > next ) {
2005-04-17 02:20:36 +04:00
if ( FRAG6_CB ( next ) - > offset > = offset )
break ; /* bingo! */
prev = next ;
}
/* We found where to put this one. Check for overlap with
* preceding fragment , and , if needed , align things so that
* any overlaps are eliminated .
*/
if ( prev ) {
int i = ( FRAG6_CB ( prev ) - > offset + prev - > len ) - offset ;
if ( i > 0 ) {
offset + = i ;
if ( end < = offset )
goto err ;
if ( ! pskb_pull ( skb , i ) )
goto err ;
if ( skb - > ip_summed ! = CHECKSUM_UNNECESSARY )
skb - > ip_summed = CHECKSUM_NONE ;
}
}
/* Look for overlap with succeeding segments.
* If we can merge fragments , do it .
*/
while ( next & & FRAG6_CB ( next ) - > offset < end ) {
int i = end - FRAG6_CB ( next ) - > offset ; /* overlap is 'i' bytes */
if ( i < next - > len ) {
/* Eat head of the next overlapped fragment
* and leave the loop . The next ones cannot overlap .
*/
if ( ! pskb_pull ( next , i ) )
goto err ;
FRAG6_CB ( next ) - > offset + = i ; /* next fragment */
2007-10-15 13:24:19 +04:00
fq - > q . meat - = i ;
2005-04-17 02:20:36 +04:00
if ( next - > ip_summed ! = CHECKSUM_UNNECESSARY )
next - > ip_summed = CHECKSUM_NONE ;
break ;
} else {
struct sk_buff * free_it = next ;
/* Old fragment is completely overridden with
* new one drop it .
*/
next = next - > next ;
if ( prev )
prev - > next = next ;
else
2007-10-15 13:24:19 +04:00
fq - > q . fragments = next ;
2005-04-17 02:20:36 +04:00
2007-10-15 13:24:19 +04:00
fq - > q . meat - = free_it - > len ;
2008-01-22 17:07:25 +03:00
frag_kfree_skb ( fq - > q . net , free_it , NULL ) ;
2005-04-17 02:20:36 +04:00
}
}
FRAG6_CB ( skb ) - > offset = offset ;
/* Insert this fragment in the chain of fragments. */
skb - > next = next ;
if ( prev )
prev - > next = skb ;
else
2007-10-15 13:24:19 +04:00
fq - > q . fragments = skb ;
2005-04-17 02:20:36 +04:00
2007-10-15 12:28:47 +04:00
dev = skb - > dev ;
if ( dev ) {
fq - > iif = dev - > ifindex ;
skb - > dev = NULL ;
}
2007-10-15 13:24:19 +04:00
fq - > q . stamp = skb - > tstamp ;
fq - > q . meat + = skb - > len ;
2008-01-22 17:07:25 +03:00
atomic_add ( skb - > truesize , & fq - > q . net - > mem ) ;
2005-04-17 02:20:36 +04:00
/* The first fragment.
* nhoffset is obtained from the first fragment , of course .
*/
if ( offset = = 0 ) {
fq - > nhoffset = nhoff ;
2008-03-29 02:35:27 +03:00
fq - > q . last_in | = INET_FRAG_FIRST_IN ;
2005-04-17 02:20:36 +04:00
}
2007-10-15 12:28:47 +04:00
2008-03-29 02:35:27 +03:00
if ( fq - > q . last_in = = ( INET_FRAG_FIRST_IN | INET_FRAG_LAST_IN ) & &
fq - > q . meat = = fq - > q . len )
2007-10-15 12:28:47 +04:00
return ip6_frag_reasm ( fq , prev , dev ) ;
2007-10-15 13:31:52 +04:00
write_lock ( & ip6_frags . lock ) ;
2008-01-22 17:11:48 +03:00
list_move_tail ( & fq - > q . lru_list , & fq - > q . net - > lru_list ) ;
2007-10-15 13:31:52 +04:00
write_unlock ( & ip6_frags . lock ) ;
2007-10-15 12:28:47 +04:00
return - 1 ;
2005-04-17 02:20:36 +04:00
err :
2008-10-08 22:09:27 +04:00
IP6_INC_STATS ( net , ip6_dst_idev ( skb - > dst ) ,
2008-10-08 21:54:51 +04:00
IPSTATS_MIB_REASMFAILS ) ;
2005-04-17 02:20:36 +04:00
kfree_skb ( skb ) ;
2007-10-15 12:28:47 +04:00
return - 1 ;
2005-04-17 02:20:36 +04:00
}
/*
* Check if this packet is complete .
* Returns NULL on failure by any reason , and pointer
* to current nexthdr field in reassembled frame .
*
* It is called with locked fq , and caller must check that
* queue is eligible for reassembly i . e . it is not COMPLETE ,
* the last and the first frames arrived and all the bits are here .
*/
2007-10-15 12:28:47 +04:00
static int ip6_frag_reasm ( struct frag_queue * fq , struct sk_buff * prev ,
2005-04-17 02:20:36 +04:00
struct net_device * dev )
{
2007-10-15 13:24:19 +04:00
struct sk_buff * fp , * head = fq - > q . fragments ;
2005-04-17 02:20:36 +04:00
int payload_len ;
unsigned int nhoff ;
fq_kill ( fq ) ;
2007-10-15 12:28:47 +04:00
/* Make the one we just received the head. */
if ( prev ) {
head = prev - > next ;
fp = skb_clone ( head , GFP_ATOMIC ) ;
if ( ! fp )
goto out_oom ;
fp - > next = head - > next ;
prev - > next = fp ;
2007-10-15 13:24:19 +04:00
skb_morph ( head , fq - > q . fragments ) ;
head - > next = fq - > q . fragments - > next ;
2007-10-15 12:28:47 +04:00
2007-10-15 13:24:19 +04:00
kfree_skb ( fq - > q . fragments ) ;
fq - > q . fragments = head ;
2007-10-15 12:28:47 +04:00
}
2008-07-26 08:43:18 +04:00
WARN_ON ( head = = NULL ) ;
WARN_ON ( FRAG6_CB ( head ) - > offset ! = 0 ) ;
2005-04-17 02:20:36 +04:00
/* Unfragmented part is taken from the first segment. */
2007-04-11 07:50:43 +04:00
payload_len = ( ( head - > data - skb_network_header ( head ) ) -
2007-10-15 13:24:19 +04:00
sizeof ( struct ipv6hdr ) + fq - > q . len -
2007-04-11 07:50:43 +04:00
sizeof ( struct frag_hdr ) ) ;
2005-04-17 02:20:36 +04:00
if ( payload_len > IPV6_MAXPLEN )
goto out_oversize ;
/* Head of list must not be cloned. */
if ( skb_cloned ( head ) & & pskb_expand_head ( head , 0 , 0 , GFP_ATOMIC ) )
goto out_oom ;
/* If the first fragment is fragmented itself, we split
* it to two chunks : the first with data and paged part
* and the second , holding only fragments . */
if ( skb_shinfo ( head ) - > frag_list ) {
struct sk_buff * clone ;
int i , plen = 0 ;
if ( ( clone = alloc_skb ( 0 , GFP_ATOMIC ) ) = = NULL )
goto out_oom ;
clone - > next = head - > next ;
head - > next = clone ;
skb_shinfo ( clone ) - > frag_list = skb_shinfo ( head ) - > frag_list ;
skb_shinfo ( head ) - > frag_list = NULL ;
for ( i = 0 ; i < skb_shinfo ( head ) - > nr_frags ; i + + )
plen + = skb_shinfo ( head ) - > frags [ i ] . size ;
clone - > len = clone - > data_len = head - > data_len - plen ;
head - > data_len - = clone - > len ;
head - > len - = clone - > len ;
clone - > csum = 0 ;
clone - > ip_summed = head - > ip_summed ;
2008-01-22 17:07:25 +03:00
atomic_add ( clone - > truesize , & fq - > q . net - > mem ) ;
2005-04-17 02:20:36 +04:00
}
/* We have to remove fragment header from datagram and to relocate
* header in order to calculate ICV correctly . */
nhoff = fq - > nhoffset ;
2007-04-11 08:21:55 +04:00
skb_network_header ( head ) [ nhoff ] = skb_transport_header ( head ) [ 0 ] ;
2007-02-09 17:24:49 +03:00
memmove ( head - > head + sizeof ( struct frag_hdr ) , head - > head ,
2005-04-17 02:20:36 +04:00
( head - > data - head - > head ) - sizeof ( struct frag_hdr ) ) ;
2007-04-11 08:21:55 +04:00
head - > mac_header + = sizeof ( struct frag_hdr ) ;
head - > network_header + = sizeof ( struct frag_hdr ) ;
2005-04-17 02:20:36 +04:00
skb_shinfo ( head ) - > frag_list = head - > next ;
2007-03-13 19:06:52 +03:00
skb_reset_transport_header ( head ) ;
2007-04-11 07:50:43 +04:00
skb_push ( head , head - > data - skb_network_header ( head ) ) ;
2008-01-22 17:07:25 +03:00
atomic_sub ( head - > truesize , & fq - > q . net - > mem ) ;
2005-04-17 02:20:36 +04:00
for ( fp = head - > next ; fp ; fp = fp - > next ) {
head - > data_len + = fp - > len ;
head - > len + = fp - > len ;
if ( head - > ip_summed ! = fp - > ip_summed )
head - > ip_summed = CHECKSUM_NONE ;
2006-08-30 03:44:56 +04:00
else if ( head - > ip_summed = = CHECKSUM_COMPLETE )
2005-04-17 02:20:36 +04:00
head - > csum = csum_add ( head - > csum , fp - > csum ) ;
head - > truesize + = fp - > truesize ;
2008-01-22 17:07:25 +03:00
atomic_sub ( fp - > truesize , & fq - > q . net - > mem ) ;
2005-04-17 02:20:36 +04:00
}
head - > next = NULL ;
head - > dev = dev ;
2007-10-15 13:24:19 +04:00
head - > tstamp = fq - > q . stamp ;
2007-04-26 04:54:47 +04:00
ipv6_hdr ( head ) - > payload_len = htons ( payload_len ) ;
2006-01-07 10:02:34 +03:00
IP6CB ( head ) - > nhoff = nhoff ;
2005-04-17 02:20:36 +04:00
/* Yes, and fold redundant checksum back. 8) */
2006-08-30 03:44:56 +04:00
if ( head - > ip_summed = = CHECKSUM_COMPLETE )
2007-04-11 07:50:43 +04:00
head - > csum = csum_partial ( skb_network_header ( head ) ,
2007-03-16 23:26:39 +03:00
skb_network_header_len ( head ) ,
2007-04-11 07:50:43 +04:00
head - > csum ) ;
2005-04-17 02:20:36 +04:00
2006-11-04 14:11:37 +03:00
rcu_read_lock ( ) ;
2008-10-08 22:09:27 +04:00
IP6_INC_STATS_BH ( dev_net ( dev ) ,
__in6_dev_get ( dev ) , IPSTATS_MIB_REASMOKS ) ;
2006-11-04 14:11:37 +03:00
rcu_read_unlock ( ) ;
2007-10-15 13:24:19 +04:00
fq - > q . fragments = NULL ;
2005-04-17 02:20:36 +04:00
return 1 ;
out_oversize :
if ( net_ratelimit ( ) )
printk ( KERN_DEBUG " ip6_frag_reasm: payload len = %d \n " , payload_len ) ;
goto out_fail ;
out_oom :
if ( net_ratelimit ( ) )
printk ( KERN_DEBUG " ip6_frag_reasm: no memory for reassembly \n " ) ;
out_fail :
2006-11-04 14:11:37 +03:00
rcu_read_lock ( ) ;
2008-10-08 22:09:27 +04:00
IP6_INC_STATS_BH ( dev_net ( dev ) ,
__in6_dev_get ( dev ) , IPSTATS_MIB_REASMFAILS ) ;
2006-11-04 14:11:37 +03:00
rcu_read_unlock ( ) ;
2005-04-17 02:20:36 +04:00
return - 1 ;
}
2007-10-15 23:50:28 +04:00
static int ipv6_frag_rcv ( struct sk_buff * skb )
2005-04-17 02:20:36 +04:00
{
struct frag_hdr * fhdr ;
struct frag_queue * fq ;
2007-04-26 04:54:47 +04:00
struct ipv6hdr * hdr = ipv6_hdr ( skb ) ;
2008-10-08 21:54:51 +04:00
struct net * net = dev_net ( skb - > dst - > dev ) ;
2005-04-17 02:20:36 +04:00
2008-10-08 22:09:27 +04:00
IP6_INC_STATS_BH ( net , ip6_dst_idev ( skb - > dst ) , IPSTATS_MIB_REASMREQDS ) ;
2005-04-17 02:20:36 +04:00
/* Jumbo payload inhibits frag. header */
2008-10-08 21:31:44 +04:00
if ( hdr - > payload_len = = 0 )
goto fail_hdr ;
2007-04-26 04:55:53 +04:00
if ( ! pskb_may_pull ( skb , ( skb_transport_offset ( skb ) +
2008-10-08 21:31:44 +04:00
sizeof ( struct frag_hdr ) ) ) )
goto fail_hdr ;
2005-04-17 02:20:36 +04:00
2007-04-26 04:54:47 +04:00
hdr = ipv6_hdr ( skb ) ;
2007-04-26 05:04:18 +04:00
fhdr = ( struct frag_hdr * ) skb_transport_header ( skb ) ;
2005-04-17 02:20:36 +04:00
if ( ! ( fhdr - > frag_off & htons ( 0xFFF9 ) ) ) {
/* It is not a fragmented frame */
2007-04-11 08:21:55 +04:00
skb - > transport_header + = sizeof ( struct frag_hdr ) ;
2008-10-08 22:09:27 +04:00
IP6_INC_STATS_BH ( net ,
ip6_dst_idev ( skb - > dst ) , IPSTATS_MIB_REASMOKS ) ;
2005-04-17 02:20:36 +04:00
2007-04-11 07:50:43 +04:00
IP6CB ( skb ) - > nhoff = ( u8 * ) fhdr - skb_network_header ( skb ) ;
2005-04-17 02:20:36 +04:00
return 1 ;
}
2008-01-22 17:10:13 +03:00
if ( atomic_read ( & net - > ipv6 . frags . mem ) > net - > ipv6 . frags . high_thresh )
2008-01-22 17:07:25 +03:00
ip6_evictor ( net , ip6_dst_idev ( skb - > dst ) ) ;
2005-04-17 02:20:36 +04:00
2008-01-22 17:02:14 +03:00
if ( ( fq = fq_find ( net , fhdr - > identification , & hdr - > saddr , & hdr - > daddr ,
2006-11-04 14:11:37 +03:00
ip6_dst_idev ( skb - > dst ) ) ) ! = NULL ) {
2007-10-15 12:28:47 +04:00
int ret ;
2005-04-17 02:20:36 +04:00
2007-10-15 13:24:19 +04:00
spin_lock ( & fq - > q . lock ) ;
2005-04-17 02:20:36 +04:00
2007-10-15 12:28:47 +04:00
ret = ip6_frag_queue ( fq , skb , fhdr , IP6CB ( skb ) - > nhoff ) ;
2005-04-17 02:20:36 +04:00
2007-10-15 13:24:19 +04:00
spin_unlock ( & fq - > q . lock ) ;
2007-10-15 13:41:09 +04:00
fq_put ( fq ) ;
2005-04-17 02:20:36 +04:00
return ret ;
}
2008-10-08 22:09:27 +04:00
IP6_INC_STATS_BH ( net , ip6_dst_idev ( skb - > dst ) , IPSTATS_MIB_REASMFAILS ) ;
2005-04-17 02:20:36 +04:00
kfree_skb ( skb ) ;
return - 1 ;
2008-10-08 21:31:44 +04:00
fail_hdr :
2008-10-08 21:54:51 +04:00
IP6_INC_STATS ( net , ip6_dst_idev ( skb - > dst ) , IPSTATS_MIB_INHDRERRORS ) ;
2008-10-08 21:31:44 +04:00
icmpv6_param_prob ( skb , ICMPV6_HDR_FIELD , skb_network_header_len ( skb ) ) ;
return - 1 ;
2005-04-17 02:20:36 +04:00
}
static struct inet6_protocol frag_protocol =
{
. handler = ipv6_frag_rcv ,
. flags = INET6_PROTO_NOPOLICY ,
} ;
2008-01-22 16:58:31 +03:00
# ifdef CONFIG_SYSCTL
2008-05-20 00:51:29 +04:00
static struct ctl_table ip6_frags_ns_ctl_table [ ] = {
2008-01-22 16:58:31 +03:00
{
. ctl_name = NET_IPV6_IP6FRAG_HIGH_THRESH ,
. procname = " ip6frag_high_thresh " ,
2008-01-22 17:10:13 +03:00
. data = & init_net . ipv6 . frags . high_thresh ,
2008-01-22 16:58:31 +03:00
. maxlen = sizeof ( int ) ,
. mode = 0644 ,
2008-11-04 05:21:05 +03:00
. proc_handler = proc_dointvec
2008-01-22 16:58:31 +03:00
} ,
{
. ctl_name = NET_IPV6_IP6FRAG_LOW_THRESH ,
. procname = " ip6frag_low_thresh " ,
2008-01-22 17:10:13 +03:00
. data = & init_net . ipv6 . frags . low_thresh ,
2008-01-22 16:58:31 +03:00
. maxlen = sizeof ( int ) ,
. mode = 0644 ,
2008-11-04 05:21:05 +03:00
. proc_handler = proc_dointvec
2008-01-22 16:58:31 +03:00
} ,
{
. ctl_name = NET_IPV6_IP6FRAG_TIME ,
. procname = " ip6frag_time " ,
2008-01-22 17:09:37 +03:00
. data = & init_net . ipv6 . frags . timeout ,
2008-01-22 16:58:31 +03:00
. maxlen = sizeof ( int ) ,
. mode = 0644 ,
2008-11-04 05:21:05 +03:00
. proc_handler = proc_dointvec_jiffies ,
. strategy = sysctl_jiffies ,
2008-01-22 16:58:31 +03:00
} ,
2008-05-20 00:53:02 +04:00
{ }
} ;
static struct ctl_table ip6_frags_ctl_table [ ] = {
2008-01-22 16:58:31 +03:00
{
. ctl_name = NET_IPV6_IP6FRAG_SECRET_INTERVAL ,
. procname = " ip6frag_secret_interval " ,
2008-01-22 17:11:04 +03:00
. data = & ip6_frags . secret_interval ,
2008-01-22 16:58:31 +03:00
. maxlen = sizeof ( int ) ,
. mode = 0644 ,
2008-11-04 05:21:05 +03:00
. proc_handler = proc_dointvec_jiffies ,
. strategy = sysctl_jiffies
2008-01-22 16:58:31 +03:00
} ,
{ }
} ;
2008-05-20 00:51:29 +04:00
static int ip6_frags_ns_sysctl_register ( struct net * net )
2008-01-22 16:58:31 +03:00
{
2008-01-22 17:08:36 +03:00
struct ctl_table * table ;
2008-01-22 16:58:31 +03:00
struct ctl_table_header * hdr ;
2008-05-20 00:51:29 +04:00
table = ip6_frags_ns_ctl_table ;
2008-01-22 17:08:36 +03:00
if ( net ! = & init_net ) {
2008-05-20 00:51:29 +04:00
table = kmemdup ( table , sizeof ( ip6_frags_ns_ctl_table ) , GFP_KERNEL ) ;
2008-01-22 17:08:36 +03:00
if ( table = = NULL )
goto err_alloc ;
2008-01-22 17:10:13 +03:00
table [ 0 ] . data = & net - > ipv6 . frags . high_thresh ;
table [ 1 ] . data = & net - > ipv6 . frags . low_thresh ;
2008-01-22 17:09:37 +03:00
table [ 2 ] . data = & net - > ipv6 . frags . timeout ;
2008-01-22 17:08:36 +03:00
}
hdr = register_net_sysctl_table ( net , net_ipv6_ctl_path , table ) ;
if ( hdr = = NULL )
goto err_reg ;
net - > ipv6 . sysctl . frags_hdr = hdr ;
return 0 ;
err_reg :
if ( net ! = & init_net )
kfree ( table ) ;
err_alloc :
return - ENOMEM ;
}
2008-05-20 00:51:29 +04:00
static void ip6_frags_ns_sysctl_unregister ( struct net * net )
2008-01-22 17:08:36 +03:00
{
struct ctl_table * table ;
table = net - > ipv6 . sysctl . frags_hdr - > ctl_table_arg ;
unregister_net_sysctl_table ( net - > ipv6 . sysctl . frags_hdr ) ;
kfree ( table ) ;
2008-01-22 16:58:31 +03:00
}
2008-05-20 00:53:02 +04:00
static struct ctl_table_header * ip6_ctl_header ;
static int ip6_frags_sysctl_register ( void )
{
ip6_ctl_header = register_net_sysctl_rotable ( net_ipv6_ctl_path ,
ip6_frags_ctl_table ) ;
return ip6_ctl_header = = NULL ? - ENOMEM : 0 ;
}
static void ip6_frags_sysctl_unregister ( void )
{
unregister_net_sysctl_table ( ip6_ctl_header ) ;
}
2008-01-22 16:58:31 +03:00
# else
2008-05-20 00:51:29 +04:00
static inline int ip6_frags_ns_sysctl_register ( struct net * net )
2008-01-10 13:56:03 +03:00
{
2008-01-22 16:58:31 +03:00
return 0 ;
}
2008-01-22 17:08:36 +03:00
2008-05-20 00:51:29 +04:00
static inline void ip6_frags_ns_sysctl_unregister ( struct net * net )
2008-01-22 17:08:36 +03:00
{
}
2008-05-20 00:53:02 +04:00
static inline int ip6_frags_sysctl_register ( void )
{
return 0 ;
}
static inline void ip6_frags_sysctl_unregister ( void )
{
}
2008-01-22 16:58:31 +03:00
# endif
2008-01-19 10:52:35 +03:00
2008-01-22 16:58:31 +03:00
static int ipv6_frags_init_net ( struct net * net )
{
2008-01-22 17:10:13 +03:00
net - > ipv6 . frags . high_thresh = 256 * 1024 ;
net - > ipv6 . frags . low_thresh = 192 * 1024 ;
2008-01-22 17:09:37 +03:00
net - > ipv6 . frags . timeout = IPV6_FRAG_TIMEOUT ;
2008-01-22 16:58:31 +03:00
2008-01-22 17:06:23 +03:00
inet_frags_init_net ( & net - > ipv6 . frags ) ;
2008-05-20 00:51:29 +04:00
return ip6_frags_ns_sysctl_register ( net ) ;
2008-01-10 13:56:03 +03:00
}
2008-01-22 17:12:39 +03:00
static void ipv6_frags_exit_net ( struct net * net )
{
2008-05-20 00:51:29 +04:00
ip6_frags_ns_sysctl_unregister ( net ) ;
2008-01-22 17:12:39 +03:00
inet_frags_exit_net ( & net - > ipv6 . frags , & ip6_frags ) ;
}
static struct pernet_operations ip6_frags_ops = {
. init = ipv6_frags_init_net ,
. exit = ipv6_frags_exit_net ,
} ;
2007-12-11 13:24:29 +03:00
int __init ipv6_frag_init ( void )
2005-04-17 02:20:36 +04:00
{
2007-12-11 13:24:29 +03:00
int ret ;
2005-04-17 02:20:36 +04:00
2007-12-11 13:24:29 +03:00
ret = inet6_add_protocol ( & frag_protocol , IPPROTO_FRAGMENT ) ;
if ( ret )
goto out ;
2008-01-10 13:56:03 +03:00
2008-05-20 00:53:02 +04:00
ret = ip6_frags_sysctl_register ( ) ;
if ( ret )
goto err_sysctl ;
2008-05-20 00:52:28 +04:00
ret = register_pernet_subsys ( & ip6_frags_ops ) ;
if ( ret )
goto err_pernet ;
2008-01-22 16:58:31 +03:00
2007-10-15 13:38:08 +04:00
ip6_frags . hashfn = ip6_hashfn ;
2007-10-18 06:46:47 +04:00
ip6_frags . constructor = ip6_frag_init ;
2007-10-18 06:48:26 +04:00
ip6_frags . destructor = NULL ;
2007-10-15 13:39:14 +04:00
ip6_frags . skb_free = NULL ;
ip6_frags . qsize = sizeof ( struct frag_queue ) ;
2007-10-18 06:47:21 +04:00
ip6_frags . match = ip6_frag_match ;
2007-10-18 06:45:23 +04:00
ip6_frags . frag_expire = ip6_frag_expire ;
2008-01-22 17:11:04 +03:00
ip6_frags . secret_interval = 10 * 60 * HZ ;
2007-10-15 13:31:52 +04:00
inet_frags_init ( & ip6_frags ) ;
2007-12-11 13:24:29 +03:00
out :
return ret ;
2008-05-20 00:52:28 +04:00
err_pernet :
2008-05-20 00:53:02 +04:00
ip6_frags_sysctl_unregister ( ) ;
err_sysctl :
2008-05-20 00:52:28 +04:00
inet6_del_protocol ( & frag_protocol , IPPROTO_FRAGMENT ) ;
goto out ;
2007-12-11 13:24:29 +03:00
}
void ipv6_frag_exit ( void )
{
inet_frags_fini ( & ip6_frags ) ;
2008-05-20 00:53:02 +04:00
ip6_frags_sysctl_unregister ( ) ;
2008-01-22 17:12:39 +03:00
unregister_pernet_subsys ( & ip6_frags_ops ) ;
2007-12-11 13:24:29 +03:00
inet6_del_protocol ( & frag_protocol , IPPROTO_FRAGMENT ) ;
2005-04-17 02:20:36 +04:00
}