Skip to content

Commit

Permalink
mptcp: validate the data checksum
Browse files Browse the repository at this point in the history
This patch added three new members named data_csum, csum_len and
map_csum in struct mptcp_subflow_context, implemented a new function
named mptcp_validate_data_checksum().

If the current mapping is valid and csum is enabled traverse the later
pending skbs and compute csum incrementally till the whole mapping has
been covered. If not enough data is available in the rx queue, return
MAPPING_EMPTY - that is, no data.

Next subflow_data_ready invocation will trigger again csum computation.

When the full DSS is available, validate the csum and return to the
caller an appropriate error code, to trigger subflow reset of fallback
as required by the RFC.

Additionally:
- if the csum prevence in the DSS don't match the negotiated value e.g.
  csum present, but not requested, return invalid mapping to trigger
  subflow reset.
- keep some csum state, to avoid re-compute the csum on the same data
  when multiple rx queue traversal are required.
- clean-up the uncompleted mapping from the receive queue on close, to
  allow proper subflow disposal

Co-developed-by: Geliang Tang <geliangtang@gmail.com>
Signed-off-by: Geliang Tang <geliangtang@gmail.com>
Reviewed-by: Mat Martineau <mathew.j.martineau@linux.intel.com>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
  • Loading branch information
Paolo Abeni authored and matttbe committed May 6, 2021
1 parent 9f6f1f4 commit ff6ab77
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 7 deletions.
2 changes: 1 addition & 1 deletion net/mptcp/protocol.c
Original file line number Diff line number Diff line change
Expand Up @@ -1285,7 +1285,7 @@ static bool mptcp_alloc_tx_skb(struct sock *sk, struct sock *ssk)
static void mptcp_update_data_checksum(struct sk_buff *skb, int added)
{
struct mptcp_ext *mpext = mptcp_get_ext(skb);
__wsum csum = csum_unfold(mpext->csum);
__wsum csum = ~csum_unfold(mpext->csum);
int offset = skb->len - added;

mpext->csum = csum_fold(csum_block_add(csum, skb_checksum(skb, offset, added, 0), offset));
Expand Down
4 changes: 4 additions & 0 deletions net/mptcp/protocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,8 @@ struct mptcp_subflow_context {
u32 map_subflow_seq;
u32 ssn_offset;
u32 map_data_len;
__wsum map_data_csum;
u32 map_csum_len;
u32 request_mptcp : 1, /* send MP_CAPABLE */
request_join : 1, /* send MP_JOIN */
request_bkup : 1,
Expand All @@ -408,6 +410,8 @@ struct mptcp_subflow_context {
pm_notified : 1, /* PM hook called for established status */
conn_finished : 1,
map_valid : 1,
map_csum_reqd : 1,
map_data_fin : 1,
mpc_map : 1,
backup : 1,
send_mp_prio : 1,
Expand Down
105 changes: 99 additions & 6 deletions net/mptcp/subflow.c
Original file line number Diff line number Diff line change
Expand Up @@ -828,10 +828,90 @@ static bool validate_mapping(struct sock *ssk, struct sk_buff *skb)
return true;
}

static enum mapping_status validate_data_csum(struct sock *ssk, struct sk_buff *skb,
bool csum_reqd)
{
struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(ssk);
struct csum_pseudo_header header;
u32 offset, seq, delta;
__wsum csum;
int len;

if (!csum_reqd)
return MAPPING_OK;

/* mapping already validated on previous traversal */
if (subflow->map_csum_len == subflow->map_data_len)
return MAPPING_OK;

/* traverse the receive queue, ensuring it contains a full
* DSS mapping and accumulating the related csum.
* Preserve the accoumlate csum across multiple calls, to compute
* the csum only once
*/
delta = subflow->map_data_len - subflow->map_csum_len;
for (;;) {
seq = tcp_sk(ssk)->copied_seq + subflow->map_csum_len;
offset = seq - TCP_SKB_CB(skb)->seq;

/* if the current skb has not been accounted yet, csum its contents
* up to the amount covered by the current DSS
*/
if (offset < skb->len) {
__wsum csum;

len = min(skb->len - offset, delta);
csum = skb_checksum(skb, offset, len, 0);
subflow->map_data_csum = csum_block_add(subflow->map_data_csum, csum,
subflow->map_csum_len);

delta -= len;
subflow->map_csum_len += len;
}
if (delta == 0)
break;

if (skb_queue_is_last(&ssk->sk_receive_queue, skb)) {
/* if this subflow is closed, the partial mapping
* will be never completed; flush the pending skbs, so
* that subflow_sched_work_if_closed() can kick in
*/
if (unlikely(ssk->sk_state == TCP_CLOSE))
while ((skb = skb_peek(&ssk->sk_receive_queue)))
sk_eat_skb(ssk, skb);

/* not enough data to validate the csum */
return MAPPING_EMPTY;
}

/* the DSS mapping for next skbs will be validated later,
* when a get_mapping_status call will process such skb
*/
skb = skb->next;
}

/* note that 'map_data_len' accounts only for the carried data, does
* not include the eventual seq increment due to the data fin,
* while the pseudo header requires the original DSS data len,
* including that
*/
header.data_seq = cpu_to_be64(subflow->map_seq);
header.subflow_seq = htonl(subflow->map_subflow_seq);
header.data_len = htons(subflow->map_data_len + subflow->map_data_fin);
header.csum = 0;

csum = csum_partial(&header, sizeof(header), subflow->map_data_csum);
if (unlikely(csum_fold(csum)))
return subflow->mp_join ? MAPPING_INVALID : MAPPING_DUMMY;

return MAPPING_OK;
}

static enum mapping_status get_mapping_status(struct sock *ssk,
struct mptcp_sock *msk)
{
struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(ssk);
bool csum_reqd = READ_ONCE(msk->csum_enabled);
struct mptcp_ext *mpext;
struct sk_buff *skb;
u16 data_len;
Expand Down Expand Up @@ -925,9 +1005,10 @@ static enum mapping_status get_mapping_status(struct sock *ssk,
/* Allow replacing only with an identical map */
if (subflow->map_seq == map_seq &&
subflow->map_subflow_seq == mpext->subflow_seq &&
subflow->map_data_len == data_len) {
subflow->map_data_len == data_len &&
subflow->map_csum_reqd == mpext->csum_reqd) {
skb_ext_del(skb, SKB_EXT_MPTCP);
return MAPPING_OK;
goto validate_csum;
}

/* If this skb data are fully covered by the current mapping,
Expand All @@ -939,17 +1020,27 @@ static enum mapping_status get_mapping_status(struct sock *ssk,
}

/* will validate the next map after consuming the current one */
return MAPPING_OK;
goto validate_csum;
}

subflow->map_seq = map_seq;
subflow->map_subflow_seq = mpext->subflow_seq;
subflow->map_data_len = data_len;
subflow->map_valid = 1;
subflow->map_data_fin = mpext->data_fin;
subflow->mpc_map = mpext->mpc_map;
pr_debug("new map seq=%llu subflow_seq=%u data_len=%u",
subflow->map_csum_reqd = mpext->csum_reqd;
subflow->map_csum_len = 0;
subflow->map_data_csum = csum_unfold(mpext->csum);

/* Cfr RFC 8684 Section 3.3.0 */
if (unlikely(subflow->map_csum_reqd != csum_reqd))
return MAPPING_INVALID;

pr_debug("new map seq=%llu subflow_seq=%u data_len=%u csum=%d:%u",
subflow->map_seq, subflow->map_subflow_seq,
subflow->map_data_len);
subflow->map_data_len, subflow->map_csum_reqd,
subflow->map_data_csum);

validate_seq:
/* we revalidate valid mapping on new skb, because we must ensure
Expand All @@ -959,7 +1050,9 @@ static enum mapping_status get_mapping_status(struct sock *ssk,
return MAPPING_INVALID;

skb_ext_del(skb, SKB_EXT_MPTCP);
return MAPPING_OK;

validate_csum:
return validate_data_csum(ssk, skb, csum_reqd);
}

static void mptcp_subflow_discard_data(struct sock *ssk, struct sk_buff *skb,
Expand Down

0 comments on commit ff6ab77

Please sign in to comment.