Skip to content

Commit

Permalink
sctp: add the probe timer in transport for PLPMTUD
Browse files Browse the repository at this point in the history
There are 3 timers described in rfc8899#section-5.1.1:

  PROBE_TIMER, PMTU_RAISE_TIMER, CONFIRMATION_TIMER

This patches adds a 'probe_timer' in transport, and it works as either
PROBE_TIMER or PMTU_RAISE_TIMER. At most time, it works as PROBE_TIMER
and expires every a 'probe_interval' time to send the HB probe packet.
When transport pl enters COMPLETE state, it works as PMTU_RAISE_TIMER
and expires in 'probe_interval * 30' time to go back to SEARCH state
and do searching again.

SCTP HB is an acknowledged packet, CONFIRMATION_TIMER is not needed.

The timer will start when transport pl enters BASE state and stop
when it enters DISABLED state.

Signed-off-by: Xin Long <lucien.xin@gmail.com>
Acked-by: Marcelo Ricardo Leitner <marcelo.leitner@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Xin Long authored and David S. Miller committed Jun 22, 2021
1 parent d9e2e41 commit 92548ec
Show file tree
Hide file tree
Showing 10 changed files with 109 additions and 1 deletion.
1 change: 1 addition & 0 deletions include/net/sctp/command.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ enum sctp_verb {
SCTP_CMD_HB_TIMERS_START, /* Start the heartbeat timers. */
SCTP_CMD_HB_TIMER_UPDATE, /* Update a heartbeat timers. */
SCTP_CMD_HB_TIMERS_STOP, /* Stop the heartbeat timers. */
SCTP_CMD_PROBE_TIMER_UPDATE, /* Update a probe timer. */
SCTP_CMD_TRANSPORT_HB_SENT, /* Reset the status of a transport. */
SCTP_CMD_TRANSPORT_IDLE, /* Do manipulations on idle transport */
SCTP_CMD_TRANSPORT_ON, /* Mark the transport as active. */
Expand Down
1 change: 1 addition & 0 deletions include/net/sctp/constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ enum sctp_event_timeout {
SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD,
SCTP_EVENT_TIMEOUT_HEARTBEAT,
SCTP_EVENT_TIMEOUT_RECONF,
SCTP_EVENT_TIMEOUT_PROBE,
SCTP_EVENT_TIMEOUT_SACK,
SCTP_EVENT_TIMEOUT_AUTOCLOSE,
};
Expand Down
9 changes: 8 additions & 1 deletion include/net/sctp/sctp.h
Original file line number Diff line number Diff line change
Expand Up @@ -635,10 +635,14 @@ static inline void sctp_transport_pl_reset(struct sctp_transport *t)
t->pl.state = SCTP_PL_BASE;
t->pl.pmtu = SCTP_BASE_PLPMTU;
t->pl.probe_size = SCTP_BASE_PLPMTU;
sctp_transport_reset_probe_timer(t);
}
} else {
if (t->pl.state != SCTP_PL_DISABLED)
if (t->pl.state != SCTP_PL_DISABLED) {
if (del_timer(&t->probe_timer))
sctp_transport_put(t);
t->pl.state = SCTP_PL_DISABLED;
}
}
}

Expand All @@ -647,6 +651,9 @@ static inline void sctp_transport_pl_update(struct sctp_transport *t)
if (t->pl.state == SCTP_PL_DISABLED)
return;

if (del_timer(&t->probe_timer))
sctp_transport_put(t);

t->pl.state = SCTP_PL_BASE;
t->pl.pmtu = SCTP_BASE_PLPMTU;
t->pl.probe_size = SCTP_BASE_PLPMTU;
Expand Down
2 changes: 2 additions & 0 deletions include/net/sctp/sm.h
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ sctp_state_fn_t sctp_sf_cookie_wait_icmp_abort;
/* Prototypes for timeout event state functions. */
sctp_state_fn_t sctp_sf_do_6_3_3_rtx;
sctp_state_fn_t sctp_sf_send_reconf;
sctp_state_fn_t sctp_sf_send_probe;
sctp_state_fn_t sctp_sf_do_6_2_sack;
sctp_state_fn_t sctp_sf_autoclose_timer_expire;

