~ubuntu-branches/ubuntu/lucid/xtables-addons/lucid

« back to all changes in this revision

Viewing changes to extensions/xt_psd.c

  • Committer: Bazaar Package Importer
  • Author(s): Pierre Chifflier
  • Date: 2009-09-10 21:42:05 UTC
  • mfrom: (1.2.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20090910214205-neqgwq7y5nctaty7
Tags: 1.18-1
* New Upstream Version
  This version has support for 2.6.31 (Closes: #545542)
* Bump standards version (no changes)
* Depend on quilt (Closes: #533653)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
  This is a module which is used for PSD (portscan detection)
 
3
  Derived from scanlogd v2.1 written by Solar Designer <solar@false.com>
 
4
  and LOG target module.
 
5
 
 
6
  Copyright (C) 2000,2001 astaro AG
 
7
 
 
8
  This file is distributed under the terms of the GNU General Public
 
9
  License (GPL). Copies of the GPL can be obtained from:
 
10
     ftp://prep.ai.mit.edu/pub/gnu/GPL
 
11
 
 
12
  2000-05-04 Markus Hennig <hennig@astaro.de> : initial
 
13
  2000-08-18 Dennis Koslowski <koslowski@astaro.de> : first release
 
14
  2000-12-01 Dennis Koslowski <koslowski@astaro.de> : UDP scans detection added
 
15
  2001-01-02 Dennis Koslowski <koslowski@astaro.de> : output modified
 
16
  2001-02-04 Jan Rekorajski <baggins@pld.org.pl> : converted from target to match
 
17
  2004-05-05 Martijn Lievaart <m@rtij.nl> : ported to 2.6
 
18
  2007-04-05 Mohd Nawawi Mohamad Jamili <nawawi@tracenetworkcorporation.com> : ported to 2.6.18
 
19
  2008-03-21 Mohd Nawawi Mohamad Jamili <nawawi@tracenetworkcorporation.com> : ported to 2.6.24
 
20
  2009-08-07 Mohd Nawawi Mohamad Jamili <nawawi@tracenetworkcorporation.com> : ported to xtables-addons
 
21
*/
 
22
 
 
23
#define pr_fmt(x) KBUILD_MODNAME ": " x
 
24
#include <linux/module.h>
 
25
#include <linux/moduleparam.h>
 
26
#include <linux/skbuff.h>
 
27
#include <linux/ip.h>
 
28
#include <net/tcp.h>
 
29
#include <linux/spinlock.h>
 
30
#include <linux/netfilter_ipv4/ip_tables.h>
 
31
#include <linux/netfilter/x_tables.h>
 
32
#include "xt_psd.h"
 
33
#include "compat_xtables.h"
 
34
 
 
35
MODULE_LICENSE("GPL");
 
36
MODULE_AUTHOR("Dennis Koslowski <koslowski@astaro.com>");
 
37
MODULE_AUTHOR("Martijn Lievaart <m@rtij.nl>");
 
38
MODULE_AUTHOR("Jan Rekorajski <baggins@pld.org.pl>");
 
39
MODULE_AUTHOR(" Mohd Nawawi Mohamad Jamili <nawawi@tracenetworkcorporation.com>");
 
40
MODULE_DESCRIPTION("Xtables: PSD - portscan detection");
 
41
MODULE_ALIAS("ipt_psd");
 
42
 
 
43
#define HF_DADDR_CHANGING   0x01
 
44
#define HF_SPORT_CHANGING   0x02
 
45
#define HF_TOS_CHANGING     0x04
 
46
#define HF_TTL_CHANGING     0x08
 
47
 
 
48
/*
 
49
 * Information we keep per each target port
 
50
 */
 
51
struct port {
 
52
        u_int16_t number;      /* port number */
 
53
        u_int8_t proto;        /* protocol number */
 
54
        u_int8_t and_flags;    /* tcp ANDed flags */
 
55
        u_int8_t or_flags;     /* tcp ORed flags */
 
56
};
 
57
 
 
58
/*
 
59
 * Information we keep per each source address.
 
60
 */
 
61
struct host {
 
62
        struct host *next;                                              /* Next entry with the same hash */
 
63
        unsigned long timestamp;                                        /* Last update time */
 
64
        struct in_addr src_addr;                                /* Source address */
 
65
        struct in_addr dest_addr;                               /* Destination address */
 
66
        unsigned short src_port;                                /* Source port */
 
67
        int count;                                                              /* Number of ports in the list */
 
68
        int weight;                                                             /* Total weight of ports in the list */
 
69
        struct port ports[SCAN_MAX_COUNT - 1];  /* List of ports */
 
70
        unsigned char tos;                                              /* TOS */
 
71
        unsigned char ttl;                                              /* TTL */
 
72
        unsigned char flags;                                    /* HF_ flags bitmask */
 
73
};
 
74
 
 
75
/*
 
76
 * State information.
 
77
 */
 
78
static struct {
 
79
        spinlock_t lock;
 
80
        struct host list[LIST_SIZE];    /* List of source addresses */
 
81
        struct host *hash[HASH_SIZE];   /* Hash: pointers into the list */
 
82
        int index;                                              /* Oldest entry to be replaced */
 
83
} state;
 
84
 
 
85
/*
 
86
 * Convert an IP address into a hash table index.
 
87
 */
 
88
static inline int hashfunc(struct in_addr addr)
 
89
{
 
90
        unsigned int value;
 
91
        int hash;
 
92
 
 
93
        value = addr.s_addr;
 
94
        hash = 0;
 
95
        do {
 
96
                hash ^= value;
 
97
        } while ((value >>= HASH_LOG) != 0);
 
98
 
 
99
        return hash & (HASH_SIZE - 1);
 
100
}
 
101
 
 
102
static bool
 
103
xt_psd_match(const struct sk_buff *pskb, const struct xt_match_param *match)
 
104
{
 
105
        struct iphdr *iph;
 
106
        struct tcphdr *tcph;
 
107
        struct in_addr addr;
 
108
        u_int16_t src_port,dest_port;
 
109
        u_int8_t tcp_flags, proto;
 
110
        unsigned long now;
 
111
        struct host *curr, *last, **head;
 
112
        int hash, index, count;
 
113
        /* Parameters from userspace */
 
114
        const struct xt_psd_info *psdinfo = match->matchinfo;
 
115
 
 
116
        /* IP header */
 
117
        iph = ip_hdr(pskb);
 
118
 
 
119
        /* Sanity check */
 
120
        if (ntohs(iph->frag_off) & IP_OFFSET) {
 
121
                pr_debug("sanity check failed\n");
 
122
                return false;
 
123
        }
 
124
 
 
125
        /* TCP or UDP ? */
 
126
        proto = iph->protocol;
 
127
 
 
128
        if (proto != IPPROTO_TCP && proto != IPPROTO_UDP) {
 
129
                pr_debug("protocol not supported\n");
 
130
                return false;
 
131
        }
 
132
 
 
133
        /* Get the source address, source & destination ports, and TCP flags */
 
134
 
 
135
        addr.s_addr = iph->saddr;
 
136
 
 
137
        tcph = (void *)iph + ip_hdrlen(pskb);
 
138
 
 
139
        /* Yep, it's dirty */
 
140
        src_port = tcph->source;
 
141
        dest_port = tcph->dest;
 
142
 
 
143
        if (proto == IPPROTO_TCP)
 
144
                tcp_flags = *((u_int8_t*)tcph + 13);
 
145
        else
 
146
                tcp_flags = 0x00;
 
147
 
 
148
        /* We're using IP address 0.0.0.0 for a special purpose here, so don't let
 
149
         * them spoof us. [DHCP needs this feature - HW] */
 
150
        if (addr.s_addr == 0) {
 
151
                pr_debug("spoofed source address (0.0.0.0)\n");
 
152
                return false;
 
153
        }
 
154
 
 
155
        /* Use jiffies here not to depend on someone setting the time while we're
 
156
         * running; we need to be careful with possible return value overflows. */
 
157
        now = jiffies;
 
158
 
 
159
        spin_lock(&state.lock);
 
160
 
 
161
        /* Do we know this source address already? */
 
162
        count = 0;
 
163
        last = NULL;
 
164
        if ((curr = *(head = &state.hash[hash = hashfunc(addr)])) != NULL)
 
165
                do {
 
166
                        if (curr->src_addr.s_addr == addr.s_addr)
 
167
                                break;
 
168
                        count++;
 
169
                        if (curr->next != NULL)
 
170
                                last = curr;
 
171
                } while ((curr = curr->next) != NULL);
 
172
 
 
173
        if (curr != NULL) {
 
174
 
 
175
                /* We know this address, and the entry isn't too old. Update it. */
 
176
                if (now - curr->timestamp <= (psdinfo->delay_threshold*HZ)/100 &&
 
177
                    time_after_eq(now, curr->timestamp)) {
 
178
 
 
179
                        /* Just update the appropriate list entry if we've seen this port already */
 
180
                        for (index = 0; index < curr->count; index++) {
 
181
                                if (curr->ports[index].number == dest_port) {
 
182
                                        curr->ports[index].proto = proto;
 
183
                                        curr->ports[index].and_flags &= tcp_flags;
 
184
                                        curr->ports[index].or_flags |= tcp_flags;
 
185
                                        goto out_no_match;
 
186
                                }
 
187
                        }
 
188
 
 
189
                        /* TCP/ACK and/or TCP/RST to a new port? This could be an outgoing connection. */
 
190
                        if (proto == IPPROTO_TCP && (tcph->ack || tcph->rst))
 
191
                                goto out_no_match;
 
192
 
 
193
                        /* Packet to a new port, and not TCP/ACK: update the timestamp */
 
194
                        curr->timestamp = now;
 
195
 
 
196
                        /* Logged this scan already? Then drop the packet. */
 
197
                        if (curr->weight >= psdinfo->weight_threshold)
 
198
                                goto out_match;
 
199
 
 
200
                        /* Specify if destination address, source port, TOS or TTL are not fixed */
 
201
                        if (curr->dest_addr.s_addr != iph->daddr)
 
202
                                curr->flags |= HF_DADDR_CHANGING;
 
203
                        if (curr->src_port != src_port)
 
204
                                curr->flags |= HF_SPORT_CHANGING;
 
205
                        if (curr->tos != iph->tos)
 
206
                                curr->flags |= HF_TOS_CHANGING;
 
207
                        if (curr->ttl != iph->ttl)
 
208
                                curr->flags |= HF_TTL_CHANGING;
 
209
 
 
210
                        /* Update the total weight */
 
211
                        curr->weight += (ntohs(dest_port) < 1024) ?
 
212
                                psdinfo->lo_ports_weight : psdinfo->hi_ports_weight;
 
213
 
 
214
                        /* Got enough destination ports to decide that this is a scan? */
 
215
                        /* Then log it and drop the packet. */
 
216
                        if (curr->weight >= psdinfo->weight_threshold)
 
217
                                goto out_match;
 
218
 
 
219
                        /* Remember the new port */
 
220
                        if (curr->count < SCAN_MAX_COUNT) {
 
221
                                curr->ports[curr->count].number = dest_port;
 
222
                                curr->ports[curr->count].proto = proto;
 
223
                                curr->ports[curr->count].and_flags = tcp_flags;
 
224
                                curr->ports[curr->count].or_flags = tcp_flags;
 
225
                                curr->count++;
 
226
                        }
 
227
 
 
228
                        goto out_no_match;
 
229
                }
 
230
 
 
231
                /* We know this address, but the entry is outdated. Mark it unused, and
 
232
                 * remove from the hash table. We'll allocate a new entry instead since
 
233
                 * this one might get re-used too soon. */
 
234
                curr->src_addr.s_addr = 0;
 
235
                if (last != NULL)
 
236
                        last->next = last->next->next;
 
237
                else if (*head != NULL)
 
238
                        *head = (*head)->next;
 
239
                last = NULL;
 
240
        }
 
241
 
 
242
        /* We don't need an ACK from a new source address */
 
243
        if (proto == IPPROTO_TCP && tcph->ack)
 
244
                goto out_no_match;
 
245
 
 
246
        /* Got too many source addresses with the same hash value? Then remove the
 
247
         * oldest one from the hash table, so that they can't take too much of our
 
248
         * CPU time even with carefully chosen spoofed IP addresses. */
 
249
        if (count >= HASH_MAX && last != NULL)
 
250
                last->next = NULL;
 
251
 
 
252
        /* We're going to re-use the oldest list entry, so remove it from the hash
 
253
         * table first (if it is really already in use, and isn't removed from the
 
254
         * hash table already because of the HASH_MAX check above). */
 
255
 
 
256
        /* First, find it */
 
257
        if (state.list[state.index].src_addr.s_addr != 0)
 
258
                head = &state.hash[hashfunc(state.list[state.index].src_addr)];
 
259
        else
 
260
                head = &last;
 
261
        last = NULL;
 
262
        if ((curr = *head) != NULL)
 
263
                do {
 
264
                        if (curr == &state.list[state.index])
 
265
                                break;
 
266
                        last = curr;
 
267
                } while ((curr = curr->next) != NULL);
 
268
 
 
269
        /* Then, remove it */
 
270
        if (curr != NULL) {
 
271
                if (last != NULL)
 
272
                        last->next = last->next->next;
 
273
                else if (*head != NULL)
 
274
                        *head = (*head)->next;
 
275
        }
 
276
 
 
277
        /* Get our list entry */
 
278
        curr = &state.list[state.index++];
 
279
        if (state.index >= LIST_SIZE)
 
280
                state.index = 0;
 
281
 
 
282
        /* Link it into the hash table */
 
283
        head = &state.hash[hash];
 
284
        curr->next = *head;
 
285
        *head = curr;
 
286
 
 
287
        /* And fill in the fields */
 
288
        curr->timestamp = now;
 
289
        curr->src_addr = addr;
 
290
        curr->dest_addr.s_addr = iph->daddr;
 
291
        curr->src_port = src_port;
 
292
        curr->count = 1;
 
293
        curr->weight = (ntohs(dest_port) < 1024) ? psdinfo->lo_ports_weight : psdinfo->hi_ports_weight;
 
294
        curr->ports[0].number = dest_port;
 
295
        curr->ports[0].proto = proto;
 
296
        curr->ports[0].and_flags = tcp_flags;
 
297
        curr->ports[0].or_flags = tcp_flags;
 
298
        curr->tos = iph->tos;
 
299
        curr->ttl = iph->ttl;
 
300
 
 
301
out_no_match:
 
302
        spin_unlock(&state.lock);
 
303
        return false;
 
304
 
 
305
out_match:
 
306
        spin_unlock(&state.lock);
 
307
        return true;
 
308
}
 
309
 
 
310
static struct xt_match xt_psd_reg __read_mostly = {
 
311
        .name           = "psd",
 
312
    .family             = AF_INET,
 
313
        .revision  = 1,
 
314
        .match          = xt_psd_match,
 
315
        .matchsize      = sizeof(struct xt_psd_info),
 
316
        .me                     = THIS_MODULE,
 
317
};
 
318
 
 
319
static int __init xt_psd_init(void)
 
320
{
 
321
        spin_lock_init(&(state.lock));
 
322
        return xt_register_match(&xt_psd_reg);
 
323
}
 
324
 
 
325
static void __exit xt_psd_exit(void)
 
326
{
 
327
        xt_unregister_match(&xt_psd_reg);
 
328
}
 
329
 
 
330
module_init(xt_psd_init);
 
331
module_exit(xt_psd_exit);
 
332