diff mbox series

[RFC,bpf-next,1/5] bpf: Add link support for sk_msg prog

Message ID 20240305202201.3891042-1-yonghong.song@linux.dev (mailing list archive)
State RFC
Delegated to: BPF
Headers show
Series Add bpf_link support for sk_msg prog | expand

Checks

Context Check Description
bpf/vmtest-bpf-next-PR success PR summary
bpf/vmtest-bpf-next-VM_Test-1 success Logs for ShellCheck
bpf/vmtest-bpf-next-VM_Test-2 success Logs for Unittests
bpf/vmtest-bpf-next-VM_Test-0 success Logs for Lint
bpf/vmtest-bpf-next-VM_Test-3 success Logs for Validate matrix.py
bpf/vmtest-bpf-next-VM_Test-5 success Logs for aarch64-gcc / build-release
bpf/vmtest-bpf-next-VM_Test-4 success Logs for aarch64-gcc / build / build for aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-10 success Logs for aarch64-gcc / veristat
bpf/vmtest-bpf-next-VM_Test-11 success Logs for s390x-gcc / build / build for s390x with gcc
bpf/vmtest-bpf-next-VM_Test-12 success Logs for s390x-gcc / build-release
bpf/vmtest-bpf-next-VM_Test-20 success Logs for x86_64-gcc / build-release
bpf/vmtest-bpf-next-VM_Test-19 success Logs for x86_64-gcc / build / build for x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-28 success Logs for x86_64-llvm-17 / build / build for x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-34 success Logs for x86_64-llvm-17 / veristat
bpf/vmtest-bpf-next-VM_Test-35 success Logs for x86_64-llvm-18 / build / build for x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-42 success Logs for x86_64-llvm-18 / veristat
bpf/vmtest-bpf-next-VM_Test-18 success Logs for set-matrix
bpf/vmtest-bpf-next-VM_Test-17 success Logs for s390x-gcc / veristat
bpf/vmtest-bpf-next-VM_Test-9 success Logs for aarch64-gcc / test (test_verifier, false, 360) / test_verifier on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-16 success Logs for s390x-gcc / test (test_verifier, false, 360) / test_verifier on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-21 success Logs for x86_64-gcc / test (test_maps, false, 360) / test_maps on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-25 success Logs for x86_64-gcc / test (test_progs_parallel, true, 30) / test_progs_parallel on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-23 success Logs for x86_64-gcc / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-24 success Logs for x86_64-gcc / test (test_progs_no_alu32_parallel, true, 30) / test_progs_no_alu32_parallel on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-26 success Logs for x86_64-gcc / test (test_verifier, false, 360) / test_verifier on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-27 success Logs for x86_64-gcc / veristat / veristat on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-30 success Logs for x86_64-llvm-17 / test (test_maps, false, 360) / test_maps on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-31 success Logs for x86_64-llvm-17 / test (test_progs, false, 360) / test_progs on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-33 success Logs for x86_64-llvm-17 / test (test_verifier, false, 360) / test_verifier on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-37 success Logs for x86_64-llvm-18 / test (test_maps, false, 360) / test_maps on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-38 success Logs for x86_64-llvm-18 / test (test_progs, false, 360) / test_progs on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-39 success Logs for x86_64-llvm-18 / test (test_progs_cpuv4, false, 360) / test_progs_cpuv4 on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-40 success Logs for x86_64-llvm-18 / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-41 success Logs for x86_64-llvm-18 / test (test_verifier, false, 360) / test_verifier on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-6 success Logs for aarch64-gcc / test (test_maps, false, 360) / test_maps on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-7 success Logs for aarch64-gcc / test (test_progs, false, 360) / test_progs on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-8 success Logs for aarch64-gcc / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-13 success Logs for s390x-gcc / test (test_maps, false, 360) / test_maps on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-22 success Logs for x86_64-gcc / test (test_progs, false, 360) / test_progs on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-32 success Logs for x86_64-llvm-17 / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-36 success Logs for x86_64-llvm-18 / build-release / build for x86_64 with llvm-18 and -O2 optimization
bpf/vmtest-bpf-next-VM_Test-15 success Logs for s390x-gcc / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-29 success Logs for x86_64-llvm-17 / build-release / build for x86_64 with llvm-17 and -O2 optimization
bpf/vmtest-bpf-next-VM_Test-14 success Logs for s390x-gcc / test (test_progs, false, 360) / test_progs on s390x with gcc
netdev/series_format success Posting correctly formatted
netdev/tree_selection success Clearly marked for bpf-next, async
netdev/ynl success Generated files up to date; no warnings/errors; no diff in generated;
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 7577 this patch: 7577
netdev/build_tools success Errors and warnings before: 1 this patch: 1
netdev/cc_maintainers warning 12 maintainers not CCed: haoluo@google.com song@kernel.org pabeni@redhat.com eddyz87@gmail.com netdev@vger.kernel.org sdf@google.com martin.lau@linux.dev kpsingh@kernel.org kuba@kernel.org jakub@cloudflare.com edumazet@google.com jolsa@kernel.org
netdev/build_clang success Errors and warnings before: 2250 this patch: 2250
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 8070 this patch: 8070
netdev/checkpatch warning CHECK: Please use a blank line after function/struct/union/enum declarations WARNING: line length of 90 exceeds 80 columns
netdev/build_clang_rust success No Rust files in patch. Skipping build
netdev/kdoc success Errors and warnings before: 6 this patch: 6
netdev/source_inline success Was 0 now: 0

