| 
									
										
										
										
											2020-12-15 16:49:57 +01:00
										 |  |  | /* SPDX-License-Identifier: GPL-2.0+ */ | 
					
						
							|  |  |  | #include <linux/types.h>
 | 
					
						
							|  |  |  | #include <linux/bpf.h> /* struct bpf_cpumap_val */
 | 
					
						
							|  |  |  | #include <bpf/bpf_helpers.h>
 | 
					
						
							|  |  |  | #include <bpf/compiler.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-16 21:09:28 +01:00
										 |  |  | #define INITVAL 15485863
 | 
					
						
							| 
									
										
										
										
											2020-12-18 20:13:55 +01:00
										 |  |  | //#define INITVAL 2654435761
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-16 21:09:28 +01:00
										 |  |  | #include "hash_func01.h" /* SuperFastHash */
 | 
					
						
							| 
									
										
										
										
											2020-12-15 16:49:57 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | #include <bpf/bpf_helpers.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define VLAN_MAX_DEPTH 2
 | 
					
						
							|  |  |  | #include <xdp/parsing_helpers.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define MAX_CPUS 24
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-16 16:40:15 +01:00
										 |  |  | /* This global variable is used for limiting CPU that can be selected */ | 
					
						
							|  |  |  | __u32 global_max_cpus = 12; /* TODO: Allow userspace to adjust this */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-15 16:49:57 +01:00
										 |  |  | /* Special map type that can XDP_REDIRECT frames to another CPU */ | 
					
						
							|  |  |  | struct { | 
					
						
							|  |  |  | 	__uint(type, BPF_MAP_TYPE_CPUMAP); | 
					
						
							|  |  |  | 	__uint(key_size, sizeof(__u32)); | 
					
						
							|  |  |  | 	__uint(value_size, sizeof(struct bpf_cpumap_val)); | 
					
						
							|  |  |  | 	__uint(max_entries, MAX_CPUS); | 
					
						
							|  |  |  | } cpumap SEC(".maps"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-18 22:09:24 +01:00
										 |  |  | /* Mapping table with CPUs enabled, for hashing between */ | 
					
						
							|  |  |  | struct { | 
					
						
							|  |  |  | 	__uint(type, BPF_MAP_TYPE_ARRAY); | 
					
						
							|  |  |  | 	__type(key, __u32); | 
					
						
							|  |  |  | 	__type(value, __u32); | 
					
						
							|  |  |  | 	__uint(max_entries, MAX_CPUS); | 
					
						
							|  |  |  | } cpus_enabled SEC(".maps"); | 
					
						
							|  |  |  | struct { | 
					
						
							|  |  |  | 	__uint(type, BPF_MAP_TYPE_ARRAY); | 
					
						
							|  |  |  | 	__type(key, __u32); | 
					
						
							|  |  |  | 	__type(value, __u32); | 
					
						
							|  |  |  | 	__uint(max_entries, 1); | 
					
						
							|  |  |  | } cpus_count SEC(".maps"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-16 16:40:15 +01:00
										 |  |  | static __always_inline | 
					
						
							| 
									
										
										
										
											2020-12-16 21:09:28 +01:00
										 |  |  | __u32 extract_vlan_key(struct collect_vlans *vlans) | 
					
						
							| 
									
										
										
										
											2020-12-16 16:40:15 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2020-12-16 21:09:28 +01:00
										 |  |  | 	/* Combine inner and outer VLAN as a key */ | 
					
						
							| 
									
										
										
										
											2020-12-18 20:13:55 +01:00
										 |  |  | 	__u32  vlan_key = (vlans->id[1] << 16) | vlans->id[0]; | 
					
						
							| 
									
										
										
										
											2020-12-16 16:40:15 +01:00
										 |  |  | 	return vlan_key; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-15 16:49:57 +01:00
										 |  |  | SEC("xdp") | 
					
						
							|  |  |  | int  xdp_cpumap_qinq(struct xdp_md *ctx) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	void *data     = (void *)(long)ctx->data; | 
					
						
							|  |  |  | 	void *data_end = (void *)(long)ctx->data_end; | 
					
						
							|  |  |  | 	struct collect_vlans vlans = { 0 }; | 
					
						
							| 
									
										
										
										
											2020-12-16 21:09:28 +01:00
										 |  |  | 	__u32 hash_key, vlan_key; | 
					
						
							| 
									
										
										
										
											2020-12-15 16:49:57 +01:00
										 |  |  | 	struct ethhdr *eth; | 
					
						
							| 
									
										
										
										
											2020-12-18 22:09:24 +01:00
										 |  |  | 	__u32 cpu_idx, cpu_dest = 0; | 
					
						
							|  |  |  | 	__u32 *cpu_lookup; | 
					
						
							| 
									
										
										
										
											2020-12-15 16:49:57 +01:00
										 |  |  | 	__u64 action; | 
					
						
							| 
									
										
										
										
											2020-12-18 22:09:24 +01:00
										 |  |  | 	__u32 *cpu_max; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-15 16:49:57 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* These keep track of the next header type and iterator pointer */ | 
					
						
							|  |  |  | 	struct hdr_cursor nh; | 
					
						
							|  |  |  | 	int eth_type; | 
					
						
							|  |  |  | 	nh.pos = data; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	eth_type = parse_ethhdr_vlan(&nh, data_end, ð, &vlans); | 
					
						
							|  |  |  | 	if (eth_type < 0) { | 
					
						
							|  |  |  | 		action = XDP_ABORTED; | 
					
						
							|  |  |  | 		goto out; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Keep ARP resolution working */ | 
					
						
							|  |  |  | 	if (eth_type == bpf_htons(ETH_P_ARP)) { | 
					
						
							|  |  |  | 		action = XDP_PASS; | 
					
						
							|  |  |  | 		goto out; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!proto_is_vlan(eth->h_proto)) { | 
					
						
							|  |  |  | 		/* Skip non-VLAN frames */ | 
					
						
							|  |  |  | 		action = XDP_PASS; | 
					
						
							|  |  |  | 		goto out; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-18 22:09:24 +01:00
										 |  |  | 	int key0 = 0; | 
					
						
							|  |  |  | 	cpu_max = bpf_map_lookup_elem(&cpus_count, &key0); | 
					
						
							|  |  |  | 	if (!cpu_max) | 
					
						
							|  |  |  | 		return XDP_ABORTED; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-16 21:09:28 +01:00
										 |  |  | 	/* Use inner+outer VLAN as key and hash based on max_cpus */ | 
					
						
							|  |  |  | 	vlan_key = extract_vlan_key(&vlans); | 
					
						
							|  |  |  | 	hash_key = SuperFastHash((char *)&vlan_key, 4, INITVAL); | 
					
						
							| 
									
										
										
										
											2020-12-18 22:09:24 +01:00
										 |  |  | 	cpu_idx = hash_key % *cpu_max; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* To allow excluding some CPUs, a mapping table cpus_enabled
 | 
					
						
							|  |  |  | 	 * translates cpu_idx to real CPU-id | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	cpu_lookup = bpf_map_lookup_elem(&cpus_enabled, &cpu_idx); | 
					
						
							|  |  |  | 	if (!cpu_lookup) | 
					
						
							|  |  |  | 		return XDP_ABORTED; | 
					
						
							|  |  |  | 	cpu_dest = *cpu_lookup; | 
					
						
							| 
									
										
										
										
											2020-12-16 21:24:14 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-16 16:40:15 +01:00
										 |  |  | 	/* Notice: Userspace MUST insert entries into cpumap */ | 
					
						
							| 
									
										
										
										
											2020-12-15 16:49:57 +01:00
										 |  |  | 	action = bpf_redirect_map(&cpumap, cpu_dest, XDP_PASS); | 
					
						
							|  |  |  | out: | 
					
						
							|  |  |  | 	return action; | 
					
						
							|  |  |  | } |