return upper;
 }
 
+/**
+ * batadv_mcast_mla_tvlv_update - update multicast tvlv
+ * @bat_priv: the bat priv with all the soft interface information
+ *
+ * Updates the own multicast tvlv with our current multicast related settings,
+ * capabilities and inabilities.
+ *
+ * Returns true if the tvlv container is registered afterwards. Otherwise
+ * returns false.
+ */
+static bool batadv_mcast_mla_tvlv_update(struct batadv_priv *bat_priv)
+{
+       struct batadv_tvlv_mcast_data mcast_data;
+
+       mcast_data.flags = BATADV_NO_FLAGS;
+       memset(mcast_data.reserved, 0, sizeof(mcast_data.reserved));
+
+       /* Avoid attaching MLAs, if there is a bridge on top of our soft
+        * interface, we don't support that yet (TODO)
+        */
+       if (batadv_mcast_has_bridge(bat_priv)) {
+               if (bat_priv->mcast.enabled) {
+                       batadv_tvlv_container_unregister(bat_priv,
+                                                        BATADV_TVLV_MCAST, 1);
+                       bat_priv->mcast.enabled = false;
+               }
+
+               return false;
+       }
+
+       if (!bat_priv->mcast.enabled ||
+           mcast_data.flags != bat_priv->mcast.flags) {
+               batadv_tvlv_container_register(bat_priv, BATADV_TVLV_MCAST, 1,
+                                              &mcast_data, sizeof(mcast_data));
+               bat_priv->mcast.flags = mcast_data.flags;
+               bat_priv->mcast.enabled = true;
+       }
+
+       return true;
+}
+
 /**
  * batadv_mcast_mla_update - update the own MLAs
  * @bat_priv: the bat priv with all the soft interface information
  *
- * Update the own multicast listener announcements in the translation
- * table.
+ * Updates the own multicast listener announcements in the translation
+ * table as well as the own, announced multicast tvlv container.
  */
 void batadv_mcast_mla_update(struct batadv_priv *bat_priv)
 {
        struct hlist_head mcast_list = HLIST_HEAD_INIT;
        int ret;
 
-       /* Avoid attaching MLAs, if there is a bridge on top of our soft
-        * interface, we don't support that yet (TODO)
-        */
-       if (batadv_mcast_has_bridge(bat_priv))
+       if (!batadv_mcast_mla_tvlv_update(bat_priv))
                goto update;
 
        ret = batadv_mcast_mla_softif_get(soft_iface, &mcast_list);
        batadv_mcast_mla_list_free(&mcast_list);
 }
 
+/**
+ * batadv_mcast_tvlv_ogm_handler_v1 - process incoming multicast tvlv container
+ * @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_mcast_tvlv_ogm_handler_v1(struct batadv_priv *bat_priv,
+                                            struct batadv_orig_node *orig,
+                                            uint8_t flags,
+                                            void *tvlv_value,
+                                            uint16_t tvlv_value_len)
+{
+       bool orig_mcast_enabled = !(flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND);
+       uint8_t mcast_flags = BATADV_NO_FLAGS;
+       bool orig_initialized;
+
+       orig_initialized = orig->capa_initialized & BATADV_ORIG_CAPA_HAS_MCAST;
+
+       /* If mcast support is turned on decrease the disabled mcast node
+        * counter only if we had increased it for this node before. If this
+        * is a completely new orig_node no need to decrease the counter.
+        */
+       if (orig_mcast_enabled &&
+           !(orig->capabilities & BATADV_ORIG_CAPA_HAS_MCAST)) {
+               if (orig_initialized)
+                       atomic_dec(&bat_priv->mcast.num_disabled);
+               orig->capabilities |= BATADV_ORIG_CAPA_HAS_MCAST;
+       /* If mcast support is being switched off increase the disabled
+        * mcast node counter.
+        */
+       } else if (!orig_mcast_enabled &&
+                  orig->capabilities & BATADV_ORIG_CAPA_HAS_MCAST) {
+               atomic_inc(&bat_priv->mcast.num_disabled);
+               orig->capabilities &= ~BATADV_ORIG_CAPA_HAS_MCAST;
+       }
+
+       orig->capa_initialized |= BATADV_ORIG_CAPA_HAS_MCAST;
+
+       if (orig_mcast_enabled && tvlv_value &&
+           (tvlv_value_len >= sizeof(mcast_flags)))
+               mcast_flags = *(uint8_t *)tvlv_value;
+
+       orig->mcast_flags = mcast_flags;
+}
+
+/**
+ * batadv_mcast_init - initialize the multicast optimizations structures
+ * @bat_priv: the bat priv with all the soft interface information
+ */
+void batadv_mcast_init(struct batadv_priv *bat_priv)
+{
+       batadv_tvlv_handler_register(bat_priv, batadv_mcast_tvlv_ogm_handler_v1,
+                                    NULL, BATADV_TVLV_MCAST, 1,
+                                    BATADV_TVLV_HANDLER_OGM_CIFNOTFND);
+}
+
 /**
  * batadv_mcast_free - free the multicast optimizations structures
  * @bat_priv: the bat priv with all the soft interface information
  */
 void batadv_mcast_free(struct batadv_priv *bat_priv)
 {
+       batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_MCAST, 1);
+       batadv_tvlv_handler_unregister(bat_priv, BATADV_TVLV_MCAST, 1);
+
        batadv_mcast_mla_tt_retract(bat_priv, NULL);
 }
