[RFC,5/6] batman-adv: Generalize snooping mechanism in order to suport NDP too

Message ID 1373242365-763-5-git-send-email-mihail.costea2005@gmail.com (mailing list archive)
State RFC, archived
Headers

Commit Message

YourName July 8, 2013, 12:12 a.m. UTC
  From: Mihail Costea <mihail.costea90@gmail.com>

Generalize snooping functions in order to support Neighbor Advertisement
and Neighbor Solicitation too. Adds snooping functions to generic
struct so there won't be a need for switch-cases.

Signed-off-by: Mihail Costea <mihail.costea90@gmail.com>
Signed-off-by: Stefan Popa <Stefan.A.Popa@intel.com>
Reviewed-by: Stefan Popa <Stefan.A.Popa@intel.com>

---
 distributed-arp-table.c |  438 +++++++++++++++++++++++++++++++++++------------
 types.h                 |   69 ++++++++
 2 files changed, 401 insertions(+), 106 deletions(-)
  

Comments

Linus Lüssing Sept. 30, 2013, 8:06 p.m. UTC | #1
Hi Mihail,

On Mon, Jul 08, 2013 at 03:12:44AM +0300, mihail.costea2005@gmail.com wrote:
> From: Mihail Costea <mihail.costea90@gmail.com>
>  /**
> - * batadv_dat_snoop_outgoing_pkt_reply - snoop the ARP reply and fill the DHT
> + * batadv_dat_snoop_outgoing_pkt_reply - snoop the ARP reply / NA and fill
> + * the DHT
>   * @bat_priv: the bat priv with all the soft interface information
>   * @skb: packet to check
>   */
>  void batadv_dat_snoop_outgoing_pkt_reply(struct batadv_priv *bat_priv,
>  					 struct sk_buff *skb)
>  {
--- snip ---
> +	/* not a solicited advertisement (see snooping mechanism) */
> +	if (ip_dst) {
> +		batadv_dat_entry_add(bat_priv, ip_dst, dat_pair_type.data_type,
> +				     hw_dst, vid);
> +		batadv_dat_send_data(bat_priv, skb, ip_dst,
> +				     dat_pair_type.data_type,
> +				     BATADV_P_DAT_DHT_PUT);
> +	}
>  }

I'm currently wondering about the unsolicited neighbor
advertisement case. So far, your concept/patchset does not seem
to apply any optimizations for those packets, does it? They are
still flooded unconditionally, right?

What do you think, would it make sense to prevent unsolicited
neighbor advertisements from being forwarded into the mesh
network? Or would that create certain issues?

Cheers, Linus
  
Linus Lüssing Sept. 30, 2013, 8:38 p.m. UTC | #2
PS: I'm very excited about your work on the IPv6 ND DAT. This is
going to help us a lot in our community mesh networks. So thanks a
lot for the time you've spent on this so far!
  
YourName Oct. 4, 2013, 6:28 p.m. UTC | #3
Hi Linus,

Unfortunately I won't work on this anymore because it will take
another 1-2 months.
I had a vacation last week and now I started school again.
I will start something new and this takes too much time.

Sorry,
Mihail


On 30 September 2013 23:38, Linus Lüssing <linus.luessing@web.de> wrote:
> PS: I'm very excited about your work on the IPv6 ND DAT. This is
> going to help us a lot in our community mesh networks. So thanks a
> lot for the time you've spent on this so far!
  
Antonio Quartulli Oct. 5, 2013, 7:14 a.m. UTC | #4
Hi Mihail,

On Fri, Oct 04, 2013 at 09:28:54PM +0300, Mihail Costea wrote:
> Hi Linus,
> 
> Unfortunately I won't work on this anymore because it will take
> another 1-2 months.
> I had a vacation last week and now I started school again.
> I will start something new and this takes too much time.
> 


