ixgbe: process the Rx ipsec offload
authorShannon Nelson <shannon.nelson@oracle.com>
Tue, 19 Dec 2017 23:59:59 +0000 (15:59 -0800)
committerJeff Kirsher <jeffrey.t.kirsher@intel.com>
Tue, 23 Jan 2018 17:52:57 +0000 (09:52 -0800)
If the chip sees and decrypts an ipsec offload, set up the skb
sp pointer with the ralated SA info.  Since the chip is rude
enough to keep to itself the table index it used for the
decryption, we have to do our own table lookup, using the
hash for speed.

Signed-off-by: Shannon Nelson <shannon.nelson@oracle.com>
Tested-by: Andrew Bowers <andrewx.bowers@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
drivers/net/ethernet/intel/ixgbe/ixgbe.h
drivers/net/ethernet/intel/ixgbe/ixgbe_ipsec.c
drivers/net/ethernet/intel/ixgbe/ixgbe_main.c

index c43ed68..e8f3c6b 100644 (file)
@@ -1021,9 +1021,15 @@ s32 ixgbe_negotiate_fc(struct ixgbe_hw *hw, u32 adv_reg, u32 lp_reg,
 void ixgbe_init_ipsec_offload(struct ixgbe_adapter *adapter);
 void ixgbe_stop_ipsec_offload(struct ixgbe_adapter *adapter);
 void ixgbe_ipsec_restore(struct ixgbe_adapter *adapter);
+void ixgbe_ipsec_rx(struct ixgbe_ring *rx_ring,
+                   union ixgbe_adv_rx_desc *rx_desc,
+                   struct sk_buff *skb);
 #else
 static inline void ixgbe_init_ipsec_offload(struct ixgbe_adapter *adapter) { };
 static inline void ixgbe_stop_ipsec_offload(struct ixgbe_adapter *adapter) { };
 static inline void ixgbe_ipsec_restore(struct ixgbe_adapter *adapter) { };
+static inline void ixgbe_ipsec_rx(struct ixgbe_ring *rx_ring,
+                                 union ixgbe_adv_rx_desc *rx_desc,
+                                 struct sk_buff *skb) { };
 #endif /* CONFIG_XFRM_OFFLOAD */
 #endif /* _IXGBE_H_ */
index d468dd3..30916da 100644 (file)
@@ -374,6 +374,38 @@ static int ixgbe_ipsec_find_empty_idx(struct ixgbe_ipsec *ipsec, bool rxtable)
 }
 
 /**
+ * ixgbe_ipsec_find_rx_state - find the state that matches
+ * @ipsec: pointer to ipsec struct
+ * @daddr: inbound address to match
+ * @proto: protocol to match
+ * @spi: SPI to match
+ * @ip4: true if using an ipv4 address
+ *
+ * Returns a pointer to the matching SA state information
+ **/
+static struct xfrm_state *ixgbe_ipsec_find_rx_state(struct ixgbe_ipsec *ipsec,
+                                                   __be32 *daddr, u8 proto,
+                                                   __be32 spi, bool ip4)
+{
+       struct rx_sa *rsa;
+       struct xfrm_state *ret = NULL;
+
+       rcu_read_lock();
+       hash_for_each_possible_rcu(ipsec->rx_sa_list, rsa, hlist, spi)
+               if (spi == rsa->xs->id.spi &&
+                   ((ip4 && *daddr == rsa->xs->id.daddr.a4) ||
+                     (!ip4 && !memcmp(daddr, &rsa->xs->id.daddr.a6,
+                                      sizeof(rsa->xs->id.daddr.a6)))) &&
+                   proto == rsa->xs->id.proto) {
+                       ret = rsa->xs;
+                       xfrm_state_hold(ret);
+                       break;
+               }
+       rcu_read_unlock();
+       return ret;
+}
+
+/**
  * ixgbe_ipsec_parse_proto_keys - find the key and salt based on the protocol
  * @xs: pointer to xfrm_state struct
  * @mykey: pointer to key array to populate
@@ -476,7 +508,7 @@ static int ixgbe_ipsec_add_sa(struct xfrm_state *xs)
                }
 
                /* get ip for rx sa table */
-               if (xs->xso.flags & XFRM_OFFLOAD_IPV6)
+               if (xs->props.family == AF_INET6)
                        memcpy(rsa.ipaddr, &xs->id.daddr.a6, 16);
                else
                        memcpy(&rsa.ipaddr[3], &xs->id.daddr.a4, 4);
