aboutsummaryrefslogtreecommitdiff
path: root/sys/kern/uipc_mbufhash.c
diff options
context:
space:
mode:
authorHans Petter Selasky <hselasky@FreeBSD.org>2015-03-11 16:02:24 +0000
committerHans Petter Selasky <hselasky@FreeBSD.org>2015-03-11 16:02:24 +0000
commitb7ba031ff71f20a2d5ad24b46595869152a53519 (patch)
tree0fcc56ca42552e19fc15d1a3f8ba6fd47d95e10d /sys/kern/uipc_mbufhash.c
parent5ba11c4c2ef4ee5177a9eb588e0234b3b3859c18 (diff)
downloadsrc-b7ba031ff71f20a2d5ad24b46595869152a53519.tar.gz
src-b7ba031ff71f20a2d5ad24b46595869152a53519.zip
Notes
Diffstat (limited to 'sys/kern/uipc_mbufhash.c')
-rw-r--r--sys/kern/uipc_mbufhash.c181
1 files changed, 181 insertions, 0 deletions
diff --git a/sys/kern/uipc_mbufhash.c b/sys/kern/uipc_mbufhash.c
new file mode 100644
index 000000000000..75f4f93988f9
--- /dev/null
+++ b/sys/kern/uipc_mbufhash.c
@@ -0,0 +1,181 @@
+/* $OpenBSD: if_trunk.c,v 1.30 2007/01/31 06:20:19 reyk Exp $ */
+
+/*
+ * Copyright (c) 2005, 2006 Reyk Floeter <reyk@openbsd.org>
+ * Copyright (c) 2007 Andrew Thompson <thompsa@FreeBSD.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_inet.h"
+#include "opt_inet6.h"
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/mbuf.h>
+#include <sys/fnv_hash.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/if_var.h>
+
+#include <net/ethernet.h>
+
+#if defined(INET) || defined(INET6)
+#include <netinet/in.h>
+#endif
+
+#ifdef INET
+#include <netinet/ip.h>
+#endif
+
+#ifdef INET6
+#include <netinet/ip6.h>
+#endif
+
+#include <net/if_vlan_var.h>
+
+static const void *
+m_ether_tcpip_hash_gethdr(const struct mbuf *m, const u_int off,
+ const u_int len, void *buf)
+{
+
+ if (m->m_pkthdr.len < (off + len)) {
+ return (NULL);
+ } else if (m->m_len < (off + len)) {
+ m_copydata(m, off, len, buf);
+ return (buf);
+ }
+ return (mtod(m, char *) + off);
+}
+
+uint32_t
+m_ether_tcpip_hash_init(void)
+{
+ uint32_t seed;
+
+ seed = arc4random();
+ return (fnv_32_buf(&seed, sizeof(seed), FNV1_32_INIT));
+}
+
+uint32_t
+m_ether_tcpip_hash(const uint32_t flags, const struct mbuf *m,
+ const uint32_t key)
+{
+ union {
+#ifdef INET
+ struct ip ip;
+#endif
+#ifdef INET6
+ struct ip6_hdr ip6;
+#endif
+ struct ether_vlan_header vlan;
+ uint32_t port;
+ } buf;
+ struct ether_header *eh;
+ const struct ether_vlan_header *vlan;
+#ifdef INET
+ const struct ip *ip;
+#endif
+#ifdef INET6
+ const struct ip6_hdr *ip6;
+#endif
+ uint32_t p;
+ int off;
+ uint16_t etype;
+
+ p = key;
+ off = sizeof(*eh);
+ if (m->m_len < off)
+ goto done;
+ eh = mtod(m, struct ether_header *);
+ etype = ntohs(eh->ether_type);
+ if (flags & MBUF_HASHFLAG_L2) {
+ p = fnv_32_buf(&eh->ether_shost, ETHER_ADDR_LEN, p);
+ p = fnv_32_buf(&eh->ether_dhost, ETHER_ADDR_LEN, p);
+ }
+ /* Special handling for encapsulating VLAN frames */
+ if ((m->m_flags & M_VLANTAG) && (flags & MBUF_HASHFLAG_L2)) {
+ p = fnv_32_buf(&m->m_pkthdr.ether_vtag,
+ sizeof(m->m_pkthdr.ether_vtag), p);
+ } else if (etype == ETHERTYPE_VLAN) {
+ vlan = m_ether_tcpip_hash_gethdr(m, off, sizeof(*vlan), &buf);
+ if (vlan == NULL)
+ goto done;
+
+ if (flags & MBUF_HASHFLAG_L2)
+ p = fnv_32_buf(&vlan->evl_tag, sizeof(vlan->evl_tag), p);
+ etype = ntohs(vlan->evl_proto);
+ off += sizeof(*vlan) - sizeof(*eh);
+ }
+ switch (etype) {
+#ifdef INET
+ case ETHERTYPE_IP:
+ ip = m_ether_tcpip_hash_gethdr(m, off, sizeof(*ip), &buf);
+ if (ip == NULL)
+ break;
+ if (flags & MBUF_HASHFLAG_L3) {
+ p = fnv_32_buf(&ip->ip_src, sizeof(struct in_addr), p);
+ p = fnv_32_buf(&ip->ip_dst, sizeof(struct in_addr), p);
+ }
+ if (flags & MBUF_HASHFLAG_L4) {
+ const uint32_t *ports;
+ int iphlen;
+
+ switch (ip->ip_p) {
+ case IPPROTO_TCP:
+ case IPPROTO_UDP:
+ case IPPROTO_SCTP:
+ iphlen = ip->ip_hl << 2;
+ if (iphlen < sizeof(*ip))
+ break;
+ off += iphlen;
+ ports = m_ether_tcpip_hash_gethdr(m,
+ off, sizeof(*ports), &buf);
+ if (ports == NULL)
+ break;
+ p = fnv_32_buf(ports, sizeof(*ports), p);
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+#endif
+#ifdef INET6
+ case ETHERTYPE_IPV6:
+ ip6 = m_ether_tcpip_hash_gethdr(m, off, sizeof(*ip6), &buf);
+ if (ip6 == NULL)
+ break;
+ if (flags & MBUF_HASHFLAG_L3) {
+ p = fnv_32_buf(&ip6->ip6_src, sizeof(struct in6_addr), p);
+ p = fnv_32_buf(&ip6->ip6_dst, sizeof(struct in6_addr), p);
+ }
+ if (flags & MBUF_HASHFLAG_L4) {
+ uint32_t flow;
+
+ /* IPv6 flow label */
+ flow = ip6->ip6_flow & IPV6_FLOWLABEL_MASK;
+ p = fnv_32_buf(&flow, sizeof(flow), p);
+ }
+ break;
+#endif
+ default:
+ break;
+ }
+done:
+ return (p);
+}