Skip to content

Commit

Permalink
[NETFILTER]: nf_conntrack: introduce expectation classes and policies
Browse files Browse the repository at this point in the history
Introduce expectation classes and policies. An expectation class
is used to distinguish different types of expectations by the
same helper (for example audio/video/t.120). The expectation
policy is used to hold the maximum number of expectations and
the initial timeout for each class.

The individual classes are isolated from each other, which means
that for example an audio expectation will only evict other audio
expectations.

Signed-off-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Patrick McHardy authored and David S. Miller committed Mar 26, 2008
1 parent 359b9ab commit 6002f26
Show file tree
Hide file tree
Showing 15 changed files with 166 additions and 77 deletions.
5 changes: 4 additions & 1 deletion include/net/netfilter/nf_conntrack.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ do { \

struct nf_conntrack_helper;

/* Must be kept in sync with the classes defined by helpers */
#define NF_CT_MAX_EXPECT_CLASSES 1

/* nf_conn feature for connections that have a helper */
struct nf_conn_help {
/* Helper. if any */
Expand All @@ -85,7 +88,7 @@ struct nf_conn_help {
struct hlist_head expectations;

/* Current number of expected connections */
unsigned int expecting;
u8 expecting[NF_CT_MAX_EXPECT_CLASSES];
};


Expand Down
13 changes: 12 additions & 1 deletion include/net/netfilter/nf_conntrack_expect.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ struct nf_conntrack_expect
/* Flags */
unsigned int flags;

/* Expectation class */
unsigned int class;

#ifdef CONFIG_NF_NAT_NEEDED
__be32 saved_ip;
/* This is the original per-proto part, used to map the
Expand All @@ -53,6 +56,14 @@ struct nf_conntrack_expect
struct rcu_head rcu;
};

struct nf_conntrack_expect_policy
{
unsigned int max_expected;
unsigned int timeout;
};

#define NF_CT_EXPECT_CLASS_DEFAULT 0

#define NF_CT_EXPECT_PERMANENT 0x1
#define NF_CT_EXPECT_INACTIVE 0x2

Expand All @@ -75,7 +86,7 @@ void nf_ct_unexpect_related(struct nf_conntrack_expect *exp);
/* Allocate space for an expectation: this is mandatory before calling
nf_ct_expect_related. You will have to call put afterwards. */
struct nf_conntrack_expect *nf_ct_expect_alloc(struct nf_conn *me);
void nf_ct_expect_init(struct nf_conntrack_expect *, int,
void nf_ct_expect_init(struct nf_conntrack_expect *, unsigned int, int,
const union nf_inet_addr *,
const union nf_inet_addr *,
u_int8_t, const __be16 *, const __be16 *);
Expand Down
5 changes: 2 additions & 3 deletions include/net/netfilter/nf_conntrack_helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,7 @@ struct nf_conntrack_helper

const char *name; /* name of the module */
struct module *me; /* pointer to self */
unsigned int max_expected; /* Maximum number of concurrent
* expected connections */
unsigned int timeout; /* timeout for expecteds */
const struct nf_conntrack_expect_policy *expect_policy;

/* Tuple of things we will help (compared against server response) */
struct nf_conntrack_tuple tuple;
Expand All @@ -37,6 +35,7 @@ struct nf_conntrack_helper
void (*destroy)(struct nf_conn *ct);

int (*to_nlattr)(struct sk_buff *skb, const struct nf_conn *ct);
unsigned int expect_class_max;
};

extern struct nf_conntrack_helper *
Expand Down
12 changes: 8 additions & 4 deletions net/ipv4/netfilter/nf_nat_snmp_basic.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
#include <net/udp.h>

#include <net/netfilter/nf_nat.h>
#include <net/netfilter/nf_conntrack_expect.h>
#include <net/netfilter/nf_conntrack_helper.h>
#include <net/netfilter/nf_nat_helper.h>

Expand Down Expand Up @@ -1267,22 +1268,25 @@ static int help(struct sk_buff *skb, unsigned int protoff,
return ret;
}

static const struct nf_conntrack_expect_policy snmp_exp_policy = {
.max_expected = 0,
.timeout = 180,
};

static struct nf_conntrack_helper snmp_helper __read_mostly = {
.max_expected = 0,
.timeout = 180,
.me = THIS_MODULE,
.help = help,
.expect_policy = &snmp_exp_policy,
.name = "snmp",
.tuple.src.l3num = AF_INET,
.tuple.src.u.udp.port = __constant_htons(SNMP_PORT),
.tuple.dst.protonum = IPPROTO_UDP,
};

static struct nf_conntrack_helper snmp_trap_helper __read_mostly = {
.max_expected = 0,
.timeout = 180,
.me = THIS_MODULE,
.help = help,
.expect_policy = &snmp_exp_policy,
.name = "snmp_trap",
.tuple.src.l3num = AF_INET,
.tuple.src.u.udp.port = __constant_htons(SNMP_TRAP_PORT),
Expand Down
14 changes: 9 additions & 5 deletions net/netfilter/nf_conntrack_amanda.c
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,8 @@ static int amanda_help(struct sk_buff *skb,
goto out;
}
tuple = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
nf_ct_expect_init(exp, family, &tuple->src.u3, &tuple->dst.u3,
nf_ct_expect_init(exp, NF_CT_EXPECT_CLASS_DEFAULT, family,
&tuple->src.u3, &tuple->dst.u3,
IPPROTO_TCP, NULL, &port);

nf_nat_amanda = rcu_dereference(nf_nat_amanda_hook);
Expand All @@ -164,26 +165,29 @@ static int amanda_help(struct sk_buff *skb,
return ret;
}

static const struct nf_conntrack_expect_policy amanda_exp_policy = {
.max_expected = 3,
.timeout = 180,
};

static struct nf_conntrack_helper amanda_helper[2] __read_mostly = {
{
.name = "amanda",
.max_expected = 3,
.timeout = 180,
.me = THIS_MODULE,
.help = amanda_help,
.tuple.src.l3num = AF_INET,
.tuple.src.u.udp.port = __constant_htons(10080),
.tuple.dst.protonum = IPPROTO_UDP,
.expect_policy = &amanda_exp_policy,
},
{
.name = "amanda",
.max_expected = 3,
.timeout = 180,
.me = THIS_MODULE,
.help = amanda_help,
.tuple.src.l3num = AF_INET6,
.tuple.src.u.udp.port = __constant_htons(10080),
.tuple.dst.protonum = IPPROTO_UDP,
.expect_policy = &amanda_exp_policy,
},
};

Expand Down
50 changes: 33 additions & 17 deletions net/netfilter/nf_conntrack_expect.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ void nf_ct_unlink_expect(struct nf_conntrack_expect *exp)
nf_ct_expect_count--;

hlist_del(&exp->lnode);
master_help->expecting--;
master_help->expecting[exp->class]--;
nf_ct_expect_put(exp);

NF_CT_STAT_INC(expect_delete);
Expand Down Expand Up @@ -171,7 +171,7 @@ void nf_ct_remove_expectations(struct nf_conn *ct)
struct hlist_node *n, *next;

/* Optimization: most connection never expect any others. */
if (!help || help->expecting == 0)
if (!help)
return;

hlist_for_each_entry_safe(exp, n, next, &help->expectations, lnode) {
Expand Down Expand Up @@ -205,7 +205,7 @@ static inline int expect_clash(const struct nf_conntrack_expect *a,
static inline int expect_matches(const struct nf_conntrack_expect *a,
const struct nf_conntrack_expect *b)
{
return a->master == b->master
return a->master == b->master && a->class == b->class
&& nf_ct_tuple_equal(&a->tuple, &b->tuple)
&& nf_ct_tuple_mask_equal(&a->mask, &b->mask);
}
Expand Down Expand Up @@ -240,7 +240,8 @@ struct nf_conntrack_expect *nf_ct_expect_alloc(struct nf_conn *me)
}
EXPORT_SYMBOL_GPL(nf_ct_expect_alloc);

void nf_ct_expect_init(struct nf_conntrack_expect *exp, int family,
void nf_ct_expect_init(struct nf_conntrack_expect *exp, unsigned int class,
int family,
const union nf_inet_addr *saddr,
const union nf_inet_addr *daddr,
u_int8_t proto, const __be16 *src, const __be16 *dst)
Expand All @@ -253,6 +254,7 @@ void nf_ct_expect_init(struct nf_conntrack_expect *exp, int family,
len = 16;

exp->flags = 0;
exp->class = class;
exp->expectfn = NULL;
exp->helper = NULL;
exp->tuple.src.l3num = family;
Expand Down Expand Up @@ -309,55 +311,63 @@ EXPORT_SYMBOL_GPL(nf_ct_expect_put);
static void nf_ct_expect_insert(struct nf_conntrack_expect *exp)
{
struct nf_conn_help *master_help = nfct_help(exp->master);
const struct nf_conntrack_expect_policy *p;
unsigned int h = nf_ct_expect_dst_hash(&exp->tuple);

atomic_inc(&exp->use);

hlist_add_head(&exp->lnode, &master_help->expectations);
master_help->expecting++;
master_help->expecting[exp->class]++;

hlist_add_head_rcu(&exp->hnode, &nf_ct_expect_hash[h]);
nf_ct_expect_count++;

setup_timer(&exp->timeout, nf_ct_expectation_timed_out,
(unsigned long)exp);
exp->timeout.expires = jiffies + master_help->helper->timeout * HZ;
p = &master_help->helper->expect_policy[exp->class];
exp->timeout.expires = jiffies + p->timeout * HZ;
add_timer(&exp->timeout);

atomic_inc(&exp->use);
NF_CT_STAT_INC(expect_create);
}

/* Race with expectations being used means we could have none to find; OK. */
static void evict_oldest_expect(struct nf_conn *master)
static void evict_oldest_expect(struct nf_conn *master,
struct nf_conntrack_expect *new)
{
struct nf_conn_help *master_help = nfct_help(master);
struct nf_conntrack_expect *exp = NULL;
struct nf_conntrack_expect *exp, *last = NULL;
struct hlist_node *n;

hlist_for_each_entry(exp, n, &master_help->expectations, lnode)
; /* nothing */
hlist_for_each_entry(exp, n, &master_help->expectations, lnode) {
if (exp->class == new->class)
last = exp;
}

if (exp && del_timer(&exp->timeout)) {
nf_ct_unlink_expect(exp);
nf_ct_expect_put(exp);
if (last && del_timer(&last->timeout)) {
nf_ct_unlink_expect(last);
nf_ct_expect_put(last);
}
}

static inline int refresh_timer(struct nf_conntrack_expect *i)
{
struct nf_conn_help *master_help = nfct_help(i->master);
const struct nf_conntrack_expect_policy *p;

if (!del_timer(&i->timeout))
return 0;

i->timeout.expires = jiffies + master_help->helper->timeout*HZ;
p = &master_help->helper->expect_policy[i->class];
i->timeout.expires = jiffies + p->timeout * HZ;
add_timer(&i->timeout);
return 1;
}

int nf_ct_expect_related(struct nf_conntrack_expect *expect)
{
const struct nf_conntrack_expect_policy *p;
struct nf_conntrack_expect *i;
struct nf_conn *master = expect->master;
struct nf_conn_help *master_help = nfct_help(master);
Expand Down Expand Up @@ -386,9 +396,15 @@ int nf_ct_expect_related(struct nf_conntrack_expect *expect)
}
}
/* Will be over limit? */
if (master_help->helper->max_expected &&
master_help->expecting >= master_help->helper->max_expected)
evict_oldest_expect(master);
p = &master_help->helper->expect_policy[expect->class];
if (p->max_expected &&
master_help->expecting[expect->class] >= p->max_expected) {
evict_oldest_expect(master, expect);
if (master_help->expecting[expect->class] >= p->max_expected) {
ret = -EMFILE;
goto out;
}
}

if (nf_ct_expect_count >= nf_ct_expect_max) {
if (net_ratelimit())
Expand Down
10 changes: 7 additions & 3 deletions net/netfilter/nf_conntrack_ftp.c
Original file line number Diff line number Diff line change
Expand Up @@ -483,7 +483,7 @@ static int help(struct sk_buff *skb,
daddr = &cmd.u3;
}

nf_ct_expect_init(exp, cmd.l3num,
nf_ct_expect_init(exp, NF_CT_EXPECT_CLASS_DEFAULT, cmd.l3num,
&ct->tuplehash[!dir].tuple.src.u3, daddr,
IPPROTO_TCP, NULL, &cmd.u.tcp.port);

Expand Down Expand Up @@ -517,6 +517,11 @@ static int help(struct sk_buff *skb,
static struct nf_conntrack_helper ftp[MAX_PORTS][2] __read_mostly;
static char ftp_names[MAX_PORTS][2][sizeof("ftp-65535")] __read_mostly;

static const struct nf_conntrack_expect_policy ftp_exp_policy = {
.max_expected = 1,
.timeout = 5 * 60,
};

/* don't make this __exit, since it's called from __init ! */
static void nf_conntrack_ftp_fini(void)
{
Expand Down Expand Up @@ -556,8 +561,7 @@ static int __init nf_conntrack_ftp_init(void)
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].max_expected = 1;
ftp[i][j].timeout = 5 * 60; /* 5 Minutes */
ftp[i][j].expect_policy = &ftp_exp_policy;
ftp[i][j].me = THIS_MODULE;
ftp[i][j].help = help;
tmpname = &ftp_names[i][j][0];
Expand Down
Loading

0 comments on commit 6002f26

Please sign in to comment.