#define CTDB_NULL_FUNC 0xFF000001
#define CTDB_FETCH_FUNC 0xFF000002
+/*
+ a tcp connection description
+ */
+struct ctdb_tcp_connection {
+ struct sockaddr_in saddr;
+ struct sockaddr_in daddr;
+};
+
+/* the wire representation for a tcp tickle array */
+struct ctdb_tcp_wire_array {
+ uint32_t num;
+ struct ctdb_tcp_connection connections[1];
+};
+
+/* the list of tcp tickles used by get/set tcp tickle list */
+struct ctdb_control_tcp_tickle_list {
+ uint32_t vnn;
+ struct ctdb_tcp_wire_array tickles;
+};
+
+/*
+ array of tcp connections
+ */
+struct ctdb_tcp_array {
+ uint32_t num;
+ struct ctdb_tcp_connection *connections;
+};
+
+
/* all tunable variables go in here */
struct ctdb_tunable {
uint32_t max_redirect_count;
uint32_t election_timeout;
uint32_t takeover_timeout;
uint32_t monitor_interval;
+ uint32_t tickle_update_interval;
uint32_t script_timeout;
uint32_t recovery_grace_period;
uint32_t recovery_ban_period;
/* List of clients to tickle for this public address */
struct ctdb_tcp_array *tcp_array;
+
+ /* whether we need to update the other nodes with changes to our list
+ of connected clients */
+ bool tcp_update_needed;
};
/*
uint32_t recovery_mode;
uint32_t monitoring_mode;
TALLOC_CTX *monitor_context;
+ TALLOC_CTX *tickle_update_context;
struct ctdb_tunable tunable;
enum ctdb_freeze_mode freeze_mode;
struct ctdb_freeze_handle *freeze_handle;
CTDB_CONTROL_MODIFY_FLAGS = 52,
CTDB_CONTROL_GET_ALL_TUNABLES = 53,
CTDB_CONTROL_KILL_TCP = 54,
- CTDB_CONTROL_TCP_TICKLE_LIST = 55,
+ CTDB_CONTROL_GET_TCP_TICKLE_LIST = 55,
+ CTDB_CONTROL_SET_TCP_TICKLE_LIST = 56,
};
/*
void ctdb_stop_monitoring(struct ctdb_context *ctdb);
void ctdb_start_monitoring(struct ctdb_context *ctdb);
+void ctdb_start_tcp_tickle_update(struct ctdb_context *ctdb);
void ctdb_send_keepalive(struct ctdb_context *ctdb, uint32_t destnode);
void ctdb_daemon_cancel_controls(struct ctdb_context *ctdb, struct ctdb_node *node);
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);
+int32_t ctdb_control_get_tcp_tickle_list(struct ctdb_context *ctdb, TDB_DATA indata, TDB_DATA *outdata);
+int32_t ctdb_control_set_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);
uint32_t destnode,
struct ctdb_control_killtcp *killtcp);
+int ctdb_ctrl_get_tcp_tickles(struct ctdb_context *ctdb,
+ struct timeval timeout,
+ uint32_t destnode,
+ TALLOC_CTX *mem_ctx,
+ uint32_t vnn,
+ struct ctdb_control_tcp_tickle_list **list);
#endif
};
-/*
- 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 ctdb_tcp_connection connection;
};
struct takeover_callback_state {
struct ctdb_req_control *c;
struct sockaddr_in *sin;
- int32_t takeover_vnn;
+ struct ctdb_node *node;
};
/*
arp->ctdb = ctdb;
arp->sin = *state->sin;
- tcparray = ctdb->nodes[state->takeover_vnn]->tcp_array;
+ tcparray = state->node->tcp_array;
if (tcparray) {
int i;
for (i=0;i<tcparray->num;i++) {
struct ctdb_tcp_list *t2 = talloc(arp, struct ctdb_tcp_list);
if (t2 == NULL) goto failed;
- 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);
anymore
*/
talloc_free(tcparray);
- ctdb->nodes[state->takeover_vnn]->tcp_array = NULL;
+ state->node->tcp_array = NULL;
}
struct takeover_callback_state *state;
struct ctdb_public_ip *pip = (struct ctdb_public_ip *)indata.dptr;
char *ip = inet_ntoa(pip->sin.sin_addr);
-
+ struct ctdb_node *node = ctdb->nodes[pip->vnn];
/* update out node table */
- ctdb->nodes[pip->vnn]->takeover_vnn = pip->takeover_vnn;
+ node->takeover_vnn = pip->takeover_vnn;
/* if our kernel already has this IP, do nothing */
if (ctdb_sys_have_ip(ip)) {
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;
- }
+ state->node = node;
DEBUG(0,("Takover of IP %s/%u on interface %s\n",
ip, ctdb->nodes[ctdb->vnn]->public_netmask_bits,
talloc_get_type(private_data, struct takeover_callback_state);
char *ip = inet_ntoa(state->sin->sin_addr);
TDB_DATA data;
- struct ctdb_tcp_array *tcparray;
ctdb_start_monitoring(ctdb);
/* kill clients that have registered with this IP */
release_kill_clients(ctdb, state->sin->sin_addr);
-
- /* tell other nodes about any tcp connections we were holding with this IP */
- 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 = 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);
- }
- }
-
/* the control succeeded */
ctdb_request_control_reply(ctdb, state->c, NULL, 0, NULL);
talloc_free(state);
struct takeover_callback_state *state;
struct ctdb_public_ip *pip = (struct ctdb_public_ip *)indata.dptr;
char *ip = inet_ntoa(pip->sin.sin_addr);
+ struct ctdb_node *node = ctdb->nodes[pip->vnn];
/* update out node table */
ctdb->nodes[pip->vnn]->takeover_vnn = pip->takeover_vnn;
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;
- }
+ state->node = node;
ctdb_stop_monitoring(ctdb);
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));
+ DEBUG(3,("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;
ctdb->nodes[p->vnn]->tcp_array = tcparray;
tcparray->num = 0;
+ tcparray->connections = talloc_size(tcparray, sizeof(struct ctdb_tcp_connection));
+ CTDB_NO_MEMORY(ctdb, tcparray->connections);
+
tcparray->connections[tcparray->num].saddr = p->src;
tcparray->connections[tcparray->num].daddr = p->dest;
tcparray->num++;
}
/* 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);
+ tcparray->connections = talloc_realloc(tcparray, tcparray->connections,
+ struct ctdb_tcp_connection,
+ tcparray->num+1);
+ CTDB_NO_MEMORY(ctdb, tcparray->connections);
ctdb->nodes[p->vnn]->tcp_array = tcparray;
tcparray->connections[tcparray->num].saddr = p->src;
clients managing that should tickled with an ACK when IP takeover is
done
*/
-int32_t ctdb_control_tcp_remove(struct ctdb_context *ctdb, TDB_DATA indata)
+static void ctdb_remove_tcp_connection(struct ctdb_context *ctdb, struct ctdb_tcp_connection *conn)
{
- struct ctdb_control_tcp_vnn *p = (struct ctdb_control_tcp_vnn *)indata.dptr;
- struct ctdb_tcp_array *tcparray;
- struct ctdb_tcp_connection tcp, *tcpp;
+ struct ctdb_tcp_connection *tcpp;
+ int32_t vnn = find_public_ip_vnn(ctdb, inet_ntoa(conn->daddr.sin_addr));
+ struct ctdb_node *node;
- tcparray = ctdb->nodes[p->vnn]->tcp_array;
+ if (vnn == -1) {
+ DEBUG(0,(__location__ " unable to find public address %s\n", inet_ntoa(conn->daddr.sin_addr)));
+ return;
+ }
+
+ node = ctdb->nodes[vnn];
/* 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;
+ if (node->tcp_array == NULL) {
+ DEBUG(2,("Trying to remove tickle that doesnt exist (array is empty) %s:%u\n",
+ inet_ntoa(conn->daddr.sin_addr),
+ ntohs(conn->daddr.sin_port)));
+ return;
}
/* 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);
+ tcpp = ctdb_tcp_find(node->tcp_array, conn);
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;
+ DEBUG(2,("Trying to remove tickle that doesnt exist %s:%u\n",
+ inet_ntoa(conn->daddr.sin_addr),
+ ntohs(conn->daddr.sin_port)));
+ return;
}
-
/* 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--;
+ *tcpp = node->tcp_array->connections[node->tcp_array->num - 1];
+ node->tcp_array->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;
+ if (node->tcp_array->num == 0) {
+ talloc_free(node->tcp_array);
+ node->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));
+ node->tcp_update_needed = true;
- return 0;
+ DEBUG(2,("Removed tickle info for %s:%u\n",
+ inet_ntoa(conn->saddr.sin_addr),
+ ntohs(conn->saddr.sin_port)));
}
void ctdb_takeover_client_destructor_hook(struct ctdb_client *client)
{
while (client->tcp_list) {
- TDB_DATA data;
- 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->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))) {
- ctdb_daemon_send_control(client->ctdb, CTDB_BROADCAST_CONNECTED, 0,
- CTDB_CONTROL_TCP_REMOVE,
- 0, CTDB_CTRL_FLAG_NOREPLY, data, NULL, NULL);
- }
- talloc_free(tcp);
+ ctdb_remove_tcp_connection(client->ctdb, &tcp->connection);
}
}
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)
+int32_t ctdb_control_set_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"));
+ DEBUG(0,("Bad indata in ctdb_control_set_tcp_tickle_list. Not enough data for the tickle.num field\n"));
return -1;
}
tickles.connections)
+ sizeof(struct ctdb_tcp_connection)
* list->tickles.num) {
- DEBUG(0,("Bad indata in ctdb_control_tcp_tickle_list\n"));
+ DEBUG(0,("Bad indata in ctdb_control_set_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));
+ if (!ctdb_validate_vnn(ctdb, list->vnn)) {
+ DEBUG(0,("Bad indata in ctdb_control_set_tcp_tickle_list. Invalid vnn: %u\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;
+ talloc_free(ctdb->nodes[list->vnn]->tcp_array);
+ ctdb->nodes[list->vnn]->tcp_array = NULL;
- tcparray = talloc_size(ctdb->nodes, size);
+ tcparray = talloc(ctdb->nodes, struct ctdb_tcp_array);
CTDB_NO_MEMORY(ctdb, tcparray);
- memcpy(tcparray, &list->tickles, size);
+ tcparray->num = list->tickles.num;
+
+ tcparray->connections = talloc_array(tcparray, struct ctdb_tcp_connection, tcparray->num);
+ CTDB_NO_MEMORY(ctdb, tcparray->connections);
+
+ memcpy(tcparray->connections, &list->tickles.connections[0],
+ sizeof(struct ctdb_tcp_connection)*tcparray->num);
/* We now have a new fresh tickle list array for this vnn */
ctdb->nodes[list->vnn]->tcp_array = tcparray;
return 0;
}
+
+/*
+ called to return the full list of tickles for the puclic address associated
+ with the provided vnn
+ */
+int32_t ctdb_control_get_tcp_tickle_list(struct ctdb_context *ctdb, TDB_DATA indata, TDB_DATA *outdata)
+{
+ uint32_t vnn = *(uint32_t *)indata.dptr;
+ struct ctdb_control_tcp_tickle_list *list;
+ struct ctdb_tcp_array *tcparray;
+ int num;
+
+
+ /* Make sure the vnn looks sane */
+ if (!ctdb_validate_vnn(ctdb, vnn)) {
+ DEBUG(0,("Bad indata in ctdb_control_get_tcp_tickle_list. Invalid vnn: %u\n", vnn));
+ return -1;
+ }
+
+ tcparray = ctdb->nodes[vnn]->tcp_array;
+ if (tcparray) {
+ num = tcparray->num;
+ } else {
+ num = 0;
+ }
+
+ outdata->dsize = offsetof(struct ctdb_control_tcp_tickle_list,
+ tickles.connections)
+ + sizeof(struct ctdb_tcp_connection) * num;
+
+ outdata->dptr = talloc_size(outdata, outdata->dsize);
+ CTDB_NO_MEMORY(ctdb, outdata->dptr);
+ list = (struct ctdb_control_tcp_tickle_list *)outdata->dptr;
+
+ list->vnn = vnn;
+ list->tickles.num = num;
+ if (num) {
+ memcpy(&list->tickles.connections[0], tcparray->connections,
+ sizeof(struct ctdb_tcp_connection) * num);
+ }
+
+ return 0;
+}
+
+
+/*
+ set the list of all tcp tickles for a public address
+ */
+static int ctdb_ctrl_set_tcp_tickles(struct ctdb_context *ctdb,
+ struct timeval timeout, uint32_t destnode,
+ uint32_t vnn,
+ struct ctdb_tcp_array *tcparray)
+{
+ int ret, num;
+ TDB_DATA data;
+ struct ctdb_control_tcp_tickle_list *list;
+
+ if (tcparray) {
+ num = tcparray->num;
+ } else {
+ num = 0;
+ }
+
+ data.dsize = offsetof(struct ctdb_control_tcp_tickle_list,
+ tickles.connections) +
+ sizeof(struct ctdb_tcp_connection) * num;
+ data.dptr = talloc_size(ctdb, data.dsize);
+ CTDB_NO_MEMORY(ctdb, data.dptr);
+
+ list = (struct ctdb_control_tcp_tickle_list *)data.dptr;
+ list->vnn = vnn;
+ list->tickles.num = num;
+ if (tcparray) {
+ memcpy(&list->tickles.connections[0], tcparray->connections, sizeof(struct ctdb_tcp_connection) * num);
+ }
+
+ ret = ctdb_daemon_send_control(ctdb, CTDB_BROADCAST_CONNECTED, 0,
+ CTDB_CONTROL_SET_TCP_TICKLE_LIST,
+ 0, CTDB_CTRL_FLAG_NOREPLY, data, NULL, NULL);
+ if (ret != 0) {
+ DEBUG(0,(__location__ " ctdb_control for set tcp tickles failed\n"));
+ return -1;
+ }
+
+ talloc_free(data.dptr);
+
+ return ret;
+}
+
+
+/*
+ perform tickle updates if required
+ */
+static void ctdb_update_tcp_tickles(struct event_context *ev,
+ struct timed_event *te,
+ struct timeval t, void *private_data)
+{
+ struct ctdb_context *ctdb = talloc_get_type(private_data, struct ctdb_context);
+ int i, ret;
+
+ for (i=0;i<ctdb->num_nodes;i++) {
+ struct ctdb_node *node = ctdb->nodes[i];
+
+ /* we only send out updates for public addresses that we
+ have taken over
+ */
+ if (ctdb->vnn != node->takeover_vnn) {
+ continue;
+ }
+ /* We only send out the updates if we need to */
+ if (!node->tcp_update_needed) {
+ continue;
+ }
+
+ ret = ctdb_ctrl_set_tcp_tickles(ctdb,
+ TAKEOVER_TIMEOUT(),
+ CTDB_BROADCAST_CONNECTED,
+ node->takeover_vnn,
+ node->tcp_array);
+ if (ret != 0) {
+ DEBUG(0,("Failed to send the tickle update for public address %s\n", node->public_address));
+ }
+ }
+
+ event_add_timed(ctdb->ev, ctdb->tickle_update_context,
+ timeval_current_ofs(ctdb->tunable.tickle_update_interval, 0),
+ ctdb_update_tcp_tickles, ctdb);
+}
+
+
+/*
+ start periodic update of tcp tickles
+ */
+void ctdb_start_tcp_tickle_update(struct ctdb_context *ctdb)
+{
+ ctdb->tickle_update_context = talloc_new(ctdb);
+
+ event_add_timed(ctdb->ev, ctdb->tickle_update_context,
+ timeval_current_ofs(ctdb->tunable.tickle_update_interval, 0),
+ ctdb_update_tcp_tickles, ctdb);
+}