Changeset View
Changeset View
Standalone View
Standalone View
sys/kern/uipc_mbufhash.c
Show All 22 Lines | |||||
#include "opt_inet.h" | #include "opt_inet.h" | ||||
#include "opt_inet6.h" | #include "opt_inet6.h" | ||||
#include <sys/param.h> | #include <sys/param.h> | ||||
#include <sys/mbuf.h> | #include <sys/mbuf.h> | ||||
#include <sys/fnv_hash.h> | #include <sys/fnv_hash.h> | ||||
#include <net/ethernet.h> | #include <net/ethernet.h> | ||||
#include <net/infiniband.h> | |||||
#if defined(INET) || defined(INET6) | #if defined(INET) || defined(INET6) | ||||
#include <netinet/in.h> | #include <netinet/in.h> | ||||
#endif | #endif | ||||
#ifdef INET | #ifdef INET | ||||
#include <netinet/ip.h> | #include <netinet/ip.h> | ||||
#endif | #endif | ||||
#ifdef INET6 | #ifdef INET6 | ||||
#include <netinet/ip6.h> | #include <netinet/ip6.h> | ||||
#endif | #endif | ||||
static const void * | static const void * | ||||
m_ether_tcpip_hash_gethdr(const struct mbuf *m, const u_int off, | m_common_hash_gethdr(const struct mbuf *m, const u_int off, | ||||
const u_int len, void *buf) | const u_int len, void *buf) | ||||
{ | { | ||||
if (m->m_pkthdr.len < (off + len)) { | if (m->m_pkthdr.len < (off + len)) { | ||||
return (NULL); | return (NULL); | ||||
} else if (m->m_len < (off + len)) { | } else if (m->m_len < (off + len)) { | ||||
m_copydata(m, off, len, buf); | m_copydata(m, off, len, buf); | ||||
return (buf); | return (buf); | ||||
} | } | ||||
return (mtod(m, char *) + off); | return (mtod(m, char *) + off); | ||||
} | } | ||||
uint32_t | uint32_t | ||||
m_ether_tcpip_hash_init(void) | m_ether_tcpip_hash_init(void) | ||||
{ | { | ||||
uint32_t seed; | uint32_t seed; | ||||
seed = arc4random(); | seed = arc4random(); | ||||
return (fnv_32_buf(&seed, sizeof(seed), FNV1_32_INIT)); | return (fnv_32_buf(&seed, sizeof(seed), FNV1_32_INIT)); | ||||
} | } | ||||
uint32_t | uint32_t | ||||
m_ether_tcpip_hash(const uint32_t flags, const struct mbuf *m, | m_infiniband_tcpip_hash_init(void) | ||||
const uint32_t key) | |||||
{ | { | ||||
uint32_t seed; | |||||
seed = arc4random(); | |||||
return (fnv_32_buf(&seed, sizeof(seed), FNV1_32_INIT)); | |||||
} | |||||
static inline uint32_t | |||||
m_tcpip_hash(const uint32_t flags, const struct mbuf *m, | |||||
uint32_t p, int off, const uint16_t etype) | |||||
{ | |||||
union { | union { | ||||
#ifdef INET | #ifdef INET | ||||
struct ip ip; | struct ip ip; | ||||
#endif | #endif | ||||
#ifdef INET6 | #ifdef INET6 | ||||
struct ip6_hdr ip6; | struct ip6_hdr ip6; | ||||
#endif | #endif | ||||
struct ether_vlan_header vlan; | |||||
uint32_t port; | uint32_t port; | ||||
} buf; | } buf; | ||||
const struct ether_header *eh; | |||||
const struct ether_vlan_header *vlan; | |||||
#ifdef INET | #ifdef INET | ||||
const struct ip *ip; | const struct ip *ip; | ||||
#endif | #endif | ||||
#ifdef INET6 | #ifdef INET6 | ||||
const struct ip6_hdr *ip6; | const struct ip6_hdr *ip6; | ||||
#endif | #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) { | switch (etype) { | ||||
#ifdef INET | #ifdef INET | ||||
case ETHERTYPE_IP: | case ETHERTYPE_IP: | ||||
ip = m_ether_tcpip_hash_gethdr(m, off, sizeof(*ip), &buf); | ip = m_common_hash_gethdr(m, off, sizeof(*ip), &buf); | ||||
if (ip == NULL) | if (ip == NULL) | ||||
break; | break; | ||||
if (flags & MBUF_HASHFLAG_L3) { | if (flags & MBUF_HASHFLAG_L3) { | ||||
p = fnv_32_buf(&ip->ip_src, sizeof(struct in_addr), p); | p = fnv_32_buf(&ip->ip_src, sizeof(struct in_addr), p); | ||||
p = fnv_32_buf(&ip->ip_dst, sizeof(struct in_addr), p); | p = fnv_32_buf(&ip->ip_dst, sizeof(struct in_addr), p); | ||||
} | } | ||||
if (flags & MBUF_HASHFLAG_L4) { | if (flags & MBUF_HASHFLAG_L4) { | ||||
const uint32_t *ports; | const uint32_t *ports; | ||||
int iphlen; | int iphlen; | ||||
switch (ip->ip_p) { | switch (ip->ip_p) { | ||||
case IPPROTO_TCP: | case IPPROTO_TCP: | ||||
case IPPROTO_UDP: | case IPPROTO_UDP: | ||||
case IPPROTO_SCTP: | case IPPROTO_SCTP: | ||||
iphlen = ip->ip_hl << 2; | iphlen = ip->ip_hl << 2; | ||||
if (iphlen < sizeof(*ip)) | if (iphlen < sizeof(*ip)) | ||||
break; | break; | ||||
off += iphlen; | off += iphlen; | ||||
ports = m_ether_tcpip_hash_gethdr(m, | ports = m_common_hash_gethdr(m, | ||||
off, sizeof(*ports), &buf); | off, sizeof(*ports), &buf); | ||||
if (ports == NULL) | if (ports == NULL) | ||||
break; | break; | ||||
p = fnv_32_buf(ports, sizeof(*ports), p); | p = fnv_32_buf(ports, sizeof(*ports), p); | ||||
break; | break; | ||||
default: | default: | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
break; | break; | ||||
#endif | #endif | ||||
#ifdef INET6 | #ifdef INET6 | ||||
case ETHERTYPE_IPV6: | case ETHERTYPE_IPV6: | ||||
ip6 = m_ether_tcpip_hash_gethdr(m, off, sizeof(*ip6), &buf); | ip6 = m_common_hash_gethdr(m, off, sizeof(*ip6), &buf); | ||||
if (ip6 == NULL) | if (ip6 == NULL) | ||||
break; | break; | ||||
if (flags & MBUF_HASHFLAG_L3) { | if (flags & MBUF_HASHFLAG_L3) { | ||||
p = fnv_32_buf(&ip6->ip6_src, sizeof(struct in6_addr), p); | p = fnv_32_buf(&ip6->ip6_src, sizeof(struct in6_addr), p); | ||||
p = fnv_32_buf(&ip6->ip6_dst, sizeof(struct in6_addr), p); | p = fnv_32_buf(&ip6->ip6_dst, sizeof(struct in6_addr), p); | ||||
} | } | ||||
if (flags & MBUF_HASHFLAG_L4) { | if (flags & MBUF_HASHFLAG_L4) { | ||||
uint32_t flow; | uint32_t flow; | ||||
/* IPv6 flow label */ | /* IPv6 flow label */ | ||||
flow = ip6->ip6_flow & IPV6_FLOWLABEL_MASK; | flow = ip6->ip6_flow & IPV6_FLOWLABEL_MASK; | ||||
p = fnv_32_buf(&flow, sizeof(flow), p); | p = fnv_32_buf(&flow, sizeof(flow), p); | ||||
} | } | ||||
break; | break; | ||||
#endif | #endif | ||||
default: | default: | ||||
break; | break; | ||||
} | } | ||||
done: | |||||
return (p); | return (p); | ||||
} | |||||
uint32_t | |||||
m_ether_tcpip_hash(const uint32_t flags, const struct mbuf *m, | |||||
uint32_t p) | |||||
{ | |||||
union { | |||||
struct ether_vlan_header vlan; | |||||
} buf; | |||||
const struct ether_header *eh; | |||||
const struct ether_vlan_header *vlan; | |||||
int off; | |||||
uint16_t etype; | |||||
off = sizeof(*eh); | |||||
if (m->m_len < off) | |||||
return (p); | |||||
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_common_hash_gethdr(m, off, sizeof(*vlan), &buf); | |||||
if (vlan == NULL) | |||||
return (p); | |||||
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); | |||||
} | |||||
melifaro: I'm wondering if if would make sense to try to have L3+ hash calculation common for ethernet… | |||||
return (m_tcpip_hash(flags, m, p, off, etype)); | |||||
} | |||||
uint32_t | |||||
m_infiniband_tcpip_hash(const uint32_t flags, const struct mbuf *m, | |||||
uint32_t p) | |||||
{ | |||||
const struct infiniband_header *ibh; | |||||
int off; | |||||
uint16_t etype; | |||||
off = sizeof(*ibh); | |||||
if (m->m_len < off) | |||||
return (p); | |||||
ibh = mtod(m, struct infiniband_header *); | |||||
etype = ntohs(ibh->ib_protocol); | |||||
if (flags & MBUF_HASHFLAG_L2) | |||||
p = fnv_32_buf(&ibh->ib_hwaddr, INFINIBAND_ADDR_LEN, p); | |||||
return (m_tcpip_hash(flags, m, p, off, etype)); | |||||
} | } |
I'm wondering if if would make sense to try to have L3+ hash calculation common for ethernet and IB - from the first glance offset is the only difference. What do you think?