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