162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Copyright(c) 2017 Oracle and/or its affiliates. All rights reserved. */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include "ixgbe.h" 562306a36Sopenharmony_ci#include <net/xfrm.h> 662306a36Sopenharmony_ci#include <crypto/aead.h> 762306a36Sopenharmony_ci#include <linux/if_bridge.h> 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#define IXGBE_IPSEC_KEY_BITS 160 1062306a36Sopenharmony_cistatic const char aes_gcm_name[] = "rfc4106(gcm(aes))"; 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_cistatic void ixgbe_ipsec_del_sa(struct xfrm_state *xs); 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci/** 1562306a36Sopenharmony_ci * ixgbe_ipsec_set_tx_sa - set the Tx SA registers 1662306a36Sopenharmony_ci * @hw: hw specific details 1762306a36Sopenharmony_ci * @idx: register index to write 1862306a36Sopenharmony_ci * @key: key byte array 1962306a36Sopenharmony_ci * @salt: salt bytes 2062306a36Sopenharmony_ci **/ 2162306a36Sopenharmony_cistatic void ixgbe_ipsec_set_tx_sa(struct ixgbe_hw *hw, u16 idx, 2262306a36Sopenharmony_ci u32 key[], u32 salt) 2362306a36Sopenharmony_ci{ 2462306a36Sopenharmony_ci u32 reg; 2562306a36Sopenharmony_ci int i; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci for (i = 0; i < 4; i++) 2862306a36Sopenharmony_ci IXGBE_WRITE_REG(hw, IXGBE_IPSTXKEY(i), 2962306a36Sopenharmony_ci (__force u32)cpu_to_be32(key[3 - i])); 3062306a36Sopenharmony_ci IXGBE_WRITE_REG(hw, IXGBE_IPSTXSALT, (__force u32)cpu_to_be32(salt)); 3162306a36Sopenharmony_ci IXGBE_WRITE_FLUSH(hw); 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci reg = IXGBE_READ_REG(hw, IXGBE_IPSTXIDX); 3462306a36Sopenharmony_ci reg &= IXGBE_RXTXIDX_IPS_EN; 3562306a36Sopenharmony_ci reg |= idx << IXGBE_RXTXIDX_IDX_SHIFT | IXGBE_RXTXIDX_WRITE; 3662306a36Sopenharmony_ci IXGBE_WRITE_REG(hw, IXGBE_IPSTXIDX, reg); 3762306a36Sopenharmony_ci IXGBE_WRITE_FLUSH(hw); 3862306a36Sopenharmony_ci} 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci/** 4162306a36Sopenharmony_ci * ixgbe_ipsec_set_rx_item - set an Rx table item 4262306a36Sopenharmony_ci * @hw: hw specific details 4362306a36Sopenharmony_ci * @idx: register index to write 4462306a36Sopenharmony_ci * @tbl: table selector 4562306a36Sopenharmony_ci * 4662306a36Sopenharmony_ci * Trigger the device to store into a particular Rx table the 4762306a36Sopenharmony_ci * data that has already been loaded into the input register 4862306a36Sopenharmony_ci **/ 4962306a36Sopenharmony_cistatic void ixgbe_ipsec_set_rx_item(struct ixgbe_hw *hw, u16 idx, 5062306a36Sopenharmony_ci enum ixgbe_ipsec_tbl_sel tbl) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci u32 reg; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci reg = IXGBE_READ_REG(hw, IXGBE_IPSRXIDX); 5562306a36Sopenharmony_ci reg &= IXGBE_RXTXIDX_IPS_EN; 5662306a36Sopenharmony_ci reg |= tbl << IXGBE_RXIDX_TBL_SHIFT | 5762306a36Sopenharmony_ci idx << IXGBE_RXTXIDX_IDX_SHIFT | 5862306a36Sopenharmony_ci IXGBE_RXTXIDX_WRITE; 5962306a36Sopenharmony_ci IXGBE_WRITE_REG(hw, IXGBE_IPSRXIDX, reg); 6062306a36Sopenharmony_ci IXGBE_WRITE_FLUSH(hw); 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci/** 6462306a36Sopenharmony_ci * ixgbe_ipsec_set_rx_sa - set up the register bits to save SA info 6562306a36Sopenharmony_ci * @hw: hw specific details 6662306a36Sopenharmony_ci * @idx: register index to write 6762306a36Sopenharmony_ci * @spi: security parameter index 6862306a36Sopenharmony_ci * @key: key byte array 6962306a36Sopenharmony_ci * @salt: salt bytes 7062306a36Sopenharmony_ci * @mode: rx decrypt control bits 7162306a36Sopenharmony_ci * @ip_idx: index into IP table for related IP address 7262306a36Sopenharmony_ci **/ 7362306a36Sopenharmony_cistatic void ixgbe_ipsec_set_rx_sa(struct ixgbe_hw *hw, u16 idx, __be32 spi, 7462306a36Sopenharmony_ci u32 key[], u32 salt, u32 mode, u32 ip_idx) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci int i; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci /* store the SPI (in bigendian) and IPidx */ 7962306a36Sopenharmony_ci IXGBE_WRITE_REG(hw, IXGBE_IPSRXSPI, 8062306a36Sopenharmony_ci (__force u32)cpu_to_le32((__force u32)spi)); 8162306a36Sopenharmony_ci IXGBE_WRITE_REG(hw, IXGBE_IPSRXIPIDX, ip_idx); 8262306a36Sopenharmony_ci IXGBE_WRITE_FLUSH(hw); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci ixgbe_ipsec_set_rx_item(hw, idx, ips_rx_spi_tbl); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci /* store the key, salt, and mode */ 8762306a36Sopenharmony_ci for (i = 0; i < 4; i++) 8862306a36Sopenharmony_ci IXGBE_WRITE_REG(hw, IXGBE_IPSRXKEY(i), 8962306a36Sopenharmony_ci (__force u32)cpu_to_be32(key[3 - i])); 9062306a36Sopenharmony_ci IXGBE_WRITE_REG(hw, IXGBE_IPSRXSALT, (__force u32)cpu_to_be32(salt)); 9162306a36Sopenharmony_ci IXGBE_WRITE_REG(hw, IXGBE_IPSRXMOD, mode); 9262306a36Sopenharmony_ci IXGBE_WRITE_FLUSH(hw); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci ixgbe_ipsec_set_rx_item(hw, idx, ips_rx_key_tbl); 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci/** 9862306a36Sopenharmony_ci * ixgbe_ipsec_set_rx_ip - set up the register bits to save SA IP addr info 9962306a36Sopenharmony_ci * @hw: hw specific details 10062306a36Sopenharmony_ci * @idx: register index to write 10162306a36Sopenharmony_ci * @addr: IP address byte array 10262306a36Sopenharmony_ci **/ 10362306a36Sopenharmony_cistatic void ixgbe_ipsec_set_rx_ip(struct ixgbe_hw *hw, u16 idx, __be32 addr[]) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci int i; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci /* store the ip address */ 10862306a36Sopenharmony_ci for (i = 0; i < 4; i++) 10962306a36Sopenharmony_ci IXGBE_WRITE_REG(hw, IXGBE_IPSRXIPADDR(i), 11062306a36Sopenharmony_ci (__force u32)cpu_to_le32((__force u32)addr[i])); 11162306a36Sopenharmony_ci IXGBE_WRITE_FLUSH(hw); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci ixgbe_ipsec_set_rx_item(hw, idx, ips_rx_ip_tbl); 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci/** 11762306a36Sopenharmony_ci * ixgbe_ipsec_clear_hw_tables - because some tables don't get cleared on reset 11862306a36Sopenharmony_ci * @adapter: board private structure 11962306a36Sopenharmony_ci **/ 12062306a36Sopenharmony_cistatic void ixgbe_ipsec_clear_hw_tables(struct ixgbe_adapter *adapter) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci struct ixgbe_hw *hw = &adapter->hw; 12362306a36Sopenharmony_ci u32 buf[4] = {0, 0, 0, 0}; 12462306a36Sopenharmony_ci u16 idx; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci /* disable Rx and Tx SA lookup */ 12762306a36Sopenharmony_ci IXGBE_WRITE_REG(hw, IXGBE_IPSRXIDX, 0); 12862306a36Sopenharmony_ci IXGBE_WRITE_REG(hw, IXGBE_IPSTXIDX, 0); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci /* scrub the tables - split the loops for the max of the IP table */ 13162306a36Sopenharmony_ci for (idx = 0; idx < IXGBE_IPSEC_MAX_RX_IP_COUNT; idx++) { 13262306a36Sopenharmony_ci ixgbe_ipsec_set_tx_sa(hw, idx, buf, 0); 13362306a36Sopenharmony_ci ixgbe_ipsec_set_rx_sa(hw, idx, 0, buf, 0, 0, 0); 13462306a36Sopenharmony_ci ixgbe_ipsec_set_rx_ip(hw, idx, (__be32 *)buf); 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci for (; idx < IXGBE_IPSEC_MAX_SA_COUNT; idx++) { 13762306a36Sopenharmony_ci ixgbe_ipsec_set_tx_sa(hw, idx, buf, 0); 13862306a36Sopenharmony_ci ixgbe_ipsec_set_rx_sa(hw, idx, 0, buf, 0, 0, 0); 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci/** 14362306a36Sopenharmony_ci * ixgbe_ipsec_stop_data 14462306a36Sopenharmony_ci * @adapter: board private structure 14562306a36Sopenharmony_ci **/ 14662306a36Sopenharmony_cistatic void ixgbe_ipsec_stop_data(struct ixgbe_adapter *adapter) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci struct ixgbe_hw *hw = &adapter->hw; 14962306a36Sopenharmony_ci bool link = adapter->link_up; 15062306a36Sopenharmony_ci u32 t_rdy, r_rdy; 15162306a36Sopenharmony_ci u32 limit; 15262306a36Sopenharmony_ci u32 reg; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci /* halt data paths */ 15562306a36Sopenharmony_ci reg = IXGBE_READ_REG(hw, IXGBE_SECTXCTRL); 15662306a36Sopenharmony_ci reg |= IXGBE_SECTXCTRL_TX_DIS; 15762306a36Sopenharmony_ci IXGBE_WRITE_REG(hw, IXGBE_SECTXCTRL, reg); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci reg = IXGBE_READ_REG(hw, IXGBE_SECRXCTRL); 16062306a36Sopenharmony_ci reg |= IXGBE_SECRXCTRL_RX_DIS; 16162306a36Sopenharmony_ci IXGBE_WRITE_REG(hw, IXGBE_SECRXCTRL, reg); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci /* If both Tx and Rx are ready there are no packets 16462306a36Sopenharmony_ci * that we need to flush so the loopback configuration 16562306a36Sopenharmony_ci * below is not necessary. 16662306a36Sopenharmony_ci */ 16762306a36Sopenharmony_ci t_rdy = IXGBE_READ_REG(hw, IXGBE_SECTXSTAT) & 16862306a36Sopenharmony_ci IXGBE_SECTXSTAT_SECTX_RDY; 16962306a36Sopenharmony_ci r_rdy = IXGBE_READ_REG(hw, IXGBE_SECRXSTAT) & 17062306a36Sopenharmony_ci IXGBE_SECRXSTAT_SECRX_RDY; 17162306a36Sopenharmony_ci if (t_rdy && r_rdy) 17262306a36Sopenharmony_ci return; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci /* If the tx fifo doesn't have link, but still has data, 17562306a36Sopenharmony_ci * we can't clear the tx sec block. Set the MAC loopback 17662306a36Sopenharmony_ci * before block clear 17762306a36Sopenharmony_ci */ 17862306a36Sopenharmony_ci if (!link) { 17962306a36Sopenharmony_ci reg = IXGBE_READ_REG(hw, IXGBE_MACC); 18062306a36Sopenharmony_ci reg |= IXGBE_MACC_FLU; 18162306a36Sopenharmony_ci IXGBE_WRITE_REG(hw, IXGBE_MACC, reg); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci reg = IXGBE_READ_REG(hw, IXGBE_HLREG0); 18462306a36Sopenharmony_ci reg |= IXGBE_HLREG0_LPBK; 18562306a36Sopenharmony_ci IXGBE_WRITE_REG(hw, IXGBE_HLREG0, reg); 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci IXGBE_WRITE_FLUSH(hw); 18862306a36Sopenharmony_ci mdelay(3); 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci /* wait for the paths to empty */ 19262306a36Sopenharmony_ci limit = 20; 19362306a36Sopenharmony_ci do { 19462306a36Sopenharmony_ci mdelay(10); 19562306a36Sopenharmony_ci t_rdy = IXGBE_READ_REG(hw, IXGBE_SECTXSTAT) & 19662306a36Sopenharmony_ci IXGBE_SECTXSTAT_SECTX_RDY; 19762306a36Sopenharmony_ci r_rdy = IXGBE_READ_REG(hw, IXGBE_SECRXSTAT) & 19862306a36Sopenharmony_ci IXGBE_SECRXSTAT_SECRX_RDY; 19962306a36Sopenharmony_ci } while (!(t_rdy && r_rdy) && limit--); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci /* undo loopback if we played with it earlier */ 20262306a36Sopenharmony_ci if (!link) { 20362306a36Sopenharmony_ci reg = IXGBE_READ_REG(hw, IXGBE_MACC); 20462306a36Sopenharmony_ci reg &= ~IXGBE_MACC_FLU; 20562306a36Sopenharmony_ci IXGBE_WRITE_REG(hw, IXGBE_MACC, reg); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci reg = IXGBE_READ_REG(hw, IXGBE_HLREG0); 20862306a36Sopenharmony_ci reg &= ~IXGBE_HLREG0_LPBK; 20962306a36Sopenharmony_ci IXGBE_WRITE_REG(hw, IXGBE_HLREG0, reg); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci IXGBE_WRITE_FLUSH(hw); 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci/** 21662306a36Sopenharmony_ci * ixgbe_ipsec_stop_engine 21762306a36Sopenharmony_ci * @adapter: board private structure 21862306a36Sopenharmony_ci **/ 21962306a36Sopenharmony_cistatic void ixgbe_ipsec_stop_engine(struct ixgbe_adapter *adapter) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci struct ixgbe_hw *hw = &adapter->hw; 22262306a36Sopenharmony_ci u32 reg; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci ixgbe_ipsec_stop_data(adapter); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci /* disable Rx and Tx SA lookup */ 22762306a36Sopenharmony_ci IXGBE_WRITE_REG(hw, IXGBE_IPSTXIDX, 0); 22862306a36Sopenharmony_ci IXGBE_WRITE_REG(hw, IXGBE_IPSRXIDX, 0); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci /* disable the Rx and Tx engines and full packet store-n-forward */ 23162306a36Sopenharmony_ci reg = IXGBE_READ_REG(hw, IXGBE_SECTXCTRL); 23262306a36Sopenharmony_ci reg |= IXGBE_SECTXCTRL_SECTX_DIS; 23362306a36Sopenharmony_ci reg &= ~IXGBE_SECTXCTRL_STORE_FORWARD; 23462306a36Sopenharmony_ci IXGBE_WRITE_REG(hw, IXGBE_SECTXCTRL, reg); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci reg = IXGBE_READ_REG(hw, IXGBE_SECRXCTRL); 23762306a36Sopenharmony_ci reg |= IXGBE_SECRXCTRL_SECRX_DIS; 23862306a36Sopenharmony_ci IXGBE_WRITE_REG(hw, IXGBE_SECRXCTRL, reg); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci /* restore the "tx security buffer almost full threshold" to 0x250 */ 24162306a36Sopenharmony_ci IXGBE_WRITE_REG(hw, IXGBE_SECTXBUFFAF, 0x250); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci /* Set minimum IFG between packets back to the default 0x1 */ 24462306a36Sopenharmony_ci reg = IXGBE_READ_REG(hw, IXGBE_SECTXMINIFG); 24562306a36Sopenharmony_ci reg = (reg & 0xfffffff0) | 0x1; 24662306a36Sopenharmony_ci IXGBE_WRITE_REG(hw, IXGBE_SECTXMINIFG, reg); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci /* final set for normal (no ipsec offload) processing */ 24962306a36Sopenharmony_ci IXGBE_WRITE_REG(hw, IXGBE_SECTXCTRL, IXGBE_SECTXCTRL_SECTX_DIS); 25062306a36Sopenharmony_ci IXGBE_WRITE_REG(hw, IXGBE_SECRXCTRL, IXGBE_SECRXCTRL_SECRX_DIS); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci IXGBE_WRITE_FLUSH(hw); 25362306a36Sopenharmony_ci} 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci/** 25662306a36Sopenharmony_ci * ixgbe_ipsec_start_engine 25762306a36Sopenharmony_ci * @adapter: board private structure 25862306a36Sopenharmony_ci * 25962306a36Sopenharmony_ci * NOTE: this increases power consumption whether being used or not 26062306a36Sopenharmony_ci **/ 26162306a36Sopenharmony_cistatic void ixgbe_ipsec_start_engine(struct ixgbe_adapter *adapter) 26262306a36Sopenharmony_ci{ 26362306a36Sopenharmony_ci struct ixgbe_hw *hw = &adapter->hw; 26462306a36Sopenharmony_ci u32 reg; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci ixgbe_ipsec_stop_data(adapter); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci /* Set minimum IFG between packets to 3 */ 26962306a36Sopenharmony_ci reg = IXGBE_READ_REG(hw, IXGBE_SECTXMINIFG); 27062306a36Sopenharmony_ci reg = (reg & 0xfffffff0) | 0x3; 27162306a36Sopenharmony_ci IXGBE_WRITE_REG(hw, IXGBE_SECTXMINIFG, reg); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci /* Set "tx security buffer almost full threshold" to 0x15 so that the 27462306a36Sopenharmony_ci * almost full indication is generated only after buffer contains at 27562306a36Sopenharmony_ci * least an entire jumbo packet. 27662306a36Sopenharmony_ci */ 27762306a36Sopenharmony_ci reg = IXGBE_READ_REG(hw, IXGBE_SECTXBUFFAF); 27862306a36Sopenharmony_ci reg = (reg & 0xfffffc00) | 0x15; 27962306a36Sopenharmony_ci IXGBE_WRITE_REG(hw, IXGBE_SECTXBUFFAF, reg); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci /* restart the data paths by clearing the DISABLE bits */ 28262306a36Sopenharmony_ci IXGBE_WRITE_REG(hw, IXGBE_SECRXCTRL, 0); 28362306a36Sopenharmony_ci IXGBE_WRITE_REG(hw, IXGBE_SECTXCTRL, IXGBE_SECTXCTRL_STORE_FORWARD); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci /* enable Rx and Tx SA lookup */ 28662306a36Sopenharmony_ci IXGBE_WRITE_REG(hw, IXGBE_IPSTXIDX, IXGBE_RXTXIDX_IPS_EN); 28762306a36Sopenharmony_ci IXGBE_WRITE_REG(hw, IXGBE_IPSRXIDX, IXGBE_RXTXIDX_IPS_EN); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci IXGBE_WRITE_FLUSH(hw); 29062306a36Sopenharmony_ci} 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci/** 29362306a36Sopenharmony_ci * ixgbe_ipsec_restore - restore the ipsec HW settings after a reset 29462306a36Sopenharmony_ci * @adapter: board private structure 29562306a36Sopenharmony_ci * 29662306a36Sopenharmony_ci * Reload the HW tables from the SW tables after they've been bashed 29762306a36Sopenharmony_ci * by a chip reset. 29862306a36Sopenharmony_ci * 29962306a36Sopenharmony_ci * Any VF entries are removed from the SW and HW tables since either 30062306a36Sopenharmony_ci * (a) the VF also gets reset on PF reset and will ask again for the 30162306a36Sopenharmony_ci * offloads, or (b) the VF has been removed by a change in the num_vfs. 30262306a36Sopenharmony_ci **/ 30362306a36Sopenharmony_civoid ixgbe_ipsec_restore(struct ixgbe_adapter *adapter) 30462306a36Sopenharmony_ci{ 30562306a36Sopenharmony_ci struct ixgbe_ipsec *ipsec = adapter->ipsec; 30662306a36Sopenharmony_ci struct ixgbe_hw *hw = &adapter->hw; 30762306a36Sopenharmony_ci int i; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci if (!(adapter->flags2 & IXGBE_FLAG2_IPSEC_ENABLED)) 31062306a36Sopenharmony_ci return; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci /* clean up and restart the engine */ 31362306a36Sopenharmony_ci ixgbe_ipsec_stop_engine(adapter); 31462306a36Sopenharmony_ci ixgbe_ipsec_clear_hw_tables(adapter); 31562306a36Sopenharmony_ci ixgbe_ipsec_start_engine(adapter); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci /* reload the Rx and Tx keys */ 31862306a36Sopenharmony_ci for (i = 0; i < IXGBE_IPSEC_MAX_SA_COUNT; i++) { 31962306a36Sopenharmony_ci struct rx_sa *r = &ipsec->rx_tbl[i]; 32062306a36Sopenharmony_ci struct tx_sa *t = &ipsec->tx_tbl[i]; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci if (r->used) { 32362306a36Sopenharmony_ci if (r->mode & IXGBE_RXTXMOD_VF) 32462306a36Sopenharmony_ci ixgbe_ipsec_del_sa(r->xs); 32562306a36Sopenharmony_ci else 32662306a36Sopenharmony_ci ixgbe_ipsec_set_rx_sa(hw, i, r->xs->id.spi, 32762306a36Sopenharmony_ci r->key, r->salt, 32862306a36Sopenharmony_ci r->mode, r->iptbl_ind); 32962306a36Sopenharmony_ci } 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci if (t->used) { 33262306a36Sopenharmony_ci if (t->mode & IXGBE_RXTXMOD_VF) 33362306a36Sopenharmony_ci ixgbe_ipsec_del_sa(t->xs); 33462306a36Sopenharmony_ci else 33562306a36Sopenharmony_ci ixgbe_ipsec_set_tx_sa(hw, i, t->key, t->salt); 33662306a36Sopenharmony_ci } 33762306a36Sopenharmony_ci } 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci /* reload the IP addrs */ 34062306a36Sopenharmony_ci for (i = 0; i < IXGBE_IPSEC_MAX_RX_IP_COUNT; i++) { 34162306a36Sopenharmony_ci struct rx_ip_sa *ipsa = &ipsec->ip_tbl[i]; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci if (ipsa->used) 34462306a36Sopenharmony_ci ixgbe_ipsec_set_rx_ip(hw, i, ipsa->ipaddr); 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci} 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci/** 34962306a36Sopenharmony_ci * ixgbe_ipsec_find_empty_idx - find the first unused security parameter index 35062306a36Sopenharmony_ci * @ipsec: pointer to ipsec struct 35162306a36Sopenharmony_ci * @rxtable: true if we need to look in the Rx table 35262306a36Sopenharmony_ci * 35362306a36Sopenharmony_ci * Returns the first unused index in either the Rx or Tx SA table 35462306a36Sopenharmony_ci **/ 35562306a36Sopenharmony_cistatic int ixgbe_ipsec_find_empty_idx(struct ixgbe_ipsec *ipsec, bool rxtable) 35662306a36Sopenharmony_ci{ 35762306a36Sopenharmony_ci u32 i; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci if (rxtable) { 36062306a36Sopenharmony_ci if (ipsec->num_rx_sa == IXGBE_IPSEC_MAX_SA_COUNT) 36162306a36Sopenharmony_ci return -ENOSPC; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci /* search rx sa table */ 36462306a36Sopenharmony_ci for (i = 0; i < IXGBE_IPSEC_MAX_SA_COUNT; i++) { 36562306a36Sopenharmony_ci if (!ipsec->rx_tbl[i].used) 36662306a36Sopenharmony_ci return i; 36762306a36Sopenharmony_ci } 36862306a36Sopenharmony_ci } else { 36962306a36Sopenharmony_ci if (ipsec->num_tx_sa == IXGBE_IPSEC_MAX_SA_COUNT) 37062306a36Sopenharmony_ci return -ENOSPC; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci /* search tx sa table */ 37362306a36Sopenharmony_ci for (i = 0; i < IXGBE_IPSEC_MAX_SA_COUNT; i++) { 37462306a36Sopenharmony_ci if (!ipsec->tx_tbl[i].used) 37562306a36Sopenharmony_ci return i; 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci } 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci return -ENOSPC; 38062306a36Sopenharmony_ci} 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci/** 38362306a36Sopenharmony_ci * ixgbe_ipsec_find_rx_state - find the state that matches 38462306a36Sopenharmony_ci * @ipsec: pointer to ipsec struct 38562306a36Sopenharmony_ci * @daddr: inbound address to match 38662306a36Sopenharmony_ci * @proto: protocol to match 38762306a36Sopenharmony_ci * @spi: SPI to match 38862306a36Sopenharmony_ci * @ip4: true if using an ipv4 address 38962306a36Sopenharmony_ci * 39062306a36Sopenharmony_ci * Returns a pointer to the matching SA state information 39162306a36Sopenharmony_ci **/ 39262306a36Sopenharmony_cistatic struct xfrm_state *ixgbe_ipsec_find_rx_state(struct ixgbe_ipsec *ipsec, 39362306a36Sopenharmony_ci __be32 *daddr, u8 proto, 39462306a36Sopenharmony_ci __be32 spi, bool ip4) 39562306a36Sopenharmony_ci{ 39662306a36Sopenharmony_ci struct rx_sa *rsa; 39762306a36Sopenharmony_ci struct xfrm_state *ret = NULL; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci rcu_read_lock(); 40062306a36Sopenharmony_ci hash_for_each_possible_rcu(ipsec->rx_sa_list, rsa, hlist, 40162306a36Sopenharmony_ci (__force u32)spi) { 40262306a36Sopenharmony_ci if (rsa->mode & IXGBE_RXTXMOD_VF) 40362306a36Sopenharmony_ci continue; 40462306a36Sopenharmony_ci if (spi == rsa->xs->id.spi && 40562306a36Sopenharmony_ci ((ip4 && *daddr == rsa->xs->id.daddr.a4) || 40662306a36Sopenharmony_ci (!ip4 && !memcmp(daddr, &rsa->xs->id.daddr.a6, 40762306a36Sopenharmony_ci sizeof(rsa->xs->id.daddr.a6)))) && 40862306a36Sopenharmony_ci proto == rsa->xs->id.proto) { 40962306a36Sopenharmony_ci ret = rsa->xs; 41062306a36Sopenharmony_ci xfrm_state_hold(ret); 41162306a36Sopenharmony_ci break; 41262306a36Sopenharmony_ci } 41362306a36Sopenharmony_ci } 41462306a36Sopenharmony_ci rcu_read_unlock(); 41562306a36Sopenharmony_ci return ret; 41662306a36Sopenharmony_ci} 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci/** 41962306a36Sopenharmony_ci * ixgbe_ipsec_parse_proto_keys - find the key and salt based on the protocol 42062306a36Sopenharmony_ci * @xs: pointer to xfrm_state struct 42162306a36Sopenharmony_ci * @mykey: pointer to key array to populate 42262306a36Sopenharmony_ci * @mysalt: pointer to salt value to populate 42362306a36Sopenharmony_ci * 42462306a36Sopenharmony_ci * This copies the protocol keys and salt to our own data tables. The 42562306a36Sopenharmony_ci * 82599 family only supports the one algorithm. 42662306a36Sopenharmony_ci **/ 42762306a36Sopenharmony_cistatic int ixgbe_ipsec_parse_proto_keys(struct xfrm_state *xs, 42862306a36Sopenharmony_ci u32 *mykey, u32 *mysalt) 42962306a36Sopenharmony_ci{ 43062306a36Sopenharmony_ci struct net_device *dev = xs->xso.real_dev; 43162306a36Sopenharmony_ci unsigned char *key_data; 43262306a36Sopenharmony_ci char *alg_name = NULL; 43362306a36Sopenharmony_ci int key_len; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci if (!xs->aead) { 43662306a36Sopenharmony_ci netdev_err(dev, "Unsupported IPsec algorithm\n"); 43762306a36Sopenharmony_ci return -EINVAL; 43862306a36Sopenharmony_ci } 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci if (xs->aead->alg_icv_len != IXGBE_IPSEC_AUTH_BITS) { 44162306a36Sopenharmony_ci netdev_err(dev, "IPsec offload requires %d bit authentication\n", 44262306a36Sopenharmony_ci IXGBE_IPSEC_AUTH_BITS); 44362306a36Sopenharmony_ci return -EINVAL; 44462306a36Sopenharmony_ci } 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci key_data = &xs->aead->alg_key[0]; 44762306a36Sopenharmony_ci key_len = xs->aead->alg_key_len; 44862306a36Sopenharmony_ci alg_name = xs->aead->alg_name; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci if (strcmp(alg_name, aes_gcm_name)) { 45162306a36Sopenharmony_ci netdev_err(dev, "Unsupported IPsec algorithm - please use %s\n", 45262306a36Sopenharmony_ci aes_gcm_name); 45362306a36Sopenharmony_ci return -EINVAL; 45462306a36Sopenharmony_ci } 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci /* The key bytes come down in a bigendian array of bytes, so 45762306a36Sopenharmony_ci * we don't need to do any byteswapping. 45862306a36Sopenharmony_ci * 160 accounts for 16 byte key and 4 byte salt 45962306a36Sopenharmony_ci */ 46062306a36Sopenharmony_ci if (key_len == IXGBE_IPSEC_KEY_BITS) { 46162306a36Sopenharmony_ci *mysalt = ((u32 *)key_data)[4]; 46262306a36Sopenharmony_ci } else if (key_len != (IXGBE_IPSEC_KEY_BITS - (sizeof(*mysalt) * 8))) { 46362306a36Sopenharmony_ci netdev_err(dev, "IPsec hw offload only supports keys up to 128 bits with a 32 bit salt\n"); 46462306a36Sopenharmony_ci return -EINVAL; 46562306a36Sopenharmony_ci } else { 46662306a36Sopenharmony_ci netdev_info(dev, "IPsec hw offload parameters missing 32 bit salt value\n"); 46762306a36Sopenharmony_ci *mysalt = 0; 46862306a36Sopenharmony_ci } 46962306a36Sopenharmony_ci memcpy(mykey, key_data, 16); 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci return 0; 47262306a36Sopenharmony_ci} 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci/** 47562306a36Sopenharmony_ci * ixgbe_ipsec_check_mgmt_ip - make sure there is no clash with mgmt IP filters 47662306a36Sopenharmony_ci * @xs: pointer to transformer state struct 47762306a36Sopenharmony_ci **/ 47862306a36Sopenharmony_cistatic int ixgbe_ipsec_check_mgmt_ip(struct xfrm_state *xs) 47962306a36Sopenharmony_ci{ 48062306a36Sopenharmony_ci struct net_device *dev = xs->xso.real_dev; 48162306a36Sopenharmony_ci struct ixgbe_adapter *adapter = netdev_priv(dev); 48262306a36Sopenharmony_ci struct ixgbe_hw *hw = &adapter->hw; 48362306a36Sopenharmony_ci u32 mfval, manc, reg; 48462306a36Sopenharmony_ci int num_filters = 4; 48562306a36Sopenharmony_ci bool manc_ipv4; 48662306a36Sopenharmony_ci u32 bmcipval; 48762306a36Sopenharmony_ci int i, j; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci#define MANC_EN_IPV4_FILTER BIT(24) 49062306a36Sopenharmony_ci#define MFVAL_IPV4_FILTER_SHIFT 16 49162306a36Sopenharmony_ci#define MFVAL_IPV6_FILTER_SHIFT 24 49262306a36Sopenharmony_ci#define MIPAF_ARR(_m, _n) (IXGBE_MIPAF + ((_m) * 0x10) + ((_n) * 4)) 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci#define IXGBE_BMCIP(_n) (0x5050 + ((_n) * 4)) 49562306a36Sopenharmony_ci#define IXGBE_BMCIPVAL 0x5060 49662306a36Sopenharmony_ci#define BMCIP_V4 0x2 49762306a36Sopenharmony_ci#define BMCIP_V6 0x3 49862306a36Sopenharmony_ci#define BMCIP_MASK 0x3 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci manc = IXGBE_READ_REG(hw, IXGBE_MANC); 50162306a36Sopenharmony_ci manc_ipv4 = !!(manc & MANC_EN_IPV4_FILTER); 50262306a36Sopenharmony_ci mfval = IXGBE_READ_REG(hw, IXGBE_MFVAL); 50362306a36Sopenharmony_ci bmcipval = IXGBE_READ_REG(hw, IXGBE_BMCIPVAL); 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci if (xs->props.family == AF_INET) { 50662306a36Sopenharmony_ci /* are there any IPv4 filters to check? */ 50762306a36Sopenharmony_ci if (manc_ipv4) { 50862306a36Sopenharmony_ci /* the 4 ipv4 filters are all in MIPAF(3, i) */ 50962306a36Sopenharmony_ci for (i = 0; i < num_filters; i++) { 51062306a36Sopenharmony_ci if (!(mfval & BIT(MFVAL_IPV4_FILTER_SHIFT + i))) 51162306a36Sopenharmony_ci continue; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci reg = IXGBE_READ_REG(hw, MIPAF_ARR(3, i)); 51462306a36Sopenharmony_ci if (reg == (__force u32)xs->id.daddr.a4) 51562306a36Sopenharmony_ci return 1; 51662306a36Sopenharmony_ci } 51762306a36Sopenharmony_ci } 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci if ((bmcipval & BMCIP_MASK) == BMCIP_V4) { 52062306a36Sopenharmony_ci reg = IXGBE_READ_REG(hw, IXGBE_BMCIP(3)); 52162306a36Sopenharmony_ci if (reg == (__force u32)xs->id.daddr.a4) 52262306a36Sopenharmony_ci return 1; 52362306a36Sopenharmony_ci } 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci } else { 52662306a36Sopenharmony_ci /* if there are ipv4 filters, they are in the last ipv6 slot */ 52762306a36Sopenharmony_ci if (manc_ipv4) 52862306a36Sopenharmony_ci num_filters = 3; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci for (i = 0; i < num_filters; i++) { 53162306a36Sopenharmony_ci if (!(mfval & BIT(MFVAL_IPV6_FILTER_SHIFT + i))) 53262306a36Sopenharmony_ci continue; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci for (j = 0; j < 4; j++) { 53562306a36Sopenharmony_ci reg = IXGBE_READ_REG(hw, MIPAF_ARR(i, j)); 53662306a36Sopenharmony_ci if (reg != (__force u32)xs->id.daddr.a6[j]) 53762306a36Sopenharmony_ci break; 53862306a36Sopenharmony_ci } 53962306a36Sopenharmony_ci if (j == 4) /* did we match all 4 words? */ 54062306a36Sopenharmony_ci return 1; 54162306a36Sopenharmony_ci } 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci if ((bmcipval & BMCIP_MASK) == BMCIP_V6) { 54462306a36Sopenharmony_ci for (j = 0; j < 4; j++) { 54562306a36Sopenharmony_ci reg = IXGBE_READ_REG(hw, IXGBE_BMCIP(j)); 54662306a36Sopenharmony_ci if (reg != (__force u32)xs->id.daddr.a6[j]) 54762306a36Sopenharmony_ci break; 54862306a36Sopenharmony_ci } 54962306a36Sopenharmony_ci if (j == 4) /* did we match all 4 words? */ 55062306a36Sopenharmony_ci return 1; 55162306a36Sopenharmony_ci } 55262306a36Sopenharmony_ci } 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci return 0; 55562306a36Sopenharmony_ci} 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci/** 55862306a36Sopenharmony_ci * ixgbe_ipsec_add_sa - program device with a security association 55962306a36Sopenharmony_ci * @xs: pointer to transformer state struct 56062306a36Sopenharmony_ci * @extack: extack point to fill failure reason 56162306a36Sopenharmony_ci **/ 56262306a36Sopenharmony_cistatic int ixgbe_ipsec_add_sa(struct xfrm_state *xs, 56362306a36Sopenharmony_ci struct netlink_ext_ack *extack) 56462306a36Sopenharmony_ci{ 56562306a36Sopenharmony_ci struct net_device *dev = xs->xso.real_dev; 56662306a36Sopenharmony_ci struct ixgbe_adapter *adapter = netdev_priv(dev); 56762306a36Sopenharmony_ci struct ixgbe_ipsec *ipsec = adapter->ipsec; 56862306a36Sopenharmony_ci struct ixgbe_hw *hw = &adapter->hw; 56962306a36Sopenharmony_ci int checked, match, first; 57062306a36Sopenharmony_ci u16 sa_idx; 57162306a36Sopenharmony_ci int ret; 57262306a36Sopenharmony_ci int i; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci if (xs->id.proto != IPPROTO_ESP && xs->id.proto != IPPROTO_AH) { 57562306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Unsupported protocol for ipsec offload"); 57662306a36Sopenharmony_ci return -EINVAL; 57762306a36Sopenharmony_ci } 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci if (xs->props.mode != XFRM_MODE_TRANSPORT) { 58062306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Unsupported mode for ipsec offload"); 58162306a36Sopenharmony_ci return -EINVAL; 58262306a36Sopenharmony_ci } 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci if (ixgbe_ipsec_check_mgmt_ip(xs)) { 58562306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "IPsec IP addr clash with mgmt filters"); 58662306a36Sopenharmony_ci return -EINVAL; 58762306a36Sopenharmony_ci } 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci if (xs->xso.type != XFRM_DEV_OFFLOAD_CRYPTO) { 59062306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Unsupported ipsec offload type"); 59162306a36Sopenharmony_ci return -EINVAL; 59262306a36Sopenharmony_ci } 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci if (xs->xso.dir == XFRM_DEV_OFFLOAD_IN) { 59562306a36Sopenharmony_ci struct rx_sa rsa; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci if (xs->calg) { 59862306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Compression offload not supported"); 59962306a36Sopenharmony_ci return -EINVAL; 60062306a36Sopenharmony_ci } 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci /* find the first unused index */ 60362306a36Sopenharmony_ci ret = ixgbe_ipsec_find_empty_idx(ipsec, true); 60462306a36Sopenharmony_ci if (ret < 0) { 60562306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "No space for SA in Rx table!"); 60662306a36Sopenharmony_ci return ret; 60762306a36Sopenharmony_ci } 60862306a36Sopenharmony_ci sa_idx = (u16)ret; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci memset(&rsa, 0, sizeof(rsa)); 61162306a36Sopenharmony_ci rsa.used = true; 61262306a36Sopenharmony_ci rsa.xs = xs; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci if (rsa.xs->id.proto & IPPROTO_ESP) 61562306a36Sopenharmony_ci rsa.decrypt = xs->ealg || xs->aead; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci /* get the key and salt */ 61862306a36Sopenharmony_ci ret = ixgbe_ipsec_parse_proto_keys(xs, rsa.key, &rsa.salt); 61962306a36Sopenharmony_ci if (ret) { 62062306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Failed to get key data for Rx SA table"); 62162306a36Sopenharmony_ci return ret; 62262306a36Sopenharmony_ci } 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci /* get ip for rx sa table */ 62562306a36Sopenharmony_ci if (xs->props.family == AF_INET6) 62662306a36Sopenharmony_ci memcpy(rsa.ipaddr, &xs->id.daddr.a6, 16); 62762306a36Sopenharmony_ci else 62862306a36Sopenharmony_ci memcpy(&rsa.ipaddr[3], &xs->id.daddr.a4, 4); 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci /* The HW does not have a 1:1 mapping from keys to IP addrs, so 63162306a36Sopenharmony_ci * check for a matching IP addr entry in the table. If the addr 63262306a36Sopenharmony_ci * already exists, use it; else find an unused slot and add the 63362306a36Sopenharmony_ci * addr. If one does not exist and there are no unused table 63462306a36Sopenharmony_ci * entries, fail the request. 63562306a36Sopenharmony_ci */ 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci /* Find an existing match or first not used, and stop looking 63862306a36Sopenharmony_ci * after we've checked all we know we have. 63962306a36Sopenharmony_ci */ 64062306a36Sopenharmony_ci checked = 0; 64162306a36Sopenharmony_ci match = -1; 64262306a36Sopenharmony_ci first = -1; 64362306a36Sopenharmony_ci for (i = 0; 64462306a36Sopenharmony_ci i < IXGBE_IPSEC_MAX_RX_IP_COUNT && 64562306a36Sopenharmony_ci (checked < ipsec->num_rx_sa || first < 0); 64662306a36Sopenharmony_ci i++) { 64762306a36Sopenharmony_ci if (ipsec->ip_tbl[i].used) { 64862306a36Sopenharmony_ci if (!memcmp(ipsec->ip_tbl[i].ipaddr, 64962306a36Sopenharmony_ci rsa.ipaddr, sizeof(rsa.ipaddr))) { 65062306a36Sopenharmony_ci match = i; 65162306a36Sopenharmony_ci break; 65262306a36Sopenharmony_ci } 65362306a36Sopenharmony_ci checked++; 65462306a36Sopenharmony_ci } else if (first < 0) { 65562306a36Sopenharmony_ci first = i; /* track the first empty seen */ 65662306a36Sopenharmony_ci } 65762306a36Sopenharmony_ci } 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci if (ipsec->num_rx_sa == 0) 66062306a36Sopenharmony_ci first = 0; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci if (match >= 0) { 66362306a36Sopenharmony_ci /* addrs are the same, we should use this one */ 66462306a36Sopenharmony_ci rsa.iptbl_ind = match; 66562306a36Sopenharmony_ci ipsec->ip_tbl[match].ref_cnt++; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci } else if (first >= 0) { 66862306a36Sopenharmony_ci /* no matches, but here's an empty slot */ 66962306a36Sopenharmony_ci rsa.iptbl_ind = first; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci memcpy(ipsec->ip_tbl[first].ipaddr, 67262306a36Sopenharmony_ci rsa.ipaddr, sizeof(rsa.ipaddr)); 67362306a36Sopenharmony_ci ipsec->ip_tbl[first].ref_cnt = 1; 67462306a36Sopenharmony_ci ipsec->ip_tbl[first].used = true; 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci ixgbe_ipsec_set_rx_ip(hw, rsa.iptbl_ind, rsa.ipaddr); 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci } else { 67962306a36Sopenharmony_ci /* no match and no empty slot */ 68062306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "No space for SA in Rx IP SA table"); 68162306a36Sopenharmony_ci memset(&rsa, 0, sizeof(rsa)); 68262306a36Sopenharmony_ci return -ENOSPC; 68362306a36Sopenharmony_ci } 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci rsa.mode = IXGBE_RXMOD_VALID; 68662306a36Sopenharmony_ci if (rsa.xs->id.proto & IPPROTO_ESP) 68762306a36Sopenharmony_ci rsa.mode |= IXGBE_RXMOD_PROTO_ESP; 68862306a36Sopenharmony_ci if (rsa.decrypt) 68962306a36Sopenharmony_ci rsa.mode |= IXGBE_RXMOD_DECRYPT; 69062306a36Sopenharmony_ci if (rsa.xs->props.family == AF_INET6) 69162306a36Sopenharmony_ci rsa.mode |= IXGBE_RXMOD_IPV6; 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci /* the preparations worked, so save the info */ 69462306a36Sopenharmony_ci memcpy(&ipsec->rx_tbl[sa_idx], &rsa, sizeof(rsa)); 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci ixgbe_ipsec_set_rx_sa(hw, sa_idx, rsa.xs->id.spi, rsa.key, 69762306a36Sopenharmony_ci rsa.salt, rsa.mode, rsa.iptbl_ind); 69862306a36Sopenharmony_ci xs->xso.offload_handle = sa_idx + IXGBE_IPSEC_BASE_RX_INDEX; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci ipsec->num_rx_sa++; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci /* hash the new entry for faster search in Rx path */ 70362306a36Sopenharmony_ci hash_add_rcu(ipsec->rx_sa_list, &ipsec->rx_tbl[sa_idx].hlist, 70462306a36Sopenharmony_ci (__force u32)rsa.xs->id.spi); 70562306a36Sopenharmony_ci } else { 70662306a36Sopenharmony_ci struct tx_sa tsa; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci if (adapter->num_vfs && 70962306a36Sopenharmony_ci adapter->bridge_mode != BRIDGE_MODE_VEPA) 71062306a36Sopenharmony_ci return -EOPNOTSUPP; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci /* find the first unused index */ 71362306a36Sopenharmony_ci ret = ixgbe_ipsec_find_empty_idx(ipsec, false); 71462306a36Sopenharmony_ci if (ret < 0) { 71562306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "No space for SA in Tx table"); 71662306a36Sopenharmony_ci return ret; 71762306a36Sopenharmony_ci } 71862306a36Sopenharmony_ci sa_idx = (u16)ret; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci memset(&tsa, 0, sizeof(tsa)); 72162306a36Sopenharmony_ci tsa.used = true; 72262306a36Sopenharmony_ci tsa.xs = xs; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci if (xs->id.proto & IPPROTO_ESP) 72562306a36Sopenharmony_ci tsa.encrypt = xs->ealg || xs->aead; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci ret = ixgbe_ipsec_parse_proto_keys(xs, tsa.key, &tsa.salt); 72862306a36Sopenharmony_ci if (ret) { 72962306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Failed to get key data for Tx SA table"); 73062306a36Sopenharmony_ci memset(&tsa, 0, sizeof(tsa)); 73162306a36Sopenharmony_ci return ret; 73262306a36Sopenharmony_ci } 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci /* the preparations worked, so save the info */ 73562306a36Sopenharmony_ci memcpy(&ipsec->tx_tbl[sa_idx], &tsa, sizeof(tsa)); 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci ixgbe_ipsec_set_tx_sa(hw, sa_idx, tsa.key, tsa.salt); 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci xs->xso.offload_handle = sa_idx + IXGBE_IPSEC_BASE_TX_INDEX; 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci ipsec->num_tx_sa++; 74262306a36Sopenharmony_ci } 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci /* enable the engine if not already warmed up */ 74562306a36Sopenharmony_ci if (!(adapter->flags2 & IXGBE_FLAG2_IPSEC_ENABLED)) { 74662306a36Sopenharmony_ci ixgbe_ipsec_start_engine(adapter); 74762306a36Sopenharmony_ci adapter->flags2 |= IXGBE_FLAG2_IPSEC_ENABLED; 74862306a36Sopenharmony_ci } 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci return 0; 75162306a36Sopenharmony_ci} 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci/** 75462306a36Sopenharmony_ci * ixgbe_ipsec_del_sa - clear out this specific SA 75562306a36Sopenharmony_ci * @xs: pointer to transformer state struct 75662306a36Sopenharmony_ci **/ 75762306a36Sopenharmony_cistatic void ixgbe_ipsec_del_sa(struct xfrm_state *xs) 75862306a36Sopenharmony_ci{ 75962306a36Sopenharmony_ci struct net_device *dev = xs->xso.real_dev; 76062306a36Sopenharmony_ci struct ixgbe_adapter *adapter = netdev_priv(dev); 76162306a36Sopenharmony_ci struct ixgbe_ipsec *ipsec = adapter->ipsec; 76262306a36Sopenharmony_ci struct ixgbe_hw *hw = &adapter->hw; 76362306a36Sopenharmony_ci u32 zerobuf[4] = {0, 0, 0, 0}; 76462306a36Sopenharmony_ci u16 sa_idx; 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci if (xs->xso.dir == XFRM_DEV_OFFLOAD_IN) { 76762306a36Sopenharmony_ci struct rx_sa *rsa; 76862306a36Sopenharmony_ci u8 ipi; 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci sa_idx = xs->xso.offload_handle - IXGBE_IPSEC_BASE_RX_INDEX; 77162306a36Sopenharmony_ci rsa = &ipsec->rx_tbl[sa_idx]; 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci if (!rsa->used) { 77462306a36Sopenharmony_ci netdev_err(dev, "Invalid Rx SA selected sa_idx=%d offload_handle=%lu\n", 77562306a36Sopenharmony_ci sa_idx, xs->xso.offload_handle); 77662306a36Sopenharmony_ci return; 77762306a36Sopenharmony_ci } 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci ixgbe_ipsec_set_rx_sa(hw, sa_idx, 0, zerobuf, 0, 0, 0); 78062306a36Sopenharmony_ci hash_del_rcu(&rsa->hlist); 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci /* if the IP table entry is referenced by only this SA, 78362306a36Sopenharmony_ci * i.e. ref_cnt is only 1, clear the IP table entry as well 78462306a36Sopenharmony_ci */ 78562306a36Sopenharmony_ci ipi = rsa->iptbl_ind; 78662306a36Sopenharmony_ci if (ipsec->ip_tbl[ipi].ref_cnt > 0) { 78762306a36Sopenharmony_ci ipsec->ip_tbl[ipi].ref_cnt--; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci if (!ipsec->ip_tbl[ipi].ref_cnt) { 79062306a36Sopenharmony_ci memset(&ipsec->ip_tbl[ipi], 0, 79162306a36Sopenharmony_ci sizeof(struct rx_ip_sa)); 79262306a36Sopenharmony_ci ixgbe_ipsec_set_rx_ip(hw, ipi, 79362306a36Sopenharmony_ci (__force __be32 *)zerobuf); 79462306a36Sopenharmony_ci } 79562306a36Sopenharmony_ci } 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci memset(rsa, 0, sizeof(struct rx_sa)); 79862306a36Sopenharmony_ci ipsec->num_rx_sa--; 79962306a36Sopenharmony_ci } else { 80062306a36Sopenharmony_ci sa_idx = xs->xso.offload_handle - IXGBE_IPSEC_BASE_TX_INDEX; 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci if (!ipsec->tx_tbl[sa_idx].used) { 80362306a36Sopenharmony_ci netdev_err(dev, "Invalid Tx SA selected sa_idx=%d offload_handle=%lu\n", 80462306a36Sopenharmony_ci sa_idx, xs->xso.offload_handle); 80562306a36Sopenharmony_ci return; 80662306a36Sopenharmony_ci } 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci ixgbe_ipsec_set_tx_sa(hw, sa_idx, zerobuf, 0); 80962306a36Sopenharmony_ci memset(&ipsec->tx_tbl[sa_idx], 0, sizeof(struct tx_sa)); 81062306a36Sopenharmony_ci ipsec->num_tx_sa--; 81162306a36Sopenharmony_ci } 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci /* if there are no SAs left, stop the engine to save energy */ 81462306a36Sopenharmony_ci if (ipsec->num_rx_sa == 0 && ipsec->num_tx_sa == 0) { 81562306a36Sopenharmony_ci adapter->flags2 &= ~IXGBE_FLAG2_IPSEC_ENABLED; 81662306a36Sopenharmony_ci ixgbe_ipsec_stop_engine(adapter); 81762306a36Sopenharmony_ci } 81862306a36Sopenharmony_ci} 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci/** 82162306a36Sopenharmony_ci * ixgbe_ipsec_offload_ok - can this packet use the xfrm hw offload 82262306a36Sopenharmony_ci * @skb: current data packet 82362306a36Sopenharmony_ci * @xs: pointer to transformer state struct 82462306a36Sopenharmony_ci **/ 82562306a36Sopenharmony_cistatic bool ixgbe_ipsec_offload_ok(struct sk_buff *skb, struct xfrm_state *xs) 82662306a36Sopenharmony_ci{ 82762306a36Sopenharmony_ci if (xs->props.family == AF_INET) { 82862306a36Sopenharmony_ci /* Offload with IPv4 options is not supported yet */ 82962306a36Sopenharmony_ci if (ip_hdr(skb)->ihl != 5) 83062306a36Sopenharmony_ci return false; 83162306a36Sopenharmony_ci } else { 83262306a36Sopenharmony_ci /* Offload with IPv6 extension headers is not support yet */ 83362306a36Sopenharmony_ci if (ipv6_ext_hdr(ipv6_hdr(skb)->nexthdr)) 83462306a36Sopenharmony_ci return false; 83562306a36Sopenharmony_ci } 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci return true; 83862306a36Sopenharmony_ci} 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_cistatic const struct xfrmdev_ops ixgbe_xfrmdev_ops = { 84162306a36Sopenharmony_ci .xdo_dev_state_add = ixgbe_ipsec_add_sa, 84262306a36Sopenharmony_ci .xdo_dev_state_delete = ixgbe_ipsec_del_sa, 84362306a36Sopenharmony_ci .xdo_dev_offload_ok = ixgbe_ipsec_offload_ok, 84462306a36Sopenharmony_ci}; 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci/** 84762306a36Sopenharmony_ci * ixgbe_ipsec_vf_clear - clear the tables of data for a VF 84862306a36Sopenharmony_ci * @adapter: board private structure 84962306a36Sopenharmony_ci * @vf: VF id to be removed 85062306a36Sopenharmony_ci **/ 85162306a36Sopenharmony_civoid ixgbe_ipsec_vf_clear(struct ixgbe_adapter *adapter, u32 vf) 85262306a36Sopenharmony_ci{ 85362306a36Sopenharmony_ci struct ixgbe_ipsec *ipsec = adapter->ipsec; 85462306a36Sopenharmony_ci int i; 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci if (!ipsec) 85762306a36Sopenharmony_ci return; 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci /* search rx sa table */ 86062306a36Sopenharmony_ci for (i = 0; i < IXGBE_IPSEC_MAX_SA_COUNT && ipsec->num_rx_sa; i++) { 86162306a36Sopenharmony_ci if (!ipsec->rx_tbl[i].used) 86262306a36Sopenharmony_ci continue; 86362306a36Sopenharmony_ci if (ipsec->rx_tbl[i].mode & IXGBE_RXTXMOD_VF && 86462306a36Sopenharmony_ci ipsec->rx_tbl[i].vf == vf) 86562306a36Sopenharmony_ci ixgbe_ipsec_del_sa(ipsec->rx_tbl[i].xs); 86662306a36Sopenharmony_ci } 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci /* search tx sa table */ 86962306a36Sopenharmony_ci for (i = 0; i < IXGBE_IPSEC_MAX_SA_COUNT && ipsec->num_tx_sa; i++) { 87062306a36Sopenharmony_ci if (!ipsec->tx_tbl[i].used) 87162306a36Sopenharmony_ci continue; 87262306a36Sopenharmony_ci if (ipsec->tx_tbl[i].mode & IXGBE_RXTXMOD_VF && 87362306a36Sopenharmony_ci ipsec->tx_tbl[i].vf == vf) 87462306a36Sopenharmony_ci ixgbe_ipsec_del_sa(ipsec->tx_tbl[i].xs); 87562306a36Sopenharmony_ci } 87662306a36Sopenharmony_ci} 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci/** 87962306a36Sopenharmony_ci * ixgbe_ipsec_vf_add_sa - translate VF request to SA add 88062306a36Sopenharmony_ci * @adapter: board private structure 88162306a36Sopenharmony_ci * @msgbuf: The message buffer 88262306a36Sopenharmony_ci * @vf: the VF index 88362306a36Sopenharmony_ci * 88462306a36Sopenharmony_ci * Make up a new xs and algorithm info from the data sent by the VF. 88562306a36Sopenharmony_ci * We only need to sketch in just enough to set up the HW offload. 88662306a36Sopenharmony_ci * Put the resulting offload_handle into the return message to the VF. 88762306a36Sopenharmony_ci * 88862306a36Sopenharmony_ci * Returns 0 or error value 88962306a36Sopenharmony_ci **/ 89062306a36Sopenharmony_ciint ixgbe_ipsec_vf_add_sa(struct ixgbe_adapter *adapter, u32 *msgbuf, u32 vf) 89162306a36Sopenharmony_ci{ 89262306a36Sopenharmony_ci struct ixgbe_ipsec *ipsec = adapter->ipsec; 89362306a36Sopenharmony_ci struct xfrm_algo_desc *algo; 89462306a36Sopenharmony_ci struct sa_mbx_msg *sam; 89562306a36Sopenharmony_ci struct xfrm_state *xs; 89662306a36Sopenharmony_ci size_t aead_len; 89762306a36Sopenharmony_ci u16 sa_idx; 89862306a36Sopenharmony_ci u32 pfsa; 89962306a36Sopenharmony_ci int err; 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci sam = (struct sa_mbx_msg *)(&msgbuf[1]); 90262306a36Sopenharmony_ci if (!adapter->vfinfo[vf].trusted || 90362306a36Sopenharmony_ci !(adapter->flags2 & IXGBE_FLAG2_VF_IPSEC_ENABLED)) { 90462306a36Sopenharmony_ci e_warn(drv, "VF %d attempted to add an IPsec SA\n", vf); 90562306a36Sopenharmony_ci err = -EACCES; 90662306a36Sopenharmony_ci goto err_out; 90762306a36Sopenharmony_ci } 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci /* Tx IPsec offload doesn't seem to work on this 91062306a36Sopenharmony_ci * device, so block these requests for now. 91162306a36Sopenharmony_ci */ 91262306a36Sopenharmony_ci if (sam->dir != XFRM_DEV_OFFLOAD_IN) { 91362306a36Sopenharmony_ci err = -EOPNOTSUPP; 91462306a36Sopenharmony_ci goto err_out; 91562306a36Sopenharmony_ci } 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci xs = kzalloc(sizeof(*xs), GFP_KERNEL); 91862306a36Sopenharmony_ci if (unlikely(!xs)) { 91962306a36Sopenharmony_ci err = -ENOMEM; 92062306a36Sopenharmony_ci goto err_out; 92162306a36Sopenharmony_ci } 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci xs->xso.dir = sam->dir; 92462306a36Sopenharmony_ci xs->id.spi = sam->spi; 92562306a36Sopenharmony_ci xs->id.proto = sam->proto; 92662306a36Sopenharmony_ci xs->props.family = sam->family; 92762306a36Sopenharmony_ci if (xs->props.family == AF_INET6) 92862306a36Sopenharmony_ci memcpy(&xs->id.daddr.a6, sam->addr, sizeof(xs->id.daddr.a6)); 92962306a36Sopenharmony_ci else 93062306a36Sopenharmony_ci memcpy(&xs->id.daddr.a4, sam->addr, sizeof(xs->id.daddr.a4)); 93162306a36Sopenharmony_ci xs->xso.dev = adapter->netdev; 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci algo = xfrm_aead_get_byname(aes_gcm_name, IXGBE_IPSEC_AUTH_BITS, 1); 93462306a36Sopenharmony_ci if (unlikely(!algo)) { 93562306a36Sopenharmony_ci err = -ENOENT; 93662306a36Sopenharmony_ci goto err_xs; 93762306a36Sopenharmony_ci } 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci aead_len = sizeof(*xs->aead) + IXGBE_IPSEC_KEY_BITS / 8; 94062306a36Sopenharmony_ci xs->aead = kzalloc(aead_len, GFP_KERNEL); 94162306a36Sopenharmony_ci if (unlikely(!xs->aead)) { 94262306a36Sopenharmony_ci err = -ENOMEM; 94362306a36Sopenharmony_ci goto err_xs; 94462306a36Sopenharmony_ci } 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci xs->props.ealgo = algo->desc.sadb_alg_id; 94762306a36Sopenharmony_ci xs->geniv = algo->uinfo.aead.geniv; 94862306a36Sopenharmony_ci xs->aead->alg_icv_len = IXGBE_IPSEC_AUTH_BITS; 94962306a36Sopenharmony_ci xs->aead->alg_key_len = IXGBE_IPSEC_KEY_BITS; 95062306a36Sopenharmony_ci memcpy(xs->aead->alg_key, sam->key, sizeof(sam->key)); 95162306a36Sopenharmony_ci memcpy(xs->aead->alg_name, aes_gcm_name, sizeof(aes_gcm_name)); 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci /* set up the HW offload */ 95462306a36Sopenharmony_ci err = ixgbe_ipsec_add_sa(xs, NULL); 95562306a36Sopenharmony_ci if (err) 95662306a36Sopenharmony_ci goto err_aead; 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci pfsa = xs->xso.offload_handle; 95962306a36Sopenharmony_ci if (pfsa < IXGBE_IPSEC_BASE_TX_INDEX) { 96062306a36Sopenharmony_ci sa_idx = pfsa - IXGBE_IPSEC_BASE_RX_INDEX; 96162306a36Sopenharmony_ci ipsec->rx_tbl[sa_idx].vf = vf; 96262306a36Sopenharmony_ci ipsec->rx_tbl[sa_idx].mode |= IXGBE_RXTXMOD_VF; 96362306a36Sopenharmony_ci } else { 96462306a36Sopenharmony_ci sa_idx = pfsa - IXGBE_IPSEC_BASE_TX_INDEX; 96562306a36Sopenharmony_ci ipsec->tx_tbl[sa_idx].vf = vf; 96662306a36Sopenharmony_ci ipsec->tx_tbl[sa_idx].mode |= IXGBE_RXTXMOD_VF; 96762306a36Sopenharmony_ci } 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci msgbuf[1] = xs->xso.offload_handle; 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci return 0; 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_cierr_aead: 97462306a36Sopenharmony_ci kfree_sensitive(xs->aead); 97562306a36Sopenharmony_cierr_xs: 97662306a36Sopenharmony_ci kfree_sensitive(xs); 97762306a36Sopenharmony_cierr_out: 97862306a36Sopenharmony_ci msgbuf[1] = err; 97962306a36Sopenharmony_ci return err; 98062306a36Sopenharmony_ci} 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci/** 98362306a36Sopenharmony_ci * ixgbe_ipsec_vf_del_sa - translate VF request to SA delete 98462306a36Sopenharmony_ci * @adapter: board private structure 98562306a36Sopenharmony_ci * @msgbuf: The message buffer 98662306a36Sopenharmony_ci * @vf: the VF index 98762306a36Sopenharmony_ci * 98862306a36Sopenharmony_ci * Given the offload_handle sent by the VF, look for the related SA table 98962306a36Sopenharmony_ci * entry and use its xs field to call for a delete of the SA. 99062306a36Sopenharmony_ci * 99162306a36Sopenharmony_ci * Note: We silently ignore requests to delete entries that are already 99262306a36Sopenharmony_ci * set to unused because when a VF is set to "DOWN", the PF first 99362306a36Sopenharmony_ci * gets a reset and clears all the VF's entries; then the VF's 99462306a36Sopenharmony_ci * XFRM stack sends individual deletes for each entry, which the 99562306a36Sopenharmony_ci * reset already removed. In the future it might be good to try to 99662306a36Sopenharmony_ci * optimize this so not so many unnecessary delete messages are sent. 99762306a36Sopenharmony_ci * 99862306a36Sopenharmony_ci * Returns 0 or error value 99962306a36Sopenharmony_ci **/ 100062306a36Sopenharmony_ciint ixgbe_ipsec_vf_del_sa(struct ixgbe_adapter *adapter, u32 *msgbuf, u32 vf) 100162306a36Sopenharmony_ci{ 100262306a36Sopenharmony_ci struct ixgbe_ipsec *ipsec = adapter->ipsec; 100362306a36Sopenharmony_ci struct xfrm_state *xs; 100462306a36Sopenharmony_ci u32 pfsa = msgbuf[1]; 100562306a36Sopenharmony_ci u16 sa_idx; 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci if (!adapter->vfinfo[vf].trusted) { 100862306a36Sopenharmony_ci e_err(drv, "vf %d attempted to delete an SA\n", vf); 100962306a36Sopenharmony_ci return -EPERM; 101062306a36Sopenharmony_ci } 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci if (pfsa < IXGBE_IPSEC_BASE_TX_INDEX) { 101362306a36Sopenharmony_ci struct rx_sa *rsa; 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci sa_idx = pfsa - IXGBE_IPSEC_BASE_RX_INDEX; 101662306a36Sopenharmony_ci if (sa_idx >= IXGBE_IPSEC_MAX_SA_COUNT) { 101762306a36Sopenharmony_ci e_err(drv, "vf %d SA index %d out of range\n", 101862306a36Sopenharmony_ci vf, sa_idx); 101962306a36Sopenharmony_ci return -EINVAL; 102062306a36Sopenharmony_ci } 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci rsa = &ipsec->rx_tbl[sa_idx]; 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci if (!rsa->used) 102562306a36Sopenharmony_ci return 0; 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci if (!(rsa->mode & IXGBE_RXTXMOD_VF) || 102862306a36Sopenharmony_ci rsa->vf != vf) { 102962306a36Sopenharmony_ci e_err(drv, "vf %d bad Rx SA index %d\n", vf, sa_idx); 103062306a36Sopenharmony_ci return -ENOENT; 103162306a36Sopenharmony_ci } 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci xs = ipsec->rx_tbl[sa_idx].xs; 103462306a36Sopenharmony_ci } else { 103562306a36Sopenharmony_ci struct tx_sa *tsa; 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci sa_idx = pfsa - IXGBE_IPSEC_BASE_TX_INDEX; 103862306a36Sopenharmony_ci if (sa_idx >= IXGBE_IPSEC_MAX_SA_COUNT) { 103962306a36Sopenharmony_ci e_err(drv, "vf %d SA index %d out of range\n", 104062306a36Sopenharmony_ci vf, sa_idx); 104162306a36Sopenharmony_ci return -EINVAL; 104262306a36Sopenharmony_ci } 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_ci tsa = &ipsec->tx_tbl[sa_idx]; 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci if (!tsa->used) 104762306a36Sopenharmony_ci return 0; 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci if (!(tsa->mode & IXGBE_RXTXMOD_VF) || 105062306a36Sopenharmony_ci tsa->vf != vf) { 105162306a36Sopenharmony_ci e_err(drv, "vf %d bad Tx SA index %d\n", vf, sa_idx); 105262306a36Sopenharmony_ci return -ENOENT; 105362306a36Sopenharmony_ci } 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci xs = ipsec->tx_tbl[sa_idx].xs; 105662306a36Sopenharmony_ci } 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ci ixgbe_ipsec_del_sa(xs); 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci /* remove the xs that was made-up in the add request */ 106162306a36Sopenharmony_ci kfree_sensitive(xs); 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci return 0; 106462306a36Sopenharmony_ci} 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci/** 106762306a36Sopenharmony_ci * ixgbe_ipsec_tx - setup Tx flags for ipsec offload 106862306a36Sopenharmony_ci * @tx_ring: outgoing context 106962306a36Sopenharmony_ci * @first: current data packet 107062306a36Sopenharmony_ci * @itd: ipsec Tx data for later use in building context descriptor 107162306a36Sopenharmony_ci **/ 107262306a36Sopenharmony_ciint ixgbe_ipsec_tx(struct ixgbe_ring *tx_ring, 107362306a36Sopenharmony_ci struct ixgbe_tx_buffer *first, 107462306a36Sopenharmony_ci struct ixgbe_ipsec_tx_data *itd) 107562306a36Sopenharmony_ci{ 107662306a36Sopenharmony_ci struct ixgbe_adapter *adapter = netdev_priv(tx_ring->netdev); 107762306a36Sopenharmony_ci struct ixgbe_ipsec *ipsec = adapter->ipsec; 107862306a36Sopenharmony_ci struct xfrm_state *xs; 107962306a36Sopenharmony_ci struct sec_path *sp; 108062306a36Sopenharmony_ci struct tx_sa *tsa; 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci sp = skb_sec_path(first->skb); 108362306a36Sopenharmony_ci if (unlikely(!sp->len)) { 108462306a36Sopenharmony_ci netdev_err(tx_ring->netdev, "%s: no xfrm state len = %d\n", 108562306a36Sopenharmony_ci __func__, sp->len); 108662306a36Sopenharmony_ci return 0; 108762306a36Sopenharmony_ci } 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci xs = xfrm_input_state(first->skb); 109062306a36Sopenharmony_ci if (unlikely(!xs)) { 109162306a36Sopenharmony_ci netdev_err(tx_ring->netdev, "%s: no xfrm_input_state() xs = %p\n", 109262306a36Sopenharmony_ci __func__, xs); 109362306a36Sopenharmony_ci return 0; 109462306a36Sopenharmony_ci } 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_ci itd->sa_idx = xs->xso.offload_handle - IXGBE_IPSEC_BASE_TX_INDEX; 109762306a36Sopenharmony_ci if (unlikely(itd->sa_idx >= IXGBE_IPSEC_MAX_SA_COUNT)) { 109862306a36Sopenharmony_ci netdev_err(tx_ring->netdev, "%s: bad sa_idx=%d handle=%lu\n", 109962306a36Sopenharmony_ci __func__, itd->sa_idx, xs->xso.offload_handle); 110062306a36Sopenharmony_ci return 0; 110162306a36Sopenharmony_ci } 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci tsa = &ipsec->tx_tbl[itd->sa_idx]; 110462306a36Sopenharmony_ci if (unlikely(!tsa->used)) { 110562306a36Sopenharmony_ci netdev_err(tx_ring->netdev, "%s: unused sa_idx=%d\n", 110662306a36Sopenharmony_ci __func__, itd->sa_idx); 110762306a36Sopenharmony_ci return 0; 110862306a36Sopenharmony_ci } 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci first->tx_flags |= IXGBE_TX_FLAGS_IPSEC | IXGBE_TX_FLAGS_CC; 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci if (xs->id.proto == IPPROTO_ESP) { 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_ci itd->flags |= IXGBE_ADVTXD_TUCMD_IPSEC_TYPE_ESP | 111562306a36Sopenharmony_ci IXGBE_ADVTXD_TUCMD_L4T_TCP; 111662306a36Sopenharmony_ci if (first->protocol == htons(ETH_P_IP)) 111762306a36Sopenharmony_ci itd->flags |= IXGBE_ADVTXD_TUCMD_IPV4; 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci /* The actual trailer length is authlen (16 bytes) plus 112062306a36Sopenharmony_ci * 2 bytes for the proto and the padlen values, plus 112162306a36Sopenharmony_ci * padlen bytes of padding. This ends up not the same 112262306a36Sopenharmony_ci * as the static value found in xs->props.trailer_len (21). 112362306a36Sopenharmony_ci * 112462306a36Sopenharmony_ci * ... but if we're doing GSO, don't bother as the stack 112562306a36Sopenharmony_ci * doesn't add a trailer for those. 112662306a36Sopenharmony_ci */ 112762306a36Sopenharmony_ci if (!skb_is_gso(first->skb)) { 112862306a36Sopenharmony_ci /* The "correct" way to get the auth length would be 112962306a36Sopenharmony_ci * to use 113062306a36Sopenharmony_ci * authlen = crypto_aead_authsize(xs->data); 113162306a36Sopenharmony_ci * but since we know we only have one size to worry 113262306a36Sopenharmony_ci * about * we can let the compiler use the constant 113362306a36Sopenharmony_ci * and save us a few CPU cycles. 113462306a36Sopenharmony_ci */ 113562306a36Sopenharmony_ci const int authlen = IXGBE_IPSEC_AUTH_BITS / 8; 113662306a36Sopenharmony_ci struct sk_buff *skb = first->skb; 113762306a36Sopenharmony_ci u8 padlen; 113862306a36Sopenharmony_ci int ret; 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci ret = skb_copy_bits(skb, skb->len - (authlen + 2), 114162306a36Sopenharmony_ci &padlen, 1); 114262306a36Sopenharmony_ci if (unlikely(ret)) 114362306a36Sopenharmony_ci return 0; 114462306a36Sopenharmony_ci itd->trailer_len = authlen + 2 + padlen; 114562306a36Sopenharmony_ci } 114662306a36Sopenharmony_ci } 114762306a36Sopenharmony_ci if (tsa->encrypt) 114862306a36Sopenharmony_ci itd->flags |= IXGBE_ADVTXD_TUCMD_IPSEC_ENCRYPT_EN; 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ci return 1; 115162306a36Sopenharmony_ci} 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_ci/** 115462306a36Sopenharmony_ci * ixgbe_ipsec_rx - decode ipsec bits from Rx descriptor 115562306a36Sopenharmony_ci * @rx_ring: receiving ring 115662306a36Sopenharmony_ci * @rx_desc: receive data descriptor 115762306a36Sopenharmony_ci * @skb: current data packet 115862306a36Sopenharmony_ci * 115962306a36Sopenharmony_ci * Determine if there was an ipsec encapsulation noticed, and if so set up 116062306a36Sopenharmony_ci * the resulting status for later in the receive stack. 116162306a36Sopenharmony_ci **/ 116262306a36Sopenharmony_civoid ixgbe_ipsec_rx(struct ixgbe_ring *rx_ring, 116362306a36Sopenharmony_ci union ixgbe_adv_rx_desc *rx_desc, 116462306a36Sopenharmony_ci struct sk_buff *skb) 116562306a36Sopenharmony_ci{ 116662306a36Sopenharmony_ci struct ixgbe_adapter *adapter = netdev_priv(rx_ring->netdev); 116762306a36Sopenharmony_ci __le16 pkt_info = rx_desc->wb.lower.lo_dword.hs_rss.pkt_info; 116862306a36Sopenharmony_ci __le16 ipsec_pkt_types = cpu_to_le16(IXGBE_RXDADV_PKTTYPE_IPSEC_AH | 116962306a36Sopenharmony_ci IXGBE_RXDADV_PKTTYPE_IPSEC_ESP); 117062306a36Sopenharmony_ci struct ixgbe_ipsec *ipsec = adapter->ipsec; 117162306a36Sopenharmony_ci struct xfrm_offload *xo = NULL; 117262306a36Sopenharmony_ci struct xfrm_state *xs = NULL; 117362306a36Sopenharmony_ci struct ipv6hdr *ip6 = NULL; 117462306a36Sopenharmony_ci struct iphdr *ip4 = NULL; 117562306a36Sopenharmony_ci struct sec_path *sp; 117662306a36Sopenharmony_ci void *daddr; 117762306a36Sopenharmony_ci __be32 spi; 117862306a36Sopenharmony_ci u8 *c_hdr; 117962306a36Sopenharmony_ci u8 proto; 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci /* Find the ip and crypto headers in the data. 118262306a36Sopenharmony_ci * We can assume no vlan header in the way, b/c the 118362306a36Sopenharmony_ci * hw won't recognize the IPsec packet and anyway the 118462306a36Sopenharmony_ci * currently vlan device doesn't support xfrm offload. 118562306a36Sopenharmony_ci */ 118662306a36Sopenharmony_ci if (pkt_info & cpu_to_le16(IXGBE_RXDADV_PKTTYPE_IPV4)) { 118762306a36Sopenharmony_ci ip4 = (struct iphdr *)(skb->data + ETH_HLEN); 118862306a36Sopenharmony_ci daddr = &ip4->daddr; 118962306a36Sopenharmony_ci c_hdr = (u8 *)ip4 + ip4->ihl * 4; 119062306a36Sopenharmony_ci } else if (pkt_info & cpu_to_le16(IXGBE_RXDADV_PKTTYPE_IPV6)) { 119162306a36Sopenharmony_ci ip6 = (struct ipv6hdr *)(skb->data + ETH_HLEN); 119262306a36Sopenharmony_ci daddr = &ip6->daddr; 119362306a36Sopenharmony_ci c_hdr = (u8 *)ip6 + sizeof(struct ipv6hdr); 119462306a36Sopenharmony_ci } else { 119562306a36Sopenharmony_ci return; 119662306a36Sopenharmony_ci } 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_ci switch (pkt_info & ipsec_pkt_types) { 119962306a36Sopenharmony_ci case cpu_to_le16(IXGBE_RXDADV_PKTTYPE_IPSEC_AH): 120062306a36Sopenharmony_ci spi = ((struct ip_auth_hdr *)c_hdr)->spi; 120162306a36Sopenharmony_ci proto = IPPROTO_AH; 120262306a36Sopenharmony_ci break; 120362306a36Sopenharmony_ci case cpu_to_le16(IXGBE_RXDADV_PKTTYPE_IPSEC_ESP): 120462306a36Sopenharmony_ci spi = ((struct ip_esp_hdr *)c_hdr)->spi; 120562306a36Sopenharmony_ci proto = IPPROTO_ESP; 120662306a36Sopenharmony_ci break; 120762306a36Sopenharmony_ci default: 120862306a36Sopenharmony_ci return; 120962306a36Sopenharmony_ci } 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci xs = ixgbe_ipsec_find_rx_state(ipsec, daddr, proto, spi, !!ip4); 121262306a36Sopenharmony_ci if (unlikely(!xs)) 121362306a36Sopenharmony_ci return; 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci sp = secpath_set(skb); 121662306a36Sopenharmony_ci if (unlikely(!sp)) 121762306a36Sopenharmony_ci return; 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci sp->xvec[sp->len++] = xs; 122062306a36Sopenharmony_ci sp->olen++; 122162306a36Sopenharmony_ci xo = xfrm_offload(skb); 122262306a36Sopenharmony_ci xo->flags = CRYPTO_DONE; 122362306a36Sopenharmony_ci xo->status = CRYPTO_SUCCESS; 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci adapter->rx_ipsec++; 122662306a36Sopenharmony_ci} 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci/** 122962306a36Sopenharmony_ci * ixgbe_init_ipsec_offload - initialize security registers for IPSec operation 123062306a36Sopenharmony_ci * @adapter: board private structure 123162306a36Sopenharmony_ci **/ 123262306a36Sopenharmony_civoid ixgbe_init_ipsec_offload(struct ixgbe_adapter *adapter) 123362306a36Sopenharmony_ci{ 123462306a36Sopenharmony_ci struct ixgbe_hw *hw = &adapter->hw; 123562306a36Sopenharmony_ci struct ixgbe_ipsec *ipsec; 123662306a36Sopenharmony_ci u32 t_dis, r_dis; 123762306a36Sopenharmony_ci size_t size; 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_ci if (hw->mac.type == ixgbe_mac_82598EB) 124062306a36Sopenharmony_ci return; 124162306a36Sopenharmony_ci 124262306a36Sopenharmony_ci /* If there is no support for either Tx or Rx offload 124362306a36Sopenharmony_ci * we should not be advertising support for IPsec. 124462306a36Sopenharmony_ci */ 124562306a36Sopenharmony_ci t_dis = IXGBE_READ_REG(hw, IXGBE_SECTXSTAT) & 124662306a36Sopenharmony_ci IXGBE_SECTXSTAT_SECTX_OFF_DIS; 124762306a36Sopenharmony_ci r_dis = IXGBE_READ_REG(hw, IXGBE_SECRXSTAT) & 124862306a36Sopenharmony_ci IXGBE_SECRXSTAT_SECRX_OFF_DIS; 124962306a36Sopenharmony_ci if (t_dis || r_dis) 125062306a36Sopenharmony_ci return; 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_ci ipsec = kzalloc(sizeof(*ipsec), GFP_KERNEL); 125362306a36Sopenharmony_ci if (!ipsec) 125462306a36Sopenharmony_ci goto err1; 125562306a36Sopenharmony_ci hash_init(ipsec->rx_sa_list); 125662306a36Sopenharmony_ci 125762306a36Sopenharmony_ci size = sizeof(struct rx_sa) * IXGBE_IPSEC_MAX_SA_COUNT; 125862306a36Sopenharmony_ci ipsec->rx_tbl = kzalloc(size, GFP_KERNEL); 125962306a36Sopenharmony_ci if (!ipsec->rx_tbl) 126062306a36Sopenharmony_ci goto err2; 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_ci size = sizeof(struct tx_sa) * IXGBE_IPSEC_MAX_SA_COUNT; 126362306a36Sopenharmony_ci ipsec->tx_tbl = kzalloc(size, GFP_KERNEL); 126462306a36Sopenharmony_ci if (!ipsec->tx_tbl) 126562306a36Sopenharmony_ci goto err2; 126662306a36Sopenharmony_ci 126762306a36Sopenharmony_ci size = sizeof(struct rx_ip_sa) * IXGBE_IPSEC_MAX_RX_IP_COUNT; 126862306a36Sopenharmony_ci ipsec->ip_tbl = kzalloc(size, GFP_KERNEL); 126962306a36Sopenharmony_ci if (!ipsec->ip_tbl) 127062306a36Sopenharmony_ci goto err2; 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_ci ipsec->num_rx_sa = 0; 127362306a36Sopenharmony_ci ipsec->num_tx_sa = 0; 127462306a36Sopenharmony_ci 127562306a36Sopenharmony_ci adapter->ipsec = ipsec; 127662306a36Sopenharmony_ci ixgbe_ipsec_stop_engine(adapter); 127762306a36Sopenharmony_ci ixgbe_ipsec_clear_hw_tables(adapter); 127862306a36Sopenharmony_ci 127962306a36Sopenharmony_ci adapter->netdev->xfrmdev_ops = &ixgbe_xfrmdev_ops; 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_ci return; 128262306a36Sopenharmony_ci 128362306a36Sopenharmony_cierr2: 128462306a36Sopenharmony_ci kfree(ipsec->ip_tbl); 128562306a36Sopenharmony_ci kfree(ipsec->rx_tbl); 128662306a36Sopenharmony_ci kfree(ipsec->tx_tbl); 128762306a36Sopenharmony_ci kfree(ipsec); 128862306a36Sopenharmony_cierr1: 128962306a36Sopenharmony_ci netdev_err(adapter->netdev, "Unable to allocate memory for SA tables"); 129062306a36Sopenharmony_ci} 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_ci/** 129362306a36Sopenharmony_ci * ixgbe_stop_ipsec_offload - tear down the ipsec offload 129462306a36Sopenharmony_ci * @adapter: board private structure 129562306a36Sopenharmony_ci **/ 129662306a36Sopenharmony_civoid ixgbe_stop_ipsec_offload(struct ixgbe_adapter *adapter) 129762306a36Sopenharmony_ci{ 129862306a36Sopenharmony_ci struct ixgbe_ipsec *ipsec = adapter->ipsec; 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_ci adapter->ipsec = NULL; 130162306a36Sopenharmony_ci if (ipsec) { 130262306a36Sopenharmony_ci kfree(ipsec->ip_tbl); 130362306a36Sopenharmony_ci kfree(ipsec->rx_tbl); 130462306a36Sopenharmony_ci kfree(ipsec->tx_tbl); 130562306a36Sopenharmony_ci kfree(ipsec); 130662306a36Sopenharmony_ci } 130762306a36Sopenharmony_ci} 1308