* (C) 2001 by Jay Schulist <jschlst@samba.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.
static inline int
ctnetlink_dump_tuples_proto(struct sk_buff *skb,
- const struct ip_conntrack_tuple *tuple,
- struct ip_conntrack_protocol *proto)
+ const struct ip_conntrack_tuple *tuple)
{
+ struct ip_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 = ip_conntrack_proto_find_get(tuple->dst.protonum);
if (likely(proto->tuple_to_nfattr))
ret = proto->tuple_to_nfattr(skb, tuple);
- NFA_NEST_END(skb, nest_parms);
+ ip_conntrack_proto_put(proto);
return ret;
}
static inline int
-ctnetlink_dump_tuples_ip(struct sk_buff *skb,
- const struct ip_conntrack_tuple *tuple)
+ctnetlink_dump_tuples(struct sk_buff *skb,
+ const struct ip_conntrack_tuple *tuple)
{
- struct nfattr *nest_parms = NFA_NEST(skb, CTA_TUPLE_IP);
+ struct nfattr *nest_parms;
+ int ret;
+ nest_parms = NFA_NEST(skb, CTA_TUPLE_IP);
NFA_PUT(skb, CTA_IP_V4_SRC, sizeof(u_int32_t), &tuple->src.ip);
NFA_PUT(skb, CTA_IP_V4_DST, sizeof(u_int32_t), &tuple->dst.ip);
+ NFA_NEST_END(skb, nest_parms);
+ nest_parms = NFA_NEST(skb, CTA_TUPLE_PROTO);
+ ret = ctnetlink_dump_tuples_proto(skb, tuple);
NFA_NEST_END(skb, nest_parms);
- return 0;
+ return ret;
nfattr_failure:
return -1;
}
-static inline int
-ctnetlink_dump_tuples(struct sk_buff *skb,
- const struct ip_conntrack_tuple *tuple)
-{
- int ret;
- struct ip_conntrack_protocol *proto;
-
- ret = ctnetlink_dump_tuples_ip(skb, tuple);
- if (unlikely(ret < 0))
- return ret;
-
- proto = ip_conntrack_proto_find_get(tuple->dst.protonum);
- ret = ctnetlink_dump_tuples_proto(skb, tuple, proto);
- ip_conntrack_proto_put(proto);
-
- return ret;
-}
-
static inline int
ctnetlink_dump_status(struct sk_buff *skb, const struct ip_conntrack *ct)
{
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)
{
DEBUGP("entered %s\n", __FUNCTION__);
- if (cb->args[1])
- ip_conntrack_put((struct ip_conntrack *)cb->args[1]);
return 0;
}
static int
ctnetlink_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
{
- struct ip_conntrack *ct, *last;
+ struct ip_conntrack *ct = NULL;
struct ip_conntrack_tuple_hash *h;
struct list_head *i;
+ u_int32_t *id = (u_int32_t *) &cb->args[1];
DEBUGP("entered %s, last bucket=%lu id=%u\n", __FUNCTION__,
cb->args[0], *id);
read_lock_bh(&ip_conntrack_lock);
- last = (struct ip_conntrack *)cb->args[1];
- for (; cb->args[0] < ip_conntrack_htable_size; cb->args[0]++) {
-restart:
+ for (; cb->args[0] < ip_conntrack_htable_size; cb->args[0]++, *id = 0) {
list_for_each_prev(i, &ip_conntrack_hash[cb->args[0]]) {
h = (struct ip_conntrack_tuple_hash *) i;
if (DIRECTION(h) != IP_CT_DIR_ORIGINAL)
continue;
ct = tuplehash_to_ctrack(h);
- 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(&ip_conntrack_lock);
- if (last)
- ip_conntrack_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 ip_conntrack *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, NF_IP_PRE_ROUTING);
- }
- 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, NF_IP_POST_ROUTING);
- }
+ 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
}
/* 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 ip_conntrack_tuple *tuple,
- const struct ip_conntrack_tuple *mask)
-{
- int ret;
- struct ip_conntrack_protocol *proto;
- struct nfattr *nest_parms = NFA_NEST(skb, CTA_EXPECT_MASK);
-
- ret = ctnetlink_dump_tuples_ip(skb, mask);
- if (unlikely(ret < 0))
- goto nfattr_failure;
-
- proto = ip_conntrack_proto_find_get(tuple->dst.protonum);
- ret = ctnetlink_dump_tuples_proto(skb, mask, proto);
- ip_conntrack_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 ip_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,