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: stable/11/sys/kern/uipc_mbufhash.c 366885 2020-10-20 08:31:55Z hselasky $");
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
32#if defined(INET) || defined(INET6)
33#include <netinet/in.h>
34#endif
35
36#ifdef INET
37#include <netinet/ip.h>
38#endif
39
40#ifdef INET6
41#include <netinet/ip6.h>
42#endif
43
44static const void *
45m_ether_tcpip_hash_gethdr(const struct mbuf *m, const u_int off,
46    const u_int len, void *buf)
47{
48
49	if (m->m_pkthdr.len < (off + len)) {
50		return (NULL);
51	} else if (m->m_len < (off + len)) {
52		m_copydata(m, off, len, buf);
53		return (buf);
54	}
55	return (mtod(m, char *) + off);
56}
57
58uint32_t
59m_ether_tcpip_hash_init(void)
60{
61	uint32_t seed;
62
63	seed = arc4random();
64	return (fnv_32_buf(&seed, sizeof(seed), FNV1_32_INIT));
65}
66
67uint32_t
68m_ether_tcpip_hash(const uint32_t flags, const struct mbuf *m,
69    const uint32_t key)
70{
71	union {
72#ifdef INET
73		struct ip ip;
74#endif
75#ifdef INET6
76		struct ip6_hdr ip6;
77#endif
78		struct ether_vlan_header vlan;
79		uint32_t port;
80	} buf;
81	const struct ether_header *eh;
82	const struct ether_vlan_header *vlan;
83#ifdef INET
84	const struct ip *ip;
85#endif
86#ifdef INET6
87	const struct ip6_hdr *ip6;
88#endif
89	uint32_t p;
90	int off;
91	uint16_t etype;
92
93	p = key;
94	off = sizeof(*eh);
95	if (m->m_len < off)
96		goto done;
97	eh = mtod(m, struct ether_header *);
98	etype = ntohs(eh->ether_type);
99	if (flags & MBUF_HASHFLAG_L2) {
100		p = fnv_32_buf(&eh->ether_shost, ETHER_ADDR_LEN, p);
101		p = fnv_32_buf(&eh->ether_dhost, ETHER_ADDR_LEN, p);
102	}
103	/* Special handling for encapsulating VLAN frames */
104	if ((m->m_flags & M_VLANTAG) && (flags & MBUF_HASHFLAG_L2)) {
105		p = fnv_32_buf(&m->m_pkthdr.ether_vtag,
106		    sizeof(m->m_pkthdr.ether_vtag), p);
107	} else if (etype == ETHERTYPE_VLAN) {
108		vlan = m_ether_tcpip_hash_gethdr(m, off, sizeof(*vlan), &buf);
109		if (vlan == NULL)
110			goto done;
111
112		if (flags & MBUF_HASHFLAG_L2)
113			p = fnv_32_buf(&vlan->evl_tag, sizeof(vlan->evl_tag), p);
114		etype = ntohs(vlan->evl_proto);
115		off += sizeof(*vlan) - sizeof(*eh);
116	}
117	switch (etype) {
118#ifdef INET
119	case ETHERTYPE_IP:
120		ip = m_ether_tcpip_hash_gethdr(m, off, sizeof(*ip), &buf);
121		if (ip == NULL)
122			break;
123		if (flags & MBUF_HASHFLAG_L3) {
124			p = fnv_32_buf(&ip->ip_src, sizeof(struct in_addr), p);
125			p = fnv_32_buf(&ip->ip_dst, sizeof(struct in_addr), p);
126		}
127		if (flags & MBUF_HASHFLAG_L4) {
128			const uint32_t *ports;
129			int iphlen;
130
131			switch (ip->ip_p) {
132			case IPPROTO_TCP:
133			case IPPROTO_UDP:
134			case IPPROTO_SCTP:
135				iphlen = ip->ip_hl << 2;
136				if (iphlen < sizeof(*ip))
137					break;
138				off += iphlen;
139				ports = m_ether_tcpip_hash_gethdr(m,
140				    off, sizeof(*ports), &buf);
141				if (ports == NULL)
142					break;
143				p = fnv_32_buf(ports, sizeof(*ports), p);
144				break;
145			default:
146				break;
147			}
148		}
149		break;
150#endif
151#ifdef INET6
152	case ETHERTYPE_IPV6:
153		ip6 = m_ether_tcpip_hash_gethdr(m, off, sizeof(*ip6), &buf);
154		if (ip6 == NULL)
155			break;
156		if (flags & MBUF_HASHFLAG_L3) {
157			p = fnv_32_buf(&ip6->ip6_src, sizeof(struct in6_addr), p);
158			p = fnv_32_buf(&ip6->ip6_dst, sizeof(struct in6_addr), p);
159		}
160		if (flags & MBUF_HASHFLAG_L4) {
161			uint32_t flow;
162
163			/* IPv6 flow label */
164			flow = ip6->ip6_flow & IPV6_FLOWLABEL_MASK;
165			p = fnv_32_buf(&flow, sizeof(flow), p);
166		}
167		break;
168#endif
169	default:
170		break;
171	}
172done:
173	return (p);
174}
175