diff options
Diffstat (limited to 'net/netfilter/nf_log.c')
| -rw-r--r-- | net/netfilter/nf_log.c | 222 | 
1 files changed, 156 insertions, 66 deletions
diff --git a/net/netfilter/nf_log.c b/net/netfilter/nf_log.c index b07393eab88..85296d4eac0 100644 --- a/net/netfilter/nf_log.c +++ b/net/netfilter/nf_log.c @@ -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,46 @@ static struct nf_logger *__find_logger(int pf, const char *str_logger)  	return NULL;  } -/* return EEXIST if the same logger is registred, 0 on success. */ +void nf_log_set(struct net *net, u_int8_t pf, const struct nf_logger *logger) +{ +	const struct nf_logger *log; + +	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; + +	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,10 +84,6 @@ 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); @@ -66,45 +94,43 @@ 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_assign_pointer(nf_loggers[i], NULL); +	for (i = 0; i < NFPROTO_NUMPROTO; i++)  		list_del(&logger->list[i]); -	}  	mutex_unlock(&nf_log_mutex); - -	synchronize_rcu();  }  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(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(net->nf.nf_loggers)) +		return;  	mutex_lock(&nf_log_mutex); -	rcu_assign_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, @@ -117,12 +143,12 @@ void nf_log_packet(u_int8_t pf,  	const struct nf_logger *logger;  	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);  		va_end(args); -		logger->logfn(pf, hooknum, skb, in, out, loginfo, prefix); +		logger->logfn(net, pf, hooknum, skb, in, out, loginfo, prefix);  	}  	rcu_read_unlock();  } @@ -131,9 +157,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; @@ -141,9 +169,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; @@ -160,8 +190,10 @@ 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 = nf_loggers[*pos]; +	logger = rcu_dereference_protected(net->nf.nf_loggers[*pos], +					   lockdep_is_held(&nf_log_mutex));  	if (!logger)  		ret = seq_printf(s, "%2lld NONE (", *pos); @@ -194,7 +226,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 = { @@ -202,25 +235,17 @@ static const struct file_operations nflog_file_ops = {  	.open	 = nflog_open,  	.read	 = seq_read,  	.llseek	 = seq_lseek, -	.release = seq_release, +	.release = seq_release_net,  };  #endif /* PROC_FS */  #ifdef CONFIG_SYSCTL -static struct ctl_path nf_log_sysctl_path[] = { -	{ .procname = "net", }, -	{ .procname = "netfilter", }, -	{ .procname = "nf_log", }, -	{ } -}; -  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, +static int nf_log_proc_dostring(struct ctl_table *table, int write,  			 void __user *buffer, size_t *lenp, loff_t *ppos)  {  	const struct nf_logger *logger; @@ -228,6 +253,7 @@ 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 (size > sizeof(buf)) @@ -236,7 +262,7 @@ static int nf_log_proc_dostring(ctl_table *table, int write,  			return -EFAULT;  		if (!strcmp(buf, "NONE")) { -			nf_log_unbind_pf(tindex); +			nf_log_unbind_pf(net, tindex);  			return 0;  		}  		mutex_lock(&nf_log_mutex); @@ -245,11 +271,12 @@ 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 = nf_loggers[tindex]; +		logger = rcu_dereference_protected(net->nf.nf_loggers[tindex], +						   lockdep_is_held(&nf_log_mutex));  		if (!logger)  			table->data = "NONE";  		else @@ -261,49 +288,112 @@ 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_sysctl_paths(nf_log_sysctl_path, -				       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: +#ifdef CONFIG_PROC_FS +	remove_proc_entry("nf_log", net->nf.proc_netfilter); +#endif +	return ret; +} + +static void __net_exit nf_log_net_exit(struct net *net) +{ +	netfilter_log_sysctl_exit(net); +#ifdef CONFIG_PROC_FS +	remove_proc_entry("nf_log", net->nf.proc_netfilter); +#endif +} + +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]));  | 