+
+/**
+ * batadv_mcast_purge_orig - reset originator global mcast state modifications
+ * @orig: the originator which is going to get purged
+ */
+void batadv_mcast_purge_orig(struct batadv_orig_node *orig)
+{
+       struct batadv_priv *bat_priv = orig->bat_priv;
+
+       if (!(orig->capabilities & BATADV_ORIG_CAPA_HAS_MCAST))
+               atomic_dec(&bat_priv->mcast.num_disabled);
+}
 
  * @batadv_dat_addr_t:  address of the orig node in the distributed hash
  * @last_seen: time when last packet from this node was received
  * @bcast_seqno_reset: time when the broadcast seqno window was reset
+ * @mcast_flags: multicast flags announced by the orig node
  * @capabilities: announced capabilities of this originator
  * @capa_initialized: bitfield to remember whether a capability was initialized
  * @last_ttvn: last seen translation table version number
 #endif
        unsigned long last_seen;
        unsigned long bcast_seqno_reset;
+#ifdef CONFIG_BATMAN_ADV_MCAST
+       uint8_t mcast_flags;
+#endif
        uint8_t capabilities;
        uint8_t capa_initialized;
        atomic_t last_ttvn;
  * @BATADV_ORIG_CAPA_HAS_DAT: orig node has distributed arp table enabled
  * @BATADV_ORIG_CAPA_HAS_NC: orig node has network coding enabled
  * @BATADV_ORIG_CAPA_HAS_TT: orig node has tt capability
+ * @BATADV_ORIG_CAPA_HAS_MCAST: orig node has some multicast capability
+ *  (= orig node announces a tvlv of type BATADV_TVLV_MCAST)
  */
 enum batadv_orig_capabilities {
        BATADV_ORIG_CAPA_HAS_DAT = BIT(0),
        BATADV_ORIG_CAPA_HAS_NC = BIT(1),
        BATADV_ORIG_CAPA_HAS_TT = BIT(2),
+       BATADV_ORIG_CAPA_HAS_MCAST = BIT(3),
 };
 
 /**
 /**
  * struct batadv_priv_mcast - per mesh interface mcast data
  * @mla_list: list of multicast addresses we are currently announcing via TT
+ * @flags: the flags we have last sent in our mcast tvlv
+ * @enabled: whether the multicast tvlv is currently enabled
+ * @num_disabled: number of nodes that have no mcast tvlv
  */
 struct batadv_priv_mcast {
        struct hlist_head mla_list;
+       uint8_t flags;
+       bool enabled;
+       atomic_t num_disabled;
 };
 #endif