[NETFILTER]: Add nf_conntrack subsystem.
The existing connection tracking subsystem in netfilter can only handle ipv4. There were basically two choices present to add connection tracking support for ipv6. We could either duplicate all of the ipv4 connection tracking code into an ipv6 counterpart, or (the choice taken by these patches) we could design a generic layer that could handle both ipv4 and ipv6 and thus requiring only one sub-protocol (TCP, UDP, etc.) connection tracking helper module to be written. In fact nf_conntrack is capable of working with any layer 3 protocol. The existing ipv4 specific conntrack code could also not deal with the pecularities of doing connection tracking on ipv6, which is also cured here. For example, these issues include: 1) ICMPv6 handling, which is used for neighbour discovery in ipv6 thus some messages such as these should not participate in connection tracking since effectively they are like ARP messages 2) fragmentation must be handled differently in ipv6, because the simplistic "defrag, connection track and NAT, refrag" (which the existing ipv4 connection tracking does) approach simply isn't feasible in ipv6 3) ipv6 extension header parsing must occur at the correct spots before and after connection tracking decisions, and there were no provisions for this in the existing connection tracking design 4) ipv6 has no need for stateful NAT The ipv4 specific conntrack layer is kept around, until all of the ipv4 specific conntrack helpers are ported over to nf_conntrack and it is feature complete. Once that occurs, the old conntrack stuff will get placed into the feature-removal-schedule and we will fully kill it off 6 months later. Signed-off-by: Yasuyuki Kozakai <yasuyuki.kozakai@toshiba.co.jp> Signed-off-by: Harald Welte <laforge@netfilter.org> Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
This commit is contained in:
committed by
David S. Miller
parent
6730c3c144
commit
9fb9cbb108
@@ -1,3 +1,6 @@
|
||||
menu "Core Netfilter Configuration"
|
||||
depends on NET && NETFILTER
|
||||
|
||||
config NETFILTER_NETLINK
|
||||
tristate "Netfilter netlink interface"
|
||||
help
|
||||
@@ -22,3 +25,74 @@ config NETFILTER_NETLINK_LOG
|
||||
and is also scheduled to replace the old syslog-based ipt_LOG
|
||||
and ip6t_LOG modules.
|
||||
|
||||
config NF_CONNTRACK
|
||||
tristate "Layer 3 Independent Connection tracking (EXPERIMENTAL)"
|
||||
depends on EXPERIMENTAL && IP_NF_CONNTRACK=n
|
||||
default n
|
||||
---help---
|
||||
Connection tracking keeps a record of what packets have passed
|
||||
through your machine, in order to figure out how they are related
|
||||
into connections.
|
||||
|
||||
Layer 3 independent connection tracking is experimental scheme
|
||||
which generalize ip_conntrack to support other layer 3 protocols.
|
||||
|
||||
To compile it as a module, choose M here. If unsure, say N.
|
||||
|
||||
config NF_CT_ACCT
|
||||
bool "Connection tracking flow accounting"
|
||||
depends on NF_CONNTRACK
|
||||
help
|
||||
If this option is enabled, the connection tracking code will
|
||||
keep per-flow packet and byte counters.
|
||||
|
||||
Those counters can be used for flow-based accounting or the
|
||||
`connbytes' match.
|
||||
|
||||
If unsure, say `N'.
|
||||
|
||||
config NF_CONNTRACK_MARK
|
||||
bool 'Connection mark tracking support'
|
||||
depends on NF_CONNTRACK
|
||||
help
|
||||
This option enables support for connection marks, used by the
|
||||
`CONNMARK' target and `connmark' match. Similar to the mark value
|
||||
of packets, but this mark value is kept in the conntrack session
|
||||
instead of the individual packets.
|
||||
|
||||
config NF_CONNTRACK_EVENTS
|
||||
bool "Connection tracking events"
|
||||
depends on NF_CONNTRACK
|
||||
help
|
||||
If this option is enabled, the connection tracking code will
|
||||
provide a notifier chain that can be used by other kernel code
|
||||
to get notified aboutchanges in the connection tracking state.
|
||||
|
||||
If unsure, say `N'.
|
||||
|
||||
config NF_CT_PROTO_SCTP
|
||||
tristate 'SCTP protocol on new connection tracking support (EXPERIMENTAL)'
|
||||
depends on EXPERIMENTAL && NF_CONNTRACK
|
||||
default n
|
||||
help
|
||||
With this option enabled, the layer 3 independent connection
|
||||
tracking code will be able to do state tracking on SCTP connections.
|
||||
|
||||
If you want to compile it as a module, say M here and read
|
||||
Documentation/modules.txt. If unsure, say `N'.
|
||||
|
||||
config NF_CONNTRACK_FTP
|
||||
tristate "FTP support on new connection tracking (EXPERIMENTAL)"
|
||||
depends on EXPERIMENTAL && NF_CONNTRACK
|
||||
help
|
||||
Tracking FTP connections is problematic: special helpers are
|
||||
required for tracking them, and doing masquerading and other forms
|
||||
of Network Address Translation on them.
|
||||
|
||||
This is FTP support on Layer 3 independent connection tracking.
|
||||
Layer 3 independent connection tracking is experimental scheme
|
||||
which generalize ip_conntrack to support other layer 3 protocols.
|
||||
|
||||
To compile it as a module, choose M here. If unsure, say N.
|
||||
|
||||
endmenu
|
||||
|
@@ -5,3 +5,11 @@ obj-$(CONFIG_NETFILTER) = netfilter.o
|
||||
obj-$(CONFIG_NETFILTER_NETLINK) += nfnetlink.o
|
||||
obj-$(CONFIG_NETFILTER_NETLINK_QUEUE) += nfnetlink_queue.o
|
||||
obj-$(CONFIG_NETFILTER_NETLINK_LOG) += nfnetlink_log.o
|
||||
|
||||
nf_conntrack-objs := nf_conntrack_core.o nf_conntrack_standalone.o nf_conntrack_l3proto_generic.o nf_conntrack_proto_generic.o nf_conntrack_proto_tcp.o nf_conntrack_proto_udp.o
|
||||
|
||||
obj-$(CONFIG_NF_CONNTRACK) += nf_conntrack.o
|
||||
obj-$(CONFIG_NF_CONNTRACK_FTP) += nf_conntrack_ftp.o
|
||||
|
||||
# SCTP protocol connection tracking
|
||||
obj-$(CONFIG_NF_CT_PROTO_SCTP) += nf_conntrack_proto_sctp.o
|
||||
|
1538
net/netfilter/nf_conntrack_core.c
Normal file
1538
net/netfilter/nf_conntrack_core.c
Normal file
File diff suppressed because it is too large
Load Diff
698
net/netfilter/nf_conntrack_ftp.c
Normal file
698
net/netfilter/nf_conntrack_ftp.c
Normal file
@@ -0,0 +1,698 @@
|
||||
/* FTP extension for connection tracking. */
|
||||
|
||||
/* (C) 1999-2001 Paul `Rusty' Russell
|
||||
* (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org>
|
||||
* (C) 2003,2004 USAGI/WIDE Project <http://www.linux-ipv6.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 16 Dec 2003: Yasuyuki Kozakai @USAGI <yasuyuki.kozakai@toshiba.co.jp>
|
||||
* - enable working with Layer 3 protocol independent connection tracking.
|
||||
* - track EPRT and EPSV commands with IPv6 address.
|
||||
*
|
||||
* Derived from net/ipv4/netfilter/ip_conntrack_ftp.c
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/netfilter.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/ipv6.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <net/checksum.h>
|
||||
#include <net/tcp.h>
|
||||
|
||||
#include <net/netfilter/nf_conntrack.h>
|
||||
#include <net/netfilter/nf_conntrack_helper.h>
|
||||
#include <linux/netfilter/nf_conntrack_ftp.h>
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Rusty Russell <rusty@rustcorp.com.au>");
|
||||
MODULE_DESCRIPTION("ftp connection tracking helper");
|
||||
|
||||
/* This is slow, but it's simple. --RR */
|
||||
static char *ftp_buffer;
|
||||
|
||||
static DEFINE_SPINLOCK(nf_ftp_lock);
|
||||
|
||||
#define MAX_PORTS 8
|
||||
static u_int16_t ports[MAX_PORTS];
|
||||
static unsigned int ports_c;
|
||||
module_param_array(ports, ushort, &ports_c, 0400);
|
||||
|
||||
static int loose;
|
||||
module_param(loose, int, 0600);
|
||||
|
||||
unsigned int (*nf_nat_ftp_hook)(struct sk_buff **pskb,
|
||||
enum ip_conntrack_info ctinfo,
|
||||
enum ip_ct_ftp_type type,
|
||||
unsigned int matchoff,
|
||||
unsigned int matchlen,
|
||||
struct nf_conntrack_expect *exp,
|
||||
u32 *seq);
|
||||
EXPORT_SYMBOL_GPL(nf_nat_ftp_hook);
|
||||
|
||||
#if 0
|
||||
#define DEBUGP printk
|
||||
#else
|
||||
#define DEBUGP(format, args...)
|
||||
#endif
|
||||
|
||||
static int try_rfc959(const char *, size_t, struct nf_conntrack_man *, char);
|
||||
static int try_eprt(const char *, size_t, struct nf_conntrack_man *, char);
|
||||
static int try_epsv_response(const char *, size_t, struct nf_conntrack_man *,
|
||||
char);
|
||||
|
||||
static struct ftp_search {
|
||||
enum ip_conntrack_dir dir;
|
||||
const char *pattern;
|
||||
size_t plen;
|
||||
char skip;
|
||||
char term;
|
||||
enum ip_ct_ftp_type ftptype;
|
||||
int (*getnum)(const char *, size_t, struct nf_conntrack_man *, char);
|
||||
} search[] = {
|
||||
{
|
||||
IP_CT_DIR_ORIGINAL,
|
||||
"PORT", sizeof("PORT") - 1, ' ', '\r',
|
||||
IP_CT_FTP_PORT,
|
||||
try_rfc959,
|
||||
},
|
||||
{
|
||||
IP_CT_DIR_REPLY,
|
||||
"227 ", sizeof("227 ") - 1, '(', ')',
|
||||
IP_CT_FTP_PASV,
|
||||
try_rfc959,
|
||||
},
|
||||
{
|
||||
IP_CT_DIR_ORIGINAL,
|
||||
"EPRT", sizeof("EPRT") - 1, ' ', '\r',
|
||||
IP_CT_FTP_EPRT,
|
||||
try_eprt,
|
||||
},
|
||||
{
|
||||
IP_CT_DIR_REPLY,
|
||||
"229 ", sizeof("229 ") - 1, '(', ')',
|
||||
IP_CT_FTP_EPSV,
|
||||
try_epsv_response,
|
||||
},
|
||||
};
|
||||
|
||||
/* This code is based on inet_pton() in glibc-2.2.4 */
|
||||
static int
|
||||
get_ipv6_addr(const char *src, size_t dlen, struct in6_addr *dst, u_int8_t term)
|
||||
{
|
||||
static const char xdigits[] = "0123456789abcdef";
|
||||
u_int8_t tmp[16], *tp, *endp, *colonp;
|
||||
int ch, saw_xdigit;
|
||||
u_int32_t val;
|
||||
size_t clen = 0;
|
||||
|
||||
tp = memset(tmp, '\0', sizeof(tmp));
|
||||
endp = tp + sizeof(tmp);
|
||||
colonp = NULL;
|
||||
|
||||
/* Leading :: requires some special handling. */
|
||||
if (*src == ':'){
|
||||
if (*++src != ':') {
|
||||
DEBUGP("invalid \":\" at the head of addr\n");
|
||||
return 0;
|
||||
}
|
||||
clen++;
|
||||
}
|
||||
|
||||
saw_xdigit = 0;
|
||||
val = 0;
|
||||
while ((clen < dlen) && (*src != term)) {
|
||||
const char *pch;
|
||||
|
||||
ch = tolower(*src++);
|
||||
clen++;
|
||||
|
||||
pch = strchr(xdigits, ch);
|
||||
if (pch != NULL) {
|
||||
val <<= 4;
|
||||
val |= (pch - xdigits);
|
||||
if (val > 0xffff)
|
||||
return 0;
|
||||
|
||||
saw_xdigit = 1;
|
||||
continue;
|
||||
}
|
||||
if (ch != ':') {
|
||||
DEBUGP("get_ipv6_addr: invalid char. \'%c\'\n", ch);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!saw_xdigit) {
|
||||
if (colonp) {
|
||||
DEBUGP("invalid location of \"::\".\n");
|
||||
return 0;
|
||||
}
|
||||
colonp = tp;
|
||||
continue;
|
||||
} else if (*src == term) {
|
||||
DEBUGP("trancated IPv6 addr\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (tp + 2 > endp)
|
||||
return 0;
|
||||
*tp++ = (u_int8_t) (val >> 8) & 0xff;
|
||||
*tp++ = (u_int8_t) val & 0xff;
|
||||
|
||||
saw_xdigit = 0;
|
||||
val = 0;
|
||||
continue;
|
||||
}
|
||||
if (saw_xdigit) {
|
||||
if (tp + 2 > endp)
|
||||
return 0;
|
||||
*tp++ = (u_int8_t) (val >> 8) & 0xff;
|
||||
*tp++ = (u_int8_t) val & 0xff;
|
||||
}
|
||||
if (colonp != NULL) {
|
||||
/*
|
||||
* Since some memmove()'s erroneously fail to handle
|
||||
* overlapping regions, we'll do the shift by hand.
|
||||
*/
|
||||
const int n = tp - colonp;
|
||||
int i;
|
||||
|
||||
if (tp == endp)
|
||||
return 0;
|
||||
|
||||
for (i = 1; i <= n; i++) {
|
||||
endp[- i] = colonp[n - i];
|
||||
colonp[n - i] = 0;
|
||||
}
|
||||
tp = endp;
|
||||
}
|
||||
if (tp != endp || (*src != term))
|
||||
return 0;
|
||||
|
||||
memcpy(dst->s6_addr, tmp, sizeof(dst->s6_addr));
|
||||
return clen;
|
||||
}
|
||||
|
||||
static int try_number(const char *data, size_t dlen, u_int32_t array[],
|
||||
int array_size, char sep, char term)
|
||||
{
|
||||
u_int32_t i, len;
|
||||
|
||||
memset(array, 0, sizeof(array[0])*array_size);
|
||||
|
||||
/* Keep data pointing at next char. */
|
||||
for (i = 0, len = 0; len < dlen && i < array_size; len++, data++) {
|
||||
if (*data >= '0' && *data <= '9') {
|
||||
array[i] = array[i]*10 + *data - '0';
|
||||
}
|
||||
else if (*data == sep)
|
||||
i++;
|
||||
else {
|
||||
/* Unexpected character; true if it's the
|
||||
terminator and we're finished. */
|
||||
if (*data == term && i == array_size - 1)
|
||||
return len;
|
||||
|
||||
DEBUGP("Char %u (got %u nums) `%u' unexpected\n",
|
||||
len, i, *data);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
DEBUGP("Failed to fill %u numbers separated by %c\n", array_size, sep);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Returns 0, or length of numbers: 192,168,1,1,5,6 */
|
||||
static int try_rfc959(const char *data, size_t dlen,
|
||||
struct nf_conntrack_man *cmd, char term)
|
||||
{
|
||||
int length;
|
||||
u_int32_t array[6];
|
||||
|
||||
length = try_number(data, dlen, array, 6, ',', term);
|
||||
if (length == 0)
|
||||
return 0;
|
||||
|
||||
cmd->u3.ip = htonl((array[0] << 24) | (array[1] << 16) |
|
||||
(array[2] << 8) | array[3]);
|
||||
cmd->u.tcp.port = htons((array[4] << 8) | array[5]);
|
||||
return length;
|
||||
}
|
||||
|
||||
/* Grab port: number up to delimiter */
|
||||
static int get_port(const char *data, int start, size_t dlen, char delim,
|
||||
u_int16_t *port)
|
||||
{
|
||||
u_int16_t tmp_port = 0;
|
||||
int i;
|
||||
|
||||
for (i = start; i < dlen; i++) {
|
||||
/* Finished? */
|
||||
if (data[i] == delim) {
|
||||
if (tmp_port == 0)
|
||||
break;
|
||||
*port = htons(tmp_port);
|
||||
DEBUGP("get_port: return %d\n", tmp_port);
|
||||
return i + 1;
|
||||
}
|
||||
else if (data[i] >= '0' && data[i] <= '9')
|
||||
tmp_port = tmp_port*10 + data[i] - '0';
|
||||
else { /* Some other crap */
|
||||
DEBUGP("get_port: invalid char.\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Returns 0, or length of numbers: |1|132.235.1.2|6275| or |2|3ffe::1|6275| */
|
||||
static int try_eprt(const char *data, size_t dlen, struct nf_conntrack_man *cmd,
|
||||
char term)
|
||||
{
|
||||
char delim;
|
||||
int length;
|
||||
|
||||
/* First character is delimiter, then "1" for IPv4 or "2" for IPv6,
|
||||
then delimiter again. */
|
||||
if (dlen <= 3) {
|
||||
DEBUGP("EPRT: too short\n");
|
||||
return 0;
|
||||
}
|
||||
delim = data[0];
|
||||
if (isdigit(delim) || delim < 33 || delim > 126 || data[2] != delim) {
|
||||
DEBUGP("try_eprt: invalid delimitter.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((cmd->l3num == PF_INET && data[1] != '1') ||
|
||||
(cmd->l3num == PF_INET6 && data[1] != '2')) {
|
||||
DEBUGP("EPRT: invalid protocol number.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEBUGP("EPRT: Got %c%c%c\n", delim, data[1], delim);
|
||||
|
||||
if (data[1] == '1') {
|
||||
u_int32_t array[4];
|
||||
|
||||
/* Now we have IP address. */
|
||||
length = try_number(data + 3, dlen - 3, array, 4, '.', delim);
|
||||
if (length != 0)
|
||||
cmd->u3.ip = htonl((array[0] << 24) | (array[1] << 16)
|
||||
| (array[2] << 8) | array[3]);
|
||||
} else {
|
||||
/* Now we have IPv6 address. */
|
||||
length = get_ipv6_addr(data + 3, dlen - 3,
|
||||
(struct in6_addr *)cmd->u3.ip6, delim);
|
||||
}
|
||||
|
||||
if (length == 0)
|
||||
return 0;
|
||||
DEBUGP("EPRT: Got IP address!\n");
|
||||
/* Start offset includes initial "|1|", and trailing delimiter */
|
||||
return get_port(data, 3 + length + 1, dlen, delim, &cmd->u.tcp.port);
|
||||
}
|
||||
|
||||
/* Returns 0, or length of numbers: |||6446| */
|
||||
static int try_epsv_response(const char *data, size_t dlen,
|
||||
struct nf_conntrack_man *cmd, char term)
|
||||
{
|
||||
char delim;
|
||||
|
||||
/* Three delimiters. */
|
||||
if (dlen <= 3) return 0;
|
||||
delim = data[0];
|
||||
if (isdigit(delim) || delim < 33 || delim > 126
|
||||
|| data[1] != delim || data[2] != delim)
|
||||
return 0;
|
||||
|
||||
return get_port(data, 3, dlen, delim, &cmd->u.tcp.port);
|
||||
}
|
||||
|
||||
/* Return 1 for match, 0 for accept, -1 for partial. */
|
||||
static int find_pattern(const char *data, size_t dlen,
|
||||
const char *pattern, size_t plen,
|
||||
char skip, char term,
|
||||
unsigned int *numoff,
|
||||
unsigned int *numlen,
|
||||
struct nf_conntrack_man *cmd,
|
||||
int (*getnum)(const char *, size_t,
|
||||
struct nf_conntrack_man *, char))
|
||||
{
|
||||
size_t i;
|
||||
|
||||
DEBUGP("find_pattern `%s': dlen = %u\n", pattern, dlen);
|
||||
if (dlen == 0)
|
||||
return 0;
|
||||
|
||||
if (dlen <= plen) {
|
||||
/* Short packet: try for partial? */
|
||||
if (strnicmp(data, pattern, dlen) == 0)
|
||||
return -1;
|
||||
else return 0;
|
||||
}
|
||||
|
||||
if (strnicmp(data, pattern, plen) != 0) {
|
||||
#if 0
|
||||
size_t i;
|
||||
|
||||
DEBUGP("ftp: string mismatch\n");
|
||||
for (i = 0; i < plen; i++) {
|
||||
DEBUGP("ftp:char %u `%c'(%u) vs `%c'(%u)\n",
|
||||
i, data[i], data[i],
|
||||
pattern[i], pattern[i]);
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEBUGP("Pattern matches!\n");
|
||||
/* Now we've found the constant string, try to skip
|
||||
to the 'skip' character */
|
||||
for (i = plen; data[i] != skip; i++)
|
||||
if (i == dlen - 1) return -1;
|
||||
|
||||
/* Skip over the last character */
|
||||
i++;
|
||||
|
||||
DEBUGP("Skipped up to `%c'!\n", skip);
|
||||
|
||||
*numoff = i;
|
||||
*numlen = getnum(data + i, dlen - i, cmd, term);
|
||||
if (!*numlen)
|
||||
return -1;
|
||||
|
||||
DEBUGP("Match succeeded!\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Look up to see if we're just after a \n. */
|
||||
static int find_nl_seq(u32 seq, const struct ip_ct_ftp_master *info, int dir)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < info->seq_aft_nl_num[dir]; i++)
|
||||
if (info->seq_aft_nl[dir][i] == seq)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* We don't update if it's older than what we have. */
|
||||
static void update_nl_seq(u32 nl_seq, struct ip_ct_ftp_master *info, int dir,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
unsigned int i, oldest = NUM_SEQ_TO_REMEMBER;
|
||||
|
||||
/* Look for oldest: if we find exact match, we're done. */
|
||||
for (i = 0; i < info->seq_aft_nl_num[dir]; i++) {
|
||||
if (info->seq_aft_nl[dir][i] == nl_seq)
|
||||
return;
|
||||
|
||||
if (oldest == info->seq_aft_nl_num[dir]
|
||||
|| before(info->seq_aft_nl[dir][i], oldest))
|
||||
oldest = i;
|
||||
}
|
||||
|
||||
if (info->seq_aft_nl_num[dir] < NUM_SEQ_TO_REMEMBER) {
|
||||
info->seq_aft_nl[dir][info->seq_aft_nl_num[dir]++] = nl_seq;
|
||||
nf_conntrack_event_cache(IPCT_HELPINFO_VOLATILE, skb);
|
||||
} else if (oldest != NUM_SEQ_TO_REMEMBER) {
|
||||
info->seq_aft_nl[dir][oldest] = nl_seq;
|
||||
nf_conntrack_event_cache(IPCT_HELPINFO_VOLATILE, skb);
|
||||
}
|
||||
}
|
||||
|
||||
static int help(struct sk_buff **pskb,
|
||||
unsigned int protoff,
|
||||
struct nf_conn *ct,
|
||||
enum ip_conntrack_info ctinfo)
|
||||
{
|
||||
unsigned int dataoff, datalen;
|
||||
struct tcphdr _tcph, *th;
|
||||
char *fb_ptr;
|
||||
int ret;
|
||||
u32 seq;
|
||||
int dir = CTINFO2DIR(ctinfo);
|
||||
unsigned int matchlen, matchoff;
|
||||
struct ip_ct_ftp_master *ct_ftp_info = &ct->help->ct_ftp_info;
|
||||
struct nf_conntrack_expect *exp;
|
||||
struct nf_conntrack_man cmd = {};
|
||||
|
||||
unsigned int i;
|
||||
int found = 0, ends_in_nl;
|
||||
|
||||
/* Until there's been traffic both ways, don't look in packets. */
|
||||
if (ctinfo != IP_CT_ESTABLISHED
|
||||
&& ctinfo != IP_CT_ESTABLISHED+IP_CT_IS_REPLY) {
|
||||
DEBUGP("ftp: Conntrackinfo = %u\n", ctinfo);
|
||||
return NF_ACCEPT;
|
||||
}
|
||||
|
||||
th = skb_header_pointer(*pskb, protoff, sizeof(_tcph), &_tcph);
|
||||
if (th == NULL)
|
||||
return NF_ACCEPT;
|
||||
|
||||
dataoff = protoff + th->doff * 4;
|
||||
/* No data? */
|
||||
if (dataoff >= (*pskb)->len) {
|
||||
DEBUGP("ftp: dataoff(%u) >= skblen(%u)\n", dataoff,
|
||||
(*pskb)->len);
|
||||
return NF_ACCEPT;
|
||||
}
|
||||
datalen = (*pskb)->len - dataoff;
|
||||
|
||||
spin_lock_bh(&nf_ftp_lock);
|
||||
fb_ptr = skb_header_pointer(*pskb, dataoff, datalen, ftp_buffer);
|
||||
BUG_ON(fb_ptr == NULL);
|
||||
|
||||
ends_in_nl = (fb_ptr[datalen - 1] == '\n');
|
||||
seq = ntohl(th->seq) + datalen;
|
||||
|
||||
/* Look up to see if we're just after a \n. */
|
||||
if (!find_nl_seq(ntohl(th->seq), ct_ftp_info, dir)) {
|
||||
/* Now if this ends in \n, update ftp info. */
|
||||
DEBUGP("nf_conntrack_ftp_help: wrong seq pos %s(%u) or %s(%u)\n",
|
||||
ct_ftp_info->seq_aft_nl_num[dir] > 0 ? "" : "(UNSET)",
|
||||
ct_ftp_info->seq_aft_nl[dir][0],
|
||||
ct_ftp_info->seq_aft_nl_num[dir] > 1 ? "" : "(UNSET)",
|
||||
ct_ftp_info->seq_aft_nl[dir][1]);
|
||||
ret = NF_ACCEPT;
|
||||
goto out_update_nl;
|
||||
}
|
||||
|
||||
/* Initialize IP/IPv6 addr to expected address (it's not mentioned
|
||||
in EPSV responses) */
|
||||
cmd.l3num = ct->tuplehash[dir].tuple.src.l3num;
|
||||
memcpy(cmd.u3.all, &ct->tuplehash[dir].tuple.src.u3.all,
|
||||
sizeof(cmd.u3.all));
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(search); i++) {
|
||||
if (search[i].dir != dir) continue;
|
||||
|
||||
found = find_pattern(fb_ptr, datalen,
|
||||
search[i].pattern,
|
||||
search[i].plen,
|
||||
search[i].skip,
|
||||
search[i].term,
|
||||
&matchoff, &matchlen,
|
||||
&cmd,
|
||||
search[i].getnum);
|
||||
if (found) break;
|
||||
}
|
||||
if (found == -1) {
|
||||
/* We don't usually drop packets. After all, this is
|
||||
connection tracking, not packet filtering.
|
||||
However, it is necessary for accurate tracking in
|
||||
this case. */
|
||||
if (net_ratelimit())
|
||||
printk("conntrack_ftp: partial %s %u+%u\n",
|
||||
search[i].pattern,
|
||||
ntohl(th->seq), datalen);
|
||||
ret = NF_DROP;
|
||||
goto out;
|
||||
} else if (found == 0) { /* No match */
|
||||
ret = NF_ACCEPT;
|
||||
goto out_update_nl;
|
||||
}
|
||||
|
||||
DEBUGP("conntrack_ftp: match `%.*s' (%u bytes at %u)\n",
|
||||
(int)matchlen, fb_ptr + matchoff,
|
||||
matchlen, ntohl(th->seq) + matchoff);
|
||||
|
||||
exp = nf_conntrack_expect_alloc(ct);
|
||||
if (exp == NULL) {
|
||||
ret = NF_DROP;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* We refer to the reverse direction ("!dir") tuples here,
|
||||
* because we're expecting something in the other direction.
|
||||
* Doesn't matter unless NAT is happening. */
|
||||
exp->tuple.dst.u3 = ct->tuplehash[!dir].tuple.dst.u3;
|
||||
|
||||
/* Update the ftp info */
|
||||
if ((cmd.l3num == ct->tuplehash[dir].tuple.src.l3num) &&
|
||||
memcmp(&cmd.u3.all, &ct->tuplehash[dir].tuple.src.u3.all,
|
||||
sizeof(cmd.u3.all))) {
|
||||
/* Enrico Scholz's passive FTP to partially RNAT'd ftp
|
||||
server: it really wants us to connect to a
|
||||
different IP address. Simply don't record it for
|
||||
NAT. */
|
||||
if (cmd.l3num == PF_INET) {
|
||||
DEBUGP("conntrack_ftp: NOT RECORDING: %u,%u,%u,%u != %u.%u.%u.%u\n",
|
||||
NIPQUAD(cmd.u3.ip),
|
||||
NIPQUAD(ct->tuplehash[dir].tuple.src.u3.ip));
|
||||
} else {
|
||||
DEBUGP("conntrack_ftp: NOT RECORDING: %x:%x:%x:%x:%x:%x:%x:%x != %x:%x:%x:%x:%x:%x:%x:%x\n",
|
||||
NIP6(*((struct in6_addr *)cmd.u3.ip6)),
|
||||
NIP6(*((struct in6_addr *)ct->tuplehash[dir]
|
||||
.tuple.src.u3.ip6)));
|
||||
}
|
||||
|
||||
/* Thanks to Cristiano Lincoln Mattos
|
||||
<lincoln@cesar.org.br> for reporting this potential
|
||||
problem (DMZ machines opening holes to internal
|
||||
networks, or the packet filter itself). */
|
||||
if (!loose) {
|
||||
ret = NF_ACCEPT;
|
||||
goto out_put_expect;
|
||||
}
|
||||
memcpy(&exp->tuple.dst.u3, &cmd.u3.all,
|
||||
sizeof(exp->tuple.dst.u3));
|
||||
}
|
||||
|
||||
exp->tuple.src.u3 = ct->tuplehash[!dir].tuple.src.u3;
|
||||
exp->tuple.src.l3num = cmd.l3num;
|
||||
exp->tuple.src.u.tcp.port = 0;
|
||||
exp->tuple.dst.u.tcp.port = cmd.u.tcp.port;
|
||||
exp->tuple.dst.protonum = IPPROTO_TCP;
|
||||
|
||||
exp->mask = (struct nf_conntrack_tuple)
|
||||
{ .src = { .l3num = 0xFFFF,
|
||||
.u = { .tcp = { 0 }},
|
||||
},
|
||||
.dst = { .protonum = 0xFF,
|
||||
.u = { .tcp = { 0xFFFF }},
|
||||
},
|
||||
};
|
||||
if (cmd.l3num == PF_INET) {
|
||||
exp->mask.src.u3.ip = 0xFFFFFFFF;
|
||||
exp->mask.dst.u3.ip = 0xFFFFFFFF;
|
||||
} else {
|
||||
memset(exp->mask.src.u3.ip6, 0xFF,
|
||||
sizeof(exp->mask.src.u3.ip6));
|
||||
memset(exp->mask.dst.u3.ip6, 0xFF,
|
||||
sizeof(exp->mask.src.u3.ip6));
|
||||
}
|
||||
|
||||
exp->expectfn = NULL;
|
||||
exp->flags = 0;
|
||||
|
||||
/* Now, NAT might want to mangle the packet, and register the
|
||||
* (possibly changed) expectation itself. */
|
||||
if (nf_nat_ftp_hook)
|
||||
ret = nf_nat_ftp_hook(pskb, ctinfo, search[i].ftptype,
|
||||
matchoff, matchlen, exp, &seq);
|
||||
else {
|
||||
/* Can't expect this? Best to drop packet now. */
|
||||
if (nf_conntrack_expect_related(exp) != 0)
|
||||
ret = NF_DROP;
|
||||
else
|
||||
ret = NF_ACCEPT;
|
||||
}
|
||||
|
||||
out_put_expect:
|
||||
nf_conntrack_expect_put(exp);
|
||||
|
||||
out_update_nl:
|
||||
/* Now if this ends in \n, update ftp info. Seq may have been
|
||||
* adjusted by NAT code. */
|
||||
if (ends_in_nl)
|
||||
update_nl_seq(seq, ct_ftp_info, dir, *pskb);
|
||||
out:
|
||||
spin_unlock_bh(&nf_ftp_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct nf_conntrack_helper ftp[MAX_PORTS][2];
|
||||
static char ftp_names[MAX_PORTS][2][sizeof("ftp-65535")];
|
||||
|
||||
/* don't make this __exit, since it's called from __init ! */
|
||||
static void fini(void)
|
||||
{
|
||||
int i, j;
|
||||
for (i = 0; i < ports_c; i++) {
|
||||
for (j = 0; j < 2; j++) {
|
||||
if (ftp[i][j].me == NULL)
|
||||
continue;
|
||||
|
||||
DEBUGP("nf_ct_ftp: unregistering helper for pf: %d "
|
||||
"port: %d\n",
|
||||
ftp[i][j].tuple.src.l3num, ports[i]);
|
||||
nf_conntrack_helper_unregister(&ftp[i][j]);
|
||||
}
|
||||
}
|
||||
|
||||
kfree(ftp_buffer);
|
||||
}
|
||||
|
||||
static int __init init(void)
|
||||
{
|
||||
int i, j = -1, ret = 0;
|
||||
char *tmpname;
|
||||
|
||||
ftp_buffer = kmalloc(65536, GFP_KERNEL);
|
||||
if (!ftp_buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
if (ports_c == 0)
|
||||
ports[ports_c++] = FTP_PORT;
|
||||
|
||||
/* FIXME should be configurable whether IPv4 and IPv6 FTP connections
|
||||
are tracked or not - YK */
|
||||
for (i = 0; i < ports_c; i++) {
|
||||
memset(&ftp[i], 0, sizeof(struct nf_conntrack_helper));
|
||||
|
||||
ftp[i][0].tuple.src.l3num = PF_INET;
|
||||
ftp[i][1].tuple.src.l3num = PF_INET6;
|
||||
for (j = 0; j < 2; j++) {
|
||||
ftp[i][j].tuple.src.u.tcp.port = htons(ports[i]);
|
||||
ftp[i][j].tuple.dst.protonum = IPPROTO_TCP;
|
||||
ftp[i][j].mask.src.u.tcp.port = 0xFFFF;
|
||||
ftp[i][j].mask.dst.protonum = 0xFF;
|
||||
ftp[i][j].max_expected = 1;
|
||||
ftp[i][j].timeout = 5 * 60; /* 5 Minutes */
|
||||
ftp[i][j].me = THIS_MODULE;
|
||||
ftp[i][j].help = help;
|
||||
tmpname = &ftp_names[i][j][0];
|
||||
if (ports[i] == FTP_PORT)
|
||||
sprintf(tmpname, "ftp");
|
||||
else
|
||||
sprintf(tmpname, "ftp-%d", ports[i]);
|
||||
ftp[i][j].name = tmpname;
|
||||
|
||||
DEBUGP("nf_ct_ftp: registering helper for pf: %d "
|
||||
"port: %d\n",
|
||||
ftp[i][j].tuple.src.l3num, ports[i]);
|
||||
ret = nf_conntrack_helper_register(&ftp[i][j]);
|
||||
if (ret) {
|
||||
printk("nf_ct_ftp: failed to register helper "
|
||||
" for pf: %d port: %d\n",
|
||||
ftp[i][j].tuple.src.l3num, ports[i]);
|
||||
fini();
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
module_init(init);
|
||||
module_exit(fini);
|
98
net/netfilter/nf_conntrack_l3proto_generic.c
Normal file
98
net/netfilter/nf_conntrack_l3proto_generic.c
Normal file
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* (C) 2003,2004 USAGI/WIDE Project <http://www.linux-ipv6.org>
|
||||
*
|
||||
* Based largely upon the original ip_conntrack code which
|
||||
* had the following copyright information:
|
||||
*
|
||||
* (C) 1999-2001 Paul `Rusty' Russell
|
||||
* (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Author:
|
||||
* Yasuyuki Kozakai @USAGI <yasuyuki.kozakai@toshiba.co.jp>
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/netfilter.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/icmp.h>
|
||||
#include <linux/sysctl.h>
|
||||
#include <net/ip.h>
|
||||
|
||||
#include <linux/netfilter_ipv4.h>
|
||||
#include <net/netfilter/nf_conntrack.h>
|
||||
#include <net/netfilter/nf_conntrack_protocol.h>
|
||||
#include <net/netfilter/nf_conntrack_l3proto.h>
|
||||
#include <net/netfilter/nf_conntrack_core.h>
|
||||
#include <net/netfilter/ipv4/nf_conntrack_ipv4.h>
|
||||
|
||||
#if 0
|
||||
#define DEBUGP printk
|
||||
#else
|
||||
#define DEBUGP(format, args...)
|
||||
#endif
|
||||
|
||||
DECLARE_PER_CPU(struct nf_conntrack_stat, nf_conntrack_stat);
|
||||
|
||||
static int generic_pkt_to_tuple(const struct sk_buff *skb, unsigned int nhoff,
|
||||
struct nf_conntrack_tuple *tuple)
|
||||
{
|
||||
memset(&tuple->src.u3, 0, sizeof(tuple->src.u3));
|
||||
memset(&tuple->dst.u3, 0, sizeof(tuple->dst.u3));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int generic_invert_tuple(struct nf_conntrack_tuple *tuple,
|
||||
const struct nf_conntrack_tuple *orig)
|
||||
{
|
||||
memset(&tuple->src.u3, 0, sizeof(tuple->src.u3));
|
||||
memset(&tuple->dst.u3, 0, sizeof(tuple->dst.u3));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int generic_print_tuple(struct seq_file *s,
|
||||
const struct nf_conntrack_tuple *tuple)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int generic_print_conntrack(struct seq_file *s,
|
||||
const struct nf_conn *conntrack)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
generic_prepare(struct sk_buff **pskb, unsigned int hooknum,
|
||||
unsigned int *dataoff, u_int8_t *protonum)
|
||||
{
|
||||
/* Never track !!! */
|
||||
return -NF_ACCEPT;
|
||||
}
|
||||
|
||||
|
||||
static u_int32_t generic_get_features(const struct nf_conntrack_tuple *tuple)
|
||||
|
||||
{
|
||||
return NF_CT_F_BASIC;
|
||||
}
|
||||
|
||||
struct nf_conntrack_l3proto nf_conntrack_generic_l3proto = {
|
||||
.l3proto = PF_UNSPEC,
|
||||
.name = "unknown",
|
||||
.pkt_to_tuple = generic_pkt_to_tuple,
|
||||
.invert_tuple = generic_invert_tuple,
|
||||
.print_tuple = generic_print_tuple,
|
||||
.print_conntrack = generic_print_conntrack,
|
||||
.prepare = generic_prepare,
|
||||
.get_features = generic_get_features,
|
||||
.me = THIS_MODULE,
|
||||
};
|
85
net/netfilter/nf_conntrack_proto_generic.c
Normal file
85
net/netfilter/nf_conntrack_proto_generic.c
Normal file
@@ -0,0 +1,85 @@
|
||||
/* (C) 1999-2001 Paul `Rusty' Russell
|
||||
* (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 16 Dec 2003: Yasuyuki Kozakai @USAGI <yasuyuki.kozakai@toshiba.co.jp>
|
||||
* - enable working with L3 protocol independent connection tracking.
|
||||
*
|
||||
* Derived from net/ipv4/netfilter/ip_conntrack_proto_generic.c
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/netfilter.h>
|
||||
#include <net/netfilter/nf_conntrack_protocol.h>
|
||||
|
||||
unsigned long nf_ct_generic_timeout = 600*HZ;
|
||||
|
||||
static int generic_pkt_to_tuple(const struct sk_buff *skb,
|
||||
unsigned int dataoff,
|
||||
struct nf_conntrack_tuple *tuple)
|
||||
{
|
||||
tuple->src.u.all = 0;
|
||||
tuple->dst.u.all = 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int generic_invert_tuple(struct nf_conntrack_tuple *tuple,
|
||||
const struct nf_conntrack_tuple *orig)
|
||||
{
|
||||
tuple->src.u.all = 0;
|
||||
tuple->dst.u.all = 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Print out the per-protocol part of the tuple. */
|
||||
static int generic_print_tuple(struct seq_file *s,
|
||||
const struct nf_conntrack_tuple *tuple)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Print out the private part of the conntrack. */
|
||||
static int generic_print_conntrack(struct seq_file *s,
|
||||
const struct nf_conn *state)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Returns verdict for packet, or -1 for invalid. */
|
||||
static int packet(struct nf_conn *conntrack,
|
||||
const struct sk_buff *skb,
|
||||
unsigned int dataoff,
|
||||
enum ip_conntrack_info ctinfo,
|
||||
int pf,
|
||||
unsigned int hooknum)
|
||||
{
|
||||
nf_ct_refresh_acct(conntrack, ctinfo, skb, nf_ct_generic_timeout);
|
||||
return NF_ACCEPT;
|
||||
}
|
||||
|
||||
/* Called when a new connection for this protocol found. */
|
||||
static int new(struct nf_conn *conntrack, const struct sk_buff *skb,
|
||||
unsigned int dataoff)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct nf_conntrack_protocol nf_conntrack_generic_protocol =
|
||||
{
|
||||
.l3proto = PF_UNSPEC,
|
||||
.proto = 0,
|
||||
.name = "unknown",
|
||||
.pkt_to_tuple = generic_pkt_to_tuple,
|
||||
.invert_tuple = generic_invert_tuple,
|
||||
.print_tuple = generic_print_tuple,
|
||||
.print_conntrack = generic_print_conntrack,
|
||||
.packet = packet,
|
||||
.new = new,
|
||||
};
|
670
net/netfilter/nf_conntrack_proto_sctp.c
Normal file
670
net/netfilter/nf_conntrack_proto_sctp.c
Normal file
@@ -0,0 +1,670 @@
|
||||
/*
|
||||
* Connection tracking protocol helper module for SCTP.
|
||||
*
|
||||
* SCTP is defined in RFC 2960. References to various sections in this code
|
||||
* are to this RFC.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 17 Oct 2004: Yasuyuki Kozakai @USAGI <yasuyuki.kozakai@toshiba.co.jp>
|
||||
* - enable working with L3 protocol independent connection tracking.
|
||||
*
|
||||
* Derived from net/ipv4/ip_conntrack_sctp.c
|
||||
*/
|
||||
|
||||
/*
|
||||
* Added support for proc manipulation of timeouts.
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/netfilter.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/sctp.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/seq_file.h>
|
||||
|
||||
#include <net/netfilter/nf_conntrack.h>
|
||||
#include <net/netfilter/nf_conntrack_protocol.h>
|
||||
|
||||
#if 0
|
||||
#define DEBUGP(format, ...) printk(format, ## __VA_ARGS__)
|
||||
#else
|
||||
#define DEBUGP(format, args...)
|
||||
#endif
|
||||
|
||||
/* Protects conntrack->proto.sctp */
|
||||
static DEFINE_RWLOCK(sctp_lock);
|
||||
|
||||
/* FIXME: Examine ipfilter's timeouts and conntrack transitions more
|
||||
closely. They're more complex. --RR
|
||||
|
||||
And so for me for SCTP :D -Kiran */
|
||||
|
||||
static const char *sctp_conntrack_names[] = {
|
||||
"NONE",
|
||||
"CLOSED",
|
||||
"COOKIE_WAIT",
|
||||
"COOKIE_ECHOED",
|
||||
"ESTABLISHED",
|
||||
"SHUTDOWN_SENT",
|
||||
"SHUTDOWN_RECD",
|
||||
"SHUTDOWN_ACK_SENT",
|
||||
};
|
||||
|
||||
#define SECS * HZ
|
||||
#define MINS * 60 SECS
|
||||
#define HOURS * 60 MINS
|
||||
#define DAYS * 24 HOURS
|
||||
|
||||
static unsigned long nf_ct_sctp_timeout_closed = 10 SECS;
|
||||
static unsigned long nf_ct_sctp_timeout_cookie_wait = 3 SECS;
|
||||
static unsigned long nf_ct_sctp_timeout_cookie_echoed = 3 SECS;
|
||||
static unsigned long nf_ct_sctp_timeout_established = 5 DAYS;
|
||||
static unsigned long nf_ct_sctp_timeout_shutdown_sent = 300 SECS / 1000;
|
||||
static unsigned long nf_ct_sctp_timeout_shutdown_recd = 300 SECS / 1000;
|
||||
static unsigned long nf_ct_sctp_timeout_shutdown_ack_sent = 3 SECS;
|
||||
|
||||
static unsigned long * sctp_timeouts[]
|
||||
= { NULL, /* SCTP_CONNTRACK_NONE */
|
||||
&nf_ct_sctp_timeout_closed, /* SCTP_CONNTRACK_CLOSED */
|
||||
&nf_ct_sctp_timeout_cookie_wait, /* SCTP_CONNTRACK_COOKIE_WAIT */
|
||||
&nf_ct_sctp_timeout_cookie_echoed, /* SCTP_CONNTRACK_COOKIE_ECHOED */
|
||||
&nf_ct_sctp_timeout_established, /* SCTP_CONNTRACK_ESTABLISHED */
|
||||
&nf_ct_sctp_timeout_shutdown_sent, /* SCTP_CONNTRACK_SHUTDOWN_SENT */
|
||||
&nf_ct_sctp_timeout_shutdown_recd, /* SCTP_CONNTRACK_SHUTDOWN_RECD */
|
||||
&nf_ct_sctp_timeout_shutdown_ack_sent /* SCTP_CONNTRACK_SHUTDOWN_ACK_SENT */
|
||||
};
|
||||
|
||||
#define sNO SCTP_CONNTRACK_NONE
|
||||
#define sCL SCTP_CONNTRACK_CLOSED
|
||||
#define sCW SCTP_CONNTRACK_COOKIE_WAIT
|
||||
#define sCE SCTP_CONNTRACK_COOKIE_ECHOED
|
||||
#define sES SCTP_CONNTRACK_ESTABLISHED
|
||||
#define sSS SCTP_CONNTRACK_SHUTDOWN_SENT
|
||||
#define sSR SCTP_CONNTRACK_SHUTDOWN_RECD
|
||||
#define sSA SCTP_CONNTRACK_SHUTDOWN_ACK_SENT
|
||||
#define sIV SCTP_CONNTRACK_MAX
|
||||
|
||||
/*
|
||||
These are the descriptions of the states:
|
||||
|
||||
NOTE: These state names are tantalizingly similar to the states of an
|
||||
SCTP endpoint. But the interpretation of the states is a little different,
|
||||
considering that these are the states of the connection and not of an end
|
||||
point. Please note the subtleties. -Kiran
|
||||
|
||||
NONE - Nothing so far.
|
||||
COOKIE WAIT - We have seen an INIT chunk in the original direction, or also
|
||||
an INIT_ACK chunk in the reply direction.
|
||||
COOKIE ECHOED - We have seen a COOKIE_ECHO chunk in the original direction.
|
||||
ESTABLISHED - We have seen a COOKIE_ACK in the reply direction.
|
||||
SHUTDOWN_SENT - We have seen a SHUTDOWN chunk in the original direction.
|
||||
SHUTDOWN_RECD - We have seen a SHUTDOWN chunk in the reply directoin.
|
||||
SHUTDOWN_ACK_SENT - We have seen a SHUTDOWN_ACK chunk in the direction opposite
|
||||
to that of the SHUTDOWN chunk.
|
||||
CLOSED - We have seen a SHUTDOWN_COMPLETE chunk in the direction of
|
||||
the SHUTDOWN chunk. Connection is closed.
|
||||
*/
|
||||
|
||||
/* TODO
|
||||
- I have assumed that the first INIT is in the original direction.
|
||||
This messes things when an INIT comes in the reply direction in CLOSED
|
||||
state.
|
||||
- Check the error type in the reply dir before transitioning from
|
||||
cookie echoed to closed.
|
||||
- Sec 5.2.4 of RFC 2960
|
||||
- Multi Homing support.
|
||||
*/
|
||||
|
||||
/* SCTP conntrack state transitions */
|
||||
static enum sctp_conntrack sctp_conntracks[2][9][SCTP_CONNTRACK_MAX] = {
|
||||
{
|
||||
/* ORIGINAL */
|
||||
/* sNO, sCL, sCW, sCE, sES, sSS, sSR, sSA */
|
||||
/* init */ {sCW, sCW, sCW, sCE, sES, sSS, sSR, sSA},
|
||||
/* init_ack */ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sSA},
|
||||
/* abort */ {sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL},
|
||||
/* shutdown */ {sCL, sCL, sCW, sCE, sSS, sSS, sSR, sSA},
|
||||
/* shutdown_ack */ {sSA, sCL, sCW, sCE, sES, sSA, sSA, sSA},
|
||||
/* error */ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sSA},/* Cant have Stale cookie*/
|
||||
/* cookie_echo */ {sCL, sCL, sCE, sCE, sES, sSS, sSR, sSA},/* 5.2.4 - Big TODO */
|
||||
/* cookie_ack */ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sSA},/* Cant come in orig dir */
|
||||
/* shutdown_comp*/ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sCL}
|
||||
},
|
||||
{
|
||||
/* REPLY */
|
||||
/* sNO, sCL, sCW, sCE, sES, sSS, sSR, sSA */
|
||||
/* init */ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sSA},/* INIT in sCL Big TODO */
|
||||
/* init_ack */ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sSA},
|
||||
/* abort */ {sIV, sCL, sCL, sCL, sCL, sCL, sCL, sCL},
|
||||
/* shutdown */ {sIV, sCL, sCW, sCE, sSR, sSS, sSR, sSA},
|
||||
/* shutdown_ack */ {sIV, sCL, sCW, sCE, sES, sSA, sSA, sSA},
|
||||
/* error */ {sIV, sCL, sCW, sCL, sES, sSS, sSR, sSA},
|
||||
/* cookie_echo */ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sSA},/* Cant come in reply dir */
|
||||
/* cookie_ack */ {sIV, sCL, sCW, sES, sES, sSS, sSR, sSA},
|
||||
/* shutdown_comp*/ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sCL}
|
||||
}
|
||||
};
|
||||
|
||||
static int sctp_pkt_to_tuple(const struct sk_buff *skb,
|
||||
unsigned int dataoff,
|
||||
struct nf_conntrack_tuple *tuple)
|
||||
{
|
||||
sctp_sctphdr_t _hdr, *hp;
|
||||
|
||||
DEBUGP(__FUNCTION__);
|
||||
DEBUGP("\n");
|
||||
|
||||
/* Actually only need first 8 bytes. */
|
||||
hp = skb_header_pointer(skb, dataoff, 8, &_hdr);
|
||||
if (hp == NULL)
|
||||
return 0;
|
||||
|
||||
tuple->src.u.sctp.port = hp->source;
|
||||
tuple->dst.u.sctp.port = hp->dest;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int sctp_invert_tuple(struct nf_conntrack_tuple *tuple,
|
||||
const struct nf_conntrack_tuple *orig)
|
||||
{
|
||||
DEBUGP(__FUNCTION__);
|
||||
DEBUGP("\n");
|
||||
|
||||
tuple->src.u.sctp.port = orig->dst.u.sctp.port;
|
||||
tuple->dst.u.sctp.port = orig->src.u.sctp.port;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Print out the per-protocol part of the tuple. */
|
||||
static int sctp_print_tuple(struct seq_file *s,
|
||||
const struct nf_conntrack_tuple *tuple)
|
||||
{
|
||||
DEBUGP(__FUNCTION__);
|
||||
DEBUGP("\n");
|
||||
|
||||
return seq_printf(s, "sport=%hu dport=%hu ",
|
||||
ntohs(tuple->src.u.sctp.port),
|
||||
ntohs(tuple->dst.u.sctp.port));
|
||||
}
|
||||
|
||||
/* Print out the private part of the conntrack. */
|
||||
static int sctp_print_conntrack(struct seq_file *s,
|
||||
const struct nf_conn *conntrack)
|
||||
{
|
||||
enum sctp_conntrack state;
|
||||
|
||||
DEBUGP(__FUNCTION__);
|
||||
DEBUGP("\n");
|
||||
|
||||
read_lock_bh(&sctp_lock);
|
||||
state = conntrack->proto.sctp.state;
|
||||
read_unlock_bh(&sctp_lock);
|
||||
|
||||
return seq_printf(s, "%s ", sctp_conntrack_names[state]);
|
||||
}
|
||||
|
||||
#define for_each_sctp_chunk(skb, sch, _sch, offset, dataoff, count) \
|
||||
for (offset = dataoff + sizeof(sctp_sctphdr_t), count = 0; \
|
||||
offset < skb->len && \
|
||||
(sch = skb_header_pointer(skb, offset, sizeof(_sch), &_sch)); \
|
||||
offset += (htons(sch->length) + 3) & ~3, count++)
|
||||
|
||||
/* Some validity checks to make sure the chunks are fine */
|
||||
static int do_basic_checks(struct nf_conn *conntrack,
|
||||
const struct sk_buff *skb,
|
||||
unsigned int dataoff,
|
||||
char *map)
|
||||
{
|
||||
u_int32_t offset, count;
|
||||
sctp_chunkhdr_t _sch, *sch;
|
||||
int flag;
|
||||
|
||||
DEBUGP(__FUNCTION__);
|
||||
DEBUGP("\n");
|
||||
|
||||
flag = 0;
|
||||
|
||||
for_each_sctp_chunk (skb, sch, _sch, offset, dataoff, count) {
|
||||
DEBUGP("Chunk Num: %d Type: %d\n", count, sch->type);
|
||||
|
||||
if (sch->type == SCTP_CID_INIT
|
||||
|| sch->type == SCTP_CID_INIT_ACK
|
||||
|| sch->type == SCTP_CID_SHUTDOWN_COMPLETE) {
|
||||
flag = 1;
|
||||
}
|
||||
|
||||
/* Cookie Ack/Echo chunks not the first OR
|
||||
Init / Init Ack / Shutdown compl chunks not the only chunks */
|
||||
if ((sch->type == SCTP_CID_COOKIE_ACK
|
||||
|| sch->type == SCTP_CID_COOKIE_ECHO
|
||||
|| flag)
|
||||
&& count !=0 ) {
|
||||
DEBUGP("Basic checks failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (map) {
|
||||
set_bit(sch->type, (void *)map);
|
||||
}
|
||||
}
|
||||
|
||||
DEBUGP("Basic checks passed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int new_state(enum ip_conntrack_dir dir,
|
||||
enum sctp_conntrack cur_state,
|
||||
int chunk_type)
|
||||
{
|
||||
int i;
|
||||
|
||||
DEBUGP(__FUNCTION__);
|
||||
DEBUGP("\n");
|
||||
|
||||
DEBUGP("Chunk type: %d\n", chunk_type);
|
||||
|
||||
switch (chunk_type) {
|
||||
case SCTP_CID_INIT:
|
||||
DEBUGP("SCTP_CID_INIT\n");
|
||||
i = 0; break;
|
||||
case SCTP_CID_INIT_ACK:
|
||||
DEBUGP("SCTP_CID_INIT_ACK\n");
|
||||
i = 1; break;
|
||||
case SCTP_CID_ABORT:
|
||||
DEBUGP("SCTP_CID_ABORT\n");
|
||||
i = 2; break;
|
||||
case SCTP_CID_SHUTDOWN:
|
||||
DEBUGP("SCTP_CID_SHUTDOWN\n");
|
||||
i = 3; break;
|
||||
case SCTP_CID_SHUTDOWN_ACK:
|
||||
DEBUGP("SCTP_CID_SHUTDOWN_ACK\n");
|
||||
i = 4; break;
|
||||
case SCTP_CID_ERROR:
|
||||
DEBUGP("SCTP_CID_ERROR\n");
|
||||
i = 5; break;
|
||||
case SCTP_CID_COOKIE_ECHO:
|
||||
DEBUGP("SCTP_CID_COOKIE_ECHO\n");
|
||||
i = 6; break;
|
||||
case SCTP_CID_COOKIE_ACK:
|
||||
DEBUGP("SCTP_CID_COOKIE_ACK\n");
|
||||
i = 7; break;
|
||||
case SCTP_CID_SHUTDOWN_COMPLETE:
|
||||
DEBUGP("SCTP_CID_SHUTDOWN_COMPLETE\n");
|
||||
i = 8; break;
|
||||
default:
|
||||
/* Other chunks like DATA, SACK, HEARTBEAT and
|
||||
its ACK do not cause a change in state */
|
||||
DEBUGP("Unknown chunk type, Will stay in %s\n",
|
||||
sctp_conntrack_names[cur_state]);
|
||||
return cur_state;
|
||||
}
|
||||
|
||||
DEBUGP("dir: %d cur_state: %s chunk_type: %d new_state: %s\n",
|
||||
dir, sctp_conntrack_names[cur_state], chunk_type,
|
||||
sctp_conntrack_names[sctp_conntracks[dir][i][cur_state]]);
|
||||
|
||||
return sctp_conntracks[dir][i][cur_state];
|
||||
}
|
||||
|
||||
/* Returns verdict for packet, or -1 for invalid. */
|
||||
static int sctp_packet(struct nf_conn *conntrack,
|
||||
const struct sk_buff *skb,
|
||||
unsigned int dataoff,
|
||||
enum ip_conntrack_info ctinfo,
|
||||
int pf,
|
||||
unsigned int hooknum)
|
||||
{
|
||||
enum sctp_conntrack newconntrack, oldsctpstate;
|
||||
sctp_sctphdr_t _sctph, *sh;
|
||||
sctp_chunkhdr_t _sch, *sch;
|
||||
u_int32_t offset, count;
|
||||
char map[256 / sizeof (char)] = {0};
|
||||
|
||||
DEBUGP(__FUNCTION__);
|
||||
DEBUGP("\n");
|
||||
|
||||
sh = skb_header_pointer(skb, dataoff, sizeof(_sctph), &_sctph);
|
||||
if (sh == NULL)
|
||||
return -1;
|
||||
|
||||
if (do_basic_checks(conntrack, skb, dataoff, map) != 0)
|
||||
return -1;
|
||||
|
||||
/* Check the verification tag (Sec 8.5) */
|
||||
if (!test_bit(SCTP_CID_INIT, (void *)map)
|
||||
&& !test_bit(SCTP_CID_SHUTDOWN_COMPLETE, (void *)map)
|
||||
&& !test_bit(SCTP_CID_COOKIE_ECHO, (void *)map)
|
||||
&& !test_bit(SCTP_CID_ABORT, (void *)map)
|
||||
&& !test_bit(SCTP_CID_SHUTDOWN_ACK, (void *)map)
|
||||
&& (sh->vtag != conntrack->proto.sctp.vtag[CTINFO2DIR(ctinfo)])) {
|
||||
DEBUGP("Verification tag check failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
oldsctpstate = newconntrack = SCTP_CONNTRACK_MAX;
|
||||
for_each_sctp_chunk (skb, sch, _sch, offset, dataoff, count) {
|
||||
write_lock_bh(&sctp_lock);
|
||||
|
||||
/* Special cases of Verification tag check (Sec 8.5.1) */
|
||||
if (sch->type == SCTP_CID_INIT) {
|
||||
/* Sec 8.5.1 (A) */
|
||||
if (sh->vtag != 0) {
|
||||
write_unlock_bh(&sctp_lock);
|
||||
return -1;
|
||||
}
|
||||
} else if (sch->type == SCTP_CID_ABORT) {
|
||||
/* Sec 8.5.1 (B) */
|
||||
if (!(sh->vtag == conntrack->proto.sctp.vtag[CTINFO2DIR(ctinfo)])
|
||||
&& !(sh->vtag == conntrack->proto.sctp.vtag
|
||||
[1 - CTINFO2DIR(ctinfo)])) {
|
||||
write_unlock_bh(&sctp_lock);
|
||||
return -1;
|
||||
}
|
||||
} else if (sch->type == SCTP_CID_SHUTDOWN_COMPLETE) {
|
||||
/* Sec 8.5.1 (C) */
|
||||
if (!(sh->vtag == conntrack->proto.sctp.vtag[CTINFO2DIR(ctinfo)])
|
||||
&& !(sh->vtag == conntrack->proto.sctp.vtag
|
||||
[1 - CTINFO2DIR(ctinfo)]
|
||||
&& (sch->flags & 1))) {
|
||||
write_unlock_bh(&sctp_lock);
|
||||
return -1;
|
||||
}
|
||||
} else if (sch->type == SCTP_CID_COOKIE_ECHO) {
|
||||
/* Sec 8.5.1 (D) */
|
||||
if (!(sh->vtag == conntrack->proto.sctp.vtag[CTINFO2DIR(ctinfo)])) {
|
||||
write_unlock_bh(&sctp_lock);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
oldsctpstate = conntrack->proto.sctp.state;
|
||||
newconntrack = new_state(CTINFO2DIR(ctinfo), oldsctpstate, sch->type);
|
||||
|
||||
/* Invalid */
|
||||
if (newconntrack == SCTP_CONNTRACK_MAX) {
|
||||
DEBUGP("nf_conntrack_sctp: Invalid dir=%i ctype=%u conntrack=%u\n",
|
||||
CTINFO2DIR(ctinfo), sch->type, oldsctpstate);
|
||||
write_unlock_bh(&sctp_lock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* If it is an INIT or an INIT ACK note down the vtag */
|
||||
if (sch->type == SCTP_CID_INIT
|
||||
|| sch->type == SCTP_CID_INIT_ACK) {
|
||||
sctp_inithdr_t _inithdr, *ih;
|
||||
|
||||
ih = skb_header_pointer(skb, offset + sizeof(sctp_chunkhdr_t),
|
||||
sizeof(_inithdr), &_inithdr);
|
||||
if (ih == NULL) {
|
||||
write_unlock_bh(&sctp_lock);
|
||||
return -1;
|
||||
}
|
||||
DEBUGP("Setting vtag %x for dir %d\n",
|
||||
ih->init_tag, !CTINFO2DIR(ctinfo));
|
||||
conntrack->proto.sctp.vtag[!CTINFO2DIR(ctinfo)] = ih->init_tag;
|
||||
}
|
||||
|
||||
conntrack->proto.sctp.state = newconntrack;
|
||||
if (oldsctpstate != newconntrack)
|
||||
nf_conntrack_event_cache(IPCT_PROTOINFO, skb);
|
||||
write_unlock_bh(&sctp_lock);
|
||||
}
|
||||
|
||||
nf_ct_refresh_acct(conntrack, ctinfo, skb, *sctp_timeouts[newconntrack]);
|
||||
|
||||
if (oldsctpstate == SCTP_CONNTRACK_COOKIE_ECHOED
|
||||
&& CTINFO2DIR(ctinfo) == IP_CT_DIR_REPLY
|
||||
&& newconntrack == SCTP_CONNTRACK_ESTABLISHED) {
|
||||
DEBUGP("Setting assured bit\n");
|
||||
set_bit(IPS_ASSURED_BIT, &conntrack->status);
|
||||
nf_conntrack_event_cache(IPCT_STATUS, skb);
|
||||
}
|
||||
|
||||
return NF_ACCEPT;
|
||||
}
|
||||
|
||||
/* Called when a new connection for this protocol found. */
|
||||
static int sctp_new(struct nf_conn *conntrack, const struct sk_buff *skb,
|
||||
unsigned int dataoff)
|
||||
{
|
||||
enum sctp_conntrack newconntrack;
|
||||
sctp_sctphdr_t _sctph, *sh;
|
||||
sctp_chunkhdr_t _sch, *sch;
|
||||
u_int32_t offset, count;
|
||||
char map[256 / sizeof (char)] = {0};
|
||||
|
||||
DEBUGP(__FUNCTION__);
|
||||
DEBUGP("\n");
|
||||
|
||||
sh = skb_header_pointer(skb, dataoff, sizeof(_sctph), &_sctph);
|
||||
if (sh == NULL)
|
||||
return 0;
|
||||
|
||||
if (do_basic_checks(conntrack, skb, dataoff, map) != 0)
|
||||
return 0;
|
||||
|
||||
/* If an OOTB packet has any of these chunks discard (Sec 8.4) */
|
||||
if ((test_bit (SCTP_CID_ABORT, (void *)map))
|
||||
|| (test_bit (SCTP_CID_SHUTDOWN_COMPLETE, (void *)map))
|
||||
|| (test_bit (SCTP_CID_COOKIE_ACK, (void *)map))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
newconntrack = SCTP_CONNTRACK_MAX;
|
||||
for_each_sctp_chunk (skb, sch, _sch, offset, dataoff, count) {
|
||||
/* Don't need lock here: this conntrack not in circulation yet */
|
||||
newconntrack = new_state(IP_CT_DIR_ORIGINAL,
|
||||
SCTP_CONNTRACK_NONE, sch->type);
|
||||
|
||||
/* Invalid: delete conntrack */
|
||||
if (newconntrack == SCTP_CONNTRACK_MAX) {
|
||||
DEBUGP("nf_conntrack_sctp: invalid new deleting.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Copy the vtag into the state info */
|
||||
if (sch->type == SCTP_CID_INIT) {
|
||||
if (sh->vtag == 0) {
|
||||
sctp_inithdr_t _inithdr, *ih;
|
||||
|
||||
ih = skb_header_pointer(skb, offset + sizeof(sctp_chunkhdr_t),
|
||||
sizeof(_inithdr), &_inithdr);
|
||||
if (ih == NULL)
|
||||
return 0;
|
||||
|
||||
DEBUGP("Setting vtag %x for new conn\n",
|
||||
ih->init_tag);
|
||||
|
||||
conntrack->proto.sctp.vtag[IP_CT_DIR_REPLY] =
|
||||
ih->init_tag;
|
||||
} else {
|
||||
/* Sec 8.5.1 (A) */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
/* If it is a shutdown ack OOTB packet, we expect a return
|
||||
shutdown complete, otherwise an ABORT Sec 8.4 (5) and (8) */
|
||||
else {
|
||||
DEBUGP("Setting vtag %x for new conn OOTB\n",
|
||||
sh->vtag);
|
||||
conntrack->proto.sctp.vtag[IP_CT_DIR_REPLY] = sh->vtag;
|
||||
}
|
||||
|
||||
conntrack->proto.sctp.state = newconntrack;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct nf_conntrack_protocol nf_conntrack_protocol_sctp4 = {
|
||||
.l3proto = PF_INET,
|
||||
.proto = IPPROTO_SCTP,
|
||||
.name = "sctp",
|
||||
.pkt_to_tuple = sctp_pkt_to_tuple,
|
||||
.invert_tuple = sctp_invert_tuple,
|
||||
.print_tuple = sctp_print_tuple,
|
||||
.print_conntrack = sctp_print_conntrack,
|
||||
.packet = sctp_packet,
|
||||
.new = sctp_new,
|
||||
.destroy = NULL,
|
||||
.me = THIS_MODULE
|
||||
};
|
||||
|
||||
struct nf_conntrack_protocol nf_conntrack_protocol_sctp6 = {
|
||||
.l3proto = PF_INET6,
|
||||
.proto = IPPROTO_SCTP,
|
||||
.name = "sctp",
|
||||
.pkt_to_tuple = sctp_pkt_to_tuple,
|
||||
.invert_tuple = sctp_invert_tuple,
|
||||
.print_tuple = sctp_print_tuple,
|
||||
.print_conntrack = sctp_print_conntrack,
|
||||
.packet = sctp_packet,
|
||||
.new = sctp_new,
|
||||
.destroy = NULL,
|
||||
.me = THIS_MODULE
|
||||
};
|
||||
|
||||
#ifdef CONFIG_SYSCTL
|
||||
static ctl_table nf_ct_sysctl_table[] = {
|
||||
{
|
||||
.ctl_name = NET_NF_CONNTRACK_SCTP_TIMEOUT_CLOSED,
|
||||
.procname = "nf_conntrack_sctp_timeout_closed",
|
||||
.data = &nf_ct_sctp_timeout_closed,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec_jiffies,
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_NF_CONNTRACK_SCTP_TIMEOUT_COOKIE_WAIT,
|
||||
.procname = "nf_conntrack_sctp_timeout_cookie_wait",
|
||||
.data = &nf_ct_sctp_timeout_cookie_wait,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec_jiffies,
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_NF_CONNTRACK_SCTP_TIMEOUT_COOKIE_ECHOED,
|
||||
.procname = "nf_conntrack_sctp_timeout_cookie_echoed",
|
||||
.data = &nf_ct_sctp_timeout_cookie_echoed,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec_jiffies,
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_NF_CONNTRACK_SCTP_TIMEOUT_ESTABLISHED,
|
||||
.procname = "nf_conntrack_sctp_timeout_established",
|
||||
.data = &nf_ct_sctp_timeout_established,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec_jiffies,
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_NF_CONNTRACK_SCTP_TIMEOUT_SHUTDOWN_SENT,
|
||||
.procname = "nf_conntrack_sctp_timeout_shutdown_sent",
|
||||
.data = &nf_ct_sctp_timeout_shutdown_sent,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec_jiffies,
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_NF_CONNTRACK_SCTP_TIMEOUT_SHUTDOWN_RECD,
|
||||
.procname = "nf_conntrack_sctp_timeout_shutdown_recd",
|
||||
.data = &nf_ct_sctp_timeout_shutdown_recd,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec_jiffies,
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_NF_CONNTRACK_SCTP_TIMEOUT_SHUTDOWN_ACK_SENT,
|
||||
.procname = "nf_conntrack_sctp_timeout_shutdown_ack_sent",
|
||||
.data = &nf_ct_sctp_timeout_shutdown_ack_sent,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec_jiffies,
|
||||
},
|
||||
{ .ctl_name = 0 }
|
||||
};
|
||||
|
||||
static ctl_table nf_ct_netfilter_table[] = {
|
||||
{
|
||||
.ctl_name = NET_NETFILTER,
|
||||
.procname = "netfilter",
|
||||
.mode = 0555,
|
||||
.child = nf_ct_sysctl_table,
|
||||
},
|
||||
{ .ctl_name = 0 }
|
||||
};
|
||||
|
||||
static ctl_table nf_ct_net_table[] = {
|
||||
{
|
||||
.ctl_name = CTL_NET,
|
||||
.procname = "net",
|
||||
.mode = 0555,
|
||||
.child = nf_ct_netfilter_table,
|
||||
},
|
||||
{ .ctl_name = 0 }
|
||||
};
|
||||
|
||||
static struct ctl_table_header *nf_ct_sysctl_header;
|
||||
#endif
|
||||
|
||||
int __init init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = nf_conntrack_protocol_register(&nf_conntrack_protocol_sctp4);
|
||||
if (ret) {
|
||||
printk("nf_conntrack_proto_sctp4: protocol register failed\n");
|
||||
goto out;
|
||||
}
|
||||
ret = nf_conntrack_protocol_register(&nf_conntrack_protocol_sctp6);
|
||||
if (ret) {
|
||||
printk("nf_conntrack_proto_sctp6: protocol register failed\n");
|
||||
goto cleanup_sctp4;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SYSCTL
|
||||
nf_ct_sysctl_header = register_sysctl_table(nf_ct_net_table, 0);
|
||||
if (nf_ct_sysctl_header == NULL) {
|
||||
printk("nf_conntrack_proto_sctp: can't register to sysctl.\n");
|
||||
goto cleanup;
|
||||
}
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
|
||||
#ifdef CONFIG_SYSCTL
|
||||
cleanup:
|
||||
nf_conntrack_protocol_unregister(&nf_conntrack_protocol_sctp6);
|
||||
#endif
|
||||
cleanup_sctp4:
|
||||
nf_conntrack_protocol_unregister(&nf_conntrack_protocol_sctp4);
|
||||
out:
|
||||
DEBUGP("SCTP conntrack module loading %s\n",
|
||||
ret ? "failed": "succeeded");
|
||||
return ret;
|
||||
}
|
||||
|
||||
void __exit fini(void)
|
||||
{
|
||||
nf_conntrack_protocol_unregister(&nf_conntrack_protocol_sctp6);
|
||||
nf_conntrack_protocol_unregister(&nf_conntrack_protocol_sctp4);
|
||||
#ifdef CONFIG_SYSCTL
|
||||
unregister_sysctl_table(nf_ct_sysctl_header);
|
||||
#endif
|
||||
DEBUGP("SCTP conntrack module unloaded\n");
|
||||
}
|
||||
|
||||
module_init(init);
|
||||
module_exit(fini);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Kiran Kumar Immidi");
|
||||
MODULE_DESCRIPTION("Netfilter connection tracking protocol helper for SCTP");
|
1162
net/netfilter/nf_conntrack_proto_tcp.c
Normal file
1162
net/netfilter/nf_conntrack_proto_tcp.c
Normal file
File diff suppressed because it is too large
Load Diff
216
net/netfilter/nf_conntrack_proto_udp.c
Normal file
216
net/netfilter/nf_conntrack_proto_udp.c
Normal file
@@ -0,0 +1,216 @@
|
||||
/* (C) 1999-2001 Paul `Rusty' Russell
|
||||
* (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 16 Dec 2003: Yasuyuki Kozakai @USAGI <yasuyuki.kozakai@toshiba.co.jp>
|
||||
* - enable working with Layer 3 protocol independent connection tracking.
|
||||
*
|
||||
* Derived from net/ipv4/netfilter/ip_conntrack_proto_udp.c
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/netfilter.h>
|
||||
#include <linux/udp.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/ipv6.h>
|
||||
#include <net/ip6_checksum.h>
|
||||
#include <net/checksum.h>
|
||||
#include <linux/netfilter.h>
|
||||
#include <linux/netfilter_ipv4.h>
|
||||
#include <linux/netfilter_ipv6.h>
|
||||
#include <net/netfilter/nf_conntrack_protocol.h>
|
||||
|
||||
unsigned long nf_ct_udp_timeout = 30*HZ;
|
||||
unsigned long nf_ct_udp_timeout_stream = 180*HZ;
|
||||
|
||||
static int udp_pkt_to_tuple(const struct sk_buff *skb,
|
||||
unsigned int dataoff,
|
||||
struct nf_conntrack_tuple *tuple)
|
||||
{
|
||||
struct udphdr _hdr, *hp;
|
||||
|
||||
/* Actually only need first 8 bytes. */
|
||||
hp = skb_header_pointer(skb, dataoff, sizeof(_hdr), &_hdr);
|
||||
if (hp == NULL)
|
||||
return 0;
|
||||
|
||||
tuple->src.u.udp.port = hp->source;
|
||||
tuple->dst.u.udp.port = hp->dest;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int udp_invert_tuple(struct nf_conntrack_tuple *tuple,
|
||||
const struct nf_conntrack_tuple *orig)
|
||||
{
|
||||
tuple->src.u.udp.port = orig->dst.u.udp.port;
|
||||
tuple->dst.u.udp.port = orig->src.u.udp.port;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Print out the per-protocol part of the tuple. */
|
||||
static int udp_print_tuple(struct seq_file *s,
|
||||
const struct nf_conntrack_tuple *tuple)
|
||||
{
|
||||
return seq_printf(s, "sport=%hu dport=%hu ",
|
||||
ntohs(tuple->src.u.udp.port),
|
||||
ntohs(tuple->dst.u.udp.port));
|
||||
}
|
||||
|
||||
/* Print out the private part of the conntrack. */
|
||||
static int udp_print_conntrack(struct seq_file *s,
|
||||
const struct nf_conn *conntrack)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Returns verdict for packet, and may modify conntracktype */
|
||||
static int udp_packet(struct nf_conn *conntrack,
|
||||
const struct sk_buff *skb,
|
||||
unsigned int dataoff,
|
||||
enum ip_conntrack_info ctinfo,
|
||||
int pf,
|
||||
unsigned int hooknum)
|
||||
{
|
||||
/* If we've seen traffic both ways, this is some kind of UDP
|
||||
stream. Extend timeout. */
|
||||
if (test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status)) {
|
||||
nf_ct_refresh_acct(conntrack, ctinfo, skb,
|
||||
nf_ct_udp_timeout_stream);
|
||||
/* Also, more likely to be important, and not a probe */
|
||||
if (!test_and_set_bit(IPS_ASSURED_BIT, &conntrack->status))
|
||||
nf_conntrack_event_cache(IPCT_STATUS, skb);
|
||||
} else
|
||||
nf_ct_refresh_acct(conntrack, ctinfo, skb, nf_ct_udp_timeout);
|
||||
|
||||
return NF_ACCEPT;
|
||||
}
|
||||
|
||||
/* Called when a new connection for this protocol found. */
|
||||
static int udp_new(struct nf_conn *conntrack, const struct sk_buff *skb,
|
||||
unsigned int dataoff)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int udp_error(struct sk_buff *skb, unsigned int dataoff,
|
||||
enum ip_conntrack_info *ctinfo,
|
||||
int pf,
|
||||
unsigned int hooknum,
|
||||
int (*csum)(const struct sk_buff *, unsigned int))
|
||||
{
|
||||
unsigned int udplen = skb->len - dataoff;
|
||||
struct udphdr _hdr, *hdr;
|
||||
|
||||
/* Header is too small? */
|
||||
hdr = skb_header_pointer(skb, dataoff, sizeof(_hdr), &_hdr);
|
||||
if (hdr == NULL) {
|
||||
if (LOG_INVALID(IPPROTO_UDP))
|
||||
nf_log_packet(pf, 0, skb, NULL, NULL, NULL,
|
||||
"nf_ct_udp: short packet ");
|
||||
return -NF_ACCEPT;
|
||||
}
|
||||
|
||||
/* Truncated/malformed packets */
|
||||
if (ntohs(hdr->len) > udplen || ntohs(hdr->len) < sizeof(*hdr)) {
|
||||
if (LOG_INVALID(IPPROTO_UDP))
|
||||
nf_log_packet(pf, 0, skb, NULL, NULL, NULL,
|
||||
"nf_ct_udp: truncated/malformed packet ");
|
||||
return -NF_ACCEPT;
|
||||
}
|
||||
|
||||
/* Packet with no checksum */
|
||||
if (!hdr->check)
|
||||
return NF_ACCEPT;
|
||||
|
||||
/* Checksum invalid? Ignore.
|
||||
* We skip checking packets on the outgoing path
|
||||
* because the semantic of CHECKSUM_HW is different there
|
||||
* and moreover root might send raw packets.
|
||||
* FIXME: Source route IP option packets --RR */
|
||||
if (((pf == PF_INET && hooknum == NF_IP_PRE_ROUTING) ||
|
||||
(pf == PF_INET6 && hooknum == NF_IP6_PRE_ROUTING))
|
||||
&& skb->ip_summed != CHECKSUM_UNNECESSARY
|
||||
&& csum(skb, dataoff)) {
|
||||
if (LOG_INVALID(IPPROTO_UDP))
|
||||
nf_log_packet(pf, 0, skb, NULL, NULL, NULL,
|
||||
"nf_ct_udp: bad UDP checksum ");
|
||||
return -NF_ACCEPT;
|
||||
}
|
||||
|
||||
return NF_ACCEPT;
|
||||
}
|
||||
|
||||
static int csum4(const struct sk_buff *skb, unsigned int dataoff)
|
||||
{
|
||||
return csum_tcpudp_magic(skb->nh.iph->saddr, skb->nh.iph->daddr,
|
||||
skb->len - dataoff, IPPROTO_UDP,
|
||||
skb->ip_summed == CHECKSUM_HW ? skb->csum
|
||||
: skb_checksum(skb, dataoff,
|
||||
skb->len - dataoff, 0));
|
||||
}
|
||||
|
||||
static int csum6(const struct sk_buff *skb, unsigned int dataoff)
|
||||
{
|
||||
return csum_ipv6_magic(&skb->nh.ipv6h->saddr, &skb->nh.ipv6h->daddr,
|
||||
skb->len - dataoff, IPPROTO_UDP,
|
||||
skb->ip_summed == CHECKSUM_HW ? skb->csum
|
||||
: skb_checksum(skb, dataoff, skb->len - dataoff,
|
||||
0));
|
||||
}
|
||||
|
||||
static int udp_error4(struct sk_buff *skb,
|
||||
unsigned int dataoff,
|
||||
enum ip_conntrack_info *ctinfo,
|
||||
int pf,
|
||||
unsigned int hooknum)
|
||||
{
|
||||
return udp_error(skb, dataoff, ctinfo, pf, hooknum, csum4);
|
||||
}
|
||||
|
||||
static int udp_error6(struct sk_buff *skb,
|
||||
unsigned int dataoff,
|
||||
enum ip_conntrack_info *ctinfo,
|
||||
int pf,
|
||||
unsigned int hooknum)
|
||||
{
|
||||
return udp_error(skb, dataoff, ctinfo, pf, hooknum, csum6);
|
||||
}
|
||||
|
||||
struct nf_conntrack_protocol nf_conntrack_protocol_udp4 =
|
||||
{
|
||||
.l3proto = PF_INET,
|
||||
.proto = IPPROTO_UDP,
|
||||
.name = "udp",
|
||||
.pkt_to_tuple = udp_pkt_to_tuple,
|
||||
.invert_tuple = udp_invert_tuple,
|
||||
.print_tuple = udp_print_tuple,
|
||||
.print_conntrack = udp_print_conntrack,
|
||||
.packet = udp_packet,
|
||||
.new = udp_new,
|
||||
.error = udp_error4,
|
||||
};
|
||||
|
||||
struct nf_conntrack_protocol nf_conntrack_protocol_udp6 =
|
||||
{
|
||||
.l3proto = PF_INET6,
|
||||
.proto = IPPROTO_UDP,
|
||||
.name = "udp",
|
||||
.pkt_to_tuple = udp_pkt_to_tuple,
|
||||
.invert_tuple = udp_invert_tuple,
|
||||
.print_tuple = udp_print_tuple,
|
||||
.print_conntrack = udp_print_conntrack,
|
||||
.packet = udp_packet,
|
||||
.new = udp_new,
|
||||
.error = udp_error6,
|
||||
};
|
||||
|
||||
EXPORT_SYMBOL(nf_conntrack_protocol_udp4);
|
||||
EXPORT_SYMBOL(nf_conntrack_protocol_udp6);
|
869
net/netfilter/nf_conntrack_standalone.c
Normal file
869
net/netfilter/nf_conntrack_standalone.c
Normal file
@@ -0,0 +1,869 @@
|
||||
/* This file contains all the functions required for the standalone
|
||||
nf_conntrack module.
|
||||
|
||||
These are not required by the compatibility layer.
|
||||
*/
|
||||
|
||||
/* (C) 1999-2001 Paul `Rusty' Russell
|
||||
* (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 16 Dec 2003: Yasuyuki Kozakai @USAGI <yasuyuki.kozakai@toshiba.co.jp>
|
||||
* - generalize L3 protocol dependent part.
|
||||
*
|
||||
* Derived from net/ipv4/netfilter/ip_conntrack_standalone.c
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/netfilter.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/percpu.h>
|
||||
#include <linux/netdevice.h>
|
||||
#ifdef CONFIG_SYSCTL
|
||||
#include <linux/sysctl.h>
|
||||
#endif
|
||||
|
||||
#define ASSERT_READ_LOCK(x)
|
||||
#define ASSERT_WRITE_LOCK(x)
|
||||
|
||||
#include <net/netfilter/nf_conntrack.h>
|
||||
#include <net/netfilter/nf_conntrack_l3proto.h>
|
||||
#include <net/netfilter/nf_conntrack_protocol.h>
|
||||
#include <net/netfilter/nf_conntrack_core.h>
|
||||
#include <net/netfilter/nf_conntrack_helper.h>
|
||||
#include <linux/netfilter_ipv4/listhelp.h>
|
||||
|
||||
#if 0
|
||||
#define DEBUGP printk
|
||||
#else
|
||||
#define DEBUGP(format, args...)
|
||||
#endif
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
extern atomic_t nf_conntrack_count;
|
||||
DECLARE_PER_CPU(struct ip_conntrack_stat, nf_conntrack_stat);
|
||||
|
||||
static int kill_l3proto(struct nf_conn *i, void *data)
|
||||
{
|
||||
return (i->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num ==
|
||||
((struct nf_conntrack_l3proto *)data)->l3proto);
|
||||
}
|
||||
|
||||
static int kill_proto(struct nf_conn *i, void *data)
|
||||
{
|
||||
struct nf_conntrack_protocol *proto;
|
||||
proto = (struct nf_conntrack_protocol *)data;
|
||||
return (i->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum ==
|
||||
proto->proto) &&
|
||||
(i->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num ==
|
||||
proto->l3proto);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
static int
|
||||
print_tuple(struct seq_file *s, const struct nf_conntrack_tuple *tuple,
|
||||
struct nf_conntrack_l3proto *l3proto,
|
||||
struct nf_conntrack_protocol *proto)
|
||||
{
|
||||
return l3proto->print_tuple(s, tuple) || proto->print_tuple(s, tuple);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_NF_CT_ACCT
|
||||
static unsigned int
|
||||
seq_print_counters(struct seq_file *s,
|
||||
const struct ip_conntrack_counter *counter)
|
||||
{
|
||||
return seq_printf(s, "packets=%llu bytes=%llu ",
|
||||
(unsigned long long)counter->packets,
|
||||
(unsigned long long)counter->bytes);
|
||||
}
|
||||
#else
|
||||
#define seq_print_counters(x, y) 0
|
||||
#endif
|
||||
|
||||
struct ct_iter_state {
|
||||
unsigned int bucket;
|
||||
};
|
||||
|
||||
static struct list_head *ct_get_first(struct seq_file *seq)
|
||||
{
|
||||
struct ct_iter_state *st = seq->private;
|
||||
|
||||
for (st->bucket = 0;
|
||||
st->bucket < nf_conntrack_htable_size;
|
||||
st->bucket++) {
|
||||
if (!list_empty(&nf_conntrack_hash[st->bucket]))
|
||||
return nf_conntrack_hash[st->bucket].next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct list_head *ct_get_next(struct seq_file *seq, struct list_head *head)
|
||||
{
|
||||
struct ct_iter_state *st = seq->private;
|
||||
|
||||
head = head->next;
|
||||
while (head == &nf_conntrack_hash[st->bucket]) {
|
||||
if (++st->bucket >= nf_conntrack_htable_size)
|
||||
return NULL;
|
||||
head = nf_conntrack_hash[st->bucket].next;
|
||||
}
|
||||
return head;
|
||||
}
|
||||
|
||||
static struct list_head *ct_get_idx(struct seq_file *seq, loff_t pos)
|
||||
{
|
||||
struct list_head *head = ct_get_first(seq);
|
||||
|
||||
if (head)
|
||||
while (pos && (head = ct_get_next(seq, head)))
|
||||
pos--;
|
||||
return pos ? NULL : head;
|
||||
}
|
||||
|
||||
static void *ct_seq_start(struct seq_file *seq, loff_t *pos)
|
||||
{
|
||||
read_lock_bh(&nf_conntrack_lock);
|
||||
return ct_get_idx(seq, *pos);
|
||||
}
|
||||
|
||||
static void *ct_seq_next(struct seq_file *s, void *v, loff_t *pos)
|
||||
{
|
||||
(*pos)++;
|
||||
return ct_get_next(s, v);
|
||||
}
|
||||
|
||||
static void ct_seq_stop(struct seq_file *s, void *v)
|
||||
{
|
||||
read_unlock_bh(&nf_conntrack_lock);
|
||||
}
|
||||
|
||||
/* return 0 on success, 1 in case of error */
|
||||
static int ct_seq_show(struct seq_file *s, void *v)
|
||||
{
|
||||
const struct nf_conntrack_tuple_hash *hash = v;
|
||||
const struct nf_conn *conntrack = nf_ct_tuplehash_to_ctrack(hash);
|
||||
struct nf_conntrack_l3proto *l3proto;
|
||||
struct nf_conntrack_protocol *proto;
|
||||
|
||||
ASSERT_READ_LOCK(&nf_conntrack_lock);
|
||||
NF_CT_ASSERT(conntrack);
|
||||
|
||||
/* we only want to print DIR_ORIGINAL */
|
||||
if (NF_CT_DIRECTION(hash))
|
||||
return 0;
|
||||
|
||||
l3proto = nf_ct_find_l3proto(conntrack->tuplehash[IP_CT_DIR_ORIGINAL]
|
||||
.tuple.src.l3num);
|
||||
|
||||
NF_CT_ASSERT(l3proto);
|
||||
proto = nf_ct_find_proto(conntrack->tuplehash[IP_CT_DIR_ORIGINAL]
|
||||
.tuple.src.l3num,
|
||||
conntrack->tuplehash[IP_CT_DIR_ORIGINAL]
|
||||
.tuple.dst.protonum);
|
||||
NF_CT_ASSERT(proto);
|
||||
|
||||
if (seq_printf(s, "%-8s %u %-8s %u %ld ",
|
||||
l3proto->name,
|
||||
conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num,
|
||||
proto->name,
|
||||
conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum,
|
||||
timer_pending(&conntrack->timeout)
|
||||
? (long)(conntrack->timeout.expires - jiffies)/HZ : 0) != 0)
|
||||
return -ENOSPC;
|
||||
|
||||
if (l3proto->print_conntrack(s, conntrack))
|
||||
return -ENOSPC;
|
||||
|
||||
if (proto->print_conntrack(s, conntrack))
|
||||
return -ENOSPC;
|
||||
|
||||
if (print_tuple(s, &conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple,
|
||||
l3proto, proto))
|
||||
return -ENOSPC;
|
||||
|
||||
if (seq_print_counters(s, &conntrack->counters[IP_CT_DIR_ORIGINAL]))
|
||||
return -ENOSPC;
|
||||
|
||||
if (!(test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status)))
|
||||
if (seq_printf(s, "[UNREPLIED] "))
|
||||
return -ENOSPC;
|
||||
|
||||
if (print_tuple(s, &conntrack->tuplehash[IP_CT_DIR_REPLY].tuple,
|
||||
l3proto, proto))
|
||||
return -ENOSPC;
|
||||
|
||||
if (seq_print_counters(s, &conntrack->counters[IP_CT_DIR_REPLY]))
|
||||
return -ENOSPC;
|
||||
|
||||
if (test_bit(IPS_ASSURED_BIT, &conntrack->status))
|
||||
if (seq_printf(s, "[ASSURED] "))
|
||||
return -ENOSPC;
|
||||
|
||||
#if defined(CONFIG_NF_CONNTRACK_MARK)
|
||||
if (seq_printf(s, "mark=%u ", conntrack->mark))
|
||||
return -ENOSPC;
|
||||
#endif
|
||||
|
||||
if (seq_printf(s, "use=%u\n", atomic_read(&conntrack->ct_general.use)))
|
||||
return -ENOSPC;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct seq_operations ct_seq_ops = {
|
||||
.start = ct_seq_start,
|
||||
.next = ct_seq_next,
|
||||
.stop = ct_seq_stop,
|
||||
.show = ct_seq_show
|
||||
};
|
||||
|
||||
static int ct_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct seq_file *seq;
|
||||
struct ct_iter_state *st;
|
||||
int ret;
|
||||
|
||||
st = kmalloc(sizeof(struct ct_iter_state), GFP_KERNEL);
|
||||
if (st == NULL)
|
||||
return -ENOMEM;
|
||||
ret = seq_open(file, &ct_seq_ops);
|
||||
if (ret)
|
||||
goto out_free;
|
||||
seq = file->private_data;
|
||||
seq->private = st;
|
||||
memset(st, 0, sizeof(struct ct_iter_state));
|
||||
return ret;
|
||||
out_free:
|
||||
kfree(st);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct file_operations ct_file_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = ct_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = seq_release_private,
|
||||
};
|
||||
|
||||
/* expects */
|
||||
static void *exp_seq_start(struct seq_file *s, loff_t *pos)
|
||||
{
|
||||
struct list_head *e = &nf_conntrack_expect_list;
|
||||
loff_t i;
|
||||
|
||||
/* strange seq_file api calls stop even if we fail,
|
||||
* thus we need to grab lock since stop unlocks */
|
||||
read_lock_bh(&nf_conntrack_lock);
|
||||
|
||||
if (list_empty(e))
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i <= *pos; i++) {
|
||||
e = e->next;
|
||||
if (e == &nf_conntrack_expect_list)
|
||||
return NULL;
|
||||
}
|
||||
return e;
|
||||
}
|
||||
|
||||
static void *exp_seq_next(struct seq_file *s, void *v, loff_t *pos)
|
||||
{
|
||||
struct list_head *e = v;
|
||||
|
||||
++*pos;
|
||||
e = e->next;
|
||||
|
||||
if (e == &nf_conntrack_expect_list)
|
||||
return NULL;
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
static void exp_seq_stop(struct seq_file *s, void *v)
|
||||
{
|
||||
read_unlock_bh(&nf_conntrack_lock);
|
||||
}
|
||||
|
||||
static int exp_seq_show(struct seq_file *s, void *v)
|
||||
{
|
||||
struct nf_conntrack_expect *expect = v;
|
||||
|
||||
if (expect->timeout.function)
|
||||
seq_printf(s, "%ld ", timer_pending(&expect->timeout)
|
||||
? (long)(expect->timeout.expires - jiffies)/HZ : 0);
|
||||
else
|
||||
seq_printf(s, "- ");
|
||||
seq_printf(s, "l3proto = %u proto=%u ",
|
||||
expect->tuple.src.l3num,
|
||||
expect->tuple.dst.protonum);
|
||||
print_tuple(s, &expect->tuple,
|
||||
nf_ct_find_l3proto(expect->tuple.src.l3num),
|
||||
nf_ct_find_proto(expect->tuple.src.l3num,
|
||||
expect->tuple.dst.protonum));
|
||||
return seq_putc(s, '\n');
|
||||
}
|
||||
|
||||
static struct seq_operations exp_seq_ops = {
|
||||
.start = exp_seq_start,
|
||||
.next = exp_seq_next,
|
||||
.stop = exp_seq_stop,
|
||||
.show = exp_seq_show
|
||||
};
|
||||
|
||||
static int exp_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return seq_open(file, &exp_seq_ops);
|
||||
}
|
||||
|
||||
static struct file_operations exp_file_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = exp_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = seq_release
|
||||
};
|
||||
|
||||
static void *ct_cpu_seq_start(struct seq_file *seq, loff_t *pos)
|
||||
{
|
||||
int cpu;
|
||||
|
||||
if (*pos == 0)
|
||||
return SEQ_START_TOKEN;
|
||||
|
||||
for (cpu = *pos-1; cpu < NR_CPUS; ++cpu) {
|
||||
if (!cpu_possible(cpu))
|
||||
continue;
|
||||
*pos = cpu + 1;
|
||||
return &per_cpu(nf_conntrack_stat, cpu);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void *ct_cpu_seq_next(struct seq_file *seq, void *v, loff_t *pos)
|
||||
{
|
||||
int cpu;
|
||||
|
||||
for (cpu = *pos; cpu < NR_CPUS; ++cpu) {
|
||||
if (!cpu_possible(cpu))
|
||||
continue;
|
||||
*pos = cpu + 1;
|
||||
return &per_cpu(nf_conntrack_stat, cpu);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void ct_cpu_seq_stop(struct seq_file *seq, void *v)
|
||||
{
|
||||
}
|
||||
|
||||
static int ct_cpu_seq_show(struct seq_file *seq, void *v)
|
||||
{
|
||||
unsigned int nr_conntracks = atomic_read(&nf_conntrack_count);
|
||||
struct ip_conntrack_stat *st = v;
|
||||
|
||||
if (v == SEQ_START_TOKEN) {
|
||||
seq_printf(seq, "entries searched found new invalid ignore delete delete_list insert insert_failed drop early_drop icmp_error expect_new expect_create expect_delete\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
seq_printf(seq, "%08x %08x %08x %08x %08x %08x %08x %08x "
|
||||
"%08x %08x %08x %08x %08x %08x %08x %08x \n",
|
||||
nr_conntracks,
|
||||
st->searched,
|
||||
st->found,
|
||||
st->new,
|
||||
st->invalid,
|
||||
st->ignore,
|
||||
st->delete,
|
||||
st->delete_list,
|
||||
st->insert,
|
||||
st->insert_failed,
|
||||
st->drop,
|
||||
st->early_drop,
|
||||
st->error,
|
||||
|
||||
st->expect_new,
|
||||
st->expect_create,
|
||||
st->expect_delete
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct seq_operations ct_cpu_seq_ops = {
|
||||
.start = ct_cpu_seq_start,
|
||||
.next = ct_cpu_seq_next,
|
||||
.stop = ct_cpu_seq_stop,
|
||||
.show = ct_cpu_seq_show,
|
||||
};
|
||||
|
||||
static int ct_cpu_seq_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return seq_open(file, &ct_cpu_seq_ops);
|
||||
}
|
||||
|
||||
static struct file_operations ct_cpu_seq_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = ct_cpu_seq_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = seq_release_private,
|
||||
};
|
||||
#endif /* CONFIG_PROC_FS */
|
||||
|
||||
/* Sysctl support */
|
||||
|
||||
#ifdef CONFIG_SYSCTL
|
||||
|
||||
/* From nf_conntrack_core.c */
|
||||
extern int nf_conntrack_max;
|
||||
extern unsigned int nf_conntrack_htable_size;
|
||||
|
||||
/* From nf_conntrack_proto_tcp.c */
|
||||
extern unsigned long nf_ct_tcp_timeout_syn_sent;
|
||||
extern unsigned long nf_ct_tcp_timeout_syn_recv;
|
||||
extern unsigned long nf_ct_tcp_timeout_established;
|
||||
extern unsigned long nf_ct_tcp_timeout_fin_wait;
|
||||
extern unsigned long nf_ct_tcp_timeout_close_wait;
|
||||
extern unsigned long nf_ct_tcp_timeout_last_ack;
|
||||
extern unsigned long nf_ct_tcp_timeout_time_wait;
|
||||
extern unsigned long nf_ct_tcp_timeout_close;
|
||||
extern unsigned long nf_ct_tcp_timeout_max_retrans;
|
||||
extern int nf_ct_tcp_loose;
|
||||
extern int nf_ct_tcp_be_liberal;
|
||||
extern int nf_ct_tcp_max_retrans;
|
||||
|
||||
/* From nf_conntrack_proto_udp.c */
|
||||
extern unsigned long nf_ct_udp_timeout;
|
||||
extern unsigned long nf_ct_udp_timeout_stream;
|
||||
|
||||
/* From nf_conntrack_proto_generic.c */
|
||||
extern unsigned long nf_ct_generic_timeout;
|
||||
|
||||
/* Log invalid packets of a given protocol */
|
||||
static int log_invalid_proto_min = 0;
|
||||
static int log_invalid_proto_max = 255;
|
||||
|
||||
static struct ctl_table_header *nf_ct_sysctl_header;
|
||||
|
||||
static ctl_table nf_ct_sysctl_table[] = {
|
||||
{
|
||||
.ctl_name = NET_NF_CONNTRACK_MAX,
|
||||
.procname = "nf_conntrack_max",
|
||||
.data = &nf_conntrack_max,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec,
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_NF_CONNTRACK_COUNT,
|
||||
.procname = "nf_conntrack_count",
|
||||
.data = &nf_conntrack_count,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0444,
|
||||
.proc_handler = &proc_dointvec,
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_NF_CONNTRACK_BUCKETS,
|
||||
.procname = "nf_conntrack_buckets",
|
||||
.data = &nf_conntrack_htable_size,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.mode = 0444,
|
||||
.proc_handler = &proc_dointvec,
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_NF_CONNTRACK_TCP_TIMEOUT_SYN_SENT,
|
||||
.procname = "nf_conntrack_tcp_timeout_syn_sent",
|
||||
.data = &nf_ct_tcp_timeout_syn_sent,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec_jiffies,
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_NF_CONNTRACK_TCP_TIMEOUT_SYN_RECV,
|
||||
.procname = "nf_conntrack_tcp_timeout_syn_recv",
|
||||
.data = &nf_ct_tcp_timeout_syn_recv,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec_jiffies,
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_NF_CONNTRACK_TCP_TIMEOUT_ESTABLISHED,
|
||||
.procname = "nf_conntrack_tcp_timeout_established",
|
||||
.data = &nf_ct_tcp_timeout_established,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec_jiffies,
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_NF_CONNTRACK_TCP_TIMEOUT_FIN_WAIT,
|
||||
.procname = "nf_conntrack_tcp_timeout_fin_wait",
|
||||
.data = &nf_ct_tcp_timeout_fin_wait,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec_jiffies,
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_NF_CONNTRACK_TCP_TIMEOUT_CLOSE_WAIT,
|
||||
.procname = "nf_conntrack_tcp_timeout_close_wait",
|
||||
.data = &nf_ct_tcp_timeout_close_wait,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec_jiffies,
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_NF_CONNTRACK_TCP_TIMEOUT_LAST_ACK,
|
||||
.procname = "nf_conntrack_tcp_timeout_last_ack",
|
||||
.data = &nf_ct_tcp_timeout_last_ack,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec_jiffies,
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_NF_CONNTRACK_TCP_TIMEOUT_TIME_WAIT,
|
||||
.procname = "nf_conntrack_tcp_timeout_time_wait",
|
||||
.data = &nf_ct_tcp_timeout_time_wait,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec_jiffies,
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_NF_CONNTRACK_TCP_TIMEOUT_CLOSE,
|
||||
.procname = "nf_conntrack_tcp_timeout_close",
|
||||
.data = &nf_ct_tcp_timeout_close,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec_jiffies,
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_NF_CONNTRACK_UDP_TIMEOUT,
|
||||
.procname = "nf_conntrack_udp_timeout",
|
||||
.data = &nf_ct_udp_timeout,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec_jiffies,
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_NF_CONNTRACK_UDP_TIMEOUT_STREAM,
|
||||
.procname = "nf_conntrack_udp_timeout_stream",
|
||||
.data = &nf_ct_udp_timeout_stream,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec_jiffies,
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_NF_CONNTRACK_GENERIC_TIMEOUT,
|
||||
.procname = "nf_conntrack_generic_timeout",
|
||||
.data = &nf_ct_generic_timeout,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec_jiffies,
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_NF_CONNTRACK_LOG_INVALID,
|
||||
.procname = "nf_conntrack_log_invalid",
|
||||
.data = &nf_ct_log_invalid,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec_minmax,
|
||||
.strategy = &sysctl_intvec,
|
||||
.extra1 = &log_invalid_proto_min,
|
||||
.extra2 = &log_invalid_proto_max,
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_NF_CONNTRACK_TCP_TIMEOUT_MAX_RETRANS,
|
||||
.procname = "nf_conntrack_tcp_timeout_max_retrans",
|
||||
.data = &nf_ct_tcp_timeout_max_retrans,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec_jiffies,
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_NF_CONNTRACK_TCP_LOOSE,
|
||||
.procname = "nf_conntrack_tcp_loose",
|
||||
.data = &nf_ct_tcp_loose,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec,
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_NF_CONNTRACK_TCP_BE_LIBERAL,
|
||||
.procname = "nf_conntrack_tcp_be_liberal",
|
||||
.data = &nf_ct_tcp_be_liberal,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec,
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_NF_CONNTRACK_TCP_MAX_RETRANS,
|
||||
.procname = "nf_conntrack_tcp_max_retrans",
|
||||
.data = &nf_ct_tcp_max_retrans,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec,
|
||||
},
|
||||
|
||||
{ .ctl_name = 0 }
|
||||
};
|
||||
|
||||
#define NET_NF_CONNTRACK_MAX 2089
|
||||
|
||||
static ctl_table nf_ct_netfilter_table[] = {
|
||||
{
|
||||
.ctl_name = NET_NETFILTER,
|
||||
.procname = "netfilter",
|
||||
.mode = 0555,
|
||||
.child = nf_ct_sysctl_table,
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_NF_CONNTRACK_MAX,
|
||||
.procname = "nf_conntrack_max",
|
||||
.data = &nf_conntrack_max,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec,
|
||||
},
|
||||
{ .ctl_name = 0 }
|
||||
};
|
||||
|
||||
static ctl_table nf_ct_net_table[] = {
|
||||
{
|
||||
.ctl_name = CTL_NET,
|
||||
.procname = "net",
|
||||
.mode = 0555,
|
||||
.child = nf_ct_netfilter_table,
|
||||
},
|
||||
{ .ctl_name = 0 }
|
||||
};
|
||||
EXPORT_SYMBOL(nf_ct_log_invalid);
|
||||
#endif /* CONFIG_SYSCTL */
|
||||
|
||||
static int init_or_cleanup(int init)
|
||||
{
|
||||
#ifdef CONFIG_PROC_FS
|
||||
struct proc_dir_entry *proc, *proc_exp, *proc_stat;
|
||||
#endif
|
||||
int ret = 0;
|
||||
|
||||
if (!init) goto cleanup;
|
||||
|
||||
ret = nf_conntrack_init();
|
||||
if (ret < 0)
|
||||
goto cleanup_nothing;
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
proc = proc_net_fops_create("nf_conntrack", 0440, &ct_file_ops);
|
||||
if (!proc) goto cleanup_init;
|
||||
|
||||
proc_exp = proc_net_fops_create("nf_conntrack_expect", 0440,
|
||||
&exp_file_ops);
|
||||
if (!proc_exp) goto cleanup_proc;
|
||||
|
||||
proc_stat = create_proc_entry("nf_conntrack", S_IRUGO, proc_net_stat);
|
||||
if (!proc_stat)
|
||||
goto cleanup_proc_exp;
|
||||
|
||||
proc_stat->proc_fops = &ct_cpu_seq_fops;
|
||||
proc_stat->owner = THIS_MODULE;
|
||||
#endif
|
||||
#ifdef CONFIG_SYSCTL
|
||||
nf_ct_sysctl_header = register_sysctl_table(nf_ct_net_table, 0);
|
||||
if (nf_ct_sysctl_header == NULL) {
|
||||
printk("nf_conntrack: can't register to sysctl.\n");
|
||||
ret = -ENOMEM;
|
||||
goto cleanup_proc_stat;
|
||||
}
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
|
||||
cleanup:
|
||||
#ifdef CONFIG_SYSCTL
|
||||
unregister_sysctl_table(nf_ct_sysctl_header);
|
||||
cleanup_proc_stat:
|
||||
#endif
|
||||
#ifdef CONFIG_PROC_FS
|
||||
proc_net_remove("nf_conntrack_stat");
|
||||
cleanup_proc_exp:
|
||||
proc_net_remove("nf_conntrack_expect");
|
||||
cleanup_proc:
|
||||
proc_net_remove("nf_conntrack");
|
||||
cleanup_init:
|
||||
#endif /* CNFIG_PROC_FS */
|
||||
nf_conntrack_cleanup();
|
||||
cleanup_nothing:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int nf_conntrack_l3proto_register(struct nf_conntrack_l3proto *proto)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
write_lock_bh(&nf_conntrack_lock);
|
||||
if (nf_ct_l3protos[proto->l3proto] != &nf_conntrack_generic_l3proto) {
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
nf_ct_l3protos[proto->l3proto] = proto;
|
||||
out:
|
||||
write_unlock_bh(&nf_conntrack_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void nf_conntrack_l3proto_unregister(struct nf_conntrack_l3proto *proto)
|
||||
{
|
||||
write_lock_bh(&nf_conntrack_lock);
|
||||
nf_ct_l3protos[proto->l3proto] = &nf_conntrack_generic_l3proto;
|
||||
write_unlock_bh(&nf_conntrack_lock);
|
||||
|
||||
/* Somebody could be still looking at the proto in bh. */
|
||||
synchronize_net();
|
||||
|
||||
/* Remove all contrack entries for this protocol */
|
||||
nf_ct_iterate_cleanup(kill_l3proto, proto);
|
||||
}
|
||||
|
||||
/* FIXME: Allow NULL functions and sub in pointers to generic for
|
||||
them. --RR */
|
||||
int nf_conntrack_protocol_register(struct nf_conntrack_protocol *proto)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
retry:
|
||||
write_lock_bh(&nf_conntrack_lock);
|
||||
if (nf_ct_protos[proto->l3proto]) {
|
||||
if (nf_ct_protos[proto->l3proto][proto->proto]
|
||||
!= &nf_conntrack_generic_protocol) {
|
||||
ret = -EBUSY;
|
||||
goto out_unlock;
|
||||
}
|
||||
} else {
|
||||
/* l3proto may be loaded latter. */
|
||||
struct nf_conntrack_protocol **proto_array;
|
||||
int i;
|
||||
|
||||
write_unlock_bh(&nf_conntrack_lock);
|
||||
|
||||
proto_array = (struct nf_conntrack_protocol **)
|
||||
kmalloc(MAX_NF_CT_PROTO *
|
||||
sizeof(struct nf_conntrack_protocol *),
|
||||
GFP_KERNEL);
|
||||
if (proto_array == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
for (i = 0; i < MAX_NF_CT_PROTO; i++)
|
||||
proto_array[i] = &nf_conntrack_generic_protocol;
|
||||
|
||||
write_lock_bh(&nf_conntrack_lock);
|
||||
if (nf_ct_protos[proto->l3proto]) {
|
||||
/* bad timing, but no problem */
|
||||
write_unlock_bh(&nf_conntrack_lock);
|
||||
kfree(proto_array);
|
||||
} else {
|
||||
nf_ct_protos[proto->l3proto] = proto_array;
|
||||
write_unlock_bh(&nf_conntrack_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Just once because array is never freed until unloading
|
||||
* nf_conntrack.ko
|
||||
*/
|
||||
goto retry;
|
||||
}
|
||||
|
||||
nf_ct_protos[proto->l3proto][proto->proto] = proto;
|
||||
|
||||
out_unlock:
|
||||
write_unlock_bh(&nf_conntrack_lock);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void nf_conntrack_protocol_unregister(struct nf_conntrack_protocol *proto)
|
||||
{
|
||||
write_lock_bh(&nf_conntrack_lock);
|
||||
nf_ct_protos[proto->l3proto][proto->proto]
|
||||
= &nf_conntrack_generic_protocol;
|
||||
write_unlock_bh(&nf_conntrack_lock);
|
||||
|
||||
/* Somebody could be still looking at the proto in bh. */
|
||||
synchronize_net();
|
||||
|
||||
/* Remove all contrack entries for this protocol */
|
||||
nf_ct_iterate_cleanup(kill_proto, proto);
|
||||
}
|
||||
|
||||
static int __init init(void)
|
||||
{
|
||||
return init_or_cleanup(1);
|
||||
}
|
||||
|
||||
static void __exit fini(void)
|
||||
{
|
||||
init_or_cleanup(0);
|
||||
}
|
||||
|
||||
module_init(init);
|
||||
module_exit(fini);
|
||||
|
||||
/* Some modules need us, but don't depend directly on any symbol.
|
||||
They should call this. */
|
||||
void need_nf_conntrack(void)
|
||||
{
|
||||
}
|
||||
|
||||
#ifdef CONFIG_NF_CONNTRACK_EVENTS
|
||||
EXPORT_SYMBOL_GPL(nf_conntrack_chain);
|
||||
EXPORT_SYMBOL_GPL(nf_conntrack_expect_chain);
|
||||
EXPORT_SYMBOL_GPL(nf_conntrack_register_notifier);
|
||||
EXPORT_SYMBOL_GPL(nf_conntrack_unregister_notifier);
|
||||
EXPORT_SYMBOL_GPL(__nf_ct_event_cache_init);
|
||||
EXPORT_PER_CPU_SYMBOL_GPL(nf_conntrack_ecache);
|
||||
EXPORT_SYMBOL_GPL(nf_ct_deliver_cached_events);
|
||||
#endif
|
||||
EXPORT_SYMBOL(nf_conntrack_l3proto_register);
|
||||
EXPORT_SYMBOL(nf_conntrack_l3proto_unregister);
|
||||
EXPORT_SYMBOL(nf_conntrack_protocol_register);
|
||||
EXPORT_SYMBOL(nf_conntrack_protocol_unregister);
|
||||
EXPORT_SYMBOL(nf_ct_invert_tuplepr);
|
||||
EXPORT_SYMBOL(nf_conntrack_alter_reply);
|
||||
EXPORT_SYMBOL(nf_conntrack_destroyed);
|
||||
EXPORT_SYMBOL(need_nf_conntrack);
|
||||
EXPORT_SYMBOL(nf_conntrack_helper_register);
|
||||
EXPORT_SYMBOL(nf_conntrack_helper_unregister);
|
||||
EXPORT_SYMBOL(nf_ct_iterate_cleanup);
|
||||
EXPORT_SYMBOL(__nf_ct_refresh_acct);
|
||||
EXPORT_SYMBOL(nf_ct_protos);
|
||||
EXPORT_SYMBOL(nf_ct_find_proto);
|
||||
EXPORT_SYMBOL(nf_ct_l3protos);
|
||||
EXPORT_SYMBOL(nf_conntrack_expect_alloc);
|
||||
EXPORT_SYMBOL(nf_conntrack_expect_put);
|
||||
EXPORT_SYMBOL(nf_conntrack_expect_related);
|
||||
EXPORT_SYMBOL(nf_conntrack_unexpect_related);
|
||||
EXPORT_SYMBOL(nf_conntrack_tuple_taken);
|
||||
EXPORT_SYMBOL(nf_conntrack_htable_size);
|
||||
EXPORT_SYMBOL(nf_conntrack_lock);
|
||||
EXPORT_SYMBOL(nf_conntrack_hash);
|
||||
EXPORT_SYMBOL(nf_conntrack_untracked);
|
||||
EXPORT_SYMBOL_GPL(nf_conntrack_find_get);
|
||||
#ifdef CONFIG_IP_NF_NAT_NEEDED
|
||||
EXPORT_SYMBOL(nf_conntrack_tcp_update);
|
||||
#endif
|
||||
EXPORT_SYMBOL(__nf_conntrack_confirm);
|
||||
EXPORT_SYMBOL(nf_ct_get_tuple);
|
||||
EXPORT_SYMBOL(nf_ct_invert_tuple);
|
||||
EXPORT_SYMBOL(nf_conntrack_in);
|
||||
EXPORT_SYMBOL(__nf_conntrack_attach);
|
Reference in New Issue
Block a user