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: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Date:	Sat, 26 Jul 2014 15:29:23 -0700 (PDT)
From:	Tom Herbert <therbert@...gle.com>
To:	davem@...emloft.net, netdev@...r.kernel.org
Subject: [PATCH net-next 1/2] udp: UDP Fast Port

This patch implements fast ports in UDP. The idea is that a kernel
module registers a port and associated receive function. In UDP receive,
the list of fast ports is scanned and the destinaton port is matched
against the registered port. If there is a match, then the receive
function is called to process the packet. When a UDP fast port is used,
we can receive encap'ed packets without performing or accessing a
socket. This is a performance gain, especially since we don't need to
take socket ref count on every packet.

Signed-off-by: Tom Herbert <therbert@...gle.com>
---
 include/net/udp.h | 23 +++++++++++++++++++
 net/ipv4/udp.c    | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++++---
 net/ipv6/udp.c    | 21 +++++++++++++++--
 3 files changed, 108 insertions(+), 5 deletions(-)

diff --git a/include/net/udp.h b/include/net/udp.h
index 70f9413..1a88f4b 100644
--- a/include/net/udp.h
+++ b/include/net/udp.h
@@ -300,6 +300,29 @@ void udp4_proc_exit(void);
 
 int udpv4_offload_init(void);
 
+/* Definitions for UDP fast port */
+
+#define UFP_HASH_SIZE (16)
+#define UFP_HASH_MASK (UFP_HASH_SIZE - 1)
+
+struct udp_fast_port {
+	__be16 port;
+	int (*encap_rcv)(struct udp_fast_port *ufp, struct sk_buff *skb);
+	void *priv_data;
+	struct list_head list;
+};
+
+extern struct list_head ufp_base[];
+
+static inline struct list_head *ufp_head(const __be16 port)
+{
+	return &ufp_base[ntohs(port) & UFP_HASH_MASK];
+}
+
+void udp_add_fast_port(struct udp_fast_port *ufp);
+void __udp_remove_fast_port(struct udp_fast_port *ufp);
+void udp_remove_fast_port(struct udp_fast_port *ufp);
+
 void udp_init(void);
 
 void udp_encap_enable(void);
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index f57c0e4..8cf359a 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -131,6 +131,10 @@ EXPORT_SYMBOL(udp_memory_allocated);
 #define MAX_UDP_PORTS 65536
 #define PORTS_PER_CHAIN (MAX_UDP_PORTS / UDP_HTABLE_SIZE_MIN)
 
