MPTCP Linux Development
 help / color / mirror / Atom feed
From: Mat Martineau <mathew.j.martineau@linux.intel.com>
To: Florian Westphal <fw@strlen.de>
Cc: mptcp@lists.linux.dev
Subject: Re: Revised mptcp_info sockopt and selftests
Date: Sat, 31 Jul 2021 09:14:24 -0700 (PDT)	[thread overview]
Message-ID: <b6cfc070-a0e1-b57c-759c-f0a3406b4189@linux.intel.com> (raw)
In-Reply-To: <aa6fcfda-9b53-c258-e03c-6ce731697ff6@linux.intel.com>

On Fri, 30 Jul 2021, Mat Martineau wrote:

> On Thu, 29 Jul 2021, Florian Westphal wrote:
>
>> Hi,
>> 
>> I have reworked the MPTCP_INFO getsockopt.
>> 
>> I think that for now its better to focus on the UAPI rather than the
>> implementation.
>> 
>
> I agree - thanks for taking this approach.
>
>> Here is a minimal usage example to illustrate the feature:
>> 
>> UAPI structures:
>> struct mptcp_info_opt_ptr {
>> 	__aligned_u64	address;			/* userspace address 
>> */
>> 	__u32		nmemb_user;			/* number of elements 
>> in address[] */
>> 	__u32		size_user;			/* size of one 
>> element in address[] */
>> 	__u32		nmemb_kernel;			/* nmemb count kernel 
>> wanted to store */
>> 	__u32		size_kernel;			/* structure size in 
>> kernel */
>> };
>> 
>
> Will embedding multiple userspace addresses in this way be a problem for 
> security frameworks, in-kernel sockets, BPF_CGROUP_RUN_PROG_SETSOCKOPT, etc?
>
> While I like the flexibility this gives us, it does seem a lot more complex 
> than any other getsockopt I can think of. The similar thing that comes to 
> mind is iovecs, but that seems like a level of complexity that we might want 
> to avoid (?).
>
> What if the first field of mptcp_info_opt_ptr was an offset instead of an 
> address? We could pass in one block of memory and still have the same 
> flexibility. I'll elaborate more below.
>
>> struct mptcp_info_opt {
>> 	struct mptcp_info_opt_ptr mptcp_info;		/* struct mptcp_info 
>> */
>> 	struct mptcp_info_opt_ptr tcp_info;		/* struct tcp_info[] 
>> of each subflow */
>> 	struct mptcp_info_opt_ptr socknames;		/* struct 
>> sockaddr_storage[] */
>> 	struct mptcp_info_opt_ptr peernames;		/* struct 
>> sockaddr_storage[] */
>> };
>
> If we were to use offsets instead of addresses, mptcp_info_opt could be 
> extensible in the future by including a "num_ptrs" (or similar) field.
>
>> 
>> /* Sample call, error checks removed */
>> void test_info_getsockopt(int fd)
>> {
>> 	struct sockaddr_storage remotes[16], locals[16];
>> 	struct mptcp_info mptcp_info;
>> 	struct tcp_info tcpinfo[16];
>> 	struct mptcp_info_opt m;
>> 	socklen_t optlen = sizeof(m);
>> 	uint32_t i;
>> 	int err;
>>
>> 	memset(&m, 0, sizeof(m));
>> 	m.mptcp_info.size_user = sizeof(mptcp_info);
>> 	m.mptcp_info.address = (uint64_t)&mptcp_info;
>>
>> 	m.tcp_info.address = (uint64_t)tcpinfo;
>> 	m.tcp_info.size_user = sizeof(tcpinfo[0]);
>> 	m.tcp_info.nmemb_user = 16;
>>
>> 	m.socknames.size_user = sizeof(locals[0]);
>> 	m.socknames.nmemb_user = 16;
>> 	m.peernames.size_user = sizeof(remotes[0]);
>> 	m.peernames.nmemb_user = 16;
>> 	m.socknames.address = (uint64_t)locals;
>> 	m.peernames.address = (uint64_t)remotes;
>>
>> 	err = getsockopt(fd, SOL_MPTCP, MPTCP_INFO, &m, &optlen);
>> 
>
> With offsets, the above code could look something like:
>
> struct my_info_opt {
> 	struct mptcp_info_opt m; /* Has to be first */
> 	struct mptcp_info minfo;
> 	struct tcp_info tinfo[16];
> 	struct sockaddr_storage locals[16];
> 	struct sockaddr_storage remotes[16];
> } my_info;
>
> memset(&my_info, 0, sizeof(my_info));
> my_info.num_ptrs = 4;
>
> my_info.m.mptcp_info.offset = offsetof(my_info.minfo);

Oops, wrong syntax for offsetof(), but hopefully the intent was clear:

my_info.m.mptcp_info.offset = offsetof(struct my_info_opt, minfo);

> my_info.m.mptcp_info.size_user = sizeof(my_info.minfo);
>
> my_info.m.tcp_info.offset = offsetof(my_info.tinfo);

and offsetof(struct my_info_opt, tinfo)


-Mat

> /* size_user / nmemb_user same as above */
>
> /* and so on for locals and remotes */
>
> optlen = sizeof(my_info);
>
> err = getsockopt(fd, SOL_MPTCP, MPTCP_INFO, &my_info, &optlen);
>
>
> So, it's almost the same but uses one contiguous chunk of memory rather than 
> having to handle five different userspace addresses.
>
>
>> 	printf("have %u subflows, token is %x\n", mptcp_info.mptcpi_subflows, 
>> mptcp_info.mptcpi_token);
>>
>> 	for (i = 0; i < m.socknames.nmemb_user; i++) {
>> 		printf("subflow %02d: ", i);
>> 		fprintf_mptcp_info(stdout, (void *)&locals[i], (void 
>> *)&remotes[i]);
>> 		printf("\n");
>> 	}
>>
>> 	printf("... dumped %u of %u addresses.\n",
>> 			m.socknames.nmemb_user, m.socknames.nmemb_kernel);
>>
>> 	for (i = 0; i < m.tcp_info.nmemb_user; i++)
>> 		dump_tcp_info(&tcpinfo[i]);
>> }
>> 
>> As you can see, I removed 'byte' based sizes, i found this to be
>> unecessarily tedious as the kernel then has to subtract from a
>> 'remaining buffer size', while making sure that this can't underflow.
>> 
>> With number-of-elements, its enough to 'remaining--' and "ptr += 
>> size_user".
>> 
>> Userspace passes the address of the structure(s), the size of a single
>> structure that it expects to get and the number of structures it has
>> allocated (16 in above example).
>> 
>> The kernel will:
>> 1. not change, or shrink the number of structures.
>>   This means that, in above example, nmemb_user will be anywhere
>>   between 0 and 16 inclusive.
>>
>>   0 would happen in the 'no subflows' case, when nothing was filled in.
>> 2. Not change, or shrink, size_user.  This happens for instance
>>   if userspace has a larger 'struct tcp_info' than the kernel,
>>   in this case size_user is updated to reflect the real element size.
>> 
>> 3. update nmemb_kernel to reflect the structure size it *WOULD* have
>>   filled. For example, if userspace tcp_info struct is smaller than the
>>   kernel, then size_user < size_kernel.
>> 
>> 4. update size_kernel with the number of elements it WOULD have filled
>>   if there would have been more space.
>> 
>> So, in above example, if there are 17 subflows then
>> socknames.nmemb_user == 16 and socknames.nmemb_kernel == 17.
>> 
>> Or, in short:
>> The _kernel members are only filled by kernel, the value provided by
>> userspace are ignored.
>> 
>> The _user members are input/output: userspace tells kernel how much
>> space it provides, kernel updates it to reflect how much it ended up
>> using.
>> 
>> Is that sane?  Any suggestions?
>
> I think it's a very flexible approach, my main suggestion is the offset vs. 
> address thing above.
>
> Would it be frowned upon to put some inline helper functions or macros in the 
> mptcp.h UAPI to reduce the boilerplate code to populate the 
> mptcp_info_opt_ptrs? It looks like some UAPI headers have that, but not many.
>
>> 
>> On a related note, how should the test infra look like?
>> kunit isn't useable as i would prefer to test the UAPI, so that means
>> kselftest.
>> 
>> I would prefer not to extend mptcp_connect again.
>> Would you be ok with a new test program?
>
> Yes
>
>> 
>> Any preferences?  If not, i would go with mptcp_getsockopt.c +
>> mptcp_getsockopt.sh.
>> 
>
> +1 for those names.
>
>> The latter would then call the former with various possible
>> combinations, such as 'ipv4', 'ipv6', 'only ask for tcp_info', etc.
>> 
>
> Thanks for the proposal.
>
> --
> Mat Martineau
> Intel
>
>

--
Mat Martineau
Intel

  reply	other threads:[~2021-07-31 16:14 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-07-29 11:50 Revised mptcp_info sockopt and selftests Florian Westphal
2021-07-30 23:43 ` Mat Martineau
2021-07-31 16:14   ` Mat Martineau [this message]
2021-08-02 14:51   ` Florian Westphal
2021-08-02 22:23     ` Mat Martineau

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=b6cfc070-a0e1-b57c-759c-f0a3406b4189@linux.intel.com \
    --to=mathew.j.martineau@linux.intel.com \
    --cc=fw@strlen.de \
    --cc=mptcp@lists.linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox