├── CHANGES ├── COPYING ├── CREDITS ├── MISC ├── Makefile.in ├── README ├── aclocal.m4 ├── config.guess ├── config.sub ├── configure ├── configure.ac ├── doc ├── API.html ├── API.txt ├── LINUX ├── NEW_LIBPCAP ├── PATCH ├── PERFORMANCE ├── TESTS └── bugtraq_post ├── install-sh ├── mkinstalldirs ├── samples ├── Makefile.in ├── chksum_ctl.c ├── nids_next.c ├── overflows.c ├── printall.c └── sniff.c └── src ├── Makefile.in ├── allpromisc.c ├── checksum.c ├── checksum.h ├── config.h.in ├── hash.c ├── hash.h ├── ip_fragment.c ├── ip_fragment.h ├── ip_options.c ├── killtcp.c ├── libnids-track-established.patch ├── libnids.3 ├── libnids.3.mdoc ├── libnids.c ├── nids.h ├── scan.c ├── scan.h ├── tcp.c ├── tcp.h ├── util.c └── util.h /CHANGES: -------------------------------------------------------------------------------- 1 | v1.26 Sep 24 2015 2 | - Set default tcp_flow_timeout to 1 hour, which should cover most firewalls' 3 | and OS's TCP idle timeout 4 | 5 | v1.25 Feb 18 2013 6 | - added timeouts for tcp streams 7 | 8 | v1.24 Mar 14 2010 9 | - fixed another remotely triggerable NULL dereference in ip_fragment.c 10 | - unofficial patch that enables tracking of already established TCP connections 11 | - missing reset of some tcp_* variables upon nids_exit 12 | - correct calculation of radiotap header 13 | - compilation warning fixes with newer gcc 14 | - use pcap_get_selectable_fd() instead of pcap_fileno() 15 | 16 | v1.23 Feb 23 2008 17 | - fixed remotely triggerable NULL dereference in ip_fragment.c 18 | - fix DLT_PRISM_HEADER linkoffset calculation 19 | - check for DATA_FRAME_IS_QOS in wireless frames 20 | - free queued tcp segments with too old seq 21 | 22 | v1.22 Jul 22 2007 23 | - in TCP stream, the byte with absolute offset 0 was treated as urgent data; 24 | fixed 25 | - DLT_IEEE802_11_RADIO handling 26 | - added a few missing checks for failed malloc 27 | 28 | v1.21 May 10 2006 29 | - more externals to access libnids' intrinsics from the outside 30 | - nids_unregister_*() 31 | - UDP checksumming fix (0 is not an error according to RFC768) 32 | - nids_params.tcp_workarounds 33 | - nids_params.multiproc and queue_limit: merged a patch which creates a 34 | separate thread for packet capture; 35 | - in killtcp.c, send two more RST packets (required because of MS05-019 36 | patch) 37 | - glibc 2.4 syslog.h disaster workaround 38 | 39 | v1.20 Feb 4 2005 40 | - added wscale option parsing; surprisingly, it seems to be in some use 41 | - added nids_dispatch(), for systems which do not ignore pcap timeout 42 | - ability to specify hosts/networks for which we do not check checksums 43 | 44 | v1.19 Aug 08 2004 45 | - fixed signed/unsigned comparisons; 1.18 could be possibly crashed in tcp 46 | options parsing (though an unlikely to happen memory layout is required); 47 | now the source is compiled with -W -Wall 48 | - export pcap header of the last received packet (to get timestamp etc) 49 | - export the timeout parameter to pcap_open_live in params 50 | - support DLT_PRISM_HEADER 51 | - support DLT_PPP_SERIAL 52 | - let through dataless acks 53 | - fixed raw_init() prototype 54 | - switched to use %edi instead of %ebx in csum_partial to make gcc-3.5 happy 55 | when compiling with -fPIC; cleaned inline asm 56 | - fixed a bug when a queued FIN segment was not processed properly, which 57 | resulted in not closing a stream 58 | 59 | v1.18 Oct 15 2003 60 | - reject tcp packets with old timestamp; needed to pass fragroute test; 61 | well, linux 2.0.36 did not support this ;) 62 | - fixed memory corruption which could be caused by overlarge TCP packets 63 | - adjusted checksum.c to not use multiline literals (for gcc 3.3) 64 | - in configure.in, even if found libnet files, try compilation; there is 65 | another library with the same name 66 | - fix a bug in "collect" field handling; if you did collect-- and then 67 | collect++ (which is rare), you would get a single junk packet 68 | - correct handling of exec_prefix in configure.in 69 | - unlink config.status in "make distclean" 70 | - use pcap_hdr->caplen instead pcap_hdr->len; the only gain seems to be to 71 | gracefully handle pcap files with too short snaplen 72 | - changed soname to libnids.so.1.x, as binary compatibility is not 73 | guaranteed 74 | - switched to sourceforge as homepage 75 | 76 | v1.17 Dec 12 2002 77 | - fixed a stupid bug in TCP reassembly; having received a particular order 78 | of TCP out of frame segments, libnids could lost track of the current 79 | seq, and miss the following data stream 80 | - DLT_FDDI 81 | - benign typo in hash.c 82 | - mentioned usefulness of two process buffering on a fast network 83 | 84 | v1.17rc1 Aug 30 2002 85 | - support for libnet-1.1 and --with-libnet=no 86 | - added support for libpcap save files 87 | - finally, DLT_LINUX_SLL is recognized 88 | - removed a horrible assumption on sizeof(pointer); it could result in 89 | segfault in scan.c 90 | - --enable-shared 91 | - __i386 -> __i386__ || __i386 :( 92 | - support for 802.1Q VLAN 93 | - support for wireless frames (DLT_IEEE802_11) 94 | - got rid of (obsolete) pcap_open_live_new 95 | - bail out if link type is unknown, instead of pretending it is ethernet 96 | - $(MAKE) -> $(MAKE) $(AM_MAKEFLAGS) 97 | - added a working link to Ptacek-Newsham paper 98 | - %hi -> %hu :) 99 | - align IP header if necessary (should not be) 100 | - improved libraries detection 101 | - mentioned usefulness od setsockopt(...SO_RCVBUF...) on a fast network 102 | 103 | v1.16 Nov 3 2000 104 | - nah, at least a release forced by a security bug. A typo in libnids.c 105 | could cause libnids to segfault when source routed frame has been received. 106 | 107 | v1.15 Oct 9 2000 108 | - token ring support 109 | - new configurable option (non-default): if a tcp callback hasn't processed 110 | all available data, it is called immediately again 111 | - fixed alignment in hash.c, which caused sigsegv on Sparc 112 | - another _obviously_ redundant include file added to configure test progs 113 | - html version of the API documentation 114 | 115 | v1.14 Jun 28 2000 116 | - fixed memory leak in tcp.c (queued tcp segments used to be not freed 117 | after connection termination) 118 | - added support to capture packets on all interfaces, including loopback 119 | (linux only, using new libpcap features - autoconf changed) 120 | - added nids_register_udp(); if anyone cares for UDP checksums... 121 | - stupid bug in nids_register_ip_frag() fixed 122 | - removed comments from asm code in checksum.c; Solaris compiler didn't 123 | recognized them - sigh 124 | - signed/unsigned bug in scan.c fixed 125 | - tcp callback could be notified even if no nw data arrived - fixed 126 | - added ability to disable tcp processing 127 | - added ability to refrain from setting promisc flag 128 | - libc5 support 129 | - alpha platform support 130 | - now it's possible to do setuid(nobody) after nids_init() with no loss 131 | of functionality (killtcp works) 132 | - removed pcap_lookupnet() call - one can capture packets from an interface 133 | with no IP assigned 134 | - hash function in tcp.c with pseudorandom parameters 135 | - #define NIDS_MAJOR 1, #define NIDS_MINOR 14 in nids.h 136 | 137 | v1.13 Jan 18 2000 138 | - Changes by Dug Song: 139 | - GNU autoconf support 140 | - code cleanup and new libnids(3) manpage 141 | - disable portscan detection if scan_num_hosts == 0 142 | - new field in nids_params for pcap(3) support: pcap_filter 143 | - subtle bugfix in ip_check_ext() 144 | - Solaris support (endianness fixes, etc.) 145 | - another tiny check in tcp.c 146 | 147 | v1.12 Sep 15 1999 148 | - processing of ICMP Destination Unreachable 149 | - nids_next() and nids_getfd() functions added; new fields in nids_params: 150 | no_mem, ip_filter 151 | - clean error reporting via nids_errbuf; used by nids_init(), nids_next(), 152 | nids_getfd() 153 | - some more samples 154 | 155 | v1.11 Aug 20 1999 156 | - some stupid bugs removed (hopefully no more segfaults) 157 | 158 | v1.1 Aug 10 1999 159 | - *BSD support added by Dug Song 160 | - some minor cleanups in libnids.c 161 | - changed the license to GPL 162 | 163 | v1.0 July 30 1999 164 | - Initial public release 165 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 5 | 675 Mass Ave, Cambridge, MA 02139, USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Library General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | Appendix: How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 19yy 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License 307 | along with this program; if not, write to the Free Software 308 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) 19yy name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Library General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /CREDITS: -------------------------------------------------------------------------------- 1 | 2 | Libnids author: 3 | 4 | Nergal 5 | 6 | 7 | Corrections to make libnids build on *BSD; bugfixes, GNU autoconf support, 8 | libnids(3) manpage, alpha platform problems solved: 9 | 10 | Dug Song 11 | 12 | 13 | The fix of the problem with gcc 2.95 and 2.0.x asm code; the ideas of 14 | nids_getfd(), nids_next(): 15 | 16 | Adam Langley 17 | 18 | 19 | The idea of ip_filter function 20 | 21 | Sebastian 22 | 23 | 24 | WIN32 support 25 | 26 | Michael Davis 27 | 28 | 29 | Memory leak in tcp.c reported 30 | 31 | billzh , Rob Willis 32 | 33 | 34 | Possible improper behaviour of notify() 35 | 36 | Rob Willis 37 | 38 | 39 | Stupid bug found in nids_register_ip_frag() 40 | 41 | Gabe Wachob , Solar Designer 42 | 43 | 44 | Patches to support libc5 45 | 46 | Solar Designer , Denis Ducamp 47 | 48 | 49 | Requests for features, constructive critics 50 | 51 | Solar Designer 52 | 53 | 54 | Support for token ring 55 | 56 | Vacuum 57 | 58 | 59 | Alignment bug in hash.c 60 | 61 | Anders Thulin 62 | Ken Mandelberg 63 | 64 | 65 | in.h missing in "configure" test programs. Damn, if a include file X needs 66 | structures defined in Y, why doesn't X include Y ? Stupid Solaris. 67 | 68 | Raymond Scott 69 | 70 | 71 | dangerous typo found in libpcap.c, in code handling source routed frames 72 | 73 | Keiji Takeda 74 | 75 | 76 | support for wireless frames (DLT_IEEE802_11) 77 | 78 | William McVey 79 | 80 | 81 | support for libpcap save files 82 | 83 | Scott Renfro 84 | actually, many people sent their save file patches; I picked Scott's one 85 | 86 | 87 | DLT_LINUX_SLL 88 | 89 | Robin Redeker 90 | 91 | 92 | support for 802.1Q VLAN 93 | 94 | Jason Ackley 95 | 96 | 97 | added AM_MAKEFLAGS 98 | 99 | Brad 100 | 101 | 102 | added a working link to Ptacek-Newsham paper 103 | 104 | Nick Drage 105 | 106 | 107 | replaced %hi with %hu 108 | 109 | Kazennov Vladimir 110 | 111 | 112 | report on configure unable to find libraries 113 | 114 | Eric Darchis 115 | 116 | 117 | DLT_FDDI 118 | 119 | jkrage@buser.net 120 | 121 | 122 | random() -> rand() 123 | 124 | Davide Madrisan 125 | 126 | 127 | provided pcap dump file triggering a bug in TCP reassembly 128 | 129 | Yoav Weiss 130 | 131 | 132 | reported a problem with fragroute "tcp_chaff paws", 133 | reported a problem with "collect" fields handling, 134 | exec_prefix patch, 135 | reported a problem with short snaplen pcapfiles 136 | 137 | Russ Fink 138 | 139 | 140 | reported a problem with memory corruption 141 | 142 | Robert Watson 143 | 144 | 145 | reported a problem with multiline literals and gcc 3.3 146 | 147 | many folks; the first report from Arkadiusz Patyk 148 | 149 | 150 | reported a problem with signed/unsigned in get_ts() 151 | 152 | many folks; the first report from Russ Fink 153 | 154 | 155 | suggestion to export the pcap header of the last packet 156 | 157 | Arthur Bergman 158 | 159 | 160 | suggestion to make pcap_timeout parameter settable in params 161 | 162 | Pedro Paulo Jr 163 | 164 | 165 | prism wireless cards support 166 | 167 | snax 168 | 169 | 170 | reported a problem which revealed that dataless acks are not let through 171 | 172 | Russ Fink 173 | 174 | 175 | reported raw_init() breakage 176 | 177 | Brian Wesley Dillard 178 | 179 | 180 | DLT_PPP_SERIAL 181 | 182 | Jean-Edouard BABIN 183 | 184 | 185 | Reported a problem with gcc 3.5 and csum_partial 186 | 187 | Jon Oberheide 188 | 189 | 190 | man page fixes 191 | 192 | Solar Designer 193 | 194 | 195 | suggested inline asm fixes to reflect the usage of registers 196 | 197 | Solar Designer 198 | 199 | 200 | nids_dispatch(), API/documentation improvements 201 | 202 | Mike Pomraning 203 | 204 | 205 | submission of pcap files containing tcp stream with wscale 206 | 207 | Marc A. Lehmann 208 | Robin Redeker 209 | 210 | 211 | manpage suggestions, valuable discussions 212 | 213 | Marc A. Lehmann 214 | 215 | 216 | two threads patch (one for packet capture, other for packets processing) 217 | 218 | Erno Rigo 219 | 220 | 221 | more externals to access libnids' intrinsics from the outside 222 | (nids_last_pcap_data, nids_linkoffset, nids_prm.pcap_desc, 223 | nids_find_tcp_stream(), nids_free_tcp_stream(), nids_pcap_handler() 224 | and nids_exit()), more API functions (nids_unregister_*()), 225 | fix to prevent adding several times the same user-defined callback 226 | function with nids_register_*()), UDP checksumming fix (0 is not an error 227 | according to RFC768), timeout'ing of TCP streams closed despite 228 | needed retransmissions if nids_params.tcp_workarounds is non-zero, 229 | tcp_stream.user for connection-wide user-defined parameter, 230 | and updated manpage & API docs :) 231 | 232 | Sebastien Raveau 233 | 234 | 235 | Persistent quering about nonworking nids_killtcp() against XP SP2: 236 | 237 | "Pedro Paulo de Magalhaes Oliveira Junior" 238 | "Rafael Donnici de Azevedo" 239 | 240 | 241 | "absolute offset 0 byte" bug report 242 | 243 | Treker Chen 244 | 245 | 246 | DLT_IEEE802_11_RADIO support 247 | 248 | crass@berlios.de 249 | 250 | 251 | fix DLT_PRISM_HEADER linkoffset calculation; 252 | check for DATA_FRAME_IS_QOS in wireless frames (code from tcpdump) 253 | 254 | spotted by xenion 255 | 256 | 257 | free queued tcp segments with too old seq 258 | 259 | "Xiang, Lin" wallyymir@yahoo.com 260 | 261 | 262 | reported possible NULL dereference in ip_fragment.c 263 | 264 | "Alfred E. Heggestad" 265 | 266 | 267 | notes about global variables in case of multiproc operation 268 | 269 | "Ben, Wu CheokMan" 270 | 271 | 272 | David Cannings , Abhisek Datta 273 | 274 | missing reset of some tcp_* variables upon nids_exit 275 | 276 | 277 | Michal Pecio 278 | 279 | correct calculation of radiotap header 280 | 281 | 282 | Jon Oberheide , Alon Bar-Lev 283 | 284 | compilation warning fixes 285 | 286 | 287 | Gao Xia 288 | 289 | another possible NULL dereference in ip_fragment.c fix 290 | 291 | 292 | Jeff Nathan 293 | 294 | pcap_get_selectable_fd() 295 | 296 | 297 | Alon Bar-Lev 298 | 299 | unofficial patch that enables tracking of already established TCP 300 | connections 301 | 302 | 303 | Libnids uses libpcap and libnet libraries: 304 | 305 | LBNL Network Research Group 306 | ftp://ftp.ee.lbl.gov/libpcap.tar.Z 307 | new versions available at http://www.tcpdump.org/release/ 308 | 309 | Mike D. Schiffman 310 | route|daemon9 311 | http://www.packetfactory.net/libnet 312 | 313 | Libnids emulates algorithms present in Linux 2.0.36 kernel. The files 314 | ip_fragment.c and ip_options.c are the modified respective files from Linux 315 | 2.0.36 kernel source. The asm code used for checksums computing is taken 316 | from Linux 2.2.10 kernel source. 317 | -------------------------------------------------------------------------------- /MISC: -------------------------------------------------------------------------------- 1 | 2 | ==================== 3 | libnids-1.26 4 | ==================== 5 | 6 | 1. Building 7 | ----------- 8 | 9 | Libnids uses libpcap (can be retrieved from 10 | http://www.tcpdump.org/release/) and libnet (available at 11 | http://www.packetfactory.net/libnet). All credits to autors of these libs. 12 | As already mentioned in README, currently libnids will compile on 13 | Linux, any *BSD and Solaris. WIN32 port is mantained separately. 14 | In order to build libnids, issue "./configure;make" command in top 15 | directory. Library files libnids.so and libnids.a should be created in "src" 16 | directory. "make install" will install library and header files. You may 17 | wish to consult "./configure --help" output for available options. 18 | 19 | 2. Limitations 20 | -------------- 21 | 22 | In their paper, T. Ptacek and T. Newsham observed that various 23 | operating systems implement IP stack differently and can interpret 24 | differently the same packet. It means that having seen a IP packet, NIDS has 25 | to interpret it with regard to receiving operating system type. A perfect NIDS 26 | E-component should possess knowledge on all operating systems network 27 | implementation oddities. I don't know any actual NIDS implementation that 28 | takes the previous into consideration. 29 | Libnids 1.0 was meant to reliably emulate behavior of Linux 2.0.36 30 | kernel. Thanks to libnids testing, some bugs in 2.0.36 networking code were 31 | found. One of them enabled an attacker to perform blind TCP spoofing against 32 | 2.0.x kernels, including 2.0.36 and 2.0.37 (that is NOT the vulnerability 33 | discovered by NAI; see my posting to Bugtrag from beginning of August 99). Info 34 | on spotted bugs was submitted to Linux kernel mantainers on 25th May 99 (before 35 | the release of 2.0.37), but none of them got fixed. File PATCH contains diffs 36 | against 2.0.37, which stop blind spoofing attack and one of data insertion 37 | attacks (now its equivalent is incorporated into Solar Designer's 38 | secure-linux-0.9 patch). Currently, libnids predicts 2.0.37 behavior as 39 | accurately as possible (with some unevitable exceptions - see my postings to 40 | Bugtraq from beginning of August 99). In extreme conditions, libnids can 41 | incorrectly emulate actions of other operating systems. However, libnids 42 | should cope with simple attacks (like these implemented in fragrouter 1.3) 43 | targetted at any OS type. 44 | All NIDS are vulnerable to DOS attacks. Libnids uses efficient data 45 | structures (i.e. hash tables) to minimize risk of CPU saturation. However, all 46 | NIDS (including ones based on libnids) has to define some resources (most 47 | notably, memory) limits. A determined attacker can attempt to make libnids use 48 | up all of its memory, which can result in dropping some data. Libnids will 49 | report such condition via its D-component interface. 50 | 51 | 3. Why does libnids emulate 2.0.x kernel instead of 2.2.x ? 52 | ------------------------------------------------------- 53 | 54 | First of all, libnids development started when 2.0.36 was the current 55 | stable kernel. Moreover, some people still prefer to use 2.0.x kernels, one of 56 | the reasons being the fact that there is still no Solar Designer 57 | non-executable stack patch for 2.2.x (not released oficially until July 99). 58 | Finally, 2.2.x kernels are highly configurable during run-time (for instance 59 | it's possible to change via proc interface the amount of kernel memory devoted 60 | for IP fragments queuing), which generally makes them unpredictable. 61 | -------------------------------------------------------------------------------- /Makefile.in: -------------------------------------------------------------------------------- 1 | # 2 | # Makefile for libnids. 3 | # 4 | # Dug Song 5 | 6 | 7 | all: static @BUILD_SHARED@ 8 | install: _install@BUILD_SHARED@ 9 | static shared _install _installshared: 10 | cd src ; $(MAKE) $(AM_MAKEFLAGS) $@ 11 | # cd samples; $(MAKE) $(AM_MAKEFLAGS) $@ 12 | clean: 13 | cd src ; $(MAKE) $(AM_MAKEFLAGS) $@ 14 | cd samples; $(MAKE) $(AM_MAKEFLAGS) $@ 15 | 16 | distclean: clean 17 | rm -f Makefile */Makefile */config.h config.status config.cache config.log *~ 18 | 19 | # EOF 20 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | 2 | ==================== 3 | libnids-1.26 4 | ==================== 5 | 6 | 1. What is libnids ? 7 | ------------------------ 8 | 9 | Libnids is a library that provides a functionality of one of NIDS 10 | (Network Intrusion Detection System) components, namely E-component. It means 11 | that libnids code watches all local network traffic, cooks received datagrams 12 | a bit (quite a bit ;)), and provides convenient information on them to 13 | analyzing modules of NIDS. Libnids performs: 14 | a) assembly of TCP segments into TCP streams 15 | b) IP defragmentation 16 | c) TCP port scan detection 17 | More technical info can be found in MISC file. 18 | So, if you intend to develop a custom NIDS, you don't have to build 19 | low-level network code. If you decide to use libnids, you have got 20 | E-component ready - you can focus on implementing other parts of NIDS. 21 | 22 | 2. Why is libnids valuable ? 23 | ---------------------------- 24 | 25 | On January 98, Thomas H. Ptacek and Timothy N. Newsham published an 26 | excellent paper entitled "Eluding Network Intrusion Detection". It's a 27 | must-read for all security concerned people, available from 28 | http://www.robertgraham.com/mirror/Ptacek-Newsham-Evasion-98.html 29 | In this paper one can find description of variety of attack against NIDS. 30 | During libnids development a lot of effort was made to make libnids immune 31 | to these attacks. During tests libnids performed TCP assembly and IP 32 | defragmentation in exactly the same way as Linux 2.0.36 hosts 33 | (targets of test packets). For details, see file TESTS; here let's just 34 | mention two things: 35 | a) libnids passed all tests implemented in fragrouter by Dug Song (see 36 | http://www.anzen.com/research/nidsbench/ ). In fact, fragrouter's tests were 37 | fairly simple when compared with other, custom ones. 38 | b) libnids IP defragmenting module contains slightly modified Linux 2.0.36 39 | kernel source files ip_fragment.c and ip_options.c. It means that libnids IP 40 | defragmentation is as reliable as one implemented in Linux 2.0.36. 41 | Libnids is easy to use and highly configurable - see API file for details. 42 | 43 | 3. On what platform does it run ? 44 | --------------------------------- 45 | 46 | Currently libnids will compile on Linux, Solaris, any *BSD. WIN32 port is 47 | available at http://www.datanerds.net/~mike/libnids.html, but currently only 48 | obsoleted versions are present there; newer ports may appear at 49 | http://www.checksum.org (in "downloads" section). 50 | 51 | 4. Who is allowed to use it ? 52 | ----------------------------- 53 | 54 | Libnids is licensed under GPL. See the file COPYING for details. 55 | 56 | 5. Contact info ? 57 | ----------------- 58 | 59 | The primary libnids site is 60 | http://libnids.sourceforge.net/ 61 | Please send bug reports, comments, or questions about this software to 62 | . 63 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | dnl 2 | dnl configure.in for libnids. 3 | dnl 4 | dnl Dug Song 5 | dnl ... mantained by Nergal 6 | 7 | AC_INIT(src/libnids.c) 8 | 9 | AC_CANONICAL_SYSTEM 10 | 11 | AC_CONFIG_HEADER(src/config.h) 12 | 13 | dnl Initialize prefix. 14 | if test "$prefix" = "NONE" ; then 15 | prefix="/usr/local" 16 | fi 17 | 18 | dnl Initialize exec_prefix. 19 | if test "$exec_prefix" = "NONE" ; then 20 | exec_prefix=$prefix 21 | fi 22 | 23 | dnl Checks for programs. 24 | AC_PROG_CC 25 | AC_PROG_RANLIB 26 | AC_PROG_INSTALL 27 | 28 | dnl Checks for header files. 29 | AC_HEADER_STDC 30 | AC_CHECK_HEADERS(sys/time.h syslog.h unistd.h) 31 | 32 | dnl Checks for typedefs, structures, and compiler characteristics. 33 | AC_C_CONST 34 | AC_C_INLINE 35 | AC_HEADER_TIME 36 | AC_C_BIGENDIAN 37 | 38 | dnl XXX - Linux sux. 39 | CFLAGS="$CFLAGS -D_BSD_SOURCE" 40 | dnl Checks for library functions. 41 | AC_CHECK_FUNCS(gettimeofday) 42 | dnl XXX - Solaris sux. 43 | AC_CHECK_LIB(socket, socket) 44 | AC_CHECK_LIB(nsl, gethostbyname) 45 | 46 | case "$target_cpu" in 47 | alpha*|arm*|hp*|mips*|sparc*) 48 | ac_cv_lbl_unaligned_fail=yes 49 | ;; 50 | *) 51 | ac_cv_lbl_unaligned_fail=no 52 | ;; 53 | esac 54 | if test $ac_cv_lbl_unaligned_fail = yes ; then 55 | AC_DEFINE(LBL_ALIGN,1,[if unaligned access fails]) 56 | fi 57 | 58 | dnl Checks for libpcap 59 | AC_MSG_CHECKING(for libpcap) 60 | AC_ARG_WITH(libpcap, 61 | [ --with-libpcap=DIR use libpcap build directory], 62 | [ case "$withval" in 63 | yes|no) 64 | AC_MSG_RESULT(no) 65 | ;; 66 | *) 67 | AC_MSG_RESULT($withval) 68 | if test -f $withval/pcap.h -a -f $withval/libpcap.a; then 69 | owd=`pwd` 70 | if cd $withval; then withval=`pwd`; cd $owd; fi 71 | PCAP_CFLAGS="-I$withval -I$withval/bpf" 72 | PCAPLIB="-L$withval -lpcap" 73 | else 74 | AC_ERROR(pcap.h or libpcap.a not found in $withval) 75 | fi 76 | ;; 77 | esac ], 78 | [ if test -f ${prefix}/include/pcap.h; then 79 | PCAP_CFLAGS="-I${prefix}/include" 80 | PCAPLIB="-L${exec_prefix}/lib -lpcap" 81 | elif test -f /usr/include/pcap/pcap.h; then 82 | PCAP_CFLAGS="-I/usr/include/pcap" 83 | PCAPLIB="-lpcap" 84 | else 85 | TMP=$LIBS 86 | LIBS="-lpcap $LIBS" 87 | AC_TRY_LINK([#include ], pcap_open_offline("",""), 88 | LIBPCAP_FOUND=1,LIBPCAP_FOUND=0) 89 | LIBS=$TMP 90 | if test $LIBPCAP_FOUND = 1 ; then 91 | PCAPLIB="-lpcap" 92 | else 93 | AC_ERROR(libpcap not found) 94 | fi 95 | fi 96 | AC_MSG_RESULT(yes) ] 97 | ) 98 | AC_SUBST(PCAP_CFLAGS) 99 | AC_SUBST(PCAPLIB) 100 | 101 | dnl Checks for libglib2.8 102 | AC_ARG_ENABLE(libglib, 103 | [ --disable-libglib use glib2 for multiprocessing support], 104 | [ 105 | AC_MSG_RESULT(skipping glib2 support) 106 | ], 107 | [ 108 | PKG_PROG_PKG_CONFIG 109 | PKG_CHECK_MODULES(GLIB, glib-2.0 >= 2.2.0,[ 110 | PKG_CHECK_MODULES(GTHREAD, gthread-2.0 >= 2.2.0,,) 111 | AC_CHECK_LIB(gthread-2.0,g_thread_init,,,$GTHREAD_LIBS) 112 | ],) 113 | ] 114 | ) 115 | 116 | dnl Checks for libnet 117 | AC_MSG_CHECKING(for libnet) 118 | AC_ARG_ENABLE(libnet, 119 | [ --disable-libnet whether to include code requiring libnet], 120 | [ case "$enableval" in 121 | yes) 122 | AC_MSG_RESULT(yes) 123 | ;; 124 | no) 125 | AC_MSG_RESULT(no) 126 | LIBNET_VER=-1 127 | ;; 128 | *) 129 | AC_ERROR(no arguments expected for --disable-libnet) 130 | ;; 131 | esac ] 132 | ) 133 | 134 | AC_ARG_WITH(libnet, 135 | [ --with-libnet=DIR use libnet build directory], 136 | [ case "$withval" in 137 | yes) 138 | ;; 139 | no) 140 | LIBNET_VER=-1 141 | AC_MSG_RESULT(no) 142 | ;; 143 | *) 144 | AC_MSG_RESULT($withval) 145 | if test -f $withval/include/libnet.h -a -f $withval/lib/libnet.a -a -f $withval/libnet-config ; then 146 | owd=`pwd` 147 | if cd $withval; then withval=`pwd`; cd $owd; fi 148 | LNET_CFLAGS="-I$withval/include `$withval/libnet-config --defines`" 149 | LNETLIB="-L$withval/lib -lnet" 150 | elif test -f $withval/include/libnet.h -a -f $withval/src/libnet.a; then 151 | owd=`pwd` 152 | if cd $withval; then withval=`pwd`; cd $owd; fi 153 | LNET_CFLAGS="-I$withval/include" 154 | LNETLIB="-L$withval/src -lnet" 155 | else 156 | echo "A working combination of libnet.h, libnet.a and libnet-config not found in $withval; get libnet from www.packetfactory.net/projects/libnet and reinstall" 157 | AC_ERROR(libnet) 158 | fi 159 | ;; 160 | esac ], 161 | [ if test "x"$LIBNET_VER = "x"-1 ; then 162 | AC_MSG_RESULT(skipping libnet) 163 | else 164 | if test -f ${prefix}/include/libnet.h -a ${exec_prefix}/lib/libnet.a ; then 165 | LNET_CFLAGS="-I${prefix}/include `${exec_prefix}/bin/libnet-config --defines 2>/dev/null`" 166 | LNETLIB="-L${exec_prefix}/lib -lnet" 167 | else 168 | LNET_CFLAGS="`libnet-config --defines 2>/dev/null`" 169 | LNETLIB="-lnet" 170 | fi 171 | fi ] 172 | ) 173 | 174 | if test "x"$LIBNET_VER != "x"-1 ; then 175 | TMPC="$CFLAGS" 176 | TMPL="$LIBS" 177 | CFLAGS="$CFLAGS $LNET_CFLAGS" 178 | LIBS="$LNETLIB $LIBS" 179 | AC_TRY_LINK([#include ], libnet_get_prand(0), 180 | LIBNET_FOUND=1,LIBNET_FOUND=0) 181 | CFLAGS="$TMPC" 182 | LIBS="$TMPL" 183 | if test $LIBNET_FOUND = 1 ; then 184 | LNETLIB="-lnet" 185 | AC_MSG_RESULT(yes) 186 | else 187 | echo "Working libnet not found; get it from www.packetfactory.net/projects/libnet and reinstall" 188 | AC_ERROR(libnet) 189 | fi 190 | fi 191 | 192 | AC_SUBST(LNET_CFLAGS) 193 | AC_SUBST(LNETLIB) 194 | 195 | BUILD_SHARED= 196 | AC_MSG_CHECKING(whether to build shared library) 197 | AC_ARG_ENABLE(shared, 198 | [ --enable-shared enable building shared libraries], 199 | [ case "$enableval" in 200 | yes) 201 | AC_MSG_RESULT(yes) 202 | BUILD_SHARED=shared 203 | ;; 204 | no) 205 | AC_MSG_RESULT(no) 206 | ;; 207 | *) 208 | AC_ERROR(no arguments expected for --enable-shared) 209 | ;; 210 | esac ], 211 | [AC_MSG_RESULT(no)] 212 | ) 213 | AC_SUBST(BUILD_SHARED) 214 | 215 | AC_MSG_CHECKING(the name of struct icmp) 216 | AC_TRY_COMPILE([#include 217 | #include 218 | #include 219 | #include ], struct icmphdr h;int c=h.type, ICMPHEADER=1,ICMPHEADER=0) 220 | AC_SUBST(ICMPHEADER) 221 | if test $ICMPHEADER = 1 ; then 222 | AC_MSG_RESULT(struct icmphdr) ; else AC_MSG_RESULT(struct icmp) 223 | fi 224 | AC_MSG_CHECKING(if tcp states are defined) 225 | AC_TRY_COMPILE([#include 226 | #include 227 | #include ], int c=TCP_ESTABLISHED,TCPSTATES=1,TCPSTATES=0) 228 | AC_SUBST(TCPSTATES) 229 | if test $TCPSTATES = 1 ; then 230 | AC_MSG_RESULT(yes) ; else AC_MSG_RESULT(no) 231 | fi 232 | 233 | AC_MSG_CHECKING(for bsd-ish struct udphdr) 234 | AC_TRY_COMPILE([#include 235 | #include 236 | #include ], struct udphdr h;int c=h.uh_ulen,HAVE_BSD_UDPHDR=1,HAVE_BSD_UDPHDR=0) 237 | AC_SUBST(HAVE_BSD_UDPHDR) 238 | if test $HAVE_BSD_UDPHDR = 1 ; then 239 | AC_MSG_RESULT(yes) ; else AC_MSG_RESULT(no) 240 | fi 241 | if test "x"$LIBNET_VER != "x"-1 ; then 242 | TMP=$CFLAGS 243 | CFLAGS="$CFLAGS $LNET_CFLAGS" 244 | AC_MSG_CHECKING(libnet version) 245 | AC_TRY_COMPILE([#include ], int c=LIBNET_PTAG_INITIALIZER, 246 | LIBNET_VER=1,LIBNET_VER=0) 247 | CFLAGS=$TMP 248 | if test $LIBNET_VER = 1 ; then 249 | AC_MSG_RESULT(looks new) ; else AC_MSG_RESULT(looks old) 250 | fi 251 | fi 252 | AC_SUBST(LIBNET_VER) 253 | AC_OUTPUT(Makefile src/Makefile samples/Makefile) 254 | -------------------------------------------------------------------------------- /doc/LINUX: -------------------------------------------------------------------------------- 1 | 2 | ==================== 3 | libnids-1.26 4 | ==================== 5 | 6 | The following applies to Linux only. 7 | Linux 2.0.x kernels introduces sockets of family PF_PACKET which 8 | allow to gather packets from all devices, including loopback (!). Recent 9 | libpcap versions (0.6.x for sure) support this feature; you have to pass 10 | device "any" to pcap_open_live in order to listen on such a socket. For 11 | backwards compatibility with libnids <= 1.16, you can also assign device "all" 12 | to nids_params.device. If nids_params.promisc is nonzero, libnids (because 13 | libpcap does not support it) will try to set all interfaces into promiscuous 14 | mode, one by one. 15 | A certain problem may arise, if the machine routes packets among its 16 | interfaces. Libpcap will pass to userspace a copy of a packet per each 17 | interface this packet travels through. This is no problem for libnids TCP 18 | reassembly, as it deals perfectly with duplicate packets - tcp callback 19 | functions will not notice anything unusual. However, UDP and IP callbacks 20 | will receive duplicate packets. 21 | 22 | -------------------------------------------------------------------------------- /doc/NEW_LIBPCAP: -------------------------------------------------------------------------------- 1 | 2 | ==================== 3 | libnids-1.26 4 | ==================== 5 | 6 | This document is obsolete; read LINUX instead ! 7 | 8 | -------------------------------------------------------------------------------- /doc/PATCH: -------------------------------------------------------------------------------- 1 | --- linux-2.0.37/net/ipv4/tcp_input.c.orig Fri Jul 23 17:25:14 1999 2 | +++ linux/net/ipv4/tcp_input.c Fri Jul 23 17:29:43 1999 3 | @@ -2764,7 +2764,18 @@ 4 | kfree_skb(skb, FREE_READ); 5 | return 0; 6 | } 7 | - 8 | + 9 | + if (sk->state==TCP_SYN_RECV && th->ack && skb->ack_seq!=sk->sent_seq) 10 | + { 11 | + /* 12 | + * Quick fix to detect too small ack_seq 13 | + * in 3rd packet of 3ws and force a RST segment. 14 | + */ 15 | + tcp_send_reset(daddr, saddr, th,sk->prot, opt, dev,0,255); 16 | + kfree_skb(skb, FREE_READ); 17 | + return 0; 18 | + } 19 | + 20 | rfc_step6: 21 | /* 22 | * If the accepted buffer put us over our queue size we 23 | -------------------------------------------------------------------------------- /doc/PERFORMANCE: -------------------------------------------------------------------------------- 1 | 2 | ==================== 3 | libnids-1.26 4 | ==================== 5 | Libnids uses efficient data structures (hash tables), so it imposes as 6 | little overhead on packets processing as possible. However, in some cases, 7 | packet content must be copies several times, which can result in burst of 8 | CPU activity. The following notes refer rather to libpcap, not libnids, but 9 | because many people seem to encounter similar performance-related problems 10 | when running libnids on fast network, this may be worth reading. 11 | Keep in mind that even if a single packet, belonging to TCP connection 12 | X, is not delivered to libnids, libnids will likely loose all the following 13 | data in X. Therefore you must avoid packet loss, at all cost. If you use the 14 | default syslog routine and see messages like "Max number of TCP streams 15 | reached" or "Too much data in TCP receive queue" then (assuming you are not 16 | under sophisticated NIDS evasion attack) most likely you are loosing packets. 17 | The packet loss usually happens when CPU is busy and cannot handle 18 | all incoming packets. It must be stressed that even if CPU seems to be fairly 19 | idle (say, load average 10%), during traffic burst it may be unable to queue 20 | all the packets, if the buffer space reserved for packets queuing is too 21 | small. And this is where the problem with libpcap is. It uses rather small 22 | buffers, and there is no API to enlarge them. 23 | So, we are left with unofficial methods. In case of Linux, libpcap 24 | 0.7.1, one can call 25 | int rcvbuf=100*1024; 26 | setsockopt(nids_getfd(),SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf)); 27 | This setsockopt doubles (approximately) the default kernel buffers size. 28 | Unfortunately, there seems to be a limit (about 100KB) for buffers 29 | allocated this way, which is way too small. 30 | Recent Linux 2.4 kernels offer PACKET_RX_RING setsockopt, which is 31 | supposed to allow to specify arbitrary buffer size. 640 K^H^H^H^H^H 10 MB 32 | buffer ought to be enough for everyone ;) This feature has not yet been 33 | integrated into libpcap (not in 0.7.1). There are floating some libpcap 34 | patches which merge this capability. See 35 | http://public.lanl.gov/cpw/ 36 | or 37 | http://pusa.uv.es/~ulisses/packet_mmap/ 38 | In case of BSD, you may play with BIOCSBLEN, but I have no experience 39 | with it. 40 | If you know how to enlarge libpcap buffers on other OS, let me know. 41 | A portable solution has been suggested by Yoav Weiss 42 | . Especially on SMP, it could be beneficial to split a 43 | libpcap application into two processes. The first one would receive packets 44 | via libpcap interface, and store them in arbitrarily large buffer; this process 45 | should run with high priority, perhaps even real time one. The second 46 | process, running with low priority, would retrieve packets from the first 47 | process and pass them to higher layers (for example to libnids). However, the 48 | efficient implementation is nontrivial. 49 | UPDATE: the current version of libnids adds experimental support for 50 | the solution mentioned above. See the documentation for nids_prm.multiproc for 51 | more information. 52 | -------------------------------------------------------------------------------- /doc/TESTS: -------------------------------------------------------------------------------- 1 | 2 | ==================== 3 | libnids-1.26 4 | ==================== 5 | 6 | In order to verify reliability of libnids, a number of tests were 7 | conducted. A small applications were composed, which displayed on stdout data 8 | received from libnids (contents of IP packets or data exchanged in TCP 9 | connections). Test packets were sent to a host running Linux 2.0.36. As we 10 | will see, libnids accurately emulated behaviour of the target host. 11 | As mentioned in README, libnids resisted all attack implemented by 12 | fragrouter 1.3. The following tests were conducted with custom tools. 13 | 14 | 1. IP defragmentation 15 | Libnids was put to the following tests: 16 | a) sending overlapping and/or duplicate IP fragments. All possible combinations 17 | of fragments positions in the final packet were tried. 18 | b) sending multiple fragments with the flag "no more fragments" 19 | c) sending a fragmented packet, pause for 30+epsilon second, then sending 20 | remaining fragments. If epsilon has been greater than zero, Linux 21 | discarded the first fragment (and ICMP message of type TIME_EXCEEDED was 22 | generated). In the other case, defragmentation succeeded. 23 | d) two fragments F1 and F2 of a packet P with header id X were build. A packet 24 | P' with header id X, but contents different to P was build as well. 25 | Finally, packets F1, P', F2 were sent (in the mentioned order). 26 | e) as a last test, resource managing was abused. Linux 2.0.x queues fragments 27 | until they consume 256 KB of kernel memory, than some of queued fragments 28 | are discarded so that less than 192 KB of kernel memory is consumed. A 29 | helper program, sendtcpfrag, was built. It accepts two command line 30 | parameters, x and y. Sendtcpfrag builds a certain TCP packet, which is 31 | than split into two fragments, named A and B. Next x random IP fragments 32 | carrying 8 data bytes are sent, than fragment A, than y random IP fragments 33 | carrying 8 data bytes are sent, finally fragment B. 34 | If all sent packets consume less than 256 KB of kernel memory, no packet 35 | is dropped, and the target host will assemble packets A and B. Otherwise, 36 | packet A can be dropped. 37 | After execution of 38 | # ./sendtcpfrag 650 61 39 | target host received a TCP segment. The situation looked similarly when 40 | second parameter was less than 61. After execution of 41 | # ./sendtcpfrag 650 62 42 | target host received no TCP segments. As we can see, 650+1+61 was the 43 | threshold number of packets, which could be queued. 44 | In the last test, libnids had to be fed a correct value of parameter 45 | sk_buff_size (see more on libnids parameters in file API). 46 | 47 | Libnids passed all above tests. 48 | 49 | 2) TCP segments assembly 50 | Libnids was put to the following tests: 51 | a) sending overlapping and/or duplicate segments. All possible 52 | combinations of segments sequence numbers precedence were tried. 53 | b) sending segments with sequence numbers ahead of expected values (test of 54 | segments queuing) 55 | c) sending segments [partially] out of connection window 56 | d) sending segments carrying invalid TCP flags 57 | e) sending segments carrying SYN flags after the sockets reached ESTABLISHED 58 | state 59 | f) sending segments carrying urgent data 60 | g) closing of previously established TCP connection with a RST segment, then 61 | setting up another connection with the same tuple (saddr, daddr, sport, 62 | dport), but different sequence numbers 63 | h) sending segments with incorrect IP or TCP checksum 64 | i) sequence numbers wrap 65 | j) initializing a TCP connection with a segment carrying not only SYN flag 66 | k) resource managing. Linux queues TCP segments which fit in the connection 67 | window until kernel memory used for this purpose reaches SK_RMEM_MAX 68 | (typically 64 KB). Queuing uses lots of auxiliary data structures; 69 | therefore Linux can discard TCP segments, which belong to the connection 70 | window. 71 | Another tool was written, tcpqueue. It sends a flow of TCP segments 72 | carrying 1 byte of data. Segments have got consecutive, decreasing 73 | sequence numbers (so they're sent in reversed order than any OS do). 74 | Tcpqueue accepts two command line parameters: S - the last segment sequence 75 | number, and N - number of segments to be sent. If S is the expected 76 | sequence number of the connection and N is not big, all sent segments are 77 | queued, then after the arrival of the last segments data from all segments 78 | is passed to the application. If N is large, some of segments will be 79 | discarded. 80 | After execution of 81 | # ./tcpqueue 2 283 82 | (connection was set up with initial sequence number equal 1) application 83 | (namely netcat) run on the target host received all 283 bytes of data. 84 | After execution of 85 | # ./tcpqueue 285 284 86 | application received data from the last segment only. As we can see, 87 | Linux queues up to 283 one-byte TCP segments, though it announces window 88 | close to 32768. 89 | Analogically to the last IP fragmentation test, libnids had to be fed a 90 | correct value of parameter sk_buff_size. 91 | Libnids passed all above tests, with one exception of test f. However, it is 92 | due to a bug in Linux kernel, not libnids. In certain conditions, a byte of 93 | urgent data can be included in normal data flow. Kernel developers were 94 | notified, perhaps the bug will be squashed soon. 95 | 96 | 3. Additionally, libnids handles properly ICMP Destination Unreachable 97 | packets. Source routed IP packets are discarded. 98 | -------------------------------------------------------------------------------- /doc/bugtraq_post: -------------------------------------------------------------------------------- 1 | [cut] 2 | Libnids was implemented with regard to results of Linux kernel 2.0.x 3 | networking code analysis, included in my Master Thesis. This analysis was 4 | conducted from NIDS developer's point of view. Of course, 2.0.x family is 5 | obsolete now (still widely used though), but some points mentioned below are 6 | worth checking against any OS. 7 | 1) In their famous paper, Ptacek and Newsham mention that a packet can 8 | be discarded by a kernel if OS resources are low (e.g. too many packets arrives 9 | in a unit of time), which makes an insertion attack possible. However, it was 10 | not stated that an attacker can create such a condition deterministicly and 11 | repeatably. 12 | One obvious target is IP defragmentation. If many IP fragments arrive, 13 | kernel has to drop some of them to avoid a DOS attack (IP fragment bomb). NIDS 14 | has to employ the same defragmentation algorithm (including the order in 15 | which fragments are discarded) which is used by protected systems; otherwise, 16 | all discrepancies can be exploited by an attacker to construct an insertion or 17 | evasion attack. The above statement is valid considering any OS type; what 18 | makes things even worse in case of Linux is the fact that the behaviour of the 19 | defragmentation algorithm used by Linux depends on kernel compilation options 20 | (size of struct sk_buff is critical). 21 | TCP segments queuing algorithm used by Linux 2.0.x is vulnerable to a 22 | similar attack. Linux queues TCP segments that fit in a connection window until 23 | the kernel memory consumed for this purpose (counted by rmem_alloc variable) 24 | reaches 64 KB; when it happens, all unacknowledged segments are discarded. TCP 25 | segments queuing requires a lot of auxiliary structures; as a result, TCP 26 | segments can be dropped, even if their sequence numbers are acceptable. For 27 | example, a tested 2.0.36 kernel could queue only 284 TCP segments carrying one 28 | byte of data each (announced connection window was about 32K). Again, the 29 | succesful emulation of this algorithm by NIDS requires knowledge of size of 30 | struct sk_buff. 31 | To sum up, it is obvious that DOS attacks against NIDS are a threat, 32 | but that's not all. A mild DOS attack against a monitored host can trigger 33 | some OS-dependent resource management algorithm, which can be tough to 34 | emulate by NIDS. 35 | Another nasty feature of Linux TCP queuing was found. If an 36 | application doesn't receive data from kernel (doesn't perform read calls on a 37 | socket for some time) all acknowledged (that is, ready to be passed to an app), 38 | buffered segments still consume kernel memory (rmem_alloc counter is not zero). 39 | As a result, even less packets can be queued (because some of 64KB pool is 40 | still in use). It means that the number of queued packets (and consequently, 41 | received data) depends on an application behaviour (!). To verify such 42 | possibility, the same stream of packets was sent twice. First, the receiving 43 | application A performed immediate read call. In the second case, the receiving 44 | application B executed 45 | sleep(1); 46 | read(sockfd, buffer, num); 47 | sequence of system calls. App A received different data than app B. The 48 | delay imposed by sleep(1) call was unnecessarily large; a delay resulting 49 | from a context switch can be big enough. 50 | Libnids uses algorithms equivalent to or taken directly from Linux 51 | kernel sources. If libnids is given a correct value of struct sk_buff size (it 52 | is configurable in run-time, along with many other parameters) the above 53 | mentioned attacks will not bypass libnids (with an exception of the last 54 | one; libnids has no way to determine frequency of read calls performed by an 55 | app). It also mean that at least IP defragmentation performed by libnids is 56 | as reliable as one offered by Linux 2.0.36 kernel. 57 | 2) Linux firewall implementation includes one hard-coded rule. Quoting 58 | from ip_fw.c: 59 | /* 60 | * Don't allow a fragment of TCP 8 bytes in. Nobody 61 | * normal causes this. Its a cracker trying to break 62 | * in by doing a flag overwrite to pass the direction 63 | * checks. 64 | */ 65 | 66 | if (offset == 1 && ip->protocol == IPPROTO_TCP) 67 | return FW_BLOCK; 68 | So, if the kernel was compiled with CONFIG_FIREWALL (for instance, Redhat 69 | install kernels are), it can block some packets (short fragmented TCP 70 | segments), even if no firewall rules was defined by the administrator. If NIDS 71 | accepts such a packet for further processing, an insertion attack is possible. 72 | It's another example that NIDS has to know the compilation options of 73 | monitored kernels. 74 | [cut] 75 | The description of other tests on libnids is included in libnids 76 | distribution. 77 | Save yourself, 78 | Nergal 79 | -------------------------------------------------------------------------------- /install-sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # 3 | # install - install a program, script, or datafile 4 | # This comes from X11R5. 5 | # 6 | # Calling this script install-sh is preferred over install.sh, to prevent 7 | # `make' implicit rules from creating a file called install from it 8 | # when there is no Makefile. 9 | # 10 | # This script is compatible with the BSD install script, but was written 11 | # from scratch. 12 | # 13 | 14 | 15 | # set DOITPROG to echo to test this script 16 | 17 | # Don't use :- since 4.3BSD and earlier shells don't like it. 18 | doit="${DOITPROG-}" 19 | 20 | 21 | # put in absolute paths if you don't have them in your path; or use env. vars. 22 | 23 | mvprog="${MVPROG-mv}" 24 | cpprog="${CPPROG-cp}" 25 | chmodprog="${CHMODPROG-chmod}" 26 | chownprog="${CHOWNPROG-chown}" 27 | chgrpprog="${CHGRPPROG-chgrp}" 28 | stripprog="${STRIPPROG-strip}" 29 | rmprog="${RMPROG-rm}" 30 | mkdirprog="${MKDIRPROG-mkdir}" 31 | 32 | tranformbasename="" 33 | transform_arg="" 34 | instcmd="$mvprog" 35 | chmodcmd="$chmodprog 0755" 36 | chowncmd="" 37 | chgrpcmd="" 38 | stripcmd="" 39 | rmcmd="$rmprog -f" 40 | mvcmd="$mvprog" 41 | src="" 42 | dst="" 43 | dir_arg="" 44 | 45 | while [ x"$1" != x ]; do 46 | case $1 in 47 | -c) instcmd="$cpprog" 48 | shift 49 | continue;; 50 | 51 | -d) dir_arg=true 52 | shift 53 | continue;; 54 | 55 | -m) chmodcmd="$chmodprog $2" 56 | shift 57 | shift 58 | continue;; 59 | 60 | -o) chowncmd="$chownprog $2" 61 | shift 62 | shift 63 | continue;; 64 | 65 | -g) chgrpcmd="$chgrpprog $2" 66 | shift 67 | shift 68 | continue;; 69 | 70 | -s) stripcmd="$stripprog" 71 | shift 72 | continue;; 73 | 74 | -t=*) transformarg=`echo $1 | sed 's/-t=//'` 75 | shift 76 | continue;; 77 | 78 | -b=*) transformbasename=`echo $1 | sed 's/-b=//'` 79 | shift 80 | continue;; 81 | 82 | *) if [ x"$src" = x ] 83 | then 84 | src=$1 85 | else 86 | # this colon is to work around a 386BSD /bin/sh bug 87 | : 88 | dst=$1 89 | fi 90 | shift 91 | continue;; 92 | esac 93 | done 94 | 95 | if [ x"$src" = x ] 96 | then 97 | echo "install: no input file specified" 98 | exit 1 99 | else 100 | true 101 | fi 102 | 103 | if [ x"$dir_arg" != x ]; then 104 | dst=$src 105 | src="" 106 | 107 | if [ -d $dst ]; then 108 | instcmd=: 109 | else 110 | instcmd=mkdir 111 | fi 112 | else 113 | 114 | # Waiting for this to be detected by the "$instcmd $src $dsttmp" command 115 | # might cause directories to be created, which would be especially bad 116 | # if $src (and thus $dsttmp) contains '*'. 117 | 118 | if [ -f $src -o -d $src ] 119 | then 120 | true 121 | else 122 | echo "install: $src does not exist" 123 | exit 1 124 | fi 125 | 126 | if [ x"$dst" = x ] 127 | then 128 | echo "install: no destination specified" 129 | exit 1 130 | else 131 | true 132 | fi 133 | 134 | # If destination is a directory, append the input filename; if your system 135 | # does not like double slashes in filenames, you may need to add some logic 136 | 137 | if [ -d $dst ] 138 | then 139 | dst="$dst"/`basename $src` 140 | else 141 | true 142 | fi 143 | fi 144 | 145 | ## this sed command emulates the dirname command 146 | dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` 147 | 148 | # Make sure that the destination directory exists. 149 | # this part is taken from Noah Friedman's mkinstalldirs script 150 | 151 | # Skip lots of stat calls in the usual case. 152 | if [ ! -d "$dstdir" ]; then 153 | defaultIFS=' 154 | ' 155 | IFS="${IFS-${defaultIFS}}" 156 | 157 | oIFS="${IFS}" 158 | # Some sh's can't handle IFS=/ for some reason. 159 | IFS='%' 160 | set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` 161 | IFS="${oIFS}" 162 | 163 | pathcomp='' 164 | 165 | while [ $# -ne 0 ] ; do 166 | pathcomp="${pathcomp}${1}" 167 | shift 168 | 169 | if [ ! -d "${pathcomp}" ] ; 170 | then 171 | $mkdirprog "${pathcomp}" 172 | else 173 | true 174 | fi 175 | 176 | pathcomp="${pathcomp}/" 177 | done 178 | fi 179 | 180 | if [ x"$dir_arg" != x ] 181 | then 182 | $doit $instcmd $dst && 183 | 184 | if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi && 185 | if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi && 186 | if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi && 187 | if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi 188 | else 189 | 190 | # If we're going to rename the final executable, determine the name now. 191 | 192 | if [ x"$transformarg" = x ] 193 | then 194 | dstfile=`basename $dst` 195 | else 196 | dstfile=`basename $dst $transformbasename | 197 | sed $transformarg`$transformbasename 198 | fi 199 | 200 | # don't allow the sed command to completely eliminate the filename 201 | 202 | if [ x"$dstfile" = x ] 203 | then 204 | dstfile=`basename $dst` 205 | else 206 | true 207 | fi 208 | 209 | # Make a temp file name in the proper directory. 210 | 211 | dsttmp=$dstdir/#inst.$$# 212 | 213 | # Move or copy the file name to the temp name 214 | 215 | $doit $instcmd $src $dsttmp && 216 | 217 | trap "rm -f ${dsttmp}" 0 && 218 | 219 | # and set any options; do chmod last to preserve setuid bits 220 | 221 | # If any of these fail, we abort the whole thing. If we want to 222 | # ignore errors from any of these, just make sure not to ignore 223 | # errors from the above "$doit $instcmd $src $dsttmp" command. 224 | 225 | if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi && 226 | if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi && 227 | if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi && 228 | if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi && 229 | 230 | # Now rename the file to the real destination. 231 | 232 | $doit $rmcmd -f $dstdir/$dstfile && 233 | $doit $mvcmd $dsttmp $dstdir/$dstfile 234 | 235 | fi && 236 | 237 | 238 | exit 0 239 | -------------------------------------------------------------------------------- /mkinstalldirs: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # mkinstalldirs --- make directory hierarchy 3 | # Author: Noah Friedman 4 | # Created: 1993-05-16 5 | # Last modified: 1994-03-25 6 | # Public domain 7 | 8 | errstatus=0 9 | 10 | for file in ${1+"$@"} ; do 11 | set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'` 12 | shift 13 | 14 | pathcomp= 15 | for d in ${1+"$@"} ; do 16 | pathcomp="$pathcomp$d" 17 | case "$pathcomp" in 18 | -* ) pathcomp=./$pathcomp ;; 19 | esac 20 | 21 | if test ! -d "$pathcomp"; then 22 | echo "mkdir $pathcomp" 1>&2 23 | mkdir "$pathcomp" || errstatus=$? 24 | fi 25 | 26 | pathcomp="$pathcomp/" 27 | done 28 | done 29 | 30 | exit $errstatus 31 | 32 | # mkinstalldirs ends here 33 | -------------------------------------------------------------------------------- /samples/Makefile.in: -------------------------------------------------------------------------------- 1 | # 2 | # Makefile for libnids samples. 3 | # 4 | # Dug Song 5 | 6 | srcdir = @srcdir@ 7 | VPATH = @srcdir@ 8 | 9 | CC = @CC@ 10 | CFLAGS = @CFLAGS@ 11 | LDFLAGS = @LDFLAGS@ 12 | 13 | PCAP_CFLAGS = @PCAP_CFLAGS@ 14 | PCAPLIB = @PCAPLIB@ 15 | 16 | LNET_CFLAGS = @LNET_CFLAGS@ 17 | LNETLIB = @LNETLIB@ 18 | 19 | LIBS_CFLAGS = -I../src $(PCAP_CFLAGS) $(LNET_CFLAGS) 20 | LIBS = -L../src -lnids $(PCAPLIB) $(LNETLIB) @LIBS@ 21 | 22 | .c.o: 23 | $(CC) -c $(CFLAGS) -I. $(LIBS_CFLAGS) $< 24 | 25 | all: overflows printall sniff 26 | static shared: all 27 | 28 | overflows: overflows.o 29 | $(CC) -o $@ overflows.o $(LDFLAGS) $(LIBS) 30 | 31 | printall: printall.o 32 | $(CC) -o $@ printall.o $(LDFLAGS) $(LIBS) 33 | 34 | sniff: sniff.o 35 | $(CC) -o $@ sniff.o $(LDFLAGS) $(LIBS) 36 | 37 | static shared install installshared: 38 | @true 39 | 40 | clean: 41 | rm -f *.o *~ overflows printall sniff 42 | 43 | # EOF 44 | -------------------------------------------------------------------------------- /samples/chksum_ctl.c: -------------------------------------------------------------------------------- 1 | #include "nids.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | /* Example 1: simple disabling of checksums on a predefined network */ 10 | void simple_chksum_ctl_example() 11 | { 12 | static struct nids_chksum_ctl ctl; 13 | 14 | ctl.netaddr = inet_addr("172.16.99.0"); 15 | ctl.mask = inet_addr("255.255.255.0"); 16 | ctl.action = NIDS_DONT_CHKSUM; 17 | nids_register_chksum_ctl(&ctl, 1); 18 | } 19 | 20 | /* Example 2: disabling checksums of packets with src ip of any local interface */ 21 | static int get_all_ifaces(struct ifreq **, int *); 22 | static unsigned int get_addr_from_ifreq(struct ifreq *); 23 | 24 | int all_local_ipaddrs_chksum_disable() 25 | { 26 | struct ifreq *ifaces; 27 | int ifaces_count; 28 | int i, ind = 0; 29 | struct nids_chksum_ctl *ctlp; 30 | unsigned int tmp; 31 | 32 | if (!get_all_ifaces(&ifaces, &ifaces_count)) 33 | return -1; 34 | ctlp = 35 | (struct nids_chksum_ctl *) malloc(ifaces_count * 36 | sizeof(struct 37 | nids_chksum_ctl)); 38 | if (!ctlp) 39 | return -1; 40 | for (i = 0; i < ifaces_count; i++) { 41 | tmp = get_addr_from_ifreq(ifaces + i); 42 | if (tmp) { 43 | ctlp[ind].netaddr = tmp; 44 | ctlp[ind].mask = inet_addr("255.255.255.255"); 45 | ctlp[ind].action = NIDS_DONT_CHKSUM; 46 | ind++; 47 | } 48 | } 49 | free(ifaces); 50 | nids_register_chksum_ctl(ctlp, ind); 51 | } 52 | 53 | /* helper functions for Example 2 */ 54 | unsigned int get_addr_from_ifreq(struct ifreq *iface) 55 | { 56 | if (iface->ifr_addr.sa_family == AF_INET) 57 | return ((struct sockaddr_in *) &(iface->ifr_addr))-> 58 | sin_addr.s_addr; 59 | return 0; 60 | } 61 | 62 | static int get_all_ifaces(struct ifreq **ifaces, int *count) 63 | { 64 | int ifaces_size = 8 * sizeof(struct ifreq); 65 | struct ifconf param; 66 | int sock; 67 | unsigned int i; 68 | 69 | *ifaces = malloc(ifaces_size); 70 | sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP); 71 | if (sock <= 0) 72 | return 0; 73 | for (;;) { 74 | param.ifc_len = ifaces_size; 75 | param.ifc_req = *ifaces; 76 | if (ioctl(sock, SIOCGIFCONF, ¶m)) 77 | goto err; 78 | if (param.ifc_len < ifaces_size) 79 | break; 80 | free(*ifaces); 81 | ifaces_size *= 2; 82 | ifaces = malloc(ifaces_size); 83 | } 84 | *count = param.ifc_len / sizeof(struct ifreq); 85 | close(sock); 86 | return 1; 87 | err: 88 | close(sock); 89 | return 0; 90 | } 91 | 92 | #ifdef TESTING 93 | main() 94 | { 95 | all_local_ipaddrs_chksum_disable(); 96 | } 97 | #endif 98 | -------------------------------------------------------------------------------- /samples/nids_next.c: -------------------------------------------------------------------------------- 1 | /* 2 | This is an example how one can use nids_getfd() and nids_next() functions. 3 | You can replace printall.c's function main with this file. 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | int 11 | main () 12 | { 13 | // here we can alter libnids params, for instance: 14 | // nids_params.n_hosts=256; 15 | int fd; 16 | int time = 0; 17 | fd_set rset; 18 | struct timeval tv; 19 | 20 | if (!nids_init ()) 21 | { 22 | fprintf(stderr,"%s\n",nids_errbuf); 23 | exit(1); 24 | } 25 | nids_register_tcp (tcp_callback); 26 | fd = nids_getfd (); 27 | for (;;) 28 | { 29 | tv.tv_sec = 1; 30 | tv.tv_usec = 0; 31 | FD_ZERO (&rset); 32 | FD_SET (fd, &rset); 33 | // add any other fd we need to take care of 34 | if (select (fd + 1, &rset, 0, 0, &tv)) 35 | { 36 | if (FD_ISSET(fd,&rset) // need to test it if there are other 37 | // fd in rset 38 | if (!nids_next ()) break; 39 | } 40 | else 41 | fprintf (stderr, "%i ", time++); 42 | } 43 | return 0; 44 | } 45 | -------------------------------------------------------------------------------- /samples/overflows.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 1999 Rafal Wojtczuk . All rights reserved. 3 | See the file COPYING for license details. 4 | */ 5 | 6 | /* 7 | This code attempts to detect attack against imapd (AUTHENTICATE hole) and 8 | wuftpd (creation of deep directory). This code is to ilustrate use of libnids; 9 | in order to improve readability, some simplifications were made, which enables 10 | an attacker to bypass this code (note, the below routines should be improved, 11 | not libnids) 12 | */ 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include "nids.h" 24 | 25 | #define int_ntoa(x) inet_ntoa(*((struct in_addr *)&x)) 26 | 27 | char * 28 | adres (struct tuple4 addr) 29 | { 30 | static char buf[256]; 31 | strcpy (buf, int_ntoa (addr.saddr)); 32 | sprintf (buf + strlen (buf), ",%i,", addr.source); 33 | strcat (buf, int_ntoa (addr.daddr)); 34 | sprintf (buf + strlen (buf), ",%i", addr.dest); 35 | return buf; 36 | } 37 | 38 | 39 | /* 40 | if we find a pattern AUTHENTICATE {an_int} in data stream sent to an imap 41 | server, where an_int >1024, it means an buffer overflow attempt. We kill the 42 | connection. 43 | */ 44 | 45 | #define PATTERN "AUTHENTICATE {" 46 | #define PATLEN strlen(PATTERN) 47 | void 48 | detect_imap (struct tcp_stream *a_tcp) 49 | { 50 | char numbuf[30]; 51 | int i, j, datalen, numberlen; 52 | struct half_stream *hlf; 53 | if (a_tcp->nids_state == NIDS_JUST_EST) 54 | { 55 | if (a_tcp->addr.dest == 143) 56 | { 57 | a_tcp->server.collect++; 58 | return; 59 | } 60 | else 61 | return; 62 | } 63 | if (a_tcp->nids_state != NIDS_DATA) 64 | return; 65 | hlf = &a_tcp->server; 66 | datalen = hlf->count - hlf->offset; 67 | if (datalen < PATLEN) 68 | { 69 | // we have too small amount of data to work on. Keep all data in buffer. 70 | nids_discard (a_tcp, 0); 71 | return; 72 | } 73 | for (i = 0; i <= datalen - PATLEN; i++) 74 | if (!memcmp (PATTERN, hlf->data + i, PATLEN)) //searching for a pattern 75 | break; 76 | if (i > datalen - PATLEN) 77 | { 78 | // retain PATLEN bytes in buffer 79 | nids_discard (a_tcp, datalen - PATLEN); 80 | return; 81 | } 82 | for (j = i + PATLEN; j < datalen; j++) // searching for a closing '}' 83 | if (*(hlf->data + j) == '}') 84 | break; 85 | if (j > datalen) 86 | { 87 | if (datalen > 20) 88 | { 89 | //number too long, perhaps we should log it, too 90 | } 91 | return; 92 | } 93 | numberlen = j - i - PATLEN; 94 | memcpy (numbuf, hlf->data + i + PATLEN, numberlen); //numbuf contains 95 | // AUTH argument 96 | numbuf[numberlen] = 0; 97 | if (atoi (numbuf) > 1024) 98 | { 99 | // notify admin 100 | syslog(nids_params.syslog_level, 101 | "Imapd exploit attempt, connection %s\n",adres(a_tcp->addr)); 102 | // kill the connection 103 | nids_killtcp (a_tcp); 104 | } 105 | nids_discard (a_tcp, datalen - PATLEN); 106 | return; 107 | } 108 | 109 | // auxiliary structure, needed to keep current dir of ftpd daemon 110 | struct supp 111 | { 112 | char *currdir; 113 | int last_newline; 114 | }; 115 | 116 | // the below function adds "elem" string to "path" string, taking care of 117 | // ".." and multiple '/'. If the resulting path is longer than 768, 118 | // return value is 1, otherwise 0 119 | int 120 | add_to_path (char *path, char *elem, int len) 121 | { 122 | int plen; 123 | char * ptr; 124 | if (len > 768) 125 | return 1; 126 | if (len == 2 && elem[0] == '.' && elem[1] == '.') 127 | { 128 | ptr = rindex (path, '/'); 129 | if (ptr != path) 130 | *ptr = 0; 131 | } 132 | else if (len > 0) 133 | { 134 | plen = strlen (path); 135 | if (plen + len + 1 > 768) 136 | return 1; 137 | if (plen==1) 138 | { 139 | strncpy(path+1,elem,len); 140 | path[1+len]=0; 141 | } 142 | else 143 | { 144 | path[plen] = '/'; 145 | strncpy (path + plen + 1, elem, len); 146 | path[plen + 1 + len] = 0; 147 | } 148 | } 149 | return 0; 150 | } 151 | 152 | void 153 | do_detect_ftp (struct tcp_stream *a_tcp, struct supp **param_ptr) 154 | { 155 | struct supp *p = *param_ptr; 156 | int index = p->last_newline + 1; 157 | char *buf = a_tcp->server.data; 158 | int offset = a_tcp->server.offset; 159 | int n_bytes = a_tcp->server.count - offset; 160 | int path_index, pi2, index2, remcaret; 161 | for (;;) 162 | { 163 | index2 = index; 164 | while (index2 - offset < n_bytes && buf[index2 - offset] != '\n') 165 | index2++; 166 | if (index2 - offset >= n_bytes) 167 | break; 168 | if (!strncasecmp (buf + index - offset, "cwd ", 4)) 169 | { 170 | path_index = index + 4; 171 | if (buf[path_index - offset] == '/') 172 | { 173 | strcpy (p->currdir, "/"); 174 | path_index++; 175 | } 176 | for (;;) 177 | { 178 | pi2 = path_index; 179 | while (buf[pi2 - offset] != '\n' && buf[pi2 - offset] != '/') 180 | pi2++; 181 | if (buf[pi2-offset]=='\n' && buf[pi2-offset-1]=='\r') 182 | remcaret=1; 183 | else remcaret=0; 184 | if (add_to_path (p->currdir, buf + path_index-offset, pi2 - path_index-remcaret)) 185 | { 186 | // notify admin 187 | syslog(nids_params.syslog_level, 188 | "Ftpd exploit attempt, connection %s\n",adres(a_tcp->addr)); 189 | nids_killtcp (a_tcp); 190 | return; 191 | } 192 | if (buf[pi2 - offset] == '\n') 193 | break; 194 | path_index = pi2 + 1; 195 | } 196 | } 197 | index = index2 + 1; 198 | } 199 | p->last_newline = index - 1; 200 | nids_discard (a_tcp, index - offset); 201 | } 202 | 203 | void 204 | detect_ftpd (struct tcp_stream *a_tcp, struct supp **param) 205 | { 206 | if (a_tcp->nids_state == NIDS_JUST_EST) 207 | { 208 | if (a_tcp->addr.dest == 21) 209 | { 210 | struct supp *one_for_conn; 211 | a_tcp->server.collect++; 212 | one_for_conn = (struct supp *) malloc (sizeof (struct supp)); 213 | one_for_conn->currdir = malloc (1024); 214 | strcpy (one_for_conn->currdir, "/"); 215 | one_for_conn->last_newline = 0; 216 | *param=one_for_conn; 217 | } 218 | return; 219 | } 220 | if (a_tcp->nids_state != NIDS_DATA) 221 | { 222 | free ((*param)->currdir); 223 | free (*param); 224 | return; 225 | } 226 | do_detect_ftp (a_tcp, param); 227 | } 228 | 229 | int 230 | main () 231 | { 232 | if (!nids_init ()) 233 | { 234 | fprintf(stderr,"%s\n",nids_errbuf); 235 | exit(1); 236 | } 237 | nids_register_tcp (detect_imap); 238 | nids_register_tcp (detect_ftpd); 239 | nids_run (); 240 | return 0; 241 | } 242 | -------------------------------------------------------------------------------- /samples/printall.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 1999 Rafal Wojtczuk . All rights reserved. 3 | See the file COPYING for license details. 4 | */ 5 | 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "nids.h" 15 | 16 | #define int_ntoa(x) inet_ntoa(*((struct in_addr *)&x)) 17 | 18 | // struct tuple4 contains addresses and port numbers of the TCP connections 19 | // the following auxiliary function produces a string looking like 20 | // 10.0.0.1,1024,10.0.0.2,23 21 | char * 22 | adres (struct tuple4 addr) 23 | { 24 | static char buf[256]; 25 | strcpy (buf, int_ntoa (addr.saddr)); 26 | sprintf (buf + strlen (buf), ",%i,", addr.source); 27 | strcat (buf, int_ntoa (addr.daddr)); 28 | sprintf (buf + strlen (buf), ",%i", addr.dest); 29 | return buf; 30 | } 31 | 32 | void 33 | tcp_callback (struct tcp_stream *a_tcp, void ** this_time_not_needed) 34 | { 35 | char buf[1024]; 36 | strcpy (buf, adres (a_tcp->addr)); // we put conn params into buf 37 | if (a_tcp->nids_state == NIDS_JUST_EST) 38 | { 39 | // connection described by a_tcp is established 40 | // here we decide, if we wish to follow this stream 41 | // sample condition: if (a_tcp->addr.dest!=23) return; 42 | // in this simple app we follow each stream, so.. 43 | a_tcp->client.collect++; // we want data received by a client 44 | a_tcp->server.collect++; // and by a server, too 45 | a_tcp->server.collect_urg++; // we want urgent data received by a 46 | // server 47 | #ifdef WE_WANT_URGENT_DATA_RECEIVED_BY_A_CLIENT 48 | a_tcp->client.collect_urg++; // if we don't increase this value, 49 | // we won't be notified of urgent data 50 | // arrival 51 | #endif 52 | fprintf (stderr, "%s established\n", buf); 53 | return; 54 | } 55 | if (a_tcp->nids_state == NIDS_CLOSE) 56 | { 57 | // connection has been closed normally 58 | fprintf (stderr, "%s closing\n", buf); 59 | return; 60 | } 61 | if (a_tcp->nids_state == NIDS_RESET) 62 | { 63 | // connection has been closed by RST 64 | fprintf (stderr, "%s reset\n", buf); 65 | return; 66 | } 67 | 68 | if (a_tcp->nids_state == NIDS_DATA) 69 | { 70 | // new data has arrived; gotta determine in what direction 71 | // and if it's urgent or not 72 | 73 | struct half_stream *hlf; 74 | 75 | if (a_tcp->server.count_new_urg) 76 | { 77 | // new byte of urgent data has arrived 78 | strcat(buf,"(urgent->)"); 79 | buf[strlen(buf)+1]=0; 80 | buf[strlen(buf)]=a_tcp->server.urgdata; 81 | write(1,buf,strlen(buf)); 82 | return; 83 | } 84 | // We don't have to check if urgent data to client has arrived, 85 | // because we haven't increased a_tcp->client.collect_urg variable. 86 | // So, we have some normal data to take care of. 87 | if (a_tcp->client.count_new) 88 | { 89 | // new data for client 90 | hlf = &a_tcp->client; // from now on, we will deal with hlf var, 91 | // which will point to client side of conn 92 | strcat (buf, "(<-)"); // symbolic direction of data 93 | } 94 | else 95 | { 96 | hlf = &a_tcp->server; // analogical 97 | strcat (buf, "(->)"); 98 | } 99 | fprintf(stderr,"%s",buf); // we print the connection parameters 100 | // (saddr, daddr, sport, dport) accompanied 101 | // by data flow direction (-> or <-) 102 | 103 | write(2,hlf->data,hlf->count_new); // we print the newly arrived data 104 | 105 | } 106 | return ; 107 | } 108 | 109 | int 110 | main () 111 | { 112 | // here we can alter libnids params, for instance: 113 | // nids_params.n_hosts=256; 114 | if (!nids_init ()) 115 | { 116 | fprintf(stderr,"%s\n",nids_errbuf); 117 | exit(1); 118 | } 119 | nids_register_tcp (tcp_callback); 120 | nids_run (); 121 | return 0; 122 | } 123 | 124 | -------------------------------------------------------------------------------- /samples/sniff.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 1999 Rafal Wojtczuk . All rights reserved. 3 | See the file COPYING for license details. 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "nids.h" 14 | 15 | #define LOG_MAX 100 16 | #define SZLACZEK "\n--------------------------------------------------\n" 17 | 18 | #define int_ntoa(x) inet_ntoa(*((struct in_addr *)&x)) 19 | 20 | char * 21 | adres (struct tuple4 addr) 22 | { 23 | static char buf[256]; 24 | strcpy (buf, int_ntoa (addr.saddr)); 25 | sprintf (buf + strlen (buf), ",%i,", addr.source); 26 | strcat (buf, int_ntoa (addr.daddr)); 27 | sprintf (buf + strlen (buf), ",%i : ", addr.dest); 28 | return buf; 29 | } 30 | 31 | int logfd; 32 | void 33 | do_log (char *adres_txt, char *data, int ile) 34 | { 35 | write (logfd, adres_txt, strlen (adres_txt)); 36 | write (logfd, data, ile); 37 | write (logfd, SZLACZEK, strlen (SZLACZEK)); 38 | } 39 | 40 | void 41 | sniff_callback (struct tcp_stream *a_tcp, void **this_time_not_needed) 42 | { 43 | int dest; 44 | if (a_tcp->nids_state == NIDS_JUST_EST) 45 | { 46 | dest = a_tcp->addr.dest; 47 | if (dest == 21 || dest == 23 || dest == 110 || dest == 143 || dest == 513) 48 | a_tcp->server.collect++; 49 | return; 50 | } 51 | if (a_tcp->nids_state != NIDS_DATA) 52 | { 53 | // seems the stream is closing, log as much as possible 54 | do_log (adres (a_tcp->addr), a_tcp->server.data, 55 | a_tcp->server.count - a_tcp->server.offset); 56 | return; 57 | } 58 | if (a_tcp->server.count - a_tcp->server.offset < LOG_MAX) 59 | { 60 | // we haven't got enough data yet; keep all of it 61 | nids_discard (a_tcp, 0); 62 | return; 63 | } 64 | 65 | // enough data 66 | do_log (adres (a_tcp->addr), a_tcp->server.data, LOG_MAX); 67 | 68 | // Now procedure sniff_callback doesn't want to see this stream anymore. 69 | // So, we decrease all the "collect" fields we have previously increased. 70 | // If there were other callbacks following a_tcp stream, they would still 71 | // receive data 72 | a_tcp->server.collect--; 73 | } 74 | 75 | 76 | int 77 | main () 78 | { 79 | logfd = open ("./logfile", O_WRONLY | O_CREAT | O_TRUNC, 0600); 80 | if (logfd < 0) 81 | { 82 | perror ("opening ./logfile:"); 83 | exit (1); 84 | } 85 | if (!nids_init ()) 86 | { 87 | fprintf (stderr, "%s\n", nids_errbuf); 88 | exit (1); 89 | } 90 | nids_register_tcp (sniff_callback); 91 | nids_run (); 92 | return 0; 93 | } 94 | -------------------------------------------------------------------------------- /src/Makefile.in: -------------------------------------------------------------------------------- 1 | # 2 | # Makefile for libnids. 3 | # 4 | # Dug Song 5 | 6 | srcdir = @srcdir@ 7 | VPATH = @srcdir@ 8 | 9 | install_prefix = 10 | prefix = @prefix@ 11 | exec_prefix = @exec_prefix@ 12 | includedir = @includedir@ 13 | libdir = @libdir@ 14 | mandir = @mandir@ 15 | LIBSTATIC = libnids.a 16 | LIBSHARED = libnids.so.1.26 17 | 18 | CC = @CC@ 19 | CFLAGS = @CFLAGS@ -DLIBNET_VER=@LIBNET_VER@ -DHAVE_ICMPHDR=@ICMPHEADER@ -DHAVE_TCP_STATES=@TCPSTATES@ -DHAVE_BSD_UDPHDR=@HAVE_BSD_UDPHDR@ 20 | LDFLAGS = @LDFLAGS@ 21 | 22 | PCAP_CFLAGS = @PCAP_CFLAGS@ 23 | PCAPLIB = @PCAPLIB@ 24 | 25 | LNET_CFLAGS = @LNET_CFLAGS@ 26 | LNETLIB = @LNETLIB@ 27 | 28 | LIBS_CFLAGS = $(PCAP_CFLAGS) $(LNET_CFLAGS) @GLIB_CFLAGS@ @GTHREAD_CFLAGS@ 29 | LIBS = @LIBS@ @GLIB_LIBS@ @GTHREAD_LIBS@ 30 | RANLIB = @RANLIB@ 31 | INSTALL = @INSTALL@ 32 | 33 | OBJS = checksum.o ip_fragment.o ip_options.o killtcp.o \ 34 | libnids.o scan.o tcp.o util.o allpromisc.o hash.o 35 | OBJS_SHARED = $(OBJS:.o=_pic.o) 36 | .c.o: 37 | $(CC) -c $(CFLAGS) $(CPPFLAGS) -I. $(LIBS_CFLAGS) $< 38 | static: $(LIBSTATIC) 39 | shared: $(LIBSHARED) 40 | # How to write the following rules compactly and portably ? 41 | # gmake accepts "%_pic.o: %.c", bsd make does not. 42 | checksum_pic.o: checksum.c 43 | $(CC) -fPIC $(CFLAGS) $(CPPFLAGS) -I. $(LIBS_CFLAGS) -c checksum.c -o $@ 44 | ip_fragment_pic.o: ip_fragment.c 45 | $(CC) -fPIC $(CFLAGS) $(CPPFLAGS) -I. $(LIBS_CFLAGS) -c ip_fragment.c -o $@ 46 | ip_options_pic.o: ip_options.c 47 | $(CC) -fPIC $(CFLAGS) $(CPPFLAGS) -I. $(LIBS_CFLAGS) -c ip_options.c -o $@ 48 | killtcp_pic.o: killtcp.c 49 | $(CC) -fPIC $(CFLAGS) $(CPPFLAGS) -I. $(LIBS_CFLAGS) -c killtcp.c -o $@ 50 | libnids_pic.o: libnids.c 51 | $(CC) -fPIC $(CFLAGS) $(CPPFLAGS) -I. $(LIBS_CFLAGS) -c libnids.c -o $@ 52 | scan_pic.o: scan.c 53 | $(CC) -fPIC $(CFLAGS) $(CPPFLAGS) -I. $(LIBS_CFLAGS) -c scan.c -o $@ 54 | tcp_pic.o: tcp.c 55 | $(CC) -fPIC $(CFLAGS) $(CPPFLAGS) -I. $(LIBS_CFLAGS) -c tcp.c -o $@ 56 | util_pic.o: util.c 57 | $(CC) -fPIC $(CFLAGS) $(CPPFLAGS) -I. $(LIBS_CFLAGS) -c util.c -o $@ 58 | allpromisc_pic.o: allpromisc.c 59 | $(CC) -fPIC $(CFLAGS) $(CPPFLAGS) -I. $(LIBS_CFLAGS) -c allpromisc.c -o $@ 60 | hash_pic.o: hash.c 61 | $(CC) -fPIC $(CFLAGS) $(CPPFLAGS) -I. $(LIBS_CFLAGS) -c hash.c -o $@ 62 | 63 | 64 | $(LIBSTATIC): $(OBJS) 65 | $(AR) -cr $@ $(OBJS) 66 | $(RANLIB) $@ 67 | $(LIBSHARED): $(OBJS_SHARED) 68 | $(CC) -shared -Wl,-soname,$(LIBSHARED) $(LDFLAGS) -o $(LIBSHARED) $(OBJS_SHARED) $(LIBS) $(LNETLIB) $(PCAPLIB) 69 | 70 | _install install: $(LIBSTATIC) 71 | ../mkinstalldirs $(install_prefix)$(libdir) 72 | ../mkinstalldirs $(install_prefix)$(includedir) 73 | ../mkinstalldirs $(install_prefix)$(mandir)/man3 74 | $(INSTALL) -c -m 644 libnids.a $(install_prefix)$(libdir) 75 | $(INSTALL) -c -m 644 nids.h $(install_prefix)$(includedir) 76 | $(INSTALL) -c -m 644 libnids.3 $(install_prefix)$(mandir)/man3 77 | _installshared installshared: install $(LIBSHARED) 78 | $(INSTALL) -c -m 755 $(LIBSHARED) $(install_prefix)$(libdir) 79 | ln -s -f $(LIBSHARED) $(install_prefix)$(libdir)/libnids.so 80 | 81 | clean: 82 | rm -f *.o *~ $(LIBSTATIC) $(LIBSHARED) 83 | 84 | # EOF 85 | -------------------------------------------------------------------------------- /src/allpromisc.c: -------------------------------------------------------------------------------- 1 | #include "nids.h" 2 | #ifdef __linux__ 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | int set_all_promisc() 11 | { 12 | struct ifreq * ifaces; 13 | int ifaces_size=8 * sizeof(struct ifreq); 14 | struct ifconf param; 15 | int sock; 16 | unsigned int i; 17 | 18 | sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP); 19 | if (sock <= 0) 20 | return 0; 21 | do { 22 | ifaces_size*=2; 23 | ifaces=alloca(ifaces_size); 24 | param.ifc_len = ifaces_size; 25 | param.ifc_req = ifaces; 26 | if (ioctl(sock, SIOCGIFCONF, ¶m)) 27 | goto err; 28 | } while (param.ifc_len>=ifaces_size); 29 | for (i = 0; i < param.ifc_len / sizeof(struct ifreq); i++) { 30 | if (ioctl(sock, SIOCGIFFLAGS, ifaces + i)) 31 | goto err; 32 | ifaces[i].ifr_flags |= IFF_PROMISC; 33 | if (ioctl(sock, SIOCSIFFLAGS, ifaces + i)) 34 | goto err; 35 | } 36 | close(sock); 37 | return 1; 38 | err: 39 | close(sock); 40 | return 0; 41 | } 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /src/checksum.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "nids.h" 9 | 10 | static struct nids_chksum_ctl * nchk; 11 | static int nrnochksum=0; 12 | 13 | void nids_register_chksum_ctl(struct nids_chksum_ctl * ptr, int nr) 14 | { 15 | nchk=ptr; 16 | nrnochksum=nr; 17 | } 18 | 19 | static int dontchksum(unsigned int ip) 20 | { 21 | int i; 22 | for (i=0;i, adapted for linux by Arnt 121 | Gulbrandsen. 122 | */ 123 | u_short ip_fast_csum(u_char * iph, u_int ihl) 124 | { 125 | u_int sum; 126 | if (dontchksum(((struct ip*)iph)->ip_src.s_addr)) 127 | return 0; 128 | __asm__ __volatile__( 129 | " movl (%1), %0 \n" 130 | " subl $4, %2 \n" 131 | " jbe 2f \n" 132 | " addl 4(%1), %0 \n" 133 | " adcl 8(%1), %0 \n" 134 | " adcl 12(%1), %0 \n" 135 | "1: adcl 16(%1), %0 \n" 136 | " lea 4(%1), %1 \n" 137 | " decl %2 \n" 138 | " jne 1b \n" 139 | " adcl $0, %0 \n" 140 | " movl %0, %2 \n" 141 | " shrl $16, %0 \n" 142 | " addw %w2, %w0 \n" 143 | " adcl $0, %0 \n" 144 | " notl %0 \n" 145 | "2: \n" 146 | /* 147 | Since the input registers which are loaded with iph and ipl 148 | are modified, we must also specify them as outputs, or gcc 149 | will assume they contain their original values. 150 | */ 151 | : "=r" (sum), "=r" (iph), "=r" (ihl) 152 | : "1" (iph), "2" (ihl) 153 | : "cc"); 154 | 155 | return (sum); 156 | } 157 | 158 | /* Fold a partial checksum. */ 159 | static inline u_int 160 | csum_fold(u_int sum) 161 | { 162 | __asm__( 163 | " addl %1, %0 \n" 164 | " adcl $0xffff, %0 \n" 165 | : "=r" (sum) 166 | : "r" (sum << 16), "0" (sum & 0xffff0000) 167 | : "cc" ); 168 | return ((~sum) >> 16); 169 | } 170 | 171 | /* 172 | computes the checksum of the TCP/UDP pseudo-header 173 | returns a 16-bit checksum, already complemented 174 | */ 175 | static inline u_short 176 | csum_tcpudp_magic(u_int saddr, u_int daddr, u_short len, 177 | u_short proto, u_int sum) 178 | { 179 | __asm__( 180 | " addl %1, %0 \n" 181 | " adcl %2, %0 \n" 182 | " adcl %3, %0 \n" 183 | " adcl $0, %0 \n" 184 | : "=r" (sum) 185 | : "g" (daddr), "g"(saddr), "g"((ntohs(len) << 16) + proto * 256), "0"(sum) 186 | : "cc"); 187 | return (csum_fold(sum)); 188 | } 189 | 190 | /* 191 | this routine is used for miscellaneous IP-like checksums, mainly in 192 | icmp.c 193 | */ 194 | u_short 195 | ip_compute_csum(u_char * buff, int len) 196 | { 197 | return (csum_fold(csum_partial(buff, len, 0))); 198 | } 199 | 200 | u_short 201 | my_tcp_check(struct tcphdr *th, int len, u_int saddr, u_int daddr) 202 | { 203 | if (dontchksum(saddr)) 204 | return 0; 205 | return csum_tcpudp_magic(saddr, daddr, len, IPPROTO_TCP, 206 | csum_partial((u_char *)th, len, 0)); 207 | } 208 | u_short 209 | my_udp_check(void *u, int len, u_int saddr, u_int daddr) 210 | { 211 | if (dontchksum(saddr)) 212 | return 0; 213 | return csum_tcpudp_magic(saddr, daddr, len, IPPROTO_UDP, 214 | csum_partial((u_char *)u, len, 0)); 215 | } 216 | 217 | #else /* !i386 */ 218 | 219 | struct psuedo_hdr 220 | { 221 | u_int saddr; 222 | u_int daddr; 223 | u_char zero; 224 | u_char protocol; 225 | u_short len; 226 | }; 227 | 228 | u_short 229 | ip_check_ext(register u_short *addr, register int len, int addon) 230 | { 231 | register int nleft = len; 232 | register u_short *w = addr; 233 | register int sum = addon; 234 | u_short answer = 0; 235 | 236 | /* 237 | * Our algorithm is simple, using a 32 bit accumulator (sum), 238 | * we add sequential 16 bit words to it, and at the end, fold 239 | * back all the carry bits from the top 16 bits into the lower 240 | * 16 bits. 241 | */ 242 | while (nleft > 1) { 243 | sum += *w++; 244 | nleft -= 2; 245 | } 246 | /* mop up an odd byte, if necessary */ 247 | if (nleft == 1) { 248 | *(u_char *)(&answer) = *(u_char *)w; 249 | sum += answer; 250 | } 251 | /* add back carry outs from top 16 bits to low 16 bits */ 252 | sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ 253 | sum += (sum >> 16); /* add carry */ 254 | answer = ~sum; /* truncate to 16 bits */ 255 | return (answer); 256 | } 257 | 258 | u_short 259 | ip_fast_csum(u_short *addr, int len) 260 | { 261 | if (dontchksum(((struct ip*)addr)->ip_src.s_addr)) 262 | return 0; 263 | return ip_check_ext(addr, len << 2, 0); 264 | } 265 | 266 | u_short 267 | ip_compute_csum(u_short *addr, int len) 268 | { 269 | return ip_check_ext(addr, len, 0); 270 | } 271 | 272 | u_short 273 | my_tcp_check(struct tcphdr *th, int len, u_int saddr, u_int daddr) 274 | { 275 | unsigned int i; 276 | int sum = 0; 277 | struct psuedo_hdr hdr; 278 | 279 | if (dontchksum(saddr)) 280 | return 0; 281 | 282 | hdr.saddr = saddr; 283 | hdr.daddr = daddr; 284 | hdr.zero = 0; 285 | hdr.protocol = IPPROTO_TCP; 286 | hdr.len = htons(len); 287 | for (i = 0; i < sizeof(hdr); i += 2) 288 | sum += *(u_short *)((char *)(&hdr) + i); 289 | 290 | return (ip_check_ext((u_short *)th, len, sum)); 291 | } 292 | u_short 293 | my_udp_check(void *u, int len, u_int saddr, u_int daddr) 294 | { 295 | unsigned int i; 296 | int sum = 0; 297 | struct psuedo_hdr hdr; 298 | 299 | if (dontchksum(saddr)) 300 | return 0; 301 | 302 | hdr.saddr = saddr; 303 | hdr.daddr = daddr; 304 | hdr.zero = 0; 305 | hdr.protocol = IPPROTO_UDP; 306 | hdr.len = htons(len); 307 | for (i = 0; i < sizeof(hdr); i += 2) 308 | sum += *(u_short *)((char *)(&hdr) + i); 309 | 310 | return (ip_check_ext((u_short *)u, len, sum)); 311 | } 312 | 313 | #endif /* !i386 */ 314 | -------------------------------------------------------------------------------- /src/checksum.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _NIDS_CHECKSUM_H 3 | #define _NIDS_CHECKSUM_H 4 | 5 | u_short ip_fast_csum(u_char *, u_int); 6 | extern u_short ip_compute_csum(char *, int len); 7 | u_short my_tcp_check(struct tcphdr *, int, u_int, u_int); 8 | u_short my_udp_check(void *, int, u_int, u_int); 9 | 10 | #endif /* _NIDS_CHECKSUM_H */ 11 | -------------------------------------------------------------------------------- /src/config.h.in: -------------------------------------------------------------------------------- 1 | /* src/config.h.in. Generated from configure.in by autoheader. */ 2 | 3 | /* Define to 1 if you have the `gettimeofday' function. */ 4 | #undef HAVE_GETTIMEOFDAY 5 | 6 | /* Define to 1 if you have the header file. */ 7 | #undef HAVE_INTTYPES_H 8 | 9 | /* Define to 1 if you have the `gthread-2.0' library (-lgthread-2.0). */ 10 | #undef HAVE_LIBGTHREAD_2_0 11 | 12 | /* Define to 1 if you have the `nsl' library (-lnsl). */ 13 | #undef HAVE_LIBNSL 14 | 15 | /* Define to 1 if you have the `socket' library (-lsocket). */ 16 | #undef HAVE_LIBSOCKET 17 | 18 | /* Define to 1 if you have the header file. */ 19 | #undef HAVE_MEMORY_H 20 | 21 | /* Define to 1 if you have the header file. */ 22 | #undef HAVE_STDINT_H 23 | 24 | /* Define to 1 if you have the header file. */ 25 | #undef HAVE_STDLIB_H 26 | 27 | /* Define to 1 if you have the header file. */ 28 | #undef HAVE_STRINGS_H 29 | 30 | /* Define to 1 if you have the header file. */ 31 | #undef HAVE_STRING_H 32 | 33 | /* Define to 1 if you have the header file. */ 34 | #undef HAVE_SYSLOG_H 35 | 36 | /* Define to 1 if you have the header file. */ 37 | #undef HAVE_SYS_STAT_H 38 | 39 | /* Define to 1 if you have the header file. */ 40 | #undef HAVE_SYS_TIME_H 41 | 42 | /* Define to 1 if you have the header file. */ 43 | #undef HAVE_SYS_TYPES_H 44 | 45 | /* Define to 1 if you have the header file. */ 46 | #undef HAVE_UNISTD_H 47 | 48 | /* if unaligned access fails */ 49 | #undef LBL_ALIGN 50 | 51 | /* Define to the address where bug reports for this package should be sent. */ 52 | #undef PACKAGE_BUGREPORT 53 | 54 | /* Define to the full name of this package. */ 55 | #undef PACKAGE_NAME 56 | 57 | /* Define to the full name and version of this package. */ 58 | #undef PACKAGE_STRING 59 | 60 | /* Define to the one symbol short name of this package. */ 61 | #undef PACKAGE_TARNAME 62 | 63 | /* Define to the version of this package. */ 64 | #undef PACKAGE_VERSION 65 | 66 | /* Define to 1 if you have the ANSI C header files. */ 67 | #undef STDC_HEADERS 68 | 69 | /* Define to 1 if you can safely include both and . */ 70 | #undef TIME_WITH_SYS_TIME 71 | 72 | /* Define to 1 if your processor stores words with the most significant byte 73 | first (like Motorola and SPARC, unlike Intel and VAX). */ 74 | #undef WORDS_BIGENDIAN 75 | 76 | /* Define to empty if `const' does not conform to ANSI C. */ 77 | #undef const 78 | 79 | /* Define to `__inline__' or `__inline' if that's what the C compiler 80 | calls it, or to nothing if 'inline' is not supported under any name. */ 81 | #ifndef __cplusplus 82 | #undef inline 83 | #endif 84 | -------------------------------------------------------------------------------- /src/hash.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | static u_char xor[12]; 9 | static u_char perm[12]; 10 | static void 11 | getrnd () 12 | { 13 | struct timeval s; 14 | u_int *ptr; 15 | int fd = open ("/dev/urandom", O_RDONLY); 16 | if (fd > 0) 17 | { 18 | read (fd, xor, 12); 19 | read (fd, perm, 12); 20 | close (fd); 21 | return; 22 | } 23 | 24 | gettimeofday (&s, 0); 25 | srand (s.tv_usec); 26 | ptr = (u_int *) xor; 27 | *ptr = rand (); 28 | *(ptr + 1) = rand (); 29 | *(ptr + 2) = rand (); 30 | ptr = (u_int *) perm; 31 | *ptr = rand (); 32 | *(ptr + 1) = rand (); 33 | *(ptr + 2) = rand (); 34 | 35 | 36 | } 37 | void 38 | init_hash () 39 | { 40 | int i, n, j; 41 | int p[12]; 42 | getrnd (); 43 | for (i = 0; i < 12; i++) 44 | p[i] = i; 45 | for (i = 0; i < 12; i++) 46 | { 47 | n = perm[i] % (12 - i); 48 | perm[i] = p[n]; 49 | for (j = 0; j < 11 - n; j++) 50 | p[n + j] = p[n + j + 1]; 51 | } 52 | } 53 | 54 | u_int 55 | mkhash (u_int src, u_short sport, u_int dest, u_short dport) 56 | { 57 | u_int res = 0; 58 | int i; 59 | u_char data[12]; 60 | u_int *stupid_strict_aliasing_warnings=(u_int*)data; 61 | *stupid_strict_aliasing_warnings = src; 62 | *(u_int *) (data + 4) = dest; 63 | *(u_short *) (data + 8) = sport; 64 | *(u_short *) (data + 10) = dport; 65 | for (i = 0; i < 12; i++) 66 | res = ( (res << 8) + (data[perm[i]] ^ xor[i])) % 0xff100f; 67 | return res; 68 | } 69 | -------------------------------------------------------------------------------- /src/hash.h: -------------------------------------------------------------------------------- 1 | void init_hash(); 2 | u_int 3 | mkhash (u_int , u_short , u_int , u_short); 4 | -------------------------------------------------------------------------------- /src/ip_fragment.c: -------------------------------------------------------------------------------- 1 | /* 2 | This file is taken from Linux 2.0.36 kernel source. 3 | Modified in Jun 99 by Nergal. 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "checksum.h" 19 | #include "ip_fragment.h" 20 | #include "tcp.h" 21 | #include "util.h" 22 | #include "nids.h" 23 | 24 | #define IP_CE 0x8000 /* Flag: "Congestion" */ 25 | #define IP_DF 0x4000 /* Flag: "Don't Fragment" */ 26 | #define IP_MF 0x2000 /* Flag: "More Fragments" */ 27 | #define IP_OFFSET 0x1FFF /* "Fragment Offset" part */ 28 | 29 | #define IP_FRAG_TIME (30 * 1000) /* fragment lifetime */ 30 | 31 | #define UNUSED 314159 32 | #define FREE_READ UNUSED 33 | #define FREE_WRITE UNUSED 34 | #define GFP_ATOMIC UNUSED 35 | #define NETDEBUG(x) 36 | 37 | struct sk_buff { 38 | char *data; 39 | int truesize; 40 | }; 41 | 42 | struct timer_list { 43 | struct timer_list *prev; 44 | struct timer_list *next; 45 | int expires; 46 | void (*function)(); 47 | unsigned long data; 48 | // struct ipq *frags; 49 | }; 50 | 51 | struct hostfrags { 52 | struct ipq *ipqueue; 53 | int ip_frag_mem; 54 | u_int ip; 55 | int hash_index; 56 | struct hostfrags *prev; 57 | struct hostfrags *next; 58 | }; 59 | 60 | /* Describe an IP fragment. */ 61 | struct ipfrag { 62 | int offset; /* offset of fragment in IP datagram */ 63 | int end; /* last byte of data in datagram */ 64 | int len; /* length of this fragment */ 65 | struct sk_buff *skb; /* complete received fragment */ 66 | unsigned char *ptr; /* pointer into real fragment data */ 67 | struct ipfrag *next; /* linked list pointers */ 68 | struct ipfrag *prev; 69 | }; 70 | 71 | /* Describe an entry in the "incomplete datagrams" queue. */ 72 | struct ipq { 73 | unsigned char *mac; /* pointer to MAC header */ 74 | struct ip *iph; /* pointer to IP header */ 75 | int len; /* total length of original datagram */ 76 | short ihlen; /* length of the IP header */ 77 | short maclen; /* length of the MAC header */ 78 | struct timer_list timer; /* when will this queue expire? */ 79 | struct ipfrag *fragments; /* linked list of received fragments */ 80 | struct hostfrags *hf; 81 | struct ipq *next; /* linked list pointers */ 82 | struct ipq *prev; 83 | // struct device *dev; /* Device - for icmp replies */ 84 | }; 85 | 86 | /* 87 | Fragment cache limits. We will commit 256K at one time. Should we 88 | cross that limit we will prune down to 192K. This should cope with 89 | even the most extreme cases without allowing an attacker to 90 | measurably harm machine performance. 91 | */ 92 | #define IPFRAG_HIGH_THRESH (256*1024) 93 | #define IPFRAG_LOW_THRESH (192*1024) 94 | 95 | /* 96 | This fragment handler is a bit of a heap. On the other hand it works 97 | quite happily and handles things quite well. 98 | */ 99 | static struct hostfrags **fragtable; 100 | static struct hostfrags *this_host; 101 | static int numpack = 0; 102 | static int hash_size; 103 | static int timenow; 104 | static unsigned int time0; 105 | static struct timer_list *timer_head = 0, *timer_tail = 0; 106 | 107 | #define int_ntoa(x) inet_ntoa(*((struct in_addr *)&x)) 108 | 109 | static int 110 | jiffies() 111 | { 112 | struct timeval tv; 113 | 114 | if (timenow) 115 | return timenow; 116 | gettimeofday(&tv, 0); 117 | timenow = (tv.tv_sec - time0) * 1000 + tv.tv_usec / 1000; 118 | 119 | return timenow; 120 | } 121 | 122 | /* Memory Tracking Functions */ 123 | static void 124 | atomic_sub(int ile, int *co) 125 | { 126 | *co -= ile; 127 | } 128 | 129 | static void 130 | atomic_add(int ile, int *co) 131 | { 132 | *co += ile; 133 | } 134 | 135 | static void 136 | kfree_skb(struct sk_buff * skb, int type) 137 | { 138 | (void)type; 139 | free(skb); 140 | } 141 | 142 | static void 143 | panic(char *str) 144 | { 145 | fprintf(stderr, "%s", str); 146 | exit(1); 147 | } 148 | 149 | static void 150 | add_timer(struct timer_list * x) 151 | { 152 | if (timer_tail) { 153 | timer_tail->next = x; 154 | x->prev = timer_tail; 155 | x->next = 0; 156 | timer_tail = x; 157 | } 158 | else { 159 | x->prev = 0; 160 | x->next = 0; 161 | timer_tail = timer_head = x; 162 | } 163 | } 164 | 165 | static void 166 | del_timer(struct timer_list * x) 167 | { 168 | if (x->prev) 169 | x->prev->next = x->next; 170 | else 171 | timer_head = x->next; 172 | if (x->next) 173 | x->next->prev = x->prev; 174 | else 175 | timer_tail = x->prev; 176 | } 177 | 178 | static void 179 | frag_kfree_skb(struct sk_buff * skb, int type) 180 | { 181 | if (this_host) 182 | atomic_sub(skb->truesize, &this_host->ip_frag_mem); 183 | kfree_skb(skb, type); 184 | } 185 | 186 | static void 187 | frag_kfree_s(void *ptr, int len) 188 | { 189 | if (this_host) 190 | atomic_sub(len, &this_host->ip_frag_mem); 191 | free(ptr); 192 | } 193 | 194 | static void * 195 | frag_kmalloc(int size, int dummy) 196 | { 197 | void *vp = (void *) malloc(size); 198 | (void)dummy; 199 | if (!vp) 200 | return NULL; 201 | atomic_add(size, &this_host->ip_frag_mem); 202 | 203 | return vp; 204 | } 205 | 206 | /* Create a new fragment entry. */ 207 | static struct ipfrag * 208 | ip_frag_create(int offset, int end, struct sk_buff * skb, unsigned char *ptr) 209 | { 210 | struct ipfrag *fp; 211 | 212 | fp = (struct ipfrag *) frag_kmalloc(sizeof(struct ipfrag), GFP_ATOMIC); 213 | if (fp == NULL) { 214 | // NETDEBUG(printk("IP: frag_create: no memory left !\n")); 215 | nids_params.no_mem("ip_frag_create"); 216 | return (NULL); 217 | } 218 | memset(fp, 0, sizeof(struct ipfrag)); 219 | 220 | /* Fill in the structure. */ 221 | fp->offset = offset; 222 | fp->end = end; 223 | fp->len = end - offset; 224 | fp->skb = skb; 225 | fp->ptr = ptr; 226 | 227 | /* Charge for the SKB as well. */ 228 | this_host->ip_frag_mem += skb->truesize; 229 | 230 | return (fp); 231 | } 232 | 233 | static int 234 | frag_index(struct ip * iph) 235 | { 236 | unsigned int ip = ntohl(iph->ip_dst.s_addr); 237 | 238 | return (ip % hash_size); 239 | } 240 | 241 | static int 242 | hostfrag_find(struct ip * iph) 243 | { 244 | int hash_index = frag_index(iph); 245 | struct hostfrags *hf; 246 | 247 | this_host = 0; 248 | for (hf = fragtable[hash_index]; hf; hf = hf->next) 249 | if (hf->ip == iph->ip_dst.s_addr) { 250 | this_host = hf; 251 | break; 252 | } 253 | if (!this_host) 254 | return 0; 255 | else 256 | return 1; 257 | } 258 | 259 | static void 260 | hostfrag_create(struct ip * iph) 261 | { 262 | struct hostfrags *hf = mknew(struct hostfrags); 263 | int hash_index = frag_index(iph); 264 | 265 | hf->prev = 0; 266 | hf->next = fragtable[hash_index]; 267 | if (hf->next) 268 | hf->next->prev = hf; 269 | fragtable[hash_index] = hf; 270 | hf->ip = iph->ip_dst.s_addr; 271 | hf->ipqueue = 0; 272 | hf->ip_frag_mem = 0; 273 | hf->hash_index = hash_index; 274 | this_host = hf; 275 | } 276 | 277 | static void 278 | rmthis_host() 279 | { 280 | int hash_index = this_host->hash_index; 281 | 282 | if (this_host->prev) { 283 | this_host->prev->next = this_host->next; 284 | if (this_host->next) 285 | this_host->next->prev = this_host->prev; 286 | } 287 | else { 288 | fragtable[hash_index] = this_host->next; 289 | if (this_host->next) 290 | this_host->next->prev = 0; 291 | } 292 | free(this_host); 293 | this_host = 0; 294 | } 295 | 296 | /* 297 | Find the correct entry in the "incomplete datagrams" queue for this 298 | IP datagram, and return the queue entry address if found. 299 | */ 300 | static struct ipq * 301 | ip_find(struct ip * iph) 302 | { 303 | struct ipq *qp; 304 | struct ipq *qplast; 305 | 306 | qplast = NULL; 307 | for (qp = this_host->ipqueue; qp != NULL; qplast = qp, qp = qp->next) { 308 | if (iph->ip_id == qp->iph->ip_id && 309 | iph->ip_src.s_addr == qp->iph->ip_src.s_addr && 310 | iph->ip_dst.s_addr == qp->iph->ip_dst.s_addr && 311 | iph->ip_p == qp->iph->ip_p) { 312 | del_timer(&qp->timer); /* So it doesn't vanish on us. The timer will 313 | be reset anyway */ 314 | return (qp); 315 | } 316 | } 317 | return (NULL); 318 | } 319 | 320 | /* 321 | Remove an entry from the "incomplete datagrams" queue, either 322 | because we completed, reassembled and processed it, or because it 323 | timed out. 324 | */ 325 | static void 326 | ip_free(struct ipq * qp) 327 | { 328 | struct ipfrag *fp; 329 | struct ipfrag *xp; 330 | 331 | /* Stop the timer for this entry. */ 332 | del_timer(&qp->timer); 333 | 334 | /* Remove this entry from the "incomplete datagrams" queue. */ 335 | if (qp->prev == NULL) { 336 | this_host->ipqueue = qp->next; 337 | if (this_host->ipqueue != NULL) 338 | this_host->ipqueue->prev = NULL; 339 | else 340 | rmthis_host(); 341 | } 342 | else { 343 | qp->prev->next = qp->next; 344 | if (qp->next != NULL) 345 | qp->next->prev = qp->prev; 346 | } 347 | /* Release all fragment data. */ 348 | fp = qp->fragments; 349 | while (fp != NULL) { 350 | xp = fp->next; 351 | frag_kfree_skb(fp->skb, FREE_READ); 352 | frag_kfree_s(fp, sizeof(struct ipfrag)); 353 | fp = xp; 354 | } 355 | /* Release the IP header. */ 356 | frag_kfree_s(qp->iph, 64 + 8); 357 | 358 | /* Finally, release the queue descriptor itself. */ 359 | frag_kfree_s(qp, sizeof(struct ipq)); 360 | } 361 | 362 | /* Oops- a fragment queue timed out. Kill it and send an ICMP reply. */ 363 | static void 364 | ip_expire(unsigned long arg) 365 | { 366 | struct ipq *qp; 367 | 368 | qp = (struct ipq *) arg; 369 | 370 | /* Nuke the fragment queue. */ 371 | ip_free(qp); 372 | } 373 | 374 | /* 375 | Memory limiting on fragments. Evictor trashes the oldest fragment 376 | queue until we are back under the low threshold. 377 | */ 378 | static void 379 | ip_evictor(void) 380 | { 381 | // fprintf(stderr, "ip_evict:numpack=%i\n", numpack); 382 | while (this_host && this_host->ip_frag_mem > IPFRAG_LOW_THRESH) { 383 | if (!this_host->ipqueue) 384 | panic("ip_evictor: memcount"); 385 | ip_free(this_host->ipqueue); 386 | } 387 | } 388 | 389 | /* 390 | Add an entry to the 'ipq' queue for a newly received IP datagram. 391 | We will (hopefully :-) receive all other fragments of this datagram 392 | in time, so we just create a queue for this datagram, in which we 393 | will insert the received fragments at their respective positions. 394 | */ 395 | static struct ipq * 396 | ip_create(struct ip * iph) 397 | { 398 | struct ipq *qp; 399 | int ihlen; 400 | 401 | qp = (struct ipq *) frag_kmalloc(sizeof(struct ipq), GFP_ATOMIC); 402 | if (qp == NULL) { 403 | // NETDEBUG(printk("IP: create: no memory left !\n")); 404 | nids_params.no_mem("ip_create"); 405 | return (NULL); 406 | } 407 | memset(qp, 0, sizeof(struct ipq)); 408 | 409 | /* Allocate memory for the IP header (plus 8 octets for ICMP). */ 410 | ihlen = iph->ip_hl * 4; 411 | qp->iph = (struct ip *) frag_kmalloc(64 + 8, GFP_ATOMIC); 412 | if (qp->iph == NULL) { 413 | //NETDEBUG(printk("IP: create: no memory left !\n")); 414 | nids_params.no_mem("ip_create"); 415 | frag_kfree_s(qp, sizeof(struct ipq)); 416 | return (NULL); 417 | } 418 | memcpy(qp->iph, iph, ihlen + 8); 419 | qp->len = 0; 420 | qp->ihlen = ihlen; 421 | qp->fragments = NULL; 422 | qp->hf = this_host; 423 | 424 | /* Start a timer for this entry. */ 425 | qp->timer.expires = jiffies() + IP_FRAG_TIME; /* about 30 seconds */ 426 | qp->timer.data = (unsigned long) qp; /* pointer to queue */ 427 | qp->timer.function = ip_expire; /* expire function */ 428 | add_timer(&qp->timer); 429 | 430 | /* Add this entry to the queue. */ 431 | qp->prev = NULL; 432 | qp->next = this_host->ipqueue; 433 | if (qp->next != NULL) 434 | qp->next->prev = qp; 435 | this_host->ipqueue = qp; 436 | 437 | return (qp); 438 | } 439 | 440 | /* See if a fragment queue is complete. */ 441 | static int 442 | ip_done(struct ipq * qp) 443 | { 444 | struct ipfrag *fp; 445 | int offset; 446 | 447 | /* Only possible if we received the final fragment. */ 448 | if (qp->len == 0) 449 | return (0); 450 | 451 | /* Check all fragment offsets to see if they connect. */ 452 | fp = qp->fragments; 453 | offset = 0; 454 | while (fp != NULL) { 455 | if (fp->offset > offset) 456 | return (0); /* fragment(s) missing */ 457 | offset = fp->end; 458 | fp = fp->next; 459 | } 460 | /* All fragments are present. */ 461 | return (1); 462 | } 463 | 464 | 465 | /* 466 | Build a new IP datagram from all its fragments. 467 | 468 | FIXME: We copy here because we lack an effective way of handling 469 | lists of bits on input. Until the new skb data handling is in I'm 470 | not going to touch this with a bargepole. 471 | */ 472 | static char * 473 | ip_glue(struct ipq * qp) 474 | { 475 | char *skb; 476 | struct ip *iph; 477 | struct ipfrag *fp; 478 | unsigned char *ptr; 479 | int count, len; 480 | 481 | /* Allocate a new buffer for the datagram. */ 482 | len = qp->ihlen + qp->len; 483 | 484 | if (len > 65535) { 485 | // NETDEBUG(printk("Oversized IP packet from %s.\n", int_ntoa(qp->iph->ip_src.s_addr))); 486 | nids_params.syslog(NIDS_WARN_IP, NIDS_WARN_IP_OVERSIZED, qp->iph, 0); 487 | ip_free(qp); 488 | return NULL; 489 | } 490 | if ((skb = (char *) malloc(len)) == NULL) { 491 | // NETDEBUG(printk("IP: queue_glue: no memory for gluing queue %p\n", qp)); 492 | nids_params.no_mem("ip_glue"); 493 | ip_free(qp); 494 | return (NULL); 495 | } 496 | /* Fill in the basic details. */ 497 | ptr = (unsigned char *)skb; 498 | memcpy(ptr, ((unsigned char *) qp->iph), qp->ihlen); 499 | ptr += qp->ihlen; 500 | count = 0; 501 | 502 | /* Copy the data portions of all fragments into the new buffer. */ 503 | fp = qp->fragments; 504 | while (fp != NULL) { 505 | if (fp->len < 0 || fp->offset + qp->ihlen + fp->len > len) { 506 | //NETDEBUG(printk("Invalid fragment list: Fragment over size.\n")); 507 | nids_params.syslog(NIDS_WARN_IP, NIDS_WARN_IP_INVLIST, qp->iph, 0); 508 | ip_free(qp); 509 | //kfree_skb(skb, FREE_WRITE); 510 | //ip_statistics.IpReasmFails++; 511 | free(skb); 512 | return NULL; 513 | } 514 | memcpy((ptr + fp->offset), fp->ptr, fp->len); 515 | count += fp->len; 516 | fp = fp->next; 517 | } 518 | /* We glued together all fragments, so remove the queue entry. */ 519 | ip_free(qp); 520 | 521 | /* Done with all fragments. Fixup the new IP header. */ 522 | iph = (struct ip *) skb; 523 | iph->ip_off = 0; 524 | iph->ip_len = htons((iph->ip_hl * 4) + count); 525 | // skb->ip_hdr = iph; 526 | 527 | return (skb); 528 | } 529 | 530 | /* Process an incoming IP datagram fragment. */ 531 | static char * 532 | ip_defrag(struct ip *iph, struct sk_buff *skb) 533 | { 534 | struct ipfrag *prev, *next, *tmp; 535 | struct ipfrag *tfp; 536 | struct ipq *qp; 537 | char *skb2; 538 | unsigned char *ptr; 539 | int flags, offset; 540 | int i, ihl, end; 541 | 542 | if (!hostfrag_find(iph) && skb) 543 | hostfrag_create(iph); 544 | 545 | /* Start by cleaning up the memory. */ 546 | if (this_host) 547 | if (this_host->ip_frag_mem > IPFRAG_HIGH_THRESH) 548 | ip_evictor(); 549 | 550 | /* Find the entry of this IP datagram in the "incomplete datagrams" queue. */ 551 | if (this_host) 552 | qp = ip_find(iph); 553 | else 554 | qp = 0; 555 | 556 | /* Is this a non-fragmented datagram? */ 557 | offset = ntohs(iph->ip_off); 558 | flags = offset & ~IP_OFFSET; 559 | offset &= IP_OFFSET; 560 | if (((flags & IP_MF) == 0) && (offset == 0)) { 561 | if (qp != NULL) 562 | ip_free(qp); /* Fragmented frame replaced by full 563 | unfragmented copy */ 564 | return 0; 565 | } 566 | 567 | /* ip_evictor() could have removed all queues for the current host */ 568 | if (!this_host) 569 | hostfrag_create(iph); 570 | 571 | offset <<= 3; /* offset is in 8-byte chunks */ 572 | ihl = iph->ip_hl * 4; 573 | 574 | /* 575 | If the queue already existed, keep restarting its timer as long as 576 | we still are receiving fragments. Otherwise, create a fresh queue 577 | entry. 578 | */ 579 | if (qp != NULL) { 580 | /* ANK. If the first fragment is received, we should remember the correct 581 | IP header (with options) */ 582 | if (offset == 0) { 583 | qp->ihlen = ihl; 584 | memcpy(qp->iph, iph, ihl + 8); 585 | } 586 | del_timer(&qp->timer); 587 | qp->timer.expires = jiffies() + IP_FRAG_TIME; /* about 30 seconds */ 588 | qp->timer.data = (unsigned long) qp; /* pointer to queue */ 589 | qp->timer.function = ip_expire; /* expire function */ 590 | add_timer(&qp->timer); 591 | } 592 | else { 593 | /* If we failed to create it, then discard the frame. */ 594 | if ((qp = ip_create(iph)) == NULL) { 595 | kfree_skb(skb, FREE_READ); 596 | return NULL; 597 | } 598 | } 599 | /* Attempt to construct an oversize packet. */ 600 | if (ntohs(iph->ip_len) + (int) offset > 65535) { 601 | // NETDEBUG(printk("Oversized packet received from %s\n", int_ntoa(iph->ip_src.s_addr))); 602 | nids_params.syslog(NIDS_WARN_IP, NIDS_WARN_IP_OVERSIZED, iph, 0); 603 | kfree_skb(skb, FREE_READ); 604 | return NULL; 605 | } 606 | /* Determine the position of this fragment. */ 607 | end = offset + ntohs(iph->ip_len) - ihl; 608 | 609 | /* Point into the IP datagram 'data' part. */ 610 | ptr = (unsigned char *)(skb->data + ihl); 611 | 612 | /* Is this the final fragment? */ 613 | if ((flags & IP_MF) == 0) 614 | qp->len = end; 615 | 616 | /* 617 | Find out which fragments are in front and at the back of us in the 618 | chain of fragments so far. We must know where to put this 619 | fragment, right? 620 | */ 621 | prev = NULL; 622 | for (next = qp->fragments; next != NULL; next = next->next) { 623 | if (next->offset >= offset) 624 | break; /* bingo! */ 625 | prev = next; 626 | } 627 | /* 628 | We found where to put this one. Check for overlap with preceding 629 | fragment, and, if needed, align things so that any overlaps are 630 | eliminated. 631 | */ 632 | if (prev != NULL && offset < prev->end) { 633 | nids_params.syslog(NIDS_WARN_IP, NIDS_WARN_IP_OVERLAP, iph, 0); 634 | i = prev->end - offset; 635 | offset += i; /* ptr into datagram */ 636 | ptr += i; /* ptr into fragment data */ 637 | } 638 | /* 639 | Look for overlap with succeeding segments. 640 | If we can merge fragments, do it. 641 | */ 642 | for (tmp = next; tmp != NULL; tmp = tfp) { 643 | tfp = tmp->next; 644 | if (tmp->offset >= end) 645 | break; /* no overlaps at all */ 646 | nids_params.syslog(NIDS_WARN_IP, NIDS_WARN_IP_OVERLAP, iph, 0); 647 | 648 | i = end - next->offset; /* overlap is 'i' bytes */ 649 | tmp->len -= i; /* so reduce size of */ 650 | tmp->offset += i; /* next fragment */ 651 | tmp->ptr += i; 652 | /* 653 | If we get a frag size of <= 0, remove it and the packet that it 654 | goes with. We never throw the new frag away, so the frag being 655 | dumped has always been charged for. 656 | */ 657 | if (tmp->len <= 0) { 658 | if (tmp->prev != NULL) 659 | tmp->prev->next = tmp->next; 660 | else 661 | qp->fragments = tmp->next; 662 | 663 | if (tmp->next != NULL) 664 | tmp->next->prev = tmp->prev; 665 | 666 | next = tfp; /* We have killed the original next frame */ 667 | 668 | frag_kfree_skb(tmp->skb, FREE_READ); 669 | frag_kfree_s(tmp, sizeof(struct ipfrag)); 670 | } 671 | } 672 | /* Insert this fragment in the chain of fragments. */ 673 | tfp = NULL; 674 | tfp = ip_frag_create(offset, end, skb, ptr); 675 | 676 | /* 677 | No memory to save the fragment - so throw the lot. If we failed 678 | the frag_create we haven't charged the queue. 679 | */ 680 | if (!tfp) { 681 | nids_params.no_mem("ip_defrag"); 682 | kfree_skb(skb, FREE_READ); 683 | return NULL; 684 | } 685 | /* From now on our buffer is charged to the queues. */ 686 | tfp->prev = prev; 687 | tfp->next = next; 688 | if (prev != NULL) 689 | prev->next = tfp; 690 | else 691 | qp->fragments = tfp; 692 | 693 | if (next != NULL) 694 | next->prev = tfp; 695 | 696 | /* 697 | OK, so we inserted this new fragment into the chain. Check if we 698 | now have a full IP datagram which we can bump up to the IP 699 | layer... 700 | */ 701 | if (ip_done(qp)) { 702 | skb2 = ip_glue(qp); /* glue together the fragments */ 703 | return (skb2); 704 | } 705 | return (NULL); 706 | } 707 | 708 | int 709 | ip_defrag_stub(struct ip *iph, struct ip **defrag) 710 | { 711 | int offset, flags, tot_len; 712 | struct sk_buff *skb; 713 | 714 | numpack++; 715 | timenow = 0; 716 | while (timer_head && timer_head->expires < jiffies()) { 717 | this_host = ((struct ipq *) (timer_head->data))->hf; 718 | timer_head->function(timer_head->data); 719 | } 720 | offset = ntohs(iph->ip_off); 721 | flags = offset & ~IP_OFFSET; 722 | offset &= IP_OFFSET; 723 | if (((flags & IP_MF) == 0) && (offset == 0)) { 724 | ip_defrag(iph, 0); 725 | return IPF_NOTF; 726 | } 727 | tot_len = ntohs(iph->ip_len); 728 | skb = (struct sk_buff *) malloc(tot_len + sizeof(struct sk_buff)); 729 | if (!skb) 730 | nids_params.no_mem("ip_defrag_stub"); 731 | skb->data = (char *) (skb + 1); 732 | memcpy(skb->data, iph, tot_len); 733 | skb->truesize = tot_len + 16 + nids_params.dev_addon; 734 | skb->truesize = (skb->truesize + 15) & ~15; 735 | skb->truesize += nids_params.sk_buff_size; 736 | 737 | if ((*defrag = (struct ip *)ip_defrag((struct ip *) (skb->data), skb))) 738 | return IPF_NEW; 739 | 740 | return IPF_ISF; 741 | } 742 | 743 | void 744 | ip_frag_init(int n) 745 | { 746 | struct timeval tv; 747 | 748 | gettimeofday(&tv, 0); 749 | time0 = tv.tv_sec; 750 | fragtable = (struct hostfrags **) calloc(n, sizeof(struct hostfrags *)); 751 | if (!fragtable) 752 | nids_params.no_mem("ip_frag_init"); 753 | hash_size = n; 754 | } 755 | 756 | void 757 | ip_frag_exit(void) 758 | { 759 | if (fragtable) { 760 | free(fragtable); 761 | fragtable = NULL; 762 | } 763 | /* FIXME: do we need to free anything else? */ 764 | } 765 | -------------------------------------------------------------------------------- /src/ip_fragment.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 1999 Rafal Wojtczuk . All rights reserved. 3 | See the file COPYING for license details. 4 | */ 5 | 6 | #ifndef _NIDS_IP_FRAGMENT_H 7 | #define _NIDS_IP_FRAGMENT_H 8 | 9 | #define IPF_NOTF 1 10 | #define IPF_NEW 2 11 | #define IPF_ISF 3 12 | 13 | void ip_frag_init(int); 14 | void ip_frag_exit(void); 15 | int ip_defrag_stub(struct ip *, struct ip **); 16 | 17 | #endif /* _NIDS_IP_FRAGMENT_H */ 18 | -------------------------------------------------------------------------------- /src/ip_options.c: -------------------------------------------------------------------------------- 1 | /* 2 | This file is taken from Linux 2.0.36 kernel source. 3 | Modified in Jun 99 by Nergal. 4 | */ 5 | #include 6 | #include 7 | 8 | #define __u8 unsigned char 9 | #define __u16 unsigned short 10 | #define __u32 unsigned int 11 | 12 | #define IPOPT_END 0 13 | #define IPOPT_NOOP 1 14 | #define IPOPT_SEC 130 15 | #define IPOPT_LSRR 131 16 | #define IPOPT_SSRR 137 17 | #define IPOPT_RR 7 18 | #define IPOPT_SID 136 19 | #define IPOPT_TIMESTAMP 68 20 | 21 | #define MAXTTL 255 22 | 23 | struct timestamp { 24 | __u8 len; 25 | __u8 ptr; 26 | #ifdef WORDS_BIGENDIAN 27 | __u8 overflow:4, flags:4; 28 | #else 29 | __u8 flags:4, overflow:4; 30 | #endif 31 | __u32 data[9]; 32 | }; 33 | 34 | #define MAX_ROUTE 16 35 | 36 | struct route { 37 | char route_size; 38 | char pointer; 39 | unsigned long route[MAX_ROUTE]; 40 | }; 41 | 42 | #define IPOPT_OPTVAL 0 43 | #define IPOPT_OLEN 1 44 | #define IPOPT_OFFSET 2 45 | #define IPOPT_MINOFF 4 46 | #define MAX_IPOPTLEN 40 47 | #define IPOPT_NOP IPOPT_NOOP 48 | #define IPOPT_EOL IPOPT_END 49 | #define IPOPT_TS IPOPT_TIMESTAMP 50 | 51 | #define IPOPT_TS_TSONLY 0 /* timestamps only */ 52 | #define IPOPT_TS_TSANDADDR 1 /* timestamps and addresses */ 53 | #define IPOPT_TS_PRESPEC 3 /* specified modules only */ 54 | 55 | struct options { 56 | __u32 faddr; /* Saved first hop address */ 57 | unsigned char optlen; 58 | unsigned char srr; 59 | unsigned char rr; 60 | unsigned char ts; 61 | unsigned char is_setbyuser:1, /* Set by setsockopt? */ 62 | is_data:1, /* Options in __data, rather than skb */ 63 | is_strictroute:1, /* Strict source route */ 64 | srr_is_hit:1, /* Packet destination addr was our one */ 65 | is_changed:1, /* IP checksum more not valid */ 66 | rr_needaddr:1, /* Need to record addr of outgoing dev */ 67 | ts_needtime:1, /* Need to record timestamp */ 68 | ts_needaddr:1; /* Need to record addr of outgoing dev */ 69 | unsigned char __pad1; 70 | unsigned char __pad2; 71 | unsigned char __pad3; 72 | unsigned char __data[]; 73 | }; 74 | 75 | struct iphdr { 76 | #ifdef WORDS_BIGENDIAN 77 | __u8 version:4, ihl:4; 78 | #else 79 | __u8 ihl:4, version:4; 80 | #endif 81 | __u8 tos; 82 | __u16 tot_len; 83 | __u16 id; 84 | __u16 frag_off; 85 | __u8 ttl; 86 | __u8 protocol; 87 | __u16 check; 88 | __u32 saddr; 89 | __u32 daddr; 90 | /* The options start here. */ 91 | }; 92 | 93 | #define ip_chk_addr(x) 0 94 | 95 | int 96 | ip_options_compile(unsigned char *iph) 97 | { 98 | int l; 99 | unsigned char *optptr; 100 | int optlen; 101 | unsigned char *pp_ptr = 0; 102 | char optholder[16]; 103 | struct options *opt; 104 | int skb = 1; 105 | int skb_pa_addr = 314159; 106 | 107 | opt = (struct options *) optholder; 108 | memset(opt, 0, sizeof(struct options)); 109 | opt->optlen = ((struct iphdr *) iph)->ihl * 4 - sizeof(struct iphdr); 110 | optptr = iph + sizeof(struct iphdr); 111 | opt->is_data = 0; 112 | 113 | for (l = opt->optlen; l > 0;) { 114 | switch (*optptr) { 115 | case IPOPT_END: 116 | for (optptr++, l--; l > 0; l--) { 117 | if (*optptr != IPOPT_END) { 118 | *optptr = IPOPT_END; 119 | opt->is_changed = 1; 120 | } 121 | } 122 | goto eol; 123 | case IPOPT_NOOP: 124 | l--; 125 | optptr++; 126 | continue; 127 | } 128 | optlen = optptr[1]; 129 | if (optlen < 2 || optlen > l) { 130 | pp_ptr = optptr; 131 | goto error; 132 | } 133 | switch (*optptr) { 134 | case IPOPT_SSRR: 135 | case IPOPT_LSRR: 136 | if (optlen < 3) { 137 | pp_ptr = optptr + 1; 138 | goto error; 139 | } 140 | if (optptr[2] < 4) { 141 | pp_ptr = optptr + 2; 142 | goto error; 143 | } 144 | /* NB: cf RFC-1812 5.2.4.1 */ 145 | if (opt->srr) { 146 | pp_ptr = optptr; 147 | goto error; 148 | } 149 | if (!skb) { 150 | if (optptr[2] != 4 || optlen < 7 || ((optlen - 3) & 3)) { 151 | pp_ptr = optptr + 1; 152 | goto error; 153 | } 154 | memcpy(&opt->faddr, &optptr[3], 4); 155 | if (optlen > 7) 156 | memmove(&optptr[3], &optptr[7], optlen - 7); 157 | } 158 | opt->is_strictroute = (optptr[0] == IPOPT_SSRR); 159 | opt->srr = optptr - iph; 160 | break; 161 | case IPOPT_RR: 162 | if (opt->rr) { 163 | pp_ptr = optptr; 164 | goto error; 165 | } 166 | if (optlen < 3) { 167 | pp_ptr = optptr + 1; 168 | goto error; 169 | } 170 | if (optptr[2] < 4) { 171 | pp_ptr = optptr + 2; 172 | goto error; 173 | } 174 | if (optptr[2] <= optlen) { 175 | if (optptr[2] + 3 > optlen) { 176 | pp_ptr = optptr + 2; 177 | goto error; 178 | } 179 | if (skb) { 180 | memcpy(&optptr[optptr[2] - 1], &skb_pa_addr, 4); 181 | opt->is_changed = 1; 182 | } 183 | optptr[2] += 4; 184 | opt->rr_needaddr = 1; 185 | } 186 | opt->rr = optptr - iph; 187 | break; 188 | case IPOPT_TIMESTAMP: 189 | if (opt->ts) { 190 | pp_ptr = optptr; 191 | goto error; 192 | } 193 | if (optlen < 4) { 194 | pp_ptr = optptr + 1; 195 | goto error; 196 | } 197 | if (optptr[2] < 5) { 198 | pp_ptr = optptr + 2; 199 | goto error; 200 | } 201 | if (optptr[2] <= optlen) { 202 | struct timestamp *ts = (struct timestamp *) (optptr + 1); 203 | __u32 *timeptr = 0; 204 | 205 | if (ts->ptr + 3 > ts->len) { 206 | pp_ptr = optptr + 2; 207 | goto error; 208 | } 209 | switch (ts->flags) { 210 | case IPOPT_TS_TSONLY: 211 | opt->ts = optptr - iph; 212 | if (skb) 213 | timeptr = (__u32 *) & optptr[ts->ptr - 1]; 214 | opt->ts_needtime = 1; 215 | ts->ptr += 4; 216 | break; 217 | case IPOPT_TS_TSANDADDR: 218 | if (ts->ptr + 7 > ts->len) { 219 | pp_ptr = optptr + 2; 220 | goto error; 221 | } 222 | opt->ts = optptr - iph; 223 | if (skb) { 224 | memcpy(&optptr[ts->ptr - 1], &skb_pa_addr, 4); 225 | timeptr = (__u32 *) & optptr[ts->ptr + 3]; 226 | } 227 | opt->ts_needaddr = 1; 228 | opt->ts_needtime = 1; 229 | ts->ptr += 8; 230 | break; 231 | case IPOPT_TS_PRESPEC: 232 | if (ts->ptr + 7 > ts->len) { 233 | pp_ptr = optptr + 2; 234 | goto error; 235 | } 236 | opt->ts = optptr - iph; 237 | { 238 | __u32 addr; 239 | 240 | memcpy(&addr, &optptr[ts->ptr - 1], 4); 241 | if (ip_chk_addr(addr) == 0) 242 | break; 243 | if (skb) 244 | timeptr = (__u32 *) & optptr[ts->ptr + 3]; 245 | } 246 | opt->ts_needaddr = 1; 247 | opt->ts_needtime = 1; 248 | ts->ptr += 8; 249 | break; 250 | default: 251 | pp_ptr = optptr + 3; 252 | goto error; 253 | } 254 | if (timeptr) { 255 | //struct timeval tv; 256 | __u32 midtime = 1; 257 | 258 | //do_gettimeofday(&tv); 259 | //midtime = htonl((tv.tv_sec % 86400) * 1000 + tv.tv_usec / 1000); 260 | memcpy(timeptr, &midtime, sizeof(__u32)); 261 | opt->is_changed = 1; 262 | } 263 | } 264 | else { 265 | struct timestamp *ts = (struct timestamp *) (optptr + 1); 266 | 267 | if (ts->overflow == 15) { 268 | pp_ptr = optptr + 3; 269 | goto error; 270 | } 271 | opt->ts = optptr - iph; 272 | if (skb) { 273 | ts->overflow++; 274 | opt->is_changed = 1; 275 | } 276 | } 277 | break; 278 | case IPOPT_SEC: 279 | case IPOPT_SID: 280 | default: 281 | if (!skb) { 282 | pp_ptr = optptr; 283 | goto error; 284 | } 285 | break; 286 | } 287 | l -= optlen; 288 | optptr += optlen; 289 | } 290 | 291 | eol: 292 | opt = (struct options *) optholder; 293 | if (!pp_ptr) 294 | if (!opt->srr) 295 | return 0; 296 | 297 | error: 298 | return -1; 299 | } 300 | -------------------------------------------------------------------------------- /src/killtcp.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 1999 Rafal Wojtczuk . All rights reserved. 3 | See the file COPYING for license details. 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include "tcp.h" 10 | #include "util.h" 11 | #include "nids.h" 12 | #if LIBNET_VER == 0 13 | #include 14 | 15 | static int libnetsock = 0; 16 | 17 | void nids_killtcp_seq(struct tcp_stream *a_tcp, int seqoff) 18 | { 19 | char buf[IP_H + TCP_H]; 20 | 21 | if (libnetsock == 0) 22 | return; 23 | 24 | libnet_build_ip(TCP_H, 0, 12345, 0, 64, IPPROTO_TCP, a_tcp->addr.saddr, 25 | a_tcp->addr.daddr, 0, 0, buf); 26 | libnet_build_tcp(a_tcp->addr.source, a_tcp->addr.dest, 27 | a_tcp->client.first_data_seq + 28 | a_tcp->server.count + a_tcp->server.urg_count + 29 | (seqoff?(a_tcp->server.window/2):0), 30 | 0, 0x4, 32000, 0, 0, 0, buf + IP_H); 31 | libnet_do_checksum(buf, IPPROTO_TCP, TCP_H); 32 | libnet_write_ip(libnetsock, buf, TCP_H + IP_H); 33 | 34 | libnet_build_ip(TCP_H, 0, 12345, 0, 64, IPPROTO_TCP, a_tcp->addr.daddr, 35 | a_tcp->addr.saddr, 0, 0, buf); 36 | libnet_build_tcp(a_tcp->addr.dest, a_tcp->addr.source, 37 | a_tcp->server.first_data_seq + 38 | a_tcp->client.count + a_tcp->client.urg_count + 39 | (seqoff?(a_tcp->client.window/2):0), 40 | 41 | 0, 0x4, 32000, 0, 0, 0, buf + IP_H); 42 | libnet_do_checksum(buf, IPPROTO_TCP, TCP_H); 43 | libnet_write_ip(libnetsock, buf, TCP_H + IP_H); 44 | } 45 | void nids_killtcp(struct tcp_stream *a_tcp) 46 | { 47 | nids_killtcp_seq(a_tcp, 0); 48 | nids_killtcp_seq(a_tcp, 1); 49 | } 50 | int raw_init() 51 | { 52 | libnetsock = libnet_open_raw_sock(IPPROTO_RAW); 53 | if (libnetsock <= 0) 54 | return 0; 55 | else 56 | return 1; 57 | } 58 | #elif LIBNET_VER == 1 59 | #include 60 | static libnet_ptag_t tcp_tag, ip_tag; 61 | static libnet_t *l = 0; 62 | int raw_init() 63 | { 64 | char errbuf[1024]; 65 | l = libnet_init(LIBNET_RAW4, /* injection type */ 66 | NULL, /* network interface */ 67 | errbuf); /* error buffer */ 68 | 69 | if (!l) { 70 | printf("%s\n", errbuf); 71 | return 0; 72 | } 73 | tcp_tag = LIBNET_PTAG_INITIALIZER; 74 | ip_tag = LIBNET_PTAG_INITIALIZER; 75 | return 1; 76 | } 77 | 78 | void nids_killtcp_seq(struct tcp_stream *a_tcp, int seqoff) 79 | { 80 | if (!l) 81 | return; 82 | tcp_tag = libnet_build_tcp(a_tcp->addr.source, a_tcp->addr.dest, 83 | a_tcp->client.first_data_seq + 84 | a_tcp->server.count + a_tcp->server.urg_count + 85 | (seqoff?(a_tcp->server.window/2):0), 86 | 0, 0x4, 32000, 0, 0, LIBNET_TCP_H, NULL, 0, l, tcp_tag); 87 | ip_tag = 88 | libnet_build_ipv4(LIBNET_TCP_H + LIBNET_IPV4_H, 0, 12345, 0, 64, 89 | IPPROTO_TCP, 0, a_tcp->addr.saddr, 90 | a_tcp->addr.daddr, 0, 0, l, ip_tag); 91 | libnet_write(l); 92 | tcp_tag = libnet_build_tcp(a_tcp->addr.dest, a_tcp->addr.source, 93 | a_tcp->server.first_data_seq + 94 | a_tcp->client.count + a_tcp->client.urg_count + 95 | (seqoff?(a_tcp->client.window/2):0), 96 | 0, 0x4, 32000, 0, 97 | 0, LIBNET_TCP_H, NULL, 0, l, tcp_tag); 98 | ip_tag = 99 | libnet_build_ipv4(LIBNET_TCP_H + LIBNET_IPV4_H, 0, 12345, 0, 64, 100 | IPPROTO_TCP, 0, a_tcp->addr.daddr, 101 | a_tcp->addr.saddr, 0, 0, l, ip_tag); 102 | libnet_write(l); 103 | } 104 | void nids_killtcp(struct tcp_stream *a_tcp) 105 | { 106 | nids_killtcp_seq(a_tcp, 0); 107 | nids_killtcp_seq(a_tcp, 1); 108 | } 109 | #elif LIBNET_VER == -1 110 | static int initialized = 0; 111 | int raw_init() 112 | { 113 | initialized = 1; 114 | return 1; 115 | } 116 | 117 | void nids_killtcp(struct tcp_stream *a_tcp) 118 | { 119 | (void)a_tcp; 120 | if (initialized) 121 | abort(); 122 | } 123 | #else 124 | #error Something wrong with LIBNET_VER 125 | #endif 126 | -------------------------------------------------------------------------------- /src/libnids-track-established.patch: -------------------------------------------------------------------------------- 1 | diff --git a/configure.in b/configure.in 2 | index 86f32b8..910b1ec 100644 3 | --- a/configure.in 4 | +++ b/configure.in 5 | @@ -98,6 +98,13 @@ AC_ARG_WITH(libpcap, 6 | AC_SUBST(PCAP_CFLAGS) 7 | AC_SUBST(PCAPLIB) 8 | 9 | +AC_ARG_ENABLE(tcpreasm, 10 | +[ --enable-tcpreasm enable tcp reassembly support], 11 | +, 12 | +[ enable_tcpreasm="no"] 13 | +) 14 | +test "${enable_tcpreasm}" = "yes" && CFLAGS="${CFLAGS} -DENABLE_TCPREASM" 15 | + 16 | dnl Checks for libglib2.8 17 | AC_ARG_ENABLE(libglib, 18 | [ --disable-libglib use glib2 for multiprocessing support], 19 | diff --git a/src/libnids.c b/src/libnids.c 20 | index af56808..d694fa9 100644 21 | --- a/src/libnids.c 22 | +++ b/src/libnids.c 23 | @@ -47,6 +47,9 @@ static struct proc_node *ip_procs; 24 | static struct proc_node *udp_procs; 25 | 26 | struct proc_node *tcp_procs; 27 | +#ifdef ENABLE_TCPREASM 28 | +struct proc_node *tcp_resume_procs; 29 | +#endif 30 | static int linktype; 31 | static pcap_t *desc = NULL; 32 | 33 | @@ -109,6 +112,9 @@ struct nids_prm nids_params = { 34 | 20000, /* queue_limit */ 35 | 0, /* tcp_workarounds */ 36 | NULL, /* pcap_desc */ 37 | +#ifdef ENABLE_TCPREASM 38 | + 1, /* tcp_resume_wscale */ 39 | +#endif 40 | 3600 /* tcp_flow_timeout */ 41 | }; 42 | 43 | @@ -471,6 +477,9 @@ static void init_procs() 44 | ip_procs->item = gen_ip_proc; 45 | ip_procs->next = 0; 46 | tcp_procs = 0; 47 | +#ifdef ENABLE_TCPREASM 48 | + tcp_resume_procs = 0; 49 | +#endif 50 | udp_procs = 0; 51 | } 52 | 53 | diff --git a/src/nids.h b/src/nids.h 54 | index a035436..2d1cc8e 100644 55 | --- a/src/nids.h 56 | +++ b/src/nids.h 57 | @@ -48,10 +48,19 @@ enum 58 | # define NIDS_RESET 4 59 | # define NIDS_TIMED_OUT 5 60 | # define NIDS_EXITING 6 /* nids is exiting; last chance to get data */ 61 | +#ifdef ENABLE_TCPREASM 62 | +# define NIDS_RESUME 7 63 | +#endif 64 | 65 | # define NIDS_DO_CHKSUM 0 66 | # define NIDS_DONT_CHKSUM 1 67 | 68 | +#ifdef ENABLE_TCPREASM 69 | +# define NIDS_TCP_RESUME_NONE 0 70 | +# define NIDS_TCP_RESUME_CLIENT 1 71 | +# define NIDS_TCP_RESUME_SERVER 2 72 | +#endif 73 | + 74 | struct tuple4 75 | { 76 | u_short source; 77 | @@ -63,6 +72,10 @@ struct tuple4 78 | struct half_stream 79 | { 80 | char state; 81 | +#ifdef ENABLE_TCPREASM 82 | + char resume_second_half; 83 | +#endif 84 | + 85 | char collect; 86 | char collect_urg; 87 | 88 | @@ -132,6 +145,9 @@ struct nids_prm 89 | int queue_limit; 90 | int tcp_workarounds; 91 | pcap_t *pcap_desc; 92 | +#ifdef ENABLE_TCPREASM 93 | + int tcp_resume_wscale; 94 | +#endif 95 | int tcp_flow_timeout; 96 | }; 97 | 98 | @@ -150,6 +166,10 @@ void nids_register_ip (void (*)); 99 | void nids_unregister_ip (void (*)); 100 | void nids_register_tcp (void (*)); 101 | void nids_unregister_tcp (void (*x)); 102 | +#ifdef ENABLE_TCPREASM 103 | +void nids_register_tcp_resume (void (*)); 104 | +void nids_unregister_tcp_resume (void (*x)); 105 | +#endif 106 | void nids_register_udp (void (*)); 107 | void nids_unregister_udp (void (*)); 108 | void nids_killtcp (struct tcp_stream *); 109 | diff --git a/src/tcp.c b/src/tcp.c 110 | index 52bb2df..6375c2d 100644 111 | --- a/src/tcp.c 112 | +++ b/src/tcp.c 113 | @@ -49,6 +49,9 @@ enum { 114 | #define EXP_SEQ (snd->first_data_seq + rcv->count + rcv->urg_count) 115 | 116 | extern struct proc_node *tcp_procs; 117 | +#ifdef ENABLE_TCPREASM 118 | +extern struct proc_node *tcp_resume_procs; 119 | +#endif 120 | 121 | static struct tcp_stream **tcp_stream_table; 122 | static struct tcp_stream *streams_pool; 123 | @@ -249,7 +252,103 @@ static int get_wscale(struct tcphdr * this_tcphdr, unsigned int * ws) 124 | return ret; 125 | } 126 | 127 | - 128 | +#ifdef ENABLE_TCPREASM 129 | +static struct tcp_stream * 130 | +initiate_tcp_resume(struct tcphdr * this_tcphdr, struct ip * this_iphdr, int direction) 131 | +{ 132 | + struct tcp_stream *tolink; 133 | + struct tcp_stream *a_tcp; 134 | + int hash_index; 135 | + struct tuple4 addr; 136 | + struct half_stream *half; 137 | + struct half_stream *other_half; 138 | + 139 | + switch (direction) 140 | + { 141 | + case NIDS_TCP_RESUME_CLIENT: 142 | + addr.source = ntohs(this_tcphdr->th_sport); 143 | + addr.dest = ntohs(this_tcphdr->th_dport); 144 | + addr.saddr = this_iphdr->ip_src.s_addr; 145 | + addr.daddr = this_iphdr->ip_dst.s_addr; 146 | + break; 147 | + case NIDS_TCP_RESUME_SERVER: 148 | + addr.source = ntohs(this_tcphdr->th_dport); 149 | + addr.dest = ntohs(this_tcphdr->th_sport); 150 | + addr.saddr = this_iphdr->ip_dst.s_addr; 151 | + addr.daddr = this_iphdr->ip_src.s_addr; 152 | + break; 153 | + default: 154 | + return NULL; 155 | + } 156 | + hash_index = mk_hash_index(addr); 157 | + 158 | + if (tcp_num > max_stream) { 159 | + struct lurker_node *i; 160 | + 161 | + tcp_oldest->nids_state = NIDS_TIMED_OUT; 162 | + for (i = tcp_oldest->listeners; i; i = i->next) 163 | + (i->item) (tcp_oldest, &i->data); 164 | + nids_free_tcp_stream(tcp_oldest); 165 | + nids_params.syslog(NIDS_WARN_TCP, NIDS_WARN_TCP_TOOMUCH, ugly_iphdr, this_tcphdr); 166 | + } 167 | + a_tcp = free_streams; 168 | + if (!a_tcp) { 169 | + fprintf(stderr, "gdb me ...\n"); 170 | + pause(); 171 | + } 172 | + free_streams = a_tcp->next_free; 173 | + 174 | + tcp_num++; 175 | + tolink = tcp_stream_table[hash_index]; 176 | + memset(a_tcp, 0, sizeof(struct tcp_stream)); 177 | + a_tcp->hash_index = hash_index; 178 | + a_tcp->addr = addr; 179 | + if (direction == NIDS_TCP_RESUME_CLIENT) 180 | + { 181 | + half = &a_tcp->client; 182 | + other_half = &a_tcp->server; 183 | + } 184 | + else 185 | + { 186 | + half = &a_tcp->server; 187 | + other_half = &a_tcp->client; 188 | + } 189 | + half->state = TCP_ESTABLISHED; 190 | + half->seq = ntohl(this_tcphdr->th_seq) + 1; 191 | + half->first_data_seq = half->seq - 1; 192 | + half->window = ntohs(this_tcphdr->th_win); 193 | + half->ts_on = 0; 194 | + half->wscale = nids_params.tcp_resume_wscale; 195 | + if (this_tcphdr->th_flags & TH_ACK) 196 | + half->ack_seq = ntohl(this_tcphdr->th_ack); 197 | + 198 | +#ifdef ENABLE_TCPREASM_DEBUG 199 | + DEBUG_REASSEMBLY("new connection: seq = %u, ack_seq = %u\n", 200 | + half->seq, half->ack_seq); 201 | +#endif 202 | + 203 | + other_half->ack_seq = half->seq; 204 | + other_half->state = TCP_ESTABLISHED; 205 | + other_half->resume_second_half = 1; 206 | + other_half->ts_on = 0; 207 | + other_half->window = half->window; 208 | + other_half->wscale = nids_params.tcp_resume_wscale; 209 | + a_tcp->next_node = tolink; 210 | + a_tcp->prev_node = 0; 211 | + if (tolink) 212 | + tolink->prev_node = a_tcp; 213 | + tcp_stream_table[hash_index] = a_tcp; 214 | + a_tcp->next_time = tcp_latest; 215 | + a_tcp->prev_time = 0; 216 | + if (!tcp_oldest) 217 | + tcp_oldest = a_tcp; 218 | + if (tcp_latest) 219 | + tcp_latest->prev_time = a_tcp; 220 | + tcp_latest = a_tcp; 221 | + 222 | + return a_tcp; 223 | +} 224 | +#endif 225 | 226 | 227 | static void 228 | @@ -715,6 +814,9 @@ process_tcp(u_char * data, int skblen) 229 | unsigned int tmp_ts; 230 | struct tcp_stream *a_tcp; 231 | struct half_stream *snd, *rcv; 232 | +#ifdef ENABLE_TCPREASM 233 | + int resumed_tcp = 0; 234 | +#endif 235 | 236 | ugly_iphdr = this_iphdr; 237 | iplen = ntohs(this_iphdr->ip_len); 238 | @@ -746,6 +848,22 @@ process_tcp(u_char * data, int skblen) 239 | this_tcphdr); 240 | return; 241 | } 242 | + 243 | +#ifdef ENABLE_TCPREASM_DEBUG 244 | + DEBUG_REASSEMBLY("process_tcp starting: packet is %u.%u.%u.%u:%u->%u.%u.%u.%u:%u\n", 245 | + (ntohl(this_iphdr->ip_src.s_addr) >> 24) & 0xff, 246 | + (ntohl(this_iphdr->ip_src.s_addr) >> 16) & 0xff, 247 | + (ntohl(this_iphdr->ip_src.s_addr) >> 8) & 0xff, 248 | + (ntohl(this_iphdr->ip_src.s_addr)) & 0xff, 249 | + ntohs(this_tcphdr->th_sport), 250 | + (ntohl(this_iphdr->ip_dst.s_addr) >> 24) & 0xff, 251 | + (ntohl(this_iphdr->ip_dst.s_addr) >> 16) & 0xff, 252 | + (ntohl(this_iphdr->ip_dst.s_addr) >> 8) & 0xff, 253 | + (ntohl(this_iphdr->ip_dst.s_addr)) & 0xff, 254 | + ntohs(this_tcphdr->th_dport) 255 | + ); 256 | +#endif 257 | + 258 | #if 0 259 | check_flags(this_iphdr, this_tcphdr); 260 | //ECN 261 | @@ -753,10 +871,64 @@ process_tcp(u_char * data, int skblen) 262 | if (!(a_tcp = find_stream(this_tcphdr, this_iphdr, &from_client))) { 263 | if ((this_tcphdr->th_flags & TH_SYN) && 264 | !(this_tcphdr->th_flags & TH_ACK) && 265 | - !(this_tcphdr->th_flags & TH_RST)) 266 | + !(this_tcphdr->th_flags & TH_RST)) { 267 | add_new_tcp(this_tcphdr, this_iphdr); 268 | + return; 269 | + } 270 | + 271 | +#ifdef ENABLE_TCPREASM 272 | +#ifdef ENABLE_TCPREASM_DEBUG 273 | + DEBUG_REASSEMBLY("packet is not in stream context: SYN %u, RST %u, ACK %u\n", 274 | + this_tcphdr->th_flags & TH_SYN, 275 | + this_tcphdr->th_flags & TH_RST, 276 | + this_tcphdr->th_flags & TH_ACK 277 | + ); 278 | +#endif 279 | + 280 | + /* does this look like a stream we should try to resume? 281 | + * the conditions for it are: 282 | + * - No SYN (that's the whole point) 283 | + * - No RST or FIN (no point in doing that) 284 | + * - we have a resume callback to identify direction 285 | + */ 286 | + if ((this_tcphdr->th_flags & TH_SYN) != 0 || 287 | + (this_tcphdr->th_flags & TH_RST) != 0 || 288 | + (this_tcphdr->th_flags & TH_FIN) != 0) { 289 | + return; 290 | + } 291 | + else { 292 | + struct proc_node *i; 293 | + for (i = tcp_resume_procs; i; i = i->next) { 294 | + int resume; 295 | + 296 | +#ifdef ENABLE_TCPREASM_DEBUG 297 | + DEBUG_REASSEMBLY("trying to resume stream\n"); 298 | +#endif 299 | + 300 | + i->item(this_tcphdr, this_iphdr, &resume); 301 | + from_client = (resume == NIDS_TCP_RESUME_CLIENT); 302 | + a_tcp = initiate_tcp_resume(this_tcphdr, this_iphdr, resume); 303 | + 304 | +#ifdef ENABLE_TCPREASM_DEBUG 305 | + DEBUG_REASSEMBLY("a_tcp = %p, from_client = %u, resume = %u\n", 306 | + a_tcp, from_client, resume); 307 | +#endif 308 | + 309 | + if (a_tcp) { 310 | + resumed_tcp = 1; 311 | + break; 312 | + } 313 | + } 314 | + } 315 | + 316 | + if (!resumed_tcp) 317 | + return; 318 | + } 319 | +#else 320 | return; 321 | } 322 | +#endif 323 | + 324 | if (from_client) { 325 | snd = &a_tcp->client; 326 | rcv = &a_tcp->server; 327 | @@ -765,6 +937,38 @@ process_tcp(u_char * data, int skblen) 328 | rcv = &a_tcp->client; 329 | snd = &a_tcp->server; 330 | } 331 | + 332 | +#ifdef ENABLE_TCPREASM 333 | +#ifdef ENABLE_TCPREASM_DEBUG 334 | + DEBUG_REASSEMBLY("processing packet: seq = %u, ack = %u, snd->seq = %u, rcv->ack_seq = %u\n", 335 | + ntohl(this_tcphdr->th_seq), 336 | + ntohl(this_tcphdr->th_ack), 337 | + snd->seq, rcv->ack_seq); 338 | +#endif 339 | + 340 | + /* are we the 2nd half of the resume? */ 341 | + if (snd->resume_second_half) { 342 | + snd->seq = ntohl(this_tcphdr->th_seq) + 1; 343 | + snd->first_data_seq = snd->seq - 1; 344 | + snd->window = ntohs(this_tcphdr->th_win); 345 | + snd->resume_second_half = 0; 346 | + snd->ack_seq = rcv->seq; 347 | + 348 | +#ifdef ENABLE_TCPREASM_DEBUG 349 | + DEBUG_REASSEMBLY("second half resumed, seq = %u, first = %u, ack = %u\n", 350 | + snd->seq, snd->first_data_seq, snd->ack_seq); 351 | +#endif 352 | + 353 | + } 354 | + 355 | + if (resumed_tcp) { 356 | + snd->state = TCP_ESTABLISHED; 357 | + a_tcp->nids_state = NIDS_RESUME; 358 | + goto do_lurkers; 359 | + } 360 | +#endif 361 | + 362 | + /* normal SYN+ACK processing */ 363 | if ((this_tcphdr->th_flags & TH_SYN)) { 364 | if (from_client) { 365 | // if timeout since previous 366 | @@ -815,8 +1019,15 @@ process_tcp(u_char * data, int skblen) 367 | ( !before(ntohl(this_tcphdr->th_seq), rcv->ack_seq + rcv->window*rcv->wscale) || 368 | before(ntohl(this_tcphdr->th_seq) + datalen, rcv->ack_seq) 369 | ) 370 | - ) 371 | + ) { 372 | +#ifdef ENABLE_TCPREASM_DEBUG 373 | + DEBUG_REASSEMBLY("packet is ignored: " 374 | + "datalen=%u, seq=%u, rcv->ack_seq=%u, rcv->window=%u, rcv->wscale=%u\n", 375 | + datalen, ntohl(this_tcphdr->th_seq), 376 | + rcv->ack_seq, rcv->window, rcv->wscale); 377 | +#endif 378 | return; 379 | + } 380 | 381 | if ((this_tcphdr->th_flags & TH_RST)) { 382 | if (a_tcp->nids_state == NIDS_DATA) { 383 | @@ -849,13 +1060,24 @@ process_tcp(u_char * data, int skblen) 384 | 385 | a_tcp->server.state = TCP_ESTABLISHED; 386 | a_tcp->nids_state = NIDS_JUST_EST; 387 | +#ifdef ENABLE_TCPREASM 388 | +do_lurkers: 389 | +#ifdef ENABLE_TCPREASM_DEBUG 390 | + DEBUG_REASSEMBLY("notifying lurkers of new stream\n"); 391 | +#endif 392 | +#endif 393 | + 394 | for (i = tcp_procs; i; i = i->next) { 395 | char whatto = 0; 396 | char cc = a_tcp->client.collect; 397 | char sc = a_tcp->server.collect; 398 | char ccu = a_tcp->client.collect_urg; 399 | char scu = a_tcp->server.collect_urg; 400 | - 401 | + 402 | +#ifdef ENABLE_TCPREASM_DEBUG 403 | + DEBUG_REASSEMBLY(" %p, nids_state = %u\n", i, a_tcp->nids_state); 404 | +#endif 405 | + 406 | (i->item) (a_tcp, &data); 407 | if (cc < a_tcp->client.collect) 408 | whatto |= COLLECT_cc; 409 | @@ -885,6 +1107,10 @@ process_tcp(u_char * data, int skblen) 410 | } 411 | } 412 | if (!a_tcp->listeners) { 413 | +#ifdef ENABLE_TCPREASM_DEBUG 414 | + DEBUG_REASSEMBLY("no listeners, killing stream\n"); 415 | +#endif 416 | + 417 | nids_free_tcp_stream(a_tcp); 418 | return; 419 | } 420 | @@ -909,9 +1135,16 @@ process_tcp(u_char * data, int skblen) 421 | } 422 | } 423 | if (datalen + (this_tcphdr->th_flags & TH_FIN) > 0) 424 | + { 425 | +#ifdef ENABLE_TCPREASM_DEBUG 426 | + DEBUG_REASSEMBLY("calling tcp_queue, datalen = %u, data = %.*s...\n", 427 | + datalen, datalen > 10 ? 10 : datalen, (char *) (this_tcphdr) + 4 * this_tcphdr->th_off); 428 | +#endif 429 | + 430 | tcp_queue(a_tcp, this_tcphdr, snd, rcv, 431 | (char *) (this_tcphdr) + 4 * this_tcphdr->th_off, 432 | datalen, skblen); 433 | + } 434 | snd->window = ntohs(this_tcphdr->th_win); 435 | if (rcv->rmem_alloc > 65535) 436 | prune_queue(rcv, this_tcphdr); 437 | @@ -938,6 +1171,20 @@ nids_unregister_tcp(void (*x)) 438 | unregister_callback(&tcp_procs, x); 439 | } 440 | 441 | +#ifdef ENABLE_TCPREASM 442 | +void 443 | +nids_register_tcp_resume(void (*x)) 444 | +{ 445 | + register_callback(&tcp_resume_procs, x); 446 | +} 447 | + 448 | +void 449 | +nids_unregister_tcp_resume(void (*x)) 450 | +{ 451 | + unregister_callback(&tcp_resume_procs, x); 452 | +} 453 | +#endif 454 | + 455 | int 456 | tcp_init(int size) 457 | { 458 | -------------------------------------------------------------------------------- /src/libnids.3: -------------------------------------------------------------------------------- 1 | .\" libnids manpage by Dug Song 2 | .\" tmac.an sux, tmac.doc rules 3 | .TH LIBNIDS 3 4 | .SH NAME 5 | libnids \- network intrusion detection system E-box library 6 | .SH SYNOPSIS 7 | .nf 8 | #include 9 | 10 | extern struct nids_prm \fInids_params\fR; 11 | extern char \fInids_errbuf\fR[]; 12 | 13 | int 14 | \fBnids_init\fR(void); 15 | 16 | void 17 | \fBnids_register_ip_frag\fR(void (*ip_frag_func)(struct ip *pkt, int len)); 18 | 19 | void 20 | \fBnids_unregister_ip_frag\fR(void (*ip_frag_func)(struct ip *pkt, int len)); 21 | 22 | void 23 | \fBnids_register_ip\fR(void (*ip_func)(struct ip *pkt, int len)); 24 | 25 | void 26 | \fBnids_unregister_ip\fR(void (*ip_func)(struct ip *pkt, int len)); 27 | 28 | void 29 | \fBnids_register_udp\fR(void (*udp_func)(struct tuple4 *addr, u_char *data, int len, struct ip *pkt)); 30 | 31 | void 32 | \fBnids_unregister_udp\fR(void (*udp_func)(struct tuple4 *addr, u_char *data, int len, struct ip *pkt)); 33 | 34 | void 35 | \fBnids_register_tcp\fR(void (*tcp_func)(struct tcp_stream *ts, void **param)); 36 | 37 | void 38 | \fBnids_unregister_tcp\fR(void (*tcp_func)(struct tcp_stream *ts, void **param)); 39 | 40 | void 41 | \fBnids_killtcp\fR(struct tcp_stream *ts); 42 | 43 | void 44 | \fBnids_discard\fR(struct tcp_stream *ts, int numbytes); 45 | 46 | void 47 | \fBnids_run\fR(void); 48 | 49 | int 50 | \fBnids_dispatch\fR(int cnt); 51 | 52 | int 53 | \fBnids_next\fR(void); 54 | 55 | int 56 | \fBnids_getfd\fR(void); 57 | 58 | int 59 | \fBnids_register_chksum_ctl\fR(struct nids_chksum_ctl *, int); 60 | 61 | void 62 | \fBnids_pcap_handler\fR(u_char *par, struct pcap_pkthdr *hdr, u_char *data); 63 | 64 | struct tcp_stream * 65 | \fBnids_find_tcp_stream\fR(struct tuple4 *addr); 66 | 67 | .fi 68 | .SH DESCRIPTION 69 | .B libnids 70 | provides the functionality of a network intrusion detection system 71 | (NIDS) E-box component. It currently performs: 72 | .LP 73 | .nf 74 | 1. IP defragmentation 75 | 2. TCP stream reassembly 76 | 3. TCP port scan detection 77 | .fi 78 | .PP 79 | .B libnids 80 | performs TCP/IP reassembly in exactly the same way as Linux 81 | 2.0.36 kernels, and correctly handles all of the attacks implemented 82 | in fragrouter(8) (plus many other attacks as well). 83 | .SH ROUTINES 84 | .PP 85 | .BR nids_init () 86 | initializes the application for sniffing, based on the values set in the 87 | global variable \fInids_params\fR, declared as follows: 88 | .LP 89 | .nf 90 | struct nids_prm { 91 | int n_tcp_streams; 92 | int n_hosts; 93 | char *device; 94 | char *filename; 95 | int sk_buff_size; 96 | int dev_addon; 97 | void (*syslog)(int type, int err, struct ip *iph, void *data); 98 | int syslog_level; 99 | int scan_num_hosts; 100 | int scan_num_ports; 101 | int scan_delay; 102 | void (*no_mem)(void); 103 | int (*ip_filter)(struct ip *iph); 104 | char *pcap_filter; 105 | int promisc; 106 | int one_loop_less; 107 | int pcap_timeout; 108 | int multiproc; 109 | int queue_limit; 110 | int tcp_workarounds; 111 | pcap_t *pcap_desc; 112 | } nids_params; 113 | .fi 114 | .PP 115 | The members of this structure are: 116 | .TP 117 | .I n_tcp_streams 118 | Size of the hash table used for storing TCP connection information ( 119 | a maximum of 3/4 * \fIn_tcp_streams\fR TCP connections will be 120 | followed simultaneously). Default value: 1024 121 | .TP 122 | .I n_hosts 123 | Size of the hash table used for storing IP defragmentation 124 | information. Default value: 256 125 | .TP 126 | .I filename 127 | It this variable is set, libnids will call pcap_open_offline with this 128 | variable as the argument (instead of pcap_open_live()). Default value: NULL 129 | .TP 130 | .I device 131 | Interface to monitor. Default value: NULL (in which case an 132 | appropriate device is determined automatically). If this variable is assigned 133 | value \fBall\fR, libnids will attempt to capture packets on all interfaces 134 | (which works on Linux only) 135 | .TP 136 | .I sk_buff_size 137 | Size of \fIstruct sk_buff\fR (used for queuing packets), which should 138 | be set to match the value on the hosts being monitored. Default value: 168 139 | .TP 140 | .I dev_addon 141 | Number of bytes in \fIstruct sk_buff\fR reserved for link-layer 142 | information. Default value: -1 (in which case an appropriate offset if 143 | determined automatically based on link-layer type) 144 | .TP 145 | .I syslog 146 | Syslog callback function, used to report unusual conditions, such as 147 | port scan attempts, invalid TCP header flags, etc. Default value: 148 | \fInids_syslog\fR (which logs messages via syslog(3) without regard 149 | for message rate per second or free disk space) 150 | .TP 151 | .I syslog_level 152 | Log level used by \fInids_syslog\fR for reporting events via 153 | syslog(3). Default value: LOG_ALERT 154 | .TP 155 | .I scan_num_hosts 156 | Size of hash table used for storing portscan information (the maximum 157 | number portscans that will be detected simultaneously). If set to 0, 158 | portscan detection will be disabled. Default value: 256 159 | .TP 160 | .I scan_num_ports 161 | Minimum number of ports that must be scanned from the same source 162 | host before it is identifed as a portscan. Default value: 10 163 | .TP 164 | .I scan_delay 165 | Maximum delay (in milliseconds) between connections to different 166 | ports for them to be identified as part of a portscan. Default value: 167 | 3000 168 | .TP 169 | .I no_mem 170 | Out-of-memory callback function, used to terminate the calling process 171 | gracefully. 172 | .TP 173 | .I ip_filter 174 | IP filtering callback function, used to selectively discard IP 175 | packets, inspected after reassembly. If the function returns a 176 | non-zero value, the packet is processed; otherwise, it is 177 | discarded. Default value: \fInids_ip_filter\fR (which always returns 178 | 1) 179 | .TP 180 | .I pcap_filter 181 | pcap(3) filter string applied to the link-layer (raw, unassembled) 182 | packets. \fBNote\fR: filters like ``tcp dst port 23'' will NOT 183 | correctly handle appropriately fragmented traffic, e.g. 8-byte IP 184 | fragments; one should add "or (ip[6:2] & 0x1fff != 0)" at the end of the 185 | filter to process reassembled packets. Default value: NULL 186 | .TP 187 | .I promisc 188 | If non-zero, libnids will set the interface(s) it listens on to 189 | promiscuous mode. Default value: 1 190 | .TP 191 | .I one_loop_less 192 | Disabled by default; see comments in API.html file 193 | .TP 194 | .I pcap_timeout 195 | Sets the pcap read timeout, which may or may not be supported by your 196 | platform. Default value: 1024. 197 | .TP 198 | .I multiproc 199 | If nonzero, creates a separate thread for packets processing. See API.html. 200 | Default value: 0. 201 | .TP 202 | .I queue_limit 203 | If multiproc is nonzero, this is the maximum number of packets queued in the 204 | thread which reads packets from libpcap. Default value: 20000 205 | .TP 206 | .I tcp_workarounds 207 | Enables extra checks for faulty implementations of TCP such as the ones 208 | which allow connections to be closed despite the fact that there should be 209 | retransmissions for lost packets first (as stated by RFC 793, section 3.5). 210 | If non-zero, libnids will set the NIDS_TIMED_OUT state for savagely closed 211 | connections. Default value: 0 212 | .TP 213 | .I pcap_desc 214 | It this variable is set, libnids will call neither pcap_open_live nor 215 | pcap_open_offline, but will use a pre-opened PCAP descriptor; use this 216 | with nids_pcap_handler() in order to interactively feed packets to 217 | libnids. Default value: NULL 218 | .PP 219 | Returns 1 on success, 0 on failure (in which case \fBnids_errbuf\fR 220 | contains an appropriate error message). 221 | .PP 222 | .BR nids_register_ip_frag () 223 | registers a user-defined callback function to process all incoming IP 224 | packets (including IP fragments, packets with invalid checksums, etc.). 225 | .PP 226 | .BR nids_unregister_ip_frag () 227 | unregisters a user-defined callback function to process all incoming IP 228 | packets. 229 | .PP 230 | .BR nids_register_ip () 231 | registers a user-defined callback function to process IP packets 232 | validated and reassembled by \fBlibnids\fR. 233 | .PP 234 | .BR nids_unregister_ip () 235 | unregisters a user-defined callback function to process IP packets. 236 | .PP 237 | .BR nids_register_udp () 238 | registers a user-defined callback function to process UDP packets 239 | validated and reassembled by \fBlibnids\fR. 240 | .PP 241 | .BR nids_unregister_udp () 242 | unregisters a user-defined callback function to process UDP packets. 243 | .PP 244 | .BR nids_register_tcp () 245 | registers a user-defined callback function to process TCP streams 246 | validated and reassembled by \fBlibnids\fR. The \fItcp_stream\fR 247 | structure is defined as follows: 248 | .LP 249 | .nf 250 | struct tcp_stream { 251 | struct tuple4 { 252 | u_short source; 253 | u_short dest; 254 | u_int saddr; 255 | u_int daddr; 256 | } addr; 257 | char nids_state; 258 | struct half_stream { 259 | char state; 260 | char collect; 261 | char collect_urg; 262 | char *data; 263 | u_char urgdata; 264 | int count; 265 | int offset; 266 | int count_new; 267 | char count_new_urg; 268 | ... 269 | } client; 270 | struct half_stream server; 271 | ... 272 | void *user; 273 | }; 274 | .fi 275 | .PP 276 | The members of the \fItuple4\fR structure identify a unique TCP 277 | connection: 278 | .TP 279 | \fIsource\fR, \fIdest\fR 280 | Client and server port numbers 281 | .TP 282 | \fIsaddr\fR, \fIdaddr\fR 283 | Client and server IP addresses 284 | .PP 285 | The members of the \fIhalf_stream\fR structure describe each half of a 286 | TCP connection (client and server): 287 | .TP 288 | .I state 289 | Socket state (e.g. TCP_ESTABLISHED). 290 | .TP 291 | .I collect 292 | A boolean which specifies whether to collect data for this half of the 293 | connection in the \fIdata\fR buffer. 294 | .TP 295 | .I collect_urg 296 | A boolean which specifies whether to collect urgent data pointed to by 297 | the TCP urgent pointer for this half of the connection in the 298 | \fIurgdata\fR buffer. 299 | .TP 300 | .I data 301 | Buffer for normal data. 302 | .TP 303 | .I urgdata 304 | One-byte buffer for urgent data. 305 | .TP 306 | .I count 307 | The number of bytes appended to \fIdata\fR since the creation of the 308 | connection. 309 | .TP 310 | .I offset 311 | The current offset from the first byte stored in the \fIdata\fR 312 | buffer, identifying the start of newly received data. 313 | .TP 314 | .I count_new 315 | The number of bytes appended to \fIdata\fR since the last invocation 316 | of the TCP callback function (if 0, no new data arrived). 317 | .TP 318 | .I count_new_urg 319 | The number of bytes appended to \fIurgdata\fR since the last 320 | invocation of the TCP callback function (if 0, no new urgent data 321 | arrived). 322 | .PP 323 | The value of the \fInids_state\fR field provides information about the 324 | state of the TCP connection, to be used by the TCP callback function: 325 | .TP 326 | NIDS_JUST_EST 327 | Connection just established. Connection parameters in the \fIaddr\fR 328 | structure are available for inspection. If the connection is 329 | interesting, the TCP callback function may specify which data it 330 | wishes to receive in the future by setting non-zero values for the 331 | \fIcollect\fR or \fIcollect_urg\fR variables in the appropriate 332 | \fIclient\fR or \fIserver\fR half_stream structure members. 333 | .TP 334 | NIDS_DATA 335 | New data has arrived on a connection. The \fIhalf_stream\fR structures 336 | contain buffers of data. 337 | .TP 338 | NIDS_CLOSE, NIDS_RESET, NIDS_TIMED_OUT 339 | Connection has closed. The TCP callback function should free any 340 | resources it may have allocated for this connection. 341 | .PP 342 | The \fIparam\fR pointer passed by libnids as argument to the TCP callback 343 | function may be set to save a pointer to user-defined 344 | connection-specific data to pass to subsequent invocations of the TCP 345 | callback function (ex. the current working directory for an FTP 346 | control connection, etc.). 347 | .PP 348 | The \fIuser\fR pointer in the tcp_stream structure has the same purpose 349 | except it is global to the stream, whereas the \fIparam\fR pointer is 350 | different from one callback function to the other even though they were 351 | called for the same stream. 352 | .PP 353 | .BR nids_unregister_tcp () 354 | unregisters a user-defined callback function to process TCP streams. 355 | .PP 356 | .BR nids_killtcp () 357 | tears down the specified TCP connection with symmetric RST packets 358 | between client and server. 359 | .PP 360 | .BR nids_discard () 361 | may be called from the TCP callback function to specify the number of 362 | bytes to discard from the beginning of the \fIdata\fR buffer (updating 363 | the \fIoffset\fR value accordingly) after the TCP callback function 364 | exits. Otherwise, the new data (totalling \fIcount_new\fR bytes) will 365 | be discarded by default. 366 | .PP 367 | .BR nids_run () 368 | starts the packet-driven application, reading packets in an endless 369 | loop, and invoking registered callback functions to handle new data as 370 | it arrives. This function does not return. 371 | .PP 372 | .BR nids_dispatch () 373 | attempts to process \fBcnt\fR packets before returning, with a cnt of -1 374 | understood as all packets available in one pcap buffer, or all packets in 375 | a file when reading offline. On success, returns the count of packets 376 | processed, which may be zero upon EOF (offline read) or upon hitting 377 | \fIpcap_timeout\fR (if supported by your platform). On failure, returns 378 | -1, putting an appropriate error message in \fBnids_errbuf\fR. 379 | .PP 380 | .BR nids_next () 381 | process the next available packet before returning. Returns 1 on success, 382 | 0 if no packet was processed, setting \fBnids_effbuf\fR appropriately if 383 | an error prevented packet processing. 384 | .PP 385 | .BR nids_getfd () 386 | may be used by an application sleeping in select(2) to snoop for a 387 | socket file descriptor present in the read fd_set. Returns the file 388 | descriptor on success, -1 on failure (in which case \fBnids_errbuf\fR 389 | contains an appropriate error message). 390 | .PP 391 | .BR nids_register_chksum_ctl () 392 | takes as arguments an array of \fIstruct nids_chksum_ctl\fR elements and 393 | the number of elements in the array. A \fInids_chksum_ctl\fR element is 394 | defined as follows: 395 | .LP 396 | .nf 397 | struct nids_chksum_ctl { 398 | u_int netaddr; 399 | u_int mask; 400 | u_int action; 401 | /* private members */ 402 | }; 403 | .fi 404 | .PP 405 | Internal checksumming functions will first check elements of this array one 406 | by one, and if the source ip SRCIP of the current packet satisfies condition 407 | 408 | (SRCIP&chksum_ctl_array[i].mask)==chksum_ctl_array[i].netaddr 409 | 410 | then if the \fIaction\fR field is \fBNIDS_DO_CHKSUM\fR, the packet will be 411 | checksummed; if the \fIaction\fR field is \fBNIDS_DONT_CHKSUM\fR, the packet 412 | will not be checksummed. If the packet matches none of the array elements, 413 | the default action is to perform checksumming. 414 | .PP 415 | .BR nids_pcap_handler () 416 | may be used by an application already running a capture with libpcap, in order 417 | to pass frames to libnids interactively (frame per frame) instead of having 418 | libnids itself do the capture. 419 | .PP 420 | .BR nids_find_tcp_stream () 421 | returns a pointer to the tcp_stream structure corresponding to the tuple 422 | passed as argument if libnids knows about this TCP connection already, 423 | otherwise it returns NULL. 424 | .PP 425 | .BR nids_free_tcp_stream () 426 | removes the given tcp_stream from the list of streams tracked by libnids. 427 | Warning: its usage can result in crashes! See comments in the API.html file. 428 | 429 | .SH SEE ALSO 430 | pcap(3), libnet(3), fragrouter(8) 431 | .SH AUTHOR 432 | Rafal Wojtczuk 433 | .PP 434 | Manpage by Dug Song , minor updates by Michael Pomraning 435 | 436 | 437 | -------------------------------------------------------------------------------- /src/libnids.3.mdoc: -------------------------------------------------------------------------------- 1 | .\" libnids manpage by Dug Song 2 | .Dd Dec 21, 1999 3 | .Dt PCAP 3 4 | .Os 5 | .Sh NAME 6 | .Nm libnids 7 | .Nd network intrusion detection system E-box library 8 | .Sh SYNOPSIS 9 | .Fd #include 10 | .Pp 11 | .Dv extern struct nids_prm nids_params; 12 | .Lp 13 | .Dv extern char *nids_warnings[]; 14 | .Lp 15 | .Dv extern char nids_errbuf[]; 16 | .Ft int 17 | .Fn nids_init "void" 18 | .Ft void 19 | .Fn nids_register_ip_frag "void (*ip_frag_func)(struct ip *pkt)" 20 | .Ft void 21 | .Fn nids_register_ip "void (*ip_func)(struct ip *pkt)" 22 | .Ft void 23 | .Fn nids_register_udp "void (*udp_func)(struct tuple4 *addr, u_char *data, int len, struct ip *pkt))" 24 | .Ft void 25 | .Fn nids_register_tcp "void (*tcp_func)(struct tcp_stream *ts, void **param)" 26 | .Ft void 27 | .Fn nids_killtcp "struct tcp_stream *ts" 28 | .Ft void 29 | .Fn nids_discard "struct tcp_stream *ts, int numbytes" 30 | .Ft void 31 | .Fn nids_run "void" 32 | .Ft int 33 | .Fn nids_dispatch "int" 34 | .Ft int 35 | .Fn nids_next "void" 36 | .Ft int 37 | .Fn nids_getfd "void" 38 | .Ft void 39 | .Fn nids_register_chksum_ctl "struct nids_chksum_ctl *, int" 40 | 41 | 42 | .Sh DESCRIPTION 43 | .Nm 44 | provides the functionality of a network intrusion detection system 45 | (NIDS) E-box component. It currently performs 46 | .Lp 47 | .Bl -enum -offset indent -compact 48 | .It 49 | IP defragmentation 50 | .It 51 | TCP stream reassembly 52 | .It 53 | TCP port scan detection 54 | .El 55 | .Lp 56 | .Nm 57 | performs TCP/IP reassembly in exactly the same way as Linux 58 | 2.0.36 kernels, and correctly handles all of the attacks implemented 59 | in 60 | .Xr fragrouter 8 61 | (plus many other attacks as well). 62 | .Sh ROUTINES 63 | 64 | .Fn nids_init 65 | initializes the application for sniffing, based on the values set in the 66 | global variable 67 | .Va nids_params , 68 | declared as follows: 69 | .Bd -literal 70 | struct nids_prm { 71 | int n_tcp_streams; 72 | int n_hosts; 73 | char *device; 74 | int sk_buff_size; 75 | int dev_addon; 76 | void (*syslog)(int type, int err, struct ip *iph, void *data); 77 | int syslog_level; 78 | int scan_num_hosts; 79 | int scan_num_ports; 80 | int scan_delay; 81 | void (*no_mem)(void); 82 | int (*ip_filter)(struct ip *iph); 83 | char *pcap_filter; 84 | int pcap_timeout; 85 | } nids_params; 86 | .Ed 87 | .Pp 88 | The members of this structure are: 89 | .Bl -tag -width scan_num_hosts 90 | .It Fa n_tcp_streams 91 | Size of the hash table used for storing TCP connection information ( 92 | a maximum of 3/4 * 93 | .Fa n_tcp_streams 94 | TCP connections will be followed simultaneously). Default value: 1024 95 | .It Fa n_hosts 96 | Size of the hash table used for storing IP defragmentation 97 | information. Default value: 256 98 | .It Fa filename 99 | It this variable is set, libnids will call pcap_open_offline with this 100 | variable as the argument (instead of pcap_open_live()). Default value: NULL 101 | .It Fa device 102 | Interface to monitor. Default value: 103 | .Dv NULL 104 | (in which case an appropriate device is determined automatically). If this 105 | variable is assigned value 106 | .Nm all, libnids will attempt to capture packets on all interfaces (which 107 | works on Linux only) 108 | .It Fa sk_buff_size 109 | Size of 110 | .Fa struct sk_buff 111 | (used for queuing packets), which should be set to match the value on 112 | the hosts being monitored. Default value: 168 113 | .It Fa dev_addon 114 | Number of bytes in 115 | .Fa struct sk_buff 116 | reserved for link-layer information. Default value: -1 (in which case 117 | an appropriate offset if determined automatically based on link-layer 118 | type) 119 | .It Fa syslog 120 | Syslog callback function, used to report unusual conditions, such as 121 | port scan attempts, invalid TCP header flags, etc. Default value: 122 | .Fa nids_syslog 123 | (which logs messages via 124 | .Xr syslog 3 125 | without regard for message rate per second or free disk space) 126 | .It Fa syslog_level 127 | Log level used by 128 | .Fa nids_syslog 129 | for reporting events via 130 | .Xr syslog 3 . 131 | Default value: 132 | .Dv LOG_ALERT 133 | .It Fa scan_num_hosts 134 | Size of hash table used for storing portscan information (the maximum 135 | number portscans that will be detected simultaneously). If set to 0, 136 | portscan detection will be disabled. Default value: 256 137 | .It Fa scan_num_ports 138 | Minimum number of ports that must be scanned from the same source 139 | host before it is identifed as a portscan. Default value: 10 140 | .It Fa scan_delay 141 | Maximum delay (in milliseconds) between connections to different 142 | ports for them to be identified as part of a portscan. Default value: 143 | 3000 144 | .It Fa no_mem 145 | Out-of-memory callback function, used to terminate the calling process 146 | gracefully. 147 | .It Fa ip_filter 148 | IP filtering callback function, used to selectively discard 149 | IP packets, inspected after reassembly. If 150 | the function returns a non-zero value, the packet is processed; 151 | otherwise, it is discarded. Default value: 152 | .Fn nids_ip_filter 153 | (which always returns 1) 154 | .It Fa pcap_filter 155 | .Xr pcap 3 156 | filter string applied to the link-layer (raw, unassembled) packets. 157 | .Sy Note: 158 | filters like ``tcp dst port 23'' will NOT correctly handle 159 | appropriately fragmented traffic, e.g. 8-byte IP fragments. One should add 160 | "or (ip[6:2] & 0x1fff != 0)" at the end of the filter to process reassembled 161 | packets. Default value: 162 | .Dv NULL 163 | .lt Fa promisc 164 | If non-zero, libnids will set the interface(s) it listens on to 165 | promiscuous mode. Default value: 1 166 | .It Fa one_loop_less 167 | Disabled by default see comments in API.html file 168 | .It Fa pcap_timeout 169 | Sets the pcap read timeout, which may or may not be supported by your 170 | platform. Default value: 1024. 171 | .El 172 | .Pp 173 | Returns 1 on success, 0 on failure (in which case 174 | .Va nids_errbuf 175 | contains an appropriate error message). 176 | .Pp 177 | .Fn nids_register_ip_frag 178 | registers a user-defined callback function to process all incoming IP 179 | packets (including IP fragments, packets with invalid checksums, etc.). 180 | .Pp 181 | .Fn nids_register_ip 182 | registers a user-defined callback function to process IP packets 183 | validated and reassembled by 184 | .Nm libnids . 185 | .Pp 186 | .Fn nids_register_udp 187 | registers a user-defined callback function to process UDP packets 188 | validated and reassembled by 189 | .Nm libnids . 190 | .Pp 191 | .Fn nids_register_tcp 192 | registers a user-defined callback function to process TCP streams 193 | validated and reassembled by 194 | .Nm libnids . 195 | The 196 | .Va tcp_stream 197 | structure is defined as follows: 198 | .Bd -literal 199 | struct tcp_stream { 200 | struct tuple4 { 201 | u_short source; 202 | u_short dest; 203 | u_int saddr; 204 | u_int daddr; 205 | } addr; 206 | char nids_state; 207 | struct half_stream { 208 | char state; 209 | char collect; 210 | char collect_urg; 211 | char *data; 212 | u_char urgdata; 213 | int count; 214 | int offset; 215 | int count_new; 216 | char count_new_urg; 217 | ... 218 | } client; 219 | struct half_stream server; 220 | ... 221 | }; 222 | .Ed 223 | .Pp 224 | The members of the 225 | .Va tuple4 226 | structure identify a unique TCP connection: 227 | .Bl -tag -width source_,_dest 228 | .It Fa source , dest 229 | Client and server port numbers 230 | .It Fa saddr , daddr 231 | Client and server IP addresses 232 | .El 233 | .Pp 234 | The members of the 235 | .Va half_stream 236 | structure describe each half of a TCP connection (client and server): 237 | .Bl -tag -width count_new_urg 238 | .It Fa state 239 | Socket state (e.g. 240 | .Dv TCP_ESTABLISHED 241 | ). 242 | .It Fa collect 243 | A boolean which specifies whether to collect data for this half of the 244 | connection in the 245 | .Va data 246 | buffer. 247 | .It Fa collect_urg 248 | A boolean which specifies whether to collect urgent data pointed to by 249 | the TCP urgent pointer for this half of the connection in the 250 | .Va urgdata 251 | buffer. 252 | .It Fa data 253 | Buffer for normal data. 254 | .It Fa urgdata 255 | One-byte buffer for urgent data. 256 | .It Fa count 257 | The number of bytes appended to 258 | .Va data 259 | since the creation of the connection. 260 | .It Fa offset 261 | The current offset from the first byte stored in the 262 | .Va data 263 | buffer, identifying the start of newly received data. 264 | .It Fa count_new 265 | The number of bytes appended to 266 | .Va data 267 | since the last invocation of the TCP callback function (if 0, no new 268 | data arrived). 269 | .It Fa count_new_urg 270 | The number of bytes appended to 271 | .Va urgdata 272 | since the last invocation of the TCP callback function (if 0, no new 273 | urgent data arrived). 274 | .El 275 | .Pp 276 | The 277 | .Va nids_state 278 | field provides information about the state of the TCP connection, to 279 | be used by the TCP callback function: 280 | .Bl -tag -width NIDS_TIMED_OUT 281 | .It Dv NIDS_JUST_EST 282 | Connection just established. Connection parameters in the 283 | .Va addr 284 | structure are available for inspection. If the connection is 285 | interesting, the TCP callback function may specify which data it 286 | wishes to receive in the future by setting non-zero values for the 287 | .Va collect 288 | or 289 | .Va collect_urg 290 | variables in the appropriate 291 | .Va client 292 | or 293 | .Va server half_stream 294 | structure members. 295 | .It Dv NIDS_DATA 296 | New data has arrived on a connection. The 297 | .Va half_stream 298 | structures contain buffers of data. 299 | .It Dv NIDS_CLOSE , NIDS_RESET , NIDS_TIMED_OUT 300 | Connection has closed. The TCP callback function should free any 301 | resources it may have allocated for this connection. 302 | .El 303 | .Pp 304 | The 305 | .Va param 306 | pointer may be set to save a pointer to user-defined 307 | connection-specific data to pass to subsequent invocations of the TCP 308 | callback function (ex. the current working directory for an FTP 309 | control connection, etc.). 310 | .Pp 311 | .Fn nids_killtcp 312 | tears down the specified TCP connection with symmetric 313 | .Dv RST 314 | packets between client and server. 315 | .Pp 316 | .Fn nids_discard 317 | may be called from the TCP callback function to specify the number of 318 | bytes to discard from the beginning of the 319 | .Va data 320 | buffer (updating the 321 | .Va offset 322 | value accordingly) after the TCP callback function exists. Otherwise, 323 | the new data (totalling 324 | .Va count_new 325 | bytes) will be discarded by default. 326 | .Pp 327 | .Fn nids_run 328 | starts the packet-driven application, reading packets in an endless 329 | loop, and invoking registered callback functions to handle new data as 330 | it arrives. This function does not return. 331 | .Pp 332 | .Fn nids_dispatch 333 | attempts to process 334 | .Va cnt 335 | packets before returning, with a cnt of -1 understood as all packets 336 | available in one pcap buffer, or all packets in a file when reading 337 | offline. On success, returns the count of packets processed, which may 338 | be zero upon EOF (offline read) or upon hitting 339 | .Va pcap_timeout 340 | (if supported by your platform). On failure, returns -1, putting an 341 | appropriate error message in 342 | .Va nids_errbuf . 343 | .Pp 344 | .Fn nids_next 345 | process the next available packet before returning. Returns 1 on success, 346 | 0 if no packet was processed, setting 347 | .Va nids_effbuf 348 | appropriately if an error prevented packet processing. 349 | .Pp 350 | .Fn nids_getfd 351 | may be used by an application sleeping in 352 | .Xr select 2 353 | to snoop for a socket file descriptor present in the read 354 | .Dv fd_set . 355 | Returns the file descriptor on success, -1 on failure (in which case 356 | .Va nids_errbuf 357 | contains an appropriate error message). 358 | .Pp 359 | .Fn nids_register_chksum_ctl 360 | takes as arguments an array of 361 | .Va struct nids_chksum_ctl 362 | elements and the number of elements in the array. A 363 | .Va nids_chksum_ctl 364 | element is defined as follows: 365 | .Bd -literal 366 | struct nids_chksum_ctl { 367 | u_int netaddr; 368 | u_int mask; 369 | u_int action; 370 | /* private members */ 371 | }; 372 | .Ed 373 | .Pp 374 | Internal checksumming functions will first check elements of this array one 375 | by one, and if the source ip SRCIP of the current packet satisfies condition 376 | 377 | (SRCIP&chksum_ctl_array[i].mask)==chksum_ctl_array[i].netaddr 378 | 379 | then if the 380 | .Va action 381 | field is NIDS_DO_CHKSUM, the packet will be checksummed; if the 382 | .Va action 383 | field is NIDS_DONT_CHKSUM, the packet will not be checksummed. If the packet 384 | matches none of the array elements, the default action is to perform 385 | checksumming. 386 | 387 | .Sh SEE ALSO 388 | .Xr pcap 3 , 389 | .Xr libnet 3 , 390 | .Xr fragrouter 8 391 | .Sh AUTHOR 392 | Rafal Wojtczuk 393 | .Pp 394 | Manpage by Dug Song , minor updates by Michael Pomraning 395 | 396 | 397 | -------------------------------------------------------------------------------- /src/libnids.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 1999 Rafal Wojtczuk . All rights reserved. 3 | See the file COPYING for license details. 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #if (HAVE_UNISTD_H) 22 | #include 23 | #endif 24 | #include 25 | #include "checksum.h" 26 | #include "ip_fragment.h" 27 | #include "scan.h" 28 | #include "tcp.h" 29 | #include "util.h" 30 | #include "nids.h" 31 | #ifdef HAVE_LIBGTHREAD_2_0 32 | #include 33 | #endif 34 | 35 | #ifdef __linux__ 36 | extern int set_all_promisc(); 37 | #endif 38 | 39 | #define int_ntoa(x) inet_ntoa(*((struct in_addr *)&x)) 40 | extern int ip_options_compile(unsigned char *); 41 | extern int raw_init(); 42 | static void nids_syslog(int, int, struct ip *, void *); 43 | static int nids_ip_filter(struct ip *, int); 44 | 45 | static struct proc_node *ip_frag_procs; 46 | static struct proc_node *ip_procs; 47 | static struct proc_node *udp_procs; 48 | 49 | struct proc_node *tcp_procs; 50 | static int linktype; 51 | static pcap_t *desc = NULL; 52 | 53 | #ifdef HAVE_LIBGTHREAD_2_0 54 | 55 | /* async queue for multiprocessing - mcree */ 56 | static GAsyncQueue *cap_queue; 57 | 58 | /* items in the queue */ 59 | struct cap_queue_item { 60 | void *data; 61 | bpf_u_int32 caplen; 62 | }; 63 | 64 | /* marks end of queue */ 65 | static struct cap_queue_item EOF_item; 66 | 67 | /* error buffer for glib calls */ 68 | static GError *gerror = NULL; 69 | 70 | #endif 71 | 72 | char nids_errbuf[PCAP_ERRBUF_SIZE]; 73 | struct pcap_pkthdr * nids_last_pcap_header = NULL; 74 | u_char *nids_last_pcap_data = NULL; 75 | u_int nids_linkoffset = 0; 76 | 77 | char *nids_warnings[] = { 78 | "Murphy - you never should see this message !", 79 | "Oversized IP packet", 80 | "Invalid IP fragment list: fragment over size", 81 | "Overlapping IP fragments", 82 | "Invalid IP header", 83 | "Source routed IP frame", 84 | "Max number of TCP streams reached", 85 | "Invalid TCP header", 86 | "Too much data in TCP receive queue", 87 | "Invalid TCP flags" 88 | }; 89 | 90 | struct nids_prm nids_params = { 91 | 1040, /* n_tcp_streams */ 92 | 256, /* n_hosts */ 93 | NULL, /* device */ 94 | NULL, /* filename */ 95 | 168, /* sk_buff_size */ 96 | -1, /* dev_addon */ 97 | nids_syslog, /* syslog() */ 98 | LOG_ALERT, /* syslog_level */ 99 | 256, /* scan_num_hosts */ 100 | 3000, /* scan_delay */ 101 | 10, /* scan_num_ports */ 102 | nids_no_mem, /* no_mem() */ 103 | nids_ip_filter, /* ip_filter() */ 104 | NULL, /* pcap_filter */ 105 | 1, /* promisc */ 106 | 0, /* one_loop_less */ 107 | 1024, /* pcap_timeout */ 108 | 0, /* multiproc */ 109 | 20000, /* queue_limit */ 110 | 0, /* tcp_workarounds */ 111 | NULL, /* pcap_desc */ 112 | 3600 /* tcp_flow_timeout */ 113 | }; 114 | 115 | static int nids_ip_filter(struct ip *x, int len) 116 | { 117 | (void)x; 118 | (void)len; 119 | return 1; 120 | } 121 | 122 | static void nids_syslog(int type, int errnum, struct ip *iph, void *data) 123 | { 124 | char saddr[20], daddr[20]; 125 | char buf[1024]; 126 | struct host *this_host; 127 | unsigned char flagsand = 255, flagsor = 0; 128 | int i; 129 | 130 | switch (type) { 131 | 132 | case NIDS_WARN_IP: 133 | if (errnum != NIDS_WARN_IP_HDR) { 134 | strcpy(saddr, int_ntoa(iph->ip_src.s_addr)); 135 | strcpy(daddr, int_ntoa(iph->ip_dst.s_addr)); 136 | syslog(nids_params.syslog_level, 137 | "%s, packet (apparently) from %s to %s\n", 138 | nids_warnings[errnum], saddr, daddr); 139 | } else 140 | syslog(nids_params.syslog_level, "%s\n", 141 | nids_warnings[errnum]); 142 | break; 143 | 144 | case NIDS_WARN_TCP: 145 | strcpy(saddr, int_ntoa(iph->ip_src.s_addr)); 146 | strcpy(daddr, int_ntoa(iph->ip_dst.s_addr)); 147 | if (errnum != NIDS_WARN_TCP_HDR) 148 | syslog(nids_params.syslog_level, 149 | "%s,from %s:%hu to %s:%hu\n", nids_warnings[errnum], 150 | saddr, ntohs(((struct tcphdr *) data)->th_sport), daddr, 151 | ntohs(((struct tcphdr *) data)->th_dport)); 152 | else 153 | syslog(nids_params.syslog_level, "%s,from %s to %s\n", 154 | nids_warnings[errnum], saddr, daddr); 155 | break; 156 | 157 | case NIDS_WARN_SCAN: 158 | this_host = (struct host *) data; 159 | sprintf(buf, "Scan from %s. Scanned ports: ", 160 | int_ntoa(this_host->addr)); 161 | for (i = 0; i < this_host->n_packets; i++) { 162 | strcat(buf, int_ntoa(this_host->packets[i].addr)); 163 | sprintf(buf + strlen(buf), ":%hu,", 164 | this_host->packets[i].port); 165 | flagsand &= this_host->packets[i].flags; 166 | flagsor |= this_host->packets[i].flags; 167 | } 168 | if (flagsand == flagsor) { 169 | i = flagsand; 170 | switch (flagsand) { 171 | case 2: 172 | strcat(buf, "scan type: SYN"); 173 | break; 174 | case 0: 175 | strcat(buf, "scan type: NULL"); 176 | break; 177 | case 1: 178 | strcat(buf, "scan type: FIN"); 179 | break; 180 | default: 181 | sprintf(buf + strlen(buf), "flags=0x%x", i); 182 | } 183 | } else 184 | strcat(buf, "various flags"); 185 | syslog(nids_params.syslog_level, "%s", buf); 186 | break; 187 | 188 | default: 189 | syslog(nids_params.syslog_level, "Unknown warning number ?\n"); 190 | } 191 | } 192 | 193 | /* called either directly from pcap_hand() or from cap_queue_process_thread() 194 | * depending on the value of nids_params.multiproc - mcree 195 | */ 196 | static void call_ip_frag_procs(void *data,bpf_u_int32 caplen) 197 | { 198 | struct proc_node *i; 199 | for (i = ip_frag_procs; i; i = i->next) 200 | (i->item) (data, caplen); 201 | } 202 | 203 | 204 | /* wireless frame types, mostly from tcpdump (wam) */ 205 | #define FC_TYPE(fc) (((fc) >> 2) & 0x3) 206 | #define FC_SUBTYPE(fc) (((fc) >> 4) & 0xF) 207 | #define DATA_FRAME_IS_QOS(x) ((x) & 0x08) 208 | #define FC_WEP(fc) ((fc) & 0x4000) 209 | #define FC_TO_DS(fc) ((fc) & 0x0100) 210 | #define FC_FROM_DS(fc) ((fc) & 0x0200) 211 | #define T_MGMT 0x0 /* management */ 212 | #define T_CTRL 0x1 /* control */ 213 | #define T_DATA 0x2 /* data */ 214 | #define T_RESV 0x3 /* reserved */ 215 | #define EXTRACT_LE_16BITS(p) \ 216 | ((unsigned short)*((const unsigned char *)(p) + 1) << 8 | \ 217 | (unsigned short)*((const unsigned char *)(p) + 0)) 218 | #define EXTRACT_16BITS(p) ((unsigned short)ntohs(*(const unsigned short *)(p))) 219 | #define LLC_FRAME_SIZE 8 220 | #define LLC_OFFSET_TO_TYPE_FIELD 6 221 | #define ETHERTYPE_IP 0x0800 222 | 223 | void nids_pcap_handler(u_char * par, struct pcap_pkthdr *hdr, u_char * data) 224 | { 225 | u_char *data_aligned; 226 | #ifdef HAVE_LIBGTHREAD_2_0 227 | struct cap_queue_item *qitem; 228 | #endif 229 | #ifdef DLT_IEEE802_11 230 | unsigned short fc; 231 | int linkoffset_tweaked_by_prism_code = 0; 232 | int linkoffset_tweaked_by_radio_code = 0; 233 | #endif 234 | 235 | /* 236 | * Check for savagely closed TCP connections. Might 237 | * happen only when nids_params.tcp_workarounds is non-zero; 238 | * otherwise nids_tcp_timeouts is always NULL. 239 | */ 240 | if (NULL != nids_tcp_timeouts) 241 | tcp_check_timeouts(&hdr->ts); 242 | 243 | nids_last_pcap_header = hdr; 244 | nids_last_pcap_data = data; 245 | (void)par; /* warnings... */ 246 | switch (linktype) { 247 | case DLT_EN10MB: 248 | if (hdr->caplen < 14) 249 | return; 250 | /* Only handle IP packets and 802.1Q VLAN tagged packets below. */ 251 | if (data[12] == 8 && data[13] == 0) { 252 | /* Regular ethernet */ 253 | nids_linkoffset = 14; 254 | } else if (data[12] == 0x81 && data[13] == 0) { 255 | /* Skip 802.1Q VLAN and priority information */ 256 | nids_linkoffset = 18; 257 | } else 258 | /* non-ip frame */ 259 | return; 260 | break; 261 | #ifdef DLT_PRISM_HEADER 262 | #ifndef DLT_IEEE802_11 263 | #error DLT_PRISM_HEADER is defined, but DLT_IEEE802_11 is not ??? 264 | #endif 265 | case DLT_PRISM_HEADER: 266 | nids_linkoffset = 144; //sizeof(prism2_hdr); 267 | linkoffset_tweaked_by_prism_code = 1; 268 | //now let DLT_IEEE802_11 do the rest 269 | #endif 270 | #ifdef DLT_IEEE802_11_RADIO 271 | case DLT_IEEE802_11_RADIO: 272 | // just get rid of the radio tap header 273 | if (!linkoffset_tweaked_by_prism_code) { 274 | nids_linkoffset = EXTRACT_LE_16BITS(data + 2); // skip radiotap header 275 | linkoffset_tweaked_by_radio_code = 1; 276 | } 277 | //now let DLT_IEEE802_11 do the rest 278 | #endif 279 | #ifdef DLT_IEEE802_11 280 | case DLT_IEEE802_11: 281 | /* I don't know why frame control is always little endian, but it 282 | * works for tcpdump, so who am I to complain? (wam) 283 | */ 284 | if (!linkoffset_tweaked_by_prism_code && !linkoffset_tweaked_by_radio_code) 285 | nids_linkoffset = 0; 286 | fc = EXTRACT_LE_16BITS(data + nids_linkoffset); 287 | if (FC_TYPE(fc) != T_DATA || FC_WEP(fc)) { 288 | return; 289 | } 290 | if (FC_TO_DS(fc) && FC_FROM_DS(fc)) { 291 | /* a wireless distribution system packet will have another 292 | * MAC addr in the frame 293 | */ 294 | nids_linkoffset += 30; 295 | } else { 296 | nids_linkoffset += 24; 297 | } 298 | if (DATA_FRAME_IS_QOS(FC_SUBTYPE(fc))) 299 | nids_linkoffset += 2; 300 | if (hdr->len < nids_linkoffset + LLC_FRAME_SIZE) 301 | return; 302 | if (ETHERTYPE_IP != 303 | EXTRACT_16BITS(data + nids_linkoffset + LLC_OFFSET_TO_TYPE_FIELD)) { 304 | /* EAP, LEAP, and other 802.11 enhancements can be 305 | * encapsulated within a data packet too. Look only at 306 | * encapsulated IP packets (Type field of the LLC frame). 307 | */ 308 | return; 309 | } 310 | nids_linkoffset += LLC_FRAME_SIZE; 311 | break; 312 | #endif 313 | default:; 314 | } 315 | if (hdr->caplen < nids_linkoffset) 316 | return; 317 | 318 | /* 319 | * sure, memcpy costs. But many EXTRACT_{SHORT, LONG} macros cost, too. 320 | * Anyway, libpcap tries to ensure proper layer 3 alignment (look for 321 | * handle->offset in pcap sources), so memcpy should not be called. 322 | */ 323 | #ifdef LBL_ALIGN 324 | if ((unsigned long) (data + nids_linkoffset) & 0x3) { 325 | data_aligned = alloca(hdr->caplen - nids_linkoffset + 4); 326 | data_aligned -= (unsigned long) data_aligned % 4; 327 | memcpy(data_aligned, data + nids_linkoffset, hdr->caplen - nids_linkoffset); 328 | } else 329 | #endif 330 | data_aligned = data + nids_linkoffset; 331 | 332 | #ifdef HAVE_LIBGTHREAD_2_0 333 | if(nids_params.multiproc) { 334 | /* 335 | * Insert received fragment into the async capture queue. 336 | * We hope that the overhead of memcpy 337 | * will be saturated by the benefits of SMP - mcree 338 | */ 339 | qitem=malloc(sizeof(struct cap_queue_item)); 340 | if (qitem && (qitem->data=malloc(hdr->caplen - nids_linkoffset))) { 341 | qitem->caplen=hdr->caplen - nids_linkoffset; 342 | memcpy(qitem->data,data_aligned,qitem->caplen); 343 | g_async_queue_lock(cap_queue); 344 | /* ensure queue does not overflow */ 345 | if(g_async_queue_length_unlocked(cap_queue) > nids_params.queue_limit) { 346 | /* queue limit reached: drop packet - should we notify user via syslog? */ 347 | free(qitem->data); 348 | free(qitem); 349 | } else { 350 | /* insert packet to queue */ 351 | g_async_queue_push_unlocked(cap_queue,qitem); 352 | } 353 | g_async_queue_unlock(cap_queue); 354 | } 355 | } else { /* user requested simple passthru - no threading */ 356 | call_ip_frag_procs(data_aligned,hdr->caplen - nids_linkoffset); 357 | } 358 | #else 359 | call_ip_frag_procs(data_aligned,hdr->caplen - nids_linkoffset); 360 | #endif 361 | } 362 | 363 | static void gen_ip_frag_proc(u_char * data, int len) 364 | { 365 | struct proc_node *i; 366 | struct ip *iph = (struct ip *) data; 367 | int need_free = 0; 368 | int skblen; 369 | void (*glibc_syslog_h_workaround)(int, int, struct ip *, void*)= 370 | nids_params.syslog; 371 | 372 | if (!nids_params.ip_filter(iph, len)) 373 | return; 374 | 375 | if (len < (int)sizeof(struct ip) || iph->ip_hl < 5 || iph->ip_v != 4 || 376 | ip_fast_csum((unsigned char *) iph, iph->ip_hl) != 0 || 377 | len < ntohs(iph->ip_len) || ntohs(iph->ip_len) < iph->ip_hl << 2) { 378 | glibc_syslog_h_workaround(NIDS_WARN_IP, NIDS_WARN_IP_HDR, iph, 0); 379 | return; 380 | } 381 | if (iph->ip_hl > 5 && ip_options_compile((unsigned char *)data)) { 382 | glibc_syslog_h_workaround(NIDS_WARN_IP, NIDS_WARN_IP_SRR, iph, 0); 383 | return; 384 | } 385 | switch (ip_defrag_stub((struct ip *) data, &iph)) { 386 | case IPF_ISF: 387 | return; 388 | case IPF_NOTF: 389 | need_free = 0; 390 | iph = (struct ip *) data; 391 | break; 392 | case IPF_NEW: 393 | need_free = 1; 394 | break; 395 | default:; 396 | } 397 | skblen = ntohs(iph->ip_len) + 16; 398 | if (!need_free) 399 | skblen += nids_params.dev_addon; 400 | skblen = (skblen + 15) & ~15; 401 | skblen += nids_params.sk_buff_size; 402 | 403 | for (i = ip_procs; i; i = i->next) 404 | (i->item) (iph, skblen); 405 | if (need_free) 406 | free(iph); 407 | } 408 | 409 | #if HAVE_BSD_UDPHDR 410 | #define UH_ULEN uh_ulen 411 | #define UH_SPORT uh_sport 412 | #define UH_DPORT uh_dport 413 | #else 414 | #define UH_ULEN len 415 | #define UH_SPORT source 416 | #define UH_DPORT dest 417 | #endif 418 | 419 | static void process_udp(char *data) 420 | { 421 | struct proc_node *ipp = udp_procs; 422 | struct ip *iph = (struct ip *) data; 423 | struct udphdr *udph; 424 | struct tuple4 addr; 425 | int hlen = iph->ip_hl << 2; 426 | int len = ntohs(iph->ip_len); 427 | int ulen; 428 | if (len - hlen < (int)sizeof(struct udphdr)) 429 | return; 430 | udph = (struct udphdr *) (data + hlen); 431 | ulen = ntohs(udph->UH_ULEN); 432 | if (len - hlen < ulen || ulen < (int)sizeof(struct udphdr)) 433 | return; 434 | /* According to RFC768 a checksum of 0 is not an error (Sebastien Raveau) */ 435 | if (udph->uh_sum && my_udp_check 436 | ((void *) udph, ulen, iph->ip_src.s_addr, 437 | iph->ip_dst.s_addr)) return; 438 | addr.source = ntohs(udph->UH_SPORT); 439 | addr.dest = ntohs(udph->UH_DPORT); 440 | addr.saddr = iph->ip_src.s_addr; 441 | addr.daddr = iph->ip_dst.s_addr; 442 | while (ipp) { 443 | ipp->item(&addr, ((char *) udph) + sizeof(struct udphdr), 444 | ulen - sizeof(struct udphdr), data); 445 | ipp = ipp->next; 446 | } 447 | } 448 | static void gen_ip_proc(u_char * data, int skblen) 449 | { 450 | switch (((struct ip *) data)->ip_p) { 451 | case IPPROTO_TCP: 452 | process_tcp(data, skblen); 453 | break; 454 | case IPPROTO_UDP: 455 | process_udp((char *)data); 456 | break; 457 | case IPPROTO_ICMP: 458 | if (nids_params.n_tcp_streams) 459 | process_icmp(data); 460 | break; 461 | default: 462 | break; 463 | } 464 | } 465 | static void init_procs() 466 | { 467 | ip_frag_procs = mknew(struct proc_node); 468 | ip_frag_procs->item = gen_ip_frag_proc; 469 | ip_frag_procs->next = 0; 470 | ip_procs = mknew(struct proc_node); 471 | ip_procs->item = gen_ip_proc; 472 | ip_procs->next = 0; 473 | tcp_procs = 0; 474 | udp_procs = 0; 475 | } 476 | 477 | void nids_register_udp(void (*x)) 478 | { 479 | register_callback(&udp_procs, x); 480 | } 481 | 482 | void nids_unregister_udp(void (*x)) 483 | { 484 | unregister_callback(&udp_procs, x); 485 | } 486 | 487 | void nids_register_ip(void (*x)) 488 | { 489 | register_callback(&ip_procs, x); 490 | } 491 | 492 | void nids_unregister_ip(void (*x)) 493 | { 494 | unregister_callback(&ip_procs, x); 495 | } 496 | 497 | void nids_register_ip_frag(void (*x)) 498 | { 499 | register_callback(&ip_frag_procs, x); 500 | } 501 | 502 | void nids_unregister_ip_frag(void (*x)) 503 | { 504 | unregister_callback(&ip_frag_procs, x); 505 | } 506 | 507 | static int open_live() 508 | { 509 | char *device; 510 | int promisc = 0; 511 | 512 | if (nids_params.device == NULL) 513 | nids_params.device = pcap_lookupdev(nids_errbuf); 514 | if (nids_params.device == NULL) 515 | return 0; 516 | 517 | device = nids_params.device; 518 | if (!strcmp(device, "all")) 519 | device = "any"; 520 | else 521 | promisc = (nids_params.promisc != 0); 522 | 523 | if ((desc = pcap_open_live(device, 16384, promisc, 524 | nids_params.pcap_timeout, nids_errbuf)) == NULL) 525 | return 0; 526 | #ifdef __linux__ 527 | if (!strcmp(device, "any") && nids_params.promisc 528 | && !set_all_promisc()) { 529 | nids_errbuf[0] = 0; 530 | strncat(nids_errbuf, strerror(errno), sizeof(nids_errbuf) - 1); 531 | return 0; 532 | } 533 | #endif 534 | if (!raw_init()) { 535 | nids_errbuf[0] = 0; 536 | strncat(nids_errbuf, strerror(errno), sizeof(nids_errbuf) - 1); 537 | return 0; 538 | } 539 | return 1; 540 | } 541 | 542 | #ifdef HAVE_LIBGTHREAD_2_0 543 | 544 | #define START_CAP_QUEUE_PROCESS_THREAD() \ 545 | if(nids_params.multiproc) { /* threading... */ \ 546 | if(!(g_thread_create_full((GThreadFunc)cap_queue_process_thread,NULL,0,FALSE,TRUE,G_THREAD_PRIORITY_LOW,&gerror))) { \ 547 | strcpy(nids_errbuf, "thread: "); \ 548 | strncat(nids_errbuf, gerror->message, sizeof(nids_errbuf) - 8); \ 549 | return 0; \ 550 | }; \ 551 | } 552 | 553 | #define STOP_CAP_QUEUE_PROCESS_THREAD() \ 554 | if(nids_params.multiproc) { /* stop the capture process thread */ \ 555 | g_async_queue_push(cap_queue,&EOF_item); \ 556 | } 557 | 558 | 559 | /* thread entry point 560 | * pops capture queue items and feeds them to 561 | * the ip fragment processors - mcree 562 | */ 563 | static void cap_queue_process_thread() 564 | { 565 | struct cap_queue_item *qitem; 566 | 567 | while(1) { /* loop "forever" */ 568 | qitem=g_async_queue_pop(cap_queue); 569 | if (qitem==&EOF_item) break; /* EOF item received: we should exit */ 570 | call_ip_frag_procs(qitem->data,qitem->caplen); 571 | free(qitem->data); 572 | free(qitem); 573 | } 574 | g_thread_exit(NULL); 575 | } 576 | 577 | #else 578 | 579 | #define START_CAP_QUEUE_PROCESS_THREAD() 580 | #define STOP_CAP_QUEUE_PROCESS_THREAD() 581 | 582 | #endif 583 | 584 | int nids_init() 585 | { 586 | /* free resources that previous usages might have allocated */ 587 | nids_exit(); 588 | 589 | if (nids_params.pcap_desc) 590 | desc = nids_params.pcap_desc; 591 | else if (nids_params.filename) { 592 | if ((desc = pcap_open_offline(nids_params.filename, 593 | nids_errbuf)) == NULL) 594 | return 0; 595 | } else if (!open_live()) 596 | return 0; 597 | 598 | if (nids_params.pcap_filter != NULL) { 599 | u_int mask = 0; 600 | struct bpf_program fcode; 601 | 602 | if (pcap_compile(desc, &fcode, nids_params.pcap_filter, 1, mask) < 603 | 0) return 0; 604 | if (pcap_setfilter(desc, &fcode) == -1) 605 | return 0; 606 | } 607 | switch ((linktype = pcap_datalink(desc))) { 608 | #ifdef DLT_IEEE802_11 609 | #ifdef DLT_PRISM_HEADER 610 | case DLT_PRISM_HEADER: 611 | #endif 612 | #ifdef DLT_IEEE802_11_RADIO 613 | case DLT_IEEE802_11_RADIO: 614 | #endif 615 | case DLT_IEEE802_11: 616 | /* wireless, need to calculate offset per frame */ 617 | break; 618 | #endif 619 | #ifdef DLT_NULL 620 | case DLT_NULL: 621 | nids_linkoffset = 4; 622 | break; 623 | #endif 624 | case DLT_EN10MB: 625 | nids_linkoffset = 14; 626 | break; 627 | case DLT_PPP: 628 | nids_linkoffset = 4; 629 | break; 630 | /* Token Ring Support by vacuum@technotronic.com, thanks dugsong! */ 631 | case DLT_IEEE802: 632 | nids_linkoffset = 22; 633 | break; 634 | 635 | case DLT_RAW: 636 | case DLT_SLIP: 637 | nids_linkoffset = 0; 638 | break; 639 | #define DLT_LINUX_SLL 113 640 | case DLT_LINUX_SLL: 641 | nids_linkoffset = 16; 642 | break; 643 | #ifdef DLT_FDDI 644 | case DLT_FDDI: 645 | nids_linkoffset = 21; 646 | break; 647 | #endif 648 | #ifdef DLT_PPP_SERIAL 649 | case DLT_PPP_SERIAL: 650 | nids_linkoffset = 4; 651 | break; 652 | #endif 653 | default: 654 | strcpy(nids_errbuf, "link type unknown"); 655 | return 0; 656 | } 657 | if (nids_params.dev_addon == -1) { 658 | if (linktype == DLT_EN10MB) 659 | nids_params.dev_addon = 16; 660 | else 661 | nids_params.dev_addon = 0; 662 | } 663 | if (nids_params.syslog == nids_syslog) 664 | openlog("libnids", 0, LOG_LOCAL0); 665 | 666 | init_procs(); 667 | tcp_init(nids_params.n_tcp_streams); 668 | ip_frag_init(nids_params.n_hosts); 669 | scan_init(); 670 | 671 | if(nids_params.multiproc) { 672 | #ifdef HAVE_LIBGTHREAD_2_0 673 | g_thread_init(NULL); 674 | cap_queue=g_async_queue_new(); 675 | #else 676 | strcpy(nids_errbuf, "libnids was compiled without threads support"); 677 | return 0; 678 | #endif 679 | } 680 | 681 | return 1; 682 | } 683 | 684 | int nids_run() 685 | { 686 | if (!desc) { 687 | strcpy(nids_errbuf, "Libnids not initialized"); 688 | return 0; 689 | } 690 | START_CAP_QUEUE_PROCESS_THREAD(); /* threading... */ 691 | pcap_loop(desc, -1, (pcap_handler) nids_pcap_handler, 0); 692 | /* FIXME: will this code ever be called? Don't think so - mcree */ 693 | STOP_CAP_QUEUE_PROCESS_THREAD(); 694 | nids_exit(); 695 | return 0; 696 | } 697 | 698 | void nids_exit() 699 | { 700 | if (!desc) { 701 | strcpy(nids_errbuf, "Libnids not initialized"); 702 | return; 703 | } 704 | #ifdef HAVE_LIBGTHREAD_2_0 705 | if (nids_params.multiproc) { 706 | /* I have no portable sys_sched_yield, 707 | and I don't want to add more synchronization... 708 | */ 709 | while (g_async_queue_length(cap_queue)>0) 710 | usleep(100000); 711 | } 712 | #endif 713 | tcp_exit(); 714 | ip_frag_exit(); 715 | scan_exit(); 716 | strcpy(nids_errbuf, "loop: "); 717 | strncat(nids_errbuf, pcap_geterr(desc), sizeof nids_errbuf - 7); 718 | if (!nids_params.pcap_desc) 719 | pcap_close(desc); 720 | desc = NULL; 721 | 722 | free(ip_procs); 723 | free(ip_frag_procs); 724 | } 725 | 726 | int nids_getfd() 727 | { 728 | if (!desc) { 729 | strcpy(nids_errbuf, "Libnids not initialized"); 730 | return -1; 731 | } 732 | return pcap_get_selectable_fd(desc); 733 | } 734 | 735 | int nids_next() 736 | { 737 | struct pcap_pkthdr h; 738 | char *data; 739 | 740 | if (!desc) { 741 | strcpy(nids_errbuf, "Libnids not initialized"); 742 | return 0; 743 | } 744 | if (!(data = (char *) pcap_next(desc, &h))) { 745 | strcpy(nids_errbuf, "next: "); 746 | strncat(nids_errbuf, pcap_geterr(desc), sizeof(nids_errbuf) - 7); 747 | return 0; 748 | } 749 | /* threading is quite useless (harmful) in this case - should we do an API change? */ 750 | START_CAP_QUEUE_PROCESS_THREAD(); 751 | nids_pcap_handler(0, &h, (u_char *)data); 752 | STOP_CAP_QUEUE_PROCESS_THREAD(); 753 | return 1; 754 | } 755 | 756 | int nids_dispatch(int cnt) 757 | { 758 | int r; 759 | 760 | if (!desc) { 761 | strcpy(nids_errbuf, "Libnids not initialized"); 762 | return -1; 763 | } 764 | START_CAP_QUEUE_PROCESS_THREAD(); /* threading... */ 765 | if ((r = pcap_dispatch(desc, cnt, (pcap_handler) nids_pcap_handler, 766 | NULL)) == -1) { 767 | strcpy(nids_errbuf, "dispatch: "); 768 | strncat(nids_errbuf, pcap_geterr(desc), sizeof(nids_errbuf) - 11); 769 | } 770 | STOP_CAP_QUEUE_PROCESS_THREAD(); 771 | return r; 772 | } 773 | -------------------------------------------------------------------------------- /src/nids.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 1999 Rafal Wojtczuk . All rights reserved. 3 | See the file COPYING for license details. 4 | */ 5 | 6 | #ifndef _NIDS_NIDS_H 7 | # define _NIDS_NIDS_H 8 | 9 | # include 10 | #include 11 | #include 12 | # include 13 | # include 14 | # include 15 | 16 | # ifdef __cplusplus 17 | extern "C" { 18 | # endif 19 | 20 | # define NIDS_MAJOR 1 21 | # define NIDS_MINOR 26 22 | 23 | enum 24 | { 25 | NIDS_WARN_IP = 1, 26 | NIDS_WARN_TCP, 27 | NIDS_WARN_UDP, 28 | NIDS_WARN_SCAN 29 | }; 30 | 31 | enum 32 | { 33 | NIDS_WARN_UNDEFINED = 0, 34 | NIDS_WARN_IP_OVERSIZED, 35 | NIDS_WARN_IP_INVLIST, 36 | NIDS_WARN_IP_OVERLAP, 37 | NIDS_WARN_IP_HDR, 38 | NIDS_WARN_IP_SRR, 39 | NIDS_WARN_TCP_TOOMUCH, 40 | NIDS_WARN_TCP_HDR, 41 | NIDS_WARN_TCP_BIGQUEUE, 42 | NIDS_WARN_TCP_BADFLAGS 43 | }; 44 | 45 | # define NIDS_JUST_EST 1 46 | # define NIDS_DATA 2 47 | # define NIDS_CLOSE 3 48 | # define NIDS_RESET 4 49 | # define NIDS_TIMED_OUT 5 50 | # define NIDS_EXITING 6 /* nids is exiting; last chance to get data */ 51 | 52 | # define NIDS_DO_CHKSUM 0 53 | # define NIDS_DONT_CHKSUM 1 54 | 55 | struct tuple4 56 | { 57 | u_short source; 58 | u_short dest; 59 | u_int saddr; 60 | u_int daddr; 61 | }; 62 | 63 | struct half_stream 64 | { 65 | char state; 66 | char collect; 67 | char collect_urg; 68 | 69 | char *data; 70 | int offset; 71 | int count; 72 | int count_new; 73 | int bufsize; 74 | int rmem_alloc; 75 | 76 | int urg_count; 77 | u_int acked; 78 | u_int seq; 79 | u_int ack_seq; 80 | u_int first_data_seq; 81 | u_char urgdata; 82 | u_char count_new_urg; 83 | u_char urg_seen; 84 | u_int urg_ptr; 85 | u_short window; 86 | u_char ts_on; 87 | u_char wscale_on; 88 | u_int curr_ts; 89 | u_int wscale; 90 | struct skbuff *list; 91 | struct skbuff *listtail; 92 | }; 93 | 94 | struct tcp_stream 95 | { 96 | struct tuple4 addr; 97 | char nids_state; 98 | struct lurker_node *listeners; 99 | struct half_stream client; 100 | struct half_stream server; 101 | struct tcp_stream *next_node; 102 | struct tcp_stream *prev_node; 103 | int hash_index; 104 | struct tcp_stream *next_time; 105 | struct tcp_stream *prev_time; 106 | int read; 107 | struct tcp_stream *next_free; 108 | void *user; 109 | long ts; 110 | }; 111 | 112 | struct nids_prm 113 | { 114 | int n_tcp_streams; 115 | int n_hosts; 116 | char *device; 117 | char *filename; 118 | int sk_buff_size; 119 | int dev_addon; 120 | void (*syslog) (); 121 | int syslog_level; 122 | int scan_num_hosts; 123 | int scan_delay; 124 | int scan_num_ports; 125 | void (*no_mem) (char *); 126 | int (*ip_filter) (); 127 | char *pcap_filter; 128 | int promisc; 129 | int one_loop_less; 130 | int pcap_timeout; 131 | int multiproc; 132 | int queue_limit; 133 | int tcp_workarounds; 134 | pcap_t *pcap_desc; 135 | int tcp_flow_timeout; 136 | }; 137 | 138 | struct tcp_timeout 139 | { 140 | struct tcp_stream *a_tcp; 141 | struct timeval timeout; 142 | struct tcp_timeout *next; 143 | struct tcp_timeout *prev; 144 | }; 145 | 146 | int nids_init (void); 147 | void nids_register_ip_frag (void (*)); 148 | void nids_unregister_ip_frag (void (*)); 149 | void nids_register_ip (void (*)); 150 | void nids_unregister_ip (void (*)); 151 | void nids_register_tcp (void (*)); 152 | void nids_unregister_tcp (void (*x)); 153 | void nids_register_udp (void (*)); 154 | void nids_unregister_udp (void (*)); 155 | void nids_killtcp (struct tcp_stream *); 156 | void nids_discard (struct tcp_stream *, int); 157 | int nids_run (void); 158 | void nids_exit(void); 159 | int nids_getfd (void); 160 | int nids_dispatch (int); 161 | int nids_next (void); 162 | void nids_pcap_handler(u_char *, struct pcap_pkthdr *, u_char *); 163 | struct tcp_stream *nids_find_tcp_stream(struct tuple4 *); 164 | void nids_free_tcp_stream(struct tcp_stream *); 165 | 166 | extern struct nids_prm nids_params; 167 | extern char *nids_warnings[]; 168 | extern char nids_errbuf[]; 169 | extern struct pcap_pkthdr *nids_last_pcap_header; 170 | extern u_char *nids_last_pcap_data; 171 | extern u_int nids_linkoffset; 172 | extern struct tcp_timeout *nids_tcp_timeouts; 173 | 174 | struct nids_chksum_ctl { 175 | u_int netaddr; 176 | u_int mask; 177 | u_int action; 178 | u_int reserved; 179 | }; 180 | extern void nids_register_chksum_ctl(struct nids_chksum_ctl *, int); 181 | 182 | # ifdef __cplusplus 183 | } 184 | # endif 185 | 186 | #endif /* _NIDS_NIDS_H */ 187 | -------------------------------------------------------------------------------- /src/scan.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 1999 Rafal Wojtczuk . All rights reserved. 3 | See the file COPYING for license details. 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "nids.h" 16 | #include "scan.h" 17 | 18 | static struct host **hashhost; 19 | static int time0; 20 | static int timenow; 21 | 22 | static int 23 | gettime() 24 | { 25 | struct timeval tv; 26 | 27 | if (timenow) 28 | return timenow; 29 | gettimeofday(&tv, 0); 30 | timenow = (tv.tv_sec - time0) * 1000 + tv.tv_usec / 1000; 31 | 32 | return timenow; 33 | } 34 | 35 | void 36 | scan_init(void) 37 | { 38 | struct timeval tv; 39 | 40 | if (nids_params.scan_num_hosts > 0) { 41 | gettimeofday(&tv, 0); 42 | time0 = tv.tv_sec; 43 | hashhost = (struct host **) calloc(nids_params.scan_num_hosts, sizeof(struct host *)); 44 | if (!hashhost) 45 | nids_params.no_mem("scan_init"); 46 | } 47 | } 48 | 49 | void 50 | scan_exit(void) 51 | { 52 | if (hashhost) { 53 | free(hashhost); 54 | hashhost = NULL; 55 | } 56 | } 57 | 58 | static int 59 | scan_hash(int addr) 60 | { 61 | return ((addr % 65536) ^ (addr >> 16)) % (nids_params.scan_num_hosts); 62 | } 63 | 64 | void 65 | detect_scan(struct ip * iph) 66 | { 67 | int i; 68 | struct tcphdr *th; 69 | int hash; 70 | struct host *this_host; 71 | struct host *oldest; 72 | int mtime = 2147483647; 73 | 74 | if (nids_params.scan_num_hosts <= 0) 75 | return; 76 | 77 | th = (struct tcphdr *) (((char *) iph) + 4 * iph->ip_hl); 78 | hash = scan_hash(iph->ip_src.s_addr); 79 | this_host = hashhost[hash]; 80 | oldest = 0; 81 | timenow = 0; 82 | 83 | for (i = 0; this_host && this_host->addr != iph->ip_src.s_addr; i++) { 84 | if (this_host->modtime < mtime) { 85 | mtime = this_host->modtime; 86 | oldest = this_host; 87 | } 88 | this_host = this_host->next; 89 | } 90 | if (!this_host) { 91 | if (i == 10) 92 | this_host = oldest; 93 | else { 94 | this_host = (struct host *) malloc(sizeof(struct host) + \ 95 | (nids_params.scan_num_ports + 1) * sizeof(struct scan)); 96 | if (!this_host) 97 | nids_params.no_mem("detect_scan"); 98 | this_host->packets = (struct scan *) (((char *) this_host) + sizeof(struct host)); 99 | if (hashhost[hash]) { 100 | hashhost[hash]->prev = this_host; 101 | this_host->next = hashhost[hash]; 102 | } 103 | else 104 | this_host->next = 0; 105 | this_host->prev = 0; 106 | hashhost[hash] = this_host; 107 | } 108 | this_host->addr = iph->ip_src.s_addr; 109 | this_host->modtime = gettime(); 110 | this_host->n_packets = 0; 111 | } 112 | if (this_host->modtime - gettime() > nids_params.scan_delay) 113 | this_host->n_packets = 0; 114 | this_host->modtime = gettime(); 115 | for (i = 0; i < this_host->n_packets; i++) 116 | if (this_host->packets[i].addr == iph->ip_dst.s_addr && 117 | this_host->packets[i].port == ntohs(th->th_dport)) 118 | return; 119 | this_host->packets[this_host->n_packets].addr = iph->ip_dst.s_addr; 120 | this_host->packets[this_host->n_packets].port = ntohs(th->th_dport); 121 | this_host->packets[this_host->n_packets].flags = *((unsigned char *) (th) + 13); 122 | this_host->n_packets++; 123 | if (this_host->n_packets > nids_params.scan_num_ports) { 124 | nids_params.syslog(NIDS_WARN_SCAN, 0, 0, this_host); 125 | this_host->n_packets = 0; 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/scan.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 1999 Rafal Wojtczuk . All rights reserved. 3 | See the file COPYING for license details. 4 | */ 5 | 6 | #ifndef _NIDS_SCAN_H 7 | #define _NIDS_SCAN_H 8 | 9 | struct scan { 10 | u_int addr; 11 | unsigned short port; 12 | u_char flags; 13 | }; 14 | 15 | struct host { 16 | struct host *next; 17 | struct host *prev; 18 | u_int addr; 19 | int modtime; 20 | int n_packets; 21 | struct scan *packets; 22 | }; 23 | 24 | void scan_init(void); 25 | void scan_exit(void); 26 | void detect_scan(struct ip *); 27 | 28 | #endif /* _NIDS_SCAN_H */ 29 | -------------------------------------------------------------------------------- /src/tcp.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 1999 Rafal Wojtczuk . All rights reserved. 3 | See the file COPYING for license details. 4 | */ 5 | #ifndef _NIDS_TCP_H 6 | #define _NIDS_TCP_H 7 | #include 8 | 9 | struct skbuff { 10 | struct skbuff *next; 11 | struct skbuff *prev; 12 | 13 | void *data; 14 | u_int len; 15 | u_int truesize; 16 | u_int urg_ptr; 17 | 18 | char fin; 19 | char urg; 20 | u_int seq; 21 | u_int ack; 22 | }; 23 | 24 | int tcp_init(int); 25 | void tcp_exit(void); 26 | void process_tcp(u_char *, int); 27 | void process_icmp(u_char *); 28 | void tcp_check_timeouts(struct timeval *); 29 | 30 | #endif /* _NIDS_TCP_H */ 31 | -------------------------------------------------------------------------------- /src/util.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 1999 Rafal Wojtczuk . All rights reserved. 3 | See the file COPYING for license details. 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "tcp.h" 12 | #include "util.h" 13 | #include "nids.h" 14 | 15 | void 16 | nids_no_mem(char *func) 17 | { 18 | fprintf(stderr, "Out of memory in %s.\n", func); 19 | exit(1); 20 | } 21 | 22 | char * 23 | test_malloc(int x) 24 | { 25 | char *ret = malloc(x); 26 | 27 | if (!ret) 28 | nids_params.no_mem("test_malloc"); 29 | 30 | return ret; 31 | } 32 | 33 | void 34 | register_callback(struct proc_node **procs, void (*x)) 35 | { 36 | struct proc_node *ipp; 37 | 38 | for (ipp = *procs; ipp; ipp = ipp->next) 39 | if (x == ipp->item) 40 | return; 41 | ipp = mknew(struct proc_node); 42 | ipp->item = x; 43 | ipp->next = *procs; 44 | *procs = ipp; 45 | } 46 | 47 | void 48 | unregister_callback(struct proc_node **procs, void (*x)) 49 | { 50 | struct proc_node *ipp; 51 | struct proc_node *ipp_prev = 0; 52 | 53 | for (ipp = *procs; ipp; ipp = ipp->next) { 54 | if (x == ipp->item) { 55 | if (ipp_prev) 56 | ipp_prev->next = ipp->next; 57 | else 58 | *procs = ipp->next; 59 | free(ipp); 60 | return; 61 | } 62 | ipp_prev = ipp; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/util.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 1999 Rafal Wojtczuk . All rights reserved. 3 | See the file COPYING for license details. 4 | */ 5 | 6 | #ifndef _NIDS_UTIL_H 7 | #define _NIDS_UTIL_H 8 | 9 | #define mknew(x) (x *)test_malloc(sizeof(x)) 10 | #define b_comp(x,y) (!memcmp(&(x), &(y), sizeof(x))) 11 | 12 | struct proc_node { 13 | void (*item)(); 14 | struct proc_node *next; 15 | }; 16 | 17 | struct lurker_node { 18 | void (*item)(); 19 | void *data; 20 | char whatto; 21 | struct lurker_node *next; 22 | }; 23 | 24 | void nids_no_mem(char *); 25 | char *test_malloc(int); 26 | void register_callback(struct proc_node **procs, void (*x)); 27 | void unregister_callback(struct proc_node **procs, void (*x)); 28 | 29 | static inline int 30 | before(u_int seq1, u_int seq2) 31 | { 32 | return ((int)(seq1 - seq2) < 0); 33 | } 34 | 35 | static inline int 36 | after(u_int seq1, u_int seq2) 37 | { 38 | return ((int)(seq2 - seq1) < 0); 39 | } 40 | 41 | #endif /* _NIDS_UTIL_H */ 42 | --------------------------------------------------------------------------------