third_party: Update resolv_wrapper to version 1.1.8
[Samba.git] / ctdb / tools / ctdb_killtcp.c
blob007422f42fc908a9386a406afcacca1ce6e647be
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 "replace.h"
21 #include "system/network.h"
23 #include <talloc.h>
24 #include <tevent.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;
39 int capture_fd;
40 struct tevent_fd *fde;
41 struct db_hash_context *connections;
42 void *private_data;
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,
53 uint16_t flags,
54 void *private_data);
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,
59 void *private_data);
61 static struct tevent_req *reset_connections_send(
62 TALLOC_CTX *mem_ctx,
63 struct tevent_context *ev,
64 const char *iface,
65 struct ctdb_connection_list *conn_list)
67 struct tevent_req *req, *subreq;
68 struct reset_connections_state *state;
69 unsigned int i;
70 int ret;
72 req = tevent_req_create(mem_ctx, &state,
73 struct reset_connections_state);
74 if (req == NULL) {
75 return NULL;
78 state->ev = ev;
80 if (conn_list->num == 0) {
81 /* No connections, done! */
82 tevent_req_done(req);
83 return tevent_req_post(req, ev);
86 ret = db_hash_init(state, "connections", 2048, DB_HASH_SIMPLE,
87 &state->connections);
88 if (ret != 0) {
89 D_ERR("Failed to initialise connection hash (%s)\n",
90 strerror(ret));
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),
105 NULL, 0);
106 if (ret != 0) {
107 D_ERR("Error adding connection to hash (%s)\n",
108 strerror(ret));
109 tevent_req_error(req, ret);
110 return tevent_req_post(req, ev);
114 state->attempts = 0;
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;
123 state->capture_fd =
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,
133 TEVENT_FD_READ,
134 reset_connections_capture_tcp_handler,
135 state);
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);
147 return 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,
155 uint16_t flags,
156 void *private_data)
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;
163 int rst;
164 uint16_t window;
165 int ret;
167 ret = ctdb_sys_read_tcp_packet(state->capture_fd,
168 state->private_data,
169 &conn.server, &conn.client,
170 &ack_seq, &seq, &rst, &window);
171 if (ret != 0) {
172 /* Not a TCP-ACK? Unexpected protocol? */
173 DBG_DEBUG("Failed to parse packet, errno=%d\n", ret);
174 return;
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));
184 return;
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));
190 if (ret == ENOENT) {
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));
194 return;
196 if (ret != 0) {
197 DBG_WARNING("Internal error (%s)\n", strerror(ret));
198 return;
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);
205 if (ret != 0) {
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);
220 bool status;
221 int count, ret;
223 status = tevent_wakeup_recv(subreq);
224 TALLOC_FREE(subreq);
226 if (! status) {
227 DBG_WARNING("Unexpected error on timer expiry\n");
228 /* Keep going... */
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,
235 state, NULL);
236 if (ret != 0) {
237 DBG_WARNING("Unexpected error traversing connections (%s)\n",
238 strerror(ret));
241 state->attempts++;
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);
248 if (ret != 0) {
249 /* What now? Try again until max_attempts reached */
250 DBG_WARNING("Unexpected error traversing connections (%s)\n",
251 strerror(ret));
252 count = 1;
254 if (count == 0 ||
255 state->attempts >= state->max_attempts) {
256 tevent_req_done(req);
257 return;
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)) {
266 return;
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,
274 void *private_data)
276 struct reset_connections_state *state = talloc_get_type_abort(
277 private_data, struct reset_connections_state);
278 struct ctdb_connection *conn;
279 int ret;
281 if (keylen != sizeof(*conn)) {
282 DBG_WARNING("Unexpected data in connection hash\n");
283 return 0;
286 conn = (struct ctdb_connection *)keybuf;
288 state->batch_count++;
289 if (state->batch_count > state->batch_size) {
290 /* Terminate the traverse */
291 return 1;
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);
297 if (ret != 0) {
298 DBG_ERR("Error sending tickle ACK\n");
299 /* continue */
302 return 0;
305 static bool reset_connections_recv(struct tevent_req *req, int *perr)
307 int err;
309 if (tevent_req_is_unix_error(req, &err)) {
310 if (perr != NULL) {
311 *perr = err;
313 return false;
316 return true;
319 static void usage(const char *prog)
321 printf("usage: %s <interface> [ <srcip:port> <dstip:port> ]\n", prog);
322 exit(1);
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;
331 const char *t;
332 struct tevent_req *req;
333 int debug_level;
334 bool status;
335 bool ok;
336 int ret;
338 /* Set the debug level */
339 t = getenv("CTDB_DEBUGLEVEL");
340 if (t != NULL) {
341 ok = debug_level_parse(t, &debug_level);
342 if (!ok) {
343 debug_level = DEBUG_ERR;
345 debuglevel_set(debug_level);
348 if (argc != 2 && argc != 4) {
349 usage(argv[0]);
352 if (argc == 4) {
353 ret = ctdb_sock_addr_from_string(argv[2], &conn.client, true);
354 if (ret != 0) {
355 D_ERR("Bad IP:port '%s'\n", argv[2]);
356 goto fail;
359 ret = ctdb_sock_addr_from_string(argv[3], &conn.server, true);
360 if (ret != 0) {
361 D_ERR("Bad IP:port '%s'\n", argv[3]);
362 goto fail;
366 conn_list = talloc_zero(mem_ctx, struct ctdb_connection_list);
367 if (conn_list == NULL) {
368 ret = ENOMEM;
369 DBG_ERR("Internal error (%s)\n", strerror(ret));
370 goto fail;
372 ret = ctdb_connection_list_add(conn_list, &conn);
373 if (ret != 0) {
374 DBG_ERR("Internal error (%s)\n", strerror(ret));
375 goto fail;
377 } else {
378 ret = ctdb_connection_list_read(mem_ctx, 0, true, &conn_list);
379 if (ret != 0) {
380 D_ERR("Unable to parse connections (%s)\n",
381 strerror(ret));
382 goto fail;
386 mem_ctx = talloc_new(NULL);
387 if (mem_ctx == NULL) {
388 DEBUG(DEBUG_ERR, (__location__ " out of memory\n"));
389 goto fail;
392 ev = tevent_context_init(mem_ctx);
393 if (ev == NULL) {
394 DEBUG(DEBUG_ERR, ("Failed to initialise tevent\n"));
395 goto fail;
398 req = reset_connections_send(mem_ctx, ev, argv[1], conn_list);
399 if (req == NULL) {
400 goto fail;
403 tevent_req_poll(req, ev);
405 status = reset_connections_recv(req, &ret);
406 if (! status) {
407 D_ERR("Failed to kill connections (%s)\n", strerror(ret));
408 goto fail;
411 talloc_free(mem_ctx);
413 return 0;
415 fail:
416 TALLOC_FREE(mem_ctx);
417 return -1;