diff options
| author | David S. Miller <davem@davemloft.net> | 2011-07-05 04:45:05 -0400 |
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2011-07-06 01:34:52 -0400 |
| commit | dc99f600698dcac69b8f56dda9a8a00d645c5ffc (patch) | |
| tree | 81599e4397761610d5020c03e2571eeceaa859b6 /net/packet | |
| parent | ce06b03e60fc19c680d1bf873e779bf11c2fc518 (diff) | |
packet: Add fanout support.
Fanouts allow packet capturing to be demuxed to a set of AF_PACKET
sockets. Two fanout policies are implemented:
1) Hashing based upon skb->rxhash
2) Pure round-robin
An AF_PACKET socket must be fully bound before it tries to add itself
to a fanout. All AF_PACKET sockets trying to join the same fanout
must all have the same bind settings.
Fanouts are identified (within a network namespace) by a 16-bit ID.
The first socket to try to add itself to a fanout with a particular
ID, creates that fanout. When the last socket leaves the fanout
(which happens only when the socket is closed), that fanout is
destroyed.
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/packet')
| -rw-r--r-- | net/packet/af_packet.c | 256 |
1 files changed, 251 insertions, 5 deletions
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index bb281bf330aa..3350f1d3c9aa 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c | |||
| @@ -187,9 +187,11 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg); | |||
| 187 | 187 | ||
| 188 | static void packet_flush_mclist(struct sock *sk); | 188 | static void packet_flush_mclist(struct sock *sk); |
| 189 | 189 | ||
| 190 | struct packet_fanout; | ||
| 190 | struct packet_sock { | 191 | struct packet_sock { |
| 191 | /* struct sock has to be the first member of packet_sock */ | 192 | /* struct sock has to be the first member of packet_sock */ |
| 192 | struct sock sk; | 193 | struct sock sk; |
| 194 | struct packet_fanout *fanout; | ||
| 193 | struct tpacket_stats stats; | 195 | struct tpacket_stats stats; |
| 194 | struct packet_ring_buffer rx_ring; | 196 | struct packet_ring_buffer rx_ring; |
| 195 | struct packet_ring_buffer tx_ring; | 197 | struct packet_ring_buffer tx_ring; |
| @@ -212,6 +214,24 @@ struct packet_sock { | |||
| 212 | struct packet_type prot_hook ____cacheline_aligned_in_smp; | 214 | struct packet_type prot_hook ____cacheline_aligned_in_smp; |
| 213 | }; | 215 | }; |
| 214 | 216 | ||
| 217 | #define PACKET_FANOUT_MAX 256 | ||
| 218 | |||
| 219 | struct packet_fanout { | ||
| 220 | #ifdef CONFIG_NET_NS | ||
| 221 | struct net *net; | ||
| 222 | #endif | ||
| 223 | unsigned int num_members; | ||
| 224 | u16 id; | ||
| 225 | u8 type; | ||
| 226 | u8 pad; | ||
| 227 | atomic_t rr_cur; | ||
| 228 | struct list_head list; | ||
| 229 | struct sock *arr[PACKET_FANOUT_MAX]; | ||
| 230 | spinlock_t lock; | ||
| 231 | atomic_t sk_ref; | ||
| 232 | struct packet_type prot_hook ____cacheline_aligned_in_smp; | ||
| 233 | }; | ||
| 234 | |||
| 215 | struct packet_skb_cb { | 235 | struct packet_skb_cb { |
| 216 | unsigned int origlen; | 236 | unsigned int origlen; |
| 217 | union { | 237 | union { |
| @@ -227,6 +247,9 @@ static inline struct packet_sock *pkt_sk(struct sock *sk) | |||
| 227 | return (struct packet_sock *)sk; | 247 | return (struct packet_sock *)sk; |
| 228 | } | 248 | } |
| 229 | 249 | ||
| 250 | static void __fanout_unlink(struct sock *sk, struct packet_sock *po); | ||
| 251 | static void __fanout_link(struct sock *sk, struct packet_sock *po); | ||
| 252 | |||
| 230 | /* register_prot_hook must be invoked with the po->bind_lock held, | 253 | /* register_prot_hook must be invoked with the po->bind_lock held, |
| 231 | * or from a context in which asynchronous accesses to the packet | 254 | * or from a context in which asynchronous accesses to the packet |
| 232 | * socket is not possible (packet_create()). | 255 | * socket is not possible (packet_create()). |
| @@ -235,7 +258,10 @@ static void register_prot_hook(struct sock *sk) | |||
| 235 | { | 258 | { |
| 236 | struct packet_sock *po = pkt_sk(sk); | 259 | struct packet_sock *po = pkt_sk(sk); |
| 237 | if (!po->running) { | 260 | if (!po->running) { |
| 238 | dev_add_pack(&po->prot_hook); | 261 | if (po->fanout) |
| 262 | __fanout_link(sk, po); | ||
| 263 | else | ||
| 264 | dev_add_pack(&po->prot_hook); | ||
| 239 | sock_hold(sk); | 265 | sock_hold(sk); |
| 240 | po->running = 1; | 266 | po->running = 1; |
| 241 | } | 267 | } |
| @@ -253,7 +279,10 @@ static void __unregister_prot_hook(struct sock *sk, bool sync) | |||
| 253 | struct packet_sock *po = pkt_sk(sk); | 279 | struct packet_sock *po = pkt_sk(sk); |
| 254 | 280 | ||
| 255 | po->running = 0; | 281 | po->running = 0; |
| 256 | __dev_remove_pack(&po->prot_hook); | 282 | if (po->fanout) |
| 283 | __fanout_unlink(sk, po); | ||
| 284 | else | ||
| 285 | __dev_remove_pack(&po->prot_hook); | ||
| 257 | __sock_put(sk); | 286 | __sock_put(sk); |
| 258 | 287 | ||
| 259 | if (sync) { | 288 | if (sync) { |
| @@ -388,6 +417,201 @@ static void packet_sock_destruct(struct sock *sk) | |||
| 388 | sk_refcnt_debug_dec(sk); | 417 | sk_refcnt_debug_dec(sk); |
| 389 | } | 418 | } |
| 390 | 419 | ||
| 420 | static int fanout_rr_next(struct packet_fanout *f, unsigned int num) | ||
| 421 | { | ||
| 422 | int x = atomic_read(&f->rr_cur) + 1; | ||
| 423 | |||
| 424 | if (x >= num) | ||
| 425 | x = 0; | ||
| 426 | |||
| 427 | return x; | ||
| 428 | } | ||
| 429 | |||
| 430 | static struct sock *fanout_demux_hash(struct packet_fanout *f, struct sk_buff *skb, unsigned int num) | ||
| 431 | { | ||
| 432 | u32 idx, hash = skb->rxhash; | ||
| 433 | |||
| 434 | idx = ((u64)hash * num) >> 32; | ||
| 435 | |||
| 436 | return f->arr[idx]; | ||
| 437 | } | ||
| 438 | |||
| 439 | static struct sock *fanout_demux_lb(struct packet_fanout *f, struct sk_buff *skb, unsigned int num) | ||
| 440 | { | ||
| 441 | int cur, old; | ||
| 442 | |||
| 443 | cur = atomic_read(&f->rr_cur); | ||
| 444 | while ((old = atomic_cmpxchg(&f->rr_cur, cur, | ||
| 445 | fanout_rr_next(f, num))) != cur) | ||
| 446 | cur = old; | ||
| 447 | return f->arr[cur]; | ||
| 448 | } | ||
| 449 | |||
| 450 | static int packet_rcv_fanout_hash(struct sk_buff *skb, struct net_device *dev, | ||
| 451 | struct packet_type *pt, struct net_device *orig_dev) | ||
| 452 | { | ||
| 453 | struct packet_fanout *f = pt->af_packet_priv; | ||
| 454 | unsigned int num = f->num_members; | ||
| 455 | struct packet_sock *po; | ||
| 456 | struct sock *sk; | ||
| 457 | |||
| 458 | if (!net_eq(dev_net(dev), read_pnet(&f->net)) || | ||
| 459 | !num) { | ||
| 460 | kfree_skb(skb); | ||
| 461 | return 0; | ||
| 462 | } | ||
| 463 | |||
| 464 | skb_get_rxhash(skb); | ||
| 465 | |||
| 466 | sk = fanout_demux_hash(f, skb, num); | ||
| 467 | po = pkt_sk(sk); | ||
| 468 | |||
| 469 | return po->prot_hook.func(skb, dev, &po->prot_hook, orig_dev); | ||
| 470 | } | ||
| 471 | |||
| 472 | static int packet_rcv_fanout_lb(struct sk_buff *skb, struct net_device *dev, | ||
| 473 | struct packet_type *pt, struct net_device *orig_dev) | ||
| 474 | { | ||
| 475 | struct packet_fanout *f = pt->af_packet_priv; | ||
| 476 | unsigned int num = f->num_members; | ||
| 477 | struct packet_sock *po; | ||
| 478 | struct sock *sk; | ||
| 479 | |||
| 480 | if (!net_eq(dev_net(dev), read_pnet(&f->net)) || | ||
| 481 | !num) { | ||
| 482 | kfree_skb(skb); | ||
| 483 | return 0; | ||
| 484 | } | ||
| 485 | |||
| 486 | sk = fanout_demux_lb(f, skb, num); | ||
| 487 | po = pkt_sk(sk); | ||
| 488 | |||
| 489 | return po->prot_hook.func(skb, dev, &po->prot_hook, orig_dev); | ||
| 490 | } | ||
| 491 | |||
| 492 | static DEFINE_MUTEX(fanout_mutex); | ||
| 493 | static LIST_HEAD(fanout_list); | ||
| 494 | |||
| 495 | static void __fanout_link(struct sock *sk, struct packet_sock *po) | ||
| 496 | { | ||
| 497 | struct packet_fanout *f = po->fanout; | ||
| 498 | |||
| 499 | spin_lock(&f->lock); | ||
| 500 | f->arr[f->num_members] = sk; | ||
| 501 | smp_wmb(); | ||
| 502 | f->num_members++; | ||
| 503 | spin_unlock(&f->lock); | ||
| 504 | } | ||
| 505 | |||
| 506 | static void __fanout_unlink(struct sock *sk, struct packet_sock *po) | ||
| 507 | { | ||
| 508 | struct packet_fanout *f = po->fanout; | ||
| 509 | int i; | ||
| 510 | |||
| 511 | spin_lock(&f->lock); | ||
| 512 | for (i = 0; i < f->num_members; i++) { | ||
| 513 | if (f->arr[i] == sk) | ||
| 514 | break; | ||
| 515 | } | ||
| 516 | BUG_ON(i >= f->num_members); | ||
| 517 | f->arr[i] = f->arr[f->num_members - 1]; | ||
| 518 | f->num_members--; | ||
| 519 | spin_unlock(&f->lock); | ||
| 520 | } | ||
| 521 | |||
| 522 | static int fanout_add(struct sock *sk, u16 id, u8 type) | ||
| 523 | { | ||
| 524 | struct packet_sock *po = pkt_sk(sk); | ||
| 525 | struct packet_fanout *f, *match; | ||
| 526 | int err; | ||
| 527 | |||
| 528 | switch | ||
