Commit b640bee6 authored by David S. Miller's avatar David S. Miller

Merge branch 'master' of git://1984.lsi.us.es/nf-next

Pablo Neira Ayuso says:

====================
This batch contains netfilter updates for you net-next tree, they are:

* The new connlabel extension for x_tables, that allows us to attach
  labels to each conntrack flow. The kernel implementation uses a
  bitmask and there's a file in user-space that maps the bits with the
  corresponding string for each existing label. By now, you can attach
  up to 128 overlapping labels. From Florian Westphal.

* A new round of improvements for the netns support for conntrack.
  Gao feng has moved many of the initialization code of each module
  of the netns init path. He also made several code refactoring, that
  code looks cleaner to me now.

* Added documentation for all possible tweaks for nf_conntrack via
  sysctl, from Jiri Pirko.

* Cisco 7941/7945 IP phone support for our SIP conntrack helper,
  from Kevin Cernekee.

* Missing header file in the snmp helper, from Stephen Hemminger.

* Finally, a couple of fixes to resolve minor issues with these
  changes, from myself.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 93b9c1dd 5f9f946b
/proc/sys/net/netfilter/nf_conntrack_* Variables:
nf_conntrack_acct - BOOLEAN
0 - disabled (default)
not 0 - enabled
Enable connection tracking flow accounting. 64-bit byte and packet
counters per flow are added.
nf_conntrack_buckets - INTEGER (read-only)
Size of hash table. If not specified as parameter during module
loading, the default size is calculated by dividing total memory
by 16384 to determine the number of buckets but the hash table will
never have fewer than 32 or more than 16384 buckets.
nf_conntrack_checksum - BOOLEAN
0 - disabled
not 0 - enabled (default)
Verify checksum of incoming packets. Packets with bad checksums are
in INVALID state. If this is enabled, such packets will not be
considered for connection tracking.
nf_conntrack_count - INTEGER (read-only)
Number of currently allocated flow entries.
nf_conntrack_events - BOOLEAN
0 - disabled
not 0 - enabled (default)
If this option is enabled, the connection tracking code will
provide userspace with connection tracking events via ctnetlink.
nf_conntrack_events_retry_timeout - INTEGER (seconds)
default 15
This option is only relevant when "reliable connection tracking
events" are used. Normally, ctnetlink is "lossy", that is,
events are normally dropped when userspace listeners can't keep up.
Userspace can request "reliable event mode". When this mode is
active, the conntrack will only be destroyed after the event was
delivered. If event delivery fails, the kernel periodically
re-tries to send the event to userspace.
This is the maximum interval the kernel should use when re-trying
to deliver the destroy event.
A higher number means there will be fewer delivery retries and it
will take longer for a backlog to be processed.
nf_conntrack_expect_max - INTEGER
Maximum size of expectation table. Default value is
nf_conntrack_buckets / 256. Minimum is 1.
nf_conntrack_frag6_high_thresh - INTEGER
default 262144
Maximum memory used to reassemble IPv6 fragments. When
nf_conntrack_frag6_high_thresh bytes of memory is allocated for this
purpose, the fragment handler will toss packets until
nf_conntrack_frag6_low_thresh is reached.
nf_conntrack_frag6_low_thresh - INTEGER
default 196608
See nf_conntrack_frag6_low_thresh
nf_conntrack_frag6_timeout - INTEGER (seconds)
default 60
Time to keep an IPv6 fragment in memory.
nf_conntrack_generic_timeout - INTEGER (seconds)
default 600
Default for generic timeout. This refers to layer 4 unknown/unsupported
protocols.
nf_conntrack_helper - BOOLEAN
0 - disabled
not 0 - enabled (default)
Enable automatic conntrack helper assignment.
nf_conntrack_icmp_timeout - INTEGER (seconds)
default 30
Default for ICMP timeout.
nf_conntrack_icmpv6_timeout - INTEGER (seconds)
default 30
Default for ICMP6 timeout.
nf_conntrack_log_invalid - INTEGER
0 - disable (default)
1 - log ICMP packets
6 - log TCP packets
17 - log UDP packets
33 - log DCCP packets
41 - log ICMPv6 packets
136 - log UDPLITE packets
255 - log packets of any protocol
Log invalid packets of a type specified by value.
nf_conntrack_max - INTEGER
Size of connection tracking table. Default value is
nf_conntrack_buckets value * 4.
nf_conntrack_tcp_be_liberal - BOOLEAN
0 - disabled (default)
not 0 - enabled
Be conservative in what you do, be liberal in what you accept from others.
If it's non-zero, we mark only out of window RST segments as INVALID.
nf_conntrack_tcp_loose - BOOLEAN
0 - disabled
not 0 - enabled (default)
If it is set to zero, we disable picking up already established
connections.
nf_conntrack_tcp_max_retrans - INTEGER
default 3
Maximum number of packets that can be retransmitted without
received an (acceptable) ACK from the destination. If this number
is reached, a shorter timer will be started.
nf_conntrack_tcp_timeout_close - INTEGER (seconds)
default 10
nf_conntrack_tcp_timeout_close_wait - INTEGER (seconds)
default 60
nf_conntrack_tcp_timeout_established - INTEGER (seconds)
default 432000 (5 days)
nf_conntrack_tcp_timeout_fin_wait - INTEGER (seconds)
default 120
nf_conntrack_tcp_timeout_last_ack - INTEGER (seconds)
default 30
nf_conntrack_tcp_timeout_max_retrans - INTEGER (seconds)
default 300
nf_conntrack_tcp_timeout_syn_recv - INTEGER (seconds)
default 60
nf_conntrack_tcp_timeout_syn_sent - INTEGER (seconds)
default 120
nf_conntrack_tcp_timeout_time_wait - INTEGER (seconds)
default 120
nf_conntrack_tcp_timeout_unacknowledged - INTEGER (seconds)
default 300
nf_conntrack_timestamp - BOOLEAN
0 - disabled (default)
not 0 - enabled
Enable connection tracking flow timestamping.
nf_conntrack_udp_timeout - INTEGER (seconds)
default 30
nf_conntrack_udp_timeout_stream2 - INTEGER (seconds)
default 180
This extended timeout will be used in case there is an UDP stream
detected.
...@@ -4,12 +4,15 @@ ...@@ -4,12 +4,15 @@
#include <net/netfilter/nf_conntrack_expect.h> #include <net/netfilter/nf_conntrack_expect.h>
#include <linux/types.h>
#define SIP_PORT 5060 #define SIP_PORT 5060
#define SIP_TIMEOUT 3600 #define SIP_TIMEOUT 3600
struct nf_ct_sip_master { struct nf_ct_sip_master {
unsigned int register_cseq; unsigned int register_cseq;
unsigned int invite_cseq; unsigned int invite_cseq;
__be16 forced_dport;
}; };
enum sip_expectation_classes { enum sip_expectation_classes {
......
...@@ -57,7 +57,9 @@ static inline void nf_ct_set_acct(struct net *net, bool enable) ...@@ -57,7 +57,9 @@ static inline void nf_ct_set_acct(struct net *net, bool enable)
net->ct.sysctl_acct = enable; net->ct.sysctl_acct = enable;
} }
extern int nf_conntrack_acct_init(struct net *net); extern int nf_conntrack_acct_pernet_init(struct net *net);
extern void nf_conntrack_acct_fini(struct net *net); extern void nf_conntrack_acct_pernet_fini(struct net *net);
extern int nf_conntrack_acct_init(void);
extern void nf_conntrack_acct_fini(void);
#endif /* _NF_CONNTRACK_ACCT_H */ #endif /* _NF_CONNTRACK_ACCT_H */
...@@ -25,12 +25,19 @@ extern unsigned int nf_conntrack_in(struct net *net, ...@@ -25,12 +25,19 @@ extern unsigned int nf_conntrack_in(struct net *net,
unsigned int hooknum, unsigned int hooknum,
struct sk_buff *skb); struct sk_buff *skb);
extern int nf_conntrack_init(struct net *net); extern int nf_conntrack_init_net(struct net *net);
extern void nf_conntrack_cleanup(struct net *net); extern void nf_conntrack_cleanup_net(struct net *net);
extern int nf_conntrack_proto_init(struct net *net); extern int nf_conntrack_proto_pernet_init(struct net *net);
extern void nf_conntrack_proto_fini(struct net *net); extern void nf_conntrack_proto_pernet_fini(struct net *net);
extern int nf_conntrack_proto_init(void);
extern void nf_conntrack_proto_fini(void);
extern int nf_conntrack_init_start(void);
extern void nf_conntrack_cleanup_start(void);
extern void nf_conntrack_init_end(void);
extern void nf_conntrack_cleanup_end(void); extern void nf_conntrack_cleanup_end(void);
extern bool extern bool
......
...@@ -207,9 +207,11 @@ nf_ct_expect_event(enum ip_conntrack_expect_events event, ...@@ -207,9 +207,11 @@ nf_ct_expect_event(enum ip_conntrack_expect_events event,
nf_ct_expect_event_report(event, exp, 0, 0); nf_ct_expect_event_report(event, exp, 0, 0);
} }
extern int nf_conntrack_ecache_init(struct net *net); extern int nf_conntrack_ecache_pernet_init(struct net *net);
extern void nf_conntrack_ecache_fini(struct net *net); extern void nf_conntrack_ecache_pernet_fini(struct net *net);
extern int nf_conntrack_ecache_init(void);
extern void nf_conntrack_ecache_fini(void);
#else /* CONFIG_NF_CONNTRACK_EVENTS */ #else /* CONFIG_NF_CONNTRACK_EVENTS */
static inline void nf_conntrack_event_cache(enum ip_conntrack_events event, static inline void nf_conntrack_event_cache(enum ip_conntrack_events event,
...@@ -232,12 +234,21 @@ static inline void nf_ct_expect_event_report(enum ip_conntrack_expect_events e, ...@@ -232,12 +234,21 @@ static inline void nf_ct_expect_event_report(enum ip_conntrack_expect_events e,
u32 portid, u32 portid,
int report) {} int report) {}
static inline int nf_conntrack_ecache_init(struct net *net) static inline int nf_conntrack_ecache_pernet_init(struct net *net)
{ {
return 0; return 0;
} }
static inline void nf_conntrack_ecache_fini(struct net *net) static inline void nf_conntrack_ecache_pernet_fini(struct net *net)
{
}
static inline int nf_conntrack_ecache_init(void)
{
return 0;
}
static inline void nf_conntrack_ecache_fini(void)
{ {
} }
#endif /* CONFIG_NF_CONNTRACK_EVENTS */ #endif /* CONFIG_NF_CONNTRACK_EVENTS */
......
...@@ -69,8 +69,11 @@ struct nf_conntrack_expect_policy { ...@@ -69,8 +69,11 @@ struct nf_conntrack_expect_policy {
#define NF_CT_EXPECT_CLASS_DEFAULT 0 #define NF_CT_EXPECT_CLASS_DEFAULT 0
int nf_conntrack_expect_init(struct net *net); int nf_conntrack_expect_pernet_init(struct net *net);
void nf_conntrack_expect_fini(struct net *net); void nf_conntrack_expect_pernet_fini(struct net *net);
int nf_conntrack_expect_init(void);
void nf_conntrack_expect_fini(void);
struct nf_conntrack_expect * struct nf_conntrack_expect *
__nf_ct_expect_find(struct net *net, u16 zone, __nf_ct_expect_find(struct net *net, u16 zone,
......
...@@ -22,6 +22,9 @@ enum nf_ct_ext_id { ...@@ -22,6 +22,9 @@ enum nf_ct_ext_id {
#endif #endif
#ifdef CONFIG_NF_CONNTRACK_TIMEOUT #ifdef CONFIG_NF_CONNTRACK_TIMEOUT
NF_CT_EXT_TIMEOUT, NF_CT_EXT_TIMEOUT,
#endif
#ifdef CONFIG_NF_CONNTRACK_LABELS
NF_CT_EXT_LABELS,
#endif #endif
NF_CT_EXT_NUM, NF_CT_EXT_NUM,
}; };
...@@ -33,6 +36,7 @@ enum nf_ct_ext_id { ...@@ -33,6 +36,7 @@ enum nf_ct_ext_id {
#define NF_CT_EXT_ZONE_TYPE struct nf_conntrack_zone #define NF_CT_EXT_ZONE_TYPE struct nf_conntrack_zone
#define NF_CT_EXT_TSTAMP_TYPE struct nf_conn_tstamp #define NF_CT_EXT_TSTAMP_TYPE struct nf_conn_tstamp
#define NF_CT_EXT_TIMEOUT_TYPE struct nf_conn_timeout #define NF_CT_EXT_TIMEOUT_TYPE struct nf_conn_timeout
#define NF_CT_EXT_LABELS_TYPE struct nf_conn_labels
/* Extensions: optional stuff which isn't permanently in struct. */ /* Extensions: optional stuff which isn't permanently in struct. */
struct nf_ct_ext { struct nf_ct_ext {
......
...@@ -82,8 +82,11 @@ static inline void *nfct_help_data(const struct nf_conn *ct) ...@@ -82,8 +82,11 @@ static inline void *nfct_help_data(const struct nf_conn *ct)
return (void *)help->data; return (void *)help->data;
} }
extern int nf_conntrack_helper_init(struct net *net); extern int nf_conntrack_helper_pernet_init(struct net *net);
extern void nf_conntrack_helper_fini(struct net *net); extern void nf_conntrack_helper_pernet_fini(struct net *net);
extern int nf_conntrack_helper_init(void);
extern void nf_conntrack_helper_fini(void);
extern int nf_conntrack_broadcast_help(struct sk_buff *skb, extern int nf_conntrack_broadcast_help(struct sk_buff *skb,
unsigned int protoff, unsigned int protoff,
......
...@@ -76,11 +76,16 @@ struct nf_conntrack_l3proto { ...@@ -76,11 +76,16 @@ struct nf_conntrack_l3proto {
extern struct nf_conntrack_l3proto __rcu *nf_ct_l3protos[AF_MAX]; extern struct nf_conntrack_l3proto __rcu *nf_ct_l3protos[AF_MAX];
/* Protocol registration. */ /* Protocol pernet registration. */
extern int nf_conntrack_l3proto_register(struct net *net, extern int nf_ct_l3proto_pernet_register(struct net *net,
struct nf_conntrack_l3proto *proto); struct nf_conntrack_l3proto *proto);
extern void nf_conntrack_l3proto_unregister(struct net *net, extern void nf_ct_l3proto_pernet_unregister(struct net *net,
struct nf_conntrack_l3proto *proto); struct nf_conntrack_l3proto *proto);
/* Protocol global registration. */
extern int nf_ct_l3proto_register(struct nf_conntrack_l3proto *proto);
extern void nf_ct_l3proto_unregister(struct nf_conntrack_l3proto *proto);
extern struct nf_conntrack_l3proto *nf_ct_l3proto_find_get(u_int16_t l3proto); extern struct nf_conntrack_l3proto *nf_ct_l3proto_find_get(u_int16_t l3proto);
extern void nf_ct_l3proto_put(struct nf_conntrack_l3proto *p); extern void nf_ct_l3proto_put(struct nf_conntrack_l3proto *p);
......
...@@ -121,12 +121,16 @@ extern struct nf_conntrack_l4proto * ...@@ -121,12 +121,16 @@ extern struct nf_conntrack_l4proto *
nf_ct_l4proto_find_get(u_int16_t l3proto, u_int8_t l4proto); nf_ct_l4proto_find_get(u_int16_t l3proto, u_int8_t l4proto);
extern void nf_ct_l4proto_put(struct nf_conntrack_l4proto *p); extern void nf_ct_l4proto_put(struct nf_conntrack_l4proto *p);
/* Protocol registration. */ /* Protocol pernet registration. */
extern int nf_conntrack_l4proto_register(struct net *net, extern int nf_ct_l4proto_pernet_register(struct net *net,
struct nf_conntrack_l4proto *proto); struct nf_conntrack_l4proto *proto);
extern void nf_conntrack_l4proto_unregister(struct net *net, extern void nf_ct_l4proto_pernet_unregister(struct net *net,
struct nf_conntrack_l4proto *proto); struct nf_conntrack_l4proto *proto);
/* Protocol global registration. */
extern int nf_ct_l4proto_register(struct nf_conntrack_l4proto *proto);
extern void nf_ct_l4proto_unregister(struct nf_conntrack_l4proto *proto);
static inline void nf_ct_kfree_compat_sysctl_table(struct nf_proto_net *pn) static inline void nf_ct_kfree_compat_sysctl_table(struct nf_proto_net *pn)
{ {
#if defined(CONFIG_SYSCTL) && defined(CONFIG_NF_CONNTRACK_PROC_COMPAT) #if defined(CONFIG_SYSCTL) && defined(CONFIG_NF_CONNTRACK_PROC_COMPAT)
......
#include <linux/types.h>
#include <net/net_namespace.h>
#include <linux/netfilter/nf_conntrack_common.h>
#include <linux/netfilter/nf_conntrack_tuple_common.h>
#include <net/netfilter/nf_conntrack.h>
#include <net/netfilter/nf_conntrack_extend.h>
#include <uapi/linux/netfilter/xt_connlabel.h>
struct nf_conn_labels {
u8 words;
unsigned long bits[];
};
static inline struct nf_conn_labels *nf_ct_labels_find(const struct nf_conn *ct)
{
#ifdef CONFIG_NF_CONNTRACK_LABELS
return nf_ct_ext_find(ct, NF_CT_EXT_LABELS);
#else
return NULL;
#endif
}
static inline struct nf_conn_labels *nf_ct_labels_ext_add(struct nf_conn *ct)
{
#ifdef CONFIG_NF_CONNTRACK_LABELS
struct nf_conn_labels *cl_ext;
struct net *net = nf_ct_net(ct);
u8 words;
words = ACCESS_ONCE(net->ct.label_words);
if (words == 0 || WARN_ON_ONCE(words > 8))
return NULL;
cl_ext = nf_ct_ext_add_length(ct, NF_CT_EXT_LABELS,
words * sizeof(long), GFP_ATOMIC);
if (cl_ext != NULL)
cl_ext->words = words;
return cl_ext;
#else
return NULL;
#endif
}
bool nf_connlabel_match(const struct nf_conn *ct, u16 bit);
int nf_connlabel_set(struct nf_conn *ct, u16 bit);
int nf_connlabels_replace(struct nf_conn *ct,
const u32 *data, const u32 *mask, unsigned int words);
#ifdef CONFIG_NF_CONNTRACK_LABELS
int nf_conntrack_labels_init(void);
void nf_conntrack_labels_fini(void);
#else
static inline int nf_conntrack_labels_init(void) { return 0; }
static inline void nf_conntrack_labels_fini(void) {}
#endif
...@@ -76,15 +76,15 @@ nf_ct_timeout_lookup(struct net *net, struct nf_conn *ct, ...@@ -76,15 +76,15 @@ nf_ct_timeout_lookup(struct net *net, struct nf_conn *ct,
} }
#ifdef CONFIG_NF_CONNTRACK_TIMEOUT #ifdef CONFIG_NF_CONNTRACK_TIMEOUT
extern int nf_conntrack_timeout_init(struct net *net); extern int nf_conntrack_timeout_init(void);
extern void nf_conntrack_timeout_fini(struct net *net); extern void nf_conntrack_timeout_fini(void);
#else #else
static inline int nf_conntrack_timeout_init(struct net *net) static inline int nf_conntrack_timeout_init(void)
{ {
return 0; return 0;
} }
static inline void nf_conntrack_timeout_fini(struct net *net) static inline void nf_conntrack_timeout_fini(void)
{ {
return; return;
} }
......
...@@ -48,15 +48,28 @@ static inline void nf_ct_set_tstamp(struct net *net, bool enable) ...@@ -48,15 +48,28 @@ static inline void nf_ct_set_tstamp(struct net *net, bool enable)
} }
#ifdef CONFIG_NF_CONNTRACK_TIMESTAMP #ifdef CONFIG_NF_CONNTRACK_TIMESTAMP
extern int nf_conntrack_tstamp_init(struct net *net); extern int nf_conntrack_tstamp_pernet_init(struct net *net);
extern void nf_conntrack_tstamp_fini(struct net *net); extern void nf_conntrack_tstamp_pernet_fini(struct net *net);
extern int nf_conntrack_tstamp_init(void);
extern void nf_conntrack_tstamp_fini(void);
#else #else
static inline int nf_conntrack_tstamp_init(struct net *net) static inline int nf_conntrack_tstamp_pernet_init(struct net *net)
{
return 0;
}
static inline void nf_conntrack_tstamp_pernet_fini(struct net *net)
{
return;
}
static inline int nf_conntrack_tstamp_init(void)
{ {
return 0; return 0;
} }
static inline void nf_conntrack_tstamp_fini(struct net *net) static inline void nf_conntrack_tstamp_fini(void)
{ {
return; return;
} }
......
...@@ -84,6 +84,10 @@ struct netns_ct { ...@@ -84,6 +84,10 @@ struct netns_ct {
int sysctl_auto_assign_helper; int sysctl_auto_assign_helper;
bool auto_assign_helper_warned; bool auto_assign_helper_warned;
struct nf_ip_net nf_ct_proto; struct nf_ip_net nf_ct_proto;
#if defined(CONFIG_NF_CONNTRACK_LABELS)
unsigned int labels_used;
u8 label_words;
#endif
#ifdef CONFIG_NF_NAT_NEEDED #ifdef CONFIG_NF_NAT_NEEDED
struct hlist_head *nat_bysource; struct hlist_head *nat_bysource;
unsigned int nat_htable_size; unsigned int nat_htable_size;
......
...@@ -35,9 +35,11 @@ header-y += xt_TCPOPTSTRIP.h ...@@ -35,9 +35,11 @@ header-y += xt_TCPOPTSTRIP.h
header-y += xt_TEE.h header-y += xt_TEE.h
header-y += xt_TPROXY.h header-y += xt_TPROXY.h
header-y += xt_addrtype.h header-y += xt_addrtype.h
header-y += xt_bpf.h
header-y += xt_cluster.h header-y += xt_cluster.h
header-y += xt_comment.h header-y += xt_comment.h
header-y += xt_connbytes.h header-y += xt_connbytes.h
header-y += xt_connlabel.h
header-y += xt_connlimit.h header-y += xt_connlimit.h
header-y += xt_connmark.h header-y += xt_connmark.h
header-y += xt_conntrack.h header-y += xt_conntrack.h
......
...@@ -101,6 +101,7 @@ enum ip_conntrack_events { ...@@ -101,6 +101,7 @@ enum ip_conntrack_events {
IPCT_MARK, /* new mark has been set */ IPCT_MARK, /* new mark has been set */
IPCT_NATSEQADJ, /* NAT is doing sequence adjustment */ IPCT_NATSEQADJ, /* NAT is doing sequence adjustment */
IPCT_SECMARK, /* new security mark has been set */ IPCT_SECMARK, /* new security mark has been set */
IPCT_LABEL, /* new connlabel has been set */
}; };
enum ip_conntrack_expect_events { enum ip_conntrack_expect_events {
......
...@@ -49,6 +49,8 @@ enum ctattr_type { ...@@ -49,6 +49,8 @@ enum ctattr_type {
CTA_SECCTX, CTA_SECCTX,
CTA_TIMESTAMP, CTA_TIMESTAMP,
CTA_MARK_MASK, CTA_MARK_MASK,
CTA_LABELS,
CTA_LABELS_MASK,
__CTA_MAX __CTA_MAX
}; };
#define CTA_MAX (__CTA_MAX - 1) #define CTA_MAX (__CTA_MAX - 1)
......
#ifndef _XT_BPF_H
#define _XT_BPF_H
#include <linux/filter.h>
#include <linux/types.h>
#define XT_BPF_MAX_NUM_INSTR 64
struct xt_bpf_info {
__u16 bpf_program_num_elem;
struct sock_filter bpf_program[XT_BPF_MAX_NUM_INSTR];
/* only used in the kernel */
struct sk_filter *filter __attribute__((aligned(8)));
};
#endif /*_XT_BPF_H */
#include <linux/types.h>
#define XT_CONNLABEL_MAXBIT 127
enum xt_connlabel_mtopts {
XT_CONNLABEL_OP_INVERT = 1 << 0,
XT_CONNLABEL_OP_SET = 1 << 1,
};
struct xt_connlabel_mtinfo {
__u16 bit;
__u16 options;
};
...@@ -420,54 +420,43 @@ static int ipv4_net_init(struct net *net) ...@@ -420,54 +420,43 @@ static int ipv4_net_init(struct net *net)
{ {
int ret = 0; int ret = 0;
ret = nf_conntrack_l4proto_register(net, ret = nf_ct_l4proto_pernet_register(net, &nf_conntrack_l4proto_tcp4);
&nf_conntrack_l4proto_tcp4);
if (ret < 0) { if (ret < 0) {
pr_err("nf_conntrack_l4proto_tcp4 :protocol register failed\n"); pr_err("nf_conntrack_tcp4: pernet registration failed\n");
goto out_tcp; goto out_tcp;
} }
ret = nf_conntrack_l4proto_register(net, ret = nf_ct_l4proto_pernet_register(net, &nf_conntrack_l4proto_udp4);
&nf_conntrack_l4proto_udp4);
if (ret < 0) { if (ret < 0) {
pr_err("nf_conntrack_l4proto_udp4 :protocol register failed\n"); pr_err("nf_conntrack_udp4: pernet registration failed\n");
goto out_udp; goto out_udp;
} }
ret = nf_conntrack_l4proto_register(net, ret = nf_ct_l4proto_pernet_register(net, &nf_conntrack_l4proto_icmp);
&nf_conntrack_l4proto_icmp);
if (ret < 0) { if (ret < 0) {
pr_err("nf_conntrack_l4proto_icmp4 :protocol register failed\n"); pr_err("nf_conntrack_icmp4: pernet registration failed\n");
goto out_icmp; goto out_icmp;
} }
ret = nf_conntrack_l3proto_register(net, ret = nf_ct_l3proto_pernet_register(net, &nf_conntrack_l3proto_ipv4);
&nf_conntrack_l3proto_ipv4);
if (ret < 0) { if (ret < 0) {
pr_err("nf_conntrack_l3proto_ipv4 :protocol register failed\n"); pr_err("nf_conntrack_ipv4: pernet registration failed\n");
goto out_ipv4; goto out_ipv4;
} }
return 0; return 0;
out_ipv4: out_ipv4:
nf_conntrack_l4proto_unregister(net, nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_icmp);
&nf_conntrack_l4proto_icmp);
out_icmp: out_icmp:
nf_conntrack_l4proto_unregister(net, nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_udp4);
&nf_conntrack_l4proto_udp4);
out_udp: out_udp:
nf_conntrack_l4proto_unregister(net, nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_tcp4);
&nf_conntrack_l4proto_tcp4);
out_tcp: out_tcp:
return ret; return ret;
} }
static void ipv4_net_exit(struct net *net) static void ipv4_net_exit(struct net *net)
{ {
nf_conntrack_l3proto_unregister(net, nf_ct_l3proto_pernet_unregister(net, &nf_conntrack_l3proto_ipv4);
&nf_conntrack_l3proto_ipv4); nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_icmp);
nf_conntrack_l4proto_unregister(net, nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_udp4);
&nf_conntrack_l4proto_icmp); nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_tcp4);
nf_conntrack_l4proto_unregister(net,
&nf_conntrack_l4proto_udp4);
nf_conntrack_l4proto_unregister(net,
&nf_conntrack_l4proto_tcp4);
} }
static struct pernet_operations ipv4_net_ops = { static struct pernet_operations ipv4_net_ops = {
...@@ -500,16 +489,49 @@ static int __init nf_conntrack_l3proto_ipv4_init(void) ...@@ -500,16 +489,49 @@ static int __init nf_conntrack_l3proto_ipv4_init(void)
pr_err("nf_conntrack_ipv4: can't register hooks.\n"); pr_err("nf_conntrack_ipv4: can't register hooks.\n");
goto cleanup_pernet; goto cleanup_pernet;
} }
ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_tcp4);
if (ret < 0) {
pr_err("nf_conntrack_ipv4: can't register tcp4 proto.\n");
goto cleanup_hooks;
}
ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_udp4);
if (ret < 0) {
pr_err("nf_conntrack_ipv4: can't register udp4 proto.\n");
goto cleanup_tcp4;
}
ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_icmp);
if (ret < 0) {
pr_err("nf_conntrack_ipv4: can't register icmpv4 proto.\n");
goto cleanup_udp4;
}
ret = nf_ct_l3proto_register(&nf_conntrack_l3proto_ipv4);
if (ret < 0) {
pr_err("nf_conntrack_ipv4: can't register ipv4 proto.\n");
goto cleanup_icmpv4;
}
#if defined(CONFIG_PROC_FS) && defined(CONFIG_NF_CONNTRACK_PROC_COMPAT) #if defined(CONFIG_PROC_FS) && defined(CONFIG_NF_CONNTRACK_PROC_COMPAT)
ret = nf_conntrack_ipv4_compat_init(); ret = nf_conntrack_ipv4_compat_init();
if (ret < 0) if (ret < 0)
goto cleanup_hooks; goto cleanup_proto;
#endif #endif
return ret; return ret;
#if defined(CONFIG_PROC_FS) && defined(CONFIG_NF_CONNTRACK_PROC_COMPAT) #if defined(CONFIG_PROC_FS) && defined(CONFIG_NF_CONNTRACK_PROC_COMPAT)
cleanup_proto:
nf_ct_l3proto_unregister(&nf_conntrack_l3proto_ipv4);
#endif
cleanup_icmpv4:
nf_ct_l4proto_unregister(&nf_conntrack_l4proto_icmp);
cleanup_udp4:
nf_ct_l4proto_unregister(&nf_conntrack_l4proto_udp4);
cleanup_tcp4:
nf_ct_l4proto_unregister(&nf_conntrack_l4proto_tcp4);
cleanup_hooks: cleanup_hooks:
nf_unregister_hooks(ipv4_conntrack_ops, ARRAY_SIZE(ipv4_conntrack_ops)); nf_unregister_hooks(ipv4_conntrack_ops, ARRAY_SIZE(ipv4_conntrack_ops));
#endif
cleanup_pernet: cleanup_pernet:
unregister_pernet_subsys(&ipv4_net_ops); unregister_pernet_subsys(&ipv4_net_ops);
cleanup_sockopt: cleanup_sockopt:
...@@ -523,6 +545,10 @@ static void __exit nf_conntrack_l3proto_ipv4_fini(void) ...@@ -523,6 +545,10 @@ static void __exit nf_conntrack_l3proto_ipv4_fini(void)
#if defined(CONFIG_PROC_FS) && defined(CONFIG_NF_CONNTRACK_PROC_COMPAT) #if defined(CONFIG_PROC_FS) && defined(CONFIG_NF_CONNTRACK_PROC_COMPAT)
nf_conntrack_ipv4_compat_fini(); nf_conntrack_ipv4_compat_fini();
#endif #endif
nf_ct_l3proto_unregister(&nf_conntrack_l3proto_ipv4);
nf_ct_l4proto_unregister(&nf_conntrack_l4proto_icmp);
nf_ct_l4proto_unregister(&nf_conntrack_l4proto_udp4);
nf_ct_l4proto_unregister(&nf_conntrack_l4proto_tcp4);
nf_unregister_hooks(ipv4_conntrack_ops, ARRAY_SIZE(ipv4_conntrack_ops)); nf_unregister_hooks(ipv4_conntrack_ops, ARRAY_SIZE(ipv4_conntrack_ops));
unregister_pernet_subsys(&ipv4_net_ops); unregister_pernet_subsys(&ipv4_net_ops);
nf_unregister_sockopt(&so_getorigdst); nf_unregister_sockopt(&so_getorigdst);
......
...@@ -421,54 +421,43 @@ static int ipv6_net_init(struct net *net) ...@@ -421,54 +421,43 @@ static int ipv6_net_init(struct net *net)
{ {
int ret = 0; int ret = 0;
ret = nf_conntrack_l4proto_register(net, ret = nf_ct_l4proto_pernet_register(net, &nf_conntrack_l4proto_tcp6);
&nf_conntrack_l4proto_tcp6);
if (ret < 0) { if (ret < 0) {
printk(KERN_ERR "nf_conntrack_l4proto_tcp6: protocol register failed\n"); pr_err("nf_conntrack_tcp6: pernet registration failed\n");
goto out; goto out;
} }
ret = nf_conntrack_l4proto_register(net, ret = nf_ct_l4proto_pernet_register(net, &nf_conntrack_l4proto_udp6);
&nf_conntrack_l4proto_udp6);
if (ret < 0) { if (ret < 0) {
printk(KERN_ERR "nf_conntrack_l4proto_udp6: protocol register failed\n"); pr_err("nf_conntrack_udp6: pernet registration failed\n");
goto cleanup_tcp6; goto cleanup_tcp6;
} }
ret = nf_conntrack_l4proto_register(net, ret = nf_ct_l4proto_pernet_register(net, &nf_conntrack_l4proto_icmpv6);
&nf_conntrack_l4proto_icmpv6);
if (ret < 0) { if (ret < 0) {
printk(KERN_ERR "nf_conntrack_l4proto_icmp6: protocol register failed\n"); pr_err("nf_conntrack_icmp6: pernet registration failed\n");
goto cleanup_udp6; goto cleanup_udp6;
} }
ret = nf_conntrack_l3proto_register(net, ret = nf_ct_l3proto_pernet_register(net, &nf_conntrack_l3proto_ipv6);
&nf_conntrack_l3proto_ipv6);
if (ret < 0) { if (ret < 0) {
printk(KERN_ERR "nf_conntrack_l3proto_ipv6: protocol register failed\n"); pr_err("nf_conntrack_ipv6: pernet registration failed.\n");
goto cleanup_icmpv6; goto cleanup_icmpv6;
} }
return 0; return 0;
cleanup_icmpv6: cleanup_icmpv6:
nf_conntrack_l4proto_unregister(net, nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_icmpv6);
&nf_conntrack_l4proto_icmpv6);
cleanup_udp6: cleanup_udp6:
nf_conntrack_l4proto_unregister(net, nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_udp6);
&nf_conntrack_l4proto_udp6);
cleanup_tcp6: cleanup_tcp6:
nf_conntrack_l4proto_unregister(net, nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_tcp6);
&nf_conntrack_l4proto_tcp6);
out: out:
return ret; return ret;
} }
static void ipv6_net_exit(struct net *net) static void ipv6_net_exit(struct net *net)
{ {
nf_conntrack_l3proto_unregister(net, nf_ct_l3proto_pernet_unregister(net, &nf_conntrack_l3proto_ipv6);
&nf_conntrack_l3proto_ipv6); nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_icmpv6);
nf_conntrack_l4proto_unregister(net, nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_udp6);
&nf_conntrack_l4proto_icmpv6); nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_tcp6);
nf_conntrack_l4proto_unregister(net,
&nf_conntrack_l4proto_udp6);
nf_conntrack_l4proto_unregister(net,
&nf_conntrack_l4proto_tcp6);
} }
static struct pernet_operations ipv6_net_ops = { static struct pernet_operations ipv6_net_ops = {
...@@ -491,19 +480,52 @@ static int __init nf_conntrack_l3proto_ipv6_init(void) ...@@ -491,19 +480,52 @@ static int __init nf_conntrack_l3proto_ipv6_init(void)
ret = register_pernet_subsys(&ipv6_net_ops); ret = register_pernet_subsys(&ipv6_net_ops);
if (ret < 0) if (ret < 0)
goto cleanup_pernet; goto cleanup_sockopt;
ret = nf_register_hooks(ipv6_conntrack_ops, ret = nf_register_hooks(ipv6_conntrack_ops,
ARRAY_SIZE(ipv6_conntrack_ops)); ARRAY_SIZE(ipv6_conntrack_ops));
if (ret < 0) { if (ret < 0) {
pr_err("nf_conntrack_ipv6: can't register pre-routing defrag " pr_err("nf_conntrack_ipv6: can't register pre-routing defrag "
"hook.\n"); "hook.\n");
goto cleanup_ipv6; goto cleanup_pernet;
}
ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_tcp6);
if (ret < 0) {
pr_err("nf_conntrack_ipv6: can't register tcp6 proto.\n");
goto cleanup_hooks;
}
ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_udp6);
if (ret < 0) {
pr_err("nf_conntrack_ipv6: can't register udp6 proto.\n");
goto cleanup_tcp6;
}
ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_icmpv6);
if (ret < 0) {
pr_err("nf_conntrack_ipv6: can't register icmpv6 proto.\n");
goto cleanup_udp6;
}
ret = nf_ct_l3proto_register(&nf_conntrack_l3proto_ipv6);
if (ret < 0) {
pr_err("nf_conntrack_ipv6: can't register ipv6 proto.\n");
goto cleanup_icmpv6;
} }
return ret; return ret;
cleanup_ipv6: cleanup_icmpv6:
unregister_pernet_subsys(&ipv6_net_ops); nf_ct_l4proto_unregister(&nf_conntrack_l4proto_icmpv6);
cleanup_udp6:
nf_ct_l4proto_unregister(&nf_conntrack_l4proto_udp6);
cleanup_tcp6:
nf_ct_l4proto_unregister(&nf_conntrack_l4proto_tcp6);
cleanup_hooks:
nf_unregister_hooks(ipv6_conntrack_ops, ARRAY_SIZE(ipv6_conntrack_ops));
cleanup_pernet: cleanup_pernet:
unregister_pernet_subsys(&ipv6_net_ops);
cleanup_sockopt:
nf_unregister_sockopt(&so_getorigdst6); nf_unregister_sockopt(&so_getorigdst6);
return ret; return ret;
} }
...@@ -511,6 +533,10 @@ static int __init nf_conntrack_l3proto_ipv6_init(void) ...@@ -511,6 +533,10 @@ static int __init nf_conntrack_l3proto_ipv6_init(void)
static void __exit nf_conntrack_l3proto_ipv6_fini(void) static void __exit nf_conntrack_l3proto_ipv6_fini(void)
{ {
synchronize_net(); synchronize_net();
nf_ct_l3proto_unregister(&nf_conntrack_l3proto_ipv6);
nf_ct_l4proto_unregister(&nf_conntrack_l4proto_tcp6);
nf_ct_l4proto_unregister(&nf_conntrack_l4proto_udp6);
nf_ct_l4proto_unregister(&nf_conntrack_l4proto_icmpv6);
nf_unregister_hooks(ipv6_conntrack_ops, ARRAY_SIZE(ipv6_conntrack_ops)); nf_unregister_hooks(ipv6_conntrack_ops, ARRAY_SIZE(ipv6_conntrack_ops));
unregister_pernet_subsys(&ipv6_net_ops); unregister_pernet_subsys(&ipv6_net_ops);
nf_unregister_sockopt(&so_getorigdst6); nf_unregister_sockopt(&so_getorigdst6);
......
...@@ -124,6 +124,12 @@ config NF_CONNTRACK_TIMESTAMP ...@@ -124,6 +124,12 @@ config NF_CONNTRACK_TIMESTAMP
If unsure, say `N'. If unsure, say `N'.
config NF_CONNTRACK_LABELS
bool
help
This option enables support for assigning user-defined flag bits
to connection tracking entries. It selected by the connlabel match.
config NF_CT_PROTO_DCCP config NF_CT_PROTO_DCCP
tristate 'DCCP protocol connection tracking support (EXPERIMENTAL)' tristate 'DCCP protocol connection tracking support (EXPERIMENTAL)'
depends on EXPERIMENTAL depends on EXPERIMENTAL
...@@ -805,6 +811,15 @@ config NETFILTER_XT_MATCH_ADDRTYPE ...@@ -805,6 +811,15 @@ config NETFILTER_XT_MATCH_ADDRTYPE
If you want to compile it as a module, say M here and read If you want to compile it as a module, say M here and read
<file:Documentation/kbuild/modules.txt>. If unsure, say `N'. <file:Documentation/kbuild/modules.txt>. If unsure, say `N'.
config NETFILTER_XT_MATCH_BPF
tristate '"bpf" match support'
depends on NETFILTER_ADVANCED
help
BPF matching applies a linux socket filter to each packet and
accepts those for which the filter returns non-zero.
To compile it as a module, choose M here. If unsure, say N.
config NETFILTER_XT_MATCH_CLUSTER config NETFILTER_XT_MATCH_CLUSTER
tristate '"cluster" match support' tristate '"cluster" match support'
depends on NF_CONNTRACK depends on NF_CONNTRACK
...@@ -842,6 +857,18 @@ config NETFILTER_XT_MATCH_CONNBYTES ...@@ -842,6 +857,18 @@ config NETFILTER_XT_MATCH_CONNBYTES
If you want to compile it as a module, say M here and read If you want to compile it as a module, say M here and read
<file:Documentation/kbuild/modules.txt>. If unsure, say `N'. <file:Documentation/kbuild/modules.txt>. If unsure, say `N'.
config NETFILTER_XT_MATCH_CONNLABEL
tristate '"connlabel" match support'
select NF_CONNTRACK_LABELS
depends on NETFILTER_ADVANCED
---help---
This match allows you to test and assign userspace-defined labels names
to a connection. The kernel only stores bit values - mapping
names to bits is done by userspace.
Unlike connmark, more than 32 flag bits may be assigned to a
connection simultaneously.
config NETFILTER_XT_MATCH_CONNLIMIT config NETFILTER_XT_MATCH_CONNLIMIT
tristate '"connlimit" match support"' tristate '"connlimit" match support"'
depends on NF_CONNTRACK depends on NF_CONNTRACK
......
...@@ -4,6 +4,7 @@ nf_conntrack-y := nf_conntrack_core.o nf_conntrack_standalone.o nf_conntrack_exp ...@@ -4,6 +4,7 @@ nf_conntrack-y := nf_conntrack_core.o nf_conntrack_standalone.o nf_conntrack_exp
nf_conntrack-$(CONFIG_NF_CONNTRACK_TIMEOUT) += nf_conntrack_timeout.o nf_conntrack-$(CONFIG_NF_CONNTRACK_TIMEOUT) += nf_conntrack_timeout.o
nf_conntrack-$(CONFIG_NF_CONNTRACK_TIMESTAMP) += nf_conntrack_timestamp.o nf_conntrack-$(CONFIG_NF_CONNTRACK_TIMESTAMP) += nf_conntrack_timestamp.o
nf_conntrack-$(CONFIG_NF_CONNTRACK_EVENTS) += nf_conntrack_ecache.o nf_conntrack-$(CONFIG_NF_CONNTRACK_EVENTS) += nf_conntrack_ecache.o
nf_conntrack-$(CONFIG_NF_CONNTRACK_LABELS) += nf_conntrack_labels.o
obj-$(CONFIG_NETFILTER) = netfilter.o obj-$(CONFIG_NETFILTER) = netfilter.o
...@@ -98,9 +99,11 @@ obj-$(CONFIG_NETFILTER_XT_TARGET_IDLETIMER) += xt_IDLETIMER.o ...@@ -98,9 +99,11 @@ obj-$(CONFIG_NETFILTER_XT_TARGET_IDLETIMER) += xt_IDLETIMER.o
# matches # matches
obj-$(CONFIG_NETFILTER_XT_MATCH_ADDRTYPE) += xt_addrtype.o obj-$(CONFIG_NETFILTER_XT_MATCH_ADDRTYPE) += xt_addrtype.o
obj-$(CONFIG_NETFILTER_XT_MATCH_BPF) += xt_bpf.o
obj-$(CONFIG_NETFILTER_XT_MATCH_CLUSTER) += xt_cluster.o obj-$(CONFIG_NETFILTER_XT_MATCH_CLUSTER) += xt_cluster.o
obj-$(CONFIG_NETFILTER_XT_MATCH_COMMENT) += xt_comment.o obj-$(CONFIG_NETFILTER_XT_MATCH_COMMENT) += xt_comment.o
obj-$(CONFIG_NETFILTER_XT_MATCH_CONNBYTES) += xt_connbytes.o obj-$(CONFIG_NETFILTER_XT_MATCH_CONNBYTES) += xt_connbytes.o
obj-$(CONFIG_NETFILTER_XT_MATCH_CONNLABEL) += xt_connlabel.o
obj-$(CONFIG_NETFILTER_XT_MATCH_CONNLIMIT) += xt_connlimit.o obj-$(CONFIG_NETFILTER_XT_MATCH_CONNLIMIT) += xt_connlimit.o
obj-$(CONFIG_NETFILTER_XT_MATCH_CONNTRACK) += xt_conntrack.o obj-$(CONFIG_NETFILTER_XT_MATCH_CONNTRACK) += xt_conntrack.o
obj-$(CONFIG_NETFILTER_XT_MATCH_CPU) += xt_cpu.o obj-$(CONFIG_NETFILTER_XT_MATCH_CPU) += xt_cpu.o
......
...@@ -106,36 +106,26 @@ static void nf_conntrack_acct_fini_sysctl(struct net *net) ...@@ -106,36 +106,26 @@ static void nf_conntrack_acct_fini_sysctl(struct net *net)
} }
#endif #endif
int nf_conntrack_acct_init(struct net *net) int nf_conntrack_acct_pernet_init(struct net *net)
{ {
int ret;
net->ct.sysctl_acct = nf_ct_acct; net->ct.sysctl_acct = nf_ct_acct;
return nf_conntrack_acct_init_sysctl(net);
}
if (net_eq(net, &init_net)) { void nf_conntrack_acct_pernet_fini(struct net *net)
ret = nf_ct_extend_register(&acct_extend); {
if (ret < 0) { nf_conntrack_acct_fini_sysctl(net);
printk(KERN_ERR "nf_conntrack_acct: Unable to register extension\n"); }
goto out_extend_register;
}
}
ret = nf_conntrack_acct_init_sysctl(net); int nf_conntrack_acct_init(void)
{
int ret = nf_ct_extend_register(&acct_extend);
if (ret < 0) if (ret < 0)
goto out_sysctl; pr_err("nf_conntrack_acct: Unable to register extension\n");
return 0;
out_sysctl:
if (net_eq(net, &init_net))
nf_ct_extend_unregister(&acct_extend);
out_extend_register:
return ret; return ret;
} }
void nf_conntrack_acct_fini(struct net *net) void nf_conntrack_acct_fini(void)
{ {
nf_conntrack_acct_fini_sysctl(net);
if (net_eq(net, &init_net))
nf_ct_extend_unregister(&acct_extend); nf_ct_extend_unregister(&acct_extend);
} }
...@@ -45,6 +45,7 @@ ...@@ -45,6 +45,7 @@
#include <net/netfilter/nf_conntrack_zones.h> #include <net/netfilter/nf_conntrack_zones.h>
#include <net/netfilter/nf_conntrack_timestamp.h> #include <net/netfilter/nf_conntrack_timestamp.h>
#include <net/netfilter/nf_conntrack_timeout.h> #include <net/netfilter/nf_conntrack_timeout.h>
#include <net/netfilter/nf_conntrack_labels.h>
#include <net/netfilter/nf_nat.h> #include <net/netfilter/nf_nat.h>
#include <net/netfilter/nf_nat_core.h> #include <net/netfilter/nf_nat_core.h>
...@@ -763,6 +764,7 @@ void nf_conntrack_free(struct nf_conn *ct) ...@@ -763,6 +764,7 @@ void nf_conntrack_free(struct nf_conn *ct)
} }
EXPORT_SYMBOL_GPL(nf_conntrack_free); EXPORT_SYMBOL_GPL(nf_conntrack_free);
/* Allocate a new conntrack: we return -ENOMEM if classification /* Allocate a new conntrack: we return -ENOMEM if classification
failed due to stress. Otherwise it really is unclassifiable. */ failed due to stress. Otherwise it really is unclassifiable. */
static struct nf_conntrack_tuple_hash * static struct nf_conntrack_tuple_hash *
...@@ -809,6 +811,7 @@ init_conntrack(struct net *net, struct nf_conn *tmpl, ...@@ -809,6 +811,7 @@ init_conntrack(struct net *net, struct nf_conn *tmpl,
nf_ct_acct_ext_add(ct, GFP_ATOMIC); nf_ct_acct_ext_add(ct, GFP_ATOMIC);
nf_ct_tstamp_ext_add(ct, GFP_ATOMIC); nf_ct_tstamp_ext_add(ct, GFP_ATOMIC);
nf_ct_labels_ext_add(ct);
ecache = tmpl ? nf_ct_ecache_find(tmpl) : NULL; ecache = tmpl ? nf_ct_ecache_find(tmpl) : NULL;
nf_ct_ecache_ext_add(ct, ecache ? ecache->ctmask : 0, nf_ct_ecache_ext_add(ct, ecache ? ecache->ctmask : 0,
...@@ -1331,18 +1334,42 @@ static int untrack_refs(void) ...@@ -1331,18 +1334,42 @@ static int untrack_refs(void)
return cnt; return cnt;
} }
static void nf_conntrack_cleanup_init_net(void) void nf_conntrack_cleanup_start(void)
{
RCU_INIT_POINTER(ip_ct_attach, NULL);
}
void nf_conntrack_cleanup_end(void)
{ {
RCU_INIT_POINTER(nf_ct_destroy, NULL);
while (untrack_refs() > 0) while (untrack_refs() > 0)
schedule(); schedule();
#ifdef CONFIG_NF_CONNTRACK_ZONES #ifdef CONFIG_NF_CONNTRACK_ZONES
nf_ct_extend_unregister(&nf_ct_zone_extend); nf_ct_extend_unregister(&nf_ct_zone_extend);
#endif #endif
nf_conntrack_proto_fini();
nf_conntrack_labels_fini();
nf_conntrack_helper_fini();
nf_conntrack_timeout_fini();
nf_conntrack_ecache_fini();
nf_conntrack_tstamp_fini();
nf_conntrack_acct_fini();
nf_conntrack_expect_fini();
} }
static void nf_conntrack_cleanup_net(struct net *net) /*
* Mishearing the voices in his head, our hero wonders how he's
* supposed to kill the mall.
*/
void nf_conntrack_cleanup_net(struct net *net)
{ {
/*
* This makes sure all current packets have passed through
* netfilter framework. Roll on, two-stage module
* delete...
*/
synchronize_net();
i_see_dead_people: i_see_dead_people:
nf_ct_iterate_cleanup(net, kill_all, NULL); nf_ct_iterate_cleanup(net, kill_all, NULL);
nf_ct_release_dying_list(net); nf_ct_release_dying_list(net);
...@@ -1352,38 +1379,17 @@ static void nf_conntrack_cleanup_net(struct net *net) ...@@ -1352,38 +1379,17 @@ static void nf_conntrack_cleanup_net(struct net *net)
} }
nf_ct_free_hashtable(net->ct.hash, net->ct.htable_size); nf_ct_free_hashtable(net->ct.hash, net->ct.htable_size);
nf_conntrack_helper_fini(net); nf_conntrack_proto_pernet_fini(net);
nf_conntrack_timeout_fini(net); nf_conntrack_helper_pernet_fini(net);
nf_conntrack_ecache_fini(net); nf_conntrack_ecache_pernet_fini(net);
nf_conntrack_tstamp_fini(net); nf_conntrack_tstamp_pernet_fini(net);
nf_conntrack_acct_fini(net); nf_conntrack_acct_pernet_fini(net);
nf_conntrack_expect_fini(net); nf_conntrack_expect_pernet_fini(net);
kmem_cache_destroy(net->ct.nf_conntrack_cachep); kmem_cache_destroy(net->ct.nf_conntrack_cachep);
kfree(net->ct.slabname); kfree(net->ct.slabname);
free_percpu(net->ct.stat); free_percpu(net->ct.stat);
} }
/* Mishearing the voices in his head, our hero wonders how he's
supposed to kill the mall. */
void nf_conntrack_cleanup(struct net *net)
{
if (net_eq(net, &init_net))
RCU_INIT_POINTER(ip_ct_attach, NULL);
/* This makes sure all current packets have passed through
netfilter framework. Roll on, two-stage module
delete... */
synchronize_net();
nf_conntrack_proto_fini(net);
nf_conntrack_cleanup_net(net);
}
void nf_conntrack_cleanup_end(void)
{
RCU_INIT_POINTER(nf_ct_destroy, NULL);
nf_conntrack_cleanup_init_net();
}
void *nf_ct_alloc_hashtable(unsigned int *sizep, int nulls) void *nf_ct_alloc_hashtable(unsigned int *sizep, int nulls)
{ {
struct hlist_nulls_head *hash; struct hlist_nulls_head *hash;
...@@ -1474,7 +1480,7 @@ void nf_ct_untracked_status_or(unsigned long bits) ...@@ -1474,7 +1480,7 @@ void nf_ct_untracked_status_or(unsigned long bits)
} }
EXPORT_SYMBOL_GPL(nf_ct_untracked_status_or); EXPORT_SYMBOL_GPL(nf_ct_untracked_status_or);
static int nf_conntrack_init_init_net(void) int nf_conntrack_init_start(void)
{ {
int max_factor = 8; int max_factor = 8;
int ret, cpu; int ret, cpu;
...@@ -1501,11 +1507,44 @@ static int nf_conntrack_init_init_net(void) ...@@ -1501,11 +1507,44 @@ static int nf_conntrack_init_init_net(void)
printk(KERN_INFO "nf_conntrack version %s (%u buckets, %d max)\n", printk(KERN_INFO "nf_conntrack version %s (%u buckets, %d max)\n",
NF_CONNTRACK_VERSION, nf_conntrack_htable_size, NF_CONNTRACK_VERSION, nf_conntrack_htable_size,
nf_conntrack_max); nf_conntrack_max);
ret = nf_conntrack_expect_init();
if (ret < 0)
goto err_expect;
ret = nf_conntrack_acct_init();
if (ret < 0)
goto err_acct;
ret = nf_conntrack_tstamp_init();
if (ret < 0)
goto err_tstamp;
ret = nf_conntrack_ecache_init();
if (ret < 0)
goto err_ecache;
ret = nf_conntrack_timeout_init();
if (ret < 0)
goto err_timeout;
ret = nf_conntrack_helper_init();
if (ret < 0)
goto err_helper;
ret = nf_conntrack_labels_init();
if (ret < 0)
goto err_labels;
#ifdef CONFIG_NF_CONNTRACK_ZONES #ifdef CONFIG_NF_CONNTRACK_ZONES
ret = nf_ct_extend_register(&nf_ct_zone_extend); ret = nf_ct_extend_register(&nf_ct_zone_extend);
if (ret < 0) if (ret < 0)
goto err_extend; goto err_extend;
#endif #endif
ret = nf_conntrack_proto_init();
if (ret < 0)
goto err_proto;
/* Set up fake conntrack: to never be deleted, not in any hashes */ /* Set up fake conntrack: to never be deleted, not in any hashes */
for_each_possible_cpu(cpu) { for_each_possible_cpu(cpu) {
struct nf_conn *ct = &per_cpu(nf_conntrack_untracked, cpu); struct nf_conn *ct = &per_cpu(nf_conntrack_untracked, cpu);
...@@ -1516,12 +1555,38 @@ static int nf_conntrack_init_init_net(void) ...@@ -1516,12 +1555,38 @@ static int nf_conntrack_init_init_net(void)
nf_ct_untracked_status_or(IPS_CONFIRMED | IPS_UNTRACKED); nf_ct_untracked_status_or(IPS_CONFIRMED | IPS_UNTRACKED);
return 0; return 0;
err_proto:
#ifdef CONFIG_NF_CONNTRACK_ZONES #ifdef CONFIG_NF_CONNTRACK_ZONES
nf_ct_extend_unregister(&nf_ct_zone_extend);
err_extend: err_extend:
#endif #endif
nf_conntrack_labels_fini();
err_labels:
nf_conntrack_helper_fini();
err_helper:
nf_conntrack_timeout_fini();
err_timeout:
nf_conntrack_ecache_fini();
err_ecache:
nf_conntrack_tstamp_fini();
err_tstamp:
nf_conntrack_acct_fini();
err_acct:
nf_conntrack_expect_fini();
err_expect:
return ret; return ret;
} }
void nf_conntrack_init_end(void)
{
/* For use by REJECT target */
RCU_INIT_POINTER(ip_ct_attach, nf_conntrack_attach);
RCU_INIT_POINTER(nf_ct_destroy, destroy_conntrack);
/* Howto get NAT offsets */
RCU_INIT_POINTER(nf_ct_nat_offset, NULL);
}
/* /*
* We need to use special "null" values, not used in hash table * We need to use special "null" values, not used in hash table
*/ */
...@@ -1529,7 +1594,7 @@ static int nf_conntrack_init_init_net(void) ...@@ -1529,7 +1594,7 @@ static int nf_conntrack_init_init_net(void)
#define DYING_NULLS_VAL ((1<<30)+1) #define DYING_NULLS_VAL ((1<<30)+1)
#define TEMPLATE_NULLS_VAL ((1<<30)+2) #define TEMPLATE_NULLS_VAL ((1<<30)+2)
static int nf_conntrack_init_net(struct net *net) int nf_conntrack_init_net(struct net *net)
{ {
int ret; int ret;
...@@ -1565,35 +1630,36 @@ static int nf_conntrack_init_net(struct net *net) ...@@ -1565,35 +1630,36 @@ static int nf_conntrack_init_net(struct net *net)
printk(KERN_ERR "Unable to create nf_conntrack_hash\n"); printk(KERN_ERR "Unable to create nf_conntrack_hash\n");
goto err_hash; goto err_hash;
} }
ret = nf_conntrack_expect_init(net); ret = nf_conntrack_expect_pernet_init(net);
if (ret < 0) if (ret < 0)
goto err_expect; goto err_expect;
ret = nf_conntrack_acct_init(net); ret = nf_conntrack_acct_pernet_init(net);
if (ret < 0) if (ret < 0)
goto err_acct; goto err_acct;
ret = nf_conntrack_tstamp_init(net); ret = nf_conntrack_tstamp_pernet_init(net);
if (ret < 0) if (ret < 0)
goto err_tstamp; goto err_tstamp;
ret = nf_conntrack_ecache_init(net); ret = nf_conntrack_ecache_pernet_init(net);
if (ret < 0) if (ret < 0)
goto err_ecache; goto err_ecache;
ret = nf_conntrack_timeout_init(net); ret = nf_conntrack_helper_pernet_init(net);
if (ret < 0)
goto err_timeout;
ret = nf_conntrack_helper_init(net);
if (ret < 0) if (ret < 0)
goto err_helper; goto err_helper;
ret = nf_conntrack_proto_pernet_init(net);
if (ret < 0)
goto err_proto;
return 0; return 0;
err_proto:
nf_conntrack_helper_pernet_fini(net);
err_helper: err_helper:
nf_conntrack_timeout_fini(net); nf_conntrack_ecache_pernet_fini(net);
err_timeout:
nf_conntrack_ecache_fini(net);
err_ecache: err_ecache:
nf_conntrack_tstamp_fini(net); nf_conntrack_tstamp_pernet_fini(net);
err_tstamp: err_tstamp:
nf_conntrack_acct_fini(net); nf_conntrack_acct_pernet_fini(net);
err_acct: err_acct:
nf_conntrack_expect_fini(net); nf_conntrack_expect_pernet_fini(net);
err_expect: err_expect:
nf_ct_free_hashtable(net->ct.hash, net->ct.htable_size); nf_ct_free_hashtable(net->ct.hash, net->ct.htable_size);
err_hash: err_hash:
...@@ -1610,38 +1676,3 @@ s16 (*nf_ct_nat_offset)(const struct nf_conn *ct, ...@@ -1610,38 +1676,3 @@ s16 (*nf_ct_nat_offset)(const struct nf_conn *ct,
enum ip_conntrack_dir dir, enum ip_conntrack_dir dir,
u32 seq); u32 seq);
EXPORT_SYMBOL_GPL(nf_ct_nat_offset); EXPORT_SYMBOL_GPL(nf_ct_nat_offset);
int nf_conntrack_init(struct net *net)
{
int ret;
if (net_eq(net, &init_net)) {
ret = nf_conntrack_init_init_net();
if (ret < 0)
goto out_init_net;
}
ret = nf_conntrack_proto_init(net);
if (ret < 0)
goto out_proto;
ret = nf_conntrack_init_net(net);
if (ret < 0)
goto out_net;
if (net_eq(net, &init_net)) {
/* For use by REJECT target */
RCU_INIT_POINTER(ip_ct_attach, nf_conntrack_attach);
RCU_INIT_POINTER(nf_ct_destroy, destroy_conntrack);
/* Howto get NAT offsets */
RCU_INIT_POINTER(nf_ct_nat_offset, NULL);
}
return 0;
out_net:
nf_conntrack_proto_fini(net);
out_proto:
if (net_eq(net, &init_net))
nf_conntrack_cleanup_init_net();
out_init_net:
return ret;
}
...@@ -233,38 +233,27 @@ static void nf_conntrack_event_fini_sysctl(struct net *net) ...@@ -233,38 +233,27 @@ static void nf_conntrack_event_fini_sysctl(struct net *net)
} }
#endif /* CONFIG_SYSCTL */ #endif /* CONFIG_SYSCTL */
int nf_conntrack_ecache_init(struct net *net) int nf_conntrack_ecache_pernet_init(struct net *net)
{ {
int ret;
net->ct.sysctl_events = nf_ct_events; net->ct.sysctl_events = nf_ct_events;
net->ct.sysctl_events_retry_timeout = nf_ct_events_retry_timeout; net->ct.sysctl_events_retry_timeout = nf_ct_events_retry_timeout;
return nf_conntrack_event_init_sysctl(net);
}
if (net_eq(net, &init_net)) { void nf_conntrack_ecache_pernet_fini(struct net *net)
ret = nf_ct_extend_register(&event_extend); {
if (ret < 0) { nf_conntrack_event_fini_sysctl(net);
printk(KERN_ERR "nf_ct_event: Unable to register " }
"event extension.\n");
goto out_extend_register;
}
}
ret = nf_conntrack_event_init_sysctl(net); int nf_conntrack_ecache_init(void)
{
int ret = nf_ct_extend_register(&event_extend);
if (ret < 0) if (ret < 0)
goto out_sysctl; pr_err("nf_ct_event: Unable to register event extension.\n");
return 0;
out_sysctl:
if (net_eq(net, &init_net))
nf_ct_extend_unregister(&event_extend);
out_extend_register:
return ret; return ret;
} }
void nf_conntrack_ecache_fini(struct net *net) void nf_conntrack_ecache_fini(void)
{ {
nf_conntrack_event_fini_sysctl(net);
if (net_eq(net, &init_net))
nf_ct_extend_unregister(&event_extend); nf_ct_extend_unregister(&event_extend);
} }
...@@ -587,53 +587,50 @@ static void exp_proc_remove(struct net *net) ...@@ -587,53 +587,50 @@ static void exp_proc_remove(struct net *net)
module_param_named(expect_hashsize, nf_ct_expect_hsize, uint, 0400); module_param_named(expect_hashsize, nf_ct_expect_hsize, uint, 0400);
int nf_conntrack_expect_init(struct net *net) int nf_conntrack_expect_pernet_init(struct net *net)
{ {
int err = -ENOMEM; int err = -ENOMEM;
if (net_eq(net, &init_net)) {
if (!nf_ct_expect_hsize) {
nf_ct_expect_hsize = net->ct.htable_size / 256;
if (!nf_ct_expect_hsize)
nf_ct_expect_hsize = 1;
}
nf_ct_expect_max = nf_ct_expect_hsize * 4;
}
net->ct.expect_count = 0; net->ct.expect_count = 0;
net->ct.expect_hash = nf_ct_alloc_hashtable(&nf_ct_expect_hsize, 0); net->ct.expect_hash = nf_ct_alloc_hashtable(&nf_ct_expect_hsize, 0);
if (net->ct.expect_hash == NULL) if (net->ct.expect_hash == NULL)
goto err1; goto err1;
if (net_eq(net, &init_net)) {
nf_ct_expect_cachep = kmem_cache_create("nf_conntrack_expect",
sizeof(struct nf_conntrack_expect),
0, 0, NULL);
if (!nf_ct_expect_cachep)
goto err2;
}
err = exp_proc_init(net); err = exp_proc_init(net);
if (err < 0) if (err < 0)
goto err3; goto err2;
return 0; return 0;
err3:
if (net_eq(net, &init_net))
kmem_cache_destroy(nf_ct_expect_cachep);
err2: err2:
nf_ct_free_hashtable(net->ct.expect_hash, nf_ct_expect_hsize); nf_ct_free_hashtable(net->ct.expect_hash, nf_ct_expect_hsize);
err1: err1:
return err; return err;
} }
void nf_conntrack_expect_fini(struct net *net) void nf_conntrack_expect_pernet_fini(struct net *net)
{ {
exp_proc_remove(net); exp_proc_remove(net);
if (net_eq(net, &init_net)) { nf_ct_free_hashtable(net->ct.expect_hash, nf_ct_expect_hsize);
}
int nf_conntrack_expect_init(void)
{
if (!nf_ct_expect_hsize) {
nf_ct_expect_hsize = nf_conntrack_htable_size / 256;
if (!nf_ct_expect_hsize)
nf_ct_expect_hsize = 1;
}
nf_ct_expect_max = nf_ct_expect_hsize * 4;
nf_ct_expect_cachep = kmem_cache_create("nf_conntrack_expect",
sizeof(struct nf_conntrack_expect),
0, 0, NULL);
if (!nf_ct_expect_cachep)
return -ENOMEM;
return 0;
}
void nf_conntrack_expect_fini(void)
{
rcu_barrier(); /* Wait for call_rcu() before destroy */ rcu_barrier(); /* Wait for call_rcu() before destroy */
kmem_cache_destroy(nf_ct_expect_cachep); kmem_cache_destroy(nf_ct_expect_cachep);
}
nf_ct_free_hashtable(net->ct.expect_hash, nf_ct_expect_hsize);
} }
...@@ -423,44 +423,41 @@ static struct nf_ct_ext_type helper_extend __read_mostly = { ...@@ -423,44 +423,41 @@ static struct nf_ct_ext_type helper_extend __read_mostly = {
.id = NF_CT_EXT_HELPER, .id = NF_CT_EXT_HELPER,
}; };
int nf_conntrack_helper_init(struct net *net) int nf_conntrack_helper_pernet_init(struct net *net)
{ {
int err;
net->ct.auto_assign_helper_warned = false; net->ct.auto_assign_helper_warned = false;
net->ct.sysctl_auto_assign_helper = nf_ct_auto_assign_helper; net->ct.sysctl_auto_assign_helper = nf_ct_auto_assign_helper;
return nf_conntrack_helper_init_sysctl(net);
}
if (net_eq(net, &init_net)) { void nf_conntrack_helper_pernet_fini(struct net *net)
{
nf_conntrack_helper_fini_sysctl(net);
}
int nf_conntrack_helper_init(void)
{
int ret;
nf_ct_helper_hsize = 1; /* gets rounded up to use one page */ nf_ct_helper_hsize = 1; /* gets rounded up to use one page */
nf_ct_helper_hash = nf_ct_helper_hash =
nf_ct_alloc_hashtable(&nf_ct_helper_hsize, 0); nf_ct_alloc_hashtable(&nf_ct_helper_hsize, 0);
if (!nf_ct_helper_hash) if (!nf_ct_helper_hash)
return -ENOMEM; return -ENOMEM;
err = nf_ct_extend_register(&helper_extend); ret = nf_ct_extend_register(&helper_extend);
if (err < 0) if (ret < 0) {
goto err1; pr_err("nf_ct_helper: Unable to register helper extension.\n");
goto out_extend;
} }
err = nf_conntrack_helper_init_sysctl(net);
if (err < 0)
goto out_sysctl;
return 0; return 0;
out_extend:
out_sysctl:
if (net_eq(net, &init_net))
nf_ct_extend_unregister(&helper_extend);
err1:
nf_ct_free_hashtable(nf_ct_helper_hash, nf_ct_helper_hsize); nf_ct_free_hashtable(nf_ct_helper_hash, nf_ct_helper_hsize);
return err; return ret;
} }
void nf_conntrack_helper_fini(struct net *net) void nf_conntrack_helper_fini(void)
{ {
nf_conntrack_helper_fini_sysctl(net);
if (net_eq(net, &init_net)) {
nf_ct_extend_unregister(&helper_extend); nf_ct_extend_unregister(&helper_extend);
nf_ct_free_hashtable(nf_ct_helper_hash, nf_ct_helper_hsize); nf_ct_free_hashtable(nf_ct_helper_hash, nf_ct_helper_hsize);
}
} }
/*
* test/set flag bits stored in conntrack extension area.
*
* (C) 2013 Astaro GmbH & Co KG
*
* 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.
*/
#include <linux/ctype.h>
#include <linux/export.h>
#include <linux/jhash.h>
#include <linux/spinlock.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <net/netfilter/nf_conntrack_ecache.h>
#include <net/netfilter/nf_conntrack_labels.h>
static unsigned int label_bits(const struct nf_conn_labels *l)
{
unsigned int longs = l->words;
return longs * BITS_PER_LONG;
}
bool nf_connlabel_match(const struct nf_conn *ct, u16 bit)
{
struct nf_conn_labels *labels = nf_ct_labels_find(ct);
if (!labels)
return false;
return bit < label_bits(labels) && test_bit(bit, labels->bits);
}
EXPORT_SYMBOL_GPL(nf_connlabel_match);
int nf_connlabel_set(struct nf_conn *ct, u16 bit)
{
struct nf_conn_labels *labels = nf_ct_labels_find(ct);
if (!labels || bit >= label_bits(labels))
return -ENOSPC;
if (test_bit(bit, labels->bits))
return 0;
if (test_and_set_bit(bit, labels->bits))
nf_conntrack_event_cache(IPCT_LABEL, ct);
return 0;
}
EXPORT_SYMBOL_GPL(nf_connlabel_set);
#if IS_ENABLED(CONFIG_NF_CT_NETLINK)
static void replace_u32(u32 *address, u32 mask, u32 new)
{
u32 old, tmp;
do {
old = *address;
tmp = (old & mask) ^ new;
} while (cmpxchg(address, old, tmp) != old);
}
int nf_connlabels_replace(struct nf_conn *ct,
const u32 *data,
const u32 *mask, unsigned int words32)
{
struct nf_conn_labels *labels;
unsigned int size, i;
u32 *dst;
labels = nf_ct_labels_find(ct);
if (!labels)
return -ENOSPC;
size = labels->words * sizeof(long);
if (size < (words32 * sizeof(u32)))
words32 = size / sizeof(u32);
dst = (u32 *) labels->bits;
if (words32) {
for (i = 0; i < words32; i++)
replace_u32(&dst[i], mask ? ~mask[i] : 0, data[i]);
}
size /= sizeof(u32);
for (i = words32; i < size; i++) /* pad */
replace_u32(&dst[i], 0, 0);
nf_conntrack_event_cache(IPCT_LABEL, ct);
return 0;
}
EXPORT_SYMBOL_GPL(nf_connlabels_replace);
#endif
static struct nf_ct_ext_type labels_extend __read_mostly = {
.len = sizeof(struct nf_conn_labels),
.align = __alignof__(struct nf_conn_labels),
.id = NF_CT_EXT_LABELS,
};
int nf_conntrack_labels_init(void)
{
return nf_ct_extend_register(&labels_extend);
}
void nf_conntrack_labels_fini(void)
{
nf_ct_extend_unregister(&labels_extend);
}
...@@ -43,6 +43,7 @@ ...@@ -43,6 +43,7 @@
#include <net/netfilter/nf_conntrack_acct.h> #include <net/netfilter/nf_conntrack_acct.h>
#include <net/netfilter/nf_conntrack_zones.h> #include <net/netfilter/nf_conntrack_zones.h>
#include <net/netfilter/nf_conntrack_timestamp.h> #include <net/netfilter/nf_conntrack_timestamp.h>
#include <net/netfilter/nf_conntrack_labels.h>
#ifdef CONFIG_NF_NAT_NEEDED #ifdef CONFIG_NF_NAT_NEEDED
#include <net/netfilter/nf_nat_core.h> #include <net/netfilter/nf_nat_core.h>
#include <net/netfilter/nf_nat_l4proto.h> #include <net/netfilter/nf_nat_l4proto.h>
...@@ -323,6 +324,40 @@ ctnetlink_dump_secctx(struct sk_buff *skb, const struct nf_conn *ct) ...@@ -323,6 +324,40 @@ ctnetlink_dump_secctx(struct sk_buff *skb, const struct nf_conn *ct)
#define ctnetlink_dump_secctx(a, b) (0) #define ctnetlink_dump_secctx(a, b) (0)
#endif #endif
#ifdef CONFIG_NF_CONNTRACK_LABELS
static int ctnetlink_label_size(const struct nf_conn *ct)
{
struct nf_conn_labels *labels = nf_ct_labels_find(ct);
if (!labels)
return 0;
return nla_total_size(labels->words * sizeof(long));
}
static int
ctnetlink_dump_labels(struct sk_buff *skb, const struct nf_conn *ct)
{
struct nf_conn_labels *labels = nf_ct_labels_find(ct);
unsigned int len, i;
if (!labels)
return 0;
len = labels->words * sizeof(long);
i = 0;
do {
if (labels->bits[i] != 0)
return nla_put(skb, CTA_LABELS, len, labels->bits);
i++;
} while (i < labels->words);
return 0;
}
#else
#define ctnetlink_dump_labels(a, b) (0)
#define ctnetlink_label_size(a) (0)
#endif
#define master_tuple(ct) &(ct->master->tuplehash[IP_CT_DIR_ORIGINAL].tuple) #define master_tuple(ct) &(ct->master->tuplehash[IP_CT_DIR_ORIGINAL].tuple)
static inline int static inline int
...@@ -463,6 +498,7 @@ ctnetlink_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type, ...@@ -463,6 +498,7 @@ ctnetlink_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type,
ctnetlink_dump_helpinfo(skb, ct) < 0 || ctnetlink_dump_helpinfo(skb, ct) < 0 ||
ctnetlink_dump_mark(skb, ct) < 0 || ctnetlink_dump_mark(skb, ct) < 0 ||
ctnetlink_dump_secctx(skb, ct) < 0 || ctnetlink_dump_secctx(skb, ct) < 0 ||
ctnetlink_dump_labels(skb, ct) < 0 ||
ctnetlink_dump_id(skb, ct) < 0 || ctnetlink_dump_id(skb, ct) < 0 ||
ctnetlink_dump_use(skb, ct) < 0 || ctnetlink_dump_use(skb, ct) < 0 ||
ctnetlink_dump_master(skb, ct) < 0 || ctnetlink_dump_master(skb, ct) < 0 ||
...@@ -561,6 +597,7 @@ ctnetlink_nlmsg_size(const struct nf_conn *ct) ...@@ -561,6 +597,7 @@ ctnetlink_nlmsg_size(const struct nf_conn *ct)
+ nla_total_size(sizeof(u_int32_t)) /* CTA_MARK */ + nla_total_size(sizeof(u_int32_t)) /* CTA_MARK */
#endif #endif
+ ctnetlink_proto_size(ct) + ctnetlink_proto_size(ct)
+ ctnetlink_label_size(ct)
; ;
} }
...@@ -662,6 +699,9 @@ ctnetlink_conntrack_event(unsigned int events, struct nf_ct_event *item) ...@@ -662,6 +699,9 @@ ctnetlink_conntrack_event(unsigned int events, struct nf_ct_event *item)
&& ctnetlink_dump_secctx(skb, ct) < 0) && ctnetlink_dump_secctx(skb, ct) < 0)
goto nla_put_failure; goto nla_put_failure;
#endif #endif
if (events & (1 << IPCT_LABEL) &&
ctnetlink_dump_labels(skb, ct) < 0)
goto nla_put_failure;
if (events & (1 << IPCT_RELATED) && if (events & (1 << IPCT_RELATED) &&
ctnetlink_dump_master(skb, ct) < 0) ctnetlink_dump_master(skb, ct) < 0)
...@@ -921,6 +961,7 @@ ctnetlink_parse_help(const struct nlattr *attr, char **helper_name, ...@@ -921,6 +961,7 @@ ctnetlink_parse_help(const struct nlattr *attr, char **helper_name,
return 0; return 0;
} }
#define __CTA_LABELS_MAX_LENGTH ((XT_CONNLABEL_MAXBIT + 1) / BITS_PER_BYTE)
static const struct nla_policy ct_nla_policy[CTA_MAX+1] = { static const struct nla_policy ct_nla_policy[CTA_MAX+1] = {
[CTA_TUPLE_ORIG] = { .type = NLA_NESTED }, [CTA_TUPLE_ORIG] = { .type = NLA_NESTED },
[CTA_TUPLE_REPLY] = { .type = NLA_NESTED }, [CTA_TUPLE_REPLY] = { .type = NLA_NESTED },
...@@ -937,6 +978,10 @@ static const struct nla_policy ct_nla_policy[CTA_MAX+1] = { ...@@ -937,6 +978,10 @@ static const struct nla_policy ct_nla_policy[CTA_MAX+1] = {
[CTA_NAT_SEQ_ADJ_REPLY] = { .type = NLA_NESTED }, [CTA_NAT_SEQ_ADJ_REPLY] = { .type = NLA_NESTED },
[CTA_ZONE] = { .type = NLA_U16 }, [CTA_ZONE] = { .type = NLA_U16 },
[CTA_MARK_MASK] = { .type = NLA_U32 }, [CTA_MARK_MASK] = { .type = NLA_U32 },
[CTA_LABELS] = { .type = NLA_BINARY,
.len = __CTA_LABELS_MAX_LENGTH },
[CTA_LABELS_MASK] = { .type = NLA_BINARY,
.len = __CTA_LABELS_MAX_LENGTH },
}; };
static int static int
...@@ -1464,6 +1509,31 @@ ctnetlink_change_nat_seq_adj(struct nf_conn *ct, ...@@ -1464,6 +1509,31 @@ ctnetlink_change_nat_seq_adj(struct nf_conn *ct,
} }
#endif #endif
static int
ctnetlink_attach_labels(struct nf_conn *ct, const struct nlattr * const cda[])
{
#ifdef CONFIG_NF_CONNTRACK_LABELS
size_t len = nla_len(cda[CTA_LABELS]);
const void *mask = cda[CTA_LABELS_MASK];
if (len & (sizeof(u32)-1)) /* must be multiple of u32 */
return -EINVAL;
if (mask) {
if (nla_len(cda[CTA_LABELS_MASK]) == 0 ||
nla_len(cda[CTA_LABELS_MASK]) != len)
return -EINVAL;
mask = nla_data(cda[CTA_LABELS_MASK]);
}
len /= sizeof(u32);
return nf_connlabels_replace(ct, nla_data(cda[CTA_LABELS]), mask, len);
#else
return -EOPNOTSUPP;
#endif
}
static int static int
ctnetlink_change_conntrack(struct nf_conn *ct, ctnetlink_change_conntrack(struct nf_conn *ct,
const struct nlattr * const cda[]) const struct nlattr * const cda[])
...@@ -1510,6 +1580,11 @@ ctnetlink_change_conntrack(struct nf_conn *ct, ...@@ -1510,6 +1580,11 @@ ctnetlink_change_conntrack(struct nf_conn *ct,
return err; return err;
} }
#endif #endif
if (cda[CTA_LABELS]) {
err = ctnetlink_attach_labels(ct, cda);
if (err < 0)
return err;
}
return 0; return 0;
} }
...@@ -1598,6 +1673,8 @@ ctnetlink_create_conntrack(struct net *net, u16 zone, ...@@ -1598,6 +1673,8 @@ ctnetlink_create_conntrack(struct net *net, u16 zone,
nf_ct_acct_ext_add(ct, GFP_ATOMIC); nf_ct_acct_ext_add(ct, GFP_ATOMIC);
nf_ct_tstamp_ext_add(ct, GFP_ATOMIC); nf_ct_tstamp_ext_add(ct, GFP_ATOMIC);
nf_ct_ecache_ext_add(ct, 0, 0, GFP_ATOMIC); nf_ct_ecache_ext_add(ct, 0, 0, GFP_ATOMIC);
nf_ct_labels_ext_add(ct);
/* we must add conntrack extensions before confirmation. */ /* we must add conntrack extensions before confirmation. */
ct->status |= IPS_CONFIRMED; ct->status |= IPS_CONFIRMED;
...@@ -1716,6 +1793,10 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb, ...@@ -1716,6 +1793,10 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb,
else else
events = IPCT_NEW; events = IPCT_NEW;
if (cda[CTA_LABELS] &&
ctnetlink_attach_labels(ct, cda) == 0)
events |= (1 << IPCT_LABEL);
nf_conntrack_eventmask_report((1 << IPCT_REPLY) | nf_conntrack_eventmask_report((1 << IPCT_REPLY) |
(1 << IPCT_ASSURED) | (1 << IPCT_ASSURED) |
(1 << IPCT_HELPER) | (1 << IPCT_HELPER) |
...@@ -1983,6 +2064,8 @@ ctnetlink_nfqueue_build(struct sk_buff *skb, struct nf_conn *ct) ...@@ -1983,6 +2064,8 @@ ctnetlink_nfqueue_build(struct sk_buff *skb, struct nf_conn *ct)
if (ct->mark && ctnetlink_dump_mark(skb, ct) < 0) if (ct->mark && ctnetlink_dump_mark(skb, ct) < 0)
goto nla_put_failure; goto nla_put_failure;
#endif #endif
if (ctnetlink_dump_labels(skb, ct) < 0)
goto nla_put_failure;
rcu_read_unlock(); rcu_read_unlock();
return 0; return 0;
...@@ -2011,6 +2094,11 @@ ctnetlink_nfqueue_parse_ct(const struct nlattr *cda[], struct nf_conn *ct) ...@@ -2011,6 +2094,11 @@ ctnetlink_nfqueue_parse_ct(const struct nlattr *cda[], struct nf_conn *ct)
if (err < 0) if (err < 0)
return err; return err;
} }
if (cda[CTA_LABELS]) {
err = ctnetlink_attach_labels(ct, cda);
if (err < 0)
return err;
}
#if defined(CONFIG_NF_CONNTRACK_MARK) #if defined(CONFIG_NF_CONNTRACK_MARK)
if (cda[CTA_MARK]) if (cda[CTA_MARK])
ct->mark = ntohl(nla_get_be32(cda[CTA_MARK])); ct->mark = ntohl(nla_get_be32(cda[CTA_MARK]));
......
...@@ -212,8 +212,7 @@ static void nf_ct_l3proto_unregister_sysctl(struct net *net, ...@@ -212,8 +212,7 @@ static void nf_ct_l3proto_unregister_sysctl(struct net *net,
#endif #endif
} }
static int int nf_ct_l3proto_register(struct nf_conntrack_l3proto *proto)
nf_conntrack_l3proto_register_net(struct nf_conntrack_l3proto *proto)
{ {
int ret = 0; int ret = 0;
struct nf_conntrack_l3proto *old; struct nf_conntrack_l3proto *old;
...@@ -242,8 +241,9 @@ nf_conntrack_l3proto_register_net(struct nf_conntrack_l3proto *proto) ...@@ -242,8 +241,9 @@ nf_conntrack_l3proto_register_net(struct nf_conntrack_l3proto *proto)
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(nf_ct_l3proto_register);
int nf_conntrack_l3proto_register(struct net *net, int nf_ct_l3proto_pernet_register(struct net *net,
struct nf_conntrack_l3proto *proto) struct nf_conntrack_l3proto *proto)
{ {
int ret = 0; int ret = 0;
...@@ -254,22 +254,11 @@ int nf_conntrack_l3proto_register(struct net *net, ...@@ -254,22 +254,11 @@ int nf_conntrack_l3proto_register(struct net *net,
return ret; return ret;
} }
ret = nf_ct_l3proto_register_sysctl(net, proto); return nf_ct_l3proto_register_sysctl(net, proto);
if (ret < 0)
return ret;
if (net == &init_net) {
ret = nf_conntrack_l3proto_register_net(proto);
if (ret < 0)
nf_ct_l3proto_unregister_sysctl(net, proto);
}
return ret;
} }
EXPORT_SYMBOL_GPL(nf_conntrack_l3proto_register); EXPORT_SYMBOL_GPL(nf_ct_l3proto_pernet_register);
static void void nf_ct_l3proto_unregister(struct nf_conntrack_l3proto *proto)
nf_conntrack_l3proto_unregister_net(struct nf_conntrack_l3proto *proto)
{ {
BUG_ON(proto->l3proto >= AF_MAX); BUG_ON(proto->l3proto >= AF_MAX);
...@@ -283,19 +272,17 @@ nf_conntrack_l3proto_unregister_net(struct nf_conntrack_l3proto *proto) ...@@ -283,19 +272,17 @@ nf_conntrack_l3proto_unregister_net(struct nf_conntrack_l3proto *proto)
synchronize_rcu(); synchronize_rcu();
} }
EXPORT_SYMBOL_GPL(nf_ct_l3proto_unregister);
void nf_conntrack_l3proto_unregister(struct net *net, void nf_ct_l3proto_pernet_unregister(struct net *net,
struct nf_conntrack_l3proto *proto) struct nf_conntrack_l3proto *proto)
{ {
if (net == &init_net)
nf_conntrack_l3proto_unregister_net(proto);
nf_ct_l3proto_unregister_sysctl(net, proto); nf_ct_l3proto_unregister_sysctl(net, proto);
/* Remove all contrack entries for this protocol */ /* Remove all contrack entries for this protocol */
nf_ct_iterate_cleanup(net, kill_l3proto, proto); nf_ct_iterate_cleanup(net, kill_l3proto, proto);
} }
EXPORT_SYMBOL_GPL(nf_conntrack_l3proto_unregister); EXPORT_SYMBOL_GPL(nf_ct_l3proto_pernet_unregister);
static struct nf_proto_net *nf_ct_l4proto_net(struct net *net, static struct nf_proto_net *nf_ct_l4proto_net(struct net *net,
struct nf_conntrack_l4proto *l4proto) struct nf_conntrack_l4proto *l4proto)
...@@ -376,8 +363,7 @@ void nf_ct_l4proto_unregister_sysctl(struct net *net, ...@@ -376,8 +363,7 @@ void nf_ct_l4proto_unregister_sysctl(struct net *net,
/* FIXME: Allow NULL functions and sub in pointers to generic for /* FIXME: Allow NULL functions and sub in pointers to generic for
them. --RR */ them. --RR */
static int int nf_ct_l4proto_register(struct nf_conntrack_l4proto *l4proto)
nf_conntrack_l4proto_register_net(struct nf_conntrack_l4proto *l4proto)
{ {
int ret = 0; int ret = 0;
...@@ -431,8 +417,9 @@ nf_conntrack_l4proto_register_net(struct nf_conntrack_l4proto *l4proto) ...@@ -431,8 +417,9 @@ nf_conntrack_l4proto_register_net(struct nf_conntrack_l4proto *l4proto)
mutex_unlock(&nf_ct_proto_mutex); mutex_unlock(&nf_ct_proto_mutex);
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(nf_ct_l4proto_register);
int nf_conntrack_l4proto_register(struct net *net, int nf_ct_l4proto_pernet_register(struct net *net,
struct nf_conntrack_l4proto *l4proto) struct nf_conntrack_l4proto *l4proto)
{ {
int ret = 0; int ret = 0;
...@@ -452,22 +439,13 @@ int nf_conntrack_l4proto_register(struct net *net, ...@@ -452,22 +439,13 @@ int nf_conntrack_l4proto_register(struct net *net,
if (ret < 0) if (ret < 0)
goto out; goto out;
if (net == &init_net) {
ret = nf_conntrack_l4proto_register_net(l4proto);
if (ret < 0) {
nf_ct_l4proto_unregister_sysctl(net, pn, l4proto);
goto out;
}
}
pn->users++; pn->users++;
out: out:
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_register); EXPORT_SYMBOL_GPL(nf_ct_l4proto_pernet_register);
static void void nf_ct_l4proto_unregister(struct nf_conntrack_l4proto *l4proto)
nf_conntrack_l4proto_unregister_net(struct nf_conntrack_l4proto *l4proto)
{ {
BUG_ON(l4proto->l3proto >= PF_MAX); BUG_ON(l4proto->l3proto >= PF_MAX);
...@@ -482,15 +460,13 @@ nf_conntrack_l4proto_unregister_net(struct nf_conntrack_l4proto *l4proto) ...@@ -482,15 +460,13 @@ nf_conntrack_l4proto_unregister_net(struct nf_conntrack_l4proto *l4proto)
synchronize_rcu(); synchronize_rcu();
} }
EXPORT_SYMBOL_GPL(nf_ct_l4proto_unregister);
void nf_conntrack_l4proto_unregister(struct net *net, void nf_ct_l4proto_pernet_unregister(struct net *net,
struct nf_conntrack_l4proto *l4proto) struct nf_conntrack_l4proto *l4proto)
{ {
struct nf_proto_net *pn = NULL; struct nf_proto_net *pn = NULL;
if (net == &init_net)
nf_conntrack_l4proto_unregister_net(l4proto);
pn = nf_ct_l4proto_net(net, l4proto); pn = nf_ct_l4proto_net(net, l4proto);
if (pn == NULL) if (pn == NULL)
return; return;
...@@ -501,11 +477,10 @@ void nf_conntrack_l4proto_unregister(struct net *net, ...@@ -501,11 +477,10 @@ void nf_conntrack_l4proto_unregister(struct net *net,
/* Remove all contrack entries for this protocol */ /* Remove all contrack entries for this protocol */
nf_ct_iterate_cleanup(net, kill_l4proto, l4proto); nf_ct_iterate_cleanup(net, kill_l4proto, l4proto);
} }
EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_unregister); EXPORT_SYMBOL_GPL(nf_ct_l4proto_pernet_unregister);
int nf_conntrack_proto_init(struct net *net) int nf_conntrack_proto_pernet_init(struct net *net)
{ {
unsigned int i;
int err; int err;
struct nf_proto_net *pn = nf_ct_l4proto_net(net, struct nf_proto_net *pn = nf_ct_l4proto_net(net,
&nf_conntrack_l4proto_generic); &nf_conntrack_l4proto_generic);
...@@ -520,19 +495,12 @@ int nf_conntrack_proto_init(struct net *net) ...@@ -520,19 +495,12 @@ int nf_conntrack_proto_init(struct net *net)
if (err < 0) if (err < 0)
return err; return err;
if (net == &init_net) {
for (i = 0; i < AF_MAX; i++)
rcu_assign_pointer(nf_ct_l3protos[i],
&nf_conntrack_l3proto_generic);
}
pn->users++; pn->users++;
return 0; return 0;
} }
void nf_conntrack_proto_fini(struct net *net) void nf_conntrack_proto_pernet_fini(struct net *net)
{ {
unsigned int i;
struct nf_proto_net *pn = nf_ct_l4proto_net(net, struct nf_proto_net *pn = nf_ct_l4proto_net(net,
&nf_conntrack_l4proto_generic); &nf_conntrack_l4proto_generic);
...@@ -540,9 +508,21 @@ void nf_conntrack_proto_fini(struct net *net) ...@@ -540,9 +508,21 @@ void nf_conntrack_proto_fini(struct net *net)
nf_ct_l4proto_unregister_sysctl(net, nf_ct_l4proto_unregister_sysctl(net,
pn, pn,
&nf_conntrack_l4proto_generic); &nf_conntrack_l4proto_generic);
if (net == &init_net) { }
int nf_conntrack_proto_init(void)
{
unsigned int i;
for (i = 0; i < AF_MAX; i++)
rcu_assign_pointer(nf_ct_l3protos[i],
&nf_conntrack_l3proto_generic);
return 0;
}
void nf_conntrack_proto_fini(void)
{
unsigned int i;
/* free l3proto protocol tables */ /* free l3proto protocol tables */
for (i = 0; i < PF_MAX; i++) for (i = 0; i < PF_MAX; i++)
kfree(nf_ct_protos[i]); kfree(nf_ct_protos[i]);
}
} }
...@@ -935,32 +935,27 @@ static struct nf_conntrack_l4proto dccp_proto6 __read_mostly = { ...@@ -935,32 +935,27 @@ static struct nf_conntrack_l4proto dccp_proto6 __read_mostly = {
static __net_init int dccp_net_init(struct net *net) static __net_init int dccp_net_init(struct net *net)
{ {
int ret = 0; int ret = 0;
ret = nf_conntrack_l4proto_register(net, ret = nf_ct_l4proto_pernet_register(net, &dccp_proto4);
&dccp_proto4);
if (ret < 0) { if (ret < 0) {
pr_err("nf_conntrack_l4proto_dccp4 :protocol register failed.\n"); pr_err("nf_conntrack_dccp4: pernet registration failed.\n");
goto out; goto out;
} }
ret = nf_conntrack_l4proto_register(net, ret = nf_ct_l4proto_pernet_register(net, &dccp_proto6);
&dccp_proto6);
if (ret < 0) { if (ret < 0) {
pr_err("nf_conntrack_l4proto_dccp6 :protocol register failed.\n"); pr_err("nf_conntrack_dccp6: pernet registration failed.\n");
goto cleanup_dccp4; goto cleanup_dccp4;
} }
return 0; return 0;
cleanup_dccp4: cleanup_dccp4:
nf_conntrack_l4proto_unregister(net, nf_ct_l4proto_pernet_unregister(net, &dccp_proto4);
&dccp_proto4);
out: out:
return ret; return ret;
} }
static __net_exit void dccp_net_exit(struct net *net) static __net_exit void dccp_net_exit(struct net *net)
{ {
nf_conntrack_l4proto_unregister(net, nf_ct_l4proto_pernet_unregister(net, &dccp_proto6);
&dccp_proto6); nf_ct_l4proto_pernet_unregister(net, &dccp_proto4);
nf_conntrack_l4proto_unregister(net,
&dccp_proto4);
} }
static struct pernet_operations dccp_net_ops = { static struct pernet_operations dccp_net_ops = {
...@@ -972,11 +967,33 @@ static struct pernet_operations dccp_net_ops = { ...@@ -972,11 +967,33 @@ static struct pernet_operations dccp_net_ops = {
static int __init nf_conntrack_proto_dccp_init(void) static int __init nf_conntrack_proto_dccp_init(void)
{ {
return register_pernet_subsys(&dccp_net_ops); int ret;
ret = nf_ct_l4proto_register(&dccp_proto4);
if (ret < 0)
goto out_dccp4;
ret = nf_ct_l4proto_register(&dccp_proto6);
if (ret < 0)
goto out_dccp6;
ret = register_pernet_subsys(&dccp_net_ops);
if (ret < 0)
goto out_pernet;
return 0;
out_pernet:
nf_ct_l4proto_unregister(&dccp_proto6);
out_dccp6:
nf_ct_l4proto_unregister(&dccp_proto4);
out_dccp4:
return ret;
} }
static void __exit nf_conntrack_proto_dccp_fini(void) static void __exit nf_conntrack_proto_dccp_fini(void)
{ {
nf_ct_l4proto_unregister(&dccp_proto6);
nf_ct_l4proto_unregister(&dccp_proto4);
unregister_pernet_subsys(&dccp_net_ops); unregister_pernet_subsys(&dccp_net_ops);
} }
......
...@@ -397,15 +397,15 @@ static struct nf_conntrack_l4proto nf_conntrack_l4proto_gre4 __read_mostly = { ...@@ -397,15 +397,15 @@ static struct nf_conntrack_l4proto nf_conntrack_l4proto_gre4 __read_mostly = {
static int proto_gre_net_init(struct net *net) static int proto_gre_net_init(struct net *net)
{ {
int ret = 0; int ret = 0;
ret = nf_conntrack_l4proto_register(net, &nf_conntrack_l4proto_gre4); ret = nf_ct_l4proto_pernet_register(net, &nf_conntrack_l4proto_gre4);
if (ret < 0) if (ret < 0)
pr_err("nf_conntrack_l4proto_gre4 :protocol register failed.\n"); pr_err("nf_conntrack_gre4: pernet registration failed.\n");
return ret; return ret;
} }
static void proto_gre_net_exit(struct net *net) static void proto_gre_net_exit(struct net *net)
{ {
nf_conntrack_l4proto_unregister(net, &nf_conntrack_l4proto_gre4); nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_gre4);
nf_ct_gre_keymap_flush(net); nf_ct_gre_keymap_flush(net);
} }
...@@ -418,11 +418,26 @@ static struct pernet_operations proto_gre_net_ops = { ...@@ -418,11 +418,26 @@ static struct pernet_operations proto_gre_net_ops = {
static int __init nf_ct_proto_gre_init(void) static int __init nf_ct_proto_gre_init(void)
{ {
return register_pernet_subsys(&proto_gre_net_ops); int ret;
ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_gre4);
if (ret < 0)
goto out_gre4;
ret = register_pernet_subsys(&proto_gre_net_ops);
if (ret < 0)
goto out_pernet;
return 0;
out_pernet:
nf_ct_l4proto_unregister(&nf_conntrack_l4proto_gre4);
out_gre4:
return ret;
} }
static void __exit nf_ct_proto_gre_fini(void) static void __exit nf_ct_proto_gre_fini(void)
{ {
nf_ct_l4proto_unregister(&nf_conntrack_l4proto_gre4);
unregister_pernet_subsys(&proto_gre_net_ops); unregister_pernet_subsys(&proto_gre_net_ops);
} }
......
...@@ -853,33 +853,28 @@ static int sctp_net_init(struct net *net) ...@@ -853,33 +853,28 @@ static int sctp_net_init(struct net *net)
{ {
int ret = 0; int ret = 0;
ret = nf_conntrack_l4proto_register(net, ret = nf_ct_l4proto_pernet_register(net, &nf_conntrack_l4proto_sctp4);
&nf_conntrack_l4proto_sctp4);
if (ret < 0) { if (ret < 0) {
pr_err("nf_conntrack_l4proto_sctp4 :protocol register failed.\n"); pr_err("nf_conntrack_sctp4: pernet registration failed.\n");
goto out; goto out;
} }
ret = nf_conntrack_l4proto_register(net, ret = nf_ct_l4proto_pernet_register(net, &nf_conntrack_l4proto_sctp6);
&nf_conntrack_l4proto_sctp6);
if (ret < 0) { if (ret < 0) {
pr_err("nf_conntrack_l4proto_sctp6 :protocol register failed.\n"); pr_err("nf_conntrack_sctp6: pernet registration failed.\n");
goto cleanup_sctp4; goto cleanup_sctp4;
} }
return 0; return 0;
cleanup_sctp4: cleanup_sctp4:
nf_conntrack_l4proto_unregister(net, nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_sctp4);
&nf_conntrack_l4proto_sctp4);
out: out:
return ret; return ret;
} }
static void sctp_net_exit(struct net *net) static void sctp_net_exit(struct net *net)
{ {
nf_conntrack_l4proto_unregister(net, nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_sctp6);
&nf_conntrack_l4proto_sctp6); nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_sctp4);
nf_conntrack_l4proto_unregister(net,
&nf_conntrack_l4proto_sctp4);
} }
static struct pernet_operations sctp_net_ops = { static struct pernet_operations sctp_net_ops = {
...@@ -891,11 +886,33 @@ static struct pernet_operations sctp_net_ops = { ...@@ -891,11 +886,33 @@ static struct pernet_operations sctp_net_ops = {
static int __init nf_conntrack_proto_sctp_init(void) static int __init nf_conntrack_proto_sctp_init(void)
{ {
return register_pernet_subsys(&sctp_net_ops); int ret;
ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_sctp4);
if (ret < 0)
goto out_sctp4;
ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_sctp6);
if (ret < 0)
goto out_sctp6;
ret = register_pernet_subsys(&sctp_net_ops);
if (ret < 0)
goto out_pernet;
return 0;
out_pernet:
nf_ct_l4proto_unregister(&nf_conntrack_l4proto_sctp6);
out_sctp6:
nf_ct_l4proto_unregister(&nf_conntrack_l4proto_sctp4);
out_sctp4:
return ret;
} }
static void __exit nf_conntrack_proto_sctp_fini(void) static void __exit nf_conntrack_proto_sctp_fini(void)
{ {
nf_ct_l4proto_unregister(&nf_conntrack_l4proto_sctp6);
nf_ct_l4proto_unregister(&nf_conntrack_l4proto_sctp4);
unregister_pernet_subsys(&sctp_net_ops); unregister_pernet_subsys(&sctp_net_ops);
} }
......
...@@ -336,30 +336,28 @@ static int udplite_net_init(struct net *net) ...@@ -336,30 +336,28 @@ static int udplite_net_init(struct net *net)
{ {
int ret = 0; int ret = 0;
ret = nf_conntrack_l4proto_register(net, ret = nf_ct_l4proto_pernet_register(net, &nf_conntrack_l4proto_udplite4);
&nf_conntrack_l4proto_udplite4);
if (ret < 0) { if (ret < 0) {
pr_err("nf_conntrack_l4proto_udplite4 :protocol register failed.\n"); pr_err("nf_conntrack_udplite4: pernet registration failed.\n");
goto out; goto out;
} }
ret = nf_conntrack_l4proto_register(net, ret = nf_ct_l4proto_pernet_register(net, &nf_conntrack_l4proto_udplite6);
&nf_conntrack_l4proto_udplite6);
if (ret < 0) { if (ret < 0) {
pr_err("nf_conntrack_l4proto_udplite4 :protocol register failed.\n"); pr_err("nf_conntrack_udplite6: pernet registration failed.\n");
goto cleanup_udplite4; goto cleanup_udplite4;
} }
return 0; return 0;
cleanup_udplite4: cleanup_udplite4:
nf_conntrack_l4proto_unregister(net, &nf_conntrack_l4proto_udplite4); nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_udplite4);
out: out:
return ret; return ret;
} }
static void udplite_net_exit(struct net *net) static void udplite_net_exit(struct net *net)
{ {
nf_conntrack_l4proto_unregister(net, &nf_conntrack_l4proto_udplite6); nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_udplite6);
nf_conntrack_l4proto_unregister(net, &nf_conntrack_l4proto_udplite4); nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_udplite4);
} }
static struct pernet_operations udplite_net_ops = { static struct pernet_operations udplite_net_ops = {
...@@ -371,11 +369,33 @@ static struct pernet_operations udplite_net_ops = { ...@@ -371,11 +369,33 @@ static struct pernet_operations udplite_net_ops = {
static int __init nf_conntrack_proto_udplite_init(void) static int __init nf_conntrack_proto_udplite_init(void)
{ {
return register_pernet_subsys(&udplite_net_ops); int ret;
ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_udplite4);
if (ret < 0)
goto out_udplite4;
ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_udplite6);
if (ret < 0)
goto out_udplite6;
ret = register_pernet_subsys(&udplite_net_ops);
if (ret < 0)
goto out_pernet;
return 0;
out_pernet:
nf_ct_l4proto_unregister(&nf_conntrack_l4proto_udplite6);
out_udplite6:
nf_ct_l4proto_unregister(&nf_conntrack_l4proto_udplite4);
out_udplite4:
return ret;
} }
static void __exit nf_conntrack_proto_udplite_exit(void) static void __exit nf_conntrack_proto_udplite_exit(void)
{ {
nf_ct_l4proto_unregister(&nf_conntrack_l4proto_udplite6);
nf_ct_l4proto_unregister(&nf_conntrack_l4proto_udplite4);
unregister_pernet_subsys(&udplite_net_ops); unregister_pernet_subsys(&udplite_net_ops);
} }
......
...@@ -1440,8 +1440,25 @@ static int process_sip_request(struct sk_buff *skb, unsigned int protoff, ...@@ -1440,8 +1440,25 @@ static int process_sip_request(struct sk_buff *skb, unsigned int protoff,
{ {
enum ip_conntrack_info ctinfo; enum ip_conntrack_info ctinfo;
struct nf_conn *ct = nf_ct_get(skb, &ctinfo); struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
struct nf_ct_sip_master *ct_sip_info = nfct_help_data(ct);
enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
unsigned int matchoff, matchlen; unsigned int matchoff, matchlen;
unsigned int cseq, i; unsigned int cseq, i;
union nf_inet_addr addr;
__be16 port;
/* Many Cisco IP phones use a high source port for SIP requests, but
* listen for the response on port 5060. If we are the local
* router for one of these phones, save the port number from the
* Via: header so that nf_nat_sip can redirect the responses to
* the correct port.
*/
if (ct_sip_parse_header_uri(ct, *dptr, NULL, *datalen,
SIP_HDR_VIA_UDP, NULL, &matchoff,
&matchlen, &addr, &port) > 0 &&
port != ct->tuplehash[dir].tuple.src.u.udp.port &&
nf_inet_addr_cmp(&addr, &ct->tuplehash[dir].tuple.src.u3))
ct_sip_info->forced_dport = port;
for (i = 0; i < ARRAY_SIZE(sip_handlers); i++) { for (i = 0; i < ARRAY_SIZE(sip_handlers); i++) {
const struct sip_handler *handler; const struct sip_handler *handler;
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include <net/netfilter/nf_conntrack.h> #include <net/netfilter/nf_conntrack.h>
#include <net/netfilter/nf_conntrack_helper.h> #include <net/netfilter/nf_conntrack_helper.h>
#include <net/netfilter/nf_conntrack_expect.h> #include <net/netfilter/nf_conntrack_expect.h>
#include <linux/netfilter/nf_conntrack_snmp.h>
#define SNMP_PORT 161 #define SNMP_PORT 161
......
...@@ -472,13 +472,6 @@ static int nf_conntrack_standalone_init_sysctl(struct net *net) ...@@ -472,13 +472,6 @@ static int nf_conntrack_standalone_init_sysctl(struct net *net)
{ {
struct ctl_table *table; struct ctl_table *table;
if (net_eq(net, &init_net)) {
nf_ct_netfilter_header =
register_net_sysctl(&init_net, "net", nf_ct_netfilter_table);
if (!nf_ct_netfilter_header)
goto out;
}
table = kmemdup(nf_ct_sysctl_table, sizeof(nf_ct_sysctl_table), table = kmemdup(nf_ct_sysctl_table, sizeof(nf_ct_sysctl_table),
GFP_KERNEL); GFP_KERNEL);
if (!table) if (!table)
...@@ -502,10 +495,6 @@ static int nf_conntrack_standalone_init_sysctl(struct net *net) ...@@ -502,10 +495,6 @@ static int nf_conntrack_standalone_init_sysctl(struct net *net)
out_unregister_netfilter: out_unregister_netfilter:
kfree(table); kfree(table);
out_kmemdup: out_kmemdup:
if (net_eq(net, &init_net))
unregister_net_sysctl_table(nf_ct_netfilter_header);
out:
printk(KERN_ERR "nf_conntrack: can't register to sysctl.\n");
return -ENOMEM; return -ENOMEM;
} }
...@@ -513,8 +502,6 @@ static void nf_conntrack_standalone_fini_sysctl(struct net *net) ...@@ -513,8 +502,6 @@ static void nf_conntrack_standalone_fini_sysctl(struct net *net)
{ {
struct ctl_table *table; struct ctl_table *table;
if (net_eq(net, &init_net))
unregister_net_sysctl_table(nf_ct_netfilter_header);
table = net->ct.sysctl_header->ctl_table_arg; table = net->ct.sysctl_header->ctl_table_arg;
unregister_net_sysctl_table(net->ct.sysctl_header); unregister_net_sysctl_table(net->ct.sysctl_header);
kfree(table); kfree(table);
...@@ -530,51 +517,85 @@ static void nf_conntrack_standalone_fini_sysctl(struct net *net) ...@@ -530,51 +517,85 @@ static void nf_conntrack_standalone_fini_sysctl(struct net *net)
} }
#endif /* CONFIG_SYSCTL */ #endif /* CONFIG_SYSCTL */
static int nf_conntrack_net_init(struct net *net) static int nf_conntrack_pernet_init(struct net *net)
{ {
int ret; int ret;
ret = nf_conntrack_init(net); ret = nf_conntrack_init_net(net);
if (ret < 0) if (ret < 0)
goto out_init; goto out_init;
ret = nf_conntrack_standalone_init_proc(net); ret = nf_conntrack_standalone_init_proc(net);
if (ret < 0) if (ret < 0)
goto out_proc; goto out_proc;
net->ct.sysctl_checksum = 1; net->ct.sysctl_checksum = 1;
net->ct.sysctl_log_invalid = 0; net->ct.sysctl_log_invalid = 0;
ret = nf_conntrack_standalone_init_sysctl(net); ret = nf_conntrack_standalone_init_sysctl(net);
if (ret < 0) if (ret < 0)
goto out_sysctl; goto out_sysctl;
return 0; return 0;
out_sysctl: out_sysctl:
nf_conntrack_standalone_fini_proc(net); nf_conntrack_standalone_fini_proc(net);
out_proc: out_proc:
nf_conntrack_cleanup(net); nf_conntrack_cleanup_net(net);
out_init: out_init:
return ret; return ret;
} }
static void nf_conntrack_net_exit(struct net *net) static void nf_conntrack_pernet_exit(struct net *net)
{ {
nf_conntrack_standalone_fini_sysctl(net); nf_conntrack_standalone_fini_sysctl(net);
nf_conntrack_standalone_fini_proc(net); nf_conntrack_standalone_fini_proc(net);
nf_conntrack_cleanup(net); nf_conntrack_cleanup_net(net);
} }
static struct pernet_operations nf_conntrack_net_ops = { static struct pernet_operations nf_conntrack_net_ops = {
.init = nf_conntrack_net_init, .init = nf_conntrack_pernet_init,
.exit = nf_conntrack_net_exit, .exit = nf_conntrack_pernet_exit,
}; };
static int __init nf_conntrack_standalone_init(void) static int __init nf_conntrack_standalone_init(void)
{ {
return register_pernet_subsys(&nf_conntrack_net_ops); int ret = nf_conntrack_init_start();
if (ret < 0)
goto out_start;
#ifdef CONFIG_SYSCTL
nf_ct_netfilter_header =
register_net_sysctl(&init_net, "net", nf_ct_netfilter_table);
if (!nf_ct_netfilter_header) {
pr_err("nf_conntrack: can't register to sysctl.\n");
goto out_sysctl;
}
#endif
ret = register_pernet_subsys(&nf_conntrack_net_ops);
if (ret < 0)
goto out_pernet;
nf_conntrack_init_end();
return 0;
out_pernet:
#ifdef CONFIG_SYSCTL
unregister_net_sysctl_table(nf_ct_netfilter_header);
out_sysctl:
#endif
nf_conntrack_cleanup_end();
out_start:
return ret;
} }
static void __exit nf_conntrack_standalone_fini(void) static void __exit nf_conntrack_standalone_fini(void)
{ {
nf_conntrack_cleanup_start();
unregister_pernet_subsys(&nf_conntrack_net_ops); unregister_pernet_subsys(&nf_conntrack_net_ops);
#ifdef CONFIG_SYSCTL
unregister_net_sysctl_table(nf_ct_netfilter_header);
#endif
nf_conntrack_cleanup_end(); nf_conntrack_cleanup_end();
} }
......
...@@ -37,24 +37,15 @@ static struct nf_ct_ext_type timeout_extend __read_mostly = { ...@@ -37,24 +37,15 @@ static struct nf_ct_ext_type timeout_extend __read_mostly = {
.id = NF_CT_EXT_TIMEOUT, .id = NF_CT_EXT_TIMEOUT,
}; };
int nf_conntrack_timeout_init(struct net *net) int nf_conntrack_timeout_init(void)
{ {
int ret = 0; int ret = nf_ct_extend_register(&timeout_extend);
if (ret < 0)
if (net_eq(net, &init_net)) { pr_err("nf_ct_timeout: Unable to register timeout extension.\n");
ret = nf_ct_extend_register(&timeout_extend);
if (ret < 0) {
printk(KERN_ERR "nf_ct_timeout: Unable to register "
"timeout extension.\n");
return ret; return ret;
}
}
return 0;
} }
void nf_conntrack_timeout_fini(struct net *net) void nf_conntrack_timeout_fini(void)
{ {
if (net_eq(net, &init_net))
nf_ct_extend_unregister(&timeout_extend); nf_ct_extend_unregister(&timeout_extend);
} }
...@@ -88,37 +88,28 @@ static void nf_conntrack_tstamp_fini_sysctl(struct net *net) ...@@ -88,37 +88,28 @@ static void nf_conntrack_tstamp_fini_sysctl(struct net *net)
} }
#endif #endif
int nf_conntrack_tstamp_init(struct net *net) int nf_conntrack_tstamp_pernet_init(struct net *net)
{ {
int ret;
net->ct.sysctl_tstamp = nf_ct_tstamp; net->ct.sysctl_tstamp = nf_ct_tstamp;
return nf_conntrack_tstamp_init_sysctl(net);
}
if (net_eq(net, &init_net)) { void nf_conntrack_tstamp_pernet_fini(struct net *net)
ret = nf_ct_extend_register(&tstamp_extend); {
if (ret < 0) { nf_conntrack_tstamp_fini_sysctl(net);
printk(KERN_ERR "nf_ct_tstamp: Unable to register " nf_ct_extend_unregister(&tstamp_extend);
"extension\n"); }
goto out_extend_register;
}
}
ret = nf_conntrack_tstamp_init_sysctl(net); int nf_conntrack_tstamp_init(void)
{
int ret;
ret = nf_ct_extend_register(&tstamp_extend);
if (ret < 0) if (ret < 0)
goto out_sysctl; pr_err("nf_ct_tstamp: Unable to register extension\n");
return 0;
out_sysctl:
if (net_eq(net, &init_net))
nf_ct_extend_unregister(&tstamp_extend);
out_extend_register:
return ret; return ret;
} }
void nf_conntrack_tstamp_fini(struct net *net) void nf_conntrack_tstamp_fini(void)
{ {
nf_conntrack_tstamp_fini_sysctl(net);
if (net_eq(net, &init_net))
nf_ct_extend_unregister(&tstamp_extend); nf_ct_extend_unregister(&tstamp_extend);
} }
...@@ -95,6 +95,7 @@ static int map_addr(struct sk_buff *skb, unsigned int protoff, ...@@ -95,6 +95,7 @@ static int map_addr(struct sk_buff *skb, unsigned int protoff,
enum ip_conntrack_info ctinfo; enum ip_conntrack_info ctinfo;
struct nf_conn *ct = nf_ct_get(skb, &ctinfo); struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
struct nf_ct_sip_master *ct_sip_info = nfct_help_data(ct);
char buffer[INET6_ADDRSTRLEN + sizeof("[]:nnnnn")]; char buffer[INET6_ADDRSTRLEN + sizeof("[]:nnnnn")];
unsigned int buflen; unsigned int buflen;
union nf_inet_addr newaddr; union nf_inet_addr newaddr;
...@@ -107,7 +108,8 @@ static int map_addr(struct sk_buff *skb, unsigned int protoff, ...@@ -107,7 +108,8 @@ static int map_addr(struct sk_buff *skb, unsigned int protoff,
} else if (nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.dst.u3, addr) && } else if (nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.dst.u3, addr) &&
ct->tuplehash[dir].tuple.dst.u.udp.port == port) { ct->tuplehash[dir].tuple.dst.u.udp.port == port) {
newaddr = ct->tuplehash[!dir].tuple.src.u3; newaddr = ct->tuplehash[!dir].tuple.src.u3;
newport = ct->tuplehash[!dir].tuple.src.u.udp.port; newport = ct_sip_info->forced_dport ? :
ct->tuplehash[!dir].tuple.src.u.udp.port;
} else } else
return 1; return 1;
...@@ -144,6 +146,7 @@ static unsigned int nf_nat_sip(struct sk_buff *skb, unsigned int protoff, ...@@ -144,6 +146,7 @@ static unsigned int nf_nat_sip(struct sk_buff *skb, unsigned int protoff,
enum ip_conntrack_info ctinfo; enum ip_conntrack_info ctinfo;
struct nf_conn *ct = nf_ct_get(skb, &ctinfo); struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
struct nf_ct_sip_master *ct_sip_info = nfct_help_data(ct);
unsigned int coff, matchoff, matchlen; unsigned int coff, matchoff, matchlen;
enum sip_header_types hdr; enum sip_header_types hdr;
union nf_inet_addr addr; union nf_inet_addr addr;
...@@ -258,6 +261,21 @@ static unsigned int nf_nat_sip(struct sk_buff *skb, unsigned int protoff, ...@@ -258,6 +261,21 @@ static unsigned int nf_nat_sip(struct sk_buff *skb, unsigned int protoff,
!map_sip_addr(skb, protoff, dataoff, dptr, datalen, SIP_HDR_TO)) !map_sip_addr(skb, protoff, dataoff, dptr, datalen, SIP_HDR_TO))
return NF_DROP; 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))
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))
return NF_DROP;
}
return NF_ACCEPT; return NF_ACCEPT;
} }
...@@ -311,8 +329,10 @@ static unsigned int nf_nat_sip_expect(struct sk_buff *skb, unsigned int protoff, ...@@ -311,8 +329,10 @@ static unsigned int nf_nat_sip_expect(struct sk_buff *skb, unsigned int protoff,
enum ip_conntrack_info ctinfo; enum ip_conntrack_info ctinfo;
struct nf_conn *ct = nf_ct_get(skb, &ctinfo); struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
struct nf_ct_sip_master *ct_sip_info = nfct_help_data(ct);
union nf_inet_addr newaddr; union nf_inet_addr newaddr;
u_int16_t port; u_int16_t port;
__be16 srcport;
char buffer[INET6_ADDRSTRLEN + sizeof("[]:nnnnn")]; char buffer[INET6_ADDRSTRLEN + sizeof("[]:nnnnn")];
unsigned int buflen; unsigned int buflen;
...@@ -326,8 +346,9 @@ static unsigned int nf_nat_sip_expect(struct sk_buff *skb, unsigned int protoff, ...@@ -326,8 +346,9 @@ static unsigned int nf_nat_sip_expect(struct sk_buff *skb, unsigned int protoff,
/* If the signalling port matches the connection's source port in the /* If the signalling port matches the connection's source port in the
* original direction, try to use the destination port in the opposite * original direction, try to use the destination port in the opposite
* direction. */ * direction. */
if (exp->tuple.dst.u.udp.port == srcport = ct_sip_info->forced_dport ? :
ct->tuplehash[dir].tuple.src.u.udp.port) ct->tuplehash[dir].tuple.src.u.udp.port;
if (exp->tuple.dst.u.udp.port == srcport)
port = ntohs(ct->tuplehash[!dir].tuple.dst.u.udp.port); port = ntohs(ct->tuplehash[!dir].tuple.dst.u.udp.port);
else else
port = ntohs(exp->tuple.dst.u.udp.port); port = ntohs(exp->tuple.dst.u.udp.port);
......
/* Xtables module to match packets using a BPF filter.
* Copyright 2013 Google Inc.
* Written by Willem de Bruijn <willemb@google.com>
*
* 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.
*/
#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/filter.h>
#include <linux/netfilter/xt_bpf.h>
#include <linux/netfilter/x_tables.h>
MODULE_AUTHOR("Willem de Bruijn <willemb@google.com>");
MODULE_DESCRIPTION("Xtables: BPF filter match");
MODULE_LICENSE("GPL");
MODULE_ALIAS("ipt_bpf");
MODULE_ALIAS("ip6t_bpf");
static int bpf_mt_check(const struct xt_mtchk_param *par)
{
struct xt_bpf_info *info = par->matchinfo;
struct sock_fprog program;
program.len = info->bpf_program_num_elem;
program.filter = (struct sock_filter __user *) info->bpf_program;
if (sk_unattached_filter_create(&info->filter, &program)) {
pr_info("bpf: check failed: parse error\n");
return -EINVAL;
}
return 0;
}
static bool bpf_mt(const struct sk_buff *skb, struct xt_action_param *par)
{
const struct xt_bpf_info *info = par->matchinfo;
return SK_RUN_FILTER(info->filter, skb);
}
static void bpf_mt_destroy(const struct xt_mtdtor_param *par)
{
const struct xt_bpf_info *info = par->matchinfo;
sk_unattached_filter_destroy(info->filter);
}
static struct xt_match bpf_mt_reg __read_mostly = {
.name = "bpf",
.revision = 0,
.family = NFPROTO_UNSPEC,
.checkentry = bpf_mt_check,
.match = bpf_mt,
.destroy = bpf_mt_destroy,
.matchsize = sizeof(struct xt_bpf_info),
.me = THIS_MODULE,
};
static int __init bpf_mt_init(void)
{
return xt_register_match(&bpf_mt_reg);
}
static void __exit bpf_mt_exit(void)
{
xt_unregister_match(&bpf_mt_reg);
}
module_init(bpf_mt_init);
module_exit(bpf_mt_exit);
/*
* (C) 2013 Astaro GmbH & Co KG
*
* 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.
*/
#include <linux/module.h>
#include <linux/skbuff.h>
#include <net/netfilter/nf_conntrack.h>
#include <net/netfilter/nf_conntrack_labels.h>
#include <linux/netfilter/x_tables.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Florian Westphal <fw@strlen.de>");
MODULE_DESCRIPTION("Xtables: add/match connection trackling labels");
MODULE_ALIAS("ipt_connlabel");
MODULE_ALIAS("ip6t_connlabel");
static bool
connlabel_mt(const struct sk_buff *skb, struct xt_action_param *par)
{
const struct xt_connlabel_mtinfo *info = par->matchinfo;
enum ip_conntrack_info ctinfo;
struct nf_conn *ct;
bool invert = info->options & XT_CONNLABEL_OP_INVERT;
ct = nf_ct_get(skb, &ctinfo);
if (ct == NULL || nf_ct_is_untracked(ct))
return invert;
if (info->options & XT_CONNLABEL_OP_SET)
return (nf_connlabel_set(ct, info->bit) == 0) ^ invert;
return nf_connlabel_match(ct, info->bit) ^ invert;
}
static int connlabel_mt_check(const struct xt_mtchk_param *par)
{
const int options = XT_CONNLABEL_OP_INVERT |
XT_CONNLABEL_OP_SET;
struct xt_connlabel_mtinfo *info = par->matchinfo;
int ret;
size_t words;
if (info->bit > XT_CONNLABEL_MAXBIT)
return -ERANGE;
if (info->options & ~options) {
pr_err("Unknown options in mask %x\n", info->options);
return -EINVAL;
}
ret = nf_ct_l3proto_try_module_get(par->family);
if (ret < 0) {
pr_info("cannot load conntrack support for proto=%u\n",
par->family);
return ret;
}
par->net->ct.labels_used++;
words = BITS_TO_LONGS(info->bit+1);
if (words > par->net->ct.label_words)
par->net->ct.label_words = words;
return ret;
}
static void connlabel_mt_destroy(const struct xt_mtdtor_param *par)
{
par->net->ct.labels_used--;
if (par->net->ct.labels_used == 0)
par->net->ct.label_words = 0;
nf_ct_l3proto_module_put(par->family);
}
static struct xt_match connlabels_mt_reg __read_mostly = {
.name = "connlabel",
.family = NFPROTO_UNSPEC,
.checkentry = connlabel_mt_check,
.match = connlabel_mt,
.matchsize = sizeof(struct xt_connlabel_mtinfo),
.destroy = connlabel_mt_destroy,
.me = THIS_MODULE,
};
static int __init connlabel_mt_init(void)
{
return xt_register_match(&connlabels_mt_reg);
}
static void __exit connlabel_mt_exit(void)
{
xt_unregister_match(&connlabels_mt_reg);
}
module_init(connlabel_mt_init);
module_exit(connlabel_mt_exit);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment