[][openwrt][mt7988][crypto][EIP197 Alpha Release]

[Description]
Add alpha version of EIP197 package(crypto-eip)

[Release-log]
N/A

Change-Id: Ib90915f531aa238b90bd0fecc81e2ec3bf6016cc
Reviewed-on: https://gerrit.mediatek.inc/c/openwrt/feeds/mtk_openwrt_feeds/+/7908928
diff --git a/target/linux/mediatek/patches-5.4/999-1022-backport-linux-6.2-xfrm-packet-mode.patch b/target/linux/mediatek/patches-5.4/999-1022-backport-linux-6.2-xfrm-packet-mode.patch
new file mode 100644
index 0000000..9eb9347
--- /dev/null
+++ b/target/linux/mediatek/patches-5.4/999-1022-backport-linux-6.2-xfrm-packet-mode.patch
@@ -0,0 +1,980 @@
+--- a/include/linux/netdevice.h
++++ b/include/linux/netdevice.h
+@@ -919,6 +919,10 @@ struct xfrmdev_ops {
+ 	bool	(*xdo_dev_offload_ok) (struct sk_buff *skb,
+ 				       struct xfrm_state *x);
+ 	void	(*xdo_dev_state_advance_esn) (struct xfrm_state *x);
++	void	(*xdo_dev_state_update_curlft) (struct xfrm_state *x);
++	int	(*xdo_dev_policy_add) (struct xfrm_policy *x);
++	void	(*xdo_dev_policy_delete) (struct xfrm_policy *x);
++	void	(*xdo_dev_policy_free) (struct xfrm_policy *x);
+ };
+ #endif
+ 
+--- a/include/net/xfrm.h
++++ b/include/net/xfrm.h
+@@ -125,11 +125,25 @@ struct xfrm_state_walk {
+ 	struct xfrm_address_filter *filter;
+ };
+ 
++enum {
++	XFRM_DEV_OFFLOAD_IN = 1,
++	XFRM_DEV_OFFLOAD_OUT,
++	XFRM_DEV_OFFLOAD_FWD,
++};
++
++enum {
++	XFRM_DEV_OFFLOAD_UNSPECIFIED,
++	XFRM_DEV_OFFLOAD_CRYPTO,
++	XFRM_DEV_OFFLOAD_PACKET,
++};
++
+ struct xfrm_state_offload {
+ 	struct net_device	*dev;
+ 	unsigned long		offload_handle;
+ 	unsigned int		num_exthdrs;
+ 	u8			flags;
++	u8			dir : 2;
++	u8			type : 2;
+ };
+ 
+ struct xfrm_mode {
+@@ -527,6 +541,8 @@ struct xfrm_policy {
+ 	struct xfrm_tmpl       	xfrm_vec[XFRM_MAX_DEPTH];
+ 	struct hlist_node	bydst_inexact_list;
+ 	struct rcu_head		rcu;
++
++	struct xfrm_state_offload xdo;
+ };
+ 
+ static inline struct net *xp_net(const struct xfrm_policy *xp)
+@@ -1084,6 +1100,29 @@ xfrm_state_addr_cmp(const struct xfrm_tm
+ }
+ 
+ #ifdef CONFIG_XFRM
++static inline struct xfrm_state *xfrm_input_state(struct sk_buff *skb)
++{
++	struct sec_path *sp = skb_sec_path(skb);
++
++	return sp->xvec[sp->len - 1];
++}
++#endif
++
++static inline struct xfrm_offload *xfrm_offload(struct sk_buff *skb)
++{
++#ifdef CONFIG_XFRM
++	struct sec_path *sp = skb_sec_path(skb);
++
++	if (!sp || !sp->olen || sp->len != sp->olen)
++		return NULL;
++
++	return &sp->ovec[sp->olen - 1];
++#else
++	return NULL;
++#endif
++}
++
++#ifdef CONFIG_XFRM
+ int __xfrm_policy_check(struct sock *, int dir, struct sk_buff *skb,
+ 			unsigned short family);
+ 
+@@ -1093,10 +1132,19 @@ static inline int __xfrm_policy_check2(s
+ {
+ 	struct net *net = dev_net(skb->dev);
+ 	int ndir = dir | (reverse ? XFRM_POLICY_MASK + 1 : 0);
++	struct xfrm_offload *xo = xfrm_offload(skb);
++	struct xfrm_state *x;
+ 
+ 	if (sk && sk->sk_policy[XFRM_POLICY_IN])
+ 		return __xfrm_policy_check(sk, ndir, skb, family);
+ 
++	if (xo) {
++		x = xfrm_input_state(skb);
++		if (x->xso.type == XFRM_DEV_OFFLOAD_PACKET)
++			return (xo->flags & CRYPTO_DONE) &&
++			       (xo->status & CRYPTO_SUCCESS);
++	}
++
+ 	return	(!net->xfrm.policy_count[dir] && !secpath_exists(skb)) ||
+ 		(skb_dst(skb) && (skb_dst(skb)->flags & DST_NOPOLICY)) ||
+ 		__xfrm_policy_check(sk, ndir, skb, family);
+@@ -1490,6 +1538,23 @@ struct xfrm_state *xfrm_stateonly_find(s
+ struct xfrm_state *xfrm_state_lookup_byspi(struct net *net, __be32 spi,
+ 					      unsigned short family);
+ int xfrm_state_check_expire(struct xfrm_state *x);
++#ifdef CONFIG_XFRM_OFFLOAD
++static inline void xfrm_dev_state_update_curlft(struct xfrm_state *x)
++{
++	struct xfrm_state_offload *xdo = &x->xso;
++	struct net_device *dev = xdo->dev;
++
++	if (x->xso.type != XFRM_DEV_OFFLOAD_PACKET)
++		return;
++
++	if (dev && dev->xfrmdev_ops &&
++	    dev->xfrmdev_ops->xdo_dev_state_update_curlft)
++		dev->xfrmdev_ops->xdo_dev_state_update_curlft(x);
++
++}
++#else
++static inline void xfrm_dev_state_update_curlft(struct xfrm_state *x) {}
++#endif
+ void xfrm_state_insert(struct xfrm_state *x);
+ int xfrm_state_add(struct xfrm_state *x);
+ int xfrm_state_update(struct xfrm_state *x);
+@@ -1539,6 +1604,8 @@ struct xfrm_state *xfrm_find_acq_byseq(s
+ int xfrm_state_delete(struct xfrm_state *x);
+ int xfrm_state_flush(struct net *net, u8 proto, bool task_valid, bool sync);
+ int xfrm_dev_state_flush(struct net *net, struct net_device *dev, bool task_valid);
++int xfrm_dev_policy_flush(struct net *net, struct net_device *dev,
++			  bool task_valid);
+ void xfrm_sad_getinfo(struct net *net, struct xfrmk_sadinfo *si);
+ void xfrm_spd_getinfo(struct net *net, struct xfrmk_spdinfo *si);
+ u32 xfrm_replay_seqhi(struct xfrm_state *x, __be32 net_seq);
+@@ -1820,29 +1887,6 @@ static inline void xfrm_states_delete(st
+ }
+ #endif
+ 
+-#ifdef CONFIG_XFRM
+-static inline struct xfrm_state *xfrm_input_state(struct sk_buff *skb)
+-{
+-	struct sec_path *sp = skb_sec_path(skb);
+-
+-	return sp->xvec[sp->len - 1];
+-}
+-#endif
+-
+-static inline struct xfrm_offload *xfrm_offload(struct sk_buff *skb)
+-{
+-#ifdef CONFIG_XFRM
+-	struct sec_path *sp = skb_sec_path(skb);
+-
+-	if (!sp || !sp->olen || sp->len != sp->olen)
+-		return NULL;
+-
+-	return &sp->ovec[sp->olen - 1];
+-#else
+-	return NULL;
+-#endif
+-}
+-
+ void __init xfrm_dev_init(void);
+ 
+ #ifdef CONFIG_XFRM_OFFLOAD
+@@ -1851,6 +1895,9 @@ void xfrm_dev_backlog(struct softnet_dat
+ struct sk_buff *validate_xmit_xfrm(struct sk_buff *skb, netdev_features_t features, bool *again);
+ int xfrm_dev_state_add(struct net *net, struct xfrm_state *x,
+ 		       struct xfrm_user_offload *xuo);
++int xfrm_dev_policy_add(struct net *net, struct xfrm_policy *xp,
++			struct xfrm_user_offload *xuo, u8 dir,
++			struct netlink_ext_ack *extack);
+ bool xfrm_dev_offload_ok(struct sk_buff *skb, struct xfrm_state *x);
+ 
+ static inline void xfrm_dev_state_advance_esn(struct xfrm_state *x)
+@@ -1899,6 +1946,27 @@ static inline void xfrm_dev_state_free(s
+ 		dev_put(dev);
+ 	}
+ }
++
++static inline void xfrm_dev_policy_delete(struct xfrm_policy *x)
++{
++	struct xfrm_state_offload *xdo = &x->xdo;
++	struct net_device *dev = xdo->dev;
++
++	if (dev && dev->xfrmdev_ops && dev->xfrmdev_ops->xdo_dev_policy_delete)
++		dev->xfrmdev_ops->xdo_dev_policy_delete(x);
++}
++
++static inline void xfrm_dev_policy_free(struct xfrm_policy *x)
++{
++	struct xfrm_state_offload *xdo = &x->xdo;
++	struct net_device *dev = xdo->dev;
++
++	if (dev && dev->xfrmdev_ops) {
++		if (dev->xfrmdev_ops->xdo_dev_policy_free)
++			dev->xfrmdev_ops->xdo_dev_policy_free(x);
++		xdo->dev = NULL;
++	}
++}
+ #else
+ static inline void xfrm_dev_resume(struct sk_buff *skb)
+ {
+@@ -1931,6 +1999,21 @@ static inline bool xfrm_dev_offload_ok(s
+ 	return false;
+ }
+ 
++static inline int xfrm_dev_policy_add(struct net *net, struct xfrm_policy *xp,
++				      struct xfrm_user_offload *xuo, u8 dir,
++				      struct netlink_ext_ack *extack)
++{
++	return 0;
++}
++
++static inline void xfrm_dev_policy_delete(struct xfrm_policy *x)
++{
++}
++
++static inline void xfrm_dev_policy_free(struct xfrm_policy *x)
++{
++}
++
+ static inline void xfrm_dev_state_advance_esn(struct xfrm_state *x)
+ {
+ }
+--- a/include/uapi/linux/xfrm.h
++++ b/include/uapi/linux/xfrm.h
+@@ -512,6 +512,12 @@ struct xfrm_user_offload {
+  */
+ #define XFRM_OFFLOAD_IPV6	1
+ #define XFRM_OFFLOAD_INBOUND	2
++/* Two bits above are relevant for state path only, while
++ * offload is used for both policy and state flows.
++ *
++ * In policy offload mode, they are free and can be safely reused.
++ */
++#define XFRM_OFFLOAD_PACKET	4
+ 
+ #ifndef __KERNEL__
+ /* backwards compatibility for userspace */
+--- a/net/xfrm/xfrm_device.c
++++ b/net/xfrm/xfrm_device.c
+@@ -80,6 +80,7 @@ struct sk_buff *validate_xmit_xfrm(struc
+ 	struct softnet_data *sd;
+ 	netdev_features_t esp_features = features;
+ 	struct xfrm_offload *xo = xfrm_offload(skb);
++	struct net_device *dev = skb->dev;
+ 	struct sec_path *sp;
+ 
+ 	if (!xo || (xo->flags & XFRM_XMIT))
+@@ -93,6 +94,17 @@ struct sk_buff *validate_xmit_xfrm(struc
+ 	if (xo->flags & XFRM_GRO || x->xso.flags & XFRM_OFFLOAD_INBOUND)
+ 		return skb;
+ 
++	/* The packet was sent to HW IPsec packet offload engine,
++	 * but to wrong device. Drop the packet, so it won't skip
++	 * XFRM stack.
++	 */
++	if (x->xso.type == XFRM_DEV_OFFLOAD_PACKET && x->xso.dev != dev) {
++		kfree_skb(skb);
++		//dev_core_stats_tx_dropped_inc(dev);
++		atomic_long_inc(&dev->tx_dropped);
++		return NULL;
++	}
++
+ 	local_irq_save(flags);
+ 	sd = this_cpu_ptr(&softnet_data);
+ 	err = !skb_queue_empty(&sd->xfrm_backlog);
+@@ -198,6 +210,7 @@ int xfrm_dev_state_add(struct net *net,
+ 	struct xfrm_state_offload *xso = &x->xso;
+ 	xfrm_address_t *saddr;
+ 	xfrm_address_t *daddr;
++	bool is_packet_offload;
+ 
+ 	if (!x->type_offload)
+ 		return -EINVAL;
+@@ -206,9 +219,11 @@ int xfrm_dev_state_add(struct net *net,
+ 	if (x->encap || x->tfcpad)
+ 		return -EINVAL;
+ 
+-	if (xuo->flags & ~(XFRM_OFFLOAD_IPV6 | XFRM_OFFLOAD_INBOUND))
++	if (xuo->flags &
++	    ~(XFRM_OFFLOAD_IPV6 | XFRM_OFFLOAD_INBOUND | XFRM_OFFLOAD_PACKET))
+ 		return -EINVAL;
+ 
++	is_packet_offload = xuo->flags & XFRM_OFFLOAD_PACKET;
+ 	dev = dev_get_by_index(net, xuo->ifindex);
+ 	if (!dev) {
+ 		if (!(xuo->flags & XFRM_OFFLOAD_INBOUND)) {
+@@ -223,7 +238,7 @@ int xfrm_dev_state_add(struct net *net,
+ 					x->props.family,
+ 					xfrm_smark_get(0, x));
+ 		if (IS_ERR(dst))
+-			return 0;
++			return (is_packet_offload) ? -EINVAL : 0;
+ 
+ 		dev = dst->dev;
+ 
+@@ -234,7 +249,7 @@ int xfrm_dev_state_add(struct net *net,
+ 	if (!dev->xfrmdev_ops || !dev->xfrmdev_ops->xdo_dev_state_add) {
+ 		xso->dev = NULL;
+ 		dev_put(dev);
+-		return 0;
++		return (is_packet_offload) ? -EINVAL : 0;
+ 	}
+ 
+ 	if (x->props.flags & XFRM_STATE_ESN &&
+@@ -249,14 +264,28 @@ int xfrm_dev_state_add(struct net *net,
+ 	/* Don't forward bit that is not implemented */
+ 	xso->flags = xuo->flags & ~XFRM_OFFLOAD_IPV6;
+ 
++	if (is_packet_offload)
++		xso->type = XFRM_DEV_OFFLOAD_PACKET;
++	else
++		xso->type = XFRM_DEV_OFFLOAD_CRYPTO;
++
+ 	err = dev->xfrmdev_ops->xdo_dev_state_add(x);
+ 	if (err) {
+ 		xso->num_exthdrs = 0;
+ 		xso->flags = 0;
+ 		xso->dev = NULL;
+ 		dev_put(dev);
++		xso->type = XFRM_DEV_OFFLOAD_UNSPECIFIED;
+ 
+-		if (err != -EOPNOTSUPP)
++		/* User explicitly requested packet offload mode and configured
++		 * policy in addition to the XFRM state. So be civil to users,
++		 * and return an error instead of taking fallback path.
++		 *
++		 * This WARN_ON() can be seen as a documentation for driver
++		 * authors to do not return -EOPNOTSUPP in packet offload mode.
++		 */
++		WARN_ON(err == -EOPNOTSUPP && is_packet_offload);
++		if (err != -EOPNOTSUPP || is_packet_offload)
+ 			return err;
+ 	}
+ 
+@@ -264,6 +293,65 @@ int xfrm_dev_state_add(struct net *net,
+ }
+ EXPORT_SYMBOL_GPL(xfrm_dev_state_add);
+ 
++int xfrm_dev_policy_add(struct net *net, struct xfrm_policy *xp,
++			struct xfrm_user_offload *xuo, u8 dir,
++			struct netlink_ext_ack *extack)
++{
++	struct xfrm_state_offload *xdo = &xp->xdo;
++	struct net_device *dev;
++	int err;
++
++	if (!xuo->flags || xuo->flags & ~XFRM_OFFLOAD_PACKET) {
++		/* We support only packet offload mode and it means
++		 * that user must set XFRM_OFFLOAD_PACKET bit.
++		 */
++		NL_SET_ERR_MSG(extack, "Unrecognized flags in offload request");
++		return -EINVAL;
++	}
++
++	dev = dev_get_by_index(net, xuo->ifindex);
++	if (!dev)
++		return -EINVAL;
++
++	if (!dev->xfrmdev_ops || !dev->xfrmdev_ops->xdo_dev_policy_add) {
++		xdo->dev = NULL;
++		dev_put(dev);
++		NL_SET_ERR_MSG(extack, "Policy offload is not supported");
++		return -EINVAL;
++	}
++
++	xdo->dev = dev;
++	xdo->type = XFRM_DEV_OFFLOAD_PACKET;
++	switch (dir) {
++	case XFRM_POLICY_IN:
++		xdo->dir = XFRM_DEV_OFFLOAD_IN;
++		break;
++	case XFRM_POLICY_OUT:
++		xdo->dir = XFRM_DEV_OFFLOAD_OUT;
++		break;
++	case XFRM_POLICY_FWD:
++		xdo->dir = XFRM_DEV_OFFLOAD_FWD;
++		break;
++	default:
++		xdo->dev = NULL;
++		dev_put(dev);
++		NL_SET_ERR_MSG(extack, "Unrecognized oflload direction");
++		return -EINVAL;
++	}
++
++	err = dev->xfrmdev_ops->xdo_dev_policy_add(xp);
++	if (err) {
++		xdo->dev = NULL;
++		xdo->type = XFRM_DEV_OFFLOAD_UNSPECIFIED;
++		xdo->dir = 0;
++		NL_SET_ERR_MSG(extack, "Device failed to offload this policy");
++		return err;
++	}
++
++	return 0;
++}
++EXPORT_SYMBOL_GPL(xfrm_dev_policy_add);
++
+ bool xfrm_dev_offload_ok(struct sk_buff *skb, struct xfrm_state *x)
+ {
+ 	int mtu;
+@@ -274,8 +362,9 @@ bool xfrm_dev_offload_ok(struct sk_buff
+ 	if (!x->type_offload || x->encap)
+ 		return false;
+ 
+-	if ((!dev || (dev == xfrm_dst_path(dst)->dev)) &&
+-	    (!xdst->child->xfrm)) {
++	if (x->xso.type == XFRM_DEV_OFFLOAD_PACKET ||
++	    ((!dev || (dev == xfrm_dst_path(dst)->dev)) &&
++	     !xdst->child->xfrm)) {
+ 		mtu = xfrm_state_mtu(x, xdst->child_mtu_cached);
+ 		if (skb->len <= mtu)
+ 			goto ok;
+@@ -376,8 +465,10 @@ static int xfrm_dev_feat_change(struct n
+ 
+ static int xfrm_dev_down(struct net_device *dev)
+ {
+-	if (dev->features & NETIF_F_HW_ESP)
++	if (dev->features & NETIF_F_HW_ESP) {
+ 		xfrm_dev_state_flush(dev_net(dev), dev, true);
++		xfrm_dev_policy_flush(dev_net(dev), dev, true);
++	}
+ 
+ 	return NOTIFY_DONE;
+ }
+--- a/net/xfrm/xfrm_output.c
++++ b/net/xfrm/xfrm_output.c
+@@ -410,7 +410,7 @@ static int xfrm_output_one(struct sk_buf
+ 	struct xfrm_state *x = dst->xfrm;
+ 	struct net *net = xs_net(x);
+ 
+-	if (err <= 0)
++	if (err <= 0 || x->xso.type == XFRM_DEV_OFFLOAD_PACKET)
+ 		goto resume;
+ 
+ 	do {
+@@ -568,6 +568,16 @@ int xfrm_output(struct sock *sk, struct
+ 	struct xfrm_state *x = skb_dst(skb)->xfrm;
+ 	int err;
+ 
++	if (x->xso.type == XFRM_DEV_OFFLOAD_PACKET) {
++		if (!xfrm_dev_offload_ok(skb, x)) {
++			XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTERROR);
++			kfree_skb(skb);
++			return -EHOSTUNREACH;
++		}
++
++		return xfrm_output_resume(skb, 0);
++	}
++
+ 	secpath_reset(skb);
+ 
+ 	if (xfrm_dev_offload_ok(skb, x)) {
+--- a/net/xfrm/xfrm_policy.c
++++ b/net/xfrm/xfrm_policy.c
+@@ -423,6 +423,7 @@ void xfrm_policy_destroy(struct xfrm_pol
+ 	if (del_timer(&policy->timer) || del_timer(&policy->polq.hold_timer))
+ 		BUG();
+ 
++	xfrm_dev_policy_free(policy);
+ 	call_rcu(&policy->rcu, xfrm_policy_destroy_rcu);
+ }
+ EXPORT_SYMBOL(xfrm_policy_destroy);
+@@ -533,7 +534,7 @@ redo:
+ 		__get_hash_thresh(net, pol->family, dir, &dbits, &sbits);
+ 		h = __addr_hash(&pol->selector.daddr, &pol->selector.saddr,
+ 				pol->family, nhashmask, dbits, sbits);
+-		if (!entry0) {
++		if (!entry0 || pol->xdo.type == XFRM_DEV_OFFLOAD_PACKET) {
+ 			hlist_del_rcu(&pol->bydst);
+ 			hlist_add_head_rcu(&pol->bydst, ndsttable + h);
+ 			h0 = h;
+@@ -864,7 +865,7 @@ static void xfrm_policy_inexact_list_rei
+ 				break;
+ 		}
+ 
+-		if (newpos)
++		if (newpos && policy->xdo.type != XFRM_DEV_OFFLOAD_PACKET)
+ 			hlist_add_behind_rcu(&policy->bydst, newpos);
+ 		else
+ 			hlist_add_head_rcu(&policy->bydst, &n->hhead);
+@@ -1345,7 +1346,7 @@ static void xfrm_hash_rebuild(struct wor
+ 			else
+ 				break;
+ 		}
+-		if (newpos)
++		if (newpos && policy->xdo.type != XFRM_DEV_OFFLOAD_PACKET)
+ 			hlist_add_behind_rcu(&policy->bydst, newpos);
+ 		else
+ 			hlist_add_head_rcu(&policy->bydst, chain);
+@@ -1522,7 +1523,7 @@ static void xfrm_policy_insert_inexact_l
+ 			break;
+ 	}
+ 
+-	if (newpos)
++	if (newpos && policy->xdo.type != XFRM_DEV_OFFLOAD_PACKET)
+ 		hlist_add_behind_rcu(&policy->bydst_inexact_list, newpos);
+ 	else
+ 		hlist_add_head_rcu(&policy->bydst_inexact_list, chain);
+@@ -1559,9 +1560,12 @@ static struct xfrm_policy *xfrm_policy_i
+ 			break;
+ 	}
+ 
+-	if (newpos)
++	if (newpos && policy->xdo.type != XFRM_DEV_OFFLOAD_PACKET)
+ 		hlist_add_behind_rcu(&policy->bydst, &newpos->bydst);
+ 	else
++		/* Packet offload policies enter to the head
++		 * to speed-up lookups.
++		 */
+ 		hlist_add_head_rcu(&policy->bydst, chain);
+ 
+ 	return delpol;
+@@ -1767,12 +1771,41 @@ xfrm_policy_flush_secctx_check(struct ne
+ 	}
+ 	return err;
+ }
++
++static inline int xfrm_dev_policy_flush_secctx_check(struct net *net,
++						     struct net_device *dev,
++						     bool task_valid)
++{
++	struct xfrm_policy *pol;
++	int err = 0;
++
++	list_for_each_entry(pol, &net->xfrm.policy_all, walk.all) {
++		if (pol->walk.dead ||
++		    xfrm_policy_id2dir(pol->index) >= XFRM_POLICY_MAX ||
++		    pol->xdo.dev != dev)
++			continue;
++
++		err = security_xfrm_policy_delete(pol->security);
++		if (err) {
++			xfrm_audit_policy_delete(pol, 0, task_valid);
++			return err;
++		}
++	}
++	return err;
++}
+ #else
+ static inline int
+ xfrm_policy_flush_secctx_check(struct net *net, u8 type, bool task_valid)
+ {
+ 	return 0;
+ }
++
++static inline int xfrm_dev_policy_flush_secctx_check(struct net *net,
++						     struct net_device *dev,
++						     bool task_valid)
++{
++	return 0;
++}
+ #endif
+ 
+ int xfrm_policy_flush(struct net *net, u8 type, bool task_valid)
+@@ -1812,6 +1845,44 @@ out:
+ }
+ EXPORT_SYMBOL(xfrm_policy_flush);
+ 
++int xfrm_dev_policy_flush(struct net *net, struct net_device *dev,
++			  bool task_valid)
++{
++	int dir, err = 0, cnt = 0;
++	struct xfrm_policy *pol;
++
++	spin_lock_bh(&net->xfrm.xfrm_policy_lock);
++
++	err = xfrm_dev_policy_flush_secctx_check(net, dev, task_valid);
++	if (err)
++		goto out;
++
++again:
++	list_for_each_entry(pol, &net->xfrm.policy_all, walk.all) {
++		dir = xfrm_policy_id2dir(pol->index);
++		if (pol->walk.dead ||
++		    dir >= XFRM_POLICY_MAX ||
++		    pol->xdo.dev != dev)
++			continue;
++
++		__xfrm_policy_unlink(pol, dir);
++		spin_unlock_bh(&net->xfrm.xfrm_policy_lock);
++		cnt++;
++		xfrm_audit_policy_delete(pol, 1, task_valid);
++		xfrm_policy_kill(pol);
++		spin_lock_bh(&net->xfrm.xfrm_policy_lock);
++		goto again;
++	}
++	if (cnt)
++		__xfrm_policy_inexact_flush(net);
++	else
++		err = -ESRCH;
++out:
++	spin_unlock_bh(&net->xfrm.xfrm_policy_lock);
++	return err;
++}
++EXPORT_SYMBOL(xfrm_dev_policy_flush);
++
+ int xfrm_policy_walk(struct net *net, struct xfrm_policy_walk *walk,
+ 		     int (*func)(struct xfrm_policy *, int, int, void*),
+ 		     void *data)
+@@ -2113,6 +2184,9 @@ static struct xfrm_policy *xfrm_policy_l
+ 			break;
+ 		}
+ 	}
++	if (ret && ret->xdo.type == XFRM_DEV_OFFLOAD_PACKET)
++		goto skip_inexact;
++
+ 	bin = xfrm_policy_inexact_lookup_rcu(net, type, family, dir, if_id);
+ 	if (!bin || !xfrm_policy_find_inexact_candidates(&cand, bin, saddr,
+ 							 daddr))
+@@ -2246,6 +2320,7 @@ int xfrm_policy_delete(struct xfrm_polic
+ 	pol = __xfrm_policy_unlink(pol, dir);
+ 	spin_unlock_bh(&net->xfrm.xfrm_policy_lock);
+ 	if (pol) {
++		xfrm_dev_policy_delete(pol);
+ 		xfrm_policy_kill(pol);
+ 		return 0;
+ 	}
+--- a/net/xfrm/xfrm_state.c
++++ b/net/xfrm/xfrm_state.c
+@@ -78,6 +78,25 @@ xfrm_spi_hash(struct net *net, const xfr
+ 	return __xfrm_spi_hash(daddr, spi, proto, family, net->xfrm.state_hmask);
+ }
+ 
++#define XFRM_STATE_INSERT(by, _n, _h, _type)                               \
++	{                                                                  \
++		struct xfrm_state *_x = NULL;                              \
++									   \
++		if (_type != XFRM_DEV_OFFLOAD_PACKET) {                    \
++			hlist_for_each_entry_rcu(_x, _h, by) {             \
++				if (_x->xso.type == XFRM_DEV_OFFLOAD_PACKET) \
++					continue;                          \
++				break;                                     \
++			}                                                  \
++		}                                                          \
++									   \
++		if (!_x || _x->xso.type == XFRM_DEV_OFFLOAD_PACKET)        \
++			/* SAD is empty or consist from HW SAs only */     \
++			hlist_add_head_rcu(_n, _h);                        \
++		else                                                       \
++			hlist_add_before_rcu(_n, &_x->by);                 \
++	}
++
+ static void xfrm_hash_transfer(struct hlist_head *list,
+ 			       struct hlist_head *ndsttable,
+ 			       struct hlist_head *nsrctable,
+@@ -93,18 +112,20 @@ static void xfrm_hash_transfer(struct hl
+ 		h = __xfrm_dst_hash(&x->id.daddr, &x->props.saddr,
+ 				    x->props.reqid, x->props.family,
+ 				    nhashmask);
+-		hlist_add_head_rcu(&x->bydst, ndsttable + h);
++		XFRM_STATE_INSERT(bydst, &x->bydst, ndsttable + h, x->xso.type);
+ 
+ 		h = __xfrm_src_hash(&x->id.daddr, &x->props.saddr,
+ 				    x->props.family,
+ 				    nhashmask);
+-		hlist_add_head_rcu(&x->bysrc, nsrctable + h);
++		XFRM_STATE_INSERT(bysrc, &x->bysrc, nsrctable + h, x->xso.type);
+ 
+ 		if (x->id.spi) {
+ 			h = __xfrm_spi_hash(&x->id.daddr, x->id.spi,
+ 					    x->id.proto, x->props.family,
+ 					    nhashmask);
+ 			hlist_add_head_rcu(&x->byspi, nspitable + h);
++			XFRM_STATE_INSERT(byspi, &x->byspi, nspitable + h,
++					  x->xso.type);
+ 		}
+ 	}
+ }
+@@ -527,6 +548,8 @@ static enum hrtimer_restart xfrm_timer_h
+ 	int err = 0;
+ 
+ 	spin_lock(&x->lock);
++	xfrm_dev_state_update_curlft(x);
++
+ 	if (x->km.state == XFRM_STATE_DEAD)
+ 		goto out;
+ 	if (x->km.state == XFRM_STATE_EXPIRED)
+@@ -923,6 +946,49 @@ xfrm_init_tempstate(struct xfrm_state *x
+ 	x->props.family = tmpl->encap_family;
+ }
+ 
++static struct xfrm_state *__xfrm_state_lookup_all(struct net *net, u32 mark,
++						  const xfrm_address_t *daddr,
++						  __be32 spi, u8 proto,
++						  unsigned short family,
++						  struct xfrm_state_offload *xdo)
++{
++	unsigned int h = xfrm_spi_hash(net, daddr, spi, proto, family);
++	struct xfrm_state *x;
++
++	hlist_for_each_entry_rcu(x, net->xfrm.state_byspi + h, byspi) {
++#ifdef CONFIG_XFRM_OFFLOAD
++		if (xdo->type == XFRM_DEV_OFFLOAD_PACKET) {
++			if (x->xso.type != XFRM_DEV_OFFLOAD_PACKET)
++				/* HW states are in the head of list, there is
++				 * no need to iterate further.
++				 */
++				break;
++
++			/* Packet offload: both policy and SA should
++			 * have same device.
++			 */
++			if (xdo->dev != x->xso.dev)
++				continue;
++		} else if (x->xso.type == XFRM_DEV_OFFLOAD_PACKET)
++			/* Skip HW policy for SW lookups */
++			continue;
++#endif
++		if (x->props.family != family ||
++		    x->id.spi       != spi ||
++		    x->id.proto     != proto ||
++		    !xfrm_addr_equal(&x->id.daddr, daddr, family))
++			continue;
++
++		if ((mark & x->mark.m) != x->mark.v)
++			continue;
++		if (!xfrm_state_hold_rcu(x))
++			continue;
++		return x;
++	}
++
++	return NULL;
++}
++
+ static struct xfrm_state *__xfrm_state_lookup(struct net *net, u32 mark,
+ 					      const xfrm_address_t *daddr,
+ 					      __be32 spi, u8 proto,
+@@ -1062,6 +1128,23 @@ xfrm_state_find(const xfrm_address_t *da
+ 	rcu_read_lock();
+ 	h = xfrm_dst_hash(net, daddr, saddr, tmpl->reqid, encap_family);
+ 	hlist_for_each_entry_rcu(x, net->xfrm.state_bydst + h, bydst) {
++#ifdef CONFIG_XFRM_OFFLOAD
++		if (pol->xdo.type == XFRM_DEV_OFFLOAD_PACKET) {
++			if (x->xso.type != XFRM_DEV_OFFLOAD_PACKET)
++				/* HW states are in the head of list, there is
++				 * no need to iterate further.
++				 */
++				break;
++
++			/* Packet offload: both policy and SA should
++			 * have same device.
++			 */
++			if (pol->xdo.dev != x->xso.dev)
++				continue;
++		} else if (x->xso.type == XFRM_DEV_OFFLOAD_PACKET)
++			/* Skip HW policy for SW lookups */
++			continue;
++#endif
+ 		if (x->props.family == encap_family &&
+ 		    x->props.reqid == tmpl->reqid &&
+ 		    (mark & x->mark.m) == x->mark.v &&
+@@ -1079,6 +1162,23 @@ xfrm_state_find(const xfrm_address_t *da
+ 
+ 	h_wildcard = xfrm_dst_hash(net, daddr, &saddr_wildcard, tmpl->reqid, encap_family);
+ 	hlist_for_each_entry_rcu(x, net->xfrm.state_bydst + h_wildcard, bydst) {
++#ifdef CONFIG_XFRM_OFFLOAD
++		if (pol->xdo.type == XFRM_DEV_OFFLOAD_PACKET) {
++			if (x->xso.type != XFRM_DEV_OFFLOAD_PACKET)
++				/* HW states are in the head of list, there is
++				 * no need to iterate further.
++				 */
++				break;
++
++			/* Packet offload: both policy and SA should
++			 * have same device.
++			 */
++			if (pol->xdo.dev != x->xso.dev)
++				continue;
++		} else if (x->xso.type == XFRM_DEV_OFFLOAD_PACKET)
++			/* Skip HW policy for SW lookups */
++			continue;
++#endif
+ 		if (x->props.family == encap_family &&
+ 		    x->props.reqid == tmpl->reqid &&
+ 		    (mark & x->mark.m) == x->mark.v &&
+@@ -1096,8 +1196,10 @@ found:
+ 	x = best;
+ 	if (!x && !error && !acquire_in_progress) {
+ 		if (tmpl->id.spi &&
+-		    (x0 = __xfrm_state_lookup(net, mark, daddr, tmpl->id.spi,
+-					      tmpl->id.proto, encap_family)) != NULL) {
++		    (x0 = __xfrm_state_lookup_all(net, mark, daddr,
++						  tmpl->id.spi, tmpl->id.proto,
++						  encap_family,
++						  &pol->xdo)) != NULL) {
+ 			to_put = x0;
+ 			error = -EEXIST;
+ 			goto out;
+@@ -1131,17 +1233,42 @@ found:
+ 			x = NULL;
+ 			goto out;
+ 		}
+-
++#ifdef CONFIG_XFRM_OFFLOAD
++		if (pol->xdo.type == XFRM_DEV_OFFLOAD_PACKET) {
++			struct xfrm_state_offload *xdo = &pol->xdo;
++			struct xfrm_state_offload *xso = &x->xso;
++
++			xso->type = XFRM_DEV_OFFLOAD_PACKET;
++			xso->dir = xdo->dir;
++			xso->dev = xdo->dev;
++			error = xso->dev->xfrmdev_ops->xdo_dev_state_add(x);
++			if (error) {
++				xso->dir = 0;
++				xso->dev = NULL;
++				xso->type = XFRM_DEV_OFFLOAD_UNSPECIFIED;
++				x->km.state = XFRM_STATE_DEAD;
++				to_put = x;
++				x = NULL;
++				goto out;
++			}
++		}
++#endif
+ 		if (km_query(x, tmpl, pol) == 0) {
+ 			spin_lock_bh(&net->xfrm.xfrm_state_lock);
+ 			x->km.state = XFRM_STATE_ACQ;
+ 			list_add(&x->km.all, &net->xfrm.state_all);
+-			hlist_add_head_rcu(&x->bydst, net->xfrm.state_bydst + h);
++			XFRM_STATE_INSERT(bydst, &x->bydst,
++					  net->xfrm.state_bydst + h,
++					  x->xso.type);
+ 			h = xfrm_src_hash(net, daddr, saddr, encap_family);
+-			hlist_add_head_rcu(&x->bysrc, net->xfrm.state_bysrc + h);
++			XFRM_STATE_INSERT(bysrc, &x->bysrc,
++					  net->xfrm.state_bysrc + h,
++					  x->xso.type);
+ 			if (x->id.spi) {
+ 				h = xfrm_spi_hash(net, &x->id.daddr, x->id.spi, x->id.proto, encap_family);
+-				hlist_add_head_rcu(&x->byspi, net->xfrm.state_byspi + h);
++				XFRM_STATE_INSERT(byspi, &x->byspi,
++						  net->xfrm.state_byspi + h,
++						  x->xso.type);
+ 			}
+ 			x->lft.hard_add_expires_seconds = net->xfrm.sysctl_acq_expires;
+ 			hrtimer_start(&x->mtimer,
+@@ -1151,6 +1278,16 @@ found:
+ 			xfrm_hash_grow_check(net, x->bydst.next != NULL);
+ 			spin_unlock_bh(&net->xfrm.xfrm_state_lock);
+ 		} else {
++#ifdef CONFIG_XFRM_OFFLOAD
++			struct xfrm_state_offload *xso = &x->xso;
++
++			if (xso->type == XFRM_DEV_OFFLOAD_PACKET) {
++				xso->dev->xfrmdev_ops->xdo_dev_state_delete(x);
++				xso->dir = 0;
++				xso->dev = NULL;
++				xso->type = XFRM_DEV_OFFLOAD_UNSPECIFIED;
++			}
++#endif
+ 			x->km.state = XFRM_STATE_DEAD;
+ 			to_put = x;
+ 			x = NULL;
+@@ -1246,16 +1383,19 @@ static void __xfrm_state_insert(struct x
+ 
+ 	h = xfrm_dst_hash(net, &x->id.daddr, &x->props.saddr,
+ 			  x->props.reqid, x->props.family);
+-	hlist_add_head_rcu(&x->bydst, net->xfrm.state_bydst + h);
++	XFRM_STATE_INSERT(bydst, &x->bydst, net->xfrm.state_bydst + h,
++			  x->xso.type);
+ 
+ 	h = xfrm_src_hash(net, &x->id.daddr, &x->props.saddr, x->props.family);
+-	hlist_add_head_rcu(&x->bysrc, net->xfrm.state_bysrc + h);
++	XFRM_STATE_INSERT(bysrc, &x->bysrc, net->xfrm.state_bysrc + h,
++			  x->xso.type);
+ 
+ 	if (x->id.spi) {
+ 		h = xfrm_spi_hash(net, &x->id.daddr, x->id.spi, x->id.proto,
+ 				  x->props.family);
+ 
+-		hlist_add_head_rcu(&x->byspi, net->xfrm.state_byspi + h);
++		XFRM_STATE_INSERT(byspi, &x->byspi, net->xfrm.state_byspi + h,
++				  x->xso.type);
+ 	}
+ 
+ 	hrtimer_start(&x->mtimer, ktime_set(1, 0), HRTIMER_MODE_REL_SOFT);
+@@ -1369,9 +1509,11 @@ static struct xfrm_state *__find_acq_cor
+ 			      ktime_set(net->xfrm.sysctl_acq_expires, 0),
+ 			      HRTIMER_MODE_REL_SOFT);
+ 		list_add(&x->km.all, &net->xfrm.state_all);
+-		hlist_add_head_rcu(&x->bydst, net->xfrm.state_bydst + h);
++		XFRM_STATE_INSERT(bydst, &x->bydst, net->xfrm.state_bydst + h,
++				  x->xso.type);
+ 		h = xfrm_src_hash(net, daddr, saddr, family);
+-		hlist_add_head_rcu(&x->bysrc, net->xfrm.state_bysrc + h);
++		XFRM_STATE_INSERT(bysrc, &x->bysrc, net->xfrm.state_bysrc + h,
++				  x->xso.type);
+ 
+ 		net->xfrm.state_num++;
+ 
+@@ -1742,6 +1884,8 @@ EXPORT_SYMBOL(xfrm_state_update);
+ 
+ int xfrm_state_check_expire(struct xfrm_state *x)
+ {
++	xfrm_dev_state_update_curlft(x);
++
+ 	if (!x->curlft.use_time)
+ 		x->curlft.use_time = ktime_get_real_seconds();
+ 
+@@ -2043,7 +2187,8 @@ int xfrm_alloc_spi(struct xfrm_state *x,
+ 		spin_lock_bh(&net->xfrm.xfrm_state_lock);
+ 		x->id.spi = newspi;
+ 		h = xfrm_spi_hash(net, &x->id.daddr, x->id.spi, x->id.proto, x->props.family);
+-		hlist_add_head_rcu(&x->byspi, net->xfrm.state_byspi + h);
++		XFRM_STATE_INSERT(byspi, &x->byspi, net->xfrm.state_byspi + h,
++				  x->xso.type);
+ 		spin_unlock_bh(&net->xfrm.xfrm_state_lock);
+ 
+ 		err = 0;
+--- a/net/xfrm/xfrm_user.c
++++ b/net/xfrm/xfrm_user.c
+@@ -844,6 +844,8 @@ static int copy_user_offload(struct xfrm
+ 	memset(xuo, 0, sizeof(*xuo));
+ 	xuo->ifindex = xso->dev->ifindex;
+ 	xuo->flags = xso->flags;
++	if (xso->type == XFRM_DEV_OFFLOAD_PACKET)
++		xuo->flags |= XFRM_OFFLOAD_PACKET;
+ 
+ 	return 0;
+ }
+@@ -1634,6 +1636,15 @@ static struct xfrm_policy *xfrm_policy_c
+ 	if (attrs[XFRMA_IF_ID])
+ 		xp->if_id = nla_get_u32(attrs[XFRMA_IF_ID]);
+ 
++	/* configure the hardware if offload is requested */
++	if (attrs[XFRMA_OFFLOAD_DEV]) {
++		err = xfrm_dev_policy_add(net, xp,
++					  nla_data(attrs[XFRMA_OFFLOAD_DEV]),
++					  p->dir, 0);
++		if (err)
++			goto error;
++	}
++
+ 	return xp;
+  error:
+ 	*errp = err;
+@@ -1672,6 +1683,7 @@ static int xfrm_add_policy(struct sk_buf
+ 	xfrm_audit_policy_add(xp, err ? 0 : 1, true);
+ 
+ 	if (err) {
++		xfrm_dev_policy_delete(xp);
+ 		security_xfrm_policy_free(xp->security);
+ 		kfree(xp);
+ 		return err;
+@@ -1783,6 +1795,8 @@ static int dump_one_policy(struct xfrm_p
+ 		err = xfrm_mark_put(skb, &xp->mark);
+ 	if (!err)
+ 		err = xfrm_if_id_put(skb, xp->if_id);
++	if (!err && xp->xdo.dev)
++		err = copy_user_offload(&xp->xdo, skb);
+ 	if (err) {
+ 		nlmsg_cancel(skb, nlh);
+ 		return err;
+@@ -2958,6 +2972,8 @@ static int build_acquire(struct sk_buff
+ 		err = xfrm_mark_put(skb, &xp->mark);
+ 	if (!err)
+ 		err = xfrm_if_id_put(skb, xp->if_id);
++	if (!err && xp->xdo.dev)
++		err = copy_user_offload(&xp->xdo, skb);
+ 	if (err) {
+ 		nlmsg_cancel(skb, nlh);
+ 		return err;
+@@ -3076,6 +3092,8 @@ static int build_polexpire(struct sk_buf
+ 		err = xfrm_mark_put(skb, &xp->mark);
+ 	if (!err)
+ 		err = xfrm_if_id_put(skb, xp->if_id);
++	if (!err && xp->xdo.dev)
++		err = copy_user_offload(&xp->xdo, skb);
+ 	if (err) {
+ 		nlmsg_cancel(skb, nlh);
+ 		return err;
+@@ -3159,6 +3177,8 @@ static int xfrm_notify_policy(struct xfr
+ 		err = xfrm_mark_put(skb, &xp->mark);
+ 	if (!err)
+ 		err = xfrm_if_id_put(skb, xp->if_id);
++	if (!err && xp->xdo.dev)
++		err = copy_user_offload(&xp->xdo, skb);
+ 	if (err)
+ 		goto out_free_skb;
+ 
diff --git a/target/linux/mediatek/patches-5.4/999-2728-xfrm-extend-packet-mode-to-support-esp-tunnel-mode.patch b/target/linux/mediatek/patches-5.4/999-2728-xfrm-extend-packet-mode-to-support-esp-tunnel-mode.patch
new file mode 100644
index 0000000..ba9bc98
--- /dev/null
+++ b/target/linux/mediatek/patches-5.4/999-2728-xfrm-extend-packet-mode-to-support-esp-tunnel-mode.patch
@@ -0,0 +1,28 @@
+--- a/net/xfrm/xfrm_output.c
++++ b/net/xfrm/xfrm_output.c
+@@ -410,7 +410,7 @@ static int xfrm_output_one(struct sk_buf
+ 	struct xfrm_state *x = dst->xfrm;
+ 	struct net *net = xs_net(x);
+ 
+-	if (err <= 0 || x->xso.type == XFRM_DEV_OFFLOAD_PACKET)
++	if (err <= 0)
+ 		goto resume;
+ 
+ 	do {
+@@ -568,16 +568,6 @@ int xfrm_output(struct sock *sk, struct
+ 	struct xfrm_state *x = skb_dst(skb)->xfrm;
+ 	int err;
+ 
+-	if (x->xso.type == XFRM_DEV_OFFLOAD_PACKET) {
+-		if (!xfrm_dev_offload_ok(skb, x)) {
+-			XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTERROR);
+-			kfree_skb(skb);
+-			return -EHOSTUNREACH;
+-		}
+-
+-		return xfrm_output_resume(skb, 0);
+-	}
+-
+ 	secpath_reset(skb);
+ 
+ 	if (xfrm_dev_offload_ok(skb, x)) {
diff --git a/target/linux/mediatek/patches-5.4/999-4103-mtk-crypto-esp-offload-support.patch b/target/linux/mediatek/patches-5.4/999-4103-mtk-crypto-esp-offload-support.patch
new file mode 100644
index 0000000..a77ef85
--- /dev/null
+++ b/target/linux/mediatek/patches-5.4/999-4103-mtk-crypto-esp-offload-support.patch
@@ -0,0 +1,168 @@
+--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+@@ -2316,6 +2316,7 @@ static int mtk_poll_rx(struct napi_struc
+ 
+ 		skb_hnat_alg(skb) = 0;
+ 		skb_hnat_filled(skb) = 0;
++		skb_hnat_set_cdrt(skb, 0);
+ 		skb_hnat_magic_tag(skb) = HNAT_MAGIC_TAG;
+ 		skb_hnat_set_tops(skb, 0);
+ 		skb_hnat_set_is_decap(skb, 0);
+--- a/drivers/net/ethernet/mediatek/mtk_hnat/hnat_nf_hook.c
++++ b/drivers/net/ethernet/mediatek/mtk_hnat/hnat_nf_hook.c
+@@ -1075,6 +1075,9 @@ static unsigned int hnat_ipv4_get_nextho
+ 		return 0;
+ 	}
+ 
++	if (!skb_hnat_cdrt(skb) && dst && dst_xfrm(dst))
++		return 0;
++
+ 	rcu_read_lock_bh();
+ 	nexthop = (__force u32)rt_nexthop(rt, ip_hdr(skb)->daddr);
+ 	neigh = __ipv4_neigh_lookup_noref(dev, nexthop);
+@@ -1096,7 +1099,7 @@ static unsigned int hnat_ipv4_get_nextho
+ 	 * outer header, we must update its outer mac header pointer
+ 	 * before filling outer mac or it may screw up inner mac
+ 	 */
+-	if (skb_hnat_tops(skb) && skb_hnat_is_encap(skb)) {
++	if ((skb_hnat_tops(skb) && skb_hnat_is_encap(skb)) || skb_hnat_cdrt(skb)) {
+ 		skb_push(skb, sizeof(struct ethhdr));
+ 		skb_reset_mac_header(skb);
+ 	}
+@@ -1104,7 +1107,7 @@ static unsigned int hnat_ipv4_get_nextho
+ 	memcpy(eth_hdr(skb)->h_dest, neigh->ha, ETH_ALEN);
+ 	memcpy(eth_hdr(skb)->h_source, out->dev_addr, ETH_ALEN);
+ 
+-	if (skb_hnat_tops(skb) && skb_hnat_is_encap(skb))
++	if ((skb_hnat_tops(skb) && skb_hnat_is_encap(skb)) || skb_hnat_cdrt(skb))
+ 		skb_pull(skb, sizeof(struct ethhdr));
+ 
+ 	rcu_read_unlock_bh();
+@@ -1289,6 +1292,9 @@ static inline void hnat_fill_offload_eng
+ 		 */
+ 		entry->ipv4_hnapt.tport_id = NR_TDMA_QDMA_TPORT;
+ 		entry->ipv4_hnapt.tops_entry = skb_hnat_tops(skb);
++	} else if (skb_hnat_cdrt(skb)) {
++		entry->ipv4_hnapt.tport_id = NR_EIP197_QDMA_TPORT;
++		entry->ipv4_hnapt.cdrt_id = skb_hnat_cdrt(skb);
+ 	} else {
+ 		return;
+ 	}
+@@ -1334,7 +1340,8 @@ static unsigned int skb_to_hnat_info(str
+ 	if (whnat && is_hnat_pre_filled(foe))
+ 		return 0;
+ 
+-	if (skb_hnat_tops(skb) && !(hw_path->flags & FLOW_OFFLOAD_PATH_TNL)) {
++	if ((skb_hnat_tops(skb) && !(hw_path->flags & FLOW_OFFLOAD_PATH_TNL))
++	    || (skb_hnat_cdrt(skb) && skb_dst(skb) && !dst_xfrm(skb_dst(skb)))) {
+ 		hnat_get_filled_unbind_entry(skb, &entry);
+ 		goto hnat_entry_bind;
+ 	}
+@@ -1734,7 +1741,8 @@ static unsigned int skb_to_hnat_info(str
+ 	/* Fill Layer2 Info.*/
+ 	entry = ppe_fill_L2_info(eth, entry, hw_path);
+ 
+-	if (skb_hnat_tops(skb) && hw_path->flags & FLOW_OFFLOAD_PATH_TNL)
++	if ((skb_hnat_tops(skb) && hw_path->flags & FLOW_OFFLOAD_PATH_TNL)
++	    || (!skb_hnat_cdrt(skb) && skb_dst(skb) && dst_xfrm(skb_dst(skb))))
+ 		goto hnat_entry_skip_bind;
+ 
+ hnat_entry_bind:
+@@ -1938,6 +1946,8 @@ hnat_entry_bind:
+ 
+ #if defined(CONFIG_MEDIATEK_NETSYS_V3)
+ 	hnat_fill_offload_engine_entry(skb, &entry, dev);
++	if (skb_hnat_cdrt(skb))
++		entry = ppe_fill_L2_info(eth, entry, hw_path);
+ #endif
+ 
+ hnat_entry_skip_bind:
+@@ -2215,6 +2225,7 @@ int mtk_sw_nat_hook_rx(struct sk_buff *s
+ 
+ 	skb_hnat_alg(skb) = 0;
+ 	skb_hnat_set_tops(skb, 0);
++	skb_hnat_set_cdrt(skb, 0);
+ 	skb_hnat_magic_tag(skb) = HNAT_MAGIC_TAG;
+ 
+ 	if (skb_hnat_iface(skb) == FOE_MAGIC_WED0)
+@@ -2301,7 +2312,8 @@ static unsigned int mtk_hnat_accel_type(
+ 	 * is from local_out which is also filtered in sanity check.
+ 	 */
+ 	dst = skb_dst(skb);
+-	if (dst && dst_xfrm(dst))
++	if (dst && dst_xfrm(dst)
++	    && (!mtk_crypto_offloadable || !mtk_crypto_offloadable(skb)))
+ 		return 0;
+ 
+ 	ct = nf_ct_get(skb, &ctinfo);
+@@ -2993,7 +3005,10 @@ mtk_hnat_ipv4_nf_local_out(void *priv, s
+ 	if (iph->protocol == IPPROTO_IPV6) {
+ 		entry->udib1.pkt_type = IPV6_6RD;
+ 		hnat_set_head_frags(state, skb, 0, hnat_set_alg);
+-	} else if (!skb_hnat_tops(skb)) {
++	} else if (is_magic_tag_valid(skb)
++		   && (skb_hnat_cdrt(skb) || skb_hnat_tops(skb))) {
++		hnat_set_head_frags(state, skb, 0, hnat_set_alg);
++	} else {
+ 		hnat_set_head_frags(state, skb, 1, hnat_set_alg);
+ 	}
+ 
+--- a/drivers/net/ethernet/mediatek/mtk_hnat/nf_hnat_mtk.h
++++ b/drivers/net/ethernet/mediatek/mtk_hnat/nf_hnat_mtk.h
+@@ -46,7 +46,8 @@ struct hnat_desc {
+ 	u32 amsdu : 1;
+ 	u32 tops : 6;
+ 	u32 is_decap : 1;
+-	u32 resv3 : 12;
++	u32 cdrt : 8;
++	u32 resv3 : 4;
+ 	u32 magic_tag_protect : 16;
+ } __packed;
+ #elif defined(CONFIG_MEDIATEK_NETSYS_RX_V2)
+@@ -99,12 +100,16 @@ struct hnat_desc {
+ #define skb_hnat_is_encap(skb) (!skb_hnat_is_decap(skb))
+ #define skb_hnat_set_tops(skb, tops) ((skb_hnat_tops(skb)) = (tops))
+ #define skb_hnat_set_is_decap(skb, is_decap) ((skb_hnat_is_decap(skb)) = (is_decap))
++#define skb_hnat_cdrt(skb) (((struct hnat_desc *)((skb)->head))->cdrt)
++#define skb_hnat_set_cdrt(skb, cdrt) ((skb_hnat_cdrt(skb)) = (cdrt))
+ #else /* !defined(CONFIG_MEDIATEK_NETSYS_V3) */
+ #define skb_hnat_tops(skb) (0)
+ #define skb_hnat_is_decap(skb) (0)
+ #define skb_hnat_is_encap(skb) (0)
+ #define skb_hnat_set_tops(skb, tops)
+ #define skb_hnat_set_is_decap(skb, is_decap)
++#define skb_hnat_cdrt(skb) (0)
++#define skb_hnat_set_cdrt(skb, cdrt)
+ #endif /* defined(CONFIG_MEDIATEK_NETSYS_V3) */
+ #define skb_hnat_magic(skb) (((struct hnat_desc *)(skb->head))->magic)
+ #define skb_hnat_reason(skb) (((struct hnat_desc *)(skb->head))->crsn)
+--- a/drivers/net/ethernet/mediatek/mtk_hnat/hnat.c
++++ b/drivers/net/ethernet/mediatek/mtk_hnat/hnat.c
+@@ -49,6 +49,8 @@ int (*mtk_tnl_decap_offload)(struct sk_b
+ EXPORT_SYMBOL(mtk_tnl_decap_offload);
+ bool (*mtk_tnl_decap_offloadable)(struct sk_buff *skb) = NULL;
+ EXPORT_SYMBOL(mtk_tnl_decap_offloadable);
++bool (*mtk_crypto_offloadable)(struct sk_buff *skb) = NULL;
++EXPORT_SYMBOL(mtk_crypto_offloadable);
+ 
+ static void hnat_sma_build_entry(struct timer_list *t)
+ {
+--- a/drivers/net/ethernet/mediatek/mtk_hnat/hnat.h
++++ b/drivers/net/ethernet/mediatek/mtk_hnat/hnat.h
+@@ -1087,6 +1087,8 @@ enum FoeIpAct {
+ #define NR_WDMA1_PORT 9
+ #define NR_WDMA2_PORT 13
+ #define NR_GMAC3_PORT 15
++#define NR_EIP197_TPORT 2
++#define NR_EIP197_QDMA_TPORT 3
+ #define NR_TDMA_TPORT 4
+ #define NR_TDMA_QDMA_TPORT 5
+ #define LAN_DEV_NAME hnat_priv->lan
+@@ -1233,6 +1235,7 @@ extern int qos_toggle;
+ extern int (*mtk_tnl_encap_offload)(struct sk_buff *skb);
+ extern int (*mtk_tnl_decap_offload)(struct sk_buff *skb);
+ extern bool (*mtk_tnl_decap_offloadable)(struct sk_buff *skb);
++extern bool (*mtk_crypto_offloadable)(struct sk_buff *skb);
+ 
+ int ext_if_add(struct extdev_entry *ext_entry);
+ int ext_if_del(struct extdev_entry *ext_entry);