Expand Down Expand Up @@ -311,6 +312,7 @@ int sctp_do_sm(struct net *net, enum sctp_event_type event_type,
void sctp_generate_t3_rtx_event(struct timer_list *t);
void sctp_generate_heartbeat_event(struct timer_list *t);
void sctp_generate_reconf_event(struct timer_list *t);
void sctp_generate_probe_event(struct timer_list *t);
void sctp_generate_proto_unreach_event(struct timer_list *t);

void sctp_ootb_pkt_free(struct sctp_packet *packet);
Expand Down
4 changes: 4 additions & 0 deletions include/net/sctp/structs.h
Original file line number Diff line number Diff line change
Expand Up @@ -936,6 +936,9 @@ struct sctp_transport {
/* Timer to handler reconf chunk rtx */
struct timer_list reconf_timer;

/* Timer to send a probe HB packet for PLPMTUD */
struct timer_list probe_timer;

/* Since we're using per-destination retransmission timers
* (see above), we're also using per-destination "transmitted"
* queues. This probably ought to be a private struct
Expand Down Expand Up @@ -1003,6 +1006,7 @@ void sctp_transport_free(struct sctp_transport *);
void sctp_transport_reset_t3_rtx(struct sctp_transport *);
void sctp_transport_reset_hb_timer(struct sctp_transport *);
void sctp_transport_reset_reconf_timer(struct sctp_transport *transport);
void sctp_transport_reset_probe_timer(struct sctp_transport *transport);
int sctp_transport_hold(struct sctp_transport *);
void sctp_transport_put(struct sctp_transport *);
void sctp_transport_update_rto(struct sctp_transport *, __u32);
Expand Down
1 change: 1 addition & 0 deletions net/sctp/debug.c
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ static const char *const sctp_timer_tbl[] = {
"TIMEOUT_T5_SHUTDOWN_GUARD",
"TIMEOUT_HEARTBEAT",
"TIMEOUT_RECONF",
"TIMEOUT_PROBE",
"TIMEOUT_SACK",
"TIMEOUT_AUTOCLOSE",
};
Expand Down
37 changes: 37 additions & 0 deletions net/sctp/sm_sideeffect.c
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,38 @@ void sctp_generate_reconf_event(struct timer_list *t)
sctp_transport_put(transport);
}

/* Handle the timeout of the probe timer. */
void sctp_generate_probe_event(struct timer_list *t)
{
struct sctp_transport *transport = from_timer(transport, t, probe_timer);
struct sctp_association *asoc = transport->asoc;
struct sock *sk = asoc->base.sk;
struct net *net = sock_net(sk);
int error = 0;

bh_lock_sock(sk);
if (sock_owned_by_user(sk)) {
pr_debug("%s: sock is busy\n", __func__);

/* Try again later. */
if (!mod_timer(&transport->probe_timer, jiffies + (HZ / 20)))
sctp_transport_hold(transport);
goto out_unlock;
}

error = sctp_do_sm(net, SCTP_EVENT_T_TIMEOUT,
SCTP_ST_TIMEOUT(SCTP_EVENT_TIMEOUT_PROBE),
asoc->state, asoc->ep, asoc,
transport, GFP_ATOMIC);

if (error)
sk->sk_err = -error;

out_unlock:
bh_unlock_sock(sk);
sctp_transport_put(transport);
}

/* Inject a SACK Timeout event into the state machine. */
static void sctp_generate_sack_event(struct timer_list *t)
{
Expand Down Expand Up @@ -1641,6 +1673,11 @@ static int sctp_cmd_interpreter(enum sctp_event_type event_type,
sctp_cmd_hb_timers_stop(commands, asoc);
break;

case SCTP_CMD_PROBE_TIMER_UPDATE:
t = cmd->obj.transport;
sctp_transport_reset_probe_timer(t);
break;

case SCTP_CMD_REPORT_ERROR:
error = cmd->obj.error;
break;
Expand Down
17 changes: 17 additions & 0 deletions net/sctp/sm_statefuns.c
Original file line number Diff line number Diff line change
Expand Up @@ -1095,6 +1095,23 @@ enum sctp_disposition sctp_sf_send_reconf(struct net *net,
return SCTP_DISPOSITION_CONSUME;
}

/* send hb chunk with padding for PLPMUTD. */
enum sctp_disposition sctp_sf_send_probe(struct net *net,
const struct sctp_endpoint *ep,
const struct sctp_association *asoc,
const union sctp_subtype type,
void *arg,
struct sctp_cmd_seq *commands)
{
struct sctp_transport *transport = (struct sctp_transport *)arg;

/* The actual handling will be performed here in a later patch. */
sctp_add_cmd_sf(commands, SCTP_CMD_PROBE_TIMER_UPDATE,
SCTP_TRANSPORT(transport));

return SCTP_DISPOSITION_CONSUME;
}

/*
* Process an heartbeat request.
*
Expand Down
20 changes: 20 additions & 0 deletions net/sctp/sm_statetable.c
Original file line number Diff line number Diff line change
Expand Up @@ -967,6 +967,25 @@ other_event_table[SCTP_NUM_OTHER_TYPES][SCTP_STATE_NUM_STATES] = {
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
}

#define TYPE_SCTP_EVENT_TIMEOUT_PROBE { \
/* SCTP_STATE_CLOSED */ \
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
/* SCTP_STATE_COOKIE_WAIT */ \
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
/* SCTP_STATE_COOKIE_ECHOED */ \
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
/* SCTP_STATE_ESTABLISHED */ \
TYPE_SCTP_FUNC(sctp_sf_send_probe), \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
/* SCTP_STATE_SHUTDOWN_SENT */ \
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
}

static const struct sctp_sm_table_entry
timeout_event_table[SCTP_NUM_TIMEOUT_TYPES][SCTP_STATE_NUM_STATES] = {
TYPE_SCTP_EVENT_TIMEOUT_NONE,
Expand All @@ -978,6 +997,7 @@ timeout_event_table[SCTP_NUM_TIMEOUT_TYPES][SCTP_STATE_NUM_STATES] = {
TYPE_SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD,
TYPE_SCTP_EVENT_TIMEOUT_HEARTBEAT,
TYPE_SCTP_EVENT_TIMEOUT_RECONF,
TYPE_SCTP_EVENT_TIMEOUT_PROBE,
TYPE_SCTP_EVENT_TIMEOUT_SACK,
TYPE_SCTP_EVENT_TIMEOUT_AUTOCLOSE,
};
Expand Down
18 changes: 18 additions & 0 deletions net/sctp/transport.c
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ static struct sctp_transport *sctp_transport_init(struct net *net,
timer_setup(&peer->T3_rtx_timer, sctp_generate_t3_rtx_event, 0);
timer_setup(&peer->hb_timer, sctp_generate_heartbeat_event, 0);
timer_setup(&peer->reconf_timer, sctp_generate_reconf_event, 0);
timer_setup(&peer->probe_timer, sctp_generate_probe_event, 0);
timer_setup(&peer->proto_unreach_timer,
sctp_generate_proto_unreach_event, 0);

Expand Down Expand Up @@ -131,6 +132,9 @@ void sctp_transport_free(struct sctp_transport *transport)
if (del_timer(&transport->reconf_timer))
sctp_transport_put(transport);

if (del_timer(&transport->probe_timer))
sctp_transport_put(transport);

/* Delete the ICMP proto unreachable timer if it's active. */
if (del_timer(&transport->proto_unreach_timer))
sctp_transport_put(transport);
Expand Down Expand Up @@ -207,6 +211,20 @@ void sctp_transport_reset_reconf_timer(struct sctp_transport *transport)
sctp_transport_hold(transport);
}

void sctp_transport_reset_probe_timer(struct sctp_transport *transport)
{
int scale = 1;

if (timer_pending(&transport->probe_timer))
return;
if (transport->pl.state == SCTP_PL_COMPLETE &&
transport->pl.probe_count == 1)
scale = 30; /* works as PMTU_RAISE_TIMER */
if (!mod_timer(&transport->probe_timer,
jiffies + transport->probe_interval * scale))
sctp_transport_hold(transport);
}

/* This transport has been assigned to an association.
* Initialize fields from the association or from the sock itself.
* Register the reference count in the association.
Expand Down

0 comments on commit 92548ec

Please sign in to comment.