]>
Commit | Line | Data |
---|---|---|
3e29cd0e CJ |
1 | /* Copyright (C) 2017 Cavium, Inc. |
2 | * | |
3 | * This program is free software; you can redistribute it and/or modify it | |
4 | * under the terms of version 2 of the GNU General Public License | |
5 | * as published by the Free Software Foundation. | |
6 | */ | |
7 | #define KBUILD_MODNAME "foo" | |
8 | #include <uapi/linux/bpf.h> | |
9 | #include <linux/in.h> | |
10 | #include <linux/if_ether.h> | |
11 | #include <linux/if_packet.h> | |
12 | #include <linux/if_vlan.h> | |
13 | #include <linux/ip.h> | |
14 | #include <linux/ipv6.h> | |
7cf245a3 | 15 | #include <bpf/bpf_helpers.h> |
3e29cd0e CJ |
16 | #include <linux/slab.h> |
17 | #include <net/ip_fib.h> | |
18 | ||
19 | struct trie_value { | |
20 | __u8 prefix[4]; | |
21 | __be64 value; | |
22 | int ifindex; | |
23 | int metric; | |
24 | __be32 gw; | |
25 | }; | |
26 | ||
27 | /* Key for lpm_trie*/ | |
28 | union key_4 { | |
29 | u32 b32[2]; | |
30 | u8 b8[8]; | |
31 | }; | |
32 | ||
33 | struct arp_entry { | |
34 | __be64 mac; | |
35 | __be32 dst; | |
36 | }; | |
37 | ||
38 | struct direct_map { | |
39 | struct arp_entry arp; | |
40 | int ifindex; | |
41 | __be64 mac; | |
42 | }; | |
43 | ||
44 | /* Map for trie implementation*/ | |
451d1dc8 DL |
45 | struct { |
46 | __uint(type, BPF_MAP_TYPE_LPM_TRIE); | |
47 | __uint(key_size, 8); | |
48 | __uint(value_size, sizeof(struct trie_value)); | |
49 | __uint(max_entries, 50); | |
50 | __uint(map_flags, BPF_F_NO_PREALLOC); | |
51 | } lpm_map SEC(".maps"); | |
3e29cd0e CJ |
52 | |
53 | /* Map for counter*/ | |
451d1dc8 DL |
54 | struct { |
55 | __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); | |
56 | __type(key, u32); | |
57 | __type(value, u64); | |
58 | __uint(max_entries, 256); | |
59 | } rxcnt SEC(".maps"); | |
3e29cd0e CJ |
60 | |
61 | /* Map for ARP table*/ | |
451d1dc8 DL |
62 | struct { |
63 | __uint(type, BPF_MAP_TYPE_HASH); | |
64 | __type(key, __be32); | |
65 | __type(value, __be64); | |
66 | __uint(max_entries, 50); | |
67 | } arp_table SEC(".maps"); | |
3e29cd0e CJ |
68 | |
69 | /* Map to keep the exact match entries in the route table*/ | |
451d1dc8 DL |
70 | struct { |
71 | __uint(type, BPF_MAP_TYPE_HASH); | |
72 | __type(key, __be32); | |
73 | __type(value, struct direct_map); | |
74 | __uint(max_entries, 50); | |
75 | } exact_match SEC(".maps"); | |
3e29cd0e | 76 | |
451d1dc8 DL |
77 | struct { |
78 | __uint(type, BPF_MAP_TYPE_DEVMAP); | |
79 | __uint(key_size, sizeof(int)); | |
80 | __uint(value_size, sizeof(int)); | |
81 | __uint(max_entries, 100); | |
82 | } tx_port SEC(".maps"); | |
3e29cd0e CJ |
83 | |
84 | /* Function to set source and destination mac of the packet */ | |
85 | static inline void set_src_dst_mac(void *data, void *src, void *dst) | |
86 | { | |
87 | unsigned short *source = src; | |
88 | unsigned short *dest = dst; | |
89 | unsigned short *p = data; | |
90 | ||
91 | __builtin_memcpy(p, dest, 6); | |
92 | __builtin_memcpy(p + 3, source, 6); | |
93 | } | |
94 | ||
95 | /* Parse IPV4 packet to get SRC, DST IP and protocol */ | |
96 | static inline int parse_ipv4(void *data, u64 nh_off, void *data_end, | |
97 | __be32 *src, __be32 *dest) | |
98 | { | |
99 | struct iphdr *iph = data + nh_off; | |
100 | ||
101 | if (iph + 1 > data_end) | |
102 | return 0; | |
103 | *src = iph->saddr; | |
104 | *dest = iph->daddr; | |
105 | return iph->protocol; | |
106 | } | |
107 | ||
108 | SEC("xdp_router_ipv4") | |
109 | int xdp_router_ipv4_prog(struct xdp_md *ctx) | |
110 | { | |
111 | void *data_end = (void *)(long)ctx->data_end; | |
112 | __be64 *dest_mac = NULL, *src_mac = NULL; | |
113 | void *data = (void *)(long)ctx->data; | |
114 | struct trie_value *prefix_value; | |
115 | int rc = XDP_DROP, forward_to; | |
116 | struct ethhdr *eth = data; | |
117 | union key_4 key4; | |
118 | long *value; | |
119 | u16 h_proto; | |
120 | u32 ipproto; | |
121 | u64 nh_off; | |
122 | ||
123 | nh_off = sizeof(*eth); | |
124 | if (data + nh_off > data_end) | |
125 | return rc; | |
126 | ||
127 | h_proto = eth->h_proto; | |
128 | ||
129 | if (h_proto == htons(ETH_P_8021Q) || h_proto == htons(ETH_P_8021AD)) { | |
130 | struct vlan_hdr *vhdr; | |
131 | ||
132 | vhdr = data + nh_off; | |
133 | nh_off += sizeof(struct vlan_hdr); | |
134 | if (data + nh_off > data_end) | |
135 | return rc; | |
136 | h_proto = vhdr->h_vlan_encapsulated_proto; | |
137 | } | |
138 | if (h_proto == htons(ETH_P_ARP)) { | |
139 | return XDP_PASS; | |
140 | } else if (h_proto == htons(ETH_P_IP)) { | |
141 | struct direct_map *direct_entry; | |
142 | __be32 src_ip = 0, dest_ip = 0; | |
143 | ||
144 | ipproto = parse_ipv4(data, nh_off, data_end, &src_ip, &dest_ip); | |
145 | direct_entry = bpf_map_lookup_elem(&exact_match, &dest_ip); | |
146 | /* Check for exact match, this would give a faster lookup*/ | |
147 | if (direct_entry && direct_entry->mac && direct_entry->arp.mac) { | |
148 | src_mac = &direct_entry->mac; | |
149 | dest_mac = &direct_entry->arp.mac; | |
150 | forward_to = direct_entry->ifindex; | |
151 | } else { | |
152 | /* Look up in the trie for lpm*/ | |
153 | key4.b32[0] = 32; | |
154 | key4.b8[4] = dest_ip & 0xff; | |
155 | key4.b8[5] = (dest_ip >> 8) & 0xff; | |
156 | key4.b8[6] = (dest_ip >> 16) & 0xff; | |
157 | key4.b8[7] = (dest_ip >> 24) & 0xff; | |
158 | prefix_value = bpf_map_lookup_elem(&lpm_map, &key4); | |
159 | if (!prefix_value) | |
160 | return XDP_DROP; | |
161 | src_mac = &prefix_value->value; | |
162 | if (!src_mac) | |
163 | return XDP_DROP; | |
164 | dest_mac = bpf_map_lookup_elem(&arp_table, &dest_ip); | |
165 | if (!dest_mac) { | |
166 | if (!prefix_value->gw) | |
167 | return XDP_DROP; | |
168 | dest_ip = prefix_value->gw; | |
169 | dest_mac = bpf_map_lookup_elem(&arp_table, &dest_ip); | |
170 | } | |
171 | forward_to = prefix_value->ifindex; | |
172 | } | |
173 | } else { | |
174 | ipproto = 0; | |
175 | } | |
176 | if (src_mac && dest_mac) { | |
177 | set_src_dst_mac(data, src_mac, dest_mac); | |
178 | value = bpf_map_lookup_elem(&rxcnt, &ipproto); | |
179 | if (value) | |
180 | *value += 1; | |
181 | return bpf_redirect_map(&tx_port, forward_to, 0); | |
182 | } | |
183 | return rc; | |
184 | } | |
185 | ||
186 | char _license[] SEC("license") = "GPL"; |