Skip to content

Commit 06f15ce

Browse files
Florian Westphaldavem330
authored andcommitted
mptcp: add MPTCP_TCPINFO getsockopt support
Allow users to retrieve TCP_INFO data of all subflows. Users need to pre-initialize a meta header that has to be prepended to the data buffer that will be filled with the tcp info data. The meta header looks like this: struct mptcp_subflow_data { __u32 size_subflow_data;/* size of this structure in userspace */ __u32 num_subflows; /* must be 0, set by kernel */ __u32 size_kernel; /* must be 0, set by kernel */ __u32 size_user; /* size of one element in data[] */ } __attribute__((aligned(8))); size_subflow_data has to be set to 'sizeof(struct mptcp_subflow_data)'. This allows to extend mptcp_subflow_data structure later on without breaking backwards compatibility. If the structure is extended later on, kernel knows where the userspace-provided meta header ends, even if userspace uses an older (smaller) version of the structure. num_subflows must be set to 0. If the getsockopt request succeeds (return value is 0), it will be updated to contain the number of active subflows for the given logical connection. size_kernel must be set to 0. If the getsockopt request is successful, it will contain the size of the 'struct tcp_info' as known by the kernel. This is informational only. size_user must be set to 'sizeof(struct tcp_info)'. This allows the kernel to only fill in the space reserved/expected by userspace. Example: struct my_tcp_info { struct mptcp_subflow_data d; struct tcp_info ti[2]; }; struct my_tcp_info ti; socklen_t olen; memset(&ti, 0, sizeof(ti)); ti.d.size_subflow_data = sizeof(struct mptcp_subflow_data); ti.d.size_user = sizeof(struct tcp_info); olen = sizeof(ti); ret = getsockopt(fd, SOL_MPTCP, MPTCP_TCPINFO, &ti, &olen); if (ret < 0) die_perror("getsockopt MPTCP_TCPINFO"); mptcp_subflow_data.num_subflows is populated with the number of subflows that exist on the kernel side for the logical mptcp connection. This allows userspace to re-try with a larger tcp_info array if the number of subflows was larger than the available space in the ti[] array. olen has to be set to the number of bytes that userspace has allocated to receive the kernel data. It will be updated to contain the real number bytes that have been copied to by the kernel. In the above example, if the number if subflows was 1, olen is equal to 'sizeof(struct mptcp_subflow_data) + sizeof(struct tcp_info). For 2 or more subflows olen is equal to 'sizeof(struct my_tcp_info)'. If there was more data that could not be copied due to lack of space in the option buffer, userspace can detect this by checking mptcp_subflow_data->num_subflows. Signed-off-by: Florian Westphal <[email protected]> Signed-off-by: Mat Martineau <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 55c42fa commit 06f15ce

File tree

2 files changed

+124
-1
lines changed

2 files changed

+124
-1
lines changed

