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
*/
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)));
}
}
struct takeover_callback_state {
struct ctdb_req_control *c;
struct sockaddr_in *sin;
+ int32_t takeover_vnn;
};
/*
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);
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,
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
*/
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));
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);
/* 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);
}
}
talloc_free(state);
}
-
/*
release an ip address
*/
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,
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);
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);
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;
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 */
/*
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;
}
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;
}
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;
}
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))) {
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;
+}