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