diff options
author | Hans Petter Selasky <hselasky@FreeBSD.org> | 2015-03-11 16:02:24 +0000 |
---|---|---|
committer | Hans Petter Selasky <hselasky@FreeBSD.org> | 2015-03-11 16:02:24 +0000 |
commit | b7ba031ff71f20a2d5ad24b46595869152a53519 (patch) | |
tree | 0fcc56ca42552e19fc15d1a3f8ba6fd47d95e10d /sys/kern/uipc_mbufhash.c | |
parent | 5ba11c4c2ef4ee5177a9eb588e0234b3b3859c18 (diff) | |
download | src-b7ba031ff71f20a2d5ad24b46595869152a53519.tar.gz src-b7ba031ff71f20a2d5ad24b46595869152a53519.zip |
Notes
Diffstat (limited to 'sys/kern/uipc_mbufhash.c')
-rw-r--r-- | sys/kern/uipc_mbufhash.c | 181 |
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); +} |