nf_log.c 11.6 KB
Newer Older
1 2 3 4 5 6
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/skbuff.h>
#include <linux/netfilter.h>
7
#include <linux/seq_file.h>
8
#include <net/protocol.h>
9
#include <net/netfilter/nf_log.h>
10 11 12

#include "nf_internals.h"

13
/* Internal logging interface, which relies on the real
14 15 16
   LOG target modules */

#define NF_LOG_PREFIXLEN		128
17
#define NFLOGGER_NAME_LEN		64
18

19
static struct nf_logger __rcu *loggers[NFPROTO_NUMPROTO][NF_LOG_TYPE_MAX] __read_mostly;
20
static DEFINE_MUTEX(nf_log_mutex);
21

22 23 24
#define nft_log_dereference(logger) \
	rcu_dereference_protected(logger, lockdep_is_held(&nf_log_mutex))

25
static struct nf_logger *__find_logger(int pf, const char *str_logger)
26
{
27 28 29 30 31 32
	struct nf_logger *log;
	int i;

	for (i = 0; i < NF_LOG_TYPE_MAX; i++) {
		if (loggers[pf][i] == NULL)
			continue;
33

34
		log = nft_log_dereference(loggers[pf][i]);
35
		if (!strncasecmp(str_logger, log->name, strlen(log->name)))
36
			return log;
37
	}
38

39
	return NULL;
40
}
41

42 43 44 45 46 47 48 49
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);
50
	log = nft_log_dereference(net->nf.nf_loggers[pf]);
51 52 53 54 55 56 57 58 59 60 61 62 63 64
	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++) {
65
		log = nft_log_dereference(net->nf.nf_loggers[i]);
66 67 68 69 70 71 72 73
		if (log == logger)
			RCU_INIT_POINTER(net->nf.nf_loggers[i], NULL);
	}
	mutex_unlock(&nf_log_mutex);
	synchronize_rcu();
}
EXPORT_SYMBOL(nf_log_unset);

74
/* return EEXIST if the same logger is registered, 0 on success. */
75
int nf_log_register(u_int8_t pf, struct nf_logger *logger)
76
{
E
Eric Dumazet 已提交
77
	int i;
78
	int ret = 0;
79

80
	if (pf >= ARRAY_SIZE(init_net.nf.nf_loggers))
81 82
		return -EINVAL;

83
	mutex_lock(&nf_log_mutex);
84 85

	if (pf == NFPROTO_UNSPEC) {
86 87 88 89 90 91
		for (i = NFPROTO_UNSPEC; i < NFPROTO_NUMPROTO; i++) {
			if (rcu_access_pointer(loggers[i][logger->type])) {
				ret = -EEXIST;
				goto unlock;
			}
		}
92
		for (i = NFPROTO_UNSPEC; i < NFPROTO_NUMPROTO; i++)
93
			rcu_assign_pointer(loggers[i][logger->type], logger);
94
	} else {
95 96 97 98
		if (rcu_access_pointer(loggers[pf][logger->type])) {
			ret = -EEXIST;
			goto unlock;
		}
99
		rcu_assign_pointer(loggers[pf][logger->type], logger);
100 101
	}

102
unlock:
103
	mutex_unlock(&nf_log_mutex);
104
	return ret;
105
}
106
EXPORT_SYMBOL(nf_log_register);
107

108
void nf_log_unregister(struct nf_logger *logger)
109 110 111
{
	int i;

112
	mutex_lock(&nf_log_mutex);
113
	for (i = 0; i < NFPROTO_NUMPROTO; i++)
114
		RCU_INIT_POINTER(loggers[i][logger->type], NULL);
115
	mutex_unlock(&nf_log_mutex);
116
}
117
EXPORT_SYMBOL(nf_log_unregister);
118

119 120
int nf_log_bind_pf(struct net *net, u_int8_t pf,
		   const struct nf_logger *logger)