Commit Message

Yonghong Song March 5, 2024, 8:22 p.m. UTC
Add link support for sk_msg program. This will make user space
easy to manage as most common used programs have alrady have
link support.

Signed-off-by: Yonghong Song <yonghong.song@linux.dev>
---
 include/linux/bpf.h            |  13 +++
 include/uapi/linux/bpf.h       |   5 ++
 kernel/bpf/syscall.c           |   3 +
 net/core/skmsg.c               | 153 +++++++++++++++++++++++++++++++++
 net/core/sock_map.c            |   6 +-
 tools/include/uapi/linux/bpf.h |   5 ++
 6 files changed, 181 insertions(+), 4 deletions(-)

Comments

Andrii Nakryiko March 9, 2024, 12:59 a.m. UTC | #1
On Tue, Mar 5, 2024 at 12:22 PM Yonghong Song <yonghong.song@linux.dev> wrote:
>
> Add link support for sk_msg program. This will make user space
> easy to manage as most common used programs have alrady have
> link support.

So we have:

SEC("sk_skb/stream_parser") mapping to SK_SKB/BPF_SK_SKB_STREAM_PARSER.
SEC("sk_skb/stream_verdict") mapping to SK_SKB/BPF_SK_SKB_STREAM_VERDICT.
SEC("sk_msg") mapping to SK_MSG/BPF_SK_MSG_VERDICT.

Are those all kind of in the same category and should we support link
for both SK_MSG and SK_SKB? I'm not too familiar, maybe John or
someone else can clarify.

>
> Signed-off-by: Yonghong Song <yonghong.song@linux.dev>
> ---
>  include/linux/bpf.h            |  13 +++
>  include/uapi/linux/bpf.h       |   5 ++
>  kernel/bpf/syscall.c           |   3 +
>  net/core/skmsg.c               | 153 +++++++++++++++++++++++++++++++++
>  net/core/sock_map.c            |   6 +-
>  tools/include/uapi/linux/bpf.h |   5 ++
>  6 files changed, 181 insertions(+), 4 deletions(-)
>

[...]
Yonghong Song March 9, 2024, 6:41 p.m. UTC | #2
On 3/8/24 4:59 PM, Andrii Nakryiko wrote:
> On Tue, Mar 5, 2024 at 12:22 PM Yonghong Song <yonghong.song@linux.dev> wrote:
>> Add link support for sk_msg program. This will make user space
>> easy to manage as most common used programs have alrady have
>> link support.
> So we have:
>
> SEC("sk_skb/stream_parser") mapping to SK_SKB/BPF_SK_SKB_STREAM_PARSER.
> SEC("sk_skb/stream_verdict") mapping to SK_SKB/BPF_SK_SKB_STREAM_VERDICT.
> SEC("sk_msg") mapping to SK_MSG/BPF_SK_MSG_VERDICT.
>
> Are those all kind of in the same category and should we support link
> for both SK_MSG and SK_SKB? I'm not too familiar, maybe John or
> someone else can clarify.

I can add sk_skb bpf_link support as well. Yes, sk_msg and sk_skb are
similar to each other.