does this mean you will not be able to complete this work? Too bad :(
It was a really nice thing..


Cheers,
  
Mihail Costea Oct. 5, 2013, 9:48 a.m. UTC | #5
Unfortunately yes. With everything it will take too many weeks and I'd
like to do other things too.

Sorry,
Mihail

On 5 October 2013 10:14, Antonio Quartulli <antonio@meshcoding.com> wrote:
> Hi Mihail,
>
> On Fri, Oct 04, 2013 at 09:28:54PM +0300, Mihail Costea wrote:
>> Hi Linus,
>>
>> Unfortunately I won't work on this anymore because it will take
>> another 1-2 months.
>> I had a vacation last week and now I started school again.
>> I will start something new and this takes too much time.
>>
>
>
> does this mean you will not be able to complete this work? Too bad :(
> It was a really nice thing..
>
>
> Cheers,
>
> --
> Antonio Quartulli
  

Patch

diff --git a/distributed-arp-table.c b/distributed-arp-table.c
index d0b9e95..1a5749b 100644
--- a/distributed-arp-table.c
+++ b/distributed-arp-table.c
@@ -33,21 +33,55 @@ 
 #include "types.h"
 #include "translation-table.h"
 
+static void batadv_dat_purge(struct work_struct *work);
+
+static bool batadv_dat_snoop_arp_pkt(struct batadv_priv *bat_priv,
+				     struct sk_buff *skb, int hdr_size,
+				     uint16_t pkt_type, uint8_t pkt_dir_type,
+				     uint8_t **hw_src, void **ip_src,
+				     uint8_t **hw_dst, void **ip_dst);
+
+static struct sk_buff *
+batadv_dat_create_arp_reply(struct batadv_priv *bat_priv,
+			    struct batadv_dat_entry *dat_entry,
+			    uint8_t *hw_src, void *ip_src, void *ip_dst)
+{
+	return arp_create(ARPOP_REPLY, ETH_P_ARP, *(__be32 *)ip_src,
+			  bat_priv->soft_iface, *(__be32 *)ip_dst,
+			  hw_src, dat_entry->mac_addr, hw_src);
+}
+
+#if IS_ENABLED(CONFIG_IPV6)
+static bool batadv_dat_snoop_ndisc_pkt(struct batadv_priv *bat_priv,
+				       struct sk_buff *skb, int hdr_size,
+				       uint16_t pkt_type, uint8_t pkt_dir_type,
+				       uint8_t **hw_src, void **ip_src,
+				       uint8_t **hw_dst, void **ip_dst);
+
+static struct sk_buff *
+batadv_dat_create_ndisc_na(struct batadv_priv *bat_priv,
+			   struct batadv_dat_entry *dat_entry,
+			   uint8_t *hw_src, void *ip_src, void *ip_dst);
+
+#endif
+
 static struct batadv_dat_type_info batadv_dat_types_info[] = {
 	{
 		.size = sizeof(__be32),
 		.str_fmt = "%pI4",
+		.snoop_pkt = batadv_dat_snoop_arp_pkt,
+		.create_skb = batadv_dat_create_arp_reply,
 	},
 #if IS_ENABLED(CONFIG_IPV6)
 	{
 		.size = sizeof(struct in6_addr),
 		.str_fmt = "%pI6c",
+		.snoop_pkt = batadv_dat_snoop_ndisc_pkt,
+		.create_skb = batadv_dat_create_ndisc_na,
 	},
 #endif
 };
 
-static void batadv_dat_purge(struct work_struct *work);
-
 /**
  * batadv_dat_data_to_str: transforms DAT data to string
  * @data: the DAT data
@@ -61,7 +95,7 @@  static char *batadv_dat_data_to_str(void *data, uint8_t type,
 				    char *buf, size_t buf_len)
 {
 	snprintf(buf, buf_len, batadv_dat_types_info[type].str_fmt, data);
-return buf;
+	return buf;
 }
 
 /**
@@ -221,9 +255,9 @@  static uint8_t *batadv_arp_hw_src(struct sk_buff *skb, int hdr_size)
  *
  * Returns the value of the ip_src field in the ARP packet.
  */
-static __be32 batadv_arp_ip_src(struct sk_buff *skb, int hdr_size)
+static __be32 *batadv_arp_ip_src(struct sk_buff *skb, int hdr_size)
 {
-	return *(__be32 *)(batadv_arp_hw_src(skb, hdr_size) + ETH_ALEN);
+	return (__be32 *)(batadv_arp_hw_src(skb, hdr_size) + ETH_ALEN);
 }
 
 /**
@@ -245,9 +279,9 @@  static uint8_t *batadv_arp_hw_dst(struct sk_buff *skb, int hdr_size)
  *
  * Returns the value of the ip_dst field in the ARP packet.
  */
-static __be32 batadv_arp_ip_dst(struct sk_buff *skb, int hdr_size)
+static __be32 *batadv_arp_ip_dst(struct sk_buff *skb, int hdr_size)
 {
-	return *(__be32 *)(batadv_arp_hw_src(skb, hdr_size) + ETH_ALEN * 2 + 4);
+	return (__be32 *)(batadv_arp_hw_src(skb, hdr_size) + ETH_ALEN * 2 + 4);
 }
 
 /**
@@ -411,8 +445,8 @@  static void batadv_dbg_arp(struct batadv_priv *bat_priv, struct sk_buff *skb,
 	if (msg)
 		batadv_dbg(BATADV_DBG_DAT, bat_priv, "%s\n", msg);
 
-	ip_src = batadv_arp_ip_src(skb, hdr_size);
-	ip_dst = batadv_arp_ip_dst(skb, hdr_size);
+	ip_src = *batadv_arp_ip_src(skb, hdr_size);
+	ip_dst = *batadv_arp_ip_dst(skb, hdr_size);
 	batadv_dbg(BATADV_DBG_DAT, bat_priv,
 		   "ARP MSG = [src: %pM-%pI4 dst: %pM-%pI4]\n",
 		   batadv_arp_hw_src(skb, hdr_size), &ip_src,
@@ -867,7 +901,7 @@  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, " * %40s %15pM %6i:%02i\n",
+			seq_printf(seq, " * %-40s %15pM %6i:%02i\n",
 				   batadv_dat_data_to_str(dat_entry->data,
 							  dat_entry->type,
 							  dbg_data,
@@ -933,8 +967,8 @@  static uint16_t batadv_arp_get_type(struct batadv_priv *bat_priv,
 	/* Check for bad reply/request. If the ARP message is not sane, DAT
 	 * will simply ignore it
 	 */
-	ip_src = batadv_arp_ip_src(skb, hdr_size);
-	ip_dst = batadv_arp_ip_dst(skb, hdr_size);
+	ip_src = *batadv_arp_ip_src(skb, hdr_size);
+	ip_dst = *batadv_arp_ip_dst(skb, hdr_size);
 	if (ipv4_is_loopback(ip_src) || ipv4_is_multicast(ip_src) ||
 	    ipv4_is_loopback(ip_dst) || ipv4_is_multicast(ip_dst) ||
 	    ipv4_is_zeronet(ip_src) || ipv4_is_lbcast(ip_src) ||
@@ -958,6 +992,40 @@  out:
 	return type;
 }
 
+static bool batadv_dat_snoop_arp_pkt(struct batadv_priv *bat_priv,
+				     struct sk_buff *skb, int hdr_size,
+				     uint16_t pkt_type, uint8_t pkt_dir_type,
+				     uint8_t **hw_src, void **ip_src,
+				     uint8_t **hw_dst, void **ip_dst)
+{
+	if ((pkt_dir_type == BATADV_DAT_OUTGOING_PKT_REQUEST ||
+	     pkt_dir_type == BATADV_DAT_INCOMING_PKT_REQUEST ||
+	     pkt_dir_type == BATADV_DAT_BROADCAST_FALLBACK) &&
+	    pkt_type != ARPOP_REQUEST)
+		return false;
+	if ((pkt_dir_type == BATADV_DAT_OUTGOING_PKT_REPLY ||
+	     pkt_dir_type == BATADV_DAT_INCOMING_PKT_REPLY) &&
+	    pkt_type != ARPOP_REPLY)
+		return false;
+
+	if (hw_src)
+		*hw_src = batadv_arp_hw_src(skb, hdr_size);
+	if (ip_src)
+		*ip_src = batadv_arp_ip_src(skb, hdr_size);
+	if (hw_dst)
+		*hw_dst = batadv_arp_hw_dst(skb, hdr_size);
+	if (ip_dst)
+		*ip_dst = batadv_arp_ip_dst(skb, hdr_size);
+
+	if (pkt_type == ARPOP_REQUEST)
+		batadv_dbg_arp(bat_priv, skb, pkt_type, hdr_size,
+			       "Parsing ARP REQUEST");
+	else if (pkt_type == ARPOP_REPLY)
+		batadv_dbg_arp(bat_priv, skb, pkt_type, hdr_size,
+			       "Parsing ARP REPLY");
+
+	return true;
+}
 /**
  * batadv_dat_get_vid - extract the VLAN identifier from skb if any
  * @skb: the buffer containing the packet to extract the VID from
@@ -1135,7 +1203,7 @@  out:
  * @bat_priv: the bat priv with all the soft interface information
  * @skb: packet to check
  * @hdr_size: size of the encapsulation header
- * @ndisc_type: type of ndisc packet to check
+ * @type: type of ndisc packet to check
  *
  * Check if all IPs are valid (source, destination, target) and if
  * options hw address is valid too.
@@ -1146,17 +1214,14 @@  out:
  */
 static bool batadv_ndisc_is_valid(struct batadv_priv *bat_priv,
 				  struct sk_buff *skb, int hdr_size,
-				  int ndisc_type)
+				  uint16_t type)
 {
 	uint8_t *hw_target = NULL;
 	struct in6_addr *ipv6_src, *ipv6_dst, *ipv6_target;
-	__u8 type = batadv_ndisc_get_type(bat_priv, skb, hdr_size);
 	int ndisc_hdr_len = hdr_size + ETH_HLEN + sizeof(struct ipv6hdr) +
 			    sizeof(struct icmp6hdr) + sizeof(struct in6_addr) +
 			    8; /* ndisc options length */
 
-	if (type != ndisc_type)
-		return false;
 	if (unlikely(!pskb_may_pull(skb, ndisc_hdr_len)))
 		return false;
 
@@ -1169,6 +1234,8 @@  static bool batadv_ndisc_is_valid(struct batadv_priv *bat_priv,
 	else if (type == NDISC_NEIGHBOUR_ADVERTISEMENT)
 		hw_target = batadv_ndisc_hw_opt(skb, hdr_size,
 						ND_OPT_TARGET_LL_ADDR);
+	else
+		return false;
 
 	if (!hw_target || is_zero_ether_addr(hw_target) ||
 	    is_multicast_ether_addr(hw_target))
@@ -1298,11 +1365,155 @@  sk_buff *batadv_ndisc_create_na(struct net_device *dev,
 
 	return skb;
 }
+
+struct sk_buff *
+batadv_dat_create_ndisc_na(struct batadv_priv *bat_priv,
+			   struct batadv_dat_entry *dat_entry,
+			   uint8_t *hw_src, void *ip_src, void *ip_dst)
+{
+	/* TODO calculate router and override parameters */
+	return batadv_ndisc_create_na(bat_priv->soft_iface,
+				      ip_src, ip_dst,
+				      hw_src, dat_entry->mac_addr,
+				      0, 1, 1);
+}
+
+/**
+ * batadv_dat_snoop_ndisc_addr - snoop addresses from NA / NS messages
+ * @skb: packet to snoop
+ * @hdr_size: size of the encapsulation header
+ * @target_address_type: type of target address
+ * (ND_OPT_SOURCE_LL_ADDR or ND_OPT_TARGET_LL_ADDR)
+ * @hw_src: source HW Address
+ * @ipv6_src: source IPv6
+ * @hw_dst: destination HW Address
+ * @ipv6_dst: destination IPv6
+ * @hw_target: target HW Address
+ * @ipv6_target: target IPv6
+ *
+ * If an address parameter is NULL, then the correspondent field is not
+ * snooped. The fields might be different depending on packet type (NS / NA).
+ *
+ * Return true if snooping was successful.
+ */
+static bool batadv_dat_snoop_ndisc_addr(struct sk_buff *skb, int hdr_size,
+					uint8_t target_address_type,
+					uint8_t **hw_src, void **ipv6_src,
+					uint8_t **hw_dst, void **ipv6_dst,
+					uint8_t **hw_target, void **ipv6_target)
+{
+	if (hw_src)
+		*hw_src = batadv_ndisc_hw_src(skb, hdr_size);
+	if (ipv6_src)
+		*ipv6_src = batadv_ndisc_ipv6_src(skb, hdr_size);
+	if (hw_dst)
+		*hw_dst = batadv_ndisc_hw_dst(skb, hdr_size);
+	if (ipv6_dst)
+		*ipv6_dst = batadv_ndisc_ipv6_dst(skb, hdr_size);
+	if (hw_target) {
+		*hw_target = batadv_ndisc_hw_opt(skb, hdr_size,
+						 target_address_type);
+		if (!*hw_target)
+			return false;
+	}
+	if (ipv6_target)
+		*ipv6_target = batadv_ndisc_ipv6_target(skb, hdr_size);
+
+	return true;
+}
+
+static bool batadv_dat_snoop_ndisc_pkt(struct batadv_priv *bat_priv,
+				       struct sk_buff *skb, int hdr_size,
+				       uint16_t pkt_type, uint8_t pkt_dir_type,
+				       uint8_t **hw_src, void **ip_src,
+				       uint8_t **hw_dst, void **ip_dst)
+{
+	if (batadv_ndisc_is_valid(bat_priv, skb, hdr_size, pkt_type)) {
+		if ((pkt_dir_type == BATADV_DAT_OUTGOING_PKT_REQUEST ||
+		     pkt_dir_type == BATADV_DAT_INCOMING_PKT_REQUEST ||
+		     pkt_dir_type == BATADV_DAT_BROADCAST_FALLBACK) &&
+		    pkt_type == NDISC_NEIGHBOUR_SOLICITATION) {
+			if (!batadv_dat_snoop_ndisc_addr(skb, hdr_size,
+							 ND_OPT_SOURCE_LL_ADDR,
+							 NULL, ip_src,
+							 hw_dst, NULL,
+							 hw_src, ip_dst))
+				return false;
+
+			if (pkt_dir_type != BATADV_DAT_BROADCAST_FALLBACK)
+				batadv_dbg(BATADV_DBG_DAT, bat_priv,
+					   "Parsing NS = [src: %pM / %pI6c -> "
+						"target: %pM / %pI6c]\n",
+					   *hw_src, *ip_src, *hw_dst, *ip_dst);
+			return true;
+		}
+
+		if ((pkt_dir_type == BATADV_DAT_OUTGOING_PKT_REPLY ||
+		     pkt_dir_type == BATADV_DAT_INCOMING_PKT_REPLY) &&
+		    pkt_type == NDISC_NEIGHBOUR_ADVERTISEMENT) {
+			if (!batadv_dat_snoop_ndisc_addr(skb, hdr_size,
+							 ND_OPT_TARGET_LL_ADDR,
+							 NULL, NULL,
+							 hw_dst, ip_dst,
+							 hw_src, ip_src))
+				return false;
+
+			batadv_dbg(BATADV_DBG_DAT, bat_priv,
+				   "Parsing NA = [src: %pM / %pI6c -> "
+					"target: %pM / %pI6c]\n",
+				   *hw_src, *ip_src, *hw_dst, *ip_dst);
+			return true;
+
+			/* ignore multicast address for unsolicited NA */
+			if (ipv6_addr_is_multicast(*ip_dst))
+				*ip_dst = NULL;
+		}
+	}
+
+	return false;
+}
+#endif
+
+/**
+ * batadv_dat_get_pair_type : gets packet and data types
+ * @bat_priv: the bat priv with all the soft interface information
+ * @skb: packet to snoop
+ * @hdr_size: size of the encapsulation header
+ *
+ * Returns the packet and types, or -1 for data_type if invalid.
+ */
+static struct batadv_dat_pair_type
+batadv_dat_get_pair_type(struct batadv_priv *bat_priv,
+			 struct sk_buff *skb, int hdr_size)
+{
+	struct batadv_dat_pair_type pair_type = {
+		.data_type = -1,
+	};
+
+	uint16_t arp_type;
+	__u8 ndisc_type;
+
+	arp_type = batadv_arp_get_type(bat_priv, skb, hdr_size);
+	if (arp_type == ARPOP_REQUEST || arp_type == ARPOP_REPLY) {
+		pair_type.pkt_type = arp_type;
+		pair_type.data_type = BATADV_DAT_IPV4;
+	}
+
+#if IS_ENABLED(CONFIG_IPV6)
+	ndisc_type = batadv_ndisc_get_type(bat_priv, skb, hdr_size);
+	if (ndisc_type == NDISC_NEIGHBOUR_SOLICITATION ||
+	    ndisc_type == NDISC_NEIGHBOUR_ADVERTISEMENT) {
+		pair_type.pkt_type = ndisc_type;
+		pair_type.data_type = BATADV_DAT_IPV6;
+	}
 #endif
 
+	return pair_type;
+}
+
 /**
- * batadv_dat_snoop_outgoing_pkt_request - snoop the ARP request and try to
- * answer using DAT
+ * batadv_dat_snoop_outgoing_pkt_request - snoop the ARP request / NS
+ * and try to answer using DAT
  * @bat_priv: the bat priv with all the soft interface information
  * @skb: packet to check
  *
@@ -1313,37 +1524,37 @@  sk_buff *batadv_ndisc_create_na(struct net_device *dev,
 bool batadv_dat_snoop_outgoing_pkt_request(struct batadv_priv *bat_priv,
 					   struct sk_buff *skb)
 {
-	uint16_t type = 0;
-	__be32 ip_dst, ip_src;
-	uint8_t *hw_src;
+	void *ip_src, *ip_dst;
+	uint8_t *hw_src, *hw_dst;
 	bool ret = false;
 	struct batadv_dat_entry *dat_entry = NULL;
 	struct sk_buff *skb_new;
 	int hdr_size = 0;
 	unsigned short vid;
+	struct batadv_dat_pair_type dat_pair_type;
 
 	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 the node gets an ARP_REQUEST it has to send a DHT_GET unicast
+	/* If the node gets an ARP_REQUEST / NS it has to send a DHT_GET unicast
 	 * message to the selected DHT candidates
 	 */
-	if (type != ARPOP_REQUEST)
+	dat_pair_type = batadv_dat_get_pair_type(bat_priv, skb, hdr_size);
+	if (dat_pair_type.data_type < 0)
+		goto out;
+	if (!batadv_dat_types_info[dat_pair_type.data_type].snoop_pkt(
+			bat_priv, skb, hdr_size, dat_pair_type.pkt_type,
+			BATADV_DAT_OUTGOING_PKT_REQUEST,
+			&hw_src, &ip_src, &hw_dst, &ip_dst))
 		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, BATADV_DAT_IPV4, hw_src, vid);
+	batadv_dat_entry_add(bat_priv, ip_src, dat_pair_type.data_type,
+			     hw_src, vid);
 
-	dat_entry = batadv_dat_entry_hash_find(bat_priv, &ip_dst,
-					       BATADV_DAT_IPV4, vid);
+	dat_entry = batadv_dat_entry_hash_find(bat_priv, ip_dst,
+					       dat_pair_type.data_type, 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
@@ -1359,9 +1570,9 @@  bool batadv_dat_snoop_outgoing_pkt_request(struct batadv_priv *bat_priv,
 			goto out;
 		}
 
-		skb_new = arp_create(ARPOP_REPLY, ETH_P_ARP, ip_src,
-				     bat_priv->soft_iface, ip_dst, hw_src,
-				     dat_entry->mac_addr, hw_src);
+		skb_new = batadv_dat_types_info[dat_pair_type.data_type].
+				create_skb(bat_priv, dat_entry,
+					   hw_src, ip_src, ip_dst);
 		if (!skb_new)
 			goto out;
 
@@ -1373,12 +1584,13 @@  bool batadv_dat_snoop_outgoing_pkt_request(struct batadv_priv *bat_priv,
 		bat_priv->soft_iface->last_rx = jiffies;
 
 		netif_rx(skb_new);
-		batadv_dbg(BATADV_DBG_DAT, bat_priv, "ARP request replied locally\n");
+		batadv_dbg(BATADV_DBG_DAT, bat_priv,
+			   "ARP request / NS replied locally\n");
 		ret = true;
 	} else {
 		/* Send the request to the DHT */
-		ret = batadv_dat_send_data(bat_priv, skb, &ip_dst,
-					   BATADV_DAT_IPV4,
+		ret = batadv_dat_send_data(bat_priv, skb, ip_dst,
+					   dat_pair_type.data_type,
 					   BATADV_P_DAT_DHT_GET);
 	}
 out:
@@ -1388,8 +1600,8 @@  out:
 }
 
 /**
- * batadv_dat_snoop_incoming_pkt_request - snoop the ARP request and try to
- * answer using the local DAT storage
+ * batadv_dat_snoop_incoming_pkt_request - snoop the ARP request / NS 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
@@ -1399,42 +1611,39 @@  out:
 bool batadv_dat_snoop_incoming_pkt_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;
+	void *ip_src, *ip_dst;
+	uint8_t *hw_src, *hw_dst;
 	struct sk_buff *skb_new;
 	struct batadv_dat_entry *dat_entry = NULL;
 	bool ret = false;
 	unsigned short vid;
 	int err;
+	struct batadv_dat_pair_type dat_pair_type;
 
 	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)
+	dat_pair_type = batadv_dat_get_pair_type(bat_priv, skb, hdr_size);
+	if (dat_pair_type.data_type < 0)
+		goto out;
+	if (!batadv_dat_types_info[dat_pair_type.data_type].snoop_pkt(
+			bat_priv, skb, hdr_size, dat_pair_type.pkt_type,
+			BATADV_DAT_INCOMING_PKT_REQUEST,
+			&hw_src, &ip_src, &hw_dst, &ip_dst))
 		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, BATADV_DAT_IPV4, hw_src, vid);
+	batadv_dat_entry_add(bat_priv, ip_src, dat_pair_type.data_type,
+			     hw_src, vid);
 
-	dat_entry = batadv_dat_entry_hash_find(bat_priv, &ip_dst,
-					       BATADV_DAT_IPV4, vid);
+	dat_entry = batadv_dat_entry_hash_find(bat_priv, ip_dst,
+					       dat_pair_type.data_type, vid);
 	if (!dat_entry)
 		goto out;
 
-	skb_new = arp_create(ARPOP_REPLY, ETH_P_ARP, ip_src,
-			     bat_priv->soft_iface, ip_dst, hw_src,
-			     dat_entry->mac_addr, hw_src);
-
+	skb_new = batadv_dat_types_info[dat_pair_type.data_type].create_skb(
+			bat_priv, dat_entry, hw_src, ip_src, ip_dst);
 	if (!skb_new)
 		goto out;
 
@@ -1462,49 +1671,55 @@  out:
 }
 
 /**
- * batadv_dat_snoop_outgoing_pkt_reply - snoop the ARP reply and fill the DHT
+ * batadv_dat_snoop_outgoing_pkt_reply - snoop the ARP reply / NA and fill
+ * the DHT
  * @bat_priv: the bat priv with all the soft interface information
  * @skb: packet to check
  */
 void batadv_dat_snoop_outgoing_pkt_reply(struct batadv_priv *bat_priv,
 					 struct sk_buff *skb)
 {
-	uint16_t type;
-	__be32 ip_src, ip_dst;
+	void *ip_src, *ip_dst;
 	uint8_t *hw_src, *hw_dst;
 	int hdr_size = 0;
 	unsigned short vid;
+	struct batadv_dat_pair_type dat_pair_type;
 
 	if (!atomic_read(&bat_priv->distributed_arp_table))
 		return;
 
 	vid = batadv_dat_get_vid(skb, &hdr_size);
 
-	type = batadv_arp_get_type(bat_priv, skb, hdr_size);
-	if (type != ARPOP_REPLY)
+	dat_pair_type = batadv_dat_get_pair_type(bat_priv, skb, hdr_size);
+	if (dat_pair_type.data_type < 0)
+		return;
+	if (!batadv_dat_types_info[dat_pair_type.data_type].snoop_pkt(
+			bat_priv, skb, hdr_size, dat_pair_type.pkt_type,
+			BATADV_DAT_OUTGOING_PKT_REPLY,
+			&hw_src, &ip_src, &hw_dst, &ip_dst))
 		return;
-
-	batadv_dbg_arp(bat_priv, skb, type, 0, "Parsing outgoing 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);
-
-	batadv_dat_entry_add(bat_priv, &ip_src, BATADV_DAT_IPV4, hw_src, vid);
-	batadv_dat_entry_add(bat_priv, &ip_dst, BATADV_DAT_IPV4, hw_dst, vid);
 
 	/* Send the ARP reply to the candidates for both the IP addresses that
 	 * the node obtained from the ARP reply
 	 */
-	batadv_dat_send_data(bat_priv, skb, &ip_src, BATADV_DAT_IPV4,
-			     BATADV_P_DAT_DHT_PUT);
-	batadv_dat_send_data(bat_priv, skb, &ip_dst, BATADV_DAT_IPV4,
+	batadv_dat_entry_add(bat_priv, ip_src, dat_pair_type.data_type,
+			     hw_src, vid);
+	batadv_dat_send_data(bat_priv, skb, ip_src, dat_pair_type.data_type,
 			     BATADV_P_DAT_DHT_PUT);
+
+	/* not a solicited advertisement (see snooping mechanism) */
+	if (ip_dst) {
+		batadv_dat_entry_add(bat_priv, ip_dst, dat_pair_type.data_type,
+				     hw_dst, vid);
+		batadv_dat_send_data(bat_priv, skb, ip_dst,
+				     dat_pair_type.data_type,
+				     BATADV_P_DAT_DHT_PUT);
+	}
 }
+
 /**
- * batadv_dat_snoop_incoming_pkt_reply - snoop the ARP reply and fill the local
- * DAT storage only
+ * batadv_dat_snoop_incoming_pkt_reply - snoop the ARP reply / NA and fill
+ * the local DAT storage only
  * @bat_priv: the bat priv with all the soft interface information
  * @skb: packet to check
  * @hdr_size: size of the encapsulation header
@@ -1512,34 +1727,35 @@  void batadv_dat_snoop_outgoing_pkt_reply(struct batadv_priv *bat_priv,
 bool batadv_dat_snoop_incoming_pkt_reply(struct batadv_priv *bat_priv,
 					 struct sk_buff *skb, int hdr_size)
 {
-	uint16_t type;
-	__be32 ip_src, ip_dst;
+	void *ip_src, *ip_dst;
 	uint8_t *hw_src, *hw_dst;
 	bool ret = false;
 	unsigned short vid;
+	struct batadv_dat_pair_type dat_pair_type;
 
 	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)
+	dat_pair_type = batadv_dat_get_pair_type(bat_priv, skb, hdr_size);
+	if (dat_pair_type.data_type < 0)
+		goto out;
+	if (!batadv_dat_types_info[dat_pair_type.data_type].snoop_pkt(
+			bat_priv, skb, hdr_size, dat_pair_type.pkt_type,
+			BATADV_DAT_INCOMING_PKT_REPLY,
+			&hw_src, &ip_src, &hw_dst, &ip_dst))
 		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, BATADV_DAT_IPV4, hw_src, vid);
-	batadv_dat_entry_add(bat_priv, &ip_dst, BATADV_DAT_IPV4, hw_dst, vid);
+	batadv_dat_entry_add(bat_priv, ip_src, dat_pair_type.data_type,
+			     hw_src, vid);
+	/* not a solicited advertisement (see snooping mechanism) */
+	if (ip_dst)
+		batadv_dat_entry_add(bat_priv, ip_dst,
+				     dat_pair_type.data_type, hw_dst, vid);
 
 	/* if this REPLY is directed to a client of mine, let's deliver the
 	 * packet to the interface
@@ -1553,8 +1769,8 @@  out:
 }
 
 /**
- * batadv_dat_drop_broadcast_packet - check if an ARP request has to be dropped
- * (because the node has already obtained the reply via DAT) or not
+ * batadv_dat_drop_broadcast_packet - check if an ARP request / NA has to be
+ * dropped (because the node has already obtained the reply via DAT) or not
  * @bat_priv: the bat priv with all the soft interface information
  * @forw_packet: the broadcast packet
  *
@@ -1563,12 +1779,13 @@  out:
 bool batadv_dat_drop_broadcast_packet(struct batadv_priv *bat_priv,
 				      struct batadv_forw_packet *forw_packet)
 {
-	uint16_t type;
-	__be32 ip_dst;
+	void *ip_dst;
 	struct batadv_dat_entry *dat_entry = NULL;
 	bool ret = false;
 	int hdr_size = sizeof(struct batadv_bcast_packet);
 	unsigned short vid;
+	struct batadv_dat_pair_type dat_pair_type;
+	char dbg_data[BATADV_DAT_DATA_MAX_LEN];
 
 	if (!atomic_read(&bat_priv->distributed_arp_table))
 		goto out;
@@ -1581,22 +1798,31 @@  bool batadv_dat_drop_broadcast_packet(struct batadv_priv *bat_priv,
 
 	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)
+	dat_pair_type = batadv_dat_get_pair_type(bat_priv, forw_packet->skb,
+						 hdr_size);
+	if (dat_pair_type.data_type < 0)
 		goto out;
+	if (!batadv_dat_types_info[dat_pair_type.data_type].snoop_pkt(
+			bat_priv, forw_packet->skb, hdr_size,
+			dat_pair_type.pkt_type,
+			BATADV_DAT_BROADCAST_FALLBACK,
+			NULL, NULL, NULL, &ip_dst))
+		goto out;
+
+	dat_entry = batadv_dat_entry_hash_find(bat_priv, ip_dst,
+					       dat_pair_type.data_type, vid);
 
-	ip_dst = batadv_arp_ip_dst(forw_packet->skb, hdr_size);
-	dat_entry = batadv_dat_entry_hash_find(bat_priv, &ip_dst,
-					       BATADV_DAT_IPV4, vid);
 	/* check if the node already got this entry */
+	batadv_dat_data_to_str(ip_dst, dat_pair_type.data_type,
+			       dbg_data, sizeof(dbg_data));
 	if (!dat_entry) {
 		batadv_dbg(BATADV_DBG_DAT, bat_priv,
-			   "ARP Request for %pI4: fallback\n", &ip_dst);
+			   "ARP Request for %s: fallback\n", dbg_data);
 		goto out;
 	}
 
 	batadv_dbg(BATADV_DBG_DAT, bat_priv,
-		   "ARP Request for %pI4: fallback prevented\n", &ip_dst);
+		   "ARP Request for %s fallback prevented\n", dbg_data);
 	ret = true;
 
 out:
diff --git a/types.h b/types.h
index 60d2d64..6ad4500 100644
--- a/types.h
+++ b/types.h
@@ -964,13 +964,82 @@  enum batadv_dat_types {
 };
 
 /**
+ * batadv_dat_pkt_types - describe generic packet types
+ * @BATADV_DAT_OUTGOING_PKT_REQUEST: an outgoing packet request
+ * @BATADV_DAT_INCOMING_PKT_REQUEST: an incoming packet request
+ * @BATADV_DAT_OUTGOING_PKT_REPLY: an outgoing packet reply
+ * @BATADV_DAT_INCOMING_PKT_REPLY: an incoming packet reply
+ * @BATADV_DAT_BROADCAST_FALLBACK: used for broadcasts fallback;
+ * can any type of request packets
+ *
+ * The packets can be ARP Request / Reply, NS / NA or other packets.
+ */
+enum batadv_dat_pkt_types {
+	BATADV_DAT_OUTGOING_PKT_REQUEST,
+	BATADV_DAT_INCOMING_PKT_REQUEST,
+	BATADV_DAT_OUTGOING_PKT_REPLY,
+	BATADV_DAT_INCOMING_PKT_REPLY,
+	BATADV_DAT_BROADCAST_FALLBACK,
+};
+
+/**
+ * batadv_dat_pair_type - types needed for a dat packet
+ * @data_type the data type (negative values represents invalid values)
+ * @pkt_type: the packet type
+ */
+struct batadv_dat_pair_type {
+	int data_type;
+	uint16_t pkt_type;
+};
+
+/**
  * batadv_dat_type_info - info needed for a DAT type data
  * @size: the size of the type data
  * @str_fmt: string format used by the data
+ * @snoop_pkt: function used to snoop addresses from a packet
  */
 struct batadv_dat_type_info {
 	size_t size;
 	char *str_fmt;
+	/**
+	 * snoop_pkt - snooping mechanism for all packets that participate
+	 * in DAT
+	 * @bat_priv: the bat priv with all the soft interface information
+	 * @skb: packet to snoop
+	 * @hdr_size: size of the encapsulation header
+	 * @pkt_type: the packet type
+	 * @pkt_dir_type: outgoing / incoming message request / reply
+	 * @hw_src: source HW Address
+	 * @ip_src: source IP
+	 * @hw_dst: destination HW Address
+	 * @ip_dst: destination IP
+	 *
+	 * Any address pointer can be null if there is no need to memorize
+	 * calculate that address.
+	 *
+	 * Returns true if snooping was successful.
+	 */
+	bool (*snoop_pkt)(struct batadv_priv *bat_priv,
+			  struct sk_buff *skb, int hdr_size,
+			  uint16_t pkt_type, uint8_t pkt_dir_type,
+			  uint8_t **hw_src, void **ip_src,
+			  uint8_t **hw_dst, void **ip_dst);
+
+	/**
+	 * batadv_dat_create_skb - creates a skb as a reply to a message request
+	 * @bat_priv: the bat priv with all the soft interface information
+	 * @dat_entry: the DAT entry used for destination HW Address
+	 * @hw_src: source HW Address
+	 * @ip_src: source IP
+	 * @ip_dst: destination IP
+	 *
+	 * Returns the newly created skb, or NULL if any error.
+	 */
+	struct sk_buff *(*create_skb)(struct batadv_priv *bat_priv,
+				      struct batadv_dat_entry *dat_entry,
+				      uint8_t *hw_src, void *ip_src,
+				      void *ip_dst);
+
 };
 
 /**