2005-04-17 02:20:36 +04:00
/*
* IP Payload Compression Protocol ( IPComp ) - RFC3173 .
*
* Copyright ( c ) 2003 James Morris < jmorris @ intercode . com . au >
*
* 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
2007-02-09 17:24:47 +03:00
* Software Foundation ; either version 2 of the License , or ( at your option )
2005-04-17 02:20:36 +04:00
* any later version .
*
* Todo :
* - Tunable compression parameters .
* - Compression stats .
* - Adaptive compression .
*/
# include <linux/module.h>
2007-11-07 13:21:47 +03:00
# include <linux/err.h>
2005-04-17 02:20:36 +04:00
# include <linux/rtnetlink.h>
# include <net/ip.h>
# include <net/xfrm.h>
# include <net/icmp.h>
# include <net/ipcomp.h>
2005-12-27 07:43:12 +03:00
# include <net/protocol.h>
2008-07-25 13:54:40 +04:00
# include <net/sock.h>
2005-04-17 02:20:36 +04:00
static void ipcomp4_err ( struct sk_buff * skb , u32 info )
{
2006-09-28 05:47:24 +04:00
__be32 spi ;
2005-04-17 02:20:36 +04:00
struct iphdr * iph = ( struct iphdr * ) skb - > data ;
struct ip_comp_hdr * ipch = ( struct ip_comp_hdr * ) ( skb - > data + ( iph - > ihl < < 2 ) ) ;
struct xfrm_state * x ;
2007-03-13 20:43:18 +03:00
if ( icmp_hdr ( skb ) - > type ! = ICMP_DEST_UNREACH | |
icmp_hdr ( skb ) - > code ! = ICMP_FRAG_NEEDED )
2005-04-17 02:20:36 +04:00
return ;
2006-05-23 03:53:22 +04:00
spi = htonl ( ntohs ( ipch - > cpi ) ) ;
2008-11-26 04:30:50 +03:00
x = xfrm_state_lookup ( & init_net , ( xfrm_address_t * ) & iph - > daddr ,
2007-02-09 17:24:47 +03:00
spi , IPPROTO_COMP , AF_INET ) ;
2005-04-17 02:20:36 +04:00
if ( ! x )
return ;
2008-10-31 10:53:57 +03:00
NETDEBUG ( KERN_DEBUG " pmtu discovery on SA IPCOMP/%08x/%pI4 \n " ,
spi , & iph - > daddr ) ;
2005-04-17 02:20:36 +04:00
xfrm_state_put ( x ) ;
}
2007-02-09 17:24:47 +03:00
/* We always hold one tunnel user reference to indicate a tunnel */
2005-04-17 02:20:36 +04:00
static struct xfrm_state * ipcomp_tunnel_create ( struct xfrm_state * x )
{
struct xfrm_state * t ;
2007-02-09 17:24:47 +03:00
2008-11-26 04:15:16 +03:00
t = xfrm_state_alloc ( & init_net ) ;
2005-04-17 02:20:36 +04:00
if ( t = = NULL )
goto out ;
t - > id . proto = IPPROTO_IPIP ;
t - > id . spi = x - > props . saddr . a4 ;
t - > id . daddr . a4 = x - > id . daddr . a4 ;
memcpy ( & t - > sel , & x - > sel , sizeof ( t - > sel ) ) ;
t - > props . family = AF_INET ;
2007-11-14 08:39:08 +03:00
t - > props . mode = x - > props . mode ;
2005-04-17 02:20:36 +04:00
t - > props . saddr . a4 = x - > props . saddr . a4 ;
t - > props . flags = x - > props . flags ;
2005-06-21 00:18:08 +04:00
if ( xfrm_init_state ( t ) )
2005-04-17 02:20:36 +04:00
goto error ;
atomic_set ( & t - > tunnel_users , 1 ) ;
out :
return t ;
error :
t - > km . state = XFRM_STATE_DEAD ;
xfrm_state_put ( t ) ;
t = NULL ;
goto out ;
}
/*
2006-03-21 09:33:17 +03:00
* Must be protected by xfrm_cfg_mutex . State and tunnel user references are
2005-04-17 02:20:36 +04:00
* always incremented on success .
*/
static int ipcomp_tunnel_attach ( struct xfrm_state * x )
{
int err = 0 ;
struct xfrm_state * t ;
2008-11-26 04:30:50 +03:00
t = xfrm_state_lookup ( & init_net , ( xfrm_address_t * ) & x - > id . daddr . a4 ,
2007-02-09 17:24:47 +03:00
x - > props . saddr . a4 , IPPROTO_IPIP , AF_INET ) ;
2005-04-17 02:20:36 +04:00
if ( ! t ) {
t = ipcomp_tunnel_create ( x ) ;
if ( ! t ) {
err = - EINVAL ;
goto out ;
}
xfrm_state_insert ( t ) ;
xfrm_state_hold ( t ) ;
}
x - > tunnel = t ;
atomic_inc ( & t - > tunnel_users ) ;
out :
return err ;
}
2008-07-25 13:54:40 +04:00
static int ipcomp4_init_state ( struct xfrm_state * x )
2005-04-17 02:20:36 +04:00
{
2008-07-27 14:59:24 +04:00
int err = - EINVAL ;
2005-04-17 02:20:36 +04:00
2007-11-14 08:39:08 +03:00
x - > props . header_len = 0 ;
switch ( x - > props . mode ) {
case XFRM_MODE_TRANSPORT :
break ;
case XFRM_MODE_TUNNEL :
x - > props . header_len + = sizeof ( struct iphdr ) ;
break ;
default :
goto out ;
}
2008-07-25 13:54:40 +04:00
err = ipcomp_init_state ( x ) ;
if ( err )
2005-04-17 02:20:36 +04:00
goto out ;
2006-09-23 02:05:15 +04:00
if ( x - > props . mode = = XFRM_MODE_TUNNEL ) {
2005-04-17 02:20:36 +04:00
err = ipcomp_tunnel_attach ( x ) ;
if ( err )
goto error_tunnel ;
}
err = 0 ;
out :
return err ;
error_tunnel :
2008-07-25 13:54:40 +04:00
ipcomp_destroy ( x ) ;
2005-04-17 02:20:36 +04:00
goto out ;
}
2008-01-31 06:11:50 +03:00
static const struct xfrm_type ipcomp_type = {
2005-04-17 02:20:36 +04:00
. description = " IPCOMP4 " ,
. owner = THIS_MODULE ,
. proto = IPPROTO_COMP ,
2008-07-25 13:54:40 +04:00
. init_state = ipcomp4_init_state ,
2005-04-17 02:20:36 +04:00
. destructor = ipcomp_destroy ,
. input = ipcomp_input ,
. output = ipcomp_output
} ;
static struct net_protocol ipcomp4_protocol = {
. handler = xfrm4_rcv ,
. err_handler = ipcomp4_err ,
. no_policy = 1 ,
} ;
static int __init ipcomp4_init ( void )
{
if ( xfrm_register_type ( & ipcomp_type , AF_INET ) < 0 ) {
printk ( KERN_INFO " ipcomp init: can't add xfrm type \n " ) ;
return - EAGAIN ;
}
if ( inet_add_protocol ( & ipcomp4_protocol , IPPROTO_COMP ) < 0 ) {
printk ( KERN_INFO " ipcomp init: can't add protocol \n " ) ;
xfrm_unregister_type ( & ipcomp_type , AF_INET ) ;
return - EAGAIN ;
}
return 0 ;
}
static void __exit ipcomp4_fini ( void )
{
if ( inet_del_protocol ( & ipcomp4_protocol , IPPROTO_COMP ) < 0 )
printk ( KERN_INFO " ip ipcomp close: can't remove protocol \n " ) ;
if ( xfrm_unregister_type ( & ipcomp_type , AF_INET ) < 0 )
printk ( KERN_INFO " ip ipcomp close: can't remove xfrm type \n " ) ;
}
module_init ( ipcomp4_init ) ;
module_exit ( ipcomp4_fini ) ;
MODULE_LICENSE ( " GPL " ) ;
2008-07-25 13:54:40 +04:00
MODULE_DESCRIPTION ( " IP Payload Compression Protocol (IPComp/IPv4) - RFC3173 " ) ;
2005-04-17 02:20:36 +04:00
MODULE_AUTHOR ( " James Morris <jmorris@intercode.com.au> " ) ;
2007-06-27 10:57:49 +04:00
MODULE_ALIAS_XFRM_TYPE ( AF_INET , XFRM_PROTO_COMP ) ;