185bf1f51SLorenzo Bianconi /* Copyright (C) 2017 Cavium, Inc.
285bf1f51SLorenzo Bianconi  *
385bf1f51SLorenzo Bianconi  * This program is free software; you can redistribute it and/or modify it
485bf1f51SLorenzo Bianconi  * under the terms of version 2 of the GNU General Public License
585bf1f51SLorenzo Bianconi  * as published by the Free Software Foundation.
685bf1f51SLorenzo Bianconi  */
785bf1f51SLorenzo Bianconi 
885bf1f51SLorenzo Bianconi #include "vmlinux.h"
985bf1f51SLorenzo Bianconi #include "xdp_sample.bpf.h"
1085bf1f51SLorenzo Bianconi #include "xdp_sample_shared.h"
1185bf1f51SLorenzo Bianconi 
1285bf1f51SLorenzo Bianconi #define ETH_ALEN	6
1385bf1f51SLorenzo Bianconi #define ETH_P_8021Q	0x8100
1485bf1f51SLorenzo Bianconi #define ETH_P_8021AD	0x88A8
1585bf1f51SLorenzo Bianconi 
1685bf1f51SLorenzo Bianconi struct trie_value {
1785bf1f51SLorenzo Bianconi 	__u8 prefix[4];
1885bf1f51SLorenzo Bianconi 	__be64 value;
1985bf1f51SLorenzo Bianconi 	int ifindex;
2085bf1f51SLorenzo Bianconi 	int metric;
2185bf1f51SLorenzo Bianconi 	__be32 gw;
2285bf1f51SLorenzo Bianconi };
2385bf1f51SLorenzo Bianconi 
2485bf1f51SLorenzo Bianconi /* Key for lpm_trie */
2585bf1f51SLorenzo Bianconi union key_4 {
2685bf1f51SLorenzo Bianconi 	u32 b32[2];
2785bf1f51SLorenzo Bianconi 	u8 b8[8];
2885bf1f51SLorenzo Bianconi };
2985bf1f51SLorenzo Bianconi 
3085bf1f51SLorenzo Bianconi struct arp_entry {
3185bf1f51SLorenzo Bianconi 	__be64 mac;
3285bf1f51SLorenzo Bianconi 	__be32 dst;
3385bf1f51SLorenzo Bianconi };
3485bf1f51SLorenzo Bianconi 
3585bf1f51SLorenzo Bianconi struct direct_map {
3685bf1f51SLorenzo Bianconi 	struct arp_entry arp;
3785bf1f51SLorenzo Bianconi 	int ifindex;
3885bf1f51SLorenzo Bianconi 	__be64 mac;
3985bf1f51SLorenzo Bianconi };
4085bf1f51SLorenzo Bianconi 
4185bf1f51SLorenzo Bianconi /* Map for trie implementation */
4285bf1f51SLorenzo Bianconi struct {
4385bf1f51SLorenzo Bianconi 	__uint(type, BPF_MAP_TYPE_LPM_TRIE);
4485bf1f51SLorenzo Bianconi 	__uint(key_size, 8);
4585bf1f51SLorenzo Bianconi 	__uint(value_size, sizeof(struct trie_value));
4685bf1f51SLorenzo Bianconi 	__uint(max_entries, 50);
4785bf1f51SLorenzo Bianconi 	__uint(map_flags, BPF_F_NO_PREALLOC);
4885bf1f51SLorenzo Bianconi } lpm_map SEC(".maps");
4985bf1f51SLorenzo Bianconi 
5085bf1f51SLorenzo Bianconi /* Map for ARP table */
5185bf1f51SLorenzo Bianconi struct {
5285bf1f51SLorenzo Bianconi 	__uint(type, BPF_MAP_TYPE_HASH);
5385bf1f51SLorenzo Bianconi 	__type(key, __be32);
5485bf1f51SLorenzo Bianconi 	__type(value, __be64);
5585bf1f51SLorenzo Bianconi 	__uint(max_entries, 50);
5685bf1f51SLorenzo Bianconi } arp_table SEC(".maps");
5785bf1f51SLorenzo Bianconi 
5885bf1f51SLorenzo Bianconi /* Map to keep the exact match entries in the route table */
5985bf1f51SLorenzo Bianconi struct {
6085bf1f51SLorenzo Bianconi 	__uint(type, BPF_MAP_TYPE_HASH);
6185bf1f51SLorenzo Bianconi 	__type(key, __be32);
6285bf1f51SLorenzo Bianconi 	__type(value, struct direct_map);
6385bf1f51SLorenzo Bianconi 	__uint(max_entries, 50);
6485bf1f51SLorenzo Bianconi } exact_match SEC(".maps");
6585bf1f51SLorenzo Bianconi 
6685bf1f51SLorenzo Bianconi struct {
6785bf1f51SLorenzo Bianconi 	__uint(type, BPF_MAP_TYPE_DEVMAP);
6885bf1f51SLorenzo Bianconi 	__uint(key_size, sizeof(int));
6985bf1f51SLorenzo Bianconi 	__uint(value_size, sizeof(int));
7085bf1f51SLorenzo Bianconi 	__uint(max_entries, 100);
7185bf1f51SLorenzo Bianconi } tx_port SEC(".maps");
7285bf1f51SLorenzo Bianconi 
7385bf1f51SLorenzo Bianconi SEC("xdp")
xdp_router_ipv4_prog(struct xdp_md * ctx)7485bf1f51SLorenzo Bianconi int xdp_router_ipv4_prog(struct xdp_md *ctx)
7585bf1f51SLorenzo Bianconi {
7685bf1f51SLorenzo Bianconi 	void *data_end = (void *)(long)ctx->data_end;
7785bf1f51SLorenzo Bianconi 	void *data = (void *)(long)ctx->data;
7885bf1f51SLorenzo Bianconi 	struct ethhdr *eth = data;
7985bf1f51SLorenzo Bianconi 	u64 nh_off = sizeof(*eth);
8085bf1f51SLorenzo Bianconi 	struct datarec *rec;
8185bf1f51SLorenzo Bianconi 	__be16 h_proto;
8285bf1f51SLorenzo Bianconi 	u32 key = 0;
8385bf1f51SLorenzo Bianconi 
8485bf1f51SLorenzo Bianconi 	rec = bpf_map_lookup_elem(&rx_cnt, &key);
8585bf1f51SLorenzo Bianconi 	if (rec)
8685bf1f51SLorenzo Bianconi 		NO_TEAR_INC(rec->processed);
8785bf1f51SLorenzo Bianconi 
8885bf1f51SLorenzo Bianconi 	if (data + nh_off > data_end)
8985bf1f51SLorenzo Bianconi 		goto drop;
9085bf1f51SLorenzo Bianconi 
9185bf1f51SLorenzo Bianconi 	h_proto = eth->h_proto;
9285bf1f51SLorenzo Bianconi 	if (h_proto == bpf_htons(ETH_P_8021Q) ||
9385bf1f51SLorenzo Bianconi 	    h_proto == bpf_htons(ETH_P_8021AD)) {
9485bf1f51SLorenzo Bianconi 		struct vlan_hdr *vhdr;
9585bf1f51SLorenzo Bianconi 
9685bf1f51SLorenzo Bianconi 		vhdr = data + nh_off;
9785bf1f51SLorenzo Bianconi 		nh_off += sizeof(struct vlan_hdr);
9885bf1f51SLorenzo Bianconi 		if (data + nh_off > data_end)
9985bf1f51SLorenzo Bianconi 			goto drop;
10085bf1f51SLorenzo Bianconi 
10185bf1f51SLorenzo Bianconi 		h_proto = vhdr->h_vlan_encapsulated_proto;
10285bf1f51SLorenzo Bianconi 	}
10385bf1f51SLorenzo Bianconi 
10485bf1f51SLorenzo Bianconi 	switch (bpf_ntohs(h_proto)) {
10585bf1f51SLorenzo Bianconi 	case ETH_P_ARP:
10685bf1f51SLorenzo Bianconi 		if (rec)
10785bf1f51SLorenzo Bianconi 			NO_TEAR_INC(rec->xdp_pass);
10885bf1f51SLorenzo Bianconi 		return XDP_PASS;
10985bf1f51SLorenzo Bianconi 	case ETH_P_IP: {
11085bf1f51SLorenzo Bianconi 		struct iphdr *iph = data + nh_off;
11185bf1f51SLorenzo Bianconi 		struct direct_map *direct_entry;
11285bf1f51SLorenzo Bianconi 		__be64 *dest_mac, *src_mac;
11385bf1f51SLorenzo Bianconi 		int forward_to;
11485bf1f51SLorenzo Bianconi 
11585bf1f51SLorenzo Bianconi 		if (iph + 1 > data_end)
11685bf1f51SLorenzo Bianconi 			goto drop;
11785bf1f51SLorenzo Bianconi 
11885bf1f51SLorenzo Bianconi 		direct_entry = bpf_map_lookup_elem(&exact_match, &iph->daddr);
11985bf1f51SLorenzo Bianconi 
12085bf1f51SLorenzo Bianconi 		/* Check for exact match, this would give a faster lookup */
12185bf1f51SLorenzo Bianconi 		if (direct_entry && direct_entry->mac &&
12285bf1f51SLorenzo Bianconi 		    direct_entry->arp.mac) {
12385bf1f51SLorenzo Bianconi 			src_mac = &direct_entry->mac;
12485bf1f51SLorenzo Bianconi 			dest_mac = &direct_entry->arp.mac;
12585bf1f51SLorenzo Bianconi 			forward_to = direct_entry->ifindex;
12685bf1f51SLorenzo Bianconi 		} else {
12785bf1f51SLorenzo Bianconi 			struct trie_value *prefix_value;
12885bf1f51SLorenzo Bianconi 			union key_4 key4;
12985bf1f51SLorenzo Bianconi 
13085bf1f51SLorenzo Bianconi 			/* Look up in the trie for lpm */
13185bf1f51SLorenzo Bianconi 			key4.b32[0] = 32;
13285bf1f51SLorenzo Bianconi 			key4.b8[4] = iph->daddr & 0xff;
13385bf1f51SLorenzo Bianconi 			key4.b8[5] = (iph->daddr >> 8) & 0xff;
13485bf1f51SLorenzo Bianconi 			key4.b8[6] = (iph->daddr >> 16) & 0xff;
13585bf1f51SLorenzo Bianconi 			key4.b8[7] = (iph->daddr >> 24) & 0xff;
13685bf1f51SLorenzo Bianconi 
13785bf1f51SLorenzo Bianconi 			prefix_value = bpf_map_lookup_elem(&lpm_map, &key4);
13885bf1f51SLorenzo Bianconi 			if (!prefix_value)
13985bf1f51SLorenzo Bianconi 				goto drop;
14085bf1f51SLorenzo Bianconi 
14185bf1f51SLorenzo Bianconi 			forward_to = prefix_value->ifindex;
14285bf1f51SLorenzo Bianconi 			src_mac = &prefix_value->value;
14385bf1f51SLorenzo Bianconi 			if (!src_mac)
14485bf1f51SLorenzo Bianconi 				goto drop;
14585bf1f51SLorenzo Bianconi 
14685bf1f51SLorenzo Bianconi 			dest_mac = bpf_map_lookup_elem(&arp_table, &iph->daddr);
14785bf1f51SLorenzo Bianconi 			if (!dest_mac) {
14885bf1f51SLorenzo Bianconi 				if (!prefix_value->gw)
14985bf1f51SLorenzo Bianconi 					goto drop;
15085bf1f51SLorenzo Bianconi 
15185bf1f51SLorenzo Bianconi 				dest_mac = bpf_map_lookup_elem(&arp_table,
15285bf1f51SLorenzo Bianconi 							       &prefix_value->gw);
153*200a89e3SLorenzo Bianconi 				if (!dest_mac) {
154*200a89e3SLorenzo Bianconi 					/* Forward the packet to the kernel in
155*200a89e3SLorenzo Bianconi 					 * order to trigger ARP discovery for
156*200a89e3SLorenzo Bianconi 					 * the default gw.
157*200a89e3SLorenzo Bianconi 					 */
158*200a89e3SLorenzo Bianconi 					if (rec)
159*200a89e3SLorenzo Bianconi 						NO_TEAR_INC(rec->xdp_pass);
160*200a89e3SLorenzo Bianconi 					return XDP_PASS;
161*200a89e3SLorenzo Bianconi 				}
16285bf1f51SLorenzo Bianconi 			}
16385bf1f51SLorenzo Bianconi 		}
16485bf1f51SLorenzo Bianconi 
16585bf1f51SLorenzo Bianconi 		if (src_mac && dest_mac) {
16685bf1f51SLorenzo Bianconi 			int ret;
16785bf1f51SLorenzo Bianconi 
16885bf1f51SLorenzo Bianconi 			__builtin_memcpy(eth->h_dest, dest_mac, ETH_ALEN);
16985bf1f51SLorenzo Bianconi 			__builtin_memcpy(eth->h_source, src_mac, ETH_ALEN);
17085bf1f51SLorenzo Bianconi 
17185bf1f51SLorenzo Bianconi 			ret = bpf_redirect_map(&tx_port, forward_to, 0);
17285bf1f51SLorenzo Bianconi 			if (ret == XDP_REDIRECT) {
17385bf1f51SLorenzo Bianconi 				if (rec)
17485bf1f51SLorenzo Bianconi 					NO_TEAR_INC(rec->xdp_redirect);
17585bf1f51SLorenzo Bianconi 				return ret;
17685bf1f51SLorenzo Bianconi 			}
17785bf1f51SLorenzo Bianconi 		}
17885bf1f51SLorenzo Bianconi 	}
17985bf1f51SLorenzo Bianconi 	default:
18085bf1f51SLorenzo Bianconi 		break;
18185bf1f51SLorenzo Bianconi 	}
18285bf1f51SLorenzo Bianconi drop:
18385bf1f51SLorenzo Bianconi 	if (rec)
18485bf1f51SLorenzo Bianconi 		NO_TEAR_INC(rec->xdp_drop);
18585bf1f51SLorenzo Bianconi 
18685bf1f51SLorenzo Bianconi 	return XDP_DROP;
18785bf1f51SLorenzo Bianconi }
18885bf1f51SLorenzo Bianconi 
18985bf1f51SLorenzo Bianconi char _license[] SEC("license") = "GPL";
190