@@ -120,6 +120,16 @@ struct sk_buff *validate_xmit_xfrm(struct sk_buff *skb, netdev_features_t featur
if (xo->flags & XFRM_GRO || x->xso.dir == XFRM_DEV_OFFLOAD_IN)
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);
+ return NULL;
+ }
+
/* This skb was already validated on the upper/virtual dev */
if ((x->xso.dev != dev) && (x->xso.real_dev == dev))
return skb;
@@ -385,8 +395,9 @@ bool xfrm_dev_offload_ok(struct sk_buff *skb, struct xfrm_state *x)
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;
@@ -494,7 +494,7 @@ static int xfrm_output_one(struct sk_buff *skb, int err)
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 {
@@ -718,6 +718,16 @@ int xfrm_output(struct sock *sk, struct sk_buff *skb)
break;
}
+ 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(sk, skb, 0);
+ }
+
secpath_reset(skb);
if (xfrm_dev_offload_ok(skb, x)) {
@@ -2619,6 +2619,7 @@ static struct dst_entry *xfrm_bundle_create(struct xfrm_policy *policy,
int tos;
int family = policy->selector.family;
xfrm_address_t saddr, daddr;
+ struct dst_entry *dst1;
xfrm_flowi_addr_get(fl, &saddr, &daddr, family);
@@ -2628,7 +2629,8 @@ static struct dst_entry *xfrm_bundle_create(struct xfrm_policy *policy,
for (; i < nx; i++) {
struct xfrm_dst *xdst = xfrm_alloc_dst(net, family);
- struct dst_entry *dst1 = &xdst->u.dst;
+
+ dst1 = &xdst->u.dst;
err = PTR_ERR(xdst);
if (IS_ERR(xdst)) {
@@ -2708,6 +2710,23 @@ static struct dst_entry *xfrm_bundle_create(struct xfrm_policy *policy,
if (!dev)
goto free_dst;
+ dst1 = &xdst0->u.dst;
+ /* Packet offload: both policy and SA should be offloaded */
+ if ((policy->xdo.type == XFRM_DEV_OFFLOAD_PACKET &&
+ dst1->xfrm->xso.type != XFRM_DEV_OFFLOAD_PACKET) ||
+ (policy->xdo.type != XFRM_DEV_OFFLOAD_PACKET &&
+ dst1->xfrm->xso.type == XFRM_DEV_OFFLOAD_PACKET)) {
+ err = -EINVAL;
+ goto free_dst;
+ }
+
+ /* Packet offload: both policy and SA should have same device */
+ if (policy->xdo.type == XFRM_DEV_OFFLOAD_PACKET &&
+ policy->xdo.dev != dst1->xfrm->xso.dev) {
+ err = -EINVAL;
+ goto free_dst;
+ }
+
xfrm_init_path(xdst0, dst, nfheader_len);
xfrm_init_pmtu(bundle, nx);