mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-15 15:15:47 +00:00
da7071d7e3
Many struct file_operations in the kernel can be "const". Marking them const moves these to the .rodata section, which avoids false sharing with potential dirty data. In addition it'll catch accidental writes at compile time to these shared resources. Signed-off-by: Arjan van de Ven <arjan@linux.intel.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
493 lines
12 KiB
C
493 lines
12 KiB
C
/* This file contains all the functions required for the standalone
|
|
nf_conntrack module.
|
|
|
|
These are not required by the compatibility layer.
|
|
*/
|
|
|
|
/* (C) 1999-2001 Paul `Rusty' Russell
|
|
* (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* 16 Dec 2003: Yasuyuki Kozakai @USAGI <yasuyuki.kozakai@toshiba.co.jp>
|
|
* - generalize L3 protocol dependent part.
|
|
*
|
|
* Derived from net/ipv4/netfilter/ip_conntrack_standalone.c
|
|
*/
|
|
|
|
#include <linux/types.h>
|
|
#include <linux/netfilter.h>
|
|
#include <linux/module.h>
|
|
#include <linux/skbuff.h>
|
|
#include <linux/proc_fs.h>
|
|
#include <linux/seq_file.h>
|
|
#include <linux/percpu.h>
|
|
#include <linux/netdevice.h>
|
|
#ifdef CONFIG_SYSCTL
|
|
#include <linux/sysctl.h>
|
|
#endif
|
|
|
|
#include <net/netfilter/nf_conntrack.h>
|
|
#include <net/netfilter/nf_conntrack_core.h>
|
|
#include <net/netfilter/nf_conntrack_l3proto.h>
|
|
#include <net/netfilter/nf_conntrack_l4proto.h>
|
|
#include <net/netfilter/nf_conntrack_expect.h>
|
|
#include <net/netfilter/nf_conntrack_helper.h>
|
|
|
|
#if 0
|
|
#define DEBUGP printk
|
|
#else
|
|
#define DEBUGP(format, args...)
|
|
#endif
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
#ifdef CONFIG_PROC_FS
|
|
int
|
|
print_tuple(struct seq_file *s, const struct nf_conntrack_tuple *tuple,
|
|
struct nf_conntrack_l3proto *l3proto,
|
|
struct nf_conntrack_l4proto *l4proto)
|
|
{
|
|
return l3proto->print_tuple(s, tuple) || l4proto->print_tuple(s, tuple);
|
|
}
|
|
EXPORT_SYMBOL_GPL(print_tuple);
|
|
|
|
#ifdef CONFIG_NF_CT_ACCT
|
|
static unsigned int
|
|
seq_print_counters(struct seq_file *s,
|
|
const struct ip_conntrack_counter *counter)
|
|
{
|
|
return seq_printf(s, "packets=%llu bytes=%llu ",
|
|
(unsigned long long)counter->packets,
|
|
(unsigned long long)counter->bytes);
|
|
}
|
|
#else
|
|
#define seq_print_counters(x, y) 0
|
|
#endif
|
|
|
|
struct ct_iter_state {
|
|
unsigned int bucket;
|
|
};
|
|
|
|
static struct list_head *ct_get_first(struct seq_file *seq)
|
|
{
|
|
struct ct_iter_state *st = seq->private;
|
|
|
|
for (st->bucket = 0;
|
|
st->bucket < nf_conntrack_htable_size;
|
|
st->bucket++) {
|
|
if (!list_empty(&nf_conntrack_hash[st->bucket]))
|
|
return nf_conntrack_hash[st->bucket].next;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static struct list_head *ct_get_next(struct seq_file *seq, struct list_head *head)
|
|
{
|
|
struct ct_iter_state *st = seq->private;
|
|
|
|
head = head->next;
|
|
while (head == &nf_conntrack_hash[st->bucket]) {
|
|
if (++st->bucket >= nf_conntrack_htable_size)
|
|
return NULL;
|
|
head = nf_conntrack_hash[st->bucket].next;
|
|
}
|
|
return head;
|
|
}
|
|
|
|
static struct list_head *ct_get_idx(struct seq_file *seq, loff_t pos)
|
|
{
|
|
struct list_head *head = ct_get_first(seq);
|
|
|
|
if (head)
|
|
while (pos && (head = ct_get_next(seq, head)))
|
|
pos--;
|
|
return pos ? NULL : head;
|
|
}
|
|
|
|
static void *ct_seq_start(struct seq_file *seq, loff_t *pos)
|
|
{
|
|
read_lock_bh(&nf_conntrack_lock);
|
|
return ct_get_idx(seq, *pos);
|
|
}
|
|
|
|
static void *ct_seq_next(struct seq_file *s, void *v, loff_t *pos)
|
|
{
|
|
(*pos)++;
|
|
return ct_get_next(s, v);
|
|
}
|
|
|
|
static void ct_seq_stop(struct seq_file *s, void *v)
|
|
{
|
|
read_unlock_bh(&nf_conntrack_lock);
|
|
}
|
|
|
|
/* return 0 on success, 1 in case of error */
|
|
static int ct_seq_show(struct seq_file *s, void *v)
|
|
{
|
|
const struct nf_conntrack_tuple_hash *hash = v;
|
|
const struct nf_conn *conntrack = nf_ct_tuplehash_to_ctrack(hash);
|
|
struct nf_conntrack_l3proto *l3proto;
|
|
struct nf_conntrack_l4proto *l4proto;
|
|
|
|
NF_CT_ASSERT(conntrack);
|
|
|
|
/* we only want to print DIR_ORIGINAL */
|
|
if (NF_CT_DIRECTION(hash))
|
|
return 0;
|
|
|
|
l3proto = __nf_ct_l3proto_find(conntrack->tuplehash[IP_CT_DIR_ORIGINAL]
|
|
.tuple.src.l3num);
|
|
|
|
NF_CT_ASSERT(l3proto);
|
|
l4proto = __nf_ct_l4proto_find(conntrack->tuplehash[IP_CT_DIR_ORIGINAL]
|
|
.tuple.src.l3num,
|
|
conntrack->tuplehash[IP_CT_DIR_ORIGINAL]
|
|
.tuple.dst.protonum);
|
|
NF_CT_ASSERT(l4proto);
|
|
|
|
if (seq_printf(s, "%-8s %u %-8s %u %ld ",
|
|
l3proto->name,
|
|
conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num,
|
|
l4proto->name,
|
|
conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum,
|
|
timer_pending(&conntrack->timeout)
|
|
? (long)(conntrack->timeout.expires - jiffies)/HZ : 0) != 0)
|
|
return -ENOSPC;
|
|
|
|
if (l3proto->print_conntrack(s, conntrack))
|
|
return -ENOSPC;
|
|
|
|
if (l4proto->print_conntrack(s, conntrack))
|
|
return -ENOSPC;
|
|
|
|
if (print_tuple(s, &conntrack->tuplehash[IP_CT_DIR_ORIGINAL].tuple,
|
|
l3proto, l4proto))
|
|
return -ENOSPC;
|
|
|
|
if (seq_print_counters(s, &conntrack->counters[IP_CT_DIR_ORIGINAL]))
|
|
return -ENOSPC;
|
|
|
|
if (!(test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status)))
|
|
if (seq_printf(s, "[UNREPLIED] "))
|
|
return -ENOSPC;
|
|
|
|
if (print_tuple(s, &conntrack->tuplehash[IP_CT_DIR_REPLY].tuple,
|
|
l3proto, l4proto))
|
|
return -ENOSPC;
|
|
|
|
if (seq_print_counters(s, &conntrack->counters[IP_CT_DIR_REPLY]))
|
|
return -ENOSPC;
|
|
|
|
if (test_bit(IPS_ASSURED_BIT, &conntrack->status))
|
|
if (seq_printf(s, "[ASSURED] "))
|
|
return -ENOSPC;
|
|
|
|
#if defined(CONFIG_NF_CONNTRACK_MARK)
|
|
if (seq_printf(s, "mark=%u ", conntrack->mark))
|
|
return -ENOSPC;
|
|
#endif
|
|
|
|
#ifdef CONFIG_NF_CONNTRACK_SECMARK
|
|
if (seq_printf(s, "secmark=%u ", conntrack->secmark))
|
|
return -ENOSPC;
|
|
#endif
|
|
|
|
if (seq_printf(s, "use=%u\n", atomic_read(&conntrack->ct_general.use)))
|
|
return -ENOSPC;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct seq_operations ct_seq_ops = {
|
|
.start = ct_seq_start,
|
|
.next = ct_seq_next,
|
|
.stop = ct_seq_stop,
|
|
.show = ct_seq_show
|
|
};
|
|
|
|
static int ct_open(struct inode *inode, struct file *file)
|
|
{
|
|
struct seq_file *seq;
|
|
struct ct_iter_state *st;
|
|
int ret;
|
|
|
|
st = kmalloc(sizeof(struct ct_iter_state), GFP_KERNEL);
|
|
if (st == NULL)
|
|
return -ENOMEM;
|
|
ret = seq_open(file, &ct_seq_ops);
|
|
if (ret)
|
|
goto out_free;
|
|
seq = file->private_data;
|
|
seq->private = st;
|
|
memset(st, 0, sizeof(struct ct_iter_state));
|
|
return ret;
|
|
out_free:
|
|
kfree(st);
|
|
return ret;
|
|
}
|
|
|
|
static const struct file_operations ct_file_ops = {
|
|
.owner = THIS_MODULE,
|
|
.open = ct_open,
|
|
.read = seq_read,
|
|
.llseek = seq_lseek,
|
|
.release = seq_release_private,
|
|
};
|
|
|
|
static void *ct_cpu_seq_start(struct seq_file *seq, loff_t *pos)
|
|
{
|
|
int cpu;
|
|
|
|
if (*pos == 0)
|
|
return SEQ_START_TOKEN;
|
|
|
|
for (cpu = *pos-1; cpu < NR_CPUS; ++cpu) {
|
|
if (!cpu_possible(cpu))
|
|
continue;
|
|
*pos = cpu + 1;
|
|
return &per_cpu(nf_conntrack_stat, cpu);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void *ct_cpu_seq_next(struct seq_file *seq, void *v, loff_t *pos)
|
|
{
|
|
int cpu;
|
|
|
|
for (cpu = *pos; cpu < NR_CPUS; ++cpu) {
|
|
if (!cpu_possible(cpu))
|
|
continue;
|
|
*pos = cpu + 1;
|
|
return &per_cpu(nf_conntrack_stat, cpu);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void ct_cpu_seq_stop(struct seq_file *seq, void *v)
|
|
{
|
|
}
|
|
|
|
static int ct_cpu_seq_show(struct seq_file *seq, void *v)
|
|
{
|
|
unsigned int nr_conntracks = atomic_read(&nf_conntrack_count);
|
|
struct ip_conntrack_stat *st = v;
|
|
|
|
if (v == SEQ_START_TOKEN) {
|
|
seq_printf(seq, "entries searched found new invalid ignore delete delete_list insert insert_failed drop early_drop icmp_error expect_new expect_create expect_delete\n");
|
|
return 0;
|
|
}
|
|
|
|
seq_printf(seq, "%08x %08x %08x %08x %08x %08x %08x %08x "
|
|
"%08x %08x %08x %08x %08x %08x %08x %08x \n",
|
|
nr_conntracks,
|
|
st->searched,
|
|
st->found,
|
|
st->new,
|
|
st->invalid,
|
|
st->ignore,
|
|
st->delete,
|
|
st->delete_list,
|
|
st->insert,
|
|
st->insert_failed,
|
|
st->drop,
|
|
st->early_drop,
|
|
st->error,
|
|
|
|
st->expect_new,
|
|
st->expect_create,
|
|
st->expect_delete
|
|
);
|
|
return 0;
|
|
}
|
|
|
|
static struct seq_operations ct_cpu_seq_ops = {
|
|
.start = ct_cpu_seq_start,
|
|
.next = ct_cpu_seq_next,
|
|
.stop = ct_cpu_seq_stop,
|
|
.show = ct_cpu_seq_show,
|
|
};
|
|
|
|
static int ct_cpu_seq_open(struct inode *inode, struct file *file)
|
|
{
|
|
return seq_open(file, &ct_cpu_seq_ops);
|
|
}
|
|
|
|
static const struct file_operations ct_cpu_seq_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = ct_cpu_seq_open,
|
|
.read = seq_read,
|
|
.llseek = seq_lseek,
|
|
.release = seq_release_private,
|
|
};
|
|
#endif /* CONFIG_PROC_FS */
|
|
|
|
/* Sysctl support */
|
|
|
|
int nf_conntrack_checksum __read_mostly = 1;
|
|
EXPORT_SYMBOL_GPL(nf_conntrack_checksum);
|
|
|
|
#ifdef CONFIG_SYSCTL
|
|
/* Log invalid packets of a given protocol */
|
|
static int log_invalid_proto_min = 0;
|
|
static int log_invalid_proto_max = 255;
|
|
|
|
static struct ctl_table_header *nf_ct_sysctl_header;
|
|
|
|
static ctl_table nf_ct_sysctl_table[] = {
|
|
{
|
|
.ctl_name = NET_NF_CONNTRACK_MAX,
|
|
.procname = "nf_conntrack_max",
|
|
.data = &nf_conntrack_max,
|
|
.maxlen = sizeof(int),
|
|
.mode = 0644,
|
|
.proc_handler = &proc_dointvec,
|
|
},
|
|
{
|
|
.ctl_name = NET_NF_CONNTRACK_COUNT,
|
|
.procname = "nf_conntrack_count",
|
|
.data = &nf_conntrack_count,
|
|
.maxlen = sizeof(int),
|
|
.mode = 0444,
|
|
.proc_handler = &proc_dointvec,
|
|
},
|
|
{
|
|
.ctl_name = NET_NF_CONNTRACK_BUCKETS,
|
|
.procname = "nf_conntrack_buckets",
|
|
.data = &nf_conntrack_htable_size,
|
|
.maxlen = sizeof(unsigned int),
|
|
.mode = 0444,
|
|
.proc_handler = &proc_dointvec,
|
|
},
|
|
{
|
|
.ctl_name = NET_NF_CONNTRACK_CHECKSUM,
|
|
.procname = "nf_conntrack_checksum",
|
|
.data = &nf_conntrack_checksum,
|
|
.maxlen = sizeof(unsigned int),
|
|
.mode = 0644,
|
|
.proc_handler = &proc_dointvec,
|
|
},
|
|
{
|
|
.ctl_name = NET_NF_CONNTRACK_LOG_INVALID,
|
|
.procname = "nf_conntrack_log_invalid",
|
|
.data = &nf_ct_log_invalid,
|
|
.maxlen = sizeof(unsigned int),
|
|
.mode = 0644,
|
|
.proc_handler = &proc_dointvec_minmax,
|
|
.strategy = &sysctl_intvec,
|
|
.extra1 = &log_invalid_proto_min,
|
|
.extra2 = &log_invalid_proto_max,
|
|
},
|
|
|
|
{ .ctl_name = 0 }
|
|
};
|
|
|
|
#define NET_NF_CONNTRACK_MAX 2089
|
|
|
|
static ctl_table nf_ct_netfilter_table[] = {
|
|
{
|
|
.ctl_name = NET_NETFILTER,
|
|
.procname = "netfilter",
|
|
.mode = 0555,
|
|
.child = nf_ct_sysctl_table,
|
|
},
|
|
{
|
|
.ctl_name = NET_NF_CONNTRACK_MAX,
|
|
.procname = "nf_conntrack_max",
|
|
.data = &nf_conntrack_max,
|
|
.maxlen = sizeof(int),
|
|
.mode = 0644,
|
|
.proc_handler = &proc_dointvec,
|
|
},
|
|
{ .ctl_name = 0 }
|
|
};
|
|
|
|
static ctl_table nf_ct_net_table[] = {
|
|
{
|
|
.ctl_name = CTL_NET,
|
|
.procname = "net",
|
|
.mode = 0555,
|
|
.child = nf_ct_netfilter_table,
|
|
},
|
|
{ .ctl_name = 0 }
|
|
};
|
|
EXPORT_SYMBOL_GPL(nf_ct_log_invalid);
|
|
#endif /* CONFIG_SYSCTL */
|
|
|
|
static int __init nf_conntrack_standalone_init(void)
|
|
{
|
|
#ifdef CONFIG_PROC_FS
|
|
struct proc_dir_entry *proc, *proc_exp, *proc_stat;
|
|
#endif
|
|
int ret = 0;
|
|
|
|
ret = nf_conntrack_init();
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
#ifdef CONFIG_PROC_FS
|
|
proc = proc_net_fops_create("nf_conntrack", 0440, &ct_file_ops);
|
|
if (!proc) goto cleanup_init;
|
|
|
|
proc_exp = proc_net_fops_create("nf_conntrack_expect", 0440,
|
|
&exp_file_ops);
|
|
if (!proc_exp) goto cleanup_proc;
|
|
|
|
proc_stat = create_proc_entry("nf_conntrack", S_IRUGO, proc_net_stat);
|
|
if (!proc_stat)
|
|
goto cleanup_proc_exp;
|
|
|
|
proc_stat->proc_fops = &ct_cpu_seq_fops;
|
|
proc_stat->owner = THIS_MODULE;
|
|
#endif
|
|
#ifdef CONFIG_SYSCTL
|
|
nf_ct_sysctl_header = register_sysctl_table(nf_ct_net_table, 0);
|
|
if (nf_ct_sysctl_header == NULL) {
|
|
printk("nf_conntrack: can't register to sysctl.\n");
|
|
ret = -ENOMEM;
|
|
goto cleanup_proc_stat;
|
|
}
|
|
#endif
|
|
return ret;
|
|
|
|
#ifdef CONFIG_SYSCTL
|
|
cleanup_proc_stat:
|
|
#endif
|
|
#ifdef CONFIG_PROC_FS
|
|
remove_proc_entry("nf_conntrack", proc_net_stat);
|
|
cleanup_proc_exp:
|
|
proc_net_remove("nf_conntrack_expect");
|
|
cleanup_proc:
|
|
proc_net_remove("nf_conntrack");
|
|
cleanup_init:
|
|
#endif /* CNFIG_PROC_FS */
|
|
nf_conntrack_cleanup();
|
|
return ret;
|
|
}
|
|
|
|
static void __exit nf_conntrack_standalone_fini(void)
|
|
{
|
|
#ifdef CONFIG_SYSCTL
|
|
unregister_sysctl_table(nf_ct_sysctl_header);
|
|
#endif
|
|
#ifdef CONFIG_PROC_FS
|
|
remove_proc_entry("nf_conntrack", proc_net_stat);
|
|
proc_net_remove("nf_conntrack_expect");
|
|
proc_net_remove("nf_conntrack");
|
|
#endif /* CNFIG_PROC_FS */
|
|
nf_conntrack_cleanup();
|
|
}
|
|
|
|
module_init(nf_conntrack_standalone_init);
|
|
module_exit(nf_conntrack_standalone_fini);
|
|
|
|
/* Some modules need us, but don't depend directly on any symbol.
|
|
They should call this. */
|
|
void need_conntrack(void)
|
|
{
|
|
}
|
|
EXPORT_SYMBOL_GPL(need_conntrack);
|