include/uapi/linux/mptcp.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,15 @@ enum mptcp_event_attr {
193193
#define MPTCP_RST_EBADPERF 5
194194
#define MPTCP_RST_EMIDDLEBOX 6
195195

196+
struct mptcp_subflow_data {
197+
__u32 size_subflow_data; /* size of this structure in userspace */
198+
__u32 num_subflows; /* must be 0, set by kernel */
199+
__u32 size_kernel; /* must be 0, set by kernel */
200+
__u32 size_user; /* size of one element in data[] */
201+
} __attribute__((aligned(8)));
202+
196203
/* MPTCP socket options */
197-
#define MPTCP_INFO 1
204+
#define MPTCP_INFO 1
205+
#define MPTCP_TCPINFO 2
198206

199207
#endif /* _UAPI_MPTCP_H */

net/mptcp/sockopt.c

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
#include <net/mptcp.h>
1515
#include "protocol.h"
1616

17+
#define MIN_INFO_OPTLEN_SIZE 16
18+
1719
static struct sock *__mptcp_tcp_fallback(struct mptcp_sock *msk)
1820
{
1921
sock_owned_by_me((const struct sock *)msk);
@@ -727,6 +729,117 @@ static int mptcp_getsockopt_info(struct mptcp_sock *msk, char __user *optval, in
727729
return 0;
728730
}
729731

732+
static int mptcp_put_subflow_data(struct mptcp_subflow_data *sfd,
733+
char __user *optval,
734+
u32 copied,
735+
int __user *optlen)
736+
{
737+
u32 copylen = min_t(u32, sfd->size_subflow_data, sizeof(*sfd));
738+
739+
if (copied)
740+
copied += sfd->size_subflow_data;
741+
else
742+
copied = copylen;
743+
744+
if (put_user(copied, optlen))
745+
return -EFAULT;
746+
747+
if (copy_to_user(optval, sfd, copylen))
748+
return -EFAULT;
749+
750+
return 0;
751+
}
752+
753+
static int mptcp_get_subflow_data(struct mptcp_subflow_data *sfd,
754+
char __user *optval, int __user *optlen)
755+
{
756+
int len, copylen;
757+
758+
if (get_user(len, optlen))
759+
return -EFAULT;
760+
761+
/* if mptcp_subflow_data size is changed, need to adjust
762+
* this function to deal with programs using old version.
763+
*/
764+
BUILD_BUG_ON(sizeof(*sfd) != MIN_INFO_OPTLEN_SIZE);
765+
766+
if (len < MIN_INFO_OPTLEN_SIZE)
767+
return -EINVAL;
768+
769+
memset(sfd, 0, sizeof(*sfd));
770+
771+
copylen = min_t(unsigned int, len, sizeof(*sfd));
772+
if (copy_from_user(sfd, optval, copylen))
773+
return -EFAULT;
774+
775+
/* size_subflow_data is u32, but len is signed */
776+
if (sfd->size_subflow_data > INT_MAX ||
777+
sfd->size_user > INT_MAX)
778+
return -EINVAL;
779+
780+
if (sfd->size_subflow_data < MIN_INFO_OPTLEN_SIZE ||
781+
sfd->size_subflow_data > len)
782+
return -EINVAL;
783+
784+
if (sfd->num_subflows || sfd->size_kernel)
785+
return -EINVAL;
786+
787+
return len - sfd->size_subflow_data;
788+
}
789+
790+
static int mptcp_getsockopt_tcpinfo(struct mptcp_sock *msk, char __user *optval,
791+
int __user *optlen)
792+
{
793+
struct mptcp_subflow_context *subflow;
794+
struct sock *sk = &msk->sk.icsk_inet.sk;
795+
unsigned int sfcount = 0, copied = 0;
796+
struct mptcp_subflow_data sfd;
797+
char __user *infoptr;
798+
int len;
799+
800+
len = mptcp_get_subflow_data(&sfd, optval, optlen);
801+
if (len < 0)
802+
return len;
803+
804+
sfd.size_kernel = sizeof(struct tcp_info);
805+
sfd.size_user = min_t(unsigned int, sfd.size_user,
806+
sizeof(struct tcp_info));
807+
808+
infoptr = optval + sfd.size_subflow_data;
809+
810+
lock_sock(sk);
811+
812+
mptcp_for_each_subflow(msk, subflow) {
813+
struct sock *ssk = mptcp_subflow_tcp_sock(subflow);
814+
815+
++sfcount;
816+
817+
if (len && len >= sfd.size_user) {
818+
struct tcp_info info;
819+
820+
tcp_get_info(ssk, &info);
821+
822+
if (copy_to_user(infoptr, &info, sfd.size_user)) {
823+
release_sock(sk);
824+
return -EFAULT;
825+
}
826+
827+
infoptr += sfd.size_user;
828+
copied += sfd.size_user;
829+
len -= sfd.size_user;
830+
}
831+
}
832+
833+
release_sock(sk);
834+
835+
sfd.num_subflows = sfcount;
836+
837+
if (mptcp_put_subflow_data(&sfd, optval, copied, optlen))
838+
return -EFAULT;
839+
840+
return 0;
841+
}
842+
730843
static int mptcp_getsockopt_sol_tcp(struct mptcp_sock *msk, int optname,
731844
char __user *optval, int __user *optlen)
732845
{
@@ -747,6 +860,8 @@ static int mptcp_getsockopt_sol_mptcp(struct mptcp_sock *msk, int optname,
747860
switch (optname) {
748861
case MPTCP_INFO:
749862
return mptcp_getsockopt_info(msk, optval, optlen);
863+
case MPTCP_TCPINFO:
864+
return mptcp_getsockopt_tcpinfo(msk, optval, optlen);
750865
}
751866

752867
return -EOPNOTSUPP;

0 commit comments

Comments
 (0)