OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [linux/] [linux-2.4/] [net/] [ipv4/] [netfilter/] [ip_conntrack_ftp.c] - Blame information for rev 1765

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 1275 phoenix
/* FTP extension for IP connection tracking. */
2
#include <linux/config.h>
3
#include <linux/module.h>
4
#include <linux/netfilter.h>
5
#include <linux/ip.h>
6
#include <linux/ctype.h>
7
#include <net/checksum.h>
8
#include <net/tcp.h>
9
 
10
#include <linux/netfilter_ipv4/lockhelp.h>
11
#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
12
#include <linux/netfilter_ipv4/ip_conntrack_ftp.h>
13
 
14
DECLARE_LOCK(ip_ftp_lock);
15
struct module *ip_conntrack_ftp = THIS_MODULE;
16
 
17
#define MAX_PORTS 8
18
static int ports[MAX_PORTS];
19
static int ports_c = 0;
20
#ifdef MODULE_PARM
21
MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_PORTS) "i");
22
#endif
23
 
24
static int loose = 0;
25
MODULE_PARM(loose, "i");
26
 
27
#if 0
28
#define DEBUGP printk
29
#else
30
#define DEBUGP(format, args...)
31
#endif
32
 
33
static int try_rfc959(const char *, size_t, u_int32_t [], char);
34
static int try_eprt(const char *, size_t, u_int32_t [], char);
35
static int try_epsv_response(const char *, size_t, u_int32_t [], char);
36
 
37
static struct ftp_search {
38
        enum ip_conntrack_dir dir;
39
        const char *pattern;
40
        size_t plen;
41
        char skip;
42
        char term;
43
        enum ip_ct_ftp_type ftptype;
44
        int (*getnum)(const char *, size_t, u_int32_t[], char);
45
} search[] = {
46
        {
47
                IP_CT_DIR_ORIGINAL,
48
                "PORT", sizeof("PORT") - 1, ' ', '\r',
49
                IP_CT_FTP_PORT,
50
                try_rfc959,
51
        },
52
        {
53
                IP_CT_DIR_REPLY,
54
                "227 ", sizeof("227 ") - 1, '(', ')',
55
                IP_CT_FTP_PASV,
56
                try_rfc959,
57
        },
58
        {
59
                IP_CT_DIR_ORIGINAL,
60
                "EPRT", sizeof("EPRT") - 1, ' ', '\r',
61
                IP_CT_FTP_EPRT,
62
                try_eprt,
63
        },
64
        {
65
                IP_CT_DIR_REPLY,
66
                "229 ", sizeof("229 ") - 1, '(', ')',
67
                IP_CT_FTP_EPSV,
68
                try_epsv_response,
69
        },
70
};
71
 
72
static int try_number(const char *data, size_t dlen, u_int32_t array[],
73
                      int array_size, char sep, char term)
74
{
75
        u_int32_t i, len;
76
 
77
        memset(array, 0, sizeof(array[0])*array_size);
78
 
79
        /* Keep data pointing at next char. */
80
        for (i = 0, len = 0; len < dlen && i < array_size; len++, data++) {
81
                if (*data >= '0' && *data <= '9') {
82
                        array[i] = array[i]*10 + *data - '0';
83
                }
84
                else if (*data == sep)
85
                        i++;
86
                else {
87
                        /* Unexpected character; true if it's the
88
                           terminator and we're finished. */
89
                        if (*data == term && i == array_size - 1)
90
                                return len;
91
 
92
                        DEBUGP("Char %u (got %u nums) `%u' unexpected\n",
93
                               len, i, *data);
94
                        return 0;
95
                }
96
        }
97
        DEBUGP("Failed to fill %u numbers separated by %c\n", array_size, sep);
98
 
99
        return 0;
100
}
101
 
102
/* Returns 0, or length of numbers: 192,168,1,1,5,6 */
103
static int try_rfc959(const char *data, size_t dlen, u_int32_t array[6],
104
                       char term)
105
{
106
        return try_number(data, dlen, array, 6, ',', term);
107
}
108
 
109
/* Grab port: number up to delimiter */
110
static int get_port(const char *data, int start, size_t dlen, char delim,
111
                    u_int32_t array[2])
112
{
113
        u_int16_t port = 0;
114
        int i;
115
 
116
        for (i = start; i < dlen; i++) {
117
                /* Finished? */
118
                if (data[i] == delim) {
119
                        if (port == 0)
120
                                break;
121
                        array[0] = port >> 8;
122
                        array[1] = port;
123
                        return i + 1;
124
                }
125
                else if (data[i] >= '0' && data[i] <= '9')
126
                        port = port*10 + data[i] - '0';
127
                else /* Some other crap */
128
                        break;
129
        }
130
        return 0;
131
}
132
 
