Start implementing support for ipv6.
authorRonnie Sahlberg <ronniesahlberg@gmail.com>
Wed, 14 May 2008 05:47:47 +0000 (15:47 +1000)
committerRonnie Sahlberg <ronniesahlberg@gmail.com>
Wed, 14 May 2008 05:47:47 +0000 (15:47 +1000)
This enhances the framework for sending tcp tickles to be able to send ipv6 tickles as well.

Since we can not use one single RAW socket to send both handcrafted ipv4 and ipv6 packets, instead of always opening TWO sockets, one ipv4 and one ipv6 we get rid of the helper ctdb_sys_open_sending_socket() and just open (and close)  a raw socket of the appropriate type inside ctdb_sys_send_tcp().
We know which type of socket v4/v6 to use based on the sin_family of the destination address.

Since ctdb_sys_send_tcp() opens its own socket  we no longer nede to pass a socket
descriptor as a parameter.  Get rid of this redundant parameter and fixup all callers.

common/ctdb_util.c
common/system_aix.c
common/system_linux.c
include/ctdb_private.h
server/ctdb_takeover.c
tools/ctdb.c

index 8ae4df7c4d8edfcac57c73c5398a8d23aa6dc72f..cb535115b422fbe7fbb4e3aa3cb7eaa0afac09ea 100644 (file)
@@ -296,18 +296,78 @@ static bool parse_ip_num(const char *s, struct in_addr *addr, unsigned *num, con
 }
 
 
+static bool parse_ipv4(const char *s, unsigned port, ctdb_sock_addr *saddr)
+{
+       saddr->ip.sin_family = AF_INET;
+       saddr->ip.sin_port   = htons(port);
+
+       if (inet_pton(AF_INET, s, &saddr->ip.sin_addr) != 1) {
+               DEBUG(DEBUG_ERR, (__location__ " Failed to translate %s into sin_addr\n", s));
+               return false;
+       }
+
+       return true;
+}
+
+static bool parse_ipv6(const char *s, unsigned port, ctdb_sock_addr *saddr)
+{
+       saddr->ip6.sin6_family   = AF_INET6;
+       saddr->ip6.sin6_port     = htons(port);
+       saddr->ip6.sin6_flowinfo = 0;
+       saddr->ip6.sin6_scope_id = 0;
+
+       if (inet_pton(AF_INET6, s, &saddr->ip6.sin6_addr) != 1) {
+               DEBUG(DEBUG_ERR, (__location__ " Failed to translate %s into sin6_addr\n", s));
+               return false;
+       }
+
+       return true;
+}
 /*
   parse a ip:port pair
  */
-bool parse_ip_port(const char *s, struct sockaddr_in *ip)
+bool parse_ip_port(const char *addr, ctdb_sock_addr *saddr)
 {
+       TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+       char *s, *p;
        unsigned port;
-       if (!parse_ip_num(s, &ip->sin_addr, &port, ':')) {
+       char *endp = NULL;
+       bool ret;
+
+       s = talloc_strdup(tmp_ctx, addr);
+       if (s == NULL) {
+               DEBUG(DEBUG_ERR, (__location__ " Failed strdup()\n"));
+               talloc_free(tmp_ctx);
                return false;
        }
-       ip->sin_family = AF_INET;
-       ip->sin_port   = htons(port);
-       return true;
+
+       p = rindex(s, ':');
+       if (p == NULL) {
+               DEBUG(DEBUG_ERR, (__location__ " This addr: %s does not contain a port number\n", s));
+               talloc_free(tmp_ctx);
+               return false;
+       }
+
+       port = strtoul(p+1, &endp, 10);
+       if (endp == NULL || *endp != 0) {
+               /* trailing garbage */
+               DEBUG(DEBUG_ERR, (__location__ " Trailing garbage after the port in %s\n", s));
+               talloc_free(tmp_ctx);
+               return false;
+       }
+       *p = 0;
+
+
+       /* now is this a ipv4 or ipv6 address ?*/
+       p = index(s, ':');
+       if (p == NULL) {
+               ret = parse_ipv4(s, port, saddr);
+       } else {
+               ret = parse_ipv6(s, port, saddr);
+       }
+
+       talloc_free(tmp_ctx);
+       return ret;
 }
 
 /*
index 50f1418061833db878860ba552f500e96a39635c..d455ac73dd7bb62577598be3fb649d4f594cbf4f 100644 (file)
@@ -31,6 +31,9 @@
 
 
 
+#if 0
+This function is no longer used and its code should be moved into
+send tcp packet   after that function has been enhanced to do ipv6 as well.
 
 /* This function is used to open a raw socket to send tickles from
  */
@@ -59,7 +62,7 @@ int ctdb_sys_open_sending_socket(void)
 
        return s;
 }
