So far, if we wanted to bridge VLAN tagged frames into the mesh one would
need to manually create an according VLAN interface on top of bat0
first, to trigger batman-adv to create the according structures
for a VID.

With this change the VLAN from bridged-in clients is now automatically
detected and added to the translation table on the fly.

Signed-off-by: Linus Lüssing <[email protected]>
---
 net/batman-adv/hard-interface.c    |   2 +-
 net/batman-adv/multicast.c         |   8 +-
 net/batman-adv/soft-interface.c    | 125 ++++++++++++++++-------------
 net/batman-adv/soft-interface.h    |   6 +-
 net/batman-adv/translation-table.c |  19 ++---
 net/batman-adv/translation-table.h |   4 +-
 6 files changed, 91 insertions(+), 73 deletions(-)

diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c
index 96a412beab2d..f5826dd8752c 100644
--- a/net/batman-adv/hard-interface.c
+++ b/net/batman-adv/hard-interface.c
@@ -946,7 +946,7 @@ static int batadv_hard_if_event_softif(unsigned long event,
        switch (event) {
        case NETDEV_REGISTER:
                bat_priv = netdev_priv(net_dev);
-               batadv_softif_create_vlan(bat_priv, BATADV_NO_FLAGS);
+               batadv_softif_create_vlan_own(bat_priv, BATADV_NO_FLAGS);
                break;
        }
 
diff --git a/net/batman-adv/multicast.c b/net/batman-adv/multicast.c
index bcb5fd4e0219..b39c214c0c6d 100644
--- a/net/batman-adv/multicast.c
+++ b/net/batman-adv/multicast.c
@@ -723,6 +723,7 @@ static void batadv_mcast_mla_tt_add(struct batadv_priv 
*bat_priv,
 {
        struct batadv_hw_addr *mcast_entry;
        struct hlist_node *tmp;
+       int ret;
 
        if (!mcast_list)
                return;
@@ -732,9 +733,10 @@ static void batadv_mcast_mla_tt_add(struct batadv_priv 
*bat_priv,
                                                  &bat_priv->mcast.mla_list))
                        continue;
 
-               if (!batadv_tt_local_add(bat_priv->soft_iface,
-                                        mcast_entry->addr, BATADV_NO_FLAGS,
-                                        BATADV_NULL_IFINDEX, BATADV_NO_MARK))
+               ret = batadv_tt_local_add(bat_priv->soft_iface,
+                                         mcast_entry->addr, BATADV_NO_FLAGS,
+                                         BATADV_NULL_IFINDEX, BATADV_NO_MARK);
+               if (ret <= 0)
                        continue;
 
                hlist_del(&mcast_entry->list);
diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c
index 282a8f9b1444..a5ccbdf12171 100644
--- a/net/batman-adv/soft-interface.c
+++ b/net/batman-adv/soft-interface.c
@@ -141,6 +141,10 @@ static int batadv_interface_set_mac_addr(struct net_device 
*dev, void *p)
 
        rcu_read_lock();
        hlist_for_each_entry_rcu(vlan, &bat_priv->softif_vlan_list, list) {
+               /* we don't use this VID ourself, avoid adding us to it */
+               if (!batadv_is_my_client(bat_priv, old_addr, vlan->vid))
+                       continue;
+
                batadv_tt_local_remove(bat_priv, old_addr, vlan->vid,
                                       "mac address changed", false);
                batadv_tt_local_add(dev, addr->sa_data, vlan->vid,
@@ -549,13 +553,15 @@ struct batadv_softif_vlan *batadv_softif_vlan_get(struct 
batadv_priv *bat_priv,
 }
 
 /**
- * batadv_softif_create_vlan() - allocate the needed resources for a new vlan
+ * batadv_softif_create_vlan() - create a softif vlan struct
  * @bat_priv: the bat priv with all the soft interface information
  * @vid: the VLAN identifier
  *
- * Return: 0 on success, a negative error otherwise.
+ * Return: a pointer to the newly allocated softif vlan struct on success, NULL
+ * otherwise.
  */
-int batadv_softif_create_vlan(struct batadv_priv *bat_priv, unsigned short vid)
+static struct batadv_softif_vlan *
+batadv_softif_create_vlan(struct batadv_priv *bat_priv, unsigned short vid)
 {
        struct batadv_softif_vlan *vlan;
 
@@ -563,55 +569,93 @@ int batadv_softif_create_vlan(struct batadv_priv 
*bat_priv, unsigned short vid)
 
        vlan = batadv_softif_vlan_get(bat_priv, vid);
        if (vlan) {
-               batadv_softif_vlan_put(vlan);
                spin_unlock_bh(&bat_priv->softif_vlan_list_lock);
-               return -EEXIST;
+               return vlan;
        }
 
        vlan = kzalloc(sizeof(*vlan), GFP_ATOMIC);
        if (!vlan) {
                spin_unlock_bh(&bat_priv->softif_vlan_list_lock);
-               return -ENOMEM;
+               return NULL;
        }
 
        vlan->bat_priv = bat_priv;
        vlan->vid = vid;
+       /* hold only one refcount, caller will store a reference to us in
+        * tt_local->vlan without releasing any refcount
+        */
        kref_init(&vlan->refcount);
 
        atomic_set(&vlan->ap_isolation, 0);
 
-       kref_get(&vlan->refcount);
        hlist_add_head_rcu(&vlan->list, &bat_priv->softif_vlan_list);
        spin_unlock_bh(&bat_priv->softif_vlan_list_lock);
 
+       return vlan;
+}
+
+/**
+ * batadv_softif_vlan_get_or_create() - retrieve or create a softif vlan struct
+ * @bat_priv: the bat priv with all the soft interface information
+ * @vid: the VLAN identifier
+ *
+ * Return: the softif vlan struct if found or created or NULL otherwise.
+ */
+struct batadv_softif_vlan *
+batadv_softif_vlan_get_or_create(struct batadv_priv *bat_priv,
+                                unsigned short vid)
+{
+       struct batadv_softif_vlan *vlan = batadv_softif_vlan_get(bat_priv, vid);
+
+       if (vlan)
+               return vlan;
+
+       return batadv_softif_create_vlan(bat_priv, vid);
+}
+
+/**
+ * batadv_softif_create_vlan_own() - add our own softif to the local TT
+ * @bat_priv: the bat priv with all the soft interface information
+ * @vid: the VLAN identifier
+ *
+ * Adds the MAC address of our own soft interface with the given VLAN ID as
+ * a permanent local TT entry.
+ *
+ * Return: 0 on success, a negative error otherwise.
+ */
+int batadv_softif_create_vlan_own(struct batadv_priv *bat_priv,
+                                 unsigned short vid)
+{
+       int ret;
+
        /* add a new TT local entry. This one will be marked with the NOPURGE
         * flag
         */
-       batadv_tt_local_add(bat_priv->soft_iface,
-                           bat_priv->soft_iface->dev_addr, vid,
-                           BATADV_NULL_IFINDEX, BATADV_NO_MARK);
-
-       /* don't return reference to new softif_vlan */
-       batadv_softif_vlan_put(vlan);
+       ret = batadv_tt_local_add(bat_priv->soft_iface,
+                                 bat_priv->soft_iface->dev_addr, vid,
+                                 BATADV_NULL_IFINDEX, BATADV_NO_MARK);
+       if (ret < 0)
+               return ret;
 
        return 0;
 }
 
 /**
- * batadv_softif_destroy_vlan() - remove and destroy a softif_vlan object
+ * batadv_softif_destroy_vlan_own() - remove our own softif from the local TT
  * @bat_priv: the bat priv with all the soft interface information
- * @vlan: the object to remove
+ * @vid: the VLAN identifier
+ *
+ * Removes the MAC address of our own soft interface with the given VLAN ID 
from
+ * the local TT.
  */
-static void batadv_softif_destroy_vlan(struct batadv_priv *bat_priv,
-                                      struct batadv_softif_vlan *vlan)
+static void batadv_softif_destroy_vlan_own(struct batadv_priv *bat_priv,
+                                          unsigned short vid)
 {
        /* explicitly remove the associated TT local entry because it is marked
         * with the NOPURGE flag
         */
-       batadv_tt_local_remove(bat_priv, bat_priv->soft_iface->dev_addr,
-                              vlan->vid, "vlan interface destroyed", false);
-
-       batadv_softif_vlan_put(vlan);
+       batadv_tt_local_remove(bat_priv, bat_priv->soft_iface->dev_addr, vid,
+                              "vlan interface destroyed", false);
 }
 
 /**
@@ -629,7 +673,6 @@ static int batadv_interface_add_vid(struct net_device *dev, 
__be16 proto,
                                    unsigned short vid)
 {
        struct batadv_priv *bat_priv = netdev_priv(dev);
-       struct batadv_softif_vlan *vlan;
 
        /* only 802.1Q vlans are supported.
         * batman-adv does not know how to handle other types
@@ -647,25 +690,7 @@ static int batadv_interface_add_vid(struct net_device 
*dev, __be16 proto,
 
        vid |= BATADV_VLAN_HAS_TAG;
 
-       /* if a new vlan is getting created and it already exists, it means that
-        * it was not deleted yet. batadv_softif_vlan_get() increases the
-        * refcount in order to revive the object.
-        *
-        * if it does not exist then create it.
-        */
-       vlan = batadv_softif_vlan_get(bat_priv, vid);
-       if (!vlan)
-               return batadv_softif_create_vlan(bat_priv, vid);
-
-       /* add a new TT local entry. This one will be marked with the NOPURGE
-        * flag. This must be added again, even if the vlan object already
-        * exists, because the entry was deleted by kill_vid()
-        */
-       batadv_tt_local_add(bat_priv->soft_iface,
-                           bat_priv->soft_iface->dev_addr, vid,
-                           BATADV_NULL_IFINDEX, BATADV_NO_MARK);
-
-       return 0;
+       return batadv_softif_create_vlan_own(bat_priv, vid);
 }
 
 /**
@@ -684,7 +709,6 @@ static int batadv_interface_kill_vid(struct net_device 
*dev, __be16 proto,
                                     unsigned short vid)
 {
        struct batadv_priv *bat_priv = netdev_priv(dev);
-       struct batadv_softif_vlan *vlan;
 
        /* only 802.1Q vlans are supported. batman-adv does not know how to
         * handle other types
@@ -698,15 +722,7 @@ static int batadv_interface_kill_vid(struct net_device 
*dev, __be16 proto,
        if (vid == 0)
                return 0;
 
-       vlan = batadv_softif_vlan_get(bat_priv, vid | BATADV_VLAN_HAS_TAG);
-       if (!vlan)
-               return -ENOENT;
-
-       batadv_softif_destroy_vlan(bat_priv, vlan);
-
-       /* finally free the vlan object */
-       batadv_softif_vlan_put(vlan);
-
+       batadv_softif_destroy_vlan_own(bat_priv, vid | BATADV_VLAN_HAS_TAG);
        return 0;
 }
 
@@ -1118,7 +1134,6 @@ static void batadv_softif_destroy_netlink(struct 
net_device *soft_iface,
 {
        struct batadv_priv *bat_priv = netdev_priv(soft_iface);
        struct batadv_hard_iface *hard_iface;
-       struct batadv_softif_vlan *vlan;
 
        list_for_each_entry(hard_iface, &batadv_hardif_list, list) {
                if (hard_iface->soft_iface == soft_iface)
@@ -1126,11 +1141,7 @@ static void batadv_softif_destroy_netlink(struct 
net_device *soft_iface,
        }
 
        /* destroy the "untagged" VLAN */
-       vlan = batadv_softif_vlan_get(bat_priv, BATADV_NO_FLAGS);
-       if (vlan) {
-               batadv_softif_destroy_vlan(bat_priv, vlan);
-               batadv_softif_vlan_put(vlan);
-       }
+       batadv_softif_destroy_vlan_own(bat_priv, BATADV_NO_FLAGS);
 
        unregister_netdevice_queue(soft_iface, head);
 }
diff --git a/net/batman-adv/soft-interface.h b/net/batman-adv/soft-interface.h
index 9f2003f1a497..7050ccd304df 100644
--- a/net/batman-adv/soft-interface.h
+++ b/net/batman-adv/soft-interface.h
@@ -21,10 +21,14 @@ void batadv_interface_rx(struct net_device *soft_iface,
                         struct batadv_orig_node *orig_node);
 bool batadv_softif_is_valid(const struct net_device *net_dev);
 extern struct rtnl_link_ops batadv_link_ops;
-int batadv_softif_create_vlan(struct batadv_priv *bat_priv, unsigned short 
vid);
+int batadv_softif_create_vlan_own(struct batadv_priv *bat_priv,
+                                 unsigned short vid);
 void batadv_softif_vlan_release(struct kref *ref);
 struct batadv_softif_vlan *batadv_softif_vlan_get(struct batadv_priv *bat_priv,
                                                  unsigned short vid);
+struct batadv_softif_vlan *
+batadv_softif_vlan_get_or_create(struct batadv_priv *bat_priv,
+                                unsigned short vid);
 
 /**
  * batadv_softif_vlan_put() - decrease the vlan object refcounter and
diff --git a/net/batman-adv/translation-table.c 
b/net/batman-adv/translation-table.c
index 7e36108bb4ef..144ce64e4600 100644
--- a/net/batman-adv/translation-table.c
+++ b/net/batman-adv/translation-table.c
@@ -626,8 +626,8 @@ static void batadv_tt_global_free(struct batadv_priv 
*bat_priv,
  *
  * Return: true if the client was successfully added, false otherwise.
  */
-bool batadv_tt_local_add(struct net_device *soft_iface, const u8 *addr,
-                        unsigned short vid, int ifindex, u32 mark)
+int batadv_tt_local_add(struct net_device *soft_iface, const u8 *addr,
+                       unsigned short vid, int ifindex, u32 mark)
 {
        struct batadv_priv *bat_priv = netdev_priv(soft_iface);
        struct batadv_tt_local_entry *tt_local;
@@ -639,10 +639,10 @@ bool batadv_tt_local_add(struct net_device *soft_iface, 
const u8 *addr,
        struct hlist_head *head;
        struct batadv_tt_orig_list_entry *orig_entry;
        int hash_added, table_size, packet_size_max;
-       bool ret = false;
        bool roamed_back = false;
        u8 remote_flags;
        u32 match_mark;
+       int ret = 0;
 
        if (ifindex != BATADV_NULL_IFINDEX)
                in_dev = dev_get_by_index(net, ifindex);
@@ -693,21 +693,22 @@ bool batadv_tt_local_add(struct net_device *soft_iface, 
const u8 *addr,
                net_ratelimited_function(batadv_info, soft_iface,
                                         "Local translation table size (%i) 
exceeds maximum packet size (%i); Ignoring new local tt entry: %pM\n",
                                         table_size, packet_size_max, addr);
+               ret = -E2BIG;
                goto out;
        }
 
        tt_local = kmem_cache_alloc(batadv_tl_cache, GFP_ATOMIC);
-       if (!tt_local)
+       if (!tt_local) {
+               ret = -ENOMEM;
                goto out;
+       }
 
        /* increase the refcounter of the related vlan */
-       vlan = batadv_softif_vlan_get(bat_priv, vid);
+       vlan = batadv_softif_vlan_get_or_create(bat_priv, vid);
        if (!vlan) {
-               net_ratelimited_function(batadv_info, soft_iface,
-                                        "adding TT local entry %pM to 
non-existent VLAN %d\n",
-                                        addr, batadv_print_vid(vid));
                kmem_cache_free(batadv_tl_cache, tt_local);
                tt_local = NULL;
+               ret = -ENOMEM;
                goto out;
        }
 
@@ -804,7 +805,7 @@ bool batadv_tt_local_add(struct net_device *soft_iface, 
const u8 *addr,
        if (remote_flags ^ (tt_local->common.flags & BATADV_TT_REMOTE_MASK))
                batadv_tt_local_event(bat_priv, tt_local, BATADV_NO_FLAGS);
 
-       ret = true;
+       ret = 1;
 out:
        batadv_hardif_put(in_hardif);
        dev_put(in_dev);
diff --git a/net/batman-adv/translation-table.h 
b/net/batman-adv/translation-table.h
index d18740d9a22b..bbdda8488c14 100644
--- a/net/batman-adv/translation-table.h
+++ b/net/batman-adv/translation-table.h
@@ -16,8 +16,8 @@
 #include <linux/types.h>
 
 int batadv_tt_init(struct batadv_priv *bat_priv);
-bool batadv_tt_local_add(struct net_device *soft_iface, const u8 *addr,
-                        unsigned short vid, int ifindex, u32 mark);
+int batadv_tt_local_add(struct net_device *soft_iface, const u8 *addr,
+                       unsigned short vid, int ifindex, u32 mark);
 u16 batadv_tt_local_remove(struct batadv_priv *bat_priv,
                           const u8 *addr, unsigned short vid,
                           const char *message, bool roaming);
-- 
2.47.2

Reply via email to