Blame view
net/netfilter/nfnetlink_log.c
24.2 KB
0597f2680
|
1 2 3 4 5 6 7 8 9 10 11 12 |
/* * This is a module which is used for logging packets to userspace via * nfetlink. * * (C) 2005 by Harald Welte <laforge@netfilter.org> * * Based on the old ipv4-only ipt_ULOG.c: * (C) 2000-2004 by Harald Welte <laforge@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. |
0597f2680
|
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
*/ #include <linux/module.h> #include <linux/skbuff.h> #include <linux/init.h> #include <linux/ip.h> #include <linux/ipv6.h> #include <linux/netdevice.h> #include <linux/netfilter.h> #include <linux/netlink.h> #include <linux/netfilter/nfnetlink.h> #include <linux/netfilter/nfnetlink_log.h> #include <linux/spinlock.h> #include <linux/sysctl.h> #include <linux/proc_fs.h> #include <linux/security.h> #include <linux/list.h> #include <linux/jhash.h> #include <linux/random.h> |
5a0e3ad6a
|
31 |
#include <linux/slab.h> |
0597f2680
|
32 |
#include <net/sock.h> |
f01ffbd6e
|
33 |
#include <net/netfilter/nf_log.h> |
d9e150071
|
34 |
#include <net/netfilter/nfnetlink_log.h> |
0597f2680
|
35 36 |
#include <asm/atomic.h> |
fbcd923c3
|
37 38 39 |
#ifdef CONFIG_BRIDGE_NETFILTER #include "../bridge/br_private.h" #endif |
c2db29243
|
40 |
#define NFULNL_NLBUFSIZ_DEFAULT NLMSG_GOODSIZE |
2c6764b74
|
41 |
#define NFULNL_TIMEOUT_DEFAULT 100 /* every second */ |
0597f2680
|
42 |
#define NFULNL_QTHRESH_DEFAULT 100 /* 100 packets */ |
6b6ec99a0
|
43 |
#define NFULNL_COPY_RANGE_MAX 0xFFFF /* max packet size is limited by 16-bit struct nfattr nfa_len field */ |
0597f2680
|
44 45 46 |
#define PRINTR(x, args...) do { if (net_ratelimit()) \ printk(x, ## args); } while (0); |
0597f2680
|
47 48 49 50 51 52 53 |
struct nfulnl_instance { struct hlist_node hlist; /* global list of instances */ spinlock_t lock; atomic_t use; /* use count */ unsigned int qlen; /* number of nlmsgs in skb */ struct sk_buff *skb; /* pre-allocatd skb */ |
0597f2680
|
54 55 56 57 58 59 60 61 |
struct timer_list timer; int peer_pid; /* PID of the peer process */ /* configurable parameters */ unsigned int flushtimeout; /* timeout until queue flush */ unsigned int nlbufsiz; /* netlink buffer allocation size */ unsigned int qthreshold; /* threshold of the queue */ u_int32_t copy_range; |
0af5f6c1e
|
62 |
u_int32_t seq; /* instance-local sequential counter */ |
0597f2680
|
63 |
u_int16_t group_num; /* number of this queue */ |
0af5f6c1e
|
64 |
u_int16_t flags; |
601e68e10
|
65 |
u_int8_t copy_mode; |
bed1be208
|
66 |
struct rcu_head rcu; |
0597f2680
|
67 |
}; |
bed1be208
|
68 |
static DEFINE_SPINLOCK(instances_lock); |
0af5f6c1e
|
69 |
static atomic_t global_seq; |
0597f2680
|
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
#define INSTANCE_BUCKETS 16 static struct hlist_head instance_table[INSTANCE_BUCKETS]; static unsigned int hash_init; static inline u_int8_t instance_hashfn(u_int16_t group_num) { return ((group_num & 0xff) % INSTANCE_BUCKETS); } static struct nfulnl_instance * __instance_lookup(u_int16_t group_num) { struct hlist_head *head; struct hlist_node *pos; struct nfulnl_instance *inst; |
0597f2680
|
86 |
head = &instance_table[instance_hashfn(group_num)]; |
bed1be208
|
87 |
hlist_for_each_entry_rcu(inst, pos, head, hlist) { |
0597f2680
|
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
if (inst->group_num == group_num) return inst; } return NULL; } static inline void instance_get(struct nfulnl_instance *inst) { atomic_inc(&inst->use); } static struct nfulnl_instance * instance_lookup_get(u_int16_t group_num) { struct nfulnl_instance *inst; |
bed1be208
|
104 |
rcu_read_lock_bh(); |
0597f2680
|
105 |
inst = __instance_lookup(group_num); |
f5c5440d4
|
106 107 |
if (inst && !atomic_inc_not_zero(&inst->use)) inst = NULL; |
bed1be208
|
108 |
rcu_read_unlock_bh(); |
0597f2680
|
109 110 111 |
return inst; } |
bed1be208
|
112 113 114 115 116 |
static void nfulnl_instance_free_rcu(struct rcu_head *head) { kfree(container_of(head, struct nfulnl_instance, rcu)); module_put(THIS_MODULE); } |
0597f2680
|
117 118 119 |
static void instance_put(struct nfulnl_instance *inst) { |
bed1be208
|
120 121 |
if (inst && atomic_dec_and_test(&inst->use)) call_rcu_bh(&inst->rcu, nfulnl_instance_free_rcu); |
0597f2680
|
122 123 124 125 126 127 128 129 |
} static void nfulnl_timer(unsigned long data); static struct nfulnl_instance * instance_create(u_int16_t group_num, int pid) { struct nfulnl_instance *inst; |
baab2ce7d
|
130 |
int err; |
0597f2680
|
131 |
|
bed1be208
|
132 |
spin_lock_bh(&instances_lock); |
0597f2680
|
133 |
if (__instance_lookup(group_num)) { |
baab2ce7d
|
134 |
err = -EEXIST; |
0597f2680
|
135 136 |
goto out_unlock; } |
10dfdc69e
|
137 |
inst = kzalloc(sizeof(*inst), GFP_ATOMIC); |
baab2ce7d
|
138 139 |
if (!inst) { err = -ENOMEM; |
0597f2680
|
140 |
goto out_unlock; |
baab2ce7d
|
141 |
} |
0597f2680
|
142 |
|
aace57e05
|
143 144 |
if (!try_module_get(THIS_MODULE)) { kfree(inst); |
baab2ce7d
|
145 |
err = -EAGAIN; |
aace57e05
|
146 147 |
goto out_unlock; } |
0597f2680
|
148 |
INIT_HLIST_NODE(&inst->hlist); |
181a46a56
|
149 |
spin_lock_init(&inst->lock); |
0597f2680
|
150 151 |
/* needs to be two, since we _put() after creation */ atomic_set(&inst->use, 2); |
e6f689db5
|
152 |
setup_timer(&inst->timer, nfulnl_timer, (unsigned long)inst); |
0597f2680
|
153 154 155 156 157 158 159 160 |
inst->peer_pid = pid; inst->group_num = group_num; inst->qthreshold = NFULNL_QTHRESH_DEFAULT; inst->flushtimeout = NFULNL_TIMEOUT_DEFAULT; inst->nlbufsiz = NFULNL_NLBUFSIZ_DEFAULT; inst->copy_mode = NFULNL_COPY_PACKET; |
6b6ec99a0
|
161 |
inst->copy_range = NFULNL_COPY_RANGE_MAX; |
0597f2680
|
162 |
|
f5c5440d4
|
163 |
hlist_add_head_rcu(&inst->hlist, |
0597f2680
|
164 |
&instance_table[instance_hashfn(group_num)]); |
bed1be208
|
165 |
spin_unlock_bh(&instances_lock); |
0597f2680
|
166 167 |
return inst; |
0597f2680
|
168 |
out_unlock: |
bed1be208
|
169 |
spin_unlock_bh(&instances_lock); |
baab2ce7d
|
170 |
return ERR_PTR(err); |
0597f2680
|
171 |
} |
e35670614
|
172 |
static void __nfulnl_flush(struct nfulnl_instance *inst); |
0597f2680
|
173 |
|
f5c5440d4
|
174 |
/* called with BH disabled */ |
0597f2680
|
175 |
static void |
9afdb00c8
|
176 |
__instance_destroy(struct nfulnl_instance *inst) |
0597f2680
|
177 178 |
{ /* first pull it out of the global list */ |
f5c5440d4
|
179 |
hlist_del_rcu(&inst->hlist); |
0597f2680
|
180 |
|
0597f2680
|
181 |
/* then flush all pending packets from skb */ |
f5c5440d4
|
182 183 184 185 |
spin_lock(&inst->lock); /* lockless readers wont be able to use us */ inst->copy_mode = NFULNL_COPY_DISABLED; |
e35670614
|
186 187 |
if (inst->skb) __nfulnl_flush(inst); |
f5c5440d4
|
188 |
spin_unlock(&inst->lock); |
0597f2680
|
189 190 191 |
/* and finally put the refcount */ instance_put(inst); |
0597f2680
|
192 193 194 |
} static inline void |
0597f2680
|
195 196 |
instance_destroy(struct nfulnl_instance *inst) { |
bed1be208
|
197 |
spin_lock_bh(&instances_lock); |
9afdb00c8
|
198 |
__instance_destroy(inst); |
bed1be208
|
199 |
spin_unlock_bh(&instances_lock); |
0597f2680
|
200 201 202 203 204 205 206 207 208 |
} static int nfulnl_set_mode(struct nfulnl_instance *inst, u_int8_t mode, unsigned int range) { int status = 0; spin_lock_bh(&inst->lock); |
601e68e10
|
209 |
|
0597f2680
|
210 211 212 213 214 215 |
switch (mode) { case NFULNL_COPY_NONE: case NFULNL_COPY_META: inst->copy_mode = mode; inst->copy_range = 0; break; |
601e68e10
|
216 |
|
0597f2680
|
217 218 |
case NFULNL_COPY_PACKET: inst->copy_mode = mode; |
6b6ec99a0
|
219 220 |
inst->copy_range = min_t(unsigned int, range, NFULNL_COPY_RANGE_MAX); |
0597f2680
|
221 |
break; |
601e68e10
|
222 |
|
0597f2680
|
223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 |
default: status = -EINVAL; break; } spin_unlock_bh(&inst->lock); return status; } static int nfulnl_set_nlbufsiz(struct nfulnl_instance *inst, u_int32_t nlbufsiz) { int status; spin_lock_bh(&inst->lock); if (nlbufsiz < NFULNL_NLBUFSIZ_DEFAULT) status = -ERANGE; else if (nlbufsiz > 131072) status = -ERANGE; else { inst->nlbufsiz = nlbufsiz; status = 0; } spin_unlock_bh(&inst->lock); return status; } static int nfulnl_set_timeout(struct nfulnl_instance *inst, u_int32_t timeout) { spin_lock_bh(&inst->lock); inst->flushtimeout = timeout; spin_unlock_bh(&inst->lock); return 0; } static int nfulnl_set_qthresh(struct nfulnl_instance *inst, u_int32_t qthresh) { spin_lock_bh(&inst->lock); inst->qthreshold = qthresh; spin_unlock_bh(&inst->lock); return 0; } |
0af5f6c1e
|
271 272 273 274 |
static int nfulnl_set_flags(struct nfulnl_instance *inst, u_int16_t flags) { spin_lock_bh(&inst->lock); |
ee433530d
|
275 |
inst->flags = flags; |
0af5f6c1e
|
276 277 278 279 |
spin_unlock_bh(&inst->lock); return 0; } |
c6a8f6483
|
280 281 |
static struct sk_buff * nfulnl_alloc_skb(unsigned int inst_size, unsigned int pkt_size) |
0597f2680
|
282 283 |
{ struct sk_buff *skb; |
ad2ad0f96
|
284 |
unsigned int n; |
0597f2680
|
285 |
|
0597f2680
|
286 287 |
/* alloc skb which should be big enough for a whole multipart * message. WARNING: has to be <= 128k due to slab restrictions */ |
ad2ad0f96
|
288 289 |
n = max(inst_size, pkt_size); skb = alloc_skb(n, GFP_ATOMIC); |
0597f2680
|
290 |
if (!skb) { |
654d0fbdc
|
291 292 |
pr_notice("nfnetlink_log: can't alloc whole buffer (%u bytes) ", |
0597f2680
|
293 |
inst_size); |
ad2ad0f96
|
294 295 296 |
if (n > pkt_size) { /* try to allocate only as much as we need for current * packet */ |
0597f2680
|
297 |
|
ad2ad0f96
|
298 299 |
skb = alloc_skb(pkt_size, GFP_ATOMIC); if (!skb) |
654d0fbdc
|
300 |
pr_err("nfnetlink_log: can't even alloc %u " |
ad2ad0f96
|
301 302 303 |
"bytes ", pkt_size); } |
0597f2680
|
304 305 306 307 308 309 310 311 |
} return skb; } static int __nfulnl_send(struct nfulnl_instance *inst) { |
29c5d4afb
|
312 |
int status = -1; |
0597f2680
|
313 |
|
0597f2680
|
314 |
if (inst->qlen > 1) |
29c5d4afb
|
315 316 317 |
NLMSG_PUT(inst->skb, 0, 0, NLMSG_DONE, sizeof(struct nfgenmsg)); |
0597f2680
|
318 |
|
cd8c20b65
|
319 320 |
status = nfnetlink_unicast(inst->skb, &init_net, inst->peer_pid, MSG_DONTWAIT); |
0597f2680
|
321 322 323 |
inst->qlen = 0; inst->skb = NULL; |
0597f2680
|
324 |
|
29c5d4afb
|
325 |
nlmsg_failure: |
0597f2680
|
326 327 |
return status; } |
e35670614
|
328 329 330 331 332 333 334 335 336 |
static void __nfulnl_flush(struct nfulnl_instance *inst) { /* timer holds a reference */ if (del_timer(&inst->timer)) instance_put(inst); if (inst->skb) __nfulnl_send(inst); } |
c6a8f6483
|
337 338 |
static void nfulnl_timer(unsigned long data) |
0597f2680
|
339 |
{ |
601e68e10
|
340 |
struct nfulnl_instance *inst = (struct nfulnl_instance *)data; |
0597f2680
|
341 |
|
0597f2680
|
342 |
spin_lock_bh(&inst->lock); |
370e6a878
|
343 344 |
if (inst->skb) __nfulnl_send(inst); |
0597f2680
|
345 |
spin_unlock_bh(&inst->lock); |
05f7b7b36
|
346 |
instance_put(inst); |
0597f2680
|
347 |
} |
0af5f6c1e
|
348 349 |
/* This is an inline function, we don't really care about a long * list of arguments */ |
601e68e10
|
350 |
static inline int |
0597f2680
|
351 |
__build_packet_message(struct nfulnl_instance *inst, |
601e68e10
|
352 |
const struct sk_buff *skb, |
0597f2680
|
353 |
unsigned int data_len, |
76108cea0
|
354 |
u_int8_t pf, |
0597f2680
|
355 356 357 358 |
unsigned int hooknum, const struct net_device *indev, const struct net_device *outdev, const struct nf_loginfo *li, |
d7a5c3244
|
359 |
const char *prefix, unsigned int plen) |
0597f2680
|
360 |
{ |
0597f2680
|
361 362 363 |
struct nfulnl_msg_packet_hdr pmsg; struct nlmsghdr *nlh; struct nfgenmsg *nfmsg; |
98a4a8612
|
364 |
__be32 tmp_uint; |
27a884dc3
|
365 |
sk_buff_data_t old_tail = inst->skb->tail; |
0597f2680
|
366 |
|
601e68e10
|
367 |
nlh = NLMSG_PUT(inst->skb, 0, 0, |
0597f2680
|
368 369 370 371 372 373 |
NFNL_SUBSYS_ULOG << 8 | NFULNL_MSG_PACKET, sizeof(struct nfgenmsg)); nfmsg = NLMSG_DATA(nlh); nfmsg->nfgen_family = pf; nfmsg->version = NFNETLINK_V0; nfmsg->res_id = htons(inst->group_num); |
febf0a431
|
374 |
pmsg.hw_protocol = skb->protocol; |
0597f2680
|
375 |
pmsg.hook = hooknum; |
df6fb868d
|
376 |
NLA_PUT(inst->skb, NFULA_PACKET_HDR, sizeof(pmsg), &pmsg); |
0597f2680
|
377 |
|
d7a5c3244
|
378 |
if (prefix) |
df6fb868d
|
379 |
NLA_PUT(inst->skb, NFULA_PREFIX, plen, prefix); |
0597f2680
|
380 381 |
if (indev) { |
fbcd923c3
|
382 |
#ifndef CONFIG_BRIDGE_NETFILTER |
0dfedd287
|
383 384 |
NLA_PUT_BE32(inst->skb, NFULA_IFINDEX_INDEV, htonl(indev->ifindex)); |
fbcd923c3
|
385 386 387 388 389 |
#else if (pf == PF_BRIDGE) { /* Case 1: outdev is physical input device, we need to * look for bridge group (when called from * netfilter_bridge) */ |
0dfedd287
|
390 391 |
NLA_PUT_BE32(inst->skb, NFULA_IFINDEX_PHYSINDEV, htonl(indev->ifindex)); |
fbcd923c3
|
392 |
/* this is the bridge group "brX" */ |
f350a0a87
|
393 |
/* rcu_read_lock()ed by nf_hook_slow or nf_log_packet */ |
0dfedd287
|
394 |
NLA_PUT_BE32(inst->skb, NFULA_IFINDEX_INDEV, |
f350a0a87
|
395 |
htonl(br_port_get_rcu(indev)->br->dev->ifindex)); |
fbcd923c3
|
396 397 398 |
} else { /* Case 2: indev is bridge group, we need to look for * physical device (when called from ipv4) */ |
0dfedd287
|
399 400 401 402 403 |
NLA_PUT_BE32(inst->skb, NFULA_IFINDEX_INDEV, htonl(indev->ifindex)); if (skb->nf_bridge && skb->nf_bridge->physindev) NLA_PUT_BE32(inst->skb, NFULA_IFINDEX_PHYSINDEV, htonl(skb->nf_bridge->physindev->ifindex)); |
fbcd923c3
|
404 405 |
} #endif |
0597f2680
|
406 407 408 409 |
} if (outdev) { tmp_uint = htonl(outdev->ifindex); |
fbcd923c3
|
410 |
#ifndef CONFIG_BRIDGE_NETFILTER |
0dfedd287
|
411 412 |
NLA_PUT_BE32(inst->skb, NFULA_IFINDEX_OUTDEV, htonl(outdev->ifindex)); |
fbcd923c3
|
413 414 415 416 417 |
#else if (pf == PF_BRIDGE) { /* Case 1: outdev is physical output device, we need to * look for bridge group (when called from * netfilter_bridge) */ |
0dfedd287
|
418 419 |
NLA_PUT_BE32(inst->skb, NFULA_IFINDEX_PHYSOUTDEV, htonl(outdev->ifindex)); |
fbcd923c3
|
420 |
/* this is the bridge group "brX" */ |
f350a0a87
|
421 |
/* rcu_read_lock()ed by nf_hook_slow or nf_log_packet */ |
0dfedd287
|
422 |
NLA_PUT_BE32(inst->skb, NFULA_IFINDEX_OUTDEV, |
f350a0a87
|
423 |
htonl(br_port_get_rcu(outdev)->br->dev->ifindex)); |
fbcd923c3
|
424 425 426 |
} else { /* Case 2: indev is a bridge group, we need to look * for physical device (when called from ipv4) */ |
0dfedd287
|
427 428 429 430 431 |
NLA_PUT_BE32(inst->skb, NFULA_IFINDEX_OUTDEV, htonl(outdev->ifindex)); if (skb->nf_bridge && skb->nf_bridge->physoutdev) NLA_PUT_BE32(inst->skb, NFULA_IFINDEX_PHYSOUTDEV, htonl(skb->nf_bridge->physoutdev->ifindex)); |
fbcd923c3
|
432 433 |
} #endif |
0597f2680
|
434 |
} |
0dfedd287
|
435 436 |
if (skb->mark) NLA_PUT_BE32(inst->skb, NFULA_MARK, htonl(skb->mark)); |
0597f2680
|
437 |
|
b95cce357
|
438 |
if (indev && skb->dev) { |
0597f2680
|
439 |
struct nfulnl_msg_packet_hw phw; |
b95cce357
|
440 441 442 |
int len = dev_parse_header(skb, phw.hw_addr); if (len > 0) { phw.hw_addrlen = htons(len); |
df6fb868d
|
443 |
NLA_PUT(inst->skb, NFULA_HWADDR, sizeof(phw), &phw); |
b95cce357
|
444 |
} |
0597f2680
|
445 |
} |
72961ecf8
|
446 447 448 449 450 451 452 |
if (indev && skb_mac_header_was_set(skb)) { NLA_PUT_BE16(inst->skb, NFULA_HWTYPE, htons(skb->dev->type)); NLA_PUT_BE16(inst->skb, NFULA_HWLEN, htons(skb->dev->hard_header_len)); NLA_PUT(inst->skb, NFULA_HWHEADER, skb->dev->hard_header_len, skb_mac_header(skb)); } |
b7aa0bf70
|
453 |
if (skb->tstamp.tv64) { |
0597f2680
|
454 |
struct nfulnl_msg_packet_timestamp ts; |
b7aa0bf70
|
455 456 457 |
struct timeval tv = ktime_to_timeval(skb->tstamp); ts.sec = cpu_to_be64(tv.tv_sec); ts.usec = cpu_to_be64(tv.tv_usec); |
0597f2680
|
458 |
|
df6fb868d
|
459 |
NLA_PUT(inst->skb, NFULA_TIMESTAMP, sizeof(ts), &ts); |
0597f2680
|
460 461 462 463 464 465 |
} /* UID */ if (skb->sk) { read_lock_bh(&skb->sk->sk_callback_lock); if (skb->sk->sk_socket && skb->sk->sk_socket->file) { |
d76b0d9b2
|
466 467 468 |
struct file *file = skb->sk->sk_socket->file; __be32 uid = htonl(file->f_cred->fsuid); __be32 gid = htonl(file->f_cred->fsgid); |
df6fb868d
|
469 |
/* need to unlock here since NLA_PUT may goto */ |
0597f2680
|
470 |
read_unlock_bh(&skb->sk->sk_callback_lock); |
0dfedd287
|
471 |
NLA_PUT_BE32(inst->skb, NFULA_UID, uid); |
76aa1ce13
|
472 |
NLA_PUT_BE32(inst->skb, NFULA_GID, gid); |
0597f2680
|
473 474 475 |
} else read_unlock_bh(&skb->sk->sk_callback_lock); } |
0af5f6c1e
|
476 |
/* local sequence number */ |
0dfedd287
|
477 478 |
if (inst->flags & NFULNL_CFG_F_SEQ) NLA_PUT_BE32(inst->skb, NFULA_SEQ, htonl(inst->seq++)); |
0af5f6c1e
|
479 |
/* global sequence number */ |
0dfedd287
|
480 481 482 |
if (inst->flags & NFULNL_CFG_F_SEQ_GLOBAL) NLA_PUT_BE32(inst->skb, NFULA_SEQ_GLOBAL, htonl(atomic_inc_return(&global_seq))); |
0af5f6c1e
|
483 |
|
0597f2680
|
484 |
if (data_len) { |
df6fb868d
|
485 486 |
struct nlattr *nla; int size = nla_attr_size(data_len); |
0597f2680
|
487 |
|
df6fb868d
|
488 |
if (skb_tailroom(inst->skb) < nla_total_size(data_len)) { |
0597f2680
|
489 490 491 492 |
printk(KERN_WARNING "nfnetlink_log: no tailroom! "); goto nlmsg_failure; } |
df6fb868d
|
493 494 495 |
nla = (struct nlattr *)skb_put(inst->skb, nla_total_size(data_len)); nla->nla_type = NFULA_PAYLOAD; nla->nla_len = size; |
0597f2680
|
496 |
|
df6fb868d
|
497 |
if (skb_copy_bits(skb, 0, nla_data(nla), data_len)) |
0597f2680
|
498 499 |
BUG(); } |
601e68e10
|
500 |
|
0597f2680
|
501 502 503 504 |
nlh->nlmsg_len = inst->skb->tail - old_tail; return 0; nlmsg_failure: |
df6fb868d
|
505 |
nla_put_failure: |
0597f2680
|
506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 |
PRINTR(KERN_ERR "nfnetlink_log: error creating log nlmsg "); return -1; } #define RCV_SKB_FAIL(err) do { netlink_ack(skb, nlh, (err)); return; } while (0) static struct nf_loginfo default_loginfo = { .type = NF_LOG_TYPE_ULOG, .u = { .ulog = { .copy_len = 0xffff, .group = 0, .qthreshold = 1, }, }, }; /* log handler for internal netfilter logging api */ |
5f7340eff
|
525 |
void |
76108cea0
|
526 |
nfulnl_log_packet(u_int8_t pf, |
0597f2680
|
527 528 529 530 531 532 533 534 535 536 537 |
unsigned int hooknum, const struct sk_buff *skb, const struct net_device *in, const struct net_device *out, const struct nf_loginfo *li_user, const char *prefix) { unsigned int size, data_len; struct nfulnl_instance *inst; const struct nf_loginfo *li; unsigned int qthreshold; |
d7a5c3244
|
538 |
unsigned int plen; |
0597f2680
|
539 |
|
601e68e10
|
540 |
if (li_user && li_user->type == NF_LOG_TYPE_ULOG) |
0597f2680
|
541 542 543 544 545 546 |
li = li_user; else li = &default_loginfo; inst = instance_lookup_get(li->u.ulog.group); if (!inst) |
0597f2680
|
547 |
return; |
0597f2680
|
548 |
|
d7a5c3244
|
549 550 |
plen = 0; if (prefix) |
881dbfe8a
|
551 |
plen = strlen(prefix) + 1; |
d7a5c3244
|
552 |
|
0597f2680
|
553 554 555 |
/* FIXME: do we want to make the size calculation conditional based on * what is actually present? way more branches and checks, but more * memory efficient... */ |
7000d38d6
|
556 |
size = NLMSG_SPACE(sizeof(struct nfgenmsg)) |
df6fb868d
|
557 558 559 |
+ nla_total_size(sizeof(struct nfulnl_msg_packet_hdr)) + nla_total_size(sizeof(u_int32_t)) /* ifindex */ + nla_total_size(sizeof(u_int32_t)) /* ifindex */ |
fbcd923c3
|
560 |
#ifdef CONFIG_BRIDGE_NETFILTER |
df6fb868d
|
561 562 |
+ nla_total_size(sizeof(u_int32_t)) /* ifindex */ + nla_total_size(sizeof(u_int32_t)) /* ifindex */ |
fbcd923c3
|
563 |
#endif |
df6fb868d
|
564 565 |
+ nla_total_size(sizeof(u_int32_t)) /* mark */ + nla_total_size(sizeof(u_int32_t)) /* uid */ |
76aa1ce13
|
566 |
+ nla_total_size(sizeof(u_int32_t)) /* gid */ |
df6fb868d
|
567 568 569 |
+ nla_total_size(plen) /* prefix */ + nla_total_size(sizeof(struct nfulnl_msg_packet_hw)) + nla_total_size(sizeof(struct nfulnl_msg_packet_timestamp)); |
0597f2680
|
570 |
|
eeff9beec
|
571 572 573 574 575 |
if (in && skb_mac_header_was_set(skb)) { size += nla_total_size(skb->dev->hard_header_len) + nla_total_size(sizeof(u_int16_t)) /* hwtype */ + nla_total_size(sizeof(u_int16_t)); /* hwlen */ } |
0597f2680
|
576 |
spin_lock_bh(&inst->lock); |
0af5f6c1e
|
577 |
if (inst->flags & NFULNL_CFG_F_SEQ) |
df6fb868d
|
578 |
size += nla_total_size(sizeof(u_int32_t)); |
0af5f6c1e
|
579 |
if (inst->flags & NFULNL_CFG_F_SEQ_GLOBAL) |
df6fb868d
|
580 |
size += nla_total_size(sizeof(u_int32_t)); |
0af5f6c1e
|
581 |
|
0597f2680
|
582 583 |
qthreshold = inst->qthreshold; /* per-rule qthreshold overrides per-instance */ |
5ca431f9a
|
584 585 586 |
if (li->u.ulog.qthreshold) if (qthreshold > li->u.ulog.qthreshold) qthreshold = li->u.ulog.qthreshold; |
601e68e10
|
587 |
|
0597f2680
|
588 589 590 591 592 |
switch (inst->copy_mode) { case NFULNL_COPY_META: case NFULNL_COPY_NONE: data_len = 0; break; |
601e68e10
|
593 |
|
0597f2680
|
594 |
case NFULNL_COPY_PACKET: |
601e68e10
|
595 |
if (inst->copy_range == 0 |
0597f2680
|
596 597 598 599 |
|| inst->copy_range > skb->len) data_len = skb->len; else data_len = inst->copy_range; |
601e68e10
|
600 |
|
df6fb868d
|
601 |
size += nla_total_size(data_len); |
0597f2680
|
602 |
break; |
601e68e10
|
603 |
|
f5c5440d4
|
604 |
case NFULNL_COPY_DISABLED: |
0597f2680
|
605 |
default: |
55b5a91e1
|
606 |
goto unlock_and_release; |
0597f2680
|
607 |
} |
d63b043d9
|
608 609 |
if (inst->skb && size > skb_tailroom(inst->skb) - sizeof(struct nfgenmsg)) { |
0597f2680
|
610 611 |
/* either the queue len is too high or we don't have * enough room in the skb left. flush to userspace. */ |
e35670614
|
612 |
__nfulnl_flush(inst); |
55b5a91e1
|
613 |
} |
0597f2680
|
614 |
|
55b5a91e1
|
615 616 617 |
if (!inst->skb) { inst->skb = nfulnl_alloc_skb(inst->nlbufsiz, size); if (!inst->skb) |
0597f2680
|
618 |
goto alloc_failure; |
0597f2680
|
619 |
} |
0597f2680
|
620 621 622 |
inst->qlen++; __build_packet_message(inst, skb, data_len, pf, |
d7a5c3244
|
623 |
hooknum, in, out, li, prefix, plen); |
0597f2680
|
624 |
|
d63b043d9
|
625 626 |
if (inst->qlen >= qthreshold) __nfulnl_flush(inst); |
0597f2680
|
627 628 |
/* timer_pending always called within inst->lock, so there * is no chance of a race here */ |
d63b043d9
|
629 |
else if (!timer_pending(&inst->timer)) { |
0597f2680
|
630 631 632 633 |
instance_get(inst); inst->timer.expires = jiffies + (inst->flushtimeout*HZ/100); add_timer(&inst->timer); } |
0597f2680
|
634 |
|
ed32abeaf
|
635 636 637 |
unlock_and_release: spin_unlock_bh(&inst->lock); instance_put(inst); |
0597f2680
|
638 639 640 |
return; alloc_failure: |
0597f2680
|
641 |
/* FIXME: statistics */ |
ed32abeaf
|
642 |
goto unlock_and_release; |
0597f2680
|
643 |
} |
5f7340eff
|
644 |
EXPORT_SYMBOL_GPL(nfulnl_log_packet); |
0597f2680
|
645 646 647 648 649 650 |
static int nfulnl_rcv_nl_event(struct notifier_block *this, unsigned long event, void *ptr) { struct netlink_notify *n = ptr; |
dee5817e8
|
651 |
if (event == NETLINK_URELEASE && n->protocol == NETLINK_NETFILTER) { |
0597f2680
|
652 653 654 |
int i; /* destroy all instances for this pid */ |
bed1be208
|
655 |
spin_lock_bh(&instances_lock); |
0597f2680
|
656 657 658 659 660 661 |
for (i = 0; i < INSTANCE_BUCKETS; i++) { struct hlist_node *tmp, *t2; struct nfulnl_instance *inst; struct hlist_head *head = &instance_table[i]; hlist_for_each_entry_safe(inst, tmp, t2, head, hlist) { |
09ad9bc75
|
662 |
if ((net_eq(n->net, &init_net)) && |
b4b510290
|
663 |
(n->pid == inst->peer_pid)) |
0597f2680
|
664 665 666 |
__instance_destroy(inst); } } |
bed1be208
|
667 |
spin_unlock_bh(&instances_lock); |
0597f2680
|
668 669 670 671 672 673 674 675 676 677 |
} return NOTIFY_DONE; } static struct notifier_block nfulnl_rtnl_notifier = { .notifier_call = nfulnl_rcv_nl_event, }; static int nfulnl_recv_unsupp(struct sock *ctnl, struct sk_buff *skb, |
399383246
|
678 679 |
const struct nlmsghdr *nlh, const struct nlattr * const nfqa[]) |
0597f2680
|
680 681 682 |
{ return -ENOTSUPP; } |
ca735b3aa
|
683 |
static struct nf_logger nfulnl_logger __read_mostly = { |
0597f2680
|
684 685 686 687 |
.name = "nfnetlink_log", .logfn = &nfulnl_log_packet, .me = THIS_MODULE, }; |
fd8281ada
|
688 689 690 691 692 693 694 |
static const struct nla_policy nfula_cfg_policy[NFULA_CFG_MAX+1] = { [NFULA_CFG_CMD] = { .len = sizeof(struct nfulnl_msg_config_cmd) }, [NFULA_CFG_MODE] = { .len = sizeof(struct nfulnl_msg_config_mode) }, [NFULA_CFG_TIMEOUT] = { .type = NLA_U32 }, [NFULA_CFG_QTHRESH] = { .type = NLA_U32 }, [NFULA_CFG_NLBUFSIZ] = { .type = NLA_U32 }, [NFULA_CFG_FLAGS] = { .type = NLA_U16 }, |
0597f2680
|
695 696 697 698 |
}; static int nfulnl_recv_config(struct sock *ctnl, struct sk_buff *skb, |
399383246
|
699 700 |
const struct nlmsghdr *nlh, const struct nlattr * const nfula[]) |
0597f2680
|
701 702 703 704 |
{ struct nfgenmsg *nfmsg = NLMSG_DATA(nlh); u_int16_t group_num = ntohs(nfmsg->res_id); struct nfulnl_instance *inst; |
b7047a1c8
|
705 |
struct nfulnl_msg_config_cmd *cmd = NULL; |
0597f2680
|
706 |
int ret = 0; |
b7047a1c8
|
707 708 709 710 711 712 713 |
if (nfula[NFULA_CFG_CMD]) { u_int8_t pf = nfmsg->nfgen_family; cmd = nla_data(nfula[NFULA_CFG_CMD]); /* Commands without queue context */ switch (cmd->command) { case NFULNL_CFG_CMD_PF_BIND: |
ca735b3aa
|
714 |
return nf_log_bind_pf(pf, &nfulnl_logger); |
b7047a1c8
|
715 |
case NFULNL_CFG_CMD_PF_UNBIND: |
ca735b3aa
|
716 |
nf_log_unbind_pf(pf); |
b7047a1c8
|
717 718 719 |
return 0; } } |
0597f2680
|
720 |
inst = instance_lookup_get(group_num); |
c0506365a
|
721 722 723 724 |
if (inst && inst->peer_pid != NETLINK_CB(skb).pid) { ret = -EPERM; goto out_put; } |
b7047a1c8
|
725 |
if (cmd != NULL) { |
0597f2680
|
726 727 728 729 730 731 732 733 734 |
switch (cmd->command) { case NFULNL_CFG_CMD_BIND: if (inst) { ret = -EBUSY; goto out_put; } inst = instance_create(group_num, NETLINK_CB(skb).pid); |
baab2ce7d
|
735 736 |
if (IS_ERR(inst)) { ret = PTR_ERR(inst); |
f414c16c0
|
737 |
goto out; |
0597f2680
|
738 739 740 741 742 |
} break; case NFULNL_CFG_CMD_UNBIND: if (!inst) { ret = -ENODEV; |
f414c16c0
|
743 |
goto out; |
0597f2680
|
744 |
} |
0597f2680
|
745 |
instance_destroy(inst); |
a49c65037
|
746 |
goto out_put; |
0597f2680
|
747 |
default: |
cd21f0ac4
|
748 |
ret = -ENOTSUPP; |
0597f2680
|
749 750 |
break; } |
0597f2680
|
751 |
} |
df6fb868d
|
752 |
if (nfula[NFULA_CFG_MODE]) { |
0597f2680
|
753 |
struct nfulnl_msg_config_mode *params; |
df6fb868d
|
754 |
params = nla_data(nfula[NFULA_CFG_MODE]); |
0597f2680
|
755 |
|
c0506365a
|
756 757 758 759 |
if (!inst) { ret = -ENODEV; goto out; } |
0597f2680
|
760 |
nfulnl_set_mode(inst, params->copy_mode, |
d1208b999
|
761 |
ntohl(params->copy_range)); |
0597f2680
|
762 |
} |
df6fb868d
|
763 |
if (nfula[NFULA_CFG_TIMEOUT]) { |
0dfedd287
|
764 |
__be32 timeout = nla_get_be32(nfula[NFULA_CFG_TIMEOUT]); |
0597f2680
|
765 |
|
c0506365a
|
766 767 768 769 |
if (!inst) { ret = -ENODEV; goto out; } |
0597f2680
|
770 771 |
nfulnl_set_timeout(inst, ntohl(timeout)); } |
df6fb868d
|
772 |
if (nfula[NFULA_CFG_NLBUFSIZ]) { |
0dfedd287
|
773 |
__be32 nlbufsiz = nla_get_be32(nfula[NFULA_CFG_NLBUFSIZ]); |
0597f2680
|
774 |
|
c0506365a
|
775 776 777 778 |
if (!inst) { ret = -ENODEV; goto out; } |
0597f2680
|
779 780 |
nfulnl_set_nlbufsiz(inst, ntohl(nlbufsiz)); } |
df6fb868d
|
781 |
if (nfula[NFULA_CFG_QTHRESH]) { |
0dfedd287
|
782 |
__be32 qthresh = nla_get_be32(nfula[NFULA_CFG_QTHRESH]); |
0597f2680
|
783 |
|
c0506365a
|
784 785 786 787 |
if (!inst) { ret = -ENODEV; goto out; } |
0597f2680
|
788 789 |
nfulnl_set_qthresh(inst, ntohl(qthresh)); } |
df6fb868d
|
790 |
if (nfula[NFULA_CFG_FLAGS]) { |
0dfedd287
|
791 |
__be16 flags = nla_get_be16(nfula[NFULA_CFG_FLAGS]); |
c0506365a
|
792 793 794 795 796 |
if (!inst) { ret = -ENODEV; goto out; } |
ee433530d
|
797 |
nfulnl_set_flags(inst, ntohs(flags)); |
0af5f6c1e
|
798 |
} |
0597f2680
|
799 800 |
out_put: instance_put(inst); |
dd16704eb
|
801 |
out: |
0597f2680
|
802 803 |
return ret; } |
7c8d4cb41
|
804 |
static const struct nfnl_callback nfulnl_cb[NFULNL_MSG_MAX] = { |
0597f2680
|
805 |
[NFULNL_MSG_PACKET] = { .call = nfulnl_recv_unsupp, |
37d2e7a20
|
806 |
.attr_count = NFULA_MAX, }, |
0597f2680
|
807 |
[NFULNL_MSG_CONFIG] = { .call = nfulnl_recv_config, |
fd8281ada
|
808 809 |
.attr_count = NFULA_CFG_MAX, .policy = nfula_cfg_policy }, |
0597f2680
|
810 |
}; |
7c8d4cb41
|
811 |
static const struct nfnetlink_subsystem nfulnl_subsys = { |
0597f2680
|
812 813 814 |
.name = "log", .subsys_id = NFNL_SUBSYS_ULOG, .cb_count = NFULNL_MSG_MAX, |
0597f2680
|
815 816 817 818 819 820 821 |
.cb = nfulnl_cb, }; #ifdef CONFIG_PROC_FS struct iter_state { unsigned int bucket; }; |
f76cdcee5
|
822 |
static struct hlist_node *get_first(struct iter_state *st) |
0597f2680
|
823 |
{ |
0597f2680
|
824 825 826 827 828 |
if (!st) return NULL; for (st->bucket = 0; st->bucket < INSTANCE_BUCKETS; st->bucket++) { if (!hlist_empty(&instance_table[st->bucket])) |
bed1be208
|
829 |
return rcu_dereference_bh(instance_table[st->bucket].first); |
0597f2680
|
830 831 832 |
} return NULL; } |
f76cdcee5
|
833 |
static struct hlist_node *get_next(struct iter_state *st, struct hlist_node *h) |
0597f2680
|
834 |
{ |
bed1be208
|
835 |
h = rcu_dereference_bh(h->next); |
0597f2680
|
836 837 838 |
while (!h) { if (++st->bucket >= INSTANCE_BUCKETS) return NULL; |
bed1be208
|
839 |
h = rcu_dereference_bh(instance_table[st->bucket].first); |
0597f2680
|
840 841 842 |
} return h; } |
f76cdcee5
|
843 |
static struct hlist_node *get_idx(struct iter_state *st, loff_t pos) |
0597f2680
|
844 845 |
{ struct hlist_node *head; |
f76cdcee5
|
846 |
head = get_first(st); |
0597f2680
|
847 848 |
if (head) |
f76cdcee5
|
849 |
while (pos && (head = get_next(st, head))) |
0597f2680
|
850 851 852 853 854 |
pos--; return pos ? NULL : head; } static void *seq_start(struct seq_file *seq, loff_t *pos) |
bed1be208
|
855 |
__acquires(rcu_bh) |
0597f2680
|
856 |
{ |
bed1be208
|
857 |
rcu_read_lock_bh(); |
f76cdcee5
|
858 |
return get_idx(seq->private, *pos); |
0597f2680
|
859 860 861 862 863 |
} static void *seq_next(struct seq_file *s, void *v, loff_t *pos) { (*pos)++; |
f76cdcee5
|
864 |
return get_next(s->private, v); |
0597f2680
|
865 866 867 |
} static void seq_stop(struct seq_file *s, void *v) |
bed1be208
|
868 |
__releases(rcu_bh) |
0597f2680
|
869 |
{ |
bed1be208
|
870 |
rcu_read_unlock_bh(); |
0597f2680
|
871 872 873 874 875 |
} static int seq_show(struct seq_file *s, void *v) { const struct nfulnl_instance *inst = v; |
601e68e10
|
876 877 |
return seq_printf(s, "%5d %6d %5d %1d %5d %6d %2d ", |
0597f2680
|
878 |
inst->group_num, |
601e68e10
|
879 |
inst->peer_pid, inst->qlen, |
0597f2680
|
880 881 882 |
inst->copy_mode, inst->copy_range, inst->flushtimeout, atomic_read(&inst->use)); } |
56b3d975b
|
883 |
static const struct seq_operations nful_seq_ops = { |
0597f2680
|
884 885 886 887 888 889 890 891 |
.start = seq_start, .next = seq_next, .stop = seq_stop, .show = seq_show, }; static int nful_open(struct inode *inode, struct file *file) { |
e2da59133
|
892 893 |
return seq_open_private(file, &nful_seq_ops, sizeof(struct iter_state)); |
0597f2680
|
894 |
} |
da7071d7e
|
895 |
static const struct file_operations nful_file_ops = { |
0597f2680
|
896 897 898 899 900 901 902 903 |
.owner = THIS_MODULE, .open = nful_open, .read = seq_read, .llseek = seq_lseek, .release = seq_release_private, }; #endif /* PROC_FS */ |
32292a7ff
|
904 |
static int __init nfnetlink_log_init(void) |
0597f2680
|
905 906 |
{ int i, status = -ENOMEM; |
601e68e10
|
907 |
|
0597f2680
|
908 909 |
for (i = 0; i < INSTANCE_BUCKETS; i++) INIT_HLIST_HEAD(&instance_table[i]); |
601e68e10
|
910 |
|
0597f2680
|
911 912 913 914 915 916 917 918 919 920 921 922 |
/* it's not really all that important to have a random value, so * we can do this from the init function, even if there hasn't * been that much entropy yet */ get_random_bytes(&hash_init, sizeof(hash_init)); netlink_register_notifier(&nfulnl_rtnl_notifier); status = nfnetlink_subsys_register(&nfulnl_subsys); if (status < 0) { printk(KERN_ERR "log: failed to create netlink socket "); goto cleanup_netlink_notifier; } |
ca735b3aa
|
923 924 925 926 927 928 |
status = nf_log_register(NFPROTO_UNSPEC, &nfulnl_logger); if (status < 0) { printk(KERN_ERR "log: failed to register logger "); goto cleanup_subsys; } |
0597f2680
|
929 |
#ifdef CONFIG_PROC_FS |
8eeee8b15
|
930 931 |
if (!proc_create("nfnetlink_log", 0440, proc_net_netfilter, &nful_file_ops)) |
ca735b3aa
|
932 |
goto cleanup_logger; |
0597f2680
|
933 |
#endif |
0597f2680
|
934 |
return status; |
0597f2680
|
935 |
#ifdef CONFIG_PROC_FS |
ca735b3aa
|
936 937 938 |
cleanup_logger: nf_log_unregister(&nfulnl_logger); #endif |
0597f2680
|
939 |
cleanup_subsys: |
0597f2680
|
940 941 942 943 944 |
nfnetlink_subsys_unregister(&nfulnl_subsys); cleanup_netlink_notifier: netlink_unregister_notifier(&nfulnl_rtnl_notifier); return status; } |
65b4b4e81
|
945 |
static void __exit nfnetlink_log_fini(void) |
0597f2680
|
946 |
{ |
e92ad99c7
|
947 |
nf_log_unregister(&nfulnl_logger); |
32292a7ff
|
948 949 950 951 952 |
#ifdef CONFIG_PROC_FS remove_proc_entry("nfnetlink_log", proc_net_netfilter); #endif nfnetlink_subsys_unregister(&nfulnl_subsys); netlink_unregister_notifier(&nfulnl_rtnl_notifier); |
0597f2680
|
953 954 955 956 957 |
} MODULE_DESCRIPTION("netfilter userspace logging"); MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>"); MODULE_LICENSE("GPL"); |
f682faefb
|
958 |
MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_ULOG); |
0597f2680
|
959 |
|
65b4b4e81
|
960 961 |
module_init(nfnetlink_log_init); module_exit(nfnetlink_log_fini); |