1 include/linux/netfilter_ipv4/ipt_psd.h | 40 +++
2 net/ipv4/netfilter/Kconfig | 10
3 net/ipv4/netfilter/Makefile | 1
4 net/ipv4/netfilter/ipt_psd.c | 358 +++++++++++++++++++++++++++++++++
5 4 files changed, 409 insertions(+)
7 diff -Nur --exclude '*.orig' linux.org/include/linux/netfilter_ipv4/ipt_psd.h linux/include/linux/netfilter_ipv4/ipt_psd.h
8 --- linux.org/include/linux/netfilter_ipv4/ipt_psd.h 1970-01-01 01:00:00.000000000 +0100
9 +++ linux/include/linux/netfilter_ipv4/ipt_psd.h 2006-05-04 10:21:57.000000000 +0200
14 +#include <linux/param.h>
15 +#include <linux/types.h>
18 + * High port numbers have a lower weight to reduce the frequency of false
19 + * positives, such as from passive mode FTP transfers.
21 +#define PORT_WEIGHT_PRIV 3
22 +#define PORT_WEIGHT_HIGH 1
25 + * Port scan detection thresholds: at least COUNT ports need to be scanned
26 + * from the same source, with no longer than DELAY ticks between ports.
28 +#define SCAN_MIN_COUNT 7
29 +#define SCAN_MAX_COUNT (SCAN_MIN_COUNT * PORT_WEIGHT_PRIV)
30 +#define SCAN_WEIGHT_THRESHOLD SCAN_MAX_COUNT
31 +#define SCAN_DELAY_THRESHOLD (300) /* old usage of HZ here was erroneously and broke under uml */
34 + * Keep track of up to LIST_SIZE source addresses, using a hash table of
35 + * HASH_SIZE entries for faster lookups, but limiting hash collisions to
36 + * HASH_MAX source addresses per the same hash value.
38 +#define LIST_SIZE 0x100
40 +#define HASH_SIZE (1 << HASH_LOG)
41 +#define HASH_MAX 0x10
43 +struct ipt_psd_info {
44 + unsigned int weight_threshold;
45 + unsigned int delay_threshold;
46 + unsigned short lo_ports_weight;
47 + unsigned short hi_ports_weight;
50 +#endif /*_IPT_PSD_H*/
51 diff -Nur --exclude '*.orig' linux.org/net/ipv4/netfilter/Kconfig linux/net/ipv4/netfilter/Kconfig
52 --- linux.org/net/ipv4/netfilter/Kconfig 2006-05-02 23:38:44.000000000 +0200
53 +++ linux/net/ipv4/netfilter/Kconfig 2006-05-04 10:21:57.000000000 +0200
55 Allows altering the ARP packet payload: source and destination
56 hardware and network addresses.
58 +config IP_NF_MATCH_PSD
59 + tristate 'psd match support'
60 + depends on IP_NF_IPTABLES
62 + This option adds a `psd' match, which allows you to create rules in
63 + any iptables table wich will detect TCP and UDP port scans.
65 + If you want to compile it as a module, say M here and read
66 + Documentation/modules.txt. If unsure, say `N'.
70 diff -Nur --exclude '*.orig' linux.org/net/ipv4/netfilter/Makefile linux/net/ipv4/netfilter/Makefile
71 --- linux.org/net/ipv4/netfilter/Makefile 2006-05-02 23:38:44.000000000 +0200
72 +++ linux/net/ipv4/netfilter/Makefile 2006-05-04 10:21:57.000000000 +0200
74 +obj-$(CONFIG_IP_NF_MATCH_PSD) += ipt_psd.o
75 diff -Nur --exclude '*.orig' linux.org/net/ipv4/netfilter/ipt_psd.c linux/net/ipv4/netfilter/ipt_psd.c
76 --- linux.org/net/ipv4/netfilter/ipt_psd.c 1970-01-01 01:00:00.000000000 +0100
77 +++ linux/net/ipv4/netfilter/ipt_psd.c 2006-05-04 10:21:57.000000000 +0200
80 + This is a module which is used for PSD (portscan detection)
81 + Derived from scanlogd v2.1 written by Solar Designer <solar@false.com>
82 + and LOG target module.
84 + Copyright (C) 2000,2001 astaro AG
86 + This file is distributed under the terms of the GNU General Public
87 + License (GPL). Copies of the GPL can be obtained from:
88 + ftp://prep.ai.mit.edu/pub/gnu/GPL
90 + 2000-05-04 Markus Hennig <hennig@astaro.de> : initial
91 + 2000-08-18 Dennis Koslowski <koslowski@astaro.de> : first release
92 + 2000-12-01 Dennis Koslowski <koslowski@astaro.de> : UDP scans detection added
93 + 2001-01-02 Dennis Koslowski <koslowski@astaro.de> : output modified
94 + 2001-02-04 Jan Rekorajski <baggins@pld.org.pl> : converted from target to match
95 + 2004-05-05 Martijn Lievaart <m@rtij.nl> : ported to 2.6
98 +#include <linux/module.h>
99 +#include <linux/skbuff.h>
100 +#include <linux/ip.h>
101 +#include <net/tcp.h>
102 +#include <linux/spinlock.h>
103 +#include <linux/netfilter_ipv4/ip_tables.h>
104 +#include <linux/netfilter_ipv4/ipt_psd.h>
107 +#define DEBUGP printk
109 +#define DEBUGP(format, args...)
112 +MODULE_LICENSE("GPL");
113 +MODULE_AUTHOR("Dennis Koslowski <koslowski@astaro.com>");
115 +#define HF_DADDR_CHANGING 0x01
116 +#define HF_SPORT_CHANGING 0x02
117 +#define HF_TOS_CHANGING 0x04
118 +#define HF_TTL_CHANGING 0x08
121 + * Information we keep per each target port
124 + u_int16_t number; /* port number */
125 + u_int8_t proto; /* protocol number */
126 + u_int8_t and_flags; /* tcp ANDed flags */
127 + u_int8_t or_flags; /* tcp ORed flags */
131 + * Information we keep per each source address.
134 + struct host *next; /* Next entry with the same hash */
135 + clock_t timestamp; /* Last update time */
136 + struct in_addr src_addr; /* Source address */
137 + struct in_addr dest_addr; /* Destination address */
138 + unsigned short src_port; /* Source port */
139 + int count; /* Number of ports in the list */
140 + int weight; /* Total weight of ports in the list */
141 + struct port ports[SCAN_MAX_COUNT - 1]; /* List of ports */
142 + unsigned char tos; /* TOS */
143 + unsigned char ttl; /* TTL */
144 + unsigned char flags; /* HF_ flags bitmask */
148 + * State information.
152 + struct host list[LIST_SIZE]; /* List of source addresses */
153 + struct host *hash[HASH_SIZE]; /* Hash: pointers into the list */
154 + int index; /* Oldest entry to be replaced */
158 + * Convert an IP address into a hash table index.
160 +static inline int hashfunc(struct in_addr addr)
162 + unsigned int value;
165 + value = addr.s_addr;
169 + } while ((value >>= HASH_LOG));
171 + return hash & (HASH_SIZE - 1);
175 +ipt_psd_match(const struct sk_buff *pskb,
176 + const struct net_device *in,
177 + const struct net_device *out,
178 + const void *matchinfo,
180 + unsigned int protoff,
183 + struct iphdr *ip_hdr;
184 + struct tcphdr *tcp_hdr;
185 + struct in_addr addr;
186 + u_int16_t src_port,dest_port;
187 + u_int8_t tcp_flags, proto;
189 + struct host *curr, *last, **head;
190 + int hash, index, count;
192 + /* Parameters from userspace */
193 + const struct ipt_psd_info *psdinfo = matchinfo;
196 + ip_hdr = pskb->nh.iph;
199 + if (ntohs(ip_hdr->frag_off) & IP_OFFSET) {
200 + DEBUGP("PSD: sanity check failed\n");
205 + proto = ip_hdr->protocol;
207 + if (proto != IPPROTO_TCP && proto != IPPROTO_UDP) {
208 + DEBUGP("PSD: protocol not supported\n");
212 + /* Get the source address, source & destination ports, and TCP flags */
214 + addr.s_addr = ip_hdr->saddr;
216 + tcp_hdr = (struct tcphdr*)((u_int32_t *)ip_hdr + ip_hdr->ihl);
218 + /* Yep, it´s dirty */
219 + src_port = tcp_hdr->source;
220 + dest_port = tcp_hdr->dest;
222 + if (proto == IPPROTO_TCP) {
223 + tcp_flags = *((u_int8_t*)tcp_hdr + 13);
229 + /* We're using IP address 0.0.0.0 for a special purpose here, so don't let
230 + * them spoof us. [DHCP needs this feature - HW] */
231 + if (!addr.s_addr) {
232 + DEBUGP("PSD: spoofed source address (0.0.0.0)\n");
236 + /* Use jiffies here not to depend on someone setting the time while we're
237 + * running; we need to be careful with possible return value overflows. */
240 + spin_lock(&state.lock);
242 + /* Do we know this source address already? */
245 + if ((curr = *(head = &state.hash[hash = hashfunc(addr)])))
247 + if (curr->src_addr.s_addr == addr.s_addr) break;
249 + if (curr->next) last = curr;
250 + } while ((curr = curr->next));
254 + /* We know this address, and the entry isn't too old. Update it. */
255 + if (now - curr->timestamp <= (psdinfo->delay_threshold*HZ)/100 &&
256 + time_after_eq(now, curr->timestamp)) {
258 + /* Just update the appropriate list entry if we've seen this port already */
259 + for (index = 0; index < curr->count; index++) {
260 + if (curr->ports[index].number == dest_port) {
261 + curr->ports[index].proto = proto;
262 + curr->ports[index].and_flags &= tcp_flags;
263 + curr->ports[index].or_flags |= tcp_flags;
268 + /* TCP/ACK and/or TCP/RST to a new port? This could be an outgoing connection. */
269 + if (proto == IPPROTO_TCP && (tcp_hdr->ack || tcp_hdr->rst))
272 + /* Packet to a new port, and not TCP/ACK: update the timestamp */
273 + curr->timestamp = now;
275 + /* Logged this scan already? Then drop the packet. */
276 + if (curr->weight >= psdinfo->weight_threshold)
279 + /* Specify if destination address, source port, TOS or TTL are not fixed */
280 + if (curr->dest_addr.s_addr != ip_hdr->daddr)
281 + curr->flags |= HF_DADDR_CHANGING;
282 + if (curr->src_port != src_port)
283 + curr->flags |= HF_SPORT_CHANGING;
284 + if (curr->tos != ip_hdr->tos)
285 + curr->flags |= HF_TOS_CHANGING;
286 + if (curr->ttl != ip_hdr->ttl)
287 + curr->flags |= HF_TTL_CHANGING;
289 + /* Update the total weight */
290 + curr->weight += (ntohs(dest_port) < 1024) ?
291 + psdinfo->lo_ports_weight : psdinfo->hi_ports_weight;
293 + /* Got enough destination ports to decide that this is a scan? */
294 + /* Then log it and drop the packet. */
295 + if (curr->weight >= psdinfo->weight_threshold)
298 + /* Remember the new port */
299 + if (curr->count < SCAN_MAX_COUNT) {
300 + curr->ports[curr->count].number = dest_port;
301 + curr->ports[curr->count].proto = proto;
302 + curr->ports[curr->count].and_flags = tcp_flags;
303 + curr->ports[curr->count].or_flags = tcp_flags;
310 + /* We know this address, but the entry is outdated. Mark it unused, and
311 + * remove from the hash table. We'll allocate a new entry instead since
312 + * this one might get re-used too soon. */
313 + curr->src_addr.s_addr = 0;
315 + last->next = last->next->next;
317 + *head = (*head)->next;
321 + /* We don't need an ACK from a new source address */
322 + if (proto == IPPROTO_TCP && tcp_hdr->ack)
325 + /* Got too many source addresses with the same hash value? Then remove the
326 + * oldest one from the hash table, so that they can't take too much of our
327 + * CPU time even with carefully chosen spoofed IP addresses. */
328 + if (count >= HASH_MAX && last) last->next = NULL;
330 + /* We're going to re-use the oldest list entry, so remove it from the hash
331 + * table first (if it is really already in use, and isn't removed from the
332 + * hash table already because of the HASH_MAX check above). */
334 + /* First, find it */
335 + if (state.list[state.index].src_addr.s_addr)
336 + head = &state.hash[hashfunc(state.list[state.index].src_addr)];
340 + if ((curr = *head))
342 + if (curr == &state.list[state.index]) break;
344 + } while ((curr = curr->next));
346 + /* Then, remove it */
349 + last->next = last->next->next;
351 + *head = (*head)->next;
354 + /* Get our list entry */
355 + curr = &state.list[state.index++];
356 + if (state.index >= LIST_SIZE) state.index = 0;
358 + /* Link it into the hash table */
359 + head = &state.hash[hash];
360 + curr->next = *head;
363 + /* And fill in the fields */
364 + curr->timestamp = now;
365 + curr->src_addr = addr;
366 + curr->dest_addr.s_addr = ip_hdr->daddr;
367 + curr->src_port = src_port;
369 + curr->weight = (ntohs(dest_port) < 1024) ?
370 + psdinfo->lo_ports_weight : psdinfo->hi_ports_weight;
371 + curr->ports[0].number = dest_port;
372 + curr->ports[0].proto = proto;
373 + curr->ports[0].and_flags = tcp_flags;
374 + curr->ports[0].or_flags = tcp_flags;
375 + curr->tos = ip_hdr->tos;
376 + curr->ttl = ip_hdr->ttl;
379 + spin_unlock(&state.lock);
383 + spin_unlock(&state.lock);
387 +static int ipt_psd_checkentry(const char *tablename,
388 + const struct ipt_ip *e,
390 + unsigned int matchsize,
391 + unsigned int hook_mask)
393 +/* const struct ipt_psd_info *psdinfo = targinfo;*/
395 + /* we accept TCP only */
396 +/* if (e->ip.proto != IPPROTO_TCP) { */
397 +/* DEBUGP("PSD: specified protocol may be TCP only\n"); */
401 + if (matchsize != IPT_ALIGN(sizeof(struct ipt_psd_info))) {
402 + DEBUGP("PSD: matchsize %u != %u\n",
404 + IPT_ALIGN(sizeof(struct ipt_psd_info)));
411 +static struct ipt_match ipt_psd_reg = {
413 + .match = ipt_psd_match,
414 + .checkentry = ipt_psd_checkentry,
415 + .me = THIS_MODULE };
417 +static int __init init(void)
419 + if (ipt_register_match(&ipt_psd_reg))
422 + memset(&state, 0, sizeof(state));
424 + spin_lock_init(&(state.lock));
426 + printk("netfilter PSD loaded - (c) astaro AG\n");
430 +static void __exit fini(void)
432 + ipt_unregister_match(&ipt_psd_reg);
433 + printk("netfilter PSD unloaded - (c) astaro AG\n");