summaryrefslogtreecommitdiffstats
path: root/src/libknot/xdp/tcp.h
blob: da0f1a4780623ab03b950a4eb2a3264e3850d4cd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
/*  Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

/*!
 * \file
 *
 * \brief TCP over XDP IO interface.
 *
 * \addtogroup xdp
 * @{
 */

#pragma once

#include "libknot/xdp/msg.h"
#include "libknot/xdp/xdp.h"

struct knot_sweep_stats;

typedef enum {
	XDP_TCP_NOOP      = 0,
	XDP_TCP_SYN       = 1,
	XDP_TCP_ESTABLISH = 2,
	XDP_TCP_CLOSE     = 3,
	XDP_TCP_RESET     = 4,
	XDP_TCP_RESEND    = 5,

	XDP_TCP_FREE      = 0x10,
} knot_tcp_action_t;

typedef enum {
	XDP_TCP_NORMAL,
	XDP_TCP_ESTABLISHING,
	XDP_TCP_CLOSING1, // FIN+ACK sent
	XDP_TCP_CLOSING2, // FIN+ACK received and sent
} knot_tcp_state_t;

typedef enum {
	XDP_TCP_FREE_NONE,
	XDP_TCP_FREE_DATA,
	XDP_TCP_FREE_PREFIX,
} knot_tcp_relay_free_t;

typedef enum {
	XDP_TCP_IGNORE_NONE        = 0,
	XDP_TCP_IGNORE_ESTABLISH   = (1 << 0),
	XDP_TCP_IGNORE_DATA_ACK    = (1 << 1),
	XDP_TCP_IGNORE_FIN         = (1 << 2),
} knot_tcp_ignore_t;

typedef struct knot_tcp_conn {
	struct {
		struct knot_tcp_conn *list_node_next;
		struct knot_tcp_conn *list_node_prev;
	} list_node_placeholder;
	struct sockaddr_in6 ip_rem;
	struct sockaddr_in6 ip_loc;
	uint8_t last_eth_rem[ETH_ALEN];
	uint8_t last_eth_loc[ETH_ALEN];
	uint16_t mss;
	uint8_t window_scale;
	uint32_t seqno;
	uint32_t ackno;
	uint32_t acked;
	uint32_t window_size;
	uint32_t last_active;
	uint32_t establish_rtt; // in microseconds
	knot_tcp_state_t state;
	struct iovec inbuf;
	struct knot_tcp_outbuf *outbufs;
	struct knot_tcp_conn *next;
} knot_tcp_conn_t;

typedef struct {
	size_t size;
	size_t usage;
	size_t inbufs_total;
	size_t outbufs_total;
	uint64_t hash_secret[2];
	knot_tcp_conn_t *next_close;
	knot_tcp_conn_t *next_ibuf;
	knot_tcp_conn_t *next_obuf;
	knot_tcp_conn_t *next_resend;
	knot_tcp_conn_t *conns[];
} knot_tcp_table_t;

typedef struct {
	const knot_xdp_msg_t *msg;
	knot_tcp_action_t action;
	knot_xdp_msg_flag_t auto_answer;
	uint32_t auto_seqno;
	knot_tcp_action_t answer;
	struct iovec *inbufs;
	size_t inbufs_count;
	knot_tcp_conn_t *conn;
} knot_tcp_relay_t;

/*!
 * \brief Return next TCP sequence number.
 */
inline static uint32_t knot_tcp_next_seqno(const knot_xdp_msg_t *msg)
{
	uint32_t res = msg->seqno + msg->payload.iov_len;
	if (msg->flags & (KNOT_XDP_MSG_SYN | KNOT_XDP_MSG_FIN)) {
		res++;
	}
	return res;
}

/*!
 * \brief Check if the relay is empty.
 */
inline static bool knot_tcp_relay_empty(const knot_tcp_relay_t *relay)
{
	return relay->action == XDP_TCP_NOOP && relay->answer == XDP_TCP_NOOP &&
	       relay->auto_answer == 0 && relay->inbufs_count == 0;
}

