/* send a batman ogm packet */
 static void batadv_iv_ogm_emit(struct batadv_forw_packet *forw_packet)
 {
-       struct batadv_hard_iface *hard_iface;
        struct net_device *soft_iface;
        struct batadv_priv *bat_priv;
        struct batadv_hard_iface *primary_if = NULL;
-       struct batadv_ogm_packet *batadv_ogm_packet;
-       unsigned char directlink;
-       uint8_t *packet_pos;
-
-       packet_pos = forw_packet->skb->data;
-       batadv_ogm_packet = (struct batadv_ogm_packet *)packet_pos;
-       directlink = (batadv_ogm_packet->flags & BATADV_DIRECTLINK ? 1 : 0);
 
        if (!forw_packet->if_incoming) {
                pr_err("Error - can't forward packet: incoming iface not specified\n");
        soft_iface = forw_packet->if_incoming->soft_iface;
        bat_priv = netdev_priv(soft_iface);
 
-       if (forw_packet->if_incoming->if_status != BATADV_IF_ACTIVE)
+       if (WARN_ON(!forw_packet->if_outgoing))
                goto out;
 
-       primary_if = batadv_primary_if_get_selected(bat_priv);
-       if (!primary_if)
+       if (WARN_ON(forw_packet->if_outgoing->soft_iface != soft_iface))
                goto out;
 
-       /* multihomed peer assumed
-        * non-primary OGMs are only broadcasted on their interface
-        */
-       if ((directlink && (batadv_ogm_packet->ttl == 1)) ||
-           (forw_packet->own && (forw_packet->if_incoming != primary_if))) {
-               /* FIXME: what about aggregated packets ? */
-               batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
-                          "%s packet (originator %pM, seqno %u, TTL %d) on interface %s [%pM]\n",
-                          (forw_packet->own ? "Sending own" : "Forwarding"),
-                          batadv_ogm_packet->orig,
-                          ntohl(batadv_ogm_packet->seqno),
-                          batadv_ogm_packet->ttl,
-                          forw_packet->if_incoming->net_dev->name,
-                          forw_packet->if_incoming->net_dev->dev_addr);
-
-               /* skb is only used once and than forw_packet is free'd */
-               batadv_send_skb_packet(forw_packet->skb,
-                                      forw_packet->if_incoming,
-                                      batadv_broadcast_addr);
-               forw_packet->skb = NULL;
-
+       if (forw_packet->if_incoming->if_status != BATADV_IF_ACTIVE)
                goto out;
-       }
 
-       /* broadcast on every interface */
-       rcu_read_lock();
-       list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) {
-               if (hard_iface->soft_iface != soft_iface)
-                       continue;
+       primary_if = batadv_primary_if_get_selected(bat_priv);
+       if (!primary_if)
+               goto out;
 
-               batadv_iv_ogm_send_to_if(forw_packet, hard_iface);
-       }
-       rcu_read_unlock();
+       /* only for one specific outgoing interface */
+       batadv_iv_ogm_send_to_if(forw_packet, forw_packet->if_outgoing);
 
 out:
        if (primary_if)
                batadv_hardif_free_ref(primary_if);
 }
 
-/* return true if new_packet can be aggregated with forw_packet */
+/**
+ * batadv_iv_ogm_can_aggregate - find out if an OGM can be aggregated on an
+ *  existing forward packet
+ * @new_bat_ogm_packet: OGM packet to be aggregated
+ * @bat_priv: the bat priv with all the soft interface information
+ * @packet_len: (total) length of the OGM
+ * @send_time: timestamp (jiffies) when the packet is to be sent
+ * @direktlink: true if this is a direct link packet
+ * @if_incoming: interface where the packet was received
+ * @if_outgoing: interface for which the retransmission should be considered
+ * @forw_packet: the forwarded packet which should be checked
+ *
+ * Returns true if new_packet can be aggregated with forw_packet
+ */
 static bool
 batadv_iv_ogm_can_aggregate(const struct batadv_ogm_packet *new_bat_ogm_packet,
                            struct batadv_priv *bat_priv,
                            int packet_len, unsigned long send_time,
                            bool directlink,
                            const struct batadv_hard_iface *if_incoming,
+                           const struct batadv_hard_iface *if_outgoing,
                            const struct batadv_forw_packet *forw_packet)
 {
        struct batadv_ogm_packet *batadv_ogm_packet;
                if (!primary_if)
                        goto out;
 
+               /* packet is not leaving on the same interface. */
+               if (forw_packet->if_outgoing != if_outgoing)
+                       goto out;
+
                /* packets without direct link flag and high TTL
                 * are flooded through the net
                 */
        return res;
 }
 
-/* create a new aggregated packet and add this packet to it */
+/* batadv_iv_ogm_aggregate_new - create a new aggregated packet and add this
+ *  packet to it.
+ * @packet_buff: pointer to the OGM
+ * @packet_len: (total) length of the OGM
+ * @send_time: timestamp (jiffies) when the packet is to be sent
+ * @direct_link: whether this OGM has direct link status
+ * @if_incoming: interface where the packet was received
+ * @if_outgoing: interface for which the retransmission should be considered
+ * @own_packet: true if it is a self-generated ogm
+ */
 static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff,
                                        int packet_len, unsigned long send_time,
                                        bool direct_link,
                                        struct batadv_hard_iface *if_incoming,
+                                       struct batadv_hard_iface *if_outgoing,
                                        int own_packet)
 {
        struct batadv_priv *bat_priv = netdev_priv(if_incoming->soft_iface);
        if (!atomic_inc_not_zero(&if_incoming->refcount))
                return;
 
+       if (!atomic_inc_not_zero(&if_outgoing->refcount))
+               goto out_free_incoming;
+
        /* own packet should always be scheduled */
        if (!own_packet) {
                if (!batadv_atomic_dec_not_zero(&bat_priv->batman_queue_left)) {
 
        forw_packet_aggr->own = own_packet;
        forw_packet_aggr->if_incoming = if_incoming;
+       forw_packet_aggr->if_outgoing = if_outgoing;
        forw_packet_aggr->num_packets = 0;
        forw_packet_aggr->direct_link_flags = BATADV_NO_FLAGS;
        forw_packet_aggr->send_time = send_time;
 
        return;
 out:
+       batadv_hardif_free_ref(if_outgoing);
+out_free_incoming:
        batadv_hardif_free_ref(if_incoming);
 }
 
        }
 }
 
