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