b.a.t.m.a.n.lists.open-mesh.org archive mirror
 help / color / mirror / Atom feed
From: Martin Weinelt <martin@darmstadt.freifunk.net>
To: b.a.t.m.a.n@lists.open-mesh.org
Subject: Re: [B.A.T.M.A.N.] [PATCH] batman-adv: Add multicast-to-unicast support for multiple targets
Date: Sat, 19 Jan 2019 14:49:55 +0100	[thread overview]
Message-ID: <9c1313ed-d5d5-1f9c-5443-0a7a009ba550@darmstadt.freifunk.net> (raw)
In-Reply-To: <20190119061422.32335-1-linus.luessing@c0d3.blue>


[-- Attachment #1.1: Type: text/plain, Size: 19980 bytes --]

Tested-by: Martin Weinelt <martin@darmstadt.freifunk.net>

We've been applying this patch to our testing firmware since april 2018
and have only initially come across issues which were promptly resolved
in a v2.

Best regards,

Martin

On 19.01.19 07:14, Linus Lüssing wrote:
> With this patch multicast packets with a limited number of destinations
> (current default: 16) will be split and transmitted by the originator as
> individual unicast transmissions.
> 
> Wifi broadcasts with their low bitrate are still a costly undertaking.
> In a mesh network this cost multiplies with the overall size of the mesh
> network. Therefore using multiple unicast transmissions instead of
> broadcast flooding is almost always less burdensome for the mesh
> network.
> 
> The maximum amount of unicast packets can be configured via the newly
> introduced multicast_fanout parameter. If this limit is exceeded
> distribution will fall back to classic broadcast flooding.
> 
> The multicast-to-unicast conversion is performed on the initial
> multicast sender node and counts on a final destination node, mesh-wide
> basis (and not next hop, neighbor node basis).
> 
> Signed-off-by: Linus Lüssing <linus.luessing@c0d3.blue>
> 
> ---
> 
> This patch introduces a new sysfs parameter even though upstream has
> signalized that we should switch to / add netlink support for
> configuration.
> 
> If upstream were to complain about yet another sysfs-only parameter, I'd
> argue that netlink config support is advancing but needs a bit more
> discussion and maturing. And it has no advantage to halt progress in
> other directions for that.
> 
> Furthermore, there is a bit of urgency for this patch with the
> increasing size of batman-adv mesh networks and the multicast burden
> this creates.
> 
> Finally, this patch was tested (and as far as I know is still applied)
> in some Freifunk communities. Maybe someone can add a "Tested-by" for
> this patch from there.
> 
> For my and other people's tests see descriptions here:
> 
> https://github.com/freifunk-gluon/gluon/pull/1357
> 
> ---
>  Documentation/ABI/testing/sysfs-class-net-mesh |   9 +
>  net/batman-adv/multicast.c                     | 268 ++++++++++++++++++++++++-
>  net/batman-adv/multicast.h                     |  17 ++
>  net/batman-adv/soft-interface.c                |   8 +-
>  net/batman-adv/sysfs.c                         |   3 +
>  net/batman-adv/translation-table.c             |   6 +-
>  net/batman-adv/translation-table.h             |   4 +
>  net/batman-adv/types.h                         |   6 +
>  8 files changed, 315 insertions(+), 6 deletions(-)
> 
> diff --git a/Documentation/ABI/testing/sysfs-class-net-mesh b/Documentation/ABI/testing/sysfs-class-net-mesh
> index c2b956d4..18734a36 100644
> --- a/Documentation/ABI/testing/sysfs-class-net-mesh
> +++ b/Documentation/ABI/testing/sysfs-class-net-mesh
> @@ -76,6 +76,15 @@ Description:
>  		is used to classify clients as "isolated" by the
>  		Extended Isolation feature.
>  
> +What:           /sys/class/net/<mesh_iface>/mesh/multicast_fanout
> +Date:           Feb 2018
> +Contact:        Linus Lüssing <linus.luessing@c0d3.blue>
> +Description:
> +                Defines the maximum number of packet copies that may
> +		be generated for a multicast-to-unicast conversion.
> +		Once this limit is exceeded distribution will fall
> +		back to broadcast.
> +
>  What:           /sys/class/net/<mesh_iface>/mesh/multicast_mode
>  Date:           Feb 2014
>  Contact:        Linus Lüssing <linus.luessing@web.de>
> diff --git a/net/batman-adv/multicast.c b/net/batman-adv/multicast.c
> index 5de6a375..d85c2226 100644
> --- a/net/batman-adv/multicast.c
> +++ b/net/batman-adv/multicast.c
> @@ -66,6 +66,7 @@
>  #include "hash.h"
>  #include "log.h"
>  #include "netlink.h"
> +#include "send.h"
>  #include "soft-interface.h"
>  #include "translation-table.h"
>  #include "tvlv.h"
> @@ -992,6 +993,7 @@ batadv_mcast_forw_mode(struct batadv_priv *bat_priv, struct sk_buff *skb,
>  	int ret, tt_count, ip_count, unsnoop_count, total_count;
>  	bool is_unsnoopable = false;
>  	struct ethhdr *ethhdr;
> +	unsigned int mcast_fanout;
>  
>  	ret = batadv_mcast_forw_mode_check(bat_priv, skb, &is_unsnoopable);
>  	if (ret == -ENOMEM)
> @@ -1025,8 +1027,272 @@ batadv_mcast_forw_mode(struct batadv_priv *bat_priv, struct sk_buff *skb,
>  	case 0:
>  		return BATADV_FORW_NONE;
>  	default:
> -		return BATADV_FORW_ALL;
> +		mcast_fanout = atomic_read(&bat_priv->multicast_fanout);
> +
> +		if (!unsnoop_count && total_count <= mcast_fanout)
> +			return BATADV_FORW_SOME;
>  	}
> +
> +	return BATADV_FORW_ALL;
> +}
> +
> +/**
> + * batadv_mcast_forw_tt_send() - send a packet to multicast listeners
> + * @bat_priv: the bat priv with all the soft interface information
> + * @skb: the multicast packet to transmit
> + * @vid: the vlan identifier
> + * @limit: number of remaining, maximum transmissions
> + *
> + * Sends copies of a frame with multicast destination to any multicast
> + * listener registered in the translation table. A transmission is performed
> + * via a batman-adv unicast packet for each such destination node.
> + *
> + * Return: NET_XMIT_DROP if limit was reached or on memory allocation failure,
> + * NET_XMIT_SUCCESS otherwise.
> + */
> +static int
> +batadv_mcast_forw_tt_send(struct batadv_priv *bat_priv, struct sk_buff *skb,
> +			  unsigned short vid, unsigned int *limit)
> +{
> +	unsigned int limit_tmp = *limit;
> +	int ret = NET_XMIT_SUCCESS;
> +	struct sk_buff *newskb;
> +
> +	struct batadv_tt_orig_list_entry *orig_entry;
> +
> +	struct batadv_tt_global_entry *tt_global;
> +	const u8 *addr = eth_hdr(skb)->h_dest;
> +
> +	tt_global = batadv_tt_global_hash_find(bat_priv, addr, vid);
> +	if (!tt_global)
> +		return NET_XMIT_DROP;
> +
> +	rcu_read_lock();
> +	hlist_for_each_entry_rcu(orig_entry, &tt_global->orig_list, list) {
> +		if (!limit_tmp) {
> +			ret = NET_XMIT_DROP;
> +			break;
> +		}
> +
> +		newskb = skb_copy(skb, GFP_ATOMIC);
> +		if (!newskb) {
> +			ret = NET_XMIT_DROP;
> +			break;
> +		}
> +
> +		ret = batadv_send_skb_unicast(bat_priv, newskb,
> +					      BATADV_UNICAST, 0,
> +					      orig_entry->orig_node, vid);
> +
> +		if (ret != NET_XMIT_SUCCESS) {
> +			/* use kfree_skb() to signalize losses here, but keep
> +			 * trying other destinations
> +			 */
> +			kfree_skb(newskb);
> +			ret = NET_XMIT_SUCCESS;
> +		}
> +
> +		limit_tmp--;
> +	}
> +	rcu_read_unlock();
> +
> +	batadv_tt_global_entry_put(tt_global);
> +	*limit = limit_tmp;
> +
> +	return ret;
> +}
> +
> +/**
> + * batadv_mcast_forw_want_all_ipv4_send() - send to nodes with want-all-ipv4
> + * @bat_priv: the bat priv with all the soft interface information
> + * @skb: the multicast packet to transmit
> + * @vid: the vlan identifier
> + * @limit: number of remaining, maximum transmissions
> + *
> + * Sends copies of a frame with multicast destination to any node with a
> + * BATADV_MCAST_WANT_ALL_IPV4 flag set. A transmission is performed via a
> + * batman-adv unicast packet for each such destination node.
> + *
> + * Return: NET_XMIT_DROP if limit was reached or on memory allocation failure,
> + * NET_XMIT_SUCCESS otherwise.
> + */
> +static int
> +batadv_mcast_forw_want_all_ipv4_send(struct batadv_priv *bat_priv,
> +				     struct sk_buff *skb, unsigned short vid,
> +				     unsigned int *limit)
> +{
> +	struct batadv_orig_node *orig_node;
> +	unsigned int limit_tmp = *limit;
> +	int ret = NET_XMIT_SUCCESS;
> +	struct sk_buff *newskb;
> +
> +	rcu_read_lock();
> +	hlist_for_each_entry_rcu(orig_node,
> +				 &bat_priv->mcast.want_all_ipv4_list,
> +				 mcast_want_all_ipv4_node) {
> +		if (!limit_tmp) {
> +			ret = NET_XMIT_DROP;
> +			break;
> +		}
> +
> +		newskb = skb_copy(skb, GFP_ATOMIC);
> +		if (!newskb) {
> +			ret = NET_XMIT_DROP;
> +			break;
> +		}
> +
> +		ret = batadv_send_skb_unicast(bat_priv, newskb,
> +					      BATADV_UNICAST, 0,
> +					      orig_node, vid);
> +
> +		if (ret != NET_XMIT_SUCCESS) {
> +			/* use kfree_skb() to signalize losses here, but keep
> +			 * trying other destinations
> +			 */
> +			kfree_skb(newskb);
> +			ret = NET_XMIT_SUCCESS;
> +		}
> +
> +		limit_tmp--;
> +	}
> +	rcu_read_unlock();
> +
> +	*limit = limit_tmp;
> +	return ret;
> +}
> +
> +/**
> + * batadv_mcast_forw_want_all_ipv6_send() - send to nodes with want-all-ipv6
> + * @bat_priv: the bat priv with all the soft interface information
> + * @skb: The multicast packet to transmit
> + * @vid: the vlan identifier
> + * @limit: number of remaining, maximum transmissions
> + *
> + * Sends copies of a frame with multicast destination to any node with a
> + * BATADV_MCAST_WANT_ALL_IPV6 flag set. A transmission is performed via a
> + * batman-adv unicast packet for each such destination node.
> + *
> + * Return: NET_XMIT_DROP if limit was reached or on memory allocation failure,
> + * NET_XMIT_SUCCESS otherwise.
> + */
> +static int
> +batadv_mcast_forw_want_all_ipv6_send(struct batadv_priv *bat_priv,
> +				     struct sk_buff *skb, unsigned short vid,
> +				     unsigned int *limit)
> +{
> +	struct batadv_orig_node *orig_node;
> +	unsigned int limit_tmp = *limit;
> +	int ret = NET_XMIT_SUCCESS;
> +	struct sk_buff *newskb;
> +
> +	rcu_read_lock();
> +	hlist_for_each_entry_rcu(orig_node,
> +				 &bat_priv->mcast.want_all_ipv6_list,
> +				 mcast_want_all_ipv6_node) {
> +		if (!limit_tmp) {
> +			ret = NET_XMIT_DROP;
> +			break;
> +		}
> +
> +		newskb = skb_copy(skb, GFP_ATOMIC);
> +		if (!newskb) {
> +			ret = NET_XMIT_DROP;
> +			break;
> +		}
> +
> +		ret = batadv_send_skb_unicast(bat_priv, newskb,
> +					      BATADV_UNICAST, 0,
> +					      orig_node, vid);
> +
> +		if (ret != NET_XMIT_SUCCESS) {
> +			/* use kfree_skb() to signalize losses here, but keep
> +			 * trying other destinations
> +			 */
> +			kfree_skb(newskb);
> +			ret = NET_XMIT_SUCCESS;
> +		}
> +
> +		limit_tmp--;
> +	}
> +	rcu_read_unlock();
> +
> +	*limit = limit_tmp;
> +	return ret;
> +}
> +
> +/**
> + * batadv_mcast_forw_want_all_send() - send packet to nodes in a want-all list
> + * @bat_priv: the bat priv with all the soft interface information
> + * @skb: the multicast packet to transmit
> + * @vid: the vlan identifier
> + * @limit: number of remaining, maximum transmissions
> + *
> + * Sends copies of a frame with multicast destination to any node with a
> + * BATADV_MCAST_WANT_ALL_IPV4 or BATADV_MCAST_WANT_ALL_IPV6 flag set. A
> + * transmission is performed via a batman-adv unicast packet for each such
> + * destination node.
> + *
> + * Return: NET_XMIT_DROP if limit was reached, on memory allocation failure
> + * or if the protocol family is neither IPv4 nor IPv6. NET_XMIT_SUCCESS
> + * otherwise.
> + */
> +static int
> +batadv_mcast_forw_want_all_send(struct batadv_priv *bat_priv,
> +				struct sk_buff *skb, unsigned short vid,
> +				unsigned int *limit)
> +{
> +	switch (ntohs(eth_hdr(skb)->h_proto)) {
> +	case ETH_P_IP:
> +		return batadv_mcast_forw_want_all_ipv4_send(bat_priv, skb, vid,
> +							    limit);
> +	case ETH_P_IPV6:
> +		return batadv_mcast_forw_want_all_ipv6_send(bat_priv, skb, vid,
> +							    limit);
> +	default:
> +		/* we shouldn't be here... */
> +		return NET_XMIT_DROP;
> +	}
> +}
> +
> +/**
> + * batadv_mcast_forw_send() - send packet to any detected multicast recpient
> + * @bat_priv: the bat priv with all the soft interface information
> + * @skb: the multicast packet to transmit
> + * @vid: the vlan identifier
> + * @limit: number of remaining, maximum transmissions
> + *
> + * Sends copies of a frame with multicast destination to any node that signaled
> + * interest in it, that is either via the translation table or the according
> + * want-all flags. A transmission is performed via a batman-adv unicast packet
> + * for each such destination node.
> + *
> + * If NET_XMIT_DROP is returned then caller needs to free the provided skb.
> + * Otherwise it is consumed.
> + *
> + * Return: NET_XMIT_DROP if limit was reached, on memory allocation failure
> + * or if the protocol family is neither IPv4 nor IPv6. NET_XMIT_SUCCESS
> + * otherwise.
> + */
> +int batadv_mcast_forw_send(struct batadv_priv *bat_priv, struct sk_buff *skb,
> +			   unsigned short vid)
> +{
> +	/* The previous forw mode check will try to limit to the configured
> +	 * fanout. Here, we allow a little bit of flexibility in case some
> +	 * new listeners might have joined between these function calls.
> +	 */
> +	unsigned int limit = 2 * atomic_read(&bat_priv->multicast_fanout);
> +	int ret;
> +
> +	ret = batadv_mcast_forw_tt_send(bat_priv, skb, vid, &limit);
> +	if (ret != NET_XMIT_SUCCESS)
> +		return ret;
> +
> +	ret = batadv_mcast_forw_want_all_send(bat_priv, skb, vid, &limit);
> +	if (ret != NET_XMIT_SUCCESS)
> +		return ret;
> +
> +	consume_skb(skb);
> +	return ret;
>  }
>  
>  /**
> diff --git a/net/batman-adv/multicast.h b/net/batman-adv/multicast.h
> index 466013fe..24424e51 100644
> --- a/net/batman-adv/multicast.h
> +++ b/net/batman-adv/multicast.h
> @@ -36,6 +36,13 @@ enum batadv_forw_mode {
>  	BATADV_FORW_ALL,
>  
>  	/**
> +	 * @BATADV_FORW_SOME: forward the packet to some nodes (currently via
> +	 *  a multicast-to-unicast conversion and the BATMAN unicast routing
> +	 *  protocol)
> +	 */
> +	BATADV_FORW_SOME,
> +
> +	/**
>  	 * @BATADV_FORW_SINGLE: forward the packet to a single node (currently
>  	 *  via the BATMAN unicast routing protocol)
>  	 */
> @@ -51,6 +58,9 @@ enum batadv_forw_mode
>  batadv_mcast_forw_mode(struct batadv_priv *bat_priv, struct sk_buff *skb,
>  		       struct batadv_orig_node **mcast_single_orig);
>  
> +int batadv_mcast_forw_send(struct batadv_priv *bat_priv, struct sk_buff *skb,
> +			   unsigned short vid);
> +
>  void batadv_mcast_init(struct batadv_priv *bat_priv);
>  
>  int batadv_mcast_flags_seq_print_text(struct seq_file *seq, void *offset);
> @@ -73,6 +83,13 @@ batadv_mcast_forw_mode(struct batadv_priv *bat_priv, struct sk_buff *skb,
>  	return BATADV_FORW_ALL;
>  }
>  
> +static inline int
> +batadv_mcast_forw_send(struct batadv_priv *bat_priv, struct sk_buff *skb,
> +		       unsigned short vid)
> +{
> +	return NET_XMIT_DROP;
> +}
> +
>  static inline int batadv_mcast_init(struct batadv_priv *bat_priv)
>  {
>  	return 0;
> diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c
> index 5357dcae..9adb9ad1 100644
> --- a/net/batman-adv/soft-interface.c
> +++ b/net/batman-adv/soft-interface.c
> @@ -209,7 +209,7 @@ static netdev_tx_t batadv_interface_tx(struct sk_buff *skb,
>  	unsigned short vid;
>  	u32 seqno;
>  	int gw_mode;
> -	enum batadv_forw_mode forw_mode;
> +	enum batadv_forw_mode forw_mode = BATADV_FORW_SINGLE;
>  	struct batadv_orig_node *mcast_single_orig = NULL;
>  	int network_offset = ETH_HLEN;
>  
> @@ -308,7 +308,8 @@ static netdev_tx_t batadv_interface_tx(struct sk_buff *skb,
>  			if (forw_mode == BATADV_FORW_NONE)
>  				goto dropped;
>  
> -			if (forw_mode == BATADV_FORW_SINGLE)
> +			if (forw_mode == BATADV_FORW_SINGLE ||
> +			    forw_mode == BATADV_FORW_SOME)
>  				do_bcast = false;
>  		}
>  	}
> @@ -368,6 +369,8 @@ static netdev_tx_t batadv_interface_tx(struct sk_buff *skb,
>  			ret = batadv_send_skb_unicast(bat_priv, skb,
>  						      BATADV_UNICAST, 0,
>  						      mcast_single_orig, vid);
> +		} else if (forw_mode == BATADV_FORW_SOME) {
> +			ret = batadv_mcast_forw_send(bat_priv, skb, vid);
>  		} else {
>  			if (batadv_dat_snoop_outgoing_arp_request(bat_priv,
>  								  skb))
> @@ -809,6 +812,7 @@ static int batadv_softif_init_late(struct net_device *dev)
>  	bat_priv->mcast.querier_ipv6.shadowing = false;
>  	bat_priv->mcast.flags = BATADV_NO_FLAGS;
>  	atomic_set(&bat_priv->multicast_mode, 1);
> +	atomic_set(&bat_priv->multicast_fanout, 16);
>  	atomic_set(&bat_priv->mcast.num_want_all_unsnoopables, 0);
>  	atomic_set(&bat_priv->mcast.num_want_all_ipv4, 0);
>  	atomic_set(&bat_priv->mcast.num_want_all_ipv6, 0);
> diff --git a/net/batman-adv/sysfs.c b/net/batman-adv/sysfs.c
> index e1b81626..15c06b4c 100644
> --- a/net/batman-adv/sysfs.c
> +++ b/net/batman-adv/sysfs.c
> @@ -697,6 +697,8 @@ static BATADV_ATTR(gw_bandwidth, 0644, batadv_show_gw_bwidth,
>  		   batadv_store_gw_bwidth);
>  #ifdef CONFIG_BATMAN_ADV_MCAST
>  BATADV_ATTR_SIF_BOOL(multicast_mode, 0644, NULL);
> +BATADV_ATTR_SIF_UINT(multicast_fanout, multicast_fanout, 0644, 1, INT_MAX,
> +		     NULL);
>  #endif
>  #ifdef CONFIG_BATMAN_ADV_DEBUG
>  BATADV_ATTR_SIF_UINT(log_level, log_level, 0644, 0, BATADV_DBG_ALL, NULL);
> @@ -718,6 +720,7 @@ static struct batadv_attribute *batadv_mesh_attrs[] = {
>  #endif
>  #ifdef CONFIG_BATMAN_ADV_MCAST
>  	&batadv_attr_multicast_mode,
> +	&batadv_attr_multicast_fanout,
>  #endif
>  	&batadv_attr_fragmentation,
>  	&batadv_attr_routing_algo,
> diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c
> index f73d7913..8f83554b 100644
> --- a/net/batman-adv/translation-table.c
> +++ b/net/batman-adv/translation-table.c
> @@ -61,6 +61,7 @@
>  #include "log.h"
>  #include "netlink.h"
>  #include "originator.h"
> +#include "send.h"
>  #include "soft-interface.h"
>  #include "tvlv.h"
>  
> @@ -205,7 +206,7 @@ batadv_tt_local_hash_find(struct batadv_priv *bat_priv, const u8 *addr,
>   * Return: a pointer to the corresponding tt_global_entry struct if the client
>   * is found, NULL otherwise.
>   */
> -static struct batadv_tt_global_entry *
> +struct batadv_tt_global_entry *
>  batadv_tt_global_hash_find(struct batadv_priv *bat_priv, const u8 *addr,
>  			   unsigned short vid)
>  {
> @@ -300,8 +301,7 @@ static void batadv_tt_global_entry_release(struct kref *ref)
>   *  possibly release it
>   * @tt_global_entry: tt_global_entry to be free'd
>   */
> -static void
> -batadv_tt_global_entry_put(struct batadv_tt_global_entry *tt_global_entry)
> +void batadv_tt_global_entry_put(struct batadv_tt_global_entry *tt_global_entry)
>  {
>  	kref_put(&tt_global_entry->common.refcount,
>  		 batadv_tt_global_entry_release);
> diff --git a/net/batman-adv/translation-table.h b/net/batman-adv/translation-table.h
> index 61bca75e..5b41f217 100644
> --- a/net/batman-adv/translation-table.h
> +++ b/net/batman-adv/translation-table.h
> @@ -41,6 +41,10 @@ int batadv_tt_global_dump(struct sk_buff *msg, struct netlink_callback *cb);
>  void batadv_tt_global_del_orig(struct batadv_priv *bat_priv,
>  			       struct batadv_orig_node *orig_node,
>  			       s32 match_vid, const char *message);
> +struct batadv_tt_global_entry *
> +batadv_tt_global_hash_find(struct batadv_priv *bat_priv, const u8 *addr,
> +			   unsigned short vid);
> +void batadv_tt_global_entry_put(struct batadv_tt_global_entry *tt_global_entry);
>  int batadv_tt_global_hash_count(struct batadv_priv *bat_priv,
>  				const u8 *addr, unsigned short vid);
>  struct batadv_orig_node *batadv_transtable_search(struct batadv_priv *bat_priv,
> diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
> index a21b34ed..d22cf01e 100644
> --- a/net/batman-adv/types.h
> +++ b/net/batman-adv/types.h
> @@ -1565,6 +1565,12 @@ struct batadv_priv {
>  	 *  node's sender/originating side
>  	 */
>  	atomic_t multicast_mode;
> +
> +	/**
> +	 * @multicast_fanout: Maximum number of packet copies to generate for a
> +	 *  multicast-to-unicast conversion
> +	 */
> +	atomic_t multicast_fanout;
>  #endif
>  
>  	/** @orig_interval: OGM broadcast interval in milliseconds */
> 



[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

  parent reply	other threads:[~2019-01-19 13:49 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-01-19  6:14 [B.A.T.M.A.N.] [PATCH] batman-adv: Add multicast-to-unicast support for multiple targets Linus Lüssing
2019-01-19  9:07 ` Sven Eckelmann
2019-01-19 13:49 ` Martin Weinelt [this message]
2019-01-29 15:42 ` Sven Eckelmann

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=9c1313ed-d5d5-1f9c-5443-0a7a009ba550@darmstadt.freifunk.net \
    --to=martin@darmstadt.freifunk.net \
    --cc=b.a.t.m.a.n@lists.open-mesh.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).