>
>> Signed-off-by: Yonghong Song <yonghong.song@linux.dev>
>> ---
>>   include/linux/bpf.h            |  13 +++
>>   include/uapi/linux/bpf.h       |   5 ++
>>   kernel/bpf/syscall.c           |   3 +
>>   net/core/skmsg.c               | 153 +++++++++++++++++++++++++++++++++
>>   net/core/sock_map.c            |   6 +-
>>   tools/include/uapi/linux/bpf.h |   5 ++
>>   6 files changed, 181 insertions(+), 4 deletions(-)
>>
> [...]
Jakub Sitnicki March 10, 2024, 7:23 p.m. UTC | #3
On Fri, Mar 08, 2024 at 04:59 PM -08, Andrii Nakryiko wrote:
> On Tue, Mar 5, 2024 at 12:22 PM Yonghong Song <yonghong.song@linux.dev> wrote:
>>
>> Add link support for sk_msg program. This will make user space
>> easy to manage as most common used programs have alrady have
>> link support.
>
> So we have:
>
> SEC("sk_skb/stream_parser") mapping to SK_SKB/BPF_SK_SKB_STREAM_PARSER.
> SEC("sk_skb/stream_verdict") mapping to SK_SKB/BPF_SK_SKB_STREAM_VERDICT.
> SEC("sk_msg") mapping to SK_MSG/BPF_SK_MSG_VERDICT.
>
> Are those all kind of in the same category and should we support link
> for both SK_MSG and SK_SKB? I'm not too familiar, maybe John or
> someone else can clarify.

We also have the most recent SK_SKB/BPF_SK_SKB_VERDICT [1], which is
what one would use instead of SK_SKB/BPF_SK_SKB_STREAM_* prog pair when
chopping the stream of bytes into frames is not needed. For instance,
because we're redirecting from a UDP socket.

I have a cheatsheet that clarifies in which redirect-with-sockmap
configuration what program type + attach type is used [2].

