]>
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> | |
15 | #include "bpf_helpers.h" | |
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*/ | |
45 | struct bpf_map_def SEC("maps") lpm_map = { | |
46 | .type = BPF_MAP_TYPE_LPM_TRIE, | |
47 | .key_size = 8, | |
48 | .value_size = sizeof(struct trie_value), | |
49 | .max_entries = 50, | |
50 | .map_flags = BPF_F_NO_PREALLOC, | |
51 | }; | |
52 | ||
53 | /* Map for counter*/ | |
54 | struct bpf_map_def SEC("maps") rxcnt = { | |
55 | .type = BPF_MAP_TYPE_PERCPU_ARRAY, | |
56 | .key_size = sizeof(u32), | |
57 | .value_size = sizeof(u64), | |
58 | .max_entries = 256, | |
59 | }; | |
60 | ||
61 | /* Map for ARP table*/ | |
62 | struct bpf_map_def SEC("maps") arp_table = { | |
63 | .type = BPF_MAP_TYPE_HASH, | |
64 | .key_size = sizeof(__be32), | |
65 | .value_size = sizeof(__be64), | |
66 | .max_entries = 50, | |
67 | }; | |
68 | ||
69 | /* Map to keep the exact match entries in the route table*/ | |
70 | struct bpf_map_def SEC("maps") exact_match = { | |
71 | .type = BPF_MAP_TYPE_HASH, | |
72 | .key_size = sizeof(__be32), | |
73 | .value_size = sizeof(struct direct_map), | |
74 | .max_entries = 50, | |
75 | }; | |
76 | ||
77 | struct bpf_map_def SEC("maps") tx_port = { | |
78 | .type = BPF_MAP_TYPE_DEVMAP, | |
79 | .key_size = sizeof(int), | |
80 | .value_size = sizeof(int), | |
81 | .max_entries = 100, | |
82 | }; | |
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"; |