12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 221f42cc9SSteffen Klassert /* 321f42cc9SSteffen Klassert * xfrm_device.c - IPsec device offloading code. 421f42cc9SSteffen Klassert * 521f42cc9SSteffen Klassert * Copyright (c) 2015 secunet Security Networks AG 621f42cc9SSteffen Klassert * 721f42cc9SSteffen Klassert * Author: 821f42cc9SSteffen Klassert * Steffen Klassert <steffen.klassert@secunet.com> 921f42cc9SSteffen Klassert */ 1021f42cc9SSteffen Klassert 1121f42cc9SSteffen Klassert #include <linux/errno.h> 1221f42cc9SSteffen Klassert #include <linux/module.h> 1321f42cc9SSteffen Klassert #include <linux/netdevice.h> 1421f42cc9SSteffen Klassert #include <linux/skbuff.h> 1521f42cc9SSteffen Klassert #include <linux/slab.h> 1621f42cc9SSteffen Klassert #include <linux/spinlock.h> 1721f42cc9SSteffen Klassert #include <net/dst.h> 1821f42cc9SSteffen Klassert #include <net/xfrm.h> 1921f42cc9SSteffen Klassert #include <linux/notifier.h> 2021f42cc9SSteffen Klassert 21b81f884aSHangbin Liu #ifdef CONFIG_XFRM_OFFLOAD 22303c5fabSFlorian Westphal static void __xfrm_transport_prep(struct xfrm_state *x, struct sk_buff *skb, 23303c5fabSFlorian Westphal unsigned int hsize) 24303c5fabSFlorian Westphal { 25303c5fabSFlorian Westphal struct xfrm_offload *xo = xfrm_offload(skb); 26303c5fabSFlorian Westphal 27303c5fabSFlorian Westphal skb_reset_mac_len(skb); 2806a0afcfSXin Long if (xo->flags & XFRM_GSO_SEGMENT) 29303c5fabSFlorian Westphal skb->transport_header -= x->props.header_len; 3006a0afcfSXin Long 3106a0afcfSXin Long pskb_pull(skb, skb_transport_offset(skb) + x->props.header_len); 32303c5fabSFlorian Westphal } 33303c5fabSFlorian Westphal 34303c5fabSFlorian Westphal static void __xfrm_mode_tunnel_prep(struct xfrm_state *x, struct sk_buff *skb, 35303c5fabSFlorian Westphal unsigned int hsize) 36303c5fabSFlorian Westphal 37303c5fabSFlorian Westphal { 38303c5fabSFlorian Westphal struct xfrm_offload *xo = xfrm_offload(skb); 39303c5fabSFlorian Westphal 40303c5fabSFlorian Westphal if (xo->flags & XFRM_GSO_SEGMENT) 41303c5fabSFlorian Westphal skb->transport_header = skb->network_header + hsize; 42303c5fabSFlorian Westphal 43303c5fabSFlorian Westphal skb_reset_mac_len(skb); 44303c5fabSFlorian Westphal pskb_pull(skb, skb->mac_len + x->props.header_len); 45303c5fabSFlorian Westphal } 46303c5fabSFlorian Westphal 4730849175SXin Long static void __xfrm_mode_beet_prep(struct xfrm_state *x, struct sk_buff *skb, 4830849175SXin Long unsigned int hsize) 4930849175SXin Long { 5030849175SXin Long struct xfrm_offload *xo = xfrm_offload(skb); 5130849175SXin Long int phlen = 0; 5230849175SXin Long 5330849175SXin Long if (xo->flags & XFRM_GSO_SEGMENT) 5430849175SXin Long skb->transport_header = skb->network_header + hsize; 5530849175SXin Long 5630849175SXin Long skb_reset_mac_len(skb); 5730849175SXin Long if (x->sel.family != AF_INET6) { 5830849175SXin Long phlen = IPV4_BEET_PHMAXLEN; 5930849175SXin Long if (x->outer_mode.family == AF_INET6) 6030849175SXin Long phlen += sizeof(struct ipv6hdr) - sizeof(struct iphdr); 6130849175SXin Long } 6230849175SXin Long 6330849175SXin Long pskb_pull(skb, skb->mac_len + hsize + (x->props.header_len - phlen)); 6430849175SXin Long } 6530849175SXin Long 66303c5fabSFlorian Westphal /* Adjust pointers into the packet when IPsec is done at layer2 */ 67303c5fabSFlorian Westphal static void xfrm_outer_mode_prep(struct xfrm_state *x, struct sk_buff *skb) 68303c5fabSFlorian Westphal { 69c9500d7bSFlorian Westphal switch (x->outer_mode.encap) { 70303c5fabSFlorian Westphal case XFRM_MODE_TUNNEL: 71c9500d7bSFlorian Westphal if (x->outer_mode.family == AF_INET) 72303c5fabSFlorian Westphal return __xfrm_mode_tunnel_prep(x, skb, 73303c5fabSFlorian Westphal sizeof(struct iphdr)); 74c9500d7bSFlorian Westphal if (x->outer_mode.family == AF_INET6) 75303c5fabSFlorian Westphal return __xfrm_mode_tunnel_prep(x, skb, 76303c5fabSFlorian Westphal sizeof(struct ipv6hdr)); 77303c5fabSFlorian Westphal break; 78303c5fabSFlorian Westphal case XFRM_MODE_TRANSPORT: 79c9500d7bSFlorian Westphal if (x->outer_mode.family == AF_INET) 80303c5fabSFlorian Westphal return __xfrm_transport_prep(x, skb, 81303c5fabSFlorian Westphal sizeof(struct iphdr)); 82c9500d7bSFlorian Westphal if (x->outer_mode.family == AF_INET6) 83303c5fabSFlorian Westphal return __xfrm_transport_prep(x, skb, 84303c5fabSFlorian Westphal sizeof(struct ipv6hdr)); 85303c5fabSFlorian Westphal break; 8630849175SXin Long case XFRM_MODE_BEET: 8730849175SXin Long if (x->outer_mode.family == AF_INET) 8830849175SXin Long return __xfrm_mode_beet_prep(x, skb, 8930849175SXin Long sizeof(struct iphdr)); 9030849175SXin Long if (x->outer_mode.family == AF_INET6) 9130849175SXin Long return __xfrm_mode_beet_prep(x, skb, 9230849175SXin Long sizeof(struct ipv6hdr)); 9330849175SXin Long break; 94303c5fabSFlorian Westphal case XFRM_MODE_ROUTEOPTIMIZATION: 95303c5fabSFlorian Westphal case XFRM_MODE_IN_TRIGGER: 96303c5fabSFlorian Westphal break; 97303c5fabSFlorian Westphal } 98303c5fabSFlorian Westphal } 99303c5fabSFlorian Westphal 100f53c7239SSteffen Klassert struct sk_buff *validate_xmit_xfrm(struct sk_buff *skb, netdev_features_t features, bool *again) 101f6e27114SSteffen Klassert { 102f6e27114SSteffen Klassert int err; 103f53c7239SSteffen Klassert unsigned long flags; 104f6e27114SSteffen Klassert struct xfrm_state *x; 105f53c7239SSteffen Klassert struct softnet_data *sd; 106d1d17a35SXin Long struct sk_buff *skb2, *nskb, *pskb = NULL; 1073dca3f38SSteffen Klassert netdev_features_t esp_features = features; 108f6e27114SSteffen Klassert struct xfrm_offload *xo = xfrm_offload(skb); 109272c2330SJarod Wilson struct net_device *dev = skb->dev; 1102294be0fSFlorian Westphal struct sec_path *sp; 111f6e27114SSteffen Klassert 11294579ac3SHuy Nguyen if (!xo || (xo->flags & XFRM_XMIT)) 1133dca3f38SSteffen Klassert return skb; 114f6e27114SSteffen Klassert 1153dca3f38SSteffen Klassert if (!(features & NETIF_F_HW_ESP)) 1163dca3f38SSteffen Klassert esp_features = features & ~(NETIF_F_SG | NETIF_F_CSUM_MASK); 1173dca3f38SSteffen Klassert 1182294be0fSFlorian Westphal sp = skb_sec_path(skb); 1192294be0fSFlorian Westphal x = sp->xvec[sp->len - 1]; 120482db2f1SLeon Romanovsky if (xo->flags & XFRM_GRO || x->xso.dir == XFRM_DEV_OFFLOAD_IN) 1213dca3f38SSteffen Klassert return skb; 122f6e27114SSteffen Klassert 123bdfd2d1fSJarod Wilson /* This skb was already validated on the upper/virtual dev */ 124bdfd2d1fSJarod Wilson if ((x->xso.dev != dev) && (x->xso.real_dev == dev)) 125272c2330SJarod Wilson return skb; 126272c2330SJarod Wilson 127f53c7239SSteffen Klassert local_irq_save(flags); 128f53c7239SSteffen Klassert sd = this_cpu_ptr(&softnet_data); 129f53c7239SSteffen Klassert err = !skb_queue_empty(&sd->xfrm_backlog); 130f53c7239SSteffen Klassert local_irq_restore(flags); 131f53c7239SSteffen Klassert 132f53c7239SSteffen Klassert if (err) { 133f53c7239SSteffen Klassert *again = true; 134f53c7239SSteffen Klassert return skb; 135f53c7239SSteffen Klassert } 136f53c7239SSteffen Klassert 137272c2330SJarod Wilson if (skb_is_gso(skb) && unlikely(x->xso.dev != dev)) { 1383dca3f38SSteffen Klassert struct sk_buff *segs; 1393dca3f38SSteffen Klassert 1403dca3f38SSteffen Klassert /* Packet got rerouted, fixup features and segment it. */ 141272c2330SJarod Wilson esp_features = esp_features & ~(NETIF_F_HW_ESP | NETIF_F_GSO_ESP); 1423dca3f38SSteffen Klassert 1433dca3f38SSteffen Klassert segs = skb_gso_segment(skb, esp_features); 1443dca3f38SSteffen Klassert if (IS_ERR(segs)) { 1453dca3f38SSteffen Klassert kfree_skb(skb); 146625788b5SEric Dumazet dev_core_stats_tx_dropped_inc(dev); 1473dca3f38SSteffen Klassert return NULL; 1483dca3f38SSteffen Klassert } else { 1493dca3f38SSteffen Klassert consume_skb(skb); 1503dca3f38SSteffen Klassert skb = segs; 1513dca3f38SSteffen Klassert } 1523dca3f38SSteffen Klassert } 1533dca3f38SSteffen Klassert 1543dca3f38SSteffen Klassert if (!skb->next) { 15565fd2c2aSBoris Pismenny esp_features |= skb->dev->gso_partial_features; 156303c5fabSFlorian Westphal xfrm_outer_mode_prep(x, skb); 157f6e27114SSteffen Klassert 158f53c7239SSteffen Klassert xo->flags |= XFRM_DEV_RESUME; 159f53c7239SSteffen Klassert 1603dca3f38SSteffen Klassert err = x->type_offload->xmit(x, skb, esp_features); 161f6e27114SSteffen Klassert if (err) { 162f53c7239SSteffen Klassert if (err == -EINPROGRESS) 163f53c7239SSteffen Klassert return NULL; 164f53c7239SSteffen Klassert 165f6e27114SSteffen Klassert XFRM_INC_STATS(xs_net(x), LINUX_MIB_XFRMOUTSTATEPROTOERROR); 1663dca3f38SSteffen Klassert kfree_skb(skb); 1673dca3f38SSteffen Klassert return NULL; 168f6e27114SSteffen Klassert } 169f6e27114SSteffen Klassert 170f6e27114SSteffen Klassert skb_push(skb, skb->data - skb_mac_header(skb)); 1713dca3f38SSteffen Klassert 1723dca3f38SSteffen Klassert return skb; 173f6e27114SSteffen Klassert } 174f6e27114SSteffen Klassert 175c3b18e0dSJason A. Donenfeld skb_list_walk_safe(skb, skb2, nskb) { 17665fd2c2aSBoris Pismenny esp_features |= skb->dev->gso_partial_features; 177a8305bffSDavid S. Miller skb_mark_not_on_list(skb2); 1783dca3f38SSteffen Klassert 1793dca3f38SSteffen Klassert xo = xfrm_offload(skb2); 180f53c7239SSteffen Klassert xo->flags |= XFRM_DEV_RESUME; 1813dca3f38SSteffen Klassert 182303c5fabSFlorian Westphal xfrm_outer_mode_prep(x, skb2); 1833dca3f38SSteffen Klassert 1843dca3f38SSteffen Klassert err = x->type_offload->xmit(x, skb2, esp_features); 185f53c7239SSteffen Klassert if (!err) { 186f53c7239SSteffen Klassert skb2->next = nskb; 187f53c7239SSteffen Klassert } else if (err != -EINPROGRESS) { 1883dca3f38SSteffen Klassert XFRM_INC_STATS(xs_net(x), LINUX_MIB_XFRMOUTSTATEPROTOERROR); 1893dca3f38SSteffen Klassert skb2->next = nskb; 1903dca3f38SSteffen Klassert kfree_skb_list(skb2); 1913dca3f38SSteffen Klassert return NULL; 192f53c7239SSteffen Klassert } else { 193f53c7239SSteffen Klassert if (skb == skb2) 194f53c7239SSteffen Klassert skb = nskb; 195d1d17a35SXin Long else 196d1d17a35SXin Long pskb->next = nskb; 197f53c7239SSteffen Klassert 198c3b18e0dSJason A. Donenfeld continue; 199f53c7239SSteffen Klassert } 2003dca3f38SSteffen Klassert 2013dca3f38SSteffen Klassert skb_push(skb2, skb2->data - skb_mac_header(skb2)); 202d1d17a35SXin Long pskb = skb2; 203c3b18e0dSJason A. Donenfeld } 2043dca3f38SSteffen Klassert 2053dca3f38SSteffen Klassert return skb; 206f6e27114SSteffen Klassert } 207f6e27114SSteffen Klassert EXPORT_SYMBOL_GPL(validate_xmit_xfrm); 208f6e27114SSteffen Klassert 209d77e38e6SSteffen Klassert int xfrm_dev_state_add(struct net *net, struct xfrm_state *x, 210*adb5c33eSSabrina Dubroca struct xfrm_user_offload *xuo, 211*adb5c33eSSabrina Dubroca struct netlink_ext_ack *extack) 212d77e38e6SSteffen Klassert { 213d77e38e6SSteffen Klassert int err; 214d77e38e6SSteffen Klassert struct dst_entry *dst; 215d77e38e6SSteffen Klassert struct net_device *dev; 21687e0a94eSLeon Romanovsky struct xfrm_dev_offload *xso = &x->xso; 217d77e38e6SSteffen Klassert xfrm_address_t *saddr; 218d77e38e6SSteffen Klassert xfrm_address_t *daddr; 219d77e38e6SSteffen Klassert 220*adb5c33eSSabrina Dubroca if (!x->type_offload) { 221*adb5c33eSSabrina Dubroca NL_SET_ERR_MSG(extack, "Type doesn't support offload"); 222ffdb5211SIlan Tayari return -EINVAL; 223*adb5c33eSSabrina Dubroca } 224d77e38e6SSteffen Klassert 22550bd870aSYossef Efraim /* We don't yet support UDP encapsulation and TFC padding. */ 226*adb5c33eSSabrina Dubroca if (x->encap || x->tfcpad) { 227*adb5c33eSSabrina Dubroca NL_SET_ERR_MSG(extack, "Encapsulation and TFC padding can't be offloaded"); 22843024b9cSYossef Efraim return -EINVAL; 229*adb5c33eSSabrina Dubroca } 230d77e38e6SSteffen Klassert 231*adb5c33eSSabrina Dubroca if (xuo->flags & ~(XFRM_OFFLOAD_IPV6 | XFRM_OFFLOAD_INBOUND)) { 232*adb5c33eSSabrina Dubroca NL_SET_ERR_MSG(extack, "Unrecognized flags in offload request"); 2337c76ecd9SLeon Romanovsky return -EINVAL; 234*adb5c33eSSabrina Dubroca } 2357c76ecd9SLeon Romanovsky 236d77e38e6SSteffen Klassert dev = dev_get_by_index(net, xuo->ifindex); 237d77e38e6SSteffen Klassert if (!dev) { 238d77e38e6SSteffen Klassert if (!(xuo->flags & XFRM_OFFLOAD_INBOUND)) { 239d77e38e6SSteffen Klassert saddr = &x->props.saddr; 240d77e38e6SSteffen Klassert daddr = &x->id.daddr; 241d77e38e6SSteffen Klassert } else { 242d77e38e6SSteffen Klassert saddr = &x->id.daddr; 243d77e38e6SSteffen Klassert daddr = &x->props.saddr; 244d77e38e6SSteffen Klassert } 245d77e38e6SSteffen Klassert 246077fbac4SLorenzo Colitti dst = __xfrm_dst_lookup(net, 0, 0, saddr, daddr, 2479b42c1f1SSteffen Klassert x->props.family, 2489b42c1f1SSteffen Klassert xfrm_smark_get(0, x)); 249d77e38e6SSteffen Klassert if (IS_ERR(dst)) 250d77e38e6SSteffen Klassert return 0; 251d77e38e6SSteffen Klassert 252d77e38e6SSteffen Klassert dev = dst->dev; 253d77e38e6SSteffen Klassert 254d77e38e6SSteffen Klassert dev_hold(dev); 255d77e38e6SSteffen Klassert dst_release(dst); 256d77e38e6SSteffen Klassert } 257d77e38e6SSteffen Klassert 258d77e38e6SSteffen Klassert if (!dev->xfrmdev_ops || !dev->xfrmdev_ops->xdo_dev_state_add) { 25967a63387SSteffen Klassert xso->dev = NULL; 260d77e38e6SSteffen Klassert dev_put(dev); 261d77e38e6SSteffen Klassert return 0; 262d77e38e6SSteffen Klassert } 263d77e38e6SSteffen Klassert 26450bd870aSYossef Efraim if (x->props.flags & XFRM_STATE_ESN && 26550bd870aSYossef Efraim !dev->xfrmdev_ops->xdo_dev_state_advance_esn) { 266*adb5c33eSSabrina Dubroca NL_SET_ERR_MSG(extack, "Device doesn't support offload with ESN"); 26750bd870aSYossef Efraim xso->dev = NULL; 26850bd870aSYossef Efraim dev_put(dev); 26950bd870aSYossef Efraim return -EINVAL; 27050bd870aSYossef Efraim } 27150bd870aSYossef Efraim 272d77e38e6SSteffen Klassert xso->dev = dev; 273e1b539bdSEric Dumazet netdev_tracker_alloc(dev, &xso->dev_tracker, GFP_ATOMIC); 274bdfd2d1fSJarod Wilson xso->real_dev = dev; 275d77e38e6SSteffen Klassert 276482db2f1SLeon Romanovsky if (xuo->flags & XFRM_OFFLOAD_INBOUND) 277482db2f1SLeon Romanovsky xso->dir = XFRM_DEV_OFFLOAD_IN; 278482db2f1SLeon Romanovsky else 279482db2f1SLeon Romanovsky xso->dir = XFRM_DEV_OFFLOAD_OUT; 280482db2f1SLeon Romanovsky 281d77e38e6SSteffen Klassert err = dev->xfrmdev_ops->xdo_dev_state_add(x); 282d77e38e6SSteffen Klassert if (err) { 283aa5dd6faSAviad Yehezkel xso->dev = NULL; 284482db2f1SLeon Romanovsky xso->dir = 0; 285dd72fadfSAyush Sawal xso->real_dev = NULL; 286d62607c3SJakub Kicinski netdev_put(dev, &xso->dev_tracker); 2874a132095SShannon Nelson 288*adb5c33eSSabrina Dubroca if (err != -EOPNOTSUPP) { 289*adb5c33eSSabrina Dubroca NL_SET_ERR_MSG(extack, "Device failed to offload this state"); 290d77e38e6SSteffen Klassert return err; 291d77e38e6SSteffen Klassert } 292*adb5c33eSSabrina Dubroca } 293d77e38e6SSteffen Klassert 294d77e38e6SSteffen Klassert return 0; 295d77e38e6SSteffen Klassert } 296d77e38e6SSteffen Klassert EXPORT_SYMBOL_GPL(xfrm_dev_state_add); 297d77e38e6SSteffen Klassert 298d77e38e6SSteffen Klassert bool xfrm_dev_offload_ok(struct sk_buff *skb, struct xfrm_state *x) 299d77e38e6SSteffen Klassert { 300d77e38e6SSteffen Klassert int mtu; 301d77e38e6SSteffen Klassert struct dst_entry *dst = skb_dst(skb); 302d77e38e6SSteffen Klassert struct xfrm_dst *xdst = (struct xfrm_dst *)dst; 303d77e38e6SSteffen Klassert struct net_device *dev = x->xso.dev; 304d77e38e6SSteffen Klassert 305d77e38e6SSteffen Klassert if (!x->type_offload || x->encap) 306d77e38e6SSteffen Klassert return false; 307d77e38e6SSteffen Klassert 308fcb662deSShannon Nelson if ((!dev || (dev == xfrm_dst_path(dst)->dev)) && 309c7b37c76SFlorian Westphal (!xdst->child->xfrm)) { 310c7b37c76SFlorian Westphal mtu = xfrm_state_mtu(x, xdst->child_mtu_cached); 311d77e38e6SSteffen Klassert if (skb->len <= mtu) 312d77e38e6SSteffen Klassert goto ok; 313d77e38e6SSteffen Klassert 314779b7931SDaniel Axtens if (skb_is_gso(skb) && skb_gso_validate_network_len(skb, mtu)) 315d77e38e6SSteffen Klassert goto ok; 316d77e38e6SSteffen Klassert } 317d77e38e6SSteffen Klassert 318d77e38e6SSteffen Klassert return false; 319d77e38e6SSteffen Klassert 320d77e38e6SSteffen Klassert ok: 321d77e38e6SSteffen Klassert if (dev && dev->xfrmdev_ops && dev->xfrmdev_ops->xdo_dev_offload_ok) 322d77e38e6SSteffen Klassert return x->xso.dev->xfrmdev_ops->xdo_dev_offload_ok(skb, x); 323d77e38e6SSteffen Klassert 324d77e38e6SSteffen Klassert return true; 325d77e38e6SSteffen Klassert } 326d77e38e6SSteffen Klassert EXPORT_SYMBOL_GPL(xfrm_dev_offload_ok); 327f53c7239SSteffen Klassert 328f53c7239SSteffen Klassert void xfrm_dev_resume(struct sk_buff *skb) 329f53c7239SSteffen Klassert { 330f53c7239SSteffen Klassert struct net_device *dev = skb->dev; 331f53c7239SSteffen Klassert int ret = NETDEV_TX_BUSY; 332f53c7239SSteffen Klassert struct netdev_queue *txq; 333f53c7239SSteffen Klassert struct softnet_data *sd; 334f53c7239SSteffen Klassert unsigned long flags; 335f53c7239SSteffen Klassert 336f53c7239SSteffen Klassert rcu_read_lock(); 3374bd97d51SPaolo Abeni txq = netdev_core_pick_tx(dev, skb, NULL); 338f53c7239SSteffen Klassert 339f53c7239SSteffen Klassert HARD_TX_LOCK(dev, txq, smp_processor_id()); 340f53c7239SSteffen Klassert if (!netif_xmit_frozen_or_stopped(txq)) 341f53c7239SSteffen Klassert skb = dev_hard_start_xmit(skb, dev, txq, &ret); 342f53c7239SSteffen Klassert HARD_TX_UNLOCK(dev, txq); 343f53c7239SSteffen Klassert 344f53c7239SSteffen Klassert if (!dev_xmit_complete(ret)) { 345f53c7239SSteffen Klassert local_irq_save(flags); 346f53c7239SSteffen Klassert sd = this_cpu_ptr(&softnet_data); 347f53c7239SSteffen Klassert skb_queue_tail(&sd->xfrm_backlog, skb); 348f53c7239SSteffen Klassert raise_softirq_irqoff(NET_TX_SOFTIRQ); 349f53c7239SSteffen Klassert local_irq_restore(flags); 350f53c7239SSteffen Klassert } 351f53c7239SSteffen Klassert rcu_read_unlock(); 352f53c7239SSteffen Klassert } 353f53c7239SSteffen Klassert EXPORT_SYMBOL_GPL(xfrm_dev_resume); 354f53c7239SSteffen Klassert 355f53c7239SSteffen Klassert void xfrm_dev_backlog(struct softnet_data *sd) 356f53c7239SSteffen Klassert { 357f53c7239SSteffen Klassert struct sk_buff_head *xfrm_backlog = &sd->xfrm_backlog; 358f53c7239SSteffen Klassert struct sk_buff_head list; 359f53c7239SSteffen Klassert struct sk_buff *skb; 360f53c7239SSteffen Klassert 361f53c7239SSteffen Klassert if (skb_queue_empty(xfrm_backlog)) 362f53c7239SSteffen Klassert return; 363f53c7239SSteffen Klassert 364f53c7239SSteffen Klassert __skb_queue_head_init(&list); 365f53c7239SSteffen Klassert 366f53c7239SSteffen Klassert spin_lock(&xfrm_backlog->lock); 367f53c7239SSteffen Klassert skb_queue_splice_init(xfrm_backlog, &list); 368f53c7239SSteffen Klassert spin_unlock(&xfrm_backlog->lock); 369f53c7239SSteffen Klassert 370f53c7239SSteffen Klassert while (!skb_queue_empty(&list)) { 371f53c7239SSteffen Klassert skb = __skb_dequeue(&list); 372f53c7239SSteffen Klassert xfrm_dev_resume(skb); 373f53c7239SSteffen Klassert } 374f53c7239SSteffen Klassert 375f53c7239SSteffen Klassert } 376b81f884aSHangbin Liu #endif 377d77e38e6SSteffen Klassert 37892a23206SShannon Nelson static int xfrm_api_check(struct net_device *dev) 379d77e38e6SSteffen Klassert { 38092a23206SShannon Nelson #ifdef CONFIG_XFRM_OFFLOAD 381d77e38e6SSteffen Klassert if ((dev->features & NETIF_F_HW_ESP_TX_CSUM) && 382d77e38e6SSteffen Klassert !(dev->features & NETIF_F_HW_ESP)) 383d77e38e6SSteffen Klassert return NOTIFY_BAD; 384d77e38e6SSteffen Klassert 38592a23206SShannon Nelson if ((dev->features & NETIF_F_HW_ESP) && 38692a23206SShannon Nelson (!(dev->xfrmdev_ops && 38792a23206SShannon Nelson dev->xfrmdev_ops->xdo_dev_state_add && 38892a23206SShannon Nelson dev->xfrmdev_ops->xdo_dev_state_delete))) 38992a23206SShannon Nelson return NOTIFY_BAD; 39092a23206SShannon Nelson #else 39192a23206SShannon Nelson if (dev->features & (NETIF_F_HW_ESP | NETIF_F_HW_ESP_TX_CSUM)) 39292a23206SShannon Nelson return NOTIFY_BAD; 39392a23206SShannon Nelson #endif 39492a23206SShannon Nelson 395d77e38e6SSteffen Klassert return NOTIFY_DONE; 396d77e38e6SSteffen Klassert } 397d77e38e6SSteffen Klassert 398d77e38e6SSteffen Klassert static int xfrm_dev_down(struct net_device *dev) 399d77e38e6SSteffen Klassert { 4002c1497bbSIlan Tayari if (dev->features & NETIF_F_HW_ESP) 401d77e38e6SSteffen Klassert xfrm_dev_state_flush(dev_net(dev), dev, true); 402d77e38e6SSteffen Klassert 403d77e38e6SSteffen Klassert return NOTIFY_DONE; 404d77e38e6SSteffen Klassert } 405d77e38e6SSteffen Klassert 40621f42cc9SSteffen Klassert static int xfrm_dev_event(struct notifier_block *this, unsigned long event, void *ptr) 40721f42cc9SSteffen Klassert { 40821f42cc9SSteffen Klassert struct net_device *dev = netdev_notifier_info_to_dev(ptr); 40921f42cc9SSteffen Klassert 41021f42cc9SSteffen Klassert switch (event) { 411d77e38e6SSteffen Klassert case NETDEV_REGISTER: 4122ecda181SLeon Romanovsky return xfrm_api_check(dev); 413d77e38e6SSteffen Klassert 414d77e38e6SSteffen Klassert case NETDEV_FEAT_CHANGE: 4152ecda181SLeon Romanovsky return xfrm_api_check(dev); 416d77e38e6SSteffen Klassert 41721f42cc9SSteffen Klassert case NETDEV_DOWN: 41803891f82SRaed Salem case NETDEV_UNREGISTER: 419d77e38e6SSteffen Klassert return xfrm_dev_down(dev); 42021f42cc9SSteffen Klassert } 42121f42cc9SSteffen Klassert return NOTIFY_DONE; 42221f42cc9SSteffen Klassert } 42321f42cc9SSteffen Klassert 42421f42cc9SSteffen Klassert static struct notifier_block xfrm_dev_notifier = { 42521f42cc9SSteffen Klassert .notifier_call = xfrm_dev_event, 42621f42cc9SSteffen Klassert }; 42721f42cc9SSteffen Klassert 428e9a441b6SKirill Tkhai void __init xfrm_dev_init(void) 42921f42cc9SSteffen Klassert { 43021f42cc9SSteffen Klassert register_netdevice_notifier(&xfrm_dev_notifier); 43121f42cc9SSteffen Klassert } 432