[qca-nss-ecm] Use the common hierarchy construct fucntion for IPv6

* Removed the IPv6 specific hierarchy construct function.
* Reworked the ecm_interface version of the function to be used
  by both IPv4 and IPv6.
* Implement a new ARP request send function.

Change-Id: I29f8bf979d40a9ad437dc0ee7e7c623b021967e5
Signed-off-by: Murat Sezgin <msezgin@codeaurora.org>
diff --git a/ecm_interface.c b/ecm_interface.c
index cb9b753..4147be3 100644
--- a/ecm_interface.c
+++ b/ecm_interface.c
@@ -595,6 +595,112 @@
 }
 EXPORT_SYMBOL(ecm_interface_route_release);
 
+#ifdef ECM_IPV6_ENABLE
+/*
+ * ecm_interface_send_neighbour_solicitation()
+ *	Issue an IPv6 Neighbour soliciation request.
+ */
+void ecm_interface_send_neighbour_solicitation(struct net_device *dev, ip_addr_t addr)
+{
+	struct in6_addr dst_addr, src_addr;
+	struct in6_addr mc_dst_addr;
+	struct rt6_info *rt6i;
+	struct neighbour *neigh;
+	ip_addr_t ecm_mc_dst_addr, ecm_src_addr;
+	struct net *netf = dev_net(dev);
+	int ret;
+
+	char __attribute__((unused)) dst_addr_str[ECM_IP_ADDR_STRING_BUFFER_SIZE];
+	char __attribute__((unused)) mc_dst_addr_str[ECM_IP_ADDR_STRING_BUFFER_SIZE];
+	char __attribute__((unused)) src_addr_str[ECM_IP_ADDR_STRING_BUFFER_SIZE];
+
+	/*
+	 * Find source and destination addresses in Linux format. We need
+	 * mcast destination address as well.
+	 */
+	ECM_IP_ADDR_TO_NIN6_ADDR(dst_addr, addr);
+	addrconf_addr_solict_mult(&dst_addr, &mc_dst_addr);
+	ret = ipv6_dev_get_saddr(netf, dev, &dst_addr, 0, &src_addr);
+
+	/*
+	 * IP address in string format for debug
+	 */
+	ecm_ip_addr_to_string(dst_addr_str, addr);
+	ECM_NIN6_ADDR_TO_IP_ADDR(ecm_mc_dst_addr, mc_dst_addr);
+	ecm_ip_addr_to_string(mc_dst_addr_str, ecm_mc_dst_addr);
+	ECM_NIN6_ADDR_TO_IP_ADDR(ecm_src_addr, src_addr);
+	ecm_ip_addr_to_string(src_addr_str, ecm_src_addr);
+
+	/*
+	 * Find the route entry
+	 */
+	rt6i = rt6_lookup(netf, &dst_addr, NULL, 0, 0);
+	if (!rt6i) {
+		DEBUG_TRACE("IPv6 Route lookup failure for destination IPv6 address %s\n", dst_addr_str);
+		return;
+	}
+
+	/*
+	 * Find the neighbor entry
+	 */
+#if (LINUX_VERSION_CODE <= KERNEL_VERSION(3,6,0))
+	neigh = rt6i->dst.ops->neigh_lookup(&rt6i->dst, &dst_addr);
+#else
+	neigh = rt6i->dst.ops->neigh_lookup(&rt6i->dst, NULL, &dst_addr);
+#endif
+	if (neigh == NULL) {
+		DEBUG_TRACE("Neighbour lookup failure for destination IPv6 address %s\n", dst_addr_str);
+		dst_release(&rt6i->dst);
+		return;
+	}
+
+	/*
+	 * Issue a Neighbour soliciation request
+	 */
+	DEBUG_TRACE("Issue Neighbour solicitation request\n");
+	ndisc_send_ns(dev, neigh, &dst_addr, &mc_dst_addr, &src_addr);
+	neigh_release(neigh);
+	dst_release(&rt6i->dst);
+}
+EXPORT_SYMBOL(ecm_interface_send_neighbour_solicitation);
+#endif
+
+/*
+ * ecm_interface_send_arp_request()
+ *	Issue and ARP request.
+ */
+void ecm_interface_send_arp_request(struct net_device *dest_dev, ip_addr_t dest_addr, bool on_link, ip_addr_t gw_addr)
+{
+	/*
+	 * Possible ARP does not know the address yet
+	 */
+	__be32 ipv4_addr;
+	__be32 src_ip;
+
+	/*
+	 * Issue an ARP request for it, select the src_ip from which to issue the request.
+	 */
+	ECM_IP_ADDR_TO_NIN4_ADDR(ipv4_addr, dest_addr);
+	src_ip = inet_select_addr(dest_dev, ipv4_addr, RT_SCOPE_LINK);
+	if (!src_ip) {
+		DEBUG_TRACE("Failed to lookup IP for %pI4\n", &ipv4_addr);
+		return;
+	}
+
+	/*
+	 * If we have a GW for this address, then we have to send ARP request to the GW
+	 */
+	if (!on_link && !ECM_IP_ADDR_IS_NULL(gw_addr)) {
+		ECM_IP_ADDR_TO_NIN4_ADDR(ipv4_addr, gw_addr);
+	}
+
+	DEBUG_TRACE("Send ARP for %pI4 using src_ip as %pI4\n", &ipv4_addr, &src_ip);
+	arp_send(ARPOP_REQUEST, ETH_P_ARP, ipv4_addr, dest_dev, src_ip, NULL, NULL, NULL);
+
+	return;
+}
+EXPORT_SYMBOL(ecm_interface_send_arp_request);
+
 #ifdef ECM_INTERFACE_PPP_ENABLE
 /*
  * ecm_interface_skip_l2tp_pptp()
@@ -2198,11 +2304,14 @@
  *
  * IMPORTANT: This function will return any known interfaces in the database, when interfaces do not exist in the database
  * they will be created and added automatically to the database.
- *
- * GGG TODO Make this function work for IPv6!!!!!!!!!!!!!!
  */
