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