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