| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * IPV4 GSO/GRO offload support |
|---|
| 3 | 4 | * Linux INET implementation |
|---|
| 4 | 5 | * |
|---|
| 5 | 6 | * Copyright (C) 2016 secunet Security Networks AG |
|---|
| 6 | 7 | * Author: Steffen Klassert <steffen.klassert@secunet.com> |
|---|
| 7 | | - * |
|---|
| 8 | | - * This program is free software; you can redistribute it and/or modify it |
|---|
| 9 | | - * under the terms and conditions of the GNU General Public License, |
|---|
| 10 | | - * version 2, as published by the Free Software Foundation. |
|---|
| 11 | 8 | * |
|---|
| 12 | 9 | * ESP GRO support |
|---|
| 13 | 10 | */ |
|---|
| .. | .. |
|---|
| 46 | 43 | |
|---|
| 47 | 44 | xo = xfrm_offload(skb); |
|---|
| 48 | 45 | if (!xo || !(xo->flags & CRYPTO_DONE)) { |
|---|
| 49 | | - err = secpath_set(skb); |
|---|
| 50 | | - if (err) |
|---|
| 46 | + struct sec_path *sp = secpath_set(skb); |
|---|
| 47 | + |
|---|
| 48 | + if (!sp) |
|---|
| 51 | 49 | goto out; |
|---|
| 52 | 50 | |
|---|
| 53 | | - if (skb->sp->len == XFRM_MAX_DEPTH) |
|---|
| 54 | | - goto out; |
|---|
| 51 | + if (sp->len == XFRM_MAX_DEPTH) |
|---|
| 52 | + goto out_reset; |
|---|
| 55 | 53 | |
|---|
| 56 | 54 | x = xfrm_state_lookup(dev_net(skb->dev), skb->mark, |
|---|
| 57 | 55 | (xfrm_address_t *)&ip_hdr(skb)->daddr, |
|---|
| 58 | 56 | spi, IPPROTO_ESP, AF_INET); |
|---|
| 59 | 57 | if (!x) |
|---|
| 60 | | - goto out; |
|---|
| 58 | + goto out_reset; |
|---|
| 61 | 59 | |
|---|
| 62 | | - skb->sp->xvec[skb->sp->len++] = x; |
|---|
| 63 | | - skb->sp->olen++; |
|---|
| 60 | + skb->mark = xfrm_smark_get(skb->mark, x); |
|---|
| 61 | + |
|---|
| 62 | + sp->xvec[sp->len++] = x; |
|---|
| 63 | + sp->olen++; |
|---|
| 64 | 64 | |
|---|
| 65 | 65 | xo = xfrm_offload(skb); |
|---|
| 66 | | - if (!xo) { |
|---|
| 67 | | - xfrm_state_put(x); |
|---|
| 68 | | - goto out; |
|---|
| 69 | | - } |
|---|
| 66 | + if (!xo) |
|---|
| 67 | + goto out_reset; |
|---|
| 70 | 68 | } |
|---|
| 71 | 69 | |
|---|
| 72 | 70 | xo->flags |= XFRM_GRO; |
|---|
| .. | .. |
|---|
| 81 | 79 | xfrm_input(skb, IPPROTO_ESP, spi, -2); |
|---|
| 82 | 80 | |
|---|
| 83 | 81 | return ERR_PTR(-EINPROGRESS); |
|---|
| 82 | +out_reset: |
|---|
| 83 | + secpath_reset(skb); |
|---|
| 84 | 84 | out: |
|---|
| 85 | 85 | skb_push(skb, offset); |
|---|
| 86 | 86 | NAPI_GRO_CB(skb)->same_flow = 0; |
|---|
| .. | .. |
|---|
| 106 | 106 | xo->proto = proto; |
|---|
| 107 | 107 | } |
|---|
| 108 | 108 | |
|---|
| 109 | +static struct sk_buff *xfrm4_tunnel_gso_segment(struct xfrm_state *x, |
|---|
| 110 | + struct sk_buff *skb, |
|---|
| 111 | + netdev_features_t features) |
|---|
| 112 | +{ |
|---|
| 113 | + __skb_push(skb, skb->mac_len); |
|---|
| 114 | + return skb_mac_gso_segment(skb, features); |
|---|
| 115 | +} |
|---|
| 116 | + |
|---|
| 117 | +static struct sk_buff *xfrm4_transport_gso_segment(struct xfrm_state *x, |
|---|
| 118 | + struct sk_buff *skb, |
|---|
| 119 | + netdev_features_t features) |
|---|
| 120 | +{ |
|---|
| 121 | + const struct net_offload *ops; |
|---|
| 122 | + struct sk_buff *segs = ERR_PTR(-EINVAL); |
|---|
| 123 | + struct xfrm_offload *xo = xfrm_offload(skb); |
|---|
| 124 | + |
|---|
| 125 | + skb->transport_header += x->props.header_len; |
|---|
| 126 | + ops = rcu_dereference(inet_offloads[xo->proto]); |
|---|
| 127 | + if (likely(ops && ops->callbacks.gso_segment)) |
|---|
| 128 | + segs = ops->callbacks.gso_segment(skb, features); |
|---|
| 129 | + |
|---|
| 130 | + return segs; |
|---|
| 131 | +} |
|---|
| 132 | + |
|---|
| 133 | +static struct sk_buff *xfrm4_beet_gso_segment(struct xfrm_state *x, |
|---|
| 134 | + struct sk_buff *skb, |
|---|
| 135 | + netdev_features_t features) |
|---|
| 136 | +{ |
|---|
| 137 | + struct xfrm_offload *xo = xfrm_offload(skb); |
|---|
| 138 | + struct sk_buff *segs = ERR_PTR(-EINVAL); |
|---|
| 139 | + const struct net_offload *ops; |
|---|
| 140 | + u8 proto = xo->proto; |
|---|
| 141 | + |
|---|
| 142 | + skb->transport_header += x->props.header_len; |
|---|
| 143 | + |
|---|
| 144 | + if (x->sel.family != AF_INET6) { |
|---|
| 145 | + if (proto == IPPROTO_BEETPH) { |
|---|
| 146 | + struct ip_beet_phdr *ph = |
|---|
| 147 | + (struct ip_beet_phdr *)skb->data; |
|---|
| 148 | + |
|---|
| 149 | + skb->transport_header += ph->hdrlen * 8; |
|---|
| 150 | + proto = ph->nexthdr; |
|---|
| 151 | + } else { |
|---|
| 152 | + skb->transport_header -= IPV4_BEET_PHMAXLEN; |
|---|
| 153 | + } |
|---|
| 154 | + } else { |
|---|
| 155 | + __be16 frag; |
|---|
| 156 | + |
|---|
| 157 | + skb->transport_header += |
|---|
| 158 | + ipv6_skip_exthdr(skb, 0, &proto, &frag); |
|---|
| 159 | + if (proto == IPPROTO_TCP) |
|---|
| 160 | + skb_shinfo(skb)->gso_type |= SKB_GSO_TCPV4; |
|---|
| 161 | + } |
|---|
| 162 | + |
|---|
| 163 | + if (proto == IPPROTO_IPV6) |
|---|
| 164 | + skb_shinfo(skb)->gso_type |= SKB_GSO_IPXIP4; |
|---|
| 165 | + |
|---|
| 166 | + __skb_pull(skb, skb_transport_offset(skb)); |
|---|
| 167 | + ops = rcu_dereference(inet_offloads[proto]); |
|---|
| 168 | + if (likely(ops && ops->callbacks.gso_segment)) |
|---|
| 169 | + segs = ops->callbacks.gso_segment(skb, features); |
|---|
| 170 | + |
|---|
| 171 | + return segs; |
|---|
| 172 | +} |
|---|
| 173 | + |
|---|
| 174 | +static struct sk_buff *xfrm4_outer_mode_gso_segment(struct xfrm_state *x, |
|---|
| 175 | + struct sk_buff *skb, |
|---|
| 176 | + netdev_features_t features) |
|---|
| 177 | +{ |
|---|
| 178 | + switch (x->outer_mode.encap) { |
|---|
| 179 | + case XFRM_MODE_TUNNEL: |
|---|
| 180 | + return xfrm4_tunnel_gso_segment(x, skb, features); |
|---|
| 181 | + case XFRM_MODE_TRANSPORT: |
|---|
| 182 | + return xfrm4_transport_gso_segment(x, skb, features); |
|---|
| 183 | + case XFRM_MODE_BEET: |
|---|
| 184 | + return xfrm4_beet_gso_segment(x, skb, features); |
|---|
| 185 | + } |
|---|
| 186 | + |
|---|
| 187 | + return ERR_PTR(-EOPNOTSUPP); |
|---|
| 188 | +} |
|---|
| 189 | + |
|---|
| 109 | 190 | static struct sk_buff *esp4_gso_segment(struct sk_buff *skb, |
|---|
| 110 | 191 | netdev_features_t features) |
|---|
| 111 | 192 | { |
|---|
| .. | .. |
|---|
| 114 | 195 | struct crypto_aead *aead; |
|---|
| 115 | 196 | netdev_features_t esp_features = features; |
|---|
| 116 | 197 | struct xfrm_offload *xo = xfrm_offload(skb); |
|---|
| 198 | + struct sec_path *sp; |
|---|
| 117 | 199 | |
|---|
| 118 | 200 | if (!xo) |
|---|
| 119 | 201 | return ERR_PTR(-EINVAL); |
|---|
| .. | .. |
|---|
| 121 | 203 | if (!(skb_shinfo(skb)->gso_type & SKB_GSO_ESP)) |
|---|
| 122 | 204 | return ERR_PTR(-EINVAL); |
|---|
| 123 | 205 | |
|---|
| 124 | | - x = skb->sp->xvec[skb->sp->len - 1]; |
|---|
| 206 | + sp = skb_sec_path(skb); |
|---|
| 207 | + x = sp->xvec[sp->len - 1]; |
|---|
| 125 | 208 | aead = x->data; |
|---|
| 126 | 209 | esph = ip_esp_hdr(skb); |
|---|
| 127 | 210 | |
|---|
| .. | .. |
|---|
| 135 | 218 | |
|---|
| 136 | 219 | skb->encap_hdr_csum = 1; |
|---|
| 137 | 220 | |
|---|
| 138 | | - if (!(features & NETIF_F_HW_ESP) || x->xso.dev != skb->dev) |
|---|
| 139 | | - esp_features = features & ~(NETIF_F_SG | NETIF_F_CSUM_MASK); |
|---|
| 140 | | - else if (!(features & NETIF_F_HW_ESP_TX_CSUM)) |
|---|
| 141 | | - esp_features = features & ~NETIF_F_CSUM_MASK; |
|---|
| 221 | + if ((!(skb->dev->gso_partial_features & NETIF_F_HW_ESP) && |
|---|
| 222 | + !(features & NETIF_F_HW_ESP)) || x->xso.dev != skb->dev) |
|---|
| 223 | + esp_features = features & ~(NETIF_F_SG | NETIF_F_CSUM_MASK | |
|---|
| 224 | + NETIF_F_SCTP_CRC); |
|---|
| 225 | + else if (!(features & NETIF_F_HW_ESP_TX_CSUM) && |
|---|
| 226 | + !(skb->dev->gso_partial_features & NETIF_F_HW_ESP_TX_CSUM)) |
|---|
| 227 | + esp_features = features & ~(NETIF_F_CSUM_MASK | |
|---|
| 228 | + NETIF_F_SCTP_CRC); |
|---|
| 142 | 229 | |
|---|
| 143 | 230 | xo->flags |= XFRM_GSO_SEGMENT; |
|---|
| 144 | 231 | |
|---|
| 145 | | - return x->outer_mode->gso_segment(x, skb, esp_features); |
|---|
| 232 | + return xfrm4_outer_mode_gso_segment(x, skb, esp_features); |
|---|
| 146 | 233 | } |
|---|
| 147 | 234 | |
|---|
| 148 | 235 | static int esp_input_tail(struct xfrm_state *x, struct sk_buff *skb) |
|---|
| .. | .. |
|---|
| 178 | 265 | if (!xo) |
|---|
| 179 | 266 | return -EINVAL; |
|---|
| 180 | 267 | |
|---|
| 181 | | - if (!(features & NETIF_F_HW_ESP) || x->xso.dev != skb->dev) { |
|---|
| 268 | + if ((!(features & NETIF_F_HW_ESP) && |
|---|
| 269 | + !(skb->dev->gso_partial_features & NETIF_F_HW_ESP)) || |
|---|
| 270 | + x->xso.dev != skb->dev) { |
|---|
| 182 | 271 | xo->flags |= CRYPTO_FALLBACK; |
|---|
| 183 | 272 | hw_offload = false; |
|---|
| 184 | 273 | } |
|---|
| .. | .. |
|---|
| 223 | 312 | xo->seq.low += skb_shinfo(skb)->gso_segs; |
|---|
| 224 | 313 | } |
|---|
| 225 | 314 | |
|---|
| 315 | + if (xo->seq.low < seq) |
|---|
| 316 | + xo->seq.hi++; |
|---|
| 317 | + |
|---|
| 226 | 318 | esp.seqno = cpu_to_be64(seq + ((u64)xo->seq.hi << 32)); |
|---|
| 227 | 319 | |
|---|
| 228 | 320 | ip_hdr(skb)->tot_len = htons(skb->len); |
|---|
| 229 | 321 | ip_send_check(ip_hdr(skb)); |
|---|
| 230 | 322 | |
|---|
| 231 | | - if (hw_offload) |
|---|
| 323 | + if (hw_offload) { |
|---|
| 324 | + if (!skb_ext_add(skb, SKB_EXT_SEC_PATH)) |
|---|
| 325 | + return -ENOMEM; |
|---|
| 326 | + |
|---|
| 327 | + xo = xfrm_offload(skb); |
|---|
| 328 | + if (!xo) |
|---|
| 329 | + return -EINVAL; |
|---|
| 330 | + |
|---|
| 331 | + xo->flags |= XFRM_XMIT; |
|---|
| 232 | 332 | return 0; |
|---|
| 333 | + } |
|---|
| 233 | 334 | |
|---|
| 234 | 335 | err = esp_output_tail(x, skb, &esp); |
|---|
| 235 | 336 | if (err) |
|---|
| .. | .. |
|---|
| 237 | 338 | |
|---|
| 238 | 339 | secpath_reset(skb); |
|---|
| 239 | 340 | |
|---|
| 341 | + if (skb_needs_linearize(skb, skb->dev->features) && |
|---|
| 342 | + __skb_linearize(skb)) |
|---|
| 343 | + return -ENOMEM; |
|---|
| 240 | 344 | return 0; |
|---|
| 241 | 345 | } |
|---|
| 242 | 346 | |
|---|
| .. | .. |
|---|
| 268 | 372 | |
|---|
| 269 | 373 | static void __exit esp4_offload_exit(void) |
|---|
| 270 | 374 | { |
|---|
| 271 | | - if (xfrm_unregister_type_offload(&esp_type_offload, AF_INET) < 0) |
|---|
| 272 | | - pr_info("%s: can't remove xfrm type offload\n", __func__); |
|---|
| 273 | | - |
|---|
| 375 | + xfrm_unregister_type_offload(&esp_type_offload, AF_INET); |
|---|
| 274 | 376 | inet_del_offload(&esp4_offload, IPPROTO_ESP); |
|---|
| 275 | 377 | } |
|---|
| 276 | 378 | |
|---|
| .. | .. |
|---|
| 279 | 381 | MODULE_LICENSE("GPL"); |
|---|
| 280 | 382 | MODULE_AUTHOR("Steffen Klassert <steffen.klassert@secunet.com>"); |
|---|
| 281 | 383 | MODULE_ALIAS_XFRM_OFFLOAD_TYPE(AF_INET, XFRM_PROTO_ESP); |
|---|
| 384 | +MODULE_DESCRIPTION("IPV4 GSO/GRO offload support"); |
|---|