1694 lines · 46.5 KB · C
RawDownload
/* Copyright (C) 2011-2013 B.A.T.M.A.N. contributors: * * Simon Wunderlich * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA */ #include "main.h"#include "hash.h"#include "hard-interface.h"#include "originator.h"#include "bridge_loop_avoidance.h"#include "translation-table.h"#include "send.h" #include <linux/etherdevice.h>#include <linux/crc16.h>#include <linux/if_arp.h>#include <net/arp.h>#include <linux/if_vlan.h> static const uint8_t batadv_announce_mac[4] = {0x43, 0x05, 0x43, 0x05}; static void batadv_bla_periodic_work(struct work_struct *work);static voidbatadv_bla_send_announce(struct batadv_priv *bat_priv,			 struct batadv_bla_backbone_gw *backbone_gw); /* return the index of the claim */static inline uint32_t batadv_choose_claim(const void *data, uint32_t size){	struct batadv_bla_claim *claim = (struct batadv_bla_claim *)data;	uint32_t hash = 0; 	hash = batadv_hash_bytes(hash, &claim->addr, sizeof(claim->addr));	hash = batadv_hash_bytes(hash, &claim->vid, sizeof(claim->vid)); 	hash += (hash << 3);	hash ^= (hash >> 11);	hash += (hash << 15); 	return hash % size;} /* return the index of the backbone gateway */static inline uint32_t batadv_choose_backbone_gw(const void *data,						 uint32_t size){	const struct batadv_bla_claim *claim = (struct batadv_bla_claim *)data;	uint32_t hash = 0; 	hash = batadv_hash_bytes(hash, &claim->addr, sizeof(claim->addr));	hash = batadv_hash_bytes(hash, &claim->vid, sizeof(claim->vid)); 	hash += (hash << 3);	hash ^= (hash >> 11);	hash += (hash << 15); 	return hash % size;}  /* compares address and vid of two backbone gws */static int batadv_compare_backbone_gw(const struct hlist_node *node,				      const void *data2){	const void *data1 = container_of(node, struct batadv_bla_backbone_gw,					 hash_entry);	const struct batadv_bla_backbone_gw *gw1 = data1, *gw2 = data2; 	if (!batadv_compare_eth(gw1->orig, gw2->orig))		return 0; 	if (gw1->vid != gw2->vid)		return 0; 	return 1;} /* compares address and vid of two claims */static int batadv_compare_claim(const struct hlist_node *node,				const void *data2){	const void *data1 = container_of(node, struct batadv_bla_claim,					 hash_entry);	const struct batadv_bla_claim *cl1 = data1, *cl2 = data2; 	if (!batadv_compare_eth(cl1->addr, cl2->addr))		return 0; 	if (cl1->vid != cl2->vid)		return 0; 	return 1;} /* free a backbone gw */static voidbatadv_backbone_gw_free_ref(struct batadv_bla_backbone_gw *backbone_gw){	if (atomic_dec_and_test(&backbone_gw->refcount))		kfree_rcu(backbone_gw, rcu);} /* finally deinitialize the claim */static void batadv_claim_free_rcu(struct rcu_head *rcu){	struct batadv_bla_claim *claim; 	claim = container_of(rcu, struct batadv_bla_claim, rcu); 	batadv_backbone_gw_free_ref(claim->backbone_gw);	kfree(claim);} /* free a claim, call claim_free_rcu if its the last reference */static void batadv_claim_free_ref(struct batadv_bla_claim *claim){	if (atomic_dec_and_test(&claim->refcount))		call_rcu(&claim->rcu, batadv_claim_free_rcu);} /* @bat_priv: the bat priv with all the soft interface information * @data: search data (may be local/static data) * * looks for a claim in the hash, and returns it if found * or NULL otherwise. */static struct batadv_bla_claim*batadv_claim_hash_find(struct batadv_priv *bat_priv,			struct batadv_bla_claim *data){	struct batadv_hashtable *hash = bat_priv->bla.claim_hash;	struct hlist_head *head;	struct batadv_bla_claim *claim;	struct batadv_bla_claim *claim_tmp = NULL;	int index; 	if (!hash)		return NULL; 	index = batadv_choose_claim(data, hash->size);	head = &hash->table[index]; 	rcu_read_lock();	hlist_for_each_entry_rcu(claim, head, hash_entry) {		if (!batadv_compare_claim(&claim->hash_entry, data))			continue; 		if (!atomic_inc_not_zero(&claim->refcount))			continue; 		claim_tmp = claim;		break;	}	rcu_read_unlock(); 	return claim_tmp;} /** * batadv_backbone_hash_find - looks for a claim in the hash * @bat_priv: the bat priv with all the soft interface information * @addr: the address of the originator * @vid: the VLAN ID * * Returns claim if found or NULL otherwise. */static struct batadv_bla_backbone_gw *batadv_backbone_hash_find(struct batadv_priv *bat_priv,			  uint8_t *addr, unsigned short vid){	struct batadv_hashtable *hash = bat_priv->bla.backbone_hash;	struct hlist_head *head;	struct batadv_bla_backbone_gw search_entry, *backbone_gw;	struct batadv_bla_backbone_gw *backbone_gw_tmp = NULL;	int index; 	if (!hash)		return NULL; 	memcpy(search_entry.orig, addr, ETH_ALEN);	search_entry.vid = vid; 	index = batadv_choose_backbone_gw(&search_entry, hash->size);	head = &hash->table[index]; 	rcu_read_lock();	hlist_for_each_entry_rcu(backbone_gw, head, hash_entry) {		if (!batadv_compare_backbone_gw(&backbone_gw->hash_entry,						&search_entry))			continue; 		if (!atomic_inc_not_zero(&backbone_gw->refcount))			continue; 		backbone_gw_tmp = backbone_gw;		break;	}	rcu_read_unlock(); 	return backbone_gw_tmp;} /* delete all claims for a backbone */static voidbatadv_bla_del_backbone_claims(struct batadv_bla_backbone_gw *backbone_gw){	struct batadv_hashtable *hash;	struct hlist_node *node_tmp;	struct hlist_head *head;	struct batadv_bla_claim *claim;	int i;	spinlock_t *list_lock;	/* protects write access to the hash lists */ 	hash = backbone_gw->bat_priv->bla.claim_hash;	if (!hash)		return; 	for (i = 0; i < hash->size; i++) {		head = &hash->table[i];		list_lock = &hash->list_locks[i]; 		spin_lock_bh(list_lock);		hlist_for_each_entry_safe(claim, node_tmp,					  head, hash_entry) {			if (claim->backbone_gw != backbone_gw)				continue; 			batadv_claim_free_ref(claim);			hlist_del_rcu(&claim->hash_entry);		}		spin_unlock_bh(list_lock);	} 	/* all claims gone, intialize CRC */	backbone_gw->crc = BATADV_BLA_CRC_INIT;} /** * batadv_bla_send_claim - sends a claim frame according to the provided info * @bat_priv: the bat priv with all the soft interface information * @orig: the mac address to be announced within the claim * @vid: the VLAN ID * @claimtype: the type of the claim (CLAIM, UNCLAIM, ANNOUNCE, ...) */static void batadv_bla_send_claim(struct batadv_priv *bat_priv, uint8_t *mac,				  unsigned short vid, int claimtype){	struct sk_buff *skb;	struct ethhdr *ethhdr;	struct batadv_hard_iface *primary_if;	struct net_device *soft_iface;	uint8_t *hw_src;	struct batadv_bla_claim_dst local_claim_dest;	__be32 zeroip = 0; 	primary_if = batadv_primary_if_get_selected(bat_priv);	if (!primary_if)		return; 	memcpy(&local_claim_dest, &bat_priv->bla.claim_dest,	       sizeof(local_claim_dest));	local_claim_dest.type = claimtype; 	soft_iface = primary_if->soft_iface; 	skb = arp_create(ARPOP_REPLY, ETH_P_ARP,			 /* IP DST: 0.0.0.0 */			 zeroip,			 primary_if->soft_iface,			 /* IP SRC: 0.0.0.0 */			 zeroip,			 /* Ethernet DST: Broadcast */			 NULL,			 /* Ethernet SRC/HW SRC:  originator mac */			 primary_if->net_dev->dev_addr,			 /* HW DST: FF:43:05:XX:YY:YY			  * with XX   = claim type			  * and YY:YY = group id			  */			 (uint8_t *)&local_claim_dest); 	if (!skb)		goto out; 	ethhdr = (struct ethhdr *)skb->data;	hw_src = (uint8_t *)ethhdr + ETH_HLEN + sizeof(struct arphdr); 	/* now we pretend that the client would have sent this ... */	switch (claimtype) {	case BATADV_CLAIM_TYPE_CLAIM:		/* normal claim frame		 * set Ethernet SRC to the clients mac		 */		memcpy(ethhdr->h_source, mac, ETH_ALEN);		batadv_dbg(BATADV_DBG_BLA, bat_priv,			   "bla_send_claim(): CLAIM %pM on vid %d\n", mac,			   BATADV_PRINT_VID(vid));		break;	case BATADV_CLAIM_TYPE_UNCLAIM:		/* unclaim frame		 * set HW SRC to the clients mac		 */		memcpy(hw_src, mac, ETH_ALEN);		batadv_dbg(BATADV_DBG_BLA, bat_priv,			   "bla_send_claim(): UNCLAIM %pM on vid %d\n", mac,			   BATADV_PRINT_VID(vid));		break;	case BATADV_CLAIM_TYPE_ANNOUNCE:		/* announcement frame		 * set HW SRC to the special mac containg the crc		 */		memcpy(hw_src, mac, ETH_ALEN);		batadv_dbg(BATADV_DBG_BLA, bat_priv,			   "bla_send_claim(): ANNOUNCE of %pM on vid %d\n",			   ethhdr->h_source, BATADV_PRINT_VID(vid));		break;	case BATADV_CLAIM_TYPE_REQUEST:		/* request frame		 * set HW SRC and header destination to the receiving backbone		 * gws mac		 */		memcpy(hw_src, mac, ETH_ALEN);		memcpy(ethhdr->h_dest, mac, ETH_ALEN);		batadv_dbg(BATADV_DBG_BLA, bat_priv,			   "bla_send_claim(): REQUEST of %pM to %pM on vid %d\n",			   ethhdr->h_source, ethhdr->h_dest,			   BATADV_PRINT_VID(vid));		break;	} 	if (vid & BATADV_VLAN_HAS_TAG)		skb = vlan_insert_tag(skb, htons(ETH_P_8021Q),				      vid & VLAN_VID_MASK); 	skb_reset_mac_header(skb);	skb->protocol = eth_type_trans(skb, soft_iface);	batadv_inc_counter(bat_priv, BATADV_CNT_RX);	batadv_add_counter(bat_priv, BATADV_CNT_RX_BYTES,			   skb->len + ETH_HLEN);	soft_iface->last_rx = jiffies; 	netif_rx(skb);out:	if (primary_if)		batadv_hardif_free_ref(primary_if);} /** * batadv_bla_get_backbone_gw * @bat_priv: the bat priv with all the soft interface information * @orig: the mac address of the originator * @vid: the VLAN ID * * searches for the backbone gw or creates a new one if it could not * be found. */static struct batadv_bla_backbone_gw *batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, uint8_t *orig,			   unsigned short vid, bool own_backbone){	struct batadv_bla_backbone_gw *entry;	struct batadv_orig_node *orig_node;	int hash_added; 	entry = batadv_backbone_hash_find(bat_priv, orig, vid); 	if (entry)		return entry; 	batadv_dbg(BATADV_DBG_BLA, bat_priv,		   "bla_get_backbone_gw(): not found (%pM, %d), creating new entry\n",		   orig, BATADV_PRINT_VID(vid)); 	entry = kzalloc(sizeof(*entry), GFP_ATOMIC);	if (!entry)		return NULL; 	entry->vid = vid;	entry->lasttime = jiffies;	entry->crc = BATADV_BLA_CRC_INIT;	entry->bat_priv = bat_priv;	atomic_set(&entry->request_sent, 0);	atomic_set(&entry->wait_periods, 0);	memcpy(entry->orig, orig, ETH_ALEN); 	/* one for the hash, one for returning */	atomic_set(&entry->refcount, 2); 	hash_added = batadv_hash_add(bat_priv->bla.backbone_hash,				     batadv_compare_backbone_gw,				     batadv_choose_backbone_gw, entry,				     &entry->hash_entry); 	if (unlikely(hash_added != 0)) {		/* hash failed, free the structure */		kfree(entry);		return NULL;	} 	/* this is a gateway now, remove any TT entry on this VLAN */	orig_node = batadv_orig_hash_find(bat_priv, orig);	if (orig_node) {		batadv_tt_global_del_orig(bat_priv, orig_node, vid,					  "became a backbone gateway");		batadv_orig_node_free_ref(orig_node);	} 	if (own_backbone) {		batadv_bla_send_announce(bat_priv, entry); 		/* this will be decreased in the worker thread */		atomic_inc(&entry->request_sent);		atomic_set(&entry->wait_periods, BATADV_BLA_WAIT_PERIODS);		atomic_inc(&bat_priv->bla.num_requests);	} 	return entry;} /* update or add the own backbone gw to make sure we announce * where we receive other backbone gws */static voidbatadv_bla_update_own_backbone_gw(struct batadv_priv *bat_priv,				  struct batadv_hard_iface *primary_if,				  unsigned short vid){	struct batadv_bla_backbone_gw *backbone_gw; 	backbone_gw = batadv_bla_get_backbone_gw(bat_priv,						 primary_if->net_dev->dev_addr,						 vid, true);	if (unlikely(!backbone_gw))		return; 	backbone_gw->lasttime = jiffies;	batadv_backbone_gw_free_ref(backbone_gw);} /* @bat_priv: the bat priv with all the soft interface information * @vid: the vid where the request came on * * Repeat all of our own claims, and finally send an ANNOUNCE frame * to allow the requester another check if the CRC is correct now. */static void batadv_bla_answer_request(struct batadv_priv *bat_priv,				      struct batadv_hard_iface *primary_if,				      unsigned short vid){	struct hlist_head *head;	struct batadv_hashtable *hash;	struct batadv_bla_claim *claim;	struct batadv_bla_backbone_gw *backbone_gw;	int i; 	batadv_dbg(BATADV_DBG_BLA, bat_priv,		   "bla_answer_request(): received a claim request, send all of our own claims again\n"); 	backbone_gw = batadv_backbone_hash_find(bat_priv,						primary_if->net_dev->dev_addr,						vid);	if (!backbone_gw)		return; 	hash = bat_priv->bla.claim_hash;	for (i = 0; i < hash->size; i++) {		head = &hash->table[i]; 		rcu_read_lock();		hlist_for_each_entry_rcu(claim, head, hash_entry) {			/* only own claims are interesting */			if (claim->backbone_gw != backbone_gw)				continue; 			batadv_bla_send_claim(bat_priv, claim->addr, claim->vid,					      BATADV_CLAIM_TYPE_CLAIM);		}		rcu_read_unlock();	} 	/* finally, send an announcement frame */	batadv_bla_send_announce(bat_priv, backbone_gw);	batadv_backbone_gw_free_ref(backbone_gw);} /* @backbone_gw: the backbone gateway from whom we are out of sync

Lines 1–500 of 1694.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t