// // // tratt.c - bridge 2 physical network segments over a TCP/IP tunnel // // (c)2001 OlleB // // original concept by Ag3nt0nd and YRG // // // TODO: // // bandwidth throttling/limiting // // transport independant design, udp, GRE/AH transports // // support for other media types? ieee802.11? TokenRing (ugh)? // // runtime tuning of control values (throttling etc.) // // /* Copyright (c) 2001, OlleB All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of the author nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pcap.h" #define VERSION "0.04-tc" #define PCAP_SNAP_LEN 1514 // from bpf.h #define DLT_EN10MB 1 /* Ethernet (10Mb) */ #define DLT_EN100MB 100 /* Ethernet (100Mb) */ #ifdef WPCAP // libwpcap definitions extern int pcap_sendpacket(pcap_t *p, u_char *buf, int size); #endif #ifdef LIBNET // libnet definitions void *libnet_open_link_interface(char *, char *); int libnet_write_link_layer(void *, const u_char *, u_char *, int); #endif #ifndef __GETOPT_H__ // getopt definitions extern int getopt(int, char * const *, const char *); extern char *optarg; #endif // the tratt header typedef struct tratthdr_s { char tratt[6]; // "tratt\0" bpf_u_int32 len; // length of packet } tratthdr_t; // ethernet and arp headers ripped from pcapsend.c by Andy Lutomirski struct ethernet_hdr { #ifndef ETHER_ADDR_LEN #define ETHER_ADDR_LEN 6 #endif u_char ether_dhost[ETHER_ADDR_LEN]; /* destination ethernet address */ u_char ether_shost[ETHER_ADDR_LEN]; /* source ethernet address */ u_short ether_type; /* packet type ID */ #define ETH_IP 0x0800 #define ETH_ARP 0x0806 }; struct arp_hdr { u_short ar_hrd; /* format of hardware address */ #define ARPHRD_ETHER 1 /* ethernet hardware format */ u_short ar_pro; /* format of protocol address */ u_char ar_hln; /* length of hardware address */ u_char ar_pln; /* length of protocol addres */ u_short ar_op; /* operation type */ #define ARPOP_REQUEST 1 /* req to resolve address */ #define ARPOP_REPLY 2 /* resp to previous request */ #define ARPOP_REVREQUEST 3 /* req protocol address given hardware */ #define ARPOP_REVREPLY 4 /* resp giving protocol address */ #define ARPOP_INVREQUEST 8 /* req to identify peer */ #define ARPOP_INVREPLY 9 /* resp identifying peer */ /* * These should implementation defined but I've hardcoded eth/IP. */ u_char ar_sha[6]; /* sender hardware address */ u_char ar_spa[4]; /* sender protocol address */ u_char ar_tha[6]; /* target hardware address */ u_char ar_tpa[4]; /* target protocol address */ }; // // die - printf vararg errors and quit // static void die(char *format, ...) { va_list args; va_start(args, format); fprintf(stderr, "\ntratt %s: ", VERSION); vfprintf(stderr, format, args); fprintf(stderr, "\n"); va_end(args); fflush(stderr); exit(1); } // // pcap_init - get handle to pcap device // pcap_t *pcap_init(char *device, char *filter) { pcap_t *pd; struct bpf_program bp; char errbuf[PCAP_ERRBUF_SIZE]; pd = pcap_open_live(device, PCAP_SNAP_LEN, 1, 1, errbuf); if (pd == NULL) die("pcap_open_live: %s", errbuf); fprintf(stderr,"listening on %s\n", device); if (pcap_compile(pd, &bp, filter, IFF_PROMISC, 0xffffffff) < 0) die("pcap_compile: %s", errbuf); if (pcap_setfilter(pd, &bp) < 0) die("pcap_setfilter: %s", errbuf); return(pd); } // // tcp_accept - get an incoming socket connection // int tcp_accept(u_int16_t port) { int s,e; struct sockaddr_in sin; fprintf(stderr,"waiting for connection on port %i\n", port); sin.sin_family = AF_INET; sin.sin_addr.s_addr = INADDR_ANY; sin.sin_port = htons(port); s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (s == -1) die("tcp_accept: unable to create socket\n"); e = bind(s, (struct sockaddr *) &sin, sizeof(sin)); if (e == -1) die("tcp_accept: unable to bind() socket\n"); e = listen(s, 0); if (e == -1) die("tcp_accept: unable to listen() on socket\n"); e = accept(s, (struct sockaddr *) &sin, &e); if (e == -1) die("tcp_accept: unable to accept() connection\n"); close(s); return(e); } // // tcp_connect - get an outgoing socket connection // int tcp_connect(char *addr, u_int16_t port) { int s,e; struct hostent *h; struct sockaddr_in sin; h = gethostbyname(addr); if (!h) die("tcp_connect: unable to resolve host\n"); sin.sin_family = h->h_addrtype; sin.sin_addr.s_addr = *((u_int32_t *) h->h_addr); sin.sin_port = htons(port); s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (s == -1) die("tcp_connect: unable to create socket\n"); e = connect(s, (struct sockaddr *) &sin, sizeof(sin)); if (e == -1) die("tcp_connect: unable to connect()\n"); return(s); } // // packet_insert_rawsock - insert a packet using raw sockets // void packet_insert_rawsock(u_char *device, u_char *packet, unsigned int len) { die("packet_insert_rawsock: not implemented yet!\n"); } // // do_layer2_magic - rewrite ethernet addresses in packet // void do_layer2_magic(u_char *packet, char *mymac) { struct ethernet_hdr *ether; struct arp_hdr *arp; ether = (struct ethernet_hdr *)packet; arp = (struct arp_hdr *)packet+sizeof(struct ethernet_hdr); // use my ethernet address bcopy(mymac, ether->ether_shost, 6); } // // get_my_mac - lookup hardware address of device // char *get_my_mac(char *device) { int s; struct ifreq ifrq; char *tmp; #ifdef WPCAP char *t; int i,ii; char cygdev[10]="\0"; char errbuf[PCAP_ERRBUF_SIZE]; char p; #endif s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (s == -1) die("get_my_mac: unable to create socket\n"); memset(&ifrq, 0, sizeof(ifrq)); #ifdef WPCAP t=pcap_lookupdev(errbuf); if (t==NULL) die("get_my_mac: pcap_lookupdev failed: %s\n", errbuf); // strip unicode nulls if necessary if (t[1]=='\0') { ii=0; for (i=0;;i++) { if (p == '\0' && t[ii] == '\0') break; p = t[i] = t[ii]; ii += 2; } } // translate pcap device to cygwin device ii=i=0; do { if ((i==0)||(t[i-1]==0)) { //fprintf(stderr, "device%i: %s\n",ii,t+i); if (strncmp(t+i, device, strlen(t+i))==0) { snprintf(cygdev, 9, "eth%i", ii); cygdev[9]='\0'; } i++; ii++; } else i++; } while (!(t[i]==0 && t[i-1]==0)); if (strlen(cygdev)<1) die("get_my_mac: could not find cygwin devicename\n"); //fprintf(stderr, "cygdev = %s\n",cygdev); strncpy(ifrq.ifr_name, cygdev, IFNAMSIZ-1); #else strncpy(ifrq.ifr_name, device, IFNAMSIZ-1); #endif if (ioctl(s, SIOCGIFHWADDR, &ifrq) < 0 ) die("couldn't find hardware address for %s\n", device); close(s); tmp = malloc(6); if (tmp==NULL) die("get_my_mac: could not malloc 6 bytes for hwaddr"); bcopy(ifrq.ifr_hwaddr.sa_data, tmp, 6); return tmp; } // // tratt - the part that actually does stuff // void tratt(char *dev, char *filter, int s) { int arg,e,p,i; char *device=dev; pcap_t *pd; u_char *packet = NULL; struct pcap_pkthdr pcaphdr; tratthdr_t tratthdr = {"tratt\0", 0}; char errbuf[PCAP_ERRBUF_SIZE]; char *mymac; struct ethernet_hdr *ether; #ifdef WPCAP u_char tmpchar = 'A'; int ii; #endif #ifdef LIBNET void *l; #endif // get default device if (device == NULL) { device = pcap_lookupdev(errbuf); if (device == NULL) die("pcap_lookupdev: %s", errbuf); #ifdef WPCAP // strip unicode nulls if necessary if (device[1]=='\0') { ii=0; for (i=0;;i++) { if (tmpchar == '\0' && device[ii] == '\0') break; tmpchar = device[i] = device[ii]; ii += 2; } } #endif } // open pcap device if ((pd = pcap_init(device, filter)) == NULL) die("pcap_init: failed"); // check for ethernet if (!(pcap_datalink(pd)==DLT_EN10MB || pcap_datalink(pd)==DLT_EN100MB)) die("link layer type %i not supported!\n", pcap_datalink(pd)); // discover mac address mymac = get_my_mac(device); fprintf(stderr, "hwaddr = %.2X:%.2X:%.2X:%.2X:%.2X:%.2X\n",mymac[0]&0xff,mymac[1]&0xff,mymac[2]&0xff,mymac[3]&0xff,mymac[4]&0xff,mymac[5]&0xff); #ifdef LIBNET // open libnet link l = libnet_open_link_interface(device, errbuf); if (!l) die("libnet_open_link_interface: %s\n", errbuf); #endif i=0; // enter main program loop while(1) { // print hashmarks i++; if (i==100) { i=0; fprintf(stderr,"."); } // process next packet from pcap packet = (char *)pcap_next(pd, &pcaphdr); if (packet) { // check if snaplen was adequate if (pcaphdr.len != pcaphdr.caplen) fprintf(stderr, "packet truncated by pcap! increase PCAP_SNAP_LEN (%i)\n\n", PCAP_SNAP_LEN); // check for & exclude injected packets ether = (struct ethernet_hdr *)packet; if (memcmp(ether->ether_shost, mymac, 6)!=0) { // make socket blocking arg = 0; if (ioctl(s, FIONBIO, &arg)) die("could not make socket blocking!\n"); // send tratt header to remote tratt tratthdr.len = pcaphdr.len; p=0; while (p0) p += e; else die("could not send header, connection closed?\n"); //if (e!=sizeof(tratthdr_t)) fprintf(stderr, "sent %i of %i header bytes.\n\n", e, p); } //fprintf(stderr, "send header sent %i bytes.\n\n", e); // send packet to remote tratt p=0; while (p0) p += e; else die("could not send packet, connection closed?\n"); //if (e!=tratthdr.len) fprintf(stderr, "sent %i of %i packet bytes.\n\n", e, p); } //fprintf(stderr, "send packet sent %i bytes.\n\n", p); } /* exclude injected packets */ } /* process next packet from pcap */ // make socket non-blocking arg = 1; if (ioctl(s, FIONBIO, &arg)) die("could not make socket non-blocking!\n"); // process traffic from remote tratt e = recv(s, (char *)&tratthdr, sizeof(tratthdr_t), 0); if (e > 0) { // make socket blocking arg = 0; if (ioctl(s, FIONBIO, &arg)) die("could not make socket blocking!\n"); // make sure we have the full header if (e != sizeof(tratthdr_t)) { //fprintf(stderr, "rcvd %i header bytes.\n\n", e); p=e; while(p0) p += e; else die("could not recv header, connection closed?\n"); //fprintf(stderr, "retry rcvd %i header bytes.\n\n", e); } } // sanity check header if (strncmp(tratthdr.tratt, "tratt\0", 6)!=0) die("invalid tratt header recieved!\n"); //fprintf(stderr, "tratt header recieved! %i bytes in packet.\n\n", tratthdr.len); // malloc for packet packet = NULL; packet = malloc(tratthdr.len+1); if (packet == NULL) die("malloc for packet size %i failed!", tratthdr.len); // recv packet p=0; while (p0) p += e; else die("could not recv packet, connection closed?\n"); //if (e!=tratthdr.len) fprintf(stderr, "rcvd %i of %i packet bytes.\n\n", e, p); } // insert packet do_layer2_magic(packet, mymac); #ifdef WPCAP e = pcap_sendpacket(pd, packet, tratthdr.len); if (e<0) die("pcap_sendpacket: couldn't insert packet: %s\n", pcap_geterr(pd)); #else #ifdef LIBNET e = libnet_write_link_layer(l, device, packet, tratthdr.len); if (e<0) die("libnet_write_link_layer: couldn't insert packet!\n"); #else packet_insert_rawsock(device, packet, tratthdr.len); #endif /* LIBNET */ #endif /* WPCAP */ // free the mallocs free(packet); } /* process traffic from remote tratt */ } /* main program loop */ pcap_close(pd); } // // print_device_list - print a pretty list of network interfaces // void print_device_list(void) { char *t; int i; int DescPos=0; char *Desc; char errbuf[PCAP_ERRBUF_SIZE]; #ifdef WPCAP int ii; char p; #endif fprintf(stderr,"Available pcap devices:\n\n"); t=pcap_lookupdev(errbuf); if (t==NULL) die("print_device_list: pcap_lookupdev failed: %s\n", errbuf); #ifdef WPCAP // strip unicode nulls if necessary if (t[1]=='\0') { ii=0; for (i=0;;i++) { if (p == '\0' && t[ii] == '\0') break; p = t[i] = t[ii]; ii += 2; } } #endif // this part is ripped from windump i=0; while(*(t+DescPos)!=0 || *(t+DescPos-1)!=0) DescPos++; DescPos<<=1; Desc=(char*)t+DescPos+2; while (!(t[i]==0 && t[i-1]==0)) { if (t[i]==0) { fprintf(stderr, "%c",' '); fprintf(stderr, "%c",'('); while(*Desc!=0){ fprintf(stderr, "%c",*Desc); Desc++; } Desc++; fprintf(stderr, "%c",')'); fprintf(stderr, "%c",'\n'); } else fprintf(stderr, "%c",t[i]); i++; } fprintf(stderr, "%c",'\n'); } // // usage - print usage information and quit // void usage(void) { fprintf(stderr,"Usage: tratt [-D] <-l | -t host> [-p port] [-d net_device] [-f bpf_filter]\n\n"); exit(1); } // // main - program entrypoint // int main(int argc, char **argv) { int arg,s; int listen = 0; short port = 80; char *device = NULL; char *target = NULL; char *filter = "ip or arp"; fprintf(stderr,"\ntratt %s (c)2001 \n\n", VERSION); // parse the commandline options while((arg = getopt(argc,argv,"lDd:t:p:f:")) != EOF) { switch (arg) { // set listen switch case 'l': listen = 1; break; // set target IP case 't': target=optarg; break; // set target port case 'p': if ((atoi(optarg)>65536) || (atoi(optarg) < 0)) die("%i is not a valid port value!\n", port); else port = atoi(optarg); break; // set pcap device case 'd': device=optarg; break; // set pcap filter case 'f': filter=optarg; break; case 'D': print_device_list(); exit(1); break; // usage default: usage(); } } // die with usage if no remote tratt specified if ((!listen) && (target == NULL)) usage(); // connect to remote tratt if (listen) s = tcp_accept(port); else s = tcp_connect(target, port); // sanity check socket if (s < 0) die("socket value insane! (%i)\n", s); // start tratting tratt(device, filter, s); exit(0); }