]> git.ipfire.org Git - thirdparty/samba.git/commitdiff
change the tickle list from one global list into an array per public
authorRonnie Sahlberg <sahlberg@ronnie>
Fri, 20 Jul 2007 00:06:41 +0000 (10:06 +1000)
committerRonnie Sahlberg <sahlberg@ronnie>
Fri, 20 Jul 2007 00:06:41 +0000 (10:06 +1000)
ip/node

once we have started sending all tickles for a specific ip   delete the
entire array   so that the tickles dont remain forever in the ctdb
server

add a control to send the full list of every tickle that is registered
for a particular public ip/node

(This used to be ctdb commit d0eee33e44d3f8e26debbec21d41e2cbdbb520e6)

ctdb/include/ctdb_private.h
ctdb/server/ctdb_control.c
ctdb/server/ctdb_takeover.c

index 58a146137397c679221a5f93b0d342bf27cf86bb..b65862f151bc1a62c2845ba747304e0281802820 100644 (file)
@@ -139,6 +139,9 @@ struct ctdb_node {
        /* the node number that has taken over this nodes public address, if any. 
           If not taken over, then set to -1 */
        int32_t takeover_vnn;
+
+       /* List of clients to tickle for this public address */
+       struct ctdb_tcp_array *tcp_array;
 };
 
 /*
@@ -305,7 +308,6 @@ struct ctdb_context {
        uint32_t recovery_master;
        struct ctdb_call_state *pending_calls;
        struct ctdb_takeover takeover;
-       struct ctdb_tcp_list *tcp_list;
        struct ctdb_client_ip *client_ip_list;
        bool do_setsched;
        void *saved_scheduler_param;
@@ -410,6 +412,7 @@ enum ctdb_controls {CTDB_CONTROL_PROCESS_EXISTS          = 0,
                    CTDB_CONTROL_MODIFY_FLAGS            = 52,
                    CTDB_CONTROL_GET_ALL_TUNABLES        = 53,
                    CTDB_CONTROL_KILL_TCP                = 54,
+                   CTDB_CONTROL_TCP_TICKLE_LIST         = 55,
 };
 
 /*
@@ -1003,11 +1006,12 @@ int ctdb_set_event_script(struct ctdb_context *ctdb, const char *script);
 int ctdb_takeover_run(struct ctdb_context *ctdb, struct ctdb_node_map *nodemap);
 
 int32_t ctdb_control_tcp_client(struct ctdb_context *ctdb, uint32_t client_id, 
-                               uint32_t srcnode, TDB_DATA indata);
+                               TDB_DATA indata);
 int32_t ctdb_control_tcp_add(struct ctdb_context *ctdb, TDB_DATA indata);
 int32_t ctdb_control_tcp_remove(struct ctdb_context *ctdb, TDB_DATA indata);
 int32_t ctdb_control_startup(struct ctdb_context *ctdb, uint32_t vnn);
 int32_t ctdb_control_kill_tcp(struct ctdb_context *ctdb, TDB_DATA indata);
+int32_t ctdb_control_tcp_tickle_list(struct ctdb_context *ctdb, TDB_DATA indata);
 
 void ctdb_takeover_client_destructor_hook(struct ctdb_client *client);
 int ctdb_event_script(struct ctdb_context *ctdb, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3);
index 6d170156572a31e181da55468002648a99e3bbc8..7b942352c30c2b55fb92cc4a71087c2f13c2ccff 100644 (file)
@@ -259,7 +259,7 @@ static int32_t ctdb_control_dispatch(struct ctdb_context *ctdb,
 
        case CTDB_CONTROL_TCP_CLIENT: 
                CHECK_CONTROL_DATA_SIZE(sizeof(struct ctdb_control_tcp));
-               return ctdb_control_tcp_client(ctdb, client_id, srcnode, indata);
+               return ctdb_control_tcp_client(ctdb, client_id, indata);
 
        case CTDB_CONTROL_STARTUP: 
                CHECK_CONTROL_DATA_SIZE(0);
@@ -290,6 +290,10 @@ static int32_t ctdb_control_dispatch(struct ctdb_context *ctdb,
                CHECK_CONTROL_DATA_SIZE(sizeof(struct ctdb_control_killtcp));
                return ctdb_control_kill_tcp(ctdb, indata);
 
+       case CTDB_CONTROL_TCP_TICKLE_LIST:
+               /* data size is verified in the called function */
+               return ctdb_control_tcp_tickle_list(ctdb, indata);
+
        default:
                DEBUG(0,(__location__ " Unknown CTDB control opcode %u\n", opcode));
                return -1;
