Patch for adding SCTP support in CTDB

Vishnu vishnumr at gmail.com
Fri Sep 23 22:24:18 UTC 2016


Hi,

SCTP is useful in HA environments with multiple IP networks / 
multi-homing. It provides automatic network redundancy. When setting up 
a HA NAS system, I added sctp support to ctdb some time last year. Just 
updated the patch to apply on the latest ctdb git tree and built it.

Discussed this with Jose Rivera at SDC recently, he seemed to like this 
idea of ctdb using sctp as a transport. Here's my patch:

Cheers
Vishnu

 From 25d538caed0dacefd28da0e7b8b7f0d06dbd91dd Mon Sep 17 00:00:00 2001
From: Vishnu Rangayyan <vishnumr at gmail.com>
Date: Fri, 23 Sep 2016 15:09:27 -0700
Subject: [PATCH] Add SCTP support to allow multi-homed / 
multi-IP-network ctdb
  nodes to use SCTP for network path redundancy. This patch allows defining
  upto 3 IP addresses on each ctdb node that it will try to use for sctp
  connections. The sctp handling is very similar to tcp and hence we 
reuse most
  of the current tcp handling code. Requires lksctp-tools and
  lksctp-tools-devel to build.

---
  Makefile.in                 |   5 +-
  common/ctdb_io.c            |  33 +++++++++++-
  config/ctdb.sysconfig       |   2 +-
  include/ctdb_private.h      |   9 +++-
  packaging/RPM/ctdb.spec.in  |   4 +-
  server/ctdb_daemon.c        |   7 +++
  server/ctdb_recover.c       |  10 ++--
  server/ctdb_server.c        |  66 ++++++++++++++++++------
  tcp/ctdb_tcp.h              |   2 +
  tcp/tcp_connect.c           | 119 
++++++++++++++++++++++++++++++++++----------
  tcp/tcp_init.c              |   7 ++-
  tests/src/ctdb_test_stubs.c |  10 ++--
  12 files changed, 216 insertions(+), 58 deletions(-)

diff --git a/Makefile.in b/Makefile.in
index 55b21b7..8b7bad9 100755
--- a/Makefile.in
+++ b/Makefile.in
@@ -42,6 +42,8 @@ TDB_LIBS = @TDB_LIBS@
  TDB_CFLAGS = @TDB_CFLAGS@
  TDB_OBJ = @TDB_OBJ@

+SCTP_LIBS = -lsctp
+
  REPLACE_OBJ = @LIBREPLACEOBJ@

  SOCKET_WRAPPER_OBJ = @SOCKET_WRAPPER_OBJS@
@@ -75,7 +77,8 @@ LDSHFLAGS=-fPIC -shared
  #LDSHFLAGS=-fPIC -shared -Wl,-Bsymbolic -Wl,-z,relo 
-Wl,-Bsymbolic-funtions -Wl,--as-needed -Wl,-z,defs
  SHLD=${CC} ${CFLAGS} ${LDSHFLAGS} -o $@

-LIB_FLAGS=@LDFLAGS@ -Llib @LIBS@ $(POPT_LIBS) $(TALLOC_LIBS) 
$(TEVENT_LIBS) $(TDB_LIBS) \
+LIB_FLAGS=@LDFLAGS@ -Llib @LIBS@ $(POPT_LIBS) $(TALLOC_LIBS) 
$(TEVENT_LIBS) \
+        $(TDB_LIBS) $(SCTP_LIBS) \
            @INFINIBAND_LIBS@ @CTDB_PCAP_LDFLAGS@

  CTDB_VERSION_H = include/ctdb_version.h
diff --git a/common/ctdb_io.c b/common/ctdb_io.c
index 351006d..6d83a7d 100644
--- a/common/ctdb_io.c
+++ b/common/ctdb_io.c
@@ -28,6 +28,7 @@
  #include "../include/ctdb_private.h"
  #include "../include/ctdb_client.h"
  #include <stdarg.h>
+#include <netinet/sctp.h>

  #define QUEUE_BUFFER_SIZE    (16*1024)

@@ -154,6 +155,9 @@ static void queue_io_read(struct ctdb_queue *queue)
      ssize_t nread;
      uint8_t *data;
      int navail;
