|
@@ -20,10 +20,14 @@
|
|
|
#include <linux/debugfs.h>
|
|
|
|
|
|
#include "main.h"
|
|
|
+#include "hash.h"
|
|
|
#include "network-coding.h"
|
|
|
+#include "send.h"
|
|
|
#include "originator.h"
|
|
|
#include "hard-interface.h"
|
|
|
|
|
|
+static struct lock_class_key batadv_nc_coding_hash_lock_class_key;
|
|
|
+
|
|
|
static void batadv_nc_worker(struct work_struct *work);
|
|
|
|
|
|
/**
|
|
@@ -42,10 +46,25 @@ static void batadv_nc_start_timer(struct batadv_priv *bat_priv)
|
|
|
*/
|
|
|
int batadv_nc_init(struct batadv_priv *bat_priv)
|
|
|
{
|
|
|
+ bat_priv->nc.timestamp_fwd_flush = jiffies;
|
|
|
+
|
|
|
+ if (bat_priv->nc.coding_hash)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ bat_priv->nc.coding_hash = batadv_hash_new(128);
|
|
|
+ if (!bat_priv->nc.coding_hash)
|
|
|
+ goto err;
|
|
|
+
|
|
|
+ batadv_hash_set_lock_class(bat_priv->nc.coding_hash,
|
|
|
+ &batadv_nc_coding_hash_lock_class_key);
|
|
|
+
|
|
|
INIT_DELAYED_WORK(&bat_priv->nc.work, batadv_nc_worker);
|
|
|
batadv_nc_start_timer(bat_priv);
|
|
|
|
|
|
return 0;
|
|
|
+
|
|
|
+err:
|
|
|
+ return -ENOMEM;
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -56,6 +75,7 @@ void batadv_nc_init_bat_priv(struct batadv_priv *bat_priv)
|
|
|
{
|
|
|
atomic_set(&bat_priv->network_coding, 1);
|
|
|
bat_priv->nc.min_tq = 200;
|
|
|
+ bat_priv->nc.max_fwd_delay = 10;
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -95,6 +115,30 @@ static void batadv_nc_node_free_ref(struct batadv_nc_node *nc_node)
|
|
|
call_rcu(&nc_node->rcu, batadv_nc_node_free_rcu);
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * batadv_nc_path_free_ref - decrements the nc path refcounter and possibly
|
|
|
+ * frees it
|
|
|
+ * @nc_path: the nc node to free
|
|
|
+ */
|
|
|
+static void batadv_nc_path_free_ref(struct batadv_nc_path *nc_path)
|
|
|
+{
|
|
|
+ if (atomic_dec_and_test(&nc_path->refcount))
|
|
|
+ kfree_rcu(nc_path, rcu);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * batadv_nc_packet_free - frees nc packet
|
|
|
+ * @nc_packet: the nc packet to free
|
|
|
+ */
|
|
|
+static void batadv_nc_packet_free(struct batadv_nc_packet *nc_packet)
|
|
|
+{
|
|
|
+ if (nc_packet->skb)
|
|
|
+ kfree_skb(nc_packet->skb);
|
|
|
+
|
|
|
+ batadv_nc_path_free_ref(nc_packet->nc_path);
|
|
|
+ kfree(nc_packet);
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* batadv_nc_to_purge_nc_node - checks whether an nc node has to be purged
|
|
|
* @bat_priv: the bat priv with all the soft interface information
|
|
@@ -111,6 +155,26 @@ static bool batadv_nc_to_purge_nc_node(struct batadv_priv *bat_priv,
|
|
|
return batadv_has_timed_out(nc_node->last_seen, BATADV_NC_NODE_TIMEOUT);
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * batadv_nc_to_purge_nc_path_coding - checks whether an nc path has timed out
|
|
|
+ * @bat_priv: the bat priv with all the soft interface information
|
|
|
+ * @nc_path: the nc path to check
|
|
|
+ *
|
|
|
+ * Returns true if the entry has to be purged now, false otherwise
|
|
|
+ */
|
|
|
+static bool batadv_nc_to_purge_nc_path_coding(struct batadv_priv *bat_priv,
|
|
|
+ struct batadv_nc_path *nc_path)
|
|
|
+{
|
|
|
+ if (atomic_read(&bat_priv->mesh_state) != BATADV_MESH_ACTIVE)
|
|
|
+ return true;
|
|
|
+
|
|
|
+ /* purge the path when no packets has been added for 10 times the
|
|
|
+ * max_fwd_delay time
|
|
|
+ */
|
|
|
+ return batadv_has_timed_out(nc_path->last_valid,
|
|
|
+ bat_priv->nc.max_fwd_delay * 10);
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* batadv_nc_purge_orig_nc_nodes - go through list of nc nodes and purge stale
|
|
|
* entries
|
|
@@ -202,6 +266,262 @@ static void batadv_nc_purge_orig_hash(struct batadv_priv *bat_priv)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * batadv_nc_purge_paths - traverse all nc paths part of the hash and remove
|
|
|
+ * unused ones
|
|
|
+ * @bat_priv: the bat priv with all the soft interface information
|
|
|
+ * @hash: hash table containing the nc paths to check
|
|
|
+ * @to_purge: function in charge to decide whether an entry has to be purged or
|
|
|
+ * not. This function takes the nc node as argument and has to return
|
|
|
+ * a boolean value: true is the entry has to be deleted, false
|
|
|
+ * otherwise
|
|
|
+ */
|
|
|
+static void batadv_nc_purge_paths(struct batadv_priv *bat_priv,
|
|
|
+ struct batadv_hashtable *hash,
|
|
|
+ bool (*to_purge)(struct batadv_priv *,
|
|
|
+ struct batadv_nc_path *))
|
|
|
+{
|
|
|
+ struct hlist_head *head;
|
|
|
+ struct hlist_node *node_tmp;
|
|
|
+ struct batadv_nc_path *nc_path;
|
|
|
+ spinlock_t *lock; /* Protects lists in hash */
|
|
|
+ uint32_t i;
|
|
|
+
|
|
|
+ for (i = 0; i < hash->size; i++) {
|
|
|
+ head = &hash->table[i];
|
|
|
+ lock = &hash->list_locks[i];
|
|
|
+
|
|
|
+ /* For each nc_path in this bin */
|
|
|
+ spin_lock_bh(lock);
|
|
|
+ hlist_for_each_entry_safe(nc_path, node_tmp, head, hash_entry) {
|
|
|
+ /* if an helper function has been passed as parameter,
|
|
|
+ * ask it if the entry has to be purged or not
|
|
|
+ */
|
|
|
+ if (to_purge && !to_purge(bat_priv, nc_path))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ /* purging an non-empty nc_path should never happen, but
|
|
|
+ * is observed under high CPU load. Delay the purging
|
|
|
+ * until next iteration to allow the packet_list to be
|
|
|
+ * emptied first.
|
|
|
+ */
|
|
|
+ if (!unlikely(list_empty(&nc_path->packet_list))) {
|
|
|
+ net_ratelimited_function(printk,
|
|
|
+ KERN_WARNING
|
|
|
+ "Skipping free of non-empty nc_path (%pM -> %pM)!\n",
|
|
|
+ nc_path->prev_hop,
|
|
|
+ nc_path->next_hop);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* nc_path is unused, so remove it */
|
|
|
+ batadv_dbg(BATADV_DBG_NC, bat_priv,
|
|
|
+ "Remove nc_path %pM -> %pM\n",
|
|
|
+ nc_path->prev_hop, nc_path->next_hop);
|
|
|
+ hlist_del_rcu(&nc_path->hash_entry);
|
|
|
+ batadv_nc_path_free_ref(nc_path);
|
|
|
+ }
|
|
|
+ spin_unlock_bh(lock);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * batadv_nc_hash_key_gen - computes the nc_path hash key
|
|
|
+ * @key: buffer to hold the final hash key
|
|
|
+ * @src: source ethernet mac address going into the hash key
|
|
|
+ * @dst: destination ethernet mac address going into the hash key
|
|
|
+ */
|
|
|
+static void batadv_nc_hash_key_gen(struct batadv_nc_path *key, const char *src,
|
|
|
+ const char *dst)
|
|
|
+{
|
|
|
+ memcpy(key->prev_hop, src, sizeof(key->prev_hop));
|
|
|
+ memcpy(key->next_hop, dst, sizeof(key->next_hop));
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * batadv_nc_hash_choose - compute the hash value for an nc path
|
|
|
+ * @data: data to hash
|
|
|
+ * @size: size of the hash table
|
|
|
+ *
|
|
|
+ * Returns the selected index in the hash table for the given data.
|
|
|
+ */
|
|
|
+static uint32_t batadv_nc_hash_choose(const void *data, uint32_t size)
|
|
|
+{
|
|
|
+ const struct batadv_nc_path *nc_path = data;
|
|
|
+ uint32_t hash = 0;
|
|
|
+
|
|
|
+ hash = batadv_hash_bytes(hash, &nc_path->prev_hop,
|
|
|
+ sizeof(nc_path->prev_hop));
|
|
|
+ hash = batadv_hash_bytes(hash, &nc_path->next_hop,
|
|
|
+ sizeof(nc_path->next_hop));
|
|
|
+
|
|
|
+ hash += (hash << 3);
|
|
|
+ hash ^= (hash >> 11);
|
|
|
+ hash += (hash << 15);
|
|
|
+
|
|
|
+ return hash % size;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * batadv_nc_hash_compare - comparing function used in the network coding hash
|
|
|
+ * tables
|
|
|
+ * @node: node in the local table
|
|
|
+ * @data2: second object to compare the node to
|
|
|
+ *
|
|
|
+ * Returns 1 if the two entry are the same, 0 otherwise
|
|
|
+ */
|
|
|
+static int batadv_nc_hash_compare(const struct hlist_node *node,
|
|
|
+ const void *data2)
|
|
|
+{
|
|
|
+ const struct batadv_nc_path *nc_path1, *nc_path2;
|
|
|
+
|
|
|
+ nc_path1 = container_of(node, struct batadv_nc_path, hash_entry);
|
|
|
+ nc_path2 = data2;
|
|
|
+
|
|
|
+ /* Return 1 if the two keys are identical */
|
|
|
+ if (memcmp(nc_path1->prev_hop, nc_path2->prev_hop,
|
|
|
+ sizeof(nc_path1->prev_hop)) != 0)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ if (memcmp(nc_path1->next_hop, nc_path2->next_hop,
|
|
|
+ sizeof(nc_path1->next_hop)) != 0)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * batadv_nc_hash_find - search for an existing nc path and return it
|
|
|
+ * @hash: hash table containing the nc path
|
|
|
+ * @data: search key
|
|
|
+ *
|
|
|
+ * Returns the nc_path if found, NULL otherwise.
|
|
|
+ */
|
|
|
+static struct batadv_nc_path *
|
|
|
+batadv_nc_hash_find(struct batadv_hashtable *hash,
|
|
|
+ void *data)
|
|
|
+{
|
|
|
+ struct hlist_head *head;
|
|
|
+ struct batadv_nc_path *nc_path, *nc_path_tmp = NULL;
|
|
|
+ int index;
|
|
|
+
|
|
|
+ if (!hash)
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ index = batadv_nc_hash_choose(data, hash->size);
|
|
|
+ head = &hash->table[index];
|
|
|
+
|
|
|
+ rcu_read_lock();
|
|
|
+ hlist_for_each_entry_rcu(nc_path, head, hash_entry) {
|
|
|
+ if (!batadv_nc_hash_compare(&nc_path->hash_entry, data))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ if (!atomic_inc_not_zero(&nc_path->refcount))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ nc_path_tmp = nc_path;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ rcu_read_unlock();
|
|
|
+
|
|
|
+ return nc_path_tmp;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * batadv_nc_send_packet - send non-coded packet and free nc_packet struct
|
|
|
+ * @nc_packet: the nc packet to send
|
|
|
+ */
|
|
|
+static void batadv_nc_send_packet(struct batadv_nc_packet *nc_packet)
|
|
|
+{
|
|
|
+ batadv_send_skb_packet(nc_packet->skb,
|
|
|
+ nc_packet->neigh_node->if_incoming,
|
|
|
+ nc_packet->nc_path->next_hop);
|
|
|
+ nc_packet->skb = NULL;
|
|
|
+ batadv_nc_packet_free(nc_packet);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * batadv_nc_fwd_flush - Checks the timestamp of the given nc packet.
|
|
|
+ * @bat_priv: the bat priv with all the soft interface information
|
|
|
+ * @nc_path: the nc path the packet belongs to
|
|
|
+ * @nc_packet: the nc packet to be checked
|
|
|
+ *
|
|
|
+ * Checks whether the given nc packet has hit its forward timeout. If so, the
|
|
|
+ * packet is no longer delayed, immediately sent and the entry deleted from the
|
|
|
+ * queue. Has to be called with the appropriate locks.
|
|
|
+ *
|
|
|
+ * Returns false as soon as the entry in the fifo queue has not been timed out
|
|
|
+ * yet and true otherwise.
|
|
|
+ */
|
|
|
+static bool batadv_nc_fwd_flush(struct batadv_priv *bat_priv,
|
|
|
+ struct batadv_nc_path *nc_path,
|
|
|
+ struct batadv_nc_packet *nc_packet)
|
|
|
+{
|
|
|
+ unsigned long timeout = bat_priv->nc.max_fwd_delay;
|
|
|
+
|
|
|
+ /* Packets are added to tail, so the remaining packets did not time
|
|
|
+ * out and we can stop processing the current queue
|
|
|
+ */
|
|
|
+ if (atomic_read(&bat_priv->mesh_state) == BATADV_MESH_ACTIVE &&
|
|
|
+ !batadv_has_timed_out(nc_packet->timestamp, timeout))
|
|
|
+ return false;
|
|
|
+
|
|
|
+ /* Send packet */
|
|
|
+ batadv_inc_counter(bat_priv, BATADV_CNT_FORWARD);
|
|
|
+ batadv_add_counter(bat_priv, BATADV_CNT_FORWARD_BYTES,
|
|
|
+ nc_packet->skb->len + ETH_HLEN);
|
|
|
+ list_del(&nc_packet->list);
|
|
|
+ batadv_nc_send_packet(nc_packet);
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * batadv_nc_process_nc_paths - traverse given nc packet pool and free timed out
|
|
|
+ * nc packets
|
|
|
+ * @bat_priv: the bat priv with all the soft interface information
|
|
|
+ * @hash: to be processed hash table
|
|
|
+ * @process_fn: Function called to process given nc packet. Should return true
|
|
|
+ * to encourage this function to proceed with the next packet.
|
|
|
+ * Otherwise the rest of the current queue is skipped.
|
|
|
+ */
|
|
|
+static void
|
|
|
+batadv_nc_process_nc_paths(struct batadv_priv *bat_priv,
|
|
|
+ struct batadv_hashtable *hash,
|
|
|
+ bool (*process_fn)(struct batadv_priv *,
|
|
|
+ struct batadv_nc_path *,
|
|
|
+ struct batadv_nc_packet *))
|
|
|
+{
|
|
|
+ struct hlist_head *head;
|
|
|
+ struct batadv_nc_packet *nc_packet, *nc_packet_tmp;
|
|
|
+ struct batadv_nc_path *nc_path;
|
|
|
+ bool ret;
|
|
|
+ int i;
|
|
|
+
|
|
|
+ if (!hash)
|
|
|
+ return;
|
|
|
+
|
|
|
+ /* Loop hash table bins */
|
|
|
+ for (i = 0; i < hash->size; i++) {
|
|
|
+ head = &hash->table[i];
|
|
|
+
|
|
|
+ /* Loop coding paths */
|
|
|
+ rcu_read_lock();
|
|
|
+ hlist_for_each_entry_rcu(nc_path, head, hash_entry) {
|
|
|
+ /* Loop packets */
|
|
|
+ spin_lock_bh(&nc_path->packet_list_lock);
|
|
|
+ list_for_each_entry_safe(nc_packet, nc_packet_tmp,
|
|
|
+ &nc_path->packet_list, list) {
|
|
|
+ ret = process_fn(bat_priv, nc_path, nc_packet);
|
|
|
+ if (!ret)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ spin_unlock_bh(&nc_path->packet_list_lock);
|
|
|
+ }
|
|
|
+ rcu_read_unlock();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* batadv_nc_worker - periodic task for house keeping related to network coding
|
|
|
* @work: kernel work struct
|
|
@@ -211,12 +531,23 @@ static void batadv_nc_worker(struct work_struct *work)
|
|
|
struct delayed_work *delayed_work;
|
|
|
struct batadv_priv_nc *priv_nc;
|
|
|
struct batadv_priv *bat_priv;
|
|
|
+ unsigned long timeout;
|
|
|
|
|
|
delayed_work = container_of(work, struct delayed_work, work);
|
|
|
priv_nc = container_of(delayed_work, struct batadv_priv_nc, work);
|
|
|
bat_priv = container_of(priv_nc, struct batadv_priv, nc);
|
|
|
|
|
|
batadv_nc_purge_orig_hash(bat_priv);
|
|
|
+ batadv_nc_purge_paths(bat_priv, bat_priv->nc.coding_hash,
|
|
|
+ batadv_nc_to_purge_nc_path_coding);
|
|
|
+
|
|
|
+ timeout = bat_priv->nc.max_fwd_delay;
|
|
|
+
|
|
|
+ if (batadv_has_timed_out(bat_priv->nc.timestamp_fwd_flush, timeout)) {
|
|
|
+ batadv_nc_process_nc_paths(bat_priv, bat_priv->nc.coding_hash,
|
|
|
+ batadv_nc_fwd_flush);
|
|
|
+ bat_priv->nc.timestamp_fwd_flush = jiffies;
|
|
|
+ }
|
|
|
|
|
|
/* Schedule a new check */
|
|
|
batadv_nc_start_timer(bat_priv);
|
|
@@ -406,6 +737,155 @@ out:
|
|
|
batadv_nc_node_free_ref(out_nc_node);
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * batadv_nc_get_path - get existing nc_path or allocate a new one
|
|
|
+ * @bat_priv: the bat priv with all the soft interface information
|
|
|
+ * @hash: hash table containing the nc path
|
|
|
+ * @src: ethernet source address - first half of the nc path search key
|
|
|
+ * @dst: ethernet destination address - second half of the nc path search key
|
|
|
+ *
|
|
|
+ * Returns pointer to nc_path if the path was found or created, returns NULL
|
|
|
+ * on error.
|
|
|
+ */
|
|
|
+static struct batadv_nc_path *batadv_nc_get_path(struct batadv_priv *bat_priv,
|
|
|
+ struct batadv_hashtable *hash,
|
|
|
+ uint8_t *src,
|
|
|
+ uint8_t *dst)
|
|
|
+{
|
|
|
+ int hash_added;
|
|
|
+ struct batadv_nc_path *nc_path, nc_path_key;
|
|
|
+
|
|
|
+ batadv_nc_hash_key_gen(&nc_path_key, src, dst);
|
|
|
+
|
|
|
+ /* Search for existing nc_path */
|
|
|
+ nc_path = batadv_nc_hash_find(hash, (void *)&nc_path_key);
|
|
|
+
|
|
|
+ if (nc_path) {
|
|
|
+ /* Set timestamp to delay removal of nc_path */
|
|
|
+ nc_path->last_valid = jiffies;
|
|
|
+ return nc_path;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* No existing nc_path was found; create a new */
|
|
|
+ nc_path = kzalloc(sizeof(*nc_path), GFP_ATOMIC);
|
|
|
+
|
|
|
+ if (!nc_path)
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ /* Initialize nc_path */
|
|
|
+ INIT_LIST_HEAD(&nc_path->packet_list);
|
|
|
+ spin_lock_init(&nc_path->packet_list_lock);
|
|
|
+ atomic_set(&nc_path->refcount, 2);
|
|
|
+ nc_path->last_valid = jiffies;
|
|
|
+ memcpy(nc_path->next_hop, dst, ETH_ALEN);
|
|
|
+ memcpy(nc_path->prev_hop, src, ETH_ALEN);
|
|
|
+
|
|
|
+ batadv_dbg(BATADV_DBG_NC, bat_priv, "Adding nc_path %pM -> %pM\n",
|
|
|
+ nc_path->prev_hop,
|
|
|
+ nc_path->next_hop);
|
|
|
+
|
|
|
+ /* Add nc_path to hash table */
|
|
|
+ hash_added = batadv_hash_add(hash, batadv_nc_hash_compare,
|
|
|
+ batadv_nc_hash_choose, &nc_path_key,
|
|
|
+ &nc_path->hash_entry);
|
|
|
+
|
|
|
+ if (hash_added < 0) {
|
|
|
+ kfree(nc_path);
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ return nc_path;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * batadv_nc_skb_add_to_path - buffer skb for later encoding / decoding
|
|
|
+ * @skb: skb to add to path
|
|
|
+ * @nc_path: path to add skb to
|
|
|
+ * @neigh_node: next hop to forward packet to
|
|
|
+ * @packet_id: checksum to identify packet
|
|
|
+ *
|
|
|
+ * Returns true if the packet was buffered or false in case of an error.
|
|
|
+ */
|
|
|
+static bool batadv_nc_skb_add_to_path(struct sk_buff *skb,
|
|
|
+ struct batadv_nc_path *nc_path,
|
|
|
+ struct batadv_neigh_node *neigh_node,
|
|
|
+ __be32 packet_id)
|
|
|
+{
|
|
|
+ struct batadv_nc_packet *nc_packet;
|
|
|
+
|
|
|
+ nc_packet = kzalloc(sizeof(*nc_packet), GFP_ATOMIC);
|
|
|
+ if (!nc_packet)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ /* Initialize nc_packet */
|
|
|
+ nc_packet->timestamp = jiffies;
|
|
|
+ nc_packet->packet_id = packet_id;
|
|
|
+ nc_packet->skb = skb;
|
|
|
+ nc_packet->neigh_node = neigh_node;
|
|
|
+ nc_packet->nc_path = nc_path;
|
|
|
+
|
|
|
+ /* Add coding packet to list */
|
|
|
+ spin_lock_bh(&nc_path->packet_list_lock);
|
|
|
+ list_add_tail(&nc_packet->list, &nc_path->packet_list);
|
|
|
+ spin_unlock_bh(&nc_path->packet_list_lock);
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * batadv_nc_skb_forward - try to code a packet or add it to the coding packet
|
|
|
+ * buffer
|
|
|
+ * @skb: data skb to forward
|
|
|
+ * @neigh_node: next hop to forward packet to
|
|
|
+ * @ethhdr: pointer to the ethernet header inside the skb
|
|
|
+ *
|
|
|
+ * Returns true if the skb was consumed (encoded packet sent) or false otherwise
|
|
|
+ */
|
|
|
+bool batadv_nc_skb_forward(struct sk_buff *skb,
|
|
|
+ struct batadv_neigh_node *neigh_node,
|
|
|
+ struct ethhdr *ethhdr)
|
|
|
+{
|
|
|
+ const struct net_device *netdev = neigh_node->if_incoming->soft_iface;
|
|
|
+ struct batadv_priv *bat_priv = netdev_priv(netdev);
|
|
|
+ struct batadv_unicast_packet *packet;
|
|
|
+ struct batadv_nc_path *nc_path;
|
|
|
+ __be32 packet_id;
|
|
|
+ u8 *payload;
|
|
|
+
|
|
|
+ /* Check if network coding is enabled */
|
|
|
+ if (!atomic_read(&bat_priv->network_coding))
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ /* We only handle unicast packets */
|
|
|
+ payload = skb_network_header(skb);
|
|
|
+ packet = (struct batadv_unicast_packet *)payload;
|
|
|
+ if (packet->header.packet_type != BATADV_UNICAST)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ /* Find or create a nc_path for this src-dst pair */
|
|
|
+ nc_path = batadv_nc_get_path(bat_priv,
|
|
|
+ bat_priv->nc.coding_hash,
|
|
|
+ ethhdr->h_source,
|
|
|
+ neigh_node->addr);
|
|
|
+
|
|
|
+ if (!nc_path)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ /* Add skb to nc_path */
|
|
|
+ packet_id = batadv_skb_crc32(skb, payload + sizeof(*packet));
|
|
|
+ if (!batadv_nc_skb_add_to_path(skb, nc_path, neigh_node, packet_id))
|
|
|
+ goto free_nc_path;
|
|
|
+
|
|
|
+ /* Packet is consumed */
|
|
|
+ return true;
|
|
|
+
|
|
|
+free_nc_path:
|
|
|
+ batadv_nc_path_free_ref(nc_path);
|
|
|
+out:
|
|
|
+ /* Packet is not consumed */
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* batadv_nc_free - clean up network coding memory
|
|
|
* @bat_priv: the bat priv with all the soft interface information
|
|
@@ -413,6 +893,8 @@ out:
|
|
|
void batadv_nc_free(struct batadv_priv *bat_priv)
|
|
|
{
|
|
|
cancel_delayed_work_sync(&bat_priv->nc.work);
|
|
|
+ batadv_nc_purge_paths(bat_priv, bat_priv->nc.coding_hash, NULL);
|
|
|
+ batadv_hash_destroy(bat_priv->nc.coding_hash);
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -488,6 +970,11 @@ int batadv_nc_init_debugfs(struct batadv_priv *bat_priv)
|
|
|
if (!file)
|
|
|
goto out;
|
|
|
|
|
|
+ file = debugfs_create_u32("max_fwd_delay", S_IRUGO | S_IWUSR, nc_dir,
|
|
|
+ &bat_priv->nc.max_fwd_delay);
|
|
|
+ if (!file)
|
|
|
+ goto out;
|
|
|
+
|
|
|
return 0;
|
|
|
|
|
|
out:
|