libgpo: add gp_inifile_init_context_direct()
[Samba.git] / ctdb / tools / ctdb_killtcp.c
blob5db7ec7853bb0c15625619b36133fb37883d8ec0
1 /*
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/>.
20 #include <talloc.h>
21 #include <tevent.h>
23 #include "replace.h"
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
37 * kill */
38 struct ctdb_kill_tcp {
39 int capture_fd;
40 struct tevent_fd *fde;
41 trbt_tree_t *connections;
42 void *private_data;
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
61 in the killtcp tree.
62 this key is used to insert and lookup matching socketpairs that are
63 to be tickled and RST
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));
74 return key;
77 switch (src->sa.sa_family) {
78 case AF_INET:
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;
83 break;
84 case AF_INET6: {
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;
99 break;
101 default:
102 DEBUG(DEBUG_ERR, (__location__ " ERROR, unknown family passed :%u\n", src->sa.sa_family));
103 return key;
106 return key;
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;
120 int rst;
121 uint16_t window;
123 if (ctdb_sys_read_tcp_packet(killtcp->capture_fd,
124 killtcp->private_data,
125 &src, &dst,
126 &ack_seq, &seq, &rst, &window) != 0) {
127 /* probably a non-tcp ACK packet */
128 return;
131 if (window == htons(1234) && (rst || seq == 0)) {
132 /* Ignore packets that we sent! */
133 DEBUG(DEBUG_DEBUG,
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)));
140 return;
143 /* check if we have this guy in our list of connections
144 to kill
146 con = trbt_lookuparray32(killtcp->connections,
147 KILLTCP_KEYLEN, killtcp_key(&src, &dst));
148 if (con == NULL) {
149 /* no this was some other packet we can just ignore */
150 return;
153 /* This connection has been tickled! RST it and remove it
154 * from the list. */
155 DEBUG(DEBUG_INFO,
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);
163 talloc_free(con);
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
169 by a RST)
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 */
179 return -1;
182 ctdb_sys_send_tcp(&con->dst_addr, &con->src_addr, 0, 0, 0);
184 return 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);
205 killtcp->attempts++;
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);
214 return;
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
231 is deleted
233 static void *add_killtcp_callback(void *parm, void *data)
235 return parm;
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
240 * the structure. */
241 static int ctdb_killtcp(struct tevent_context *ev,
242 TALLOC_CTX *mem_ctx,
243 const char *iface,
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"));
253 return -1;
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"));
264 return -1;
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);
286 if (con == NULL) {
287 DEBUG(DEBUG_ERR, (__location__ " out of memory\n"));
288 return -1;
290 con->src_addr = *src;
291 con->dst_addr = *dst;
292 con->killtcp = killtcp;
295 trbt_insertarray32_callback(killtcp->connections,
296 KILLTCP_KEYLEN,
297 killtcp_key(&con->dst_addr,
298 &con->src_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)));
312 return -1;
317 if (killtcp->fde == NULL) {
318 killtcp->fde = tevent_add_fd(ev, killtcp,
319 killtcp->capture_fd,
320 TEVENT_FD_READ,
321 capture_tcp_handler, killtcp);
322 tevent_fd_set_auto_close(killtcp->fde);
325 return 0;
328 static int ctdb_killtcp_destructor(struct ctdb_kill_tcp *killtcp)
330 bool *done = killtcp->destructor_data;
331 *done = true;
333 return 0;
336 static void usage(void)
338 printf("usage: %s <interface> [ <srcip:port> <dstip:port> ]\n", prog);
339 exit(1);
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;
349 const char *t;
350 int debug_level;
351 bool done;
352 int num = 0;
353 int i, ret;
355 /* Set the debug level */
356 t = getenv("CTDB_DEBUGLEVEL");
357 if (t != NULL) {
358 if (debug_level_parse(t, &debug_level)) {
359 DEBUGLEVEL = debug_level;
360 } else {
361 DEBUGLEVEL = DEBUG_ERR;
365 prog = argv[0];
367 if (argc != 2 && argc != 4) {
368 usage();
371 if (argc == 4) {
372 if (!parse_ip_port(argv[2], &conn.src)) {
373 DEBUG(DEBUG_ERR, ("Bad IP:port '%s'\n", argv[2]));
374 goto fail;
377 if (!parse_ip_port(argv[3], &conn.dst)) {
378 DEBUG(DEBUG_ERR, ("Bad IP:port '%s'\n", argv[3]));
379 goto fail;
382 conns = &conn;
383 num = 1;
384 } else {
385 ret = ctdb_parse_connections(stdin, mem_ctx, &num, &conns);
386 if (ret != 0) {
387 DEBUG(DEBUG_ERR,
388 ("Unable to parse connections [%s]\n",
389 strerror(ret)));
390 goto fail;
394 mem_ctx = talloc_new(NULL);
395 if (mem_ctx == NULL) {
396 DEBUG(DEBUG_ERR, (__location__ " out of memory\n"));
397 goto fail;
400 ev = tevent_context_init(mem_ctx);
401 if (ev == NULL) {
402 DEBUG(DEBUG_ERR, ("Failed to initialise tevent\n"));
403 goto fail;
406 if (num == 0) {
407 /* No connections, done! */
408 talloc_free(mem_ctx);
409 return 0;
412 for (i = 0; i < num; i++) {
413 ret = ctdb_killtcp(ev, mem_ctx, argv[1],
414 &conns[i].src, &conns[i].dst,
415 &killtcp);
416 if (ret != 0) {
417 DEBUG(DEBUG_ERR, ("Unable to killtcp\n"));
418 goto fail;
422 done = false;
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);
431 while (!done) {
432 tevent_loop_once(ev);
435 talloc_free(mem_ctx);
437 return 0;
439 fail:
440 TALLOC_FREE(mem_ctx);
441 return -1;