@@ -575,6 +575,14 @@ extern int bpf_iter_css_new(struct bpf_iter_css *it,
extern struct cgroup_subsys_state *bpf_iter_css_next(struct bpf_iter_css *it) __weak __ksym;
extern void bpf_iter_css_destroy(struct bpf_iter_css *it) __weak __ksym;
+struct bpf_iter_mptcp_subflow;
+extern int bpf_iter_mptcp_subflow_new(struct bpf_iter_mptcp_subflow *it,
+ struct mptcp_sock *msk) __weak __ksym;
+extern struct mptcp_subflow_context *
+bpf_iter_mptcp_subflow_next(struct bpf_iter_mptcp_subflow *it) __weak __ksym;
+extern void
+bpf_iter_mptcp_subflow_destroy(struct bpf_iter_mptcp_subflow *it) __weak __ksym;
+
extern int bpf_wq_init(struct bpf_wq *wq, void *p__map, unsigned int flags) __weak __ksym;
extern int bpf_wq_start(struct bpf_wq *wq, unsigned int flags) __weak __ksym;
extern int bpf_wq_set_callback_impl(struct bpf_wq *wq,
@@ -11,6 +11,7 @@
#include "mptcp_sock.skel.h"
#include "mptcpify.skel.h"
#include "mptcp_subflow.skel.h"
+#include "mptcp_bpf_iters.skel.h"
#define NS_TEST "mptcp_ns"
#define ADDR_1 "10.0.1.1"
@@ -33,6 +34,9 @@
#ifndef MPTCP_INFO
#define MPTCP_INFO 1
#endif
+#ifndef TCP_IS_MPTCP
+#define TCP_IS_MPTCP 43 /* Is MPTCP being used? */
+#endif
#ifndef MPTCP_INFO_FLAG_FALLBACK
#define MPTCP_INFO_FLAG_FALLBACK _BITUL(0)
#endif
@@ -480,6 +484,73 @@ static void test_subflow(void)
close(cgroup_fd);
}
+static void run_iters_subflow(void)
+{
+ int server_fd, client_fd;
+ int is_mptcp, err;
+ socklen_t len;
+
+ server_fd = start_mptcp_server(AF_INET, ADDR_1, PORT_1, 0);
+ if (!ASSERT_OK_FD(server_fd, "start_mptcp_server"))
+ return;
+
+ client_fd = connect_to_fd(server_fd, 0);
+ if (!ASSERT_OK_FD(client_fd, "connect_to_fd"))
+ goto close_server;
+
+ send_byte(client_fd);
+ wait_for_new_subflows(client_fd);
+
+ len = sizeof(is_mptcp);
+ /* mainly to trigger the BPF program */
+ err = getsockopt(client_fd, SOL_TCP, TCP_IS_MPTCP, &is_mptcp, &len);
+ if (ASSERT_OK(err, "getsockopt(client_fd, TCP_IS_MPTCP)"))
+ ASSERT_EQ(is_mptcp, 1, "is_mptcp");
+
+ close(client_fd);
+close_server:
+ close(server_fd);
+}
+
+static void test_iters_subflow(void)
+{
+ struct mptcp_bpf_iters *skel;
+ struct netns_obj *netns;
+ int cgroup_fd;
+
+ cgroup_fd = test__join_cgroup("/iters_subflow");
+ if (!ASSERT_OK_FD(cgroup_fd, "join_cgroup: iters_subflow"))
+ return;
+
+ skel = mptcp_bpf_iters__open_and_load();
+ if (!ASSERT_OK_PTR(skel, "skel_open_load: iters_subflow"))
+ goto close_cgroup;
+
+ skel->links.iters_subflow = bpf_program__attach_cgroup(skel->progs.iters_subflow,
+ cgroup_fd);
+ if (!ASSERT_OK_PTR(skel->links.iters_subflow, "attach getsockopt"))
+ goto skel_destroy;
+
+ netns = netns_new(NS_TEST, true);
+ if (!ASSERT_OK_PTR(netns, "netns_new: iters_subflow"))
+ goto skel_destroy;
+
+ if (endpoint_init("subflow", 4) < 0)
+ goto close_netns;
+
+ run_iters_subflow();
+
+ /* 1 + 2 + 3 + 4 = 10 */
+ ASSERT_EQ(skel->bss->ids, 10, "subflow ids");
+
+close_netns:
+ netns_free(netns);
+skel_destroy:
+ mptcp_bpf_iters__destroy(skel);
+close_cgroup:
+ close(cgroup_fd);
+}
+
void test_mptcp(void)
{
if (test__start_subtest("base"))
@@ -488,4 +559,6 @@ void test_mptcp(void)
test_mptcpify();
if (test__start_subtest("subflow"))
test_subflow();
+ if (test__start_subtest("iters_subflow"))
+ test_iters_subflow();
}
@@ -39,4 +39,13 @@ mptcp_subflow_tcp_sock(const struct mptcp_subflow_context *subflow)
return subflow->tcp_sock;
}
+/* ksym */
+extern struct mptcp_sock *bpf_mptcp_sock_acquire(struct mptcp_sock *msk) __ksym;
+extern void bpf_mptcp_sock_release(struct mptcp_sock *msk) __ksym;
+
+extern struct mptcp_subflow_context *
+bpf_mptcp_subflow_ctx(const struct sock *sk) __ksym;
+extern struct sock *
+bpf_mptcp_subflow_tcp_sock(const struct mptcp_subflow_context *subflow) __ksym;
+
#endif
new file mode 100644
@@ -0,0 +1,63 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2024, Kylin Software */
+
+/* vmlinux.h, bpf_helpers.h and other 'define' */
+#include "bpf_tracing_net.h"
+#include "mptcp_bpf.h"
+
+char _license[] SEC("license") = "GPL";
+int ids;
+
+#ifndef TCP_IS_MPTCP
+#define TCP_IS_MPTCP 43 /* Is MPTCP being used? */
+#endif
+
+SEC("cgroup/getsockopt")
+int iters_subflow(struct bpf_sockopt *ctx)
+{
+ struct mptcp_subflow_context *subflow;
+ struct bpf_sock *sk = ctx->sk;
+ struct sock *ssk = NULL;
+ struct mptcp_sock *msk;
+ int local_ids = 0;
+
+ if (ctx->level != SOL_TCP || ctx->optname != TCP_IS_MPTCP)
+ return 1;
+
+ msk = bpf_skc_to_mptcp_sock(sk);
+ if (!msk || msk->pm.server_side || !msk->pm.subflows)
+ return 1;
+
+ msk = bpf_mptcp_sock_acquire(msk);
+ if (!msk)
+ return 1;
+ bpf_for_each(mptcp_subflow, subflow, msk) {
+ /* Here MPTCP-specific packet scheduler kfunc can be called:
+ * this test is not doing anything really useful, only to
+ * verify the iteration works.
+ */
+
+ local_ids += subflow->subflow_id;
+
+ /* only to check the following kfunc works */
+ ssk = mptcp_subflow_tcp_sock(subflow);
+ }
+
+ if (!ssk)
+ goto out;
+
+ /* assert: if not OK, something wrong on the kernel side */
+ if (ssk->sk_dport != ((struct sock *)msk)->sk_dport)
+ goto out;
+
+ /* only to check the following kfunc works */
+ subflow = bpf_mptcp_subflow_ctx(ssk);
+ if (!subflow || subflow->token != msk->token)
+ goto out;
+
+ ids = local_ids;
+
+out:
+ bpf_mptcp_sock_release(msk);
+ return 1;
+}