[ipq806x] Fix for mac address look up issue

Add support to look up in neighbour discovery table for neighbour entry,
if not found, look in route table

CRs-Fixed: 543364

Change-Id: I1445efe80ec096685556ace3802186610d5399f4
Signed-off-by: Pamidipati, Vijay <vpamidip@codeaurora.org>
Reviewed-by: Abhishek Rastogi <arastogi@codeaurora.org>
diff --git a/nss_connmgr_ipv4.c b/nss_connmgr_ipv4.c
index 42d7749..c1b0d05 100755
--- a/nss_connmgr_ipv4.c
+++ b/nss_connmgr_ipv4.c
@@ -61,6 +61,7 @@
 #include <net/netfilter/ipv4/nf_defrag_ipv4.h>
 
 #include <net/arp.h>
+#include <net/neighbour.h>
 
 #include "nss_api_if.h"
 #include <linux/../../net/8021q/vlan.h>
@@ -273,8 +274,12 @@
 	int32_t  dest_port;		/* Non-NAT destination port */
 	uint32_t dest_addr_xlate;	/* NAT translated destination address, i.e. the to whom the connection was created */
 	int32_t  dest_port_xlate;	/* NAT translated destination port */
+	struct neighbour *src_neigh;
+					/* Neighbour reference of source */
+	struct neighbour *dest_neigh;
+					/* Neighbour reference of dest */
 	uint64_t stats[NSS_CONNMGR_IPV4_STATS_MAX];
-					/* Connection statistics */
+	/* Connection statistics */
 	uint32_t last_sync;		/* Last sync time as jiffies */
 };
 
@@ -287,7 +292,7 @@
 	int32_t stopped;		/* General operational control.When non-zero further traffic will not be processed */
 	int32_t terminate;		/* Signal to tell the control thread to terminate */
 	struct task_struct *thread;
-					/* Control thread */
+	/* Control thread */
 	void *nss_context;		/* Registration context used to identify the manager in calls to the NSS driver */
 	struct nf_ct_event_notifier conntrack_notifier;
 					/* NF conntrack event system to monitor connection tracking changes */
@@ -405,15 +410,11 @@
  */
 
 /*
- * nss_connmgr_ipv4_mac_addr_get()
- *	Return the hardware (MAC) address of the given IPv4 address, if any.
+ * nss_connmgr_ipv4_neigh_get()
+ * 	Returns neighbour reference for a given IP address
  *
- * Returns 0 on success or a negative result on failure.
- * We look up the rtable entry for the address and,
- * from its neighbour structure,obtain the hardware address.
- * This means we will also work if the neighbours are routers too.
  */
