ctdb-common: Avoid aliasing errors during code optimization
[samba.git] / ctdb / common / system_socket.c
index a6c5f5c093df331f20249614c74b47f2d838b826..bb513508353ee21d239f02664c2cde5d6897e604 100644 (file)
 /*
   uint16 checksum for n bytes
  */
-static uint32_t uint16_checksum(uint16_t *data, size_t n)
+static uint32_t uint16_checksum(uint8_t *data, size_t n)
 {
        uint32_t sum=0;
+       uint16_t value;
+
        while (n>=2) {
-               sum += (uint32_t)ntohs(*data);
-               data++;
+               memcpy(&value, data, 2);
+               sum += (uint32_t)ntohs(value);
+               data += 2;
                n -= 2;
        }
        if (n == 1) {
-               sum += (uint32_t)ntohs(*(uint8_t *)data);
+               sum += (uint32_t)ntohs(*data);
        }
        return sum;
 }
@@ -117,13 +120,13 @@ bool ctdb_sys_have_ip(ctdb_sock_addr *_addr)
 /*
  * simple TCP checksum - assumes data is multiple of 2 bytes long
  */
-static uint16_t ip_checksum(uint16_t *data, size_t n, struct ip *ip)
+static uint16_t ip_checksum(uint8_t *data, size_t n, struct ip *ip)
 {
        uint32_t sum = uint16_checksum(data, n);
        uint16_t sum2;
 
-       sum += uint16_checksum((uint16_t *)&ip->ip_src, sizeof(ip->ip_src));
-       sum += uint16_checksum((uint16_t *)&ip->ip_dst, sizeof(ip->ip_dst));
+       sum += uint16_checksum((uint8_t *)&ip->ip_src, sizeof(ip->ip_src));
+       sum += uint16_checksum((uint8_t *)&ip->ip_dst, sizeof(ip->ip_dst));
        sum += ip->ip_p + n;
        sum = (sum & 0xFFFF) + (sum >> 16);
        sum = (sum & 0xFFFF) + (sum >> 16);
@@ -135,22 +138,22 @@ static uint16_t ip_checksum(uint16_t *data, size_t n, struct ip *ip)
        return sum2;
 }
 
