So far, only batman-adv broadcast packets are allowed if they have a broadcast ethernet destination.
For the upcoming broadcast-via-unicasts feature it is necessary that a neighboring node is capable of receiving a batman-adv broadcast packet with a unicast ethernet destination, too.
Therefore this patch relaxes the ethernet destination check for batman-adv broadcast packets and adds the necessary capability tracking for later compatibility checking.
Signed-off-by: Linus Lüssing linus.luessing@c0d3.blue --- include/uapi/linux/batadv_packet.h | 2 + net/batman-adv/bat_iv_ogm.c | 6 ++- net/batman-adv/bat_v_elp.c | 2 + net/batman-adv/hard-interface.c | 1 + net/batman-adv/originator.c | 65 ++++++++++++++++++++++++++++++ net/batman-adv/originator.h | 3 ++ net/batman-adv/routing.c | 7 +++- net/batman-adv/types.h | 21 ++++++++++ 8 files changed, 104 insertions(+), 3 deletions(-)
diff --git a/include/uapi/linux/batadv_packet.h b/include/uapi/linux/batadv_packet.h index 4ebc2135..2ec59e4a 100644 --- a/include/uapi/linux/batadv_packet.h +++ b/include/uapi/linux/batadv_packet.h @@ -164,6 +164,7 @@ enum batadv_bla_claimframe { * @BATADV_TVLV_TT: translation table tvlv * @BATADV_TVLV_ROAM: roaming advertisement tvlv * @BATADV_TVLV_MCAST: multicast capability tvlv + * @BATADV_TVLV_BCAST: broadcast capability tvlv */ enum batadv_tvlv_type { BATADV_TVLV_GW = 0x01, @@ -172,6 +173,7 @@ enum batadv_tvlv_type { BATADV_TVLV_TT = 0x04, BATADV_TVLV_ROAM = 0x05, BATADV_TVLV_MCAST = 0x06, + BATADV_TVLV_BCAST = 0x07, };
#pragma pack(2) diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c index bd4138dd..bcbebf10 100644 --- a/net/batman-adv/bat_iv_ogm.c +++ b/net/batman-adv/bat_iv_ogm.c @@ -1310,8 +1310,12 @@ batadv_iv_ogm_process_per_outif(const struct sk_buff *skb, int ogm_offset, if (is_single_hop_neigh) { hardif_neigh = batadv_hardif_neigh_get(if_incoming, ethhdr->h_source); - if (hardif_neigh) + if (hardif_neigh) { hardif_neigh->last_seen = jiffies; + + batadv_hardif_neigh_update_capa(orig_node, + hardif_neigh); + } }
router = batadv_orig_router_get(orig_node, if_outgoing); diff --git a/net/batman-adv/bat_v_elp.c b/net/batman-adv/bat_v_elp.c index 2614a9ca..d49b61c1 100644 --- a/net/batman-adv/bat_v_elp.c +++ b/net/batman-adv/bat_v_elp.c @@ -489,6 +489,8 @@ static void batadv_v_elp_neigh_update(struct batadv_priv *bat_priv, hardif_neigh->bat_v.elp_latest_seqno = ntohl(elp_packet->seqno); hardif_neigh->bat_v.elp_interval = ntohl(elp_packet->elp_interval);
+ batadv_hardif_neigh_update_capa(orig_neigh, hardif_neigh); + hardif_free: if (hardif_neigh) batadv_hardif_neigh_put(hardif_neigh); diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c index 79d1731b..b71d8efc 100644 --- a/net/batman-adv/hard-interface.c +++ b/net/batman-adv/hard-interface.c @@ -936,6 +936,7 @@ batadv_hardif_add_interface(struct net_device *net_dev) hard_iface->num_bcasts = BATADV_NUM_BCASTS_WIRELESS;
batadv_v_hardif_init(hard_iface); + atomic_set(&hard_iface->num_bcast_no_urcv, 0);
batadv_check_known_mac_addr(hard_iface->net_dev); kref_get(&hard_iface->refcount); diff --git a/net/batman-adv/originator.c b/net/batman-adv/originator.c index 45db798a..73eacccf 100644 --- a/net/batman-adv/originator.c +++ b/net/batman-adv/originator.c @@ -42,6 +42,7 @@ #include "routing.h" #include "soft-interface.h" #include "translation-table.h" +#include "tvlv.h"
/* hash class keys */ static struct lock_class_key batadv_orig_hash_lock_class_key; @@ -196,6 +197,26 @@ void batadv_orig_node_vlan_put(struct batadv_orig_node_vlan *orig_vlan) kref_put(&orig_vlan->refcount, batadv_orig_node_vlan_release); }
+/** + * batadv_orig_bcast_tvlv_ogm_handler() - process incoming broadcast tvlv + * @bat_priv: the bat priv with all the soft interface information + * @orig: the orig_node of the ogm + * @flags: flags indicating the tvlv state (see batadv_tvlv_handler_flags) + * @tvlv_value: tvlv buffer containing the multicast data + * @tvlv_value_len: tvlv buffer length + */ +static void batadv_orig_bcast_tvlv_ogm_handler(struct batadv_priv *bat_priv, + struct batadv_orig_node *orig, + u8 flags, + void *tvlv_value, + u16 tvlv_value_len) +{ + if (flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND) + clear_bit(BATADV_ORIG_CAPA_HAS_BCAST_URCV, &orig->capabilities); + else + set_bit(BATADV_ORIG_CAPA_HAS_BCAST_URCV, &orig->capabilities); +} + /** * batadv_originator_init() - Initialize all originator structures * @bat_priv: the bat priv with all the soft interface information @@ -215,6 +236,12 @@ int batadv_originator_init(struct batadv_priv *bat_priv) batadv_hash_set_lock_class(bat_priv->orig_hash, &batadv_orig_hash_lock_class_key);
+ batadv_tvlv_container_register(bat_priv, BATADV_TVLV_BCAST, 1, NULL, 0); + batadv_tvlv_handler_register(bat_priv, + batadv_orig_bcast_tvlv_ogm_handler, + NULL, BATADV_TVLV_BCAST, 1, + BATADV_TVLV_HANDLER_OGM_CIFNOTFND); + INIT_DELAYED_WORK(&bat_priv->orig_work, batadv_purge_orig); queue_delayed_work(batadv_event_workqueue, &bat_priv->orig_work, @@ -269,6 +296,9 @@ static void batadv_hardif_neigh_release(struct kref *ref) hlist_del_init_rcu(&hardif_neigh->list); spin_unlock_bh(&hardif_neigh->if_incoming->neigh_list_lock);
+ if (!atomic_read(&hardif_neigh->bcast_has_urcv)) + atomic_dec(&hardif_neigh->if_incoming->num_bcast_no_urcv); + batadv_hardif_put(hardif_neigh->if_incoming); kfree_rcu(hardif_neigh, rcu); } @@ -542,6 +572,34 @@ batadv_neigh_node_get(const struct batadv_orig_node *orig_node, return res; }
+/** + * batadv_hardif_neigh_update_capa() - update hardif neighbor capabilities + * @orig_node: originator object representing the neighbour + * @hardif_neigh: the hardif neighbor to update + * + * Propagates neighbor node specific capabilities from an originator node onto a + * hardif neighbor node: + * + * This updates the broadcast-via-unicast reception capability flag of a + * neighbor node and updates the matching counter on the hard interfaces it + * belongs to. + */ +void +batadv_hardif_neigh_update_capa(const struct batadv_orig_node *orig_node, + struct batadv_hardif_neigh_node *hardif_neigh) +{ + struct batadv_hard_iface *hard_iface = hardif_neigh->if_incoming; + + if (test_bit(BATADV_ORIG_CAPA_HAS_BCAST_URCV, + &orig_node->capabilities)) { + if (atomic_add_unless(&hardif_neigh->bcast_has_urcv, 1, 1)) + atomic_dec(&hard_iface->num_bcast_no_urcv); + } else { + if (atomic_add_unless(&hardif_neigh->bcast_has_urcv, -1, 0)) + atomic_inc(&hard_iface->num_bcast_no_urcv); + } +} + /** * batadv_hardif_neigh_create() - create a hardif neighbour node * @hard_iface: the interface this neighbour is connected to @@ -576,6 +634,10 @@ batadv_hardif_neigh_create(struct batadv_hard_iface *hard_iface, hardif_neigh->if_incoming = hard_iface; hardif_neigh->last_seen = jiffies;
+ atomic_set(&hardif_neigh->bcast_has_urcv, 0); + atomic_inc(&hardif_neigh->if_incoming->num_bcast_no_urcv); + batadv_hardif_neigh_update_capa(orig_node, hardif_neigh); + kref_init(&hardif_neigh->refcount);
if (bat_priv->algo_ops->neigh.hardif_init) @@ -975,6 +1037,9 @@ void batadv_originator_free(struct batadv_priv *bat_priv)
cancel_delayed_work_sync(&bat_priv->orig_work);
+ batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_BCAST, 1); + batadv_tvlv_handler_unregister(bat_priv, BATADV_TVLV_BCAST, 1); + bat_priv->orig_hash = NULL;
for (i = 0; i < hash->size; i++) { diff --git a/net/batman-adv/originator.h b/net/batman-adv/originator.h index 3829e26f..fb22161d 100644 --- a/net/batman-adv/originator.h +++ b/net/batman-adv/originator.h @@ -30,6 +30,9 @@ batadv_hardif_neigh_get(const struct batadv_hard_iface *hard_iface, const u8 *neigh_addr); void batadv_hardif_neigh_put(struct batadv_hardif_neigh_node *hardif_neigh); +void +batadv_hardif_neigh_update_capa(const struct batadv_orig_node *orig_node, + struct batadv_hardif_neigh_node *hardif_neigh); struct batadv_neigh_node * batadv_neigh_node_get_or_create(struct batadv_orig_node *orig_node, struct batadv_hard_iface *hard_iface, diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c index 30fbd073..03b4e609 100644 --- a/net/batman-adv/routing.c +++ b/net/batman-adv/routing.c @@ -1198,8 +1198,11 @@ int batadv_recv_bcast_packet(struct sk_buff *skb,
ethhdr = eth_hdr(skb);
- /* packet with broadcast indication but unicast recipient */ - if (!is_broadcast_ether_addr(ethhdr->h_dest)) + /* packet with broadcast indication but unicast recipient + * which is not us + */ + if (!is_broadcast_ether_addr(ethhdr->h_dest) && + !batadv_is_my_mac(bat_priv, ethhdr->h_dest)) goto free_skb;
/* packet with broadcast/multicast sender address */ diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index 74b64473..877a2762 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -193,6 +193,13 @@ struct batadv_hard_iface { /** @rcu: struct used for freeing in an RCU-safe manner */ struct rcu_head rcu;
+ /** + * @num_bcast_no_urcv: number of neighbor nodes on this interface which + * do not support receiving batman-adv broadcast packets with a + * unicast ethernet frame destination + */ + atomic_t num_bcast_no_urcv; + /** @bat_iv: per hard-interface B.A.T.M.A.N. IV data */ struct batadv_hard_iface_bat_iv bat_iv;
@@ -528,6 +535,13 @@ enum batadv_orig_capabilities { * (= orig node announces a tvlv of type BATADV_TVLV_MCAST) */ BATADV_ORIG_CAPA_HAS_MCAST, + + /** + * BATADV_ORIG_CAPA_HAS_BCAST_URCV: orig node is able to receive + * batman-adv broadcast packets with a unicast ethernet frame + * destination + */ + BATADV_ORIG_CAPA_HAS_BCAST_URCV, };
/** @@ -600,6 +614,13 @@ struct batadv_hardif_neigh_node { /** @last_seen: when last packet via this neighbor was received */ unsigned long last_seen;
+ /** + * @bcast_has_urcv: a flag indicating whether this neighbor node + * supports receiving batman-adv broadcast packets with a unicast + * ethernet frame destination + */ + atomic_t bcast_has_urcv; + #ifdef CONFIG_BATMAN_ADV_BATMAN_V /** @bat_v: B.A.T.M.A.N. V private data */ struct batadv_hardif_neigh_node_bat_v bat_v;