+    struct ctdb_req_header hdr;
+    int msgflags = MSG_PEEK;
+    socklen_t fromlen = 0;

      /* check how much data is available on the socket for immediately
         guaranteed nonblocking access.
@@ -161,9 +165,34 @@ static void queue_io_read(struct ctdb_queue *queue)
         we know all reads will be successful and will neither block
         nor fail with a "data not available right now" error
      */
-    if (ioctl(queue->fd, FIONREAD, &num_ready) != 0) {
-        return;
+    if (queue->ctdb->protocol != IPPROTO_SCTP) {
+        if (ioctl(queue->fd, FIONREAD, &num_ready) != 0) {
+            return;
+        }
+    } else {
+        /* sctp does not support ioctls.
+         * peek into the socket buffer and read the hdr.
+         */
+        num_ready = sctp_recvmsg(queue->fd, (void *)&hdr, sizeof(hdr),
+                     NULL, &fromlen, NULL, &msgflags);
+        if (num_ready < 0) {
+            if (errno == ECONNRESET || errno == EINTR ||
+                errno == EAGAIN || errno == EWOULDBLOCK)
+                return;
+            /* something bad happened.. cleanup. */
+            DEBUG(DEBUG_ERR, ("recv error on socket %d\n", errno));
+            goto failed;
+        }
+
+        if (num_ready && num_ready < sizeof(hdr))
+            /* not enough data in socket buffer */
+            return;
+
+        if (num_ready == sizeof(hdr) && hdr.ctdb_magic == CTDB_MAGIC)
+            /* adjust length to read to entire message size */
+            num_ready = hdr.length;
      }
+
      if (num_ready == 0) {
          /* the descriptor has been closed */
          goto failed;
diff --git a/config/ctdb.sysconfig b/config/ctdb.sysconfig
index 35bf5f8..63ec5a4 100644
--- a/config/ctdb.sysconfig
+++ b/config/ctdb.sysconfig
@@ -111,7 +111,7 @@ CTDB_RECOVERY_LOCK="/some/place/on/shared/storage"
  # defaults to /var/run/ctdb/ctdbd.socket
  # CTDB_SOCKET=/var/run/ctdb/ctdbd.socket

-# what transport to use. Only tcp is currently supported
+# what transport to use. tcp or sctp are allowed options.
  # defaults to tcp
  # CTDB_TRANSPORT="tcp"

diff --git a/include/ctdb_private.h b/include/ctdb_private.h
index b4966b8..b6176e0 100644
--- a/include/ctdb_private.h
+++ b/include/ctdb_private.h
@@ -209,12 +209,15 @@ struct ctdb_vnn {
      bool update_in_flight;
  };

+#define CTDB_SCTP_MAX_NETS    3 /* max networks/IPs for sctp 
multi-homing */
+
  /*
    state associated with one node
  */
  struct ctdb_node {
      struct ctdb_context *ctdb;
-    struct ctdb_address address;
+    /* support multiple addresses for sctp. only address[0] used for 
tcp. */
+    struct ctdb_address address[CTDB_SCTP_MAX_NETS];
      const char *name; /* for debug messages */
      void *private_data; /* private to transport */
      uint32_t pnn;
@@ -459,7 +462,9 @@ struct ctdb_context {
      struct ctdb_freeze_handle *freeze_handles[NUM_DB_PRIORITIES+1];
      bool freeze_transaction_started;
      uint32_t freeze_transaction_id;
-    struct ctdb_address address;
+    /* support multiple addresses for sctp. only address[0] used for 
tcp. */
+    struct ctdb_address address[CTDB_SCTP_MAX_NETS];
+    int protocol;
      const char *name;
      const char *db_directory;
      const char *db_directory_persistent;
diff --git a/packaging/RPM/ctdb.spec.in b/packaging/RPM/ctdb.spec.in
index b7d1bef..fb27cb5 100644
--- a/packaging/RPM/ctdb.spec.in
+++ b/packaging/RPM/ctdb.spec.in
@@ -14,7 +14,7 @@ URL: http://ctdb.samba.org/
  Source: ctdb-%{version}.tar.gz

  # Packages
-Requires: coreutils, sed, gawk, iptables, iproute, procps, ethtool, sudo
+Requires: coreutils, sed, gawk, iptables, iproute, procps, ethtool, 
sudo, lksctp-tools
  # Commands - package name might vary
  Requires: /usr/bin/killall, /bin/kill, /bin/netstat

@@ -60,6 +60,8 @@ BuildRequires: pcp-libs-devel
  BuildRequires: systemd-units
  %endif

+BuildRequires: lksctp-tools-devel
+
  %description
  ctdb is the clustered database used by samba

diff --git a/server/ctdb_daemon.c b/server/ctdb_daemon.c
index cbe6b23..db10f50 100644
--- a/server/ctdb_daemon.c
+++ b/server/ctdb_daemon.c
@@ -1238,6 +1238,13 @@ int ctdb_start_daemon(struct ctdb_context *ctdb, 
bool do_fork, bool use_syslog)
          int ctdb_tcp_init(struct ctdb_context *);
          ret = ctdb_tcp_init(ctdb);
      }
+
+    /* we reuse most of the existing tcp code for sctp */
+    if (strcmp(ctdb->transport, "sctp") == 0) {
+        int ctdb_tcp_init(struct ctdb_context *);
+        ret = ctdb_tcp_init(ctdb);
+    }
+
  #ifdef USE_INFINIBAND
      if (strcmp(ctdb->transport, "ib") == 0) {
          int ctdb_ibw_init(struct ctdb_context *);
diff --git a/server/ctdb_recover.c b/server/ctdb_recover.c
index 1cbcc59..e22471a 100644
--- a/server/ctdb_recover.c
+++ b/server/ctdb_recover.c
@@ -137,12 +137,12 @@ ctdb_control_getnodemap(struct ctdb_context *ctdb, 
uint32_t opcode, TDB_DATA ind
      node_map = (struct ctdb_node_map *)outdata->dptr;
      node_map->num = num_nodes;
      for (i=0; i<num_nodes; i++) {
-        if (parse_ip(ctdb->nodes[i]->address.address,
+        if (parse_ip(ctdb->nodes[i]->address[0].address,
                   NULL, /* TODO: pass in the correct interface here*/
                   0,
                   &node_map->nodes[i].addr) == 0)
          {
-            DEBUG(DEBUG_ERR, (__location__ " Failed to parse %s into a 
sockaddr\n", ctdb->nodes[i]->address.address));
+            DEBUG(DEBUG_ERR, (__location__ " Failed to parse %s into a 
sockaddr\n", ctdb->nodes[i]->address[0].address));
          }

          node_map->nodes[i].pnn   = ctdb->nodes[i]->pnn;
@@ -175,8 +175,8 @@ ctdb_control_getnodemapv4(struct ctdb_context *ctdb, 
uint32_t opcode, TDB_DATA i
      node_map = (struct ctdb_node_mapv4 *)outdata->dptr;
      node_map->num = num_nodes;
      for (i=0; i<num_nodes; i++) {
-        if (parse_ipv4(ctdb->nodes[i]->address.address, 0, 
&node_map->nodes[i].sin) == 0) {
-            DEBUG(DEBUG_ERR, (__location__ " Failed to parse %s into a 
sockaddr\n", ctdb->nodes[i]->address.address));
+        if (parse_ipv4(ctdb->nodes[i]->address[0].address, 0, 
&node_map->nodes[i].sin) == 0) {
+            DEBUG(DEBUG_ERR, (__location__ " Failed to parse %s into a 
sockaddr\n", ctdb->nodes[i]->address[0].address));
              return -1;
          }

@@ -210,7 +210,7 @@ ctdb_reload_nodes_event(struct event_context *ev, 
struct timed_event *te,

      for (i=0; i<ctdb->num_nodes; i++) {
          /* keep any identical pre-existing nodes and connections */
-        if ((i < num_nodes) && 
ctdb_same_address(&ctdb->nodes[i]->address, &nodes[i]->address)) {
+        if ((i < num_nodes) && 
ctdb_same_address(&ctdb->nodes[i]->address[0], &nodes[i]->address[0])) {
              talloc_free(ctdb->nodes[i]);
              ctdb->nodes[i] = talloc_steal(ctdb->nodes, nodes[i]);
              continue;
diff --git a/server/ctdb_server.c b/server/ctdb_server.c
index c45f4cb..22e83c6 100644
--- a/server/ctdb_server.c
+++ b/server/ctdb_server.c
@@ -41,14 +41,19 @@ int ctdb_set_transport(struct ctdb_context *ctdb, 
const char *transport)
  */
  int ctdb_ip_to_nodeid(struct ctdb_context *ctdb, const char *nodeip)
  {
-    int nodeid;
+    int nodeid, i;

      for (nodeid=0;nodeid<ctdb->num_nodes;nodeid++) {
          if (ctdb->nodes[nodeid]->flags & NODE_FLAGS_DELETED) {
              continue;
          }
-        if (!strcmp(ctdb->nodes[nodeid]->address.address, nodeip)) {
-            return nodeid;
+        for (i = 0; i < CTDB_SCTP_MAX_NETS; i++) {
+            if (!ctdb->nodes[nodeid]->address[i].address)
+                break;
+            if (!strcmp(ctdb->nodes[nodeid]->address[i].address,
+                    nodeip)) {
+                return nodeid;
+            }
          }
      }

@@ -83,6 +88,8 @@ int ctdb_set_recovery_lock_file(struct ctdb_context 
*ctdb, const char *file)
  static int ctdb_add_node(struct ctdb_context *ctdb, char *nstr)
  {
      struct ctdb_node *node, **nodep;
+    int i, ret;
+    char *addr_list, *token, *saveptr;

      nodep = talloc_realloc(ctdb, ctdb->nodes, struct ctdb_node *, 
ctdb->num_nodes+1);
      CTDB_NO_MEMORY(ctdb, nodep);
@@ -93,21 +100,33 @@ static int ctdb_add_node(struct ctdb_context *ctdb, 
char *nstr)
      CTDB_NO_MEMORY(ctdb, *nodep);
      node = *nodep;

-    if (ctdb_parse_address(ctdb, node, nstr, &node->address) != 0) {
-        return -1;
+    /* allow a comma or space separated list of ip addresses per node */
+    addr_list = talloc_strdup(ctdb->nodes, nstr);
+    CTDB_NO_MEMORY(ctdb, addr_list);
+
+    token = strtok_r(addr_list, " ,", &saveptr);
+    for (i = 0; i < CTDB_SCTP_MAX_NETS && token; i++) {
+        ret = ctdb_parse_address(ctdb, node, token, &(node->address[i]));
+        if (ret)
+            break;
+        token = strtok_r(NULL, " ,", &saveptr);
      }
+    talloc_free(addr_list);
+    if (ret)
+        return -1;
+
      node->ctdb = ctdb;
      node->name = talloc_asprintf(node, "%s:%u",
-                     node->address.address,
-                     node->address.port);
+                     node->address[0].address,
+                     node->address[0].port);
      /* this assumes that the nodes are kept in sorted order, and no 
gaps */
      node->pnn = ctdb->num_nodes;

      /* nodes start out disconnected and unhealthy */
      node->flags = (NODE_FLAGS_DISCONNECTED | NODE_FLAGS_UNHEALTHY);

-    if (ctdb->address.address &&
-        ctdb_same_address(&ctdb->address, &node->address)) {
+    if (ctdb->address[0].address &&
+        ctdb_same_address(&ctdb->address[0], &node->address[0])) {
          /* for automatic binding to interfaces, see tcp_connect.c */
          ctdb->pnn = node->pnn;
      }
@@ -138,7 +157,7 @@ static int ctdb_add_deleted_node(struct ctdb_context 
*ctdb)
      CTDB_NO_MEMORY(ctdb, *nodep);
      node = *nodep;

-    if (ctdb_parse_address(ctdb, node, "0.0.0.0", &node->address) != 0) {
+    if (ctdb_parse_address(ctdb, node, "0.0.0.0", &node->address[0]) != 
0) {
          DEBUG(DEBUG_ERR,("Failed to setup deleted node %d\n", 
ctdb->num_nodes));
          return -1;
      }
@@ -245,13 +264,28 @@ void ctdb_load_nodes_file(struct ctdb_context *ctdb)
  */
  int ctdb_set_address(struct ctdb_context *ctdb, const char *address)
  {
-    if (ctdb_parse_address(ctdb, ctdb, address, &ctdb->address) != 0) {
-        return -1;
+    int i, ret = 0;
+    char *addr_list, *token, *saveptr;
+
+    /* allow a comma or space separated list of ip addresses for us */
+    addr_list = talloc_strdup(ctdb, address);
+    CTDB_NO_MEMORY(ctdb, addr_list);
+
+    token = strtok_r(addr_list, " ,", &saveptr);
+    for (i = 0; i < CTDB_SCTP_MAX_NETS && token; i++) {
+        ret = ctdb_parse_address(ctdb, ctdb, token, &(ctdb->address[i]));
+        if (ret)
+            break;
+        token = strtok_r(NULL, " ,", &saveptr);
      }
-
-    ctdb->name = talloc_asprintf(ctdb, "%s:%u",
-                     ctdb->address.address,
-                     ctdb->address.port);
+    talloc_free(addr_list);
+    if (ret)
+        return -1;
+
+    ctdb->name = talloc_asprintf(ctdb, "%s:%u",
+                     ctdb->address[0].address,
+                     ctdb->address[0].port);
+
      return 0;
  }

diff --git a/tcp/ctdb_tcp.h b/tcp/ctdb_tcp.h
index 5b6b651..37b0457 100644
--- a/tcp/ctdb_tcp.h
+++ b/tcp/ctdb_tcp.h
@@ -20,6 +20,8 @@
  #ifndef _CTDB_TCP_H
  #define _CTDB_TCP_H

+#include <netinet/sctp.h>
+
  /* ctdb_tcp main state */
  struct ctdb_tcp {
      struct ctdb_context *ctdb;
diff --git a/tcp/tcp_connect.c b/tcp/tcp_connect.c
index 9df3300..3c74b44 100644
--- a/tcp/tcp_connect.c
+++ b/tcp/tcp_connect.c
@@ -95,7 +95,10 @@ static void ctdb_node_connect_write(struct 
event_context *ev, struct fd_event *f
      talloc_free(tnode->connect_fde);
      tnode->connect_fde = NULL;

-        setsockopt(tnode->fd,IPPROTO_TCP,TCP_NODELAY,(char 
*)&one,sizeof(one));
+    if (ctdb->protocol == IPPROTO_TCP) {
+            setsockopt(tnode->fd,IPPROTO_TCP,TCP_NODELAY,(char *)&one,
+               sizeof(one));
+    }
          setsockopt(tnode->fd,SOL_SOCKET,SO_KEEPALIVE,(char 
*)&one,sizeof(one));

      ctdb_queue_set_fd(tnode->out_queue, tnode->fd);
@@ -137,15 +140,15 @@ void ctdb_tcp_node_connect(struct event_context 
*ev, struct timed_event *te,
  #ifdef HAVE_SOCK_SIN_LEN
      sock_out.ip.sin_len = sizeof(sock_out);
  #endif
-    if (ctdb_tcp_get_address(ctdb, node->address.address, &sock_out) != 
0) {
+    if (ctdb_tcp_get_address(ctdb, node->address[0].address, &sock_out) 
!= 0) {
          return;
      }
      switch (sock_out.sa.sa_family) {
      case AF_INET:
-        sock_out.ip.sin_port = htons(node->address.port);
+        sock_out.ip.sin_port = htons(node->address[0].port);
          break;
      case AF_INET6:
-        sock_out.ip6.sin6_port = htons(node->address.port);
+        sock_out.ip6.sin6_port = htons(node->address[0].port);
          break;
      default:
          DEBUG(DEBUG_ERR, (__location__ " unknown family %u\n",
@@ -153,7 +156,7 @@ void ctdb_tcp_node_connect(struct event_context *ev, 
struct timed_event *te,
          return;
      }

-    tnode->fd = socket(sock_out.sa.sa_family, SOCK_STREAM, IPPROTO_TCP);
+    tnode->fd = socket(sock_out.sa.sa_family, SOCK_STREAM, ctdb->protocol);
      if (tnode->fd == -1) {
          DEBUG(DEBUG_ERR, (__location__ "Failed to create socket\n"));
          return;
@@ -170,7 +173,7 @@ void ctdb_tcp_node_connect(struct event_context *ev, 
struct timed_event *te,
       * a dedicated non-routeable network.
       */
      ZERO_STRUCT(sock_in);
-    if (ctdb_tcp_get_address(ctdb, ctdb->address.address, &sock_in) != 0) {
+    if (ctdb_tcp_get_address(ctdb, ctdb->address[0].address, &sock_in) 
!= 0) {
          DEBUG(DEBUG_ERR, (__location__ " Failed to find our address. 
Failing bind.\n"));
          close(tnode->fd);
          return;
@@ -282,8 +285,8 @@ static int ctdb_tcp_listen_automatic(struct 
ctdb_context *ctdb)
  {
      struct ctdb_tcp *ctcp = talloc_get_type(ctdb->private_data,
                          struct ctdb_tcp);
-        ctdb_sock_addr sock;
-    int lock_fd, i;
+        ctdb_sock_addr sock, sock_sctp;
+    int lock_fd, i, j;
      const char *lock_path = VARDIR "/run/ctdb/.socket_lock";
      struct flock lock;
      int one = 1;
@@ -327,18 +330,18 @@ static int ctdb_tcp_listen_automatic(struct 
ctdb_context *ctdb)
          }
          ZERO_STRUCT(sock);
          if (ctdb_tcp_get_address(ctdb,
-                ctdb->nodes[i]->address.address,
+                ctdb->nodes[i]->address[0].address,
                  &sock) != 0) {
              continue;
          }

          switch (sock.sa.sa_family) {
          case AF_INET:
-            sock.ip.sin_port = htons(ctdb->nodes[i]->address.port);
+            sock.ip.sin_port = htons(ctdb->nodes[i]->address[0].port);
              sock_size = sizeof(sock.ip);
              break;
          case AF_INET6:
-            sock.ip6.sin6_port = htons(ctdb->nodes[i]->address.port);
+            sock.ip6.sin6_port = htons(ctdb->nodes[i]->address[0].port);
              sock_size = sizeof(sock.ip6);
              break;
          default:
@@ -350,7 +353,7 @@ static int ctdb_tcp_listen_automatic(struct 
ctdb_context *ctdb)
          sock.ip.sin_len = sock_size;
  #endif

-        ctcp->listen_fd = socket(sock.sa.sa_family, SOCK_STREAM, 
IPPROTO_TCP);
+        ctcp->listen_fd = socket(sock.sa.sa_family, SOCK_STREAM, 
ctdb->protocol);
          if (ctcp->listen_fd == -1) {
              ctdb_set_error(ctdb, "socket failed\n");
              continue;
@@ -377,17 +380,51 @@ static int ctdb_tcp_listen_automatic(struct 
ctdb_context *ctdb)
          DEBUG(DEBUG_CRIT,("Unable to bind to any of the node addresses 
- giving up\n"));
          goto failed;
      }
-    ctdb->address.address = talloc_strdup(ctdb, 
ctdb->nodes[i]->address.address);
-    ctdb->address.port    = ctdb->nodes[i]->address.port;
+    ctdb->address[0].address = talloc_strdup(ctdb, 
ctdb->nodes[i]->address[0].address);
+    ctdb->address[0].port    = ctdb->nodes[i]->address[0].port;
      ctdb->name = talloc_asprintf(ctdb, "%s:%u",
-                     ctdb->address.address,
-                     ctdb->address.port);
+                     ctdb->address[0].address,
+                     ctdb->address[0].port);
      ctdb->pnn = ctdb->nodes[i]->pnn;
      DEBUG(DEBUG_INFO,("ctdb chose network address %s:%u pnn %u\n",
-         ctdb->address.address,
-         ctdb->address.port,
+         ctdb->address[0].address,
+         ctdb->address[0].port,
           ctdb->pnn));

+    if (ctdb->protocol == IPPROTO_SCTP) {
+        /* try to add any additional addresses specified */
+        for (j = 1; j < CTDB_SCTP_MAX_NETS; j++) {
+            if (!ctdb->nodes[i]->address[j].address)
+                break;
+            ZERO_STRUCT(sock_sctp);
+            if (ctdb_tcp_get_address(ctdb,
+                    ctdb->nodes[i]->address[j].address,
+                    &sock_sctp) != 0) {
+                continue;
+            }
+            if (sock_sctp.sa.sa_family != sock.sa.sa_family) {
+                DEBUG(DEBUG_ERR, (__location__ "Cannot mix IPv4 and 
IPv6 addresses, skipping %s\n",
+                        ctdb->nodes[i]->address[j].address));
+                continue;
+            }
+            switch (sock_sctp.sa.sa_family) {
+            case AF_INET:
+                sock_sctp.ip.sin_port = 
htons(ctdb->nodes[i]->address[j].port);
+                break;
+            case AF_INET6:
+                sock_sctp.ip6.sin6_port = 
htons(ctdb->nodes[i]->address[j].port);
+                break;
+            }
+            if (sctp_bindx(ctcp->listen_fd,
+                       (struct sockaddr *)&sock_sctp, 1,
+                       SCTP_BINDX_ADD_ADDR) != 0) {
+                DEBUG(DEBUG_ERR, (__location__ "sctp_bindx failed for 
%s\n",
+                    ctdb->nodes[i]->address[j].address));
+                continue;
+            }
+        }
+    }
+
      if (listen(ctcp->listen_fd, 10) == -1) {
          goto failed;
      }
@@ -415,30 +452,30 @@ int ctdb_tcp_listen(struct ctdb_context *ctdb)
  {
      struct ctdb_tcp *ctcp = talloc_get_type(ctdb->private_data,
                          struct ctdb_tcp);
-        ctdb_sock_addr sock;
-    int sock_size;
+        ctdb_sock_addr sock, sock_sctp;
+    int sock_size, i;
      int one = 1;
      struct tevent_fd *fde;

      /* we can either auto-bind to the first available address, or we can
         use a specified address */
-    if (!ctdb->address.address) {
+    if (!ctdb->address[0].address) {
          return ctdb_tcp_listen_automatic(ctdb);
      }

      ZERO_STRUCT(sock);
-    if (ctdb_tcp_get_address(ctdb, ctdb->address.address,
+    if (ctdb_tcp_get_address(ctdb, ctdb->address[0].address,
                   &sock) != 0) {
          goto failed;
      }

      switch (sock.sa.sa_family) {
      case AF_INET:
-        sock.ip.sin_port = htons(ctdb->address.port);
+        sock.ip.sin_port = htons(ctdb->address[0].port);
          sock_size = sizeof(sock.ip);
          break;
      case AF_INET6:
-        sock.ip6.sin6_port = htons(ctdb->address.port);
+        sock.ip6.sin6_port = htons(ctdb->address[0].port);
          sock_size = sizeof(sock.ip6);
          break;
      default:
@@ -450,7 +487,7 @@ int ctdb_tcp_listen(struct ctdb_context *ctdb)
      sock.ip.sin_len = sock_size;
  #endif

-    ctcp->listen_fd = socket(sock.sa.sa_family, SOCK_STREAM, IPPROTO_TCP);
+    ctcp->listen_fd = socket(sock.sa.sa_family, SOCK_STREAM, 
ctdb->protocol);
      if (ctcp->listen_fd == -1) {
          ctdb_set_error(ctdb, "socket failed\n");
          return -1;
@@ -465,6 +502,38 @@ int ctdb_tcp_listen(struct ctdb_context *ctdb)
          goto failed;
      }

+    if (ctdb->protocol == IPPROTO_SCTP) {
+        /* try to add any additional addresses specified */
+        for (i = 1; i < CTDB_SCTP_MAX_NETS; i++) {
+            if (!ctdb->address[i].address)
+                break;
+            ZERO_STRUCT(sock_sctp);
+            if (ctdb_tcp_get_address(ctdb, ctdb->address[i].address,
+                         &sock_sctp) != 0) {
+                continue;
+            }
+            if (sock_sctp.sa.sa_family != sock.sa.sa_family) {
+                DEBUG(DEBUG_ERR, (__location__ "Cannot mix IPv4 and 
IPv6 addresses, skipping %s\n",
+                    ctdb->address[i].address));
+                continue;
+            }
+            switch (sock_sctp.sa.sa_family) {
+            case AF_INET:
+                sock_sctp.ip.sin_port = htons(ctdb->address[i].port);
+                break;
+            case AF_INET6:
+                sock_sctp.ip6.sin6_port = htons(ctdb->address[i].port);
+                break;
+            }
+            if (sctp_bindx(ctcp->listen_fd,(struct sockaddr *)&sock_sctp,
+                       1, SCTP_BINDX_ADD_ADDR) != 0) {
+                DEBUG(DEBUG_ERR, (__location__ "sctp_bindx failed for 
%s\n",
+                    ctdb->address[i].address));
+                continue;
+            }
+        }
+    }
+
      if (listen(ctcp->listen_fd, 10) == -1) {
          goto failed;
      }
diff --git a/tcp/tcp_init.c b/tcp/tcp_init.c
index a65e732..8ab94c4 100644
--- a/tcp/tcp_init.c
+++ b/tcp/tcp_init.c
@@ -92,7 +92,7 @@ static int ctdb_tcp_connect_node(struct ctdb_node *node)

      /* startup connection to the other server - will happen on
         next event loop */
-    if (!ctdb_same_address(&ctdb->address, &node->address)) {
+    if (!ctdb_same_address(&ctdb->address[0], &node->address[0])) {
          tnode->connect_te = event_add_timed(ctdb->ev, tnode,
                              timeval_zero(),
                              ctdb_tcp_node_connect, node);
@@ -190,6 +190,11 @@ int ctdb_tcp_init(struct ctdb_context *ctdb)
      ctcp = talloc_zero(ctdb, struct ctdb_tcp);
      CTDB_NO_MEMORY(ctdb, ctcp);

+    if (strcmp(ctdb->transport, "sctp") == 0) {
+        ctdb->protocol = IPPROTO_SCTP;
+    } else {
+        ctdb->protocol = IPPROTO_TCP;    /* default */
+    }
      ctcp->listen_fd = -1;
      ctcp->ctdb      = ctdb;
      ctdb->private_data = ctcp;
diff --git a/tests/src/ctdb_test_stubs.c b/tests/src/ctdb_test_stubs.c
index 456b1fd..c545577 100644
--- a/tests/src/ctdb_test_stubs.c
+++ b/tests/src/ctdb_test_stubs.c
@@ -17,6 +17,8 @@
     along with this program; if not, see <http://www.gnu.org/licenses/>.
  */

+#include <netinet/sctp.h>
+
  /* Read a nodemap from stdin.  Each line looks like:
   *  <PNN> <FLAGS> [RECMASTER] [CURRENT]
   * EOF or a blank line terminates input.
@@ -95,8 +97,8 @@ void ctdb_test_stubs_read_nodemap(struct ctdb_context 
*ctdb)
          ctdb->nodes[ctdb->num_nodes]->ctdb = ctdb;
          ctdb->nodes[ctdb->num_nodes]->name = "fakectdb";
          ctdb->nodes[ctdb->num_nodes]->pnn = pnn;
-        ctdb->nodes[ctdb->num_nodes]->address.address = ip;
-        ctdb->nodes[ctdb->num_nodes]->address.port = 0;
+        ctdb->nodes[ctdb->num_nodes]->address[0].address = ip;
+        ctdb->nodes[ctdb->num_nodes]->address[0].port = 0;
          ctdb->nodes[ctdb->num_nodes]->flags = flags;
          ctdb->num_nodes++;
      }
@@ -352,12 +354,12 @@ ctdb_control_getnodemap(struct ctdb_context *ctdb, 
uint32_t opcode, TDB_DATA ind
      node_map = (struct ctdb_node_map *)outdata->dptr;
      node_map->num = num_nodes;
      for (i=0; i<num_nodes; i++) {
-        if (parse_ip(ctdb->nodes[i]->address.address,
+        if (parse_ip(ctdb->nodes[i]->address[0].address,
                   NULL, /* TODO: pass in the correct interface here*/
                   0,
                   &node_map->nodes[i].addr) == 0)
          {
-            DEBUG(DEBUG_ERR, (__location__ " Failed to parse %s into a 
sockaddr\n", ctdb->nodes[i]->address.address));
+            DEBUG(DEBUG_ERR, (__location__ " Failed to parse %s into a 
sockaddr\n", ctdb->nodes[i]->address[0].address));
          }

          node_map->nodes[i].pnn   = ctdb->nodes[i]->pnn;
-- 
1.8.3.1





More information about the samba-technical mailing list