--- a/net/netfilter/nf_conntrack_proto_tcp.c
+++ b/net/netfilter/nf_conntrack_proto_tcp.c
@@ -32,12 +32,14 @@
 #include <net/netfilter/ipv6/nf_conntrack_ipv6.h>
 
 /* Do not check the TCP window for incoming packets  */
-static int nf_ct_tcp_no_window_check __read_mostly = 1;
+int nf_ct_tcp_no_window_check __read_mostly = 1;
+EXPORT_SYMBOL_GPL(nf_ct_tcp_no_window_check);
 
 /* "Be conservative in what you do,
     be liberal in what you accept from others."
     If it's non-zero, we mark only out of window RST segments as INVALID. */
-static int nf_ct_tcp_be_liberal __read_mostly = 0;
+int nf_ct_tcp_be_liberal __read_mostly = 0;
+EXPORT_SYMBOL_GPL(nf_ct_tcp_be_liberal);
 
 /* If it is set to zero, we disable picking up already established
    connections. */
--- a/include/linux/if_bridge.h
+++ b/include/linux/if_bridge.h
@@ -60,6 +60,9 @@ struct br_ip_list {
 #define BR_DEFAULT_AGEING_TIME	(300 * HZ)
 
 extern void brioctl_set(int (*ioctl_hook)(struct net *, unsigned int, void __user *));
+extern void br_dev_update_stats(struct net_device *dev,
+				struct rtnl_link_stats64 *nlstats);
+extern bool br_is_hairpin_enabled(struct net_device *dev);
 
 #if IS_ENABLED(CONFIG_BRIDGE) && IS_ENABLED(CONFIG_BRIDGE_IGMP_SNOOPING)
 int br_multicast_list_adjacent(struct net_device *dev,
@@ -155,4 +158,41 @@ br_port_flag_is_set(const struct net_dev
 }
 #endif
 
+/* QCA NSS ECM support - Start */
+extern struct net_device *br_port_dev_get(struct net_device *dev,
+					  unsigned char *addr,
+					  struct sk_buff *skb,
+					  unsigned int cookie);
+extern void br_refresh_fdb_entry(struct net_device *dev, const char *addr);
+extern struct net_bridge_fdb_entry *br_fdb_has_entry(struct net_device *dev,
+						     const char *addr,
+						     __u16 vid);
+extern void br_fdb_update_register_notify(struct notifier_block *nb);
+extern void br_fdb_update_unregister_notify(struct notifier_block *nb);
+
+typedef struct net_bridge_port *br_port_dev_get_hook_t(struct net_device *dev,
+						       struct sk_buff *skb,
+						       unsigned char *addr,
+						       unsigned int cookie);
+extern br_port_dev_get_hook_t __rcu *br_port_dev_get_hook;
+
+#define BR_FDB_EVENT_ADD     0x01
+#define BR_FDB_EVENT_DEL     0x02
+
+struct br_fdb_event {
+	struct net_device *dev;
+	unsigned char      addr[6];
+	unsigned char      is_local;
+	struct net_bridge *br;
+	struct net_device *orig_dev;
+};
+extern void br_fdb_register_notify(struct notifier_block *nb);
+extern void br_fdb_unregister_notify(struct notifier_block *nb);
+
+typedef struct net_bridge_port *br_get_dst_hook_t(
+		const struct net_bridge_port *src,
+		struct sk_buff **skb);
+extern br_get_dst_hook_t __rcu *br_get_dst_hook;
+/* QCA NSS ECM support - End */
+
 #endif
--- a/include/linux/if_vlan.h
+++ b/include/linux/if_vlan.h
@@ -222,7 +222,28 @@ extern void vlan_vids_del_by_dev(struct
 
 extern bool vlan_uses_dev(const struct net_device *dev);
 
+/* QCA NSS ECM support - Start */
+extern void __vlan_dev_update_accel_stats(struct net_device *dev,
+                                    struct rtnl_link_stats64 *stats);
+extern u16 vlan_dev_get_egress_prio(struct net_device *dev, u32 skb_prio);
+extern struct net_device *vlan_dev_next_dev(const struct net_device *dev);
+/* QCA NSS ECM support - End */
+
 #else
+/* QCA NSS ECM support - Start */
+static inline void __vlan_dev_update_accel_stats(struct net_device *dev,
+					   struct rtnl_link_stats64 *stats)
+{
+
+}
+
+static inline u16 vlan_dev_get_egress_prio(struct net_device *dev,
+						u32 skb_prio)
+{
+	return 0;
+}
+/* QCA NSS ECM support - End */
+
 static inline struct net_device *
 __vlan_find_dev_deep_rcu(struct net_device *real_dev,
 		     __be16 vlan_proto, u16 vlan_id)
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -2745,6 +2745,10 @@ enum netdev_cmd {
 	NETDEV_CVLAN_FILTER_DROP_INFO,
 	NETDEV_SVLAN_FILTER_PUSH_INFO,
 	NETDEV_SVLAN_FILTER_DROP_INFO,
+	/* QCA NSS ECM Support - Start */
+	NETDEV_BR_JOIN,
+	NETDEV_BR_LEAVE,
+	/* QCA NSS ECM Support - End */
 };
 const char *netdev_cmd_to_name(enum netdev_cmd cmd);
 
--- a/include/net/ip6_route.h
+++ b/include/net/ip6_route.h
@@ -210,6 +210,11 @@ void rt6_multipath_rebalance(struct fib6
 void rt6_uncached_list_add(struct rt6_info *rt);
 void rt6_uncached_list_del(struct rt6_info *rt);
 
+/* QCA NSS ECM support - Start */
+int rt6_register_notifier(struct notifier_block *nb);
+int rt6_unregister_notifier(struct notifier_block *nb);
+/* QCA NSS ECM support - End */
+
 static inline const struct rt6_info *skb_rt6_info(const struct sk_buff *skb)
 {
 	const struct dst_entry *dst = skb_dst(skb);
--- a/include/net/neighbour.h
+++ b/include/net/neighbour.h
@@ -568,4 +568,15 @@ static inline void neigh_update_is_route
 		*notify = 1;
 	}
 }
+
+/* QCA NSS ECM support - Start */
+struct neigh_mac_update {
+	unsigned char old_mac[ALIGN(MAX_ADDR_LEN, sizeof(unsigned long))];
+	unsigned char update_mac[ALIGN(MAX_ADDR_LEN, sizeof(unsigned long))];
+};
+
+extern void neigh_mac_update_register_notify(struct notifier_block *nb);
+extern void neigh_mac_update_unregister_notify(struct notifier_block *nb);
+/* QCA NSS ECM support - End */
+
 #endif
--- a/include/net/route.h
+++ b/include/net/route.h
@@ -234,6 +234,11 @@ struct rtable *rt_dst_alloc(struct net_d
 			     bool nopolicy, bool noxfrm);
 struct rtable *rt_dst_clone(struct net_device *dev, struct rtable *rt);
 
+/* QCA NSS ECM support - Start */
+int ip_rt_register_notifier(struct notifier_block *nb);
+int ip_rt_unregister_notifier(struct notifier_block *nb);
+/* QCA NSS ECM support - End */
+
 struct in_ifaddr;
 void fib_add_ifaddr(struct in_ifaddr *);
 void fib_del_ifaddr(struct in_ifaddr *, struct in_ifaddr *);
--- a/net/8021q/vlan_core.c
+++ b/net/8021q/vlan_core.c
@@ -550,4 +550,52 @@ static int __init vlan_offload_init(void
 	return 0;
 }
 
+/* QCA NSS ECM support - Start */
+/* Update the VLAN device with statistics from network offload engines */
+void __vlan_dev_update_accel_stats(struct net_device *dev,
+				   struct rtnl_link_stats64 *nlstats)
+{
+	struct vlan_pcpu_stats *stats;
+
+	if (!is_vlan_dev(dev))
+		return;
+
+	stats = per_cpu_ptr(vlan_dev_priv(dev)->vlan_pcpu_stats, 0);
+
+	u64_stats_update_begin(&stats->syncp);
+	stats->rx_packets += nlstats->rx_packets;
+	stats->rx_bytes += nlstats->rx_bytes;
+	stats->tx_packets += nlstats->tx_packets;
+	stats->tx_bytes += nlstats->tx_bytes;
+	u64_stats_update_end(&stats->syncp);
+}
+EXPORT_SYMBOL(__vlan_dev_update_accel_stats);
+
+/* Lookup the 802.1p egress_map table and return the 802.1p value */
+u16 vlan_dev_get_egress_prio(struct net_device *dev, u32 skb_prio)
+{
+	struct vlan_priority_tci_mapping *mp;
+
+	mp = vlan_dev_priv(dev)->egress_priority_map[(skb_prio & 0xf)];
+	while (mp) {
+		if (mp->priority == skb_prio) {
+			/* This should already be shifted
+			 * to mask correctly with the
+			 * VLAN's TCI
+			 */
+			return mp->vlan_qos;
+		}
+		mp = mp->next;
+	}
+	return 0;
+}
+EXPORT_SYMBOL(vlan_dev_get_egress_prio);
+
+struct net_device *vlan_dev_next_dev(const struct net_device *dev)
+{
+	return vlan_dev_priv(dev)->real_dev;
+}
+EXPORT_SYMBOL(vlan_dev_next_dev);
+/* QCA NSS ECM support - End */
+
 fs_initcall(vlan_offload_init);
--- a/net/bridge/br_fdb.c
+++ b/net/bridge/br_fdb.c
@@ -37,6 +37,35 @@ static int fdb_insert(struct net_bridge
 static void fdb_notify(struct net_bridge *br,
 		       const struct net_bridge_fdb_entry *, int, bool);
 
+/* QCA NSS ECM support - Start */
+ATOMIC_NOTIFIER_HEAD(br_fdb_notifier_list);
+ATOMIC_NOTIFIER_HEAD(br_fdb_update_notifier_list);
+
+void br_fdb_register_notify(struct notifier_block *nb)
+{
+	atomic_notifier_chain_register(&br_fdb_notifier_list, nb);
+}
+EXPORT_SYMBOL_GPL(br_fdb_register_notify);
+
+void br_fdb_unregister_notify(struct notifier_block *nb)
+{
+	atomic_notifier_chain_unregister(&br_fdb_notifier_list, nb);
+}
+EXPORT_SYMBOL_GPL(br_fdb_unregister_notify);
+
+void br_fdb_update_register_notify(struct notifier_block *nb)
+{
+	atomic_notifier_chain_register(&br_fdb_update_notifier_list, nb);
+}
+EXPORT_SYMBOL_GPL(br_fdb_update_register_notify);
+
+void br_fdb_update_unregister_notify(struct notifier_block *nb)
+{
+	atomic_notifier_chain_unregister(&br_fdb_update_notifier_list, nb);
+}
+EXPORT_SYMBOL_GPL(br_fdb_update_unregister_notify);
+/* QCA NSS ECM support - End */
+
 int __init br_fdb_init(void)
 {
 	br_fdb_cache = kmem_cache_create("bridge_fdb_cache",
@@ -342,6 +371,7 @@ void br_fdb_cleanup(struct work_struct *
 	unsigned long delay = hold_time(br);
 	unsigned long work_delay = delay;
 	unsigned long now = jiffies;
+	u8 mac_addr[6]; /* QCA NSS ECM support */
 
 	/* this part is tricky, in order to avoid blocking learning and
 	 * consequently forwarding, we rely on rcu to delete objects with
@@ -368,8 +398,15 @@ void br_fdb_cleanup(struct work_struct *
 			work_delay = min(work_delay, this_timer - now);
 		} else {
 			spin_lock_bh(&br->hash_lock);
-			if (!hlist_unhashed(&f->fdb_node))
+			if (!hlist_unhashed(&f->fdb_node)) {
+				ether_addr_copy(mac_addr, f->key.addr.addr);
 				fdb_delete(br, f, true);
+				/* QCA NSS ECM support - Start */
+				atomic_notifier_call_chain(
+					&br_fdb_update_notifier_list, 0,
+					(void *)mac_addr);
+				/* QCA NSS ECM support - End */
+			}
 			spin_unlock_bh(&br->hash_lock);
 		}
 	}
@@ -610,6 +647,12 @@ void br_fdb_update(struct net_bridge *br
 						      &fdb->flags)))
 					clear_bit(BR_FDB_ADDED_BY_EXT_LEARN,
 						  &fdb->flags);
+
+				/* QCA NSS ECM support - Start */
+				atomic_notifier_call_chain(
+					&br_fdb_update_notifier_list,
+					0, (void *)addr);
+				/* QCA NSS ECM support - End */
 			}
 
 			if (unlikely(test_bit(BR_FDB_ADDED_BY_USER, &flags)))
@@ -734,6 +777,25 @@ static void fdb_notify(struct net_bridge
 	struct sk_buff *skb;
 	int err = -ENOBUFS;
 
+	/* QCA NSS ECM support - Start */
+	if (fdb->dst) {
+		int event;
+		struct br_fdb_event fdb_event;
+
+		if (type == RTM_NEWNEIGH)
+			event = BR_FDB_EVENT_ADD;
+		else
+			event = BR_FDB_EVENT_DEL;
+
+		fdb_event.dev = fdb->dst->dev;
+		ether_addr_copy(fdb_event.addr, fdb->key.addr.addr);
+		fdb_event.is_local = test_bit(BR_FDB_LOCAL, &fdb->dst->flags);
+		atomic_notifier_call_chain(&br_fdb_notifier_list,
+					   event,
+					   (void *)&fdb_event);
+	}
+	/* QCA NSS ECM support - End */
+
 	if (swdev_notify)
 		br_switchdev_fdb_notify(br, fdb, type);
 
@@ -1302,3 +1364,44 @@ void br_fdb_clear_offload(const struct n
 	spin_unlock_bh(&p->br->hash_lock);
 }
 EXPORT_SYMBOL_GPL(br_fdb_clear_offload);
+
+/* QCA NSS ECM support - Start */
+/* Refresh FDB entries for bridge packets being forwarded by offload engines */
+void br_refresh_fdb_entry(struct net_device *dev, const char *addr)
+{
+	struct net_bridge_port *p = br_port_get_rcu(dev);
+
+	if (!p || p->state == BR_STATE_DISABLED)
+		return;
+
+	if (!is_valid_ether_addr(addr)) {
+		pr_info("bridge: Attempt to refresh with invalid ether address %pM\n",
+			addr);
+		return;
+	}
+
+	rcu_read_lock();
+	br_fdb_update(p->br, p, addr, 0, true);
+	rcu_read_unlock();
+}
+EXPORT_SYMBOL_GPL(br_refresh_fdb_entry);
+
+/* Look up the MAC address in the device's bridge fdb table */
+struct net_bridge_fdb_entry *br_fdb_has_entry(struct net_device *dev,
+					      const char *addr, __u16 vid)
+{
+	struct net_bridge_port *p = br_port_get_rcu(dev);
+	struct net_bridge_fdb_entry *fdb;
+
+	if (!p || p->state == BR_STATE_DISABLED)
+		return NULL;
+
+	rcu_read_lock();
+	fdb = fdb_find_rcu(&p->br->fdb_hash_tbl, addr, vid);
+	rcu_read_unlock();
+
+	return fdb;
+}
+EXPORT_SYMBOL_GPL(br_fdb_has_entry);
+/* QCA NSS ECM support - End */
+
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -26,6 +26,12 @@
 
 #include "br_private.h"
 
+/* QCA NSS ECM support - Start */
+/* Hook for external forwarding logic */
+br_port_dev_get_hook_t __rcu *br_port_dev_get_hook __read_mostly;
+EXPORT_SYMBOL_GPL(br_port_dev_get_hook);
+/* QCA NSS ECM support - End */
+
 /*
  * Determine initial path cost based on speed.
  * using recommendations from 802.1d standard
@@ -697,6 +703,8 @@ int br_add_if(struct net_bridge *br, str
 
 	kobject_uevent(&p->kobj, KOBJ_ADD);
 
+	call_netdevice_notifiers(NETDEV_BR_JOIN, dev); /* QCA NSS ECM support */
+
 	return 0;
 
 err7:
@@ -730,6 +738,8 @@ int br_del_if(struct net_bridge *br, str
 	if (!p || p->br != br)
 		return -EINVAL;
 
+	call_netdevice_notifiers(NETDEV_BR_LEAVE, dev); /* QCA NSS ECM support */
+
 	/* Since more than one interface can be attached to a bridge,
 	 * there still maybe an alternate path for netconsole to use;
 	 * therefore there is no reason for a NETDEV_RELEASE event.
@@ -773,3 +783,98 @@ bool br_port_flag_is_set(const struct ne
 	return p->flags & flag;
 }
 EXPORT_SYMBOL_GPL(br_port_flag_is_set);
+
+/* QCA NSS ECM support - Start */
+/* Update bridge statistics for bridge packets processed by offload engines */
+void br_dev_update_stats(struct net_device *dev,
+			 struct rtnl_link_stats64 *nlstats)
+{
+	struct net_bridge *br;
+	struct pcpu_sw_netstats *stats;
+
+	/* Is this a bridge? */
+	if (!(dev->priv_flags & IFF_EBRIDGE))
+		return;
+
+	br = netdev_priv(dev);
+	stats = this_cpu_ptr(br->stats);
+
+	u64_stats_update_begin(&stats->syncp);
+	stats->rx_packets += nlstats->rx_packets;
+	stats->rx_bytes += nlstats->rx_bytes;
+	stats->tx_packets += nlstats->tx_packets;
+	stats->tx_bytes += nlstats->tx_bytes;
+	u64_stats_update_end(&stats->syncp);
+}
+EXPORT_SYMBOL_GPL(br_dev_update_stats);
+
+/* API to know if hairpin feature is enabled/disabled on this bridge port */
+bool br_is_hairpin_enabled(struct net_device *dev)
+{
+	struct net_bridge_port *port = br_port_get_check_rcu(dev);
+
+	if (likely(port))
+		return port->flags & BR_HAIRPIN_MODE;
+	return false;
+}
+EXPORT_SYMBOL_GPL(br_is_hairpin_enabled);
+
+/* br_port_dev_get()
+ *      If a skb is provided, and the br_port_dev_get_hook_t hook exists,
+ *      use that to try and determine the egress port for that skb.
+ *      If not, or no egress port could be determined, use the given addr
+ *      to identify the port to which it is reachable,
+ *	returing a reference to the net device associated with that port.
+ *
+ * NOTE: Return NULL if given dev is not a bridge or the mac has no
+ * associated port.
+ */
+struct net_device *br_port_dev_get(struct net_device *dev, unsigned char *addr,
+				   struct sk_buff *skb,
+				   unsigned int cookie)
+{
+	struct net_bridge_fdb_entry *fdbe;
+	struct net_bridge *br;
+	struct net_device *netdev = NULL;
+
+	/* Is this a bridge? */
+	if (!(dev->priv_flags & IFF_EBRIDGE))
+		return NULL;
+
+	rcu_read_lock();
+
+	/* If the hook exists and the skb isn't NULL, try and get the port */
+	if (skb) {
+		br_port_dev_get_hook_t *port_dev_get_hook;
+
+		port_dev_get_hook = rcu_dereference(br_port_dev_get_hook);
+		if (port_dev_get_hook) {
+			struct net_bridge_port *pdst =
+				__br_get(port_dev_get_hook, NULL, dev, skb,
+					 addr, cookie);
+			if (pdst) {
+				dev_hold(pdst->dev);
+				netdev = pdst->dev;
+				goto out;
+			}
+		}
+	}
+
+	/* Either there is no hook, or can't
+	 * determine the port to use - fall back to using FDB
+	 */
+
+	br = netdev_priv(dev);
+
+	/* Lookup the fdb entry and get reference to the port dev */
+	fdbe = br_fdb_find_rcu(br, addr, 0);
+	if (fdbe && fdbe->dst) {
+		netdev = fdbe->dst->dev; /* port device */
+		dev_hold(netdev);
+	}
+out:
+	rcu_read_unlock();
+	return netdev;
+}
+EXPORT_SYMBOL_GPL(br_port_dev_get);
+/* QCA NSS ECM support - End */
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -1593,4 +1593,9 @@ void br_do_proxy_suppress_arp(struct sk_
 void br_do_suppress_nd(struct sk_buff *skb, struct net_bridge *br,
 		       u16 vid, struct net_bridge_port *p, struct nd_msg *msg);
 struct nd_msg *br_is_nd_neigh_msg(struct sk_buff *skb, struct nd_msg *m);
+
+/* QCA NSS ECM support - Start */
+#define __br_get(__hook, __default, __args ...) \
+		(__hook ? (__hook(__args)) : (__default))
+/* QCA NSS ECM support - End */
 #endif
--- a/net/core/neighbour.c
+++ b/net/core/neighbour.c
@@ -1205,7 +1205,21 @@ static void neigh_update_hhs(struct neig
 	}
 }
 
+/* QCA NSS ECM support - start */
+ATOMIC_NOTIFIER_HEAD(neigh_mac_update_notifier_list);
+ 
+void neigh_mac_update_register_notify(struct notifier_block *nb)
+{
+	atomic_notifier_chain_register(&neigh_mac_update_notifier_list, nb);
+}
+EXPORT_SYMBOL_GPL(neigh_mac_update_register_notify);
 
+void neigh_mac_update_unregister_notify(struct notifier_block *nb)
+{
+	atomic_notifier_chain_unregister(&neigh_mac_update_notifier_list, nb);
+}
+EXPORT_SYMBOL_GPL(neigh_mac_update_unregister_notify);
+/* QCA NSS ECM support - End */
 
 /* Generic update routine.
    -- lladdr is new lladdr or NULL, if it is not supplied.
@@ -1236,6 +1250,7 @@ static int __neigh_update(struct neighbo
 	int notify = 0;
 	struct net_device *dev;
 	int update_isrouter = 0;
+	struct neigh_mac_update nmu; /* QCA NSS ECM support */
 
 	trace_neigh_update(neigh, lladdr, new, flags, nlmsg_pid);
 
@@ -1250,6 +1265,8 @@ static int __neigh_update(struct neighbo
 		new = old;
 		goto out;
 	}
+	memset(&nmu, 0, sizeof(struct neigh_mac_update)); /* QCA NSS ECM support */
+
 	if (!(flags & NEIGH_UPDATE_F_ADMIN) &&
 	    (old & (NUD_NOARP | NUD_PERMANENT)))
 		goto out;
@@ -1281,6 +1298,11 @@ static int __neigh_update(struct neighbo
 		   - compare new & old
 		   - if they are different, check override flag
 		 */
+		/* QCA NSS ECM update - Start */
+		memcpy(nmu.old_mac, neigh->ha, dev->addr_len);
+		memcpy(nmu.update_mac, lladdr, dev->addr_len);
+		/* QCA NSS ECM update - End */
+
 		if ((old & NUD_VALID) &&
 		    !memcmp(lladdr, neigh->ha, dev->addr_len))
 			lladdr = neigh->ha;
@@ -1403,8 +1425,11 @@ out:
 	if (((new ^ old) & NUD_PERMANENT) || ext_learn_change)
 		neigh_update_gc_list(neigh);
 
-	if (notify)
+	if (notify) {
 		neigh_update_notify(neigh, nlmsg_pid);
+		atomic_notifier_call_chain(&neigh_mac_update_notifier_list, 0,
+					   (struct neigh_mac_update *)&nmu); /* QCA NSS ECM support */
+	}
 
 	trace_neigh_update_done(neigh, err);
 
--- a/net/ipv4/fib_trie.c
+++ b/net/ipv4/fib_trie.c
@@ -1164,6 +1164,9 @@ static bool fib_valid_key_len(u32 key, u
 static void fib_remove_alias(struct trie *t, struct key_vector *tp,
 			     struct key_vector *l, struct fib_alias *old);
 
+/* Define route change notification chain. */
+static BLOCKING_NOTIFIER_HEAD(iproute_chain);	/* QCA NSS ECM support */
+
 /* Caller must hold RTNL. */
 int fib_table_insert(struct net *net, struct fib_table *tb,
 		     struct fib_config *cfg, struct netlink_ext_ack *extack)
@@ -1352,6 +1355,9 @@ int fib_table_insert(struct net *net, st
 	rtmsg_fib(RTM_NEWROUTE, htonl(key), new_fa, plen, new_fa->tb_id,
 		  &cfg->fc_nlinfo, nlflags);
 succeeded:
+	blocking_notifier_call_chain(&iproute_chain,
+				     RTM_NEWROUTE, fi);
+
 	return 0;
 
 out_remove_new_fa:
@@ -1722,6 +1728,9 @@ int fib_table_delete(struct net *net, st
 	if (fa_to_delete->fa_state & FA_S_ACCESSED)
 		rt_cache_flush(cfg->fc_nlinfo.nl_net);
 
+	blocking_notifier_call_chain(&iproute_chain,
+				     RTM_DELROUTE, fa_to_delete->fa_info);
+
 	fib_release_info(fa_to_delete->fa_info);
 	alias_free_mem_rcu(fa_to_delete);
 	return 0;
@@ -2358,6 +2367,20 @@ void __init fib_trie_init(void)
 					   0, SLAB_PANIC, NULL);
 }
 
+/* QCA NSS ECM support - Start */
+int ip_rt_register_notifier(struct notifier_block *nb)
+{
+	return blocking_notifier_chain_register(&iproute_chain, nb);
+}
+EXPORT_SYMBOL(ip_rt_register_notifier);
+
+int ip_rt_unregister_notifier(struct notifier_block *nb)
+{
+	return blocking_notifier_chain_unregister(&iproute_chain, nb);
+}
+EXPORT_SYMBOL(ip_rt_unregister_notifier);
+/* QCA NSS ECM support - End */
+
 struct fib_table *fib_trie_table(u32 id, struct fib_table *alias)
 {
 	struct fib_table *tb;
--- a/include/net/addrconf.h
+++ b/include/net/addrconf.h
@@ -501,4 +501,9 @@ int if6_proc_init(void);
 void if6_proc_exit(void);
 #endif
 
+/* QCA NSS ECM support - Start */
+struct net_device *ipv6_dev_find_and_hold(struct net *net, struct in6_addr *addr,
+				 int strict);
+/* QCA NSS ECM support - End */
+
 #endif
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -7233,3 +7233,35 @@ void addrconf_cleanup(void)
 
 	destroy_workqueue(addrconf_wq);
 }
+
+/* QCA NSS ECM support - Start */
+/* ipv6_dev_find_and_hold()
+ *	Find (and hold) net device that has the given address.
+ *	Or NULL on failure.
+ */
+struct net_device *ipv6_dev_find_and_hold(struct net *net, struct in6_addr *addr,
+				 int strict)
+{
+	struct inet6_ifaddr *ifp;
+	struct net_device *dev;
+
+	ifp = ipv6_get_ifaddr(net, addr, NULL, strict);
+	if (!ifp)
+		return NULL;
+
+	if (!ifp->idev) {
+		in6_ifa_put(ifp);
+		return NULL;
+	}
+
+	dev = ifp->idev->dev;
+	if (dev)
+		dev_hold(dev);
+
+	in6_ifa_put(ifp);
+
+	return dev;
+}
+EXPORT_SYMBOL(ipv6_dev_find_and_hold);
+/* QCA NSS ECM support - End */
+
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -649,6 +649,7 @@ void ndisc_send_ns(struct net_device *de
 
 	ndisc_send_skb(skb, daddr, saddr);
 }
+EXPORT_SYMBOL(ndisc_send_ns);
 
 void ndisc_send_rs(struct net_device *dev, const struct in6_addr *saddr,
 		   const struct in6_addr *daddr)
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -3733,6 +3733,9 @@ out:
 	return ERR_PTR(err);
 }
 
+/* Define route change notification chain. */
+ATOMIC_NOTIFIER_HEAD(ip6route_chain);	/* QCA NSS ECM support */
+
 int ip6_route_add(struct fib6_config *cfg, gfp_t gfp_flags,
 		  struct netlink_ext_ack *extack)
 {
@@ -3744,6 +3747,10 @@ int ip6_route_add(struct fib6_config *cf
 		return PTR_ERR(rt);
 
 	err = __ip6_ins_rt(rt, &cfg->fc_nlinfo, extack);
+	if (!err)
+		atomic_notifier_call_chain(&ip6route_chain,
+					   RTM_NEWROUTE, rt);
+
 	fib6_info_release(rt);
 
 	return err;
@@ -3765,6 +3772,9 @@ static int __ip6_del_rt(struct fib6_info
 	err = fib6_del(rt, info);
 	spin_unlock_bh(&table->tb6_lock);
 
+	if (!err)
+		atomic_notifier_call_chain(&ip6route_chain,
+					   RTM_DELROUTE, rt);
 out:
 	fib6_info_release(rt);
 	return err;
@@ -6116,6 +6126,20 @@ static int ip6_route_dev_notify(struct n
 	return NOTIFY_OK;
 }
 
+/* QCA NSS ECM support - Start */
+int rt6_register_notifier(struct notifier_block *nb)
+{
+	return atomic_notifier_chain_register(&ip6route_chain, nb);
+}
+EXPORT_SYMBOL(rt6_register_notifier);
+
+int rt6_unregister_notifier(struct notifier_block *nb)
+{
+	return atomic_notifier_chain_unregister(&ip6route_chain, nb);
+}
+EXPORT_SYMBOL(rt6_unregister_notifier);
+/* QCA NSS ECM support - End */
+
 /*
  *	/proc
  */
