This cleanup patchset includes the following patches:

- drop hardif global list, by Nora Schiffer (2 patches)
 
  - make hard_iface->mesh_iface immutable, by Sven Eckelmann
 
  - further post-hardif global list cleanups,
    by Nora Schiffer (3 patches)
 
  - cleanups and simplifications depending on the hardif->mesh_iface
    immutability guarantee, by Sven Eckelmann (3 patches)
 
  - tvlv: extract tvlv header iterator, by Sven Eckelmann
 
  - tp_meter: improve unacked list handling,
    by Sven Eckelmann (5 patches)
 -----BEGIN PGP SIGNATURE-----
 
 iQJKBAABCgA0FiEE1ilQI7G+y+fdhnrfoSvjmEKSnqEFAmpDzOAWHHN3QHNpbW9u
 d3VuZGVybGljaC5kZQAKCRChK+OYQpKeod9SD/wJfdheMNGfp4H+drVIsf4eusM6
 xmdTR5we2dhvfllb4aPQsEQ9TUQgzYU7j8VfUkQ5G8cryqbR7/s5YSTivWY09CN5
 ecqgRSOHRrzZGDmsfnXykKMzIhw/7JW7ytRXoH1OagwyN38byFy8df+W7EuWx9mn
 oQv/Fxhej+zmq/wOyORgaUJO/35otEW2m95eepE292uPZw4Tyxfh1dYBufQhJCyU
 XC0vSe77AcHN6JeiY9kVD8p6rlAU7ZM0inZx6ZdUPnbaYRSDri9wCFjQTGgogoXt
 8uQPuTY4fcba7ljnKxG124xXghq5U18hJfPT6xi8oNTlCKm4MKKt8Z6YudDzGqXh
 YRj/abFuTU8FxoOi0NnrZqFEL9lGDeHv7/j+BEKcpRLPc0DnMWwlgDRVUcPKGntE
 5qA7c0+/2DPLOfohIvcR9cCikWozXDLDjzkkW19Jt3HAAaApr27V3NsKcV9TqYMj
 DtmjSPR9H7sxGrCKJcfNVuE8bctVKKlF/uFLflBDKWq8XU+6Eth++C6ZKd1oCiKT
 drvsV44UWK2mQHTBYs3XwlX7XW+6tvTlpImmv1sarBGdGUKQcKgzxtmImeYGtXHd
 r6Yrc1Pqqe2SdnSOVEG3c3hxWKn1ckDNF7lC3y0lCFWYKP32FsLrfmHl7YtbnXWV
 w8o7IrYlPGAJn4NhvQ==
 =TykP
 -----END PGP SIGNATURE-----

Merge tag 'batadv-next-pullrequest-20260630' of https://git.open-mesh.org/batadv

Simon Wunderlich says:

====================
This cleanup patchset includes the following patches:

 - drop hardif global list, by Nora Schiffer (2 patches)

 - make hard_iface->mesh_iface immutable, by Sven Eckelmann

 - further post-hardif global list cleanups,
   by Nora Schiffer (3 patches)

 - cleanups and simplifications depending on the hardif->mesh_iface
   immutability guarantee, by Sven Eckelmann (3 patches)

 - tvlv: extract tvlv header iterator, by Sven Eckelmann

 - tp_meter: improve unacked list handling,
   by Sven Eckelmann (5 patches)

* tag 'batadv-next-pullrequest-20260630' of https://git.open-mesh.org/batadv:
  batman-adv: tp_meter: delay allocation of unacked entry
  batman-adv: tp_meter: adjust name of receiver lock
  batman-adv: tp_meter: keep unacked list for receivers
  batman-adv: tp_meter: combine adjacent/overlapping unacked entries
  batman-adv: tp_meter: simplify unordered ack calculation
  batman-adv: tvlv: extract tvlv header iterator
  batman-adv: iv: drop migration check for batadv_hard_iface
  Revert "batman-adv: v: stop OGMv2 on disabled interface"
  batman-adv: drop NULL check for immutable hardif->mesh_iface
  batman-adv: drop unneeded goto and initialization from batadv_hardif_disable_interface()
  batman-adv: move hardif generation counter into batadv_priv
  batman-adv: remove BATADV_IF_NOT_IN_USE hardif state
  batman-adv: make hard_iface->mesh_iface immutable
  batman-adv: remove global hardif list
  batman-adv: create hardif only for netdevs that are part of a mesh
====================

Link: https://patch.msgid.link/
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
Paolo Abeni 2026-07-02 10:30:26 +02:00
commit f07f1e3ded
14 changed files with 272 additions and 328 deletions

View File