index b1d4b190ee374e20146608f3929466c5fba59968..d89c3d082ff4daaf6d37b9b32451096e88051e87 100644 (file)
@@ -39,17 +39,37 @@ struct ctdb_takeover_arp {
        struct ctdb_tcp_list *tcp_list;
 };
 
+
+/*
+  a tcp connection description
+ */
+struct ctdb_tcp_connection {
+       struct sockaddr_in saddr;
+       struct sockaddr_in daddr;
+};
+
+/*
+  array of tcp connections
+ */
+struct ctdb_tcp_array {
+       uint32_t num;
+       struct ctdb_tcp_connection connections[1];
+};     
+
+struct ctdb_control_tcp_tickle_list {
+       int32_t vnn;
+       struct ctdb_tcp_array tickles;
+};
+
 /*
   lists of tcp endpoints
  */
 struct ctdb_tcp_list {
        struct ctdb_tcp_list *prev, *next;
        uint32_t vnn;
-       struct sockaddr_in saddr;
-       struct sockaddr_in daddr;
+       struct ctdb_tcp_connection connection;
 };
 
-
 /*
   list of clients to kill on IP release
  */
@@ -85,13 +105,14 @@ static void ctdb_control_send_arp(struct event_context *ev, struct timed_event *
 
        for (tcp=arp->tcp_list;tcp;tcp=tcp->next) {
                DEBUG(2,("sending tcp tickle ack for %u->%s:%u\n",
-                        (unsigned)ntohs(tcp->daddr.sin_port), 
-                        inet_ntoa(tcp->saddr.sin_addr),
-                        (unsigned)ntohs(tcp->saddr.sin_port)));
-               ret = ctdb_sys_send_tcp(s, &tcp->saddr, &tcp->daddr, 0, 0, 0);
+                        (unsigned)ntohs(tcp->connection.daddr.sin_port), 
+                        inet_ntoa(tcp->connection.saddr.sin_addr),
+                        (unsigned)ntohs(tcp->connection.saddr.sin_port)));
+               ret = ctdb_sys_send_tcp(s, &tcp->connection.saddr, 
+                                       &tcp->connection.daddr, 0, 0, 0);
                if (ret != 0) {
                        DEBUG(0,(__location__ " Failed to send tcp tickle ack for %s\n",
-                                inet_ntoa(tcp->saddr.sin_addr)));
+                                inet_ntoa(tcp->connection.saddr.sin_addr)));
                }
        }
 
