xref: /freebsd/sys/kern/uipc_mbufhash.c (revision 685dc743)
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 *
m_common_hash_gethdr(const struct mbuf * m,const u_int off,const u_int len,void * buf)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
m_ether_tcpip_hash_init(void)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
m_infiniband_tcpip_hash_init(void)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
m_tcpip_hash(const uint32_t flags,const struct mbuf * m,uint32_t p,int off,const uint16_t etype)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
m_ether_tcpip_hash(const uint32_t flags,const struct mbuf * m,uint32_t p)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
m_infiniband_tcpip_hash(const uint32_t flags,const struct mbuf * m,uint32_t p)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