Commit 40171248 authored by Xin Long's avatar Xin Long Committed by David S. Miller

sctp: allow IP fragmentation when PLPMTUD enters Error state

Currently when PLPMTUD enters Error state, transport pathmtu will be set
to MIN_PLPMTU(512) while probe is continuing with BASE_PLPMTU(1200). It
will cause pathmtu to stay in a very small value, even if the real pmtu
is some value like 1000.

RFC8899 doesn't clearly say how to set the value in Error state. But one
possibility could be keep using BASE_PLPMTU for the real pmtu, but allow
to do IP fragmentation when it's in Error state.

As it says in rfc8899#section-5.4:

   Some paths could be unable to sustain packets of the BASE_PLPMTU
   size.  The Error State could be implemented to provide robustness to
   such paths.  This allows fallback to a smaller than desired PLPMTU
   rather than suffer connectivity failure.  This could utilize methods
   such as endpoint IP fragmentation to enable the PL sender to
   communicate using packets smaller than the BASE_PLPMTU.

This patch is to set pmtu to BASE_PLPMTU instead of MIN_PLPMTU for Error
state in sctp_transport_pl_send/toobig(), and set packet ipfragok for
non-probe packets when it's in Error state.

Fixes: 1dc68c19 ("sctp: do state transition when PROBE_COUNT == MAX_PROBES on HB send path")
Reported-by: default avatarYing Xu <yinxu@redhat.com>
Signed-off-by: default avatarXin Long <lucien.xin@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 411a44c2
...@@ -581,14 +581,17 @@ int sctp_packet_transmit(struct sctp_packet *packet, gfp_t gfp) ...@@ -581,14 +581,17 @@ int sctp_packet_transmit(struct sctp_packet *packet, gfp_t gfp)
chunk = list_entry(packet->chunk_list.next, struct sctp_chunk, list); chunk = list_entry(packet->chunk_list.next, struct sctp_chunk, list);
sk = chunk->skb->sk; sk = chunk->skb->sk;
/* check gso */
if (packet->size > tp->pathmtu && !packet->ipfragok && !chunk->pmtu_probe) { if (packet->size > tp->pathmtu && !packet->ipfragok && !chunk->pmtu_probe) {
if (!sk_can_gso(sk)) { if (tp->pl.state == SCTP_PL_ERROR) { /* do IP fragmentation if in Error state */
packet->ipfragok = 1;
} else {
if (!sk_can_gso(sk)) { /* check gso */
pr_err_once("Trying to GSO but underlying device doesn't support it."); pr_err_once("Trying to GSO but underlying device doesn't support it.");
goto out; goto out;
} }
gso = 1; gso = 1;
} }
}
/* alloc head skb */ /* alloc head skb */
head = alloc_skb((gso ? packet->overhead : packet->size) + head = alloc_skb((gso ? packet->overhead : packet->size) +
......
...@@ -269,7 +269,7 @@ bool sctp_transport_pl_send(struct sctp_transport *t) ...@@ -269,7 +269,7 @@ bool sctp_transport_pl_send(struct sctp_transport *t)
if (t->pl.probe_size == SCTP_BASE_PLPMTU) { /* BASE_PLPMTU Confirmation Failed */ if (t->pl.probe_size == SCTP_BASE_PLPMTU) { /* BASE_PLPMTU Confirmation Failed */
t->pl.state = SCTP_PL_ERROR; /* Base -> Error */ t->pl.state = SCTP_PL_ERROR; /* Base -> Error */
t->pl.pmtu = SCTP_MIN_PLPMTU; t->pl.pmtu = SCTP_BASE_PLPMTU;
t->pathmtu = t->pl.pmtu + sctp_transport_pl_hlen(t); t->pathmtu = t->pl.pmtu + sctp_transport_pl_hlen(t);
sctp_assoc_sync_pmtu(t->asoc); sctp_assoc_sync_pmtu(t->asoc);
} }
...@@ -366,7 +366,7 @@ static bool sctp_transport_pl_toobig(struct sctp_transport *t, u32 pmtu) ...@@ -366,7 +366,7 @@ static bool sctp_transport_pl_toobig(struct sctp_transport *t, u32 pmtu)
if (pmtu >= SCTP_MIN_PLPMTU && pmtu < SCTP_BASE_PLPMTU) { if (pmtu >= SCTP_MIN_PLPMTU && pmtu < SCTP_BASE_PLPMTU) {
t->pl.state = SCTP_PL_ERROR; /* Base -> Error */ t->pl.state = SCTP_PL_ERROR; /* Base -> Error */
t->pl.pmtu = SCTP_MIN_PLPMTU; t->pl.pmtu = SCTP_BASE_PLPMTU;
t->pathmtu = t->pl.pmtu + sctp_transport_pl_hlen(t); t->pathmtu = t->pl.pmtu + sctp_transport_pl_hlen(t);
} }
} else if (t->pl.state == SCTP_PL_SEARCH) { } else if (t->pl.state == SCTP_PL_SEARCH) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment