xref: /freebsd/sys/kern/uipc_mbufhash.c (revision b7ba031f)
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