datapath: compat: Update udp_sock_create
authorPravin B Shelar <pshelar@ovn.org>
Fri, 8 Jul 2016 04:51:17 +0000 (21:51 -0700)
committerPravin B Shelar <pshelar@ovn.org>
Sat, 9 Jul 2016 02:27:49 +0000 (19:27 -0700)
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 <pshelar@ovn.org>
Acked-by: Jesse Gross <jesse@kernel.org>
acinclude.m4
datapath/linux/Modules.mk
datapath/linux/compat/include/linux/udp.h [new file with mode: 0644]
datapath/linux/compat/include/net/udp.h
datapath/linux/compat/include/net/udp_tunnel.h
datapath/linux/compat/udp.c
datapath/linux/compat/udp_tunnel.c
datapath/linux/compat/vxlan.c

index 4bb65c7..003ba72 100644 (file)
@@ -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])])
 
index ae7c753..ef08083 100644 (file)
@@ -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 (file)
index 0000000..22e57d4
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef __LINUX_UDP_WRAPPER_H
+#define __LINUX_UDP_WRAPPER_H  1
+
+#include_next <linux/udp.h>
+#include <linux/ipv6.h>
+
+#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
index 41254aa..fa49fa5 100644 (file)
@@ -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
index 605fe63..d953e09 100644 (file)
@@ -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);
index 487d317..f0362b6 100644 (file)
@@ -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
index b4d345b..daa3fa1 100644 (file)
 #include <net/ip6_tunnel.h>
 
 
-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)
index d45125d..3776990 100644 (file)
@@ -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
        }