@@ -111,6 +132,7 @@ static void ctdb_control_send_arp(struct event_context *ev, struct timed_event *
 struct takeover_callback_state {
        struct ctdb_req_control *c;
        struct sockaddr_in *sin;
+       int32_t takeover_vnn;
 };
 
 /*
@@ -123,7 +145,7 @@ static void takeover_ip_callback(struct ctdb_context *ctdb, int status,
                talloc_get_type(private_data, struct takeover_callback_state);
        struct ctdb_takeover_arp *arp;
        char *ip = inet_ntoa(state->sin->sin_addr);
-       struct ctdb_tcp_list *tcp;
+       struct ctdb_tcp_array *tcparray;
 
        ctdb_start_monitoring(ctdb);
 
@@ -146,15 +168,28 @@ static void takeover_ip_callback(struct ctdb_context *ctdb, int status,
        arp->ctdb = ctdb;
        arp->sin = *state->sin;
 
-       /* add all of the known tcp connections for this IP to the
-          list of tcp connections to send tickle acks for */
-       for (tcp=ctdb->tcp_list;tcp;tcp=tcp->next) {
-               if (state->sin->sin_addr.s_addr == tcp->daddr.sin_addr.s_addr) {
+       tcparray = ctdb->nodes[state->takeover_vnn]->tcp_array;
+       if (tcparray) {
+               int i;
+       
+               /* add all of the known tcp connections for this IP to the
+                  list of tcp connections to send tickle acks for */
+               for (i=0;i<tcparray->num;i++) {
                        struct ctdb_tcp_list *t2 = talloc(arp, struct ctdb_tcp_list);
                        if (t2 == NULL) goto failed;
-                       *t2 = *tcp;
+                       t2->vnn = state->takeover_vnn;
+                       t2->connection.saddr = tcparray->connections[i].saddr;
+                       t2->connection.daddr = tcparray->connections[i].daddr;
                        DLIST_ADD(arp->tcp_list, t2);
                }
+
+               /* Delete the entire tickle array for this public address
+                  We have tickled them now so we dont need them
+                  anymore
+                */
+               talloc_free(tcparray);
+               ctdb->nodes[state->takeover_vnn]->tcp_array = NULL;
+                       
        }
 
        event_add_timed(arp->ctdb->ev, arp->ctdb->takeover.last_ctx, 
@@ -171,6 +206,26 @@ failed:
        return;
 }
 
+/*
+  Find the vnn of the node that has a public ip address
+  returns -1 if the address is not known as a public address
+ */
+static int32_t find_public_ip_vnn(struct ctdb_context *ctdb, char *ip)
+{
+       int32_t vnn = -1;
+       int i;
+
+       for (i=0;i<ctdb->num_nodes;i++) {
+               if (!strcmp(ip, ctdb->nodes[i]->public_address)) {
+                       vnn = i;
+                       break;
+               }
+       }
+
+       return vnn;
+}
+
+
 /*
   take over an ip address
  */
@@ -201,6 +256,12 @@ int32_t ctdb_control_takeover_ip(struct ctdb_context *ctdb,
        CTDB_NO_MEMORY(ctdb, state->sin);
        *state->sin = pip->sin;
 
+       state->takeover_vnn = find_public_ip_vnn(ctdb, ip);
+       if (state->takeover_vnn == -1) {
+               DEBUG(0,("Could not takover of IP %s. It is not a public address.\n", ip)); 
+               return -1;
+       }
+
        DEBUG(0,("Takover of IP %s/%u on interface %s\n", 
                 ip, ctdb->nodes[ctdb->vnn]->public_netmask_bits, 
                 ctdb->takeover.interface));
@@ -259,7 +320,7 @@ static void release_ip_callback(struct ctdb_context *ctdb, int status,
                talloc_get_type(private_data, struct takeover_callback_state);
        char *ip = inet_ntoa(state->sin->sin_addr);
        TDB_DATA data;
-       struct ctdb_tcp_list *tcp;
+       struct ctdb_tcp_array *tcparray;
 
        ctdb_start_monitoring(ctdb);
 
@@ -276,21 +337,25 @@ static void release_ip_callback(struct ctdb_context *ctdb, int status,
        
 
        /* tell other nodes about any tcp connections we were holding with this IP */
-       for (tcp=ctdb->tcp_list;tcp;tcp=tcp->next) {
-               if (tcp->vnn == ctdb->vnn && 
-                   state->sin->sin_addr.s_addr == tcp->daddr.sin_addr.s_addr) {
+       tcparray = ctdb->nodes[state->takeover_vnn]->tcp_array;
+       if (tcparray) {
+               int i;
+
+               for (i=0;i<tcparray->num;i++) {
                        struct ctdb_control_tcp_vnn t;
 
-                       t.vnn  = ctdb->vnn;
-                       t.src  = tcp->saddr;
-                       t.dest = tcp->daddr;
+                       t.vnn  = state->takeover_vnn;
+                       t.src  = tcparray->connections[i].saddr;
+                       t.dest = tcparray->connections[i].daddr;
 
                        data.dptr = (uint8_t *)&t;
                        data.dsize = sizeof(t);
 
-                       ctdb_daemon_send_control(ctdb, CTDB_BROADCAST_CONNECTED, 0, 
-                                                CTDB_CONTROL_TCP_ADD,
-                                                0, CTDB_CTRL_FLAG_NOREPLY, data, NULL, NULL);
+                       ctdb_daemon_send_control(ctdb,
+                                       CTDB_BROADCAST_CONNECTED, 0, 
+                                       CTDB_CONTROL_TCP_ADD, 0, 
+                                       CTDB_CTRL_FLAG_NOREPLY, data,
+                                       NULL, NULL);
                }
        }
 
@@ -299,7 +364,6 @@ static void release_ip_callback(struct ctdb_context *ctdb, int status,
        talloc_free(state);
 }
 
-
 /*
   release an ip address
  */
@@ -336,6 +400,12 @@ int32_t ctdb_control_release_ip(struct ctdb_context *ctdb,
        CTDB_NO_MEMORY(ctdb, state->sin);
        *state->sin = pip->sin;
 
+       state->takeover_vnn = find_public_ip_vnn(ctdb, ip);
+       if (state->takeover_vnn == -1) {
+               DEBUG(0,("Could not release IP %s. It is not a public address.\n", ip)); 
+               return -1;
+       }
+
        ctdb_stop_monitoring(ctdb);
 
        ret = ctdb_event_script_callback(ctdb, 
@@ -608,7 +678,7 @@ static int ctdb_client_ip_destructor(struct ctdb_client_ip *ip)
   called by a client to inform us of a TCP connection that it is managing
   that should tickled with an ACK when IP takeover is done
  */
-int32_t ctdb_control_tcp_client(struct ctdb_context *ctdb, uint32_t client_id, uint32_t vnn,
+int32_t ctdb_control_tcp_client(struct ctdb_context *ctdb, uint32_t client_id,
                                TDB_DATA indata)
 {
        struct ctdb_client *client = ctdb_reqid_find(ctdb, client_id, struct ctdb_client);
@@ -618,6 +688,8 @@ int32_t ctdb_control_tcp_client(struct ctdb_context *ctdb, uint32_t client_id, u
        int ret;
        TDB_DATA data;
        struct ctdb_client_ip *ip;
+       char *addr;
+       int32_t takeover_vnn;
 
        ip = talloc(client, struct ctdb_client_ip);
        CTDB_NO_MEMORY(ctdb, ip);
@@ -631,13 +703,21 @@ int32_t ctdb_control_tcp_client(struct ctdb_context *ctdb, uint32_t client_id, u
        tcp = talloc(client, struct ctdb_tcp_list);
        CTDB_NO_MEMORY(ctdb, tcp);
 
-       tcp->vnn   = vnn;
-       tcp->saddr = p->src;
-       tcp->daddr = p->dest;
+       addr = inet_ntoa(p->src.sin_addr);
+
+       takeover_vnn = find_public_ip_vnn(ctdb, addr);
+       if (takeover_vnn == -1) {
+               DEBUG(0,("Could not add client IP %s. It is not a public address.\n", addr)); 
+               return -1;
+       }
+
+       tcp->vnn   = takeover_vnn;
+       tcp->connection.saddr = p->src;
+       tcp->connection.daddr = p->dest;
 
        DLIST_ADD(client->tcp_list, tcp);
 
-       t.vnn  = vnn;
+       t.vnn  = takeover_vnn;
        t.src  = p->src;
        t.dest = p->dest;
 
@@ -646,7 +726,7 @@ int32_t ctdb_control_tcp_client(struct ctdb_context *ctdb, uint32_t client_id, u
 
        DEBUG(2,("registered tcp client for %u->%s:%u\n",
                 (unsigned)ntohs(p->dest.sin_port), 
-                inet_ntoa(p->src.sin_addr),
+                addr,
                 (unsigned)ntohs(p->src.sin_port)));
 
        /* tell all nodes about this tcp connection */
@@ -674,15 +754,20 @@ static bool same_sockaddr_in(struct sockaddr_in *in1, struct sockaddr_in *in2)
 /*
   find a tcp address on a list
  */
-static struct ctdb_tcp_list *ctdb_tcp_find(struct ctdb_tcp_list *list
-                                          struct ctdb_tcp_list *tcp)
+static struct ctdb_tcp_connection *ctdb_tcp_find(struct ctdb_tcp_array *array
+                                          struct ctdb_tcp_connection *tcp)
 {
-       while (list) {
-               if (same_sockaddr_in(&list->saddr, &tcp->saddr) &&
-                   same_sockaddr_in(&list->daddr, &tcp->daddr)) {
-                       return list;
+       int i;
+
+       if (array == NULL) {
+               return NULL;
+       }
+
+       for (i=0;i<array->num;i++) {
+               if (same_sockaddr_in(&array->connections[i].saddr, &tcp->saddr) &&
+                   same_sockaddr_in(&array->connections[i].daddr, &tcp->daddr)) {
+                       return &array->connections[i];
                }
-               list = list->next;
        }
        return NULL;
 }
@@ -695,26 +780,56 @@ static struct ctdb_tcp_list *ctdb_tcp_find(struct ctdb_tcp_list *list,
 int32_t ctdb_control_tcp_add(struct ctdb_context *ctdb, TDB_DATA indata)
 {
        struct ctdb_control_tcp_vnn *p = (struct ctdb_control_tcp_vnn *)indata.dptr;
-       struct ctdb_tcp_list *tcp;
+       struct ctdb_tcp_array *tcparray;
+       struct ctdb_tcp_connection tcp;
 
-       tcp = talloc(ctdb, struct ctdb_tcp_list);
-       CTDB_NO_MEMORY(ctdb, tcp);
 
-       tcp->vnn   = p->vnn;
-       tcp->saddr = p->src;
-       tcp->daddr = p->dest;
+       tcparray = ctdb->nodes[p->vnn]->tcp_array;
+
+       /* If this is the first tickle */
+       if (tcparray == NULL) {
+               tcparray = talloc_size(ctdb->nodes, 
+                       offsetof(struct ctdb_tcp_array, connections) +
+                       sizeof(struct ctdb_tcp_connection) * 1);
+               CTDB_NO_MEMORY(ctdb, tcparray);
+               ctdb->nodes[p->vnn]->tcp_array = tcparray;
+
+               tcparray->num = 0;
+               tcparray->connections[tcparray->num].saddr = p->src;
+               tcparray->connections[tcparray->num].daddr = p->dest;
+               tcparray->num++;
+               return 0;
+       }
+
 
-       if (NULL == ctdb_tcp_find(ctdb->tcp_list, tcp)) {
-               DLIST_ADD(ctdb->tcp_list, tcp);
-               DEBUG(2,("Added tickle info for %s:%u from vnn %u\n",
-                        inet_ntoa(tcp->daddr.sin_addr), ntohs(tcp->daddr.sin_port),
-                        tcp->vnn));
-       } else {
+       /* Do we already have this tickle ?*/
+       tcp.saddr = p->src;
+       tcp.daddr = p->dest;
+       if (ctdb_tcp_find(ctdb->nodes[p->vnn]->tcp_array, &tcp) != NULL) {
                DEBUG(4,("Already had tickle info for %s:%u from vnn %u\n",
-                        inet_ntoa(tcp->daddr.sin_addr), ntohs(tcp->daddr.sin_port),
-                        tcp->vnn));
+                        inet_ntoa(tcp.daddr.sin_addr),
+                        ntohs(tcp.daddr.sin_port),
+                        p->vnn));
+               return 0;
        }
 
+       /* A new tickle, we must add it to the array */
+       tcparray = talloc_realloc(ctdb->nodes, tcparray, 
+                               struct ctdb_tcp_array,
+                               offsetof(struct ctdb_tcp_array, connections) +
+                               sizeof(struct ctdb_tcp_connection) * (tcparray->num+1) );
+       CTDB_NO_MEMORY(ctdb, tcparray);
+
+       ctdb->nodes[p->vnn]->tcp_array = tcparray;
+       tcparray->connections[tcparray->num].saddr = p->src;
+       tcparray->connections[tcparray->num].daddr = p->dest;
+       tcparray->num++;
+                               
+       DEBUG(2,("Added tickle info for %s:%u from vnn %u\n",
+                inet_ntoa(tcp.daddr.sin_addr),
+                ntohs(tcp.daddr.sin_port),
+                p->vnn));
+
        return 0;
 }
 
@@ -727,56 +842,73 @@ int32_t ctdb_control_tcp_add(struct ctdb_context *ctdb, TDB_DATA indata)
 int32_t ctdb_control_tcp_remove(struct ctdb_context *ctdb, TDB_DATA indata)
 {
        struct ctdb_control_tcp_vnn *p = (struct ctdb_control_tcp_vnn *)indata.dptr;
-       struct ctdb_tcp_list t, *tcp;
-
-       t.vnn   = p->vnn;
-       t.saddr = p->src;
-       t.daddr = p->dest;
-
-       tcp = ctdb_tcp_find(ctdb->tcp_list, &t);
-       if (tcp) {
-               DEBUG(2,("Removed tickle info for %s:%u from vnn %u\n",
-                        inet_ntoa(tcp->daddr.sin_addr), ntohs(tcp->daddr.sin_port),
-                        tcp->vnn));
-               DLIST_REMOVE(ctdb->tcp_list, tcp);
-               talloc_free(tcp);
+       struct ctdb_tcp_array *tcparray;
+       struct ctdb_tcp_connection tcp, *tcpp;
+
+       tcparray = ctdb->nodes[p->vnn]->tcp_array;
+
+       /* if the array is empty we cant remove it
+          and we dont need to do anything
+        */
+       if (tcparray == NULL) {
+               DEBUG(2,("Trying to remove tickle that doesnt exist (array is empty)  %s:%u from vnn %u\n",
+                        inet_ntoa(p->dest.sin_addr),
+                        ntohs(p->dest.sin_port),
+                        p->vnn));
+               return 0;
        }
 
+
+       /* See if we know this connection
+          if we dont know this connection  then we dont need to do anything
+        */
+       tcp.saddr = p->src;
+       tcp.daddr = p->dest;
+       tcpp = ctdb_tcp_find(ctdb->nodes[p->vnn]->tcp_array, &tcp);
+       if (tcpp == NULL) {
+               DEBUG(2,("Trying to remove tickle that doesnt exist  %s:%u from vnn %u\n",
+                        inet_ntoa(p->dest.sin_addr),
+                        ntohs(p->dest.sin_port),
+                        p->vnn));
+
+               return 0;
+       }
+
+
+
+       /* We need to remove this entry from the array.
+           Instead of allocating a new array and copying data to it
+          we cheat and just copy the last entry in the existing array
+          to the entry that is to be removed and just shring the 
+          ->num field
+        */
+       tcpp->saddr = tcparray->connections[tcparray->num-1].saddr;
+       tcpp->daddr = tcparray->connections[tcparray->num-1].daddr;
+       tcparray->num--;
+
+       /* If we deleted the last entry we also need to remove the entire array
+        */
+       if (tcparray->num == 0) {
+               talloc_free(tcparray);
+               ctdb->nodes[p->vnn]->tcp_array = NULL;
+       }               
+
+       DEBUG(2,("Removed tickle info for %s:%u from vnn %u\n",
+                inet_ntoa(p->src.sin_addr),
+                ntohs(p->src.sin_port),
+                p->vnn));
+
        return 0;
 }
 
 
 /*
-  called when a daemon restarts - wipes all tcp entries from that vnn
+  called when a daemon restarts - send all tickes for all public addresses
+  we are serving immediately to the new node.
  */
 int32_t ctdb_control_startup(struct ctdb_context *ctdb, uint32_t vnn)
 {
-       struct ctdb_tcp_list *tcp, *next;       
-       for (tcp=ctdb->tcp_list;tcp;tcp=next) {
-               next = tcp->next;
-               if (tcp->vnn == vnn) {
-                       DLIST_REMOVE(ctdb->tcp_list, tcp);
-                       talloc_free(tcp);
-               }
-
-               /* and tell the new guy about any that he should have
-                  from us */
-               if (tcp->vnn == ctdb->vnn) {
-                       struct ctdb_control_tcp_vnn t;
-                       TDB_DATA data;
-
-                       t.vnn  = tcp->vnn;
-                       t.src  = tcp->saddr;
-                       t.dest = tcp->daddr;
-
-                       data.dptr = (uint8_t *)&t;
-                       data.dsize = sizeof(t);
-
-                       ctdb_daemon_send_control(ctdb, vnn, 0, 
-                                                CTDB_CONTROL_TCP_ADD,
-                                                0, CTDB_CTRL_FLAG_NOREPLY, data, NULL, NULL);
-               }
-       }
+/*XXX here we should send all tickes we are serving to the new node */
        return 0;
 }
 
@@ -792,9 +924,10 @@ void ctdb_takeover_client_destructor_hook(struct ctdb_client *client)
                struct ctdb_control_tcp_vnn p;
                struct ctdb_tcp_list *tcp = client->tcp_list;
                DLIST_REMOVE(client->tcp_list, tcp);
-               p.vnn = tcp->vnn;
-               p.src = tcp->saddr;
-               p.dest = tcp->daddr;
+
+               p.vnn  = tcp->vnn;
+               p.src  = tcp->connection.saddr;
+               p.dest = tcp->connection.daddr;
                data.dptr = (uint8_t *)&p;
                data.dsize = sizeof(p);
                if (ctdb_sys_have_ip(inet_ntoa(p.dest.sin_addr))) {
@@ -1092,3 +1225,63 @@ int32_t ctdb_control_kill_tcp(struct ctdb_context *ctdb, TDB_DATA indata)
 
        return 0;
 }
+
+/*
+  called by a daemon to inform us of the entire list of TCP tickles for
+  a particular public address.
+  this control should only be sent by the node that is currently serving
+  that public address.
+ */
+int32_t ctdb_control_tcp_tickle_list(struct ctdb_context *ctdb, TDB_DATA indata)
+{
+       struct ctdb_control_tcp_tickle_list *list = (struct ctdb_control_tcp_tickle_list *)indata.dptr;
+       struct ctdb_tcp_array *tcparray;
+       int size;
+
+
+       /* We must at least have tickles.num or else we cant verify the size
+          of the received data blob
+        */
+       if (indata.dsize < offsetof(struct ctdb_control_tcp_tickle_list, 
+                                       tickles.connections)) {
+               DEBUG(0,("Bad indata in ctdb_control_tcp_tickle_list. Not enough data for the tickle.num field\n"));
+               return -1;
+       }
+
+       /* verify that the size of data matches what we expect */
+       if (indata.dsize < offsetof(struct ctdb_control_tcp_tickle_list, 
+                               tickles.connections)
+                        + sizeof(struct ctdb_tcp_connection)
+                                * list->tickles.num) {
+               DEBUG(0,("Bad indata in ctdb_control_tcp_tickle_list\n"));
+               return -1;
+       }       
+
+       /* Make sure the vnn looks sane */
+       if ( (list->vnn < 0) || (list->vnn >= ctdb->num_nodes) ) {
+               DEBUG(0,("Bad indata in ctdb_control_tcp_tickle_list. Invalid vnn:%d\n", list->vnn));
+               return -1;
+       }
+
+
+       /* remove any old ticklelist we might have */
+       tcparray = ctdb->nodes[list->vnn]->tcp_array;
+       if (tcparray) {
+               talloc_free(tcparray);
+               ctdb->nodes[list->vnn]->tcp_array = NULL;
+       }
+
+       /* allocate a new tcparray and copy all data that was sent to us */
+       size = offsetof(struct ctdb_tcp_array, connections) +
+                       sizeof(struct ctdb_tcp_connection) * list->tickles.num;
+
+       tcparray = talloc_size(ctdb->nodes, size);
+       CTDB_NO_MEMORY(ctdb, tcparray);
+
+       memcpy(tcparray, &list->tickles, size);
+
+       /* We now have a new fresh tickle list array for this vnn */
+       ctdb->nodes[list->vnn]->tcp_array = tcparray;
+       
+       return 0;
+}