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
43static const void *
44m_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
57uint32_t
58m_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
66uint32_t
67m_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
75static inline uint32_t
76m_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
154uint32_t
155m_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
192uint32_t
193m_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