ctdb-tool: Add helper for talking to event daemon
[samba.git] / ctdb / tools / ctdb_killtcp.c
1 /*
2    CTDB TCP connection killing utility
3
4    Copyright (C) Martin Schwenke <martin@meltin.net> 2016
5
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.
10
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.
15
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/>.
18 */
19
20 #include <talloc.h>
21 #include <tevent.h>
22
23 #include "replace.h"
24 #include "system/network.h"
25
26 #include "lib/util/debug.h"
27
28 #include "protocol/protocol.h"
29 #include "protocol/protocol_api.h"
30
31 #include "common/rb_tree.h"
32 #include "common/system.h"
33 #include "common/logging.h"
34
35
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;
49 };
50
51 static const char *prog;
52
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;
58 };
59
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
64 */
65 #define KILLTCP_KEYLEN  10
66 static uint32_t *killtcp_key(ctdb_sock_addr *src, ctdb_sock_addr *dst)
67 {
68         static uint32_t key[KILLTCP_KEYLEN];
69
70         bzero(key, sizeof(key));
71
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;
75         }
76
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;
100         }
101         default:
102                 DEBUG(DEBUG_ERR, (__location__ " ERROR, unknown family passed :%u\n", src->sa.sa_family));
103                 return key;
104         }
105
106         return key;
107 }
108
109 /*
110   called when we get a read event on the raw socket
111  */
112 static void capture_tcp_handler(struct tevent_context *ev,
113                                 struct tevent_fd *fde,
114                                 uint16_t flags, void *private_data)
115 {
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;
122
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;
129         }
130
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;
141         }
142
143         /* check if we have this guy in our list of connections
144            to kill
145         */
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;
151         }
152
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)));
161
162         ctdb_sys_send_tcp(&con->dst_addr, &con->src_addr, ack_seq, seq, 1);
163         talloc_free(con);
164 }
165
166
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
171 */
172 static int tickle_connection_traverse(void *param, void *data)
173 {
174         struct ctdb_killtcp_con *con = talloc_get_type(data, struct ctdb_killtcp_con);
175
176         con->killtcp->batch_count++;
177         if (con->killtcp->batch_count > con->killtcp->batch_size) {
178                 /* Terminate the traverse */
179                 return -1;
180         }
181
182         ctdb_sys_send_tcp(&con->dst_addr, &con->src_addr, 0, 0, 0);
183
184         return 0;
185 }
186
187
188 /*
189    called every second until all sentenced connections have been reset
190  */
191 static void ctdb_tickle_sentenced_connections(struct tevent_context *ev,
192                                               struct tevent_timer *te,
193                                               struct timeval t, void *private_data)
194 {
195         struct ctdb_kill_tcp *killtcp = talloc_get_type(private_data, struct ctdb_kill_tcp);
196         void *delete_cons = talloc_new(NULL);
197
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);
201
202         /* now we've finished traverse, it's safe to do deletion. */
203         talloc_free(delete_cons);
204
205         killtcp->attempts++;
206
207         /* If there are no more connections to kill or we have tried
208            too many times we can remove the entire killtcp structure
209          */
210         if (killtcp->connections == NULL ||
211             killtcp->connections->root == NULL ||
212                 killtcp->attempts >= killtcp->max_attempts) {
213                 talloc_free(killtcp);
214                 return;
215         }
216
217         /* try tickling them again in a seconds time
218          */
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);
224 }
225
226 /* nothing fancy here, just unconditionally replace any existing
227    connection structure with the new one.
228
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
232 */
233 static void *add_killtcp_callback(void *parm, void *data)
234 {
235         return parm;
236 }
237
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)
247 {
248         struct ctdb_kill_tcp *killtcp;
249         struct ctdb_killtcp_con *con;
250
251         if (killtcp_arg == NULL) {
252                 DEBUG(DEBUG_ERR, (__location__ " killtcp_arg is NULL!\n"));
253                 return -1;
254         }
255
256         killtcp = *killtcp_arg;
257
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;
265                 }
266
267                 killtcp->capture_fd  = -1;
268                 killtcp->connections = trbt_create(killtcp, 0);
269
270                 killtcp->attempts = 0;
271                 killtcp->max_attempts = 50;
272
273                 killtcp->retry_interval.tv_sec = 0;
274                 killtcp->retry_interval.tv_usec = 100 * 1000;
275
276                 killtcp->batch_count = 0;
277                 killtcp->batch_size = 300;
278
279                 *killtcp_arg = killtcp;
280         }
281
282         /* create a structure that describes this connection we want to
283            RST and store it in killtcp->connections
284         */
285         con = talloc(killtcp, struct ctdb_killtcp_con);
286         if (con == NULL) {
287                 DEBUG(DEBUG_ERR, (__location__ " out of memory\n"));
288                 return -1;
289         }
290         con->src_addr = *src;
291         con->dst_addr = *dst;
292         con->killtcp  = killtcp;
293
294
295         trbt_insertarray32_callback(killtcp->connections,
296                                     KILLTCP_KEYLEN,
297                                     killtcp_key(&con->dst_addr,
298                                                 &con->src_addr),
299                                     add_killtcp_callback, con);
300
301         /*
302            If we don't have a socket to listen on yet we must create it
303          */
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;
313                 }
314         }
315
316
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);
323         }
324
325         return 0;
326 }
327
328 static int ctdb_killtcp_destructor(struct ctdb_kill_tcp *killtcp)
329 {
330         bool *done = killtcp->destructor_data;
331         *done = true;
332
333         return 0;
334 }
335
336 static void usage(void)
337 {
338         printf("usage: %s <interface> [ <srcip:port> <dstip:port> ]\n", prog);
339         exit(1);
340 }
341
342 int main(int argc, char **argv)
343 {
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;
354
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;
362                 }
363         }
364
365         prog = argv[0];
366
367         if (argc != 2 && argc != 4) {
368                 usage();
369         }
370
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;
375                 }
376
377                 if (!parse_ip_port(argv[3], &conn.dst)) {
378                         DEBUG(DEBUG_ERR, ("Bad IP:port '%s'\n", argv[3]));
379                         goto fail;
380                 }
381
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;
391                 }
392         }
393
394         mem_ctx = talloc_new(NULL);
395         if (mem_ctx == NULL) {
396                 DEBUG(DEBUG_ERR, (__location__ " out of memory\n"));
397                 goto fail;
398         }
399
400         ev = tevent_context_init(mem_ctx);
401         if (ev == NULL) {
402                 DEBUG(DEBUG_ERR, ("Failed to initialise tevent\n"));
403                 goto fail;
404         }
405
406         if (num == 0) {
407                 /* No connections, done! */
408                 talloc_free(mem_ctx);
409                 return 0;
410         }
411
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;
419                 }
420         }
421
422         done = false;
423         killtcp->destructor_data = &done;
424         talloc_set_destructor(killtcp, ctdb_killtcp_destructor);
425
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);
430
431         while (!done) {
432                 tevent_loop_once(ev);
433         }
434
435         talloc_free(mem_ctx);
436
437         return 0;
438
439 fail:
440         TALLOC_FREE(mem_ctx);
441         return -1;
442 }