|
@@ -16,7 +16,6 @@
|
|
|
#define NF_LOG_PREFIXLEN 128
|
|
|
#define NFLOGGER_NAME_LEN 64
|
|
|
|
|
|
-static const struct nf_logger __rcu *nf_loggers[NFPROTO_NUMPROTO] __read_mostly;
|
|
|
static struct list_head nf_loggers_l[NFPROTO_NUMPROTO] __read_mostly;
|
|
|
static DEFINE_MUTEX(nf_log_mutex);
|
|
|
|
|
@@ -32,13 +31,52 @@ static struct nf_logger *__find_logger(int pf, const char *str_logger)
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
+void nf_log_set(struct net *net, u_int8_t pf, const struct nf_logger *logger)
|
|
|
+{
|
|
|
+ const struct nf_logger *log;
|
|
|
+
|
|
|
+ if (!net_eq(net, &init_net))
|
|
|
+ return;
|
|
|
+
|
|
|
+ if (pf == NFPROTO_UNSPEC)
|
|
|
+ return;
|
|
|
+
|
|
|
+ mutex_lock(&nf_log_mutex);
|
|
|
+ log = rcu_dereference_protected(net->nf.nf_loggers[pf],
|
|
|
+ lockdep_is_held(&nf_log_mutex));
|
|
|
+ if (log == NULL)
|
|
|
+ rcu_assign_pointer(net->nf.nf_loggers[pf], logger);
|
|
|
+
|
|
|
+ mutex_unlock(&nf_log_mutex);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(nf_log_set);
|
|
|
+
|
|
|
+void nf_log_unset(struct net *net, const struct nf_logger *logger)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+ const struct nf_logger *log;
|
|
|
+
|
|
|
+ if (!net_eq(net, &init_net))
|
|
|
+ return;
|
|
|
+
|
|
|
+ mutex_lock(&nf_log_mutex);
|
|
|
+ for (i = 0; i < NFPROTO_NUMPROTO; i++) {
|
|
|
+ log = rcu_dereference_protected(net->nf.nf_loggers[i],
|
|
|
+ lockdep_is_held(&nf_log_mutex));
|
|
|
+ if (log == logger)
|
|
|
+ RCU_INIT_POINTER(net->nf.nf_loggers[i], NULL);
|
|
|
+ }
|
|
|
+ mutex_unlock(&nf_log_mutex);
|
|
|
+ synchronize_rcu();
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(nf_log_unset);
|
|
|
+
|
|
|
/* return EEXIST if the same logger is registered, 0 on success. */
|
|
|
int nf_log_register(u_int8_t pf, struct nf_logger *logger)
|
|
|
{
|
|
|
- const struct nf_logger *llog;
|
|
|
int i;
|
|
|
|
|
|
- if (pf >= ARRAY_SIZE(nf_loggers))
|
|
|
+ if (pf >= ARRAY_SIZE(init_net.nf.nf_loggers))
|
|
|
return -EINVAL;
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(logger->list); i++)
|
|
@@ -52,63 +90,62 @@ int nf_log_register(u_int8_t pf, struct nf_logger *logger)
|
|
|
} else {
|
|
|
/* register at end of list to honor first register win */
|
|
|
list_add_tail(&logger->list[pf], &nf_loggers_l[pf]);
|
|
|
- llog = rcu_dereference_protected(nf_loggers[pf],
|
|
|
- lockdep_is_held(&nf_log_mutex));
|
|
|
- if (llog == NULL)
|
|
|
- rcu_assign_pointer(nf_loggers[pf], logger);
|
|
|
}
|
|
|
|
|
|
mutex_unlock(&nf_log_mutex);
|
|
|
|
|
|
+ nf_log_set(&init_net, pf, logger);
|
|
|
return 0;
|
|
|
}
|
|
|
EXPORT_SYMBOL(nf_log_register);
|
|
|
|
|
|
void nf_log_unregister(struct nf_logger *logger)
|
|
|
{
|
|
|
- const struct nf_logger *c_logger;
|
|
|
int i;
|
|
|
|
|
|
mutex_lock(&nf_log_mutex);
|
|
|
- for (i = 0; i < ARRAY_SIZE(nf_loggers); i++) {
|
|
|
- c_logger = rcu_dereference_protected(nf_loggers[i],
|
|
|
- lockdep_is_held(&nf_log_mutex));
|
|
|
- if (c_logger == logger)
|
|
|
- RCU_INIT_POINTER(nf_loggers[i], NULL);
|
|
|
+ for (i = 0; i < NFPROTO_NUMPROTO; i++)
|
|
|
list_del(&logger->list[i]);
|
|
|
- }
|
|
|
mutex_unlock(&nf_log_mutex);
|
|
|
|
|
|
- synchronize_rcu();
|
|
|
+ nf_log_unset(&init_net, logger);
|
|
|
}
|
|
|
EXPORT_SYMBOL(nf_log_unregister);
|
|
|
|
|
|
-int nf_log_bind_pf(u_int8_t pf, const struct nf_logger *logger)
|
|
|
+int nf_log_bind_pf(struct net *net, u_int8_t pf,
|
|
|
+ const struct nf_logger *logger)
|
|
|
{
|
|
|
- if (pf >= ARRAY_SIZE(nf_loggers))
|
|
|
+ if (!net_eq(net, &init_net))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ if (pf >= ARRAY_SIZE(net->nf.nf_loggers))
|
|
|
return -EINVAL;
|
|
|
mutex_lock(&nf_log_mutex);
|
|
|
if (__find_logger(pf, logger->name) == NULL) {
|
|
|
mutex_unlock(&nf_log_mutex);
|
|
|
return -ENOENT;
|
|
|
}
|
|
|
- rcu_assign_pointer(nf_loggers[pf], logger);
|
|
|
+ rcu_assign_pointer(net->nf.nf_loggers[pf], logger);
|
|
|
mutex_unlock(&nf_log_mutex);
|
|
|
return 0;
|
|
|
}
|
|
|
EXPORT_SYMBOL(nf_log_bind_pf);
|
|
|
|
|
|
-void nf_log_unbind_pf(u_int8_t pf)
|
|
|
+void nf_log_unbind_pf(struct net *net, u_int8_t pf)
|
|
|
{
|
|
|
- if (pf >= ARRAY_SIZE(nf_loggers))
|
|
|
+ if (!net_eq(net, &init_net))
|
|
|
+ return;
|
|
|
+
|
|
|
+ if (pf >= ARRAY_SIZE(net->nf.nf_loggers))
|
|
|
return;
|
|
|
mutex_lock(&nf_log_mutex);
|
|
|
- RCU_INIT_POINTER(nf_loggers[pf], NULL);
|
|
|
+ RCU_INIT_POINTER(net->nf.nf_loggers[pf], NULL);
|
|
|
mutex_unlock(&nf_log_mutex);
|
|
|
}
|
|
|
EXPORT_SYMBOL(nf_log_unbind_pf);
|
|
|
|
|
|
-void nf_log_packet(u_int8_t pf,
|
|
|
+void nf_log_packet(struct net *net,
|
|
|
+ u_int8_t pf,
|
|
|
unsigned int hooknum,
|
|
|
const struct sk_buff *skb,
|
|
|
const struct net_device *in,
|
|
@@ -120,8 +157,11 @@ void nf_log_packet(u_int8_t pf,
|
|
|
char prefix[NF_LOG_PREFIXLEN];
|
|
|
const struct nf_logger *logger;
|
|
|
|
|
|
+ if (!net_eq(net, &init_net))
|
|
|
+ return;
|
|
|
+
|
|
|
rcu_read_lock();
|
|
|
- logger = rcu_dereference(nf_loggers[pf]);
|
|
|
+ logger = rcu_dereference(net->nf.nf_loggers[pf]);
|
|
|
if (logger) {
|
|
|
va_start(args, fmt);
|
|
|
vsnprintf(prefix, sizeof(prefix), fmt, args);
|
|
@@ -135,9 +175,11 @@ EXPORT_SYMBOL(nf_log_packet);
|
|
|
#ifdef CONFIG_PROC_FS
|
|
|
static void *seq_start(struct seq_file *seq, loff_t *pos)
|
|
|
{
|
|
|
+ struct net *net = seq_file_net(seq);
|
|
|
+
|
|
|
mutex_lock(&nf_log_mutex);
|
|
|
|
|
|
- if (*pos >= ARRAY_SIZE(nf_loggers))
|
|
|
+ if (*pos >= ARRAY_SIZE(net->nf.nf_loggers))
|
|
|
return NULL;
|
|
|
|
|
|
return pos;
|
|
@@ -145,9 +187,11 @@ static void *seq_start(struct seq_file *seq, loff_t *pos)
|
|
|
|
|
|
static void *seq_next(struct seq_file *s, void *v, loff_t *pos)
|
|
|
{
|
|
|
+ struct net *net = seq_file_net(s);
|
|
|
+
|
|
|
(*pos)++;
|
|
|
|
|
|
- if (*pos >= ARRAY_SIZE(nf_loggers))
|
|
|
+ if (*pos >= ARRAY_SIZE(net->nf.nf_loggers))
|
|
|
return NULL;
|
|
|
|
|
|
return pos;
|
|
@@ -164,8 +208,9 @@ static int seq_show(struct seq_file *s, void *v)
|
|
|
const struct nf_logger *logger;
|
|
|
struct nf_logger *t;
|
|
|
int ret;
|
|
|
+ struct net *net = seq_file_net(s);
|
|
|
|
|
|
- logger = rcu_dereference_protected(nf_loggers[*pos],
|
|
|
+ logger = rcu_dereference_protected(net->nf.nf_loggers[*pos],
|
|
|
lockdep_is_held(&nf_log_mutex));
|
|
|
|
|
|
if (!logger)
|
|
@@ -199,7 +244,8 @@ static const struct seq_operations nflog_seq_ops = {
|
|
|
|
|
|
static int nflog_open(struct inode *inode, struct file *file)
|
|
|
{
|
|
|
- return seq_open(file, &nflog_seq_ops);
|
|
|
+ return seq_open_net(inode, file, &nflog_seq_ops,
|
|
|
+ sizeof(struct seq_net_private));
|
|
|
}
|
|
|
|
|
|
static const struct file_operations nflog_file_ops = {
|
|
@@ -207,7 +253,7 @@ static const struct file_operations nflog_file_ops = {
|
|
|
.open = nflog_open,
|
|
|
.read = seq_read,
|
|
|
.llseek = seq_lseek,
|
|
|
- .release = seq_release,
|
|
|
+ .release = seq_release_net,
|
|
|
};
|
|
|
|
|
|
|
|
@@ -216,7 +262,6 @@ static const struct file_operations nflog_file_ops = {
|
|
|
#ifdef CONFIG_SYSCTL
|
|
|
static char nf_log_sysctl_fnames[NFPROTO_NUMPROTO-NFPROTO_UNSPEC][3];
|
|
|
static struct ctl_table nf_log_sysctl_table[NFPROTO_NUMPROTO+1];
|
|
|
-static struct ctl_table_header *nf_log_dir_header;
|
|
|
|
|
|
static int nf_log_proc_dostring(ctl_table *table, int write,
|
|
|
void __user *buffer, size_t *lenp, loff_t *ppos)
|
|
@@ -226,15 +271,19 @@ static int nf_log_proc_dostring(ctl_table *table, int write,
|
|
|
size_t size = *lenp;
|
|
|
int r = 0;
|
|
|
int tindex = (unsigned long)table->extra1;
|
|
|
+ struct net *net = current->nsproxy->net_ns;
|
|
|
|
|
|
if (write) {
|
|
|
+ if (!net_eq(net, &init_net))
|
|
|
+ return -EPERM;
|
|
|
+
|
|
|
if (size > sizeof(buf))
|
|
|
size = sizeof(buf);
|
|
|
if (copy_from_user(buf, buffer, size))
|
|
|
return -EFAULT;
|
|
|
|
|
|
if (!strcmp(buf, "NONE")) {
|
|
|
- nf_log_unbind_pf(tindex);
|
|
|
+ nf_log_unbind_pf(net, tindex);
|
|
|
return 0;
|
|
|
}
|
|
|
mutex_lock(&nf_log_mutex);
|
|
@@ -243,11 +292,11 @@ static int nf_log_proc_dostring(ctl_table *table, int write,
|
|
|
mutex_unlock(&nf_log_mutex);
|
|
|
return -ENOENT;
|
|
|
}
|
|
|
- rcu_assign_pointer(nf_loggers[tindex], logger);
|
|
|
+ rcu_assign_pointer(net->nf.nf_loggers[tindex], logger);
|
|
|
mutex_unlock(&nf_log_mutex);
|
|
|
} else {
|
|
|
mutex_lock(&nf_log_mutex);
|
|
|
- logger = rcu_dereference_protected(nf_loggers[tindex],
|
|
|
+ logger = rcu_dereference_protected(net->nf.nf_loggers[tindex],
|
|
|
lockdep_is_held(&nf_log_mutex));
|
|
|
if (!logger)
|
|
|
table->data = "NONE";
|
|
@@ -260,49 +309,111 @@ static int nf_log_proc_dostring(ctl_table *table, int write,
|
|
|
return r;
|
|
|
}
|
|
|
|
|
|
-static __init int netfilter_log_sysctl_init(void)
|
|
|
+static int netfilter_log_sysctl_init(struct net *net)
|
|
|
{
|
|
|
int i;
|
|
|
-
|
|
|
- for (i = NFPROTO_UNSPEC; i < NFPROTO_NUMPROTO; i++) {
|
|
|
- snprintf(nf_log_sysctl_fnames[i-NFPROTO_UNSPEC], 3, "%d", i);
|
|
|
- nf_log_sysctl_table[i].procname =
|
|
|
- nf_log_sysctl_fnames[i-NFPROTO_UNSPEC];
|
|
|
- nf_log_sysctl_table[i].data = NULL;
|
|
|
- nf_log_sysctl_table[i].maxlen =
|
|
|
- NFLOGGER_NAME_LEN * sizeof(char);
|
|
|
- nf_log_sysctl_table[i].mode = 0644;
|
|
|
- nf_log_sysctl_table[i].proc_handler = nf_log_proc_dostring;
|
|
|
- nf_log_sysctl_table[i].extra1 = (void *)(unsigned long) i;
|
|
|
+ struct ctl_table *table;
|
|
|
+
|
|
|
+ table = nf_log_sysctl_table;
|
|
|
+ if (!net_eq(net, &init_net)) {
|
|
|
+ table = kmemdup(nf_log_sysctl_table,
|
|
|
+ sizeof(nf_log_sysctl_table),
|
|
|
+ GFP_KERNEL);
|
|
|
+ if (!table)
|
|
|
+ goto err_alloc;
|
|
|
+ } else {
|
|
|
+ for (i = NFPROTO_UNSPEC; i < NFPROTO_NUMPROTO; i++) {
|
|
|
+ snprintf(nf_log_sysctl_fnames[i],
|
|
|
+ 3, "%d", i);
|
|
|
+ nf_log_sysctl_table[i].procname =
|
|
|
+ nf_log_sysctl_fnames[i];
|
|
|
+ nf_log_sysctl_table[i].data = NULL;
|
|
|
+ nf_log_sysctl_table[i].maxlen =
|
|
|
+ NFLOGGER_NAME_LEN * sizeof(char);
|
|
|
+ nf_log_sysctl_table[i].mode = 0644;
|
|
|
+ nf_log_sysctl_table[i].proc_handler =
|
|
|
+ nf_log_proc_dostring;
|
|
|
+ nf_log_sysctl_table[i].extra1 =
|
|
|
+ (void *)(unsigned long) i;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- nf_log_dir_header = register_net_sysctl(&init_net, "net/netfilter/nf_log",
|
|
|
- nf_log_sysctl_table);
|
|
|
- if (!nf_log_dir_header)
|
|
|
- return -ENOMEM;
|
|
|
+ net->nf.nf_log_dir_header = register_net_sysctl(net,
|
|
|
+ "net/netfilter/nf_log",
|
|
|
+ table);
|
|
|
+ if (!net->nf.nf_log_dir_header)
|
|
|
+ goto err_reg;
|
|
|
|
|
|
return 0;
|
|
|
+
|
|
|
+err_reg:
|
|
|
+ if (!net_eq(net, &init_net))
|
|
|
+ kfree(table);
|
|
|
+err_alloc:
|
|
|
+ return -ENOMEM;
|
|
|
+}
|
|
|
+
|
|
|
+static void netfilter_log_sysctl_exit(struct net *net)
|
|
|
+{
|
|
|
+ struct ctl_table *table;
|
|
|
+
|
|
|
+ table = net->nf.nf_log_dir_header->ctl_table_arg;
|
|
|
+ unregister_net_sysctl_table(net->nf.nf_log_dir_header);
|
|
|
+ if (!net_eq(net, &init_net))
|
|
|
+ kfree(table);
|
|
|
}
|
|
|
#else
|
|
|
-static __init int netfilter_log_sysctl_init(void)
|
|
|
+static int netfilter_log_sysctl_init(struct net *net)
|
|
|
{
|
|
|
return 0;
|
|
|
}
|
|
|
+
|
|
|
+static void netfilter_log_sysctl_exit(struct net *net)
|
|
|
+{
|
|
|
+}
|
|
|
#endif /* CONFIG_SYSCTL */
|
|
|
|
|
|
-int __init netfilter_log_init(void)
|
|
|
+static int __net_init nf_log_net_init(struct net *net)
|
|
|
{
|
|
|
- int i, r;
|
|
|
+ int ret = -ENOMEM;
|
|
|
+
|
|
|
#ifdef CONFIG_PROC_FS
|
|
|
if (!proc_create("nf_log", S_IRUGO,
|
|
|
- proc_net_netfilter, &nflog_file_ops))
|
|
|
- return -1;
|
|
|
+ net->nf.proc_netfilter, &nflog_file_ops))
|
|
|
+ return ret;
|
|
|
#endif
|
|
|
+ ret = netfilter_log_sysctl_init(net);
|
|
|
+ if (ret < 0)
|
|
|
+ goto out_sysctl;
|
|
|
|
|
|
- /* Errors will trigger panic, unroll on error is unnecessary. */
|
|
|
- r = netfilter_log_sysctl_init();
|
|
|
- if (r < 0)
|
|
|
- return r;
|
|
|
+ return 0;
|
|
|
+
|
|
|
+out_sysctl:
|
|
|
+ /* For init_net: errors will trigger panic, don't unroll on error. */
|
|
|
+ if (!net_eq(net, &init_net))
|
|
|
+ remove_proc_entry("nf_log", net->nf.proc_netfilter);
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static void __net_exit nf_log_net_exit(struct net *net)
|
|
|
+{
|
|
|
+ netfilter_log_sysctl_exit(net);
|
|
|
+ remove_proc_entry("nf_log", net->nf.proc_netfilter);
|
|
|
+}
|
|
|
+
|
|
|
+static struct pernet_operations nf_log_net_ops = {
|
|
|
+ .init = nf_log_net_init,
|
|
|
+ .exit = nf_log_net_exit,
|
|
|
+};
|
|
|
+
|
|
|
+int __init netfilter_log_init(void)
|
|
|
+{
|
|
|
+ int i, ret;
|
|
|
+
|
|
|
+ ret = register_pernet_subsys(&nf_log_net_ops);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
|
|
|
for (i = NFPROTO_UNSPEC; i < NFPROTO_NUMPROTO; i++)
|
|
|
INIT_LIST_HEAD(&(nf_loggers_l[i]));
|