batman-adv: Make bat_priv->primary_if an rcu protected pointer

Message ID 1303307015-970-1-git-send-email-lindner_marek@yahoo.de (mailing list archive)
State Accepted, archived
Headers

Commit Message

Marek Lindner April 20, 2011, 1:43 p.m. UTC
  The rcu protected macros rcu_dereference() and rcu_assign_pointer()
for the bat_priv->primary_if need to be used, as well as spin/rcu locking.

Otherwise we might end up using a primary_if pointer pointing to already
freed memory.

Signed-off-by: Marek Lindner <lindner_marek@yahoo.de>
---
 gateway_client.c    |   33 ++++++++++-------
 hard-interface.c    |  100 +++++++++++++++++++++++++++++++++++++-------------
 hard-interface.h    |    1 +
 icmp_socket.c       |   19 +++++++---
 originator.c        |   34 +++++++++++-------
 routing.c           |   18 ++++++---
 send.c              |   17 +++++++--
 soft-interface.c    |   57 +++++++++++++++++++----------
 translation-table.c |   57 ++++++++++++++++++++++-------
 types.h             |    2 +-
 unicast.c           |   16 ++++++---
 vis.c               |   37 +++++++++++++------
 12 files changed, 273 insertions(+), 118 deletions(-)
  

Comments

Marek Lindner April 26, 2011, 12:03 a.m. UTC | #1
On Wednesday 20 April 2011 15:43:34 Marek Lindner wrote:
> The rcu protected macros rcu_dereference() and rcu_assign_pointer()
> for the bat_priv->primary_if need to be used, as well as spin/rcu locking.
> 
> Otherwise we might end up using a primary_if pointer pointing to already
> freed memory.

Applied in revision 89079ae.

Regards,
Marek
  

Patch

diff --git a/gateway_client.c b/gateway_client.c
index 2acd7a6..c394154 100644
--- a/gateway_client.c
+++ b/gateway_client.c
@@ -435,30 +435,32 @@  int gw_client_seq_print_text(struct seq_file *seq, void *offset)
 {
 	struct net_device *net_dev = (struct net_device *)seq->private;
 	struct bat_priv *bat_priv = netdev_priv(net_dev);
+	struct hard_iface *primary_if;
 	struct gw_node *gw_node;
 	struct hlist_node *node;
-	int gw_count = 0;
+	int gw_count = 0, ret = 0;
 
-	if (!bat_priv->primary_if) {
-
-		return seq_printf(seq, "BATMAN mesh %s disabled - please "
-				  "specify interfaces to enable it\n",
-				  net_dev->name);
+	primary_if = primary_if_get_selected(bat_priv);
+	if (!primary_if) {
+		ret = seq_printf(seq, "BATMAN mesh %s disabled - please "
+				 "specify interfaces to enable it\n",
+				 net_dev->name);
+		goto out;
 	}
 
-	if (bat_priv->primary_if->if_status != IF_ACTIVE) {
-
-		return seq_printf(seq, "BATMAN mesh %s disabled - "
-				       "primary interface not active\n",
-				       net_dev->name);
+	if (primary_if->if_status != IF_ACTIVE) {
+		ret = seq_printf(seq, "BATMAN mesh %s disabled - "
+				 "primary interface not active\n",
+				 net_dev->name);
+		goto out;
 	}
 
 	seq_printf(seq, "      %-12s (%s/%i) %17s [%10s]: gw_class ... "
 		   "[B.A.T.M.A.N. adv %s%s, MainIF/MAC: %s/%pM (%s)]\n",
 		   "Gateway", "#", TQ_MAX_VALUE, "Nexthop",
 		   "outgoingIF", SOURCE_VERSION, REVISION_VERSION_STR,
-		   bat_priv->primary_if->net_dev->name,
-		   bat_priv->primary_if->net_dev->dev_addr, net_dev->name);
+		   primary_if->net_dev->name,
+		   primary_if->net_dev->dev_addr, net_dev->name);
 
 	rcu_read_lock();
 	hlist_for_each_entry_rcu(gw_node, node, &bat_priv->gw_list, list) {
@@ -476,7 +478,10 @@  int gw_client_seq_print_text(struct seq_file *seq, void *offset)
 	if (gw_count == 0)
 		seq_printf(seq, "No gateways in range ...\n");
 
-	return 0;
+out:
+	if (primary_if)
+		hardif_free_ref(primary_if);
+	return ret;
 }
 
 int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb)
diff --git a/hard-interface.c b/hard-interface.c
index b3058e4..158d127 100644
--- a/hard-interface.c
+++ b/hard-interface.c
@@ -110,47 +110,77 @@  out:
 	return hard_iface;
 }
 
-static void update_primary_addr(struct bat_priv *bat_priv)
+static void primary_if_update_addr(struct bat_priv *bat_priv)
 {
 	struct vis_packet *vis_packet;
+	struct hard_iface *primary_if;
+
+	primary_if = primary_if_get_selected(bat_priv);
+	if (!primary_if)
+		goto out;
 
 	vis_packet = (struct vis_packet *)
 				bat_priv->my_vis_info->skb_packet->data;
-	memcpy(vis_packet->vis_orig,
-	       bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN);
+	memcpy(vis_packet->vis_orig, primary_if->net_dev->dev_addr, ETH_ALEN);
 	memcpy(vis_packet->sender_orig,
-	       bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN);
+	       primary_if->net_dev->dev_addr, ETH_ALEN);
+
+out:
+	if (primary_if)
+		hardif_free_ref(primary_if);
 }
 
