/* Tickle TCP connections tool Author: Jiaju Zhang Based on the code in CTDB http://ctdb.samba.org/ written by Andrew Tridgell and Ronnie Sahlberg This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define discard_const(ptr) ((void *)((intptr_t)(ptr))) typedef union { struct sockaddr sa; struct sockaddr_in ip; struct sockaddr_in6 ip6; } sock_addr; void set_nonblocking(int fd); void set_close_on_exec(int fd); static int parse_ipv4(const char *s, unsigned port, struct sockaddr_in *sin); static int parse_ipv6(const char *s, const char *iface, unsigned port, sock_addr *saddr); int parse_ip(const char *addr, const char *iface, unsigned port, sock_addr *saddr); int parse_ip_port(const char *addr, sock_addr *saddr); int send_tickle_ack(const sock_addr *dst, const sock_addr *src, uint32_t seq, uint32_t ack, int rst); static void usage(void); static uint32_t uint16_checksum(uint16_t *data, size_t n) { uint32_t sum=0; while (n >= 2) { sum += (uint32_t)ntohs(*data); data++; n -= 2; } if (n == 1) { sum += (uint32_t)ntohs(*(uint8_t *)data); } return sum; } static uint16_t tcp_checksum(uint16_t *data, size_t n, struct iphdr *ip) { uint32_t sum = uint16_checksum(data, n); uint16_t sum2; sum += uint16_checksum((uint16_t *)(void *)&ip->saddr, sizeof(ip->saddr)); sum += uint16_checksum((uint16_t *)(void *)&ip->daddr, sizeof(ip->daddr)); sum += ip->protocol + n; sum = (sum & 0xFFFF) + (sum >> 16); sum = (sum & 0xFFFF) + (sum >> 16); sum2 = htons(sum); sum2 = ~sum2; if (sum2 == 0) { return 0xFFFF; } return sum2; } static uint16_t tcp_checksum6(uint16_t *data, size_t n, struct ip6_hdr *ip6) { uint32_t phdr[2]; uint32_t sum = 0; uint16_t sum2; memset(phdr, 0, sizeof(phdr)); sum += uint16_checksum((uint16_t *)(void *)&ip6->ip6_src, 16); sum += uint16_checksum((uint16_t *)(void *)&ip6->ip6_dst, 16); phdr[0] = htonl(n); phdr[1] = htonl(ip6->ip6_nxt); sum += uint16_checksum((uint16_t *)phdr, 8); sum += uint16_checksum(data, n); sum = (sum & 0xFFFF) + (sum >> 16); sum = (sum & 0xFFFF) + (sum >> 16); sum2 = htons(sum); sum2 = ~sum2; if (sum2 == 0) { return 0xFFFF; } return sum2; } void set_nonblocking(int fd) { unsigned v; v = fcntl(fd, F_GETFL, 0); fcntl(fd, F_SETFL, v | O_NONBLOCK); } void set_close_on_exec(int fd) { unsigned v; v = fcntl(fd, F_GETFD, 0); fcntl(fd, F_SETFD, v | FD_CLOEXEC); } static int parse_ipv4(const char *s, unsigned port, struct sockaddr_in *sin) { sin->sin_family = AF_INET; sin->sin_port = htons(port); if (inet_pton(AF_INET, s, &sin->sin_addr) != 1) { fprintf(stderr, "Failed to translate %s into sin_addr\n", s); return -1; } return 0; } static int parse_ipv6(const char *s, const char *iface, unsigned port, 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) { fprintf(stderr, "Failed to translate %s into sin6_addr\n", s); return -1; } if (iface && IN6_IS_ADDR_LINKLOCAL(&saddr->ip6.sin6_addr)) { saddr->ip6.sin6_scope_id = if_nametoindex(iface); } return 0; } int parse_ip(const char *addr, const char *iface, unsigned port, sock_addr *saddr) { char *p; int ret; p = index(addr, ':'); if (!p) ret = parse_ipv4(addr, port, &saddr->ip); else ret = parse_ipv6(addr, iface, port, saddr); return ret; } int parse_ip_port(const char *addr, sock_addr *saddr) { char *s, *p; unsigned port; char *endp = NULL; int ret; s = strdup(addr); if (!s) { fprintf(stderr, "Failed strdup()\n"); return -1; } p = rindex(s, ':'); if (!p) { fprintf(stderr, "This addr: %s does not contain a port number\n", s); free(s); return -1; } port = strtoul(p+1, &endp, 10); if (!endp || *endp != 0) { fprintf(stderr, "Trailing garbage after the port in %s\n", s); free(s); return -1; } *p = 0; ret = parse_ip(s, NULL, port, saddr); free(s); return ret; } int send_tickle_ack(const sock_addr *dst, const sock_addr *src, uint32_t seq, uint32_t ack, int rst) { int s; int ret; uint32_t one = 1; uint16_t tmpport; sock_addr *tmpdest; struct { struct iphdr ip; struct tcphdr tcp; } ip4pkt; struct { struct ip6_hdr ip6; struct tcphdr tcp; } ip6pkt; switch (src->ip.sin_family) { case AF_INET: memset(&ip4pkt, 0, sizeof(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 = dst->ip.sin_addr.s_addr; ip4pkt.ip.check = 0; ip4pkt.tcp.source = src->ip.sin_port; ip4pkt.tcp.dest = dst->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; ip4pkt.tcp.window = htons(1234); ip4pkt.tcp.check = tcp_checksum((uint16_t *)&ip4pkt.tcp, sizeof(ip4pkt.tcp), &ip4pkt.ip); s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); if (s == -1) { fprintf(stderr, "Failed to open raw socket (%s)\n", strerror(errno)); return -1; } ret = setsockopt(s, SOL_IP, IP_HDRINCL, &one, sizeof(one)); if (ret != 0) { fprintf(stderr, "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, (const struct sockaddr *)&dst->ip, sizeof(dst->ip)); close(s); if (ret != sizeof(ip4pkt)) { fprintf(stderr, "Failed sendto (%s)\n", strerror(errno)); return -1; } break; case AF_INET6: memset(&ip6pkt, 0, sizeof(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 = dst->ip6.sin6_addr; ip6pkt.tcp.source = src->ip6.sin6_port; ip6pkt.tcp.dest = dst->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; 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) { fprintf(stderr, "Failed to open sending socket\n"); return -1; } tmpdest = discard_const(dst); tmpport = tmpdest->ip6.sin6_port; tmpdest->ip6.sin6_port = 0; ret = sendto(s, &ip6pkt, sizeof(ip6pkt), 0, (const struct sockaddr *)&dst->ip6, sizeof(dst->ip6)); tmpdest->ip6.sin6_port = tmpport; close(s); if (ret != sizeof(ip6pkt)) { fprintf(stderr, "Failed sendto (%s)\n", strerror(errno)); return -1; } break; default: fprintf(stderr, "Not an ipv4/v6 address\n"); return -1; } return 0; } static void usage(void) { printf("Usage: /usr/lib/heartbeat/tickle_tcp [ -n num ]\n"); printf("Please note that this program need to read the list of\n"); printf("{local_ip:port remote_ip:port} from stdin.\n"); exit(1); } #define OPTION_STRING "n:h" int main(int argc, char *argv[]) { int optchar, i, num = 1, cont = 1; sock_addr src, dst; char addrline[128], addr1[64], addr2[64]; while(cont) { optchar = getopt(argc, argv, OPTION_STRING); switch(optchar) { case 'n': num = atoi(optarg); break; case 'h': usage(); exit(EXIT_SUCCESS); break; case EOF: cont = 0; break; default: fprintf(stderr, "unknown option, please use '-h' for usage.\n"); exit(EXIT_FAILURE); break; }; } while(fgets(addrline, sizeof(addrline), stdin)) { sscanf(addrline, "%s %s", addr1, addr2); if (parse_ip_port(addr1, &src)) { fprintf(stderr, "Bad IP:port '%s'\n", addr1); return -1; } if (parse_ip_port(addr2, &dst)) { fprintf(stderr, "Bad IP:port '%s'\n", addr2); return -1; } for (i = 1; i <= num; i++) { if (send_tickle_ack(&dst, &src, 0, 0, 0)) { fprintf(stderr, "Error while sending tickle ack from '%s' to '%s'\n", addr1, addr2); return -1; } } } return 0; }