[1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=a7ba4558e69a3c2ae4ca521f015832ef44799538
[2] https://github.com/jsitnicki/srecon-2023-sockmap/blob/main/sockmap-cheatsheet.png
Jiri Olsa March 11, 2024, 8:30 a.m. UTC | #4
On Tue, Mar 05, 2024 at 12:22:00PM -0800, Yonghong Song wrote:

SNIP

> +
> +int bpf_skmsg_link_create(const union bpf_attr *attr, struct bpf_prog *prog)
> +{
> +	struct bpf_link_primer link_primer;
> +	struct bpf_skmsg_link *skmsg_link;
> +	enum bpf_attach_type attach_type;
> +	struct bpf_map *map;
> +	int ret;
> +
> +	if (attr->link_create.flags)
> +		return -EINVAL;
> +
> +	map = bpf_map_get_with_uref(attr->link_create.target_fd);
> +	if (IS_ERR(map))
> +		return PTR_ERR(map);
> +
> +	skmsg_link = kzalloc(sizeof(*skmsg_link), GFP_USER);
> +	if (!skmsg_link) {
> +		ret = -ENOMEM;
> +		goto out;
> +	}
> +
> +	attach_type = attr->link_create.attach_type;
> +	bpf_link_init(&skmsg_link->link, BPF_LINK_TYPE_SK_MSG, &bpf_skmsg_link_ops, prog);
> +	skmsg_link->map = map;
> +	skmsg_link->attach_type = attach_type;
> +
> +	ret = bpf_link_prime(&skmsg_link->link, &link_primer);
> +	if (ret) {
> +		kfree(skmsg_link);
> +		goto out;
> +	}
> +
> +	ret = sock_map_prog_update(map, prog, NULL, attach_type);
> +	if (ret) {
> +		bpf_link_cleanup(&link_primer);
> +		goto out;
> +	}
> +
> +	bpf_prog_inc(prog);

there's already prog ref taken in link_create, is this needed?
also I might be missing some skmsg logic, but I can't see thi
being released

jirka

> +
> +	return bpf_link_settle(&link_primer);
> +
> +out:
> +	bpf_map_put_with_uref(map);
> +	return ret;
> +}
> diff --git a/net/core/sock_map.c b/net/core/sock_map.c
> index 27d733c0f65e..63372bc368f1 100644
> --- a/net/core/sock_map.c
> +++ b/net/core/sock_map.c
> @@ -24,8 +24,6 @@ struct bpf_stab {
>  #define SOCK_CREATE_FLAG_MASK				\
>  	(BPF_F_NUMA_NODE | BPF_F_RDONLY | BPF_F_WRONLY)
>  
> -static int sock_map_prog_update(struct bpf_map *map, struct bpf_prog *prog,
> -				struct bpf_prog *old, u32 which);
>  static struct sk_psock_progs *sock_map_progs(struct bpf_map *map);
>  
>  static struct bpf_map *sock_map_alloc(union bpf_attr *attr)
> @@ -1488,8 +1486,8 @@ static int sock_map_prog_lookup(struct bpf_map *map, struct bpf_prog ***pprog,
>  	return 0;
>  }
>  
> -static int sock_map_prog_update(struct bpf_map *map, struct bpf_prog *prog,
> -				struct bpf_prog *old, u32 which)
> +int sock_map_prog_update(struct bpf_map *map, struct bpf_prog *prog,
> +			 struct bpf_prog *old, u32 which)
>  {
>  	struct bpf_prog **pprog;
>  	int ret;
> diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
> index a241f407c234..c7d2a5fcf37a 100644
> --- a/tools/include/uapi/linux/bpf.h
> +++ b/tools/include/uapi/linux/bpf.h
> @@ -1129,6 +1129,7 @@ enum bpf_link_type {
>  	BPF_LINK_TYPE_TCX = 11,
>  	BPF_LINK_TYPE_UPROBE_MULTI = 12,
>  	BPF_LINK_TYPE_NETKIT = 13,
> +	BPF_LINK_TYPE_SK_MSG = 14,
>  	__MAX_BPF_LINK_TYPE,
>  };
>  
> @@ -6699,6 +6700,10 @@ struct bpf_link_info {
>  			__u32 ifindex;
>  			__u32 attach_type;
>  		} netkit;
> +		struct {
> +			__u32 map_id;
> +			__u32 attach_type;
> +		} skmsg;
>  	};
>  } __attribute__((aligned(8)));
>  
> -- 
> 2.43.0
> 
>
Yonghong Song March 11, 2024, 9:58 p.m. UTC | #5
On 3/10/24 12:23 PM, Jakub Sitnicki wrote:
> On Fri, Mar 08, 2024 at 04:59 PM -08, Andrii Nakryiko wrote:
>> On Tue, Mar 5, 2024 at 12:22 PM Yonghong Song <yonghong.song@linux.dev> wrote:
>>> Add link support for sk_msg program. This will make user space
>>> easy to manage as most common used programs have alrady have
>>> link support.
>> So we have:
>>
>> SEC("sk_skb/stream_parser") mapping to SK_SKB/BPF_SK_SKB_STREAM_PARSER.
>> SEC("sk_skb/stream_verdict") mapping to SK_SKB/BPF_SK_SKB_STREAM_VERDICT.
>> SEC("sk_msg") mapping to SK_MSG/BPF_SK_MSG_VERDICT.
>>
>> Are those all kind of in the same category and should we support link
>> for both SK_MSG and SK_SKB? I'm not too familiar, maybe John or
>> someone else can clarify.
> We also have the most recent SK_SKB/BPF_SK_SKB_VERDICT [1], which is
> what one would use instead of SK_SKB/BPF_SK_SKB_STREAM_* prog pair when
> chopping the stream of bytes into frames is not needed. For instance,
> because we're redirecting from a UDP socket.
>
> I have a cheatsheet that clarifies in which redirect-with-sockmap
> configuration what program type + attach type is used [2].
>
> [1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=a7ba4558e69a3c2ae4ca521f015832ef44799538
> [2] https://github.com/jsitnicki/srecon-2023-sockmap/blob/main/sockmap-cheatsheet.png

Thanks. I will take a look. Yes, agree, we only need to implement
the recommended one, which is SK_SKB/BPF_SK_SKB_VERDICT if I understand correctly.
Yonghong Song March 11, 2024, 10:07 p.m. UTC | #6
On 3/11/24 1:30 AM, Jiri Olsa wrote:
> On Tue, Mar 05, 2024 at 12:22:00PM -0800, Yonghong Song wrote:
>
> SNIP
>
>> +
>> +int bpf_skmsg_link_create(const union bpf_attr *attr, struct bpf_prog *prog)
>> +{
>> +	struct bpf_link_primer link_primer;
>> +	struct bpf_skmsg_link *skmsg_link;
>> +	enum bpf_attach_type attach_type;
>> +	struct bpf_map *map;
>> +	int ret;
>> +
>> +	if (attr->link_create.flags)
>> +		return -EINVAL;
>> +
>> +	map = bpf_map_get_with_uref(attr->link_create.target_fd);
>> +	if (IS_ERR(map))
>> +		return PTR_ERR(map);
>> +
>> +	skmsg_link = kzalloc(sizeof(*skmsg_link), GFP_USER);
>> +	if (!skmsg_link) {
>> +		ret = -ENOMEM;
>> +		goto out;
>> +	}
>> +
>> +	attach_type = attr->link_create.attach_type;
>> +	bpf_link_init(&skmsg_link->link, BPF_LINK_TYPE_SK_MSG, &bpf_skmsg_link_ops, prog);
>> +	skmsg_link->map = map;
>> +	skmsg_link->attach_type = attach_type;
>> +
>> +	ret = bpf_link_prime(&skmsg_link->link, &link_primer);
>> +	if (ret) {
>> +		kfree(skmsg_link);
>> +		goto out;
>> +	}
>> +
>> +	ret = sock_map_prog_update(map, prog, NULL, attach_type);
>> +	if (ret) {
>> +		bpf_link_cleanup(&link_primer);
>> +		goto out;
>> +	}
>> +
>> +	bpf_prog_inc(prog);
> there's already prog ref taken in link_create, is this needed?
> also I might be missing some skmsg logic, but I can't see thi
> being released

Here, we are trying to do create/attach.
The prog reference count will be decremented during detach
(sock_map_prog_detach), so we need to increase prog ref count here.

Another thing, it is possible a attached prog is swapped out.
In current implementation, ref count will be decremented for
the prog swapped out. So increasing ref count for prog here
is needed to balance that ref count decrement.

>
> jirka
>
>> +
>> +	return bpf_link_settle(&link_primer);
>> +
>> +out:
>> +	bpf_map_put_with_uref(map);
>> +	return ret;
>> +}
[...]
diff mbox series

Patch

diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 785660810e6a..a3112a295f5c 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -2982,10 +2982,14 @@  int sock_map_prog_detach(const union bpf_attr *attr, enum bpf_prog_type ptype);
 int sock_map_update_elem_sys(struct bpf_map *map, void *key, void *value, u64 flags);
 int sock_map_bpf_prog_query(const union bpf_attr *attr,
 			    union bpf_attr __user *uattr);
+int sock_map_prog_update(struct bpf_map *map, struct bpf_prog *prog,
+			 struct bpf_prog *old, u32 which);
 
 void sock_map_unhash(struct sock *sk);
 void sock_map_destroy(struct sock *sk);
 void sock_map_close(struct sock *sk, long timeout);
+
+int bpf_skmsg_link_create(const union bpf_attr *attr, struct bpf_prog *prog);
 #else
 static inline int bpf_dev_bound_kfunc_check(struct bpf_verifier_log *log,
 					    struct bpf_prog_aux *prog_aux)
@@ -3080,6 +3084,15 @@  static inline int sock_map_bpf_prog_query(const union bpf_attr *attr,
 {
 	return -EINVAL;
 }
+int sock_map_prog_update(struct bpf_map *map, struct bpf_prog *prog,
+			 struct bpf_prog *old, u32 which)
+{
+	return -EOPNOTSUPP;
+}
+int bpf_skmsg_link_create(const union bpf_attr *attr, struct bpf_prog *prog)
+{
+	return -EOPNOTSUPP;
+}
 #endif /* CONFIG_BPF_SYSCALL */
 #endif /* CONFIG_NET && CONFIG_BPF_SYSCALL */
 
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index a241f407c234..c7d2a5fcf37a 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -1129,6 +1129,7 @@  enum bpf_link_type {
 	BPF_LINK_TYPE_TCX = 11,
 	BPF_LINK_TYPE_UPROBE_MULTI = 12,
 	BPF_LINK_TYPE_NETKIT = 13,
+	BPF_LINK_TYPE_SK_MSG = 14,
 	__MAX_BPF_LINK_TYPE,
 };
 
@@ -6699,6 +6700,10 @@  struct bpf_link_info {
 			__u32 ifindex;
 			__u32 attach_type;
 		} netkit;
+		struct {
+			__u32 map_id;
+			__u32 attach_type;
+		} skmsg;
 	};
 } __attribute__((aligned(8)));
 
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index b2750b79ac80..7fd3e6c93612 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -5155,6 +5155,9 @@  static int link_create(union bpf_attr *attr, bpfptr_t uattr)
 	case BPF_PROG_TYPE_SK_LOOKUP:
 		ret = netns_bpf_link_create(attr, prog);
 		break;
+	case BPF_PROG_TYPE_SK_MSG:
+		ret = bpf_skmsg_link_create(attr, prog);
+		break;
 #ifdef CONFIG_NET
 	case BPF_PROG_TYPE_XDP:
 		ret = bpf_xdp_link_attach(attr, prog);
diff --git a/net/core/skmsg.c b/net/core/skmsg.c
index 4d75ef9d24bf..2e3d15294966 100644
--- a/net/core/skmsg.c
+++ b/net/core/skmsg.c
@@ -1256,3 +1256,156 @@  void sk_psock_stop_verdict(struct sock *sk, struct sk_psock *psock)
 	sk->sk_data_ready = psock->saved_data_ready;
 	psock->saved_data_ready = NULL;
 }
