Linux-libre 3.16.85-gnu
[librecmc/linux-libre.git] / net / netfilter / nf_log.c
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
18 static struct list_head nf_loggers_l[NFPROTO_NUMPROTO] __read_mostly;
19 static DEFINE_MUTEX(nf_log_mutex);
20
21 static struct nf_logger *__find_logger(int pf, const char *str_logger)
22 {
23         struct nf_logger *t;
24
25         list_for_each_entry(t, &nf_loggers_l[pf], list[pf]) {
26                 if (!strnicmp(str_logger, t->name, strlen(t->name)))
27                         return t;
28         }
29
30         return NULL;
31 }
32
33 void nf_log_set(struct net *net, u_int8_t pf, const struct nf_logger *logger)
34 {
35         const struct nf_logger *log;
36
37         if (pf == NFPROTO_UNSPEC)
38                 return;
39
40         mutex_lock(&nf_log_mutex);
41         log = rcu_dereference_protected(net->nf.nf_loggers[pf],
42                                         lockdep_is_held(&nf_log_mutex));
43         if (log == NULL)
44                 rcu_assign_pointer(net->nf.nf_loggers[pf], logger);
45
46         mutex_unlock(&nf_log_mutex);
47 }
48 EXPORT_SYMBOL(nf_log_set);
49
50 void nf_log_unset(struct net *net, const struct nf_logger *logger)
51 {
52         int i;
53         const struct nf_logger *log;
54
55         mutex_lock(&nf_log_mutex);
56         for (i = 0; i < NFPROTO_NUMPROTO; i++) {
57                 log = rcu_dereference_protected(net->nf.nf_loggers[i],
58                                 lockdep_is_held(&nf_log_mutex));
59                 if (log == logger)
60                         RCU_INIT_POINTER(net->nf.nf_loggers[i], NULL);
61         }
62         mutex_unlock(&nf_log_mutex);
63         synchronize_rcu();
64 }
65 EXPORT_SYMBOL(nf_log_unset);
66
67 /* return EEXIST if the same logger is registered, 0 on success. */
68 int nf_log_register(u_int8_t pf, struct nf_logger *logger)
69 {
70         int i;
71
72         if (pf >= ARRAY_SIZE(init_net.nf.nf_loggers))
73                 return -EINVAL;
74
75         for (i = 0; i < ARRAY_SIZE(logger->list); i++)
76                 INIT_LIST_HEAD(&logger->list[i]);
77
78         mutex_lock(&nf_log_mutex);
79
80         if (pf == NFPROTO_UNSPEC) {
81                 for (i = NFPROTO_UNSPEC; i < NFPROTO_NUMPROTO; i++)
82                         list_add_tail(&(logger->list[i]), &(nf_loggers_l[i]));
83         } else {
84                 /* register at end of list to honor first register win */
85                 list_add_tail(&logger->list[pf], &nf_loggers_l[pf]);
86         }
87
88         mutex_unlock(&nf_log_mutex);
89
90         return 0;
91 }
92 EXPORT_SYMBOL(nf_log_register);
93
94 void nf_log_unregister(struct nf_logger *logger)
95 {
96         int i;
97
98         mutex_lock(&nf_log_mutex);
99         for (i = 0; i < NFPROTO_NUMPROTO; i++)
100                 list_del(&logger->list[i]);
101         mutex_unlock(&nf_log_mutex);
102 }
103 EXPORT_SYMBOL(nf_log_unregister);
104
105 int nf_log_bind_pf(struct net *net, u_int8_t pf,
106                    const struct nf_logger *logger)
107 {
108         if (pf >= ARRAY_SIZE(net->nf.nf_loggers))
109                 return -EINVAL;
110         mutex_lock(&nf_log_mutex);
111         if (__find_logger(pf, logger->name) == NULL) {
112                 mutex_unlock(&nf_log_mutex);
113                 return -ENOENT;
114         }
115         rcu_assign_pointer(net->nf.nf_loggers[pf], logger);
116         mutex_unlock(&nf_log_mutex);
117         return 0;
118 }
119 EXPORT_SYMBOL(nf_log_bind_pf);
120
121 void nf_log_unbind_pf(struct net *net, u_int8_t pf)
122 {
123         if (pf >= ARRAY_SIZE(net->nf.nf_loggers))
124                 return;
125         mutex_lock(&nf_log_mutex);
126         RCU_INIT_POINTER(net->nf.nf_loggers[pf], NULL);
127         mutex_unlock(&nf_log_mutex);
128 }
129 EXPORT_SYMBOL(nf_log_unbind_pf);
130
131 void nf_log_packet(struct net *net,
132                    u_int8_t pf,
133                    unsigned int hooknum,
134                    const struct sk_buff *skb,
135                    const struct net_device *in,
136                    const struct net_device *out,
137                    const struct nf_loginfo *loginfo,
138                    const char *fmt, ...)
139 {
140         va_list args;
141         char prefix[NF_LOG_PREFIXLEN];
142         const struct nf_logger *logger;
143
144         rcu_read_lock();
145         logger = rcu_dereference(net->nf.nf_loggers[pf]);
146         if (logger) {
147                 va_start(args, fmt);
148                 vsnprintf(prefix, sizeof(prefix), fmt, args);
149                 va_end(args);
150                 logger->logfn(net, pf, hooknum, skb, in, out, loginfo, prefix);
151         }
152         rcu_read_unlock();
153 }
154 EXPORT_SYMBOL(nf_log_packet);
155
156 #ifdef CONFIG_PROC_FS
157 static void *seq_start(struct seq_file *seq, loff_t *pos)
158 {
159         struct net *net = seq_file_net(seq);
160
161         mutex_lock(&nf_log_mutex);
162
163         if (*pos >= ARRAY_SIZE(net->nf.nf_loggers))
164                 return NULL;
165
166         return pos;
167 }
168
169 static void *seq_next(struct seq_file *s, void *v, loff_t *pos)
170 {
171         struct net *net = seq_file_net(s);
172
173         (*pos)++;
174
175         if (*pos >= ARRAY_SIZE(net->nf.nf_loggers))
176                 return NULL;
177
178         return pos;
179 }
180
181 static void seq_stop(struct seq_file *s, void *v)
182 {
183         mutex_unlock(&nf_log_mutex);
184 }
185
186 static int seq_show(struct seq_file *s, void *v)
187 {
188         loff_t *pos = v;
189         const struct nf_logger *logger;
190         struct nf_logger *t;
191         int ret;
192         struct net *net = seq_file_net(s);
193
194         logger = rcu_dereference_protected(net->nf.nf_loggers[*pos],
195                                            lockdep_is_held(&nf_log_mutex));
196
197         if (!logger)
198                 ret = seq_printf(s, "%2lld NONE (", *pos);
199         else
200                 ret = seq_printf(s, "%2lld %s (", *pos, logger->name);
201
202         if (ret < 0)
203                 return ret;
204
205         list_for_each_entry(t, &nf_loggers_l[*pos], list[*pos]) {
206                 ret = seq_printf(s, "%s", t->name);
207                 if (ret < 0)
208                         return ret;
209                 if (&t->list[*pos] != nf_loggers_l[*pos].prev) {
210                         ret = seq_printf(s, ",");
211                         if (ret < 0)
212                                 return ret;
213                 }
214         }
215
216         return seq_printf(s, ")\n");
217 }
218
219 static const struct seq_operations nflog_seq_ops = {
220         .start  = seq_start,
221         .next   = seq_next,
222         .stop   = seq_stop,
223         .show   = seq_show,
224 };
225
226 static int nflog_open(struct inode *inode, struct file *file)
227 {
228         return seq_open_net(inode, file, &nflog_seq_ops,
229                             sizeof(struct seq_net_private));
230 }
231
232 static const struct file_operations nflog_file_ops = {
233         .owner   = THIS_MODULE,
234         .open    = nflog_open,
235         .read    = seq_read,
236         .llseek  = seq_lseek,
237         .release = seq_release_net,
238 };
239
240
241 #endif /* PROC_FS */
242
243 #ifdef CONFIG_SYSCTL
244 static char nf_log_sysctl_fnames[NFPROTO_NUMPROTO-NFPROTO_UNSPEC][3];
245 static struct ctl_table nf_log_sysctl_table[NFPROTO_NUMPROTO+1];
246
247 static int nf_log_proc_dostring(struct ctl_table *table, int write,
248                          void __user *buffer, size_t *lenp, loff_t *ppos)
249 {
250         const struct nf_logger *logger;
251         char buf[NFLOGGER_NAME_LEN];
252         size_t size = *lenp;
253         int r = 0;
254         int tindex = (unsigned long)table->extra1;
255         struct net *net = current->nsproxy->net_ns;
256
257         if (write) {
258                 if (size > sizeof(buf))
259                         size = sizeof(buf);
260                 if (copy_from_user(buf, buffer, size))
261                         return -EFAULT;
262
263                 if (!strcmp(buf, "NONE")) {
264                         nf_log_unbind_pf(net, tindex);
265                         return 0;
266                 }
267                 mutex_lock(&nf_log_mutex);
268                 logger = __find_logger(tindex, buf);
269                 if (logger == NULL) {
270                         mutex_unlock(&nf_log_mutex);
271                         return -ENOENT;
272                 }
273                 rcu_assign_pointer(net->nf.nf_loggers[tindex], logger);
274                 mutex_unlock(&nf_log_mutex);
275         } else {
276                 struct ctl_table tmp = *table;
277
278                 tmp.data = buf;
279                 mutex_lock(&nf_log_mutex);
280                 logger = rcu_dereference_protected(net->nf.nf_loggers[tindex],
281                                                    lockdep_is_held(&nf_log_mutex));
282                 if (!logger)
283                         strlcpy(buf, "NONE", sizeof(buf));
284                 else
285                         strlcpy(buf, logger->name, sizeof(buf));
286                 mutex_unlock(&nf_log_mutex);
287                 r = proc_dostring(&tmp, write, buffer, lenp, ppos);
288         }
289
290         return r;
291 }
292
293 static int netfilter_log_sysctl_init(struct net *net)
294 {
295         int i;
296         struct ctl_table *table;
297
298         table = nf_log_sysctl_table;
299         if (!net_eq(net, &init_net)) {
300                 table = kmemdup(nf_log_sysctl_table,
301                                  sizeof(nf_log_sysctl_table),
302                                  GFP_KERNEL);
303                 if (!table)
304                         goto err_alloc;
305         } else {
306                 for (i = NFPROTO_UNSPEC; i < NFPROTO_NUMPROTO; i++) {
307                         snprintf(nf_log_sysctl_fnames[i],
308                                  3, "%d", i);
309                         nf_log_sysctl_table[i].procname =
310                                 nf_log_sysctl_fnames[i];
311                         nf_log_sysctl_table[i].data = NULL;
312                         nf_log_sysctl_table[i].maxlen =
313                                 NFLOGGER_NAME_LEN * sizeof(char);
314                         nf_log_sysctl_table[i].mode = 0644;
315                         nf_log_sysctl_table[i].proc_handler =
316                                 nf_log_proc_dostring;
317                         nf_log_sysctl_table[i].extra1 =
318                                 (void *)(unsigned long) i;
319                 }
320         }
321
322         net->nf.nf_log_dir_header = register_net_sysctl(net,
323                                                 "net/netfilter/nf_log",
324                                                 table);
325         if (!net->nf.nf_log_dir_header)
326                 goto err_reg;
327
328         return 0;
329
330 err_reg:
331         if (!net_eq(net, &init_net))
332                 kfree(table);
333 err_alloc:
334         return -ENOMEM;
335 }
336
337 static void netfilter_log_sysctl_exit(struct net *net)
338 {
339         struct ctl_table *table;
340
341         table = net->nf.nf_log_dir_header->ctl_table_arg;
342         unregister_net_sysctl_table(net->nf.nf_log_dir_header);
343         if (!net_eq(net, &init_net))
344                 kfree(table);
345 }
346 #else
347 static int netfilter_log_sysctl_init(struct net *net)
348 {
349         return 0;
350 }
351
352 static void netfilter_log_sysctl_exit(struct net *net)
353 {
354 }
355 #endif /* CONFIG_SYSCTL */
356
357 static int __net_init nf_log_net_init(struct net *net)
358 {
359         int ret = -ENOMEM;
360
361 #ifdef CONFIG_PROC_FS
362         if (!proc_create("nf_log", S_IRUGO,
363                          net->nf.proc_netfilter, &nflog_file_ops))
364                 return ret;
365 #endif
366         ret = netfilter_log_sysctl_init(net);
367         if (ret < 0)
368                 goto out_sysctl;
369
370         return 0;
371
372 out_sysctl:
373 #ifdef CONFIG_PROC_FS
374         remove_proc_entry("nf_log", net->nf.proc_netfilter);
375 #endif
376         return ret;
377 }
378
379 static void __net_exit nf_log_net_exit(struct net *net)
380 {
381         netfilter_log_sysctl_exit(net);
382 #ifdef CONFIG_PROC_FS
383         remove_proc_entry("nf_log", net->nf.proc_netfilter);
384 #endif
385 }
386
387 static struct pernet_operations nf_log_net_ops = {
388         .init = nf_log_net_init,
389         .exit = nf_log_net_exit,
390 };
391
392 int __init netfilter_log_init(void)
393 {
394         int i, ret;
395
396         ret = register_pernet_subsys(&nf_log_net_ops);
397         if (ret < 0)
398                 return ret;
399
400         for (i = NFPROTO_UNSPEC; i < NFPROTO_NUMPROTO; i++)
401                 INIT_LIST_HEAD(&(nf_loggers_l[i]));
402
403         return 0;
404 }