sctp: add the probe timer in transport for PLPMTUD
authorXin Long <lucien.xin@gmail.com>
Tue, 22 Jun 2021 18:04:51 +0000 (14:04 -0400)
committerDavid S. Miller <davem@davemloft.net>
Tue, 22 Jun 2021 18:28:52 +0000 (11:28 -0700)
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>
include/net/sctp/command.h
include/net/sctp/constants.h
include/net/sctp/sctp.h
include/net/sctp/sm.h
include/net/sctp/structs.h
net/sctp/debug.c
net/sctp/sm_sideeffect.c
net/sctp/sm_statefuns.c
net/sctp/sm_statetable.c
net/sctp/transport.c

index 5e848884ff61a9ddb6d47f2f2f4c9ab8fc520842..2058fabffbf6d988e97ccaeb466bec0221965dec 100644 (file)
@@ -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. */
index 85f6a105c59dd1126ad26635f947b8a376dd17f2..265fffa33dad3dd52b2c5f680b37d42f692b34e1 100644 (file)
@@ -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,
 };
index 08347d3f004f662cbed09703eedde0a4c772c517..f7e083602c108a17470792742ad0aeebf9f42bf1 100644 (file)
@@ -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;
+               }
        }
 }
 
@@ -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;
index 09c59154634defd0f2118e913cc18fc44261a47b..45542e2bac938f9eaa6abfd7a01503c1d1985a6d 100644 (file)
@@ -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;
 
@@ -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);
index 85d3566c22276c8427d9ed76c0c54627fe73016e..a3772f8ee7f66f93ecc72eec06f11bfe9c7a09d6 100644 (file)
@@ -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
@@ -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);
index c4d9c7feffb9eaff5bca9e1a6d5a6ad220bebcaa..ccd773e4c371460b1c9f62381cfe2791ab1d4874 100644 (file)
@@ -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",
 };
index ce15d590a615a3dd93d019011c93bd64bd413e34..b3815b568e8e5cfbf51a20d7358566462b0867bd 100644 (file)
@@ -471,6 +471,38 @@ out_unlock:
        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)
 {
@@ -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;
index 4f30388a0dd0b5b1042e2e7a565f96e91cd01844..3b99eda50618427e504b913fde9d8f24cda6cd66 100644 (file)
@@ -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.
  *
index c82c4233ec6bd173994a9a24f1e54c3a620746ed..1816a4410b2be591165a4d6046f82603ac328bf3 100644 (file)
@@ -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,
@@ -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,
 };
index bf0ac467e75799555c8d7548a1ab45557b18ecf1..ca3343c2c80e1bbfd1d55b979a542d7514446231 100644 (file)
@@ -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);
 
@@ -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);
@@ -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.