-static int nss_connmgr_ipv4_mac_addr_get(ipv4_addr_t addr, mac_addr_t mac_addr)
+static struct neighbour *nss_connmgr_ipv4_neigh_get(ipv4_addr_t addr)
 {
 	struct neighbour *neigh;
 	struct rtable *rt;
@@ -426,40 +427,68 @@
 	 */
 	rt = ip_route_output(&init_net, addr, 0, 0, 0);
 	if (IS_ERR(rt)) {
-		return -1;
+		return NULL;
 	}
 
 	dst = (struct dst_entry *)rt;
 
-	rcu_read_lock();
 	neigh = dst_get_neighbour_noref(dst);
+
 	if (!neigh) {
-		rcu_read_unlock();
-		dst_release(dst);
-		return -2;
+		neigh = neigh_lookup(&arp_tbl, &addr, dst->dev);
 	}
-	if (!(neigh->nud_state & NUD_VALID)) {
-		rcu_read_unlock();
-		dst_release(dst);
-		return -3;
-	}
-	if (!neigh->dev) {
-		rcu_read_unlock();
-		dst_release(dst);
-		return -4;
-	}
-	memcpy(mac_addr, neigh->ha, (size_t)neigh->dev->addr_len);
-	rcu_read_unlock();
 
 	dst_release(dst);
 
+	return neigh;
+}
+
+/*
+ * nss_connmgr_ipv4_mac_addr_get()
+ *	Return the hardware (MAC) address of the given IPv4 address, if any.
+ *
+ * Returns 0 on success or a negative result on failure.
+ * We look up the rtable entry for the address and,
+ * from its neighbour structure,obtain the hardware address.
+ * This means we will also work if the neighbours are routers too.
+ */
+static int nss_connmgr_ipv4_mac_addr_get(ipv4_addr_t addr, mac_addr_t mac_addr)
+{
+	struct neighbour *neigh;
+
+	rcu_read_lock();
+
+	neigh = nss_connmgr_ipv4_neigh_get(addr);
+
+	if (!neigh) {
+		rcu_read_unlock();
+		NSS_CONNMGR_DEBUG_WARN("Error: No neigh reference \n");
+		return -1;
+	}
+
+	if (!(neigh->nud_state & NUD_VALID)) {
+		rcu_read_unlock();
+		NSS_CONNMGR_DEBUG_WARN("NUD Invalid \n");
+		return -2;
+	}
+
+	if (!neigh->dev) {
+		rcu_read_unlock();
+		NSS_CONNMGR_DEBUG_WARN("Neigh Dev Invalid \n");
+		return -3;
+	}
+
+	memcpy(mac_addr, neigh->ha, (size_t)neigh->dev->addr_len);
+
+	rcu_read_unlock();
+
 	/*
 	 * If this mac looks like a multicast then it MAY be either truly multicast or it could be broadcast
 	 * Either way we fail!  We don't want to deal with multicasts or any sort because the NSS cannot deal with them.
 	 */
 	if (is_multicast_ether_addr(mac_addr)) {
 		NSS_CONNMGR_DEBUG_TRACE("Mac is multicast / broadcast - ignoring\n");
-		return -5;
+		return -4;
 	}
 
 	return 0;
@@ -469,6 +498,7 @@
 {
 	struct rtable *rt;
 	struct dst_entry *dst;
+	struct net_device *dev;
 
 	rt = ip_route_output(&init_net, addr, 0, 0, 0);
 	if (IS_ERR(rt)) {
@@ -477,8 +507,10 @@
 	}
 
 	dst = (struct dst_entry *)rt;
+	dev = dst->dev;
+	dst_release(dst);
 
-	return dst->dev;
+	return dev;
 }
 
 /*
@@ -1236,6 +1268,14 @@
 	}
 
 	/*
+	 * If egress interface is a bridge,ignore; the rule will be added by bridge post-routing hook
+	 */
+	if (is_bridge_device(out)) {
+		NSS_CONNMGR_DEBUG_TRACE("ignoring bridge device in post-routing hook: %p\n", skb);
+		goto out;
+	}
+
+	/*
 	 * Only process packets that are also tracked by conntrack
 	 */
 	ct = nf_ct_get(skb, &ctinfo);
@@ -1764,17 +1804,14 @@
 	struct nf_conn *ct;
 	struct nf_conn_counter *acct;
 	struct nss_connmgr_ipv4_connection *connection;
-	struct neighbour *neigh;
 
 	struct nss_ipv4_sync *sync;
 	struct nss_ipv4_establish *establish;
 
+	struct neighbour *neigh;
 	struct net_device *flow_dev;
 	struct net_device *return_dev;
 
-	uint32_t arp_key;
-
-
 	switch (nicp->reason)
 	{
 		case NSS_IPV4_CB_REASON_ESTABLISH:
@@ -1823,6 +1860,18 @@
 			connection->dest_addr_xlate = establish->return_ip_xlate;
 			connection->dest_port_xlate = establish->return_ident_xlate;
 
+			connection->src_neigh = nss_connmgr_ipv4_neigh_get(ntohl(connection->src_addr));
+
+			if (connection->src_neigh) {
+				neigh_hold(connection->src_neigh);
+			}
+
+			connection->dest_neigh = nss_connmgr_ipv4_neigh_get(ntohl(connection->dest_addr));
+
+			if (connection->dest_neigh) {
+				neigh_hold(connection->dest_neigh);
+			}
+
 			memset(connection->stats, 0, (8*NSS_CONNMGR_IPV4_STATS_MAX));
 
 			spin_unlock_bh(&nss_connmgr_ipv4.lock);
@@ -1863,6 +1912,14 @@
 					connection->state = NSS_CONNMGR_IPV4_STATE_INACTIVE;
 					nss_connmgr_ipv4.debug_stats[NSS_CONNMGR_IPV4_ACTIVE_CONN]--;
 					spin_unlock_bh(&nss_connmgr_ipv4.lock);
+
+					if (connection->src_neigh) {
+						neigh_release(connection->src_neigh);
+					}
+
+					if (connection->dest_neigh) {
+						neigh_release(connection->dest_neigh);
+					}
 					/* Fall through to increment stats */
 
 				case NSS_IPV4_SYNC_REASON_STATS:
@@ -1994,35 +2051,16 @@
 		return_dev = return_dev->master;
 	}
 
-	/*
-	 * Hold the net_device references
-	 */
-	dev_hold(flow_dev);
-	dev_hold(return_dev);
-
-	/*
-	 * Update ARP table.
-	 */
-	arp_key = ntohl(connection->src_addr);
-	neigh = neigh_lookup(&arp_tbl, &arp_key, flow_dev);
+	neigh = connection->src_neigh;
 	if (neigh) {
 		neigh_update(neigh, NULL, neigh->nud_state, NEIGH_UPDATE_F_WEAK_OVERRIDE);
-		neigh_release(neigh);
 	}
 
-	arp_key = ntohl(connection->dest_addr);
-	neigh = neigh_lookup(&arp_tbl, &arp_key, return_dev);
+	neigh = connection->dest_neigh;
 	if (neigh) {
 		neigh_update(neigh, NULL, neigh->nud_state, NEIGH_UPDATE_F_WEAK_OVERRIDE);
-		neigh_release(neigh);
 	}
 
-	/*
-	 * Release the net_device references
-	 */
-	dev_put(flow_dev);
-	dev_put(return_dev);
-
 out:
 	/*
 	 * Release connection
diff --git a/nss_connmgr_ipv6.c b/nss_connmgr_ipv6.c
index e79d4cf..2379b16 100755
--- a/nss_connmgr_ipv6.c
+++ b/nss_connmgr_ipv6.c
@@ -63,6 +63,7 @@
 #include <net/netfilter/ipv6/nf_defrag_ipv6.h>
 
 #include <net/arp.h>
+#include <net/neighbour.h>
 
 #include "nss_api_if.h"
 #include <linux/../../net/8021q/vlan.h>
@@ -278,6 +279,10 @@
 	uint64_t stats[NSS_CONNMGR_IPV6_STATS_MAX];
 				/* Connection statistics */
 	uint32_t last_sync;	/* Last sync time as jiffies */
+	struct neighbour *src_neigh;
+				/* Ingress routed interface */
+	struct neighbour *dest_neigh;
+				/* Egress routed interface */
 };
 
 /*
@@ -404,29 +409,21 @@
  * NOTE: IPx is considered to be IP addressing, protocol and port information combined.
  */
 
