github.com/datadog/cilium@v1.6.12/bpf/bpf_xdp.c (about) 1 /* 2 * Copyright (C) 2017 Authors of Cilium 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation; either version 2 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, write to the Free Software 16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 17 */ 18 #define SKIP_CALLS_MAP 19 20 #include <node_config.h> 21 #include <netdev_config.h> 22 #include <filter_config.h> 23 24 #include <bpf/api.h> 25 26 #include <stdint.h> 27 #include <stdio.h> 28 29 #include <linux/bpf.h> 30 #include <linux/if_ether.h> 31 32 #include "lib/utils.h" 33 #include "lib/common.h" 34 #include "lib/maps.h" 35 #include "lib/xdp.h" 36 #include "lib/eps.h" 37 #include "lib/events.h" 38 39 #ifndef HAVE_LPM_MAP_TYPE 40 # undef CIDR4_LPM_PREFILTER 41 # undef CIDR6_LPM_PREFILTER 42 #endif 43 44 #ifdef CIDR4_FILTER 45 struct bpf_elf_map __section_maps CIDR4_HMAP_NAME = { 46 .type = BPF_MAP_TYPE_HASH, 47 .size_key = sizeof(struct lpm_v4_key), 48 .size_value = sizeof(struct lpm_val), 49 .flags = BPF_F_NO_PREALLOC, 50 .pinning = PIN_GLOBAL_NS, 51 .max_elem = CIDR4_HMAP_ELEMS, 52 }; 53 54 #ifdef CIDR4_LPM_PREFILTER 55 struct bpf_elf_map __section_maps CIDR4_LMAP_NAME = { 56 .type = BPF_MAP_TYPE_LPM_TRIE, 57 .size_key = sizeof(struct lpm_v4_key), 58 .size_value = sizeof(struct lpm_val), 59 .flags = BPF_F_NO_PREALLOC, 60 .pinning = PIN_GLOBAL_NS, 61 .max_elem = CIDR4_LMAP_ELEMS, 62 }; 63 #endif /* CIDR4_LPM_PREFILTER */ 64 #endif /* CIDR4_FILTER */ 65 66 #ifdef CIDR6_FILTER 67 struct bpf_elf_map __section_maps CIDR6_HMAP_NAME = { 68 .type = BPF_MAP_TYPE_HASH, 69 .size_key = sizeof(struct lpm_v6_key), 70 .size_value = sizeof(struct lpm_val), 71 .flags = BPF_F_NO_PREALLOC, 72 .pinning = PIN_GLOBAL_NS, 73 .max_elem = CIDR4_HMAP_ELEMS, 74 }; 75 76 #ifdef CIDR6_LPM_PREFILTER 77 struct bpf_elf_map __section_maps CIDR6_LMAP_NAME = { 78 .type = BPF_MAP_TYPE_LPM_TRIE, 79 .size_key = sizeof(struct lpm_v6_key), 80 .size_value = sizeof(struct lpm_val), 81 .flags = BPF_F_NO_PREALLOC, 82 .pinning = PIN_GLOBAL_NS, 83 .max_elem = CIDR4_LMAP_ELEMS, 84 }; 85 #endif /* CIDR6_LPM_PREFILTER */ 86 #endif /* CIDR6_FILTER */ 87 88 static __always_inline int check_v4_endpoint(struct xdp_md *xdp, 89 struct iphdr *ipv4_hdr) 90 { 91 if (lookup_ip4_endpoint(ipv4_hdr)) 92 return XDP_PASS; 93 94 return XDP_DROP; 95 } 96 97 static __always_inline int check_v4(struct xdp_md *xdp) 98 { 99 void *data_end = xdp_data_end(xdp); 100 void *data = xdp_data(xdp); 101 struct iphdr *ipv4_hdr = data + sizeof(struct ethhdr); 102 struct lpm_v4_key pfx __maybe_unused; 103 104 if (xdp_no_room(ipv4_hdr + 1, data_end)) 105 return XDP_DROP; 106 107 #ifdef CIDR4_FILTER 108 __builtin_memcpy(pfx.lpm.data, &ipv4_hdr->saddr, sizeof(pfx.addr)); 109 pfx.lpm.prefixlen = 32; 110 111 #ifdef CIDR4_LPM_PREFILTER 112 if (map_lookup_elem(&CIDR4_LMAP_NAME, &pfx)) 113 return XDP_DROP; 114 else 115 #endif /* CIDR4_LPM_PREFILTER */ 116 return map_lookup_elem(&CIDR4_HMAP_NAME, &pfx) ? 117 XDP_DROP : check_v4_endpoint(xdp, ipv4_hdr); 118 #else 119 return check_v4_endpoint(xdp, ipv4_hdr); 120 #endif /* CIDR4_FILTER */ 121 } 122 123 static __always_inline int check_v6_endpoint(struct xdp_md *xdp, 124 struct ipv6hdr *ipv6_hdr) 125 { 126 if (lookup_ip6_endpoint(ipv6_hdr)) 127 return XDP_PASS; 128 129 return XDP_DROP; 130 } 131 132 static __always_inline int check_v6(struct xdp_md *xdp) 133 { 134 void *data_end = xdp_data_end(xdp); 135 void *data = xdp_data(xdp); 136 struct ipv6hdr *ipv6_hdr = data + sizeof(struct ethhdr); 137 struct lpm_v6_key pfx __maybe_unused; 138 139 if (xdp_no_room(ipv6_hdr + 1, data_end)) 140 return XDP_DROP; 141 142 #ifdef CIDR6_FILTER 143 __builtin_memcpy(pfx.lpm.data, &ipv6_hdr->saddr, sizeof(pfx.addr)); 144 pfx.lpm.prefixlen = 128; 145 146 #ifdef CIDR6_LPM_PREFILTER 147 if (map_lookup_elem(&CIDR6_LMAP_NAME, &pfx)) 148 return XDP_DROP; 149 else 150 #endif /* CIDR6_LPM_PREFILTER */ 151 return map_lookup_elem(&CIDR6_HMAP_NAME, &pfx) ? 152 XDP_DROP : check_v6_endpoint(xdp, ipv6_hdr); 153 #else 154 return check_v6_endpoint(xdp, ipv6_hdr); 155 #endif /* CIDR6_FILTER */ 156 } 157 158 static __always_inline int check_filters(struct xdp_md *xdp) 159 { 160 void *data_end = xdp_data_end(xdp); 161 void *data = xdp_data(xdp); 162 struct ethhdr *eth = data; 163 __u16 proto; 164 165 if (xdp_no_room(eth + 1, data_end)) 166 return XDP_DROP; 167 168 proto = eth->h_proto; 169 if (proto == bpf_htons(ETH_P_IP)) 170 return check_v4(xdp); 171 else if (proto == bpf_htons(ETH_P_IPV6)) 172 return check_v6(xdp); 173 else 174 /* Pass the rest to stack, we might later do more 175 * fine-grained filtering here. 176 */ 177 return XDP_PASS; 178 } 179 180 __section("from-netdev") 181 int xdp_start(struct xdp_md *xdp) 182 { 183 return check_filters(xdp); 184 } 185 186 BPF_LICENSE("GPL");