2019-05-19 16:51:43 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2005-04-17 02:20:36 +04:00
/*
* IP Payload Compression Protocol ( IPComp ) for IPv6 - RFC3173
*
* Copyright ( C ) 2003 USAGI / WIDE Project
*
* Author Mitsuru KANDA < mk @ linux - ipv6 . org >
*/
2007-02-09 17:24:49 +03:00
/*
2005-04-17 02:20:36 +04:00
* [ Memo ]
*
* Outbound :
2007-02-09 17:24:49 +03:00
* The compression of IP datagram MUST be done before AH / ESP processing ,
* fragmentation , and the addition of Hop - by - Hop / Routing header .
2005-04-17 02:20:36 +04:00
*
* Inbound :
2007-02-09 17:24:49 +03:00
* The decompression of IP datagram MUST be done after the reassembly ,
2005-04-17 02:20:36 +04:00
* AH / ESP processing .
*/
2012-05-15 18:11:53 +04:00
# define pr_fmt(fmt) "IPv6: " fmt
2005-04-17 02:20:36 +04:00
# include <linux/module.h>
# include <net/ip.h>
# include <net/xfrm.h>
# include <net/ipcomp.h>
# include <linux/crypto.h>
2007-11-07 13:21:47 +03:00
# include <linux/err.h>
2005-04-17 02:20:36 +04:00
# include <linux/pfkeyv2.h>
# include <linux/random.h>
# include <linux/percpu.h>
# include <linux/smp.h>
# include <linux/list.h>
# include <linux/vmalloc.h>
# include <linux/rtnetlink.h>
2012-06-16 01:54:11 +04:00
# include <net/ip6_route.h>
2005-04-17 02:20:36 +04:00
# include <net/icmp.h>
# include <net/ipv6.h>
2005-12-27 07:43:12 +03:00
# include <net/protocol.h>
2005-04-17 02:20:36 +04:00
# include <linux/ipv6.h>
# include <linux/icmpv6.h>
2006-03-21 09:33:17 +03:00
# include <linux/mutex.h>
2005-04-17 02:20:36 +04:00
2014-03-14 10:28:07 +04:00
static int ipcomp6_err ( struct sk_buff * skb , struct inet6_skb_parm * opt ,
2009-06-23 15:31:07 +04:00
u8 type , u8 code , int offset , __be32 info )
2005-04-17 02:20:36 +04:00
{
2010-01-25 13:39:09 +03:00
struct net * net = dev_net ( skb - > dev ) ;
2006-09-28 05:47:24 +04:00
__be32 spi ;
2011-04-22 08:53:02 +04:00
const struct ipv6hdr * iph = ( const struct ipv6hdr * ) skb - > data ;
2007-10-11 02:45:25 +04:00
struct ip_comp_hdr * ipcomph =
( struct ip_comp_hdr * ) ( skb - > data + offset ) ;
2005-04-17 02:20:36 +04:00
struct xfrm_state * x ;
2013-09-10 15:43:09 +04:00
if ( type ! = ICMPV6_PKT_TOOBIG & &
2012-07-12 11:25:15 +04:00
type ! = NDISC_REDIRECT )
2014-03-14 10:28:07 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
2006-05-23 03:53:22 +04:00
spi = htonl ( ntohs ( ipcomph - > cpi ) ) ;
2011-04-22 08:53:02 +04:00
x = xfrm_state_lookup ( net , skb - > mark , ( const xfrm_address_t * ) & iph - > daddr ,
spi , IPPROTO_COMP , AF_INET6 ) ;
2005-04-17 02:20:36 +04:00
if ( ! x )
2014-03-14 10:28:07 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
2012-07-12 11:25:15 +04:00
if ( type = = NDISC_REDIRECT )
2016-11-03 20:23:43 +03:00
ip6_redirect ( skb , net , skb - > dev - > ifindex , 0 ,
sock_net_uid ( net , NULL ) ) ;
2012-07-12 11:25:15 +04:00
else
2016-11-03 20:23:43 +03:00
ip6_update_pmtu ( skb , net , info , 0 , 0 , sock_net_uid ( net , NULL ) ) ;
2005-04-17 02:20:36 +04:00
xfrm_state_put ( x ) ;
2014-03-14 10:28:07 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
static struct xfrm_state * ipcomp6_tunnel_create ( struct xfrm_state * x )
{
2010-01-25 13:39:09 +03:00
struct net * net = xs_net ( x ) ;
2005-04-17 02:20:36 +04:00
struct xfrm_state * t = NULL ;
2010-01-25 13:39:09 +03:00
t = xfrm_state_alloc ( net ) ;
2005-04-17 02:20:36 +04:00
if ( ! t )
goto out ;
t - > id . proto = IPPROTO_IPV6 ;
2010-01-25 13:39:09 +03:00
t - > id . spi = xfrm6_tunnel_alloc_spi ( net , ( xfrm_address_t * ) & x - > props . saddr ) ;
2006-03-27 05:37:54 +04:00
if ( ! t - > id . spi )
goto error ;
2005-04-17 02:20:36 +04:00
memcpy ( t - > id . daddr . a6 , x - > id . daddr . a6 , sizeof ( struct in6_addr ) ) ;
memcpy ( & t - > sel , & x - > sel , sizeof ( t - > sel ) ) ;
t - > props . family = AF_INET6 ;
2007-11-14 08:39:08 +03:00
t - > props . mode = x - > props . mode ;
2005-04-17 02:20:36 +04:00
memcpy ( t - > props . saddr . a6 , x - > props . saddr . a6 , sizeof ( struct in6_addr ) ) ;
2010-02-23 03:20:22 +03:00
memcpy ( & t - > mark , & x - > mark , sizeof ( t - > mark ) ) ;
2005-04-17 02:20:36 +04:00
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 :
2006-03-27 05:37:54 +04:00
t - > km . state = XFRM_STATE_DEAD ;
2005-04-17 02:20:36 +04:00
xfrm_state_put ( t ) ;
2006-03-27 05:37:54 +04:00
t = NULL ;
2005-04-17 02:20:36 +04:00
goto out ;
}
static int ipcomp6_tunnel_attach ( struct xfrm_state * x )
{
2010-01-25 13:39:09 +03:00
struct net * net = xs_net ( x ) ;
2005-04-17 02:20:36 +04:00
int err = 0 ;
struct xfrm_state * t = NULL ;
2006-09-28 05:47:24 +04:00
__be32 spi ;
2010-02-23 03:20:22 +03:00
u32 mark = x - > mark . m & x - > mark . v ;
2005-04-17 02:20:36 +04:00
2010-01-25 13:39:09 +03:00
spi = xfrm6_tunnel_spi_lookup ( net , ( xfrm_address_t * ) & x - > props . saddr ) ;
2005-04-17 02:20:36 +04:00
if ( spi )
2010-02-23 03:20:22 +03:00
t = xfrm_state_lookup ( net , mark , ( xfrm_address_t * ) & x - > id . daddr ,
2005-04-17 02:20:36 +04:00
spi , IPPROTO_IPV6 , AF_INET6 ) ;
if ( ! t ) {
t = ipcomp6_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 ;
}
2005-06-21 00:18:08 +04:00
static int ipcomp6_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
x - > props . header_len = 0 ;
2007-10-18 08:35:15 +04:00
switch ( x - > props . mode ) {
case XFRM_MODE_TRANSPORT :
break ;
case XFRM_MODE_TUNNEL :
2005-04-17 02:20:36 +04:00
x - > props . header_len + = sizeof ( struct ipv6hdr ) ;
2007-11-14 08:39:08 +03:00
break ;
2007-10-18 08:35:15 +04:00
default :
2007-11-14 08:39:08 +03:00
goto out ;
2007-10-18 08:35:15 +04:00
}
2007-02-09 17:24:49 +03:00
2008-07-25 13:54:40 +04:00
err = ipcomp_init_state ( x ) ;
if ( err )
2007-11-14 08:39:08 +03: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 = ipcomp6_tunnel_attach ( x ) ;
if ( err )
2010-02-15 22:24:30 +03:00
goto out ;
2005-04-17 02:20:36 +04:00
}
err = 0 ;
out :
return err ;
}
2014-03-14 10:28:07 +04:00
static int ipcomp6_rcv_cb ( struct sk_buff * skb , int err )
{
return 0 ;
}
2014-08-25 00:53:11 +04:00
static const struct xfrm_type ipcomp6_type = {
2005-04-17 02:20:36 +04:00
. description = " IPCOMP6 " ,
. owner = THIS_MODULE ,
. proto = IPPROTO_COMP ,
. init_state = ipcomp6_init_state ,
2008-07-25 13:54:40 +04:00
. destructor = ipcomp_destroy ,
. input = ipcomp_input ,
. output = ipcomp_output ,
2006-08-24 04:57:28 +04:00
. hdr_offset = xfrm6_find_1stfragopt ,
2005-04-17 02:20:36 +04:00
} ;
2014-08-25 00:53:11 +04:00
static struct xfrm6_protocol ipcomp6_protocol = {
2005-04-17 02:20:36 +04:00
. handler = xfrm6_rcv ,
2014-03-14 10:28:07 +04:00
. cb_handler = ipcomp6_rcv_cb ,
2005-04-17 02:20:36 +04:00
. err_handler = ipcomp6_err ,
2014-03-14 10:28:07 +04:00
. priority = 0 ,
2005-04-17 02:20:36 +04:00
} ;
static int __init ipcomp6_init ( void )
{
if ( xfrm_register_type ( & ipcomp6_type , AF_INET6 ) < 0 ) {
2012-05-15 18:11:53 +04:00
pr_info ( " %s: can't add xfrm type \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
return - EAGAIN ;
}
2014-03-14 10:28:07 +04:00
if ( xfrm6_protocol_register ( & ipcomp6_protocol , IPPROTO_COMP ) < 0 ) {
2012-05-15 18:11:53 +04:00
pr_info ( " %s: can't add protocol \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
xfrm_unregister_type ( & ipcomp6_type , AF_INET6 ) ;
return - EAGAIN ;
}
return 0 ;
}
static void __exit ipcomp6_fini ( void )
{
2014-03-14 10:28:07 +04:00
if ( xfrm6_protocol_deregister ( & ipcomp6_protocol , IPPROTO_COMP ) < 0 )
2012-05-15 18:11:53 +04:00
pr_info ( " %s: can't remove protocol \n " , __func__ ) ;
2019-05-03 18:46:19 +03:00
xfrm_unregister_type ( & ipcomp6_type , AF_INET6 ) ;
2005-04-17 02:20:36 +04:00
}
module_init ( ipcomp6_init ) ;
module_exit ( ipcomp6_fini ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_DESCRIPTION ( " IP Payload Compression Protocol (IPComp) for IPv6 - RFC3173 " ) ;
MODULE_AUTHOR ( " Mitsuru KANDA <mk@linux-ipv6.org> " ) ;
2007-06-27 10:57:49 +04:00
MODULE_ALIAS_XFRM_TYPE ( AF_INET6 , XFRM_PROTO_COMP ) ;