FILE
nf_bpf_link.c
File preview with syntax highlighting.
linux-6.19.14/net/netfilter/nf_bpf_link.c
1// SPDX-License-Identifier: GPL-2.0 2#include <linux/bpf.h> 3#include <linux/filter.h> 4#include <linux/kmod.h> 5#include <linux/module.h> 6#include <linux/netfilter.h> 7 8#include <net/netfilter/nf_bpf_link.h> 9#include <uapi/linux/netfilter_ipv4.h> 10 11static unsigned int nf_hook_run_bpf(void *bpf_prog, struct sk_buff *skb, 12 const struct nf_hook_state *s) 13{ 14 const struct bpf_prog *prog = bpf_prog; 15 struct bpf_nf_ctx ctx = { 16 .state = s, 17 .skb = skb, 18 }; 19 20 return bpf_prog_run_pin_on_cpu(prog, &ctx); 21} 22 23struct bpf_nf_link { 24 struct bpf_link link; 25 struct nf_hook_ops hook_ops; 26 netns_tracker ns_tracker; 27 struct net *net; 28 u32 dead; 29 const struct nf_defrag_hook *defrag_hook; 30}; 31 32#if IS_ENABLED(CONFIG_NF_DEFRAG_IPV4) || IS_ENABLED(CONFIG_NF_DEFRAG_IPV6) 33static const struct nf_defrag_hook * 34get_proto_defrag_hook(struct bpf_nf_link *link, 35 const struct nf_defrag_hook __rcu **ptr_global_hook, 36 const char *mod) 37{ 38 const struct nf_defrag_hook *hook; 39 int err; 40 41 /* RCU protects us from races against module unloading */ 42 rcu_read_lock(); 43 hook = rcu_dereference(*ptr_global_hook); 44 if (!hook) { 45 rcu_read_unlock(); 46 err = request_module("%s", mod); 47 if (err) 48 return ERR_PTR(err < 0 ? err : -EINVAL); 49 50 rcu_read_lock(); 51 hook = rcu_dereference(*ptr_global_hook); 52 } 53 54 if (hook && try_module_get(hook->owner)) { 55 /* Once we have a refcnt on the module, we no longer need RCU */ 56 hook = rcu_pointer_handoff(hook); 57 } else { 58 WARN_ONCE(!hook, "%s has bad registration", mod); 59 hook = ERR_PTR(-ENOENT); 60 } 61 rcu_read_unlock(); 62 63 if (!IS_ERR(hook)) { 64 err = hook->enable(link->net); 65 if (err) { 66 module_put(hook->owner); 67 hook = ERR_PTR(err); 68 } 69 } 70 71 return hook; 72} 73#endif 74 75static int bpf_nf_enable_defrag(struct bpf_nf_link *link) 76{ 77 const struct nf_defrag_hook __maybe_unused *hook; 78 79 switch (link->hook_ops.pf) { 80#if IS_ENABLED(CONFIG_NF_DEFRAG_IPV4) 81 case NFPROTO_IPV4: 82 hook = get_proto_defrag_hook(link, &nf_defrag_v4_hook, "nf_defrag_ipv4"); 83 if (IS_ERR(hook)) 84 return PTR_ERR(hook); 85 86 link->defrag_hook = hook; 87 return 0; 88#endif 89#if IS_ENABLED(CONFIG_NF_DEFRAG_IPV6) 90 case NFPROTO_IPV6: 91 hook = get_proto_defrag_hook(link, &nf_defrag_v6_hook, "nf_defrag_ipv6"); 92 if (IS_ERR(hook)) 93 return PTR_ERR(hook); 94 95 link->defrag_hook = hook; 96 return 0; 97#endif 98 default: 99 return -EAFNOSUPPORT; 100 } 101} 102 103static void bpf_nf_disable_defrag(struct bpf_nf_link *link) 104{ 105 const struct nf_defrag_hook *hook = link->defrag_hook; 106 107 if (!hook) 108 return; 109 hook->disable(link->net); 110 module_put(hook->owner); 111} 112 113static void bpf_nf_link_release(struct bpf_link *link) 114{ 115 struct bpf_nf_link *nf_link = container_of(link, struct bpf_nf_link, link); 116 117 if (nf_link->dead) 118 return; 119 120 /* do not double release in case .detach was already called */ 121 if (!cmpxchg(&nf_link->dead, 0, 1)) { 122 nf_unregister_net_hook(nf_link->net, &nf_link->hook_ops); 123 bpf_nf_disable_defrag(nf_link); 124 put_net_track(nf_link->net, &nf_link->ns_tracker); 125 } 126} 127 128static void bpf_nf_link_dealloc(struct bpf_link *link) 129{ 130 struct bpf_nf_link *nf_link = container_of(link, struct bpf_nf_link, link); 131 132 kfree(nf_link); 133} 134 135static int bpf_nf_link_detach(struct bpf_link *link) 136{ 137 bpf_nf_link_release(link); 138 return 0; 139} 140 141static void bpf_nf_link_show_info(const struct bpf_link *link, 142 struct seq_file *seq) 143{ 144 struct bpf_nf_link *nf_link = container_of(link, struct bpf_nf_link, link); 145 146 seq_printf(seq, "pf:\t%u\thooknum:\t%u\tprio:\t%d\n", 147 nf_link->hook_ops.pf, nf_link->hook_ops.hooknum, 148 nf_link->hook_ops.priority); 149} 150 151static int bpf_nf_link_fill_link_info(const struct bpf_link *link, 152 struct bpf_link_info *info) 153{ 154 struct bpf_nf_link *nf_link = container_of(link, struct bpf_nf_link, link); 155 const struct nf_defrag_hook *hook = nf_link->defrag_hook; 156 157 info->netfilter.pf = nf_link->hook_ops.pf; 158 info->netfilter.hooknum = nf_link->hook_ops.hooknum; 159 info->netfilter.priority = nf_link->hook_ops.priority; 160 info->netfilter.flags = hook ? BPF_F_NETFILTER_IP_DEFRAG : 0; 161 162 return 0; 163} 164 165static int bpf_nf_link_update(struct bpf_link *link, struct bpf_prog *new_prog, 166 struct bpf_prog *old_prog) 167{ 168 return -EOPNOTSUPP; 169} 170 171static const struct bpf_link_ops bpf_nf_link_lops = { 172 .release = bpf_nf_link_release, 173 .dealloc_deferred = bpf_nf_link_dealloc, 174 .detach = bpf_nf_link_detach, 175 .show_fdinfo = bpf_nf_link_show_info, 176 .fill_link_info = bpf_nf_link_fill_link_info, 177 .update_prog = bpf_nf_link_update, 178}; 179 180static int bpf_nf_check_pf_and_hooks(const union bpf_attr *attr) 181{ 182 int prio; 183 184 switch (attr->link_create.netfilter.pf) { 185 case NFPROTO_IPV4: 186 case NFPROTO_IPV6: 187 if (attr->link_create.netfilter.hooknum >= NF_INET_NUMHOOKS) 188 return -EPROTO; 189 break; 190 default: 191 return -EAFNOSUPPORT; 192 } 193 194 if (attr->link_create.netfilter.flags & ~BPF_F_NETFILTER_IP_DEFRAG) 195 return -EOPNOTSUPP; 196 197 /* make sure conntrack confirm is always last */ 198 prio = attr->link_create.netfilter.priority; 199 if (prio == NF_IP_PRI_FIRST) 200 return -ERANGE; /* sabotage_in and other warts */ 201 else if (prio == NF_IP_PRI_LAST) 202 return -ERANGE; /* e.g. conntrack confirm */ 203 else if ((attr->link_create.netfilter.flags & BPF_F_NETFILTER_IP_DEFRAG) && 204 prio <= NF_IP_PRI_CONNTRACK_DEFRAG) 205 return -ERANGE; /* cannot use defrag if prog runs before nf_defrag */ 206 207 return 0; 208} 209 210int bpf_nf_link_attach(const union bpf_attr *attr, struct bpf_prog *prog) 211{ 212 struct net *net = current->nsproxy->net_ns; 213 struct bpf_link_primer link_primer; 214 struct bpf_nf_link *link; 215 int err; 216 217 if (attr->link_create.flags) 218 return -EINVAL; 219 220 err = bpf_nf_check_pf_and_hooks(attr); 221 if (err) 222 return err; 223 224 link = kzalloc(sizeof(*link), GFP_USER); 225 if (!link) 226 return -ENOMEM; 227 228 bpf_link_init(&link->link, BPF_LINK_TYPE_NETFILTER, &bpf_nf_link_lops, prog, 229 attr->link_create.attach_type); 230 231 link->hook_ops.hook = nf_hook_run_bpf; 232 link->hook_ops.hook_ops_type = NF_HOOK_OP_BPF; 233 link->hook_ops.priv = prog; 234 235 link->hook_ops.pf = attr->link_create.netfilter.pf; 236 link->hook_ops.priority = attr->link_create.netfilter.priority; 237 link->hook_ops.hooknum = attr->link_create.netfilter.hooknum; 238 239 link->net = net; 240 link->dead = false; 241 link->defrag_hook = NULL; 242 243 err = bpf_link_prime(&link->link, &link_primer); 244 if (err) { 245 kfree(link); 246 return err; 247 } 248 249 if (attr->link_create.netfilter.flags & BPF_F_NETFILTER_IP_DEFRAG) { 250 err = bpf_nf_enable_defrag(link); 251 if (err) { 252 bpf_link_cleanup(&link_primer); 253 return err; 254 } 255 } 256 257 err = nf_register_net_hook(net, &link->hook_ops); 258 if (err) { 259 bpf_nf_disable_defrag(link); 260 bpf_link_cleanup(&link_primer); 261 return err; 262 } 263 264 get_net_track(net, &link->ns_tracker, GFP_KERNEL); 265 266 return bpf_link_settle(&link_primer); 267} 268 269const struct bpf_prog_ops netfilter_prog_ops = { 270 .test_run = bpf_prog_test_run_nf, 271}; 272 273static bool nf_ptr_to_btf_id(struct bpf_insn_access_aux *info, const char *name) 274{ 275 struct btf *btf; 276 s32 type_id; 277 278 btf = bpf_get_btf_vmlinux(); 279 if (IS_ERR_OR_NULL(btf)) 280 return false; 281 282 type_id = btf_find_by_name_kind(btf, name, BTF_KIND_STRUCT); 283 if (WARN_ON_ONCE(type_id < 0)) 284 return false; 285 286 info->btf = btf; 287 info->btf_id = type_id; 288 info->reg_type = PTR_TO_BTF_ID | PTR_TRUSTED; 289 return true; 290} 291 292static bool nf_is_valid_access(int off, int size, enum bpf_access_type type, 293 const struct bpf_prog *prog, 294 struct bpf_insn_access_aux *info) 295{ 296 if (off < 0 || off >= sizeof(struct bpf_nf_ctx)) 297 return false; 298 299 if (off % size != 0) 300 return false; 301 302 if (type == BPF_WRITE) 303 return false; 304 305 switch (off) { 306 case bpf_ctx_range(struct bpf_nf_ctx, skb): 307 if (size != sizeof_field(struct bpf_nf_ctx, skb)) 308 return false; 309 310 return nf_ptr_to_btf_id(info, "sk_buff"); 311 case bpf_ctx_range(struct bpf_nf_ctx, state): 312 if (size != sizeof_field(struct bpf_nf_ctx, state)) 313 return false; 314 315 return nf_ptr_to_btf_id(info, "nf_hook_state"); 316 default: 317 return false; 318 } 319 320 return false; 321} 322 323static const struct bpf_func_proto * 324bpf_nf_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) 325{ 326 return bpf_base_func_proto(func_id, prog); 327} 328 329const struct bpf_verifier_ops netfilter_verifier_ops = { 330 .is_valid_access = nf_is_valid_access, 331 .get_func_proto = bpf_nf_func_proto, 332};
~/
Read-only file preview.