1 | This series fixes the issue #544 where a middlebox drops MPTCP options | 1 | - Patch 1: fix issue #544 where a middlebox drops MPTCP options after a |
---|---|---|---|
2 | after a 3WHS on the additional subflow. | 2 | 3WHS on the additional subflow. |
3 | 3 | ||
4 | Two additional patches have been added: | 4 | - Patch 2: a safety check is added when asking to do a fallback to do so |
5 | 5 | only when allowed. If not, a new warning will be emitted. | |
6 | - Patch 2: for an issue seen when debugging the previous one: there | ||
7 | should be a fallback after the reception of an infinite mapping, only | ||
8 | if allowed. | ||
9 | |||
10 | - Patch 3: to avoid the issues fixed by the two patches above, a safety | ||
11 | check is added when asking to do a fallback if it is not possible to | ||
12 | do so. A warning will be emitted in this case. | ||
13 | 6 | ||
14 | Note that two new packetdrill tests have been added in a PR to cover | 7 | Note that two new packetdrill tests have been added in a PR to cover |
15 | this case: | 8 | this case: |
16 | 9 | ||
17 | https://github.com/multipath-tcp/packetdrill/pull/160 | 10 | https://github.com/multipath-tcp/packetdrill/pull/160 |
18 | 11 | ||
19 | Signed-off-by: Matthieu Baerts (NGI0) <matttbe@kernel.org> | 12 | Signed-off-by: Matthieu Baerts (NGI0) <matttbe@kernel.org> |
20 | --- | 13 | --- |
21 | Matthieu Baerts (NGI0) (3): | 14 | Changes in v2: |
15 | - Squashed previous patches 1 and 2 (Paolo). | ||
16 | - Patch 1: no more special cases for csum (handled before) and inf map | ||
17 | (Paolo). | ||
18 | - Link to v1: https://lore.kernel.org/r/20250217-mptcp-dss-drop-mpj-v1-0-d671d6b9a153@kernel.org | ||
19 | |||
20 | --- | ||
21 | Matthieu Baerts (NGI0) (2): | ||
22 | mptcp: reset when MPTCP opts are dropped after join | 22 | mptcp: reset when MPTCP opts are dropped after join |
23 | mptcp: only fallback due to inf mapping if allowed | ||
24 | mptcp: safety check before fallback | 23 | mptcp: safety check before fallback |
25 | 24 | ||
26 | net/mptcp/protocol.h | 2 ++ | 25 | net/mptcp/protocol.h | 2 ++ |
27 | net/mptcp/subflow.c | 23 ++++++++++++++--------- | 26 | net/mptcp/subflow.c | 15 +-------------- |
28 | 2 files changed, 16 insertions(+), 9 deletions(-) | 27 | 2 files changed, 3 insertions(+), 14 deletions(-) |
29 | --- | 28 | --- |
30 | base-commit: 3a7786db65e8f361a8fc8abcd3cfbe17411b8b7f | 29 | base-commit: 3a7786db65e8f361a8fc8abcd3cfbe17411b8b7f |
31 | change-id: 20250205-mptcp-dss-drop-mpj-ec227f4ed942 | 30 | change-id: 20250205-mptcp-dss-drop-mpj-ec227f4ed942 |
32 | 31 | ||
33 | Best regards, | 32 | Best regards, |
34 | -- | 33 | -- |
35 | Matthieu Baerts (NGI0) <matttbe@kernel.org> | 34 | Matthieu Baerts (NGI0) <matttbe@kernel.org> | diff view generated by jsdifflib |
... | ... | ||
---|---|---|---|
3 | invalid mapping, map_data_len was not set to the data len, and then the | 3 | invalid mapping, map_data_len was not set to the data len, and then the |
4 | subflow was not reset as it should have been, leaving the MPTCP | 4 | subflow was not reset as it should have been, leaving the MPTCP |
5 | connection in a wrong fallback mode. | 5 | connection in a wrong fallback mode. |
6 | 6 | ||
7 | This map_data_len condition has been introduced to handle the reception | 7 | This map_data_len condition has been introduced to handle the reception |
8 | of the infinite mapping. So instead, a new dedicated mapping error is | 8 | of the infinite mapping. Instead, a new dedicated mapping error could |
9 | now returned (infinite), and this special case is properly handled: the | 9 | have been returned and treated as a special case. However, the commit |
10 | exception is only applied to this case now, and not other ones by | 10 | 31bf11de146c ("mptcp: introduce MAPPING_BAD_CSUM") has been introduced |
11 | mistake. | 11 | by Paolo Abeni soon after, and backported later on to stable. It better |
12 | handle the csum case, and it means the exception for valid_csum_seen in | ||
13 | subflow_can_fallback(), plus this one for the infinite mapping in | ||
14 | subflow_check_data_avail(), are no longer needed. | ||
12 | 15 | ||
13 | While at it, no need to set map_data_len to 0 as it will be set to | 16 | In other words, the code can be simplified there: a fallback should only |
14 | skb->len just after, at the end of subflow_check_data_avail(). | 17 | be done if msk->allow_infinite_fallback is set. This boolean is set to |
18 | false once MPTCP-specific operations acting on the whole MPTCP | ||
19 | connection vs the initial path have been done, e.g. a second path has | ||
20 | been created, or an MPTCP re-injection -- yes, possible even with a | ||
21 | single subflow. The subflow_can_fallback() helper can then be dropped, | ||
22 | and replaced by this single condition. | ||
23 | |||
24 | This also makes the code clearer: a fallback should only be done if it | ||
25 | is possible to do so. | ||
26 | |||
27 | While at it, no need to set map_data_len to 0 in get_mapping_status() | ||
28 | for the infinite mapping case: it will be set to skb->len just after, at | ||
29 | the end of subflow_check_data_avail(), and not read in between. | ||
15 | 30 | ||
16 | Fixes: f8d4bcacff3b ("mptcp: infinite mapping receiving") | 31 | Fixes: f8d4bcacff3b ("mptcp: infinite mapping receiving") |
17 | Reported-by: Chester A. Unal <chester.a.unal@xpedite-tech.com> | 32 | Reported-by: Chester A. Unal <chester.a.unal@xpedite-tech.com> |
18 | Closes: https://github.com/multipath-tcp/mptcp_net-next/issues/544 | 33 | Closes: https://github.com/multipath-tcp/mptcp_net-next/issues/544 |
19 | Signed-off-by: Matthieu Baerts (NGI0) <matttbe@kernel.org> | 34 | Signed-off-by: Matthieu Baerts (NGI0) <matttbe@kernel.org> |
20 | --- | 35 | --- |
21 | net/mptcp/subflow.c | 9 +++++---- | 36 | Notes: |
22 | 1 file changed, 5 insertions(+), 4 deletions(-) | 37 | - v2: |
38 | - Squash previous patches 1 and 2, and drop csum case from | ||
39 | subflow_can_fallback (Paolo). | ||
40 | - v2 is quite different, Chester's Tested-by tag has not been added. | ||
41 | --- | ||
42 | net/mptcp/subflow.c | 15 +-------------- | ||
43 | 1 file changed, 1 insertion(+), 14 deletions(-) | ||
23 | 44 | ||
24 | diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c | 45 | diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c |
25 | index XXXXXXX..XXXXXXX 100644 | 46 | index XXXXXXX..XXXXXXX 100644 |
26 | --- a/net/mptcp/subflow.c | 47 | --- a/net/mptcp/subflow.c |
27 | +++ b/net/mptcp/subflow.c | 48 | +++ b/net/mptcp/subflow.c |
28 | @@ -XXX,XX +XXX,XX @@ enum mapping_status { | ||
29 | MAPPING_DATA_FIN, | ||
30 | MAPPING_DUMMY, | ||
31 | MAPPING_BAD_CSUM, | ||
32 | + MAPPING_INFINITE, | ||
33 | MAPPING_NODSS | ||
34 | }; | ||
35 | |||
36 | @@ -XXX,XX +XXX,XX @@ static enum mapping_status get_mapping_status(struct sock *ssk, | 49 | @@ -XXX,XX +XXX,XX @@ static enum mapping_status get_mapping_status(struct sock *ssk, |
37 | if (data_len == 0) { | 50 | if (data_len == 0) { |
38 | pr_debug("infinite mapping received\n"); | 51 | pr_debug("infinite mapping received\n"); |
39 | MPTCP_INC_STATS(sock_net(ssk), MPTCP_MIB_INFINITEMAPRX); | 52 | MPTCP_INC_STATS(sock_net(ssk), MPTCP_MIB_INFINITEMAPRX); |
40 | - subflow->map_data_len = 0; | 53 | - subflow->map_data_len = 0; |
41 | - return MAPPING_INVALID; | 54 | return MAPPING_INVALID; |
42 | + return MAPPING_INFINITE; | ||
43 | } | 55 | } |
44 | 56 | ||
45 | if (mpext->data_fin == 1) { | 57 | @@ -XXX,XX +XXX,XX @@ static void subflow_sched_work_if_closed(struct mptcp_sock *msk, struct sock *ss |
46 | @@ -XXX,XX +XXX,XX @@ static bool subflow_check_data_avail(struct sock *ssk) | 58 | mptcp_schedule_work(sk); |
47 | status = get_mapping_status(ssk, msk); | 59 | } |
48 | trace_subflow_check_data_avail(status, skb_peek(&ssk->sk_receive_queue)); | 60 | |
49 | if (unlikely(status == MAPPING_INVALID || status == MAPPING_DUMMY || | 61 | -static bool subflow_can_fallback(struct mptcp_subflow_context *subflow) |
50 | - status == MAPPING_BAD_CSUM || status == MAPPING_NODSS)) | 62 | -{ |
51 | + status == MAPPING_BAD_CSUM || status == MAPPING_INFINITE || | 63 | - struct mptcp_sock *msk = mptcp_sk(subflow->conn); |
52 | + status == MAPPING_NODSS)) | 64 | - |
53 | goto fallback; | 65 | - if (subflow->mp_join) |
54 | 66 | - return false; | |
55 | if (status != MAPPING_OK) | 67 | - else if (READ_ONCE(msk->csum_enabled)) |
68 | - return !subflow->valid_csum_seen; | ||
69 | - else | ||
70 | - return READ_ONCE(msk->allow_infinite_fallback); | ||
71 | -} | ||
72 | - | ||
73 | static void mptcp_subflow_fail(struct mptcp_sock *msk, struct sock *ssk) | ||
74 | { | ||
75 | struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(ssk); | ||
56 | @@ -XXX,XX +XXX,XX @@ static bool subflow_check_data_avail(struct sock *ssk) | 76 | @@ -XXX,XX +XXX,XX @@ static bool subflow_check_data_avail(struct sock *ssk) |
57 | return true; | 77 | return true; |
58 | } | 78 | } |
59 | 79 | ||
60 | - if (!subflow_can_fallback(subflow) && subflow->map_data_len) { | 80 | - if (!subflow_can_fallback(subflow) && subflow->map_data_len) { |
61 | + if (!subflow_can_fallback(subflow) && status != MAPPING_INFINITE) { | 81 | + if (!READ_ONCE(msk->allow_infinite_fallback)) { |
62 | /* fatal protocol error, close the socket. | 82 | /* fatal protocol error, close the socket. |
63 | * subflow_error_report() will introduce the appropriate barriers | 83 | * subflow_error_report() will introduce the appropriate barriers |
64 | */ | 84 | */ |
65 | 85 | ||
66 | -- | 86 | -- |
67 | 2.47.1 | 87 | 2.47.1 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | Any fallback should happen only if allowed, so only if this variable is | ||
2 | set: msk->allow_infinite_fallback. This boolean will be set to false | ||
3 | once MPTCP-specific operations acting on the whole MPTCP connection vs | ||
4 | the initial path have been done, e.g. a second path has been created, or | ||
5 | an MPTCP re-injection -- yes, possible even with a single subflow. | ||
6 | 1 | ||
7 | In other words, the !msk->allow_infinite_fallback condition should be | ||
8 | placed first. If it is no longer possible to do a fallback, there should | ||
9 | not be any bypass, e.g. when receiving an infinite mapping. | ||
10 | |||
11 | Now, regarding the conditions to do a fallback, the RFC mentions [1] | ||
12 | that if the checksum is used, a fallback is only possible when "it is | ||
13 | known that all unacknowledged data in flight is contiguous (which will | ||
14 | usually be the case with a single subflow)". In other words, if the | ||
15 | checksum is used, a fallback is possible when the other peer sent an | ||
16 | infinite mapping indicating the flow has been altered. | ||
17 | |||
18 | Note that the issue is present since a merge commit, where both | ||
19 | subflow_can_fallback() and the previous extra condition with | ||
20 | 'subflow->map_data_len' got introduced. | ||
21 | |||
22 | Fixes: d7e6f5836038 ("Merge git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net") | ||
23 | Link: https://datatracker.ietf.org/doc/html/rfc8684#section-3.7-11 [1] | ||
24 | Signed-off-by: Matthieu Baerts (NGI0) <matttbe@kernel.org> | ||
25 | --- | ||
26 | net/mptcp/subflow.c | 16 ++++++++++------ | ||
27 | 1 file changed, 10 insertions(+), 6 deletions(-) | ||
28 | |||
29 | diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c | ||
30 | index XXXXXXX..XXXXXXX 100644 | ||
31 | --- a/net/mptcp/subflow.c | ||
32 | +++ b/net/mptcp/subflow.c | ||
33 | @@ -XXX,XX +XXX,XX @@ static void subflow_sched_work_if_closed(struct mptcp_sock *msk, struct sock *ss | ||
34 | mptcp_schedule_work(sk); | ||
35 | } | ||
36 | |||
37 | -static bool subflow_can_fallback(struct mptcp_subflow_context *subflow) | ||
38 | +static bool subflow_can_fallback(struct mptcp_subflow_context *subflow, | ||
39 | + enum mapping_status status) | ||
40 | { | ||
41 | struct mptcp_sock *msk = mptcp_sk(subflow->conn); | ||
42 | |||
43 | - if (subflow->mp_join) | ||
44 | + /* If not allowed (additional paths, MPTCP reinjections): no fallback */ | ||
45 | + if (!READ_ONCE(msk->allow_infinite_fallback)) | ||
46 | return false; | ||
47 | - else if (READ_ONCE(msk->csum_enabled)) | ||
48 | + | ||
49 | + /* More strict with csum: fallback in 2 cases: inf map or never valid */ | ||
50 | + if (status != MAPPING_INFINITE && READ_ONCE(msk->csum_enabled)) | ||
51 | return !subflow->valid_csum_seen; | ||
52 | - else | ||
53 | - return READ_ONCE(msk->allow_infinite_fallback); | ||
54 | + | ||
55 | + return true; | ||
56 | } | ||
57 | |||
58 | static void mptcp_subflow_fail(struct mptcp_sock *msk, struct sock *ssk) | ||
59 | @@ -XXX,XX +XXX,XX @@ static bool subflow_check_data_avail(struct sock *ssk) | ||
60 | return true; | ||
61 | } | ||
62 | |||
63 | - if (!subflow_can_fallback(subflow) && status != MAPPING_INFINITE) { | ||
64 | + if (!subflow_can_fallback(subflow, status)) { | ||
65 | /* fatal protocol error, close the socket. | ||
66 | * subflow_error_report() will introduce the appropriate barriers | ||
67 | */ | ||
68 | |||
69 | -- | ||
70 | 2.47.1 | diff view generated by jsdifflib |
1 | Recently, some fallback have been initiated, while the connection was | 1 | Recently, some fallback have been initiated, while the connection was |
---|---|---|---|
2 | not supposed to fallback. | 2 | not supposed to fallback. |
3 | 3 | ||
4 | Add a safety check with a warning to detect when an wrong attempt to | 4 | Add a safety check with a warning to detect when an wrong attempt to |
5 | fallback is being done. This should help detecting any future issues | 5 | fallback is being done. This should help detecting any future issues |
6 | quicker. | 6 | quicker. |
7 | 7 | ||
8 | Signed-off-by: Matthieu Baerts (NGI0) <matttbe@kernel.org> | 8 | Signed-off-by: Matthieu Baerts (NGI0) <matttbe@kernel.org> |
9 | --- | 9 | --- |
10 | Notes: | 10 | Notes: |
11 | - I guess this patch could be sent to -net as well. | 11 | - I guess this patch could be sent to -net as well. |
12 | - We could also squash this in one of the previous commit, but because | 12 | - We could also squash this in one of the previous commit, but because |
13 | the two are needed to avoid the warning, maybe better with a | 13 | the two are needed to avoid the warning, maybe better with a |
14 | dedicated commit? | 14 | dedicated commit? |
15 | --- | 15 | --- |
16 | net/mptcp/protocol.h | 2 ++ | 16 | net/mptcp/protocol.h | 2 ++ |
17 | 1 file changed, 2 insertions(+) | 17 | 1 file changed, 2 insertions(+) |
18 | 18 | ||
19 | diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h | 19 | diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h |
20 | index XXXXXXX..XXXXXXX 100644 | 20 | index XXXXXXX..XXXXXXX 100644 |
21 | --- a/net/mptcp/protocol.h | 21 | --- a/net/mptcp/protocol.h |
22 | +++ b/net/mptcp/protocol.h | 22 | +++ b/net/mptcp/protocol.h |
23 | @@ -XXX,XX +XXX,XX @@ static inline void __mptcp_do_fallback(struct mptcp_sock *msk) | 23 | @@ -XXX,XX +XXX,XX @@ static inline void __mptcp_do_fallback(struct mptcp_sock *msk) |
24 | pr_debug("TCP fallback already done (msk=%p)\n", msk); | 24 | pr_debug("TCP fallback already done (msk=%p)\n", msk); |
25 | return; | 25 | return; |
26 | } | 26 | } |
27 | + if (WARN_ON_ONCE(!READ_ONCE(msk->allow_infinite_fallback))) | 27 | + if (WARN_ON_ONCE(!READ_ONCE(msk->allow_infinite_fallback))) |
28 | + return; | 28 | + return; |
29 | set_bit(MPTCP_FALLBACK_DONE, &msk->flags); | 29 | set_bit(MPTCP_FALLBACK_DONE, &msk->flags); |
30 | } | 30 | } |
31 | 31 | ||
32 | 32 | ||
33 | -- | 33 | -- |
34 | 2.47.1 | 34 | 2.47.1 | diff view generated by jsdifflib |