[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,