Re: [RFC PATCH v2] netfilter: conntrack: simplify sctp state machine

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Thu, Jan 5, 2023 at 6:47 AM Sriram Yagnaraman
<sriram.yagnaraman@xxxxxxxx> wrote:
>
> All the paths in an SCTP connection are kept alive either by actual
> DATA/SACK running through the connection or by HEARTBEAT. This patch
> proposes a simple state machine with only two states OPEN_WAIT and
> ESTABLISHED (similar to UDP). The reason for this change is a full
> stateful approach to SCTP is difficult when the association is
> multihomed since the endpoints could use different paths in the network
> during the lifetime of an association.
>
> Default timeouts are:
> OPEN_WAIT:   3 seconds   (rto_initial)
> ESTABLISHED: 210 seconds (rto_max + hb_interval * path_max_retrans)
>
> Important changes/notes
> - Timeout is used to clean up conntrack entries
> - VTAG checks are kept as is (can be moved to a conntrack extension if
>   desired)
> - SCTP chunks are parsed only once, and a map is populated with the
>   information on the chunks present in the packet
> - ASSURED bit is NOT set in this version of the patch, need help
>   understanding when to set it
I can still see a regression from the change:

After the 1st connection shutdown successfully, the 2nd connection
with the same 4-tuple will fail as the old conntrack entry stays in
ESTABLISHED and waits to time out.

This is actually acting differently from TCP and UDP NAT.
Attached is a script to help reproduce it.

Thanks.

