diff options
Diffstat (limited to 'net/batman-adv/routing.c')
| -rw-r--r-- | net/batman-adv/routing.c | 572 | 
1 files changed, 286 insertions, 286 deletions
| diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c index c172f5d0e05a..bb1c3ec7e3ff 100644 --- a/net/batman-adv/routing.c +++ b/net/batman-adv/routing.c @@ -64,80 +64,97 @@ void slide_own_bcast_window(struct hard_iface *hard_iface)  	}  } -static void update_HNA(struct bat_priv *bat_priv, struct orig_node *orig_node, -		       unsigned char *hna_buff, int hna_buff_len) +static void update_TT(struct bat_priv *bat_priv, struct orig_node *orig_node, +		       unsigned char *tt_buff, int tt_buff_len)  { -	if ((hna_buff_len != orig_node->hna_buff_len) || -	    ((hna_buff_len > 0) && -	     (orig_node->hna_buff_len > 0) && -	     (memcmp(orig_node->hna_buff, hna_buff, hna_buff_len) != 0))) { - -		if (orig_node->hna_buff_len > 0) -			hna_global_del_orig(bat_priv, orig_node, -					    "originator changed hna"); - -		if ((hna_buff_len > 0) && (hna_buff)) -			hna_global_add_orig(bat_priv, orig_node, -					    hna_buff, hna_buff_len); +	if ((tt_buff_len != orig_node->tt_buff_len) || +	    ((tt_buff_len > 0) && +	     (orig_node->tt_buff_len > 0) && +	     (memcmp(orig_node->tt_buff, tt_buff, tt_buff_len) != 0))) { + +		if (orig_node->tt_buff_len > 0) +			tt_global_del_orig(bat_priv, orig_node, +					    "originator changed tt"); + +		if ((tt_buff_len > 0) && (tt_buff)) +			tt_global_add_orig(bat_priv, orig_node, +					    tt_buff, tt_buff_len);  	}  }  static void update_route(struct bat_priv *bat_priv,  			 struct orig_node *orig_node,  			 struct neigh_node *neigh_node, -			 unsigned char *hna_buff, int hna_buff_len) +			 unsigned char *tt_buff, int tt_buff_len)  { -	struct neigh_node *neigh_node_tmp; +	struct neigh_node *curr_router; + +	curr_router = orig_node_get_router(orig_node);  	/* route deleted */ -	if ((orig_node->router) && (!neigh_node)) { +	if ((curr_router) && (!neigh_node)) {  		bat_dbg(DBG_ROUTES, bat_priv, "Deleting route towards: %pM\n",  			orig_node->orig); -		hna_global_del_orig(bat_priv, orig_node, +		tt_global_del_orig(bat_priv, orig_node,  				    "originator timed out"); -		/* route added */ -	} else if ((!orig_node->router) && (neigh_node)) { +	/* route added */ +	} else if ((!curr_router) && (neigh_node)) {  		bat_dbg(DBG_ROUTES, bat_priv,  			"Adding route towards: %pM (via %pM)\n",  			orig_node->orig, neigh_node->addr); -		hna_global_add_orig(bat_priv, orig_node, -				    hna_buff, hna_buff_len); +		tt_global_add_orig(bat_priv, orig_node, +				    tt_buff, tt_buff_len); -		/* route changed */ +	/* route changed */  	} else {  		bat_dbg(DBG_ROUTES, bat_priv,  			"Changing route towards: %pM "  			"(now via %pM - was via %pM)\n",  			orig_node->orig, neigh_node->addr, -			orig_node->router->addr); +			curr_router->addr);  	} +	if (curr_router) +		neigh_node_free_ref(curr_router); + +	/* increase refcount of new best neighbor */  	if (neigh_node && !atomic_inc_not_zero(&neigh_node->refcount))  		neigh_node = NULL; -	neigh_node_tmp = orig_node->router; -	orig_node->router = neigh_node; -	if (neigh_node_tmp) -		neigh_node_free_ref(neigh_node_tmp); + +	spin_lock_bh(&orig_node->neigh_list_lock); +	rcu_assign_pointer(orig_node->router, neigh_node); +	spin_unlock_bh(&orig_node->neigh_list_lock); + +	/* decrease refcount of previous best neighbor */ +	if (curr_router) +		neigh_node_free_ref(curr_router);  }  void update_routes(struct bat_priv *bat_priv, struct orig_node *orig_node, -		   struct neigh_node *neigh_node, unsigned char *hna_buff, -		   int hna_buff_len) +		   struct neigh_node *neigh_node, unsigned char *tt_buff, +		   int tt_buff_len)  { +	struct neigh_node *router = NULL;  	if (!orig_node) -		return; +		goto out; + +	router = orig_node_get_router(orig_node); -	if (orig_node->router != neigh_node) +	if (router != neigh_node)  		update_route(bat_priv, orig_node, neigh_node, -			     hna_buff, hna_buff_len); -	/* may be just HNA changed */ +			     tt_buff, tt_buff_len); +	/* may be just TT changed */  	else -		update_HNA(bat_priv, orig_node, hna_buff, hna_buff_len); +		update_TT(bat_priv, orig_node, tt_buff, tt_buff_len); + +out: +	if (router) +		neigh_node_free_ref(router);  }  static int is_bidirectional_neigh(struct orig_node *orig_node, @@ -152,65 +169,41 @@ static int is_bidirectional_neigh(struct orig_node *orig_node,  	uint8_t orig_eq_count, neigh_rq_count, tq_own;  	int tq_asym_penalty, ret = 0; -	if (orig_node == orig_neigh_node) { -		rcu_read_lock(); -		hlist_for_each_entry_rcu(tmp_neigh_node, node, -					 &orig_node->neigh_list, list) { - -			if (!compare_eth(tmp_neigh_node->addr, -					 orig_neigh_node->orig)) -				continue; - -			if (tmp_neigh_node->if_incoming != if_incoming) -				continue; - -			if (!atomic_inc_not_zero(&tmp_neigh_node->refcount)) -				continue; - -			neigh_node = tmp_neigh_node; -		} -		rcu_read_unlock(); +	/* find corresponding one hop neighbor */ +	rcu_read_lock(); +	hlist_for_each_entry_rcu(tmp_neigh_node, node, +				 &orig_neigh_node->neigh_list, list) { -		if (!neigh_node) -			neigh_node = create_neighbor(orig_node, -						     orig_neigh_node, -						     orig_neigh_node->orig, -						     if_incoming); -		if (!neigh_node) -			goto out; +		if (!compare_eth(tmp_neigh_node->addr, orig_neigh_node->orig)) +			continue; -		neigh_node->last_valid = jiffies; -	} else { -		/* find packet count of corresponding one hop neighbor */ -		rcu_read_lock(); -		hlist_for_each_entry_rcu(tmp_neigh_node, node, -					 &orig_neigh_node->neigh_list, list) { +		if (tmp_neigh_node->if_incoming != if_incoming) +			continue; -			if (!compare_eth(tmp_neigh_node->addr, -					 orig_neigh_node->orig)) -				continue; +		if (!atomic_inc_not_zero(&tmp_neigh_node->refcount)) +			continue; -			if (tmp_neigh_node->if_incoming != if_incoming) -				continue; +		neigh_node = tmp_neigh_node; +		break; +	} +	rcu_read_unlock(); -			if (!atomic_inc_not_zero(&tmp_neigh_node->refcount)) -				continue; +	if (!neigh_node) +		neigh_node = create_neighbor(orig_neigh_node, +					     orig_neigh_node, +					     orig_neigh_node->orig, +					     if_incoming); -			neigh_node = tmp_neigh_node; -		} -		rcu_read_unlock(); +	if (!neigh_node) +		goto out; -		if (!neigh_node) -			neigh_node = create_neighbor(orig_neigh_node, -						     orig_neigh_node, -						     orig_neigh_node->orig, -						     if_incoming); -		if (!neigh_node) -			goto out; -	} +	/* if orig_node is direct neighbour update neigh_node last_valid */ +	if (orig_node == orig_neigh_node) +		neigh_node->last_valid = jiffies;  	orig_node->last_valid = jiffies; +	/* find packet count of corresponding one hop neighbor */  	spin_lock_bh(&orig_node->ogm_cnt_lock);  	orig_eq_count = orig_neigh_node->bcast_own_sum[if_incoming->if_num];  	neigh_rq_count = neigh_node->real_packet_count; @@ -288,8 +281,8 @@ static void bonding_candidate_add(struct orig_node *orig_node,  				  struct neigh_node *neigh_node)  {  	struct hlist_node *node; -	struct neigh_node *tmp_neigh_node; -	uint8_t best_tq, interference_candidate = 0; +	struct neigh_node *tmp_neigh_node, *router = NULL; +	uint8_t interference_candidate = 0;  	spin_lock_bh(&orig_node->neigh_list_lock); @@ -298,13 +291,12 @@ static void bonding_candidate_add(struct orig_node *orig_node,  			 neigh_node->orig_node->primary_addr))  		goto candidate_del; -	if (!orig_node->router) +	router = orig_node_get_router(orig_node); +	if (!router)  		goto candidate_del; -	best_tq = orig_node->router->tq_avg; -  	/* ... and is good enough to be considered */ -	if (neigh_node->tq_avg < best_tq - BONDING_TQ_THRESHOLD) +	if (neigh_node->tq_avg < router->tq_avg - BONDING_TQ_THRESHOLD)  		goto candidate_del;  	/** @@ -350,7 +342,9 @@ candidate_del:  out:  	spin_unlock_bh(&orig_node->neigh_list_lock); -	return; + +	if (router) +		neigh_node_free_ref(router);  }  /* copy primary address for bonding */ @@ -369,13 +363,14 @@ static void update_orig(struct bat_priv *bat_priv,  			struct ethhdr *ethhdr,  			struct batman_packet *batman_packet,  			struct hard_iface *if_incoming, -			unsigned char *hna_buff, int hna_buff_len, +			unsigned char *tt_buff, int tt_buff_len,  			char is_duplicate)  {  	struct neigh_node *neigh_node = NULL, *tmp_neigh_node = NULL; +	struct neigh_node *router = NULL;  	struct orig_node *orig_node_tmp;  	struct hlist_node *node; -	int tmp_hna_buff_len; +	int tmp_tt_buff_len;  	uint8_t bcast_own_sum_orig, bcast_own_sum_neigh;  	bat_dbg(DBG_BATMAN, bat_priv, "update_originator(): " @@ -396,10 +391,12 @@ static void update_orig(struct bat_priv *bat_priv,  		if (is_duplicate)  			continue; +		spin_lock_bh(&tmp_neigh_node->tq_lock);  		ring_buffer_set(tmp_neigh_node->tq_recv,  				&tmp_neigh_node->tq_index, 0);  		tmp_neigh_node->tq_avg =  			ring_buffer_avg(tmp_neigh_node->tq_recv); +		spin_unlock_bh(&tmp_neigh_node->tq_lock);  	}  	if (!neigh_node) { @@ -424,10 +421,12 @@ static void update_orig(struct bat_priv *bat_priv,  	orig_node->flags = batman_packet->flags;  	neigh_node->last_valid = jiffies; +	spin_lock_bh(&neigh_node->tq_lock);  	ring_buffer_set(neigh_node->tq_recv,  			&neigh_node->tq_index,  			batman_packet->tq);  	neigh_node->tq_avg = ring_buffer_avg(neigh_node->tq_recv); +	spin_unlock_bh(&neigh_node->tq_lock);  	if (!is_duplicate) {  		orig_node->last_ttl = batman_packet->ttl; @@ -436,24 +435,23 @@ static void update_orig(struct bat_priv *bat_priv,  	bonding_candidate_add(orig_node, neigh_node); -	tmp_hna_buff_len = (hna_buff_len > batman_packet->num_hna * ETH_ALEN ? -			    batman_packet->num_hna * ETH_ALEN : hna_buff_len); +	tmp_tt_buff_len = (tt_buff_len > batman_packet->num_tt * ETH_ALEN ? +			    batman_packet->num_tt * ETH_ALEN : tt_buff_len);  	/* if this neighbor already is our next hop there is nothing  	 * to change */ -	if (orig_node->router == neigh_node) -		goto update_hna; +	router = orig_node_get_router(orig_node); +	if (router == neigh_node) +		goto update_tt;  	/* if this neighbor does not offer a better TQ we won't consider it */ -	if ((orig_node->router) && -	    (orig_node->router->tq_avg > neigh_node->tq_avg)) -		goto update_hna; +	if (router && (router->tq_avg > neigh_node->tq_avg)) +		goto update_tt;  	/* if the TQ is the same and the link not more symetric we  	 * won't consider it either */ -	if ((orig_node->router) && -	     (neigh_node->tq_avg == orig_node->router->tq_avg)) { -		orig_node_tmp = orig_node->router->orig_node; +	if (router && (neigh_node->tq_avg == router->tq_avg)) { +		orig_node_tmp = router->orig_node;  		spin_lock_bh(&orig_node_tmp->ogm_cnt_lock);  		bcast_own_sum_orig =  			orig_node_tmp->bcast_own_sum[if_incoming->if_num]; @@ -466,16 +464,16 @@ static void update_orig(struct bat_priv *bat_priv,  		spin_unlock_bh(&orig_node_tmp->ogm_cnt_lock);  		if (bcast_own_sum_orig >= bcast_own_sum_neigh) -			goto update_hna; +			goto update_tt;  	}  	update_routes(bat_priv, orig_node, neigh_node, -		      hna_buff, tmp_hna_buff_len); +		      tt_buff, tmp_tt_buff_len);  	goto update_gw; -update_hna: -	update_routes(bat_priv, orig_node, orig_node->router, -		      hna_buff, tmp_hna_buff_len); +update_tt: +	update_routes(bat_priv, orig_node, router, +		      tt_buff, tmp_tt_buff_len);  update_gw:  	if (orig_node->gw_flags != batman_packet->gw_flags) @@ -496,6 +494,8 @@ unlock:  out:  	if (neigh_node)  		neigh_node_free_ref(neigh_node); +	if (router) +		neigh_node_free_ref(router);  }  /* checks whether the host restarted and is in the protection time. @@ -597,12 +597,14 @@ out:  void receive_bat_packet(struct ethhdr *ethhdr,  			struct batman_packet *batman_packet, -			unsigned char *hna_buff, int hna_buff_len, +			unsigned char *tt_buff, int tt_buff_len,  			struct hard_iface *if_incoming)  {  	struct bat_priv *bat_priv = netdev_priv(if_incoming->soft_iface);  	struct hard_iface *hard_iface;  	struct orig_node *orig_neigh_node, *orig_node; +	struct neigh_node *router = NULL, *router_router = NULL; +	struct neigh_node *orig_neigh_router = NULL;  	char has_directlink_flag;  	char is_my_addr = 0, is_my_orig = 0, is_my_oldorig = 0;  	char is_broadcast = 0, is_bidirectional, is_single_hop_neigh; @@ -747,14 +749,15 @@ void receive_bat_packet(struct ethhdr *ethhdr,  		goto out;  	} +	router = orig_node_get_router(orig_node); +	if (router) +		router_router = orig_node_get_router(router->orig_node); +  	/* avoid temporary routing loops */ -	if ((orig_node->router) && -	    (orig_node->router->orig_node->router) && -	    (compare_eth(orig_node->router->addr, -			 batman_packet->prev_sender)) && +	if (router && router_router && +	    (compare_eth(router->addr, batman_packet->prev_sender)) &&  	    !(compare_eth(batman_packet->orig, batman_packet->prev_sender)) && -	    (compare_eth(orig_node->router->addr, -			 orig_node->router->orig_node->router->addr))) { +	    (compare_eth(router->addr, router_router->addr))) {  		bat_dbg(DBG_BATMAN, bat_priv,  			"Drop packet: ignoring all rebroadcast packets that "  			"may make me loop (sender: %pM)\n", ethhdr->h_source); @@ -769,9 +772,11 @@ void receive_bat_packet(struct ethhdr *ethhdr,  	if (!orig_neigh_node)  		goto out; +	orig_neigh_router = orig_node_get_router(orig_neigh_node); +  	/* drop packet if sender is not a direct neighbor and if we  	 * don't route towards it */ -	if (!is_single_hop_neigh && (!orig_neigh_node->router)) { +	if (!is_single_hop_neigh && (!orig_neigh_router)) {  		bat_dbg(DBG_BATMAN, bat_priv,  			"Drop packet: OGM via unknown neighbor!\n");  		goto out_neigh; @@ -789,14 +794,14 @@ void receive_bat_packet(struct ethhdr *ethhdr,  	     ((orig_node->last_real_seqno == batman_packet->seqno) &&  	      (orig_node->last_ttl - 3 <= batman_packet->ttl))))  		update_orig(bat_priv, orig_node, ethhdr, batman_packet, -			    if_incoming, hna_buff, hna_buff_len, is_duplicate); +			    if_incoming, tt_buff, tt_buff_len, is_duplicate);  	/* is single hop (direct) neighbor */  	if (is_single_hop_neigh) {  		/* mark direct link on incoming interface */  		schedule_forward_packet(orig_node, ethhdr, batman_packet, -					1, hna_buff_len, if_incoming); +					1, tt_buff_len, if_incoming);  		bat_dbg(DBG_BATMAN, bat_priv, "Forwarding packet: "  			"rebroadcast neighbor packet with direct link flag\n"); @@ -819,12 +824,19 @@ void receive_bat_packet(struct ethhdr *ethhdr,  	bat_dbg(DBG_BATMAN, bat_priv,  		"Forwarding packet: rebroadcast originator packet\n");  	schedule_forward_packet(orig_node, ethhdr, batman_packet, -				0, hna_buff_len, if_incoming); +				0, tt_buff_len, if_incoming);  out_neigh:  	if ((orig_neigh_node) && (!is_single_hop_neigh))  		orig_node_free_ref(orig_neigh_node);  out: +	if (router) +		neigh_node_free_ref(router); +	if (router_router) +		neigh_node_free_ref(router_router); +	if (orig_neigh_router) +		neigh_node_free_ref(orig_neigh_router); +  	orig_node_free_ref(orig_node);  } @@ -868,8 +880,9 @@ int recv_bat_packet(struct sk_buff *skb, struct hard_iface *hard_iface)  static int recv_my_icmp_packet(struct bat_priv *bat_priv,  			       struct sk_buff *skb, size_t icmp_len)  { +	struct hard_iface *primary_if = NULL;  	struct orig_node *orig_node = NULL; -	struct neigh_node *neigh_node = NULL; +	struct neigh_node *router = NULL;  	struct icmp_packet_rr *icmp_packet;  	int ret = NET_RX_DROP; @@ -881,28 +894,19 @@ static int recv_my_icmp_packet(struct bat_priv *bat_priv,  		goto out;  	} -	if (!bat_priv->primary_if) +	primary_if = primary_if_get_selected(bat_priv); +	if (!primary_if)  		goto out;  	/* answer echo request (ping) */  	/* get routing information */ -	rcu_read_lock();  	orig_node = orig_hash_find(bat_priv, icmp_packet->orig); -  	if (!orig_node) -		goto unlock; - -	neigh_node = orig_node->router; - -	if (!neigh_node) -		goto unlock; - -	if (!atomic_inc_not_zero(&neigh_node->refcount)) { -		neigh_node = NULL; -		goto unlock; -	} +		goto out; -	rcu_read_unlock(); +	router = orig_node_get_router(orig_node); +	if (!router) +		goto out;  	/* create a copy of the skb, if needed, to modify it. */  	if (skb_cow(skb, sizeof(struct ethhdr)) < 0) @@ -911,20 +915,18 @@ static int recv_my_icmp_packet(struct bat_priv *bat_priv,  	icmp_packet = (struct icmp_packet_rr *)skb->data;  	memcpy(icmp_packet->dst, icmp_packet->orig, ETH_ALEN); -	memcpy(icmp_packet->orig, -		bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN); +	memcpy(icmp_packet->orig, primary_if->net_dev->dev_addr, ETH_ALEN);  	icmp_packet->msg_type = ECHO_REPLY;  	icmp_packet->ttl = TTL; -	send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr); +	send_skb_packet(skb, router->if_incoming, router->addr);  	ret = NET_RX_SUCCESS; -	goto out; -unlock: -	rcu_read_unlock();  out: -	if (neigh_node) -		neigh_node_free_ref(neigh_node); +	if (primary_if) +		hardif_free_ref(primary_if); +	if (router) +		neigh_node_free_ref(router);  	if (orig_node)  		orig_node_free_ref(orig_node);  	return ret; @@ -933,8 +935,9 @@ out:  static int recv_icmp_ttl_exceeded(struct bat_priv *bat_priv,  				  struct sk_buff *skb)  { +	struct hard_iface *primary_if = NULL;  	struct orig_node *orig_node = NULL; -	struct neigh_node *neigh_node = NULL; +	struct neigh_node *router = NULL;  	struct icmp_packet *icmp_packet;  	int ret = NET_RX_DROP; @@ -948,27 +951,18 @@ static int recv_icmp_ttl_exceeded(struct bat_priv *bat_priv,  		goto out;  	} -	if (!bat_priv->primary_if) +	primary_if = primary_if_get_selected(bat_priv); +	if (!primary_if)  		goto out;  	/* get routing information */ -	rcu_read_lock();  	orig_node = orig_hash_find(bat_priv, icmp_packet->orig); -  	if (!orig_node) -		goto unlock; - -	neigh_node = orig_node->router; - -	if (!neigh_node) -		goto unlock; - -	if (!atomic_inc_not_zero(&neigh_node->refcount)) { -		neigh_node = NULL; -		goto unlock; -	} +		goto out; -	rcu_read_unlock(); +	router = orig_node_get_router(orig_node); +	if (!router) +		goto out;  	/* create a copy of the skb, if needed, to modify it. */  	if (skb_cow(skb, sizeof(struct ethhdr)) < 0) @@ -977,20 +971,18 @@ static int recv_icmp_ttl_exceeded(struct bat_priv *bat_priv,  	icmp_packet = (struct icmp_packet *)skb->data;  	memcpy(icmp_packet->dst, icmp_packet->orig, ETH_ALEN); -	memcpy(icmp_packet->orig, -		bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN); +	memcpy(icmp_packet->orig, primary_if->net_dev->dev_addr, ETH_ALEN);  	icmp_packet->msg_type = TTL_EXCEEDED;  	icmp_packet->ttl = TTL; -	send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr); +	send_skb_packet(skb, router->if_incoming, router->addr);  	ret = NET_RX_SUCCESS; -	goto out; -unlock: -	rcu_read_unlock();  out: -	if (neigh_node) -		neigh_node_free_ref(neigh_node); +	if (primary_if) +		hardif_free_ref(primary_if); +	if (router) +		neigh_node_free_ref(router);  	if (orig_node)  		orig_node_free_ref(orig_node);  	return ret; @@ -1003,7 +995,7 @@ int recv_icmp_packet(struct sk_buff *skb, struct hard_iface *recv_if)  	struct icmp_packet_rr *icmp_packet;  	struct ethhdr *ethhdr;  	struct orig_node *orig_node = NULL; -	struct neigh_node *neigh_node = NULL; +	struct neigh_node *router = NULL;  	int hdr_size = sizeof(struct icmp_packet);  	int ret = NET_RX_DROP; @@ -1050,23 +1042,13 @@ int recv_icmp_packet(struct sk_buff *skb, struct hard_iface *recv_if)  		return recv_icmp_ttl_exceeded(bat_priv, skb);  	/* get routing information */ -	rcu_read_lock();  	orig_node = orig_hash_find(bat_priv, icmp_packet->dst); -  	if (!orig_node) -		goto unlock; - -	neigh_node = orig_node->router; - -	if (!neigh_node) -		goto unlock; - -	if (!atomic_inc_not_zero(&neigh_node->refcount)) { -		neigh_node = NULL; -		goto unlock; -	} +		goto out; -	rcu_read_unlock(); +	router = orig_node_get_router(orig_node); +	if (!router) +		goto out;  	/* create a copy of the skb, if needed, to modify it. */  	if (skb_cow(skb, sizeof(struct ethhdr)) < 0) @@ -1078,20 +1060,117 @@ int recv_icmp_packet(struct sk_buff *skb, struct hard_iface *recv_if)  	icmp_packet->ttl--;  	/* route it */ -	send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr); +	send_skb_packet(skb, router->if_incoming, router->addr);  	ret = NET_RX_SUCCESS; -	goto out; -unlock: -	rcu_read_unlock();  out: -	if (neigh_node) -		neigh_node_free_ref(neigh_node); +	if (router) +		neigh_node_free_ref(router);  	if (orig_node)  		orig_node_free_ref(orig_node);  	return ret;  } +/* In the bonding case, send the packets in a round + * robin fashion over the remaining interfaces. + * + * This method rotates the bonding list and increases the + * returned router's refcount. */ +static struct neigh_node *find_bond_router(struct orig_node *primary_orig, +					   struct hard_iface *recv_if) +{ +	struct neigh_node *tmp_neigh_node; +	struct neigh_node *router = NULL, *first_candidate = NULL; + +	rcu_read_lock(); +	list_for_each_entry_rcu(tmp_neigh_node, &primary_orig->bond_list, +				bonding_list) { +		if (!first_candidate) +			first_candidate = tmp_neigh_node; + +		/* recv_if == NULL on the first node. */ +		if (tmp_neigh_node->if_incoming == recv_if) +			continue; + +		if (!atomic_inc_not_zero(&tmp_neigh_node->refcount)) +			continue; + +		router = tmp_neigh_node; +		break; +	} + +	/* use the first candidate if nothing was found. */ +	if (!router && first_candidate && +	    atomic_inc_not_zero(&first_candidate->refcount)) +		router = first_candidate; + +	if (!router) +		goto out; + +	/* selected should point to the next element +	 * after the current router */ +	spin_lock_bh(&primary_orig->neigh_list_lock); +	/* this is a list_move(), which unfortunately +	 * does not exist as rcu version */ +	list_del_rcu(&primary_orig->bond_list); +	list_add_rcu(&primary_orig->bond_list, +		     &router->bonding_list); +	spin_unlock_bh(&primary_orig->neigh_list_lock); + +out: +	rcu_read_unlock(); +	return router; +} + +/* Interface Alternating: Use the best of the + * remaining candidates which are not using + * this interface. + * + * Increases the returned router's refcount */ +static struct neigh_node *find_ifalter_router(struct orig_node *primary_orig, +					      struct hard_iface *recv_if) +{ +	struct neigh_node *tmp_neigh_node; +	struct neigh_node *router = NULL, *first_candidate = NULL; + +	rcu_read_lock(); +	list_for_each_entry_rcu(tmp_neigh_node, &primary_orig->bond_list, +				bonding_list) { +		if (!first_candidate) +			first_candidate = tmp_neigh_node; + +		/* recv_if == NULL on the first node. */ +		if (tmp_neigh_node->if_incoming == recv_if) +			continue; + +		if (!atomic_inc_not_zero(&tmp_neigh_node->refcount)) +			continue; + +		/* if we don't have a router yet +		 * or this one is better, choose it. */ +		if ((!router) || +		    (tmp_neigh_node->tq_avg > router->tq_avg)) { +			/* decrement refcount of +			 * previously selected router */ +			if (router) +				neigh_node_free_ref(router); + +			router = tmp_neigh_node; +			atomic_inc_not_zero(&router->refcount); +		} + +		neigh_node_free_ref(tmp_neigh_node); +	} + +	/* use the first candidate if nothing was found. */ +	if (!router && first_candidate && +	    atomic_inc_not_zero(&first_candidate->refcount)) +		router = first_candidate; + +	rcu_read_unlock(); +	return router; +} +  /* find a suitable router for this originator, and use   * bonding if possible. increases the found neighbors   * refcount.*/ @@ -1101,15 +1180,16 @@ struct neigh_node *find_router(struct bat_priv *bat_priv,  {  	struct orig_node *primary_orig_node;  	struct orig_node *router_orig; -	struct neigh_node *router, *first_candidate, *tmp_neigh_node; +	struct neigh_node *router;  	static uint8_t zero_mac[ETH_ALEN] = {0, 0, 0, 0, 0, 0};  	int bonding_enabled;  	if (!orig_node)  		return NULL; -	if (!orig_node->router) -		return NULL; +	router = orig_node_get_router(orig_node); +	if (!router) +		goto err;  	/* without bonding, the first node should  	 * always choose the default router. */ @@ -1117,12 +1197,9 @@ struct neigh_node *find_router(struct bat_priv *bat_priv,  	rcu_read_lock();  	/* select default router to output */ -	router = orig_node->router; -	router_orig = orig_node->router->orig_node; -	if (!router_orig || !atomic_inc_not_zero(&router->refcount)) { -		rcu_read_unlock(); -		return NULL; -	} +	router_orig = router->orig_node; +	if (!router_orig) +		goto err_unlock;  	if ((!recv_if) && (!bonding_enabled))  		goto return_router; @@ -1151,91 +1228,26 @@ struct neigh_node *find_router(struct bat_priv *bat_priv,  	if (atomic_read(&primary_orig_node->bond_candidates) < 2)  		goto return_router; -  	/* all nodes between should choose a candidate which  	 * is is not on the interface where the packet came  	 * in. */  	neigh_node_free_ref(router); -	first_candidate = NULL; -	router = NULL; - -	if (bonding_enabled) { -		/* in the bonding case, send the packets in a round -		 * robin fashion over the remaining interfaces. */ - -		list_for_each_entry_rcu(tmp_neigh_node, -				&primary_orig_node->bond_list, bonding_list) { -			if (!first_candidate) -				first_candidate = tmp_neigh_node; -			/* recv_if == NULL on the first node. */ -			if (tmp_neigh_node->if_incoming != recv_if && -			    atomic_inc_not_zero(&tmp_neigh_node->refcount)) { -				router = tmp_neigh_node; -				break; -			} -		} - -		/* use the first candidate if nothing was found. */ -		if (!router && first_candidate && -		    atomic_inc_not_zero(&first_candidate->refcount)) -			router = first_candidate; -		if (!router) { -			rcu_read_unlock(); -			return NULL; -		} - -		/* selected should point to the next element -		 * after the current router */ -		spin_lock_bh(&primary_orig_node->neigh_list_lock); -		/* this is a list_move(), which unfortunately -		 * does not exist as rcu version */ -		list_del_rcu(&primary_orig_node->bond_list); -		list_add_rcu(&primary_orig_node->bond_list, -				&router->bonding_list); -		spin_unlock_bh(&primary_orig_node->neigh_list_lock); - -	} else { -		/* if bonding is disabled, use the best of the -		 * remaining candidates which are not using -		 * this interface. */ -		list_for_each_entry_rcu(tmp_neigh_node, -			&primary_orig_node->bond_list, bonding_list) { -			if (!first_candidate) -				first_candidate = tmp_neigh_node; - -			/* recv_if == NULL on the first node. */ -			if (tmp_neigh_node->if_incoming == recv_if) -				continue; - -			if (!atomic_inc_not_zero(&tmp_neigh_node->refcount)) -				continue; - -			/* if we don't have a router yet -			 * or this one is better, choose it. */ -			if ((!router) || -			    (tmp_neigh_node->tq_avg > router->tq_avg)) { -				/* decrement refcount of -				 * previously selected router */ -				if (router) -					neigh_node_free_ref(router); - -				router = tmp_neigh_node; -				atomic_inc_not_zero(&router->refcount); -			} - -			neigh_node_free_ref(tmp_neigh_node); -		} +	if (bonding_enabled) +		router = find_bond_router(primary_orig_node, recv_if); +	else +		router = find_ifalter_router(primary_orig_node, recv_if); -		/* use the first candidate if nothing was found. */ -		if (!router && first_candidate && -		    atomic_inc_not_zero(&first_candidate->refcount)) -			router = first_candidate; -	}  return_router:  	rcu_read_unlock();  	return router; +err_unlock: +	rcu_read_unlock(); +err: +	if (router) +		neigh_node_free_ref(router); +	return NULL;  }  static int check_unicast_packet(struct sk_buff *skb, int hdr_size) @@ -1284,13 +1296,10 @@ int route_unicast_packet(struct sk_buff *skb, struct hard_iface *recv_if)  	}  	/* get routing information */ -	rcu_read_lock();  	orig_node = orig_hash_find(bat_priv, unicast_packet->dest);  	if (!orig_node) -		goto unlock; - -	rcu_read_unlock(); +		goto out;  	/* find_router() increases neigh_nodes refcount if found. */  	neigh_node = find_router(bat_priv, orig_node, recv_if); @@ -1336,10 +1345,7 @@ int route_unicast_packet(struct sk_buff *skb, struct hard_iface *recv_if)  	/* route it */  	send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr);  	ret = NET_RX_SUCCESS; -	goto out; -unlock: -	rcu_read_unlock();  out:  	if (neigh_node)  		neigh_node_free_ref(neigh_node); @@ -1438,13 +1444,10 @@ int recv_bcast_packet(struct sk_buff *skb, struct hard_iface *recv_if)  	if (bcast_packet->ttl < 2)  		goto out; -	rcu_read_lock();  	orig_node = orig_hash_find(bat_priv, bcast_packet->orig);  	if (!orig_node) -		goto rcu_unlock; - -	rcu_read_unlock(); +		goto out;  	spin_lock_bh(&orig_node->bcast_seqno_lock); @@ -1475,9 +1478,6 @@ int recv_bcast_packet(struct sk_buff *skb, struct hard_iface *recv_if)  	ret = NET_RX_SUCCESS;  	goto out; -rcu_unlock: -	rcu_read_unlock(); -	goto out;  spin_unlock:  	spin_unlock_bh(&orig_node->bcast_seqno_lock);  out: |