mirror of
https://github.com/tbsdtv/linux_media.git
synced 2025-07-23 12:43:29 +02:00
mptcp: introduce MPTCP_FULL_INFO getsockopt
Some user-space applications want to monitor the subflows utilization. Dumping the per subflow tcp_info is not enough, as the PM could close and re-create the subflows under-the-hood, fooling the accounting. Even checking the src/dst addresses used by each subflow could not be enough, because new subflows could re-use the same address/port of the just closed one. This patch introduces a new socket option, allow dumping all the relevant information all-at-once (everything, everywhere...), in a consistent manner. Closes: https://github.com/multipath-tcp/mptcp_net-next/issues/388 Signed-off-by: Paolo Abeni <pabeni@redhat.com> Reviewed-by: Matthieu Baerts <matthieu.baerts@tessares.net> Signed-off-by: Matthieu Baerts <matthieu.baerts@tessares.net> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
committed by
Jakub Kicinski
parent
6f06b4d4d1
commit
492432074e
@@ -249,9 +249,33 @@ struct mptcp_subflow_addrs {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct mptcp_subflow_info {
|
||||||
|
__u32 id;
|
||||||
|
struct mptcp_subflow_addrs addrs;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct mptcp_full_info {
|
||||||
|
__u32 size_tcpinfo_kernel; /* must be 0, set by kernel */
|
||||||
|
__u32 size_tcpinfo_user;
|
||||||
|
__u32 size_sfinfo_kernel; /* must be 0, set by kernel */
|
||||||
|
__u32 size_sfinfo_user;
|
||||||
|
__u32 num_subflows; /* must be 0, set by kernel (real subflow count) */
|
||||||
|
__u32 size_arrays_user; /* max subflows that userspace is interested in;
|
||||||
|
* the buffers at subflow_info/tcp_info
|
||||||
|
* are respectively at least:
|
||||||
|
* size_arrays * size_sfinfo_user
|
||||||
|
* size_arrays * size_tcpinfo_user
|
||||||
|
* bytes wide
|
||||||
|
*/
|
||||||
|
__aligned_u64 subflow_info;
|
||||||
|
__aligned_u64 tcp_info;
|
||||||
|
struct mptcp_info mptcp_info;
|
||||||
|
};
|
||||||
|
|
||||||
/* MPTCP socket options */
|
/* MPTCP socket options */
|
||||||
#define MPTCP_INFO 1
|
#define MPTCP_INFO 1
|
||||||
#define MPTCP_TCPINFO 2
|
#define MPTCP_TCPINFO 2
|
||||||
#define MPTCP_SUBFLOW_ADDRS 3
|
#define MPTCP_SUBFLOW_ADDRS 3
|
||||||
|
#define MPTCP_FULL_INFO 4
|
||||||
|
|
||||||
#endif /* _UAPI_MPTCP_H */
|
#endif /* _UAPI_MPTCP_H */
|
||||||
|
@@ -15,6 +15,7 @@
|
|||||||
#include "protocol.h"
|
#include "protocol.h"
|
||||||
|
|
||||||
#define MIN_INFO_OPTLEN_SIZE 16
|
#define MIN_INFO_OPTLEN_SIZE 16
|
||||||
|
#define MIN_FULL_INFO_OPTLEN_SIZE 40
|
||||||
|
|
||||||
static struct sock *__mptcp_tcp_fallback(struct mptcp_sock *msk)
|
static struct sock *__mptcp_tcp_fallback(struct mptcp_sock *msk)
|
||||||
{
|
{
|
||||||
@@ -981,7 +982,8 @@ static int mptcp_put_subflow_data(struct mptcp_subflow_data *sfd,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int mptcp_get_subflow_data(struct mptcp_subflow_data *sfd,
|
static int mptcp_get_subflow_data(struct mptcp_subflow_data *sfd,
|
||||||
char __user *optval, int __user *optlen)
|
char __user *optval,
|
||||||
|
int __user *optlen)
|
||||||
{
|
{
|
||||||
int len, copylen;
|
int len, copylen;
|
||||||
|
|
||||||
@@ -1162,6 +1164,125 @@ static int mptcp_getsockopt_subflow_addrs(struct mptcp_sock *msk, char __user *o
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int mptcp_get_full_info(struct mptcp_full_info *mfi,
|
||||||
|
char __user *optval,
|
||||||
|
int __user *optlen)
|
||||||
|
{
|
||||||
|
int len;
|
||||||
|
|
||||||
|
BUILD_BUG_ON(offsetof(struct mptcp_full_info, mptcp_info) !=
|
||||||
|
MIN_FULL_INFO_OPTLEN_SIZE);
|
||||||
|
|
||||||
|
if (get_user(len, optlen))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
if (len < MIN_FULL_INFO_OPTLEN_SIZE)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
memset(mfi, 0, sizeof(*mfi));
|
||||||
|
if (copy_from_user(mfi, optval, MIN_FULL_INFO_OPTLEN_SIZE))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
if (mfi->size_tcpinfo_kernel ||
|
||||||
|
mfi->size_sfinfo_kernel ||
|
||||||
|
mfi->num_subflows)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (mfi->size_sfinfo_user > INT_MAX ||
|
||||||
|
mfi->size_tcpinfo_user > INT_MAX)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
return len - MIN_FULL_INFO_OPTLEN_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mptcp_put_full_info(struct mptcp_full_info *mfi,
|
||||||
|
char __user *optval,
|
||||||
|
u32 copylen,
|
||||||
|
int __user *optlen)
|
||||||
|
{
|
||||||
|
copylen += MIN_FULL_INFO_OPTLEN_SIZE;
|
||||||
|
if (put_user(copylen, optlen))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
if (copy_to_user(optval, mfi, copylen))
|
||||||
|
return -EFAULT;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mptcp_getsockopt_full_info(struct mptcp_sock *msk, char __user *optval,
|
||||||
|
int __user *optlen)
|
||||||
|
{
|
||||||
|
unsigned int sfcount = 0, copylen = 0;
|
||||||
|
struct mptcp_subflow_context *subflow;
|
||||||
|
struct sock *sk = (struct sock *)msk;
|
||||||
|
void __user *tcpinfoptr, *sfinfoptr;
|
||||||
|
struct mptcp_full_info mfi;
|
||||||
|
int len;
|
||||||
|
|
||||||
|
len = mptcp_get_full_info(&mfi, optval, optlen);
|
||||||
|
if (len < 0)
|
||||||
|
return len;
|
||||||
|
|
||||||
|
/* don't bother filling the mptcp info if there is not enough
|
||||||
|
* user-space-provided storage
|
||||||
|
*/
|
||||||
|
if (len > 0) {
|
||||||
|
mptcp_diag_fill_info(msk, &mfi.mptcp_info);
|
||||||
|
copylen += min_t(unsigned int, len, sizeof(struct mptcp_info));
|
||||||
|
}
|
||||||
|
|
||||||
|
mfi.size_tcpinfo_kernel = sizeof(struct tcp_info);
|
||||||
|
mfi.size_tcpinfo_user = min_t(unsigned int, mfi.size_tcpinfo_user,
|
||||||
|
sizeof(struct tcp_info));
|
||||||
|
sfinfoptr = u64_to_user_ptr(mfi.subflow_info);
|
||||||
|
mfi.size_sfinfo_kernel = sizeof(struct mptcp_subflow_info);
|
||||||
|
mfi.size_sfinfo_user = min_t(unsigned int, mfi.size_sfinfo_user,
|
||||||
|
sizeof(struct mptcp_subflow_info));
|
||||||
|
tcpinfoptr = u64_to_user_ptr(mfi.tcp_info);
|
||||||
|
|
||||||
|
lock_sock(sk);
|
||||||
|
mptcp_for_each_subflow(msk, subflow) {
|
||||||
|
struct sock *ssk = mptcp_subflow_tcp_sock(subflow);
|
||||||
|
struct mptcp_subflow_info sfinfo;
|
||||||
|
struct tcp_info tcp_info;
|
||||||
|
|
||||||
|
if (sfcount++ >= mfi.size_arrays_user)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* fetch addr/tcp_info only if the user space buffers
|
||||||
|
* are wide enough
|
||||||
|
*/
|
||||||
|
memset(&sfinfo, 0, sizeof(sfinfo));
|
||||||
|
sfinfo.id = subflow->subflow_id;
|
||||||
|
if (mfi.size_sfinfo_user >
|
||||||
|
offsetof(struct mptcp_subflow_info, addrs))
|
||||||
|
mptcp_get_sub_addrs(ssk, &sfinfo.addrs);
|
||||||
|
if (copy_to_user(sfinfoptr, &sfinfo, mfi.size_sfinfo_user))
|
||||||
|
goto fail_release;
|
||||||
|
|
||||||
|
if (mfi.size_tcpinfo_user) {
|
||||||
|
tcp_get_info(ssk, &tcp_info);
|
||||||
|
if (copy_to_user(tcpinfoptr, &tcp_info,
|
||||||
|
mfi.size_tcpinfo_user))
|
||||||
|
goto fail_release;
|
||||||
|
}
|
||||||
|
|
||||||
|
tcpinfoptr += mfi.size_tcpinfo_user;
|
||||||
|
sfinfoptr += mfi.size_sfinfo_user;
|
||||||
|
}
|
||||||
|
release_sock(sk);
|
||||||
|
|
||||||
|
mfi.num_subflows = sfcount;
|
||||||
|
if (mptcp_put_full_info(&mfi, optval, copylen, optlen))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
fail_release:
|
||||||
|
release_sock(sk);
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
static int mptcp_put_int_option(struct mptcp_sock *msk, char __user *optval,
|
static int mptcp_put_int_option(struct mptcp_sock *msk, char __user *optval,
|
||||||
int __user *optlen, int val)
|
int __user *optlen, int val)
|
||||||
{
|
{
|
||||||
@@ -1235,6 +1356,8 @@ static int mptcp_getsockopt_sol_mptcp(struct mptcp_sock *msk, int optname,
|
|||||||
switch (optname) {
|
switch (optname) {
|
||||||
case MPTCP_INFO:
|
case MPTCP_INFO:
|
||||||
return mptcp_getsockopt_info(msk, optval, optlen);
|
return mptcp_getsockopt_info(msk, optval, optlen);
|
||||||
|
case MPTCP_FULL_INFO:
|
||||||
|
return mptcp_getsockopt_full_info(msk, optval, optlen);
|
||||||
case MPTCP_TCPINFO:
|
case MPTCP_TCPINFO:
|
||||||
return mptcp_getsockopt_tcpinfo(msk, optval, optlen);
|
return mptcp_getsockopt_tcpinfo(msk, optval, optlen);
|
||||||
case MPTCP_SUBFLOW_ADDRS:
|
case MPTCP_SUBFLOW_ADDRS:
|
||||||
|
Reference in New Issue
Block a user