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
45static const void *
46m_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
59uint32_t
60m_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
68uint32_t
69m_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
77static inline uint32_t
78m_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
156uint32_t
157m_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
194uint32_t
195m_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