-
+#endif
 
 /*
   uint16 checksum for n bytes
index 9d9c387c5d688a282dd2e39fd9867f49d21762e8..fb50c6b29b221e7d52859dd501f1228f4ba5f1fd 100644 (file)
@@ -211,48 +211,122 @@ static uint16_t tcp_checksum6(uint16_t *data, size_t n, struct ip6_hdr *ip6)
   This can also be used to send RST segments (if rst is true) and also
   if correct seq and ack numbers are provided.
  */
-int ctdb_sys_send_tcp(int s,
-                     const struct sockaddr_in *dest, 
-                     const struct sockaddr_in *src,
+int ctdb_sys_send_tcp(const ctdb_sock_addr *dest, 
+                     const ctdb_sock_addr *src,
                      uint32_t seq, uint32_t ack, int rst)
 {
+       int s;
        int ret;
+       uint32_t one = 1;
+       uint16_t tmpport;
+       ctdb_sock_addr *tmpdest;
        struct {
                struct iphdr ip;
                struct tcphdr tcp;
-       } pkt;
-
-       /* for now, we only handle AF_INET addresses */
-       if (src->sin_family != AF_INET || dest->sin_family != AF_INET) {
-               DEBUG(DEBUG_CRIT,(__location__ " not an ipv4 address\n"));
-               return -1;
-       }
+       } ip4pkt;
+       struct {
+               struct ip6_hdr ip6;
+               struct tcphdr tcp;
+       } ip6pkt;
+
+       switch (src->ip.sin_family) {
+       case AF_INET:
+               ZERO_STRUCT(ip4pkt);
+               ip4pkt.ip.version  = 4;
+               ip4pkt.ip.ihl      = sizeof(ip4pkt.ip)/4;
+               ip4pkt.ip.tot_len  = htons(sizeof(ip4pkt));
+               ip4pkt.ip.ttl      = 255;
+               ip4pkt.ip.protocol = IPPROTO_TCP;
+               ip4pkt.ip.saddr    = src->ip.sin_addr.s_addr;
+               ip4pkt.ip.daddr    = dest->ip.sin_addr.s_addr;
+               ip4pkt.ip.check    = 0;
+
+               ip4pkt.tcp.source   = src->ip.sin_port;
+               ip4pkt.tcp.dest     = dest->ip.sin_port;
+               ip4pkt.tcp.seq      = seq;
+               ip4pkt.tcp.ack_seq  = ack;
+               ip4pkt.tcp.ack      = 1;
+               if (rst) {
+                       ip4pkt.tcp.rst      = 1;
+               }
+               ip4pkt.tcp.doff     = sizeof(ip4pkt.tcp)/4;
+               /* this makes it easier to spot in a sniffer */
+               ip4pkt.tcp.window   = htons(1234);
+               ip4pkt.tcp.check    = tcp_checksum((uint16_t *)&ip4pkt.tcp, sizeof(ip4pkt.tcp), &ip4pkt.ip);
+
+               /* open a raw socket to send this segment from */
+               s = socket(AF_INET, SOCK_RAW, htons(IPPROTO_RAW));
+               if (s == -1) {
+                       DEBUG(DEBUG_CRIT,(__location__ " failed to open raw socket (%s)\n",
+                                strerror(errno)));
+                       return -1;
+               }
+
+               ret = setsockopt(s, SOL_IP, IP_HDRINCL, &one, sizeof(one));
+               if (ret != 0) {
+                       DEBUG(DEBUG_CRIT,(__location__ " failed to setup IP headers (%s)\n",
+                                strerror(errno)));
+                       close(s);
+                       return -1;
+               }
+
+               set_nonblocking(s);
+               set_close_on_exec(s);
+
+               ret = sendto(s, &ip4pkt, sizeof(ip4pkt), 0, &dest->ip, sizeof(dest->ip));
+               close(s);
+               if (ret != sizeof(ip4pkt)) {
+                       DEBUG(DEBUG_CRIT,(__location__ " failed sendto (%s)\n", strerror(errno)));
+                       return -1;
+               }
+               break;
+       case AF_INET6:
+               ZERO_STRUCT(ip6pkt);
+               ip6pkt.ip6.ip6_vfc  = 0x60;
+               ip6pkt.ip6.ip6_plen = 20;
+               ip6pkt.ip6.ip6_nxt  = IPPROTO_TCP;
+               ip6pkt.ip6.ip6_hlim = 64;
+               ip6pkt.ip6.ip6_src  = src->ip6.sin6_addr;
+               ip6pkt.ip6.ip6_dst  = dest->ip6.sin6_addr;
+
+               ip6pkt.tcp.source   = src->ip6.sin6_port;
+               ip6pkt.tcp.dest     = dest->ip6.sin6_port;
+               ip6pkt.tcp.seq      = seq;
+               ip6pkt.tcp.ack_seq  = ack;
+               ip6pkt.tcp.ack      = 1;
+               if (rst) {
+                       ip6pkt.tcp.rst      = 1;
+               }
+               ip6pkt.tcp.doff     = sizeof(ip6pkt.tcp)/4;
+               /* this makes it easier to spot in a sniffer */
+               ip6pkt.tcp.window   = htons(1234);
+               ip6pkt.tcp.check    = tcp_checksum6((uint16_t *)&ip6pkt.tcp, sizeof(ip6pkt.tcp), &ip6pkt.ip6);
+
+               s = socket(PF_INET6, SOCK_RAW, IPPROTO_RAW);
+               if (s == -1) {
+                       DEBUG(DEBUG_CRIT, (__location__ " Failed to open sending socket\n"));
+                       return -1;
+
+               }
+               /* sendto() dont like if the port is set and the socket is
+                  in raw mode.
+               */
+               tmpdest = discard_const(dest);
+               tmpport = tmpdest->ip6.sin6_port;
+
+               tmpdest->ip6.sin6_port = 0;
+               ret = sendto(s, &ip6pkt, sizeof(ip6pkt), 0, &dest->ip6, sizeof(dest->ip6));
+               tmpdest->ip6.sin6_port = tmpport;
+               close(s);
 
-       ZERO_STRUCT(pkt);
-       pkt.ip.version  = 4;
-       pkt.ip.ihl      = sizeof(pkt.ip)/4;
-       pkt.ip.tot_len  = htons(sizeof(pkt));
-       pkt.ip.ttl      = 255;
-       pkt.ip.protocol = IPPROTO_TCP;
-       pkt.ip.saddr    = src->sin_addr.s_addr;
-       pkt.ip.daddr    = dest->sin_addr.s_addr;
-       pkt.ip.check    = 0;
-
-       pkt.tcp.source   = src->sin_port;
-       pkt.tcp.dest     = dest->sin_port;
-       pkt.tcp.seq      = seq;
-       pkt.tcp.ack_seq  = ack;
-       pkt.tcp.ack      = 1;
-       if (rst) {
-               pkt.tcp.rst      = 1;
-       }
-       pkt.tcp.doff     = sizeof(pkt.tcp)/4;
-       pkt.tcp.window   = htons(1234); /* this makes it easier to spot in a sniffer */
-       pkt.tcp.check    = tcp_checksum((uint16_t *)&pkt.tcp, sizeof(pkt.tcp), &pkt.ip);
+               if (ret != sizeof(ip6pkt)) {
+                       DEBUG(DEBUG_CRIT,(__location__ " failed sendto (%s)\n", strerror(errno)));
+                       return -1;
+               }
+               break;
 
-       ret = sendto(s, &pkt, sizeof(pkt), 0, dest, sizeof(*dest));
-       if (ret != sizeof(pkt)) {
-               DEBUG(DEBUG_CRIT,(__location__ " failed sendto (%s)\n", strerror(errno)));
+       default:
+               DEBUG(DEBUG_CRIT,(__location__ " not an ipv4/v6 address\n"));
                return -1;
        }
 
@@ -314,34 +388,6 @@ int ctdb_sys_close_capture_socket(void *private_data)
        return 0;
 }
 
-/* 
-   This function is used to open a raw socket to send tickles from
- */
-int ctdb_sys_open_sending_socket(void)
-{
-       int s, ret;
-       uint32_t one = 1;
-
-       s = socket(AF_INET, SOCK_RAW, htons(IPPROTO_RAW));
-       if (s == -1) {
-               DEBUG(DEBUG_CRIT,(__location__ " failed to open raw socket (%s)\n",
-                        strerror(errno)));
-               return -1;
-       }
-
-       ret = setsockopt(s, SOL_IP, IP_HDRINCL, &one, sizeof(one));
-       if (ret != 0) {
-               DEBUG(DEBUG_CRIT,(__location__ " failed to setup IP headers (%s)\n",
-                        strerror(errno)));
-               close(s);
-               return -1;
-       }
-
-       set_nonblocking(s);
-       set_close_on_exec(s);
-
-       return s;
-}
 
 /*
   called when the raw socket becomes readable
index eaba40ea0027084234830a418f22455c41176e26..4eccc84198564582d2ca9539d444f90b82d994cc 100644 (file)
@@ -1163,9 +1163,8 @@ int ctdb_ctrl_get_public_ips(struct ctdb_context *ctdb,
 /* from takeover/system.c */
 int ctdb_sys_send_arp(const struct sockaddr_in *saddr, const char *iface);
 bool ctdb_sys_have_ip(struct sockaddr_in ip);
-int ctdb_sys_send_tcp(int fd,
-                     const struct sockaddr_in *dest, 
-                     const struct sockaddr_in *src,
+int ctdb_sys_send_tcp(const ctdb_sock_addr *dest, 
+                     const ctdb_sock_addr *src,
                      uint32_t seq, uint32_t ack, int rst);
 
 int ctdb_set_public_addresses(struct ctdb_context *ctdb, const char *alist);
@@ -1220,11 +1219,10 @@ int ctdb_ctrl_get_all_tunables(struct ctdb_context *ctdb,
 
 void ctdb_start_freeze(struct ctdb_context *ctdb);
 
-bool parse_ip_port(const char *s, struct sockaddr_in *ip);
+bool parse_ip_port(const char *s, ctdb_sock_addr *saddr);
 
 int ctdb_sys_open_capture_socket(const char *iface, void **private_data);
 int ctdb_sys_close_capture_socket(void *private_data);
-int ctdb_sys_open_sending_socket(void);
 int ctdb_sys_read_tcp_packet(int s, void *private_data, struct sockaddr_in *src, struct sockaddr_in *dst,
                             uint32_t *ack_seq, uint32_t *seq);
 
index 26f75bf922876a1e1f67defd1f9109008c3a2f3c..e3f0a83405f3a5d9b1bc60e2758aa922379b4d06 100644 (file)
@@ -69,7 +69,7 @@ static void ctdb_control_send_arp(struct event_context *ev, struct timed_event *
 {
        struct ctdb_takeover_arp *arp = talloc_get_type(private_data, 
                                                        struct ctdb_takeover_arp);
-       int i, s, ret;
+       int i, ret;
        struct ctdb_tcp_array *tcparray;
 
 
@@ -78,12 +78,6 @@ static void ctdb_control_send_arp(struct event_context *ev, struct timed_event *
                DEBUG(DEBUG_CRIT,(__location__ " sending of arp failed (%s)\n", strerror(errno)));
        }
 
-       s = ctdb_sys_open_sending_socket();
-       if (s == -1) {
-               DEBUG(DEBUG_CRIT,(__location__ " failed to open raw socket for sending tickles\n"));
-               return;
-       }
-
        tcparray = arp->tcparray;
        if (tcparray) {
                for (i=0;i<tcparray->num;i++) {
@@ -91,8 +85,10 @@ static void ctdb_control_send_arp(struct event_context *ev, struct timed_event *
                                 (unsigned)ntohs(tcparray->connections[i].daddr.sin_port), 
                                 inet_ntoa(tcparray->connections[i].saddr.sin_addr),
                                 (unsigned)ntohs(tcparray->connections[i].saddr.sin_port)));
-                       ret = ctdb_sys_send_tcp(s, &tcparray->connections[i].saddr, 
-                                               &tcparray->connections[i].daddr, 0, 0, 0);
+                       ret = ctdb_sys_send_tcp(
+                               (ctdb_sock_addr *)&tcparray->connections[i].saddr, 
+                               (ctdb_sock_addr *)&tcparray->connections[i].daddr,
+                               0, 0, 0);
                        if (ret != 0) {
                                DEBUG(DEBUG_CRIT,(__location__ " Failed to send tcp tickle ack for %s\n",
                                         inet_ntoa(tcparray->connections[i].saddr.sin_addr)));
@@ -100,7 +96,6 @@ static void ctdb_control_send_arp(struct event_context *ev, struct timed_event *
                }
        }
 
-       close(s);
        arp->count++;
 
        if (arp->count == CTDB_ARP_REPEAT) {
@@ -1266,7 +1261,6 @@ struct ctdb_kill_tcp {
        struct ctdb_vnn *vnn;
        struct ctdb_context *ctdb;
        int capture_fd;
-       int sending_fd;
        struct fd_event *fde;
        trbt_tree_t *connections;
        void *private_data;
@@ -1338,8 +1332,10 @@ static void capture_tcp_handler(struct event_context *ev, struct fd_event *fde,
         */
        DEBUG(DEBUG_INFO, ("sending a tcp reset to kill connection :%d -> %s:%d\n", ntohs(con->dst.sin_port), inet_ntoa(con->src.sin_addr), ntohs(con->src.sin_port)));
 
-       ctdb_sys_send_tcp(killtcp->sending_fd, &con->dst, 
-                         &con->src, ack_seq, seq, 1);
+       ctdb_sys_send_tcp(
+                       (ctdb_sock_addr *)&con->dst, 
+                       (ctdb_sock_addr *)&con->src,
+                       ack_seq, seq, 1);
        talloc_free(con);
 }
 
@@ -1352,7 +1348,6 @@ static void capture_tcp_handler(struct event_context *ev, struct fd_event *fde,
 static void tickle_connection_traverse(void *param, void *data)
 {
        struct ctdb_killtcp_con *con = talloc_get_type(data, struct ctdb_killtcp_con);
-       struct ctdb_kill_tcp *killtcp = talloc_get_type(param, struct ctdb_kill_tcp);
 
        /* have tried too many times, just give up */
        if (con->count >= 5) {
@@ -1362,7 +1357,10 @@ static void tickle_connection_traverse(void *param, void *data)
 
        /* othervise, try tickling it again */
        con->count++;
-       ctdb_sys_send_tcp(killtcp->sending_fd, &con->dst, &con->src, 0, 0, 0);
+       ctdb_sys_send_tcp(
+               (ctdb_sock_addr *)&con->dst,
+               (ctdb_sock_addr *)&con->src,
+               0, 0, 0);
 }
 
 
@@ -1376,7 +1374,7 @@ static void ctdb_tickle_sentenced_connections(struct event_context *ev, struct t
 
 
        /* loop over all connections sending tickle ACKs */
-       trbt_traversearray32(killtcp->connections, KILLTCP_KEYLEN, tickle_connection_traverse, killtcp);
+       trbt_traversearray32(killtcp->connections, KILLTCP_KEYLEN, tickle_connection_traverse, NULL);
 
 
        /* If there are no more connections to kill we can remove the
@@ -1399,10 +1397,6 @@ static void ctdb_tickle_sentenced_connections(struct event_context *ev, struct t
  */
 static int ctdb_killtcp_destructor(struct ctdb_kill_tcp *killtcp)
 {
-       if (killtcp->sending_fd != -1) {
-               close(killtcp->sending_fd);
-               killtcp->sending_fd = -1;
-       }
        killtcp->vnn->killtcp = NULL;
        return 0;
 }
@@ -1459,7 +1453,6 @@ static int ctdb_killtcp_add_connection(struct ctdb_context *ctdb,
                killtcp->vnn         = vnn;
                killtcp->ctdb        = ctdb;
                killtcp->capture_fd  = -1;
-               killtcp->sending_fd  = -1;
                killtcp->connections = trbt_create(killtcp, 0);
 
                vnn->killtcp         = killtcp;
@@ -1483,17 +1476,6 @@ static int ctdb_killtcp_add_connection(struct ctdb_context *ctdb,
                        KILLTCP_KEYLEN, killtcp_key(&con->dst, &con->src),
                        add_killtcp_callback, con);
 
-       /* 
-          If we dont have a socket to send from yet we must create it
-        */
-       if (killtcp->sending_fd == -1) {
-               killtcp->sending_fd = ctdb_sys_open_sending_socket();
-               if (killtcp->sending_fd == -1) {
-                       DEBUG(DEBUG_CRIT,(__location__ " Failed to open sending socket for killtcp\n"));
-                       goto failed;
-               }
-       }
-
        /* 
           If we dont have a socket to listen on yet we must create it
         */
@@ -1519,7 +1501,10 @@ static int ctdb_killtcp_add_connection(struct ctdb_context *ctdb,
        }
 
        /* tickle him once now */
-       ctdb_sys_send_tcp(killtcp->sending_fd, &con->dst, &con->src, 0, 0, 0);
+       ctdb_sys_send_tcp(
+               (ctdb_sock_addr *)&con->dst,
+               (ctdb_sock_addr *)&con->src,
+               0, 0, 0);
 
        return 0;
 
index 6f78b6540841fc0732f8aa3dc8eea8988cd33eea..06ebf131b0e81b1010ae8e3f5055922e60a8ce82 100644 (file)
@@ -808,12 +808,12 @@ static int kill_tcp(struct ctdb_context *ctdb, int argc, const char **argv)
                usage();
        }
 
-       if (!parse_ip_port(argv[0], &killtcp.src)) {
+       if (!parse_ip_port(argv[0], (ctdb_sock_addr *)&killtcp.src)) {
                DEBUG(DEBUG_ERR, ("Bad IP:port '%s'\n", argv[0]));
                return -1;
        }
 
-       if (!parse_ip_port(argv[1], &killtcp.dst)) {
+       if (!parse_ip_port(argv[1], (ctdb_sock_addr *)&killtcp.dst)) {
                DEBUG(DEBUG_ERR, ("Bad IP:port '%s'\n", argv[1]));
                return -1;
        }
@@ -963,8 +963,8 @@ static int getsrvids(struct ctdb_context *ctdb, int argc, const char **argv)
  */
 static int tickle_tcp(struct ctdb_context *ctdb, int argc, const char **argv)
 {
-       int s, ret;
-       struct sockaddr_in src, dst;
+       int ret;
+       ctdb_sock_addr  src, dst;
 
        if (argc < 2) {
                usage();
@@ -980,14 +980,7 @@ static int tickle_tcp(struct ctdb_context *ctdb, int argc, const char **argv)
                return -1;
        }
 
-       s = ctdb_sys_open_sending_socket();
-       if (s == -1) {
-               DEBUG(DEBUG_ERR, ("Failed to open socket for sending tickle\n"));
-               return 0;
-       }
-
-       ret = ctdb_sys_send_tcp(s, &src, &dst, 0, 0, 0);
-       close(s);
+       ret = ctdb_sys_send_tcp(&src, &dst, 0, 0, 0);
        if (ret==0) {
                return 0;
        }