1/*	$NetBSD: rss_config.c,v 1.3 2021/09/24 04:11:02 knakahara Exp $  */
2
3/*
4 * Copyright (c) 2018 Internet Initiative Japan Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include <sys/cdefs.h>
30__KERNEL_RCSID(0, "$NetBSD: rss_config.c,v 1.3 2021/09/24 04:11:02 knakahara Exp $");
31
32#include <sys/param.h>
33#include <sys/systm.h>
34#include <sys/kernel.h>
35#include <sys/mbuf.h>
36
37#include <net/rss_config.h>
38#include <net/toeplitz.h>
39
40#include <netinet/in.h>
41#include <netinet/ip.h>
42#include <netinet/tcp.h>
43#include <netinet/udp.h>
44#include <netinet/ip6.h>
45
46/*
47 * Same as FreeBSD.
48 *
49 * This rss key is assumed for verification suite in many intel Gigabit and
50 * 10 Gigabit Controller specifications.
51 */
52static uint8_t rss_default_key[RSS_KEYSIZE] = {
53	0x6d, 0x5a, 0x56, 0xda, 0x25, 0x5b, 0x0e, 0xc2,
54	0x41, 0x67, 0x25, 0x3d, 0x43, 0xa3, 0x8f, 0xb0,
55	0xd0, 0xca, 0x2b, 0xcb, 0xae, 0x7b, 0x30, 0xb4,
56	0x77, 0xcb, 0x2d, 0xa3, 0x80, 0x30, 0xf2, 0x0c,
57	0x6a, 0x42, 0xb7, 0x3b, 0xbe, 0xac, 0x01, 0xfa,
58};
59
60#ifdef NOTYET
61/*
62 * Same as DragonFlyBSD.
63 *
64 * This rss key make rss hash value symmetric, that is, the hash value
65 * calculated by func("source address", "destination address") equals to
66 * the hash value calculated by func("destination address", "source address").
67 */
68static uint8_t rss_symmetric_key[RSS_KEYSIZE] = {
69	0x6d, 0x5a, 0x6d, 0x5a, 0x6d, 0x5a, 0x6d, 0x5a,
70	0x6d, 0x5a, 0x6d, 0x5a, 0x6d, 0x5a, 0x6d, 0x5a,
71	0x6d, 0x5a, 0x6d, 0x5a, 0x6d, 0x5a, 0x6d, 0x5a,
72	0x6d, 0x5a, 0x6d, 0x5a, 0x6d, 0x5a, 0x6d, 0x5a,
73	0x6d, 0x5a, 0x6d, 0x5a, 0x6d, 0x5a, 0x6d, 0x5a,
74};
75#endif
76
77/*
78 * sizeof(key) must be more than or equal to RSS_KEYSIZE.
79 */
80void
81rss_getkey(uint8_t *key)
82{
83
84	memcpy(key, rss_default_key, sizeof(rss_default_key));
85}
86
87/*
88 * Calculate rss hash value from IPv4 mbuf.
89 * This function should be called before ip_input().
90 */
91uint32_t
92rss_toeplitz_hash_from_mbuf_ipv4(const struct mbuf *m, u_int flag)
93{
94	struct ip *ip;
95	int hlen;
96	uint8_t key[RSS_KEYSIZE];
97
98	KASSERT((m->m_flags & M_PKTHDR) != 0);
99	KASSERT(m->m_len >= sizeof (struct ip));
100
101	ip = mtod(m, struct ip *);
102	KASSERT(ip->ip_v == IPVERSION);
103
104	hlen = ip->ip_hl << 2;
105	if (hlen < sizeof(struct ip))
106		return 0;
107
108	rss_getkey(key);
109
110	switch (ip->ip_p) {
111	case IPPROTO_TCP:
112	{
113		if ((flag & RSS_TOEPLITZ_USE_TCP_PORT) != 0) {
114			if (m->m_len >= hlen + sizeof(struct tcphdr)) {
115				struct tcphdr *th;
116
117				th = (struct tcphdr *)(mtod(m, char *) + hlen);
118				return toeplitz_vhash(key, sizeof(key),
119				    /* ip_src and ip_dst in struct ip must be sequential */
120				    &ip->ip_src, sizeof(ip->ip_src) * 2,
121				    /* th_sport and th_dport in tcphdr must be sequential */
122				    &th->th_sport, sizeof(th->th_sport) * 2,
123				    NULL);
124			} else if (m->m_pkthdr.len >= hlen + sizeof(struct tcphdr)) {
125				uint16_t ports[2];
126
127				/* ditto */
128				m_copydata(__UNCONST(m), hlen + offsetof(struct tcphdr, th_sport),
129				    sizeof(ports), ports);
130				return toeplitz_vhash(key, sizeof(key),
131				    &ip->ip_src, sizeof(ip->ip_src) * 2,
132				    ports, sizeof(ports),
133				    NULL);
134			}
135		}
136		/*
137		 * Treat as raw packet.
138		 */
139		return toeplitz_vhash(key, sizeof(key),
140		    /* ditto */
141		    &ip->ip_src, sizeof(ip->ip_src) * 2,
142		    NULL);
143	}
144	case IPPROTO_UDP:
145	{
146		if ((flag & RSS_TOEPLITZ_USE_UDP_PORT) != 0) {
147			if (m->m_len >= hlen + sizeof(struct udphdr)) {
148				struct udphdr *uh;
149
150				uh = (struct udphdr *)(mtod(m, char *) + hlen);
151				return toeplitz_vhash(key, sizeof(key),
152				    /* ip_src and ip_dst in struct ip must sequential */
153				    &ip->ip_src, sizeof(ip->ip_src) * 2,
154				    /* uh_sport and uh_dport in udphdr must be sequential */
155				    &uh->uh_sport, sizeof(uh->uh_sport) * 2,
156				    NULL);
157			} else if (m->m_pkthdr.len >= hlen + sizeof(struct udphdr)) {
158				uint16_t ports[2];
159
160				/* ditto */
161				m_copydata(__UNCONST(m), hlen + offsetof(struct udphdr, uh_sport),
162				    sizeof(ports), ports);
163				return toeplitz_vhash(key, sizeof(key),
164				    &ip->ip_src, sizeof(ip->ip_src) * 2,
165				    ports, sizeof(ports),
166				    NULL);
167			}
168		}
169		/*
170		 * Treat as raw packet.
171		 */
172		return toeplitz_vhash(key, sizeof(key),
173		    /* ditto */
174		    &ip->ip_src, sizeof(ip->ip_src) * 2,
175		    NULL);
176	}
177	/*
178	 * Other protocols are treated as raw packets to apply RPS.
179	 */
180	default:
181		return toeplitz_vhash(key, sizeof(key),
182		    /* ditto */
183		    &ip->ip_src, sizeof(ip->ip_src) * 2,
184		    NULL);
185	}
186}
187
188/*
189 * Calculate rss hash value from IPv6 mbuf.
190 * This function should be called before ip6_input().
191 */
192uint32_t
193rss_toeplitz_hash_from_mbuf_ipv6(const struct mbuf *m, u_int flag)
194{
195	struct ip6_hdr *ip6;
196	int hlen;
197	uint8_t key[RSS_KEYSIZE];
198
199	KASSERT((m->m_flags & M_PKTHDR) != 0);
200	KASSERT(m->m_len >= sizeof (struct ip6_hdr));
201
202	ip6 = mtod(m, struct ip6_hdr *);
203	KASSERT((ip6->ip6_vfc & IPV6_VERSION_MASK) == IPV6_VERSION);
204
205	hlen = sizeof(struct ip6_hdr);
206	rss_getkey(key);
207
208	switch (ip6->ip6_nxt) {
209	case IPPROTO_TCP:
210	{
211		if ((flag & RSS_TOEPLITZ_USE_TCP_PORT) != 0) {
212			if (m->m_len >= hlen + sizeof(struct tcphdr)) {
213				struct tcphdr *th;
214
215				th = (struct tcphdr *)(mtod(m, char *) + hlen);
216				return toeplitz_vhash(key, sizeof(key),
217				    /* ip6_src and ip6_dst in ip6_hdr must be sequential */
218				    &ip6->ip6_src, sizeof(ip6->ip6_src) * 2,
219				    /* th_sport and th_dport in tcphdr must be sequential */
220				    &th->th_sport, sizeof(th->th_sport) * 2,
221				    NULL);
222			} else if (m->m_pkthdr.len >= hlen + sizeof(struct tcphdr)) {
223				uint16_t ports[2];
224
225				/* ditto */
226				m_copydata(__UNCONST(m), hlen + offsetof(struct tcphdr, th_sport),
227				    sizeof(ports), ports);
228				return toeplitz_vhash(key, sizeof(key),
229				    &ip6->ip6_src, sizeof(ip6->ip6_src) * 2,
230				    ports, sizeof(ports),
231				    NULL);
232			}
233		}
234		/*
235		 * Treat as raw packet.
236		 */
237		return toeplitz_vhash(key, sizeof(key),
238		    &ip6->ip6_src, sizeof(ip6->ip6_src) * 2,
239		    NULL);
240	}
241	case IPPROTO_UDP:
242	{
243		if ((flag & RSS_TOEPLITZ_USE_UDP_PORT) != 0) {
244			if (m->m_len >= hlen + sizeof(struct udphdr)) {
245				struct udphdr *uh;
246
247				uh = (struct udphdr *)(mtod(m, char *) + hlen);
248				return toeplitz_vhash(key, sizeof(key),
249				    /* ip6_src and ip6_dst in ip6_hdr must sequential */
250				    &ip6->ip6_src, sizeof(ip6->ip6_src) * 2,
251				    /* uh_sport and uh_dport in udphdr must be sequential */
252				    &uh->uh_sport, sizeof(uh->uh_sport) * 2,
253				    NULL);
254			} else if (m->m_pkthdr.len >= hlen + sizeof(struct udphdr)) {
255				uint16_t ports[2];
256
257				/* ditto */
258				m_copydata(__UNCONST(m), hlen + offsetof(struct udphdr, uh_sport),
259				    sizeof(ports), ports);
260				return toeplitz_vhash(key, sizeof(key),
261				    &ip6->ip6_src, sizeof(ip6->ip6_src) * 2,
262				    &ports, sizeof(ports),
263				    NULL);
264			}
265		}
266		/*
267		 * Treat as raw packet.
268		 */
269		return toeplitz_vhash(key, sizeof(key),
270		    &ip6->ip6_src, sizeof(ip6->ip6_src) * 2,
271		    NULL);
272	}
273	/*
274	 * Other protocols are treated as raw packets to apply RPS.
275	 */
276	default:
277		return toeplitz_vhash(key, sizeof(key),
278		    &ip6->ip6_src, sizeof(ip6->ip6_src) * 2,
279		    NULL);
280	}
281
282	return 0;
283}
284