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/>.
21 #include "system/network.h"
26 #include "lib/util/debug.h"
27 #include "lib/util/tevent_unix.h"
29 #include "protocol/protocol.h"
30 #include "protocol/protocol_util.h"
32 #include "common/db_hash.h"
33 #include "common/system_socket.h"
34 #include "common/logging.h"
37 struct reset_connections_state
{
38 struct tevent_context
*ev
;
40 struct tevent_fd
*fde
;
41 struct db_hash_context
*connections
;
43 unsigned int attempts
;
44 unsigned int max_attempts
;
45 struct timeval retry_interval
;
46 unsigned int batch_count
;
47 unsigned int batch_size
;
51 static void reset_connections_capture_tcp_handler(struct tevent_context
*ev
,
52 struct tevent_fd
*fde
,
55 static void reset_connections_batch(struct tevent_req
*subreq
);
56 static int reset_connections_tickle_connection(
57 uint8_t *keybuf
, size_t keylen
,
58 uint8_t *databuf
, size_t datalen
,
61 static struct tevent_req
*reset_connections_send(
63 struct tevent_context
*ev
,
65 struct ctdb_connection_list
*conn_list
)
67 struct tevent_req
*req
, *subreq
;
68 struct reset_connections_state
*state
;
72 req
= tevent_req_create(mem_ctx
, &state
,
73 struct reset_connections_state
);
80 if (conn_list
->num
== 0) {
81 /* No connections, done! */
83 return tevent_req_post(req
, ev
);
86 ret
= db_hash_init(state
, "connections", 2048, DB_HASH_SIMPLE
,
89 D_ERR("Failed to initialise connection hash (%s)\n",
91 tevent_req_error(req
, ret
);
92 return tevent_req_post(req
, ev
);
95 DBG_DEBUG("Adding %u connections to hash\n", conn_list
->num
);
96 for (i
= 0; i
< conn_list
->num
; i
++) {
97 struct ctdb_connection
*c
= &conn_list
->conn
[i
];
99 DBG_DEBUG("Adding connection to hash: %s\n",
100 ctdb_connection_to_string(conn_list
, c
, true));
102 /* Connection is stored as a key in the connections hash */
103 ret
= db_hash_add(state
->connections
,
104 (uint8_t *)discard_const(c
), sizeof(*c
),
107 D_ERR("Error adding connection to hash (%s)\n",
109 tevent_req_error(req
, ret
);
110 return tevent_req_post(req
, ev
);
115 state
->max_attempts
= 50;
117 state
->retry_interval
.tv_sec
= 0;
118 state
->retry_interval
.tv_usec
= 100 * 1000;
120 state
->batch_count
= 0;
121 state
->batch_size
= 300;
124 ctdb_sys_open_capture_socket(iface
, &state
->private_data
);
125 if (state
->capture_fd
== -1) {
126 D_ERR("Failed to open capture socket on iface '%s' (%s)\n",
127 iface
, strerror(errno
));
128 tevent_req_error(req
, EIO
);
129 return tevent_req_post(req
, ev
);
132 state
->fde
= tevent_add_fd(ev
, state
, state
->capture_fd
,
134 reset_connections_capture_tcp_handler
,
136 if (tevent_req_nomem(state
->fde
, req
)) {
137 return tevent_req_post(req
, ev
);
139 tevent_fd_set_auto_close(state
->fde
);
141 subreq
= tevent_wakeup_send(state
, ev
, tevent_timeval_current_ofs(0,0));
142 if (tevent_req_nomem(subreq
, req
)) {
143 return tevent_req_post(req
, ev
);
145 tevent_req_set_callback(subreq
, reset_connections_batch
, req
);
151 called when we get a read event on the raw socket
153 static void reset_connections_capture_tcp_handler(struct tevent_context
*ev
,
154 struct tevent_fd
*fde
,
158 struct reset_connections_state
*state
= talloc_get_type_abort(
159 private_data
, struct reset_connections_state
);
160 /* 0 the parts that don't get set by ctdb_sys_read_tcp_packet */
161 struct ctdb_connection conn
;
162 uint32_t ack_seq
, seq
;
167 ret
= ctdb_sys_read_tcp_packet(state
->capture_fd
,
169 &conn
.server
, &conn
.client
,
170 &ack_seq
, &seq
, &rst
, &window
);
172 /* Not a TCP-ACK? Unexpected protocol? */
173 DBG_DEBUG("Failed to parse packet, errno=%d\n", ret
);
177 if (window
== htons(1234) && (rst
|| seq
== 0)) {
178 /* Ignore packets that we sent! */
179 DBG_DEBUG("Ignoring sent packet: %s, "
180 "seq=%"PRIu32
", ack_seq=%"PRIu32
", "
181 "rst=%d, window=%"PRIu16
"\n",
182 ctdb_connection_to_string(state
, &conn
, false),
183 seq
, ack_seq
, rst
, ntohs(window
));
187 /* Check if this connection is one being reset, if found then delete */
188 ret
= db_hash_delete(state
->connections
,
189 (uint8_t*)&conn
, sizeof(conn
));
191 /* Packet for some other connection, ignore */
192 DBG_DEBUG("Ignoring packet for unknown connection: %s\n",
193 ctdb_connection_to_string(state
, &conn
, true));
197 DBG_WARNING("Internal error (%s)\n", strerror(ret
));
201 D_INFO("Sending a TCP RST to for connection %s\n",
202 ctdb_connection_to_string(state
, &conn
, true));
204 ret
= ctdb_sys_send_tcp(&conn
.server
, &conn
.client
, ack_seq
, seq
, 1);
206 DBG_ERR("Error sending TCP RST for connection\n");
211 * Called periodically until all sentenced connections have been reset
212 * or enough attempts have been made
214 static void reset_connections_batch(struct tevent_req
*subreq
)
216 struct tevent_req
*req
= tevent_req_callback_data(
217 subreq
, struct tevent_req
);
218 struct reset_connections_state
*state
= tevent_req_data(
219 req
, struct reset_connections_state
);
223 status
= tevent_wakeup_recv(subreq
);
227 DBG_WARNING("Unexpected error on timer expiry\n");
231 /* loop over up to batch_size connections sending tickle ACKs */
232 state
->batch_count
= 0;
233 ret
= db_hash_traverse(state
->connections
,
234 reset_connections_tickle_connection
,
237 DBG_WARNING("Unexpected error traversing connections (%s)\n",
244 * If there are no more connections to kill or we have tried
245 * too many times we're finished
247 ret
= db_hash_traverse(state
->connections
, NULL
, NULL
, &count
);
249 /* What now? Try again until max_attempts reached */
250 DBG_WARNING("Unexpected error traversing connections (%s)\n",
255 state
->attempts
>= state
->max_attempts
) {
256 tevent_req_done(req
);
260 /* Schedule next attempt */
261 subreq
= tevent_wakeup_send(state
, state
->ev
,
262 tevent_timeval_current_ofs(
263 state
->retry_interval
.tv_sec
,
264 state
->retry_interval
.tv_usec
));
265 if (tevent_req_nomem(subreq
, req
)) {
268 tevent_req_set_callback(subreq
, reset_connections_batch
, req
);
271 static int reset_connections_tickle_connection(
272 uint8_t *keybuf
, size_t keylen
,
273 uint8_t *databuf
, size_t datalen
,
276 struct reset_connections_state
*state
= talloc_get_type_abort(
277 private_data
, struct reset_connections_state
);
278 struct ctdb_connection
*conn
;
281 if (keylen
!= sizeof(*conn
)) {
282 DBG_WARNING("Unexpected data in connection hash\n");
286 conn
= (struct ctdb_connection
*)keybuf
;
288 state
->batch_count
++;
289 if (state
->batch_count
> state
->batch_size
) {
290 /* Terminate the traverse */
294 DBG_DEBUG("Sending tickle ACK for connection '%s'\n",
295 ctdb_connection_to_string(state
, conn
, true));
296 ret
= ctdb_sys_send_tcp(&conn
->server
, &conn
->client
, 0, 0, 0);
298 DBG_ERR("Error sending tickle ACK\n");
305 static bool reset_connections_recv(struct tevent_req
*req
, int *perr
)
309 if (tevent_req_is_unix_error(req
, &err
)) {
319 static void usage(const char *prog
)
321 printf("usage: %s <interface> [ <srcip:port> <dstip:port> ]\n", prog
);
325 int main(int argc
, char **argv
)
327 struct ctdb_connection conn
;
328 struct tevent_context
*ev
= NULL
;
329 TALLOC_CTX
*mem_ctx
= NULL
;
330 struct ctdb_connection_list
*conn_list
= NULL
;
332 struct tevent_req
*req
;
338 /* Set the debug level */
339 t
= getenv("CTDB_DEBUGLEVEL");
341 ok
= debug_level_parse(t
, &debug_level
);
343 debug_level
= DEBUG_ERR
;
345 debuglevel_set(debug_level
);
348 if (argc
!= 2 && argc
!= 4) {
353 ret
= ctdb_sock_addr_from_string(argv
[2], &conn
.client
, true);
355 D_ERR("Bad IP:port '%s'\n", argv
[2]);
359 ret
= ctdb_sock_addr_from_string(argv
[3], &conn
.server
, true);
361 D_ERR("Bad IP:port '%s'\n", argv
[3]);
366 conn_list
= talloc_zero(mem_ctx
, struct ctdb_connection_list
);
367 if (conn_list
== NULL
) {
369 DBG_ERR("Internal error (%s)\n", strerror(ret
));
372 ret
= ctdb_connection_list_add(conn_list
, &conn
);
374 DBG_ERR("Internal error (%s)\n", strerror(ret
));
378 ret
= ctdb_connection_list_read(mem_ctx
, 0, true, &conn_list
);
380 D_ERR("Unable to parse connections (%s)\n",
386 mem_ctx
= talloc_new(NULL
);
387 if (mem_ctx
== NULL
) {
388 DEBUG(DEBUG_ERR
, (__location__
" out of memory\n"));
392 ev
= tevent_context_init(mem_ctx
);
394 DEBUG(DEBUG_ERR
, ("Failed to initialise tevent\n"));
398 req
= reset_connections_send(mem_ctx
, ev
, argv
[1], conn_list
);
403 tevent_req_poll(req
, ev
);
405 status
= reset_connections_recv(req
, &ret
);
407 D_ERR("Failed to kill connections (%s)\n", strerror(ret
));
411 talloc_free(mem_ctx
);
416 TALLOC_FREE(mem_ctx
);