mirror of
https://github.com/samba-team/samba.git
synced 2025-03-27 22:50:26 +03:00
first cut at a better and more scalable socketkiller
that can kill multiple connections asynchronously using one listening socket (This used to be ctdb commit 22bb44f3d745aa354becd75d30774992f6c40b3a)
This commit is contained in:
parent
0c44e0ad46
commit
aa080f66d9
@ -383,3 +383,99 @@ int ctdb_sys_kill_tcp(struct event_context *ev,
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* This function is used to open a raw socket to capture from
|
||||
*/
|
||||
int ctdb_sys_open_capture_socket(void)
|
||||
{
|
||||
int s;
|
||||
|
||||
/* Open a socket to capture all traffic */
|
||||
s=socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
|
||||
if (s == -1){
|
||||
DEBUG(0,(__location__ " failed to open raw socket\n"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
int sys_read_tcp_packet(struct ctdb_kill_tcp *killtcp)
|
||||
{
|
||||
int ret;
|
||||
#define RCVPKTSIZE 100
|
||||
char pkt[RCVPKTSIZE];
|
||||
struct ether_header *eth;
|
||||
struct iphdr *ip;
|
||||
struct tcphdr *tcp;
|
||||
struct ctdb_killtcp_connection *conn;
|
||||
|
||||
ret = recv(killtcp->fd, pkt, RCVPKTSIZE, MSG_TRUNC);
|
||||
if (ret < sizeof(*eth)+sizeof(*ip)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Ethernet */
|
||||
eth = (struct ether_header *)pkt;
|
||||
/* We only want IP packets */
|
||||
if (ntohs(eth->ether_type) != ETHERTYPE_IP) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* IP */
|
||||
ip = (struct iphdr *)(eth+1);
|
||||
|
||||
/* We only want IPv4 packets */
|
||||
if (ip->version != 4) {
|
||||
return -1;
|
||||
}
|
||||
/* Dont look at fragments */
|
||||
if ((ntohs(ip->frag_off)&0x1fff) != 0) {
|
||||
return -1;
|
||||
}
|
||||
/* we only want TCP */
|
||||
if (ip->protocol != IPPROTO_TCP) {
|
||||
return -1;
|
||||
}
|
||||
/* make sure its not a short packet */
|
||||
if (offsetof(struct tcphdr, ack_seq) + 4 +
|
||||
(ip->ihl*4) + sizeof(*eth) > ret) {
|
||||
return -1;
|
||||
}
|
||||
/* TCP */
|
||||
tcp = (struct tcphdr *)((ip->ihl*4) + (char *)ip);
|
||||
|
||||
|
||||
/* loop over all connections and see if we find one that matches */
|
||||
for(conn = killtcp->connections; conn; conn = conn->next) {
|
||||
/* We only want packets sent from a guy we have tickled */
|
||||
if (ip->saddr != conn->dst.sin_addr.s_addr) {
|
||||
continue;
|
||||
}
|
||||
/* We only want packets sent to us */
|
||||
if (ip->daddr != conn->src.sin_addr.s_addr) {
|
||||
continue;
|
||||
}
|
||||
/* We only want replies from a port we tickled */
|
||||
if (tcp->source != conn->dst.sin_port) {
|
||||
continue;
|
||||
}
|
||||
if (tcp->dest != conn->src.sin_port) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* This one has been tickled !
|
||||
now reset him and remove him from the list.
|
||||
*/
|
||||
ctdb_sys_send_tcp(&conn->dst, &conn->src, tcp->ack_seq, tcp->seq, 1);
|
||||
talloc_free(conn);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
@ -307,6 +307,7 @@ struct ctdb_context {
|
||||
struct ctdb_takeover takeover;
|
||||
struct ctdb_tcp_list *tcp_list;
|
||||
struct ctdb_client_ip *client_ip_list;
|
||||
struct ctdb_kill_tcp *killtcp;
|
||||
};
|
||||
|
||||
struct ctdb_db_context {
|
||||
@ -1032,11 +1033,29 @@ void ctdb_start_freeze(struct ctdb_context *ctdb);
|
||||
|
||||
bool parse_ip_port(const char *s, struct sockaddr_in *ip);
|
||||
|
||||
int ctdb_kill_tcp_callback(struct ctdb_context *ctdb,
|
||||
struct timeval timeout,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
void (*callback)(struct ctdb_context *, int, struct sockaddr_in *, struct sockaddr_in *),
|
||||
struct sockaddr_in *dst,
|
||||
struct sockaddr_in *src);
|
||||
|
||||
/*
|
||||
list of tcp connections to kill
|
||||
*/
|
||||
struct ctdb_killtcp_connection {
|
||||
struct ctdb_killtcp_connection *prev, *next;
|
||||
struct ctdb_context *ctdb;
|
||||
struct sockaddr_in src;
|
||||
struct sockaddr_in dst;
|
||||
int count;
|
||||
};
|
||||
|
||||
/* structure containing the listening socket and the list of tcp connections
|
||||
that the ctdb daemon is to kill
|
||||
*/
|
||||
struct ctdb_kill_tcp {
|
||||
struct ctdb_context *ctdb;
|
||||
int fd;
|
||||
struct fd_event *fde;
|
||||
struct ctdb_killtcp_connection *connections;
|
||||
};
|
||||
int ctdb_sys_open_capture_socket(void);
|
||||
int killtcp_add_connection(struct ctdb_context *ctdb, struct sockaddr_in *src, struct sockaddr_in *dst);
|
||||
int sys_read_tcp_packet(struct ctdb_kill_tcp *killtcp);
|
||||
|
||||
#endif
|
||||
|
@ -823,126 +823,133 @@ int32_t ctdb_control_get_public_ips(struct ctdb_context *ctdb, struct ctdb_req_c
|
||||
|
||||
|
||||
|
||||
static void capture_tcp_handler(struct event_context *ev, struct fd_event *fde,
|
||||
uint16_t flags, void *private_data)
|
||||
{
|
||||
struct ctdb_kill_tcp *killtcp = talloc_get_type(private_data, struct ctdb_kill_tcp);
|
||||
|
||||
struct ctdb_kill_tcp_state {
|
||||
struct ctdb_context *ctdb;
|
||||
pid_t child;
|
||||
void (*callback)(struct ctdb_context *, int, struct sockaddr_in *, struct sockaddr_in *);
|
||||
int fd[2];
|
||||
/* in case we need them in the callback */
|
||||
struct sockaddr_in dst;
|
||||
struct sockaddr_in src;
|
||||
};
|
||||
if (flags & EVENT_FD_READ) {
|
||||
sys_read_tcp_packet(killtcp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* called every second until all sentenced connections have been reset
|
||||
*/
|
||||
static void ctdb_tickle_sentenced_connections(struct event_context *ev, struct timed_event *te,
|
||||
struct timeval t, void *private_data)
|
||||
{
|
||||
struct ctdb_kill_tcp *killtcp = talloc_get_type(private_data, struct ctdb_kill_tcp);
|
||||
struct ctdb_killtcp_connection *conn, tmpcon;
|
||||
|
||||
|
||||
/* loop over all connections and see if we find one that matches */
|
||||
for(conn = killtcp->connections; conn; conn = conn->next) {
|
||||
conn->count++;
|
||||
if (conn->count > 5) {
|
||||
tmpcon.next=conn->next;
|
||||
talloc_free(conn);
|
||||
conn=&tmpcon;
|
||||
|
||||
continue;
|
||||
}
|
||||
ctdb_sys_send_tcp(&conn->dst, &conn->src, 0, 0, 0);
|
||||
}
|
||||
|
||||
/* If there are no more connections to kill we can remove the
|
||||
entire killtcp structure
|
||||
*/
|
||||
if (killtcp->connections == NULL) {
|
||||
talloc_free(killtcp);
|
||||
return;
|
||||
}
|
||||
|
||||
/* try tickling them again in a seconds time
|
||||
*/
|
||||
event_add_timed(killtcp->ctdb->ev, killtcp, timeval_current_ofs(1, 0),
|
||||
ctdb_tickle_sentenced_connections, killtcp);
|
||||
}
|
||||
|
||||
/*
|
||||
destroy a running kill tcp
|
||||
destroy the killtcp structure
|
||||
*/
|
||||
static int ctdb_kill_tcp_destructor(struct ctdb_kill_tcp_state *state)
|
||||
static int ctdb_killtcp_destructor(struct ctdb_kill_tcp *killtcp)
|
||||
{
|
||||
DEBUG(0,("kill tcp destructor\n"));
|
||||
kill(state->child, SIGKILL);
|
||||
waitpid(state->child, NULL, 0);
|
||||
close(killtcp->fd);
|
||||
killtcp->fd = -1;
|
||||
killtcp->ctdb->killtcp = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* called when the kill tcp child is finished */
|
||||
static void ctdb_kill_tcp_handler(struct event_context *ev, struct fd_event *fde,
|
||||
uint16_t flags, void *p)
|
||||
{
|
||||
struct ctdb_kill_tcp_state *state =
|
||||
talloc_get_type(p, struct ctdb_kill_tcp_state);
|
||||
int status = -1;
|
||||
void (*callback)(struct ctdb_context *, int, struct sockaddr_in *, struct sockaddr_in *) = state->callback;
|
||||
struct ctdb_context *ctdb = state->ctdb;
|
||||
|
||||
waitpid(state->child, &status, 0);
|
||||
if (status != -1) {
|
||||
status = WEXITSTATUS(status);
|
||||
}
|
||||
|
||||
DEBUG(0,("kill tcp handler status %d\n",status));
|
||||
talloc_set_destructor(state, NULL);
|
||||
talloc_free(state);
|
||||
if (callback) {
|
||||
callback(ctdb, status, &state->dst, &state->src);
|
||||
}
|
||||
}
|
||||
|
||||
/* called when kill tcp times out */
|
||||
static void ctdb_kill_tcp_timeout(struct event_context *ev, struct timed_event *te,
|
||||
struct timeval t, void *p)
|
||||
{
|
||||
struct ctdb_kill_tcp_state *state = talloc_get_type(p, struct ctdb_kill_tcp_state);
|
||||
void (*callback)(struct ctdb_context *, int, struct sockaddr_in *, struct sockaddr_in *) = state->callback;
|
||||
struct ctdb_context *ctdb = state->ctdb;
|
||||
|
||||
DEBUG(0,("kill tcp timed out\n"));
|
||||
talloc_free(state);
|
||||
if (callback) {
|
||||
callback(ctdb, -1, &state->dst, &state->src);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
run killtcp in the background calling the callback once it has
|
||||
finished
|
||||
destroy a killtcp connection structure
|
||||
*/
|
||||
int ctdb_kill_tcp_callback(struct ctdb_context *ctdb,
|
||||
struct timeval timeout,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
void (*callback)(struct ctdb_context *, int, struct sockaddr_in *, struct sockaddr_in *),
|
||||
struct sockaddr_in *dst,
|
||||
struct sockaddr_in *src)
|
||||
static int ctdb_killtcp_connection_destructor(struct ctdb_killtcp_connection *conn)
|
||||
{
|
||||
struct ctdb_kill_tcp_state *state;
|
||||
int ret;
|
||||
DLIST_REMOVE(conn->ctdb->killtcp->connections, conn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
state = talloc(mem_ctx, struct ctdb_kill_tcp_state);
|
||||
CTDB_NO_MEMORY(ctdb, state);
|
||||
|
||||
DEBUG(0,("kill tcp callback\n"));
|
||||
|
||||
state->ctdb = ctdb;
|
||||
state->callback = callback;
|
||||
state->src = *src;
|
||||
state->dst = *dst;
|
||||
int killtcp_add_connection(struct ctdb_context *ctdb, struct sockaddr_in *src, struct sockaddr_in *dst)
|
||||
{
|
||||
struct ctdb_kill_tcp *killtcp=ctdb->killtcp;;
|
||||
struct ctdb_killtcp_connection *conn;
|
||||
|
||||
ret = pipe(state->fd);
|
||||
if (ret != 0) {
|
||||
talloc_free(state);
|
||||
return -1;
|
||||
/* If this is the first connection to kill we must allocate
|
||||
a new structure
|
||||
*/
|
||||
if (killtcp == NULL) {
|
||||
killtcp = talloc(ctdb, struct ctdb_kill_tcp);
|
||||
CTDB_NO_MEMORY(ctdb, killtcp);
|
||||
|
||||
killtcp->ctdb = ctdb;
|
||||
killtcp->fd = -1;
|
||||
killtcp->connections = NULL;
|
||||
ctdb->killtcp = killtcp;
|
||||
talloc_set_destructor(killtcp, ctdb_killtcp_destructor);
|
||||
}
|
||||
|
||||
state->child = fork();
|
||||
/* If we dont have a socket to listen on yet we must create it
|
||||
*/
|
||||
if (killtcp->fd == -1) {
|
||||
killtcp->fd = ctdb_sys_open_capture_socket();
|
||||
if (killtcp->fd == -1) {
|
||||
DEBUG(0,(__location__ " Failed to open listening socket for killtcp\n"));
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (state->child == (pid_t)-1) {
|
||||
close(state->fd[0]);
|
||||
close(state->fd[1]);
|
||||
talloc_free(state);
|
||||
return -1;
|
||||
set_nonblocking(killtcp->fd);
|
||||
set_close_on_exec(killtcp->fd);
|
||||
}
|
||||
|
||||
if (state->child == 0) {
|
||||
close(state->fd[0]);
|
||||
ctdb_set_realtime(true);
|
||||
set_close_on_exec(state->fd[1]);
|
||||
ret = ctdb_sys_kill_tcp(ctdb->ev, dst, src);
|
||||
_exit(ret);
|
||||
}
|
||||
|
||||
talloc_set_destructor(state, ctdb_kill_tcp_destructor);
|
||||
|
||||
close(state->fd[1]);
|
||||
conn = talloc(killtcp, struct ctdb_killtcp_connection);
|
||||
CTDB_NO_MEMORY(ctdb, conn);
|
||||
conn->src = *src;
|
||||
conn->dst = *dst;
|
||||
conn->ctdb = ctdb;
|
||||
conn->count = 0;
|
||||
talloc_set_destructor(conn, ctdb_killtcp_connection_destructor);
|
||||
DLIST_ADD(killtcp->connections, conn);
|
||||
|
||||
|
||||
event_add_fd(ctdb->ev, state, state->fd[0], EVENT_FD_READ|EVENT_FD_AUTOCLOSE,
|
||||
ctdb_kill_tcp_handler, state);
|
||||
killtcp->fde = event_add_fd(ctdb->ev, killtcp, killtcp->fd,
|
||||
EVENT_FD_READ|EVENT_FD_AUTOCLOSE,
|
||||
capture_tcp_handler, killtcp);
|
||||
|
||||
|
||||
/* We also need to set up some events to tickle all these connections
|
||||
until they are all reset
|
||||
*/
|
||||
event_add_timed(ctdb->ev, killtcp, timeval_current_ofs(0, 0),
|
||||
ctdb_tickle_sentenced_connections, killtcp);
|
||||
|
||||
if (!timeval_is_zero(&timeout)) {
|
||||
event_add_timed(ctdb->ev, state, timeout, ctdb_kill_tcp_timeout, state);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
failed:
|
||||
talloc_free(ctdb->killtcp);
|
||||
ctdb->killtcp = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user