@ -404,23 +404,14 @@ static void batadv_iv_ogm_send_to_if(struct batadv_forw_packet *forw_packet,
/* send a batman ogm packet */
static void batadv_iv_ogm_emit(struct batadv_forw_packet *forw_packet)
{
struct net_device *mesh_iface;
if (!forw_packet->if_incoming) {
pr_err("Error - can't forward packet: incoming iface not specified\n");
return;
}
mesh_iface = forw_packet->if_incoming->mesh_iface;
if (WARN_ON(!forw_packet->if_outgoing))
return;
if (forw_packet->if_outgoing->mesh_iface != mesh_iface) {
pr_warn("%s: mesh interface switch for queued OGM\n", __func__);
return;
}
if (forw_packet->if_incoming->if_status != BATADV_IF_ACTIVE)
return;
@ -910,8 +901,7 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface)
static void batadv_iv_ogm_schedule(struct batadv_hard_iface *hard_iface)
{
if (hard_iface->if_status == BATADV_IF_NOT_IN_USE ||
hard_iface->if_status == BATADV_IF_TO_BE_REMOVED)
if (hard_iface->if_status == BATADV_IF_TO_BE_REMOVED)
return;
mutex_lock(&hard_iface->bat_iv.ogm_buff_mutex);

View File

@ -90,12 +90,6 @@ static bool batadv_v_elp_get_throughput(struct batadv_hardif_neigh_node *neigh,
u32 throughput;
int ret;
/* don't query throughput when no longer associated with any
* batman-adv interface
*/
if (!mesh_iface)
return false;
/* if the user specified a customised value for this interface, then
* return it directly
*/
@ -311,8 +305,7 @@ static void batadv_v_elp_periodic_work(struct work_struct *work)
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)
if (hard_iface->if_status == BATADV_IF_TO_BE_REMOVED)
goto out;
/* the interface was enabled but may not be ready yet */

View File

@ -115,14 +115,14 @@ static void batadv_v_ogm_start_timer(struct batadv_priv *bat_priv)
/**
* batadv_v_ogm_send_to_if() - send a batman ogm using a given interface
* @bat_priv: the bat priv with all the mesh interface information
* @skb: the OGM to send
* @hard_iface: the interface to use to send the OGM
*/
static void batadv_v_ogm_send_to_if(struct batadv_priv *bat_priv,
struct sk_buff *skb,
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->mesh_iface);
if (hard_iface->if_status != BATADV_IF_ACTIVE) {
kfree_skb(skb);
return;
@ -189,7 +189,6 @@ static void batadv_v_ogm_aggr_list_free(struct batadv_hard_iface *hard_iface)
/**
* batadv_v_ogm_aggr_send() - flush & send aggregation queue
* @bat_priv: the bat priv with all the mesh interface information
* @hard_iface: the interface with the aggregation queue to flush
*
* Aggregates all OGMv2 packets currently in the aggregation queue into a
@ -199,8 +198,7 @@ static void batadv_v_ogm_aggr_list_free(struct batadv_hard_iface *hard_iface)
*
* Caller needs to hold the hard_iface->bat_v.aggr_list.lock.
*/
static void batadv_v_ogm_aggr_send(struct batadv_priv *bat_priv,
struct batadv_hard_iface *hard_iface)
static void batadv_v_ogm_aggr_send(struct batadv_hard_iface *hard_iface)
{
unsigned int aggr_len = hard_iface->bat_v.aggr_len;
struct sk_buff *skb_aggr;
@ -230,26 +228,21 @@ static void batadv_v_ogm_aggr_send(struct batadv_priv *bat_priv,
consume_skb(skb);
}
batadv_v_ogm_send_to_if(bat_priv, skb_aggr, hard_iface);
batadv_v_ogm_send_to_if(skb_aggr, hard_iface);
}
/**
* batadv_v_ogm_queue_on_if() - queue a batman ogm on a given interface
* @bat_priv: the bat priv with all the mesh interface information
* @skb: the OGM to queue
* @hard_iface: the interface to queue the OGM on
*/
static void batadv_v_ogm_queue_on_if(struct batadv_priv *bat_priv,
struct sk_buff *skb,
static void batadv_v_ogm_queue_on_if(struct sk_buff *skb,
struct batadv_hard_iface *hard_iface)
{
if (hard_iface->mesh_iface != bat_priv->mesh_iface) {
kfree_skb(skb);
return;
}
struct batadv_priv *bat_priv = netdev_priv(hard_iface->mesh_iface);
if (!READ_ONCE(bat_priv->aggregated_ogms)) {
batadv_v_ogm_send_to_if(bat_priv, skb, hard_iface);
batadv_v_ogm_send_to_if(skb, hard_iface);
return;
}
@ -260,7 +253,7 @@ static void batadv_v_ogm_queue_on_if(struct batadv_priv *bat_priv,
}
if (!batadv_v_ogm_queue_left(skb, hard_iface))
batadv_v_ogm_aggr_send(bat_priv, hard_iface);
batadv_v_ogm_aggr_send(hard_iface);
hard_iface->bat_v.aggr_len += batadv_v_ogm_len(skb);
__skb_queue_tail(&hard_iface->bat_v.aggr_list, skb);
@ -357,7 +350,7 @@ static void batadv_v_ogm_send_meshif(struct batadv_priv *bat_priv)
break;
}
batadv_v_ogm_queue_on_if(bat_priv, skb_tmp, hard_iface);
batadv_v_ogm_queue_on_if(skb_tmp, hard_iface);
batadv_hardif_put(hard_iface);
}
rcu_read_unlock();
@ -397,14 +390,12 @@ void batadv_v_ogm_aggr_work(struct work_struct *work)
{
struct batadv_hard_iface_bat_v *batv;
struct batadv_hard_iface *hard_iface;
struct batadv_priv *bat_priv;
batv = container_of(work, struct batadv_hard_iface_bat_v, aggr_wq.work);
hard_iface = container_of(batv, struct batadv_hard_iface, bat_v);
bat_priv = netdev_priv(hard_iface->mesh_iface);
spin_lock_bh(&hard_iface->bat_v.aggr_list.lock);
batadv_v_ogm_aggr_send(bat_priv, hard_iface);
batadv_v_ogm_aggr_send(hard_iface);
spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock);
batadv_v_ogm_start_queue_timer(hard_iface);
@ -601,7 +592,7 @@ static void batadv_v_ogm_forward(struct batadv_priv *bat_priv,
if_outgoing->net_dev->name, ntohl(ogm_forward->throughput),
ogm_forward->ttl, if_incoming->net_dev->name);
batadv_v_ogm_queue_on_if(bat_priv, skb, if_outgoing);
batadv_v_ogm_queue_on_if(skb, if_outgoing);
out:
batadv_orig_ifinfo_put(orig_ifinfo);

View File

@ -344,7 +344,6 @@ static void batadv_bla_send_claim(struct batadv_priv *bat_priv, const u8 *mac,
struct sk_buff *skb;
struct ethhdr *ethhdr;
struct batadv_hard_iface *primary_if;
struct net_device *mesh_iface;
u8 *hw_src;
struct batadv_bla_claim_dst local_claim_dest;
__be32 zeroip = 0;
@ -357,14 +356,10 @@ static void batadv_bla_send_claim(struct batadv_priv *bat_priv, const u8 *mac,
sizeof(local_claim_dest));
local_claim_dest.type = claimtype;
mesh_iface = READ_ONCE(primary_if->mesh_iface);
if (!mesh_iface)
goto out;
skb = arp_create(ARPOP_REPLY, ETH_P_ARP,
/* IP DST: 0.0.0.0 */
zeroip,
mesh_iface,
primary_if->mesh_iface,
/* IP SRC: 0.0.0.0 */
zeroip,
/* Ethernet DST: Broadcast */
@ -442,7 +437,7 @@ static void batadv_bla_send_claim(struct batadv_priv *bat_priv, const u8 *mac,
}
skb_reset_mac_header(skb);
skb->protocol = eth_type_trans(skb, mesh_iface);
skb->protocol = eth_type_trans(skb, primary_if->mesh_iface);
batadv_inc_counter(bat_priv, BATADV_CNT_RX);
batadv_add_counter(bat_priv, BATADV_CNT_RX_BYTES,
skb->len + ETH_HLEN);

View File

@ -63,6 +63,7 @@ void batadv_hardif_release(struct kref *ref)
struct batadv_hard_iface *hard_iface;
hard_iface = container_of(ref, struct batadv_hard_iface, refcount);
netdev_put(hard_iface->mesh_iface, &hard_iface->meshif_dev_tracker);
netdev_put(hard_iface->net_dev, &hard_iface->dev_tracker);
kfree_rcu(hard_iface, rcu);
@ -75,21 +76,21 @@ void batadv_hardif_release(struct kref *ref)
* Return: batadv_hard_iface of net_dev (with increased refcnt), NULL on errors
*/
struct batadv_hard_iface *
batadv_hardif_get_by_netdev(const struct net_device *net_dev)
batadv_hardif_get_by_netdev(struct net_device *net_dev)
{
struct batadv_hard_iface *hard_iface;
struct net_device *mesh_iface;
rcu_read_lock();
list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) {
if (hard_iface->net_dev == net_dev &&
kref_get_unless_zero(&hard_iface->refcount))
goto out;
}
ASSERT_RTNL();
hard_iface = NULL;
mesh_iface = netdev_master_upper_dev_get(net_dev);
if (!mesh_iface || !batadv_meshif_is_valid(mesh_iface))
return NULL;
hard_iface = netdev_lower_dev_get_private(mesh_iface, net_dev);
if (!kref_get_unless_zero(&hard_iface->refcount))
return NULL;
out:
rcu_read_unlock();
return hard_iface;
}
@ -245,7 +246,7 @@ struct net_device *__batadv_get_real_netdev(struct net_device *netdev)
}
hard_iface = batadv_hardif_get_by_netdev(netdev);
if (!hard_iface || !hard_iface->mesh_iface)
if (!hard_iface)
goto out;
net = dev_net(hard_iface->mesh_iface);
@ -539,16 +540,10 @@ static void batadv_check_known_mac_addr(const struct batadv_hard_iface *hard_ifa
const struct batadv_hard_iface *tmp_hard_iface;
struct list_head *iter;
if (!mesh_iface)
return;
netdev_for_each_lower_private(mesh_iface, tmp_hard_iface, iter) {
if (tmp_hard_iface == hard_iface)
continue;
if (tmp_hard_iface->if_status == BATADV_IF_NOT_IN_USE)
continue;
if (!batadv_compare_eth(tmp_hard_iface->net_dev->dev_addr,
hard_iface->net_dev->dev_addr))
continue;
@ -574,9 +569,6 @@ static void batadv_hardif_recalc_extra_skbroom(struct net_device *mesh_iface)
rcu_read_lock();
netdev_for_each_lower_private_rcu(mesh_iface, hard_iface, iter) {
if (hard_iface->if_status == BATADV_IF_NOT_IN_USE)
continue;
lower_header_len = max_t(unsigned short, lower_header_len,
hard_iface->net_dev->hard_header_len);
@ -723,38 +715,63 @@ batadv_hardif_deactivate_interface(struct batadv_hard_iface *hard_iface)
}
/**
* batadv_hardif_enable_interface() - Enslave hard interface to mesh interface
* @hard_iface: hard interface to add to mesh interface
* batadv_hardif_enable_interface() - Enslave interface to mesh interface
* @net_dev: netdev struct of the interface to add to mesh interface
* @mesh_iface: netdev struct of the mesh interface
*
* Return: 0 on success or negative error number in case of failure
*/
int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,
int batadv_hardif_enable_interface(struct net_device *net_dev,
struct net_device *mesh_iface)
{
struct batadv_priv *bat_priv;
__be16 ethertype = htons(ETH_P_BATMAN);
int max_header_len = batadv_max_header_len();
struct batadv_hard_iface *hard_iface;
unsigned int required_mtu;
unsigned int hardif_mtu;
bool fragmentation;
int ret;
hardif_mtu = READ_ONCE(hard_iface->net_dev->mtu);
ASSERT_RTNL();
if (!batadv_is_valid_iface(net_dev))
return -EINVAL;
hardif_mtu = READ_ONCE(net_dev->mtu);
required_mtu = READ_ONCE(mesh_iface->mtu) + max_header_len;
if (hardif_mtu < ETH_MIN_MTU + max_header_len)
return -EINVAL;
if (hard_iface->if_status != BATADV_IF_NOT_IN_USE)
goto out;
hard_iface = kzalloc_obj(*hard_iface, GFP_ATOMIC);
if (!hard_iface)
return -ENOMEM;
kref_get(&hard_iface->refcount);
netdev_hold(net_dev, &hard_iface->dev_tracker, GFP_ATOMIC);
hard_iface->net_dev = net_dev;
hard_iface->if_status = BATADV_IF_INACTIVE;
INIT_HLIST_HEAD(&hard_iface->neigh_list);
mutex_init(&hard_iface->bat_iv.ogm_buff_mutex);
spin_lock_init(&hard_iface->neigh_list_lock);
kref_init(&hard_iface->refcount);
hard_iface->num_bcasts = BATADV_NUM_BCASTS_DEFAULT;
if (batadv_is_wifi_hardif(hard_iface))
hard_iface->num_bcasts = BATADV_NUM_BCASTS_WIRELESS;
WRITE_ONCE(hard_iface->hop_penalty, 0);
batadv_v_hardif_init(hard_iface);
netdev_hold(mesh_iface, &hard_iface->meshif_dev_tracker, GFP_ATOMIC);
hard_iface->mesh_iface = mesh_iface;
bat_priv = netdev_priv(hard_iface->mesh_iface);
bat_priv->hardif_generation++;
ret = netdev_master_upper_dev_link(hard_iface->net_dev,
mesh_iface, hard_iface, NULL, NULL);
if (ret)
@ -764,9 +781,6 @@ int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,
if (ret < 0)
goto err_upper;
hard_iface->if_status = BATADV_IF_INACTIVE;
kref_get(&hard_iface->refcount);
hard_iface->batman_adv_ptype.type = ethertype;
hard_iface->batman_adv_ptype.func = batadv_batman_skb_recv;
hard_iface->batman_adv_ptype.dev = hard_iface->net_dev;
@ -802,14 +816,11 @@ int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,
if (bat_priv->algo_ops->iface.enabled)
bat_priv->algo_ops->iface.enabled(hard_iface);
out:
return 0;
err_upper:
netdev_upper_dev_unlink(hard_iface->net_dev, mesh_iface);
err_dev:
hard_iface->mesh_iface = NULL;
netdev_put(mesh_iface, &hard_iface->meshif_dev_tracker);
batadv_hardif_put(hard_iface);
return ret;
}
@ -821,17 +832,18 @@ int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,
void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface)
{
struct batadv_priv *bat_priv = netdev_priv(hard_iface->mesh_iface);
struct batadv_hard_iface *primary_if = NULL;
struct batadv_hard_iface *primary_if;
ASSERT_RTNL();
batadv_hardif_deactivate_interface(hard_iface);
if (hard_iface->if_status != BATADV_IF_INACTIVE)
goto out;
return;
batadv_info(hard_iface->mesh_iface, "Removing interface: %s\n",
hard_iface->net_dev->name);
dev_remove_pack(&hard_iface->batman_adv_ptype);
batadv_hardif_put(hard_iface);
primary_if = batadv_primary_if_get_selected(bat_priv);
if (hard_iface == primary_if) {
@ -842,15 +854,16 @@ void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface)
batadv_hardif_put(new_if);
}
batadv_hardif_put(primary_if);
bat_priv->algo_ops->iface.disable(hard_iface);
hard_iface->if_status = BATADV_IF_NOT_IN_USE;
hard_iface->if_status = BATADV_IF_TO_BE_REMOVED;
/* delete all references to this hard_iface */
batadv_purge_orig_ref(bat_priv);
batadv_purge_outstanding_packets(bat_priv, hard_iface);
netdev_put(hard_iface->mesh_iface, &hard_iface->meshif_dev_tracker);
bat_priv->hardif_generation++;
netdev_upper_dev_unlink(hard_iface->net_dev, hard_iface->mesh_iface);
batadv_hardif_recalc_extra_skbroom(hard_iface->mesh_iface);
@ -858,67 +871,6 @@ void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface)
if (list_empty(&hard_iface->mesh_iface->adj_list.lower))
batadv_gw_check_client_stop(bat_priv);
hard_iface->mesh_iface = NULL;
batadv_hardif_put(hard_iface);
out:
batadv_hardif_put(primary_if);
}
static struct batadv_hard_iface *
batadv_hardif_add_interface(struct net_device *net_dev)
{
struct batadv_hard_iface *hard_iface;
ASSERT_RTNL();
if (!batadv_is_valid_iface(net_dev))
return NULL;
hard_iface = kzalloc_obj(*hard_iface, GFP_ATOMIC);
if (!hard_iface)
return NULL;
netdev_hold(net_dev, &hard_iface->dev_tracker, GFP_ATOMIC);
hard_iface->net_dev = net_dev;
hard_iface->mesh_iface = NULL;
hard_iface->if_status = BATADV_IF_NOT_IN_USE;
INIT_LIST_HEAD(&hard_iface->list);
INIT_HLIST_HEAD(&hard_iface->neigh_list);
mutex_init(&hard_iface->bat_iv.ogm_buff_mutex);
spin_lock_init(&hard_iface->neigh_list_lock);
kref_init(&hard_iface->refcount);
hard_iface->num_bcasts = BATADV_NUM_BCASTS_DEFAULT;
if (batadv_is_wifi_hardif(hard_iface))
hard_iface->num_bcasts = BATADV_NUM_BCASTS_WIRELESS;
WRITE_ONCE(hard_iface->hop_penalty, 0);
batadv_v_hardif_init(hard_iface);
kref_get(&hard_iface->refcount);
list_add_tail_rcu(&hard_iface->list, &batadv_hardif_list);
batadv_hardif_generation++;
return hard_iface;
}
static void batadv_hardif_remove_interface(struct batadv_hard_iface *hard_iface)
{
ASSERT_RTNL();
/* first deactivate interface */
if (hard_iface->if_status != BATADV_IF_NOT_IN_USE)
batadv_hardif_disable_interface(hard_iface);
if (hard_iface->if_status != BATADV_IF_NOT_IN_USE)
return;
hard_iface->if_status = BATADV_IF_TO_BE_REMOVED;
batadv_hardif_put(hard_iface);
}
@ -1082,10 +1034,6 @@ static int batadv_hard_if_event(struct notifier_block *this,
batadv_wifi_net_device_event(event, net_dev);
hard_iface = batadv_hardif_get_by_netdev(net_dev);
if (!hard_iface && (event == NETDEV_REGISTER ||
event == NETDEV_POST_TYPE_CHANGE))
hard_iface = batadv_hardif_add_interface(net_dev);
if (!hard_iface)
goto out;
@ -1099,19 +1047,12 @@ static int batadv_hard_if_event(struct notifier_block *this,
break;
case NETDEV_UNREGISTER:
case NETDEV_PRE_TYPE_CHANGE:
list_del_rcu(&hard_iface->list);
batadv_hardif_generation++;
batadv_hardif_remove_interface(hard_iface);
batadv_hardif_disable_interface(hard_iface);
break;
case NETDEV_CHANGEMTU:
if (hard_iface->mesh_iface)
batadv_update_min_mtu(hard_iface->mesh_iface);
batadv_update_min_mtu(hard_iface->mesh_iface);
break;
case NETDEV_CHANGEADDR:
if (hard_iface->if_status == BATADV_IF_NOT_IN_USE)
goto hardif_put;
batadv_check_known_mac_addr(hard_iface);
bat_priv = netdev_priv(hard_iface->mesh_iface);

View File

@ -21,12 +21,6 @@
* enum batadv_hard_if_state - State of a hard interface
*/
enum batadv_hard_if_state {
/**
* @BATADV_IF_NOT_IN_USE: interface is not used as slave interface of a
* batman-adv mesh interface
*/
BATADV_IF_NOT_IN_USE,
/**
* @BATADV_IF_TO_BE_REMOVED: interface will be removed from mesh
* interface
@ -74,8 +68,8 @@ u32 batadv_netdev_get_wifi_flags(struct net_device *net_dev);
u32 batadv_hardif_get_wifi_flags(struct batadv_hard_iface *hard_iface);
bool batadv_is_wifi_hardif(struct batadv_hard_iface *hard_iface);
struct batadv_hard_iface*
batadv_hardif_get_by_netdev(const struct net_device *net_dev);
int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,
batadv_hardif_get_by_netdev(struct net_device *net_dev);
int batadv_hardif_enable_interface(struct net_device *net_dev,
struct net_device *mesh_iface);
void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface);
int batadv_hardif_min_mtu(struct net_device *mesh_iface);

View File

@ -59,11 +59,6 @@
#include "tp_meter.h"
#include "translation-table.h"
/* List manipulations on hardif_list have to be rtnl_lock()'ed,
* list traversals just rcu-locked
*/
struct list_head batadv_hardif_list;
unsigned int batadv_hardif_generation;
static int (*batadv_rx_handler[256])(struct sk_buff *skb,
struct batadv_hard_iface *recv_if);
@ -95,7 +90,6 @@ static int __init batadv_init(void)
if (ret < 0)
return ret;
INIT_LIST_HEAD(&batadv_hardif_list);
batadv_algo_init();
batadv_recv_handler_init();
@ -450,9 +444,6 @@ int batadv_batman_skb_recv(struct sk_buff *skb, struct net_device *dev,
if (unlikely(skb->mac_len != ETH_HLEN || !skb_mac_header(skb)))
goto err_free;
if (!hard_iface->mesh_iface)
goto err_free;
bat_priv = netdev_priv(hard_iface->mesh_iface);
if (READ_ONCE(bat_priv->mesh_state) != BATADV_MESH_ACTIVE)

View File

@ -226,9 +226,6 @@ static inline int batadv_print_vid(unsigned short vid)
return -1;
}
extern struct list_head batadv_hardif_list;
extern unsigned int batadv_hardif_generation;
extern struct workqueue_struct *batadv_event_workqueue;
int batadv_mesh_init(struct net_device *mesh_iface);

View File

@ -836,18 +836,7 @@ static int batadv_meshif_slave_add(struct net_device *dev,
struct net_device *slave_dev,
struct netlink_ext_ack *extack)
{
struct batadv_hard_iface *hard_iface;
int ret = -EINVAL;
hard_iface = batadv_hardif_get_by_netdev(slave_dev);
if (!hard_iface || hard_iface->mesh_iface)
goto out;
ret = batadv_hardif_enable_interface(hard_iface, dev);
out:
batadv_hardif_put(hard_iface);
return ret;
return batadv_hardif_enable_interface(slave_dev, dev);
}
/**

View File

@ -968,7 +968,7 @@ batadv_netlink_dump_hardif(struct sk_buff *msg, struct netlink_callback *cb)
bat_priv = netdev_priv(mesh_iface);
rtnl_lock();
cb->seq = batadv_hardif_generation << 1 | 1;
cb->seq = bat_priv->hardif_generation << 1 | 1;
netdev_for_each_lower_private(mesh_iface, hard_iface, iter) {
if (i++ < skip)
@ -1211,7 +1211,9 @@ batadv_netlink_get_hardif_from_ifindex(struct batadv_priv *bat_priv,
if (!hard_dev)
return ERR_PTR(-ENODEV);
rtnl_lock();
hard_iface = batadv_hardif_get_by_netdev(hard_dev);
rtnl_unlock();
if (!hard_iface)
goto err_put_harddev;

View File

@ -1033,7 +1033,6 @@ batadv_purge_neigh_ifinfo(struct batadv_priv *bat_priv,
/* don't purge if the interface is not (going) down */
if (if_outgoing->if_status != BATADV_IF_INACTIVE &&
if_outgoing->if_status != BATADV_IF_NOT_IN_USE &&
if_outgoing->if_status != BATADV_IF_TO_BE_REMOVED)
continue;
@ -1077,7 +1076,6 @@ batadv_purge_orig_ifinfo(struct batadv_priv *bat_priv,
/* don't purge if the interface is not (going) down */
if (if_outgoing->if_status != BATADV_IF_INACTIVE &&
if_outgoing->if_status != BATADV_IF_NOT_IN_USE &&
if_outgoing->if_status != BATADV_IF_TO_BE_REMOVED)
continue;
@ -1127,10 +1125,8 @@ batadv_purge_orig_neighbors(struct batadv_priv *bat_priv,
if (batadv_has_timed_out(last_seen, BATADV_PURGE_TIMEOUT) ||
if_incoming->if_status == BATADV_IF_INACTIVE ||
if_incoming->if_status == BATADV_IF_NOT_IN_USE ||
if_incoming->if_status == BATADV_IF_TO_BE_REMOVED) {
if (if_incoming->if_status == BATADV_IF_INACTIVE ||
if_incoming->if_status == BATADV_IF_NOT_IN_USE ||
if_incoming->if_status == BATADV_IF_TO_BE_REMOVED)
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
"neighbor purge: originator %pM, neighbor: %pM, iface: %s\n",

View File

@ -358,28 +358,16 @@ batadv_tp_list_find_sender_session(struct batadv_priv *bat_priv, const u8 *dst,
}
/**
* batadv_tp_vars_common_release() - release batadv_tp_vars_common from lists
* batadv_tp_sender_release() - release batadv_tp_sender
* and queue for free after rcu grace period
* @ref: kref pointer of the batadv_tp_vars_common
* @ref: kref pointer of the batadv_tp_sender
*/
static void batadv_tp_vars_common_release(struct kref *ref)
static void batadv_tp_sender_release(struct kref *ref)
{
struct batadv_tp_vars_common *tp_vars;
struct batadv_tp_unacked *un, *safe;
struct batadv_tp_sender *tp_vars;
tp_vars = container_of(ref, struct batadv_tp_vars_common, refcount);
/* lock should not be needed because this object is now out of any
* context!
*/
spin_lock_bh(&tp_vars->unacked_lock);
list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) {
list_del(&un->list);
kfree(un);
}
spin_unlock_bh(&tp_vars->unacked_lock);
kfree_rcu(tp_vars, rcu);
tp_vars = container_of(ref, struct batadv_tp_sender, common.refcount);
kfree_rcu(tp_vars, common.rcu);
}
/**
@ -392,7 +380,7 @@ static void batadv_tp_sender_put(struct batadv_tp_sender *tp_vars)
if (!tp_vars)
return;
kref_put(&tp_vars->common.refcount, batadv_tp_vars_common_release);
kref_put(&tp_vars->common.refcount, batadv_tp_sender_release);
}
/**
@ -1145,9 +1133,6 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
init_waitqueue_head(&tp_vars->more_bytes);
init_completion(&tp_vars->finished);
spin_lock_init(&tp_vars->common.unacked_lock);
INIT_LIST_HEAD(&tp_vars->common.unacked_list);
spin_lock_init(&tp_vars->cc_lock);
tp_vars->prerandom_offset = 0;
@ -1251,6 +1236,33 @@ batadv_tp_list_find_receiver_session(struct batadv_priv *bat_priv, const u8 *dst
return tp_vars;
}
/**
* batadv_tp_receiver_release() - release batadv_tp_receiver
* and queue for free after rcu grace period
* @ref: kref pointer of the batadv_tp_receiver
*/
static void batadv_tp_receiver_release(struct kref *ref)
{
struct batadv_tp_receiver *tp_vars;
struct batadv_tp_unacked *safe;
struct batadv_tp_unacked *un;
tp_vars = container_of(ref, struct batadv_tp_receiver, common.refcount);
/* lock should not be needed because this object is now out of any
* context!
*/
spin_lock_bh(&tp_vars->ack_seqno_lock);
list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) {
list_del(&un->list);
kfree(un);
tp_vars->unacked_count--;
}
spin_unlock_bh(&tp_vars->ack_seqno_lock);
kfree_rcu(tp_vars, common.rcu);
}
/**
* batadv_tp_receiver_put() - decrement the batadv_tp_receiver
* refcounter and possibly release it
@ -1261,7 +1273,7 @@ static void batadv_tp_receiver_put(struct batadv_tp_receiver *tp_vars)
if (!tp_vars)
return;
kref_put(&tp_vars->common.refcount, batadv_tp_vars_common_release);
kref_put(&tp_vars->common.refcount, batadv_tp_receiver_release);
}
/**
@ -1304,13 +1316,13 @@ static void batadv_tp_receiver_shutdown(struct timer_list *t)
if (batadv_tp_list_detach(&tp_vars->common))
batadv_tp_receiver_put(tp_vars);
spin_lock_bh(&tp_vars->common.unacked_lock);
list_for_each_entry_safe(un, safe, &tp_vars->common.unacked_list, list) {
spin_lock_bh(&tp_vars->ack_seqno_lock);
list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) {
list_del(&un->list);
kfree(un);
tp_vars->common.unacked_count--;
tp_vars->unacked_count--;
}
spin_unlock_bh(&tp_vars->common.unacked_lock);
spin_unlock_bh(&tp_vars->ack_seqno_lock);
/* drop reference of timer */
if (WARN_ON(atomic_xchg(&tp_vars->receiving, 0) != 1))
@ -1403,69 +1415,106 @@ static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst,
*/
static bool batadv_tp_handle_out_of_order(struct batadv_tp_receiver *tp_vars,
u32 seqno, u32 payload_len)
__must_hold(&tp_vars->common.unacked_lock)
__must_hold(&tp_vars->ack_seqno_lock)
{
struct batadv_tp_unacked *un, *new;
bool added = false;
struct list_head *pos = &tp_vars->unacked_list;
struct batadv_tp_unacked *new = NULL;
u32 end_seqno = seqno + payload_len;
struct batadv_tp_unacked *safe;
struct batadv_tp_unacked *un;
new = kmalloc_obj(*new, GFP_ATOMIC);
if (unlikely(!new))
return false;
new->seqno = seqno;
new->len = payload_len;
/* if the list is empty immediately attach this new object */
if (list_empty(&tp_vars->common.unacked_list)) {
list_add(&new->list, &tp_vars->common.unacked_list);
tp_vars->common.unacked_count++;
return true;
}
/* otherwise loop over the list and either drop the packet because this
* is a duplicate or store it at the right position.
/* loop over the list to find either an existing entry which the new
* seqno range can be merged with or the position at which a new entry
* has to be inserted.
*
* The iteration is done in the reverse way because it is likely that
* the last received packet (the one being processed now) has a bigger
* seqno than all the others already stored.
*/
list_for_each_entry_reverse(un, &tp_vars->common.unacked_list, list) {
/* check for duplicates */
if (new->seqno == un->seqno) {
if (new->len > un->len)
un->len = new->len;
kfree(new);
added = true;
break;
}
/* look for the right position */
if (batadv_seq_before(new->seqno, un->seqno))
list_for_each_entry_reverse(un, &tp_vars->unacked_list, list) {
/* look for the right position - an un which is smaller */
if (batadv_seq_before(seqno, un->seqno))
continue;
/* as soon as an entry having a bigger seqno is found, the new
* one is attached _after_ it. In this way the list is kept in
* ascending order
/* smaller/equal seqno was found but they might be directly
* after another or overlapping. keep only a single entry
*
* It is already known that:
*
* un->seqno <= seqno
*
* When establishing that:
*
* seqno <= un->seqno + un->len
*
* Then it is not necessary to add a new entry because the
* smaller/equal seqno of un might already contain the new
* received packet or we only add new data directly after
* the end of un. The latter can be identified using:
*
* un->seqno + un->len <= end_seqno
*/
list_add(&new->list, &un->list);
added = true;
tp_vars->common.unacked_count++;
if (!batadv_seq_before(un->seqno + un->len, seqno)) {
/* new data directly after un? */
if (!batadv_seq_before(end_seqno, un->seqno + un->len))
un->len = end_seqno - un->seqno;
/* un now represents both old un + new range and has to
* be used to check if the gap to the next seqno range
* was closed
*/
new = un;
} else {
/* as soon as an entry having a smaller seqno is found,
* the new one is attached _after_ it. In this way the
* list is kept in ascending order
*/
pos = &un->list;
}
break;
}
/* received packet with smallest seqno out of order; add it to front */
if (!added) {
list_add(&new->list, &tp_vars->common.unacked_list);
tp_vars->common.unacked_count++;
/* no entry to merge with was found; insert a new one after the entry
* with the next smaller seqno (or at the front of the list when the
* new seqno is the smallest or the list is empty)
*/
if (!new) {
new = kmalloc_obj(*new, GFP_ATOMIC);
if (unlikely(!new))
return false;
new->seqno = seqno;
new->len = payload_len;
list_add(&new->list, pos);
tp_vars->unacked_count++;
}
/* check if new filled the gap to the next list entries */
un = new;
list_for_each_entry_safe_continue(un, safe, &tp_vars->unacked_list, list) {
if (batadv_seq_before(end_seqno, un->seqno))
break;
/* next entry is overlapping or adjacent - combine both */
if (batadv_seq_before(end_seqno, un->seqno + un->len)) {
end_seqno = un->seqno + un->len;
new->len = end_seqno - new->seqno;
}
list_del(&un->list);
kfree(un);
tp_vars->unacked_count--;
}
/* remove the last (biggest) unacked seqno when list is too large */
if (tp_vars->common.unacked_count > BATADV_TP_MAX_UNACKED) {
un = list_last_entry(&tp_vars->common.unacked_list,
if (tp_vars->unacked_count > BATADV_TP_MAX_UNACKED) {
un = list_last_entry(&tp_vars->unacked_list,
struct batadv_tp_unacked, list);
list_del(&un->list);
kfree(un);
tp_vars->common.unacked_count--;
tp_vars->unacked_count--;
}
return true;
@ -1477,7 +1526,7 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_receiver *tp_vars,
* @tp_vars: the private data of the current TP meter session
*/
static void batadv_tp_ack_unordered(struct batadv_tp_receiver *tp_vars)
__must_hold(&tp_vars->common.unacked_lock)
__must_hold(&tp_vars->ack_seqno_lock)
{
struct batadv_tp_unacked *un, *safe;
u32 to_ack;
@ -1485,7 +1534,7 @@ static void batadv_tp_ack_unordered(struct batadv_tp_receiver *tp_vars)
/* go through the unacked packet list and possibly ACK them as
* well
*/
list_for_each_entry_safe(un, safe, &tp_vars->common.unacked_list, list) {
list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) {
/* the list is ordered, therefore it is possible to stop as soon
* there is a gap between the last acked seqno and the seqno of
* the packet under inspection
@ -1493,14 +1542,14 @@ static void batadv_tp_ack_unordered(struct batadv_tp_receiver *tp_vars)
if (batadv_seq_before(tp_vars->last_recv, un->seqno))
break;
to_ack = un->seqno + un->len - tp_vars->last_recv;
to_ack = un->seqno + un->len;
if (batadv_seq_before(tp_vars->last_recv, un->seqno + un->len))
tp_vars->last_recv += to_ack;
if (batadv_seq_before(tp_vars->last_recv, to_ack))
tp_vars->last_recv = to_ack;
list_del(&un->list);
kfree(un);
tp_vars->common.unacked_count--;
tp_vars->unacked_count--;
}
}
@ -1547,9 +1596,9 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv,
tp_vars->common.bat_priv = bat_priv;
kref_init(&tp_vars->common.refcount);
spin_lock_init(&tp_vars->common.unacked_lock);
INIT_LIST_HEAD(&tp_vars->common.unacked_list);
tp_vars->common.unacked_count = 0;
spin_lock_init(&tp_vars->ack_seqno_lock);
INIT_LIST_HEAD(&tp_vars->unacked_list);
tp_vars->unacked_count = 0;
kref_get(&tp_vars->common.refcount);
timer_setup(&tp_vars->common.timer, batadv_tp_receiver_shutdown, 0);
@ -1609,7 +1658,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
WRITE_ONCE(tp_vars->last_recv_time, jiffies);
}
spin_lock_bh(&tp_vars->common.unacked_lock);
spin_lock_bh(&tp_vars->ack_seqno_lock);
/* if the packet is a duplicate, it may be the case that an ACK has been
* lost. Resend the ACK
@ -1625,7 +1674,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
* not been enqueued correctly
*/
if (!batadv_tp_handle_out_of_order(tp_vars, seqno, payload_len)) {
spin_unlock_bh(&tp_vars->common.unacked_lock);
spin_unlock_bh(&tp_vars->ack_seqno_lock);
goto out;
}
@ -1641,7 +1690,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
send_ack:
to_ack = tp_vars->last_recv;
spin_unlock_bh(&tp_vars->common.unacked_lock);
spin_unlock_bh(&tp_vars->ack_seqno_lock);
/* send the ACK. If the received packet was out of order, the ACK that
* is going to be sent is a duplicate (the sender will count them and

View File

@ -442,6 +442,54 @@ static int batadv_tvlv_call_handler(struct batadv_priv *bat_priv,
return NET_RX_SUCCESS;
}
/**
* batadv_tvlv_hdr_next() - move a tvlv buffer cursor to the next container
* @tvlv_value: cursor into the tvlv buffer, advanced past the returned
* container's content on success
* @tvlv_value_len: remaining length of the tvlv buffer, reduced by the returned
* container's size on success
*
* Parses a single container header at the current cursor position and, if a
* complete container is available, advances the cursor and remaining length
* past it. The returned header stays valid; its content is located at
* (returned header + 1) and is ntohs(hdr->len) bytes long.
*
* Return: pointer to the next tvlv container header, or NULL if no further
* complete container is present in the buffer.
*/
static struct batadv_tvlv_hdr *batadv_tvlv_hdr_next(void **tvlv_value, u16 *tvlv_value_len)
{
struct batadv_tvlv_hdr *tvlv_hdr;
u16 tvlv_value_cont_len;
void *tvlv_value_cont;
u16 tvlv_len;
tvlv_value_cont = *tvlv_value;
tvlv_len = *tvlv_value_len;
if (tvlv_len < sizeof(*tvlv_hdr))
return NULL;
tvlv_hdr = tvlv_value_cont;
tvlv_value_cont_len = ntohs(tvlv_hdr->len);
tvlv_value_cont = tvlv_hdr + 1;
tvlv_len -= sizeof(*tvlv_hdr);
if (tvlv_value_cont_len > tvlv_len)
return NULL;
/* the next tvlv header is accessed assuming (at least) 2-byte
* alignment, so it must start at an even offset.
*/
if (tvlv_value_cont_len & 1)
return NULL;
*tvlv_value = (u8 *)tvlv_value_cont + tvlv_value_cont_len;
*tvlv_value_len = tvlv_len - tvlv_value_cont_len;
return tvlv_hdr;
}
/**
* batadv_tvlv_containers_contain() - check if a tvlv buffer holds a container
* @tvlv_value: tvlv content
@ -457,28 +505,10 @@ static bool batadv_tvlv_containers_contain(void *tvlv_value,
u8 version)
{
struct batadv_tvlv_hdr *tvlv_hdr;
u16 tvlv_value_cont_len;
while (tvlv_value_len >= sizeof(*tvlv_hdr)) {
tvlv_hdr = tvlv_value;
tvlv_value_cont_len = ntohs(tvlv_hdr->len);
tvlv_value = tvlv_hdr + 1;
tvlv_value_len -= sizeof(*tvlv_hdr);
if (tvlv_value_cont_len > tvlv_value_len)
break;
/* the next tvlv header is accessed assuming (at least) 2-byte
* alignment, so it must start at an even offset.
*/
if (tvlv_value_cont_len & 1)
break;
while ((tvlv_hdr = batadv_tvlv_hdr_next(&tvlv_value, &tvlv_value_len))) {
if (tvlv_hdr->type == type && tvlv_hdr->version == version)
return true;
tvlv_value = (u8 *)tvlv_value + tvlv_value_cont_len;
tvlv_value_len -= tvlv_value_cont_len;
}
return false;
@ -511,20 +541,8 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv,
u8 cifnotfound = BATADV_TVLV_HANDLER_OGM_CIFNOTFND;
int ret = NET_RX_SUCCESS;
while (tvlv_value_len >= sizeof(*tvlv_hdr)) {
tvlv_hdr = tvlv_value;
while ((tvlv_hdr = batadv_tvlv_hdr_next(&tvlv_value, &tvlv_value_len))) {
tvlv_value_cont_len = ntohs(tvlv_hdr->len);
tvlv_value = tvlv_hdr + 1;
tvlv_value_len -= sizeof(*tvlv_hdr);
if (tvlv_value_cont_len > tvlv_value_len)
break;
/* the next tvlv header is accessed assuming (at least) 2-byte
* alignment, so it must start at an even offset.
*/
if (tvlv_value_cont_len & 1)
break;
tvlv_handler = batadv_tvlv_handler_get(bat_priv,
tvlv_hdr->type,
@ -532,11 +550,9 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv,
ret |= batadv_tvlv_call_handler(bat_priv, tvlv_handler,
packet_type, orig_node, skb,
tvlv_value,
tvlv_hdr + 1,
tvlv_value_cont_len);
batadv_tvlv_handler_put(tvlv_handler);
tvlv_value = (u8 *)tvlv_value + tvlv_value_cont_len;
tvlv_value_len -= tvlv_value_cont_len;
}
if (packet_type != BATADV_IV_OGM &&

View File

@ -214,9 +214,6 @@ struct batadv_wifi_net_device_state {
* struct batadv_hard_iface - network device known to batman-adv
*/
struct batadv_hard_iface {
/** @list: list node for batadv_hardif_list */
struct list_head list;
/** @if_status: status of the interface for batman-adv */
char if_status;
@ -1335,9 +1332,9 @@ struct batadv_tp_unacked {
u32 seqno;
/** @len: length of the packet */
u16 len;
u32 len;
/** @list: list node for &batadv_tp_vars_common.unacked_list */
/** @list: list node for &batadv_tp_receiver.unacked_list */
struct list_head list;
};
@ -1360,15 +1357,6 @@ struct batadv_tp_vars_common {
/** @session: TP session identifier */
u8 session[2];
/** @unacked_list: list of unacked packets (meta-info only) */
struct list_head unacked_list;
/** @unacked_lock: protect unacked_list + &batadv_tp_receiver.last_recv */
spinlock_t unacked_lock;
/** @unacked_count: number of unacked entries */
size_t unacked_count;
/** @refcount: number of context where the object is used */
struct kref refcount;
@ -1482,6 +1470,15 @@ struct batadv_tp_receiver {
/** @last_recv_time: time (jiffies) a msg was received */
unsigned long last_recv_time;
/** @unacked_list: list of unacked packets (meta-info only) */
struct list_head unacked_list;
/** @ack_seqno_lock: protect unacked_list + &batadv_tp_receiver.last_recv */
spinlock_t ack_seqno_lock;
/** @unacked_count: number of unacked entries */
size_t unacked_count;
};
/**
@ -1679,6 +1676,9 @@ struct batadv_priv {
/** @tp_num: number of currently active tp sessions */
atomic_t tp_num;
/** @hardif_generation: generation counter added to netlink hardif dumps */
unsigned int hardif_generation;
/** @orig_work: work queue callback item for orig node purging */
struct delayed_work orig_work;