+struct list_head ufp_base[UFP_HASH_SIZE] __read_mostly;
+EXPORT_SYMBOL(ufp_base);
+static DEFINE_SPINLOCK(ufp_lock);
+
 static int udp_lib_lport_inuse(struct net *net, __u16 num,
 			       const struct udp_hslot *hslot,
 			       unsigned long *bitmap,
@@ -1735,6 +1739,7 @@ int __udp4_lib_rcv(struct sk_buff *skb, struct udp_table *udptable,
 	struct rtable *rt = skb_rtable(skb);
 	__be32 saddr, daddr;
 	struct net *net = dev_net(skb->dev);
+	int ret;
 
 	/*
 	 *  Validate the packet.
@@ -1763,7 +1768,6 @@ int __udp4_lib_rcv(struct sk_buff *skb, struct udp_table *udptable,
 	sk = skb_steal_sock(skb);
 	if (sk) {
 		struct dst_entry *dst = skb_dst(skb);
-		int ret;
 
 		if (unlikely(sk->sk_rx_dst != dst))
 			udp_sk_rx_dst_set(sk, dst);
@@ -1777,16 +1781,33 @@ int __udp4_lib_rcv(struct sk_buff *skb, struct udp_table *udptable,
 			return -ret;
 		return 0;
 	} else {
+		struct udp_fast_port *ufp;
+
 		if (rt->rt_flags & (RTCF_BROADCAST|RTCF_MULTICAST))
 			return __udp4_lib_mcast_deliver(net, skb, uh,
 					saddr, daddr, udptable);
 
+		/* Check for a UDP fast port */
+		list_for_each_entry_rcu(ufp, ufp_head(uh->dest), list) {
+			if (uh->dest != ufp->port)
+				continue;
+
+			/* Verify checksum before giving to encap */
+			if (udp_lib_checksum_complete(skb))
+				goto csum_error;
+
+			ret = ufp->encap_rcv(ufp, skb);
+			if (ret <= 0) {
+				UDP_INC_STATS_BH(net, UDP_MIB_INDATAGRAMS,
+						 proto == IPPROTO_UDPLITE);
+				return -ret;
+			}
+		}
+
 		sk = __udp4_lib_lookup_skb(skb, uh->source, uh->dest, udptable);
 	}
 
 	if (sk != NULL) {
-		int ret;
-
 		ret = udp_queue_rcv_skb(sk, skb);
 		sock_put(sk);
 
@@ -1981,6 +2002,44 @@ int udp_rcv(struct sk_buff *skb)
 	return __udp4_lib_rcv(skb, &udp_table, IPPROTO_UDP);
 }
 
+void udp_add_fast_port(struct udp_fast_port *ufp)
+{
+	struct list_head *head = ufp_head(ufp->port);
+
+	spin_lock(&ufp_lock);
+	list_add_rcu(&ufp->list, head);
+	spin_unlock(&ufp_lock);
+}
+EXPORT_SYMBOL(udp_add_fast_port);
+
+void __udp_remove_fast_port(struct udp_fast_port *ufp)
+{
+	struct list_head *head = ufp_head(ufp->port);
+	struct udp_fast_port *ufp1;
+
+	spin_lock(&ufp_lock);
+
+	list_for_each_entry(ufp1, head, list) {
+		if (ufp == ufp1) {
+			list_del_rcu(&ufp->list);
+			goto out;
+		}
+	}
+
+	pr_warn("udp_remove_fast_port: %p not found\n", ufp);
+out:
+	spin_unlock(&ufp_lock);
+}
+EXPORT_SYMBOL(__udp_remove_fast_port);
+
+void udp_remove_fast_port(struct udp_fast_port *ufp)
+{
+	__udp_remove_fast_port(ufp);
+
+	synchronize_net();
+}
+EXPORT_SYMBOL(udp_remove_fast_port);
+
 void udp_destroy_sock(struct sock *sk)
 {
 	struct udp_sock *up = udp_sk(sk);
@@ -2505,6 +2564,7 @@ void __init udp_table_init(struct udp_table *table, const char *name)
 void __init udp_init(void)
 {
 	unsigned long limit;
+	int i;
 
 	udp_table_init(&udp_table, "UDP");
 	limit = nr_free_buffer_pages() / 8;
@@ -2515,4 +2575,7 @@ void __init udp_init(void)
 
 	sysctl_udp_rmem_min = SK_MEM_QUANTUM;
 	sysctl_udp_wmem_min = SK_MEM_QUANTUM;
+
+	for (i = 0; i < UFP_HASH_SIZE; i++)
+		INIT_LIST_HEAD(&ufp_base[i]);
 }
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index 5b6091d..e928182 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -830,6 +830,8 @@ int __udp6_lib_rcv(struct sk_buff *skb, struct udp_table *udptable,
 	struct udphdr *uh;
 	const struct in6_addr *saddr, *daddr;
 	u32 ulen = 0;
+	struct udp_fast_port *ufp;
+	int ret;
 
 	if (!pskb_may_pull(skb, sizeof(struct udphdr)))
 		goto discard;
@@ -873,14 +875,29 @@ int __udp6_lib_rcv(struct sk_buff *skb, struct udp_table *udptable,
 
 	/* Unicast */
 
+	/* Check for a UDP fast port */
+	list_for_each_entry_rcu(ufp, ufp_head(uh->dest), list) {
+		if (uh->dest != ufp->port)
+			continue;
+
+		/* Verify checksum before giving to encap */
+		if (udp_lib_checksum_complete(skb))
+			goto csum_error;
+
+		ret = ufp->encap_rcv(ufp, skb);
+		if (ret <= 0) {
+			UDP_INC_STATS_BH(net, UDP_MIB_INDATAGRAMS,
+					 proto == IPPROTO_UDPLITE);
+			return -ret;
+		}
+	}
+
 	/*
 	 * check socket cache ... must talk to Alan about his plans
 	 * for sock caches... i'll skip this for now.
 	 */
 	sk = __udp6_lib_lookup_skb(skb, uh->source, uh->dest, udptable);
 	if (sk != NULL) {
-		int ret;
-
 		if (!uh->check && !udp_sk(sk)->no_check6_rx) {
 			sock_put(sk);
 			udp6_csum_zero_error(skb);
-- 
2.0.0.526.g5318336

--
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