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]; 120f6e27114SSteffen Klassert if (xo->flags & XFRM_GRO || x->xso.flags & XFRM_OFFLOAD_INBOUND) 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); 146*625788b5SEric 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, 210d77e38e6SSteffen Klassert struct xfrm_user_offload *xuo) 211d77e38e6SSteffen Klassert { 212d77e38e6SSteffen Klassert int err; 213d77e38e6SSteffen Klassert struct dst_entry *dst; 214d77e38e6SSteffen Klassert struct net_device *dev; 215d77e38e6SSteffen Klassert struct xfrm_state_offload *xso = &x->xso; 216d77e38e6SSteffen Klassert xfrm_address_t *saddr; 217d77e38e6SSteffen Klassert xfrm_address_t *daddr; 218d77e38e6SSteffen Klassert 219d77e38e6SSteffen Klassert if (!x->type_offload) 220ffdb5211SIlan Tayari return -EINVAL; 221d77e38e6SSteffen Klassert 22250bd870aSYossef Efraim /* We don't yet support UDP encapsulation and TFC padding. */ 22350bd870aSYossef Efraim if (x->encap || x->tfcpad) 22443024b9cSYossef Efraim return -EINVAL; 225d77e38e6SSteffen Klassert 2267c76ecd9SLeon Romanovsky if (xuo->flags & ~(XFRM_OFFLOAD_IPV6 | XFRM_OFFLOAD_INBOUND)) 2277c76ecd9SLeon Romanovsky return -EINVAL; 2287c76ecd9SLeon Romanovsky 229d77e38e6SSteffen Klassert dev = dev_get_by_index(net, xuo->ifindex); 230d77e38e6SSteffen Klassert if (!dev) { 231d77e38e6SSteffen Klassert if (!(xuo->flags & XFRM_OFFLOAD_INBOUND)) { 232d77e38e6SSteffen Klassert saddr = &x->props.saddr; 233d77e38e6SSteffen Klassert daddr = &x->id.daddr; 234d77e38e6SSteffen Klassert } else { 235d77e38e6SSteffen Klassert saddr = &x->id.daddr; 236d77e38e6SSteffen Klassert daddr = &x->props.saddr; 237d77e38e6SSteffen Klassert } 238d77e38e6SSteffen Klassert 239077fbac4SLorenzo Colitti dst = __xfrm_dst_lookup(net, 0, 0, saddr, daddr, 2409b42c1f1SSteffen Klassert x->props.family, 2419b42c1f1SSteffen Klassert xfrm_smark_get(0, x)); 242d77e38e6SSteffen Klassert if (IS_ERR(dst)) 243d77e38e6SSteffen Klassert return 0; 244d77e38e6SSteffen Klassert 245d77e38e6SSteffen Klassert dev = dst->dev; 246d77e38e6SSteffen Klassert 247d77e38e6SSteffen Klassert dev_hold(dev); 248d77e38e6SSteffen Klassert dst_release(dst); 249d77e38e6SSteffen Klassert } 250d77e38e6SSteffen Klassert 251d77e38e6SSteffen Klassert if (!dev->xfrmdev_ops || !dev->xfrmdev_ops->xdo_dev_state_add) { 25267a63387SSteffen Klassert xso->dev = NULL; 253d77e38e6SSteffen Klassert dev_put(dev); 254d77e38e6SSteffen Klassert return 0; 255d77e38e6SSteffen Klassert } 256d77e38e6SSteffen Klassert 25750bd870aSYossef Efraim if (x->props.flags & XFRM_STATE_ESN && 25850bd870aSYossef Efraim !dev->xfrmdev_ops->xdo_dev_state_advance_esn) { 25950bd870aSYossef Efraim xso->dev = NULL; 26050bd870aSYossef Efraim dev_put(dev); 26150bd870aSYossef Efraim return -EINVAL; 26250bd870aSYossef Efraim } 26350bd870aSYossef Efraim 264d77e38e6SSteffen Klassert xso->dev = dev; 265e1b539bdSEric Dumazet netdev_tracker_alloc(dev, &xso->dev_tracker, GFP_ATOMIC); 266bdfd2d1fSJarod Wilson xso->real_dev = dev; 267d77e38e6SSteffen Klassert xso->num_exthdrs = 1; 2687c76ecd9SLeon Romanovsky /* Don't forward bit that is not implemented */ 2697c76ecd9SLeon Romanovsky xso->flags = xuo->flags & ~XFRM_OFFLOAD_IPV6; 270d77e38e6SSteffen Klassert 271d77e38e6SSteffen Klassert err = dev->xfrmdev_ops->xdo_dev_state_add(x); 272d77e38e6SSteffen Klassert if (err) { 2734a132095SShannon Nelson xso->num_exthdrs = 0; 2744a132095SShannon Nelson xso->flags = 0; 275aa5dd6faSAviad Yehezkel xso->dev = NULL; 276dd72fadfSAyush Sawal xso->real_dev = NULL; 277e1b539bdSEric Dumazet dev_put_track(dev, &xso->dev_tracker); 2784a132095SShannon Nelson 2794a132095SShannon Nelson if (err != -EOPNOTSUPP) 280d77e38e6SSteffen Klassert return err; 281d77e38e6SSteffen Klassert } 282d77e38e6SSteffen Klassert 283d77e38e6SSteffen Klassert return 0; 284d77e38e6SSteffen Klassert } 285d77e38e6SSteffen Klassert EXPORT_SYMBOL_GPL(xfrm_dev_state_add); 286d77e38e6SSteffen Klassert 287d77e38e6SSteffen Klassert bool xfrm_dev_offload_ok(struct sk_buff *skb, struct xfrm_state *x) 288d77e38e6SSteffen Klassert { 289d77e38e6SSteffen Klassert int mtu; 290d77e38e6SSteffen Klassert struct dst_entry *dst = skb_dst(skb); 291d77e38e6SSteffen Klassert struct xfrm_dst *xdst = (struct xfrm_dst *)dst; 292d77e38e6SSteffen Klassert struct net_device *dev = x->xso.dev; 293d77e38e6SSteffen Klassert 294d77e38e6SSteffen Klassert if (!x->type_offload || x->encap) 295d77e38e6SSteffen Klassert return false; 296d77e38e6SSteffen Klassert 297fcb662deSShannon Nelson if ((!dev || (dev == xfrm_dst_path(dst)->dev)) && 298c7b37c76SFlorian Westphal (!xdst->child->xfrm)) { 299c7b37c76SFlorian Westphal mtu = xfrm_state_mtu(x, xdst->child_mtu_cached); 300d77e38e6SSteffen Klassert if (skb->len <= mtu) 301d77e38e6SSteffen Klassert goto ok; 302d77e38e6SSteffen Klassert 303779b7931SDaniel Axtens if (skb_is_gso(skb) && skb_gso_validate_network_len(skb, mtu)) 304d77e38e6SSteffen Klassert goto ok; 305d77e38e6SSteffen Klassert } 306d77e38e6SSteffen Klassert 307d77e38e6SSteffen Klassert return false; 308d77e38e6SSteffen Klassert 309d77e38e6SSteffen Klassert ok: 310d77e38e6SSteffen Klassert if (dev && dev->xfrmdev_ops && dev->xfrmdev_ops->xdo_dev_offload_ok) 311d77e38e6SSteffen Klassert return x->xso.dev->xfrmdev_ops->xdo_dev_offload_ok(skb, x); 312d77e38e6SSteffen Klassert 313d77e38e6SSteffen Klassert return true; 314d77e38e6SSteffen Klassert } 315d77e38e6SSteffen Klassert EXPORT_SYMBOL_GPL(xfrm_dev_offload_ok); 316f53c7239SSteffen Klassert 317f53c7239SSteffen Klassert void xfrm_dev_resume(struct sk_buff *skb) 318f53c7239SSteffen Klassert { 319f53c7239SSteffen Klassert struct net_device *dev = skb->dev; 320f53c7239SSteffen Klassert int ret = NETDEV_TX_BUSY; 321f53c7239SSteffen Klassert struct netdev_queue *txq; 322f53c7239SSteffen Klassert struct softnet_data *sd; 323f53c7239SSteffen Klassert unsigned long flags; 324f53c7239SSteffen Klassert 325f53c7239SSteffen Klassert rcu_read_lock(); 3264bd97d51SPaolo Abeni txq = netdev_core_pick_tx(dev, skb, NULL); 327f53c7239SSteffen Klassert 328f53c7239SSteffen Klassert HARD_TX_LOCK(dev, txq, smp_processor_id()); 329f53c7239SSteffen Klassert if (!netif_xmit_frozen_or_stopped(txq)) 330f53c7239SSteffen Klassert skb = dev_hard_start_xmit(skb, dev, txq, &ret); 331f53c7239SSteffen Klassert HARD_TX_UNLOCK(dev, txq); 332f53c7239SSteffen Klassert 333f53c7239SSteffen Klassert if (!dev_xmit_complete(ret)) { 334f53c7239SSteffen Klassert local_irq_save(flags); 335f53c7239SSteffen Klassert sd = this_cpu_ptr(&softnet_data); 336f53c7239SSteffen Klassert skb_queue_tail(&sd->xfrm_backlog, skb); 337f53c7239SSteffen Klassert raise_softirq_irqoff(NET_TX_SOFTIRQ); 338f53c7239SSteffen Klassert local_irq_restore(flags); 339f53c7239SSteffen Klassert } 340f53c7239SSteffen Klassert rcu_read_unlock(); 341f53c7239SSteffen Klassert } 342f53c7239SSteffen Klassert EXPORT_SYMBOL_GPL(xfrm_dev_resume); 343f53c7239SSteffen Klassert 344f53c7239SSteffen Klassert void xfrm_dev_backlog(struct softnet_data *sd) 345f53c7239SSteffen Klassert { 346f53c7239SSteffen Klassert struct sk_buff_head *xfrm_backlog = &sd->xfrm_backlog; 347f53c7239SSteffen Klassert struct sk_buff_head list; 348f53c7239SSteffen Klassert struct sk_buff *skb; 349f53c7239SSteffen Klassert 350f53c7239SSteffen Klassert if (skb_queue_empty(xfrm_backlog)) 351f53c7239SSteffen Klassert return; 352f53c7239SSteffen Klassert 353f53c7239SSteffen Klassert __skb_queue_head_init(&list); 354f53c7239SSteffen Klassert 355f53c7239SSteffen Klassert spin_lock(&xfrm_backlog->lock); 356f53c7239SSteffen Klassert skb_queue_splice_init(xfrm_backlog, &list); 357f53c7239SSteffen Klassert spin_unlock(&xfrm_backlog->lock); 358f53c7239SSteffen Klassert 359f53c7239SSteffen Klassert while (!skb_queue_empty(&list)) { 360f53c7239SSteffen Klassert skb = __skb_dequeue(&list); 361f53c7239SSteffen Klassert xfrm_dev_resume(skb); 362f53c7239SSteffen Klassert } 363f53c7239SSteffen Klassert 364f53c7239SSteffen Klassert } 365b81f884aSHangbin Liu #endif 366d77e38e6SSteffen Klassert 36792a23206SShannon Nelson static int xfrm_api_check(struct net_device *dev) 368d77e38e6SSteffen Klassert { 36992a23206SShannon Nelson #ifdef CONFIG_XFRM_OFFLOAD 370d77e38e6SSteffen Klassert if ((dev->features & NETIF_F_HW_ESP_TX_CSUM) && 371d77e38e6SSteffen Klassert !(dev->features & NETIF_F_HW_ESP)) 372d77e38e6SSteffen Klassert return NOTIFY_BAD; 373d77e38e6SSteffen Klassert 37492a23206SShannon Nelson if ((dev->features & NETIF_F_HW_ESP) && 37592a23206SShannon Nelson (!(dev->xfrmdev_ops && 37692a23206SShannon Nelson dev->xfrmdev_ops->xdo_dev_state_add && 37792a23206SShannon Nelson dev->xfrmdev_ops->xdo_dev_state_delete))) 37892a23206SShannon Nelson return NOTIFY_BAD; 37992a23206SShannon Nelson #else 38092a23206SShannon Nelson if (dev->features & (NETIF_F_HW_ESP | NETIF_F_HW_ESP_TX_CSUM)) 38192a23206SShannon Nelson return NOTIFY_BAD; 38292a23206SShannon Nelson #endif 38392a23206SShannon Nelson 384d77e38e6SSteffen Klassert return NOTIFY_DONE; 385d77e38e6SSteffen Klassert } 386d77e38e6SSteffen Klassert 38792a23206SShannon Nelson static int xfrm_dev_register(struct net_device *dev) 38892a23206SShannon Nelson { 38992a23206SShannon Nelson return xfrm_api_check(dev); 39092a23206SShannon Nelson } 39192a23206SShannon Nelson 392d77e38e6SSteffen Klassert static int xfrm_dev_feat_change(struct net_device *dev) 393d77e38e6SSteffen Klassert { 39492a23206SShannon Nelson return xfrm_api_check(dev); 395d77e38e6SSteffen Klassert } 396d77e38e6SSteffen Klassert 397d77e38e6SSteffen Klassert static int xfrm_dev_down(struct net_device *dev) 398d77e38e6SSteffen Klassert { 3992c1497bbSIlan Tayari if (dev->features & NETIF_F_HW_ESP) 400d77e38e6SSteffen Klassert xfrm_dev_state_flush(dev_net(dev), dev, true); 401d77e38e6SSteffen Klassert 402d77e38e6SSteffen Klassert return NOTIFY_DONE; 403d77e38e6SSteffen Klassert } 404d77e38e6SSteffen Klassert 40521f42cc9SSteffen Klassert static int xfrm_dev_event(struct notifier_block *this, unsigned long event, void *ptr) 40621f42cc9SSteffen Klassert { 40721f42cc9SSteffen Klassert struct net_device *dev = netdev_notifier_info_to_dev(ptr); 40821f42cc9SSteffen Klassert 40921f42cc9SSteffen Klassert switch (event) { 410d77e38e6SSteffen Klassert case NETDEV_REGISTER: 411d77e38e6SSteffen Klassert return xfrm_dev_register(dev); 412d77e38e6SSteffen Klassert 413d77e38e6SSteffen Klassert case NETDEV_FEAT_CHANGE: 414d77e38e6SSteffen Klassert return xfrm_dev_feat_change(dev); 415d77e38e6SSteffen Klassert 41621f42cc9SSteffen Klassert case NETDEV_DOWN: 41703891f82SRaed Salem case NETDEV_UNREGISTER: 418d77e38e6SSteffen Klassert return xfrm_dev_down(dev); 41921f42cc9SSteffen Klassert } 42021f42cc9SSteffen Klassert return NOTIFY_DONE; 42121f42cc9SSteffen Klassert } 42221f42cc9SSteffen Klassert 42321f42cc9SSteffen Klassert static struct notifier_block xfrm_dev_notifier = { 42421f42cc9SSteffen Klassert .notifier_call = xfrm_dev_event, 42521f42cc9SSteffen Klassert }; 42621f42cc9SSteffen Klassert 427e9a441b6SKirill Tkhai void __init xfrm_dev_init(void) 42821f42cc9SSteffen Klassert { 42921f42cc9SSteffen Klassert register_netdevice_notifier(&xfrm_dev_notifier); 43021f42cc9SSteffen Klassert } 431