diff options
author | Linus Lüssing <linus.luessing@web.de> | 2011-03-14 18:43:37 -0400 |
---|---|---|
committer | Sven Eckelmann <sven@narfation.org> | 2011-04-17 15:11:01 -0400 |
commit | e1a5382f978b67b5cc36eec65e6046730ce07714 (patch) | |
tree | f7ca07cde3a49858d0cfa33e0189a659a1fcc95d /net/batman-adv/vis.c | |
parent | 57f0c07c4d0da8bcc23e21c330fe9c7c5cf776b5 (diff) |
batman-adv: Make orig_node->router an rcu protected pointer
The rcu protected macros rcu_dereference() and rcu_assign_pointer()
for the orig_node->router need to be used, as well as spin/rcu locking.
Otherwise we might end up using a router pointer pointing to already
freed memory.
Therefore this commit introduces the safe getter method
orig_node_get_router().
Signed-off-by: Linus Lüssing <linus.luessing@web.de>
Signed-off-by: Marek Lindner <lindner_marek@yahoo.de>
Signed-off-by: Sven Eckelmann <sven@narfation.org>
Diffstat (limited to 'net/batman-adv/vis.c')
-rw-r--r-- | net/batman-adv/vis.c | 91 |
1 files changed, 45 insertions, 46 deletions
diff --git a/net/batman-adv/vis.c b/net/batman-adv/vis.c index f90212f42082..d4cc4f5399f4 100644 --- a/net/batman-adv/vis.c +++ b/net/batman-adv/vis.c | |||
@@ -558,6 +558,7 @@ static int find_best_vis_server(struct bat_priv *bat_priv, | |||
558 | struct vis_info *info) | 558 | struct vis_info *info) |
559 | { | 559 | { |
560 | struct hashtable_t *hash = bat_priv->orig_hash; | 560 | struct hashtable_t *hash = bat_priv->orig_hash; |
561 | struct neigh_node *router; | ||
561 | struct hlist_node *node; | 562 | struct hlist_node *node; |
562 | struct hlist_head *head; | 563 | struct hlist_head *head; |
563 | struct orig_node *orig_node; | 564 | struct orig_node *orig_node; |
@@ -571,13 +572,17 @@ static int find_best_vis_server(struct bat_priv *bat_priv, | |||
571 | 572 | ||
572 | rcu_read_lock(); | 573 | rcu_read_lock(); |
573 | hlist_for_each_entry_rcu(orig_node, node, head, hash_entry) { | 574 | hlist_for_each_entry_rcu(orig_node, node, head, hash_entry) { |
574 | if ((orig_node) && (orig_node->router) && | 575 | router = orig_node_get_router(orig_node); |
575 | (orig_node->flags & VIS_SERVER) && | 576 | if (!router) |
576 | (orig_node->router->tq_avg > best_tq)) { | 577 | continue; |
577 | best_tq = orig_node->router->tq_avg; | 578 | |
579 | if ((orig_node->flags & VIS_SERVER) && | ||
580 | (router->tq_avg > best_tq)) { | ||
581 | best_tq = router->tq_avg; | ||
578 | memcpy(packet->target_orig, orig_node->orig, | 582 | memcpy(packet->target_orig, orig_node->orig, |
579 | ETH_ALEN); | 583 | ETH_ALEN); |
580 | } | 584 | } |
585 | neigh_node_free_ref(router); | ||
581 | } | 586 | } |
582 | rcu_read_unlock(); | 587 | rcu_read_unlock(); |
583 | } | 588 | } |
@@ -605,7 +610,7 @@ static int generate_vis_packet(struct bat_priv *bat_priv) | |||
605 | struct hlist_node *node; | 610 | struct hlist_node *node; |
606 | struct hlist_head *head; | 611 | struct hlist_head *head; |
607 | struct orig_node *orig_node; | 612 | struct orig_node *orig_node; |
608 | struct neigh_node *neigh_node; | 613 | struct neigh_node *router; |
609 | struct vis_info *info = (struct vis_info *)bat_priv->my_vis_info; | 614 | struct vis_info *info = (struct vis_info *)bat_priv->my_vis_info; |
610 | struct vis_packet *packet = (struct vis_packet *)info->skb_packet->data; | 615 | struct vis_packet *packet = (struct vis_packet *)info->skb_packet->data; |
611 | struct vis_info_entry *entry; | 616 | struct vis_info_entry *entry; |
@@ -633,30 +638,32 @@ static int generate_vis_packet(struct bat_priv *bat_priv) | |||
633 | 638 | ||
634 | rcu_read_lock(); | 639 | rcu_read_lock(); |
635 | hlist_for_each_entry_rcu(orig_node, node, head, hash_entry) { | 640 | hlist_for_each_entry_rcu(orig_node, node, head, hash_entry) { |
636 | neigh_node = orig_node->router; | 641 | router = orig_node_get_router(orig_node); |
637 | 642 | if (!router) | |
638 | if (!neigh_node) | ||
639 | continue; | 643 | continue; |
640 | 644 | ||
641 | if (!compare_eth(neigh_node->addr, orig_node->orig)) | 645 | if (!compare_eth(router->addr, orig_node->orig)) |
642 | continue; | 646 | goto next; |
643 | 647 | ||
644 | if (neigh_node->if_incoming->if_status != IF_ACTIVE) | 648 | if (router->if_incoming->if_status != IF_ACTIVE) |
645 | continue; | 649 | goto next; |
646 | 650 | ||
647 | if (neigh_node->tq_avg < 1) | 651 | if (router->tq_avg < 1) |
648 | continue; | 652 | goto next; |
649 | 653 | ||
650 | /* fill one entry into buffer. */ | 654 | /* fill one entry into buffer. */ |
651 | entry = (struct vis_info_entry *) | 655 | entry = (struct vis_info_entry *) |
652 | skb_put(info->skb_packet, sizeof(*entry)); | 656 | skb_put(info->skb_packet, sizeof(*entry)); |
653 | memcpy(entry->src, | 657 | memcpy(entry->src, |
654 | neigh_node->if_incoming->net_dev->dev_addr, | 658 | router->if_incoming->net_dev->dev_addr, |
655 | ETH_ALEN); | 659 | ETH_ALEN); |
656 | memcpy(entry->dest, orig_node->orig, ETH_ALEN); | 660 | memcpy(entry->dest, orig_node->orig, ETH_ALEN); |
657 | entry->quality = neigh_node->tq_avg; | 661 | entry->quality = router->tq_avg; |
658 | packet->entries++; | 662 | packet->entries++; |
659 | 663 | ||
664 | next: | ||
665 | neigh_node_free_ref(router); | ||
666 | |||
660 | if (vis_packet_full(info)) | 667 | if (vis_packet_full(info)) |
661 | goto unlock; | 668 | goto unlock; |
662 | } | 669 | } |
@@ -725,6 +732,7 @@ static void purge_vis_packets(struct bat_priv *bat_priv) | |||
725 | static void broadcast_vis_packet(struct bat_priv *bat_priv, | 732 | static void broadcast_vis_packet(struct bat_priv *bat_priv, |
726 | struct vis_info *info) | 733 | struct vis_info *info) |
727 | { | 734 | { |
735 | struct neigh_node *router; | ||
728 | struct hashtable_t *hash = bat_priv->orig_hash; | 736 | struct hashtable_t *hash = bat_priv->orig_hash; |
729 | struct hlist_node *node; | 737 | struct hlist_node *node; |
730 | struct hlist_head *head; | 738 | struct hlist_head *head; |
@@ -745,19 +753,26 @@ static void broadcast_vis_packet(struct bat_priv *bat_priv, | |||
745 | rcu_read_lock(); | 753 | rcu_read_lock(); |
746 | hlist_for_each_entry_rcu(orig_node, node, head, hash_entry) { | 754 | hlist_for_each_entry_rcu(orig_node, node, head, hash_entry) { |
747 | /* if it's a vis server and reachable, send it. */ | 755 | /* if it's a vis server and reachable, send it. */ |
748 | if ((!orig_node) || (!orig_node->router)) | ||
749 | continue; | ||
750 | if (!(orig_node->flags & VIS_SERVER)) | 756 | if (!(orig_node->flags & VIS_SERVER)) |
751 | continue; | 757 | continue; |
758 | |||
759 | router = orig_node_get_router(orig_node); | ||
760 | if (!router) | ||
761 | continue; | ||
762 | |||
752 | /* don't send it if we already received the packet from | 763 | /* don't send it if we already received the packet from |
753 | * this node. */ | 764 | * this node. */ |
754 | if (recv_list_is_in(bat_priv, &info->recv_list, | 765 | if (recv_list_is_in(bat_priv, &info->recv_list, |
755 | orig_node->orig)) | 766 | orig_node->orig)) { |
767 | neigh_node_free_ref(router); | ||
756 | continue; | 768 | continue; |
769 | } | ||
757 | 770 | ||
758 | memcpy(packet->target_orig, orig_node->orig, ETH_ALEN); | 771 | memcpy(packet->target_orig, orig_node->orig, ETH_ALEN); |
759 | hard_iface = orig_node->router->if_incoming; | 772 | hard_iface = router->if_incoming; |
760 | memcpy(dstaddr, orig_node->router->addr, ETH_ALEN); | 773 | memcpy(dstaddr, router->addr, ETH_ALEN); |
774 | |||
775 | neigh_node_free_ref(router); | ||
761 | 776 | ||
762 | skb = skb_clone(info->skb_packet, GFP_ATOMIC); | 777 | skb = skb_clone(info->skb_packet, GFP_ATOMIC); |
763 | if (skb) | 778 | if (skb) |
@@ -772,45 +787,29 @@ static void unicast_vis_packet(struct bat_priv *bat_priv, | |||
772 | struct vis_info *info) | 787 | struct vis_info *info) |
773 | { | 788 | { |
774 | struct orig_node *orig_node; | 789 | struct orig_node *orig_node; |
775 | struct neigh_node *neigh_node = NULL; | 790 | struct neigh_node *router = NULL; |
776 | struct sk_buff *skb; | 791 | struct sk_buff *skb; |
777 | struct vis_packet *packet; | 792 | struct vis_packet *packet; |
778 | 793 | ||
779 | packet = (struct vis_packet *)info->skb_packet->data; | 794 | packet = (struct vis_packet *)info->skb_packet->data; |
780 | 795 | ||
781 | rcu_read_lock(); | ||
782 | orig_node = orig_hash_find(bat_priv, packet->target_orig); | 796 | orig_node = orig_hash_find(bat_priv, packet->target_orig); |
783 | |||
784 | if (!orig_node) | 797 | if (!orig_node) |
785 | goto unlock; | 798 | goto out; |
786 | 799 | ||
787 | neigh_node = orig_node->router; | 800 | router = orig_node_get_router(orig_node); |
788 | 801 | if (!router) | |
789 | if (!neigh_node) | 802 | goto out; |
790 | goto unlock; | ||
791 | |||
792 | if (!atomic_inc_not_zero(&neigh_node->refcount)) { | ||
793 | neigh_node = NULL; | ||
794 | goto unlock; | ||
795 | } | ||
796 | |||
797 | rcu_read_unlock(); | ||
798 | 803 | ||
799 | skb = skb_clone(info->skb_packet, GFP_ATOMIC); | 804 | skb = skb_clone(info->skb_packet, GFP_ATOMIC); |
800 | if (skb) | 805 | if (skb) |
801 | send_skb_packet(skb, neigh_node->if_incoming, | 806 | send_skb_packet(skb, router->if_incoming, router->addr); |
802 | neigh_node->addr); | ||
803 | |||
804 | goto out; | ||
805 | 807 | ||
806 | unlock: | ||
807 | rcu_read_unlock(); | ||
808 | out: | 808 | out: |
809 | if (neigh_node) | 809 | if (router) |
810 | neigh_node_free_ref(neigh_node); | 810 | neigh_node_free_ref(router); |
811 | if (orig_node) | 811 | if (orig_node) |
812 | orig_node_free_ref(orig_node); | 812 | orig_node_free_ref(orig_node); |
813 | return; | ||
814 | } | 813 | } |
815 | 814 | ||
816 | /* only send one vis packet. called from send_vis_packets() */ | 815 | /* only send one vis packet. called from send_vis_packets() */ |