2006-10-04 10:47:05 +04:00
/*
* xfrm4_mode_beet . c - BEET mode encapsulation for IPv4 .
*
* Copyright ( c ) 2006 Diego Beltrami < diego . beltrami @ gmail . com >
* Miika Komu < miika @ iki . fi >
* Herbert Xu < herbert @ gondor . apana . org . au >
* Abhinav Pathak < abhinav . pathak @ hiit . fi >
* Jeff Ahrenholz < ahrenholz @ gmail . com >
*/
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/skbuff.h>
# include <linux/stringify.h>
# include <net/dst.h>
# include <net/ip.h>
# include <net/xfrm.h>
/* Add encapsulation header.
*
* The top IP header will be constructed per draft - nikander - esp - beet - mode - 06. txt .
* The following fields in it shall be filled in by x - > type - > output :
* tot_len
* check
*
* On exit , skb - > h will be set to the start of the payload to be processed
* by x - > type - > output and skb - > nh will be set to the top IP header .
*/
static int xfrm4_beet_output ( struct xfrm_state * x , struct sk_buff * skb )
{
struct iphdr * iph , * top_iph = NULL ;
int hdrlen , optlen ;
2007-04-21 09:47:35 +04:00
iph = ip_hdr ( skb ) ;
skb - > h . raw = skb - > nh . raw ;
2006-10-04 10:47:05 +04:00
hdrlen = 0 ;
optlen = iph - > ihl * 4 - sizeof ( * iph ) ;
if ( unlikely ( optlen ) )
hdrlen + = IPV4_BEET_PHMAXLEN - ( optlen & 4 ) ;
2007-04-11 07:46:21 +04:00
skb_push ( skb , x - > props . header_len + hdrlen ) ;
skb_reset_network_header ( skb ) ;
2007-04-21 09:47:35 +04:00
top_iph = ip_hdr ( skb ) ;
2007-04-06 02:54:02 +04:00
skb - > h . raw + = sizeof ( * iph ) - hdrlen ;
2006-10-04 10:47:05 +04:00
2007-04-06 02:54:02 +04:00
memmove ( top_iph , iph , sizeof ( * iph ) ) ;
2006-10-04 10:47:05 +04:00
if ( unlikely ( optlen ) ) {
struct ip_beet_phdr * ph ;
BUG_ON ( optlen < 0 ) ;
ph = ( struct ip_beet_phdr * ) skb - > h . raw ;
ph - > padlen = 4 - ( optlen & 4 ) ;
2007-04-24 09:39:02 +04:00
ph - > hdrlen = optlen / 8 ;
2006-10-04 10:47:05 +04:00
ph - > nexthdr = top_iph - > protocol ;
2007-04-06 02:54:39 +04:00
if ( ph - > padlen )
memset ( ph + 1 , IPOPT_NOP , ph - > padlen ) ;
2006-10-04 10:47:05 +04:00
top_iph - > protocol = IPPROTO_BEETPH ;
top_iph - > ihl = sizeof ( struct iphdr ) / 4 ;
}
top_iph - > saddr = x - > props . saddr . a4 ;
top_iph - > daddr = x - > id . daddr . a4 ;
return 0 ;
}
static int xfrm4_beet_input ( struct xfrm_state * x , struct sk_buff * skb )
{
2007-04-21 09:47:35 +04:00
struct iphdr * iph = ip_hdr ( skb ) ;
2006-10-04 10:47:05 +04:00
int phlen = 0 ;
int optlen = 0 ;
__u8 ph_nexthdr = 0 , protocol = 0 ;
int err = - EINVAL ;
protocol = iph - > protocol ;
if ( unlikely ( iph - > protocol = = IPPROTO_BEETPH ) ) {
2007-04-06 03:03:33 +04:00
struct ip_beet_phdr * ph ;
2006-10-04 10:47:05 +04:00
if ( ! pskb_may_pull ( skb , sizeof ( * ph ) ) )
goto out ;
2007-04-26 05:02:22 +04:00
ph = ( struct ip_beet_phdr * ) ( ipip_hdr ( skb ) + 1 ) ;
2006-10-04 10:47:05 +04:00
2007-04-06 02:59:41 +04:00
phlen = sizeof ( * ph ) + ph - > padlen ;
2007-04-24 09:39:02 +04:00
optlen = ph - > hdrlen * 8 + ( IPV4_BEET_PHMAXLEN - phlen ) ;
2006-10-04 10:47:05 +04:00
if ( optlen < 0 | | optlen & 3 | | optlen > 250 )
goto out ;
2007-04-06 02:59:41 +04:00
if ( ! pskb_may_pull ( skb , phlen + optlen ) )
2006-10-04 10:47:05 +04:00
goto out ;
2007-04-06 03:03:33 +04:00
skb - > len - = phlen + optlen ;
2006-10-04 10:47:05 +04:00
ph_nexthdr = ph - > nexthdr ;
}
2007-04-26 05:02:22 +04:00
skb_set_network_header ( skb , phlen - sizeof ( * iph ) ) ;
2007-04-11 07:50:43 +04:00
memmove ( skb_network_header ( skb ) , iph , sizeof ( * iph ) ) ;
2007-04-26 05:02:22 +04:00
skb_set_transport_header ( skb , phlen + optlen ) ;
2007-04-06 03:03:33 +04:00
skb - > data = skb - > h . raw ;
2006-10-04 10:47:05 +04:00
2007-04-21 09:47:35 +04:00
iph = ip_hdr ( skb ) ;
2006-10-04 10:47:05 +04:00
iph - > ihl = ( sizeof ( * iph ) + optlen ) / 4 ;
2007-04-06 02:59:41 +04:00
iph - > tot_len = htons ( skb - > len + iph - > ihl * 4 ) ;
2006-10-04 10:47:05 +04:00
iph - > daddr = x - > sel . daddr . a4 ;
iph - > saddr = x - > sel . saddr . a4 ;
if ( ph_nexthdr )
iph - > protocol = ph_nexthdr ;
else
iph - > protocol = protocol ;
iph - > check = 0 ;
2007-04-11 07:50:43 +04:00
iph - > check = ip_fast_csum ( skb_network_header ( skb ) , iph - > ihl ) ;
2006-10-04 10:47:05 +04:00
err = 0 ;
out :
return err ;
}
static struct xfrm_mode xfrm4_beet_mode = {
. input = xfrm4_beet_input ,
. output = xfrm4_beet_output ,
. owner = THIS_MODULE ,
. encap = XFRM_MODE_BEET ,
} ;
static int __init xfrm4_beet_init ( void )
{
return xfrm_register_mode ( & xfrm4_beet_mode , AF_INET ) ;
}
static void __exit xfrm4_beet_exit ( void )
{
int err ;
err = xfrm_unregister_mode ( & xfrm4_beet_mode , AF_INET ) ;
BUG_ON ( err ) ;
}
module_init ( xfrm4_beet_init ) ;
module_exit ( xfrm4_beet_exit ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS_XFRM_MODE ( AF_INET , XFRM_MODE_BEET ) ;