-static void set_primary_if(struct bat_priv *bat_priv,
-			   struct hard_iface *hard_iface)
+struct hard_iface *primary_if_get_selected(struct bat_priv *bat_priv)
 {
-	struct batman_packet *batman_packet;
-	struct hard_iface *old_if;
+	struct hard_iface *hard_iface;
 
-	if (hard_iface && !atomic_inc_not_zero(&hard_iface->refcount))
+	rcu_read_lock();
+	hard_iface = rcu_dereference(bat_priv->primary_if);
+	if (!hard_iface)
+		goto out;
+
+	if (!atomic_inc_not_zero(&hard_iface->refcount))
 		hard_iface = NULL;
 
-	old_if = bat_priv->primary_if;
-	bat_priv->primary_if = hard_iface;
+out:
+	rcu_read_unlock();
+	return hard_iface;
+}
 
-	if (old_if)
-		hardif_free_ref(old_if);
+static void primary_if_select(struct bat_priv *bat_priv,
+			      struct hard_iface *new_hard_iface)
+{
+	struct hard_iface *curr_hard_iface;
+	struct batman_packet *batman_packet;
 
-	if (!bat_priv->primary_if)
-		return;
+	spin_lock_bh(&hardif_list_lock);
 
-	batman_packet = (struct batman_packet *)(hard_iface->packet_buff);
+	if (new_hard_iface && !atomic_inc_not_zero(&new_hard_iface->refcount))
+		new_hard_iface = NULL;
+
+	curr_hard_iface = bat_priv->primary_if;
+	rcu_assign_pointer(bat_priv->primary_if, new_hard_iface);
+
+	if (curr_hard_iface)
+		hardif_free_ref(curr_hard_iface);
+
+	if (!new_hard_iface)
+		goto out;
+
+	batman_packet = (struct batman_packet *)(new_hard_iface->packet_buff);
 	batman_packet->flags = PRIMARIES_FIRST_HOP;
 	batman_packet->ttl = TTL;
 
-	update_primary_addr(bat_priv);
+	primary_if_update_addr(bat_priv);
 
 	/***
 	 * hacky trick to make sure that we send the HNA information via
 	 * our new primary interface
 	 */
 	atomic_set(&bat_priv->hna_local_changed, 1);
+
+out:
+	spin_unlock_bh(&hardif_list_lock);
 }
 
 static bool hardif_is_iface_up(struct hard_iface *hard_iface)
@@ -236,9 +266,10 @@  void update_min_mtu(struct net_device *soft_iface)
 static void hardif_activate_interface(struct hard_iface *hard_iface)
 {
 	struct bat_priv *bat_priv;
+	struct hard_iface *primary_if = NULL;
 
 	if (hard_iface->if_status != IF_INACTIVE)
-		return;
+		goto out;
 
 	bat_priv = netdev_priv(hard_iface->soft_iface);
 
@@ -249,14 +280,18 @@  static void hardif_activate_interface(struct hard_iface *hard_iface)
 	 * the first active interface becomes our primary interface or
 	 * the next active interface after the old primay interface was removed
 	 */
-	if (!bat_priv->primary_if)
-		set_primary_if(bat_priv, hard_iface);
+	primary_if = primary_if_get_selected(bat_priv);
+	if (!primary_if)
+		primary_if_select(bat_priv, hard_iface);
 
 	bat_info(hard_iface->soft_iface, "Interface activated: %s\n",
 		 hard_iface->net_dev->name);
 
 	update_min_mtu(hard_iface->soft_iface);
-	return;
+
+out:
+	if (primary_if)
+		hardif_free_ref(primary_if);
 }
 
 static void hardif_deactivate_interface(struct hard_iface *hard_iface)
