* protocol helpers and general trouble making from userspace.
*
* (C) 2001 by Jay Schulist <jschlst@samba.org>
- * (C) 2002-2006 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2002-2005 by Harald Welte <laforge@gnumonks.org>
* (C) 2003 by Patrick Mchardy <kaber@trash.net>
- * (C) 2005-2006 by Pablo Neira Ayuso <pablo@eurodev.net>
+ * (C) 2005 by Pablo Neira Ayuso <pablo@eurodev.net>
*
* I've reworked this stuff to use attributes instead of conntrack
* structures. 5.44 am. I need more tea. --pablo 05/07/11.
#include <linux/errno.h>
#include <linux/netlink.h>
#include <linux/spinlock.h>
-#include <linux/interrupt.h>
#include <linux/notifier.h>
#include <linux/netfilter.h>
MODULE_LICENSE("GPL");
-static char __initdata version[] = "0.93";
+static char __initdata version[] = "0.92";
#if 0
#define DEBUGP printk
static inline int
ctnetlink_dump_tuples_proto(struct sk_buff *skb,
- const struct nf_conntrack_tuple *tuple,
- struct nf_conntrack_protocol *proto)
+ const struct nf_conntrack_tuple *tuple)
{
+ struct nf_conntrack_protocol *proto;
int ret = 0;
- struct nfattr *nest_parms = NFA_NEST(skb, CTA_TUPLE_PROTO);
NFA_PUT(skb, CTA_PROTO_NUM, sizeof(u_int8_t), &tuple->dst.protonum);
+ /* If no protocol helper is found, this function will return the
+ * generic protocol helper, so proto won't *ever* be NULL */
+ proto = nf_ct_proto_find_get(tuple->src.l3num, tuple->dst.protonum);
if (likely(proto->tuple_to_nfattr))
ret = proto->tuple_to_nfattr(skb, tuple);
- NFA_NEST_END(skb, nest_parms);
+ nf_ct_proto_put(proto);
return ret;
}
static inline int
-ctnetlink_dump_tuples_ip(struct sk_buff *skb,
- const struct nf_conntrack_tuple *tuple,
- struct nf_conntrack_l3proto *l3proto)
+ctnetlink_dump_tuples(struct sk_buff *skb,
+ const struct nf_conntrack_tuple *tuple)
{
+ struct nfattr *nest_parms;
+ struct nf_conntrack_l3proto *l3proto;
int ret = 0;
- struct nfattr *nest_parms = NFA_NEST(skb, CTA_TUPLE_IP);
-
+
+ l3proto = nf_ct_l3proto_find_get(tuple->src.l3num);
+
+ nest_parms = NFA_NEST(skb, CTA_TUPLE_IP);
if (likely(l3proto->tuple_to_nfattr))
ret = l3proto->tuple_to_nfattr(skb, tuple);
-
NFA_NEST_END(skb, nest_parms);
- return ret;
-
-nfattr_failure:
- return -1;
-}
-
-static inline int
-ctnetlink_dump_tuples(struct sk_buff *skb,
- const struct nf_conntrack_tuple *tuple)
-{
- int ret;
- struct nf_conntrack_l3proto *l3proto;
- struct nf_conntrack_protocol *proto;
-
- l3proto = nf_ct_l3proto_find_get(tuple->src.l3num);
- ret = ctnetlink_dump_tuples_ip(skb, tuple, l3proto);
nf_ct_l3proto_put(l3proto);
if (unlikely(ret < 0))
return ret;
- proto = nf_ct_proto_find_get(tuple->src.l3num, tuple->dst.protonum);
- ret = ctnetlink_dump_tuples_proto(skb, tuple, proto);
- nf_ct_proto_put(proto);
+ nest_parms = NFA_NEST(skb, CTA_TUPLE_PROTO);
+ ret = ctnetlink_dump_tuples_proto(skb, tuple);
+ NFA_NEST_END(skb, nest_parms);
return ret;
+
+nfattr_failure:
+ return -1;
}
static inline int
ctnetlink_dump_helpinfo(struct sk_buff *skb, const struct nf_conn *ct)
{
struct nfattr *nest_helper;
- const struct nf_conn_help *help = nfct_help(ct);
- if (!help || !help->helper)
+ if (!ct->helper)
return 0;
nest_helper = NFA_NEST(skb, CTA_HELP);
- NFA_PUT(skb, CTA_HELP_NAME, strlen(help->helper->name), help->helper->name);
+ NFA_PUT(skb, CTA_HELP_NAME, strlen(ct->helper->name), ct->helper->name);
- if (help->helper->to_nfattr)
- help->helper->to_nfattr(skb, ct);
+ if (ct->helper->to_nfattr)
+ ct->helper->to_nfattr(skb, ct);
NFA_NEST_END(skb, nest_helper);
group = NFNLGRP_CONNTRACK_UPDATE;
} else
return NOTIFY_DONE;
-
- if (!nfnetlink_has_listeners(group))
- return NOTIFY_DONE;
-
+
+ /* FIXME: Check if there are any listeners before, don't hurt performance */
+
skb = alloc_skb(NLMSG_GOODSIZE, GFP_ATOMIC);
if (!skb)
return NOTIFY_DONE;
static int ctnetlink_done(struct netlink_callback *cb)
{
- if (cb->args[1])
- nf_ct_put((struct nf_conn *)cb->args[1]);
DEBUGP("entered %s\n", __FUNCTION__);
return 0;
}
static int
ctnetlink_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
{
- struct nf_conn *ct, *last;
+ struct nf_conn *ct = NULL;
struct nf_conntrack_tuple_hash *h;
struct list_head *i;
+ u_int32_t *id = (u_int32_t *) &cb->args[1];
struct nfgenmsg *nfmsg = NLMSG_DATA(cb->nlh);
u_int8_t l3proto = nfmsg->nfgen_family;
cb->args[0], *id);
read_lock_bh(&nf_conntrack_lock);
- last = (struct nf_conn *)cb->args[1];
- for (; cb->args[0] < nf_conntrack_htable_size; cb->args[0]++) {
-restart:
+ for (; cb->args[0] < nf_conntrack_htable_size; cb->args[0]++, *id = 0) {
list_for_each_prev(i, &nf_conntrack_hash[cb->args[0]]) {
h = (struct nf_conntrack_tuple_hash *) i;
if (DIRECTION(h) != IP_CT_DIR_ORIGINAL)
* then dump everything. */
if (l3proto && L3PROTO(ct) != l3proto)
continue;
- if (cb->args[1]) {
- if (ct != last)
- continue;
- cb->args[1] = 0;
- }
+ if (ct->id <= *id)
+ continue;
if (ctnetlink_fill_info(skb, NETLINK_CB(cb->skb).pid,
cb->nlh->nlmsg_seq,
IPCTNL_MSG_CT_NEW,
- 1, ct) < 0) {
- nf_conntrack_get(&ct->ct_general);
- cb->args[1] = (unsigned long)ct;
+ 1, ct) < 0)
goto out;
- }
- }
- if (cb->args[1]) {
- cb->args[1] = 0;
- goto restart;
+ *id = ct->id;
}
}
-out:
+out:
read_unlock_bh(&nf_conntrack_lock);
- if (last)
- nf_ct_put(last);
DEBUGP("leaving, last bucket=%lu id=%u\n", cb->args[0], *id);
+
return skb->len;
}
};
static inline int
-ctnetlink_parse_nat(struct nfattr *nat,
+ctnetlink_parse_nat(struct nfattr *cda[],
const struct nf_conn *ct, struct ip_nat_range *range)
{
struct nfattr *tb[CTA_NAT_MAX];
memset(range, 0, sizeof(*range));
- nfattr_parse_nested(tb, CTA_NAT_MAX, nat);
+ nfattr_parse_nested(tb, CTA_NAT_MAX, cda[CTA_NAT-1]);
if (nfattr_bad_size(tb, CTA_NAT_MAX, cta_min_nat))
return -EINVAL;
/* ASSURED bit can only be set */
return -EINVAL;
- if (cda[CTA_NAT_SRC-1] || cda[CTA_NAT_DST-1]) {
+ if (cda[CTA_NAT-1]) {
#ifndef CONFIG_IP_NF_NAT_NEEDED
return -EINVAL;
#else
+ unsigned int hooknum;
struct ip_nat_range range;
- if (cda[CTA_NAT_DST-1]) {
- if (ctnetlink_parse_nat(cda[CTA_NAT_DST-1], ct,
- &range) < 0)
- return -EINVAL;
- if (ip_nat_initialized(ct,
- HOOK2MANIP(NF_IP_PRE_ROUTING)))
- return -EEXIST;
- ip_nat_setup_info(ct, &range, hooknum);
- }
- if (cda[CTA_NAT_SRC-1]) {
- if (ctnetlink_parse_nat(cda[CTA_NAT_SRC-1], ct,
- &range) < 0)
- return -EINVAL;
- if (ip_nat_initialized(ct,
- HOOK2MANIP(NF_IP_POST_ROUTING)))
- return -EEXIST;
- ip_nat_setup_info(ct, &range, hooknum);
- }
+ if (ctnetlink_parse_nat(cda, ct, &range) < 0)
+ return -EINVAL;
+
+ DEBUGP("NAT: %u.%u.%u.%u-%u.%u.%u.%u:%u-%u\n",
+ NIPQUAD(range.min_ip), NIPQUAD(range.max_ip),
+ htons(range.min.all), htons(range.max.all));
+
+ /* This is tricky but it works. ip_nat_setup_info needs the
+ * hook number as parameter, so let's do the correct
+ * conversion and run away */
+ if (status & IPS_SRC_NAT_DONE)
+ hooknum = NF_IP_POST_ROUTING; /* IP_NAT_MANIP_SRC */
+ else if (status & IPS_DST_NAT_DONE)
+ hooknum = NF_IP_PRE_ROUTING; /* IP_NAT_MANIP_DST */
+ else
+ return -EINVAL; /* Missing NAT flags */
+
+ DEBUGP("NAT status: %lu\n",
+ status & (IPS_NAT_MASK | IPS_NAT_DONE_MASK));
+
+ if (ip_nat_initialized(ct, HOOK2MANIP(hooknum)))
+ return -EEXIST;
+ ip_nat_setup_info(ct, &range, hooknum);
+
+ DEBUGP("NAT status after setup_info: %lu\n",
+ ct->status & (IPS_NAT_MASK | IPS_NAT_DONE_MASK));
#endif
}
ctnetlink_change_helper(struct nf_conn *ct, struct nfattr *cda[])
{
struct nf_conntrack_helper *helper;
- struct nf_conn_help *help = nfct_help(ct);
char *helpname;
int err;
DEBUGP("entered %s\n", __FUNCTION__);
- if (!help) {
- /* FIXME: we need to reallocate and rehash */
- return -EBUSY;
- }
-
/* don't change helper of sibling connections */
if (ct->master)
return -EINVAL;
return -EINVAL;
}
- if (help->helper) {
+ if (ct->helper) {
if (!helper) {
/* we had a helper before ... */
nf_ct_remove_expectations(ct);
- help->helper = NULL;
+ ct->helper = NULL;
} else {
/* need to zero data of old helper */
- memset(&help->help, 0, sizeof(help->help));
+ memset(&ct->help, 0, sizeof(ct->help));
}
}
- help->helper = helper;
+ ct->helper = helper;
return 0;
}
return err;
}
-#if defined(CONFIG_NF_CONNTRACK_MARK)
+#if defined(CONFIG_IP_NF_CONNTRACK_MARK)
if (cda[CTA_MARK-1])
ct->mark = ntohl(*(u_int32_t *)NFA_DATA(cda[CTA_MARK-1]));
#endif
return err;
}
-#if defined(CONFIG_NF_CONNTRACK_MARK)
+#if defined(CONFIG_IP_NF_CONNTRACK_MARK)
if (cda[CTA_MARK-1])
ct->mark = ntohl(*(u_int32_t *)NFA_DATA(cda[CTA_MARK-1]));
#endif
+ ct->helper = nf_ct_helper_find_get(rtuple);
+
add_timer(&ct->timeout);
nf_conntrack_hash_insert(ct);
+ if (ct->helper)
+ nf_ct_helper_put(ct->helper);
+
DEBUGP("conntrack with id %u inserted\n", ct->id);
return 0;
/* implicit 'else' */
/* we only allow nat config for new conntracks */
- if (cda[CTA_NAT_SRC-1] || cda[CTA_NAT_DST-1]) {
+ if (cda[CTA_NAT-1]) {
err = -EINVAL;
goto out_unlock;
}
return -1;
}
-static inline int
-ctnetlink_exp_dump_mask(struct sk_buff *skb,
- const struct nf_conntrack_tuple *tuple,
- const struct nf_conntrack_tuple *mask)
-{
- int ret;
- struct nf_conntrack_l3proto *l3proto;
- struct nf_conntrack_protocol *proto;
- struct nfattr *nest_parms = NFA_NEST(skb, CTA_EXPECT_MASK);
-
- l3proto = nf_ct_l3proto_find_get(tuple->src.l3num);
- ret = ctnetlink_dump_tuples_ip(skb, mask, l3proto);
- nf_ct_l3proto_put(l3proto);
-
- if (unlikely(ret < 0))
- goto nfattr_failure;
-
- proto = nf_ct_proto_find_get(tuple->src.l3num, tuple->dst.protonum);
- ret = ctnetlink_dump_tuples_proto(skb, mask, proto);
- nf_ct_proto_put(proto);
- if (unlikely(ret < 0))
- goto nfattr_failure;
-
- NFA_NEST_END(skb, nest_parms);
-
- return 0;
-
-nfattr_failure:
- return -1;
-}
-
static inline int
ctnetlink_exp_dump_expect(struct sk_buff *skb,
const struct nf_conntrack_expect *exp)
if (ctnetlink_exp_dump_tuple(skb, &exp->tuple, CTA_EXPECT_TUPLE) < 0)
goto nfattr_failure;
- if (ctnetlink_exp_dump_mask(skb, &exp->tuple, &exp->mask) < 0)
+ if (ctnetlink_exp_dump_tuple(skb, &exp->mask, CTA_EXPECT_MASK) < 0)
goto nfattr_failure;
if (ctnetlink_exp_dump_tuple(skb,
&master->tuplehash[IP_CT_DIR_ORIGINAL].tuple,
}
list_for_each_entry_safe(exp, tmp, &nf_conntrack_expect_list,
list) {
- struct nf_conn_help *m_help = nfct_help(exp->master);
- if (m_help->helper == h
+ if (exp->master->helper == h
&& del_timer(&exp->timeout)) {
nf_ct_unlink_expect(exp);
nf_conntrack_expect_put(exp);
struct nf_conntrack_tuple_hash *h = NULL;
struct nf_conntrack_expect *exp;
struct nf_conn *ct;
- struct nf_conn_help *help;
int err = 0;
DEBUGP("entered %s\n", __FUNCTION__);
if (!h)
return -ENOENT;
ct = nf_ct_tuplehash_to_ctrack(h);
- help = nfct_help(ct);
- if (!help || !help->helper) {
+ if (!ct->helper) {
/* such conntrack hasn't got any helper, abort */
err = -EINVAL;
goto out;