This patch adds the capability to encapsulate and send a node's own multicast data packets. Based on the previously established multicast forwarding table, the sender can decide wheather it actually has to send the multicast data to one or more of its interfaces or not.
Furthermore, the sending procedure also decides whether to broadcast or unicast a multicast data packet to its next-hops, depending on the configured mcast_fanout (default: < 3 next hops on an interface, send seperate unicast packets).
Signed-off-by: Linus Lüssing linus.luessing@saxnet.de --- multicast.c | 165 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ multicast.h | 1 + packet.h | 9 +++ soft-interface.c | 27 ++++++++- types.h | 1 + 5 files changed, 199 insertions(+), 4 deletions(-)
diff --git a/batman-adv/multicast.c b/batman-adv/multicast.c index fefef41..685d3f3 100644 --- a/batman-adv/multicast.c +++ b/batman-adv/multicast.c @@ -23,6 +23,7 @@ #include "multicast.h" #include "hash.h" #include "send.h" +#include "soft-interface.h" #include "hard-interface.h" #include "originator.h" #include "compat.h" @@ -1139,6 +1140,170 @@ int mcast_forw_table_seq_print_text(struct seq_file *seq, void *offset) return 0; }
+static inline void nexthops_from_if_list(struct hlist_head *mcast_if_list, + struct list_head *nexthop_list, + struct bat_priv *bat_priv) +{ + struct batman_if *batman_if; + struct mcast_forw_if_entry *if_entry; + struct mcast_forw_nexthop_entry *nexthop_entry; + struct hlist_node *node, *node2; + struct dest_entries_list *dest_entry; + int mcast_fanout = atomic_read(&bat_priv->mcast_fanout); + + hlist_for_each_entry(if_entry, node, mcast_if_list, list) { + rcu_read_lock(); + batman_if = if_num_to_batman_if(if_entry->if_num); + if (!batman_if) { + rcu_read_unlock(); + continue; + } + + kref_get(&batman_if->refcount); + rcu_read_unlock(); + + + /* send via broadcast */ + if (if_entry->num_nexthops > mcast_fanout) { + dest_entry = kmalloc(sizeof(struct dest_entries_list), + GFP_ATOMIC); + memcpy(dest_entry->dest, broadcast_addr, ETH_ALEN); + dest_entry->batman_if = batman_if; + list_add(&dest_entry->list, nexthop_list); + continue; + } + + /* send separate unicast packets */ + hlist_for_each_entry(nexthop_entry, node2, + &if_entry->mcast_nexthop_list, list) { + if (!get_remaining_timeout(nexthop_entry, bat_priv)) + continue; + + dest_entry = kmalloc(sizeof(struct dest_entries_list), + GFP_ATOMIC); + memcpy(dest_entry->dest, nexthop_entry->neigh_addr, + ETH_ALEN); + + kref_get(&batman_if->refcount); + dest_entry->batman_if = batman_if; + list_add(&dest_entry->list, nexthop_list); + } + kref_put(&batman_if->refcount, hardif_free_ref); + } +} + +static inline void nexthops_from_orig_list(uint8_t *orig, + struct hlist_head *mcast_orig_list, + struct list_head *nexthop_list, + struct bat_priv *bat_priv) +{ + struct mcast_forw_orig_entry *orig_entry; + struct hlist_node *node; + + hlist_for_each_entry(orig_entry, node, mcast_orig_list, list) { + if (memcmp(orig, orig_entry->orig, ETH_ALEN)) + continue; + + nexthops_from_if_list(&orig_entry->mcast_if_list, nexthop_list, + bat_priv); + break; + } +} + +static inline void nexthops_from_table(uint8_t *dest, uint8_t *orig, + struct hlist_head *mcast_forw_table, + struct list_head *nexthop_list, + struct bat_priv *bat_priv) +{ + struct mcast_forw_table_entry *table_entry; + struct hlist_node *node; + + hlist_for_each_entry(table_entry, node, mcast_forw_table, list) { + if (memcmp(dest, table_entry->mcast_addr, ETH_ALEN)) + continue; + + nexthops_from_orig_list(orig, &table_entry->mcast_orig_list, + nexthop_list, bat_priv); + break; + } +} + +static void route_mcast_packet(struct sk_buff *skb, struct bat_priv *bat_priv) +{ + struct sk_buff *skb1; + struct mcast_packet *mcast_packet; + struct ethhdr *ethhdr; + int num_bcasts = 3, i; + struct list_head nexthop_list; + struct dest_entries_list *dest_entry, *tmp; + + mcast_packet = (struct mcast_packet *)skb->data; + ethhdr = (struct ethhdr *)(mcast_packet + 1); + + INIT_LIST_HEAD(&nexthop_list); + + mcast_packet->ttl--; + + spin_lock_bh(&bat_priv->mcast_forw_table_lock); + nexthops_from_table(ethhdr->h_dest, mcast_packet->orig, + &bat_priv->mcast_forw_table, &nexthop_list, + bat_priv); + spin_unlock_bh(&bat_priv->mcast_forw_table_lock); + + list_for_each_entry_safe(dest_entry, tmp, &nexthop_list, list) { + if (is_broadcast_ether_addr(dest_entry->dest)) { + for (i = 0; i < num_bcasts; i++) { + skb1 = skb_clone(skb, GFP_ATOMIC); + send_skb_packet(skb1, dest_entry->batman_if, + dest_entry->dest); + } + } else { + skb1 = skb_clone(skb, GFP_ATOMIC); + send_skb_packet(skb1, dest_entry->batman_if, + dest_entry->dest); + } + kref_put(&dest_entry->batman_if->refcount, hardif_free_ref); + list_del(&dest_entry->list); + kfree(dest_entry); + } +} + +int mcast_send_skb(struct sk_buff *skb, struct bat_priv *bat_priv) +{ + struct mcast_packet *mcast_packet; + + if (!bat_priv->primary_if) + goto dropped; + + if (my_skb_head_push(skb, sizeof(struct mcast_packet)) < 0) + goto dropped; + + mcast_packet = (struct mcast_packet *)skb->data; + mcast_packet->version = COMPAT_VERSION; + mcast_packet->ttl = TTL; + + /* batman packet type: broadcast */ + mcast_packet->packet_type = BAT_MCAST; + + /* hw address of first interface is the orig mac because only + * this mac is known throughout the mesh */ + memcpy(mcast_packet->orig, + bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN); + + /* set broadcast sequence number */ + mcast_packet->seqno = + htonl(atomic_inc_return(&bat_priv->mcast_seqno)); + + route_mcast_packet(skb, bat_priv); + + kfree_skb(skb); + return 0; + +dropped: + kfree_skb(skb); + return 1; +} + int mcast_init(struct bat_priv *bat_priv) { INIT_DELAYED_WORK(&bat_priv->mcast_tracker_work, mcast_tracker_timer); diff --git a/batman-adv/multicast.h b/batman-adv/multicast.h index 40f9da0..2fe9910 100644 --- a/batman-adv/multicast.h +++ b/batman-adv/multicast.h @@ -31,6 +31,7 @@ void route_mcast_tracker_packet(struct sk_buff *tracker_packet, struct bat_priv *bat_priv); void purge_mcast_forw_table(struct bat_priv *bat_priv); int mcast_forw_table_seq_print_text(struct seq_file *seq, void *offset); +int mcast_send_skb(struct sk_buff *skb, struct bat_priv *bat_priv); int mcast_init(struct bat_priv *bat_priv); void mcast_free(struct bat_priv *bat_priv);
diff --git a/batman-adv/packet.h b/batman-adv/packet.h index 6f42a2f..daa5d54 100644 --- a/batman-adv/packet.h +++ b/batman-adv/packet.h @@ -31,6 +31,7 @@ #define BAT_VIS 0x05 #define BAT_UNICAST_FRAG 0x06 #define BAT_MCAST_TRACKER 0x07 +#define BAT_MCAST 0x08
/* this file is included by batctl which needs these defines */ #define COMPAT_VERSION 14 @@ -126,6 +127,14 @@ struct bcast_packet { uint32_t seqno; } __packed;
+struct mcast_packet { + uint8_t packet_type; /* BAT_MCAST */ + uint8_t version; /* batman version field */ + uint8_t orig[6]; + uint32_t seqno; + uint8_t ttl; +} __packed; + /* marks the path for multicast streams */ struct mcast_tracker_packet { uint8_t packet_type; /* BAT_MCAST_TRACKER */ diff --git a/batman-adv/soft-interface.c b/batman-adv/soft-interface.c index f25fe9d..3e16522 100644 --- a/batman-adv/soft-interface.c +++ b/batman-adv/soft-interface.c @@ -38,6 +38,7 @@ #include <linux/if_vlan.h> #include "unicast.h" #include "routing.h" +#include "multicast.h"
static int bat_get_settings(struct net_device *dev, struct ethtool_cmd *cmd); @@ -347,7 +348,7 @@ int interface_tx(struct sk_buff *skb, struct net_device *soft_iface) struct vlan_ethhdr *vhdr; int data_len = skb->len, ret; short vid = -1; - bool do_bcast = false; + bool bcast_dst = false, mcast_dst = false;
if (atomic_read(&bat_priv->mesh_state) != MESH_ACTIVE) goto dropped; @@ -384,12 +385,22 @@ int interface_tx(struct sk_buff *skb, struct net_device *soft_iface) if (ret < 0) goto dropped;
- if (ret == 0) - do_bcast = true; + /* dhcp request, which should be sent to the gateway + * directly? */ + if (ret) + goto unicast; + + if (is_broadcast_ether_addr(ethhdr->h_dest)) + bcast_dst = true; + else if (atomic_read(&bat_priv->mcast_mode) == + MCAST_MODE_PROACT_TRACKING) + mcast_dst = true; + else + bcast_dst = true; }
/* ethernet packet should be broadcasted */ - if (do_bcast) { + if (bcast_dst) { if (!bat_priv->primary_if) goto dropped;
@@ -418,8 +429,15 @@ int interface_tx(struct sk_buff *skb, struct net_device *soft_iface) * the original skb. */ kfree_skb(skb);
+ /* multicast data with path optimization */ + } else if (mcast_dst) { + ret = mcast_send_skb(skb, bat_priv); + if (ret != 0) + goto dropped_freed; + /* unicast packet */ } else { +unicast: ret = unicast_send_skb(skb, bat_priv); if (ret != 0) goto dropped_freed; @@ -608,6 +626,7 @@ struct net_device *softif_create(char *name)
atomic_set(&bat_priv->mesh_state, MESH_INACTIVE); atomic_set(&bat_priv->bcast_seqno, 1); + atomic_set(&bat_priv->mcast_seqno, 1); atomic_set(&bat_priv->hna_local_changed, 0);
bat_priv->primary_if = NULL; diff --git a/batman-adv/types.h b/batman-adv/types.h index 0acfd2e..c3dde7c 100644 --- a/batman-adv/types.h +++ b/batman-adv/types.h @@ -147,6 +147,7 @@ struct bat_priv { atomic_t mcast_fanout; /* uint */ atomic_t log_level; /* uint */ atomic_t bcast_seqno; + atomic_t mcast_seqno; atomic_t bcast_queue_left; atomic_t batman_queue_left; char num_ifaces;