diff options
Diffstat (limited to 'drivers/staging/batman-adv/routing.c')
-rw-r--r-- | drivers/staging/batman-adv/routing.c | 326 |
1 files changed, 275 insertions, 51 deletions
diff --git a/drivers/staging/batman-adv/routing.c b/drivers/staging/batman-adv/routing.c index 066dc8b38817..acd8f745ebc0 100644 --- a/drivers/staging/batman-adv/routing.c +++ b/drivers/staging/batman-adv/routing.c @@ -25,7 +25,7 @@ #include "hash.h" #include "soft-interface.h" #include "hard-interface.h" -#include "device.h" +#include "icmp_socket.h" #include "translation-table.h" #include "originator.h" #include "types.h" @@ -33,7 +33,7 @@ #include "vis.h" #include "aggregation.h" -DECLARE_WAIT_QUEUE_HEAD(thread_wait); +static DECLARE_WAIT_QUEUE_HEAD(thread_wait); void slide_own_bcast_window(struct batman_if *batman_if) { @@ -318,7 +318,7 @@ update_hna: * 0 if the packet is to be accepted * 1 if the packet is to be ignored. */ -static int window_protected(int16_t seq_num_diff, +static int window_protected(int32_t seq_num_diff, unsigned long *last_reset) { if ((seq_num_diff <= -TQ_LOCAL_WINDOW_SIZE) @@ -352,7 +352,7 @@ static char count_real_packets(struct ethhdr *ethhdr, struct orig_node *orig_node; struct neigh_node *tmp_neigh_node; char is_duplicate = 0; - int16_t seq_diff; + int32_t seq_diff; int need_update = 0; int set_mark; @@ -395,18 +395,127 @@ static char count_real_packets(struct ethhdr *ethhdr, return is_duplicate; } +/* copy primary address for bonding */ +static void mark_bonding_address(struct bat_priv *bat_priv, + struct orig_node *orig_node, + struct orig_node *orig_neigh_node, + struct batman_packet *batman_packet) + +{ + if (batman_packet->flags & PRIMARIES_FIRST_HOP) + memcpy(orig_neigh_node->primary_addr, + orig_node->orig, ETH_ALEN); + + return; +} + +/* mark possible bond.candidates in the neighbor list */ +void update_bonding_candidates(struct bat_priv *bat_priv, + struct orig_node *orig_node) +{ + int candidates; + int interference_candidate; + int best_tq; + struct neigh_node *tmp_neigh_node, *tmp_neigh_node2; + struct neigh_node *first_candidate, *last_candidate; + + /* update the candidates for this originator */ + if (!orig_node->router) { + orig_node->bond.candidates = 0; + return; + } + + best_tq = orig_node->router->tq_avg; + + /* update bond.candidates */ + + candidates = 0; + + /* mark other nodes which also received "PRIMARIES FIRST HOP" packets + * as "bonding partner" */ + + /* first, zero the list */ + list_for_each_entry(tmp_neigh_node, &orig_node->neigh_list, list) { + tmp_neigh_node->next_bond_candidate = NULL; + } + + first_candidate = NULL; + last_candidate = NULL; + list_for_each_entry(tmp_neigh_node, &orig_node->neigh_list, list) { + + /* only consider if it has the same primary address ... */ + if (memcmp(orig_node->orig, + tmp_neigh_node->orig_node->primary_addr, + ETH_ALEN) != 0) + continue; + + /* ... and is good enough to be considered */ + if (tmp_neigh_node->tq_avg < best_tq - BONDING_TQ_THRESHOLD) + continue; + + /* check if we have another candidate with the same + * mac address or interface. If we do, we won't + * select this candidate because of possible interference. */ + + interference_candidate = 0; + list_for_each_entry(tmp_neigh_node2, + &orig_node->neigh_list, list) { + + if (tmp_neigh_node2 == tmp_neigh_node) + continue; + + /* we only care if the other candidate is even + * considered as candidate. */ + if (tmp_neigh_node2->next_bond_candidate == NULL) + continue; + + + if ((tmp_neigh_node->if_incoming == + tmp_neigh_node2->if_incoming) + || (memcmp(tmp_neigh_node->addr, + tmp_neigh_node2->addr, ETH_ALEN) == 0)) { + + interference_candidate = 1; + break; + } + } + /* don't care further if it is an interference candidate */ + if (interference_candidate) + continue; + + if (first_candidate == NULL) { + first_candidate = tmp_neigh_node; + tmp_neigh_node->next_bond_candidate = first_candidate; + } else + tmp_neigh_node->next_bond_candidate = last_candidate; + + last_candidate = tmp_neigh_node; + + candidates++; + } + + if (candidates > 0) { + first_candidate->next_bond_candidate = last_candidate; + orig_node->bond.selected = first_candidate; + } + + orig_node->bond.candidates = candidates; +} + void receive_bat_packet(struct ethhdr *ethhdr, struct batman_packet *batman_packet, unsigned char *hna_buff, int hna_buff_len, struct batman_if *if_incoming) { + /* FIXME: each orig_node->batman_if will be attached to a softif */ + struct bat_priv *bat_priv = netdev_priv(soft_device); struct batman_if *batman_if; struct orig_node *orig_neigh_node, *orig_node; 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; char is_duplicate; - unsigned short if_incoming_seqno; + uint32_t if_incoming_seqno; /* Silently drop when the batman packet is actually not a * correct packet. @@ -577,6 +686,10 @@ void receive_bat_packet(struct ethhdr *ethhdr, update_orig(orig_node, ethhdr, batman_packet, if_incoming, hna_buff, hna_buff_len, is_duplicate); + mark_bonding_address(bat_priv, orig_node, + orig_neigh_node, batman_packet); + update_bonding_candidates(bat_priv, orig_node); + /* is single hop (direct) neighbor */ if (is_single_hop_neigh) { @@ -652,10 +765,10 @@ int recv_bat_packet(struct sk_buff *skb, return NET_RX_SUCCESS; } -static int recv_my_icmp_packet(struct sk_buff *skb) +static int recv_my_icmp_packet(struct sk_buff *skb, size_t icmp_len) { struct orig_node *orig_node; - struct icmp_packet *icmp_packet; + struct icmp_packet_rr *icmp_packet; struct ethhdr *ethhdr; struct sk_buff *skb_old; struct batman_if *batman_if; @@ -663,12 +776,12 @@ static int recv_my_icmp_packet(struct sk_buff *skb) unsigned long flags; uint8_t dstaddr[ETH_ALEN]; - icmp_packet = (struct icmp_packet *)skb->data; + icmp_packet = (struct icmp_packet_rr *)skb->data; ethhdr = (struct ethhdr *)skb_mac_header(skb); /* add data to device queue */ if (icmp_packet->msg_type != ECHO_REQUEST) { - bat_device_receive_packet(icmp_packet); + bat_socket_receive_packet(icmp_packet, icmp_len); return NET_RX_DROP; } @@ -690,13 +803,12 @@ static int recv_my_icmp_packet(struct sk_buff *skb) /* create a copy of the skb, if needed, to modify it. */ skb_old = NULL; - if (!skb_clone_writable(skb, sizeof(struct icmp_packet))) { + if (!skb_clone_writable(skb, icmp_len)) { skb_old = skb; skb = skb_copy(skb, GFP_ATOMIC); if (!skb) return NET_RX_DROP; - - icmp_packet = (struct icmp_packet *)skb->data; + icmp_packet = (struct icmp_packet_rr *)skb->data; ethhdr = (struct ethhdr *)skb_mac_header(skb); kfree_skb(skb_old); } @@ -715,7 +827,7 @@ static int recv_my_icmp_packet(struct sk_buff *skb) return ret; } -static int recv_icmp_ttl_exceeded(struct sk_buff *skb) +static int recv_icmp_ttl_exceeded(struct sk_buff *skb, size_t icmp_len) { struct orig_node *orig_node; struct icmp_packet *icmp_packet; @@ -754,7 +866,7 @@ static int recv_icmp_ttl_exceeded(struct sk_buff *skb) spin_unlock_irqrestore(&orig_hash_lock, flags); /* create a copy of the skb, if needed, to modify it. */ - if (!skb_clone_writable(skb, sizeof(struct icmp_packet))) { + if (!skb_clone_writable(skb, icmp_len)) { skb_old = skb; skb = skb_copy(skb, GFP_ATOMIC); if (!skb) @@ -781,7 +893,7 @@ static int recv_icmp_ttl_exceeded(struct sk_buff *skb) int recv_icmp_packet(struct sk_buff *skb) { - struct icmp_packet *icmp_packet; + struct icmp_packet_rr *icmp_packet; struct ethhdr *ethhdr; struct orig_node *orig_node; struct sk_buff *skb_old; @@ -791,6 +903,12 @@ int recv_icmp_packet(struct sk_buff *skb) unsigned long flags; uint8_t dstaddr[ETH_ALEN]; + /** + * we truncate all incoming icmp packets if they don't match our size + */ + if (skb_headlen(skb) >= sizeof(struct icmp_packet_rr)) + hdr_size = sizeof(struct icmp_packet_rr); + /* drop packet if it has not necessary minimum size */ if (skb_headlen(skb) < hdr_size) return NET_RX_DROP; @@ -809,15 +927,23 @@ int recv_icmp_packet(struct sk_buff *skb) if (!is_my_mac(ethhdr->h_dest)) return NET_RX_DROP; - icmp_packet = (struct icmp_packet *)skb->data; + icmp_packet = (struct icmp_packet_rr *)skb->data; + + /* add record route information if not full */ + if ((hdr_size == sizeof(struct icmp_packet_rr)) && + (icmp_packet->rr_cur < BAT_RR_LEN)) { + memcpy(&(icmp_packet->rr[icmp_packet->rr_cur]), + ethhdr->h_dest, ETH_ALEN); + icmp_packet->rr_cur++; + } /* packet for me */ if (is_my_mac(icmp_packet->dst)) - return recv_my_icmp_packet(skb); + return recv_my_icmp_packet(skb, hdr_size); /* TTL exceeded */ if (icmp_packet->ttl < 2) - return recv_icmp_ttl_exceeded(skb); + return recv_icmp_ttl_exceeded(skb, hdr_size); ret = NET_RX_DROP; @@ -836,12 +962,12 @@ int recv_icmp_packet(struct sk_buff *skb) spin_unlock_irqrestore(&orig_hash_lock, flags); /* create a copy of the skb, if needed, to modify it. */ - if (!skb_clone_writable(skb, sizeof(struct icmp_packet))) { + if (!skb_clone_writable(skb, hdr_size)) { skb_old = skb; skb = skb_copy(skb, GFP_ATOMIC); if (!skb) return NET_RX_DROP; - icmp_packet = (struct icmp_packet *)skb->data; + icmp_packet = (struct icmp_packet_rr *)skb->data; ethhdr = (struct ethhdr *)skb_mac_header(skb); kfree_skb(skb_old); } @@ -859,16 +985,109 @@ int recv_icmp_packet(struct sk_buff *skb) return ret; } -int recv_unicast_packet(struct sk_buff *skb) +/* find a suitable router for this originator, and use + * bonding if possible. */ +struct neigh_node *find_router(struct orig_node *orig_node, + struct batman_if *recv_if) +{ + /* FIXME: each orig_node->batman_if will be attached to a softif */ + struct bat_priv *bat_priv = netdev_priv(soft_device); + struct orig_node *primary_orig_node; + struct orig_node *router_orig; + struct neigh_node *router, *first_candidate, *best_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; + + /* without bonding, the first node should + * always choose the default router. */ + + bonding_enabled = atomic_read(&bat_priv->bonding_enabled); + if (!bonding_enabled && (recv_if == NULL)) + return orig_node->router; + + router_orig = orig_node->router->orig_node; + + /* if we have something in the primary_addr, we can search + * for a potential bonding candidate. */ + if (memcmp(router_orig->primary_addr, zero_mac, ETH_ALEN) == 0) + return orig_node->router; + + /* find the orig_node which has the primary interface. might + * even be the same as our router_orig in many cases */ + + if (memcmp(router_orig->primary_addr, + router_orig->orig, ETH_ALEN) == 0) { + primary_orig_node = router_orig; + } else { + primary_orig_node = hash_find(orig_hash, + router_orig->primary_addr); + if (!primary_orig_node) + return orig_node->router; + } + + /* with less than 2 candidates, we can't do any + * bonding and prefer the original router. */ + + if (primary_orig_node->bond.candidates < 2) + return orig_node->router; + + + /* all nodes between should choose a candidate which + * is is not on the interface where the packet came + * in. */ + first_candidate = primary_orig_node->bond.selected; + router = first_candidate; + + if (bonding_enabled) { + /* in the bonding case, send the packets in a round + * robin fashion over the remaining interfaces. */ + do { + /* recv_if == NULL on the first node. */ + if (router->if_incoming != recv_if) + break; + + router = router->next_bond_candidate; + } while (router != first_candidate); + + primary_orig_node->bond.selected = router->next_bond_candidate; + + } else { + /* if bonding is disabled, use the best of the + * remaining candidates which are not using + * this interface. */ + best_router = first_candidate; + + do { + /* recv_if == NULL on the first node. */ + if ((router->if_incoming != recv_if) && + (router->tq_avg > best_router->tq_avg)) + best_router = router; + + router = router->next_bond_candidate; + } while (router != first_candidate); + + router = best_router; + } + + return router; +} + +int recv_unicast_packet(struct sk_buff *skb, struct batman_if *recv_if) { struct unicast_packet *unicast_packet; struct orig_node *orig_node; + struct neigh_node *router; struct ethhdr *ethhdr; struct batman_if *batman_if; struct sk_buff *skb_old; uint8_t dstaddr[ETH_ALEN]; int hdr_size = sizeof(struct unicast_packet); - int ret; unsigned long flags; /* drop packet if it has not necessary minimum size */ @@ -906,42 +1125,44 @@ int recv_unicast_packet(struct sk_buff *skb) return NET_RX_DROP; } - ret = NET_RX_DROP; /* get routing information */ spin_lock_irqsave(&orig_hash_lock, flags); orig_node = ((struct orig_node *) hash_find(orig_hash, unicast_packet->dest)); - if ((orig_node != NULL) && - (orig_node->router != NULL)) { + router = find_router(orig_node, recv_if); - /* don't lock while sending the packets ... we therefore - * copy the required data before sending */ - batman_if = orig_node->router->if_incoming; - memcpy(dstaddr, orig_node->router->addr, ETH_ALEN); + if (!router) { spin_unlock_irqrestore(&orig_hash_lock, flags); + return NET_RX_DROP; + } - /* create a copy of the skb, if needed, to modify it. */ - if (!skb_clone_writable(skb, sizeof(struct unicast_packet))) { - skb_old = skb; - skb = skb_copy(skb, GFP_ATOMIC); - if (!skb) - return NET_RX_DROP; - unicast_packet = (struct unicast_packet *)skb->data; - ethhdr = (struct ethhdr *)skb_mac_header(skb); - kfree_skb(skb_old); - } - /* decrement ttl */ - unicast_packet->ttl--; + /* don't lock while sending the packets ... we therefore + * copy the required data before sending */ - /* route it */ - send_skb_packet(skb, batman_if, dstaddr); - ret = NET_RX_SUCCESS; + batman_if = router->if_incoming; + memcpy(dstaddr, router->addr, ETH_ALEN); - } else - spin_unlock_irqrestore(&orig_hash_lock, flags); + spin_unlock_irqrestore(&orig_hash_lock, flags); - return ret; + /* create a copy of the skb, if needed, to modify it. */ + if (!skb_clone_writable(skb, sizeof(struct unicast_packet))) { + skb_old = skb; + skb = skb_copy(skb, GFP_ATOMIC); + if (!skb) + return NET_RX_DROP; + unicast_packet = (struct unicast_packet *) skb->data; + ethhdr = (struct ethhdr *)skb_mac_header(skb); + kfree_skb(skb_old); + } + + /* decrement ttl */ + unicast_packet->ttl--; + + /* route it */ + send_skb_packet(skb, batman_if, dstaddr); + + return NET_RX_SUCCESS; } int recv_bcast_packet(struct sk_buff *skb) @@ -950,7 +1171,7 @@ int recv_bcast_packet(struct sk_buff *skb) struct bcast_packet *bcast_packet; struct ethhdr *ethhdr; int hdr_size = sizeof(struct bcast_packet); - int16_t seq_diff; + int32_t seq_diff; unsigned long flags; /* drop packet if it has not necessary minimum size */ @@ -977,6 +1198,9 @@ int recv_bcast_packet(struct sk_buff *skb) if (is_my_mac(bcast_packet->orig)) return NET_RX_DROP; + if (bcast_packet->ttl < 2) + return NET_RX_DROP; + spin_lock_irqsave(&orig_hash_lock, flags); orig_node = ((struct orig_node *) hash_find(orig_hash, bcast_packet->orig)); @@ -989,12 +1213,12 @@ int recv_bcast_packet(struct sk_buff *skb) /* check whether the packet is a duplicate */ if (get_bit_status(orig_node->bcast_bits, orig_node->last_bcast_seqno, - ntohs(bcast_packet->seqno))) { + ntohl(bcast_packet->seqno))) { spin_unlock_irqrestore(&orig_hash_lock, flags); return NET_RX_DROP; } - seq_diff = ntohs(bcast_packet->seqno) - orig_node->last_bcast_seqno; + seq_diff = ntohl(bcast_packet->seqno) - orig_node->last_bcast_seqno; /* check whether the packet is old and the host just restarted. */ if (window_protected(seq_diff, &orig_node->bcast_seqno_reset)) { @@ -1005,7 +1229,7 @@ int recv_bcast_packet(struct sk_buff *skb) /* mark broadcast in flood history, update window position * if required. */ if (bit_get_packet(orig_node->bcast_bits, seq_diff, 1)) - orig_node->last_bcast_seqno = ntohs(bcast_packet->seqno); + orig_node->last_bcast_seqno = ntohl(bcast_packet->seqno); spin_unlock_irqrestore(&orig_hash_lock, flags); /* rebroadcast packet */ |