|
Line
Link Here
|
| 0 |
-- /dev/null |
0 |
++ b/include/linux/netfilter_ipv4/ipt_NETFLOW.h |
|
Line 0
Link Here
|
| 0 |
-- a/net/ipv4/netfilter/Kconfig |
1 |
|
|
|
2 |
#ifndef _IP_NETFLOW_H |
| 3 |
#define _IP_NETFLOW_H |
| 4 |
|
| 5 |
/* |
| 6 |
* Some tech info: |
| 7 |
* http://www.cisco.com/en/US/products/ps6601/prod_white_papers_list.html |
| 8 |
* http://www.cisco.com/en/US/products/sw/netmgtsw/ps1964/products_implementation_design_guide09186a00800d6a11.html |
| 9 |
*/ |
| 10 |
|
| 11 |
#define NETFLOW5_RECORDS_MAX 30 |
| 12 |
|
| 13 |
struct netflow5_record { |
| 14 |
__be32 s_addr; |
| 15 |
__be32 d_addr; |
| 16 |
__be32 nexthop; |
| 17 |
__be16 i_ifc; |
| 18 |
__be16 o_ifc; |
| 19 |
__be32 nr_packets; |
| 20 |
__be32 nr_octets; |
| 21 |
__be32 ts_first; |
| 22 |
__be32 ts_last; |
| 23 |
__be16 s_port; |
| 24 |
__be16 d_port; |
| 25 |
__u8 reserved; |
| 26 |
__u8 tcp_flags; |
| 27 |
__u8 protocol; |
| 28 |
__u8 tos; |
| 29 |
__be16 s_as; |
| 30 |
__be16 d_as; |
| 31 |
__u8 s_mask; |
| 32 |
__u8 d_mask; |
| 33 |
__u16 padding; |
| 34 |
} __attribute__ ((packed)); |
| 35 |
|
| 36 |
/* NetFlow v5 packet */ |
| 37 |
struct netflow5_pdu { |
| 38 |
__be16 version; |
| 39 |
__be16 nr_records; |
| 40 |
__be32 ts_uptime; |
| 41 |
__be32 ts_usecs; |
| 42 |
__be32 ts_unsecs; |
| 43 |
__be32 seq; |
| 44 |
__u8 eng_type; |
| 45 |
__u8 eng_id; |
| 46 |
__u16 padding; |
| 47 |
struct netflow5_record flow[NETFLOW5_RECORDS_MAX]; |
| 48 |
} __attribute__ ((packed)); |
| 49 |
#define NETFLOW5_HEADER_SIZE (sizeof(struct netflow5_pdu) - NETFLOW5_RECORDS_MAX * sizeof(struct netflow5_record)) |
| 50 |
|
| 51 |
/* hashed data which identify unique flow */ |
| 52 |
struct ipt_netflow_tuple { |
| 53 |
__be32 s_addr; // Network byte order |
| 54 |
__be32 d_addr; // -"- |
| 55 |
__be16 s_port; // -"- |
| 56 |
__be16 d_port; // -"- |
| 57 |
__be16 i_ifc; // Local byte order |
| 58 |
__u8 protocol; |
| 59 |
__u8 tos; |
| 60 |
}; |
| 61 |
/* tuple size is rounded to u32s */ |
| 62 |
#define NETFLOW_TUPLE_SIZE (sizeof(struct ipt_netflow_tuple) / 4) |
| 63 |
|
| 64 |
/* maximum bytes flow can have, after it reached flow become not searchable and will be exported soon */ |
| 65 |
#define FLOW_FULL_WATERMARK 0xffefffff |
| 66 |
|
| 67 |
/* flow entry */ |
| 68 |
struct ipt_netflow { |
| 69 |
struct hlist_node hlist; // hashtable search chain |
| 70 |
struct list_head list; // all flows chain |
| 71 |
|
| 72 |
/* unique per flow data (hashed, NETFLOW_TUPLE_SIZE) */ |
| 73 |
struct ipt_netflow_tuple tuple; |
| 74 |
|
| 75 |
/* volatile data */ |
| 76 |
__be16 o_ifc; |
| 77 |
__u8 s_mask; |
| 78 |
__u8 d_mask; |
| 79 |
|
| 80 |
/* flow statistics */ |
| 81 |
u_int32_t nr_packets; |
| 82 |
u_int32_t nr_bytes; |
| 83 |
unsigned long ts_first; |
| 84 |
unsigned long ts_last; |
| 85 |
__u8 tcp_flags; /* `OR' of all tcp flags */ |
| 86 |
}; |
| 87 |
|
| 88 |
static inline int ipt_netflow_tuple_equal(const struct ipt_netflow_tuple *t1, |
| 89 |
const struct ipt_netflow_tuple *t2) |
| 90 |
{ |
| 91 |
return (!memcmp(t1, t2, sizeof(struct ipt_netflow_tuple))); |
| 92 |
} |
| 93 |
|
| 94 |
struct ipt_netflow_sock { |
| 95 |
struct list_head list; |
| 96 |
struct socket *sock; |
| 97 |
__be32 ipaddr; |
| 98 |
unsigned short port; |
| 99 |
atomic_t wmem_peak; // sk_wmem_alloc peak value |
| 100 |
atomic_t err_full; // socket filled error |
| 101 |
atomic_t err_other; // other socket errors |
| 102 |
}; |
| 103 |
|
| 104 |
struct netflow_aggr_n { |
| 105 |
struct list_head list; |
| 106 |
__u32 mask; |
| 107 |
__u32 addr; |
| 108 |
__u32 aggr_mask; |
| 109 |
__u8 prefix; |
| 110 |
}; |
| 111 |
|
| 112 |
struct netflow_aggr_p { |
| 113 |
struct list_head list; |
| 114 |
__u16 port1; |
| 115 |
__u16 port2; |
| 116 |
__u16 aggr_port; |
| 117 |
}; |
| 118 |
|
| 119 |
#define NETFLOW_STAT_INC(count) (__get_cpu_var(ipt_netflow_stat).count++) |
| 120 |
#define NETFLOW_STAT_ADD(count, val) (__get_cpu_var(ipt_netflow_stat).count += (unsigned long long)val) |
| 121 |
|
| 122 |
#define NETFLOW_STAT_INC_ATOMIC(count) \ |
| 123 |
do { \ |
| 124 |
preempt_disable(); \ |
| 125 |
(__get_cpu_var(ipt_netflow_stat).count++); \ |
| 126 |
preempt_enable(); \ |
| 127 |
} while(0); |
| 128 |
|
| 129 |
#define NETFLOW_STAT_ADD_ATOMIC(count, val) \ |
| 130 |
do { \ |
| 131 |
preempt_disable(); \ |
| 132 |
(__get_cpu_var(ipt_netflow_stat).count += (unsigned long long)val); \ |
| 133 |
preempt_enable(); \ |
| 134 |
} while(0); |
| 135 |
|
| 136 |
|
| 137 |
/* statistics */ |
| 138 |
struct ipt_netflow_stat { |
| 139 |
u64 searched; // hash stat |
| 140 |
u64 found; // hash stat |
| 141 |
u64 notfound; // hash stat |
| 142 |
unsigned int truncated; // packets stat |
| 143 |
unsigned int frags; // packets stat |
| 144 |
unsigned int alloc_err; // failed to allocate flow mem |
| 145 |
unsigned int maxflows_err; // maxflows reached |
| 146 |
unsigned int send_success; // sendmsg() ok |
| 147 |
unsigned int send_failed; // sendmsg() failed |
| 148 |
unsigned int sock_errors; // socket error callback called (got icmp refused) |
| 149 |
u64 exported_size; // netflow traffic itself |
| 150 |
u64 pkt_total; // packets accounted total |
| 151 |
u64 traf_total; // traffic accounted total |
| 152 |
u64 pkt_drop; // packets not accounted total |
| 153 |
u64 traf_drop; // traffic not accounted total |
| 154 |
u64 pkt_out; // packets out of the memory |
| 155 |
u64 traf_out; // traffic out of the memory |
| 156 |
}; |
| 157 |
|
| 158 |
#ifndef list_first_entry |
| 159 |
#define list_first_entry(ptr, type, member) \ |
| 160 |
list_entry((ptr)->next, type, member) |
| 161 |
#endif |
| 162 |
|
| 163 |
#endif |
| 164 |
/* vim: set sw=8: */ |
|
|
165 |
++ b/net/ipv4/netfilter/Kconfig |
|
Lines 181-186
config IP_NF_TARGET_MASQUERADE
Link Here
|
| 181 |
|
181 |
|
| 182 |
To compile it as a module, choose M here. If unsure, say N. |
182 |
To compile it as a module, choose M here. If unsure, say N. |
| 183 |
|
183 |
|
|
|
184 |
config IP_NF_TARGET_NETFLOW |
| 185 |
tristate "NETFLOW target support" |
| 186 |
depends on NF_NAT |
| 187 |
depends on NETFILTER_ADVANCED |
| 188 |
help |
| 189 |
NETFLOW is an implementation of in-kernel netflow probe |
| 190 |
|
| 191 |
To compile it as a module, choose M here. If unsure, say N. |
| 192 |
|
| 184 |
config IP_NF_TARGET_NETMAP |
193 |
config IP_NF_TARGET_NETMAP |
| 185 |
tristate "NETMAP target support" |
194 |
tristate "NETMAP target support" |
| 186 |
depends on NF_NAT |
195 |
depends on NF_NAT |
| 187 |
-- a/net/ipv4/netfilter/Makefile |
196 |
++ b/net/ipv4/netfilter/Makefile |
|
Lines 57-62
obj-$(CONFIG_IP_NF_TARGET_CLUSTERIP) += ipt_CLUSTERIP.o
Link Here
|
| 57 |
obj-$(CONFIG_IP_NF_TARGET_ECN) += ipt_ECN.o |
57 |
obj-$(CONFIG_IP_NF_TARGET_ECN) += ipt_ECN.o |
| 58 |
obj-$(CONFIG_IP_NF_TARGET_LOG) += ipt_LOG.o |
58 |
obj-$(CONFIG_IP_NF_TARGET_LOG) += ipt_LOG.o |
| 59 |
obj-$(CONFIG_IP_NF_TARGET_MASQUERADE) += ipt_MASQUERADE.o |
59 |
obj-$(CONFIG_IP_NF_TARGET_MASQUERADE) += ipt_MASQUERADE.o |
|
|
60 |
obj-$(CONFIG_IP_NF_TARGET_NETFLOW) += ipt_NETFLOW.o |
| 60 |
obj-$(CONFIG_IP_NF_TARGET_NETMAP) += ipt_NETMAP.o |
61 |
obj-$(CONFIG_IP_NF_TARGET_NETMAP) += ipt_NETMAP.o |
| 61 |
obj-$(CONFIG_IP_NF_TARGET_REDIRECT) += ipt_REDIRECT.o |
62 |
obj-$(CONFIG_IP_NF_TARGET_REDIRECT) += ipt_REDIRECT.o |
| 62 |
obj-$(CONFIG_IP_NF_TARGET_REJECT) += ipt_REJECT.o |
63 |
obj-$(CONFIG_IP_NF_TARGET_REJECT) += ipt_REJECT.o |
| 63 |
-- /dev/null |
64 |
++ b/net/ipv4/netfilter/ipt_NETFLOW.c |
|
Line 0
Link Here
|
|
|
1 |
/* |
| 2 |
* This is NetFlow exporting module (NETFLOW target) |
| 3 |
* (c) 2008 <abc@telekom.ru> |
| 4 |
* |
| 5 |
*/ |
| 6 |
|
| 7 |
//#define RAW_PROMISC_HACK |
| 8 |
|
| 9 |
#include <linux/module.h> |
| 10 |
#include <linux/skbuff.h> |
| 11 |
#include <linux/proc_fs.h> |
| 12 |
#include <linux/vmalloc.h> |
| 13 |
#include <linux/seq_file.h> |
| 14 |
#include <linux/random.h> |
| 15 |
#include <linux/ip.h> |
| 16 |
#include <linux/udp.h> |
| 17 |
#include <linux/icmp.h> |
| 18 |
#include <linux/igmp.h> |
| 19 |
#include <linux/inetdevice.h> |
| 20 |
#include <linux/jhash.h> |
| 21 |
#include <net/icmp.h> |
| 22 |
#include <net/ip.h> |
| 23 |
#include <net/tcp.h> |
| 24 |
#include <net/route.h> |
| 25 |
#include <net/dst.h> |
| 26 |
#include <linux/netfilter/x_tables.h> |
| 27 |
#include <asm/unaligned.h> |
| 28 |
#include <linux/netfilter_ipv4/ipt_NETFLOW.h> |
| 29 |
#ifdef CONFIG_BRIDGE_NETFILTER |
| 30 |
#include <linux/netfilter_bridge.h> |
| 31 |
#endif |
| 32 |
#ifdef CONFIG_SYSCTL |
| 33 |
#include <linux/sysctl.h> |
| 34 |
#endif |
| 35 |
|
| 36 |
#ifndef HIPQUAD |
| 37 |
#if defined(__LITTLE_ENDIAN) |
| 38 |
#define HIPQUAD(addr) \ |
| 39 |
((unsigned char *)&addr)[3], \ |
| 40 |
((unsigned char *)&addr)[2], \ |
| 41 |
((unsigned char *)&addr)[1], \ |
| 42 |
((unsigned char *)&addr)[0] |
| 43 |
#elif defined(__BIG_ENDIAN) |
| 44 |
#define HIPQUAD NIPQUAD |
| 45 |
#else |
| 46 |
#error "Please fix asm/byteorder.h" |
| 47 |
#endif /* __LITTLE_ENDIAN */ |
| 48 |
#endif |
| 49 |
|
| 50 |
#define IPT_NETFLOW_VERSION "1.6" |
| 51 |
|
| 52 |
MODULE_LICENSE("GPL"); |
| 53 |
MODULE_AUTHOR("<abc@telekom.ru>"); |
| 54 |
MODULE_DESCRIPTION("iptables NETFLOW target module"); |
| 55 |
MODULE_VERSION(IPT_NETFLOW_VERSION); |
| 56 |
|
| 57 |
#define DST_SIZE 256 |
| 58 |
static char destination_buf[DST_SIZE] = "127.0.0.1:2055"; |
| 59 |
static char *destination = destination_buf; |
| 60 |
module_param(destination, charp, 0400); |
| 61 |
MODULE_PARM_DESC(destination, "export destination ipaddress:port"); |
| 62 |
|
| 63 |
static int inactive_timeout = 15; |
| 64 |
module_param(inactive_timeout, int, 0600); |
| 65 |
MODULE_PARM_DESC(inactive_timeout, "inactive flows timeout in seconds"); |
| 66 |
|
| 67 |
static int active_timeout = 30 * 60; |
| 68 |
module_param(active_timeout, int, 0600); |
| 69 |
MODULE_PARM_DESC(active_timeout, "active flows timeout in seconds"); |
| 70 |
|
| 71 |
static int debug = 0; |
| 72 |
module_param(debug, int, 0600); |
| 73 |
MODULE_PARM_DESC(debug, "debug verbosity level"); |
| 74 |
|
| 75 |
static int sndbuf; |
| 76 |
module_param(sndbuf, int, 0400); |
| 77 |
MODULE_PARM_DESC(sndbuf, "udp socket SNDBUF size"); |
| 78 |
|
| 79 |
static int hashsize; |
| 80 |
module_param(hashsize, int, 0400); |
| 81 |
MODULE_PARM_DESC(hashsize, "hash table size"); |
| 82 |
|
| 83 |
static int maxflows = 2000000; |
| 84 |
module_param(maxflows, int, 0600); |
| 85 |
MODULE_PARM_DESC(maxflows, "maximum number of flows"); |
| 86 |
static int peakflows = 0; |
| 87 |
static unsigned long peakflows_at; |
| 88 |
|
| 89 |
#define AGGR_SIZE 1024 |
| 90 |
static char aggregation_buf[AGGR_SIZE] = ""; |
| 91 |
static char *aggregation = aggregation_buf; |
| 92 |
module_param(aggregation, charp, 0400); |
| 93 |
MODULE_PARM_DESC(aggregation, "aggregation ruleset"); |
| 94 |
|
| 95 |
static DEFINE_PER_CPU(struct ipt_netflow_stat, ipt_netflow_stat); |
| 96 |
static LIST_HEAD(usock_list); |
| 97 |
static DEFINE_RWLOCK(sock_lock); |
| 98 |
|
| 99 |
static unsigned int ipt_netflow_hash_rnd; |
| 100 |
struct hlist_head *ipt_netflow_hash __read_mostly; /* hash table memory */ |
| 101 |
static unsigned int ipt_netflow_hash_size __read_mostly = 0; /* buckets */ |
| 102 |
static LIST_HEAD(ipt_netflow_list); /* all flows */ |
| 103 |
static LIST_HEAD(aggr_n_list); |
| 104 |
static LIST_HEAD(aggr_p_list); |
| 105 |
static DEFINE_RWLOCK(aggr_lock); |
| 106 |
static struct kmem_cache *ipt_netflow_cachep __read_mostly; /* ipt_netflow memory */ |
| 107 |
static atomic_t ipt_netflow_count = ATOMIC_INIT(0); |
| 108 |
static DEFINE_SPINLOCK(ipt_netflow_lock); |
| 109 |
|
| 110 |
static DEFINE_SPINLOCK(pdu_lock); |
| 111 |
static long long pdu_packets = 0, pdu_traf = 0; |
| 112 |
static struct netflow5_pdu pdu; |
| 113 |
static unsigned long pdu_ts_mod; |
| 114 |
static void netflow_work_fn(struct work_struct *work); |
| 115 |
static DECLARE_DELAYED_WORK(netflow_work, netflow_work_fn); |
| 116 |
static struct timer_list rate_timer; |
| 117 |
|
| 118 |
#define TCP_FIN_RST 0x05 |
| 119 |
|
| 120 |
static long long sec_prate = 0, sec_brate = 0; |
| 121 |
static long long min_prate = 0, min_brate = 0; |
| 122 |
static long long min5_prate = 0, min5_brate = 0; |
| 123 |
static unsigned int metric = 10, min15_metric = 10, min5_metric = 10, min_metric = 10; /* hash metrics */ |
| 124 |
|
| 125 |
static int set_hashsize(int new_size); |
| 126 |
static void destination_fini(void); |
| 127 |
static int add_destinations(char *ptr); |
| 128 |
static void aggregation_fini(struct list_head *list); |
| 129 |
static int add_aggregation(char *ptr); |
| 130 |
|
| 131 |
static inline __be32 bits2mask(int bits) { |
| 132 |
return (bits? 0xffffffff << (32 - bits) : 0); |
| 133 |
} |
| 134 |
|
| 135 |
static inline int mask2bits(__be32 mask) { |
| 136 |
int n; |
| 137 |
|
| 138 |
for (n = 0; mask; n++) |
| 139 |
mask = (mask << 1) & 0xffffffff; |
| 140 |
return n; |
| 141 |
} |
| 142 |
|
| 143 |
#ifdef CONFIG_PROC_FS |
| 144 |
/* procfs statistics /proc/net/stat/ipt_netflow */ |
| 145 |
static int nf_seq_show(struct seq_file *seq, void *v) |
| 146 |
{ |
| 147 |
unsigned int nr_flows = atomic_read(&ipt_netflow_count); |
| 148 |
int cpu; |
| 149 |
unsigned long long searched = 0, found = 0, notfound = 0; |
| 150 |
unsigned int truncated = 0, frags = 0, alloc_err = 0, maxflows_err = 0; |
| 151 |
unsigned int sock_errors = 0, send_failed = 0, send_success = 0; |
| 152 |
unsigned long long pkt_total = 0, traf_total = 0, exported_size = 0; |
| 153 |
unsigned long long pkt_drop = 0, traf_drop = 0; |
| 154 |
unsigned long long pkt_out = 0, traf_out = 0; |
| 155 |
struct ipt_netflow_sock *usock; |
| 156 |
struct netflow_aggr_n *aggr_n; |
| 157 |
struct netflow_aggr_p *aggr_p; |
| 158 |
int snum = 0; |
| 159 |
int peak = (jiffies - peakflows_at) / HZ; |
| 160 |
|
| 161 |
seq_printf(seq, "Flows: active %u (peak %u reached %ud%uh%um ago), mem %uK\n", |
| 162 |
nr_flows, |
| 163 |
peakflows, |
| 164 |
peak / (60 * 60 * 24), (peak / (60 * 60)) % 24, (peak / 60) % 60, |
| 165 |
(unsigned int)((nr_flows * sizeof(struct ipt_netflow)) >> 10)); |
| 166 |
|
| 167 |
for_each_present_cpu(cpu) { |
| 168 |
struct ipt_netflow_stat *st = &per_cpu(ipt_netflow_stat, cpu); |
| 169 |
|
| 170 |
searched += st->searched; |
| 171 |
found += st->found; |
| 172 |
notfound += st->notfound; |
| 173 |
truncated += st->truncated; |
| 174 |
frags += st->frags; |
| 175 |
alloc_err += st->alloc_err; |
| 176 |
maxflows_err += st->maxflows_err; |
| 177 |
send_success += st->send_success; |
| 178 |
send_failed += st->send_failed; |
| 179 |
sock_errors += st->sock_errors; |
| 180 |
exported_size += st->exported_size; |
| 181 |
pkt_total += st->pkt_total; |
| 182 |
traf_total += st->traf_total; |
| 183 |
pkt_drop += st->pkt_drop; |
| 184 |
traf_drop += st->traf_drop; |
| 185 |
pkt_out += st->pkt_out; |
| 186 |
traf_out += st->traf_out; |
| 187 |
} |
| 188 |
|
| 189 |
#define FFLOAT(x, prec) (int)(x) / prec, (int)(x) % prec |
| 190 |
seq_printf(seq, "Hash: size %u (mem %uK), metric %d.%d, %d.%d, %d.%d, %d.%d. MemTraf: %llu pkt, %llu K (pdu %llu, %llu).\n", |
| 191 |
ipt_netflow_hash_size, |
| 192 |
(unsigned int)((ipt_netflow_hash_size * sizeof(struct hlist_head)) >> 10), |
| 193 |
FFLOAT(metric, 10), |
| 194 |
FFLOAT(min_metric, 10), |
| 195 |
FFLOAT(min5_metric, 10), |
| 196 |
FFLOAT(min15_metric, 10), |
| 197 |
pkt_total - pkt_out + pdu_packets, |
| 198 |
(traf_total - traf_out + pdu_traf) >> 10, |
| 199 |
pdu_packets, |
| 200 |
pdu_traf); |
| 201 |
|
| 202 |
seq_printf(seq, "Timeout: active %d, inactive %d. Maxflows %u\n", |
| 203 |
active_timeout, |
| 204 |
inactive_timeout, |
| 205 |
maxflows); |
| 206 |
|
| 207 |
seq_printf(seq, "Rate: %llu bits/sec, %llu packets/sec; Avg 1 min: %llu bps, %llu pps; 5 min: %llu bps, %llu pps\n", |
| 208 |
sec_brate, sec_prate, min_brate, min_prate, min5_brate, min5_prate); |
| 209 |
|
| 210 |
seq_printf(seq, "cpu# stat: <search found new, trunc frag alloc maxflows>, sock: <ok fail cberr, bytes>, traffic: <pkt, bytes>, drop: <pkt, bytes>\n"); |
| 211 |
|
| 212 |
seq_printf(seq, "Total stat: %6llu %6llu %6llu, %4u %4u %4u %4u, sock: %6u %u %u, %llu K, traffic: %llu, %llu MB, drop: %llu, %llu K\n", |
| 213 |
(unsigned long long)searched, |
| 214 |
(unsigned long long)found, |
| 215 |
(unsigned long long)notfound, |
| 216 |
truncated, frags, alloc_err, maxflows_err, |
| 217 |
send_success, send_failed, sock_errors, |
| 218 |
(unsigned long long)exported_size >> 10, |
| 219 |
(unsigned long long)pkt_total, (unsigned long long)traf_total >> 20, |
| 220 |
(unsigned long long)pkt_drop, (unsigned long long)traf_drop >> 10); |
| 221 |
|
| 222 |
if (num_present_cpus() > 1) { |
| 223 |
for_each_present_cpu(cpu) { |
| 224 |
struct ipt_netflow_stat *st; |
| 225 |
|
| 226 |
st = &per_cpu(ipt_netflow_stat, cpu); |
| 227 |
seq_printf(seq, "cpu%u stat: %6llu %6llu %6llu, %4u %4u %4u %4u, sock: %6u %u %u, %llu K, traffic: %llu, %llu MB, drop: %llu, %llu K\n", |
| 228 |
cpu, |
| 229 |
(unsigned long long)st->searched, |
| 230 |
(unsigned long long)st->found, |
| 231 |
(unsigned long long)st->notfound, |
| 232 |
st->truncated, st->frags, st->alloc_err, st->maxflows_err, |
| 233 |
st->send_success, st->send_failed, st->sock_errors, |
| 234 |
(unsigned long long)st->exported_size >> 10, |
| 235 |
(unsigned long long)st->pkt_total, (unsigned long long)st->traf_total >> 20, |
| 236 |
(unsigned long long)st->pkt_drop, (unsigned long long)st->traf_drop >> 10); |
| 237 |
} |
| 238 |
} |
| 239 |
|
| 240 |
read_lock(&sock_lock); |
| 241 |
list_for_each_entry(usock, &usock_list, list) { |
| 242 |
struct sock *sk = usock->sock->sk; |
| 243 |
|
| 244 |
seq_printf(seq, "sock%d: %u.%u.%u.%u:%u, sndbuf %u, filled %u, peak %u; err: sndbuf reached %u, other %u\n", |
| 245 |
snum, |
| 246 |
usock->ipaddr >> 24, |
| 247 |
(usock->ipaddr >> 16) & 255, |
| 248 |
(usock->ipaddr >> 8) & 255, |
| 249 |
usock->ipaddr & 255, |
| 250 |
usock->port, |
| 251 |
sk->sk_sndbuf, |
| 252 |
atomic_read(&sk->sk_wmem_alloc), |
| 253 |
atomic_read(&usock->wmem_peak), |
| 254 |
atomic_read(&usock->err_full), |
| 255 |
atomic_read(&usock->err_other)); |
| 256 |
snum++; |
| 257 |
} |
| 258 |
read_unlock(&sock_lock); |
| 259 |
|
| 260 |
read_lock_bh(&aggr_lock); |
| 261 |
snum = 0; |
| 262 |
list_for_each_entry(aggr_n, &aggr_n_list, list) { |
| 263 |
seq_printf(seq, "aggr#%d net: match %u.%u.%u.%u/%d strip %d\n", |
| 264 |
snum, |
| 265 |
HIPQUAD(aggr_n->addr), |
| 266 |
mask2bits(aggr_n->mask), |
| 267 |
mask2bits(aggr_n->aggr_mask)); |
| 268 |
snum++; |
| 269 |
} |
| 270 |
snum = 0; |
| 271 |
list_for_each_entry(aggr_p, &aggr_p_list, list) { |
| 272 |
seq_printf(seq, "aggr#%d port: ports %u-%u replace %u\n", |
| 273 |
snum, |
| 274 |
aggr_p->port1, |
| 275 |
aggr_p->port2, |
| 276 |
aggr_p->aggr_port); |
| 277 |
snum++; |
| 278 |
} |
| 279 |
read_unlock_bh(&aggr_lock); |
| 280 |
return 0; |
| 281 |
} |
| 282 |
|
| 283 |
static int nf_seq_open(struct inode *inode, struct file *file) |
| 284 |
{ |
| 285 |
return single_open(file, nf_seq_show, NULL); |
| 286 |
} |
| 287 |
|
| 288 |
static struct file_operations nf_seq_fops = { |
| 289 |
.owner = THIS_MODULE, |
| 290 |
.open = nf_seq_open, |
| 291 |
.read = seq_read, |
| 292 |
.llseek = seq_lseek, |
| 293 |
.release = single_release, |
| 294 |
}; |
| 295 |
#endif /* CONFIG_PROC_FS */ |
| 296 |
|
| 297 |
#ifdef CONFIG_SYSCTL |
| 298 |
/* sysctl /proc/sys/net/netflow */ |
| 299 |
static int hsize_procctl(ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *fpos) |
| 300 |
{ |
| 301 |
void *orig = ctl->data; |
| 302 |
int ret, hsize; |
| 303 |
|
| 304 |
if (write) |
| 305 |
ctl->data = &hsize; |
| 306 |
ret = proc_dointvec(ctl, write, buffer, lenp, fpos); |
| 307 |
if (write) { |
| 308 |
ctl->data = orig; |
| 309 |
if (hsize < 1) |
| 310 |
return -EPERM; |
| 311 |
return set_hashsize(hsize)?:ret; |
| 312 |
} else |
| 313 |
return ret; |
| 314 |
} |
| 315 |
|
| 316 |
static int sndbuf_procctl(ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *fpos) |
| 317 |
{ |
| 318 |
int ret; |
| 319 |
struct ipt_netflow_sock *usock; |
| 320 |
|
| 321 |
read_lock(&sock_lock); |
| 322 |
if (list_empty(&usock_list)) { |
| 323 |
read_unlock(&sock_lock); |
| 324 |
return -ENOENT; |
| 325 |
} |
| 326 |
usock = list_first_entry(&usock_list, struct ipt_netflow_sock, list); |
| 327 |
sndbuf = usock->sock->sk->sk_sndbuf; |
| 328 |
read_unlock(&sock_lock); |
| 329 |
|
| 330 |
ctl->data = &sndbuf; |
| 331 |
ret = proc_dointvec(ctl, write, buffer, lenp, fpos); |
| 332 |
if (!write) |
| 333 |
return ret; |
| 334 |
if (sndbuf < SOCK_MIN_SNDBUF) |
| 335 |
sndbuf = SOCK_MIN_SNDBUF; |
| 336 |
write_lock(&sock_lock); |
| 337 |
list_for_each_entry(usock, &usock_list, list) { |
| 338 |
usock->sock->sk->sk_sndbuf = sndbuf; |
| 339 |
} |
| 340 |
write_unlock(&sock_lock); |
| 341 |
return ret; |
| 342 |
} |
| 343 |
|
| 344 |
static int destination_procctl(ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *fpos) |
| 345 |
{ |
| 346 |
int ret; |
| 347 |
|
| 348 |
ret = proc_dostring(ctl, write, buffer, lenp, fpos); |
| 349 |
if (ret >= 0 && write) { |
| 350 |
destination_fini(); |
| 351 |
add_destinations(destination_buf); |
| 352 |
} |
| 353 |
return ret; |
| 354 |
} |
| 355 |
|
| 356 |
static int aggregation_procctl(ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *fpos) |
| 357 |
{ |
| 358 |
int ret; |
| 359 |
|
| 360 |
if (debug > 1) |
| 361 |
printk(KERN_INFO "aggregation_procctl (%d) %u %llu\n", write, (unsigned int)(*lenp), *fpos); |
| 362 |
ret = proc_dostring(ctl, write, buffer, lenp, fpos); |
| 363 |
if (ret >= 0 && write) { |
| 364 |
add_aggregation(aggregation_buf); |
| 365 |
} |
| 366 |
return ret; |
| 367 |
} |
| 368 |
|
| 369 |
static struct ctl_table_header *netflow_sysctl_header; |
| 370 |
|
| 371 |
static struct ctl_table netflow_sysctl_table[] = { |
| 372 |
{ |
| 373 |
.procname = "active_timeout", |
| 374 |
.mode = 0644, |
| 375 |
.data = &active_timeout, |
| 376 |
.maxlen = sizeof(int), |
| 377 |
.proc_handler = &proc_dointvec, |
| 378 |
}, |
| 379 |
{ |
| 380 |
.procname = "inactive_timeout", |
| 381 |
.mode = 0644, |
| 382 |
.data = &inactive_timeout, |
| 383 |
.maxlen = sizeof(int), |
| 384 |
.proc_handler = &proc_dointvec, |
| 385 |
}, |
| 386 |
{ |
| 387 |
.procname = "debug", |
| 388 |
.mode = 0644, |
| 389 |
.data = &debug, |
| 390 |
.maxlen = sizeof(int), |
| 391 |
.proc_handler = &proc_dointvec, |
| 392 |
}, |
| 393 |
{ |
| 394 |
.procname = "hashsize", |
| 395 |
.mode = 0644, |
| 396 |
.data = &ipt_netflow_hash_size, |
| 397 |
.maxlen = sizeof(int), |
| 398 |
.proc_handler = &hsize_procctl, |
| 399 |
}, |
| 400 |
{ |
| 401 |
.procname = "sndbuf", |
| 402 |
.mode = 0644, |
| 403 |
.maxlen = sizeof(int), |
| 404 |
.proc_handler = &sndbuf_procctl, |
| 405 |
}, |
| 406 |
{ |
| 407 |
.procname = "destination", |
| 408 |
.mode = 0644, |
| 409 |
.data = &destination_buf, |
| 410 |
.maxlen = sizeof(destination_buf), |
| 411 |
.proc_handler = &destination_procctl, |
| 412 |
}, |
| 413 |
{ |
| 414 |
.procname = "aggregation", |
| 415 |
.mode = 0644, |
| 416 |
.data = &aggregation_buf, |
| 417 |
.maxlen = sizeof(aggregation_buf), |
| 418 |
.proc_handler = &aggregation_procctl, |
| 419 |
}, |
| 420 |
{ |
| 421 |
.procname = "maxflows", |
| 422 |
.mode = 0644, |
| 423 |
.data = &maxflows, |
| 424 |
.maxlen = sizeof(int), |
| 425 |
.proc_handler = &proc_dointvec, |
| 426 |
}, |
| 427 |
{ } |
| 428 |
}; |
| 429 |
|
| 430 |
static struct ctl_path netflow_sysctl_path[] = { |
| 431 |
{ .procname = "net" }, |
| 432 |
{ .procname = "netflow" }, |
| 433 |
{ } |
| 434 |
}; |
| 435 |
#endif /* CONFIG_SYSCTL */ |
| 436 |
|
| 437 |
/* socket code */ |
| 438 |
static void sk_error_report(struct sock *sk) |
| 439 |
{ |
| 440 |
/* clear connection refused errors if any */ |
| 441 |
write_lock_bh(&sk->sk_callback_lock); |
| 442 |
if (debug > 1) |
| 443 |
printk(KERN_INFO "NETFLOW: socket error <%d>\n", sk->sk_err); |
| 444 |
sk->sk_err = 0; |
| 445 |
NETFLOW_STAT_INC(sock_errors); |
| 446 |
write_unlock_bh(&sk->sk_callback_lock); |
| 447 |
return; |
| 448 |
} |
| 449 |
|
| 450 |
// return numbers of sends succeded, 0 if none |
| 451 |
static int netflow_send_pdu(void *buffer, int len) |
| 452 |
{ |
| 453 |
struct msghdr msg = { .msg_flags = MSG_DONTWAIT|MSG_NOSIGNAL }; |
| 454 |
struct kvec iov = { buffer, len }; |
| 455 |
int retok = 0, ret; |
| 456 |
int snum = 0; |
| 457 |
struct ipt_netflow_sock *usock; |
| 458 |
|
| 459 |
read_lock(&sock_lock); |
| 460 |
list_for_each_entry(usock, &usock_list, list) { |
| 461 |
if (debug) |
| 462 |
printk(KERN_INFO "netflow_send_pdu: sendmsg(%d, %d) [%u %u]\n", |
| 463 |
snum, |
| 464 |
len, |
| 465 |
atomic_read(&usock->sock->sk->sk_wmem_alloc), |
| 466 |
usock->sock->sk->sk_sndbuf); |
| 467 |
ret = kernel_sendmsg(usock->sock, &msg, &iov, 1, (size_t)len); |
| 468 |
if (ret < 0) { |
| 469 |
char *suggestion = ""; |
| 470 |
|
| 471 |
NETFLOW_STAT_INC_ATOMIC(send_failed); |
| 472 |
if (ret == -EAGAIN) { |
| 473 |
atomic_inc(&usock->err_full); |
| 474 |
suggestion = ": increase sndbuf!"; |
| 475 |
} else |
| 476 |
atomic_inc(&usock->err_other); |
| 477 |
printk(KERN_ERR "netflow_send_pdu[%d]: sendmsg error %d: data loss %llu pkt, %llu bytes%s\n", |
| 478 |
snum, ret, pdu_packets, pdu_traf, suggestion); |
| 479 |
} else { |
| 480 |
unsigned int wmem = atomic_read(&usock->sock->sk->sk_wmem_alloc); |
| 481 |
if (wmem > atomic_read(&usock->wmem_peak)) |
| 482 |
atomic_set(&usock->wmem_peak, wmem); |
| 483 |
NETFLOW_STAT_INC_ATOMIC(send_success); |
| 484 |
NETFLOW_STAT_ADD_ATOMIC(exported_size, ret); |
| 485 |
retok++; |
| 486 |
} |
| 487 |
snum++; |
| 488 |
} |
| 489 |
read_unlock(&sock_lock); |
| 490 |
return retok; |
| 491 |
} |
| 492 |
|
| 493 |
static void usock_free(struct ipt_netflow_sock *usock) |
| 494 |
{ |
| 495 |
printk(KERN_INFO "netflow: remove destination %u.%u.%u.%u:%u (%p)\n", |
| 496 |
HIPQUAD(usock->ipaddr), |
| 497 |
usock->port, |
| 498 |
usock->sock); |
| 499 |
if (usock->sock) |
| 500 |
sock_release(usock->sock); |
| 501 |
usock->sock = NULL; |
| 502 |
vfree(usock); |
| 503 |
} |
| 504 |
|
| 505 |
static void destination_fini(void) |
| 506 |
{ |
| 507 |
write_lock(&sock_lock); |
| 508 |
while (!list_empty(&usock_list)) { |
| 509 |
struct ipt_netflow_sock *usock; |
| 510 |
|
| 511 |
usock = list_entry(usock_list.next, struct ipt_netflow_sock, list); |
| 512 |
list_del(&usock->list); |
| 513 |
write_unlock(&sock_lock); |
| 514 |
usock_free(usock); |
| 515 |
write_lock(&sock_lock); |
| 516 |
} |
| 517 |
write_unlock(&sock_lock); |
| 518 |
} |
| 519 |
|
| 520 |
static void add_usock(struct ipt_netflow_sock *usock) |
| 521 |
{ |
| 522 |
struct ipt_netflow_sock *sk; |
| 523 |
|
| 524 |
/* don't need empty sockets */ |
| 525 |
if (!usock->sock) { |
| 526 |
usock_free(usock); |
| 527 |
return; |
| 528 |
} |
| 529 |
|
| 530 |
write_lock(&sock_lock); |
| 531 |
/* don't need duplicated sockets */ |
| 532 |
list_for_each_entry(sk, &usock_list, list) { |
| 533 |
if (sk->ipaddr == usock->ipaddr && |
| 534 |
sk->port == usock->port) { |
| 535 |
write_unlock(&sock_lock); |
| 536 |
usock_free(usock); |
| 537 |
return; |
| 538 |
} |
| 539 |
} |
| 540 |
list_add_tail(&usock->list, &usock_list); |
| 541 |
printk(KERN_INFO "netflow: added destination %u.%u.%u.%u:%u\n", |
| 542 |
HIPQUAD(usock->ipaddr), |
| 543 |
usock->port); |
| 544 |
write_unlock(&sock_lock); |
| 545 |
} |
| 546 |
|
| 547 |
static struct socket *usock_alloc(__be32 ipaddr, unsigned short port) |
| 548 |
{ |
| 549 |
struct sockaddr_in sin; |
| 550 |
struct socket *sock; |
| 551 |
int error; |
| 552 |
|
| 553 |
if ((error = sock_create_kern(PF_INET, SOCK_DGRAM, IPPROTO_UDP, &sock)) < 0) { |
| 554 |
printk(KERN_ERR "netflow: sock_create_kern error %d\n", error); |
| 555 |
return NULL; |
| 556 |
} |
| 557 |
sock->sk->sk_allocation = GFP_ATOMIC; |
| 558 |
sock->sk->sk_prot->unhash(sock->sk); /* hidden from input */ |
| 559 |
sock->sk->sk_error_report = &sk_error_report; /* clear ECONNREFUSED */ |
| 560 |
if (sndbuf) |
| 561 |
sock->sk->sk_sndbuf = sndbuf; |
| 562 |
else |
| 563 |
sndbuf = sock->sk->sk_sndbuf; |
| 564 |
memset(&sin, 0, sizeof(sin)); |
| 565 |
sin.sin_family = AF_INET; |
| 566 |
sin.sin_addr.s_addr = htonl(ipaddr); |
| 567 |
sin.sin_port = htons(port); |
| 568 |
if ((error = sock->ops->connect(sock, (struct sockaddr *)&sin, |
| 569 |
sizeof(sin), 0)) < 0) { |
| 570 |
printk(KERN_ERR "netflow: error connecting UDP socket %d\n", error); |
| 571 |
sock_release(sock); |
| 572 |
return NULL; |
| 573 |
} |
| 574 |
return sock; |
| 575 |
} |
| 576 |
|
| 577 |
#define SEPARATORS " ,;\t\n" |
| 578 |
static int add_destinations(char *ptr) |
| 579 |
{ |
| 580 |
while (ptr) { |
| 581 |
unsigned char ip[4]; |
| 582 |
unsigned short port; |
| 583 |
|
| 584 |
ptr += strspn(ptr, SEPARATORS); |
| 585 |
|
| 586 |
if (sscanf(ptr, "%hhu.%hhu.%hhu.%hhu:%hu", |
| 587 |
ip, ip + 1, ip + 2, ip + 3, &port) == 5) { |
| 588 |
struct ipt_netflow_sock *usock; |
| 589 |
|
| 590 |
if (!(usock = vmalloc(sizeof(*usock)))) { |
| 591 |
printk(KERN_ERR "netflow: can't vmalloc socket\n"); |
| 592 |
return -ENOMEM; |
| 593 |
} |
| 594 |
|
| 595 |
memset(usock, 0, sizeof(*usock)); |
| 596 |
usock->ipaddr = ntohl(*(__be32 *)ip); |
| 597 |
usock->port = port; |
| 598 |
usock->sock = usock_alloc(usock->ipaddr, port); |
| 599 |
atomic_set(&usock->wmem_peak, 0); |
| 600 |
atomic_set(&usock->err_full, 0); |
| 601 |
atomic_set(&usock->err_other, 0); |
| 602 |
add_usock(usock); |
| 603 |
} else |
| 604 |
break; |
| 605 |
|
| 606 |
ptr = strpbrk(ptr, SEPARATORS); |
| 607 |
} |
| 608 |
return 0; |
| 609 |
} |
| 610 |
|
| 611 |
static void aggregation_fini(struct list_head *list) |
| 612 |
{ |
| 613 |
write_lock_bh(&aggr_lock); |
| 614 |
while (!list_empty(list)) { |
| 615 |
struct netflow_aggr_n *aggr; /* match netflow_aggr_p too */ |
| 616 |
|
| 617 |
aggr = list_entry(list->next, struct netflow_aggr_n, list); |
| 618 |
list_del(&aggr->list); |
| 619 |
write_unlock_bh(&aggr_lock); |
| 620 |
vfree(aggr); |
| 621 |
write_lock_bh(&aggr_lock); |
| 622 |
} |
| 623 |
write_unlock_bh(&aggr_lock); |
| 624 |
} |
| 625 |
|
| 626 |
static int add_aggregation(char *ptr) |
| 627 |
{ |
| 628 |
struct netflow_aggr_n *aggr_n, *aggr, *tmp; |
| 629 |
struct netflow_aggr_p *aggr_p; |
| 630 |
LIST_HEAD(new_aggr_n_list); |
| 631 |
LIST_HEAD(new_aggr_p_list); |
| 632 |
LIST_HEAD(old_aggr_list); |
| 633 |
|
| 634 |
while (ptr && *ptr) { |
| 635 |
unsigned char ip[4]; |
| 636 |
unsigned int mask; |
| 637 |
unsigned int port1, port2; |
| 638 |
unsigned int aggr_to; |
| 639 |
|
| 640 |
ptr += strspn(ptr, SEPARATORS); |
| 641 |
|
| 642 |
if (sscanf(ptr, "%hhu.%hhu.%hhu.%hhu/%u=%u", |
| 643 |
ip, ip + 1, ip + 2, ip + 3, &mask, &aggr_to) == 6) { |
| 644 |
|
| 645 |
if (!(aggr_n = vmalloc(sizeof(*aggr_n)))) { |
| 646 |
printk(KERN_ERR "netflow: can't vmalloc aggr\n"); |
| 647 |
return -ENOMEM; |
| 648 |
} |
| 649 |
memset(aggr_n, 0, sizeof(*aggr_n)); |
| 650 |
|
| 651 |
aggr_n->addr = ntohl(*(__be32 *)ip); |
| 652 |
aggr_n->mask = bits2mask(mask); |
| 653 |
aggr_n->aggr_mask = bits2mask(aggr_to); |
| 654 |
aggr_n->prefix = mask; |
| 655 |
printk(KERN_INFO "netflow: add aggregation [%u.%u.%u.%u/%u=%u]\n", |
| 656 |
HIPQUAD(aggr_n->addr), mask, aggr_to); |
| 657 |
list_add_tail(&aggr_n->list, &new_aggr_n_list); |
| 658 |
|
| 659 |
} else if (sscanf(ptr, "%u-%u=%u", &port1, &port2, &aggr_to) == 3 || |
| 660 |
sscanf(ptr, "%u=%u", &port2, &aggr_to) == 2) { |
| 661 |
|
| 662 |
if (!(aggr_p = vmalloc(sizeof(*aggr_p)))) { |
| 663 |
printk(KERN_ERR "netflow: can't vmalloc aggr\n"); |
| 664 |
return -ENOMEM; |
| 665 |
} |
| 666 |
memset(aggr_p, 0, sizeof(*aggr_p)); |
| 667 |
|
| 668 |
aggr_p->port1 = port1; |
| 669 |
aggr_p->port2 = port2; |
| 670 |
aggr_p->aggr_port = aggr_to; |
| 671 |
printk(KERN_INFO "netflow: add aggregation [%u-%u=%u]\n", |
| 672 |
port1, port2, aggr_to); |
| 673 |
list_add_tail(&aggr_p->list, &new_aggr_p_list); |
| 674 |
} else { |
| 675 |
printk(KERN_ERR "netflow: bad aggregation rule: %s (ignoring)\n", ptr); |
| 676 |
break; |
| 677 |
} |
| 678 |
|
| 679 |
ptr = strpbrk(ptr, SEPARATORS); |
| 680 |
} |
| 681 |
|
| 682 |
/* swap lists */ |
| 683 |
write_lock_bh(&aggr_lock); |
| 684 |
list_for_each_entry_safe(aggr, tmp, &aggr_n_list, list) |
| 685 |
list_move(&aggr->list, &old_aggr_list); |
| 686 |
list_for_each_entry_safe(aggr, tmp, &aggr_p_list, list) |
| 687 |
list_move(&aggr->list, &old_aggr_list); |
| 688 |
|
| 689 |
list_for_each_entry_safe(aggr, tmp, &new_aggr_n_list, list) |
| 690 |
list_move_tail(&aggr->list, &aggr_n_list); |
| 691 |
list_for_each_entry_safe(aggr, tmp, &new_aggr_p_list, list) |
| 692 |
list_move_tail(&aggr->list, &aggr_p_list); |
| 693 |
write_unlock_bh(&aggr_lock); |
| 694 |
aggregation_fini(&old_aggr_list); |
| 695 |
return 0; |
| 696 |
} |
| 697 |
|
| 698 |
static inline u_int32_t hash_netflow(const struct ipt_netflow_tuple *tuple) |
| 699 |
{ |
| 700 |
/* tuple is rounded to u32s */ |
| 701 |
return jhash2((u32 *)tuple, NETFLOW_TUPLE_SIZE, ipt_netflow_hash_rnd) % ipt_netflow_hash_size; |
| 702 |
} |
| 703 |
|
| 704 |
static struct ipt_netflow * |
| 705 |
ipt_netflow_find(const struct ipt_netflow_tuple *tuple) |
| 706 |
{ |
| 707 |
struct ipt_netflow *nf; |
| 708 |
unsigned int hash = hash_netflow(tuple); |
| 709 |
struct hlist_node *pos; |
| 710 |
|
| 711 |
hlist_for_each_entry(nf, pos, &ipt_netflow_hash[hash], hlist) { |
| 712 |
if (ipt_netflow_tuple_equal(tuple, &nf->tuple) && |
| 713 |
nf->nr_bytes < FLOW_FULL_WATERMARK) { |
| 714 |
NETFLOW_STAT_INC(found); |
| 715 |
return nf; |
| 716 |
} |
| 717 |
NETFLOW_STAT_INC(searched); |
| 718 |
} |
| 719 |
NETFLOW_STAT_INC(notfound); |
| 720 |
return NULL; |
| 721 |
} |
| 722 |
|
| 723 |
static struct hlist_head *alloc_hashtable(int size) |
| 724 |
{ |
| 725 |
struct hlist_head *hash; |
| 726 |
|
| 727 |
hash = vmalloc(sizeof(struct hlist_head) * size); |
| 728 |
if (hash) { |
| 729 |
int i; |
| 730 |
|
| 731 |
for (i = 0; i < size; i++) |
| 732 |
INIT_HLIST_HEAD(&hash[i]); |
| 733 |
} else |
| 734 |
printk(KERN_ERR "netflow: unable to vmalloc hash table.\n"); |
| 735 |
|
| 736 |
return hash; |
| 737 |
} |
| 738 |
|
| 739 |
static int set_hashsize(int new_size) |
| 740 |
{ |
| 741 |
struct hlist_head *new_hash, *old_hash; |
| 742 |
unsigned int hash; |
| 743 |
struct ipt_netflow *nf; |
| 744 |
int rnd; |
| 745 |
|
| 746 |
printk(KERN_INFO "netflow: allocating new hash table %u -> %u buckets\n", |
| 747 |
ipt_netflow_hash_size, new_size); |
| 748 |
new_hash = alloc_hashtable(new_size); |
| 749 |
if (!new_hash) |
| 750 |
return -ENOMEM; |
| 751 |
|
| 752 |
get_random_bytes(&rnd, 4); |
| 753 |
|
| 754 |
/* rehash */ |
| 755 |
spin_lock_bh(&ipt_netflow_lock); |
| 756 |
old_hash = ipt_netflow_hash; |
| 757 |
ipt_netflow_hash = new_hash; |
| 758 |
ipt_netflow_hash_size = new_size; |
| 759 |
ipt_netflow_hash_rnd = rnd; |
| 760 |
/* hash_netflow() is dependent on ipt_netflow_hash_* values */ |
| 761 |
list_for_each_entry(nf, &ipt_netflow_list, list) { |
| 762 |
hash = hash_netflow(&nf->tuple); |
| 763 |
/* hlist_add_head overwrites hlist pointers for this node |
| 764 |
* so it's good */ |
| 765 |
hlist_add_head(&nf->hlist, &new_hash[hash]); |
| 766 |
} |
| 767 |
spin_unlock_bh(&ipt_netflow_lock); |
| 768 |
|
| 769 |
vfree(old_hash); |
| 770 |
|
| 771 |
return 0; |
| 772 |
} |
| 773 |
|
| 774 |
static struct ipt_netflow * |
| 775 |
ipt_netflow_alloc(struct ipt_netflow_tuple *tuple) |
| 776 |
{ |
| 777 |
struct ipt_netflow *nf; |
| 778 |
long count; |
| 779 |
|
| 780 |
nf = kmem_cache_alloc(ipt_netflow_cachep, GFP_ATOMIC); |
| 781 |
if (!nf) { |
| 782 |
printk(KERN_ERR "Can't allocate netflow.\n"); |
| 783 |
return NULL; |
| 784 |
} |
| 785 |
|
| 786 |
memset(nf, 0, sizeof(*nf)); |
| 787 |
nf->tuple = *tuple; |
| 788 |
|
| 789 |
count = atomic_inc_return(&ipt_netflow_count); |
| 790 |
if (count > peakflows) { |
| 791 |
peakflows = count; |
| 792 |
peakflows_at = jiffies; |
| 793 |
} |
| 794 |
|
| 795 |
return nf; |
| 796 |
} |
| 797 |
|
| 798 |
static void ipt_netflow_free(struct ipt_netflow *nf) |
| 799 |
{ |
| 800 |
atomic_dec(&ipt_netflow_count); |
| 801 |
kmem_cache_free(ipt_netflow_cachep, nf); |
| 802 |
} |
| 803 |
|
| 804 |
static struct ipt_netflow * |
| 805 |
init_netflow(struct ipt_netflow_tuple *tuple, |
| 806 |
struct sk_buff *skb) |
| 807 |
{ |
| 808 |
struct ipt_netflow *nf; |
| 809 |
unsigned int hash; |
| 810 |
|
| 811 |
nf = ipt_netflow_alloc(tuple); |
| 812 |
if (!nf) |
| 813 |
return NULL; |
| 814 |
|
| 815 |
hash = hash_netflow(&nf->tuple); |
| 816 |
hlist_add_head(&nf->hlist, &ipt_netflow_hash[hash]); |
| 817 |
list_add(&nf->list, &ipt_netflow_list); |
| 818 |
|
| 819 |
return nf; |
| 820 |
} |
| 821 |
|
| 822 |
/* cook pdu, send, and clean */ |
| 823 |
static void __netflow_export_pdu(void) |
| 824 |
{ |
| 825 |
struct timeval tv; |
| 826 |
int pdusize; |
| 827 |
|
| 828 |
if (!pdu.nr_records) |
| 829 |
return; |
| 830 |
|
| 831 |
if (debug > 1) |
| 832 |
printk(KERN_INFO "netflow_export_pdu with %d records\n", pdu.nr_records); |
| 833 |
do_gettimeofday(&tv); |
| 834 |
|
| 835 |
pdu.version = htons(5); |
| 836 |
pdu.ts_uptime = htonl(jiffies_to_msecs(jiffies)); |
| 837 |
pdu.ts_usecs = htonl(tv.tv_sec); |
| 838 |
pdu.ts_unsecs = htonl(tv.tv_usec); |
| 839 |
//pdu.eng_type = 0; |
| 840 |
//pdu.eng_id = 0; |
| 841 |
//pdu.padding = 0; |
| 842 |
|
| 843 |
pdusize = NETFLOW5_HEADER_SIZE + sizeof(struct netflow5_record) * pdu.nr_records; |
| 844 |
|
| 845 |
/* especially fix nr_records before export */ |
| 846 |
pdu.nr_records = htons(pdu.nr_records); |
| 847 |
|
| 848 |
if (netflow_send_pdu(&pdu, pdusize) == 0) { |
| 849 |
/* not least one send succeded, account stat for dropped packets */ |
| 850 |
NETFLOW_STAT_ADD_ATOMIC(pkt_drop, pdu_packets); |
| 851 |
NETFLOW_STAT_ADD_ATOMIC(traf_drop, pdu_traf); |
| 852 |
} |
| 853 |
|
| 854 |
pdu.seq = htonl(ntohl(pdu.seq) + ntohs(pdu.nr_records)); |
| 855 |
|
| 856 |
pdu.nr_records = 0; |
| 857 |
pdu_packets = 0; |
| 858 |
pdu_traf = 0; |
| 859 |
} |
| 860 |
|
| 861 |
static void netflow_export_flow(struct ipt_netflow *nf) |
| 862 |
{ |
| 863 |
struct netflow5_record *rec; |
| 864 |
|
| 865 |
spin_lock(&pdu_lock); |
| 866 |
if (debug > 2) |
| 867 |
printk(KERN_INFO "adding flow to export (%d)\n", pdu.nr_records); |
| 868 |
|
| 869 |
pdu_packets += nf->nr_packets; |
| 870 |
pdu_traf += nf->nr_bytes; |
| 871 |
pdu_ts_mod = jiffies; |
| 872 |
rec = &pdu.flow[pdu.nr_records++]; |
| 873 |
|
| 874 |
/* make V5 flow record */ |
| 875 |
rec->s_addr = nf->tuple.s_addr; |
| 876 |
rec->d_addr = nf->tuple.d_addr; |
| 877 |
//rec->nexthop = 0; |
| 878 |
rec->i_ifc = htons(nf->tuple.i_ifc); |
| 879 |
rec->o_ifc = htons(nf->o_ifc); |
| 880 |
rec->nr_packets = htonl(nf->nr_packets); |
| 881 |
rec->nr_octets = htonl(nf->nr_bytes); |
| 882 |
rec->ts_first = htonl(jiffies_to_msecs(nf->ts_first)); |
| 883 |
rec->ts_last = htonl(jiffies_to_msecs(nf->ts_last)); |
| 884 |
rec->s_port = nf->tuple.s_port; |
| 885 |
rec->d_port = nf->tuple.d_port; |
| 886 |
//rec->reserved = 0; |
| 887 |
rec->tcp_flags = nf->tcp_flags; |
| 888 |
rec->protocol = nf->tuple.protocol; |
| 889 |
rec->tos = nf->tuple.tos; |
| 890 |
//rec->s_as = 0; |
| 891 |
//rec->d_as = 0; |
| 892 |
rec->s_mask = nf->s_mask; |
| 893 |
rec->d_mask = nf->d_mask; |
| 894 |
//rec->padding = 0; |
| 895 |
ipt_netflow_free(nf); |
| 896 |
|
| 897 |
if (pdu.nr_records == NETFLOW5_RECORDS_MAX) |
| 898 |
__netflow_export_pdu(); |
| 899 |
spin_unlock(&pdu_lock); |
| 900 |
} |
| 901 |
|
| 902 |
static inline int active_needs_export(struct ipt_netflow *nf, long a_timeout) |
| 903 |
{ |
| 904 |
/* active too long, finishing, or having too much bytes */ |
| 905 |
return ((jiffies - nf->ts_first) > a_timeout) || |
| 906 |
(nf->tuple.protocol == IPPROTO_TCP && |
| 907 |
(nf->tcp_flags & TCP_FIN_RST) && |
| 908 |
(jiffies - nf->ts_last) > (1 * HZ)) || |
| 909 |
nf->nr_bytes >= FLOW_FULL_WATERMARK; |
| 910 |
} |
| 911 |
|
| 912 |
/* could be called with zero to flush cache and pdu */ |
| 913 |
static void netflow_scan_inactive_timeout(long timeout) |
| 914 |
{ |
| 915 |
long i_timeout = timeout * HZ; |
| 916 |
long a_timeout = active_timeout * HZ; |
| 917 |
|
| 918 |
spin_lock_bh(&ipt_netflow_lock); |
| 919 |
while (!list_empty(&ipt_netflow_list)) { |
| 920 |
struct ipt_netflow *nf; |
| 921 |
|
| 922 |
nf = list_entry(ipt_netflow_list.prev, struct ipt_netflow, list); |
| 923 |
/* Note: i_timeout checked with >= to allow specifying zero timeout |
| 924 |
* to purge all flows on module unload */ |
| 925 |
if (((jiffies - nf->ts_last) >= i_timeout) || |
| 926 |
active_needs_export(nf, a_timeout)) { |
| 927 |
hlist_del(&nf->hlist); |
| 928 |
list_del(&nf->list); |
| 929 |
NETFLOW_STAT_ADD(pkt_out, nf->nr_packets); |
| 930 |
NETFLOW_STAT_ADD(traf_out, nf->nr_bytes); |
| 931 |
spin_unlock_bh(&ipt_netflow_lock); |
| 932 |
netflow_export_flow(nf); |
| 933 |
spin_lock_bh(&ipt_netflow_lock); |
| 934 |
} else { |
| 935 |
/* all flows which need to be exported is always at the tail |
| 936 |
* so if no more exportable flows we can break */ |
| 937 |
break; |
| 938 |
} |
| 939 |
} |
| 940 |
spin_unlock_bh(&ipt_netflow_lock); |
| 941 |
|
| 942 |
/* flush flows stored in pdu if there no new flows for too long */ |
| 943 |
/* Note: using >= to allow flow purge on zero timeout */ |
| 944 |
if ((jiffies - pdu_ts_mod) >= i_timeout) { |
| 945 |
spin_lock(&pdu_lock); |
| 946 |
__netflow_export_pdu(); |
| 947 |
spin_unlock(&pdu_lock); |
| 948 |
} |
| 949 |
} |
| 950 |
|
| 951 |
static void netflow_work_fn(struct work_struct *dummy) |
| 952 |
{ |
| 953 |
netflow_scan_inactive_timeout(inactive_timeout); |
| 954 |
schedule_delayed_work(&netflow_work, HZ / 10); |
| 955 |
} |
| 956 |
|
| 957 |
#define RATESHIFT 2 |
| 958 |
#define SAMPLERATE (RATESHIFT*RATESHIFT) |
| 959 |
#define NUMSAMPLES(minutes) (minutes * 60 / SAMPLERATE) |
| 960 |
#define _A(v, m) (v) * (1024 * 2 / (NUMSAMPLES(m) + 1)) >> 10 |
| 961 |
// x * (1024 / y) >> 10 is because I can not just divide long long integer |
| 962 |
#define CALC_RATE(ewma, cur, minutes) ewma += _A(cur - ewma, minutes) |
| 963 |
// calculate EWMA throughput rate for whole module |
| 964 |
static void rate_timer_calc(unsigned long dummy) |
| 965 |
{ |
| 966 |
static u64 old_pkt_total = 0; |
| 967 |
static u64 old_traf_total = 0; |
| 968 |
static u64 old_searched = 0; |
| 969 |
static u64 old_found = 0; |
| 970 |
static u64 old_notfound = 0; |
| 971 |
u64 searched = 0; |
| 972 |
u64 found = 0; |
| 973 |
u64 notfound = 0; |
| 974 |
unsigned int dsrch, dfnd, dnfnd; |
| 975 |
u64 pkt_total = 0; |
| 976 |
u64 traf_total = 0; |
| 977 |
int cpu; |
| 978 |
|
| 979 |
for_each_present_cpu(cpu) { |
| 980 |
struct ipt_netflow_stat *st = &per_cpu(ipt_netflow_stat, cpu); |
| 981 |
|
| 982 |
pkt_total += st->pkt_total; |
| 983 |
traf_total += st->traf_total; |
| 984 |
searched += st->searched; |
| 985 |
found += st->found; |
| 986 |
notfound += st->notfound; |
| 987 |
} |
| 988 |
|
| 989 |
sec_prate = (pkt_total - old_pkt_total) >> RATESHIFT; |
| 990 |
CALC_RATE(min5_prate, sec_prate, 5); |
| 991 |
CALC_RATE(min_prate, sec_prate, 1); |
| 992 |
old_pkt_total = pkt_total; |
| 993 |
|
| 994 |
sec_brate = ((traf_total - old_traf_total) * 8) >> RATESHIFT; |
| 995 |
CALC_RATE(min5_brate, sec_brate, 5); |
| 996 |
CALC_RATE(min_brate, sec_brate, 1); |
| 997 |
old_traf_total = traf_total; |
| 998 |
|
| 999 |
dsrch = searched - old_searched; |
| 1000 |
dfnd = found - old_found; |
| 1001 |
dnfnd = notfound - old_notfound; |
| 1002 |
old_searched = searched; |
| 1003 |
old_found = found; |
| 1004 |
old_notfound = notfound; |
| 1005 |
/* if there is no access to hash keep rate steady */ |
| 1006 |
metric = (dfnd + dnfnd)? 10 * (dsrch + dfnd + dnfnd) / (dfnd + dnfnd) : metric; |
| 1007 |
CALC_RATE(min15_metric, (unsigned long long)metric, 15); |
| 1008 |
CALC_RATE(min5_metric, (unsigned long long)metric, 5); |
| 1009 |
CALC_RATE(min_metric, (unsigned long long)metric, 1); |
| 1010 |
|
| 1011 |
mod_timer(&rate_timer, jiffies + (HZ * SAMPLERATE)); |
| 1012 |
} |
| 1013 |
|
| 1014 |
/* packet receiver */ |
| 1015 |
static unsigned int netflow_target( |
| 1016 |
struct sk_buff *skb, |
| 1017 |
const struct xt_action_param *par |
| 1018 |
) |
| 1019 |
{ |
| 1020 |
struct iphdr _iph, *iph; |
| 1021 |
struct ipt_netflow_tuple tuple; |
| 1022 |
struct ipt_netflow *nf; |
| 1023 |
__u8 tcp_flags; |
| 1024 |
struct netflow_aggr_n *aggr_n; |
| 1025 |
struct netflow_aggr_p *aggr_p; |
| 1026 |
__u8 s_mask, d_mask; |
| 1027 |
|
| 1028 |
iph = skb_header_pointer(skb, 0, sizeof(_iph), &_iph); //iph = ip_hdr(skb); |
| 1029 |
|
| 1030 |
if (iph == NULL) { |
| 1031 |
NETFLOW_STAT_INC(truncated); |
| 1032 |
NETFLOW_STAT_INC(pkt_drop); |
| 1033 |
return XT_CONTINUE; |
| 1034 |
} |
| 1035 |
|
| 1036 |
tuple.s_addr = iph->saddr; |
| 1037 |
tuple.d_addr = iph->daddr; |
| 1038 |
tuple.s_port = 0; |
| 1039 |
tuple.d_port = 0; |
| 1040 |
tuple.i_ifc = par->in? par->in->ifindex : -1; |
| 1041 |
tuple.protocol = iph->protocol; |
| 1042 |
tuple.tos = iph->tos; |
| 1043 |
tcp_flags = 0; /* Cisco sometimes have TCP ACK for non TCP packets, don't get it */ |
| 1044 |
s_mask = 0; |
| 1045 |
d_mask = 0; |
| 1046 |
|
| 1047 |
if (iph->frag_off & htons(IP_OFFSET)) |
| 1048 |
NETFLOW_STAT_INC(frags); |
| 1049 |
else { |
| 1050 |
switch (tuple.protocol) { |
| 1051 |
case IPPROTO_TCP: { |
| 1052 |
struct tcphdr _hdr, *hp; |
| 1053 |
|
| 1054 |
if ((hp = skb_header_pointer(skb, iph->ihl * 4, 14, &_hdr))) { |
| 1055 |
tuple.s_port = hp->source; |
| 1056 |
tuple.d_port = hp->dest; |
| 1057 |
tcp_flags = (u_int8_t)(ntohl(tcp_flag_word(hp)) >> 16); |
| 1058 |
} |
| 1059 |
break; |
| 1060 |
} |
| 1061 |
case IPPROTO_UDP: { |
| 1062 |
struct udphdr _hdr, *hp; |
| 1063 |
|
| 1064 |
if ((hp = skb_header_pointer(skb, iph->ihl * 4, 4, &_hdr))) { |
| 1065 |
tuple.s_port = hp->source; |
| 1066 |
tuple.d_port = hp->dest; |
| 1067 |
} |
| 1068 |
break; |
| 1069 |
} |
| 1070 |
case IPPROTO_ICMP: { |
| 1071 |
struct icmphdr _hdr, *hp; |
| 1072 |
|
| 1073 |
if ((hp = skb_header_pointer(skb, iph->ihl * 4, 2, &_hdr))) |
| 1074 |
tuple.d_port = (hp->type << 8) | hp->code; |
| 1075 |
break; |
| 1076 |
} |
| 1077 |
case IPPROTO_IGMP: { |
| 1078 |
struct igmphdr *_hdr, *hp; |
| 1079 |
|
| 1080 |
if ((hp = skb_header_pointer(skb, iph->ihl * 4, 1, &_hdr))) |
| 1081 |
tuple.d_port = hp->type; |
| 1082 |
} |
| 1083 |
break; |
| 1084 |
} |
| 1085 |
} /* not fragmented */ |
| 1086 |
|
| 1087 |
/* aggregate networks */ |
| 1088 |
read_lock_bh(&aggr_lock); |
| 1089 |
list_for_each_entry(aggr_n, &aggr_n_list, list) |
| 1090 |
if ((ntohl(tuple.s_addr) & aggr_n->mask) == aggr_n->addr) { |
| 1091 |
tuple.s_addr &= htonl(aggr_n->aggr_mask); |
| 1092 |
s_mask = aggr_n->prefix; |
| 1093 |
break; |
| 1094 |
} |
| 1095 |
list_for_each_entry(aggr_n, &aggr_n_list, list) |
| 1096 |
if ((ntohl(tuple.d_addr) & aggr_n->mask) == aggr_n->addr) { |
| 1097 |
tuple.d_addr &= htonl(aggr_n->aggr_mask); |
| 1098 |
d_mask = aggr_n->prefix; |
| 1099 |
break; |
| 1100 |
} |
| 1101 |
|
| 1102 |
/* aggregate ports */ |
| 1103 |
list_for_each_entry(aggr_p, &aggr_p_list, list) |
| 1104 |
if (ntohs(tuple.s_port) >= aggr_p->port1 && |
| 1105 |
ntohs(tuple.s_port) <= aggr_p->port2) { |
| 1106 |
tuple.s_port = htons(aggr_p->aggr_port); |
| 1107 |
break; |
| 1108 |
} |
| 1109 |
|
| 1110 |
list_for_each_entry(aggr_p, &aggr_p_list, list) |
| 1111 |
if (ntohs(tuple.d_port) >= aggr_p->port1 && |
| 1112 |
ntohs(tuple.d_port) <= aggr_p->port2) { |
| 1113 |
tuple.d_port = htons(aggr_p->aggr_port); |
| 1114 |
break; |
| 1115 |
} |
| 1116 |
read_unlock_bh(&aggr_lock); |
| 1117 |
|
| 1118 |
spin_lock_bh(&ipt_netflow_lock); |
| 1119 |
/* record */ |
| 1120 |
nf = ipt_netflow_find(&tuple); |
| 1121 |
if (!nf) { |
| 1122 |
if (maxflows > 0 && atomic_read(&ipt_netflow_count) >= maxflows) { |
| 1123 |
/* This is DOS attack prevention */ |
| 1124 |
NETFLOW_STAT_INC(maxflows_err); |
| 1125 |
NETFLOW_STAT_INC(pkt_drop); |
| 1126 |
NETFLOW_STAT_ADD(traf_drop, ntohs(iph->tot_len)); |
| 1127 |
spin_unlock_bh(&ipt_netflow_lock); |
| 1128 |
return XT_CONTINUE; |
| 1129 |
} |
| 1130 |
|
| 1131 |
nf = init_netflow(&tuple, skb); |
| 1132 |
if (!nf || IS_ERR(nf)) { |
| 1133 |
NETFLOW_STAT_INC(alloc_err); |
| 1134 |
NETFLOW_STAT_INC(pkt_drop); |
| 1135 |
NETFLOW_STAT_ADD(traf_drop, ntohs(iph->tot_len)); |
| 1136 |
spin_unlock_bh(&ipt_netflow_lock); |
| 1137 |
return XT_CONTINUE; |
| 1138 |
} |
| 1139 |
|
| 1140 |
nf->ts_first = jiffies; |
| 1141 |
nf->tcp_flags = tcp_flags; |
| 1142 |
nf->o_ifc = par->out? par->out->ifindex : -1; |
| 1143 |
nf->s_mask = s_mask; |
| 1144 |
nf->d_mask = d_mask; |
| 1145 |
|
| 1146 |
if (debug > 2) |
| 1147 |
printk(KERN_INFO "ipt_netflow: new (%u) %hd:%hd SRC=%u.%u.%u.%u:%u DST=%u.%u.%u.%u:%u\n", |
| 1148 |
atomic_read(&ipt_netflow_count), |
| 1149 |
tuple.i_ifc, nf->o_ifc, |
| 1150 |
NIPQUAD(tuple.s_addr), ntohs(tuple.s_port), |
| 1151 |
NIPQUAD(tuple.d_addr), ntohs(tuple.d_port)); |
| 1152 |
} else { |
| 1153 |
/* ipt_netflow_list is sorted by access time: |
| 1154 |
* most recently accessed flows are at head, old flows remain at tail |
| 1155 |
* this function bubble up flow to the head */ |
| 1156 |
list_move(&nf->list, &ipt_netflow_list); |
| 1157 |
} |
| 1158 |
|
| 1159 |
nf->nr_packets++; |
| 1160 |
nf->nr_bytes += ntohs(iph->tot_len); |
| 1161 |
nf->ts_last = jiffies; |
| 1162 |
nf->tcp_flags |= tcp_flags; |
| 1163 |
|
| 1164 |
NETFLOW_STAT_INC(pkt_total); |
| 1165 |
NETFLOW_STAT_ADD(traf_total, ntohs(iph->tot_len)); |
| 1166 |
|
| 1167 |
if (active_needs_export(nf, active_timeout * HZ)) { |
| 1168 |
/* ok, if this active flow to be exported |
| 1169 |
* bubble it to the tail */ |
| 1170 |
list_move_tail(&nf->list, &ipt_netflow_list); |
| 1171 |
|
| 1172 |
/* Blog: I thought about forcing timer to wake up sooner if we have |
| 1173 |
* enough exportable flows, but in fact this doesn't have much sense, |
| 1174 |
* becasue this would only move flow data from one memory to another |
| 1175 |
* (from our buffers to socket buffers, and socket buffers even have |
| 1176 |
* limited size). But yes, this is disputable. */ |
| 1177 |
} |
| 1178 |
|
| 1179 |
spin_unlock_bh(&ipt_netflow_lock); |
| 1180 |
|
| 1181 |
return XT_CONTINUE; |
| 1182 |
} |
| 1183 |
|
| 1184 |
static struct xt_target ipt_netflow_reg __read_mostly = { |
| 1185 |
.name = "NETFLOW", |
| 1186 |
.family = NFPROTO_IPV4, |
| 1187 |
.target = netflow_target, |
| 1188 |
.table = "filter", |
| 1189 |
.hooks = (1 << NF_INET_LOCAL_IN) | (1 << NF_INET_FORWARD) | |
| 1190 |
(1 << NF_INET_LOCAL_OUT), |
| 1191 |
.me = THIS_MODULE |
| 1192 |
}; |
| 1193 |
|
| 1194 |
static int __init ipt_netflow_init(void) |
| 1195 |
{ |
| 1196 |
#ifdef CONFIG_PROC_FS |
| 1197 |
struct proc_dir_entry *proc_stat; |
| 1198 |
#endif |
| 1199 |
|
| 1200 |
get_random_bytes(&ipt_netflow_hash_rnd, 4); |
| 1201 |
|
| 1202 |
/* determine hash size (idea from nf_conntrack_core.c) */ |
| 1203 |
if (!hashsize) { |
| 1204 |
hashsize = (((num_physpages << PAGE_SHIFT) / 16384) |
| 1205 |
/ sizeof(struct hlist_head)); |
| 1206 |
if (num_physpages > (1024 * 1024 * 1024 / PAGE_SIZE)) |
| 1207 |
hashsize = 8192; |
| 1208 |
} |
| 1209 |
if (hashsize < 16) |
| 1210 |
hashsize = 16; |
| 1211 |
printk(KERN_INFO "ipt_netflow version %s (%u buckets)\n", |
| 1212 |
IPT_NETFLOW_VERSION, hashsize); |
| 1213 |
|
| 1214 |
ipt_netflow_hash_size = hashsize; |
| 1215 |
ipt_netflow_hash = alloc_hashtable(ipt_netflow_hash_size); |
| 1216 |
if (!ipt_netflow_hash) { |
| 1217 |
printk(KERN_ERR "Unable to create ipt_neflow_hash\n"); |
| 1218 |
goto err; |
| 1219 |
} |
| 1220 |
|
| 1221 |
ipt_netflow_cachep = kmem_cache_create("ipt_netflow", |
| 1222 |
sizeof(struct ipt_netflow), 0, |
| 1223 |
0, NULL |
| 1224 |
); |
| 1225 |
if (!ipt_netflow_cachep) { |
| 1226 |
printk(KERN_ERR "Unable to create ipt_netflow slab cache\n"); |
| 1227 |
goto err_free_hash; |
| 1228 |
} |
| 1229 |
|
| 1230 |
#ifdef CONFIG_PROC_FS |
| 1231 |
proc_stat = create_proc_entry("ipt_netflow", S_IRUGO, init_net.proc_net_stat); |
| 1232 |
if (!proc_stat) { |
| 1233 |
printk(KERN_ERR "Unable to create /proc/net/stat/ipt_netflow entry\n"); |
| 1234 |
goto err_free_netflow_slab; |
| 1235 |
} |
| 1236 |
proc_stat->proc_fops = &nf_seq_fops; |
| 1237 |
printk(KERN_INFO "netflow: registered: /proc/net/stat/ipt_netflow\n"); |
| 1238 |
#endif |
| 1239 |
|
| 1240 |
#ifdef CONFIG_SYSCTL |
| 1241 |
netflow_sysctl_header = register_sysctl_paths(netflow_sysctl_path, netflow_sysctl_table); |
| 1242 |
if (!netflow_sysctl_header) { |
| 1243 |
printk(KERN_ERR "netflow: can't register to sysctl\n"); |
| 1244 |
goto err_free_proc_stat; |
| 1245 |
} else |
| 1246 |
printk(KERN_INFO "netflow: registered: sysctl net.netflow\n"); |
| 1247 |
#endif |
| 1248 |
|
| 1249 |
if (!destination) |
| 1250 |
destination = aggregation_buf; |
| 1251 |
if (destination != destination_buf) { |
| 1252 |
strlcpy(destination_buf, destination, sizeof(destination_buf)); |
| 1253 |
destination = destination_buf; |
| 1254 |
} |
| 1255 |
if (add_destinations(destination) < 0) |
| 1256 |
goto err_free_sysctl; |
| 1257 |
|
| 1258 |
if (!aggregation) |
| 1259 |
aggregation = aggregation_buf; |
| 1260 |
if (aggregation != aggregation_buf) { |
| 1261 |
strlcpy(aggregation_buf, aggregation, sizeof(aggregation_buf)); |
| 1262 |
aggregation = aggregation_buf; |
| 1263 |
} |
| 1264 |
add_aggregation(aggregation); |
| 1265 |
|
| 1266 |
schedule_delayed_work(&netflow_work, HZ / 10); |
| 1267 |
setup_timer(&rate_timer, rate_timer_calc, 0); |
| 1268 |
mod_timer(&rate_timer, jiffies + (HZ * SAMPLERATE)); |
| 1269 |
|
| 1270 |
if (xt_register_target(&ipt_netflow_reg)) |
| 1271 |
goto err_stop_timer; |
| 1272 |
|
| 1273 |
peakflows_at = jiffies; |
| 1274 |
|
| 1275 |
printk(KERN_INFO "ipt_netflow loaded.\n"); |
| 1276 |
return 0; |
| 1277 |
|
| 1278 |
err_stop_timer: |
| 1279 |
cancel_delayed_work(&netflow_work); |
| 1280 |
flush_scheduled_work(); |
| 1281 |
del_timer_sync(&rate_timer); |
| 1282 |
destination_fini(); |
| 1283 |
|
| 1284 |
aggregation_fini(&aggr_n_list); |
| 1285 |
aggregation_fini(&aggr_p_list); |
| 1286 |
destination_fini(); |
| 1287 |
err_free_sysctl: |
| 1288 |
#ifdef CONFIG_SYSCTL |
| 1289 |
unregister_sysctl_table(netflow_sysctl_header); |
| 1290 |
err_free_proc_stat: |
| 1291 |
#endif |
| 1292 |
#ifdef CONFIG_PROC_FS |
| 1293 |
remove_proc_entry("ipt_netflow", init_net.proc_net_stat); |
| 1294 |
err_free_netflow_slab: |
| 1295 |
#endif |
| 1296 |
kmem_cache_destroy(ipt_netflow_cachep); |
| 1297 |
err_free_hash: |
| 1298 |
vfree(ipt_netflow_hash); |
| 1299 |
err: |
| 1300 |
return -ENOMEM; |
| 1301 |
} |
| 1302 |
|
| 1303 |
static void __exit ipt_netflow_fini(void) |
| 1304 |
{ |
| 1305 |
printk(KERN_INFO "ipt_netflow unloading..\n"); |
| 1306 |
|
| 1307 |
xt_unregister_target(&ipt_netflow_reg); |
| 1308 |
cancel_delayed_work(&netflow_work); |
| 1309 |
flush_scheduled_work(); |
| 1310 |
del_timer_sync(&rate_timer); |
| 1311 |
|
| 1312 |
synchronize_sched(); |
| 1313 |
|
| 1314 |
#ifdef CONFIG_SYSCTL |
| 1315 |
unregister_sysctl_table(netflow_sysctl_header); |
| 1316 |
#endif |
| 1317 |
#ifdef CONFIG_PROC_FS |
| 1318 |
remove_proc_entry("ipt_netflow", init_net.proc_net_stat); |
| 1319 |
#endif |
| 1320 |
|
| 1321 |
netflow_scan_inactive_timeout(0); /* flush cache and pdu */ |
| 1322 |
destination_fini(); |
| 1323 |
aggregation_fini(&aggr_n_list); |
| 1324 |
aggregation_fini(&aggr_p_list); |
| 1325 |
|
| 1326 |
kmem_cache_destroy(ipt_netflow_cachep); |
| 1327 |
vfree(ipt_netflow_hash); |
| 1328 |
|
| 1329 |
printk(KERN_INFO "ipt_netflow unloaded.\n"); |
| 1330 |
} |
| 1331 |
|
| 1332 |
module_init(ipt_netflow_init); |
| 1333 |
module_exit(ipt_netflow_fini); |
| 1334 |
|
| 1335 |
/* vim: set sw=8: */ |