nf_log.c (12656B)
1// SPDX-License-Identifier: GPL-2.0-only 2#include <linux/kernel.h> 3#include <linux/init.h> 4#include <linux/module.h> 5#include <linux/proc_fs.h> 6#include <linux/skbuff.h> 7#include <linux/netfilter.h> 8#include <linux/seq_file.h> 9#include <net/protocol.h> 10#include <net/netfilter/nf_log.h> 11 12#include "nf_internals.h" 13 14/* Internal logging interface, which relies on the real 15 LOG target modules */ 16 17#define NFLOGGER_NAME_LEN 64 18 19int sysctl_nf_log_all_netns __read_mostly; 20EXPORT_SYMBOL(sysctl_nf_log_all_netns); 21 22static struct nf_logger __rcu *loggers[NFPROTO_NUMPROTO][NF_LOG_TYPE_MAX] __read_mostly; 23static DEFINE_MUTEX(nf_log_mutex); 24 25#define nft_log_dereference(logger) \ 26 rcu_dereference_protected(logger, lockdep_is_held(&nf_log_mutex)) 27 28static struct nf_logger *__find_logger(int pf, const char *str_logger) 29{ 30 struct nf_logger *log; 31 int i; 32 33 for (i = 0; i < NF_LOG_TYPE_MAX; i++) { 34 if (loggers[pf][i] == NULL) 35 continue; 36 37 log = nft_log_dereference(loggers[pf][i]); 38 if (!strncasecmp(str_logger, log->name, strlen(log->name))) 39 return log; 40 } 41 42 return NULL; 43} 44 45int nf_log_set(struct net *net, u_int8_t pf, const struct nf_logger *logger) 46{ 47 const struct nf_logger *log; 48 49 if (pf == NFPROTO_UNSPEC || pf >= ARRAY_SIZE(net->nf.nf_loggers)) 50 return -EOPNOTSUPP; 51 52 mutex_lock(&nf_log_mutex); 53 log = nft_log_dereference(net->nf.nf_loggers[pf]); 54 if (log == NULL) 55 rcu_assign_pointer(net->nf.nf_loggers[pf], logger); 56 57 mutex_unlock(&nf_log_mutex); 58 59 return 0; 60} 61EXPORT_SYMBOL(nf_log_set); 62 63void nf_log_unset(struct net *net, const struct nf_logger *logger) 64{ 65 int i; 66 const struct nf_logger *log; 67 68 mutex_lock(&nf_log_mutex); 69 for (i = 0; i < NFPROTO_NUMPROTO; i++) { 70 log = nft_log_dereference(net->nf.nf_loggers[i]); 71 if (log == logger) 72 RCU_INIT_POINTER(net->nf.nf_loggers[i], NULL); 73 } 74 mutex_unlock(&nf_log_mutex); 75} 76EXPORT_SYMBOL(nf_log_unset); 77 78/* return EEXIST if the same logger is registered, 0 on success. */ 79int nf_log_register(u_int8_t pf, struct nf_logger *logger) 80{ 81 int i; 82 int ret = 0; 83 84 if (pf >= ARRAY_SIZE(init_net.nf.nf_loggers)) 85 return -EINVAL; 86 87 mutex_lock(&nf_log_mutex); 88 89 if (pf == NFPROTO_UNSPEC) { 90 for (i = NFPROTO_UNSPEC; i < NFPROTO_NUMPROTO; i++) { 91 if (rcu_access_pointer(loggers[i][logger->type])) { 92 ret = -EEXIST; 93 goto unlock; 94 } 95 } 96 for (i = NFPROTO_UNSPEC; i < NFPROTO_NUMPROTO; i++) 97 rcu_assign_pointer(loggers[i][logger->type], logger); 98 } else { 99 if (rcu_access_pointer(loggers[pf][logger->type])) { 100 ret = -EEXIST; 101 goto unlock; 102 } 103 rcu_assign_pointer(loggers[pf][logger->type], logger); 104 } 105 106unlock: 107 mutex_unlock(&nf_log_mutex); 108 return ret; 109} 110EXPORT_SYMBOL(nf_log_register); 111 112void nf_log_unregister(struct nf_logger *logger) 113{ 114 const struct nf_logger *log; 115 int i; 116 117 mutex_lock(&nf_log_mutex); 118 for (i = 0; i < NFPROTO_NUMPROTO; i++) { 119 log = nft_log_dereference(loggers[i][logger->type]); 120 if (log == logger) 121 RCU_INIT_POINTER(loggers[i][logger->type], NULL); 122 } 123 mutex_unlock(&nf_log_mutex); 124 synchronize_rcu(); 125} 126EXPORT_SYMBOL(nf_log_unregister); 127 128int nf_log_bind_pf(struct net *net, u_int8_t pf, 129 const struct nf_logger *logger) 130{ 131 if (pf >= ARRAY_SIZE(net->nf.nf_loggers)) 132 return -EINVAL; 133 mutex_lock(&nf_log_mutex); 134 if (__find_logger(pf, logger->name) == NULL) { 135 mutex_unlock(&nf_log_mutex); 136 return -ENOENT; 137 } 138 rcu_assign_pointer(net->nf.nf_loggers[pf], logger); 139 mutex_unlock(&nf_log_mutex); 140 return 0; 141} 142EXPORT_SYMBOL(nf_log_bind_pf); 143 144void nf_log_unbind_pf(struct net *net, u_int8_t pf) 145{ 146 if (pf >= ARRAY_SIZE(net->nf.nf_loggers)) 147 return; 148 mutex_lock(&nf_log_mutex); 149 RCU_INIT_POINTER(net->nf.nf_loggers[pf], NULL); 150 mutex_unlock(&nf_log_mutex); 151} 152EXPORT_SYMBOL(nf_log_unbind_pf); 153 154int nf_logger_find_get(int pf, enum nf_log_type type) 155{ 156 struct nf_logger *logger; 157 int ret = -ENOENT; 158 159 if (pf == NFPROTO_INET) { 160 ret = nf_logger_find_get(NFPROTO_IPV4, type); 161 if (ret < 0) 162 return ret; 163 164 ret = nf_logger_find_get(NFPROTO_IPV6, type); 165 if (ret < 0) { 166 nf_logger_put(NFPROTO_IPV4, type); 167 return ret; 168 } 169 170 return 0; 171 } 172 173 rcu_read_lock(); 174 logger = rcu_dereference(loggers[pf][type]); 175 if (logger == NULL) 176 goto out; 177 178 if (try_module_get(logger->me)) 179 ret = 0; 180out: 181 rcu_read_unlock(); 182 return ret; 183} 184EXPORT_SYMBOL_GPL(nf_logger_find_get); 185 186void nf_logger_put(int pf, enum nf_log_type type) 187{ 188 struct nf_logger *logger; 189 190 if (pf == NFPROTO_INET) { 191 nf_logger_put(NFPROTO_IPV4, type); 192 nf_logger_put(NFPROTO_IPV6, type); 193 return; 194 } 195 196 BUG_ON(loggers[pf][type] == NULL); 197 198 rcu_read_lock(); 199 logger = rcu_dereference(loggers[pf][type]); 200 module_put(logger->me); 201 rcu_read_unlock(); 202} 203EXPORT_SYMBOL_GPL(nf_logger_put); 204 205void nf_log_packet(struct net *net, 206 u_int8_t pf, 207 unsigned int hooknum, 208 const struct sk_buff *skb, 209 const struct net_device *in, 210 const struct net_device *out, 211 const struct nf_loginfo *loginfo, 212 const char *fmt, ...) 213{ 214 va_list args; 215 char prefix[NF_LOG_PREFIXLEN]; 216 const struct nf_logger *logger; 217 218 rcu_read_lock(); 219 if (loginfo != NULL) 220 logger = rcu_dereference(loggers[pf][loginfo->type]); 221 else 222 logger = rcu_dereference(net->nf.nf_loggers[pf]); 223 224 if (logger) { 225 va_start(args, fmt); 226 vsnprintf(prefix, sizeof(prefix), fmt, args); 227 va_end(args); 228 logger->logfn(net, pf, hooknum, skb, in, out, loginfo, prefix); 229 } 230 rcu_read_unlock(); 231} 232EXPORT_SYMBOL(nf_log_packet); 233 234void nf_log_trace(struct net *net, 235 u_int8_t pf, 236 unsigned int hooknum, 237 const struct sk_buff *skb, 238 const struct net_device *in, 239 const struct net_device *out, 240 const struct nf_loginfo *loginfo, const char *fmt, ...) 241{ 242 va_list args; 243 char prefix[NF_LOG_PREFIXLEN]; 244 const struct nf_logger *logger; 245 246 rcu_read_lock(); 247 logger = rcu_dereference(net->nf.nf_loggers[pf]); 248 if (logger) { 249 va_start(args, fmt); 250 vsnprintf(prefix, sizeof(prefix), fmt, args); 251 va_end(args); 252 logger->logfn(net, pf, hooknum, skb, in, out, loginfo, prefix); 253 } 254 rcu_read_unlock(); 255} 256EXPORT_SYMBOL(nf_log_trace); 257 258#define S_SIZE (1024 - (sizeof(unsigned int) + 1)) 259 260struct nf_log_buf { 261 unsigned int count; 262 char buf[S_SIZE + 1]; 263}; 264static struct nf_log_buf emergency, *emergency_ptr = &emergency; 265 266__printf(2, 3) int nf_log_buf_add(struct nf_log_buf *m, const char *f, ...) 267{ 268 va_list args; 269 int len; 270 271 if (likely(m->count < S_SIZE)) { 272 va_start(args, f); 273 len = vsnprintf(m->buf + m->count, S_SIZE - m->count, f, args); 274 va_end(args); 275 if (likely(m->count + len < S_SIZE)) { 276 m->count += len; 277 return 0; 278 } 279 } 280 m->count = S_SIZE; 281 printk_once(KERN_ERR KBUILD_MODNAME " please increase S_SIZE\n"); 282 return -1; 283} 284EXPORT_SYMBOL_GPL(nf_log_buf_add); 285 286struct nf_log_buf *nf_log_buf_open(void) 287{ 288 struct nf_log_buf *m = kmalloc(sizeof(*m), GFP_ATOMIC); 289 290 if (unlikely(!m)) { 291 local_bh_disable(); 292 do { 293 m = xchg(&emergency_ptr, NULL); 294 } while (!m); 295 } 296 m->count = 0; 297 return m; 298} 299EXPORT_SYMBOL_GPL(nf_log_buf_open); 300 301void nf_log_buf_close(struct nf_log_buf *m) 302{ 303 m->buf[m->count] = 0; 304 printk("%s\n", m->buf); 305 306 if (likely(m != &emergency)) 307 kfree(m); 308 else { 309 emergency_ptr = m; 310 local_bh_enable(); 311 } 312} 313EXPORT_SYMBOL_GPL(nf_log_buf_close); 314 315#ifdef CONFIG_PROC_FS 316static void *seq_start(struct seq_file *seq, loff_t *pos) 317{ 318 struct net *net = seq_file_net(seq); 319 320 mutex_lock(&nf_log_mutex); 321 322 if (*pos >= ARRAY_SIZE(net->nf.nf_loggers)) 323 return NULL; 324 325 return pos; 326} 327 328static void *seq_next(struct seq_file *s, void *v, loff_t *pos) 329{ 330 struct net *net = seq_file_net(s); 331 332 (*pos)++; 333 334 if (*pos >= ARRAY_SIZE(net->nf.nf_loggers)) 335 return NULL; 336 337 return pos; 338} 339 340static void seq_stop(struct seq_file *s, void *v) 341{ 342 mutex_unlock(&nf_log_mutex); 343} 344 345static int seq_show(struct seq_file *s, void *v) 346{ 347 loff_t *pos = v; 348 const struct nf_logger *logger; 349 int i; 350 struct net *net = seq_file_net(s); 351 352 logger = nft_log_dereference(net->nf.nf_loggers[*pos]); 353 354 if (!logger) 355 seq_printf(s, "%2lld NONE (", *pos); 356 else 357 seq_printf(s, "%2lld %s (", *pos, logger->name); 358 359 if (seq_has_overflowed(s)) 360 return -ENOSPC; 361 362 for (i = 0; i < NF_LOG_TYPE_MAX; i++) { 363 if (loggers[*pos][i] == NULL) 364 continue; 365 366 logger = nft_log_dereference(loggers[*pos][i]); 367 seq_puts(s, logger->name); 368 if (i == 0 && loggers[*pos][i + 1] != NULL) 369 seq_puts(s, ","); 370 371 if (seq_has_overflowed(s)) 372 return -ENOSPC; 373 } 374 375 seq_puts(s, ")\n"); 376 377 if (seq_has_overflowed(s)) 378 return -ENOSPC; 379 return 0; 380} 381 382static const struct seq_operations nflog_seq_ops = { 383 .start = seq_start, 384 .next = seq_next, 385 .stop = seq_stop, 386 .show = seq_show, 387}; 388#endif /* PROC_FS */ 389 390#ifdef CONFIG_SYSCTL 391static char nf_log_sysctl_fnames[NFPROTO_NUMPROTO-NFPROTO_UNSPEC][3]; 392static struct ctl_table nf_log_sysctl_table[NFPROTO_NUMPROTO+1]; 393static struct ctl_table_header *nf_log_sysctl_fhdr; 394 395static struct ctl_table nf_log_sysctl_ftable[] = { 396 { 397 .procname = "nf_log_all_netns", 398 .data = &sysctl_nf_log_all_netns, 399 .maxlen = sizeof(sysctl_nf_log_all_netns), 400 .mode = 0644, 401 .proc_handler = proc_dointvec, 402 }, 403 { } 404}; 405 406static int nf_log_proc_dostring(struct ctl_table *table, int write, 407 void *buffer, size_t *lenp, loff_t *ppos) 408{ 409 const struct nf_logger *logger; 410 char buf[NFLOGGER_NAME_LEN]; 411 int r = 0; 412 int tindex = (unsigned long)table->extra1; 413 struct net *net = table->extra2; 414 415 if (write) { 416 struct ctl_table tmp = *table; 417 418 /* proc_dostring() can append to existing strings, so we need to 419 * initialize it as an empty string. 420 */ 421 buf[0] = '\0'; 422 tmp.data = buf; 423 r = proc_dostring(&tmp, write, buffer, lenp, ppos); 424 if (r) 425 return r; 426 427 if (!strcmp(buf, "NONE")) { 428 nf_log_unbind_pf(net, tindex); 429 return 0; 430 } 431 mutex_lock(&nf_log_mutex); 432 logger = __find_logger(tindex, buf); 433 if (logger == NULL) { 434 mutex_unlock(&nf_log_mutex); 435 return -ENOENT; 436 } 437 rcu_assign_pointer(net->nf.nf_loggers[tindex], logger); 438 mutex_unlock(&nf_log_mutex); 439 } else { 440 struct ctl_table tmp = *table; 441 442 tmp.data = buf; 443 mutex_lock(&nf_log_mutex); 444 logger = nft_log_dereference(net->nf.nf_loggers[tindex]); 445 if (!logger) 446 strlcpy(buf, "NONE", sizeof(buf)); 447 else 448 strlcpy(buf, logger->name, sizeof(buf)); 449 mutex_unlock(&nf_log_mutex); 450 r = proc_dostring(&tmp, write, buffer, lenp, ppos); 451 } 452 453 return r; 454} 455 456static int netfilter_log_sysctl_init(struct net *net) 457{ 458 int i; 459 struct ctl_table *table; 460 461 table = nf_log_sysctl_table; 462 if (!net_eq(net, &init_net)) { 463 table = kmemdup(nf_log_sysctl_table, 464 sizeof(nf_log_sysctl_table), 465 GFP_KERNEL); 466 if (!table) 467 goto err_alloc; 468 } else { 469 for (i = NFPROTO_UNSPEC; i < NFPROTO_NUMPROTO; i++) { 470 snprintf(nf_log_sysctl_fnames[i], 471 3, "%d", i); 472 nf_log_sysctl_table[i].procname = 473 nf_log_sysctl_fnames[i]; 474 nf_log_sysctl_table[i].maxlen = NFLOGGER_NAME_LEN; 475 nf_log_sysctl_table[i].mode = 0644; 476 nf_log_sysctl_table[i].proc_handler = 477 nf_log_proc_dostring; 478 nf_log_sysctl_table[i].extra1 = 479 (void *)(unsigned long) i; 480 } 481 nf_log_sysctl_fhdr = register_net_sysctl(net, "net/netfilter", 482 nf_log_sysctl_ftable); 483 if (!nf_log_sysctl_fhdr) 484 goto err_freg; 485 } 486 487 for (i = NFPROTO_UNSPEC; i < NFPROTO_NUMPROTO; i++) 488 table[i].extra2 = net; 489 490 net->nf.nf_log_dir_header = register_net_sysctl(net, 491 "net/netfilter/nf_log", 492 table); 493 if (!net->nf.nf_log_dir_header) 494 goto err_reg; 495 496 return 0; 497 498err_reg: 499 if (!net_eq(net, &init_net)) 500 kfree(table); 501 else 502 unregister_net_sysctl_table(nf_log_sysctl_fhdr); 503err_freg: 504err_alloc: 505 return -ENOMEM; 506} 507 508static void netfilter_log_sysctl_exit(struct net *net) 509{ 510 struct ctl_table *table; 511 512 table = net->nf.nf_log_dir_header->ctl_table_arg; 513 unregister_net_sysctl_table(net->nf.nf_log_dir_header); 514 if (!net_eq(net, &init_net)) 515 kfree(table); 516 else 517 unregister_net_sysctl_table(nf_log_sysctl_fhdr); 518} 519#else 520static int netfilter_log_sysctl_init(struct net *net) 521{ 522 return 0; 523} 524 525static void netfilter_log_sysctl_exit(struct net *net) 526{ 527} 528#endif /* CONFIG_SYSCTL */ 529 530static int __net_init nf_log_net_init(struct net *net) 531{ 532 int ret = -ENOMEM; 533 534#ifdef CONFIG_PROC_FS 535 if (!proc_create_net("nf_log", 0444, net->nf.proc_netfilter, 536 &nflog_seq_ops, sizeof(struct seq_net_private))) 537 return ret; 538#endif 539 ret = netfilter_log_sysctl_init(net); 540 if (ret < 0) 541 goto out_sysctl; 542 543 return 0; 544 545out_sysctl: 546#ifdef CONFIG_PROC_FS 547 remove_proc_entry("nf_log", net->nf.proc_netfilter); 548#endif 549 return ret; 550} 551 552static void __net_exit nf_log_net_exit(struct net *net) 553{ 554 netfilter_log_sysctl_exit(net); 555#ifdef CONFIG_PROC_FS 556 remove_proc_entry("nf_log", net->nf.proc_netfilter); 557#endif 558} 559 560static struct pernet_operations nf_log_net_ops = { 561 .init = nf_log_net_init, 562 .exit = nf_log_net_exit, 563}; 564 565int __init netfilter_log_init(void) 566{ 567 return register_pernet_subsys(&nf_log_net_ops); 568}