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