Hello list,
this is the first _RFC_ introducing a new routing protocol in the batman-adv kernel module, namely B.A.T.M.A.N. V.
Basically this is the result of three major ideas: 1) separate link cost estimation and routing information spreading (B.A.T.M.A.N. IV performs the two operations by using OGMs only) 2) introduce a throughput based metric (B.A.T.M.A.N. IV metric is mainly based on packet loss) 3) create a new protocol that groups all the knowledge gained so far
For what concerns point 1) in this patchset we introduce the Echo Locating Protocol (aka ELP), which is a standalone component aimed to only estimate link costs. ELP was originally written by Linus Lüssing.
For point 2) we have already proposed an extension of the cfg80211 module (not yet merged) which allows any kernel module to retrieve the expected throughput towards another peer over a wireless link. ELP is the component in charge of interacting with cfg80211 to retrieve the expected throughput. For wired connections ELP directly reads the bandwidth advertised by the Ethernet card (not optimal in case of complex topologies - but it is a first attempt).
Point 3) gets concrete under the new OGMv2 protocol, which is the core of B.A.T.M.A.N. V.
The documentation is not yet ready to be linked, because the draft we have on the wiki needs to be adapted to reflect the last changes (in particular about the metric change from TQ to expected throughput).
In the various patches some kerneldoc is still missing: I am working on that now. But I wanted to throw the patchset out as soon as possible to let anybody review this code (this is why this is an RFC).
Notes: ======= 1) yes,_this_patchset_is_not_yet_checkpatch_clean_ 2) patch 1/23 is already pending on the ml as RFC but I am resending it here since it is part of the whole batman v picture.
Cheers,
Antonio Quartulli (18): batman-adv: invoke ogm_schedule() only when the interface is activated batman-adv: OGMv2 - add basic infrastructure batman-adv: OGMv2 - implement originators logic batman-adv: OGMv2 - purge obsolete potential routers batman-adv: split name from variable for uint mesh attributes batman-adv: add throughput attribute to hard_ifaces batman-adv: add base throughput attribute batman-adv: add last_unicast_tx to struct neigh_node_elp batman-adv: ELP - compute the metric based on the estimated throughput batman-adv: ELP - send unicast ELP packets for throughput sampling batman-adv: ELP - read estimated throughput from cfg80211 batman-adv: ELP - implement dead neigh node detection batman-adv: ELP - use phydev to determine link characteristics batman-adv: add bat_neigh_free() API batman-adv: B.A.T.M.A.N. V - implement bat_neigh_free() API batman-adv: B.A.T.M.A.N. V - implement neigh_is_equiv_or_better API batman-adv: B.A.T.M.A.N. V - implement bat_neigh_cmp API batman-adv: B.A.T.M.A.N. V - implement bat_orig_print API
Linus Luessing (5): batman-adv: Add hard_iface specific sysfs wrapper macros for UINT batman-adv: ELP - adding basic infrastructure batman-adv: ELP - creating neighbor structures batman-adv: ELP - exporting neighbor list via debugfs batman-adv: ELP - adding sysfs parameter for elp interval
Makefile | 2 + Makefile.kbuild | 3 + README.external | 1 + bat_algo.h | 26 +- bat_iv_ogm.c | 2 +- bat_v.c | 266 +++++++++++++++++ bat_v_elp.c | 718 +++++++++++++++++++++++++++++++++++++++++++++ bat_v_elp.h | 39 +++ bat_v_ogm.c | 716 ++++++++++++++++++++++++++++++++++++++++++++ bat_v_ogm.h | 32 ++ compat.h | 20 ++ debugfs.c | 17 ++ distributed-arp-table.c | 4 +- fragmentation.c | 8 +- gen-compat-autoconf.sh | 1 + hard-interface.c | 24 +- icmp_socket.c | 2 +- main.c | 32 +- main.h | 16 + network-coding.c | 20 +- originator.c | 69 +++-- originator.h | 2 + packet.h | 50 ++++ send.c | 46 ++- send.h | 10 +- sysfs-class-net-batman-adv | 8 +- sysfs.c | 87 +++++- types.h | 113 +++++++ 28 files changed, 2260 insertions(+), 74 deletions(-) create mode 100644 bat_v.c create mode 100644 bat_v_elp.c create mode 100644 bat_v_elp.h create mode 100644 bat_v_ogm.c create mode 100644 bat_v_ogm.h
From: Antonio Quartulli antonio@open-mesh.com
In the current implementation the first OGM is scheduled when a new hard-interface is enabled, no matter if it is active (thus ready for sending packets) or not.
However, the B.A.T.M.A.N. IV OGM scheduling mechanism works in a way that reschedules the task forever even if the interface is not yet ready and therefore everything works as expected.
Unfortunately this behaviour is not fine in general and other algorithms may pretend the interface to be active before scheduling the first OGM.
Change this behaviour by scheduling the first OGM upon interface activation.
Signed-off-by: Antonio Quartulli antonio@open-mesh.com --- hard-interface.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/hard-interface.c b/hard-interface.c index b851cc5..2a04130 100644 --- a/hard-interface.c +++ b/hard-interface.c @@ -320,6 +320,8 @@ batadv_hardif_activate_interface(struct batadv_hard_iface *hard_iface)
batadv_update_min_mtu(hard_iface->soft_iface);
+ /* begin scheduling originator messages on that interface */ + batadv_schedule_bat_ogm(hard_iface); out: if (primary_if) batadv_hardif_free_ref(primary_if); @@ -459,9 +461,6 @@ int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface, "Not using interface %s (retrying later): interface not active\n", hard_iface->net_dev->name);
- /* begin scheduling originator messages on that interface */ - batadv_schedule_bat_ogm(hard_iface); - out: return 0;
From: Linus Luessing linus.luessing@web.de
This allows us to easily add a sysfs parameter for an unsigned int later, which is not for a batman mesh interface (e.g. bat0), but for a common interface instead. It allows reading and writing an atomic_t in hard_iface (instead of bat_priv compared to the mesh variant).
Developed by Linus during a 6 months trainee study period in Ascom (Switzerland) AG.
Signed-off-by: Linus Luessing linus.luessing@web.de Signed-off-by: Marek Lindner lindner_marek@yahoo.de [antonio@open-mesh.com: rename functions and move macros] Signed-off-by: Antonio Quartulli antonio@open-mesh.com --- sysfs.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+)
diff --git a/sysfs.c b/sysfs.c index e456bf6..1e9ee3a 100644 --- a/sysfs.c +++ b/sysfs.c @@ -213,6 +213,55 @@ ssize_t batadv_show_vlan_##_name(struct kobject *kobj, \ static BATADV_ATTR_VLAN(_name, _mode, batadv_show_vlan_##_name, \ batadv_store_vlan_##_name)
+#define BATADV_ATTR_HIF_STORE_UINT(_name, _var, _min, _max, _post_func) \ +ssize_t batadv_store_##_name(struct kobject *kobj, \ + struct attribute *attr, char *buff, \ + size_t count) \ +{ \ + struct net_device *net_dev = batadv_kobj_to_netdev(kobj); \ + struct batadv_hard_iface *hard_iface; \ + ssize_t length; \ + \ + hard_iface = batadv_hardif_get_by_netdev(net_dev); \ + if (!hard_iface) \ + return 0; \ + \ + length = __batadv_store_uint_attr(buff, count, _min, _max, \ + _post_func, attr, \ + &hard_iface->_var, net_dev); \ + \ + batadv_hardif_free_ref(hard_iface); \ + return length; \ +} + +#define BATADV_ATTR_HIF_SHOW_UINT(_name, _var) \ +ssize_t batadv_show_##_name(struct kobject *kobj, \ + struct attribute *attr, char *buff) \ +{ \ + struct net_device *net_dev = batadv_kobj_to_netdev(kobj); \ + struct batadv_hard_iface *hard_iface; \ + ssize_t length; \ + \ + hard_iface = batadv_hardif_get_by_netdev(net_dev); \ + if (!hard_iface) \ + return 0; \ + \ + length = sprintf(buff, "%i\n", atomic_read(&hard_iface->_var)); \ + \ + batadv_hardif_free_ref(hard_iface); \ + return length; \ +} + +/* Use this, if you are going to set [name] in hard_iface to an + * unsigned integer value + */ +#define BATADV_ATTR_HIF_UINT(_name, _var, _mode, _min, _max, _post_func)\ + static BATADV_ATTR_HIF_STORE_UINT(_name, _var, _min, \ + _max, _post_func) \ + static BATADV_ATTR_HIF_SHOW_UINT(_name, _var) \ + static BATADV_ATTR(_name, _mode, batadv_show_##_name, \ + batadv_store_##_name) + static int batadv_store_bool_attr(char *buff, size_t count, struct net_device *net_dev, const char *attr_name, atomic_t *attr)
From: Linus Luessing linus.luessing@web.de
The B.A.T.M.A.N. protocol originally only used a single message type (called OGM) to determine the link qualities to the direct neighbors and spreading these link quality information through the whole mesh. This procedure is summarized on the BATMAN concept page and explained in details in the RFC draft published in 2008.
This approach was chosen for its simplicity during the protocol design phase and the implementation. However, it also bears some drawbacks:
* Wireless interfaces usually come with some packet loss, therefore a higher broadcast rate is desirable to allow a fast reaction on flaky connections. Other interfaces of the same host might be connected to Ethernet LANs / VPNs / etc which rarely exhibit packet loss would benefit from a lower broadcast rate to reduce overhead. * It generally is more desirable to detect local link quality changes at a faster rate than propagating all these changes through the entire mesh (the far end of the mesh does not need to care about local link quality changes that much). Other optimizations strategies, like reducing overhead, might be possible if OGMs weren't used for all tasks in the mesh at the same time.
As a result detecting local link qualities shall be handled by an independent message type, ELP, whereas the OGM message type remains responsible for flooding the mesh with these link quality information and determining the overall path transmit qualities.
Developed by Linus during a 6 months trainee study period in Ascom (Switzerland) AG.
Signed-off-by: Linus Luessing linus.luessing@web.de Signed-off-by: Marek Lindner lindner_marek@yahoo.de Signed-off-by: Antonio Quartulli antonio@open-mesh.com --- Makefile | 2 + Makefile.kbuild | 2 + README.external | 1 + bat_algo.h | 15 ++++- bat_v.c | 65 ++++++++++++++++++ bat_v_elp.c | 179 +++++++++++++++++++++++++++++++++++++++++++++++++ bat_v_elp.h | 30 +++++++++ gen-compat-autoconf.sh | 1 + main.c | 1 + packet.h | 25 +++++++ types.h | 18 +++++ 11 files changed, 338 insertions(+), 1 deletion(-) create mode 100644 bat_v.c create mode 100644 bat_v_elp.c create mode 100644 bat_v_elp.h
diff --git a/Makefile b/Makefile index 9a7cbae..405175d 100644 --- a/Makefile +++ b/Makefile @@ -25,6 +25,8 @@ export CONFIG_BATMAN_ADV_BLA=y export CONFIG_BATMAN_ADV_DAT=y # B.A.T.M.A.N network coding (catwoman): export CONFIG_BATMAN_ADV_NC=n +# B.A.T.M.A.N. V routing algorithm (experimental): +# export CONFIG_BATMAN_ADV_BATMAN_V=y
PWD:=$(shell pwd) KERNELPATH ?= /lib/modules/$(shell uname -r)/build diff --git a/Makefile.kbuild b/Makefile.kbuild index 42df18f..ae9a6ca 100644 --- a/Makefile.kbuild +++ b/Makefile.kbuild @@ -18,6 +18,8 @@
obj-$(CONFIG_BATMAN_ADV) += batman-adv.o batman-adv-y += bat_iv_ogm.o +batman-adv-$(CONFIG_BATMAN_ADV_BATMAN_V) += bat_v.o +batman-adv-$(CONFIG_BATMAN_ADV_BATMAN_V) += bat_v_elp.o batman-adv-y += bitarray.o batman-adv-$(CONFIG_BATMAN_ADV_BLA) += bridge_loop_avoidance.o batman-adv-y += debugfs.o diff --git a/README.external b/README.external index c8eac1a..6f0d18b 100644 --- a/README.external +++ b/README.external @@ -39,6 +39,7 @@ module). Available options and their possible values are * CONFIG_BATMAN_ADV_BLA=[y*|n] (B.A.T.M.A.N. bridge loop avoidance) * CONFIG_BATMAN_ADV_DAT=[y*|n] (B.A.T.M.A.N. Distributed ARP Table) * CONFIG_BATMAN_ADV_NC=[y|n*] (B.A.T.M.A.N. Network Coding) + * CONFIG_BATMAN_ADV_BATMAN_V=[y|n*] (B.A.T.M.A.N. V routing algorithm)
e.g., debugging can be enabled by
diff --git a/bat_algo.h b/bat_algo.h index 4e49666..7289336 100644 --- a/bat_algo.h +++ b/bat_algo.h @@ -1,6 +1,6 @@ /* Copyright (C) 2011-2014 B.A.T.M.A.N. contributors: * - * Marek Lindner + * Marek Lindner, Antonio Quartulli * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public @@ -20,4 +20,17 @@
int batadv_iv_init(void);
+#ifdef CONFIG_BATMAN_ADV_BATMAN_V + +int batadv_v_init(void); + +#else + +static inline int batadv_v_init(void) +{ + return 0; +} + +#endif /* CONFIG_BATMAN_ADV_BATMAN_V */ + #endif /* _NET_BATMAN_ADV_BAT_ALGO_H_ */ diff --git a/bat_v.c b/bat_v.c new file mode 100644 index 0000000..7247d7f --- /dev/null +++ b/bat_v.c @@ -0,0 +1,65 @@ +/* Copyright (C) 2013-2014 B.A.T.M.A.N. contributors: + * + * Antonio Quartulli + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include "main.h" +#include "bat_algo.h" +#include "bat_v_elp.h" + +static int batadv_v_iface_enable(struct batadv_hard_iface *hard_iface) +{ + return batadv_v_elp_iface_enable(hard_iface); +} + +static void batadv_v_iface_disable(struct batadv_hard_iface *hard_iface) +{ + batadv_v_elp_iface_disable(hard_iface); +} + +static void batadv_v_iface_update_mac(struct batadv_hard_iface *hard_iface) +{ +} + +static void batadv_v_primary_iface_set(struct batadv_hard_iface *hard_iface) +{ + batadv_v_elp_primary_iface_set(hard_iface); +} + +static void batadv_v_ogm_schedule(struct batadv_hard_iface *hard_iface) +{ +} + +static void batadv_v_ogm_emit(struct batadv_forw_packet *forw_packet) +{ +} + +static struct batadv_algo_ops batadv_batman_v __read_mostly = { + .name = "BATMAN_V", + .bat_iface_enable = batadv_v_iface_enable, + .bat_iface_disable = batadv_v_iface_disable, + .bat_iface_update_mac = batadv_v_iface_update_mac, + .bat_primary_iface_set = batadv_v_primary_iface_set, + .bat_ogm_emit = batadv_v_ogm_emit, + .bat_ogm_schedule = batadv_v_ogm_schedule, +}; + + +int __init batadv_v_init(void) +{ + return batadv_algo_register(&batadv_batman_v); +} diff --git a/bat_v_elp.c b/bat_v_elp.c new file mode 100644 index 0000000..45b082b --- /dev/null +++ b/bat_v_elp.c @@ -0,0 +1,179 @@ +/* Copyright (C) 2011-2014 B.A.T.M.A.N. contributors: + * + * Linus Lüssing, Marek Lindner, Antonio Quartulli + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + +#include "main.h" +#include "hard-interface.h" +#include "send.h" +#include "bat_algo.h" +#include "bat_v_elp.h" + +/** + * batadv_v_elp_start_timer - restart timer for ELP periodic work + * @hard_iface: the interface for which the timer has to be reset + */ +static void batadv_v_elp_start_timer(struct batadv_hard_iface *hard_iface) +{ + unsigned int msecs; + + msecs = atomic_read(&hard_iface->bat_v.elp_interval) - BATADV_JITTER; + msecs += prandom_u32() % (2 * BATADV_JITTER); + + queue_delayed_work(batadv_event_workqueue, &hard_iface->bat_v.elp_wq, + msecs_to_jiffies(msecs)); +} + +/** + * batadv_v_elp_send_outstanding - ELP periodic broadcast sending + * @work: work queue item + * + * Sends a broadcast ELP message over the interface that this work item belongs + * to. + */ +static void batadv_v_elp_send_outstanding(struct work_struct *work) +{ + struct batadv_hard_iface *hard_iface; + struct batadv_hard_iface_bat_v *bat_v; + struct batadv_priv *bat_priv; + struct batadv_elp_packet *elp_packet; + uint32_t elp_interval; + struct sk_buff *skb; + + bat_v = container_of(work, struct batadv_hard_iface_bat_v, elp_wq.work); + hard_iface = container_of(bat_v, struct batadv_hard_iface, bat_v); + bat_priv = netdev_priv(hard_iface->soft_iface); + + if (atomic_read(&bat_priv->mesh_state) == BATADV_MESH_DEACTIVATING) + goto out; + + /* we are in the process of shutting this interface down */ + if ((hard_iface->if_status == BATADV_IF_NOT_IN_USE) || + (hard_iface->if_status == BATADV_IF_TO_BE_REMOVED)) + goto out; + + /* the interface was enabled but may not be ready yet */ + if (hard_iface->if_status != BATADV_IF_ACTIVE) + goto restart_timer; + + skb = skb_copy(hard_iface->bat_v.elp_skb, GFP_ATOMIC); + if (!skb) + goto out; + + elp_packet = (struct batadv_elp_packet *)skb->data; + elp_packet->seqno = htonl(atomic_read(&hard_iface->bat_v.elp_seqno)); + elp_packet->num_neighbors = 0; + elp_interval = atomic_read(&hard_iface->bat_v.elp_interval); + elp_packet->elp_interval = htonl(elp_interval); + + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, + "Sending ELP packet on interface %s, seqno %u\n", + hard_iface->net_dev->name, + atomic_read(&hard_iface->bat_v.elp_seqno)); + + batadv_send_skb_packet(skb, hard_iface, batadv_broadcast_addr); + + atomic_inc(&hard_iface->bat_v.elp_seqno); + +restart_timer: + batadv_v_elp_start_timer(hard_iface); +out: + return; +} + +/** + * batadv_v_elp_iface_enable - setup the ELP interface private resources + * @hard_iface: interface for which the data has to be prepared + * + * Returns 0 on success or a -ENOMEM in case of failure. + */ +int batadv_v_elp_iface_enable(struct batadv_hard_iface *hard_iface) +{ + struct batadv_elp_packet *elp_packet; + unsigned char *elp_buff; + uint32_t random_seqno; + size_t size; + int res = -ENOMEM; + + size = ETH_HLEN + NET_IP_ALIGN + BATADV_ELP_HLEN; + hard_iface->bat_v.elp_skb = dev_alloc_skb(size); + if (!hard_iface->bat_v.elp_skb) + goto out; + + skb_reserve(hard_iface->bat_v.elp_skb, ETH_HLEN + NET_IP_ALIGN); + elp_buff = skb_push(hard_iface->bat_v.elp_skb, BATADV_ELP_HLEN); + elp_packet = (struct batadv_elp_packet *)elp_buff; + memset(elp_packet, 0, BATADV_ELP_HLEN); + + elp_packet->packet_type = BATADV_ELP; + elp_packet->version = BATADV_COMPAT_VERSION; + elp_packet->ttl = 0; + + /* randomize initial seqno to avoid collision */ + get_random_bytes(&random_seqno, sizeof(random_seqno)); + atomic_set(&hard_iface->bat_v.elp_seqno, random_seqno); + atomic_set(&hard_iface->bat_v.elp_interval, 500); + + INIT_DELAYED_WORK(&hard_iface->bat_v.elp_wq, + batadv_v_elp_send_outstanding); + batadv_v_elp_start_timer(hard_iface); + res = 0; + +out: + return res; +} + +/** + * batadv_v_elp_iface_disable - release ELP interface private resources + * @hard_iface: interface for which the resources have to be released + */ +void batadv_v_elp_iface_disable(struct batadv_hard_iface *hard_iface) +{ + cancel_delayed_work_sync(&hard_iface->bat_v.elp_wq); + + dev_kfree_skb(hard_iface->bat_v.elp_skb); + hard_iface->bat_v.elp_skb = NULL; +} + +/** + * batadv_v_elp_primary_iface_set - change internal data to reflect the new + * primary interface + * @hard_iface: the new primary interface + */ +void batadv_v_elp_primary_iface_set(struct batadv_hard_iface *hard_iface) +{ + struct batadv_hard_iface *hard_iface_tmp; + struct batadv_elp_packet *elp_packet; + struct sk_buff *skb; + + /* update orig field of every elp iface belonging to this mesh */ + rcu_read_lock(); + list_for_each_entry_rcu(hard_iface_tmp, &batadv_hardif_list, list) { + if (hard_iface->soft_iface != hard_iface_tmp->soft_iface) + continue; + + if (!hard_iface_tmp->bat_v.elp_skb) + continue; + + skb = hard_iface_tmp->bat_v.elp_skb; + elp_packet = (struct batadv_elp_packet *)skb->data; + memcpy(elp_packet->orig, + hard_iface->net_dev->dev_addr, ETH_ALEN); + } + rcu_read_unlock(); +} diff --git a/bat_v_elp.h b/bat_v_elp.h new file mode 100644 index 0000000..06d2cb1 --- /dev/null +++ b/bat_v_elp.h @@ -0,0 +1,30 @@ +/* Copyright (C) 2013-2014 B.A.T.M.A.N. contributors: + * + * Antonio Quartulli + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + +#include "main.h" + +#ifndef _NET_BATMAN_ADV_BAT_V_ELP_H_ +#define _NET_BATMAN_ADV_BAT_V_ELP_H_ + +int batadv_v_elp_iface_enable(struct batadv_hard_iface *hard_iface); +void batadv_v_elp_iface_disable(struct batadv_hard_iface *hard_iface); +void batadv_v_elp_primary_iface_set(struct batadv_hard_iface *hard_iface); + +#endif /* _NET_BATMAN_ADV_BAT_V_ELP_H_ */ diff --git a/gen-compat-autoconf.sh b/gen-compat-autoconf.sh index 78573e4..001707f 100755 --- a/gen-compat-autoconf.sh +++ b/gen-compat-autoconf.sh @@ -40,6 +40,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_NC' ${CONFIG_BATMAN_ADV_NC:="n"} >> "${TMP}" +gen_config 'CONFIG_BATMAN_ADV_BATMAN_V' ${CONFIG_BATMAN_ADV_BATMAN_V:="n"} >> "${TMP}"
# only regenerate compat-autoconf.h when config was changed diff "${TMP}" "${TARGET}" > /dev/null 2>&1 || cp "${TMP}" "${TARGET}" diff --git a/main.c b/main.c index fbeaebd..b7d9996 100644 --- a/main.c +++ b/main.c @@ -63,6 +63,7 @@ static int __init batadv_init(void)
batadv_recv_handler_init();
+ batadv_v_init(); batadv_iv_init(); batadv_nc_init();
diff --git a/packet.h b/packet.h index 0a381d1..71b1aeb 100644 --- a/packet.h +++ b/packet.h @@ -37,6 +37,7 @@ enum batadv_packettype { BATADV_IV_OGM = 0x00, BATADV_BCAST = 0x01, BATADV_CODED = 0x02, + BATADV_ELP = 0x03, /* 0x40 - 0x7f: unicast */ #define BATADV_UNICAST_MIN 0x40 BATADV_UNICAST = 0x40, @@ -192,6 +193,30 @@ struct batadv_ogm_packet { #define BATADV_OGM_HLEN sizeof(struct batadv_ogm_packet)
/** + * struct batadv_elp_packet - elp (neighbor discovery) packet + * @packet_type: batman-adv packet type, part of the general header + * @version: batman-adv protocol version, part of the genereal header + * @ttl: time to live for this packet, part of the genereal header + * @num_neighbors: number of already discovered neighbors + * @seqno: sequence number + * @elp_interval: currently used ELP sending interval + * @orig: originator mac address + * @align: not used - useful for alignment purposes only + */ +struct batadv_elp_packet { + uint8_t packet_type; + uint8_t version; + uint8_t ttl; + uint8_t num_neighbors; + __be32 seqno; + __be32 elp_interval; + uint8_t orig[ETH_ALEN]; + uint8_t align[2]; +}; + +#define BATADV_ELP_HLEN sizeof(struct batadv_elp_packet) + +/** * batadv_icmp_header - common members among all the ICMP packets * @packet_type: batman-adv packet type, part of the general header * @version: batman-adv protocol version, part of the genereal header diff --git a/types.h b/types.h index 9f52517..518fa81 100644 --- a/types.h +++ b/types.h @@ -70,6 +70,20 @@ struct batadv_hard_iface_bat_iv { };
/** + * struct batadv_hard_iface_bat_v - per hard interface B.A.T.M.A.N. V data + * @elp_interval: time interval between two ELP transmissions + * @elp_seqno: current ELP sequence number + * @elp_skb: base skb containing the ELP message to send + * @elp_wq: workqueue used to schedule ELP transmissions + */ +struct batadv_hard_iface_bat_v { + atomic_t elp_interval; + atomic_t elp_seqno; + struct sk_buff *elp_skb; + struct delayed_work elp_wq; +}; + +/** * struct batadv_hard_iface - network device known to batman-adv * @list: list node for batadv_hardif_list * @if_num: identificator of the interface @@ -83,6 +97,7 @@ struct batadv_hard_iface_bat_iv { * @soft_iface: the batman-adv interface which uses this network interface * @rcu: struct used for freeing in an RCU-safe manner * @bat_iv: BATMAN IV specific per hard interface data + * @bat_v: BATMAN V specific per hard interface data * @cleanup_work: work queue callback item for hard interface deinit * @debug_dir: dentry for nc subdir in batman-adv directory in debugfs */ @@ -98,6 +113,9 @@ struct batadv_hard_iface { struct net_device *soft_iface; struct rcu_head rcu; struct batadv_hard_iface_bat_iv bat_iv; +#ifdef CONFIG_BATMAN_ADV_BATMAN_V + struct batadv_hard_iface_bat_v bat_v; +#endif struct work_struct cleanup_work; struct dentry *debug_dir; };
From: Linus Luessing linus.luessing@web.de
Initially developed by Linus during a 6 months trainee study period in Ascom (Switzerland) AG.
Signed-off-by: Linus Luessing linus.luessing@web.de Signed-off-by: Marek Lindner lindner_marek@yahoo.de Signed-off-by: Antonio Quartulli antonio@open-mesh.com --- bat_v.c | 18 +++++- bat_v_elp.c | 204 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- bat_v_elp.h | 6 ++ main.h | 2 + types.h | 53 ++++++++++++++++ 5 files changed, 281 insertions(+), 2 deletions(-)
diff --git a/bat_v.c b/bat_v.c index 7247d7f..bed5e00 100644 --- a/bat_v.c +++ b/bat_v.c @@ -61,5 +61,21 @@ static struct batadv_algo_ops batadv_batman_v __read_mostly = {
int __init batadv_v_init(void) { - return batadv_algo_register(&batadv_batman_v); + int ret; + + /* batman v echo location protocol packet */ + ret = batadv_recv_handler_register(BATADV_ELP, + batadv_v_elp_packet_recv); + if (ret < 0) + goto elp_unregister; + + ret = batadv_algo_register(&batadv_batman_v); + + return ret; + +elp_unregister: + if (ret < 0) + batadv_recv_handler_unregister(BATADV_ELP); + + return ret; } diff --git a/bat_v_elp.c b/bat_v_elp.c index 45b082b..ee4207d 100644 --- a/bat_v_elp.c +++ b/bat_v_elp.c @@ -23,6 +23,8 @@ #include "send.h" #include "bat_algo.h" #include "bat_v_elp.h" +#include "originator.h" +#include "routing.h"
/** * batadv_v_elp_start_timer - restart timer for ELP periodic work @@ -40,6 +42,111 @@ static void batadv_v_elp_start_timer(struct batadv_hard_iface *hard_iface) }
/** + * batadv_elp_neigh_node_free_ref - release a ELP neighbour node + * @neigh: the neighbour object to release + */ +void batadv_elp_neigh_node_free_ref(struct batadv_elp_neigh_node *neigh) +{ + if (atomic_dec_and_test(&neigh->refcount)) + kfree_rcu(neigh, rcu); +} + +/** + * batadv_v_elp_neigh_new - create a new ELP neighbour node + * @hard_iface: the interface the neighbour is connected to + * @neigh_addr: the neighbour interface address + * @orig_addr: the neighbour originator address + * @seqno: the received sequence number to be used to initialize the object + * + * Returns a new ELP neighbour node or NULL in case of failure. + */ +static struct batadv_elp_neigh_node * +batadv_v_elp_neigh_new(struct batadv_hard_iface *hard_iface, + uint8_t *neigh_addr, uint8_t *orig_addr, uint32_t seqno) +{ + struct batadv_elp_neigh_node *neigh; + + neigh = kzalloc(sizeof(*neigh), GFP_ATOMIC); + if (!neigh) + return NULL; + + memcpy(neigh->orig, orig_addr, ETH_ALEN); + memcpy(neigh->addr, neigh_addr, ETH_ALEN); + neigh->last_recv_seqno = seqno - 1; + neigh->last_seen = jiffies; + ewma_init(&neigh->metric, 1024, 8); + /* recount initialised to 2 to simplify the caller function */ + atomic_set(&neigh->refcount, 2); + + spin_lock_bh(&hard_iface->bat_v.neigh_list_lock); + hlist_add_head_rcu(&neigh->list, &hard_iface->bat_v.neigh_list); + spin_unlock_bh(&hard_iface->bat_v.neigh_list_lock); + atomic_inc(&hard_iface->bat_v.num_neighbors); + + return neigh; +} + +/** + * batadv_v_elp_neigh_get - retrieve an ELP neighbour node + * @hard_iface: the interface this neighbour is connected to + * @neigh_addr: the interface address of the neighbour to retrieve + * + * Returns the ELP neighbour node if found or NULL otherwise. + */ +struct batadv_elp_neigh_node * +batadv_v_elp_neigh_get(struct batadv_hard_iface *hard_iface, + uint8_t *neigh_addr) +{ + struct batadv_elp_neigh_node *neigh = NULL, *neigh_tmp; + + rcu_read_lock(); + hlist_for_each_entry_rcu(neigh_tmp, + &hard_iface->bat_v.neigh_list, list) { + if (!batadv_compare_eth(neigh_tmp->addr, neigh_addr)) + continue; + + if (!atomic_inc_not_zero(&neigh_tmp->refcount)) + continue; + + neigh = neigh_tmp; + break; + } + rcu_read_unlock(); + + return neigh; +} + +/** + * batadv_v_elp_neigh_purge - purge obsolete neighbour nodes + * + * Deletes the ELP neighbour nodes that did not send any ELP message for a + * pre-defined amount of time. + */ +static void batadv_v_elp_neigh_purge(struct batadv_hard_iface *hard_iface) +{ + unsigned long timeout; + struct batadv_elp_neigh_node *neigh; + struct hlist_node *node; + bool has_timed_out; + + spin_lock_bh(&hard_iface->bat_v.neigh_list_lock); + hlist_for_each_entry_safe(neigh, node, &hard_iface->bat_v.neigh_list, + list) { + timeout = neigh->elp_interval * BATADV_ELP_OUTDATED_MAX; + has_timed_out = batadv_has_timed_out(neigh->last_seen, timeout); + + if ((!has_timed_out) && + (hard_iface->if_status == BATADV_IF_ACTIVE)) + continue; + + hlist_del_rcu(&neigh->list); + atomic_dec(&hard_iface->bat_v.num_neighbors); + batadv_elp_neigh_node_free_ref(neigh); + } + spin_unlock_bh(&hard_iface->bat_v.neigh_list_lock); +} + +/** * batadv_v_elp_send_outstanding - ELP periodic broadcast sending * @work: work queue item * @@ -54,6 +161,7 @@ static void batadv_v_elp_send_outstanding(struct work_struct *work) struct batadv_elp_packet *elp_packet; uint32_t elp_interval; struct sk_buff *skb; + uint8_t num_neighs;
bat_v = container_of(work, struct batadv_hard_iface_bat_v, elp_wq.work); hard_iface = container_of(bat_v, struct batadv_hard_iface, bat_v); @@ -75,9 +183,15 @@ static void batadv_v_elp_send_outstanding(struct work_struct *work) if (!skb) goto out;
+ /* purge outdated entries first */ + batadv_v_elp_neigh_purge(hard_iface); + elp_packet = (struct batadv_elp_packet *)skb->data; elp_packet->seqno = htonl(atomic_read(&hard_iface->bat_v.elp_seqno)); - elp_packet->num_neighbors = 0; + + num_neighs = atomic_read(&hard_iface->bat_v.num_neighbors); + elp_packet->num_neighbors = num_neighs; + elp_interval = atomic_read(&hard_iface->bat_v.elp_interval); elp_packet->elp_interval = htonl(elp_interval);
@@ -110,6 +224,9 @@ int batadv_v_elp_iface_enable(struct batadv_hard_iface *hard_iface) size_t size; int res = -ENOMEM;
+ INIT_HLIST_HEAD(&hard_iface->bat_v.neigh_list); + spin_lock_init(&hard_iface->bat_v.neigh_list_lock); + size = ETH_HLEN + NET_IP_ALIGN + BATADV_ELP_HLEN; hard_iface->bat_v.elp_skb = dev_alloc_skb(size); if (!hard_iface->bat_v.elp_skb) @@ -148,6 +265,8 @@ void batadv_v_elp_iface_disable(struct batadv_hard_iface *hard_iface)
dev_kfree_skb(hard_iface->bat_v.elp_skb); hard_iface->bat_v.elp_skb = NULL; + + batadv_v_elp_neigh_purge(hard_iface); }
/** @@ -177,3 +296,86 @@ void batadv_v_elp_primary_iface_set(struct batadv_hard_iface *hard_iface) } rcu_read_unlock(); } + +/** + * batadv_v_elp_neigh_update - update an ELP neighbour node + * @bat_priv: the bat priv with all the soft interface information + * @neigh_addr: the neighbour interface address + * @if_incoming: the interface the packet was received through + * @elp_packet: the received ELP packet + * + * Updates the ELP neighbour node state with the data received within the new + * ELP packet. + */ +static void batadv_v_elp_neigh_update(struct batadv_priv *bat_priv, + uint8_t *neigh_addr, + struct batadv_hard_iface *if_incoming, + struct batadv_elp_packet *elp_packet) +{ + struct batadv_elp_neigh_node *neigh; + + neigh = batadv_v_elp_neigh_get(if_incoming, neigh_addr); + if (!neigh) { + neigh = batadv_v_elp_neigh_new(if_incoming, neigh_addr, + elp_packet->orig, + ntohl(elp_packet->seqno)); + if (!neigh) + goto out; + } + + neigh->last_seen = jiffies; + +out: + if (neigh) + batadv_elp_neigh_node_free_ref(neigh); +} + +/** + * batadv_v_elp_packet_recv - main ELP packet handler + * @skb: the received packet + * @if_incoming: the interface this packet was received through + * + * Returns NET_RX_SUCCESS and consumes the skb if the packet was peoperly + * processed or NET_RX_DROP in case of failure. + */ +int batadv_v_elp_packet_recv(struct sk_buff *skb, + struct batadv_hard_iface *if_incoming) +{ + struct batadv_priv *bat_priv = netdev_priv(if_incoming->soft_iface); + struct batadv_elp_packet *elp_packet; + struct batadv_hard_iface *primary_if; + struct ethhdr *ethhdr = (struct ethhdr *)skb_mac_header(skb); + bool ret; + + ret = batadv_check_management_packet(skb, if_incoming, BATADV_ELP_HLEN); + if (!ret) + return NET_RX_DROP; + + if (batadv_is_my_mac(bat_priv, ethhdr->h_source)) + return NET_RX_DROP; + + /* did we receive a B.A.T.M.A.N. V ELP packet on an interface + * that does not have B.A.T.M.A.N. V ELP enabled ? */ + if (strcmp(bat_priv->bat_algo_ops->name, "BATMAN_V") != 0) + return NET_RX_DROP; + + elp_packet = (struct batadv_elp_packet *)skb->data; + + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, + "Received ELP packet from %pM seqno %u ORIG: %pM\n", + ethhdr->h_source, ntohl(elp_packet->seqno), + elp_packet->orig); + + primary_if = batadv_primary_if_get_selected(bat_priv); + if (!primary_if) + goto out; + + batadv_v_elp_neigh_update(bat_priv, ethhdr->h_source, if_incoming, + elp_packet); + +out: + if (primary_if) + batadv_hardif_free_ref(primary_if); + consume_skb(skb); + return NET_RX_SUCCESS; +} diff --git a/bat_v_elp.h b/bat_v_elp.h index 06d2cb1..186ff4b 100644 --- a/bat_v_elp.h +++ b/bat_v_elp.h @@ -23,8 +23,14 @@ #ifndef _NET_BATMAN_ADV_BAT_V_ELP_H_ #define _NET_BATMAN_ADV_BAT_V_ELP_H_
+struct batadv_elp_neigh_node * +batadv_v_elp_neigh_get(struct batadv_hard_iface *hard_iface, + uint8_t *neigh_addr); +void batadv_elp_neigh_node_free_ref(struct batadv_elp_neigh_node *neigh); int batadv_v_elp_iface_enable(struct batadv_hard_iface *hard_iface); void batadv_v_elp_iface_disable(struct batadv_hard_iface *hard_iface); void batadv_v_elp_primary_iface_set(struct batadv_hard_iface *hard_iface); +int batadv_v_elp_packet_recv(struct sk_buff *skb, + struct batadv_hard_iface *if_incoming);
#endif /* _NET_BATMAN_ADV_BAT_V_ELP_H_ */ diff --git a/main.h b/main.h index 34e85a2..4ab3216 100644 --- a/main.h +++ b/main.h @@ -45,6 +45,7 @@ #define BATADV_TT_WORK_PERIOD 5000 /* 5 seconds */ #define BATADV_ORIG_WORK_PERIOD 1000 /* 1 second */ #define BATADV_DAT_ENTRY_TIMEOUT (5*60000) /* 5 mins in milliseconds */ +#define BATADV_ELP_OUTDATED_MAX 4 /* sliding packet range of received originator messages in sequence numbers * (should be a multiple of our word size) */ @@ -180,6 +181,7 @@ enum batadv_uev_type { #include <linux/jiffies.h> #include <linux/seq_file.h> #include <linux/if_vlan.h> +#include <linux/average.h> /* ewma */ #include "compat.h"
#include "types.h" diff --git a/types.h b/types.h index 518fa81..be230b6 100644 --- a/types.h +++ b/types.h @@ -73,12 +73,18 @@ struct batadv_hard_iface_bat_iv { * struct batadv_hard_iface_bat_v - per hard interface B.A.T.M.A.N. V data * @elp_interval: time interval between two ELP transmissions * @elp_seqno: current ELP sequence number + * @neigh_list: list of ELP neighbour objects + * @neigh_list_lock: protects neigh_list + * @num_neighbours: number of neighbours in the neigh_list * @elp_skb: base skb containing the ELP message to send * @elp_wq: workqueue used to schedule ELP transmissions */ struct batadv_hard_iface_bat_v { atomic_t elp_interval; atomic_t elp_seqno; + struct hlist_head neigh_list; + spinlock_t neigh_list_lock; + atomic_t num_neighbors; struct sk_buff *elp_skb; struct delayed_work elp_wq; }; @@ -327,6 +333,37 @@ struct batadv_gw_node { };
/** + * struct batadv_elp_neigh - hard_iface ralated neighbor information + * @list: list node for batadv_hard_iface::neigh_list + * @orig: the MAC address of the corresponding orig_node + * @addr: the MAC address of the neighboring interface + * @metric: ewma link metric towards this neighbor + * @last_recv_seqno: last ELP received sequence number + * @lest_seen: last time this neigh has been seen + * @refcount: number of contexts the object is used + * @rcu: struct used for freeing in an RCU-safe manner + */ +struct batadv_elp_neigh_node { + struct hlist_node list; + uint8_t orig[ETH_ALEN]; + uint8_t addr[ETH_ALEN]; + struct ewma metric; + uint32_t last_recv_seqno; + unsigned long last_seen; + uint32_t elp_interval; + atomic_t refcount; + struct rcu_head rcu; +}; + +/** + * batadv_neigh_node_bat_v - B.A.T.M.A.N. V private neighbor information + * @elp_neigh: ELP private neighbour data + */ +struct batadv_neigh_node_bat_v { + struct batadv_elp_neigh_node *elp_neigh; +}; + +/** * struct batadv_neigh_node - structure for single hops neighbors * @list: list node for batadv_orig_node::neigh_list * @orig_node: pointer to corresponding orig_node @@ -336,6 +373,7 @@ struct batadv_gw_node { * @if_incoming: pointer to incoming hard interface * @last_seen: when last packet via this neighbor was received * @last_ttl: last received ttl from this neigh node + * @elp_neigh: ELP private neighbour data * @rcu: struct used for freeing in an RCU-safe manner * @bat_iv: B.A.T.M.A.N. IV private structure */ @@ -347,6 +385,9 @@ struct batadv_neigh_node { spinlock_t ifinfo_lock; /* protects ifinfo_list and its members */ struct batadv_hard_iface *if_incoming; unsigned long last_seen; +#ifdef CONFIG_BATMAN_ADV_BATMAN_V + struct batadv_neigh_node_bat_v bat_v; +#endif atomic_t refcount; struct rcu_head rcu; }; @@ -370,6 +411,15 @@ struct batadv_neigh_ifinfo_bat_iv { };
/** + * struct batadv_neigh_ifinfo_bat_v - neighbor information per outgoing + * interface for BATMAN V + * @metric: last metric received from an originator via this neigh + */ +struct batadv_neigh_ifinfo_bat_v { + uint32_t metric; +}; + +/** * struct batadv_neigh_ifinfo - neighbor information per outgoing interface * @list: list node for batadv_neigh_node::ifinfo_list * @if_outgoing: pointer to outgoing hard interface @@ -382,6 +432,9 @@ struct batadv_neigh_ifinfo { struct hlist_node list; struct batadv_hard_iface *if_outgoing; struct batadv_neigh_ifinfo_bat_iv bat_iv; +#ifdef CONFIG_BATMAN_ADV_BATMAN_V + struct batadv_neigh_ifinfo_bat_v bat_v; +#endif uint8_t last_ttl; atomic_t refcount; struct rcu_head rcu;
On Tue, Feb 11, 2014 at 01:48:04PM +0100, Antonio Quartulli wrote:
From: Linus Luessing linus.luessing@web.de
Initially developed by Linus during a 6 months trainee study period in Ascom (Switzerland) AG.
Signed-off-by: Linus Luessing linus.luessing@web.de Signed-off-by: Marek Lindner lindner_marek@yahoo.de Signed-off-by: Antonio Quartulli antonio@open-mesh.com
bat_v.c | 18 +++++- bat_v_elp.c | 204 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- bat_v_elp.h | 6 ++ main.h | 2 + types.h | 53 ++++++++++++++++ 5 files changed, 281 insertions(+), 2 deletions(-)
diff --git a/bat_v.c b/bat_v.c index 7247d7f..bed5e00 100644 --- a/bat_v.c +++ b/bat_v.c @@ -61,5 +61,21 @@ static struct batadv_algo_ops batadv_batman_v __read_mostly = {
int __init batadv_v_init(void) {
- return batadv_algo_register(&batadv_batman_v);
- int ret;
- /* batman v echo location protocol packet */
- ret = batadv_recv_handler_register(BATADV_ELP,
batadv_v_elp_packet_recv);
- if (ret < 0)
goto elp_unregister;
- ret = batadv_algo_register(&batadv_batman_v);
- return ret;
+elp_unregister:
- if (ret < 0)
batadv_recv_handler_unregister(BATADV_ELP);
No need to check ret here. If we are here, it has to be < 0.
It also seems odd to me you are unregistering the handler when the registration of the handler fails!
I suspect the first if (ret < 0) should be followed by a plain return ret; and there should be a second test for the return value of batadv_algo_register() which should goto the label and unregister the handler.
Andrew
On 11/02/14 16:32, Andrew Lunn wrote:
On Tue, Feb 11, 2014 at 01:48:04PM +0100, Antonio Quartulli wrote:
From: Linus Luessing linus.luessing@web.de
Initially developed by Linus during a 6 months trainee study period in Ascom (Switzerland) AG.
Signed-off-by: Linus Luessing linus.luessing@web.de Signed-off-by: Marek Lindner lindner_marek@yahoo.de Signed-off-by: Antonio Quartulli antonio@open-mesh.com
bat_v.c | 18 +++++- bat_v_elp.c | 204 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- bat_v_elp.h | 6 ++ main.h | 2 + types.h | 53 ++++++++++++++++ 5 files changed, 281 insertions(+), 2 deletions(-)
diff --git a/bat_v.c b/bat_v.c index 7247d7f..bed5e00 100644 --- a/bat_v.c +++ b/bat_v.c @@ -61,5 +61,21 @@ static struct batadv_algo_ops batadv_batman_v __read_mostly = {
int __init batadv_v_init(void) {
- return batadv_algo_register(&batadv_batman_v);
- int ret;
- /* batman v echo location protocol packet */
- ret = batadv_recv_handler_register(BATADV_ELP,
batadv_v_elp_packet_recv);
- if (ret < 0)
goto elp_unregister;
- ret = batadv_algo_register(&batadv_batman_v);
- return ret;
+elp_unregister:
- if (ret < 0)
batadv_recv_handler_unregister(BATADV_ELP);
No need to check ret here. If we are here, it has to be < 0.
It also seems odd to me you are unregistering the handler when the registration of the handler fails!
I suspect the first if (ret < 0) should be followed by a plain return ret; and there should be a second test for the return value of batadv_algo_register() which should goto the label and unregister the handler.
I totally agree with what you said. We should jump to elp_unregister if batadv_algo_register() fails.
Thanks!
On Tuesday 11 February 2014 11:02:11 Antonio Quartulli wrote:
On 11/02/14 16:32, Andrew Lunn wrote:
On Tue, Feb 11, 2014 at 01:48:04PM +0100, Antonio Quartulli wrote:
From: Linus Luessing linus.luessing@web.de
Initially developed by Linus during a 6 months trainee study period in Ascom (Switzerland) AG.
Signed-off-by: Linus Luessing linus.luessing@web.de Signed-off-by: Marek Lindner lindner_marek@yahoo.de Signed-off-by: Antonio Quartulli antonio@open-mesh.com
bat_v.c | 18 +++++- bat_v_elp.c | 204
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
bat_v_elp.h | 6 ++ main.h | 2 + types.h | 53 ++++++++++++++++ 5 files changed, 281 insertions(+), 2 deletions(-)
diff --git a/bat_v.c b/bat_v.c index 7247d7f..bed5e00 100644 --- a/bat_v.c +++ b/bat_v.c @@ -61,5 +61,21 @@ static struct batadv_algo_ops batadv_batman_v
__read_mostly = {
int __init batadv_v_init(void) {
- return batadv_algo_register(&batadv_batman_v);
- int ret;
- /* batman v echo location protocol packet */
- ret = batadv_recv_handler_register(BATADV_ELP,
batadv_v_elp_packet_recv);
- if (ret < 0)
goto elp_unregister;
- ret = batadv_algo_register(&batadv_batman_v);
- return ret;
+elp_unregister:
- if (ret < 0)
batadv_recv_handler_unregister(BATADV_ELP);
No need to check ret here. If we are here, it has to be < 0.
It also seems odd to me you are unregistering the handler when the registration of the handler fails!
I suspect the first if (ret < 0) should be followed by a plain return ret; and there should be a second test for the return value of batadv_algo_register() which should goto the label and unregister the handler.
I totally agree with what you said. We should jump to elp_unregister if batadv_algo_register() fails.
Sorry to break in here, but the (ex) professional programmer in me just /has/ to comment.
Why not just int __init batadv_v_init(void) { - return batadv_algo_register(&batadv_batman_v); + int ret; + + /* batman v echo location protocol packet */ + ret = batadv_recv_handler_register(BATADV_ELP, + batadv_v_elp_packet_recv); + + if (ret >= 0) + ret = batadv_algo_register(&batadv_batman_v); + else + batadv_recv_handler_unregister(BATADV_ELP); + + return ret; ?
On 11/02/14 17:11, Lew Pitcher wrote:
On Tuesday 11 February 2014 11:02:11 Antonio Quartulli wrote:
On 11/02/14 16:32, Andrew Lunn wrote:
On Tue, Feb 11, 2014 at 01:48:04PM +0100, Antonio Quartulli wrote:
From: Linus Luessing linus.luessing@web.de
Initially developed by Linus during a 6 months trainee study period in Ascom (Switzerland) AG.
Signed-off-by: Linus Luessing linus.luessing@web.de Signed-off-by: Marek Lindner lindner_marek@yahoo.de Signed-off-by: Antonio Quartulli antonio@open-mesh.com
bat_v.c | 18 +++++- bat_v_elp.c | 204
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
bat_v_elp.h | 6 ++ main.h | 2 + types.h | 53 ++++++++++++++++ 5 files changed, 281 insertions(+), 2 deletions(-)
diff --git a/bat_v.c b/bat_v.c index 7247d7f..bed5e00 100644 --- a/bat_v.c +++ b/bat_v.c @@ -61,5 +61,21 @@ static struct batadv_algo_ops batadv_batman_v
__read_mostly = {
int __init batadv_v_init(void) {
- return batadv_algo_register(&batadv_batman_v);
- int ret;
- /* batman v echo location protocol packet */
- ret = batadv_recv_handler_register(BATADV_ELP,
batadv_v_elp_packet_recv);
- if (ret < 0)
goto elp_unregister;
- ret = batadv_algo_register(&batadv_batman_v);
- return ret;
+elp_unregister:
- if (ret < 0)
batadv_recv_handler_unregister(BATADV_ELP);
No need to check ret here. If we are here, it has to be < 0.
It also seems odd to me you are unregistering the handler when the registration of the handler fails!
I suspect the first if (ret < 0) should be followed by a plain return ret; and there should be a second test for the return value of batadv_algo_register() which should goto the label and unregister the handler.
I totally agree with what you said. We should jump to elp_unregister if batadv_algo_register() fails.
Sorry to break in here, but the (ex) professional programmer in me just /has/ to comment.
Everybody is welcome! :)
Why not just int __init batadv_v_init(void) {
- return batadv_algo_register(&batadv_batman_v);
- int ret;
- /* batman v echo location protocol packet */
- ret = batadv_recv_handler_register(BATADV_ELP,
batadv_v_elp_packet_recv);
- if (ret >= 0)
ret = batadv_algo_register(&batadv_batman_v);
- else
batadv_recv_handler_unregister(BATADV_ELP);
- return ret;
?
Actually I already fixed it like this:
int __init batadv_v_init(void) { - return batadv_algo_register(&batadv_batman_v); + int ret; + + /* batman v echo location protocol packet */ + ret = batadv_recv_handler_register(BATADV_ELP, + batadv_v_elp_packet_recv); + if (ret < 0) + return ret; + + ret = batadv_algo_register(&batadv_batman_v); + if (ret < 0) + batadv_recv_handler_unregister(BATADV_ELP); + + return ret;
I think it is easier to read, no? Anyway, this chunk is changed later b patch 7/23 because we add the OGM2 registration.
Cheers,
From: Linus Luessing linus.luessing@web.de
Lists all neighbours detected by the Echo Locating Protocol (ELP) and their metric values.
Initially Developed by Linus during a 6 months trainee study period in Ascom (Switzerland) AG.
Signed-off-by: Linus Luessing linus.luessing@web.de Signed-off-by: Marek Lindner lindner_marek@yahoo.de Signed-off-by: Antonio Quartulli antonio@open-mesh.com --- bat_v_elp.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ bat_v_elp.h | 1 + debugfs.c | 17 +++++++++++++ 3 files changed, 98 insertions(+)
diff --git a/bat_v_elp.c b/bat_v_elp.c index ee4207d..9c40415 100644 --- a/bat_v_elp.c +++ b/bat_v_elp.c @@ -379,3 +379,83 @@ out: consume_skb(skb); return NET_RX_SUCCESS; } + +/** + * batadv_v_elp_seq_print_neigh - print a single ELP neighbour node + * @seq: neighbour table seq_file struct + * @hard_iface: interface the neighbour is connected to + * @neigh: the neighbour node to print + */ +static void batadv_v_elp_seq_print_neigh(struct seq_file *seq, + struct batadv_hard_iface *hard_iface, + struct batadv_elp_neigh_node *neigh) +{ + int last_secs, last_msecs; + uint32_t metric; + + last_secs = jiffies_to_msecs(jiffies - neigh->last_seen) / 1000; + last_msecs = jiffies_to_msecs(jiffies - neigh->last_seen) % 1000; + metric = ewma_read(&neigh->metric); + + seq_printf(seq, "%pM %4i.%03is (%9u.%1u) [%10s]\n", + neigh->addr, last_secs, last_msecs, metric / 10, + metric % 10, hard_iface->net_dev->name); +} + +/** + * batadv_v_elp_seq_print_text - print the neighbour table + * @seq: neighbour table seq_file struct + * @offset: not used + * + * Returns always 0. + */ +int batadv_v_elp_seq_print_text(struct seq_file *seq, void *offset) +{ + struct net_device *net_dev = (struct net_device *)seq->private; + struct batadv_priv *bat_priv = netdev_priv(net_dev); + struct batadv_hard_iface *hard_iface, *primary_if; + struct batadv_elp_neigh_node *neigh; + int batman_count = 0; + + primary_if = batadv_primary_if_get_selected(bat_priv); + + if (!primary_if) { + seq_printf(seq, + "BATMAN mesh %s disabled - please specify interfaces to enable it\n", + net_dev->name); + goto out; + } + + if (primary_if->if_status != BATADV_IF_ACTIVE) { + seq_printf(seq, + "BATMAN mesh %s disabled - primary interface not active\n", + net_dev->name); + goto out; + } + + seq_printf(seq, "[B.A.T.M.A.N. adv %s, MainIF/MAC: %s/%pM (%s)]\n", + BATADV_SOURCE_VERSION, primary_if->net_dev->name, + primary_if->net_dev->dev_addr, net_dev->name); + seq_printf(seq, " %-15s %s (%11s) [%10s]\n", "Neighbor", + "last-seen", "metric", "IF"); + + rcu_read_lock(); + list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) { + if (hard_iface->soft_iface != net_dev) + continue; + + hlist_for_each_entry_rcu(neigh, &hard_iface->bat_v.neigh_list, list) { + batadv_v_elp_seq_print_neigh(seq, hard_iface, neigh); + batman_count++; + } + } + rcu_read_unlock(); + + if (batman_count == 0) + seq_printf(seq, "No batman nodes in range ...\n"); + +out: + if (primary_if) + batadv_hardif_free_ref(primary_if); + return 0; +} diff --git a/bat_v_elp.h b/bat_v_elp.h index 186ff4b..4f34ecb 100644 --- a/bat_v_elp.h +++ b/bat_v_elp.h @@ -32,5 +32,6 @@ void batadv_v_elp_iface_disable(struct batadv_hard_iface *hard_iface); void batadv_v_elp_primary_iface_set(struct batadv_hard_iface *hard_iface); int batadv_v_elp_packet_recv(struct sk_buff *skb, struct batadv_hard_iface *if_incoming); +int batadv_v_elp_seq_print_text(struct seq_file *seq, void *offset);
#endif /* _NET_BATMAN_ADV_BAT_V_ELP_H_ */ diff --git a/debugfs.c b/debugfs.c index b758881..23990cf 100644 --- a/debugfs.c +++ b/debugfs.c @@ -20,6 +20,7 @@ #include <linux/debugfs.h>
#include "debugfs.h" +#include "bat_v_elp.h" #include "translation-table.h" #include "originator.h" #include "hard-interface.h" @@ -242,6 +243,16 @@ static int batadv_algorithms_open(struct inode *inode, struct file *file) return single_open(file, batadv_algo_seq_print_text, NULL); }
+#ifdef CONFIG_BATMAN_ADV_BATMAN_V +int batadv_v_elp_seq_print_text(struct seq_file *seq, void *offset); + +static int neighbors_open(struct inode *inode, struct file *file) +{ + struct net_device *net_dev = (struct net_device *)inode->i_private; + return single_open(file, batadv_v_elp_seq_print_text, net_dev); +} +#endif + static int batadv_originators_open(struct inode *inode, struct file *file) { struct net_device *net_dev = (struct net_device *)inode->i_private; @@ -346,6 +357,9 @@ static struct batadv_debuginfo *batadv_general_debuginfos[] = { };
/* The following attributes are per soft interface */ +#ifdef CONFIG_BATMAN_ADV_BATMAN_V +static BATADV_DEBUGINFO(neighbors, S_IRUGO, neighbors_open); +#endif static BATADV_DEBUGINFO(originators, S_IRUGO, batadv_originators_open); static BATADV_DEBUGINFO(gateways, S_IRUGO, batadv_gateways_open); static BATADV_DEBUGINFO(transtable_global, S_IRUGO, @@ -365,6 +379,9 @@ static BATADV_DEBUGINFO(nc_nodes, S_IRUGO, batadv_nc_nodes_open); #endif
static struct batadv_debuginfo *batadv_mesh_debuginfos[] = { +#ifdef CONFIG_BATMAN_ADV_BATMAN_V + &batadv_debuginfo_neighbors, +#endif &batadv_debuginfo_originators, &batadv_debuginfo_gateways, &batadv_debuginfo_transtable_global,
From: Linus Luessing linus.luessing@web.de
This parameter can be set individually on each interface and allows the configuration of the ndp interval for the link quality measurements during runtime. Usually it is desirable to set it to a higher (= slower) value on interfaces which have a more static characteristic (e.g. wired interfaces) or very dense neighbourhoods to reduce overhead.
Developed by Linus during a 6 months trainee study period in Ascom (Switzerland) AG.
Signed-off-by: Linus Luessing linus.luessing@web.de Signed-off-by: Marek Lindner lindner_marek@yahoo.de [antonio@open-mesh.com: respin on top of the latest master] Signed-off-by: Antonio Quartulli antonio@open-mesh.com --- sysfs-class-net-batman-adv | 8 +++++++- sysfs.c | 7 +++++++ 2 files changed, 14 insertions(+), 1 deletion(-)
diff --git a/sysfs-class-net-batman-adv b/sysfs-class-net-batman-adv index 7f34a95..2884c98 100644 --- a/sysfs-class-net-batman-adv +++ b/sysfs-class-net-batman-adv @@ -1,4 +1,11 @@
+What: /sys/class/net/<mesh_iface>/batman-adv/elp_interval +Date: Mar 2012 +Contact: Linus Lüssing linus.luessing@web.de +Description: + Defines the interval in milliseconds in which batman + sends its probing packets for link quality measurements. + What: /sys/class/net/<iface>/batman-adv/iface_status Date: May 2010 Contact: Marek Lindner mareklindner@neomailbox.ch @@ -12,4 +19,3 @@ Description: The /sys/class/net/<iface>/batman-adv/mesh_iface file displays the batman mesh interface this <iface> currently is associated with. - diff --git a/sysfs.c b/sysfs.c index 1e9ee3a..1d6653c 100644 --- a/sysfs.c +++ b/sysfs.c @@ -877,10 +877,17 @@ static ssize_t batadv_show_iface_status(struct kobject *kobj, static BATADV_ATTR(mesh_iface, S_IRUGO | S_IWUSR, batadv_show_mesh_iface, batadv_store_mesh_iface); static BATADV_ATTR(iface_status, S_IRUGO, batadv_show_iface_status, NULL); +#ifdef CONFIG_BATMAN_ADV_BATMAN_V +BATADV_ATTR_HIF_UINT(elp_interval, bat_v.elp_interval, S_IRUGO | S_IWUSR, + 2 * BATADV_JITTER, INT_MAX, NULL); +#endif
static struct batadv_attribute *batadv_batman_attrs[] = { &batadv_attr_mesh_iface, &batadv_attr_iface_status, +#ifdef CONFIG_BATMAN_ADV_BATMAN_V + &batadv_attr_elp_interval, +#endif NULL, };
On Tue, Feb 11, 2014 at 01:48:06PM +0100, Antonio Quartulli wrote:
From: Linus Luessing linus.luessing@web.de
This parameter can be set individually on each interface and allows the configuration of the ndp interval for the link
s/ndp/elp
quality measurements during runtime. Usually it is desirable to set it to a higher (= slower) value on interfaces which have a more static characteristic (e.g. wired interfaces) or very dense neighbourhoods to reduce overhead.
Developed by Linus during a 6 months trainee study period in Ascom (Switzerland) AG.
Signed-off-by: Linus Luessing linus.luessing@web.de Signed-off-by: Marek Lindner lindner_marek@yahoo.de [antonio@open-mesh.com: respin on top of the latest master] Signed-off-by: Antonio Quartulli antonio@open-mesh.com
sysfs-class-net-batman-adv | 8 +++++++- sysfs.c | 7 +++++++ 2 files changed, 14 insertions(+), 1 deletion(-)
diff --git a/sysfs-class-net-batman-adv b/sysfs-class-net-batman-adv index 7f34a95..2884c98 100644 --- a/sysfs-class-net-batman-adv +++ b/sysfs-class-net-batman-adv @@ -1,4 +1,11 @@
+What: /sys/class/net/<mesh_iface>/batman-adv/elp_interval +Date: Mar 2012
Might make sense to update this to 2014?
Andrew
On 11/02/14 17:59, Andrew Lunn wrote:
On Tue, Feb 11, 2014 at 01:48:06PM +0100, Antonio Quartulli wrote:
From: Linus Luessing linus.luessing@web.de
This parameter can be set individually on each interface and allows the configuration of the ndp interval for the link
s/ndp/elp
thanks!
quality measurements during runtime. Usually it is desirable to set it to a higher (= slower) value on interfaces which have a more static characteristic (e.g. wired interfaces) or very dense neighbourhoods to reduce overhead.
Developed by Linus during a 6 months trainee study period in Ascom (Switzerland) AG.
Signed-off-by: Linus Luessing linus.luessing@web.de Signed-off-by: Marek Lindner lindner_marek@yahoo.de [antonio@open-mesh.com: respin on top of the latest master] Signed-off-by: Antonio Quartulli antonio@open-mesh.com
sysfs-class-net-batman-adv | 8 +++++++- sysfs.c | 7 +++++++ 2 files changed, 14 insertions(+), 1 deletion(-)
diff --git a/sysfs-class-net-batman-adv b/sysfs-class-net-batman-adv index 7f34a95..2884c98 100644 --- a/sysfs-class-net-batman-adv +++ b/sysfs-class-net-batman-adv @@ -1,4 +1,11 @@
+What: /sys/class/net/<mesh_iface>/batman-adv/elp_interval +Date: Mar 2012
Might make sense to update this to 2014?
I kept it as it was when the patch got merged in our repo. But I think it should match the month it gets introduced in the kernel.
Thanks²!
From: Antonio Quartulli antonio@open-mesh.com
This is the initial implementation of the new OGM protocol (version 2). It has been designed to work on top of the newly added ELP.
In the previous version the OGM protocol was used to both measure link qualities and flood the network with the metric information. In this version the protocol is in charge of the latter task only, leaving the former to ELP.
This means being able to decouple the interval used by the neighbor discovery from the OGM broadcasting, which revealed to be costly in dense networks and needed to be relaxed so leading to a less responsive routing protocol.
Signed-off-by: Antonio Quartulli antonio@open-mesh.com --- Makefile.kbuild | 1 + bat_algo.h | 11 +++ bat_v.c | 36 ++++++++- bat_v_elp.c | 11 ++- bat_v_ogm.c | 230 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ bat_v_ogm.h | 32 ++++++++ main.c | 31 +++++++- main.h | 1 + packet.h | 25 ++++++ types.h | 15 ++++ 10 files changed, 385 insertions(+), 8 deletions(-) create mode 100644 bat_v_ogm.c create mode 100644 bat_v_ogm.h
diff --git a/Makefile.kbuild b/Makefile.kbuild index ae9a6ca..8073940 100644 --- a/Makefile.kbuild +++ b/Makefile.kbuild @@ -20,6 +20,7 @@ obj-$(CONFIG_BATMAN_ADV) += batman-adv.o batman-adv-y += bat_iv_ogm.o batman-adv-$(CONFIG_BATMAN_ADV_BATMAN_V) += bat_v.o batman-adv-$(CONFIG_BATMAN_ADV_BATMAN_V) += bat_v_elp.o +batman-adv-$(CONFIG_BATMAN_ADV_BATMAN_V) += bat_v_ogm.o batman-adv-y += bitarray.o batman-adv-$(CONFIG_BATMAN_ADV_BLA) += bridge_loop_avoidance.o batman-adv-y += debugfs.o diff --git a/bat_algo.h b/bat_algo.h index 7289336..14cf89f 100644 --- a/bat_algo.h +++ b/bat_algo.h @@ -23,6 +23,8 @@ int batadv_iv_init(void); #ifdef CONFIG_BATMAN_ADV_BATMAN_V
int batadv_v_init(void); +int batadv_v_mesh_init(struct batadv_priv *bat_priv); +void batadv_v_mesh_free(struct batadv_priv *bat_priv);
#else
@@ -31,6 +33,15 @@ static inline int batadv_v_init(void) return 0; }
+static inline int batadv_v_mesh_init(struct batadv_priv *bat_priv) +{ + return 0; +} + +static inline void batadv_v_mesh_free(struct batadv_priv *bat_priv) +{ +} + #endif /* CONFIG_BATMAN_ADV_BATMAN_V */
#endif /* _NET_BATMAN_ADV_BAT_ALGO_H_ */ diff --git a/bat_v.c b/bat_v.c index bed5e00..b4214c5 100644 --- a/bat_v.c +++ b/bat_v.c @@ -20,10 +20,21 @@ #include "main.h" #include "bat_algo.h" #include "bat_v_elp.h" +#include "bat_v_ogm.h"
static int batadv_v_iface_enable(struct batadv_hard_iface *hard_iface) { - return batadv_v_elp_iface_enable(hard_iface); + int ret; + + ret = batadv_v_elp_iface_enable(hard_iface); + if (ret < 0) + return ret; + + ret = batadv_v_ogm_iface_enable(hard_iface); + if (ret < 0) + batadv_v_elp_iface_disable(hard_iface); + + return ret; }
static void batadv_v_iface_disable(struct batadv_hard_iface *hard_iface) @@ -38,6 +49,7 @@ static void batadv_v_iface_update_mac(struct batadv_hard_iface *hard_iface) static void batadv_v_primary_iface_set(struct batadv_hard_iface *hard_iface) { batadv_v_elp_primary_iface_set(hard_iface); + batadv_v_ogm_primary_iface_set(hard_iface); }
static void batadv_v_ogm_schedule(struct batadv_hard_iface *hard_iface) @@ -58,6 +70,15 @@ static struct batadv_algo_ops batadv_batman_v __read_mostly = { .bat_ogm_schedule = batadv_v_ogm_schedule, };
+int batadv_v_mesh_init(struct batadv_priv *bat_priv) +{ + return batadv_v_ogm_init(bat_priv); +} + +void batadv_v_mesh_free(struct batadv_priv *bat_priv) +{ + batadv_v_ogm_free(bat_priv); +}
int __init batadv_v_init(void) { @@ -67,15 +88,24 @@ int __init batadv_v_init(void) ret = batadv_recv_handler_register(BATADV_ELP, batadv_v_elp_packet_recv); if (ret < 0) + return ret; + + ret = batadv_recv_handler_register(BATADV_OGM2, + batadv_v_ogm_packet_recv); + if (ret < 0) goto elp_unregister;
ret = batadv_algo_register(&batadv_batman_v); + if (ret < 0) + goto ogm_unregister;
return ret;
+ogm_unregister: + batadv_recv_handler_unregister(BATADV_OGM2); + elp_unregister: - if (ret < 0) - batadv_recv_handler_unregister(BATADV_ELP); + batadv_recv_handler_unregister(BATADV_ELP);
return ret; } diff --git a/bat_v_elp.c b/bat_v_elp.c index 9c40415..1c23951 100644 --- a/bat_v_elp.c +++ b/bat_v_elp.c @@ -25,6 +25,7 @@ #include "bat_v_elp.h" #include "originator.h" #include "routing.h" +#include "translation-table.h"
/** * batadv_v_elp_start_timer - restart timer for ELP periodic work @@ -276,11 +277,15 @@ void batadv_v_elp_iface_disable(struct batadv_hard_iface *hard_iface) */ void batadv_v_elp_primary_iface_set(struct batadv_hard_iface *hard_iface) { + struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface); struct batadv_hard_iface *hard_iface_tmp; struct batadv_elp_packet *elp_packet; + struct batadv_ogm2_packet *ogm2; struct sk_buff *skb;
- /* update orig field of every elp iface belonging to this mesh */ + /* update orig field of every elp and ogm iface belonging to this + * mesh + */ rcu_read_lock(); list_for_each_entry_rcu(hard_iface_tmp, &batadv_hardif_list, list) { if (hard_iface->soft_iface != hard_iface_tmp->soft_iface) @@ -293,6 +298,9 @@ void batadv_v_elp_primary_iface_set(struct batadv_hard_iface *hard_iface) elp_packet = (struct batadv_elp_packet *)skb->data; memcpy(elp_packet->orig, hard_iface->net_dev->dev_addr, ETH_ALEN); + + ogm2 = (struct batadv_ogm2_packet *)bat_priv->bat_v.ogm_buff; + memcpy(ogm2->orig, hard_iface->net_dev->dev_addr, ETH_ALEN); } rcu_read_unlock(); } @@ -311,6 +319,7 @@ static void batadv_v_elp_neigh_update(struct batadv_priv *bat_priv, uint8_t *neigh_addr, struct batadv_hard_iface *if_incoming, struct batadv_elp_packet *elp_packet) + { struct batadv_elp_neigh_node *neigh;
diff --git a/bat_v_ogm.c b/bat_v_ogm.c new file mode 100644 index 0000000..f66dc02 --- /dev/null +++ b/bat_v_ogm.c @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2013 B.A.T.M.A.N. contributors: + * + * Antonio Quartulli + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + +#include "main.h" + +#include "bat_v_ogm.h" +#include "hard-interface.h" +#include "originator.h" +#include "routing.h" +#include "send.h" +#include "translation-table.h" + +static void batadv_v_ogm_start_timer(struct batadv_priv *bat_priv) +{ + unsigned long msecs; + /* this function may be invoked in different contexts (ogm rescheduling + * or hard_iface activation), but the work timer should not be reset + */ + if (delayed_work_pending(&bat_priv->bat_v.ogm_wq)) + return; + + msecs = atomic_read(&bat_priv->orig_interval) - BATADV_JITTER; + msecs += prandom_u32() % (2 * BATADV_JITTER); + queue_delayed_work(batadv_event_workqueue, &bat_priv->bat_v.ogm_wq, + msecs_to_jiffies(msecs)); +} + +/* send a batman ogm to a given interface */ +static void batadv_v_ogm_send_to_if(struct sk_buff *skb, + struct batadv_hard_iface *hard_iface) +{ + struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface); + struct sk_buff *tmp_skb; + + if (hard_iface->if_status != BATADV_IF_ACTIVE) + return; + + tmp_skb = skb_clone(skb, GFP_ATOMIC); + if (!tmp_skb) + return; + + batadv_inc_counter(bat_priv, BATADV_CNT_MGMT_TX); + batadv_add_counter(bat_priv, BATADV_CNT_MGMT_TX_BYTES, + tmp_skb->len + ETH_HLEN); + + batadv_send_skb_packet(tmp_skb, hard_iface, batadv_broadcast_addr); +} + +static void batadv_v_ogm_send(struct work_struct *work) +{ + struct batadv_hard_iface *hard_iface; + struct batadv_priv_bat_v *bat_v; + struct batadv_priv *bat_priv; + struct batadv_ogm2_packet *ogm2; + struct sk_buff *skb; + unsigned char *ogm_buff, *pkt_buff; + int ogm_buff_len; + uint16_t tvlv_len = 0; + + bat_v = container_of(work, struct batadv_priv_bat_v, ogm_wq.work); + bat_priv = container_of(bat_v, struct batadv_priv, bat_v); + + if (atomic_read(&bat_priv->mesh_state) == BATADV_MESH_DEACTIVATING) + goto out; + + ogm_buff = bat_priv->bat_v.ogm_buff; + ogm_buff_len = bat_priv->bat_v.ogm_buff_len; + /* tt changes have to be committed before the tvlv data is + * appended as it may alter the tt tvlv container + */ + batadv_tt_local_commit_changes(bat_priv); + tvlv_len = batadv_tvlv_container_ogm_append(bat_priv, &ogm_buff, + &ogm_buff_len, + BATADV_OGM2_HLEN); + + bat_priv->bat_v.ogm_buff = ogm_buff; + bat_priv->bat_v.ogm_buff_len = ogm_buff_len; + + skb = netdev_alloc_skb_ip_align(NULL, ETH_HLEN + ogm_buff_len); + if (!skb) + goto out; + + skb_reserve(skb, ETH_HLEN); + pkt_buff = skb_put(skb, ogm_buff_len); + memcpy(pkt_buff, ogm_buff, ogm_buff_len); + + ogm2 = (struct batadv_ogm2_packet *)skb->data; + ogm2->seqno = htonl(atomic_read(&bat_priv->bat_v.ogm_seqno)); + atomic_inc(&bat_priv->bat_v.ogm_seqno); + ogm2->tvlv_len = htons(tvlv_len); + + /* broadcast on every interface */ + rcu_read_lock(); + list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) { + if (hard_iface->soft_iface != bat_priv->soft_iface) + continue; + + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, + "Sending own OGM2 packet (originator %pM, seqno %u, metric %u, TTL %d) on interface %s [%pM]\n", + ogm2->orig, ntohl(ogm2->seqno), ntohl(ogm2->metric), + ogm2->ttl, hard_iface->net_dev->name, + hard_iface->net_dev->dev_addr); + + batadv_v_ogm_send_to_if(skb, hard_iface); + } + rcu_read_unlock(); + + consume_skb(skb); + + batadv_v_ogm_start_timer(bat_priv); +out: + return; +} + +int batadv_v_ogm_iface_enable(struct batadv_hard_iface *hard_iface) +{ + struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface); + + batadv_v_ogm_start_timer(bat_priv); + + return 0; +} + +void batadv_v_ogm_primary_iface_set(struct batadv_hard_iface *hard_iface) +{ + struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface); + struct batadv_hard_iface *hard_iface_tmp; + struct batadv_ogm2_packet *ogm2; + + /* update orig field of every elp and ogm iface belonging to this + * mesh + */ + rcu_read_lock(); + list_for_each_entry_rcu(hard_iface_tmp, &batadv_hardif_list, list) { + if (hard_iface->soft_iface != hard_iface_tmp->soft_iface) + continue; + + if (!hard_iface_tmp->bat_v.elp_skb) + continue; + + ogm2 = (struct batadv_ogm2_packet *)bat_priv->bat_v.ogm_buff; + memcpy(ogm2->orig, hard_iface->net_dev->dev_addr, ETH_ALEN); + } + rcu_read_unlock(); +} + +int batadv_v_ogm_packet_recv(struct sk_buff *skb, + struct batadv_hard_iface *if_incoming) +{ struct batadv_priv *bat_priv = netdev_priv(if_incoming->soft_iface); + struct ethhdr *ethhdr = (struct ethhdr *)skb_mac_header(skb); + struct batadv_ogm2_packet *ogm2; + + /* did we receive a OGM2 packet on an interface that does not have + * B.A.T.M.A.N. V enabled ? + */ + if (strcmp(bat_priv->bat_algo_ops->name, "BATMAN_V") != 0) + return NET_RX_DROP; + + if (!batadv_check_management_packet(skb, if_incoming, BATADV_OGM2_HLEN)) + return NET_RX_DROP; + + if (batadv_is_my_mac(bat_priv, ethhdr->h_source)) + return NET_RX_DROP; + + ogm2 = (struct batadv_ogm2_packet *)skb->data; + + if (batadv_is_my_mac(bat_priv, ogm2->orig)) + return NET_RX_DROP; + + batadv_inc_counter(bat_priv, BATADV_CNT_MGMT_RX); + batadv_add_counter(bat_priv, BATADV_CNT_MGMT_RX_BYTES, + skb->len + ETH_HLEN); + + consume_skb(skb); + return NET_RX_SUCCESS; +} + +int batadv_v_ogm_init(struct batadv_priv *bat_priv) +{ + struct batadv_ogm2_packet *ogm2; + unsigned char *ogm_buff; + uint32_t random_seqno; + + bat_priv->bat_v.ogm_buff_len = BATADV_OGM2_HLEN; + ogm_buff = kmalloc(bat_priv->bat_v.ogm_buff_len, GFP_ATOMIC); + if (!ogm_buff) + return -ENOMEM; + + bat_priv->bat_v.ogm_buff = ogm_buff; + ogm2 = (struct batadv_ogm2_packet *)ogm_buff; + ogm2->packet_type = BATADV_OGM2; + ogm2->version = BATADV_COMPAT_VERSION; + ogm2->ttl = BATADV_TTL; + ogm2->flags = BATADV_NO_FLAGS; + ogm2->metric = htonl(BATADV_MAX_METRIC); + + /* randomize initial seqno to avoid collision */ + get_random_bytes(&random_seqno, sizeof(random_seqno)); + atomic_set(&bat_priv->bat_v.ogm_seqno, random_seqno); + INIT_DELAYED_WORK(&bat_priv->bat_v.ogm_wq, batadv_v_ogm_send); + + return 0; +} + +void batadv_v_ogm_free(struct batadv_priv *bat_priv) +{ + cancel_delayed_work_sync(&bat_priv->bat_v.ogm_wq); + + kfree(bat_priv->bat_v.ogm_buff); + bat_priv->bat_v.ogm_buff = NULL; + bat_priv->bat_v.ogm_buff_len = 0; +} diff --git a/bat_v_ogm.h b/bat_v_ogm.h new file mode 100644 index 0000000..7f7341e --- /dev/null +++ b/bat_v_ogm.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2013 B.A.T.M.A.N. contributors: + * + * Antonio Quartulli + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + +#ifndef _BATAMAN_ADV_BATADV_V_OGM_H_ +#define _BATAMAN_ADV_BATADV_V_OGM_H_ + +int batadv_v_ogm_init(struct batadv_priv *bat_priv); +void batadv_v_ogm_free(struct batadv_priv *bat_priv); +int batadv_v_ogm_iface_enable(struct batadv_hard_iface *hard_iface); +void batadv_v_ogm_primary_iface_set(struct batadv_hard_iface *hard_iface); +int batadv_v_ogm_packet_recv(struct sk_buff *skb, + struct batadv_hard_iface *if_incoming); + +#endif /* _BATAMAN_ADV_BATADV_V_OGM_H_ */ diff --git a/main.c b/main.c index b7d9996..424fa82 100644 --- a/main.c +++ b/main.c @@ -35,6 +35,7 @@ #include "bridge_loop_avoidance.h" #include "distributed-arp-table.h" #include "gateway_common.h" +#include "hard-interface.h" #include "hash.h" #include "bat_algo.h" #include "network-coding.h" @@ -125,6 +126,10 @@ int batadv_mesh_init(struct net_device *soft_iface) INIT_HLIST_HEAD(&bat_priv->tvlv.handler_list); INIT_HLIST_HEAD(&bat_priv->softif_vlan_list);
+ ret = batadv_v_mesh_init(bat_priv); + if (ret < 0) + goto err; + ret = batadv_originator_init(bat_priv); if (ret < 0) goto err; @@ -165,6 +170,8 @@ void batadv_mesh_free(struct net_device *soft_iface)
batadv_purge_outstanding_packets(bat_priv, NULL);
+ batadv_v_mesh_free(bat_priv); + batadv_gw_node_purge(bat_priv); batadv_nc_mesh_free(bat_priv); batadv_dat_free(bat_priv); @@ -835,21 +842,32 @@ uint16_t batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv, { struct batadv_tvlv_container *tvlv; struct batadv_tvlv_hdr *tvlv_hdr; - uint16_t tvlv_value_len; + uint16_t tvlv_value_len = 0; void *tvlv_value; bool ret; + struct batadv_hard_iface *primary_if; + + primary_if = batadv_primary_if_get_selected(bat_priv); + if (!primary_if) + goto end;
spin_lock_bh(&bat_priv->tvlv.container_list_lock); tvlv_value_len = batadv_tvlv_container_list_size(bat_priv);
+ if (packet_min_len + tvlv_value_len > primary_if->net_dev->mtu) { + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, + "OGM size too big to fit the primary_if mtu\n"); + goto unlock; + } + ret = batadv_tvlv_realloc_packet_buff(packet_buff, packet_buff_len, packet_min_len, tvlv_value_len);
if (!ret) - goto end; + goto unlock;
if (!tvlv_value_len) - goto end; + goto unlock;
tvlv_value = (*packet_buff) + packet_min_len;
@@ -863,8 +881,13 @@ uint16_t batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv, tvlv_value = (uint8_t *)tvlv_value + ntohs(tvlv->tvlv_hdr.len); }
-end: +unlock: spin_unlock_bh(&bat_priv->tvlv.container_list_lock); + +end: + if (primary_if) + batadv_hardif_free_ref(primary_if); + return tvlv_value_len; }
diff --git a/main.h b/main.h index 4ab3216..4badd01 100644 --- a/main.h +++ b/main.h @@ -30,6 +30,7 @@ /* B.A.T.M.A.N. parameters */
#define BATADV_TQ_MAX_VALUE 255 +#define BATADV_MAX_METRIC 0xFFFFFFFF #define BATADV_JITTER 20
/* Time To Live of broadcast messages */ diff --git a/packet.h b/packet.h index 71b1aeb..a985802 100644 --- a/packet.h +++ b/packet.h @@ -38,6 +38,7 @@ enum batadv_packettype { BATADV_BCAST = 0x01, BATADV_CODED = 0x02, BATADV_ELP = 0x03, + BATADV_OGM2 = 0x0d, /* 0x40 - 0x7f: unicast */ #define BATADV_UNICAST_MIN 0x40 BATADV_UNICAST = 0x40, @@ -193,6 +194,30 @@ struct batadv_ogm_packet { #define BATADV_OGM_HLEN sizeof(struct batadv_ogm_packet)
/** + * struct batadv_ogm2_packet - ogm2 (routing protocol) packet + * @packet_type: batman-adv packet type, part of the general header + * @version: batman-adv protocol version, part of the general header + * @ttl: time to live for this packet, part of the general header + * @flags: + * @seqno: sequence number + * @orig: originator mac address + * @tvlv_len: length of the appended tvlv buffer (in bytes) + * @metric: the currently flooded metric value + */ +struct batadv_ogm2_packet { + uint8_t packet_type; + uint8_t version; + uint8_t ttl; + uint8_t flags; + __be32 seqno; + uint8_t orig[ETH_ALEN]; + __be16 tvlv_len; + __be32 metric; +}; + +#define BATADV_OGM2_HLEN sizeof(struct batadv_ogm2_packet) + +/** * struct batadv_elp_packet - elp (neighbor discovery) packet * @packet_type: batman-adv packet type, part of the general header * @version: batman-adv protocol version, part of the genereal header diff --git a/types.h b/types.h index be230b6..e9149d1 100644 --- a/types.h +++ b/types.h @@ -728,6 +728,18 @@ struct batadv_softif_vlan { };
/** + * struct batadv_priv_bat_v - + * @ogm_buff: + * @ogm_buff_len: + */ +struct batadv_priv_bat_v { + unsigned char *ogm_buff; + int ogm_buff_len; + struct delayed_work ogm_wq; + atomic_t ogm_seqno; +}; + +/** * struct batadv_priv - per mesh interface data * @mesh_state: current status of the mesh (inactive/active/deactivating) * @soft_iface: net device which holds this struct as private data @@ -834,6 +846,9 @@ struct batadv_priv { atomic_t network_coding; struct batadv_priv_nc nc; #endif /* CONFIG_BATMAN_ADV_NC */ +#ifdef CONFIG_BATMAN_ADV_BATMAN_V + struct batadv_priv_bat_v bat_v; +#endif };
/**
+static void batadv_v_ogm_send(struct work_struct *work) +{
- struct batadv_hard_iface *hard_iface;
- struct batadv_priv_bat_v *bat_v;
- struct batadv_priv *bat_priv;
- struct batadv_ogm2_packet *ogm2;
- struct sk_buff *skb;
- unsigned char *ogm_buff, *pkt_buff;
- int ogm_buff_len;
- uint16_t tvlv_len = 0;
- bat_v = container_of(work, struct batadv_priv_bat_v, ogm_wq.work);
- bat_priv = container_of(bat_v, struct batadv_priv, bat_v);
- if (atomic_read(&bat_priv->mesh_state) == BATADV_MESH_DEACTIVATING)
goto out;
- ogm_buff = bat_priv->bat_v.ogm_buff;
- ogm_buff_len = bat_priv->bat_v.ogm_buff_len;
- /* tt changes have to be committed before the tvlv data is
* appended as it may alter the tt tvlv container
*/
- batadv_tt_local_commit_changes(bat_priv);
- tvlv_len = batadv_tvlv_container_ogm_append(bat_priv, &ogm_buff,
&ogm_buff_len,
BATADV_OGM2_HLEN);
- bat_priv->bat_v.ogm_buff = ogm_buff;
- bat_priv->bat_v.ogm_buff_len = ogm_buff_len;
- skb = netdev_alloc_skb_ip_align(NULL, ETH_HLEN + ogm_buff_len);
- if (!skb)
goto out;
- skb_reserve(skb, ETH_HLEN);
- pkt_buff = skb_put(skb, ogm_buff_len);
- memcpy(pkt_buff, ogm_buff, ogm_buff_len);
- ogm2 = (struct batadv_ogm2_packet *)skb->data;
- ogm2->seqno = htonl(atomic_read(&bat_priv->bat_v.ogm_seqno));
- atomic_inc(&bat_priv->bat_v.ogm_seqno);
- ogm2->tvlv_len = htons(tvlv_len);
- /* broadcast on every interface */
- rcu_read_lock();
- list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) {
if (hard_iface->soft_iface != bat_priv->soft_iface)
continue;
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"Sending own OGM2 packet (originator %pM, seqno %u, metric %u, TTL %d) on interface %s [%pM]\n",
ogm2->orig, ntohl(ogm2->seqno), ntohl(ogm2->metric),
ogm2->ttl, hard_iface->net_dev->name,
hard_iface->net_dev->dev_addr);
batadv_v_ogm_send_to_if(skb, hard_iface);
- }
- rcu_read_unlock();
- consume_skb(skb);
- batadv_v_ogm_start_timer(bat_priv);
+out:
- return;
If you jump to out because netdev_alloc_skb_ip_align() failed, don't you want to start the timer again? I would assume it is a transient problem not being able to get memory for the skb, and the next time the timer goes off it might work?
- bat_priv->bat_v.ogm_buff_len = BATADV_OGM2_HLEN;
- ogm_buff = kmalloc(bat_priv->bat_v.ogm_buff_len, GFP_ATOMIC);
Maybe use kzalloc to ensure it is zero?
Andrew
On 11/02/14 18:12, Andrew Lunn wrote:
- batadv_v_ogm_start_timer(bat_priv);
+out:
- return;
If you jump to out because netdev_alloc_skb_ip_align() failed, don't you want to start the timer again? I would assume it is a transient problem not being able to get memory for the skb, and the next time the timer goes off it might work?
Now that I think about it I agree with you. If netdev_alloc_skb_ip_align() fails either: - the situation is permanent -> OOM soon - or the situation is transient -> we should try re-scheduling the task.
- bat_priv->bat_v.ogm_buff_len = BATADV_OGM2_HLEN;
- ogm_buff = kmalloc(bat_priv->bat_v.ogm_buff_len, GFP_ATOMIC);
Maybe use kzalloc to ensure it is zero?
Not really needed because each field is explicitly assigned later. But to be sure we sit on the safe side we can do that: in the end we are not in the fastpath here.
Cheers,
- bat_priv->bat_v.ogm_buff_len = BATADV_OGM2_HLEN;
- ogm_buff = kmalloc(bat_priv->bat_v.ogm_buff_len, GFP_ATOMIC);
Maybe use kzalloc to ensure it is zero?
Not really needed because each field is explicitly assigned later. But to be sure we sit on the safe side we can do that: in the end we are not in the fastpath here.
I know some of your structures have explicit align bytes. It would be good to ensure they are zero so that they could be used in the future without having a protocol version bump. So i think it is a good patterns to follow in general when not on the fast path.
Andrew
On 12/02/14 08:44, Andrew Lunn wrote:
- bat_priv->bat_v.ogm_buff_len = BATADV_OGM2_HLEN;
- ogm_buff = kmalloc(bat_priv->bat_v.ogm_buff_len, GFP_ATOMIC);
Maybe use kzalloc to ensure it is zero?
Not really needed because each field is explicitly assigned later. But to be sure we sit on the safe side we can do that: in the end we are not in the fastpath here.
I know some of your structures have explicit align bytes. It would be good to ensure they are zero so that they could be used in the future without having a protocol version bump. So i think it is a good patterns to follow in general when not on the fast path.
True. _I_ had this problem in the past already :-) I'll use kzalloc then!
Thanks!
Andrew
From: Antonio Quartulli antonio@open-mesh.com
Add the support for recognising new originators in the network and rebroadcast their OGMs.
Signed-off-by: Antonio Quartulli antonio@open-mesh.com --- bat_v_elp.c | 7 +- bat_v_ogm.c | 422 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 414 insertions(+), 15 deletions(-)
diff --git a/bat_v_elp.c b/bat_v_elp.c index 1c23951..f9a6bfe 100644 --- a/bat_v_elp.c +++ b/bat_v_elp.c @@ -63,7 +63,7 @@ void batadv_elp_neigh_node_free_ref(struct batadv_elp_neigh_node *neigh) */ static struct batadv_elp_neigh_node * batadv_v_elp_neigh_new(struct batadv_hard_iface *hard_iface, - uint8_t *neigh_addr, uint8_t *orig_addr, uint32_t seqno) + uint8_t *neigh_addr, uint8_t *orig_addr) { struct batadv_elp_neigh_node *neigh;
@@ -73,7 +73,6 @@ batadv_v_elp_neigh_new(struct batadv_hard_iface *hard_iface,
memcpy(neigh->orig, orig_addr, ETH_ALEN); memcpy(neigh->addr, neigh_addr, ETH_ALEN); - neigh->last_recv_seqno = seqno - 1; neigh->last_seen = jiffies; ewma_init(&neigh->metric, 1024, 8); /* recount initialised to 2 to simplify the caller function */ @@ -326,13 +325,13 @@ static void batadv_v_elp_neigh_update(struct batadv_priv *bat_priv, neigh = batadv_v_elp_neigh_get(if_incoming, neigh_addr); if (!neigh) { neigh = batadv_v_elp_neigh_new(if_incoming, neigh_addr, - elp_packet->orig, - ntohl(elp_packet->seqno)); + elp_packet->orig); if (!neigh) goto out; }
neigh->last_seen = jiffies; + neigh->last_recv_seqno = ntohl(elp_packet->seqno);
out: if (neigh) diff --git a/bat_v_ogm.c b/bat_v_ogm.c index f66dc02..4981d7c 100644 --- a/bat_v_ogm.c +++ b/bat_v_ogm.c @@ -21,6 +21,7 @@
#include "main.h"
+#include "bat_v_elp.h" #include "bat_v_ogm.h" #include "hard-interface.h" #include "originator.h" @@ -28,6 +29,78 @@ #include "send.h" #include "translation-table.h"
+static struct batadv_orig_node * +batadv_v_ogm_orig_get(struct batadv_priv *bat_priv, const uint8_t *addr) +{ + struct batadv_orig_node *orig_node; + int hash_added; + + orig_node = batadv_orig_hash_find(bat_priv, addr); + if (orig_node) + return orig_node; + + orig_node = batadv_orig_node_new(bat_priv, addr); + + hash_added = batadv_hash_add(bat_priv->orig_hash, batadv_compare_orig, + batadv_choose_orig, orig_node, + &orig_node->hash_entry); + if (hash_added != 0) { + batadv_orig_node_free_ref(orig_node); + batadv_orig_node_free_ref(orig_node); + orig_node = NULL; + } + + return orig_node; +} + +static struct batadv_neigh_node * +batadv_v_ogm_neigh_new(struct batadv_priv *bat_priv, + struct batadv_hard_iface *hard_iface, + uint8_t *addr, + struct batadv_orig_node *orig_node, + struct batadv_orig_node *orig_neigh) +{ + struct batadv_neigh_node *neigh_node, *tmp_neigh_node; + struct batadv_elp_neigh_node *elp_neigh; + + neigh_node = batadv_neigh_node_new(hard_iface, addr, orig_neigh); + if (!neigh_node) + return NULL; + + if (!atomic_inc_not_zero(&hard_iface->refcount)) { + kfree(neigh_node); + return NULL; + } + + elp_neigh = batadv_v_elp_neigh_get(hard_iface, addr); + if (!elp_neigh) { + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, + "No ELP object for this neighbor!\n"); + return NULL; + } + + neigh_node->bat_v.elp_neigh = elp_neigh; + + spin_lock_bh(&orig_node->neigh_list_lock); + tmp_neigh_node = batadv_neigh_node_get(orig_node, hard_iface, addr); + if (!tmp_neigh_node) { + hlist_add_head_rcu(&neigh_node->list, &orig_node->neigh_list); + } else { + kfree(neigh_node); + batadv_hardif_free_ref(hard_iface); + batadv_elp_neigh_node_free_ref(elp_neigh); + neigh_node = tmp_neigh_node; + } + spin_unlock_bh(&orig_node->neigh_list_lock); + + if (!tmp_neigh_node) + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, + "Creating new neighbor %pM for orig_node %pM on interface %s\n", + addr, orig_node->orig, hard_iface->net_dev->name); + + return neigh_node; +} + static void batadv_v_ogm_start_timer(struct batadv_priv *bat_priv) { unsigned long msecs; @@ -48,20 +121,15 @@ static void batadv_v_ogm_send_to_if(struct sk_buff *skb, struct batadv_hard_iface *hard_iface) { struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface); - struct sk_buff *tmp_skb;
if (hard_iface->if_status != BATADV_IF_ACTIVE) return;
- tmp_skb = skb_clone(skb, GFP_ATOMIC); - if (!tmp_skb) - return; - batadv_inc_counter(bat_priv, BATADV_CNT_MGMT_TX); batadv_add_counter(bat_priv, BATADV_CNT_MGMT_TX_BYTES, - tmp_skb->len + ETH_HLEN); + skb->len + ETH_HLEN);
- batadv_send_skb_packet(tmp_skb, hard_iface, batadv_broadcast_addr); + batadv_send_skb_packet(skb, hard_iface, batadv_broadcast_addr); }
static void batadv_v_ogm_send(struct work_struct *work) @@ -70,7 +138,7 @@ static void batadv_v_ogm_send(struct work_struct *work) struct batadv_priv_bat_v *bat_v; struct batadv_priv *bat_priv; struct batadv_ogm2_packet *ogm2; - struct sk_buff *skb; + struct sk_buff *skb, *tmp_skb; unsigned char *ogm_buff, *pkt_buff; int ogm_buff_len; uint16_t tvlv_len = 0; @@ -119,7 +187,12 @@ static void batadv_v_ogm_send(struct work_struct *work) ogm2->ttl, hard_iface->net_dev->name, hard_iface->net_dev->dev_addr);
- batadv_v_ogm_send_to_if(skb, hard_iface); + /* this skb gets consumed by batadv_v_ogm_send_to_if() */ + tmp_skb = skb_clone(skb, GFP_ATOMIC); + if (!tmp_skb) + break; + + batadv_v_ogm_send_to_if(tmp_skb, hard_iface); } rcu_read_unlock();
@@ -162,11 +235,259 @@ void batadv_v_ogm_primary_iface_set(struct batadv_hard_iface *hard_iface) rcu_read_unlock(); }
+static void +batadv_v_ogm_orig_update(struct batadv_priv *bat_priv, + struct batadv_orig_node *orig_node, + struct batadv_neigh_node *neigh_node, + const struct batadv_ogm2_packet *ogm2, + struct batadv_hard_iface *if_outgoing) +{ + struct batadv_neigh_ifinfo *router_ifinfo = NULL, *neigh_ifinfo = NULL; + struct batadv_neigh_node *router = NULL; + struct batadv_orig_ifinfo *orig_ifinfo; + + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, + "update_originator(): Searching and updating originator entry of received packet\n"); + + /* get the TVLV container and process it */ + batadv_tvlv_containers_process(bat_priv, true, orig_node, NULL, NULL, + (unsigned char *)(ogm2 + 1), + ntohs(ogm2->tvlv_len)); + + orig_node->last_seen = jiffies; + + orig_ifinfo = batadv_orig_ifinfo_new(orig_node, if_outgoing); + orig_ifinfo->last_real_seqno = ntohl(ogm2->seqno); + orig_ifinfo->last_ttl = ogm2->ttl; + batadv_orig_ifinfo_free_ref(orig_ifinfo); + + /* if this neighbor already is our next hop there is nothing + * to change + */ + router = batadv_orig_router_get(orig_node, if_outgoing); + if (router == neigh_node) + goto out; + + /* don't consider neighbours with worse metric */ + if (router) { + router_ifinfo = batadv_neigh_ifinfo_get(router, if_outgoing); + neigh_ifinfo = batadv_neigh_ifinfo_get(neigh_node, if_outgoing); + if (router_ifinfo && neigh_ifinfo && + router_ifinfo->bat_v.metric >= neigh_ifinfo->bat_v.metric) + goto out; + } + + batadv_update_route(bat_priv, orig_node, if_outgoing, neigh_node); + +out: + if (router_ifinfo) + batadv_neigh_ifinfo_free_ref(router_ifinfo); + if (neigh_ifinfo) + batadv_neigh_ifinfo_free_ref(neigh_ifinfo); + if (router) + batadv_neigh_node_free_ref(router); +} + +/** + * batadv_v_penalty - apply a penalty to the metric forwarded by the OGM + * @bat_priv: the bat priv with all the soft interface information + * @metric: the current metric value + * @link_metric: the metric of the last link traversed by the OGM + * @if_incoming: the interface where the OGM has been received + * + * Apply a penalty on the current metric value based on the characteristic of + * the interface where the OGM has been received. The return value is computed + * as follows: + * - metric * 50% if the incoming interface is wireless and the metric is not + * less than 10% of the link throughput + * - metric * (100 - hop_penalty)% otherwise + * + * Return the penalised metric value + */ +static uint32_t batadv_v_penalty(struct batadv_priv *bat_priv, + uint32_t metric, uint32_t link_metric, + struct batadv_hard_iface *if_incoming) +{ + int hop_penalty = atomic_read(&bat_priv->hop_penalty); + + /* proportion to use the same value used in batman iv (x * 128 / 256) */ + hop_penalty = hop_penalty * 100 / 255; + + if (batadv_is_wifi_netdev(if_incoming->net_dev) && + metric > link_metric / 10) + return metric / 2; + + return metric * (100 - hop_penalty) / 100; +} + +static void batadv_v_ogm_forward(struct batadv_priv *bat_priv, + const struct batadv_ogm2_packet *ogm2, + struct batadv_neigh_node *neigh_node, + struct batadv_hard_iface *if_incoming, + struct batadv_hard_iface *if_outgoing) +{ + struct batadv_ogm2_packet *ogm_new; + uint32_t link_metric, new_metric; + unsigned char *skb_buff; + struct sk_buff *skb; + uint16_t tvlv_len; + size_t packet_len; + + if (ogm2->ttl <= 1) { + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, "ttl exceeded\n"); + return; + } + + tvlv_len = ntohs(ogm2->tvlv_len); + + packet_len = BATADV_OGM2_HLEN + tvlv_len; + skb = netdev_alloc_skb_ip_align(if_outgoing->net_dev, + ETH_HLEN + packet_len); + if (!skb) + return; + + skb_reserve(skb, ETH_HLEN); + skb_buff = skb_put(skb, packet_len); + memcpy(skb_buff, ogm2, packet_len); + + /* apply hop penalty */ + ogm_new = (struct batadv_ogm2_packet *)skb_buff; + link_metric = ewma_read(&neigh_node->bat_v.elp_neigh->metric); + new_metric = batadv_v_penalty(bat_priv, ntohl(ogm_new->metric), + link_metric, if_incoming); + ogm_new->metric = htonl(new_metric); + ogm_new->ttl--; + + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, + "Forwarding OGM2 packet on %s: metric %u, ttl %u\n", + if_outgoing->net_dev->name, new_metric, ogm_new->ttl); + + batadv_v_ogm_send_to_if(skb, if_outgoing); +} + +/** + * batadv_v_ogm_process_per_outif - process a batman iv OGM for an outgoing if + * @skb: the skb containing the OGM + * @if_incoming: the interface where this packet was received + * @if_outgoing: the interface for which the packet should be considered + */ +static void +batadv_v_ogm_process_per_outif(struct batadv_priv *bat_priv, + const struct ethhdr *ethhdr, + const struct batadv_ogm2_packet *ogm2, + struct batadv_orig_node *orig_node, + struct batadv_neigh_node *neigh_node, + struct batadv_hard_iface *if_incoming, + struct batadv_hard_iface *if_outgoing) +{ + struct batadv_neigh_ifinfo *neigh_ifinfo = NULL, *router_ifinfo = NULL; + struct batadv_neigh_node *orig_neigh_router = NULL; + struct batadv_neigh_node *router = NULL, *router_router = NULL; + struct batadv_orig_node *orig_neigh_node; + struct batadv_orig_ifinfo *orig_ifinfo = NULL; + bool is_from_best_next_hop = false; + uint32_t new_metric; + int32_t seq_diff; + + orig_ifinfo = batadv_orig_ifinfo_new(orig_node, if_outgoing); + seq_diff = ntohl(ogm2->seqno) - orig_ifinfo->last_real_seqno; + if (!hlist_empty(&orig_node->neigh_list) && + batadv_window_protected(bat_priv, seq_diff, + &orig_ifinfo->batman_seqno_reset)) { + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, + "Drop packet: packet within window protection time from %pM\n", + ogm2->orig); + goto out; + } + + new_metric = ntohl(ogm2->metric); + neigh_ifinfo = batadv_neigh_ifinfo_new(neigh_node, if_outgoing); + if (seq_diff > 0 || new_metric > neigh_ifinfo->bat_v.metric) { + neigh_ifinfo->bat_v.metric = new_metric; + neigh_ifinfo->last_ttl = ogm2->ttl; + } + + router = batadv_orig_router_get(orig_node, if_outgoing); + if (router) { + router_ifinfo = batadv_neigh_ifinfo_get(router, if_outgoing); + if (router_ifinfo && (router_ifinfo->bat_v.metric != 0) && + (batadv_compare_eth(router->addr, ethhdr->h_source))) + is_from_best_next_hop = true; + } + + orig_ifinfo->last_real_seqno = ntohl(ogm2->seqno); + + /* if sender is a direct neighbor the sender mac equals + * originator mac + */ + if (router && router->orig_node == orig_node) + orig_neigh_node = orig_node; + else + orig_neigh_node = batadv_v_ogm_orig_get(bat_priv, + ethhdr->h_source); + + if (!orig_neigh_node) + goto out; + + orig_neigh_router = batadv_orig_router_get(orig_neigh_node, + if_outgoing); + + /* drop packet if sender is not a direct neighbor and if we + * don't route towards it + */ + if (router && router->orig_node != orig_node && + (!orig_neigh_router)) { + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, + "Drop packet: OGM via unknown neighbor!\n"); + goto out_neigh; + } + + /* if there is a router and the sequence number is the same as the last + * received from it, but the metric is worse, then drop the packet + */ + if (router && seq_diff == 0 && new_metric < router_ifinfo->bat_v.metric) + goto out_neigh; + + batadv_v_ogm_orig_update(bat_priv, orig_node, neigh_node, ogm2, + if_outgoing); + + /* strict rule: forward packets coming from the best next hop only */ + if (router && !is_from_best_next_hop) + goto out_neigh; + + /* only forward for specific interface, not for the default one. */ + if (if_outgoing != BATADV_IF_DEFAULT) + batadv_v_ogm_forward(bat_priv, ogm2, neigh_node, if_incoming, + if_outgoing); + +out_neigh: + if (orig_neigh_node != orig_node && orig_neigh_node) + batadv_orig_node_free_ref(orig_neigh_node); +out: + if (orig_ifinfo) + batadv_orig_ifinfo_free_ref(orig_ifinfo); + if (router) + batadv_neigh_node_free_ref(router); + if (router_ifinfo) + batadv_neigh_ifinfo_free_ref(router_ifinfo); + if (router_router) + batadv_neigh_node_free_ref(router_router); + if (orig_neigh_router) + batadv_neigh_node_free_ref(orig_neigh_router); + if (neigh_ifinfo) + batadv_neigh_ifinfo_free_ref(neigh_ifinfo); +} + int batadv_v_ogm_packet_recv(struct sk_buff *skb, struct batadv_hard_iface *if_incoming) { struct batadv_priv *bat_priv = netdev_priv(if_incoming->soft_iface); - struct ethhdr *ethhdr = (struct ethhdr *)skb_mac_header(skb); + struct batadv_orig_node *orig_node, *orig_neigh; + uint32_t ogm_metric, link_metric, new_metric; + struct batadv_neigh_node *neigh_node = NULL; + struct ethhdr *ethhdr = eth_hdr(skb); + struct batadv_hard_iface *hard_iface; struct batadv_ogm2_packet *ogm2; + int ret = NET_RX_DROP;
/* did we receive a OGM2 packet on an interface that does not have * B.A.T.M.A.N. V enabled ? @@ -189,8 +510,87 @@ int batadv_v_ogm_packet_recv(struct sk_buff *skb, batadv_add_counter(bat_priv, BATADV_CNT_MGMT_RX_BYTES, skb->len + ETH_HLEN);
+ ogm_metric = ntohl(ogm2->metric); + + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, + "Received OGM2 packet via NB: %pM, IF: %s [%pM] (from OG: %pM, seqno %u, metric %u, TTL %u, V %u, tvlv_len %u)\n", + ethhdr->h_source, if_incoming->net_dev->name, + if_incoming->net_dev->dev_addr, ogm2->orig, + ntohl(ogm2->seqno), ogm_metric, ogm2->ttl, + ogm2->version, ntohs(ogm2->tvlv_len)); + + /* if the metric is 0, immediately drop the packet. no need to create + * orig_node/neigh_node for a not usable route + */ + if (ogm_metric == 0) { + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, + "Drop packet: originator packet with metric equal 0\n"); + return NET_RX_DROP; + } + + orig_node = batadv_v_ogm_orig_get(bat_priv, ogm2->orig); + if (!orig_node) + return NET_RX_DROP; + + orig_neigh = batadv_v_ogm_orig_get(bat_priv, ethhdr->h_source); + if (!orig_neigh) + goto out; + + neigh_node = batadv_v_ogm_neigh_new(bat_priv, if_incoming, + ethhdr->h_source, orig_node, + orig_neigh); + if (!neigh_node) { + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, + "Could not create neigh node!\n"); + goto out; + } + + /* update the neigh_node information */ + neigh_node->last_seen = jiffies; + + /* update the received metric to match the node topology: if this node + * is the first hop traversed by the OGM, then the metric is substituted + * with the value of the traversed link. Otherwise the metric is + * computed as the minimum between the metric of the traversed link and + * the value contained in the OGM + */ + link_metric = ewma_read(&neigh_node->bat_v.elp_neigh->metric); + if (ogm_metric == BATADV_MAX_METRIC) + new_metric = link_metric; + else + new_metric = min_t(uint32_t, link_metric, ogm_metric); + ogm2->metric = htonl(new_metric); + + batadv_v_ogm_process_per_outif(bat_priv, ethhdr, ogm2, orig_node, + neigh_node, if_incoming, + BATADV_IF_DEFAULT); + + rcu_read_lock(); + list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) { + if (hard_iface->if_status != BATADV_IF_ACTIVE) + continue; + + if (hard_iface->soft_iface != bat_priv->soft_iface) + continue; + + batadv_v_ogm_process_per_outif(bat_priv, ethhdr, ogm2, + orig_node, neigh_node, + if_incoming, hard_iface); + } + rcu_read_unlock(); + + ret = NET_RX_SUCCESS; consume_skb(skb); - return NET_RX_SUCCESS; + +out: + if (orig_node) + batadv_orig_node_free_ref(orig_node); + if (orig_neigh) + batadv_orig_node_free_ref(orig_neigh); + if (neigh_node) + batadv_neigh_node_free_ref(neigh_node); + + return ret; }
int batadv_v_ogm_init(struct batadv_priv *bat_priv)
+static struct batadv_orig_node * +batadv_v_ogm_orig_get(struct batadv_priv *bat_priv, const uint8_t *addr) +{
- struct batadv_orig_node *orig_node;
- int hash_added;
- orig_node = batadv_orig_hash_find(bat_priv, addr);
- if (orig_node)
return orig_node;
- orig_node = batadv_orig_node_new(bat_priv, addr);
Can this fail and return NULL?
- hash_added = batadv_hash_add(bat_priv->orig_hash, batadv_compare_orig,
batadv_choose_orig, orig_node,
&orig_node->hash_entry);
- if (hash_added != 0) {
batadv_orig_node_free_ref(orig_node);
batadv_orig_node_free_ref(orig_node);
Looks odd. If it is correct, it should have a comment why it is correct.
Andrew
On 11/02/14 18:22, Andrew Lunn wrote:
+static struct batadv_orig_node * +batadv_v_ogm_orig_get(struct batadv_priv *bat_priv, const uint8_t *addr) +{
- struct batadv_orig_node *orig_node;
- int hash_added;
- orig_node = batadv_orig_hash_find(bat_priv, addr);
- if (orig_node)
return orig_node;
- orig_node = batadv_orig_node_new(bat_priv, addr);
Can this fail and return NULL?
Yes it can. Better to check.
- hash_added = batadv_hash_add(bat_priv->orig_hash, batadv_compare_orig,
batadv_choose_orig, orig_node,
&orig_node->hash_entry);
- if (hash_added != 0) {
batadv_orig_node_free_ref(orig_node);
batadv_orig_node_free_ref(orig_node);
Looks odd. If it is correct, it should have a comment why it is correct.
We have a similar problem in the current (B.A.T.M.A.N. IV) code too. The point is that orig_node->refcounter is initialised to 2, therefore if we want to free the object in this early phase we need to invoke the free_ref() twice.
By the way...I was just thinking that we could invoke kfree() directly here (the orig_node is not referenced in any other context since it has been just allocated).
Cheers,
From: Antonio Quartulli antonio@open-mesh.com
If a neighbor is not forwarding OGMs since OGM_SEQ_RANGE sequence numbers then it can be considered obsolete and removed from the potential routers list. This may lead to a route switch.
Signed-off-by: Antonio Quartulli antonio@open-mesh.com --- bat_v_ogm.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- main.h | 3 ++ originator.c | 63 +++++++++++++++++++++++------------------ originator.h | 2 ++ types.h | 2 ++ 5 files changed, 131 insertions(+), 31 deletions(-)
diff --git a/bat_v_ogm.c b/bat_v_ogm.c index 4981d7c..5876164 100644 --- a/bat_v_ogm.c +++ b/bat_v_ogm.c @@ -79,8 +79,6 @@ batadv_v_ogm_neigh_new(struct batadv_priv *bat_priv, return NULL; }
- neigh_node->bat_v.elp_neigh = elp_neigh; - spin_lock_bh(&orig_node->neigh_list_lock); tmp_neigh_node = batadv_neigh_node_get(orig_node, hard_iface, addr); if (!tmp_neigh_node) { @@ -98,6 +96,8 @@ batadv_v_ogm_neigh_new(struct batadv_priv *bat_priv, "Creating new neighbor %pM for orig_node %pM on interface %s\n", addr, orig_node->orig, hard_iface->net_dev->name);
+ neigh_node->bat_v.elp_neigh = elp_neigh; + return neigh_node; }
@@ -235,6 +235,86 @@ void batadv_v_ogm_primary_iface_set(struct batadv_hard_iface *hard_iface) rcu_read_unlock(); }
+static bool batadv_ogm_deselect_router(struct batadv_priv *bat_priv, + struct batadv_orig_node *orig_node, + struct batadv_neigh_node *neigh) +{ + struct batadv_hard_iface *if_outgoing; + struct batadv_neigh_node *router; + bool removed = false; + + router = batadv_orig_router_get(orig_node, BATADV_IF_DEFAULT); + if (router == neigh) + batadv_update_route(bat_priv, orig_node, BATADV_IF_DEFAULT, + NULL); + if (router) + batadv_neigh_node_free_ref(router); + + rcu_read_lock(); + list_for_each_entry_rcu(if_outgoing, &batadv_hardif_list, list) { + if (if_outgoing->if_status != BATADV_IF_ACTIVE) + continue; + + if (if_outgoing->soft_iface != bat_priv->soft_iface) + continue; + + router = batadv_orig_router_get(orig_node, if_outgoing); + /* if this is the current router, unset it */ + if (router == neigh) { + batadv_update_route(bat_priv, orig_node, if_outgoing, + NULL); + removed = true; + } + if (router) + batadv_neigh_node_free_ref(router); + } + rcu_read_unlock(); + + return removed; +} + +/** + * batadv_v_ogm_purge_neighs - purge obsolete potential routers + * @bat_priv: + * @orig_node: + * @seqno: + * + * Purge routers from which this node did not receive at least + * BATADV_OGM_SEQ_RANGE OGMs + */ +static void batadv_v_ogm_purge_neighs(struct batadv_priv *bat_priv, + struct batadv_orig_node *orig_node, + uint32_t seqno) +{ + struct batadv_neigh_node *neigh; + struct hlist_node *node_tmp; + int32_t diff; + + rcu_read_lock(); + spin_lock_bh(&orig_node->neigh_list_lock); + hlist_for_each_entry_safe(neigh, node_tmp, &orig_node->neigh_list, + list) { + diff = seqno - neigh->bat_v.last_recv_seqno; + if (diff < BATADV_OGM_SEQ_RANGE) + continue; + + hlist_del_rcu(&neigh->list); + batadv_neigh_node_free_ref(neigh); + } + spin_unlock_bh(&orig_node->neigh_list_lock); + + /* check if this neighbour is a router for any outgoing + * interface and possibly deselect it + */ + if (batadv_ogm_deselect_router(bat_priv, orig_node, neigh)) + goto out; + + batadv_router_selection(bat_priv, orig_node); +out: + rcu_read_unlock(); + return; +} + static void batadv_v_ogm_orig_update(struct batadv_priv *bat_priv, struct batadv_orig_node *orig_node, @@ -262,7 +342,7 @@ batadv_v_ogm_orig_update(struct batadv_priv *bat_priv, batadv_orig_ifinfo_free_ref(orig_ifinfo);
/* if this neighbor already is our next hop there is nothing - * to change + * to change. The router may have been changed by the purge routine */ router = batadv_orig_router_get(orig_node, if_outgoing); if (router == neigh_node) @@ -547,6 +627,7 @@ int batadv_v_ogm_packet_recv(struct sk_buff *skb,
/* update the neigh_node information */ neigh_node->last_seen = jiffies; + neigh_node->bat_v.last_recv_seqno = ntohl(ogm2->seqno);
/* update the received metric to match the node topology: if this node * is the first hop traversed by the OGM, then the metric is substituted @@ -579,6 +660,11 @@ int batadv_v_ogm_packet_recv(struct sk_buff *skb, } rcu_read_unlock();
+ /* purge potential neighs not forwarding OGMs anymore. + * This may lead to a router change + */ + batadv_v_ogm_purge_neighs(bat_priv, orig_node, ntohl(ogm2->seqno)); + ret = NET_RX_SUCCESS; consume_skb(skb);
diff --git a/main.h b/main.h index 4badd01..47fb9ed 100644 --- a/main.h +++ b/main.h @@ -62,6 +62,9 @@ /* number of OGMs sent with the last tt diff */ #define BATADV_TT_OGM_APPEND_MAX 3
+/* number of missing OGMs to wait before considering a router unusable */ +#define BATADV_OGM_SEQ_RANGE 5 + /* Time in which a client can roam at most ROAMING_MAX_COUNT times in * milliseconds */ diff --git a/originator.c b/originator.c index 9f2da37..447f417 100644 --- a/originator.c +++ b/originator.c @@ -837,6 +837,40 @@ batadv_find_best_neighbor(struct batadv_priv *bat_priv, return best; }
+void batadv_router_selection(struct batadv_priv *bat_priv, + struct batadv_orig_node *orig_node) +{ + struct batadv_neigh_node *best_neigh_node; + struct batadv_hard_iface *hard_iface; + + /* First for the default routing table ... */ + best_neigh_node = batadv_find_best_neighbor(bat_priv, orig_node, + BATADV_IF_DEFAULT); + batadv_update_route(bat_priv, orig_node, BATADV_IF_DEFAULT, + best_neigh_node); + if (best_neigh_node) + batadv_neigh_node_free_ref(best_neigh_node); + + /* ... then for all other outgoing interfaces. */ + rcu_read_lock(); + list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) { + if (hard_iface->if_status != BATADV_IF_ACTIVE) + continue; + + if (hard_iface->soft_iface != bat_priv->soft_iface) + continue; + + best_neigh_node = batadv_find_best_neighbor(bat_priv, + orig_node, + hard_iface); + batadv_update_route(bat_priv, orig_node, hard_iface, + best_neigh_node); + if (best_neigh_node) + batadv_neigh_node_free_ref(best_neigh_node); + } + rcu_read_unlock(); +} + /** * batadv_purge_orig_node - purges obsolete information from an orig_node * @bat_priv: the bat priv with all the soft interface information @@ -850,8 +884,6 @@ batadv_find_best_neighbor(struct batadv_priv *bat_priv, static bool batadv_purge_orig_node(struct batadv_priv *bat_priv, struct batadv_orig_node *orig_node) { - struct batadv_neigh_node *best_neigh_node; - struct batadv_hard_iface *hard_iface; bool changed;
if (batadv_has_timed_out(orig_node->last_seen, @@ -868,32 +900,7 @@ static bool batadv_purge_orig_node(struct batadv_priv *bat_priv, if (!changed) return false;
- /* first for NULL ... */ - best_neigh_node = batadv_find_best_neighbor(bat_priv, orig_node, - BATADV_IF_DEFAULT); - batadv_update_route(bat_priv, orig_node, BATADV_IF_DEFAULT, - best_neigh_node); - if (best_neigh_node) - batadv_neigh_node_free_ref(best_neigh_node); - - /* ... then for all other interfaces. */ - rcu_read_lock(); - list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) { - if (hard_iface->if_status != BATADV_IF_ACTIVE) - continue; - - if (hard_iface->soft_iface != bat_priv->soft_iface) - continue; - - best_neigh_node = batadv_find_best_neighbor(bat_priv, - orig_node, - hard_iface); - batadv_update_route(bat_priv, orig_node, hard_iface, - best_neigh_node); - if (best_neigh_node) - batadv_neigh_node_free_ref(best_neigh_node); - } - rcu_read_unlock(); + batadv_router_selection(bat_priv, orig_node);
return false; } diff --git a/originator.h b/originator.h index db3a9ed..62e8e68 100644 --- a/originator.h +++ b/originator.h @@ -55,6 +55,8 @@ struct batadv_orig_ifinfo * batadv_orig_ifinfo_new(struct batadv_orig_node *orig_node, struct batadv_hard_iface *if_outgoing); void batadv_orig_ifinfo_free_ref(struct batadv_orig_ifinfo *orig_ifinfo); +void batadv_router_selection(struct batadv_priv *bat_priv, + struct batadv_orig_node *orig_node);
int batadv_orig_seq_print_text(struct seq_file *seq, void *offset); int batadv_orig_hardif_seq_print_text(struct seq_file *seq, void *offset); diff --git a/types.h b/types.h index e9149d1..28f2b85 100644 --- a/types.h +++ b/types.h @@ -358,9 +358,11 @@ struct batadv_elp_neigh_node { /** * batadv_neigh_node_bat_v - B.A.T.M.A.N. V private neighbor information * @elp_neigh: ELP private neighbour data + * @last_recv_seqno: last OGM seqno received through this neighbour */ struct batadv_neigh_node_bat_v { struct batadv_elp_neigh_node *elp_neigh; + uint32_t last_recv_seqno; };
/**
From: Antonio Quartulli antonio@open-mesh.com
Some mesh attributes are behind substructs in the batadv_priv object and for this reason the name cannot be used anymore to refer to them.
This patch allows to specify the variable name where the attribute is stored inside batadv_priv instead of using the name
Signed-off-by: Antonio Quartulli antonio@open-mesh.com --- sysfs.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-)
diff --git a/sysfs.c b/sysfs.c index 1d6653c..9139319 100644 --- a/sysfs.c +++ b/sysfs.c @@ -148,7 +148,7 @@ ssize_t batadv_show_##_name(struct kobject *kobj, \ batadv_store_##_name)
-#define BATADV_ATTR_SIF_STORE_UINT(_name, _min, _max, _post_func) \ +#define BATADV_ATTR_SIF_STORE_UINT(_name, _var, _min, _max, _post_func) \ ssize_t batadv_store_##_name(struct kobject *kobj, \ struct attribute *attr, char *buff, \ size_t count) \ @@ -157,23 +157,32 @@ ssize_t batadv_store_##_name(struct kobject *kobj, \ struct batadv_priv *bat_priv = netdev_priv(net_dev); \ return __batadv_store_uint_attr(buff, count, _min, _max, \ _post_func, attr, \ - &bat_priv->_name, net_dev); \ + &bat_priv->_var, net_dev); \ }
-#define BATADV_ATTR_SIF_SHOW_UINT(_name) \ +#define BATADV_ATTR_SIF_SHOW_UINT(_name, _var) \ ssize_t batadv_show_##_name(struct kobject *kobj, \ struct attribute *attr, char *buff) \ { \ struct batadv_priv *bat_priv = batadv_kobj_to_batpriv(kobj); \ - return sprintf(buff, "%i\n", atomic_read(&bat_priv->_name)); \ + return sprintf(buff, "%i\n", atomic_read(&bat_priv->_var)); \ } \
/* Use this, if you are going to set [name] in the soft-interface * (bat_priv) to an unsigned integer value */ #define BATADV_ATTR_SIF_UINT(_name, _mode, _min, _max, _post_func) \ - static BATADV_ATTR_SIF_STORE_UINT(_name, _min, _max, _post_func)\ - static BATADV_ATTR_SIF_SHOW_UINT(_name) \ + static BATADV_ATTR_SIF_STORE_UINT(_name, _name, _min, _max, _post_func)\ + static BATADV_ATTR_SIF_SHOW_UINT(_name, _name) \ + static BATADV_ATTR(_name, _mode, batadv_show_##_name, \ + batadv_store_##_name) + +/* Same as BATADV_ATTR_SIF_UINT, but allow to specify the variable name as + * second parameter so that it does not need to match the attribute name + */ +#define BATADV_ATTR_SIF_UINT2(_name, _var, _mode, _min, _max, _post_func)\ + static BATADV_ATTR_SIF_STORE_UINT(_name, _var, _min, _max, _post_func)\ + static BATADV_ATTR_SIF_SHOW_UINT(_name, _var) \ static BATADV_ATTR(_name, _mode, batadv_show_##_name, \ batadv_store_##_name)
From: Antonio Quartulli antonio@open-mesh.com
This attribute is exported to the user which can manually select its value. It is the throughput value to be used by default when batman-adv is trying to compute the link throughput towards a neighbour using this interface.
If the value is set to 0 then batman-adv will try to detect the throughput by itself.
Signed-off-by: Antonio Quartulli antonio@open-mesh.com --- bat_v.c | 3 +++ sysfs.c | 3 +++ types.h | 2 ++ 3 files changed, 8 insertions(+)
diff --git a/bat_v.c b/bat_v.c index b4214c5..d5457b3 100644 --- a/bat_v.c +++ b/bat_v.c @@ -34,6 +34,9 @@ static int batadv_v_iface_enable(struct batadv_hard_iface *hard_iface) if (ret < 0) batadv_v_elp_iface_disable(hard_iface);
+ /* disable user customised throughput by default */ + atomic_set(&hard_iface->bat_v.user_throughput, 0); + return ret; }
diff --git a/sysfs.c b/sysfs.c index 9139319..8bbbdfa 100644 --- a/sysfs.c +++ b/sysfs.c @@ -889,6 +889,8 @@ static BATADV_ATTR(iface_status, S_IRUGO, batadv_show_iface_status, NULL); #ifdef CONFIG_BATMAN_ADV_BATMAN_V BATADV_ATTR_HIF_UINT(elp_interval, bat_v.elp_interval, S_IRUGO | S_IWUSR, 2 * BATADV_JITTER, INT_MAX, NULL); +BATADV_ATTR_HIF_UINT(throughput, bat_v.user_throughput, S_IRUGO | S_IWUSR, + 0, UINT_MAX, NULL); #endif
static struct batadv_attribute *batadv_batman_attrs[] = { @@ -896,6 +898,7 @@ static struct batadv_attribute *batadv_batman_attrs[] = { &batadv_attr_iface_status, #ifdef CONFIG_BATMAN_ADV_BATMAN_V &batadv_attr_elp_interval, + &batadv_attr_throughput, #endif NULL, }; diff --git a/types.h b/types.h index 28f2b85..64316b9 100644 --- a/types.h +++ b/types.h @@ -78,6 +78,7 @@ struct batadv_hard_iface_bat_iv { * @num_neighbours: number of neighbours in the neigh_list * @elp_skb: base skb containing the ELP message to send * @elp_wq: workqueue used to schedule ELP transmissions + * @user_throughput: user specified throughput */ struct batadv_hard_iface_bat_v { atomic_t elp_interval; @@ -87,6 +88,7 @@ struct batadv_hard_iface_bat_v { atomic_t num_neighbors; struct sk_buff *elp_skb; struct delayed_work elp_wq; + atomic_t user_throughput; };
/**
On Tue, Feb 11, 2014 at 01:48:11PM +0100, Antonio Quartulli wrote:
From: Antonio Quartulli antonio@open-mesh.com
This attribute is exported to the user which can manually select its value. It is the throughput value to be used by default when batman-adv is trying to compute the link throughput towards a neighbour using this interface.
If the value is set to 0 then batman-adv will try to detect the throughput by itself.
sysfs documentation is missing, but i guess you know what.
Andrew
From: Antonio Quartulli antonio@open-mesh.com
when batman-adv is asked to estimate/compute the throughput of an interface, but it fails for whatever reason, then the value in this attribute is used
Signed-off-by: Antonio Quartulli antonio@open-mesh.com --- bat_v.c | 3 +++ main.h | 7 +++++++ sysfs.c | 7 +++++++ types.h | 1 + 4 files changed, 18 insertions(+)
diff --git a/bat_v.c b/bat_v.c index d5457b3..3aa42ec 100644 --- a/bat_v.c +++ b/bat_v.c @@ -75,6 +75,9 @@ static struct batadv_algo_ops batadv_batman_v __read_mostly = {
int batadv_v_mesh_init(struct batadv_priv *bat_priv) { + atomic_set(&bat_priv->bat_v.base_throughput, + BATADV_DEFAULT_BASE_THROUGHPUT); + return batadv_v_ogm_init(bat_priv); }
diff --git a/main.h b/main.h index 47fb9ed..5699c9f 100644 --- a/main.h +++ b/main.h @@ -33,6 +33,13 @@ #define BATADV_MAX_METRIC 0xFFFFFFFF #define BATADV_JITTER 20
+/** + * BATADV_DEFAULT_BASE_THROUGHPUT - default value used as throughput for + * hard_ifaces for which it is not possible to measure/estimate the real one. + * Value is expressed in Mbps/10 + */ +#define BATADV_DEFAULT_BASE_THROUGHPUT 10 + /* Time To Live of broadcast messages */ #define BATADV_TTL 50
diff --git a/sysfs.c b/sysfs.c index 8bbbdfa..a670eeb 100644 --- a/sysfs.c +++ b/sysfs.c @@ -606,6 +606,10 @@ BATADV_ATTR_SIF_BOOL(network_coding, S_IRUGO | S_IWUSR, #endif static BATADV_ATTR(isolation_mark, S_IRUGO | S_IWUSR, batadv_show_isolation_mark, batadv_store_isolation_mark); +#ifdef CONFIG_BATMAN_ADV_BATMAN_V +BATADV_ATTR_SIF_UINT2(base_throughput, bat_v.base_throughput, S_IRUGO | S_IWUSR, + 1, UINT_MAX, NULL); +#endif
static struct batadv_attribute *batadv_mesh_attrs[] = { &batadv_attr_aggregated_ogms, @@ -630,6 +634,9 @@ static struct batadv_attribute *batadv_mesh_attrs[] = { &batadv_attr_network_coding, #endif &batadv_attr_isolation_mark, +#ifdef CONFIG_BATMAN_ADV_BATMAN_V + &batadv_attr_base_throughput, +#endif NULL, };
diff --git a/types.h b/types.h index 64316b9..22d621e 100644 --- a/types.h +++ b/types.h @@ -741,6 +741,7 @@ struct batadv_priv_bat_v { int ogm_buff_len; struct delayed_work ogm_wq; atomic_t ogm_seqno; + atomic_t base_throughput; };
/**
On Tue, Feb 11, 2014 at 01:48:12PM +0100, Antonio Quartulli wrote:
From: Antonio Quartulli antonio@open-mesh.com
when batman-adv is asked to estimate/compute the throughput of an interface, but it fails for whatever reason, then the value in this attribute is used
Signed-off-by: Antonio Quartulli antonio@open-mesh.com
bat_v.c | 3 +++ main.h | 7 +++++++ sysfs.c | 7 +++++++ types.h | 1 + 4 files changed, 18 insertions(+)
diff --git a/bat_v.c b/bat_v.c index d5457b3..3aa42ec 100644 --- a/bat_v.c +++ b/bat_v.c @@ -75,6 +75,9 @@ static struct batadv_algo_ops batadv_batman_v __read_mostly = {
int batadv_v_mesh_init(struct batadv_priv *bat_priv) {
- atomic_set(&bat_priv->bat_v.base_throughput,
BATADV_DEFAULT_BASE_THROUGHPUT);
- return batadv_v_ogm_init(bat_priv);
}
diff --git a/main.h b/main.h index 47fb9ed..5699c9f 100644 --- a/main.h +++ b/main.h @@ -33,6 +33,13 @@ #define BATADV_MAX_METRIC 0xFFFFFFFF #define BATADV_JITTER 20
+/**
- BATADV_DEFAULT_BASE_THROUGHPUT - default value used as throughput for
- hard_ifaces for which it is not possible to measure/estimate the real one.
- Value is expressed in Mbps/10
Mbps/10 is a rather odd unit. How about just Kbps?
Andrew
On 12/02/14 09:40, Andrew Lunn wrote:
On Tue, Feb 11, 2014 at 01:48:12PM +0100, Antonio Quartulli wrote:
From: Antonio Quartulli antonio@open-mesh.com
when batman-adv is asked to estimate/compute the throughput of an interface, but it fails for whatever reason, then the value in this attribute is used
Signed-off-by: Antonio Quartulli antonio@open-mesh.com
bat_v.c | 3 +++ main.h | 7 +++++++ sysfs.c | 7 +++++++ types.h | 1 + 4 files changed, 18 insertions(+)
diff --git a/bat_v.c b/bat_v.c index d5457b3..3aa42ec 100644 --- a/bat_v.c +++ b/bat_v.c @@ -75,6 +75,9 @@ static struct batadv_algo_ops batadv_batman_v __read_mostly = {
int batadv_v_mesh_init(struct batadv_priv *bat_priv) {
- atomic_set(&bat_priv->bat_v.base_throughput,
BATADV_DEFAULT_BASE_THROUGHPUT);
- return batadv_v_ogm_init(bat_priv);
}
diff --git a/main.h b/main.h index 47fb9ed..5699c9f 100644 --- a/main.h +++ b/main.h @@ -33,6 +33,13 @@ #define BATADV_MAX_METRIC 0xFFFFFFFF #define BATADV_JITTER 20
+/**
- BATADV_DEFAULT_BASE_THROUGHPUT - default value used as throughput for
- hard_ifaces for which it is not possible to measure/estimate the real one.
- Value is expressed in Mbps/10
Mbps/10 is a rather odd unit. How about just Kbps?
This is the unit used in cfg/mac80211 because most of the rates are either in the form of XX.0Mbps of XX.YMbps (so just one decimal number needs to be saved). Moreover I thought that having an higher precision is not very useful because the routing decision cannot be taken on such a fine grained value. Actually the originator table prints the metric in Mbps (XX.YMbps).
Do you think we should really go for the Kbps? this means losing one order of magnitude on the highest value we can store (yeah, it's large enough right now..but in the future who knows!)
Cheers,
On Wed, Feb 12, 2014 at 01:20:17PM +0100, Antonio Quartulli wrote:
On 12/02/14 09:40, Andrew Lunn wrote:
On Tue, Feb 11, 2014 at 01:48:12PM +0100, Antonio Quartulli wrote:
From: Antonio Quartulli antonio@open-mesh.com
when batman-adv is asked to estimate/compute the throughput of an interface, but it fails for whatever reason, then the value in this attribute is used
Signed-off-by: Antonio Quartulli antonio@open-mesh.com
bat_v.c | 3 +++ main.h | 7 +++++++ sysfs.c | 7 +++++++ types.h | 1 + 4 files changed, 18 insertions(+)
diff --git a/bat_v.c b/bat_v.c index d5457b3..3aa42ec 100644 --- a/bat_v.c +++ b/bat_v.c @@ -75,6 +75,9 @@ static struct batadv_algo_ops batadv_batman_v __read_mostly = {
int batadv_v_mesh_init(struct batadv_priv *bat_priv) {
- atomic_set(&bat_priv->bat_v.base_throughput,
BATADV_DEFAULT_BASE_THROUGHPUT);
- return batadv_v_ogm_init(bat_priv);
}
diff --git a/main.h b/main.h index 47fb9ed..5699c9f 100644 --- a/main.h +++ b/main.h @@ -33,6 +33,13 @@ #define BATADV_MAX_METRIC 0xFFFFFFFF #define BATADV_JITTER 20
+/**
- BATADV_DEFAULT_BASE_THROUGHPUT - default value used as throughput for
- hard_ifaces for which it is not possible to measure/estimate the real one.
- Value is expressed in Mbps/10
Mbps/10 is a rather odd unit. How about just Kbps?
This is the unit used in cfg/mac80211 because most of the rates are either in the form of XX.0Mbps of XX.YMbps (so just one decimal number needs to be saved). Moreover I thought that having an higher precision is not very useful because the routing decision cannot be taken on such a fine grained value. Actually the originator table prints the metric in Mbps (XX.YMbps).
It is not precision which i think is important, it is having a unit a user expects. Networking people work in Kbps, or MBps. If it is printed in the originator table as XX.YMBps, then i think this sysfs file also needs to be XX.YMBps. That does however make your parsing more complex, since it is no longer an int. I suppose there is the use case of an ADSL link, with bandwidth of say 2Mbps down, 256Kbps up, and then you do need the decimal point in XX.YMBps.
Andrew
On 13/02/14 10:36, Andrew Lunn wrote:
+/**
- BATADV_DEFAULT_BASE_THROUGHPUT - default value used as throughput for
- hard_ifaces for which it is not possible to measure/estimate the real one.
- Value is expressed in Mbps/10
Mbps/10 is a rather odd unit. How about just Kbps?
This is the unit used in cfg/mac80211 because most of the rates are either in the form of XX.0Mbps of XX.YMbps (so just one decimal number needs to be saved). Moreover I thought that having an higher precision is not very useful because the routing decision cannot be taken on such a fine grained value. Actually the originator table prints the metric in Mbps (XX.YMbps).
It is not precision which i think is important, it is having a unit a user expects. Networking people work in Kbps, or MBps. If it is printed in the originator table as XX.YMBps, then i think this sysfs file also needs to be XX.YMBps. That does however make your parsing more complex, since it is no longer an int. I suppose there is the use case of an ADSL link, with bandwidth of say 2Mbps down, 256Kbps up, and then you do need the decimal point in XX.YMBps.
I just checked what we do in the "improved GW bandwidth advertisement" introduced not so much time ago:
- we store the bandwidth value in Mbps/10 - we print it in the form XX.YMbps + but we allow the user to specify the unit when entering the value in the sysfs interface: "xxxMbps" or "yyyKbps".
This way the user can pick the favourite unit, but of course the value is truncated (in the case of Kbps) to fit the internal representation.
What do you think about this approach? I also think we have to keep the two consistent with each other (it would allow me to re-use the same parsing function).
Cheers,
I just checked what we do in the "improved GW bandwidth advertisement" introduced not so much time ago:
- we store the bandwidth value in Mbps/10
- we print it in the form XX.YMbps
- but we allow the user to specify the unit when entering the value in
the sysfs interface: "xxxMbps" or "yyyKbps".
This way the user can pick the favourite unit, but of course the value is truncated (in the case of Kbps) to fit the internal representation.
What do you think about this approach?
I think that is good.
Thanks Andrew
From: Antonio Quartulli antonio@open-mesh.com
This timestamp registers the last time a unicast packet was sent to a given neighbour
Signed-off-by: Antonio Quartulli antonio@open-mesh.com --- bat_iv_ogm.c | 2 +- bat_v_elp.c | 2 +- bat_v_ogm.c | 2 +- distributed-arp-table.c | 4 +--- fragmentation.c | 8 +++----- icmp_socket.c | 2 +- network-coding.c | 20 +++++++++----------- send.c | 46 +++++++++++++++++++++++++++++++++++++++------- send.h | 10 +++++++--- types.h | 2 ++ 10 files changed, 65 insertions(+), 33 deletions(-)
diff --git a/bat_iv_ogm.c b/bat_iv_ogm.c index f92253a..e2069c9 100644 --- a/bat_iv_ogm.c +++ b/bat_iv_ogm.c @@ -469,7 +469,7 @@ static void batadv_iv_ogm_send_to_if(struct batadv_forw_packet *forw_packet, batadv_inc_counter(bat_priv, BATADV_CNT_MGMT_TX); batadv_add_counter(bat_priv, BATADV_CNT_MGMT_TX_BYTES, skb->len + ETH_HLEN); - batadv_send_skb_packet(skb, hard_iface, batadv_broadcast_addr); + batadv_send_broadcast_skb(skb, hard_iface); } }
diff --git a/bat_v_elp.c b/bat_v_elp.c index f9a6bfe..4185d8c 100644 --- a/bat_v_elp.c +++ b/bat_v_elp.c @@ -200,7 +200,7 @@ static void batadv_v_elp_send_outstanding(struct work_struct *work) hard_iface->net_dev->name, atomic_read(&hard_iface->bat_v.elp_seqno));
- batadv_send_skb_packet(skb, hard_iface, batadv_broadcast_addr); + batadv_send_broadcast_skb(skb, hard_iface);
atomic_inc(&hard_iface->bat_v.elp_seqno);
diff --git a/bat_v_ogm.c b/bat_v_ogm.c index 5876164..fa3d1a1 100644 --- a/bat_v_ogm.c +++ b/bat_v_ogm.c @@ -129,7 +129,7 @@ static void batadv_v_ogm_send_to_if(struct sk_buff *skb, batadv_add_counter(bat_priv, BATADV_CNT_MGMT_TX_BYTES, skb->len + ETH_HLEN);
- batadv_send_skb_packet(skb, hard_iface, batadv_broadcast_addr); + batadv_send_broadcast_skb(skb, hard_iface); }
static void batadv_v_ogm_send(struct work_struct *work) diff --git a/distributed-arp-table.c b/distributed-arp-table.c index 78803b8..bc37033 100644 --- a/distributed-arp-table.c +++ b/distributed-arp-table.c @@ -602,9 +602,7 @@ static bool batadv_dat_send_data(struct batadv_priv *bat_priv, goto free_neigh; }
- send_status = batadv_send_skb_packet(tmp_skb, - neigh_node->if_incoming, - neigh_node->addr); + send_status = batadv_send_unicast_skb(tmp_skb, neigh_node); if (send_status == NET_XMIT_SUCCESS) { /* count the sent packet */ switch (packet_subtype) { diff --git a/fragmentation.c b/fragmentation.c index bcc4bea..12811e0 100644 --- a/fragmentation.c +++ b/fragmentation.c @@ -354,8 +354,7 @@ bool batadv_frag_skb_fwd(struct sk_buff *skb, skb->len + ETH_HLEN);
packet->ttl--; - batadv_send_skb_packet(skb, neigh_node->if_incoming, - neigh_node->addr); + batadv_send_unicast_skb(skb, neigh_node); ret = true; }
@@ -461,8 +460,7 @@ bool batadv_frag_send_packet(struct sk_buff *skb, batadv_inc_counter(bat_priv, BATADV_CNT_FRAG_TX); batadv_add_counter(bat_priv, BATADV_CNT_FRAG_TX_BYTES, skb_fragment->len + ETH_HLEN); - batadv_send_skb_packet(skb_fragment, neigh_node->if_incoming, - neigh_node->addr); + batadv_send_unicast_skb(skb_fragment, neigh_node); frag_header.no++;
/* The initial check in this function should cover this case */ @@ -481,7 +479,7 @@ bool batadv_frag_send_packet(struct sk_buff *skb, batadv_inc_counter(bat_priv, BATADV_CNT_FRAG_TX); batadv_add_counter(bat_priv, BATADV_CNT_FRAG_TX_BYTES, skb->len + ETH_HLEN); - batadv_send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr); + batadv_send_unicast_skb(skb, neigh_node);
return true; out_err: diff --git a/icmp_socket.c b/icmp_socket.c index bf07dfd..4734e02 100644 --- a/icmp_socket.c +++ b/icmp_socket.c @@ -253,7 +253,7 @@ static ssize_t batadv_socket_write(struct file *file, const char __user *buff,
ether_addr_copy(icmp_header->orig, primary_if->net_dev->dev_addr);
- batadv_send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr); + batadv_send_unicast_skb(skb, neigh_node); goto out;
dst_unreach: diff --git a/network-coding.c b/network-coding.c index a9546fe..bbb4ac0 100644 --- a/network-coding.c +++ b/network-coding.c @@ -535,9 +535,7 @@ batadv_nc_hash_find(struct batadv_hashtable *hash, */ static void batadv_nc_send_packet(struct batadv_nc_packet *nc_packet) { - batadv_send_skb_packet(nc_packet->skb, - nc_packet->neigh_node->if_incoming, - nc_packet->nc_path->next_hop); + batadv_send_unicast_skb(nc_packet->skb, nc_packet->neigh_node); nc_packet->skb = NULL; batadv_nc_packet_free(nc_packet); } @@ -1018,11 +1016,11 @@ static bool batadv_nc_code_packets(struct batadv_priv *bat_priv, struct batadv_unicast_packet *packet1; struct batadv_unicast_packet *packet2; struct batadv_coded_packet *coded_packet; - struct batadv_neigh_node *neigh_tmp, *router_neigh; - struct batadv_neigh_node *router_coding = NULL; + struct batadv_neigh_node *neigh_tmp, *router_neigh, *first_dest; + struct batadv_neigh_node *router_coding = NULL, *second_dest; struct batadv_neigh_ifinfo *router_neigh_ifinfo = NULL; struct batadv_neigh_ifinfo *router_coding_ifinfo = NULL; - uint8_t *first_source, *first_dest, *second_source, *second_dest; + uint8_t *first_source, *second_source; __be32 packet_id1, packet_id2; size_t count; bool res = false; @@ -1065,9 +1063,9 @@ static bool batadv_nc_code_packets(struct batadv_priv *bat_priv, */ if (tq_weighted_neigh >= tq_weighted_coding) { /* Destination from nc_packet is selected for MAC-header */ - first_dest = nc_packet->nc_path->next_hop; + first_dest = nc_packet->neigh_node; first_source = nc_packet->nc_path->prev_hop; - second_dest = neigh_node->addr; + second_dest = neigh_node; second_source = ethhdr->h_source; packet1 = (struct batadv_unicast_packet *)nc_packet->skb->data; packet2 = (struct batadv_unicast_packet *)skb->data; @@ -1076,9 +1074,9 @@ static bool batadv_nc_code_packets(struct batadv_priv *bat_priv, skb->data + sizeof(*packet2)); } else { /* Destination for skb is selected for MAC-header */ - first_dest = neigh_node->addr; + first_dest = neigh_node; first_source = ethhdr->h_source; - second_dest = nc_packet->nc_path->next_hop; + second_dest = nc_packet->neigh_node; second_source = nc_packet->nc_path->prev_hop; packet1 = (struct batadv_unicast_packet *)skb->data; packet2 = (struct batadv_unicast_packet *)nc_packet->skb->data; @@ -1175,7 +1173,7 @@ static bool batadv_nc_code_packets(struct batadv_priv *bat_priv, batadv_nc_packet_free(nc_packet);
/* Send the coded packet and return true */ - batadv_send_skb_packet(skb_dest, neigh_node->if_incoming, first_dest); + batadv_send_unicast_skb(skb_dest, first_dest); res = true; out: if (router_neigh) diff --git a/send.c b/send.c index 63a46c3..734b24e 100644 --- a/send.c +++ b/send.c @@ -30,16 +30,30 @@
static void batadv_send_outstanding_bcast_packet(struct work_struct *work);
-/* send out an already prepared packet to the given address via the - * specified batman interface +/** + * batadv_send_skb_packet - send an already prepared packet + * @skb: the packet to send + * @hard_iface: the interface to use to send the broadcast packet + * @neigh_node: the destination node. If not NULL packet is sent as unicast + * + * Send out an already prepared packet to the given neighbor or broadcast it + * using the specified interface. Either hard_iface or neigh_node must be not + * NULL. + * If neigh_node is NULL, then the packet is broadcasted using hard_iface, + * otherwise it is sent as unicast to the given neighbor. + * + * Return NET_TX_DROP in case of error or the result of dev_queue_xmit(skb) + * otherwise */ int batadv_send_skb_packet(struct sk_buff *skb, struct batadv_hard_iface *hard_iface, const uint8_t *dst_addr) { - struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface); + struct batadv_priv *bat_priv; struct ethhdr *ethhdr;
+ bat_priv = netdev_priv(hard_iface->soft_iface); + if (hard_iface->if_status != BATADV_IF_ACTIVE) goto send_skb_err;
@@ -81,6 +95,26 @@ send_skb_err: return NET_XMIT_DROP; }
+int batadv_send_broadcast_skb(struct sk_buff *skb, + struct batadv_hard_iface *hard_iface) +{ + return batadv_send_skb_packet(skb, hard_iface, batadv_broadcast_addr); +} + +int batadv_send_unicast_skb(struct sk_buff *skb, + struct batadv_neigh_node *neigh) +{ + int ret; + + ret = batadv_send_skb_packet(skb, neigh->if_incoming, neigh->addr); +#ifdef CONFIG_BATMAN_ADV_BATMAN_V + if (ret != NET_XMIT_DROP && neigh->elp_neigh) + neigh->elp_neigh->last_unicast_tx = jiffies; +#endif + + return ret; +} + /** * batadv_send_skb_to_orig - Lookup next-hop and transmit skb. * @skb: Packet to be transmitted. @@ -127,8 +161,7 @@ int batadv_send_skb_to_orig(struct sk_buff *skb, if (recv_if && batadv_nc_skb_forward(skb, neigh_node)) { ret = NET_XMIT_POLICED; } else { - batadv_send_skb_packet(skb, neigh_node->if_incoming, - neigh_node->addr); + batadv_send_unicast_skb(skb, neigh_node); ret = NET_XMIT_SUCCESS; }
@@ -516,8 +549,7 @@ static void batadv_send_outstanding_bcast_packet(struct work_struct *work) /* send a copy of the saved skb */ skb1 = skb_clone(forw_packet->skb, GFP_ATOMIC); if (skb1) - batadv_send_skb_packet(skb1, hard_iface, - batadv_broadcast_addr); + batadv_send_broadcast_skb(skb1, hard_iface); } rcu_read_unlock();
diff --git a/send.h b/send.h index 20e1668..f510dee 100644 --- a/send.h +++ b/send.h @@ -18,12 +18,16 @@ #ifndef _NET_BATMAN_ADV_SEND_H_ #define _NET_BATMAN_ADV_SEND_H_
-int batadv_send_skb_packet(struct sk_buff *skb, - struct batadv_hard_iface *hard_iface, - const uint8_t *dst_addr); int batadv_send_skb_to_orig(struct sk_buff *skb, struct batadv_orig_node *orig_node, struct batadv_hard_iface *recv_if); +int batadv_send_skb_packet(struct sk_buff *skb, + struct batadv_hard_iface *hard_iface, + const uint8_t *dst_addr); +int batadv_send_broadcast_skb(struct sk_buff *skb, + struct batadv_hard_iface *hard_iface); +int batadv_send_unicast_skb(struct sk_buff *skb, + struct batadv_neigh_node *neigh_node); void batadv_schedule_bat_ogm(struct batadv_hard_iface *hard_iface); int batadv_add_bcast_packet_to_list(struct batadv_priv *bat_priv, const struct sk_buff *skb, diff --git a/types.h b/types.h index 22d621e..54fae29 100644 --- a/types.h +++ b/types.h @@ -342,6 +342,7 @@ struct batadv_gw_node { * @metric: ewma link metric towards this neighbor * @last_recv_seqno: last ELP received sequence number * @lest_seen: last time this neigh has been seen + * @last_unicast_tx: when the last unicast packet has been sent to this neighbor * @refcount: number of contexts the object is used * @rcu: struct used for freeing in an RCU-safe manner */ @@ -353,6 +354,7 @@ struct batadv_elp_neigh_node { uint32_t last_recv_seqno; unsigned long last_seen; uint32_t elp_interval; + unsigned long last_unicast_tx; atomic_t refcount; struct rcu_head rcu; };
On Tue, Feb 11, 2014 at 01:48:13PM +0100, Antonio Quartulli wrote:
From: Antonio Quartulli antonio@open-mesh.com
This timestamp registers the last time a unicast packet was sent to a given neighbour
I think this changelog needs improving. 95% of the patch is about refactoring a send function into a unicast send and a broadcast send function. The counter makes up the other 5% of the patch.
Andrew
On 12/02/14 09:49, Andrew Lunn wrote:
On Tue, Feb 11, 2014 at 01:48:13PM +0100, Antonio Quartulli wrote:
From: Antonio Quartulli antonio@open-mesh.com
This timestamp registers the last time a unicast packet was sent to a given neighbour
I think this changelog needs improving. 95% of the patch is about refactoring a send function into a unicast send and a broadcast send function. The counter makes up the other 5% of the patch.
Yeah I should definitely improve it. Actually the refactoring has been done to allow batman-adv to easily save the last_unicast_tx time. But you are right, I should explain it better.
Thanks!
From: Antonio Quartulli antonio@open-mesh.com
Implement a stub get_throughput() function which returns the estimated throughput towards a given neighbour. Its result is then used to compute the metric value.
The metric is updated each time a new ELP packet is sent, this way it is possible to timely react to a metric variation which can imply (for example) a neighbour disconnection.
Signed-off-by: Antonio Quartulli antonio@open-mesh.com --- bat_v_elp.c | 49 +++++++++++++++++++++++++++++++++++++++++++------ types.h | 2 ++ 2 files changed, 45 insertions(+), 6 deletions(-)
diff --git a/bat_v_elp.c b/bat_v_elp.c index 4185d8c..ef4e476 100644 --- a/bat_v_elp.c +++ b/bat_v_elp.c @@ -53,6 +53,29 @@ void batadv_elp_neigh_node_free_ref(struct batadv_elp_neigh_node *neigh) }
/** + * batadv_v_elp_get_throughput - get the throughput towards a neighbour + * @neigh: the neighbour for which the throughput has to be obtained + * + * Returns the throughput towards the given neighbour. + */ +static uint32_t +batadv_v_elp_get_throughput(struct batadv_elp_neigh_node *neigh) +{ + struct batadv_hard_iface *hard_iface = neigh->hard_iface; + struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface); + uint32_t throughput; + + /* get the customised user value for the throughput */ + throughput = atomic_read(&hard_iface->bat_v.user_throughput); + /* if the user specified a value, let's return it */ + if (throughput != 0) + return throughput; + + /* throughput cannot be computed right now. Return base value */ + return atomic_read(&bat_priv->bat_v.base_throughput); +} + +/** * batadv_v_elp_neigh_new - create a new ELP neighbour node * @hard_iface: the interface the neighbour is connected to * @neigh_addr: the neighbour interface address @@ -75,6 +98,7 @@ batadv_v_elp_neigh_new(struct batadv_hard_iface *hard_iface, memcpy(neigh->addr, neigh_addr, ETH_ALEN); neigh->last_seen = jiffies; ewma_init(&neigh->metric, 1024, 8); + neigh->hard_iface = hard_iface; /* recount initialised to 2 to simplify the caller function */ atomic_set(&neigh->refcount, 2);
@@ -147,19 +171,20 @@ static void batadv_v_elp_neigh_purge(struct batadv_hard_iface *hard_iface) }
/** - * batadv_v_elp_send_outstanding - ELP periodic broadcast sending + * batadv_v_elp_periodic_work - ELP periodic task per interface * @work: work queue item * - * Sends a broadcast ELP message over the interface that this work item belongs - * to. + * Sends a broadcast ELP message and reads the metric for all the neighbours + * connected to the interface that this work item belongs to. */ -static void batadv_v_elp_send_outstanding(struct work_struct *work) +static void batadv_v_elp_periodic_work(struct work_struct *work) { struct batadv_hard_iface *hard_iface; struct batadv_hard_iface_bat_v *bat_v; struct batadv_priv *bat_priv; struct batadv_elp_packet *elp_packet; uint32_t elp_interval; + struct batadv_elp_neigh_node *neigh; struct sk_buff *skb; uint8_t num_neighs;
@@ -181,7 +206,7 @@ static void batadv_v_elp_send_outstanding(struct work_struct *work)
skb = skb_copy(hard_iface->bat_v.elp_skb, GFP_ATOMIC); if (!skb) - goto out; + goto update_metric;
/* purge outdated entries first */ batadv_v_elp_neigh_purge(hard_iface); @@ -204,6 +229,17 @@ static void batadv_v_elp_send_outstanding(struct work_struct *work)
atomic_inc(&hard_iface->bat_v.elp_seqno);
+update_metric: + /* Instead of updating the metric each "received" ELP packet, it is + * better to do it on each ELP sending. This way, if a node is dead and + * does not send packets anymore, batman-adv is still able to timely + * react to its death. + */ + rcu_read_lock(); + hlist_for_each_entry_rcu(neigh, &hard_iface->bat_v.neigh_list, list) + ewma_add(&neigh->metric, batadv_v_elp_get_throughput(neigh)); + rcu_read_unlock(); + restart_timer: batadv_v_elp_start_timer(hard_iface); out: @@ -247,7 +283,7 @@ int batadv_v_elp_iface_enable(struct batadv_hard_iface *hard_iface) atomic_set(&hard_iface->bat_v.elp_interval, 500);
INIT_DELAYED_WORK(&hard_iface->bat_v.elp_wq, - batadv_v_elp_send_outstanding); + batadv_v_elp_periodic_work); batadv_v_elp_start_timer(hard_iface); res = 0;
@@ -332,6 +368,7 @@ static void batadv_v_elp_neigh_update(struct batadv_priv *bat_priv,
neigh->last_seen = jiffies; neigh->last_recv_seqno = ntohl(elp_packet->seqno); + ewma_add(&neigh->metric, batadv_v_elp_get_throughput(neigh));
out: if (neigh) diff --git a/types.h b/types.h index 54fae29..948d5dc 100644 --- a/types.h +++ b/types.h @@ -345,6 +345,7 @@ struct batadv_gw_node { * @last_unicast_tx: when the last unicast packet has been sent to this neighbor * @refcount: number of contexts the object is used * @rcu: struct used for freeing in an RCU-safe manner + * @hard_iface: the interface where this neighbor is connected to */ struct batadv_elp_neigh_node { struct hlist_node list; @@ -357,6 +358,7 @@ struct batadv_elp_neigh_node { unsigned long last_unicast_tx; atomic_t refcount; struct rcu_head rcu; + struct batadv_hard_iface *hard_iface; };
/**
On Tue, Feb 11, 2014 at 01:48:14PM +0100, Antonio Quartulli wrote:
From: Antonio Quartulli antonio@open-mesh.com
Implement a stub get_throughput() function which returns the estimated throughput towards a given neighbour. Its result is then used to compute the metric value.
The metric is updated each time a new ELP packet is sent, this way it is possible to timely react to a metric variation which can imply (for example) a neighbour disconnection.
It is very much a style issue, but i would probably split this into two patches. Updating the metric at send time rather than receive should have nothing to do with estimated bandwidth.
Andrew
Signed-off-by: Antonio Quartulli antonio@open-mesh.com
bat_v_elp.c | 49 +++++++++++++++++++++++++++++++++++++++++++------ types.h | 2 ++ 2 files changed, 45 insertions(+), 6 deletions(-)
diff --git a/bat_v_elp.c b/bat_v_elp.c index 4185d8c..ef4e476 100644 --- a/bat_v_elp.c +++ b/bat_v_elp.c @@ -53,6 +53,29 @@ void batadv_elp_neigh_node_free_ref(struct batadv_elp_neigh_node *neigh) }
/**
- batadv_v_elp_get_throughput - get the throughput towards a neighbour
- @neigh: the neighbour for which the throughput has to be obtained
- Returns the throughput towards the given neighbour.
- */
+static uint32_t +batadv_v_elp_get_throughput(struct batadv_elp_neigh_node *neigh) +{
- struct batadv_hard_iface *hard_iface = neigh->hard_iface;
- struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
- uint32_t throughput;
- /* get the customised user value for the throughput */
- throughput = atomic_read(&hard_iface->bat_v.user_throughput);
- /* if the user specified a value, let's return it */
- if (throughput != 0)
return throughput;
- /* throughput cannot be computed right now. Return base value */
- return atomic_read(&bat_priv->bat_v.base_throughput);
+}
+/**
- batadv_v_elp_neigh_new - create a new ELP neighbour node
- @hard_iface: the interface the neighbour is connected to
- @neigh_addr: the neighbour interface address
@@ -75,6 +98,7 @@ batadv_v_elp_neigh_new(struct batadv_hard_iface *hard_iface, memcpy(neigh->addr, neigh_addr, ETH_ALEN); neigh->last_seen = jiffies; ewma_init(&neigh->metric, 1024, 8);
- neigh->hard_iface = hard_iface; /* recount initialised to 2 to simplify the caller function */ atomic_set(&neigh->refcount, 2);
@@ -147,19 +171,20 @@ static void batadv_v_elp_neigh_purge(struct batadv_hard_iface *hard_iface) }
/**
- batadv_v_elp_send_outstanding - ELP periodic broadcast sending
- batadv_v_elp_periodic_work - ELP periodic task per interface
- @work: work queue item
- Sends a broadcast ELP message over the interface that this work item belongs
- to.
- Sends a broadcast ELP message and reads the metric for all the neighbours
*/
- connected to the interface that this work item belongs to.
-static void batadv_v_elp_send_outstanding(struct work_struct *work) +static void batadv_v_elp_periodic_work(struct work_struct *work) { struct batadv_hard_iface *hard_iface; struct batadv_hard_iface_bat_v *bat_v; struct batadv_priv *bat_priv; struct batadv_elp_packet *elp_packet; uint32_t elp_interval;
- struct batadv_elp_neigh_node *neigh; struct sk_buff *skb; uint8_t num_neighs;
@@ -181,7 +206,7 @@ static void batadv_v_elp_send_outstanding(struct work_struct *work)
skb = skb_copy(hard_iface->bat_v.elp_skb, GFP_ATOMIC); if (!skb)
goto out;
goto update_metric;
/* purge outdated entries first */ batadv_v_elp_neigh_purge(hard_iface);
@@ -204,6 +229,17 @@ static void batadv_v_elp_send_outstanding(struct work_struct *work)
atomic_inc(&hard_iface->bat_v.elp_seqno);
+update_metric:
- /* Instead of updating the metric each "received" ELP packet, it is
* better to do it on each ELP sending. This way, if a node is dead and
* does not send packets anymore, batman-adv is still able to timely
* react to its death.
*/
- rcu_read_lock();
- hlist_for_each_entry_rcu(neigh, &hard_iface->bat_v.neigh_list, list)
ewma_add(&neigh->metric, batadv_v_elp_get_throughput(neigh));
- rcu_read_unlock();
restart_timer: batadv_v_elp_start_timer(hard_iface); out: @@ -247,7 +283,7 @@ int batadv_v_elp_iface_enable(struct batadv_hard_iface *hard_iface) atomic_set(&hard_iface->bat_v.elp_interval, 500);
INIT_DELAYED_WORK(&hard_iface->bat_v.elp_wq,
batadv_v_elp_send_outstanding);
batadv_v_elp_start_timer(hard_iface); res = 0;batadv_v_elp_periodic_work);
@@ -332,6 +368,7 @@ static void batadv_v_elp_neigh_update(struct batadv_priv *bat_priv,
neigh->last_seen = jiffies; neigh->last_recv_seqno = ntohl(elp_packet->seqno);
- ewma_add(&neigh->metric, batadv_v_elp_get_throughput(neigh));
out: if (neigh) diff --git a/types.h b/types.h index 54fae29..948d5dc 100644 --- a/types.h +++ b/types.h @@ -345,6 +345,7 @@ struct batadv_gw_node {
- @last_unicast_tx: when the last unicast packet has been sent to this neighbor
- @refcount: number of contexts the object is used
- @rcu: struct used for freeing in an RCU-safe manner
*/
- @hard_iface: the interface where this neighbor is connected to
struct batadv_elp_neigh_node { struct hlist_node list; @@ -357,6 +358,7 @@ struct batadv_elp_neigh_node { unsigned long last_unicast_tx; atomic_t refcount; struct rcu_head rcu;
- struct batadv_hard_iface *hard_iface;
};
/**
1.8.5.3
On 12/02/14 09:58, Andrew Lunn wrote:
On Tue, Feb 11, 2014 at 01:48:14PM +0100, Antonio Quartulli wrote:
From: Antonio Quartulli antonio@open-mesh.com
Implement a stub get_throughput() function which returns the estimated throughput towards a given neighbour. Its result is then used to compute the metric value.
The metric is updated each time a new ELP packet is sent, this way it is possible to timely react to a metric variation which can imply (for example) a neighbour disconnection.
It is very much a style issue, but i would probably split this into two patches. Updating the metric at send time rather than receive should have nothing to do with estimated bandwidth.
Yes, to make it easier I will first move the reading at sending time and then I'll introduce the throughput estimation.
Thanks!
On 12/02/14 13:27, Antonio Quartulli wrote:
On 12/02/14 09:58, Andrew Lunn wrote:
On Tue, Feb 11, 2014 at 01:48:14PM +0100, Antonio Quartulli wrote:
From: Antonio Quartulli antonio@open-mesh.com
Implement a stub get_throughput() function which returns the estimated throughput towards a given neighbour. Its result is then used to compute the metric value.
The metric is updated each time a new ELP packet is sent, this way it is possible to timely react to a metric variation which can imply (for example) a neighbour disconnection.
It is very much a style issue, but i would probably split this into two patches. Updating the metric at send time rather than receive should have nothing to do with estimated bandwidth.
Yes, to make it easier I will first move the reading at sending time and then I'll introduce the throughput estimation.
Andrew,
I just rechecked the code and I realised that these "2 changes" are one only because there is no "second change".
Before this patch ELP does not perform any metric estimation.
This patch adds the estimation mechanism and in particular adds it along the ELP message sending path.
Maybe this was not clear? Maybe some previous patch gave you the impression that a sort of metric estimation was already performed in the receiving routine?
Cheers,
On Wed, Feb 12, 2014 at 04:44:18PM +0100, Antonio Quartulli wrote:
On 12/02/14 13:27, Antonio Quartulli wrote:
On 12/02/14 09:58, Andrew Lunn wrote:
On Tue, Feb 11, 2014 at 01:48:14PM +0100, Antonio Quartulli wrote:
From: Antonio Quartulli antonio@open-mesh.com
Implement a stub get_throughput() function which returns the estimated throughput towards a given neighbour. Its result is then used to compute the metric value.
The metric is updated each time a new ELP packet is sent, this way it is possible to timely react to a metric variation which can imply (for example) a neighbour disconnection.
It is very much a style issue, but i would probably split this into two patches. Updating the metric at send time rather than receive should have nothing to do with estimated bandwidth.
Yes, to make it easier I will first move the reading at sending time and then I'll introduce the throughput estimation.
Andrew,
I just rechecked the code and I realised that these "2 changes" are one only because there is no "second change".
Before this patch ELP does not perform any metric estimation.
This patch adds the estimation mechanism and in particular adds it along the ELP message sending path.
Maybe this was not clear? Maybe some previous patch gave you the impression that a sort of metric estimation was already performed in the receiving routine?
Ah, ok, it was this comment which confused me:
+ /* Instead of updating the metric each "received" ELP packet, it is + * better to do it on each ELP sending. This way, if a node is dead and + * does not send packets anymore, batman-adv is still able to timely + * react to its death.
The "instead of" suggests it was previously done differently. The BATMAN philosophy has been in the past to react on received packets, so i can understand where this came from. But i think this comment could be worded:
/* The metric is updated on each sent packet. This way, if a node is * dead and no longer sends packets, batman-adv is still able to * react timely to its death. */
Andrew
On 13/02/14 10:45, Andrew Lunn wrote:
On Wed, Feb 12, 2014 at 04:44:18PM +0100, Antonio Quartulli wrote:
On 12/02/14 13:27, Antonio Quartulli wrote:
On 12/02/14 09:58, Andrew Lunn wrote:
On Tue, Feb 11, 2014 at 01:48:14PM +0100, Antonio Quartulli wrote:
From: Antonio Quartulli antonio@open-mesh.com
Implement a stub get_throughput() function which returns the estimated throughput towards a given neighbour. Its result is then used to compute the metric value.
The metric is updated each time a new ELP packet is sent, this way it is possible to timely react to a metric variation which can imply (for example) a neighbour disconnection.
It is very much a style issue, but i would probably split this into two patches. Updating the metric at send time rather than receive should have nothing to do with estimated bandwidth.
Yes, to make it easier I will first move the reading at sending time and then I'll introduce the throughput estimation.
Andrew,
I just rechecked the code and I realised that these "2 changes" are one only because there is no "second change".
Before this patch ELP does not perform any metric estimation.
This patch adds the estimation mechanism and in particular adds it along the ELP message sending path.
Maybe this was not clear? Maybe some previous patch gave you the impression that a sort of metric estimation was already performed in the receiving routine?
Ah, ok, it was this comment which confused me:
/* Instead of updating the metric each "received" ELP packet, it is
* better to do it on each ELP sending. This way, if a node is dead and
* does not send packets anymore, batman-adv is still able to timely
* react to its death.
Yeah...it is a refusal from a previous approach that has been changed during the development phase, but of course I forgot to reword the comment.
The "instead of" suggests it was previously done differently. The BATMAN philosophy has been in the past to react on received packets, so i can understand where this came from. But i think this comment could be worded:
/* The metric is updated on each sent packet. This way, if a node is
- dead and no longer sends packets, batman-adv is still able to
- react timely to its death. */
Thanks for the suggestion!
Cheers,
From: Antonio Quartulli antonio@open-mesh.com
In case of a unused link, the throughput estimation will get stuck to the last sampled value and therefore the reported metric will becomes obsolete.
Send unicast ELP packets to each neighbor to trigger throughput sampling on unused links.
These packets will fill an entire frame so that the measurement is as much reliable as possible
Signed-off-by: Antonio Quartulli antonio@open-mesh.com --- bat_v_elp.c | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++----- main.h | 3 +++ send.c | 4 +-- 3 files changed, 85 insertions(+), 8 deletions(-)
diff --git a/bat_v_elp.c b/bat_v_elp.c index ef4e476..14a1e15 100644 --- a/bat_v_elp.c +++ b/bat_v_elp.c @@ -25,6 +25,7 @@ #include "bat_v_elp.h" #include "originator.h" #include "routing.h" +#include "soft-interface.h" #include "translation-table.h"
/** @@ -171,6 +172,67 @@ static void batadv_v_elp_neigh_purge(struct batadv_hard_iface *hard_iface) }
/** + * batadv_v_elp_wifi_neigh_probe - send link probing packets to a neighbour + * @hard_iface: the interface to use while sending the probing packets + * @neigh: the neighbour to probe + * + * Sends a predefined number of unicast wifi packets to a given neighbour in + * order to trigger the throughput estimation on this link by the RC algorithm. + * Packets are sent only if there there is not enough payload unicast traffic + * towards this neighbour.. + * + * Returns 0 on success and -1 in case of error during skb preparation. + */ +static int batadv_v_elp_wifi_neigh_probe(struct batadv_hard_iface *hard_iface, + struct batadv_elp_neigh_node *neigh) +{ + struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface); + unsigned long last_tx_diff; + struct sk_buff *skb; + int probe_len, i; + + /* this probing routine is for Wifi neighbours only */ + if (batadv_is_wifi_netdev(hard_iface->net_dev)) + return 0; + + /* probe the neighbor only if no unicast packets have been sent + * to it in the last 100 milliseconds: this is the rate control + * algorithm sampling interval (minstrel). In this way, if not + * enough traffic has been sent to the neighbor, batman-adv can + * generate 2 probe packets and push the RC algorithm to perform + * the sampling + */ + last_tx_diff = jiffies_to_msecs(jiffies - neigh->last_unicast_tx); + if (last_tx_diff <= BATADV_ELP_PROBE_MAX_TX_DIFF) + return 0; + + probe_len = max_t(int, sizeof(struct batadv_elp_packet), + BATADV_ELP_MIN_PROBE_SIZE); + + for (i = 0; i < BATADV_ELP_PROBES_PER_NODE; i++) { + skb = skb_copy_expand(hard_iface->bat_v.elp_skb, 0, + probe_len - hard_iface->bat_v.elp_skb->len, + GFP_ATOMIC); + if (!skb) + return -1; + + /* Tell the skb to get as bigger as the allocated space (we want + * the packet to be exactly of that size to make the link + * throughput estimation effective. + */ + skb_put(skb, probe_len - hard_iface->bat_v.elp_skb->len); + + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, + "Sending unicast (probe) ELP packet on interface %s to %pM\n", + hard_iface->net_dev->name, neigh->addr); + + batadv_send_skb_packet(skb, hard_iface, neigh->addr); + } + + return 0; +} + +/** * batadv_v_elp_periodic_work - ELP periodic task per interface * @work: work queue item * @@ -181,11 +243,11 @@ static void batadv_v_elp_periodic_work(struct work_struct *work) { struct batadv_hard_iface *hard_iface; struct batadv_hard_iface_bat_v *bat_v; - struct batadv_priv *bat_priv; struct batadv_elp_packet *elp_packet; - uint32_t elp_interval; struct batadv_elp_neigh_node *neigh; + struct batadv_priv *bat_priv; struct sk_buff *skb; + uint32_t elp_interval; uint8_t num_neighs;
bat_v = container_of(work, struct batadv_hard_iface_bat_v, elp_wq.work); @@ -206,7 +268,7 @@ static void batadv_v_elp_periodic_work(struct work_struct *work)
skb = skb_copy(hard_iface->bat_v.elp_skb, GFP_ATOMIC); if (!skb) - goto update_metric; + goto restart_timer;
/* purge outdated entries first */ batadv_v_elp_neigh_purge(hard_iface); @@ -221,7 +283,7 @@ static void batadv_v_elp_periodic_work(struct work_struct *work) elp_packet->elp_interval = htonl(elp_interval);
batadv_dbg(BATADV_DBG_BATMAN, bat_priv, - "Sending ELP packet on interface %s, seqno %u\n", + "Sending broadcast ELP packet on interface %s, seqno %u\n", hard_iface->net_dev->name, atomic_read(&hard_iface->bat_v.elp_seqno));
@@ -229,15 +291,27 @@ static void batadv_v_elp_periodic_work(struct work_struct *work)
atomic_inc(&hard_iface->bat_v.elp_seqno);
-update_metric: /* Instead of updating the metric each "received" ELP packet, it is * better to do it on each ELP sending. This way, if a node is dead and * does not send packets anymore, batman-adv is still able to timely * react to its death. + * + * The metric is updated by following these steps: + * 1) if the hard_iface if wifi => send a unicast ELP for + * probing/sampling to each neighbor + * 2) update the metric value of each neighbor + * */ rcu_read_lock(); - hlist_for_each_entry_rcu(neigh, &hard_iface->bat_v.neigh_list, list) + hlist_for_each_entry_rcu(neigh, &hard_iface->bat_v.neigh_list, list) { + if (batadv_v_elp_wifi_neigh_probe(hard_iface, neigh) < 0) + /* if something goes wrong while probing, better to stop + * sending packets immediately and reschedule the task + */ + break; + ewma_add(&neigh->metric, batadv_v_elp_get_throughput(neigh)); + } rcu_read_unlock();
restart_timer: diff --git a/main.h b/main.h index 5699c9f..9cfdbb3 100644 --- a/main.h +++ b/main.h @@ -54,6 +54,9 @@ #define BATADV_ORIG_WORK_PERIOD 1000 /* 1 second */ #define BATADV_DAT_ENTRY_TIMEOUT (5*60000) /* 5 mins in milliseconds */ #define BATADV_ELP_OUTDATED_MAX 4 +#define BATADV_ELP_PROBES_PER_NODE 2 +#define BATADV_ELP_MIN_PROBE_SIZE 200 /* bytes */ +#define BATADV_ELP_PROBE_MAX_TX_DIFF 100 /* milliseconds */ /* sliding packet range of received originator messages in sequence numbers * (should be a multiple of our word size) */ diff --git a/send.c b/send.c index 734b24e..20b60d7 100644 --- a/send.c +++ b/send.c @@ -108,8 +108,8 @@ int batadv_send_unicast_skb(struct sk_buff *skb,
ret = batadv_send_skb_packet(skb, neigh->if_incoming, neigh->addr); #ifdef CONFIG_BATMAN_ADV_BATMAN_V - if (ret != NET_XMIT_DROP && neigh->elp_neigh) - neigh->elp_neigh->last_unicast_tx = jiffies; + if (ret != NET_XMIT_DROP && neigh->bat_v.elp_neigh) + neigh->bat_v.elp_neigh->last_unicast_tx = jiffies; #endif
return ret;
On Tue, Feb 11, 2014 at 01:48:15PM +0100, Antonio Quartulli wrote:
From: Antonio Quartulli antonio@open-mesh.com
In case of a unused link, the throughput estimation will get stuck to the last sampled value and therefore the reported metric will becomes obsolete.
Send unicast ELP packets to each neighbor to trigger throughput sampling on unused links.
Humm, i can understand the need for this, but i really think the rate control code should be sending the probes, not batman. What do the wifi people say about this? Have they tried submitting patches to them?
/* Instead of updating the metric each "received" ELP packet, it is * better to do it on each ELP sending. This way, if a node is dead and * does not send packets anymore, batman-adv is still able to timely * react to its death.
*
* The metric is updated by following these steps:
* 1) if the hard_iface if wifi => send a unicast ELP for
* probing/sampling to each neighbor
* 2) update the metric value of each neighbor
Might be worth pointing out here, that because of queuing, there is no guarantee the ELP packets have been send yet and the RC estimated bandwidth could be up to 100ms old.
*/ rcu_read_lock();*
- hlist_for_each_entry_rcu(neigh, &hard_iface->bat_v.neigh_list, list)
- hlist_for_each_entry_rcu(neigh, &hard_iface->bat_v.neigh_list, list) {
if (batadv_v_elp_wifi_neigh_probe(hard_iface, neigh) < 0)
/* if something goes wrong while probing, better to stop
* sending packets immediately and reschedule the task
*/
break;
- ewma_add(&neigh->metric, batadv_v_elp_get_throughput(neigh));
- } rcu_read_unlock();
On 12/02/14 10:12, Andrew Lunn wrote:
On Tue, Feb 11, 2014 at 01:48:15PM +0100, Antonio Quartulli wrote:
From: Antonio Quartulli antonio@open-mesh.com
In case of a unused link, the throughput estimation will get stuck to the last sampled value and therefore the reported metric will becomes obsolete.
Send unicast ELP packets to each neighbor to trigger throughput sampling on unused links.
Humm, i can understand the need for this, but i really think the rate control code should be sending the probes, not batman. What do the wifi people say about this? Have they tried submitting patches to them?
I am CC'ing Felix so that he can give his opinion. But last time I checked Minstrel I realised that it uses data packets to probe rates (there is not a specific probing packet), meaning that if there is no data packet to send, then no probing is performed.
Sending this ELP packets (when there is no unicast traffic) is a way to trigger this mechanism in Minstrel.
/* Instead of updating the metric each "received" ELP packet, it is * better to do it on each ELP sending. This way, if a node is dead and * does not send packets anymore, batman-adv is still able to timely * react to its death.
*
* The metric is updated by following these steps:
* 1) if the hard_iface if wifi => send a unicast ELP for
* probing/sampling to each neighbor
* 2) update the metric value of each neighbor
Might be worth pointing out here, that because of queuing, there is no guarantee the ELP packets have been send yet and the RC estimated bandwidth could be up to 100ms old.
OK, I'll make it explicit. Actually this routine is no meant to get a "real time estimation", also because the routing algorithm would not be that fast to react on time.
Cheers,
On 2014-02-12 13:12, Antonio Quartulli wrote:
On 12/02/14 10:12, Andrew Lunn wrote:
On Tue, Feb 11, 2014 at 01:48:15PM +0100, Antonio Quartulli wrote:
From: Antonio Quartulli antonio@open-mesh.com
In case of a unused link, the throughput estimation will get stuck to the last sampled value and therefore the reported metric will becomes obsolete.
Send unicast ELP packets to each neighbor to trigger throughput sampling on unused links.
Humm, i can understand the need for this, but i really think the rate control code should be sending the probes, not batman. What do the wifi people say about this? Have they tried submitting patches to them?
I am CC'ing Felix so that he can give his opinion. But last time I checked Minstrel I realised that it uses data packets to probe rates (there is not a specific probing packet), meaning that if there is no data packet to send, then no probing is performed.
Correct. Minstrel never sends dedicated probing packets. Changing minstrel to send active probing packets to serve the needs of batman would be a very bad idea, because pretty much all regular users do not have any need for extra probing.
Sending this ELP packets (when there is no unicast traffic) is a way to trigger this mechanism in Minstrel.
Right. If I remember correctly, if you send less than 1 packet per 100 ms, then all packets should end up being probing packets. If that doesn't work, we can change minstrel's probing pattern to allow things like batman to get a desirable amount of rate control probing simply by sending unicast packets.
- Felix
On 12/02/14 13:54, Felix Fietkau wrote:
On 2014-02-12 13:12, Antonio Quartulli wrote:
On 12/02/14 10:12, Andrew Lunn wrote:
On Tue, Feb 11, 2014 at 01:48:15PM +0100, Antonio Quartulli wrote:
From: Antonio Quartulli antonio@open-mesh.com
In case of a unused link, the throughput estimation will get stuck to the last sampled value and therefore the reported metric will becomes obsolete.
Send unicast ELP packets to each neighbor to trigger throughput sampling on unused links.
Humm, i can understand the need for this, but i really think the rate control code should be sending the probes, not batman. What do the wifi people say about this? Have they tried submitting patches to them?
I am CC'ing Felix so that he can give his opinion. But last time I checked Minstrel I realised that it uses data packets to probe rates (there is not a specific probing packet), meaning that if there is no data packet to send, then no probing is performed.
Correct. Minstrel never sends dedicated probing packets. Changing minstrel to send active probing packets to serve the needs of batman would be a very bad idea, because pretty much all regular users do not have any need for extra probing.
Sending this ELP packets (when there is no unicast traffic) is a way to trigger this mechanism in Minstrel.
Right. If I remember correctly, if you send less than 1 packet per 100 ms, then all packets should end up being probing packets. If that doesn't work, we can change minstrel's probing pattern to allow things like batman to get a desirable amount of rate control probing simply by sending unicast packets.
This is what I am trying to achieve here: if no unicast packets have been sent for the last 100ms (at least) then send N probing packets in a row (with N = 2 at the moment - it is a define in main.h).
Thanks Felix!
Cheers,
On 12/02/14 13:56, Antonio Quartulli wrote:
On 12/02/14 13:54, Felix Fietkau wrote:
On 2014-02-12 13:12, Antonio Quartulli wrote:
On 12/02/14 10:12, Andrew Lunn wrote:
On Tue, Feb 11, 2014 at 01:48:15PM +0100, Antonio Quartulli wrote:
From: Antonio Quartulli antonio@open-mesh.com
In case of a unused link, the throughput estimation will get stuck to the last sampled value and therefore the reported metric will becomes obsolete.
Send unicast ELP packets to each neighbor to trigger throughput sampling on unused links.
Humm, i can understand the need for this, but i really think the rate control code should be sending the probes, not batman. What do the wifi people say about this? Have they tried submitting patches to them?
I am CC'ing Felix so that he can give his opinion. But last time I checked Minstrel I realised that it uses data packets to probe rates (there is not a specific probing packet), meaning that if there is no data packet to send, then no probing is performed.
Correct. Minstrel never sends dedicated probing packets. Changing minstrel to send active probing packets to serve the needs of batman would be a very bad idea, because pretty much all regular users do not have any need for extra probing.
Sending this ELP packets (when there is no unicast traffic) is a way to trigger this mechanism in Minstrel.
Right. If I remember correctly, if you send less than 1 packet per 100 ms, then all packets should end up being probing packets. If that doesn't work, we can change minstrel's probing pattern to allow things like batman to get a desirable amount of rate control probing simply by sending unicast packets.
This is what I am trying to achieve here: if no unicast packets have been sent for the last 100ms (at least) then send N probing packets in a row (with N = 2 at the moment - it is a define in main.h).
However, from the experiments performed so far it looked like Minstrel was behaving properly under these batman probing packets.
As soon as we move on with this project I am sure we will get some more feedback from other users (perhaps this is something we can test at the WBM? - this code should be ready and usable by then - cross fingers)
Cheers,
Right. If I remember correctly, if you send less than 1 packet per 100 ms, then all packets should end up being probing packets. If that doesn't work, we can change minstrel's probing pattern to allow things like batman to get a desirable amount of rate control probing simply by sending unicast packets.
This is what I am trying to achieve here: if no unicast packets have been sent for the last 100ms (at least) then send N probing packets in a row (with N = 2 at the moment - it is a define in main.h).
Hi Antonio
One minor thing to consider is that your statement is actually:
if no _batman_ unicast packets have been sent for the last 100ms ...
I've used batman in setups where it has not been the exclusive user of the hard interface. So there could be other traffic which is keeping the RC algorithm up to date. So maybe it would be better to use the hard interface statistic counters rather than last time batman sent a packet?
Andrew
On 13/02/14 10:55, Andrew Lunn wrote:
Right. If I remember correctly, if you send less than 1 packet per 100 ms, then all packets should end up being probing packets. If that doesn't work, we can change minstrel's probing pattern to allow things like batman to get a desirable amount of rate control probing simply by sending unicast packets.
This is what I am trying to achieve here: if no unicast packets have been sent for the last 100ms (at least) then send N probing packets in a row (with N = 2 at the moment - it is a define in main.h).
Hi Antonio
One minor thing to consider is that your statement is actually:
if no _batman_ unicast packets have been sent for the last 100ms ...
I've used batman in setups where it has not been the exclusive user of the hard interface. So there could be other traffic which is keeping the RC algorithm up to date. So maybe it would be better to use the hard interface statistic counters rather than last time batman sent a packet?
We can't use the hard_iface statistics because we need to know where that traffic has been sent to. The same interface might connected to several neighbours and the rates are selected/probed on a per-peer base.
However, to go in the direction that you are suggesting, I could use the same API I use to read the throughput (cfg80211_get_station()) to also read when the last packet was sent to a given peer. This information should be good for our purposes, but it means that we probe the neighbours right after having read the throughput.
This should not be a problem because as we said in a previous email we do not require real time throughput updates.
Cheers,
We can't use the hard_iface statistics because we need to know where that traffic has been sent to.
Oh, yes, my error.
However, to go in the direction that you are suggesting, I could use the same API I use to read the throughput (cfg80211_get_station()) to also read when the last packet was sent to a given peer. This information should be good for our purposes, but it means that we probe the neighbours right after having read the throughput.
My guess is, there are not many use cases where the hard interface is used for other traffic as well as BATMAN. So i don't see it as being a big issue. Maybe put it onto a TODO list once the code has been merged, to swap to using statistics from lower down in the stack.
Andrew
On 13/02/14 11:09, Andrew Lunn wrote:
We can't use the hard_iface statistics because we need to know where that traffic has been sent to.
Oh, yes, my error.
However, to go in the direction that you are suggesting, I could use the same API I use to read the throughput (cfg80211_get_station()) to also read when the last packet was sent to a given peer. This information should be good for our purposes, but it means that we probe the neighbours right after having read the throughput.
My guess is, there are not many use cases where the hard interface is used for other traffic as well as BATMAN. So i don't see it as being a big issue. Maybe put it onto a TODO list once the code has been merged, to swap to using statistics from lower down in the stack.
Yes, note taken ;)
Thanks!
From: Antonio Quartulli antonio@open-mesh.com
In case of wireless interface retrieve the throughput by querying cfg80211. To perform this call a separate work must be scheduled because the function may sleep and this is not allowed within an RCU protected context (RCU in this case is used to iterate over all the neighbours).
Signed-off-by: Antonio Quartulli antonio@open-mesh.com --- bat_v_elp.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++++------- compat.h | 20 ++++++++++++++++++++ types.h | 2 ++ 3 files changed, 76 insertions(+), 7 deletions(-)
diff --git a/bat_v_elp.c b/bat_v_elp.c index 14a1e15..31190af 100644 --- a/bat_v_elp.c +++ b/bat_v_elp.c @@ -18,6 +18,8 @@ * */
+#include <net/cfg80211.h> + #include "main.h" #include "hard-interface.h" #include "send.h" @@ -64,19 +66,56 @@ batadv_v_elp_get_throughput(struct batadv_elp_neigh_node *neigh) { struct batadv_hard_iface *hard_iface = neigh->hard_iface; struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface); + struct station_info sinfo; uint32_t throughput; + int r;
- /* get the customised user value for the throughput */ - throughput = atomic_read(&hard_iface->bat_v.user_throughput); - /* if the user specified a value, let's return it */ + /* if the user specified a customised value for this interface, then + * return it directly + */ + throughput = atomic_read(&hard_iface->bat_v.user_throughput); if (throughput != 0) return throughput;
- /* throughput cannot be computed right now. Return base value */ + /* if this is a wireless device, then ask its throughput through + * cfg80211 API + */ + if (hard_iface->net_dev->ieee80211_ptr) { + r = cfg80211_get_station(hard_iface->net_dev, neigh->addr, + &sinfo); + if (r == -ENOENT) { + /* node is not associated anymore! it would be possible + * to delete this neighbor. for now set metric to 0 + */ + return 0; + } + if (!r) + return sinfo.expected_throughput; + } + + /* if none of the above cases apply, return the base_throughput */ return atomic_read(&bat_priv->bat_v.base_throughput); }
/** + * batadv_v_elp_metric_update - worker updating the metric of one neighbour + * @work: the work queue item + */ +static void batadv_v_elp_metric_update(struct work_struct *work) +{ + struct batadv_elp_neigh_node *neigh; + + neigh = container_of(work, struct batadv_elp_neigh_node, metric_work); + + ewma_add(&neigh->metric, batadv_v_elp_get_throughput(neigh)); + + /* decrement refcounter to balance increment performed before scheduling + * this task + */ + batadv_elp_neigh_node_free_ref(neigh); +} + +/** * batadv_v_elp_neigh_new - create a new ELP neighbour node * @hard_iface: the interface the neighbour is connected to * @neigh_addr: the neighbour interface address @@ -108,6 +147,8 @@ batadv_v_elp_neigh_new(struct batadv_hard_iface *hard_iface, spin_unlock_bh(&hard_iface->bat_v.neigh_list_lock); atomic_inc(&hard_iface->bat_v.num_neighbors);
+ INIT_WORK(&neigh->metric_work, batadv_v_elp_metric_update); + return neigh; }
@@ -192,7 +233,7 @@ static int batadv_v_elp_wifi_neigh_probe(struct batadv_hard_iface *hard_iface, int probe_len, i;
/* this probing routine is for Wifi neighbours only */ - if (batadv_is_wifi_netdev(hard_iface->net_dev)) + if (!batadv_is_wifi_netdev(hard_iface->net_dev)) return 0;
/* probe the neighbor only if no unicast packets have been sent @@ -310,7 +351,14 @@ static void batadv_v_elp_periodic_work(struct work_struct *work) */ break;
- ewma_add(&neigh->metric, batadv_v_elp_get_throughput(neigh)); + if (!atomic_inc_not_zero(&neigh->refcount)) + continue; + + /* reading the estimated throughput from cfg80211 is a task that + * may sleep and that is not allowed in an rcu protected + * context. Therefore schedule a task for that. + */ + queue_work(batadv_event_workqueue, &neigh->metric_work); } rcu_read_unlock();
@@ -442,7 +490,6 @@ static void batadv_v_elp_neigh_update(struct batadv_priv *bat_priv,
neigh->last_seen = jiffies; neigh->last_recv_seqno = ntohl(elp_packet->seqno); - ewma_add(&neigh->metric, batadv_v_elp_get_throughput(neigh));
out: if (neigh) diff --git a/compat.h b/compat.h index 12bc8d8..9f71f51 100644 --- a/compat.h +++ b/compat.h @@ -407,4 +407,24 @@ static int __batadv_interface_kill_vid(struct net_device *dev, __be16 proto,\
#endif /* < KERNEL_VERSION(3, 14, 0) */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 15, 0) + +/* NOTE: shall I put this ifndef outside of every #if block ? */ +#ifndef cfg80211_get_station + +/* the expected behaviour of this function is to return 0 on success, therefore + * it is possible to define it as 1 so that batman-adv thinks like something + * went wrong. It will then decide what to do. + */ +#define cfg80211_get_station(_a, _b, _c) (1) +/* the following define substitute the expected_throughput field with a random + * one existing in the station_info struct. It can be random because due to the + * define above it will never be used. We need it only to make the code compile + */ +#define expected_throughput filled + +#endif /* cfg80211_get_station */ + +#endif /* < KERNEL_VERSION(3, 15, 0) */ + #endif /* _NET_BATMAN_ADV_COMPAT_H_ */ diff --git a/types.h b/types.h index 948d5dc..6f05d99 100644 --- a/types.h +++ b/types.h @@ -346,6 +346,7 @@ struct batadv_gw_node { * @refcount: number of contexts the object is used * @rcu: struct used for freeing in an RCU-safe manner * @hard_iface: the interface where this neighbor is connected to + * @metric_work: work queue callback item for metric update */ struct batadv_elp_neigh_node { struct hlist_node list; @@ -359,6 +360,7 @@ struct batadv_elp_neigh_node { atomic_t refcount; struct rcu_head rcu; struct batadv_hard_iface *hard_iface; + struct work_struct metric_work; };
/**
From: Antonio Quartulli antonio@open-mesh.com
Improve the neighbour purging routine to timely detect when a neighbour is not sending data anymore without waiting for a real timeout. This detection system can be used by the B.A.T.M.A.N. V routing algorithm to trigger an on-demand route reconstruction.
Signed-off-by: Antonio Quartulli antonio@open-mesh.com --- bat_v.c | 7 +++++ bat_v_elp.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- bat_v_elp.h | 2 ++ types.h | 2 ++ 4 files changed, 98 insertions(+), 4 deletions(-)
diff --git a/bat_v.c b/bat_v.c index 3aa42ec..5681d19 100644 --- a/bat_v.c +++ b/bat_v.c @@ -75,14 +75,21 @@ static struct batadv_algo_ops batadv_batman_v __read_mostly = {
int batadv_v_mesh_init(struct batadv_priv *bat_priv) { + int r; + atomic_set(&bat_priv->bat_v.base_throughput, BATADV_DEFAULT_BASE_THROUGHPUT);
+ r = batadv_v_elp_init(bat_priv); + if (r) + return r; + return batadv_v_ogm_init(bat_priv); }
void batadv_v_mesh_free(struct batadv_priv *bat_priv) { + batadv_v_elp_free(bat_priv); batadv_v_ogm_free(bat_priv); }
diff --git a/bat_v_elp.c b/bat_v_elp.c index 31190af..763113e 100644 --- a/bat_v_elp.c +++ b/bat_v_elp.c @@ -25,6 +25,7 @@ #include "send.h" #include "bat_algo.h" #include "bat_v_elp.h" +#include "bat_v_ogm.h" #include "originator.h" #include "routing.h" #include "soft-interface.h" @@ -186,30 +187,55 @@ batadv_v_elp_neigh_get(struct batadv_hard_iface *hard_iface, * batadv_v_elp_neigh_purge - purge obsolete neighbour nodes * * Deletes the ELP neighbour nodes that did not send any ELP message for a - * pre-defined amount of time. + * pre-defined amount of time and returns the minimum ELP interval among all the + * remaining neighbours. */ -static void batadv_v_elp_neigh_purge(struct batadv_hard_iface *hard_iface) +static uint32_t batadv_v_elp_neigh_purge(struct batadv_hard_iface *hard_iface) { + struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface); unsigned long timeout; struct batadv_elp_neigh_node *neigh; struct hlist_node *node; bool has_timed_out; + uint32_t tmp_interval, min_interval = UINT_MAX;
spin_lock_bh(&hard_iface->bat_v.neigh_list_lock); hlist_for_each_entry_safe(neigh, node, &hard_iface->bat_v.neigh_list, list) { - timeout = neigh->elp_interval * BATADV_ELP_OUTDATED_MAX; + tmp_interval = neigh->elp_interval; + + timeout = tmp_interval * BATADV_ELP_OUTDATED_MAX; has_timed_out = batadv_has_timed_out(neigh->last_seen, timeout); + if (has_timed_out) { + /* this neigh is dead! the node could react somehow. + * TODO: implement dead node reaction mechanism + */ + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, + "Node %pM is dead.\n", neigh->addr);
+ /* this node is not sending ELP packets anymore. Don't + * consider it in the minimum ELP interval research + */ + tmp_interval = UINT_MAX; + } + + has_timed_out = batadv_has_timed_out(neigh->last_seen, + BATADV_PURGE_TIMEOUT); if ((!has_timed_out) && - (hard_iface->if_status == BATADV_IF_ACTIVE)) + (hard_iface->if_status == BATADV_IF_ACTIVE)) { + if (tmp_interval < min_interval) + min_interval = tmp_interval; continue; + }
hlist_del_rcu(&neigh->list); atomic_dec(&hard_iface->bat_v.num_neighbors); batadv_elp_neigh_node_free_ref(neigh); + } spin_unlock_bh(&hard_iface->bat_v.neigh_list_lock); + + return min_interval; }
/** @@ -479,6 +505,7 @@ static void batadv_v_elp_neigh_update(struct batadv_priv *bat_priv,
{ struct batadv_elp_neigh_node *neigh; + uint32_t interval;
neigh = batadv_v_elp_neigh_get(if_incoming, neigh_addr); if (!neigh) { @@ -490,6 +517,16 @@ static void batadv_v_elp_neigh_update(struct batadv_priv *bat_priv,
neigh->last_seen = jiffies; neigh->last_recv_seqno = ntohl(elp_packet->seqno); + neigh->elp_interval = ntohl(elp_packet->elp_interval); + + interval = atomic_read(&bat_priv->bat_v.neigh_interval); + if (neigh->elp_interval >= interval) + goto out; + + /* got a shorter elp interval: reschedule the neigh check */ + atomic_set(&bat_priv->bat_v.neigh_interval, neigh->elp_interval); + queue_delayed_work(batadv_event_workqueue, &bat_priv->bat_v.neigh_wq, + msecs_to_jiffies(neigh->elp_interval));
out: if (neigh) @@ -625,3 +662,49 @@ out: batadv_hardif_free_ref(primary_if); return 0; } + +static void batadv_v_elp_check_neigh(struct work_struct *work) +{ + struct batadv_priv_bat_v *bat_v; + struct batadv_priv *bat_priv; + struct batadv_hard_iface *hard_iface; + uint32_t tmp_interval, min_interval = UINT_MAX; + + bat_v = container_of(work, struct batadv_priv_bat_v, neigh_wq.work); + bat_priv = container_of(bat_v, struct batadv_priv, bat_v); + + list_for_each_entry(hard_iface, &batadv_hardif_list, list) { + if (hard_iface->soft_iface != bat_priv->soft_iface) + continue; + + tmp_interval = batadv_v_elp_neigh_purge(hard_iface); + /* while iterating over all the ELP neighbors, find the smallest + * ELP interval to use to schedule the next check + */ + if (tmp_interval < min_interval) + min_interval = tmp_interval; + } + + /* if there are no more ELP neighbors the work does not need to be + * rescheduled + */ + if (min_interval == UINT_MAX) + return; + + atomic_set(&bat_priv->bat_v.neigh_interval, min_interval); + queue_delayed_work(batadv_event_workqueue, &bat_priv->bat_v.neigh_wq, + msecs_to_jiffies(min_interval)); +} + +int batadv_v_elp_init(struct batadv_priv *bat_priv) +{ + INIT_DELAYED_WORK(&bat_priv->bat_v.neigh_wq, batadv_v_elp_check_neigh); + atomic_set(&bat_priv->bat_v.neigh_interval, UINT_MAX); + + return 0; +} + +void batadv_v_elp_free(struct batadv_priv *bat_priv) +{ + cancel_delayed_work_sync(&bat_priv->bat_v.neigh_wq); +} diff --git a/bat_v_elp.h b/bat_v_elp.h index 4f34ecb..5dd0568 100644 --- a/bat_v_elp.h +++ b/bat_v_elp.h @@ -33,5 +33,7 @@ void batadv_v_elp_primary_iface_set(struct batadv_hard_iface *hard_iface); int batadv_v_elp_packet_recv(struct sk_buff *skb, struct batadv_hard_iface *if_incoming); int batadv_v_elp_seq_print_text(struct seq_file *seq, void *offset); +int batadv_v_elp_init(struct batadv_priv *bat_priv); +void batadv_v_elp_free(struct batadv_priv *bat_priv);
#endif /* _NET_BATMAN_ADV_BAT_V_ELP_H_ */ diff --git a/types.h b/types.h index 6f05d99..2ba27ab 100644 --- a/types.h +++ b/types.h @@ -748,6 +748,8 @@ struct batadv_priv_bat_v { struct delayed_work ogm_wq; atomic_t ogm_seqno; atomic_t base_throughput; + atomic_t neigh_interval; + struct delayed_work neigh_wq; };
/**
From: Antonio Quartulli antonio@open-mesh.com
The phydev member of a net_device can be used to get information about an ethernet link like HALF/FULL_DUPLEX and advertised bandwidth (e.g. 100/10Mbps).
This information are then stored in the hard_iface object to be used during the metric computation routine.
Signed-off-by: Antonio Quartulli antonio@open-mesh.com --- bat_v_elp.c | 8 ++++++++ bat_v_ogm.c | 4 ++-- hard-interface.c | 19 +++++++++++++++++++ types.h | 12 ++++++++++++ 4 files changed, 41 insertions(+), 2 deletions(-)
diff --git a/bat_v_elp.c b/bat_v_elp.c index 763113e..958843c 100644 --- a/bat_v_elp.c +++ b/bat_v_elp.c @@ -78,6 +78,14 @@ batadv_v_elp_get_throughput(struct batadv_elp_neigh_node *neigh) if (throughput != 0) return throughput;
+ /* In case of Ethernet interface, the throughput has already been + * obtained from the phydev object in the net_device struct (see + * batadv_hardif_activate_interface()). So return this value. + */ + throughput = hard_iface->bat_v.eth_throughput; + if (throughput != 0) + return throughput; + /* if this is a wireless device, then ask its throughput through * cfg80211 API */ diff --git a/bat_v_ogm.c b/bat_v_ogm.c index fa3d1a1..060ce80 100644 --- a/bat_v_ogm.c +++ b/bat_v_ogm.c @@ -393,8 +393,8 @@ static uint32_t batadv_v_penalty(struct batadv_priv *bat_priv, /* proportion to use the same value used in batman iv (x * 128 / 256) */ hop_penalty = hop_penalty * 100 / 255;
- if (batadv_is_wifi_netdev(if_incoming->net_dev) && - metric > link_metric / 10) + if ((if_incoming->bat_v.flags & BATADV_FULL_DUPLEX) && + (metric > link_metric / 10)) return metric / 2;
return metric * (100 - hop_penalty) / 100; diff --git a/hard-interface.c b/hard-interface.c index 2a04130..58c8669 100644 --- a/hard-interface.c +++ b/hard-interface.c @@ -31,6 +31,8 @@
#include <linux/if_arp.h> #include <linux/if_ether.h> +#include <linux/netdevice.h> +#include <linux/phy.h>
void batadv_hardif_free_rcu(struct rcu_head *rcu) { @@ -297,6 +299,7 @@ void batadv_update_min_mtu(struct net_device *soft_iface) static void batadv_hardif_activate_interface(struct batadv_hard_iface *hard_iface) { + struct net_device *dev = hard_iface->net_dev; struct batadv_priv *bat_priv; struct batadv_hard_iface *primary_if = NULL;
@@ -315,6 +318,22 @@ batadv_hardif_activate_interface(struct batadv_hard_iface *hard_iface) if (!primary_if) batadv_primary_if_select(bat_priv, hard_iface);
+ /* set the default values */ + hard_iface->bat_v.flags &= ~BATADV_FULL_DUPLEX; + hard_iface->bat_v.eth_throughput = 0; + if (dev->phydev) { + if (dev->phydev->duplex == DUPLEX_FULL) + hard_iface->bat_v.flags |= BATADV_FULL_DUPLEX; + + /* set the speed according to the phydev setting. Store the + * value in Kbps (as done for the other throughput variables) + */ + if (dev->phydev->speed != SPEED_UNKNOWN) { + hard_iface->bat_v.eth_throughput = dev->phydev->speed; + hard_iface->bat_v.eth_throughput *= 10; + } + } + batadv_info(hard_iface->soft_iface, "Interface activated: %s\n", hard_iface->net_dev->name);
diff --git a/types.h b/types.h index 2ba27ab..a51921e 100644 --- a/types.h +++ b/types.h @@ -70,6 +70,14 @@ struct batadv_hard_iface_bat_iv { };
/** + * enum batadv_v_hard_iface_flags - interface flags useful to B.A.T.M.A.N. V + * @BATADV_FULL_DUPLEX: tells if the connection over this link is full-duplex + */ +enum batadv_v_hard_iface_flags { + BATADV_FULL_DUPLEX = BIT(0), +}; + +/** * struct batadv_hard_iface_bat_v - per hard interface B.A.T.M.A.N. V data * @elp_interval: time interval between two ELP transmissions * @elp_seqno: current ELP sequence number @@ -79,6 +87,8 @@ struct batadv_hard_iface_bat_iv { * @elp_skb: base skb containing the ELP message to send * @elp_wq: workqueue used to schedule ELP transmissions * @user_throughput: user specified throughput + * @eth_throughput: throughput for wired interfaces (obtained from phydev) + * @flags: interface specific flags */ struct batadv_hard_iface_bat_v { atomic_t elp_interval; @@ -89,6 +99,8 @@ struct batadv_hard_iface_bat_v { struct sk_buff *elp_skb; struct delayed_work elp_wq; atomic_t user_throughput; + uint32_t eth_throughput; + uint8_t flags; };
/**
On 11/02/14 13:48, Antonio Quartulli wrote:
@@ -297,6 +299,7 @@ void batadv_update_min_mtu(struct net_device *soft_iface) static void batadv_hardif_activate_interface(struct batadv_hard_iface *hard_iface) {
- struct net_device *dev = hard_iface->net_dev; struct batadv_priv *bat_priv; struct batadv_hard_iface *primary_if = NULL;
@@ -315,6 +318,22 @@ batadv_hardif_activate_interface(struct batadv_hard_iface *hard_iface) if (!primary_if) batadv_primary_if_select(bat_priv, hard_iface);
- /* set the default values */
- hard_iface->bat_v.flags &= ~BATADV_FULL_DUPLEX;
- hard_iface->bat_v.eth_throughput = 0;
- if (dev->phydev) {
if (dev->phydev->duplex == DUPLEX_FULL)
hard_iface->bat_v.flags |= BATADV_FULL_DUPLEX;
/* set the speed according to the phydev setting. Store the
* value in Kbps (as done for the other throughput variables)
*/
if (dev->phydev->speed != SPEED_UNKNOWN) {
hard_iface->bat_v.eth_throughput = dev->phydev->speed;
hard_iface->bat_v.eth_throughput *= 10;
}
- }
For the record: I just moved this chunk into a proper bat_algo_ops->bat_iface_activate() API.
This initialisation is obviously B.A.T.M.A.N. V specific and cannot live in the batadv_hardif_activate_interface() function.
Cheers,
On 13/02/14 09:17, Antonio Quartulli wrote:
On 11/02/14 13:48, Antonio Quartulli wrote:
@@ -297,6 +299,7 @@ void batadv_update_min_mtu(struct net_device *soft_iface) static void batadv_hardif_activate_interface(struct batadv_hard_iface *hard_iface) {
- struct net_device *dev = hard_iface->net_dev; struct batadv_priv *bat_priv; struct batadv_hard_iface *primary_if = NULL;
@@ -315,6 +318,22 @@ batadv_hardif_activate_interface(struct batadv_hard_iface *hard_iface) if (!primary_if) batadv_primary_if_select(bat_priv, hard_iface);
- /* set the default values */
- hard_iface->bat_v.flags &= ~BATADV_FULL_DUPLEX;
- hard_iface->bat_v.eth_throughput = 0;
- if (dev->phydev) {
if (dev->phydev->duplex == DUPLEX_FULL)
hard_iface->bat_v.flags |= BATADV_FULL_DUPLEX;
/* set the speed according to the phydev setting. Store the
* value in Kbps (as done for the other throughput variables)
*/
And of course this is not Kbps but Mbps/10 (like everywhere else)..
if (dev->phydev->speed != SPEED_UNKNOWN) {
hard_iface->bat_v.eth_throughput = dev->phydev->speed;
hard_iface->bat_v.eth_throughput *= 10;
}
- }
For the record: I just moved this chunk into a proper bat_algo_ops->bat_iface_activate() API.
This initialisation is obviously B.A.T.M.A.N. V specific and cannot live in the batadv_hardif_activate_interface() function.
Cheers,
On Tue, Feb 11, 2014 at 01:48:18PM +0100, Antonio Quartulli wrote:
From: Antonio Quartulli antonio@open-mesh.com
The phydev member of a net_device can be used to get information about an ethernet link like HALF/FULL_DUPLEX and advertised bandwidth (e.g. 100/10Mbps).
This information are then stored in the hard_iface object to be used during the metric computation routine.
Signed-off-by: Antonio Quartulli antonio@open-mesh.com
bat_v_elp.c | 8 ++++++++ bat_v_ogm.c | 4 ++-- hard-interface.c | 19 +++++++++++++++++++ types.h | 12 ++++++++++++ 4 files changed, 41 insertions(+), 2 deletions(-)
diff --git a/bat_v_elp.c b/bat_v_elp.c index 763113e..958843c 100644 --- a/bat_v_elp.c +++ b/bat_v_elp.c @@ -78,6 +78,14 @@ batadv_v_elp_get_throughput(struct batadv_elp_neigh_node *neigh) if (throughput != 0) return throughput;
- /* In case of Ethernet interface, the throughput has already been
* obtained from the phydev object in the net_device struct (see
* batadv_hardif_activate_interface()). So return this value.
*/
- throughput = hard_iface->bat_v.eth_throughput;
- if (throughput != 0)
return throughput;
- /* if this is a wireless device, then ask its throughput through
*/
- cfg80211 API
diff --git a/bat_v_ogm.c b/bat_v_ogm.c index fa3d1a1..060ce80 100644 --- a/bat_v_ogm.c +++ b/bat_v_ogm.c @@ -393,8 +393,8 @@ static uint32_t batadv_v_penalty(struct batadv_priv *bat_priv, /* proportion to use the same value used in batman iv (x * 128 / 256) */ hop_penalty = hop_penalty * 100 / 255;
- if (batadv_is_wifi_netdev(if_incoming->net_dev) &&
metric > link_metric / 10)
if ((if_incoming->bat_v.flags & BATADV_FULL_DUPLEX) &&
(metric > link_metric / 10))
return metric / 2;
return metric * (100 - hop_penalty) / 100;
diff --git a/hard-interface.c b/hard-interface.c index 2a04130..58c8669 100644 --- a/hard-interface.c +++ b/hard-interface.c @@ -31,6 +31,8 @@
#include <linux/if_arp.h> #include <linux/if_ether.h> +#include <linux/netdevice.h> +#include <linux/phy.h>
void batadv_hardif_free_rcu(struct rcu_head *rcu) { @@ -297,6 +299,7 @@ void batadv_update_min_mtu(struct net_device *soft_iface) static void batadv_hardif_activate_interface(struct batadv_hard_iface *hard_iface) {
- struct net_device *dev = hard_iface->net_dev; struct batadv_priv *bat_priv; struct batadv_hard_iface *primary_if = NULL;
@@ -315,6 +318,22 @@ batadv_hardif_activate_interface(struct batadv_hard_iface *hard_iface) if (!primary_if) batadv_primary_if_select(bat_priv, hard_iface);
- /* set the default values */
- hard_iface->bat_v.flags &= ~BATADV_FULL_DUPLEX;
- hard_iface->bat_v.eth_throughput = 0;
- if (dev->phydev) {
if (dev->phydev->duplex == DUPLEX_FULL)
hard_iface->bat_v.flags |= BATADV_FULL_DUPLEX;
/* set the speed according to the phydev setting. Store the
* value in Kbps (as done for the other throughput variables)
*/
if (dev->phydev->speed != SPEED_UNKNOWN) {
hard_iface->bat_v.eth_throughput = dev->phydev->speed;
hard_iface->bat_v.eth_throughput *= 10;
}
- }
Hi Antonio
If i'm reading this correctly, you get the bandwidth once when the interface is added to bat0? Are we sure that auto-negotiation has finished? It can take a few seconds to complete after the interface is brought up. It would not be good to have batman use 10Mbps/Half Duplex on my gigabit links...
Is there a notification mechanism which could be used to keep these values up to date? Or is that in a patch i have not yet got to?
Andrew
On 13/02/14 11:52, Andrew Lunn wrote:
On Tue, Feb 11, 2014 at 01:48:18PM +0100, Antonio Quartulli wrote:
From: Antonio Quartulli antonio@open-mesh.com
The phydev member of a net_device can be used to get information about an ethernet link like HALF/FULL_DUPLEX and advertised bandwidth (e.g. 100/10Mbps).
This information are then stored in the hard_iface object to be used during the metric computation routine.
Signed-off-by: Antonio Quartulli antonio@open-mesh.com
bat_v_elp.c | 8 ++++++++ bat_v_ogm.c | 4 ++-- hard-interface.c | 19 +++++++++++++++++++ types.h | 12 ++++++++++++ 4 files changed, 41 insertions(+), 2 deletions(-)
diff --git a/bat_v_elp.c b/bat_v_elp.c index 763113e..958843c 100644 --- a/bat_v_elp.c +++ b/bat_v_elp.c @@ -78,6 +78,14 @@ batadv_v_elp_get_throughput(struct batadv_elp_neigh_node *neigh) if (throughput != 0) return throughput;
- /* In case of Ethernet interface, the throughput has already been
* obtained from the phydev object in the net_device struct (see
* batadv_hardif_activate_interface()). So return this value.
*/
- throughput = hard_iface->bat_v.eth_throughput;
- if (throughput != 0)
return throughput;
- /* if this is a wireless device, then ask its throughput through
*/
- cfg80211 API
diff --git a/bat_v_ogm.c b/bat_v_ogm.c index fa3d1a1..060ce80 100644 --- a/bat_v_ogm.c +++ b/bat_v_ogm.c @@ -393,8 +393,8 @@ static uint32_t batadv_v_penalty(struct batadv_priv *bat_priv, /* proportion to use the same value used in batman iv (x * 128 / 256) */ hop_penalty = hop_penalty * 100 / 255;
- if (batadv_is_wifi_netdev(if_incoming->net_dev) &&
metric > link_metric / 10)
if ((if_incoming->bat_v.flags & BATADV_FULL_DUPLEX) &&
(metric > link_metric / 10))
return metric / 2;
return metric * (100 - hop_penalty) / 100;
diff --git a/hard-interface.c b/hard-interface.c index 2a04130..58c8669 100644 --- a/hard-interface.c +++ b/hard-interface.c @@ -31,6 +31,8 @@
#include <linux/if_arp.h> #include <linux/if_ether.h> +#include <linux/netdevice.h> +#include <linux/phy.h>
void batadv_hardif_free_rcu(struct rcu_head *rcu) { @@ -297,6 +299,7 @@ void batadv_update_min_mtu(struct net_device *soft_iface) static void batadv_hardif_activate_interface(struct batadv_hard_iface *hard_iface) {
- struct net_device *dev = hard_iface->net_dev; struct batadv_priv *bat_priv; struct batadv_hard_iface *primary_if = NULL;
@@ -315,6 +318,22 @@ batadv_hardif_activate_interface(struct batadv_hard_iface *hard_iface) if (!primary_if) batadv_primary_if_select(bat_priv, hard_iface);
- /* set the default values */
- hard_iface->bat_v.flags &= ~BATADV_FULL_DUPLEX;
- hard_iface->bat_v.eth_throughput = 0;
- if (dev->phydev) {
if (dev->phydev->duplex == DUPLEX_FULL)
hard_iface->bat_v.flags |= BATADV_FULL_DUPLEX;
/* set the speed according to the phydev setting. Store the
* value in Kbps (as done for the other throughput variables)
*/
if (dev->phydev->speed != SPEED_UNKNOWN) {
hard_iface->bat_v.eth_throughput = dev->phydev->speed;
hard_iface->bat_v.eth_throughput *= 10;
}
- }
Hi Antonio
If i'm reading this correctly, you get the bandwidth once when the interface is added to bat0?
it is invoked every time the interface is brought up. So if you disconnect and then reconnect the cable (e.g. to connect the NIC to a faster switch) this function is invoked again and the bandwidth is updated.
Are we sure that auto-negotiation has finished? It can take a few seconds to complete after the interface is brought up. It would not be good to have batman use 10Mbps/Half Duplex on my gigabit links...
So it can take up to "few" seconds? I did not expect such a delay!
However this function if invoked when the NETDEV_UP event is issued by the underlying system. I expect the event to be thrown when the interface is ready to be used, not when the auto-negotiatin is still going on. I will double check.
Is there a notification mechanism which could be used to keep these values up to date? Or is that in a patch i have not yet got to.
No there is no notification mechanism.
I see only two ways of changing this value at runtime:
1) NIC disconnected and re-connected to a different "thing" -> NETDEV_UP is issued, we re-read the values
2) the user manually changes the bandwidth (e.g. using ethtool) -> not handled right now (I would also not consider it a high priority item), but maybe the NETDEV_CHANGE event is fired up in this case?
Cheers,
it is invoked every time the interface is brought up. So if you disconnect and then reconnect the cable (e.g. to connect the NIC to a faster switch) this function is invoked again and the bandwidth is updated.
Are we sure that auto-negotiation has finished? It can take a few seconds to complete after the interface is brought up. It would not be good to have batman use 10Mbps/Half Duplex on my gigabit links...
So it can take up to "few" seconds? I did not expect such a delay!
It varies a lot between devices. I have a NAS box which i use for kernel hacking. I TFTP boot the kernel, using u-boot configuration variables. I found that for a cold boot it works great, but a warm boot seems to be faster and the TFTP GET command is sent while the phy is still auto-negotiating, and it gets lost. This then triggers a bug in u-boot, it never re-transmits the GET command, and then whole TFTP boot times out eventually.
So i'm just cautious about making assumptions here, especially assumptions which might be mostly true, but in some odd edge case turn out to be false.
However this function if invoked when the NETDEV_UP event is issued by the underlying system. I expect the event to be thrown when the interface is ready to be used, not when the auto-negotiatin is still going on. I will double check.
I had a quick look at the code. It seems like NETDEV_UP is issued directly after the device is opened. There does not seem to be any waiting around for auto-neg.
Andrew
On 13/02/14 12:44, Andrew Lunn wrote:
it is invoked every time the interface is brought up. So if you disconnect and then reconnect the cable (e.g. to connect the NIC to a faster switch) this function is invoked again and the bandwidth is updated.
Are we sure that auto-negotiation has finished? It can take a few seconds to complete after the interface is brought up. It would not be good to have batman use 10Mbps/Half Duplex on my gigabit links...
So it can take up to "few" seconds? I did not expect such a delay!
It varies a lot between devices. I have a NAS box which i use for kernel hacking. I TFTP boot the kernel, using u-boot configuration variables. I found that for a cold boot it works great, but a warm boot seems to be faster and the TFTP GET command is sent while the phy is still auto-negotiating, and it gets lost. This then triggers a bug in u-boot, it never re-transmits the GET command, and then whole TFTP boot times out eventually.
So i'm just cautious about making assumptions here, especially assumptions which might be mostly true, but in some odd edge case turn out to be false.
However this function if invoked when the NETDEV_UP event is issued by the underlying system. I expect the event to be thrown when the interface is ready to be used, not when the auto-negotiatin is still going on. I will double check.
I had a quick look at the code. It seems like NETDEV_UP is issued directly after the device is opened. There does not seem to be any waiting around for auto-neg.
Hi Andrew,
This morning I've been trying to dig into the tg3 Ethernet driver to get an idea of what happens before the NETDEV_UP event is fired.
The network stack invokes the ndo_open() callback implemented by the driver and, as far as I can see, this function does not return until having set speed and half/full_duplex state. Thus it looks like that when NETDEV_UP is fired the speed and the full_duplex attribute have already a meaningful value.
Of course this may vary depending on the driver, so this may not be the case everywhere.
I think that we can keep this approach for now and then perform some tests on a couple of platforms before merging the code. This way we should be "sure" about this particular behaviour.
I think that your NAS might also be a good testing platform, assuming that the owner is willing to insmod batman-adv on that machine :-)
Cheers,
Hi Andrew,
This morning I've been trying to dig into the tg3 Ethernet driver to get an idea of what happens before the NETDEV_UP event is fired.
The network stack invokes the ndo_open() callback implemented by the driver and, as far as I can see, this function does not return until having set speed and half/full_duplex state. Thus it looks like that when NETDEV_UP is fired the speed and the full_duplex attribute have already a meaningful value.
Of course this may vary depending on the driver, so this may not be the case everywhere.
Hi Antonio
I just looked at mv643xx_eth.c, the ethernet driver for my NAS box. Its ndo_open() function calls phy_start(), but does not wait around for the auto-negotiation to complete.
I hacked together a quick test.
#!/bin/bash ifconfig eth0 down sleep 2 ifconfig eth0 up cat /sys/class/net/eth0/duplex cat /sys/class/net/eth0/speed sleep 0.5 cat /sys/class/net/eth0/duplex cat /sys/class/net/eth0/speed sleep 0.5 cat /sys/class/net/eth0/duplex cat /sys/class/net/eth0/speed sleep 0.5 cat /sys/class/net/eth0/duplex cat /sys/class/net/eth0/speed sleep 0.5 cat /sys/class/net/eth0/duplex cat /sys/class/net/eth0/speed sleep 0.5 cat /sys/class/net/eth0/duplex cat /sys/class/net/eth0/speed sleep 0.5 cat /sys/class/net/eth0/duplex cat /sys/class/net/eth0/speed
Which when run gives:
half 1000 half 1000 half 1000 half 1000 half 1000 half 1000 full 1000
So it is taking 3 seconds after ifconfig returns before it has the right duplex mode.
I think that your NAS might also be a good testing platform, assuming that the owner is willing to insmod batman-adv on that machine :-)
I've no problems with batman on this NAS box. It is a box dedicated to kernel hacking and experienced many an opps. I currently have 3.13-rc1 on it, which happens to break the ethernet PHY on reboot :-(
Andrew
On 14/02/14 18:38, Andrew Lunn wrote:
Hi Andrew,
This morning I've been trying to dig into the tg3 Ethernet driver to get an idea of what happens before the NETDEV_UP event is fired.
The network stack invokes the ndo_open() callback implemented by the driver and, as far as I can see, this function does not return until having set speed and half/full_duplex state. Thus it looks like that when NETDEV_UP is fired the speed and the full_duplex attribute have already a meaningful value.
Of course this may vary depending on the driver, so this may not be the case everywhere.
Hi Antonio
Hi Andrew,
I just looked at mv643xx_eth.c, the ethernet driver for my NAS box. Its ndo_open() function calls phy_start(), but does not wait around for the auto-negotiation to complete.
oh ok..then it seems we really need to get some kind of notification for this..
I hacked together a quick test.
#!/bin/bash ifconfig eth0 down sleep 2 ifconfig eth0 up cat /sys/class/net/eth0/duplex cat /sys/class/net/eth0/speed sleep 0.5 cat /sys/class/net/eth0/duplex cat /sys/class/net/eth0/speed sleep 0.5 cat /sys/class/net/eth0/duplex cat /sys/class/net/eth0/speed sleep 0.5 cat /sys/class/net/eth0/duplex cat /sys/class/net/eth0/speed sleep 0.5 cat /sys/class/net/eth0/duplex cat /sys/class/net/eth0/speed sleep 0.5 cat /sys/class/net/eth0/duplex cat /sys/class/net/eth0/speed sleep 0.5 cat /sys/class/net/eth0/duplex cat /sys/class/net/eth0/speed
Which when run gives:
half 1000 half 1000 half 1000 half 1000 half 1000 half 1000 full 1000
So it is taking 3 seconds after ifconfig returns before it has the right duplex mode.
Are you 100% sure that the NETDEV_UP event was sent within those 3 seconds and not after? If you use a serial console you should see the kernel output mixed with your test. (just to be 100% sure..)
I think that your NAS might also be a good testing platform, assuming that the owner is willing to insmod batman-adv on that machine :-)
I've no problems with batman on this NAS box. It is a box dedicated to kernel hacking and experienced many an opps. I currently have 3.13-rc1 on it, which happens to break the ethernet PHY on reboot :-(
you shoul dmove to 3.13.3 ;)
Cheers,
Are you 100% sure that the NETDEV_UP event was sent within those 3 seconds and not after? If you use a serial console you should see the kernel output mixed with your test. (just to be 100% sure..)
http://lxr.free-electrons.com/source/net/core/dev.c#L1286
1286 int dev_open(struct net_device *dev) 1287 { 1288 int ret; 1289 1290 if (dev->flags & IFF_UP) 1291 return 0; 1292 1293 ret = __dev_open(dev); 1294 if (ret < 0) 1295 return ret; 1296 1297 rtmsg_ifinfo(RTM_NEWLINK, dev, IFF_UP|IFF_RUNNING, GFP_KERNEL); 1298 call_netdevice_notifiers(NETDEV_UP, dev); 1299 1300 return ret; 1301 }
So the NETDEV_UP is sent as soon as __dev_open() returns, i.e. ndo_open().
What i do see is:
half 1000 half 1000 half 1000 half 1000 half 1000 half 1000 mv643xx_eth_port mv643xx_eth_port.0 eth0: link up, 1000 Mb/s, full duplex, flowd full 1000
The link up is from:
http://lxr.free-electrons.com/source/drivers/net/ethernet/marvell/mv643xx_et...
There is no sign of a call_netdevice_notifiers() here, but i think a netlink message will be sent to userspace because of the netif_carrier_on() call.
Andrew
On 14/02/14 19:18, Andrew Lunn wrote:
Are you 100% sure that the NETDEV_UP event was sent within those 3 seconds and not after? If you use a serial console you should see the kernel output mixed with your test. (just to be 100% sure..)
http://lxr.free-electrons.com/source/net/core/dev.c#L1286
1286 int dev_open(struct net_device *dev) 1287 { 1288 int ret; 1289 1290 if (dev->flags & IFF_UP) 1291 return 0; 1292 1293 ret = __dev_open(dev); 1294 if (ret < 0) 1295 return ret; 1296 1297 rtmsg_ifinfo(RTM_NEWLINK, dev, IFF_UP|IFF_RUNNING, GFP_KERNEL); 1298 call_netdevice_notifiers(NETDEV_UP, dev); 1299 1300 return ret; 1301 }
So the NETDEV_UP is sent as soon as __dev_open() returns, i.e. ndo_open().
Yeah, I hoped that the negotiation was performed within dev_open() :-(
What i do see is:
half 1000 half 1000 half 1000 half 1000 half 1000 half 1000 mv643xx_eth_port mv643xx_eth_port.0 eth0: link up, 1000 Mb/s, full duplex, flowd full 1000
The link up is from:
http://lxr.free-electrons.com/source/drivers/net/ethernet/marvell/mv643xx_et...
There is no sign of a call_netdevice_notifiers() here, but i think a netlink message will be sent to userspace because of the netif_carrier_on() call.
I tried to follow netif_carrier_on() but I couldn't find any particular function firing any event.
Maybe I should simply make it simple and read the Ethernet card properties inside get_throughput() upon ELP sending (like I do for the wifi throughput)?
I did put it in iface_activate() because I expected this values to be rather static, but now you convinced me that this is not the case :-)
Cheers,
From: Antonio Quartulli antonio@open-mesh.com
This API has to be used to let any routing protocol free neighbor specific allocated resources
Signed-off-by: Antonio Quartulli antonio@open-mesh.com --- originator.c | 6 ++++++ types.h | 2 ++ 2 files changed, 8 insertions(+)
diff --git a/originator.c b/originator.c index 447f417..5670df8 100644 --- a/originator.c +++ b/originator.c @@ -196,13 +196,19 @@ static void batadv_neigh_node_free_rcu(struct rcu_head *rcu) struct hlist_node *node_tmp; struct batadv_neigh_node *neigh_node; struct batadv_neigh_ifinfo *neigh_ifinfo; + struct batadv_algo_ops *bao;
neigh_node = container_of(rcu, struct batadv_neigh_node, rcu); + bao = neigh_node->orig_node->bat_priv->bat_algo_ops;
hlist_for_each_entry_safe(neigh_ifinfo, node_tmp, &neigh_node->ifinfo_list, list) { batadv_neigh_ifinfo_free_ref_now(neigh_ifinfo); } + + if (bao->bat_neigh_free) + bao->bat_neigh_free(neigh_node); + batadv_hardif_free_ref_now(neigh_node->if_incoming);
kfree(neigh_node); diff --git a/types.h b/types.h index a51921e..df1479e 100644 --- a/types.h +++ b/types.h @@ -1203,6 +1203,8 @@ struct batadv_algo_ops { int max_if_num); int (*bat_orig_del_if)(struct batadv_orig_node *orig_node, int max_if_num, int del_if_num); + /* neigh_node handling API */ + void (*bat_neigh_free)(struct batadv_neigh_node *neigh); };
/**
From: Antonio Quartulli antonio@open-mesh.com
Signed-off-by: Antonio Quartulli antonio@open-mesh.com --- bat_v.c | 7 +++++++ 1 file changed, 7 insertions(+)
diff --git a/bat_v.c b/bat_v.c index 5681d19..3e4575d 100644 --- a/bat_v.c +++ b/bat_v.c @@ -63,6 +63,12 @@ static void batadv_v_ogm_emit(struct batadv_forw_packet *forw_packet) { }
+static void batadv_v_neigh_free(struct batadv_neigh_node *neigh) +{ + batadv_elp_neigh_node_free_ref(neigh->bat_v.elp_neigh); + neigh->bat_v.elp_neigh = NULL; +} + static struct batadv_algo_ops batadv_batman_v __read_mostly = { .name = "BATMAN_V", .bat_iface_enable = batadv_v_iface_enable, @@ -71,6 +77,7 @@ static struct batadv_algo_ops batadv_batman_v __read_mostly = { .bat_primary_iface_set = batadv_v_primary_iface_set, .bat_ogm_emit = batadv_v_ogm_emit, .bat_ogm_schedule = batadv_v_ogm_schedule, + .bat_neigh_free = batadv_v_neigh_free, };
int batadv_v_mesh_init(struct batadv_priv *bat_priv)
From: Antonio Quartulli antonio@open-mesh.com
Signed-off-by: Antonio Quartulli antonio@open-mesh.com --- bat_v.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+)
diff --git a/bat_v.c b/bat_v.c index 3e4575d..70ff9e9 100644 --- a/bat_v.c +++ b/bat_v.c @@ -21,6 +21,7 @@ #include "bat_algo.h" #include "bat_v_elp.h" #include "bat_v_ogm.h" +#include "originator.h"
static int batadv_v_iface_enable(struct batadv_hard_iface *hard_iface) { @@ -63,6 +64,23 @@ static void batadv_v_ogm_emit(struct batadv_forw_packet *forw_packet) { }
+static bool batadv_v_neigh_is_eob(struct batadv_neigh_node *neigh1, + struct batadv_hard_iface *if_outgoing1, + struct batadv_neigh_node *neigh2, + struct batadv_hard_iface *if_outgoing2) +{ + struct batadv_neigh_ifinfo *ifinfo1, *ifinfo2; + uint32_t threshold; + + ifinfo1 = batadv_neigh_ifinfo_get(neigh1, if_outgoing1); + ifinfo2 = batadv_neigh_ifinfo_get(neigh2, if_outgoing2); + + threshold = ifinfo1->bat_v.metric << 8 >> 10; + threshold = ifinfo1->bat_v.metric - threshold; + + return ifinfo2->bat_v.metric > threshold; +} + static void batadv_v_neigh_free(struct batadv_neigh_node *neigh) { batadv_elp_neigh_node_free_ref(neigh->bat_v.elp_neigh); @@ -77,6 +95,7 @@ static struct batadv_algo_ops batadv_batman_v __read_mostly = { .bat_primary_iface_set = batadv_v_primary_iface_set, .bat_ogm_emit = batadv_v_ogm_emit, .bat_ogm_schedule = batadv_v_ogm_schedule, + .bat_neigh_is_equiv_or_better = batadv_v_neigh_is_eob, .bat_neigh_free = batadv_v_neigh_free, };
Signed-off-by: Antonio Quartulli antonio@meshcoding.com --- bat_v.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+)
diff --git a/bat_v.c b/bat_v.c index 70ff9e9..d05d548 100644 --- a/bat_v.c +++ b/bat_v.c @@ -64,6 +64,23 @@ static void batadv_v_ogm_emit(struct batadv_forw_packet *forw_packet) { }
+static int batadv_v_neigh_cmp(struct batadv_neigh_node *neigh1, + struct batadv_hard_iface *if_outgoing1, + struct batadv_neigh_node *neigh2, + struct batadv_hard_iface *if_outgoing2) +{ + struct batadv_neigh_ifinfo *ifinfo1, *ifinfo2; + + ifinfo1 = batadv_neigh_ifinfo_get(neigh1, if_outgoing1); + ifinfo2 = batadv_neigh_ifinfo_get(neigh2, if_outgoing2); + + if (WARN_ON(!ifinfo1 || !ifinfo2)) + return 0; + + return ifinfo1->bat_v.metric - ifinfo2->bat_v.metric; + +} + static bool batadv_v_neigh_is_eob(struct batadv_neigh_node *neigh1, struct batadv_hard_iface *if_outgoing1, struct batadv_neigh_node *neigh2, @@ -95,6 +112,7 @@ static struct batadv_algo_ops batadv_batman_v __read_mostly = { .bat_primary_iface_set = batadv_v_primary_iface_set, .bat_ogm_emit = batadv_v_ogm_emit, .bat_ogm_schedule = batadv_v_ogm_schedule, + .bat_neigh_cmp = batadv_v_neigh_cmp, .bat_neigh_is_equiv_or_better = batadv_v_neigh_is_eob, .bat_neigh_free = batadv_v_neigh_free, };
From: Antonio Quartulli antonio@open-mesh.com
Signed-off-by: Antonio Quartulli antonio@open-mesh.com --- bat_v.c | 98 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+)
diff --git a/bat_v.c b/bat_v.c index d05d548..6b21afe 100644 --- a/bat_v.c +++ b/bat_v.c @@ -64,6 +64,103 @@ static void batadv_v_ogm_emit(struct batadv_forw_packet *forw_packet) { }
+/** + * batadv_v_orig_print_neigh - print neighbors for the originator table + * @orig_node: the orig_node for which the neighbors are printed + * @if_outgoing: outgoing interface for these entries + * @seq: debugfs table seq_file struct + * + * Must be called while holding an rcu lock. + */ +static void +batadv_v_orig_print_neigh(struct batadv_orig_node *orig_node, + struct batadv_hard_iface *if_outgoing, + struct seq_file *seq) +{ + struct batadv_neigh_node *neigh_node; + struct batadv_neigh_ifinfo *n_ifinfo; + + hlist_for_each_entry_rcu(neigh_node, &orig_node->neigh_list, list) { + n_ifinfo = batadv_neigh_ifinfo_get(neigh_node, if_outgoing); + if (!n_ifinfo) + continue; + + seq_printf(seq, " %pM (%9u.%1u)", + neigh_node->addr, + n_ifinfo->bat_v.metric / 10, + n_ifinfo->bat_v.metric % 10); + + batadv_neigh_ifinfo_free_ref(n_ifinfo); + } +} + +/** + * batadv_v_orig_print - print the originator table + * @bat_priv: the bat priv with all the soft interface information + * @seq: debugfs table seq_file struct + * @if_outgoing: the outgoing interface for which this should be printed + */ +static void batadv_v_orig_print(struct batadv_priv *bat_priv, + struct seq_file *seq, + struct batadv_hard_iface *if_outgoing) +{ + struct batadv_neigh_node *neigh_node; + struct batadv_hashtable *hash = bat_priv->orig_hash; + int last_seen_msecs, last_seen_secs; + struct batadv_orig_node *orig_node; + struct batadv_neigh_ifinfo *n_ifinfo; + unsigned long last_seen_jiffies; + struct hlist_head *head; + int batman_count = 0; + uint32_t i; + + seq_printf(seq, " %-15s %s (%11s) %17s [%10s]: %20s ...\n", + "Originator", "last-seen", "metric", "Nexthop", + "outgoingIF", "Potential nexthops"); + + for (i = 0; i < hash->size; i++) { + head = &hash->table[i]; + + rcu_read_lock(); + hlist_for_each_entry_rcu(orig_node, head, hash_entry) { + neigh_node = batadv_orig_router_get(orig_node, + if_outgoing); + if (!neigh_node) + continue; + + n_ifinfo = batadv_neigh_ifinfo_get(neigh_node, + if_outgoing); + if (!n_ifinfo) + goto next; + + last_seen_jiffies = jiffies - orig_node->last_seen; + last_seen_msecs = jiffies_to_msecs(last_seen_jiffies); + last_seen_secs = last_seen_msecs / 1000; + last_seen_msecs = last_seen_msecs % 1000; + + seq_printf(seq, "%pM %4i.%03is (%9u.%1u) %pM [%10s]:", + orig_node->orig, last_seen_secs, + last_seen_msecs, n_ifinfo->bat_v.metric / 10, + n_ifinfo->bat_v.metric % 10, + neigh_node->addr, + neigh_node->if_incoming->net_dev->name); + + batadv_v_orig_print_neigh(orig_node, if_outgoing, seq); + seq_puts(seq, "\n"); + batman_count++; + +next: + batadv_neigh_node_free_ref(neigh_node); + if (n_ifinfo) + batadv_neigh_ifinfo_free_ref(n_ifinfo); + } + rcu_read_unlock(); + } + + if (batman_count == 0) + seq_puts(seq, "No batman nodes in range ...\n"); +} + static int batadv_v_neigh_cmp(struct batadv_neigh_node *neigh1, struct batadv_hard_iface *if_outgoing1, struct batadv_neigh_node *neigh2, @@ -112,6 +209,7 @@ static struct batadv_algo_ops batadv_batman_v __read_mostly = { .bat_primary_iface_set = batadv_v_primary_iface_set, .bat_ogm_emit = batadv_v_ogm_emit, .bat_ogm_schedule = batadv_v_ogm_schedule, + .bat_orig_print = batadv_v_orig_print, .bat_neigh_cmp = batadv_v_neigh_cmp, .bat_neigh_is_equiv_or_better = batadv_v_neigh_is_eob, .bat_neigh_free = batadv_v_neigh_free,
b.a.t.m.a.n@lists.open-mesh.org