|
@@ -797,6 +797,403 @@ static struct batadv_nc_path *batadv_nc_get_path(struct batadv_priv *bat_priv,
|
|
|
return nc_path;
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * batadv_nc_random_weight_tq - scale the receivers TQ-value to avoid unfair
|
|
|
+ * selection of a receiver with slightly lower TQ than the other
|
|
|
+ * @tq: to be weighted tq value
|
|
|
+ */
|
|
|
+static uint8_t batadv_nc_random_weight_tq(uint8_t tq)
|
|
|
+{
|
|
|
+ uint8_t rand_val, rand_tq;
|
|
|
+
|
|
|
+ get_random_bytes(&rand_val, sizeof(rand_val));
|
|
|
+
|
|
|
+ /* randomize the estimated packet loss (max TQ - estimated TQ) */
|
|
|
+ rand_tq = rand_val * (BATADV_TQ_MAX_VALUE - tq);
|
|
|
+
|
|
|
+ /* normalize the randomized packet loss */
|
|
|
+ rand_tq /= BATADV_TQ_MAX_VALUE;
|
|
|
+
|
|
|
+ /* convert to (randomized) estimated tq again */
|
|
|
+ return BATADV_TQ_MAX_VALUE - rand_tq;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * batadv_nc_memxor - XOR destination with source
|
|
|
+ * @dst: byte array to XOR into
|
|
|
+ * @src: byte array to XOR from
|
|
|
+ * @len: length of destination array
|
|
|
+ */
|
|
|
+static void batadv_nc_memxor(char *dst, const char *src, unsigned int len)
|
|
|
+{
|
|
|
+ unsigned int i;
|
|
|
+
|
|
|
+ for (i = 0; i < len; ++i)
|
|
|
+ dst[i] ^= src[i];
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * batadv_nc_code_packets - code a received unicast_packet with an nc packet
|
|
|
+ * into a coded_packet and send it
|
|
|
+ * @bat_priv: the bat priv with all the soft interface information
|
|
|
+ * @skb: data skb to forward
|
|
|
+ * @ethhdr: pointer to the ethernet header inside the skb
|
|
|
+ * @nc_packet: structure containing the packet to the skb can be coded with
|
|
|
+ * @neigh_node: next hop to forward packet to
|
|
|
+ *
|
|
|
+ * Returns true if both packets are consumed, false otherwise.
|
|
|
+ */
|
|
|
+static bool batadv_nc_code_packets(struct batadv_priv *bat_priv,
|
|
|
+ struct sk_buff *skb,
|
|
|
+ struct ethhdr *ethhdr,
|
|
|
+ struct batadv_nc_packet *nc_packet,
|
|
|
+ struct batadv_neigh_node *neigh_node)
|
|
|
+{
|
|
|
+ uint8_t tq_weighted_neigh, tq_weighted_coding;
|
|
|
+ struct sk_buff *skb_dest, *skb_src;
|
|
|
+ struct batadv_unicast_packet *packet1;
|
|
|
+ struct batadv_unicast_packet *packet2;
|
|
|
+ struct batadv_coded_packet *coded_packet;
|
|
|
+ struct batadv_neigh_node *neigh_tmp, *router_neigh;
|
|
|
+ struct batadv_neigh_node *router_coding = NULL;
|
|
|
+ uint8_t *first_source, *first_dest, *second_source, *second_dest;
|
|
|
+ __be32 packet_id1, packet_id2;
|
|
|
+ size_t count;
|
|
|
+ bool res = false;
|
|
|
+ int coding_len;
|
|
|
+ int unicast_size = sizeof(*packet1);
|
|
|
+ int coded_size = sizeof(*coded_packet);
|
|
|
+ int header_add = coded_size - unicast_size;
|
|
|
+
|
|
|
+ router_neigh = batadv_orig_node_get_router(neigh_node->orig_node);
|
|
|
+ if (!router_neigh)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ neigh_tmp = nc_packet->neigh_node;
|
|
|
+ router_coding = batadv_orig_node_get_router(neigh_tmp->orig_node);
|
|
|
+ if (!router_coding)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ tq_weighted_neigh = batadv_nc_random_weight_tq(router_neigh->tq_avg);
|
|
|
+ tq_weighted_coding = batadv_nc_random_weight_tq(router_coding->tq_avg);
|
|
|
+
|
|
|
+ /* Select one destination for the MAC-header dst-field based on
|
|
|
+ * weighted TQ-values.
|
|
|
+ */
|
|
|
+ if (tq_weighted_neigh >= tq_weighted_coding) {
|
|
|
+ /* Destination from nc_packet is selected for MAC-header */
|
|
|
+ first_dest = nc_packet->nc_path->next_hop;
|
|
|
+ first_source = nc_packet->nc_path->prev_hop;
|
|
|
+ second_dest = neigh_node->addr;
|
|
|
+ second_source = ethhdr->h_source;
|
|
|
+ packet1 = (struct batadv_unicast_packet *)nc_packet->skb->data;
|
|
|
+ packet2 = (struct batadv_unicast_packet *)skb->data;
|
|
|
+ packet_id1 = nc_packet->packet_id;
|
|
|
+ packet_id2 = batadv_skb_crc32(skb,
|
|
|
+ skb->data + sizeof(*packet2));
|
|
|
+ } else {
|
|
|
+ /* Destination for skb is selected for MAC-header */
|
|
|
+ first_dest = neigh_node->addr;
|
|
|
+ first_source = ethhdr->h_source;
|
|
|
+ second_dest = nc_packet->nc_path->next_hop;
|
|
|
+ second_source = nc_packet->nc_path->prev_hop;
|
|
|
+ packet1 = (struct batadv_unicast_packet *)skb->data;
|
|
|
+ packet2 = (struct batadv_unicast_packet *)nc_packet->skb->data;
|
|
|
+ packet_id1 = batadv_skb_crc32(skb,
|
|
|
+ skb->data + sizeof(*packet1));
|
|
|
+ packet_id2 = nc_packet->packet_id;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Instead of zero padding the smallest data buffer, we
|
|
|
+ * code into the largest.
|
|
|
+ */
|
|
|
+ if (skb->len <= nc_packet->skb->len) {
|
|
|
+ skb_dest = nc_packet->skb;
|
|
|
+ skb_src = skb;
|
|
|
+ } else {
|
|
|
+ skb_dest = skb;
|
|
|
+ skb_src = nc_packet->skb;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* coding_len is used when decoding the packet shorter packet */
|
|
|
+ coding_len = skb_src->len - unicast_size;
|
|
|
+
|
|
|
+ if (skb_linearize(skb_dest) < 0 || skb_linearize(skb_src) < 0)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ skb_push(skb_dest, header_add);
|
|
|
+
|
|
|
+ coded_packet = (struct batadv_coded_packet *)skb_dest->data;
|
|
|
+ skb_reset_mac_header(skb_dest);
|
|
|
+
|
|
|
+ coded_packet->header.packet_type = BATADV_CODED;
|
|
|
+ coded_packet->header.version = BATADV_COMPAT_VERSION;
|
|
|
+ coded_packet->header.ttl = packet1->header.ttl;
|
|
|
+
|
|
|
+ /* Info about first unicast packet */
|
|
|
+ memcpy(coded_packet->first_source, first_source, ETH_ALEN);
|
|
|
+ memcpy(coded_packet->first_orig_dest, packet1->dest, ETH_ALEN);
|
|
|
+ coded_packet->first_crc = packet_id1;
|
|
|
+ coded_packet->first_ttvn = packet1->ttvn;
|
|
|
+
|
|
|
+ /* Info about second unicast packet */
|
|
|
+ memcpy(coded_packet->second_dest, second_dest, ETH_ALEN);
|
|
|
+ memcpy(coded_packet->second_source, second_source, ETH_ALEN);
|
|
|
+ memcpy(coded_packet->second_orig_dest, packet2->dest, ETH_ALEN);
|
|
|
+ coded_packet->second_crc = packet_id2;
|
|
|
+ coded_packet->second_ttl = packet2->header.ttl;
|
|
|
+ coded_packet->second_ttvn = packet2->ttvn;
|
|
|
+ coded_packet->coded_len = htons(coding_len);
|
|
|
+
|
|
|
+ /* This is where the magic happens: Code skb_src into skb_dest */
|
|
|
+ batadv_nc_memxor(skb_dest->data + coded_size,
|
|
|
+ skb_src->data + unicast_size, coding_len);
|
|
|
+
|
|
|
+ /* Update counters accordingly */
|
|
|
+ if (BATADV_SKB_CB(skb_src)->decoded &&
|
|
|
+ BATADV_SKB_CB(skb_dest)->decoded) {
|
|
|
+ /* Both packets are recoded */
|
|
|
+ count = skb_src->len + ETH_HLEN;
|
|
|
+ count += skb_dest->len + ETH_HLEN;
|
|
|
+ batadv_add_counter(bat_priv, BATADV_CNT_NC_RECODE, 2);
|
|
|
+ batadv_add_counter(bat_priv, BATADV_CNT_NC_RECODE_BYTES, count);
|
|
|
+ } else if (!BATADV_SKB_CB(skb_src)->decoded &&
|
|
|
+ !BATADV_SKB_CB(skb_dest)->decoded) {
|
|
|
+ /* Both packets are newly coded */
|
|
|
+ count = skb_src->len + ETH_HLEN;
|
|
|
+ count += skb_dest->len + ETH_HLEN;
|
|
|
+ batadv_add_counter(bat_priv, BATADV_CNT_NC_CODE, 2);
|
|
|
+ batadv_add_counter(bat_priv, BATADV_CNT_NC_CODE_BYTES, count);
|
|
|
+ } else if (BATADV_SKB_CB(skb_src)->decoded &&
|
|
|
+ !BATADV_SKB_CB(skb_dest)->decoded) {
|
|
|
+ /* skb_src recoded and skb_dest is newly coded */
|
|
|
+ batadv_inc_counter(bat_priv, BATADV_CNT_NC_RECODE);
|
|
|
+ batadv_add_counter(bat_priv, BATADV_CNT_NC_RECODE_BYTES,
|
|
|
+ skb_src->len + ETH_HLEN);
|
|
|
+ batadv_inc_counter(bat_priv, BATADV_CNT_NC_CODE);
|
|
|
+ batadv_add_counter(bat_priv, BATADV_CNT_NC_CODE_BYTES,
|
|
|
+ skb_dest->len + ETH_HLEN);
|
|
|
+ } else if (!BATADV_SKB_CB(skb_src)->decoded &&
|
|
|
+ BATADV_SKB_CB(skb_dest)->decoded) {
|
|
|
+ /* skb_src is newly coded and skb_dest is recoded */
|
|
|
+ batadv_inc_counter(bat_priv, BATADV_CNT_NC_CODE);
|
|
|
+ batadv_add_counter(bat_priv, BATADV_CNT_NC_CODE_BYTES,
|
|
|
+ skb_src->len + ETH_HLEN);
|
|
|
+ batadv_inc_counter(bat_priv, BATADV_CNT_NC_RECODE);
|
|
|
+ batadv_add_counter(bat_priv, BATADV_CNT_NC_RECODE_BYTES,
|
|
|
+ skb_dest->len + ETH_HLEN);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* skb_src is now coded into skb_dest, so free it */
|
|
|
+ kfree_skb(skb_src);
|
|
|
+
|
|
|
+ /* avoid duplicate free of skb from nc_packet */
|
|
|
+ nc_packet->skb = NULL;
|
|
|
+ batadv_nc_packet_free(nc_packet);
|
|
|
+
|
|
|
+ /* Send the coded packet and return true */
|
|
|
+ batadv_send_skb_packet(skb_dest, neigh_node->if_incoming, first_dest);
|
|
|
+ res = true;
|
|
|
+out:
|
|
|
+ if (router_neigh)
|
|
|
+ batadv_neigh_node_free_ref(router_neigh);
|
|
|
+ if (router_coding)
|
|
|
+ batadv_neigh_node_free_ref(router_coding);
|
|
|
+ return res;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * batadv_nc_skb_coding_possible - true if a decoded skb is available at dst.
|
|
|
+ * @skb: data skb to forward
|
|
|
+ * @dst: destination mac address of the other skb to code with
|
|
|
+ * @src: source mac address of skb
|
|
|
+ *
|
|
|
+ * Whenever we network code a packet we have to check whether we received it in
|
|
|
+ * a network coded form. If so, we may not be able to use it for coding because
|
|
|
+ * some neighbors may also have received (overheard) the packet in the network
|
|
|
+ * coded form without being able to decode it. It is hard to know which of the
|
|
|
+ * neighboring nodes was able to decode the packet, therefore we can only
|
|
|
+ * re-code the packet if the source of the previous encoded packet is involved.
|
|
|
+ * Since the source encoded the packet we can be certain it has all necessary
|
|
|
+ * decode information.
|
|
|
+ *
|
|
|
+ * Returns true if coding of a decoded packet is allowed.
|
|
|
+ */
|
|
|
+static bool batadv_nc_skb_coding_possible(struct sk_buff *skb,
|
|
|
+ uint8_t *dst, uint8_t *src)
|
|
|
+{
|
|
|
+ if (BATADV_SKB_CB(skb)->decoded && !batadv_compare_eth(dst, src))
|
|
|
+ return false;
|
|
|
+ else
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * batadv_nc_path_search - Find the coding path matching in_nc_node and
|
|
|
+ * out_nc_node to retrieve a buffered packet that can be used for coding.
|
|
|
+ * @bat_priv: the bat priv with all the soft interface information
|
|
|
+ * @in_nc_node: pointer to skb next hop's neighbor nc node
|
|
|
+ * @out_nc_node: pointer to skb source's neighbor nc node
|
|
|
+ * @skb: data skb to forward
|
|
|
+ * @eth_dst: next hop mac address of skb
|
|
|
+ *
|
|
|
+ * Returns true if coding of a decoded skb is allowed.
|
|
|
+ */
|
|
|
+static struct batadv_nc_packet *
|
|
|
+batadv_nc_path_search(struct batadv_priv *bat_priv,
|
|
|
+ struct batadv_nc_node *in_nc_node,
|
|
|
+ struct batadv_nc_node *out_nc_node,
|
|
|
+ struct sk_buff *skb,
|
|
|
+ uint8_t *eth_dst)
|
|
|
+{
|
|
|
+ struct batadv_nc_path *nc_path, nc_path_key;
|
|
|
+ struct batadv_nc_packet *nc_packet_out = NULL;
|
|
|
+ struct batadv_nc_packet *nc_packet, *nc_packet_tmp;
|
|
|
+ struct batadv_hashtable *hash = bat_priv->nc.coding_hash;
|
|
|
+ int idx;
|
|
|
+
|
|
|
+ if (!hash)
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ /* Create almost path key */
|
|
|
+ batadv_nc_hash_key_gen(&nc_path_key, in_nc_node->addr,
|
|
|
+ out_nc_node->addr);
|
|
|
+ idx = batadv_nc_hash_choose(&nc_path_key, hash->size);
|
|
|
+
|
|
|
+ /* Check for coding opportunities in this nc_path */
|
|
|
+ rcu_read_lock();
|
|
|
+ hlist_for_each_entry_rcu(nc_path, &hash->table[idx], hash_entry) {
|
|
|
+ if (!batadv_compare_eth(nc_path->prev_hop, in_nc_node->addr))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ if (!batadv_compare_eth(nc_path->next_hop, out_nc_node->addr))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ spin_lock_bh(&nc_path->packet_list_lock);
|
|
|
+ if (list_empty(&nc_path->packet_list)) {
|
|
|
+ spin_unlock_bh(&nc_path->packet_list_lock);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ list_for_each_entry_safe(nc_packet, nc_packet_tmp,
|
|
|
+ &nc_path->packet_list, list) {
|
|
|
+ if (!batadv_nc_skb_coding_possible(nc_packet->skb,
|
|
|
+ eth_dst,
|
|
|
+ in_nc_node->addr))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ /* Coding opportunity is found! */
|
|
|
+ list_del(&nc_packet->list);
|
|
|
+ nc_packet_out = nc_packet;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ spin_unlock_bh(&nc_path->packet_list_lock);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ rcu_read_unlock();
|
|
|
+
|
|
|
+ return nc_packet_out;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * batadv_nc_skb_src_search - Loops through the list of neighoring nodes of the
|
|
|
+ * skb's sender (may be equal to the originator).
|
|
|
+ * @bat_priv: the bat priv with all the soft interface information
|
|
|
+ * @skb: data skb to forward
|
|
|
+ * @eth_dst: next hop mac address of skb
|
|
|
+ * @eth_src: source mac address of skb
|
|
|
+ * @in_nc_node: pointer to skb next hop's neighbor nc node
|
|
|
+ *
|
|
|
+ * Returns an nc packet if a suitable coding packet was found, NULL otherwise.
|
|
|
+ */
|
|
|
+static struct batadv_nc_packet *
|
|
|
+batadv_nc_skb_src_search(struct batadv_priv *bat_priv,
|
|
|
+ struct sk_buff *skb,
|
|
|
+ uint8_t *eth_dst,
|
|
|
+ uint8_t *eth_src,
|
|
|
+ struct batadv_nc_node *in_nc_node)
|
|
|
+{
|
|
|
+ struct batadv_orig_node *orig_node;
|
|
|
+ struct batadv_nc_node *out_nc_node;
|
|
|
+ struct batadv_nc_packet *nc_packet = NULL;
|
|
|
+
|
|
|
+ orig_node = batadv_orig_hash_find(bat_priv, eth_src);
|
|
|
+ if (!orig_node)
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ rcu_read_lock();
|
|
|
+ list_for_each_entry_rcu(out_nc_node,
|
|
|
+ &orig_node->out_coding_list, list) {
|
|
|
+ /* Check if the skb is decoded and if recoding is possible */
|
|
|
+ if (!batadv_nc_skb_coding_possible(skb,
|
|
|
+ out_nc_node->addr, eth_src))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ /* Search for an opportunity in this nc_path */
|
|
|
+ nc_packet = batadv_nc_path_search(bat_priv, in_nc_node,
|
|
|
+ out_nc_node, skb, eth_dst);
|
|
|
+ if (nc_packet)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ rcu_read_unlock();
|
|
|
+
|
|
|
+ batadv_orig_node_free_ref(orig_node);
|
|
|
+ return nc_packet;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * batadv_nc_skb_dst_search - Loops through list of neighboring nodes to dst.
|
|
|
+ * @skb: data skb to forward
|
|
|
+ * @neigh_node: next hop to forward packet to
|
|
|
+ * @ethhdr: pointer to the ethernet header inside the skb
|
|
|
+ *
|
|
|
+ * Loops through list of neighboring nodes the next hop has a good connection to
|
|
|
+ * (receives OGMs with a sufficient quality). We need to find a neighbor of our
|
|
|
+ * next hop that potentially sent a packet which our next hop also received
|
|
|
+ * (overheard) and has stored for later decoding.
|
|
|
+ *
|
|
|
+ * Returns true if the skb was consumed (encoded packet sent) or false otherwise
|
|
|
+ */
|
|
|
+static bool batadv_nc_skb_dst_search(struct sk_buff *skb,
|
|
|
+ struct batadv_neigh_node *neigh_node,
|
|
|
+ struct ethhdr *ethhdr)
|
|
|
+{
|
|
|
+ struct net_device *netdev = neigh_node->if_incoming->soft_iface;
|
|
|
+ struct batadv_priv *bat_priv = netdev_priv(netdev);
|
|
|
+ struct batadv_orig_node *orig_node = neigh_node->orig_node;
|
|
|
+ struct batadv_nc_node *nc_node;
|
|
|
+ struct batadv_nc_packet *nc_packet = NULL;
|
|
|
+
|
|
|
+ rcu_read_lock();
|
|
|
+ list_for_each_entry_rcu(nc_node, &orig_node->in_coding_list, list) {
|
|
|
+ /* Search for coding opportunity with this in_nc_node */
|
|
|
+ nc_packet = batadv_nc_skb_src_search(bat_priv, skb,
|
|
|
+ neigh_node->addr,
|
|
|
+ ethhdr->h_source, nc_node);
|
|
|
+
|
|
|
+ /* Opportunity was found, so stop searching */
|
|
|
+ if (nc_packet)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ rcu_read_unlock();
|
|
|
+
|
|
|
+ if (!nc_packet)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ /* Code and send packets */
|
|
|
+ if (batadv_nc_code_packets(bat_priv, skb, ethhdr, nc_packet,
|
|
|
+ neigh_node))
|
|
|
+ return true;
|
|
|
+
|
|
|
+ /* out of mem ? Coding failed - we have to free the buffered packet
|
|
|
+ * to avoid memleaks. The skb passed as argument will be dealt with
|
|
|
+ * by the calling function.
|
|
|
+ */
|
|
|
+ batadv_nc_send_packet(nc_packet);
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* batadv_nc_skb_add_to_path - buffer skb for later encoding / decoding
|
|
|
* @skb: skb to add to path
|
|
@@ -862,6 +1259,10 @@ bool batadv_nc_skb_forward(struct sk_buff *skb,
|
|
|
if (packet->header.packet_type != BATADV_UNICAST)
|
|
|
goto out;
|
|
|
|
|
|
+ /* Try to find a coding opportunity and send the skb if one is found */
|
|
|
+ if (batadv_nc_skb_dst_search(skb, neigh_node, ethhdr))
|
|
|
+ return true;
|
|
|
+
|
|
|
/* Find or create a nc_path for this src-dst pair */
|
|
|
nc_path = batadv_nc_get_path(bat_priv,
|
|
|
bat_priv->nc.coding_hash,
|