2 CTDB TCP connection killing utility
4 Copyright (C) Martin Schwenke <martin@meltin.net> 2016
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, see <http://www.gnu.org/licenses/>.
24 #include "system/network.h"
26 #include "lib/util/debug.h"
28 #include "protocol/protocol.h"
29 #include "protocol/protocol_util.h"
31 #include "common/db_hash.h"
32 #include "common/system.h"
33 #include "common/logging.h"
36 /* Contains the listening socket and the list of TCP connections to
38 struct ctdb_kill_tcp {
40 struct tevent_fd *fde;
41 struct db_hash_context *connections;
43 void *destructor_data;
44 unsigned int attempts;
45 unsigned int max_attempts;
46 struct timeval retry_interval;
47 unsigned int batch_count;
48 unsigned int batch_size;
53 called when we get a read event on the raw socket
55 static void capture_tcp_handler(struct tevent_context *ev,
56 struct tevent_fd *fde,
57 uint16_t flags, void *private_data)
59 struct ctdb_kill_tcp *killtcp = talloc_get_type(private_data, struct ctdb_kill_tcp);
60 /* 0 the parts that don't get set by ctdb_sys_read_tcp_packet */
61 struct ctdb_connection conn;
62 uint32_t ack_seq, seq;
67 ret = ctdb_sys_read_tcp_packet(killtcp->capture_fd,
68 killtcp->private_data,
69 &conn.server, &conn.client,
70 &ack_seq, &seq, &rst, &window);
72 /* probably a non-tcp ACK packet */
76 if (window == htons(1234) && (rst || seq == 0)) {
77 /* Ignore packets that we sent! */
78 D_DEBUG("Ignoring packet: %s, "
79 "seq=%"PRIu32", ack_seq=%"PRIu32", "
80 "rst=%d, window=%"PRIu16"\n",
81 ctdb_connection_to_string(killtcp, &conn, false),
82 seq, ack_seq, rst, ntohs(window));
86 /* Check if this connection is one being reset, if found then delete */
87 ret = db_hash_delete(killtcp->connections,
88 (uint8_t*)&conn, sizeof(conn));
90 /* Packet for some other connection, ignore */
94 DBG_WARNING("Internal error (%s)\n", strerror(ret));
98 D_INFO("Sending a TCP RST to kill connection %s\n",
99 ctdb_connection_to_string(killtcp, &conn, true));
101 ret = ctdb_sys_send_tcp(&conn.server, &conn.client, ack_seq, seq, 1);
103 DBG_ERR("Error sending TCP RST for connection\n");
108 static int tickle_connection_parser(uint8_t *keybuf, size_t keylen,
109 uint8_t *databuf, size_t datalen,
112 struct ctdb_kill_tcp *killtcp = talloc_get_type_abort(
113 private_data, struct ctdb_kill_tcp);
114 struct ctdb_connection *conn;
117 if (keylen != sizeof(*conn)) {
118 DBG_WARNING("Unexpected data in connection hash\n");
122 conn = (struct ctdb_connection *)keybuf;
124 killtcp->batch_count++;
125 if (killtcp->batch_count > killtcp->batch_size) {
126 /* Terminate the traverse */
130 ret = ctdb_sys_send_tcp(&conn->server, &conn->client, 0, 0, 0);
132 DBG_ERR("Error sending tickle ACK\n");
140 * Called periodically until all sentenced connections have been reset
141 * or enough attempts have been made
143 static void ctdb_tickle_sentenced_connections(struct tevent_context *ev,
144 struct tevent_timer *te,
145 struct timeval t, void *private_data)
147 struct ctdb_kill_tcp *killtcp = talloc_get_type(private_data, struct ctdb_kill_tcp);
150 /* loop over up to batch_size connections sending tickle ACKs */
151 killtcp->batch_count = 0;
152 ret = db_hash_traverse(killtcp->connections,
153 tickle_connection_parser, killtcp, NULL);
155 DBG_WARNING("Unexpected error traversing connections (%s)\n",
162 * If there are no more connections to kill or we have tried
163 * too many times we can remove the entire killtcp structure
165 ret = db_hash_traverse(killtcp->connections, NULL, NULL, &count);
167 /* What now? Try again until max_attempts reached */
168 DBG_WARNING("Unexpected error traversing connections (%s)\n",
173 killtcp->attempts >= killtcp->max_attempts) {
174 talloc_free(killtcp);
178 /* try tickling them again in a seconds time
180 tevent_add_timer(ev, killtcp,
181 tevent_timeval_current_ofs(
182 killtcp->retry_interval.tv_sec,
183 killtcp->retry_interval.tv_usec),
184 ctdb_tickle_sentenced_connections, killtcp);
187 /* Add a TCP socket to the list of connections we want to RST. The
188 * list is attached to *killtcp_arg. If this is NULL then allocate
190 static int ctdb_killtcp(struct tevent_context *ev,
193 struct ctdb_connection *conn,
194 struct ctdb_kill_tcp **killtcp_arg)
196 struct ctdb_kill_tcp *killtcp;
199 if (killtcp_arg == NULL) {
200 DEBUG(DEBUG_ERR, (__location__ " killtcp_arg is NULL!\n"));
204 killtcp = *killtcp_arg;
206 /* Allocate a new structure if necessary. The structure is
207 * only freed when mem_ctx is freed. */
208 if (killtcp == NULL) {
209 killtcp = talloc_zero(mem_ctx, struct ctdb_kill_tcp);
210 if (killtcp == NULL) {
211 DEBUG(DEBUG_ERR, (__location__ " out of memory\n"));
215 killtcp->capture_fd = -1;
216 ret = db_hash_init(killtcp, "connections", 2048, DB_HASH_SIMPLE,
217 &killtcp->connections);
219 D_ERR("Failed to initialise connection hash (%s)\n",
221 talloc_free(killtcp);
225 killtcp->attempts = 0;
226 killtcp->max_attempts = 50;
228 killtcp->retry_interval.tv_sec = 0;
229 killtcp->retry_interval.tv_usec = 100 * 1000;
231 killtcp->batch_count = 0;
232 killtcp->batch_size = 300;
234 *killtcp_arg = killtcp;
237 /* Connection is stored as a key in the connections hash */
238 ret = db_hash_add(killtcp->connections,
239 (uint8_t *)conn, sizeof(*conn),
242 D_ERR("Error adding connection to hash (%s)\n", strerror(ret));
247 If we don't have a socket to listen on yet we must create it
249 if (killtcp->capture_fd == -1) {
250 killtcp->capture_fd =
251 ctdb_sys_open_capture_socket(iface,
252 &killtcp->private_data);
253 if (killtcp->capture_fd == -1) {
254 DEBUG(DEBUG_CRIT,(__location__ " Failed to open capturing "
255 "socket on iface '%s' for killtcp (%s)\n",
256 iface, strerror(errno)));
262 if (killtcp->fde == NULL) {
263 killtcp->fde = tevent_add_fd(ev, killtcp,
266 capture_tcp_handler, killtcp);
267 tevent_fd_set_auto_close(killtcp->fde);
273 static int ctdb_killtcp_destructor(struct ctdb_kill_tcp *killtcp)
275 bool *done = killtcp->destructor_data;
281 static void usage(const char *prog)
283 printf("usage: %s <interface> [ <srcip:port> <dstip:port> ]\n", prog);
287 int main(int argc, char **argv)
289 struct ctdb_connection conn;
290 struct ctdb_kill_tcp *killtcp = NULL;
291 struct tevent_context *ev = NULL;
292 struct TALLOC_CONTEXT *mem_ctx = NULL;
293 struct ctdb_connection_list *conn_list = NULL;
299 /* Set the debug level */
300 t = getenv("CTDB_DEBUGLEVEL");
302 if (debug_level_parse(t, &debug_level)) {
303 DEBUGLEVEL = debug_level;
305 DEBUGLEVEL = DEBUG_ERR;
309 if (argc != 2 && argc != 4) {
314 ret = ctdb_sock_addr_from_string(argv[2], &conn.client, true);
316 D_ERR("Bad IP:port '%s'\n", argv[2]);
320 ret = ctdb_sock_addr_from_string(argv[3], &conn.server, true);
322 D_ERR("Bad IP:port '%s'\n", argv[3]);
327 conn_list = talloc_zero(mem_ctx, struct ctdb_connection_list);
328 if (conn_list == NULL) {
330 DBG_ERR("Internal error (%s)\n", strerror(ret));
333 ret = ctdb_connection_list_add(conn_list, &conn);
335 DBG_ERR("Internal error (%s)\n", strerror(ret));
339 ret = ctdb_connection_list_read(mem_ctx, true, &conn_list);
341 D_ERR("Unable to parse connections (%s)\n",
347 mem_ctx = talloc_new(NULL);
348 if (mem_ctx == NULL) {
349 DEBUG(DEBUG_ERR, (__location__ " out of memory\n"));
353 ev = tevent_context_init(mem_ctx);
355 DEBUG(DEBUG_ERR, ("Failed to initialise tevent\n"));
359 if (conn_list->num == 0) {
360 /* No connections, done! */
361 talloc_free(mem_ctx);
365 for (i = 0; i < conn_list->num; i++) {
366 ret = ctdb_killtcp(ev, mem_ctx, argv[1],
367 &conn_list->conn[i], &killtcp);
369 DEBUG(DEBUG_ERR, ("Unable to killtcp\n"));
375 killtcp->destructor_data = &done;
376 talloc_set_destructor(killtcp, ctdb_killtcp_destructor);
378 /* Do the initial processing of connections */
379 tevent_add_timer(ev, killtcp,
380 tevent_timeval_current_ofs(0, 0),
381 ctdb_tickle_sentenced_connections, killtcp);
384 tevent_loop_once(ev);
387 talloc_free(mem_ctx);
392 TALLOC_FREE(mem_ctx);