2015-11-09 14:56:51 +00:00

3144 lines
95 KiB
Diff

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