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 __FBSDID("$FreeBSD$"); 22 23 #include "opt_inet.h" 24 #include "opt_inet6.h" 25 26 #include <sys/param.h> 27 #include <sys/mbuf.h> 28 #include <sys/fnv_hash.h> 29 30 #include <net/ethernet.h> 31 32 #if defined(INET) || defined(INET6) 33 #include <netinet/in.h> 34 #endif 35 36 #ifdef INET 37 #include <netinet/ip.h> 38 #endif 39 40 #ifdef INET6 41 #include <netinet/ip6.h> 42 #endif 43 44 static const void * 45 m_ether_tcpip_hash_gethdr(const struct mbuf *m, const u_int off, 46 const u_int len, void *buf) 47 { 48 49 if (m->m_pkthdr.len < (off + len)) { 50 return (NULL); 51 } else if (m->m_len < (off + len)) { 52 m_copydata(m, off, len, buf); 53 return (buf); 54 } 55 return (mtod(m, char *) + off); 56 } 57 58 uint32_t 59 m_ether_tcpip_hash_init(void) 60 { 61 uint32_t seed; 62 63 seed = arc4random(); 64 return (fnv_32_buf(&seed, sizeof(seed), FNV1_32_INIT)); 65 } 66 67 uint32_t 68 m_ether_tcpip_hash(const uint32_t flags, const struct mbuf *m, 69 const uint32_t key) 70 { 71 union { 72 #ifdef INET 73 struct ip ip; 74 #endif 75 #ifdef INET6 76 struct ip6_hdr ip6; 77 #endif 78 struct ether_vlan_header vlan; 79 uint32_t port; 80 } buf; 81 struct ether_header *eh; 82 const struct ether_vlan_header *vlan; 83 #ifdef INET 84 const struct ip *ip; 85 #endif 86 #ifdef INET6 87 const struct ip6_hdr *ip6; 88 #endif 89 uint32_t p; 90 int off; 91 uint16_t etype; 92 93 p = key; 94 off = sizeof(*eh); 95 if (m->m_len < off) 96 goto done; 97 eh = mtod(m, struct ether_header *); 98 etype = ntohs(eh->ether_type); 99 if (flags & MBUF_HASHFLAG_L2) { 100 p = fnv_32_buf(&eh->ether_shost, ETHER_ADDR_LEN, p); 101 p = fnv_32_buf(&eh->ether_dhost, ETHER_ADDR_LEN, p); 102 } 103 /* Special handling for encapsulating VLAN frames */ 104 if ((m->m_flags & M_VLANTAG) && (flags & MBUF_HASHFLAG_L2)) { 105 p = fnv_32_buf(&m->m_pkthdr.ether_vtag, 106 sizeof(m->m_pkthdr.ether_vtag), p); 107 } else if (etype == ETHERTYPE_VLAN) { 108 vlan = m_ether_tcpip_hash_gethdr(m, off, sizeof(*vlan), &buf); 109 if (vlan == NULL) 110 goto done; 111 112 if (flags & MBUF_HASHFLAG_L2) 113 p = fnv_32_buf(&vlan->evl_tag, sizeof(vlan->evl_tag), p); 114 etype = ntohs(vlan->evl_proto); 115 off += sizeof(*vlan) - sizeof(*eh); 116 } 117 switch (etype) { 118 #ifdef INET 119 case ETHERTYPE_IP: 120 ip = m_ether_tcpip_hash_gethdr(m, off, sizeof(*ip), &buf); 121 if (ip == NULL) 122 break; 123 if (flags & MBUF_HASHFLAG_L3) { 124 p = fnv_32_buf(&ip->ip_src, sizeof(struct in_addr), p); 125 p = fnv_32_buf(&ip->ip_dst, sizeof(struct in_addr), p); 126 } 127 if (flags & MBUF_HASHFLAG_L4) { 128 const uint32_t *ports; 129 int iphlen; 130 131 switch (ip->ip_p) { 132 case IPPROTO_TCP: 133 case IPPROTO_UDP: 134 case IPPROTO_SCTP: 135 iphlen = ip->ip_hl << 2; 136 if (iphlen < sizeof(*ip)) 137 break; 138 off += iphlen; 139 ports = m_ether_tcpip_hash_gethdr(m, 140 off, sizeof(*ports), &buf); 141 if (ports == NULL) 142 break; 143 p = fnv_32_buf(ports, sizeof(*ports), p); 144 break; 145 default: 146 break; 147 } 148 } 149 break; 150 #endif 151 #ifdef INET6 152 case ETHERTYPE_IPV6: 153 ip6 = m_ether_tcpip_hash_gethdr(m, off, sizeof(*ip6), &buf); 154 if (ip6 == NULL) 155 break; 156 if (flags & MBUF_HASHFLAG_L3) { 157 p = fnv_32_buf(&ip6->ip6_src, sizeof(struct in6_addr), p); 158 p = fnv_32_buf(&ip6->ip6_dst, sizeof(struct in6_addr), p); 159 } 160 if (flags & MBUF_HASHFLAG_L4) { 161 uint32_t flow; 162 163 /* IPv6 flow label */ 164 flow = ip6->ip6_flow & IPV6_FLOWLABEL_MASK; 165 p = fnv_32_buf(&flow, sizeof(flow), p); 166 } 167 break; 168 #endif 169 default: 170 break; 171 } 172 done: 173 return (p); 174 } 175