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