# This is a shell archive. Save it in a file, remove anything before # this line, and then unpack it by entering "sh file". Note, it may # create directories; files and directories will be owned by you and # have default permissions. # # This archive contains: # # finrst/Makefile # finrst/finrst.c # finrst/README # echo x - finrst/Makefile sed 's/^X//' >finrst/Makefile << 'END-of-finrst/Makefile' XPROG= finrst XNOMAN= # X X.include END-of-finrst/Makefile echo x - finrst/finrst.c sed 's/^X//' >finrst/finrst.c << 'END-of-finrst/finrst.c' X/* X * See README X * X * Author: YASUOKA Masahiko X * Date: 2013-09-30 X */ X#include X X#include X#include X#include X X#include X#include X#include X#include X#include X#include X X#include X#include X#include X#ifdef __NetBSD__ X#include X#else X#include X#endif X#include X X#include X#include X#include X#include X#include X#include X#include X#include X X#ifndef nitems X#define nitems(_x) (sizeof(_x) / sizeof(_x)[0]) X#endif X Xstatic int tcp_open (void); Xu_short tcp_ourport (int); Xstatic void tcp_send (struct in_addr *, struct in_addr *, u_short, X u_short, u_char, u_short, u_long, u_long); Xstatic int bpf_open (void); Xstatic void bpf_setup (int, int, int, struct in_addr *, struct in_addr *, X const char *); Xstatic int bpf_recv (int, struct tcpiphdr *); Xstatic int bpf_dlt_hdrlen (int); Xstatic uint16_t in_cksum (uint16_t *, int); X Xstatic void press_enter (int, const char *); X#ifdef __NetBSD__ Xlong long strtonum (const char *, long long, long long, const char **); X#endif X X#define BPF_CAPTURE_SIZ 128 X#define BPF_READ_TIMEOUT 4 X Xstatic void Xusage(void) X{ X extern char *__progname; X X fprintf(stderr, X "usage: %s -h\n" X " %s [-nRFf] ifname ip_src ip_dst dport\n" X " %s [-nRFf] -a ifname ip_dst dport\n" X "\t-a : Test a connection opened actively\n" X "\t-n : Not stop before sending FIN or RST\n" X "\t-R : Don't send RST\n" X "\t-F : Don't send FIN\n" X "\t-f : Send RST to fix the problem\n" X "\t-ff: Send RST to fix the problem(Not stop before sending)\n" X "\t-h : Show this usage\n", X __progname, __progname, __progname); X} X Xint Xmain(int argc, char *argv[]) X{ X int ch, bpf, tcp = -1, dlen, noFIN = 0, noRST = 0; X int doFIX = 0, nonstop = 0, active = 0, sz; X struct in_addr ip_src, ip_dst; X u_short sport, dport; X char *ifname; X const char *errstr; X struct tcpiphdr ti; X tcp_seq seq, ack; X X while ((ch = getopt(argc, argv, "anfFRh")) != -1) X switch (ch) { X case 'a': X active = 1; X break; X case 'n': X nonstop = 1; X break; X case 'f': X doFIX++; X break; X case 'F': X noFIN = 1; X break; X case 'R': X noRST = 1; X break; X case 'h': X usage(); X exit(EX_USAGE); X /* NOTREACHED */ X } X X argc -= optind; X argv += optind; X X if (!((active && argc == 3) || (!active && argc == 4))) { X usage(); X exit(EX_USAGE); X } X ifname = *(argv++); X X if (!active) { X if (inet_aton(*(argv++), &ip_src) != 1) X errx(EX_USAGE, X "could not parse ip_src as an IP address"); X } X if (inet_aton(*(argv++), &ip_dst) != 1) X errx(EX_USAGE, "could not parse ip_dst as an IP address"); X X dport = strtonum(*(argv++), 1, 65535, &errstr); X if (errstr != NULL) X errx(EX_USAGE, "could not parse dport: %s", errstr); X X if ((bpf = bpf_open()) < 0) X err(EX_OSERR, "could not open BPF device"); X X if (!active) { X tcp = tcp_open(); X sport = tcp_ourport(tcp); X fprintf(stderr, "*** our port number is selected to %d\n", X sport); X bpf_setup(bpf, -1, sport, &ip_dst, &ip_src, ifname); X X seq = arc4random(); X tcp_send(&ip_src, &ip_dst, sport, dport, TH_SYN, 65535, seq++, X 0); X bpf_recv(bpf, &ti); X ack = ntohl(ti.ti_seq); X tcp_send(&ip_src, &ip_dst, sport, dport, TH_ACK, 65535, seq, X ++ack); X X fprintf(stderr, "*** waiting data from the peer."); X } else { X bpf_setup(bpf, -1, dport, NULL, &ip_dst, ifname); X fprintf(stderr, "*** Waiting for connection to %s %d/tcp\n", X inet_ntoa(ip_dst), sport); X for (;;) { X sz = bpf_recv(bpf, &ti); X if (sz > 0 && ti.ti_flags == TH_SYN) X break; X } X fprintf(stderr, "*** ok\n"); X ip_src = ip_dst; X sport = dport; X ip_dst = ((struct ip *)&ti)->ip_src; X dport = ntohs(ti.ti_sport); X seq = arc4random(); X ack = ntohl(ti.ti_seq); X tcp_send(&ip_src, &ip_dst, sport, dport, TH_SYN|TH_ACK, X 65535, seq++, ++ack); X do { X sz = bpf_recv(bpf, &ti); X } while (sz <= 0); X fprintf(stderr, "*** say something."); X } X X for (;;) { X sz = bpf_recv(bpf, &ti); X dlen = ntohs(ti.ti_len); X if (sz > 0 && dlen > 0) { X fprintf(stderr, " ok\n"); X break; X } X } X if (!noFIN) { X press_enter(nonstop, "send FIN"); X tcp_send(&ip_src, &ip_dst, sport, dport, X TH_FIN | TH_ACK, 65535, seq++, ack); X } X if (!noRST) { X press_enter(nonstop, "send RST"); X tcp_send(&ip_src, &ip_dst, sport, dport, TH_RST | TH_ACK, 0, X seq, ack + dlen); X } X if (bpf_recv(bpf, &ti) == 0) X fprintf(stderr, "*** the peer didn't respond\n"); X else if (ntohl(ti.ti_ack) == seq) X fprintf(stderr, "*** received ack is ok.\n"); X else if (ntohl(ti.ti_ack) == seq - 1) { X fprintf(stderr, "*** received ack is NG.\n"); X if (doFIX) { X press_enter(doFIX > 1, "send RST to fix"); X tcp_send(&ip_src, &ip_dst, sport, dport, X TH_RST, 0, seq - 1, ack); X } X } else X fprintf(stderr, "*** received ack is unknown.\n"); X X if (tcp > 0) X close(tcp); X close(bpf); X X exit(EXIT_SUCCESS); X} X X/*********************************************************************** X * TCP X ***********************************************************************/ Xstatic int Xtcp_open(void) X{ X int sock; X struct sockaddr_in sin4; X X if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) X err(EX_OSERR, "socket"); X X memset(&sin4, 0, sizeof(sin4)); X sin4.sin_family = AF_INET; X sin4.sin_len = sizeof(sin4); X X if (bind(sock, (struct sockaddr *)&sin4, sizeof(sin4)) == -1) X err(EX_OSERR, "bind"); X X return (sock); X} X Xu_short Xtcp_ourport(int sock) X{ X struct sockaddr_in sin4; X socklen_t slen; X X slen = sizeof(sin4); X if (getsockname(sock, (struct sockaddr *)&sin4, &slen) == -1) X err(EX_OSERR, "getsockname"); X X return (sin4.sin_port); X} X Xstatic void Xtcp_send(struct in_addr *ip_src, struct in_addr *ip_dst, u_short sport, X u_short dport, u_char flags, u_short winsz, u_long seq, u_long ack) X{ X u_char buf0[128]; X struct ip ip0; X struct ippseudo ipp0; X struct tcphdr tcp0; X struct iovec iov[3]; X struct msghdr msg; X int sz, x, rawsock, ip_len, tcp_len; X struct sockaddr_in sin4; X uint32_t tcpopt[2]; X X if ((rawsock = socket(AF_INET, SOCK_RAW, IPPROTO_TCP)) < 0) X err(EX_OSERR, "socket(AF_INET, SOCK_RAW, IPPROTO_TCP)"); X x = 1; X if (setsockopt(rawsock, IPPROTO_IP, IP_HDRINCL, &x, sizeof(x)) != 0) X err(EX_OSERR, "setsockopt(,IPPROTO_IP, IP_HDRINCL,)"); X X if ((flags & TH_SYN) == TH_SYN) X tcp_len = sizeof(struct tcphdr) + sizeof(tcpopt); X else X tcp_len = sizeof(struct tcphdr); X ip_len = sizeof(struct ip) + tcp_len; X X memset(&ipp0, 0, sizeof(ipp0)); X memset(&tcp0, 0, sizeof(tcp0)); X memset(&msg, 0, sizeof(msg)); X X ipp0.ippseudo_src = *ip_src; X ipp0.ippseudo_dst = *ip_dst; X ipp0.ippseudo_p = IPPROTO_TCP; X ipp0.ippseudo_len = htons(tcp_len); X tcp0.th_sport = htons(sport); X tcp0.th_dport = htons(dport); X tcp0.th_off = tcp_len >> 2; X tcp0.th_flags = flags; X tcp0.th_win = htons(winsz); X tcp0.th_sum = 0; X tcp0.th_seq = htonl(seq); X tcp0.th_ack = htonl(ack); X X memcpy(buf0, &ipp0, sizeof(ipp0)); X memcpy(buf0 + sizeof(ipp0), &tcp0, sizeof(tcp0)); X if ((flags & TH_SYN) == TH_SYN) { X tcpopt[0] = htonl(0x020405b4); X tcpopt[1] = htonl(0x01010402); X memcpy(buf0 + sizeof(ipp0) + sizeof(tcp0), tcpopt, X sizeof(tcpopt)); X } X tcp0.th_sum = in_cksum((uint16_t *)buf0, sizeof(ipp0) + tcp_len); X X ip0.ip_v = 4; X ip0.ip_hl = 5; X ip0.ip_id = arc4random(); X ip0.ip_src = *ip_src; X ip0.ip_dst = *ip_dst; X ip0.ip_tos = /* IPTOS_LOWDELAY */ 0; X ip0.ip_ttl = 255; X#ifdef __NetBSD__ X ip0.ip_len = ip_len; X ip0.ip_off = IP_DF; X#else X ip0.ip_len = htons(ip_len); X ip0.ip_off = htons(IP_DF); X#endif X ip0.ip_p = IPPROTO_TCP; X ip0.ip_sum = in_cksum((uint16_t *)&ip0, sizeof(struct ip)); X X iov[0].iov_base = &ip0; X iov[0].iov_len = sizeof(struct ip); X iov[1].iov_base = &tcp0; X iov[1].iov_len = sizeof(struct tcphdr); X X if ((flags & TH_SYN) == TH_SYN) { X msg.msg_iovlen = 3; X iov[2].iov_base = tcpopt; X iov[2].iov_len = sizeof(tcpopt); X } else X msg.msg_iovlen = 2; X X sin4.sin_len = sizeof(sin4); X sin4.sin_family = AF_INET; X sin4.sin_addr = *ip_dst; X msg.msg_name = &sin4; X msg.msg_namelen = sizeof(sin4); X msg.msg_iov = iov; X X if ((sz = sendmsg(rawsock, &msg, 0)) < 0) X err(EX_OSERR, "send dst_ip=%s", inet_ntoa(*ip_dst), flags); X close(rawsock); X} X X/*********************************************************************** X * BPF X ***********************************************************************/ Xstatic int Xbpf_open(void) X{ X int f; X X#ifdef __NetBSD__ X f = open("/dev/bpf", O_RDWR); X#else X char pathbpf[MAXPATHLEN + 1]; X int i; X X i = 0; X do { X snprintf(pathbpf, sizeof(pathbpf), "/dev/bpf%d", i++); X if ((f = open(pathbpf, O_RDWR)) >= 0) X break; X } while (errno == EBUSY); X#endif X X return (f); X} X X#define SUBST_BPF_STMT(_insns, _code, _k) \ X do { \ X struct bpf_insn insnx = BPF_STMT((_code), (_k)); \ X (_insns) = insnx; \ X } while (0 /* CONSTCOND */) X X#define SUBST_BPF_JUMP(_insns, _code, _k, _jt, _jf) \ X do { \ X struct bpf_insn insnx = \ X BPF_JUMP((_code), (_k), (_jt), (_jf)); \ X (_insns) = insnx; \ X } while (0 /* CONSTCOND */) X Xstatic void Xbpf_setup(int bpf, int sport, int dport, struct in_addr *ip_src, X struct in_addr *ip_dst, const char *ifname) X{ X int ival, hdrlen, ninsns = 0; X u_int dlt; X struct ifreq ifr; X struct bpf_insn insns0[32], *insns; X struct bpf_program bf_filter; X struct timeval tv; X X ival = BPF_CAPTURE_SIZ; X if (ioctl(bpf, BIOCSBLEN, &ival) != 0) X err(EX_OSERR, "ioctl(bpf, BIOCSBLEN(%d))", ival); X ival = 1; X if (ioctl(bpf, BIOCIMMEDIATE, &ival) != 0) X err(EX_OSERR, "ioctl(bpf, BIOCIMMEDIATE)"); X X tv.tv_sec = BPF_READ_TIMEOUT; X tv.tv_usec = 0; X if (ioctl(bpf, BIOCSRTIMEOUT, &tv) != 0) X err(EX_OSERR, "ioctl(bpf, BIOCSRTIMEOUT)"); X X memset(&ifr, 0, sizeof(ifr)); X strlcat(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); X if (ioctl(bpf, BIOCSETIF, &ifr) != 0) X err(EX_OSERR, "ioctl(/dev/bpf, BIOCSETIF)"); X if (ioctl(bpf, BIOCGDLT, &dlt) != 0) X err(EX_OSERR, "ioctl(bpf, BIOCGDLT)"); X X hdrlen = bpf_dlt_hdrlen(dlt); X if (hdrlen < 0) X errx(EX_USAGE, "DLT = %u in not supported", dlt); X insns = &insns0[nitems(insns0) - 1]; X insns -= 2; X SUBST_BPF_STMT(insns[0], BPF_RET+BPF_K, (u_int)-1); X SUBST_BPF_STMT(insns[1], BPF_RET+BPF_K, (u_int)0); X ninsns += 2; X X if (sport > 0) { X insns -= 3; X SUBST_BPF_STMT(insns[0], BPF_LDX+BPF_B+BPF_MSH, hdrlen); X SUBST_BPF_STMT(insns[1], BPF_LD+BPF_H+BPF_IND, hdrlen); X SUBST_BPF_JUMP(insns[2], BPF_JMP+BPF_JEQ+BPF_K, (int)sport, 0, X ninsns - 1); X ninsns += 3; X } X if (dport > 0) { X insns -= 3; X SUBST_BPF_STMT(insns[0], BPF_LDX+BPF_B+BPF_MSH, hdrlen); X SUBST_BPF_STMT(insns[1], BPF_LD+BPF_H+BPF_IND, hdrlen + 2); X SUBST_BPF_JUMP(insns[2], BPF_JMP+BPF_JEQ+BPF_K, (int)dport, 0, X ninsns - 1); X ninsns += 3; X } X X if (ip_src != NULL) { X insns -= 2; X SUBST_BPF_STMT(insns[0], BPF_LD+BPF_W+BPF_ABS, hdrlen + 12); X SUBST_BPF_JUMP(insns[1], BPF_JMP+BPF_JEQ+BPF_K, X ntohl(ip_src->s_addr), 0, ninsns - 1); X ninsns += 2; X } X if (ip_dst != NULL) { X insns -= 2; X SUBST_BPF_STMT(insns[0], BPF_LD+BPF_W+BPF_ABS, hdrlen + 16); X SUBST_BPF_JUMP(insns[1], BPF_JMP+BPF_JEQ+BPF_K, X ntohl(ip_dst->s_addr), 0, ninsns - 1); X ninsns += 2; X } X X /* ip protocol is tcp(6) */ X insns -= 2; X SUBST_BPF_STMT(insns[0], BPF_LD+BPF_B+BPF_ABS, hdrlen + 9); X SUBST_BPF_JUMP(insns[1], BPF_JMP+BPF_JEQ+BPF_K, 6, 0, ninsns - 1); X ninsns += 2; X X switch (dlt) { X case DLT_EN10MB: X /* ether type is ip */ X insns -= 2; X SUBST_BPF_STMT(insns[0], BPF_LD+BPF_H+BPF_ABS, 12); X SUBST_BPF_JUMP(insns[1], BPF_JMP+BPF_JEQ+BPF_K, X ETHERTYPE_IP, 0, ninsns - 1); X ninsns += 2; X break; X X#ifdef __NetBSD__ X case DLT_NULL: X insns -= 2; X SUBST_BPF_STMT(insns[0], BPF_LD+BPF_W+BPF_ABS, 0); X SUBST_BPF_JUMP(insns[1], BPF_JMP+BPF_JEQ+BPF_K, X htonl(AF_INET), 0, ninsns - 1); X ninsns += 2; X break; X#endif X X case DLT_LOOP: X insns -= 2; X SUBST_BPF_STMT(insns[0], BPF_LD+BPF_W+BPF_ABS, 0); X SUBST_BPF_JUMP(insns[1], BPF_JMP+BPF_JEQ+BPF_K, AF_INET, X 0, ninsns - 1); X ninsns += 2; X break; X X default: X errx(EX_USAGE, "DLT = %u in not supported", dlt); X } X X bf_filter.bf_len = ninsns; X bf_filter.bf_insns = insns; X if (ioctl(bpf, BIOCSETF, &bf_filter) != 0) X err(EX_OSERR, "ioctl(bpf, BIOCSETF())"); X} X Xstatic int Xbpf_recv(int bpf, struct tcpiphdr *ti) X{ X struct bpf_hdr *bh; X struct ip *ip0; X u_char *pkt, pkt0[128]; X int sz, off, hdrlen; X u_int dlt; X X if (ioctl(bpf, BIOCGDLT, &dlt) != 0) X err(EX_OSERR, "ioctl(bpf, BIOCGDLT)"); X hdrlen = bpf_dlt_hdrlen(dlt); X X if ((sz = read(bpf, pkt0, sizeof(pkt0))) < 0) X err(EX_OSERR, "read(bpf)"); X pkt = pkt0; X while (sz > 0) { X if (sz < sizeof(struct bpf_hdr) + hdrlen) X err(EX_OSERR, "Received BPF is too short(%d)", sz); X bh = (struct bpf_hdr *)pkt; X off = bh->bh_hdrlen + hdrlen; X memcpy(&ti->ti_i, pkt + off, sizeof(struct ip)); X ip0 = (struct ip *)ti; X if (ip0->ip_p != IPPROTO_TCP) X goto next_pkt; X off += ip0->ip_hl << 2; X memcpy(&ti->ti_t, pkt + off, sizeof(struct tcphdr)); X X /* set the tcp payload length to ti->ti_len */ X ti->ti_len = htons(htons(ip0->ip_len) - X (ip0->ip_hl << 2) - (ti->ti_off << 2)); X X return (sz); Xnext_pkt: X pkt = pkt + BPF_WORDALIGN(bh->bh_hdrlen + X bh->bh_caplen); X sz -= BPF_WORDALIGN(bh->bh_hdrlen + bh->bh_caplen); X } X return (sz); X} X Xstatic int Xbpf_dlt_hdrlen(int dlt) X{ X switch (dlt) { X case DLT_EN10MB: X return (ETHER_HDR_LEN); X#ifdef __NetBSD__ X case DLT_NULL: X return (sizeof(u_int)); X#endif X case DLT_LOOP: X return (sizeof(u_int)); X } X return (-1); X} X X/*********************************************************************** X * Miscellaneous functions X ***********************************************************************/ X/* adapted from sbin/ping/ping.c */ Xstatic uint16_t Xin_cksum(uint16_t *addr, int len) X{ X register int nleft = len; X register uint16_t *w = addr; X register int sum = 0; X uint16_t answer = 0; X X /* X * Our algorithm is simple, using a 32 bit accumulator (sum), we add X * sequential 16 bit words to it, and at the end, fold back all the X * carry bits from the top 16 bits into the lower 16 bits. X */ X while (nleft > 1) { X sum += *w++; X nleft -= 2; X } X X /* mop up an odd byte, if necessary */ X if (nleft == 1) { X *(u_char *)(&answer) = *(u_char *)w ; X sum += answer; X } X X /* add back carry outs from top 16 bits to low 16 bits */ X sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ X sum += (sum >> 16); /* add carry */ X answer = ~sum; /* truncate to 16 bits */ X X return (answer); X} X Xstatic void Xpress_enter(int nonstop, const char *what) X{ X if (!nonstop) { X fprintf(stderr, "Press to %s ", what); X getchar(); X } X} X X#ifdef __NetBSD__ X/* adapted from lib/libc/stdlib/strtonum.c */ X X/* X * Copyright (c) 2004 Ted Unangst and Todd Miller X * All rights reserved. X * X * Permission to use, copy, modify, and distribute this software for any X * purpose with or without fee is hereby granted, provided that the above X * copyright notice and this permission notice appear in all copies. X * X * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES X * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF X * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR X * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES X * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN X * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF X * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. X */ X#define INVALID 1 X#define TOOSMALL 2 X#define TOOLARGE 3 X Xlong long Xstrtonum(const char *numstr, long long minval, long long maxval, X const char **errstrp) X{ X long long ll = 0; X char *ep; X int error = 0; X struct errval { X const char *errstr; X int err; X } ev[4] = { X { NULL, 0 }, X { "invalid", EINVAL }, X { "too small", ERANGE }, X { "too large", ERANGE }, X }; X X ev[0].err = errno; X errno = 0; X if (minval > maxval) X error = INVALID; X else { X ll = strtoll(numstr, &ep, 10); X if (numstr == ep || *ep != '\0') X error = INVALID; X else if ((ll == LLONG_MIN && errno == ERANGE) || ll < minval) X error = TOOSMALL; X else if ((ll == LLONG_MAX && errno == ERANGE) || ll > maxval) X error = TOOLARGE; X } X if (errstrp != NULL) X *errstrp = ev[error].errstr; X errno = ev[error].err; X if (error) X ll = 0; X X return (ll); X} X#endif END-of-finrst/finrst.c echo x - finrst/README sed 's/^X//' >finrst/README << 'END-of-finrst/README' X`finrst' is to repeat the TCP half-open problem X X 1. Establish TCP X 2. Wait a message from the peer X 3. Send FIN ignoring the received message X 4. Send RST with acking the received message X X XPrerequirement: X X Packets to the `ip_src' must be captured on the interface X specified by the `ifname' and nobody other than this program must not X response the packets. X X XUsage: X X usage: finrst -h X finrst [-nRFf] ifname ip_src ip_dst dport X finrst [-nRFf] -a ifname ip_dst dport X -a : Test a connection opened actively X -n : Not stop before sending FIN or RST X -R : Don't send RST X -F : Don't send FIN X -f : Send RST to fix the problem X -ff: Send RST to fix the problem(Not stop before sending) X -h : Show this usage X XExample: X X % sudo route add -blackhole -host 10.1.1.1 127.0.0.1 X X % sudo ./finrst -nf lo0 10.1.1.1 127.0.0.1 22 X *** our port number is selected to 26561 X *** waiting data from the peer. ok X *** received ack is NG. # The problem is repeated. X Press to send RST to fix # Press to fix the PCB. X % X X % sudo ./finrst -nf lo0 10.1.1.1 127.0.0.1 22 X *** our port number is selected to 26305 X *** waiting data from the peer. ok # The problem is not repeated X *** received ack is ok. X % X END-of-finrst/README exit