2019-05-19 16:51:43 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2006-08-24 07:31:11 +04:00
/*
* Copyright ( C ) 2003 - 2006 Helsinki University of Technology
* Copyright ( C ) 2003 - 2006 USAGI / WIDE Project
*/
/*
* Authors :
* Noriaki TAKAMIYA @ USAGI
* Masahide NAKAMURA @ USAGI
*/
2012-05-15 18:11:53 +04:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2006-08-24 07:31:11 +04:00
# include <linux/module.h>
# include <linux/skbuff.h>
2006-08-24 07:45:55 +04:00
# include <linux/time.h>
2006-08-24 07:31:11 +04:00
# include <linux/ipv6.h>
2006-08-24 07:35:31 +04:00
# include <linux/icmpv6.h>
# include <net/sock.h>
2006-08-24 07:31:11 +04:00
# include <net/ipv6.h>
2006-08-24 07:35:31 +04:00
# include <net/ip6_checksum.h>
2007-06-27 10:56:32 +04:00
# include <net/rawv6.h>
2006-08-24 07:31:11 +04:00
# include <net/xfrm.h>
# include <net/mip6.h>
2006-08-24 07:32:34 +04:00
static inline unsigned int calc_padlen ( unsigned int len , unsigned int n )
{
return ( n - len + 16 ) & 0x7 ;
}
static inline void * mip6_padn ( __u8 * data , __u8 padlen )
{
if ( ! data )
return NULL ;
if ( padlen = = 1 ) {
2012-05-17 10:00:25 +04:00
data [ 0 ] = IPV6_TLV_PAD1 ;
2006-08-24 07:32:34 +04:00
} else if ( padlen > 1 ) {
2008-04-10 10:42:12 +04:00
data [ 0 ] = IPV6_TLV_PADN ;
2006-08-24 07:32:34 +04:00
data [ 1 ] = padlen - 2 ;
if ( padlen > 2 )
memset ( data + 2 , 0 , data [ 1 ] ) ;
}
return data + padlen ;
}
2009-06-23 15:31:07 +04:00
static inline void mip6_param_prob ( struct sk_buff * skb , u8 code , int pos )
2006-08-24 07:35:31 +04:00
{
2010-02-18 11:25:24 +03:00
icmpv6_send ( skb , ICMPV6_PARAMPROB , code , pos ) ;
2006-08-24 07:35:31 +04:00
}
static int mip6_mh_len ( int type )
{
int len = 0 ;
switch ( type ) {
case IP6_MH_TYPE_BRR :
len = 0 ;
break ;
case IP6_MH_TYPE_HOTI :
case IP6_MH_TYPE_COTI :
case IP6_MH_TYPE_BU :
case IP6_MH_TYPE_BACK :
len = 1 ;
break ;
case IP6_MH_TYPE_HOT :
case IP6_MH_TYPE_COT :
case IP6_MH_TYPE_BERROR :
len = 2 ;
break ;
}
return len ;
}
2007-06-27 10:56:32 +04:00
static int mip6_mh_filter ( struct sock * sk , struct sk_buff * skb )
2006-08-24 07:35:31 +04:00
{
2012-09-26 00:01:28 +04:00
struct ip6_mh _hdr ;
const struct ip6_mh * mh ;
2006-08-24 07:35:31 +04:00
2012-09-26 00:01:28 +04:00
mh = skb_header_pointer ( skb , skb_transport_offset ( skb ) ,
sizeof ( _hdr ) , & _hdr ) ;
if ( ! mh )
2006-08-24 07:35:31 +04:00
return - 1 ;
2012-09-26 00:01:28 +04:00
if ( ( ( mh - > ip6mh_hdrlen + 1 ) < < 3 ) > skb - > len )
return - 1 ;
2006-08-24 07:35:31 +04:00
if ( mh - > ip6mh_hdrlen < mip6_mh_len ( mh - > ip6mh_type ) ) {
2014-11-11 21:59:17 +03:00
net_dbg_ratelimited ( " mip6: MH message too short: %d vs >=%d \n " ,
mh - > ip6mh_hdrlen ,
mip6_mh_len ( mh - > ip6mh_type ) ) ;
2012-09-26 00:01:28 +04:00
mip6_param_prob ( skb , 0 , offsetof ( struct ip6_mh , ip6mh_hdrlen ) +
skb_network_header_len ( skb ) ) ;
2006-08-24 07:35:31 +04:00
return - 1 ;
}
if ( mh - > ip6mh_proto ! = IPPROTO_NONE ) {
2014-11-11 21:59:17 +03:00
net_dbg_ratelimited ( " mip6: MH invalid payload proto = %d \n " ,
mh - > ip6mh_proto ) ;
2012-09-26 00:01:28 +04:00
mip6_param_prob ( skb , 0 , offsetof ( struct ip6_mh , ip6mh_proto ) +
skb_network_header_len ( skb ) ) ;
2006-08-24 07:35:31 +04:00
return - 1 ;
}
return 0 ;
}
2006-08-24 07:45:55 +04:00
struct mip6_report_rate_limiter {
spinlock_t lock ;
2015-09-30 14:26:39 +03:00
ktime_t stamp ;
2006-08-24 07:45:55 +04:00
int iif ;
struct in6_addr src ;
struct in6_addr dst ;
} ;
static struct mip6_report_rate_limiter mip6_report_rl = {
2007-04-26 12:37:44 +04:00
. lock = __SPIN_LOCK_UNLOCKED ( mip6_report_rl . lock )
2006-08-24 07:45:55 +04:00
} ;
2006-08-24 07:32:34 +04:00
static int mip6_destopt_input ( struct xfrm_state * x , struct sk_buff * skb )
{
2011-04-22 08:53:02 +04:00
const struct ipv6hdr * iph = ipv6_hdr ( skb ) ;
2006-08-24 07:32:34 +04:00
struct ipv6_destopt_hdr * destopt = ( struct ipv6_destopt_hdr * ) skb - > data ;
2007-11-14 08:45:58 +03:00
int err = destopt - > nexthdr ;
2006-08-24 07:32:34 +04:00
2007-11-14 08:45:58 +03:00
spin_lock ( & x - > lock ) ;
2006-08-24 07:32:34 +04:00
if ( ! ipv6_addr_equal ( & iph - > saddr , ( struct in6_addr * ) x - > coaddr ) & &
! ipv6_addr_any ( ( struct in6_addr * ) x - > coaddr ) )
2007-11-14 08:45:58 +03:00
err = - ENOENT ;
spin_unlock ( & x - > lock ) ;
2006-08-24 07:32:34 +04:00
2007-11-14 08:45:58 +03:00
return err ;
2006-08-24 07:32:34 +04:00
}
/* Destination Option Header is inserted.
* IP Header ' s src address is replaced with Home Address Option in
* Destination Option Header .
*/
static int mip6_destopt_output ( struct xfrm_state * x , struct sk_buff * skb )
{
struct ipv6hdr * iph ;
struct ipv6_destopt_hdr * dstopt ;
struct ipv6_destopt_hao * hao ;
u8 nexthdr ;
int len ;
2007-10-11 02:44:06 +04:00
skb_push ( skb , - skb_network_offset ( skb ) ) ;
2007-10-10 00:25:59 +04:00
iph = ipv6_hdr ( skb ) ;
2006-08-24 07:32:34 +04:00
2007-10-10 00:25:59 +04:00
nexthdr = * skb_mac_header ( skb ) ;
* skb_mac_header ( skb ) = IPPROTO_DSTOPTS ;
2006-08-24 07:32:34 +04:00
2007-04-26 05:04:18 +04:00
dstopt = ( struct ipv6_destopt_hdr * ) skb_transport_header ( skb ) ;
2006-08-24 07:32:34 +04:00
dstopt - > nexthdr = nexthdr ;
hao = mip6_padn ( ( char * ) ( dstopt + 1 ) ,
calc_padlen ( sizeof ( * dstopt ) , 6 ) ) ;
hao - > type = IPV6_TLV_HAO ;
2008-07-26 08:43:18 +04:00
BUILD_BUG_ON ( sizeof ( * hao ) ! = 18 ) ;
2006-08-24 07:32:34 +04:00
hao - > length = sizeof ( * hao ) - 2 ;
len = ( ( char * ) hao - ( char * ) dstopt ) + sizeof ( * hao ) ;
memcpy ( & hao - > addr , & iph - > saddr , sizeof ( hao - > addr ) ) ;
2007-10-10 00:33:35 +04:00
spin_lock_bh ( & x - > lock ) ;
2006-08-24 07:32:34 +04:00
memcpy ( & iph - > saddr , x - > coaddr , sizeof ( iph - > saddr ) ) ;
2007-10-10 00:33:35 +04:00
spin_unlock_bh ( & x - > lock ) ;
2006-08-24 07:32:34 +04:00
2008-07-26 08:43:18 +04:00
WARN_ON ( len ! = x - > props . header_len ) ;
2006-08-24 07:32:34 +04:00
dstopt - > hdrlen = ( x - > props . header_len > > 3 ) - 1 ;
return 0 ;
}
2015-09-30 14:26:39 +03:00
static inline int mip6_report_rl_allow ( ktime_t stamp ,
2011-04-22 08:53:02 +04:00
const struct in6_addr * dst ,
const struct in6_addr * src , int iif )
2006-08-24 07:45:55 +04:00
{
int allow = 0 ;
spin_lock_bh ( & mip6_report_rl . lock ) ;
2016-12-25 14:43:07 +03:00
if ( mip6_report_rl . stamp ! = stamp | |
2006-08-24 07:45:55 +04:00
mip6_report_rl . iif ! = iif | |
! ipv6_addr_equal ( & mip6_report_rl . src , src ) | |
! ipv6_addr_equal ( & mip6_report_rl . dst , dst ) ) {
2015-09-30 14:26:39 +03:00
mip6_report_rl . stamp = stamp ;
2006-08-24 07:45:55 +04:00
mip6_report_rl . iif = iif ;
2011-11-21 07:39:03 +04:00
mip6_report_rl . src = * src ;
mip6_report_rl . dst = * dst ;
2006-08-24 07:45:55 +04:00
allow = 1 ;
}
spin_unlock_bh ( & mip6_report_rl . lock ) ;
return allow ;
}
2011-02-23 04:59:59 +03:00
static int mip6_destopt_reject ( struct xfrm_state * x , struct sk_buff * skb ,
const struct flowi * fl )
2006-08-24 07:45:55 +04:00
{
2008-11-26 04:51:01 +03:00
struct net * net = xs_net ( x ) ;
2006-08-24 07:45:55 +04:00
struct inet6_skb_parm * opt = ( struct inet6_skb_parm * ) skb - > cb ;
2011-03-13 00:22:43 +03:00
const struct flowi6 * fl6 = & fl - > u . ip6 ;
2006-08-24 07:45:55 +04:00
struct ipv6_destopt_hao * hao = NULL ;
struct xfrm_selector sel ;
int offset ;
2015-09-30 14:26:39 +03:00
ktime_t stamp ;
2006-08-24 07:45:55 +04:00
int err = 0 ;
2011-03-13 00:22:43 +03:00
if ( unlikely ( fl6 - > flowi6_proto = = IPPROTO_MH & &
2011-03-13 00:36:19 +03:00
fl6 - > fl6_mh_type < = IP6_MH_TYPE_MAX ) )
2006-08-24 07:47:44 +04:00
goto out ;
2006-08-24 07:45:55 +04:00
if ( likely ( opt - > dsthao ) ) {
offset = ipv6_find_tlv ( skb , opt - > dsthao , IPV6_TLV_HAO ) ;
if ( likely ( offset > = 0 ) )
2007-04-11 07:50:43 +04:00
hao = ( struct ipv6_destopt_hao * )
( skb_network_header ( skb ) + offset ) ;
2006-08-24 07:45:55 +04:00
}
2015-09-30 14:26:39 +03:00
stamp = skb_get_ktime ( skb ) ;
2006-08-24 07:45:55 +04:00
2015-09-30 14:26:39 +03:00
if ( ! mip6_report_rl_allow ( stamp , & ipv6_hdr ( skb ) - > daddr ,
2007-04-26 04:54:47 +04:00
hao ? & hao - > addr : & ipv6_hdr ( skb ) - > saddr ,
2006-08-24 07:45:55 +04:00
opt - > iif ) )
goto out ;
memset ( & sel , 0 , sizeof ( sel ) ) ;
2007-04-26 04:54:47 +04:00
memcpy ( & sel . daddr , ( xfrm_address_t * ) & ipv6_hdr ( skb ) - > daddr ,
2006-08-24 07:45:55 +04:00
sizeof ( sel . daddr ) ) ;
sel . prefixlen_d = 128 ;
2007-04-26 04:54:47 +04:00
memcpy ( & sel . saddr , ( xfrm_address_t * ) & ipv6_hdr ( skb ) - > saddr ,
2006-08-24 07:45:55 +04:00
sizeof ( sel . saddr ) ) ;
sel . prefixlen_s = 128 ;
sel . family = AF_INET6 ;
2011-03-13 00:22:43 +03:00
sel . proto = fl6 - > flowi6_proto ;
sel . dport = xfrm_flowi_dport ( fl , & fl6 - > uli ) ;
2006-08-24 07:45:55 +04:00
if ( sel . dport )
2006-11-15 07:56:00 +03:00
sel . dport_mask = htons ( ~ 0 ) ;
2011-03-13 00:22:43 +03:00
sel . sport = xfrm_flowi_sport ( fl , & fl6 - > uli ) ;
2006-08-24 07:45:55 +04:00
if ( sel . sport )
2006-11-15 07:56:00 +03:00
sel . sport_mask = htons ( ~ 0 ) ;
2011-03-13 00:22:43 +03:00
sel . ifindex = fl6 - > flowi6_oif ;
2006-08-24 07:45:55 +04:00
2008-11-26 04:51:01 +03:00
err = km_report ( net , IPPROTO_DSTOPTS , & sel ,
2006-08-24 07:45:55 +04:00
( hao ? ( xfrm_address_t * ) & hao - > addr : NULL ) ) ;
out :
return err ;
}
2006-08-24 07:32:34 +04:00
static int mip6_destopt_init_state ( struct xfrm_state * x )
{
if ( x - > id . spi ) {
2012-05-15 18:11:53 +04:00
pr_info ( " %s: spi is not 0: %u \n " , __func__ , x - > id . spi ) ;
2006-08-24 07:32:34 +04:00
return - EINVAL ;
}
if ( x - > props . mode ! = XFRM_MODE_ROUTEOPTIMIZATION ) {
2012-05-15 18:11:53 +04:00
pr_info ( " %s: state's mode is not %u: %u \n " ,
__func__ , XFRM_MODE_ROUTEOPTIMIZATION , x - > props . mode ) ;
2006-08-24 07:32:34 +04:00
return - EINVAL ;
}
x - > props . header_len = sizeof ( struct ipv6_destopt_hdr ) +
calc_padlen ( sizeof ( struct ipv6_destopt_hdr ) , 6 ) +
sizeof ( struct ipv6_destopt_hao ) ;
2008-07-26 08:43:18 +04:00
WARN_ON ( x - > props . header_len ! = 24 ) ;
2006-08-24 07:32:34 +04:00
return 0 ;
}
/*
* Do nothing about destroying since it has no specific operation for
* destination options header unlike IPsec protocols .
*/
static void mip6_destopt_destroy ( struct xfrm_state * x )
{
}
2014-08-25 00:53:11 +04:00
static const struct xfrm_type mip6_destopt_type = {
2006-08-24 07:32:34 +04:00
. owner = THIS_MODULE ,
2014-08-25 00:53:11 +04:00
. proto = IPPROTO_DSTOPTS ,
2007-11-14 08:36:51 +03:00
. flags = XFRM_TYPE_NON_FRAGMENT | XFRM_TYPE_LOCAL_COADDR ,
2006-08-24 07:32:34 +04:00
. init_state = mip6_destopt_init_state ,
. destructor = mip6_destopt_destroy ,
. input = mip6_destopt_input ,
. output = mip6_destopt_output ,
2007-02-09 17:24:49 +03:00
. reject = mip6_destopt_reject ,
2006-08-24 07:32:34 +04:00
} ;
2006-08-24 07:31:11 +04:00
static int mip6_rthdr_input ( struct xfrm_state * x , struct sk_buff * skb )
{
2011-04-22 08:53:02 +04:00
const struct ipv6hdr * iph = ipv6_hdr ( skb ) ;
2006-08-24 07:31:11 +04:00
struct rt2_hdr * rt2 = ( struct rt2_hdr * ) skb - > data ;
2007-11-14 08:45:58 +03:00
int err = rt2 - > rt_hdr . nexthdr ;
2006-08-24 07:31:11 +04:00
2007-11-14 08:45:58 +03:00
spin_lock ( & x - > lock ) ;
2010-07-16 04:38:44 +04:00
if ( ! ipv6_addr_equal ( & iph - > daddr , ( struct in6_addr * ) x - > coaddr ) & &
2006-08-24 07:31:11 +04:00
! ipv6_addr_any ( ( struct in6_addr * ) x - > coaddr ) )
2007-11-14 08:45:58 +03:00
err = - ENOENT ;
spin_unlock ( & x - > lock ) ;
2006-08-24 07:31:11 +04:00
2007-11-14 08:45:58 +03:00
return err ;
2006-08-24 07:31:11 +04:00
}
/* Routing Header type 2 is inserted.
* IP Header ' s dst address is replaced with Routing Header ' s Home Address .
*/
static int mip6_rthdr_output ( struct xfrm_state * x , struct sk_buff * skb )
{
struct ipv6hdr * iph ;
struct rt2_hdr * rt2 ;
u8 nexthdr ;
2007-10-11 02:44:06 +04:00
skb_push ( skb , - skb_network_offset ( skb ) ) ;
2007-10-10 00:25:59 +04:00
iph = ipv6_hdr ( skb ) ;
2006-08-24 07:31:11 +04:00
2007-10-10 00:25:59 +04:00
nexthdr = * skb_mac_header ( skb ) ;
* skb_mac_header ( skb ) = IPPROTO_ROUTING ;
2006-08-24 07:31:11 +04:00
2007-04-26 05:04:18 +04:00
rt2 = ( struct rt2_hdr * ) skb_transport_header ( skb ) ;
2006-08-24 07:31:11 +04:00
rt2 - > rt_hdr . nexthdr = nexthdr ;
rt2 - > rt_hdr . hdrlen = ( x - > props . header_len > > 3 ) - 1 ;
rt2 - > rt_hdr . type = IPV6_SRCRT_TYPE_2 ;
rt2 - > rt_hdr . segments_left = 1 ;
memset ( & rt2 - > reserved , 0 , sizeof ( rt2 - > reserved ) ) ;
2008-07-26 08:43:18 +04:00
WARN_ON ( rt2 - > rt_hdr . hdrlen ! = 2 ) ;
2006-08-24 07:31:11 +04:00
memcpy ( & rt2 - > addr , & iph - > daddr , sizeof ( rt2 - > addr ) ) ;
2007-10-10 00:33:35 +04:00
spin_lock_bh ( & x - > lock ) ;
2006-08-24 07:31:11 +04:00
memcpy ( & iph - > daddr , x - > coaddr , sizeof ( iph - > daddr ) ) ;
2007-10-10 00:33:35 +04:00
spin_unlock_bh ( & x - > lock ) ;
2006-08-24 07:31:11 +04:00
return 0 ;
}
static int mip6_rthdr_init_state ( struct xfrm_state * x )
{
if ( x - > id . spi ) {
2012-05-15 18:11:53 +04:00
pr_info ( " %s: spi is not 0: %u \n " , __func__ , x - > id . spi ) ;
2006-08-24 07:31:11 +04:00
return - EINVAL ;
}
if ( x - > props . mode ! = XFRM_MODE_ROUTEOPTIMIZATION ) {
2012-05-15 18:11:53 +04:00
pr_info ( " %s: state's mode is not %u: %u \n " ,
__func__ , XFRM_MODE_ROUTEOPTIMIZATION , x - > props . mode ) ;
2006-08-24 07:31:11 +04:00
return - EINVAL ;
}
x - > props . header_len = sizeof ( struct rt2_hdr ) ;
return 0 ;
}
/*
* Do nothing about destroying since it has no specific operation for routing
* header type 2 unlike IPsec protocols .
*/
static void mip6_rthdr_destroy ( struct xfrm_state * x )
{
}
2014-08-25 00:53:11 +04:00
static const struct xfrm_type mip6_rthdr_type = {
2006-08-24 07:31:11 +04:00
. owner = THIS_MODULE ,
2014-08-25 00:53:11 +04:00
. proto = IPPROTO_ROUTING ,
2007-11-14 08:36:51 +03:00
. flags = XFRM_TYPE_NON_FRAGMENT | XFRM_TYPE_REMOTE_COADDR ,
2006-08-24 07:31:11 +04:00
. init_state = mip6_rthdr_init_state ,
. destructor = mip6_rthdr_destroy ,
. input = mip6_rthdr_input ,
. output = mip6_rthdr_output ,
} ;
2007-06-27 10:56:32 +04:00
static int __init mip6_init ( void )
2006-08-24 07:31:11 +04:00
{
2012-05-15 18:11:53 +04:00
pr_info ( " Mobile IPv6 \n " ) ;
2006-08-24 07:31:11 +04:00
2006-08-24 07:32:34 +04:00
if ( xfrm_register_type ( & mip6_destopt_type , AF_INET6 ) < 0 ) {
2012-05-15 18:11:53 +04:00
pr_info ( " %s: can't add xfrm type(destopt) \n " , __func__ ) ;
2006-08-24 07:32:34 +04:00
goto mip6_destopt_xfrm_fail ;
}
2006-08-24 07:31:11 +04:00
if ( xfrm_register_type ( & mip6_rthdr_type , AF_INET6 ) < 0 ) {
2012-05-15 18:11:53 +04:00
pr_info ( " %s: can't add xfrm type(rthdr) \n " , __func__ ) ;
2006-08-24 07:31:11 +04:00
goto mip6_rthdr_xfrm_fail ;
}
2007-06-27 10:56:32 +04:00
if ( rawv6_mh_filter_register ( mip6_mh_filter ) < 0 ) {
2012-05-15 18:11:53 +04:00
pr_info ( " %s: can't add rawv6 mh filter \n " , __func__ ) ;
2007-06-27 10:56:32 +04:00
goto mip6_rawv6_mh_fail ;
}
2006-08-24 07:31:11 +04:00
return 0 ;
2007-06-27 10:56:32 +04:00
mip6_rawv6_mh_fail :
xfrm_unregister_type ( & mip6_rthdr_type , AF_INET6 ) ;
2006-08-24 07:31:11 +04:00
mip6_rthdr_xfrm_fail :
2006-08-24 07:32:34 +04:00
xfrm_unregister_type ( & mip6_destopt_type , AF_INET6 ) ;
mip6_destopt_xfrm_fail :
2006-08-24 07:31:11 +04:00
return - EAGAIN ;
}
2007-06-27 10:56:32 +04:00
static void __exit mip6_fini ( void )
2006-08-24 07:31:11 +04:00
{
2007-06-27 10:56:32 +04:00
if ( rawv6_mh_filter_unregister ( mip6_mh_filter ) < 0 )
2012-05-15 18:11:53 +04:00
pr_info ( " %s: can't remove rawv6 mh filter \n " , __func__ ) ;
2019-05-03 18:46:19 +03:00
xfrm_unregister_type ( & mip6_rthdr_type , AF_INET6 ) ;
xfrm_unregister_type ( & mip6_destopt_type , AF_INET6 ) ;
2006-08-24 07:31:11 +04:00
}
2007-06-27 10:56:32 +04:00
module_init ( mip6_init ) ;
module_exit ( mip6_fini ) ;
MODULE_LICENSE ( " GPL " ) ;
2007-06-27 10:57:49 +04:00
MODULE_ALIAS_XFRM_TYPE ( AF_INET6 , XFRM_PROTO_DSTOPTS ) ;
MODULE_ALIAS_XFRM_TYPE ( AF_INET6 , XFRM_PROTO_ROUTING ) ;