Lorenz Bauer | edc6741c | 2020-03-09 11:12:38 +0000 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | /* Copyright (c) 2020 Cloudflare Ltd https://cloudflare.com */ |
| 3 | |
| 4 | #include <linux/skmsg.h> |
| 5 | #include <net/sock.h> |
| 6 | #include <net/udp.h> |
| 7 | |
| 8 | enum { |
| 9 | UDP_BPF_IPV4, |
| 10 | UDP_BPF_IPV6, |
| 11 | UDP_BPF_NUM_PROTS, |
| 12 | }; |
| 13 | |
| 14 | static struct proto *udpv6_prot_saved __read_mostly; |
| 15 | static DEFINE_SPINLOCK(udpv6_prot_lock); |
| 16 | static struct proto udp_bpf_prots[UDP_BPF_NUM_PROTS]; |
| 17 | |
| 18 | static void udp_bpf_rebuild_protos(struct proto *prot, const struct proto *base) |
| 19 | { |
| 20 | *prot = *base; |
| 21 | prot->unhash = sock_map_unhash; |
| 22 | prot->close = sock_map_close; |
| 23 | } |
| 24 | |
| 25 | static void udp_bpf_check_v6_needs_rebuild(struct sock *sk, struct proto *ops) |
| 26 | { |
| 27 | if (sk->sk_family == AF_INET6 && |
| 28 | unlikely(ops != smp_load_acquire(&udpv6_prot_saved))) { |
| 29 | spin_lock_bh(&udpv6_prot_lock); |
| 30 | if (likely(ops != udpv6_prot_saved)) { |
| 31 | udp_bpf_rebuild_protos(&udp_bpf_prots[UDP_BPF_IPV6], ops); |
| 32 | smp_store_release(&udpv6_prot_saved, ops); |
| 33 | } |
| 34 | spin_unlock_bh(&udpv6_prot_lock); |
| 35 | } |
| 36 | } |
| 37 | |
| 38 | static int __init udp_bpf_v4_build_proto(void) |
| 39 | { |
| 40 | udp_bpf_rebuild_protos(&udp_bpf_prots[UDP_BPF_IPV4], &udp_prot); |
| 41 | return 0; |
| 42 | } |
| 43 | core_initcall(udp_bpf_v4_build_proto); |
| 44 | |
| 45 | struct proto *udp_bpf_get_proto(struct sock *sk, struct sk_psock *psock) |
| 46 | { |
| 47 | int family = sk->sk_family == AF_INET ? UDP_BPF_IPV4 : UDP_BPF_IPV6; |
| 48 | |
| 49 | if (!psock->sk_proto) |
| 50 | udp_bpf_check_v6_needs_rebuild(sk, READ_ONCE(sk->sk_prot)); |
| 51 | |
| 52 | return &udp_bpf_prots[family]; |
| 53 | } |