@@ -386,12 +421,13 @@  err:
 void hardif_disable_interface(struct hard_iface *hard_iface)
 {
 	struct bat_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
+	struct hard_iface *primary_if = NULL;
 
 	if (hard_iface->if_status == IF_ACTIVE)
 		hardif_deactivate_interface(hard_iface);
 
 	if (hard_iface->if_status != IF_INACTIVE)
-		return;
+		goto out;
 
 	bat_info(hard_iface->soft_iface, "Removing interface: %s\n",
 		 hard_iface->net_dev->name);
@@ -400,11 +436,12 @@  void hardif_disable_interface(struct hard_iface *hard_iface)
 	bat_priv->num_ifaces--;
 	orig_hash_del_if(hard_iface, bat_priv->num_ifaces);
 
-	if (hard_iface == bat_priv->primary_if) {
+	primary_if = primary_if_get_selected(bat_priv);
+	if (hard_iface == primary_if) {
 		struct hard_iface *new_if;
 
 		new_if = hardif_get_active(hard_iface->soft_iface);
-		set_primary_if(bat_priv, new_if);
+		primary_if_select(bat_priv, new_if);
 
 		if (new_if)
 			hardif_free_ref(new_if);
@@ -425,6 +462,10 @@  void hardif_disable_interface(struct hard_iface *hard_iface)
 
 	hard_iface->soft_iface = NULL;
 	hardif_free_ref(hard_iface);
+
+out:
+	if (primary_if)
+		hardif_free_ref(primary_if);
 }
 
 static struct hard_iface *hardif_add_interface(struct net_device *net_dev)
@@ -514,6 +555,7 @@  static int hard_if_event(struct notifier_block *this,
 {
 	struct net_device *net_dev = (struct net_device *)ptr;
 	struct hard_iface *hard_iface = hardif_get_by_netdev(net_dev);
+	struct hard_iface *primary_if = NULL;
 	struct bat_priv *bat_priv;
 
 	if (!hard_iface && event == NETDEV_REGISTER)
@@ -549,8 +591,12 @@  static int hard_if_event(struct notifier_block *this,
 		update_mac_addresses(hard_iface);
 
 		bat_priv = netdev_priv(hard_iface->soft_iface);
-		if (hard_iface == bat_priv->primary_if)
-			update_primary_addr(bat_priv);
+		primary_if = primary_if_get_selected(bat_priv);
+		if (!primary_if)
+			goto hardif_put;
+
+		if (hard_iface == primary_if)
+			primary_if_update_addr(bat_priv);
 		break;
 	default:
 		break;
@@ -559,6 +605,8 @@  static int hard_if_event(struct notifier_block *this,
 hardif_put:
 	hardif_free_ref(hard_iface);
 out:
+	if (primary_if)
+		hardif_free_ref(primary_if);
 	return NOTIFY_DONE;
 }
 
diff --git a/hard-interface.h b/hard-interface.h
index a9ddf36..d5f6351 100644
--- a/hard-interface.h
+++ b/hard-interface.h
@@ -38,6 +38,7 @@  void hardif_remove_interfaces(void);
 int hardif_min_mtu(struct net_device *soft_iface);
 void update_min_mtu(struct net_device *soft_iface);
 void hardif_free_rcu(struct rcu_head *rcu);
+struct hard_iface *primary_if_get_selected(struct bat_priv *bat_priv);
 
 static inline void hardif_free_ref(struct hard_iface *hard_iface)
 {
diff --git a/icmp_socket.c b/icmp_socket.c
index 49079c2..fa22ba2 100644
--- a/icmp_socket.c
+++ b/icmp_socket.c
@@ -153,6 +153,7 @@  static ssize_t bat_socket_write(struct file *file, const char __user *buff,
 {
 	struct socket_client *socket_client = file->private_data;
 	struct bat_priv *bat_priv = socket_client->bat_priv;
+	struct hard_iface *primary_if = NULL;
 	struct sk_buff *skb;
 	struct icmp_packet_rr *icmp_packet;
 
@@ -167,15 +168,21 @@  static ssize_t bat_socket_write(struct file *file, const char __user *buff,
 		return -EINVAL;
 	}
 
-	if (!bat_priv->primary_if)
-		return -EFAULT;
+	primary_if = primary_if_get_selected(bat_priv);
+
+	if (!primary_if) {
+		len = -EFAULT;
+		goto out;
+	}
 
 	if (len >= sizeof(struct icmp_packet_rr))
 		packet_len = sizeof(struct icmp_packet_rr);
 
 	skb = dev_alloc_skb(packet_len + sizeof(struct ethhdr));
-	if (!skb)
-		return -ENOMEM;
+	if (!skb) {
+		len = -ENOMEM;
+		goto out;
+	}
 
 	skb_reserve(skb, sizeof(struct ethhdr));
 	icmp_packet = (struct icmp_packet_rr *)skb_put(skb, packet_len);
@@ -233,7 +240,7 @@  static ssize_t bat_socket_write(struct file *file, const char __user *buff,
 		goto dst_unreach;
 
 	memcpy(icmp_packet->orig,
-	       bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN);
+	       primary_if->net_dev->dev_addr, ETH_ALEN);
 
 	if (packet_len == sizeof(struct icmp_packet_rr))
 		memcpy(icmp_packet->rr,
@@ -248,6 +255,8 @@  dst_unreach:
 free_skb:
 	kfree_skb(skb);
 out:
+	if (primary_if)
+		hardif_free_ref(primary_if);
 	if (neigh_node)
 		neigh_node_free_ref(neigh_node);
 	if (orig_node)
diff --git a/originator.c b/originator.c
index 5b8fe32..ef4a9be 100644
--- a/originator.c
+++ b/originator.c
@@ -405,29 +405,34 @@  int orig_seq_print_text(struct seq_file *seq, void *offset)
 	struct hashtable_t *hash = bat_priv->orig_hash;
 	struct hlist_node *node, *node_tmp;
 	struct hlist_head *head;
+	struct hard_iface *primary_if;
 	struct orig_node *orig_node;
 	struct neigh_node *neigh_node, *neigh_node_tmp;
 	int batman_count = 0;
 	int last_seen_secs;
 	int last_seen_msecs;
-	int i;
+	int i, ret = 0;
 
-	if ((!bat_priv->primary_if) ||
-	    (bat_priv->primary_if->if_status != IF_ACTIVE)) {
-		if (!bat_priv->primary_if)
-			return seq_printf(seq, "BATMAN mesh %s disabled - "
-				     "please specify interfaces to enable it\n",
-				     net_dev->name);
+	primary_if = primary_if_get_selected(bat_priv);
 
-		return seq_printf(seq, "BATMAN mesh %s "
-				  "disabled - primary interface not active\n",
-				  net_dev->name);
+	if (!primary_if) {
+		ret = seq_printf(seq, "BATMAN mesh %s disabled - "
+				 "please specify interfaces to enable it\n",
+				 net_dev->name);
+		goto out;
+	}
+
+	if (primary_if->if_status != IF_ACTIVE) {
+		ret = seq_printf(seq, "BATMAN mesh %s "
+				 "disabled - primary interface not active\n",
+				 net_dev->name);
+		goto out;
 	}
 
 	seq_printf(seq, "[B.A.T.M.A.N. adv %s%s, MainIF/MAC: %s/%pM (%s)]\n",
 		   SOURCE_VERSION, REVISION_VERSION_STR,
-		   bat_priv->primary_if->net_dev->name,
-		   bat_priv->primary_if->net_dev->dev_addr, net_dev->name);
+		   primary_if->net_dev->name,
+		   primary_if->net_dev->dev_addr, net_dev->name);
 	seq_printf(seq, "  %-15s %s (%s/%i) %17s [%10s]: %20s ...\n",
 		   "Originator", "last-seen", "#", TQ_MAX_VALUE, "Nexthop",
 		   "outgoingIF", "Potential nexthops");
@@ -474,7 +479,10 @@  next:
 	if (batman_count == 0)
 		seq_printf(seq, "No batman nodes in range ...\n");
 
-	return 0;
+out:
+	if (primary_if)
+		hardif_free_ref(primary_if);
+	return ret;
 }
 
 static int orig_node_add_if(struct orig_node *orig_node, int max_if_num)
diff --git a/routing.c b/routing.c
index 2d77bd3..49f5715 100644
--- a/routing.c
+++ b/routing.c
@@ -904,6 +904,7 @@  int recv_bat_packet(struct sk_buff *skb, struct hard_iface *hard_iface)
 static int recv_my_icmp_packet(struct bat_priv *bat_priv,
 			       struct sk_buff *skb, size_t icmp_len)
 {
+	struct hard_iface *primary_if = NULL;
 	struct orig_node *orig_node = NULL;
 	struct neigh_node *router = NULL;
 	struct icmp_packet_rr *icmp_packet;
@@ -917,7 +918,8 @@  static int recv_my_icmp_packet(struct bat_priv *bat_priv,
 		goto out;
 	}
 
-	if (!bat_priv->primary_if)
+	primary_if = primary_if_get_selected(bat_priv);
+	if (!primary_if)
 		goto out;
 
 	/* answer echo request (ping) */
@@ -937,8 +939,7 @@  static int recv_my_icmp_packet(struct bat_priv *bat_priv,
 	icmp_packet = (struct icmp_packet_rr *)skb->data;
 
 	memcpy(icmp_packet->dst, icmp_packet->orig, ETH_ALEN);
-	memcpy(icmp_packet->orig,
-		bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN);
+	memcpy(icmp_packet->orig, primary_if->net_dev->dev_addr, ETH_ALEN);
 	icmp_packet->msg_type = ECHO_REPLY;
 	icmp_packet->ttl = TTL;
 
@@ -946,6 +947,8 @@  static int recv_my_icmp_packet(struct bat_priv *bat_priv,
 	ret = NET_RX_SUCCESS;
 
 out:
+	if (primary_if)
+		hardif_free_ref(primary_if);
 	if (router)
 		neigh_node_free_ref(router);
 	if (orig_node)
@@ -956,6 +959,7 @@  out:
 static int recv_icmp_ttl_exceeded(struct bat_priv *bat_priv,
 				  struct sk_buff *skb)
 {
+	struct hard_iface *primary_if = NULL;
 	struct orig_node *orig_node = NULL;
 	struct neigh_node *router = NULL;
 	struct icmp_packet *icmp_packet;
@@ -971,7 +975,8 @@  static int recv_icmp_ttl_exceeded(struct bat_priv *bat_priv,
 		goto out;
 	}
 
-	if (!bat_priv->primary_if)
+	primary_if = primary_if_get_selected(bat_priv);
+	if (!primary_if)
 		goto out;
 
 	/* get routing information */
@@ -990,8 +995,7 @@  static int recv_icmp_ttl_exceeded(struct bat_priv *bat_priv,
 	icmp_packet = (struct icmp_packet *)skb->data;
 
 	memcpy(icmp_packet->dst, icmp_packet->orig, ETH_ALEN);
-	memcpy(icmp_packet->orig,
-		bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN);
+	memcpy(icmp_packet->orig, primary_if->net_dev->dev_addr, ETH_ALEN);
 	icmp_packet->msg_type = TTL_EXCEEDED;
 	icmp_packet->ttl = TTL;
 
@@ -999,6 +1003,8 @@  static int recv_icmp_ttl_exceeded(struct bat_priv *bat_priv,
 	ret = NET_RX_SUCCESS;
 
 out:
+	if (primary_if)
+		hardif_free_ref(primary_if);
 	if (router)
 		neigh_node_free_ref(router);
 	if (orig_node)
diff --git a/send.c b/send.c
index 7650e2b..02b541a 100644
--- a/send.c
+++ b/send.c
@@ -244,6 +244,7 @@  static void rebuild_batman_packet(struct bat_priv *bat_priv,
 void schedule_own_packet(struct hard_iface *hard_iface)
 {
 	struct bat_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
+	struct hard_iface *primary_if;
 	unsigned long send_time;
 	struct batman_packet *batman_packet;
 	int vis_server;
@@ -253,6 +254,7 @@  void schedule_own_packet(struct hard_iface *hard_iface)
 		return;
 
 	vis_server = atomic_read(&bat_priv->vis_mode);
+	primary_if = primary_if_get_selected(bat_priv);
 
 	/**
 	 * the interface gets activated here to avoid race conditions between
@@ -266,7 +268,7 @@  void schedule_own_packet(struct hard_iface *hard_iface)
 
 	/* if local hna has changed and interface is a primary interface */
 	if ((atomic_read(&bat_priv->hna_local_changed)) &&
-	    (hard_iface == bat_priv->primary_if))
+	    (hard_iface == primary_if))
 		rebuild_batman_packet(bat_priv, hard_iface);
 
 	/**
@@ -284,7 +286,7 @@  void schedule_own_packet(struct hard_iface *hard_iface)
 	else
 		batman_packet->flags &= ~VIS_SERVER;
 
-	if ((hard_iface == bat_priv->primary_if) &&
+	if ((hard_iface == primary_if) &&
 	    (atomic_read(&bat_priv->gw_mode) == GW_MODE_SERVER))
 		batman_packet->gw_flags =
 				(uint8_t)atomic_read(&bat_priv->gw_bandwidth);
@@ -299,6 +301,9 @@  void schedule_own_packet(struct hard_iface *hard_iface)
 			       hard_iface->packet_buff,
 			       hard_iface->packet_len,
 			       hard_iface, 1, send_time);
+
+	if (primary_if)
+		hardif_free_ref(primary_if);
 }
 
 void schedule_forward_packet(struct orig_node *orig_node,
@@ -403,6 +408,7 @@  static void _add_bcast_packet_to_list(struct bat_priv *bat_priv,
  * skb is freed. */
 int add_bcast_packet_to_list(struct bat_priv *bat_priv, struct sk_buff *skb)
 {
+	struct hard_iface *primary_if = NULL;
 	struct forw_packet *forw_packet;
 	struct bcast_packet *bcast_packet;
 
@@ -411,7 +417,8 @@  int add_bcast_packet_to_list(struct bat_priv *bat_priv, struct sk_buff *skb)
 		goto out;
 	}
 
-	if (!bat_priv->primary_if)
+	primary_if = primary_if_get_selected(bat_priv);
+	if (!primary_if)
 		goto out;
 
 	forw_packet = kmalloc(sizeof(struct forw_packet), GFP_ATOMIC);
@@ -430,7 +437,7 @@  int add_bcast_packet_to_list(struct bat_priv *bat_priv, struct sk_buff *skb)
 	skb_reset_mac_header(skb);
 
 	forw_packet->skb = skb;
-	forw_packet->if_incoming = bat_priv->primary_if;
+	forw_packet->if_incoming = primary_if;
 
 	/* how often did we send the bcast packet ? */
 	forw_packet->num_packets = 0;
@@ -443,6 +450,8 @@  packet_free:
 out_and_inc:
 	atomic_inc(&bat_priv->bcast_queue_left);
 out:
+	if (primary_if)
+		hardif_free_ref(primary_if);
 	return NETDEV_TX_BUSY;
 }
 
diff --git a/soft-interface.c b/soft-interface.c
index f4d80ad..0b12e8b 100644
--- a/soft-interface.c
+++ b/soft-interface.c
@@ -211,13 +211,17 @@  int softif_neigh_seq_print_text(struct seq_file *seq, void *offset)
 	struct net_device *net_dev = (struct net_device *)seq->private;
 	struct bat_priv *bat_priv = netdev_priv(net_dev);
 	struct softif_neigh *softif_neigh;
+	struct hard_iface *primary_if;
 	struct hlist_node *node;
 	struct softif_neigh *curr_softif_neigh;
+	int ret = 0;
 
-	if (!bat_priv->primary_if) {
-		return seq_printf(seq, "BATMAN mesh %s disabled - "
-			       "please specify interfaces to enable it\n",
-			       net_dev->name);
+	primary_if = primary_if_get_selected(bat_priv);
+	if (!primary_if) {
+		ret = seq_printf(seq, "BATMAN mesh %s disabled - "
+				 "please specify interfaces to enable it\n",
+				 net_dev->name);
+		goto out;
 	}
 
 	seq_printf(seq, "Softif neighbor list (%s)\n", net_dev->name);
@@ -234,7 +238,10 @@  int softif_neigh_seq_print_text(struct seq_file *seq, void *offset)
 	if (curr_softif_neigh)
 		softif_neigh_free_ref(curr_softif_neigh);
 
-	return 0;
+out:
+	if (primary_if)
+		hardif_free_ref(primary_if);
+	return ret;
 }
 
 static void softif_batman_recv(struct sk_buff *skb, struct net_device *dev,
@@ -243,7 +250,8 @@  static void softif_batman_recv(struct sk_buff *skb, struct net_device *dev,
 	struct bat_priv *bat_priv = netdev_priv(dev);
 	struct ethhdr *ethhdr = (struct ethhdr *)skb->data;
 	struct batman_packet *batman_packet;
-	struct softif_neigh *softif_neigh;
+	struct softif_neigh *softif_neigh = NULL;
+	struct hard_iface *primary_if = NULL;
 	struct softif_neigh *curr_softif_neigh = NULL;
 
 	if (ntohs(ethhdr->h_proto) == ETH_P_8021Q)
@@ -253,28 +261,34 @@  static void softif_batman_recv(struct sk_buff *skb, struct net_device *dev,
 		batman_packet = (struct batman_packet *)(skb->data + ETH_HLEN);
 
 	if (batman_packet->version != COMPAT_VERSION)
-		goto err;
+		goto out;
 
 	if (batman_packet->packet_type != BAT_PACKET)
-		goto err;
+		goto out;
 
 	if (!(batman_packet->flags & PRIMARIES_FIRST_HOP))
-		goto err;
+		goto out;
 
 	if (is_my_mac(batman_packet->orig))
-		goto err;
+		goto out;
 
 	softif_neigh = softif_neigh_get(bat_priv, batman_packet->orig, vid);
-
 	if (!softif_neigh)
-		goto err;
+		goto out;
 
 	curr_softif_neigh = softif_neigh_get_selected(bat_priv);
+	if (!curr_softif_neigh)
+		goto out;
+
 	if (curr_softif_neigh == softif_neigh)
 		goto out;
 
+	primary_if = primary_if_get_selected(bat_priv);
+	if (!primary_if)
+		goto out;
+
 	/* we got a neighbor but its mac is 'bigger' than ours  */
-	if (memcmp(bat_priv->primary_if->net_dev->dev_addr,
+	if (memcmp(primary_if->net_dev->dev_addr,
 		   softif_neigh->addr, ETH_ALEN) < 0)
 		goto out;
 
@@ -296,7 +310,7 @@  static void softif_batman_recv(struct sk_buff *skb, struct net_device *dev,
 	/* close own batX device and use softif_neigh as exit node */
 	if ((!curr_softif_neigh) &&
 	    (memcmp(softif_neigh->addr,
-		    bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN) < 0)) {
+		    primary_if->net_dev->dev_addr, ETH_ALEN) < 0)) {
 		bat_dbg(DBG_ROUTES, bat_priv,
 			"Setting mesh exit point to %pM (vid: %d).\n",
 			softif_neigh->addr, softif_neigh->vid);
@@ -306,12 +320,13 @@  static void softif_batman_recv(struct sk_buff *skb, struct net_device *dev,
 	}
 
 out:
-	softif_neigh_free_ref(softif_neigh);
-err:
 	kfree_skb(skb);
+	if (softif_neigh)
+		softif_neigh_free_ref(softif_neigh);
 	if (curr_softif_neigh)
 		softif_neigh_free_ref(curr_softif_neigh);
-
+	if (primary_if)
+		hardif_free_ref(primary_if);
 	return;
 }
 
@@ -367,6 +382,7 @@  int interface_tx(struct sk_buff *skb, struct net_device *soft_iface)
 {
 	struct ethhdr *ethhdr = (struct ethhdr *)skb->data;
 	struct bat_priv *bat_priv = netdev_priv(soft_iface);
+	struct hard_iface *primary_if = NULL;
 	struct bcast_packet *bcast_packet;
 	struct vlan_ethhdr *vhdr;
 	struct softif_neigh *curr_softif_neigh = NULL;
@@ -416,7 +432,8 @@  int interface_tx(struct sk_buff *skb, struct net_device *soft_iface)
 
 	/* ethernet packet should be broadcasted */
 	if (do_bcast) {
-		if (!bat_priv->primary_if)
+		primary_if = primary_if_get_selected(bat_priv);
+		if (!primary_if)
 			goto dropped;
 
 		if (my_skb_head_push(skb, sizeof(struct bcast_packet)) < 0)
@@ -432,7 +449,7 @@  int interface_tx(struct sk_buff *skb, struct net_device *soft_iface)
 		/* hw address of first interface is the orig mac because only
 		 * this mac is known throughout the mesh */
 		memcpy(bcast_packet->orig,
-		       bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN);
+		       primary_if->net_dev->dev_addr, ETH_ALEN);
 
 		/* set broadcast sequence number */
 		bcast_packet->seqno =
@@ -462,6 +479,8 @@  dropped_freed:
 end:
 	if (curr_softif_neigh)
 		softif_neigh_free_ref(curr_softif_neigh);
+	if (primary_if)
+		hardif_free_ref(primary_if);
 	return NETDEV_TX_OK;
 }
 
diff --git a/translation-table.c b/translation-table.c
index 8d15b48..f931830 100644
--- a/translation-table.c
+++ b/translation-table.c
@@ -22,6 +22,7 @@ 
 #include "main.h"
 #include "translation-table.h"
 #include "soft-interface.h"
+#include "hard-interface.h"
 #include "hash.h"
 #include "originator.h"
 
@@ -237,16 +238,26 @@  int hna_local_seq_print_text(struct seq_file *seq, void *offset)
 	struct bat_priv *bat_priv = netdev_priv(net_dev);
 	struct hashtable_t *hash = bat_priv->hna_local_hash;
 	struct hna_local_entry *hna_local_entry;
+	struct hard_iface *primary_if;
 	struct hlist_node *node;
 	struct hlist_head *head;
 	size_t buf_size, pos;
 	char *buff;
-	int i;
+	int i, ret = 0;
 
-	if (!bat_priv->primary_if) {
-		return seq_printf(seq, "BATMAN mesh %s disabled - "
-			       "please specify interfaces to enable it\n",
-			       net_dev->name);
+	primary_if = primary_if_get_selected(bat_priv);
+	if (!primary_if) {
+		ret = seq_printf(seq, "BATMAN mesh %s disabled - "
+				 "please specify interfaces to enable it\n",
+				 net_dev->name);
+		goto out;
+	}
+
+	if (primary_if->if_status != IF_ACTIVE) {
+		ret = seq_printf(seq, "BATMAN mesh %s disabled - "
+				 "primary interface not active\n",
+				 net_dev->name);
+		goto out;
 	}
 
 	seq_printf(seq, "Locally retrieved addresses (from %s) "
@@ -269,7 +280,8 @@  int hna_local_seq_print_text(struct seq_file *seq, void *offset)
 	buff = kmalloc(buf_size, GFP_ATOMIC);
 	if (!buff) {
 		spin_unlock_bh(&bat_priv->hna_lhash_lock);
-		return -ENOMEM;
+		ret = -ENOMEM;
+		goto out;
 	}
 
 	buff[0] = '\0';
@@ -291,7 +303,10 @@  int hna_local_seq_print_text(struct seq_file *seq, void *offset)
 
 	seq_printf(seq, "%s", buff);
 	kfree(buff);
-	return 0;
+out:
+	if (primary_if)
+		hardif_free_ref(primary_if);
+	return ret;
 }
 
 static void _hna_local_del(struct hlist_node *node, void *arg)
@@ -468,16 +483,26 @@  int hna_global_seq_print_text(struct seq_file *seq, void *offset)
 	struct bat_priv *bat_priv = netdev_priv(net_dev);
 	struct hashtable_t *hash = bat_priv->hna_global_hash;
 	struct hna_global_entry *hna_global_entry;
+	struct hard_iface *primary_if;
 	struct hlist_node *node;
 	struct hlist_head *head;
 	size_t buf_size, pos;
 	char *buff;
-	int i;
+	int i, ret = 0;
 
-	if (!bat_priv->primary_if) {
-		return seq_printf(seq, "BATMAN mesh %s disabled - "
-				  "please specify interfaces to enable it\n",
-				  net_dev->name);
+	primary_if = primary_if_get_selected(bat_priv);
+	if (!primary_if) {
+		ret = seq_printf(seq, "BATMAN mesh %s disabled - please "
+				 "specify interfaces to enable it\n",
+				 net_dev->name);
+		goto out;
+	}
+
+	if (primary_if->if_status != IF_ACTIVE) {
+		ret = seq_printf(seq, "BATMAN mesh %s disabled - "
+				 "primary interface not active\n",
+				 net_dev->name);
+		goto out;
 	}
 
 	seq_printf(seq, "Globally announced HNAs received via the mesh %s\n",
@@ -499,7 +524,8 @@  int hna_global_seq_print_text(struct seq_file *seq, void *offset)
 	buff = kmalloc(buf_size, GFP_ATOMIC);
 	if (!buff) {
 		spin_unlock_bh(&bat_priv->hna_ghash_lock);
-		return -ENOMEM;
+		ret = -ENOMEM;
+		goto out;
 	}
 	buff[0] = '\0';
 	pos = 0;
@@ -522,7 +548,10 @@  int hna_global_seq_print_text(struct seq_file *seq, void *offset)
 
 	seq_printf(seq, "%s", buff);
 	kfree(buff);
-	return 0;
+out:
+	if (primary_if)
+		hardif_free_ref(primary_if);
+	return ret;
 }
 
 static void _hna_global_del_orig(struct bat_priv *bat_priv,
diff --git a/types.h b/types.h
index 75123b1..f849f9b 100644
--- a/types.h
+++ b/types.h
@@ -149,7 +149,7 @@  struct bat_priv {
 	struct hlist_head softif_neigh_list;
 	struct softif_neigh __rcu *softif_neigh;
 	struct debug_log *debug_log;
-	struct hard_iface *primary_if;
+	struct hard_iface __rcu *primary_if;
 	struct kobject *mesh_obj;
 	struct dentry *debug_dir;
 	struct hlist_head forw_bat_list;
diff --git a/unicast.c b/unicast.c
index d46acc8..b46cbf1 100644
--- a/unicast.c
+++ b/unicast.c
@@ -221,15 +221,17 @@  int frag_send_skb(struct sk_buff *skb, struct bat_priv *bat_priv,
 		  struct hard_iface *hard_iface, uint8_t dstaddr[])
 {
 	struct unicast_packet tmp_uc, *unicast_packet;
+	struct hard_iface *primary_if;
 	struct sk_buff *frag_skb;
 	struct unicast_frag_packet *frag1, *frag2;
 	int uc_hdr_len = sizeof(struct unicast_packet);
 	int ucf_hdr_len = sizeof(struct unicast_frag_packet);
 	int data_len = skb->len - uc_hdr_len;
-	int large_tail = 0;
+	int large_tail = 0, ret = NET_RX_DROP;
 	uint16_t seqno;
 
-	if (!bat_priv->primary_if)
+	primary_if = primary_if_get_selected(bat_priv);
+	if (!primary_if)
 		goto dropped;
 
 	frag_skb = dev_alloc_skb(data_len - (data_len / 2) + ucf_hdr_len);
@@ -254,7 +256,7 @@  int frag_send_skb(struct sk_buff *skb, struct bat_priv *bat_priv,
 	frag1->version = COMPAT_VERSION;
 	frag1->packet_type = BAT_UNICAST_FRAG;
 
-	memcpy(frag1->orig, bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN);
+	memcpy(frag1->orig, primary_if->net_dev->dev_addr, ETH_ALEN);
 	memcpy(frag2, frag1, sizeof(struct unicast_frag_packet));
 
 	if (data_len & 1)
@@ -269,13 +271,17 @@  int frag_send_skb(struct sk_buff *skb, struct bat_priv *bat_priv,
 
 	send_skb_packet(skb, hard_iface, dstaddr);
 	send_skb_packet(frag_skb, hard_iface, dstaddr);
-	return NET_RX_SUCCESS;
+	ret = NET_RX_SUCCESS;
+	goto out;
 
 drop_frag:
 	kfree_skb(frag_skb);
 dropped:
 	kfree_skb(skb);
-	return NET_RX_DROP;
+out:
+	if (primary_if)
+		hardif_free_ref(primary_if);
+	return ret;
 }
 
 int unicast_send_skb(struct sk_buff *skb, struct bat_priv *bat_priv)
diff --git a/vis.c b/vis.c
index d4cc4f5..c8f571d 100644
--- a/vis.c
+++ b/vis.c
@@ -204,6 +204,7 @@  static ssize_t vis_data_read_entry(char *buff, struct vis_info_entry *entry,
 
 int vis_seq_print_text(struct seq_file *seq, void *offset)
 {
+	struct hard_iface *primary_if;
 	struct hlist_node *node;
 	struct hlist_head *head;
 	struct vis_info *info;
@@ -215,15 +216,18 @@  int vis_seq_print_text(struct seq_file *seq, void *offset)
 	HLIST_HEAD(vis_if_list);
 	struct if_list_entry *entry;
 	struct hlist_node *pos, *n;
-	int i, j;
+	int i, j, ret = 0;
 	int vis_server = atomic_read(&bat_priv->vis_mode);
 	size_t buff_pos, buf_size;
 	char *buff;
 	int compare;
 
-	if ((!bat_priv->primary_if) ||
-	    (vis_server == VIS_TYPE_CLIENT_UPDATE))
-		return 0;
+	primary_if = primary_if_get_selected(bat_priv);
+	if (!primary_if)
+		goto out;
+
+	if (vis_server == VIS_TYPE_CLIENT_UPDATE)
+		goto out;
 
 	buf_size = 1;
 	/* Estimate length */
@@ -270,7 +274,8 @@  int vis_seq_print_text(struct seq_file *seq, void *offset)
 	buff = kmalloc(buf_size, GFP_ATOMIC);
 	if (!buff) {
 		spin_unlock_bh(&bat_priv->vis_hash_lock);
-		return -ENOMEM;
+		ret = -ENOMEM;
+		goto out;
 	}
 	buff[0] = '\0';
 	buff_pos = 0;
@@ -328,7 +333,10 @@  int vis_seq_print_text(struct seq_file *seq, void *offset)
 	seq_printf(seq, "%s", buff);
 	kfree(buff);
 
-	return 0;
+out:
+	if (primary_if)
+		hardif_free_ref(primary_if);
+	return ret;
 }
 
 /* add the info packet to the send list, if it was not
@@ -815,16 +823,20 @@  out:
 /* only send one vis packet. called from send_vis_packets() */
 static void send_vis_packet(struct bat_priv *bat_priv, struct vis_info *info)
 {
+	struct hard_iface *primary_if;
 	struct vis_packet *packet;
 
+	primary_if = primary_if_get_selected(bat_priv);
+	if (!primary_if)
+		goto out;
+
 	packet = (struct vis_packet *)info->skb_packet->data;
 	if (packet->ttl < 2) {
 		pr_debug("Error - can't send vis packet: ttl exceeded\n");
-		return;
+		goto out;
 	}
 
-	memcpy(packet->sender_orig, bat_priv->primary_if->net_dev->dev_addr,
-	       ETH_ALEN);
+	memcpy(packet->sender_orig, primary_if->net_dev->dev_addr, ETH_ALEN);
 	packet->ttl--;
 
 	if (is_broadcast_ether_addr(packet->target_orig))
@@ -832,6 +844,10 @@  static void send_vis_packet(struct bat_priv *bat_priv, struct vis_info *info)
 	else
 		unicast_vis_packet(bat_priv, info);
 	packet->ttl++; /* restore TTL */
+
+out:
+	if (primary_if)
+		hardif_free_ref(primary_if);
 }
 
 /* called from timer; send (and maybe generate) vis packet. */
@@ -858,8 +874,7 @@  static void send_vis_packets(struct work_struct *work)
 		kref_get(&info->refcount);
 		spin_unlock_bh(&bat_priv->vis_hash_lock);
 
-		if (bat_priv->primary_if)
-			send_vis_packet(bat_priv, info);
+		send_vis_packet(bat_priv, info);
 
 		spin_lock_bh(&bat_priv->vis_hash_lock);
 		send_list_del(info);