>
>
> Changes since v1:
> - sctp_new() brought back for unconfirmed CT entries
> - sctp_vtag_check() does all vtag related check, except for
>   HEARTBEAT/HEARTBEACK_ACK chunks
> - Style check fixes
> - Revert all UAPI changes
>
> Signed-off-by: Sriram Yagnaraman <sriram.yagnaraman@xxxxxxxx>
> ---
>  .../uapi/linux/netfilter/nf_conntrack_sctp.h  |   1 +
>  .../linux/netfilter/nfnetlink_cttimeout.h     |   1 +
>  net/netfilter/nf_conntrack_proto_sctp.c       | 624 +++++-------------
>  net/netfilter/nf_conntrack_standalone.c       |  72 +-
>  4 files changed, 179 insertions(+), 519 deletions(-)
>
> diff --git a/include/uapi/linux/netfilter/nf_conntrack_sctp.h b/include/uapi/linux/netfilter/nf_conntrack_sctp.h
> index c742469afe21..703c2ae5adf4 100644
> --- a/include/uapi/linux/netfilter/nf_conntrack_sctp.h
> +++ b/include/uapi/linux/netfilter/nf_conntrack_sctp.h
> @@ -9,6 +9,7 @@ enum sctp_conntrack {
>         SCTP_CONNTRACK_NONE,
>         SCTP_CONNTRACK_CLOSED,
>         SCTP_CONNTRACK_COOKIE_WAIT,
> +       SCTP_CONNTRACK_OPEN_WAIT = SCTP_CONNTRACK_COOKIE_WAIT,
>         SCTP_CONNTRACK_COOKIE_ECHOED,
>         SCTP_CONNTRACK_ESTABLISHED,
>         SCTP_CONNTRACK_SHUTDOWN_SENT,
> diff --git a/include/uapi/linux/netfilter/nfnetlink_cttimeout.h b/include/uapi/linux/netfilter/nfnetlink_cttimeout.h
> index 94e74034706d..a76ea744d3e3 100644
> --- a/include/uapi/linux/netfilter/nfnetlink_cttimeout.h
> +++ b/include/uapi/linux/netfilter/nfnetlink_cttimeout.h
> @@ -88,6 +88,7 @@ enum ctattr_timeout_sctp {
>         CTA_TIMEOUT_SCTP_UNSPEC,
>         CTA_TIMEOUT_SCTP_CLOSED,
>         CTA_TIMEOUT_SCTP_COOKIE_WAIT,
> +       CTA_TIMEOUT_SCTP_OPEN_WAIT = CTA_TIMEOUT_SCTP_COOKIE_WAIT,
>         CTA_TIMEOUT_SCTP_COOKIE_ECHOED,
>         CTA_TIMEOUT_SCTP_ESTABLISHED,
>         CTA_TIMEOUT_SCTP_SHUTDOWN_SENT,
> diff --git a/net/netfilter/nf_conntrack_proto_sctp.c b/net/netfilter/nf_conntrack_proto_sctp.c
> index d88b92a8ffca..8c18528972b7 100644
> --- a/net/netfilter/nf_conntrack_proto_sctp.c
> +++ b/net/netfilter/nf_conntrack_proto_sctp.c
> @@ -5,12 +5,13 @@
>   * Copyright (c) 2004 Kiran Kumar Immidi <immidi_kiran@xxxxxxxxx>
>   * Copyright (c) 2004-2012 Patrick McHardy <kaber@xxxxxxxxx>
>   *
> - * SCTP is defined in RFC 2960. References to various sections in this code
> + * SCTP is defined in RFC 4960. References to various sections in this code
>   * are to this RFC.
>   */
>
>  #include <linux/types.h>
>  #include <linux/timer.h>
> +#include <linux/jiffies.h>
>  #include <linux/netfilter.h>
>  #include <linux/in.h>
>  #include <linux/ip.h>
> @@ -27,127 +28,19 @@
>  #include <net/netfilter/nf_conntrack_ecache.h>
>  #include <net/netfilter/nf_conntrack_timeout.h>
>
> -/* FIXME: Examine ipfilter's timeouts and conntrack transitions more
> -   closely.  They're more complex. --RR
> -
> -   And so for me for SCTP :D -Kiran */
> +#define        SCTP_FLAG_HEARTBEAT_VTAG_FAILED 1
>
>  static const char *const sctp_conntrack_names[] = {
>         "NONE",
> -       "CLOSED",
> -       "COOKIE_WAIT",
> -       "COOKIE_ECHOED",
> +       "OPEN_WAIT",
>         "ESTABLISHED",
> -       "SHUTDOWN_SENT",
> -       "SHUTDOWN_RECD",
> -       "SHUTDOWN_ACK_SENT",
> -       "HEARTBEAT_SENT",
> -       "HEARTBEAT_ACKED",
>  };
>
>  #define SECS  * HZ
> -#define MINS  * 60 SECS
> -#define HOURS * 60 MINS
> -#define DAYS  * 24 HOURS
>
>  static const unsigned int sctp_timeouts[SCTP_CONNTRACK_MAX] = {
> -       [SCTP_CONNTRACK_CLOSED]                 = 10 SECS,
> -       [SCTP_CONNTRACK_COOKIE_WAIT]            = 3 SECS,
> -       [SCTP_CONNTRACK_COOKIE_ECHOED]          = 3 SECS,
> -       [SCTP_CONNTRACK_ESTABLISHED]            = 5 DAYS,
> -       [SCTP_CONNTRACK_SHUTDOWN_SENT]          = 300 SECS / 1000,
> -       [SCTP_CONNTRACK_SHUTDOWN_RECD]          = 300 SECS / 1000,
> -       [SCTP_CONNTRACK_SHUTDOWN_ACK_SENT]      = 3 SECS,
> -       [SCTP_CONNTRACK_HEARTBEAT_SENT]         = 30 SECS,
> -       [SCTP_CONNTRACK_HEARTBEAT_ACKED]        = 210 SECS,
> -       [SCTP_CONNTRACK_DATA_SENT]              = 30 SECS,
> -};
> -
> -#define        SCTP_FLAG_HEARTBEAT_VTAG_FAILED 1
> -
> -#define sNO SCTP_CONNTRACK_NONE
> -#define        sCL SCTP_CONNTRACK_CLOSED
> -#define        sCW SCTP_CONNTRACK_COOKIE_WAIT
> -#define        sCE SCTP_CONNTRACK_COOKIE_ECHOED
> -#define        sES SCTP_CONNTRACK_ESTABLISHED
> -#define        sSS SCTP_CONNTRACK_SHUTDOWN_SENT
> -#define        sSR SCTP_CONNTRACK_SHUTDOWN_RECD
> -#define        sSA SCTP_CONNTRACK_SHUTDOWN_ACK_SENT
> -#define        sHS SCTP_CONNTRACK_HEARTBEAT_SENT
> -#define        sHA SCTP_CONNTRACK_HEARTBEAT_ACKED
> -#define        sDS SCTP_CONNTRACK_DATA_SENT
> -#define        sIV SCTP_CONNTRACK_MAX
> -
> -/*
> -       These are the descriptions of the states:
> -
> -NOTE: These state names are tantalizingly similar to the states of an
> -SCTP endpoint. But the interpretation of the states is a little different,
> -considering that these are the states of the connection and not of an end
> -point. Please note the subtleties. -Kiran
> -
> -NONE              - Nothing so far.
> -COOKIE WAIT       - We have seen an INIT chunk in the original direction, or also
> -                   an INIT_ACK chunk in the reply direction.
> -COOKIE ECHOED     - We have seen a COOKIE_ECHO chunk in the original direction.
> -ESTABLISHED       - We have seen a COOKIE_ACK in the reply direction.
> -SHUTDOWN_SENT     - We have seen a SHUTDOWN chunk in the original direction.
> -SHUTDOWN_RECD     - We have seen a SHUTDOWN chunk in the reply direction.
> -SHUTDOWN_ACK_SENT - We have seen a SHUTDOWN_ACK chunk in the direction opposite
> -                   to that of the SHUTDOWN chunk.
> -CLOSED            - We have seen a SHUTDOWN_COMPLETE chunk in the direction of
> -                   the SHUTDOWN chunk. Connection is closed.
> -HEARTBEAT_SENT    - We have seen a HEARTBEAT in a new flow.
> -HEARTBEAT_ACKED   - We have seen a HEARTBEAT-ACK/DATA/SACK in the direction
> -                   opposite to that of the HEARTBEAT/DATA chunk. Secondary connection
> -                   is established.
> -DATA_SENT         - We have seen a DATA/SACK in a new flow.
> -*/
> -
> -/* TODO
> - - I have assumed that the first INIT is in the original direction.
> - This messes things when an INIT comes in the reply direction in CLOSED
> - state.
> - - Check the error type in the reply dir before transitioning from
> -cookie echoed to closed.
> - - Sec 5.2.4 of RFC 2960
> - - Full Multi Homing support.
> -*/
> -
> -/* SCTP conntrack state transitions */
> -static const u8 sctp_conntracks[2][12][SCTP_CONNTRACK_MAX] = {
> -       {
> -/*     ORIGINAL        */
> -/*                  sNO, sCL, sCW, sCE, sES, sSS, sSR, sSA, sHS, sHA, sDS */
> -/* init         */ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sSA, sCW, sHA, sCW},
> -/* init_ack     */ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sSA, sCL, sHA, sCL},
> -/* abort        */ {sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL},
> -/* shutdown     */ {sCL, sCL, sCW, sCE, sSS, sSS, sSR, sSA, sCL, sSS, sCL},
> -/* shutdown_ack */ {sSA, sCL, sCW, sCE, sES, sSA, sSA, sSA, sSA, sHA, sSA},
> -/* error        */ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sSA, sCL, sHA, sCL},/* Can't have Stale cookie*/
> -/* cookie_echo  */ {sCL, sCL, sCE, sCE, sES, sSS, sSR, sSA, sCL, sHA, sCL},/* 5.2.4 - Big TODO */
> -/* cookie_ack   */ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sSA, sCL, sHA, sCL},/* Can't come in orig dir */
> -/* shutdown_comp*/ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sCL, sCL, sHA, sCL},
> -/* heartbeat    */ {sHS, sCL, sCW, sCE, sES, sSS, sSR, sSA, sHS, sHA, sDS},
> -/* heartbeat_ack*/ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sSA, sHS, sHA, sDS},
> -/* data/sack    */ {sDS, sCL, sCW, sCE, sES, sSS, sSR, sSA, sHS, sHA, sDS}
> -       },
> -       {
> -/*     REPLY   */
> -/*                  sNO, sCL, sCW, sCE, sES, sSS, sSR, sSA, sHS, sHA, sDS */
> -/* init         */ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sSA, sIV, sHA, sIV},/* INIT in sCL Big TODO */
> -/* init_ack     */ {sIV, sCW, sCW, sCE, sES, sSS, sSR, sSA, sIV, sHA, sIV},
> -/* abort        */ {sIV, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sIV, sCL, sIV},
> -/* shutdown     */ {sIV, sCL, sCW, sCE, sSR, sSS, sSR, sSA, sIV, sSR, sIV},
> -/* shutdown_ack */ {sIV, sCL, sCW, sCE, sES, sSA, sSA, sSA, sIV, sHA, sIV},
> -/* error        */ {sIV, sCL, sCW, sCL, sES, sSS, sSR, sSA, sIV, sHA, sIV},
> -/* cookie_echo  */ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sSA, sIV, sHA, sIV},/* Can't come in reply dir */
> -/* cookie_ack   */ {sIV, sCL, sCW, sES, sES, sSS, sSR, sSA, sIV, sHA, sIV},
> -/* shutdown_comp*/ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sCL, sIV, sHA, sIV},
> -/* heartbeat    */ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sSA, sHS, sHA, sHA},
> -/* heartbeat_ack*/ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sSA, sHA, sHA, sHA},
> -/* data/sack    */ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sSA, sHA, sHA, sHA},
> -       }
> +       [SCTP_CONNTRACK_OPEN_WAIT]                      = 3 SECS,
> +       [SCTP_CONNTRACK_ESTABLISHED]            = 210 SECS,
>  };
>
>  #ifdef CONFIG_NF_CONNTRACK_PROCFS
> @@ -158,184 +51,6 @@ static void sctp_print_conntrack(struct seq_file *s, struct nf_conn *ct)
>  }
>  #endif
>
> -#define for_each_sctp_chunk(skb, sch, _sch, offset, dataoff, count)    \
> -for ((offset) = (dataoff) + sizeof(struct sctphdr), (count) = 0;       \
> -       (offset) < (skb)->len &&                                        \
> -       ((sch) = skb_header_pointer((skb), (offset), sizeof(_sch), &(_sch)));   \
> -       (offset) += (ntohs((sch)->length) + 3) & ~3, (count)++)
> -
> -/* Some validity checks to make sure the chunks are fine */
> -static int do_basic_checks(struct nf_conn *ct,
> -                          const struct sk_buff *skb,
> -                          unsigned int dataoff,
> -                          unsigned long *map)
> -{
> -       u_int32_t offset, count;
> -       struct sctp_chunkhdr _sch, *sch;
> -       int flag;
> -
> -       flag = 0;
> -
> -       for_each_sctp_chunk (skb, sch, _sch, offset, dataoff, count) {
> -               pr_debug("Chunk Num: %d  Type: %d\n", count, sch->type);
> -
> -               if (sch->type == SCTP_CID_INIT ||
> -                   sch->type == SCTP_CID_INIT_ACK ||
> -                   sch->type == SCTP_CID_SHUTDOWN_COMPLETE)
> -                       flag = 1;
> -
> -               /*
> -                * Cookie Ack/Echo chunks not the first OR
> -                * Init / Init Ack / Shutdown compl chunks not the only chunks
> -                * OR zero-length.
> -                */
> -               if (((sch->type == SCTP_CID_COOKIE_ACK ||
> -                     sch->type == SCTP_CID_COOKIE_ECHO ||
> -                     flag) &&
> -                    count != 0) || !sch->length) {
> -                       pr_debug("Basic checks failed\n");
> -                       return 1;
> -               }
> -
> -               if (map)
> -                       set_bit(sch->type, map);
> -       }
> -
> -       pr_debug("Basic checks passed\n");
> -       return count == 0;
> -}
> -
> -static int sctp_new_state(enum ip_conntrack_dir dir,
> -                         enum sctp_conntrack cur_state,
> -                         int chunk_type)
> -{
> -       int i;
> -
> -       pr_debug("Chunk type: %d\n", chunk_type);
> -
> -       switch (chunk_type) {
> -       case SCTP_CID_INIT:
> -               pr_debug("SCTP_CID_INIT\n");
> -               i = 0;
> -               break;
> -       case SCTP_CID_INIT_ACK:
> -               pr_debug("SCTP_CID_INIT_ACK\n");
> -               i = 1;
> -               break;
> -       case SCTP_CID_ABORT:
> -               pr_debug("SCTP_CID_ABORT\n");
> -               i = 2;
> -               break;
> -       case SCTP_CID_SHUTDOWN:
> -               pr_debug("SCTP_CID_SHUTDOWN\n");
> -               i = 3;
> -               break;
> -       case SCTP_CID_SHUTDOWN_ACK:
> -               pr_debug("SCTP_CID_SHUTDOWN_ACK\n");
> -               i = 4;
> -               break;
> -       case SCTP_CID_ERROR:
> -               pr_debug("SCTP_CID_ERROR\n");
> -               i = 5;
> -               break;
> -       case SCTP_CID_COOKIE_ECHO:
> -               pr_debug("SCTP_CID_COOKIE_ECHO\n");
> -               i = 6;
> -               break;
> -       case SCTP_CID_COOKIE_ACK:
> -               pr_debug("SCTP_CID_COOKIE_ACK\n");
> -               i = 7;
> -               break;
> -       case SCTP_CID_SHUTDOWN_COMPLETE:
> -               pr_debug("SCTP_CID_SHUTDOWN_COMPLETE\n");
> -               i = 8;
> -               break;
> -       case SCTP_CID_HEARTBEAT:
> -               pr_debug("SCTP_CID_HEARTBEAT");
> -               i = 9;
> -               break;
> -       case SCTP_CID_HEARTBEAT_ACK:
> -               pr_debug("SCTP_CID_HEARTBEAT_ACK");
> -               i = 10;
> -               break;
> -       case SCTP_CID_DATA:
> -       case SCTP_CID_SACK:
> -               pr_debug("SCTP_CID_DATA/SACK");
> -               i = 11;
> -               break;
> -       default:
> -               /* Other chunks like DATA or SACK do not change the state */
> -               pr_debug("Unknown chunk type, Will stay in %s\n",
> -                        sctp_conntrack_names[cur_state]);
> -               return cur_state;
> -       }
> -
> -       pr_debug("dir: %d   cur_state: %s  chunk_type: %d  new_state: %s\n",
> -                dir, sctp_conntrack_names[cur_state], chunk_type,
> -                sctp_conntrack_names[sctp_conntracks[dir][i][cur_state]]);
> -
> -       return sctp_conntracks[dir][i][cur_state];
> -}
> -
> -/* Don't need lock here: this conntrack not in circulation yet */
> -static noinline bool
> -sctp_new(struct nf_conn *ct, const struct sk_buff *skb,
> -        const struct sctphdr *sh, unsigned int dataoff)
> -{
> -       enum sctp_conntrack new_state;
> -       const struct sctp_chunkhdr *sch;
> -       struct sctp_chunkhdr _sch;
> -       u32 offset, count;
> -
> -       memset(&ct->proto.sctp, 0, sizeof(ct->proto.sctp));
> -       new_state = SCTP_CONNTRACK_MAX;
> -       for_each_sctp_chunk(skb, sch, _sch, offset, dataoff, count) {
> -               new_state = sctp_new_state(IP_CT_DIR_ORIGINAL,
> -                                          SCTP_CONNTRACK_NONE, sch->type);
> -
> -               /* Invalid: delete conntrack */
> -               if (new_state == SCTP_CONNTRACK_NONE ||
> -                   new_state == SCTP_CONNTRACK_MAX) {
> -                       pr_debug("nf_conntrack_sctp: invalid new deleting.\n");
> -                       return false;
> -               }
> -
> -               /* Copy the vtag into the state info */
> -               if (sch->type == SCTP_CID_INIT) {
> -                       struct sctp_inithdr _inithdr, *ih;
> -                       /* Sec 8.5.1 (A) */
> -                       if (sh->vtag)
> -                               return false;
> -
> -                       ih = skb_header_pointer(skb, offset + sizeof(_sch),
> -                                               sizeof(_inithdr), &_inithdr);
> -                       if (!ih)
> -                               return false;
> -
> -                       pr_debug("Setting vtag %x for new conn\n",
> -                                ih->init_tag);
> -
> -                       ct->proto.sctp.vtag[IP_CT_DIR_REPLY] = ih->init_tag;
> -               } else if (sch->type == SCTP_CID_HEARTBEAT ||
> -                          sch->type == SCTP_CID_DATA ||
> -                          sch->type == SCTP_CID_SACK) {
> -                       pr_debug("Setting vtag %x for secondary conntrack\n",
> -                                sh->vtag);
> -                       ct->proto.sctp.vtag[IP_CT_DIR_ORIGINAL] = sh->vtag;
> -               } else {
> -               /* If it is a shutdown ack OOTB packet, we expect a return
> -                  shutdown complete, otherwise an ABORT Sec 8.4 (5) and (8) */
> -                       pr_debug("Setting vtag %x for new conn OOTB\n",
> -                                sh->vtag);
> -                       ct->proto.sctp.vtag[IP_CT_DIR_REPLY] = sh->vtag;
> -               }
> -
> -               ct->proto.sctp.state = SCTP_CONNTRACK_NONE;
> -       }
> -
> -       return true;
> -}
> -
>  static bool sctp_error(struct sk_buff *skb,
>                        unsigned int dataoff,
>                        const struct nf_hook_state *state)
> @@ -367,6 +82,90 @@ static bool sctp_error(struct sk_buff *skb,
>         return true;
>  }
>
> +static void sctp_new(struct nf_conn *ct,
> +                    enum ip_conntrack_info ctinfo,
> +                    u32 init_vtag,
> +                    u32 vtag,
> +                    unsigned long *map)
> +{
> +       enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
> +
> +       memset(&ct->proto.sctp, 0, sizeof(ct->proto.sctp));
> +       ct->proto.sctp.state = SCTP_CONNTRACK_OPEN_WAIT;
> +       nf_conntrack_event_cache(IPCT_PROTOINFO, ct);
> +
> +       if (test_bit(SCTP_CID_INIT, map))
> +               ct->proto.sctp.vtag[!dir] = init_vtag;
> +       else if (test_bit(SCTP_CID_SHUTDOWN_ACK, map))
> +               /* If it is a shutdown ack OOTB packet, we expect a return
> +                * shutdown complete, otherwise an ABORT Sec 8.4 (5) and (8)
> +                */
> +               ct->proto.sctp.vtag[!dir] = vtag;
> +       else
> +               ct->proto.sctp.vtag[dir] = vtag;
> +}
> +
> +static bool sctp_vtag_check(struct nf_conn *ct,
> +                           enum ip_conntrack_info ctinfo,
> +                           u32 vtag,
> +                           unsigned long *map,
> +                           unsigned long *tflags)
> +{
> +       enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
> +
> +       /* Check the verification tag (Sec 8.5) */
> +       if (!test_bit(SCTP_CID_INIT, map) &&
> +           !test_bit(SCTP_CID_SHUTDOWN_COMPLETE, map) &&
> +           !test_bit(SCTP_CID_COOKIE_ECHO, map) &&
> +           !test_bit(SCTP_CID_ABORT, map) &&
> +           !test_bit(SCTP_CID_SHUTDOWN_ACK, map) &&
> +           !test_bit(SCTP_CID_HEARTBEAT, map) &&
> +           !test_bit(SCTP_CID_HEARTBEAT_ACK, map) &&
> +           vtag != ct->proto.sctp.vtag[dir]) {
> +               return false;
> +       }
> +
> +       /* Special cases of Verification tag check (Sec 8.5.1) */
> +       if (test_bit(SCTP_CID_INIT, map)) {
> +               /* (A) vtag MUST be zero */
> +               if (vtag != 0)
> +                       return false;
> +       }
> +       if (test_bit(SCTP_CID_ABORT, map)) {
> +               /* (B) vtag MUST match own vtag if T flag is unset OR
> +                * MUST match peer's vtag if T flag is set
> +                */
> +               if ((!test_bit(SCTP_CID_ABORT, tflags) &&
> +                    vtag != ct->proto.sctp.vtag[dir]) ||
> +                   (test_bit(SCTP_CID_ABORT, tflags) &&
> +                    vtag != ct->proto.sctp.vtag[!dir]))
> +                       return false;
> +       }
> +       if (test_bit(SCTP_CID_SHUTDOWN_COMPLETE, map)) {
> +               /* (C) vtag MUST match own vtag if T flag is unset OR
> +                * MUST match peer's vtag if T flag is set
> +                */
> +               if ((!test_bit(SCTP_CID_SHUTDOWN_COMPLETE, tflags) &&
> +                    vtag != ct->proto.sctp.vtag[dir]) ||
> +                   (test_bit(SCTP_CID_SHUTDOWN_COMPLETE, tflags) &&
> +                    vtag != ct->proto.sctp.vtag[!dir]))
> +                       return false;
> +       }
> +       if (test_bit(SCTP_CID_COOKIE_ECHO, map)) {
> +               /* (D) vtag must be same as init_vtag as found in INIT_ACK */
> +               if (vtag != ct->proto.sctp.vtag[dir])
> +                       return false;
> +       }
> +
> +       return true;
> +}
> +
> +#define for_each_sctp_chunk(skb, sch, _sch, offset, dataoff)   \
> +for ((offset) = (dataoff) + sizeof(struct sctphdr);    \
> +       ((sch) = skb_header_pointer((skb), (offset), sizeof(_sch), &(_sch))) && \
> +       (sch)->length;  \
> +       (offset) += (ntohs((sch)->length) + 3) & ~3)
> +
>  /* Returns verdict for packet, or -NF_ACCEPT for invalid. */
>  int nf_conntrack_sctp_packet(struct nf_conn *ct,
>                              struct sk_buff *skb,
> @@ -374,26 +173,39 @@ int nf_conntrack_sctp_packet(struct nf_conn *ct,
>                              enum ip_conntrack_info ctinfo,
>                              const struct nf_hook_state *state)
>  {
> -       enum sctp_conntrack new_state, old_state;
>         enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
> -       const struct sctphdr *sh;
> -       struct sctphdr _sctph;
> -       const struct sctp_chunkhdr *sch;
> -       struct sctp_chunkhdr _sch;
> -       u_int32_t offset, count;
> -       unsigned int *timeouts;
>         unsigned long map[256 / sizeof(unsigned long)] = { 0 };
> -       bool ignore = false;
> +       unsigned long tflags[256 / sizeof(unsigned long)] = { 0 };
> +       unsigned int *timeouts;
> +       u32 init_vtag = 0;
> +       u32 offset, count;
> +       struct sctphdr _sctph, *sctph;
> +       struct sctp_chunkhdr _sch, *sch;
>
>         if (sctp_error(skb, dataoff, state))
>                 return -NF_ACCEPT;
>
> -       sh = skb_header_pointer(skb, dataoff, sizeof(_sctph), &_sctph);
> -       if (sh == NULL)
> -               goto out;
> +       sctph = skb_header_pointer(skb, dataoff, sizeof(_sctph), &_sctph);
> +       if (!sctph)
> +               return -NF_ACCEPT;
>
> -       if (do_basic_checks(ct, skb, dataoff, map) != 0)
> -               goto out;
> +       for_each_sctp_chunk (skb, sch, _sch, offset, dataoff) {
> +               set_bit(sch->type, map);
> +               if (sch->flags & SCTP_CHUNK_FLAG_T)
> +                       set_bit(sch->type, tflags);
> +
> +               if (sch->type == SCTP_CID_INIT ||
> +                   sch->type == SCTP_CID_INIT_ACK) {
> +                       struct sctp_inithdr _inith, *inith;
> +
> +                       inith = skb_header_pointer(skb, offset + sizeof(_sch),
> +                                                  sizeof(_inith), &_inith);
> +                       if (inith)
> +                               init_vtag = inith->init_tag;
> +                       else
> +                               return -NF_ACCEPT;
> +               }
> +       }
>
>         if (!nf_ct_is_confirmed(ct)) {
>                 /* If an OOTB packet has any of these chunks discard (Sec 8.4) */
> @@ -402,167 +214,86 @@ int nf_conntrack_sctp_packet(struct nf_conn *ct,
>                     test_bit(SCTP_CID_COOKIE_ACK, map))
>                         return -NF_ACCEPT;
>
> -               if (!sctp_new(ct, skb, sh, dataoff))
> -                       return -NF_ACCEPT;
> -       } else {
> -               /* Check the verification tag (Sec 8.5) */
> -               if (!test_bit(SCTP_CID_INIT, map) &&
> -                   !test_bit(SCTP_CID_SHUTDOWN_COMPLETE, map) &&
> -                   !test_bit(SCTP_CID_COOKIE_ECHO, map) &&
> -                   !test_bit(SCTP_CID_ABORT, map) &&
> -                   !test_bit(SCTP_CID_SHUTDOWN_ACK, map) &&
> -                   !test_bit(SCTP_CID_HEARTBEAT, map) &&
> -                   !test_bit(SCTP_CID_HEARTBEAT_ACK, map) &&
> -                   sh->vtag != ct->proto.sctp.vtag[dir]) {
> -                       pr_debug("Verification tag check failed\n");
> -                       goto out;
> -               }
> +               sctp_new(ct, ctinfo, init_vtag, sctph->vtag, map);
> +               goto out;
>         }
>
> -       old_state = new_state = SCTP_CONNTRACK_NONE;
> +       /* don't renew timeout on init retransmit so
> +        * port reuse by client or NAT middlebox cannot
> +        * keep entry alive indefinitely (incl. nat info).
> +        */
> +       if (test_bit(SCTP_CID_INIT, map))
> +               return NF_ACCEPT;
> +
>         spin_lock_bh(&ct->lock);
> -       for_each_sctp_chunk (skb, sch, _sch, offset, dataoff, count) {
> -               /* Special cases of Verification tag check (Sec 8.5.1) */
> -               if (sch->type == SCTP_CID_INIT) {
> -                       /* Sec 8.5.1 (A) */
> -                       if (sh->vtag != 0)
> -                               goto out_unlock;
> -               } else if (sch->type == SCTP_CID_ABORT) {
> -                       /* Sec 8.5.1 (B) */
> -                       if (sh->vtag != ct->proto.sctp.vtag[dir] &&
> -                           sh->vtag != ct->proto.sctp.vtag[!dir])
> -                               goto out_unlock;
> -               } else if (sch->type == SCTP_CID_SHUTDOWN_COMPLETE) {
> -                       /* Sec 8.5.1 (C) */
> -                       if (sh->vtag != ct->proto.sctp.vtag[dir] &&
> -                           sh->vtag != ct->proto.sctp.vtag[!dir] &&
> -                           sch->flags & SCTP_CHUNK_FLAG_T)
> -                               goto out_unlock;
> -               } else if (sch->type == SCTP_CID_COOKIE_ECHO) {
> -                       /* Sec 8.5.1 (D) */
> -                       if (sh->vtag != ct->proto.sctp.vtag[dir])
> -                               goto out_unlock;
> -               } else if (sch->type == SCTP_CID_HEARTBEAT) {
> -                       if (ct->proto.sctp.vtag[dir] == 0) {
> -                               pr_debug("Setting %d vtag %x for dir %d\n", sch->type, sh->vtag, dir);
> -                               ct->proto.sctp.vtag[dir] = sh->vtag;
> -                       } else if (sh->vtag != ct->proto.sctp.vtag[dir]) {
> -                               if (test_bit(SCTP_CID_DATA, map) || ignore)
> -                                       goto out_unlock;
> -
> -                               ct->proto.sctp.flags |= SCTP_FLAG_HEARTBEAT_VTAG_FAILED;
> -                               ct->proto.sctp.last_dir = dir;
> -                               ignore = true;
> -                               continue;
> -                       } else if (ct->proto.sctp.flags & SCTP_FLAG_HEARTBEAT_VTAG_FAILED) {
> -                               ct->proto.sctp.flags &= ~SCTP_FLAG_HEARTBEAT_VTAG_FAILED;
> -                       }
> -               } else if (sch->type == SCTP_CID_HEARTBEAT_ACK) {
> -                       if (ct->proto.sctp.vtag[dir] == 0) {
> -                               pr_debug("Setting vtag %x for dir %d\n",
> -                                        sh->vtag, dir);
> -                               ct->proto.sctp.vtag[dir] = sh->vtag;
> -                       } else if (sh->vtag != ct->proto.sctp.vtag[dir]) {
> -                               if (test_bit(SCTP_CID_DATA, map) || ignore)
> -                                       goto out_unlock;
> -
> -                               if ((ct->proto.sctp.flags & SCTP_FLAG_HEARTBEAT_VTAG_FAILED) == 0 ||
> -                                   ct->proto.sctp.last_dir == dir)
> -                                       goto out_unlock;
> -
> -                               ct->proto.sctp.flags &= ~SCTP_FLAG_HEARTBEAT_VTAG_FAILED;
> -                               ct->proto.sctp.vtag[dir] = sh->vtag;
> -                               ct->proto.sctp.vtag[!dir] = 0;
> -                       } else if (ct->proto.sctp.flags & SCTP_FLAG_HEARTBEAT_VTAG_FAILED) {
> -                               ct->proto.sctp.flags &= ~SCTP_FLAG_HEARTBEAT_VTAG_FAILED;
> -                       }
> -               } else if (sch->type == SCTP_CID_DATA || sch->type == SCTP_CID_SACK) {
> -                       if (ct->proto.sctp.vtag[dir] == 0) {
> -                               pr_debug("Setting vtag %x for dir %d\n", sh->vtag, dir);
> -                               ct->proto.sctp.vtag[dir] = sh->vtag;
> -                       }
> -               }
> +       if (!ct->proto.sctp.vtag[!dir] &&
> +           test_bit(SCTP_CID_INIT_ACK, map))
> +               ct->proto.sctp.vtag[!dir] = init_vtag;
>
> -               old_state = ct->proto.sctp.state;
> -               new_state = sctp_new_state(dir, old_state, sch->type);
> +       if (!ct->proto.sctp.vtag[dir])
> +               ct->proto.sctp.vtag[dir] = sctph->vtag;
>
> -               /* Invalid */
> -               if (new_state == SCTP_CONNTRACK_MAX) {
> -                       pr_debug("nf_conntrack_sctp: Invalid dir=%i ctype=%u "
> -                                "conntrack=%u\n",
> -                                dir, sch->type, old_state);
> -                       goto out_unlock;
> -               }
> +       /* we have seen traffic both ways, go to established */
> +       if (dir == IP_CT_DIR_REPLY &&
> +           ct->proto.sctp.state == SCTP_CONNTRACK_OPEN_WAIT) {
> +               ct->proto.sctp.state = SCTP_CONNTRACK_ESTABLISHED;
> +               nf_conntrack_event_cache(IPCT_PROTOINFO, ct);
>
> -               /* If it is an INIT or an INIT ACK note down the vtag */
> -               if (sch->type == SCTP_CID_INIT ||
> -                   sch->type == SCTP_CID_INIT_ACK) {
> -                       struct sctp_inithdr _inithdr, *ih;
> +               if (!test_and_set_bit(IPS_ASSURED_BIT, &ct->status))
> +                       nf_conntrack_event_cache(IPCT_ASSURED, ct);
> +       }
>
> -                       ih = skb_header_pointer(skb, offset + sizeof(_sch),
> -                                               sizeof(_inithdr), &_inithdr);
> -                       if (ih == NULL)
> +       if (test_bit(SCTP_CID_HEARTBEAT, map)) {
> +               if (sctph->vtag != ct->proto.sctp.vtag[dir]) {
> +                       if (test_bit(SCTP_CID_DATA, map))
>                                 goto out_unlock;
> -                       pr_debug("Setting vtag %x for dir %d\n",
> -                                ih->init_tag, !dir);
> -                       ct->proto.sctp.vtag[!dir] = ih->init_tag;
> -
> -                       /* don't renew timeout on init retransmit so
> -                        * port reuse by client or NAT middlebox cannot
> -                        * keep entry alive indefinitely (incl. nat info).
> -                        */
> -                       if (new_state == SCTP_CONNTRACK_CLOSED &&
> -                           old_state == SCTP_CONNTRACK_CLOSED &&
> -                           nf_ct_is_confirmed(ct))
> -                               ignore = true;
> +
> +                       ct->proto.sctp.flags |= SCTP_FLAG_HEARTBEAT_VTAG_FAILED;
> +                       ct->proto.sctp.last_dir = dir;
> +               } else if (ct->proto.sctp.flags & SCTP_FLAG_HEARTBEAT_VTAG_FAILED) {
> +                       ct->proto.sctp.flags &= ~SCTP_FLAG_HEARTBEAT_VTAG_FAILED;
>                 }
> +       }
> +       if (test_bit(SCTP_CID_HEARTBEAT_ACK, map)) {
> +               if (sctph->vtag != ct->proto.sctp.vtag[dir]) {
> +                       if (test_bit(SCTP_CID_DATA, map))
> +                               goto out_unlock;
> +
> +                       if ((ct->proto.sctp.flags & SCTP_FLAG_HEARTBEAT_VTAG_FAILED) == 0 ||
> +                           ct->proto.sctp.last_dir == dir)
> +                               goto out_unlock;
>
> -               ct->proto.sctp.state = new_state;
> -               if (old_state != new_state)
> -                       nf_conntrack_event_cache(IPCT_PROTOINFO, ct);
> +                       ct->proto.sctp.flags &= ~SCTP_FLAG_HEARTBEAT_VTAG_FAILED;
> +                       ct->proto.sctp.vtag[dir] = sctph->vtag;
> +                       ct->proto.sctp.vtag[!dir] = 0;
> +               } else if (ct->proto.sctp.flags & SCTP_FLAG_HEARTBEAT_VTAG_FAILED) {
> +                       ct->proto.sctp.flags &= ~SCTP_FLAG_HEARTBEAT_VTAG_FAILED;
> +               }
>         }
>         spin_unlock_bh(&ct->lock);
>
> -       /* allow but do not refresh timeout */
> -       if (ignore)
> -               return NF_ACCEPT;
> +       if (!sctp_vtag_check(ct, ctinfo, sctph->vtag, map, tflags)) {
> +               nf_ct_l4proto_log_invalid(skb, ct, state,
> +                                         "verification tag check failed %x vs (%x: dir %d) and (%x: dir %d)",
> +                                         sctph->vtag, ct->proto.sctp.vtag[dir], dir,
> +                                         ct->proto.sctp.vtag[!dir], !dir);
> +               return -NF_ACCEPT;
> +       }
>
> +out:
>         timeouts = nf_ct_timeout_lookup(ct);
>         if (!timeouts)
>                 timeouts = nf_sctp_pernet(nf_ct_net(ct))->timeouts;
>
> -       nf_ct_refresh_acct(ct, ctinfo, skb, timeouts[new_state]);
> -
> -       if (old_state == SCTP_CONNTRACK_COOKIE_ECHOED &&
> -           dir == IP_CT_DIR_REPLY &&
> -           new_state == SCTP_CONNTRACK_ESTABLISHED) {
> -               pr_debug("Setting assured bit\n");
> -               set_bit(IPS_ASSURED_BIT, &ct->status);
> -               nf_conntrack_event_cache(IPCT_ASSURED, ct);
> -       }
> +       nf_ct_refresh_acct(ct, ctinfo, skb, timeouts[ct->proto.sctp.state]);
>
>         return NF_ACCEPT;
>
>  out_unlock:
>         spin_unlock_bh(&ct->lock);
> -out:
>         return -NF_ACCEPT;
>  }
>
> -static bool sctp_can_early_drop(const struct nf_conn *ct)
> -{
> -       switch (ct->proto.sctp.state) {
> -       case SCTP_CONNTRACK_SHUTDOWN_SENT:
> -       case SCTP_CONNTRACK_SHUTDOWN_RECD:
> -       case SCTP_CONNTRACK_SHUTDOWN_ACK_SENT:
> -               return true;
> -       default:
> -               break;
> -       }
> -
> -       return false;
> -}
> -
>  #if IS_ENABLED(CONFIG_NF_CT_NETLINK)
>
>  #include <linux/netfilter/nfnetlink.h>
> @@ -670,7 +401,7 @@ static int sctp_timeout_nlattr_to_obj(struct nlattr *tb[],
>                 }
>         }
>
> -       timeouts[CTA_TIMEOUT_SCTP_UNSPEC] = timeouts[CTA_TIMEOUT_SCTP_CLOSED];
> +       timeouts[CTA_TIMEOUT_SCTP_UNSPEC] = timeouts[CTA_TIMEOUT_SCTP_OPEN_WAIT];
>         return 0;
>  }
>
> @@ -692,16 +423,8 @@ sctp_timeout_obj_to_nlattr(struct sk_buff *skb, const void *data)
>
>  static const struct nla_policy
>  sctp_timeout_nla_policy[CTA_TIMEOUT_SCTP_MAX+1] = {
> -       [CTA_TIMEOUT_SCTP_CLOSED]               = { .type = NLA_U32 },
> -       [CTA_TIMEOUT_SCTP_COOKIE_WAIT]          = { .type = NLA_U32 },
> -       [CTA_TIMEOUT_SCTP_COOKIE_ECHOED]        = { .type = NLA_U32 },
> +       [CTA_TIMEOUT_SCTP_OPEN_WAIT]            = { .type = NLA_U32 },
>         [CTA_TIMEOUT_SCTP_ESTABLISHED]          = { .type = NLA_U32 },
> -       [CTA_TIMEOUT_SCTP_SHUTDOWN_SENT]        = { .type = NLA_U32 },
> -       [CTA_TIMEOUT_SCTP_SHUTDOWN_RECD]        = { .type = NLA_U32 },
> -       [CTA_TIMEOUT_SCTP_SHUTDOWN_ACK_SENT]    = { .type = NLA_U32 },
> -       [CTA_TIMEOUT_SCTP_HEARTBEAT_SENT]       = { .type = NLA_U32 },
> -       [CTA_TIMEOUT_SCTP_HEARTBEAT_ACKED]      = { .type = NLA_U32 },
> -       [CTA_TIMEOUT_SCTP_DATA_SENT]            = { .type = NLA_U32 },
>  };
>  #endif /* CONFIG_NF_CONNTRACK_TIMEOUT */
>
> @@ -716,7 +439,7 @@ void nf_conntrack_sctp_init_net(struct net *net)
>         /* timeouts[0] is unused, init it so ->timeouts[0] contains
>          * 'new' timeout, like udp or icmp.
>          */
> -       sn->timeouts[0] = sctp_timeouts[SCTP_CONNTRACK_CLOSED];
> +       sn->timeouts[0] = sctp_timeouts[SCTP_CONNTRACK_OPEN_WAIT];
>  }
>
>  const struct nf_conntrack_l4proto nf_conntrack_l4proto_sctp = {
> @@ -724,7 +447,6 @@ const struct nf_conntrack_l4proto nf_conntrack_l4proto_sctp = {
>  #ifdef CONFIG_NF_CONNTRACK_PROCFS
>         .print_conntrack        = sctp_print_conntrack,
>  #endif
> -       .can_early_drop         = sctp_can_early_drop,
>  #if IS_ENABLED(CONFIG_NF_CT_NETLINK)
>         .nlattr_size            = SCTP_NLATTR_SIZE,
>         .to_nlattr              = sctp_to_nlattr,
> diff --git a/net/netfilter/nf_conntrack_standalone.c b/net/netfilter/nf_conntrack_standalone.c
> index 0250725e38a4..07da9db31783 100644
> --- a/net/netfilter/nf_conntrack_standalone.c
> +++ b/net/netfilter/nf_conntrack_standalone.c
> @@ -593,16 +593,8 @@ enum nf_ct_sysctl_index {
>         NF_SYSCTL_CT_PROTO_TIMEOUT_ICMP,
>         NF_SYSCTL_CT_PROTO_TIMEOUT_ICMPV6,
>  #ifdef CONFIG_NF_CT_PROTO_SCTP
> -       NF_SYSCTL_CT_PROTO_TIMEOUT_SCTP_CLOSED,
> -       NF_SYSCTL_CT_PROTO_TIMEOUT_SCTP_COOKIE_WAIT,
> -       NF_SYSCTL_CT_PROTO_TIMEOUT_SCTP_COOKIE_ECHOED,
> +       NF_SYSCTL_CT_PROTO_TIMEOUT_SCTP_OPEN_WAIT,
>         NF_SYSCTL_CT_PROTO_TIMEOUT_SCTP_ESTABLISHED,
> -       NF_SYSCTL_CT_PROTO_TIMEOUT_SCTP_SHUTDOWN_SENT,
> -       NF_SYSCTL_CT_PROTO_TIMEOUT_SCTP_SHUTDOWN_RECD,
> -       NF_SYSCTL_CT_PROTO_TIMEOUT_SCTP_SHUTDOWN_ACK_SENT,
> -       NF_SYSCTL_CT_PROTO_TIMEOUT_SCTP_HEARTBEAT_SENT,
> -       NF_SYSCTL_CT_PROTO_TIMEOUT_SCTP_HEARTBEAT_ACKED,
> -       NF_SYSCTL_CT_PROTO_TIMEOUT_SCTP_DATA_SENT,
>  #endif
>  #ifdef CONFIG_NF_CT_PROTO_DCCP
>         NF_SYSCTL_CT_PROTO_TIMEOUT_DCCP_REQUEST,
> @@ -839,20 +831,8 @@ static struct ctl_table nf_ct_sysctl_table[] = {
>                 .proc_handler   = proc_dointvec_jiffies,
>         },
>  #ifdef CONFIG_NF_CT_PROTO_SCTP
> -       [NF_SYSCTL_CT_PROTO_TIMEOUT_SCTP_CLOSED] = {
> -               .procname       = "nf_conntrack_sctp_timeout_closed",
> -               .maxlen         = sizeof(unsigned int),
> -               .mode           = 0644,
> -               .proc_handler   = proc_dointvec_jiffies,
> -       },
> -       [NF_SYSCTL_CT_PROTO_TIMEOUT_SCTP_COOKIE_WAIT] = {
> -               .procname       = "nf_conntrack_sctp_timeout_cookie_wait",
> -               .maxlen         = sizeof(unsigned int),
> -               .mode           = 0644,
> -               .proc_handler   = proc_dointvec_jiffies,
> -       },
> -       [NF_SYSCTL_CT_PROTO_TIMEOUT_SCTP_COOKIE_ECHOED] = {
> -               .procname       = "nf_conntrack_sctp_timeout_cookie_echoed",
> +       [NF_SYSCTL_CT_PROTO_TIMEOUT_SCTP_OPEN_WAIT] = {
> +               .procname       = "nf_conntrack_sctp_timeout_open_wait",
>                 .maxlen         = sizeof(unsigned int),
>                 .mode           = 0644,
>                 .proc_handler   = proc_dointvec_jiffies,
> @@ -863,42 +843,6 @@ static struct ctl_table nf_ct_sysctl_table[] = {
>                 .mode           = 0644,
>                 .proc_handler   = proc_dointvec_jiffies,
>         },
> -       [NF_SYSCTL_CT_PROTO_TIMEOUT_SCTP_SHUTDOWN_SENT] = {
> -               .procname       = "nf_conntrack_sctp_timeout_shutdown_sent",
> -               .maxlen         = sizeof(unsigned int),
> -               .mode           = 0644,
> -               .proc_handler   = proc_dointvec_jiffies,
> -       },
> -       [NF_SYSCTL_CT_PROTO_TIMEOUT_SCTP_SHUTDOWN_RECD] = {
> -               .procname       = "nf_conntrack_sctp_timeout_shutdown_recd",
> -               .maxlen         = sizeof(unsigned int),
> -               .mode           = 0644,
> -               .proc_handler   = proc_dointvec_jiffies,
> -       },
> -       [NF_SYSCTL_CT_PROTO_TIMEOUT_SCTP_SHUTDOWN_ACK_SENT] = {
> -               .procname       = "nf_conntrack_sctp_timeout_shutdown_ack_sent",
> -               .maxlen         = sizeof(unsigned int),
> -               .mode           = 0644,
> -               .proc_handler   = proc_dointvec_jiffies,
> -       },
> -       [NF_SYSCTL_CT_PROTO_TIMEOUT_SCTP_HEARTBEAT_SENT] = {
> -               .procname       = "nf_conntrack_sctp_timeout_heartbeat_sent",
> -               .maxlen         = sizeof(unsigned int),
> -               .mode           = 0644,
> -               .proc_handler   = proc_dointvec_jiffies,
> -       },
> -       [NF_SYSCTL_CT_PROTO_TIMEOUT_SCTP_HEARTBEAT_ACKED] = {
> -               .procname       = "nf_conntrack_sctp_timeout_heartbeat_acked",
> -               .maxlen         = sizeof(unsigned int),
> -               .mode           = 0644,
> -               .proc_handler   = proc_dointvec_jiffies,
> -       },
> -       [NF_SYSCTL_CT_PROTO_TIMEOUT_SCTP_DATA_SENT] = {
> -               .procname       = "nf_conntrack_sctp_timeout_data_sent",
> -               .maxlen         = sizeof(unsigned int),
> -               .mode           = 0644,
> -               .proc_handler   = proc_dointvec_jiffies,
> -       },
>  #endif
>  #ifdef CONFIG_NF_CT_PROTO_DCCP
>         [NF_SYSCTL_CT_PROTO_TIMEOUT_DCCP_REQUEST] = {
> @@ -1034,16 +978,8 @@ static void nf_conntrack_standalone_init_sctp_sysctl(struct net *net,
>         table[NF_SYSCTL_CT_PROTO_TIMEOUT_SCTP_ ## XNAME].data = \
>                         &(sn)->timeouts[SCTP_CONNTRACK_ ## XNAME]
>
> -       XASSIGN(CLOSED, sn);
> -       XASSIGN(COOKIE_WAIT, sn);
> -       XASSIGN(COOKIE_ECHOED, sn);
> +       XASSIGN(OPEN_WAIT, sn);
>         XASSIGN(ESTABLISHED, sn);
> -       XASSIGN(SHUTDOWN_SENT, sn);
> -       XASSIGN(SHUTDOWN_RECD, sn);
> -       XASSIGN(SHUTDOWN_ACK_SENT, sn);
> -       XASSIGN(HEARTBEAT_SENT, sn);
> -       XASSIGN(HEARTBEAT_ACKED, sn);
> -       XASSIGN(DATA_SENT, sn);
>  #undef XASSIGN
>  #endif
>  }
> --
> 2.34.1
>
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
#
# Testing For TCP/UDP/SCTP(4/6) NAT.
# TOPO: CLIENT_NS (link0)<--->(link1) HOST/ROUTER (link2)<--->(link3) SERVER_NS

CLIENT_NS="client-ns"
CLIENT_IP4="198.51.100.1"
CLIENT_GW4="198.51.100.2"
CLIENT_IP6="2001:db8:1::1"
CLIENT_GW6="2001:db8:1::2"

SERVER_NS="server-ns"
SERVER_IP4="203.0.113.1"
SERVER_GW4="203.0.113.2"
SERVER_IP6="2001:db8:2::1"
SERVER_GW6="2001:db8:2::2"

setup() {
	ip netns add $CLIENT_NS
	ip netns add $SERVER_NS
	ip link add link1 type veth peer name link0 netns $CLIENT_NS
	ip link add link2 type veth peer name link3 netns $SERVER_NS

	ip net exec $CLIENT_NS ip link set link0 up
	ip net exec $CLIENT_NS ip addr add $CLIENT_IP4/24 dev link0
	ip net exec $CLIENT_NS ip addr add $CLIENT_IP6/64 dev link0 nodad
	ip net exec $CLIENT_NS ip route add $SERVER_IP4 dev link0 via $CLIENT_GW4
	ip net exec $CLIENT_NS ip route add $SERVER_IP6 dev link0 via $CLIENT_GW6

	ip link set link1 up
	ip link set link2 up
	ip addr add $CLIENT_GW4/24 dev link1
	ip addr add $CLIENT_GW6/64 dev link1 nodad
	ip addr add $SERVER_GW4/24 dev link2
	ip addr add $SERVER_GW6/64 dev link2 nodad
	iptables -t nat -A POSTROUTING -o link2 -j MASQUERADE
	ip6tables -t nat -A POSTROUTING -o link2 -j MASQUERADE

	ip net exec $SERVER_NS ip link set link3 up
	ip net exec $SERVER_NS ip addr add $SERVER_IP4/24 dev link3
	ip net exec $SERVER_NS ip addr add $SERVER_IP6/64 dev link3 nodad

	IP4_FWD=`cat /proc/sys/net/ipv4/ip_forward`
	IP6_FWD=`cat /proc/sys/net/ipv6/conf/all/forwarding`
	sysctl -w net.ipv4.ip_forward=1 2>&1 >/dev/null
	sysctl -w net.ipv6.conf.all.forwarding=1 2>&1 >/dev/null
	modprobe sctp
}

cleanup() {
	sysctl -w net.ipv4.ip_forward=$IP4_FWD 2>&1 >/dev/null
	sysctl -w net.ipv6.conf.all.forwarding=$IP6_FWD 2>&1 >/dev/null
	ip link del link1
	ip link del link2
	ip netns del "$CLIENT_NS"
	ip netns del "$SERVER_NS"
}

testup() {
	local ipaddr="$1"
	local proto="$2"

	ip net exec $SERVER_NS nc -l $proto -p 1234 2>&1 >/dev/null &
	disown
	echo -n "msg1" | ip net exec $CLIENT_NS nc $ipaddr 1234 -p 4321 $proto

	ip net exec $SERVER_NS nc -l $proto -p 1234 2>&1 >/dev/null &
	disown
	echo -n "msg2" | ip net exec $CLIENT_NS nc $ipaddr 1234 -p 4321 $proto

	RET=$?
	ip net exec $SERVER_NS pkill nc
	return $RET
}

if ! nc --version &> /dev/null; then
	echo "SKIP: Could not run test without nc tool"
	exit 4
fi

trap cleanup EXIT
setup &&
testup $SERVER_IP4 && echo "TCP4 NAT: PASS" &&
testup $SERVER_IP6 && echo "TCP6 NAT: PASS" &&
testup $SERVER_IP4 "--udp" && echo "UDP4 NAT: PASS" &&
testup $SERVER_IP6 "--udp" && echo "UDP6 NAT: PASS" &&
testup $SERVER_IP4 "--sctp" && echo "SCTP4 NAT: PASS" &&
testup $SERVER_IP6 "--sctp" && echo "SCTP6 NAT: PASS"
exit $?

[Index of Archives]     [Netfitler Users]     [Berkeley Packet Filter]     [LARTC]     [Bugtraq]     [Yosemite Forum]

  Powered by Linux