/*!
 * \brief Allocate TCP connection-handling hash table.
 *
 * \param size           Number of records for the hash table.
 * \param secret_share   Optional: share the hashing secret with another table.
 *
 * \note Hashing conflicts are solved by single-linked-lists in each record.
 *
 * \return The table, or NULL.
 */
knot_tcp_table_t *knot_tcp_table_new(size_t size, knot_tcp_table_t *secret_share);

/*!
 * \brief Free TCP connection hash table including all connection records.
 *
 * \note The freed connections are not closed nor reset.
 */
void knot_tcp_table_free(knot_tcp_table_t *table);

/*!
 * \brief Process received packets, prepare automatic responses (e.g. ACK), pick incoming data.
 *
 * \param relays      Out: relays to be filled with message/connection details.
 * \param msgs        Packets received by knot_xdp_recv().
 * \param msg_count   Number of received packets.
 * \param tcp_table   Table of TCP connections.
 * \param syn_table   Optional: extra table for handling partially established connections.
 * \param ignore      Ignore specific TCP packets indication.
 *
 * \return KNOT_E*
 */
int knot_tcp_recv(knot_tcp_relay_t *relays, knot_xdp_msg_t msgs[], uint32_t msg_count,
                  knot_tcp_table_t *tcp_table, knot_tcp_table_t *syn_table,
                  knot_tcp_ignore_t ignore);

/*!
 * \brief Prepare data (payload) to be sent as a response on specific relay.
 *
 * \param relay            Relay with active connection.
 * \param tcp_table        TCP table.
 * \param ignore_lastbyte  Evil mode: drop last byte of the payload.
 * \param data             Data payload, possibly > MSS and > window.
 * \param len              Payload length, < 64k.
 *
 * \return KNOT_E*
 */
int knot_tcp_reply_data(knot_tcp_relay_t *relay, knot_tcp_table_t *tcp_table,
                        bool ignore_lastbyte, uint8_t *data, uint32_t len);

/*!
 * \brief Send TCP packets.
 *
 * \param socket       XDP socket to send through.
 * \param relays       Connection changes and data.
 * \param relay_count  Number of connection changes and data.
 * \param max_at_once  Limit of packet batch sent by knot_xdp_send().
 *
 * \return KNOT_E*
 */
int knot_tcp_send(knot_xdp_socket_t *socket, knot_tcp_relay_t relays[],
                  uint32_t relay_count, uint32_t max_at_once);

/*!
 * \brief Cleanup old TCP connections, perform timeout checks.
 *
 * \param tcp_table        TCP connection table to clean up.
 * \param close_timeout    Gracefully close connections older than this (usecs).
 * \param reset_timeout    Reset connections older than this (usecs).
 * \param resend_timeout   Resend unAcked data older than this (usecs).
 * \param limit_conn_count Limit of active connections in TCP table, reset if more.
 * \param limit_ibuf_size  Limit of memory usage by input buffers, reset if exceeded.
 * \param limit_obuf_size  Limit of memory usage by output buffers, reset if exceeded.
 * \param relays           Out: relays to be filled with close/reset instructions for knot_tcp_send().
 * \param max_relays       Maximum relays to be used.
 * \param stats            Out: sweeped out connection statistics.
 *
 * \return KNOT_E*
 */
int knot_tcp_sweep(knot_tcp_table_t *tcp_table,
                   uint32_t close_timeout, uint32_t reset_timeout,
                   uint32_t resend_timeout, uint32_t limit_conn_count,
                   size_t limit_ibuf_size, size_t limit_obuf_size,
                   knot_tcp_relay_t *relays, uint32_t max_relays,
                   struct knot_sweep_stats *stats);

/*!
 * \brief Free resources of closed/reset connections.
 *
 * \param tcp_table    TCP table with connections.
 * \param relays       Relays with closed/reset (or other, ignored) connections.
 * \param relay_count  Number of relays.
 */
void knot_tcp_cleanup(knot_tcp_table_t *tcp_table, knot_tcp_relay_t relays[],
                      uint32_t relay_count);

/*! @} */