+/**
+ * batadv_iv_ogm_queue_add - queue up an OGM for transmission
+ * @bat_priv: the bat priv with all the soft interface information
+ * @packet_buff: pointer to the OGM
+ * @packet_len: (total) length of the OGM
+ * @if_incoming: interface where the packet was received
+ * @if_outgoing: interface for which the retransmission should be considered
+ * @own_packet: true if it is a self-generated ogm
+ * @send_time: timestamp (jiffies) when the packet is to be sent
+ */
 static void batadv_iv_ogm_queue_add(struct batadv_priv *bat_priv,
                                    unsigned char *packet_buff,
                                    int packet_len,
                                    struct batadv_hard_iface *if_incoming,
+                                   struct batadv_hard_iface *if_outgoing,
                                    int own_packet, unsigned long send_time)
 {
        /* _aggr -> pointer to the packet we want to aggregate with
                                                        bat_priv, packet_len,
                                                        send_time, direct_link,
                                                        if_incoming,
+                                                       if_outgoing,
                                                        forw_packet_pos)) {
                                forw_packet_aggr = forw_packet_pos;
                                break;
 
                batadv_iv_ogm_aggregate_new(packet_buff, packet_len,
                                            send_time, direct_link,
-                                           if_incoming, own_packet);
+                                           if_incoming, if_outgoing,
+                                           own_packet);
        } else {
                batadv_iv_ogm_aggregate(forw_packet_aggr, packet_buff,
                                        packet_len, direct_link);
                                  struct batadv_ogm_packet *batadv_ogm_packet,
                                  bool is_single_hop_neigh,
                                  bool is_from_best_next_hop,
-                                 struct batadv_hard_iface *if_incoming)
+                                 struct batadv_hard_iface *if_incoming,
+                                 struct batadv_hard_iface *if_outgoing)
 {
        struct batadv_priv *bat_priv = netdev_priv(if_incoming->soft_iface);
        uint16_t tvlv_len;
 
        batadv_iv_ogm_queue_add(bat_priv, (unsigned char *)batadv_ogm_packet,
                                BATADV_OGM_HLEN + tvlv_len,
-                               if_incoming, 0, batadv_iv_ogm_fwd_send_time());
+                               if_incoming, if_outgoing, 0,
+                               batadv_iv_ogm_fwd_send_time());
 }
 
 /**
        struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
        unsigned char **ogm_buff = &hard_iface->bat_iv.ogm_buff;
        struct batadv_ogm_packet *batadv_ogm_packet;
-       struct batadv_hard_iface *primary_if;
+       struct batadv_hard_iface *primary_if, *tmp_hard_iface;
        int *ogm_buff_len = &hard_iface->bat_iv.ogm_buff_len;
        uint32_t seqno;
        uint16_t tvlv_len = 0;
+       unsigned long send_time;
 
        primary_if = batadv_primary_if_get_selected(bat_priv);
 
        atomic_inc(&hard_iface->bat_iv.ogm_seqno);
 
        batadv_iv_ogm_slide_own_bcast_window(hard_iface);
-       batadv_iv_ogm_queue_add(bat_priv, hard_iface->bat_iv.ogm_buff,
-                               hard_iface->bat_iv.ogm_buff_len, hard_iface, 1,
-                               batadv_iv_ogm_emit_send_time(bat_priv));
 
+       send_time = batadv_iv_ogm_emit_send_time(bat_priv);
+
+       if (hard_iface != primary_if) {
+               /* OGMs from secondary interfaces are only scheduled on their
+                * respective interfaces.
+                */
+               batadv_iv_ogm_queue_add(bat_priv, *ogm_buff, *ogm_buff_len,
+                                       hard_iface, hard_iface, 1, send_time);
+               goto out;
+       }
+
+       /* OGMs from primary interfaces are scheduled on all
+        * interfaces.
+        */
+       rcu_read_lock();
+       list_for_each_entry_rcu(tmp_hard_iface, &batadv_hardif_list, list) {
+               if (tmp_hard_iface->soft_iface != hard_iface->soft_iface)
+                               continue;
+               batadv_iv_ogm_queue_add(bat_priv, *ogm_buff,
+                                       *ogm_buff_len, hard_iface,
+                                       tmp_hard_iface, 1, send_time);
+       }
+       rcu_read_unlock();
+
+out:
        if (primary_if)
                batadv_hardif_free_ref(primary_if);
 }
        }
        batadv_orig_ifinfo_free_ref(orig_ifinfo);
 
+       /* only forward for specific interface, not for the default one. */
+       if (if_outgoing == BATADV_IF_DEFAULT)
+               goto out_neigh;
+
        /* is single hop (direct) neighbor */
        if (is_single_hop_neigh) {
                /* OGMs from secondary interfaces should only scheduled once
                /* mark direct link on incoming interface */
                batadv_iv_ogm_forward(orig_node, ethhdr, ogm_packet,
                                      is_single_hop_neigh,
-                                     is_from_best_next_hop, if_incoming);
+                                     is_from_best_next_hop, if_incoming,
+                                     if_outgoing);
 
                batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
                           "Forwarding packet: rebroadcast neighbor packet with direct link flag\n");
                goto out_neigh;
        }
 
-       /* only forward the packet on the default interface until the
-        * OGM forwarding has been reworked to send on specific interfaces.
-        */
-       if (if_outgoing != BATADV_IF_DEFAULT)
-               goto out_neigh;
        batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
                   "Forwarding packet: rebroadcast originator packet\n");
        batadv_iv_ogm_forward(orig_node, ethhdr, ogm_packet,
                              is_single_hop_neigh, is_from_best_next_hop,
-                             if_incoming);
+                             if_incoming, if_outgoing);
 
 out_neigh:
        if ((orig_neigh_node) && (!is_single_hop_neigh))