/* $Id: rst.c 7073 2010-09-13 00:45:02Z vern $ */ /* Derived from traceroute, which has the following copyright: * * Copyright (c) 1999, 2002 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that: (1) source code distributions * retain the above copyright notice and this paragraph in its entirety, (2) * distributions including binary code include the above copyright notice and * this paragraph in its entirety in the documentation or other materials * provided with the distribution, and (3) all advertising materials mentioning * features or use of this software display the following acknowledgement: * ``This product includes software developed by the University of California, * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of * the University nor the names of its contributors may be used to endorse * or promote products derived from this software without specific prior * written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1999, 2002\nThe Regents of the University of California. All rights reserved.\n"; static const char rcsid[] = "@(#) $Id: rst.c 7073 2010-09-13 00:45:02Z vern $ (LBL)"; #endif /* need this due to linux's funny idea of a tcphdr */ #if defined(__linux__) #define _BSD_SOURCE #endif #include #include #include #include #include #include #include #include #include #include #include #include #include "../../config.h" /* Forwards */ void gripe(const char *, const char *); void pgripe(const char *); u_short in_cksum(register u_short *, register int); int ones_complement_checksum(const void *, int, u_int32_t); int tcp_checksum(const struct ip *, const struct tcphdr *, int); void send_pkt(int, struct in_addr, int, u_int32_t, struct in_addr, int, u_int32_t, int, int, int, int, const char *); void terminate(int, const char *, int, u_int32_t, const char *, int, u_int32_t, int, int, int, int, const char *); void usage(void); int main(int, char **); const char *prog_name; void gripe(const char *fmt, const char *arg) { fprintf(stderr, "%s: ", prog_name); fprintf(stderr, fmt, arg); fprintf(stderr, "\n"); } void pgripe(const char *msg) { fprintf(stderr, "%s: %s (%s)\n", prog_name, msg, strerror(errno)); exit(1); } /* * Checksum routine for Internet Protocol family headers (C Version) */ u_short in_cksum(register u_short *addr, register int len) { register int nleft = len; register u_short *w = addr; register u_short answer; register int sum = 0; /* * Our algorithm is simple, using a 32 bit accumulator (sum), * we add sequential 16 bit words to it, and at the end, fold * back all the carry bits from the top 16 bits into the lower * 16 bits. */ while (nleft > 1) { sum += *w++; nleft -= 2; } /* mop up an odd byte, if necessary */ if (nleft == 1) sum += *(u_char *)w; /* * add back carry outs from top 16 bits to low 16 bits */ sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ sum += (sum >> 16); /* add carry */ answer = ~sum; /* truncate to 16 bits */ return (answer); } // - adapted from tcpdump // Returns the ones-complement checksum of a chunk of b short-aligned bytes. int ones_complement_checksum(const void *p, int b, u_int32_t sum) { const u_short *sp = (u_short *) p; // better be aligned! b /= 2; // convert to count of short's /* No need for endian conversions. */ while ( --b >= 0 ) sum += *sp++; while ( sum > 0xffff ) sum = (sum & 0xffff) + (sum >> 16); return sum; } int tcp_checksum(const struct ip *ip, const struct tcphdr *tp, int len) { int tcp_len = tp->th_off * 4 + len; u_int32_t sum, addl_pseudo; if ( len % 2 == 1 ) // Add in pad byte. sum = htons(((const u_char*) tp)[tcp_len - 1] << 8); else sum = 0; sum = ones_complement_checksum((void*) &ip->ip_src.s_addr, 4, sum); sum = ones_complement_checksum((void*) &ip->ip_dst.s_addr, 4, sum); addl_pseudo = (htons(IPPROTO_TCP) << 16) | htons((unsigned short) tcp_len); sum = ones_complement_checksum((void*) &addl_pseudo, 4, sum); sum = ones_complement_checksum((void*) tp, tcp_len, sum); return sum; } void send_pkt(int s, struct in_addr from, int from_port, u_int32_t from_seq, struct in_addr to, int to_port, u_int32_t to_seq, int size, int redundancy, int delay, int flags, const char *inject) { int cc; int pktlen = 40 + size; const int max_injection_size = 4096; char *pkt = malloc(pktlen + max_injection_size + 1024 /* slop */); struct ip *ip = (struct ip *) pkt; struct tcphdr *tcp = (struct tcphdr *) &pkt[20]; if ( ! pkt ) pgripe("couldn't malloc memory"); if ( inject && *inject ) { size = strlen(inject); if ( size > max_injection_size ) gripe("injection text too large%s", ""); pktlen = 40 + size; } memset(pkt, 0, pktlen); ip->ip_v = IPVERSION; ip->ip_len = pktlen; /* on FreeBSD, don't use htons(); YMMV */ ip->ip_off = 0; ip->ip_src = from; ip->ip_dst = to; ip->ip_hl = 5; ip->ip_p = IPPROTO_TCP; ip->ip_ttl = 255; ip->ip_id = 0; ip->ip_sum = in_cksum((u_short *) ip, sizeof(*ip)); if (ip->ip_sum == 0) ip->ip_sum = 0xffff; tcp->th_sport = htons(from_port); tcp->th_dport = htons(to_port); tcp->th_seq = htonl(from_seq); tcp->th_ack = htonl(to_seq); tcp->th_off = 5; tcp->th_flags = flags; tcp->th_win = 0; tcp->th_urp = 0; tcp->th_sum = 0; if ( inject && *inject ) { char *payload = &pkt[40]; strcpy(payload, inject); } else if ( size > 0 ) { const char *fill_string = (inject && *inject) ? inject : "BRO-RST\n"; char *payload = &pkt[40]; int n = strlen(fill_string); int i; for ( i = size; i > n + 1; i -= n ) { strcpy(payload, fill_string); payload += n; } for ( ; i > 0; --i ) *(payload++) = '\n'; } tcp->th_sum = ~tcp_checksum(ip, tcp, size); while ( redundancy-- > 0 ) { cc = send(s, (char *) ip, pktlen, 0); if (cc < 0 || cc != pktlen) pgripe("problem in sendto()"); usleep(delay * 1000); } free(pkt); } void terminate(int s, const char *from_addr, int from_port, u_int32_t from_seq, const char *to_addr, int to_port, u_int32_t to_seq, int num, int redundancy, int stride, int delay, const char *inject) { struct sockaddr where_from, where_to; struct sockaddr_in *from = (struct sockaddr_in *) &where_from; struct sockaddr_in *to = (struct sockaddr_in *) &where_to; memset(from, 0, sizeof(*from)); memset(to, 0, sizeof(*to)); #ifdef SIN_LEN from->sin_len = to->sin_len = sizeof(*to); #endif /* SIN_LEN */ from->sin_family = to->sin_family = AF_INET; if ( inet_aton(from_addr, (struct in_addr *) &from->sin_addr) == 0 ) gripe("bad from address %s", from_addr); if ( inet_aton(to_addr, (struct in_addr *) &to->sin_addr) == 0 ) gripe("bad to address %s", to_addr); if ( connect(s, &where_to, sizeof(where_to)) < 0 ) pgripe("can't connect"); while ( num-- > 0 ) { send_pkt(s, from->sin_addr, from_port, from_seq, to->sin_addr, to_port, to_seq, 0, redundancy, delay, (*inject ? 0 : TH_RST) | TH_ACK, inject); if ( num > 0 && stride > 1 ) send_pkt(s, from->sin_addr, from_port, from_seq, to->sin_addr, to_port, to_seq, stride, redundancy, delay, TH_ACK, inject); from_seq += stride; } } void usage() { fprintf(stderr, "%s [-R] [-I text-to-inject] [-d delay-msec] [-n num] [-r redundancy] [-s stride] from_addr from_port from_seq to_addr to_port to_seq\n", prog_name); exit(0); } int main(int argc, char **argv) { extern char* optarg; extern int optind, opterr; const char *from_addr, *to_addr; char inject[8192]; int from_port, to_port; u_int32_t from_seq, to_seq; int delay = 0.0; int redundancy = 1; int num = 1; int stride = 1; int reverse = 0; int s; int on = 1; int op; prog_name = argv[0]; opterr = 0; inject[0] = 0; while ( (op = getopt(argc, argv, "RI:d:n:r:s:")) != EOF ) switch ( op ) { case 'R': reverse = 1; break; case 'I': { char *ap = optarg; char *ip; for ( ip = inject; *ap; ++ip, ++ap ) { if ( ap[0] == '\\' && ap[1] == 'n' ) *ip = '\n', ++ap; else *ip = *ap; } } break; case 'd': delay = atoi(optarg); break; case 'n': num = atoi(optarg); break; case 'r': redundancy = atoi(optarg); break; case 's': stride = atoi(optarg); break; default: usage(); break; } if ( argc - optind != 6 ) usage(); s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); if ( s < 0 ) pgripe("couldn't create raw socket"); setuid(getuid()); if (setsockopt(s, 0, IP_HDRINCL, (char *) &on, sizeof(on)) < 0) pgripe("can't turn on IP_HDRINCL"); from_addr = argv[optind++]; from_port = atoi(argv[optind++]); from_seq = strtoul(argv[optind++], 0, 10); to_addr = argv[optind++]; to_port = atoi(argv[optind++]); to_seq = strtoul(argv[optind++], 0, 10); if ( reverse ) terminate(s, to_addr, to_port, to_seq, from_addr, from_port, from_seq, num, redundancy, stride, delay, inject); else terminate(s, from_addr, from_port, from_seq, to_addr, to_port, to_seq, num, redundancy, stride, delay, inject); return 0; }