* Re: [PATCH net v2 1/2] net: netem: fix backlog accounting for corrupted GSO frames
From: Cong Wang @ 2019-06-17 22:32 UTC (permalink / raw)
To: Jakub Kicinski
Cc: David Miller, Stephen Hemminger, Jamal Hadi Salim, Jiri Pirko,
netem, Linux Kernel Network Developers, oss-drivers, Eric Dumazet,
posk, Neil Horman
In-Reply-To: <20190617181111.5025-2-jakub.kicinski@netronome.com>
On Mon, Jun 17, 2019 at 11:11 AM Jakub Kicinski
<jakub.kicinski@netronome.com> wrote:
>
> When GSO frame has to be corrupted netem uses skb_gso_segment()
> to produce the list of frames, and re-enqueues the segments one
> by one. The backlog length has to be adjusted to account for
> new frames.
>
> The current calculation is incorrect, leading to wrong backlog
> lengths in the parent qdisc (both bytes and packets), and
> incorrect packet backlog count in netem itself.
>
> Parent backlog goes negative, netem's packet backlog counts
> all non-first segments twice (thus remaining non-zero even
> after qdisc is emptied).
>
> Move the variables used to count the adjustment into local
> scope to make 100% sure they aren't used at any stage in
> backports.
>
> Fixes: 6071bd1aa13e ("netem: Segment GSO packets on enqueue")
> Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com>
> Reviewed-by: Dirk van der Merwe <dirk.vandermerwe@netronome.com>
Looks good!
Acked-by: Cong Wang <xiyou.wangcong@gmail.com>
^ permalink raw reply
* Re: [PATCH net v2 2/2] net: netem: fix use after free and double free with packet corruption
From: Cong Wang @ 2019-06-17 22:33 UTC (permalink / raw)
To: Jakub Kicinski
Cc: David Miller, Stephen Hemminger, Jamal Hadi Salim, Jiri Pirko,
netem, Linux Kernel Network Developers, oss-drivers, Eric Dumazet,
posk, Neil Horman
In-Reply-To: <20190617181111.5025-3-jakub.kicinski@netronome.com>
On Mon, Jun 17, 2019 at 11:11 AM Jakub Kicinski
<jakub.kicinski@netronome.com> wrote:
>
> Brendan reports that the use of netem's packet corruption capability
> leads to strange crashes. This seems to be caused by
> commit d66280b12bd7 ("net: netem: use a list in addition to rbtree")
> which uses skb->next pointer to construct a fast-path queue of
> in-order skbs.
>
> Packet corruption code has to invoke skb_gso_segment() in case
> of skbs in need of GSO. skb_gso_segment() returns a list of
> skbs. If next pointers of the skbs on that list do not get cleared
> fast path list may point to freed skbs or skbs which are also on
> the RB tree.
>
> Let's say skb gets segmented into 3 frames:
>
> A -> B -> C
>
> A gets hooked to the t_head t_tail list by tfifo_enqueue(), but it's
> next pointer didn't get cleared so we have:
>
> h t
> |/
> A -> B -> C
>
> Now if B and C get also get enqueued successfully all is fine, because
> tfifo_enqueue() will overwrite the list in order. IOW:
>
> Enqueue B:
>
> h t
> | |
> A -> B C
>
> Enqueue C:
>
> h t
> | |
> A -> B -> C
>
> But if B and C get reordered we may end up with:
>
> h t RB tree
> |/ |
> A -> B -> C B
> \
> C
>
> Or if they get dropped just:
>
> h t
> |/
> A -> B -> C
>
> where A and B are already freed.
>
> To reproduce either limit has to be set low to cause freeing of
> segs or reorders have to happen (due to delay jitter).
>
> Note that we only have to mark the first segment as not on the
> list, "finish_segs" handling of other frags already does that.
>
> Another caveat is that qdisc_drop_all() still has to free all
> segments correctly in case of drop of first segment, therefore
> we re-link segs before calling it.
Acked-by: Cong Wang <xiyou.wangcong@gmail.com>
Thanks for the detailed description!
^ permalink raw reply
* [PATCH 0/3] Netfilter fixes for net
From: Pablo Neira Ayuso @ 2019-06-17 22:42 UTC (permalink / raw)
To: netfilter-devel; +Cc: davem, netdev
Hi David,
1) Module autoload for masquerade and redirection does not work.
2) Leak in unqueued packets in nf_ct_frag6_queue(). Ignore duplicated
fragments, pretend they are placed into the queue. Patches from
Guillaume Nault.
You can pull these changes from:
git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf.git
Thanks!
----------------------------------------------------------------
The following changes since commit 100f6d8e09905c59be45b6316f8f369c0be1b2d8:
net: correct zerocopy refcnt with udp MSG_MORE (2019-05-30 15:54:04 -0700)
are available in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf.git HEAD
for you to fetch changes up to 8a3dca632538c550930ce8bafa8c906b130d35cf:
netfilter: ipv6: nf_defrag: accept duplicate fragments again (2019-06-07 14:49:01 +0200)
----------------------------------------------------------------
Guillaume Nault (2):
netfilter: ipv6: nf_defrag: fix leakage of unqueued fragments
netfilter: ipv6: nf_defrag: accept duplicate fragments again
Pablo Neira Ayuso (1):
netfilter: nf_tables: fix module autoload with inet family
net/ipv6/netfilter/nf_conntrack_reasm.c | 22 ++++++++++++----------
net/netfilter/nft_masq.c | 3 +--
net/netfilter/nft_redir.c | 3 +--
3 files changed, 14 insertions(+), 14 deletions(-)
^ permalink raw reply
* [PATCH 1/3] netfilter: nf_tables: fix module autoload with inet family
From: Pablo Neira Ayuso @ 2019-06-17 22:42 UTC (permalink / raw)
To: netfilter-devel; +Cc: davem, netdev
In-Reply-To: <20190617224223.1004-1-pablo@netfilter.org>
Use MODULE_ALIAS_NFT_EXPR() to make happy the inet family with nat.
Fixes: 63ce3940f3ab ("netfilter: nft_redir: add inet support")
Fixes: 071657d2c38c ("netfilter: nft_masq: add inet support")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
net/netfilter/nft_masq.c | 3 +--
net/netfilter/nft_redir.c | 3 +--
2 files changed, 2 insertions(+), 4 deletions(-)
diff --git a/net/netfilter/nft_masq.c b/net/netfilter/nft_masq.c
index 86fd90085eaf..8c1612d6bc2c 100644
--- a/net/netfilter/nft_masq.c
+++ b/net/netfilter/nft_masq.c
@@ -307,5 +307,4 @@ module_exit(nft_masq_module_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Arturo Borrero Gonzalez <arturo@debian.org>");
-MODULE_ALIAS_NFT_AF_EXPR(AF_INET6, "masq");
-MODULE_ALIAS_NFT_AF_EXPR(AF_INET, "masq");
+MODULE_ALIAS_NFT_EXPR("masq");
diff --git a/net/netfilter/nft_redir.c b/net/netfilter/nft_redir.c
index da74fdc4a684..8787e9f8ed71 100644
--- a/net/netfilter/nft_redir.c
+++ b/net/netfilter/nft_redir.c
@@ -294,5 +294,4 @@ module_exit(nft_redir_module_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Arturo Borrero Gonzalez <arturo@debian.org>");
-MODULE_ALIAS_NFT_AF_EXPR(AF_INET, "redir");
-MODULE_ALIAS_NFT_AF_EXPR(AF_INET6, "redir");
+MODULE_ALIAS_NFT_EXPR("nat");
--
2.11.0
^ permalink raw reply related
* [PATCH 3/3] netfilter: ipv6: nf_defrag: accept duplicate fragments again
From: Pablo Neira Ayuso @ 2019-06-17 22:42 UTC (permalink / raw)
To: netfilter-devel; +Cc: davem, netdev
In-Reply-To: <20190617224223.1004-1-pablo@netfilter.org>
From: Guillaume Nault <gnault@redhat.com>
When fixing the skb leak introduced by the conversion to rbtree, I
forgot about the special case of duplicate fragments. The condition
under the 'insert_error' label isn't effective anymore as
nf_ct_frg6_gather() doesn't override the returned value anymore. So
duplicate fragments now get NF_DROP verdict.
To accept duplicate fragments again, handle them specially as soon as
inet_frag_queue_insert() reports them. Return -EINPROGRESS which will
translate to NF_STOLEN verdict, like any accepted fragment. However,
such packets don't carry any new information and aren't queued, so we
just drop them immediately.
Fixes: a0d56cb911ca ("netfilter: ipv6: nf_defrag: fix leakage of unqueued fragments")
Signed-off-by: Guillaume Nault <gnault@redhat.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
net/ipv6/netfilter/nf_conntrack_reasm.c | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c
index 5b3f65e29b6f..8951de8b568f 100644
--- a/net/ipv6/netfilter/nf_conntrack_reasm.c
+++ b/net/ipv6/netfilter/nf_conntrack_reasm.c
@@ -265,8 +265,14 @@ static int nf_ct_frag6_queue(struct frag_queue *fq, struct sk_buff *skb,
prev = fq->q.fragments_tail;
err = inet_frag_queue_insert(&fq->q, skb, offset, end);
- if (err)
+ if (err) {
+ if (err == IPFRAG_DUP) {
+ /* No error for duplicates, pretend they got queued. */
+ kfree_skb(skb);
+ return -EINPROGRESS;
+ }
goto insert_error;
+ }
if (dev)
fq->iif = dev->ifindex;
@@ -304,8 +310,6 @@ static int nf_ct_frag6_queue(struct frag_queue *fq, struct sk_buff *skb,
return -EINPROGRESS;
insert_error:
- if (err == IPFRAG_DUP)
- goto err;
inet_frag_kill(&fq->q);
err:
skb_dst_drop(skb);
--
2.11.0
^ permalink raw reply related
* Re: [PATCH] netfilter: nft_paylaod: add base type NFT_PAYLOAD_LL_HEADER_NO_TAG
From: Florian Westphal @ 2019-06-17 22:42 UTC (permalink / raw)
To: Pablo Neira Ayuso; +Cc: Florian Westphal, wenxu, netfilter-devel, netdev
In-Reply-To: <20190617223004.tnqz2bl7qp63fcfy@salvia>
Pablo Neira Ayuso <pablo@netfilter.org> wrote:
> > Subject: Change bridge l3 dependency to meta protocol
> >
> > This examines skb->protocol instead of ethernet header type, which
> > might be different when vlan is involved.
> >
> > + if (ctx->pctx.family == NFPROTO_BRIDGE && desc == &proto_eth) {
> > + if (expr->payload.desc == &proto_ip ||
> > + expr->payload.desc == &proto_ip6)
> > + desc = &proto_metaeth;
> > + }i
>
> Is this sufficient to restrict the matching? Is this still buggy from
> ingress?
This is what netdev family uses as well (skb->protocol i mean).
I'm not sure it will work for output however (haven't checked).
> I wonder if an explicit NFT_PAYLOAD_CHECK_VLAN flag would be useful in
> the kernel, if so we could rename NFTA_PAYLOAD_CSUM_FLAGS to
> NFTA_PAYLOAD_FLAGS and place it there. Just an idea.
What would NFT_PAYLOAD_CHECK_VLAN do?
You mean disable/enable the 'vlan is there' illusion that nft_payload
provides? That would work as well of course, but I would prefer to
switch to meta dependencies where possible so we don't rely on
particular layout of a different header class (e.g. meta l4proto doesn't
depend on ip version, and meta protocol won't depend on particular
ethernet frame).
What might be useful is an nft switch to turn off dependeny
insertion, this would also avoid the problem (if users restrict the
matching properly...).
Another unresolved issue is presence of multiple vlan tags, so we might
have to add yet another meta key to retrieve the l3 protocol in use
(the problem at hand was 'ip protocol icmp' not matching traffic inside
a vlan).
The other issue is lack of vlan awareness in some bridge/netdev
expressions, e.g. reject.
I think we could apply this patch to nft after making sure it works
for output as thats probably the only solution that won't need
changes in the kernel.
If it doesn't, we will need to find a different solution in any case.
^ permalink raw reply
* [PATCH 2/3] netfilter: ipv6: nf_defrag: fix leakage of unqueued fragments
From: Pablo Neira Ayuso @ 2019-06-17 22:42 UTC (permalink / raw)
To: netfilter-devel; +Cc: davem, netdev
In-Reply-To: <20190617224223.1004-1-pablo@netfilter.org>
From: Guillaume Nault <gnault@redhat.com>
With commit 997dd9647164 ("net: IP6 defrag: use rbtrees in
nf_conntrack_reasm.c"), nf_ct_frag6_reasm() is now called from
nf_ct_frag6_queue(). With this change, nf_ct_frag6_queue() can fail
after the skb has been added to the fragment queue and
nf_ct_frag6_gather() was adapted to handle this case.
But nf_ct_frag6_queue() can still fail before the fragment has been
queued. nf_ct_frag6_gather() can't handle this case anymore, because it
has no way to know if nf_ct_frag6_queue() queued the fragment before
failing. If it didn't, the skb is lost as the error code is overwritten
with -EINPROGRESS.
Fix this by setting -EINPROGRESS directly in nf_ct_frag6_queue(), so
that nf_ct_frag6_gather() can propagate the error as is.
Fixes: 997dd9647164 ("net: IP6 defrag: use rbtrees in nf_conntrack_reasm.c")
Signed-off-by: Guillaume Nault <gnault@redhat.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
net/ipv6/netfilter/nf_conntrack_reasm.c | 12 +++++-------
1 file changed, 5 insertions(+), 7 deletions(-)
diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c
index 3de0e9b0a482..5b3f65e29b6f 100644
--- a/net/ipv6/netfilter/nf_conntrack_reasm.c
+++ b/net/ipv6/netfilter/nf_conntrack_reasm.c
@@ -293,7 +293,11 @@ static int nf_ct_frag6_queue(struct frag_queue *fq, struct sk_buff *skb,
skb->_skb_refdst = 0UL;
err = nf_ct_frag6_reasm(fq, skb, prev, dev);
skb->_skb_refdst = orefdst;
- return err;
+
+ /* After queue has assumed skb ownership, only 0 or
+ * -EINPROGRESS must be returned.
+ */
+ return err ? -EINPROGRESS : 0;
}
skb_dst_drop(skb);
@@ -480,12 +484,6 @@ int nf_ct_frag6_gather(struct net *net, struct sk_buff *skb, u32 user)
ret = 0;
}
- /* after queue has assumed skb ownership, only 0 or -EINPROGRESS
- * must be returned.
- */
- if (ret)
- ret = -EINPROGRESS;
-
spin_unlock_bh(&fq->q.lock);
inet_frag_put(&fq->q);
return ret;
--
2.11.0
^ permalink raw reply related
* Re: [PATCH v3 2/7] lib/hexdump.c: Relax rowsize checks in hex_dump_to_buffer
From: Randy Dunlap @ 2019-06-17 22:47 UTC (permalink / raw)
To: Alastair D'Silva, alastair
Cc: Jani Nikula, Joonas Lahtinen, Rodrigo Vivi, David Airlie,
Daniel Vetter, Dan Carpenter, Karsten Keil, Jassi Brar,
Tom Lendacky, David S. Miller, Jose Abreu, Kalle Valo,
Stanislaw Gruszka, Benson Leung, Enric Balletbo i Serra,
James E.J. Bottomley, Martin K. Petersen, Greg Kroah-Hartman,
Alexander Viro, Petr Mladek, Sergey Senozhatsky, Steven Rostedt,
David Laight, Andrew Morton, intel-gfx, dri-devel, linux-kernel,
netdev, ath10k, linux-wireless, linux-scsi, linux-fbdev, devel,
linux-fsdevel
In-Reply-To: <20190617020430.8708-3-alastair@au1.ibm.com>
Hi,
Just a comment style nit below...
On 6/16/19 7:04 PM, Alastair D'Silva wrote:
> From: Alastair D'Silva <alastair@d-silva.org>
>
> This patch removes the hardcoded row limits and allows for
> other lengths. These lengths must still be a multiple of
> groupsize.
>
> This allows structs that are not 16/32 bytes to display on
> a single line.
>
> This patch also expands the self-tests to test row sizes
> up to 64 bytes (though they can now be arbitrarily long).
>
> Signed-off-by: Alastair D'Silva <alastair@d-silva.org>
> ---
> lib/hexdump.c | 48 ++++++++++++++++++++++++++++--------------
> lib/test_hexdump.c | 52 ++++++++++++++++++++++++++++++++++++++--------
> 2 files changed, 75 insertions(+), 25 deletions(-)
>
> diff --git a/lib/hexdump.c b/lib/hexdump.c
> index 81b70ed37209..3943507bc0e9 100644
> --- a/lib/hexdump.c
> +++ b/lib/hexdump.c
> @@ -246,17 +248,29 @@ void print_hex_dump(const char *level, const char *prefix_str, int prefix_type,
> {
> const u8 *ptr = buf;
> int i, linelen, remaining = len;
> - unsigned char linebuf[32 * 3 + 2 + 32 + 1];
> + unsigned char *linebuf;
> + unsigned int linebuf_len;
>
> - if (rowsize != 16 && rowsize != 32)
> - rowsize = 16;
> + if (rowsize % groupsize)
> + rowsize -= rowsize % groupsize;
> +
> + /* Worst case line length:
> + * 2 hex chars + space per byte in, 2 spaces, 1 char per byte in, NULL
> + */
According to Documentation/process/coding-style.rst:
The preferred style for long (multi-line) comments is:
.. code-block:: c
/*
* This is the preferred style for multi-line
* comments in the Linux kernel source code.
* Please use it consistently.
*
* Description: A column of asterisks on the left side,
* with beginning and ending almost-blank lines.
*/
except in networking software.
> + linebuf_len = rowsize * 3 + 2 + rowsize + 1;
> + linebuf = kzalloc(linebuf_len, GFP_KERNEL);
> + if (!linebuf) {
> + printk("%s%shexdump: Could not alloc %u bytes for buffer\n",
> + level, prefix_str, linebuf_len);
> + return;
> + }
--
~Randy
^ permalink raw reply
* [PATCH bpf-next] libbpf: constify getter APIs
From: Andrii Nakryiko @ 2019-06-17 22:48 UTC (permalink / raw)
To: andrii.nakryiko, ast, daniel, bpf, netdev, kernel-team; +Cc: Andrii Nakryiko
Add const qualifiers to bpf_object/bpf_program/bpf_map arguments for
getter APIs. There is no need for them to not be const pointers.
Verified that
make -C tools/lib/bpf
make -C tools/testing/selftests/bpf
make -C tools/perf
all build without warnings.
Signed-off-by: Andrii Nakryiko <andriin@fb.com>
---
tools/lib/bpf/libbpf.c | 80 ++++++++++++++++++++++--------------------
tools/lib/bpf/libbpf.h | 62 ++++++++++++++++----------------
2 files changed, 72 insertions(+), 70 deletions(-)
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index e725fa86b189..fbf2c6bee573 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -1275,7 +1275,8 @@ bpf_object__find_prog_by_idx(struct bpf_object *obj, int idx)
}
struct bpf_program *
-bpf_object__find_program_by_title(struct bpf_object *obj, const char *title)
+bpf_object__find_program_by_title(const struct bpf_object *obj,
+ const char *title)
{
struct bpf_program *pos;
@@ -2192,8 +2193,8 @@ bpf_program__load(struct bpf_program *prog,
return err;
}
-static bool bpf_program__is_function_storage(struct bpf_program *prog,
- struct bpf_object *obj)
+static bool bpf_program__is_function_storage(const struct bpf_program *prog,
+ const struct bpf_object *obj)
{
return prog->idx == obj->efile.text_shndx && obj->has_pseudo_calls;
}
@@ -2899,17 +2900,17 @@ bpf_object__next(struct bpf_object *prev)
return next;
}
-const char *bpf_object__name(struct bpf_object *obj)
+const char *bpf_object__name(const struct bpf_object *obj)
{
return obj ? obj->path : ERR_PTR(-EINVAL);
}
-unsigned int bpf_object__kversion(struct bpf_object *obj)
+unsigned int bpf_object__kversion(const struct bpf_object *obj)
{
return obj ? obj->kern_version : 0;
}
-struct btf *bpf_object__btf(struct bpf_object *obj)
+struct btf *bpf_object__btf(const struct bpf_object *obj)
{
return obj ? obj->btf : NULL;
}
@@ -2930,13 +2931,14 @@ int bpf_object__set_priv(struct bpf_object *obj, void *priv,
return 0;
}
-void *bpf_object__priv(struct bpf_object *obj)
+void *bpf_object__priv(const struct bpf_object *obj)
{
return obj ? obj->priv : ERR_PTR(-EINVAL);
}
static struct bpf_program *
-__bpf_program__iter(struct bpf_program *p, struct bpf_object *obj, bool forward)
+__bpf_program__iter(const struct bpf_program *p, const struct bpf_object *obj,
+ bool forward)
{
size_t nr_programs = obj->nr_programs;
ssize_t idx;
@@ -2961,7 +2963,7 @@ __bpf_program__iter(struct bpf_program *p, struct bpf_object *obj, bool forward)
}
struct bpf_program *
-bpf_program__next(struct bpf_program *prev, struct bpf_object *obj)
+bpf_program__next(struct bpf_program *prev, const struct bpf_object *obj)
{
struct bpf_program *prog = prev;
@@ -2973,7 +2975,7 @@ bpf_program__next(struct bpf_program *prev, struct bpf_object *obj)
}
struct bpf_program *
-bpf_program__prev(struct bpf_program *next, struct bpf_object *obj)
+bpf_program__prev(struct bpf_program *next, const struct bpf_object *obj)
{
struct bpf_program *prog = next;
@@ -2995,7 +2997,7 @@ int bpf_program__set_priv(struct bpf_program *prog, void *priv,
return 0;
}
-void *bpf_program__priv(struct bpf_program *prog)
+void *bpf_program__priv(const struct bpf_program *prog)
{
return prog ? prog->priv : ERR_PTR(-EINVAL);
}
@@ -3005,7 +3007,7 @@ void bpf_program__set_ifindex(struct bpf_program *prog, __u32 ifindex)
prog->prog_ifindex = ifindex;
}
-const char *bpf_program__title(struct bpf_program *prog, bool needs_copy)
+const char *bpf_program__title(const struct bpf_program *prog, bool needs_copy)
{
const char *title;
@@ -3021,7 +3023,7 @@ const char *bpf_program__title(struct bpf_program *prog, bool needs_copy)
return title;
}
-int bpf_program__fd(struct bpf_program *prog)
+int bpf_program__fd(const struct bpf_program *prog)
{
return bpf_program__nth_fd(prog, 0);
}
@@ -3054,7 +3056,7 @@ int bpf_program__set_prep(struct bpf_program *prog, int nr_instances,
return 0;
}
-int bpf_program__nth_fd(struct bpf_program *prog, int n)
+int bpf_program__nth_fd(const struct bpf_program *prog, int n)
{
int fd;
@@ -3082,25 +3084,25 @@ void bpf_program__set_type(struct bpf_program *prog, enum bpf_prog_type type)
prog->type = type;
}
-static bool bpf_program__is_type(struct bpf_program *prog,
+static bool bpf_program__is_type(const struct bpf_program *prog,
enum bpf_prog_type type)
{
return prog ? (prog->type == type) : false;
}
-#define BPF_PROG_TYPE_FNS(NAME, TYPE) \
-int bpf_program__set_##NAME(struct bpf_program *prog) \
-{ \
- if (!prog) \
- return -EINVAL; \
- bpf_program__set_type(prog, TYPE); \
- return 0; \
-} \
- \
-bool bpf_program__is_##NAME(struct bpf_program *prog) \
-{ \
- return bpf_program__is_type(prog, TYPE); \
-} \
+#define BPF_PROG_TYPE_FNS(NAME, TYPE) \
+int bpf_program__set_##NAME(struct bpf_program *prog) \
+{ \
+ if (!prog) \
+ return -EINVAL; \
+ bpf_program__set_type(prog, TYPE); \
+ return 0; \
+} \
+ \
+bool bpf_program__is_##NAME(const struct bpf_program *prog) \
+{ \
+ return bpf_program__is_type(prog, TYPE); \
+} \
BPF_PROG_TYPE_FNS(socket_filter, BPF_PROG_TYPE_SOCKET_FILTER);
BPF_PROG_TYPE_FNS(kprobe, BPF_PROG_TYPE_KPROBE);
@@ -3295,17 +3297,17 @@ bpf_program__identify_section(struct bpf_program *prog,
expected_attach_type);
}
-int bpf_map__fd(struct bpf_map *map)
+int bpf_map__fd(const struct bpf_map *map)
{
return map ? map->fd : -EINVAL;
}
-const struct bpf_map_def *bpf_map__def(struct bpf_map *map)
+const struct bpf_map_def *bpf_map__def(const struct bpf_map *map)
{
return map ? &map->def : ERR_PTR(-EINVAL);
}
-const char *bpf_map__name(struct bpf_map *map)
+const char *bpf_map__name(const struct bpf_map *map)
{
return map ? map->name : NULL;
}
@@ -3336,17 +3338,17 @@ int bpf_map__set_priv(struct bpf_map *map, void *priv,
return 0;
}
-void *bpf_map__priv(struct bpf_map *map)
+void *bpf_map__priv(const struct bpf_map *map)
{
return map ? map->priv : ERR_PTR(-EINVAL);
}
-bool bpf_map__is_offload_neutral(struct bpf_map *map)
+bool bpf_map__is_offload_neutral(const struct bpf_map *map)
{
return map->def.type == BPF_MAP_TYPE_PERF_EVENT_ARRAY;
}
-bool bpf_map__is_internal(struct bpf_map *map)
+bool bpf_map__is_internal(const struct bpf_map *map)
{
return map->libbpf_type != LIBBPF_MAP_UNSPEC;
}
@@ -3371,7 +3373,7 @@ int bpf_map__set_inner_map_fd(struct bpf_map *map, int fd)
}
static struct bpf_map *
-__bpf_map__iter(struct bpf_map *m, struct bpf_object *obj, int i)
+__bpf_map__iter(const struct bpf_map *m, const struct bpf_object *obj, int i)
{
ssize_t idx;
struct bpf_map *s, *e;
@@ -3395,7 +3397,7 @@ __bpf_map__iter(struct bpf_map *m, struct bpf_object *obj, int i)
}
struct bpf_map *
-bpf_map__next(struct bpf_map *prev, struct bpf_object *obj)
+bpf_map__next(const struct bpf_map *prev, const struct bpf_object *obj)
{
if (prev == NULL)
return obj->maps;
@@ -3404,7 +3406,7 @@ bpf_map__next(struct bpf_map *prev, struct bpf_object *obj)
}
struct bpf_map *
-bpf_map__prev(struct bpf_map *next, struct bpf_object *obj)
+bpf_map__prev(const struct bpf_map *next, const struct bpf_object *obj)
{
if (next == NULL) {
if (!obj->nr_maps)
@@ -3416,7 +3418,7 @@ bpf_map__prev(struct bpf_map *next, struct bpf_object *obj)
}
struct bpf_map *
-bpf_object__find_map_by_name(struct bpf_object *obj, const char *name)
+bpf_object__find_map_by_name(const struct bpf_object *obj, const char *name)
{
struct bpf_map *pos;
@@ -3428,7 +3430,7 @@ bpf_object__find_map_by_name(struct bpf_object *obj, const char *name)
}
int
-bpf_object__find_map_fd_by_name(struct bpf_object *obj, const char *name)
+bpf_object__find_map_fd_by_name(const struct bpf_object *obj, const char *name)
{
return bpf_map__fd(bpf_object__find_map_by_name(obj, name));
}
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index 2e594a0fa961..d639f47e3110 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -98,15 +98,16 @@ struct bpf_object_load_attr {
LIBBPF_API int bpf_object__load(struct bpf_object *obj);
LIBBPF_API int bpf_object__load_xattr(struct bpf_object_load_attr *attr);
LIBBPF_API int bpf_object__unload(struct bpf_object *obj);
-LIBBPF_API const char *bpf_object__name(struct bpf_object *obj);
-LIBBPF_API unsigned int bpf_object__kversion(struct bpf_object *obj);
+LIBBPF_API const char *bpf_object__name(const struct bpf_object *obj);
+LIBBPF_API unsigned int bpf_object__kversion(const struct bpf_object *obj);
struct btf;
-LIBBPF_API struct btf *bpf_object__btf(struct bpf_object *obj);
+LIBBPF_API struct btf *bpf_object__btf(const struct bpf_object *obj);
LIBBPF_API int bpf_object__btf_fd(const struct bpf_object *obj);
LIBBPF_API struct bpf_program *
-bpf_object__find_program_by_title(struct bpf_object *obj, const char *title);
+bpf_object__find_program_by_title(const struct bpf_object *obj,
+ const char *title);
LIBBPF_API struct bpf_object *bpf_object__next(struct bpf_object *prev);
#define bpf_object__for_each_safe(pos, tmp) \
@@ -118,7 +119,7 @@ LIBBPF_API struct bpf_object *bpf_object__next(struct bpf_object *prev);
typedef void (*bpf_object_clear_priv_t)(struct bpf_object *, void *);
LIBBPF_API int bpf_object__set_priv(struct bpf_object *obj, void *priv,
bpf_object_clear_priv_t clear_priv);
-LIBBPF_API void *bpf_object__priv(struct bpf_object *prog);
+LIBBPF_API void *bpf_object__priv(const struct bpf_object *prog);
LIBBPF_API int
libbpf_prog_type_by_name(const char *name, enum bpf_prog_type *prog_type,
@@ -129,7 +130,7 @@ LIBBPF_API int libbpf_attach_type_by_name(const char *name,
/* Accessors of bpf_program */
struct bpf_program;
LIBBPF_API struct bpf_program *bpf_program__next(struct bpf_program *prog,
- struct bpf_object *obj);
+ const struct bpf_object *obj);
#define bpf_object__for_each_program(pos, obj) \
for ((pos) = bpf_program__next(NULL, (obj)); \
@@ -137,24 +138,23 @@ LIBBPF_API struct bpf_program *bpf_program__next(struct bpf_program *prog,
(pos) = bpf_program__next((pos), (obj)))
LIBBPF_API struct bpf_program *bpf_program__prev(struct bpf_program *prog,
- struct bpf_object *obj);
+ const struct bpf_object *obj);
-typedef void (*bpf_program_clear_priv_t)(struct bpf_program *,
- void *);
+typedef void (*bpf_program_clear_priv_t)(struct bpf_program *, void *);
LIBBPF_API int bpf_program__set_priv(struct bpf_program *prog, void *priv,
bpf_program_clear_priv_t clear_priv);
-LIBBPF_API void *bpf_program__priv(struct bpf_program *prog);
+LIBBPF_API void *bpf_program__priv(const struct bpf_program *prog);
LIBBPF_API void bpf_program__set_ifindex(struct bpf_program *prog,
__u32 ifindex);
-LIBBPF_API const char *bpf_program__title(struct bpf_program *prog,
+LIBBPF_API const char *bpf_program__title(const struct bpf_program *prog,
bool needs_copy);
LIBBPF_API int bpf_program__load(struct bpf_program *prog, char *license,
__u32 kern_version);
-LIBBPF_API int bpf_program__fd(struct bpf_program *prog);
+LIBBPF_API int bpf_program__fd(const struct bpf_program *prog);
LIBBPF_API int bpf_program__pin_instance(struct bpf_program *prog,
const char *path,
int instance);
@@ -227,7 +227,7 @@ typedef int (*bpf_program_prep_t)(struct bpf_program *prog, int n,
LIBBPF_API int bpf_program__set_prep(struct bpf_program *prog, int nr_instance,
bpf_program_prep_t prep);
-LIBBPF_API int bpf_program__nth_fd(struct bpf_program *prog, int n);
+LIBBPF_API int bpf_program__nth_fd(const struct bpf_program *prog, int n);
/*
* Adjust type of BPF program. Default is kprobe.
@@ -246,14 +246,14 @@ LIBBPF_API void
bpf_program__set_expected_attach_type(struct bpf_program *prog,
enum bpf_attach_type type);
-LIBBPF_API bool bpf_program__is_socket_filter(struct bpf_program *prog);
-LIBBPF_API bool bpf_program__is_tracepoint(struct bpf_program *prog);
-LIBBPF_API bool bpf_program__is_raw_tracepoint(struct bpf_program *prog);
-LIBBPF_API bool bpf_program__is_kprobe(struct bpf_program *prog);
-LIBBPF_API bool bpf_program__is_sched_cls(struct bpf_program *prog);
-LIBBPF_API bool bpf_program__is_sched_act(struct bpf_program *prog);
-LIBBPF_API bool bpf_program__is_xdp(struct bpf_program *prog);
-LIBBPF_API bool bpf_program__is_perf_event(struct bpf_program *prog);
+LIBBPF_API bool bpf_program__is_socket_filter(const struct bpf_program *prog);
+LIBBPF_API bool bpf_program__is_tracepoint(const struct bpf_program *prog);
+LIBBPF_API bool bpf_program__is_raw_tracepoint(const struct bpf_program *prog);
+LIBBPF_API bool bpf_program__is_kprobe(const struct bpf_program *prog);
+LIBBPF_API bool bpf_program__is_sched_cls(const struct bpf_program *prog);
+LIBBPF_API bool bpf_program__is_sched_act(const struct bpf_program *prog);
+LIBBPF_API bool bpf_program__is_xdp(const struct bpf_program *prog);
+LIBBPF_API bool bpf_program__is_perf_event(const struct bpf_program *prog);
/*
* No need for __attribute__((packed)), all members of 'bpf_map_def'
@@ -275,10 +275,10 @@ struct bpf_map_def {
*/
struct bpf_map;
LIBBPF_API struct bpf_map *
-bpf_object__find_map_by_name(struct bpf_object *obj, const char *name);
+bpf_object__find_map_by_name(const struct bpf_object *obj, const char *name);
LIBBPF_API int
-bpf_object__find_map_fd_by_name(struct bpf_object *obj, const char *name);
+bpf_object__find_map_fd_by_name(const struct bpf_object *obj, const char *name);
/*
* Get bpf_map through the offset of corresponding struct bpf_map_def
@@ -288,7 +288,7 @@ LIBBPF_API struct bpf_map *
bpf_object__find_map_by_offset(struct bpf_object *obj, size_t offset);
LIBBPF_API struct bpf_map *
-bpf_map__next(struct bpf_map *map, struct bpf_object *obj);
+bpf_map__next(const struct bpf_map *map, const struct bpf_object *obj);
#define bpf_object__for_each_map(pos, obj) \
for ((pos) = bpf_map__next(NULL, (obj)); \
(pos) != NULL; \
@@ -296,22 +296,22 @@ bpf_map__next(struct bpf_map *map, struct bpf_object *obj);
#define bpf_map__for_each bpf_object__for_each_map
LIBBPF_API struct bpf_map *
-bpf_map__prev(struct bpf_map *map, struct bpf_object *obj);
+bpf_map__prev(const struct bpf_map *map, const struct bpf_object *obj);
-LIBBPF_API int bpf_map__fd(struct bpf_map *map);
-LIBBPF_API const struct bpf_map_def *bpf_map__def(struct bpf_map *map);
-LIBBPF_API const char *bpf_map__name(struct bpf_map *map);
+LIBBPF_API int bpf_map__fd(const struct bpf_map *map);
+LIBBPF_API const struct bpf_map_def *bpf_map__def(const struct bpf_map *map);
+LIBBPF_API const char *bpf_map__name(const struct bpf_map *map);
LIBBPF_API __u32 bpf_map__btf_key_type_id(const struct bpf_map *map);
LIBBPF_API __u32 bpf_map__btf_value_type_id(const struct bpf_map *map);
typedef void (*bpf_map_clear_priv_t)(struct bpf_map *, void *);
LIBBPF_API int bpf_map__set_priv(struct bpf_map *map, void *priv,
bpf_map_clear_priv_t clear_priv);
-LIBBPF_API void *bpf_map__priv(struct bpf_map *map);
+LIBBPF_API void *bpf_map__priv(const struct bpf_map *map);
LIBBPF_API int bpf_map__reuse_fd(struct bpf_map *map, int fd);
LIBBPF_API int bpf_map__resize(struct bpf_map *map, __u32 max_entries);
-LIBBPF_API bool bpf_map__is_offload_neutral(struct bpf_map *map);
-LIBBPF_API bool bpf_map__is_internal(struct bpf_map *map);
+LIBBPF_API bool bpf_map__is_offload_neutral(const struct bpf_map *map);
+LIBBPF_API bool bpf_map__is_internal(const struct bpf_map *map);
LIBBPF_API void bpf_map__set_ifindex(struct bpf_map *map, __u32 ifindex);
LIBBPF_API int bpf_map__pin(struct bpf_map *map, const char *path);
LIBBPF_API int bpf_map__unpin(struct bpf_map *map, const char *path);
--
2.17.1
^ permalink raw reply related
* Re: [PATCH net-next 4/4] qed: Add devlink support for configuration attributes.
From: Jakub Kicinski @ 2019-06-17 22:54 UTC (permalink / raw)
To: Sudarsana Reddy Kalluru; +Cc: davem, netdev, mkalderon, aelior, Jiri Pirko
In-Reply-To: <20190617114528.17086-5-skalluru@marvell.com>
On Mon, 17 Jun 2019 04:45:28 -0700, Sudarsana Reddy Kalluru wrote:
> This patch adds implementation for devlink callbacks for reading/
> configuring the device attributes.
>
> Signed-off-by: Sudarsana Reddy Kalluru <skalluru@marvell.com>
> Signed-off-by: Ariel Elior <aelior@marvell.com>
You need to provide documentation for your parameters, plus some of
them look like they should potentially be port params, not device
params.
^ permalink raw reply
* [RFC PATCH net-next 02/33] tcp: Define IPPROTO_MPTCP
From: Mat Martineau @ 2019-06-17 22:57 UTC (permalink / raw)
To: edumazet, netdev
Cc: Mat Martineau, cpaasch, fw, pabeni, peter.krystad, dcaratti,
matthieu.baerts
In-Reply-To: <20190617225808.665-1-mathew.j.martineau@linux.intel.com>
To open a MPTCP socket with socket(AF_INET, SOCK_STREAM, IPPROTO_MPTCP),
IPPROTO_MPTCP needs a value that differs from IPPROTO_TCP. The existing
IPPROTO numbers mostly map directly to IANA-specified protocol numbers.
MPTCP does not have a protocol number allocated because MPTCP packets
use the TCP protocol number. Use private number not used OTA.
Signed-off-by: Mat Martineau <mathew.j.martineau@linux.intel.com>
---
include/uapi/linux/in.h | 2 ++
tools/include/uapi/linux/in.h | 2 ++
2 files changed, 4 insertions(+)
diff --git a/include/uapi/linux/in.h b/include/uapi/linux/in.h
index e7ad9d350a28..44df6dc1ff1d 100644
--- a/include/uapi/linux/in.h
+++ b/include/uapi/linux/in.h
@@ -76,6 +76,8 @@ enum {
#define IPPROTO_MPLS IPPROTO_MPLS
IPPROTO_RAW = 255, /* Raw IP packets */
#define IPPROTO_RAW IPPROTO_RAW
+ IPPROTO_MPTCP = 262, /* Multipath TCP connection */
+#define IPPROTO_MPTCP IPPROTO_MPTCP
IPPROTO_MAX
};
#endif
diff --git a/tools/include/uapi/linux/in.h b/tools/include/uapi/linux/in.h
index e7ad9d350a28..44df6dc1ff1d 100644
--- a/tools/include/uapi/linux/in.h
+++ b/tools/include/uapi/linux/in.h
@@ -76,6 +76,8 @@ enum {
#define IPPROTO_MPLS IPPROTO_MPLS
IPPROTO_RAW = 255, /* Raw IP packets */
#define IPPROTO_RAW IPPROTO_RAW
+ IPPROTO_MPTCP = 262, /* Multipath TCP connection */
+#define IPPROTO_MPTCP IPPROTO_MPTCP
IPPROTO_MAX
};
#endif
--
2.22.0
^ permalink raw reply related
* [RFC PATCH net-next 03/33] mptcp: Add MPTCP socket stubs
From: Mat Martineau @ 2019-06-17 22:57 UTC (permalink / raw)
To: edumazet, netdev
Cc: Mat Martineau, cpaasch, fw, pabeni, peter.krystad, dcaratti,
matthieu.baerts
In-Reply-To: <20190617225808.665-1-mathew.j.martineau@linux.intel.com>
Implements the infrastructure for MPTCP sockets.
MPTCP sockets open one in-kernel TCP socket per subflow. These subflow
sockets are only managed by the MPTCP socket that owns them and are not
visible from userspace. This commit allows a userspace program to open
an MPTCP socket with:
sock = socket(AF_INET, SOCK_STREAM, IPPROTO_MPTCP);
The resulting socket is simply a wrapper around a single regular TCP
socket, without any of the MPTCP protocol implemented over the wire.
Signed-off-by: Mat Martineau <mathew.j.martineau@linux.intel.com>
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Peter Krystad <peter.krystad@linux.intel.com>
Signed-off-by: Matthieu Baerts <matthieu.baerts@tessares.net>
---
include/net/mptcp.h | 22 ++++++++
net/Kconfig | 1 +
net/Makefile | 1 +
net/ipv4/tcp.c | 2 +
net/mptcp/Kconfig | 10 ++++
net/mptcp/Makefile | 4 ++
net/mptcp/protocol.c | 118 +++++++++++++++++++++++++++++++++++++++++++
net/mptcp/protocol.h | 22 ++++++++
8 files changed, 180 insertions(+)
create mode 100644 include/net/mptcp.h
create mode 100644 net/mptcp/Kconfig
create mode 100644 net/mptcp/Makefile
create mode 100644 net/mptcp/protocol.c
create mode 100644 net/mptcp/protocol.h
diff --git a/include/net/mptcp.h b/include/net/mptcp.h
new file mode 100644
index 000000000000..0fe78fddc638
--- /dev/null
+++ b/include/net/mptcp.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Multipath TCP
+ *
+ * Copyright (c) 2017 - 2019, Intel Corporation.
+ */
+
+#ifndef __NET_MPTCP_H
+#define __NET_MPTCP_H
+
+#ifdef CONFIG_MPTCP
+
+void mptcp_init(void);
+
+#else
+
+static inline void mptcp_init(void)
+{
+}
+
+#endif /* CONFIG_MPTCP */
+#endif /* __NET_MPTCP_H */
diff --git a/net/Kconfig b/net/Kconfig
index d122f53c6fa2..8d5d43017feb 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -93,6 +93,7 @@ if INET
source "net/ipv4/Kconfig"
source "net/ipv6/Kconfig"
source "net/netlabel/Kconfig"
+source "net/mptcp/Kconfig"
endif # if INET
diff --git a/net/Makefile b/net/Makefile
index 449fc0b221f8..306d2e8c12c0 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -87,3 +87,4 @@ endif
obj-$(CONFIG_QRTR) += qrtr/
obj-$(CONFIG_NET_NCSI) += ncsi/
obj-$(CONFIG_XDP_SOCKETS) += xdp/
+obj-$(CONFIG_MPTCP) += mptcp/
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index 5542e3d778e6..866c985a0c04 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -271,6 +271,7 @@
#include <net/icmp.h>
#include <net/inet_common.h>
#include <net/tcp.h>
+#include <net/mptcp.h>
#include <net/xfrm.h>
#include <net/ip.h>
#include <net/sock.h>
@@ -3978,4 +3979,5 @@ void __init tcp_init(void)
tcp_metrics_init();
BUG_ON(tcp_register_congestion_control(&tcp_reno) != 0);
tcp_tasklet_init();
+ mptcp_init();
}
diff --git a/net/mptcp/Kconfig b/net/mptcp/Kconfig
new file mode 100644
index 000000000000..d87dfdc210cc
--- /dev/null
+++ b/net/mptcp/Kconfig
@@ -0,0 +1,10 @@
+
+config MPTCP
+ bool "Multipath TCP"
+ depends on INET
+ help
+ Multipath TCP (MPTCP) connections send and receive data over multiple
+ subflows in order to utilize multiple network paths. Each subflow
+ uses the TCP protocol, and TCP options carry header information for
+ MPTCP.
+
diff --git a/net/mptcp/Makefile b/net/mptcp/Makefile
new file mode 100644
index 000000000000..659129d1fcbf
--- /dev/null
+++ b/net/mptcp/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_MPTCP) += mptcp.o
+
+mptcp-y := protocol.o
diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
new file mode 100644
index 000000000000..86db17af8c05
--- /dev/null
+++ b/net/mptcp/protocol.c
@@ -0,0 +1,118 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Multipath TCP
+ *
+ * Copyright (c) 2017 - 2019, Intel Corporation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <net/sock.h>
+#include <net/inet_common.h>
+#include <net/inet_hashtables.h>
+#include <net/protocol.h>
+#include <net/tcp.h>
+#include <net/mptcp.h>
+#include "protocol.h"
+
+static int mptcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
+{
+ struct mptcp_sock *msk = mptcp_sk(sk);
+ struct socket *subflow = msk->subflow;
+
+ pr_debug("subflow=%p", subflow->sk);
+
+ return sock_sendmsg(subflow, msg);
+}
+
+static int mptcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
+ int nonblock, int flags, int *addr_len)
+{
+ struct mptcp_sock *msk = mptcp_sk(sk);
+ struct socket *subflow = msk->subflow;
+
+ pr_debug("subflow=%p", subflow->sk);
+
+ return sock_recvmsg(subflow, msg, flags);
+}
+
+static int mptcp_init_sock(struct sock *sk)
+{
+ struct mptcp_sock *msk = mptcp_sk(sk);
+ struct socket *sf;
+ int err;
+
+ pr_debug("msk=%p", msk);
+
+ err = sock_create_kern(&init_net, PF_INET, SOCK_STREAM, IPPROTO_TCP,
+ &sf);
+ if (!err) {
+ pr_debug("subflow=%p", sf->sk);
+ msk->subflow = sf;
+ }
+
+ return err;
+}
+
+static void mptcp_close(struct sock *sk, long timeout)
+{
+ struct mptcp_sock *msk = mptcp_sk(sk);
+
+ inet_sk_state_store(sk, TCP_CLOSE);
+
+ if (msk->subflow) {
+ pr_debug("subflow=%p", msk->subflow->sk);
+ sock_release(msk->subflow);
+ }
+
+ sock_orphan(sk);
+ sock_put(sk);
+}
+
+static int mptcp_connect(struct sock *sk, struct sockaddr *saddr, int len)
+{
+ struct mptcp_sock *msk = mptcp_sk(sk);
+ int err;
+
+ saddr->sa_family = AF_INET;
+
+ pr_debug("msk=%p, subflow=%p", msk, msk->subflow->sk);
+
+ err = kernel_connect(msk->subflow, saddr, len, 0);
+
+ sk->sk_state = TCP_ESTABLISHED;
+
+ return err;
+}
+
+static struct proto mptcp_prot = {
+ .name = "MPTCP",
+ .owner = THIS_MODULE,
+ .init = mptcp_init_sock,
+ .close = mptcp_close,
+ .accept = inet_csk_accept,
+ .connect = mptcp_connect,
+ .shutdown = tcp_shutdown,
+ .sendmsg = mptcp_sendmsg,
+ .recvmsg = mptcp_recvmsg,
+ .hash = inet_hash,
+ .unhash = inet_unhash,
+ .get_port = inet_csk_get_port,
+ .obj_size = sizeof(struct mptcp_sock),
+ .no_autobind = 1,
+};
+
+static struct inet_protosw mptcp_protosw = {
+ .type = SOCK_STREAM,
+ .protocol = IPPROTO_MPTCP,
+ .prot = &mptcp_prot,
+ .ops = &inet_stream_ops,
+};
+
+void __init mptcp_init(void)
+{
+ if (proto_register(&mptcp_prot, 1) != 0)
+ panic("Failed to register MPTCP proto.\n");
+
+ inet_register_protosw(&mptcp_protosw);
+}
diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h
new file mode 100644
index 000000000000..972204835421
--- /dev/null
+++ b/net/mptcp/protocol.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Multipath TCP
+ *
+ * Copyright (c) 2017 - 2019, Intel Corporation.
+ */
+
+#ifndef __MPTCP_PROTOCOL_H
+#define __MPTCP_PROTOCOL_H
+
+/* MPTCP connection sock */
+struct mptcp_sock {
+ /* inet_connection_sock must be the first member */
+ struct inet_connection_sock sk;
+ struct socket *subflow;
+};
+
+static inline struct mptcp_sock *mptcp_sk(const struct sock *sk)
+{
+ return (struct mptcp_sock *)sk;
+}
+
+#endif /* __MPTCP_PROTOCOL_H */
--
2.22.0
^ permalink raw reply related
* [RFC PATCH net-next 06/33] tcp: Expose tcp struct and routine for MPTCP
From: Mat Martineau @ 2019-06-17 22:57 UTC (permalink / raw)
To: edumazet, netdev
Cc: Peter Krystad, cpaasch, fw, pabeni, dcaratti, matthieu.baerts
In-Reply-To: <20190617225808.665-1-mathew.j.martineau@linux.intel.com>
From: Peter Krystad <peter.krystad@linux.intel.com>
tcp_request_sock_ipv4_ops and tcp_v4_init_sock().
This function is needed for MPTCP subflow initialization.
Signed-off-by: Peter Krystad <peter.krystad@linux.intel.com>
---
include/net/tcp.h | 3 +++
net/ipv4/tcp_ipv4.c | 4 ++--
2 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/include/net/tcp.h b/include/net/tcp.h
index a8ec09b7767e..cdbc2a6d9d9a 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -432,6 +432,7 @@ struct sock *tcp_v4_syn_recv_sock(const struct sock *sk, struct sk_buff *skb,
int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb);
int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len);
int tcp_connect(struct sock *sk);
+int tcp_v4_init_sock(struct sock *sk);
enum tcp_synack_type {
TCP_SYNACK_NORMAL,
TCP_SYNACK_FASTOPEN,
@@ -1959,6 +1960,8 @@ struct tcp_request_sock_ops {
enum tcp_synack_type synack_type);
};
+extern const struct tcp_request_sock_ops tcp_request_sock_ipv4_ops;
+
#ifdef CONFIG_SYN_COOKIES
static inline __u32 cookie_init_sequence(const struct tcp_request_sock_ops *ops,
const struct sock *sk, struct sk_buff *skb,
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 633e8244ed5b..ac61c6c9ec13 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -1367,7 +1367,7 @@ struct request_sock_ops tcp_request_sock_ops __read_mostly = {
.syn_ack_timeout = tcp_syn_ack_timeout,
};
-static const struct tcp_request_sock_ops tcp_request_sock_ipv4_ops = {
+const struct tcp_request_sock_ops tcp_request_sock_ipv4_ops = {
.mss_clamp = TCP_MSS_DEFAULT,
#ifdef CONFIG_TCP_MD5SIG
.req_md5_lookup = tcp_v4_md5_lookup,
@@ -2053,7 +2053,7 @@ static const struct tcp_sock_af_ops tcp_sock_ipv4_specific = {
/* NOTE: A lot of things set to zero explicitly by call to
* sk_alloc() so need not be done here.
*/
-static int tcp_v4_init_sock(struct sock *sk)
+int tcp_v4_init_sock(struct sock *sk)
{
struct inet_connection_sock *icsk = inet_csk(sk);
--
2.22.0
^ permalink raw reply related
* [RFC PATCH net-next 00/33] Multipath TCP
From: Mat Martineau @ 2019-06-17 22:57 UTC (permalink / raw)
To: edumazet, netdev
Cc: Mat Martineau, cpaasch, fw, pabeni, peter.krystad, dcaratti,
matthieu.baerts
The MPTCP upstreaming community has prepared a net-next RFC patch set
for review.
Clone/fetch:
https://github.com/multipath-tcp/mptcp_net-next.git (tag: netdev-rfc)
Browse:
https://github.com/multipath-tcp/mptcp_net-next/tree/netdev-rfc
With CONFIG_MPTCP=y, a socket created with IPPROTO_MPTCP will attempt to
create an MPTCP connection but remains compatible with regular
TCP. IPPROTO_TCP socket behavior is unchanged.
This implementation makes use of ULP between the userspace-facing MPTCP
socket and the set of in-kernel TCP sockets it controls. ULP has been
extended for use with listening sockets. skb_ext is used to carry MPTCP
metadata.
The patch set includes a self-test to exercise MPTCP in various
connection and routing scenarios.
We have more work to do to reach the initial feature set for merging,
notably:
* Finish MP_JOIN work
* Couple receive windows across sibling subflow TCP sockets as required
by RFC 6824
* IPv6
* Limit subflow ULP visibility to kernel space
Thank you for your review. You can find us at mptcp@lists.01.org and
https://is.gd/mptcp_upstream
Florian Westphal (6):
mptcp: add mptcp_poll
mptcp: add and use mptcp_subflow_hold
mptcp: add basic kselftest program
mptcp: selftests: switch to netns+veth based tests
mptcp: accept: don't leak mptcp socket structure
mptcp: switch sublist to mptcp socket lock protection
Mat Martineau (11):
tcp: Add MPTCP option number
tcp: Define IPPROTO_MPTCP
mptcp: Add MPTCP socket stubs
tcp, ulp: Add clone operation to tcp_ulp_ops
mptcp: Add MPTCP to skb extensions
tcp: Prevent coalesce/collapse when skb has MPTCP extensions
tcp: Export low-level TCP functions
mptcp: Write MPTCP DSS headers to outgoing data packets
mptcp: Implement MPTCP receive path
mptcp: selftests: Add capture option
tcp: Check for filled TCP option space before SACK
Paolo Abeni (4):
tcp: clean ext on tx recycle
mptcp: use sk_page_frag() in sendmsg
mptcp: sendmsg() do spool all the provided data
mptcp: allow collapsing consecutive sendpages on the same substream
Peter Krystad (12):
mptcp: Handle MPTCP TCP options
mptcp: Associate MPTCP context with TCP socket
tcp: Expose tcp struct and routine for MPTCP
mptcp: Handle MP_CAPABLE options for outgoing connections
mptcp: Create SUBFLOW socket for incoming connections
mptcp: Add key generation and token tree
mptcp: Add shutdown() socket operation
mptcp: Add setsockopt()/getsockopt() socket operations
mptcp: Make connection_list a real list of subflows
mptcp: Add path manager interface
mptcp: Add ADD_ADDR handling
mptcp: Add handling of incoming MP_JOIN requests
include/linux/skbuff.h | 11 +
include/linux/tcp.h | 51 +
include/net/mptcp.h | 158 +++
include/net/sock.h | 1 +
include/net/tcp.h | 20 +
include/uapi/linux/in.h | 2 +
net/Kconfig | 1 +
net/Makefile | 1 +
net/core/skbuff.c | 7 +
net/ipv4/inet_connection_sock.c | 2 +
net/ipv4/tcp.c | 8 +-
net/ipv4/tcp_input.c | 25 +-
net/ipv4/tcp_ipv4.c | 4 +-
net/ipv4/tcp_minisocks.c | 6 +
net/ipv4/tcp_output.c | 62 +-
net/ipv4/tcp_ulp.c | 12 +
net/mptcp/Kconfig | 11 +
net/mptcp/Makefile | 4 +
net/mptcp/crypto.c | 206 ++++
net/mptcp/options.c | 621 ++++++++++
net/mptcp/pm.c | 66 ++
net/mptcp/protocol.c | 1043 +++++++++++++++++
net/mptcp/protocol.h | 229 ++++
net/mptcp/subflow.c | 344 ++++++
net/mptcp/token.c | 373 ++++++
tools/include/uapi/linux/in.h | 2 +
tools/testing/selftests/Makefile | 1 +
tools/testing/selftests/net/mptcp/.gitignore | 1 +
tools/testing/selftests/net/mptcp/Makefile | 11 +
tools/testing/selftests/net/mptcp/config | 1 +
.../selftests/net/mptcp/mptcp_connect.c | 408 +++++++
.../selftests/net/mptcp/mptcp_connect.sh | 271 +++++
32 files changed, 3955 insertions(+), 8 deletions(-)
create mode 100644 include/net/mptcp.h
create mode 100644 net/mptcp/Kconfig
create mode 100644 net/mptcp/Makefile
create mode 100644 net/mptcp/crypto.c
create mode 100644 net/mptcp/options.c
create mode 100644 net/mptcp/pm.c
create mode 100644 net/mptcp/protocol.c
create mode 100644 net/mptcp/protocol.h
create mode 100644 net/mptcp/subflow.c
create mode 100644 net/mptcp/token.c
create mode 100644 tools/testing/selftests/net/mptcp/.gitignore
create mode 100644 tools/testing/selftests/net/mptcp/Makefile
create mode 100644 tools/testing/selftests/net/mptcp/config
create mode 100644 tools/testing/selftests/net/mptcp/mptcp_connect.c
create mode 100755 tools/testing/selftests/net/mptcp/mptcp_connect.sh
--
2.22.0
^ permalink raw reply
* [RFC PATCH net-next 16/33] tcp: Prevent coalesce/collapse when skb has MPTCP extensions
From: Mat Martineau @ 2019-06-17 22:57 UTC (permalink / raw)
To: edumazet, netdev
Cc: Mat Martineau, cpaasch, fw, pabeni, peter.krystad, dcaratti,
matthieu.baerts
In-Reply-To: <20190617225808.665-1-mathew.j.martineau@linux.intel.com>
The MPTCP extension data needs to be preserved as it passes through the
TCP stack. Make sure that these skbs are not appended to others during
coalesce or collapse, so the data remains associated with the payload of
the given skb.
Signed-off-by: Mat Martineau <mathew.j.martineau@linux.intel.com>
---
include/net/mptcp.h | 10 ++++++++++
include/net/tcp.h | 8 ++++++++
net/ipv4/tcp_input.c | 10 ++++++++--
net/ipv4/tcp_output.c | 2 +-
4 files changed, 27 insertions(+), 3 deletions(-)
diff --git a/include/net/mptcp.h b/include/net/mptcp.h
index 805206359135..30cfa473e8bf 100644
--- a/include/net/mptcp.h
+++ b/include/net/mptcp.h
@@ -61,6 +61,11 @@ bool mptcp_synack_options(const struct request_sock *req, unsigned int *size,
bool mptcp_established_options(struct sock *sk, unsigned int *size,
struct mptcp_out_options *opts);
+static inline bool mptcp_skb_ext_exist(const struct sk_buff *skb)
+{
+ return skb_ext_exist(skb, SKB_EXT_MPTCP);
+}
+
void mptcp_write_options(__be32 *ptr, struct mptcp_out_options *opts);
#else
@@ -108,5 +113,10 @@ static inline bool mptcp_established_options(struct sock *sk,
return false;
}
+static inline bool mptcp_skb_ext_exist(const struct sk_buff *skb)
+{
+ return false;
+}
+
#endif /* CONFIG_MPTCP */
#endif /* __NET_MPTCP_H */
diff --git a/include/net/tcp.h b/include/net/tcp.h
index 23995f8c11fa..af13d91f4a0f 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -39,6 +39,7 @@
#include <net/tcp_states.h>
#include <net/inet_ecn.h>
#include <net/dst.h>
+#include <net/mptcp.h>
#include <linux/seq_file.h>
#include <linux/memcontrol.h>
@@ -949,6 +950,13 @@ static inline bool tcp_skb_can_collapse_to(const struct sk_buff *skb)
return likely(!TCP_SKB_CB(skb)->eor);
}
+static inline bool tcp_skb_can_collapse(const struct sk_buff *to,
+ const struct sk_buff *from)
+{
+ return likely(tcp_skb_can_collapse_to(to) &&
+ !mptcp_skb_ext_exist(from));
+}
+
/* Events passed to congestion control interface */
enum tcp_ca_event {
CA_EVENT_TX_START, /* first transmit when no packets in flight */
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index 240eb75c7b84..5e634fdd8e1c 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -1402,7 +1402,7 @@ static struct sk_buff *tcp_shift_skb_data(struct sock *sk, struct sk_buff *skb,
if ((TCP_SKB_CB(prev)->sacked & TCPCB_TAGBITS) != TCPCB_SACKED_ACKED)
goto fallback;
- if (!tcp_skb_can_collapse_to(prev))
+ if (!tcp_skb_can_collapse(prev, skb))
goto fallback;
in_sack = !after(start_seq, TCP_SKB_CB(skb)->seq) &&
@@ -4362,6 +4362,9 @@ static bool tcp_try_coalesce(struct sock *sk,
if (TCP_SKB_CB(from)->seq != TCP_SKB_CB(to)->end_seq)
return false;
+ if (mptcp_skb_ext_exist(from))
+ return false;
+
#ifdef CONFIG_TLS_DEVICE
if (from->decrypted != to->decrypted)
return false;
@@ -4869,10 +4872,12 @@ tcp_collapse(struct sock *sk, struct sk_buff_head *list, struct rb_root *root,
/* The first skb to collapse is:
* - not SYN/FIN and
+ * - does not include a MPTCP skb extension
* - bloated or contains data before "start" or
* overlaps to the next one.
*/
if (!(TCP_SKB_CB(skb)->tcp_flags & (TCPHDR_SYN | TCPHDR_FIN)) &&
+ !mptcp_skb_ext_exist(skb) &&
(tcp_win_from_space(sk, skb->truesize) > skb->len ||
before(TCP_SKB_CB(skb)->seq, start))) {
end_of_skbs = false;
@@ -4888,7 +4893,7 @@ tcp_collapse(struct sock *sk, struct sk_buff_head *list, struct rb_root *root,
/* Decided to skip this, advance start seq. */
start = TCP_SKB_CB(skb)->end_seq;
}
- if (end_of_skbs ||
+ if (end_of_skbs || mptcp_skb_ext_exist(skb) ||
(TCP_SKB_CB(skb)->tcp_flags & (TCPHDR_SYN | TCPHDR_FIN)))
return;
@@ -4931,6 +4936,7 @@ tcp_collapse(struct sock *sk, struct sk_buff_head *list, struct rb_root *root,
skb = tcp_collapse_one(sk, skb, list, root);
if (!skb ||
skb == tail ||
+ mptcp_skb_ext_exist(skb) ||
(TCP_SKB_CB(skb)->tcp_flags & (TCPHDR_SYN | TCPHDR_FIN)))
goto end;
#ifdef CONFIG_TLS_DEVICE
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index a41ba69760f1..4e49b2c40820 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -2882,7 +2882,7 @@ static void tcp_retrans_try_collapse(struct sock *sk, struct sk_buff *to,
if (!tcp_can_collapse(sk, skb))
break;
- if (!tcp_skb_can_collapse_to(to))
+ if (!tcp_skb_can_collapse(to, skb))
break;
space -= skb->len;
--
2.22.0
^ permalink raw reply related
* [RFC PATCH net-next 18/33] mptcp: Write MPTCP DSS headers to outgoing data packets
From: Mat Martineau @ 2019-06-17 22:57 UTC (permalink / raw)
To: edumazet, netdev
Cc: Mat Martineau, cpaasch, fw, pabeni, peter.krystad, dcaratti,
matthieu.baerts
In-Reply-To: <20190617225808.665-1-mathew.j.martineau@linux.intel.com>
Per-packet metadata required to write the MPTCP DSS option is written to
the skb_ext area. One write to the socket may contain more than one
packet of data, in which case the DSS option in the first packet will
have a mapping covering all of the data in that write. Packets after the
first do not have a DSS option. This is complicated to handle under
memory pressure, since the first packet (with the DSS mapping) is pushed
to the TCP core before the remaining skbs are allocated.
The current implementation is limited. It will only send up to one page
of data. The number of bytes sent is returned so the caller knows which
bytes were sent and which were not. More work is required to ensure that
it works correctly with full buffers or under memory pressure.
The MPTCP DSS checksum is not yet implemented.
Signed-off-by: Mat Martineau <mathew.j.martineau@linux.intel.com>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
Signed-off-by: Peter Krystad <peter.krystad@linux.intel.com>
---
include/net/mptcp.h | 14 ++++-
net/ipv4/tcp_output.c | 11 ++--
net/mptcp/options.c | 143 +++++++++++++++++++++++++++++++++++++++++-
net/mptcp/protocol.c | 117 ++++++++++++++++++++++++++++++----
net/mptcp/protocol.h | 18 +++++-
net/mptcp/subflow.c | 2 +
net/mptcp/token.c | 13 ++--
7 files changed, 289 insertions(+), 29 deletions(-)
diff --git a/include/net/mptcp.h b/include/net/mptcp.h
index 30cfa473e8bf..003150a8e406 100644
--- a/include/net/mptcp.h
+++ b/include/net/mptcp.h
@@ -8,6 +8,14 @@
#ifndef __NET_MPTCP_H
#define __NET_MPTCP_H
+/* MPTCP DSS flags */
+
+#define MPTCP_DSS_DATA_FIN BIT(4)
+#define MPTCP_DSS_DSN64 BIT(3)
+#define MPTCP_DSS_HAS_MAP BIT(2)
+#define MPTCP_DSS_ACK64 BIT(1)
+#define MPTCP_DSS_HAS_ACK BIT(0)
+
/* MPTCP sk_buff extension data */
struct mptcp_ext {
u64 data_ack;
@@ -34,6 +42,7 @@ struct mptcp_out_options {
u16 suboptions;
u64 sndr_key;
u64 rcvr_key;
+ struct mptcp_ext ext_copy;
#endif
};
@@ -58,7 +67,8 @@ bool mptcp_syn_options(struct sock *sk, unsigned int *size,
void mptcp_rcv_synsent(struct sock *sk);
bool mptcp_synack_options(const struct request_sock *req, unsigned int *size,
struct mptcp_out_options *opts);
-bool mptcp_established_options(struct sock *sk, unsigned int *size,
+bool mptcp_established_options(struct sock *sk, struct sk_buff *skb,
+ unsigned int *size, unsigned int remaining,
struct mptcp_out_options *opts);
static inline bool mptcp_skb_ext_exist(const struct sk_buff *skb)
@@ -107,7 +117,9 @@ static inline bool mptcp_synack_options(const struct request_sock *req,
}
static inline bool mptcp_established_options(struct sock *sk,
+ struct sk_buff *skb,
unsigned int *size,
+ unsigned int remaining,
struct mptcp_out_options *opts)
{
return false;
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index 4e49b2c40820..5fe9459bbd6a 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -796,13 +796,12 @@ static unsigned int tcp_established_options(struct sock *sk, struct sk_buff *skb
*/
if (sk_is_mptcp(sk)) {
unsigned int remaining = MAX_TCP_OPTION_SPACE - size;
- unsigned int opt_size;
+ unsigned int opt_size = 0;
- if (mptcp_established_options(sk, &opt_size, &opts->mptcp)) {
- if (remaining >= opt_size) {
- opts->options |= OPTION_MPTCP;
- size += opt_size;
- }
+ if (mptcp_established_options(sk, skb, &opt_size, remaining,
+ &opts->mptcp)) {
+ opts->options |= OPTION_MPTCP;
+ size += opt_size;
}
}
diff --git a/net/mptcp/options.c b/net/mptcp/options.c
index d8e77cd5664d..625cd93fb9a8 100644
--- a/net/mptcp/options.c
+++ b/net/mptcp/options.c
@@ -181,12 +181,13 @@ void mptcp_rcv_synsent(struct sock *sk)
}
}
-bool mptcp_established_options(struct sock *sk, unsigned int *size,
- struct mptcp_out_options *opts)
+static bool mptcp_established_options_mp(struct sock *sk, unsigned int *size,
+ unsigned int remaining,
+ struct mptcp_out_options *opts)
{
struct subflow_context *subflow = subflow_ctx(sk);
- if (subflow->mp_capable && !subflow->fourth_ack) {
+ if (!subflow->fourth_ack && remaining >= TCPOLEN_MPTCP_MPC_ACK) {
opts->suboptions = OPTION_MPTCP_MPC_ACK;
opts->sndr_key = subflow->local_key;
opts->rcvr_key = subflow->remote_key;
@@ -199,6 +200,92 @@ bool mptcp_established_options(struct sock *sk, unsigned int *size,
return false;
}
+static bool mptcp_established_options_dss(struct sock *sk, struct sk_buff *skb,
+ unsigned int *size,
+ unsigned int remaining,
+ struct mptcp_out_options *opts)
+{
+ unsigned int dss_size = 0;
+ struct mptcp_ext *mpext;
+ unsigned int ack_size;
+
+ mpext = skb ? mptcp_get_ext(skb) : NULL;
+
+ if (!skb || (mpext && mpext->use_map)) {
+ unsigned int map_size;
+ bool use_csum;
+
+ map_size = TCPOLEN_MPTCP_DSS_BASE + TCPOLEN_MPTCP_DSS_MAP64;
+ use_csum = subflow_ctx(sk)->use_checksum;
+ if (use_csum)
+ map_size += TCPOLEN_MPTCP_DSS_CHECKSUM;
+
+ if (map_size <= remaining) {
+ remaining -= map_size;
+ dss_size = map_size;
+ if (mpext) {
+ opts->ext_copy.data_seq = mpext->data_seq;
+ opts->ext_copy.subflow_seq = mpext->subflow_seq;
+ opts->ext_copy.data_len = mpext->data_len;
+ opts->ext_copy.checksum = mpext->checksum;
+ opts->ext_copy.use_map = 1;
+ opts->ext_copy.dsn64 = mpext->dsn64;
+ opts->ext_copy.use_checksum = use_csum;
+ }
+ } else {
+ opts->ext_copy.use_map = 0;
+ WARN_ONCE(1, "MPTCP: Map dropped");
+ }
+ }
+
+ if (mpext && mpext->use_ack) {
+ ack_size = TCPOLEN_MPTCP_DSS_ACK64;
+
+ /* Add kind/lenght/subtype/flag overhead if mapping is not
+ * populated
+ */
+ if (dss_size == 0)
+ ack_size += TCPOLEN_MPTCP_DSS_BASE;
+
+ if (ack_size <= remaining) {
+ dss_size += ack_size;
+
+ opts->ext_copy.data_ack = mpext->data_ack;
+ opts->ext_copy.ack64 = 1;
+ opts->ext_copy.use_ack = 1;
+ } else {
+ opts->ext_copy.use_ack = 0;
+ WARN(1, "MPTCP: Ack dropped");
+ }
+ }
+
+ *size = ALIGN(dss_size, 4);
+ return true;
+}
+
+bool mptcp_established_options(struct sock *sk, struct sk_buff *skb,
+ unsigned int *size, unsigned int remaining,
+ struct mptcp_out_options *opts)
+{
+ unsigned int opt_size = 0;
+
+ if (!subflow_ctx(sk)->mp_capable)
+ return false;
+
+ if (mptcp_established_options_mp(sk, &opt_size, remaining, opts)) {
+ *size += opt_size;
+ remaining -= opt_size;
+ return true;
+ } else if (mptcp_established_options_dss(sk, skb, &opt_size, remaining,
+ opts)) {
+ *size += opt_size;
+ remaining -= opt_size;
+ return true;
+ }
+
+ return false;
+}
+
bool mptcp_synack_options(const struct request_sock *req, unsigned int *size,
struct mptcp_out_options *opts)
{
@@ -243,4 +330,54 @@ void mptcp_write_options(__be32 *ptr, struct mptcp_out_options *opts)
ptr += 2;
}
}
+
+ if (opts->ext_copy.use_ack || opts->ext_copy.use_map) {
+ struct mptcp_ext *mpext = &opts->ext_copy;
+ u8 len = TCPOLEN_MPTCP_DSS_BASE;
+ u8 flags = 0;
+
+ if (mpext->use_ack) {
+ len += TCPOLEN_MPTCP_DSS_ACK64;
+ flags = MPTCP_DSS_HAS_ACK | MPTCP_DSS_ACK64;
+ }
+
+ if (mpext->use_map) {
+ len += TCPOLEN_MPTCP_DSS_MAP64;
+
+ if (mpext->use_checksum)
+ len += TCPOLEN_MPTCP_DSS_CHECKSUM;
+
+ /* Use only 64-bit mapping flags for now, add
+ * support for optional 32-bit mappings later.
+ */
+ flags |= MPTCP_DSS_HAS_MAP | MPTCP_DSS_DSN64;
+ if (mpext->data_fin)
+ flags |= MPTCP_DSS_DATA_FIN;
+ }
+
+ *ptr++ = htonl((TCPOPT_MPTCP << 24) |
+ (len << 16) |
+ (MPTCPOPT_DSS << 12) |
+ (flags));
+
+ if (mpext->use_ack) {
+ put_unaligned_be64(mpext->data_ack, ptr);
+ ptr += 2;
+ }
+
+ if (mpext->use_map) {
+ __sum16 checksum;
+
+ pr_debug("Writing map values");
+ put_unaligned_be64(mpext->data_seq, ptr);
+ ptr += 2;
+ *ptr++ = htonl(mpext->subflow_seq);
+
+ if (mpext->use_checksum)
+ checksum = mpext->checksum;
+ else
+ checksum = TCPOPT_NOP << 8 | TCPOPT_NOP;
+ *ptr = htonl(mpext->data_len << 16 | checksum);
+ }
+ }
}
diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
index 3215601b9c43..a6e6367c8ed1 100644
--- a/net/mptcp/protocol.c
+++ b/net/mptcp/protocol.c
@@ -18,17 +18,104 @@
static int mptcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
{
struct mptcp_sock *msk = mptcp_sk(sk);
- struct socket *subflow;
+ int mss_now, size_goal, poffset, ret;
+ struct mptcp_ext *mpext = NULL;
+ struct page *page = NULL;
+ struct sk_buff *skb;
+ struct sock *ssk;
+ size_t psize;
- if (msk->connection_list) {
- subflow = msk->connection_list;
- pr_debug("conn_list->subflow=%p", subflow_ctx(subflow->sk));
- } else {
- subflow = msk->subflow;
- pr_debug("subflow=%p", subflow_ctx(subflow->sk));
+ pr_debug("msk=%p", msk);
+ if (!msk->connection_list && msk->subflow) {
+ pr_debug("fallback passthrough");
+ return sock_sendmsg(msk->subflow, msg);
}
- return sock_sendmsg(subflow, msg);
+ if (!msg_data_left(msg)) {
+ pr_debug("empty send");
+ return sock_sendmsg(msk->connection_list, msg);
+ }
+
+ ssk = msk->connection_list->sk;
+
+ if (msg->msg_flags & ~(MSG_MORE | MSG_DONTWAIT | MSG_NOSIGNAL))
+ return -ENOTSUPP;
+
+ /* Initial experiment: new page per send. Real code will
+ * maintain list of active pages and DSS mappings, append to the
+ * end and honor zerocopy
+ */
+ page = alloc_page(GFP_KERNEL);
+ if (!page)
+ return -ENOMEM;
+
+ /* Copy to page */
+ poffset = 0;
+ pr_debug("left=%zu", msg_data_left(msg));
+ psize = copy_page_from_iter(page, poffset,
+ min_t(size_t, msg_data_left(msg),
+ PAGE_SIZE),
+ &msg->msg_iter);
+ pr_debug("left=%zu", msg_data_left(msg));
+
+ if (!psize) {
+ put_page(page);
+ return -EINVAL;
+ }
+
+ lock_sock(sk);
+ lock_sock(ssk);
+
+ /* Mark the end of the previous write so the beginning of the
+ * next write (with its own mptcp skb extension data) is not
+ * collapsed.
+ */
+ skb = tcp_write_queue_tail(ssk);
+ if (skb)
+ TCP_SKB_CB(skb)->eor = 1;
+
+ mss_now = tcp_send_mss(ssk, &size_goal, msg->msg_flags);
+
+ ret = do_tcp_sendpages(ssk, page, poffset, min_t(int, size_goal, psize),
+ msg->msg_flags | MSG_SENDPAGE_NOTLAST);
+ put_page(page);
+ if (ret <= 0)
+ goto error_out;
+
+ if (skb == tcp_write_queue_tail(ssk))
+ pr_err("no new skb %p/%p", sk, ssk);
+
+ skb = tcp_write_queue_tail(ssk);
+
+ mpext = skb_ext_add(skb, SKB_EXT_MPTCP);
+
+ if (mpext) {
+ memset(mpext, 0, sizeof(*mpext));
+ mpext->data_ack = msk->ack_seq;
+ mpext->data_seq = msk->write_seq;
+ mpext->subflow_seq = subflow_ctx(ssk)->rel_write_seq;
+ mpext->data_len = ret;
+ mpext->checksum = 0xbeef;
+ mpext->use_map = 1;
+ mpext->dsn64 = 1;
+ mpext->use_ack = 1;
+ mpext->ack64 = 1;
+
+ pr_debug("data_seq=%llu subflow_seq=%u data_len=%u checksum=%u, dsn64=%d",
+ mpext->data_seq, mpext->subflow_seq, mpext->data_len,
+ mpext->checksum, mpext->dsn64);
+ } /* TODO: else fallback */
+
+ msk->write_seq += ret;
+ subflow_ctx(ssk)->rel_write_seq += ret;
+
+ tcp_push(ssk, msg->msg_flags, mss_now, tcp_sk(ssk)->nonagle, size_goal);
+
+error_out:
+ release_sock(ssk);
+ release_sock(sk);
+
+ return ret;
}
static int mptcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
@@ -109,11 +196,14 @@ static struct sock *mptcp_accept(struct sock *sk, int flags, int *err,
subflow->tcp_sock = new_sock;
if (subflow->mp_capable) {
- msk->remote_key = subflow->remote_key;
msk->local_key = subflow->local_key;
msk->token = subflow->token;
- pr_debug("token=%u", msk->token);
token_update_accept(new_sock->sk, new_mptcp_sock->sk);
+ msk->write_seq = subflow->idsn + 1;
+ subflow->rel_write_seq = 1;
+ msk->remote_key = subflow->remote_key;
+ crypto_key_sha1(msk->remote_key, NULL, &msk->ack_seq);
+ msk->ack_seq++;
msk->connection_list = new_sock;
} else {
msk->subflow = new_sock;
@@ -192,10 +282,13 @@ void mptcp_finish_connect(struct sock *sk, int mp_capable)
struct subflow_context *subflow = subflow_ctx(msk->subflow->sk);
if (mp_capable) {
- msk->remote_key = subflow->remote_key;
msk->local_key = subflow->local_key;
msk->token = subflow->token;
- pr_debug("token=%u", msk->token);
+ msk->write_seq = subflow->idsn + 1;
+ subflow->rel_write_seq = 1;
+ msk->remote_key = subflow->remote_key;
+ crypto_key_sha1(msk->remote_key, NULL, &msk->ack_seq);
+ msk->ack_seq++;
msk->connection_list = msk->subflow;
msk->subflow = NULL;
}
diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h
index 5a8ed316d70e..79a9ce6c4d31 100644
--- a/net/mptcp/protocol.h
+++ b/net/mptcp/protocol.h
@@ -21,6 +21,10 @@
#define TCPOLEN_MPTCP_MPC_SYN 12
#define TCPOLEN_MPTCP_MPC_SYNACK 20
#define TCPOLEN_MPTCP_MPC_ACK 20
+#define TCPOLEN_MPTCP_DSS_BASE 4
+#define TCPOLEN_MPTCP_DSS_ACK64 8
+#define TCPOLEN_MPTCP_DSS_MAP64 14
+#define TCPOLEN_MPTCP_DSS_CHECKSUM 2
/* MPTCP MP_CAPABLE flags */
#define MPTCP_VERSION_MASK (0x0F)
@@ -35,6 +39,8 @@ struct mptcp_sock {
struct inet_connection_sock sk;
u64 local_key;
u64 remote_key;
+ u64 write_seq;
+ u64 ack_seq;
u32 token;
struct socket *connection_list; /* @@ needs to be a list */
struct socket *subflow; /* outgoing connect, listener or !mp_capable */
@@ -54,6 +60,7 @@ struct subflow_request_sock {
version : 4;
u64 local_key;
u64 remote_key;
+ u64 idsn;
u32 token;
};
@@ -68,12 +75,16 @@ struct subflow_context {
u64 local_key;
u64 remote_key;
u32 token;
+ u32 rel_write_seq;
+ u64 idsn;
u32 request_mptcp : 1, /* send MP_CAPABLE */
request_cksum : 1,
mp_capable : 1, /* remote is MPTCP capable */
fourth_ack : 1, /* send initial DSS */
version : 4,
- conn_finished : 1;
+ conn_finished : 1,
+ use_checksum : 1;
+
struct socket *tcp_sock; /* underlying tcp_sock */
struct sock *conn; /* parent mptcp_sock */
};
@@ -123,4 +134,9 @@ void crypto_key_sha1(u64 key, u32 *token, u64 *idsn);
void crypto_hmac_sha1(u64 key1, u64 key2, u32 *hash_out,
int arg_num, ...);
+static inline struct mptcp_ext *mptcp_get_ext(struct sk_buff *skb)
+{
+ return (struct mptcp_ext *)skb_ext_find(skb, SKB_EXT_MPTCP);
+}
+
#endif /* __MPTCP_PROTOCOL_H */
diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c
index abae6a42a101..bbfdf03489bb 100644
--- a/net/mptcp/subflow.c
+++ b/net/mptcp/subflow.c
@@ -188,6 +188,7 @@ static int subflow_ulp_init(struct sock *sk)
tp->is_mptcp = 1;
icsk->icsk_af_ops = &subflow_specific;
+ ctx->use_checksum = 0;
out:
return err;
}
@@ -216,6 +217,7 @@ static void subflow_ulp_clone(const struct request_sock *req,
subflow->conn = NULL;
subflow->conn_finished = 1;
+ subflow->use_checksum = 0;
if (subflow_req->mp_capable) {
subflow->mp_capable = 1;
diff --git a/net/mptcp/token.c b/net/mptcp/token.c
index 8c15b8134f70..b055a3e82add 100644
--- a/net/mptcp/token.c
+++ b/net/mptcp/token.c
@@ -74,10 +74,11 @@ static void new_req_token(struct request_sock *req,
ireq->ir_rmt_port);
#endif
}
- pr_debug("local_key=%llu:%llx", local_key, local_key);
subflow_req->local_key = local_key;
- crypto_key_sha1(subflow_req->local_key, &subflow_req->token, NULL);
- pr_debug("token=%u", subflow_req->token);
+ crypto_key_sha1(subflow_req->local_key, &subflow_req->token,
+ &subflow_req->idsn);
+ pr_debug("local_key=%llu, token=%u, idsn=%llu", subflow_req->local_key,
+ subflow_req->token, subflow_req->idsn);
}
static void new_token(const struct sock *sk)
@@ -98,9 +99,9 @@ static void new_token(const struct sock *sk)
isk->inet_dport);
#endif
}
- pr_debug("local_key=%llu:%llx", subflow->local_key, subflow->local_key);
- crypto_key_sha1(subflow->local_key, &subflow->token, NULL);
- pr_debug("token=%u", subflow->token);
+ crypto_key_sha1(subflow->local_key, &subflow->token, &subflow->idsn);
+ pr_debug("local_key=%llu, token=%u, idsn=%llu", subflow->local_key,
+ subflow->token, subflow->idsn);
}
static int insert_req_token(u32 token)
--
2.22.0
^ permalink raw reply related
* [RFC PATCH net-next 26/33] mptcp: sendmsg() do spool all the provided data
From: Mat Martineau @ 2019-06-17 22:58 UTC (permalink / raw)
To: edumazet, netdev
Cc: Paolo Abeni, cpaasch, fw, peter.krystad, dcaratti,
matthieu.baerts
In-Reply-To: <20190617225808.665-1-mathew.j.martineau@linux.intel.com>
From: Paolo Abeni <pabeni@redhat.com>
This makes mptcp sendmsg() behaviour more consistent and
improves xmit performances.
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
---
net/mptcp/protocol.c | 110 +++++++++++++++++++++++++------------------
1 file changed, 63 insertions(+), 47 deletions(-)
diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
index 98257a70ac2b..d51201c09519 100644
--- a/net/mptcp/protocol.c
+++ b/net/mptcp/protocol.c
@@ -47,66 +47,37 @@ static struct sock *mptcp_subflow_get_ref(const struct mptcp_sock *msk)
return NULL;
}
-static int mptcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
+static int mptcp_sendmsg_frag(struct sock *sk, struct sock *ssk,
+ struct msghdr *msg, long *timeo)
{
struct mptcp_sock *msk = mptcp_sk(sk);
- int mss_now, size_goal, poffset, ret;
struct mptcp_ext *mpext = NULL;
+ int mss_now, size_goal, ret;
struct page_frag *pfrag;
struct sk_buff *skb;
- struct sock *ssk;
size_t psize;
- long timeo;
-
- pr_debug("msk=%p", msk);
- if (msk->subflow) {
- pr_debug("fallback passthrough");
- return sock_sendmsg(msk->subflow, msg);
- }
-
- ssk = mptcp_subflow_get_ref(msk);
- if (!ssk)
- return -ENOTCONN;
-
- if (!msg_data_left(msg)) {
- pr_debug("empty send");
- ret = sock_sendmsg(ssk->sk_socket, msg);
- goto put_out;
- }
-
- pr_debug("conn_list->subflow=%p", ssk);
-
- if (msg->msg_flags & ~(MSG_MORE | MSG_DONTWAIT | MSG_NOSIGNAL)) {
- ret = -ENOTSUPP;
- goto put_out;
- }
-
- lock_sock(sk);
- lock_sock(ssk);
- timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT);
/* use the mptcp page cache so that we can easily move the data
* from one substream to another, but do per subflow memory accounting
*/
pfrag = sk_page_frag(sk);
while (!sk_page_frag_refill(ssk, pfrag)) {
- ret = sk_stream_wait_memory(ssk, &timeo);
+ ret = sk_stream_wait_memory(ssk, timeo);
if (ret)
- goto release_out;
+ return ret;
}
- /* Copy to page */
- poffset = pfrag->offset;
+ /* compute copy limit */
+ mss_now = tcp_send_mss(ssk, &size_goal, msg->msg_flags);
+ psize = min_t(int, pfrag->size - pfrag->offset, size_goal);
+
pr_debug("left=%zu", msg_data_left(msg));
- psize = copy_page_from_iter(pfrag->page, poffset,
- min_t(size_t, msg_data_left(msg),
- pfrag->size - poffset),
+ psize = copy_page_from_iter(pfrag->page, pfrag->offset,
+ min_t(size_t, msg_data_left(msg), psize),
&msg->msg_iter);
pr_debug("left=%zu", msg_data_left(msg));
- if (!psize) {
- ret = -EINVAL;
- goto release_out;
- }
+ if (!psize)
+ return -EINVAL;
/* Mark the end of the previous write so the beginning of the
* next write (with its own mptcp skb extension data) is not
@@ -116,12 +87,12 @@ static int mptcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
if (skb)
TCP_SKB_CB(skb)->eor = 1;
- mss_now = tcp_send_mss(ssk, &size_goal, msg->msg_flags);
- psize = min_t(int, size_goal, psize);
- ret = do_tcp_sendpages(ssk, pfrag->page, poffset, psize,
+ ret = do_tcp_sendpages(ssk, pfrag->page, pfrag->offset, psize,
msg->msg_flags | MSG_SENDPAGE_NOTLAST);
if (ret <= 0)
- goto release_out;
+ return ret;
+ if (unlikely(ret < psize))
+ iov_iter_revert(&msg->msg_iter, psize - ret);
if (skb == tcp_write_queue_tail(ssk))
pr_err("no new skb %p/%p", sk, ssk);
@@ -149,8 +120,53 @@ static int mptcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
subflow_ctx(ssk)->rel_write_seq += ret;
tcp_push(ssk, msg->msg_flags, mss_now, tcp_sk(ssk)->nonagle, size_goal);
+ return ret;
+}
+
+static int mptcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
+{
+ struct mptcp_sock *msk = mptcp_sk(sk);
+ size_t copied = 0;
+ struct sock *ssk;
+ int ret = 0;
+ long timeo;
+
+ pr_debug("msk=%p", msk);
+ if (msk->subflow) {
+ pr_debug("fallback passthrough");
+ return sock_sendmsg(msk->subflow, msg);
+ }
+
+ ssk = mptcp_subflow_get_ref(msk);
+ if (!ssk)
+ return -ENOTCONN;
+
+ if (!msg_data_left(msg)) {
+ pr_debug("empty send");
+ ret = sock_sendmsg(ssk->sk_socket, msg);
+ goto put_out;
+ }
+
+ pr_debug("conn_list->subflow=%p", ssk);
+
+ if (msg->msg_flags & ~(MSG_MORE | MSG_DONTWAIT | MSG_NOSIGNAL)) {
+ ret = -ENOTSUPP;
+ goto put_out;
+ }
+
+ lock_sock(sk);
+ lock_sock(ssk);
+ timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT);
+ while (msg_data_left(msg)) {
+ ret = mptcp_sendmsg_frag(sk, ssk, msg, &timeo);
+ if (ret < 0)
+ break;
+
+ copied += ret;
+ }
+ if (copied > 0)
+ ret = copied;
-release_out:
release_sock(ssk);
release_sock(sk);
--
2.22.0
^ permalink raw reply related
* [RFC PATCH net-next 32/33] mptcp: Add ADD_ADDR handling
From: Mat Martineau @ 2019-06-17 22:58 UTC (permalink / raw)
To: edumazet, netdev
Cc: Peter Krystad, cpaasch, fw, pabeni, dcaratti, matthieu.baerts
In-Reply-To: <20190617225808.665-1-mathew.j.martineau@linux.intel.com>
From: Peter Krystad <peter.krystad@linux.intel.com>
Add handling for sending and receiving the ADD_ADDR, ADD_ADDR6,
and RM_ADDR suboptions.
Signed-off-by: Peter Krystad <peter.krystad@linux.intel.com>
---
include/linux/tcp.h | 11 +++++
include/net/mptcp.h | 16 ++++++--
net/mptcp/options.c | 98 +++++++++++++++++++++++++++++++++++++++-----
net/mptcp/pm.c | 11 ++++-
net/mptcp/protocol.h | 16 ++++++++
5 files changed, 138 insertions(+), 14 deletions(-)
diff --git a/include/linux/tcp.h b/include/linux/tcp.h
index 81cfa7834111..b1d2ff2af0c2 100644
--- a/include/linux/tcp.h
+++ b/include/linux/tcp.h
@@ -122,6 +122,16 @@ struct tcp_options_received {
use_ack:1,
ack64:1,
__unused:2;
+ u8 add_addr : 1,
+ rm_addr : 1,
+ family : 4;
+ u8 addr_id;
+ union {
+ struct in_addr addr;
+#if IS_ENABLED(CONFIG_IPV6)
+ struct in6_addr addr6;
+#endif
+ };
} mptcp;
#endif
};
@@ -135,6 +145,7 @@ static inline void tcp_clear_options(struct tcp_options_received *rx_opt)
#endif
#if IS_ENABLED(CONFIG_MPTCP)
rx_opt->mptcp.mp_capable = rx_opt->mptcp.mp_join = 0;
+ rx_opt->mptcp.add_addr = rx_opt->mptcp.rm_addr = 0;
rx_opt->mptcp.dss = 0;
#endif
}
diff --git a/include/net/mptcp.h b/include/net/mptcp.h
index ecc45733d8cf..92c630a25666 100644
--- a/include/net/mptcp.h
+++ b/include/net/mptcp.h
@@ -25,15 +25,25 @@ struct mptcp_ext {
};
/* MPTCP option subtypes */
-#define OPTION_MPTCP_MPC_SYN BIT(0)
-#define OPTION_MPTCP_MPC_SYNACK BIT(1)
-#define OPTION_MPTCP_MPC_ACK BIT(2)
+#define OPTION_MPTCP_MPC_SYN BIT(0)
+#define OPTION_MPTCP_MPC_SYNACK BIT(1)
+#define OPTION_MPTCP_MPC_ACK BIT(2)
+#define OPTION_MPTCP_ADD_ADDR BIT(6)
+#define OPTION_MPTCP_ADD_ADDR6 BIT(7)
+#define OPTION_MPTCP_RM_ADDR BIT(8)
struct mptcp_out_options {
#if IS_ENABLED(CONFIG_MPTCP)
u16 suboptions;
u64 sndr_key;
u64 rcvr_key;
+ union {
+ struct in_addr addr;
+#if IS_ENABLED(CONFIG_IPV6)
+ struct in6_addr addr6;
+#endif
+ };
+ u8 addr_id;
struct mptcp_ext ext_copy;
#endif
};
diff --git a/net/mptcp/options.c b/net/mptcp/options.c
index 6c5aed6351b3..68d0b4bec1dd 100644
--- a/net/mptcp/options.c
+++ b/net/mptcp/options.c
@@ -160,12 +160,51 @@ void mptcp_parse_option(const unsigned char *ptr, int opsize,
* 4 or 16 bytes of address (depending on ip version)
* 0 or 2 bytes of port (depending on length)
*/
+ case MPTCPOPT_ADD_ADDR:
+ if (opsize != TCPOLEN_MPTCP_ADD_ADDR &&
+ opsize != TCPOLEN_MPTCP_ADD_ADDR6)
+ break;
+ mp_opt->family = *ptr++ & MPTCP_ADDR_FAMILY_MASK;
+ if (mp_opt->family != MPTCP_ADDR_IPVERSION_4 &&
+ mp_opt->family != MPTCP_ADDR_IPVERSION_6)
+ break;
+
+ if (mp_opt->family == MPTCP_ADDR_IPVERSION_4 &&
+ opsize != TCPOLEN_MPTCP_ADD_ADDR)
+ break;
+#if IS_ENABLED(CONFIG_IPV6)
+ if (mp_opt->family == MPTCP_ADDR_IPVERSION_6 &&
+ opsize != TCPOLEN_MPTCP_ADD_ADDR6)
+ break;
+#endif
+ mp_opt->addr_id = *ptr++;
+ if (mp_opt->family == MPTCP_ADDR_IPVERSION_4) {
+ mp_opt->add_addr = 1;
+ mp_opt->addr.s_addr = get_unaligned_be32(ptr);
+ pr_debug("ADD_ADDR: addr=%x, id=%d",
+ mp_opt->addr.s_addr, mp_opt->addr_id);
+#if IS_ENABLED(CONFIG_IPV6)
+ } else {
+ mp_opt->add_addr = 1;
+ memcpy(mp_opt->addr6.s6_addr, (u8 *)ptr, 16);
+ pr_debug("ADD_ADDR: addr6=, id=%d", mp_opt->addr_id);
+#endif
+ }
+ break;
- /* MPTCPOPT_REMOVE_ADDR
+ /* MPTCPOPT_RM_ADDR
* 0: 4MSB=subtype, 0000
* 1: Address ID
* Additional bytes: More address IDs (depending on length)
*/
+ case MPTCPOPT_RM_ADDR:
+ if (opsize != TCPOLEN_MPTCP_RM_ADDR)
+ break;
+
+ mp_opt->rm_addr = 1;
+ mp_opt->addr_id = *ptr++;
+ pr_debug("RM_ADDR: id=%d", mp_opt->addr_id);
+ break;
/* MPTCPOPT_MP_PRIO
* 0: 4MSB=subtype, 000, 1LSB=Backup
@@ -336,27 +375,47 @@ static bool mptcp_established_options_dss(struct sock *sk, struct sk_buff *skb,
return true;
}
+static bool mptcp_established_options_addr(struct sock *sk,
+ unsigned int *size,
+ unsigned int remaining,
+ struct mptcp_out_options *opts)
+{
+ struct subflow_context *subflow = subflow_ctx(sk);
+ struct mptcp_sock *msk = mptcp_sk(subflow->conn);
+
+ if (subflow->fourth_ack)
+ return pm_addr_signal(msk, size, remaining, opts);
+
+ return false;
+}
+
bool mptcp_established_options(struct sock *sk, struct sk_buff *skb,
unsigned int *size, unsigned int remaining,
struct mptcp_out_options *opts)
{
unsigned int opt_size = 0;
+ bool ret = false;
if (!subflow_ctx(sk)->mp_capable)
return false;
+ opts->suboptions = 0;
if (mptcp_established_options_mp(sk, &opt_size, remaining, opts)) {
*size += opt_size;
remaining -= opt_size;
- return true;
+ ret = true;
} else if (mptcp_established_options_dss(sk, skb, &opt_size, remaining,
- opts)) {
+ opts)) {
*size += opt_size;
remaining -= opt_size;
- return true;
+ ret = true;
}
-
- return false;
+ if (mptcp_established_options_addr(sk, &opt_size, remaining, opts)) {
+ *size += opt_size;
+ remaining -= opt_size;
+ ret = true;
+ }
+ return ret;
}
bool mptcp_synack_options(const struct request_sock *req, unsigned int *size,
@@ -427,10 +486,8 @@ void mptcp_write_options(__be32 *ptr, struct mptcp_out_options *opts)
else
len = TCPOLEN_MPTCP_MPC_ACK;
- *ptr++ = htonl((TCPOPT_MPTCP << 24) | (len << 16) |
- (MPTCPOPT_MP_CAPABLE << 12) |
- ((MPTCP_VERSION_MASK & 0) << 8) |
- MPTCP_CAP_HMAC_SHA1);
+ *ptr++ = mptcp_option(MPTCPOPT_MP_CAPABLE, len, 0,
+ MPTCP_CAP_HMAC_SHA1);
put_unaligned_be64(opts->sndr_key, ptr);
ptr += 2;
if ((OPTION_MPTCP_MPC_SYNACK |
@@ -440,6 +497,27 @@ void mptcp_write_options(__be32 *ptr, struct mptcp_out_options *opts)
}
}
+ if (OPTION_MPTCP_ADD_ADDR & opts->suboptions) {
+ *ptr++ = mptcp_option(MPTCPOPT_ADD_ADDR, TCPOLEN_MPTCP_ADD_ADDR,
+ MPTCP_ADDR_IPVERSION_4, opts->addr_id);
+ *ptr++ = htonl(opts->addr.s_addr);
+ }
+
+#if IS_ENABLED(CONFIG_IPV6)
+ if (OPTION_MPTCP_ADD_ADDR6 & opts->suboptions) {
+ *ptr++ = mptcp_option(MPTCPOPT_ADD_ADDR,
+ TCPOLEN_MPTCP_ADD_ADDR6,
+ MPTCP_ADDR_IPVERSION_6, opts->addr_id);
+ memcpy((u8 *)ptr, opts->addr6.s6_addr, 16);
+ ptr += 4;
+ }
+#endif
+
+ if (OPTION_MPTCP_RM_ADDR & opts->suboptions) {
+ *ptr++ = mptcp_option(MPTCPOPT_RM_ADDR, TCPOLEN_MPTCP_RM_ADDR,
+ 0, opts->addr_id);
+ }
+
if (opts->ext_copy.use_ack || opts->ext_copy.use_map) {
struct mptcp_ext *mpext = &opts->ext_copy;
u8 len = TCPOLEN_MPTCP_DSS_BASE;
diff --git a/net/mptcp/pm.c b/net/mptcp/pm.c
index 512dc110098a..9e9c681a4544 100644
--- a/net/mptcp/pm.c
+++ b/net/mptcp/pm.c
@@ -51,7 +51,16 @@ void pm_rm_addr(struct mptcp_sock *msk, u8 id)
bool pm_addr_signal(struct mptcp_sock *msk, unsigned int *size,
unsigned int remaining, struct mptcp_out_options *opts)
{
+ if (!msk || !msk->addr_signal)
+ return false;
+
+ if (msk->pm.family == AF_INET && remaining < TCPOLEN_MPTCP_ADD_ADDR)
+ return false;
+
pr_debug("msk=%p", msk);
+ opts->suboptions |= OPTION_MPTCP_ADD_ADDR;
+ opts->addr_id = msk->pm.addr_id;
+ opts->addr.s_addr = msk->pm.addr.s_addr;
- return false;
+ return true;
}
diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h
index 044665328b79..4e4c8fc59972 100644
--- a/net/mptcp/protocol.h
+++ b/net/mptcp/protocol.h
@@ -8,6 +8,7 @@
#define __MPTCP_PROTOCOL_H
#include <linux/spinlock.h>
+#include <net/tcp.h>
/* MPTCP option subtypes */
#define MPTCPOPT_MP_CAPABLE 0
@@ -29,6 +30,9 @@
#define TCPOLEN_MPTCP_DSS_MAP32 10
#define TCPOLEN_MPTCP_DSS_MAP64 14
#define TCPOLEN_MPTCP_DSS_CHECKSUM 2
+#define TCPOLEN_MPTCP_ADD_ADDR 8
+#define TCPOLEN_MPTCP_ADD_ADDR6 20
+#define TCPOLEN_MPTCP_RM_ADDR 4
/* MPTCP MP_CAPABLE flags */
#define MPTCP_VERSION_MASK (0x0F)
@@ -45,6 +49,17 @@
#define MPTCP_DSS_HAS_ACK BIT(0)
#define MPTCP_DSS_FLAG_MASK (0x1F)
+/* MPTCP ADD_ADDR flags */
+#define MPTCP_ADDR_FAMILY_MASK (0x0F)
+#define MPTCP_ADDR_IPVERSION_4 4
+#define MPTCP_ADDR_IPVERSION_6 6
+
+static inline u32 mptcp_option(u8 subopt, u8 len, u8 nib, u8 field)
+{
+ return htonl((TCPOPT_MPTCP << 24) | (len << 16) | (subopt << 12) |
+ ((nib & 0xF) << 8) | field);
+}
+
struct pm_data {
u8 addr_id;
sa_family_t family;
@@ -68,6 +83,7 @@ struct mptcp_sock {
struct list_head conn_list;
struct socket *subflow; /* outgoing connect/listener/!mp_capable */
struct pm_data pm;
+ u8 addr_signal;
};
#define mptcp_for_each_subflow(__msk, __subflow) \
--
2.22.0
^ permalink raw reply related
* [RFC PATCH net-next 33/33] mptcp: Add handling of incoming MP_JOIN requests
From: Mat Martineau @ 2019-06-17 22:58 UTC (permalink / raw)
To: edumazet, netdev
Cc: Peter Krystad, cpaasch, fw, pabeni, dcaratti, matthieu.baerts
In-Reply-To: <20190617225808.665-1-mathew.j.martineau@linux.intel.com>
From: Peter Krystad <peter.krystad@linux.intel.com>
Process the MP_JOIN option in a SYN packet with the same flow
as MP_CAPABLE but when the third ACK is received add the
subflow to the MPTCP socket subflow list instead of adding it to
the TCP socket accept queue.
The subflow is added at the end of the subflow list so it will not
interfere with the existing subflows operation and no data is
expected to be transmitted on it.
Signed-off-by: Peter Krystad <peter.krystad@linux.intel.com>
---
include/linux/tcp.h | 6 ++
include/net/mptcp.h | 14 +++++
net/ipv4/tcp_minisocks.c | 6 ++
net/mptcp/options.c | 58 ++++++++++++++++--
net/mptcp/protocol.c | 21 +++++++
net/mptcp/protocol.h | 29 ++++++++-
net/mptcp/subflow.c | 58 +++++++++++++++---
net/mptcp/token.c | 124 +++++++++++++++++++++++++++++++++++++++
8 files changed, 301 insertions(+), 15 deletions(-)
diff --git a/include/linux/tcp.h b/include/linux/tcp.h
index b1d2ff2af0c2..68ff73ce8ac2 100644
--- a/include/linux/tcp.h
+++ b/include/linux/tcp.h
@@ -112,8 +112,14 @@ struct tcp_options_received {
u8 mp_capable : 1,
mp_join : 1,
dss : 1,
+ backup : 1,
version : 4;
u8 flags;
+ u8 join_id;
+ u32 token;
+ u32 nonce;
+ u64 thmac;
+ u8 hmac[20];
u8 dss_flags;
u8 use_map:1,
dsn64:1,
diff --git a/include/net/mptcp.h b/include/net/mptcp.h
index 92c630a25666..68e674f453e4 100644
--- a/include/net/mptcp.h
+++ b/include/net/mptcp.h
@@ -28,6 +28,9 @@ struct mptcp_ext {
#define OPTION_MPTCP_MPC_SYN BIT(0)
#define OPTION_MPTCP_MPC_SYNACK BIT(1)
#define OPTION_MPTCP_MPC_ACK BIT(2)
+#define OPTION_MPTCP_MPJ_SYN BIT(3)
+#define OPTION_MPTCP_MPJ_SYNACK BIT(4)
+#define OPTION_MPTCP_MPJ_ACK BIT(5)
#define OPTION_MPTCP_ADD_ADDR BIT(6)
#define OPTION_MPTCP_ADD_ADDR6 BIT(7)
#define OPTION_MPTCP_RM_ADDR BIT(8)
@@ -44,6 +47,10 @@ struct mptcp_out_options {
#endif
};
u8 addr_id;
+ u8 join_id;
+ u8 backup;
+ u32 nonce;
+ u64 thmac;
struct mptcp_ext ext_copy;
#endif
};
@@ -83,6 +90,8 @@ static inline bool mptcp_skb_ext_exist(const struct sk_buff *skb)
void mptcp_write_options(__be32 *ptr, struct mptcp_out_options *opts);
+bool mptcp_sk_is_subflow(const struct sock *sk);
+
#else
static inline void mptcp_init(void)
@@ -140,5 +149,10 @@ static inline bool mptcp_skb_ext_exist(const struct sk_buff *skb)
return false;
}
+static inline bool mptcp_sk_is_subflow(const struct sock *sk)
+{
+ return false;
+}
+
#endif /* CONFIG_MPTCP */
#endif /* __NET_MPTCP_H */
diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c
index 8bcaf2586b68..081b410592b3 100644
--- a/net/ipv4/tcp_minisocks.c
+++ b/net/ipv4/tcp_minisocks.c
@@ -766,6 +766,12 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb,
if (!child)
goto listen_overflow;
+ if (own_req && sk_is_mptcp(child) && mptcp_sk_is_subflow(child)) {
+ inet_csk_reqsk_queue_drop(sk, req);
+ reqsk_queue_removed(&inet_csk(sk)->icsk_accept_queue, req);
+ return child;
+ }
+
sock_rps_save_rxhash(child, skb);
tcp_synack_rtt_meas(child, req);
*req_stolen = !own_req;
diff --git a/net/mptcp/options.c b/net/mptcp/options.c
index 68d0b4bec1dd..58215f19829a 100644
--- a/net/mptcp/options.c
+++ b/net/mptcp/options.c
@@ -54,24 +54,53 @@ void mptcp_parse_option(const unsigned char *ptr, int opsize,
break;
/* MPTCPOPT_MP_JOIN
- *
* Initial SYN
* 0: 4MSB=subtype, 000, 1LSB=Backup
* 1: Address ID
* 2-5: Receiver token
* 6-9: Sender random number
- *
* SYN/ACK response
* 0: 4MSB=subtype, 000, 1LSB=Backup
* 1: Address ID
* 2-9: Sender truncated HMAC
* 10-13: Sender random number
- *
* Third ACK
* 0: 4MSB=subtype, 0000
* 1: 0 (Reserved)
* 2-21: Sender HMAC
*/
+ case MPTCPOPT_MP_JOIN:
+ mp_opt->mp_join = 1;
+ if (opsize == TCPOLEN_MPTCP_MPJ_SYN) {
+ mp_opt->backup = *ptr++ & MPTCPOPT_BACKUP;
+ mp_opt->join_id = *ptr++;
+ mp_opt->token = get_unaligned_be32(ptr);
+ ptr += 4;
+ mp_opt->nonce = get_unaligned_be32(ptr);
+ ptr += 4;
+ pr_debug("MP_JOIN bkup=%u, id=%u, token=%u, nonce=%u",
+ mp_opt->backup, mp_opt->join_id,
+ mp_opt->token, mp_opt->nonce);
+ } else if (opsize == TCPOLEN_MPTCP_MPJ_SYNACK) {
+ mp_opt->backup = *ptr++ & MPTCPOPT_BACKUP;
+ mp_opt->join_id = *ptr++;
+ mp_opt->thmac = get_unaligned_be64(ptr);
+ ptr += 8;
+ mp_opt->nonce = get_unaligned_be32(ptr);
+ ptr += 4;
+ pr_debug("MP_JOIN bkup=%u, id=%u, thmac=%llu, nonce=%u",
+ mp_opt->backup, mp_opt->join_id,
+ mp_opt->thmac, mp_opt->nonce);
+ } else if (opsize == TCPOLEN_MPTCP_MPJ_ACK) {
+ ptr++;
+ memcpy(mp_opt->hmac, ptr, MPTCPOPT_HMAC_LEN);
+ pr_debug("MP_JOIN hmac");
+ } else {
+ pr_warn("MP_JOIN bad option size");
+ mp_opt->mp_join = 0;
+ }
+ break;
+
/* MPTCPOPT_DSS
* 0: 4MSB=subtype, 0000
@@ -428,10 +457,21 @@ bool mptcp_synack_options(const struct request_sock *req, unsigned int *size,
opts->sndr_key = subflow_req->local_key;
opts->rcvr_key = subflow_req->remote_key;
*size = TCPOLEN_MPTCP_MPC_SYNACK;
- pr_debug("subflow_req=%p, local_key=%llu, remote_key=%llu",
+ pr_debug("req=%p, local_key=%llu, remote_key=%llu",
subflow_req, subflow_req->local_key,
subflow_req->remote_key);
return true;
+ } else if (subflow_req->mp_join) {
+ opts->suboptions = OPTION_MPTCP_MPJ_SYNACK;
+ opts->backup = subflow_req->backup;
+ opts->join_id = subflow_req->local_id;
+ opts->thmac = subflow_req->thmac;
+ opts->nonce = subflow_req->local_nonce;
+ pr_debug("req=%p, bkup=%u, id=%u, thmac=%llu, nonce=%u",
+ subflow_req, opts->backup, opts->join_id,
+ opts->thmac, opts->nonce);
+ *size = TCPOLEN_MPTCP_MPJ_SYNACK;
+ return true;
}
return false;
}
@@ -518,6 +558,16 @@ void mptcp_write_options(__be32 *ptr, struct mptcp_out_options *opts)
0, opts->addr_id);
}
+ if (OPTION_MPTCP_MPJ_SYNACK & opts->suboptions) {
+ *ptr++ = mptcp_option(MPTCPOPT_MP_JOIN,
+ TCPOLEN_MPTCP_MPJ_SYNACK,
+ opts->backup, opts->join_id);
+ put_unaligned_be64(opts->thmac, ptr);
+ ptr += 2;
+ put_unaligned_be32(opts->nonce, ptr);
+ ptr += 1;
+ }
+
if (opts->ext_copy.use_ack || opts->ext_copy.use_map) {
struct mptcp_ext *mpext = &opts->ext_copy;
u8 len = TCPOLEN_MPTCP_DSS_BASE;
diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
index e071fc8191ee..042811a1e01b 100644
--- a/net/mptcp/protocol.c
+++ b/net/mptcp/protocol.c
@@ -777,6 +777,27 @@ void mptcp_finish_connect(struct sock *sk, int mp_capable)
inet_sk_state_store(sk, TCP_ESTABLISHED);
}
+void mptcp_finish_join(struct sock *conn, struct sock *sk)
+{
+ struct subflow_context *subflow = subflow_ctx(sk);
+ struct mptcp_sock *msk = mptcp_sk(conn);
+
+ pr_debug("msk=%p, subflow=%p", msk, subflow);
+
+ local_bh_disable();
+ bh_lock_sock_nested(sk);
+ list_add_tail(&subflow->node, &msk->conn_list);
+ bh_unlock_sock(sk);
+ local_bh_enable();
+}
+
+bool mptcp_sk_is_subflow(const struct sock *sk)
+{
+ struct subflow_context *subflow = subflow_ctx(sk);
+
+ return subflow->mp_join == 1;
+}
+
static struct proto mptcp_prot = {
.name = "MPTCP",
.owner = THIS_MODULE,
diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h
index 4e4c8fc59972..61e9f15de9d6 100644
--- a/net/mptcp/protocol.h
+++ b/net/mptcp/protocol.h
@@ -24,6 +24,9 @@
#define TCPOLEN_MPTCP_MPC_SYN 12
#define TCPOLEN_MPTCP_MPC_SYNACK 20
#define TCPOLEN_MPTCP_MPC_ACK 20
+#define TCPOLEN_MPTCP_MPJ_SYN 12
+#define TCPOLEN_MPTCP_MPJ_SYNACK 16
+#define TCPOLEN_MPTCP_MPJ_ACK 24
#define TCPOLEN_MPTCP_DSS_BASE 4
#define TCPOLEN_MPTCP_DSS_ACK32 4
#define TCPOLEN_MPTCP_DSS_ACK64 8
@@ -34,6 +37,9 @@
#define TCPOLEN_MPTCP_ADD_ADDR6 20
#define TCPOLEN_MPTCP_RM_ADDR 4
+#define MPTCPOPT_BACKUP BIT(0)
+#define MPTCPOPT_HMAC_LEN 20
+
/* MPTCP MP_CAPABLE flags */
#define MPTCP_VERSION_MASK (0x0F)
#define MPTCP_CAP_CHECKSUM_REQD BIT(7)
@@ -101,11 +107,16 @@ struct subflow_request_sock {
checksum : 1,
backup : 1,
version : 4;
+ u8 local_id;
+ u8 remote_id;
u64 local_key;
u64 remote_key;
u64 idsn;
u32 token;
u32 ssn_offset;
+ u64 thmac;
+ u32 local_nonce;
+ u32 remote_nonce;
};
static inline
@@ -128,15 +139,23 @@ struct subflow_context {
u16 map_data_len;
u16 request_mptcp : 1, /* send MP_CAPABLE */
request_cksum : 1,
- mp_capable : 1, /* remote is MPTCP capable */
+ mp_capable : 1, /* remote is MPTCP capable */
+ mp_join : 1, /* remote is JOINing */
fourth_ack : 1, /* send initial DSS */
version : 4,
conn_finished : 1,
use_checksum : 1,
- map_valid : 1;
+ map_valid : 1,
+ backup : 1;
+ u32 remote_nonce;
+ u64 thmac;
+ u32 local_nonce;
+ u8 local_id;
+ u8 remote_id;
struct socket *tcp_sock; /* underlying tcp_sock */
struct sock *conn; /* parent mptcp_sock */
+
void (*tcp_sk_data_ready)(struct sock *sk);
};
@@ -161,13 +180,19 @@ void mptcp_get_options(const struct sk_buff *skb,
struct tcp_options_received *opt_rx);
void mptcp_finish_connect(struct sock *sk, int mp_capable);
+void mptcp_finish_join(struct sock *conn, struct sock *sk);
void token_init(void);
void token_new_request(struct request_sock *req, const struct sk_buff *skb);
+int token_join_request(struct request_sock *req, const struct sk_buff *skb);
+int token_join_valid(struct request_sock *req,
+ struct tcp_options_received *rx_opt);
void token_destroy_request(u32 token);
void token_new_connect(struct sock *sk);
void token_new_accept(struct sock *sk);
+int token_new_join(struct sock *sk);
void token_update_accept(struct sock *sk, struct sock *conn);
+void token_release(u32 token);
void token_destroy(u32 token);
void crypto_init(void);
diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c
index a82f5091eed8..a858cc966724 100644
--- a/net/mptcp/subflow.c
+++ b/net/mptcp/subflow.c
@@ -54,6 +54,12 @@ static void subflow_v4_init_req(struct request_sock *req,
memset(&rx_opt.mptcp, 0, sizeof(rx_opt.mptcp));
mptcp_get_options(skb, &rx_opt);
+ subflow_req->mp_capable = 0;
+ subflow_req->mp_join = 0;
+
+ if (rx_opt.mptcp.mp_capable && rx_opt.mptcp.mp_join)
+ return;
+
if (rx_opt.mptcp.mp_capable && listener->request_mptcp) {
subflow_req->mp_capable = 1;
if (rx_opt.mptcp.version >= listener->version)
@@ -68,8 +74,18 @@ static void subflow_v4_init_req(struct request_sock *req,
token_new_request(req, skb);
pr_debug("syn seq=%u", TCP_SKB_CB(skb)->seq);
subflow_req->ssn_offset = TCP_SKB_CB(skb)->seq;
- } else {
- subflow_req->mp_capable = 0;
+ } else if (rx_opt.mptcp.mp_join && listener->request_mptcp) {
+ subflow_req->mp_join = 1;
+ subflow_req->backup = rx_opt.mptcp.backup;
+ subflow_req->remote_id = rx_opt.mptcp.join_id;
+ subflow_req->token = rx_opt.mptcp.token;
+ subflow_req->remote_nonce = rx_opt.mptcp.nonce;
+ pr_debug("token=%u, remote_nonce=%u", subflow_req->token,
+ subflow_req->remote_nonce);
+ if (token_join_request(req, skb)) {
+ subflow_req->mp_join = 0;
+ // @@ need to trigger RST
+ }
}
}
@@ -134,6 +150,11 @@ static struct sock *subflow_syn_recv_sock(const struct sock *sk,
subflow_req->local_key != opt_rx.mptcp.rcvr_key ||
subflow_req->remote_key != opt_rx.mptcp.sndr_key)
return NULL;
+ } else if (subflow_req->mp_join) {
+ opt_rx.mptcp.mp_join = 0;
+ mptcp_get_options(skb, &opt_rx);
+ if (!opt_rx.mptcp.mp_join || token_join_valid(req, &opt_rx))
+ return NULL;
}
child = tcp_v4_syn_recv_sock(sk, skb, req, dst, req_unhash, own_req);
@@ -141,18 +162,27 @@ static struct sock *subflow_syn_recv_sock(const struct sock *sk,
if (child && *own_req) {
struct subflow_context *ctx = subflow_ctx(child);
- if (!ctx) {
- pr_debug("Closing child socket");
- inet_sk_set_state(child, TCP_CLOSE);
- sock_set_flag(child, SOCK_DEAD);
- inet_csk_destroy_sock(child);
- child = NULL;
- } else if (ctx->mp_capable) {
+ if (!ctx)
+ goto close_child;
+
+ if (ctx->mp_capable) {
token_new_accept(child);
+ } else if (ctx->mp_join) {
+ if (token_new_join(child))
+ goto close_child;
+ else
+ mptcp_finish_join(ctx->conn, child);
}
}
return child;
+
+close_child:
+ pr_debug("closing child socket");
+ inet_sk_set_state(child, TCP_CLOSE);
+ sock_set_flag(child, SOCK_DEAD);
+ inet_csk_destroy_sock(child);
+ return NULL;
}
static struct inet_connection_sock_af_ops subflow_specific;
@@ -222,6 +252,8 @@ static void subflow_ulp_release(struct sock *sk)
pr_debug("subflow=%p", ctx);
+ token_release(ctx->token);
+
kfree(ctx);
}
@@ -255,6 +287,14 @@ static void subflow_ulp_clone(const struct request_sock *req,
new_ctx->ssn_offset = subflow_req->ssn_offset;
new_ctx->idsn = subflow_req->idsn;
pr_debug("token=%u", new_ctx->token);
+ } else if (subflow_req->mp_join) {
+ new_ctx->mp_join = 1;
+ new_ctx->fourth_ack = 1;
+ new_ctx->backup = subflow_req->backup;
+ new_ctx->local_id = subflow_req->local_id;
+ new_ctx->token = subflow_req->token;
+ new_ctx->thmac = subflow_req->thmac;
+ pr_debug("token=%u", new_ctx->token);
}
}
diff --git a/net/mptcp/token.c b/net/mptcp/token.c
index b055a3e82add..c2f4fcb37566 100644
--- a/net/mptcp/token.c
+++ b/net/mptcp/token.c
@@ -54,6 +54,15 @@ static bool find_token(u32 token)
return used;
}
+static struct sock *lookup_token(u32 token)
+{
+ void *conn;
+
+ pr_debug("token=%u", token);
+ conn = radix_tree_lookup(&token_tree, token);
+ return (struct sock *)conn;
+}
+
static void new_req_token(struct request_sock *req,
const struct sk_buff *skb)
{
@@ -81,6 +90,56 @@ static void new_req_token(struct request_sock *req,
subflow_req->token, subflow_req->idsn);
}
+static void new_req_join(struct request_sock *req, struct sock *sk,
+ const struct sk_buff *skb)
+{
+ const struct inet_request_sock *ireq = inet_rsk(req);
+ struct subflow_request_sock *subflow_req = subflow_rsk(req);
+ struct mptcp_sock *msk = mptcp_sk(sk);
+ u8 hmac[MPTCPOPT_HMAC_LEN];
+ u32 nonce;
+
+ if (skb->protocol == htons(ETH_P_IP)) {
+ nonce = crypto_v4_get_nonce(ip_hdr(skb)->saddr,
+ ip_hdr(skb)->daddr,
+ htons(ireq->ir_num),
+ ireq->ir_rmt_port);
+#if IS_ENABLED(CONFIG_IPV6)
+ } else {
+ nonce = crypto_v6_get_nonce(&ipv6_hdr(skb)->saddr,
+ &ipv6_hdr(skb)->daddr,
+ htons(ireq->ir_num),
+ ireq->ir_rmt_port);
+#endif
+ }
+ subflow_req->local_nonce = nonce;
+
+ crypto_hmac_sha1(msk->local_key,
+ msk->remote_key,
+ (u32 *)hmac, 2,
+ 4, (u8 *)&subflow_req->local_nonce,
+ 4, (u8 *)&subflow_req->remote_nonce);
+ subflow_req->thmac = *(u64 *)hmac;
+ pr_debug("local_nonce=%u, thmac=%llu", subflow_req->local_nonce,
+ subflow_req->thmac);
+}
+
+static int new_join_valid(struct request_sock *req, struct sock *sk,
+ struct tcp_options_received *rx_opt)
+{
+ struct subflow_request_sock *subflow_req = subflow_rsk(req);
+ struct mptcp_sock *msk = mptcp_sk(sk);
+ u8 hmac[MPTCPOPT_HMAC_LEN];
+
+ crypto_hmac_sha1(msk->remote_key,
+ msk->local_key,
+ (u32 *)hmac, 2,
+ 4, (u8 *)&subflow_req->remote_nonce,
+ 4, (u8 *)&subflow_req->local_nonce);
+
+ return memcmp(hmac, (char *)rx_opt->mptcp.hmac, MPTCPOPT_HMAC_LEN);
+}
+
static void new_token(const struct sock *sk)
{
struct subflow_context *subflow = subflow_ctx(sk);
@@ -177,6 +236,42 @@ void token_new_request(struct request_sock *req,
spin_unlock_bh(&token_tree_lock);
}
+/* validate received token and create truncated hmac and nonce for SYN-ACK */
+int token_join_request(struct request_sock *req, const struct sk_buff *skb)
+{
+ struct subflow_request_sock *subflow_req = subflow_rsk(req);
+ struct sock *conn;
+
+ pr_debug("subflow_req=%p, token=%u", subflow_req, subflow_req->token);
+ spin_lock_bh(&token_tree_lock);
+ conn = lookup_token(subflow_req->token);
+ spin_unlock_bh(&token_tree_lock);
+ if (conn) {
+ // @@ get real local address id for this skb->saddr
+ subflow_req->local_id = 0;
+ new_req_join(req, conn, skb);
+ return 0;
+ }
+ return -1;
+}
+
+/* validate hmac received in third ACK */
+int token_join_valid(struct request_sock *req,
+ struct tcp_options_received *rx_opt)
+{
+ struct subflow_request_sock *subflow_req = subflow_rsk(req);
+ struct sock *conn;
+
+ pr_debug("subflow_req=%p, token=%u", subflow_req, subflow_req->token);
+ spin_lock_bh(&token_tree_lock);
+ conn = lookup_token(subflow_req->token);
+ spin_unlock_bh(&token_tree_lock);
+ if (conn)
+ return new_join_valid(req, conn, rx_opt);
+
+ return -1;
+}
+
/* create new local key, idsn, and token for subflow */
void token_new_connect(struct sock *sk)
{
@@ -220,6 +315,23 @@ void token_update_accept(struct sock *sk, struct sock *conn)
spin_unlock_bh(&token_tree_lock);
}
+int token_new_join(struct sock *sk)
+{
+ struct subflow_context *subflow = subflow_ctx(sk);
+ struct sock *conn;
+
+ spin_lock_bh(&token_tree_lock);
+ conn = lookup_token(subflow->token);
+ if (conn) {
+ sock_hold(conn);
+ spin_unlock_bh(&token_tree_lock);
+ subflow->conn = conn;
+ return 0;
+ }
+ spin_unlock_bh(&token_tree_lock);
+ return -1;
+}
+
void token_destroy_request(u32 token)
{
pr_debug("token=%u", token);
@@ -229,6 +341,18 @@ void token_destroy_request(u32 token)
spin_unlock_bh(&token_tree_lock);
}
+void token_release(u32 token)
+{
+ struct sock *conn;
+
+ pr_debug("token=%u", token);
+ spin_lock_bh(&token_tree_lock);
+ conn = lookup_token(token);
+ if (conn)
+ sock_put(conn);
+ spin_unlock_bh(&token_tree_lock);
+}
+
void token_destroy(u32 token)
{
struct sock *conn;
--
2.22.0
^ permalink raw reply related
* [RFC PATCH net-next 27/33] mptcp: allow collapsing consecutive sendpages on the same substream
From: Mat Martineau @ 2019-06-17 22:58 UTC (permalink / raw)
To: edumazet, netdev
Cc: Paolo Abeni, cpaasch, fw, peter.krystad, dcaratti,
matthieu.baerts
In-Reply-To: <20190617225808.665-1-mathew.j.martineau@linux.intel.com>
From: Paolo Abeni <pabeni@redhat.com>
If the current sendmsg() lands on the same subflow we used last, we
can try to collapse the data.
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
---
net/mptcp/protocol.c | 79 +++++++++++++++++++++++++++++++++-----------
1 file changed, 60 insertions(+), 19 deletions(-)
diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
index d51201c09519..3fb0f3163743 100644
--- a/net/mptcp/protocol.c
+++ b/net/mptcp/protocol.c
@@ -47,12 +47,25 @@ static struct sock *mptcp_subflow_get_ref(const struct mptcp_sock *msk)
return NULL;
}
+static inline bool mptcp_skb_can_collapse_to(const struct mptcp_sock *msk,
+ const struct sk_buff *skb,
+ const struct mptcp_ext *mpext)
+{
+ if (!tcp_skb_can_collapse_to(skb))
+ return false;
+
+ /* can collapse only if MPTCP level sequence is in order */
+ return mpext && mpext->data_seq + mpext->data_len == msk->write_seq;
+}
+
static int mptcp_sendmsg_frag(struct sock *sk, struct sock *ssk,
- struct msghdr *msg, long *timeo)
+ struct msghdr *msg, long *timeo, int *pmss_now,
+ int *ps_goal)
{
+ int mss_now, avail_size, size_goal, ret;
struct mptcp_sock *msk = mptcp_sk(sk);
+ bool collapsed, can_collapse = false;
struct mptcp_ext *mpext = NULL;
- int mss_now, size_goal, ret;
struct page_frag *pfrag;
struct sk_buff *skb;
size_t psize;
@@ -69,8 +82,31 @@ static int mptcp_sendmsg_frag(struct sock *sk, struct sock *ssk,
/* compute copy limit */
mss_now = tcp_send_mss(ssk, &size_goal, msg->msg_flags);
- psize = min_t(int, pfrag->size - pfrag->offset, size_goal);
+ *pmss_now = mss_now;
+ *ps_goal = size_goal;
+ avail_size = size_goal;
+ skb = tcp_write_queue_tail(ssk);
+ if (skb) {
+ mpext = skb_ext_find(skb, SKB_EXT_MPTCP);
+
+ /* Limit the write to the size available in the
+ * current skb, if any, so that we create at most a new skb.
+ * If we run out of space in the current skb (e.g. the window
+ * size shrunk from last sent) a new skb will be allocated even
+ * is collapsing was allowed: collapsing is effectively
+ * disabled.
+ */
+ can_collapse = mptcp_skb_can_collapse_to(msk, skb, mpext);
+ if (!can_collapse)
+ TCP_SKB_CB(skb)->eor = 1;
+ else if (size_goal - skb->len > 0)
+ avail_size = size_goal - skb->len;
+ else
+ can_collapse = false;
+ }
+ psize = min_t(size_t, pfrag->size - pfrag->offset, avail_size);
+ /* Copy to page */
pr_debug("left=%zu", msg_data_left(msg));
psize = copy_page_from_iter(pfrag->page, pfrag->offset,
min_t(size_t, msg_data_left(msg), psize),
@@ -79,14 +115,9 @@ static int mptcp_sendmsg_frag(struct sock *sk, struct sock *ssk,
if (!psize)
return -EINVAL;
- /* Mark the end of the previous write so the beginning of the
- * next write (with its own mptcp skb extension data) is not
- * collapsed.
+ /* tell the TCP stack to delay the push so that we can safely
+ * access the skb after the sendpages call
*/
- skb = tcp_write_queue_tail(ssk);
- if (skb)
- TCP_SKB_CB(skb)->eor = 1;
-
ret = do_tcp_sendpages(ssk, pfrag->page, pfrag->offset, psize,
msg->msg_flags | MSG_SENDPAGE_NOTLAST);
if (ret <= 0)
@@ -94,13 +125,16 @@ static int mptcp_sendmsg_frag(struct sock *sk, struct sock *ssk,
if (unlikely(ret < psize))
iov_iter_revert(&msg->msg_iter, psize - ret);
- if (skb == tcp_write_queue_tail(ssk))
- pr_err("no new skb %p/%p", sk, ssk);
+ collapsed = skb == tcp_write_queue_tail(ssk);
+ BUG_ON(collapsed && !can_collapse);
+ if (collapsed) {
+ /* when collapsing mpext always exists */
+ mpext->data_len += ret;
+ goto out;
+ }
skb = tcp_write_queue_tail(ssk);
-
mpext = skb_ext_add(skb, SKB_EXT_MPTCP);
-
if (mpext) {
memset(mpext, 0, sizeof(*mpext));
mpext->data_seq = msk->write_seq;
@@ -113,22 +147,25 @@ static int mptcp_sendmsg_frag(struct sock *sk, struct sock *ssk,
pr_debug("data_seq=%llu subflow_seq=%u data_len=%u checksum=%u, dsn64=%d",
mpext->data_seq, mpext->subflow_seq, mpext->data_len,
mpext->checksum, mpext->dsn64);
- } /* TODO: else fallback */
+ }
+ /* TODO: else fallback; allocation can fail, but we can't easily retire
+ * skbs from the write_queue, as we need to roll-back TCP status
+ */
+out:
pfrag->offset += ret;
msk->write_seq += ret;
subflow_ctx(ssk)->rel_write_seq += ret;
- tcp_push(ssk, msg->msg_flags, mss_now, tcp_sk(ssk)->nonagle, size_goal);
return ret;
}
static int mptcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
{
+ int mss_now = 0, size_goal = 0, ret = 0;
struct mptcp_sock *msk = mptcp_sk(sk);
size_t copied = 0;
struct sock *ssk;
- int ret = 0;
long timeo;
pr_debug("msk=%p", msk);
@@ -158,14 +195,18 @@ static int mptcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
lock_sock(ssk);
timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT);
while (msg_data_left(msg)) {
- ret = mptcp_sendmsg_frag(sk, ssk, msg, &timeo);
+ ret = mptcp_sendmsg_frag(sk, ssk, msg, &timeo, &mss_now,
+ &size_goal);
if (ret < 0)
break;
copied += ret;
}
- if (copied > 0)
+ if (copied) {
ret = copied;
+ tcp_push(ssk, msg->msg_flags, mss_now, tcp_sk(ssk)->nonagle,
+ size_goal);
+ }
release_sock(ssk);
release_sock(sk);
--
2.22.0
^ permalink raw reply related
* [RFC PATCH net-next 25/33] mptcp: use sk_page_frag() in sendmsg
From: Mat Martineau @ 2019-06-17 22:58 UTC (permalink / raw)
To: edumazet, netdev
Cc: Paolo Abeni, cpaasch, fw, peter.krystad, dcaratti,
matthieu.baerts
In-Reply-To: <20190617225808.665-1-mathew.j.martineau@linux.intel.com>
From: Paolo Abeni <pabeni@redhat.com>
This clean-up a bit the send path, and allows better performances.
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
---
net/mptcp/protocol.c | 41 ++++++++++++++++++++---------------------
1 file changed, 20 insertions(+), 21 deletions(-)
diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
index 0db4099d9c13..98257a70ac2b 100644
--- a/net/mptcp/protocol.c
+++ b/net/mptcp/protocol.c
@@ -52,10 +52,11 @@ static int mptcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
struct mptcp_sock *msk = mptcp_sk(sk);
int mss_now, size_goal, poffset, ret;
struct mptcp_ext *mpext = NULL;
- struct page *page = NULL;
+ struct page_frag *pfrag;
struct sk_buff *skb;
struct sock *ssk;
size_t psize;
+ long timeo;
pr_debug("msk=%p", msk);
if (msk->subflow) {
@@ -80,33 +81,33 @@ static int mptcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
goto put_out;
}
- /* Initial experiment: new page per send. Real code will
- * maintain list of active pages and DSS mappings, append to the
- * end and honor zerocopy
+ lock_sock(sk);
+ lock_sock(ssk);
+ timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT);
+
+ /* use the mptcp page cache so that we can easily move the data
+ * from one substream to another, but do per subflow memory accounting
*/
- page = alloc_page(GFP_KERNEL);
- if (!page) {
- ret = -ENOMEM;
- goto put_out;
+ pfrag = sk_page_frag(sk);
+ while (!sk_page_frag_refill(ssk, pfrag)) {
+ ret = sk_stream_wait_memory(ssk, &timeo);
+ if (ret)
+ goto release_out;
}
/* Copy to page */
- poffset = 0;
+ poffset = pfrag->offset;
pr_debug("left=%zu", msg_data_left(msg));
- psize = copy_page_from_iter(page, poffset,
+ psize = copy_page_from_iter(pfrag->page, poffset,
min_t(size_t, msg_data_left(msg),
- PAGE_SIZE),
+ pfrag->size - poffset),
&msg->msg_iter);
pr_debug("left=%zu", msg_data_left(msg));
-
if (!psize) {
ret = -EINVAL;
- goto put_out;
+ goto release_out;
}
- lock_sock(sk);
- lock_sock(ssk);
-
/* Mark the end of the previous write so the beginning of the
* next write (with its own mptcp skb extension data) is not
* collapsed.
@@ -116,8 +117,8 @@ static int mptcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
TCP_SKB_CB(skb)->eor = 1;
mss_now = tcp_send_mss(ssk, &size_goal, msg->msg_flags);
-
- ret = do_tcp_sendpages(ssk, page, poffset, min_t(int, size_goal, psize),
+ psize = min_t(int, size_goal, psize);
+ ret = do_tcp_sendpages(ssk, pfrag->page, poffset, psize,
msg->msg_flags | MSG_SENDPAGE_NOTLAST);
if (ret <= 0)
goto release_out;
@@ -143,6 +144,7 @@ static int mptcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
mpext->checksum, mpext->dsn64);
} /* TODO: else fallback */
+ pfrag->offset += ret;
msk->write_seq += ret;
subflow_ctx(ssk)->rel_write_seq += ret;
@@ -153,9 +155,6 @@ static int mptcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
release_sock(sk);
put_out:
- if (page)
- put_page(page);
-
sock_put(ssk);
return ret;
}
--
2.22.0
^ permalink raw reply related
* [RFC PATCH net-next 24/33] mptcp: selftests: Add capture option
From: Mat Martineau @ 2019-06-17 22:57 UTC (permalink / raw)
To: edumazet, netdev
Cc: Mat Martineau, cpaasch, fw, pabeni, peter.krystad, dcaratti,
matthieu.baerts
In-Reply-To: <20190617225808.665-1-mathew.j.martineau@linux.intel.com>
Added a "-c" command line option for mptcp_connect.sh to make it easier
to capture packets from each test. The script will use tcpdump to create
one .pcap file per test case, named according to the namespaces,
protocols, and connect address in use. For example, the first test case
writes the capture to ns1-ns1-MPTCP-MPTCP-10.0.1.1.pcap
The stderr output from tcpdump is printed after the test completes to
show tcpdump's "packets dropped by kernel" information.
Signed-off-by: Mat Martineau <mathew.j.martineau@linux.intel.com>
---
.../selftests/net/mptcp/mptcp_connect.sh | 33 +++++++++++++++++++
1 file changed, 33 insertions(+)
diff --git a/tools/testing/selftests/net/mptcp/mptcp_connect.sh b/tools/testing/selftests/net/mptcp/mptcp_connect.sh
index e694dc9d312c..4418163af001 100755
--- a/tools/testing/selftests/net/mptcp/mptcp_connect.sh
+++ b/tools/testing/selftests/net/mptcp/mptcp_connect.sh
@@ -7,6 +7,7 @@ sout=""
cin=""
cout=""
ksft_skip=4
+capture=0
timeout=30
TEST_COUNT=0
@@ -15,12 +16,19 @@ cleanup()
{
rm -f "$cin" "$cout"
rm -f "$sin" "$sout"
+ rm -f "$capout"
for i in 1 2 3 4; do
ip netns del ns$i
done
}
+for arg in "$@"; do
+ if [ "$arg" = "-c" ]; then
+ capture=1
+ fi
+done
+
ip -Version > /dev/null 2>&1
if [ $? -ne 0 ];then
echo "SKIP: Could not run test without ip tool"
@@ -31,6 +39,7 @@ sin=$(mktemp)
sout=$(mktemp)
cin=$(mktemp)
cout=$(mktemp)
+capout=$(mktemp)
trap cleanup EXIT
for i in 1 2 3 4;do
@@ -123,9 +132,25 @@ do_transfer()
:> "$cout"
:> "$sout"
+ :> "$capout"
printf "%-4s %-5s -> %-4s (%s:%d) %-5s\t" ${connector_ns} ${cl_proto} ${listener_ns} ${connect_addr} ${port} ${srv_proto}
+ if [ $capture -eq 1 ]; then
+ if [ -z $SUDO_USER ] ; then
+ capuser=""
+ else
+ capuser="-Z $SUDO_USER"
+ fi
+
+ capfile="${listener_ns}-${connector_ns}-${cl_proto}-${srv_proto}-${connect_addr}.pcap"
+
+ ip netns exec ${listener_ns} tcpdump -i any -s 65535 -B 32768 $capuser -w $capfile > "$capout" 2>&1 &
+ cappid=$!
+
+ sleep 1
+ fi
+
ip netns exec ${listener_ns} ./mptcp_connect -t $timeout -l -p $port -s ${srv_proto} 0.0.0.0 < "$sin" > "$sout" &
spid=$!
@@ -139,6 +164,11 @@ do_transfer()
wait $spid
rets=$?
+ if [ $capture -eq 1 ]; then
+ sleep 1
+ kill $cappid
+ fi
+
if [ ${rets} -ne 0 ] || [ ${retc} -ne 0 ]; then
echo "[ FAIL ] client exit code $retc, server $rets" 1>&2
echo "\nnetns ${listener_ns} socket stat for $port:" 1>&2
@@ -146,6 +176,7 @@ do_transfer()
echo "\nnetns ${connector_ns} socket stat for $port:" 1>&2
ip netns exec ${connector_ns} ss -nita 1>&2 -o "dport = :$port"
+ cat "$capout"
return 1
fi
@@ -156,9 +187,11 @@ do_transfer()
if [ $retc -eq 0 ] && [ $rets -eq 0 ];then
echo "[ OK ]"
+ cat "$capout"
return 0
fi
+ cat "$capout"
return 1
}
--
2.22.0
^ permalink raw reply related
* [RFC PATCH net-next 23/33] mptcp: selftests: switch to netns+veth based tests
From: Mat Martineau @ 2019-06-17 22:57 UTC (permalink / raw)
To: edumazet, netdev
Cc: Florian Westphal, cpaasch, pabeni, peter.krystad, dcaratti,
matthieu.baerts
In-Reply-To: <20190617225808.665-1-mathew.j.martineau@linux.intel.com>
From: Florian Westphal <fw@strlen.de>
... so we can exercise PMTU and MSS handling.
MTU on lo is 64k, so we never had to deal with segmentation either.
This also avoids problems with timewait state in inet_ns,
all net namespaces are torn down before script exits.
This uncovers several bugs:
1. mptcp_init_sock() passes init_net instead of sock_net(sk), i.e.
network namespaces are not supported
2. We corrupt tcp option space, I can see invalid tcp headers (too
short) in tcpdump, and receiver process hangs without getting the
data (poll timeout).
Seems to be gone after adding
if (size == MAX_TCP_OPTION_SPACE)
return size;
BUG(size > MAX_TCP_OPTION_SPACE);
in tcp_established_options() after writing mptcp options.
SACK writing doesn't handle 'no option space left' case.
3. Several WARN_ON from networking core are triggered, e.g. due
to mem accounting being off. Maybe fixed already with pending
locking fix in mptcp_recv.
4. "Replaced mapping before it was done" in dmesg.
5. receiver blocking in read(), while TCP socket is in CLOSE-WAIT.
wait_woken+0xd6/0x170
sk_wait_data+0x248/0x270
mptcp_recvmsg+0x5c0/0xd50
6. kmemleak gets noisy, we probably leak a refcount somewhere (iirc
Davide is already working on this).
7. crash on connect completion, probably same bug that Paolo reported
already.
As the script did not yet turn up any problem when using only
tcp, it appears these are MPTCP related bugs rather than with script
or mptcp_connect.c .
Once above issues are fixed, this will be extended again to
set different/varying MTU in ns1 and ns4.
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
---
.../selftests/net/mptcp/mptcp_connect.c | 310 +++++++++---------
.../selftests/net/mptcp/mptcp_connect.sh | 246 ++++++++++++--
2 files changed, 382 insertions(+), 174 deletions(-)
diff --git a/tools/testing/selftests/net/mptcp/mptcp_connect.c b/tools/testing/selftests/net/mptcp/mptcp_connect.c
index 78c43624e84f..cac71f0ac8f8 100644
--- a/tools/testing/selftests/net/mptcp/mptcp_connect.c
+++ b/tools/testing/selftests/net/mptcp/mptcp_connect.c
@@ -26,16 +26,19 @@ extern int optind;
#define IPPROTO_MPTCP 262
#endif
+static bool listen_mode;
+static int poll_timeout;
+
static const char *cfg_host;
static const char *cfg_port = "12000";
-static int cfg_server_proto = IPPROTO_MPTCP;
-static int cfg_client_proto = IPPROTO_MPTCP;
+static int cfg_sock_proto = IPPROTO_MPTCP;
+
static void die_usage(void)
{
- fprintf(stderr, "Usage: mptcp_connect [-c MPTCP|TCP] [-p port] "
- "[-s MPTCP|TCP]\n");
- exit(-1);
+ fprintf(stderr, "Usage: mptcp_connect [-s MPTCP|TCP] [-p port] "
+ "[ -l ] [ -t timeout ] connect_address\n");
+ exit(1);
}
static const char *getxinfo_strerr(int err)
@@ -79,11 +82,9 @@ static int sock_listen_mptcp(const char * const listenaddr,
xgetaddrinfo(listenaddr, port, &hints, &addr);
for (a = addr; a; a = a->ai_next) {
- sock = socket(a->ai_family, a->ai_socktype, cfg_server_proto);
- if (sock < 0) {
- perror("socket");
+ sock = socket(a->ai_family, a->ai_socktype, cfg_sock_proto);
+ if (sock < 0)
continue;
- }
if (-1 == setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one,
sizeof(one)))
@@ -97,10 +98,19 @@ static int sock_listen_mptcp(const char * const listenaddr,
sock = -1;
}
- if (sock >= 0 && listen(sock, 20))
+ freeaddrinfo(addr);
+
+ if (sock < 0) {
+ fprintf(stderr, "Could not create listen socket\n");
+ return sock;
+ }
+
+ if (listen(sock, 20)) {
perror("listen");
+ close(sock);
+ return -1;
+ }
- freeaddrinfo(addr);
return sock;
}
@@ -136,7 +146,7 @@ static int sock_connect_mptcp(const char * const remoteaddr,
return sock;
}
-static size_t do_write(const int fd, char *buf, const size_t len)
+static size_t do_rnd_write(const int fd, char *buf, const size_t len)
{
size_t offset = 0;
@@ -161,62 +171,149 @@ static size_t do_write(const int fd, char *buf, const size_t len)
return offset;
}
-static void copyfd_io(int peerfd)
+static size_t do_write(const int fd, char *buf, const size_t len)
{
- struct pollfd fds = { .events = POLLIN };
+ size_t offset = 0;
- fds.fd = peerfd;
+ while (offset < len) {
+ size_t written;
+ ssize_t bw;
+
+ bw = write(fd, buf + offset, len - offset);
+ if (bw < 0) {
+ perror("write");
+ return 0;
+ }
+
+ written = (size_t)bw;
+ offset += written;
+ }
+
+ return offset;
+}
+
+static ssize_t do_rnd_read(const int fd, char *buf, const size_t len)
+{
+ size_t cap = rand();
+
+ cap &= 0xffff;
+
+ if (cap == 0)
+ cap = 1;
+ else if (cap > len)
+ cap = len;
+
+ return read(fd, buf, cap);
+}
+
+static int copyfd_io(int infd, int peerfd, int outfd)
+{
+ struct pollfd fds = {
+ .fd = peerfd,
+ .events = POLLIN | POLLOUT,
+ };
for (;;) {
- char buf[4096];
+ char buf[8192];
ssize_t len;
- switch (poll(&fds, 1, -1)) {
+ if (fds.events == 0)
+ break;
+
+ switch (poll(&fds, 1, poll_timeout)) {
case -1:
if (errno == EINTR)
continue;
perror("poll");
- return;
+ return 1;
case 0:
- /* should not happen, we requested infinite wait */
- fputs("Timed out?!", stderr);
- return;
+ fprintf(stderr, "%s: poll timed out (events: "
+ "POLLIN %u, POLLOUT %u)\n", __func__,
+ fds.events & POLLIN, fds.events & POLLOUT);
+ return 2;
}
- if ((fds.revents & POLLIN) == 0)
- return;
+ if (fds.revents & POLLIN) {
+ len = do_rnd_read(peerfd, buf, sizeof(buf));
+ if (len == 0) {
+ /* no more data to receive:
+ * peer has closed its write side
+ */
+ fds.events &= ~POLLIN;
- len = read(peerfd, buf, sizeof(buf));
- if (!len)
- return;
- if (len < 0) {
- if (errno == EINTR)
- continue;
+ if ((fds.events & POLLOUT) == 0)
+ /* and nothing more to send */
+ break;
+
+ /* Else, still have data to transmit */
+ } else if (len < 0) {
+ perror("read");
+ return 3;
+ }
- perror("read");
- return;
+ do_write(outfd, buf, len);
}
- if (!do_write(peerfd, buf, len))
- return;
+ if (fds.revents & POLLOUT) {
+ len = do_rnd_read(infd, buf, sizeof(buf));
+ if (len > 0) {
+ if (!do_rnd_write(peerfd, buf, len))
+ return 111;
+ } else if (len == 0) {
+ /* We have no more data to send. */
+ fds.events &= ~POLLOUT;
+
+ if ((fds.events & POLLIN) == 0)
+ /* ... and peer also closed already */
+ break;
+
+ /* ... but we still receive.
+ * Close our write side.
+ */
+ shutdown(peerfd, SHUT_WR);
+ } else {
+ if (errno == EINTR)
+ continue;
+ perror("read");
+ return 4;
+ }
+ }
}
+
+ close(peerfd);
+ return 0;
}
int main_loop_s(int listensock)
{
struct sockaddr_storage ss;
+ struct pollfd polls;
socklen_t salen;
int remotesock;
+ polls.fd = listensock;
+ polls.events = POLLIN;
+
+ switch (poll(&polls, 1, poll_timeout)) {
+ case -1:
+ perror("poll");
+ return 1;
+ case 0:
+ fprintf(stderr, "%s: timed out\n", __func__);
+ close(listensock);
+ return 2;
+ }
+
salen = sizeof(ss);
- while ((remotesock = accept(listensock, (struct sockaddr *)&ss,
- &salen)) < 0)
- perror("accept");
+ remotesock = accept(listensock, (struct sockaddr *)&ss, &salen);
+ if (remotesock >= 0) {
+ copyfd_io(0, remotesock, 1);
+ return 0;
+ }
- copyfd_io(remotesock);
- close(remotesock);
+ perror("accept");
- return 0;
+ return 1;
}
static void init_rng(void)
@@ -225,7 +322,10 @@ static void init_rng(void)
unsigned int foo;
if (fd > 0) {
- read(fd, &foo, sizeof(foo));
+ int ret = read(fd, &foo, sizeof(foo));
+
+ if (ret < 0)
+ srand(fd + foo);
close(fd);
}
@@ -234,113 +334,14 @@ static void init_rng(void)
int main_loop(void)
{
- int pollfds = 2, timeout = -1;
- char start[32];
- int pipefd[2];
- ssize_t ret;
int fd;
- if (pipe(pipefd)) {
- perror("pipe");
- exit(1);
- }
-
- switch (fork()) {
- case 0:
- close(pipefd[0]);
-
- init_rng();
-
- fd = sock_listen_mptcp(NULL, cfg_port);
- if (fd < 0)
- return -1;
-
- write(pipefd[1], "RDY\n", 4);
- main_loop_s(fd);
- exit(1);
- case -1:
- perror("fork");
- return -1;
- default:
- close(pipefd[1]);
- break;
- }
-
- init_rng();
- ret = read(pipefd[0], start, (int)sizeof(start));
- if (ret < 0) {
- perror("read");
- return -1;
- }
-
- if (ret != 4 || strcmp(start, "RDY\n"))
- return -1;
-
/* listener is ready. */
- fd = sock_connect_mptcp(cfg_host, cfg_port, cfg_client_proto);
+ fd = sock_connect_mptcp(cfg_host, cfg_port, cfg_sock_proto);
if (fd < 0)
- return -1;
-
- for (;;) {
- struct pollfd fds[2];
- char buf[4096];
- ssize_t len;
-
- fds[0].fd = fd;
- fds[0].events = POLLIN;
- fds[1].fd = 0;
- fds[1].events = POLLIN;
- fds[1].revents = 0;
-
- switch (poll(fds, pollfds, timeout)) {
- case -1:
- if (errno == EINTR)
- continue;
- perror("poll");
- return -1;
- case 0:
- close(fd);
- return 0;
- }
-
- if (fds[0].revents & POLLIN) {
- unsigned int blen = rand();
-
- blen %= sizeof(buf);
-
- ++blen;
- len = read(fd, buf, blen);
- if (len < 0) {
- perror("read");
- return -1;
- }
-
- if (len > blen) {
- fprintf(stderr, "read returned more data than "
- "buffer length\n");
- len = blen;
- }
-
- write(1, buf, len);
- }
- if (fds[1].revents & POLLIN) {
- len = read(0, buf, sizeof(buf));
- if (len == 0) {
- pollfds = 1;
- timeout = 1000;
- continue;
- }
+ return 2;
- if (len < 0) {
- perror("read");
- break;
- }
-
- do_write(fd, buf, len);
- }
- }
-
- return 1;
+ return copyfd_io(0, fd, 1);
}
int parse_proto(const char *proto)
@@ -349,6 +350,8 @@ int parse_proto(const char *proto)
return IPPROTO_MPTCP;
if (!strcasecmp(proto, "TCP"))
return IPPROTO_TCP;
+
+ fprintf(stderr, "Unknown protocol: %s.", proto);
die_usage();
/* silence compiler warning */
@@ -359,20 +362,25 @@ static void parse_opts(int argc, char **argv)
{
int c;
- while ((c = getopt(argc, argv, "c:p:s:h")) != -1) {
+ while ((c = getopt(argc, argv, "lp:s:ht:")) != -1) {
switch (c) {
- case 'c':
- cfg_client_proto = parse_proto(optarg);
+ case 'l':
+ listen_mode = true;
break;
case 'p':
cfg_port = optarg;
break;
case 's':
- cfg_server_proto = parse_proto(optarg);
+ cfg_sock_proto = parse_proto(optarg);
break;
case 'h':
die_usage();
break;
+ case 't':
+ poll_timeout = atoi(optarg) * 1000;
+ if (poll_timeout <= 0)
+ poll_timeout = -1;
+ break;
}
}
@@ -386,5 +394,15 @@ int main(int argc, char *argv[])
init_rng();
parse_opts(argc, argv);
+
+ if (listen_mode) {
+ int fd = sock_listen_mptcp(cfg_host, cfg_port);
+
+ if (fd < 0)
+ return 1;
+
+ return main_loop_s(fd);
+ }
+
return main_loop();
}
diff --git a/tools/testing/selftests/net/mptcp/mptcp_connect.sh b/tools/testing/selftests/net/mptcp/mptcp_connect.sh
index efcdda84b62a..e694dc9d312c 100755
--- a/tools/testing/selftests/net/mptcp/mptcp_connect.sh
+++ b/tools/testing/selftests/net/mptcp/mptcp_connect.sh
@@ -1,48 +1,238 @@
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
-tmpin=$(mktemp)
-tmpout=$(mktemp)
+ret=0
+sin=""
+sout=""
+cin=""
+cout=""
+ksft_skip=4
+timeout=30
+
+TEST_COUNT=0
cleanup()
{
- rm -f "$tmpin" "$tmpout"
+ rm -f "$cin" "$cout"
+ rm -f "$sin" "$sout"
+
+ for i in 1 2 3 4; do
+ ip netns del ns$i
+ done
+}
+
+ip -Version > /dev/null 2>&1
+if [ $? -ne 0 ];then
+ echo "SKIP: Could not run test without ip tool"
+ exit $ksft_skip
+fi
+
+sin=$(mktemp)
+sout=$(mktemp)
+cin=$(mktemp)
+cout=$(mktemp)
+trap cleanup EXIT
+
+for i in 1 2 3 4;do
+ ip netns add ns$i || exit $ksft_skip
+ ip -net ns$i link set lo up
+done
+
+# ns1 ns2 ns3 ns4
+# ns1eth2 ns2eth1 ns2eth3 ns3eth2 ns3eth4 ns4eth3
+# - drop 1% -> reorder 25%
+# <- TSO off -
+
+ip link add ns1eth2 netns ns1 type veth peer name ns2eth1 netns ns2
+ip link add ns2eth3 netns ns2 type veth peer name ns3eth2 netns ns3
+ip link add ns3eth4 netns ns3 type veth peer name ns4eth3 netns ns4
+
+ip -net ns1 addr add 10.0.1.1/24 dev ns1eth2
+ip -net ns1 link set ns1eth2 up
+ip -net ns1 route add default via 10.0.1.2
+
+ip -net ns2 addr add 10.0.1.2/24 dev ns2eth1
+ip -net ns2 link set ns2eth1 up
+
+ip -net ns2 addr add 10.0.2.1/24 dev ns2eth3
+ip -net ns2 link set ns2eth3 up
+ip -net ns2 route add default via 10.0.2.2
+ip netns exec ns2 sysctl -q net.ipv4.ip_forward=1
+
+ip -net ns3 addr add 10.0.2.2/24 dev ns3eth2
+ip -net ns3 link set ns3eth2 up
+
+ip -net ns3 addr add 10.0.3.2/24 dev ns3eth4
+ip -net ns3 link set ns3eth4 up
+ip -net ns3 route add default via 10.0.2.1
+ip netns exec ns3 ethtool -K ns3eth2 tso off 2>/dev/null
+ip netns exec ns3 sysctl -q net.ipv4.ip_forward=1
+
+ip -net ns4 addr add 10.0.3.1/24 dev ns4eth3
+ip -net ns4 link set ns4eth3 up
+ip -net ns4 route add default via 10.0.3.2
+
+print_file_err()
+{
+ ls -l "$1" 1>&2
+ echo "Trailing bytes are: "
+ tail -c 27 "$1"
}
check_transfer()
{
- cl_proto=${1}
- srv_proto=${2}
+ in=$1
+ out=$2
+ what=$3
- printf "%-8s -> %-8s socket\t\t" ${cl_proto} ${srv_proto}
+ cmp "$in" "$out" > /dev/null 2>&1
+ if [ $? -ne 0 ] ;then
+ echo "[ FAIL ] $what does not match (in, out):"
+ print_file_err "$in"
+ print_file_err "$out"
- ./mptcp_connect -c ${cl_proto} -p 43212 -s ${srv_proto} 127.0.0.1 < "$tmpin" > "$tmpout" 2>/dev/null
- ret=$?
- if [ ${ret} -ne 0 ]; then
- echo "[ FAIL ]"
- echo " exit code ${ret}"
- return ${ret}
+ return 1
fi
- cmp "$tmpin" "$tmpout" > /dev/null 2>&1
- if [ $? -ne 0 ]; then
- echo "[ FAIL ]"
- ls -l "$tmpin" "$tmpout" 1>&2
- else
- echo "[ OK ]"
+
+ return 0
+}
+
+do_ping()
+{
+ listener_ns="$1"
+ connector_ns="$2"
+ connect_addr="$3"
+
+ ip netns exec ${connector_ns} ping -q -c 1 $connect_addr >/dev/null
+ if [ $? -ne 0 ] ; then
+ echo "$listener_ns -> $connect_addr connectivity [ FAIL ]" 1>&2
+ ret=1
fi
}
-trap cleanup EXIT
+do_transfer()
+{
+ listener_ns="$1"
+ connector_ns="$2"
+ cl_proto="$3"
+ srv_proto="$4"
+ connect_addr="$5"
-SIZE=$((RANDOM % (1024 * 1024)))
-if [ $SIZE -eq 0 ]; then
- SIZE=1
-fi
+ port=$((10000+$TEST_COUNT))
+ TEST_COUNT=$((TEST_COUNT+1))
+
+ :> "$cout"
+ :> "$sout"
+
+ printf "%-4s %-5s -> %-4s (%s:%d) %-5s\t" ${connector_ns} ${cl_proto} ${listener_ns} ${connect_addr} ${port} ${srv_proto}
+
+ ip netns exec ${listener_ns} ./mptcp_connect -t $timeout -l -p $port -s ${srv_proto} 0.0.0.0 < "$sin" > "$sout" &
+ spid=$!
+
+ sleep 1
+
+ ip netns exec ${connector_ns} ./mptcp_connect -t $timeout -p $port -s ${cl_proto} $connect_addr < "$cin" > "$cout" &
+ cpid=$!
+
+ wait $cpid
+ retc=$?
+ wait $spid
+ rets=$?
+
+ if [ ${rets} -ne 0 ] || [ ${retc} -ne 0 ]; then
+ echo "[ FAIL ] client exit code $retc, server $rets" 1>&2
+ echo "\nnetns ${listener_ns} socket stat for $port:" 1>&2
+ ip netns exec ${listener_ns} ss -nita 1>&2 -o "sport = :$port"
+ echo "\nnetns ${connector_ns} socket stat for $port:" 1>&2
+ ip netns exec ${connector_ns} ss -nita 1>&2 -o "dport = :$port"
+
+ return 1
+ fi
+
+ check_transfer $sin $cout "file received by client"
+ retc=$?
+ check_transfer $cin $sout "file received by server"
+ rets=$?
+
+ if [ $retc -eq 0 ] && [ $rets -eq 0 ];then
+ echo "[ OK ]"
+ return 0
+ fi
+
+ return 1
+}
+
+make_file()
+{
+ name=$1
+ who=$2
+
+ SIZE=$((RANDOM % (1024 * 8)))
+ TSIZE=$((SIZE * 1024))
+
+ dd if=/dev/urandom of="$name" bs=1024 count=$SIZE 2> /dev/null
+
+ SIZE=$((RANDOM % 1024))
+ SIZE=$((SIZE + 128))
+ TSIZE=$((TSIZE + SIZE))
+ dd if=/dev/urandom conf=notrunc of="$name" bs=1 count=$SIZE 2> /dev/null
+ echo -e "\nMPTCP_TEST_FILE_END_MARKER" >> "$name"
+
+ echo "Created $name (size $TSIZE) containing data sent by $who"
+}
+
+run_tests()
+{
+ listener_ns="$1"
+ connector_ns="$2"
+ connect_addr="$3"
+ lret=0
+
+ for proto in MPTCP TCP;do
+ do_transfer ${listener_ns} ${connector_ns} MPTCP "$proto" ${connect_addr}
+ lret=$?
+ if [ $lret -ne 0 ]; then
+ ret=$lret
+ return
+ fi
+ done
+
+ do_transfer ${listener_ns} ${connector_ns} TCP MPTCP ${connect_addr}
+ lret=$?
+ if [ $lret -ne 0 ]; then
+ ret=$lret
+ return
+ fi
+}
+
+make_file "$cin" "client"
+make_file "$sin" "server"
+
+for sender in 1 2 3 4;do
+ do_ping ns1 ns$sender 10.0.1.1
+
+ do_ping ns2 ns$sender 10.0.1.2
+ do_ping ns2 ns$sender 10.0.2.1
+
+ do_ping ns3 ns$sender 10.0.2.2
+ do_ping ns3 ns$sender 10.0.3.2
+
+ do_ping ns4 ns$sender 10.0.3.1
+done
+
+tc -net ns2 qdisc add dev ns2eth3 root netem loss random 1
+tc -net ns3 qdisc add dev ns3eth4 root netem delay 10ms reorder 25% 50% gap 5
+
+for sender in 1 2 3 4;do
+ run_tests ns1 ns$sender 10.0.1.1
+
+ run_tests ns2 ns$sender 10.0.1.2
+ run_tests ns2 ns$sender 10.0.2.1
-dd if=/dev/urandom of="$tmpin" bs=1 count=$SIZE 2> /dev/null
+ run_tests ns3 ns$sender 10.0.2.2
+ run_tests ns3 ns$sender 10.0.3.2
-check_transfer MPTCP MPTCP
-check_transfer MPTCP TCP
-check_transfer TCP MPTCP
+ run_tests ns4 ns$sender 10.0.3.1
+done
-exit 0
+exit $ret
--
2.22.0
^ permalink raw reply related
* [RFC PATCH net-next 11/33] mptcp: Add key generation and token tree
From: Mat Martineau @ 2019-06-17 22:57 UTC (permalink / raw)
To: edumazet, netdev
Cc: Peter Krystad, cpaasch, fw, pabeni, dcaratti, matthieu.baerts
In-Reply-To: <20190617225808.665-1-mathew.j.martineau@linux.intel.com>
From: Peter Krystad <peter.krystad@linux.intel.com>
Generate the local keys, IDSN, and token when creating a new
socket. Introduce the token tree to track all tokens in use using
a radix tree with the MPTCP token itself as the index.
Signed-off-by: Peter Krystad <peter.krystad@linux.intel.com>
---
net/mptcp/Makefile | 2 +-
net/mptcp/crypto.c | 206 +++++++++++++++++++++++++++++++++++
net/mptcp/protocol.c | 17 +++
net/mptcp/protocol.h | 26 +++++
net/mptcp/subflow.c | 36 ++++++-
net/mptcp/token.c | 248 +++++++++++++++++++++++++++++++++++++++++++
6 files changed, 533 insertions(+), 2 deletions(-)
create mode 100644 net/mptcp/crypto.c
create mode 100644 net/mptcp/token.c
diff --git a/net/mptcp/Makefile b/net/mptcp/Makefile
index e1ee5aade8b0..178ae81d8b66 100644
--- a/net/mptcp/Makefile
+++ b/net/mptcp/Makefile
@@ -1,4 +1,4 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_MPTCP) += mptcp.o
-mptcp-y := protocol.o subflow.o options.o
+mptcp-y := protocol.o subflow.o options.o token.o crypto.o
diff --git a/net/mptcp/crypto.c b/net/mptcp/crypto.c
new file mode 100644
index 000000000000..26a68aa1933a
--- /dev/null
+++ b/net/mptcp/crypto.c
@@ -0,0 +1,206 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Multipath TCP cryptographic functions
+ * Copyright (c) 2017 - 2019, Intel Corporation.
+ *
+ * Note: This code is based on mptcp_ctrl.c, mptcp_ipv4.c, and
+ * mptcp_ipv6 from multipath-tcp.org, authored by:
+ *
+ * Sébastien Barré <sebastien.barre@uclouvain.be>
+ * Christoph Paasch <christoph.paasch@uclouvain.be>
+ * Jaakko Korkeaniemi <jaakko.korkeaniemi@aalto.fi>
+ * Gregory Detal <gregory.detal@uclouvain.be>
+ * Fabien Duchêne <fabien.duchene@uclouvain.be>
+ * Andreas Seelinger <Andreas.Seelinger@rwth-aachen.de>
+ * Lavkesh Lahngir <lavkesh51@gmail.com>
+ * Andreas Ripke <ripke@neclab.eu>
+ * Vlad Dogaru <vlad.dogaru@intel.com>
+ * Octavian Purdila <octavian.purdila@intel.com>
+ * John Ronan <jronan@tssg.org>
+ * Catalin Nicutar <catalin.nicutar@gmail.com>
+ * Brandon Heller <brandonh@stanford.edu>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/cryptohash.h>
+#include <linux/random.h>
+#include <linux/siphash.h>
+#include <asm/unaligned.h>
+
+static siphash_key_t crypto_key_secret __read_mostly;
+static hsiphash_key_t crypto_nonce_secret __read_mostly;
+static u32 crypto_seed;
+
+u32 crypto_v4_get_nonce(__be32 saddr, __be32 daddr, __be16 sport, __be16 dport)
+{
+ return hsiphash_4u32((__force u32)saddr, (__force u32)daddr,
+ (__force u32)sport << 16 | (__force u32)dport,
+ crypto_seed++, &crypto_nonce_secret);
+}
+
+u64 crypto_v4_get_key(__be32 saddr, __be32 daddr, __be16 sport, __be16 dport)
+{
+ pr_debug("src=%x:%d, dst=%x:%d", saddr, sport, daddr, dport);
+ return siphash_4u32((__force u32)saddr, (__force u32)daddr,
+ (__force u32)sport << 16 | (__force u32)dport,
+ crypto_seed++, &crypto_key_secret);
+}
+
+u32 crypto_v6_get_nonce(const struct in6_addr *saddr,
+ const struct in6_addr *daddr,
+ __be16 sport, __be16 dport)
+{
+ const struct {
+ struct in6_addr saddr;
+ struct in6_addr daddr;
+ u32 seed;
+ __be16 sport;
+ __be16 dport;
+ } __aligned(SIPHASH_ALIGNMENT) combined = {
+ .saddr = *saddr,
+ .daddr = *daddr,
+ .seed = crypto_seed++,
+ .sport = sport,
+ .dport = dport,
+ };
+
+ return hsiphash(&combined, offsetofend(typeof(combined), dport),
+ &crypto_nonce_secret);
+}
+
+u64 crypto_v6_get_key(const struct in6_addr *saddr,
+ const struct in6_addr *daddr,
+ __be16 sport, __be16 dport)
+{
+ const struct {
+ struct in6_addr saddr;
+ struct in6_addr daddr;
+ u32 seed;
+ __be16 sport;
+ __be16 dport;
+ } __aligned(SIPHASH_ALIGNMENT) combined = {
+ .saddr = *saddr,
+ .daddr = *daddr,
+ .seed = crypto_seed++,
+ .sport = sport,
+ .dport = dport,
+ };
+
+ return siphash(&combined, offsetofend(typeof(combined), dport),
+ &crypto_key_secret);
+}
+
+void crypto_key_sha1(u64 key, u32 *token, u64 *idsn)
+{
+ u32 workspace[SHA_WORKSPACE_WORDS];
+ u32 mptcp_hashed_key[SHA_DIGEST_WORDS];
+ u8 input[64];
+
+ memset(workspace, 0, sizeof(workspace));
+
+ /* Initialize input with appropriate padding */
+ memset(&input[9], 0, sizeof(input) - 10); /* -10, because the last byte
+ * is explicitly set too
+ */
+ put_unaligned_be64(key, input);
+ input[8] = 0x80; /* Padding: First bit after message = 1 */
+ input[63] = 0x40; /* Padding: Length of the message = 64 bits */
+
+ sha_init(mptcp_hashed_key);
+ sha_transform(mptcp_hashed_key, input, workspace);
+
+ if (token)
+ *token = mptcp_hashed_key[0];
+ if (idsn)
+ *idsn = ((u64)mptcp_hashed_key[3] << 32) + mptcp_hashed_key[4];
+}
+
+void crypto_hmac_sha1(u64 key1, u64 key2, u32 *hash_out,
+ int arg_num, ...)
+{
+ u32 workspace[SHA_WORKSPACE_WORDS];
+ u8 input[128]; /* 2 512-bit blocks */
+ int i;
+ int index;
+ int length;
+ u8 *msg;
+ va_list list;
+ u8 key_1[8];
+ u8 key_2[8];
+
+ memset(workspace, 0, sizeof(workspace));
+
+ put_unaligned_be64(key1, key_1);
+ put_unaligned_be64(key2, key_2);
+
+ /* Generate key xored with ipad */
+ memset(input, 0x36, 64);
+ for (i = 0; i < 8; i++)
+ input[i] ^= key_1[i];
+ for (i = 0; i < 8; i++)
+ input[i + 8] ^= key_2[i];
+
+ va_start(list, arg_num);
+ index = 64;
+ for (i = 0; i < arg_num; i++) {
+ length = va_arg(list, int);
+ msg = va_arg(list, u8 *);
+ WARN_ON(index + length > 125); /* Message is too long */
+ memcpy(&input[index], msg, length);
+ index += length;
+ }
+ va_end(list);
+
+ input[index] = 0x80; /* Padding: First bit after message = 1 */
+ memset(&input[index + 1], 0, (126 - index));
+
+ /* Padding: Length of the message = 512 + message length (bits) */
+ input[126] = 0x02;
+ input[127] = ((index - 64) * 8); /* Message length (bits) */
+
+ sha_init(hash_out);
+ sha_transform(hash_out, input, workspace);
+ memset(workspace, 0, sizeof(workspace));
+
+ sha_transform(hash_out, &input[64], workspace);
+ memset(workspace, 0, sizeof(workspace));
+
+ for (i = 0; i < 5; i++)
+ hash_out[i] = (__force u32)cpu_to_be32(hash_out[i]);
+
+ /* Prepare second part of hmac */
+ memset(input, 0x5C, 64);
+ for (i = 0; i < 8; i++)
+ input[i] ^= key_1[i];
+ for (i = 0; i < 8; i++)
+ input[i + 8] ^= key_2[i];
+
+ memcpy(&input[64], hash_out, 20);
+ input[84] = 0x80;
+ memset(&input[85], 0, 41);
+
+ /* Padding: Length of the message = 512 + 160 bits */
+ input[126] = 0x02;
+ input[127] = 0xA0;
+
+ sha_init(hash_out);
+ sha_transform(hash_out, input, workspace);
+ memset(workspace, 0, sizeof(workspace));
+
+ sha_transform(hash_out, &input[64], workspace);
+
+ for (i = 0; i < 5; i++)
+ hash_out[i] = (__force u32)cpu_to_be32(hash_out[i]);
+}
+
+void crypto_init(void)
+{
+ get_random_bytes((void *)&crypto_key_secret,
+ sizeof(crypto_key_secret));
+ get_random_bytes((void *)&crypto_nonce_secret,
+ sizeof(crypto_nonce_secret));
+ crypto_seed = 0;
+}
diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
index ea771f537ac0..2f340ef8e281 100644
--- a/net/mptcp/protocol.c
+++ b/net/mptcp/protocol.c
@@ -111,6 +111,9 @@ static struct sock *mptcp_accept(struct sock *sk, int flags, int *err,
if (subflow->mp_capable) {
msk->remote_key = subflow->remote_key;
msk->local_key = subflow->local_key;
+ msk->token = subflow->token;
+ pr_debug("token=%u", msk->token);
+ token_update_accept(new_sock->sk, new_mptcp_sock->sk);
msk->connection_list = new_sock;
} else {
msk->subflow = new_sock;
@@ -119,6 +122,15 @@ static struct sock *mptcp_accept(struct sock *sk, int flags, int *err,
return new_mptcp_sock->sk;
}
+static void mptcp_destroy(struct sock *sk)
+{
+ struct mptcp_sock *msk = mptcp_sk(sk);
+
+ pr_debug("msk=%p, subflow=%p", sk, msk->subflow->sk);
+
+ token_destroy(msk->token);
+}
+
static int mptcp_get_port(struct sock *sk, unsigned short snum)
{
struct mptcp_sock *msk = mptcp_sk(sk);
@@ -136,6 +148,8 @@ void mptcp_finish_connect(struct sock *sk, int mp_capable)
if (mp_capable) {
msk->remote_key = subflow->remote_key;
msk->local_key = subflow->local_key;
+ msk->token = subflow->token;
+ pr_debug("token=%u", msk->token);
msk->connection_list = msk->subflow;
msk->subflow = NULL;
}
@@ -149,6 +163,7 @@ static struct proto mptcp_prot = {
.close = mptcp_close,
.accept = mptcp_accept,
.shutdown = tcp_shutdown,
+ .destroy = mptcp_destroy,
.sendmsg = mptcp_sendmsg,
.recvmsg = mptcp_recvmsg,
.hash = inet_hash,
@@ -302,6 +317,8 @@ void __init mptcp_init(void)
mptcp_stream_ops.getname = mptcp_getname;
mptcp_stream_ops.listen = mptcp_listen;
+ token_init();
+ crypto_init();
subflow_init();
if (proto_register(&mptcp_prot, 1) != 0)
diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h
index 34eb10c279f0..5a8ed316d70e 100644
--- a/net/mptcp/protocol.h
+++ b/net/mptcp/protocol.h
@@ -35,6 +35,7 @@ struct mptcp_sock {
struct inet_connection_sock sk;
u64 local_key;
u64 remote_key;
+ u32 token;
struct socket *connection_list; /* @@ needs to be a list */
struct socket *subflow; /* outgoing connect, listener or !mp_capable */
};
@@ -53,6 +54,7 @@ struct subflow_request_sock {
version : 4;
u64 local_key;
u64 remote_key;
+ u32 token;
};
static inline
@@ -65,6 +67,7 @@ struct subflow_request_sock *subflow_rsk(const struct request_sock *rsk)
struct subflow_context {
u64 local_key;
u64 remote_key;
+ u32 token;
u32 request_mptcp : 1, /* send MP_CAPABLE */
request_cksum : 1,
mp_capable : 1, /* remote is MPTCP capable */
@@ -97,4 +100,27 @@ void mptcp_get_options(const struct sk_buff *skb,
void mptcp_finish_connect(struct sock *sk, int mp_capable);
+void token_init(void);
+void token_new_request(struct request_sock *req, const struct sk_buff *skb);
+void token_destroy_request(u32 token);
+void token_new_connect(struct sock *sk);
+void token_new_accept(struct sock *sk);
+void token_update_accept(struct sock *sk, struct sock *conn);
+void token_destroy(u32 token);
+
+void crypto_init(void);
+u32 crypto_v4_get_nonce(__be32 saddr, __be32 daddr,
+ __be16 sport, __be16 dport);
+u64 crypto_v4_get_key(__be32 saddr, __be32 daddr,
+ __be16 sport, __be16 dport);
+u64 crypto_v6_get_key(const struct in6_addr *saddr,
+ const struct in6_addr *daddr,
+ __be16 sport, __be16 dport);
+u32 crypto_v6_get_nonce(const struct in6_addr *saddr,
+ const struct in6_addr *daddr,
+ __be16 sport, __be16 dport);
+void crypto_key_sha1(u64 key, u32 *token, u64 *idsn);
+void crypto_hmac_sha1(u64 key1, u64 key2, u32 *hash_out,
+ int arg_num, ...);
+
#endif /* __MPTCP_PROTOCOL_H */
diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c
index fd2bf7621f0e..abae6a42a101 100644
--- a/net/mptcp/subflow.c
+++ b/net/mptcp/subflow.c
@@ -15,6 +15,29 @@
#include <net/mptcp.h>
#include "protocol.h"
+static int subflow_rebuild_header(struct sock *sk)
+{
+ struct subflow_context *subflow = subflow_ctx(sk);
+
+ if (subflow->request_mptcp && !subflow->token) {
+ pr_debug("subflow=%p", sk);
+ token_new_connect(sk);
+ }
+
+ return inet_sk_rebuild_header(sk);
+}
+
+static void subflow_req_destructor(struct request_sock *req)
+{
+ struct subflow_request_sock *subflow_req = subflow_rsk(req);
+
+ pr_debug("subflow_req=%p", subflow_req);
+
+ if (subflow_req->mp_capable)
+ token_destroy_request(subflow_req->token);
+ tcp_request_sock_ops.destructor(req);
+}
+
static void subflow_v4_init_req(struct request_sock *req,
const struct sock *sk_listener,
struct sk_buff *skb)
@@ -41,6 +64,8 @@ static void subflow_v4_init_req(struct request_sock *req,
listener->request_cksum)
subflow_req->checksum = 1;
subflow_req->remote_key = rx_opt.mptcp.sndr_key;
+ pr_debug("remote_key=%llu", subflow_req->remote_key);
+ token_new_request(req, skb);
} else {
subflow_req->mp_capable = 0;
}
@@ -107,12 +132,16 @@ static struct sock *subflow_syn_recv_sock(const struct sock *sk,
child = tcp_v4_syn_recv_sock(sk, skb, req, dst, req_unhash, own_req);
if (child && *own_req) {
- if (!subflow_ctx(child)) {
+ struct subflow_context *ctx = subflow_ctx(child);
+
+ if (!ctx) {
pr_debug("Closing child socket");
inet_sk_set_state(child, TCP_CLOSE);
sock_set_flag(child, SOCK_DEAD);
inet_csk_destroy_sock(child);
child = NULL;
+ } else if (ctx->mp_capable) {
+ token_new_accept(child);
}
}
@@ -193,6 +222,8 @@ static void subflow_ulp_clone(const struct request_sock *req,
subflow->fourth_ack = 1;
subflow->remote_key = subflow_req->remote_key;
subflow->local_key = subflow_req->local_key;
+ subflow->token = subflow_req->token;
+ pr_debug("token=%u", subflow->token);
}
}
@@ -217,6 +248,8 @@ static int subflow_ops_init(struct request_sock_ops *subflow_ops)
if (!subflow_ops->slab)
return -ENOMEM;
+ subflow_ops->destructor = subflow_req_destructor;
+
return 0;
}
@@ -233,6 +266,7 @@ void subflow_init(void)
subflow_specific.conn_request = subflow_conn_request;
subflow_specific.syn_recv_sock = subflow_syn_recv_sock;
subflow_specific.sk_rx_dst_set = subflow_finish_connect;
+ subflow_specific.rebuild_header = subflow_rebuild_header;
if (tcp_register_ulp(&subflow_ulp_ops) != 0)
panic("MPTCP: failed to register subflows to ULP\n");
diff --git a/net/mptcp/token.c b/net/mptcp/token.c
new file mode 100644
index 000000000000..8c15b8134f70
--- /dev/null
+++ b/net/mptcp/token.c
@@ -0,0 +1,248 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Multipath TCP token management
+ * Copyright (c) 2017 - 2019, Intel Corporation.
+ *
+ * Note: This code is based on mptcp_ctrl.c from multipath-tcp.org,
+ * authored by:
+ *
+ * Sébastien Barré <sebastien.barre@uclouvain.be>
+ * Christoph Paasch <christoph.paasch@uclouvain.be>
+ * Jaakko Korkeaniemi <jaakko.korkeaniemi@aalto.fi>
+ * Gregory Detal <gregory.detal@uclouvain.be>
+ * Fabien Duchêne <fabien.duchene@uclouvain.be>
+ * Andreas Seelinger <Andreas.Seelinger@rwth-aachen.de>
+ * Lavkesh Lahngir <lavkesh51@gmail.com>
+ * Andreas Ripke <ripke@neclab.eu>
+ * Vlad Dogaru <vlad.dogaru@intel.com>
+ * Octavian Purdila <octavian.purdila@intel.com>
+ * John Ronan <jronan@tssg.org>
+ * Catalin Nicutar <catalin.nicutar@gmail.com>
+ * Brandon Heller <brandonh@stanford.edu>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/radix-tree.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <net/sock.h>
+#include <net/inet_common.h>
+#include <net/protocol.h>
+#include <net/mptcp.h>
+#include "protocol.h"
+
+static struct radix_tree_root token_tree;
+static struct radix_tree_root token_req_tree;
+static spinlock_t token_tree_lock;
+static int token_used;
+
+static bool find_req_token(u32 token)
+{
+ void *used;
+
+ pr_debug("token=%u", token);
+ used = radix_tree_lookup(&token_req_tree, token);
+ return used;
+}
+
+static bool find_token(u32 token)
+{
+ void *used;
+
+ pr_debug("token=%u", token);
+ used = radix_tree_lookup(&token_tree, token);
+ return used;
+}
+
+static void new_req_token(struct request_sock *req,
+ const struct sk_buff *skb)
+{
+ const struct inet_request_sock *ireq = inet_rsk(req);
+ struct subflow_request_sock *subflow_req = subflow_rsk(req);
+ u64 local_key;
+
+ if (!IS_ENABLED(CONFIG_IPV6) || skb->protocol == htons(ETH_P_IP)) {
+ local_key = crypto_v4_get_key(ip_hdr(skb)->saddr,
+ ip_hdr(skb)->daddr,
+ htons(ireq->ir_num),
+ ireq->ir_rmt_port);
+#if IS_ENABLED(CONFIG_IPV6)
+ } else {
+ local_key = crypto_v6_get_key(&ipv6_hdr(skb)->saddr,
+ &ipv6_hdr(skb)->daddr,
+ htons(ireq->ir_num),
+ ireq->ir_rmt_port);
+#endif
+ }
+ pr_debug("local_key=%llu:%llx", local_key, local_key);
+ subflow_req->local_key = local_key;
+ crypto_key_sha1(subflow_req->local_key, &subflow_req->token, NULL);
+ pr_debug("token=%u", subflow_req->token);
+}
+
+static void new_token(const struct sock *sk)
+{
+ struct subflow_context *subflow = subflow_ctx(sk);
+ const struct inet_sock *isk = inet_sk(sk);
+
+ if (sk->sk_family == AF_INET) {
+ subflow->local_key = crypto_v4_get_key(isk->inet_saddr,
+ isk->inet_daddr,
+ isk->inet_sport,
+ isk->inet_dport);
+#if IS_ENABLED(CONFIG_IPV6)
+ } else {
+ subflow->local_key = crypto_v6_get_key(&inet6_sk(sk)->saddr,
+ &sk->sk_v6_daddr,
+ isk->inet_sport,
+ isk->inet_dport);
+#endif
+ }
+ pr_debug("local_key=%llu:%llx", subflow->local_key, subflow->local_key);
+ crypto_key_sha1(subflow->local_key, &subflow->token, NULL);
+ pr_debug("token=%u", subflow->token);
+}
+
+static int insert_req_token(u32 token)
+{
+ void *used = &token_used;
+
+ pr_debug("token=%u", token);
+ return radix_tree_insert(&token_req_tree, token, used);
+}
+
+static int insert_token(u32 token, void *conn)
+{
+ void *used = &token_used;
+
+ if (conn)
+ used = conn;
+
+ pr_debug("token=%u, conn=%p", token, used);
+ return radix_tree_insert(&token_tree, token, used);
+}
+
+static void update_token(u32 token, void *conn)
+{
+ void **slot;
+
+ pr_debug("token=%u, conn=%p", token, conn);
+ slot = radix_tree_lookup_slot(&token_tree, token);
+ if (slot) {
+ if (*slot != &token_used)
+ pr_err("slot ALREADY updated!");
+ *slot = conn;
+ } else {
+ pr_warn("token NOT FOUND!");
+ }
+}
+
+static void destroy_req_token(u32 token)
+{
+ void *cur;
+
+ cur = radix_tree_delete(&token_req_tree, token);
+ if (!cur)
+ pr_warn("token NOT FOUND!");
+}
+
+static struct sock *destroy_token(u32 token)
+{
+ void *conn;
+
+ pr_debug("token=%u", token);
+ conn = radix_tree_delete(&token_tree, token);
+ if (conn && conn != &token_used)
+ return (struct sock *)conn;
+ return NULL;
+}
+
+/* create new local key, idsn, and token for subflow_request */
+void token_new_request(struct request_sock *req,
+ const struct sk_buff *skb)
+{
+ struct subflow_request_sock *subflow_req = subflow_rsk(req);
+
+ pr_debug("subflow_req=%p", req);
+ while (1) {
+ new_req_token(req, skb);
+ spin_lock_bh(&token_tree_lock);
+ if (!find_req_token(subflow_req->token) &&
+ !find_token(subflow_req->token))
+ break;
+ spin_unlock_bh(&token_tree_lock);
+ }
+ insert_req_token(subflow_req->token);
+ spin_unlock_bh(&token_tree_lock);
+}
+
+/* create new local key, idsn, and token for subflow */
+void token_new_connect(struct sock *sk)
+{
+ struct subflow_context *subflow = subflow_ctx(sk);
+
+ pr_debug("subflow=%p", sk);
+
+ while (1) {
+ new_token(sk);
+ spin_lock_bh(&token_tree_lock);
+ if (!find_req_token(subflow->token) &&
+ !find_token(subflow->token))
+ break;
+ spin_unlock_bh(&token_tree_lock);
+ }
+ insert_token(subflow->token, subflow->conn);
+ sock_hold(subflow->conn);
+ spin_unlock_bh(&token_tree_lock);
+}
+
+void token_new_accept(struct sock *sk)
+{
+ struct subflow_context *subflow = subflow_ctx(sk);
+
+ pr_debug("subflow=%p", sk);
+
+ spin_lock_bh(&token_tree_lock);
+ insert_token(subflow->token, NULL);
+ spin_unlock_bh(&token_tree_lock);
+}
+
+void token_update_accept(struct sock *sk, struct sock *conn)
+{
+ struct subflow_context *subflow = subflow_ctx(sk);
+
+ pr_debug("subflow=%p, conn=%p", sk, conn);
+
+ spin_lock_bh(&token_tree_lock);
+ update_token(subflow->token, conn);
+ sock_hold(conn);
+ spin_unlock_bh(&token_tree_lock);
+}
+
+void token_destroy_request(u32 token)
+{
+ pr_debug("token=%u", token);
+
+ spin_lock_bh(&token_tree_lock);
+ destroy_req_token(token);
+ spin_unlock_bh(&token_tree_lock);
+}
+
+void token_destroy(u32 token)
+{
+ struct sock *conn;
+
+ pr_debug("token=%u", token);
+ spin_lock_bh(&token_tree_lock);
+ conn = destroy_token(token);
+ if (conn)
+ sock_put(conn);
+ spin_unlock_bh(&token_tree_lock);
+}
+
+void token_init(void)
+{
+ INIT_RADIX_TREE(&token_tree, GFP_ATOMIC);
+ INIT_RADIX_TREE(&token_req_tree, GFP_ATOMIC);
+ spin_lock_init(&token_tree_lock);
+}
--
2.22.0
^ permalink raw reply related
* [RFC PATCH net-next 31/33] mptcp: Add path manager interface
From: Mat Martineau @ 2019-06-17 22:58 UTC (permalink / raw)
To: edumazet, netdev
Cc: Peter Krystad, cpaasch, fw, pabeni, dcaratti, matthieu.baerts
In-Reply-To: <20190617225808.665-1-mathew.j.martineau@linux.intel.com>
From: Peter Krystad <peter.krystad@linux.intel.com>
Add enough of a path manager interface to allow sending of ADD_ADDR
when an incoming MPTCP connection is created. Capable of sending only
a single IPv4 ADD_ADDR option. The 'pm_data' element of the connection
sock will need to be expanded to handle multiple interfaces and IPv6.
This is a skeleton interface definition for events generated by
MPTCP.
Signed-off-by: Peter Krystad <peter.krystad@linux.intel.com>
---
net/mptcp/Makefile | 2 +-
net/mptcp/pm.c | 57 ++++++++++++++++++++++++++++++++++++++++++++
net/mptcp/protocol.c | 4 ++++
net/mptcp/protocol.h | 25 ++++++++++++++++++-
4 files changed, 86 insertions(+), 2 deletions(-)
create mode 100644 net/mptcp/pm.c
diff --git a/net/mptcp/Makefile b/net/mptcp/Makefile
index 178ae81d8b66..7fe7aa64eda0 100644
--- a/net/mptcp/Makefile
+++ b/net/mptcp/Makefile
@@ -1,4 +1,4 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_MPTCP) += mptcp.o
-mptcp-y := protocol.o subflow.o options.o token.o crypto.o
+mptcp-y := protocol.o subflow.o options.o token.o crypto.o pm.o
diff --git a/net/mptcp/pm.c b/net/mptcp/pm.c
new file mode 100644
index 000000000000..512dc110098a
--- /dev/null
+++ b/net/mptcp/pm.c
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Multipath TCP
+ *
+ * Copyright (c) 2019, Intel Corporation.
+ */
+#include <linux/kernel.h>
+#include <net/tcp.h>
+#include <net/mptcp.h>
+#include "protocol.h"
+
+void pm_new_connection(struct mptcp_sock *msk)
+{
+ pr_debug("msk=%p", msk);
+}
+
+void pm_fully_established(struct mptcp_sock *msk)
+{
+ pr_debug("msk=%p", msk);
+}
+
+void pm_connection_closed(struct mptcp_sock *msk)
+{
+ pr_debug("msk=%p", msk);
+}
+
+void pm_subflow_established(struct mptcp_sock *msk, u8 id)
+{
+ pr_debug("msk=%p", msk);
+}
+
+void pm_subflow_closed(struct mptcp_sock *msk, u8 id)
+{
+ pr_debug("msk=%p", msk);
+}
+
+void pm_add_addr(struct mptcp_sock *msk, const struct in_addr *addr, u8 id)
+{
+ pr_debug("msk=%p", msk);
+}
+
+void pm_add_addr6(struct mptcp_sock *msk, const struct in6_addr *addr, u8 id)
+{
+ pr_debug("msk=%p", msk);
+}
+
+void pm_rm_addr(struct mptcp_sock *msk, u8 id)
+{
+ pr_debug("msk=%p", msk);
+}
+
+bool pm_addr_signal(struct mptcp_sock *msk, unsigned int *size,
+ unsigned int remaining, struct mptcp_out_options *opts)
+{
+ pr_debug("msk=%p", msk);
+
+ return false;
+}
diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
index d4ffa47f53ef..e071fc8191ee 100644
--- a/net/mptcp/protocol.c
+++ b/net/mptcp/protocol.c
@@ -632,6 +632,8 @@ static struct sock *mptcp_accept(struct sock *sk, int flags, int *err,
token_update_accept(new_sock->sk, new_mptcp_sock);
msk->subflow = NULL;
+ pm_new_connection(msk);
+
crypto_key_sha1(msk->remote_key, NULL, &ack_seq);
msk->write_seq = subflow->idsn + 1;
ack_seq++;
@@ -757,6 +759,8 @@ void mptcp_finish_connect(struct sock *sk, int mp_capable)
msk->token = subflow->token;
pr_debug("msk=%p, token=%u", msk, msk->token);
+ pm_new_connection(msk);
+
crypto_key_sha1(msk->remote_key, NULL, &ack_seq);
msk->write_seq = subflow->idsn + 1;
ack_seq++;
diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h
index 556981f9e5fd..044665328b79 100644
--- a/net/mptcp/protocol.h
+++ b/net/mptcp/protocol.h
@@ -14,7 +14,7 @@
#define MPTCPOPT_MP_JOIN 1
#define MPTCPOPT_DSS 2
#define MPTCPOPT_ADD_ADDR 3
-#define MPTCPOPT_REMOVE_ADDR 4
+#define MPTCPOPT_RM_ADDR 4
#define MPTCPOPT_MP_PRIO 5
#define MPTCPOPT_MP_FAIL 6
#define MPTCPOPT_MP_FASTCLOSE 7
@@ -45,6 +45,17 @@
#define MPTCP_DSS_HAS_ACK BIT(0)
#define MPTCP_DSS_FLAG_MASK (0x1F)
+struct pm_data {
+ u8 addr_id;
+ sa_family_t family;
+ union {
+ struct in_addr addr;
+#if IS_ENABLED(CONFIG_IPV6)
+ struct in6_addr addr6;
+#endif
+ };
+};
+
/* MPTCP connection sock */
struct mptcp_sock {
/* inet_connection_sock must be the first member */
@@ -56,6 +67,7 @@ struct mptcp_sock {
u32 token;
struct list_head conn_list;
struct socket *subflow; /* outgoing connect/listener/!mp_capable */
+ struct pm_data pm;
};
#define mptcp_for_each_subflow(__msk, __subflow) \
@@ -157,6 +169,17 @@ void crypto_key_sha1(u64 key, u32 *token, u64 *idsn);
void crypto_hmac_sha1(u64 key1, u64 key2, u32 *hash_out,
int arg_num, ...);
+void pm_new_connection(struct mptcp_sock *msk);
+void pm_fully_established(struct mptcp_sock *msk);
+void pm_connection_closed(struct mptcp_sock *msk);
+void pm_subflow_established(struct mptcp_sock *msk, u8 id);
+void pm_subflow_closed(struct mptcp_sock *msk, u8 id);
+void pm_add_addr(struct mptcp_sock *msk, const struct in_addr *addr, u8 id);
+void pm_add_addr6(struct mptcp_sock *msk, const struct in6_addr *addr, u8 id);
+void pm_rm_addr(struct mptcp_sock *msk, u8 id);
+bool pm_addr_signal(struct mptcp_sock *msk, unsigned int *size,
+ unsigned int remaining, struct mptcp_out_options *opts);
+
static inline struct mptcp_ext *mptcp_get_ext(struct sk_buff *skb)
{
return (struct mptcp_ext *)skb_ext_find(skb, SKB_EXT_MPTCP);
--
2.22.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox