int set_optmin;
int set_optmax;
int (*set)(struct sock *sk, int optval, void __user *user, unsigned int len);
+#ifdef CONFIG_COMPAT
int (*compat_set)(struct sock *sk, int optval,
void __user *user, unsigned int len);
-
+#endif
int get_optmin;
int get_optmax;
int (*get)(struct sock *sk, int optval, void __user *user, int *len);
+#ifdef CONFIG_COMPAT
int (*compat_get)(struct sock *sk, int optval,
void __user *user, int *len);
-
+#endif
/* Use the module struct to lock set/get code in place */
struct module *owner;
};
unsigned int len);
int nf_getsockopt(struct sock *sk, u_int8_t pf, int optval, char __user *opt,
int *len);
-
+#ifdef CONFIG_COMPAT
int compat_nf_setsockopt(struct sock *sk, u_int8_t pf, int optval,
char __user *opt, unsigned int len);
int compat_nf_getsockopt(struct sock *sk, u_int8_t pf, int optval,
char __user *opt, int *len);
+#endif
/* Call this before modifying an existing packet: ensures it is
modifiable and linear to the point you care about (writable_len).
header-y += xt_CLASSIFY.h
header-y += xt_CONNMARK.h
header-y += xt_CONNSECMARK.h
+header-y += xt_CT.h
header-y += xt_DSCP.h
header-y += xt_LED.h
header-y += xt_MARK.h
/* Connection has fixed timeout. */
IPS_FIXED_TIMEOUT_BIT = 10,
IPS_FIXED_TIMEOUT = (1 << IPS_FIXED_TIMEOUT_BIT),
+
+ /* Conntrack is a template */
+ IPS_TEMPLATE_BIT = 11,
+ IPS_TEMPLATE = (1 << IPS_TEMPLATE_BIT),
+};
+
+/* Connection tracking event types */
+enum ip_conntrack_events {
+ IPCT_NEW, /* new conntrack */
+ IPCT_RELATED, /* related conntrack */
+ IPCT_DESTROY, /* destroyed conntrack */
+ IPCT_REPLY, /* connection has seen two-way traffic */
+ IPCT_ASSURED, /* connection status has changed to assured */
+ IPCT_PROTOINFO, /* protocol information has changed */
+ IPCT_HELPER, /* new helper has been set */
+ IPCT_MARK, /* new mark has been set */
+ IPCT_NATSEQADJ, /* NAT is doing sequence adjustment */
+ IPCT_SECMARK, /* new security mark has been set */
+};
+
+enum ip_conntrack_expect_events {
+ IPEXP_NEW, /* new expectation */
};
#ifdef __KERNEL__
extern int nfnetlink_subsys_register(const struct nfnetlink_subsystem *n);
extern int nfnetlink_subsys_unregister(const struct nfnetlink_subsystem *n);
-extern int nfnetlink_has_listeners(unsigned int group);
-extern int nfnetlink_send(struct sk_buff *skb, u32 pid, unsigned group,
+extern int nfnetlink_has_listeners(struct net *net, unsigned int group);
+extern int nfnetlink_send(struct sk_buff *skb, struct net *net, u32 pid, unsigned group,
int echo, gfp_t flags);
-extern void nfnetlink_set_err(u32 pid, u32 group, int error);
-extern int nfnetlink_unicast(struct sk_buff *skb, u_int32_t pid, int flags);
+extern void nfnetlink_set_err(struct net *net, u32 pid, u32 group, int error);
+extern int nfnetlink_unicast(struct sk_buff *skb, struct net *net, u_int32_t pid, int flags);
extern void nfnl_lock(void);
extern void nfnl_unlock(void);
* @hook_mask: via which hooks the new rule is reachable
*/
struct xt_mtchk_param {
+ struct net *net;
const char *table;
const void *entryinfo;
const struct xt_match *match;
/* Match destructor parameters */
struct xt_mtdtor_param {
+ struct net *net;
const struct xt_match *match;
void *matchinfo;
u_int8_t family;
* Other fields see above.
*/
struct xt_tgchk_param {
+ struct net *net;
const char *table;
const void *entryinfo;
const struct xt_target *target;
/* Target destructor parameters */
struct xt_tgdtor_param {
+ struct net *net;
const struct xt_target *target;
void *targinfo;
u_int8_t family;
/* Called when entry of this type deleted. */
void (*destroy)(const struct xt_mtdtor_param *);
-
+#ifdef CONFIG_COMPAT
/* Called when userspace align differs from kernel space one */
void (*compat_from_user)(void *dst, void *src);
int (*compat_to_user)(void __user *dst, void *src);
-
+#endif
/* Set this to THIS_MODULE if you are a module, otherwise NULL */
struct module *me;
const char *table;
unsigned int matchsize;
+#ifdef CONFIG_COMPAT
unsigned int compatsize;
+#endif
unsigned int hooks;
unsigned short proto;
/* Called when entry of this type deleted. */
void (*destroy)(const struct xt_tgdtor_param *);
-
+#ifdef CONFIG_COMPAT
/* Called when userspace align differs from kernel space one */
void (*compat_from_user)(void *dst, void *src);
int (*compat_to_user)(void __user *dst, void *src);
-
+#endif
/* Set this to THIS_MODULE if you are a module, otherwise NULL */
struct module *me;
const char *table;
unsigned int targetsize;
+#ifdef CONFIG_COMPAT
unsigned int compatsize;
+#endif
unsigned int hooks;
unsigned short proto;
--- /dev/null
+#ifndef _XT_CT_H
+#define _XT_CT_H
+
+#define XT_CT_NOTRACK 0x1
+
+struct xt_ct_target_info {
+ u_int16_t flags;
+ u_int16_t __unused;
+ u_int32_t ct_events;
+ u_int32_t exp_events;
+ char helper[16];
+
+ /* Used internally by the kernel */
+ struct nf_conn *ct __attribute__((aligned(8)));
+};
+
+#endif /* _XT_CT_H */
~(__alignof__(struct ebt_replace)-1))
extern struct ebt_table *ebt_register_table(struct net *net,
const struct ebt_table *table);
-extern void ebt_unregister_table(struct ebt_table *table);
+extern void ebt_unregister_table(struct net *net, struct ebt_table *table);
extern unsigned int ebt_do_table(unsigned int hook, struct sk_buff *skb,
const struct net_device *in, const struct net_device *out,
struct ebt_table *table);
extern struct xt_table *ipt_register_table(struct net *net,
const struct xt_table *table,
const struct ipt_replace *repl);
-extern void ipt_unregister_table(struct xt_table *table);
+extern void ipt_unregister_table(struct net *net, struct xt_table *table);
/* Standard entry. */
struct ipt_standard {
extern struct xt_table *ip6t_register_table(struct net *net,
const struct xt_table *table,
const struct ip6t_replace *repl);
-extern void ip6t_unregister_table(struct xt_table *table);
+extern void ip6t_unregister_table(struct net *net, struct xt_table *table);
extern unsigned int ip6t_do_table(struct sk_buff *skb,
unsigned int hook,
const struct net_device *in,
#include <linux/ipv6.h> /* for struct ipv6hdr */
#include <net/ipv6.h> /* for ipv6_addr_copy */
+
+/* Connections' size value needed by ip_vs_ctl.c */
+extern int ip_vs_conn_tab_size;
+
+
struct ip_vs_iphdr {
int len;
__u8 protocol;
* (from ip_vs_conn.c)
*/
-/*
- * IPVS connection entry hash table
- */
-#ifndef CONFIG_IP_VS_TAB_BITS
-#define CONFIG_IP_VS_TAB_BITS 12
-#endif
-
-#define IP_VS_CONN_TAB_BITS CONFIG_IP_VS_TAB_BITS
-#define IP_VS_CONN_TAB_SIZE (1 << IP_VS_CONN_TAB_BITS)
-#define IP_VS_CONN_TAB_MASK (IP_VS_CONN_TAB_SIZE - 1)
-
enum {
IP_VS_DIR_INPUT = 0,
IP_VS_DIR_OUTPUT,
int ip6_frag_nqueues(struct net *net);
int ip6_frag_mem(struct net *net);
+#define IPV6_FRAG_HIGH_THRESH 262144 /* == 256*1024 */
+#define IPV6_FRAG_LOW_THRESH 196608 /* == 192*1024 */
#define IPV6_FRAG_TIMEOUT (60*HZ) /* 60 seconds */
extern int __ipv6_addr_type(const struct in6_addr *addr);
#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
struct netns_ct ct;
#endif
+ struct sock *nfnl;
+ struct sock *nfnl_stash;
#endif
#ifdef CONFIG_XFRM
struct netns_xfrm xfrm;
const struct nf_conntrack_tuple *repl,
gfp_t gfp);
+static inline int nf_ct_is_template(const struct nf_conn *ct)
+{
+ return test_bit(IPS_TEMPLATE_BIT, &ct->status);
+}
+
/* It's confirmed if it is, or has been in the hash table. */
static inline int nf_ct_is_confirmed(struct nf_conn *ct)
{
#include <linux/netfilter/nf_conntrack_tuple_common.h>
#include <net/netfilter/nf_conntrack_extend.h>
-/* Connection tracking event types */
-enum ip_conntrack_events {
- IPCT_NEW = 0, /* new conntrack */
- IPCT_RELATED = 1, /* related conntrack */
- IPCT_DESTROY = 2, /* destroyed conntrack */
- IPCT_STATUS = 3, /* status has changed */
- IPCT_PROTOINFO = 4, /* protocol information has changed */
- IPCT_HELPER = 5, /* new helper has been set */
- IPCT_MARK = 6, /* new mark has been set */
- IPCT_NATSEQADJ = 7, /* NAT is doing sequence adjustment */
- IPCT_SECMARK = 8, /* new security mark has been set */
-};
-
-enum ip_conntrack_expect_events {
- IPEXP_NEW = 0, /* new expectation */
-};
-
struct nf_conntrack_ecache {
- unsigned long cache; /* bitops want long */
- unsigned long missed; /* missed events */
- u32 pid; /* netlink pid of destroyer */
+ unsigned long cache; /* bitops want long */
+ unsigned long missed; /* missed events */
+ u16 ctmask; /* bitmask of ct events to be delivered */
+ u16 expmask; /* bitmask of expect events to be delivered */
+ u32 pid; /* netlink pid of destroyer */
};
static inline struct nf_conntrack_ecache *
}
static inline struct nf_conntrack_ecache *
-nf_ct_ecache_ext_add(struct nf_conn *ct, gfp_t gfp)
+nf_ct_ecache_ext_add(struct nf_conn *ct, u16 ctmask, u16 expmask, gfp_t gfp)
{
struct net *net = nf_ct_net(ct);
+ struct nf_conntrack_ecache *e;
- if (!net->ct.sysctl_events)
+ if (!ctmask && !expmask && net->ct.sysctl_events) {
+ ctmask = ~0;
+ expmask = ~0;
+ }
+ if (!ctmask && !expmask)
return NULL;
- return nf_ct_ext_add(ct, NF_CT_EXT_ECACHE, gfp);
+ e = nf_ct_ext_add(ct, NF_CT_EXT_ECACHE, gfp);
+ if (e) {
+ e->ctmask = ctmask;
+ e->expmask = expmask;
+ }
+ return e;
};
#ifdef CONFIG_NF_CONNTRACK_EVENTS
if (e == NULL)
return;
+ if (!(e->ctmask & (1 << event)))
+ return;
+
set_bit(event, &e->cache);
}
int report)
{
int ret = 0;
- struct net *net = nf_ct_net(ct);
struct nf_ct_event_notifier *notify;
struct nf_conntrack_ecache *e;
if (notify == NULL)
goto out_unlock;
- if (!net->ct.sysctl_events)
- goto out_unlock;
-
e = nf_ct_ecache_find(ct);
if (e == NULL)
goto out_unlock;
/* This is a resent of a destroy event? If so, skip missed */
unsigned long missed = e->pid ? 0 : e->missed;
+ if (!((eventmask | missed) & e->ctmask))
+ goto out_unlock;
+
ret = notify->fcn(eventmask | missed, &item);
if (unlikely(ret < 0 || missed)) {
spin_lock_bh(&ct->lock);
u32 pid,
int report)
{
- struct net *net = nf_ct_exp_net(exp);
struct nf_exp_event_notifier *notify;
+ struct nf_conntrack_ecache *e;
rcu_read_lock();
notify = rcu_dereference(nf_expect_event_cb);
if (notify == NULL)
goto out_unlock;
- if (!net->ct.sysctl_events)
+ e = nf_ct_ecache_find(exp->master);
+ if (e == NULL)
goto out_unlock;
- {
+ if (e->expmask & (1 << event)) {
struct nf_exp_event item = {
.exp = exp,
.pid = pid,
};
extern struct nf_conntrack_helper *
-__nf_conntrack_helper_find_byname(const char *name);
+__nf_conntrack_helper_find(const char *name, u16 l3num, u8 protonum);
+
+extern struct nf_conntrack_helper *
+nf_conntrack_helper_try_module_get(const char *name, u16 l3num, u8 protonum);
extern int nf_conntrack_helper_register(struct nf_conntrack_helper *);
extern void nf_conntrack_helper_unregister(struct nf_conntrack_helper *);
extern struct nf_conn_help *nf_ct_helper_ext_add(struct nf_conn *ct, gfp_t gfp);
-extern int __nf_ct_try_assign_helper(struct nf_conn *ct, gfp_t flags);
+extern int __nf_ct_try_assign_helper(struct nf_conn *ct, struct nf_conn *tmpl,
+ gfp_t flags);
extern void nf_ct_helper_destroy(struct nf_conn *ct);
struct xt_table *iptable_mangle;
struct xt_table *iptable_raw;
struct xt_table *arptable_filter;
+#ifdef CONFIG_SECURITY
struct xt_table *iptable_security;
+#endif
struct xt_table *nat_table;
struct hlist_head *nat_bysource;
unsigned int nat_htable_size;
struct xt_table *ip6table_filter;
struct xt_table *ip6table_mangle;
struct xt_table *ip6table_raw;
+#ifdef CONFIG_SECURITY
struct xt_table *ip6table_security;
+#endif
#endif
struct rt6_info *ip6_null_entry;
struct rt6_statistics *rt6_stats;
static void __net_exit broute_net_exit(struct net *net)
{
- ebt_unregister_table(net->xt.broute_table);
+ ebt_unregister_table(net, net->xt.broute_table);
}
static struct pernet_operations broute_net_ops = {
static void __net_exit frame_filter_net_exit(struct net *net)
{
- ebt_unregister_table(net->xt.frame_filter);
+ ebt_unregister_table(net, net->xt.frame_filter);
}
static struct pernet_operations frame_filter_net_ops = {
static void __net_exit frame_nat_net_exit(struct net *net)
{
- ebt_unregister_table(net->xt.frame_nat);
+ ebt_unregister_table(net, net->xt.frame_nat);
}
static struct pernet_operations frame_nat_net_ops = {
}
static inline int
-ebt_cleanup_match(struct ebt_entry_match *m, unsigned int *i)
+ebt_cleanup_match(struct ebt_entry_match *m, struct net *net, unsigned int *i)
{
struct xt_mtdtor_param par;
if (i && (*i)-- == 0)
return 1;
+ par.net = net;
par.match = m->u.match;
par.matchinfo = m->data;
par.family = NFPROTO_BRIDGE;
}
static inline int
-ebt_cleanup_watcher(struct ebt_entry_watcher *w, unsigned int *i)
+ebt_cleanup_watcher(struct ebt_entry_watcher *w, struct net *net, unsigned int *i)
{
struct xt_tgdtor_param par;
if (i && (*i)-- == 0)
return 1;
+ par.net = net;
par.target = w->u.watcher;
par.targinfo = w->data;
par.family = NFPROTO_BRIDGE;
}
static inline int
-ebt_cleanup_entry(struct ebt_entry *e, unsigned int *cnt)
+ebt_cleanup_entry(struct ebt_entry *e, struct net *net, unsigned int *cnt)
{
struct xt_tgdtor_param par;
struct ebt_entry_target *t;
/* we're done */
if (cnt && (*cnt)-- == 0)
return 1;
- EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, NULL);
- EBT_MATCH_ITERATE(e, ebt_cleanup_match, NULL);
+ EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, net, NULL);
+ EBT_MATCH_ITERATE(e, ebt_cleanup_match, net, NULL);
t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
+ par.net = net;
par.target = t->u.target;
par.targinfo = t->data;
par.family = NFPROTO_BRIDGE;
}
static inline int
-ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo,
+ebt_check_entry(struct ebt_entry *e,
+ struct net *net,
+ struct ebt_table_info *newinfo,
const char *name, unsigned int *cnt,
struct ebt_cl_stack *cl_s, unsigned int udc_cnt)
{
}
i = 0;
+ mtpar.net = tgpar.net = net;
mtpar.table = tgpar.table = name;
mtpar.entryinfo = tgpar.entryinfo = e;
mtpar.hook_mask = tgpar.hook_mask = hookmask;
(*cnt)++;
return 0;
cleanup_watchers:
- EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, &j);
+ EBT_WATCHER_ITERATE(e, ebt_cleanup_watcher, net, &j);
cleanup_matches:
- EBT_MATCH_ITERATE(e, ebt_cleanup_match, &i);
+ EBT_MATCH_ITERATE(e, ebt_cleanup_match, net, &i);
return ret;
}
}
/* do the parsing of the table/chains/entries/matches/watchers/targets, heh */
-static int translate_table(char *name, struct ebt_table_info *newinfo)
+static int translate_table(struct net *net, char *name,
+ struct ebt_table_info *newinfo)
{
unsigned int i, j, k, udc_cnt;
int ret;
/* used to know what we need to clean up if something goes wrong */
i = 0;
ret = EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
- ebt_check_entry, newinfo, name, &i, cl_s, udc_cnt);
+ ebt_check_entry, net, newinfo, name, &i, cl_s, udc_cnt);
if (ret != 0) {
EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
- ebt_cleanup_entry, &i);
+ ebt_cleanup_entry, net, &i);
}
vfree(cl_s);
return ret;
if (ret != 0)
goto free_counterstmp;
- ret = translate_table(tmp.name, newinfo);
+ ret = translate_table(net, tmp.name, newinfo);
if (ret != 0)
goto free_counterstmp;
/* decrease module count and free resources */
EBT_ENTRY_ITERATE(table->entries, table->entries_size,
- ebt_cleanup_entry, NULL);
+ ebt_cleanup_entry, net, NULL);
vfree(table->entries);
if (table->chainstack) {
mutex_unlock(&ebt_mutex);
free_iterate:
EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
- ebt_cleanup_entry, NULL);
+ ebt_cleanup_entry, net, NULL);
free_counterstmp:
vfree(counterstmp);
/* can be initialized in translate_table() */
newinfo->hook_entry[i] = p +
((char *)repl->hook_entry[i] - repl->entries);
}
- ret = translate_table(repl->name, newinfo);
+ ret = translate_table(net, repl->name, newinfo);
if (ret != 0) {
BUGPRINT("Translate_table failed\n");
goto free_chainstack;
return ERR_PTR(ret);
}
-void ebt_unregister_table(struct ebt_table *table)
+void ebt_unregister_table(struct net *net, struct ebt_table *table)
{
int i;
list_del(&table->list);
mutex_unlock(&ebt_mutex);
EBT_ENTRY_ITERATE(table->private->entries, table->private->entries_size,
- ebt_cleanup_entry, NULL);
+ ebt_cleanup_entry, net, NULL);
if (table->private->nentries)
module_put(table->me);
vfree(table->private->entries);
}
static int
-cleanup_match(struct ipt_entry_match *m, unsigned int *i)
+cleanup_match(struct ipt_entry_match *m, struct net *net, unsigned int *i)
{
struct xt_mtdtor_param par;
if (i && (*i)-- == 0)
return 1;
+ par.net = net;
par.match = m->u.kernel.match;
par.matchinfo = m->data;
par.family = NFPROTO_IPV4;
return ret;
}
-static int check_target(struct ipt_entry *e, const char *name)
+static int check_target(struct ipt_entry *e, struct net *net, const char *name)
{
struct ipt_entry_target *t = ipt_get_target(e);
struct xt_tgchk_param par = {
+ .net = net,
.table = name,
.entryinfo = e,
.target = t->u.kernel.target,
}
static int
-find_check_entry(struct ipt_entry *e, const char *name, unsigned int size,
- unsigned int *i)
+find_check_entry(struct ipt_entry *e, struct net *net, const char *name,
+ unsigned int size, unsigned int *i)
{
struct ipt_entry_target *t;
struct xt_target *target;
return ret;
j = 0;
+ mtpar.net = net;
mtpar.table = name;
mtpar.entryinfo = &e->ip;
mtpar.hook_mask = e->comefrom;
}
t->u.kernel.target = target;
- ret = check_target(e, name);
+ ret = check_target(e, net, name);
if (ret)
goto err;
err:
module_put(t->u.kernel.target->me);
cleanup_matches:
- IPT_MATCH_ITERATE(e, cleanup_match, &j);
+ IPT_MATCH_ITERATE(e, cleanup_match, net, &j);
return ret;
}
}
static int
-cleanup_entry(struct ipt_entry *e, unsigned int *i)
+cleanup_entry(struct ipt_entry *e, struct net *net, unsigned int *i)
{
struct xt_tgdtor_param par;
struct ipt_entry_target *t;
return 1;
/* Cleanup all matches */
- IPT_MATCH_ITERATE(e, cleanup_match, NULL);
+ IPT_MATCH_ITERATE(e, cleanup_match, net, NULL);
t = ipt_get_target(e);
+ par.net = net;
par.target = t->u.kernel.target;
par.targinfo = t->data;
par.family = NFPROTO_IPV4;
/* Checks and translates the user-supplied table segment (held in
newinfo) */
static int
-translate_table(const char *name,
+translate_table(struct net *net,
+ const char *name,
unsigned int valid_hooks,
struct xt_table_info *newinfo,
void *entry0,
/* Finally, each sanity check must pass */
i = 0;
ret = IPT_ENTRY_ITERATE(entry0, newinfo->size,
- find_check_entry, name, size, &i);
+ find_check_entry, net, name, size, &i);
if (ret != 0) {
IPT_ENTRY_ITERATE(entry0, newinfo->size,
- cleanup_entry, &i);
+ cleanup_entry, net, &i);
return ret;
}
/* Decrease module usage counts and free resource */
loc_cpu_old_entry = oldinfo->entries[raw_smp_processor_id()];
IPT_ENTRY_ITERATE(loc_cpu_old_entry, oldinfo->size, cleanup_entry,
- NULL);
+ net, NULL);
xt_free_table_info(oldinfo);
if (copy_to_user(counters_ptr, counters,
sizeof(struct xt_counters) * num_counters) != 0)
goto free_newinfo;
}
- ret = translate_table(tmp.name, tmp.valid_hooks,
+ ret = translate_table(net, tmp.name, tmp.valid_hooks,
newinfo, loc_cpu_entry, tmp.size, tmp.num_entries,
tmp.hook_entry, tmp.underflow);
if (ret != 0)
return 0;
free_newinfo_untrans:
- IPT_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry, NULL);
+ IPT_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry, net, NULL);
free_newinfo:
xt_free_table_info(newinfo);
return ret;
}
static int
-compat_check_entry(struct ipt_entry *e, const char *name,
+compat_check_entry(struct ipt_entry *e, struct net *net, const char *name,
unsigned int *i)
{
struct xt_mtchk_param mtpar;
int ret;
j = 0;
+ mtpar.net = net;
mtpar.table = name;
mtpar.entryinfo = &e->ip;
mtpar.hook_mask = e->comefrom;
if (ret)
goto cleanup_matches;
- ret = check_target(e, name);
+ ret = check_target(e, net, name);
if (ret)
goto cleanup_matches;
return 0;
cleanup_matches:
- IPT_MATCH_ITERATE(e, cleanup_match, &j);
+ IPT_MATCH_ITERATE(e, cleanup_match, net, &j);
return ret;
}
static int
-translate_compat_table(const char *name,
+translate_compat_table(struct net *net,
+ const char *name,
unsigned int valid_hooks,
struct xt_table_info **pinfo,
void **pentry0,
i = 0;
ret = IPT_ENTRY_ITERATE(entry1, newinfo->size, compat_check_entry,
- name, &i);
+ net, name, &i);
if (ret) {
j -= i;
COMPAT_IPT_ENTRY_ITERATE_CONTINUE(entry0, newinfo->size, i,
compat_release_entry, &j);
- IPT_ENTRY_ITERATE(entry1, newinfo->size, cleanup_entry, &i);
+ IPT_ENTRY_ITERATE(entry1, newinfo->size, cleanup_entry, net, &i);
xt_free_table_info(newinfo);
return ret;
}
goto free_newinfo;
}
- ret = translate_compat_table(tmp.name, tmp.valid_hooks,
+ ret = translate_compat_table(net, tmp.name, tmp.valid_hooks,
&newinfo, &loc_cpu_entry, tmp.size,
tmp.num_entries, tmp.hook_entry,
tmp.underflow);
return 0;
free_newinfo_untrans:
- IPT_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry, NULL);
+ IPT_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry, net, NULL);
free_newinfo:
xt_free_table_info(newinfo);
return ret;
loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
memcpy(loc_cpu_entry, repl->entries, repl->size);
- ret = translate_table(table->name, table->valid_hooks,
+ ret = translate_table(net, table->name, table->valid_hooks,
newinfo, loc_cpu_entry, repl->size,
repl->num_entries,
repl->hook_entry,
return ERR_PTR(ret);
}
-void ipt_unregister_table(struct xt_table *table)
+void ipt_unregister_table(struct net *net, struct xt_table *table)
{
struct xt_table_info *private;
void *loc_cpu_entry;
/* Decrease module usage counts and free resources */
loc_cpu_entry = private->entries[raw_smp_processor_id()];
- IPT_ENTRY_ITERATE(loc_cpu_entry, private->size, cleanup_entry, NULL);
+ IPT_ENTRY_ITERATE(loc_cpu_entry, private->size, cleanup_entry, net, NULL);
if (private->number > private->initial_entries)
module_put(table_owner);
xt_free_table_info(private);
static void *clusterip_seq_start(struct seq_file *s, loff_t *pos)
{
- const struct proc_dir_entry *pde = s->private;
- struct clusterip_config *c = pde->data;
+ struct clusterip_config *c = s->private;
unsigned int weight;
u_int32_t local_nodes;
struct clusterip_seq_position *idx;
if (!ret) {
struct seq_file *sf = file->private_data;
- struct proc_dir_entry *pde = PDE(inode);
- struct clusterip_config *c = pde->data;
+ struct clusterip_config *c = PDE(inode)->data;
- sf->private = pde;
+ sf->private = c;
clusterip_config_get(c);
}
static int clusterip_proc_release(struct inode *inode, struct file *file)
{
- struct proc_dir_entry *pde = PDE(inode);
- struct clusterip_config *c = pde->data;
+ struct clusterip_config *c = PDE(inode)->data;
int ret;
ret = seq_release(inode, file);
static ssize_t clusterip_proc_write(struct file *file, const char __user *input,
size_t size, loff_t *ofs)
{
+ struct clusterip_config *c = PDE(file->f_path.dentry->d_inode)->data;
#define PROC_WRITELEN 10
char buffer[PROC_WRITELEN+1];
- const struct proc_dir_entry *pde = PDE(file->f_path.dentry->d_inode);
- struct clusterip_config *c = pde->data;
unsigned long nodenum;
if (copy_from_user(buffer, input, PROC_WRITELEN))
static void __net_exit iptable_filter_net_exit(struct net *net)
{
- ipt_unregister_table(net->ipv4.iptable_filter);
+ ipt_unregister_table(net, net->ipv4.iptable_filter);
}
static struct pernet_operations iptable_filter_net_ops = {
static void __net_exit iptable_mangle_net_exit(struct net *net)
{
- ipt_unregister_table(net->ipv4.iptable_mangle);
+ ipt_unregister_table(net, net->ipv4.iptable_mangle);
}
static struct pernet_operations iptable_mangle_net_ops = {
static void __net_exit iptable_raw_net_exit(struct net *net)
{
- ipt_unregister_table(net->ipv4.iptable_raw);
+ ipt_unregister_table(net, net->ipv4.iptable_raw);
}
static struct pernet_operations iptable_raw_net_ops = {
static void __net_exit iptable_security_net_exit(struct net *net)
{
- ipt_unregister_table(net->ipv4.iptable_security);
+ ipt_unregister_table(net, net->ipv4.iptable_security);
}
static struct pernet_operations iptable_security_net_ops = {
#include <linux/netfilter_bridge.h>
#include <linux/netfilter_ipv4.h>
#include <net/netfilter/ipv4/nf_defrag_ipv4.h>
+#include <net/netfilter/nf_conntrack.h>
/* Returns new sk_buff, or NULL */
static int nf_ct_ipv4_gather_frags(struct sk_buff *skb, u_int32_t user)
#if !defined(CONFIG_NF_NAT) && !defined(CONFIG_NF_NAT_MODULE)
/* Previously seen (loopback)? Ignore. Do this before
fragment check. */
- if (skb->nfct)
+ if (skb->nfct && !nf_ct_is_template((struct nf_conn *)skb->nfct))
return NF_ACCEPT;
#endif
#endif
/* FIXME: Time out? --RR */
-static int
-mangle_rfc959_packet(struct sk_buff *skb,
- __be32 newip,
- u_int16_t port,
- unsigned int matchoff,
- unsigned int matchlen,
- struct nf_conn *ct,
- enum ip_conntrack_info ctinfo)
+static int nf_nat_ftp_fmt_cmd(enum nf_ct_ftp_type type,
+ char *buffer, size_t buflen,
+ __be32 addr, u16 port)
{
- char buffer[sizeof("nnn,nnn,nnn,nnn,nnn,nnn")];
-
- sprintf(buffer, "%u,%u,%u,%u,%u,%u",
- NIPQUAD(newip), port>>8, port&0xFF);
-
- pr_debug("calling nf_nat_mangle_tcp_packet\n");
-
- return nf_nat_mangle_tcp_packet(skb, ct, ctinfo, matchoff,
- matchlen, buffer, strlen(buffer));
-}
-
-/* |1|132.235.1.2|6275| */
-static int
-mangle_eprt_packet(struct sk_buff *skb,
- __be32 newip,
- u_int16_t port,
- unsigned int matchoff,
- unsigned int matchlen,
- struct nf_conn *ct,
- enum ip_conntrack_info ctinfo)
-{
- char buffer[sizeof("|1|255.255.255.255|65535|")];
-
- sprintf(buffer, "|1|%u.%u.%u.%u|%u|", NIPQUAD(newip), port);
-
- pr_debug("calling nf_nat_mangle_tcp_packet\n");
-
- return nf_nat_mangle_tcp_packet(skb, ct, ctinfo, matchoff,
- matchlen, buffer, strlen(buffer));
-}
-
-/* |1|132.235.1.2|6275| */
-static int
-mangle_epsv_packet(struct sk_buff *skb,
- __be32 newip,
- u_int16_t port,
- unsigned int matchoff,
- unsigned int matchlen,
- struct nf_conn *ct,
- enum ip_conntrack_info ctinfo)
-{
- char buffer[sizeof("|||65535|")];
-
- sprintf(buffer, "|||%u|", port);
-
- pr_debug("calling nf_nat_mangle_tcp_packet\n");
+ switch (type) {
+ case NF_CT_FTP_PORT:
+ case NF_CT_FTP_PASV:
+ return snprintf(buffer, buflen, "%u,%u,%u,%u,%u,%u",
+ ((unsigned char *)&addr)[0],
+ ((unsigned char *)&addr)[1],
+ ((unsigned char *)&addr)[2],
+ ((unsigned char *)&addr)[3],
+ port >> 8,
+ port & 0xFF);
+ case NF_CT_FTP_EPRT:
+ return snprintf(buffer, buflen, "|1|%pI4|%u|", &addr, port);
+ case NF_CT_FTP_EPSV:
+ return snprintf(buffer, buflen, "|||%u|", port);
+ }
- return nf_nat_mangle_tcp_packet(skb, ct, ctinfo, matchoff,
- matchlen, buffer, strlen(buffer));
+ return 0;
}
-static int (*mangle[])(struct sk_buff *, __be32, u_int16_t,
- unsigned int, unsigned int, struct nf_conn *,
- enum ip_conntrack_info)
-= {
- [NF_CT_FTP_PORT] = mangle_rfc959_packet,
- [NF_CT_FTP_PASV] = mangle_rfc959_packet,
- [NF_CT_FTP_EPRT] = mangle_eprt_packet,
- [NF_CT_FTP_EPSV] = mangle_epsv_packet
-};
-
/* So, this packet has hit the connection tracking matching code.
Mangle it, and change the expectation to match the new version. */
static unsigned int nf_nat_ftp(struct sk_buff *skb,
u_int16_t port;
int dir = CTINFO2DIR(ctinfo);
struct nf_conn *ct = exp->master;
+ char buffer[sizeof("|1|255.255.255.255|65535|")];
+ unsigned int buflen;
pr_debug("FTP_NAT: type %i, off %u len %u\n", type, matchoff, matchlen);
if (port == 0)
return NF_DROP;
- if (!mangle[type](skb, newip, port, matchoff, matchlen, ct, ctinfo)) {
- nf_ct_unexpect_related(exp);
- return NF_DROP;
- }
+ buflen = nf_nat_ftp_fmt_cmd(type, buffer, sizeof(buffer), newip, port);
+ if (!buflen)
+ goto out;
+
+ pr_debug("calling nf_nat_mangle_tcp_packet\n");
+
+ if (!nf_nat_mangle_tcp_packet(skb, ct, ctinfo, matchoff,
+ matchlen, buffer, buflen))
+ goto out;
+
return NF_ACCEPT;
+
+out:
+ nf_ct_unexpect_related(exp);
+ return NF_DROP;
}
static void __exit nf_nat_ftp_fini(void)
static void __net_exit nf_nat_rule_net_exit(struct net *net)
{
- ipt_unregister_table(net->ipv4.nat_table);
+ ipt_unregister_table(net, net->ipv4.nat_table);
}
static struct pernet_operations nf_nat_rule_net_ops = {
unsigned int cls, con, tag, vers, pdutype;
struct asn1_ctx ctx;
struct asn1_octstr comm;
- struct snmp_object **obj;
+ struct snmp_object *obj;
if (debug > 1)
hex_dump(msg, len);
if (cls != ASN1_UNI || con != ASN1_CON || tag != ASN1_SEQ)
return 0;
- obj = kmalloc(sizeof(struct snmp_object), GFP_ATOMIC);
- if (obj == NULL) {
- if (net_ratelimit())
- printk(KERN_WARNING "OOM in bsalg(%d)\n", __LINE__);
- return 0;
- }
-
while (!asn1_eoc_decode(&ctx, eoc)) {
unsigned int i;
- if (!snmp_object_decode(&ctx, obj)) {
- if (*obj) {
- kfree((*obj)->id);
- kfree(*obj);
+ if (!snmp_object_decode(&ctx, &obj)) {
+ if (obj) {
+ kfree(obj->id);
+ kfree(obj);
}
- kfree(obj);
return 0;
}
if (debug > 1) {
printk(KERN_DEBUG "bsalg: object: ");
- for (i = 0; i < (*obj)->id_len; i++) {
+ for (i = 0; i < obj->id_len; i++) {
if (i > 0)
printk(".");
- printk("%lu", (*obj)->id[i]);
+ printk("%lu", obj->id[i]);
}
- printk(": type=%u\n", (*obj)->type);
+ printk(": type=%u\n", obj->type);
}
- if ((*obj)->type == SNMP_IPADDR)
+ if (obj->type == SNMP_IPADDR)
mangle_address(ctx.begin, ctx.pointer - 4 , map, check);
- kfree((*obj)->id);
- kfree(*obj);
+ kfree(obj->id);
+ kfree(obj);
}
- kfree(obj);
if (!asn1_eoc_decode(&ctx, eoc))
return 0;
}
static int
-cleanup_match(struct ip6t_entry_match *m, unsigned int *i)
+cleanup_match(struct ip6t_entry_match *m, struct net *net, unsigned int *i)
{
struct xt_mtdtor_param par;
if (i && (*i)-- == 0)
return 1;
+ par.net = net;
par.match = m->u.kernel.match;
par.matchinfo = m->data;
par.family = NFPROTO_IPV6;
return ret;
}
-static int check_target(struct ip6t_entry *e, const char *name)
+static int check_target(struct ip6t_entry *e, struct net *net, const char *name)
{
struct ip6t_entry_target *t = ip6t_get_target(e);
struct xt_tgchk_param par = {
+ .net = net,
.table = name,
.entryinfo = e,
.target = t->u.kernel.target,
}
static int
-find_check_entry(struct ip6t_entry *e, const char *name, unsigned int size,
- unsigned int *i)
+find_check_entry(struct ip6t_entry *e, struct net *net, const char *name,
+ unsigned int size, unsigned int *i)
{
struct ip6t_entry_target *t;
struct xt_target *target;
return ret;
j = 0;
+ mtpar.net = net;
mtpar.table = name;
mtpar.entryinfo = &e->ipv6;
mtpar.hook_mask = e->comefrom;
}
t->u.kernel.target = target;
- ret = check_target(e, name);
+ ret = check_target(e, net, name);
if (ret)
goto err;
err:
module_put(t->u.kernel.target->me);
cleanup_matches:
- IP6T_MATCH_ITERATE(e, cleanup_match, &j);
+ IP6T_MATCH_ITERATE(e, cleanup_match, net, &j);
return ret;
}
}
static int
-cleanup_entry(struct ip6t_entry *e, unsigned int *i)
+cleanup_entry(struct ip6t_entry *e, struct net *net, unsigned int *i)
{
struct xt_tgdtor_param par;
struct ip6t_entry_target *t;
return 1;
/* Cleanup all matches */
- IP6T_MATCH_ITERATE(e, cleanup_match, NULL);
+ IP6T_MATCH_ITERATE(e, cleanup_match, net, NULL);
t = ip6t_get_target(e);
+ par.net = net;
par.target = t->u.kernel.target;
par.targinfo = t->data;
par.family = NFPROTO_IPV6;
/* Checks and translates the user-supplied table segment (held in
newinfo) */
static int
-translate_table(const char *name,
+translate_table(struct net *net,
+ const char *name,
unsigned int valid_hooks,
struct xt_table_info *newinfo,
void *entry0,
/* Finally, each sanity check must pass */
i = 0;
ret = IP6T_ENTRY_ITERATE(entry0, newinfo->size,
- find_check_entry, name, size, &i);
+ find_check_entry, net, name, size, &i);
if (ret != 0) {
IP6T_ENTRY_ITERATE(entry0, newinfo->size,
- cleanup_entry, &i);
+ cleanup_entry, net, &i);
return ret;
}
/* Decrease module usage counts and free resource */
loc_cpu_old_entry = oldinfo->entries[raw_smp_processor_id()];
IP6T_ENTRY_ITERATE(loc_cpu_old_entry, oldinfo->size, cleanup_entry,
- NULL);
+ net, NULL);
xt_free_table_info(oldinfo);
if (copy_to_user(counters_ptr, counters,
sizeof(struct xt_counters) * num_counters) != 0)
goto free_newinfo;
}
- ret = translate_table(tmp.name, tmp.valid_hooks,
+ ret = translate_table(net, tmp.name, tmp.valid_hooks,
newinfo, loc_cpu_entry, tmp.size, tmp.num_entries,
tmp.hook_entry, tmp.underflow);
if (ret != 0)
return 0;
free_newinfo_untrans:
- IP6T_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry, NULL);
+ IP6T_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry, net, NULL);
free_newinfo:
xt_free_table_info(newinfo);
return ret;
return ret;
}
-static int compat_check_entry(struct ip6t_entry *e, const char *name,
- unsigned int *i)
+static int compat_check_entry(struct ip6t_entry *e, struct net *net,
+ const char *name, unsigned int *i)
{
unsigned int j;
int ret;
struct xt_mtchk_param mtpar;
j = 0;
+ mtpar.net = net;
mtpar.table = name;
mtpar.entryinfo = &e->ipv6;
mtpar.hook_mask = e->comefrom;
if (ret)
goto cleanup_matches;
- ret = check_target(e, name);
+ ret = check_target(e, net, name);
if (ret)
goto cleanup_matches;
return 0;
cleanup_matches:
- IP6T_MATCH_ITERATE(e, cleanup_match, &j);
+ IP6T_MATCH_ITERATE(e, cleanup_match, net, &j);
return ret;
}
static int
-translate_compat_table(const char *name,
+translate_compat_table(struct net *net,
+ const char *name,
unsigned int valid_hooks,
struct xt_table_info **pinfo,
void **pentry0,
i = 0;
ret = IP6T_ENTRY_ITERATE(entry1, newinfo->size, compat_check_entry,
- name, &i);
+ net, name, &i);
if (ret) {
j -= i;
COMPAT_IP6T_ENTRY_ITERATE_CONTINUE(entry0, newinfo->size, i,
compat_release_entry, &j);
- IP6T_ENTRY_ITERATE(entry1, newinfo->size, cleanup_entry, &i);
+ IP6T_ENTRY_ITERATE(entry1, newinfo->size, cleanup_entry, net, &i);
xt_free_table_info(newinfo);
return ret;
}
goto free_newinfo;
}
- ret = translate_compat_table(tmp.name, tmp.valid_hooks,
+ ret = translate_compat_table(net, tmp.name, tmp.valid_hooks,
&newinfo, &loc_cpu_entry, tmp.size,
tmp.num_entries, tmp.hook_entry,
tmp.underflow);
return 0;
free_newinfo_untrans:
- IP6T_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry, NULL);
+ IP6T_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry, net, NULL);
free_newinfo:
xt_free_table_info(newinfo);
return ret;
loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
memcpy(loc_cpu_entry, repl->entries, repl->size);
- ret = translate_table(table->name, table->valid_hooks,
+ ret = translate_table(net, table->name, table->valid_hooks,
newinfo, loc_cpu_entry, repl->size,
repl->num_entries,
repl->hook_entry,
return ERR_PTR(ret);
}
-void ip6t_unregister_table(struct xt_table *table)
+void ip6t_unregister_table(struct net *net, struct xt_table *table)
{
struct xt_table_info *private;
void *loc_cpu_entry;
/* Decrease module usage counts and free resources */
loc_cpu_entry = private->entries[raw_smp_processor_id()];
- IP6T_ENTRY_ITERATE(loc_cpu_entry, private->size, cleanup_entry, NULL);
+ IP6T_ENTRY_ITERATE(loc_cpu_entry, private->size, cleanup_entry, net, NULL);
if (private->number > private->initial_entries)
module_put(table_owner);
xt_free_table_info(private);
static void __net_exit ip6table_filter_net_exit(struct net *net)
{
- ip6t_unregister_table(net->ipv6.ip6table_filter);
+ ip6t_unregister_table(net, net->ipv6.ip6table_filter);
}
static struct pernet_operations ip6table_filter_net_ops = {
static void __net_exit ip6table_mangle_net_exit(struct net *net)
{
- ip6t_unregister_table(net->ipv6.ip6table_mangle);
+ ip6t_unregister_table(net, net->ipv6.ip6table_mangle);
}
static struct pernet_operations ip6table_mangle_net_ops = {
static void __net_exit ip6table_raw_net_exit(struct net *net)
{
- ip6t_unregister_table(net->ipv6.ip6table_raw);
+ ip6t_unregister_table(net, net->ipv6.ip6table_raw);
}
static struct pernet_operations ip6table_raw_net_ops = {
static void __net_exit ip6table_security_net_exit(struct net *net)
{
- ip6t_unregister_table(net->ipv6.ip6table_security);
+ ip6t_unregister_table(net, net->ipv6.ip6table_security);
}
static struct pernet_operations ip6table_security_net_ops = {
struct sk_buff *reasm;
/* Previously seen (loopback)? */
- if (skb->nfct)
+ if (skb->nfct && !nf_ct_is_template((struct nf_conn *)skb->nfct))
return NF_ACCEPT;
reasm = nf_ct_frag6_gather(skb, nf_ct6_defrag_user(hooknum, skb));
#include <linux/kernel.h>
#include <linux/module.h>
-#define NF_CT_FRAG6_HIGH_THRESH 262144 /* == 256*1024 */
-#define NF_CT_FRAG6_LOW_THRESH 196608 /* == 192*1024 */
-#define NF_CT_FRAG6_TIMEOUT IPV6_FRAG_TIMEOUT
struct nf_ct_frag6_skb_cb
{
nf_frags.frag_expire = nf_ct_frag6_expire;
nf_frags.secret_interval = 10 * 60 * HZ;
nf_init_frags.timeout = IPV6_FRAG_TIMEOUT;
- nf_init_frags.high_thresh = 256 * 1024;
- nf_init_frags.low_thresh = 192 * 1024;
+ nf_init_frags.high_thresh = IPV6_FRAG_HIGH_THRESH;
+ nf_init_frags.low_thresh = IPV6_FRAG_LOW_THRESH;
inet_frags_init_net(&nf_init_frags);
inet_frags_init(&nf_frags);
static int __net_init ipv6_frags_init_net(struct net *net)
{
- net->ipv6.frags.high_thresh = 256 * 1024;
- net->ipv6.frags.low_thresh = 192 * 1024;
+ net->ipv6.frags.high_thresh = IPV6_FRAG_HIGH_THRESH;
+ net->ipv6.frags.low_thresh = IPV6_FRAG_LOW_THRESH;
net->ipv6.frags.timeout = IPV6_FRAG_TIMEOUT;
inet_frags_init_net(&net->ipv6.frags);
To compile it as a module, choose M here. If unsure, say N.
+config NETFILTER_XT_TARGET_CT
+ tristate '"CT" target support'
+ depends on NF_CONNTRACK
+ depends on IP_NF_RAW || IP6_NF_RAW
+ depends on NETFILTER_ADVANCED
+ help
+ This options adds a `CT' target, which allows to specify initial
+ connection tracking parameters like events to be delivered and
+ the helper to be used.
+
+ To compile it as a module, choose M here. If unsure, say N.
+
config NETFILTER_XT_TARGET_DSCP
tristate '"DSCP" and "TOS" target support'
depends on IP_NF_MANGLE || IP6_NF_MANGLE
obj-$(CONFIG_NETFILTER_XT_TARGET_CLASSIFY) += xt_CLASSIFY.o
obj-$(CONFIG_NETFILTER_XT_TARGET_CONNMARK) += xt_CONNMARK.o
obj-$(CONFIG_NETFILTER_XT_TARGET_CONNSECMARK) += xt_CONNSECMARK.o
+obj-$(CONFIG_NETFILTER_XT_TARGET_CT) += xt_CT.o
obj-$(CONFIG_NETFILTER_XT_TARGET_DSCP) += xt_DSCP.o
obj-$(CONFIG_NETFILTER_XT_TARGET_HL) += xt_HL.o
obj-$(CONFIG_NETFILTER_XT_TARGET_LED) += xt_LED.o
each hash entry uses 8 bytes, so you can estimate how much memory is
needed for your box.
+ You can overwrite this number setting conn_tab_bits module parameter
+ or by appending ip_vs.conn_tab_bits=? to the kernel command line
+ if IP VS was compiled built-in.
+
comment "IPVS transport protocol load balancing support"
config IP_VS_PROTO_TCP
#include <net/ip_vs.h>
+#ifndef CONFIG_IP_VS_TAB_BITS
+#define CONFIG_IP_VS_TAB_BITS 12
+#endif
+
+/*
+ * Connection hash size. Default is what was selected at compile time.
+*/
+int ip_vs_conn_tab_bits = CONFIG_IP_VS_TAB_BITS;
+module_param_named(conn_tab_bits, ip_vs_conn_tab_bits, int, 0444);
+MODULE_PARM_DESC(conn_tab_bits, "Set connections' hash size");
+
+/* size and mask values */
+int ip_vs_conn_tab_size;
+int ip_vs_conn_tab_mask;
+
/*
* Connection hash table: for input and output packets lookups of IPVS
*/
if (af == AF_INET6)
return jhash_3words(jhash(addr, 16, ip_vs_conn_rnd),
(__force u32)port, proto, ip_vs_conn_rnd)
- & IP_VS_CONN_TAB_MASK;
+ & ip_vs_conn_tab_mask;
#endif
return jhash_3words((__force u32)addr->ip, (__force u32)port, proto,
ip_vs_conn_rnd)
- & IP_VS_CONN_TAB_MASK;
+ & ip_vs_conn_tab_mask;
}
int idx;
struct ip_vs_conn *cp;
- for(idx = 0; idx < IP_VS_CONN_TAB_SIZE; idx++) {
+ for (idx = 0; idx < ip_vs_conn_tab_size; idx++) {
ct_read_lock_bh(idx);
list_for_each_entry(cp, &ip_vs_conn_tab[idx], c_list) {
if (pos-- == 0) {
idx = l - ip_vs_conn_tab;
ct_read_unlock_bh(idx);
- while (++idx < IP_VS_CONN_TAB_SIZE) {
+ while (++idx < ip_vs_conn_tab_size) {
ct_read_lock_bh(idx);
list_for_each_entry(cp, &ip_vs_conn_tab[idx], c_list) {
seq->private = &ip_vs_conn_tab[idx];
/*
* Randomly scan 1/32 of the whole table every second
*/
- for (idx = 0; idx < (IP_VS_CONN_TAB_SIZE>>5); idx++) {
- unsigned hash = net_random() & IP_VS_CONN_TAB_MASK;
+ for (idx = 0; idx < (ip_vs_conn_tab_size>>5); idx++) {
+ unsigned hash = net_random() & ip_vs_conn_tab_mask;
/*
* Lock is actually needed in this loop.
struct ip_vs_conn *cp;
flush_again:
- for (idx=0; idx<IP_VS_CONN_TAB_SIZE; idx++) {
+ for (idx = 0; idx < ip_vs_conn_tab_size; idx++) {
/*
* Lock is actually needed in this loop.
*/
{
int idx;
+ /* Compute size and mask */
+ ip_vs_conn_tab_size = 1 << ip_vs_conn_tab_bits;
+ ip_vs_conn_tab_mask = ip_vs_conn_tab_size - 1;
+
/*
* Allocate the connection hash table and initialize its list heads
*/
- ip_vs_conn_tab = vmalloc(IP_VS_CONN_TAB_SIZE*sizeof(struct list_head));
+ ip_vs_conn_tab = vmalloc(ip_vs_conn_tab_size *
+ sizeof(struct list_head));
if (!ip_vs_conn_tab)
return -ENOMEM;
pr_info("Connection hash table configured "
"(size=%d, memory=%ldKbytes)\n",
- IP_VS_CONN_TAB_SIZE,
- (long)(IP_VS_CONN_TAB_SIZE*sizeof(struct list_head))/1024);
+ ip_vs_conn_tab_size,
+ (long)(ip_vs_conn_tab_size*sizeof(struct list_head))/1024);
IP_VS_DBG(0, "Each connection entry needs %Zd bytes at least\n",
sizeof(struct ip_vs_conn));
- for (idx = 0; idx < IP_VS_CONN_TAB_SIZE; idx++) {
+ for (idx = 0; idx < ip_vs_conn_tab_size; idx++) {
INIT_LIST_HEAD(&ip_vs_conn_tab[idx]);
}
if (v == SEQ_START_TOKEN) {
seq_printf(seq,
"IP Virtual Server version %d.%d.%d (size=%d)\n",
- NVERSION(IP_VS_VERSION_CODE), IP_VS_CONN_TAB_SIZE);
+ NVERSION(IP_VS_VERSION_CODE), ip_vs_conn_tab_size);
seq_puts(seq,
"Prot LocalAddress:Port Scheduler Flags\n");
seq_puts(seq,
char buf[64];
sprintf(buf, "IP Virtual Server version %d.%d.%d (size=%d)",
- NVERSION(IP_VS_VERSION_CODE), IP_VS_CONN_TAB_SIZE);
+ NVERSION(IP_VS_VERSION_CODE), ip_vs_conn_tab_size);
if (copy_to_user(user, buf, strlen(buf)+1) != 0) {
ret = -EFAULT;
goto out;
{
struct ip_vs_getinfo info;
info.version = IP_VS_VERSION_CODE;
- info.size = IP_VS_CONN_TAB_SIZE;
+ info.size = ip_vs_conn_tab_size;
info.num_services = ip_vs_num_services;
if (copy_to_user(user, &info, sizeof(info)) != 0)
ret = -EFAULT;
case IPVS_CMD_GET_INFO:
NLA_PUT_U32(msg, IPVS_INFO_ATTR_VERSION, IP_VS_VERSION_CODE);
NLA_PUT_U32(msg, IPVS_INFO_ATTR_CONN_TAB_SIZE,
- IP_VS_CONN_TAB_SIZE);
+ ip_vs_conn_tab_size);
break;
}
*/
from.ip = n_cp->vaddr.ip;
port = n_cp->vport;
- sprintf(buf, "%d,%d,%d,%d,%d,%d", NIPQUAD(from.ip),
+ sprintf(buf, "%u,%u,%u,%u,%u,%u", NIPQUAD(from.ip),
(ntohs(port)>>8)&255, ntohs(port)&255);
buf_len = strlen(buf);
/* Allocate a new conntrack: we return -ENOMEM if classification
failed due to stress. Otherwise it really is unclassifiable. */
static struct nf_conntrack_tuple_hash *
-init_conntrack(struct net *net,
+init_conntrack(struct net *net, struct nf_conn *tmpl,
const struct nf_conntrack_tuple *tuple,
struct nf_conntrack_l3proto *l3proto,
struct nf_conntrack_l4proto *l4proto,
struct nf_conn *ct;
struct nf_conn_help *help;
struct nf_conntrack_tuple repl_tuple;
+ struct nf_conntrack_ecache *ecache;
struct nf_conntrack_expect *exp;
if (!nf_ct_invert_tuple(&repl_tuple, tuple, l3proto, l4proto)) {
}
nf_ct_acct_ext_add(ct, GFP_ATOMIC);
- nf_ct_ecache_ext_add(ct, GFP_ATOMIC);
+
+ ecache = tmpl ? nf_ct_ecache_find(tmpl) : NULL;
+ nf_ct_ecache_ext_add(ct, ecache ? ecache->ctmask : 0,
+ ecache ? ecache->expmask : 0,
+ GFP_ATOMIC);
spin_lock_bh(&nf_conntrack_lock);
exp = nf_ct_find_expectation(net, tuple);
nf_conntrack_get(&ct->master->ct_general);
NF_CT_STAT_INC(net, expect_new);
} else {
- __nf_ct_try_assign_helper(ct, GFP_ATOMIC);
+ __nf_ct_try_assign_helper(ct, tmpl, GFP_ATOMIC);
NF_CT_STAT_INC(net, new);
}
/* On success, returns conntrack ptr, sets skb->nfct and ctinfo */
static inline struct nf_conn *
-resolve_normal_ct(struct net *net,
+resolve_normal_ct(struct net *net, struct nf_conn *tmpl,
struct sk_buff *skb,
unsigned int dataoff,
u_int16_t l3num,
/* look for tuple match */
h = nf_conntrack_find_get(net, &tuple);
if (!h) {
- h = init_conntrack(net, &tuple, l3proto, l4proto, skb, dataoff);
+ h = init_conntrack(net, tmpl, &tuple, l3proto, l4proto,
+ skb, dataoff);
if (!h)
return NULL;
if (IS_ERR(h))
nf_conntrack_in(struct net *net, u_int8_t pf, unsigned int hooknum,
struct sk_buff *skb)
{
- struct nf_conn *ct;
+ struct nf_conn *ct, *tmpl = NULL;
enum ip_conntrack_info ctinfo;
struct nf_conntrack_l3proto *l3proto;
struct nf_conntrack_l4proto *l4proto;
int set_reply = 0;
int ret;
- /* Previously seen (loopback or untracked)? Ignore. */
if (skb->nfct) {
- NF_CT_STAT_INC_ATOMIC(net, ignore);
- return NF_ACCEPT;
+ /* Previously seen (loopback or untracked)? Ignore. */
+ tmpl = (struct nf_conn *)skb->nfct;
+ if (!nf_ct_is_template(tmpl)) {
+ NF_CT_STAT_INC_ATOMIC(net, ignore);
+ return NF_ACCEPT;
+ }
+ skb->nfct = NULL;
}
/* rcu_read_lock()ed by nf_hook_slow */
pr_debug("not prepared to track yet or error occured\n");
NF_CT_STAT_INC_ATOMIC(net, error);
NF_CT_STAT_INC_ATOMIC(net, invalid);
- return -ret;
+ ret = -ret;
+ goto out;
}
l4proto = __nf_ct_l4proto_find(pf, protonum);
if (ret <= 0) {
NF_CT_STAT_INC_ATOMIC(net, error);
NF_CT_STAT_INC_ATOMIC(net, invalid);
- return -ret;
+ ret = -ret;
+ goto out;
}
}
- ct = resolve_normal_ct(net, skb, dataoff, pf, protonum,
+ ct = resolve_normal_ct(net, tmpl, skb, dataoff, pf, protonum,
l3proto, l4proto, &set_reply, &ctinfo);
if (!ct) {
/* Not valid part of a connection */
NF_CT_STAT_INC_ATOMIC(net, invalid);
- return NF_ACCEPT;
+ ret = NF_ACCEPT;
+ goto out;
}
if (IS_ERR(ct)) {
/* Too stressed to deal. */
NF_CT_STAT_INC_ATOMIC(net, drop);
- return NF_DROP;
+ ret = NF_DROP;
+ goto out;
}
NF_CT_ASSERT(skb->nfct);
NF_CT_STAT_INC_ATOMIC(net, invalid);
if (ret == -NF_DROP)
NF_CT_STAT_INC_ATOMIC(net, drop);
- return -ret;
+ ret = -ret;
+ goto out;
}
if (set_reply && !test_and_set_bit(IPS_SEEN_REPLY_BIT, &ct->status))
- nf_conntrack_event_cache(IPCT_STATUS, ct);
+ nf_conntrack_event_cache(IPCT_REPLY, ct);
+out:
+ if (tmpl)
+ nf_ct_put(tmpl);
return ret;
}
return;
rcu_read_lock();
- __nf_ct_try_assign_helper(ct, GFP_ATOMIC);
+ __nf_ct_try_assign_helper(ct, NULL, GFP_ATOMIC);
rcu_read_unlock();
}
EXPORT_SYMBOL_GPL(nf_conntrack_alter_reply);
}
struct nf_conntrack_helper *
-__nf_conntrack_helper_find_byname(const char *name)
+__nf_conntrack_helper_find(const char *name, u16 l3num, u8 protonum)
{
struct nf_conntrack_helper *h;
struct hlist_node *n;
for (i = 0; i < nf_ct_helper_hsize; i++) {
hlist_for_each_entry_rcu(h, n, &nf_ct_helper_hash[i], hnode) {
- if (!strcmp(h->name, name))
+ if (!strcmp(h->name, name) &&
+ h->tuple.src.l3num == l3num &&
+ h->tuple.dst.protonum == protonum)
return h;
}
}
return NULL;
}
-EXPORT_SYMBOL_GPL(__nf_conntrack_helper_find_byname);
+EXPORT_SYMBOL_GPL(__nf_conntrack_helper_find);
+
+struct nf_conntrack_helper *
+nf_conntrack_helper_try_module_get(const char *name, u16 l3num, u8 protonum)
+{
+ struct nf_conntrack_helper *h;
+
+ h = __nf_conntrack_helper_find(name, l3num, protonum);
+#ifdef CONFIG_MODULES
+ if (h == NULL) {
+ if (request_module("nfct-helper-%s", name) == 0)
+ h = __nf_conntrack_helper_find(name, l3num, protonum);
+ }
+#endif
+ if (h != NULL && !try_module_get(h->me))
+ h = NULL;
+
+ return h;
+}
+EXPORT_SYMBOL_GPL(nf_conntrack_helper_try_module_get);
struct nf_conn_help *nf_ct_helper_ext_add(struct nf_conn *ct, gfp_t gfp)
{
}
EXPORT_SYMBOL_GPL(nf_ct_helper_ext_add);
-int __nf_ct_try_assign_helper(struct nf_conn *ct, gfp_t flags)
+int __nf_ct_try_assign_helper(struct nf_conn *ct, struct nf_conn *tmpl,
+ gfp_t flags)
{
+ struct nf_conntrack_helper *helper = NULL;
+ struct nf_conn_help *help;
int ret = 0;
- struct nf_conntrack_helper *helper;
- struct nf_conn_help *help = nfct_help(ct);
- helper = __nf_ct_helper_find(&ct->tuplehash[IP_CT_DIR_REPLY].tuple);
+ if (tmpl != NULL) {
+ help = nfct_help(tmpl);
+ if (help != NULL)
+ helper = help->helper;
+ }
+
+ help = nfct_help(ct);
+ if (helper == NULL)
+ helper = __nf_ct_helper_find(&ct->tuplehash[IP_CT_DIR_REPLY].tuple);
if (helper == NULL) {
if (help)
rcu_assign_pointer(help->helper, NULL);
#include <linux/netfilter.h>
#include <net/netlink.h>
+#include <net/sock.h>
#include <net/netfilter/nf_conntrack.h>
#include <net/netfilter/nf_conntrack_core.h>
#include <net/netfilter/nf_conntrack_expect.h>
static int
ctnetlink_conntrack_event(unsigned int events, struct nf_ct_event *item)
{
+ struct net *net;
struct nlmsghdr *nlh;
struct nfgenmsg *nfmsg;
struct nlattr *nest_parms;
} else
return 0;
- if (!item->report && !nfnetlink_has_listeners(group))
+ net = nf_ct_net(ct);
+ if (!item->report && !nfnetlink_has_listeners(net, group))
return 0;
skb = nlmsg_new(ctnetlink_nlmsg_size(ct), GFP_ATOMIC);
rcu_read_unlock();
nlmsg_end(skb, nlh);
- err = nfnetlink_send(skb, item->pid, group, item->report, GFP_ATOMIC);
+ err = nfnetlink_send(skb, net, item->pid, group, item->report,
+ GFP_ATOMIC);
if (err == -ENOBUFS || err == -EAGAIN)
return -ENOBUFS;
nlmsg_failure:
kfree_skb(skb);
errout:
- nfnetlink_set_err(0, group, -ENOBUFS);
+ nfnetlink_set_err(net, 0, group, -ENOBUFS);
return 0;
}
#endif /* CONFIG_NF_CONNTRACK_EVENTS */
static int
ctnetlink_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
{
+ struct net *net = sock_net(skb->sk);
struct nf_conn *ct, *last;
struct nf_conntrack_tuple_hash *h;
struct hlist_nulls_node *n;
rcu_read_lock();
last = (struct nf_conn *)cb->args[1];
- for (; cb->args[0] < init_net.ct.htable_size; cb->args[0]++) {
+ for (; cb->args[0] < net->ct.htable_size; cb->args[0]++) {
restart:
- hlist_nulls_for_each_entry_rcu(h, n, &init_net.ct.hash[cb->args[0]],
+ hlist_nulls_for_each_entry_rcu(h, n, &net->ct.hash[cb->args[0]],
hnnode) {
if (NF_CT_DIRECTION(h) != IP_CT_DIR_ORIGINAL)
continue;
const struct nlmsghdr *nlh,
const struct nlattr * const cda[])
{
+ struct net *net = sock_net(ctnl);
struct nf_conntrack_tuple_hash *h;
struct nf_conntrack_tuple tuple;
struct nf_conn *ct;
err = ctnetlink_parse_tuple(cda, &tuple, CTA_TUPLE_REPLY, u3);
else {
/* Flush the whole table */
- nf_conntrack_flush_report(&init_net,
+ nf_conntrack_flush_report(net,
NETLINK_CB(skb).pid,
nlmsg_report(nlh));
return 0;
if (err < 0)
return err;
- h = nf_conntrack_find_get(&init_net, &tuple);
+ h = nf_conntrack_find_get(net, &tuple);
if (!h)
return -ENOENT;
const struct nlmsghdr *nlh,
const struct nlattr * const cda[])
{
+ struct net *net = sock_net(ctnl);
struct nf_conntrack_tuple_hash *h;
struct nf_conntrack_tuple tuple;
struct nf_conn *ct;
if (err < 0)
return err;
- h = nf_conntrack_find_get(&init_net, &tuple);
+ h = nf_conntrack_find_get(net, &tuple);
if (!h)
return -ENOENT;
return 0;
}
- helper = __nf_conntrack_helper_find_byname(helpname);
+ helper = __nf_conntrack_helper_find(helpname, nf_ct_l3num(ct),
+ nf_ct_protonum(ct));
if (helper == NULL) {
#ifdef CONFIG_MODULES
spin_unlock_bh(&nf_conntrack_lock);
}
spin_lock_bh(&nf_conntrack_lock);
- helper = __nf_conntrack_helper_find_byname(helpname);
+ helper = __nf_conntrack_helper_find(helpname, nf_ct_l3num(ct),
+ nf_ct_protonum(ct));
if (helper)
return -EAGAIN;
#endif
}
static struct nf_conn *
-ctnetlink_create_conntrack(const struct nlattr * const cda[],
+ctnetlink_create_conntrack(struct net *net,
+ const struct nlattr * const cda[],
struct nf_conntrack_tuple *otuple,
struct nf_conntrack_tuple *rtuple,
u8 u3)
int err = -EINVAL;
struct nf_conntrack_helper *helper;
- ct = nf_conntrack_alloc(&init_net, otuple, rtuple, GFP_ATOMIC);
+ ct = nf_conntrack_alloc(net, otuple, rtuple, GFP_ATOMIC);
if (IS_ERR(ct))
return ERR_PTR(-ENOMEM);
if (err < 0)
goto err2;
- helper = __nf_conntrack_helper_find_byname(helpname);
+ helper = __nf_conntrack_helper_find(helpname, nf_ct_l3num(ct),
+ nf_ct_protonum(ct));
if (helper == NULL) {
rcu_read_unlock();
#ifdef CONFIG_MODULES
}
rcu_read_lock();
- helper = __nf_conntrack_helper_find_byname(helpname);
+ helper = __nf_conntrack_helper_find(helpname,
+ nf_ct_l3num(ct),
+ nf_ct_protonum(ct));
if (helper) {
err = -EAGAIN;
goto err2;
}
} else {
/* try an implicit helper assignation */
- err = __nf_ct_try_assign_helper(ct, GFP_ATOMIC);
+ err = __nf_ct_try_assign_helper(ct, NULL, GFP_ATOMIC);
if (err < 0)
goto err2;
}
}
nf_ct_acct_ext_add(ct, GFP_ATOMIC);
- nf_ct_ecache_ext_add(ct, GFP_ATOMIC);
+ nf_ct_ecache_ext_add(ct, 0, 0, GFP_ATOMIC);
#if defined(CONFIG_NF_CONNTRACK_MARK)
if (cda[CTA_MARK])
if (err < 0)
goto err2;
- master_h = nf_conntrack_find_get(&init_net, &master);
+ master_h = nf_conntrack_find_get(net, &master);
if (master_h == NULL) {
err = -ENOENT;
goto err2;
const struct nlmsghdr *nlh,
const struct nlattr * const cda[])
{
+ struct net *net = sock_net(ctnl);
struct nf_conntrack_tuple otuple, rtuple;
struct nf_conntrack_tuple_hash *h = NULL;
struct nfgenmsg *nfmsg = nlmsg_data(nlh);
spin_lock_bh(&nf_conntrack_lock);
if (cda[CTA_TUPLE_ORIG])
- h = __nf_conntrack_find(&init_net, &otuple);
+ h = __nf_conntrack_find(net, &otuple);
else if (cda[CTA_TUPLE_REPLY])
- h = __nf_conntrack_find(&init_net, &rtuple);
+ h = __nf_conntrack_find(net, &rtuple);
if (h == NULL) {
err = -ENOENT;
struct nf_conn *ct;
enum ip_conntrack_events events;
- ct = ctnetlink_create_conntrack(cda, &otuple,
+ ct = ctnetlink_create_conntrack(net, cda, &otuple,
&rtuple, u3);
if (IS_ERR(ct)) {
err = PTR_ERR(ct);
else
events = IPCT_NEW;
- nf_conntrack_eventmask_report((1 << IPCT_STATUS) |
+ nf_conntrack_eventmask_report((1 << IPCT_REPLY) |
+ (1 << IPCT_ASSURED) |
(1 << IPCT_HELPER) |
(1 << IPCT_PROTOINFO) |
(1 << IPCT_NATSEQADJ) |
if (err == 0) {
nf_conntrack_get(&ct->ct_general);
spin_unlock_bh(&nf_conntrack_lock);
- nf_conntrack_eventmask_report((1 << IPCT_STATUS) |
+ nf_conntrack_eventmask_report((1 << IPCT_REPLY) |
+ (1 << IPCT_ASSURED) |
(1 << IPCT_HELPER) |
(1 << IPCT_PROTOINFO) |
(1 << IPCT_NATSEQADJ) |
static int
ctnetlink_expect_event(unsigned int events, struct nf_exp_event *item)
{
+ struct nf_conntrack_expect *exp = item->exp;
+ struct net *net = nf_ct_exp_net(exp);
struct nlmsghdr *nlh;
struct nfgenmsg *nfmsg;
- struct nf_conntrack_expect *exp = item->exp;
struct sk_buff *skb;
unsigned int type;
int flags = 0;
return 0;
if (!item->report &&
- !nfnetlink_has_listeners(NFNLGRP_CONNTRACK_EXP_NEW))
+ !nfnetlink_has_listeners(net, NFNLGRP_CONNTRACK_EXP_NEW))
return 0;
skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
rcu_read_unlock();
nlmsg_end(skb, nlh);
- nfnetlink_send(skb, item->pid, NFNLGRP_CONNTRACK_EXP_NEW,
+ nfnetlink_send(skb, net, item->pid, NFNLGRP_CONNTRACK_EXP_NEW,
item->report, GFP_ATOMIC);
return 0;
nlmsg_failure:
kfree_skb(skb);
errout:
- nfnetlink_set_err(0, 0, -ENOBUFS);
+ nfnetlink_set_err(net, 0, 0, -ENOBUFS);
return 0;
}
#endif
static int
ctnetlink_exp_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
{
- struct net *net = &init_net;
+ struct net *net = sock_net(skb->sk);
struct nf_conntrack_expect *exp, *last;
struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
struct hlist_node *n;
const struct nlmsghdr *nlh,
const struct nlattr * const cda[])
{
+ struct net *net = sock_net(ctnl);
struct nf_conntrack_tuple tuple;
struct nf_conntrack_expect *exp;
struct sk_buff *skb2;
if (err < 0)
return err;
- exp = nf_ct_expect_find_get(&init_net, &tuple);
+ exp = nf_ct_expect_find_get(net, &tuple);
if (!exp)
return -ENOENT;
const struct nlmsghdr *nlh,
const struct nlattr * const cda[])
{
+ struct net *net = sock_net(ctnl);
struct nf_conntrack_expect *exp;
struct nf_conntrack_tuple tuple;
- struct nf_conntrack_helper *h;
struct nfgenmsg *nfmsg = nlmsg_data(nlh);
struct hlist_node *n, *next;
u_int8_t u3 = nfmsg->nfgen_family;
return err;
/* bump usage count to 2 */
- exp = nf_ct_expect_find_get(&init_net, &tuple);
+ exp = nf_ct_expect_find_get(net, &tuple);
if (!exp)
return -ENOENT;
/* delete all expectations for this helper */
spin_lock_bh(&nf_conntrack_lock);
- h = __nf_conntrack_helper_find_byname(name);
- if (!h) {
- spin_unlock_bh(&nf_conntrack_lock);
- return -EOPNOTSUPP;
- }
for (i = 0; i < nf_ct_expect_hsize; i++) {
hlist_for_each_entry_safe(exp, n, next,
- &init_net.ct.expect_hash[i],
+ &net->ct.expect_hash[i],
hnode) {
m_help = nfct_help(exp->master);
- if (m_help->helper == h
- && del_timer(&exp->timeout)) {
+ if (!strcmp(m_help->helper->name, name) &&
+ del_timer(&exp->timeout)) {
nf_ct_unlink_expect(exp);
nf_ct_expect_put(exp);
}
spin_lock_bh(&nf_conntrack_lock);
for (i = 0; i < nf_ct_expect_hsize; i++) {
hlist_for_each_entry_safe(exp, n, next,
- &init_net.ct.expect_hash[i],
+ &net->ct.expect_hash[i],
hnode) {
if (del_timer(&exp->timeout)) {
nf_ct_unlink_expect(exp);
}
static int
-ctnetlink_create_expect(const struct nlattr * const cda[], u_int8_t u3,
+ctnetlink_create_expect(struct net *net, const struct nlattr * const cda[],
+ u_int8_t u3,
u32 pid, int report)
{
struct nf_conntrack_tuple tuple, mask, master_tuple;
return err;
/* Look for master conntrack of this expectation */
- h = nf_conntrack_find_get(&init_net, &master_tuple);
+ h = nf_conntrack_find_get(net, &master_tuple);
if (!h)
return -ENOENT;
ct = nf_ct_tuplehash_to_ctrack(h);
const struct nlmsghdr *nlh,
const struct nlattr * const cda[])
{
+ struct net *net = sock_net(ctnl);
struct nf_conntrack_tuple tuple;
struct nf_conntrack_expect *exp;
struct nfgenmsg *nfmsg = nlmsg_data(nlh);
return err;
spin_lock_bh(&nf_conntrack_lock);
- exp = __nf_ct_expect_find(&init_net, &tuple);
+ exp = __nf_ct_expect_find(net, &tuple);
if (!exp) {
spin_unlock_bh(&nf_conntrack_lock);
err = -ENOENT;
if (nlh->nlmsg_flags & NLM_F_CREATE) {
- err = ctnetlink_create_expect(cda,
+ err = ctnetlink_create_expect(net, cda,
u3,
NETLINK_CB(skb).pid,
nlmsg_report(nlh));
ct->proto.gre.stream_timeout);
/* Also, more likely to be important, and not a probe. */
set_bit(IPS_ASSURED_BIT, &ct->status);
- nf_conntrack_event_cache(IPCT_STATUS, ct);
+ nf_conntrack_event_cache(IPCT_ASSURED, ct);
} else
nf_ct_refresh_acct(ct, ctinfo, skb,
ct->proto.gre.timeout);
new_state == SCTP_CONNTRACK_ESTABLISHED) {
pr_debug("Setting assured bit\n");
set_bit(IPS_ASSURED_BIT, &ct->status);
- nf_conntrack_event_cache(IPCT_STATUS, ct);
+ nf_conntrack_event_cache(IPCT_ASSURED, ct);
}
return NF_ACCEPT;
after SYN_RECV or a valid answer for a picked up
connection. */
set_bit(IPS_ASSURED_BIT, &ct->status);
- nf_conntrack_event_cache(IPCT_STATUS, ct);
+ nf_conntrack_event_cache(IPCT_ASSURED, ct);
}
nf_ct_refresh_acct(ct, ctinfo, skb, timeout);
nf_ct_refresh_acct(ct, ctinfo, skb, nf_ct_udp_timeout_stream);
/* Also, more likely to be important, and not a probe */
if (!test_and_set_bit(IPS_ASSURED_BIT, &ct->status))
- nf_conntrack_event_cache(IPCT_STATUS, ct);
+ nf_conntrack_event_cache(IPCT_ASSURED, ct);
} else
nf_ct_refresh_acct(ct, ctinfo, skb, nf_ct_udp_timeout);
nf_ct_udplite_timeout_stream);
/* Also, more likely to be important, and not a probe */
if (!test_and_set_bit(IPS_ASSURED_BIT, &ct->status))
- nf_conntrack_event_cache(IPCT_STATUS, ct);
+ nf_conntrack_event_cache(IPCT_ASSURED, ct);
} else
nf_ct_refresh_acct(ct, ctinfo, skb, nf_ct_udplite_timeout);
static char __initdata nfversion[] = "0.30";
-static struct sock *nfnl = NULL;
static const struct nfnetlink_subsystem *subsys_table[NFNL_SUBSYS_COUNT];
static DEFINE_MUTEX(nfnl_mutex);
return &ss->cb[cb_id];
}
-int nfnetlink_has_listeners(unsigned int group)
+int nfnetlink_has_listeners(struct net *net, unsigned int group)
{
- return netlink_has_listeners(nfnl, group);
+ return netlink_has_listeners(net->nfnl, group);
}
EXPORT_SYMBOL_GPL(nfnetlink_has_listeners);
-int nfnetlink_send(struct sk_buff *skb, u32 pid,
+int nfnetlink_send(struct sk_buff *skb, struct net *net, u32 pid,
unsigned group, int echo, gfp_t flags)
{
- return nlmsg_notify(nfnl, skb, pid, group, echo, flags);
+ return nlmsg_notify(net->nfnl, skb, pid, group, echo, flags);
}
EXPORT_SYMBOL_GPL(nfnetlink_send);
-void nfnetlink_set_err(u32 pid, u32 group, int error)
+void nfnetlink_set_err(struct net *net, u32 pid, u32 group, int error)
{
- netlink_set_err(nfnl, pid, group, error);
+ netlink_set_err(net->nfnl, pid, group, error);
}
EXPORT_SYMBOL_GPL(nfnetlink_set_err);
-int nfnetlink_unicast(struct sk_buff *skb, u_int32_t pid, int flags)
+int nfnetlink_unicast(struct sk_buff *skb, struct net *net, u_int32_t pid, int flags)
{
- return netlink_unicast(nfnl, skb, pid, flags);
+ return netlink_unicast(net->nfnl, skb, pid, flags);
}
EXPORT_SYMBOL_GPL(nfnetlink_unicast);
/* Process one complete nfnetlink message. */
static int nfnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
{
+ struct net *net = sock_net(skb->sk);
const struct nfnl_callback *nc;
const struct nfnetlink_subsystem *ss;
int type, err;
if (err < 0)
return err;
- err = nc->call(nfnl, skb, nlh, (const struct nlattr **)cda);
+ err = nc->call(net->nfnl, skb, nlh, (const struct nlattr **)cda);
if (err == -EAGAIN)
goto replay;
return err;
nfnl_unlock();
}
-static void __exit nfnetlink_exit(void)
+static int __net_init nfnetlink_net_init(struct net *net)
{
- printk("Removing netfilter NETLINK layer.\n");
- netlink_kernel_release(nfnl);
- return;
+ struct sock *nfnl;
+
+ nfnl = netlink_kernel_create(net, NETLINK_NETFILTER, NFNLGRP_MAX,
+ nfnetlink_rcv, NULL, THIS_MODULE);
+ if (!nfnl)
+ return -ENOMEM;
+ net->nfnl_stash = nfnl;
+ rcu_assign_pointer(net->nfnl, nfnl);
+ return 0;
}
-static int __init nfnetlink_init(void)
+static void __net_exit nfnetlink_net_exit_batch(struct list_head *net_exit_list)
{
- printk("Netfilter messages via NETLINK v%s.\n", nfversion);
+ struct net *net;
- nfnl = netlink_kernel_create(&init_net, NETLINK_NETFILTER, NFNLGRP_MAX,
- nfnetlink_rcv, NULL, THIS_MODULE);
- if (!nfnl) {
- printk(KERN_ERR "cannot initialize nfnetlink!\n");
- return -ENOMEM;
- }
+ list_for_each_entry(net, net_exit_list, exit_list)
+ rcu_assign_pointer(net->nfnl, NULL);
+ synchronize_net();
+ list_for_each_entry(net, net_exit_list, exit_list)
+ netlink_kernel_release(net->nfnl_stash);
+}
- return 0;
+static struct pernet_operations nfnetlink_net_ops = {
+ .init = nfnetlink_net_init,
+ .exit_batch = nfnetlink_net_exit_batch,
+};
+
+static int __init nfnetlink_init(void)
+{
+ printk("Netfilter messages via NETLINK v%s.\n", nfversion);
+ return register_pernet_subsys(&nfnetlink_net_ops);
}
+static void __exit nfnetlink_exit(void)
+{
+ printk("Removing netfilter NETLINK layer.\n");
+ unregister_pernet_subsys(&nfnetlink_net_ops);
+}
module_init(nfnetlink_init);
module_exit(nfnetlink_exit);
NLMSG_DONE,
sizeof(struct nfgenmsg));
- status = nfnetlink_unicast(inst->skb, inst->peer_pid, MSG_DONTWAIT);
+ status = nfnetlink_unicast(inst->skb, &init_net, inst->peer_pid,
+ MSG_DONTWAIT);
inst->qlen = 0;
inst->skb = NULL;
queue->queue_dropped++;
if (net_ratelimit())
printk(KERN_WARNING "nf_queue: full at %d entries, "
- "dropping packets(s). Dropped: %d\n",
- queue->queue_total, queue->queue_dropped);
+ "dropping packets(s).\n",
+ queue->queue_total);
goto err_out_free_nskb;
}
/* nfnetlink_unicast will either free the nskb or add it to a socket */
- err = nfnetlink_unicast(nskb, queue->peer_pid, MSG_DONTWAIT);
+ err = nfnetlink_unicast(nskb, &init_net, queue->peer_pid, MSG_DONTWAIT);
if (err < 0) {
queue->queue_user_dropped++;
goto err_out_unlock;
--- /dev/null
+/*
+ * Copyright (c) 2010 Patrick McHardy <kaber@trash.net>
+ *
+ * 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/selinux.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#include <linux/netfilter/x_tables.h>
+#include <linux/netfilter/xt_CT.h>
+#include <net/netfilter/nf_conntrack.h>
+#include <net/netfilter/nf_conntrack_helper.h>
+#include <net/netfilter/nf_conntrack_ecache.h>
+
+static unsigned int xt_ct_target(struct sk_buff *skb,
+ const struct xt_target_param *par)
+{
+ const struct xt_ct_target_info *info = par->targinfo;
+ struct nf_conn *ct = info->ct;
+
+ /* Previously seen (loopback)? Ignore. */
+ if (skb->nfct != NULL)
+ return XT_CONTINUE;
+
+ atomic_inc(&ct->ct_general.use);
+ skb->nfct = &ct->ct_general;
+ skb->nfctinfo = IP_CT_NEW;
+
+ return XT_CONTINUE;
+}
+
+static u8 xt_ct_find_proto(const struct xt_tgchk_param *par)
+{
+ if (par->family == AF_INET) {
+ const struct ipt_entry *e = par->entryinfo;
+
+ if (e->ip.invflags & IPT_INV_PROTO)
+ return 0;
+ return e->ip.proto;
+ } else if (par->family == AF_INET6) {
+ const struct ip6t_entry *e = par->entryinfo;
+
+ if (e->ipv6.invflags & IP6T_INV_PROTO)
+ return 0;
+ return e->ipv6.proto;
+ } else
+ return 0;
+}
+
+static bool xt_ct_tg_check(const struct xt_tgchk_param *par)
+{
+ struct xt_ct_target_info *info = par->targinfo;
+ struct nf_conntrack_tuple t;
+ struct nf_conn_help *help;
+ struct nf_conn *ct;
+ u8 proto;
+
+ if (info->flags & ~XT_CT_NOTRACK)
+ return false;
+
+ if (info->flags & XT_CT_NOTRACK) {
+ ct = &nf_conntrack_untracked;
+ atomic_inc(&ct->ct_general.use);
+ goto out;
+ }
+
+ if (nf_ct_l3proto_try_module_get(par->family) < 0)
+ goto err1;
+
+ memset(&t, 0, sizeof(t));
+ ct = nf_conntrack_alloc(par->net, &t, &t, GFP_KERNEL);
+ if (IS_ERR(ct))
+ goto err2;
+
+ if ((info->ct_events || info->exp_events) &&
+ !nf_ct_ecache_ext_add(ct, info->ct_events, info->exp_events,
+ GFP_KERNEL))
+ goto err3;
+
+ if (info->helper[0]) {
+ proto = xt_ct_find_proto(par);
+ if (!proto)
+ goto err3;
+
+ help = nf_ct_helper_ext_add(ct, GFP_KERNEL);
+ if (help == NULL)
+ goto err3;
+
+ help->helper = nf_conntrack_helper_try_module_get(info->helper,
+ par->family,
+ proto);
+ if (help->helper == NULL)
+ goto err3;
+ }
+
+ __set_bit(IPS_TEMPLATE_BIT, &ct->status);
+ __set_bit(IPS_CONFIRMED_BIT, &ct->status);
+out:
+ info->ct = ct;
+ return true;
+
+err3:
+ nf_conntrack_free(ct);
+err2:
+ nf_ct_l3proto_module_put(par->family);
+err1:
+ return false;
+}
+
+static void xt_ct_tg_destroy(const struct xt_tgdtor_param *par)
+{
+ struct xt_ct_target_info *info = par->targinfo;
+ struct nf_conn *ct = info->ct;
+ struct nf_conn_help *help;
+
+ if (ct != &nf_conntrack_untracked) {
+ help = nfct_help(ct);
+ if (help)
+ module_put(help->helper->me);
+
+ nf_ct_l3proto_module_put(par->family);
+ }
+ nf_ct_put(info->ct);
+}
+
+static struct xt_target xt_ct_tg __read_mostly = {
+ .name = "CT",
+ .family = NFPROTO_UNSPEC,
+ .targetsize = XT_ALIGN(sizeof(struct xt_ct_target_info)),
+ .checkentry = xt_ct_tg_check,
+ .destroy = xt_ct_tg_destroy,
+ .target = xt_ct_target,
+ .table = "raw",
+ .me = THIS_MODULE,
+};
+
+static int __init xt_ct_tg_init(void)
+{
+ return xt_register_target(&xt_ct_tg);
+}
+
+static void __exit xt_ct_tg_exit(void)
+{
+ xt_unregister_target(&xt_ct_tg);
+}
+
+module_init(xt_ct_tg_init);
+module_exit(xt_ct_tg_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Xtables: connection tracking target");
+MODULE_ALIAS("ipt_CT");
+MODULE_ALIAS("ip6t_CT");
MODULE_ALIAS("arpt_NFQUEUE");
static u32 jhash_initval __read_mostly;
+static bool rnd_inited __read_mostly;
static unsigned int
nfqueue_tg(struct sk_buff *skb, const struct xt_target_param *par)
const struct xt_NFQ_info_v1 *info = par->targinfo;
u32 maxid;
+ if (unlikely(!rnd_inited)) {
+ get_random_bytes(&jhash_initval, sizeof(jhash_initval));
+ rnd_inited = true;
+ }
if (info->queues_total == 0) {
pr_err("NFQUEUE: number of total queues is 0\n");
return false;
static int __init nfqueue_tg_init(void)
{
- get_random_bytes(&jhash_initval, sizeof(jhash_initval));
return xt_register_targets(nfqueue_tg_reg, ARRAY_SIZE(nfqueue_tg_reg));
}
#define RATEEST_HSIZE 16
static struct hlist_head rateest_hash[RATEEST_HSIZE] __read_mostly;
static unsigned int jhash_rnd __read_mostly;
+static bool rnd_inited __read_mostly;
static unsigned int xt_rateest_hash(const char *name)
{
struct gnet_estimator est;
} cfg;
+ if (unlikely(!rnd_inited)) {
+ get_random_bytes(&jhash_rnd, sizeof(jhash_rnd));
+ rnd_inited = true;
+ }
+
est = xt_rateest_lookup(info->name);
if (est) {
/*
for (i = 0; i < ARRAY_SIZE(rateest_hash); i++)
INIT_HLIST_HEAD(&rateest_hash[i]);
- get_random_bytes(&jhash_rnd, sizeof(jhash_rnd));
return xt_register_target(&xt_rateest_tg_reg);
}
tcplen = skb->len - tcphoff;
tcph = (struct tcphdr *)(skb_network_header(skb) + tcphoff);
- /* Since it passed flags test in tcp match, we know it is is
- not a fragment, and has data >= tcp header length. SYN
- packets should not contain data: if they did, then we risk
- running over MTU, sending Frag Needed and breaking things
- badly. --RR */
- if (tcplen != tcph->doff*4) {
- if (net_ratelimit())
- printk(KERN_ERR "xt_TCPMSS: bad length (%u bytes)\n",
- skb->len);
+ /* Header cannot be larger than the packet */
+ if (tcplen < tcph->doff*4)
return -1;
- }
if (info->mss == XT_TCPMSS_CLAMP_PMTU) {
if (dst_mtu(skb_dst(skb)) <= minlen) {
}
}
+ /* There is data after the header so the option can't be added
+ without moving it, and doing so may make the SYN packet
+ itself too large. Accept the packet unmodified instead. */
+ if (tcplen > tcph->doff*4)
+ return 0;
+
/*
* MSS Option not found ?! add it..
*/
spinlock_t lock;
};
-static u_int32_t connlimit_rnd;
-static bool connlimit_rnd_inited;
+static u_int32_t connlimit_rnd __read_mostly;
+static bool connlimit_rnd_inited __read_mostly;
static inline unsigned int connlimit_iphash(__be32 addr)
{
- if (unlikely(!connlimit_rnd_inited)) {
- get_random_bytes(&connlimit_rnd, sizeof(connlimit_rnd));
- connlimit_rnd_inited = true;
- }
return jhash_1word((__force __u32)addr, connlimit_rnd) & 0xFF;
}
union nf_inet_addr res;
unsigned int i;
- if (unlikely(!connlimit_rnd_inited)) {
- get_random_bytes(&connlimit_rnd, sizeof(connlimit_rnd));
- connlimit_rnd_inited = true;
- }
-
for (i = 0; i < ARRAY_SIZE(addr->ip6); ++i)
res.ip6[i] = addr->ip6[i] & mask->ip6[i];
}
}
-static int count_them(struct xt_connlimit_data *data,
+static int count_them(struct net *net,
+ struct xt_connlimit_data *data,
const struct nf_conntrack_tuple *tuple,
const union nf_inet_addr *addr,
const union nf_inet_addr *mask,
/* check the saved connections */
list_for_each_entry_safe(conn, tmp, hash, list) {
- found = nf_conntrack_find_get(&init_net, &conn->tuple);
+ found = nf_conntrack_find_get(net, &conn->tuple);
found_ct = NULL;
if (found != NULL)
static bool
connlimit_mt(const struct sk_buff *skb, const struct xt_match_param *par)
{
+ struct net *net = dev_net(par->in ? par->in : par->out);
const struct xt_connlimit_info *info = par->matchinfo;
union nf_inet_addr addr;
struct nf_conntrack_tuple tuple;
}
spin_lock_bh(&info->data->lock);
- connections = count_them(info->data, tuple_ptr, &addr,
+ connections = count_them(net, info->data, tuple_ptr, &addr,
&info->mask, par->family);
spin_unlock_bh(&info->data->lock);
struct xt_connlimit_info *info = par->matchinfo;
unsigned int i;
+ if (unlikely(!connlimit_rnd_inited)) {
+ get_random_bytes(&connlimit_rnd, sizeof(connlimit_rnd));
+ connlimit_rnd_inited = true;
+ }
if (nf_ct_l3proto_try_module_get(par->family) < 0) {
printk(KERN_WARNING "cannot load conntrack support for "
"address family %u\n", par->family);
#endif
#include <net/net_namespace.h>
+#include <net/netns/generic.h>
#include <linux/netfilter/x_tables.h>
#include <linux/netfilter_ipv4/ip_tables.h>
MODULE_ALIAS("ipt_hashlimit");
MODULE_ALIAS("ip6t_hashlimit");
+struct hashlimit_net {
+ struct hlist_head htables;
+ struct proc_dir_entry *ipt_hashlimit;
+ struct proc_dir_entry *ip6t_hashlimit;
+};
+
+static int hashlimit_net_id;
+static inline struct hashlimit_net *hashlimit_pernet(struct net *net)
+{
+ return net_generic(net, hashlimit_net_id);
+}
+
/* need to declare this at the top */
-static struct proc_dir_entry *hashlimit_procdir4;
-static struct proc_dir_entry *hashlimit_procdir6;
static const struct file_operations dl_file_ops;
/* hash table crap */
struct xt_hashlimit_htable {
struct hlist_node node; /* global list of all htables */
- atomic_t use;
+ int use;
u_int8_t family;
+ bool rnd_initialized;
struct hashlimit_cfg1 cfg; /* config */
/* used internally */
spinlock_t lock; /* lock for list_head */
u_int32_t rnd; /* random seed for hash */
- int rnd_initialized;
unsigned int count; /* number entries in table */
struct timer_list timer; /* timer for gc */
/* seq_file stuff */
struct proc_dir_entry *pde;
+ struct net *net;
struct hlist_head hash[0]; /* hashtable itself */
};
-static DEFINE_SPINLOCK(hashlimit_lock); /* protects htables list */
-static DEFINE_MUTEX(hlimit_mutex); /* additional checkentry protection */
-static HLIST_HEAD(hashlimit_htables);
+static DEFINE_MUTEX(hashlimit_mutex); /* protects htables list */
static struct kmem_cache *hashlimit_cachep __read_mostly;
static inline bool dst_cmp(const struct dsthash_ent *ent,
* the first hashtable entry */
if (!ht->rnd_initialized) {
get_random_bytes(&ht->rnd, sizeof(ht->rnd));
- ht->rnd_initialized = 1;
+ ht->rnd_initialized = true;
}
if (ht->cfg.max && ht->count >= ht->cfg.max) {
}
static void htable_gc(unsigned long htlong);
-static int htable_create_v0(struct xt_hashlimit_info *minfo, u_int8_t family)
+static int htable_create_v0(struct net *net, struct xt_hashlimit_info *minfo, u_int8_t family)
{
+ struct hashlimit_net *hashlimit_net = hashlimit_pernet(net);
struct xt_hashlimit_htable *hinfo;
unsigned int size;
unsigned int i;
for (i = 0; i < hinfo->cfg.size; i++)
INIT_HLIST_HEAD(&hinfo->hash[i]);
- atomic_set(&hinfo->use, 1);
+ hinfo->use = 1;
hinfo->count = 0;
hinfo->family = family;
- hinfo->rnd_initialized = 0;
+ hinfo->rnd_initialized = false;
spin_lock_init(&hinfo->lock);
hinfo->pde = proc_create_data(minfo->name, 0,
(family == NFPROTO_IPV4) ?
- hashlimit_procdir4 : hashlimit_procdir6,
+ hashlimit_net->ipt_hashlimit : hashlimit_net->ip6t_hashlimit,
&dl_file_ops, hinfo);
if (!hinfo->pde) {
vfree(hinfo);
return -1;
}
+ hinfo->net = net;
setup_timer(&hinfo->timer, htable_gc, (unsigned long )hinfo);
hinfo->timer.expires = jiffies + msecs_to_jiffies(hinfo->cfg.gc_interval);
add_timer(&hinfo->timer);
- spin_lock_bh(&hashlimit_lock);
- hlist_add_head(&hinfo->node, &hashlimit_htables);
- spin_unlock_bh(&hashlimit_lock);
+ mutex_lock(&hashlimit_mutex);
+ hlist_add_head(&hinfo->node, &hashlimit_net->htables);
+ mutex_unlock(&hashlimit_mutex);
return 0;
}
-static int htable_create(struct xt_hashlimit_mtinfo1 *minfo, u_int8_t family)
+static int htable_create(struct net *net, struct xt_hashlimit_mtinfo1 *minfo,
+ u_int8_t family)
{
+ struct hashlimit_net *hashlimit_net = hashlimit_pernet(net);
struct xt_hashlimit_htable *hinfo;
unsigned int size;
unsigned int i;
for (i = 0; i < hinfo->cfg.size; i++)
INIT_HLIST_HEAD(&hinfo->hash[i]);
- atomic_set(&hinfo->use, 1);
+ hinfo->use = 1;
hinfo->count = 0;
hinfo->family = family;
- hinfo->rnd_initialized = 0;
+ hinfo->rnd_initialized = false;
spin_lock_init(&hinfo->lock);
hinfo->pde = proc_create_data(minfo->name, 0,
(family == NFPROTO_IPV4) ?
- hashlimit_procdir4 : hashlimit_procdir6,
+ hashlimit_net->ipt_hashlimit : hashlimit_net->ip6t_hashlimit,
&dl_file_ops, hinfo);
if (hinfo->pde == NULL) {
vfree(hinfo);
return -1;
}
+ hinfo->net = net;
setup_timer(&hinfo->timer, htable_gc, (unsigned long)hinfo);
hinfo->timer.expires = jiffies + msecs_to_jiffies(hinfo->cfg.gc_interval);
add_timer(&hinfo->timer);
- spin_lock_bh(&hashlimit_lock);
- hlist_add_head(&hinfo->node, &hashlimit_htables);
- spin_unlock_bh(&hashlimit_lock);
+ mutex_lock(&hashlimit_mutex);
+ hlist_add_head(&hinfo->node, &hashlimit_net->htables);
+ mutex_unlock(&hashlimit_mutex);
return 0;
}
static void htable_destroy(struct xt_hashlimit_htable *hinfo)
{
+ struct hashlimit_net *hashlimit_net = hashlimit_pernet(hinfo->net);
+ struct proc_dir_entry *parent;
+
del_timer_sync(&hinfo->timer);
- /* remove proc entry */
- remove_proc_entry(hinfo->pde->name,
- hinfo->family == NFPROTO_IPV4 ? hashlimit_procdir4 :
- hashlimit_procdir6);
+ if (hinfo->family == NFPROTO_IPV4)
+ parent = hashlimit_net->ipt_hashlimit;
+ else
+ parent = hashlimit_net->ip6t_hashlimit;
+ remove_proc_entry(hinfo->pde->name, parent);
htable_selective_cleanup(hinfo, select_all);
vfree(hinfo);
}
-static struct xt_hashlimit_htable *htable_find_get(const char *name,
+static struct xt_hashlimit_htable *htable_find_get(struct net *net,
+ const char *name,
u_int8_t family)
{
+ struct hashlimit_net *hashlimit_net = hashlimit_pernet(net);
struct xt_hashlimit_htable *hinfo;
struct hlist_node *pos;
- spin_lock_bh(&hashlimit_lock);
- hlist_for_each_entry(hinfo, pos, &hashlimit_htables, node) {
+ hlist_for_each_entry(hinfo, pos, &hashlimit_net->htables, node) {
if (!strcmp(name, hinfo->pde->name) &&
hinfo->family == family) {
- atomic_inc(&hinfo->use);
- spin_unlock_bh(&hashlimit_lock);
+ hinfo->use++;
return hinfo;
}
}
- spin_unlock_bh(&hashlimit_lock);
return NULL;
}
static void htable_put(struct xt_hashlimit_htable *hinfo)
{
- if (atomic_dec_and_test(&hinfo->use)) {
- spin_lock_bh(&hashlimit_lock);
+ mutex_lock(&hashlimit_mutex);
+ if (--hinfo->use == 0) {
hlist_del(&hinfo->node);
- spin_unlock_bh(&hashlimit_lock);
htable_destroy(hinfo);
}
+ mutex_unlock(&hashlimit_mutex);
}
/* The algorithm used is the Simple Token Bucket Filter (TBF)
static bool hashlimit_mt_check_v0(const struct xt_mtchk_param *par)
{
+ struct net *net = par->net;
struct xt_hashlimit_info *r = par->matchinfo;
/* Check for overflow. */
if (r->name[sizeof(r->name) - 1] != '\0')
return false;
- /* This is the best we've got: We cannot release and re-grab lock,
- * since checkentry() is called before x_tables.c grabs xt_mutex.
- * We also cannot grab the hashtable spinlock, since htable_create will
- * call vmalloc, and that can sleep. And we cannot just re-search
- * the list of htable's in htable_create(), since then we would
- * create duplicate proc files. -HW */
- mutex_lock(&hlimit_mutex);
- r->hinfo = htable_find_get(r->name, par->match->family);
- if (!r->hinfo && htable_create_v0(r, par->match->family) != 0) {
- mutex_unlock(&hlimit_mutex);
+ mutex_lock(&hashlimit_mutex);
+ r->hinfo = htable_find_get(net, r->name, par->match->family);
+ if (!r->hinfo && htable_create_v0(net, r, par->match->family) != 0) {
+ mutex_unlock(&hashlimit_mutex);
return false;
}
- mutex_unlock(&hlimit_mutex);
+ mutex_unlock(&hashlimit_mutex);
return true;
}
static bool hashlimit_mt_check(const struct xt_mtchk_param *par)
{
+ struct net *net = par->net;
struct xt_hashlimit_mtinfo1 *info = par->matchinfo;
/* Check for overflow. */
return false;
}
- /* This is the best we've got: We cannot release and re-grab lock,
- * since checkentry() is called before x_tables.c grabs xt_mutex.
- * We also cannot grab the hashtable spinlock, since htable_create will
- * call vmalloc, and that can sleep. And we cannot just re-search
- * the list of htable's in htable_create(), since then we would
- * create duplicate proc files. -HW */
- mutex_lock(&hlimit_mutex);
- info->hinfo = htable_find_get(info->name, par->match->family);
- if (!info->hinfo && htable_create(info, par->match->family) != 0) {
- mutex_unlock(&hlimit_mutex);
+ mutex_lock(&hashlimit_mutex);
+ info->hinfo = htable_find_get(net, info->name, par->match->family);
+ if (!info->hinfo && htable_create(net, info, par->match->family) != 0) {
+ mutex_unlock(&hashlimit_mutex);
return false;
}
- mutex_unlock(&hlimit_mutex);
+ mutex_unlock(&hashlimit_mutex);
return true;
}
static void *dl_seq_start(struct seq_file *s, loff_t *pos)
__acquires(htable->lock)
{
- struct proc_dir_entry *pde = s->private;
- struct xt_hashlimit_htable *htable = pde->data;
+ struct xt_hashlimit_htable *htable = s->private;
unsigned int *bucket;
spin_lock_bh(&htable->lock);
static void *dl_seq_next(struct seq_file *s, void *v, loff_t *pos)
{
- struct proc_dir_entry *pde = s->private;
- struct xt_hashlimit_htable *htable = pde->data;
+ struct xt_hashlimit_htable *htable = s->private;
unsigned int *bucket = (unsigned int *)v;
*pos = ++(*bucket);
static void dl_seq_stop(struct seq_file *s, void *v)
__releases(htable->lock)
{
- struct proc_dir_entry *pde = s->private;
- struct xt_hashlimit_htable *htable = pde->data;
+ struct xt_hashlimit_htable *htable = s->private;
unsigned int *bucket = (unsigned int *)v;
kfree(bucket);
static int dl_seq_show(struct seq_file *s, void *v)
{
- struct proc_dir_entry *pde = s->private;
- struct xt_hashlimit_htable *htable = pde->data;
+ struct xt_hashlimit_htable *htable = s->private;
unsigned int *bucket = (unsigned int *)v;
struct dsthash_ent *ent;
struct hlist_node *pos;
if (!ret) {
struct seq_file *sf = file->private_data;
- sf->private = PDE(inode);
+ sf->private = PDE(inode)->data;
}
return ret;
}
.release = seq_release
};
+static int __net_init hashlimit_proc_net_init(struct net *net)
+{
+ struct hashlimit_net *hashlimit_net = hashlimit_pernet(net);
+
+ hashlimit_net->ipt_hashlimit = proc_mkdir("ipt_hashlimit", net->proc_net);
+ if (!hashlimit_net->ipt_hashlimit)
+ return -ENOMEM;
+#if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE)
+ hashlimit_net->ip6t_hashlimit = proc_mkdir("ip6t_hashlimit", net->proc_net);
+ if (!hashlimit_net->ip6t_hashlimit) {
+ proc_net_remove(net, "ipt_hashlimit");
+ return -ENOMEM;
+ }
+#endif
+ return 0;
+}
+
+static void __net_exit hashlimit_proc_net_exit(struct net *net)
+{
+ proc_net_remove(net, "ipt_hashlimit");
+#if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE)
+ proc_net_remove(net, "ip6t_hashlimit");
+#endif
+}
+
+static int __net_init hashlimit_net_init(struct net *net)
+{
+ struct hashlimit_net *hashlimit_net = hashlimit_pernet(net);
+
+ INIT_HLIST_HEAD(&hashlimit_net->htables);
+ return hashlimit_proc_net_init(net);
+}
+
+static void __net_exit hashlimit_net_exit(struct net *net)
+{
+ struct hashlimit_net *hashlimit_net = hashlimit_pernet(net);
+
+ BUG_ON(!hlist_empty(&hashlimit_net->htables));
+ hashlimit_proc_net_exit(net);
+}
+
+static struct pernet_operations hashlimit_net_ops = {
+ .init = hashlimit_net_init,
+ .exit = hashlimit_net_exit,
+ .id = &hashlimit_net_id,
+ .size = sizeof(struct hashlimit_net),
+};
+
static int __init hashlimit_mt_init(void)
{
int err;
+ err = register_pernet_subsys(&hashlimit_net_ops);
+ if (err < 0)
+ return err;
err = xt_register_matches(hashlimit_mt_reg,
ARRAY_SIZE(hashlimit_mt_reg));
if (err < 0)
printk(KERN_ERR "xt_hashlimit: unable to create slab cache\n");
goto err2;
}
- hashlimit_procdir4 = proc_mkdir("ipt_hashlimit", init_net.proc_net);
- if (!hashlimit_procdir4) {
- printk(KERN_ERR "xt_hashlimit: unable to create proc dir "
- "entry\n");
- goto err3;
- }
- err = 0;
-#if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE)
- hashlimit_procdir6 = proc_mkdir("ip6t_hashlimit", init_net.proc_net);
- if (!hashlimit_procdir6) {
- printk(KERN_ERR "xt_hashlimit: unable to create proc dir "
- "entry\n");
- err = -ENOMEM;
- }
-#endif
- if (!err)
- return 0;
- remove_proc_entry("ipt_hashlimit", init_net.proc_net);
-err3:
- kmem_cache_destroy(hashlimit_cachep);
+ return 0;
+
err2:
xt_unregister_matches(hashlimit_mt_reg, ARRAY_SIZE(hashlimit_mt_reg));
err1:
+ unregister_pernet_subsys(&hashlimit_net_ops);
return err;
}
static void __exit hashlimit_mt_exit(void)
{
- remove_proc_entry("ipt_hashlimit", init_net.proc_net);
-#if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE)
- remove_proc_entry("ip6t_hashlimit", init_net.proc_net);
-#endif
kmem_cache_destroy(hashlimit_cachep);
xt_unregister_matches(hashlimit_mt_reg, ARRAY_SIZE(hashlimit_mt_reg));
+ unregister_pernet_subsys(&hashlimit_net_ops);
}
module_init(hashlimit_mt_init);
if (info->flags & XT_OSF_LOG)
nf_log_packet(p->family, p->hooknum, skb,
p->in, p->out, NULL,
- "%s [%s:%s] : %pi4:%d -> %pi4:%d hops=%d\n",
+ "%s [%s:%s] : %pI4:%d -> %pI4:%d hops=%d\n",
f->genre, f->version, f->subtype,
&ip->saddr, ntohs(tcp->source),
&ip->daddr, ntohs(tcp->dest),
if (!fcount && (info->flags & XT_OSF_LOG))
nf_log_packet(p->family, p->hooknum, skb, p->in, p->out, NULL,
- "Remote OS is not known: %pi4:%u -> %pi4:%u\n",
+ "Remote OS is not known: %pI4:%u -> %pI4:%u\n",
&ip->saddr, ntohs(tcp->source),
&ip->daddr, ntohs(tcp->dest));
#include <linux/skbuff.h>
#include <linux/inet.h>
#include <net/net_namespace.h>
+#include <net/netns/generic.h>
#include <linux/netfilter/x_tables.h>
#include <linux/netfilter/xt_recent.h>
struct list_head iphash[0];
};
-static LIST_HEAD(tables);
+struct recent_net {
+ struct list_head tables;
+#ifdef CONFIG_PROC_FS
+ struct proc_dir_entry *xt_recent;
+#ifdef CONFIG_NETFILTER_XT_MATCH_RECENT_PROC_COMPAT
+ struct proc_dir_entry *ipt_recent;
+#endif
+#endif
+};
+
+static int recent_net_id;
+static inline struct recent_net *recent_pernet(struct net *net)
+{
+ return net_generic(net, recent_net_id);
+}
+
static DEFINE_SPINLOCK(recent_lock);
static DEFINE_MUTEX(recent_mutex);
#ifdef CONFIG_PROC_FS
-#ifdef CONFIG_NETFILTER_XT_MATCH_RECENT_PROC_COMPAT
-static struct proc_dir_entry *proc_old_dir;
-#endif
-static struct proc_dir_entry *recent_proc_dir;
static const struct file_operations recent_old_fops, recent_mt_fops;
#endif
-static u_int32_t hash_rnd;
-static bool hash_rnd_initted;
+static u_int32_t hash_rnd __read_mostly;
+static bool hash_rnd_inited __read_mostly;
-static unsigned int recent_entry_hash4(const union nf_inet_addr *addr)
+static inline unsigned int recent_entry_hash4(const union nf_inet_addr *addr)
{
- if (!hash_rnd_initted) {
- get_random_bytes(&hash_rnd, sizeof(hash_rnd));
- hash_rnd_initted = true;
- }
return jhash_1word((__force u32)addr->ip, hash_rnd) &
(ip_list_hash_size - 1);
}
-static unsigned int recent_entry_hash6(const union nf_inet_addr *addr)
+static inline unsigned int recent_entry_hash6(const union nf_inet_addr *addr)
{
- if (!hash_rnd_initted) {
- get_random_bytes(&hash_rnd, sizeof(hash_rnd));
- hash_rnd_initted = true;
- }
return jhash2((u32 *)addr->ip6, ARRAY_SIZE(addr->ip6), hash_rnd) &
(ip_list_hash_size - 1);
}
list_move_tail(&e->lru_list, &t->lru_list);
}
-static struct recent_table *recent_table_lookup(const char *name)
+static struct recent_table *recent_table_lookup(struct recent_net *recent_net,
+ const char *name)
{
struct recent_table *t;
- list_for_each_entry(t, &tables, list)
+ list_for_each_entry(t, &recent_net->tables, list)
if (!strcmp(t->name, name))
return t;
return NULL;
static bool
recent_mt(const struct sk_buff *skb, const struct xt_match_param *par)
{
+ struct net *net = dev_net(par->in ? par->in : par->out);
+ struct recent_net *recent_net = recent_pernet(net);
const struct xt_recent_mtinfo *info = par->matchinfo;
struct recent_table *t;
struct recent_entry *e;
ttl++;
spin_lock_bh(&recent_lock);
- t = recent_table_lookup(info->name);
+ t = recent_table_lookup(recent_net, info->name);
e = recent_entry_lookup(t, &addr, par->match->family,
(info->check_set & XT_RECENT_TTL) ? ttl : 0);
if (e == NULL) {
static bool recent_mt_check(const struct xt_mtchk_param *par)
{
+ struct recent_net *recent_net = recent_pernet(par->net);
const struct xt_recent_mtinfo *info = par->matchinfo;
struct recent_table *t;
#ifdef CONFIG_PROC_FS
unsigned i;
bool ret = false;
+ if (unlikely(!hash_rnd_inited)) {
+ get_random_bytes(&hash_rnd, sizeof(hash_rnd));
+ hash_rnd_inited = true;
+ }
if (hweight8(info->check_set &
(XT_RECENT_SET | XT_RECENT_REMOVE |
XT_RECENT_CHECK | XT_RECENT_UPDATE)) != 1)
return false;
mutex_lock(&recent_mutex);
- t = recent_table_lookup(info->name);
+ t = recent_table_lookup(recent_net, info->name);
if (t != NULL) {
t->refcnt++;
ret = true;
for (i = 0; i < ip_list_hash_size; i++)
INIT_LIST_HEAD(&t->iphash[i]);
#ifdef CONFIG_PROC_FS
- pde = proc_create_data(t->name, ip_list_perms, recent_proc_dir,
+ pde = proc_create_data(t->name, ip_list_perms, recent_net->xt_recent,
&recent_mt_fops, t);
if (pde == NULL) {
kfree(t);
pde->uid = ip_list_uid;
pde->gid = ip_list_gid;
#ifdef CONFIG_NETFILTER_XT_MATCH_RECENT_PROC_COMPAT
- pde = proc_create_data(t->name, ip_list_perms, proc_old_dir,
+ pde = proc_create_data(t->name, ip_list_perms, recent_net->ipt_recent,
&recent_old_fops, t);
if (pde == NULL) {
- remove_proc_entry(t->name, proc_old_dir);
+ remove_proc_entry(t->name, recent_net->xt_recent);
kfree(t);
goto out;
}
#endif
#endif
spin_lock_bh(&recent_lock);
- list_add_tail(&t->list, &tables);
+ list_add_tail(&t->list, &recent_net->tables);
spin_unlock_bh(&recent_lock);
ret = true;
out:
static void recent_mt_destroy(const struct xt_mtdtor_param *par)
{
+ struct recent_net *recent_net = recent_pernet(par->net);
const struct xt_recent_mtinfo *info = par->matchinfo;
struct recent_table *t;
mutex_lock(&recent_mutex);
- t = recent_table_lookup(info->name);
+ t = recent_table_lookup(recent_net, info->name);
if (--t->refcnt == 0) {
spin_lock_bh(&recent_lock);
list_del(&t->list);
spin_unlock_bh(&recent_lock);
#ifdef CONFIG_PROC_FS
#ifdef CONFIG_NETFILTER_XT_MATCH_RECENT_PROC_COMPAT
- remove_proc_entry(t->name, proc_old_dir);
+ remove_proc_entry(t->name, recent_net->ipt_recent);
#endif
- remove_proc_entry(t->name, recent_proc_dir);
+ remove_proc_entry(t->name, recent_net->xt_recent);
#endif
recent_table_flush(t);
kfree(t);
.release = seq_release_private,
.owner = THIS_MODULE,
};
+
+static int __net_init recent_proc_net_init(struct net *net)
+{
+ struct recent_net *recent_net = recent_pernet(net);
+
+ recent_net->xt_recent = proc_mkdir("xt_recent", net->proc_net);
+ if (!recent_net->xt_recent)
+ return -ENOMEM;
+#ifdef CONFIG_NETFILTER_XT_MATCH_RECENT_PROC_COMPAT
+ recent_net->ipt_recent = proc_mkdir("ipt_recent", net->proc_net);
+ if (!recent_net->ipt_recent) {
+ proc_net_remove(net, "xt_recent");
+ return -ENOMEM;
+ }
+#endif
+ return 0;
+}
+
+static void __net_exit recent_proc_net_exit(struct net *net)
+{
+#ifdef CONFIG_NETFILTER_XT_MATCH_RECENT_PROC_COMPAT
+ proc_net_remove(net, "ipt_recent");
+#endif
+ proc_net_remove(net, "xt_recent");
+}
+#else
+static inline int recent_proc_net_init(struct net *net)
+{
+ return 0;
+}
+
+static inline void recent_proc_net_exit(struct net *net)
+{
+}
#endif /* CONFIG_PROC_FS */
+static int __net_init recent_net_init(struct net *net)
+{
+ struct recent_net *recent_net = recent_pernet(net);
+
+ INIT_LIST_HEAD(&recent_net->tables);
+ return recent_proc_net_init(net);
+}
+
+static void __net_exit recent_net_exit(struct net *net)
+{
+ struct recent_net *recent_net = recent_pernet(net);
+
+ BUG_ON(!list_empty(&recent_net->tables));
+ recent_proc_net_exit(net);
+}
+
+static struct pernet_operations recent_net_ops = {
+ .init = recent_net_init,
+ .exit = recent_net_exit,
+ .id = &recent_net_id,
+ .size = sizeof(struct recent_net),
+};
+
static struct xt_match recent_mt_reg[] __read_mostly = {
{
.name = "recent",
return -EINVAL;
ip_list_hash_size = 1 << fls(ip_list_tot);
- err = xt_register_matches(recent_mt_reg, ARRAY_SIZE(recent_mt_reg));
-#ifdef CONFIG_PROC_FS
+ err = register_pernet_subsys(&recent_net_ops);
if (err)
return err;
- recent_proc_dir = proc_mkdir("xt_recent", init_net.proc_net);
- if (recent_proc_dir == NULL) {
- xt_unregister_matches(recent_mt_reg, ARRAY_SIZE(recent_mt_reg));
- err = -ENOMEM;
- }
-#ifdef CONFIG_NETFILTER_XT_MATCH_RECENT_PROC_COMPAT
- if (err < 0)
- return err;
- proc_old_dir = proc_mkdir("ipt_recent", init_net.proc_net);
- if (proc_old_dir == NULL) {
- remove_proc_entry("xt_recent", init_net.proc_net);
- xt_unregister_matches(recent_mt_reg, ARRAY_SIZE(recent_mt_reg));
- err = -ENOMEM;
- }
-#endif
-#endif
+ err = xt_register_matches(recent_mt_reg, ARRAY_SIZE(recent_mt_reg));
+ if (err)
+ unregister_pernet_subsys(&recent_net_ops);
return err;
}
static void __exit recent_mt_exit(void)
{
- BUG_ON(!list_empty(&tables));
xt_unregister_matches(recent_mt_reg, ARRAY_SIZE(recent_mt_reg));
-#ifdef CONFIG_PROC_FS
-#ifdef CONFIG_NETFILTER_XT_MATCH_RECENT_PROC_COMPAT
- remove_proc_entry("ipt_recent", init_net.proc_net);
-#endif
- remove_proc_entry("xt_recent", init_net.proc_net);
-#endif
+ unregister_pernet_subsys(&recent_net_ops);
}
module_init(recent_mt_init);