Hi,
This patchset introduces aggregation support for BATMAN V - as well as any other packet type with a broadcast destination.
Branch: * https://git.open-mesh.org/batman-adv.git/shortlog/refs/heads/linus/aggregati...
Regards, Linus
---
Changelog:
v3: * moved "Introduce packet type independent TVLV ..." from linus/neighhash to this patchset (this patchset is more important, therefore swapping their order) * removed the two already applied patches * rebased to master
v2: * reset skb->cb on the soft-iface reception path, too [PATCH 5/6] (thanks Sven!)
RFC->v1: * removed tvlv (un)pack ctx wrappers * fixed a crash when deaggregating ARP packets * now compile tested down to 3.2, too (no issues found) * now stress tested with multicast traffic of a larger, public mesh setup
Instead of having one TVLV registration function for OGMs and one for unicast packets, this patch adds a new, generic tvlv handler registratiorn function, which simply uses a packet type as parameter.
For now, this patch only migrates the multicast and gateway tvlv handlers to this new API, as these two have been tested and verified to work by the author.
The benefits of this new API:
* A more unified TVLV handling * Easier to add TVLV capabilities to any new, upcoming packet type * Does not rely on linearized skb data
Further ToDos (for later patches):
* Insert handler hooks for unicast_tvlv packets too and migrate BATADV_TVLV_{DAT,NC,TT,ROAM} to new API after further testing. * Remove old TVLV handler API
Signed-off-by: Linus Lüssing linus.luessing@c0d3.blue
---
Old changelog (from neighborhood patchset):
Changes in v4: * remove unnecessary bug.h include (not needed anymore since the removal of the wrappers in v3)
Changes in v3: * remove unnecessary skb_reset_network_header() call shortly before kfree_skb() batadv_iv_ogm_receive() * remove tvlv (un)pack_ctx wrappers (thanks Sven!)
Changes in v2: * add includes for linux/{bug,printk}.h * update forward declaration to "struct sk_buff;" in tvlv.h * kerneldoc: - change a "@tvlv_type" to "@packet_type", - fixed batadv_tvlv_ogm_unpack_ctx() parameter kerneldoc (thanks Sven!) --- net/batman-adv/bat_iv_ogm.c | 6 +- net/batman-adv/bat_v_ogm.c | 17 +- net/batman-adv/distributed-arp-table.c | 4 +- net/batman-adv/gateway_common.c | 44 +++-- net/batman-adv/multicast.c | 37 +++-- net/batman-adv/network-coding.c | 4 +- net/batman-adv/tvlv.c | 295 ++++++++++++++++++++++++++++++--- net/batman-adv/tvlv.h | 22 ++- net/batman-adv/types.h | 24 ++- 9 files changed, 373 insertions(+), 80 deletions(-)
diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c index 495ba7c..95dfc5c 100644 --- a/net/batman-adv/bat_iv_ogm.c +++ b/net/batman-adv/bat_iv_ogm.c @@ -1492,7 +1492,7 @@ batadv_iv_ogm_process_per_outif(const struct sk_buff *skb, int ogm_offset, }
if (if_outgoing == BATADV_IF_DEFAULT) - batadv_tvlv_ogm_receive(bat_priv, ogm_packet, orig_node); + batadv_tvlv_ogm_receive(bat_priv, skb, orig_node);
/* if sender is a direct neighbor the sender mac equals * originator mac @@ -1837,6 +1837,8 @@ static int batadv_iv_ogm_receive(struct sk_buff *skb, ogm_offset = 0; ogm_packet = (struct batadv_ogm_packet *)skb->data;
+ WARN_ON(skb_network_offset(skb) != 0); + /* unpack the aggregated packets and process them one by one */ while (batadv_iv_ogm_aggr_packet(ogm_offset, skb_headlen(skb), ogm_packet->tvlv_len)) { @@ -1847,6 +1849,8 @@ static int batadv_iv_ogm_receive(struct sk_buff *skb,
packet_pos = skb->data + ogm_offset; ogm_packet = (struct batadv_ogm_packet *)packet_pos; + + skb_set_network_header(skb, ogm_offset); }
ret = NET_RX_SUCCESS; diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c index 03a35c9..cfc9228 100644 --- a/net/batman-adv/bat_v_ogm.c +++ b/net/batman-adv/bat_v_ogm.c @@ -594,7 +594,7 @@ static bool batadv_v_ogm_route_update(struct batadv_priv *bat_priv, /** * batadv_v_ogm_process_per_outif - process a batman v OGM for an outgoing if * @bat_priv: the bat priv with all the soft interface information - * @ethhdr: the Ethernet header of the OGM2 + * @skb: the skb containing the OGM2 * @ogm2: OGM2 structure * @orig_node: Originator structure for which the OGM has been received * @neigh_node: the neigh_node through with the OGM has been received @@ -603,13 +603,15 @@ static bool batadv_v_ogm_route_update(struct batadv_priv *bat_priv, */ static void batadv_v_ogm_process_per_outif(struct batadv_priv *bat_priv, - const struct ethhdr *ethhdr, + const struct sk_buff *skb, const struct batadv_ogm2_packet *ogm2, struct batadv_orig_node *orig_node, struct batadv_neigh_node *neigh_node, struct batadv_hard_iface *if_incoming, struct batadv_hard_iface *if_outgoing) { + const struct ethhdr *ethhdr = eth_hdr(skb); + unsigned int tvlv_offset = sizeof(*ogm2); int seqno_age; bool forward;
@@ -623,11 +625,16 @@ batadv_v_ogm_process_per_outif(struct batadv_priv *bat_priv, return;
/* only unknown & newer OGMs contain TVLVs we are interested in */ - if ((seqno_age > 0) && (if_outgoing == BATADV_IF_DEFAULT)) + if ((seqno_age > 0) && (if_outgoing == BATADV_IF_DEFAULT)) { + batadv_tvlv_containers_process2(bat_priv, skb, BATADV_OGM2, + tvlv_offset, + ntohs(ogm2->tvlv_len), + orig_node); batadv_tvlv_containers_process(bat_priv, true, orig_node, NULL, NULL, (unsigned char *)(ogm2 + 1), ntohs(ogm2->tvlv_len)); + }
/* if the metric update went through, update routes if needed */ forward = batadv_v_ogm_route_update(bat_priv, ethhdr, ogm2, orig_node, @@ -728,7 +735,7 @@ static void batadv_v_ogm_process(const struct sk_buff *skb, int ogm_offset, path_throughput = min_t(u32, link_throughput, ogm_throughput); ogm_packet->throughput = htonl(path_throughput);
- batadv_v_ogm_process_per_outif(bat_priv, ethhdr, ogm_packet, orig_node, + batadv_v_ogm_process_per_outif(bat_priv, skb, ogm_packet, orig_node, neigh_node, if_incoming, BATADV_IF_DEFAULT);
@@ -772,7 +779,7 @@ static void batadv_v_ogm_process(const struct sk_buff *skb, int ogm_offset, continue; }
- batadv_v_ogm_process_per_outif(bat_priv, ethhdr, ogm_packet, + batadv_v_ogm_process_per_outif(bat_priv, skb, ogm_packet, orig_node, neigh_node, if_incoming, hard_iface);
diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c index 013e970..ce2cc7a 100644 --- a/net/batman-adv/distributed-arp-table.c +++ b/net/batman-adv/distributed-arp-table.c @@ -734,7 +734,7 @@ static void batadv_dat_tvlv_ogm_handler_v1(struct batadv_priv *bat_priv, u8 flags, void *tvlv_value, u16 tvlv_value_len) { - if (flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND) + if (flags & BATADV_TVLV_HANDLER_CIFNOTFND) clear_bit(BATADV_ORIG_CAPA_HAS_DAT, &orig->capabilities); else set_bit(BATADV_ORIG_CAPA_HAS_DAT, &orig->capabilities); @@ -776,7 +776,7 @@ int batadv_dat_init(struct batadv_priv *bat_priv)
batadv_tvlv_handler_register(bat_priv, batadv_dat_tvlv_ogm_handler_v1, NULL, BATADV_TVLV_DAT, 1, - BATADV_TVLV_HANDLER_OGM_CIFNOTFND); + BATADV_TVLV_HANDLER_CIFNOTFND); batadv_dat_tvlv_container_update(bat_priv); return 0; } diff --git a/net/batman-adv/gateway_common.c b/net/batman-adv/gateway_common.c index 33940c5..c7ec934 100644 --- a/net/batman-adv/gateway_common.c +++ b/net/batman-adv/gateway_common.c @@ -207,25 +207,26 @@ ssize_t batadv_gw_bandwidth_set(struct net_device *net_dev, char *buff, }
/** - * batadv_gw_tvlv_ogm_handler_v1 - process incoming gateway tvlv container + * batadv_gw_tvlv_ogm_handler - process incoming gateway tvlv container * @bat_priv: the bat priv with all the soft interface information - * @orig: the orig_node of the ogm - * @flags: flags indicating the tvlv state (see batadv_tvlv_handler_flags) * @tvlv_value: tvlv buffer containing the gateway data * @tvlv_value_len: tvlv buffer length + * @ctx: handler specific context information (here: orig_node) + * + * Return: Always NET_RX_SUCCESS. */ -static void batadv_gw_tvlv_ogm_handler_v1(struct batadv_priv *bat_priv, - struct batadv_orig_node *orig, - u8 flags, - void *tvlv_value, u16 tvlv_value_len) +static int batadv_gw_tvlv_ogm_handler(struct batadv_priv *bat_priv, + void *tvlv_value, + u16 tvlv_value_len, + void *ctx) { + struct batadv_orig_node *orig_node = ctx; struct batadv_tvlv_gateway_data gateway, *gateway_ptr;
- /* only fetch the tvlv value if the handler wasn't called via the - * CIFNOTFND flag and if there is data to fetch + /* might either be too small due to a broken packet, + * or zero because no matching TVLV was found in the provided OGM */ - if ((flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND) || - (tvlv_value_len < sizeof(gateway))) { + if (tvlv_value_len < sizeof(gateway)) { gateway.bandwidth_down = 0; gateway.bandwidth_up = 0; } else { @@ -239,12 +240,14 @@ static void batadv_gw_tvlv_ogm_handler_v1(struct batadv_priv *bat_priv, } }
- batadv_gw_node_update(bat_priv, orig, &gateway); + batadv_gw_node_update(bat_priv, orig_node, &gateway);
/* restart gateway selection */ if ((gateway.bandwidth_down != 0) && (atomic_read(&bat_priv->gw.mode) == BATADV_GW_MODE_CLIENT)) - batadv_gw_check_election(bat_priv, orig); + batadv_gw_check_election(bat_priv, orig_node); + + return NET_RX_SUCCESS; }
/** @@ -258,9 +261,12 @@ void batadv_gw_init(struct batadv_priv *bat_priv) else atomic_set(&bat_priv->gw.sel_class, 1);
- batadv_tvlv_handler_register(bat_priv, batadv_gw_tvlv_ogm_handler_v1, - NULL, BATADV_TVLV_GW, 1, - BATADV_TVLV_HANDLER_OGM_CIFNOTFND); + batadv_tvlv_handler_register2(bat_priv, batadv_gw_tvlv_ogm_handler, + BATADV_IV_OGM, BATADV_TVLV_GW, 1, + BATADV_TVLV_HANDLER_CIFNOTFND); + batadv_tvlv_handler_register2(bat_priv, batadv_gw_tvlv_ogm_handler, + BATADV_OGM2, BATADV_TVLV_GW, 1, + BATADV_TVLV_HANDLER_CIFNOTFND); }
/** @@ -270,5 +276,9 @@ void batadv_gw_init(struct batadv_priv *bat_priv) void batadv_gw_free(struct batadv_priv *bat_priv) { batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_GW, 1); - batadv_tvlv_handler_unregister(bat_priv, BATADV_TVLV_GW, 1); + + batadv_tvlv_handler_unregister2(bat_priv, BATADV_IV_OGM, BATADV_TVLV_GW, + 1); + batadv_tvlv_handler_unregister2(bat_priv, BATADV_OGM2, BATADV_TVLV_GW, + 1); } diff --git a/net/batman-adv/multicast.c b/net/batman-adv/multicast.c index d327670..57311fa 100644 --- a/net/batman-adv/multicast.c +++ b/net/batman-adv/multicast.c @@ -1110,23 +1110,23 @@ static void batadv_mcast_want_ipv6_update(struct batadv_priv *bat_priv, /** * batadv_mcast_tvlv_ogm_handler - process incoming multicast tvlv container * @bat_priv: the bat priv with all the soft interface information - * @orig: the orig_node of the ogm - * @flags: flags indicating the tvlv state (see batadv_tvlv_handler_flags) * @tvlv_value: tvlv buffer containing the multicast data * @tvlv_value_len: tvlv buffer length + * @ctx: handler specific context information (here: orig_node) + * + * Return: Always NET_RX_SUCCESS. */ -static void batadv_mcast_tvlv_ogm_handler(struct batadv_priv *bat_priv, - struct batadv_orig_node *orig, - u8 flags, - void *tvlv_value, - u16 tvlv_value_len) +static int batadv_mcast_tvlv_ogm_handler(struct batadv_priv *bat_priv, + void *tvlv_value, + u16 tvlv_value_len, + void *ctx) { - bool orig_mcast_enabled = !(flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND); + struct batadv_orig_node *orig = ctx; + bool orig_mcast_enabled = !!tvlv_value; u8 mcast_flags = BATADV_NO_FLAGS; bool orig_initialized;
- if (orig_mcast_enabled && tvlv_value && - (tvlv_value_len >= sizeof(mcast_flags))) + if (orig_mcast_enabled && (tvlv_value_len >= sizeof(mcast_flags))) mcast_flags = *(u8 *)tvlv_value;
spin_lock_bh(&orig->mcast_handler_lock); @@ -1161,6 +1161,8 @@ static void batadv_mcast_tvlv_ogm_handler(struct batadv_priv *bat_priv,
orig->mcast_flags = mcast_flags; spin_unlock_bh(&orig->mcast_handler_lock); + + return NET_RX_SUCCESS; }
/** @@ -1169,9 +1171,12 @@ static void batadv_mcast_tvlv_ogm_handler(struct batadv_priv *bat_priv, */ void batadv_mcast_init(struct batadv_priv *bat_priv) { - batadv_tvlv_handler_register(bat_priv, batadv_mcast_tvlv_ogm_handler, - NULL, BATADV_TVLV_MCAST, 2, - BATADV_TVLV_HANDLER_OGM_CIFNOTFND); + batadv_tvlv_handler_register2(bat_priv, batadv_mcast_tvlv_ogm_handler, + BATADV_IV_OGM, BATADV_TVLV_MCAST, 2, + BATADV_TVLV_HANDLER_CIFNOTFND); + batadv_tvlv_handler_register2(bat_priv, batadv_mcast_tvlv_ogm_handler, + BATADV_OGM2, BATADV_TVLV_MCAST, 2, + BATADV_TVLV_HANDLER_CIFNOTFND);
INIT_DELAYED_WORK(&bat_priv->mcast.work, batadv_mcast_mla_update); batadv_mcast_start_timer(bat_priv); @@ -1289,7 +1294,11 @@ void batadv_mcast_free(struct batadv_priv *bat_priv) cancel_delayed_work_sync(&bat_priv->mcast.work);
batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_MCAST, 2); - batadv_tvlv_handler_unregister(bat_priv, BATADV_TVLV_MCAST, 2); + + batadv_tvlv_handler_unregister2(bat_priv, BATADV_IV_OGM, + BATADV_TVLV_MCAST, 2); + batadv_tvlv_handler_unregister2(bat_priv, BATADV_OGM2, + BATADV_TVLV_MCAST, 2);
/* safely calling outside of worker, as worker was canceled above */ batadv_mcast_mla_tt_retract(bat_priv, NULL); diff --git a/net/batman-adv/network-coding.c b/net/batman-adv/network-coding.c index e1f6fc7..e340662 100644 --- a/net/batman-adv/network-coding.c +++ b/net/batman-adv/network-coding.c @@ -137,7 +137,7 @@ static void batadv_nc_tvlv_ogm_handler_v1(struct batadv_priv *bat_priv, u8 flags, void *tvlv_value, u16 tvlv_value_len) { - if (flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND) + if (flags & BATADV_TVLV_HANDLER_CIFNOTFND) clear_bit(BATADV_ORIG_CAPA_HAS_NC, &orig->capabilities); else set_bit(BATADV_ORIG_CAPA_HAS_NC, &orig->capabilities); @@ -176,7 +176,7 @@ int batadv_nc_mesh_init(struct batadv_priv *bat_priv)
batadv_tvlv_handler_register(bat_priv, batadv_nc_tvlv_ogm_handler_v1, NULL, BATADV_TVLV_NC, 1, - BATADV_TVLV_HANDLER_OGM_CIFNOTFND); + BATADV_TVLV_HANDLER_CIFNOTFND); batadv_nc_tvlv_container_update(bat_priv); return 0;
diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c index 1d9e267..7dbda3a 100644 --- a/net/batman-adv/tvlv.c +++ b/net/batman-adv/tvlv.c @@ -27,6 +27,7 @@ #include <linux/lockdep.h> #include <linux/netdevice.h> #include <linux/pkt_sched.h> +#include <linux/printk.h> #include <linux/rculist.h> #include <linux/rcupdate.h> #include <linux/skbuff.h> @@ -68,23 +69,28 @@ static void batadv_tvlv_handler_put(struct batadv_tvlv_handler *tvlv_handler) * batadv_tvlv_handler_get - retrieve tvlv handler from the tvlv handler list * based on the provided type and version (both need to match) * @bat_priv: the bat priv with all the soft interface information - * @type: tvlv handler type to look for - * @version: tvlv handler version to look for + * @packet_type: packet type to look for + * @tvlv_type: tvlv handler type to look for + * @tvlv_version: tvlv handler version to look for * * Return: tvlv handler if found or NULL otherwise. */ static struct batadv_tvlv_handler * -batadv_tvlv_handler_get(struct batadv_priv *bat_priv, u8 type, u8 version) +batadv_tvlv_handler_get(struct batadv_priv *bat_priv, int packet_type, + u8 tvlv_type, u8 tvlv_version) { struct batadv_tvlv_handler *tvlv_handler_tmp, *tvlv_handler = NULL;
rcu_read_lock(); hlist_for_each_entry_rcu(tvlv_handler_tmp, &bat_priv->tvlv.handler_list, list) { - if (tvlv_handler_tmp->type != type) + if (tvlv_handler_tmp->packet_type != packet_type) continue;
- if (tvlv_handler_tmp->version != version) + if (tvlv_handler_tmp->tvlv_type != tvlv_type) + continue; + + if (tvlv_handler_tmp->tvlv_version != tvlv_version) continue;
if (!kref_get_unless_zero(&tvlv_handler_tmp->refcount)) @@ -387,7 +393,7 @@ static int batadv_tvlv_call_handler(struct batadv_priv *bat_priv, tvlv_handler->ogm_handler(bat_priv, orig_node, BATADV_NO_FLAGS, tvlv_value, tvlv_value_len); - tvlv_handler->flags |= BATADV_TVLV_HANDLER_OGM_CALLED; + tvlv_handler->flags |= BATADV_TVLV_HANDLER_CALLED; } else { if (!src) return NET_RX_SUCCESS; @@ -407,6 +413,139 @@ static int batadv_tvlv_call_handler(struct batadv_priv *bat_priv, }
/** + * batadv_tvlv_call_handler2 - call the appropriate tvlv handler + * @bat_priv: the bat priv with all the soft interface information + * @packet_type: packet type to look and call for + * @tvlv_type: tvlv handler type to look and call for + * @tvlv_version: tvlv handler version to look and call for + * @tvlv_value: tvlv content + * @tvlv_value_len: tvlv content length + * @ctx: handler specific context information + * + * Return: NET_RX_SUCCESS if handler was found and called successfully, + * NET_RX_DROP otherwise. + */ +static int batadv_tvlv_call_handler2(struct batadv_priv *bat_priv, + u8 packet_type, u8 tvlv_type, + u8 tvlv_version, void *tvlv_value, + u16 tvlv_value_len, void *ctx) +{ + struct batadv_tvlv_handler *tvlv_handler; + int ret; + + tvlv_handler = batadv_tvlv_handler_get(bat_priv, packet_type, tvlv_type, + tvlv_version); + if (!tvlv_handler) + return NET_RX_DROP; + + ret = tvlv_handler->handler(bat_priv, tvlv_value, tvlv_value_len, ctx); + tvlv_handler->flags |= BATADV_TVLV_HANDLER_CALLED; + + batadv_tvlv_handler_put(tvlv_handler); + + return ret; +} + +/** + * batadv_tvlv_call_unfound_handlers - call any handler not called yet + * @bat_priv: the bat priv with all the soft interface information + * @packet_type: the packet type to call handlers of unfound TVLVs for + * @ctx: handler specific context information + * + * For any registered TVLV handler with a CIFNOTFND flag: If a matching + * tvlv type was not found in a specific packet (type) then this calls the + * according handler with an empty (NULL) tvlv_value and tvlv_value_len of + * zero now. + */ +static void batadv_tvlv_call_unfound_handlers(struct batadv_priv *bat_priv, + int packet_type, void *ctx) +{ + struct batadv_tvlv_handler *tvlv_handler; + + rcu_read_lock(); + hlist_for_each_entry_rcu(tvlv_handler, + &bat_priv->tvlv.handler_list, list) { + if (tvlv_handler->packet_type != packet_type) + continue; + + if ((tvlv_handler->flags & BATADV_TVLV_HANDLER_CIFNOTFND) && + !(tvlv_handler->flags & BATADV_TVLV_HANDLER_CALLED)) + tvlv_handler->handler(bat_priv, NULL, 0, ctx); + + tvlv_handler->flags &= ~BATADV_TVLV_HANDLER_CALLED; + } + rcu_read_unlock(); +} + +/** + * batadv_tvlv_containers_process2 - parse and process TVLV content of a packet + * @bat_priv: the bat priv with all the soft interface information + * @skb: the packet to parse and process TVLV data from + * @packet_type: the packet type to call handlers for + * @tvlv_offset: offset from the skb data pointer to the first tvlv header + * @tvlv_value_len: total tvlv content length (sum of all tvlv headers+values) + * @ctx: handler specific context information + * + * This function parses TVLV options of the given skb and tries to call the + * appropriate, registered handlers. + * + * In the end, all not yet called handlers (because no appropriate TVLV was + * found in the packet) which were registered with a CIFNOTFND flag are + * called with empty tvlv_value pointers. + * + * Return: NET_RX_SUCCESS if all TVLVs were known and parsed, as well as + * any TVLV handler called successfully. Returns NET_RX_DROP otherwise. + */ +int batadv_tvlv_containers_process2(struct batadv_priv *bat_priv, + const struct sk_buff *skb, u8 packet_type, + unsigned int tvlv_offset, + u16 tvlv_value_len, void *ctx) +{ + struct batadv_tvlv_hdr *tvlv_hdr, tvlv_hdr_buff; + u8 *tvlv_value, tvlv_value_buff[128]; + u16 tvlv_value_cont_len; + int ret = NET_RX_SUCCESS; + + while (tvlv_value_len >= sizeof(*tvlv_hdr)) { + tvlv_hdr = skb_header_pointer(skb, tvlv_offset, + sizeof(tvlv_hdr_buff), + &tvlv_hdr_buff); + if (!tvlv_hdr) + return NET_RX_DROP; + + tvlv_value_cont_len = ntohs(tvlv_hdr->len); + tvlv_offset += sizeof(*tvlv_hdr); + tvlv_value_len -= sizeof(*tvlv_hdr); + + if (tvlv_value_cont_len > sizeof(tvlv_value_buff)) { + pr_warn_once("batman-adv: TVLVs greater than 128 bytes unsupported for now, ignoring\n"); + goto skip_handler_call; + } + + if (tvlv_value_cont_len > tvlv_value_len) + return NET_RX_DROP; + + tvlv_value = skb_header_pointer(skb, tvlv_offset, + tvlv_value_cont_len, + tvlv_value_buff); + if (!tvlv_value) + return NET_RX_DROP; + + ret |= batadv_tvlv_call_handler2(bat_priv, packet_type, + tvlv_hdr->type, + tvlv_hdr->version, tvlv_value, + tvlv_value_cont_len, ctx); +skip_handler_call: + tvlv_offset += tvlv_value_cont_len; + tvlv_value_len -= tvlv_value_cont_len; + } + + batadv_tvlv_call_unfound_handlers(bat_priv, packet_type, ctx); + + return ret; +} + +/** * batadv_tvlv_containers_process - parse the given tvlv buffer to call the * appropriate handlers * @bat_priv: the bat priv with all the soft interface information @@ -429,7 +568,7 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv, struct batadv_tvlv_handler *tvlv_handler; struct batadv_tvlv_hdr *tvlv_hdr; u16 tvlv_value_cont_len; - u8 cifnotfound = BATADV_TVLV_HANDLER_OGM_CIFNOTFND; + u8 cifnotfound = BATADV_TVLV_HANDLER_CIFNOTFND; int ret = NET_RX_SUCCESS;
while (tvlv_value_len >= sizeof(*tvlv_hdr)) { @@ -441,7 +580,7 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv, if (tvlv_value_cont_len > tvlv_value_len) break;
- tvlv_handler = batadv_tvlv_handler_get(bat_priv, + tvlv_handler = batadv_tvlv_handler_get(bat_priv, -1, tvlv_hdr->type, tvlv_hdr->version);
@@ -461,12 +600,15 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv, rcu_read_lock(); hlist_for_each_entry_rcu(tvlv_handler, &bat_priv->tvlv.handler_list, list) { - if ((tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND) && - !(tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CALLED)) + if (tvlv_handler->packet_type != -1) + continue; + + if ((tvlv_handler->flags & BATADV_TVLV_HANDLER_CIFNOTFND) && + !(tvlv_handler->flags & BATADV_TVLV_HANDLER_CALLED)) tvlv_handler->ogm_handler(bat_priv, orig_node, cifnotfound, NULL, 0);
- tvlv_handler->flags &= ~BATADV_TVLV_HANDLER_OGM_CALLED; + tvlv_handler->flags &= ~BATADV_TVLV_HANDLER_CALLED; } rcu_read_unlock();
@@ -477,27 +619,95 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv, * batadv_tvlv_ogm_receive - process an incoming ogm and call the appropriate * handlers * @bat_priv: the bat priv with all the soft interface information - * @batadv_ogm_packet: ogm packet containing the tvlv containers + * @skb: ogm packet containing the tvlv containers * @orig_node: orig node emitting the ogm packet + * + * Caller needs to ensure that the skb network header points to the appropriate + * OGM header. */ void batadv_tvlv_ogm_receive(struct batadv_priv *bat_priv, - struct batadv_ogm_packet *batadv_ogm_packet, + const struct sk_buff *skb, struct batadv_orig_node *orig_node) { + struct batadv_ogm_packet *ogm_packet; + unsigned int tvlv_offset; void *tvlv_value; u16 tvlv_value_len;
- if (!batadv_ogm_packet) + ogm_packet = (struct batadv_ogm_packet *)skb_network_header(skb); + if (!ogm_packet) return;
- tvlv_value_len = ntohs(batadv_ogm_packet->tvlv_len); + tvlv_value_len = ntohs(ogm_packet->tvlv_len); if (!tvlv_value_len) return;
- tvlv_value = batadv_ogm_packet + 1; + tvlv_offset = skb_network_offset(skb) + sizeof(*ogm_packet); + tvlv_value = ogm_packet + 1;
batadv_tvlv_containers_process(bat_priv, true, orig_node, NULL, NULL, tvlv_value, tvlv_value_len); + batadv_tvlv_containers_process2(bat_priv, skb, BATADV_IV_OGM, + tvlv_offset, tvlv_value_len, orig_node); +} + +/** + * batadv_tvlv_handler_register - register a tvlv handler + * @bat_priv: the bat priv with all the soft interface information + * @handler: TVLV handler callback function + * @packet_type: packet type to register this handler for + * @tvlv_type: tvlv handler type to be registered + * @tvlv_version: tvlv handler version to be registered + * @flags: flags to enable or disable TVLV API behavior + * + * Registers a handler for incoming packets of the provided packet type. + * When a packet of this type with a matching TVLV (both tvlv type and version) + * is received then the registered handler is called with the according TVLV + * value, length and packet context. + * + * If 'flags' is set to BATADV_TVLV_HANDLER_CIFNOTFND: + * Then the handler might be called with an empty tvlv_value (NULL) and + * tvlv_value_len (zero) if a packet with a matching packet type but no + * matching TVLV was received. + */ +void batadv_tvlv_handler_register2(struct batadv_priv *bat_priv, + int (*handler)(struct batadv_priv *bat_priv, + void *tvlv_value, + u16 tvlv_value_len, + void *ctx), + u8 packet_type, u8 tvlv_type, + u8 tvlv_version, u8 flags) +{ + struct batadv_tvlv_handler *tvlv_handler; + + tvlv_handler = batadv_tvlv_handler_get(bat_priv, packet_type, tvlv_type, + tvlv_version); + if (tvlv_handler) { + batadv_tvlv_handler_put(tvlv_handler); + return; + } + + tvlv_handler = kzalloc(sizeof(*tvlv_handler), GFP_ATOMIC); + if (!tvlv_handler) + return; + + tvlv_handler->ogm_handler = NULL; + tvlv_handler->unicast_handler = NULL; + tvlv_handler->handler = handler; + tvlv_handler->packet_type = packet_type; + tvlv_handler->tvlv_type = tvlv_type; + tvlv_handler->tvlv_version = tvlv_version; + tvlv_handler->flags = flags; + kref_init(&tvlv_handler->refcount); + INIT_HLIST_NODE(&tvlv_handler->list); + + spin_lock_bh(&bat_priv->tvlv.handler_list_lock); + kref_get(&tvlv_handler->refcount); + hlist_add_head_rcu(&tvlv_handler->list, &bat_priv->tvlv.handler_list); + spin_unlock_bh(&bat_priv->tvlv.handler_list_lock); + + /* don't return reference to new tvlv_handler */ + batadv_tvlv_handler_put(tvlv_handler); }
/** @@ -510,8 +720,8 @@ void batadv_tvlv_ogm_receive(struct batadv_priv *bat_priv, * @uptr: unicast tvlv handler callback function. This function receives the * source & destination of the unicast packet as well as the tvlv content * to process. - * @type: tvlv handler type to be registered - * @version: tvlv handler version to be registered + * @tvlv_type: tvlv handler type to be registered + * @tvlv_version: tvlv handler version to be registered * @flags: flags to enable or disable TVLV API behavior */ void batadv_tvlv_handler_register(struct batadv_priv *bat_priv, @@ -524,11 +734,13 @@ void batadv_tvlv_handler_register(struct batadv_priv *bat_priv, u8 *src, u8 *dst, void *tvlv_value, u16 tvlv_value_len), - u8 type, u8 version, u8 flags) + u8 tvlv_type, u8 tvlv_version, + u8 flags) { struct batadv_tvlv_handler *tvlv_handler;
- tvlv_handler = batadv_tvlv_handler_get(bat_priv, type, version); + tvlv_handler = batadv_tvlv_handler_get(bat_priv, -1, tvlv_type, + tvlv_version); if (tvlv_handler) { batadv_tvlv_handler_put(tvlv_handler); return; @@ -540,8 +752,10 @@ void batadv_tvlv_handler_register(struct batadv_priv *bat_priv,
tvlv_handler->ogm_handler = optr; tvlv_handler->unicast_handler = uptr; - tvlv_handler->type = type; - tvlv_handler->version = version; + tvlv_handler->handler = NULL; + tvlv_handler->packet_type = -1; + tvlv_handler->tvlv_type = tvlv_type; + tvlv_handler->tvlv_version = tvlv_version; tvlv_handler->flags = flags; kref_init(&tvlv_handler->refcount); INIT_HLIST_NODE(&tvlv_handler->list); @@ -556,18 +770,47 @@ void batadv_tvlv_handler_register(struct batadv_priv *bat_priv, }
/** + * batadv_tvlv_handler_unregister2 - unregister a tvlv handler + * @bat_priv: the bat priv with all the soft interface information + * @packet_type: packet type to unregister for + * @tvlv_type: tvlv handler type to be unregistered + * @tvlv_version: tvlv handler version to be unregistered + * + * Unregisters a TVLV handler based on the provided packet type, tvlv type + * and version (all need to match). + */ +void batadv_tvlv_handler_unregister2(struct batadv_priv *bat_priv, + u8 packet_type, u8 tvlv_type, + u8 tvlv_version) +{ + struct batadv_tvlv_handler *tvlv_handler; + + tvlv_handler = batadv_tvlv_handler_get(bat_priv, packet_type, tvlv_type, + tvlv_version); + if (!tvlv_handler) + return; + + batadv_tvlv_handler_put(tvlv_handler); + spin_lock_bh(&bat_priv->tvlv.handler_list_lock); + hlist_del_rcu(&tvlv_handler->list); + spin_unlock_bh(&bat_priv->tvlv.handler_list_lock); + batadv_tvlv_handler_put(tvlv_handler); +} + +/** * batadv_tvlv_handler_unregister - unregister tvlv handler based on the * provided type and version (both need to match) * @bat_priv: the bat priv with all the soft interface information - * @type: tvlv handler type to be unregistered - * @version: tvlv handler version to be unregistered + * @tvlv_type: tvlv handler type to be unregistered + * @tvlv_version: tvlv handler version to be unregistered */ void batadv_tvlv_handler_unregister(struct batadv_priv *bat_priv, - u8 type, u8 version) + u8 tvlv_type, u8 tvlv_version) { struct batadv_tvlv_handler *tvlv_handler;
- tvlv_handler = batadv_tvlv_handler_get(bat_priv, type, version); + tvlv_handler = batadv_tvlv_handler_get(bat_priv, -1, tvlv_type, + tvlv_version); if (!tvlv_handler) return;
diff --git a/net/batman-adv/tvlv.h b/net/batman-adv/tvlv.h index 4d01400..3496f36 100644 --- a/net/batman-adv/tvlv.h +++ b/net/batman-adv/tvlv.h @@ -22,7 +22,7 @@
#include <linux/types.h>
-struct batadv_ogm_packet; +struct sk_buff;
void batadv_tvlv_container_register(struct batadv_priv *bat_priv, u8 type, u8 version, @@ -31,11 +31,18 @@ u16 batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv, unsigned char **packet_buff, int *packet_buff_len, int packet_min_len); void batadv_tvlv_ogm_receive(struct batadv_priv *bat_priv, - struct batadv_ogm_packet *batadv_ogm_packet, + const struct sk_buff *skb, struct batadv_orig_node *orig_node); void batadv_tvlv_container_unregister(struct batadv_priv *bat_priv, u8 type, u8 version);
+void batadv_tvlv_handler_register2(struct batadv_priv *bat_priv, + int (*handler)(struct batadv_priv *bat_priv, + void *tvlv_value, + u16 tvlv_value_len, + void *ctx), + u8 packet_type, u8 tvlv_type, + u8 tvlv_version, u8 flags); void batadv_tvlv_handler_register(struct batadv_priv *bat_priv, void (*optr)(struct batadv_priv *bat_priv, struct batadv_orig_node *orig, @@ -46,14 +53,21 @@ void batadv_tvlv_handler_register(struct batadv_priv *bat_priv, u8 *src, u8 *dst, void *tvlv_value, u16 tvlv_value_len), - u8 type, u8 version, u8 flags); + u8 tvlv_type, u8 tvlv_version, u8 flags); +void batadv_tvlv_handler_unregister2(struct batadv_priv *bat_priv, + u8 packet_type, u8 tvlv_type, + u8 tvlv_version); void batadv_tvlv_handler_unregister(struct batadv_priv *bat_priv, - u8 type, u8 version); + u8 tvlv_type, u8 tvlv_version); int batadv_tvlv_containers_process(struct batadv_priv *bat_priv, bool ogm_source, struct batadv_orig_node *orig_node, u8 *src, u8 *dst, void *tvlv_buff, u16 tvlv_buff_len); +int batadv_tvlv_containers_process2(struct batadv_priv *bat_priv, + const struct sk_buff *skb, u8 packet_type, + unsigned int tvlv_offset, + u16 tvlv_value_len, void *ctx); void batadv_tvlv_unicast_send(struct batadv_priv *bat_priv, u8 *src, u8 *dst, u8 type, u8 version, void *tvlv_value, u16 tvlv_value_len); diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index ea43a64..e7f25a1 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -1597,8 +1597,11 @@ struct batadv_tvlv_container { * incoming OGM packets * @unicast_handler: handler callback which is given the tvlv payload to process * on incoming unicast tvlv packets - * @type: tvlv type this handler feels responsible for - * @version: tvlv version this handler feels responsible for + * @handler: handler callback which is given the tvlv payload to process on + * incoming packets of the given packet type + * @packet_type: packet type this handler feels responsible for + * @tvlv_type: tvlv type this handler feels responsible for + * @tvlv_version: tvlv version this handler feels responsible for * @flags: tvlv handler flags * @refcount: number of contexts the object is used * @rcu: struct used for freeing in an RCU-safe manner @@ -1611,8 +1614,11 @@ struct batadv_tvlv_handler { int (*unicast_handler)(struct batadv_priv *bat_priv, u8 *src, u8 *dst, void *tvlv_value, u16 tvlv_value_len); - u8 type; - u8 version; + int (*handler)(struct batadv_priv *bat_priv, void *tvlv_value, + u16 tvlv_value_len, void *ctx); + int packet_type; + u8 tvlv_type; + u8 tvlv_version; u8 flags; struct kref refcount; struct rcu_head rcu; @@ -1620,15 +1626,15 @@ struct batadv_tvlv_handler {
/** * enum batadv_tvlv_handler_flags - tvlv handler flags definitions - * @BATADV_TVLV_HANDLER_OGM_CIFNOTFND: tvlv ogm processing function will call + * @BATADV_TVLV_HANDLER_CIFNOTFND: tvlv processing function will call * this handler even if its type was not found (with no data) - * @BATADV_TVLV_HANDLER_OGM_CALLED: interval tvlv handling flag - the API marks + * @BATADV_TVLV_HANDLER_CALLED: internal tvlv handling flag - the API marks * a handler as being called, so it won't be called if the - * BATADV_TVLV_HANDLER_OGM_CIFNOTFND flag was set + * BATADV_TVLV_HANDLER_CIFNOTFND flag was set */ enum batadv_tvlv_handler_flags { - BATADV_TVLV_HANDLER_OGM_CIFNOTFND = BIT(1), - BATADV_TVLV_HANDLER_OGM_CALLED = BIT(2), + BATADV_TVLV_HANDLER_CIFNOTFND = BIT(1), + BATADV_TVLV_HANDLER_CALLED = BIT(2), };
/**
The upcoming aggregation TVLVs will need to be able to hold larger broadcast packets too.
For one thing this patch increases the according stack buffer to 256 bytes to be able to handle the most common broadcast packet cases quickly.
For larger TVLVs, this patch will try to linearize skb data up to and including the TVLV within the received packet.
Signed-off-by: Linus Lüssing linus.luessing@c0d3.blue --- net/batman-adv/bat_iv_ogm.c | 4 ++-- net/batman-adv/bat_v_ogm.c | 4 ++-- net/batman-adv/tvlv.c | 20 +++++++++++--------- net/batman-adv/tvlv.h | 4 ++-- 4 files changed, 17 insertions(+), 15 deletions(-)
diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c index 95dfc5c..e27a23f 100644 --- a/net/batman-adv/bat_iv_ogm.c +++ b/net/batman-adv/bat_iv_ogm.c @@ -1410,7 +1410,7 @@ batadv_iv_ogm_update_seqnos(const struct ethhdr *ethhdr, * @if_outgoing: the interface for which the packet should be considered */ static void -batadv_iv_ogm_process_per_outif(const struct sk_buff *skb, int ogm_offset, +batadv_iv_ogm_process_per_outif(struct sk_buff *skb, int ogm_offset, struct batadv_orig_node *orig_node, struct batadv_hard_iface *if_incoming, struct batadv_hard_iface *if_outgoing) @@ -1614,7 +1614,7 @@ batadv_iv_ogm_process_per_outif(const struct sk_buff *skb, int ogm_offset, * @ogm_offset: offset to the OGM which should be processed (for aggregates) * @if_incoming: the interface where this packet was receved */ -static void batadv_iv_ogm_process(const struct sk_buff *skb, int ogm_offset, +static void batadv_iv_ogm_process(struct sk_buff *skb, int ogm_offset, struct batadv_hard_iface *if_incoming) { struct batadv_priv *bat_priv = netdev_priv(if_incoming->soft_iface); diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c index cfc9228..fad96c9 100644 --- a/net/batman-adv/bat_v_ogm.c +++ b/net/batman-adv/bat_v_ogm.c @@ -603,7 +603,7 @@ static bool batadv_v_ogm_route_update(struct batadv_priv *bat_priv, */ static void batadv_v_ogm_process_per_outif(struct batadv_priv *bat_priv, - const struct sk_buff *skb, + struct sk_buff *skb, const struct batadv_ogm2_packet *ogm2, struct batadv_orig_node *orig_node, struct batadv_neigh_node *neigh_node, @@ -673,7 +673,7 @@ static bool batadv_v_ogm_aggr_packet(int buff_pos, int packet_len, * @ogm_offset: offset to the OGM which should be processed (for aggregates) * @if_incoming: the interface where this packet was receved */ -static void batadv_v_ogm_process(const struct sk_buff *skb, int ogm_offset, +static void batadv_v_ogm_process(struct sk_buff *skb, int ogm_offset, struct batadv_hard_iface *if_incoming) { struct batadv_priv *bat_priv = netdev_priv(if_incoming->soft_iface); diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c index 7dbda3a..9179e87 100644 --- a/net/batman-adv/tvlv.c +++ b/net/batman-adv/tvlv.c @@ -497,12 +497,12 @@ static void batadv_tvlv_call_unfound_handlers(struct batadv_priv *bat_priv, * any TVLV handler called successfully. Returns NET_RX_DROP otherwise. */ int batadv_tvlv_containers_process2(struct batadv_priv *bat_priv, - const struct sk_buff *skb, u8 packet_type, + struct sk_buff *skb, u8 packet_type, unsigned int tvlv_offset, u16 tvlv_value_len, void *ctx) { struct batadv_tvlv_hdr *tvlv_hdr, tvlv_hdr_buff; - u8 *tvlv_value, tvlv_value_buff[128]; + u8 *tvlv_value, tvlv_value_buff[256]; u16 tvlv_value_cont_len; int ret = NET_RX_SUCCESS;
@@ -517,14 +517,16 @@ int batadv_tvlv_containers_process2(struct batadv_priv *bat_priv, tvlv_offset += sizeof(*tvlv_hdr); tvlv_value_len -= sizeof(*tvlv_hdr);
- if (tvlv_value_cont_len > sizeof(tvlv_value_buff)) { - pr_warn_once("batman-adv: TVLVs greater than 128 bytes unsupported for now, ignoring\n"); - goto skip_handler_call; - } - if (tvlv_value_cont_len > tvlv_value_len) return NET_RX_DROP;
+ /* check for sufficient space either in stack buffer or + * in skb's linear data buffer + */ + if (tvlv_value_cont_len > sizeof(tvlv_value_buff) && + !pskb_may_pull(skb, tvlv_offset + tvlv_value_cont_len)) + return NET_RX_DROP; + tvlv_value = skb_header_pointer(skb, tvlv_offset, tvlv_value_cont_len, tvlv_value_buff); @@ -535,7 +537,7 @@ int batadv_tvlv_containers_process2(struct batadv_priv *bat_priv, tvlv_hdr->type, tvlv_hdr->version, tvlv_value, tvlv_value_cont_len, ctx); -skip_handler_call: + tvlv_offset += tvlv_value_cont_len; tvlv_value_len -= tvlv_value_cont_len; } @@ -626,7 +628,7 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv, * OGM header. */ void batadv_tvlv_ogm_receive(struct batadv_priv *bat_priv, - const struct sk_buff *skb, + struct sk_buff *skb, struct batadv_orig_node *orig_node) { struct batadv_ogm_packet *ogm_packet; diff --git a/net/batman-adv/tvlv.h b/net/batman-adv/tvlv.h index 3496f36..feae0ea 100644 --- a/net/batman-adv/tvlv.h +++ b/net/batman-adv/tvlv.h @@ -31,7 +31,7 @@ u16 batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv, unsigned char **packet_buff, int *packet_buff_len, int packet_min_len); void batadv_tvlv_ogm_receive(struct batadv_priv *bat_priv, - const struct sk_buff *skb, + struct sk_buff *skb, struct batadv_orig_node *orig_node); void batadv_tvlv_container_unregister(struct batadv_priv *bat_priv, u8 type, u8 version); @@ -65,7 +65,7 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv, u8 *src, u8 *dst, void *tvlv_buff, u16 tvlv_buff_len); int batadv_tvlv_containers_process2(struct batadv_priv *bat_priv, - const struct sk_buff *skb, u8 packet_type, + struct sk_buff *skb, u8 packet_type, unsigned int tvlv_offset, u16 tvlv_value_len, void *ctx); void batadv_tvlv_unicast_send(struct batadv_priv *bat_priv, u8 *src,
This patch implements the de-aggregation and reception of broadcast aggregation packets, which are introduced through the new BATADV_BCAST_AGGR packet type.
It enables us to receive aggregation packets consisting of any batman packet type with a broadcast ethernet destination.
Signed-off-by: Linus Lüssing linus.luessing@c0d3.blue --- Makefile | 3 + gen-compat-autoconf.sh | 1 + net/batman-adv/Makefile | 1 + net/batman-adv/aggregation.c | 177 ++++++++++++++++++++++++++++++++++++++++ net/batman-adv/aggregation.h | 51 ++++++++++++ net/batman-adv/main.c | 9 ++ net/batman-adv/packet.h | 18 ++++ net/batman-adv/routing.c | 61 ++++++++++++++ net/batman-adv/routing.h | 2 + net/batman-adv/soft-interface.c | 6 ++ net/batman-adv/tvlv.c | 13 ++- net/batman-adv/types.h | 38 +++++++++ 12 files changed, 378 insertions(+), 2 deletions(-) create mode 100644 net/batman-adv/aggregation.c create mode 100644 net/batman-adv/aggregation.h
diff --git a/Makefile b/Makefile index 1eb6821..6b9d565 100644 --- a/Makefile +++ b/Makefile @@ -29,6 +29,8 @@ export CONFIG_BATMAN_ADV_DAT=y export CONFIG_BATMAN_ADV_NC=n # B.A.T.M.A.N. multicast optimizations: export CONFIG_BATMAN_ADV_MCAST=y +# B.A.T.M.A.N. generic broadcast aggregation: +export CONFIG_BATMAN_ADV_AGGR=y # B.A.T.M.A.N. V routing algorithm (experimental): export CONFIG_BATMAN_ADV_BATMAN_V=n
@@ -83,6 +85,7 @@ BUILD_FLAGS := \ CONFIG_BATMAN_ADV_DAT=$(CONFIG_BATMAN_ADV_DAT) \ CONFIG_BATMAN_ADV_NC=$(CONFIG_BATMAN_ADV_NC) \ CONFIG_BATMAN_ADV_MCAST=$(CONFIG_BATMAN_ADV_MCAST) \ + CONFIG_BATMAN_ADV_AGGR=$(CONFIG_BATMAN_ADV_AGGR) \ CONFIG_BATMAN_ADV_BATMAN_V=$(CONFIG_BATMAN_ADV_BATMAN_V) \ INSTALL_MOD_DIR=updates/
diff --git a/gen-compat-autoconf.sh b/gen-compat-autoconf.sh index cf36e55..c835a6d 100755 --- a/gen-compat-autoconf.sh +++ b/gen-compat-autoconf.sh @@ -41,6 +41,7 @@ gen_config 'CONFIG_BATMAN_ADV_DEBUG' ${CONFIG_BATMAN_ADV_DEBUG:="n"} >> "${TMP}" gen_config 'CONFIG_BATMAN_ADV_BLA' ${CONFIG_BATMAN_ADV_BLA:="y"} >> "${TMP}" gen_config 'CONFIG_BATMAN_ADV_DAT' ${CONFIG_BATMAN_ADV_DAT:="y"} >> "${TMP}" gen_config 'CONFIG_BATMAN_ADV_MCAST' ${CONFIG_BATMAN_ADV_MCAST:="y"} >> "${TMP}" +gen_config 'CONFIG_BATMAN_ADV_AGGR' ${CONFIG_BATMAN_ADV_AGGR:="y"} >> "${TMP}" gen_config 'CONFIG_BATMAN_ADV_NC' ${CONFIG_BATMAN_ADV_NC:="n"} >> "${TMP}" gen_config 'CONFIG_BATMAN_ADV_BATMAN_V' ${CONFIG_BATMAN_ADV_BATMAN_V:="n"} >> "${TMP}"
diff --git a/net/batman-adv/Makefile b/net/batman-adv/Makefile index 915987b..00f3e92 100644 --- a/net/batman-adv/Makefile +++ b/net/batman-adv/Makefile @@ -17,6 +17,7 @@ #
obj-$(CONFIG_BATMAN_ADV) += batman-adv.o +batman-adv-$(CONFIG_BATMAN_ADV_AGGR) += aggregation.o batman-adv-y += bat_algo.o batman-adv-y += bat_iv_ogm.o batman-adv-$(CONFIG_BATMAN_ADV_BATMAN_V) += bat_v.o diff --git a/net/batman-adv/aggregation.c b/net/batman-adv/aggregation.c new file mode 100644 index 0000000..f8b40d1 --- /dev/null +++ b/net/batman-adv/aggregation.c @@ -0,0 +1,177 @@ +/* Copyright (C) 2016 B.A.T.M.A.N. contributors: + * + * Linus Lüssing + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see http://www.gnu.org/licenses/. + */ + +#include "aggregation.h" +#include "main.h" + +#include <linux/etherdevice.h> +#include <linux/if_ether.h> +#include <linux/skbuff.h> +#include <linux/string.h> +#include <linux/types.h> + +#include "tvlv.h" + +/** + * batadv_aggr_add_counter_rx - update aggregation rx statistics + * @bat_priv: the bat priv with all the soft interface information + * @skb: the skb to count + * + * Updates statistics for received aggregation packets with the given skb. + */ +void batadv_aggr_add_counter_rx(struct batadv_priv *bat_priv, + struct sk_buff *skb) +{ + batadv_inc_counter(bat_priv, BATADV_CNT_AGGR_RX); + batadv_add_counter(bat_priv, BATADV_CNT_AGGR_RX_BYTES, + skb->len + ETH_HLEN); +} + +/** + * batadv_aggr_put_ethhdr - append a mac header to skb + * @skb: the packet to append to + * @h_source: the ethernet source address to set + * + * Appends a mac header to the given skb with the ethernet source address + * set to the provided h_source and the ethernet destination address to + * a broadcast one. Furthermore, sets the ethernet type to ETH_P_BATMAN. + * + * Also sets the skb mac header pointer to the beginning of the appended mac + * header. + */ +static void batadv_aggr_put_ethhdr(struct sk_buff *skb, unsigned char *h_source) +{ + struct ethhdr *ethhdr; + + skb_reset_mac_header(skb); + + ethhdr = (struct ethhdr *)skb_put(skb, ETH_HLEN); + ethhdr->h_proto = htons(ETH_P_BATMAN); + ether_addr_copy(ethhdr->h_source, h_source); + ether_addr_copy(ethhdr->h_dest, batadv_broadcast_addr); +} + +/** + * batadv_aggr_put_batadv - append batman header and data to skb + * @skb: the packet to append to + * @data: the data to append after the batman header + * @data_len: the length of the data to append + * @packet_type: the packet type to set in the batman header + * + * First appends a batman header consisting of the given packet type and the + * compatibility version to the given skb. Then copies the given data behind + * this minimal batman header in the skb. + * + * Also sets the skb network header pointer to the beginning of the batman + * header. + */ +static void batadv_aggr_put_batadv(struct sk_buff *skb, void *data, + u16 data_len, u8 packet_type) +{ + u8 version = BATADV_COMPAT_VERSION; + u8 *pos; + + skb_reset_network_header(skb); + skb_reset_mac_len(skb); + + pos = (u8 *)skb_put(skb, sizeof(packet_type)); + *pos = packet_type; + pos = (u8 *)skb_put(skb, sizeof(version)); + *pos = version; + + pos = (u8 *)skb_put(skb, data_len); + memcpy(pos, data, data_len); +} + +/** + * batadv_aggr_tvlv_handler - process incoming aggregation tvlv container + * @bat_priv: the bat priv with all the soft interface information + * @tvlv_value: tvlv buffer containing an aggregated packet + * @tvlv_value_len: length of the aggregated packet + * @ctx: handler specific context information + * (here: recv_if, h_source and packet type of aggregated packet) + * + * De-aggregates the given, specific broadcast packet and transparently + * forwards it for broadcast packet reception. + * + * Return: NET_RX_SUCCESS on success, NET_RX_DROP otherwise. + */ +static int batadv_aggr_tvlv_handler(struct batadv_priv *bat_priv, + void *tvlv_value, u16 tvlv_value_len, + void *ctx) +{ + struct batadv_aggr_ctx *aggr_ctx = ctx; + struct batadv_hard_iface *recv_if = aggr_ctx->recv_if; + struct sk_buff *skb; + unsigned int size; + u8 version = BATADV_COMPAT_VERSION; + u8 packet_type = aggr_ctx->handler.tvlv_type; + + /* disallow aggr-in-aggr-in-... to avoid stack overflows */ + if (packet_type == BATADV_BCAST_AGGR) + return NET_RX_DROP; + + size = NET_IP_ALIGN + ETH_HLEN; + size += sizeof(packet_type) + sizeof(version); + size += tvlv_value_len; + + skb = dev_alloc_skb(size); + if (!skb) + return NET_RX_DROP; + + skb_reserve(skb, NET_IP_ALIGN); + batadv_aggr_put_ethhdr(skb, aggr_ctx->h_source); + skb_pull(skb, ETH_HLEN); + batadv_aggr_put_batadv(skb, tvlv_value, tvlv_value_len, packet_type); + + skb->protocol = htons(ETH_P_BATMAN); + skb->dev = recv_if->net_dev; + + batadv_inc_counter(bat_priv, BATADV_CNT_AGGR_PARTS_RX); + batadv_add_counter(bat_priv, BATADV_CNT_AGGR_PARTS_RX_BYTES, + skb->len + ETH_HLEN); + + return batadv_batman_skb_recv(skb, recv_if->net_dev, + &recv_if->batman_adv_ptype, NULL); +} + +/** + * batadv_aggr_mesh_init - initialise the generic aggregation engine + * @bat_priv: the bat priv with all the soft interface information + * + * Return: 0 on success or a negative error code in case of failure + */ +int batadv_aggr_mesh_init(struct batadv_priv *bat_priv) +{ + batadv_tvlv_handler_register2(bat_priv, batadv_aggr_tvlv_handler, + BATADV_BCAST_AGGR, BATADV_TVLV_ANY, 1, + BATADV_TVLV_HANDLER_MORECTX); + batadv_tvlv_container_register(bat_priv, BATADV_TVLV_AGGR, 1, NULL, 0); + + return 0; +} + +/** + * batadv_mcast_free - shutdown the generic aggregation engine + * @bat_priv: the bat priv with all the soft interface information + */ +void batadv_aggr_mesh_free(struct batadv_priv *bat_priv) +{ + batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_AGGR, 1); + batadv_tvlv_handler_unregister2(bat_priv, BATADV_BCAST_AGGR, + BATADV_TVLV_ANY, 1); +} diff --git a/net/batman-adv/aggregation.h b/net/batman-adv/aggregation.h new file mode 100644 index 0000000..5b577e6 --- /dev/null +++ b/net/batman-adv/aggregation.h @@ -0,0 +1,51 @@ +/* Copyright (C) 2016 B.A.T.M.A.N. contributors: + * + * Linus Lüssing + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see http://www.gnu.org/licenses/. + */ + +#ifndef _NET_BATMAN_ADV_AGGREGATION_H_ +#define _NET_BATMAN_ADV_AGGREGATION_H_ + +#include "main.h" + +struct sk_buff; + +#ifdef CONFIG_BATMAN_ADV_AGGR + +void batadv_aggr_add_counter_rx(struct batadv_priv *bat_priv, + struct sk_buff *skb); + +int batadv_aggr_mesh_init(struct batadv_priv *bat_priv); +void batadv_aggr_mesh_free(struct batadv_priv *bat_priv); + +#else + +static inline void batadv_aggr_add_counter_rx(struct batadv_priv *bat_priv, + struct sk_buff *skb) +{ +} + +static inline int batadv_aggr_mesh_init(struct batadv_priv *bat_priv) +{ + return 0; +} + +static inline void batadv_aggr_mesh_free(struct batadv_priv *bat_priv) +{ +} + +#endif /* CONFIG_BATMAN_ADV_AGGR */ + +#endif /* _NET_BATMAN_ADV_AGGREGATION_H_ */ diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c index fb381fb..28caafa 100644 --- a/net/batman-adv/main.c +++ b/net/batman-adv/main.c @@ -47,6 +47,7 @@ #include <net/rtnetlink.h> #include <uapi/linux/batman_adv.h>
+#include "aggregation.h" #include "bat_algo.h" #include "bat_iv_ogm.h" #include "bat_v.h" @@ -179,6 +180,10 @@ int batadv_mesh_init(struct net_device *soft_iface) INIT_HLIST_HEAD(&bat_priv->softif_vlan_list); INIT_HLIST_HEAD(&bat_priv->tp_list);
+ ret = batadv_aggr_mesh_init(bat_priv); + if (ret < 0) + goto err; + ret = batadv_v_mesh_init(bat_priv); if (ret < 0) goto err; @@ -226,6 +231,7 @@ void batadv_mesh_free(struct net_device *soft_iface)
batadv_gw_node_free(bat_priv);
+ batadv_aggr_mesh_free(bat_priv); batadv_v_mesh_free(bat_priv); batadv_nc_mesh_free(bat_priv); batadv_dat_free(bat_priv); @@ -507,6 +513,7 @@ static void batadv_recv_handler_init(void) BUILD_BUG_ON(sizeof(struct batadv_unicast_packet) != 10); BUILD_BUG_ON(sizeof(struct batadv_unicast_4addr_packet) != 18); BUILD_BUG_ON(sizeof(struct batadv_frag_packet) != 20); + BUILD_BUG_ON(sizeof(struct batadv_aggr_packet) != 4); BUILD_BUG_ON(sizeof(struct batadv_bcast_packet) != 14); BUILD_BUG_ON(sizeof(struct batadv_coded_packet) != 46); BUILD_BUG_ON(sizeof(struct batadv_unicast_tvlv_packet) != 20); @@ -533,6 +540,8 @@ static void batadv_recv_handler_init(void) batadv_rx_handler[BATADV_ICMP] = batadv_recv_icmp_packet; /* Fragmented packets */ batadv_rx_handler[BATADV_UNICAST_FRAG] = batadv_recv_frag_packet; + /* Aggregation packets */ + batadv_rx_handler[BATADV_BCAST_AGGR] = batadv_recv_aggr_packet; }
int diff --git a/net/batman-adv/packet.h b/net/batman-adv/packet.h index 8e8a5db..76c1901 100644 --- a/net/batman-adv/packet.h +++ b/net/batman-adv/packet.h @@ -30,6 +30,7 @@ * @BATADV_CODED: network coded packets * @BATADV_ELP: echo location packets for B.A.T.M.A.N. V * @BATADV_OGM2: originator messages for B.A.T.M.A.N. V + * @BATADV_AGGR: broadcast aggregation packets * * @BATADV_UNICAST: unicast packets carrying unicast payload traffic * @BATADV_UNICAST_FRAG: unicast packets carrying a fragment of the original @@ -46,6 +47,7 @@ enum batadv_packettype { BATADV_CODED = 0x02, BATADV_ELP = 0x03, BATADV_OGM2 = 0x04, + BATADV_BCAST_AGGR = 0x05, /* 0x40 - 0x7f: unicast */ #define BATADV_UNICAST_MIN 0x40 BATADV_UNICAST = 0x40, @@ -153,6 +155,8 @@ enum batadv_bla_claimframe { * @BATADV_TVLV_TT: translation table tvlv * @BATADV_TVLV_ROAM: roaming advertisement tvlv * @BATADV_TVLV_MCAST: multicast capability tvlv + * @BATADV_TVLV_AGGR: generic broadcast aggregation capability tvlv + * @BATADV_ANYTYPE: internal place holder for TVLV handlers, not for the "wire" */ enum batadv_tvlv_type { BATADV_TVLV_GW = 0x01, @@ -161,6 +165,8 @@ enum batadv_tvlv_type { BATADV_TVLV_TT = 0x04, BATADV_TVLV_ROAM = 0x05, BATADV_TVLV_MCAST = 0x06, + BATADV_TVLV_AGGR = 0x07, + BATADV_TVLV_ANY = 0xff, };
#pragma pack(2) @@ -455,6 +461,18 @@ struct batadv_frag_packet { };
/** + * struct batadv_aggr_packet - aggregation packet for broadcast packets + * @packet_type: batman-adv packet type, part of the general header + * @version: batman-adv protocol version, part of the genereal header + * @tvlv_len: length of tvlv data following the aggregation header + */ +struct batadv_aggr_packet { + u8 packet_type; + u8 version; /* batman version field */ + __be16 tvlv_len; +}; + +/** * struct batadv_bcast_packet - broadcast packet for network payload * @packet_type: batman-adv packet type, part of the general header * @version: batman-adv protocol version, part of the genereal header diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c index e1ebe14..1d74e62 100644 --- a/net/batman-adv/routing.c +++ b/net/batman-adv/routing.c @@ -34,6 +34,7 @@ #include <linux/spinlock.h> #include <linux/stddef.h>
+#include "aggregation.h" #include "bitarray.h" #include "bridge_loop_avoidance.h" #include "distributed-arp-table.h" @@ -1155,6 +1156,66 @@ int batadv_recv_frag_packet(struct sk_buff *skb, return ret; }
+/** + * batadv_recv_aggr_get_tvlv_len - get tvlv_len of an aggregation packet + * @skb: the aggregation packet to parse + * + * Return: Length of the tvlv data of the given skb on success, + * -EINVAL otherwise (i.e. packet is too short). + */ +static int batadv_recv_aggr_get_tvlv_len(struct sk_buff *skb) +{ + unsigned int tvlv_len_offset; + __be16 *tvlv_len, tvlv_len_buff; + + tvlv_len_offset = offsetof(struct batadv_aggr_packet, tvlv_len); + tvlv_len = skb_header_pointer(skb, tvlv_len_offset, + sizeof(tvlv_len_buff), &tvlv_len_buff); + + if (!tvlv_len) + return -EINVAL; + + return ntohs(*tvlv_len); +} + +/** + * batadv_recv_aggr_packet - process received aggregation packet + * @skb: the aggregation packet to process + * @recv_if: interface that the skb is received on + * + * This function de-aggregates broadcast packets from the given + * aggregation packet. + * + * Frees/consumes the provided skb. + * + * Return: NET_RX_SUCCESS on success, NET_RX_DROP otherwise. + */ +int batadv_recv_aggr_packet(struct sk_buff *skb, + struct batadv_hard_iface *recv_if) +{ + struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface); + struct batadv_aggr_ctx aggr_ctx; + unsigned int tvlv_offset = sizeof(struct batadv_aggr_packet); + int tvlv_len, ret; + + aggr_ctx.recv_if = recv_if; + aggr_ctx.h_source = eth_hdr(skb)->h_source; + tvlv_len = batadv_recv_aggr_get_tvlv_len(skb); + + if (tvlv_len < 0) { + kfree_skb(skb); + return NET_RX_DROP; + } + + batadv_aggr_add_counter_rx(bat_priv, skb); + + ret = batadv_tvlv_containers_process2(bat_priv, skb, BATADV_BCAST_AGGR, + tvlv_offset, tvlv_len, &aggr_ctx); + + consume_skb(skb); + return ret; +} + int batadv_recv_bcast_packet(struct sk_buff *skb, struct batadv_hard_iface *recv_if) { diff --git a/net/batman-adv/routing.h b/net/batman-adv/routing.h index 5ede16c..7e9225a 100644 --- a/net/batman-adv/routing.h +++ b/net/batman-adv/routing.h @@ -37,6 +37,8 @@ int batadv_recv_unicast_packet(struct sk_buff *skb, struct batadv_hard_iface *recv_if); int batadv_recv_frag_packet(struct sk_buff *skb, struct batadv_hard_iface *iface); +int batadv_recv_aggr_packet(struct sk_buff *skb, + struct batadv_hard_iface *recv_if); int batadv_recv_bcast_packet(struct sk_buff *skb, struct batadv_hard_iface *recv_if); int batadv_recv_tt_query(struct sk_buff *skb, diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c index b25789a..32a34a2 100644 --- a/net/batman-adv/soft-interface.c +++ b/net/batman-adv/soft-interface.c @@ -961,6 +961,12 @@ static const struct { { "frag_rx_bytes" }, { "frag_fwd" }, { "frag_fwd_bytes" }, +#ifdef CONFIG_BATMAN_ADV_AGGR + { "aggr_rx" }, + { "aggr_rx_bytes" }, + { "aggr_parts_rx" }, + { "aggr_parts_rx_bytes" }, +#endif { "tt_request_tx" }, { "tt_request_rx" }, { "tt_response_tx" }, diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c index 9179e87..3048ed5 100644 --- a/net/batman-adv/tvlv.c +++ b/net/batman-adv/tvlv.c @@ -87,10 +87,12 @@ batadv_tvlv_handler_get(struct batadv_priv *bat_priv, int packet_type, if (tvlv_handler_tmp->packet_type != packet_type) continue;
- if (tvlv_handler_tmp->tvlv_type != tvlv_type) + if (tvlv_handler_tmp->tvlv_type != BATADV_TVLV_ANY && + tvlv_handler_tmp->tvlv_type != tvlv_type) continue;
- if (tvlv_handler_tmp->tvlv_version != tvlv_version) + if (tvlv_handler_tmp->tvlv_type != BATADV_TVLV_ANY && + tvlv_handler_tmp->tvlv_version != tvlv_version) continue;
if (!kref_get_unless_zero(&tvlv_handler_tmp->refcount)) @@ -430,6 +432,7 @@ static int batadv_tvlv_call_handler2(struct batadv_priv *bat_priv, u8 tvlv_version, void *tvlv_value, u16 tvlv_value_len, void *ctx) { + struct batadv_tvlv_handler_ctx *handler_ctx; struct batadv_tvlv_handler *tvlv_handler; int ret;
@@ -438,6 +441,12 @@ static int batadv_tvlv_call_handler2(struct batadv_priv *bat_priv, if (!tvlv_handler) return NET_RX_DROP;
+ if (tvlv_handler->flags & BATADV_TVLV_HANDLER_MORECTX) { + handler_ctx = (struct batadv_tvlv_handler_ctx *)ctx; + handler_ctx->tvlv_type = tvlv_type; + handler_ctx->tvlv_version = tvlv_version; + } + ret = tvlv_handler->handler(bat_priv, tvlv_value, tvlv_value_len, ctx); tvlv_handler->flags |= BATADV_TVLV_HANDLER_CALLED;
diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index e7f25a1..e9d7b99 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -556,6 +556,10 @@ struct batadv_bcast_duplist_entry { * @BATADV_CNT_FRAG_RX_BYTES: received fragment traffic bytes counter * @BATADV_CNT_FRAG_FWD: forwarded fragment traffic packet counter * @BATADV_CNT_FRAG_FWD_BYTES: forwarded fragment traffic bytes counter + * @BATADV_CNT_AGGR_RX: received aggregation traffic packet count + * @BATADV_CNT_AGGR_RX_BYTES: received aggregation traffic bytes counter + * @BATADV_CNT_AGGR_PARTS_RX: received aggregated traffic packet counter + * @BATADV_CNT_AGGR_PARTS_RX_BYTES: received aggregated bytes counter * @BATADV_CNT_TT_REQUEST_TX: transmitted tt req traffic packet counter * @BATADV_CNT_TT_REQUEST_RX: received tt req traffic packet counter * @BATADV_CNT_TT_RESPONSE_TX: transmitted tt resp traffic packet counter @@ -599,6 +603,12 @@ enum batadv_counters { BATADV_CNT_FRAG_RX_BYTES, BATADV_CNT_FRAG_FWD, BATADV_CNT_FRAG_FWD_BYTES, +#ifdef CONFIG_BATMAN_ADV_AGGR + BATADV_CNT_AGGR_RX, + BATADV_CNT_AGGR_RX_BYTES, + BATADV_CNT_AGGR_PARTS_RX, + BATADV_CNT_AGGR_PARTS_RX_BYTES, +#endif BATADV_CNT_TT_REQUEST_TX, BATADV_CNT_TT_REQUEST_RX, BATADV_CNT_TT_RESPONSE_TX, @@ -1625,16 +1635,44 @@ struct batadv_tvlv_handler { };
/** + * struct batadv_tvlv_handler_ctx - handler meta information + * @tvlv_type: type of the processed tvlv + * @tvlv_version: version of the processed tvlv + * + * This structure is provided to a tvlv handler if the + * BATADV_TVLV_HANDLER_MORECTX flag was set during registration. + */ +struct batadv_tvlv_handler_ctx { + u8 tvlv_type; + u8 tvlv_version; +}; + +/** + * struct batadv_aggr_ctx - aggregation tvlv context + * @handler: information regarding the tvlv handler itself + * @recv_if: interface the packet was received from + * @h_source: ethernet address of the neighbor the packet was received from + */ +struct batadv_aggr_ctx { + struct batadv_tvlv_handler_ctx handler; + struct batadv_hard_iface *recv_if; + unsigned char *h_source; +}; + +/** * enum batadv_tvlv_handler_flags - tvlv handler flags definitions * @BATADV_TVLV_HANDLER_CIFNOTFND: tvlv processing function will call * this handler even if its type was not found (with no data) * @BATADV_TVLV_HANDLER_CALLED: internal tvlv handling flag - the API marks * a handler as being called, so it won't be called if the * BATADV_TVLV_HANDLER_CIFNOTFND flag was set + * @BATADV_TVLV_HANDLER_MORECTX: tvlv processing function will be provided with + * handler specific context (e.g. tvlv type and version) */ enum batadv_tvlv_handler_flags { BATADV_TVLV_HANDLER_CIFNOTFND = BIT(1), BATADV_TVLV_HANDLER_CALLED = BIT(2), + BATADV_TVLV_HANDLER_MORECTX = BIT(3), };
/**
This patch implements the queueing of any broadcast packet and its following tranmission in an aggregation packet.
A broadcast packet is usually queued for up to 100ms (plus-minus some jitter) or about 50ms on average.
However if the aggregation worker cannot keep up with emptying its queue, a 10 to 20 times faster aggregation packet transmission rate is used.
If an aggregation queue still runs full then broadcast packets are handed over to the driver directly without aggregation, leaving it up to the driver to either transmit or drop it, for instance due to congestion.
Signed-off-by: Linus Lüssing linus.luessing@c0d3.blue --- net/batman-adv/aggregation.c | 475 ++++++++++++++++++++++++++++++++++++++++ net/batman-adv/aggregation.h | 31 +++ net/batman-adv/bat_iv_ogm.c | 6 +- net/batman-adv/hard-interface.c | 5 + net/batman-adv/main.c | 10 + net/batman-adv/main.h | 1 + net/batman-adv/originator.c | 2 + net/batman-adv/send.c | 9 + net/batman-adv/soft-interface.c | 11 +- net/batman-adv/sysfs.c | 5 +- net/batman-adv/types.h | 42 +++- 11 files changed, 590 insertions(+), 7 deletions(-)
diff --git a/net/batman-adv/aggregation.c b/net/batman-adv/aggregation.c index f8b40d1..21b29be 100644 --- a/net/batman-adv/aggregation.c +++ b/net/batman-adv/aggregation.c @@ -18,15 +18,430 @@ #include "aggregation.h" #include "main.h"
+#include <linux/atomic.h> +#include <linux/bitops.h> #include <linux/etherdevice.h> #include <linux/if_ether.h> +#include <linux/jiffies.h> +#include <linux/kernel.h> +#include <linux/netdevice.h> +#include <linux/random.h> #include <linux/skbuff.h> +#include <linux/spinlock.h> #include <linux/string.h> #include <linux/types.h> +#include <linux/workqueue.h>
+#include "send.h" #include "tvlv.h"
/** + * batadv_aggr_hardif_start - schedule aggregation packet transmission + * @hard_iface: the hard interface to schedule the transmission on + * + * Schedules an aggregation packet transmission for the next aggregation + * interval, plus/minus some jitter. + */ +void batadv_aggr_hardif_start(struct batadv_hard_iface *hard_iface) +{ + unsigned int msecs = BATADV_MAX_AGGREGATION_MS * 1000; + + /* msecs * [0.9, 1.1] */ + msecs += prandom_u32() % (msecs / 5) - (msecs / 10); + queue_delayed_work(batadv_event_workqueue, &hard_iface->aggr.work, + msecs_to_jiffies(msecs / 1000)); +} + +/** + * batadv_aggr_hardif_start_urgent - schedule urgent aggregate transmission + * @hard_iface: the hard interface to schedule the transmission on + * + * Schedules an urgent aggregation packet transmission, plus/minus some jitter. + * That is at some time between an interval 10 to 20 times faster than the + * regular aggregation interval. + */ +static void +batadv_aggr_hardif_start_urgent(struct batadv_hard_iface *hard_iface) +{ + unsigned int msecs = BATADV_MAX_AGGREGATION_MS * 1000; + + /* msecs * [0.05, 0.1] */ + msecs = (msecs / 20) + prandom_u32() % (msecs / 20); + queue_delayed_work(batadv_event_workqueue, &hard_iface->aggr.work, + msecs_to_jiffies(msecs / 1000)); +} + +/** + * batadv_aggr_skb_queue_free - free all elements in an skb queue + * @head: the skb queue to empty + * + * Empties an skb queue and frees all the skbs it contained. + */ +static void batadv_aggr_skb_queue_free(struct sk_buff_head *head) +{ + struct sk_buff *skb; + + while ((skb = skb_dequeue(head))) + kfree_skb(skb); +} + +/** + * batadv_aggr_hardif_stop - shutdown an aggregation routine + * @hard_iface: the interface to stop aggregation on + * + * Stops an aggregation timer and destroys the packets queued + * on the given interface. + */ +void batadv_aggr_hardif_stop(struct batadv_hard_iface *hard_iface) +{ + cancel_delayed_work_sync(&hard_iface->aggr.work); + + spin_lock_bh(&hard_iface->aggr.aggr_list_lock); + batadv_aggr_skb_queue_free(&hard_iface->aggr.aggr_list); + spin_unlock_bh(&hard_iface->aggr.aggr_list_lock); +} + +/** + * batadv_aggr_chunk_reserve - reserve space in an aggregation packet + * @hard_iface: the interface to reserve on + * @skb: the to be aggregated packet to reserve for + * @size: size of the aggregation packet + * + * Tries to reserve space in the aggregation packet for the given skb. + * If reservation was successful, then the size of the to be allocated + * aggregation packet is increased accordingly. + * + * Return: True if there was enough space in the aggregation packet left, + * false otherwise. + */ +static bool batadv_aggr_chunk_reserve(struct batadv_hard_iface *hard_iface, + struct sk_buff *skb, + int *size) +{ + unsigned int len = skb->len + sizeof(struct batadv_tvlv_hdr); + + len -= sizeof(((struct batadv_aggr_packet *)0)->packet_type); + len -= sizeof(((struct batadv_aggr_packet *)0)->version); + + if (*size + len > hard_iface->net_dev->mtu) + return false; + + *size += len; + return true; +} + +/** + * batadv_aggr_get_chunk - gets a chunk of packets from the aggregation queue + * @hard_iface: the interface to get to be aggregated packets from + * @head: queue to stage a chunk of to be aggregated+transmitted packets on + * @size: size of the aggregation packet + * + * Tries to grab as many packets from the aggregation queue as fit into a + * single aggregation packet. + * + * Return: True if there are no packets in the aggregation queue + * of the provided interface left afterwards, false otherwise. + */ +static bool batadv_aggr_get_chunk(struct batadv_hard_iface *hard_iface, + struct sk_buff_head *head, + int *size) +{ + struct sk_buff *skb, *skb_tmp; + bool emptied = true; + + *size = sizeof(struct batadv_aggr_packet); + + if (skb_queue_empty(&hard_iface->aggr.aggr_list)) + return emptied; + + spin_lock_bh(&hard_iface->aggr.aggr_list_lock); + skb_queue_walk_safe(&hard_iface->aggr.aggr_list, skb, skb_tmp) { + if (!batadv_aggr_chunk_reserve(hard_iface, skb, size)) { + emptied = false; + break; + } + + skb_unlink(skb, &hard_iface->aggr.aggr_list); + skb_queue_tail(head, skb); + } + spin_unlock_bh(&hard_iface->aggr.aggr_list_lock); + + return emptied; +} + +/** + * batadv_aggr_alloc_skb - allocate an aggregation packet + * @size: size of the to be allocated packet (excluding ethernet header) + * + * Allocates a broadcast aggregation packet. + * + * Return: An aggregation packet on success, NULL otherwise. + */ +static struct sk_buff *batadv_aggr_alloc_skb(int size) +{ + struct batadv_aggr_packet *aggr_packet; + struct sk_buff *skb; + unsigned char *skb_buff; + unsigned int offset; + + skb = dev_alloc_skb(size + ETH_HLEN + NET_IP_ALIGN); + if (!skb) + return NULL; + + skb_reserve(skb, ETH_HLEN + NET_IP_ALIGN); + skb_reset_network_header(skb); + + skb_buff = skb_put(skb, sizeof(*aggr_packet)); + aggr_packet = (struct batadv_aggr_packet *)skb_buff; + aggr_packet->packet_type = BATADV_BCAST_AGGR; + aggr_packet->version = BATADV_COMPAT_VERSION; + + offset = skb_network_offset(skb) + sizeof(*aggr_packet); + skb_set_transport_header(skb, offset); + + return skb; +} + +/** + * batadv_aggr_get_pkttypes - get the packet type of a batman packet + * @skb: the packet to get the type from + * + * Return: The packet type of the provided batman packet. + */ +static u8 batadv_aggr_get_pkttype(struct sk_buff *skb) +{ + struct batadv_aggr_packet *packet; + + packet = (struct batadv_aggr_packet *)skb_network_header(skb); + + return packet->packet_type; +} + +/** + * batadv_aggr_put_tvlvhdr - append a tvlv header to an skb + * @skb: the aggregation packet to append the tvlv header to + * @type: the packet (= tvlv) type to set in the tvlv header + * @len: the size of the to be added tvlv data + * + * Appends a tvlv header to the given aggregation packet and sets its type and + * length as provided. + */ +static void batadv_aggr_put_tvlvhdr(struct sk_buff *skb, u8 type, + unsigned int len) +{ + struct batadv_tvlv_hdr *tvlv_hdr; + + tvlv_hdr = (struct batadv_tvlv_hdr *)skb_put(skb, sizeof(*tvlv_hdr)); + + tvlv_hdr->type = type; + tvlv_hdr->version = 1; + tvlv_hdr->len = htons(len); +} + +/** + * batadv_aggr_queue_is_full - check for slots left in aggregation queue + * @hard_iface: the interface to check + * + * Return: True if if the queue is full, false otherwise. + */ +static inline bool +batadv_aggr_queue_is_full(struct batadv_hard_iface *hard_iface) +{ + struct sk_buff_head *head = &hard_iface->aggr.aggr_list; + + return skb_queue_len(head) >= BATADV_AGGR_QUEUE_LEN; +} + +/** + * batadv_aggr_squash_chunk - squash packets into an aggregate + * @head: a list of to be squashed packets + * @size: the size of the to be created aggregation packet + * (excluding the ethernet header) + * + * Allocates an aggregation packet and squashes the provided list of broadcast + * packets into it. The provided list of packets is freed/consumed. + * + * Return: An aggregation packet ready for transmission on success, NULL + * otherwise. + */ +static struct sk_buff * +batadv_aggr_squash_chunk(struct sk_buff_head *head, + int size) +{ + struct sk_buff *skb, *skb_tmp, *skb_aggr; + struct batadv_aggr_packet *aggr_packet; + unsigned int len, offset, tvlv_len = 0; + unsigned char *to; + u8 type; + + if (skb_queue_empty(head)) + return NULL; + + skb_aggr = batadv_aggr_alloc_skb(size); + if (!skb_aggr) { + batadv_aggr_skb_queue_free(head); + return NULL; + } + + aggr_packet = (struct batadv_aggr_packet *)skb_network_header(skb_aggr); + + skb_queue_walk_safe(head, skb, skb_tmp) { + offset = skb_network_offset(skb); + offset += sizeof(aggr_packet->packet_type); + offset += sizeof(aggr_packet->version); + len = skb->len - offset; + type = batadv_aggr_get_pkttype(skb); + + batadv_aggr_put_tvlvhdr(skb_aggr, type, len); + to = skb_put(skb_aggr, len); + skb_copy_bits(skb, offset, to, len); + skb_unlink(skb, head); + consume_skb(skb); + + tvlv_len += len + sizeof(struct batadv_tvlv_hdr); + } + + aggr_packet->tvlv_len = htons(tvlv_len); + + return skb_aggr; +} + +/** + * __batadv_aggr_send_chunk - send a prepared aggregation packet + * @hard_iface: the interface to transmit on + * @skb: the prepared aggregation packet to send + */ +static void __batadv_aggr_send_chunk(struct batadv_hard_iface *hard_iface, + struct sk_buff *skb) +{ + struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface); + + batadv_inc_counter(bat_priv, BATADV_CNT_AGGR_TX); + batadv_add_counter(bat_priv, BATADV_CNT_AGGR_TX_BYTES, + skb->len + ETH_HLEN); + + /* ToDo: Track transmission failures? */ + batadv_send_skb_packet(skb, hard_iface, batadv_broadcast_addr); +} + +/** + * batadv_aggr_send_chunk - prepare and transmit an aggregation packet + * @hard_iface: the interface to transmit on + * + * Fetches as many packets from the aggregation queue of the provided interface + * as fit into a single aggregation packet. Then aggregates them into such an + * aggregation packet and transmits the final aggregate. + * + * Return: True if there are no packets in the aggregation queue + * of the provided interface left afterwards, false otherwise. + */ +static bool batadv_aggr_send_chunk(struct batadv_hard_iface *hard_iface) +{ + struct sk_buff_head head; + struct sk_buff *skb; + int size = 0; + bool emptied; + + skb_queue_head_init(&head); + emptied = batadv_aggr_get_chunk(hard_iface, &head, &size); + + skb = batadv_aggr_squash_chunk(&head, size); + if (!skb) + goto out; + + __batadv_aggr_send_chunk(hard_iface, skb); + +out: + return emptied; +} + +/** + * batadv_aggr_work - periodic aggregation worker + * @work: work queue item + * + * Prepares and sends out an aggregation packet. In the end rearms the timer + * either to the next aggregation interval, or if there were still packets left + * in the aggregation queue, sets it to an earlier time. + */ +static void batadv_aggr_work(struct work_struct *work) +{ + struct batadv_hard_iface_aggr *aggr; + struct batadv_hard_iface *hard_iface; + struct batadv_priv *bat_priv; + bool emptied; + + aggr = container_of(work, struct batadv_hard_iface_aggr, work.work); + hard_iface = container_of(aggr, struct batadv_hard_iface, aggr); + bat_priv = netdev_priv(hard_iface->soft_iface); + + emptied = batadv_aggr_send_chunk(hard_iface); + if (emptied) { + batadv_aggr_hardif_start(hard_iface); + } else { + batadv_inc_counter(bat_priv, BATADV_CNT_AGGR_URGENT); + batadv_aggr_hardif_start_urgent(hard_iface); + } +} + +/** + * batadv_aggr_queue - queue a broadcast packet for aggregation + * @skb: the packet to queue + * @hard_iface: the interface to queue on + * + * Return: NET_XMIT_SUCCESS if the skb was queued, NET_XMIT_DROP otherwise. + * The former consumes the skb. + */ +int batadv_aggr_queue(struct sk_buff *skb, struct batadv_hard_iface *hard_iface) +{ + struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface); + + if (!atomic_read(&bat_priv->aggregation)) + return NET_XMIT_DROP; + + if (atomic_read(&bat_priv->aggr_num_disabled)) + return NET_XMIT_DROP; + + if (batadv_aggr_queue_is_full(hard_iface)) { + batadv_inc_counter(bat_priv, BATADV_CNT_AGGR_QUEUE_FULL); + return NET_XMIT_DROP; + } + + spin_lock_bh(&hard_iface->aggr.aggr_list_lock); + skb_queue_tail(&hard_iface->aggr.aggr_list, skb); + spin_unlock_bh(&hard_iface->aggr.aggr_list_lock); + + batadv_inc_counter(bat_priv, BATADV_CNT_AGGR_PARTS_TX); + batadv_add_counter(bat_priv, BATADV_CNT_AGGR_PARTS_TX_BYTES, + skb->len + ETH_HLEN); + + return NET_XMIT_SUCCESS; +} + +/** + * batadv_aggr_purge_orig - reset originator aggregation state modifications + * @orig: the originator which is going to get purged + */ +void batadv_aggr_purge_orig(struct batadv_orig_node *orig) +{ + struct batadv_priv *bat_priv = orig->bat_priv; + + if (!test_bit(BATADV_ORIG_CAPA_HAS_AGGR, &orig->capabilities) && + test_bit(BATADV_ORIG_CAPA_HAS_AGGR, &orig->capa_initialized)) + atomic_dec(&bat_priv->aggr_num_disabled); +} + +/** + * batadv_aggr_hardif_init - initialize an interface for aggregation + * @hard_iface: the interface to initialize + */ +void batadv_aggr_hardif_init(struct batadv_hard_iface *hard_iface) +{ + INIT_DELAYED_WORK(&hard_iface->aggr.work, batadv_aggr_work); + skb_queue_head_init(&hard_iface->aggr.aggr_list); + spin_lock_init(&hard_iface->aggr.aggr_list_lock); +} + +/** * batadv_aggr_add_counter_rx - update aggregation rx statistics * @bat_priv: the bat priv with all the soft interface information * @skb: the skb to count @@ -150,6 +565,55 @@ static int batadv_aggr_tvlv_handler(struct batadv_priv *bat_priv, }
/** + * batadv_aggr_ogm_handler - process incoming aggregation tvlv container + * @bat_priv: the bat priv with all the soft interface information + * @tvlv_value: tvlv buffer containing an aggregated broadcast packet + * @tvlv_value_len: tvlv buffer length + * @ctx: handler specific context information + * (here: recv_if, h_source and packet type of aggregate) + * + * Parses an aggregation tvlv attached to an originator message and updates + * aggregation capabilities accordingly. + * + * Return: Always NET_RX_SUCCESS. + */ +static int batadv_aggr_ogm_handler(struct batadv_priv *bat_priv, + void *tvlv_value, u16 tvlv_value_len, + void *ctx) +{ + struct batadv_orig_node *orig = ctx; + bool orig_aggr_enabled = !!tvlv_value; + bool orig_initialized; + + orig_initialized = test_bit(BATADV_ORIG_CAPA_HAS_AGGR, + &orig->capa_initialized); + + /* If aggregation support is turned on decrease the disabled aggregation + * node counter only if we had increased it for this node before. If + * this is a completely new orig_node no need to decrease the counter. + */ + if (orig_aggr_enabled && + !test_bit(BATADV_ORIG_CAPA_HAS_AGGR, &orig->capabilities)) { + if (orig_initialized) + atomic_dec(&bat_priv->aggr_num_disabled); + set_bit(BATADV_ORIG_CAPA_HAS_AGGR, &orig->capabilities); + /* If aggregation support is being switched off or if this is an initial + * OGM without aggregation support then increase the disabled + * aggregation node counter. + */ + } else if (!orig_aggr_enabled && + (test_bit(BATADV_ORIG_CAPA_HAS_AGGR, &orig->capabilities) || + !orig_initialized)) { + atomic_inc(&bat_priv->aggr_num_disabled); + clear_bit(BATADV_ORIG_CAPA_HAS_AGGR, &orig->capabilities); + } + + set_bit(BATADV_ORIG_CAPA_HAS_AGGR, &orig->capa_initialized); + + return NET_RX_SUCCESS; +} + +/** * batadv_aggr_mesh_init - initialise the generic aggregation engine * @bat_priv: the bat priv with all the soft interface information * @@ -160,6 +624,13 @@ int batadv_aggr_mesh_init(struct batadv_priv *bat_priv) batadv_tvlv_handler_register2(bat_priv, batadv_aggr_tvlv_handler, BATADV_BCAST_AGGR, BATADV_TVLV_ANY, 1, BATADV_TVLV_HANDLER_MORECTX); + batadv_tvlv_handler_register2(bat_priv, batadv_aggr_ogm_handler, + BATADV_IV_OGM, BATADV_TVLV_AGGR, 1, + BATADV_TVLV_HANDLER_CIFNOTFND); + batadv_tvlv_handler_register2(bat_priv, batadv_aggr_ogm_handler, + BATADV_OGM2, BATADV_TVLV_AGGR, 1, + BATADV_TVLV_HANDLER_CIFNOTFND); + batadv_tvlv_container_register(bat_priv, BATADV_TVLV_AGGR, 1, NULL, 0);
return 0; @@ -172,6 +643,10 @@ int batadv_aggr_mesh_init(struct batadv_priv *bat_priv) void batadv_aggr_mesh_free(struct batadv_priv *bat_priv) { batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_AGGR, 1); + batadv_tvlv_handler_unregister2(bat_priv, BATADV_OGM2, BATADV_TVLV_AGGR, + 1); + batadv_tvlv_handler_unregister2(bat_priv, BATADV_IV_OGM, + BATADV_TVLV_AGGR, 1); batadv_tvlv_handler_unregister2(bat_priv, BATADV_BCAST_AGGR, BATADV_TVLV_ANY, 1); } diff --git a/net/batman-adv/aggregation.h b/net/batman-adv/aggregation.h index 5b577e6..7f4c3d6 100644 --- a/net/batman-adv/aggregation.h +++ b/net/batman-adv/aggregation.h @@ -27,6 +27,14 @@ struct sk_buff; void batadv_aggr_add_counter_rx(struct batadv_priv *bat_priv, struct sk_buff *skb);
+void batadv_aggr_hardif_start(struct batadv_hard_iface *hard_iface); +void batadv_aggr_hardif_stop(struct batadv_hard_iface *hard_iface); + +int batadv_aggr_queue(struct sk_buff *skb, + struct batadv_hard_iface *hard_iface); + +void batadv_aggr_purge_orig(struct batadv_orig_node *orig); +void batadv_aggr_hardif_init(struct batadv_hard_iface *hard_iface); int batadv_aggr_mesh_init(struct batadv_priv *bat_priv); void batadv_aggr_mesh_free(struct batadv_priv *bat_priv);
@@ -37,6 +45,29 @@ static inline void batadv_aggr_add_counter_rx(struct batadv_priv *bat_priv, { }
+static inline void +batadv_aggr_hardif_start(struct batadv_hard_iface *hard_iface) +{ +} + +static inline void batadv_aggr_hardif_stop(struct batadv_hard_iface *hard_iface) +{ +} + +static inline int batadv_aggr_queue(struct sk_buff *skb, + struct batadv_hard_iface *hard_iface) +{ + return NET_XMIT_DROP; +} + +static inline void batadv_aggr_purge_orig(struct batadv_orig_node *orig) +{ +} + +static inline void batadv_aggr_hardif_init(struct batadv_hard_iface *hard_iface) +{ +} + static inline int batadv_aggr_mesh_init(struct batadv_priv *bat_priv) { return 0; diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c index e27a23f..3b91e06 100644 --- a/net/batman-adv/bat_iv_ogm.c +++ b/net/batman-adv/bat_iv_ogm.c @@ -684,7 +684,7 @@ static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff, unsigned int skb_size; atomic_t *queue_left = own_packet ? NULL : &bat_priv->batman_queue_left;
- if (atomic_read(&bat_priv->aggregated_ogms) && + if (atomic_read(&bat_priv->aggregation) && packet_len < BATADV_MAX_AGGREGATION_BYTES) skb_size = BATADV_MAX_AGGREGATION_BYTES; else @@ -777,7 +777,7 @@ static void batadv_iv_ogm_queue_add(struct batadv_priv *bat_priv, /* find position for the packet in the forward queue */ spin_lock_bh(&bat_priv->forw_bat_list_lock); /* own packets are not to be aggregated */ - if (atomic_read(&bat_priv->aggregated_ogms) && !own_packet) { + if (atomic_read(&bat_priv->aggregation) && !own_packet) { hlist_for_each_entry(forw_packet_pos, &bat_priv->forw_bat_list, list) { if (batadv_iv_ogm_can_aggregate(batadv_ogm_packet, @@ -803,7 +803,7 @@ static void batadv_iv_ogm_queue_add(struct batadv_priv *bat_priv, * we hold it back for a while, so that it might be aggregated * later on */ - if (!own_packet && atomic_read(&bat_priv->aggregated_ogms)) + if (!own_packet && atomic_read(&bat_priv->aggregation)) send_time += max_aggregation_jiffies;
batadv_iv_ogm_aggregate_new(packet_buff, packet_len, diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c index e348f76..091b39f 100644 --- a/net/batman-adv/hard-interface.c +++ b/net/batman-adv/hard-interface.c @@ -38,6 +38,7 @@ #include <net/net_namespace.h> #include <net/rtnetlink.h>
+#include "aggregation.h" #include "bat_v.h" #include "bridge_loop_avoidance.h" #include "debugfs.h" @@ -746,6 +747,8 @@ int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface, if (ret < 0) goto err_upper;
+ batadv_aggr_hardif_start(hard_iface); + hard_iface->if_num = bat_priv->num_ifaces; bat_priv->num_ifaces++; hard_iface->if_status = BATADV_IF_INACTIVE; @@ -832,6 +835,7 @@ void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface, batadv_hardif_put(new_if); }
+ batadv_aggr_hardif_stop(hard_iface); bat_priv->algo_ops->iface.disable(hard_iface); hard_iface->if_status = BATADV_IF_NOT_IN_USE;
@@ -901,6 +905,7 @@ batadv_hardif_add_interface(struct net_device *net_dev) hard_iface->num_bcasts = BATADV_NUM_BCASTS_WIRELESS;
batadv_v_hardif_init(hard_iface); + batadv_aggr_hardif_init(hard_iface);
batadv_check_known_mac_addr(hard_iface->net_dev); kref_get(&hard_iface->refcount); diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c index 28caafa..f4f1b24 100644 --- a/net/batman-adv/main.c +++ b/net/batman-adv/main.c @@ -349,6 +349,16 @@ int batadv_max_header_len(void) header_len = max_t(int, header_len, sizeof(struct batadv_coded_packet)); #endif +#ifdef CONFIG_BATMAN_ADV_AGGR + header_len = max_t(int, header_len, + sizeof(struct batadv_aggr_packet) + + sizeof(struct batadv_tvlv_hdr) + + sizeof(struct batadv_bcast_packet) - + sizeof(((struct batadv_bcast_packet *)0) + ->packet_type) - + sizeof(((struct batadv_bcast_packet *)0) + ->version)); +#endif
return header_len + ETH_HLEN; } diff --git a/net/batman-adv/main.h b/net/batman-adv/main.h index d224034..abe5201 100644 --- a/net/batman-adv/main.h +++ b/net/batman-adv/main.h @@ -148,6 +148,7 @@ enum batadv_mesh_state {
#define BATADV_BCAST_QUEUE_LEN 256 #define BATADV_BATMAN_QUEUE_LEN 256 +#define BATADV_AGGR_QUEUE_LEN 512
enum batadv_uev_action { BATADV_UEV_ADD = 0, diff --git a/net/batman-adv/originator.c b/net/batman-adv/originator.c index 8e2a4b2..809704a 100644 --- a/net/batman-adv/originator.c +++ b/net/batman-adv/originator.c @@ -38,6 +38,7 @@ #include <net/sock.h> #include <uapi/linux/batman_adv.h>
+#include "aggregation.h" #include "bat_algo.h" #include "distributed-arp-table.h" #include "fragmentation.h" @@ -857,6 +858,7 @@ static void batadv_orig_node_free_rcu(struct rcu_head *rcu) batadv_mcast_purge_orig(orig_node);
batadv_frag_purge_orig(orig_node, NULL); + batadv_aggr_purge_orig(orig_node);
if (orig_node->bat_priv->algo_ops->orig.free) orig_node->bat_priv->algo_ops->orig.free(orig_node); diff --git a/net/batman-adv/send.c b/net/batman-adv/send.c index 403df59..887bdbe 100644 --- a/net/batman-adv/send.c +++ b/net/batman-adv/send.c @@ -40,6 +40,7 @@ #include <linux/stddef.h> #include <linux/workqueue.h>
+#include "aggregation.h" #include "distributed-arp-table.h" #include "fragmentation.h" #include "gateway_client.h" @@ -126,6 +127,14 @@ int batadv_send_skb_packet(struct sk_buff *skb, int batadv_send_broadcast_skb(struct sk_buff *skb, struct batadv_hard_iface *hard_iface) { + int ret; + + skb_reset_network_header(skb); + + ret = batadv_aggr_queue(skb, hard_iface); + if (ret == NET_XMIT_SUCCESS) + return ret; + return batadv_send_skb_packet(skb, hard_iface, batadv_broadcast_addr); }
diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c index 32a34a2..a931b2e 100644 --- a/net/batman-adv/soft-interface.c +++ b/net/batman-adv/soft-interface.c @@ -779,7 +779,10 @@ static int batadv_softif_init_late(struct net_device *dev) if (!bat_priv->bat_counters) return -ENOMEM;
- atomic_set(&bat_priv->aggregated_ogms, 1); + atomic_set(&bat_priv->aggregation, 1); +#ifdef CONFIG_BATMAN_ADV_AGGR + atomic_set(&bat_priv->aggr_num_disabled, 0); +#endif atomic_set(&bat_priv->bonding, 0); #ifdef CONFIG_BATMAN_ADV_BLA atomic_set(&bat_priv->bridge_loop_avoidance, 1); @@ -962,10 +965,16 @@ static const struct { { "frag_fwd" }, { "frag_fwd_bytes" }, #ifdef CONFIG_BATMAN_ADV_AGGR + { "aggr_tx" }, + { "aggr_tx_bytes" }, { "aggr_rx" }, { "aggr_rx_bytes" }, + { "aggr_parts_tx" }, + { "aggr_parts_tx_bytes" }, { "aggr_parts_rx" }, { "aggr_parts_rx_bytes" }, + { "aggr_queue_full" }, + { "aggr_urgent" }, #endif { "tt_request_tx" }, { "tt_request_rx" }, diff --git a/net/batman-adv/sysfs.c b/net/batman-adv/sysfs.c index 0ae8b30..387d324 100644 --- a/net/batman-adv/sysfs.c +++ b/net/batman-adv/sysfs.c @@ -665,7 +665,9 @@ static ssize_t batadv_store_isolation_mark(struct kobject *kobj, return count; }
-BATADV_ATTR_SIF_BOOL(aggregated_ogms, 0644, NULL); +BATADV_ATTR_SIF_BOOL(aggregation, 0644, NULL); +static BATADV_ATTR(aggregated_ogms, 0644, batadv_show_aggregation, + batadv_store_aggregation); BATADV_ATTR_SIF_BOOL(bonding, 0644, NULL); #ifdef CONFIG_BATMAN_ADV_BLA BATADV_ATTR_SIF_BOOL(bridge_loop_avoidance, 0644, batadv_bla_status_update); @@ -698,6 +700,7 @@ static BATADV_ATTR(isolation_mark, 0644, batadv_show_isolation_mark,
static struct batadv_attribute *batadv_mesh_attrs[] = { &batadv_attr_aggregated_ogms, + &batadv_attr_aggregation, &batadv_attr_bonding, #ifdef CONFIG_BATMAN_ADV_BLA &batadv_attr_bridge_loop_avoidance, diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index e9d7b99..359f330 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -133,6 +133,20 @@ enum batadv_hard_iface_wifi_flags { BATADV_HARDIF_WIFI_CFG80211_INDIRECT = BIT(3), };
+#ifdef CONFIG_BATMAN_ADV_AGGR +/** + * struct batadv_hard_iface_aggr - per hard-interface aggregation data + * @aggr_list: list for to be aggregated broadcast packets + * @aggr_list_lock: lock protecting aggr_list + * @work: work item for periodic aggregation packet transmissions + */ +struct batadv_hard_iface_aggr { + struct sk_buff_head aggr_list; + spinlock_t aggr_list_lock; /* protects aggr_list */ + struct delayed_work work; +}; +#endif + /** * struct batadv_hard_iface - network device known to batman-adv * @list: list node for batadv_hardif_list @@ -149,6 +163,7 @@ enum batadv_hard_iface_wifi_flags { * @rcu: struct used for freeing in an RCU-safe manner * @bat_iv: per hard-interface B.A.T.M.A.N. IV data * @bat_v: per hard-interface B.A.T.M.A.N. V data + * @aggr: per hard-interface aggregation data * @debug_dir: dentry for nc subdir in batman-adv directory in debugfs * @neigh_list: list of unique single hop neighbors via this interface * @neigh_list_lock: lock protecting neigh_list @@ -169,6 +184,9 @@ struct batadv_hard_iface { #ifdef CONFIG_BATMAN_ADV_BATMAN_V struct batadv_hard_iface_bat_v bat_v; #endif +#ifdef CONFIG_BATMAN_ADV_AGGR + struct batadv_hard_iface_aggr aggr; +#endif struct dentry *debug_dir; struct hlist_head neigh_list; /* neigh_list_lock protects: neigh_list */ @@ -376,12 +394,14 @@ struct batadv_orig_node { * @BATADV_ORIG_CAPA_HAS_TT: orig node has tt capability * @BATADV_ORIG_CAPA_HAS_MCAST: orig node has some multicast capability * (= orig node announces a tvlv of type BATADV_TVLV_MCAST) + * @BATADV_ORIG_CAPA_HAS_AGGR: orig node has broadcast aggregation capability */ enum batadv_orig_capabilities { BATADV_ORIG_CAPA_HAS_DAT, BATADV_ORIG_CAPA_HAS_NC, BATADV_ORIG_CAPA_HAS_TT, BATADV_ORIG_CAPA_HAS_MCAST, + BATADV_ORIG_CAPA_HAS_AGGR, };
/** @@ -556,10 +576,17 @@ struct batadv_bcast_duplist_entry { * @BATADV_CNT_FRAG_RX_BYTES: received fragment traffic bytes counter * @BATADV_CNT_FRAG_FWD: forwarded fragment traffic packet counter * @BATADV_CNT_FRAG_FWD_BYTES: forwarded fragment traffic bytes counter + * @BATADV_CNT_AGGR_TX: transmitted aggregation traffic packet count + * @BATADV_CNT_AGGR_TX_BYTES: transmitted aggregation traffic bytes counter * @BATADV_CNT_AGGR_RX: received aggregation traffic packet count * @BATADV_CNT_AGGR_RX_BYTES: received aggregation traffic bytes counter + * @BATADV_CNT_AGGR_PARTS_TX: transmitted aggregated traffic packet counter + * @BATADV_CNT_AGGR_PARTS_TX_BYTES: transmitted aggregated bytes counter * @BATADV_CNT_AGGR_PARTS_RX: received aggregated traffic packet counter * @BATADV_CNT_AGGR_PARTS_RX_BYTES: received aggregated bytes counter + * @BATADV_CNT_AGGR_QUEUE_FULL: counter for packets dismissed for aggregation + * due to a full aggregation queue + * @BATADV_CNT_AGGR_URGENT: counter for early aggregation packet transmissions * @BATADV_CNT_TT_REQUEST_TX: transmitted tt req traffic packet counter * @BATADV_CNT_TT_REQUEST_RX: received tt req traffic packet counter * @BATADV_CNT_TT_RESPONSE_TX: transmitted tt resp traffic packet counter @@ -604,10 +631,16 @@ enum batadv_counters { BATADV_CNT_FRAG_FWD, BATADV_CNT_FRAG_FWD_BYTES, #ifdef CONFIG_BATMAN_ADV_AGGR + BATADV_CNT_AGGR_TX, + BATADV_CNT_AGGR_TX_BYTES, BATADV_CNT_AGGR_RX, BATADV_CNT_AGGR_RX_BYTES, + BATADV_CNT_AGGR_PARTS_TX, + BATADV_CNT_AGGR_PARTS_TX_BYTES, BATADV_CNT_AGGR_PARTS_RX, BATADV_CNT_AGGR_PARTS_RX_BYTES, + BATADV_CNT_AGGR_QUEUE_FULL, + BATADV_CNT_AGGR_URGENT, #endif BATADV_CNT_TT_REQUEST_TX, BATADV_CNT_TT_REQUEST_RX, @@ -1011,7 +1044,9 @@ struct batadv_priv_bat_v { * @mesh_state: current status of the mesh (inactive/active/deactivating) * @soft_iface: net device which holds this struct as private data * @bat_counters: mesh internal traffic statistic counters (see batadv_counters) - * @aggregated_ogms: bool indicating whether OGM aggregation is enabled + * @aggregation: bool indicating whether aggregation is enabled + * @aggr_num_disabled: number of nodes that have no broadcast aggregation + * capability * @bonding: bool indicating whether traffic bonding is enabled * @fragmentation: bool indicating whether traffic fragmentation is enabled * @packet_size_max: max packet size that can be transmitted via @@ -1065,7 +1100,10 @@ struct batadv_priv { atomic_t mesh_state; struct net_device *soft_iface; u64 __percpu *bat_counters; /* Per cpu counters */ - atomic_t aggregated_ogms; + atomic_t aggregation; +#ifdef CONFIG_BATMAN_ADV_AGGR + atomic_t aggr_num_disabled; +#endif atomic_t bonding; atomic_t fragmentation; atomic_t packet_size_max;
With this patch, aggregating a broadcast packet multiple times into the same aggregation packet is avoided. Otherwise such aggregation would defeat the purpose of retransmissions (i.e. increasing reliability).
Instead packets which are supposed to be retransmitted are just put back into the aggregation queue and will be part of the next aggregation packet again.
Signed-off-by: Linus Lüssing linus.luessing@c0d3.blue --- net/batman-adv/aggregation.c | 69 +++++++++++++++++++++++++++++++++++--------- net/batman-adv/bat_iv_ogm.c | 3 +- net/batman-adv/send.c | 66 +++++++++++++++++++++++++++++++++++------- net/batman-adv/send.h | 8 ++++- net/batman-adv/types.h | 3 ++ 5 files changed, 124 insertions(+), 25 deletions(-)
diff --git a/net/batman-adv/aggregation.c b/net/batman-adv/aggregation.c index 21b29be..1dd6eb9 100644 --- a/net/batman-adv/aggregation.c +++ b/net/batman-adv/aggregation.c @@ -253,7 +253,41 @@ batadv_aggr_queue_is_full(struct batadv_hard_iface *hard_iface) }
/** + * batadv_aggr_queue_tail - add packet to tail of an aggregation queue + * @skb: the packet to queue + * @hard_iface: the interface to queue on + * + * Tries to add a broadcast packet to an aggregation queue. This might fail if + * the queue has no more free slots available. In that case, the caller needs to + * take care of freeing the skb. + * + * Return: True on successful queueing, false otherwise. + */ +static bool batadv_aggr_queue_tail(struct sk_buff *skb, + struct batadv_hard_iface *hard_iface) +{ + struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface); + bool ret = true; + + spin_lock_bh(&hard_iface->aggr.aggr_list_lock); + if (batadv_aggr_queue_is_full(hard_iface)) { + batadv_inc_counter(bat_priv, BATADV_CNT_AGGR_QUEUE_FULL); + ret = false; + } else { + batadv_inc_counter(bat_priv, BATADV_CNT_AGGR_PARTS_TX); + batadv_add_counter(bat_priv, BATADV_CNT_AGGR_PARTS_TX_BYTES, + skb->len + ETH_HLEN); + + skb_queue_tail(&hard_iface->aggr.aggr_list, skb); + } + spin_unlock_bh(&hard_iface->aggr.aggr_list_lock); + + return ret; +} + +/** * batadv_aggr_squash_chunk - squash packets into an aggregate + * @hard_iface: the interface to potentially requeue on * @head: a list of to be squashed packets * @size: the size of the to be created aggregation packet * (excluding the ethernet header) @@ -261,11 +295,14 @@ batadv_aggr_queue_is_full(struct batadv_hard_iface *hard_iface) * Allocates an aggregation packet and squashes the provided list of broadcast * packets into it. The provided list of packets is freed/consumed. * + * Batman broadcast packets are potentially requeued. + * * Return: An aggregation packet ready for transmission on success, NULL * otherwise. */ static struct sk_buff * -batadv_aggr_squash_chunk(struct sk_buff_head *head, +batadv_aggr_squash_chunk(struct batadv_hard_iface *hard_iface, + struct sk_buff_head *head, int size) { struct sk_buff *skb, *skb_tmp, *skb_aggr; @@ -296,7 +333,15 @@ batadv_aggr_squash_chunk(struct sk_buff_head *head, to = skb_put(skb_aggr, len); skb_copy_bits(skb, offset, to, len); skb_unlink(skb, head); - consume_skb(skb); + + batadv_send_bcasts_inc(skb); + + if (batadv_send_bcasts_left(skb, hard_iface)) { + if (!batadv_aggr_queue_tail(skb, hard_iface)) + kfree_skb(skb); + } else { + consume_skb(skb); + }
tvlv_len += len + sizeof(struct batadv_tvlv_hdr); } @@ -345,7 +390,7 @@ static bool batadv_aggr_send_chunk(struct batadv_hard_iface *hard_iface) skb_queue_head_init(&head); emptied = batadv_aggr_get_chunk(hard_iface, &head, &size);
- skb = batadv_aggr_squash_chunk(&head, size); + skb = batadv_aggr_squash_chunk(hard_iface, &head, size); if (!skb) goto out;
@@ -394,6 +439,7 @@ static void batadv_aggr_work(struct work_struct *work) int batadv_aggr_queue(struct sk_buff *skb, struct batadv_hard_iface *hard_iface) { struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface); + int ret;
if (!atomic_read(&bat_priv->aggregation)) return NET_XMIT_DROP; @@ -401,18 +447,15 @@ int batadv_aggr_queue(struct sk_buff *skb, struct batadv_hard_iface *hard_iface) if (atomic_read(&bat_priv->aggr_num_disabled)) return NET_XMIT_DROP;
- if (batadv_aggr_queue_is_full(hard_iface)) { - batadv_inc_counter(bat_priv, BATADV_CNT_AGGR_QUEUE_FULL); - return NET_XMIT_DROP; + /* we handle rebroadcasts here instead of the forw_packet API */ + if (batadv_send_is_rebroadcast(skb)) { + consume_skb(skb); + return NET_XMIT_SUCCESS; }
- spin_lock_bh(&hard_iface->aggr.aggr_list_lock); - skb_queue_tail(&hard_iface->aggr.aggr_list, skb); - spin_unlock_bh(&hard_iface->aggr.aggr_list_lock); - - batadv_inc_counter(bat_priv, BATADV_CNT_AGGR_PARTS_TX); - batadv_add_counter(bat_priv, BATADV_CNT_AGGR_PARTS_TX_BYTES, - skb->len + ETH_HLEN); + ret = batadv_aggr_queue_tail(skb, hard_iface); + if (!ret) + return NET_XMIT_DROP;
return NET_XMIT_SUCCESS; } diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c index 3b91e06..7d92752 100644 --- a/net/batman-adv/bat_iv_ogm.c +++ b/net/batman-adv/bat_iv_ogm.c @@ -697,7 +697,8 @@ static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff, return;
forw_packet_aggr = batadv_forw_packet_alloc(if_incoming, if_outgoing, - queue_left, bat_priv, skb); + queue_left, bat_priv, skb, + false); if (!forw_packet_aggr) { kfree_skb(skb); return; diff --git a/net/batman-adv/send.c b/net/batman-adv/send.c index 887bdbe..52660cd 100644 --- a/net/batman-adv/send.c +++ b/net/batman-adv/send.c @@ -492,6 +492,8 @@ void batadv_forw_packet_free(struct batadv_forw_packet *forw_packet, * @queue_left: The (optional) queue counter to decrease * @bat_priv: The bat_priv for the mesh of this forw_packet * @skb: The raw packet this forwarding packet shall contain + * @resend: Whether this packet should be transmitted more than once on wireless + * interfaces * * Allocates a forwarding packet and tries to get a reference to the * (optional) if_incoming, if_outgoing and queue_left. If queue_left @@ -504,7 +506,8 @@ batadv_forw_packet_alloc(struct batadv_hard_iface *if_incoming, struct batadv_hard_iface *if_outgoing, atomic_t *queue_left, struct batadv_priv *bat_priv, - struct sk_buff *skb) + struct sk_buff *skb, + bool resend) { struct batadv_forw_packet *forw_packet; const char *qname; @@ -542,6 +545,8 @@ batadv_forw_packet_alloc(struct batadv_hard_iface *if_incoming, forw_packet->if_outgoing = if_outgoing; forw_packet->num_packets = 0;
+ BATADV_SKB_CB(forw_packet->skb)->resend = resend; + return forw_packet;
err: @@ -775,7 +780,7 @@ int batadv_add_bcast_packet_to_list(struct batadv_priv *bat_priv,
forw_packet = batadv_forw_packet_alloc(primary_if, NULL, &bat_priv->bcast_queue_left, - bat_priv, newskb); + bat_priv, newskb, true); batadv_hardif_put(primary_if); if (!forw_packet) goto err_packet_free; @@ -799,8 +804,8 @@ int batadv_add_bcast_packet_to_list(struct batadv_priv *bat_priv, }
/** - * batadv_forw_packet_bcasts_left - check if a retransmission is necessary - * @forw_packet: the forwarding packet to check + * batadv_send_bcasts_left - check if a retransmission is necessary + * @skb: the packet to check * @hard_iface: the interface to check on * * Checks whether a given packet has any (re)transmissions left on the provided @@ -812,10 +817,10 @@ int batadv_add_bcast_packet_to_list(struct batadv_priv *bat_priv, * * Return: True if (re)transmissions are left, false otherwise. */ -static bool -batadv_forw_packet_bcasts_left(struct batadv_forw_packet *forw_packet, - struct batadv_hard_iface *hard_iface) +bool batadv_send_bcasts_left(struct sk_buff *skb, + struct batadv_hard_iface *hard_iface) { + bool resend = !hard_iface || BATADV_SKB_CB(skb)->resend; unsigned int max;
if (hard_iface) @@ -823,7 +828,37 @@ batadv_forw_packet_bcasts_left(struct batadv_forw_packet *forw_packet, else max = BATADV_NUM_BCASTS_MAX;
- return BATADV_SKB_CB(forw_packet->skb)->num_bcasts < max; + return resend && BATADV_SKB_CB(skb)->num_bcasts < max; +} + +/** + * batadv_forw_packet_bcasts_left - check if a retransmission is necessary + * @forw_packet: the forwarding packet to check + * @hard_iface: the interface to check on + * + * Checks whether a given packet has any (re)transmissions left on the provided + * interface. + * + * hard_iface may be NULL: In that case the number of transmissions this skb had + * so far is compared with the maximum amount of retransmissions independent of + * any interface instead. + * + * Return: True if (re)transmissions are left, false otherwise. + */ +static bool +batadv_forw_packet_bcasts_left(struct batadv_forw_packet *forw_packet, + struct batadv_hard_iface *hard_iface) +{ + return batadv_send_bcasts_left(forw_packet->skb, hard_iface); +} + +/** + * batadv_send_bcasts_inc - increment the retransmission counter of an skb + * @skb: the packet to increase the counter for + */ +void batadv_send_bcasts_inc(struct sk_buff *skb) +{ + BATADV_SKB_CB(skb)->num_bcasts++; }
/** @@ -833,7 +868,18 @@ batadv_forw_packet_bcasts_left(struct batadv_forw_packet *forw_packet, static void batadv_forw_packet_bcasts_inc(struct batadv_forw_packet *forw_packet) { - BATADV_SKB_CB(forw_packet->skb)->num_bcasts++; + batadv_send_bcasts_inc(forw_packet->skb); +} + +/** + * batadv_send_is_rebroadcast - check whether this packet was transmitted before + * @skb: the packet to check + * + * Return: True if this packet was transmitted already, false otherwise. + */ +bool batadv_send_is_rebroadcast(struct sk_buff *skb) +{ + return BATADV_SKB_CB(skb)->num_bcasts > 0; }
/** @@ -844,7 +890,7 @@ batadv_forw_packet_bcasts_inc(struct batadv_forw_packet *forw_packet) */ bool batadv_forw_packet_is_rebroadcast(struct batadv_forw_packet *forw_packet) { - return BATADV_SKB_CB(forw_packet->skb)->num_bcasts > 0; + return batadv_send_is_rebroadcast(forw_packet->skb); }
static void batadv_send_outstanding_bcast_packet(struct work_struct *work) diff --git a/net/batman-adv/send.h b/net/batman-adv/send.h index a16b34f..cd1ee81 100644 --- a/net/batman-adv/send.h +++ b/net/batman-adv/send.h @@ -35,13 +35,19 @@ batadv_forw_packet_alloc(struct batadv_hard_iface *if_incoming, struct batadv_hard_iface *if_outgoing, atomic_t *queue_left, struct batadv_priv *bat_priv, - struct sk_buff *skb); + struct sk_buff *skb, + bool resend); bool batadv_forw_packet_steal(struct batadv_forw_packet *packet, spinlock_t *l); void batadv_forw_packet_ogmv1_queue(struct batadv_priv *bat_priv, struct batadv_forw_packet *forw_packet, unsigned long send_time); bool batadv_forw_packet_is_rebroadcast(struct batadv_forw_packet *forw_packet);
+bool batadv_send_bcasts_left(struct sk_buff *skb, + struct batadv_hard_iface *hard_iface); +void batadv_send_bcasts_inc(struct sk_buff *skb); +bool batadv_send_is_rebroadcast(struct sk_buff *skb); + int batadv_send_skb_to_orig(struct sk_buff *skb, struct batadv_orig_node *orig_node, struct batadv_hard_iface *recv_if); diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index 359f330..de4282f 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -1424,10 +1424,13 @@ struct batadv_nc_packet { * @decoded: Marks a skb as decoded, which is checked when searching for coding * opportunities in network-coding.c * @num_bcasts: Counter for broadcast packet retransmissions + * @resend: Whether this packet should be transmitted more than once on wireless + * interfaces */ struct batadv_skb_cb { bool decoded; unsigned int num_bcasts; + bool resend; };
/**
Hrm, forgot to set the subject in the git-send-email-compose message...
Here you go :).
b.a.t.m.a.n@lists.open-mesh.org