1bd4aed0eSJiong Wang // SPDX-License-Identifier: GPL-2.0
2bd4aed0eSJiong Wang // Copyright (c) 2018 Facebook
3bd4aed0eSJiong Wang 
46cd4dcc3SFlorent Revest #include "vmlinux.h"
5bd4aed0eSJiong Wang 
63e689141SToke Høiland-Jørgensen #include <bpf/bpf_helpers.h>
73e689141SToke Høiland-Jørgensen #include <bpf/bpf_endian.h>
8*6fdd671bSFlorent Revest #include <bpf/bpf_tracing.h>
9bd4aed0eSJiong Wang 
106cd4dcc3SFlorent Revest #define AF_INET6 10
116cd4dcc3SFlorent Revest 
1269d96519SStanislav Fomichev struct socket_cookie {
1369d96519SStanislav Fomichev 	__u64 cookie_key;
1469d96519SStanislav Fomichev 	__u32 cookie_value;
15bd4aed0eSJiong Wang };
16bd4aed0eSJiong Wang 
17df0b7792SAndrii Nakryiko struct {
18bc7430ccSAndrii Nakryiko 	__uint(type, BPF_MAP_TYPE_SK_STORAGE);
19bc7430ccSAndrii Nakryiko 	__uint(map_flags, BPF_F_NO_PREALLOC);
20bc7430ccSAndrii Nakryiko 	__type(key, int);
21bc7430ccSAndrii Nakryiko 	__type(value, struct socket_cookie);
22bc7430ccSAndrii Nakryiko } socket_cookies SEC(".maps");
2369d96519SStanislav Fomichev 
24*6fdd671bSFlorent Revest /*
25*6fdd671bSFlorent Revest  * These three programs get executed in a row on connect() syscalls. The
26*6fdd671bSFlorent Revest  * userspace side of the test creates a client socket, issues a connect() on it
27*6fdd671bSFlorent Revest  * and then checks that the local storage associated with this socket has:
28*6fdd671bSFlorent Revest  * cookie_value == local_port << 8 | 0xFF
29*6fdd671bSFlorent Revest  * The different parts of this cookie_value are appended by those hooks if they
30*6fdd671bSFlorent Revest  * all agree on the output of bpf_get_socket_cookie().
31*6fdd671bSFlorent Revest  */
32bd4aed0eSJiong Wang SEC("cgroup/connect6")
set_cookie(struct bpf_sock_addr * ctx)33bd4aed0eSJiong Wang int set_cookie(struct bpf_sock_addr *ctx)
34bd4aed0eSJiong Wang {
3569d96519SStanislav Fomichev 	struct socket_cookie *p;
36bd4aed0eSJiong Wang 
37bd4aed0eSJiong Wang 	if (ctx->family != AF_INET6 || ctx->user_family != AF_INET6)
38bd4aed0eSJiong Wang 		return 1;
39bd4aed0eSJiong Wang 
4069d96519SStanislav Fomichev 	p = bpf_sk_storage_get(&socket_cookies, ctx->sk, 0,
4169d96519SStanislav Fomichev 			       BPF_SK_STORAGE_GET_F_CREATE);
4269d96519SStanislav Fomichev 	if (!p)
4369d96519SStanislav Fomichev 		return 1;
4469d96519SStanislav Fomichev 
45*6fdd671bSFlorent Revest 	p->cookie_value = 0xF;
4669d96519SStanislav Fomichev 	p->cookie_key = bpf_get_socket_cookie(ctx);
47bd4aed0eSJiong Wang 
48bd4aed0eSJiong Wang 	return 1;
49bd4aed0eSJiong Wang }
50bd4aed0eSJiong Wang 
51bd4aed0eSJiong Wang SEC("sockops")
update_cookie_sockops(struct bpf_sock_ops * ctx)52*6fdd671bSFlorent Revest int update_cookie_sockops(struct bpf_sock_ops *ctx)
53bd4aed0eSJiong Wang {
546cd4dcc3SFlorent Revest 	struct bpf_sock *sk = ctx->sk;
5569d96519SStanislav Fomichev 	struct socket_cookie *p;
56bd4aed0eSJiong Wang 
57bd4aed0eSJiong Wang 	if (ctx->family != AF_INET6)
58bd4aed0eSJiong Wang 		return 1;
59bd4aed0eSJiong Wang 
60bd4aed0eSJiong Wang 	if (ctx->op != BPF_SOCK_OPS_TCP_CONNECT_CB)
61bd4aed0eSJiong Wang 		return 1;
62bd4aed0eSJiong Wang 
636cd4dcc3SFlorent Revest 	if (!sk)
64bd4aed0eSJiong Wang 		return 1;
65bd4aed0eSJiong Wang 
666cd4dcc3SFlorent Revest 	p = bpf_sk_storage_get(&socket_cookies, sk, 0, 0);
6769d96519SStanislav Fomichev 	if (!p)
6869d96519SStanislav Fomichev 		return 1;
6969d96519SStanislav Fomichev 
7069d96519SStanislav Fomichev 	if (p->cookie_key != bpf_get_socket_cookie(ctx))
7169d96519SStanislav Fomichev 		return 1;
7269d96519SStanislav Fomichev 
73*6fdd671bSFlorent Revest 	p->cookie_value |= (ctx->local_port << 8);
74bd4aed0eSJiong Wang 
75bd4aed0eSJiong Wang 	return 1;
76bd4aed0eSJiong Wang }
77bd4aed0eSJiong Wang 
78*6fdd671bSFlorent Revest SEC("fexit/inet_stream_connect")
BPF_PROG(update_cookie_tracing,struct socket * sock,struct sockaddr * uaddr,int addr_len,int flags)79*6fdd671bSFlorent Revest int BPF_PROG(update_cookie_tracing, struct socket *sock,
80*6fdd671bSFlorent Revest 	     struct sockaddr *uaddr, int addr_len, int flags)
81*6fdd671bSFlorent Revest {
82*6fdd671bSFlorent Revest 	struct socket_cookie *p;
83*6fdd671bSFlorent Revest 
84*6fdd671bSFlorent Revest 	if (uaddr->sa_family != AF_INET6)
85*6fdd671bSFlorent Revest 		return 0;
86*6fdd671bSFlorent Revest 
87*6fdd671bSFlorent Revest 	p = bpf_sk_storage_get(&socket_cookies, sock->sk, 0, 0);
88*6fdd671bSFlorent Revest 	if (!p)
89*6fdd671bSFlorent Revest 		return 0;
90*6fdd671bSFlorent Revest 
91*6fdd671bSFlorent Revest 	if (p->cookie_key != bpf_get_socket_cookie(sock->sk))
92*6fdd671bSFlorent Revest 		return 0;
93*6fdd671bSFlorent Revest 
94*6fdd671bSFlorent Revest 	p->cookie_value |= 0xF0;
95*6fdd671bSFlorent Revest 
96*6fdd671bSFlorent Revest 	return 0;
97*6fdd671bSFlorent Revest }
98*6fdd671bSFlorent Revest 
99bd4aed0eSJiong Wang char _license[] SEC("license") = "GPL";
100