2016-05-18 19:06:18 +03:00
# include <linux/module.h>
# include <linux/errno.h>
# include <linux/socket.h>
# include <linux/skbuff.h>
# include <linux/ip.h>
# include <linux/udp.h>
2018-11-08 14:19:23 +03:00
# include <linux/icmpv6.h>
2016-05-18 19:06:18 +03:00
# include <linux/types.h>
# include <linux/kernel.h>
# include <net/fou.h>
# include <net/ip.h>
# include <net/ip6_tunnel.h>
# include <net/ip6_checksum.h>
# include <net/protocol.h>
# include <net/udp.h>
# include <net/udp_tunnel.h>
2017-05-19 19:55:54 +03:00
# if IS_ENABLED(CONFIG_IPV6_FOU_TUNNEL)
2016-05-18 19:06:18 +03:00
static void fou6_build_udp ( struct sk_buff * skb , struct ip_tunnel_encap * e ,
struct flowi6 * fl6 , u8 * protocol , __be16 sport )
{
struct udphdr * uh ;
skb_push ( skb , sizeof ( struct udphdr ) ) ;
skb_reset_transport_header ( skb ) ;
uh = udp_hdr ( skb ) ;
uh - > dest = e - > dport ;
uh - > source = sport ;
uh - > len = htons ( skb - > len ) ;
udp6_set_csum ( ! ( e - > flags & TUNNEL_ENCAP_FLAG_CSUM6 ) , skb ,
& fl6 - > saddr , & fl6 - > daddr , skb - > len ) ;
* protocol = IPPROTO_UDP ;
}
2017-05-19 19:55:54 +03:00
static int fou6_build_header ( struct sk_buff * skb , struct ip_tunnel_encap * e ,
u8 * protocol , struct flowi6 * fl6 )
2016-05-18 19:06:18 +03:00
{
__be16 sport ;
int err ;
int type = e - > flags & TUNNEL_ENCAP_FLAG_CSUM6 ?
SKB_GSO_UDP_TUNNEL_CSUM : SKB_GSO_UDP_TUNNEL ;
err = __fou_build_header ( skb , e , protocol , & sport , type ) ;
if ( err )
return err ;
fou6_build_udp ( skb , e , fl6 , protocol , sport ) ;
return 0 ;
}
2017-05-19 19:55:54 +03:00
static int gue6_build_header ( struct sk_buff * skb , struct ip_tunnel_encap * e ,
u8 * protocol , struct flowi6 * fl6 )
2016-05-18 19:06:18 +03:00
{
__be16 sport ;
int err ;
int type = e - > flags & TUNNEL_ENCAP_FLAG_CSUM6 ?
SKB_GSO_UDP_TUNNEL_CSUM : SKB_GSO_UDP_TUNNEL ;
err = __gue_build_header ( skb , e , protocol , & sport , type ) ;
if ( err )
return err ;
fou6_build_udp ( skb , e , fl6 , protocol , sport ) ;
return 0 ;
}
2018-11-08 14:19:23 +03:00
static int gue6_err_proto_handler ( int proto , struct sk_buff * skb ,
struct inet6_skb_parm * opt ,
u8 type , u8 code , int offset , u32 info )
{
const struct inet6_protocol * ipprot ;
ipprot = rcu_dereference ( inet6_protos [ proto ] ) ;
if ( ipprot & & ipprot - > err_handler ) {
if ( ! ipprot - > err_handler ( skb , opt , type , code , offset , info ) )
return 0 ;
}
return - ENOENT ;
}
static int gue6_err ( struct sk_buff * skb , struct inet6_skb_parm * opt ,
u8 type , u8 code , int offset , __be32 info )
{
int transport_offset = skb_transport_offset ( skb ) ;
struct guehdr * guehdr ;
2019-01-11 17:27:35 +03:00
size_t len , optlen ;
2018-11-08 14:19:23 +03:00
int ret ;
2019-01-11 17:27:35 +03:00
len = sizeof ( struct udphdr ) + sizeof ( struct guehdr ) ;
if ( ! pskb_may_pull ( skb , len ) )
2018-11-08 14:19:23 +03:00
return - EINVAL ;
guehdr = ( struct guehdr * ) & udp_hdr ( skb ) [ 1 ] ;
switch ( guehdr - > version ) {
case 0 : /* Full GUE header present */
break ;
case 1 : {
/* Direct encasulation of IPv4 or IPv6 */
skb_set_transport_header ( skb , - ( int ) sizeof ( struct icmp6hdr ) ) ;
switch ( ( ( struct iphdr * ) guehdr ) - > version ) {
case 4 :
ret = gue6_err_proto_handler ( IPPROTO_IPIP , skb , opt ,
type , code , offset , info ) ;
goto out ;
case 6 :
ret = gue6_err_proto_handler ( IPPROTO_IPV6 , skb , opt ,
type , code , offset , info ) ;
goto out ;
default :
ret = - EOPNOTSUPP ;
goto out ;
}
}
default : /* Undefined version */
return - EOPNOTSUPP ;
}
if ( guehdr - > control )
return - ENOENT ;
optlen = guehdr - > hlen < < 2 ;
2019-01-11 17:27:35 +03:00
if ( ! pskb_may_pull ( skb , len + optlen ) )
return - EINVAL ;
guehdr = ( struct guehdr * ) & udp_hdr ( skb ) [ 1 ] ;
2018-11-08 14:19:23 +03:00
if ( validate_gue_flags ( guehdr , optlen ) )
return - EINVAL ;
2019-01-03 23:43:35 +03:00
/* Handling exceptions for direct UDP encapsulation in GUE would lead to
* recursion . Besides , this kind of encapsulation can ' t even be
* configured currently . Discard this .
*/
if ( guehdr - > proto_ctype = = IPPROTO_UDP | |
guehdr - > proto_ctype = = IPPROTO_UDPLITE )
return - EOPNOTSUPP ;
2018-11-08 14:19:23 +03:00
skb_set_transport_header ( skb , - ( int ) sizeof ( struct icmp6hdr ) ) ;
ret = gue6_err_proto_handler ( guehdr - > proto_ctype , skb ,
opt , type , code , offset , info ) ;
out :
skb_set_transport_header ( skb , transport_offset ) ;
return ret ;
}
2016-05-18 19:06:18 +03:00
static const struct ip6_tnl_encap_ops fou_ip6tun_ops = {
. encap_hlen = fou_encap_hlen ,
. build_header = fou6_build_header ,
2018-11-08 14:19:23 +03:00
. err_handler = gue6_err ,
2016-05-18 19:06:18 +03:00
} ;
static const struct ip6_tnl_encap_ops gue_ip6tun_ops = {
. encap_hlen = gue_encap_hlen ,
. build_header = gue6_build_header ,
2018-11-08 14:19:23 +03:00
. err_handler = gue6_err ,
2016-05-18 19:06:18 +03:00
} ;
static int ip6_tnl_encap_add_fou_ops ( void )
{
int ret ;
ret = ip6_tnl_encap_add_ops ( & fou_ip6tun_ops , TUNNEL_ENCAP_FOU ) ;
if ( ret < 0 ) {
pr_err ( " can't add fou6 ops \n " ) ;
return ret ;
}
ret = ip6_tnl_encap_add_ops ( & gue_ip6tun_ops , TUNNEL_ENCAP_GUE ) ;
if ( ret < 0 ) {
pr_err ( " can't add gue6 ops \n " ) ;
ip6_tnl_encap_del_ops ( & fou_ip6tun_ops , TUNNEL_ENCAP_FOU ) ;
return ret ;
}
return 0 ;
}
static void ip6_tnl_encap_del_fou_ops ( void )
{
ip6_tnl_encap_del_ops ( & fou_ip6tun_ops , TUNNEL_ENCAP_FOU ) ;
ip6_tnl_encap_del_ops ( & gue_ip6tun_ops , TUNNEL_ENCAP_GUE ) ;
}
# else
static int ip6_tnl_encap_add_fou_ops ( void )
{
return 0 ;
}
static void ip6_tnl_encap_del_fou_ops ( void )
{
}
# endif
static int __init fou6_init ( void )
{
int ret ;
ret = ip6_tnl_encap_add_fou_ops ( ) ;
return ret ;
}
static void __exit fou6_fini ( void )
{
ip6_tnl_encap_del_fou_ops ( ) ;
}
module_init ( fou6_init ) ;
module_exit ( fou6_fini ) ;
MODULE_AUTHOR ( " Tom Herbert <therbert@google.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;