Blame view
net/netfilter/nf_log.c
7.07 KB
f6ebe77f9
|
1 2 3 4 5 6 |
#include <linux/kernel.h> #include <linux/init.h> #include <linux/module.h> #include <linux/proc_fs.h> #include <linux/skbuff.h> #include <linux/netfilter.h> |
bbd86b9fc
|
7 |
#include <linux/seq_file.h> |
f6ebe77f9
|
8 |
#include <net/protocol.h> |
f01ffbd6e
|
9 |
#include <net/netfilter/nf_log.h> |
f6ebe77f9
|
10 11 |
#include "nf_internals.h" |
a5d292646
|
12 |
/* Internal logging interface, which relies on the real |
f6ebe77f9
|
13 14 15 |
LOG target modules */ #define NF_LOG_PREFIXLEN 128 |
176252746
|
16 |
#define NFLOGGER_NAME_LEN 64 |
f6ebe77f9
|
17 |
|
0906a372f
|
18 |
static const struct nf_logger __rcu *nf_loggers[NFPROTO_NUMPROTO] __read_mostly; |
ca735b3aa
|
19 |
static struct list_head nf_loggers_l[NFPROTO_NUMPROTO] __read_mostly; |
9b73534dc
|
20 |
static DEFINE_MUTEX(nf_log_mutex); |
f6ebe77f9
|
21 |
|
ca735b3aa
|
22 |
static struct nf_logger *__find_logger(int pf, const char *str_logger) |
f6ebe77f9
|
23 |
{ |
ca735b3aa
|
24 |
struct nf_logger *t; |
f6ebe77f9
|
25 |
|
ca735b3aa
|
26 27 28 29 |
list_for_each_entry(t, &nf_loggers_l[pf], list[pf]) { if (!strnicmp(str_logger, t->name, strlen(t->name))) return t; } |
d72367b6f
|
30 |
|
ca735b3aa
|
31 |
return NULL; |
601e68e10
|
32 |
} |
f6ebe77f9
|
33 |
|
ca735b3aa
|
34 35 |
/* return EEXIST if the same logger is registred, 0 on success. */ int nf_log_register(u_int8_t pf, struct nf_logger *logger) |
f6ebe77f9
|
36 |
{ |
b56f2d55c
|
37 |
const struct nf_logger *llog; |
b6f0a3652
|
38 |
int i; |
ca735b3aa
|
39 |
|
7e9c6eeb1
|
40 |
if (pf >= ARRAY_SIZE(nf_loggers)) |
ca735b3aa
|
41 |
return -EINVAL; |
b6f0a3652
|
42 43 |
for (i = 0; i < ARRAY_SIZE(logger->list); i++) INIT_LIST_HEAD(&logger->list[i]); |
9b73534dc
|
44 |
mutex_lock(&nf_log_mutex); |
ca735b3aa
|
45 46 |
if (pf == NFPROTO_UNSPEC) { |
ca735b3aa
|
47 48 49 50 51 |
for (i = NFPROTO_UNSPEC; i < NFPROTO_NUMPROTO; i++) list_add_tail(&(logger->list[i]), &(nf_loggers_l[i])); } else { /* register at end of list to honor first register win */ list_add_tail(&logger->list[pf], &nf_loggers_l[pf]); |
b56f2d55c
|
52 53 54 |
llog = rcu_dereference_protected(nf_loggers[pf], lockdep_is_held(&nf_log_mutex)); if (llog == NULL) |
ca735b3aa
|
55 56 |
rcu_assign_pointer(nf_loggers[pf], logger); } |
9b73534dc
|
57 |
mutex_unlock(&nf_log_mutex); |
f6ebe77f9
|
58 |
|
ca735b3aa
|
59 |
return 0; |
f6ebe77f9
|
60 |
} |
ca735b3aa
|
61 |
EXPORT_SYMBOL(nf_log_register); |
f6ebe77f9
|
62 |
|
ca735b3aa
|
63 |
void nf_log_unregister(struct nf_logger *logger) |
f6ebe77f9
|
64 |
{ |
b56f2d55c
|
65 |
const struct nf_logger *c_logger; |
f6ebe77f9
|
66 |
int i; |
9b73534dc
|
67 |
mutex_lock(&nf_log_mutex); |
7e9c6eeb1
|
68 |
for (i = 0; i < ARRAY_SIZE(nf_loggers); i++) { |
b56f2d55c
|
69 70 71 |
c_logger = rcu_dereference_protected(nf_loggers[i], lockdep_is_held(&nf_log_mutex)); if (c_logger == logger) |
e92ad99c7
|
72 |
rcu_assign_pointer(nf_loggers[i], NULL); |
ca735b3aa
|
73 |
list_del(&logger->list[i]); |
f6ebe77f9
|
74 |
} |
9b73534dc
|
75 |
mutex_unlock(&nf_log_mutex); |
f6ebe77f9
|
76 |
|
a5ea6169f
|
77 |
synchronize_rcu(); |
f6ebe77f9
|
78 |
} |
e92ad99c7
|
79 |
EXPORT_SYMBOL(nf_log_unregister); |
f6ebe77f9
|
80 |
|
ca735b3aa
|
81 82 |
int nf_log_bind_pf(u_int8_t pf, const struct nf_logger *logger) { |
9ef0298a8
|
83 84 |
if (pf >= ARRAY_SIZE(nf_loggers)) return -EINVAL; |
ca735b3aa
|
85 86 87 88 89 90 91 92 93 94 95 96 97 |
mutex_lock(&nf_log_mutex); if (__find_logger(pf, logger->name) == NULL) { mutex_unlock(&nf_log_mutex); return -ENOENT; } rcu_assign_pointer(nf_loggers[pf], logger); mutex_unlock(&nf_log_mutex); return 0; } EXPORT_SYMBOL(nf_log_bind_pf); void nf_log_unbind_pf(u_int8_t pf) { |
9ef0298a8
|
98 99 |
if (pf >= ARRAY_SIZE(nf_loggers)) return; |
ca735b3aa
|
100 101 102 103 104 |
mutex_lock(&nf_log_mutex); rcu_assign_pointer(nf_loggers[pf], NULL); mutex_unlock(&nf_log_mutex); } EXPORT_SYMBOL(nf_log_unbind_pf); |
76108cea0
|
105 |
void nf_log_packet(u_int8_t pf, |
f6ebe77f9
|
106 107 108 109 |
unsigned int hooknum, const struct sk_buff *skb, const struct net_device *in, const struct net_device *out, |
7b2f9631e
|
110 |
const struct nf_loginfo *loginfo, |
f6ebe77f9
|
111 112 113 114 |
const char *fmt, ...) { va_list args; char prefix[NF_LOG_PREFIXLEN]; |
7b2f9631e
|
115 |
const struct nf_logger *logger; |
601e68e10
|
116 |
|
f6ebe77f9
|
117 |
rcu_read_lock(); |
e92ad99c7
|
118 |
logger = rcu_dereference(nf_loggers[pf]); |
f6ebe77f9
|
119 120 121 122 |
if (logger) { va_start(args, fmt); vsnprintf(prefix, sizeof(prefix), fmt, args); va_end(args); |
f6ebe77f9
|
123 |
logger->logfn(pf, hooknum, skb, in, out, loginfo, prefix); |
f6ebe77f9
|
124 125 126 127 128 129 130 131 |
} rcu_read_unlock(); } EXPORT_SYMBOL(nf_log_packet); #ifdef CONFIG_PROC_FS static void *seq_start(struct seq_file *seq, loff_t *pos) { |
6440fe059
|
132 |
mutex_lock(&nf_log_mutex); |
f6ebe77f9
|
133 |
|
7e9c6eeb1
|
134 |
if (*pos >= ARRAY_SIZE(nf_loggers)) |
f6ebe77f9
|
135 136 137 138 139 140 141 142 |
return NULL; return pos; } static void *seq_next(struct seq_file *s, void *v, loff_t *pos) { (*pos)++; |
7e9c6eeb1
|
143 |
if (*pos >= ARRAY_SIZE(nf_loggers)) |
f6ebe77f9
|
144 145 146 147 148 149 150 |
return NULL; return pos; } static void seq_stop(struct seq_file *s, void *v) { |
6440fe059
|
151 |
mutex_unlock(&nf_log_mutex); |
f6ebe77f9
|
152 153 154 155 156 157 |
} static int seq_show(struct seq_file *s, void *v) { loff_t *pos = v; const struct nf_logger *logger; |
c7a913cd5
|
158 159 |
struct nf_logger *t; int ret; |
f6ebe77f9
|
160 |
|
6440fe059
|
161 |
logger = nf_loggers[*pos]; |
f6ebe77f9
|
162 163 |
if (!logger) |
c7a913cd5
|
164 165 166 167 168 169 |
ret = seq_printf(s, "%2lld NONE (", *pos); else ret = seq_printf(s, "%2lld %s (", *pos, logger->name); if (ret < 0) return ret; |
c7a913cd5
|
170 171 |
list_for_each_entry(t, &nf_loggers_l[*pos], list[*pos]) { ret = seq_printf(s, "%s", t->name); |
6440fe059
|
172 |
if (ret < 0) |
c7a913cd5
|
173 |
return ret; |
c7a913cd5
|
174 175 |
if (&t->list[*pos] != nf_loggers_l[*pos].prev) { ret = seq_printf(s, ","); |
6440fe059
|
176 |
if (ret < 0) |
c7a913cd5
|
177 |
return ret; |
c7a913cd5
|
178 179 |
} } |
601e68e10
|
180 |
|
c7a913cd5
|
181 182 |
return seq_printf(s, ") "); |
f6ebe77f9
|
183 |
} |
56b3d975b
|
184 |
static const struct seq_operations nflog_seq_ops = { |
f6ebe77f9
|
185 186 187 188 189 190 191 192 193 194 |
.start = seq_start, .next = seq_next, .stop = seq_stop, .show = seq_show, }; static int nflog_open(struct inode *inode, struct file *file) { return seq_open(file, &nflog_seq_ops); } |
da7071d7e
|
195 |
static const struct file_operations nflog_file_ops = { |
f6ebe77f9
|
196 197 198 199 200 201 |
.owner = THIS_MODULE, .open = nflog_open, .read = seq_read, .llseek = seq_lseek, .release = seq_release, }; |
176252746
|
202 |
|
f6ebe77f9
|
203 |
#endif /* PROC_FS */ |
176252746
|
204 |
#ifdef CONFIG_SYSCTL |
249556192
|
205 |
static struct ctl_path nf_log_sysctl_path[] = { |
f8572d8f2
|
206 207 208 |
{ .procname = "net", }, { .procname = "netfilter", }, { .procname = "nf_log", }, |
176252746
|
209 210 211 212 213 214 |
{ } }; static char nf_log_sysctl_fnames[NFPROTO_NUMPROTO-NFPROTO_UNSPEC][3]; static struct ctl_table nf_log_sysctl_table[NFPROTO_NUMPROTO+1]; static struct ctl_table_header *nf_log_dir_header; |
f6ebe77f9
|
215 |
|
8d65af789
|
216 |
static int nf_log_proc_dostring(ctl_table *table, int write, |
249556192
|
217 |
void __user *buffer, size_t *lenp, loff_t *ppos) |
176252746
|
218 219 |
{ const struct nf_logger *logger; |
249556192
|
220 221 |
char buf[NFLOGGER_NAME_LEN]; size_t size = *lenp; |
176252746
|
222 223 224 225 |
int r = 0; int tindex = (unsigned long)table->extra1; if (write) { |
249556192
|
226 227 228 229 230 231 |
if (size > sizeof(buf)) size = sizeof(buf); if (copy_from_user(buf, buffer, size)) return -EFAULT; if (!strcmp(buf, "NONE")) { |
176252746
|
232 233 234 235 |
nf_log_unbind_pf(tindex); return 0; } mutex_lock(&nf_log_mutex); |
249556192
|
236 |
logger = __find_logger(tindex, buf); |
176252746
|
237 238 239 240 241 242 243 |
if (logger == NULL) { mutex_unlock(&nf_log_mutex); return -ENOENT; } rcu_assign_pointer(nf_loggers[tindex], logger); mutex_unlock(&nf_log_mutex); } else { |
266d07cb1
|
244 245 |
mutex_lock(&nf_log_mutex); logger = nf_loggers[tindex]; |
176252746
|
246 247 248 249 |
if (!logger) table->data = "NONE"; else table->data = logger->name; |
8d65af789
|
250 |
r = proc_dostring(table, write, buffer, lenp, ppos); |
266d07cb1
|
251 |
mutex_unlock(&nf_log_mutex); |
176252746
|
252 253 254 255 256 257 |
} return r; } static __init int netfilter_log_sysctl_init(void) |
f6ebe77f9
|
258 |
{ |
ca735b3aa
|
259 |
int i; |
176252746
|
260 261 262 |
for (i = NFPROTO_UNSPEC; i < NFPROTO_NUMPROTO; i++) { snprintf(nf_log_sysctl_fnames[i-NFPROTO_UNSPEC], 3, "%d", i); |
176252746
|
263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 |
nf_log_sysctl_table[i].procname = nf_log_sysctl_fnames[i-NFPROTO_UNSPEC]; nf_log_sysctl_table[i].data = NULL; nf_log_sysctl_table[i].maxlen = NFLOGGER_NAME_LEN * sizeof(char); nf_log_sysctl_table[i].mode = 0644; nf_log_sysctl_table[i].proc_handler = nf_log_proc_dostring; nf_log_sysctl_table[i].extra1 = (void *)(unsigned long) i; } nf_log_dir_header = register_sysctl_paths(nf_log_sysctl_path, nf_log_sysctl_table); if (!nf_log_dir_header) return -ENOMEM; return 0; } #else static __init int netfilter_log_sysctl_init(void) { return 0; } #endif /* CONFIG_SYSCTL */ int __init netfilter_log_init(void) { int i, r; |
f6ebe77f9
|
290 |
#ifdef CONFIG_PROC_FS |
8eeee8b15
|
291 292 |
if (!proc_create("nf_log", S_IRUGO, proc_net_netfilter, &nflog_file_ops)) |
f6ebe77f9
|
293 |
return -1; |
622439270
|
294 |
#endif |
ca735b3aa
|
295 |
|
176252746
|
296 297 298 299 |
/* Errors will trigger panic, unroll on error is unnecessary. */ r = netfilter_log_sysctl_init(); if (r < 0) return r; |
ca735b3aa
|
300 301 |
for (i = NFPROTO_UNSPEC; i < NFPROTO_NUMPROTO; i++) INIT_LIST_HEAD(&(nf_loggers_l[i])); |
f6ebe77f9
|
302 303 |
return 0; } |