diff options
Diffstat (limited to 'net/batman-adv/distributed-arp-table.c')
| -rw-r--r-- | net/batman-adv/distributed-arp-table.c | 259 | 
1 files changed, 193 insertions, 66 deletions
diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c index 06345d40158..f2c066b2171 100644 --- a/net/batman-adv/distributed-arp-table.c +++ b/net/batman-adv/distributed-arp-table.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2011-2013 B.A.T.M.A.N. contributors: +/* Copyright (C) 2011-2014 B.A.T.M.A.N. contributors:   *   * Antonio Quartulli   * @@ -12,13 +12,12 @@   * 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 + * along with this program; if not, see <http://www.gnu.org/licenses/>.   */  #include <linux/if_ether.h>  #include <linux/if_arp.h> +#include <linux/if_vlan.h>  #include <net/arp.h>  #include "main.h" @@ -29,7 +28,6 @@  #include "send.h"  #include "types.h"  #include "translation-table.h" -#include "unicast.h"  static void batadv_dat_purge(struct work_struct *work); @@ -141,7 +139,7 @@ static int batadv_compare_dat(const struct hlist_node *node, const void *data2)  	const void *data1 = container_of(node, struct batadv_dat_entry,  					 hash_entry); -	return (memcmp(data1, data2, sizeof(__be32)) == 0 ? 1 : 0); +	return memcmp(data1, data2, sizeof(__be32)) == 0 ? 1 : 0;  }  /** @@ -206,15 +204,11 @@ static __be32 batadv_arp_ip_dst(struct sk_buff *skb, int hdr_size)   */  static uint32_t batadv_hash_dat(const void *data, uint32_t size)  { -	const unsigned char *key = data;  	uint32_t hash = 0; -	size_t i; +	const struct batadv_dat_entry *dat = data; -	for (i = 0; i < 4; i++) { -		hash += key[i]; -		hash += (hash << 10); -		hash ^= (hash >> 6); -	} +	hash = batadv_hash_bytes(hash, &dat->ip, sizeof(dat->ip)); +	hash = batadv_hash_bytes(hash, &dat->vid, sizeof(dat->vid));  	hash += (hash << 3);  	hash ^= (hash >> 11); @@ -228,21 +222,26 @@ static uint32_t batadv_hash_dat(const void *data, uint32_t size)   * table   * @bat_priv: the bat priv with all the soft interface information   * @ip: search key + * @vid: VLAN identifier   *   * Returns the dat_entry if found, NULL otherwise.   */  static struct batadv_dat_entry * -batadv_dat_entry_hash_find(struct batadv_priv *bat_priv, __be32 ip) +batadv_dat_entry_hash_find(struct batadv_priv *bat_priv, __be32 ip, +			   unsigned short vid)  {  	struct hlist_head *head; -	struct batadv_dat_entry *dat_entry, *dat_entry_tmp = NULL; +	struct batadv_dat_entry to_find, *dat_entry, *dat_entry_tmp = NULL;  	struct batadv_hashtable *hash = bat_priv->dat.hash;  	uint32_t index;  	if (!hash)  		return NULL; -	index = batadv_hash_dat(&ip, hash->size); +	to_find.ip = ip; +	to_find.vid = vid; + +	index = batadv_hash_dat(&to_find, hash->size);  	head = &hash->table[index];  	rcu_read_lock(); @@ -266,22 +265,24 @@ batadv_dat_entry_hash_find(struct batadv_priv *bat_priv, __be32 ip)   * @bat_priv: the bat priv with all the soft interface information   * @ip: ipv4 to add/edit   * @mac_addr: mac address to assign to the given ipv4 + * @vid: VLAN identifier   */  static void batadv_dat_entry_add(struct batadv_priv *bat_priv, __be32 ip, -				 uint8_t *mac_addr) +				 uint8_t *mac_addr, unsigned short vid)  {  	struct batadv_dat_entry *dat_entry;  	int hash_added; -	dat_entry = batadv_dat_entry_hash_find(bat_priv, ip); +	dat_entry = batadv_dat_entry_hash_find(bat_priv, ip, vid);  	/* if this entry is already known, just update it */  	if (dat_entry) {  		if (!batadv_compare_eth(dat_entry->mac_addr, mac_addr)) -			memcpy(dat_entry->mac_addr, mac_addr, ETH_ALEN); +			ether_addr_copy(dat_entry->mac_addr, mac_addr);  		dat_entry->last_update = jiffies;  		batadv_dbg(BATADV_DBG_DAT, bat_priv, -			   "Entry updated: %pI4 %pM\n", &dat_entry->ip, -			   dat_entry->mac_addr); +			   "Entry updated: %pI4 %pM (vid: %d)\n", +			   &dat_entry->ip, dat_entry->mac_addr, +			   BATADV_PRINT_VID(vid));  		goto out;  	} @@ -290,12 +291,13 @@ static void batadv_dat_entry_add(struct batadv_priv *bat_priv, __be32 ip,  		goto out;  	dat_entry->ip = ip; -	memcpy(dat_entry->mac_addr, mac_addr, ETH_ALEN); +	dat_entry->vid = vid; +	ether_addr_copy(dat_entry->mac_addr, mac_addr);  	dat_entry->last_update = jiffies;  	atomic_set(&dat_entry->refcount, 2);  	hash_added = batadv_hash_add(bat_priv->dat.hash, batadv_compare_dat, -				     batadv_hash_dat, &dat_entry->ip, +				     batadv_hash_dat, dat_entry,  				     &dat_entry->hash_entry);  	if (unlikely(hash_added != 0)) { @@ -304,8 +306,8 @@ static void batadv_dat_entry_add(struct batadv_priv *bat_priv, __be32 ip,  		goto out;  	} -	batadv_dbg(BATADV_DBG_DAT, bat_priv, "New entry added: %pI4 %pM\n", -		   &dat_entry->ip, dat_entry->mac_addr); +	batadv_dbg(BATADV_DBG_DAT, bat_priv, "New entry added: %pI4 %pM (vid: %d)\n", +		   &dat_entry->ip, dat_entry->mac_addr, BATADV_PRINT_VID(vid));  out:  	if (dat_entry) @@ -345,7 +347,7 @@ static void batadv_dbg_arp(struct batadv_priv *bat_priv, struct sk_buff *skb,  	unicast_4addr_packet = (struct batadv_unicast_4addr_packet *)skb->data; -	switch (unicast_4addr_packet->u.header.packet_type) { +	switch (unicast_4addr_packet->u.packet_type) {  	case BATADV_UNICAST:  		batadv_dbg(BATADV_DBG_DAT, bat_priv,  			   "* encapsulated within a UNICAST packet\n"); @@ -370,7 +372,7 @@ static void batadv_dbg_arp(struct batadv_priv *bat_priv, struct sk_buff *skb,  			break;  		default:  			batadv_dbg(BATADV_DBG_DAT, bat_priv, "* type: Unknown (%u)!\n", -				   unicast_4addr_packet->u.header.packet_type); +				   unicast_4addr_packet->u.packet_type);  		}  		break;  	case BATADV_BCAST: @@ -383,7 +385,7 @@ static void batadv_dbg_arp(struct batadv_priv *bat_priv, struct sk_buff *skb,  	default:  		batadv_dbg(BATADV_DBG_DAT, bat_priv,  			   "* encapsulated within an unknown packet type (0x%x)\n", -			   unicast_4addr_packet->u.header.packet_type); +			   unicast_4addr_packet->u.packet_type);  	}  } @@ -419,6 +421,10 @@ static bool batadv_is_orig_node_eligible(struct batadv_dat_candidate *res,  	bool ret = false;  	int j; +	/* check if orig node candidate is running DAT */ +	if (!(candidate->capabilities & BATADV_ORIG_CAPA_HAS_DAT)) +		goto out; +  	/* Check if this node has already been selected... */  	for (j = 0; j < select; j++)  		if (res[j].orig_node == candidate) @@ -583,14 +589,15 @@ static bool batadv_dat_send_data(struct batadv_priv *bat_priv,  		if (cand[i].type == BATADV_DAT_CANDIDATE_NOT_FOUND)  			continue; -		neigh_node = batadv_orig_node_get_router(cand[i].orig_node); +		neigh_node = batadv_orig_router_get(cand[i].orig_node, +						    BATADV_IF_DEFAULT);  		if (!neigh_node)  			goto free_orig; -		tmp_skb = pskb_copy(skb, GFP_ATOMIC); -		if (!batadv_unicast_4addr_prepare_skb(bat_priv, tmp_skb, -						      cand[i].orig_node, -						      packet_subtype)) { +		tmp_skb = pskb_copy_for_clone(skb, GFP_ATOMIC); +		if (!batadv_send_skb_prepare_unicast_4addr(bat_priv, tmp_skb, +							   cand[i].orig_node, +							   packet_subtype)) {  			kfree_skb(tmp_skb);  			goto free_neigh;  		} @@ -626,6 +633,60 @@ out:  }  /** + * batadv_dat_tvlv_container_update - update the dat tvlv container after dat + *  setting change + * @bat_priv: the bat priv with all the soft interface information + */ +static void batadv_dat_tvlv_container_update(struct batadv_priv *bat_priv) +{ +	char dat_mode; + +	dat_mode = atomic_read(&bat_priv->distributed_arp_table); + +	switch (dat_mode) { +	case 0: +		batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_DAT, 1); +		break; +	case 1: +		batadv_tvlv_container_register(bat_priv, BATADV_TVLV_DAT, 1, +					       NULL, 0); +		break; +	} +} + +/** + * batadv_dat_status_update - update the dat tvlv container after dat + *  setting change + * @net_dev: the soft interface net device + */ +void batadv_dat_status_update(struct net_device *net_dev) +{ +	struct batadv_priv *bat_priv = netdev_priv(net_dev); + +	batadv_dat_tvlv_container_update(bat_priv); +} + +/** + * batadv_gw_tvlv_ogm_handler_v1 - process incoming dat tvlv container + * @bat_priv: the bat priv with all the soft interface information + * @orig: the orig_node of the ogm + * @flags: flags indicating the tvlv state (see batadv_tvlv_handler_flags) + * @tvlv_value: tvlv buffer containing the gateway data + * @tvlv_value_len: tvlv buffer length + */ +static void batadv_dat_tvlv_ogm_handler_v1(struct batadv_priv *bat_priv, +					   struct batadv_orig_node *orig, +					   uint8_t flags, +					   void *tvlv_value, +					   uint16_t tvlv_value_len) +{ +	if (flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND) +		orig->capabilities &= ~BATADV_ORIG_CAPA_HAS_DAT; +	else +		orig->capabilities |= BATADV_ORIG_CAPA_HAS_DAT; +} + +/**   * batadv_dat_hash_free - free the local DAT hash table   * @bat_priv: the bat priv with all the soft interface information   */ @@ -657,6 +718,10 @@ int batadv_dat_init(struct batadv_priv *bat_priv)  	batadv_dat_start_timer(bat_priv); +	batadv_tvlv_handler_register(bat_priv, batadv_dat_tvlv_ogm_handler_v1, +				     NULL, BATADV_TVLV_DAT, 1, +				     BATADV_TVLV_HANDLER_OGM_CIFNOTFND); +	batadv_dat_tvlv_container_update(bat_priv);  	return 0;  } @@ -666,6 +731,9 @@ int batadv_dat_init(struct batadv_priv *bat_priv)   */  void batadv_dat_free(struct batadv_priv *bat_priv)  { +	batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_DAT, 1); +	batadv_tvlv_handler_unregister(bat_priv, BATADV_TVLV_DAT, 1); +  	cancel_delayed_work_sync(&bat_priv->dat.work);  	batadv_dat_hash_free(bat_priv); @@ -693,8 +761,8 @@ int batadv_dat_cache_seq_print_text(struct seq_file *seq, void *offset)  		goto out;  	seq_printf(seq, "Distributed ARP Table (%s):\n", net_dev->name); -	seq_printf(seq, "          %-7s          %-13s %5s\n", "IPv4", "MAC", -		   "last-seen"); +	seq_printf(seq, "          %-7s          %-9s %4s %11s\n", "IPv4", +		   "MAC", "VID", "last-seen");  	for (i = 0; i < hash->size; i++) {  		head = &hash->table[i]; @@ -707,8 +775,9 @@ int batadv_dat_cache_seq_print_text(struct seq_file *seq, void *offset)  			last_seen_msecs = last_seen_msecs % 60000;  			last_seen_secs = last_seen_msecs / 1000; -			seq_printf(seq, " * %15pI4 %14pM %6i:%02i\n", +			seq_printf(seq, " * %15pI4 %14pM %4i %6i:%02i\n",  				   &dat_entry->ip, dat_entry->mac_addr, +				   BATADV_PRINT_VID(dat_entry->vid),  				   last_seen_mins, last_seen_secs);  		}  		rcu_read_unlock(); @@ -795,6 +864,31 @@ out:  }  /** + * batadv_dat_get_vid - extract the VLAN identifier from skb if any + * @skb: the buffer containing the packet to extract the VID from + * @hdr_size: the size of the batman-adv header encapsulating the packet + * + * If the packet embedded in the skb is vlan tagged this function returns the + * VID with the BATADV_VLAN_HAS_TAG flag. Otherwise BATADV_NO_FLAGS is returned. + */ +static unsigned short batadv_dat_get_vid(struct sk_buff *skb, int *hdr_size) +{ +	unsigned short vid; + +	vid = batadv_get_vid(skb, *hdr_size); + +	/* ARP parsing functions jump forward of hdr_size + ETH_HLEN. +	 * If the header contained in the packet is a VLAN one (which is longer) +	 * hdr_size is updated so that the functions will still skip the +	 * correct amount of bytes. +	 */ +	if (vid & BATADV_VLAN_HAS_TAG) +		*hdr_size += VLAN_HLEN; + +	return vid; +} + +/**   * batadv_dat_snoop_outgoing_arp_request - snoop the ARP request and try to   * answer using DAT   * @bat_priv: the bat priv with all the soft interface information @@ -813,26 +907,31 @@ bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv,  	bool ret = false;  	struct batadv_dat_entry *dat_entry = NULL;  	struct sk_buff *skb_new; +	int hdr_size = 0; +	unsigned short vid;  	if (!atomic_read(&bat_priv->distributed_arp_table))  		goto out; -	type = batadv_arp_get_type(bat_priv, skb, 0); +	vid = batadv_dat_get_vid(skb, &hdr_size); + +	type = batadv_arp_get_type(bat_priv, skb, hdr_size);  	/* If the node gets an ARP_REQUEST it has to send a DHT_GET unicast  	 * message to the selected DHT candidates  	 */  	if (type != ARPOP_REQUEST)  		goto out; -	batadv_dbg_arp(bat_priv, skb, type, 0, "Parsing outgoing ARP REQUEST"); +	batadv_dbg_arp(bat_priv, skb, type, hdr_size, +		       "Parsing outgoing ARP REQUEST"); -	ip_src = batadv_arp_ip_src(skb, 0); -	hw_src = batadv_arp_hw_src(skb, 0); -	ip_dst = batadv_arp_ip_dst(skb, 0); +	ip_src = batadv_arp_ip_src(skb, hdr_size); +	hw_src = batadv_arp_hw_src(skb, hdr_size); +	ip_dst = batadv_arp_ip_dst(skb, hdr_size); -	batadv_dat_entry_add(bat_priv, ip_src, hw_src); +	batadv_dat_entry_add(bat_priv, ip_src, hw_src, vid); -	dat_entry = batadv_dat_entry_hash_find(bat_priv, ip_dst); +	dat_entry = batadv_dat_entry_hash_find(bat_priv, ip_dst, vid);  	if (dat_entry) {  		/* If the ARP request is destined for a local client the local  		 * client will answer itself. DAT would only generate a @@ -842,7 +941,7 @@ bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv,  		 * additional DAT answer may trigger kernel warnings about  		 * a packet coming from the wrong port.  		 */ -		if (batadv_is_my_client(bat_priv, dat_entry->mac_addr)) { +		if (batadv_is_my_client(bat_priv, dat_entry->mac_addr, vid)) {  			ret = true;  			goto out;  		} @@ -853,11 +952,15 @@ bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv,  		if (!skb_new)  			goto out; +		if (vid & BATADV_VLAN_HAS_TAG) +			skb_new = vlan_insert_tag(skb_new, htons(ETH_P_8021Q), +						  vid & VLAN_VID_MASK); +  		skb_reset_mac_header(skb_new);  		skb_new->protocol = eth_type_trans(skb_new,  						   bat_priv->soft_iface);  		bat_priv->stats.rx_packets++; -		bat_priv->stats.rx_bytes += skb->len + ETH_HLEN; +		bat_priv->stats.rx_bytes += skb->len + ETH_HLEN + hdr_size;  		bat_priv->soft_iface->last_rx = jiffies;  		netif_rx(skb_new); @@ -892,11 +995,14 @@ bool batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv,  	struct sk_buff *skb_new;  	struct batadv_dat_entry *dat_entry = NULL;  	bool ret = false; +	unsigned short vid;  	int err;  	if (!atomic_read(&bat_priv->distributed_arp_table))  		goto out; +	vid = batadv_dat_get_vid(skb, &hdr_size); +  	type = batadv_arp_get_type(bat_priv, skb, hdr_size);  	if (type != ARPOP_REQUEST)  		goto out; @@ -908,9 +1014,9 @@ bool batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv,  	batadv_dbg_arp(bat_priv, skb, type, hdr_size,  		       "Parsing incoming ARP REQUEST"); -	batadv_dat_entry_add(bat_priv, ip_src, hw_src); +	batadv_dat_entry_add(bat_priv, ip_src, hw_src, vid); -	dat_entry = batadv_dat_entry_hash_find(bat_priv, ip_dst); +	dat_entry = batadv_dat_entry_hash_find(bat_priv, ip_dst, vid);  	if (!dat_entry)  		goto out; @@ -921,17 +1027,27 @@ bool batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv,  	if (!skb_new)  		goto out; +	/* the rest of the TX path assumes that the mac_header offset pointing +	 * to the inner Ethernet header has been set, therefore reset it now. +	 */ +	skb_reset_mac_header(skb_new); + +	if (vid & BATADV_VLAN_HAS_TAG) +		skb_new = vlan_insert_tag(skb_new, htons(ETH_P_8021Q), +					  vid & VLAN_VID_MASK); +  	/* To preserve backwards compatibility, the node has choose the outgoing  	 * format based on the incoming request packet type. The assumption is  	 * that a node not using the 4addr packet format doesn't support it.  	 */  	if (hdr_size == sizeof(struct batadv_unicast_4addr_packet)) -		err = batadv_unicast_4addr_send_skb(bat_priv, skb_new, -						    BATADV_P_DAT_CACHE_REPLY); +		err = batadv_send_skb_via_tt_4addr(bat_priv, skb_new, +						   BATADV_P_DAT_CACHE_REPLY, +						   NULL, vid);  	else -		err = batadv_unicast_send_skb(bat_priv, skb_new); +		err = batadv_send_skb_via_tt(bat_priv, skb_new, NULL, vid); -	if (!err) { +	if (err != NET_XMIT_DROP) {  		batadv_inc_counter(bat_priv, BATADV_CNT_DAT_CACHED_REPLY_TX);  		ret = true;  	} @@ -954,23 +1070,28 @@ void batadv_dat_snoop_outgoing_arp_reply(struct batadv_priv *bat_priv,  	uint16_t type;  	__be32 ip_src, ip_dst;  	uint8_t *hw_src, *hw_dst; +	int hdr_size = 0; +	unsigned short vid;  	if (!atomic_read(&bat_priv->distributed_arp_table))  		return; -	type = batadv_arp_get_type(bat_priv, skb, 0); +	vid = batadv_dat_get_vid(skb, &hdr_size); + +	type = batadv_arp_get_type(bat_priv, skb, hdr_size);  	if (type != ARPOP_REPLY)  		return; -	batadv_dbg_arp(bat_priv, skb, type, 0, "Parsing outgoing ARP REPLY"); +	batadv_dbg_arp(bat_priv, skb, type, hdr_size, +		       "Parsing outgoing ARP REPLY"); -	hw_src = batadv_arp_hw_src(skb, 0); -	ip_src = batadv_arp_ip_src(skb, 0); -	hw_dst = batadv_arp_hw_dst(skb, 0); -	ip_dst = batadv_arp_ip_dst(skb, 0); +	hw_src = batadv_arp_hw_src(skb, hdr_size); +	ip_src = batadv_arp_ip_src(skb, hdr_size); +	hw_dst = batadv_arp_hw_dst(skb, hdr_size); +	ip_dst = batadv_arp_ip_dst(skb, hdr_size); -	batadv_dat_entry_add(bat_priv, ip_src, hw_src); -	batadv_dat_entry_add(bat_priv, ip_dst, hw_dst); +	batadv_dat_entry_add(bat_priv, ip_src, hw_src, vid); +	batadv_dat_entry_add(bat_priv, ip_dst, hw_dst, vid);  	/* Send the ARP reply to the candidates for both the IP addresses that  	 * the node obtained from the ARP reply @@ -992,10 +1113,13 @@ bool batadv_dat_snoop_incoming_arp_reply(struct batadv_priv *bat_priv,  	__be32 ip_src, ip_dst;  	uint8_t *hw_src, *hw_dst;  	bool ret = false; +	unsigned short vid;  	if (!atomic_read(&bat_priv->distributed_arp_table))  		goto out; +	vid = batadv_dat_get_vid(skb, &hdr_size); +  	type = batadv_arp_get_type(bat_priv, skb, hdr_size);  	if (type != ARPOP_REPLY)  		goto out; @@ -1011,13 +1135,13 @@ bool batadv_dat_snoop_incoming_arp_reply(struct batadv_priv *bat_priv,  	/* Update our internal cache with both the IP addresses the node got  	 * within the ARP reply  	 */ -	batadv_dat_entry_add(bat_priv, ip_src, hw_src); -	batadv_dat_entry_add(bat_priv, ip_dst, hw_dst); +	batadv_dat_entry_add(bat_priv, ip_src, hw_src, vid); +	batadv_dat_entry_add(bat_priv, ip_dst, hw_dst, vid);  	/* if this REPLY is directed to a client of mine, let's deliver the  	 * packet to the interface  	 */ -	ret = !batadv_is_my_client(bat_priv, hw_dst); +	ret = !batadv_is_my_client(bat_priv, hw_dst, vid);  out:  	if (ret)  		kfree_skb(skb); @@ -1040,7 +1164,8 @@ bool batadv_dat_drop_broadcast_packet(struct batadv_priv *bat_priv,  	__be32 ip_dst;  	struct batadv_dat_entry *dat_entry = NULL;  	bool ret = false; -	const size_t bcast_len = sizeof(struct batadv_bcast_packet); +	int hdr_size = sizeof(struct batadv_bcast_packet); +	unsigned short vid;  	if (!atomic_read(&bat_priv->distributed_arp_table))  		goto out; @@ -1051,12 +1176,14 @@ bool batadv_dat_drop_broadcast_packet(struct batadv_priv *bat_priv,  	if (forw_packet->num_packets)  		goto out; -	type = batadv_arp_get_type(bat_priv, forw_packet->skb, bcast_len); +	vid = batadv_dat_get_vid(forw_packet->skb, &hdr_size); + +	type = batadv_arp_get_type(bat_priv, forw_packet->skb, hdr_size);  	if (type != ARPOP_REQUEST)  		goto out; -	ip_dst = batadv_arp_ip_dst(forw_packet->skb, bcast_len); -	dat_entry = batadv_dat_entry_hash_find(bat_priv, ip_dst); +	ip_dst = batadv_arp_ip_dst(forw_packet->skb, hdr_size); +	dat_entry = batadv_dat_entry_hash_find(bat_priv, ip_dst, vid);  	/* check if the node already got this entry */  	if (!dat_entry) {  		batadv_dbg(BATADV_DBG_DAT, bat_priv,  | 
