summaryrefslogtreecommitdiff
path: root/tools/testing/selftests/bpf/progs/bpf_smc.c
blob: 6263a45bf0066a582abf6cd84da4bba352811b47 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
// SPDX-License-Identifier: GPL-2.0

#include "vmlinux.h"

#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include "bpf_tracing_net.h"

char _license[] SEC("license") = "GPL";

#ifndef SMC_HS_CTRL_NAME_MAX
#define SMC_HS_CTRL_NAME_MAX 16
#endif

enum {
	BPF_SMC_LISTEN	= 10,
};

struct smc_sock___local {
	struct sock sk;
	struct smc_sock *listen_smc;
	bool use_fallback;
} __attribute__((preserve_access_index));

struct smc_hs_ctrl___local {
	char name[SMC_HS_CTRL_NAME_MAX];
	int (*syn_option)(struct tcp_sock *);
	int (*synack_option)(const struct tcp_sock  *, struct inet_request_sock *);
} __attribute__((preserve_access_index));

struct netns_smc___local {
	struct smc_hs_ctrl___local *hs_ctrl;
} __attribute__((preserve_access_index));

struct net___local {
	struct netns_smc___local smc;
} __attribute__((preserve_access_index));

int smc_cnt = 0;
int fallback_cnt = 0;

SEC("fentry/smc_release")
int BPF_PROG(bpf_smc_release, struct socket *sock)
{
	/* only count from one side (client) */
	if (sock->sk->__sk_common.skc_state == BPF_SMC_LISTEN)
		return 0;
	smc_cnt++;
	return 0;
}

SEC("fentry/smc_switch_to_fallback")
int BPF_PROG(bpf_smc_switch_to_fallback, struct smc_sock___local *smc)
{
	/* only count from one side (client) */
	if (smc && !smc->listen_smc)
		fallback_cnt++;
	return 0;
}

/* go with default value if no strat was found */
bool default_ip_strat_value = true;

struct smc_policy_ip_key {
	__u32	sip;
	__u32	dip;
};

struct smc_policy_ip_value {
	__u8	mode;
};

struct {
	__uint(type, BPF_MAP_TYPE_HASH);
	__uint(key_size, sizeof(struct smc_policy_ip_key));
	__uint(value_size, sizeof(struct smc_policy_ip_value));
	__uint(max_entries, 128);
	__uint(map_flags, BPF_F_NO_PREALLOC);
} smc_policy_ip SEC(".maps");

static bool smc_check(__u32 src, __u32 dst)
{
	struct smc_policy_ip_value *value;
	struct smc_policy_ip_key key = {
		.sip = src,
		.dip = dst,
	};

	value = bpf_map_lookup_elem(&smc_policy_ip, &key);
	return value ? value->mode : default_ip_strat_value;
}

SEC("fmod_ret/update_socket_protocol")
int BPF_PROG(smc_run, int family, int type, int protocol)
{
	struct task_struct *task;

	if (family != AF_INET && family != AF_INET6)
		return protocol;

	if ((type & 0xf) != SOCK_STREAM)
		return protocol;

	if (protocol != 0 && protocol != IPPROTO_TCP)
		return protocol;

	task = bpf_get_current_task_btf();
	/* Prevent from affecting other tests */
	if (!task) {
		return protocol;
	} else {
		struct net___local *net = (struct net___local *)task->nsproxy->net_ns;

		if (!bpf_core_field_exists(struct net___local, smc) || !net->smc.hs_ctrl)
			return protocol;
	}

	return IPPROTO_SMC;
}

SEC("struct_ops")
int BPF_PROG(bpf_smc_set_tcp_option_cond, const struct tcp_sock *tp,
	     struct inet_request_sock *ireq)
{
	return smc_check(ireq->req.__req_common.skc_daddr,
			 ireq->req.__req_common.skc_rcv_saddr);
}

SEC("struct_ops")
int BPF_PROG(bpf_smc_set_tcp_option, struct tcp_sock *tp)
{
	return smc_check(tp->inet_conn.icsk_inet.sk.__sk_common.skc_rcv_saddr,
			 tp->inet_conn.icsk_inet.sk.__sk_common.skc_daddr);
}

SEC(".struct_ops")
struct smc_hs_ctrl___local  linkcheck = {
	.name		= "linkcheck",
	.syn_option	= (void *)bpf_smc_set_tcp_option,
	.synack_option	= (void *)bpf_smc_set_tcp_option_cond,
};