5c0ea112faf5abb6458d6bd04a9325d3952029fe
[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_util.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         struct ctdb_connection conn;
56         struct ctdb_kill_tcp *killtcp;
57 };
58
59 /* this function is used to create a key to represent this socketpair
60    in the killtcp tree.
61    this key is used to insert and lookup matching socketpairs that are
62    to be tickled and RST
63 */
64 #define KILLTCP_KEYLEN  10
65 static uint32_t *killtcp_key(ctdb_sock_addr *src, ctdb_sock_addr *dst)
66 {
67         static uint32_t key[KILLTCP_KEYLEN];
68
69         bzero(key, sizeof(key));
70
71         if (src->sa.sa_family != dst->sa.sa_family) {
72                 DEBUG(DEBUG_ERR, (__location__ " ERROR, different families passed :%u vs %u\n", src->sa.sa_family, dst->sa.sa_family));
73                 return key;
74         }
75
76         switch (src->sa.sa_family) {
77         case AF_INET:
78                 key[0]  = dst->ip.sin_addr.s_addr;
79                 key[1]  = src->ip.sin_addr.s_addr;
80                 key[2]  = dst->ip.sin_port;
81                 key[3]  = src->ip.sin_port;
82                 break;
83         case AF_INET6: {
84                 uint32_t *dst6_addr32 =
85                         (uint32_t *)&(dst->ip6.sin6_addr.s6_addr);
86                 uint32_t *src6_addr32 =
87                         (uint32_t *)&(src->ip6.sin6_addr.s6_addr);
88                 key[0]  = dst6_addr32[3];
89                 key[1]  = src6_addr32[3];
90                 key[2]  = dst6_addr32[2];
91                 key[3]  = src6_addr32[2];
92                 key[4]  = dst6_addr32[1];
93                 key[5]  = src6_addr32[1];
94                 key[6]  = dst6_addr32[0];
95                 key[7]  = src6_addr32[0];
96                 key[8]  = dst->ip6.sin6_port;
97                 key[9]  = src->ip6.sin6_port;
98                 break;
99         }
100         default:
101                 DEBUG(DEBUG_ERR, (__location__ " ERROR, unknown family passed :%u\n", src->sa.sa_family));
102                 return key;
103         }
104
105         return key;
106 }
107
108 /*
109   called when we get a read event on the raw socket
110  */
111 static void capture_tcp_handler(struct tevent_context *ev,
112                                 struct tevent_fd *fde,
113                                 uint16_t flags, void *private_data)
114 {
115         struct ctdb_kill_tcp *killtcp = talloc_get_type(private_data, struct ctdb_kill_tcp);
116         struct ctdb_killtcp_con *con;
117         /* 0 the parts that don't get set by ctdb_sys_read_tcp_packet */
118         struct ctdb_connection conn;
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                                      &conn.server, &conn.client,
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                 D_DEBUG("Ignoring packet: %s, "
134                         "seq=%"PRIu32", ack_seq=%"PRIu32", "
135                         "rst=%d, window=%"PRIu16"\n",
136                         ctdb_connection_to_string(killtcp, &conn, false),
137                         seq, ack_seq, rst, ntohs(window));
138                 return;
139         }
140
141         /* check if we have this guy in our list of connections
142            to kill
143         */
144         con = trbt_lookuparray32(killtcp->connections,
145                                  KILLTCP_KEYLEN,
146                                  killtcp_key(&conn.server, &conn.client));
147         if (con == NULL) {
148                 /* no this was some other packet we can just ignore */
149                 return;
150         }
151
152         /* This connection has been tickled!  RST it and remove it
153          * from the list. */
154         D_INFO("Sending a TCP RST to kill connection %s\n",
155                ctdb_connection_to_string(killtcp, &con->conn, true));
156
157         ctdb_sys_send_tcp(&con->conn.server, &con->conn.client,
158                           ack_seq, seq, 1);
159         talloc_free(con);
160 }
161
162
163 /* when traversing the list of all tcp connections to send tickle acks to
164    (so that we can capture the ack coming back and kill the connection
165     by a RST)
166    this callback is called for each connection we are currently trying to kill
167 */
168 static int tickle_connection_traverse(void *param, void *data)
169 {
170         struct ctdb_killtcp_con *con = talloc_get_type(data, struct ctdb_killtcp_con);
171
172         con->killtcp->batch_count++;
173         if (con->killtcp->batch_count > con->killtcp->batch_size) {
174                 /* Terminate the traverse */
175                 return -1;
176         }
177
178         ctdb_sys_send_tcp(&con->conn.server, &con->conn.client, 0, 0, 0);
179
180         return 0;
181 }
182
183
184 /*
185    called every second until all sentenced connections have been reset
186  */
187 static void ctdb_tickle_sentenced_connections(struct tevent_context *ev,
188                                               struct tevent_timer *te,
189                                               struct timeval t, void *private_data)
190 {
191         struct ctdb_kill_tcp *killtcp = talloc_get_type(private_data, struct ctdb_kill_tcp);
192         void *delete_cons = talloc_new(NULL);
193
194         /* loop over up to batch_size connections sending tickle ACKs */
195         killtcp->batch_count = 0;
196         trbt_traversearray32(killtcp->connections, KILLTCP_KEYLEN, tickle_connection_traverse, delete_cons);
197
198         /* now we've finished traverse, it's safe to do deletion. */
199         talloc_free(delete_cons);
200
201         killtcp->attempts++;
202
203         /* If there are no more connections to kill or we have tried
204            too many times we can remove the entire killtcp structure
205          */
206         if (killtcp->connections == NULL ||
207             killtcp->connections->root == NULL ||
208                 killtcp->attempts >= killtcp->max_attempts) {
209                 talloc_free(killtcp);
210                 return;
211         }
212
213         /* try tickling them again in a seconds time
214          */
215         tevent_add_timer(ev, killtcp,
216                          tevent_timeval_current_ofs(
217                                  killtcp->retry_interval.tv_sec,
218                                  killtcp->retry_interval.tv_usec),
219                          ctdb_tickle_sentenced_connections, killtcp);
220 }
221
222 /* nothing fancy here, just unconditionally replace any existing
223    connection structure with the new one.
224
225    don't even free the old one if it did exist, that one is talloc_stolen
226    by the same node in the tree anyway and will be deleted when the new data
227    is deleted
228 */
229 static void *add_killtcp_callback(void *parm, void *data)
230 {
231         return parm;
232 }
233
234 /* Add a TCP socket to the list of connections we want to RST.  The
235  * list is attached to *killtcp_arg.  If this is NULL then allocate
236  * the structure.  */
237 static int ctdb_killtcp(struct tevent_context *ev,
238                         TALLOC_CTX *mem_ctx,
239                         const char *iface,
240                         struct ctdb_connection *conn,
241                         struct ctdb_kill_tcp **killtcp_arg)
242 {
243         struct ctdb_kill_tcp *killtcp;
244         struct ctdb_killtcp_con *con;
245
246         if (killtcp_arg == NULL) {
247                 DEBUG(DEBUG_ERR, (__location__ " killtcp_arg is NULL!\n"));
248                 return -1;
249         }
250
251         killtcp = *killtcp_arg;
252
253         /* Allocate a new structure if necessary.  The structure is
254          * only freed when mem_ctx is freed. */
255         if (killtcp == NULL) {
256                 killtcp = talloc_zero(mem_ctx, struct ctdb_kill_tcp);
257                 if (killtcp == NULL) {
258                         DEBUG(DEBUG_ERR, (__location__ " out of memory\n"));
259                         return -1;
260                 }
261
262                 killtcp->capture_fd  = -1;
263                 killtcp->connections = trbt_create(killtcp, 0);
264
265                 killtcp->attempts = 0;
266                 killtcp->max_attempts = 50;
267
268                 killtcp->retry_interval.tv_sec = 0;
269                 killtcp->retry_interval.tv_usec = 100 * 1000;
270
271                 killtcp->batch_count = 0;
272                 killtcp->batch_size = 300;
273
274                 *killtcp_arg = killtcp;
275         }
276
277         /* create a structure that describes this connection we want to
278            RST and store it in killtcp->connections
279         */
280         con = talloc(killtcp, struct ctdb_killtcp_con);
281         if (con == NULL) {
282                 DEBUG(DEBUG_ERR, (__location__ " out of memory\n"));
283                 return -1;
284         }
285         con->conn.client = conn->client;
286         con->conn.server = conn->server;
287         con->killtcp  = killtcp;
288
289
290         trbt_insertarray32_callback(killtcp->connections,
291                                     KILLTCP_KEYLEN,
292                                     killtcp_key(&con->conn.server,
293                                                 &con->conn.client),
294                                     add_killtcp_callback, con);
295
296         /*
297            If we don't have a socket to listen on yet we must create it
298          */
299         if (killtcp->capture_fd == -1) {
300                 killtcp->capture_fd =
301                         ctdb_sys_open_capture_socket(iface,
302                                                      &killtcp->private_data);
303                 if (killtcp->capture_fd == -1) {
304                         DEBUG(DEBUG_CRIT,(__location__ " Failed to open capturing "
305                                           "socket on iface '%s' for killtcp (%s)\n",
306                                           iface, strerror(errno)));
307                         return -1;
308                 }
309         }
310
311
312         if (killtcp->fde == NULL) {
313                 killtcp->fde = tevent_add_fd(ev, killtcp,
314                                              killtcp->capture_fd,
315                                              TEVENT_FD_READ,
316                                              capture_tcp_handler, killtcp);
317                 tevent_fd_set_auto_close(killtcp->fde);
318         }
319
320         return 0;
321 }
322
323 static int ctdb_killtcp_destructor(struct ctdb_kill_tcp *killtcp)
324 {
325         bool *done = killtcp->destructor_data;
326         *done = true;
327
328         return 0;
329 }
330
331 static void usage(void)
332 {
333         printf("usage: %s <interface> [ <srcip:port> <dstip:port> ]\n", prog);
334         exit(1);
335 }
336
337 int main(int argc, char **argv)
338 {
339         struct ctdb_connection conn;
340         struct ctdb_kill_tcp *killtcp = NULL;
341         struct tevent_context *ev = NULL;
342         struct TALLOC_CONTEXT *mem_ctx = NULL;
343         struct ctdb_connection_list *conn_list = NULL;
344         const char *t;
345         int debug_level;
346         bool done;
347         int i, ret;
348
349         /* Set the debug level */
350         t = getenv("CTDB_DEBUGLEVEL");
351         if (t != NULL) {
352                 if (debug_level_parse(t, &debug_level)) {
353                         DEBUGLEVEL = debug_level;
354                 } else {
355                         DEBUGLEVEL = DEBUG_ERR;
356                 }
357         }
358
359         prog = argv[0];
360
361         if (argc != 2 && argc != 4) {
362                 usage();
363         }
364
365         if (argc == 4) {
366                 ret = ctdb_sock_addr_from_string(argv[2], &conn.client, true);
367                 if (ret != 0) {
368                         D_ERR("Bad IP:port '%s'\n", argv[2]);
369                         goto fail;
370                 }
371
372                 ret = ctdb_sock_addr_from_string(argv[3], &conn.server, true);
373                 if (ret != 0) {
374                         D_ERR("Bad IP:port '%s'\n", argv[3]);
375                         goto fail;
376                 }
377
378
379                 conn_list = talloc_zero(mem_ctx, struct ctdb_connection_list);
380                 if (conn_list == NULL) {
381                         ret = ENOMEM;
382                         DBG_ERR("Internal error (%s)\n", strerror(ret));
383                         goto fail;
384                 }
385                 ret = ctdb_connection_list_add(conn_list, &conn);
386                 if (ret != 0) {
387                         DBG_ERR("Internal error (%s)\n", strerror(ret));
388                         goto fail;
389                 }
390         } else {
391                 ret = ctdb_connection_list_read(mem_ctx, true, &conn_list);
392                 if (ret != 0) {
393                         D_ERR("Unable to parse connections (%s)\n",
394                               strerror(ret));
395                         goto fail;
396                 }
397         }
398
399         mem_ctx = talloc_new(NULL);
400         if (mem_ctx == NULL) {
401                 DEBUG(DEBUG_ERR, (__location__ " out of memory\n"));
402                 goto fail;
403         }
404
405         ev = tevent_context_init(mem_ctx);
406         if (ev == NULL) {
407                 DEBUG(DEBUG_ERR, ("Failed to initialise tevent\n"));
408                 goto fail;
409         }
410
411         if (conn_list->num == 0) {
412                 /* No connections, done! */
413                 talloc_free(mem_ctx);
414                 return 0;
415         }
416
417         for (i = 0; i < conn_list->num; i++) {
418                 ret = ctdb_killtcp(ev, mem_ctx, argv[1],
419                                    &conn_list->conn[i], &killtcp);
420                 if (ret != 0) {
421                         DEBUG(DEBUG_ERR, ("Unable to killtcp\n"));
422                         goto fail;
423                 }
424         }
425
426         done = false;
427         killtcp->destructor_data = &done;
428         talloc_set_destructor(killtcp, ctdb_killtcp_destructor);
429
430         /* Do the initial processing of connections */
431         tevent_add_timer(ev, killtcp,
432                          tevent_timeval_current_ofs(0, 0),
433                          ctdb_tickle_sentenced_connections, killtcp);
434
435         while (!done) {
436                 tevent_loop_once(ev);
437         }
438
439         talloc_free(mem_ctx);
440
441         return 0;
442
443 fail:
444         TALLOC_FREE(mem_ctx);
445         return -1;
446 }