162306a36Sopenharmony_ci/* Copyright (C) 2017 Cavium, Inc.
262306a36Sopenharmony_ci *
362306a36Sopenharmony_ci * This program is free software; you can redistribute it and/or modify it
462306a36Sopenharmony_ci * under the terms of version 2 of the GNU General Public License
562306a36Sopenharmony_ci * as published by the Free Software Foundation.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include "vmlinux.h"
962306a36Sopenharmony_ci#include "xdp_sample.bpf.h"
1062306a36Sopenharmony_ci#include "xdp_sample_shared.h"
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#define ETH_ALEN	6
1362306a36Sopenharmony_ci#define ETH_P_8021Q	0x8100
1462306a36Sopenharmony_ci#define ETH_P_8021AD	0x88A8
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_cistruct trie_value {
1762306a36Sopenharmony_ci	__u8 prefix[4];
1862306a36Sopenharmony_ci	__be64 value;
1962306a36Sopenharmony_ci	int ifindex;
2062306a36Sopenharmony_ci	int metric;
2162306a36Sopenharmony_ci	__be32 gw;
2262306a36Sopenharmony_ci};
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci/* Key for lpm_trie */
2562306a36Sopenharmony_ciunion key_4 {
2662306a36Sopenharmony_ci	u32 b32[2];
2762306a36Sopenharmony_ci	u8 b8[8];
2862306a36Sopenharmony_ci};
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistruct arp_entry {
3162306a36Sopenharmony_ci	__be64 mac;
3262306a36Sopenharmony_ci	__be32 dst;
3362306a36Sopenharmony_ci};
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistruct direct_map {
3662306a36Sopenharmony_ci	struct arp_entry arp;
3762306a36Sopenharmony_ci	int ifindex;
3862306a36Sopenharmony_ci	__be64 mac;
3962306a36Sopenharmony_ci};
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci/* Map for trie implementation */
4262306a36Sopenharmony_cistruct {
4362306a36Sopenharmony_ci	__uint(type, BPF_MAP_TYPE_LPM_TRIE);
4462306a36Sopenharmony_ci	__uint(key_size, 8);
4562306a36Sopenharmony_ci	__uint(value_size, sizeof(struct trie_value));
4662306a36Sopenharmony_ci	__uint(max_entries, 50);
4762306a36Sopenharmony_ci	__uint(map_flags, BPF_F_NO_PREALLOC);
4862306a36Sopenharmony_ci} lpm_map SEC(".maps");
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci/* Map for ARP table */
5162306a36Sopenharmony_cistruct {
5262306a36Sopenharmony_ci	__uint(type, BPF_MAP_TYPE_HASH);
5362306a36Sopenharmony_ci	__type(key, __be32);
5462306a36Sopenharmony_ci	__type(value, __be64);
5562306a36Sopenharmony_ci	__uint(max_entries, 50);
5662306a36Sopenharmony_ci} arp_table SEC(".maps");
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci/* Map to keep the exact match entries in the route table */
5962306a36Sopenharmony_cistruct {
6062306a36Sopenharmony_ci	__uint(type, BPF_MAP_TYPE_HASH);
6162306a36Sopenharmony_ci	__type(key, __be32);
6262306a36Sopenharmony_ci	__type(value, struct direct_map);
6362306a36Sopenharmony_ci	__uint(max_entries, 50);
6462306a36Sopenharmony_ci} exact_match SEC(".maps");
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_cistruct {
6762306a36Sopenharmony_ci	__uint(type, BPF_MAP_TYPE_DEVMAP);
6862306a36Sopenharmony_ci	__uint(key_size, sizeof(int));
6962306a36Sopenharmony_ci	__uint(value_size, sizeof(int));
7062306a36Sopenharmony_ci	__uint(max_entries, 100);
7162306a36Sopenharmony_ci} tx_port SEC(".maps");
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ciSEC("xdp")
7462306a36Sopenharmony_ciint xdp_router_ipv4_prog(struct xdp_md *ctx)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	void *data_end = (void *)(long)ctx->data_end;
7762306a36Sopenharmony_ci	void *data = (void *)(long)ctx->data;
7862306a36Sopenharmony_ci	struct ethhdr *eth = data;
7962306a36Sopenharmony_ci	u64 nh_off = sizeof(*eth);
8062306a36Sopenharmony_ci	struct datarec *rec;
8162306a36Sopenharmony_ci	__be16 h_proto;
8262306a36Sopenharmony_ci	u32 key = 0;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	rec = bpf_map_lookup_elem(&rx_cnt, &key);
8562306a36Sopenharmony_ci	if (rec)
8662306a36Sopenharmony_ci		NO_TEAR_INC(rec->processed);
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	if (data + nh_off > data_end)
8962306a36Sopenharmony_ci		goto drop;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	h_proto = eth->h_proto;
9262306a36Sopenharmony_ci	if (h_proto == bpf_htons(ETH_P_8021Q) ||
9362306a36Sopenharmony_ci	    h_proto == bpf_htons(ETH_P_8021AD)) {
9462306a36Sopenharmony_ci		struct vlan_hdr *vhdr;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci		vhdr = data + nh_off;
9762306a36Sopenharmony_ci		nh_off += sizeof(struct vlan_hdr);
9862306a36Sopenharmony_ci		if (data + nh_off > data_end)
9962306a36Sopenharmony_ci			goto drop;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci		h_proto = vhdr->h_vlan_encapsulated_proto;
10262306a36Sopenharmony_ci	}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	switch (bpf_ntohs(h_proto)) {
10562306a36Sopenharmony_ci	case ETH_P_ARP:
10662306a36Sopenharmony_ci		if (rec)
10762306a36Sopenharmony_ci			NO_TEAR_INC(rec->xdp_pass);
10862306a36Sopenharmony_ci		return XDP_PASS;
10962306a36Sopenharmony_ci	case ETH_P_IP: {
11062306a36Sopenharmony_ci		struct iphdr *iph = data + nh_off;
11162306a36Sopenharmony_ci		struct direct_map *direct_entry;
11262306a36Sopenharmony_ci		__be64 *dest_mac, *src_mac;
11362306a36Sopenharmony_ci		int forward_to;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci		if (iph + 1 > data_end)
11662306a36Sopenharmony_ci			goto drop;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci		direct_entry = bpf_map_lookup_elem(&exact_match, &iph->daddr);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci		/* Check for exact match, this would give a faster lookup */
12162306a36Sopenharmony_ci		if (direct_entry && direct_entry->mac &&
12262306a36Sopenharmony_ci		    direct_entry->arp.mac) {
12362306a36Sopenharmony_ci			src_mac = &direct_entry->mac;
12462306a36Sopenharmony_ci			dest_mac = &direct_entry->arp.mac;
12562306a36Sopenharmony_ci			forward_to = direct_entry->ifindex;
12662306a36Sopenharmony_ci		} else {
12762306a36Sopenharmony_ci			struct trie_value *prefix_value;
12862306a36Sopenharmony_ci			union key_4 key4;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci			/* Look up in the trie for lpm */
13162306a36Sopenharmony_ci			key4.b32[0] = 32;
13262306a36Sopenharmony_ci			key4.b8[4] = iph->daddr & 0xff;
13362306a36Sopenharmony_ci			key4.b8[5] = (iph->daddr >> 8) & 0xff;
13462306a36Sopenharmony_ci			key4.b8[6] = (iph->daddr >> 16) & 0xff;
13562306a36Sopenharmony_ci			key4.b8[7] = (iph->daddr >> 24) & 0xff;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci			prefix_value = bpf_map_lookup_elem(&lpm_map, &key4);
13862306a36Sopenharmony_ci			if (!prefix_value)
13962306a36Sopenharmony_ci				goto drop;
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci			forward_to = prefix_value->ifindex;
14262306a36Sopenharmony_ci			src_mac = &prefix_value->value;
14362306a36Sopenharmony_ci			if (!src_mac)
14462306a36Sopenharmony_ci				goto drop;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci			dest_mac = bpf_map_lookup_elem(&arp_table, &iph->daddr);
14762306a36Sopenharmony_ci			if (!dest_mac) {
14862306a36Sopenharmony_ci				if (!prefix_value->gw)
14962306a36Sopenharmony_ci					goto drop;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci				dest_mac = bpf_map_lookup_elem(&arp_table,
15262306a36Sopenharmony_ci							       &prefix_value->gw);
15362306a36Sopenharmony_ci				if (!dest_mac) {
15462306a36Sopenharmony_ci					/* Forward the packet to the kernel in
15562306a36Sopenharmony_ci					 * order to trigger ARP discovery for
15662306a36Sopenharmony_ci					 * the default gw.
15762306a36Sopenharmony_ci					 */
15862306a36Sopenharmony_ci					if (rec)
15962306a36Sopenharmony_ci						NO_TEAR_INC(rec->xdp_pass);
16062306a36Sopenharmony_ci					return XDP_PASS;
16162306a36Sopenharmony_ci				}
16262306a36Sopenharmony_ci			}
16362306a36Sopenharmony_ci		}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci		if (src_mac && dest_mac) {
16662306a36Sopenharmony_ci			int ret;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci			__builtin_memcpy(eth->h_dest, dest_mac, ETH_ALEN);
16962306a36Sopenharmony_ci			__builtin_memcpy(eth->h_source, src_mac, ETH_ALEN);
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci			ret = bpf_redirect_map(&tx_port, forward_to, 0);
17262306a36Sopenharmony_ci			if (ret == XDP_REDIRECT) {
17362306a36Sopenharmony_ci				if (rec)
17462306a36Sopenharmony_ci					NO_TEAR_INC(rec->xdp_redirect);
17562306a36Sopenharmony_ci				return ret;
17662306a36Sopenharmony_ci			}
17762306a36Sopenharmony_ci		}
17862306a36Sopenharmony_ci	}
17962306a36Sopenharmony_ci	default:
18062306a36Sopenharmony_ci		break;
18162306a36Sopenharmony_ci	}
18262306a36Sopenharmony_cidrop:
18362306a36Sopenharmony_ci	if (rec)
18462306a36Sopenharmony_ci		NO_TEAR_INC(rec->xdp_drop);
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	return XDP_DROP;
18762306a36Sopenharmony_ci}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_cichar _license[] SEC("license") = "GPL";
190