extern int nfnetlink_set_err(struct net *net, u32 pid, u32 group, int error);
extern int nfnetlink_unicast(struct sk_buff *skb, struct net *net, u_int32_t pid, int flags);
-extern void nfnl_lock(void);
-extern void nfnl_unlock(void);
+extern void nfnl_lock(__u8 subsys_id);
+extern void nfnl_unlock(__u8 subsys_id);
#define MODULE_ALIAS_NFNL_SUBSYS(subsys) \
MODULE_ALIAS("nfnetlink-subsys-" __stringify(subsys))
void (*expectfn)(struct nf_conn *ct, struct nf_conntrack_expect *exp);
};
+__printf(3,4)
+void nf_ct_helper_log(struct sk_buff *skb, const struct nf_conn *ct,
+ const char *fmt, ...);
+
void nf_ct_helper_expectfn_register(struct nf_ct_helper_expectfn *n);
void nf_ct_helper_expectfn_unregister(struct nf_ct_helper_expectfn *n);
struct nf_ct_helper_expectfn *
#include <linux/types.h>
-#define XT_CT_NOTRACK 0x1
+enum {
+ XT_CT_NOTRACK = 1 << 0,
+ XT_CT_NOTRACK_ALIAS = 1 << 1,
+ XT_CT_MASK = XT_CT_NOTRACK | XT_CT_NOTRACK_ALIAS,
+};
struct xt_ct_target_info {
__u16 flags;
XT_CONNTRACK_REPLSRC_PORT = 1 << 10,
XT_CONNTRACK_REPLDST_PORT = 1 << 11,
XT_CONNTRACK_DIRECTION = 1 << 12,
+ XT_CONNTRACK_STATE_ALIAS = 1 << 13,
};
struct xt_conntrack_mtinfo1 {
enum ip_conntrack_info ctinfo;
const struct nf_conn_help *help;
const struct nf_conntrack_helper *helper;
- unsigned int ret;
/* This is where we call the helper: as the packet goes out. */
ct = nf_ct_get(skb, &ctinfo);
if (!helper)
return NF_ACCEPT;
- ret = helper->help(skb, skb_network_offset(skb) + ip_hdrlen(skb),
- ct, ctinfo);
- if (ret != NF_ACCEPT && (ret & NF_VERDICT_MASK) != NF_QUEUE) {
- nf_log_packet(NFPROTO_IPV4, hooknum, skb, in, out, NULL,
- "nf_ct_%s: dropping packet", helper->name);
- }
- return ret;
+ return helper->help(skb, skb_network_offset(skb) + ip_hdrlen(skb),
+ ct, ctinfo);
}
static unsigned int ipv4_confirm(unsigned int hooknum,
const struct nf_conn_help *help;
const struct nf_conntrack_helper *helper;
enum ip_conntrack_info ctinfo;
- unsigned int ret;
__be16 frag_off;
int protoff;
u8 nexthdr;
return NF_ACCEPT;
}
- ret = helper->help(skb, protoff, ct, ctinfo);
- if (ret != NF_ACCEPT && (ret & NF_VERDICT_MASK) != NF_QUEUE) {
- nf_log_packet(NFPROTO_IPV6, hooknum, skb, in, out, NULL,
- "nf_ct_%s: dropping packet", helper->name);
- }
- return ret;
+ return helper->help(skb, protoff, ct, ctinfo);
}
static unsigned int ipv6_confirm(unsigned int hooknum,
config NETFILTER_XT_MATCH_CONNLABEL
tristate '"connlabel" match support'
select NF_CONNTRACK_LABELS
+ depends on NF_CONNTRACK
depends on NETFILTER_ADVANCED
---help---
This match allows you to test and assign userspace-defined labels names
static bool
load_settype(const char *name)
{
- nfnl_unlock();
+ nfnl_unlock(NFNL_SUBSYS_IPSET);
pr_debug("try to load ip_set_%s\n", name);
if (request_module("ip_set_%s", name) < 0) {
pr_warning("Can't find ip_set type %s\n", name);
- nfnl_lock();
+ nfnl_lock(NFNL_SUBSYS_IPSET);
return false;
}
- nfnl_lock();
+ nfnl_lock(NFNL_SUBSYS_IPSET);
return true;
}
ip_set_id_t i, index = IPSET_INVALID_ID;
struct ip_set *s;
- nfnl_lock();
+ nfnl_lock(NFNL_SUBSYS_IPSET);
for (i = 0; i < ip_set_max; i++) {
s = nfnl_set(i);
if (s != NULL && STREQ(s->name, name)) {
break;
}
}
- nfnl_unlock();
+ nfnl_unlock(NFNL_SUBSYS_IPSET);
return index;
}
if (index > ip_set_max)
return IPSET_INVALID_ID;
- nfnl_lock();
+ nfnl_lock(NFNL_SUBSYS_IPSET);
set = nfnl_set(index);
if (set)
__ip_set_get(set);
else
index = IPSET_INVALID_ID;
- nfnl_unlock();
+ nfnl_unlock(NFNL_SUBSYS_IPSET);
return index;
}
ip_set_nfnl_put(ip_set_id_t index)
{
struct ip_set *set;
- nfnl_lock();
+ nfnl_lock(NFNL_SUBSYS_IPSET);
set = nfnl_set(index);
if (set != NULL)
__ip_set_put(set);
- nfnl_unlock();
+ nfnl_unlock(NFNL_SUBSYS_IPSET);
}
EXPORT_SYMBOL_GPL(ip_set_nfnl_put);
goto done;
}
req_get->set.name[IPSET_MAXNAMELEN - 1] = '\0';
- nfnl_lock();
+ nfnl_lock(NFNL_SUBSYS_IPSET);
find_set_and_id(req_get->set.name, &id);
req_get->set.index = id;
- nfnl_unlock();
+ nfnl_unlock(NFNL_SUBSYS_IPSET);
goto copy;
}
case IP_SET_OP_GET_BYINDEX: {
ret = -EINVAL;
goto done;
}
- nfnl_lock();
+ nfnl_lock(NFNL_SUBSYS_IPSET);
set = nfnl_set(req_get->set.index);
strncpy(req_get->set.name, set ? set->name : "",
IPSET_MAXNAMELEN);
- nfnl_unlock();
+ nfnl_unlock(NFNL_SUBSYS_IPSET);
goto copy;
}
default:
exp = nf_ct_expect_alloc(ct);
if (exp == NULL) {
+ nf_ct_helper_log(skb, ct, "cannot alloc expectation");
ret = NF_DROP;
goto out;
}
if (nf_nat_amanda && ct->status & IPS_NAT_MASK)
ret = nf_nat_amanda(skb, ctinfo, protoff,
off - dataoff, len, exp);
- else if (nf_ct_expect_related(exp) != 0)
+ else if (nf_ct_expect_related(exp) != 0) {
+ nf_ct_helper_log(skb, ct, "cannot add expectation");
ret = NF_DROP;
+ }
nf_ct_expect_put(exp);
}
connection tracking, not packet filtering.
However, it is necessary for accurate tracking in
this case. */
- pr_debug("conntrack_ftp: partial %s %u+%u\n",
- search[dir][i].pattern, ntohl(th->seq), datalen);
+ nf_ct_helper_log(skb, ct, "partial matching of `%s'",
+ search[dir][i].pattern);
ret = NF_DROP;
goto out;
} else if (found == 0) { /* No match */
exp = nf_ct_expect_alloc(ct);
if (exp == NULL) {
+ nf_ct_helper_log(skb, ct, "cannot alloc expectation");
ret = NF_DROP;
goto out;
}
protoff, matchoff, matchlen, exp);
else {
/* Can't expect this? Best to drop packet now. */
- if (nf_ct_expect_related(exp) != 0)
+ if (nf_ct_expect_related(exp) != 0) {
+ nf_ct_helper_log(skb, ct, "cannot add expectation");
ret = NF_DROP;
- else
+ } else
ret = NF_ACCEPT;
}
drop:
spin_unlock_bh(&nf_h323_lock);
- net_info_ratelimited("nf_ct_h245: packet dropped\n");
+ nf_ct_helper_log(skb, ct, "cannot process H.245 message");
return NF_DROP;
}
drop:
spin_unlock_bh(&nf_h323_lock);
- net_info_ratelimited("nf_ct_q931: packet dropped\n");
+ nf_ct_helper_log(skb, ct, "cannot process Q.931 message");
return NF_DROP;
}
drop:
spin_unlock_bh(&nf_h323_lock);
- net_info_ratelimited("nf_ct_ras: packet dropped\n");
+ nf_ct_helper_log(skb, ct, "cannot process RAS message");
return NF_DROP;
}
#include <net/netfilter/nf_conntrack_helper.h>
#include <net/netfilter/nf_conntrack_core.h>
#include <net/netfilter/nf_conntrack_extend.h>
+#include <net/netfilter/nf_log.h>
static DEFINE_MUTEX(nf_ct_helper_mutex);
struct hlist_head *nf_ct_helper_hash __read_mostly;
}
EXPORT_SYMBOL_GPL(nf_ct_helper_expectfn_find_by_symbol);
+__printf(3, 4)
+void nf_ct_helper_log(struct sk_buff *skb, const struct nf_conn *ct,
+ const char *fmt, ...)
+{
+ const struct nf_conn_help *help;
+ const struct nf_conntrack_helper *helper;
+
+ /* Called from the helper function, this call never fails */
+ help = nfct_help(ct);
+
+ /* rcu_read_lock()ed by nf_hook_slow */
+ helper = rcu_dereference(help->helper);
+
+ nf_log_packet(nf_ct_l3num(ct), 0, skb, NULL, NULL, NULL,
+ "nf_ct_%s: dropping packet: %s ", helper->name, fmt);
+}
+EXPORT_SYMBOL_GPL(nf_ct_helper_log);
+
int nf_conntrack_helper_register(struct nf_conntrack_helper *me)
{
int ret = 0;
exp = nf_ct_expect_alloc(ct);
if (exp == NULL) {
+ nf_ct_helper_log(skb, ct,
+ "cannot alloc expectation");
ret = NF_DROP;
goto out;
}
addr_beg_p - ib_ptr,
addr_end_p - addr_beg_p,
exp);
- else if (nf_ct_expect_related(exp) != 0)
+ else if (nf_ct_expect_related(exp) != 0) {
+ nf_ct_helper_log(skb, ct,
+ "cannot add expectation");
ret = NF_DROP;
+ }
nf_ct_expect_put(exp);
goto out;
}
if (!parse_nat_setup) {
#ifdef CONFIG_MODULES
rcu_read_unlock();
- nfnl_unlock();
+ nfnl_unlock(NFNL_SUBSYS_CTNETLINK);
if (request_module("nf-nat") < 0) {
- nfnl_lock();
+ nfnl_lock(NFNL_SUBSYS_CTNETLINK);
rcu_read_lock();
return -EOPNOTSUPP;
}
- nfnl_lock();
+ nfnl_lock(NFNL_SUBSYS_CTNETLINK);
rcu_read_lock();
if (nfnetlink_parse_nat_setup_hook)
return -EAGAIN;
if (err == -EAGAIN) {
#ifdef CONFIG_MODULES
rcu_read_unlock();
- nfnl_unlock();
+ nfnl_unlock(NFNL_SUBSYS_CTNETLINK);
if (request_module("nf-nat-%u", nf_ct_l3num(ct)) < 0) {
- nfnl_lock();
+ nfnl_lock(NFNL_SUBSYS_CTNETLINK);
rcu_read_lock();
return -EOPNOTSUPP;
}
- nfnl_lock();
+ nfnl_lock(NFNL_SUBSYS_CTNETLINK);
rcu_read_lock();
#else
err = -EOPNOTSUPP;
* Limitations:
* - We blindly assume that control connections are always
* established in PNS->PAC direction. This is a violation
- * of RFFC2673
+ * of RFC 2637
* - We can only support one single call within each session
* TODO:
* - testing of incoming PPTP calls
exp = nf_ct_expect_alloc(ct);
if (exp == NULL) {
+ nf_ct_helper_log(skb, ct, "cannot alloc expectation");
ret = NF_DROP;
goto out;
}
nf_ct_dump_tuple(&exp->tuple);
/* Can't expect this? Best to drop packet now. */
- if (nf_ct_expect_related(exp) != 0)
+ if (nf_ct_expect_related(exp) != 0) {
+ nf_ct_helper_log(skb, ct, "cannot add expectation");
ret = NF_DROP;
+ }
nf_ct_expect_put(exp);
port = simple_strtoul(*dptr + mediaoff, NULL, 10);
if (port == 0)
continue;
- if (port < 1024 || port > 65535)
+ if (port < 1024 || port > 65535) {
+ nf_ct_helper_log(skb, ct, "wrong port %u", port);
return NF_DROP;
+ }
/* The media description overrides the session description. */
maddr_len = 0;
memcpy(&rtp_addr, &maddr, sizeof(rtp_addr));
} else if (caddr_len)
memcpy(&rtp_addr, &caddr, sizeof(rtp_addr));
- else
+ else {
+ nf_ct_helper_log(skb, ct, "cannot parse SDP message");
return NF_DROP;
+ }
ret = set_expected_rtp_rtcp(skb, protoff, dataoff,
dptr, datalen,
&rtp_addr, htons(port), t->class,
mediaoff, medialen);
- if (ret != NF_ACCEPT)
+ if (ret != NF_ACCEPT) {
+ nf_ct_helper_log(skb, ct,
+ "cannot add expectation for voice");
return ret;
+ }
/* Update media connection address if present */
if (maddr_len && nf_nat_sdp_addr && ct->status & IPS_NAT_MASK) {
dptr, datalen, mediaoff,
SDP_HDR_CONNECTION, SDP_HDR_MEDIA,
&rtp_addr);
- if (ret != NF_ACCEPT)
+ if (ret != NF_ACCEPT) {
+ nf_ct_helper_log(skb, ct, "cannot mangle SDP");
return ret;
+ }
}
i++;
}
ret = ct_sip_parse_header_uri(ct, *dptr, NULL, *datalen,
SIP_HDR_CONTACT, NULL,
&matchoff, &matchlen, &daddr, &port);
- if (ret < 0)
+ if (ret < 0) {
+ nf_ct_helper_log(skb, ct, "cannot parse contact");
return NF_DROP;
- else if (ret == 0)
+ } else if (ret == 0)
return NF_ACCEPT;
/* We don't support third-party registrations */
if (ct_sip_parse_numerical_param(ct, *dptr,
matchoff + matchlen, *datalen,
- "expires=", NULL, NULL, &expires) < 0)
+ "expires=", NULL, NULL, &expires) < 0) {
+ nf_ct_helper_log(skb, ct, "cannot parse expires");
return NF_DROP;
+ }
if (expires == 0) {
ret = NF_ACCEPT;
}
exp = nf_ct_expect_alloc(ct);
- if (!exp)
+ if (!exp) {
+ nf_ct_helper_log(skb, ct, "cannot alloc expectation");
return NF_DROP;
+ }
saddr = NULL;
if (sip_direct_signalling)
ret = nf_nat_sip_expect(skb, protoff, dataoff, dptr, datalen,
exp, matchoff, matchlen);
else {
- if (nf_ct_expect_related(exp) != 0)
+ if (nf_ct_expect_related(exp) != 0) {
+ nf_ct_helper_log(skb, ct, "cannot add expectation");
ret = NF_DROP;
- else
+ } else
ret = NF_ACCEPT;
}
nf_ct_expect_put(exp);
SIP_HDR_CONTACT, &in_contact,
&matchoff, &matchlen,
&addr, &port);
- if (ret < 0)
+ if (ret < 0) {
+ nf_ct_helper_log(skb, ct, "cannot parse contact");
return NF_DROP;
- else if (ret == 0)
+ } else if (ret == 0)
break;
/* We don't support third-party registrations */
matchoff + matchlen,
*datalen, "expires=",
NULL, NULL, &c_expires);
- if (ret < 0)
+ if (ret < 0) {
+ nf_ct_helper_log(skb, ct, "cannot parse expires");
return NF_DROP;
+ }
if (c_expires == 0)
break;
if (refresh_signalling_expectation(ct, &addr, proto, port,
if (*datalen < strlen("SIP/2.0 200"))
return NF_ACCEPT;
code = simple_strtoul(*dptr + strlen("SIP/2.0 "), NULL, 10);
- if (!code)
+ if (!code) {
+ nf_ct_helper_log(skb, ct, "cannot get code");
return NF_DROP;
+ }
if (ct_sip_get_header(ct, *dptr, 0, *datalen, SIP_HDR_CSEQ,
- &matchoff, &matchlen) <= 0)
+ &matchoff, &matchlen) <= 0) {
+ nf_ct_helper_log(skb, ct, "cannot parse cseq");
return NF_DROP;
+ }
cseq = simple_strtoul(*dptr + matchoff, NULL, 10);
- if (!cseq)
+ if (!cseq) {
+ nf_ct_helper_log(skb, ct, "cannot get cseq");
return NF_DROP;
+ }
matchend = matchoff + matchlen + 1;
for (i = 0; i < ARRAY_SIZE(sip_handlers); i++) {
continue;
if (ct_sip_get_header(ct, *dptr, 0, *datalen, SIP_HDR_CSEQ,
- &matchoff, &matchlen) <= 0)
+ &matchoff, &matchlen) <= 0) {
+ nf_ct_helper_log(skb, ct, "cannot parse cseq");
return NF_DROP;
+ }
cseq = simple_strtoul(*dptr + matchoff, NULL, 10);
- if (!cseq)
+ if (!cseq) {
+ nf_ct_helper_log(skb, ct, "cannot get cseq");
return NF_DROP;
+ }
return handler->request(skb, protoff, dataoff, dptr, datalen,
cseq);
if (ret == NF_ACCEPT && ct->status & IPS_NAT_MASK) {
nf_nat_sip = rcu_dereference(nf_nat_sip_hook);
if (nf_nat_sip && !nf_nat_sip(skb, protoff, dataoff,
- dptr, datalen))
+ dptr, datalen)) {
+ nf_ct_helper_log(skb, ct, "cannot NAT SIP message");
ret = NF_DROP;
+ }
}
return ret;
end += strlen("\r\n\r\n") + clen;
msglen = origlen = end - dptr;
- if (msglen > datalen)
+ if (msglen > datalen) {
+ nf_ct_helper_log(skb, ct, "incomplete/bad SIP message");
return NF_DROP;
+ }
ret = process_sip_msg(skb, ct, protoff, dataoff,
&dptr, &msglen);
+ /* process_sip_* functions report why this packet is dropped */
if (ret != NF_ACCEPT)
break;
diff = msglen - origlen;
nf_ct_dump_tuple(&ct->tuplehash[IP_CT_DIR_REPLY].tuple);
exp = nf_ct_expect_alloc(ct);
- if (exp == NULL)
+ if (exp == NULL) {
+ nf_ct_helper_log(skb, ct, "cannot alloc expectation");
return NF_DROP;
+ }
tuple = &ct->tuplehash[IP_CT_DIR_REPLY].tuple;
nf_ct_expect_init(exp, NF_CT_EXPECT_CLASS_DEFAULT,
nf_ct_l3num(ct),
nf_nat_tftp = rcu_dereference(nf_nat_tftp_hook);
if (nf_nat_tftp && ct->status & IPS_NAT_MASK)
ret = nf_nat_tftp(skb, ctinfo, exp);
- else if (nf_ct_expect_related(exp) != 0)
+ else if (nf_ct_expect_related(exp) != 0) {
+ nf_ct_helper_log(skb, ct, "cannot add expectation");
ret = NF_DROP;
+ }
nf_ct_expect_put(exp);
break;
case TFTP_OPCODE_DATA:
}
}
- if (port == 0)
+ if (port == 0) {
+ nf_ct_helper_log(skb, exp->master, "all ports in use");
return NF_DROP;
+ }
sprintf(buffer, "%u", port);
ret = nf_nat_mangle_udp_packet(skb, exp->master, ctinfo,
protoff, matchoff, matchlen,
buffer, strlen(buffer));
- if (ret != NF_ACCEPT)
+ if (ret != NF_ACCEPT) {
+ nf_ct_helper_log(skb, exp->master, "cannot mangle packet");
nf_ct_unexpect_related(exp);
+ }
return ret;
}
}
}
- if (port == 0)
+ if (port == 0) {
+ nf_ct_helper_log(skb, ct, "all ports in use");
return NF_DROP;
+ }
buflen = nf_nat_ftp_fmt_cmd(ct, type, buffer, sizeof(buffer),
&newaddr, port);
return NF_ACCEPT;
out:
+ nf_ct_helper_log(skb, ct, "cannot mangle packet");
nf_ct_unexpect_related(exp);
return NF_DROP;
}
}
}
- if (port == 0)
+ if (port == 0) {
+ nf_ct_helper_log(skb, exp->master, "all ports in use");
return NF_DROP;
+ }
ret = nf_nat_mangle_tcp_packet(skb, exp->master, ctinfo,
protoff, matchoff, matchlen, buffer,
strlen(buffer));
- if (ret != NF_ACCEPT)
+ if (ret != NF_ACCEPT) {
+ nf_ct_helper_log(skb, exp->master, "cannot mangle packet");
nf_ct_unexpect_related(exp);
+ }
return ret;
}
&matchoff, &matchlen,
&addr, &port) > 0 &&
!map_addr(skb, protoff, dataoff, dptr, datalen,
- matchoff, matchlen, &addr, port))
+ matchoff, matchlen, &addr, port)) {
+ nf_ct_helper_log(skb, ct, "cannot mangle SIP message");
return NF_DROP;
+ }
request = 1;
} else
request = 0;
olen = *datalen;
if (!map_addr(skb, protoff, dataoff, dptr, datalen,
- matchoff, matchlen, &addr, port))
+ matchoff, matchlen, &addr, port)) {
+ nf_ct_helper_log(skb, ct, "cannot mangle Via header");
return NF_DROP;
+ }
matchend = matchoff + matchlen + *datalen - olen;
&ct->tuplehash[!dir].tuple.dst.u3,
true);
if (!mangle_packet(skb, protoff, dataoff, dptr, datalen,
- poff, plen, buffer, buflen))
+ poff, plen, buffer, buflen)) {
+ nf_ct_helper_log(skb, ct, "cannot mangle maddr");
return NF_DROP;
+ }
}
/* The received= parameter (RFC 2361) contains the address
false);
if (!mangle_packet(skb, protoff, dataoff, dptr, datalen,
poff, plen, buffer, buflen))
+ nf_ct_helper_log(skb, ct, "cannot mangle received");
return NF_DROP;
}
__be16 p = ct->tuplehash[!dir].tuple.src.u.udp.port;
buflen = sprintf(buffer, "%u", ntohs(p));
if (!mangle_packet(skb, protoff, dataoff, dptr, datalen,
- poff, plen, buffer, buflen))
+ poff, plen, buffer, buflen)) {
+ nf_ct_helper_log(skb, ct, "cannot mangle rport");
return NF_DROP;
+ }
}
}
&addr, &port) > 0) {
if (!map_addr(skb, protoff, dataoff, dptr, datalen,
matchoff, matchlen,
- &addr, port))
+ &addr, port)) {
+ nf_ct_helper_log(skb, ct, "cannot mangle contact");
return NF_DROP;
+ }
}
if (!map_sip_addr(skb, protoff, dataoff, dptr, datalen, SIP_HDR_FROM) ||
- !map_sip_addr(skb, protoff, dataoff, dptr, datalen, SIP_HDR_TO))
+ !map_sip_addr(skb, protoff, dataoff, dptr, datalen, SIP_HDR_TO)) {
+ nf_ct_helper_log(skb, ct, "cannot mangle SIP from/to");
return NF_DROP;
+ }
/* Mangle destination port for Cisco phones, then fix up checksums */
if (dir == IP_CT_DIR_REPLY && ct_sip_info->forced_dport) {
struct udphdr *uh;
- if (!skb_make_writable(skb, skb->len))
+ if (!skb_make_writable(skb, skb->len)) {
+ nf_ct_helper_log(skb, ct, "cannot mangle packet");
return NF_DROP;
+ }
uh = (void *)skb->data + protoff;
uh->dest = ct_sip_info->forced_dport;
if (!nf_nat_mangle_udp_packet(skb, ct, ctinfo, protoff,
- 0, 0, NULL, 0))
+ 0, 0, NULL, 0)) {
+ nf_ct_helper_log(skb, ct, "cannot mangle packet");
return NF_DROP;
+ }
}
return NF_ACCEPT;
}
}
- if (port == 0)
+ if (port == 0) {
+ nf_ct_helper_log(skb, ct, "all ports in use for SIP");
return NF_DROP;
+ }
if (!nf_inet_addr_cmp(&exp->tuple.dst.u3, &exp->saved_addr) ||
exp->tuple.dst.u.udp.port != exp->saved_proto.udp.port) {
buflen = sip_sprintf_addr_port(ct, buffer, &newaddr, port);
if (!mangle_packet(skb, protoff, dataoff, dptr, datalen,
- matchoff, matchlen, buffer, buflen))
+ matchoff, matchlen, buffer, buflen)) {
+ nf_ct_helper_log(skb, ct, "cannot mangle packet");
goto err;
+ }
}
return NF_ACCEPT;
}
}
- if (port == 0)
+ if (port == 0) {
+ nf_ct_helper_log(skb, ct, "all ports in use for SDP media");
goto err1;
+ }
/* Update media port. */
if (rtp_exp->tuple.dst.u.udp.port != rtp_exp->saved_proto.udp.port &&
!nf_nat_sdp_port(skb, protoff, dataoff, dptr, datalen,
- mediaoff, medialen, port))
+ mediaoff, medialen, port)) {
+ nf_ct_helper_log(skb, ct, "cannot mangle SDP message");
goto err2;
+ }
return NF_ACCEPT;
= ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.udp.port;
exp->dir = IP_CT_DIR_REPLY;
exp->expectfn = nf_nat_follow_master;
- if (nf_ct_expect_related(exp) != 0)
+ if (nf_ct_expect_related(exp) != 0) {
+ nf_ct_helper_log(skb, exp->master, "cannot add expectation");
return NF_DROP;
+ }
return NF_ACCEPT;
}
static char __initdata nfversion[] = "0.30";
-static const struct nfnetlink_subsystem __rcu *subsys_table[NFNL_SUBSYS_COUNT];
-static DEFINE_MUTEX(nfnl_mutex);
+static struct {
+ struct mutex mutex;
+ const struct nfnetlink_subsystem __rcu *subsys;
+} table[NFNL_SUBSYS_COUNT];
static const int nfnl_group2type[NFNLGRP_MAX+1] = {
[NFNLGRP_CONNTRACK_NEW] = NFNL_SUBSYS_CTNETLINK,
[NFNLGRP_CONNTRACK_EXP_DESTROY] = NFNL_SUBSYS_CTNETLINK_EXP,
};
-void nfnl_lock(void)
+void nfnl_lock(__u8 subsys_id)
{
- mutex_lock(&nfnl_mutex);
+ mutex_lock(&table[subsys_id].mutex);
}
EXPORT_SYMBOL_GPL(nfnl_lock);
-void nfnl_unlock(void)
+void nfnl_unlock(__u8 subsys_id)
{
- mutex_unlock(&nfnl_mutex);
+ mutex_unlock(&table[subsys_id].mutex);
}
EXPORT_SYMBOL_GPL(nfnl_unlock);
+static struct mutex *nfnl_get_lock(__u8 subsys_id)
+{
+ return &table[subsys_id].mutex;
+}
+
int nfnetlink_subsys_register(const struct nfnetlink_subsystem *n)
{
- nfnl_lock();
- if (subsys_table[n->subsys_id]) {
- nfnl_unlock();
+ nfnl_lock(n->subsys_id);
+ if (table[n->subsys_id].subsys) {
+ nfnl_unlock(n->subsys_id);
return -EBUSY;
}
- rcu_assign_pointer(subsys_table[n->subsys_id], n);
- nfnl_unlock();
+ rcu_assign_pointer(table[n->subsys_id].subsys, n);
+ nfnl_unlock(n->subsys_id);
return 0;
}
int nfnetlink_subsys_unregister(const struct nfnetlink_subsystem *n)
{
- nfnl_lock();
- subsys_table[n->subsys_id] = NULL;
- nfnl_unlock();
+ nfnl_lock(n->subsys_id);
+ table[n->subsys_id].subsys = NULL;
+ nfnl_unlock(n->subsys_id);
synchronize_rcu();
return 0;
}
if (subsys_id >= NFNL_SUBSYS_COUNT)
return NULL;
- return rcu_dereference(subsys_table[subsys_id]);
+ return rcu_dereference(table[subsys_id].subsys);
}
static inline const struct nfnl_callback *
struct nlattr *cda[ss->cb[cb_id].attr_count + 1];
struct nlattr *attr = (void *)nlh + min_len;
int attrlen = nlh->nlmsg_len - min_len;
+ __u8 subsys_id = NFNL_SUBSYS_ID(type);
err = nla_parse(cda, ss->cb[cb_id].attr_count,
attr, attrlen, ss->cb[cb_id].policy);
rcu_read_unlock();
} else {
rcu_read_unlock();
- nfnl_lock();
- if (rcu_dereference_protected(
- subsys_table[NFNL_SUBSYS_ID(type)],
- lockdep_is_held(&nfnl_mutex)) != ss ||
+ nfnl_lock(subsys_id);
+ if (rcu_dereference_protected(table[subsys_id].subsys,
+ lockdep_is_held(nfnl_get_lock(subsys_id))) != ss ||
nfnetlink_find_client(type, ss) != nc)
err = -EAGAIN;
else if (nc->call)
(const struct nlattr **)cda);
else
err = -EINVAL;
- nfnl_unlock();
+ nfnl_unlock(subsys_id);
}
if (err == -EAGAIN)
goto replay;
static int __init nfnetlink_init(void)
{
+ int i;
+
+ for (i=0; i<NFNL_SUBSYS_COUNT; i++)
+ mutex_init(&table[i].mutex);
+
pr_info("Netfilter messages via NETLINK v%s.\n", nfversion);
return register_pernet_subsys(&nfnetlink_net_ops);
}
#include <net/netfilter/nf_conntrack_timeout.h>
#include <net/netfilter/nf_conntrack_zones.h>
-static unsigned int xt_ct_target_v0(struct sk_buff *skb,
- const struct xt_action_param *par)
+static inline int xt_ct_target(struct sk_buff *skb, struct nf_conn *ct)
{
- const struct xt_ct_target_info *info = par->targinfo;
- struct nf_conn *ct = info->ct;
-
/* Previously seen (loopback)? Ignore. */
if (skb->nfct != NULL)
return XT_CONTINUE;
return XT_CONTINUE;
}
-static unsigned int xt_ct_target_v1(struct sk_buff *skb,
+static unsigned int xt_ct_target_v0(struct sk_buff *skb,
const struct xt_action_param *par)
{
- const struct xt_ct_target_info_v1 *info = par->targinfo;
+ const struct xt_ct_target_info *info = par->targinfo;
struct nf_conn *ct = info->ct;
- /* Previously seen (loopback)? Ignore. */
- if (skb->nfct != NULL)
- return XT_CONTINUE;
+ return xt_ct_target(skb, ct);
+}
- atomic_inc(&ct->ct_general.use);
- skb->nfct = &ct->ct_general;
- skb->nfctinfo = IP_CT_NEW;
+static unsigned int xt_ct_target_v1(struct sk_buff *skb,
+ const struct xt_action_param *par)
+{
+ const struct xt_ct_target_info_v1 *info = par->targinfo;
+ struct nf_conn *ct = info->ct;
- return XT_CONTINUE;
+ return xt_ct_target(skb, ct);
}
static u8 xt_ct_find_proto(const struct xt_tgchk_param *par)
return 0;
}
-static int xt_ct_tg_check_v0(const struct xt_tgchk_param *par)
-{
- struct xt_ct_target_info *info = par->targinfo;
- struct nf_conntrack_tuple t;
- struct nf_conn *ct;
- int ret = -EOPNOTSUPP;
-
- if (info->flags & ~XT_CT_NOTRACK)
- return -EINVAL;
-
- if (info->flags & XT_CT_NOTRACK) {
- ct = nf_ct_untracked_get();
- atomic_inc(&ct->ct_general.use);
- goto out;
- }
-
-#ifndef CONFIG_NF_CONNTRACK_ZONES
- if (info->zone)
- goto err1;
-#endif
-
- ret = nf_ct_l3proto_try_module_get(par->family);
- if (ret < 0)
- goto err1;
-
- memset(&t, 0, sizeof(t));
- ct = nf_conntrack_alloc(par->net, info->zone, &t, &t, GFP_KERNEL);
- ret = PTR_ERR(ct);
- if (IS_ERR(ct))
- goto err2;
-
- ret = 0;
- if ((info->ct_events || info->exp_events) &&
- !nf_ct_ecache_ext_add(ct, info->ct_events, info->exp_events,
- GFP_KERNEL))
- goto err3;
-
- if (info->helper[0]) {
- ret = xt_ct_set_helper(ct, info->helper, par);
- if (ret < 0)
- goto err3;
- }
-
- __set_bit(IPS_TEMPLATE_BIT, &ct->status);
- __set_bit(IPS_CONFIRMED_BIT, &ct->status);
-
- /* Overload tuple linked list to put us in template list. */
- hlist_nulls_add_head_rcu(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode,
- &par->net->ct.tmpl);
-out:
- info->ct = ct;
- return 0;
-
-err3:
- nf_conntrack_free(ct);
-err2:
- nf_ct_l3proto_module_put(par->family);
-err1:
- return ret;
-}
-
#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
static void __xt_ct_tg_timeout_put(struct ctnl_timeout *timeout)
{
#endif
}
-static int xt_ct_tg_check_v1(const struct xt_tgchk_param *par)
+static int xt_ct_tg_check(const struct xt_tgchk_param *par,
+ struct xt_ct_target_info_v1 *info)
{
- struct xt_ct_target_info_v1 *info = par->targinfo;
struct nf_conntrack_tuple t;
struct nf_conn *ct;
int ret = -EOPNOTSUPP;
- if (info->flags & ~XT_CT_NOTRACK)
- return -EINVAL;
-
if (info->flags & XT_CT_NOTRACK) {
ct = nf_ct_untracked_get();
atomic_inc(&ct->ct_general.use);
return ret;
}
-static void xt_ct_tg_destroy_v0(const struct xt_tgdtor_param *par)
+static int xt_ct_tg_check_v0(const struct xt_tgchk_param *par)
{
struct xt_ct_target_info *info = par->targinfo;
- struct nf_conn *ct = info->ct;
- struct nf_conn_help *help;
+ struct xt_ct_target_info_v1 info_v1 = {
+ .flags = info->flags,
+ .zone = info->zone,
+ .ct_events = info->ct_events,
+ .exp_events = info->exp_events,
+ };
+ int ret;
- if (!nf_ct_is_untracked(ct)) {
- help = nfct_help(ct);
- if (help)
- module_put(help->helper->me);
+ if (info->flags & ~XT_CT_NOTRACK)
+ return -EINVAL;
- nf_ct_l3proto_module_put(par->family);
- }
- nf_ct_put(info->ct);
+ memcpy(info_v1.helper, info->helper, sizeof(info->helper));
+
+ ret = xt_ct_tg_check(par, &info_v1);
+ if (ret < 0)
+ return ret;
+
+ info->ct = info_v1.ct;
+
+ return ret;
+}
+
+static int xt_ct_tg_check_v1(const struct xt_tgchk_param *par)
+{
+ struct xt_ct_target_info_v1 *info = par->targinfo;
+
+ if (info->flags & ~XT_CT_NOTRACK)
+ return -EINVAL;
+
+ return xt_ct_tg_check(par, par->targinfo);
+}
+
+static int xt_ct_tg_check_v2(const struct xt_tgchk_param *par)
+{
+ struct xt_ct_target_info_v1 *info = par->targinfo;
+
+ if (info->flags & ~XT_CT_MASK)
+ return -EINVAL;
+
+ return xt_ct_tg_check(par, par->targinfo);
}
static void xt_ct_destroy_timeout(struct nf_conn *ct)
#endif
}
-static void xt_ct_tg_destroy_v1(const struct xt_tgdtor_param *par)
+static void xt_ct_tg_destroy(const struct xt_tgdtor_param *par,
+ struct xt_ct_target_info_v1 *info)
{
- struct xt_ct_target_info_v1 *info = par->targinfo;
struct nf_conn *ct = info->ct;
struct nf_conn_help *help;
nf_ct_put(info->ct);
}
+static void xt_ct_tg_destroy_v0(const struct xt_tgdtor_param *par)
+{
+ struct xt_ct_target_info *info = par->targinfo;
+ struct xt_ct_target_info_v1 info_v1 = {
+ .flags = info->flags,
+ .zone = info->zone,
+ .ct_events = info->ct_events,
+ .exp_events = info->exp_events,
+ .ct = info->ct,
+ };
+ memcpy(info_v1.helper, info->helper, sizeof(info->helper));
+
+ xt_ct_tg_destroy(par, &info_v1);
+}
+
+static void xt_ct_tg_destroy_v1(const struct xt_tgdtor_param *par)
+{
+ xt_ct_tg_destroy(par, par->targinfo);
+}
+
static struct xt_target xt_ct_tg_reg[] __read_mostly = {
{
.name = "CT",
.table = "raw",
.me = THIS_MODULE,
},
+ {
+ .name = "CT",
+ .family = NFPROTO_UNSPEC,
+ .revision = 2,
+ .targetsize = sizeof(struct xt_ct_target_info_v1),
+ .checkentry = xt_ct_tg_check_v2,
+ .destroy = xt_ct_tg_destroy_v1,
+ .target = xt_ct_target_v1,
+ .table = "raw",
+ .me = THIS_MODULE,
+ },
};
static unsigned int