+
+struct bpf_skmsg_link {
+	struct bpf_link link;
+	struct bpf_map *map;
+	enum bpf_attach_type attach_type;
+};
+
+static DEFINE_MUTEX(link_mutex);
+
+static struct bpf_skmsg_link *bpf_skmsg_link(const struct bpf_link *link)
+{
+	return container_of(link, struct bpf_skmsg_link, link);
+}
+
+static void bpf_skmsg_link_release(struct bpf_link *link)
+{
+	struct bpf_skmsg_link *skmsg_link = bpf_skmsg_link(link);
+
+	mutex_lock(&link_mutex);
+	if (skmsg_link->map) {
+		(void)sock_map_prog_update(skmsg_link->map, NULL, link->prog,
+					   skmsg_link->attach_type);
+		bpf_map_put_with_uref(skmsg_link->map);
+		skmsg_link->map = NULL;
+	}
+	mutex_unlock(&link_mutex);
+}
+
+static int bpf_skmsg_link_detach(struct bpf_link *link)
+{
+	bpf_skmsg_link_release(link);
+	return 0;
+}
+
+static void bpf_skmsg_link_dealloc(struct bpf_link *link)
+{
+	kfree(bpf_skmsg_link(link));
+}
+
+static int bpf_skmsg_link_update_prog(struct bpf_link *link,
+				      struct bpf_prog *new_prog,
+				      struct bpf_prog *old_prog)
+{
+	const struct bpf_skmsg_link *skmsg_link = bpf_skmsg_link(link);
+	int ret = 0;
+
+	mutex_lock(&link_mutex);
+	if (old_prog && link->prog != old_prog) {
+		ret = -EPERM;
+		goto out;
+	}
+
+	if (link->prog->type != new_prog->type) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	ret = sock_map_prog_update(skmsg_link->map, new_prog, old_prog,
+				   skmsg_link->attach_type);
+	if (!ret)
+		bpf_prog_inc(new_prog);
+
+out:
+	mutex_unlock(&link_mutex);
+	return ret;
+}
+
+static int bpf_skmsg_link_fill_info(const struct bpf_link *link,
+				    struct bpf_link_info *info)
+{
+	const struct bpf_skmsg_link *skmsg_link = bpf_skmsg_link(link);
+	u32 map_id = 0;
+
+	mutex_lock(&link_mutex);
+	if (skmsg_link->map)
+		map_id = skmsg_link->map->id;
+	mutex_unlock(&link_mutex);
+
+	info->skmsg.map_id = map_id;
+	info->skmsg.attach_type = skmsg_link->attach_type;
+	return 0;
+}
+
+static void bpf_skmsg_link_show_fdinfo(const struct bpf_link *link,
+				       struct seq_file *seq)
+{
+	const struct bpf_skmsg_link *skmsg_link = bpf_skmsg_link(link);
+	u32 map_id = 0;
+
+	mutex_lock(&link_mutex);
+	if (skmsg_link->map)
+		map_id = skmsg_link->map->id;
+	mutex_unlock(&link_mutex);
+
+	seq_printf(seq, "map_id:\t%u\n", map_id);
+	seq_printf(seq, "attach_type:\t%u (...)\n", skmsg_link->attach_type);
+}
+
+static const struct bpf_link_ops bpf_skmsg_link_ops = {
+	.release = bpf_skmsg_link_release,
+	.dealloc = bpf_skmsg_link_dealloc,
+	.detach = bpf_skmsg_link_detach,
+	.update_prog = bpf_skmsg_link_update_prog,
+	.fill_link_info = bpf_skmsg_link_fill_info,
+	.show_fdinfo = bpf_skmsg_link_show_fdinfo,
+};
+
+int bpf_skmsg_link_create(const union bpf_attr *attr, struct bpf_prog *prog)
+{
+	struct bpf_link_primer link_primer;
+	struct bpf_skmsg_link *skmsg_link;
+	enum bpf_attach_type attach_type;
+	struct bpf_map *map;
+	int ret;
+
+	if (attr->link_create.flags)
+		return -EINVAL;
+
+	map = bpf_map_get_with_uref(attr->link_create.target_fd);
+	if (IS_ERR(map))
+		return PTR_ERR(map);
+
+	skmsg_link = kzalloc(sizeof(*skmsg_link), GFP_USER);
+	if (!skmsg_link) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	attach_type = attr->link_create.attach_type;
+	bpf_link_init(&skmsg_link->link, BPF_LINK_TYPE_SK_MSG, &bpf_skmsg_link_ops, prog);
+	skmsg_link->map = map;
+	skmsg_link->attach_type = attach_type;
+
+	ret = bpf_link_prime(&skmsg_link->link, &link_primer);
+	if (ret) {
+		kfree(skmsg_link);
+		goto out;
+	}
+
+	ret = sock_map_prog_update(map, prog, NULL, attach_type);
+	if (ret) {
+		bpf_link_cleanup(&link_primer);
+		goto out;
+	}
+
+	bpf_prog_inc(prog);
+
+	return bpf_link_settle(&link_primer);
+
+out:
+	bpf_map_put_with_uref(map);
+	return ret;
+}
diff --git a/net/core/sock_map.c b/net/core/sock_map.c
index 27d733c0f65e..63372bc368f1 100644
--- a/net/core/sock_map.c
+++ b/net/core/sock_map.c
@@ -24,8 +24,6 @@  struct bpf_stab {
 #define SOCK_CREATE_FLAG_MASK				\
 	(BPF_F_NUMA_NODE | BPF_F_RDONLY | BPF_F_WRONLY)
 
-static int sock_map_prog_update(struct bpf_map *map, struct bpf_prog *prog,
-				struct bpf_prog *old, u32 which);
 static struct sk_psock_progs *sock_map_progs(struct bpf_map *map);
 
 static struct bpf_map *sock_map_alloc(union bpf_attr *attr)
@@ -1488,8 +1486,8 @@  static int sock_map_prog_lookup(struct bpf_map *map, struct bpf_prog ***pprog,
 	return 0;
 }
 
-static int sock_map_prog_update(struct bpf_map *map, struct bpf_prog *prog,
-				struct bpf_prog *old, u32 which)
+int sock_map_prog_update(struct bpf_map *map, struct bpf_prog *prog,
+			 struct bpf_prog *old, u32 which)
 {
 	struct bpf_prog **pprog;
 	int ret;
diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
index a241f407c234..c7d2a5fcf37a 100644
--- a/tools/include/uapi/linux/bpf.h
+++ b/tools/include/uapi/linux/bpf.h
@@ -1129,6 +1129,7 @@  enum bpf_link_type {
 	BPF_LINK_TYPE_TCX = 11,
 	BPF_LINK_TYPE_UPROBE_MULTI = 12,
 	BPF_LINK_TYPE_NETKIT = 13,
+	BPF_LINK_TYPE_SK_MSG = 14,
 	__MAX_BPF_LINK_TYPE,
 };
 
@@ -6699,6 +6700,10 @@  struct bpf_link_info {
 			__u32 ifindex;
 			__u32 attach_type;
 		} netkit;
+		struct {
+			__u32 map_id;
+			__u32 attach_type;
+		} skmsg;
 	};
 } __attribute__((aligned(8)));