133
/* Returns 0, or length of numbers: |1|132.235.1.2|6275| */
134
static int try_eprt(const char *data, size_t dlen, u_int32_t array[6],
135
                    char term)
136
{
137
        char delim;
138
        int length;
139
 
140
        /* First character is delimiter, then "1" for IPv4, then
141
           delimiter again. */
142
        if (dlen <= 3) return 0;
143
        delim = data[0];
144
        if (isdigit(delim) || delim < 33 || delim > 126
145
            || data[1] != '1' || data[2] != delim)
146
                return 0;
147
 
148
        DEBUGP("EPRT: Got |1|!\n");
149
        /* Now we have IP address. */
150
        length = try_number(data + 3, dlen - 3, array, 4, '.', delim);
151
        if (length == 0)
152
                return 0;
153
 
154
        DEBUGP("EPRT: Got IP address!\n");
155
        /* Start offset includes initial "|1|", and trailing delimiter */
156
        return get_port(data, 3 + length + 1, dlen, delim, array+4);
157
}
158
 
159
/* Returns 0, or length of numbers: |||6446| */
160
static int try_epsv_response(const char *data, size_t dlen, u_int32_t array[6],
161
                             char term)
162
{
163
        char delim;
164
 
165
        /* Three delimiters. */
166
        if (dlen <= 3) return 0;
167
        delim = data[0];
168
        if (isdigit(delim) || delim < 33 || delim > 126
169
            || data[1] != delim || data[2] != delim)
170
                return 0;
171
 
172
        return get_port(data, 3, dlen, delim, array+4);
173
}
174
 
175
/* Return 1 for match, 0 for accept, -1 for partial. */
176
static int find_pattern(const char *data, size_t dlen,
177
                        const char *pattern, size_t plen,
178
                        char skip, char term,
179
                        unsigned int *numoff,
180
                        unsigned int *numlen,
181
                        u_int32_t array[6],
182
                        int (*getnum)(const char *, size_t, u_int32_t[], char))
183
{
184
        size_t i;
185
 
186
        DEBUGP("find_pattern `%s': dlen = %u\n", pattern, dlen);
187
        if (dlen == 0)
188
                return 0;
189
 
190
        if (dlen <= plen) {
191
                /* Short packet: try for partial? */
192
                if (strnicmp(data, pattern, dlen) == 0)
193
                        return -1;
194
                else return 0;
195
        }
196
 
197
        if (strnicmp(data, pattern, plen) != 0) {
198
#if 0
199
                size_t i;
200
 
201
                DEBUGP("ftp: string mismatch\n");
202
                for (i = 0; i < plen; i++) {
203
                        DEBUGP("ftp:char %u `%c'(%u) vs `%c'(%u)\n",
204
                                i, data[i], data[i],
205
                                pattern[i], pattern[i]);
206
                }
207
#endif
208
                return 0;
209
        }
210
 
211
        DEBUGP("Pattern matches!\n");
212
        /* Now we've found the constant string, try to skip
213
           to the 'skip' character */
214
        for (i = plen; data[i] != skip; i++)
215
                if (i == dlen - 1) return -1;
216
 
217
        /* Skip over the last character */
218
        i++;
219
 
220
        DEBUGP("Skipped up to `%c'!\n", skip);
221
 
222
        *numoff = i;
223
        *numlen = getnum(data + i, dlen - i, array, term);
224
        if (!*numlen)
225
                return -1;
226
 
227
        DEBUGP("Match succeeded!\n");
228
        return 1;
229
}
230
 
231
/* FIXME: This should be in userspace.  Later. */
232
static int help(const struct iphdr *iph, size_t len,
233
                struct ip_conntrack *ct,
234
                enum ip_conntrack_info ctinfo)
