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 log = nft_log_dereference(loggers[pf][i]); 35 if (!log) 36 continue; 37 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 >= ARRAY_SIZE(loggers)) 160 return -EINVAL; 161 if (type >= NF_LOG_TYPE_MAX) 162 return -EINVAL; 163 164 if (pf == NFPROTO_INET) { 165 ret = nf_logger_find_get(NFPROTO_IPV4, type); 166 if (ret < 0) 167 return ret; 168 169 ret = nf_logger_find_get(NFPROTO_IPV6, type); 170 if (ret < 0) { 171 nf_logger_put(NFPROTO_IPV4, type); 172 return ret; 173 } 174 175 return 0; 176 } 177 178 rcu_read_lock(); 179 logger = rcu_dereference(loggers[pf][type]); 180 if (logger == NULL) 181 goto out; 182 183 if (try_module_get(logger->me)) 184 ret = 0; 185out: 186 rcu_read_unlock(); 187 return ret; 188} 189EXPORT_SYMBOL_GPL(nf_logger_find_get); 190 191void nf_logger_put(int pf, enum nf_log_type type) 192{ 193 struct nf_logger *logger; 194 195 if (pf == NFPROTO_INET) { 196 nf_logger_put(NFPROTO_IPV4, type); 197 nf_logger_put(NFPROTO_IPV6, type); 198 return; 199 } 200 201 rcu_read_lock(); 202 logger = rcu_dereference(loggers[pf][type]); 203 if (!logger) 204 WARN_ON_ONCE(1); 205 else 206 module_put(logger->me); 207 rcu_read_unlock(); 208} 209EXPORT_SYMBOL_GPL(nf_logger_put); 210 211void nf_log_packet(struct net *net, 212 u_int8_t pf, 213 unsigned int hooknum, 214 const struct sk_buff *skb, 215 const struct net_device *in, 216 const struct net_device *out, 217 const struct nf_loginfo *loginfo, 218 const char *fmt, ...) 219{ 220 va_list args; 221 char prefix[NF_LOG_PREFIXLEN]; 222 const struct nf_logger *logger; 223 224 rcu_read_lock(); 225 if (loginfo != NULL) 226 logger = rcu_dereference(loggers[pf][loginfo->type]); 227 else 228 logger = rcu_dereference(net->nf.nf_loggers[pf]); 229 230 if (logger) { 231 va_start(args, fmt); 232 vsnprintf(prefix, sizeof(prefix), fmt, args); 233 va_end(args); 234 logger->logfn(net, pf, hooknum, skb, in, out, loginfo, prefix); 235 } 236 rcu_read_unlock(); 237} 238EXPORT_SYMBOL(nf_log_packet); 239 240void nf_log_trace(struct net *net, 241 u_int8_t pf, 242 unsigned int hooknum, 243 const struct sk_buff *skb, 244 const struct net_device *in, 245 const struct net_device *out, 246 const struct nf_loginfo *loginfo, const char *fmt, ...) 247{ 248 va_list args; 249 char prefix[NF_LOG_PREFIXLEN]; 250 const struct nf_logger *logger; 251 252 rcu_read_lock(); 253 logger = rcu_dereference(net->nf.nf_loggers[pf]); 254 if (logger) { 255 va_start(args, fmt); 256 vsnprintf(prefix, sizeof(prefix), fmt, args); 257 va_end(args); 258 logger->logfn(net, pf, hooknum, skb, in, out, loginfo, prefix); 259 } 260 rcu_read_unlock(); 261} 262EXPORT_SYMBOL(nf_log_trace); 263 264#define S_SIZE (1024 - (sizeof(unsigned int) + 1)) 265 266struct nf_log_buf { 267 unsigned int count; 268 char buf[S_SIZE + 1]; 269}; 270static struct nf_log_buf emergency, *emergency_ptr = &emergency; 271 272__printf(2, 3) int nf_log_buf_add(struct nf_log_buf *m, const char *f, ...) 273{ 274 va_list args; 275 int len; 276 277 if (likely(m->count < S_SIZE)) { 278 va_start(args, f); 279 len = vsnprintf(m->buf + m->count, S_SIZE - m->count, f, args); 280 va_end(args); 281 if (likely(m->count + len < S_SIZE)) { 282 m->count += len; 283 return 0; 284 } 285 } 286 m->count = S_SIZE; 287 printk_once(KERN_ERR KBUILD_MODNAME " please increase S_SIZE\n"); 288 return -1; 289} 290EXPORT_SYMBOL_GPL(nf_log_buf_add); 291 292struct nf_log_buf *nf_log_buf_open(void) 293{ 294 struct nf_log_buf *m = kmalloc(sizeof(*m), GFP_ATOMIC); 295 296 if (unlikely(!m)) { 297 local_bh_disable(); 298 do { 299 m = xchg(&emergency_ptr, NULL); 300 } while (!m); 301 } 302 m->count = 0; 303 return m; 304} 305EXPORT_SYMBOL_GPL(nf_log_buf_open); 306 307void nf_log_buf_close(struct nf_log_buf *m) 308{ 309 m->buf[m->count] = 0; 310 printk("%s\n", m->buf); 311 312 if (likely(m != &emergency)) 313 kfree(m); 314 else { 315 emergency_ptr = m; 316 local_bh_enable(); 317 } 318} 319EXPORT_SYMBOL_GPL(nf_log_buf_close); 320 321#ifdef CONFIG_PROC_FS 322static void *seq_start(struct seq_file *seq, loff_t *pos) 323{ 324 struct net *net = seq_file_net(seq); 325 326 mutex_lock(&nf_log_mutex); 327 328 if (*pos >= ARRAY_SIZE(net->nf.nf_loggers)) 329 return NULL; 330 331 return pos; 332} 333 334static void *seq_next(struct seq_file *s, void *v, loff_t *pos) 335{ 336 struct net *net = seq_file_net(s); 337 338 (*pos)++; 339 340 if (*pos >= ARRAY_SIZE(net->nf.nf_loggers)) 341 return NULL; 342 343 return pos; 344} 345 346static void seq_stop(struct seq_file *s, void *v) 347{ 348 mutex_unlock(&nf_log_mutex); 349} 350 351static int seq_show(struct seq_file *s, void *v) 352{ 353 loff_t *pos = v; 354 const struct nf_logger *logger; 355 int i; 356 struct net *net = seq_file_net(s); 357 358 logger = nft_log_dereference(net->nf.nf_loggers[*pos]); 359 360 if (!logger) 361 seq_printf(s, "%2lld NONE (", *pos); 362 else 363 seq_printf(s, "%2lld %s (", *pos, logger->name); 364 365 if (seq_has_overflowed(s)) 366 return -ENOSPC; 367 368 for (i = 0; i < NF_LOG_TYPE_MAX; i++) { 369 if (loggers[*pos][i] == NULL) 370 continue; 371 372 logger = nft_log_dereference(loggers[*pos][i]); 373 seq_puts(s, logger->name); 374 if (i == 0 && loggers[*pos][i + 1] != NULL) 375 seq_puts(s, ","); 376 377 if (seq_has_overflowed(s)) 378 return -ENOSPC; 379 } 380 381 seq_puts(s, ")\n"); 382 383 if (seq_has_overflowed(s)) 384 return -ENOSPC; 385 return 0; 386} 387 388static const struct seq_operations nflog_seq_ops = { 389 .start = seq_start, 390 .next = seq_next, 391 .stop = seq_stop, 392 .show = seq_show, 393}; 394#endif /* PROC_FS */ 395 396#ifdef CONFIG_SYSCTL 397static char nf_log_sysctl_fnames[NFPROTO_NUMPROTO-NFPROTO_UNSPEC][3]; 398static struct ctl_table nf_log_sysctl_table[NFPROTO_NUMPROTO+1]; 399static struct ctl_table_header *nf_log_sysctl_fhdr; 400 401static struct ctl_table nf_log_sysctl_ftable[] = { 402 { 403 .procname = "nf_log_all_netns", 404 .data = &sysctl_nf_log_all_netns, 405 .maxlen = sizeof(sysctl_nf_log_all_netns), 406 .mode = 0644, 407 .proc_handler = proc_dointvec, 408 }, 409 { } 410}; 411 412static int nf_log_proc_dostring(struct ctl_table *table, int write, 413 void *buffer, size_t *lenp, loff_t *ppos) 414{ 415 const struct nf_logger *logger; 416 char buf[NFLOGGER_NAME_LEN]; 417 int r = 0; 418 int tindex = (unsigned long)table->extra1; 419 struct net *net = table->extra2; 420 421 if (write) { 422 struct ctl_table tmp = *table; 423 424 /* proc_dostring() can append to existing strings, so we need to 425 * initialize it as an empty string. 426 */ 427 buf[0] = '\0'; 428 tmp.data = buf; 429 r = proc_dostring(&tmp, write, buffer, lenp, ppos); 430 if (r) 431 return r; 432 433 if (!strcmp(buf, "NONE")) { 434 nf_log_unbind_pf(net, tindex); 435 return 0; 436 } 437 mutex_lock(&nf_log_mutex); 438 logger = __find_logger(tindex, buf); 439 if (logger == NULL) { 440 mutex_unlock(&nf_log_mutex); 441 return -ENOENT; 442 } 443 rcu_assign_pointer(net->nf.nf_loggers[tindex], logger); 444 mutex_unlock(&nf_log_mutex); 445 } else { 446 struct ctl_table tmp = *table; 447 448 tmp.data = buf; 449 mutex_lock(&nf_log_mutex); 450 logger = nft_log_dereference(net->nf.nf_loggers[tindex]); 451 if (!logger) 452 strscpy(buf, "NONE", sizeof(buf)); 453 else 454 strscpy(buf, logger->name, sizeof(buf)); 455 mutex_unlock(&nf_log_mutex); 456 r = proc_dostring(&tmp, write, buffer, lenp, ppos); 457 } 458 459 return r; 460} 461 462static int netfilter_log_sysctl_init(struct net *net) 463{ 464 int i; 465 struct ctl_table *table; 466 467 table = nf_log_sysctl_table; 468 if (!net_eq(net, &init_net)) { 469 table = kmemdup(nf_log_sysctl_table, 470 sizeof(nf_log_sysctl_table), 471 GFP_KERNEL); 472 if (!table) 473 goto err_alloc; 474 } else { 475 for (i = NFPROTO_UNSPEC; i < NFPROTO_NUMPROTO; i++) { 476 snprintf(nf_log_sysctl_fnames[i], 477 3, "%d", i); 478 nf_log_sysctl_table[i].procname = 479 nf_log_sysctl_fnames[i]; 480 nf_log_sysctl_table[i].maxlen = NFLOGGER_NAME_LEN; 481 nf_log_sysctl_table[i].mode = 0644; 482 nf_log_sysctl_table[i].proc_handler = 483 nf_log_proc_dostring; 484 nf_log_sysctl_table[i].extra1 = 485 (void *)(unsigned long) i; 486 } 487 nf_log_sysctl_fhdr = register_net_sysctl(net, "net/netfilter", 488 nf_log_sysctl_ftable); 489 if (!nf_log_sysctl_fhdr) 490 goto err_freg; 491 } 492 493 for (i = NFPROTO_UNSPEC; i < NFPROTO_NUMPROTO; i++) 494 table[i].extra2 = net; 495 496 net->nf.nf_log_dir_header = register_net_sysctl_sz(net, 497 "net/netfilter/nf_log", 498 table, 499 ARRAY_SIZE(nf_log_sysctl_table)); 500 if (!net->nf.nf_log_dir_header) 501 goto err_reg; 502 503 return 0; 504 505err_reg: 506 if (!net_eq(net, &init_net)) 507 kfree(table); 508 else 509 unregister_net_sysctl_table(nf_log_sysctl_fhdr); 510err_freg: 511err_alloc: 512 return -ENOMEM; 513} 514 515static void netfilter_log_sysctl_exit(struct net *net) 516{ 517 struct ctl_table *table; 518 519 table = net->nf.nf_log_dir_header->ctl_table_arg; 520 unregister_net_sysctl_table(net->nf.nf_log_dir_header); 521 if (!net_eq(net, &init_net)) 522 kfree(table); 523 else 524 unregister_net_sysctl_table(nf_log_sysctl_fhdr); 525} 526#else 527static int netfilter_log_sysctl_init(struct net *net) 528{ 529 return 0; 530} 531 532static void netfilter_log_sysctl_exit(struct net *net) 533{ 534} 535#endif /* CONFIG_SYSCTL */ 536 537static int __net_init nf_log_net_init(struct net *net) 538{ 539 int ret = -ENOMEM; 540 541#ifdef CONFIG_PROC_FS 542 if (!proc_create_net("nf_log", 0444, net->nf.proc_netfilter, 543 &nflog_seq_ops, sizeof(struct seq_net_private))) 544 return ret; 545#endif 546 ret = netfilter_log_sysctl_init(net); 547 if (ret < 0) 548 goto out_sysctl; 549 550 return 0; 551 552out_sysctl: 553#ifdef CONFIG_PROC_FS 554 remove_proc_entry("nf_log", net->nf.proc_netfilter); 555#endif 556 return ret; 557} 558 559static void __net_exit nf_log_net_exit(struct net *net) 560{ 561 netfilter_log_sysctl_exit(net); 562#ifdef CONFIG_PROC_FS 563 remove_proc_entry("nf_log", net->nf.proc_netfilter); 564#endif 565} 566 567static struct pernet_operations nf_log_net_ops = { 568 .init = nf_log_net_init, 569 .exit = nf_log_net_exit, 570}; 571 572int __init netfilter_log_init(void) 573{ 574 return register_pernet_subsys(&nf_log_net_ops); 575} 576