aboutsummaryrefslogtreecommitdiffstats
path: root/testing
diff options
context:
space:
mode:
authorNatanael Copa <ncopa@alpinelinux.org>2015-11-09 14:51:07 +0000
committerNatanael Copa <ncopa@alpinelinux.org>2015-11-09 14:56:51 +0000
commitae6b767aea6d26e0ec1f8f59bc935806795a1992 (patch)
treea6eee58ac998882f18d0f7e481f3811d58bc2e3f /testing
parentab4196ca0174dab98f2df12d27040c75540a9fd6 (diff)
downloadaports-ae6b767aea6d26e0ec1f8f59bc935806795a1992.tar.bz2
aports-ae6b767aea6d26e0ec1f8f59bc935806795a1992.tar.xz
testing/ipt-netflow-grsec: fix build for 4.1 kernel
Diffstat (limited to 'testing')
-rw-r--r--testing/ipt-netflow-grsec/APKBUILD13
-rw-r--r--testing/ipt-netflow-grsec/git.patch3143
2 files changed, 3151 insertions, 5 deletions
diff --git a/testing/ipt-netflow-grsec/APKBUILD b/testing/ipt-netflow-grsec/APKBUILD
index 1f1ad5e502..a14591bd8a 100644
--- a/testing/ipt-netflow-grsec/APKBUILD
+++ b/testing/ipt-netflow-grsec/APKBUILD
@@ -27,10 +27,10 @@ pkgver=$_kver
pkgrel=$(( $_kpkgrel + $_mypkgrel ))
pkgdesc="Linux kernel netflow sensor module"
url="http://ipt-netflow.sourceforge.net/"
-#arch="all"
-arch=""
+arch="all"
license=GPL3+
source="ipt-netflow-$_ver.tar.gz::https://github.com/aabc/ipt-netflow/archive/v$_ver.tar.gz
+ git.patch
"
depends="$_kpkg-dev=$_kpkgver"
makedepends="linux-${_flavor}-dev=$_kpkgver iptables-dev bash"
@@ -64,6 +64,9 @@ dev() {
default_dev
}
-md5sums="bd8fc7d609c12832f5052f342640a839 ipt-netflow-2.1.tar.gz"
-sha256sums="a3eafe7a09f09e15d330539fab983b88c887db56e5614bc820619e8462bc7749 ipt-netflow-2.1.tar.gz"
-sha512sums="4ec48cb4612bd99d06ed2832d165fcddaf265f0c9555c7c49e012375c38c72cd57d5ee194a6b11e32eecfd2750e710a2bd167f3f2b2597b2e880a3f9b13ba27e ipt-netflow-2.1.tar.gz"
+md5sums="bd8fc7d609c12832f5052f342640a839 ipt-netflow-2.1.tar.gz
+98c5286c6471e47d3bb25026ac406b30 git.patch"
+sha256sums="a3eafe7a09f09e15d330539fab983b88c887db56e5614bc820619e8462bc7749 ipt-netflow-2.1.tar.gz
+4d45daecb82ca29a513ebaed7c9bab91f0d16fa7834b52a7ff06dc0d1cd4cd3a git.patch"
+sha512sums="4ec48cb4612bd99d06ed2832d165fcddaf265f0c9555c7c49e012375c38c72cd57d5ee194a6b11e32eecfd2750e710a2bd167f3f2b2597b2e880a3f9b13ba27e ipt-netflow-2.1.tar.gz
+3714c78ae8e8ca432a3216c2067ec00f64f1e25985b39996907f4638835509ab587605937c81d823a9dc95cf3b963a0119eade75a3f2286d425aa0d333a44bc9 git.patch"
diff --git a/testing/ipt-netflow-grsec/git.patch b/testing/ipt-netflow-grsec/git.patch
new file mode 100644
index 0000000000..643da202c6
--- /dev/null
+++ b/testing/ipt-netflow-grsec/git.patch
@@ -0,0 +1,3143 @@
+diff --git a/CREDITS b/CREDITS
+index 51e736e..e3fb344 100644
+--- a/CREDITS
++++ b/CREDITS
+@@ -48,6 +48,12 @@ Principal author and project maintainer:
+ ABC <abc@telekom.ru> [2008-2015]
+
+
++Compatibility layer is using code from Linux Kernel and should be
++attributed to respective Linux developers.
++
++MurmurHash3 is based on smhasher (2012) of Austin Appleby.
++
++
+ Patch authors and submitters:
+
+ Ilya Evseev [2010]
+@@ -82,6 +88,7 @@ Extensive testing and other help:
+ Andrew Savin @ Starlink [2014]
+ Alexander Zakharov @ WAW Technologies [2015]
+ Ivanov Eduard [2015]
++ Maciej Zdeb [2015]
+
+
+ (Send your names, emails, or nicks to add to the list.)
+diff --git a/Makefile.in b/Makefile.in
+index e96d50b..d72cb90 100644
+--- a/Makefile.in
++++ b/Makefile.in
+@@ -20,15 +20,18 @@ ccflags-y = @KOPTS@
+
+ all: ipt_NETFLOW.ko libipt_NETFLOW.so libip6t_NETFLOW.so @SNMPTARGET@
+
+-ipt_NETFLOW.ko: version.h ipt_NETFLOW.c ipt_NETFLOW.h Makefile
++ipt_NETFLOW.ko: version.h ipt_NETFLOW.c ipt_NETFLOW.h compat.h Makefile
+ @echo Compiling for kernel $(KVERSION)
+ make -C $(KDIR) M=$(CURDIR) modules CONFIG_DEBUG_INFO=y
+ @touch $@
+-sparse: | version.h ipt_NETFLOW.c ipt_NETFLOW.h Makefile
++sparse: | version.h ipt_NETFLOW.c ipt_NETFLOW.h compat.h Makefile
+ @rm -f ipt_NETFLOW.ko ipt_NETFLOW.o
+ @echo Compiling for kernel $(KVERSION)
+ make -C $(KDIR) M=$(CURDIR) modules C=1
+ @touch ipt_NETFLOW.ko
++coverity:
++ coverity-submit -v
++
+ minstall: | ipt_NETFLOW.ko
+ @echo " *"
+ make -C $(KDIR) M=$(CURDIR) modules_install INSTALL_MOD_PATH=$(DESTDIR)
+@@ -66,7 +69,7 @@ sinstall: | snmp_NETFLOW.so IPT-NETFLOW-MIB.my
+ %.so: %_sh.o
+ gcc -shared -o $@ $<
+
+-version.h: ipt_NETFLOW.c ipt_NETFLOW.h Makefile
++version.h: ipt_NETFLOW.c ipt_NETFLOW.h compat.h Makefile
+ @./version.sh --define > version.h
+
+ linstall: | libipt_NETFLOW.so libip6t_NETFLOW.so
+@@ -113,3 +116,8 @@ unload:
+ -rmmod ipt_NETFLOW.ko
+
+ reload: unload load
++
++ChangeLog:
++ gitlog-to-changelog > ChangeLog
++.PHONY: ChangeLog
++
+diff --git a/README b/README
+index d524b17..c82cdf7 100644
+--- a/README
++++ b/README
+@@ -1,4 +1,4 @@
+-ipt_NETFLOW linux 2.6.x-3.x kernel module by <abc@telekom.ru> -- 2008-2015.
++ipt_NETFLOW linux 2.6.x-4.x kernel module by <abc@telekom.ru> -- 2008-2015.
+
+ High performance NetFlow v5, v9, IPFIX flow data export module for Linux
+ kernel. Created to be useful for highly loaded linux router. It should be
+@@ -39,9 +39,9 @@ ipt_NETFLOW linux 2.6.x-3.x kernel module by <abc@telekom.ru> -- 2008-2015.
+ Such as metering, exporting, sampling stat and reliability stat, sampling
+ configuration, network devices ifName, ifDescr list.
+
+- * Tested to compile and work out of the box on Centos 5, 6, 7, and Debian.
+- Many vanilla Linux kernels since 2.6.18 up to the latest (as of writing
+- is 3.16) are supported and tested.
++ * Tested to compile and work out of the box on Centos 5, 6, 7, Debian and
++ * Ubuntu. Many vanilla Linux kernels since 2.6.18 up to the latest (as of
++ * writing is 3.19) are supported and tested.
+
+ * Module load time and run-time (via sysctl) configuration.
+
+@@ -52,14 +52,15 @@ ipt_NETFLOW linux 2.6.x-3.x kernel module by <abc@telekom.ru> -- 2008-2015.
+ * SNMP-index translation rules, let convert meaningless and unstable
+ interface indexes (ifIndex) to more meaningful numbering scheme.
+
+- * Easy support for catching mirrored traffic with promisc option.
++ * Easy support for catching mirrored traffic with promisc option. Which is
++ also supporting optional MPLS decapsulation and MPLS-aware NetFlow.
+
+
+ ============================
+ = OBTAINING LATEST VERSION =
+ ============================
+
+- $ git clone git://git.code.sf.net/p/ipt-netflow/code ipt-netflow
++ $ git clone git://github.com/aabc/ipt-netflow.git ipt-netflow
+ $ cd ipt-netflow
+
+
+@@ -79,7 +80,7 @@ ipt_NETFLOW linux 2.6.x-3.x kernel module by <abc@telekom.ru> -- 2008-2015.
+
+ ~# yum install kernel-devel
+
+- b) What to do for Debian:
++ b) What to do for Debian and Ubuntu:
+
+ ~# apt-get install module-assistant
+ ~# m-a prepare
+@@ -118,7 +119,7 @@ ipt_NETFLOW linux 2.6.x-3.x kernel module by <abc@telekom.ru> -- 2008-2015.
+
+ # yum install iptables-devel
+
+- b) What to do for Debian:
++ b) What to do for Debian or Ubuntu:
+
+ # apt-get install iptables-dev pkg-config
+
+@@ -134,7 +135,7 @@ ipt_NETFLOW linux 2.6.x-3.x kernel module by <abc@telekom.ru> -- 2008-2015.
+
+ # yum install net-snmp net-snmp-devel
+
+- b) For Debian:
++ b) For Debian or Ubuntu:
+
+ # apt-get install snmpd libsnmp-dev
+
+@@ -161,6 +162,14 @@ ipt_NETFLOW linux 2.6.x-3.x kernel module by <abc@telekom.ru> -- 2008-2015.
+ c) If you have sources in non-standard places or configure isn't able to
+ find something run ./configure --help to see how to specify paths manually.
+
++ d) To run irqtop on Debian 8 you may need to install:
++
++ # apt-get install ruby ruby-dev ncurses-dev
++ # gem install curses
++
++ z) If all fails create ticket at
++ https://github.com/aabc/ipt-netflow/issues
++
+ ** 5. After this point you should be able to load module and
+ use -j NETFLOW target in your iptables. See next section.
+
+@@ -227,6 +236,25 @@ ipt_NETFLOW linux 2.6.x-3.x kernel module by <abc@telekom.ru> -- 2008-2015.
+ only disable auto-install into DKMS, but still create dkms.conf, in
+ case you will want to install it manually.
+
++ --enable-physdev
++ Export ingressPhysicalInterface(252) and egressPhysicalInterface(253)
++ (relevant for bridges) in V9 and IPFIX. If your collector does not
++ support these Elements but you still need physdevs then use
++ --enable-physdev-override, in that case physdevs will override normal
++ interface numbers ingressInterface(10) and egressInterface(14).
++
++ --enable-promisc
++ Enables capturing of promiscuous packets into raw/PREROUTING chain.
++ See README.promisc Solution 1 for usage details and example.
++
++ --promisc-mpls
++ Enables MPLS label stack decapsulation for promiscuous packets. (For
++ IPv4 and IPv6 packets only). This also enables MPLS-aware NetFlow (v9
++ and IPFIX), you may wish to specify with --promisc-mpls=n how much MPLS
++ labels you want to be recorded and exported (default is 3, maximum is
++ 10, set to 0 to not report anything).
++
++
+ ===========
+ = RUNNING =
+ ===========
+@@ -381,10 +409,15 @@ ipt_NETFLOW linux 2.6.x-3.x kernel module by <abc@telekom.ru> -- 2008-2015.
+ because NetFlow v5 isn't compatible with IPv6.
+
+ destination=127.0.0.1:2055
+- - where to export netflow, to this ip address
+- You will see this connection in netstat like this:
++ - where to export netflow, to this ip address. Port is optional, default
++ is 2055. You will see this connection in netstat like this:
++
+ udp 0 0 127.0.0.1:32772 127.0.0.1:2055 ESTABLISHED
+
++ destination=[2001:db8::1]:2055
++ - export target using IPv6 address. Brackets are optional, but otherwise
++ you should delimit port with 'p' or '#' character.
++
+ destination=127.0.0.1:2055,192.0.0.1:2055
+ - mirror flows to two (can be more) addresses, separate addresses
+ with comma.
+@@ -523,13 +556,25 @@ ipt_NETFLOW linux 2.6.x-3.x kernel module by <abc@telekom.ru> -- 2008-2015.
+ snmp-rules are compilation disabled by default, to enable you will need
+ to add --enable-snmp option to ./configure script.
+
+- scan-min = 1
++ scan-min=1
+ - Minimal interval between flow export scans. Sometimes could be useful
+ to reduce load on exporting CPU by increasing this interval. Value are
+ in kernel jiffies units (which is x/HZ seconds).
+
+- promisc = 1
+- - Enable promisc hack. See README.promisc Solution.1 for details.
++ promisc=1
++ - Enables promisc hack. See README.promisc Solution 1 for details.
++
++ exportcpu=number
++ - Lock exporter to single CPU. This may be useful to fine control CPU
++ load. Common use case: with smp_affinity and RSS you spread packet
++ processing to all CPUs except one, and lock it to the exporter. While
++ exporter CPU load generally is not high, for someone it may be not
++ desirable to combine it with packet processing on very highly loaded
++ routers.
++
++ This option also could be changed at runtime with:
++
++ # echo number > /sys/module/ipt_NETFLOW/parameters/exportcpu
+
+
+ ====================
+@@ -722,17 +767,26 @@ ipt_NETFLOW linux 2.6.x-3.x kernel module by <abc@telekom.ru> -- 2008-2015.
+ IP TOS: ipClassOfService(5),
+ and address family (IP or IPv6).
+
+- If VLAN exporting is enabled:
++ Additional Flow Keys if VLAN exporting is enabled:
++
+ First (outer) dot1q VLAN tag: dot1qVlanId(243) and
+ dot1qPriority(244) for IPFIX,
+ or vlanId(243) for NetFlow v9.
+ Second (customer) dot1q VLAN tag: dot1qCustomerVlanId(245)
+ and dot1qCustomerPriority(246).
+
+- If MAC address exporting is enabled:
++ Additional Flow Keys if MAC address exporting is enabled:
++
+ Destination MAC address: destinationMacAddress(80),
+ Source MAC address: sourceMacAddress(56).
+
++ Additional Flow Keys if MPLS-aware NetFlow is enabled:
++
++ Captured MPLS stack is fully treated as flow key (including TTL values),
++ which is Elements from mplsTopLabelStackSection(70) to
++ mplsLabelStackSection10(79), and, if present, mplsTopLabelTTL(200).
++
++
+ Other Elements are not Flow Keys. Note that outer interface, which is
+ egressInterface(14), is not regarded as Flow Key. Quoting RFC 7012: "For
+ Information Elements ... for which the value may change from packet to packet
+diff --git a/compat.h b/compat.h
+new file mode 100644
+index 0000000..2b05994
+--- /dev/null
++++ b/compat.h
+@@ -0,0 +1,598 @@
++/* This code is derived from the Linux Kernel sources intended
++ * to maintain compatibility with different Kernel versions.
++ * Copyright of original source is of respective Linux Kernel authors.
++ * License is GPLv2.
++ */
++
++#ifndef COMPAT_NETFLOW_H
++#define COMPAT_NETFLOW_H
++
++
++#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
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
++union nf_inet_addr {
++ __be32 ip;
++ __be32 ip6[4];
++ struct in_addr in;
++ struct in6_addr in6;
++};
++#endif
++
++#ifndef list_first_entry
++#define list_first_entry(ptr, type, member) \
++ list_entry((ptr)->next, type, member)
++#endif
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
++# define INIT_NET(x) x
++#else
++# define INIT_NET(x) init_net.x
++#endif
++
++#ifndef ETH_P_8021AD
++# define ETH_P_8021AD 0x88A8 /* 802.1ad Service VLAN */
++#endif
++
++#ifndef ETH_P_QINQ1
++# define ETH_P_QINQ1 0x9100 /* deprecated QinQ VLAN */
++# define ETH_P_QINQ2 0x9200 /* deprecated QinQ VLAN */
++# define ETH_P_QINQ3 0x9300 /* deprecated QinQ VLAN */
++#endif
++
++#ifndef IPPROTO_MH
++# define IPPROTO_MH 135
++#endif
++
++#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
++
++# if LINUX_VERSION_CODE >= KERNEL_VERSION(3,17,0)
++# define ctl_table struct ctl_table
++# endif
++
++# ifndef HAVE_GRSECURITY_H
++# define ctl_table_no_const ctl_table
++# endif
++#endif
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(3,9,0)
++# define compat_hlist_for_each_entry hlist_for_each_entry
++# define compat_hlist_for_each_entry_safe hlist_for_each_entry_safe
++#else /* since 3.9.0 */
++# define compat_hlist_for_each_entry(a,pos,c,d) hlist_for_each_entry(a,c,d)
++# define compat_hlist_for_each_entry_safe(a,pos,c,d,e) hlist_for_each_entry_safe(a,c,d,e)
++#endif
++
++#ifndef WARN_ONCE
++#define WARN_ONCE(x,fmt...) ({ if (x) printk(KERN_WARNING fmt); })
++#endif
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
++# define IPPROTO_UDPLITE 136
++#endif
++
++#ifndef time_is_before_jiffies
++# define time_is_before_jiffies(a) time_after(jiffies, a)
++#endif
++#ifndef time_is_after_jiffies
++# define time_is_after_jiffies(a) time_before(jiffies, a)
++#endif
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(3,14,0)
++# if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
++# define prandom_u32 get_random_int
++# elif LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0)
++# define prandom_u32 random32
++#endif
++#define prandom_u32_max compat_prandom_u32_max
++static inline u32 prandom_u32_max(u32 ep_ro)
++{
++ return (u32)(((u64) prandom_u32() * ep_ro) >> 32);
++}
++#endif
++
++#ifndef min_not_zero
++# define min_not_zero(x, y) ({ \
++ typeof(x) __x = (x); \
++ typeof(y) __y = (y); \
++ __x == 0 ? __y : ((__y == 0) ? __x : min(__x, __y)); })
++#endif
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(3,2,0)
++static int __ethtool_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
++{
++ ASSERT_RTNL();
++
++ if (!dev->ethtool_ops->get_settings)
++ return -EOPNOTSUPP;
++
++ memset(cmd, 0, sizeof(struct ethtool_cmd));
++ cmd->cmd = ETHTOOL_GSET;
++ return dev->ethtool_ops->get_settings(dev, cmd);
++}
++#endif
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27)
++# define ethtool_cmd_speed(x) (x)->speed
++#endif
++
++#ifndef ARPHRD_PHONET
++# define ARPHRD_PHONET 820
++# define ARPHRD_PHONET_PIPE 821
++#endif
++#ifndef ARPHRD_IEEE802154
++# define ARPHRD_IEEE802154 804
++#endif
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)
++# define for_each_netdev_ns(net, dev) for (dev = dev_base; dev; dev = dev->next)
++#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
++# define for_each_netdev_ns(net, d) for_each_netdev(d)
++#else
++# define for_each_netdev_ns(net, d) for_each_netdev(net, d)
++#endif
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35)
++# define CHECK_FAIL 0
++# define CHECK_OK 1
++#else
++# define CHECK_FAIL -EINVAL
++# define CHECK_OK 0
++#endif
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)
++# define use_module ref_module
++#endif
++
++#ifndef NF_IP_LOCAL_IN /* 2.6.25 */
++# define NF_IP_PRE_ROUTING NF_INET_PRE_ROUTING
++# define NF_IP_LOCAL_IN NF_INET_LOCAL_IN
++# define NF_IP_FORWARD NF_INET_FORWARD
++# define NF_IP_LOCAL_OUT NF_INET_LOCAL_OUT
++# define NF_IP_POST_ROUTING NF_INET_POST_ROUTING
++#endif
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
++/* net/netfilter/x_tables.c */
++static void xt_unregister_targets(struct xt_target *target, unsigned int n)
++{
++ unsigned int i;
++
++ for (i = 0; i < n; i++)
++ xt_unregister_target(&target[i]);
++}
++static int xt_register_targets(struct xt_target *target, unsigned int n)
++{
++ unsigned int i;
++
++ int err = 0;
++ for (i = 0; i < n; i++)
++ if ((err = xt_register_target(&target[i])))
++ goto err;
++ return err;
++err:
++ if (i > 0)
++ xt_unregister_targets(target, i);
++ return err;
++}
++#endif
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0)
++#define num_physpages totalram_pages
++#endif
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35)
++# ifdef ktime_to_timeval
++/* ktime_to_timeval is defined on 64bit and inline on 32bit cpu */
++/* when it's defined it calls ns_to_timeval, which is not exported */
++struct timeval portable_ns_to_timeval(const s64 nsec)
++{
++ struct timespec ts = ns_to_timespec(nsec);
++ struct timeval tv;
++
++ tv.tv_sec = ts.tv_sec;
++ tv.tv_usec = (suseconds_t) ts.tv_nsec / 1000;
++
++ return tv;
++}
++# define ns_to_timeval portable_ns_to_timeval
++# endif
++
++static inline s64 portable_ktime_to_ms(const ktime_t kt)
++{
++ struct timeval tv = ktime_to_timeval(kt);
++ return (s64) tv.tv_sec * MSEC_PER_SEC + tv.tv_usec / USEC_PER_MSEC;
++}
++# define ktime_to_ms portable_ktime_to_ms
++#endif /* before 2.6.35 */
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)
++static inline s64 portable_ktime_to_us(const ktime_t kt)
++{
++ struct timeval tv = ktime_to_timeval(kt);
++ return (s64) tv.tv_sec * USEC_PER_SEC + tv.tv_usec;
++}
++#define ktime_to_us portable_ktime_to_us
++#endif
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26)
++static inline void put_unaligned_be16(u16 val, void *p)
++{
++ put_unaligned(cpu_to_be16(val), (__be16 *)p);
++}
++static inline void put_unaligned_be32(u32 val, void *p)
++{
++ put_unaligned(cpu_to_be32(val), (__be32 *)p);
++}
++static inline void put_unaligned_be64(u64 val, void *p)
++{
++ put_unaligned(cpu_to_be64(val), (__be64 *)p);
++}
++#endif
++
++#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,24) && !defined(RHEL_MAJOR)
++static void *__seq_open_private(struct file *f, struct seq_operations *ops,
++ int psize)
++{
++ int rc;
++ void *private;
++ struct seq_file *seq;
++
++ private = kzalloc(psize, GFP_KERNEL);
++ if (private == NULL)
++ goto out;
++
++ rc = seq_open(f, ops);
++ if (rc < 0)
++ goto out_free;
++
++ seq = f->private_data;
++ seq->private = private;
++ return private;
++
++out_free:
++ kfree(private);
++out:
++ return NULL;
++}
++#endif
++
++/* disappeared in v3.19 */
++#ifndef __get_cpu_var
++#define __get_cpu_var(var) (*this_cpu_ptr(&(var)))
++#endif
++
++#ifndef MPLS_HLEN
++#define MPLS_HLEN 4
++static inline int eth_p_mpls(__be16 eth_type)
++{
++ return eth_type == htons(ETH_P_MPLS_UC) ||
++ eth_type == htons(ETH_P_MPLS_MC);
++}
++#endif
++#ifndef MPLS_LS_S_MASK
++struct mpls_label {
++ __be32 entry;
++};
++#define MPLS_LS_S_MASK 0x00000100
++
++#endif
++
++/* sockaddr comparison functions is from fs/nfs/client.c */
++static int sockaddr_match_ipaddr6(const struct sockaddr *sa1, const struct sockaddr *sa2)
++{
++ const struct sockaddr_in6 *sin1 = (const struct sockaddr_in6 *)sa1;
++ const struct sockaddr_in6 *sin2 = (const struct sockaddr_in6 *)sa2;
++
++ if (!ipv6_addr_equal(&sin1->sin6_addr, &sin2->sin6_addr))
++ return 0;
++#if 0
++ else if (ipv6_addr_type(&sin1->sin6_addr) & IPV6_ADDR_LINKLOCAL)
++ return sin1->sin6_scope_id == sin2->sin6_scope_id;
++#endif
++ return 1;
++}
++
++static int sockaddr_match_ipaddr4(const struct sockaddr *sa1, const struct sockaddr *sa2)
++{
++ const struct sockaddr_in *sin1 = (const struct sockaddr_in *)sa1;
++ const struct sockaddr_in *sin2 = (const struct sockaddr_in *)sa2;
++
++ return sin1->sin_addr.s_addr == sin2->sin_addr.s_addr;
++}
++
++static int sockaddr_cmp_ip6(const struct sockaddr *sa1, const struct sockaddr *sa2)
++{
++ const struct sockaddr_in6 *sin1 = (const struct sockaddr_in6 *)sa1;
++ const struct sockaddr_in6 *sin2 = (const struct sockaddr_in6 *)sa2;
++
++ return sockaddr_match_ipaddr6(sa1, sa2) &&
++ (sin1->sin6_port == sin2->sin6_port);
++}
++
++static int sockaddr_cmp_ip4(const struct sockaddr *sa1, const struct sockaddr *sa2)
++{
++ const struct sockaddr_in *sin1 = (const struct sockaddr_in *)sa1;
++ const struct sockaddr_in *sin2 = (const struct sockaddr_in *)sa2;
++
++ return sockaddr_match_ipaddr4(sa1, sa2) &&
++ (sin1->sin_port == sin2->sin_port);
++}
++
++static int sockaddr_cmp(const struct sockaddr_storage *sa1, const struct sockaddr_storage *sa2)
++{
++ const struct sockaddr *s1 = (const struct sockaddr *)sa1;
++ const struct sockaddr *s2 = (const struct sockaddr *)sa2;
++
++ if (sa1->ss_family != sa2->ss_family)
++ return 0;
++
++ switch (sa1->ss_family) {
++ case AF_INET:
++ return sockaddr_cmp_ip4(s1, s2);
++ case AF_INET6:
++ return sockaddr_cmp_ip6(s1, s2);
++ }
++ return 0;
++}
++
++#ifndef IN6PTON_XDIGIT
++#define hex_to_bin compat_hex_to_bin
++/* lib/hexdump.c */
++int hex_to_bin(char ch)
++{
++ if ((ch >= '0') && (ch <= '9'))
++ return ch - '0';
++ ch = tolower(ch);
++ if ((ch >= 'a') && (ch <= 'f'))
++ return ch - 'a' + 10;
++ return -1;
++}
++
++/* net/core/utils.c */
++#define IN6PTON_XDIGIT 0x00010000
++#define IN6PTON_DIGIT 0x00020000
++#define IN6PTON_COLON_MASK 0x00700000
++#define IN6PTON_COLON_1 0x00100000 /* single : requested */
++#define IN6PTON_COLON_2 0x00200000 /* second : requested */
++#define IN6PTON_COLON_1_2 0x00400000 /* :: requested */
++#define IN6PTON_DOT 0x00800000 /* . */
++#define IN6PTON_DELIM 0x10000000
++#define IN6PTON_NULL 0x20000000 /* first/tail */
++#define IN6PTON_UNKNOWN 0x40000000
++
++static inline int xdigit2bin(char c, int delim)
++{
++ int val;
++
++ if (c == delim || c == '\0')
++ return IN6PTON_DELIM;
++ if (c == ':')
++ return IN6PTON_COLON_MASK;
++ if (c == '.')
++ return IN6PTON_DOT;
++
++ val = hex_to_bin(c);
++ if (val >= 0)
++ return val | IN6PTON_XDIGIT | (val < 10 ? IN6PTON_DIGIT : 0);
++
++ if (delim == -1)
++ return IN6PTON_DELIM;
++ return IN6PTON_UNKNOWN;
++}
++
++int in4_pton(const char *src, int srclen,
++ u8 *dst,
++ int delim, const char **end)
++{
++ const char *s;
++ u8 *d;
++ u8 dbuf[4];
++ int ret = 0;
++ int i;
++ int w = 0;
++
++ if (srclen < 0)
++ srclen = strlen(src);
++ s = src;
++ d = dbuf;
++ i = 0;
++ while(1) {
++ int c;
++ c = xdigit2bin(srclen > 0 ? *s : '\0', delim);
++ if (!(c & (IN6PTON_DIGIT | IN6PTON_DOT | IN6PTON_DELIM | IN6PTON_COLON_MASK))) {
++ goto out;
++ }
++ if (c & (IN6PTON_DOT | IN6PTON_DELIM | IN6PTON_COLON_MASK)) {
++ if (w == 0)
++ goto out;
++ *d++ = w & 0xff;
++ w = 0;
++ i++;
++ if (c & (IN6PTON_DELIM | IN6PTON_COLON_MASK)) {
++ if (i != 4)
++ goto out;
++ break;
++ }
++ goto cont;
++ }
++ w = (w * 10) + c;
++ if ((w & 0xffff) > 255) {
++ goto out;
++ }
++cont:
++ if (i >= 4)
++ goto out;
++ s++;
++ srclen--;
++ }
++ ret = 1;
++ memcpy(dst, dbuf, sizeof(dbuf));
++out:
++ if (end)
++ *end = s;
++ return ret;
++}
++
++int in6_pton(const char *src, int srclen,
++ u8 *dst,
++ int delim, const char **end)
++{
++ const char *s, *tok = NULL;
++ u8 *d, *dc = NULL;
++ u8 dbuf[16];
++ int ret = 0;
++ int i;
++ int state = IN6PTON_COLON_1_2 | IN6PTON_XDIGIT | IN6PTON_NULL;
++ int w = 0;
++
++ memset(dbuf, 0, sizeof(dbuf));
++
++ s = src;
++ d = dbuf;
++ if (srclen < 0)
++ srclen = strlen(src);
++
++ while (1) {
++ int c;
++
++ c = xdigit2bin(srclen > 0 ? *s : '\0', delim);
++ if (!(c & state))
++ goto out;
++ if (c & (IN6PTON_DELIM | IN6PTON_COLON_MASK)) {
++ /* process one 16-bit word */
++ if (!(state & IN6PTON_NULL)) {
++ *d++ = (w >> 8) & 0xff;
++ *d++ = w & 0xff;
++ }
++ w = 0;
++ if (c & IN6PTON_DELIM) {
++ /* We've processed last word */
++ break;
++ }
++ /*
++ * COLON_1 => XDIGIT
++ * COLON_2 => XDIGIT|DELIM
++ * COLON_1_2 => COLON_2
++ */
++ switch (state & IN6PTON_COLON_MASK) {
++ case IN6PTON_COLON_2:
++ dc = d;
++ state = IN6PTON_XDIGIT | IN6PTON_DELIM;
++ if (dc - dbuf >= sizeof(dbuf))
++ state |= IN6PTON_NULL;
++ break;
++ case IN6PTON_COLON_1|IN6PTON_COLON_1_2:
++ state = IN6PTON_XDIGIT | IN6PTON_COLON_2;
++ break;
++ case IN6PTON_COLON_1:
++ state = IN6PTON_XDIGIT;
++ break;
++ case IN6PTON_COLON_1_2:
++ state = IN6PTON_COLON_2;
++ break;
++ default:
++ state = 0;
++ }
++ tok = s + 1;
++ goto cont;
++ }
++
++ if (c & IN6PTON_DOT) {
++ ret = in4_pton(tok ? tok : s, srclen + (int)(s - tok), d, delim, &s);
++ if (ret > 0) {
++ d += 4;
++ break;
++ }
++ goto out;
++ }
++
++ w = (w << 4) | (0xff & c);
++ state = IN6PTON_COLON_1 | IN6PTON_DELIM;
++ if (!(w & 0xf000)) {
++ state |= IN6PTON_XDIGIT;
++ }
++ if (!dc && d + 2 < dbuf + sizeof(dbuf)) {
++ state |= IN6PTON_COLON_1_2;
++ state &= ~IN6PTON_DELIM;
++ }
++ if (d + 2 >= dbuf + sizeof(dbuf)) {
++ state &= ~(IN6PTON_COLON_1|IN6PTON_COLON_1_2);
++ }
++cont:
++ if ((dc && d + 4 < dbuf + sizeof(dbuf)) ||
++ d + 4 == dbuf + sizeof(dbuf)) {
++ state |= IN6PTON_DOT;
++ }
++ if (d >= dbuf + sizeof(dbuf)) {
++ state &= ~(IN6PTON_XDIGIT|IN6PTON_COLON_MASK);
++ }
++ s++;
++ srclen--;
++ }
++
++ i = 15; d--;
++
++ if (dc) {
++ while(d >= dc)
++ dst[i--] = *d--;
++ while(i >= dc - dbuf)
++ dst[i--] = 0;
++ while(i >= 0)
++ dst[i--] = *d--;
++ } else
++ memcpy(dst, dbuf, sizeof(dbuf));
++
++ ret = 1;
++out:
++ if (end)
++ *end = s;
++ return ret;
++}
++#endif /* IN6PTON_XDIGIT */
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,2,0)
++# define sock_create_kern(f, t, p, s) sock_create_kern(&init_net, f, t, p, s)
++#endif
++
++#if !defined(vlan_tx_tag_get) && defined(skb_vlan_tag_get)
++# define vlan_tx_tag_get skb_vlan_tag_get
++# define vlan_tx_tag_present skb_vlan_tag_present
++#endif
++
++#ifndef NF_HOOK
++# define NF_HOOK_COMPAT NF_HOOK
++#else
++# define NF_HOOK_COMPAT(a,b,c,d,e,f,g) NF_HOOK(a,b,d,e,f,g)
++#endif
++
++#endif /* COMPAT_NETFLOW_H */
+diff --git a/configure b/configure
+index 30a928c..1278c61 100755
+--- a/configure
++++ b/configure
+@@ -263,7 +263,6 @@ show_help() {
+ echo " --kver=.. kernel version (ex.: 2.6.30-std-def-alt15)"
+ echo " --kdir=.. directory for kernel source (ex.: /usr/src/kernel)"
+ echo " --enable-natevents enables natevents support"
+- echo " --enable-debugfs enables debugfs support for flow lists"
+ echo " --enable-snmp-rules enables SNMP-index conversion rules"
+ echo " --enable-macaddress enables MAC address for v9/IPFIX"
+ echo " --enable-vlan enables VLAN Ids for v9/IPFIX"
+@@ -271,7 +270,11 @@ show_help() {
+ echo " --enable-sampler enables Flow Sampling"
+ echo " --enable-sampler=hash enables Hash sampler"
+ echo " --enable-aggregation enables aggregation rules"
+- echo " --enable-promisc enables promisc hack"
++ echo " --enable-promisc enables promisc hack mode"
++ echo " --promisc-mpls decapsulate MPLS in promisc mode"
++ echo " --promisc-mpls=N -- and record N labels (default 3)"
++ echo " --enable-physdev enables physdev reporting"
++ echo " --enable-physdev-override to override interfaces"
+ echo " --disable-snmp-agent disables net-snmp agent"
+ echo " --disable-dkms disables DKMS support completely"
+ echo " --disable-dkms-install no DKMS install but still create dkms.conf"
+@@ -295,15 +298,17 @@ do
+ --kver=*) KVERSION="$ac_optarg" ;;
+ --kdir=*) KDIR="$ac_optarg" ;;
+ --enable-nat*) KOPTS="$KOPTS -DENABLE_NAT" ;;
+- --enable-debug*) KOPTS="$KOPTS -DENABLE_DEBUGFS" ;;
+ --enable-mac*) KOPTS="$KOPTS -DENABLE_MAC" ;;
+ --enable-vlan*) KOPTS="$KOPTS -DENABLE_VLAN" ;;
+ --enable-direc*) KOPTS="$KOPTS -DENABLE_DIRECTION" ;;
+ --enable-sampl*hash) KOPTS="$KOPTS -DENABLE_SAMPLER -DSAMPLING_HASH" ;;
+ --enable-sampl*) KOPTS="$KOPTS -DENABLE_SAMPLER" ;;
+ --enable-aggr*) KOPTS="$KOPTS -DENABLE_AGGR" ;;
+- --enable-promi*) KOPTS="$KOPTS -DENABLE_PROMISC" ;;
++ --enable-promi*) ENABLE_PROMISC=1 ;;
++ --promisc-mpls*) ENABLE_PROMISC=1; PROMISC_MPLS=1; MPLS_DEPTH="$ac_optarg" ;;
+ --enable-snmp-r*) KOPTS="$KOPTS -DENABLE_SNMP" ;;
++ --enable-physdev) KOPTS="$KOPTS -DENABLE_PHYSDEV" ;;
++ --enable-physdev-over*) KOPTS="$KOPTS -DENABLE_PHYSDEV_OVER" ;;
+ --disable-snmp-a*) SKIPSNMP=1 ;;
+ --disable-net-snmp*) SKIPSNMP=1 ;;
+ --disable-dkms*) SKIPDKMS=1 ;;
+@@ -316,6 +321,18 @@ do
+ esac
+ done
+
++if [ "$ENABLE_PROMISC" = 1 ]; then KOPTS="$KOPTS -DENABLE_PROMISC"; fi
++if [ "$PROMISC_MPLS" = 1 ]; then
++ KOPTS="$KOPTS -DPROMISC_MPLS"
++ if [ "$MPLS_DEPTH" -gt 10 ]; then
++ echo "! Requested MPLS stack depth is too big, limiting to 10."
++ MPLS_DEPTH=10;
++ elif [ ! "$MPLS_DEPTH" ]; then
++ MPLS_DEPTH=3
++ fi
++ if [ "$MPLS_DEPTH" -ge 1 ]; then KOPTS="$KOPTS -DMPLS_DEPTH=$MPLS_DEPTH"; fi
++fi
++
+ kernel_find_version() {
+ KHOW=requested
+ test "$KVERSION" && return 0
+@@ -436,6 +453,7 @@ kernel_check_include() {
+
+ kernel_check_features() {
+ kernel_check_include include/linux/llist.h -DHAVE_LLIST
++ kernel_check_include include/linux/grsecurity.h -DHAVE_GRSECURITY_H
+ }
+
+ snmp_check() {
+diff --git a/ipt_NETFLOW.c b/ipt_NETFLOW.c
+index f6bd7a8..2d5e95b 100644
+--- a/ipt_NETFLOW.c
++++ b/ipt_NETFLOW.c
+@@ -26,6 +26,8 @@
+ #include <linux/seq_file.h>
+ #include <linux/random.h>
+ #include <linux/in6.h>
++#include <linux/inet.h>
++#include <linux/kernel.h>
+ #include <linux/ip.h>
+ #include <linux/udp.h>
+ #include <linux/icmp.h>
+@@ -51,69 +53,37 @@
+ #ifndef ENABLE_NAT
+ # undef CONFIG_NF_NAT_NEEDED
+ #endif
+-#ifdef ENABLE_VLAN
+-#include <linux/if_vlan.h>
++#if defined(ENABLE_VLAN) || defined(ENABLE_PROMISC)
++# include <linux/if_vlan.h>
+ #endif
+ #ifdef ENABLE_MAC
+-#include <linux/if_ether.h>
+-#include <linux/etherdevice.h>
++# include <linux/if_ether.h>
++# include <linux/etherdevice.h>
+ #endif
+ #if defined(CONFIG_NF_NAT_NEEDED)
+-#include <linux/notifier.h>
+-#include <net/netfilter/nf_conntrack.h>
+-#include <net/netfilter/nf_conntrack_core.h>
++# include <linux/notifier.h>
++# include <net/netfilter/nf_conntrack.h>
++# include <net/netfilter/nf_conntrack_core.h>
+ #endif
+ #include <linux/version.h>
+ #include <asm/unaligned.h>
+ #ifdef HAVE_LLIST
+ /* llist.h is officially defined since linux 3.1,
+ * but centos6 have it backported on its 2.6.32.el6 */
+-#include <linux/llist.h>
++# include <linux/llist.h>
+ #endif
++#include "compat.h"
+ #include "ipt_NETFLOW.h"
+ #include "murmur3.h"
+ #ifdef CONFIG_BRIDGE_NETFILTER
+-#include <linux/netfilter_bridge.h>
++# include <linux/netfilter_bridge.h>
+ #endif
+ #ifdef CONFIG_SYSCTL
+-#include <linux/sysctl.h>
+-#endif
+-#ifdef ENABLE_DEBUGFS
+-# ifdef CONFIG_DEBUG_FS
+-# include <linux/debugfs.h>
+-# else
+-# undef ENABLE_DEBUGFS
+-# endif
++# include <linux/sysctl.h>
+ #endif
+ #ifndef CONFIG_NF_CONNTRACK_EVENTS
+ /* No conntrack events in the kernel imply no natevents. */
+-#undef CONFIG_NF_NAT_NEEDED
+-#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
++# undef CONFIG_NF_NAT_NEEDED
+ #endif
+
+ #define IPT_NETFLOW_VERSION "2.1" /* Note that if you are using git, you
+@@ -339,7 +309,7 @@ static int metric = METRIC_DFL,
+
+ static int set_hashsize(int new_size);
+ static void destination_removeall(void);
+-static int add_destinations(char *ptr);
++static int add_destinations(const char *ptr);
+ static int netflow_scan_and_export(int flush);
+ enum {
+ DONT_FLUSH, AND_FLUSH
+@@ -351,10 +321,6 @@ static int tpl_count = 0; /* how much active templates */
+ static unsigned long ts_stat_last = 0; /* (jiffies) */
+ static unsigned long ts_sysinf_last = 0; /* (jiffies) */
+ static unsigned long ts_ifnames_last = 0; /* (jiffies) */
+-#ifdef ENABLE_DEBUGFS
+-static atomic_t freeze = ATOMIC_INIT(0);
+-static struct dentry *flows_dump_d;
+-#endif
+
+ static inline __be32 bits2mask(int bits) {
+ return (bits? 0xffffffff << (32 - bits) : 0);
+@@ -424,12 +390,6 @@ static inline void pause_scan_worker(void)
+ _unschedule_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 ENABLE_SAMPLER
+ static inline unsigned char get_sampler_mode(void)
+ {
+@@ -457,6 +417,55 @@ static inline unsigned short sampler_nf_v5(void)
+ }
+ #endif
+
++/* return value is different from usual snprintf */
++static char *snprintf_sockaddr(char *buf, size_t len, const struct sockaddr_storage *ss)
++{
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32)
++ if (ss->ss_family == AF_INET) {
++ const struct sockaddr_in *sin = (struct sockaddr_in *)ss;
++
++ snprintf(buf, len, "%u.%u.%u.%u:%u",
++ NIPQUAD(sin->sin_addr.s_addr),
++ ntohs(sin->sin_port));
++ } else if (ss->ss_family == AF_INET6) {
++ const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ss;
++
++ snprintf(buf, len, "[%x:%x:%x:%x:%x:%x:%x:%x]:%u",
++ ntohs(sin6->sin6_addr.s6_addr16[0]),
++ ntohs(sin6->sin6_addr.s6_addr16[1]),
++ ntohs(sin6->sin6_addr.s6_addr16[2]),
++ ntohs(sin6->sin6_addr.s6_addr16[3]),
++ ntohs(sin6->sin6_addr.s6_addr16[4]),
++ ntohs(sin6->sin6_addr.s6_addr16[5]),
++ ntohs(sin6->sin6_addr.s6_addr16[6]),
++ ntohs(sin6->sin6_addr.s6_addr16[7]),
++ ntohs(sin6->sin6_port));
++ } else
++ snprintf(buf, len, "(invalid address)");
++#elif LINUX_VERSION_CODE < KERNEL_VERSION(3,11,0)
++ if (ss->ss_family == AF_INET)
++ snprintf(buf, len, "%pI4:%u",
++ &((const struct sockaddr_in *)ss)->sin_addr,
++ ntohs(((const struct sockaddr_in *)ss)->sin_port));
++ else if (ss->ss_family == AF_INET6)
++ snprintf(buf, len, "[%pI6c]:%u",
++ &((const struct sockaddr_in6 *)ss)->sin6_addr,
++ ntohs(((const struct sockaddr_in6 *)ss)->sin6_port));
++ else
++ snprintf(buf, len, "(invalid address)");
++#else
++ snprintf(buf, len, "%pISpc", ss);
++#endif
++ return buf;
++}
++
++static char *print_sockaddr(const struct sockaddr_storage *ss)
++{
++ static char buf[64];
++
++ return snprintf_sockaddr(buf, sizeof(buf), ss);
++}
++
+ #ifdef CONFIG_PROC_FS
+ static inline int ABS(int x) { return x >= 0 ? x : -x; }
+ #define SAFEDIV(x,y) ((y)? ({ u64 __tmp = x; do_div(__tmp, y); (int)__tmp; }) : 0)
+@@ -561,10 +570,9 @@ static int snmp_seq_show(struct seq_file *seq, void *v)
+
+ if (sndbuf_peak < wmem_peak)
+ sndbuf_peak = wmem_peak;
+- seq_printf(seq, "sock%d %u.%u.%u.%u:%u %d %u %u %u %u",
++ seq_printf(seq, "sock%d %s %d %u %u %u %u",
+ snum,
+- HIPQUAD(usock->ipaddr),
+- usock->port,
++ print_sockaddr(&usock->addr),
+ !!usock->sock,
+ usock->err_connect,
+ usock->err_full,
+@@ -606,9 +614,6 @@ static int nf_seq_show(struct seq_file *seq, void *v)
+ #ifdef ENABLE_AGGR
+ " aggr"
+ #endif
+-#ifdef ENABLE_DEBUGFS
+- " debugfs"
+-#endif
+ #ifdef ENABLE_DIRECTION
+ " dir"
+ #endif
+@@ -623,6 +628,9 @@ static int nf_seq_show(struct seq_file *seq, void *v)
+ #endif
+ #ifdef ENABLE_PROMISC
+ " promisc"
++# ifdef PROMISC_MPLS
++ "+mpls"
++# endif
+ #endif
+ #ifdef ENABLE_SAMPLER
+ " samp"
+@@ -808,10 +816,9 @@ static int nf_seq_show(struct seq_file *seq, void *v)
+
+ mutex_lock(&sock_lock);
+ list_for_each_entry(usock, &usock_list, list) {
+- seq_printf(seq, "sock%d: %u.%u.%u.%u:%u",
++ seq_printf(seq, "sock%d: %s",
+ snum,
+- HIPQUAD(usock->ipaddr),
+- usock->port);
++ print_sockaddr(&usock->addr));
+ if (usock->sock) {
+ struct sock *sk = usock->sock->sk;
+
+@@ -902,110 +909,171 @@ static struct file_operations snmp_seq_fops = {
+ .llseek = seq_lseek,
+ .release = single_release,
+ };
+-#endif /* CONFIG_PROC_FS */
+
+-#ifdef ENABLE_DEBUGFS
+-static inline int active_needs_export(const struct ipt_netflow *nf, const long a_timeout, const unsigned long jiff);
++static inline int inactive_needs_export(const struct ipt_netflow *nf, const long i_timeout,
++ const unsigned long jiff);
++static inline int active_needs_export(const struct ipt_netflow *nf, const long a_timeout,
++ const unsigned long jiff);
+ static inline u_int32_t hash_netflow(const struct ipt_netflow_tuple *tuple);
+
+-static int seq_stripe = -1;
++struct flows_dump_private {
++ int pcache; /* pos */
++ void *vcache; /* corresponding pointer for pos */
++ int stripe; /* current stripe */
++ struct list_head list; /* copy of stripe */
++ int alloc_errors;
++};
++
++/* deallocate copied stripe */
++static void nf_free_stripe(struct list_head *list)
++{
++ struct ipt_netflow *cf, *tmp;
++
++ list_for_each_entry_safe(cf, tmp, list, flows_list) {
++ kmem_cache_free(ipt_netflow_cachep, cf);
++ }
++ INIT_LIST_HEAD(list);
++}
++
++/* quickly clone stripe into flows_dump_private then it can be walked slowly
++ * and lockless */
++static void __nf_copy_stripe(struct flows_dump_private *st, const struct list_head *list)
++{
++ const struct ipt_netflow *nf;
++ struct ipt_netflow *cf;
++
++ nf_free_stripe(&st->list);
++ list_for_each_entry(nf, list, flows_list) {
++ cf = kmem_cache_alloc(ipt_netflow_cachep, GFP_ATOMIC);
++ if (!cf) {
++ st->alloc_errors++;
++ continue;
++ }
++ memcpy(cf, nf, sizeof(*cf));
++ list_add(&cf->flows_list, &st->list);
++ }
++}
+
+-/* get first & next ipt_netflow list entry and lock it */
+-static struct list_head *nf_get_first(int nstripe)
++/* nstripe is desired stripe, in st->stripe will be recorded actual stripe used
++ * (with empty stripes skipped), -1 is there is no valid stripes anymore,
++ * return first element in stripe list or NULL */
++static struct list_head *nf_get_stripe(struct flows_dump_private *st, int nstripe)
+ {
+- /* no locking here since it's under global rwlock */
++ read_lock_bh(&htable_rwlock);
+ for (; nstripe < LOCK_COUNT; nstripe++) {
+ struct stripe_entry *stripe = &htable_stripes[nstripe];
+
++ spin_lock(&stripe->lock);
+ if (!list_empty(&stripe->list)) {
+- seq_stripe = nstripe;
+- return stripe->list.next;
++ st->stripe = nstripe;
++ __nf_copy_stripe(st, &stripe->list);
++ spin_unlock(&stripe->lock);
++ read_unlock_bh(&htable_rwlock);
++ return st->list.next;
+ }
++ spin_unlock(&stripe->lock);
+ }
+- seq_stripe = -1;
++ read_unlock_bh(&htable_rwlock);
++ st->stripe = -1;
+ return NULL;
+ }
+
+-static struct list_head *nf_get_next(struct list_head *head)
++/* simply next element in flows list or NULL */
++static struct list_head *nf_get_next(struct flows_dump_private *st, struct list_head *head)
+ {
+- struct stripe_entry *stripe;
+-
+- if (seq_stripe < 0)
++ if (head == SEQ_START_TOKEN)
++ return nf_get_stripe(st, 0);
++ if (st->stripe < 0)
+ return NULL;
+- stripe = &htable_stripes[seq_stripe];
+- head = head->next;
+- if (head != &stripe->list)
+- return head;
+- return nf_get_first(seq_stripe + 1);
++ /* next element */
++ if (!list_is_last(head, &st->list))
++ return head->next;
++ /* next bucket */
++ return nf_get_stripe(st, st->stripe + 1);
+ }
+
+-static int seq_pcache;
+-static void *seq_vcache;
++/* seq_file could arbitrarily start/stop iteration as it feels need,
++ * so, I try to cache things to (significantly) speed it up. */
+ static void *flows_dump_seq_start(struct seq_file *seq, loff_t *pos)
+ {
++ struct flows_dump_private *st = seq->private;
+ int ppos = *pos;
+ struct list_head *lh;
+
+ if (!ppos) {
+- seq_pcache = 0;
+- seq_vcache = nf_get_first(0);
+- return seq_vcache;
+- }
+- if (ppos >= seq_pcache) {
+- ppos -= seq_pcache;
+- lh = seq_vcache;
+- } else
+- lh = nf_get_first(0);
++ /* first */
++ st->pcache = 0;
++ st->vcache = SEQ_START_TOKEN;
++ return st->vcache;
++ }
++ if (ppos >= st->pcache) {
++ /* can iterate forward */
++ ppos -= st->pcache;
++ lh = st->vcache;
++ } else /* can't, start from 0 */
++ lh = SEQ_START_TOKEN;
++ /* iterate forward */
+ while (ppos--)
+- lh = nf_get_next(lh);
+- seq_pcache = *pos;
+- seq_vcache = lh;
+- return seq_vcache;
++ lh = nf_get_next(st, lh);
++ st->pcache = *pos;
++ st->vcache = lh;
++ return st->vcache;
+ }
+
+ static void *flows_dump_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+ {
+- seq_pcache = ++*pos;
+- seq_vcache = nf_get_next((struct list_head *)v);
+- return seq_vcache;
++ struct flows_dump_private *st = seq->private;
++
++ st->pcache = ++*pos;
++ st->vcache = nf_get_next(st, (struct list_head *)v);
++ return st->vcache;
+ }
+
+ static void flows_dump_seq_stop(struct seq_file *seq, void *v)
+ {
+ }
+
+-static unsigned long dump_start; /* jiffies */
+-static unsigned int dump_err;
+ /* To view this: cat /sys/kernel/debug/netflow_dump */
+ static int flows_dump_seq_show(struct seq_file *seq, void *v)
+ {
+- struct ipt_netflow *nf = list_entry(v, struct ipt_netflow, flows_list);
+- long i_timeout = inactive_timeout * HZ;
+- long a_timeout = active_timeout * HZ;
+- int inactive = (jiffies - nf->nf_ts_last) >= i_timeout;
+- int active = active_needs_export(nf, a_timeout, dump_start);
+- u_int32_t hash = hash_netflow(&nf->tuple);
++ struct flows_dump_private *st = seq->private;
++ const long i_timeout = inactive_timeout * HZ;
++ const long a_timeout = active_timeout * HZ;
++ const struct ipt_netflow *nf;
+
+- if (seq_pcache == 0) {
+- unsigned int nr_flows = atomic_read(&ipt_netflow_count);
+-
+- seq_printf(seq, "# Attention: netflow processing is disabled while dumping. (~%u flows)\n", nr_flows);
++ if (v == SEQ_START_TOKEN) {
++ seq_printf(seq, "# hash a dev:i,o"
++#ifdef SNMP_RULES
++ " snmp:i,o"
++#endif
++#ifdef ENABLE_MAC
++ " mac:src,dst"
++#endif
++#ifdef ENABLE_VLAN
++ " vlan"
++#endif
++#if defined(ENABLE_MAC) || defined(ENABLE_VLAN)
++ " type"
++#endif
++ " proto src:ip,port dst:ip,port nexthop"
++ " tos,tcpflags,options,tcpoptions"
++ " packets bytes ts:first,last\n");
+ return 0;
+ }
+
+- seq_printf(seq, "%d %02x,%04x %02d",
+- seq_pcache,
+- seq_stripe,
+- hash,
+- inactive * 10 + active);
+-#ifdef SNMP_RULES
+- seq_printf(seq, " %hd,%hd(%hd,%hd)",
+- nf->i_ifcr,
+- nf->o_ifcr,
++ nf = list_entry(v, struct ipt_netflow, flows_list);
++ seq_printf(seq, "%d %04x %x",
++ st->pcache,
++ hash_netflow(&nf->tuple),
++ (!!inactive_needs_export(nf, i_timeout, jiffies)) |
++ (active_needs_export(nf, a_timeout, jiffies) << 1));
++ seq_printf(seq, " %hd,%hd",
+ nf->tuple.i_ifc,
+ nf->o_ifc);
+-#else
++#ifdef SNMP_RULES
+ seq_printf(seq, " %hd,%hd",
++ nf->i_ifcr,
++ nf->o_ifcr,
+ nf->tuple.i_ifc,
+ nf->o_ifc);
+ #endif
+@@ -1013,55 +1081,44 @@ static int flows_dump_seq_show(struct seq_file *seq, void *v)
+ seq_printf(seq, " %pM,%pM", &nf->tuple.h_src, &nf->tuple.h_dst);
+ #endif
+ #ifdef ENABLE_VLAN
+- if (nf->tuple.tag1 || nf->tuple.tag2) {
+- seq_printf(seq, ",%d", ntohs(nf->tuple.tag1));
+- if (nf->tuple.tag2)
+- seq_printf(seq, ",%d", ntohs(nf->tuple.tag2));
++ if (nf->tuple.tag[0]) {
++ seq_printf(seq, " %d", ntohs(nf->tuple.tag[0]));
++ if (nf->tuple.tag[1])
++ seq_printf(seq, ",%d", ntohs(nf->tuple.tag[1]));
+ }
+ #endif
+ #if defined(ENABLE_MAC) || defined(ENABLE_VLAN)
+- seq_printf(seq, " [%04x]", ntohs(nf->ethernetType));
++ seq_printf(seq, " %04x", ntohs(nf->ethernetType));
+ #endif
+- seq_printf(seq, " %u,%u ",
+- nf->tuple.l3proto,
++ seq_printf(seq, " %u ",
+ nf->tuple.protocol);
+ if (nf->tuple.l3proto == AF_INET) {
+- seq_printf(seq, "%pI4n:%u,%pI4n:%u %pI4n",
++ seq_printf(seq, "%pI4n,%u %pI4n,%u %pI4n",
+ &nf->tuple.src,
+ ntohs(nf->tuple.s_port),
+ &nf->tuple.dst,
+ ntohs(nf->tuple.d_port),
+ &nf->nh);
+- /* sanity check */
+- if (nf->tuple.src.ip6[1] ||
+- nf->tuple.src.ip6[2] ||
+- nf->tuple.src.ip6[3])
+- seq_puts(seq, "error:src:garbage");
+- if (nf->tuple.dst.ip6[1] ||
+- nf->tuple.dst.ip6[2] ||
+- nf->tuple.dst.ip6[3])
+- seq_puts(seq, "error:dst:garbage");
+ } else if (nf->tuple.l3proto == AF_INET6) {
+- seq_printf(seq, "%pI6c#%u,%pI6c#%u %pI6c",
++ seq_printf(seq, "%pI6c,%u %pI6c,%u %pI6c",
+ &nf->tuple.src,
+ ntohs(nf->tuple.s_port),
+ &nf->tuple.dst,
+ ntohs(nf->tuple.d_port),
+ &nf->nh);
+ } else {
+- seq_puts(seq, "error:l3proto:unknown");
++ seq_puts(seq, "?,? ?,? ?");
+ }
+ seq_printf(seq, " %x,%x,%x,%x",
+ nf->tuple.tos,
+ nf->tcp_flags,
+ nf->options,
+ nf->tcpoptions);
+- seq_printf(seq, " %d,%d %lu,%lu\n",
++ seq_printf(seq, " %u %u %lu,%lu\n",
+ nf->nr_packets,
+ nf->nr_bytes,
+ jiffies - nf->nf_ts_first,
+- jiffies - nf->nf_ts_last
+- );
++ jiffies - nf->nf_ts_last);
+
+ return 0;
+ }
+@@ -1073,67 +1130,54 @@ static struct seq_operations flows_dump_seq_ops = {
+ .stop = flows_dump_seq_stop,
+ };
+
+-static int flows_dump_open(struct inode *inode, struct file *file)
++static int flows_seq_open(struct inode *inode, struct file *file)
+ {
+- int ret;
++ struct flows_dump_private *st;
+ char *buf;
++ const size_t size = 4 * PAGE_SIZE;
+
+- if (atomic_inc_return(&freeze) > 1) {
+- /* do not let concurrent dumps. */
+- atomic_dec(&freeze);
+- return -EAGAIN;
+- }
+- buf = kmalloc(KMALLOC_MAX_SIZE, GFP_KERNEL);
+- if (!buf) {
+- atomic_dec(&freeze);
++ buf = kmalloc(size, GFP_KERNEL);
++ if (!buf)
+ return -ENOMEM;
+- }
+- pause_scan_worker();
+- synchronize_sched();
+- /* write_lock to be sure that softirq is finished */
+- write_lock(&htable_rwlock);
+-
+- dump_start = jiffies;
+- dump_err = NETFLOW_STAT_READ(freeze_err);
+
+- ret = seq_open(file, &flows_dump_seq_ops);
+- if (ret) {
+- write_unlock(&htable_rwlock);
+- cont_scan_worker();
++ st = __seq_open_private(file, &flows_dump_seq_ops, sizeof(struct flows_dump_private));
++ if (!st) {
+ kfree(buf);
+- atomic_dec(&freeze);
+- return ret;
++ return -ENOMEM;
+ }
++ INIT_LIST_HEAD(&st->list);
++ /* speed up seq interface with bigger buffer */
+ ((struct seq_file *)file->private_data)->buf = buf;
+- ((struct seq_file *)file->private_data)->size = KMALLOC_MAX_SIZE;
++ ((struct seq_file *)file->private_data)->size = size;
+ return 0;
+
+ }
+-static int flows_dump_release(struct inode *inode, struct file *file)
++static int flows_seq_release(struct inode *inode, struct file *file)
+ {
+- seq_stripe = -1;
+- write_unlock(&htable_rwlock);
+- cont_scan_worker();
+- atomic_dec(&freeze);
++ struct seq_file *seq = file->private_data;
++ struct flows_dump_private *st = seq->private;
+
+- printk(KERN_INFO "ipt_NETFLOW: dump finished in %lu/%lu sec, dropped %u packets.\n",
+- jiffies - dump_start,
+- msecs_to_jiffies(1000),
+- NETFLOW_STAT_READ(freeze_err) - dump_err);
+- return seq_release(inode, file);
++ nf_free_stripe(&st->list);
++ if (st->alloc_errors)
++ printk(KERN_INFO "ipt_NETFLOW: alloc_errors %d\n", st->alloc_errors);
++ return seq_release_private(inode, file);
+ }
+
+-static const struct file_operations flows_dump_fops = {
++static struct file_operations flows_seq_fops = {
+ .owner = THIS_MODULE,
+- .open = flows_dump_open,
++ .open = flows_seq_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+- .release = flows_dump_release,
++ .release = flows_seq_release,
+ };
+-#endif /* ENABLE_DEBUGFS */
++#endif /* CONFIG_PROC_FS */
+
+ #ifdef ENABLE_PROMISC
+-static int promisc_finish(struct sk_buff *skb)
++static int promisc_finish(
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,1,0)
++ struct sock *sk,
++#endif
++ struct sk_buff *skb)
+ {
+ /* don't pass to the routing */
+ kfree_skb(skb);
+@@ -1145,16 +1189,8 @@ static int promisc4_rcv(struct sk_buff *skb, struct net_device *dev, struct pack
+ const struct iphdr *iph;
+ u32 len;
+
+- /* what is not PACKET_OTHERHOST will be parsed at ip_rcv() */
+- if (skb->pkt_type != PACKET_OTHERHOST)
+- goto out;
+-
+- NETFLOW_STAT_INC(pkt_promisc);
+-
+ /* clone skb and do basic IPv4 sanity checking and preparations
+ * for L3, this is quick and dirty version of ip_rcv() */
+- if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL)
+- goto drop;
+ if (!pskb_may_pull(skb, sizeof(struct iphdr)))
+ goto drop;
+ iph = ip_hdr(skb);
+@@ -1176,10 +1212,10 @@ static int promisc4_rcv(struct sk_buff *skb, struct net_device *dev, struct pack
+ memset(IPCB(skb), 0, sizeof(struct inet_skb_parm));
+ skb_orphan(skb);
+
+- return NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING, skb, dev, NULL, promisc_finish);
++ return NF_HOOK_COMPAT(NFPROTO_IPV4, NF_INET_PRE_ROUTING, NULL,
++ skb, dev, NULL, promisc_finish);
+ drop:
+ NETFLOW_STAT_INC(pkt_promisc_drop);
+-out:
+ kfree_skb(skb);
+ return NET_RX_DROP;
+ }
+@@ -1190,18 +1226,11 @@ static int promisc6_rcv(struct sk_buff *skb, struct net_device *dev, struct pack
+ u32 pkt_len;
+ struct inet6_dev *idev;
+
+- /* what is not PACKET_OTHERHOST will be parsed at ipv6_rcv() */
+- if (skb->pkt_type != PACKET_OTHERHOST)
+- goto out;
+-
+- NETFLOW_STAT_INC(pkt_promisc);
+-
+ /* quick and dirty version of ipv6_rcv(), basic sanity checking
+ * and preparation of skb for later processing */
+ rcu_read_lock();
+ idev = __in6_dev_get(skb->dev);
+- if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL ||
+- !idev || unlikely(idev->cnf.disable_ipv6))
++ if (!idev || unlikely(idev->cnf.disable_ipv6))
+ goto drop;
+ memset(IP6CB(skb), 0, sizeof(struct inet6_skb_parm));
+ IP6CB(skb)->iif = skb_dst(skb) ? ip6_dst_idev(skb_dst(skb))->dev->ifindex : dev->ifindex;
+@@ -1252,22 +1281,127 @@ static int promisc6_rcv(struct sk_buff *skb, struct net_device *dev, struct pack
+ rcu_read_unlock();
+ skb_orphan(skb);
+
+- return NF_HOOK(NFPROTO_IPV6, NF_INET_PRE_ROUTING, skb, dev, NULL, promisc_finish);
++ return NF_HOOK_COMPAT(NFPROTO_IPV6, NF_INET_PRE_ROUTING, NULL,
++ skb, dev, NULL, promisc_finish);
+ drop:
+ rcu_read_unlock();
+ NETFLOW_STAT_INC(pkt_promisc_drop);
+-out:
+ kfree_skb(skb);
+ return NET_RX_DROP;
+ }
+
+-static struct packet_type promisc4_packet_type __read_mostly = {
+- .type = htons(ETH_P_IP),
+- .func = promisc4_rcv,
+-};
+-static struct packet_type promisc6_packet_type __read_mostly = {
+- .type = htons(ETH_P_IPV6),
+- .func = promisc6_rcv,
++/* source is skb_network_protocol() and __vlan_get_protocol() */
++static __be16 __skb_network_protocol(struct sk_buff *skb, int *depth)
++{
++ __be16 type = skb->protocol;
++ unsigned int vlan_depth;
++
++ if (type == htons(ETH_P_TEB)) {
++ struct ethhdr *eth;
++
++ if (unlikely(!pskb_may_pull(skb, sizeof(struct ethhdr))))
++ return 0;
++
++ eth = (struct ethhdr *)skb_mac_header(skb);
++ type = eth->h_proto;
++ }
++
++ vlan_depth = skb->mac_len;
++ if (type == htons(ETH_P_8021Q) || type == htons(ETH_P_8021AD)) {
++ if (vlan_depth) {
++ if (WARN_ON(vlan_depth < VLAN_HLEN))
++ return 0;
++ vlan_depth -= VLAN_HLEN;
++ } else {
++ vlan_depth = ETH_HLEN;
++ }
++ do {
++ struct vlan_hdr *vh;
++
++ if (unlikely(!pskb_may_pull(skb, vlan_depth + VLAN_HLEN)))
++ return 0;
++
++ vh = (struct vlan_hdr *)(skb->data + vlan_depth);
++ type = vh->h_vlan_encapsulated_proto;
++ vlan_depth += VLAN_HLEN;
++ } while (type == htons(ETH_P_8021Q) ||
++ type == htons(ETH_P_8021AD));
++ }
++
++ *depth = vlan_depth;
++
++ return type;
++}
++
++static int promisc_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev)
++{
++ /* what is not PACKET_OTHERHOST will be processed normally */
++ if (skb->pkt_type != PACKET_OTHERHOST)
++ goto out;
++
++ NETFLOW_STAT_INC(pkt_promisc);
++
++ if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL)
++ goto drop;
++
++ /* Note about vlans:
++ * - older kernels will pass raw packet;
++ * - newer kernes (since 3.0) will have one vlan tag
++ * physically stripped out of the packet, and it will
++ * be saved into skb->vlan_tci. skb->protocol will be
++ * untagged etherType. */
++
++ if (skb->protocol == cpu_to_be16(ETH_P_8021Q) ||
++ skb->protocol == cpu_to_be16(ETH_P_8021AD)) {
++ int vlan_depth = skb->mac_len;
++
++ skb_push(skb, skb->data - skb_mac_header(skb));
++ skb->protocol = __skb_network_protocol(skb, &vlan_depth);
++ skb_pull(skb, vlan_depth);
++
++ skb_reset_network_header(skb);
++ skb_reset_mac_len(skb);
++ }
++# ifdef PROMISC_MPLS
++ if (eth_p_mpls(skb->protocol)) {
++ size_t stack_len = 0;
++ const struct mpls_label *mpls;
++
++ do {
++ stack_len += MPLS_HLEN;
++ if (unlikely(!pskb_may_pull(skb, stack_len)))
++ goto drop;
++ mpls = (struct mpls_label *)(skb->data + stack_len - MPLS_HLEN);
++ } while (!(mpls->entry & htonl(MPLS_LS_S_MASK)));
++
++ skb_pull(skb, stack_len);
++ skb_reset_network_header(skb);
++
++ if (!pskb_may_pull(skb, 1))
++ goto drop;
++ switch (ip_hdr(skb)->version) {
++ case 4: skb->protocol = htons(ETH_P_IP); break;
++ case 6: skb->protocol = htons(ETH_P_IPV6); break;
++ default: goto drop;
++ }
++ }
++# endif
++ switch (skb->protocol) {
++ case htons(ETH_P_IP):
++ return promisc4_rcv(skb, dev, pt, orig_dev);
++ case htons(ETH_P_IPV6):
++ return promisc6_rcv(skb, dev, pt, orig_dev);
++ }
++drop:
++ NETFLOW_STAT_INC(pkt_promisc_drop);
++out:
++ kfree_skb(skb);
++ return 0;
++}
++
++static struct packet_type promisc_packet_type __read_mostly = {
++ .type = htons(ETH_P_ALL),
++ .func = promisc_rcv,
+ };
+
+ /* should not have promisc passed as parameter */
+@@ -1277,13 +1411,10 @@ static int switch_promisc(int newpromisc)
+ mutex_lock(&promisc_lock);
+ if (newpromisc == promisc)
+ goto unlock;
+- if (newpromisc) {
+- dev_add_pack(&promisc4_packet_type);
+- dev_add_pack(&promisc6_packet_type);
+- } else {
+- dev_remove_pack(&promisc4_packet_type);
+- dev_remove_pack(&promisc6_packet_type);
+- }
++ if (newpromisc)
++ dev_add_pack(&promisc_packet_type);
++ else
++ dev_remove_pack(&promisc_packet_type);
+ printk(KERN_INFO "ipt_NETFLOW: promisc hack is %s\n",
+ newpromisc? "enabled" : "disabled");
+ promisc = newpromisc;
+@@ -1294,21 +1425,6 @@ unlock:
+ #endif
+
+ #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
+-
+-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,17,0)
+-#define ctl_table struct ctl_table
+-#endif
+-
+-#ifndef CONFIG_GRKERNSEC
+-#define ctl_table_no_const ctl_table
+-#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)
+@@ -1753,30 +1869,25 @@ static void sk_error_report(struct sock *sk)
+ return;
+ }
+
+-static struct socket *usock_open_sock(const __be32 ipaddr, const unsigned short port, void *u)
++static struct socket *usock_open_sock(const struct sockaddr_storage *addr, void *user_data)
+ {
+- struct sockaddr_in sin;
+ struct socket *sock;
+ int error;
+
+- if ((error = sock_create_kern(PF_INET, SOCK_DGRAM, IPPROTO_UDP, &sock)) < 0) {
++ if ((error = sock_create_kern(addr->ss_family, SOCK_DGRAM, IPPROTO_UDP, &sock)) < 0) {
+ printk(KERN_ERR "ipt_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 */
+- sock->sk->sk_user_data = u;
++ sock->sk->sk_user_data = user_data; /* usock */
+ 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) {
++ error = sock->ops->connect(sock, (struct sockaddr *)addr, sizeof(*addr), 0);
++ if (error < 0) {
+ printk(KERN_ERR "ipt_NETFLOW: error connecting UDP socket %d,"
+ " don't worry, will try reconnect later.\n", -error);
+ /* ENETUNREACH when no interfaces */
+@@ -1788,18 +1899,16 @@ static struct socket *usock_open_sock(const __be32 ipaddr, const unsigned short
+
+ static void usock_connect(struct ipt_netflow_sock *usock, const int sendmsg)
+ {
+- usock->sock = usock_open_sock(usock->ipaddr, usock->port, usock);
++ usock->sock = usock_open_sock(&usock->addr, usock);
+ if (usock->sock) {
+ if (sendmsg || debug)
+- printk(KERN_INFO "ipt_NETFLOW: connected %u.%u.%u.%u:%u\n",
+- HIPQUAD(usock->ipaddr),
+- usock->port);
++ printk(KERN_INFO "ipt_NETFLOW: connected %s\n",
++ print_sockaddr(&usock->addr));
+ } else {
+ usock->err_connect++;
+ if (debug)
+- printk(KERN_INFO "ipt_NETFLOW: connect to %u.%u.%u.%u:%u failed%s.\n",
+- HIPQUAD(usock->ipaddr),
+- usock->port,
++ printk(KERN_INFO "ipt_NETFLOW: connect to %s failed%s.\n",
++ print_sockaddr(&usock->addr),
+ (sendmsg)? " (pdu lost)" : "");
+ }
+ atomic_set(&usock->wmem_peak, 0);
+@@ -1888,9 +1997,8 @@ static void netflow_sendmsg(void *buffer, const int len)
+
+ static void usock_close_free(struct ipt_netflow_sock *usock)
+ {
+- printk(KERN_INFO "ipt_NETFLOW: removed destination %u.%u.%u.%u:%u\n",
+- HIPQUAD(usock->ipaddr),
+- usock->port);
++ printk(KERN_INFO "ipt_NETFLOW: removed destination %s\n",
++ print_sockaddr(&usock->addr));
+ usock_close(usock);
+ vfree(usock);
+ }
+@@ -1917,17 +2025,15 @@ static void add_usock(struct ipt_netflow_sock *usock)
+ mutex_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) {
++ if (sockaddr_cmp(&sk->addr, &usock->addr)) {
+ mutex_unlock(&sock_lock);
+ usock_close_free(usock);
+ return;
+ }
+ }
+ list_add_tail(&usock->list, &usock_list);
+- printk(KERN_INFO "ipt_NETFLOW: added destination %u.%u.%u.%u:%u%s\n",
+- HIPQUAD(usock->ipaddr),
+- usock->port,
++ printk(KERN_INFO "ipt_NETFLOW: added destination %s%s\n",
++ print_sockaddr(&usock->addr),
+ (!usock->sock)? " (unconnected)" : "");
+ mutex_unlock(&sock_lock);
+ }
+@@ -2105,33 +2211,76 @@ static inline int resolve_snmp(const struct net_device *ifc)
+ }
+ #endif /* SNMP_RULES */
+
++/* count how much character c is in the string */
++static size_t strncount(const char *s, size_t count, int c)
++{
++ size_t amount = 0;
++
++ for (; count-- && *s != '\0'; ++s)
++ if (*s == (char)c)
++ ++amount;
++ return amount;
++}
++
+ #define SEPARATORS " ,;\t\n"
+-static int add_destinations(char *ptr)
++static int add_destinations(const char *ptr)
+ {
+- while (ptr) {
+- unsigned char ip[4];
+- unsigned short port;
++ int len;
+
+- ptr += strspn(ptr, SEPARATORS);
++ for (; ptr; ptr += len) {
++ struct sockaddr_storage ss;
++ struct ipt_netflow_sock *usock;
++ const char *end;
++ int succ = 0;
+
+- if (sscanf(ptr, "%hhu.%hhu.%hhu.%hhu:%hu",
+- ip, ip + 1, ip + 2, ip + 3, &port) == 5) {
+- struct ipt_netflow_sock *usock;
++ /* skip initial separators */
++ ptr += strspn(ptr, SEPARATORS);
+
+- if (!(usock = vmalloc(sizeof(*usock)))) {
+- printk(KERN_ERR "ipt_NETFLOW: can't vmalloc socket\n");
+- return -ENOMEM;
++ len = strcspn(ptr, SEPARATORS);
++ if (!len)
++ break;
++ memset(&ss, 0, sizeof(ss));
++
++ if (strncount(ptr, len, ':') >= 2) {
++ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&ss;
++ const char *c = ptr;
++ int clen = len;
++
++ sin6->sin6_family = AF_INET6;
++ sin6->sin6_port = htons(2055);
++ if (*c == '[') {
++ ++c;
++ --clen;
+ }
++ succ = in6_pton(c, clen, (u8 *)&sin6->sin6_addr, -1, &end);
++ if (succ && *ptr == '[' && *end == ']')
++ ++end;
++ if (succ &&
++ (*end == ':' || *end == '.' || *end == 'p' || *end == '#'))
++ sin6->sin6_port = htons(simple_strtoul(++end, NULL, 0));
++ } else {
++ struct sockaddr_in *sin = (struct sockaddr_in *)&ss;
+
+- memset(usock, 0, sizeof(*usock));
+- usock->ipaddr = ntohl(*(__be32 *)ip);
+- usock->port = port;
+- usock_connect(usock, 0);
+- add_usock(usock);
+- } else
+- break;
++ sin->sin_family = AF_INET;
++ sin->sin_port = htons(2055);
++ succ = in4_pton(ptr, len, (u8 *)&sin->sin_addr, -1, &end);
++ if (succ && *end == ':')
++ sin->sin_port = htons(simple_strtoul(++end, NULL, 0));
++ }
++ if (!succ) {
++ printk(KERN_ERR "ipt_NETFLOW: can't parse destination: %.*s\n",
++ len, ptr);
++ continue;
++ }
+
+- ptr = strpbrk(ptr, SEPARATORS);
++ if (!(usock = vmalloc(sizeof(*usock)))) {
++ printk(KERN_ERR "ipt_NETFLOW: can't vmalloc socket\n");
++ return -ENOMEM;
++ }
++ memset(usock, 0, sizeof(*usock));
++ usock->addr = ss;
++ usock_connect(usock, 0);
++ add_usock(usock);
+ }
+ return 0;
+ }
+@@ -2246,12 +2395,7 @@ ipt_netflow_find(const struct ipt_netflow_tuple *tuple, const unsigned int hash)
+ {
+ struct ipt_netflow *nf;
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(3,9,0)
+-#define compat_hlist_for_each_entry hlist_for_each_entry
+-#define compat_hlist_for_each_entry_safe hlist_for_each_entry_safe
+ struct hlist_node *pos;
+-#else /* since 3.9.0 */
+-#define compat_hlist_for_each_entry(a,pos,c,d) hlist_for_each_entry(a,c,d)
+-#define compat_hlist_for_each_entry_safe(a,pos,c,d,e) hlist_for_each_entry_safe(a,c,d,e)
+ #endif
+
+ compat_hlist_for_each_entry(nf, pos, &htable[hash], hlist) {
+@@ -2529,7 +2673,7 @@ static inline void pdu_rewind_space(const size_t size)
+ /* allocate data space in pdu, or export (reallocate) and fail. */
+ static inline unsigned char *pdu_alloc_fail_export(const size_t size)
+ {
+- if (!pdu_have_space(size)) {
++ if (unlikely(!pdu_have_space(size))) {
+ netflow_export_pdu();
+ return NULL;
+ }
+@@ -2585,6 +2729,7 @@ struct base_template {
+ #define BTPL_DIRECTION 0x00200000 /* flowDirection */
+ #define BTPL_SAMPLERID 0x00400000 /* samplerId (v9) */
+ #define BTPL_SELECTORID 0x00800000 /* selectorId (IPFIX) */
++#define BTPL_MPLS 0x01000000 /* MPLS stack */
+ #define BTPL_OPTION 0x80000000 /* Options Template */
+ #define BTPL_MAX 32
+ /* Options Templates */
+@@ -2604,6 +2749,10 @@ static struct base_template template_base_9 = {
+ .types = {
+ INPUT_SNMP,
+ OUTPUT_SNMP,
++#ifdef ENABLE_PHYSDEV
++ ingressPhysicalInterface,
++ egressPhysicalInterface,
++#endif
+ IN_PKTS,
+ IN_BYTES,
+ FIRST_SWITCHED,
+@@ -2617,12 +2766,17 @@ static struct base_template template_base_ipfix = {
+ .types = {
+ ingressInterface,
+ egressInterface,
++#ifdef ENABLE_PHYSDEV
++ ingressPhysicalInterface,
++ egressPhysicalInterface,
++#endif
+ packetDeltaCount,
+ octetDeltaCount,
+ flowStartMilliseconds,
+ flowEndMilliseconds,
+ protocolIdentifier,
+ ipClassOfService,
++ flowEndReason,
+ 0
+ }
+ };
+@@ -2660,6 +2814,27 @@ static struct base_template template_vlan_inner = {
+ }
+ };
+ #endif
++#ifdef MPLS_DEPTH
++static struct base_template template_mpls = {
++ .types = {
++ mplsTopLabelTTL,
++ /* do not just add element here, becasue this array
++ * is truncated in ipt_netflow_init() */
++#define MPLS_LABELS_BASE_INDEX 1
++ MPLS_LABEL_1,
++ MPLS_LABEL_2,
++ MPLS_LABEL_3,
++ MPLS_LABEL_4,
++ MPLS_LABEL_5,
++ MPLS_LABEL_6,
++ MPLS_LABEL_7,
++ MPLS_LABEL_8,
++ MPLS_LABEL_9,
++ MPLS_LABEL_10,
++ 0
++ }
++};
++#endif
+ #ifdef ENABLE_DIRECTION
+ static struct base_template template_direction = {
+ .types = { DIRECTION, 0 }
+@@ -3026,6 +3201,10 @@ static struct data_template *get_template(const unsigned int tmask)
+ if (tmask & BTPL_ETHERTYPE)
+ tlist[tnum++] = &template_ethertype;
+ #endif
++#ifdef MPLS_DEPTH
++ if (tmask & BTPL_MPLS)
++ tlist[tnum++] = &template_mpls;
++#endif
+ #ifdef ENABLE_DIRECTION
+ if (tmask & BTPL_DIRECTION)
+ tlist[tnum++] = &template_direction;
+@@ -3176,23 +3355,6 @@ static void pdu_add_template(struct data_template *tpl)
+ pdu_tpl_records++;
+ }
+
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35)
+-static inline s64 portable_ktime_to_ms(const ktime_t kt)
+-{
+- struct timeval tv = ktime_to_timeval(kt);
+- return (s64) tv.tv_sec * MSEC_PER_SEC + tv.tv_usec / USEC_PER_MSEC;
+-}
+-#define ktime_to_ms portable_ktime_to_ms
+-#endif
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)
+-static inline s64 portable_ktime_to_us(const ktime_t kt)
+-{
+- struct timeval tv = ktime_to_timeval(kt);
+- return (s64) tv.tv_sec * USEC_PER_SEC + tv.tv_usec;
+-}
+-#define ktime_to_us portable_ktime_to_us
+-#endif
+-
+ #ifdef ENABLE_DIRECTION
+ static inline __u8 hook2dir(const __u8 hooknum)
+ {
+@@ -3209,21 +3371,6 @@ static inline __u8 hook2dir(const __u8 hooknum)
+ }
+ #endif
+
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26)
+-static inline void put_unaligned_be16(u16 val, void *p)
+-{
+- put_unaligned(cpu_to_be16(val), (__be16 *)p);
+-}
+-static inline void put_unaligned_be32(u32 val, void *p)
+-{
+- put_unaligned(cpu_to_be32(val), (__be32 *)p);
+-}
+-static inline void put_unaligned_be64(u64 val, void *p)
+-{
+- put_unaligned(cpu_to_be64(val), (__be64 *)p);
+-}
+-#endif
+-
+ static inline void put_unaligned_be24(u32 val, unsigned char *p)
+ {
+ *p++ = val >> 16;
+@@ -3260,9 +3407,6 @@ static inline s64 jiffies_to_ms_abs(unsigned long j)
+ return jiffies_base.ms + (s64)jiffies_to_msecs(-jdiff);
+ }
+
+-#ifndef WARN_ONCE
+-#define WARN_ONCE(x,fmt...) ({ if (x) printk(KERN_WARNING fmt); })
+-#endif
+ typedef struct in6_addr in6_t;
+ /* encode one field (data records only) */
+ static inline void add_tpl_field(__u8 *ptr, const int type, const struct ipt_netflow *nf)
+@@ -3286,14 +3430,20 @@ static inline void add_tpl_field(__u8 *ptr, const int type, const struct ipt_net
+ case INPUT_SNMP: put_unaligned_be16(nf->tuple.i_ifc, ptr); break;
+ case OUTPUT_SNMP: put_unaligned_be16(nf->o_ifc, ptr); break;
+ #endif
++#ifdef ENABLE_PHYSDEV
++ case ingressPhysicalInterface:
++ put_unaligned_be16(nf->i_ifphys, ptr); break;
++ case egressPhysicalInterface:
++ put_unaligned_be16(nf->o_ifphys, ptr); break;
++#endif
+ #ifdef ENABLE_VLAN
+ #define EXTRACT_VLAN_PRIO(tag) ((ntohs(tag) & VLAN_PRIO_MASK) >> VLAN_PRIO_SHIFT)
+ case SRC_VLAN:
+- case dot1qVlanId: put_unaligned(nf->tuple.tag1 & htons(VLAN_VID_MASK), (__be16 *)ptr); break;
+- case dot1qPriority: *ptr = EXTRACT_VLAN_PRIO(nf->tuple.tag1); break;
++ case dot1qVlanId: put_unaligned(nf->tuple.tag[0] & htons(VLAN_VID_MASK), (__be16 *)ptr); break;
++ case dot1qPriority: *ptr = EXTRACT_VLAN_PRIO(nf->tuple.tag[0]); break;
+ case dot1qCustomerVlanId:
+- put_unaligned(nf->tuple.tag2 & htons(VLAN_VID_MASK), (__be16 *)ptr); break;
+- case dot1qCustomerPriority: *ptr = EXTRACT_VLAN_PRIO(nf->tuple.tag2); break;
++ put_unaligned(nf->tuple.tag[1] & htons(VLAN_VID_MASK), (__be16 *)ptr); break;
++ case dot1qCustomerPriority: *ptr = EXTRACT_VLAN_PRIO(nf->tuple.tag[1]); break;
+ #endif
+ #if defined(ENABLE_MAC) || defined(ENABLE_VLAN)
+ case ethernetType: put_unaligned(nf->ethernetType, (__be16 *)ptr); break;
+@@ -3302,6 +3452,21 @@ static inline void add_tpl_field(__u8 *ptr, const int type, const struct ipt_net
+ case destinationMacAddress: memcpy(ptr, &nf->tuple.h_dst, ETH_ALEN); break;
+ case sourceMacAddress: memcpy(ptr, &nf->tuple.h_src, ETH_ALEN); break;
+ #endif
++#ifdef MPLS_DEPTH
++ case MPLS_LABEL_1: memcpy(ptr, &nf->tuple.mpls[0], 3); break;
++ case MPLS_LABEL_2: memcpy(ptr, &nf->tuple.mpls[1], 3); break;
++ case MPLS_LABEL_3: memcpy(ptr, &nf->tuple.mpls[2], 3); break;
++# if MPLS_DEPTH > 3
++ case MPLS_LABEL_4: memcpy(ptr, &nf->tuple.mpls[3], 3); break;
++ case MPLS_LABEL_5: memcpy(ptr, &nf->tuple.mpls[4], 3); break;
++ case MPLS_LABEL_6: memcpy(ptr, &nf->tuple.mpls[5], 3); break;
++ case MPLS_LABEL_7: memcpy(ptr, &nf->tuple.mpls[6], 3); break;
++ case MPLS_LABEL_8: memcpy(ptr, &nf->tuple.mpls[7], 3); break;
++ case MPLS_LABEL_9: memcpy(ptr, &nf->tuple.mpls[8], 3); break;
++ case MPLS_LABEL_10: memcpy(ptr, &nf->tuple.mpls[9], 3); break;
++# endif
++ case mplsTopLabelTTL: *ptr = ntohl(nf->tuple.mpls[0]); break;
++#endif
+ #ifdef ENABLE_DIRECTION
+ case DIRECTION: *ptr = hook2dir(nf->hooknumx - 1); break;
+ #endif
+@@ -3321,6 +3486,7 @@ static inline void add_tpl_field(__u8 *ptr, const int type, const struct ipt_net
+ case icmpTypeCodeIPv4: /*FALLTHROUGH*/
+ case icmpTypeCodeIPv6: put_unaligned(nf->tuple.d_port, (__be16 *)ptr); break;
+ case MUL_IGMP_TYPE: *ptr = nf->tuple.d_port; break;
++ case flowEndReason: *ptr = nf->flowEndReason; break;
+ #ifdef CONFIG_NF_NAT_NEEDED
+ case postNATSourceIPv4Address: put_unaligned(nf->nat->post.s_addr, (__be32 *)ptr); break;
+ case postNATDestinationIPv4Address: put_unaligned(nf->nat->post.d_addr, (__be32 *)ptr); break;
+@@ -3363,18 +3529,6 @@ static inline unsigned long timeout_rate_j(void)
+ return t_rate_j;
+ }
+
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
+-#define IPPROTO_UDPLITE 136
+-#endif
+-
+-#ifndef time_is_before_jiffies
+-#define time_is_before_jiffies(a) time_after(jiffies, a)
+-#endif
+-#ifndef time_is_after_jiffies
+-#define time_is_after_jiffies(a) time_before(jiffies, a)
+-#endif
+-
+-
+ /* return buffer where to write records data */
+ static unsigned char *alloc_record_tpl(struct data_template *tpl)
+ {
+@@ -3382,9 +3536,9 @@ static unsigned char *alloc_record_tpl(struct data_template *tpl)
+
+ /* If previous write was to the same template and there is room, then we just add new record,
+ * otherwise we (re)allocate flowset (and/or whole pdu). */
+- if (unlikely(!pdu_flowset ||
++ if (!pdu_flowset ||
+ pdu_flowset->flowset_id != tpl->template_id_n ||
+- !(ptr = pdu_alloc_fail_export(tpl->rec_size)))) {
++ !(ptr = pdu_alloc_fail_export(tpl->rec_size))) {
+
+ /* if there was previous data template we should pad it to 4 bytes */
+ if (pdu_flowset) {
+@@ -3427,18 +3581,6 @@ static unsigned char *alloc_record_key(const unsigned int t_key, struct data_tem
+ return alloc_record_tpl(tpl);
+ }
+
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(3,14,0)
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
+-#define prandom_u32 get_random_int
+-#elif LINUX_VERSION_CODE < KERNEL_VERSION(3,8,0)
+-#define prandom_u32 random32
+-#endif
+-static inline u32 prandom_u32_max(u32 ep_ro)
+-{
+- return (u32)(((u64) prandom_u32() * ep_ro) >> 32);
+-}
+-#endif
+-
+ static void netflow_export_flow_tpl(struct ipt_netflow *nf)
+ {
+ unsigned char *ptr;
+@@ -3497,12 +3639,12 @@ static void netflow_export_flow_tpl(struct ipt_netflow *nf)
+ tpl_mask |= BTPL_MAC;
+ #endif
+ #ifdef ENABLE_VLAN
+- if (nf->tuple.tag1) {
++ if (nf->tuple.tag[0]) {
+ if (protocol == 9)
+ tpl_mask |= BTPL_VLAN9;
+ else {
+ tpl_mask |= BTPL_VLANX;
+- if (nf->tuple.tag2)
++ if (nf->tuple.tag[1])
+ tpl_mask |= BTPL_VLANI;
+ }
+ }
+@@ -3511,6 +3653,10 @@ static void netflow_export_flow_tpl(struct ipt_netflow *nf)
+ if (nf->ethernetType)
+ tpl_mask |= BTPL_ETHERTYPE;
+ #endif
++#ifdef MPLS_DEPTH
++ if (nf->tuple.mpls[0])
++ tpl_mask |= BTPL_MPLS;
++#endif
+ #ifdef ENABLE_DIRECTION
+ if (nf->hooknumx)
+ tpl_mask |= BTPL_DIRECTION;
+@@ -3657,13 +3803,6 @@ static inline void export_stat(const unsigned int tpl_mask)
+ export_stat_st(tpl_mask, NULL);
+ }
+
+-#ifndef min_not_zero
+-#define min_not_zero(x, y) ({ \
+- typeof(x) __x = (x); \
+- typeof(y) __y = (y); \
+- __x == 0 ? __y : ((__y == 0) ? __x : min(__x, __y)); })
+-#endif
+-
+ static void netflow_export_stats(void)
+ {
+ struct ipt_netflow_stat t = { 0 };
+@@ -3748,20 +3887,6 @@ static void export_sampler_parameters(void)
+ }
+ #endif
+
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(3,2,0)
+-int __ethtool_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+-{
+- ASSERT_RTNL();
+-
+- if (!dev->ethtool_ops->get_settings)
+- return -EOPNOTSUPP;
+-
+- memset(cmd, 0, sizeof(struct ethtool_cmd));
+- cmd->cmd = ETHTOOL_GSET;
+- return dev->ethtool_ops->get_settings(dev, cmd);
+-}
+-#endif
+-
+ static int ethtool_drvinfo(unsigned char *ptr, size_t size, struct net_device *dev)
+ {
+ struct ethtool_drvinfo info = { 0 };
+@@ -3774,8 +3899,8 @@ static int ethtool_drvinfo(unsigned char *ptr, size_t size, struct net_device *d
+ return 0;
+ if (ops->begin) {
+ /* was not called before __ethtool_get_settings() though */
+- if (ops->begin(dev) < 0);
+- return 0;
++ if (ops->begin(dev) < 0)
++ return 0;
+ }
+
+ /* driver name */
+@@ -3792,13 +3917,13 @@ static int ethtool_drvinfo(unsigned char *ptr, size_t size, struct net_device *d
+ if (!n || len <= 1) /* have room for separator too */
+ goto ret;
+
+- /* append basic parameters: speed and port */
+- if (!__ethtool_get_settings(dev, &ecmd)) {
++ /* only get_settings for running devices to not trigger link negotiation */
++ if (dev->flags & IFF_UP &&
++ dev->flags & IFF_RUNNING &&
++ !__ethtool_get_settings(dev, &ecmd)) {
+ char *s, *p;
+
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27)
+-#define ethtool_cmd_speed(x) (x)->speed
+-#endif
++ /* append basic parameters: speed and port */
+ switch (ethtool_cmd_speed(&ecmd)) {
+ case SPEED_10000: s = "10Gb"; break;
+ case SPEED_2500: s = "2.5Gb"; break;
+@@ -3827,14 +3952,6 @@ ret:
+ return size - len;
+ }
+
+-#ifndef ARPHRD_PHONET
+-#define ARPHRD_PHONET 820
+-#define ARPHRD_PHONET_PIPE 821
+-#endif
+-#ifndef ARPHRD_IEEE802154
+-#define ARPHRD_IEEE802154 804
+-#endif
+-
+ static const unsigned short netdev_type[] =
+ {ARPHRD_NETROM, ARPHRD_ETHER, ARPHRD_AX25,
+ ARPHRD_IEEE802, ARPHRD_ARCNET,
+@@ -3939,13 +4056,6 @@ static void export_dev(struct net_device *dev)
+ pdu_ts_mod = jiffies;
+ }
+
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)
+-#define for_each_netdev_ns(net, dev) for (dev = dev_base; dev; dev = dev->next)
+-#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+-#define for_each_netdev_ns(net, d) for_each_netdev(d)
+-#else
+-#define for_each_netdev_ns(net, d) for_each_netdev(net, d)
+-#endif
+ static void export_ifnames(void)
+ {
+ struct net_device *dev;
+@@ -4041,14 +4151,40 @@ static void export_nat_event(struct nat_event *nel)
+ }
+ #endif /* CONFIG_NF_NAT_NEEDED */
+
+-static inline int active_needs_export(const struct ipt_netflow *nf, const long a_timeout, const unsigned long jiff)
++static inline int active_needs_export(const struct ipt_netflow *nf, const long a_timeout,
++ const unsigned long j)
++{
++ return ((j - nf->nf_ts_first) > a_timeout) ||
++ nf->nr_bytes >= FLOW_FULL_WATERMARK;
++}
++
++/* return flowEndReason (rfc5102) */
++/* i_timeout == 0 is flush */
++static inline int inactive_needs_export(const struct ipt_netflow *nf, const long i_timeout,
++ const unsigned long j)
+ {
+- /* active too long, finishing, or having too much bytes */
+- return ((jiff - nf->nf_ts_first) > a_timeout) ||
+- (nf->tuple.protocol == IPPROTO_TCP &&
+- (nf->tcp_flags & TCP_FIN_RST) &&
+- (jiff - nf->nf_ts_last) > (1 * HZ)) ||
+- nf->nr_bytes >= FLOW_FULL_WATERMARK;
++ if (likely(i_timeout)) {
++ if (unlikely((j - nf->nf_ts_last) > i_timeout)) {
++ if (nf->tuple.protocol == IPPROTO_TCP &&
++ (nf->tcp_flags & TCP_FIN_RST))
++ return 0x03; /* end of Flow detected */
++ else
++ return 0x01; /* idle timeout */
++ } else
++ return 0;
++ } else
++ return 0x04; /* forced end */
++}
++
++/* helper which also record to nf->flowEndReason */
++static inline int needs_export_rec(struct ipt_netflow *nf, const long i_timeout,
++ const long a_timeout, const unsigned long j)
++{
++ int reason = inactive_needs_export(nf, i_timeout, j);
++
++ if (!reason && active_needs_export(nf, a_timeout, j))
++ reason = 0x02; /* active timeout or just active flow */
++ return (nf->flowEndReason = reason);
+ }
+
+ /* could be called with zero to flush cache and pdu */
+@@ -4056,13 +4192,12 @@ static inline int active_needs_export(const struct ipt_netflow *nf, const long a
+ /* return number of pdus sent */
+ static int netflow_scan_and_export(const int flush)
+ {
+- long i_timeout = inactive_timeout * HZ;
++ const long i_timeout = flush? 0 : inactive_timeout * HZ;
++ const long a_timeout = active_timeout * HZ;
+ #ifdef HAVE_LLIST
+ struct llist_node *node;
+-#else
+- long a_timeout = active_timeout * HZ;
+ #endif
+- int pdu_c = pdu_count;
++ const int pdu_c = pdu_count;
+ LIST_HEAD(export_list);
+ struct ipt_netflow *nf, *tmp;
+ int i;
+@@ -4070,9 +4205,6 @@ static int netflow_scan_and_export(const int flush)
+ unsigned char mode;
+ #endif
+
+- if (flush)
+- i_timeout = 0;
+-
+ if (protocol >= 9) {
+ netflow_export_stats();
+ #ifdef ENABLE_SAMPLER
+@@ -4091,14 +4223,7 @@ static int netflow_scan_and_export(const int flush)
+ }
+ list_for_each_entry_safe_reverse(nf, tmp, &stripe->list, flows_list) {
+ ++wk_count;
+- if (((jiffies - nf->nf_ts_last) >= i_timeout)
+-#ifdef HAVE_LLIST
+- /* exportable actives already go into export_llist,
+- * thus this check is redundant. */
+-#else
+- || active_needs_export(nf, a_timeout, jiffies)
+-#endif
+- ) {
++ if (needs_export_rec(nf, i_timeout, a_timeout, jiffies)) {
+ hlist_del(&nf->hlist);
+ list_del(&nf->flows_list);
+ list_add(&nf->flows_list, &export_list);
+@@ -4387,13 +4512,6 @@ netflow_target_check(const struct xt_tgchk_param *par)
+ if (strcmp("nat", tablename) == 0) {
+ /* In the nat table we only see single packet per flow, which is useless. */
+ printk(KERN_ERR "%s target: is not valid in %s table\n", target->name, tablename);
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35)
+-#define CHECK_FAIL 0
+-#define CHECK_OK 1
+-#else
+-#define CHECK_FAIL -EINVAL
+-#define CHECK_OK 0
+-#endif
+ return CHECK_FAIL;
+ }
+ if (target->family == AF_INET6 && protocol == 5) {
+@@ -4404,9 +4522,6 @@ netflow_target_check(const struct xt_tgchk_param *par)
+ }
+
+ #define SetXBit(x) (0x8000 >> (x)) /* Proper bit for htons later. */
+-#ifndef IPPROTO_MH
+-#define IPPROTO_MH 135
+-#endif
+ static inline __u16 observed_hdrs(const __u8 currenthdr)
+ {
+ switch (currenthdr) {
+@@ -4537,69 +4652,80 @@ static inline __u32 tcp_options(const struct sk_buff *skb, const unsigned int pt
+ return ret;
+ }
+
+-#ifdef ENABLE_VLAN
+-/* double tagged header */
+-struct vlan_ethhdr2 {
+- unsigned char h_dest[ETH_ALEN];
+- unsigned char h_source[ETH_ALEN];
+- __be16 h_vlan_proto;
+- __be16 h_vlan_TCI;
+- __be16 h2_vlan_proto;
+- __be16 h2_vlan_TCI;
+- __be16 h2_vlan_encapsulated_proto;
+-};
+-static inline struct vlan_ethhdr2 *vlan_eth_hdr2(const struct sk_buff *skb)
++/* check if data region is in header boundary */
++inline static int skb_in_header(const struct sk_buff *skb, const void *ptr, size_t off)
+ {
+- return (struct vlan_ethhdr2 *)skb_mac_header(skb);
++ return ((unsigned char *)ptr + off) <= skb->data;
+ }
+-#define VLAN_ETH_H2LEN (VLAN_ETH_HLEN + 4)
+-#ifndef ETH_P_8021AD
+-#define ETH_P_8021AD 0x88A8 /* 802.1ad Service VLAN */
+-#endif
+-#ifndef ETH_P_QINQ1
+-#define ETH_P_QINQ1 0x9100 /* deprecated QinQ VLAN */
+-#define ETH_P_QINQ2 0x9200 /* deprecated QinQ VLAN */
+-#define ETH_P_QINQ3 0x9300 /* deprecated QinQ VLAN */
+-#endif
+-/* http://tools.ietf.org/html/rfc7133 */
+-static inline __u16 parse_vlan_tags(const struct sk_buff *skb, struct ipt_netflow_tuple *tuple)
+-{
+- /* no even untagged ethernet header */
+- if (skb_mac_header(skb) < skb->head ||
+- skb_mac_header(skb) + ETH_HLEN > skb->data)
+- return 0;
+-
+- switch (eth_hdr(skb)->h_proto) {
+- case htons(ETH_P_QINQ1):
+- case htons(ETH_P_QINQ2):
+- case htons(ETH_P_QINQ3):
+- case htons(ETH_P_8021AD): /* S-TAG or B-TAG */
+- case htons(ETH_P_8021Q): /* C-TAG */
+- /* tagged and have full vlan header */
+- if (skb_mac_header(skb) + VLAN_ETH_HLEN <= skb->data)
+- break;
+- /* FALLTHROUGH */
+- default:
+- return eth_hdr(skb)->h_proto;
+- }
+
+- /* outer tag */
+- tuple->tag1 = vlan_eth_hdr(skb)->h_vlan_TCI;
++static inline int eth_p_vlan(__be16 eth_type)
++{
++ return eth_type == htons(ETH_P_8021Q) ||
++ eth_type == htons(ETH_P_8021AD);
++}
+
+- switch (vlan_eth_hdr(skb)->h_vlan_encapsulated_proto) {
+- case htons(ETH_P_8021Q): /* C-TAG */
+- if (skb_mac_header(skb) + VLAN_ETH_H2LEN <= skb->data)
+- break;
+- /* FALLTHROUGH */
+- default:
+- return vlan_eth_hdr(skb)->h_vlan_encapsulated_proto;
+- }
++/* Extract all L2 header data, currently (in iptables) skb->data is
++ * pointing to network_header, so we use mac_header instead. */
++/* Parse eth header, then vlans, then mpls. */
++static void parse_l2_header(const struct sk_buff *skb, struct ipt_netflow_tuple *tuple)
++{
++#if defined(ENABLE_MAC) || defined(ENABLE_VLAN) || defined(MPLS_DEPTH)
++#define ENABLE_L2
++ unsigned char *mac_header = skb_mac_header(skb);
++# if defined(ENABLE_VLAN) || defined(MPLS_DEPTH)
++ unsigned int hdr_depth;
++ __be16 proto;
++# endif
++# ifdef ENABLE_VLAN
++ int tag_num = 0;
+
+- /* second tag */
+- tuple->tag2 = vlan_eth_hdr2(skb)->h2_vlan_TCI;
+- return vlan_eth_hdr2(skb)->h2_vlan_encapsulated_proto;
++ /* get vlan tag that is saved in skb->vlan_tci */
++ if (vlan_tx_tag_present(skb))
++ tuple->tag[tag_num++] = htons(vlan_tx_tag_get(skb));
++# endif
++ if (mac_header < skb->head ||
++ mac_header + ETH_HLEN > skb->data)
++ return;
++# ifdef ENABLE_MAC
++ memcpy(&tuple->h_dst, eth_hdr(skb)->h_dest, ETH_ALEN);
++ memcpy(&tuple->h_src, eth_hdr(skb)->h_source, ETH_ALEN);
++# endif
++# if defined(ENABLE_VLAN) || defined(MPLS_DEPTH)
++ hdr_depth = ETH_HLEN;
++ proto = eth_hdr(skb)->h_proto;
++ if (eth_p_vlan(proto)) {
++ do {
++ const struct vlan_hdr *vh;
++
++ vh = (struct vlan_hdr *)(mac_header + hdr_depth);
++ if (!skb_in_header(skb, vh, VLAN_HLEN))
++ return;
++ proto = vh->h_vlan_encapsulated_proto;
++# ifdef ENABLE_VLAN
++ if (tag_num < MAX_VLAN_TAGS)
++ tuple->tag[tag_num++] = vh->h_vlan_TCI;
++# endif
++ hdr_depth += VLAN_HLEN;
++ } while (eth_p_vlan(proto));
++ }
++# ifdef MPLS_DEPTH
++ if (eth_p_mpls(proto)) {
++ const struct mpls_label *mpls;
++ int label_num = 0;
++
++ do {
++ mpls = (struct mpls_label *)(mac_header + hdr_depth);
++ if (!skb_in_header(skb, mpls, MPLS_HLEN))
++ return;
++ if (label_num < MPLS_DEPTH)
++ tuple->mpls[label_num++] = mpls->entry;
++ hdr_depth += MPLS_HLEN;
++ } while (!(mpls->entry & htonl(MPLS_LS_S_MASK)));
++ }
++# endif
++# endif /* defined(ENABLE_VLAN) || defined(MPLS_DEPTH) */
++#endif /* defined(ENABLE_MAC) || defined(ENABLE_VLAN) || defined(MPLS_DEPTH) */
+ }
+-#endif /* ENABLE_VLAN */
+
+ /* packet receiver */
+ static unsigned int netflow_target(
+@@ -4622,6 +4748,8 @@ static unsigned int netflow_target(
+ const void *targinfo
+ # endif
+ #else /* since 2.6.28 */
++# define if_in par->in
++# define if_out par->out
+ # if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35)
+ const struct xt_target_param *par
+ # else
+@@ -4631,7 +4759,11 @@ static unsigned int netflow_target(
+ )
+ {
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+- const struct sk_buff *skb = *pskb;
++# ifndef ENABLE_L2
++ /* pskb_may_pull() may modify skb */
++ const
++# endif
++ struct sk_buff *skb = *pskb;
+ #endif
+ union {
+ struct iphdr ip;
+@@ -4660,12 +4792,15 @@ static unsigned int netflow_target(
+ int options = 0;
+ int tcpoptions = 0;
+ struct stripe_entry *stripe;
+-#if defined(ENABLE_MAC) || defined(ENABLE_VLAN)
+- __be16 ethernetType = 0;
+-#endif
+
+- iph = skb_header_pointer(skb, 0, (likely(family == AF_INET))? sizeof(_iph.ip) : sizeof(_iph.ip6), &iph);
+- if (unlikely(iph == NULL)) {
++ if (unlikely(
++#ifdef ENABLE_L2
++ /* to ensure that full L2 headers are present */
++ unlikely(!pskb_may_pull(skb, 0)) ||
++#endif
++ !(iph = skb_header_pointer(skb, 0,
++ (likely(family == AF_INET))? sizeof(_iph.ip) : sizeof(_iph.ip6),
++ &iph)))) {
+ NETFLOW_STAT_INC(truncated);
+ NETFLOW_STAT_INC(pkt_drop);
+ NETFLOW_STAT_ADD(traf_drop, skb->len);
+@@ -4673,46 +4808,18 @@ static unsigned int netflow_target(
+ return IPT_CONTINUE;
+ }
+
+-#ifdef ENABLE_DEBUGFS
+- if (atomic_read(&freeze)) {
+- NETFLOW_STAT_INC(freeze_err);
+- NETFLOW_STAT_INC(pkt_drop);
+- NETFLOW_STAT_ADD(traf_drop, skb->len);
+- NETFLOW_STAT_TS(drop);
+- return IPT_CONTINUE;
+- }
++ memset(&tuple, 0, sizeof(tuple));
++ tuple.l3proto = family;
++#ifdef ENABLE_PHYSDEV_OVER
++ if (skb->nf_bridge && skb->nf_bridge->physindev)
++ tuple.i_ifc = skb->nf_bridge->physindev->ifindex;
++ else /* FALLTHROUGH */
+ #endif
+-
+- tuple.l3proto = family;
+- 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
+- tcp_flags = 0; /* Cisco sometimes have TCP ACK for non TCP packets, don't get it */
++ tcp_flags = 0;
+ s_mask = 0;
+ d_mask = 0;
+-#ifdef ENABLE_MAC
+- if (skb_mac_header(skb) >= skb->head &&
+- skb_mac_header(skb) + ETH_HLEN <= skb->data) {
+- memcpy(&tuple.h_dst, eth_hdr(skb)->h_dest, ETH_ALEN);
+- memcpy(&tuple.h_src, eth_hdr(skb)->h_source, ETH_ALEN);
+-#ifndef ENABLE_VLAN
+- ethernetType = eth_hdr(skb)->h_proto;
+-#endif
+- } else {
+- memset(&tuple.h_dst, 0, ETH_ALEN);
+- memset(&tuple.h_src, 0, ETH_ALEN);
+- }
+-#endif
+-#ifdef ENABLE_VLAN
+- tuple.tag2 = tuple.tag1 = 0;
+- if (vlan_tx_tag_present(skb))
+- tuple.tag1 = htons(vlan_tx_tag_get(skb));
+- ethernetType = parse_vlan_tags(skb, &tuple);
+-#endif
++ parse_l2_header(skb, &tuple);
+
+ if (likely(family == AF_INET)) {
+ tuple.src = (union nf_inet_addr){ .ip = iph->ip.saddr };
+@@ -4832,16 +4939,16 @@ do_protocols:
+ case IPPROTO_ICMP: {
+ struct icmphdr _hdr, *hp;
+
+- if (likely(family == AF_INET &&
+- (hp = skb_header_pointer(skb, ptr, 2, &_hdr))))
++ if (likely(family == AF_INET) &&
++ likely(hp = skb_header_pointer(skb, ptr, 2, &_hdr)))
+ tuple.d_port = htons((hp->type << 8) | hp->code);
+ break;
+ }
+ case IPPROTO_ICMPV6: {
+ struct icmp6hdr _icmp6h, *ic;
+
+- if (likely(family == AF_INET6 &&
+- (ic = skb_header_pointer(skb, ptr, 2, &_icmp6h))))
++ if (likely(family == AF_INET6) &&
++ likely(ic = skb_header_pointer(skb, ptr, 2, &_icmp6h)))
+ tuple.d_port = htons((ic->icmp6_type << 8) | ic->icmp6_code);
+ break;
+ }
+@@ -4856,8 +4963,8 @@ do_protocols:
+ struct ip_auth_hdr _hdr, *hp;
+
+ /* This is for IPv4 only. IPv6 it's parsed above. */
+- if (likely(family == AF_INET &&
+- (hp = skb_header_pointer(skb, ptr, 8, &_hdr))))
++ if (likely(family == AF_INET) &&
++ likely(hp = skb_header_pointer(skb, ptr, 8, &_hdr)))
+ SAVE_SPI(tuple, hp->spi);
+ break;
+ }
+@@ -4967,27 +5074,43 @@ do_protocols:
+ #endif
+ nf->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;
++#ifdef ENABLE_PHYSDEV_OVER
++ if (skb->nf_bridge && skb->nf_bridge->physoutdev)
++ nf->o_ifc = skb->nf_bridge->physoutdev->ifindex;
+ #endif
++
+ #ifdef SNMP_RULES
+ rcu_read_lock();
+-# if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
++#else
++# define resolve_snmp(dev) ((dev)? (dev)->ifindex : -1)
++#endif
++/* copy and snmp-resolve device with physdev overriding normal dev */
++#define copy_dev(out, physdev, dev) \
++ if (skb->nf_bridge && skb->nf_bridge->physdev) \
++ out = resolve_snmp(skb->nf_bridge->physdev); \
++ else \
++ out = resolve_snmp(dev);
++#ifdef ENABLE_PHYSDEV
++ copy_dev(nf->o_ifphys, physoutdev, if_out);
++ copy_dev(nf->i_ifphys, physindev, if_in);
++#endif
++#ifdef SNMP_RULES
++# ifdef ENABLE_PHYSDEV_OVER
++ copy_dev(nf->o_ifcr, physoutdev, if_out);
++ copy_dev(nf->i_ifcr, physindev, if_in);
++# else
+ nf->o_ifcr = resolve_snmp(if_out);
+ nf->i_ifcr = resolve_snmp(if_in);
+-# else
+- nf->o_ifcr = resolve_snmp(par->out);
+- nf->i_ifcr = resolve_snmp(par->in);
+ # endif
+ rcu_read_unlock();
++
+ #endif
+ nf->s_mask = s_mask;
+ nf->d_mask = d_mask;
+
+ #if defined(ENABLE_MAC) || defined(ENABLE_VLAN)
+- nf->ethernetType = ethernetType;
++ nf->ethernetType = skb->protocol;
+ #endif
+
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26)
+@@ -5034,7 +5157,7 @@ do_protocols:
+
+ #define LIST_IS_NULL(name) (!(name)->next)
+
+- if (likely(active_needs_export(nf, active_timeout * HZ, jiffies))) {
++ if (unlikely(active_needs_export(nf, active_timeout * HZ, jiffies))) {
+ /* ok, if this is active flow to be exported */
+ #ifdef HAVE_LLIST
+ /* delete from hash and add to the export llist */
+@@ -5150,9 +5273,6 @@ static void register_ct_events(void)
+ /* Reference netlink module to prevent it's unsafe unload before us. */
+ if (!referenced && (netlink_m = find_module(NETLINK_M))) {
+ referenced++;
+-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)
+-#define use_module ref_module
+-#endif
+ use_module(THIS_MODULE, netlink_m);
+ }
+
+@@ -5190,39 +5310,6 @@ static void unregister_ct_events(void)
+ }
+ #endif /* CONFIG_NF_NAT_NEEDED */
+
+-#ifndef NF_IP_LOCAL_IN /* 2.6.25 */
+-#define NF_IP_PRE_ROUTING NF_INET_PRE_ROUTING
+-#define NF_IP_LOCAL_IN NF_INET_LOCAL_IN
+-#define NF_IP_FORWARD NF_INET_FORWARD
+-#define NF_IP_LOCAL_OUT NF_INET_LOCAL_OUT
+-#define NF_IP_POST_ROUTING NF_INET_POST_ROUTING
+-#endif
+-
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
+-/* net/netfilter/x_tables.c */
+-static void xt_unregister_targets(struct xt_target *target, unsigned int n)
+-{
+- unsigned int i;
+-
+- for (i = 0; i < n; i++)
+- xt_unregister_target(&target[i]);
+-}
+-static int xt_register_targets(struct xt_target *target, unsigned int n)
+-{
+- unsigned int i;
+-
+- int err = 0;
+- for (i = 0; i < n; i++)
+- if ((err = xt_register_target(&target[i])))
+- goto err;
+- return err;
+-err:
+- if (i > 0)
+- xt_unregister_targets(target, i);
+- return err;
+-}
+-#endif
+-
+ static struct ipt_target ipt_netflow_reg[] __read_mostly = {
+ {
+ .name = "NETFLOW",
+@@ -5296,9 +5383,6 @@ static int __init ipt_netflow_init(void)
+ clear_ipt_netflow_stat();
+
+ if (!hashsize) {
+-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,11,0)
+-#define num_physpages totalram_pages
+-#endif
+ /* use 1/1024 of memory, 1M for hash table on 1G box */
+ unsigned long memksize = (num_physpages << PAGE_SHIFT) / 1024;
+
+@@ -5318,6 +5402,11 @@ static int __init ipt_netflow_init(void)
+ goto err;
+ }
+
++#ifdef MPLS_DEPTH
++ if (MPLS_DEPTH >= 0 && MPLS_DEPTH < 10)
++ template_mpls.types[MPLS_LABELS_BASE_INDEX + MPLS_DEPTH] = 0;
++#endif
++
+ for (i = 0; i < LOCK_COUNT; i++) {
+ spin_lock_init(&htable_stripes[i].lock);
+ INIT_LIST_HEAD(&htable_stripes[i].list);
+@@ -5339,10 +5428,8 @@ static int __init ipt_netflow_init(void)
+ goto err_free_netflow_slab;
+ if (!register_stat("ipt_netflow_snmp", &snmp_seq_fops))
+ goto err_free_proc_stat1;
+-
+-#ifdef ENABLE_DEBUGFS
+- flows_dump_d = debugfs_create_file("netflow_dump", S_IRUGO, NULL, NULL, &flows_dump_fops);
+-#endif
++ if (!register_stat("ipt_netflow_flows", &flows_seq_fops))
++ goto err_free_proc_stat2;
+
+ #ifdef CONFIG_SYSCTL
+ ctl_table_renumber(netflow_sysctl_table);
+@@ -5357,7 +5444,7 @@ static int __init ipt_netflow_init(void)
+ #endif
+ if (!netflow_sysctl_header) {
+ printk(KERN_ERR "netflow: can't register to sysctl\n");
+- goto err_free_proc_stat2;
++ goto err_free_proc_stat3;
+ } else
+ printk(KERN_INFO "netflow: registered: sysctl net.netflow\n");
+ #endif
+@@ -5443,12 +5530,11 @@ err_stop_timer:
+ err_free_sysctl:
+ #ifdef CONFIG_SYSCTL
+ unregister_sysctl_table(netflow_sysctl_header);
+-err_free_proc_stat2:
+-#endif
+-#ifdef ENABLE_DEBUGFS
+- debugfs_remove(flows_dump_d);
+ #endif
++err_free_proc_stat3:
+ #ifdef CONFIG_PROC_FS
++ remove_proc_entry("ipt_netflow_flows", INIT_NET(proc_net_stat));
++err_free_proc_stat2:
+ remove_proc_entry("ipt_netflow_snmp", INIT_NET(proc_net_stat));
+ err_free_proc_stat1:
+ remove_proc_entry("ipt_netflow", INIT_NET(proc_net_stat));
+@@ -5469,10 +5555,8 @@ static void __exit ipt_netflow_fini(void)
+ #ifdef CONFIG_SYSCTL
+ unregister_sysctl_table(netflow_sysctl_header);
+ #endif
+-#ifdef ENABLE_DEBUGFS
+- debugfs_remove(flows_dump_d);
+-#endif
+ #ifdef CONFIG_PROC_FS
++ remove_proc_entry("ipt_netflow_flows", INIT_NET(proc_net_stat));
+ remove_proc_entry("ipt_netflow_snmp", INIT_NET(proc_net_stat));
+ remove_proc_entry("ipt_netflow", INIT_NET(proc_net_stat));
+ #endif
+diff --git a/ipt_NETFLOW.h b/ipt_NETFLOW.h
+index 59512f0..eb00e94 100644
+--- a/ipt_NETFLOW.h
++++ b/ipt_NETFLOW.h
+@@ -16,8 +16,8 @@
+ *
+ */
+
+-#ifndef _IP_NETFLOW_H
+-#define _IP_NETFLOW_H
++#ifndef _IPT_NETFLOW_H
++#define _IPT_NETFLOW_H
+
+ /*
+ * Some tech info:
+@@ -113,9 +113,20 @@ struct netflow5_pdu {
+ two(61, DIRECTION, flowDirection, 1) \
+ two(62, IPV6_NEXT_HOP, ipNextHopIPv6Address, 16) \
+ two(64, IPV6_OPTION_HEADERS, ipv6ExtensionHeaders, 2) \
++ two(70, MPLS_LABEL_1, mplsTopLabelStackSection, 3) \
++ two(71, MPLS_LABEL_2, mplsLabelStackSection2, 3) \
++ two(72, MPLS_LABEL_3, mplsLabelStackSection3, 3) \
++ two(73, MPLS_LABEL_4, mplsLabelStackSection4, 3) \
++ two(74, MPLS_LABEL_5, mplsLabelStackSection5, 3) \
++ two(75, MPLS_LABEL_6, mplsLabelStackSection6, 3) \
++ two(76, MPLS_LABEL_7, mplsLabelStackSection7, 3) \
++ two(77, MPLS_LABEL_8, mplsLabelStackSection8, 3) \
++ two(78, MPLS_LABEL_9, mplsLabelStackSection9, 3) \
++ two(79, MPLS_LABEL_10, mplsLabelStackSection10, 3) \
+ one(80, destinationMacAddress, 6) \
+ two(82, IF_NAME, interfaceName, IF_NAME_SZ) \
+ two(83, IF_DESC, interfaceDescription, IF_DESC_SZ) \
++ one(136, flowEndReason, 1) \
+ one(138, observationPointId, 4) \
+ one(139, icmpTypeCodeIPv6, 2) \
+ one(141, LineCardId, 4) \
+@@ -135,6 +146,9 @@ struct netflow5_pdu {
+ one(166, notSentFlowTotalCount, 8) \
+ one(167, notSentPacketTotalCount, 8) \
+ one(168, notSentOctetTotalCount, 8) \
++ one(200, mplsTopLabelTTL, 1) \
++ one(201, mplsLabelStackLength, 1) \
++ one(202, mplsLabelStackDepth, 1) \
+ one(208, ipv4Options, 4) \
+ one(209, tcpOptions, 4) \
+ one(225, postNATSourceIPv4Address, 4) \
+@@ -146,6 +160,8 @@ struct netflow5_pdu {
+ one(244, dot1qPriority, 1) \
+ one(245, dot1qCustomerVlanId, 2) \
+ one(246, dot1qCustomerPriority, 1) \
++ one(252, ingressPhysicalInterface, 2) \
++ one(253, egressPhysicalInterface, 2) \
+ one(256, ethernetType, 2) \
+ one(295, IPSecSPI, 4) \
+ one(300, observationDomainName, 128) \
+@@ -240,18 +256,10 @@ struct ipfix_pdu {
+ * not searchable and will be exported soon. */
+ #define FLOW_FULL_WATERMARK 0xffefffff
+
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25)
+-union nf_inet_addr {
+- __be32 ip;
+- __be32 ip6[4];
+- struct in_addr in;
+- struct in6_addr in6;
+-};
+-#endif
+-
+ #define EXTRACT_SPI(tuple) ((tuple.s_port << 16) | tuple.d_port)
+ #define SAVE_SPI(tuple, spi) { tuple.s_port = spi >> 16; \
+ tuple.d_port = spi; }
++#define MAX_VLAN_TAGS 2
+
+ /* hashed data which identify unique flow */
+ /* 16+16 + 2+2 + 2+1+1+1 = 41 */
+@@ -260,10 +268,12 @@ struct ipt_netflow_tuple {
+ union nf_inet_addr dst;
+ __be16 s_port; // Network byte order
+ __be16 d_port; // -"-
++#ifdef MPLS_DEPTH
++ __be32 mpls[MPLS_DEPTH]; /* Network byte order */
++#endif
+ __u16 i_ifc; // Host byte order
+ #ifdef ENABLE_VLAN
+- __be16 tag1; // Network byte order (outer tag)
+- __be16 tag2; // -"-
++ __be16 tag[MAX_VLAN_TAGS]; // Network byte order (outer tag first)
+ #endif
+ __u8 protocol;
+ __u8 tos;
+@@ -287,13 +297,18 @@ struct ipt_netflow {
+ __be16 ethernetType; /* Network byte order */
+ #endif
+ __u16 o_ifc;
++#ifdef ENABLE_PHYSDEV
++ __u16 i_ifphys;
++ __u16 o_ifphys;
++#endif
+ #ifdef SNMP_RULES
+- __u16 i_ifcr;
++ __u16 i_ifcr; /* translated interface numbers*/
+ __u16 o_ifcr;
+ #endif
+ __u8 s_mask;
+ __u8 d_mask;
+ __u8 tcp_flags; /* `OR' of all tcp flags */
++ __u8 flowEndReason;
+ #ifdef ENABLE_DIRECTION
+ __u8 hooknumx; /* hooknum + 1 */
+ #endif
+@@ -362,8 +377,7 @@ static inline int ipt_netflow_tuple_equal(const struct ipt_netflow_tuple *t1,
+ struct ipt_netflow_sock {
+ struct list_head list;
+ struct socket *sock;
+- __be32 ipaddr; // destination
+- unsigned short port;
++ struct sockaddr_storage addr; // destination
+ atomic_t wmem_peak; // sk_wmem_alloc peak value
+ unsigned int err_connect; // connect errors
+ unsigned int err_full; // socket filled error
+@@ -446,9 +460,6 @@ struct ipt_netflow_stat {
+ unsigned int frags; // packets stat (drop)
+ unsigned int maxflows_err; // maxflows reached (drop)
+ unsigned int alloc_err; // failed to allocate memory (drop & lost)
+-#ifdef ENABLE_DEBUGFS
+- unsigned int freeze_err; // freeze errors (drop)
+-#endif
+ struct duration drop;
+ unsigned int send_success; // sendmsg() ok
+ unsigned int send_failed; // sendmsg() failed
+@@ -478,10 +489,5 @@ struct ipt_netflow_stat {
+ int metric; // one minute ewma of hash efficiency
+ };
+
+-#ifndef list_first_entry
+-#define list_first_entry(ptr, type, member) \
+- list_entry((ptr)->next, type, member)
+-#endif
+-
+ #endif
+ /* vim: set sw=8: */
+diff --git a/snmp_NETFLOW.c b/snmp_NETFLOW.c
+index cf1d743..62457db 100644
+--- a/snmp_NETFLOW.c
++++ b/snmp_NETFLOW.c
+@@ -368,10 +368,10 @@ static int iptNetflowSysctl_handler(
+ return sysctl_read(request, obj);
+ case MODE_SET_RESERVE1:
+ sys = find_varinfo(sysctls, obj);
+- if (request->requestvb->type != sys->type)
+- netsnmp_request_set_error(request, SNMP_ERR_WRONGTYPE);
+- if (!sysctl_access_ok(sys->name))
++ if (!sys || !sysctl_access_ok(sys->name))
+ netsnmp_request_set_error(request, SNMP_ERR_NOSUCHNAME);
++ if (sys && request->requestvb->type != sys->type)
++ netsnmp_request_set_error(request, SNMP_ERR_WRONGTYPE);
+ break;
+ case MODE_SET_RESERVE2:
+ case MODE_SET_FREE:
+diff --git a/testing.sh b/testing.sh
+index b465c8d..45a0619 100755
+--- a/testing.sh
++++ b/testing.sh
+@@ -6,7 +6,7 @@ if [ "$1" = "" ]; then
+ echo Maintainer only tool.
+ exit 1
+ elif [ "$1" = all ]; then
+- exec bash $0 linux-2.6.18 centos5 linux-3.11.2 centos6 linux-3.4.66 linux-3.9.11 centos7 linux-3.14 linux-3.17
++ exec bash $0 linux-2.6.18 centos5 linux-3.11.2 centos6 linux-3.4.66 linux-3.9.11 centos7 linux-3.14 linux-3.17 linux-3.19
+ exit 1
+ fi
+
+@@ -28,11 +28,17 @@ readarray -t opts <<EOF
+ --enable-snmp-rules
+ --enable-macaddress
+ --enable-vlan
++ --promisc-mpls
+ --enable-direction
+ --enable-sampler
+ --enable-sampler=hash
+- --enable-promisc
++ --enable-promisc --promisc-mpls
++ --enable-physdev
++ --enable-physdev-override
+ EOF
++if [ "$SHORT" ]; then
++ opts=("$SHORT")
++fi
+
+ colorecho() {
+ echo -e "\033[1;32m$@\033[m"
+diff --git a/version.sh b/version.sh
+index e39433b..2509149 100755
+--- a/version.sh
++++ b/version.sh
+@@ -7,25 +7,29 @@ PATH=$PATH:/usr/local/bin:/usr/bin:/bin
+ MVERSION=`sed -n 's/^#define.*IPT_NETFLOW_VERSION.*"\(.*\)".*/\1/p' ipt_NETFLOW.c`
+
+ # GITVERSION overrides base version.
+-if [ -e version.h ]; then
++if [ -e version.h ] && grep -q GITVERSION version.h; then
+ MVERSION=`sed -n 's/#define GITVERSION "\(.*\)".*/\1/p' version.h`
+ fi
+
+ # git describe overrides version from the source.
+ if [ -d .git ] && which git >/dev/null 2>&1; then \
+- GVERSION=`git describe --dirty`
+- MVERSION=${GVERSION#v}
++ GVERSION=`git describe --dirty 2>/dev/null`
++ if [ "$GVERSION" ]; then
++ MVERSION=${GVERSION#v}
++ fi
+ else
+ GVERSION=
+ fi
+
+ if [ "$1" = --define ]; then
+- # output version.h which is GITVERSION or empty.
++ # called from Makefile to create version.h
++ # which should contain GITVERSION or be empty.
+ if [ "$GVERSION" ]; then
+ echo "#define GITVERSION \"$MVERSION\""
+ else
+- echo "/* kernel doesn't like empty files */"
++ echo "/* placeholder, because kernel doesn't like empty files */"
+ fi
+ else
++ # normal run
+ echo $MVERSION
+ fi