-static uint16_t ip6_checksum(uint16_t *data, size_t n, struct ip6_hdr *ip6)
+static uint16_t ip6_checksum(uint8_t *data, size_t n, struct ip6_hdr *ip6)
 {
        uint16_t phdr[3];
        uint32_t sum = 0;
        uint16_t sum2;
        uint32_t len;
 
-       sum += uint16_checksum((uint16_t *)(void *)&ip6->ip6_src, 16);
-       sum += uint16_checksum((uint16_t *)(void *)&ip6->ip6_dst, 16);
+       sum += uint16_checksum((uint8_t *)&ip6->ip6_src, 16);
+       sum += uint16_checksum((uint8_t *)&ip6->ip6_dst, 16);
 
        len = htonl(n);
        phdr[0] = len & UINT16_MAX;
        phdr[1] = (len >> 16) & UINT16_MAX;
        /* ip6_nxt is only 8 bits, so fits comfortably into a uint16_t */
        phdr[2] = htons(ip6->ip6_nxt);
-       sum += uint16_checksum(phdr, sizeof(phdr));
+       sum += uint16_checksum((uint8_t *)phdr, sizeof(phdr));
 
        sum += uint16_checksum(data, n);
 
@@ -216,12 +219,12 @@ static int arp_build(uint8_t *buffer,
        memcpy(eh->ether_shost, hwaddr, ETH_ALEN);
        eh->ether_type = htons(ETHERTYPE_ARP);
 
-       ea = (struct ether_arp *)&buffer[sizeof(struct ether_header)];
+       ea = (struct ether_arp *)(buffer + sizeof(struct ether_header));
        ah = &ea->ea_hdr;
        ah->ar_hrd = htons(ARPHRD_ETHER);
        ah->ar_pro = htons(ETH_P_IP);
        ah->ar_hln = ETH_ALEN;
-       ah->ar_pln = 4;
+       ah->ar_pln = sizeof(ea->arp_spa);
 
        if (! reply) {
                ah->ar_op  = htons(ARPOP_REQUEST);
@@ -272,14 +275,15 @@ static int ip6_na_build(uint8_t *buffer,
         * Ethernet multicast: 33:33:00:00:00:01 (see RFC2464,
         * section 7) - note memset 0 above!
         */
-       eh->ether_dhost[0] = eh->ether_dhost[1] = 0x33;
+       eh->ether_dhost[0] = 0x33;
+       eh->ether_dhost[1] = 0x33;
        eh->ether_dhost[5] = 0x01;
        memcpy(eh->ether_shost, hwaddr, ETH_ALEN);
        eh->ether_type = htons(ETHERTYPE_IP6);
 
-       ip6 = (struct ip6_hdr *)(eh+1);
-       ip6->ip6_vfc  = 0x60;
-       ip6->ip6_plen = htons(sizeof(*nd_na) +
+       ip6 = (struct ip6_hdr *)(buffer + sizeof(struct ether_header));
+       ip6->ip6_vfc  = 6 << 4;
+       ip6->ip6_plen = htons(sizeof(struct nd_neighbor_advert) +
                              sizeof(struct nd_opt_hdr) +
                              ETH_ALEN);
        ip6->ip6_nxt  = IPPROTO_ICMPV6;
@@ -292,20 +296,30 @@ static int ip6_na_build(uint8_t *buffer,
                return EIO;
        }
 
-       nd_na = (struct nd_neighbor_advert *)(ip6+1);
+       nd_na = (struct nd_neighbor_advert *)(buffer +
+                                             sizeof(struct ether_header) +
+                                             sizeof(struct ip6_hdr));
        nd_na->nd_na_type = ND_NEIGHBOR_ADVERT;
        nd_na->nd_na_code = 0;
        nd_na->nd_na_flags_reserved = ND_NA_FLAG_OVERRIDE;
        nd_na->nd_na_target = addr->sin6_addr;
+
        /* Option: Target link-layer address */
-       nd_oh = (struct nd_opt_hdr *)(nd_na+1);
+       nd_oh = (struct nd_opt_hdr *)(buffer +
+                                     sizeof(struct ether_header) +
+                                     sizeof(struct ip6_hdr) +
+                                     sizeof(struct nd_neighbor_advert));
        nd_oh->nd_opt_type = ND_OPT_TARGET_LINKADDR;
-       nd_oh->nd_opt_len = 1;
+       nd_oh->nd_opt_len = 1;  /* multiple of 8 octets */
 
-       ea = (struct ether_addr *)(nd_oh+1);
+       ea = (struct ether_addr *)(buffer +
+                                  sizeof(struct ether_header) +
+                                  sizeof(struct ip6_hdr) +
+                                  sizeof(struct nd_neighbor_advert) +
+                                  sizeof(struct nd_opt_hdr));
        memcpy(ea, hwaddr, ETH_ALEN);
 
-       nd_na->nd_na_cksum = ip6_checksum((uint16_t *)nd_na,
+       nd_na->nd_na_cksum = ip6_checksum((uint8_t *)nd_na,
                                          ntohs(ip6->ip6_plen),
                                          ip6);
 
@@ -318,9 +332,17 @@ int ctdb_sys_send_arp(const ctdb_sock_addr *addr, const char *iface)
 {
        int s;
        struct sockaddr_ll sall = {0};
-       struct ifreq if_hwaddr = {{{0}}};
+       struct ifreq if_hwaddr = {
+               .ifr_ifru = {
+                       .ifru_flags = 0
+               },
+       };
        uint8_t buffer[MAX(ARP_BUFFER_SIZE, IP6_NA_BUFFER_SIZE)];
-       struct ifreq ifr = {{{0}}};
+       struct ifreq ifr = {
+               .ifr_ifru = {
+                       .ifru_flags = 0
+               },
+       };
        struct ether_addr *hwaddr = NULL;
        struct ether_addr *ether_dhost = NULL;
        size_t len = 0;
@@ -438,7 +460,7 @@ int ctdb_sys_send_arp(const ctdb_sock_addr *addr, const char *iface)
                                   &ether_dhost,
                                   &len);
                if (ret != 0) {
-                       DBG_ERR("Failed to build IPv6 neighbor advertisment\n");
+                       DBG_ERR("Failed to build IPv6 neighbor advertisement\n");
                        goto fail;
                }
 
@@ -483,6 +505,121 @@ int ctdb_sys_send_arp(const ctdb_sock_addr *addr, const char *iface)
 
 #endif /* HAVE_PACKETSOCKET */
 
+
+#define IP4_TCP_BUFFER_SIZE sizeof(struct ip) + \
+                           sizeof(struct tcphdr)
+
+#define IP6_TCP_BUFFER_SIZE sizeof(struct ip6_hdr) + \
+                           sizeof(struct tcphdr)
+
+static int tcp4_build(uint8_t *buf,
+                     size_t buflen,
+                     const struct sockaddr_in *src,
+                     const struct sockaddr_in *dst,
+                     uint32_t seq,
+                     uint32_t ack,
+                     int rst,
+                     size_t *len)
+{
+       size_t l = IP4_TCP_BUFFER_SIZE;
+       struct {
+               struct ip ip;
+               struct tcphdr tcp;
+       } *ip4pkt;
+
+       if (l != sizeof(*ip4pkt)) {
+               return EMSGSIZE;
+       }
+
+       if (buflen < l) {
+               return EMSGSIZE;
+       }
+
+       ip4pkt = (void *)buf;
+       memset(ip4pkt, 0, l);
+
+       ip4pkt->ip.ip_v     = 4;
+       ip4pkt->ip.ip_hl    = sizeof(ip4pkt->ip)/sizeof(uint32_t);
+       ip4pkt->ip.ip_len   = htons(sizeof(ip4pkt));
+       ip4pkt->ip.ip_ttl   = 255;
+       ip4pkt->ip.ip_p     = IPPROTO_TCP;
+       ip4pkt->ip.ip_src.s_addr = src->sin_addr.s_addr;
+       ip4pkt->ip.ip_dst.s_addr = dst->sin_addr.s_addr;
+       ip4pkt->ip.ip_sum   = 0;
+
+       ip4pkt->tcp.th_sport = src->sin_port;
+       ip4pkt->tcp.th_dport = dst->sin_port;
+       ip4pkt->tcp.th_seq   = seq;
+       ip4pkt->tcp.th_ack   = ack;
+       ip4pkt->tcp.th_flags = 0;
+       ip4pkt->tcp.th_flags |= TH_ACK;
+       if (rst) {
+               ip4pkt->tcp.th_flags |= TH_RST;
+       }
+       ip4pkt->tcp.th_off   = sizeof(ip4pkt->tcp)/sizeof(uint32_t);
+       /* this makes it easier to spot in a sniffer */
+       ip4pkt->tcp.th_win   = htons(1234);
+       ip4pkt->tcp.th_sum   = ip_checksum((uint8_t *)&ip4pkt->tcp,
+                                          sizeof(ip4pkt->tcp),
+                                          &ip4pkt->ip);
+
+       *len = l;
+       return 0;
+}
+
+static int tcp6_build(uint8_t *buf,
+                     size_t buflen,
+                     const struct sockaddr_in6 *src,
+                     const struct sockaddr_in6 *dst,
+                     uint32_t seq,
+                     uint32_t ack,
+                     int rst,
+                     size_t *len)
+{
+       size_t l = IP6_TCP_BUFFER_SIZE;
+       struct {
+               struct ip6_hdr ip6;
+               struct tcphdr tcp;
+       } *ip6pkt;
+
+       if (l != sizeof(*ip6pkt)) {
+               return EMSGSIZE;
+       }
+
+       if (buflen < l) {
+               return EMSGSIZE;
+       }
+
+       ip6pkt = (void *)buf;
+       memset(ip6pkt, 0, l);
+
+       ip6pkt->ip6.ip6_vfc  = 6 << 4;
+       ip6pkt->ip6.ip6_plen = htons(sizeof(struct tcphdr));
+       ip6pkt->ip6.ip6_nxt  = IPPROTO_TCP;
+       ip6pkt->ip6.ip6_hlim = 64;
+       ip6pkt->ip6.ip6_src  = src->sin6_addr;
+       ip6pkt->ip6.ip6_dst  = dst->sin6_addr;
+
+       ip6pkt->tcp.th_sport = src->sin6_port;
+       ip6pkt->tcp.th_dport = dst->sin6_port;
+       ip6pkt->tcp.th_seq   = seq;
+       ip6pkt->tcp.th_ack   = ack;
+       ip6pkt->tcp.th_flags = 0;
+       ip6pkt->tcp.th_flags |= TH_ACK;
+       if (rst) {
+               ip6pkt->tcp.th_flags |= TH_RST;
+       }
+       ip6pkt->tcp.th_off    = sizeof(ip6pkt->tcp)/sizeof(uint32_t);
+       /* this makes it easier to spot in a sniffer */
+       ip6pkt->tcp.th_win   = htons(1234);
+       ip6pkt->tcp.th_sum   = ip6_checksum((uint8_t *)&ip6pkt->tcp,
+                                           sizeof(ip6pkt->tcp),
+                                           &ip6pkt->ip6);
+
+       *len = l;
+       return 0;
+}
+
 /*
  * Send tcp segment from the specified IP/port to the specified
  * destination IP/port.
@@ -500,48 +637,28 @@ int ctdb_sys_send_tcp(const ctdb_sock_addr *dest,
                      uint32_t ack,
                      int rst)
 {
-       int s;
+       uint8_t buf[MAX(IP4_TCP_BUFFER_SIZE, IP6_TCP_BUFFER_SIZE)];
+       size_t len = 0;
        int ret;
+       int s;
        uint32_t one = 1;
-       uint16_t tmpport;
-       ctdb_sock_addr *tmpdest;
-       struct {
-               struct ip ip;
-               struct tcphdr tcp;
-       } ip4pkt;
-       struct {
-               struct ip6_hdr ip6;
-               struct tcphdr tcp;
-       } ip6pkt;
+       struct sockaddr_in6 tmpdest = { 0 };
        int saved_errno;
 
        switch (src->ip.sin_family) {
        case AF_INET:
-               ZERO_STRUCT(ip4pkt);
-               ip4pkt.ip.ip_v     = 4;
-               ip4pkt.ip.ip_hl    = sizeof(ip4pkt.ip)/4;
-               ip4pkt.ip.ip_len   = htons(sizeof(ip4pkt));
-               ip4pkt.ip.ip_ttl   = 255;
-               ip4pkt.ip.ip_p     = IPPROTO_TCP;
-               ip4pkt.ip.ip_src.s_addr    = src->ip.sin_addr.s_addr;
-               ip4pkt.ip.ip_dst.s_addr    = dest->ip.sin_addr.s_addr;
-               ip4pkt.ip.ip_sum   = 0;
-
-               ip4pkt.tcp.th_sport = src->ip.sin_port;
-               ip4pkt.tcp.th_dport = dest->ip.sin_port;
-               ip4pkt.tcp.th_seq   = seq;
-               ip4pkt.tcp.th_ack   = ack;
-               ip4pkt.tcp.th_flags = 0;
-               ip4pkt.tcp.th_flags |= TH_ACK;
-               if (rst) {
-                       ip4pkt.tcp.th_flags |= TH_RST;
+               ret = tcp4_build(buf,
+                                sizeof(buf),
+                                &src->ip,
+                                &dest->ip,
+                                seq,
+                                ack,
+                                rst,
+                                &len);
+               if (ret != 0) {
+                       DBG_ERR("Failed to build TCP packet (%d)\n", ret);
+                       return ret;
                }
-               ip4pkt.tcp.th_off   = sizeof(ip4pkt.tcp)/4;
-               /* this makes it easier to spot in a sniffer */
-               ip4pkt.tcp.th_win   = htons(1234);
-               ip4pkt.tcp.th_sum   = ip_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, IPPROTO_RAW);
@@ -559,40 +676,37 @@ int ctdb_sys_send_tcp(const ctdb_sock_addr *dest,
                        return -1;
                }
 
-               ret = sendto(s, &ip4pkt, sizeof(ip4pkt), 0,
+               ret = sendto(s,
+                            buf,
+                            len,
+                            0,
                             (const struct sockaddr *)&dest->ip,
                             sizeof(dest->ip));
                saved_errno = errno;
                close(s);
-               if (ret != sizeof(ip4pkt)) {
+               if (ret == -1) {
                        D_ERR("Failed sendto (%s)\n", strerror(saved_errno));
                        return -1;
                }
+               if ((size_t)ret != len) {
+                       DBG_ERR("Failed sendto - didn't send full packet\n");
+                       return -1;
+               }
                break;
+
        case AF_INET6:
-               ZERO_STRUCT(ip6pkt);
-               ip6pkt.ip6.ip6_vfc  = 0x60;
-               ip6pkt.ip6.ip6_plen = htons(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.th_sport = src->ip6.sin6_port;
-               ip6pkt.tcp.th_dport = dest->ip6.sin6_port;
-               ip6pkt.tcp.th_seq   = seq;
-               ip6pkt.tcp.th_ack   = ack;
-               ip6pkt.tcp.th_flags = 0;
-               ip6pkt.tcp.th_flags |= TH_RST;
-               if (rst) {
-                       ip6pkt.tcp.th_flags |= TH_RST;
+               ret = tcp6_build(buf,
+                                sizeof(buf),
+                                &src->ip6,
+                                &dest->ip6,
+                                seq,
+                                ack,
+                                rst,
+                                &len);
+               if (ret != 0) {
+                       DBG_ERR("Failed to build TCP packet (%d)\n", ret);
+                       return ret;
                }
-               ip6pkt.tcp.th_off    = sizeof(ip6pkt.tcp)/4;
-               /* this makes it easier to spot in a sniffer */
-               ip6pkt.tcp.th_win   = htons(1234);
-               ip6pkt.tcp.th_sum   = ip6_checksum((uint16_t *)&ip6pkt.tcp,
-                                                  sizeof(ip6pkt.tcp),
-                                                  &ip6pkt.ip6);
 
                s = socket(AF_INET6, SOCK_RAW, IPPROTO_RAW);
                if (s == -1) {
@@ -600,24 +714,29 @@ int ctdb_sys_send_tcp(const ctdb_sock_addr *dest,
                        return -1;
 
                }
-               /* sendto() don't 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,
-                            (const struct sockaddr *)&dest->ip6,
-                            sizeof(dest->ip6));
+               /*
+                * sendto() on an IPv6 raw socket requires the port to
+                * be either 0 or a protocol value
+                */
+               tmpdest = dest->ip6;
+               tmpdest.sin6_port = 0;
+
+               ret = sendto(s,
+                            buf,
+                            len,
+                            0,
+                            (const struct sockaddr *)&tmpdest,
+                            sizeof(tmpdest));
                saved_errno = errno;
-               tmpdest->ip6.sin6_port = tmpport;
                close(s);
-
-               if (ret != sizeof(ip6pkt)) {
+               if (ret == -1) {
                        D_ERR("Failed sendto (%s)\n", strerror(saved_errno));
                        return -1;
                }
+               if ((size_t)ret != len) {
+                       DBG_ERR("Failed sendto - didn't send full packet\n");
+                       return -1;
+               }
                break;
 
        default:
@@ -635,6 +754,117 @@ int ctdb_sys_send_tcp(const ctdb_sock_addr *dest,
  * wscript has checked to make sure that pcap is available if needed.
  */
 
+static int tcp4_extract(const uint8_t *ip_pkt,
+                       size_t pktlen,
+                       struct sockaddr_in *src,
+                       struct sockaddr_in *dst,
+                       uint32_t *ack_seq,
+                       uint32_t *seq,
+                       int *rst,
+                       uint16_t *window)
+{
+       const struct ip *ip;
+       const struct tcphdr *tcp;
+
+       if (pktlen < sizeof(struct ip)) {
+               return EMSGSIZE;
+       }
+
+       ip = (const struct ip *)ip_pkt;
+
+       /* IPv4 only */
+       if (ip->ip_v != 4) {
+               return ENOMSG;
+       }
+       /* Don't look at fragments */
+       if ((ntohs(ip->ip_off)&0x1fff) != 0) {
+               return ENOMSG;
+       }
+       /* TCP only */
+       if (ip->ip_p != IPPROTO_TCP) {
+               return ENOMSG;
+       }
+
+       /* Ensure there is enough of the packet to gather required fields */
+       if (pktlen <
+           (ip->ip_hl * sizeof(uint32_t)) + offsetof(struct tcphdr, th_sum)) {
+               return EMSGSIZE;
+       }
+
+       tcp = (const struct tcphdr *)(ip_pkt + (ip->ip_hl * sizeof(uint32_t)));
+
+       src->sin_family      = AF_INET;
+       src->sin_addr.s_addr = ip->ip_src.s_addr;
+       src->sin_port        = tcp->th_sport;
+
+       dst->sin_family      = AF_INET;
+       dst->sin_addr.s_addr = ip->ip_dst.s_addr;
+       dst->sin_port        = tcp->th_dport;
+
+       *ack_seq             = tcp->th_ack;
+       *seq                 = tcp->th_seq;
+       if (window != NULL) {
+               *window = tcp->th_win;
+       }
+       if (rst != NULL) {
+               *rst = tcp->th_flags & TH_RST;
+       }
+
+       return 0;
+}
+
+static int tcp6_extract(const uint8_t *ip_pkt,
+                       size_t pktlen,
+                       struct sockaddr_in6 *src,
+                       struct sockaddr_in6 *dst,
+                       uint32_t *ack_seq,
+                       uint32_t *seq,
+                       int *rst,
+                       uint16_t *window)
+{
+       const struct ip6_hdr *ip6;
+       const struct tcphdr *tcp;
+
+       /* Ensure there is enough of the packet to gather required fields */
+       if (pktlen < sizeof(struct ip6_hdr) + offsetof(struct tcphdr, th_sum)) {
+               return EMSGSIZE;
+       }
+
+       ip6 = (const struct ip6_hdr *)ip_pkt;
+
+       /* IPv6 only */
+       if ((ip6->ip6_vfc >> 4) != 6){
+               return ENOMSG;
+       }
+
+       /* TCP only */
+       if (ip6->ip6_nxt != IPPROTO_TCP) {
+               return ENOMSG;
+       }
+
+       tcp = (const struct tcphdr *)(ip_pkt + sizeof(struct ip6_hdr));
+
+       src->sin6_family = AF_INET6;
+       src->sin6_port   = tcp->th_sport;
+       src->sin6_addr   = ip6->ip6_src;
+
+       dst->sin6_family = AF_INET6;
+       dst->sin6_port   = tcp->th_dport;
+       dst->sin6_addr   = ip6->ip6_dst;
+
+       *ack_seq             = tcp->th_ack;
+       *seq                 = tcp->th_seq;
+       if (window != NULL) {
+               *window = tcp->th_win;
+       }
+       if (rst != NULL) {
+               *rst = tcp->th_flags & TH_RST;
+       }
+
+       return 0;
+}
+
+
 #ifdef HAVE_AF_PACKET
 
 /*
@@ -688,17 +918,17 @@ int ctdb_sys_read_tcp_packet(int s, void *private_data,
                             int *rst,
                             uint16_t *window)
 {
-       int ret;
-#define RCVPKTSIZE 100
-       char pkt[RCVPKTSIZE];
+       ssize_t nread;
+       uint8_t pkt[100]; /* Large enough for simple ACK/RST packets */
        struct ether_header *eth;
-       struct iphdr *ip;
-       struct ip6_hdr *ip6;
-       struct tcphdr *tcp;
+       int ret;
 
-       ret = recv(s, pkt, RCVPKTSIZE, MSG_TRUNC);
-       if (ret < sizeof(*eth)+sizeof(*ip)) {
-               return -1;
+       nread = recv(s, pkt, sizeof(pkt), MSG_TRUNC);
+       if (nread == -1) {
+               return errno;
+       }
+       if ((size_t)nread < sizeof(*eth)) {
+               return EMSGSIZE;
        }
 
        ZERO_STRUCTP(src);
@@ -709,81 +939,29 @@ int ctdb_sys_read_tcp_packet(int s, void *private_data,
 
        /* we want either IPv4 or IPv6 */
        if (ntohs(eth->ether_type) == ETHERTYPE_IP) {
-               /* IP */
-               ip = (struct iphdr *)(eth+1);
-
-               /* We only want IPv4 packets */
-               if (ip->version != 4) {
-                       return -1;
-               }
-               /* Dont look at fragments */
-               if ((ntohs(ip->frag_off)&0x1fff) != 0) {
-                       return -1;
-               }
-               /* we only want TCP */
-               if (ip->protocol != IPPROTO_TCP) {
-                       return -1;
-               }
-
-               /* make sure its not a short packet */
-               if (offsetof(struct tcphdr, th_ack) + 4 +
-                   (ip->ihl*4) + sizeof(*eth) > ret) {
-                       return -1;
-               }
-               /* TCP */
-               tcp = (struct tcphdr *)((ip->ihl*4) + (char *)ip);
-
-               /* tell the caller which one we've found */
-               src->ip.sin_family      = AF_INET;
-               src->ip.sin_addr.s_addr = ip->saddr;
-               src->ip.sin_port        = tcp->th_sport;
-               dst->ip.sin_family      = AF_INET;
-               dst->ip.sin_addr.s_addr = ip->daddr;
-               dst->ip.sin_port        = tcp->th_dport;
-               *ack_seq                = tcp->th_ack;
-               *seq                    = tcp->th_seq;
-               if (window != NULL) {
-                       *window = tcp->th_win;
-               }
-               if (rst != NULL) {
-                       *rst = tcp->th_flags & TH_RST;
-               }
+               ret = tcp4_extract(pkt + sizeof(struct ether_header),
+                                  (size_t)nread - sizeof(struct ether_header),
+                                  &src->ip,
+                                  &dst->ip,
+                                  ack_seq,
+                                  seq,
+                                  rst,
+                                  window);
+               return ret;
 
-               return 0;
        } else if (ntohs(eth->ether_type) == ETHERTYPE_IP6) {
-               /* IP6 */
-               ip6 = (struct ip6_hdr *)(eth+1);
-
-               /* we only want TCP */
-               if (ip6->ip6_nxt != IPPROTO_TCP) {
-                       return -1;
-               }
-
-               /* TCP */
-               tcp = (struct tcphdr *)(ip6+1);
-
-               /* tell the caller which one we've found */
-               src->ip6.sin6_family = AF_INET6;
-               src->ip6.sin6_port   = tcp->th_sport;
-               src->ip6.sin6_addr   = ip6->ip6_src;
-
-               dst->ip6.sin6_family = AF_INET6;
-               dst->ip6.sin6_port   = tcp->th_dport;
-               dst->ip6.sin6_addr   = ip6->ip6_dst;
-
-               *ack_seq             = tcp->th_ack;
-               *seq                 = tcp->th_seq;
-               if (window != NULL) {
-                       *window = tcp->th_win;
-               }
-               if (rst != NULL) {
-                       *rst = tcp->th_flags & TH_RST;
-               }
-
-               return 0;
+               ret = tcp6_extract(pkt + sizeof(struct ether_header),
+                                  (size_t)nread - sizeof(struct ether_header),
+                                  &src->ip6,
+                                  &dst->ip6,
+                                  ack_seq,
+                                  seq,
+                                  rst,
+                                  window);
+               return ret;
        }
 
-       return -1;
+       return ENOMSG;
 }
 
 #else /* HAVE_AF_PACKET */
@@ -822,17 +1000,13 @@ int ctdb_sys_read_tcp_packet(int s,
 {
        int ret;
        struct ether_header *eth;
-       struct ip *ip;
-       struct ip6_hdr *ip6;
-       struct tcphdr *tcp;
-       struct ctdb_killtcp_connection *conn;
        struct pcap_pkthdr pkthdr;
        const u_char *buffer;
        pcap_t *pt = (pcap_t *)private_data;
 
        buffer=pcap_next(pt, &pkthdr);
        if (buffer==NULL) {
-               return -1;
+               return ENOMSG;
        }
 
        ZERO_STRUCTP(src);
@@ -843,81 +1017,31 @@ int ctdb_sys_read_tcp_packet(int s,
 
        /* we want either IPv4 or IPv6 */
        if (eth->ether_type == htons(ETHERTYPE_IP)) {
-               /* IP */
-               ip = (struct ip *)(eth+1);
-
-               /* We only want IPv4 packets */
-               if (ip->ip_v != 4) {
-                       return -1;
-               }
-               /* Dont look at fragments */
-               if ((ntohs(ip->ip_off)&0x1fff) != 0) {
-                       return -1;
-               }
-               /* we only want TCP */
-               if (ip->ip_p != IPPROTO_TCP) {
-                       return -1;
-               }
-
-               /* make sure its not a short packet */
-               if (offsetof(struct tcphdr, th_ack) + 4 +
-                   (ip->ip_hl*4) > pkthdr.len) {
-                       return -1;
-               }
-               /* TCP */
-               tcp = (struct tcphdr *)((ip->ip_hl*4) + (char *)ip);
-
-               /* tell the caller which one we've found */
-               src->ip.sin_family      = AF_INET;
-               src->ip.sin_addr.s_addr = ip->ip_src.s_addr;
-               src->ip.sin_port        = tcp->th_sport;
-               dst->ip.sin_family      = AF_INET;
-               dst->ip.sin_addr.s_addr = ip->ip_dst.s_addr;
-               dst->ip.sin_port        = tcp->th_dport;
-               *ack_seq                = tcp->th_ack;
-               *seq                    = tcp->th_seq;
-               if (window != NULL) {
-                       *window = tcp->th_win;
-               }
-               if (rst != NULL) {
-                       *rst = tcp->th_flags & TH_RST;
-               }
+               ret = tcp4_extract(buffer + sizeof(struct ether_header),
+                                  (size_t)(pkthdr.caplen -
+                                           sizeof(struct ether_header)),
+                                  &src->ip,
+                                  &dst->ip,
+                                  ack_seq,
+                                  seq,
+                                  rst,
+                                  window);
+               return ret;
 
-               return 0;
        } else if (eth->ether_type == htons(ETHERTYPE_IP6)) {
-                       /* IP6 */
-               ip6 = (struct ip6_hdr *)(eth+1);
-
-               /* we only want TCP */
-               if (ip6->ip6_nxt != IPPROTO_TCP) {
-                       return -1;
-               }
-
-               /* TCP */
-               tcp = (struct tcphdr *)(ip6+1);
-
-               /* tell the caller which one we've found */
-               src->ip6.sin6_family = AF_INET6;
-               src->ip6.sin6_port   = tcp->th_sport;
-               src->ip6.sin6_addr   = ip6->ip6_src;
-
-               dst->ip6.sin6_family = AF_INET6;
-               dst->ip6.sin6_port   = tcp->th_dport;
-               dst->ip6.sin6_addr   = ip6->ip6_dst;
-
-               *ack_seq             = tcp->th_ack;
-               *seq                 = tcp->th_seq;
-               if (window != NULL) {
-                       *window = tcp->th_win;
-               }
-               if (rst != NULL) {
-                       *rst = tcp->th_flags & TH_RST;
-               }
-
-               return 0;
+               ret = tcp6_extract(buffer + sizeof(struct ether_header),
+                                  (size_t)(pkthdr.caplen -
+                                           sizeof(struct ether_header)),
+                                  &src->ip6,
+                                  &dst->ip6,
+                                  ack_seq,
+                                  seq,
+                                  rst,
+                                  window);
+               return ret;
        }
 
-       return -1;
+       return ENOMSG;
 }
 
 #endif /* HAVE_AF_PACKET */