2558 lines · 71.5 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* Copyright (C) B.A.T.M.A.N. contributors: * * Simon Wunderlich */ #include "bridge_loop_avoidance.h"#include "main.h" #include <linux/atomic.h>#include <linux/bug.h>#include <linux/byteorder/generic.h>#include <linux/compiler.h>#include <linux/container_of.h>#include <linux/crc16.h>#include <linux/err.h>#include <linux/errno.h>#include <linux/etherdevice.h>#include <linux/gfp.h>#include <linux/if_arp.h>#include <linux/if_ether.h>#include <linux/if_vlan.h>#include <linux/jhash.h>#include <linux/jiffies.h>#include <linux/kref.h>#include <linux/list.h>#include <linux/lockdep.h>#include <linux/netdevice.h>#include <linux/netlink.h>#include <linux/rculist.h>#include <linux/rcupdate.h>#include <linux/skbuff.h>#include <linux/slab.h>#include <linux/spinlock.h>#include <linux/sprintf.h>#include <linux/stddef.h>#include <linux/string.h>#include <linux/string_choices.h>#include <linux/workqueue.h>#include <net/arp.h>#include <net/genetlink.h>#include <net/netlink.h>#include <uapi/linux/batadv_packet.h>#include <uapi/linux/batman_adv.h> #include "hard-interface.h"#include "hash.h"#include "log.h"#include "netlink.h"#include "originator.h"#include "translation-table.h" static const u8 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); /** * batadv_choose_claim() - choose the right bucket for a claim. * @data: data to hash * @size: size of the hash table * * Return: the hash index of the claim */static inline u32 batadv_choose_claim(const void *data, u32 size){	const struct batadv_bla_claim *claim = data;	u32 hash = 0; 	hash = jhash(&claim->addr, sizeof(claim->addr), hash);	hash = jhash(&claim->vid, sizeof(claim->vid), hash); 	return hash % size;} /** * batadv_choose_backbone_gw() - choose the right bucket for a backbone gateway. * @data: data to hash * @size: size of the hash table * * Return: the hash index of the backbone gateway */static inline u32 batadv_choose_backbone_gw(const void *data, u32 size){	const struct batadv_bla_backbone_gw *gw;	u32 hash = 0; 	gw = data;	hash = jhash(&gw->orig, sizeof(gw->orig), hash);	hash = jhash(&gw->vid, sizeof(gw->vid), hash); 	return hash % size;} /** * batadv_compare_backbone_gw() - compare address and vid of two backbone gws * @node: list node of the first entry to compare * @data2: pointer to the second backbone gateway * * Return: true if the backbones have the same data, false otherwise */static bool 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;	const struct batadv_bla_backbone_gw *gw2 = data2; 	if (!batadv_compare_eth(gw1->orig, gw2->orig))		return false; 	if (gw1->vid != gw2->vid)		return false; 	return true;} /** * batadv_compare_claim() - compare address and vid of two claims * @node: list node of the first entry to compare * @data2: pointer to the second claims * * Return: true if the claims have the same data, false otherwise */static bool 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;	const struct batadv_bla_claim *cl2 = data2; 	if (!batadv_compare_eth(cl1->addr, cl2->addr))		return false; 	if (cl1->vid != cl2->vid)		return false; 	return true;} /** * batadv_backbone_gw_release() - release backbone gw from lists and queue for *  free after rcu grace period * @ref: kref pointer of the backbone gw */static void batadv_backbone_gw_release(struct kref *ref){	struct batadv_bla_backbone_gw *backbone_gw; 	backbone_gw = container_of(ref, struct batadv_bla_backbone_gw,				   refcount); 	kfree_rcu(backbone_gw, rcu);} /** * batadv_backbone_gw_put() - decrement the backbone gw refcounter and possibly *  release it * @backbone_gw: backbone gateway to be free'd */static void batadv_backbone_gw_put(struct batadv_bla_backbone_gw *backbone_gw){	if (!backbone_gw)		return; 	kref_put(&backbone_gw->refcount, batadv_backbone_gw_release);} /** * batadv_claim_release() - release claim from lists and queue for free after *  rcu grace period * @ref: kref pointer of the claim */static void batadv_claim_release(struct kref *ref){	struct batadv_bla_backbone_gw *old_backbone_gw;	struct batadv_bla_claim *claim; 	claim = container_of(ref, struct batadv_bla_claim, refcount); 	spin_lock_bh(&claim->backbone_lock);	old_backbone_gw = claim->backbone_gw;	claim->backbone_gw = NULL;	spin_unlock_bh(&claim->backbone_lock); 	spin_lock_bh(&old_backbone_gw->crc_lock);	old_backbone_gw->crc ^= crc16(0, claim->addr, ETH_ALEN);	spin_unlock_bh(&old_backbone_gw->crc_lock); 	batadv_backbone_gw_put(old_backbone_gw); 	kfree_rcu(claim, rcu);} /** * batadv_claim_put() - decrement the claim refcounter and possibly release it * @claim: claim to be free'd */static void batadv_claim_put(struct batadv_bla_claim *claim){	if (!claim)		return; 	kref_put(&claim->refcount, batadv_claim_release);} /** * batadv_claim_hash_find() - looks for a claim in the claim hash * @bat_priv: the bat priv with all the mesh interface information * @data: search data (may be local/static data) * * Return: claim 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 batadv_bla_claim *claim_tmp = NULL;	struct batadv_bla_claim *claim;	struct hlist_head *head;	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 (!kref_get_unless_zero(&claim->refcount))			continue; 		claim_tmp = claim;		break;	}	rcu_read_unlock(); 	return claim_tmp;} /** * batadv_backbone_hash_find() - looks for a backbone gateway in the hash * @bat_priv: the bat priv with all the mesh interface information * @addr: the address of the originator * @vid: the VLAN ID * * Return: backbone gateway if found or NULL otherwise */static struct batadv_bla_backbone_gw *batadv_backbone_hash_find(struct batadv_priv *bat_priv, const u8 *addr,			  unsigned short vid){	struct batadv_hashtable *hash = bat_priv->bla.backbone_hash;	struct batadv_bla_backbone_gw *backbone_gw_tmp = NULL;	struct batadv_bla_backbone_gw search_entry;	struct batadv_bla_backbone_gw *backbone_gw;	struct hlist_head *head;	int index; 	if (!hash)		return NULL; 	ether_addr_copy(search_entry.orig, addr);	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 (!kref_get_unless_zero(&backbone_gw->refcount))			continue; 		backbone_gw_tmp = backbone_gw;		break;	}	rcu_read_unlock(); 	return backbone_gw_tmp;} /** * batadv_bla_del_backbone_claims() - delete all claims for a backbone * @backbone_gw: backbone gateway where the claims should be removed */static voidbatadv_bla_del_backbone_claims(struct batadv_bla_backbone_gw *backbone_gw){	spinlock_t *list_lock;	/* protects write access to the hash lists */	struct batadv_bla_claim *claim;	struct batadv_hashtable *hash;	struct hlist_node *node_tmp;	struct hlist_head *head;	int i; 	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; 			hlist_del_rcu(&claim->hash_entry);			batadv_claim_put(claim);		}		spin_unlock_bh(list_lock);	}} /** * batadv_bla_send_claim() - sends a claim frame according to the provided info * @bat_priv: the bat priv with all the mesh interface information * @mac: 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, const u8 *mac,				  unsigned short vid, int claimtype){	struct batadv_bla_claim_dst local_claim_dest;	struct batadv_hard_iface *primary_if;	struct ethhdr *ethhdr;	struct sk_buff *skb;	__be32 zeroip = 0;	u8 *hw_src; 	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; 	skb = arp_create(ARPOP_REPLY, ETH_P_ARP,			 /* IP DST: 0.0.0.0 */			 zeroip,			 primary_if->mesh_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			  */			 (u8 *)&local_claim_dest); 	if (!skb)		goto out; 	ethhdr = (struct ethhdr *)skb->data;	hw_src = (u8 *)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		 */		ether_addr_copy(ethhdr->h_source, mac);		batadv_dbg(BATADV_DBG_BLA, bat_priv,			   "%s(): CLAIM %pM on vid %d\n", __func__, mac,			   batadv_print_vid(vid));		break;	case BATADV_CLAIM_TYPE_UNCLAIM:		/* unclaim frame		 * set HW SRC to the clients mac		 */		ether_addr_copy(hw_src, mac);		batadv_dbg(BATADV_DBG_BLA, bat_priv,			   "%s(): UNCLAIM %pM on vid %d\n", __func__, mac,			   batadv_print_vid(vid));		break;	case BATADV_CLAIM_TYPE_ANNOUNCE:		/* announcement frame		 * set HW SRC to the special mac containing the crc		 */		ether_addr_copy(hw_src, mac);		batadv_dbg(BATADV_DBG_BLA, bat_priv,			   "%s(): ANNOUNCE of %pM on vid %d\n", __func__,			   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		 */		ether_addr_copy(hw_src, mac);		ether_addr_copy(ethhdr->h_dest, mac);		batadv_dbg(BATADV_DBG_BLA, bat_priv,			   "%s(): REQUEST of %pM to %pM on vid %d\n", __func__,			   ethhdr->h_source, ethhdr->h_dest,			   batadv_print_vid(vid));		break;	case BATADV_CLAIM_TYPE_LOOPDETECT:		ether_addr_copy(ethhdr->h_source, mac);		batadv_dbg(BATADV_DBG_BLA, bat_priv,			   "%s(): LOOPDETECT of %pM to %pM on vid %d\n",			   __func__, 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);		if (!skb)			goto out;	} 	skb_reset_mac_header(skb);	skb->protocol = eth_type_trans(skb, primary_if->mesh_iface);	batadv_inc_counter(bat_priv, BATADV_CNT_RX);	batadv_add_counter(bat_priv, BATADV_CNT_RX_BYTES,			   skb->len + ETH_HLEN); 	netif_rx(skb);out:	batadv_hardif_put(primary_if);} /** * batadv_bla_loopdetect_report() - worker for reporting the loop * @work: work queue item * * Throws an uevent, as the loopdetect check function can't do that itself * since the kernel may sleep while throwing uevents. */static void batadv_bla_loopdetect_report(struct work_struct *work){	struct batadv_bla_backbone_gw *backbone_gw;	struct batadv_priv *bat_priv;	char vid_str[6] = { '\0' }; 	backbone_gw = container_of(work, struct batadv_bla_backbone_gw,				   report_work);	bat_priv = backbone_gw->bat_priv; 	batadv_info(bat_priv->mesh_iface,		    "Possible loop on VLAN %d detected which can't be handled by BLA - please check your network setup!\n",		    batadv_print_vid(backbone_gw->vid));	snprintf(vid_str, sizeof(vid_str), "%d",		 batadv_print_vid(backbone_gw->vid));	vid_str[sizeof(vid_str) - 1] = 0; 	batadv_throw_uevent(bat_priv, BATADV_UEV_BLA, BATADV_UEV_LOOPDETECT,			    vid_str); 	batadv_backbone_gw_put(backbone_gw);} /** * batadv_bla_get_backbone_gw() - finds or creates a backbone gateway * @bat_priv: the bat priv with all the mesh interface information * @orig: the mac address of the originator * @vid: the VLAN ID * @own_backbone: set if the requested backbone is local * * Return: the (possibly created) backbone gateway or NULL on error */static struct batadv_bla_backbone_gw *batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, const u8 *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,		   "%s(): not found (%pM, %d), creating new entry\n", __func__,		   orig, batadv_print_vid(vid));

Lines 1–500 of 2558.