235
{
236
        /* tcplen not negative guaranteed by ip_conntrack_tcp.c */
237
        struct tcphdr *tcph = (void *)iph + iph->ihl * 4;
238
        const char *data = (const char *)tcph + tcph->doff * 4;
239
        unsigned int tcplen = len - iph->ihl * 4;
240
        unsigned int datalen = tcplen - tcph->doff * 4;
241
        u_int32_t old_seq_aft_nl;
242
        int old_seq_aft_nl_set;
243
        u_int32_t array[6] = { 0 };
244
        int dir = CTINFO2DIR(ctinfo);
245
        unsigned int matchlen, matchoff;
246
        struct ip_ct_ftp_master *ct_ftp_info = &ct->help.ct_ftp_info;
247
        struct ip_conntrack_expect expect, *exp = &expect;
248
        struct ip_ct_ftp_expect *exp_ftp_info = &exp->help.exp_ftp_info;
249
 
250
        unsigned int i;
251
        int found = 0;
252
 
253
        /* Until there's been traffic both ways, don't look in packets. */
254
        if (ctinfo != IP_CT_ESTABLISHED
255
            && ctinfo != IP_CT_ESTABLISHED+IP_CT_IS_REPLY) {
256
                DEBUGP("ftp: Conntrackinfo = %u\n", ctinfo);
257
                return NF_ACCEPT;
258
        }
259
 
260
        /* Not whole TCP header? */
261
        if (tcplen < sizeof(struct tcphdr) || tcplen < tcph->doff*4) {
262
                DEBUGP("ftp: tcplen = %u\n", (unsigned)tcplen);
263
                return NF_ACCEPT;
264
        }
265
 
266
        /* Checksum invalid?  Ignore. */
267
        /* FIXME: Source route IP option packets --RR */
268
        if (tcp_v4_check(tcph, tcplen, iph->saddr, iph->daddr,
269
                         csum_partial((char *)tcph, tcplen, 0))) {
270
                DEBUGP("ftp_help: bad csum: %p %u %u.%u.%u.%u %u.%u.%u.%u\n",
271
                       tcph, tcplen, NIPQUAD(iph->saddr),
272
                       NIPQUAD(iph->daddr));
273
                return NF_ACCEPT;
274
        }
275
 
276
        LOCK_BH(&ip_ftp_lock);
277
        old_seq_aft_nl_set = ct_ftp_info->seq_aft_nl_set[dir];
278
        old_seq_aft_nl = ct_ftp_info->seq_aft_nl[dir];
279
 
280
        DEBUGP("conntrack_ftp: datalen %u\n", datalen);
281
        if ((datalen > 0) && (data[datalen-1] == '\n')) {
282
                DEBUGP("conntrack_ftp: datalen %u ends in \\n\n", datalen);
283
                if (!old_seq_aft_nl_set
284
                    || after(ntohl(tcph->seq) + datalen, old_seq_aft_nl)) {
285
                        DEBUGP("conntrack_ftp: updating nl to %u\n",
286
                               ntohl(tcph->seq) + datalen);
287
                        ct_ftp_info->seq_aft_nl[dir] =
288
                                                ntohl(tcph->seq) + datalen;
289
                        ct_ftp_info->seq_aft_nl_set[dir] = 1;
290
                }
291
        }
292
        UNLOCK_BH(&ip_ftp_lock);
293
 
294
        if(!old_seq_aft_nl_set ||
295
                        (ntohl(tcph->seq) != old_seq_aft_nl)) {
296
                DEBUGP("ip_conntrack_ftp_help: wrong seq pos %s(%u)\n",
297
                       old_seq_aft_nl_set ? "":"(UNSET) ", old_seq_aft_nl);
298
                return NF_ACCEPT;
299
        }
300
 
301
        /* Initialize IP array to expected address (it's not mentioned
302
           in EPSV responses) */
303
        array[0] = (ntohl(ct->tuplehash[dir].tuple.src.ip) >> 24) & 0xFF;
304
        array[1] = (ntohl(ct->tuplehash[dir].tuple.src.ip) >> 16) & 0xFF;
305
        array[2] = (ntohl(ct->tuplehash[dir].tuple.src.ip) >> 8) & 0xFF;
306
        array[3] = ntohl(ct->tuplehash[dir].tuple.src.ip) & 0xFF;
307
 
308
        for (i = 0; i < sizeof(search) / sizeof(search[0]); i++) {
309
                if (search[i].dir != dir) continue;
310
 
311
                found = find_pattern(data, datalen,
312
                                     search[i].pattern,
313
                                     search[i].plen,
314
                                     search[i].skip,
315
                                     search[i].term,
316
                                     &matchoff, &matchlen,
317
                                     array,
318
                                     search[i].getnum);
319
                if (found) break;
320
        }
321
        if (found == -1) {
322
                /* We don't usually drop packets.  After all, this is
323
                   connection tracking, not packet filtering.
324
                   However, it is neccessary for accurate tracking in
325
                   this case. */
326
                if (net_ratelimit())
327
                        printk("conntrack_ftp: partial %s %u+%u\n",
328
                               search[i].pattern,
329
                               ntohl(tcph->seq), datalen);
330
                return NF_DROP;
331
        } else if (found == 0) /* No match */
332
                return NF_ACCEPT;
333
 
334
        DEBUGP("conntrack_ftp: match `%.*s' (%u bytes at %u)\n",
335
               (int)matchlen, data + matchoff,
336
               matchlen, ntohl(tcph->seq) + matchoff);
337
 
338
        memset(&expect, 0, sizeof(expect));
339
 
340
        /* Update the ftp info */
341
        LOCK_BH(&ip_ftp_lock);
342
        if (htonl((array[0] << 24) | (array[1] << 16) | (array[2] << 8) | array[3])
343
            == ct->tuplehash[dir].tuple.src.ip) {
344
                exp->seq = ntohl(tcph->seq) + matchoff;
345
                exp_ftp_info->len = matchlen;
346
                exp_ftp_info->ftptype = search[i].ftptype;
347
                exp_ftp_info->port = array[4] << 8 | array[5];
348
        } else {
349
                /* Enrico Scholz's passive FTP to partially RNAT'd ftp
350
                   server: it really wants us to connect to a
351
                   different IP address.  Simply don't record it for
352
                   NAT. */
353
                DEBUGP("conntrack_ftp: NOT RECORDING: %u,%u,%u,%u != %u.%u.%u.%u\n",
354
                       array[0], array[1], array[2], array[3],
355
                       NIPQUAD(ct->tuplehash[dir].tuple.src.ip));
356
 
357
                /* Thanks to Cristiano Lincoln Mattos
358
                   <lincoln@cesar.org.br> for reporting this potential
359
                   problem (DMZ machines opening holes to internal
360
                   networks, or the packet filter itself). */
361
                if (!loose) goto out;
362
        }
363
 
364
        exp->tuple = ((struct ip_conntrack_tuple)
365
                { { ct->tuplehash[!dir].tuple.src.ip,
366
                    { 0 } },
367
                  { htonl((array[0] << 24) | (array[1] << 16)
368
                          | (array[2] << 8) | array[3]),
369
                    { .tcp = { htons(array[4] << 8 | array[5]) } },
370
                    IPPROTO_TCP }});
371
        exp->mask = ((struct ip_conntrack_tuple)
372
                { { 0xFFFFFFFF, { 0 } },
373
                  { 0xFFFFFFFF, { .tcp = { 0xFFFF } }, 0xFFFF }});
374
 
375
        exp->expectfn = NULL;
376
 
377
        /* Ignore failure; should only happen with NAT */
378
        ip_conntrack_expect_related(ct, &expect);
379
 out:
380
        UNLOCK_BH(&ip_ftp_lock);
381
 
382
        return NF_ACCEPT;
383
}
384
 
