From b7ebebcdd7d6513fee9b3f181c47bb5b11a08297 Mon Sep 17 00:00:00 2001 From: Pravin B Shelar Date: Thu, 7 Jul 2016 21:51:17 -0700 Subject: [PATCH] datapath: compat: Update udp_sock_create Update udp-socket-create to create ipv6 socket currectly. Partially backports commit fd384412e199b ("udp_tunnel: Seperate ipv6 functions into its own file.") Signed-off-by: Pravin B Shelar Acked-by: Jesse Gross --- acinclude.m4 | 2 + datapath/linux/Modules.mk | 1 + datapath/linux/compat/include/linux/udp.h | 33 +++++ datapath/linux/compat/include/net/udp.h | 1 - .../linux/compat/include/net/udp_tunnel.h | 30 ++++- datapath/linux/compat/udp.c | 12 ++ datapath/linux/compat/udp_tunnel.c | 123 ++++++++++-------- datapath/linux/compat/vxlan.c | 7 + 8 files changed, 155 insertions(+), 54 deletions(-) create mode 100644 datapath/linux/compat/include/linux/udp.h diff --git a/acinclude.m4 b/acinclude.m4 index 4bb65c720..003ba72c4 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -619,6 +619,8 @@ AC_DEFUN([OVS_CHECK_LINUX_COMPAT], [ OVS_GREP_IFELSE([$KSRC/include/uapi/linux/netdevice.h], [NET_NAME_UNKNOWN], [OVS_DEFINE([HAVE_NET_NAME_UNKNOWN])]) + OVS_GREP_IFELSE([$KSRC/include/net/sock.h], [sk_no_check_tx]) + OVS_GREP_IFELSE([$KSRC/include/linux/udp.h], [no_check6_tx]) OVS_GREP_IFELSE([$KSRC/include/linux/utsrelease.h], [el6], [OVS_DEFINE([HAVE_RHEL6_PER_CPU])]) diff --git a/datapath/linux/Modules.mk b/datapath/linux/Modules.mk index ae7c75324..ef080837b 100644 --- a/datapath/linux/Modules.mk +++ b/datapath/linux/Modules.mk @@ -67,6 +67,7 @@ openvswitch_headers += \ linux/compat/include/linux/stddef.h \ linux/compat/include/linux/types.h \ linux/compat/include/linux/u64_stats_sync.h \ + linux/compat/include/linux/udp.h \ linux/compat/include/linux/workqueue.h \ linux/compat/include/net/checksum.h \ linux/compat/include/net/dst.h \ diff --git a/datapath/linux/compat/include/linux/udp.h b/datapath/linux/compat/include/linux/udp.h new file mode 100644 index 000000000..22e57d4c0 --- /dev/null +++ b/datapath/linux/compat/include/linux/udp.h @@ -0,0 +1,33 @@ +#ifndef __LINUX_UDP_WRAPPER_H +#define __LINUX_UDP_WRAPPER_H 1 + +#include_next +#include + +#ifndef HAVE_NO_CHECK6_TX +static inline void udp_set_no_check6_tx(struct sock *sk, bool val) +{ +#ifdef HAVE_SK_NO_CHECK_TX + sk->sk_no_check_tx = val; +#endif +} + +static inline void udp_set_no_check6_rx(struct sock *sk, bool val) +{ +#ifdef HAVE_SK_NO_CHECK_TX + sk->sk_no_check_rx = val; +#else + /* since netwroking stack is not checking for zero UDP checksum + * check it in OVS module. */ + #define OVS_CHECK_UDP_TUNNEL_ZERO_CSUM +#endif +} +#endif + +#ifdef OVS_CHECK_UDP_TUNNEL_ZERO_CSUM +#define udp6_csum_zero_error rpl_udp6_csum_zero_error + +void rpl_udp6_csum_zero_error(struct sk_buff *skb); +#endif + +#endif diff --git a/datapath/linux/compat/include/net/udp.h b/datapath/linux/compat/include/net/udp.h index 41254aad7..fa49fa573 100644 --- a/datapath/linux/compat/include/net/udp.h +++ b/datapath/linux/compat/include/net/udp.h @@ -59,5 +59,4 @@ static inline __sum16 udp_v4_check(int len, __be32 saddr, void rpl_udp_set_csum(bool nocheck, struct sk_buff *skb, __be32 saddr, __be32 daddr, int len); #endif - #endif diff --git a/datapath/linux/compat/include/net/udp_tunnel.h b/datapath/linux/compat/include/net/udp_tunnel.h index 605fe63e3..d953e091b 100644 --- a/datapath/linux/compat/include/net/udp_tunnel.h +++ b/datapath/linux/compat/include/net/udp_tunnel.h @@ -41,9 +41,35 @@ struct udp_port_cfg { ipv6_v6only:1; }; +#define udp_sock_create4 rpl_udp_sock_create4 +int rpl_udp_sock_create4(struct net *net, struct udp_port_cfg *cfg, + struct socket **sockp); + +#define udp_sock_create6 rpl_udp_sock_create6 +#if IS_ENABLED(CONFIG_IPV6) +int rpl_udp_sock_create6(struct net *net, struct udp_port_cfg *cfg, + struct socket **sockp); +#else +static inline int udp_sock_create6(struct net *net, struct udp_port_cfg *cfg, + struct socket **sockp) +{ + return -EPFNOSUPPORT; +} +#endif + #define udp_sock_create rpl_udp_sock_create -int rpl_udp_sock_create(struct net *net, struct udp_port_cfg *cfg, - struct socket **sockp); +static inline int udp_sock_create(struct net *net, + struct udp_port_cfg *cfg, + struct socket **sockp) +{ + if (cfg->family == AF_INET) + return udp_sock_create4(net, cfg, sockp); + + if (cfg->family == AF_INET6) + return udp_sock_create6(net, cfg, sockp); + + return -EPFNOSUPPORT; +} typedef int (*udp_tunnel_encap_rcv_t)(struct sock *sk, struct sk_buff *skb); typedef void (*udp_tunnel_encap_destroy_t)(struct sock *sk); diff --git a/datapath/linux/compat/udp.c b/datapath/linux/compat/udp.c index 487d317bb..f0362b64d 100644 --- a/datapath/linux/compat/udp.c +++ b/datapath/linux/compat/udp.c @@ -42,3 +42,15 @@ void rpl_udp_set_csum(bool nocheck, struct sk_buff *skb, EXPORT_SYMBOL_GPL(rpl_udp_set_csum); #endif /* Linux version < 3.16 */ + +#ifdef OVS_CHECK_UDP_TUNNEL_ZERO_CSUM +void rpl_udp6_csum_zero_error(struct sk_buff *skb) +{ + /* RFC 2460 section 8.1 says that we SHOULD log + * this error. Well, it is reasonable. + */ + net_dbg_ratelimited("IPv6: udp checksum is 0 for [%pI6c]:%u->[%pI6c]:%u\n", + &ipv6_hdr(skb)->saddr, ntohs(udp_hdr(skb)->source), + &ipv6_hdr(skb)->daddr, ntohs(udp_hdr(skb)->dest)); +} +#endif diff --git a/datapath/linux/compat/udp_tunnel.c b/datapath/linux/compat/udp_tunnel.c index b4d345b56..daa3fa15f 100644 --- a/datapath/linux/compat/udp_tunnel.c +++ b/datapath/linux/compat/udp_tunnel.c @@ -16,74 +16,95 @@ #include -int rpl_udp_sock_create(struct net *net, struct udp_port_cfg *cfg, - struct socket **sockp) +int rpl_udp_sock_create4(struct net *net, struct udp_port_cfg *cfg, + struct socket **sockp) { int err; struct socket *sock = NULL; + struct sockaddr_in udp_addr; -#if IS_ENABLED(CONFIG_IPV6) - if (cfg->family == AF_INET6) { - struct sockaddr_in6 udp6_addr; + err = sock_create_kern(net, AF_INET, SOCK_DGRAM, 0, &sock); + if (err < 0) + goto error; - err = sock_create_kern(net, AF_INET6, SOCK_DGRAM, 0, &sock); - if (err < 0) - goto error; + udp_addr.sin_family = AF_INET; + udp_addr.sin_addr = cfg->local_ip; + udp_addr.sin_port = cfg->local_udp_port; + err = kernel_bind(sock, (struct sockaddr *)&udp_addr, + sizeof(udp_addr)); + if (err < 0) + goto error; - udp6_addr.sin6_family = AF_INET6; - memcpy(&udp6_addr.sin6_addr, &cfg->local_ip6, - sizeof(udp6_addr.sin6_addr)); - udp6_addr.sin6_port = cfg->local_udp_port; - err = kernel_bind(sock, (struct sockaddr *)&udp6_addr, - sizeof(udp6_addr)); - if (err < 0) - goto error; - - if (cfg->peer_udp_port) { - udp6_addr.sin6_family = AF_INET6; - memcpy(&udp6_addr.sin6_addr, &cfg->peer_ip6, - sizeof(udp6_addr.sin6_addr)); - udp6_addr.sin6_port = cfg->peer_udp_port; - err = kernel_connect(sock, - (struct sockaddr *)&udp6_addr, - sizeof(udp6_addr), 0); - } + if (cfg->peer_udp_port) { + udp_addr.sin_family = AF_INET; + udp_addr.sin_addr = cfg->peer_ip; + udp_addr.sin_port = cfg->peer_udp_port; + err = kernel_connect(sock, (struct sockaddr *)&udp_addr, + sizeof(udp_addr), 0); if (err < 0) goto error; - } else + } +#ifdef HAVE_SK_NO_CHECK_TX + sock->sk->sk_no_check_tx = !cfg->use_udp_checksums; #endif - if (cfg->family == AF_INET) { - struct sockaddr_in udp_addr; + *sockp = sock; + return 0; - err = sock_create_kern(net, AF_INET, SOCK_DGRAM, 0, &sock); - if (err < 0) - goto error; +error: + if (sock) { + kernel_sock_shutdown(sock, SHUT_RDWR); + sock_release(sock); + } + *sockp = NULL; + return err; +} +EXPORT_SYMBOL(rpl_udp_sock_create4); - udp_addr.sin_family = AF_INET; - udp_addr.sin_addr = cfg->local_ip; - udp_addr.sin_port = cfg->local_udp_port; - err = kernel_bind(sock, (struct sockaddr *)&udp_addr, - sizeof(udp_addr)); +int rpl_udp_sock_create6(struct net *net, struct udp_port_cfg *cfg, + struct socket **sockp) +{ + struct sockaddr_in6 udp6_addr; + int err; + struct socket *sock = NULL; + + err = sock_create_kern(net, AF_INET6, SOCK_DGRAM, 0, &sock); + if (err < 0) + goto error; + + if (cfg->ipv6_v6only) { + int val = 1; + + err = kernel_setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, + (char *) &val, sizeof(val)); if (err < 0) goto error; + } - if (cfg->peer_udp_port) { - udp_addr.sin_family = AF_INET; - udp_addr.sin_addr = cfg->peer_ip; - udp_addr.sin_port = cfg->peer_udp_port; - err = kernel_connect(sock, - (struct sockaddr *)&udp_addr, - sizeof(udp_addr), 0); - if (err < 0) - goto error; - } - } else { - return -EPFNOSUPPORT; + udp6_addr.sin6_family = AF_INET6; + memcpy(&udp6_addr.sin6_addr, &cfg->local_ip6, + sizeof(udp6_addr.sin6_addr)); + udp6_addr.sin6_port = cfg->local_udp_port; + err = kernel_bind(sock, (struct sockaddr *)&udp6_addr, + sizeof(udp6_addr)); + if (err < 0) + goto error; + + if (cfg->peer_udp_port) { + udp6_addr.sin6_family = AF_INET6; + memcpy(&udp6_addr.sin6_addr, &cfg->peer_ip6, + sizeof(udp6_addr.sin6_addr)); + udp6_addr.sin6_port = cfg->peer_udp_port; + err = kernel_connect(sock, + (struct sockaddr *)&udp6_addr, + sizeof(udp6_addr), 0); } + if (err < 0) + goto error; + udp_set_no_check6_tx(sock->sk, !cfg->use_udp6_tx_checksums); + udp_set_no_check6_rx(sock->sk, !cfg->use_udp6_rx_checksums); *sockp = sock; - return 0; error: @@ -94,7 +115,7 @@ error: *sockp = NULL; return err; } -EXPORT_SYMBOL_GPL(rpl_udp_sock_create); +EXPORT_SYMBOL_GPL(rpl_udp_sock_create6); void rpl_setup_udp_tunnel_sock(struct net *net, struct socket *sock, struct udp_tunnel_sock_cfg *cfg) diff --git a/datapath/linux/compat/vxlan.c b/datapath/linux/compat/vxlan.c index d45125d4c..37769903c 100644 --- a/datapath/linux/compat/vxlan.c +++ b/datapath/linux/compat/vxlan.c @@ -850,6 +850,13 @@ static void vxlan_rcv(struct vxlan_sock *vs, struct sk_buff *skb, oip6 = ipv6_hdr(skb); saddr.sin6.sin6_addr = oip6->saddr; saddr.sa.sa_family = AF_INET6; +#ifdef OVS_CHECK_UDP_TUNNEL_ZERO_CSUM + if (!udp_hdr(skb)->check && + !(vs->flags & VXLAN_F_UDP_ZERO_CSUM6_RX)) { + udp6_csum_zero_error(skb); + goto drop; + } +#endif #endif } -- 2.20.1