lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite for Android: free password hash cracker in your pocket
[<prev] [next>] [day] [month] [year] [list]
Message-ID: <alpine.DEB.2.02.1401021401200.32160@tomh.mtv.corp.google.com>
Date:	Thu, 2 Jan 2014 14:12:52 -0800 (PST)
From:	Tom Herbert <therbert@...gle.com>
To:	davem@...emloft.net, netdev@...r.kernel.org
cc:	hkchu@...gle.com
Subject: [PATCH RFC 2/7] net: Support for GUE RX via xfrm

This patch provides a Generic Receive Encapsulation receive path
using the XFRM framework (udp_encap_rcv).

Signed-off-by: Tom Herbert <therbert@...gle.com>
---
 include/net/gue.h |  50 +++++++++
 net/ipv4/Kconfig  |   6 +
 net/ipv4/Makefile |   1 +
 net/ipv4/gue.c    | 322 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 379 insertions(+)
 create mode 100644 include/net/gue.h
 create mode 100644 net/ipv4/gue.c

diff --git a/include/net/gue.h b/include/net/gue.h
new file mode 100644
index 0000000..247274d
--- /dev/null
+++ b/include/net/gue.h
@@ -0,0 +1,50 @@
+#ifndef GUEHDR
+#define GUEHDR
+
+struct guehdr {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+	__u8    hlen:4,
+		version:4;
+#elif defined (__BIG_ENDIAN_BITFIELD)
+	__u8    version:4,
+		hlen:4;
+#else
+#error  "Please fix <asm/byteorder.h>"
+#endif
+	__u8    next_hdr;
+	__u16   flags;
+};
+
+#if defined(CONFIG_NET_GUE) || defined(CONFIG_NET_GUE_MODULE)
+
+struct gue_port_cfg {
+	u8			protocol;
+	int			encap_type;
+
+	/* Used only for kernel-created sockets */
+	struct in_addr		local_ip;
+	struct in_addr		peer_ip;
+#if IS_ENABLED(CONFIG_IPV6)
+	struct in6_addr		*local_ip6;
+	struct in6_addr		*peer_ip6;
+#endif
+	u16			local_udp_port;
+	u16			peer_udp_port;
+	unsigned int		use_udp_checksums:1;
+};
+
+enum gue_encap_types {
+	GUE_ENCAP_DIRECT,
+	GUE_ENCAP_GUEHDR
+};
+
+int gue_port_create(struct net *net, struct gue_port_cfg *cfg,
+		    struct socket **sockp);
+int gue_open_direct_port(unsigned short port, unsigned char proto,
+			 struct socket **sockp);
+int gue_open_guehdr_port(unsigned short port, struct socket **sockp);
+void gue_close_port(struct socket *sock);
+
+#endif /* defined(CONFIG_NET_GUE) || defined(CONFIG_NET_GUE_MODULE) */
+
+#endif
diff --git a/net/ipv4/Kconfig b/net/ipv4/Kconfig
index 05c57f0..12b1e46 100644
--- a/net/ipv4/Kconfig
+++ b/net/ipv4/Kconfig
@@ -307,6 +307,12 @@ config NET_IPVTI
 	  the notion of a secure tunnel for IPSEC and then use routing protocol
 	  on top.
 
+config NET_GUE
+	tristate "IP: Generic UDP Encpasulation"
+	select XFRM
+	help
+	 This is helper module to decapsulate GUE packets.
+
 config INET_AH
 	tristate "IP: AH transformation"
 	select XFRM_ALGO
diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile
index 4b81e91..14430a7 100644
--- a/net/ipv4/Makefile
+++ b/net/ipv4/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_PROC_FS) += proc.o
 obj-$(CONFIG_IP_MULTIPLE_TABLES) += fib_rules.o
 obj-$(CONFIG_IP_MROUTE) += ipmr.o
 obj-$(CONFIG_NET_IPIP) += ipip.o
+obj-$(CONFIG_NET_GUE) += gue.o
 gre-y := gre_demux.o gre_offload.o
 obj-$(CONFIG_NET_IPGRE_DEMUX) += gre.o
 obj-$(CONFIG_NET_IPGRE) += ip_gre.o
