diff options
Diffstat (limited to 'lib/libtle_l4p')
-rw-r--r-- | lib/libtle_l4p/tcp_ctl.h | 55 | ||||
-rw-r--r-- | lib/libtle_l4p/tcp_rxtx.c | 4 | ||||
-rw-r--r-- | lib/libtle_l4p/tcp_stream.c | 87 | ||||
-rw-r--r-- | lib/libtle_l4p/tcp_stream.h | 1 | ||||
-rw-r--r-- | 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 */ @@ -610,6 +583,52 @@ tle_tcp_stream_close(struct tle_stream *ts) } 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: <SEQ=SND.NXT><CTL=RST> + * ...; 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 @@ -165,6 +168,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: * - connection termination would be performed. |