From d2baf65c86ac164346f25828c54775bab8343841 Mon Sep 17 00:00:00 2001 From: Konstantin Ananyev Date: Wed, 7 Jul 2021 17:25:00 +0000 Subject: l4p/tcp: introduce tle_tcp_stream_abort() API Introduce ability ot perform abnormal connection termination. Signed-off-by: Konstantin Ananyev Change-Id: I75b5153505348ceface903cd2c8e012707631168 --- lib/libtle_l4p/tcp_ctl.h | 55 +++++++++++++++++++++++++++- lib/libtle_l4p/tcp_rxtx.c | 4 +++ lib/libtle_l4p/tcp_stream.c | 87 +++++++++++++++++++++++++++------------------ lib/libtle_l4p/tcp_stream.h | 1 + lib/libtle_l4p/tle_tcp.h | 24 ++++++++++++- 5 files changed, 135 insertions(+), 36 deletions(-) diff --git a/lib/libtle_l4p/tcp_ctl.h b/lib/libtle_l4p/tcp_ctl.h index 57f3ffc..814cc82 100644 --- a/lib/libtle_l4p/tcp_ctl.h +++ b/lib/libtle_l4p/tcp_ctl.h @@ -103,6 +103,10 @@ calc_rx_wnd(const struct tle_tcp_stream *s, uint32_t scale) return _rte_ring_get_mask(s->rx.q) << scale; } +/* + * Helper functions for stream_close() + */ + /* empty stream's send queue */ static inline void empty_tq(struct tle_tcp_stream *s) @@ -152,7 +156,7 @@ tcp_stream_reset(struct tle_ctx *ctx, struct tle_tcp_stream *s) rte_atomic32_set(&s->tx.arm, 0); /* reset TCB */ - uop = s->tcb.uop & ~TLE_TCP_OP_CLOSE; + uop = s->tcb.uop & ~TLE_TCP_OP_CLOSE_ABORT; memset(&s->tcb, 0, sizeof(s->tcb)); /* reset remote events */ @@ -192,6 +196,55 @@ tcp_stream_reset(struct tle_ctx *ctx, struct tle_tcp_stream *s) } } +/* + * - set new uop (CLOSE, ABORT) atomically + * - mark stream down + * - reset events/callbacks + * - if no further actions are necessary, then reset the stream straightway + * @return + * - negative error code + * - zero if stream was terminated and no further action is required + * - current stream state (TLE_TCP_ST *) otherwise + */ +static inline int +stream_close_prolog(struct tle_ctx *ctx, struct tle_tcp_stream *s, uint16_t nop) +{ + uint16_t uop; + uint32_t state; + static const struct tle_stream_cb zcb; + + /* check was *nop* already invoked */ + uop = s->tcb.uop; + if ((uop & nop) == nop) + return -EDEADLK; + + /* record that *nop* was already invoked */ + if (rte_atomic16_cmpset(&s->tcb.uop, uop, uop | nop) == 0) + return -EDEADLK; + + /* mark stream as unavaialbe for RX/TX. */ + tcp_stream_down(s); + + /* reset events/callbacks */ + s->rx.ev = NULL; + s->tx.ev = NULL; + s->err.ev = NULL; + + s->rx.cb = zcb; + s->tx.cb = zcb; + s->err.cb = zcb; + + state = s->tcb.state; + + /* CLOSED, LISTEN, SYN_SENT - we can close the stream straighway */ + if (state <= TLE_TCP_ST_SYN_SENT) { + tcp_stream_reset(ctx, s); + return 0; + } + + return state; +} + static inline struct tle_tcp_stream * tcp_stream_get(struct tle_ctx *ctx, uint32_t flag) { diff --git a/lib/libtle_l4p/tcp_rxtx.c b/lib/libtle_l4p/tcp_rxtx.c index 03b6c63..af885ca 100644 --- a/lib/libtle_l4p/tcp_rxtx.c +++ b/lib/libtle_l4p/tcp_rxtx.c @@ -2697,6 +2697,10 @@ tx_stream(struct tle_tcp_stream *s, uint32_t tms) /* start RTO timer. */ if (s->tcb.snd.nxt != s->tcb.snd.una) timer_start(s); + } else if (state == TLE_TCP_ST_CLOSED) { + if ((s->tcb.snd.close_flags & TCP_FLAG_RST) != 0) + send_rst(s, s->tcb.snd.nxt); + stream_term(s); } } diff --git a/lib/libtle_l4p/tcp_stream.c b/lib/libtle_l4p/tcp_stream.c index d97a6ce..59299f7 100644 --- a/lib/libtle_l4p/tcp_stream.c +++ b/lib/libtle_l4p/tcp_stream.c @@ -503,49 +503,22 @@ tle_tcp_stream_open(struct tle_ctx *ctx, } /* - * Helper functions, used by close API. + * Helper function, used by close API. */ static inline int stream_close(struct tle_ctx *ctx, struct tle_tcp_stream *s) { - uint16_t uop; - uint32_t state; - static const struct tle_stream_cb zcb; - - /* check was close() already invoked */ - uop = s->tcb.uop; - if ((uop & TLE_TCP_OP_CLOSE) != 0) - return -EDEADLK; - - /* record that close() was already invoked */ - if (rte_atomic16_cmpset(&s->tcb.uop, uop, uop | TLE_TCP_OP_CLOSE) == 0) - return -EDEADLK; - - /* mark stream as unavaialbe for RX/TX. */ - tcp_stream_down(s); - - /* reset events/callbacks */ - s->rx.ev = NULL; - s->tx.ev = NULL; - s->err.ev = NULL; - - s->rx.cb = zcb; - s->tx.cb = zcb; - s->err.cb = zcb; - - state = s->tcb.state; + int32_t rc; - /* CLOSED, LISTEN, SYN_SENT - we can close the stream straighway */ - if (state <= TLE_TCP_ST_SYN_SENT) { - tcp_stream_reset(ctx, s); - return 0; - } + rc = stream_close_prolog(ctx, s, TLE_TCP_OP_CLOSE); + if (rc <= 0) + return rc; /* generate FIN and proceed with normal connection termination */ - if (state == TLE_TCP_ST_ESTABLISHED || state == TLE_TCP_ST_CLOSE_WAIT) { + if (rc == TLE_TCP_ST_ESTABLISHED || rc == TLE_TCP_ST_CLOSE_WAIT) { /* change state */ - s->tcb.state = (state == TLE_TCP_ST_ESTABLISHED) ? + s->tcb.state = (rc == TLE_TCP_ST_ESTABLISHED) ? TLE_TCP_ST_FIN_WAIT_1 : TLE_TCP_ST_LAST_ACK; /* mark stream as writable/readable again */ @@ -609,6 +582,52 @@ tle_tcp_stream_close(struct tle_stream *ts) return rc; } +int +tle_tcp_stream_abort(struct tle_stream *ts) +{ + int32_t rc; + struct tle_ctx *ctx; + struct tle_tcp_stream *s; + + s = TCP_STREAM(ts); + if (ts == NULL || s->s.type >= TLE_VNUM) + return -EINVAL; + + ctx = s->s.ctx; + rc = stream_close_prolog(ctx, s, TLE_TCP_OP_CLOSE_ABORT); + if (rc > 0) { + + /* + * RFC 793, On ABORT call, for states: + * SYN-RECEIVED STATE + * ESTABLISHED STATE + * FIN-WAIT-1 STATE + * FIN-WAIT-2 STATE + * CLOSE-WAIT STATE + * Send a reset segment: + * ...; all segments queued for transmission (except for the + * RST formed above) or retransmission should be flushed, + * delete the TCB, enter CLOSED state, and return. + */ + + if (rc >= TLE_TCP_ST_ESTABLISHED && rc <= TLE_TCP_ST_CLOSE_WAIT) + s->tcb.snd.close_flags |= TCP_FLAG_RST; + + /* + * set state to CLOSED, mark stream as writable/readable again + * and enqueue stream into to-send queue. + * That will cause later RST generation and stream termination. + */ + s->tcb.state = TLE_TCP_ST_CLOSED; + tcp_stream_up(s); + txs_enqueue(ctx, s); + rc = 0; + } + + tle_memtank_shrink(CTX_TCP_MTS(ctx)); + return rc; +} + int tle_tcp_stream_get_addr(const struct tle_stream *ts, struct tle_tcp_stream_addr *addr) diff --git a/lib/libtle_l4p/tcp_stream.h b/lib/libtle_l4p/tcp_stream.h index f04052d..9b22b38 100644 --- a/lib/libtle_l4p/tcp_stream.h +++ b/lib/libtle_l4p/tcp_stream.h @@ -67,6 +67,7 @@ struct tcb { uint8_t wscale; uint8_t nb_retx; /* number of retransmission */ uint8_t nb_retm; /**< max number of retx attempts. */ + uint8_t close_flags; /* tcp flags to send on close */ } snd; struct tle_tcp_syn_opts so; /* initial syn options. */ }; diff --git a/lib/libtle_l4p/tle_tcp.h b/lib/libtle_l4p/tle_tcp.h index df50a23..289683f 100644 --- a/lib/libtle_l4p/tle_tcp.h +++ b/lib/libtle_l4p/tle_tcp.h @@ -49,8 +49,11 @@ enum { TLE_TCP_OP_CONNECT = 0x4, TLE_TCP_OP_ESTABLISH = 0x8, TLE_TCP_OP_CLOSE = 0x10, + TLE_TCP_OP_ABORT = 0x20, }; +#define TLE_TCP_OP_CLOSE_ABORT (TLE_TCP_OP_CLOSE | TLE_TCP_OP_ABORT) + /** * termination/error events from remote peer */ @@ -154,7 +157,7 @@ tle_tcp_stream_open(struct tle_ctx *ctx, * - if stream contains unsent data, then actual close will be postponed * till either remaining data will be TX-ed, or timeout will expire. * All packets that belong to that stream and remain in the device - * TX queue will be kept for father transmission. + * TX queue will be kept for further transmission. * @param s * Pointer to the stream to close. * @return @@ -164,6 +167,25 @@ tle_tcp_stream_open(struct tle_ctx *ctx, */ int tle_tcp_stream_close(struct tle_stream *s); +/** + * abnormal stream termination. + * if the stream is in connected state, then: + * - abnormal connection termination would be performed. + * - if stream contains unread data, then it will be wiped out. + * - if stream contains unsent data, then it will be wiped out, + * without further attempt to TX it. + * All packets that belong to that stream and remain in the device + * TX queue will be kept for further transmission. + * @param s + * Pointer to the stream to close. + * @return + * zero on successful completion. + * - -EINVAL - invalid parameter passed to function + * - -EDEADLK - close was already invoked on that stream + */ +int tle_tcp_stream_abort(struct tle_stream *s); + + /** * close a group of open streams. * if the stream is in connected state, then: -- cgit 1.2.3-korg