-int32_t ecm_interface_heirarchy_construct(struct ecm_db_iface_instance *interfaces[], ip_addr_t packet_src_addr, ip_addr_t packet_dest_addr, int packet_protocol,
-						struct net_device *given_dest_dev, bool is_routed, struct net_device *given_src_dev, uint8_t *dest_node_addr, uint8_t *src_node_addr)
+int32_t ecm_interface_heirarchy_construct(struct ecm_db_iface_instance *interfaces[],
+						ip_addr_t packet_src_addr,
+						ip_addr_t packet_dest_addr,
+						int ip_version, int packet_protocol,
+						struct net_device *given_dest_dev,
+						bool is_routed, struct net_device *given_src_dev,
+						uint8_t *dest_node_addr, uint8_t *src_node_addr)
 {
 	int protocol;
 	ip_addr_t src_addr;
@@ -2222,8 +2331,19 @@
 	protocol = packet_protocol;
 	ECM_IP_ADDR_COPY(src_addr, packet_src_addr);
 	ECM_IP_ADDR_COPY(dest_addr, packet_dest_addr);
-	DEBUG_TRACE("Construct interface heirarchy for from src_addr: " ECM_IP_ADDR_DOT_FMT " to dest_addr: " ECM_IP_ADDR_DOT_FMT ", protocol: %d\n",
-			ECM_IP_ADDR_TO_DOT(src_addr), ECM_IP_ADDR_TO_DOT(dest_addr), protocol);
+
+	if (ip_version == 4) {
+		DEBUG_TRACE("Construct interface heirarchy for from src_addr: " ECM_IP_ADDR_DOT_FMT " to dest_addr: " ECM_IP_ADDR_DOT_FMT ", protocol: %d\n",
+				ECM_IP_ADDR_TO_DOT(src_addr), ECM_IP_ADDR_TO_DOT(dest_addr), protocol);
+#ifdef ECM_IPV6_ENABLE
+	} else if (ip_version == 6) {
+		DEBUG_TRACE("Construct interface heirarchy for from src_addr: " ECM_IP_ADDR_OCTAL_FMT " to dest_addr: " ECM_IP_ADDR_OCTAL_FMT ", protocol: %d\n",
+				ECM_IP_ADDR_TO_OCTAL(src_addr), ECM_IP_ADDR_TO_OCTAL(dest_addr), protocol);
+#endif
+	} else {
+		DEBUG_WARN("Wrong IP protocol: %d\n", ip_version);
+		return ECM_DB_IFACE_HEIRARCHY_MAX;
+	}
 
 	/*
 	 * Get device to reach the given destination address.
@@ -2258,12 +2378,17 @@
 	 * PARSE THE DEVICES AND WORK OUT THE PROPER INTERFACES INVOLVED.
 	 * E.G. IF WE TRIED TO RUN A TUNNEL OVER A VLAN OR QINQ THIS WILL BREAK AS WE DON'T DISCOVER THAT HIERARCHY
 	 */
-	if (dest_dev && from_local_addr && (protocol == IPPROTO_IPV6)) {
-		dev_put(dest_dev);
-		dest_dev = given_dest_dev;
-		if (dest_dev) {
-			dev_hold(dest_dev);
-			DEBUG_TRACE("HACK: IPV6 tunnel packet with dest_addr: " ECM_IP_ADDR_OCTAL_FMT " uses dev: %p(%s)\n", ECM_IP_ADDR_TO_OCTAL(dest_addr), dest_dev, dest_dev->name);
+	if (dest_dev && from_local_addr) {
+		if (((ip_version == 4) && (protocol == IPPROTO_IPV6)) ||
+				((ip_version == 6) && (protocol == IPPROTO_IPIP))) {
+			dev_put(dest_dev);
+			dest_dev = given_dest_dev;
+			if (dest_dev) {
+				dev_hold(dest_dev);
+				DEBUG_TRACE("HACK: %s tunnel packet with dest_addr: " ECM_IP_ADDR_OCTAL_FMT " uses dev: %p(%s)\n",
+						(ip_version == 4) ? "IPV6" : "IPIP",
+						ECM_IP_ADDR_TO_OCTAL(dest_addr), dest_dev, dest_dev->name);
+			}
 		}
 	}
 	if (!dest_dev) {
@@ -2306,14 +2431,20 @@
 	 * PARSE THE DEVICES AND WORK OUT THE PROPER INTERFACES INVOLVED.
 	 * E.G. IF WE TRIED TO RUN A TUNNEL OVER A VLAN OR QINQ THIS WILL BREAK AS WE DON'T DISCOVER THAT HIERARCHY
 	 */
-	if (src_dev && from_local_addr && (protocol == IPPROTO_IPV6)) {
-		dev_put(src_dev);
-		src_dev = given_src_dev;
-		if (src_dev) {
-			dev_hold(src_dev);
-			DEBUG_TRACE("HACK: IPV6 tunnel packet with src_addr: " ECM_IP_ADDR_OCTAL_FMT " uses dev: %p(%s)\n", ECM_IP_ADDR_TO_OCTAL(src_addr), src_dev, src_dev->name);
+	if (src_dev && from_local_addr) {
+		if (((ip_version == 4) && (protocol == IPPROTO_IPV6)) ||
+				((ip_version == 6) && (protocol == IPPROTO_IPIP))) {
+			dev_put(src_dev);
+			src_dev = given_src_dev;
+			if (src_dev) {
+				dev_hold(src_dev);
+				DEBUG_TRACE("HACK: %s tunnel packet with src_addr: " ECM_IP_ADDR_OCTAL_FMT " uses dev: %p(%s)\n",
+						(ip_version == 4) ? "IPV6" : "IPIP",
+						ECM_IP_ADDR_TO_OCTAL(src_addr), src_dev, src_dev->name);
+			}
 		}
 	}
+
 	if (!src_dev) {
 		DEBUG_WARN("src_addr: " ECM_IP_ADDR_OCTAL_FMT " - cannot locate device\n", ECM_IP_ADDR_TO_OCTAL(src_addr));
 		dev_put(dest_dev);
@@ -2328,7 +2459,8 @@
 	 */
 	if (src_dev == dest_dev) {
 		DEBUG_TRACE("Protocol is :%d source dev and dest dev are same\n", protocol);
-		if ((protocol == IPPROTO_IPV6) || (protocol == IPPROTO_ESP)) {
+		if (((ip_version == 4) && ((protocol == IPPROTO_IPV6) || (protocol == IPPROTO_ESP)))
+				|| ((ip_version == 6) && (protocol == IPPROTO_IPIP))) {
 			/*
 			 * This happens from the input hook
 			 * We do not want to create a connection entry for this
@@ -2432,44 +2564,16 @@
 					ip_addr_t gw_addr = ECM_IP_ADDR_NULL;
 					uint8_t mac_addr[ETH_ALEN];
 					if (!ecm_interface_mac_addr_get(dest_addr, mac_addr, &on_link, gw_addr)) {
-						/*
-						 * Possible ARP does not know the address yet
-						 */
-						DEBUG_INFO("Unable to obtain MAC address for " ECM_IP_ADDR_DOT_FMT "\n", ECM_IP_ADDR_TO_DOT(dest_addr));
-						if (ECM_IP_ADDR_IS_V4(dest_addr)) {
-							__be32 ipv4_addr;
-							__be32 src_ip;
-
-							/*
-							 * Issue an ARP request for it, select the src_ip from which to issue the request.
-							 */
-							ECM_IP_ADDR_TO_NIN4_ADDR(ipv4_addr, dest_addr);
-							src_ip = inet_select_addr(dest_dev, ipv4_addr, RT_SCOPE_LINK);
-							if (!src_ip) {
-								DEBUG_TRACE("failed to lookup IP for %pI4\n", &ipv4_addr);
-
-								dev_put(src_dev);
-								dev_put(dest_dev);
-
-								/*
-								* Release the interfaces heirarchy we constructed to this point.
-								*/
-								ecm_db_connection_interfaces_deref(interfaces, current_interface_index);
-								return ECM_DB_IFACE_HEIRARCHY_MAX;
-							}
-
-							/*
-							 * If we have a GW for this address, then we have to send ARP request to the GW
-							 */
-							if (!on_link && !ECM_IP_ADDR_IS_NULL(gw_addr)) {
-								ECM_IP_ADDR_TO_NIN4_ADDR(ipv4_addr, gw_addr);
-							}
-
-							DEBUG_TRACE("Send ARP for %pI4 using src_ip as %pI4\n", &ipv4_addr, &src_ip);
-							arp_send(ARPOP_REQUEST, ETH_P_ARP, ipv4_addr, dest_dev, src_ip, NULL, NULL, NULL);
+						if (ip_version == 4) {
+							DEBUG_WARN("Unable to obtain MAC address for " ECM_IP_ADDR_DOT_FMT "\n", ECM_IP_ADDR_TO_DOT(dest_addr));
+							ecm_interface_send_arp_request(dest_dev, dest_addr, on_link, gw_addr);
 						}
-
-						DEBUG_WARN("Unable to obtain MAC address for " ECM_IP_ADDR_DOT_FMT "\n", ECM_IP_ADDR_TO_DOT(dest_addr));
+#ifdef ECM_IPV6_ENABLE
+						if (ip_version == 6) {
+							DEBUG_WARN("Unable to obtain MAC address for " ECM_IP_ADDR_OCTAL_FMT "\n", ECM_IP_ADDR_TO_OCTAL(dest_addr));
+							ecm_interface_send_neighbour_solicitation(dest_dev, dest_addr);
+						}
+#endif
 						dev_put(src_dev);
 						dev_put(dest_dev);
 
@@ -2539,18 +2643,10 @@
 						 */
 						if (!ecm_interface_mac_addr_get(dest_addr, dest_mac_addr,
 									&dest_on_link, dest_gw_addr)) {
-							__be32 ipv4_addr = 0;
-							__be32 src_ip = 0;
-							DEBUG_WARN("Unable to obtain MAC address for "
-										ECM_IP_ADDR_DOT_FMT "\n", ECM_IP_ADDR_TO_DOT(dest_addr));
-
 
 							/*
-							 * Issue an ARP request, select the src_ip from which to issue the request.
-							 */
-
-							/*
-							 * find proper interfce from which to issue ARP
+							 * Find proper interfce from which to issue ARP
+							 * or neighbour solicitation packet.
 							 */
 							if (dest_dev_master) {
 								master_dev = dest_dev_master;
@@ -2564,33 +2660,16 @@
 								dev_put(dest_dev_master);
 							}
 
-							ECM_IP_ADDR_TO_NIN4_ADDR(ipv4_addr, dest_addr);
-							src_ip = inet_select_addr(master_dev, ipv4_addr, RT_SCOPE_LINK);
-							if (!src_ip) {
-								DEBUG_TRACE("failed to lookup IP for %pI4\n", &ipv4_addr);
-
-								dev_put(src_dev);
-								dev_put(dest_dev);
-								dev_put(master_dev);
-
-								/*
-								* Release the interfaces heirarchy we constructed to this point.
-								*/
-								ecm_db_connection_interfaces_deref(interfaces, current_interface_index);
-								return ECM_DB_IFACE_HEIRARCHY_MAX;
+							if (ip_version == 4) {
+								DEBUG_WARN("Unable to obtain MAC address for " ECM_IP_ADDR_DOT_FMT "\n", ECM_IP_ADDR_TO_DOT(dest_addr));
+								ecm_interface_send_arp_request(dest_dev, dest_addr, dest_on_link, dest_gw_addr);
 							}
-
-							/*
-							 * If we have a GW for this address, then we have to send ARP request to the GW
-							 */
-							if (!dest_on_link && !ECM_IP_ADDR_IS_NULL(dest_gw_addr)) {
-								ECM_IP_ADDR_TO_NIN4_ADDR(ipv4_addr, dest_gw_addr);
+#ifdef ECM_IPV6_ENABLE
+							if (ip_version == 6) {
+								DEBUG_WARN("Unable to obtain MAC address for " ECM_IP_ADDR_OCTAL_FMT "\n",ECM_IP_ADDR_TO_OCTAL(dest_addr));
+								ecm_interface_send_neighbour_solicitation(master_dev, dest_addr);
 							}
-
-							DEBUG_TRACE("Send ARP for %pI4 using src_ip as %pI4\n", &ipv4_addr, &src_ip);
-							arp_send(ARPOP_REQUEST, ETH_P_ARP, ipv4_addr, master_dev, src_ip, NULL, NULL, NULL);
-
-
+#endif
 							dev_put(src_dev);
 							dev_put(dest_dev);
 							dev_put(master_dev);
@@ -2603,9 +2682,16 @@
 						}
 					}
 
-					next_dev = bond_get_tx_dev(NULL, src_mac_addr, dest_mac_addr,
-								   &src_addr_32, &dest_addr_32,
-								   htons((uint16_t)ETH_P_IP), dest_dev);
+					if (ip_version == 4) {
+						next_dev = bond_get_tx_dev(NULL, src_mac_addr, dest_mac_addr,
+										&src_addr_32, &dest_addr_32,
+										htons((uint16_t)ETH_P_IP), dest_dev);
+					} else if (ip_version == 6) {
+						next_dev = bond_get_tx_dev(NULL, src_mac_addr, dest_mac_addr,
+										src_addr, dest_addr,
+										htons((uint16_t)ETH_P_IPV6), dest_dev);
+					}
+
 					if (next_dev && netif_carrier_ok(next_dev)) {
 						dev_hold(next_dev);
 					} else {
diff --git a/ecm_interface.h b/ecm_interface.h
index afeb327..de509e1 100644
--- a/ecm_interface.h
+++ b/ecm_interface.h
@@ -42,9 +42,12 @@
 bool ecm_interface_mac_addr_get(ip_addr_t addr, uint8_t *mac_addr, bool *on_link, ip_addr_t gw_addr);
 bool ecm_interface_find_route_by_addr(ip_addr_t addr, struct ecm_interface_route *ecm_rt);
 void ecm_interface_route_release(struct ecm_interface_route *rt);
+#ifdef ECM_IPV6_ENABLE
+void ecm_interface_send_neighbour_solicitation(struct net_device *dev, ip_addr_t addr);
+#endif
+void ecm_interface_send_arp_request(struct net_device *dest_dev, ip_addr_t dest_addr, bool on_link, ip_addr_t gw_addr);
 bool ecm_interface_skip_l2tp_pptp(struct sk_buff *skb, const struct net_device *out);
 struct ecm_db_iface_instance *ecm_interface_establish_and_ref(struct net_device *dev);
-int32_t ecm_interface_heirarchy_construct(struct ecm_db_iface_instance *interfaces[], ip_addr_t packet_src_addr, ip_addr_t packet_dest_addr, int packet_protocol, struct net_device *given_dest_dev, bool is_routed, struct net_device *given_src_dev, uint8_t *dest_node_addr, uint8_t *src_node_addr);
 
 #ifdef ECM_MULTICAST_ENABLE
 int32_t ecm_interface_multicast_heirarchy_construct_routed(struct ecm_db_iface_instance *interfaces, struct net_device *in_dev, ip_addr_t packet_src_addr, ip_addr_t packet_dest_addr, uint8_t maxvif, uint32_t *dst_dev, uint32_t *to_interface_first);
@@ -58,6 +61,13 @@
 bool ecm_interface_multicast_check_for_br_dev(uint32_t dest_if[], uint8_t max_if);
 #endif
 
+int32_t ecm_interface_heirarchy_construct(struct ecm_db_iface_instance *interfaces[],
+					ip_addr_t packet_src_addr, ip_addr_t packet_dest_addr,
+					int ip_version, int packet_protocol,
+					struct net_device *given_dest_dev, bool is_routed,
+					struct net_device *given_src_dev,
+					uint8_t *dest_node_addr, uint8_t *src_node_addr);
+void ecm_interface_stats_update(struct ecm_db_connection_instance *ci, uint32_t from_tx_packets, uint32_t from_tx_bytes, uint32_t from_rx_packets, uint32_t from_rx_bytes, uint32_t to_tx_packets, uint32_t to_tx_bytes, uint32_t to_rx_packets, uint32_t to_rx_bytes);
 struct net_device *ecm_interface_dev_find_by_addr(ip_addr_t addr, bool *from_local_addr);
 void ecm_interface_stats_update(struct ecm_db_connection_instance *ci, uint32_t from_tx_packets, uint32_t from_tx_bytes, uint32_t from_rx_packets, uint32_t from_rx_bytes, uint32_t to_tx_packets, uint32_t to_tx_bytes, uint32_t to_rx_packets, uint32_t to_rx_bytes);
 
diff --git a/ecm_types.h b/ecm_types.h
index d214e2a..7a94c73 100644
--- a/ecm_types.h
+++ b/ecm_types.h
@@ -52,6 +52,8 @@
 
 #define ECM_IP_ADDR_NULL {0, 0, 0, 0}
 
+#define ECM_IP_ADDR_STRING_BUFFER_SIZE 40
+
 /*
  * Type checking functions for various forms of IP address
  * Placing these in a macro, enables the compiler to check
diff --git a/frontends/nss/ecm_front_end_ipv6.c b/frontends/nss/ecm_front_end_ipv6.c
index 099de20..6f397d7 100644
--- a/frontends/nss/ecm_front_end_ipv6.c
+++ b/frontends/nss/ecm_front_end_ipv6.c
@@ -202,613 +202,6 @@
 extern int nf_ct_tcp_be_liberal;
 
 /*
- * ecm_front_end_ipv6_send_neighbour_solicitation()
- *	Issue an IPv6 Neighbour soliciation request.
- */
-void ecm_front_end_ipv6_send_neighbour_solicitation(struct net_device *dev, ip_addr_t addr)
-{
-	struct in6_addr dst_addr, src_addr;
-	struct in6_addr mc_dst_addr;
-	struct rt6_info *rt6i;
-	struct neighbour *neigh;
-	ip_addr_t ecm_multicast_dst_addr, ecm_src_addr;
-	struct net *netf = dev_net(dev);
-	int ret;
-
-	char __attribute__((unused)) dst_addr_str[40];
-	char __attribute__((unused)) mc_dst_addr_str[40];
-	char __attribute__((unused)) src_addr_str[40];
-
-	/*
-	 * Find source and destination addresses in Linux format. We need
-	 * mcast destination address as well.
-	 */
-	ECM_IP_ADDR_TO_NIN6_ADDR(dst_addr, addr);
-	addrconf_addr_solict_mult(&dst_addr, &mc_dst_addr);
-	ret = ipv6_dev_get_saddr(netf, dev, &dst_addr, 0, &src_addr);
-
-	/*
-	 * IP address in string format for debug
-	 */
-	ecm_ip_addr_to_string(dst_addr_str, addr);
-	ECM_NIN6_ADDR_TO_IP_ADDR(ecm_multicast_dst_addr, mc_dst_addr);
-	ecm_ip_addr_to_string(mc_dst_addr_str, ecm_multicast_dst_addr);
-	ECM_NIN6_ADDR_TO_IP_ADDR(ecm_src_addr, src_addr);
-	ecm_ip_addr_to_string(src_addr_str, ecm_src_addr);
-
-	/*
-	 * Find the route entry
-	 */
-	rt6i = rt6_lookup(netf, &dst_addr, NULL, 0, 0);
-	if (!rt6i) {
-		DEBUG_TRACE("IPv6 Route lookup failure for destination IPv6 address %s\n", dst_addr_str);
-		return;
-		// GGG TODO Flatten
-	} else {
-		/*
-		 * Find the neighbor entry
-		 */
-#if (LINUX_VERSION_CODE <= KERNEL_VERSION(3,6,0))
-		neigh = rt6i->dst.ops->neigh_lookup(&rt6i->dst, &dst_addr);
-#else
-		neigh = rt6i->dst.ops->neigh_lookup(&rt6i->dst, NULL, &dst_addr);
-#endif
-		if (neigh == NULL) {
-			DEBUG_TRACE("Neighbour lookup failure for destination IPv6 address %s\n", dst_addr_str);
-			return;
-		} else {
-			/*
-			 * Issue a Neighbour soliciation request
-			 */
-			DEBUG_TRACE("Issue Neighbour solicitation request\n");
-			ndisc_send_ns(dev, neigh, &dst_addr, &mc_dst_addr, &src_addr);
-			neigh_release(neigh);
-		}
-	}
-}
-
-/*
- * ecm_front_end_ipv6_interface_heirarchy_construct()
- *	Construct an interface heirarchy.
- *
- * Using the given addressing, locate the interface heirarchy used to emit packets to that destination.
- * This is the heirarchy of interfaces a packet would transit to emit from the device.
- *
- * We will use the given src/dest devices when is_routed is false.
- * When is_routed is true we will try routing tables first, failing back to any given.
- *
- * For example, with this network arrangement:
- *
- * PPPoE--VLAN--BRIDGE--BRIDGE_PORT(LAG_MASTER)--LAG_SLAVE_0--10.22.33.11
- *
- * Given the IP address 10.22.33.11 this will create an interface heirarchy (in interracfes[]) of:
- * LAG_SLAVE_0 @ [ECM_DB_IFACE_HEIRARCHY_MAX - 5]
- * LAG_MASTER @ [ECM_DB_IFACE_HEIRARCHY_MAX - 4]
- * BRIDGE @ [ECM_DB_IFACE_HEIRARCHY_MAX - 3]
- * VLAN @ [ECM_DB_IFACE_HEIRARCHY_MAX - 2]
- * PPPOE @ [ECM_DB_IFACE_HEIRARCHY_MAX - 1]
- * The value returned is (ECM_DB_IFACE_HEIRARCHY_MAX - 5)
- *
- * IMPORTANT: This function will return any known interfaces in the database, when interfaces do not exist in the database
- * they will be created and added automatically to the database.
- *
- * GGG TODO Remove this in favour of the ecm_interface version - understand any refactoring needs first.
- */
-int32_t ecm_front_end_ipv6_interface_heirarchy_construct(struct ecm_db_iface_instance *interfaces[], ip_addr_t packet_src_addr, ip_addr_t packet_dest_addr, int packet_protocol,
-								struct net_device *given_dest_dev, bool is_routed, struct net_device *given_src_dev, uint8_t *dest_node_addr, uint8_t *src_node_addr)
-{
-	int protocol;
-	ip_addr_t src_addr;
-	ip_addr_t dest_addr;
-	struct net_device *dest_dev;
-	char *dest_dev_name;
-	int32_t dest_dev_type;
-	struct net_device *src_dev;
-	char *src_dev_name;
-	int32_t src_dev_type;
-	int32_t current_interface_index;
-	bool from_local_addr;
-
-	/*
-	 * Get a big endian of the IPv6 address we have been given as our starting point.
-	 */
-	protocol = packet_protocol;
-	ECM_IP_ADDR_COPY(src_addr, packet_src_addr);
-	ECM_IP_ADDR_COPY(dest_addr, packet_dest_addr);
-	DEBUG_TRACE("Construct interface heirarchy for from src_addr: " ECM_IP_ADDR_OCTAL_FMT " to dest_addr: " ECM_IP_ADDR_OCTAL_FMT ", protocol: %d\n",
-			ECM_IP_ADDR_TO_OCTAL(src_addr), ECM_IP_ADDR_TO_OCTAL(dest_addr), protocol);
-
-	/*
-	 * Get device to reach the given destination address.
-	 * If the heirarchy is for a routed connection we must try route lookup first, falling back to any given_dest_dev.
-	 * If the heirarchy is NOT for a routed connection we try the given_dest_dev first, followed by routed lookup.
-	 */
-	from_local_addr = false;
-	if (is_routed) {
-		dest_dev = ecm_interface_dev_find_by_addr(dest_addr, &from_local_addr);
-		if (!dest_dev && given_dest_dev) {
-			/*
-			 * Fall back to any given
-			 */
-			dest_dev = given_dest_dev;
-			dev_hold(dest_dev);
-		}
-	} else if (given_dest_dev) {
-		dest_dev = given_dest_dev;
-		dev_hold(dest_dev);
-	} else {
-		/*
-		 * Fall back to routed look up
-		 */
-		dest_dev = ecm_interface_dev_find_by_addr(dest_addr, &from_local_addr);
-	}
-
-	/*
-	 * GGG ALERT: If the address is a local address and protocol is an IP tunnel
-	 * then this connection is a tunnel endpoint made to this device.
-	 * In which case we circumvent all proper procedure and just hack the devices to make stuff work.
-	 * GGG TODO THIS MUST BE FIXED - WE MUST USE THE INTERFACE HIERARCHY FOR ITS INTENDED PURPOSE TO
-	 * PARSE THE DEVICES AND WORK OUT THE PROPER INTERFACES INVOLVED.
-	 * E.G. IF WE TRIED TO RUN A TUNNEL OVER A VLAN OR QINQ THIS WILL BREAK AS WE DON'T DISCOVER THAT HIERARCHY
-	 */
-	if (dest_dev && from_local_addr && (protocol == IPPROTO_IPIP)) {
-		dev_put(dest_dev);
-		dest_dev = given_dest_dev;
-		if (dest_dev) {
-			dev_hold(dest_dev);
-			DEBUG_TRACE("HACK: IPIP tunnel packet with dest_addr: " ECM_IP_ADDR_OCTAL_FMT " uses dev: %p(%s)\n", ECM_IP_ADDR_TO_OCTAL(dest_addr), dest_dev, dest_dev->name);
-		}
-	}
-	if (!dest_dev) {
-		DEBUG_WARN("dest_addr: " ECM_IP_ADDR_OCTAL_FMT " - cannot locate device\n", ECM_IP_ADDR_TO_OCTAL(dest_addr));
-		return ECM_DB_IFACE_HEIRARCHY_MAX;
-	}
-	dest_dev_name = dest_dev->name;
-	dest_dev_type = dest_dev->type;
-
-	/*
-	 * Get device to reach the given source address.
-	 * If the heirarchy is for a routed connection we must try route lookup first, falling back to any given_src_dev.
-	 * If the heirarchy is NOT for a routed connection we try the given_src_dev first, followed by routed lookup.
-	 */
-	from_local_addr = false;
-	if (is_routed) {
-		src_dev = ecm_interface_dev_find_by_addr(src_addr, &from_local_addr);
-		if (!src_dev && given_src_dev) {
-			/*
-			 * Fall back to any given
-			 */
-			src_dev = given_src_dev;
-			dev_hold(src_dev);
-		}
-	} else if (given_src_dev) {
-		src_dev = given_src_dev;
-		dev_hold(src_dev);
-	} else {
-		/*
-		 * Fall back to routed look up
-		 */
-		src_dev = ecm_interface_dev_find_by_addr(src_addr, &from_local_addr);
-	}
-
-	/*
-	 * GGG ALERT: If the address is a local address and protocol is an IP tunnel
-	 * then this connection is a tunnel endpoint made to this device.
-	 * In which case we circumvent all proper procedure and just hack the devices to make stuff work.
-	 * GGG TODO THIS MUST BE FIXED - WE MUST USE THE INTERFACE HIERARCHY FOR ITS INTENDED PURPOSE TO
-	 * PARSE THE DEVICES AND WORK OUT THE PROPER INTERFACES INVOLVED.
-	 * E.G. IF WE TRIED TO RUN A TUNNEL OVER A VLAN OR QINQ THIS WILL BREAK AS WE DON'T DISCOVER THAT HIERARCHY
-	 */
-	if (src_dev && from_local_addr && (protocol == IPPROTO_IPIP)) {
-		dev_put(src_dev);
-		src_dev = given_src_dev;
-		if (src_dev) {
-			dev_hold(src_dev);
-			DEBUG_TRACE("HACK: IPIP tunnel packet with src_addr: " ECM_IP_ADDR_OCTAL_FMT " uses dev: %p(%s)\n", ECM_IP_ADDR_TO_OCTAL(src_addr), src_dev, src_dev->name);
-		}
-	}
-	if (!src_dev) {
-		DEBUG_WARN("src_addr: " ECM_IP_ADDR_OCTAL_FMT " - cannot locate device\n", ECM_IP_ADDR_TO_OCTAL(src_addr));
-		dev_put(dest_dev);
-		return ECM_DB_IFACE_HEIRARCHY_MAX;
-	}
-	src_dev_name = src_dev->name;
-	src_dev_type = src_dev->type;
-
-	/*
-	 * Check if source and dest dev are same.
-	 * For the forwarded flows which involve tunnels this will happen when called from input hook.
-	 */
-	if (src_dev == dest_dev) {
-		DEBUG_TRACE("Protocol is :%d source dev and dest dev are same\n", protocol);
-		if (protocol == IPPROTO_IPIP) {
-			/*
-			 * This happens from the input hook
-			 * We do not want to create a connection entry for this
-			 * GGG TODO YES WE DO.
-			 * GGG TODO THIS CONCERNS ME AS THIS SHOULD BE CAUGHT MUCH
-			 * EARLIER IN THE FRONT END IF POSSIBLE TO AVOID PERFORMANCE PENALTIES.
-			 * WE HAVE DONE A TREMENDOUS AMOUT OF WORK TO GET TO THIS POINT.
-			 * WE WILL ABORT HERE AND THIS WILL BE REPEATED FOR EVERY PACKET.
-			 * IN KEEPING WITH THE ECM DESIGN IT IS BETTER TO CREATE A CONNECTION AND RECORD IN THE HIERARCHY
-			 * ENOUGH INFORMATION TO ENSURE THAT ACCELERATION IS NOT BROKEN / DOES NOT OCCUR AT ALL.
-			 * THAT WAY WE DO A HEAVYWEIGHT ESTABLISHING OF A CONNECTION ONCE AND NEVER AGAIN...
-			 */
-			dev_put(src_dev);
-			dev_put(dest_dev);
-			return ECM_DB_IFACE_HEIRARCHY_MAX;
-		}
-	}
-
-	/*
-	 * Iterate until we are done or get to the max number of interfaces we can record.
-	 * NOTE: current_interface_index tracks the position of the first interface position in interfaces[]
-	 * because we add from the end first_interface grows downwards.
-	 */
-	current_interface_index = ECM_DB_IFACE_HEIRARCHY_MAX;
-	while (current_interface_index > 0) {
-		struct ecm_db_iface_instance *ii;
-		struct net_device *next_dev;
-
-		/*
-		 * Get the ecm db interface instance for the device at hand
-		 */
-		ii = ecm_interface_establish_and_ref(dest_dev);
-
-		/*
-		 * If the interface could not be established then we abort
-		 */
-		if (!ii) {
-			DEBUG_WARN("Failed to establish interface: %p, name: %s\n", dest_dev, dest_dev_name);
-			dev_put(src_dev);
-			dev_put(dest_dev);
-
-			/*
-			 * Release the interfaces heirarchy we constructed to this point.
-			 */
-			ecm_db_connection_interfaces_deref(interfaces, current_interface_index);
-			return ECM_DB_IFACE_HEIRARCHY_MAX;
-		}
-
-		/*
-		 * Record the interface instance into the interfaces[]
-		 */
-		current_interface_index--;
-		interfaces[current_interface_index] = ii;
-
-		/*
-		 * Now we have to figure out what the next device will be (in the transmission path) the skb
-		 * will use to emit to the destination address.
-		 */
-		do {
-#ifdef ECM_INTERFACE_PPP_ENABLE
-			int channel_count;
-			struct ppp_channel *ppp_chan[1];
-			int channel_protocol;
-			struct pppoe_opt addressing;
-#endif
-
-			DEBUG_TRACE("Net device: %p is type: %d, name: %s\n", dest_dev, dest_dev_type, dest_dev_name);
-			next_dev = NULL;
-
-			if (dest_dev_type == ARPHRD_ETHER) {
-				/*
-				 * Ethernet - but what sub type?
-				 */
-
-#ifdef ECM_INTERFACE_VLAN_ENABLE
-				/*
-				 * VLAN?
-				 */
-				if (is_vlan_dev(dest_dev)) {
-					/*
-					 * VLAN master
-					 * No locking needed here, ASSUMPTION is that real_dev is held for as long as we have dev.
-					 */
-					next_dev = vlan_dev_priv(dest_dev)->real_dev;
-					dev_hold(next_dev);
-					DEBUG_TRACE("Net device: %p is VLAN, slave dev: %p (%s)\n",
-							dest_dev, next_dev, next_dev->name);
-					break;
-				}
-#endif
-
-				/*
-				 * BRIDGE?
-				 */
-				if (ecm_front_end_is_bridge_device(dest_dev)) {
-					/*
-					 * Bridge
-					 * Figure out which port device the skb will go to using the dest_addr.
-					 */
-					bool on_link;
-					ip_addr_t gw_addr;
-					uint8_t mac_addr[ETH_ALEN];
-					if (!ecm_interface_mac_addr_get(dest_addr, mac_addr, &on_link, gw_addr)) {
-						/*
-						 * Possible ARP does not know the address yet
-						 */
-						DEBUG_WARN("Unable to obtain MAC address for " ECM_IP_ADDR_OCTAL_FMT "\n", ECM_IP_ADDR_TO_OCTAL(dest_addr));
-						ecm_front_end_ipv6_send_neighbour_solicitation(dest_dev, dest_addr);
-						dev_put(src_dev);
-						dev_put(dest_dev);
-
-						/*
-						 * Release the interfaces heirarchy we constructed to this point.
-						 */
-						ecm_db_connection_interfaces_deref(interfaces, current_interface_index);
-						return ECM_DB_IFACE_HEIRARCHY_MAX;
-					}
-					next_dev = br_port_dev_get(dest_dev, mac_addr);
-					if (!next_dev) {
-						DEBUG_WARN("Unable to obtain output port for: %pM\n", mac_addr);
-						dev_put(src_dev);
-						dev_put(dest_dev);
-
-						/*
-						 * Release the interfaces heirarchy we constructed to this point.
-						 */
-						ecm_db_connection_interfaces_deref(interfaces, current_interface_index);
-						return ECM_DB_IFACE_HEIRARCHY_MAX;
-					}
-					DEBUG_TRACE("Net device: %p is BRIDGE, next_dev: %p (%s)\n", dest_dev, next_dev, next_dev->name);
-					break;
-				}
-#ifdef ECM_INTERFACE_BOND_ENABLE
-				/*
-				 * LAG?
-				 */
-				if (ecm_front_end_is_lag_master(dest_dev)) {
-					/*
-					 * Link aggregation
-					 * Figure out which slave device of the link aggregation will be used to reach the destination.
-					 */
-					bool dest_on_link = false;
-					ip_addr_t dest_gw_addr = ECM_IP_ADDR_NULL;
-					uint8_t src_mac_addr[ETH_ALEN];
-					uint8_t dest_mac_addr[ETH_ALEN];
-					struct net_device *master_dev = NULL;
-
-					memset(src_mac_addr, 0, ETH_ALEN);
-					memset(dest_mac_addr, 0, ETH_ALEN);
-
-					if (!is_routed) {
-						memcpy(src_mac_addr, src_node_addr, ETH_ALEN);
-						memcpy(dest_mac_addr, dest_node_addr, ETH_ALEN);
-					} else {
-						struct net_device *dest_dev_master;
-						/*
-						 * Use appropriate source MAC address for routed packets
-						 */
-						dest_dev_master = ecm_interface_get_and_hold_dev_master(dest_dev);
-						if (dest_dev_master) {
-							memcpy(src_mac_addr, dest_dev_master->dev_addr, ETH_ALEN);
-						} else {
-							memcpy(src_mac_addr, dest_dev->dev_addr, ETH_ALEN);
-						}
-
-						if (!ecm_interface_mac_addr_get(dest_addr, dest_mac_addr, &dest_on_link, dest_gw_addr)) {
-							/*
-							 * Possible ARP does not know the address yet
-							 */
-							DEBUG_WARN("Unable to obtain MAC address for " ECM_IP_ADDR_DOT_FMT "\n", ECM_IP_ADDR_TO_DOT(dest_addr));
-
-							/*
-							 * find proper interfce from which to issue neighbour solicitation
-							 */
-							if (dest_dev_master) {
-								master_dev = dest_dev_master;
-							} else {
-								master_dev = dest_dev;
-							}
-
-							dev_hold(master_dev);
-
-							if (dest_dev_master) {
-								dev_put(dest_dev_master);
-							}
-
-							ecm_front_end_ipv6_send_neighbour_solicitation(master_dev, dest_addr);
-							dev_put(src_dev);
-							dev_put(dest_dev);
-							dev_put(master_dev);
-
-							/*
-							 * Release the interfaces heirarchy we constructed to this point.
-							 */
-							ecm_db_connection_interfaces_deref(interfaces, current_interface_index);
-							return ECM_DB_IFACE_HEIRARCHY_MAX;
-						}
-
-						if (dest_dev_master) {
-							dev_put(dest_dev_master);
-						}
-					}
-
-					next_dev = bond_get_tx_dev(NULL, src_mac_addr, dest_mac_addr, src_addr, dest_addr, htons((uint16_t)ETH_P_IPV6), dest_dev);
-					if (next_dev && netif_carrier_ok(next_dev)) {
-						dev_hold(next_dev);
-					} else {
-						DEBUG_WARN("Unable to obtain LAG output slave device\n");
-						dev_put(src_dev);
-						dev_put(dest_dev);
-
-						/*
-						 * Release the interfaces heirarchy we constructed to this point.
-						 */
-						ecm_db_connection_interfaces_deref(interfaces, current_interface_index);
-						return ECM_DB_IFACE_HEIRARCHY_MAX;
-					}
-
-					DEBUG_TRACE("Net device: %p is LAG, slave dev: %p (%s)\n", dest_dev, next_dev, next_dev->name);
-
-					break;
-				}
-#endif
-
-				/*
-				 * ETHERNET!
-				 * Just plain ethernet it seems.
-				 */
-				DEBUG_TRACE("Net device: %p is ETHERNET\n", dest_dev);
-				break;
-			}
-
-			/*
-			 * LOOPBACK?
-			 */
-			if (dest_dev_type == ARPHRD_LOOPBACK) {
-				DEBUG_TRACE("Net device: %p is LOOPBACK type: %d\n", dest_dev, dest_dev_type);
-				break;
-			}
-
-			/*
-			 * IPSEC?
-			 */
-			if (dest_dev_type == ECM_ARPHRD_IPSEC_TUNNEL_TYPE) {
-				DEBUG_TRACE("Net device: %p is IPSec tunnel type: %d\n", dest_dev, dest_dev_type);
-				// GGG TODO Figure out the next device the tunnel is using...
-				break;
-			}
-
-			/*
-			 * SIT (6-in-4)?
-			 */
-			if (dest_dev_type == ARPHRD_SIT) {
-				DEBUG_TRACE("Net device: %p is SIT (6-in-4) type: %d\n", dest_dev, dest_dev_type);
-				// GGG TODO Figure out the next device the tunnel is using...
-				break;
-			}
-
-			/*
-			 * IPIP6 Tunnel?
-			 */
-			if (dest_dev_type == ARPHRD_TUNNEL6) {
-				DEBUG_TRACE("Net device: %p is TUNIPIP6 type: %d\n", dest_dev, dest_dev_type);
-				// GGG TODO Figure out the next device the tunnel is using...
-				break;
-			}
-
-			/*
-			 * If this is NOT PPP then it is unknown to the ecm and we cannot figure out it's next device.
-			 */
-			if (dest_dev_type != ARPHRD_PPP) {
-				DEBUG_TRACE("Net device: %p is UNKNOWN type: %d\n", dest_dev, dest_dev_type);
-				break;
-			}
-
-#ifndef ECM_INTERFACE_PPP_ENABLE
-			DEBUG_TRACE("Net device: %p is UNKNOWN (PPP Unsupported) type: %d\n", dest_dev, dest_dev_type);
-#else
-			/*
-			 * PPP - but what is the channel type?
-			 * First: If this is multi-link then we do not support it
-			 */
-			if (ppp_is_multilink(dest_dev) > 0) {
-				DEBUG_TRACE("Net device: %p is MULTILINK PPP - Unknown to the ECM\n", dest_dev);
-				break;
-			}
-
-			DEBUG_TRACE("Net device: %p is PPP\n", dest_dev);
-
-			/*
-			 * Get the PPP channel and then enquire what kind of channel it is
-			 * NOTE: Not multilink so only one channel to get.
-			 */
-			channel_count = ppp_hold_channels(dest_dev, ppp_chan, 1);
-			if (channel_count != 1) {
-				DEBUG_TRACE("Net device: %p PPP has %d channels - ECM cannot handle this (interface becomes Unknown type)\n",
-						dest_dev, channel_count);
-				break;
-			}
-
-			/*
-			 * Get channel protocol type
-			 * NOTE: Not all PPP channels support channel specific methods.
-			 */
-			channel_protocol = ppp_channel_get_protocol(ppp_chan[0]);
-			if (channel_protocol != PX_PROTO_OE) {
-				DEBUG_TRACE("Net device: %p PPP channel protocol: %d - Unknown to the ECM\n",
-						dest_dev, channel_protocol);
-
-				/*
-				 * Release the channel
-				 */
-				ppp_release_channels(ppp_chan, 1);
-
-				break;
-			}
-
-			/*
-			 * PPPoE channel
-			 */
-			DEBUG_TRACE("Net device: %p PPP channel is PPPoE\n", dest_dev);
-
-			/*
-			 * Get PPPoE session information and the underlying device it is using.
-			 */
-			pppoe_channel_addressing_get(ppp_chan[0], &addressing);
-
-			/*
-			 * Copy the dev hold into this, we will release the hold later
-			 */
-			next_dev = addressing.dev;
-
-			DEBUG_TRACE("Net device: %p, next device: %p (%s)\n", dest_dev, next_dev, next_dev->name);
-
-			/*
-			 * Release the channel.  Note that next_dev is still (correctly) held.
-			 */
-			ppp_release_channels(ppp_chan, 1);
-#endif
-		} while (false);
-
-		/*
-		 * No longer need dest_dev as it may become next_dev
-		 */
-		dev_put(dest_dev);
-
-		/*
-		 * Check out the next_dev, if any
-		 */
-		if (!next_dev) {
-			int32_t i __attribute__((unused));
-			DEBUG_INFO("Completed interface heirarchy construct with first interface @: %d\n", current_interface_index);
-#if DEBUG_LEVEL > 1
-			for (i = current_interface_index; i < ECM_DB_IFACE_HEIRARCHY_MAX; ++i) {
-				DEBUG_TRACE("\tInterface @ %d: %p, type: %d, name: %s\n",
-						i, interfaces[i], ecm_db_connection_iface_type_get(interfaces[i]),
-						ecm_db_interface_type_to_string(ecm_db_connection_iface_type_get(interfaces[i])));
-			}
-#endif
-			/*
-			 * Release src_dev now
-			 */
-			dev_put(src_dev);
-			return current_interface_index;
-		}
-
-		/*
-		 * dest_dev becomes next_dev
-		 */
-		dest_dev = next_dev;
-		dest_dev_name = dest_dev->name;
-		dest_dev_type = dest_dev->type;
-	}
-
-	DEBUG_WARN("Too many interfaces: %d\n", current_interface_index);
-	DEBUG_ASSERT(current_interface_index == 0, "Bad logic handling current_interface_index: %d\n", current_interface_index);
-	dev_put(src_dev);
-	dev_put(dest_dev);
-
-	/*
-	 * Release the interfaces heirarchy we constructed to this point.
-	 */
-	ecm_db_connection_interfaces_deref(interfaces, current_interface_index);
-	return ECM_DB_IFACE_HEIRARCHY_MAX;
-}
-
-/*
  * ecm_front_end_ipv6_node_establish_and_ref()
  *	Returns a reference to a node, possibly creating one if necessary.
  *
@@ -895,10 +288,10 @@
 					struct net_device *master;
 					master = ecm_interface_get_and_hold_dev_master(dev);
 					DEBUG_ASSERT(master, "Expected a master\n");
-					ecm_front_end_ipv6_send_neighbour_solicitation(master, addr);
+					ecm_interface_send_neighbour_solicitation(master, addr);
 					dev_put(master);
 				} else {
-					ecm_front_end_ipv6_send_neighbour_solicitation(dev, addr);
+					ecm_interface_send_neighbour_solicitation(dev, addr);
 				}
 				return NULL;
 			}
@@ -5874,7 +5267,7 @@
 	ecm_db_connection_to_node_address_get(ci, dest_node_addr);
 
 	DEBUG_TRACE("%p: Update the 'from' interface heirarchy list\n", ci);
-	from_list_first = ecm_front_end_ipv6_interface_heirarchy_construct(from_list, ip_dest_addr, ip_src_addr, protocol, in_dev, is_routed, in_dev, src_node_addr, dest_node_addr);
+	from_list_first = ecm_interface_heirarchy_construct(from_list, ip_dest_addr, ip_src_addr, 6, protocol, in_dev, is_routed, in_dev, src_node_addr, dest_node_addr);
 	if (from_list_first == ECM_DB_IFACE_HEIRARCHY_MAX) {
 		goto ecm_ipv6_retry_regen;
 	}
@@ -5883,7 +5276,7 @@
 	ecm_db_connection_interfaces_deref(from_list, from_list_first);
 
 	DEBUG_TRACE("%p: Update the 'to' interface heirarchy list\n", ci);
-	to_list_first = ecm_front_end_ipv6_interface_heirarchy_construct(to_list, ip_src_addr, ip_dest_addr, protocol, out_dev, is_routed, in_dev, dest_node_addr, src_node_addr);
+	to_list_first = ecm_interface_heirarchy_construct(to_list, ip_src_addr, ip_dest_addr, 6, protocol, out_dev, is_routed, in_dev, dest_node_addr, src_node_addr);
 	if (to_list_first == ECM_DB_IFACE_HEIRARCHY_MAX) {
 		goto ecm_ipv6_retry_regen;
 	}
@@ -6112,7 +5505,7 @@
 		 * GGG TODO The empty list checks should not be needed, mapping_establish_and_ref() should fail out if there is no list anyway.
 		 */
 		DEBUG_TRACE("%p: Create the 'from' interface heirarchy list\n", nci);
-		from_list_first = ecm_front_end_ipv6_interface_heirarchy_construct(from_list, ip_dest_addr, ip_src_addr, IPPROTO_TCP, in_dev, is_routed, in_dev, src_node_addr, dest_node_addr);
+		from_list_first = ecm_interface_heirarchy_construct(from_list, ip_dest_addr, ip_src_addr, 6, IPPROTO_TCP, in_dev, is_routed, in_dev, src_node_addr, dest_node_addr);
 		if (from_list_first == ECM_DB_IFACE_HEIRARCHY_MAX) {
 			ecm_db_connection_deref(nci);
 			DEBUG_WARN("Failed to obtain 'from' heirarchy list\n");
@@ -6139,7 +5532,7 @@
 		}
 
 		DEBUG_TRACE("%p: Create the 'to' interface heirarchy list\n", nci);
-		to_list_first = ecm_front_end_ipv6_interface_heirarchy_construct(to_list, ip_src_addr, ip_dest_addr, IPPROTO_TCP, out_dev, is_routed, in_dev, dest_node_addr, src_node_addr);
+		to_list_first = ecm_interface_heirarchy_construct(to_list, ip_src_addr, ip_dest_addr, 6, IPPROTO_TCP, out_dev, is_routed, in_dev, dest_node_addr, src_node_addr);
 		if (to_list_first == ECM_DB_IFACE_HEIRARCHY_MAX) {
 			ecm_db_mapping_deref(src_mi);
 			ecm_db_node_deref(src_ni);
@@ -6639,7 +6032,7 @@
 		 * GGG TODO The empty list checks should not be needed, mapping_establish_and_ref() should fail out if there is no list anyway.
 		 */
 		DEBUG_TRACE("%p: Create the 'from' interface heirarchy list\n", nci);
-		from_list_first = ecm_front_end_ipv6_interface_heirarchy_construct(from_list, ip_dest_addr, ip_src_addr, IPPROTO_UDP, in_dev, is_routed, in_dev, src_node_addr, dest_node_addr);
+		from_list_first = ecm_interface_heirarchy_construct(from_list, ip_dest_addr, ip_src_addr, 6, IPPROTO_UDP, in_dev, is_routed, in_dev, src_node_addr, dest_node_addr);
 		if (from_list_first == ECM_DB_IFACE_HEIRARCHY_MAX) {
 			ecm_db_connection_deref(nci);
 			DEBUG_WARN("Failed to obtain 'from' heirarchy list\n");
@@ -6666,7 +6059,7 @@
 		}
 
 		DEBUG_TRACE("%p: Create the 'to' interface heirarchy list\n", nci);
-		to_list_first = ecm_front_end_ipv6_interface_heirarchy_construct(to_list, ip_src_addr, ip_dest_addr, IPPROTO_UDP, out_dev, is_routed, in_dev, dest_node_addr, src_node_addr);
+		to_list_first = ecm_interface_heirarchy_construct(to_list, ip_src_addr, ip_dest_addr, 6, IPPROTO_UDP, out_dev, is_routed, in_dev, dest_node_addr, src_node_addr);
 		if (to_list_first == ECM_DB_IFACE_HEIRARCHY_MAX) {
 			ecm_db_mapping_deref(src_mi);
 			ecm_db_node_deref(src_ni);
@@ -7122,7 +6515,7 @@
 		 * GGG TODO The empty list checks should not be needed, mapping_establish_and_ref() should fail out if there is no list anyway.
 		 */
 		DEBUG_TRACE("%p: Create the 'from' interface heirarchy list\n", nci);
-		from_list_first = ecm_front_end_ipv6_interface_heirarchy_construct(from_list, ip_dest_addr, ip_src_addr, protocol, in_dev, is_routed, in_dev, src_node_addr, dest_node_addr);
+		from_list_first = ecm_interface_heirarchy_construct(from_list, ip_dest_addr, ip_src_addr, 6, protocol, in_dev, is_routed, in_dev, src_node_addr, dest_node_addr);
 		if (from_list_first == ECM_DB_IFACE_HEIRARCHY_MAX) {
 			ecm_db_connection_deref(nci);
 			DEBUG_WARN("Failed to obtain 'from' heirarchy list\n");
@@ -7149,7 +6542,7 @@
 		}
 
 		DEBUG_TRACE("%p: Create the 'to' interface heirarchy list\n", nci);
-		to_list_first = ecm_front_end_ipv6_interface_heirarchy_construct(to_list, ip_src_addr, ip_dest_addr, protocol, out_dev, is_routed, in_dev, dest_node_addr, src_node_addr);
+		to_list_first = ecm_interface_heirarchy_construct(to_list, ip_src_addr, ip_dest_addr, 6, protocol, out_dev, is_routed, in_dev, dest_node_addr, src_node_addr);
 		if (to_list_first == ECM_DB_IFACE_HEIRARCHY_MAX) {
 			ecm_db_mapping_deref(src_mi);
 			ecm_db_node_deref(src_ni);
@@ -8824,7 +8217,7 @@
 		 * GGG TODO The empty list checks should not be needed, mapping_establish_and_ref() should fail out if there is no list anyway.
 		 */
 		DEBUG_TRACE("%p: Create the 'from' interface heirarchy list\n", nci);
-		from_list_first = ecm_front_end_ipv6_interface_heirarchy_construct(from_list, ip_dest_addr, ip_src_addr, IPPROTO_UDP, in_dev, is_routed, in_dev, src_node_addr, dest_node_addr);
+		from_list_first = ecm_interface_heirarchy_construct(from_list, ip_dest_addr, ip_src_addr, 6, IPPROTO_UDP, in_dev, is_routed, in_dev, src_node_addr, dest_node_addr);
 		if (from_list_first == ECM_DB_IFACE_HEIRARCHY_MAX) {
 			ecm_db_connection_deref(nci);
 			DEBUG_WARN("Failed to obtain 'from' heirarchy list\n");
diff --git a/frontends/nss/ecm_nss_ipv4.c b/frontends/nss/ecm_nss_ipv4.c
index 24b58ff..4ee3d8e 100644
--- a/frontends/nss/ecm_nss_ipv4.c
+++ b/frontends/nss/ecm_nss_ipv4.c
@@ -237,34 +237,15 @@
 		case ECM_DB_IFACE_TYPE_BRIDGE:
 		case ECM_DB_IFACE_TYPE_IPSEC_TUNNEL:
 			if (!ecm_interface_mac_addr_get(addr, node_addr, &on_link, gw_addr)) {
-				__be32 ipv4_addr;
-				__be32 src_ip;
 				DEBUG_TRACE("failed to obtain node address for host " ECM_IP_ADDR_DOT_FMT "\n", ECM_IP_ADDR_TO_DOT(addr));
-				/*
-				 * Issue an ARP request for it, select the src_ip from which to issue the request.
-				 */
-				ECM_IP_ADDR_TO_NIN4_ADDR(ipv4_addr, addr);
-				src_ip = inet_select_addr(dev, ipv4_addr, RT_SCOPE_LINK);
-				if (!src_ip) {
-					DEBUG_TRACE("failed to lookup IP for %pI4\n", &ipv4_addr);
-					return NULL;
-				}
-
-				/*
-				 * If we have a GW for this address, then we have to send ARP request to the GW
-				 */
-				if (!on_link && !ECM_IP_ADDR_IS_NULL(gw_addr)) {
-					ECM_IP_ADDR_TO_NIN4_ADDR(ipv4_addr, gw_addr);
-				}
-
-				DEBUG_TRACE("Send ARP for %pI4 using src_ip as %pI4\n", &ipv4_addr, &src_ip);
-				arp_send(ARPOP_REQUEST, ETH_P_ARP, ipv4_addr, dev, src_ip, NULL, NULL, NULL);
+				ecm_interface_send_arp_request(dev, addr, on_link, gw_addr);
 
 				/*
 				 * Unable to get node address at this time.
 				 */
 				return NULL;
 			}
+
 			if (is_multicast_ether_addr(node_addr)) {
 				DEBUG_TRACE("multicast node address for host " ECM_IP_ADDR_DOT_FMT ", node_addr: %pM\n", ECM_IP_ADDR_TO_DOT(addr), node_addr);
 				return NULL;
@@ -732,7 +713,7 @@
 
 
 	DEBUG_TRACE("%p: Update the 'from' interface heirarchy list\n", ci);
-	from_list_first = ecm_interface_heirarchy_construct(from_list, ip_dest_addr, ip_src_addr, protocol, in_dev, is_routed, in_dev, src_node_addr, dest_node_addr);
+	from_list_first = ecm_interface_heirarchy_construct(from_list, ip_dest_addr, ip_src_addr, 4, protocol, in_dev, is_routed, in_dev, src_node_addr, dest_node_addr);
 	if (from_list_first == ECM_DB_IFACE_HEIRARCHY_MAX) {
 		goto ecm_ipv4_retry_regen;
 	}
@@ -741,7 +722,7 @@
 	ecm_db_connection_interfaces_deref(from_list, from_list_first);
 
 	DEBUG_TRACE("%p: Update the 'from NAT' interface heirarchy list\n", ci);
-	from_nat_list_first = ecm_interface_heirarchy_construct(from_nat_list, ip_dest_addr, ip_src_addr_nat, protocol, in_dev_nat, is_routed, in_dev_nat, src_node_addr_nat, dest_node_addr_nat);
+	from_nat_list_first = ecm_interface_heirarchy_construct(from_nat_list, ip_dest_addr, ip_src_addr_nat, 4, protocol, in_dev_nat, is_routed, in_dev_nat, src_node_addr_nat, dest_node_addr_nat);
 	if (from_nat_list_first == ECM_DB_IFACE_HEIRARCHY_MAX) {
 		goto ecm_ipv4_retry_regen;
 	}
@@ -750,7 +731,7 @@
 	ecm_db_connection_interfaces_deref(from_nat_list, from_nat_list_first);
 
 	DEBUG_TRACE("%p: Update the 'to' interface heirarchy list\n", ci);
-	to_list_first = ecm_interface_heirarchy_construct(to_list, ip_src_addr, ip_dest_addr, protocol, out_dev, is_routed, in_dev, dest_node_addr, src_node_addr);
+	to_list_first = ecm_interface_heirarchy_construct(to_list, ip_src_addr, ip_dest_addr, 4, protocol, out_dev, is_routed, in_dev, dest_node_addr, src_node_addr);
 	if (to_list_first == ECM_DB_IFACE_HEIRARCHY_MAX) {
 		goto ecm_ipv4_retry_regen;
 	}
@@ -759,7 +740,7 @@
 	ecm_db_connection_interfaces_deref(to_list, to_list_first);
 
 	DEBUG_TRACE("%p: Update the 'to NAT' interface heirarchy list\n", ci);
-	to_nat_list_first = ecm_interface_heirarchy_construct(to_nat_list, ip_src_addr, ip_dest_addr_nat, protocol, out_dev_nat, is_routed, in_dev, dest_node_addr_nat, src_node_addr_nat);
+	to_nat_list_first = ecm_interface_heirarchy_construct(to_nat_list, ip_src_addr, ip_dest_addr_nat, 4, protocol, out_dev_nat, is_routed, in_dev, dest_node_addr_nat, src_node_addr_nat);
 	if (to_nat_list_first == ECM_DB_IFACE_HEIRARCHY_MAX) {
 		goto ecm_ipv4_retry_regen;
 	}
diff --git a/frontends/nss/ecm_nss_multicast_ipv4.c b/frontends/nss/ecm_nss_multicast_ipv4.c
index afdace0..60d0127 100644
--- a/frontends/nss/ecm_nss_multicast_ipv4.c
+++ b/frontends/nss/ecm_nss_multicast_ipv4.c
@@ -2161,7 +2161,7 @@
 		 * For this we also need the interface lists which we also set upon the new connection while we are at it.
 		 */
 		DEBUG_TRACE("%p: Create the 'from' interface heirarchy list\n", nci);
-		from_list_first = ecm_interface_heirarchy_construct(from_list, ip_dest_addr, ip_src_addr, protocol, in_dev, is_routed, in_dev, src_node_addr, dest_node_addr);
+		from_list_first = ecm_interface_heirarchy_construct(from_list, ip_dest_addr, ip_src_addr, 4, protocol, in_dev, is_routed, in_dev, src_node_addr, dest_node_addr);
 		if (from_list_first == ECM_DB_IFACE_HEIRARCHY_MAX) {
 			ecm_db_connection_deref(nci);
 			DEBUG_WARN("Failed to obtain 'from' heirarchy list\n");
@@ -2264,7 +2264,7 @@
 		}
 
 		DEBUG_TRACE("%p: Create the 'from NAT' interface heirarchy list\n", nci);
-		from_nat_list_first = ecm_interface_heirarchy_construct(from_nat_list, ip_dest_addr, ip_src_addr_nat, protocol, in_dev_nat, is_routed, in_dev_nat, src_node_addr, dest_node_addr);
+		from_nat_list_first = ecm_interface_heirarchy_construct(from_nat_list, ip_dest_addr, ip_src_addr_nat, 4, protocol, in_dev_nat, is_routed, in_dev_nat, src_node_addr, dest_node_addr);
 		if (from_nat_list_first == ECM_DB_IFACE_HEIRARCHY_MAX) {
 			for (vif = 0; vif < ECM_DB_MULTICAST_IF_MAX; vif++) {
 				to_list_single = ecm_db_multicast_if_heirarchy_get(to_list, vif);
diff --git a/frontends/nss/ecm_nss_non_ported_ipv4.c b/frontends/nss/ecm_nss_non_ported_ipv4.c
index f6b0427..eacd916 100644
--- a/frontends/nss/ecm_nss_non_ported_ipv4.c
+++ b/frontends/nss/ecm_nss_non_ported_ipv4.c
@@ -1822,7 +1822,7 @@
 		 * GGG TODO The empty list checks should not be needed, mapping_establish_and_ref() should fail out if there is no list anyway.
 		 */
 		DEBUG_TRACE("%p: Create the 'from' interface heirarchy list\n", nci);
-		from_list_first = ecm_interface_heirarchy_construct(from_list, ip_dest_addr, ip_src_addr, protocol, in_dev, is_routed, in_dev, src_node_addr, dest_node_addr);
+		from_list_first = ecm_interface_heirarchy_construct(from_list, ip_dest_addr, ip_src_addr, 4, protocol, in_dev, is_routed, in_dev, src_node_addr, dest_node_addr);
 		if (from_list_first == ECM_DB_IFACE_HEIRARCHY_MAX) {
 			ecm_db_connection_deref(nci);
 			DEBUG_WARN("Failed to obtain 'from' heirarchy list\n");
@@ -1849,7 +1849,7 @@
 		}
 
 		DEBUG_TRACE("%p: Create the 'to' interface heirarchy list\n", nci);
-		to_list_first = ecm_interface_heirarchy_construct(to_list, ip_src_addr, ip_dest_addr, protocol, out_dev, is_routed, in_dev, dest_node_addr, src_node_addr);
+		to_list_first = ecm_interface_heirarchy_construct(to_list, ip_src_addr, ip_dest_addr, 4, protocol, out_dev, is_routed, in_dev, dest_node_addr, src_node_addr);
 		if (to_list_first == ECM_DB_IFACE_HEIRARCHY_MAX) {
 			ecm_db_mapping_deref(src_mi);
 			ecm_db_node_deref(src_ni);
@@ -1893,9 +1893,9 @@
 		 */
 		DEBUG_TRACE("%p: Create the 'from NAT' interface heirarchy list\n", nci);
 		if ((protocol == IPPROTO_IPV6) || (protocol == IPPROTO_ESP)) {
-			from_nat_list_first = ecm_interface_heirarchy_construct(from_nat_list, ip_dest_addr, ip_src_addr_nat, protocol, in_dev, is_routed, in_dev, src_node_addr_nat, dest_node_addr_nat);
+			from_nat_list_first = ecm_interface_heirarchy_construct(from_nat_list, ip_dest_addr, ip_src_addr_nat, 4, protocol, in_dev, is_routed, in_dev, src_node_addr_nat, dest_node_addr_nat);
 		} else {
-			from_nat_list_first = ecm_interface_heirarchy_construct(from_nat_list, ip_dest_addr, ip_src_addr_nat, protocol, in_dev_nat, is_routed, in_dev, src_node_addr_nat, dest_node_addr_nat);
+			from_nat_list_first = ecm_interface_heirarchy_construct(from_nat_list, ip_dest_addr, ip_src_addr_nat, 4, protocol, in_dev_nat, is_routed, in_dev, src_node_addr_nat, dest_node_addr_nat);
 		}
 
 		if (from_nat_list_first == ECM_DB_IFACE_HEIRARCHY_MAX) {
@@ -1935,7 +1935,7 @@
 		}
 
 		DEBUG_TRACE("%p: Create the 'to NAT' interface heirarchy list\n", nci);
-		to_nat_list_first = ecm_interface_heirarchy_construct(to_nat_list, ip_src_addr, ip_dest_addr_nat, protocol, out_dev_nat, is_routed, in_dev, dest_node_addr_nat, src_node_addr_nat);
+		to_nat_list_first = ecm_interface_heirarchy_construct(to_nat_list, ip_src_addr, ip_dest_addr_nat, 4, protocol, out_dev_nat, is_routed, in_dev, dest_node_addr_nat, src_node_addr_nat);
 		if (to_nat_list_first == ECM_DB_IFACE_HEIRARCHY_MAX) {
 			ecm_db_mapping_deref(src_nat_mi);
 			ecm_db_node_deref(src_nat_ni);
diff --git a/frontends/nss/ecm_nss_ported_ipv4.c b/frontends/nss/ecm_nss_ported_ipv4.c
index 0b19772..13dc1f1 100644
--- a/frontends/nss/ecm_nss_ported_ipv4.c
+++ b/frontends/nss/ecm_nss_ported_ipv4.c
@@ -1964,7 +1964,7 @@
 		 * GGG TODO The empty list checks should not be needed, mapping_establish_and_ref() should fail out if there is no list anyway.
 		 */
 		DEBUG_TRACE("%p: Create the 'from' interface heirarchy list\n", nci);
-		from_list_first = ecm_interface_heirarchy_construct(from_list, ip_dest_addr, ip_src_addr, protocol, in_dev, is_routed, in_dev, src_node_addr, dest_node_addr);
+		from_list_first = ecm_interface_heirarchy_construct(from_list, ip_dest_addr, ip_src_addr, 4, protocol, in_dev, is_routed, in_dev, src_node_addr, dest_node_addr);
 		if (from_list_first == ECM_DB_IFACE_HEIRARCHY_MAX) {
 			ecm_db_connection_deref(nci);
 			DEBUG_WARN("Failed to obtain 'from' heirarchy list\n");
@@ -1991,7 +1991,7 @@
 		}
 
 		DEBUG_TRACE("%p: Create the 'to' interface heirarchy list\n", nci);
-		to_list_first = ecm_interface_heirarchy_construct(to_list, ip_src_addr, ip_dest_addr, protocol, out_dev, is_routed, in_dev, dest_node_addr, src_node_addr);
+		to_list_first = ecm_interface_heirarchy_construct(to_list, ip_src_addr, ip_dest_addr, 4, protocol, out_dev, is_routed, in_dev, dest_node_addr, src_node_addr);
 		if (to_list_first == ECM_DB_IFACE_HEIRARCHY_MAX) {
 			ecm_db_mapping_deref(src_mi);
 			ecm_db_node_deref(src_ni);
@@ -2030,7 +2030,7 @@
 		 * GGG TODO The empty list checks should not be needed, mapping_establish_and_ref() should fail out if there is no list anyway.
 		 */
 		DEBUG_TRACE("%p: Create the 'from NAT' interface heirarchy list\n", nci);
-		from_nat_list_first = ecm_interface_heirarchy_construct(from_nat_list, ip_dest_addr, ip_src_addr_nat, protocol, in_dev_nat, is_routed, in_dev_nat, src_node_addr_nat, dest_node_addr_nat);
+		from_nat_list_first = ecm_interface_heirarchy_construct(from_nat_list, ip_dest_addr, ip_src_addr_nat, 4, protocol, in_dev_nat, is_routed, in_dev_nat, src_node_addr_nat, dest_node_addr_nat);
 		if (from_nat_list_first == ECM_DB_IFACE_HEIRARCHY_MAX) {
 			ecm_db_mapping_deref(dest_mi);
 			ecm_db_node_deref(dest_ni);
@@ -2068,7 +2068,7 @@
 		}
 
 		DEBUG_TRACE("%p: Create the 'to NAT' interface heirarchy list\n", nci);
-		to_nat_list_first = ecm_interface_heirarchy_construct(to_nat_list, ip_src_addr, ip_dest_addr_nat, protocol, out_dev_nat, is_routed, in_dev, dest_node_addr_nat, src_node_addr_nat);
+		to_nat_list_first = ecm_interface_heirarchy_construct(to_nat_list, ip_src_addr, ip_dest_addr_nat, 4, protocol, out_dev_nat, is_routed, in_dev, dest_node_addr_nat, src_node_addr_nat);
 		if (to_nat_list_first == ECM_DB_IFACE_HEIRARCHY_MAX) {
 			ecm_db_mapping_deref(src_nat_mi);
 			ecm_db_node_deref(src_nat_ni);