@@ -541,7 +573,7 @@ static int ixgbe_ipsec_add_sa(struct xfrm_state *xs)
                        rsa.mode |= IXGBE_RXMOD_PROTO_ESP;
                if (rsa.decrypt)
                        rsa.mode |= IXGBE_RXMOD_DECRYPT;
-               if (rsa.xs->xso.flags & XFRM_OFFLOAD_IPV6)
+               if (rsa.xs->props.family == AF_INET6)
                        rsa.mode |= IXGBE_RXMOD_IPV6;
 
                /* the preparations worked, so save the info */
@@ -672,6 +704,78 @@ static const struct xfrmdev_ops ixgbe_xfrmdev_ops = {
 };
 
 /**
+ * ixgbe_ipsec_rx - decode ipsec bits from Rx descriptor
+ * @rx_ring: receiving ring
+ * @rx_desc: receive data descriptor
+ * @skb: current data packet
+ *
+ * Determine if there was an ipsec encapsulation noticed, and if so set up
+ * the resulting status for later in the receive stack.
+ **/
+void ixgbe_ipsec_rx(struct ixgbe_ring *rx_ring,
+                   union ixgbe_adv_rx_desc *rx_desc,
+                   struct sk_buff *skb)
+{
+       struct ixgbe_adapter *adapter = netdev_priv(rx_ring->netdev);
+       __le16 pkt_info = rx_desc->wb.lower.lo_dword.hs_rss.pkt_info;
+       __le16 ipsec_pkt_types = cpu_to_le16(IXGBE_RXDADV_PKTTYPE_IPSEC_AH |
+                                            IXGBE_RXDADV_PKTTYPE_IPSEC_ESP);
+       struct ixgbe_ipsec *ipsec = adapter->ipsec;
+       struct xfrm_offload *xo = NULL;
+       struct xfrm_state *xs = NULL;
+       struct ipv6hdr *ip6 = NULL;
+       struct iphdr *ip4 = NULL;
+       void *daddr;
+       __be32 spi;
+       u8 *c_hdr;
+       u8 proto;
+
+       /* Find the ip and crypto headers in the data.
+        * We can assume no vlan header in the way, b/c the
+        * hw won't recognize the IPsec packet and anyway the
+        * currently vlan device doesn't support xfrm offload.
+        */
+       if (pkt_info & cpu_to_le16(IXGBE_RXDADV_PKTTYPE_IPV4)) {
+               ip4 = (struct iphdr *)(skb->data + ETH_HLEN);
+               daddr = &ip4->daddr;
+               c_hdr = (u8 *)ip4 + ip4->ihl * 4;
+       } else if (pkt_info & cpu_to_le16(IXGBE_RXDADV_PKTTYPE_IPV6)) {
+               ip6 = (struct ipv6hdr *)(skb->data + ETH_HLEN);
+               daddr = &ip6->daddr;
+               c_hdr = (u8 *)ip6 + sizeof(struct ipv6hdr);
+       } else {
+               return;
+       }
+
+       switch (pkt_info & ipsec_pkt_types) {
+       case cpu_to_le16(IXGBE_RXDADV_PKTTYPE_IPSEC_AH):
+               spi = ((struct ip_auth_hdr *)c_hdr)->spi;
+               proto = IPPROTO_AH;
+               break;
+       case cpu_to_le16(IXGBE_RXDADV_PKTTYPE_IPSEC_ESP):
+               spi = ((struct ip_esp_hdr *)c_hdr)->spi;
+               proto = IPPROTO_ESP;
+               break;
+       default:
+               return;
+       }
+
+       xs = ixgbe_ipsec_find_rx_state(ipsec, daddr, proto, spi, !!ip4);
+       if (unlikely(!xs))
+               return;
+
+       skb->sp = secpath_dup(skb->sp);
+       if (unlikely(!skb->sp))
+               return;
+
+       skb->sp->xvec[skb->sp->len++] = xs;
+       skb->sp->olen++;
+       xo = xfrm_offload(skb);
+       xo->flags = CRYPTO_DONE;
+       xo->status = CRYPTO_SUCCESS;
+}
+
+/**
  * ixgbe_init_ipsec_offload - initialize security registers for IPSec operation
  * @adapter: board private structure
  **/
index 80635d1..9616f28 100644 (file)
@@ -1752,6 +1752,9 @@ static void ixgbe_process_skb_fields(struct ixgbe_ring *rx_ring,
                __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vid);
        }
 
+       if (ixgbe_test_staterr(rx_desc, IXGBE_RXDADV_STAT_SECP))
+               ixgbe_ipsec_rx(rx_ring, rx_desc, skb);
+
        skb->protocol = eth_type_trans(skb, dev);
 
        /* record Rx queue, or update MACVLAN statistics */