merged patch from tridge
authorRonnie Sahlberg <sahlberg@ronnie>
Mon, 10 Sep 2007 06:23:06 +0000 (16:23 +1000)
committerRonnie Sahlberg <sahlberg@ronnie>
Mon, 10 Sep 2007 06:23:06 +0000 (16:23 +1000)
1  2 
client/ctdb_client.c
common/ctdb_util.c
common/system_aix.c
common/system_linux.c
config/ctdb.sysconfig
include/ctdb_private.h
server/ctdb_serverids.c
server/ctdb_takeover.c

index 3a9b6c44203d6832fa13fda9722c9a850ebd8567,85c55971342cf3cdf04ca7f82427c732622f3e4b..6463c2d8fdb6a5298395921f0398bc41f7defd37
@@@ -2379,6 -2378,6 +2379,8 @@@ int ctdb_ctrl_unregister_server_id(stru
  
  /*
    check if a server id exists
++
++  if a server id does exist, return *status == 1, otherwise *status == 0
   */
  int ctdb_ctrl_check_server_id(struct ctdb_context *ctdb, 
                      struct timeval timeout, 
index 54b1e4e7ff5f68887b7f1c169533401b253cf651,54b1e4e7ff5f68887b7f1c169533401b253cf651..dc8128aa20fb62d1d902c8b05bd1622b256aaa31
@@@ -248,18 -248,18 +248,15 @@@ void set_close_on_exec(int fd
  
  
  /*
--  parse a ip:port pair
++  parse a ip:num pair with the given separator
   */
--bool parse_ip_port(const char *s, struct sockaddr_in *ip)
++static bool parse_ip_num(const char *s, struct in_addr *addr, unsigned *num, const char sep)
  {
        const char *p;
        char *endp = NULL;
--      unsigned port;
        char buf[16];
  
--      ip->sin_family = AF_INET;
--
--      p = strchr(s, ':');
++      p = strchr(s, sep);
        if (p == NULL) {
                return false;
        }
                return false;
        }
  
--      port = strtoul(p+1, &endp, 10);
++      *num = strtoul(p+1, &endp, 10);
        if (endp == NULL || *endp != 0) {
                /* trailing garbage */
                return false;
        }
--      ip->sin_port = htons(port);
  
        strlcpy(buf, s, 1+p-s);
  
--      if (inet_aton(buf, &ip->sin_addr) == 0) {
++      if (inet_aton(buf, addr) == 0) {
                return false;
        }
  
        return true;
  }
++
++
++/*
++  parse a ip:port pair
++ */
++bool parse_ip_port(const char *s, struct sockaddr_in *ip)
++{
++      unsigned port;
++      if (!parse_ip_num(s, &ip->sin_addr, &port, ':')) {
++              return false;
++      }
++      ip->sin_family = AF_INET;
++      ip->sin_port   = htons(port);
++      return true;
++}
++
++/*
++  parse a ip/mask pair
++ */
++bool parse_ip_mask(const char *s, struct sockaddr_in *ip, unsigned *mask)
++{
++      if (!parse_ip_num(s, &ip->sin_addr, mask, '/')) {
++              return false;
++      }
++      if (*mask > 32) {
++              return false;
++      }
++      ip->sin_family = AF_INET;
++      ip->sin_port   = 0;
++      return true;
++}
++
++/*
++  compare two sockaddr_in structures - matching only on IP
++ */
++bool ctdb_same_ip(const struct sockaddr_in *ip1, const struct sockaddr_in *ip2)
++{
++      return ip1->sin_family == ip2->sin_family &&
++              ip1->sin_addr.s_addr == ip2->sin_addr.s_addr;
++}
++
++/*
++  compare two sockaddr_in structures
++ */
++bool ctdb_same_sockaddr(const struct sockaddr_in *ip1, const struct sockaddr_in *ip2)
++{
++      return ctdb_same_ip(ip1, ip2) && ip1->sin_port == ip2->sin_port;
++}
index 0587e83f03289d1e79727a4138ab4479e773855b,872c4cbb73b845f13c11c5454f8f270f30fbe734..5d5af10336244808b1bbce3060e85c2d638ee96b
@@@ -165,24 -165,20 +165,21 @@@ int ctdb_sys_send_tcp(int s
    we try to bind to it, and if that fails then we don't have that IP
    on an interface
   */
- bool ctdb_sys_have_ip(const char *ip, bool *is_loopback, TALLOC_CTX *mem_ctx, char **ifname)
 -bool ctdb_sys_have_ip(const char *ip)
++bool ctdb_sys_have_ip(struct sockaddr_in ip, bool *is_loopback, TALLOC_CTX *mem_ctx, char **ifname)
  {
--      struct sockaddr_in sin;
        int s;
        int ret;
++      
 +      if (is_loopback) {
 +              DEBUG(0,(__location__ " NOT IMPLEMENTED YET: ctdb_sys_have_ip() does not yet support is_loopback on AIX. This needs to be implemented similar to system_linux.c\n"));
 +      }
  
--      sin.sin_port = 0;
--      inet_aton(ip, &sin.sin_addr);
--      sin.sin_family = AF_INET;
++      ip.sin_port = 0;
        s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
        if (s == -1) {
                return false;
        }
--      ret = bind(s, (struct sockaddr *)&sin, sizeof(sin));
++      ret = bind(s, (struct sockaddr *)&ip, sizeof(ip));
        close(s);
        return ret == 0;
  }
index fc94bd14eefeb3fca5e776af1ce319f263977e92,ba2c4dfc83f67e89c9c61e4679b6b4b6339302b1..82a6575bab333198a2fbe042cfd2bb1b73045757
@@@ -241,103 -241,21 +241,100 @@@ int ctdb_sys_send_tcp(int s
  
    we try to bind to it, and if that fails then we don't have that IP
    on an interface
 +  if is_loopback is specified it will also return whether the ip address
 +  is attached to the loopback interface or not
 +
 +  ifname, if non-NULL, will return the name of the interface this ip is tied to
   */
- bool ctdb_sys_have_ip(const char *ip, bool *is_loopback, TALLOC_CTX *mem_ctx, char **ifname)
 -bool ctdb_sys_have_ip(const char *ip)
++bool ctdb_sys_have_ip(struct sockaddr_in ip, bool *is_loopback, TALLOC_CTX *mem_ctx, char **ifname)
  {
--      struct sockaddr_in sin;
 -      int s;
 +      struct ifreq *ifr = NULL;
 +      struct ifconf ifc;
 +      int s, i, num_ifs;
        int ret;
  
 -      sin.sin_port = 0;
 -      inet_aton(ip, &sin.sin_addr);
 -      sin.sin_family = AF_INET;
 +      if (is_loopback) {
 +              *is_loopback = false;
 +      }
 +      if (*ifname) {
 +              *ifname = NULL;
 +      }
 +      
-       sin.sin_port = 0;
-       inet_aton(ip, &sin.sin_addr);
-       sin.sin_family = AF_INET;
++      ip.sin_port = 0;
        s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
        if (s == -1) {
                return false;
        }
--      ret = bind(s, (struct sockaddr *)&sin, sizeof(sin));
++      ret = bind(s, (struct sockaddr *)&ip, sizeof(ip));
 +      if (ret) {
 +              goto finished;
 +      }
 +
 +
 +      /* find out how much space we need to store the interface details */
 +      ifc.ifc_len = 0;
 +      ifc.ifc_req = NULL;
 +      ret = ioctl(s, SIOCGIFCONF, &ifc);
 +      if (ret) {
 +              DEBUG(0,(__location__ " ioctl to read interface list failed\n"));
 +              goto finished;
 +      }
 +
 +      ifr = talloc_size(mem_ctx, ifc.ifc_len);
 +
 +      /* get a list of all interface names and addresses */
 +      ifc.ifc_req = ifr;
 +      ret = ioctl(s, SIOCGIFCONF, &ifc);
 +      if (ret) {
 +              DEBUG(0,(__location__ " ioctl to read interface list failed\n"));
 +              goto finished;
 +      }
 +
 +      /* loop over all interfaces and search for the one matching ip */
 +      num_ifs = ifc.ifc_len/sizeof(struct ifreq);
 +      for (i=0; i<num_ifs;i++) {
 +              struct sockaddr_in *sa;
 +
 +              /* we only care bout ipv4 addresses */
 +              sa = (struct sockaddr_in *)&ifr[i].ifr_addr;
 +              if (sa->sin_family != AF_INET) {
 +                      continue;
 +              }
 +
 +              /* this is not the interface you are looking for */
-               if(strcmp(inet_ntoa(sa->sin_addr), ip)){
++              if (!ctdb_same_ip(sa, &ip)) {
 +                      continue;
 +              }
 +
 +              /* this is the ifr entry for this interface/address 
 +                 read the interface flags so we can tell if it is 
 +                 loopback or not
 +              */
 +              ret = ioctl(s, SIOCGIFFLAGS, &ifr[i]);
 +              if (ret) {
 +                      DEBUG(0,(__location__ " failed to read interface flags for interface %s\n", ifr[i].ifr_name));
 +                      goto finished;
 +              }
 +
 +              /* was this ip tied to a loopback interface ? */
-               if(ifr[i].ifr_flags & IFF_LOOPBACK) {
++              if (ifr[i].ifr_flags & IFF_LOOPBACK) {
 +                      if (is_loopback != NULL) {
 +                              *is_loopback = true;
 +                      }
 +              }
 +
 +              if (ifname) {
 +                      *ifname = talloc_asprintf(mem_ctx, "%s", ifr[i].ifr_name);
 +              }
 +
 +              /* if we got this far, we have found our interface so we can
 +                 exit the loop.
 +              */              
 +              break;
 +      }       
 +
 +finished:
 +      talloc_free(ifr);
        close(s);
        return ret == 0;
  }
index 23a861170fc5591244541b378943d39ef4e89a16,5116b040f483e62769b32baf811992968643db6a..11c71cef3efd77a57bc8e3af22446bce9c8f89e2
@@@ -5,16 -5,17 +5,22 @@@
  # there is no default
  # CTDB_RECOVERY_LOCK="/some/place/on/shared/storage"
  
 -# should ctdb do IP takeover? If it should, then specify a file
++# when doing IP takeover you also may specify what network interface
++# to use by default for the public addresses. Otherwise you must
++# specify an interface on each line of the public addresses file
++# there is no default
++# CTDB_PUBLIC_INTERFACE=eth0
++
 +# Should ctdb do IP takeover? If it should, then specify a file
  # containing the list of public IP addresses that ctdb will manage
  # Note that these IPs must be different from those in $NODES above
 -# there is no default
 +# there is no default.
 +# The syntax is one line per public address of the form :
 +#   <ipaddress>/<netmask> <interface>
 +# Example: 10.1.1.1/24 eth0
 +#
  # CTDB_PUBLIC_ADDRESSES=/etc/ctdb/public_addresses
  
 -# when doing IP takeover you also must specify what network interface
 -# to use for the public addresses
 -# there is no default
 -# CTDB_PUBLIC_INTERFACE=eth0
 -
  # should ctdb manage starting/stopping the Samba service for you?
  # default is to not manage Samba
  # CTDB_MANAGES_SAMBA=yes
index 69d6a41454b108909efce338867022531cdab3f5,516e3111c39a8a4a48406525115ca309820aba6d..8c8e8b151a4dd4574bcd92e86f993a49e0b592e4
@@@ -136,31 -136,6 +136,31 @@@ struct ctdb_client 
  };
  
  
-       const char *public_address;
 +/* state associated with a public ip address */
 +struct ctdb_vnn {
 +      struct ctdb_vnn *prev, *next;
 +
 +      const char *iface;
++      struct sockaddr_in public_address;
 +      uint8_t public_netmask_bits;
 +
 +      /* the node number that is serving this public address, if any. 
 +         If no node serves this ip it is set to -1 */
 +      int32_t pnn;
 +
 +      /* 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;
 +
 +      /* a context to hang sending gratious arp events off */
 +      TALLOC_CTX *takeover_ctx;
 +
 +      struct ctdb_kill_tcp *killtcp;
 +};
 +
  /*
    state associated with one node
  */
@@@ -356,8 -347,9 +356,9 @@@ struct ctdb_context 
        struct ctdb_client_ip *client_ip_list;
        bool do_setsched;
        void *saved_scheduler_param;
 -      struct ctdb_kill_tcp *killtcp;
        struct _trbt_tree_t *server_ids;        
 +      const char *event_script_dir;
++      const char *default_public_interface;
  };
  
  struct ctdb_db_context {
@@@ -693,9 -686,9 +694,12 @@@ struct ctdb_req_keepalive 
  void ctdb_set_error(struct ctdb_context *ctdb, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3);
  void ctdb_fatal(struct ctdb_context *ctdb, const char *msg);
  bool ctdb_same_address(struct ctdb_address *a1, struct ctdb_address *a2);
++bool parse_ip_mask(const char *s, struct sockaddr_in *ip, unsigned *mask);
  int ctdb_parse_address(struct ctdb_context *ctdb,
                       TALLOC_CTX *mem_ctx, const char *str,
                       struct ctdb_address *address);
++bool ctdb_same_ip(const struct sockaddr_in *ip1, const struct sockaddr_in *ip2);
++bool ctdb_same_sockaddr(const struct sockaddr_in *ip1, const struct sockaddr_in *ip2);
  uint32_t ctdb_hash(const TDB_DATA *key);
  uint32_t ctdb_hash_string(const char *str);
  void ctdb_request_call(struct ctdb_context *ctdb, struct ctdb_req_header *hdr);
@@@ -1058,7 -1052,7 +1062,7 @@@ int ctdb_ctrl_get_public_ips(struct ctd
  
  /* from takeover/system.c */
  int ctdb_sys_send_arp(const struct sockaddr_in *saddr, const char *iface);
- bool ctdb_sys_have_ip(const char *ip, bool *is_loopback, TALLOC_CTX *mem_ctx, char **ifname);
 -bool ctdb_sys_have_ip(const char *ip);
++bool ctdb_sys_have_ip(struct sockaddr_in ip, bool *is_loopback, TALLOC_CTX *mem_ctx, char **ifname);
  int ctdb_sys_send_tcp(int fd,
                      const struct sockaddr_in *dest, 
                      const struct sockaddr_in *src,
index ca9be264af94f4efdfbdd0b45af5cf35a5e7e1a4,8c8c9308744253759aaaf1e3ad7d70d270c6ad54..17380f1a9e84be36146f685a0d53b01d5dc1b9c1
@@@ -92,9 -92,9 +92,9 @@@ int32_t ctdb_control_check_server_id(st
  {
        struct ctdb_server_id *server_id = (struct ctdb_server_id *)indata.dptr;
  
--      return (int32_t)trbt_lookuparray32(ctdb->server_ids, 
--                              SERVER_ID_KEY_SIZE,
--                              get_server_id_key(server_id));
++      return trbt_lookuparray32(ctdb->server_ids, 
++                                SERVER_ID_KEY_SIZE,
++                                get_server_id_key(server_id)) == NULL? 0 : 1;
  }
  
  /*
index b252b07737d0c1e1362a74c40ac4a009b60083da,89787caf1be080a827acd30e6d0bb1f4f052daa1..781bfaf32406af6ee3ddc469af8ff8d2ee4ab3c9
@@@ -183,13 -179,15 +183,13 @@@ failed
    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 struct ctdb_vnn *find_public_ip_vnn(struct ctdb_context *ctdb, char *ip)
 -static int32_t find_public_ip_vnn(struct ctdb_context *ctdb, char *ip)
++static struct ctdb_vnn *find_public_ip_vnn(struct ctdb_context *ctdb, struct sockaddr_in ip)
  {
 -      int32_t vnn = -1;
 -      int i;
 +      struct ctdb_vnn *vnn;
  
 -      for (i=0;i<ctdb->num_nodes;i++) {
 -              if (ctdb->nodes[i]->public_address && !strcmp(ip, ctdb->nodes[i]->public_address)) {
 -                      vnn = i;
 -                      break;
 +      for (vnn=ctdb->vnn;vnn;vnn=vnn->next) {
-               if (!strcmp(vnn->public_address, ip)) {
++              if (ctdb_same_ip(&vnn->public_address, &ip)) {
 +                      return vnn;
                }
        }
  
@@@ -209,25 -206,14 +209,25 @@@ int32_t ctdb_control_takeover_ip(struc
        int ret;
        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 */
 -      node->takeover_vnn = pip->takeover_vnn;
 +      struct ctdb_vnn *vnn;
-       char *ip = talloc_strdup(tmp_ctx, inet_ntoa(pip->sin.sin_addr));
 +      bool have_ip, is_loopback;
 +      char *ifname = NULL;
 +
 +      /* update out vnn list */
-       vnn = find_public_ip_vnn(ctdb, ip);
++      vnn = find_public_ip_vnn(ctdb, pip->sin);
 +      if (vnn == NULL) {
-               DEBUG(0,("takeoverip called for an ip '%s' that is not a public address\n", ip));
++              DEBUG(0,("takeoverip called for an ip '%s' that is not a public address\n", 
++                       inet_ntoa(pip->sin.sin_addr)));
 +              talloc_free(tmp_ctx);
 +              return 0;
 +      }
 +      vnn->pnn = pip->pnn;
  
        /* if our kernel already has this IP, do nothing */
-       have_ip = ctdb_sys_have_ip(ip, &is_loopback, tmp_ctx, &ifname);
 -      if (ctdb_sys_have_ip(ip)) {
++      have_ip = ctdb_sys_have_ip(pip->sin, &is_loopback, tmp_ctx, &ifname);
 +      /* if we have the ip and it is not set to a loopback address */
 +      if (have_ip && !is_loopback) {
 +              talloc_free(tmp_ctx);
                return 0;
        }
  
        CTDB_NO_MEMORY(ctdb, state->sin);
        *state->sin = pip->sin;
  
 -      state->node = node;
 +      state->vnn = vnn;
  
 -      DEBUG(0,("Takover of IP %s/%u on interface %s\n", 
 -               ip, ctdb->nodes[ctdb->vnn]->public_netmask_bits, 
 -               ctdb->takeover.interface));
 +      DEBUG(0,("Takeover of IP %s/%u on interface %s\n", 
-                ip, vnn->public_netmask_bits, 
++               inet_ntoa(pip->sin.sin_addr), vnn->public_netmask_bits, 
 +               vnn->iface));
  
        ctdb_stop_monitoring(ctdb);
  
                                         timeval_current_ofs(ctdb->tunable.script_timeout, 0),
                                         state, takeover_ip_callback, state,
                                         "takeip %s %s %u",
 -                                       ctdb->takeover.interface, 
 -                                       ip,
 -                                       ctdb->nodes[ctdb->vnn]->public_netmask_bits);
 +                                       vnn->iface, 
-                                        ip,
++                                       inet_ntoa(pip->sin.sin_addr),
 +                                       vnn->public_netmask_bits);
        if (ret != 0) {
                DEBUG(0,(__location__ " Failed to takeover IP %s on interface %s\n",
-                        ip, vnn->iface));
 -                       ip, ctdb->takeover.interface));
++                       inet_ntoa(pip->sin.sin_addr), vnn->iface));
 +              talloc_free(tmp_ctx);
                talloc_free(state);
                return -1;
        }
  /*
    kill any clients that are registered with a IP that is being released
   */
--static void release_kill_clients(struct ctdb_context *ctdb, struct in_addr in)
++static void release_kill_clients(struct ctdb_context *ctdb, struct sockaddr_in in)
  {
        struct ctdb_client_ip *ip;
  
        for (ip=ctdb->client_ip_list; ip; ip=ip->next) {
--              if (ip->ip.sin_addr.s_addr == in.s_addr) {
++              if (ctdb_same_ip(&ip->ip, &in)) {
                        struct ctdb_client *client = ctdb_reqid_find(ctdb, 
                                                                     ip->client_id, 
                                                                     struct ctdb_client);
                        if (client->pid != 0) {
                                DEBUG(0,(__location__ " Killing client pid %u for IP %s on client_id %u\n",
--                                       (unsigned)client->pid, inet_ntoa(in),
++                                       (unsigned)client->pid, inet_ntoa(in.sin_addr),
                                         ip->client_id));
                                kill(client->pid, SIGKILL);
                        }
@@@ -310,10 -294,10 +310,10 @@@ static void release_ip_callback(struct 
        data.dptr = (uint8_t *)ip;
        data.dsize = strlen(ip)+1;
  
 -      ctdb_daemon_send_message(ctdb, ctdb->vnn, CTDB_SRVID_RELEASE_IP, data);
 +      ctdb_daemon_send_message(ctdb, ctdb->pnn, CTDB_SRVID_RELEASE_IP, data);
  
        /* kill clients that have registered with this IP */
--      release_kill_clients(ctdb, state->sin->sin_addr);
++      release_kill_clients(ctdb, *state->sin);
        
        /* the control succeeded */
        ctdb_request_control_reply(ctdb, state->c, NULL, 0, NULL);
@@@ -332,36 -315,23 +332,36 @@@ int32_t ctdb_control_release_ip(struct 
        int ret;
        struct takeover_callback_state *state;
        struct ctdb_public_ip *pip = (struct ctdb_public_ip *)indata.dptr;
-       char *ip = talloc_strdup(tmp_ctx, inet_ntoa(pip->sin.sin_addr));
 -      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;
 -
 -      if (!ctdb_sys_have_ip(ip)) {
 +      struct ctdb_vnn *vnn;
 +      bool have_ip, is_loopback;
 +      char *ifname = NULL;
 +
 +      /* update our vnn list */
-       vnn = find_public_ip_vnn(ctdb, ip);
++      vnn = find_public_ip_vnn(ctdb, pip->sin);
 +      if (vnn == NULL) {
-               DEBUG(0,("releaseip called for an ip '%s' that is not a public address\n", ip));
++              DEBUG(0,("releaseip called for an ip '%s' that is not a public address\n", 
++                       inet_ntoa(pip->sin.sin_addr)));
 +              talloc_free(tmp_ctx);
 +              return 0;
 +      }
 +      vnn->pnn = pip->pnn;
 +
-       have_ip = ctdb_sys_have_ip(ip, &is_loopback, tmp_ctx, &ifname);
++      have_ip = ctdb_sys_have_ip(pip->sin, &is_loopback, tmp_ctx, &ifname);
 +      if ( (!have_ip) || is_loopback) { 
 +              DEBUG(0,("Redundant release of IP %s/%u on interface %s (ip not held)\n", 
-                        ip, vnn->public_netmask_bits, 
++                       inet_ntoa(pip->sin.sin_addr), vnn->public_netmask_bits, 
 +                       vnn->iface));
 +              talloc_free(tmp_ctx);
                return 0;
        }
  
        DEBUG(0,("Release of IP %s/%u on interface %s\n", 
-                ip, vnn->public_netmask_bits, 
 -               ip, ctdb->nodes[ctdb->vnn]->public_netmask_bits, 
 -               ctdb->takeover.interface));
++               inet_ntoa(pip->sin.sin_addr), vnn->public_netmask_bits, 
 +               vnn->iface));
  
        /* stop any previous arps */
 -      talloc_free(ctdb->takeover.last_ctx);
 -      ctdb->takeover.last_ctx = NULL;
 +      talloc_free(vnn->takeover_ctx);
 +      vnn->takeover_ctx = NULL;
  
        state = talloc(ctdb, struct takeover_callback_state);
        CTDB_NO_MEMORY(ctdb, state);
                                         timeval_current_ofs(ctdb->tunable.script_timeout, 0),
                                         state, release_ip_callback, state,
                                         "releaseip %s %s %u",
 -                                       ctdb->takeover.interface, 
 -                                       ip,
 -                                       ctdb->nodes[ctdb->vnn]->public_netmask_bits);
 +                                       vnn->iface, 
-                                        ip,
++                                       inet_ntoa(pip->sin.sin_addr),
 +                                       vnn->public_netmask_bits);
        if (ret != 0) {
                DEBUG(0,(__location__ " Failed to release IP %s on interface %s\n",
-                        ip, vnn->iface));
 -                       ip, ctdb->takeover.interface));
++                       inet_ntoa(pip->sin.sin_addr), vnn->iface));
 +              talloc_free(tmp_ctx);
                talloc_free(state);
                return -1;
        }
        /* tell the control that we will be reply asynchronously */
        *async_reply = true;
  
- static int add_public_address(struct ctdb_context *ctdb, int ip0, int ip1, int ip2, int ip3, int nm, char *iface)
 +      talloc_free(tmp_ctx);
 +      return 0;
 +}
 +
 +
 +
-       char *public_address;
++static int add_public_address(struct ctdb_context *ctdb, struct sockaddr_in addr, unsigned mask, const char *iface)
 +{
 +      struct ctdb_vnn      *vnn;
-       public_address = talloc_asprintf(ctdb, "%d.%d.%d.%d", ip0, ip1, ip2, ip3);
-       CTDB_NO_MEMORY_FATAL(ctdb, public_address);
 +
 +      /* Verify that we dont have an entry for this ip yet */
-               if (!strcmp(public_address, vnn->public_address)) {
-                       DEBUG(0,("Same ip '%s' specified multiple times in the public address list \n", public_address));
-                       talloc_free(public_address);
 +      for (vnn=ctdb->vnn;vnn;vnn=vnn->next) {
-       
-       /* make sure the netmask is ok */
-       if (nm > 32) {
-               DEBUG(0, ("Illegal netmask for IP %s\n", public_address));
-               talloc_free(public_address);
-               exit(1);
-       }
++              if (ctdb_same_sockaddr(&addr, &vnn->public_address)) {
++                      DEBUG(0,("Same ip '%s' specified multiple times in the public address list \n", 
++                               inet_ntoa(addr.sin_addr)));
 +                      exit(1);
 +              }               
 +      }
 +
-       if (iface) {
-               vnn->iface = talloc_strdup(vnn, iface);
-       }
-       vnn->public_address      = talloc_steal(vnn, public_address);
-       vnn->public_netmask_bits = nm;
-       vnn->pnn        = -1;
 +      /* create a new vnn structure for this ip address */
 +      vnn = talloc_zero(ctdb, struct ctdb_vnn);
 +      CTDB_NO_MEMORY_FATAL(ctdb, vnn);
++      vnn->iface = talloc_strdup(vnn, iface);
++      vnn->public_address      = addr;
++      vnn->public_netmask_bits = mask;
++      vnn->pnn                 = -1;
 +      
 +      DLIST_ADD(ctdb->vnn, vnn);
 +
        return 0;
  }
  
@@@ -467,19 -394,37 +454,35 @@@ int ctdb_set_public_addresses(struct ct
                nlines--;
        }
  
 -      if (nlines != ctdb->num_nodes) {
 -              DEBUG(0,("Number of lines in %s does not match number of nodes!\n", alist));
 -              talloc_free(lines);
 -              return -1;
 -      }
 -
        for (i=0;i<nlines;i++) {
-               int ip0, ip1, ip2, ip3, nm;
-               char iface[256];
 -              char *p;
 -              struct in_addr in;
--
-               if (sscanf(lines[i], "%d.%d.%d.%d/%d %255s", &ip0, &ip1, &ip2, &ip3, &nm, iface) != 6) {
-                       DEBUG(0,("Badly formed line '%s' in public address list\n", lines[i]));
 -              ctdb->nodes[i]->public_address = talloc_strdup(ctdb->nodes[i], lines[i]);
 -              CTDB_NO_MEMORY(ctdb, ctdb->nodes[i]->public_address);
 -              ctdb->nodes[i]->takeover_vnn = -1;
 -
 -              /* see if they supplied a netmask length */
 -              p = strchr(ctdb->nodes[i]->public_address, '/');
 -              if (!p) {
 -                      DEBUG(0,("You must supply a netmask for public address %s\n",
 -                               ctdb->nodes[i]->public_address));
++              unsigned mask;
++              struct sockaddr_in addr;
++              char ifacebuf[100];
++              const char *iface;
++              char *tok;
++
++              tok = strtok(lines[i], " \t");
++              if (!tok || !parse_ip_mask(tok, &addr, &mask)) {
++                      DEBUG(0,("Badly formed line %u in public address list\n", i+1));
 +                      talloc_free(lines);
                        return -1;
                }
 -              *p = 0;
 -              ctdb->nodes[i]->public_netmask_bits = atoi(p+1);
 -
 -              if (ctdb->nodes[i]->public_netmask_bits > 32) {
 -                      DEBUG(0, ("Illegal netmask for IP %s\n", ctdb->nodes[i]->public_address));
 -                      return -1;
++              tok = strtok(NULL, " \t");
++              if (tok == NULL) {
++                      if (NULL == ctdb->default_public_interface) {
++                              DEBUG(0,("No default public interface and no interface specified at line %u of public address list\n",
++                                       i+1));
++                              talloc_free(lines);
++                              return -1;
++                      }
++                      iface = ctdb->default_public_interface;
++              } else {
++                      iface = ifacebuf;
+               }
  
-               if (add_public_address(ctdb, ip0, ip1, ip2, ip3, nm, iface)) {
-                       DEBUG(0,("Failed to add '%s' to the public address list\n", lines[i]));
 -              if (inet_aton(ctdb->nodes[i]->public_address, &in) == 0) {
 -                      DEBUG(0,("Badly formed IP '%s' in public address list\n", ctdb->nodes[i]->public_address));
++              if (add_public_address(ctdb, addr, mask, iface)) {
++                      DEBUG(0,("Failed to add line %u to the public address list\n", i+1));
 +                      talloc_free(lines);
                        return -1;
                }
        }
@@@ -542,118 -454,63 +545,118 @@@ static int can_node_serve_ip(struct ctd
  }
  
  
 -/*
 -  try to find an available node to take a given nodes IP that meets the
 -  criterion given by the flags
 - */
 -static void ctdb_takeover_find_node(struct ctdb_context *ctdb, struct ctdb_node_map *nodemap,
 -                                  int node, uint32_t mask_flags)
 +/* search the node lists list for a node to takeover this ip.
 +   pick the node that currently are serving the least number of ips
 +   so that the ips get spread out evenly.
 +*/
 +static int find_takeover_node(struct ctdb_context *ctdb, 
 +              struct ctdb_node_map *nodemap, uint32_t mask, 
 +              struct ctdb_public_ip_list *ip,
 +              struct ctdb_public_ip_list *all_ips)
  {
-       int pnn, min, num;
 -      static int start_node=0;
 -      int j;
++      int pnn, min=0, num;
 +      int i;
  
 -      /* If we add facilities to add/remove nodes to a cluster at runtime
 -         we must make sure that start_node is suddently not beyond the
 -         end of the nodelist
 -      */
 -      if (start_node >= nodemap->num) {
 -              start_node = 0;
 -      }
 -
 -      j=start_node;
 -      while (1) {
 -              if (!(nodemap->nodes[j].flags & mask_flags) &&
 -                  ctdb_same_subnet(ctdb->nodes[j]->public_address, 
 -                                   ctdb->nodes[node]->public_address, 
 -                                   ctdb->nodes[j]->public_netmask_bits)) {
 -                      ctdb->nodes[node]->takeover_vnn = nodemap->nodes[j].vnn;
 -                      /* We found a node to take over
 -                         also update the startnode so that we start at a 
 -                         different node next time we are called.
 -                       */
 -                      start_node = (j+1)%nodemap->num;;
 -                      return;
 +      pnn    = -1;
 +      for (i=0;i<nodemap->num;i++) {
 +              if (nodemap->nodes[i].flags & mask) {
 +                      /* This node is not healty and can not be used to serve
 +                         a public address 
 +                      */
 +                      continue;
                }
  
 -              /* Try the next node */
 -              j=(j+1)%nodemap->num;
 +              /* verify that this node can serve this ip */
 +              if (can_node_serve_ip(ctdb, i, ip)) {
 +                      /* no it couldnt   so skip to the next node */
 +                      continue;
 +              }
  
 -              /* We tried all the nodes and got back to where we started,
 -                 there is no node that can take over
 -               */
 -              if (j == start_node) {
 -                      break;
 +              num = node_ip_coverage(ctdb, i, all_ips);
 +              /* was this the first node we checked ? */
 +              if (pnn == -1) {
 +                      pnn = i;
 +                      min  = num;
 +              } else {
 +                      if (num < min) {
 +                              pnn = i;
 +                              min  = num;
 +                      }
                }
 +      }       
 +      if (pnn == -1) {
 +              DEBUG(0,(__location__ " Could not find node to take over public address '%s'\n", inet_ntoa(ip->sin.sin_addr)));
 +              return -1;
        }
  
 -      /* No takeover node found */
 -      return;
 +      ip->pnn = pnn;
 +      return 0;
 +}
 +
 +struct ctdb_public_ip_list *
 +add_ip_to_merged_list(struct ctdb_context *ctdb,
 +                      TALLOC_CTX *tmp_ctx, 
 +                      struct ctdb_public_ip_list *ip_list, 
 +                      struct ctdb_public_ip *ip)
 +{
 +      struct ctdb_public_ip_list *tmp_ip; 
 +
 +      /* do we already have this ip in our merged list ?*/
 +      for (tmp_ip=ip_list;tmp_ip;tmp_ip=tmp_ip->next) {
 +
 +              /* we already  have this public ip in the list */
 +              if (tmp_ip->sin.sin_addr.s_addr == ip->sin.sin_addr.s_addr) {
 +                      return ip_list;
 +              }
 +      }
 +
 +      /* this is a new public ip, we must add it to the list */
 +      tmp_ip = talloc_zero(tmp_ctx, struct ctdb_public_ip_list);
 +      CTDB_NO_MEMORY_NULL(ctdb, tmp_ip);
 +      tmp_ip->pnn  = ip->pnn;
 +      tmp_ip->sin  = ip->sin;
 +      tmp_ip->next = ip_list;
 +
 +      return tmp_ip;
  }
  
 +struct ctdb_public_ip_list *
 +create_merged_ip_list(struct ctdb_context *ctdb, TALLOC_CTX *tmp_ctx)
 +{
 +      int i, j;
 +      struct ctdb_public_ip_list *ip_list = NULL;
 +      struct ctdb_all_public_ips *public_ips;
 +
 +      for (i=0;i<ctdb->num_nodes;i++) {
 +              public_ips = ctdb->nodes[i]->public_ips;
 +
 +              /* there were no public ips for this node */
 +              if (public_ips == NULL) {
 +                      continue;
 +              }               
 +
 +              for (j=0;j<public_ips->num;j++) {
 +                      ip_list = add_ip_to_merged_list(ctdb, tmp_ctx,
 +                                      ip_list, &public_ips->ips[j]);
 +              }
 +      }
 +
 +      return ip_list;
 +}
  
  /*
    make any IP alias changes for public addresses that are necessary 
   */
  int ctdb_takeover_run(struct ctdb_context *ctdb, struct ctdb_node_map *nodemap)
  {
 -      int i, j;
 +      int i, num_healthy, retries;
        int ret;
        struct ctdb_public_ip ip;
-       int maxnode, maxnum, minnode, minnum, num;
 +      uint32_t mask;
 +      struct ctdb_public_ip_list *all_ips, *tmp_ip;
++      int maxnode, maxnum=0, minnode, minnum=0, num;
 +      TALLOC_CTX *tmp_ctx = talloc_new(ctdb);
 +
  
        ZERO_STRUCT(ip);
  
@@@ -882,14 -638,14 +885,11 @@@ int32_t ctdb_control_tcp_client(struct 
        int ret;
        TDB_DATA data;
        struct ctdb_client_ip *ip;
--      char *addr;
 -      int32_t takeover_vnn;
 +      struct ctdb_vnn *vnn;
  
--      addr = inet_ntoa(p->dest.sin_addr);
--
-       vnn = find_public_ip_vnn(ctdb, addr);
 -      takeover_vnn = find_public_ip_vnn(ctdb, addr);
 -      if (takeover_vnn == -1) {
 -              DEBUG(3,("Could not add client IP %s. This is not a public address.\n", addr)); 
++      vnn = find_public_ip_vnn(ctdb, p->dest);
 +      if (vnn == NULL) {
-               DEBUG(3,("Could not add client IP %s. This is not a public address.\n", addr)); 
++              DEBUG(3,("Could not add client IP %s. This is not a public address.\n", inet_ntoa(p->dest.sin_addr))); 
                return 0;
        }
  
@@@ -974,18 -731,8 +974,17 @@@ int32_t ctdb_control_tcp_add(struct ctd
        struct ctdb_control_tcp_vnn *p = (struct ctdb_control_tcp_vnn *)indata.dptr;
        struct ctdb_tcp_array *tcparray;
        struct ctdb_tcp_connection tcp;
-       char *addr;
 +      struct ctdb_vnn *vnn;
-       addr = inet_ntoa(p->dest.sin_addr);
-       vnn = find_public_ip_vnn(ctdb, addr);
 +
-               DEBUG(0,(__location__ " got TCP_ADD control for an address which is not a public address '%s'\n", addr));
++      vnn = find_public_ip_vnn(ctdb, p->dest);
 +      if (vnn == NULL) {
++              DEBUG(0,(__location__ " got TCP_ADD control for an address which is not a public address '%s'\n", 
++                       inet_ntoa(p->dest.sin_addr)));
 +              return-1;
 +      }
  
 -      tcparray = ctdb->nodes[p->vnn]->tcp_array;
 +
 +      tcparray = vnn->tcp_array;
  
        /* If this is the first tickle */
        if (tcparray == NULL) {
  static void ctdb_remove_tcp_connection(struct ctdb_context *ctdb, struct ctdb_tcp_connection *conn)
  {
        struct ctdb_tcp_connection *tcpp;
-       struct ctdb_vnn *vnn = find_public_ip_vnn(ctdb, inet_ntoa(conn->daddr.sin_addr));
 -      int32_t vnn = find_public_ip_vnn(ctdb, inet_ntoa(conn->daddr.sin_addr));
 -      struct ctdb_node *node;
++      struct ctdb_vnn *vnn = find_public_ip_vnn(ctdb, conn->daddr);
  
 -      if (vnn == -1) {
 +      if (vnn == NULL) {
                DEBUG(0,(__location__ " unable to find public address %s\n", inet_ntoa(conn->daddr.sin_addr)));
                return;
        }
@@@ -1129,26 -879,25 +1128,22 @@@ void ctdb_takeover_client_destructor_ho
   */
  void ctdb_release_all_ips(struct ctdb_context *ctdb)
  {
 -      int i;
 -
 -      if (!ctdb->takeover.enabled) {
 -              return;
 -      }
 -
 -      for (i=0;i<ctdb->num_nodes;i++) {
 -              struct ctdb_node *node = ctdb->nodes[i];
 -              if (ctdb_sys_have_ip(node->public_address)) {
 -                      struct in_addr in;
 +      struct ctdb_vnn *vnn;
 +      TALLOC_CTX *tmp_ctx = talloc_new(ctdb);
 +      bool have_ip, is_loopback;
 +      char *ifname = NULL;
 +
 +      for (vnn=ctdb->vnn;vnn;vnn=vnn->next) {
 +              have_ip = ctdb_sys_have_ip(vnn->public_address, &is_loopback, tmp_ctx, &ifname);
 +              if (have_ip && !is_loopback) {
-                       struct in_addr in;
                        ctdb_event_script(ctdb, "releaseip %s %s %u",
 -                                        ctdb->takeover.interface, 
 -                                        node->public_address,
 -                                        node->public_netmask_bits);
 -                      if (inet_aton(node->public_address, &in) != 0) {
 -                              release_kill_clients(ctdb, in);
 -                      }
 +                                        vnn->iface, 
-                                         vnn->public_address,
++                                        inet_ntoa(vnn->public_address.sin_addr),
 +                                        vnn->public_netmask_bits);
-                       if (inet_aton(vnn->public_address, &in) != 0) {
-                               release_kill_clients(ctdb, in);
-                       }
++                      release_kill_clients(ctdb, vnn->public_address);
                }
        }
 +      talloc_free(tmp_ctx);
  }
  
  
@@@ -1176,14 -919,15 +1171,12 @@@ int32_t ctdb_control_get_public_ips(str
        outdata->dsize = len;
        outdata->dptr  = (uint8_t *)ips;
  
 -      ips->num = ctdb->num_nodes;
 -      for(i=0;i<ctdb->num_nodes;i++){
 -              ips->ips[i].vnn = i;
 -              ips->ips[i].takeover_vnn = ctdb->nodes[i]->takeover_vnn;
 -              ips->ips[i].sin.sin_family = AF_INET;
 -              if (ctdb->nodes[i]->public_address) {
 -                      inet_aton(ctdb->nodes[i]->public_address, 
 -                                &ips->ips[i].sin.sin_addr);
 -              }
 +      ips->num = num;
 +      i = 0;
 +      for (vnn=ctdb->vnn;vnn;vnn=vnn->next) {
-               ips->ips[i].pnn   = vnn->pnn;
-               ips->ips[i].sin.sin_family = AF_INET;
-               inet_aton(vnn->public_address, 
-                         &ips->ips[i].sin.sin_addr);
++              ips->ips[i].pnn = vnn->pnn;
++              ips->ips[i].sin = vnn->public_address;
 +              i++;
        }
  
        return 0;
@@@ -1359,20 -1102,8 +1352,20 @@@ static void *add_killtcp_callback(void 
  static int ctdb_killtcp_add_connection(struct ctdb_context *ctdb, 
                                       struct sockaddr_in *src, struct sockaddr_in *dst)
  {
 -      struct ctdb_kill_tcp *killtcp = ctdb->killtcp;
 +      struct ctdb_kill_tcp *killtcp;
        struct ctdb_killtcp_con *con;
-       vnn = find_public_ip_vnn(ctdb, inet_ntoa(dst->sin_addr));
 +      struct ctdb_vnn *vnn;
 +
-               vnn = find_public_ip_vnn(ctdb, inet_ntoa(src->sin_addr));
++      vnn = find_public_ip_vnn(ctdb, *dst);
 +      if (vnn == NULL) {
++              vnn = find_public_ip_vnn(ctdb, *src);
 +      }
 +      if (vnn == NULL) {
 +              DEBUG(0,(__location__ " Could not killtcp, not a public address\n")); 
 +              return -1;
 +      }
 +
 +      killtcp = vnn->killtcp;
        
        /* If this is the first connection to kill we must allocate
           a new structure
@@@ -1474,8 -1207,6 +1467,7 @@@ int32_t ctdb_control_set_tcp_tickle_lis
  {
        struct ctdb_control_tcp_tickle_list *list = (struct ctdb_control_tcp_tickle_list *)indata.dptr;
        struct ctdb_tcp_array *tcparray;
-       char *addr;
 +      struct ctdb_vnn *vnn;
  
        /* We must at least have tickles.num or else we cant verify the size
           of the received data blob
                return -1;
        }       
  
-       addr = inet_ntoa(list->ip.sin_addr);
-       vnn = find_public_ip_vnn(ctdb, addr);
 -      /* Make sure the vnn looks sane */
 -      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;
++      vnn = find_public_ip_vnn(ctdb, list->ip);
 +      if (vnn == NULL) {
-               DEBUG(0,(__location__ " Could not set tcp tickle list, '%s' is not a public address\n", addr)); 
++              DEBUG(0,(__location__ " Could not set tcp tickle list, '%s' is not a public address\n", 
++                       inet_ntoa(list->ip.sin_addr))); 
 +              return 1;
        }
  
 -
        /* remove any old ticklelist we might have */
 -      talloc_free(ctdb->nodes[list->vnn]->tcp_array);
 -      ctdb->nodes[list->vnn]->tcp_array = NULL;
 +      talloc_free(vnn->tcp_array);
 +      vnn->tcp_array = NULL;
  
        tcparray = talloc(ctdb->nodes, struct ctdb_tcp_array);
        CTDB_NO_MEMORY(ctdb, tcparray);
@@@ -1534,19 -1264,15 +1525,16 @@@ int32_t ctdb_control_get_tcp_tickle_lis
        struct ctdb_control_tcp_tickle_list *list;
        struct ctdb_tcp_array *tcparray;
        int num;
-       char *addr;
 +      struct ctdb_vnn *vnn;
  
-       addr = inet_ntoa(ip->sin_addr);
--
-       vnn = find_public_ip_vnn(ctdb, addr);
 -      /* 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;
++      vnn = find_public_ip_vnn(ctdb, *ip);
 +      if (vnn == NULL) {
-               DEBUG(0,(__location__ " Could not get tcp tickle list, '%s' is not a public address\n", addr)); 
++              DEBUG(0,(__location__ " Could not get tcp tickle list, '%s' is not a public address\n", 
++                       inet_ntoa(ip->sin_addr))); 
 +              return 1;
        }
  
 -      tcparray = ctdb->nodes[vnn]->tcp_array;
 +      tcparray = vnn->tcp_array;
        if (tcparray) {
                num = tcparray->num;
        } else {
@@@ -1625,29 -1351,29 +1613,28 @@@ static void ctdb_update_tcp_tickles(str
                                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];
 +      int ret;
 +      struct ctdb_vnn *vnn;
-       struct sockaddr_in ip;
  
 -              /* we only send out updates for public addresses that we
 -                 have taken over
 +      for (vnn=ctdb->vnn;vnn;vnn=vnn->next) {
 +              /* we only send out updates for public addresses that 
 +                 we have taken over
                 */
 -              if (ctdb->vnn != node->takeover_vnn) {
 +              if (ctdb->pnn != vnn->pnn) {
                        continue;
                }
                /* We only send out the updates if we need to */
 -              if (!node->tcp_update_needed) {
 +              if (!vnn->tcp_update_needed) {
                        continue;
                }
-               inet_aton(vnn->public_address, &ip.sin_addr);
 -
                ret = ctdb_ctrl_set_tcp_tickles(ctdb, 
                                TAKEOVER_TIMEOUT(),
                                CTDB_BROADCAST_CONNECTED,
-                               &ip,
 -                              node->takeover_vnn,
 -                              node->tcp_array);
++                              &vnn->public_address,
 +                              vnn->tcp_array);
                if (ret != 0) {
-                       DEBUG(0,("Failed to send the tickle update for public address %s\n", vnn->public_address));
 -                      DEBUG(0,("Failed to send the tickle update for public address %s\n", node->public_address));
++                      DEBUG(0,("Failed to send the tickle update for public address %s\n", 
++                               inet_ntoa(vnn->public_address.sin_addr)));
                }
        }