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;
+}
+
+
+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;
+}