diff options
author | Daniel Borkmann <danborkmann@googlemail.com> | 2010-01-12 09:27:30 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-01-13 23:38:26 -0500 |
commit | 508e14b4a4fb1a824a14f2c5b8d7df67b313f8e4 (patch) | |
tree | 4ae5de122ebb70f2395739c85cba86bb9df8f712 /net/core/netpoll.c | |
parent | e1d5a010729a7a495a8a8de85727f3f0d62f06a0 (diff) |
netpoll: allow execution of multiple rx_hooks per interface
Signed-off-by: Daniel Borkmann <danborkmann@googlemail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/core/netpoll.c')
-rw-r--r-- | net/core/netpoll.c | 169 |
1 files changed, 106 insertions, 63 deletions
diff --git a/net/core/netpoll.c b/net/core/netpoll.c index 0b4d0d35ef40..7aa697253765 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c | |||
@@ -407,11 +407,24 @@ static void arp_reply(struct sk_buff *skb) | |||
407 | __be32 sip, tip; | 407 | __be32 sip, tip; |
408 | unsigned char *sha; | 408 | unsigned char *sha; |
409 | struct sk_buff *send_skb; | 409 | struct sk_buff *send_skb; |
410 | struct netpoll *np = NULL; | 410 | struct netpoll *np, *tmp; |
411 | unsigned long flags; | ||
412 | int hits = 0; | ||
413 | |||
414 | if (list_empty(&npinfo->rx_np)) | ||
415 | return; | ||
416 | |||
417 | /* Before checking the packet, we do some early | ||
418 | inspection whether this is interesting at all */ | ||
419 | spin_lock_irqsave(&npinfo->rx_lock, flags); | ||
420 | list_for_each_entry_safe(np, tmp, &npinfo->rx_np, rx) { | ||
421 | if (np->dev == skb->dev) | ||
422 | hits++; | ||
423 | } | ||
424 | spin_unlock_irqrestore(&npinfo->rx_lock, flags); | ||
411 | 425 | ||
412 | if (npinfo->rx_np && npinfo->rx_np->dev == skb->dev) | 426 | /* No netpoll struct is using this dev */ |
413 | np = npinfo->rx_np; | 427 | if (!hits) |
414 | if (!np) | ||
415 | return; | 428 | return; |
416 | 429 | ||
417 | /* No arp on this interface */ | 430 | /* No arp on this interface */ |
@@ -437,77 +450,91 @@ static void arp_reply(struct sk_buff *skb) | |||
437 | arp_ptr += skb->dev->addr_len; | 450 | arp_ptr += skb->dev->addr_len; |
438 | memcpy(&sip, arp_ptr, 4); | 451 | memcpy(&sip, arp_ptr, 4); |
439 | arp_ptr += 4; | 452 | arp_ptr += 4; |
440 | /* if we actually cared about dst hw addr, it would get copied here */ | 453 | /* If we actually cared about dst hw addr, |
454 | it would get copied here */ | ||
441 | arp_ptr += skb->dev->addr_len; | 455 | arp_ptr += skb->dev->addr_len; |
442 | memcpy(&tip, arp_ptr, 4); | 456 | memcpy(&tip, arp_ptr, 4); |
443 | 457 | ||
444 | /* Should we ignore arp? */ | 458 | /* Should we ignore arp? */ |
445 | if (tip != np->local_ip || | 459 | if (ipv4_is_loopback(tip) || ipv4_is_multicast(tip)) |
446 | ipv4_is_loopback(tip) || ipv4_is_multicast(tip)) | ||
447 | return; | 460 | return; |
448 | 461 | ||
449 | size = arp_hdr_len(skb->dev); | 462 | size = arp_hdr_len(skb->dev); |
450 | send_skb = find_skb(np, size + LL_ALLOCATED_SPACE(np->dev), | ||
451 | LL_RESERVED_SPACE(np->dev)); | ||
452 | 463 | ||
453 | if (!send_skb) | 464 | spin_lock_irqsave(&npinfo->rx_lock, flags); |
454 | return; | 465 | list_for_each_entry_safe(np, tmp, &npinfo->rx_np, rx) { |
455 | 466 | if (tip != np->local_ip) | |
456 | skb_reset_network_header(send_skb); | 467 | continue; |
457 | arp = (struct arphdr *) skb_put(send_skb, size); | ||
458 | send_skb->dev = skb->dev; | ||
459 | send_skb->protocol = htons(ETH_P_ARP); | ||
460 | 468 | ||
461 | /* Fill the device header for the ARP frame */ | 469 | send_skb = find_skb(np, size + LL_ALLOCATED_SPACE(np->dev), |
462 | if (dev_hard_header(send_skb, skb->dev, ptype, | 470 | LL_RESERVED_SPACE(np->dev)); |
463 | sha, np->dev->dev_addr, | 471 | if (!send_skb) |
464 | send_skb->len) < 0) { | 472 | continue; |
465 | kfree_skb(send_skb); | ||
466 | return; | ||
467 | } | ||
468 | 473 | ||
469 | /* | 474 | skb_reset_network_header(send_skb); |
470 | * Fill out the arp protocol part. | 475 | arp = (struct arphdr *) skb_put(send_skb, size); |
471 | * | 476 | send_skb->dev = skb->dev; |
472 | * we only support ethernet device type, | 477 | send_skb->protocol = htons(ETH_P_ARP); |
473 | * which (according to RFC 1390) should always equal 1 (Ethernet). | ||
474 | */ | ||
475 | 478 | ||
476 | arp->ar_hrd = htons(np->dev->type); | 479 | /* Fill the device header for the ARP frame */ |
477 | arp->ar_pro = htons(ETH_P_IP); | 480 | if (dev_hard_header(send_skb, skb->dev, ptype, |
478 | arp->ar_hln = np->dev->addr_len; | 481 | sha, np->dev->dev_addr, |
479 | arp->ar_pln = 4; | 482 | send_skb->len) < 0) { |
480 | arp->ar_op = htons(type); | 483 | kfree_skb(send_skb); |
484 | continue; | ||
485 | } | ||
481 | 486 | ||
482 | arp_ptr=(unsigned char *)(arp + 1); | 487 | /* |
483 | memcpy(arp_ptr, np->dev->dev_addr, np->dev->addr_len); | 488 | * Fill out the arp protocol part. |
484 | arp_ptr += np->dev->addr_len; | 489 | * |
485 | memcpy(arp_ptr, &tip, 4); | 490 | * we only support ethernet device type, |
486 | arp_ptr += 4; | 491 | * which (according to RFC 1390) should |
487 | memcpy(arp_ptr, sha, np->dev->addr_len); | 492 | * always equal 1 (Ethernet). |
488 | arp_ptr += np->dev->addr_len; | 493 | */ |
489 | memcpy(arp_ptr, &sip, 4); | ||
490 | 494 | ||
491 | netpoll_send_skb(np, send_skb); | 495 | arp->ar_hrd = htons(np->dev->type); |
496 | arp->ar_pro = htons(ETH_P_IP); | ||
497 | arp->ar_hln = np->dev->addr_len; | ||
498 | arp->ar_pln = 4; | ||
499 | arp->ar_op = htons(type); | ||
500 | |||
501 | arp_ptr = (unsigned char *)(arp + 1); | ||
502 | memcpy(arp_ptr, np->dev->dev_addr, np->dev->addr_len); | ||
503 | arp_ptr += np->dev->addr_len; | ||
504 | memcpy(arp_ptr, &tip, 4); | ||
505 | arp_ptr += 4; | ||
506 | memcpy(arp_ptr, sha, np->dev->addr_len); | ||
507 | arp_ptr += np->dev->addr_len; | ||
508 | memcpy(arp_ptr, &sip, 4); | ||
509 | |||
510 | netpoll_send_skb(np, send_skb); | ||
511 | |||
512 | /* If there are several rx_hooks for the same address, | ||
513 | we're fine by sending a single reply */ | ||
514 | break; | ||
515 | } | ||
516 | spin_unlock_irqrestore(&npinfo->rx_lock, flags); | ||
492 | } | 517 | } |
493 | 518 | ||
494 | int __netpoll_rx(struct sk_buff *skb) | 519 | int __netpoll_rx(struct sk_buff *skb) |
495 | { | 520 | { |
496 | int proto, len, ulen; | 521 | int proto, len, ulen; |
522 | int hits = 0; | ||
497 | struct iphdr *iph; | 523 | struct iphdr *iph; |
498 | struct udphdr *uh; | 524 | struct udphdr *uh; |
499 | struct netpoll_info *npi = skb->dev->npinfo; | 525 | struct netpoll_info *npinfo = skb->dev->npinfo; |
500 | struct netpoll *np = npi->rx_np; | 526 | struct netpoll *np, *tmp; |
501 | 527 | ||
502 | if (!np) | 528 | if (list_empty(&npinfo->rx_np)) |
503 | goto out; | 529 | goto out; |
530 | |||
504 | if (skb->dev->type != ARPHRD_ETHER) | 531 | if (skb->dev->type != ARPHRD_ETHER) |
505 | goto out; | 532 | goto out; |
506 | 533 | ||
507 | /* check if netpoll clients need ARP */ | 534 | /* check if netpoll clients need ARP */ |
508 | if (skb->protocol == htons(ETH_P_ARP) && | 535 | if (skb->protocol == htons(ETH_P_ARP) && |
509 | atomic_read(&trapped)) { | 536 | atomic_read(&trapped)) { |
510 | skb_queue_tail(&npi->arp_tx, skb); | 537 | skb_queue_tail(&npinfo->arp_tx, skb); |
511 | return 1; | 538 | return 1; |
512 | } | 539 | } |
513 | 540 | ||
@@ -551,16 +578,23 @@ int __netpoll_rx(struct sk_buff *skb) | |||
551 | goto out; | 578 | goto out; |
552 | if (checksum_udp(skb, uh, ulen, iph->saddr, iph->daddr)) | 579 | if (checksum_udp(skb, uh, ulen, iph->saddr, iph->daddr)) |
553 | goto out; | 580 | goto out; |
554 | if (np->local_ip && np->local_ip != iph->daddr) | ||
555 | goto out; | ||
556 | if (np->remote_ip && np->remote_ip != iph->saddr) | ||
557 | goto out; | ||
558 | if (np->local_port && np->local_port != ntohs(uh->dest)) | ||
559 | goto out; | ||
560 | 581 | ||
561 | np->rx_hook(np, ntohs(uh->source), | 582 | list_for_each_entry_safe(np, tmp, &npinfo->rx_np, rx) { |
562 | (char *)(uh+1), | 583 | if (np->local_ip && np->local_ip != iph->daddr) |
563 | ulen - sizeof(struct udphdr)); | 584 | continue; |
585 | if (np->remote_ip && np->remote_ip != iph->saddr) | ||
586 | continue; | ||
587 | if (np->local_port && np->local_port != ntohs(uh->dest)) | ||
588 | continue; | ||
589 | |||
590 | np->rx_hook(np, ntohs(uh->source), | ||
591 | (char *)(uh+1), | ||
592 | ulen - sizeof(struct udphdr)); | ||
593 | hits++; | ||
594 | } | ||
595 | |||
596 | if (!hits) | ||
597 | goto out; | ||
564 | 598 | ||
565 | kfree_skb(skb); | 599 | kfree_skb(skb); |
566 | return 1; | 600 | return 1; |
@@ -684,6 +718,7 @@ int netpoll_setup(struct netpoll *np) | |||
684 | struct net_device *ndev = NULL; | 718 | struct net_device *ndev = NULL; |
685 | struct in_device *in_dev; | 719 | struct in_device *in_dev; |
686 | struct netpoll_info *npinfo; | 720 | struct netpoll_info *npinfo; |
721 | struct netpoll *npe, *tmp; | ||
687 | unsigned long flags; | 722 | unsigned long flags; |
688 | int err; | 723 | int err; |
689 | 724 | ||
@@ -704,7 +739,7 @@ int netpoll_setup(struct netpoll *np) | |||
704 | } | 739 | } |
705 | 740 | ||
706 | npinfo->rx_flags = 0; | 741 | npinfo->rx_flags = 0; |
707 | npinfo->rx_np = NULL; | 742 | INIT_LIST_HEAD(&npinfo->rx_np); |
708 | 743 | ||
709 | spin_lock_init(&npinfo->rx_lock); | 744 | spin_lock_init(&npinfo->rx_lock); |
710 | skb_queue_head_init(&npinfo->arp_tx); | 745 | skb_queue_head_init(&npinfo->arp_tx); |
@@ -785,7 +820,7 @@ int netpoll_setup(struct netpoll *np) | |||
785 | if (np->rx_hook) { | 820 | if (np->rx_hook) { |
786 | spin_lock_irqsave(&npinfo->rx_lock, flags); | 821 | spin_lock_irqsave(&npinfo->rx_lock, flags); |
787 | npinfo->rx_flags |= NETPOLL_RX_ENABLED; | 822 | npinfo->rx_flags |= NETPOLL_RX_ENABLED; |
788 | npinfo->rx_np = np; | 823 | list_add_tail(&np->rx, &npinfo->rx_np); |
789 | spin_unlock_irqrestore(&npinfo->rx_lock, flags); | 824 | spin_unlock_irqrestore(&npinfo->rx_lock, flags); |
790 | } | 825 | } |
791 | 826 | ||
@@ -801,9 +836,16 @@ int netpoll_setup(struct netpoll *np) | |||
801 | return 0; | 836 | return 0; |
802 | 837 | ||
803 | release: | 838 | release: |
804 | if (!ndev->npinfo) | 839 | if (!ndev->npinfo) { |
840 | spin_lock_irqsave(&npinfo->rx_lock, flags); | ||
841 | list_for_each_entry_safe(npe, tmp, &npinfo->rx_np, rx) { | ||
842 | npe->dev = NULL; | ||
843 | } | ||
844 | spin_unlock_irqrestore(&npinfo->rx_lock, flags); | ||
845 | |||
805 | kfree(npinfo); | 846 | kfree(npinfo); |
806 | np->dev = NULL; | 847 | } |
848 | |||
807 | dev_put(ndev); | 849 | dev_put(ndev); |
808 | return err; | 850 | return err; |
809 | } | 851 | } |
@@ -823,10 +865,11 @@ void netpoll_cleanup(struct netpoll *np) | |||
823 | if (np->dev) { | 865 | if (np->dev) { |
824 | npinfo = np->dev->npinfo; | 866 | npinfo = np->dev->npinfo; |
825 | if (npinfo) { | 867 | if (npinfo) { |
826 | if (npinfo->rx_np == np) { | 868 | if (!list_empty(&npinfo->rx_np)) { |
827 | spin_lock_irqsave(&npinfo->rx_lock, flags); | 869 | spin_lock_irqsave(&npinfo->rx_lock, flags); |
828 | npinfo->rx_np = NULL; | 870 | list_del(&np->rx); |
829 | npinfo->rx_flags &= ~NETPOLL_RX_ENABLED; | 871 | if (list_empty(&npinfo->rx_np)) |
872 | npinfo->rx_flags &= ~NETPOLL_RX_ENABLED; | ||
830 | spin_unlock_irqrestore(&npinfo->rx_lock, flags); | 873 | spin_unlock_irqrestore(&npinfo->rx_lock, flags); |
831 | } | 874 | } |
832 | 875 | ||