385
static struct ip_conntrack_helper ftp[MAX_PORTS];
386
static char ftp_names[MAX_PORTS][10];
387
 
388
/* Not __exit: called from init() */
389
static void fini(void)
390
{
391
        int i;
392
        for (i = 0; i < ports_c; i++) {
393
                DEBUGP("ip_ct_ftp: unregistering helper for port %d\n",
394
                                ports[i]);
395
                ip_conntrack_helper_unregister(&ftp[i]);
396
        }
397
}
398
 
399
static int __init init(void)
400
{
401
        int i, ret;
402
        char *tmpname;
403
 
404
        if (ports[0] == 0)
405
                ports[0] = FTP_PORT;
406
 
407
        for (i = 0; (i < MAX_PORTS) && ports[i]; i++) {
408
                ftp[i].tuple.src.u.tcp.port = htons(ports[i]);
409
                ftp[i].tuple.dst.protonum = IPPROTO_TCP;
410
                ftp[i].mask.src.u.tcp.port = 0xFFFF;
411
                ftp[i].mask.dst.protonum = 0xFFFF;
412
                ftp[i].max_expected = 1;
413
                ftp[i].timeout = 0;
414
                ftp[i].flags = IP_CT_HELPER_F_REUSE_EXPECT;
415
                ftp[i].me = ip_conntrack_ftp;
416
                ftp[i].help = help;
417
 
418
                tmpname = &ftp_names[i][0];
419
                if (ports[i] == FTP_PORT)
420
                        sprintf(tmpname, "ftp");
421
                else
422
                        sprintf(tmpname, "ftp-%d", ports[i]);
423
                ftp[i].name = tmpname;
424
 
425
                DEBUGP("ip_ct_ftp: registering helper for port %d\n",
426
                                ports[i]);
427
                ret = ip_conntrack_helper_register(&ftp[i]);
428
 
429
                if (ret) {
430
                        fini();
431
                        return ret;
432
                }
433
                ports_c++;
434
        }
435
        return 0;
436
}
437
 
438
EXPORT_SYMBOL(ip_ftp_lock);
439
 
440
MODULE_LICENSE("GPL");
441
module_init(init);
442
module_exit(fini);

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.