]> git.ipfire.org Git - thirdparty/samba.git/commitdiff
first cut at a better and more scalable socketkiller
authorRonnie Sahlberg <sahlberg@ronnie>
Wed, 11 Jul 2007 07:43:51 +0000 (17:43 +1000)
committerRonnie Sahlberg <sahlberg@ronnie>
Wed, 11 Jul 2007 07:43:51 +0000 (17:43 +1000)
that can kill multiple connections asynchronously using one listening
socket

(This used to be ctdb commit 22bb44f3d745aa354becd75d30774992f6c40b3a)

ctdb/common/system.c
ctdb/include/ctdb_private.h
ctdb/server/ctdb_takeover.c

index 1e536f5e8a4eef2c066d8debb4d7a3133be863e5..c05898e267e5de7677e3b5083565f1f0e99e4a07 100644 (file)
@@ -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;
+}
+
+
index e41035f5cbc5e828407f6bfa8fcfd17a852b575d..13e3323b66ea6d3c2777ace47cb009b80981ce62 100644 (file)
@@ -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
index 9b602ee04fc781c052ab78d3d4717c2f65d5875a..76460facbddeb45d90a2b86f9c762510a0c0e226 100644 (file)
@@ -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);
+       }
+}
 
-/*
-  destroy a running kill tcp
+
+/* called every second until all sentenced connections have been reset
  */
-static int ctdb_kill_tcp_destructor(struct ctdb_kill_tcp_state *state)
+static void ctdb_tickle_sentenced_connections(struct event_context *ev, struct timed_event *te, 
+                                     struct timeval t, void *private_data)
 {
-       DEBUG(0,("kill tcp destructor\n"));
-       kill(state->child, SIGKILL);
-       waitpid(state->child, NULL, 0);
-       return 0;
-}
+       struct ctdb_kill_tcp *killtcp = talloc_get_type(private_data, struct ctdb_kill_tcp);
+       struct ctdb_killtcp_connection *conn, tmpcon;
 
-/* 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);
+       /* 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);
        }
 
-       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);
+       /* 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);
 }
 
-/* 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)
+/*
+  destroy the killtcp structure
+ */
+static int ctdb_killtcp_destructor(struct ctdb_kill_tcp *killtcp)
 {
-       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;
+       close(killtcp->fd);
+       killtcp->fd = -1;
+       killtcp->ctdb->killtcp = NULL;
 
-       DEBUG(0,("kill tcp timed out\n"));
-       talloc_free(state);
-       if (callback) {
-               callback(ctdb, -1, &state->dst, &state->src);
-       }
+       return 0;
 }
 
 /*
-  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;
-
-       state = talloc(mem_ctx, struct ctdb_kill_tcp_state);
-       CTDB_NO_MEMORY(ctdb, state);
-
-       DEBUG(0,("kill tcp callback\n"));
+       DLIST_REMOVE(conn->ctdb->killtcp->connections, conn);
+       return 0;
+}
 
-       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);
+       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);
 
-       close(state->fd[1]);
 
+       killtcp->fde = event_add_fd(ctdb->ev, killtcp, killtcp->fd, 
+                               EVENT_FD_READ|EVENT_FD_AUTOCLOSE, 
+                               capture_tcp_handler, killtcp);
 
-       event_add_fd(ctdb->ev, state, state->fd[0], EVENT_FD_READ|EVENT_FD_AUTOCLOSE,
-                    ctdb_kill_tcp_handler, state);
 
-       if (!timeval_is_zero(&timeout)) {
-               event_add_timed(ctdb->ev, state, timeout, ctdb_kill_tcp_timeout, state);
-       }
+       /* 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);
 
-       return 0;
-}
 
+       return 0;
 
+failed:
+       talloc_free(ctdb->killtcp);
+       ctdb->killtcp = NULL;
+       return -1;
+}