121
{
122
	if (pf >= ARRAY_SIZE(net->nf.nf_loggers))
123
		return -EINVAL;
124 125 126 127 128
	mutex_lock(&nf_log_mutex);
	if (__find_logger(pf, logger->name) == NULL) {
		mutex_unlock(&nf_log_mutex);
		return -ENOENT;
	}
129
	rcu_assign_pointer(net->nf.nf_loggers[pf], logger);
130 131 132 133 134
	mutex_unlock(&nf_log_mutex);
	return 0;
}
EXPORT_SYMBOL(nf_log_bind_pf);

135
void nf_log_unbind_pf(struct net *net, u_int8_t pf)
136
{
137
	if (pf >= ARRAY_SIZE(net->nf.nf_loggers))
138
		return;
139
	mutex_lock(&nf_log_mutex);
140
	RCU_INIT_POINTER(net->nf.nf_loggers[pf], NULL);
141 142 143 144
	mutex_unlock(&nf_log_mutex);
}
EXPORT_SYMBOL(nf_log_unbind_pf);

145 146 147 148 149 150 151
void nf_logger_request_module(int pf, enum nf_log_type type)
{
	if (loggers[pf][type] == NULL)
		request_module("nf-logger-%u-%u", pf, type);
}
EXPORT_SYMBOL_GPL(nf_logger_request_module);

152 153 154 155 156
int nf_logger_find_get(int pf, enum nf_log_type type)
{
	struct nf_logger *logger;
	int ret = -ENOENT;

157
	if (rcu_access_pointer(loggers[pf][type]) == NULL)
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
		request_module("nf-logger-%u-%u", pf, type);

	rcu_read_lock();
	logger = rcu_dereference(loggers[pf][type]);
	if (logger == NULL)
		goto out;

	if (logger && try_module_get(logger->me))
		ret = 0;
out:
	rcu_read_unlock();
	return ret;
}
EXPORT_SYMBOL_GPL(nf_logger_find_get);

void nf_logger_put(int pf, enum nf_log_type type)
{
	struct nf_logger *logger;

	BUG_ON(loggers[pf][type] == NULL);

	rcu_read_lock();
	logger = rcu_dereference(loggers[pf][type]);
	module_put(logger->me);
	rcu_read_unlock();
}
EXPORT_SYMBOL_GPL(nf_logger_put);

186 187
void nf_log_packet(struct net *net,
		   u_int8_t pf,
188 189 190 191
		   unsigned int hooknum,
		   const struct sk_buff *skb,
		   const struct net_device *in,
		   const struct net_device *out,
192
		   const struct nf_loginfo *loginfo,
193 194 195 196
		   const char *fmt, ...)
{
	va_list args;
	char prefix[NF_LOG_PREFIXLEN];
197
	const struct nf_logger *logger;
198

199
	rcu_read_lock();
200 201 202 203 204
	if (loginfo != NULL)
		logger = rcu_dereference(loggers[pf][loginfo->type]);
	else
		logger = rcu_dereference(net->nf.nf_loggers[pf]);

205 206 207 208
	if (logger) {
		va_start(args, fmt);
		vsnprintf(prefix, sizeof(prefix), fmt, args);
		va_end(args);
209
		logger->logfn(net, pf, hooknum, skb, in, out, loginfo, prefix);
210 211 212 213 214
	}
	rcu_read_unlock();
}
EXPORT_SYMBOL(nf_log_packet);

215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238
void nf_log_trace(struct net *net,
		  u_int8_t pf,
		  unsigned int hooknum,
		  const struct sk_buff *skb,
		  const struct net_device *in,
		  const struct net_device *out,
		  const struct nf_loginfo *loginfo, const char *fmt, ...)
{
	va_list args;
	char prefix[NF_LOG_PREFIXLEN];
	const struct nf_logger *logger;

	rcu_read_lock();
	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(net, pf, hooknum, skb, in, out, loginfo, prefix);
	}
	rcu_read_unlock();
}
EXPORT_SYMBOL(nf_log_trace);

239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295
#define S_SIZE (1024 - (sizeof(unsigned int) + 1))

