pax_global_header00006660000000000000000000000064121714713420014514gustar00rootroot0000000000000052 comment=b760c76835df194637644831b0c168ca82db62ec kernel-source-ipt_netflow-1.8/000075500000000000000000000000001217147134200164705ustar00rootroot00000000000000kernel-source-ipt_netflow-1.8/.gear-rules000064400000000000000000000000071217147134200205340ustar00rootroot00000000000000tar: . kernel-source-ipt_netflow-1.8/Makefile.in000064400000000000000000000022021217147134200205310ustar00rootroot00000000000000# KVERSION = @KVERSION@ KDIR = @KDIR@ IPTABLES_CFLAGS = @IPTABLES_CFLAGS@ IPTABLES_MODULES = @IPTABLES_MODULES@ obj-m = ipt_NETFLOW.o all: ipt_NETFLOW.ko libipt_NETFLOW.so ipt_NETFLOW.ko: ipt_NETFLOW.c ipt_NETFLOW.h Makefile @echo Compiling for kernel $(KVERSION) make -C $(KDIR) M=$(CURDIR) modules @touch $@ minstall: | ipt_NETFLOW.ko make -C $(KDIR) M=$(CURDIR) modules_install INSTALL_MOD_PATH=$(DESTDIR) mclean: make -C $(KDIR) M=$(CURDIR) clean lclean: -rm -f *.so *_sh.o clean: mclean lclean -rm -f *.so *.o modules.order libipt_NETFLOW.so: libipt_NETFLOW.c Makefile gcc -O2 -Wall -Wunused $(IPTABLES_CFLAGS) -fPIC -o libipt_NETFLOW_sh.o -c libipt_NETFLOW.c gcc -shared -o libipt_NETFLOW.so libipt_NETFLOW_sh.o linstall: | libipt_NETFLOW.so install -D libipt_NETFLOW.so $(DESTDIR)$(IPTABLES_MODULES)/libipt_NETFLOW.so install: minstall linstall Makefile: Makefile.in configure ./configure --make load: all insmod ipt_NETFLOW.ko active_timeout=5 iptables -A OUTPUT -d 0/0 -j NETFLOW iptables -A INPUT -d 0/0 -j NETFLOW unload: iptables -D OUTPUT -d 0/0 -j NETFLOW iptables -D INPUT -d 0/0 -j NETFLOW rmmod ipt_NETFLOW.ko kernel-source-ipt_netflow-1.8/README000064400000000000000000000244231217147134200173550ustar00rootroot00000000000000ipt_NETFLOW linux 2.6 kernel module by -- 11 Feb 2008 ============================ = OBTAINING LATEST VERSION = ============================ $ git clone git://ipt-netflow.git.sourceforge.net/gitroot/ipt-netflow/ipt-netflow $ cd ipt-netflow ================ = INSTALLATION = ================ 1. Besides kernel you will need iptables/netfilter source matching your installation or just fresh install from there: ftp://ftp.netfilter.org/pub/iptables/snapshot/ I have this: ftp://ftp.netfilter.org/pub/iptables/snapshot/iptables-1.3.7-20070329.tar.bz2 Unpack it somewhere and build with make. 2. Run ./configure script and it will create Makefile 3. make all install; depmod This will install kernel module and iptable specific library. Troubleshooting: 1) Sometimes you will want to add CC=gcc-3 to make command. Example: make CC=gcc-3.3 2) Compile module with actual kernel source compiled. I.e. first compile kernel and boot into it, and then compile module. 3) For autoloading module after reboot: set net.netflow.destination (or load module, if idestination set on load) after interfaces are up. Becasue module needs exporting interface (usually lo) to establish export connection. 4. After this point you should be able to load module and use -j NETFLOW target in your iptables. See next section. =========== = RUNNING = =========== 1. You can load module by insmod like this: # insmod ipt_NETFLOW.ko destination=127.0.0.1:2055 debug=1 Or if properly installed (make install; depmod) by this: # modprobe ipt_NETFLOW destination=127.0.0.1:2055 See, you may add options in insmod/modprobe command line, or add them in /etc/ to modules.conf or modprobe.conf like thus: options ipt_NETFLOW destination=127.0.0.1:2055 2. Statistics is in /proc/net/stat/ipt_netflow To view slab statistics: grep ipt_netflow /proc/slabinfo 3. You can view parameters and control them via sysctl, example: # sysctl -w net.netflow.hashsize=32768 4. Example of directing all traffic into module: # iptables -A FORWARD -j NETFLOW # iptables -A INPUT -j NETFLOW # iptables -A OUTPUT -j NETFLOW =========== = OPTIONS = =========== destination=127.0.0.1:2055 - where to export netflow, to this ip address You will see this connection in netstat like this: udp 0 0 127.0.0.1:32772 127.0.0.1:2055 ESTABLISHED destination=127.0.0.1:2055,192.0.0.1:2055 - mirror flows to two (can be more) addresses, separate addresses with comma. inactive_timeout=15 - export flow after it's inactive 15 seconds. Default value is 15. active_timeout=1800 - export flow after it's active 1800 seconds (30 minutes). Default value is 1800. debug=0 - debug level (none). sndbuf=number - size of output socket buffer in bytes. Recommend you to put higher value if you experience netflow packet drops (can be seen in statistics as 'sock: fail' number.) Default value is system default. hashsize=number - Hash table bucket size. Used for performance tuning. Abstractly speaking, it should be two times bigger than flows you usually have, but not need to. Default is system memory dependent small enough value. maxflows=2000000 - Maximum number of flows to account. It's here to prevent DOS attacks. After this limit reached new flows will not be accounted. Default is 2000000, zero is unlimited. aggregation=string.. - Few aggregation rules (or some say they are rule.) Buffer for aggregation string 1024 bytes, and sysctl limit it to ~700 bytes, so don't write there a lot. Rules worked in definition order for each packet, so don't write them a lot again. Rules applied to both directions (dst and src). Rules tried until first match, but for netmask and port aggregations separately. Delimit them with commas. Rules are of two kinds: for netmask aggregation and port aggregation: a) Netmask aggregation example: 192.0.0.0/8=16 Which mean to strip addresses matching subnet 192.0.0.0/8 to /16. b) Port aggregation example: 80-89=80 Which mean to replace ports from 80 to 89 with 80. Full example: aggregation=192.0.0.0/8=16,10.0.0.0/8=16,80-89=80,3128=80 ==================== = HOW TO READ STAT = ==================== Statistics is your friend to fine tune and understand netflow module performance. To see stat: # cat /proc/net/stat/ipt_netflow How to interpret the data: > Flows: active 5187 (peak 83905 reached 0d0h1m ago, maxflows 2000000), mem 283K active X: currently active flows in memory cache. - for optimum CPU performance it is recommended to set hash table size to twice of average of this value, or higher. peak X reached Y ago: peak value of active flows. mem XK: how much kilobytes of memory currently taken by active flows. - one active flow taking 56 bytes of memory. - there is system limit on cache size too. > Hash: size 8192 (mem 32K), metric 1.0, 1.0, 1.0, 1.0. MemTraf: 1420 pkt, 364 K (pdu 0, 0). Hash: size X: current hash size/limit. - you can control this by sysctl net.netflow.hashsize variable. - increasing this value can significantly reduce CPU load. - default value is not optimal for performance. - optimal value is twice of average of active flows. mem XK: how much memory occupied by hash table. - hash table is fixed size by nature, taking 4 bytes per entry. metric X, X, X, X: how optimal is your hash table being used. - lesser value mean more optimal hash table use, min is 1.0. - this is moving average (EWMA) of hash table access divided by match rate (searches / matches) for 4sec, and 1, 5, 15 minutes. Sort of hash table load average. MemTraf: X pkt, X K: how much traffic accounted for flows that are in memory. - these flows that are residing in internal hash table. pdu X, X: how much traffic in flows preparing to be exported. - it is included already in aforementioned MemTraf total. > Timeout: active 1800, inactive 15. Maxflows 2000000 Timeout: active X: how much seconds to wait before exporting active flow. - same as sysctl net.netflow.active_timeout variable. inactive X: how much seconds to wait before exporting inactive flow. - same as sysctl net.netflow.inactive_timeout variable. Maxflows 2000000: maxflows limit. - all flows above maxflows limit must be dropped. - you can control maxflows limit by sysctl net.netflow.maxflows variable. > Rate: 202448 bits/sec, 83 packets/sec; 1 min: 668463 bps, 930 pps; 5 min: 329039 bps, 483 pps - Module throughput values for 1 second, 1 minute, and 5 minutes. > cpu# stat: , sock: , traffic: , drop: > cpu0 stat: 980540 10473 180600, 0 0 0 0, sock: 4983 928 0, 7124 K, traffic: 188765, 14 MB, drop: 27863, 1142 K cpu#: this is Total and per CPU statistics for: stat: : internal stat for: search found new: hash table searched, found, and not found counters. trunc: how much truncated packets is ignored - these are that possible don't have valid IP header. - accounted in drop packets counter but not in drop bytes. frag: how much fragmented packets have seen. - kernel always defragments INPUT/OUTPUT chains for us. - these packets are not ignored but not reassembled either, so: - if there is no enough data in fragment (ex. tcp ports) it is considered zero. alloc: how much cache memory allocations is failed. - packets ignored and accounted in drop stat. - probably increase system memory if this ever happen. maxflows: how much packets ignored on maxflows (maximum active flows reached). - packets ignored and accounted in drop stat. - you can control maxflows limit by sysctl net.netflow.maxflows variable. sock: : table of exporting stats for: ok: how much Netflow PDUs are exported (i.e. UDP packets sent by module). fail: how much socket errors (i.e. packets failed to be sent). - packets dropped and their internal statistics cumulatively accounted in drop stat. cberr: how much connection refused ICMP errors we got from export target. - probably you not launched collector software on destination, - or specified wrong destination address. - flows lost in this fashion is not possible to account in drop stat. - these are ICMP errors, and would look like this in tcpdump: 05:04:09.281247 IP alice.19440 > bob.2055: UDP, length 120 05:04:09.281405 IP bob > alice: ICMP bob udp port 2055 unreachable, length 156 bytes: how much kilobytes of exporting data successfully sent by the module. traffic: : how much traffic is accounted. pkt, bytes: sum of packets/megabytes accounted by module. - flows that failed to be exported (on socket error) is accounted here too. drop: : how much of traffic is not accounted. pkt, bytes: sum of packets/kilobytes we are lost/dropped. - reasons they are dropped and accounted here: truncated/fragmented packets, packet is for new flow but failed to allocate memory for it, packet is for new flow but maxflows is already reached, all flows in export packets that got socket error. > sock0: 10.0.0.2:2055, sndbuf 106496, filled 0, peak 106848; err: sndbuf reached 928, other 0 sockX: per destination stats for: X.X.X.X:Y: destination ip address and port. - controlled by sysctl net.netflow.destination variable. sndbuf X: how much data socket can hold in buffers. - controlled by sysctl net.netflow.sndbuf variable. - if you have packet drops due to sndbuf reached (error -11) increase this value. filled X: how much data in socket buffers right now. peak X: peak value of how much data in socket buffers was. - you will be interested to keep it below sndbuf value. err: how much packets are dropped due to errors. - all flows from them will be accounted in drop stat. sndbuf reached X: how much packets dropped due to sndbuf being too small (error -11). other X: dropped due to other possible errors. > aggr0: ... aggrX: aggregation rulesets. - controlled by sysctl net.netflow.aggregation variable. ========= = VOILA = ========= kernel-source-ipt_netflow-1.8/README.promisc000064400000000000000000000040731217147134200210270ustar00rootroot00000000000000Hello, If you wish to account with netflow module traffic mirrored on switch you may follow this example: Solution 1: General kernel patch. Solution 2: Alternative w/o kernel patch. ************** * Solution 1 * ************** 1. Patch your kernel with `raw_promisc.patch' to enable raw table to see promisc traffic. # cd /usr/src/linux # patch -p1 < ~/ipt_netflow/raw_promisc.patch Then recompile and reinstall patched kernel. 2. For example you mirroring traffic on your Cisco switch to 47th vlan: # interface FastEthernet0/32 # description this port with vlan 47 # switchport trunk encapsulation dot1q # switchport mode trunk # ! # interface FastEthernet0/33 # port monitor FastEthernet0/32 # ! 3. Enable promisc mode on interface to actually see the packets: # /sbin/ifconfig eth1 promisc 4. Configure vlan on your linux box: # /sbin/vconfig add eth1 47 # /sbin/ifconfig eth1.47 up 5. Recompile ipt_netflow module with #define RAW_PROMISC_HACK uncommented: Find this line in ipt_NETFLOW.c (should be line 7): //#define RAW_PROMISC_HACK And remove two slashes at beginning of the line, so it become like this: #define RAW_PROMISC_HACK Re-compile module: # make clean all install 6. Load ipt_netflow module: # /sbin/modprobe ipt_NETFLOW hashsize=160000 destination=127.0.0.1:9800 7. Direct all packets from 47th vlan to ipt_netflow module: # /sbin/iptables -A PREROUTING -t raw -i eth1.47 -j NETFLOW Voila. ps. For Debian Squeeze instructions look at raw_promisc_debian_squeeze6.patch ************** * Solution 2 * ************** By Anonymous. > raw promisc hack is not needed > there is a more elegant way to capture port mirrored traffic: > > 1. create a bridge of eth0 and dummy0 > 2. put eth0 to promisc > 3. add a "-i br0 -j NETFLOW" rule to FORWARD (possibly also -j DROP after that) > > ...for some reason it works when ipt_netflow is attached to a bridge, but > requires the promisc hack when attached to a real promisc interface. Sometimes you may need to run: # brctl setageing br0 0 for this scheme to work. kernel-source-ipt_netflow-1.8/configure000075500000000000000000000141311217147134200203770ustar00rootroot00000000000000#!/bin/bash PATH=$PATH:/bin:/usr/bin:/usr/sbin:/sbin:/usr/local/sbin error() { echo "! Error: $@" exit 1 } iptables_src_version() { test "$IPTSRC" || return 1 echo -n "Checking iptables sources version: " SRC="$IPTSRC/Makefile" test -s "$SRC" || error "Please build iptables first." VER=`sed -n 's/^\(IPTABLES_\)\?VERSION[ :]= \?//p' "$SRC"` test "$VER" || error "Unknown version of iptables." if [ "$VER" = "$IPTVER" ]; then echo "$VER (ok)" else echo "$VER" error "Source version ($VER) doesn't match binary ($IPTVER)" fi } get_lib_dir() { test -s "$1" && LIB=`sed -n 's/.*_LIB_DIR "\(.*\)"/\1/p' "$1"` if [ "$LIB" ]; then IPTLIB=$LIB echo "$IPTLIB (from sources)" return 0 fi return 1 } get_lib_from_bin() { LIB=`strings $IPTBIN | grep ^/.*lib.*tables` if [ "$LIB" ]; then IPTLIB=$LIB echo "$IPTLIB (from binary)" return 0 fi return 1 } get_lib_from_lib() { XLIB=`/usr/bin/ldd $IPTBIN | grep libxtables | sed -n 's!.* \(/[^ ]\+\).*!\1!p'` test "$XLIB" || return 1 LIB=`strings $XLIB | grep ^/.*lib.*tables` if [ "$LIB" ]; then IPTLIB=$LIB echo "$IPTLIB (from library)" return 0 fi return 1 } iptables_inc() { echo -n "Iptables include flags: " if [ "$IPTINC" ]; then IPTINC="-I$IPTINC" echo "$IPTINC (user specified)" elif [ "$PKGVER" ]; then IPTINC="$PKGINC" echo "$IPTINC (pkg-config)" else IPTINC="$IPTSRC/include" IPTINC="-I$IPTINC" echo "$IPTINC (from source)" fi } iptables_modules() { echo -n "Iptables module path: " if [ "$IPTLIB" ]; then echo "$IPTLIB (user specified)" else if [ "$PKGLIB" ]; then IPTLIB="$PKGLIB" echo "$IPTLIB (pkg-config)" else get_lib_dir "$IPTSRC/include/iptables.h" && return 0 get_lib_dir "$IPTSRC/include/xtables.h" && return 0 get_lib_dir "$IPTSRC/xtables/internal.h" && return 0 get_lib_from_bin && return 0 get_lib_from_lib && return 0 error "can not find, try setting it with --ipt-lib=" fi fi } try_dir() { if [ -d "$1/include" ]; then echo "Found iptables sources at $1" IPTSRC=$1 return 0 fi return 1 } try_dirg() { try_dir "$1" && return 0 try_dir "$1.git" && return 0 } try_dir2() { test -d "$1" && try_dir `dirname $1` && return 0 } iptables_find_version() { echo -n "Iptables binary version: " if [ "$IPTVER" ]; then echo "$IPTVER (user specified)" else IPTVER=`$IPTBIN -V 2>/dev/null | sed -n s/iptables.v//p` if [ "$IPTVER" ]; then echo "$IPTVER (detected from $IPTBIN)" return else echo "no iptables binary found" fi PKGVER=`pkg-config --modversion xtables 2>/dev/null` if [ "$PKGVER" ]; then IPTVER="$PKGVER" echo "Xtables version: $IPTVER (detected from `which pkg-config`)" return fi error "Can not find iptables version, try setting it with --ipt-ver=" fi } iptables_try_pkgconfig() { if [ ! "$PKGVER" ]; then TRYPKGVER=`pkg-config --modversion xtables 2>/dev/null` echo -n "pkg-config for version $IPTVER exists: " pkg-config --exact-version=$IPTVER xtables if [ $? = 0 ]; then echo "Yes" PKGVER=$TRYPKGVER else echo "No (reported: $TRYPKGVER)" fi fi if [ "$PKGVER" ]; then PKGINC=`pkg-config --cflags xtables` PKGLIB=`pkg-config --variable=xtlibdir xtables` IPTCFLAGS="-DXTABLES" else # Newer versions of iptables should not have -I/kernel/include! # So I assume that newer version will have correct pkg-config set up # and if not, then it's older who need it. IPTCFLAGS="-I$KDIR/include -DIPTABLES_VERSION=\\\\\"$IPTVER\\\\\"" fi } iptables_find_src() { test "$IPTINC" && return 1 test "$PKGVER" && return 1 VER="iptables-$IPTVER" if [ "$IPTSRC" ]; then echo "User specified source directory: $IPTSRC" try_dir $IPTSRC || error "Specified directory is not iptables source.." else echo "Searching for $VER sources.." try_dir "./$VER" && return 0 try_dir "../$VER" && return 0 try_dir "/usr/src/$VER" && return 0 try_dirg "iptables" && return 0 try_dirg "../iptables" && return 0 try_dirg "/usr/src/iptables" && return 0 try_dir2 `locate $VER/extensions | head -1` && return 0 error "Can not find iptables source directory, try setting it with --ipt-src=" fi } show_help() { echo "Possible options:" echo " --ipt-ver=.. iptables version (ex.: 1.4.2)" echo " --ipt-bin=.. iptables binary to use (ex.: /usr/sbin/iptables)" echo " --ipt-src=.. directory for iptable source (ex.: ../iptables-1.4.2)" echo " --ipt-lib=.. iptable modules path (ex.: /usr/libexec/xtables)" echo " --ipt-inc=.. directory for iptable headers (ex.: /usr/include)" echo " --kver=.. kernel version (ex.: 2.6.30-std-def-alt15)" echo " --kdir=.. directory for kernel source (ex.: /usr/src/kernel)" exit 0 } for ac_option do case "$ac_option" in -*=*) ac_optarg=`echo "$ac_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;; *) ac_optarg= ;; esac case "$ac_option" in --ipt-bin=*) IPTBIN="$ac_optarg" ;; --ipt-lib=*) IPTLIB="$ac_optarg" ;; --ipt-src=*) IPTSRC="$ac_optarg" ;; --ipt-ver=*) IPTVER="$ac_optarg" ;; --ipt-inc=*) IPTINC="$ac_optarg" ;; --kver=*) KVERSION="$ac_optarg" ;; --kdir=*) KDIR="$ac_optarg" ;; --make) echo called from make ;; --help) show_help ;; -*) echo Invalid option: $ac_option; exit 1 ;; # *) ni="$ni $ac_option" ;; esac done test "$KVERSION" || KVERSION=`uname -r` echo Kernel version: $KVERSION test "$KDIR" || KDIR=/lib/modules/$KVERSION/build echo Kernel sources: $KDIR test "$IPTBIN" || IPTBIN=`which iptables` iptables_find_version #IPTVER iptables_try_pkgconfig #try to configure from pkg-config iptables_find_src #IPTSRC iptables_src_version #check that IPTSRC match to IPTVER iptables_inc #IPTINC iptables_modules #IPTLIB REPLACE="\ s!@KVERSION@!$KVERSION!;\ s!@KDIR@!$KDIR!;\ s!@IPTABLES_VERSION@!$IPTVER!;\ s!@IPTABLES_CFLAGS@!$IPTCFLAGS $IPTINC!;\ s!@IPTABLES_MODULES@!$IPTLIB!" echo -n "Creating Makefile.. " sed "$REPLACE" Makefile.in > Makefile echo done. echo echo " Now run: make all install" echo kernel-source-ipt_netflow-1.8/ipt_NETFLOW.c000064400000000000000000001232461217147134200206360ustar00rootroot00000000000000/* * This is NetFlow exporting module (NETFLOW target) for linux * (c) 2008-2012 * * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ //#define RAW_PROMISC_HACK #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ipt_NETFLOW.h" #ifdef CONFIG_BRIDGE_NETFILTER #include #endif #ifdef CONFIG_SYSCTL #include #endif #ifndef NIPQUAD #define NIPQUAD(addr) \ ((unsigned char *)&addr)[0], \ ((unsigned char *)&addr)[1], \ ((unsigned char *)&addr)[2], \ ((unsigned char *)&addr)[3] #endif #ifndef HIPQUAD #if defined(__LITTLE_ENDIAN) #define HIPQUAD(addr) \ ((unsigned char *)&addr)[3], \ ((unsigned char *)&addr)[2], \ ((unsigned char *)&addr)[1], \ ((unsigned char *)&addr)[0] #elif defined(__BIG_ENDIAN) #define HIPQUAD NIPQUAD #else #error "Please fix asm/byteorder.h" #endif /* __LITTLE_ENDIAN */ #endif #ifndef IPT_CONTINUE #define IPT_CONTINUE XT_CONTINUE #define ipt_target xt_target #endif #define IPT_NETFLOW_VERSION "1.8" MODULE_LICENSE("GPL"); MODULE_AUTHOR(""); MODULE_DESCRIPTION("iptables NETFLOW target module"); MODULE_VERSION(IPT_NETFLOW_VERSION); #define DST_SIZE 256 static char destination_buf[DST_SIZE] = "127.0.0.1:2055"; static char *destination = destination_buf; module_param(destination, charp, 0400); MODULE_PARM_DESC(destination, "export destination ipaddress:port"); static int inactive_timeout = 15; module_param(inactive_timeout, int, 0600); MODULE_PARM_DESC(inactive_timeout, "inactive flows timeout in seconds"); static int active_timeout = 30 * 60; module_param(active_timeout, int, 0600); MODULE_PARM_DESC(active_timeout, "active flows timeout in seconds"); static int debug = 0; module_param(debug, int, 0600); MODULE_PARM_DESC(debug, "debug verbosity level"); static int sndbuf; module_param(sndbuf, int, 0400); MODULE_PARM_DESC(sndbuf, "udp socket SNDBUF size"); static int hashsize; module_param(hashsize, int, 0400); MODULE_PARM_DESC(hashsize, "hash table size"); static int maxflows = 2000000; module_param(maxflows, int, 0600); MODULE_PARM_DESC(maxflows, "maximum number of flows"); static int peakflows = 0; static unsigned long peakflows_at; #define AGGR_SIZE 1024 static char aggregation_buf[AGGR_SIZE] = ""; static char *aggregation = aggregation_buf; module_param(aggregation, charp, 0400); MODULE_PARM_DESC(aggregation, "aggregation ruleset"); static DEFINE_PER_CPU(struct ipt_netflow_stat, ipt_netflow_stat); static LIST_HEAD(usock_list); static DEFINE_RWLOCK(sock_lock); static unsigned int ipt_netflow_hash_rnd; struct hlist_head *ipt_netflow_hash __read_mostly; /* hash table memory */ static unsigned int ipt_netflow_hash_size __read_mostly = 0; /* buckets */ static LIST_HEAD(ipt_netflow_list); /* all flows */ static LIST_HEAD(aggr_n_list); static LIST_HEAD(aggr_p_list); static DEFINE_RWLOCK(aggr_lock); static struct kmem_cache *ipt_netflow_cachep __read_mostly; /* ipt_netflow memory */ static atomic_t ipt_netflow_count = ATOMIC_INIT(0); static DEFINE_SPINLOCK(ipt_netflow_lock); /* hash table lock */ static long long pdu_packets = 0, pdu_traf = 0; static struct netflow5_pdu pdu; static unsigned long pdu_ts_mod; #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) static void netflow_work_fn(void *work); static DECLARE_WORK(netflow_work, netflow_work_fn, NULL); #else static void netflow_work_fn(struct work_struct *work); static DECLARE_DELAYED_WORK(netflow_work, netflow_work_fn); #endif static struct timer_list rate_timer; #define TCP_FIN_RST 0x05 static long long sec_prate = 0, sec_brate = 0; static long long min_prate = 0, min_brate = 0; static long long min5_prate = 0, min5_brate = 0; static unsigned int metric = 10, min15_metric = 10, min5_metric = 10, min_metric = 10; /* hash metrics */ static int set_hashsize(int new_size); static void destination_removeall(void); static int add_destinations(char *ptr); static void aggregation_remove(struct list_head *list); static int add_aggregation(char *ptr); static void netflow_scan_and_export(int flush); static inline __be32 bits2mask(int bits) { return (bits? 0xffffffff << (32 - bits) : 0); } static inline int mask2bits(__be32 mask) { int n; for (n = 0; mask; n++) mask = (mask << 1) & 0xffffffff; return n; } /* under that lock worker is always stopped and not rescheduled, * and we can call worker sub-functions manually */ static DEFINE_MUTEX(worker_lock); static inline void __start_scan_worker(void) { schedule_delayed_work(&netflow_work, HZ / 10); } static inline void start_scan_worker(void) { __start_scan_worker(); mutex_unlock(&worker_lock); } /* we always stop scanner before write_lock(&sock_lock) * to let it never hold that spin lock */ static inline void __stop_scan_worker(void) { cancel_delayed_work_sync(&netflow_work); } static inline void stop_scan_worker(void) { mutex_lock(&worker_lock); __stop_scan_worker(); } #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) #define INIT_NET(x) x #else #define INIT_NET(x) init_net.x #endif #ifdef CONFIG_PROC_FS /* procfs statistics /proc/net/stat/ipt_netflow */ static int nf_seq_show(struct seq_file *seq, void *v) { unsigned int nr_flows = atomic_read(&ipt_netflow_count); int cpu; unsigned long long searched = 0, found = 0, notfound = 0; unsigned int truncated = 0, frags = 0, alloc_err = 0, maxflows_err = 0; unsigned int sock_errors = 0, send_failed = 0, send_success = 0; unsigned long long pkt_total = 0, traf_total = 0, exported_size = 0; unsigned long long pkt_drop = 0, traf_drop = 0; unsigned long long pkt_out = 0, traf_out = 0; struct ipt_netflow_sock *usock; struct netflow_aggr_n *aggr_n; struct netflow_aggr_p *aggr_p; int snum = 0; int peak = (jiffies - peakflows_at) / HZ; seq_printf(seq, "Flows: active %u (peak %u reached %ud%uh%um ago), mem %uK\n", nr_flows, peakflows, peak / (60 * 60 * 24), (peak / (60 * 60)) % 24, (peak / 60) % 60, (unsigned int)((nr_flows * sizeof(struct ipt_netflow)) >> 10)); for_each_present_cpu(cpu) { struct ipt_netflow_stat *st = &per_cpu(ipt_netflow_stat, cpu); searched += st->searched; found += st->found; notfound += st->notfound; truncated += st->truncated; frags += st->frags; alloc_err += st->alloc_err; maxflows_err += st->maxflows_err; send_success += st->send_success; send_failed += st->send_failed; sock_errors += st->sock_errors; exported_size += st->exported_size; pkt_total += st->pkt_total; traf_total += st->traf_total; pkt_drop += st->pkt_drop; traf_drop += st->traf_drop; pkt_out += st->pkt_out; traf_out += st->traf_out; } #define FFLOAT(x, prec) (int)(x) / prec, (int)(x) % prec seq_printf(seq, "Hash: size %u (mem %uK), metric %d.%d, %d.%d, %d.%d, %d.%d. MemTraf: %llu pkt, %llu K (pdu %llu, %llu).\n", ipt_netflow_hash_size, (unsigned int)((ipt_netflow_hash_size * sizeof(struct hlist_head)) >> 10), FFLOAT(metric, 10), FFLOAT(min_metric, 10), FFLOAT(min5_metric, 10), FFLOAT(min15_metric, 10), pkt_total - pkt_out + pdu_packets, (traf_total - traf_out + pdu_traf) >> 10, pdu_packets, pdu_traf); seq_printf(seq, "Timeout: active %d, inactive %d. Maxflows %u\n", active_timeout, inactive_timeout, maxflows); seq_printf(seq, "Rate: %llu bits/sec, %llu packets/sec; Avg 1 min: %llu bps, %llu pps; 5 min: %llu bps, %llu pps\n", sec_brate, sec_prate, min_brate, min_prate, min5_brate, min5_prate); seq_printf(seq, "cpu# stat: , sock: , traffic: , drop: \n"); seq_printf(seq, "Total stat: %6llu %6llu %6llu, %4u %4u %4u %4u, sock: %6u %u %u, %llu K, traffic: %llu, %llu MB, drop: %llu, %llu K\n", (unsigned long long)searched, (unsigned long long)found, (unsigned long long)notfound, truncated, frags, alloc_err, maxflows_err, send_success, send_failed, sock_errors, (unsigned long long)exported_size >> 10, (unsigned long long)pkt_total, (unsigned long long)traf_total >> 20, (unsigned long long)pkt_drop, (unsigned long long)traf_drop >> 10); if (num_present_cpus() > 1) { for_each_present_cpu(cpu) { struct ipt_netflow_stat *st; st = &per_cpu(ipt_netflow_stat, cpu); seq_printf(seq, "cpu%u stat: %6llu %6llu %6llu, %4u %4u %4u %4u, sock: %6u %u %u, %llu K, traffic: %llu, %llu MB, drop: %llu, %llu K\n", cpu, (unsigned long long)st->searched, (unsigned long long)st->found, (unsigned long long)st->notfound, st->truncated, st->frags, st->alloc_err, st->maxflows_err, st->send_success, st->send_failed, st->sock_errors, (unsigned long long)st->exported_size >> 10, (unsigned long long)st->pkt_total, (unsigned long long)st->traf_total >> 20, (unsigned long long)st->pkt_drop, (unsigned long long)st->traf_drop >> 10); } } read_lock(&sock_lock); list_for_each_entry(usock, &usock_list, list) { struct sock *sk = usock->sock->sk; seq_printf(seq, "sock%d: %u.%u.%u.%u:%u, sndbuf %u, filled %u, peak %u; err: sndbuf reached %u, other %u\n", snum, usock->ipaddr >> 24, (usock->ipaddr >> 16) & 255, (usock->ipaddr >> 8) & 255, usock->ipaddr & 255, usock->port, sk->sk_sndbuf, atomic_read(&sk->sk_wmem_alloc), atomic_read(&usock->wmem_peak), atomic_read(&usock->err_full), atomic_read(&usock->err_other)); snum++; } read_unlock(&sock_lock); read_lock_bh(&aggr_lock); snum = 0; list_for_each_entry(aggr_n, &aggr_n_list, list) { seq_printf(seq, "aggr#%d net: match %u.%u.%u.%u/%d strip %d\n", snum, HIPQUAD(aggr_n->addr), mask2bits(aggr_n->mask), mask2bits(aggr_n->aggr_mask)); snum++; } snum = 0; list_for_each_entry(aggr_p, &aggr_p_list, list) { seq_printf(seq, "aggr#%d port: ports %u-%u replace %u\n", snum, aggr_p->port1, aggr_p->port2, aggr_p->aggr_port); snum++; } read_unlock_bh(&aggr_lock); return 0; } static int nf_seq_open(struct inode *inode, struct file *file) { return single_open(file, nf_seq_show, NULL); } static struct file_operations nf_seq_fops = { .owner = THIS_MODULE, .open = nf_seq_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; #endif /* CONFIG_PROC_FS */ #ifdef CONFIG_SYSCTL #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32) #define BEFORE2632(x,y) x,y #else /* since 2.6.32 */ #define BEFORE2632(x,y) #endif /* sysctl /proc/sys/net/netflow */ static int hsize_procctl(ctl_table *ctl, int write, BEFORE2632(struct file *filp,) void __user *buffer, size_t *lenp, loff_t *fpos) { void *orig = ctl->data; int ret, hsize; if (write) ctl->data = &hsize; ret = proc_dointvec(ctl, write, BEFORE2632(filp,) buffer, lenp, fpos); if (write) { ctl->data = orig; if (hsize < 1) return -EPERM; return set_hashsize(hsize)?:ret; } else return ret; } static int sndbuf_procctl(ctl_table *ctl, int write, BEFORE2632(struct file *filp,) void __user *buffer, size_t *lenp, loff_t *fpos) { int ret; struct ipt_netflow_sock *usock; read_lock(&sock_lock); if (list_empty(&usock_list)) { read_unlock(&sock_lock); return -ENOENT; } usock = list_first_entry(&usock_list, struct ipt_netflow_sock, list); sndbuf = usock->sock->sk->sk_sndbuf; read_unlock(&sock_lock); ctl->data = &sndbuf; ret = proc_dointvec(ctl, write, BEFORE2632(filp,) buffer, lenp, fpos); if (!write) return ret; if (sndbuf < SOCK_MIN_SNDBUF) sndbuf = SOCK_MIN_SNDBUF; stop_scan_worker(); write_lock(&sock_lock); list_for_each_entry(usock, &usock_list, list) { usock->sock->sk->sk_sndbuf = sndbuf; } write_unlock(&sock_lock); start_scan_worker(); return ret; } static int destination_procctl(ctl_table *ctl, int write, BEFORE2632(struct file *filp,) void __user *buffer, size_t *lenp, loff_t *fpos) { int ret; ret = proc_dostring(ctl, write, BEFORE2632(filp,) buffer, lenp, fpos); if (ret >= 0 && write) { stop_scan_worker(); destination_removeall(); add_destinations(destination_buf); start_scan_worker(); } return ret; } static int aggregation_procctl(ctl_table *ctl, int write, BEFORE2632(struct file *filp,) void __user *buffer, size_t *lenp, loff_t *fpos) { int ret; if (debug > 1) printk(KERN_INFO "aggregation_procctl (%d) %u %llu\n", write, (unsigned int)(*lenp), *fpos); ret = proc_dostring(ctl, write, BEFORE2632(filp,) buffer, lenp, fpos); if (ret >= 0 && write) { add_aggregation(aggregation_buf); } return ret; } static int flush_procctl(ctl_table *ctl, int write, BEFORE2632(struct file *filp,) void __user *buffer, size_t *lenp, loff_t *fpos) { int ret; int val; val = 0; ctl->data = &val; ret = proc_dointvec(ctl, write, BEFORE2632(filp,) buffer, lenp, fpos); if (!write) return ret; if (val > 0) { printk(KERN_INFO "ipt_NETFLOW: forced flush\n"); stop_scan_worker(); netflow_scan_and_export(1); start_scan_worker(); } return ret; } static struct ctl_table_header *netflow_sysctl_header; #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) #define _CTL_NAME(x) .ctl_name = x, #else #define _CTL_NAME(x) #endif static struct ctl_table netflow_sysctl_table[] = { { _CTL_NAME(1) .procname = "active_timeout", .mode = 0644, .data = &active_timeout, .maxlen = sizeof(int), .proc_handler = &proc_dointvec, }, { _CTL_NAME(2) .procname = "inactive_timeout", .mode = 0644, .data = &inactive_timeout, .maxlen = sizeof(int), .proc_handler = &proc_dointvec, }, { _CTL_NAME(3) .procname = "debug", .mode = 0644, .data = &debug, .maxlen = sizeof(int), .proc_handler = &proc_dointvec, }, { _CTL_NAME(4) .procname = "hashsize", .mode = 0644, .data = &ipt_netflow_hash_size, .maxlen = sizeof(int), .proc_handler = &hsize_procctl, }, { _CTL_NAME(5) .procname = "sndbuf", .mode = 0644, .maxlen = sizeof(int), .proc_handler = &sndbuf_procctl, }, { _CTL_NAME(6) .procname = "destination", .mode = 0644, .data = &destination_buf, .maxlen = sizeof(destination_buf), .proc_handler = &destination_procctl, }, { _CTL_NAME(7) .procname = "aggregation", .mode = 0644, .data = &aggregation_buf, .maxlen = sizeof(aggregation_buf), .proc_handler = &aggregation_procctl, }, { _CTL_NAME(8) .procname = "maxflows", .mode = 0644, .data = &maxflows, .maxlen = sizeof(int), .proc_handler = &proc_dointvec, }, { _CTL_NAME(9) .procname = "flush", .mode = 0644, .maxlen = sizeof(int), .proc_handler = &flush_procctl, }, { } }; #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25) static struct ctl_table netflow_sysctl_root[] = { { _CTL_NAME(33) .procname = "netflow", .mode = 0555, .child = netflow_sysctl_table, }, { } }; static struct ctl_table netflow_net_table[] = { { .ctl_name = CTL_NET, .procname = "net", .mode = 0555, .child = netflow_sysctl_root, }, { } }; #else /* >= 2.6.25 */ static struct ctl_path netflow_sysctl_path[] = { { .procname = "net", #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33) .ctl_name = CTL_NET #endif }, { .procname = "netflow" }, { } }; #endif /* 2.6.25 */ #endif /* CONFIG_SYSCTL */ /* socket code */ static void sk_error_report(struct sock *sk) { /* clear connection refused errors if any */ write_lock_bh(&sk->sk_callback_lock); if (debug > 1) printk(KERN_INFO "NETFLOW: socket error <%d>\n", sk->sk_err); sk->sk_err = 0; NETFLOW_STAT_INC(sock_errors); write_unlock_bh(&sk->sk_callback_lock); return; } // return numbers of sends succeded, 0 if none /* only called in scan worker path */ static int netflow_send_pdu(void *buffer, int len) { struct msghdr msg = { .msg_flags = MSG_DONTWAIT|MSG_NOSIGNAL }; struct kvec iov = { buffer, len }; int retok = 0, ret; int snum = 0; struct ipt_netflow_sock *usock; list_for_each_entry(usock, &usock_list, list) { if (debug) printk(KERN_INFO "netflow_send_pdu: sendmsg(%d, %d) [%u %u]\n", snum, len, atomic_read(&usock->sock->sk->sk_wmem_alloc), usock->sock->sk->sk_sndbuf); ret = kernel_sendmsg(usock->sock, &msg, &iov, 1, (size_t)len); if (ret < 0) { char *suggestion = ""; NETFLOW_STAT_INC_ATOMIC(send_failed); if (ret == -EAGAIN) { atomic_inc(&usock->err_full); suggestion = ": increase sndbuf!"; } else atomic_inc(&usock->err_other); printk(KERN_ERR "netflow_send_pdu[%d]: sendmsg error %d: data loss %llu pkt, %llu bytes%s\n", snum, ret, pdu_packets, pdu_traf, suggestion); } else { unsigned int wmem = atomic_read(&usock->sock->sk->sk_wmem_alloc); if (wmem > atomic_read(&usock->wmem_peak)) atomic_set(&usock->wmem_peak, wmem); NETFLOW_STAT_INC_ATOMIC(send_success); NETFLOW_STAT_ADD_ATOMIC(exported_size, ret); retok++; } snum++; } return retok; } static void usock_free(struct ipt_netflow_sock *usock) { printk(KERN_INFO "netflow: remove destination %u.%u.%u.%u:%u (%p)\n", HIPQUAD(usock->ipaddr), usock->port, usock->sock); if (usock->sock) sock_release(usock->sock); usock->sock = NULL; vfree(usock); } static void destination_removeall(void) { write_lock(&sock_lock); while (!list_empty(&usock_list)) { struct ipt_netflow_sock *usock; usock = list_entry(usock_list.next, struct ipt_netflow_sock, list); list_del(&usock->list); write_unlock(&sock_lock); usock_free(usock); write_lock(&sock_lock); } write_unlock(&sock_lock); } static void add_usock(struct ipt_netflow_sock *usock) { struct ipt_netflow_sock *sk; /* don't need empty sockets */ if (!usock->sock) { usock_free(usock); return; } write_lock(&sock_lock); /* don't need duplicated sockets */ list_for_each_entry(sk, &usock_list, list) { if (sk->ipaddr == usock->ipaddr && sk->port == usock->port) { write_unlock(&sock_lock); usock_free(usock); return; } } list_add_tail(&usock->list, &usock_list); printk(KERN_INFO "netflow: added destination %u.%u.%u.%u:%u\n", HIPQUAD(usock->ipaddr), usock->port); write_unlock(&sock_lock); } static struct socket *usock_alloc(__be32 ipaddr, unsigned short port) { struct sockaddr_in sin; struct socket *sock; int error; if ((error = sock_create_kern(PF_INET, SOCK_DGRAM, IPPROTO_UDP, &sock)) < 0) { printk(KERN_ERR "netflow: sock_create_kern error %d\n", error); return NULL; } sock->sk->sk_allocation = GFP_ATOMIC; sock->sk->sk_prot->unhash(sock->sk); /* hidden from input */ sock->sk->sk_error_report = &sk_error_report; /* clear ECONNREFUSED */ if (sndbuf) sock->sk->sk_sndbuf = sndbuf; else sndbuf = sock->sk->sk_sndbuf; memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = htonl(ipaddr); sin.sin_port = htons(port); if ((error = sock->ops->connect(sock, (struct sockaddr *)&sin, sizeof(sin), 0)) < 0) { printk(KERN_ERR "netflow: error connecting UDP socket %d\n", error); sock_release(sock); return NULL; } return sock; } #define SEPARATORS " ,;\t\n" static int add_destinations(char *ptr) { while (ptr) { unsigned char ip[4]; unsigned short port; ptr += strspn(ptr, SEPARATORS); if (sscanf(ptr, "%hhu.%hhu.%hhu.%hhu:%hu", ip, ip + 1, ip + 2, ip + 3, &port) == 5) { struct ipt_netflow_sock *usock; if (!(usock = vmalloc(sizeof(*usock)))) { printk(KERN_ERR "netflow: can't vmalloc socket\n"); return -ENOMEM; } memset(usock, 0, sizeof(*usock)); usock->ipaddr = ntohl(*(__be32 *)ip); usock->port = port; usock->sock = usock_alloc(usock->ipaddr, port); atomic_set(&usock->wmem_peak, 0); atomic_set(&usock->err_full, 0); atomic_set(&usock->err_other, 0); add_usock(usock); } else break; ptr = strpbrk(ptr, SEPARATORS); } return 0; } static void aggregation_remove(struct list_head *list) { write_lock_bh(&aggr_lock); while (!list_empty(list)) { struct netflow_aggr_n *aggr; /* match netflow_aggr_p too */ aggr = list_entry(list->next, struct netflow_aggr_n, list); list_del(&aggr->list); write_unlock_bh(&aggr_lock); vfree(aggr); write_lock_bh(&aggr_lock); } write_unlock_bh(&aggr_lock); } static int add_aggregation(char *ptr) { struct netflow_aggr_n *aggr_n, *aggr, *tmp; struct netflow_aggr_p *aggr_p; LIST_HEAD(new_aggr_n_list); LIST_HEAD(new_aggr_p_list); LIST_HEAD(old_aggr_list); while (ptr && *ptr) { unsigned char ip[4]; unsigned int mask; unsigned int port1, port2; unsigned int aggr_to; ptr += strspn(ptr, SEPARATORS); if (sscanf(ptr, "%hhu.%hhu.%hhu.%hhu/%u=%u", ip, ip + 1, ip + 2, ip + 3, &mask, &aggr_to) == 6) { if (!(aggr_n = vmalloc(sizeof(*aggr_n)))) { printk(KERN_ERR "netflow: can't vmalloc aggr\n"); return -ENOMEM; } memset(aggr_n, 0, sizeof(*aggr_n)); aggr_n->addr = ntohl(*(__be32 *)ip); aggr_n->mask = bits2mask(mask); aggr_n->aggr_mask = bits2mask(aggr_to); aggr_n->prefix = mask; printk(KERN_INFO "netflow: add aggregation [%u.%u.%u.%u/%u=%u]\n", HIPQUAD(aggr_n->addr), mask, aggr_to); list_add_tail(&aggr_n->list, &new_aggr_n_list); } else if (sscanf(ptr, "%u-%u=%u", &port1, &port2, &aggr_to) == 3 || sscanf(ptr, "%u=%u", &port2, &aggr_to) == 2) { if (!(aggr_p = vmalloc(sizeof(*aggr_p)))) { printk(KERN_ERR "netflow: can't vmalloc aggr\n"); return -ENOMEM; } memset(aggr_p, 0, sizeof(*aggr_p)); aggr_p->port1 = port1; aggr_p->port2 = port2; aggr_p->aggr_port = aggr_to; printk(KERN_INFO "netflow: add aggregation [%u-%u=%u]\n", port1, port2, aggr_to); list_add_tail(&aggr_p->list, &new_aggr_p_list); } else { printk(KERN_ERR "netflow: bad aggregation rule: %s (ignoring)\n", ptr); break; } ptr = strpbrk(ptr, SEPARATORS); } /* swap lists */ write_lock_bh(&aggr_lock); list_for_each_entry_safe(aggr, tmp, &aggr_n_list, list) list_move(&aggr->list, &old_aggr_list); list_for_each_entry_safe(aggr, tmp, &aggr_p_list, list) list_move(&aggr->list, &old_aggr_list); list_for_each_entry_safe(aggr, tmp, &new_aggr_n_list, list) list_move_tail(&aggr->list, &aggr_n_list); list_for_each_entry_safe(aggr, tmp, &new_aggr_p_list, list) list_move_tail(&aggr->list, &aggr_p_list); write_unlock_bh(&aggr_lock); aggregation_remove(&old_aggr_list); return 0; } static inline u_int32_t hash_netflow(const struct ipt_netflow_tuple *tuple) { /* tuple is rounded to u32s */ return jhash2((u32 *)tuple, NETFLOW_TUPLE_SIZE, ipt_netflow_hash_rnd) % ipt_netflow_hash_size; } static struct ipt_netflow * ipt_netflow_find(const struct ipt_netflow_tuple *tuple, unsigned int hash) { struct ipt_netflow *nf; struct hlist_node *pos; hlist_for_each_entry(nf, pos, &ipt_netflow_hash[hash], hlist) { if (ipt_netflow_tuple_equal(tuple, &nf->tuple) && nf->nr_bytes < FLOW_FULL_WATERMARK) { NETFLOW_STAT_INC(found); return nf; } NETFLOW_STAT_INC(searched); } NETFLOW_STAT_INC(notfound); return NULL; } static struct hlist_head *alloc_hashtable(int size) { struct hlist_head *hash; hash = vmalloc(sizeof(struct hlist_head) * size); if (hash) { int i; for (i = 0; i < size; i++) INIT_HLIST_HEAD(&hash[i]); } else printk(KERN_ERR "netflow: unable to vmalloc hash table.\n"); return hash; } static int set_hashsize(int new_size) { struct hlist_head *new_hash, *old_hash; unsigned int hash; struct ipt_netflow *nf; int rnd; printk(KERN_INFO "netflow: allocating new hash table %u -> %u buckets\n", ipt_netflow_hash_size, new_size); new_hash = alloc_hashtable(new_size); if (!new_hash) return -ENOMEM; get_random_bytes(&rnd, 4); /* rehash */ spin_lock_bh(&ipt_netflow_lock); old_hash = ipt_netflow_hash; ipt_netflow_hash = new_hash; ipt_netflow_hash_size = new_size; ipt_netflow_hash_rnd = rnd; /* hash_netflow() is dependent on ipt_netflow_hash_* values */ list_for_each_entry(nf, &ipt_netflow_list, list) { hash = hash_netflow(&nf->tuple); /* hlist_add_head overwrites hlist pointers for this node * so it's good */ hlist_add_head(&nf->hlist, &new_hash[hash]); } spin_unlock_bh(&ipt_netflow_lock); vfree(old_hash); return 0; } static struct ipt_netflow * ipt_netflow_alloc(struct ipt_netflow_tuple *tuple) { struct ipt_netflow *nf; long count; nf = kmem_cache_alloc(ipt_netflow_cachep, GFP_ATOMIC); if (!nf) { printk(KERN_ERR "Can't allocate netflow.\n"); return NULL; } memset(nf, 0, sizeof(*nf)); nf->tuple = *tuple; count = atomic_inc_return(&ipt_netflow_count); if (count > peakflows) { peakflows = count; peakflows_at = jiffies; } return nf; } static void ipt_netflow_free(struct ipt_netflow *nf) { atomic_dec(&ipt_netflow_count); kmem_cache_free(ipt_netflow_cachep, nf); } static struct ipt_netflow * init_netflow(struct ipt_netflow_tuple *tuple, struct sk_buff *skb, unsigned int hash) { struct ipt_netflow *nf; nf = ipt_netflow_alloc(tuple); if (!nf) return NULL; hlist_add_head(&nf->hlist, &ipt_netflow_hash[hash]); list_add(&nf->list, &ipt_netflow_list); return nf; } /* cook pdu, send, and clean */ /* only called in scan worker path */ static void netflow_export_pdu(void) { struct timeval tv; int pdusize; if (!pdu.nr_records) return; if (debug > 1) printk(KERN_INFO "netflow_export_pdu with %d records\n", pdu.nr_records); do_gettimeofday(&tv); pdu.version = htons(5); pdu.ts_uptime = htonl(jiffies_to_msecs(jiffies)); pdu.ts_usecs = htonl(tv.tv_sec); pdu.ts_unsecs = htonl(tv.tv_usec); //pdu.eng_type = 0; //pdu.eng_id = 0; //pdu.padding = 0; pdusize = NETFLOW5_HEADER_SIZE + sizeof(struct netflow5_record) * pdu.nr_records; /* especially fix nr_records before export */ pdu.nr_records = htons(pdu.nr_records); if (netflow_send_pdu(&pdu, pdusize) == 0) { /* not least one send succeded, account stat for dropped packets */ NETFLOW_STAT_ADD_ATOMIC(pkt_drop, pdu_packets); NETFLOW_STAT_ADD_ATOMIC(traf_drop, pdu_traf); } pdu.seq = htonl(ntohl(pdu.seq) + ntohs(pdu.nr_records)); pdu.nr_records = 0; pdu_packets = 0; pdu_traf = 0; } /* only called in scan worker path */ static void netflow_export_flow(struct ipt_netflow *nf) { struct netflow5_record *rec; if (debug > 2) printk(KERN_INFO "adding flow to export (%d)\n", pdu.nr_records); pdu_packets += nf->nr_packets; pdu_traf += nf->nr_bytes; pdu_ts_mod = jiffies; rec = &pdu.flow[pdu.nr_records++]; /* make V5 flow record */ rec->s_addr = nf->tuple.s_addr; rec->d_addr = nf->tuple.d_addr; //rec->nexthop = 0; rec->i_ifc = htons(nf->tuple.i_ifc); rec->o_ifc = htons(nf->o_ifc); rec->nr_packets = htonl(nf->nr_packets); rec->nr_octets = htonl(nf->nr_bytes); rec->ts_first = htonl(jiffies_to_msecs(nf->ts_first)); rec->ts_last = htonl(jiffies_to_msecs(nf->ts_last)); rec->s_port = nf->tuple.s_port; rec->d_port = nf->tuple.d_port; //rec->reserved = 0; rec->tcp_flags = nf->tcp_flags; rec->protocol = nf->tuple.protocol; rec->tos = nf->tuple.tos; //rec->s_as = 0; //rec->d_as = 0; rec->s_mask = nf->s_mask; rec->d_mask = nf->d_mask; //rec->padding = 0; ipt_netflow_free(nf); if (pdu.nr_records == NETFLOW5_RECORDS_MAX) netflow_export_pdu(); } static inline int active_needs_export(struct ipt_netflow *nf, long a_timeout) { /* active too long, finishing, or having too much bytes */ return ((jiffies - nf->ts_first) > a_timeout) || (nf->tuple.protocol == IPPROTO_TCP && (nf->tcp_flags & TCP_FIN_RST) && (jiffies - nf->ts_last) > (1 * HZ)) || nf->nr_bytes >= FLOW_FULL_WATERMARK; } /* could be called with zero to flush cache and pdu */ /* this function is guaranteed to be called non-concurrently */ static void netflow_scan_and_export(int flush) { long i_timeout = inactive_timeout * HZ; long a_timeout = active_timeout * HZ; if (flush) i_timeout = 0; spin_lock_bh(&ipt_netflow_lock); while (!list_empty(&ipt_netflow_list)) { struct ipt_netflow *nf; nf = list_entry(ipt_netflow_list.prev, struct ipt_netflow, list); /* Note: i_timeout checked with >= to allow specifying zero timeout * to purge all flows on module unload */ if (((jiffies - nf->ts_last) >= i_timeout) || active_needs_export(nf, a_timeout)) { hlist_del(&nf->hlist); list_del(&nf->list); NETFLOW_STAT_ADD(pkt_out, nf->nr_packets); NETFLOW_STAT_ADD(traf_out, nf->nr_bytes); spin_unlock_bh(&ipt_netflow_lock); netflow_export_flow(nf); spin_lock_bh(&ipt_netflow_lock); } else { /* all flows which need to be exported is always at the tail * so if no more exportable flows we can break */ break; } } spin_unlock_bh(&ipt_netflow_lock); /* flush flows stored in pdu if there no new flows for too long */ /* Note: using >= to allow flow purge on zero timeout */ if ((jiffies - pdu_ts_mod) >= i_timeout) netflow_export_pdu(); } #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) static void netflow_work_fn(void *dummy) #else static void netflow_work_fn(struct work_struct *dummy) #endif { netflow_scan_and_export(0); __start_scan_worker(); } #define RATESHIFT 2 #define SAMPLERATE (RATESHIFT*RATESHIFT) #define NUMSAMPLES(minutes) (minutes * 60 / SAMPLERATE) #define _A(v, m) (v) * (1024 * 2 / (NUMSAMPLES(m) + 1)) >> 10 // x * (1024 / y) >> 10 is because I can not just divide long long integer #define CALC_RATE(ewma, cur, minutes) ewma += _A(cur - ewma, minutes) // calculate EWMA throughput rate for whole module static void rate_timer_calc(unsigned long dummy) { static u64 old_pkt_total = 0; static u64 old_traf_total = 0; static u64 old_searched = 0; static u64 old_found = 0; static u64 old_notfound = 0; u64 searched = 0; u64 found = 0; u64 notfound = 0; unsigned int dsrch, dfnd, dnfnd; u64 pkt_total = 0; u64 traf_total = 0; int cpu; for_each_present_cpu(cpu) { struct ipt_netflow_stat *st = &per_cpu(ipt_netflow_stat, cpu); pkt_total += st->pkt_total; traf_total += st->traf_total; searched += st->searched; found += st->found; notfound += st->notfound; } sec_prate = (pkt_total - old_pkt_total) >> RATESHIFT; CALC_RATE(min5_prate, sec_prate, 5); CALC_RATE(min_prate, sec_prate, 1); old_pkt_total = pkt_total; sec_brate = ((traf_total - old_traf_total) * 8) >> RATESHIFT; CALC_RATE(min5_brate, sec_brate, 5); CALC_RATE(min_brate, sec_brate, 1); old_traf_total = traf_total; dsrch = searched - old_searched; dfnd = found - old_found; dnfnd = notfound - old_notfound; old_searched = searched; old_found = found; old_notfound = notfound; /* if there is no access to hash keep rate steady */ metric = (dfnd + dnfnd)? 10 * (dsrch + dfnd + dnfnd) / (dfnd + dnfnd) : metric; CALC_RATE(min15_metric, (unsigned long long)metric, 15); CALC_RATE(min5_metric, (unsigned long long)metric, 5); CALC_RATE(min_metric, (unsigned long long)metric, 1); mod_timer(&rate_timer, jiffies + (HZ * SAMPLERATE)); } /* packet receiver */ static unsigned int netflow_target( #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) struct sk_buff **pskb, #else struct sk_buff *skb, #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) const struct net_device *if_in, const struct net_device *if_out, unsigned int hooknum, #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) const struct xt_target *target, #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) const void *targinfo, void *userinfo #else const void *targinfo #endif #else /* since 2.6.28 */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35) const struct xt_target_param *par #else const struct xt_action_param *par #endif #endif ) { #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) struct sk_buff *skb = *pskb; #endif struct iphdr _iph, *iph; struct ipt_netflow_tuple tuple; struct ipt_netflow *nf; __u8 tcp_flags; struct netflow_aggr_n *aggr_n; struct netflow_aggr_p *aggr_p; __u8 s_mask, d_mask; unsigned int hash; iph = skb_header_pointer(skb, 0, sizeof(_iph), &_iph); //iph = ip_hdr(skb); if (iph == NULL) { NETFLOW_STAT_INC(truncated); NETFLOW_STAT_INC(pkt_drop); return IPT_CONTINUE; } tuple.s_addr = iph->saddr; tuple.d_addr = iph->daddr; tuple.s_port = 0; tuple.d_port = 0; #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) tuple.i_ifc = if_in? if_in->ifindex : -1; #else tuple.i_ifc = par->in? par->in->ifindex : -1; #endif tuple.protocol = iph->protocol; tuple.tos = iph->tos; tcp_flags = 0; /* Cisco sometimes have TCP ACK for non TCP packets, don't get it */ s_mask = 0; d_mask = 0; if (iph->frag_off & htons(IP_OFFSET)) NETFLOW_STAT_INC(frags); else { switch (tuple.protocol) { case IPPROTO_TCP: { struct tcphdr _hdr, *hp; if ((hp = skb_header_pointer(skb, iph->ihl * 4, 14, &_hdr))) { tuple.s_port = hp->source; tuple.d_port = hp->dest; tcp_flags = (u_int8_t)(ntohl(tcp_flag_word(hp)) >> 16); } break; } case IPPROTO_UDP: { struct udphdr _hdr, *hp; if ((hp = skb_header_pointer(skb, iph->ihl * 4, 4, &_hdr))) { tuple.s_port = hp->source; tuple.d_port = hp->dest; } break; } case IPPROTO_ICMP: { struct icmphdr _hdr, *hp; if ((hp = skb_header_pointer(skb, iph->ihl * 4, 2, &_hdr))) tuple.d_port = (hp->type << 8) | hp->code; break; } case IPPROTO_IGMP: { struct igmphdr *_hdr, *hp; if ((hp = skb_header_pointer(skb, iph->ihl * 4, 1, &_hdr))) tuple.d_port = hp->type; } break; } } /* not fragmented */ /* aggregate networks */ read_lock_bh(&aggr_lock); list_for_each_entry(aggr_n, &aggr_n_list, list) if ((ntohl(tuple.s_addr) & aggr_n->mask) == aggr_n->addr) { tuple.s_addr &= htonl(aggr_n->aggr_mask); s_mask = aggr_n->prefix; break; } list_for_each_entry(aggr_n, &aggr_n_list, list) if ((ntohl(tuple.d_addr) & aggr_n->mask) == aggr_n->addr) { tuple.d_addr &= htonl(aggr_n->aggr_mask); d_mask = aggr_n->prefix; break; } /* aggregate ports */ list_for_each_entry(aggr_p, &aggr_p_list, list) if (ntohs(tuple.s_port) >= aggr_p->port1 && ntohs(tuple.s_port) <= aggr_p->port2) { tuple.s_port = htons(aggr_p->aggr_port); break; } list_for_each_entry(aggr_p, &aggr_p_list, list) if (ntohs(tuple.d_port) >= aggr_p->port1 && ntohs(tuple.d_port) <= aggr_p->port2) { tuple.d_port = htons(aggr_p->aggr_port); break; } read_unlock_bh(&aggr_lock); hash = hash_netflow(&tuple); spin_lock_bh(&ipt_netflow_lock); /* record */ nf = ipt_netflow_find(&tuple, hash); if (!nf) { if (maxflows > 0 && atomic_read(&ipt_netflow_count) >= maxflows) { /* This is DOS attack prevention */ NETFLOW_STAT_INC(maxflows_err); NETFLOW_STAT_INC(pkt_drop); NETFLOW_STAT_ADD(traf_drop, ntohs(iph->tot_len)); spin_unlock_bh(&ipt_netflow_lock); return IPT_CONTINUE; } nf = init_netflow(&tuple, skb, hash); if (!nf || IS_ERR(nf)) { NETFLOW_STAT_INC(alloc_err); NETFLOW_STAT_INC(pkt_drop); NETFLOW_STAT_ADD(traf_drop, ntohs(iph->tot_len)); spin_unlock_bh(&ipt_netflow_lock); return IPT_CONTINUE; } nf->ts_first = jiffies; nf->tcp_flags = tcp_flags; #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) nf->o_ifc = if_out? if_out->ifindex : -1; #else nf->o_ifc = par->out? par->out->ifindex : -1; #endif nf->s_mask = s_mask; nf->d_mask = d_mask; if (debug > 2) printk(KERN_INFO "ipt_netflow: new (%u) %hd:%hd SRC=%u.%u.%u.%u:%u DST=%u.%u.%u.%u:%u\n", atomic_read(&ipt_netflow_count), tuple.i_ifc, nf->o_ifc, NIPQUAD(tuple.s_addr), ntohs(tuple.s_port), NIPQUAD(tuple.d_addr), ntohs(tuple.d_port)); } else { /* ipt_netflow_list is sorted by access time: * most recently accessed flows are at head, old flows remain at tail * this function bubble up flow to the head */ list_move(&nf->list, &ipt_netflow_list); } nf->nr_packets++; nf->nr_bytes += ntohs(iph->tot_len); nf->ts_last = jiffies; nf->tcp_flags |= tcp_flags; NETFLOW_STAT_INC(pkt_total); NETFLOW_STAT_ADD(traf_total, ntohs(iph->tot_len)); if (active_needs_export(nf, active_timeout * HZ)) { /* ok, if this active flow to be exported * bubble it to the tail */ list_move_tail(&nf->list, &ipt_netflow_list); /* Blog: I thought about forcing timer to wake up sooner if we have * enough exportable flows, but in fact this doesn't have much sense, * becasue this would only move flow data from one memory to another * (from our buffers to socket buffers, and socket buffers even have * limited size). But yes, this is disputable. */ } spin_unlock_bh(&ipt_netflow_lock); return IPT_CONTINUE; } static struct ipt_target ipt_netflow_reg = { .name = "NETFLOW", .target = netflow_target, .family = AF_INET, #ifndef RAW_PROMISC_HACK .table = "filter", #ifndef NF_IP_LOCAL_IN /* 2.6.25 */ .hooks = (1 << NF_INET_LOCAL_IN) | (1 << NF_INET_FORWARD) | (1 << NF_INET_LOCAL_OUT), #else .hooks = (1 << NF_IP_LOCAL_IN) | (1 << NF_IP_FORWARD) | (1 << NF_IP_LOCAL_OUT), #endif /* NF_IP_LOCAL_IN */ #else .table = "raw", #ifndef NF_IP_LOCAL_IN .hooks = (1 << NF_INET_LOCAL_IN) | (1 << NF_INET_FORWARD) | (1 << NF_INET_LOCAL_OUT) | (1 << NF_INET_PRE_ROUTING), #else .hooks = (1 << NF_IP_LOCAL_IN) | (1 << NF_IP_FORWARD) | (1 << NF_IP_LOCAL_OUT) | (1 << NF_IP_PRE_ROUTING), #endif /* NF_IP_LOCAL_IN */ #endif /* !RAW_PROMISC_HACK */ .me = THIS_MODULE }; static int __init ipt_netflow_init(void) { #ifdef CONFIG_PROC_FS struct proc_dir_entry *proc_stat; #endif get_random_bytes(&ipt_netflow_hash_rnd, 4); /* determine hash size (idea from nf_conntrack_core.c) */ if (!hashsize) { hashsize = (((num_physpages << PAGE_SHIFT) / 16384) / sizeof(struct hlist_head)); if (num_physpages > (1024 * 1024 * 1024 / PAGE_SIZE)) hashsize = 8192; } if (hashsize < 16) hashsize = 16; printk(KERN_INFO "ipt_netflow version %s (%u buckets)\n", IPT_NETFLOW_VERSION, hashsize); ipt_netflow_hash_size = hashsize; ipt_netflow_hash = alloc_hashtable(ipt_netflow_hash_size); if (!ipt_netflow_hash) { printk(KERN_ERR "Unable to create ipt_neflow_hash\n"); goto err; } ipt_netflow_cachep = kmem_cache_create("ipt_netflow", sizeof(struct ipt_netflow), 0, 0, NULL #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23) , NULL #endif ); if (!ipt_netflow_cachep) { printk(KERN_ERR "Unable to create ipt_netflow slab cache\n"); goto err_free_hash; } #ifdef CONFIG_PROC_FS #if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0) proc_stat = create_proc_entry("ipt_netflow", S_IRUGO, INIT_NET(proc_net_stat)); if (!proc_stat) { printk(KERN_ERR "Unable to create /proc/net/stat/ipt_netflow entry\n"); goto err_free_netflow_slab; } proc_stat->proc_fops = &nf_seq_fops; #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30) proc_stat->owner = THIS_MODULE; #endif printk(KERN_INFO "netflow: registered: /proc/net/stat/ipt_netflow\n"); #endif #else proc_stat = proc_create("ipt_netflow", S_IRUGO, INIT_NET(proc_net_stat),&nf_seq_fops); if (!proc_stat) { printk(KERN_ERR "Unable to create /proc/net/stat/ipt_netflow entry\n"); goto err_free_netflow_slab; } #endif #ifdef CONFIG_SYSCTL #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25) netflow_sysctl_header = register_sysctl_table(netflow_net_table #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21) , 0 /* insert_at_head */ #endif ); #else /* 2.6.25 */ netflow_sysctl_header = register_sysctl_paths(netflow_sysctl_path, netflow_sysctl_table); #endif if (!netflow_sysctl_header) { printk(KERN_ERR "netflow: can't register to sysctl\n"); goto err_free_proc_stat; } else printk(KERN_INFO "netflow: registered: sysctl net.netflow\n"); #endif if (!destination) destination = aggregation_buf; if (destination != destination_buf) { strlcpy(destination_buf, destination, sizeof(destination_buf)); destination = destination_buf; } if (add_destinations(destination) < 0) goto err_free_sysctl; if (!aggregation) aggregation = aggregation_buf; if (aggregation != aggregation_buf) { strlcpy(aggregation_buf, aggregation, sizeof(aggregation_buf)); aggregation = aggregation_buf; } add_aggregation(aggregation); __start_scan_worker(); setup_timer(&rate_timer, rate_timer_calc, 0); mod_timer(&rate_timer, jiffies + (HZ * SAMPLERATE)); if (xt_register_target(&ipt_netflow_reg)) goto err_stop_timer; peakflows_at = jiffies; printk(KERN_INFO "ipt_netflow loaded.\n"); return 0; err_stop_timer: __stop_scan_worker(); del_timer_sync(&rate_timer); destination_removeall(); aggregation_remove(&aggr_n_list); aggregation_remove(&aggr_p_list); err_free_sysctl: #ifdef CONFIG_SYSCTL unregister_sysctl_table(netflow_sysctl_header); err_free_proc_stat: #endif #ifdef CONFIG_PROC_FS remove_proc_entry("ipt_netflow", INIT_NET(proc_net_stat)); err_free_netflow_slab: #endif kmem_cache_destroy(ipt_netflow_cachep); err_free_hash: vfree(ipt_netflow_hash); err: return -ENOMEM; } static void __exit ipt_netflow_fini(void) { printk(KERN_INFO "ipt_netflow unloading..\n"); #ifdef CONFIG_SYSCTL unregister_sysctl_table(netflow_sysctl_header); #endif #ifdef CONFIG_PROC_FS remove_proc_entry("ipt_netflow", INIT_NET(proc_net_stat)); #endif xt_unregister_target(&ipt_netflow_reg); __stop_scan_worker(); netflow_scan_and_export(1); del_timer_sync(&rate_timer); synchronize_sched(); destination_removeall(); aggregation_remove(&aggr_n_list); aggregation_remove(&aggr_p_list); kmem_cache_destroy(ipt_netflow_cachep); vfree(ipt_netflow_hash); printk(KERN_INFO "ipt_netflow unloaded.\n"); } module_init(ipt_netflow_init); module_exit(ipt_netflow_fini); /* vim: set sw=8: */ kernel-source-ipt_netflow-1.8/ipt_NETFLOW.h000064400000000000000000000115171217147134200206400ustar00rootroot00000000000000/* * This file is part of NetFlow exporting module. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef _IP_NETFLOW_H #define _IP_NETFLOW_H /* * Some tech info: * http://www.cisco.com/en/US/products/ps6601/prod_white_papers_list.html * http://www.cisco.com/en/US/products/sw/netmgtsw/ps1964/products_implementation_design_guide09186a00800d6a11.html */ #define NETFLOW5_RECORDS_MAX 30 struct netflow5_record { __be32 s_addr; __be32 d_addr; __be32 nexthop; __be16 i_ifc; __be16 o_ifc; __be32 nr_packets; __be32 nr_octets; __be32 ts_first; __be32 ts_last; __be16 s_port; __be16 d_port; __u8 reserved; __u8 tcp_flags; __u8 protocol; __u8 tos; __be16 s_as; __be16 d_as; __u8 s_mask; __u8 d_mask; __u16 padding; } __attribute__ ((packed)); /* NetFlow v5 packet */ struct netflow5_pdu { __be16 version; __be16 nr_records; __be32 ts_uptime; __be32 ts_usecs; __be32 ts_unsecs; __be32 seq; __u8 eng_type; __u8 eng_id; __u16 padding; struct netflow5_record flow[NETFLOW5_RECORDS_MAX]; } __attribute__ ((packed)); #define NETFLOW5_HEADER_SIZE (sizeof(struct netflow5_pdu) - NETFLOW5_RECORDS_MAX * sizeof(struct netflow5_record)) /* hashed data which identify unique flow */ struct ipt_netflow_tuple { __be32 s_addr; // Network byte order __be32 d_addr; // -"- __be16 s_port; // -"- __be16 d_port; // -"- __be16 i_ifc; // Local byte order __u8 protocol; __u8 tos; }; /* tuple size is rounded to u32s */ #define NETFLOW_TUPLE_SIZE (sizeof(struct ipt_netflow_tuple) / 4) /* maximum bytes flow can have, after it reached flow become not searchable and will be exported soon */ #define FLOW_FULL_WATERMARK 0xffefffff /* flow entry */ struct ipt_netflow { struct hlist_node hlist; // hashtable search chain struct list_head list; // all flows chain /* unique per flow data (hashed, NETFLOW_TUPLE_SIZE) */ struct ipt_netflow_tuple tuple; /* volatile data */ __be16 o_ifc; __u8 s_mask; __u8 d_mask; /* flow statistics */ u_int32_t nr_packets; u_int32_t nr_bytes; unsigned long ts_first; unsigned long ts_last; __u8 tcp_flags; /* `OR' of all tcp flags */ }; static inline int ipt_netflow_tuple_equal(const struct ipt_netflow_tuple *t1, const struct ipt_netflow_tuple *t2) { return (!memcmp(t1, t2, sizeof(struct ipt_netflow_tuple))); } struct ipt_netflow_sock { struct list_head list; struct socket *sock; __be32 ipaddr; unsigned short port; atomic_t wmem_peak; // sk_wmem_alloc peak value atomic_t err_full; // socket filled error atomic_t err_other; // other socket errors }; struct netflow_aggr_n { struct list_head list; __u32 mask; __u32 addr; __u32 aggr_mask; __u8 prefix; }; struct netflow_aggr_p { struct list_head list; __u16 port1; __u16 port2; __u16 aggr_port; }; #define NETFLOW_STAT_INC(count) (__get_cpu_var(ipt_netflow_stat).count++) #define NETFLOW_STAT_ADD(count, val) (__get_cpu_var(ipt_netflow_stat).count += (unsigned long long)val) #define NETFLOW_STAT_INC_ATOMIC(count) \ do { \ preempt_disable(); \ (__get_cpu_var(ipt_netflow_stat).count++); \ preempt_enable(); \ } while(0); #define NETFLOW_STAT_ADD_ATOMIC(count, val) \ do { \ preempt_disable(); \ (__get_cpu_var(ipt_netflow_stat).count += (unsigned long long)val); \ preempt_enable(); \ } while(0); /* statistics */ struct ipt_netflow_stat { u64 searched; // hash stat u64 found; // hash stat u64 notfound; // hash stat unsigned int truncated; // packets stat unsigned int frags; // packets stat unsigned int alloc_err; // failed to allocate flow mem unsigned int maxflows_err; // maxflows reached unsigned int send_success; // sendmsg() ok unsigned int send_failed; // sendmsg() failed unsigned int sock_errors; // socket error callback called (got icmp refused) u64 exported_size; // netflow traffic itself u64 pkt_total; // packets accounted total u64 traf_total; // traffic accounted total u64 pkt_drop; // packets not accounted total u64 traf_drop; // traffic not accounted total u64 pkt_out; // packets out of the memory u64 traf_out; // traffic out of the memory }; #ifndef list_first_entry #define list_first_entry(ptr, type, member) \ list_entry((ptr)->next, type, member) #endif #endif /* vim: set sw=8: */ kernel-source-ipt_netflow-1.8/kernel-source-ipt_netflow.spec000064400000000000000000000017621217147134200244600ustar00rootroot00000000000000Name: kernel-source-ipt_netflow Version: 1.8 Release: alt2 Summary: Netflow iptables module for Linux kernel License: GPL Group: Development/Kernel URL: http://sourceforge.net/projects/ipt-netflow/ Packager: Kernel Maintainer Team Source0: %name-%version.tar BuildArch: noarch BuildPreReq: rpm-build-kernel %description Ipt-netflow is very fast and effective Netflow exporting module for Linux kernel. Designed for Linux router with heavy network load. This is netfilter/iptables module adding support for NETFLOW target. %prep %setup -q -c %install mkdir -p %kernel_srcdir mv %name-%version ipt_netflow-%version tar -cjf %kernel_srcdir/ipt_netflow-%version.tar.bz2 ipt_netflow-%version %files %attr(0644,root,root) %kernel_src/ipt_netflow-%version.tar.bz2 %changelog * Wed Jul 17 2013 Anton V. Boyarshinov 1.8-alt2 - support for kernel 3.10 added * Tue May 07 2013 Anton V. Boyarshinov 1.8-alt1 - build as separate module kernel-source-ipt_netflow-1.8/libipt_NETFLOW.c000064400000000000000000000052731217147134200213240ustar00rootroot00000000000000/* * iptables helper for NETFLOW target * * * * This file is part of NetFlow exporting module. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include #include #include #include #include #include #include #include #define __EXPORTED_HEADERS__ #ifdef XTABLES #include #else #include #endif #ifdef XTABLES_VERSION_CODE // since 1.4.1 #define MOD140 #define iptables_target xtables_target #endif #ifdef iptables_target // only in 1.4.0 #define MOD140 #endif #ifdef MOD140 #define ipt_entry_target xt_entry_target #define register_target xtables_register_target #define _IPT_ENTRY void #define _IPT_IP void #ifndef IPT_ALIGN #define IPT_ALIGN XT_ALIGN #endif #else // before 1.3.x #define _IPT_ENTRY struct ipt_entry #define _IPT_IP struct ipt_ip #endif static struct option opts[] = { {0} }; static void help(void) { printf( "NETFLOW target\n"); } //static int parse(int c, char **argv, int invert, unsigned int *flags, // const _IPT_ENTRY *entry, // struct ipt_entry_target **target) static int parse(int c, char **argv, int invert, unsigned int *flags, const _IPT_ENTRY *entry, struct ipt_entry_target **targetinfo) { return 1; } static void final_check(unsigned int flags) { } static void save(const _IPT_IP *ip, const struct ipt_entry_target *match) { } static void print(const _IPT_IP *ip, const struct ipt_entry_target *target, int numeric) { printf("NETFLOW "); } static struct iptables_target netflow = { #ifdef MOD140 .family = AF_INET, #endif .next = NULL, .name = "NETFLOW", #ifdef XTABLES_VERSION .version = XTABLES_VERSION, #else .version = IPTABLES_VERSION, #endif .size = IPT_ALIGN(0), .userspacesize = IPT_ALIGN(0), .help = &help, .parse = &parse, .final_check = &final_check, .print = &print, .save = &save, .extra_opts = opts }; #ifndef _init #define _init __attribute__((constructor)) _INIT #endif void _init(void) { register_target(&netflow); } kernel-source-ipt_netflow-1.8/raw_promisc.patch000064400000000000000000000014761217147134200220460ustar00rootroot00000000000000 This simple hack will allow to see promisc traffic in raw table of iptables. Of course you will need to enable promisc on the interface. Refer to README.promisc for details. Example how to catch desired traffic: iptables -A PREROUTING -t raw -i eth2 -j NETFLOW --- linux-2.6.26/net/ipv4/ip_input.old.c 2008-07-14 01:51:29.000000000 +0400 +++ linux-2.6.26/net/ipv4/ip_input.c 2008-08-06 14:02:16.000000000 +0400 @@ -378,12 +378,6 @@ struct iphdr *iph; u32 len; - /* When the interface is in promisc. mode, drop all the crap - * that it receives, do not try to analyse it. - */ - if (skb->pkt_type == PACKET_OTHERHOST) - goto drop; - IP_INC_STATS_BH(IPSTATS_MIB_INRECEIVES); if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) { kernel-source-ipt_netflow-1.8/raw_promisc_debian_squeeze6.patch000064400000000000000000000022551217147134200251730ustar00rootroot00000000000000 Short manual and patch for Debian Squeeze suggested by Pavel Odintsov: On Thu, Dec 27, 2012 at 07:46:30PM +0400, Pavel Odintsov wrote: > > краткий мануал для патчинга Debian Squeeze ядра патчем promisc. > > cd /usr/src > apt-get install -y dpkg-dev > apt-get build-dep linux-image-2.6.32-5-amd64 > cd linux-2.6-2.6.32/ > apt-get source linux-image-2.6.32-5-amd64 > > wget .... /root/raw_promisc_debian_squeeze6.patch > patch -p1 < raw_promisc_debian_squeeze6.patch > Накладываем патчи дебияна: > debian/rules source > > Запускаем сборку: > debian/rules binary > diff -rupN linux-2.6-2.6.32/net/ipv4/ip_input.c linux-2.6-2.6.32_promisc_raw//net/ipv4/ip_input.c --- linux-2.6-2.6.32/net/ipv4/ip_input.c 2009-12-03 04:51:21.000000000 +0100 +++ linux-2.6-2.6.32_promisc_raw//net/ipv4/ip_input.c 2012-06-25 19:13:49.000000000 +0200 @@ -383,8 +383,8 @@ int ip_rcv(struct sk_buff *skb, struct n /* When the interface is in promisc. mode, drop all the crap * that it receives, do not try to analyse it. */ - if (skb->pkt_type == PACKET_OTHERHOST) - goto drop; + //if (skb->pkt_type == PACKET_OTHERHOST) + // goto drop; IP_UPD_PO_STATS_BH(dev_net(dev), IPSTATS_MIB_IN, skb->len);