1d30ea906Sjfb8856606 /* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
2d30ea906Sjfb8856606 * Copyright 2017 Mellanox Technologies, Ltd
3d30ea906Sjfb8856606 */
4d30ea906Sjfb8856606
5d30ea906Sjfb8856606 #include <stdint.h>
6d30ea906Sjfb8856606 #include <stdbool.h>
7d30ea906Sjfb8856606 #include <sys/types.h>
8d30ea906Sjfb8856606 #include <sys/socket.h>
9d30ea906Sjfb8856606 #include <asm/types.h>
10d30ea906Sjfb8856606 #include <linux/in.h>
11d30ea906Sjfb8856606 #include <linux/if.h>
12d30ea906Sjfb8856606 #include <linux/if_ether.h>
13d30ea906Sjfb8856606 #include <linux/ip.h>
14d30ea906Sjfb8856606 #include <linux/ipv6.h>
15d30ea906Sjfb8856606 #include <linux/if_tunnel.h>
16d30ea906Sjfb8856606 #include <linux/filter.h>
17d30ea906Sjfb8856606 #include <linux/bpf.h>
18d30ea906Sjfb8856606
19d30ea906Sjfb8856606 #include "tap_rss.h"
20d30ea906Sjfb8856606
21d30ea906Sjfb8856606 /** Create IPv4 address */
22d30ea906Sjfb8856606 #define IPv4(a, b, c, d) ((__u32)(((a) & 0xff) << 24) | \
23d30ea906Sjfb8856606 (((b) & 0xff) << 16) | \
24d30ea906Sjfb8856606 (((c) & 0xff) << 8) | \
25d30ea906Sjfb8856606 ((d) & 0xff))
26d30ea906Sjfb8856606
27d30ea906Sjfb8856606 #define PORT(a, b) ((__u16)(((a) & 0xff) << 8) | \
28d30ea906Sjfb8856606 ((b) & 0xff))
29d30ea906Sjfb8856606
30d30ea906Sjfb8856606 /*
31d30ea906Sjfb8856606 * The queue number is offset by a unique QUEUE_OFFSET, to distinguish
32d30ea906Sjfb8856606 * packets that have gone through this rule (skb->cb[1] != 0) from others.
33d30ea906Sjfb8856606 */
34d30ea906Sjfb8856606 #define QUEUE_OFFSET 0x7cafe800
35d30ea906Sjfb8856606 #define PIN_GLOBAL_NS 2
36d30ea906Sjfb8856606
37d30ea906Sjfb8856606 #define KEY_IDX 0
38d30ea906Sjfb8856606 #define BPF_MAP_ID_KEY 1
39d30ea906Sjfb8856606
40d30ea906Sjfb8856606 struct vlan_hdr {
41d30ea906Sjfb8856606 __be16 proto;
42d30ea906Sjfb8856606 __be16 tci;
43d30ea906Sjfb8856606 };
44d30ea906Sjfb8856606
45d30ea906Sjfb8856606 struct bpf_elf_map __attribute__((section("maps"), used))
46d30ea906Sjfb8856606 map_keys = {
47d30ea906Sjfb8856606 .type = BPF_MAP_TYPE_HASH,
48d30ea906Sjfb8856606 .id = BPF_MAP_ID_KEY,
49d30ea906Sjfb8856606 .size_key = sizeof(__u32),
50d30ea906Sjfb8856606 .size_value = sizeof(struct rss_key),
51d30ea906Sjfb8856606 .max_elem = 256,
52d30ea906Sjfb8856606 .pinning = PIN_GLOBAL_NS,
53d30ea906Sjfb8856606 };
54d30ea906Sjfb8856606
55d30ea906Sjfb8856606 __section("cls_q") int
match_q(struct __sk_buff * skb)56d30ea906Sjfb8856606 match_q(struct __sk_buff *skb)
57d30ea906Sjfb8856606 {
58d30ea906Sjfb8856606 __u32 queue = skb->cb[1];
59d30ea906Sjfb8856606 volatile __u32 q = 0xdeadbeef;
60d30ea906Sjfb8856606 __u32 match_queue = QUEUE_OFFSET + q;
61d30ea906Sjfb8856606
62d30ea906Sjfb8856606 /* printt("match_q$i() queue = %d\n", queue); */
63d30ea906Sjfb8856606
64d30ea906Sjfb8856606 if (queue != match_queue)
65d30ea906Sjfb8856606 return TC_ACT_OK;
66d30ea906Sjfb8856606
67d30ea906Sjfb8856606 /* queue match */
68d30ea906Sjfb8856606 skb->cb[1] = 0;
69d30ea906Sjfb8856606 return TC_ACT_UNSPEC;
70d30ea906Sjfb8856606 }
71d30ea906Sjfb8856606
72d30ea906Sjfb8856606
73d30ea906Sjfb8856606 struct ipv4_l3_l4_tuple {
74d30ea906Sjfb8856606 __u32 src_addr;
75d30ea906Sjfb8856606 __u32 dst_addr;
76d30ea906Sjfb8856606 __u16 dport;
77d30ea906Sjfb8856606 __u16 sport;
78*2d9fd380Sjfb8856606 } __rte_packed;
79d30ea906Sjfb8856606
80d30ea906Sjfb8856606 struct ipv6_l3_l4_tuple {
81d30ea906Sjfb8856606 __u8 src_addr[16];
82d30ea906Sjfb8856606 __u8 dst_addr[16];
83d30ea906Sjfb8856606 __u16 dport;
84d30ea906Sjfb8856606 __u16 sport;
85*2d9fd380Sjfb8856606 } __rte_packed;
86d30ea906Sjfb8856606
87d30ea906Sjfb8856606 static const __u8 def_rss_key[TAP_RSS_HASH_KEY_SIZE] = {
88d30ea906Sjfb8856606 0xd1, 0x81, 0xc6, 0x2c,
89d30ea906Sjfb8856606 0xf7, 0xf4, 0xdb, 0x5b,
90d30ea906Sjfb8856606 0x19, 0x83, 0xa2, 0xfc,
91d30ea906Sjfb8856606 0x94, 0x3e, 0x1a, 0xdb,
92d30ea906Sjfb8856606 0xd9, 0x38, 0x9e, 0x6b,
93d30ea906Sjfb8856606 0xd1, 0x03, 0x9c, 0x2c,
94d30ea906Sjfb8856606 0xa7, 0x44, 0x99, 0xad,
95d30ea906Sjfb8856606 0x59, 0x3d, 0x56, 0xd9,
96d30ea906Sjfb8856606 0xf3, 0x25, 0x3c, 0x06,
97d30ea906Sjfb8856606 0x2a, 0xdc, 0x1f, 0xfc,
98d30ea906Sjfb8856606 };
99d30ea906Sjfb8856606
100d30ea906Sjfb8856606 static __u32 __attribute__((always_inline))
rte_softrss_be(const __u32 * input_tuple,const uint8_t * rss_key,__u8 input_len)101d30ea906Sjfb8856606 rte_softrss_be(const __u32 *input_tuple, const uint8_t *rss_key,
102d30ea906Sjfb8856606 __u8 input_len)
103d30ea906Sjfb8856606 {
104d30ea906Sjfb8856606 __u32 i, j, hash = 0;
105d30ea906Sjfb8856606 #pragma unroll
106d30ea906Sjfb8856606 for (j = 0; j < input_len; j++) {
107d30ea906Sjfb8856606 #pragma unroll
108d30ea906Sjfb8856606 for (i = 0; i < 32; i++) {
1091646932aSjfb8856606 if (input_tuple[j] & (1U << (31 - i))) {
110d30ea906Sjfb8856606 hash ^= ((const __u32 *)def_rss_key)[j] << i |
111d30ea906Sjfb8856606 (__u32)((uint64_t)
112d30ea906Sjfb8856606 (((const __u32 *)def_rss_key)[j + 1])
113d30ea906Sjfb8856606 >> (32 - i));
114d30ea906Sjfb8856606 }
115d30ea906Sjfb8856606 }
116d30ea906Sjfb8856606 }
117d30ea906Sjfb8856606 return hash;
118d30ea906Sjfb8856606 }
119d30ea906Sjfb8856606
120d30ea906Sjfb8856606 static int __attribute__((always_inline))
rss_l3_l4(struct __sk_buff * skb)121d30ea906Sjfb8856606 rss_l3_l4(struct __sk_buff *skb)
122d30ea906Sjfb8856606 {
123d30ea906Sjfb8856606 void *data_end = (void *)(long)skb->data_end;
124d30ea906Sjfb8856606 void *data = (void *)(long)skb->data;
125d30ea906Sjfb8856606 __u16 proto = (__u16)skb->protocol;
126d30ea906Sjfb8856606 __u32 key_idx = 0xdeadbeef;
127d30ea906Sjfb8856606 __u32 hash;
128d30ea906Sjfb8856606 struct rss_key *rsskey;
129d30ea906Sjfb8856606 __u64 off = ETH_HLEN;
130d30ea906Sjfb8856606 int j;
131d30ea906Sjfb8856606 __u8 *key = 0;
132d30ea906Sjfb8856606 __u32 len;
133d30ea906Sjfb8856606 __u32 queue = 0;
134d30ea906Sjfb8856606
135d30ea906Sjfb8856606 rsskey = map_lookup_elem(&map_keys, &key_idx);
136d30ea906Sjfb8856606 if (!rsskey) {
137d30ea906Sjfb8856606 printt("hash(): rss key is not configured\n");
138d30ea906Sjfb8856606 return TC_ACT_OK;
139d30ea906Sjfb8856606 }
140d30ea906Sjfb8856606 key = (__u8 *)rsskey->key;
141d30ea906Sjfb8856606
142d30ea906Sjfb8856606 /* Get correct proto for 802.1ad */
143d30ea906Sjfb8856606 if (skb->vlan_present && skb->vlan_proto == htons(ETH_P_8021AD)) {
144d30ea906Sjfb8856606 if (data + ETH_ALEN * 2 + sizeof(struct vlan_hdr) +
145d30ea906Sjfb8856606 sizeof(proto) > data_end)
146d30ea906Sjfb8856606 return TC_ACT_OK;
147d30ea906Sjfb8856606 proto = *(__u16 *)(data + ETH_ALEN * 2 +
148d30ea906Sjfb8856606 sizeof(struct vlan_hdr));
149d30ea906Sjfb8856606 off += sizeof(struct vlan_hdr);
150d30ea906Sjfb8856606 }
151d30ea906Sjfb8856606
152d30ea906Sjfb8856606 if (proto == htons(ETH_P_IP)) {
153d30ea906Sjfb8856606 if (data + off + sizeof(struct iphdr) + sizeof(__u32)
154d30ea906Sjfb8856606 > data_end)
155d30ea906Sjfb8856606 return TC_ACT_OK;
156d30ea906Sjfb8856606
157d30ea906Sjfb8856606 __u8 *src_dst_addr = data + off + offsetof(struct iphdr, saddr);
158d30ea906Sjfb8856606 __u8 *src_dst_port = data + off + sizeof(struct iphdr);
159d30ea906Sjfb8856606 struct ipv4_l3_l4_tuple v4_tuple = {
160d30ea906Sjfb8856606 .src_addr = IPv4(*(src_dst_addr + 0),
161d30ea906Sjfb8856606 *(src_dst_addr + 1),
162d30ea906Sjfb8856606 *(src_dst_addr + 2),
163d30ea906Sjfb8856606 *(src_dst_addr + 3)),
164d30ea906Sjfb8856606 .dst_addr = IPv4(*(src_dst_addr + 4),
165d30ea906Sjfb8856606 *(src_dst_addr + 5),
166d30ea906Sjfb8856606 *(src_dst_addr + 6),
167d30ea906Sjfb8856606 *(src_dst_addr + 7)),
168d30ea906Sjfb8856606 .sport = PORT(*(src_dst_port + 0),
169d30ea906Sjfb8856606 *(src_dst_port + 1)),
170d30ea906Sjfb8856606 .dport = PORT(*(src_dst_port + 2),
171d30ea906Sjfb8856606 *(src_dst_port + 3)),
172d30ea906Sjfb8856606 };
173d30ea906Sjfb8856606 __u8 input_len = sizeof(v4_tuple) / sizeof(__u32);
174d30ea906Sjfb8856606 if (rsskey->hash_fields & (1 << HASH_FIELD_IPV4_L3))
175d30ea906Sjfb8856606 input_len--;
176d30ea906Sjfb8856606 hash = rte_softrss_be((__u32 *)&v4_tuple, key, 3);
177d30ea906Sjfb8856606 } else if (proto == htons(ETH_P_IPV6)) {
178d30ea906Sjfb8856606 if (data + off + sizeof(struct ipv6hdr) +
179d30ea906Sjfb8856606 sizeof(__u32) > data_end)
180d30ea906Sjfb8856606 return TC_ACT_OK;
181d30ea906Sjfb8856606 __u8 *src_dst_addr = data + off +
182d30ea906Sjfb8856606 offsetof(struct ipv6hdr, saddr);
183d30ea906Sjfb8856606 __u8 *src_dst_port = data + off +
184d30ea906Sjfb8856606 sizeof(struct ipv6hdr);
185d30ea906Sjfb8856606 struct ipv6_l3_l4_tuple v6_tuple;
186d30ea906Sjfb8856606 for (j = 0; j < 4; j++)
187d30ea906Sjfb8856606 *((uint32_t *)&v6_tuple.src_addr + j) =
188d30ea906Sjfb8856606 __builtin_bswap32(*((uint32_t *)
189d30ea906Sjfb8856606 src_dst_addr + j));
190d30ea906Sjfb8856606 for (j = 0; j < 4; j++)
191d30ea906Sjfb8856606 *((uint32_t *)&v6_tuple.dst_addr + j) =
192d30ea906Sjfb8856606 __builtin_bswap32(*((uint32_t *)
193d30ea906Sjfb8856606 src_dst_addr + 4 + j));
194d30ea906Sjfb8856606 v6_tuple.sport = PORT(*(src_dst_port + 0),
195d30ea906Sjfb8856606 *(src_dst_port + 1));
196d30ea906Sjfb8856606 v6_tuple.dport = PORT(*(src_dst_port + 2),
197d30ea906Sjfb8856606 *(src_dst_port + 3));
198d30ea906Sjfb8856606
199d30ea906Sjfb8856606 __u8 input_len = sizeof(v6_tuple) / sizeof(__u32);
200d30ea906Sjfb8856606 if (rsskey->hash_fields & (1 << HASH_FIELD_IPV6_L3))
201d30ea906Sjfb8856606 input_len--;
202d30ea906Sjfb8856606 hash = rte_softrss_be((__u32 *)&v6_tuple, key, 9);
203d30ea906Sjfb8856606 } else {
204d30ea906Sjfb8856606 return TC_ACT_PIPE;
205d30ea906Sjfb8856606 }
206d30ea906Sjfb8856606
207d30ea906Sjfb8856606 queue = rsskey->queues[(hash % rsskey->nb_queues) &
208d30ea906Sjfb8856606 (TAP_MAX_QUEUES - 1)];
209d30ea906Sjfb8856606 skb->cb[1] = QUEUE_OFFSET + queue;
210d30ea906Sjfb8856606 /* printt(">>>>> rss_l3_l4 hash=0x%x queue=%u\n", hash, queue); */
211d30ea906Sjfb8856606
212d30ea906Sjfb8856606 return TC_ACT_RECLASSIFY;
213d30ea906Sjfb8856606 }
214d30ea906Sjfb8856606
215d30ea906Sjfb8856606 #define RSS(L) \
216d30ea906Sjfb8856606 __section(#L) int \
217d30ea906Sjfb8856606 L ## _hash(struct __sk_buff *skb) \
218d30ea906Sjfb8856606 { \
219d30ea906Sjfb8856606 return rss_ ## L (skb); \
220d30ea906Sjfb8856606 }
221d30ea906Sjfb8856606
222d30ea906Sjfb8856606 RSS(l3_l4)
223d30ea906Sjfb8856606
224d30ea906Sjfb8856606 BPF_LICENSE("Dual BSD/GPL");
225