1 /* $OpenBSD: if_trunk.c,v 1.30 2007/01/31 06:20:19 reyk Exp $ */ 2 3 /* 4 * Copyright (c) 2005, 2006 Reyk Floeter <reyk@openbsd.org> 5 * Copyright (c) 2007 Andrew Thompson <thompsa@FreeBSD.org> 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 #include <sys/cdefs.h> 21 #include "opt_inet.h" 22 #include "opt_inet6.h" 23 24 #include <sys/param.h> 25 #include <sys/mbuf.h> 26 #include <sys/fnv_hash.h> 27 28 #include <net/ethernet.h> 29 #include <net/infiniband.h> 30 31 #if defined(INET) || defined(INET6) 32 #include <netinet/in.h> 33 #endif 34 35 #ifdef INET 36 #include <netinet/ip.h> 37 #endif 38 39 #ifdef INET6 40 #include <netinet/ip6.h> 41 #endif 42 43 static const void * 44 m_common_hash_gethdr(const struct mbuf *m, const u_int off, 45 const u_int len, void *buf) 46 { 47 48 if (m->m_pkthdr.len < (off + len)) { 49 return (NULL); 50 } else if (m->m_len < (off + len)) { 51 m_copydata(m, off, len, buf); 52 return (buf); 53 } 54 return (mtod(m, char *) + off); 55 } 56 57 uint32_t 58 m_ether_tcpip_hash_init(void) 59 { 60 uint32_t seed; 61 62 seed = arc4random(); 63 return (fnv_32_buf(&seed, sizeof(seed), FNV1_32_INIT)); 64 } 65 66 uint32_t 67 m_infiniband_tcpip_hash_init(void) 68 { 69 uint32_t seed; 70 71 seed = arc4random(); 72 return (fnv_32_buf(&seed, sizeof(seed), FNV1_32_INIT)); 73 } 74 75 static inline uint32_t 76 m_tcpip_hash(const uint32_t flags, const struct mbuf *m, 77 uint32_t p, int off, const uint16_t etype) 78 { 79 #if defined(INET) || defined(INET6) 80 union { 81 #ifdef INET 82 struct ip ip; 83 #endif 84 #ifdef INET6 85 struct ip6_hdr ip6; 86 #endif 87 uint32_t port; 88 } buf; 89 #ifdef INET 90 const struct ip *ip; 91 #endif 92 #ifdef INET6 93 const struct ip6_hdr *ip6; 94 #endif 95 #endif 96 switch (etype) { 97 #ifdef INET 98 case ETHERTYPE_IP: 99 ip = m_common_hash_gethdr(m, off, sizeof(*ip), &buf); 100 if (ip == NULL) 101 break; 102 if (flags & MBUF_HASHFLAG_L3) { 103 p = fnv_32_buf(&ip->ip_src, sizeof(struct in_addr), p); 104 p = fnv_32_buf(&ip->ip_dst, sizeof(struct in_addr), p); 105 } 106 if (flags & MBUF_HASHFLAG_L4) { 107 const uint32_t *ports; 108 int iphlen; 109 110 switch (ip->ip_p) { 111 case IPPROTO_TCP: 112 case IPPROTO_UDP: 113 case IPPROTO_SCTP: 114 iphlen = ip->ip_hl << 2; 115 if (iphlen < sizeof(*ip)) 116 break; 117 off += iphlen; 118 ports = m_common_hash_gethdr(m, 119 off, sizeof(*ports), &buf); 120 if (ports == NULL) 121 break; 122 p = fnv_32_buf(ports, sizeof(*ports), p); 123 break; 124 default: 125 break; 126 } 127 } 128 break; 129 #endif 130 #ifdef INET6 131 case ETHERTYPE_IPV6: 132 ip6 = m_common_hash_gethdr(m, off, sizeof(*ip6), &buf); 133 if (ip6 == NULL) 134 break; 135 if (flags & MBUF_HASHFLAG_L3) { 136 p = fnv_32_buf(&ip6->ip6_src, sizeof(struct in6_addr), p); 137 p = fnv_32_buf(&ip6->ip6_dst, sizeof(struct in6_addr), p); 138 } 139 if (flags & MBUF_HASHFLAG_L4) { 140 uint32_t flow; 141 142 /* IPv6 flow label */ 143 flow = ip6->ip6_flow & IPV6_FLOWLABEL_MASK; 144 p = fnv_32_buf(&flow, sizeof(flow), p); 145 } 146 break; 147 #endif 148 default: 149 break; 150 } 151 return (p); 152 } 153 154 uint32_t 155 m_ether_tcpip_hash(const uint32_t flags, const struct mbuf *m, 156 uint32_t p) 157 { 158 union { 159 struct ether_vlan_header vlan; 160 } buf; 161 const struct ether_header *eh; 162 const struct ether_vlan_header *vlan; 163 int off; 164 uint16_t etype; 165 166 off = sizeof(*eh); 167 if (m->m_len < off) 168 return (p); 169 eh = mtod(m, struct ether_header *); 170 etype = ntohs(eh->ether_type); 171 if (flags & MBUF_HASHFLAG_L2) { 172 p = fnv_32_buf(&eh->ether_shost, ETHER_ADDR_LEN, p); 173 p = fnv_32_buf(&eh->ether_dhost, ETHER_ADDR_LEN, p); 174 } 175 /* Special handling for encapsulating VLAN frames */ 176 if ((m->m_flags & M_VLANTAG) && (flags & MBUF_HASHFLAG_L2)) { 177 p = fnv_32_buf(&m->m_pkthdr.ether_vtag, 178 sizeof(m->m_pkthdr.ether_vtag), p); 179 } else if (etype == ETHERTYPE_VLAN) { 180 vlan = m_common_hash_gethdr(m, off, sizeof(*vlan), &buf); 181 if (vlan == NULL) 182 return (p); 183 184 if (flags & MBUF_HASHFLAG_L2) 185 p = fnv_32_buf(&vlan->evl_tag, sizeof(vlan->evl_tag), p); 186 etype = ntohs(vlan->evl_proto); 187 off += sizeof(*vlan) - sizeof(*eh); 188 } 189 return (m_tcpip_hash(flags, m, p, off, etype)); 190 } 191 192 uint32_t 193 m_infiniband_tcpip_hash(const uint32_t flags, const struct mbuf *m, 194 uint32_t p) 195 { 196 const struct infiniband_header *ibh; 197 int off; 198 uint16_t etype; 199 200 off = sizeof(*ibh); 201 if (m->m_len < off) 202 return (p); 203 ibh = mtod(m, struct infiniband_header *); 204 etype = ntohs(ibh->ib_protocol); 205 if (flags & MBUF_HASHFLAG_L2) 206 p = fnv_32_buf(&ibh->ib_hwaddr, INFINIBAND_ADDR_LEN, p); 207 208 return (m_tcpip_hash(flags, m, p, off, etype)); 209 } 210