-/*
- * nss_connmgr_ipv6_mac_addr_get()
- *	Return the hardware (MAC) address of the given ipv6 address, if any.
- *
- * Returns 0 on success or a negative result on failure.
- * We look up the rtable entry for the address and, from its neighbour structure, obtain the hardware address.
- * This means we will also work if the neighbours are routers too.
- */
-static int nss_connmgr_ipv6_mac_addr_get(struct net *net, int oif, ipv6_addr_t addr, mac_addr_t mac_addr)
-{
-	struct neighbour *neigh;
-	struct rt6_info *rt6i;
+static struct neighbour *nss_connmgr_ipv6_neigh_get(ipv6_addr_t addr) {
+
 	struct dst_entry *dst;
 	struct in6_addr daddr;
+	struct rt6_info *rt6i;
+	struct neighbour *neigh;
 
 	/*
 	 * Look up the route to this address
 	 */
 	IPV6_ADDR_TO_IN6_ADDR(daddr, addr);
-	rt6i = rt6_lookup(net, &daddr, NULL, oif, 0);
+	rt6i = rt6_lookup(&init_net, &daddr, NULL, 0, 0);
 	if (!rt6i) {
 		NSS_CONNMGR_DEBUG_TRACE("rt6_lookup failed for: " IPV6_ADDR_OCTAL_FMT "\n", IPV6_ADDR_TO_OCTAL(addr));
-		return -1;
+		return NULL;
 	}
 
 	/*
@@ -434,43 +431,66 @@
 	 */
 	dst = (struct dst_entry *)rt6i;
 
-	rcu_read_lock();
-
 	/*
 	 * Get the neighbour to which this destination is pointing
 	 */
 	neigh = dst_get_neighbour_noref(dst);
+
+	if (!neigh) {
+		neigh = neigh_lookup(&nd_tbl, &daddr, dst->dev);
+	}
+
+	/* Release dst reference */
+	dst_release(dst);
+
+	return neigh;
+}
+
+/*
+ * nss_connmgr_ipv6_mac_addr_get()
+ *	Return the hardware (MAC) address of the given ipv6 address, if any.
+ *
+ * Returns 0 on success or a negative result on failure.
+ * We first look up for an entry in the neighbour discovery table, if entry is not found,
+ * We look up the rtable entry for the address and, from its neighbour structure, obtain the hardware address.
+ * This means we will also work if the neighbours are routers too.
+ */
+static int nss_connmgr_ipv6_mac_addr_get(ipv6_addr_t addr, mac_addr_t mac_addr)
+{
+	struct neighbour *neigh;
+
+	rcu_read_lock();
+
+	neigh = nss_connmgr_ipv6_neigh_get(addr);
+
 	if (!neigh) {
 		rcu_read_unlock();
-		dst_release(dst);
-		NSS_CONNMGR_DEBUG_TRACE("Error: No DST reference");
-		return -2;
+		NSS_CONNMGR_DEBUG_WARN("Error: No neigh reference \n");
+		return -1;
 	}
 
 	if (!(neigh->nud_state & NUD_VALID)) {
 		rcu_read_unlock();
-		dst_release(dst);
-		NSS_CONNMGR_DEBUG_TRACE("NUD Invalid");
-		return -3;
+		NSS_CONNMGR_DEBUG_WARN("NUD Invalid \n");
+		return -2;
 	}
+
 	if (!neigh->dev) {
 		rcu_read_unlock();
-		dst_release(dst);
-		NSS_CONNMGR_DEBUG_TRACE("Neigh Dev Invalid");
-		return -4;
+		NSS_CONNMGR_DEBUG_WARN("Neigh Dev Invalid \n");
+		return -3;
 	}
+
 	memcpy(mac_addr, neigh->ha, (size_t)neigh->dev->addr_len);
 	rcu_read_unlock();
 
-	dst_release(dst);
-
 	/*
 	 * If this mac looks like a multicast then it MAY be either truly multicast or it could be broadcast
 	 * Either way we fail!  We don't want to deal with multicasts or any sort because the NSS cannot deal with them.
 	 */
 	if (is_multicast_ether_addr(mac_addr)) {
-		NSS_CONNMGR_DEBUG_TRACE("Mac is multicast / broadcast - ignoring\n");
-		return -5;
+		NSS_CONNMGR_DEBUG_TRACE("Mac is multicast / broadcast - ignoring \n");
+		return -4;
 	}
 
 	return 0;
@@ -480,6 +500,7 @@
 {
 	struct rt6_info *rt6i;
 	struct dst_entry *dst;
+	struct net_device *dev;
 
 	/*
 	 * Look up the route to this address
@@ -491,8 +512,10 @@
 	}
 
 	dst = (struct dst_entry *)rt6i;
+	dev = dst->dev;
+	dst_release(dst);
 
-	return dst->dev;
+	return dev;
 }
 
 /*
@@ -793,7 +816,7 @@
 		/*
 		 * Get the MAC addresses that correspond to source and destination host addresses.
 		 */
-		if (nss_connmgr_ipv6_mac_addr_get(dev_net(src_dev), src_dev->ifindex, unic.src_ip, unic.src_mac)) {
+		if (nss_connmgr_ipv6_mac_addr_get(unic.src_ip, unic.src_mac)) {
 			dev_put(in);
 			NSS_CONNMGR_DEBUG_TRACE("%p: Failed to find MAC address for src IP: %pI6\n", ct, &unic.src_ip);
 			return NF_ACCEPT;
@@ -802,7 +825,7 @@
 		/*
 		 * Do dest now
 		 */
-		if (nss_connmgr_ipv6_mac_addr_get(dev_net(dest_dev), dest_dev->ifindex, unic.dest_ip, unic.dest_mac)) {
+		if (nss_connmgr_ipv6_mac_addr_get(unic.dest_ip, unic.dest_mac)) {
 			dev_put(in);
 			NSS_CONNMGR_DEBUG_TRACE("%p: Failed to find MAC address for dest IP: %pI6\n", ct, &unic.dest_ip);
 			return NF_ACCEPT;
@@ -874,6 +897,7 @@
 	IPV6_ADDR_NTOH(unic.dest_ip, unic.dest_ip);
 	unic.src_port = ntohs(unic.src_port);
 	unic.dest_port = ntohs(unic.dest_port);
+
 	/*
 	 * If operations have stopped then do not process packets
 	 */
@@ -978,6 +1002,14 @@
 	}
 
 	/*
+	 * If egress interface is a bridge,ignore; the rule will be added by bridge post-routing hook
+	 */
+	if (is_bridge_device(out)) {
+		NSS_CONNMGR_DEBUG_TRACE("ignoring bridge device in post-routing hook: %p\n", skb);
+		goto out;
+	}
+
+	/*
 	 * Only process packets that are also tracked by conntrack
 	 */
 	ct = nf_ct_get(skb, &ctinfo);
@@ -1209,7 +1241,7 @@
 		unic.flow_pppoe_session_id = (uint16_t)ppp_get_session_id(ppp_src);
 		memcpy(unic.flow_pppoe_remote_mac, (uint8_t *)ppp_get_remote_mac(ppp_src), ETH_ALEN);
 	} else {
-		if (nss_connmgr_ipv6_mac_addr_get(dev_net(src_dev), src_dev->ifindex, unic.src_ip, unic.src_mac)) {
+		if (nss_connmgr_ipv6_mac_addr_get(unic.src_ip, unic.src_mac)) {
 			NSS_CONNMGR_DEBUG_TRACE("%p: Failed to find MAC address for src IP: %pI6\n", ct, &unic.src_ip);
 			goto out;
 		}
@@ -1229,7 +1261,7 @@
 		unic.return_pppoe_session_id = (uint16_t)ppp_get_session_id(ppp_dest);
 		memcpy(unic.return_pppoe_remote_mac, (uint8_t *)ppp_get_remote_mac(ppp_dest), ETH_ALEN);
 	} else {
-		if (nss_connmgr_ipv6_mac_addr_get(dev_net(dest_dev), dest_dev->ifindex, unic.dest_ip, unic.dest_mac)) {
+		if (nss_connmgr_ipv6_mac_addr_get(unic.dest_ip, unic.dest_mac)) {
 			NSS_CONNMGR_DEBUG_TRACE("%p: Failed to find MAC address for dest IP: %pI6\n", ct, &unic.dest_ip);
 			goto out;
 		}
@@ -1372,6 +1404,7 @@
 	IPV6_ADDR_NTOH(unic.dest_ip, unic.dest_ip);
 	unic.src_port = ntohs(unic.src_port);
 	unic.dest_port = ntohs(unic.dest_port);
+
 	/*
 	 * If operations have stopped then do not process packets
 	 */
@@ -1397,7 +1430,6 @@
 		nss_connmgr_ipv6.debug_stats[NSS_CONNMGR_IPV6_CREATE_FAIL]++;
 		spin_unlock_bh(&nss_connmgr_ipv6.lock);
 	}
-
 out:
 	/*
 	 * Release the interface on which this skb arrived
@@ -1442,15 +1474,10 @@
 	struct nf_conn_counter *acct;
 	struct nss_connmgr_ipv6_connection *connection;
 
-	struct neighbour *neigh;
-
 	struct nss_ipv6_sync *sync;
 	struct nss_ipv6_establish *establish;
 
-	struct net_device *flow_dev;
-	struct net_device *return_dev;
-
-	uint32_t nd_key[4];
+	ipv6_addr_t daddr;
 
 	switch(nicp->reason)
 	{
@@ -1495,6 +1522,18 @@
 			connection->stats[NSS_CONNMGR_IPV6_ACCELERATED_TX_PKTS] = 0;
 			connection->stats[NSS_CONNMGR_IPV6_ACCELERATED_TX_BYTES] = 0;
 
+			IPV6_ADDR_NTOH(daddr, connection->src_addr);
+			connection->src_neigh = nss_connmgr_ipv6_neigh_get(daddr);
+			if (connection->src_neigh) {
+				neigh_hold(connection->src_neigh);
+			}
+
+			IPV6_ADDR_NTOH(daddr, connection->dest_addr);
+			connection->dest_neigh = nss_connmgr_ipv6_neigh_get(daddr);
+			if (connection->dest_neigh) {
+				neigh_hold(connection->dest_neigh);
+			}
+
 			spin_unlock_bh(&nss_connmgr_ipv6.lock);
 
 			return;
@@ -1617,53 +1656,30 @@
 		break;
 	}
 
-	flow_dev = nss_get_interface_dev(nss_connmgr_ipv6.nss_context, connection->src_interface);
-	if (unlikely(!flow_dev)) {
-		NSS_CONNMGR_DEBUG_WARN("Invalid src dev reference from NSS \n");
-		goto out;
-	}
-
-	return_dev = nss_get_interface_dev(nss_connmgr_ipv6.nss_context, connection->dest_interface);
-	if (unlikely(!return_dev)) {
-		NSS_CONNMGR_DEBUG_WARN("Invalid dest dev reference from NSS \n");
-		goto out;
-	}
-
-	/*
-	 * Hold the net_device references
-	 */
-	dev_hold(flow_dev);
-	dev_hold(return_dev);
-
 	/*
 	 * Update ND table.
 	 */
-	IPV6_ADDR_NTOH(nd_key, connection->src_addr);
-	neigh = neigh_lookup(&nd_tbl, &nd_key, flow_dev);
-	if (neigh) {
-		neigh_update(neigh, NULL, neigh->nud_state, NEIGH_UPDATE_F_WEAK_OVERRIDE);
-		neigh_release(neigh);
+	if (connection->src_neigh) {
+		if (sync->final_sync) {
+			neigh_release(connection->src_neigh);
+		} else {
+			neigh_update(connection->src_neigh, NULL, connection->src_neigh->nud_state, NEIGH_UPDATE_F_WEAK_OVERRIDE);
+		}
 	} else {
-		NSS_CONNMGR_DEBUG_TRACE("Neighbour entry could not be found for flow_dev\n");
+		NSS_CONNMGR_DEBUG_TRACE("Neighbour entry could not be found for onward flow\n");
 	}
 
-	IPV6_ADDR_NTOH(nd_key, connection->dest_addr);
-	neigh = neigh_lookup(&nd_tbl, &nd_key, return_dev);
-	if (neigh) {
-		neigh_update(neigh, NULL, neigh->nud_state, NEIGH_UPDATE_F_WEAK_OVERRIDE);
-		neigh_release(neigh);
+	if (connection->dest_neigh) {
+		if (sync->final_sync) {
+			neigh_release(connection->dest_neigh);
+		} else {
+			neigh_update(connection->dest_neigh, NULL, connection->dest_neigh->nud_state, NEIGH_UPDATE_F_WEAK_OVERRIDE);
+		}
 	} else {
-		NSS_CONNMGR_DEBUG_TRACE("Neighbour entry could not be found for return_dev\n");
+		NSS_CONNMGR_DEBUG_TRACE("Neighbour entry could not be found for return flow\n");
 	}
 
-	/*
-	 * Release the net_device references
-	 */
-	dev_put(flow_dev);
-	dev_put(return_dev);
-
-out:
-	/*
+		/*
 	 * Release connection
 	 */
 	nf_ct_put(ct);
@@ -2096,10 +2112,10 @@
 			}
 
 			size_wr += scnprintf(lbuf + size_wr, size_al - size_wr,
-					"src: " IPV6_ADDR_OCTAL_FMT ":%d\n"
-					"dest: " IPV6_ADDR_OCTAL_FMT ":%d\n",
-					IPV6_ADDR_TO_OCTAL(src_addr[j]), (int)ntohs(src_port[j]),
-					IPV6_ADDR_TO_OCTAL(dest_addr[j]), (int)ntohs(dest_port[j]));
+					"src_addr: %pI6:%d\n"
+					"dest_addr: %pI6:%d\n",
+					&(src_addr[j]), (int)ntohs(src_port[j]),
+					&(dest_addr[j]), (int)ntohs(dest_port[j]));
 
 			for (k = 0; (k < NSS_CONNMGR_IPV6_STATS_MAX); k++) {
 				size_wr += scnprintf(lbuf + size_wr, size_al - size_wr,