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_api.h"
31 #include "common/rb_tree.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 trbt_tree_t
*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
;
51 static const char *prog
;
53 /* TCP connection to be killed */
54 struct ctdb_killtcp_con
{
55 ctdb_sock_addr src_addr
;
56 ctdb_sock_addr dst_addr
;
57 struct ctdb_kill_tcp
*killtcp
;
60 /* this function is used to create a key to represent this socketpair
62 this key is used to insert and lookup matching socketpairs that are
65 #define KILLTCP_KEYLEN 10
66 static uint32_t *killtcp_key(ctdb_sock_addr
*src
, ctdb_sock_addr
*dst
)
68 static uint32_t key
[KILLTCP_KEYLEN
];
70 bzero(key
, sizeof(key
));
72 if (src
->sa
.sa_family
!= dst
->sa
.sa_family
) {
73 DEBUG(DEBUG_ERR
, (__location__
" ERROR, different families passed :%u vs %u\n", src
->sa
.sa_family
, dst
->sa
.sa_family
));
77 switch (src
->sa
.sa_family
) {
79 key
[0] = dst
->ip
.sin_addr
.s_addr
;
80 key
[1] = src
->ip
.sin_addr
.s_addr
;
81 key
[2] = dst
->ip
.sin_port
;
82 key
[3] = src
->ip
.sin_port
;
85 uint32_t *dst6_addr32
=
86 (uint32_t *)&(dst
->ip6
.sin6_addr
.s6_addr
);
87 uint32_t *src6_addr32
=
88 (uint32_t *)&(src
->ip6
.sin6_addr
.s6_addr
);
89 key
[0] = dst6_addr32
[3];
90 key
[1] = src6_addr32
[3];
91 key
[2] = dst6_addr32
[2];
92 key
[3] = src6_addr32
[2];
93 key
[4] = dst6_addr32
[1];
94 key
[5] = src6_addr32
[1];
95 key
[6] = dst6_addr32
[0];
96 key
[7] = src6_addr32
[0];
97 key
[8] = dst
->ip6
.sin6_port
;
98 key
[9] = src
->ip6
.sin6_port
;
102 DEBUG(DEBUG_ERR
, (__location__
" ERROR, unknown family passed :%u\n", src
->sa
.sa_family
));
110 called when we get a read event on the raw socket
112 static void capture_tcp_handler(struct tevent_context
*ev
,
113 struct tevent_fd
*fde
,
114 uint16_t flags
, void *private_data
)
116 struct ctdb_kill_tcp
*killtcp
= talloc_get_type(private_data
, struct ctdb_kill_tcp
);
117 struct ctdb_killtcp_con
*con
;
118 ctdb_sock_addr src
, dst
;
119 uint32_t ack_seq
, seq
;
123 if (ctdb_sys_read_tcp_packet(killtcp
->capture_fd
,
124 killtcp
->private_data
,
126 &ack_seq
, &seq
, &rst
, &window
) != 0) {
127 /* probably a non-tcp ACK packet */
131 if (window
== htons(1234) && (rst
|| seq
== 0)) {
132 /* Ignore packets that we sent! */
134 ("Ignoring packet with dst=%s:%d, src=%s:%d, seq=%"PRIu32
", ack_seq=%"PRIu32
", rst=%d, window=%"PRIu16
"\n",
135 ctdb_sock_addr_to_string(killtcp
, &dst
),
136 ntohs(dst
.ip
.sin_port
),
137 ctdb_sock_addr_to_string(killtcp
, &src
),
138 ntohs(src
.ip
.sin_port
),
139 seq
, ack_seq
, rst
, ntohs(window
)));
143 /* check if we have this guy in our list of connections
146 con
= trbt_lookuparray32(killtcp
->connections
,
147 KILLTCP_KEYLEN
, killtcp_key(&src
, &dst
));
149 /* no this was some other packet we can just ignore */
153 /* This connection has been tickled! RST it and remove it
156 ("Sending a TCP RST to kill connection (%s:%d) -> %s:%d\n",
157 ctdb_sock_addr_to_string(con
, &con
->src_addr
),
158 ntohs(con
->src_addr
.ip
.sin_port
),
159 ctdb_sock_addr_to_string(con
, &con
->dst_addr
),
160 ntohs(con
->dst_addr
.ip
.sin_port
)));
162 ctdb_sys_send_tcp(&con
->dst_addr
, &con
->src_addr
, ack_seq
, seq
, 1);
167 /* when traversing the list of all tcp connections to send tickle acks to
168 (so that we can capture the ack coming back and kill the connection
170 this callback is called for each connection we are currently trying to kill
172 static int tickle_connection_traverse(void *param
, void *data
)
174 struct ctdb_killtcp_con
*con
= talloc_get_type(data
, struct ctdb_killtcp_con
);
176 con
->killtcp
->batch_count
++;
177 if (con
->killtcp
->batch_count
> con
->killtcp
->batch_size
) {
178 /* Terminate the traverse */
182 ctdb_sys_send_tcp(&con
->dst_addr
, &con
->src_addr
, 0, 0, 0);
189 called every second until all sentenced connections have been reset
191 static void ctdb_tickle_sentenced_connections(struct tevent_context
*ev
,
192 struct tevent_timer
*te
,
193 struct timeval t
, void *private_data
)
195 struct ctdb_kill_tcp
*killtcp
= talloc_get_type(private_data
, struct ctdb_kill_tcp
);
196 void *delete_cons
= talloc_new(NULL
);
198 /* loop over up to batch_size connections sending tickle ACKs */
199 killtcp
->batch_count
= 0;
200 trbt_traversearray32(killtcp
->connections
, KILLTCP_KEYLEN
, tickle_connection_traverse
, delete_cons
);
202 /* now we've finished traverse, it's safe to do deletion. */
203 talloc_free(delete_cons
);
207 /* If there are no more connections to kill or we have tried
208 too many times we can remove the entire killtcp structure
210 if (killtcp
->connections
== NULL
||
211 killtcp
->connections
->root
== NULL
||
212 killtcp
->attempts
>= killtcp
->max_attempts
) {
213 talloc_free(killtcp
);
217 /* try tickling them again in a seconds time
219 tevent_add_timer(ev
, killtcp
,
220 tevent_timeval_current_ofs(
221 killtcp
->retry_interval
.tv_sec
,
222 killtcp
->retry_interval
.tv_usec
),
223 ctdb_tickle_sentenced_connections
, killtcp
);
226 /* nothing fancy here, just unconditionally replace any existing
227 connection structure with the new one.
229 don't even free the old one if it did exist, that one is talloc_stolen
230 by the same node in the tree anyway and will be deleted when the new data
233 static void *add_killtcp_callback(void *parm
, void *data
)
238 /* Add a TCP socket to the list of connections we want to RST. The
239 * list is attached to *killtcp_arg. If this is NULL then allocate
241 static int ctdb_killtcp(struct tevent_context
*ev
,
244 const ctdb_sock_addr
*src
,
245 const ctdb_sock_addr
*dst
,
246 struct ctdb_kill_tcp
**killtcp_arg
)
248 struct ctdb_kill_tcp
*killtcp
;
249 struct ctdb_killtcp_con
*con
;
251 if (killtcp_arg
== NULL
) {
252 DEBUG(DEBUG_ERR
, (__location__
" killtcp_arg is NULL!\n"));
256 killtcp
= *killtcp_arg
;
258 /* Allocate a new structure if necessary. The structure is
259 * only freed when mem_ctx is freed. */
260 if (killtcp
== NULL
) {
261 killtcp
= talloc_zero(mem_ctx
, struct ctdb_kill_tcp
);
262 if (killtcp
== NULL
) {
263 DEBUG(DEBUG_ERR
, (__location__
" out of memory\n"));
267 killtcp
->capture_fd
= -1;
268 killtcp
->connections
= trbt_create(killtcp
, 0);
270 killtcp
->attempts
= 0;
271 killtcp
->max_attempts
= 50;
273 killtcp
->retry_interval
.tv_sec
= 0;
274 killtcp
->retry_interval
.tv_usec
= 100 * 1000;
276 killtcp
->batch_count
= 0;
277 killtcp
->batch_size
= 300;
279 *killtcp_arg
= killtcp
;
282 /* create a structure that describes this connection we want to
283 RST and store it in killtcp->connections
285 con
= talloc(killtcp
, struct ctdb_killtcp_con
);
287 DEBUG(DEBUG_ERR
, (__location__
" out of memory\n"));
290 con
->src_addr
= *src
;
291 con
->dst_addr
= *dst
;
292 con
->killtcp
= killtcp
;
295 trbt_insertarray32_callback(killtcp
->connections
,
297 killtcp_key(&con
->dst_addr
,
299 add_killtcp_callback
, con
);
302 If we don't have a socket to listen on yet we must create it
304 if (killtcp
->capture_fd
== -1) {
305 killtcp
->capture_fd
=
306 ctdb_sys_open_capture_socket(iface
,
307 &killtcp
->private_data
);
308 if (killtcp
->capture_fd
== -1) {
309 DEBUG(DEBUG_CRIT
,(__location__
" Failed to open capturing "
310 "socket on iface '%s' for killtcp (%s)\n",
311 iface
, strerror(errno
)));
317 if (killtcp
->fde
== NULL
) {
318 killtcp
->fde
= tevent_add_fd(ev
, killtcp
,
321 capture_tcp_handler
, killtcp
);
322 tevent_fd_set_auto_close(killtcp
->fde
);
328 static int ctdb_killtcp_destructor(struct ctdb_kill_tcp
*killtcp
)
330 bool *done
= killtcp
->destructor_data
;
336 static void usage(void)
338 printf("usage: %s <interface> [ <srcip:port> <dstip:port> ]\n", prog
);
342 int main(int argc
, char **argv
)
344 struct ctdb_connection conn
;
345 struct ctdb_kill_tcp
*killtcp
= NULL
;
346 struct tevent_context
*ev
= NULL
;
347 struct TALLOC_CONTEXT
*mem_ctx
= NULL
;
348 struct ctdb_connection
*conns
= NULL
;
355 /* Set the debug level */
356 t
= getenv("CTDB_DEBUGLEVEL");
358 if (debug_level_parse(t
, &debug_level
)) {
359 DEBUGLEVEL
= debug_level
;
361 DEBUGLEVEL
= DEBUG_ERR
;
367 if (argc
!= 2 && argc
!= 4) {
372 if (!parse_ip_port(argv
[2], &conn
.src
)) {
373 DEBUG(DEBUG_ERR
, ("Bad IP:port '%s'\n", argv
[2]));
377 if (!parse_ip_port(argv
[3], &conn
.dst
)) {
378 DEBUG(DEBUG_ERR
, ("Bad IP:port '%s'\n", argv
[3]));
385 ret
= ctdb_parse_connections(stdin
, mem_ctx
, &num
, &conns
);
388 ("Unable to parse connections [%s]\n",
394 mem_ctx
= talloc_new(NULL
);
395 if (mem_ctx
== NULL
) {
396 DEBUG(DEBUG_ERR
, (__location__
" out of memory\n"));
400 ev
= tevent_context_init(mem_ctx
);
402 DEBUG(DEBUG_ERR
, ("Failed to initialise tevent\n"));
407 /* No connections, done! */
408 talloc_free(mem_ctx
);
412 for (i
= 0; i
< num
; i
++) {
413 ret
= ctdb_killtcp(ev
, mem_ctx
, argv
[1],
414 &conns
[i
].src
, &conns
[i
].dst
,
417 DEBUG(DEBUG_ERR
, ("Unable to killtcp\n"));
423 killtcp
->destructor_data
= &done
;
424 talloc_set_destructor(killtcp
, ctdb_killtcp_destructor
);
426 /* Do the initial processing of connections */
427 tevent_add_timer(ev
, killtcp
,
428 tevent_timeval_current_ofs(0, 0),
429 ctdb_tickle_sentenced_connections
, killtcp
);
432 tevent_loop_once(ev
);
435 talloc_free(mem_ctx
);
440 TALLOC_FREE(mem_ctx
);