From: Martin Schwenke Date: Fri, 11 Mar 2016 05:04:30 +0000 (+1100) Subject: ctdb-killtcp: Merge "common" killtcp code into helper X-Git-Tag: tdb-1.3.9~39 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=2e1e1e8268a2143eb69ea46c9b3335dcd4377914;p=thirdparty%2Fsamba.git ctdb-killtcp: Merge "common" killtcp code into helper ctdb_killtcp.c is now the only place it is needed. Signed-off-by: Martin Schwenke Reviewed-by: Amitay Isaacs --- diff --git a/ctdb/server/killtcp.c b/ctdb/server/killtcp.c deleted file mode 100644 index ff32fdc1fc5..00000000000 --- a/ctdb/server/killtcp.c +++ /dev/null @@ -1,302 +0,0 @@ -/* - CTDB TCP connection killing code - - Copyright (C) Ronnie Sahlberg 2007 - Copyright (C) Andrew Tridgell 2007 - Copyright (C) Martin Schwenke 2016 - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see . -*/ - -#include "replace.h" -#include "system/network.h" - -#include "lib/util/debug.h" - -#include "protocol/protocol.h" -#include "protocol/protocol_api.h" - -#include "common/rb_tree.h" -#include "common/system.h" -#include "common/logging.h" - -#include "killtcp.h" - -/* TCP connection to be killed */ -struct ctdb_killtcp_con { - ctdb_sock_addr src_addr; - ctdb_sock_addr dst_addr; - int count; - struct ctdb_kill_tcp *killtcp; -}; - -/* this function is used to create a key to represent this socketpair - in the killtcp tree. - this key is used to insert and lookup matching socketpairs that are - to be tickled and RST -*/ -#define KILLTCP_KEYLEN 10 -static uint32_t *killtcp_key(ctdb_sock_addr *src, ctdb_sock_addr *dst) -{ - static uint32_t key[KILLTCP_KEYLEN]; - - bzero(key, sizeof(key)); - - if (src->sa.sa_family != dst->sa.sa_family) { - DEBUG(DEBUG_ERR, (__location__ " ERROR, different families passed :%u vs %u\n", src->sa.sa_family, dst->sa.sa_family)); - return key; - } - - switch (src->sa.sa_family) { - case AF_INET: - key[0] = dst->ip.sin_addr.s_addr; - key[1] = src->ip.sin_addr.s_addr; - key[2] = dst->ip.sin_port; - key[3] = src->ip.sin_port; - break; - case AF_INET6: { - uint32_t *dst6_addr32 = - (uint32_t *)&(dst->ip6.sin6_addr.s6_addr); - uint32_t *src6_addr32 = - (uint32_t *)&(src->ip6.sin6_addr.s6_addr); - key[0] = dst6_addr32[3]; - key[1] = src6_addr32[3]; - key[2] = dst6_addr32[2]; - key[3] = src6_addr32[2]; - key[4] = dst6_addr32[1]; - key[5] = src6_addr32[1]; - key[6] = dst6_addr32[0]; - key[7] = src6_addr32[0]; - key[8] = dst->ip6.sin6_port; - key[9] = src->ip6.sin6_port; - break; - } - default: - DEBUG(DEBUG_ERR, (__location__ " ERROR, unknown family passed :%u\n", src->sa.sa_family)); - return key; - } - - return key; -} - -/* - called when we get a read event on the raw socket - */ -static void capture_tcp_handler(struct tevent_context *ev, - struct tevent_fd *fde, - uint16_t flags, void *private_data) -{ - struct ctdb_kill_tcp *killtcp = talloc_get_type(private_data, struct ctdb_kill_tcp); - struct ctdb_killtcp_con *con; - ctdb_sock_addr src, dst; - uint32_t ack_seq, seq; - - if (!(flags & TEVENT_FD_READ)) { - return; - } - - if (ctdb_sys_read_tcp_packet(killtcp->capture_fd, - killtcp->private_data, - &src, &dst, - &ack_seq, &seq) != 0) { - /* probably a non-tcp ACK packet */ - return; - } - - /* check if we have this guy in our list of connections - to kill - */ - con = trbt_lookuparray32(killtcp->connections, - KILLTCP_KEYLEN, killtcp_key(&src, &dst)); - if (con == NULL) { - /* no this was some other packet we can just ignore */ - return; - } - - /* This one has been tickled ! - now reset him and remove him from the list. - */ - DEBUG(DEBUG_INFO, - ("sending a tcp reset to kill connection :%d -> %s:%d\n", - ntohs(con->dst_addr.ip.sin_port), - ctdb_sock_addr_to_string(con, &con->src_addr), - ntohs(con->src_addr.ip.sin_port))); - - ctdb_sys_send_tcp(&con->dst_addr, &con->src_addr, ack_seq, seq, 1); - talloc_free(con); -} - - -/* when traversing the list of all tcp connections to send tickle acks to - (so that we can capture the ack coming back and kill the connection - by a RST) - this callback is called for each connection we are currently trying to kill -*/ -static int tickle_connection_traverse(void *param, void *data) -{ - struct ctdb_killtcp_con *con = talloc_get_type(data, struct ctdb_killtcp_con); - - /* have tried too many times, just give up */ - if (con->count >= 5) { - /* can't delete in traverse: reparent to delete_cons */ - talloc_steal(param, con); - return 0; - } - - /* othervise, try tickling it again */ - con->count++; - ctdb_sys_send_tcp( - (ctdb_sock_addr *)&con->dst_addr, - (ctdb_sock_addr *)&con->src_addr, - 0, 0, 0); - return 0; -} - - -/* - called every second until all sentenced connections have been reset - */ -static void ctdb_tickle_sentenced_connections(struct tevent_context *ev, - struct tevent_timer *te, - struct timeval t, void *private_data) -{ - struct ctdb_kill_tcp *killtcp = talloc_get_type(private_data, struct ctdb_kill_tcp); - void *delete_cons = talloc_new(NULL); - - /* loop over all connections sending tickle ACKs */ - trbt_traversearray32(killtcp->connections, KILLTCP_KEYLEN, tickle_connection_traverse, delete_cons); - - /* now we've finished traverse, it's safe to do deletion. */ - talloc_free(delete_cons); - - /* If there are no more connections to kill we can remove the - entire killtcp structure - */ - if ((killtcp->connections == NULL) || - (killtcp->connections->root == NULL)) { - talloc_free(killtcp); - return; - } - - /* try tickling them again in a seconds time - */ - tevent_add_timer(ev, killtcp, - tevent_timeval_current_ofs(1, 0), - ctdb_tickle_sentenced_connections, killtcp); -} - -/* nothing fancy here, just unconditionally replace any existing - connection structure with the new one. - - don't even free the old one if it did exist, that one is talloc_stolen - by the same node in the tree anyway and will be deleted when the new data - is deleted -*/ -static void *add_killtcp_callback(void *parm, void *data) -{ - return parm; -} - -/* Add a TCP socket to the list of connections we want to RST. The - * list is attached to *killtcp_arg. If this is NULL then allocate - * the structure. */ -int ctdb_killtcp(struct tevent_context *ev, - TALLOC_CTX *mem_ctx, - const char *iface, - const ctdb_sock_addr *src, - const ctdb_sock_addr *dst, - struct ctdb_kill_tcp **killtcp_arg) -{ - struct ctdb_kill_tcp *killtcp; - struct ctdb_killtcp_con *con; - - if (killtcp_arg == NULL) { - DEBUG(DEBUG_ERR, (__location__ " killtcp_arg is NULL!\n")); - return -1; - } - - killtcp = *killtcp_arg; - - /* Allocate a new structure if necessary. The structure is - * only freed when mem_ctx is freed. */ - if (killtcp == NULL) { - killtcp = talloc_zero(mem_ctx, struct ctdb_kill_tcp); - if (killtcp == NULL) { - DEBUG(DEBUG_ERR, (__location__ " out of memory\n")); - return -1; - } - - killtcp->capture_fd = -1; - killtcp->connections = trbt_create(killtcp, 0); - *killtcp_arg = killtcp; - } - - /* create a structure that describes this connection we want to - RST and store it in killtcp->connections - */ - con = talloc(killtcp, struct ctdb_killtcp_con); - if (con == NULL) { - DEBUG(DEBUG_ERR, (__location__ " out of memory\n")); - return -1; - } - con->src_addr = *src; - con->dst_addr = *dst; - con->count = 0; - con->killtcp = killtcp; - - - trbt_insertarray32_callback(killtcp->connections, - KILLTCP_KEYLEN, - killtcp_key(&con->dst_addr, - &con->src_addr), - add_killtcp_callback, con); - - /* - If we don't have a socket to listen on yet we must create it - */ - if (killtcp->capture_fd == -1) { - killtcp->capture_fd = - ctdb_sys_open_capture_socket(iface, - &killtcp->private_data); - if (killtcp->capture_fd == -1) { - DEBUG(DEBUG_CRIT,(__location__ " Failed to open capturing " - "socket on iface '%s' for killtcp (%s)\n", - iface, strerror(errno))); - return -1; - } - } - - - if (killtcp->fde == NULL) { - killtcp->fde = tevent_add_fd(ev, killtcp, - killtcp->capture_fd, - TEVENT_FD_READ, - capture_tcp_handler, killtcp); - tevent_fd_set_auto_close(killtcp->fde); - - /* We also need to set up some events to tickle all these connections - until they are all reset - */ - tevent_add_timer(ev, killtcp, tevent_timeval_current_ofs(1, 0), - ctdb_tickle_sentenced_connections, killtcp); - } - - /* tickle him once now */ - ctdb_sys_send_tcp( - &con->dst_addr, - &con->src_addr, - 0, 0, 0); - - return 0; -} diff --git a/ctdb/server/killtcp.h b/ctdb/server/killtcp.h deleted file mode 100644 index 24ab35136d3..00000000000 --- a/ctdb/server/killtcp.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - CTDB TCP connection killing code - - Copyright (C) Ronnie Sahlberg 2007 - Copyright (C) Andrew Tridgell 2007 - Copyright (C) Martin Schwenke 2016 - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, see . -*/ - -#ifndef _CTDB_KILLTCP_H_ -#define _CTDB_KILLTCP_H_ - -#include -#include - -#include "replace.h" -#include "system/network.h" - -#include "common/rb_tree.h" - -#include "protocol/protocol.h" - -/* Contains the listening socket and the list of TCP connections to - * kill */ -struct ctdb_kill_tcp { - int capture_fd; - struct tevent_fd *fde; - trbt_tree_t *connections; - void *private_data; - void *destructor_data; -}; - - -/* Add a TCP socket to the list of connections we want to RST. The - * list is attached to *killtcp_arg. If this is NULL then allocate - * the structure. */ -int ctdb_killtcp(struct tevent_context *ev, - TALLOC_CTX *mem_ctx, - const char *iface, - const ctdb_sock_addr *src, - const ctdb_sock_addr *dst, - struct ctdb_kill_tcp **killtcp_arg); - -#endif /* _CTDB_KILLTCP_H_ */ diff --git a/ctdb/tools/ctdb_killtcp.c b/ctdb/tools/ctdb_killtcp.c index 3b1c4d927bc..e6a19a75ca3 100644 --- a/ctdb/tools/ctdb_killtcp.c +++ b/ctdb/tools/ctdb_killtcp.c @@ -17,20 +17,302 @@ along with this program; if not, see . */ +#include +#include + #include "replace.h" #include "system/network.h" #include "lib/util/debug.h" #include "protocol/protocol.h" +#include "protocol/protocol_api.h" +#include "common/rb_tree.h" #include "common/system.h" #include "common/logging.h" -#include "server/killtcp.h" + +/* Contains the listening socket and the list of TCP connections to + * kill */ +struct ctdb_kill_tcp { + int capture_fd; + struct tevent_fd *fde; + trbt_tree_t *connections; + void *private_data; + void *destructor_data; +}; static const char *prog; +/* TCP connection to be killed */ +struct ctdb_killtcp_con { + ctdb_sock_addr src_addr; + ctdb_sock_addr dst_addr; + int count; + struct ctdb_kill_tcp *killtcp; +}; + +/* this function is used to create a key to represent this socketpair + in the killtcp tree. + this key is used to insert and lookup matching socketpairs that are + to be tickled and RST +*/ +#define KILLTCP_KEYLEN 10 +static uint32_t *killtcp_key(ctdb_sock_addr *src, ctdb_sock_addr *dst) +{ + static uint32_t key[KILLTCP_KEYLEN]; + + bzero(key, sizeof(key)); + + if (src->sa.sa_family != dst->sa.sa_family) { + DEBUG(DEBUG_ERR, (__location__ " ERROR, different families passed :%u vs %u\n", src->sa.sa_family, dst->sa.sa_family)); + return key; + } + + switch (src->sa.sa_family) { + case AF_INET: + key[0] = dst->ip.sin_addr.s_addr; + key[1] = src->ip.sin_addr.s_addr; + key[2] = dst->ip.sin_port; + key[3] = src->ip.sin_port; + break; + case AF_INET6: { + uint32_t *dst6_addr32 = + (uint32_t *)&(dst->ip6.sin6_addr.s6_addr); + uint32_t *src6_addr32 = + (uint32_t *)&(src->ip6.sin6_addr.s6_addr); + key[0] = dst6_addr32[3]; + key[1] = src6_addr32[3]; + key[2] = dst6_addr32[2]; + key[3] = src6_addr32[2]; + key[4] = dst6_addr32[1]; + key[5] = src6_addr32[1]; + key[6] = dst6_addr32[0]; + key[7] = src6_addr32[0]; + key[8] = dst->ip6.sin6_port; + key[9] = src->ip6.sin6_port; + break; + } + default: + DEBUG(DEBUG_ERR, (__location__ " ERROR, unknown family passed :%u\n", src->sa.sa_family)); + return key; + } + + return key; +} + +/* + called when we get a read event on the raw socket + */ +static void capture_tcp_handler(struct tevent_context *ev, + struct tevent_fd *fde, + uint16_t flags, void *private_data) +{ + struct ctdb_kill_tcp *killtcp = talloc_get_type(private_data, struct ctdb_kill_tcp); + struct ctdb_killtcp_con *con; + ctdb_sock_addr src, dst; + uint32_t ack_seq, seq; + + if (!(flags & TEVENT_FD_READ)) { + return; + } + + if (ctdb_sys_read_tcp_packet(killtcp->capture_fd, + killtcp->private_data, + &src, &dst, + &ack_seq, &seq) != 0) { + /* probably a non-tcp ACK packet */ + return; + } + + /* check if we have this guy in our list of connections + to kill + */ + con = trbt_lookuparray32(killtcp->connections, + KILLTCP_KEYLEN, killtcp_key(&src, &dst)); + if (con == NULL) { + /* no this was some other packet we can just ignore */ + return; + } + + /* This one has been tickled ! + now reset him and remove him from the list. + */ + DEBUG(DEBUG_INFO, + ("sending a tcp reset to kill connection :%d -> %s:%d\n", + ntohs(con->dst_addr.ip.sin_port), + ctdb_sock_addr_to_string(con, &con->src_addr), + ntohs(con->src_addr.ip.sin_port))); + + ctdb_sys_send_tcp(&con->dst_addr, &con->src_addr, ack_seq, seq, 1); + talloc_free(con); +} + + +/* when traversing the list of all tcp connections to send tickle acks to + (so that we can capture the ack coming back and kill the connection + by a RST) + this callback is called for each connection we are currently trying to kill +*/ +static int tickle_connection_traverse(void *param, void *data) +{ + struct ctdb_killtcp_con *con = talloc_get_type(data, struct ctdb_killtcp_con); + + /* have tried too many times, just give up */ + if (con->count >= 5) { + /* can't delete in traverse: reparent to delete_cons */ + talloc_steal(param, con); + return 0; + } + + /* othervise, try tickling it again */ + con->count++; + ctdb_sys_send_tcp( + (ctdb_sock_addr *)&con->dst_addr, + (ctdb_sock_addr *)&con->src_addr, + 0, 0, 0); + return 0; +} + + +/* + called every second until all sentenced connections have been reset + */ +static void ctdb_tickle_sentenced_connections(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval t, void *private_data) +{ + struct ctdb_kill_tcp *killtcp = talloc_get_type(private_data, struct ctdb_kill_tcp); + void *delete_cons = talloc_new(NULL); + + /* loop over all connections sending tickle ACKs */ + trbt_traversearray32(killtcp->connections, KILLTCP_KEYLEN, tickle_connection_traverse, delete_cons); + + /* now we've finished traverse, it's safe to do deletion. */ + talloc_free(delete_cons); + + /* If there are no more connections to kill we can remove the + entire killtcp structure + */ + if ((killtcp->connections == NULL) || + (killtcp->connections->root == NULL)) { + talloc_free(killtcp); + return; + } + + /* try tickling them again in a seconds time + */ + tevent_add_timer(ev, killtcp, + tevent_timeval_current_ofs(1, 0), + ctdb_tickle_sentenced_connections, killtcp); +} + +/* nothing fancy here, just unconditionally replace any existing + connection structure with the new one. + + don't even free the old one if it did exist, that one is talloc_stolen + by the same node in the tree anyway and will be deleted when the new data + is deleted +*/ +static void *add_killtcp_callback(void *parm, void *data) +{ + return parm; +} + +/* Add a TCP socket to the list of connections we want to RST. The + * list is attached to *killtcp_arg. If this is NULL then allocate + * the structure. */ +static int ctdb_killtcp(struct tevent_context *ev, + TALLOC_CTX *mem_ctx, + const char *iface, + const ctdb_sock_addr *src, + const ctdb_sock_addr *dst, + struct ctdb_kill_tcp **killtcp_arg) +{ + struct ctdb_kill_tcp *killtcp; + struct ctdb_killtcp_con *con; + + if (killtcp_arg == NULL) { + DEBUG(DEBUG_ERR, (__location__ " killtcp_arg is NULL!\n")); + return -1; + } + + killtcp = *killtcp_arg; + + /* Allocate a new structure if necessary. The structure is + * only freed when mem_ctx is freed. */ + if (killtcp == NULL) { + killtcp = talloc_zero(mem_ctx, struct ctdb_kill_tcp); + if (killtcp == NULL) { + DEBUG(DEBUG_ERR, (__location__ " out of memory\n")); + return -1; + } + + killtcp->capture_fd = -1; + killtcp->connections = trbt_create(killtcp, 0); + *killtcp_arg = killtcp; + } + + /* create a structure that describes this connection we want to + RST and store it in killtcp->connections + */ + con = talloc(killtcp, struct ctdb_killtcp_con); + if (con == NULL) { + DEBUG(DEBUG_ERR, (__location__ " out of memory\n")); + return -1; + } + con->src_addr = *src; + con->dst_addr = *dst; + con->count = 0; + con->killtcp = killtcp; + + + trbt_insertarray32_callback(killtcp->connections, + KILLTCP_KEYLEN, + killtcp_key(&con->dst_addr, + &con->src_addr), + add_killtcp_callback, con); + + /* + If we don't have a socket to listen on yet we must create it + */ + if (killtcp->capture_fd == -1) { + killtcp->capture_fd = + ctdb_sys_open_capture_socket(iface, + &killtcp->private_data); + if (killtcp->capture_fd == -1) { + DEBUG(DEBUG_CRIT,(__location__ " Failed to open capturing " + "socket on iface '%s' for killtcp (%s)\n", + iface, strerror(errno))); + return -1; + } + } + + + if (killtcp->fde == NULL) { + killtcp->fde = tevent_add_fd(ev, killtcp, + killtcp->capture_fd, + TEVENT_FD_READ, + capture_tcp_handler, killtcp); + tevent_fd_set_auto_close(killtcp->fde); + + /* We also need to set up some events to tickle all these connections + until they are all reset + */ + tevent_add_timer(ev, killtcp, tevent_timeval_current_ofs(1, 0), + ctdb_tickle_sentenced_connections, killtcp); + } + + /* tickle him once now */ + ctdb_sys_send_tcp( + &con->dst_addr, + &con->src_addr, + 0, 0, 0); + + return 0; +} + static int ctdb_killtcp_destructor(struct ctdb_kill_tcp *killtcp) { bool *done = killtcp->destructor_data; diff --git a/ctdb/wscript b/ctdb/wscript index a73910b8193..41048fb8fc8 100755 --- a/ctdb/wscript +++ b/ctdb/wscript @@ -421,7 +421,7 @@ def build(bld): manpages='ctdb.1') bld.SAMBA_BINARY('ctdb_killtcp', - source='tools/ctdb_killtcp.c server/killtcp.c', + source='tools/ctdb_killtcp.c', deps='''ctdb-protocol ctdb-util ctdb-system samba-util replace''', install_path='${CTDB_HELPER_BINDIR}')