diff --git a/net/ipv4/gue.c b/net/ipv4/gue.c
new file mode 100644
index 0000000..04c31ff
--- /dev/null
+++ b/net/ipv4/gue.c
@@ -0,0 +1,322 @@
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/socket.h>
+#include <linux/skbuff.h>
+#include <linux/ip.h>
+#include <linux/udp.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <net/ip.h>
+#include <net/udp.h>
+#include <net/gue.h>
+#include <net/xfrm.h>
+
+static int gue_port __read_mostly = 0;
+MODULE_PARM_DESC(gue_port, "GUE header port");
+module_param(gue_port, int, 0);
+
+struct socket *gue_sock = NULL;
+
+struct gue {
+	struct sock *sock;
+	void (*old_sk_destruct)(struct sock *sk);
+	unsigned char  protocol;
+};
+
+static inline struct gue *gue_from_sock(struct sock *sk)
+{
+	return (struct gue *)sk->sk_user_data;
+}
+
+static int gue_udp_encap_recv_deliver(struct sk_buff *skb,
+				      unsigned char protocol, size_t len,
+				      int encap_type)
+{
+	struct iphdr *iph = ip_hdr(skb);
+
+	/*
+	 * Remove 'len' bytes from the packet (UDP header and
+	 * GUE header if present), modify the protocol to the one
+	 * we found, and then call rcv_encap.
+	 */
+	iph->tot_len = htons(ntohs(iph->tot_len) - len);
+	__skb_pull(skb, len);
+	skb_reset_transport_header(skb);
+
+	return -protocol;
+}
+
+int gue_udp_direct_recv(struct sock *sk, struct sk_buff *skb)
+{
+	struct gue *gue;
+	struct udp_sock *up = udp_sk(sk);
+
+	if (!(gue = gue_from_sock(sk)))
+		return 1;
+
+	return gue_udp_encap_recv_deliver(skb, gue->protocol,
+					  sizeof(struct udphdr),
+					  up->encap_type);
+}
+
+int gue_udp_guehdr_recv(struct sock *sk, struct sk_buff *skb)
+{
+	struct gue *gue;
+	size_t len;
+	struct guehdr *guehdr;
+	struct udp_sock *up = udp_sk(sk);
+
+	if (!(gue = gue_from_sock(sk)))
+		return 1;
+
+	len = sizeof(struct udphdr) + sizeof(struct guehdr);
+	if (!pskb_may_pull(skb, len))
+		goto drop;
+
+	guehdr = (struct guehdr *)(skb->data + sizeof(struct udphdr));;
+
+	len += guehdr->hlen << 2;
+	if (!pskb_may_pull(skb, len))
+		goto drop;
+
+	if (guehdr->version != 0)
+		goto drop;
+
+	if (guehdr->flags) {
+		/* No support yet */
+		goto drop;
+	}
+
+	return gue_udp_encap_recv_deliver(skb, guehdr->next_hdr, len,
+					  up->encap_type);
+drop:
+	kfree_skb(skb);
+	return 0;
+}
+
+static int gue_sock_create(struct net *net,
+			   struct gue_port_cfg *cfg,
+			   struct socket **sockp)
+{
+	int err = -EINVAL;
+	struct socket *sock = NULL;
+	struct sockaddr_in udp_addr = {0};
+#if IS_ENABLED(CONFIG_IPV6)
+	struct sockaddr_in6 udp6_addr = {0};
+#endif
+
+#if IS_ENABLED(CONFIG_IPV6)
+	if (cfg->local_ip6 && cfg->peer_ip6) {
+		err = sock_create_kern(AF_INET6, SOCK_DGRAM, 0, &sock);
+		if (err < 0)
+			goto error;
+
+		sk_change_net(sock->sk, net);
+
+		udp6_addr.sin6_family = AF_INET6;
+		memcpy(&udp6_addr.sin6_addr, cfg->local_ip6,
+		       sizeof(udp6_addr.sin6_addr));
+		udp6_addr.sin6_port = htons(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 = htons(cfg->peer_udp_port);
+			err = kernel_connect(sock,
+					     (struct sockaddr *) &udp6_addr,
+					     sizeof(udp6_addr), 0);
+		}
+		if (err < 0)
+			goto error;
+	} else
+#endif
+	{
+		err = sock_create_kern(AF_INET, SOCK_DGRAM, 0, &sock);
+		if (err < 0)
+			goto error;
+
+		sk_change_net(sock->sk, net);
+
+		udp_addr.sin_family = AF_INET;
+		udp_addr.sin_addr = cfg->local_ip;
+		udp_addr.sin_port = htons(cfg->local_udp_port);
+		err = kernel_bind(sock, (struct sockaddr *) &udp_addr,
+				  sizeof(udp_addr));
+		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 = htons(cfg->peer_udp_port);
+			err = kernel_connect(sock,
+					     (struct sockaddr *) &udp_addr,
+					     sizeof(udp_addr), 0);
+			if (err < 0)
+				goto error;
+		}
+	}
+
+	if (!cfg->use_udp_checksums)
+		sock->sk->sk_no_check = UDP_CSUM_NOXMIT;
+
+	*sockp = sock;
+
+	return 0;
+
+error:
+	if (sock) {
+		kernel_sock_shutdown(sock, SHUT_RDWR);
+		sk_release_kernel(sock->sk);
+	}
+	*sockp = NULL;
+	return err;
+}
+
+void gue_sock_destruct(struct sock *sk)
+{
+	struct gue *gue;
+	if (!(gue = gue_from_sock(sk)))
+		return;
+
+	/* Remove hooks into tunnel socket */
+	sk->sk_destruct = gue->old_sk_destruct;
+	sk->sk_user_data = NULL;
+	gue->sock = NULL;
+
+	if (sk->sk_destruct)
+		sk->sk_destruct(sk);
+
+	kfree(gue);
+}
+
+
+int
+gue_port_create(struct net *net, struct gue_port_cfg *cfg,
+		struct socket **sockp)
+{
+	struct gue *gue = NULL;
+	int err;
+	struct socket *sock = NULL;
+	struct sock *sk;
+
+	/* Open UDP socket */
+	err = gue_sock_create(net, cfg, &sock);
+	if (err < 0)
+		goto error;
+
+	sk = sock->sk;
+
+	/* Allocate GUE port structure */
+	gue = kzalloc(sizeof(*gue), GFP_KERNEL);
+	if (!gue) {
+		err = -ENOMEM;
+		goto error;
+	}
+
+	/* Mark socket as an encapsulation socket. See net/ipv4/udp.c */
+	switch (cfg->encap_type) {
+	case GUE_ENCAP_DIRECT:
+		gue->protocol = cfg->protocol;
+		udp_sk(sk)->encap_rcv = gue_udp_direct_recv;
+		break;
+	case GUE_ENCAP_GUEHDR:
+		udp_sk(sk)->encap_rcv = gue_udp_guehdr_recv;
+		break;
+	default:
+		err = -EINVAL;
+		goto error;
+	}
+
+	/* Mark socket as an encapsulation socket. */
+	udp_sk(sk)->encap_type = 1;
+	udp_encap_enable();
+
+	sk->sk_user_data = gue;
+	gue->sock = sk;
+
+	sk->sk_allocation = GFP_ATOMIC;
+
+	/* Hook on the gue_port socket destructor so that we can cleanup
+	 * if the tunnel socket goes away.
+	 */
+	sk->sk_destruct = &gue_sock_destruct;
+	gue->old_sk_destruct = sk->sk_destruct;
+
+	if (sockp)
+		*sockp = sock;
+
+	return 0;
+
+error:
+	kfree(gue);
+	if (sock)
+		sock_release(sock);
+
+	return err;
+}
+EXPORT_SYMBOL_GPL(gue_port_create);
+
+int gue_open_direct_port(unsigned short port, unsigned char proto,
+			 struct socket **sockp)
+{
+        struct gue_port_cfg cfg;
+
+        memset(&cfg, 0, sizeof(cfg));
+
+        cfg.local_ip.s_addr = INADDR_ANY;
+        cfg.local_udp_port = port;
+        cfg.encap_type = GUE_ENCAP_DIRECT;
+        cfg.protocol = proto;
+
+        return gue_port_create(&init_net, &cfg, sockp);
+}
+EXPORT_SYMBOL_GPL(gue_open_direct_port);
+
+int gue_open_guehdr_port(unsigned short port, struct socket **sockp)
+{
+	struct gue_port_cfg cfg;
+
+	memset(&cfg, 0, sizeof(cfg));
+
+	cfg.local_ip.s_addr = INADDR_ANY;
+	cfg.local_udp_port = port;
+	cfg.encap_type = GUE_ENCAP_GUEHDR;
+
+	return gue_port_create(&init_net, &cfg, NULL);
+}
+EXPORT_SYMBOL_GPL(gue_open_guehdr_port);
+
+void gue_close_port(struct socket *sock)
+{
+	sock_release(sock);
+}
+EXPORT_SYMBOL_GPL(gue_close_port);
+
+static int __init gue_init(void)
+{
+	int err = 0;
+
+	if (gue_port)
+		err = gue_open_guehdr_port(gue_port, &gue_sock);
+
+	return err;
+}
+
+static void __exit gue_fini(void)
+{
+	if (gue_sock) {
+		gue_close_port(gue_sock);
+		gue_sock = NULL;
+	}
+}
+
+module_init(gue_init);
+module_exit(gue_fini);
+MODULE_AUTHOR("Tom Herbert <therbert@...gle.com>");
+MODULE_LICENSE("GPL");
-- 
1.8.5.1

--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