Hi,
Here is a first patchset to reintroduce aggregation support for BATMAN V, too - 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
PS: Requires: "batman-adv: Introduce packet type independent TVLV handler API" (40e5c9e798ec, available on patchwork or linus/aggregation branch)
---
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
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 c79699d..3718ac9 100644 --- a/net/batman-adv/bat_iv_ogm.c +++ b/net/batman-adv/bat_iv_ogm.c @@ -1409,7 +1409,7 @@ out: * @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) @@ -1613,7 +1613,7 @@ out: * @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 6be707d..157288a 100644 --- a/net/batman-adv/bat_v_ogm.c +++ b/net/batman-adv/bat_v_ogm.c @@ -606,7 +606,7 @@ out: */ 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, @@ -676,7 +676,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 2e5886f..4259993 100644 --- a/net/batman-adv/tvlv.c +++ b/net/batman-adv/tvlv.c @@ -498,12 +498,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;
@@ -518,14 +518,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); @@ -536,7 +538,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; } @@ -627,7 +629,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 areintroduced 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 5efe40b..6ea29c5 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" @@ -180,6 +181,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; @@ -227,6 +232,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); @@ -508,6 +514,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); @@ -531,6 +538,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 4de324f..c2c20fd 100644 --- a/net/batman-adv/packet.h +++ b/net/batman-adv/packet.h @@ -31,6 +31,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 @@ -47,6 +48,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, @@ -154,6 +156,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, @@ -162,6 +166,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, };
/** @@ -468,6 +474,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 7fd740b..b74779b 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" @@ -1136,6 +1137,66 @@ free_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 5d099b2..1b0ef7e 100644 --- a/net/batman-adv/soft-interface.c +++ b/net/batman-adv/soft-interface.c @@ -1149,6 +1149,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 4259993..b505640 100644 --- a/net/batman-adv/tvlv.c +++ b/net/batman-adv/tvlv.c @@ -88,10 +88,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)) @@ -431,6 +433,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;
@@ -439,6 +442,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 d189860..0e25f01 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -575,6 +575,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 @@ -618,6 +622,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, @@ -1648,16 +1658,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 3718ac9..f493fc8 100644 --- a/net/batman-adv/bat_iv_ogm.c +++ b/net/batman-adv/bat_iv_ogm.c @@ -688,7 +688,7 @@ static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff, if (!forw_packet_aggr) return;
- 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 @@ -776,7 +776,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, @@ -802,7 +802,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 c502c41..42d8e2b 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" @@ -759,6 +760,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; @@ -845,6 +848,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;
@@ -914,6 +918,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 6ea29c5..5d96eae 100644 --- a/net/batman-adv/main.c +++ b/net/batman-adv/main.c @@ -350,6 +350,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 57a8103..c9830ef 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 ed802b5..f44aa16 100644 --- a/net/batman-adv/originator.c +++ b/net/batman-adv/originator.c @@ -39,6 +39,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" @@ -902,6 +903,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 b60efbb..4789cbe 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 @@ send_skb_err: 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 1b0ef7e..abe21f1 100644 --- a/net/batman-adv/soft-interface.c +++ b/net/batman-adv/soft-interface.c @@ -798,7 +798,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); @@ -1150,10 +1153,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 0e25f01..575fdf1 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -145,6 +145,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 @@ -161,6 +175,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 @@ -181,6 +196,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 */ @@ -388,12 +406,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, };
/** @@ -575,10 +595,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 @@ -623,10 +650,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, @@ -1031,7 +1064,9 @@ struct batadv_priv_bat_v { * @soft_iface: net device which holds this struct as private data * @stats: structure holding the data for the ndo_get_stats() call * @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 @@ -1086,7 +1121,10 @@ struct batadv_priv { struct net_device *soft_iface; struct net_device_stats stats; 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;
An skb is assigned to a forw_packet only once, shortly after the forw_packet allocation.
With this patch the assignment is moved into the this allocation function.
Signed-off-by: Linus Lüssing linus.luessing@c0d3.blue --- net/batman-adv/bat_iv_ogm.c | 17 +++++++++-------- net/batman-adv/send.c | 21 ++++++++++++--------- net/batman-adv/send.h | 3 ++- 3 files changed, 23 insertions(+), 18 deletions(-)
diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c index f493fc8..896a387 100644 --- a/net/batman-adv/bat_iv_ogm.c +++ b/net/batman-adv/bat_iv_ogm.c @@ -679,15 +679,11 @@ static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff, { struct batadv_priv *bat_priv = netdev_priv(if_incoming->soft_iface); struct batadv_forw_packet *forw_packet_aggr; + struct sk_buff *skb; unsigned char *skb_buff; unsigned int skb_size; atomic_t *queue_left = own_packet ? NULL : &bat_priv->batman_queue_left;
- forw_packet_aggr = batadv_forw_packet_alloc(if_incoming, if_outgoing, - queue_left, bat_priv); - if (!forw_packet_aggr) - return; - if (atomic_read(&bat_priv->aggregation) && packet_len < BATADV_MAX_AGGREGATION_BYTES) skb_size = BATADV_MAX_AGGREGATION_BYTES; @@ -696,9 +692,14 @@ static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff,
skb_size += ETH_HLEN;
- forw_packet_aggr->skb = netdev_alloc_skb_ip_align(NULL, skb_size); - if (!forw_packet_aggr->skb) { - batadv_forw_packet_free(forw_packet_aggr, true); + skb = netdev_alloc_skb_ip_align(NULL, skb_size); + if (!skb) + return; + + forw_packet_aggr = batadv_forw_packet_alloc(if_incoming, if_outgoing, + queue_left, bat_priv, skb); + if (!forw_packet_aggr) { + kfree_skb(skb); return; }
diff --git a/net/batman-adv/send.c b/net/batman-adv/send.c index 4789cbe..d2fa511 100644 --- a/net/batman-adv/send.c +++ b/net/batman-adv/send.c @@ -491,6 +491,7 @@ void batadv_forw_packet_free(struct batadv_forw_packet *forw_packet, * @if_outgoing: The (optional) if_outgoing to be grabbed * @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 * * Allocates a forwarding packet and tries to get a reference to the * (optional) if_incoming, if_outgoing and queue_left. If queue_left @@ -502,7 +503,8 @@ struct batadv_forw_packet * 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 batadv_priv *bat_priv, + struct sk_buff *skb) { struct batadv_forw_packet *forw_packet; const char *qname; @@ -534,7 +536,7 @@ batadv_forw_packet_alloc(struct batadv_hard_iface *if_incoming,
INIT_HLIST_NODE(&forw_packet->list); INIT_HLIST_NODE(&forw_packet->cleanup_list); - forw_packet->skb = NULL; + forw_packet->skb = skb; forw_packet->queue_left = queue_left; forw_packet->if_incoming = if_incoming; forw_packet->if_outgoing = if_outgoing; @@ -765,22 +767,23 @@ int batadv_add_bcast_packet_to_list(struct batadv_priv *bat_priv, if (!primary_if) goto err;
+ newskb = skb_copy(skb, GFP_ATOMIC); + if (!newskb) { + batadv_hardif_put(primary_if); + goto err; + } + forw_packet = batadv_forw_packet_alloc(primary_if, NULL, &bat_priv->bcast_queue_left, - bat_priv); + bat_priv, newskb); batadv_hardif_put(primary_if); if (!forw_packet) - goto err; - - newskb = skb_copy(skb, GFP_ATOMIC); - if (!newskb) goto err_packet_free;
/* as we have a copy now, it is safe to decrease the TTL */ bcast_packet = (struct batadv_bcast_packet *)newskb->data; bcast_packet->ttl--;
- forw_packet->skb = newskb; forw_packet->own = own_packet;
INIT_DELAYED_WORK(&forw_packet->delayed_work, @@ -790,7 +793,7 @@ int batadv_add_bcast_packet_to_list(struct batadv_priv *bat_priv, return NETDEV_TX_OK;
err_packet_free: - batadv_forw_packet_free(forw_packet, true); + kfree_skb(newskb); err: return NETDEV_TX_BUSY; } diff --git a/net/batman-adv/send.h b/net/batman-adv/send.h index f21166d..8e75890 100644 --- a/net/batman-adv/send.h +++ b/net/batman-adv/send.h @@ -34,7 +34,8 @@ struct batadv_forw_packet * 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 batadv_priv *bat_priv, + struct sk_buff *skb); 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,
This patch refactors the num_packets counter of a forw_packet in the following three ways:
1) Removed dual-use of forw_packet::num_packets: -> now for aggregation purposes only 2) Using forw_packet::skb::cb::num_bcasts instead: -> for easier access in aggregation code later 3) make access to num_bcasts private to batadv_forw_packet_*()
Signed-off-by: Linus Lüssing linus.luessing@c0d3.blue --- net/batman-adv/distributed-arp-table.c | 2 +- net/batman-adv/main.c | 3 ++ net/batman-adv/send.c | 50 ++++++++++++++++++++++++++++++++-- net/batman-adv/send.h | 1 + net/batman-adv/types.h | 4 ++- 5 files changed, 55 insertions(+), 5 deletions(-)
diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c index fa3768a..959ffae 100644 --- a/net/batman-adv/distributed-arp-table.c +++ b/net/batman-adv/distributed-arp-table.c @@ -1256,7 +1256,7 @@ bool batadv_dat_drop_broadcast_packet(struct batadv_priv *bat_priv, /* If this packet is an ARP_REQUEST and the node already has the * information that it is going to ask, then the packet can be dropped */ - if (forw_packet->num_packets) + if (batadv_forw_packet_is_rebroadcast(forw_packet)) goto out;
vid = batadv_dat_get_vid(forw_packet->skb, &hdr_size); diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c index 5d96eae..0faf28f 100644 --- a/net/batman-adv/main.c +++ b/net/batman-adv/main.c @@ -534,6 +534,9 @@ static void batadv_recv_handler_init(void) BUILD_BUG_ON(sizeof(struct batadv_tvlv_tt_change) != 12); BUILD_BUG_ON(sizeof(struct batadv_tvlv_roam_adv) != 8);
+ i = FIELD_SIZEOF(struct sk_buff, cb); + BUILD_BUG_ON(sizeof(struct batadv_skb_cb) > i); + /* broadcast packet */ batadv_rx_handler[BATADV_BCAST] = batadv_recv_bcast_packet;
diff --git a/net/batman-adv/send.c b/net/batman-adv/send.c index d2fa511..08b61e0 100644 --- a/net/batman-adv/send.c +++ b/net/batman-adv/send.c @@ -798,6 +798,50 @@ err: return NETDEV_TX_BUSY; }
+/** + * 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) +{ + int max = hard_iface ? hard_iface->num_bcasts : BATADV_NUM_BCASTS_MAX; + + return BATADV_SKB_CB(forw_packet->skb)->num_bcasts < max; +} + +/** + * batadv_forw_packet_bcasts_inc - increment retransmission counter of a packet + * @forw_packet: the packet to increase the counter for + */ +static void +batadv_forw_packet_bcasts_inc(struct batadv_forw_packet *forw_packet) +{ + BATADV_SKB_CB(forw_packet->skb)->num_bcasts++; +} + +/** + * batadv_forw_packet_is_rebroadcast - check packet for previous transmissions + * @forw_packet: the packet to check + * + * Return: True if this packet was transmitted before, false otherwise. + */ +bool batadv_forw_packet_is_rebroadcast(struct batadv_forw_packet *forw_packet) +{ + return BATADV_SKB_CB(forw_packet->skb)->num_bcasts > 0; +} + static void batadv_send_outstanding_bcast_packet(struct work_struct *work) { struct batadv_hard_iface *hard_iface; @@ -837,7 +881,7 @@ static void batadv_send_outstanding_bcast_packet(struct work_struct *work) if (hard_iface->soft_iface != soft_iface) continue;
- if (forw_packet->num_packets >= hard_iface->num_bcasts) + if (!batadv_forw_packet_bcasts_left(forw_packet, hard_iface)) continue;
if (forw_packet->own) { @@ -896,10 +940,10 @@ static void batadv_send_outstanding_bcast_packet(struct work_struct *work) } rcu_read_unlock();
- forw_packet->num_packets++; + batadv_forw_packet_bcasts_inc(forw_packet);
/* if we still have some more bcasts to send */ - if (forw_packet->num_packets < BATADV_NUM_BCASTS_MAX) { + if (batadv_forw_packet_bcasts_left(forw_packet, NULL)) { batadv_forw_packet_bcast_queue(bat_priv, forw_packet, send_time); return; diff --git a/net/batman-adv/send.h b/net/batman-adv/send.h index 8e75890..a16b34f 100644 --- a/net/batman-adv/send.h +++ b/net/batman-adv/send.h @@ -40,6 +40,7 @@ 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);
int batadv_send_skb_to_orig(struct sk_buff *skb, struct batadv_orig_node *orig_node, diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index 575fdf1..128285f 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -1444,9 +1444,11 @@ struct batadv_nc_packet { * relevant to batman-adv in the skb->cb buffer in skbs. * @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 */ struct batadv_skb_cb { bool decoded; + int num_bcasts; };
/** @@ -1459,7 +1461,7 @@ struct batadv_skb_cb { * @skb: bcast packet's skb buffer * @packet_len: size of aggregated OGM packet inside the skb buffer * @direct_link_flags: direct link flags for aggregated OGM packets - * @num_packets: counter for bcast packet retransmission + * @num_packets: counter for aggregated OGMv1 packets * @delayed_work: work queue callback item for packet sending * @if_incoming: pointer to incoming hard-iface or primary iface if * locally generated packet
On Sonntag, 29. Januar 2017 14:57:42 CET Linus Lüssing wrote:
This patch refactors the num_packets counter of a forw_packet in the following three ways:
- Removed dual-use of forw_packet::num_packets: -> now for aggregation purposes only
- Using forw_packet::skb::cb::num_bcasts instead: -> for easier access in aggregation code later
- make access to num_bcasts private to batadv_forw_packet_*()
I like this change but have some questions (but no time to check it). Is cb initialized when a new skbuff is allocated? And what about the initialization for packets we get via batadv_interface_tx? At least batman-adv is only setting it to zero when we receive a new packet from an hardif (batadv_batman_skb_recv).
Kind regards, Sven
On Mon, Feb 06, 2017 at 11:25:00AM +0100, Sven Eckelmann wrote:
I like this change but have some questions (but no time to check it). Is cb initialized when a new skbuff is allocated?
Hm, good point. It looks like skb->cb is set to zero for newly allocated skbs via a memset down to skb->tail. However, I could not find anything resetting it to zero in net/core/dev.c, so there could potentially be an issue.
It seems like network-coding.c might have the same issue already for skb::cb::decoded.
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 | 58 +++++++++++++++++++++++++++++++++---- net/batman-adv/send.h | 8 ++++- net/batman-adv/types.h | 3 ++ 5 files changed, 120 insertions(+), 21 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 896a387..5f61b6b 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 08b61e0..cec6ae8 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,6 +804,29 @@ err: }
/** + * 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 + * 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. + */ +bool batadv_send_bcasts_left(struct sk_buff *skb, + struct batadv_hard_iface *hard_iface) +{ + int max = hard_iface ? hard_iface->num_bcasts : BATADV_NUM_BCASTS_MAX; + bool resend = !hard_iface || BATADV_SKB_CB(skb)->resend; + + 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 @@ -816,9 +844,16 @@ static bool batadv_forw_packet_bcasts_left(struct batadv_forw_packet *forw_packet, struct batadv_hard_iface *hard_iface) { - int max = hard_iface ? hard_iface->num_bcasts : BATADV_NUM_BCASTS_MAX; + return batadv_send_bcasts_left(forw_packet->skb, hard_iface); +}
- return BATADV_SKB_CB(forw_packet->skb)->num_bcasts < max; +/** + * 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++; }
/** @@ -828,7 +863,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; }
/** @@ -839,7 +885,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 128285f..110d355 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -1445,10 +1445,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; int num_bcasts; + bool resend; };
/**
b.a.t.m.a.n@lists.open-mesh.org