Commit 612d2b4f authored by Martin Hundebøll's avatar Martin Hundebøll Committed by Antonio Quartulli

batman-adv: network coding - save overheard and tx packets for decoding

To be able to decode a network coded packet, a node must already know
one of the two coded packets. This is done by buffering skbs before
transmission and buffering packets sniffed with promiscuous mode from
other hosts.

Packets are kept in a buffer similar to the one with forward-skbs: A
hash table, where each entry, which corresponds to a src-dst pair, has a
linked list packets.
Signed-off-by: default avatarMartin Hundebøll <martin@hundeboll.net>
Signed-off-by: default avatarMarek Lindner <lindner_marek@yahoo.de>
Signed-off-by: default avatarAntonio Quartulli <ordex@autistici.org>
parent 3c12de9a
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include "hard-interface.h" #include "hard-interface.h"
static struct lock_class_key batadv_nc_coding_hash_lock_class_key; static struct lock_class_key batadv_nc_coding_hash_lock_class_key;
static struct lock_class_key batadv_nc_decoding_hash_lock_class_key;
static void batadv_nc_worker(struct work_struct *work); static void batadv_nc_worker(struct work_struct *work);
...@@ -47,8 +48,9 @@ static void batadv_nc_start_timer(struct batadv_priv *bat_priv) ...@@ -47,8 +48,9 @@ static void batadv_nc_start_timer(struct batadv_priv *bat_priv)
int batadv_nc_init(struct batadv_priv *bat_priv) int batadv_nc_init(struct batadv_priv *bat_priv)
{ {
bat_priv->nc.timestamp_fwd_flush = jiffies; bat_priv->nc.timestamp_fwd_flush = jiffies;
bat_priv->nc.timestamp_sniffed_purge = jiffies;
if (bat_priv->nc.coding_hash) if (bat_priv->nc.coding_hash || bat_priv->nc.decoding_hash)
return 0; return 0;
bat_priv->nc.coding_hash = batadv_hash_new(128); bat_priv->nc.coding_hash = batadv_hash_new(128);
...@@ -58,6 +60,13 @@ int batadv_nc_init(struct batadv_priv *bat_priv) ...@@ -58,6 +60,13 @@ int batadv_nc_init(struct batadv_priv *bat_priv)
batadv_hash_set_lock_class(bat_priv->nc.coding_hash, batadv_hash_set_lock_class(bat_priv->nc.coding_hash,
&batadv_nc_coding_hash_lock_class_key); &batadv_nc_coding_hash_lock_class_key);
bat_priv->nc.decoding_hash = batadv_hash_new(128);
if (!bat_priv->nc.decoding_hash)
goto err;
batadv_hash_set_lock_class(bat_priv->nc.coding_hash,
&batadv_nc_decoding_hash_lock_class_key);
INIT_DELAYED_WORK(&bat_priv->nc.work, batadv_nc_worker); INIT_DELAYED_WORK(&bat_priv->nc.work, batadv_nc_worker);
batadv_nc_start_timer(bat_priv); batadv_nc_start_timer(bat_priv);
...@@ -76,6 +85,7 @@ void batadv_nc_init_bat_priv(struct batadv_priv *bat_priv) ...@@ -76,6 +85,7 @@ void batadv_nc_init_bat_priv(struct batadv_priv *bat_priv)
atomic_set(&bat_priv->network_coding, 1); atomic_set(&bat_priv->network_coding, 1);
bat_priv->nc.min_tq = 200; bat_priv->nc.min_tq = 200;
bat_priv->nc.max_fwd_delay = 10; bat_priv->nc.max_fwd_delay = 10;
bat_priv->nc.max_buffer_time = 200;
} }
/** /**
...@@ -175,6 +185,26 @@ static bool batadv_nc_to_purge_nc_path_coding(struct batadv_priv *bat_priv, ...@@ -175,6 +185,26 @@ static bool batadv_nc_to_purge_nc_path_coding(struct batadv_priv *bat_priv,
bat_priv->nc.max_fwd_delay * 10); bat_priv->nc.max_fwd_delay * 10);
} }
/**
* batadv_nc_to_purge_nc_path_decoding - 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_decoding(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_buffer time
*/
return batadv_has_timed_out(nc_path->last_valid,
bat_priv->nc.max_buffer_time*10);
}
/** /**
* batadv_nc_purge_orig_nc_nodes - go through list of nc nodes and purge stale * batadv_nc_purge_orig_nc_nodes - go through list of nc nodes and purge stale
* entries * entries
...@@ -440,6 +470,43 @@ static void batadv_nc_send_packet(struct batadv_nc_packet *nc_packet) ...@@ -440,6 +470,43 @@ static void batadv_nc_send_packet(struct batadv_nc_packet *nc_packet)
batadv_nc_packet_free(nc_packet); batadv_nc_packet_free(nc_packet);
} }
/**
* batadv_nc_sniffed_purge - Checks timestamp of given sniffed 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 sniffed (overheard) nc_packet has hit its buffering
* timeout. If so, the packet is no longer kept 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_sniffed_purge(struct batadv_priv *bat_priv,
struct batadv_nc_path *nc_path,
struct batadv_nc_packet *nc_packet)
{
unsigned long timeout = bat_priv->nc.max_buffer_time;
bool res = false;
/* 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))
goto out;
/* purge nc packet */
list_del(&nc_packet->list);
batadv_nc_packet_free(nc_packet);
res = true;
out:
return res;
}
/** /**
* batadv_nc_fwd_flush - Checks the timestamp of the given 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 * @bat_priv: the bat priv with all the soft interface information
...@@ -540,6 +607,8 @@ static void batadv_nc_worker(struct work_struct *work) ...@@ -540,6 +607,8 @@ static void batadv_nc_worker(struct work_struct *work)
batadv_nc_purge_orig_hash(bat_priv); batadv_nc_purge_orig_hash(bat_priv);
batadv_nc_purge_paths(bat_priv, bat_priv->nc.coding_hash, batadv_nc_purge_paths(bat_priv, bat_priv->nc.coding_hash,
batadv_nc_to_purge_nc_path_coding); batadv_nc_to_purge_nc_path_coding);
batadv_nc_purge_paths(bat_priv, bat_priv->nc.decoding_hash,
batadv_nc_to_purge_nc_path_decoding);
timeout = bat_priv->nc.max_fwd_delay; timeout = bat_priv->nc.max_fwd_delay;
...@@ -549,6 +618,13 @@ static void batadv_nc_worker(struct work_struct *work) ...@@ -549,6 +618,13 @@ static void batadv_nc_worker(struct work_struct *work)
bat_priv->nc.timestamp_fwd_flush = jiffies; bat_priv->nc.timestamp_fwd_flush = jiffies;
} }
if (batadv_has_timed_out(bat_priv->nc.timestamp_sniffed_purge,
bat_priv->nc.max_buffer_time)) {
batadv_nc_process_nc_paths(bat_priv, bat_priv->nc.decoding_hash,
batadv_nc_sniffed_purge);
bat_priv->nc.timestamp_sniffed_purge = jiffies;
}
/* Schedule a new check */ /* Schedule a new check */
batadv_nc_start_timer(bat_priv); batadv_nc_start_timer(bat_priv);
} }
...@@ -1142,6 +1218,41 @@ batadv_nc_skb_src_search(struct batadv_priv *bat_priv, ...@@ -1142,6 +1218,41 @@ batadv_nc_skb_src_search(struct batadv_priv *bat_priv,
return nc_packet; return nc_packet;
} }
/**
* batadv_nc_skb_store_before_coding - set the ethernet src and dst of the
* unicast skb before it is stored for use in later decoding
* @bat_priv: the bat priv with all the soft interface information
* @skb: data skb to store
* @eth_dst_new: new destination mac address of skb
*/
static void batadv_nc_skb_store_before_coding(struct batadv_priv *bat_priv,
struct sk_buff *skb,
uint8_t *eth_dst_new)
{
struct ethhdr *ethhdr;
/* Copy skb header to change the mac header */
skb = pskb_copy(skb, GFP_ATOMIC);
if (!skb)
return;
/* Set the mac header as if we actually sent the packet uncoded */
ethhdr = (struct ethhdr *)skb_mac_header(skb);
memcpy(ethhdr->h_source, ethhdr->h_dest, ETH_ALEN);
memcpy(ethhdr->h_dest, eth_dst_new, ETH_ALEN);
/* Set data pointer to MAC header to mimic packets from our tx path */
skb_push(skb, ETH_HLEN);
/* Add the packet to the decoding packet pool */
batadv_nc_skb_store_for_decoding(bat_priv, skb);
/* batadv_nc_skb_store_for_decoding() clones the skb, so we must free
* our ref
*/
kfree_skb(skb);
}
/** /**
* batadv_nc_skb_dst_search - Loops through list of neighboring nodes to dst. * batadv_nc_skb_dst_search - Loops through list of neighboring nodes to dst.
* @skb: data skb to forward * @skb: data skb to forward
...@@ -1181,6 +1292,12 @@ static bool batadv_nc_skb_dst_search(struct sk_buff *skb, ...@@ -1181,6 +1292,12 @@ static bool batadv_nc_skb_dst_search(struct sk_buff *skb,
if (!nc_packet) if (!nc_packet)
return false; return false;
/* Save packets for later decoding */
batadv_nc_skb_store_before_coding(bat_priv, skb,
neigh_node->addr);
batadv_nc_skb_store_before_coding(bat_priv, nc_packet->skb,
nc_packet->neigh_node->addr);
/* Code and send packets */ /* Code and send packets */
if (batadv_nc_code_packets(bat_priv, skb, ethhdr, nc_packet, if (batadv_nc_code_packets(bat_priv, skb, ethhdr, nc_packet,
neigh_node)) neigh_node))
...@@ -1287,6 +1404,87 @@ bool batadv_nc_skb_forward(struct sk_buff *skb, ...@@ -1287,6 +1404,87 @@ bool batadv_nc_skb_forward(struct sk_buff *skb,
return false; return false;
} }
/**
* batadv_nc_skb_store_for_decoding - save a clone of the skb which can be used
* when decoding coded packets
* @bat_priv: the bat priv with all the soft interface information
* @skb: data skb to store
*/
void batadv_nc_skb_store_for_decoding(struct batadv_priv *bat_priv,
struct sk_buff *skb)
{
struct batadv_unicast_packet *packet;
struct batadv_nc_path *nc_path;
struct ethhdr *ethhdr = (struct ethhdr *)skb_mac_header(skb);
__be32 packet_id;
u8 *payload;
/* Check if network coding is enabled */
if (!atomic_read(&bat_priv->network_coding))
goto out;
/* Check for supported packet type */
payload = skb_network_header(skb);
packet = (struct batadv_unicast_packet *)payload;
if (packet->header.packet_type != BATADV_UNICAST)
goto out;
/* Find existing nc_path or create a new */
nc_path = batadv_nc_get_path(bat_priv,
bat_priv->nc.decoding_hash,
ethhdr->h_source,
ethhdr->h_dest);
if (!nc_path)
goto out;
/* Clone skb and adjust skb->data to point at batman header */
skb = skb_clone(skb, GFP_ATOMIC);
if (unlikely(!skb))
goto free_nc_path;
if (unlikely(!pskb_may_pull(skb, ETH_HLEN)))
goto free_skb;
if (unlikely(!skb_pull_rcsum(skb, ETH_HLEN)))
goto free_skb;
/* Add skb to nc_path */
packet_id = batadv_skb_crc32(skb, payload + sizeof(*packet));
if (!batadv_nc_skb_add_to_path(skb, nc_path, NULL, packet_id))
goto free_skb;
batadv_inc_counter(bat_priv, BATADV_CNT_NC_BUFFER);
return;
free_skb:
kfree_skb(skb);
free_nc_path:
batadv_nc_path_free_ref(nc_path);
out:
return;
}
/**
* batadv_nc_skb_store_sniffed_unicast - check if a received unicast packet
* should be saved in the decoding buffer and, if so, store it there
* @bat_priv: the bat priv with all the soft interface information
* @skb: unicast skb to store
*/
void batadv_nc_skb_store_sniffed_unicast(struct batadv_priv *bat_priv,
struct sk_buff *skb)
{
struct ethhdr *ethhdr = (struct ethhdr *)skb_mac_header(skb);
if (batadv_is_my_mac(ethhdr->h_dest))
return;
/* Set data pointer to MAC header to mimic packets from our tx path */
skb_push(skb, ETH_HLEN);
batadv_nc_skb_store_for_decoding(bat_priv, skb);
}
/** /**
* batadv_nc_free - clean up network coding memory * batadv_nc_free - clean up network coding memory
* @bat_priv: the bat priv with all the soft interface information * @bat_priv: the bat priv with all the soft interface information
...@@ -1294,8 +1492,11 @@ bool batadv_nc_skb_forward(struct sk_buff *skb, ...@@ -1294,8 +1492,11 @@ bool batadv_nc_skb_forward(struct sk_buff *skb,
void batadv_nc_free(struct batadv_priv *bat_priv) void batadv_nc_free(struct batadv_priv *bat_priv)
{ {
cancel_delayed_work_sync(&bat_priv->nc.work); cancel_delayed_work_sync(&bat_priv->nc.work);
batadv_nc_purge_paths(bat_priv, bat_priv->nc.coding_hash, NULL); batadv_nc_purge_paths(bat_priv, bat_priv->nc.coding_hash, NULL);
batadv_hash_destroy(bat_priv->nc.coding_hash); batadv_hash_destroy(bat_priv->nc.coding_hash);
batadv_nc_purge_paths(bat_priv, bat_priv->nc.decoding_hash, NULL);
batadv_hash_destroy(bat_priv->nc.decoding_hash);
} }
/** /**
...@@ -1376,6 +1577,11 @@ int batadv_nc_init_debugfs(struct batadv_priv *bat_priv) ...@@ -1376,6 +1577,11 @@ int batadv_nc_init_debugfs(struct batadv_priv *bat_priv)
if (!file) if (!file)
goto out; goto out;
file = debugfs_create_u32("max_buffer_time", S_IRUGO | S_IWUSR, nc_dir,
&bat_priv->nc.max_buffer_time);
if (!file)
goto out;
return 0; return 0;
out: out:
......
...@@ -38,6 +38,10 @@ void batadv_nc_init_orig(struct batadv_orig_node *orig_node); ...@@ -38,6 +38,10 @@ void batadv_nc_init_orig(struct batadv_orig_node *orig_node);
bool batadv_nc_skb_forward(struct sk_buff *skb, bool batadv_nc_skb_forward(struct sk_buff *skb,
struct batadv_neigh_node *neigh_node, struct batadv_neigh_node *neigh_node,
struct ethhdr *ethhdr); struct ethhdr *ethhdr);
void batadv_nc_skb_store_for_decoding(struct batadv_priv *bat_priv,
struct sk_buff *skb);
void batadv_nc_skb_store_sniffed_unicast(struct batadv_priv *bat_priv,
struct sk_buff *skb);
int batadv_nc_nodes_seq_print_text(struct seq_file *seq, void *offset); int batadv_nc_nodes_seq_print_text(struct seq_file *seq, void *offset);
int batadv_nc_init_debugfs(struct batadv_priv *bat_priv); int batadv_nc_init_debugfs(struct batadv_priv *bat_priv);
...@@ -89,6 +93,20 @@ static inline bool batadv_nc_skb_forward(struct sk_buff *skb, ...@@ -89,6 +93,20 @@ static inline bool batadv_nc_skb_forward(struct sk_buff *skb,
return false; return false;
} }
static inline void
batadv_nc_skb_store_for_decoding(struct batadv_priv *bat_priv,
struct sk_buff *skb)
{
return;
}
static inline void
batadv_nc_skb_store_sniffed_unicast(struct batadv_priv *bat_priv,
struct sk_buff *skb)
{
return;
}
static inline int batadv_nc_nodes_seq_print_text(struct seq_file *seq, static inline int batadv_nc_nodes_seq_print_text(struct seq_file *seq,
void *offset) void *offset)
{ {
......
...@@ -1047,7 +1047,7 @@ int batadv_recv_unicast_packet(struct sk_buff *skb, ...@@ -1047,7 +1047,7 @@ int batadv_recv_unicast_packet(struct sk_buff *skb,
struct batadv_unicast_4addr_packet *unicast_4addr_packet; struct batadv_unicast_4addr_packet *unicast_4addr_packet;
uint8_t *orig_addr; uint8_t *orig_addr;
struct batadv_orig_node *orig_node = NULL; struct batadv_orig_node *orig_node = NULL;
int hdr_size = sizeof(*unicast_packet); int check, hdr_size = sizeof(*unicast_packet);
bool is4addr; bool is4addr;
unicast_packet = (struct batadv_unicast_packet *)skb->data; unicast_packet = (struct batadv_unicast_packet *)skb->data;
...@@ -1058,7 +1058,16 @@ int batadv_recv_unicast_packet(struct sk_buff *skb, ...@@ -1058,7 +1058,16 @@ int batadv_recv_unicast_packet(struct sk_buff *skb,
if (is4addr) if (is4addr)
hdr_size = sizeof(*unicast_4addr_packet); hdr_size = sizeof(*unicast_4addr_packet);
if (batadv_check_unicast_packet(skb, hdr_size) < 0) /* function returns -EREMOTE for promiscuous packets */
check = batadv_check_unicast_packet(skb, hdr_size);
/* Even though the packet is not for us, we might save it to use for
* decoding a later received coded packet
*/
if (check == -EREMOTE)
batadv_nc_skb_store_sniffed_unicast(bat_priv, skb);
if (check < 0)
return NET_RX_DROP; return NET_RX_DROP;
if (!batadv_check_unicast_ttvn(bat_priv, skb)) if (!batadv_check_unicast_ttvn(bat_priv, skb))
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include "vis.h" #include "vis.h"
#include "gateway_common.h" #include "gateway_common.h"
#include "originator.h" #include "originator.h"
#include "network-coding.h"
#include <linux/if_ether.h> #include <linux/if_ether.h>
...@@ -39,6 +40,7 @@ int batadv_send_skb_packet(struct sk_buff *skb, ...@@ -39,6 +40,7 @@ int batadv_send_skb_packet(struct sk_buff *skb,
struct batadv_hard_iface *hard_iface, struct batadv_hard_iface *hard_iface,
const uint8_t *dst_addr) const uint8_t *dst_addr)
{ {
struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
struct ethhdr *ethhdr; struct ethhdr *ethhdr;
if (hard_iface->if_status != BATADV_IF_ACTIVE) if (hard_iface->if_status != BATADV_IF_ACTIVE)
...@@ -70,6 +72,9 @@ int batadv_send_skb_packet(struct sk_buff *skb, ...@@ -70,6 +72,9 @@ int batadv_send_skb_packet(struct sk_buff *skb,
skb->dev = hard_iface->net_dev; skb->dev = hard_iface->net_dev;
/* Save a clone of the skb to use when decoding coded packets */
batadv_nc_skb_store_for_decoding(bat_priv, skb);
/* dev_queue_xmit() returns a negative result on error. However on /* dev_queue_xmit() returns a negative result on error. However on
* congestion and traffic shaping, it drops and returns NET_XMIT_DROP * congestion and traffic shaping, it drops and returns NET_XMIT_DROP
* (which is > 0). This will not be treated as an error. * (which is > 0). This will not be treated as an error.
......
...@@ -670,6 +670,7 @@ static const struct { ...@@ -670,6 +670,7 @@ static const struct {
{ "nc_code_bytes" }, { "nc_code_bytes" },
{ "nc_recode" }, { "nc_recode" },
{ "nc_recode_bytes" }, { "nc_recode_bytes" },
{ "nc_buffer" },
#endif #endif
}; };
......
...@@ -279,6 +279,7 @@ struct batadv_bcast_duplist_entry { ...@@ -279,6 +279,7 @@ struct batadv_bcast_duplist_entry {
* @BATADV_CNT_NC_CODE_BYTES: transmitted nc-combined traffic bytes counter * @BATADV_CNT_NC_CODE_BYTES: transmitted nc-combined traffic bytes counter
* @BATADV_CNT_NC_RECODE: transmitted nc-recombined traffic packet counter * @BATADV_CNT_NC_RECODE: transmitted nc-recombined traffic packet counter
* @BATADV_CNT_NC_RECODE_BYTES: transmitted nc-recombined traffic bytes counter * @BATADV_CNT_NC_RECODE_BYTES: transmitted nc-recombined traffic bytes counter
* @BATADV_CNT_NC_BUFFER: counter for packets buffered for later nc decoding
* @BATADV_CNT_NUM: number of traffic counters * @BATADV_CNT_NUM: number of traffic counters
*/ */
enum batadv_counters { enum batadv_counters {
...@@ -311,6 +312,7 @@ enum batadv_counters { ...@@ -311,6 +312,7 @@ enum batadv_counters {
BATADV_CNT_NC_CODE_BYTES, BATADV_CNT_NC_CODE_BYTES,
BATADV_CNT_NC_RECODE, BATADV_CNT_NC_RECODE,
BATADV_CNT_NC_RECODE_BYTES, BATADV_CNT_NC_RECODE_BYTES,
BATADV_CNT_NC_BUFFER,
#endif #endif
BATADV_CNT_NUM, BATADV_CNT_NUM,
}; };
...@@ -453,18 +455,27 @@ struct batadv_priv_dat { ...@@ -453,18 +455,27 @@ struct batadv_priv_dat {
* @debug_dir: dentry for nc subdir in batman-adv directory in debugfs * @debug_dir: dentry for nc subdir in batman-adv directory in debugfs
* @min_tq: only consider neighbors for encoding if neigh_tq > min_tq * @min_tq: only consider neighbors for encoding if neigh_tq > min_tq
* @max_fwd_delay: maximum packet forward delay to allow coding of packets * @max_fwd_delay: maximum packet forward delay to allow coding of packets
* @max_buffer_time: buffer time for sniffed packets used to decoding
* @timestamp_fwd_flush: timestamp of last forward packet queue flush * @timestamp_fwd_flush: timestamp of last forward packet queue flush
* @timestamp_sniffed_purge: timestamp of last sniffed packet queue purge
* @coding_hash: Hash table used to buffer skbs while waiting for another * @coding_hash: Hash table used to buffer skbs while waiting for another
* incoming skb to code it with. Skbs are added to the buffer just before being * incoming skb to code it with. Skbs are added to the buffer just before being
* forwarded in routing.c * forwarded in routing.c
* @decoding_hash: Hash table used to buffer skbs that might be needed to decode
* a received coded skb. The buffer is used for 1) skbs arriving on the
* soft-interface; 2) skbs overheard on the hard-interface; and 3) skbs
* forwarded by batman-adv.
*/ */
struct batadv_priv_nc { struct batadv_priv_nc {
struct delayed_work work; struct delayed_work work;
struct dentry *debug_dir; struct dentry *debug_dir;
u8 min_tq; u8 min_tq;
u32 max_fwd_delay; u32 max_fwd_delay;
u32 max_buffer_time;
unsigned long timestamp_fwd_flush; unsigned long timestamp_fwd_flush;
unsigned long timestamp_sniffed_purge;
struct batadv_hashtable *coding_hash; struct batadv_hashtable *coding_hash;
struct batadv_hashtable *decoding_hash;
}; };
/** /**
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment