Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c384ea3e authored by Antonio Quartulli's avatar Antonio Quartulli
Browse files

batman-adv: Distributed ARP Table - add snooping functions for ARP messages



In case of an ARP message going in or out the soft_iface, it is intercepted and
a special action is performed. In particular the DHT helper functions previously
implemented are used to store all the ARP entries belonging to the network in
order to provide a fast and unicast lookup instead of the classic broadcast
flooding mechanism.
Each node stores the entries it is responsible for (following the DHT rules) in
its soft_iface ARP table. This makes it possible to reuse the kernel data
structures and functions for ARP management.

Signed-off-by: default avatarAntonio Quartulli <ordex@autistici.org>
parent 5c3a0e55
Loading
Loading
Loading
Loading
+265 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@

#include <linux/if_ether.h>
#include <linux/if_arp.h>
#include <net/arp.h>

#include "main.h"
#include "hash.h"
@@ -27,6 +28,7 @@
#include "originator.h"
#include "send.h"
#include "types.h"
#include "translation-table.h"
#include "unicast.h"

static void batadv_dat_purge(struct work_struct *work);
@@ -766,3 +768,266 @@ static uint16_t batadv_arp_get_type(struct batadv_priv *bat_priv,
out:
	return type;
}

/**
 * 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
 * @skb: packet to check
 *
 * Returns true if the message has been sent to the dht candidates, false
 * otherwise. In case of true the message has to be enqueued to permit the
 * fallback
 */
bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv,
					   struct sk_buff *skb)
{
	uint16_t type = 0;
	__be32 ip_dst, ip_src;
	uint8_t *hw_src;
	bool ret = false;
	struct batadv_dat_entry *dat_entry = NULL;
	struct sk_buff *skb_new;
	struct batadv_hard_iface *primary_if = NULL;

	type = batadv_arp_get_type(bat_priv, skb, 0);
	/* 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");

	ip_src = batadv_arp_ip_src(skb, 0);
	hw_src = batadv_arp_hw_src(skb, 0);
	ip_dst = batadv_arp_ip_dst(skb, 0);

	batadv_dat_entry_add(bat_priv, ip_src, hw_src);

	dat_entry = batadv_dat_entry_hash_find(bat_priv, ip_dst);
	if (dat_entry) {
		primary_if = batadv_primary_if_get_selected(bat_priv);
		if (!primary_if)
			goto out;

		skb_new = arp_create(ARPOP_REPLY, ETH_P_ARP, ip_src,
				     primary_if->soft_iface, ip_dst, hw_src,
				     dat_entry->mac_addr, hw_src);
		if (!skb_new)
			goto out;

		skb_reset_mac_header(skb_new);
		skb_new->protocol = eth_type_trans(skb_new,
						   primary_if->soft_iface);
		bat_priv->stats.rx_packets++;
		bat_priv->stats.rx_bytes += skb->len + ETH_HLEN;
		primary_if->soft_iface->last_rx = jiffies;

		netif_rx(skb_new);
		batadv_dbg(BATADV_DBG_DAT, bat_priv, "ARP request replied locally\n");
		ret = true;
	} else {
		/* Send the request on the DHT */
		ret = batadv_dat_send_data(bat_priv, skb, ip_dst,
					   BATADV_P_DAT_DHT_GET);
	}
out:
	if (dat_entry)
		batadv_dat_entry_free_ref(dat_entry);
	if (primary_if)
		batadv_hardif_free_ref(primary_if);
	return ret;
}

/**
 * batadv_dat_snoop_incoming_arp_request - snoop the ARP request and try to
 * answer using the local DAT storage
 * @bat_priv: the bat priv with all the soft interface information
 * @skb: packet to check
 * @hdr_size: size of the encapsulation header
 *
 * Returns true if the request has been answered, false otherwise
 */
bool batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv,
					   struct sk_buff *skb, int hdr_size)
{
	uint16_t type;
	__be32 ip_src, ip_dst;
	uint8_t *hw_src;
	struct sk_buff *skb_new;
	struct batadv_hard_iface *primary_if = NULL;
	struct batadv_dat_entry *dat_entry = NULL;
	bool ret = false;
	int err;

	type = batadv_arp_get_type(bat_priv, skb, hdr_size);
	if (type != ARPOP_REQUEST)
		goto out;

	hw_src = batadv_arp_hw_src(skb, hdr_size);
	ip_src = batadv_arp_ip_src(skb, hdr_size);
	ip_dst = batadv_arp_ip_dst(skb, hdr_size);

	batadv_dbg_arp(bat_priv, skb, type, hdr_size,
		       "Parsing incoming ARP REQUEST");

	batadv_dat_entry_add(bat_priv, ip_src, hw_src);

	dat_entry = batadv_dat_entry_hash_find(bat_priv, ip_dst);
	if (!dat_entry)
		goto out;

	primary_if = batadv_primary_if_get_selected(bat_priv);
	if (!primary_if)
		goto out;

	skb_new = arp_create(ARPOP_REPLY, ETH_P_ARP, ip_src,
			     primary_if->soft_iface, ip_dst, hw_src,
			     dat_entry->mac_addr, hw_src);

	if (!skb_new)
		goto out;

	/* to preserve backwards compatibility, here the node has to answer
	 * using the same packet type it received for the request. This is due
	 * to that if a node is not using the 4addr packet format it may not
	 * 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);
	else
		err = batadv_unicast_send_skb(bat_priv, skb_new);

	if (!err)
		ret = true;
out:
	if (dat_entry)
		batadv_dat_entry_free_ref(dat_entry);
	if (primary_if)
		batadv_hardif_free_ref(primary_if);
	if (ret)
		kfree_skb(skb);
	return ret;
}

/**
 * batadv_dat_snoop_outgoing_arp_reply - snoop the ARP reply and fill the DHT
 * @bat_priv: the bat priv with all the soft interface information
 * @skb: packet to check
 */
void batadv_dat_snoop_outgoing_arp_reply(struct batadv_priv *bat_priv,
					 struct sk_buff *skb)
{
	uint16_t type;
	__be32 ip_src, ip_dst;
	uint8_t *hw_src, *hw_dst;

	type = batadv_arp_get_type(bat_priv, skb, 0);
	if (type != ARPOP_REPLY)
		return;

	batadv_dbg_arp(bat_priv, skb, type, 0, "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);

	batadv_dat_entry_add(bat_priv, ip_src, hw_src);
	batadv_dat_entry_add(bat_priv, ip_dst, hw_dst);

	/* Send the ARP reply to the candidates for both the IP addresses that
	 * the node got within the ARP reply
	 */
	batadv_dat_send_data(bat_priv, skb, ip_src, BATADV_P_DAT_DHT_PUT);
	batadv_dat_send_data(bat_priv, skb, ip_dst, BATADV_P_DAT_DHT_PUT);
}
/**
 * batadv_dat_snoop_incoming_arp_reply - snoop the ARP reply and fill the local
 * DAT storage only
 * @bat_priv: the bat priv with all the soft interface information
 * @skb: packet to check
 * @hdr_size: siaze of the encapsulation header
 */
bool batadv_dat_snoop_incoming_arp_reply(struct batadv_priv *bat_priv,
					 struct sk_buff *skb, int hdr_size)
{
	uint16_t type;
	__be32 ip_src, ip_dst;
	uint8_t *hw_src, *hw_dst;
	bool ret = false;

	type = batadv_arp_get_type(bat_priv, skb, hdr_size);
	if (type != ARPOP_REPLY)
		goto out;

	batadv_dbg_arp(bat_priv, skb, type, hdr_size,
		       "Parsing incoming ARP REPLY");

	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);

	/* 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);

	/* 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);
out:
	/* if ret == false -> packet has to be delivered to the interface */
	return ret;
}

/**
 * batadv_dat_drop_broadcast_packet - check if an ARP request has to be dropped
 * (because the node has already got the reply via DAT) or not
 * @bat_priv: the bat priv with all the soft interface information
 * @forw_packet: the broadcast packet
 *
 * Returns true if the node can drop the packet, false otherwise
 */
bool batadv_dat_drop_broadcast_packet(struct batadv_priv *bat_priv,
				      struct batadv_forw_packet *forw_packet)
{
	uint16_t type;
	__be32 ip_dst;
	struct batadv_dat_entry *dat_entry = NULL;
	bool ret = false;
	const size_t bcast_len = sizeof(struct batadv_bcast_packet);

	/* If this packet is an ARP_REQUEST and the node already has the
	 * information that it is going to ask, then the packet can be dropped
	 */
	if (forw_packet->num_packets)
		goto out;

	type = batadv_arp_get_type(bat_priv, forw_packet->skb, bcast_len);
	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);
	/* check if the node already got this entry */
	if (!dat_entry) {
		batadv_dbg(BATADV_DBG_DAT, bat_priv,
			   "ARP Request for %pI4: fallback\n", &ip_dst);
		goto out;
	}

	batadv_dbg(BATADV_DBG_DAT, bat_priv,
		   "ARP Request for %pI4: fallback prevented\n", &ip_dst);
	ret = true;

out:
	if (dat_entry)
		batadv_dat_entry_free_ref(dat_entry);
	return ret;
}
+11 −0
Original line number Diff line number Diff line
@@ -27,6 +27,17 @@

#define BATADV_DAT_ADDR_MAX ((batadv_dat_addr_t)~(batadv_dat_addr_t)0)

bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv,
					   struct sk_buff *skb);
bool batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv,
					   struct sk_buff *skb, int hdr_size);
void batadv_dat_snoop_outgoing_arp_reply(struct batadv_priv *bat_priv,
					 struct sk_buff *skb);
bool batadv_dat_snoop_incoming_arp_reply(struct batadv_priv *bat_priv,
					 struct sk_buff *skb, int hdr_size);
bool batadv_dat_drop_broadcast_packet(struct batadv_priv *bat_priv,
				      struct batadv_forw_packet *forw_packet);

/**
 * batadv_dat_init_orig_node_addr - assign a DAT address to the orig_node
 * @orig_node: the node to assign the DAT address to
+2 −0
Original line number Diff line number Diff line
@@ -74,6 +74,8 @@

#define BATADV_LOG_BUF_LEN 8192	  /* has to be a power of 2 */

/* msecs after which an ARP_REQUEST is sent in broadcast as fallback */
#define ARP_REQ_DELAY 250
/* numbers of originator to contact for any PUT/GET DHT operation */
#define BATADV_DAT_CANDIDATES_NUM 3

+28 −1
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@
#include "vis.h"
#include "unicast.h"
#include "bridge_loop_avoidance.h"
#include "distributed-arp-table.h"

static int batadv_route_unicast_packet(struct sk_buff *skb,
				       struct batadv_hard_iface *recv_if);
@@ -985,11 +986,13 @@ int batadv_recv_unicast_packet(struct sk_buff *skb,
	struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface);
	struct batadv_unicast_packet *unicast_packet;
	int hdr_size = sizeof(*unicast_packet);
	bool is4addr;

	unicast_packet = (struct batadv_unicast_packet *)skb->data;

	is4addr = unicast_packet->header.packet_type == BATADV_UNICAST_4ADDR;
	/* the caller function should have already pulled 2 bytes */
	if (unicast_packet->header.packet_type == BATADV_UNICAST_4ADDR)
	if (is4addr)
		hdr_size = sizeof(struct batadv_unicast_4addr_packet);

	if (batadv_check_unicast_packet(skb, hdr_size) < 0)
@@ -1000,9 +1003,17 @@ int batadv_recv_unicast_packet(struct sk_buff *skb,

	/* packet for me */
	if (batadv_is_my_mac(unicast_packet->dest)) {
		if (batadv_dat_snoop_incoming_arp_request(bat_priv, skb,
							  hdr_size))
			goto rx_success;
		if (batadv_dat_snoop_incoming_arp_reply(bat_priv, skb,
							hdr_size))
			goto rx_success;

		batadv_interface_rx(recv_if->soft_iface, skb, recv_if, hdr_size,
				    NULL);

rx_success:
		return NET_RX_SUCCESS;
	}

@@ -1038,8 +1049,17 @@ int batadv_recv_ucast_frag_packet(struct sk_buff *skb,
		if (!new_skb)
			return NET_RX_SUCCESS;

		if (batadv_dat_snoop_incoming_arp_request(bat_priv, new_skb,
							  hdr_size))
			goto rx_success;
		if (batadv_dat_snoop_incoming_arp_reply(bat_priv, new_skb,
							hdr_size))
			goto rx_success;

		batadv_interface_rx(recv_if->soft_iface, new_skb, recv_if,
				    sizeof(struct batadv_unicast_packet), NULL);

rx_success:
		return NET_RX_SUCCESS;
	}

@@ -1131,9 +1151,16 @@ int batadv_recv_bcast_packet(struct sk_buff *skb,
	if (batadv_bla_is_backbone_gw(skb, orig_node, hdr_size))
		goto out;

	if (batadv_dat_snoop_incoming_arp_request(bat_priv, skb, hdr_size))
		goto rx_success;
	if (batadv_dat_snoop_incoming_arp_reply(bat_priv, skb, hdr_size))
		goto rx_success;

	/* broadcast for me */
	batadv_interface_rx(recv_if->soft_iface, skb, recv_if, hdr_size,
			    orig_node);

rx_success:
	ret = NET_RX_SUCCESS;
	goto out;

+4 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@
 */

#include "main.h"
#include "distributed-arp-table.h"
#include "send.h"
#include "routing.h"
#include "translation-table.h"
@@ -209,6 +210,9 @@ static void batadv_send_outstanding_bcast_packet(struct work_struct *work)
	if (atomic_read(&bat_priv->mesh_state) == BATADV_MESH_DEACTIVATING)
		goto out;

	if (batadv_dat_drop_broadcast_packet(bat_priv, forw_packet))
		goto out;

	/* rebroadcast packet */
	rcu_read_lock();
	list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) {
Loading