162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Copyright (c) 2017 - 2018 Covalent IO, Inc. http://covalent.io */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/bpf.h> 562306a36Sopenharmony_ci#include <linux/btf_ids.h> 662306a36Sopenharmony_ci#include <linux/filter.h> 762306a36Sopenharmony_ci#include <linux/errno.h> 862306a36Sopenharmony_ci#include <linux/file.h> 962306a36Sopenharmony_ci#include <linux/net.h> 1062306a36Sopenharmony_ci#include <linux/workqueue.h> 1162306a36Sopenharmony_ci#include <linux/skmsg.h> 1262306a36Sopenharmony_ci#include <linux/list.h> 1362306a36Sopenharmony_ci#include <linux/jhash.h> 1462306a36Sopenharmony_ci#include <linux/sock_diag.h> 1562306a36Sopenharmony_ci#include <net/udp.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_cistruct bpf_stab { 1862306a36Sopenharmony_ci struct bpf_map map; 1962306a36Sopenharmony_ci struct sock **sks; 2062306a36Sopenharmony_ci struct sk_psock_progs progs; 2162306a36Sopenharmony_ci spinlock_t lock; 2262306a36Sopenharmony_ci}; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define SOCK_CREATE_FLAG_MASK \ 2562306a36Sopenharmony_ci (BPF_F_NUMA_NODE | BPF_F_RDONLY | BPF_F_WRONLY) 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic int sock_map_prog_update(struct bpf_map *map, struct bpf_prog *prog, 2862306a36Sopenharmony_ci struct bpf_prog *old, u32 which); 2962306a36Sopenharmony_cistatic struct sk_psock_progs *sock_map_progs(struct bpf_map *map); 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic struct bpf_map *sock_map_alloc(union bpf_attr *attr) 3262306a36Sopenharmony_ci{ 3362306a36Sopenharmony_ci struct bpf_stab *stab; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci if (attr->max_entries == 0 || 3662306a36Sopenharmony_ci attr->key_size != 4 || 3762306a36Sopenharmony_ci (attr->value_size != sizeof(u32) && 3862306a36Sopenharmony_ci attr->value_size != sizeof(u64)) || 3962306a36Sopenharmony_ci attr->map_flags & ~SOCK_CREATE_FLAG_MASK) 4062306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci stab = bpf_map_area_alloc(sizeof(*stab), NUMA_NO_NODE); 4362306a36Sopenharmony_ci if (!stab) 4462306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci bpf_map_init_from_attr(&stab->map, attr); 4762306a36Sopenharmony_ci spin_lock_init(&stab->lock); 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci stab->sks = bpf_map_area_alloc((u64) stab->map.max_entries * 5062306a36Sopenharmony_ci sizeof(struct sock *), 5162306a36Sopenharmony_ci stab->map.numa_node); 5262306a36Sopenharmony_ci if (!stab->sks) { 5362306a36Sopenharmony_ci bpf_map_area_free(stab); 5462306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 5562306a36Sopenharmony_ci } 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci return &stab->map; 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ciint sock_map_get_from_fd(const union bpf_attr *attr, struct bpf_prog *prog) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci u32 ufd = attr->target_fd; 6362306a36Sopenharmony_ci struct bpf_map *map; 6462306a36Sopenharmony_ci struct fd f; 6562306a36Sopenharmony_ci int ret; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci if (attr->attach_flags || attr->replace_bpf_fd) 6862306a36Sopenharmony_ci return -EINVAL; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci f = fdget(ufd); 7162306a36Sopenharmony_ci map = __bpf_map_get(f); 7262306a36Sopenharmony_ci if (IS_ERR(map)) 7362306a36Sopenharmony_ci return PTR_ERR(map); 7462306a36Sopenharmony_ci ret = sock_map_prog_update(map, prog, NULL, attr->attach_type); 7562306a36Sopenharmony_ci fdput(f); 7662306a36Sopenharmony_ci return ret; 7762306a36Sopenharmony_ci} 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ciint sock_map_prog_detach(const union bpf_attr *attr, enum bpf_prog_type ptype) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci u32 ufd = attr->target_fd; 8262306a36Sopenharmony_ci struct bpf_prog *prog; 8362306a36Sopenharmony_ci struct bpf_map *map; 8462306a36Sopenharmony_ci struct fd f; 8562306a36Sopenharmony_ci int ret; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci if (attr->attach_flags || attr->replace_bpf_fd) 8862306a36Sopenharmony_ci return -EINVAL; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci f = fdget(ufd); 9162306a36Sopenharmony_ci map = __bpf_map_get(f); 9262306a36Sopenharmony_ci if (IS_ERR(map)) 9362306a36Sopenharmony_ci return PTR_ERR(map); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci prog = bpf_prog_get(attr->attach_bpf_fd); 9662306a36Sopenharmony_ci if (IS_ERR(prog)) { 9762306a36Sopenharmony_ci ret = PTR_ERR(prog); 9862306a36Sopenharmony_ci goto put_map; 9962306a36Sopenharmony_ci } 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci if (prog->type != ptype) { 10262306a36Sopenharmony_ci ret = -EINVAL; 10362306a36Sopenharmony_ci goto put_prog; 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci ret = sock_map_prog_update(map, NULL, prog, attr->attach_type); 10762306a36Sopenharmony_ciput_prog: 10862306a36Sopenharmony_ci bpf_prog_put(prog); 10962306a36Sopenharmony_ciput_map: 11062306a36Sopenharmony_ci fdput(f); 11162306a36Sopenharmony_ci return ret; 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cistatic void sock_map_sk_acquire(struct sock *sk) 11562306a36Sopenharmony_ci __acquires(&sk->sk_lock.slock) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci lock_sock(sk); 11862306a36Sopenharmony_ci rcu_read_lock(); 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic void sock_map_sk_release(struct sock *sk) 12262306a36Sopenharmony_ci __releases(&sk->sk_lock.slock) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci rcu_read_unlock(); 12562306a36Sopenharmony_ci release_sock(sk); 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic void sock_map_add_link(struct sk_psock *psock, 12962306a36Sopenharmony_ci struct sk_psock_link *link, 13062306a36Sopenharmony_ci struct bpf_map *map, void *link_raw) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci link->link_raw = link_raw; 13362306a36Sopenharmony_ci link->map = map; 13462306a36Sopenharmony_ci spin_lock_bh(&psock->link_lock); 13562306a36Sopenharmony_ci list_add_tail(&link->list, &psock->link); 13662306a36Sopenharmony_ci spin_unlock_bh(&psock->link_lock); 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_cistatic void sock_map_del_link(struct sock *sk, 14062306a36Sopenharmony_ci struct sk_psock *psock, void *link_raw) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci bool strp_stop = false, verdict_stop = false; 14362306a36Sopenharmony_ci struct sk_psock_link *link, *tmp; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci spin_lock_bh(&psock->link_lock); 14662306a36Sopenharmony_ci list_for_each_entry_safe(link, tmp, &psock->link, list) { 14762306a36Sopenharmony_ci if (link->link_raw == link_raw) { 14862306a36Sopenharmony_ci struct bpf_map *map = link->map; 14962306a36Sopenharmony_ci struct sk_psock_progs *progs = sock_map_progs(map); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci if (psock->saved_data_ready && progs->stream_parser) 15262306a36Sopenharmony_ci strp_stop = true; 15362306a36Sopenharmony_ci if (psock->saved_data_ready && progs->stream_verdict) 15462306a36Sopenharmony_ci verdict_stop = true; 15562306a36Sopenharmony_ci if (psock->saved_data_ready && progs->skb_verdict) 15662306a36Sopenharmony_ci verdict_stop = true; 15762306a36Sopenharmony_ci list_del(&link->list); 15862306a36Sopenharmony_ci sk_psock_free_link(link); 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci spin_unlock_bh(&psock->link_lock); 16262306a36Sopenharmony_ci if (strp_stop || verdict_stop) { 16362306a36Sopenharmony_ci write_lock_bh(&sk->sk_callback_lock); 16462306a36Sopenharmony_ci if (strp_stop) 16562306a36Sopenharmony_ci sk_psock_stop_strp(sk, psock); 16662306a36Sopenharmony_ci if (verdict_stop) 16762306a36Sopenharmony_ci sk_psock_stop_verdict(sk, psock); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci if (psock->psock_update_sk_prot) 17062306a36Sopenharmony_ci psock->psock_update_sk_prot(sk, psock, false); 17162306a36Sopenharmony_ci write_unlock_bh(&sk->sk_callback_lock); 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci} 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_cistatic void sock_map_unref(struct sock *sk, void *link_raw) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci struct sk_psock *psock = sk_psock(sk); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci if (likely(psock)) { 18062306a36Sopenharmony_ci sock_map_del_link(sk, psock, link_raw); 18162306a36Sopenharmony_ci sk_psock_put(sk, psock); 18262306a36Sopenharmony_ci } 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_cistatic int sock_map_init_proto(struct sock *sk, struct sk_psock *psock) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci if (!sk->sk_prot->psock_update_sk_prot) 18862306a36Sopenharmony_ci return -EINVAL; 18962306a36Sopenharmony_ci psock->psock_update_sk_prot = sk->sk_prot->psock_update_sk_prot; 19062306a36Sopenharmony_ci return sk->sk_prot->psock_update_sk_prot(sk, psock, false); 19162306a36Sopenharmony_ci} 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_cistatic struct sk_psock *sock_map_psock_get_checked(struct sock *sk) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci struct sk_psock *psock; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci rcu_read_lock(); 19862306a36Sopenharmony_ci psock = sk_psock(sk); 19962306a36Sopenharmony_ci if (psock) { 20062306a36Sopenharmony_ci if (sk->sk_prot->close != sock_map_close) { 20162306a36Sopenharmony_ci psock = ERR_PTR(-EBUSY); 20262306a36Sopenharmony_ci goto out; 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci if (!refcount_inc_not_zero(&psock->refcnt)) 20662306a36Sopenharmony_ci psock = ERR_PTR(-EBUSY); 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ciout: 20962306a36Sopenharmony_ci rcu_read_unlock(); 21062306a36Sopenharmony_ci return psock; 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_cistatic int sock_map_link(struct bpf_map *map, struct sock *sk) 21462306a36Sopenharmony_ci{ 21562306a36Sopenharmony_ci struct sk_psock_progs *progs = sock_map_progs(map); 21662306a36Sopenharmony_ci struct bpf_prog *stream_verdict = NULL; 21762306a36Sopenharmony_ci struct bpf_prog *stream_parser = NULL; 21862306a36Sopenharmony_ci struct bpf_prog *skb_verdict = NULL; 21962306a36Sopenharmony_ci struct bpf_prog *msg_parser = NULL; 22062306a36Sopenharmony_ci struct sk_psock *psock; 22162306a36Sopenharmony_ci int ret; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci stream_verdict = READ_ONCE(progs->stream_verdict); 22462306a36Sopenharmony_ci if (stream_verdict) { 22562306a36Sopenharmony_ci stream_verdict = bpf_prog_inc_not_zero(stream_verdict); 22662306a36Sopenharmony_ci if (IS_ERR(stream_verdict)) 22762306a36Sopenharmony_ci return PTR_ERR(stream_verdict); 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci stream_parser = READ_ONCE(progs->stream_parser); 23162306a36Sopenharmony_ci if (stream_parser) { 23262306a36Sopenharmony_ci stream_parser = bpf_prog_inc_not_zero(stream_parser); 23362306a36Sopenharmony_ci if (IS_ERR(stream_parser)) { 23462306a36Sopenharmony_ci ret = PTR_ERR(stream_parser); 23562306a36Sopenharmony_ci goto out_put_stream_verdict; 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci msg_parser = READ_ONCE(progs->msg_parser); 24062306a36Sopenharmony_ci if (msg_parser) { 24162306a36Sopenharmony_ci msg_parser = bpf_prog_inc_not_zero(msg_parser); 24262306a36Sopenharmony_ci if (IS_ERR(msg_parser)) { 24362306a36Sopenharmony_ci ret = PTR_ERR(msg_parser); 24462306a36Sopenharmony_ci goto out_put_stream_parser; 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci } 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci skb_verdict = READ_ONCE(progs->skb_verdict); 24962306a36Sopenharmony_ci if (skb_verdict) { 25062306a36Sopenharmony_ci skb_verdict = bpf_prog_inc_not_zero(skb_verdict); 25162306a36Sopenharmony_ci if (IS_ERR(skb_verdict)) { 25262306a36Sopenharmony_ci ret = PTR_ERR(skb_verdict); 25362306a36Sopenharmony_ci goto out_put_msg_parser; 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci psock = sock_map_psock_get_checked(sk); 25862306a36Sopenharmony_ci if (IS_ERR(psock)) { 25962306a36Sopenharmony_ci ret = PTR_ERR(psock); 26062306a36Sopenharmony_ci goto out_progs; 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci if (psock) { 26462306a36Sopenharmony_ci if ((msg_parser && READ_ONCE(psock->progs.msg_parser)) || 26562306a36Sopenharmony_ci (stream_parser && READ_ONCE(psock->progs.stream_parser)) || 26662306a36Sopenharmony_ci (skb_verdict && READ_ONCE(psock->progs.skb_verdict)) || 26762306a36Sopenharmony_ci (skb_verdict && READ_ONCE(psock->progs.stream_verdict)) || 26862306a36Sopenharmony_ci (stream_verdict && READ_ONCE(psock->progs.skb_verdict)) || 26962306a36Sopenharmony_ci (stream_verdict && READ_ONCE(psock->progs.stream_verdict))) { 27062306a36Sopenharmony_ci sk_psock_put(sk, psock); 27162306a36Sopenharmony_ci ret = -EBUSY; 27262306a36Sopenharmony_ci goto out_progs; 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci } else { 27562306a36Sopenharmony_ci psock = sk_psock_init(sk, map->numa_node); 27662306a36Sopenharmony_ci if (IS_ERR(psock)) { 27762306a36Sopenharmony_ci ret = PTR_ERR(psock); 27862306a36Sopenharmony_ci goto out_progs; 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci if (msg_parser) 28362306a36Sopenharmony_ci psock_set_prog(&psock->progs.msg_parser, msg_parser); 28462306a36Sopenharmony_ci if (stream_parser) 28562306a36Sopenharmony_ci psock_set_prog(&psock->progs.stream_parser, stream_parser); 28662306a36Sopenharmony_ci if (stream_verdict) 28762306a36Sopenharmony_ci psock_set_prog(&psock->progs.stream_verdict, stream_verdict); 28862306a36Sopenharmony_ci if (skb_verdict) 28962306a36Sopenharmony_ci psock_set_prog(&psock->progs.skb_verdict, skb_verdict); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci /* msg_* and stream_* programs references tracked in psock after this 29262306a36Sopenharmony_ci * point. Reference dec and cleanup will occur through psock destructor 29362306a36Sopenharmony_ci */ 29462306a36Sopenharmony_ci ret = sock_map_init_proto(sk, psock); 29562306a36Sopenharmony_ci if (ret < 0) { 29662306a36Sopenharmony_ci sk_psock_put(sk, psock); 29762306a36Sopenharmony_ci goto out; 29862306a36Sopenharmony_ci } 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci write_lock_bh(&sk->sk_callback_lock); 30162306a36Sopenharmony_ci if (stream_parser && stream_verdict && !psock->saved_data_ready) { 30262306a36Sopenharmony_ci ret = sk_psock_init_strp(sk, psock); 30362306a36Sopenharmony_ci if (ret) { 30462306a36Sopenharmony_ci write_unlock_bh(&sk->sk_callback_lock); 30562306a36Sopenharmony_ci sk_psock_put(sk, psock); 30662306a36Sopenharmony_ci goto out; 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci sk_psock_start_strp(sk, psock); 30962306a36Sopenharmony_ci } else if (!stream_parser && stream_verdict && !psock->saved_data_ready) { 31062306a36Sopenharmony_ci sk_psock_start_verdict(sk,psock); 31162306a36Sopenharmony_ci } else if (!stream_verdict && skb_verdict && !psock->saved_data_ready) { 31262306a36Sopenharmony_ci sk_psock_start_verdict(sk, psock); 31362306a36Sopenharmony_ci } 31462306a36Sopenharmony_ci write_unlock_bh(&sk->sk_callback_lock); 31562306a36Sopenharmony_ci return 0; 31662306a36Sopenharmony_ciout_progs: 31762306a36Sopenharmony_ci if (skb_verdict) 31862306a36Sopenharmony_ci bpf_prog_put(skb_verdict); 31962306a36Sopenharmony_ciout_put_msg_parser: 32062306a36Sopenharmony_ci if (msg_parser) 32162306a36Sopenharmony_ci bpf_prog_put(msg_parser); 32262306a36Sopenharmony_ciout_put_stream_parser: 32362306a36Sopenharmony_ci if (stream_parser) 32462306a36Sopenharmony_ci bpf_prog_put(stream_parser); 32562306a36Sopenharmony_ciout_put_stream_verdict: 32662306a36Sopenharmony_ci if (stream_verdict) 32762306a36Sopenharmony_ci bpf_prog_put(stream_verdict); 32862306a36Sopenharmony_ciout: 32962306a36Sopenharmony_ci return ret; 33062306a36Sopenharmony_ci} 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_cistatic void sock_map_free(struct bpf_map *map) 33362306a36Sopenharmony_ci{ 33462306a36Sopenharmony_ci struct bpf_stab *stab = container_of(map, struct bpf_stab, map); 33562306a36Sopenharmony_ci int i; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci /* After the sync no updates or deletes will be in-flight so it 33862306a36Sopenharmony_ci * is safe to walk map and remove entries without risking a race 33962306a36Sopenharmony_ci * in EEXIST update case. 34062306a36Sopenharmony_ci */ 34162306a36Sopenharmony_ci synchronize_rcu(); 34262306a36Sopenharmony_ci for (i = 0; i < stab->map.max_entries; i++) { 34362306a36Sopenharmony_ci struct sock **psk = &stab->sks[i]; 34462306a36Sopenharmony_ci struct sock *sk; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci sk = xchg(psk, NULL); 34762306a36Sopenharmony_ci if (sk) { 34862306a36Sopenharmony_ci sock_hold(sk); 34962306a36Sopenharmony_ci lock_sock(sk); 35062306a36Sopenharmony_ci rcu_read_lock(); 35162306a36Sopenharmony_ci sock_map_unref(sk, psk); 35262306a36Sopenharmony_ci rcu_read_unlock(); 35362306a36Sopenharmony_ci release_sock(sk); 35462306a36Sopenharmony_ci sock_put(sk); 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci /* wait for psock readers accessing its map link */ 35962306a36Sopenharmony_ci synchronize_rcu(); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci bpf_map_area_free(stab->sks); 36262306a36Sopenharmony_ci bpf_map_area_free(stab); 36362306a36Sopenharmony_ci} 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_cistatic void sock_map_release_progs(struct bpf_map *map) 36662306a36Sopenharmony_ci{ 36762306a36Sopenharmony_ci psock_progs_drop(&container_of(map, struct bpf_stab, map)->progs); 36862306a36Sopenharmony_ci} 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_cistatic struct sock *__sock_map_lookup_elem(struct bpf_map *map, u32 key) 37162306a36Sopenharmony_ci{ 37262306a36Sopenharmony_ci struct bpf_stab *stab = container_of(map, struct bpf_stab, map); 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci WARN_ON_ONCE(!rcu_read_lock_held()); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci if (unlikely(key >= map->max_entries)) 37762306a36Sopenharmony_ci return NULL; 37862306a36Sopenharmony_ci return READ_ONCE(stab->sks[key]); 37962306a36Sopenharmony_ci} 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_cistatic void *sock_map_lookup(struct bpf_map *map, void *key) 38262306a36Sopenharmony_ci{ 38362306a36Sopenharmony_ci struct sock *sk; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci sk = __sock_map_lookup_elem(map, *(u32 *)key); 38662306a36Sopenharmony_ci if (!sk) 38762306a36Sopenharmony_ci return NULL; 38862306a36Sopenharmony_ci if (sk_is_refcounted(sk) && !refcount_inc_not_zero(&sk->sk_refcnt)) 38962306a36Sopenharmony_ci return NULL; 39062306a36Sopenharmony_ci return sk; 39162306a36Sopenharmony_ci} 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_cistatic void *sock_map_lookup_sys(struct bpf_map *map, void *key) 39462306a36Sopenharmony_ci{ 39562306a36Sopenharmony_ci struct sock *sk; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci if (map->value_size != sizeof(u64)) 39862306a36Sopenharmony_ci return ERR_PTR(-ENOSPC); 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci sk = __sock_map_lookup_elem(map, *(u32 *)key); 40162306a36Sopenharmony_ci if (!sk) 40262306a36Sopenharmony_ci return ERR_PTR(-ENOENT); 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci __sock_gen_cookie(sk); 40562306a36Sopenharmony_ci return &sk->sk_cookie; 40662306a36Sopenharmony_ci} 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_cistatic int __sock_map_delete(struct bpf_stab *stab, struct sock *sk_test, 40962306a36Sopenharmony_ci struct sock **psk) 41062306a36Sopenharmony_ci{ 41162306a36Sopenharmony_ci struct sock *sk; 41262306a36Sopenharmony_ci int err = 0; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci spin_lock_bh(&stab->lock); 41562306a36Sopenharmony_ci sk = *psk; 41662306a36Sopenharmony_ci if (!sk_test || sk_test == sk) 41762306a36Sopenharmony_ci sk = xchg(psk, NULL); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci if (likely(sk)) 42062306a36Sopenharmony_ci sock_map_unref(sk, psk); 42162306a36Sopenharmony_ci else 42262306a36Sopenharmony_ci err = -EINVAL; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci spin_unlock_bh(&stab->lock); 42562306a36Sopenharmony_ci return err; 42662306a36Sopenharmony_ci} 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_cistatic void sock_map_delete_from_link(struct bpf_map *map, struct sock *sk, 42962306a36Sopenharmony_ci void *link_raw) 43062306a36Sopenharmony_ci{ 43162306a36Sopenharmony_ci struct bpf_stab *stab = container_of(map, struct bpf_stab, map); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci __sock_map_delete(stab, sk, link_raw); 43462306a36Sopenharmony_ci} 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_cistatic long sock_map_delete_elem(struct bpf_map *map, void *key) 43762306a36Sopenharmony_ci{ 43862306a36Sopenharmony_ci struct bpf_stab *stab = container_of(map, struct bpf_stab, map); 43962306a36Sopenharmony_ci u32 i = *(u32 *)key; 44062306a36Sopenharmony_ci struct sock **psk; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci if (unlikely(i >= map->max_entries)) 44362306a36Sopenharmony_ci return -EINVAL; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci psk = &stab->sks[i]; 44662306a36Sopenharmony_ci return __sock_map_delete(stab, NULL, psk); 44762306a36Sopenharmony_ci} 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_cistatic int sock_map_get_next_key(struct bpf_map *map, void *key, void *next) 45062306a36Sopenharmony_ci{ 45162306a36Sopenharmony_ci struct bpf_stab *stab = container_of(map, struct bpf_stab, map); 45262306a36Sopenharmony_ci u32 i = key ? *(u32 *)key : U32_MAX; 45362306a36Sopenharmony_ci u32 *key_next = next; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci if (i == stab->map.max_entries - 1) 45662306a36Sopenharmony_ci return -ENOENT; 45762306a36Sopenharmony_ci if (i >= stab->map.max_entries) 45862306a36Sopenharmony_ci *key_next = 0; 45962306a36Sopenharmony_ci else 46062306a36Sopenharmony_ci *key_next = i + 1; 46162306a36Sopenharmony_ci return 0; 46262306a36Sopenharmony_ci} 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_cistatic int sock_map_update_common(struct bpf_map *map, u32 idx, 46562306a36Sopenharmony_ci struct sock *sk, u64 flags) 46662306a36Sopenharmony_ci{ 46762306a36Sopenharmony_ci struct bpf_stab *stab = container_of(map, struct bpf_stab, map); 46862306a36Sopenharmony_ci struct sk_psock_link *link; 46962306a36Sopenharmony_ci struct sk_psock *psock; 47062306a36Sopenharmony_ci struct sock *osk; 47162306a36Sopenharmony_ci int ret; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci WARN_ON_ONCE(!rcu_read_lock_held()); 47462306a36Sopenharmony_ci if (unlikely(flags > BPF_EXIST)) 47562306a36Sopenharmony_ci return -EINVAL; 47662306a36Sopenharmony_ci if (unlikely(idx >= map->max_entries)) 47762306a36Sopenharmony_ci return -E2BIG; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci link = sk_psock_init_link(); 48062306a36Sopenharmony_ci if (!link) 48162306a36Sopenharmony_ci return -ENOMEM; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci ret = sock_map_link(map, sk); 48462306a36Sopenharmony_ci if (ret < 0) 48562306a36Sopenharmony_ci goto out_free; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci psock = sk_psock(sk); 48862306a36Sopenharmony_ci WARN_ON_ONCE(!psock); 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci spin_lock_bh(&stab->lock); 49162306a36Sopenharmony_ci osk = stab->sks[idx]; 49262306a36Sopenharmony_ci if (osk && flags == BPF_NOEXIST) { 49362306a36Sopenharmony_ci ret = -EEXIST; 49462306a36Sopenharmony_ci goto out_unlock; 49562306a36Sopenharmony_ci } else if (!osk && flags == BPF_EXIST) { 49662306a36Sopenharmony_ci ret = -ENOENT; 49762306a36Sopenharmony_ci goto out_unlock; 49862306a36Sopenharmony_ci } 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci sock_map_add_link(psock, link, map, &stab->sks[idx]); 50162306a36Sopenharmony_ci stab->sks[idx] = sk; 50262306a36Sopenharmony_ci if (osk) 50362306a36Sopenharmony_ci sock_map_unref(osk, &stab->sks[idx]); 50462306a36Sopenharmony_ci spin_unlock_bh(&stab->lock); 50562306a36Sopenharmony_ci return 0; 50662306a36Sopenharmony_ciout_unlock: 50762306a36Sopenharmony_ci spin_unlock_bh(&stab->lock); 50862306a36Sopenharmony_ci if (psock) 50962306a36Sopenharmony_ci sk_psock_put(sk, psock); 51062306a36Sopenharmony_ciout_free: 51162306a36Sopenharmony_ci sk_psock_free_link(link); 51262306a36Sopenharmony_ci return ret; 51362306a36Sopenharmony_ci} 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_cistatic bool sock_map_op_okay(const struct bpf_sock_ops_kern *ops) 51662306a36Sopenharmony_ci{ 51762306a36Sopenharmony_ci return ops->op == BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB || 51862306a36Sopenharmony_ci ops->op == BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB || 51962306a36Sopenharmony_ci ops->op == BPF_SOCK_OPS_TCP_LISTEN_CB; 52062306a36Sopenharmony_ci} 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_cistatic bool sock_map_redirect_allowed(const struct sock *sk) 52362306a36Sopenharmony_ci{ 52462306a36Sopenharmony_ci if (sk_is_tcp(sk)) 52562306a36Sopenharmony_ci return sk->sk_state != TCP_LISTEN; 52662306a36Sopenharmony_ci else 52762306a36Sopenharmony_ci return sk->sk_state == TCP_ESTABLISHED; 52862306a36Sopenharmony_ci} 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_cistatic bool sock_map_sk_is_suitable(const struct sock *sk) 53162306a36Sopenharmony_ci{ 53262306a36Sopenharmony_ci return !!sk->sk_prot->psock_update_sk_prot; 53362306a36Sopenharmony_ci} 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_cistatic bool sock_map_sk_state_allowed(const struct sock *sk) 53662306a36Sopenharmony_ci{ 53762306a36Sopenharmony_ci if (sk_is_tcp(sk)) 53862306a36Sopenharmony_ci return (1 << sk->sk_state) & (TCPF_ESTABLISHED | TCPF_LISTEN); 53962306a36Sopenharmony_ci if (sk_is_stream_unix(sk)) 54062306a36Sopenharmony_ci return (1 << sk->sk_state) & TCPF_ESTABLISHED; 54162306a36Sopenharmony_ci return true; 54262306a36Sopenharmony_ci} 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_cistatic int sock_hash_update_common(struct bpf_map *map, void *key, 54562306a36Sopenharmony_ci struct sock *sk, u64 flags); 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ciint sock_map_update_elem_sys(struct bpf_map *map, void *key, void *value, 54862306a36Sopenharmony_ci u64 flags) 54962306a36Sopenharmony_ci{ 55062306a36Sopenharmony_ci struct socket *sock; 55162306a36Sopenharmony_ci struct sock *sk; 55262306a36Sopenharmony_ci int ret; 55362306a36Sopenharmony_ci u64 ufd; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci if (map->value_size == sizeof(u64)) 55662306a36Sopenharmony_ci ufd = *(u64 *)value; 55762306a36Sopenharmony_ci else 55862306a36Sopenharmony_ci ufd = *(u32 *)value; 55962306a36Sopenharmony_ci if (ufd > S32_MAX) 56062306a36Sopenharmony_ci return -EINVAL; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci sock = sockfd_lookup(ufd, &ret); 56362306a36Sopenharmony_ci if (!sock) 56462306a36Sopenharmony_ci return ret; 56562306a36Sopenharmony_ci sk = sock->sk; 56662306a36Sopenharmony_ci if (!sk) { 56762306a36Sopenharmony_ci ret = -EINVAL; 56862306a36Sopenharmony_ci goto out; 56962306a36Sopenharmony_ci } 57062306a36Sopenharmony_ci if (!sock_map_sk_is_suitable(sk)) { 57162306a36Sopenharmony_ci ret = -EOPNOTSUPP; 57262306a36Sopenharmony_ci goto out; 57362306a36Sopenharmony_ci } 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci sock_map_sk_acquire(sk); 57662306a36Sopenharmony_ci if (!sock_map_sk_state_allowed(sk)) 57762306a36Sopenharmony_ci ret = -EOPNOTSUPP; 57862306a36Sopenharmony_ci else if (map->map_type == BPF_MAP_TYPE_SOCKMAP) 57962306a36Sopenharmony_ci ret = sock_map_update_common(map, *(u32 *)key, sk, flags); 58062306a36Sopenharmony_ci else 58162306a36Sopenharmony_ci ret = sock_hash_update_common(map, key, sk, flags); 58262306a36Sopenharmony_ci sock_map_sk_release(sk); 58362306a36Sopenharmony_ciout: 58462306a36Sopenharmony_ci sockfd_put(sock); 58562306a36Sopenharmony_ci return ret; 58662306a36Sopenharmony_ci} 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_cistatic long sock_map_update_elem(struct bpf_map *map, void *key, 58962306a36Sopenharmony_ci void *value, u64 flags) 59062306a36Sopenharmony_ci{ 59162306a36Sopenharmony_ci struct sock *sk = (struct sock *)value; 59262306a36Sopenharmony_ci int ret; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci if (unlikely(!sk || !sk_fullsock(sk))) 59562306a36Sopenharmony_ci return -EINVAL; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci if (!sock_map_sk_is_suitable(sk)) 59862306a36Sopenharmony_ci return -EOPNOTSUPP; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci local_bh_disable(); 60162306a36Sopenharmony_ci bh_lock_sock(sk); 60262306a36Sopenharmony_ci if (!sock_map_sk_state_allowed(sk)) 60362306a36Sopenharmony_ci ret = -EOPNOTSUPP; 60462306a36Sopenharmony_ci else if (map->map_type == BPF_MAP_TYPE_SOCKMAP) 60562306a36Sopenharmony_ci ret = sock_map_update_common(map, *(u32 *)key, sk, flags); 60662306a36Sopenharmony_ci else 60762306a36Sopenharmony_ci ret = sock_hash_update_common(map, key, sk, flags); 60862306a36Sopenharmony_ci bh_unlock_sock(sk); 60962306a36Sopenharmony_ci local_bh_enable(); 61062306a36Sopenharmony_ci return ret; 61162306a36Sopenharmony_ci} 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ciBPF_CALL_4(bpf_sock_map_update, struct bpf_sock_ops_kern *, sops, 61462306a36Sopenharmony_ci struct bpf_map *, map, void *, key, u64, flags) 61562306a36Sopenharmony_ci{ 61662306a36Sopenharmony_ci WARN_ON_ONCE(!rcu_read_lock_held()); 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci if (likely(sock_map_sk_is_suitable(sops->sk) && 61962306a36Sopenharmony_ci sock_map_op_okay(sops))) 62062306a36Sopenharmony_ci return sock_map_update_common(map, *(u32 *)key, sops->sk, 62162306a36Sopenharmony_ci flags); 62262306a36Sopenharmony_ci return -EOPNOTSUPP; 62362306a36Sopenharmony_ci} 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ciconst struct bpf_func_proto bpf_sock_map_update_proto = { 62662306a36Sopenharmony_ci .func = bpf_sock_map_update, 62762306a36Sopenharmony_ci .gpl_only = false, 62862306a36Sopenharmony_ci .pkt_access = true, 62962306a36Sopenharmony_ci .ret_type = RET_INTEGER, 63062306a36Sopenharmony_ci .arg1_type = ARG_PTR_TO_CTX, 63162306a36Sopenharmony_ci .arg2_type = ARG_CONST_MAP_PTR, 63262306a36Sopenharmony_ci .arg3_type = ARG_PTR_TO_MAP_KEY, 63362306a36Sopenharmony_ci .arg4_type = ARG_ANYTHING, 63462306a36Sopenharmony_ci}; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ciBPF_CALL_4(bpf_sk_redirect_map, struct sk_buff *, skb, 63762306a36Sopenharmony_ci struct bpf_map *, map, u32, key, u64, flags) 63862306a36Sopenharmony_ci{ 63962306a36Sopenharmony_ci struct sock *sk; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci if (unlikely(flags & ~(BPF_F_INGRESS))) 64262306a36Sopenharmony_ci return SK_DROP; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci sk = __sock_map_lookup_elem(map, key); 64562306a36Sopenharmony_ci if (unlikely(!sk || !sock_map_redirect_allowed(sk))) 64662306a36Sopenharmony_ci return SK_DROP; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci skb_bpf_set_redir(skb, sk, flags & BPF_F_INGRESS); 64962306a36Sopenharmony_ci return SK_PASS; 65062306a36Sopenharmony_ci} 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ciconst struct bpf_func_proto bpf_sk_redirect_map_proto = { 65362306a36Sopenharmony_ci .func = bpf_sk_redirect_map, 65462306a36Sopenharmony_ci .gpl_only = false, 65562306a36Sopenharmony_ci .ret_type = RET_INTEGER, 65662306a36Sopenharmony_ci .arg1_type = ARG_PTR_TO_CTX, 65762306a36Sopenharmony_ci .arg2_type = ARG_CONST_MAP_PTR, 65862306a36Sopenharmony_ci .arg3_type = ARG_ANYTHING, 65962306a36Sopenharmony_ci .arg4_type = ARG_ANYTHING, 66062306a36Sopenharmony_ci}; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ciBPF_CALL_4(bpf_msg_redirect_map, struct sk_msg *, msg, 66362306a36Sopenharmony_ci struct bpf_map *, map, u32, key, u64, flags) 66462306a36Sopenharmony_ci{ 66562306a36Sopenharmony_ci struct sock *sk; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci if (unlikely(flags & ~(BPF_F_INGRESS))) 66862306a36Sopenharmony_ci return SK_DROP; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci sk = __sock_map_lookup_elem(map, key); 67162306a36Sopenharmony_ci if (unlikely(!sk || !sock_map_redirect_allowed(sk))) 67262306a36Sopenharmony_ci return SK_DROP; 67362306a36Sopenharmony_ci if (!(flags & BPF_F_INGRESS) && !sk_is_tcp(sk)) 67462306a36Sopenharmony_ci return SK_DROP; 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci msg->flags = flags; 67762306a36Sopenharmony_ci msg->sk_redir = sk; 67862306a36Sopenharmony_ci return SK_PASS; 67962306a36Sopenharmony_ci} 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ciconst struct bpf_func_proto bpf_msg_redirect_map_proto = { 68262306a36Sopenharmony_ci .func = bpf_msg_redirect_map, 68362306a36Sopenharmony_ci .gpl_only = false, 68462306a36Sopenharmony_ci .ret_type = RET_INTEGER, 68562306a36Sopenharmony_ci .arg1_type = ARG_PTR_TO_CTX, 68662306a36Sopenharmony_ci .arg2_type = ARG_CONST_MAP_PTR, 68762306a36Sopenharmony_ci .arg3_type = ARG_ANYTHING, 68862306a36Sopenharmony_ci .arg4_type = ARG_ANYTHING, 68962306a36Sopenharmony_ci}; 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_cistruct sock_map_seq_info { 69262306a36Sopenharmony_ci struct bpf_map *map; 69362306a36Sopenharmony_ci struct sock *sk; 69462306a36Sopenharmony_ci u32 index; 69562306a36Sopenharmony_ci}; 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_cistruct bpf_iter__sockmap { 69862306a36Sopenharmony_ci __bpf_md_ptr(struct bpf_iter_meta *, meta); 69962306a36Sopenharmony_ci __bpf_md_ptr(struct bpf_map *, map); 70062306a36Sopenharmony_ci __bpf_md_ptr(void *, key); 70162306a36Sopenharmony_ci __bpf_md_ptr(struct sock *, sk); 70262306a36Sopenharmony_ci}; 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ciDEFINE_BPF_ITER_FUNC(sockmap, struct bpf_iter_meta *meta, 70562306a36Sopenharmony_ci struct bpf_map *map, void *key, 70662306a36Sopenharmony_ci struct sock *sk) 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_cistatic void *sock_map_seq_lookup_elem(struct sock_map_seq_info *info) 70962306a36Sopenharmony_ci{ 71062306a36Sopenharmony_ci if (unlikely(info->index >= info->map->max_entries)) 71162306a36Sopenharmony_ci return NULL; 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci info->sk = __sock_map_lookup_elem(info->map, info->index); 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci /* can't return sk directly, since that might be NULL */ 71662306a36Sopenharmony_ci return info; 71762306a36Sopenharmony_ci} 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_cistatic void *sock_map_seq_start(struct seq_file *seq, loff_t *pos) 72062306a36Sopenharmony_ci __acquires(rcu) 72162306a36Sopenharmony_ci{ 72262306a36Sopenharmony_ci struct sock_map_seq_info *info = seq->private; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci if (*pos == 0) 72562306a36Sopenharmony_ci ++*pos; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci /* pairs with sock_map_seq_stop */ 72862306a36Sopenharmony_ci rcu_read_lock(); 72962306a36Sopenharmony_ci return sock_map_seq_lookup_elem(info); 73062306a36Sopenharmony_ci} 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_cistatic void *sock_map_seq_next(struct seq_file *seq, void *v, loff_t *pos) 73362306a36Sopenharmony_ci __must_hold(rcu) 73462306a36Sopenharmony_ci{ 73562306a36Sopenharmony_ci struct sock_map_seq_info *info = seq->private; 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci ++*pos; 73862306a36Sopenharmony_ci ++info->index; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci return sock_map_seq_lookup_elem(info); 74162306a36Sopenharmony_ci} 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_cistatic int sock_map_seq_show(struct seq_file *seq, void *v) 74462306a36Sopenharmony_ci __must_hold(rcu) 74562306a36Sopenharmony_ci{ 74662306a36Sopenharmony_ci struct sock_map_seq_info *info = seq->private; 74762306a36Sopenharmony_ci struct bpf_iter__sockmap ctx = {}; 74862306a36Sopenharmony_ci struct bpf_iter_meta meta; 74962306a36Sopenharmony_ci struct bpf_prog *prog; 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci meta.seq = seq; 75262306a36Sopenharmony_ci prog = bpf_iter_get_info(&meta, !v); 75362306a36Sopenharmony_ci if (!prog) 75462306a36Sopenharmony_ci return 0; 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci ctx.meta = &meta; 75762306a36Sopenharmony_ci ctx.map = info->map; 75862306a36Sopenharmony_ci if (v) { 75962306a36Sopenharmony_ci ctx.key = &info->index; 76062306a36Sopenharmony_ci ctx.sk = info->sk; 76162306a36Sopenharmony_ci } 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci return bpf_iter_run_prog(prog, &ctx); 76462306a36Sopenharmony_ci} 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_cistatic void sock_map_seq_stop(struct seq_file *seq, void *v) 76762306a36Sopenharmony_ci __releases(rcu) 76862306a36Sopenharmony_ci{ 76962306a36Sopenharmony_ci if (!v) 77062306a36Sopenharmony_ci (void)sock_map_seq_show(seq, NULL); 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci /* pairs with sock_map_seq_start */ 77362306a36Sopenharmony_ci rcu_read_unlock(); 77462306a36Sopenharmony_ci} 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_cistatic const struct seq_operations sock_map_seq_ops = { 77762306a36Sopenharmony_ci .start = sock_map_seq_start, 77862306a36Sopenharmony_ci .next = sock_map_seq_next, 77962306a36Sopenharmony_ci .stop = sock_map_seq_stop, 78062306a36Sopenharmony_ci .show = sock_map_seq_show, 78162306a36Sopenharmony_ci}; 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_cistatic int sock_map_init_seq_private(void *priv_data, 78462306a36Sopenharmony_ci struct bpf_iter_aux_info *aux) 78562306a36Sopenharmony_ci{ 78662306a36Sopenharmony_ci struct sock_map_seq_info *info = priv_data; 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci bpf_map_inc_with_uref(aux->map); 78962306a36Sopenharmony_ci info->map = aux->map; 79062306a36Sopenharmony_ci return 0; 79162306a36Sopenharmony_ci} 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_cistatic void sock_map_fini_seq_private(void *priv_data) 79462306a36Sopenharmony_ci{ 79562306a36Sopenharmony_ci struct sock_map_seq_info *info = priv_data; 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci bpf_map_put_with_uref(info->map); 79862306a36Sopenharmony_ci} 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_cistatic u64 sock_map_mem_usage(const struct bpf_map *map) 80162306a36Sopenharmony_ci{ 80262306a36Sopenharmony_ci u64 usage = sizeof(struct bpf_stab); 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci usage += (u64)map->max_entries * sizeof(struct sock *); 80562306a36Sopenharmony_ci return usage; 80662306a36Sopenharmony_ci} 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_cistatic const struct bpf_iter_seq_info sock_map_iter_seq_info = { 80962306a36Sopenharmony_ci .seq_ops = &sock_map_seq_ops, 81062306a36Sopenharmony_ci .init_seq_private = sock_map_init_seq_private, 81162306a36Sopenharmony_ci .fini_seq_private = sock_map_fini_seq_private, 81262306a36Sopenharmony_ci .seq_priv_size = sizeof(struct sock_map_seq_info), 81362306a36Sopenharmony_ci}; 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ciBTF_ID_LIST_SINGLE(sock_map_btf_ids, struct, bpf_stab) 81662306a36Sopenharmony_ciconst struct bpf_map_ops sock_map_ops = { 81762306a36Sopenharmony_ci .map_meta_equal = bpf_map_meta_equal, 81862306a36Sopenharmony_ci .map_alloc = sock_map_alloc, 81962306a36Sopenharmony_ci .map_free = sock_map_free, 82062306a36Sopenharmony_ci .map_get_next_key = sock_map_get_next_key, 82162306a36Sopenharmony_ci .map_lookup_elem_sys_only = sock_map_lookup_sys, 82262306a36Sopenharmony_ci .map_update_elem = sock_map_update_elem, 82362306a36Sopenharmony_ci .map_delete_elem = sock_map_delete_elem, 82462306a36Sopenharmony_ci .map_lookup_elem = sock_map_lookup, 82562306a36Sopenharmony_ci .map_release_uref = sock_map_release_progs, 82662306a36Sopenharmony_ci .map_check_btf = map_check_no_btf, 82762306a36Sopenharmony_ci .map_mem_usage = sock_map_mem_usage, 82862306a36Sopenharmony_ci .map_btf_id = &sock_map_btf_ids[0], 82962306a36Sopenharmony_ci .iter_seq_info = &sock_map_iter_seq_info, 83062306a36Sopenharmony_ci}; 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_cistruct bpf_shtab_elem { 83362306a36Sopenharmony_ci struct rcu_head rcu; 83462306a36Sopenharmony_ci u32 hash; 83562306a36Sopenharmony_ci struct sock *sk; 83662306a36Sopenharmony_ci struct hlist_node node; 83762306a36Sopenharmony_ci u8 key[]; 83862306a36Sopenharmony_ci}; 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_cistruct bpf_shtab_bucket { 84162306a36Sopenharmony_ci struct hlist_head head; 84262306a36Sopenharmony_ci spinlock_t lock; 84362306a36Sopenharmony_ci}; 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_cistruct bpf_shtab { 84662306a36Sopenharmony_ci struct bpf_map map; 84762306a36Sopenharmony_ci struct bpf_shtab_bucket *buckets; 84862306a36Sopenharmony_ci u32 buckets_num; 84962306a36Sopenharmony_ci u32 elem_size; 85062306a36Sopenharmony_ci struct sk_psock_progs progs; 85162306a36Sopenharmony_ci atomic_t count; 85262306a36Sopenharmony_ci}; 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_cistatic inline u32 sock_hash_bucket_hash(const void *key, u32 len) 85562306a36Sopenharmony_ci{ 85662306a36Sopenharmony_ci return jhash(key, len, 0); 85762306a36Sopenharmony_ci} 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_cistatic struct bpf_shtab_bucket *sock_hash_select_bucket(struct bpf_shtab *htab, 86062306a36Sopenharmony_ci u32 hash) 86162306a36Sopenharmony_ci{ 86262306a36Sopenharmony_ci return &htab->buckets[hash & (htab->buckets_num - 1)]; 86362306a36Sopenharmony_ci} 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_cistatic struct bpf_shtab_elem * 86662306a36Sopenharmony_cisock_hash_lookup_elem_raw(struct hlist_head *head, u32 hash, void *key, 86762306a36Sopenharmony_ci u32 key_size) 86862306a36Sopenharmony_ci{ 86962306a36Sopenharmony_ci struct bpf_shtab_elem *elem; 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci hlist_for_each_entry_rcu(elem, head, node) { 87262306a36Sopenharmony_ci if (elem->hash == hash && 87362306a36Sopenharmony_ci !memcmp(&elem->key, key, key_size)) 87462306a36Sopenharmony_ci return elem; 87562306a36Sopenharmony_ci } 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci return NULL; 87862306a36Sopenharmony_ci} 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_cistatic struct sock *__sock_hash_lookup_elem(struct bpf_map *map, void *key) 88162306a36Sopenharmony_ci{ 88262306a36Sopenharmony_ci struct bpf_shtab *htab = container_of(map, struct bpf_shtab, map); 88362306a36Sopenharmony_ci u32 key_size = map->key_size, hash; 88462306a36Sopenharmony_ci struct bpf_shtab_bucket *bucket; 88562306a36Sopenharmony_ci struct bpf_shtab_elem *elem; 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci WARN_ON_ONCE(!rcu_read_lock_held()); 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci hash = sock_hash_bucket_hash(key, key_size); 89062306a36Sopenharmony_ci bucket = sock_hash_select_bucket(htab, hash); 89162306a36Sopenharmony_ci elem = sock_hash_lookup_elem_raw(&bucket->head, hash, key, key_size); 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci return elem ? elem->sk : NULL; 89462306a36Sopenharmony_ci} 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_cistatic void sock_hash_free_elem(struct bpf_shtab *htab, 89762306a36Sopenharmony_ci struct bpf_shtab_elem *elem) 89862306a36Sopenharmony_ci{ 89962306a36Sopenharmony_ci atomic_dec(&htab->count); 90062306a36Sopenharmony_ci kfree_rcu(elem, rcu); 90162306a36Sopenharmony_ci} 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_cistatic void sock_hash_delete_from_link(struct bpf_map *map, struct sock *sk, 90462306a36Sopenharmony_ci void *link_raw) 90562306a36Sopenharmony_ci{ 90662306a36Sopenharmony_ci struct bpf_shtab *htab = container_of(map, struct bpf_shtab, map); 90762306a36Sopenharmony_ci struct bpf_shtab_elem *elem_probe, *elem = link_raw; 90862306a36Sopenharmony_ci struct bpf_shtab_bucket *bucket; 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci WARN_ON_ONCE(!rcu_read_lock_held()); 91162306a36Sopenharmony_ci bucket = sock_hash_select_bucket(htab, elem->hash); 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci /* elem may be deleted in parallel from the map, but access here 91462306a36Sopenharmony_ci * is okay since it's going away only after RCU grace period. 91562306a36Sopenharmony_ci * However, we need to check whether it's still present. 91662306a36Sopenharmony_ci */ 91762306a36Sopenharmony_ci spin_lock_bh(&bucket->lock); 91862306a36Sopenharmony_ci elem_probe = sock_hash_lookup_elem_raw(&bucket->head, elem->hash, 91962306a36Sopenharmony_ci elem->key, map->key_size); 92062306a36Sopenharmony_ci if (elem_probe && elem_probe == elem) { 92162306a36Sopenharmony_ci hlist_del_rcu(&elem->node); 92262306a36Sopenharmony_ci sock_map_unref(elem->sk, elem); 92362306a36Sopenharmony_ci sock_hash_free_elem(htab, elem); 92462306a36Sopenharmony_ci } 92562306a36Sopenharmony_ci spin_unlock_bh(&bucket->lock); 92662306a36Sopenharmony_ci} 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_cistatic long sock_hash_delete_elem(struct bpf_map *map, void *key) 92962306a36Sopenharmony_ci{ 93062306a36Sopenharmony_ci struct bpf_shtab *htab = container_of(map, struct bpf_shtab, map); 93162306a36Sopenharmony_ci u32 hash, key_size = map->key_size; 93262306a36Sopenharmony_ci struct bpf_shtab_bucket *bucket; 93362306a36Sopenharmony_ci struct bpf_shtab_elem *elem; 93462306a36Sopenharmony_ci int ret = -ENOENT; 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci hash = sock_hash_bucket_hash(key, key_size); 93762306a36Sopenharmony_ci bucket = sock_hash_select_bucket(htab, hash); 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci spin_lock_bh(&bucket->lock); 94062306a36Sopenharmony_ci elem = sock_hash_lookup_elem_raw(&bucket->head, hash, key, key_size); 94162306a36Sopenharmony_ci if (elem) { 94262306a36Sopenharmony_ci hlist_del_rcu(&elem->node); 94362306a36Sopenharmony_ci sock_map_unref(elem->sk, elem); 94462306a36Sopenharmony_ci sock_hash_free_elem(htab, elem); 94562306a36Sopenharmony_ci ret = 0; 94662306a36Sopenharmony_ci } 94762306a36Sopenharmony_ci spin_unlock_bh(&bucket->lock); 94862306a36Sopenharmony_ci return ret; 94962306a36Sopenharmony_ci} 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_cistatic struct bpf_shtab_elem *sock_hash_alloc_elem(struct bpf_shtab *htab, 95262306a36Sopenharmony_ci void *key, u32 key_size, 95362306a36Sopenharmony_ci u32 hash, struct sock *sk, 95462306a36Sopenharmony_ci struct bpf_shtab_elem *old) 95562306a36Sopenharmony_ci{ 95662306a36Sopenharmony_ci struct bpf_shtab_elem *new; 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci if (atomic_inc_return(&htab->count) > htab->map.max_entries) { 95962306a36Sopenharmony_ci if (!old) { 96062306a36Sopenharmony_ci atomic_dec(&htab->count); 96162306a36Sopenharmony_ci return ERR_PTR(-E2BIG); 96262306a36Sopenharmony_ci } 96362306a36Sopenharmony_ci } 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci new = bpf_map_kmalloc_node(&htab->map, htab->elem_size, 96662306a36Sopenharmony_ci GFP_ATOMIC | __GFP_NOWARN, 96762306a36Sopenharmony_ci htab->map.numa_node); 96862306a36Sopenharmony_ci if (!new) { 96962306a36Sopenharmony_ci atomic_dec(&htab->count); 97062306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 97162306a36Sopenharmony_ci } 97262306a36Sopenharmony_ci memcpy(new->key, key, key_size); 97362306a36Sopenharmony_ci new->sk = sk; 97462306a36Sopenharmony_ci new->hash = hash; 97562306a36Sopenharmony_ci return new; 97662306a36Sopenharmony_ci} 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_cistatic int sock_hash_update_common(struct bpf_map *map, void *key, 97962306a36Sopenharmony_ci struct sock *sk, u64 flags) 98062306a36Sopenharmony_ci{ 98162306a36Sopenharmony_ci struct bpf_shtab *htab = container_of(map, struct bpf_shtab, map); 98262306a36Sopenharmony_ci u32 key_size = map->key_size, hash; 98362306a36Sopenharmony_ci struct bpf_shtab_elem *elem, *elem_new; 98462306a36Sopenharmony_ci struct bpf_shtab_bucket *bucket; 98562306a36Sopenharmony_ci struct sk_psock_link *link; 98662306a36Sopenharmony_ci struct sk_psock *psock; 98762306a36Sopenharmony_ci int ret; 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci WARN_ON_ONCE(!rcu_read_lock_held()); 99062306a36Sopenharmony_ci if (unlikely(flags > BPF_EXIST)) 99162306a36Sopenharmony_ci return -EINVAL; 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci link = sk_psock_init_link(); 99462306a36Sopenharmony_ci if (!link) 99562306a36Sopenharmony_ci return -ENOMEM; 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci ret = sock_map_link(map, sk); 99862306a36Sopenharmony_ci if (ret < 0) 99962306a36Sopenharmony_ci goto out_free; 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci psock = sk_psock(sk); 100262306a36Sopenharmony_ci WARN_ON_ONCE(!psock); 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci hash = sock_hash_bucket_hash(key, key_size); 100562306a36Sopenharmony_ci bucket = sock_hash_select_bucket(htab, hash); 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci spin_lock_bh(&bucket->lock); 100862306a36Sopenharmony_ci elem = sock_hash_lookup_elem_raw(&bucket->head, hash, key, key_size); 100962306a36Sopenharmony_ci if (elem && flags == BPF_NOEXIST) { 101062306a36Sopenharmony_ci ret = -EEXIST; 101162306a36Sopenharmony_ci goto out_unlock; 101262306a36Sopenharmony_ci } else if (!elem && flags == BPF_EXIST) { 101362306a36Sopenharmony_ci ret = -ENOENT; 101462306a36Sopenharmony_ci goto out_unlock; 101562306a36Sopenharmony_ci } 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci elem_new = sock_hash_alloc_elem(htab, key, key_size, hash, sk, elem); 101862306a36Sopenharmony_ci if (IS_ERR(elem_new)) { 101962306a36Sopenharmony_ci ret = PTR_ERR(elem_new); 102062306a36Sopenharmony_ci goto out_unlock; 102162306a36Sopenharmony_ci } 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci sock_map_add_link(psock, link, map, elem_new); 102462306a36Sopenharmony_ci /* Add new element to the head of the list, so that 102562306a36Sopenharmony_ci * concurrent search will find it before old elem. 102662306a36Sopenharmony_ci */ 102762306a36Sopenharmony_ci hlist_add_head_rcu(&elem_new->node, &bucket->head); 102862306a36Sopenharmony_ci if (elem) { 102962306a36Sopenharmony_ci hlist_del_rcu(&elem->node); 103062306a36Sopenharmony_ci sock_map_unref(elem->sk, elem); 103162306a36Sopenharmony_ci sock_hash_free_elem(htab, elem); 103262306a36Sopenharmony_ci } 103362306a36Sopenharmony_ci spin_unlock_bh(&bucket->lock); 103462306a36Sopenharmony_ci return 0; 103562306a36Sopenharmony_ciout_unlock: 103662306a36Sopenharmony_ci spin_unlock_bh(&bucket->lock); 103762306a36Sopenharmony_ci sk_psock_put(sk, psock); 103862306a36Sopenharmony_ciout_free: 103962306a36Sopenharmony_ci sk_psock_free_link(link); 104062306a36Sopenharmony_ci return ret; 104162306a36Sopenharmony_ci} 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_cistatic int sock_hash_get_next_key(struct bpf_map *map, void *key, 104462306a36Sopenharmony_ci void *key_next) 104562306a36Sopenharmony_ci{ 104662306a36Sopenharmony_ci struct bpf_shtab *htab = container_of(map, struct bpf_shtab, map); 104762306a36Sopenharmony_ci struct bpf_shtab_elem *elem, *elem_next; 104862306a36Sopenharmony_ci u32 hash, key_size = map->key_size; 104962306a36Sopenharmony_ci struct hlist_head *head; 105062306a36Sopenharmony_ci int i = 0; 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci if (!key) 105362306a36Sopenharmony_ci goto find_first_elem; 105462306a36Sopenharmony_ci hash = sock_hash_bucket_hash(key, key_size); 105562306a36Sopenharmony_ci head = &sock_hash_select_bucket(htab, hash)->head; 105662306a36Sopenharmony_ci elem = sock_hash_lookup_elem_raw(head, hash, key, key_size); 105762306a36Sopenharmony_ci if (!elem) 105862306a36Sopenharmony_ci goto find_first_elem; 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci elem_next = hlist_entry_safe(rcu_dereference(hlist_next_rcu(&elem->node)), 106162306a36Sopenharmony_ci struct bpf_shtab_elem, node); 106262306a36Sopenharmony_ci if (elem_next) { 106362306a36Sopenharmony_ci memcpy(key_next, elem_next->key, key_size); 106462306a36Sopenharmony_ci return 0; 106562306a36Sopenharmony_ci } 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci i = hash & (htab->buckets_num - 1); 106862306a36Sopenharmony_ci i++; 106962306a36Sopenharmony_cifind_first_elem: 107062306a36Sopenharmony_ci for (; i < htab->buckets_num; i++) { 107162306a36Sopenharmony_ci head = &sock_hash_select_bucket(htab, i)->head; 107262306a36Sopenharmony_ci elem_next = hlist_entry_safe(rcu_dereference(hlist_first_rcu(head)), 107362306a36Sopenharmony_ci struct bpf_shtab_elem, node); 107462306a36Sopenharmony_ci if (elem_next) { 107562306a36Sopenharmony_ci memcpy(key_next, elem_next->key, key_size); 107662306a36Sopenharmony_ci return 0; 107762306a36Sopenharmony_ci } 107862306a36Sopenharmony_ci } 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci return -ENOENT; 108162306a36Sopenharmony_ci} 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_cistatic struct bpf_map *sock_hash_alloc(union bpf_attr *attr) 108462306a36Sopenharmony_ci{ 108562306a36Sopenharmony_ci struct bpf_shtab *htab; 108662306a36Sopenharmony_ci int i, err; 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_ci if (attr->max_entries == 0 || 108962306a36Sopenharmony_ci attr->key_size == 0 || 109062306a36Sopenharmony_ci (attr->value_size != sizeof(u32) && 109162306a36Sopenharmony_ci attr->value_size != sizeof(u64)) || 109262306a36Sopenharmony_ci attr->map_flags & ~SOCK_CREATE_FLAG_MASK) 109362306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 109462306a36Sopenharmony_ci if (attr->key_size > MAX_BPF_STACK) 109562306a36Sopenharmony_ci return ERR_PTR(-E2BIG); 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci htab = bpf_map_area_alloc(sizeof(*htab), NUMA_NO_NODE); 109862306a36Sopenharmony_ci if (!htab) 109962306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci bpf_map_init_from_attr(&htab->map, attr); 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci htab->buckets_num = roundup_pow_of_two(htab->map.max_entries); 110462306a36Sopenharmony_ci htab->elem_size = sizeof(struct bpf_shtab_elem) + 110562306a36Sopenharmony_ci round_up(htab->map.key_size, 8); 110662306a36Sopenharmony_ci if (htab->buckets_num == 0 || 110762306a36Sopenharmony_ci htab->buckets_num > U32_MAX / sizeof(struct bpf_shtab_bucket)) { 110862306a36Sopenharmony_ci err = -EINVAL; 110962306a36Sopenharmony_ci goto free_htab; 111062306a36Sopenharmony_ci } 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci htab->buckets = bpf_map_area_alloc(htab->buckets_num * 111362306a36Sopenharmony_ci sizeof(struct bpf_shtab_bucket), 111462306a36Sopenharmony_ci htab->map.numa_node); 111562306a36Sopenharmony_ci if (!htab->buckets) { 111662306a36Sopenharmony_ci err = -ENOMEM; 111762306a36Sopenharmony_ci goto free_htab; 111862306a36Sopenharmony_ci } 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ci for (i = 0; i < htab->buckets_num; i++) { 112162306a36Sopenharmony_ci INIT_HLIST_HEAD(&htab->buckets[i].head); 112262306a36Sopenharmony_ci spin_lock_init(&htab->buckets[i].lock); 112362306a36Sopenharmony_ci } 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci return &htab->map; 112662306a36Sopenharmony_cifree_htab: 112762306a36Sopenharmony_ci bpf_map_area_free(htab); 112862306a36Sopenharmony_ci return ERR_PTR(err); 112962306a36Sopenharmony_ci} 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_cistatic void sock_hash_free(struct bpf_map *map) 113262306a36Sopenharmony_ci{ 113362306a36Sopenharmony_ci struct bpf_shtab *htab = container_of(map, struct bpf_shtab, map); 113462306a36Sopenharmony_ci struct bpf_shtab_bucket *bucket; 113562306a36Sopenharmony_ci struct hlist_head unlink_list; 113662306a36Sopenharmony_ci struct bpf_shtab_elem *elem; 113762306a36Sopenharmony_ci struct hlist_node *node; 113862306a36Sopenharmony_ci int i; 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci /* After the sync no updates or deletes will be in-flight so it 114162306a36Sopenharmony_ci * is safe to walk map and remove entries without risking a race 114262306a36Sopenharmony_ci * in EEXIST update case. 114362306a36Sopenharmony_ci */ 114462306a36Sopenharmony_ci synchronize_rcu(); 114562306a36Sopenharmony_ci for (i = 0; i < htab->buckets_num; i++) { 114662306a36Sopenharmony_ci bucket = sock_hash_select_bucket(htab, i); 114762306a36Sopenharmony_ci 114862306a36Sopenharmony_ci /* We are racing with sock_hash_delete_from_link to 114962306a36Sopenharmony_ci * enter the spin-lock critical section. Every socket on 115062306a36Sopenharmony_ci * the list is still linked to sockhash. Since link 115162306a36Sopenharmony_ci * exists, psock exists and holds a ref to socket. That 115262306a36Sopenharmony_ci * lets us to grab a socket ref too. 115362306a36Sopenharmony_ci */ 115462306a36Sopenharmony_ci spin_lock_bh(&bucket->lock); 115562306a36Sopenharmony_ci hlist_for_each_entry(elem, &bucket->head, node) 115662306a36Sopenharmony_ci sock_hold(elem->sk); 115762306a36Sopenharmony_ci hlist_move_list(&bucket->head, &unlink_list); 115862306a36Sopenharmony_ci spin_unlock_bh(&bucket->lock); 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_ci /* Process removed entries out of atomic context to 116162306a36Sopenharmony_ci * block for socket lock before deleting the psock's 116262306a36Sopenharmony_ci * link to sockhash. 116362306a36Sopenharmony_ci */ 116462306a36Sopenharmony_ci hlist_for_each_entry_safe(elem, node, &unlink_list, node) { 116562306a36Sopenharmony_ci hlist_del(&elem->node); 116662306a36Sopenharmony_ci lock_sock(elem->sk); 116762306a36Sopenharmony_ci rcu_read_lock(); 116862306a36Sopenharmony_ci sock_map_unref(elem->sk, elem); 116962306a36Sopenharmony_ci rcu_read_unlock(); 117062306a36Sopenharmony_ci release_sock(elem->sk); 117162306a36Sopenharmony_ci sock_put(elem->sk); 117262306a36Sopenharmony_ci sock_hash_free_elem(htab, elem); 117362306a36Sopenharmony_ci } 117462306a36Sopenharmony_ci } 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_ci /* wait for psock readers accessing its map link */ 117762306a36Sopenharmony_ci synchronize_rcu(); 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci bpf_map_area_free(htab->buckets); 118062306a36Sopenharmony_ci bpf_map_area_free(htab); 118162306a36Sopenharmony_ci} 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_cistatic void *sock_hash_lookup_sys(struct bpf_map *map, void *key) 118462306a36Sopenharmony_ci{ 118562306a36Sopenharmony_ci struct sock *sk; 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_ci if (map->value_size != sizeof(u64)) 118862306a36Sopenharmony_ci return ERR_PTR(-ENOSPC); 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci sk = __sock_hash_lookup_elem(map, key); 119162306a36Sopenharmony_ci if (!sk) 119262306a36Sopenharmony_ci return ERR_PTR(-ENOENT); 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci __sock_gen_cookie(sk); 119562306a36Sopenharmony_ci return &sk->sk_cookie; 119662306a36Sopenharmony_ci} 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_cistatic void *sock_hash_lookup(struct bpf_map *map, void *key) 119962306a36Sopenharmony_ci{ 120062306a36Sopenharmony_ci struct sock *sk; 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_ci sk = __sock_hash_lookup_elem(map, key); 120362306a36Sopenharmony_ci if (!sk) 120462306a36Sopenharmony_ci return NULL; 120562306a36Sopenharmony_ci if (sk_is_refcounted(sk) && !refcount_inc_not_zero(&sk->sk_refcnt)) 120662306a36Sopenharmony_ci return NULL; 120762306a36Sopenharmony_ci return sk; 120862306a36Sopenharmony_ci} 120962306a36Sopenharmony_ci 121062306a36Sopenharmony_cistatic void sock_hash_release_progs(struct bpf_map *map) 121162306a36Sopenharmony_ci{ 121262306a36Sopenharmony_ci psock_progs_drop(&container_of(map, struct bpf_shtab, map)->progs); 121362306a36Sopenharmony_ci} 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ciBPF_CALL_4(bpf_sock_hash_update, struct bpf_sock_ops_kern *, sops, 121662306a36Sopenharmony_ci struct bpf_map *, map, void *, key, u64, flags) 121762306a36Sopenharmony_ci{ 121862306a36Sopenharmony_ci WARN_ON_ONCE(!rcu_read_lock_held()); 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ci if (likely(sock_map_sk_is_suitable(sops->sk) && 122162306a36Sopenharmony_ci sock_map_op_okay(sops))) 122262306a36Sopenharmony_ci return sock_hash_update_common(map, key, sops->sk, flags); 122362306a36Sopenharmony_ci return -EOPNOTSUPP; 122462306a36Sopenharmony_ci} 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_ciconst struct bpf_func_proto bpf_sock_hash_update_proto = { 122762306a36Sopenharmony_ci .func = bpf_sock_hash_update, 122862306a36Sopenharmony_ci .gpl_only = false, 122962306a36Sopenharmony_ci .pkt_access = true, 123062306a36Sopenharmony_ci .ret_type = RET_INTEGER, 123162306a36Sopenharmony_ci .arg1_type = ARG_PTR_TO_CTX, 123262306a36Sopenharmony_ci .arg2_type = ARG_CONST_MAP_PTR, 123362306a36Sopenharmony_ci .arg3_type = ARG_PTR_TO_MAP_KEY, 123462306a36Sopenharmony_ci .arg4_type = ARG_ANYTHING, 123562306a36Sopenharmony_ci}; 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_ciBPF_CALL_4(bpf_sk_redirect_hash, struct sk_buff *, skb, 123862306a36Sopenharmony_ci struct bpf_map *, map, void *, key, u64, flags) 123962306a36Sopenharmony_ci{ 124062306a36Sopenharmony_ci struct sock *sk; 124162306a36Sopenharmony_ci 124262306a36Sopenharmony_ci if (unlikely(flags & ~(BPF_F_INGRESS))) 124362306a36Sopenharmony_ci return SK_DROP; 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_ci sk = __sock_hash_lookup_elem(map, key); 124662306a36Sopenharmony_ci if (unlikely(!sk || !sock_map_redirect_allowed(sk))) 124762306a36Sopenharmony_ci return SK_DROP; 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_ci skb_bpf_set_redir(skb, sk, flags & BPF_F_INGRESS); 125062306a36Sopenharmony_ci return SK_PASS; 125162306a36Sopenharmony_ci} 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_ciconst struct bpf_func_proto bpf_sk_redirect_hash_proto = { 125462306a36Sopenharmony_ci .func = bpf_sk_redirect_hash, 125562306a36Sopenharmony_ci .gpl_only = false, 125662306a36Sopenharmony_ci .ret_type = RET_INTEGER, 125762306a36Sopenharmony_ci .arg1_type = ARG_PTR_TO_CTX, 125862306a36Sopenharmony_ci .arg2_type = ARG_CONST_MAP_PTR, 125962306a36Sopenharmony_ci .arg3_type = ARG_PTR_TO_MAP_KEY, 126062306a36Sopenharmony_ci .arg4_type = ARG_ANYTHING, 126162306a36Sopenharmony_ci}; 126262306a36Sopenharmony_ci 126362306a36Sopenharmony_ciBPF_CALL_4(bpf_msg_redirect_hash, struct sk_msg *, msg, 126462306a36Sopenharmony_ci struct bpf_map *, map, void *, key, u64, flags) 126562306a36Sopenharmony_ci{ 126662306a36Sopenharmony_ci struct sock *sk; 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_ci if (unlikely(flags & ~(BPF_F_INGRESS))) 126962306a36Sopenharmony_ci return SK_DROP; 127062306a36Sopenharmony_ci 127162306a36Sopenharmony_ci sk = __sock_hash_lookup_elem(map, key); 127262306a36Sopenharmony_ci if (unlikely(!sk || !sock_map_redirect_allowed(sk))) 127362306a36Sopenharmony_ci return SK_DROP; 127462306a36Sopenharmony_ci if (!(flags & BPF_F_INGRESS) && !sk_is_tcp(sk)) 127562306a36Sopenharmony_ci return SK_DROP; 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_ci msg->flags = flags; 127862306a36Sopenharmony_ci msg->sk_redir = sk; 127962306a36Sopenharmony_ci return SK_PASS; 128062306a36Sopenharmony_ci} 128162306a36Sopenharmony_ci 128262306a36Sopenharmony_ciconst struct bpf_func_proto bpf_msg_redirect_hash_proto = { 128362306a36Sopenharmony_ci .func = bpf_msg_redirect_hash, 128462306a36Sopenharmony_ci .gpl_only = false, 128562306a36Sopenharmony_ci .ret_type = RET_INTEGER, 128662306a36Sopenharmony_ci .arg1_type = ARG_PTR_TO_CTX, 128762306a36Sopenharmony_ci .arg2_type = ARG_CONST_MAP_PTR, 128862306a36Sopenharmony_ci .arg3_type = ARG_PTR_TO_MAP_KEY, 128962306a36Sopenharmony_ci .arg4_type = ARG_ANYTHING, 129062306a36Sopenharmony_ci}; 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_cistruct sock_hash_seq_info { 129362306a36Sopenharmony_ci struct bpf_map *map; 129462306a36Sopenharmony_ci struct bpf_shtab *htab; 129562306a36Sopenharmony_ci u32 bucket_id; 129662306a36Sopenharmony_ci}; 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_cistatic void *sock_hash_seq_find_next(struct sock_hash_seq_info *info, 129962306a36Sopenharmony_ci struct bpf_shtab_elem *prev_elem) 130062306a36Sopenharmony_ci{ 130162306a36Sopenharmony_ci const struct bpf_shtab *htab = info->htab; 130262306a36Sopenharmony_ci struct bpf_shtab_bucket *bucket; 130362306a36Sopenharmony_ci struct bpf_shtab_elem *elem; 130462306a36Sopenharmony_ci struct hlist_node *node; 130562306a36Sopenharmony_ci 130662306a36Sopenharmony_ci /* try to find next elem in the same bucket */ 130762306a36Sopenharmony_ci if (prev_elem) { 130862306a36Sopenharmony_ci node = rcu_dereference(hlist_next_rcu(&prev_elem->node)); 130962306a36Sopenharmony_ci elem = hlist_entry_safe(node, struct bpf_shtab_elem, node); 131062306a36Sopenharmony_ci if (elem) 131162306a36Sopenharmony_ci return elem; 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_ci /* no more elements, continue in the next bucket */ 131462306a36Sopenharmony_ci info->bucket_id++; 131562306a36Sopenharmony_ci } 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_ci for (; info->bucket_id < htab->buckets_num; info->bucket_id++) { 131862306a36Sopenharmony_ci bucket = &htab->buckets[info->bucket_id]; 131962306a36Sopenharmony_ci node = rcu_dereference(hlist_first_rcu(&bucket->head)); 132062306a36Sopenharmony_ci elem = hlist_entry_safe(node, struct bpf_shtab_elem, node); 132162306a36Sopenharmony_ci if (elem) 132262306a36Sopenharmony_ci return elem; 132362306a36Sopenharmony_ci } 132462306a36Sopenharmony_ci 132562306a36Sopenharmony_ci return NULL; 132662306a36Sopenharmony_ci} 132762306a36Sopenharmony_ci 132862306a36Sopenharmony_cistatic void *sock_hash_seq_start(struct seq_file *seq, loff_t *pos) 132962306a36Sopenharmony_ci __acquires(rcu) 133062306a36Sopenharmony_ci{ 133162306a36Sopenharmony_ci struct sock_hash_seq_info *info = seq->private; 133262306a36Sopenharmony_ci 133362306a36Sopenharmony_ci if (*pos == 0) 133462306a36Sopenharmony_ci ++*pos; 133562306a36Sopenharmony_ci 133662306a36Sopenharmony_ci /* pairs with sock_hash_seq_stop */ 133762306a36Sopenharmony_ci rcu_read_lock(); 133862306a36Sopenharmony_ci return sock_hash_seq_find_next(info, NULL); 133962306a36Sopenharmony_ci} 134062306a36Sopenharmony_ci 134162306a36Sopenharmony_cistatic void *sock_hash_seq_next(struct seq_file *seq, void *v, loff_t *pos) 134262306a36Sopenharmony_ci __must_hold(rcu) 134362306a36Sopenharmony_ci{ 134462306a36Sopenharmony_ci struct sock_hash_seq_info *info = seq->private; 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_ci ++*pos; 134762306a36Sopenharmony_ci return sock_hash_seq_find_next(info, v); 134862306a36Sopenharmony_ci} 134962306a36Sopenharmony_ci 135062306a36Sopenharmony_cistatic int sock_hash_seq_show(struct seq_file *seq, void *v) 135162306a36Sopenharmony_ci __must_hold(rcu) 135262306a36Sopenharmony_ci{ 135362306a36Sopenharmony_ci struct sock_hash_seq_info *info = seq->private; 135462306a36Sopenharmony_ci struct bpf_iter__sockmap ctx = {}; 135562306a36Sopenharmony_ci struct bpf_shtab_elem *elem = v; 135662306a36Sopenharmony_ci struct bpf_iter_meta meta; 135762306a36Sopenharmony_ci struct bpf_prog *prog; 135862306a36Sopenharmony_ci 135962306a36Sopenharmony_ci meta.seq = seq; 136062306a36Sopenharmony_ci prog = bpf_iter_get_info(&meta, !elem); 136162306a36Sopenharmony_ci if (!prog) 136262306a36Sopenharmony_ci return 0; 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_ci ctx.meta = &meta; 136562306a36Sopenharmony_ci ctx.map = info->map; 136662306a36Sopenharmony_ci if (elem) { 136762306a36Sopenharmony_ci ctx.key = elem->key; 136862306a36Sopenharmony_ci ctx.sk = elem->sk; 136962306a36Sopenharmony_ci } 137062306a36Sopenharmony_ci 137162306a36Sopenharmony_ci return bpf_iter_run_prog(prog, &ctx); 137262306a36Sopenharmony_ci} 137362306a36Sopenharmony_ci 137462306a36Sopenharmony_cistatic void sock_hash_seq_stop(struct seq_file *seq, void *v) 137562306a36Sopenharmony_ci __releases(rcu) 137662306a36Sopenharmony_ci{ 137762306a36Sopenharmony_ci if (!v) 137862306a36Sopenharmony_ci (void)sock_hash_seq_show(seq, NULL); 137962306a36Sopenharmony_ci 138062306a36Sopenharmony_ci /* pairs with sock_hash_seq_start */ 138162306a36Sopenharmony_ci rcu_read_unlock(); 138262306a36Sopenharmony_ci} 138362306a36Sopenharmony_ci 138462306a36Sopenharmony_cistatic const struct seq_operations sock_hash_seq_ops = { 138562306a36Sopenharmony_ci .start = sock_hash_seq_start, 138662306a36Sopenharmony_ci .next = sock_hash_seq_next, 138762306a36Sopenharmony_ci .stop = sock_hash_seq_stop, 138862306a36Sopenharmony_ci .show = sock_hash_seq_show, 138962306a36Sopenharmony_ci}; 139062306a36Sopenharmony_ci 139162306a36Sopenharmony_cistatic int sock_hash_init_seq_private(void *priv_data, 139262306a36Sopenharmony_ci struct bpf_iter_aux_info *aux) 139362306a36Sopenharmony_ci{ 139462306a36Sopenharmony_ci struct sock_hash_seq_info *info = priv_data; 139562306a36Sopenharmony_ci 139662306a36Sopenharmony_ci bpf_map_inc_with_uref(aux->map); 139762306a36Sopenharmony_ci info->map = aux->map; 139862306a36Sopenharmony_ci info->htab = container_of(aux->map, struct bpf_shtab, map); 139962306a36Sopenharmony_ci return 0; 140062306a36Sopenharmony_ci} 140162306a36Sopenharmony_ci 140262306a36Sopenharmony_cistatic void sock_hash_fini_seq_private(void *priv_data) 140362306a36Sopenharmony_ci{ 140462306a36Sopenharmony_ci struct sock_hash_seq_info *info = priv_data; 140562306a36Sopenharmony_ci 140662306a36Sopenharmony_ci bpf_map_put_with_uref(info->map); 140762306a36Sopenharmony_ci} 140862306a36Sopenharmony_ci 140962306a36Sopenharmony_cistatic u64 sock_hash_mem_usage(const struct bpf_map *map) 141062306a36Sopenharmony_ci{ 141162306a36Sopenharmony_ci struct bpf_shtab *htab = container_of(map, struct bpf_shtab, map); 141262306a36Sopenharmony_ci u64 usage = sizeof(*htab); 141362306a36Sopenharmony_ci 141462306a36Sopenharmony_ci usage += htab->buckets_num * sizeof(struct bpf_shtab_bucket); 141562306a36Sopenharmony_ci usage += atomic_read(&htab->count) * (u64)htab->elem_size; 141662306a36Sopenharmony_ci return usage; 141762306a36Sopenharmony_ci} 141862306a36Sopenharmony_ci 141962306a36Sopenharmony_cistatic const struct bpf_iter_seq_info sock_hash_iter_seq_info = { 142062306a36Sopenharmony_ci .seq_ops = &sock_hash_seq_ops, 142162306a36Sopenharmony_ci .init_seq_private = sock_hash_init_seq_private, 142262306a36Sopenharmony_ci .fini_seq_private = sock_hash_fini_seq_private, 142362306a36Sopenharmony_ci .seq_priv_size = sizeof(struct sock_hash_seq_info), 142462306a36Sopenharmony_ci}; 142562306a36Sopenharmony_ci 142662306a36Sopenharmony_ciBTF_ID_LIST_SINGLE(sock_hash_map_btf_ids, struct, bpf_shtab) 142762306a36Sopenharmony_ciconst struct bpf_map_ops sock_hash_ops = { 142862306a36Sopenharmony_ci .map_meta_equal = bpf_map_meta_equal, 142962306a36Sopenharmony_ci .map_alloc = sock_hash_alloc, 143062306a36Sopenharmony_ci .map_free = sock_hash_free, 143162306a36Sopenharmony_ci .map_get_next_key = sock_hash_get_next_key, 143262306a36Sopenharmony_ci .map_update_elem = sock_map_update_elem, 143362306a36Sopenharmony_ci .map_delete_elem = sock_hash_delete_elem, 143462306a36Sopenharmony_ci .map_lookup_elem = sock_hash_lookup, 143562306a36Sopenharmony_ci .map_lookup_elem_sys_only = sock_hash_lookup_sys, 143662306a36Sopenharmony_ci .map_release_uref = sock_hash_release_progs, 143762306a36Sopenharmony_ci .map_check_btf = map_check_no_btf, 143862306a36Sopenharmony_ci .map_mem_usage = sock_hash_mem_usage, 143962306a36Sopenharmony_ci .map_btf_id = &sock_hash_map_btf_ids[0], 144062306a36Sopenharmony_ci .iter_seq_info = &sock_hash_iter_seq_info, 144162306a36Sopenharmony_ci}; 144262306a36Sopenharmony_ci 144362306a36Sopenharmony_cistatic struct sk_psock_progs *sock_map_progs(struct bpf_map *map) 144462306a36Sopenharmony_ci{ 144562306a36Sopenharmony_ci switch (map->map_type) { 144662306a36Sopenharmony_ci case BPF_MAP_TYPE_SOCKMAP: 144762306a36Sopenharmony_ci return &container_of(map, struct bpf_stab, map)->progs; 144862306a36Sopenharmony_ci case BPF_MAP_TYPE_SOCKHASH: 144962306a36Sopenharmony_ci return &container_of(map, struct bpf_shtab, map)->progs; 145062306a36Sopenharmony_ci default: 145162306a36Sopenharmony_ci break; 145262306a36Sopenharmony_ci } 145362306a36Sopenharmony_ci 145462306a36Sopenharmony_ci return NULL; 145562306a36Sopenharmony_ci} 145662306a36Sopenharmony_ci 145762306a36Sopenharmony_cistatic int sock_map_prog_lookup(struct bpf_map *map, struct bpf_prog ***pprog, 145862306a36Sopenharmony_ci u32 which) 145962306a36Sopenharmony_ci{ 146062306a36Sopenharmony_ci struct sk_psock_progs *progs = sock_map_progs(map); 146162306a36Sopenharmony_ci 146262306a36Sopenharmony_ci if (!progs) 146362306a36Sopenharmony_ci return -EOPNOTSUPP; 146462306a36Sopenharmony_ci 146562306a36Sopenharmony_ci switch (which) { 146662306a36Sopenharmony_ci case BPF_SK_MSG_VERDICT: 146762306a36Sopenharmony_ci *pprog = &progs->msg_parser; 146862306a36Sopenharmony_ci break; 146962306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_BPF_STREAM_PARSER) 147062306a36Sopenharmony_ci case BPF_SK_SKB_STREAM_PARSER: 147162306a36Sopenharmony_ci *pprog = &progs->stream_parser; 147262306a36Sopenharmony_ci break; 147362306a36Sopenharmony_ci#endif 147462306a36Sopenharmony_ci case BPF_SK_SKB_STREAM_VERDICT: 147562306a36Sopenharmony_ci if (progs->skb_verdict) 147662306a36Sopenharmony_ci return -EBUSY; 147762306a36Sopenharmony_ci *pprog = &progs->stream_verdict; 147862306a36Sopenharmony_ci break; 147962306a36Sopenharmony_ci case BPF_SK_SKB_VERDICT: 148062306a36Sopenharmony_ci if (progs->stream_verdict) 148162306a36Sopenharmony_ci return -EBUSY; 148262306a36Sopenharmony_ci *pprog = &progs->skb_verdict; 148362306a36Sopenharmony_ci break; 148462306a36Sopenharmony_ci default: 148562306a36Sopenharmony_ci return -EOPNOTSUPP; 148662306a36Sopenharmony_ci } 148762306a36Sopenharmony_ci 148862306a36Sopenharmony_ci return 0; 148962306a36Sopenharmony_ci} 149062306a36Sopenharmony_ci 149162306a36Sopenharmony_cistatic int sock_map_prog_update(struct bpf_map *map, struct bpf_prog *prog, 149262306a36Sopenharmony_ci struct bpf_prog *old, u32 which) 149362306a36Sopenharmony_ci{ 149462306a36Sopenharmony_ci struct bpf_prog **pprog; 149562306a36Sopenharmony_ci int ret; 149662306a36Sopenharmony_ci 149762306a36Sopenharmony_ci ret = sock_map_prog_lookup(map, &pprog, which); 149862306a36Sopenharmony_ci if (ret) 149962306a36Sopenharmony_ci return ret; 150062306a36Sopenharmony_ci 150162306a36Sopenharmony_ci if (old) 150262306a36Sopenharmony_ci return psock_replace_prog(pprog, prog, old); 150362306a36Sopenharmony_ci 150462306a36Sopenharmony_ci psock_set_prog(pprog, prog); 150562306a36Sopenharmony_ci return 0; 150662306a36Sopenharmony_ci} 150762306a36Sopenharmony_ci 150862306a36Sopenharmony_ciint sock_map_bpf_prog_query(const union bpf_attr *attr, 150962306a36Sopenharmony_ci union bpf_attr __user *uattr) 151062306a36Sopenharmony_ci{ 151162306a36Sopenharmony_ci __u32 __user *prog_ids = u64_to_user_ptr(attr->query.prog_ids); 151262306a36Sopenharmony_ci u32 prog_cnt = 0, flags = 0, ufd = attr->target_fd; 151362306a36Sopenharmony_ci struct bpf_prog **pprog; 151462306a36Sopenharmony_ci struct bpf_prog *prog; 151562306a36Sopenharmony_ci struct bpf_map *map; 151662306a36Sopenharmony_ci struct fd f; 151762306a36Sopenharmony_ci u32 id = 0; 151862306a36Sopenharmony_ci int ret; 151962306a36Sopenharmony_ci 152062306a36Sopenharmony_ci if (attr->query.query_flags) 152162306a36Sopenharmony_ci return -EINVAL; 152262306a36Sopenharmony_ci 152362306a36Sopenharmony_ci f = fdget(ufd); 152462306a36Sopenharmony_ci map = __bpf_map_get(f); 152562306a36Sopenharmony_ci if (IS_ERR(map)) 152662306a36Sopenharmony_ci return PTR_ERR(map); 152762306a36Sopenharmony_ci 152862306a36Sopenharmony_ci rcu_read_lock(); 152962306a36Sopenharmony_ci 153062306a36Sopenharmony_ci ret = sock_map_prog_lookup(map, &pprog, attr->query.attach_type); 153162306a36Sopenharmony_ci if (ret) 153262306a36Sopenharmony_ci goto end; 153362306a36Sopenharmony_ci 153462306a36Sopenharmony_ci prog = *pprog; 153562306a36Sopenharmony_ci prog_cnt = !prog ? 0 : 1; 153662306a36Sopenharmony_ci 153762306a36Sopenharmony_ci if (!attr->query.prog_cnt || !prog_ids || !prog_cnt) 153862306a36Sopenharmony_ci goto end; 153962306a36Sopenharmony_ci 154062306a36Sopenharmony_ci /* we do not hold the refcnt, the bpf prog may be released 154162306a36Sopenharmony_ci * asynchronously and the id would be set to 0. 154262306a36Sopenharmony_ci */ 154362306a36Sopenharmony_ci id = data_race(prog->aux->id); 154462306a36Sopenharmony_ci if (id == 0) 154562306a36Sopenharmony_ci prog_cnt = 0; 154662306a36Sopenharmony_ci 154762306a36Sopenharmony_ciend: 154862306a36Sopenharmony_ci rcu_read_unlock(); 154962306a36Sopenharmony_ci 155062306a36Sopenharmony_ci if (copy_to_user(&uattr->query.attach_flags, &flags, sizeof(flags)) || 155162306a36Sopenharmony_ci (id != 0 && copy_to_user(prog_ids, &id, sizeof(u32))) || 155262306a36Sopenharmony_ci copy_to_user(&uattr->query.prog_cnt, &prog_cnt, sizeof(prog_cnt))) 155362306a36Sopenharmony_ci ret = -EFAULT; 155462306a36Sopenharmony_ci 155562306a36Sopenharmony_ci fdput(f); 155662306a36Sopenharmony_ci return ret; 155762306a36Sopenharmony_ci} 155862306a36Sopenharmony_ci 155962306a36Sopenharmony_cistatic void sock_map_unlink(struct sock *sk, struct sk_psock_link *link) 156062306a36Sopenharmony_ci{ 156162306a36Sopenharmony_ci switch (link->map->map_type) { 156262306a36Sopenharmony_ci case BPF_MAP_TYPE_SOCKMAP: 156362306a36Sopenharmony_ci return sock_map_delete_from_link(link->map, sk, 156462306a36Sopenharmony_ci link->link_raw); 156562306a36Sopenharmony_ci case BPF_MAP_TYPE_SOCKHASH: 156662306a36Sopenharmony_ci return sock_hash_delete_from_link(link->map, sk, 156762306a36Sopenharmony_ci link->link_raw); 156862306a36Sopenharmony_ci default: 156962306a36Sopenharmony_ci break; 157062306a36Sopenharmony_ci } 157162306a36Sopenharmony_ci} 157262306a36Sopenharmony_ci 157362306a36Sopenharmony_cistatic void sock_map_remove_links(struct sock *sk, struct sk_psock *psock) 157462306a36Sopenharmony_ci{ 157562306a36Sopenharmony_ci struct sk_psock_link *link; 157662306a36Sopenharmony_ci 157762306a36Sopenharmony_ci while ((link = sk_psock_link_pop(psock))) { 157862306a36Sopenharmony_ci sock_map_unlink(sk, link); 157962306a36Sopenharmony_ci sk_psock_free_link(link); 158062306a36Sopenharmony_ci } 158162306a36Sopenharmony_ci} 158262306a36Sopenharmony_ci 158362306a36Sopenharmony_civoid sock_map_unhash(struct sock *sk) 158462306a36Sopenharmony_ci{ 158562306a36Sopenharmony_ci void (*saved_unhash)(struct sock *sk); 158662306a36Sopenharmony_ci struct sk_psock *psock; 158762306a36Sopenharmony_ci 158862306a36Sopenharmony_ci rcu_read_lock(); 158962306a36Sopenharmony_ci psock = sk_psock(sk); 159062306a36Sopenharmony_ci if (unlikely(!psock)) { 159162306a36Sopenharmony_ci rcu_read_unlock(); 159262306a36Sopenharmony_ci saved_unhash = READ_ONCE(sk->sk_prot)->unhash; 159362306a36Sopenharmony_ci } else { 159462306a36Sopenharmony_ci saved_unhash = psock->saved_unhash; 159562306a36Sopenharmony_ci sock_map_remove_links(sk, psock); 159662306a36Sopenharmony_ci rcu_read_unlock(); 159762306a36Sopenharmony_ci } 159862306a36Sopenharmony_ci if (WARN_ON_ONCE(saved_unhash == sock_map_unhash)) 159962306a36Sopenharmony_ci return; 160062306a36Sopenharmony_ci if (saved_unhash) 160162306a36Sopenharmony_ci saved_unhash(sk); 160262306a36Sopenharmony_ci} 160362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sock_map_unhash); 160462306a36Sopenharmony_ci 160562306a36Sopenharmony_civoid sock_map_destroy(struct sock *sk) 160662306a36Sopenharmony_ci{ 160762306a36Sopenharmony_ci void (*saved_destroy)(struct sock *sk); 160862306a36Sopenharmony_ci struct sk_psock *psock; 160962306a36Sopenharmony_ci 161062306a36Sopenharmony_ci rcu_read_lock(); 161162306a36Sopenharmony_ci psock = sk_psock_get(sk); 161262306a36Sopenharmony_ci if (unlikely(!psock)) { 161362306a36Sopenharmony_ci rcu_read_unlock(); 161462306a36Sopenharmony_ci saved_destroy = READ_ONCE(sk->sk_prot)->destroy; 161562306a36Sopenharmony_ci } else { 161662306a36Sopenharmony_ci saved_destroy = psock->saved_destroy; 161762306a36Sopenharmony_ci sock_map_remove_links(sk, psock); 161862306a36Sopenharmony_ci rcu_read_unlock(); 161962306a36Sopenharmony_ci sk_psock_stop(psock); 162062306a36Sopenharmony_ci sk_psock_put(sk, psock); 162162306a36Sopenharmony_ci } 162262306a36Sopenharmony_ci if (WARN_ON_ONCE(saved_destroy == sock_map_destroy)) 162362306a36Sopenharmony_ci return; 162462306a36Sopenharmony_ci if (saved_destroy) 162562306a36Sopenharmony_ci saved_destroy(sk); 162662306a36Sopenharmony_ci} 162762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sock_map_destroy); 162862306a36Sopenharmony_ci 162962306a36Sopenharmony_civoid sock_map_close(struct sock *sk, long timeout) 163062306a36Sopenharmony_ci{ 163162306a36Sopenharmony_ci void (*saved_close)(struct sock *sk, long timeout); 163262306a36Sopenharmony_ci struct sk_psock *psock; 163362306a36Sopenharmony_ci 163462306a36Sopenharmony_ci lock_sock(sk); 163562306a36Sopenharmony_ci rcu_read_lock(); 163662306a36Sopenharmony_ci psock = sk_psock_get(sk); 163762306a36Sopenharmony_ci if (unlikely(!psock)) { 163862306a36Sopenharmony_ci rcu_read_unlock(); 163962306a36Sopenharmony_ci release_sock(sk); 164062306a36Sopenharmony_ci saved_close = READ_ONCE(sk->sk_prot)->close; 164162306a36Sopenharmony_ci } else { 164262306a36Sopenharmony_ci saved_close = psock->saved_close; 164362306a36Sopenharmony_ci sock_map_remove_links(sk, psock); 164462306a36Sopenharmony_ci rcu_read_unlock(); 164562306a36Sopenharmony_ci sk_psock_stop(psock); 164662306a36Sopenharmony_ci release_sock(sk); 164762306a36Sopenharmony_ci cancel_delayed_work_sync(&psock->work); 164862306a36Sopenharmony_ci sk_psock_put(sk, psock); 164962306a36Sopenharmony_ci } 165062306a36Sopenharmony_ci 165162306a36Sopenharmony_ci /* Make sure we do not recurse. This is a bug. 165262306a36Sopenharmony_ci * Leak the socket instead of crashing on a stack overflow. 165362306a36Sopenharmony_ci */ 165462306a36Sopenharmony_ci if (WARN_ON_ONCE(saved_close == sock_map_close)) 165562306a36Sopenharmony_ci return; 165662306a36Sopenharmony_ci saved_close(sk, timeout); 165762306a36Sopenharmony_ci} 165862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sock_map_close); 165962306a36Sopenharmony_ci 166062306a36Sopenharmony_cistatic int sock_map_iter_attach_target(struct bpf_prog *prog, 166162306a36Sopenharmony_ci union bpf_iter_link_info *linfo, 166262306a36Sopenharmony_ci struct bpf_iter_aux_info *aux) 166362306a36Sopenharmony_ci{ 166462306a36Sopenharmony_ci struct bpf_map *map; 166562306a36Sopenharmony_ci int err = -EINVAL; 166662306a36Sopenharmony_ci 166762306a36Sopenharmony_ci if (!linfo->map.map_fd) 166862306a36Sopenharmony_ci return -EBADF; 166962306a36Sopenharmony_ci 167062306a36Sopenharmony_ci map = bpf_map_get_with_uref(linfo->map.map_fd); 167162306a36Sopenharmony_ci if (IS_ERR(map)) 167262306a36Sopenharmony_ci return PTR_ERR(map); 167362306a36Sopenharmony_ci 167462306a36Sopenharmony_ci if (map->map_type != BPF_MAP_TYPE_SOCKMAP && 167562306a36Sopenharmony_ci map->map_type != BPF_MAP_TYPE_SOCKHASH) 167662306a36Sopenharmony_ci goto put_map; 167762306a36Sopenharmony_ci 167862306a36Sopenharmony_ci if (prog->aux->max_rdonly_access > map->key_size) { 167962306a36Sopenharmony_ci err = -EACCES; 168062306a36Sopenharmony_ci goto put_map; 168162306a36Sopenharmony_ci } 168262306a36Sopenharmony_ci 168362306a36Sopenharmony_ci aux->map = map; 168462306a36Sopenharmony_ci return 0; 168562306a36Sopenharmony_ci 168662306a36Sopenharmony_ciput_map: 168762306a36Sopenharmony_ci bpf_map_put_with_uref(map); 168862306a36Sopenharmony_ci return err; 168962306a36Sopenharmony_ci} 169062306a36Sopenharmony_ci 169162306a36Sopenharmony_cistatic void sock_map_iter_detach_target(struct bpf_iter_aux_info *aux) 169262306a36Sopenharmony_ci{ 169362306a36Sopenharmony_ci bpf_map_put_with_uref(aux->map); 169462306a36Sopenharmony_ci} 169562306a36Sopenharmony_ci 169662306a36Sopenharmony_cistatic struct bpf_iter_reg sock_map_iter_reg = { 169762306a36Sopenharmony_ci .target = "sockmap", 169862306a36Sopenharmony_ci .attach_target = sock_map_iter_attach_target, 169962306a36Sopenharmony_ci .detach_target = sock_map_iter_detach_target, 170062306a36Sopenharmony_ci .show_fdinfo = bpf_iter_map_show_fdinfo, 170162306a36Sopenharmony_ci .fill_link_info = bpf_iter_map_fill_link_info, 170262306a36Sopenharmony_ci .ctx_arg_info_size = 2, 170362306a36Sopenharmony_ci .ctx_arg_info = { 170462306a36Sopenharmony_ci { offsetof(struct bpf_iter__sockmap, key), 170562306a36Sopenharmony_ci PTR_TO_BUF | PTR_MAYBE_NULL | MEM_RDONLY }, 170662306a36Sopenharmony_ci { offsetof(struct bpf_iter__sockmap, sk), 170762306a36Sopenharmony_ci PTR_TO_BTF_ID_OR_NULL }, 170862306a36Sopenharmony_ci }, 170962306a36Sopenharmony_ci}; 171062306a36Sopenharmony_ci 171162306a36Sopenharmony_cistatic int __init bpf_sockmap_iter_init(void) 171262306a36Sopenharmony_ci{ 171362306a36Sopenharmony_ci sock_map_iter_reg.ctx_arg_info[1].btf_id = 171462306a36Sopenharmony_ci btf_sock_ids[BTF_SOCK_TYPE_SOCK]; 171562306a36Sopenharmony_ci return bpf_iter_reg_target(&sock_map_iter_reg); 171662306a36Sopenharmony_ci} 171762306a36Sopenharmony_cilate_initcall(bpf_sockmap_iter_init); 1718