struct nf_log_buf {
	unsigned int	count;
	char		buf[S_SIZE + 1];
};
static struct nf_log_buf emergency, *emergency_ptr = &emergency;

__printf(2, 3) int nf_log_buf_add(struct nf_log_buf *m, const char *f, ...)
{
	va_list args;
	int len;

	if (likely(m->count < S_SIZE)) {
		va_start(args, f);
		len = vsnprintf(m->buf + m->count, S_SIZE - m->count, f, args);
		va_end(args);
		if (likely(m->count + len < S_SIZE)) {
			m->count += len;
			return 0;
		}
	}
	m->count = S_SIZE;
	printk_once(KERN_ERR KBUILD_MODNAME " please increase S_SIZE\n");
	return -1;
}
EXPORT_SYMBOL_GPL(nf_log_buf_add);

struct nf_log_buf *nf_log_buf_open(void)
{
	struct nf_log_buf *m = kmalloc(sizeof(*m), GFP_ATOMIC);

	if (unlikely(!m)) {
		local_bh_disable();
		do {
			m = xchg(&emergency_ptr, NULL);
		} while (!m);
	}
	m->count = 0;
	return m;
}
EXPORT_SYMBOL_GPL(nf_log_buf_open);

void nf_log_buf_close(struct nf_log_buf *m)
{
	m->buf[m->count] = 0;
	printk("%s\n", m->buf);

	if (likely(m != &emergency))
		kfree(m);
	else {
		emergency_ptr = m;
		local_bh_enable();
	}
}
EXPORT_SYMBOL_GPL(nf_log_buf_close);

296 297 298
#ifdef CONFIG_PROC_FS
static void *seq_start(struct seq_file *seq, loff_t *pos)
{
299 300
	struct net *net = seq_file_net(seq);

301
	mutex_lock(&nf_log_mutex);
302

303
	if (*pos >= ARRAY_SIZE(net->nf.nf_loggers))
304 305 306 307 308 309 310
		return NULL;

	return pos;
}

static void *seq_next(struct seq_file *s, void *v, loff_t *pos)
{
311 312
	struct net *net = seq_file_net(s);

313 314
	(*pos)++;

315
	if (*pos >= ARRAY_SIZE(net->nf.nf_loggers))
316 317 318 319 320 321 322
		return NULL;

	return pos;
}

static void seq_stop(struct seq_file *s, void *v)
{
323
	mutex_unlock(&nf_log_mutex);
324 325 326 327 328 329
}

static int seq_show(struct seq_file *s, void *v)
{
	loff_t *pos = v;
	const struct nf_logger *logger;
330
	int i;
331
	struct net *net = seq_file_net(s);
332

333
	logger = nft_log_dereference(net->nf.nf_loggers[*pos]);
334 335

	if (!logger)
336
		seq_printf(s, "%2lld NONE (", *pos);
337
	else
338
		seq_printf(s, "%2lld %s (", *pos, logger->name);
339

340 341
	if (seq_has_overflowed(s))
		return -ENOSPC;
342

343 344 345 346
	for (i = 0; i < NF_LOG_TYPE_MAX; i++) {
		if (loggers[*pos][i] == NULL)
			continue;

347
		logger = nft_log_dereference(loggers[*pos][i]);
348 349 350 351 352 353
		seq_printf(s, "%s", logger->name);
		if (i == 0 && loggers[*pos][i + 1] != NULL)
			seq_printf(s, ",");

		if (seq_has_overflowed(s))
			return -ENOSPC;
354
	}
355

356 357 358 359 360
	seq_printf(s, ")\n");

	if (seq_has_overflowed(s))
		return -ENOSPC;
	return 0;
361 362
}

363
static const struct seq_operations nflog_seq_ops = {
364 365 366 367 368 369 370 371
	.start	= seq_start,
	.next	= seq_next,
	.stop	= seq_stop,
	.show	= seq_show,
};

