github.com/dylandreimerink/gobpfld@v0.6.1-0.20220205171531-e79c330ad608/cmd/examples/bpf_to_bpf/src/xdp.c (about) 1 #include <stddef.h> 2 #include <linux/bpf.h> 3 #include <linux/in.h> 4 #include <linux/if_ether.h> 5 #include <linux/if_packet.h> 6 #include <linux/ip.h> 7 #include <linux/ipv6.h> 8 #include <linux/tcp.h> 9 #include <linux/udp.h> 10 #include "bpf_endian.h" 11 12 #define SEC(NAME) __attribute__((section(NAME), used)) 13 14 #ifndef __noinline 15 #define __noinline __attribute__((noinline)) 16 #endif 17 18 /* 19 * bpf_map_lookup_elem 20 * 21 * Perform a lookup in *map* for an entry associated to *key*. 22 * 23 * Returns 24 * Map value associated to *key*, or **NULL** if no entry was 25 * found. 26 */ 27 static void *(*bpf_map_lookup_elem)(void *map, const void *key) = (void *) 1; 28 29 /* 30 * bpf_map_update_elem 31 * 32 * Add or update the value of the entry associated to *key* in 33 * *map* with *value*. *flags* is one of: 34 * 35 * **BPF_NOEXIST** 36 * The entry for *key* must not exist in the map. 37 * **BPF_EXIST** 38 * The entry for *key* must already exist in the map. 39 * **BPF_ANY** 40 * No condition on the existence of the entry for *key*. 41 * 42 * Flag value **BPF_NOEXIST** cannot be used for maps of types 43 * **BPF_MAP_TYPE_ARRAY** or **BPF_MAP_TYPE_PERCPU_ARRAY** (all 44 * elements always exist), the helper would return an error. 45 * 46 * Returns 47 * 0 on success, or a negative error in case of failure. 48 */ 49 static long (*bpf_map_update_elem)(void *map, const void *key, const void *value, __u64 flags) = (void *) 2; 50 51 52 struct bpf_map_def { 53 unsigned int type; 54 unsigned int key_size; 55 unsigned int value_size; 56 unsigned int max_entries; 57 unsigned int map_flags; 58 }; 59 60 struct traffic_stats { 61 __u64 pkts; 62 __u64 bytes; 63 }; 64 65 // Stats on packets keyed by protocol number 66 struct bpf_map_def SEC("maps") ip_proto_stats = { 67 .type = BPF_MAP_TYPE_LRU_PERCPU_HASH, 68 .key_size = sizeof(__u8), 69 .value_size = sizeof(struct traffic_stats), 70 .max_entries = 16, // stats on top 16 protocol seem more than enough 71 .map_flags = BPF_F_NO_COMMON_LRU, 72 }; 73 74 // Stats on udp packets keyed by dest port 75 struct bpf_map_def SEC("maps") udp_stats = { 76 .type = BPF_MAP_TYPE_LRU_PERCPU_HASH, 77 .key_size = sizeof(__u16), 78 .value_size = sizeof(struct traffic_stats), 79 .max_entries = 128, // top 128 udp ports seems good enough 80 .map_flags = BPF_F_NO_COMMON_LRU, 81 }; 82 83 // Stats on tcp packets keyed by dest port 84 struct bpf_map_def SEC("maps") tcp_stats = { 85 .type = BPF_MAP_TYPE_LRU_PERCPU_HASH, 86 .key_size = sizeof(__u16), 87 .value_size = sizeof(struct traffic_stats), 88 .max_entries = 128, // top 128 tcp ports seems good enough 89 .map_flags = BPF_F_NO_COMMON_LRU, 90 }; 91 92 struct vlan_hdr { 93 __be16 h_vlan_TCI; 94 __be16 h_vlan_encapsulated_proto; 95 }; 96 97 static __noinline void inc_ip_proto( 98 __u8 proto, 99 __u64 framesize 100 ) { 101 struct traffic_stats* stats_ptr = bpf_map_lookup_elem(&ip_proto_stats, &proto); 102 if( stats_ptr == NULL ) { 103 // Make a new stats object 104 struct traffic_stats stats = { 105 .pkts = 1, 106 .bytes = framesize, 107 }; 108 bpf_map_update_elem(&ip_proto_stats, &proto, &stats, BPF_ANY); 109 } else { 110 stats_ptr->pkts++; 111 stats_ptr->bytes += framesize; 112 } 113 } 114 115 static __noinline void inc_tcp( 116 struct tcphdr* tcphdr, 117 __u64 framesize 118 ){ 119 __le16 le_dest = bpf_ntohs(tcphdr->dest); 120 // Get existing stats 121 struct traffic_stats* stats_ptr = bpf_map_lookup_elem(&tcp_stats, &le_dest); 122 if( stats_ptr == NULL ) { 123 // Make a new stats object 124 struct traffic_stats stats = { 125 .pkts = 1, 126 .bytes = framesize, 127 }; 128 bpf_map_update_elem(&tcp_stats, &le_dest, &stats, BPF_ANY); 129 } else { 130 stats_ptr->pkts++; 131 stats_ptr->bytes += framesize; 132 } 133 } 134 135 static __noinline void inc_udp( 136 struct udphdr* udphdr, 137 __u64 framesize 138 ){ 139 __le16 le_dest = bpf_ntohs(udphdr->dest); 140 // Get existing stats 141 struct traffic_stats* stats_ptr = bpf_map_lookup_elem(&udp_stats, &le_dest); 142 if( stats_ptr == NULL ) { 143 // Make a new stats object 144 struct traffic_stats stats = { 145 .pkts = 1, 146 .bytes = framesize, 147 }; 148 149 bpf_map_update_elem(&udp_stats, &le_dest, &stats, BPF_ANY); 150 } else { 151 stats_ptr->pkts++; 152 stats_ptr->bytes += framesize; 153 } 154 } 155 156 static __noinline void handle_ipv4(void* data, void* data_end, __u64 nh_off) { 157 struct iphdr* iph = data + nh_off; 158 nh_off += sizeof(struct iphdr); 159 __u64 framesize = data_end - data; 160 161 // Drop packets which don't have enough data to fit the IPv4 header 162 if( data + nh_off > data_end ){ 163 return; 164 } 165 166 __u8 ipproto = iph->protocol; 167 168 inc_ip_proto(ipproto, framesize); 169 170 if( ipproto == IPPROTO_UDP ){ 171 struct udphdr* udphdr = data + nh_off; 172 nh_off += sizeof(struct udphdr); 173 174 // If there is not enough data to parse a UDP header, drop the packet 175 if( data + nh_off > data_end ) { 176 return; 177 } 178 179 inc_udp(udphdr, framesize); 180 } 181 182 if( ipproto == IPPROTO_TCP ){ 183 struct tcphdr* tcphdr = data + nh_off; 184 nh_off += sizeof(struct tcphdr); 185 186 187 // If there is not enough data to parse a UDP header, drop the packet 188 if( data + nh_off > data_end ) { 189 return; 190 } 191 192 inc_tcp(tcphdr, framesize); 193 } 194 } 195 196 static __noinline void handle_ipv6(void* data, void* data_end, __u64 nh_off) { 197 struct ipv6hdr* ip6h = data + nh_off; 198 nh_off += sizeof(struct ipv6hdr); 199 __u64 framesize = data_end - data; 200 201 // Drop packets which don't have enough data to fit the IPv4 header 202 if( data + nh_off > data_end ){ 203 return; 204 } 205 206 __u8 ipproto = ip6h->nexthdr; 207 208 inc_ip_proto(ipproto, framesize); 209 210 if( ipproto == IPPROTO_UDP ){ 211 struct udphdr* udphdr = data + nh_off; 212 nh_off += sizeof(struct udphdr); 213 214 // If there is not enough data to parse a UDP header, drop the packet 215 if( data + nh_off > data_end ) { 216 return; 217 } 218 219 inc_udp(udphdr, framesize); 220 } 221 222 if( ipproto == IPPROTO_TCP ){ 223 struct tcphdr* tcphdr = data + nh_off; 224 nh_off += sizeof(struct tcphdr); 225 226 // If there is not enough data to parse a TCP header, drop the packet 227 if( data + nh_off > data_end ) { 228 return; 229 } 230 231 inc_tcp(tcphdr, framesize); 232 } 233 } 234 235 SEC("xdp/proto_stats") 236 int firewall_prog(struct xdp_md *ctx) 237 { 238 void* data_end = (void *)(long)ctx->data_end; 239 void* data = (void *)(long)ctx->data; 240 241 // Offset to the next header 242 __u64 nh_off = sizeof(struct ethhdr); 243 244 // If we don't even have enough data to a ethernet frame header, drop the message 245 if( data + nh_off > data_end ){ 246 return XDP_DROP; 247 } 248 249 struct ethhdr* eth = data; 250 __be16 h_proto = eth->h_proto; 251 252 // If the ethernet packet contains a IEEE 802.1Q or 802.1AD VLAN header 253 if( h_proto == bpf_htons(ETH_P_8021Q) || h_proto == bpf_htons(ETH_P_8021AD) ){ 254 struct vlan_hdr* vhdr = data + nh_off; 255 nh_off += sizeof(struct vlan_hdr); 256 257 // Drop packets which don't have enough data to fit the VLAN header 258 if( data + nh_off > data_end ){ 259 return XDP_DROP; 260 } 261 262 h_proto = vhdr->h_vlan_encapsulated_proto; 263 } 264 265 if( h_proto == bpf_htons(ETH_P_IP) ){ 266 handle_ipv4(data, data_end, nh_off); 267 } else if( h_proto == bpf_htons(ETH_P_IPV6) ){ 268 handle_ipv6(data, data_end, nh_off); 269 } 270 271 return XDP_PASS; 272 } 273 274 char _license[] SEC("license") = "GPL";