static int nflog_open(struct inode *inode, struct file *file)
{
372 373
	return seq_open_net(inode, file, &nflog_seq_ops,
			    sizeof(struct seq_net_private));
374 375
}

376
static const struct file_operations nflog_file_ops = {
377 378 379 380
	.owner	 = THIS_MODULE,
	.open	 = nflog_open,
	.read	 = seq_read,
	.llseek	 = seq_lseek,
381
	.release = seq_release_net,
382 383
};

384

385 386
#endif /* PROC_FS */

387 388 389
#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];
390

391
static int nf_log_proc_dostring(struct ctl_table *table, int write,
392
			 void __user *buffer, size_t *lenp, loff_t *ppos)
393 394
{
	const struct nf_logger *logger;
395 396
	char buf[NFLOGGER_NAME_LEN];
	size_t size = *lenp;
397 398
	int r = 0;
	int tindex = (unsigned long)table->extra1;
399
	struct net *net = current->nsproxy->net_ns;
400 401

	if (write) {
402 403 404 405 406 407
		if (size > sizeof(buf))
			size = sizeof(buf);
		if (copy_from_user(buf, buffer, size))
			return -EFAULT;

		if (!strcmp(buf, "NONE")) {
408
			nf_log_unbind_pf(net, tindex);
409 410 411
			return 0;
		}
		mutex_lock(&nf_log_mutex);
412
		logger = __find_logger(tindex, buf);
413 414 415 416
		if (logger == NULL) {
			mutex_unlock(&nf_log_mutex);
			return -ENOENT;
		}
417
		rcu_assign_pointer(net->nf.nf_loggers[tindex], logger);
418 419
		mutex_unlock(&nf_log_mutex);
	} else {
420
		mutex_lock(&nf_log_mutex);
421
		logger = nft_log_dereference(net->nf.nf_loggers[tindex]);
422 423 424 425
		if (!logger)
			table->data = "NONE";
		else
			table->data = logger->name;
426
		r = proc_dostring(table, write, buffer, lenp, ppos);
427
		mutex_unlock(&nf_log_mutex);
428 429 430 431 432
	}

	return r;
}

433
static int netfilter_log_sysctl_init(struct net *net)
434
{
435
	int i;
436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451
	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;
452
			nf_log_sysctl_table[i].maxlen = NFLOGGER_NAME_LEN;
453 454 455 456 457 458
			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;
		}
459 460
	}

461 462 463 464 465
	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;
466 467

	return 0;
468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483

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);
484 485
}
#else
486
static int netfilter_log_sysctl_init(struct net *net)
487 488 489
{
	return 0;
}
490 491 492 493

static void netfilter_log_sysctl_exit(struct net *net)
{
}
494 495
#endif /* CONFIG_SYSCTL */

496
static int __net_init nf_log_net_init(struct net *net)
497
{
498 499
	int ret = -ENOMEM;

500
#ifdef CONFIG_PROC_FS
501
	if (!proc_create("nf_log", S_IRUGO,
502 503
			 net->nf.proc_netfilter, &nflog_file_ops))
		return ret;
504
#endif
505 506 507
	ret = netfilter_log_sysctl_init(net);
	if (ret < 0)
		goto out_sysctl;
508

509 510 511
	return 0;

out_sysctl:
512
#ifdef CONFIG_PROC_FS
513
	remove_proc_entry("nf_log", net->nf.proc_netfilter);
514
#endif
515 516 517 518 519 520
	return ret;
}

static void __net_exit nf_log_net_exit(struct net *net)
{
	netfilter_log_sysctl_exit(net);
521
#ifdef CONFIG_PROC_FS
522
	remove_proc_entry("nf_log", net->nf.proc_netfilter);
523
#endif
524 525 526 527 528 529 530 531 532
}

static struct pernet_operations nf_log_net_ops = {
	.init = nf_log_net_init,
	.exit = nf_log_net_exit,
};

int __init netfilter_log_init(void)
{
533
	return register_pernet_subsys(&nf_log_net_ops);
534
}