.. SPDX-License-Identifier: BSD-3-Clause Copyright(c) 2018-2020 Intel Corporation. IPsec Packet Processing Library =============================== DPDK provides a library for IPsec data-path processing. The library utilizes the existing DPDK crypto-dev and security API to provide the application with a transparent and high performant IPsec packet processing API. The library is concentrated on data-path protocols processing (ESP and AH), IKE protocol(s) implementation is out of scope for this library. SA level API ------------ This API operates on the IPsec Security Association (SA) level. It provides functionality that allows user for given SA to process inbound and outbound IPsec packets. To be more specific: * for inbound ESP/AH packets perform decryption, authentication, integrity checking, remove ESP/AH related headers * for outbound packets perform payload encryption, attach ICV, update/add IP headers, add ESP/AH headers/trailers, * setup related mbuf fields (ol_flags, tx_offloads, etc.). * initialize/un-initialize given SA based on user provided parameters. The SA level API is based on top of crypto-dev/security API and relies on them to perform actual cipher and integrity checking. Due to the nature of the crypto-dev API (enqueue/dequeue model) the library introduces an asynchronous API for IPsec packets destined to be processed by the crypto-device. The expected API call sequence for data-path processing would be: .. code-block:: c /* enqueue for processing by crypto-device */ rte_ipsec_pkt_crypto_prepare(...); rte_cryptodev_enqueue_burst(...); /* dequeue from crypto-device and do final processing (if any) */ rte_cryptodev_dequeue_burst(...); rte_ipsec_pkt_crypto_group(...); /* optional */ rte_ipsec_pkt_process(...); For packets destined for inline processing no extra overhead is required and the synchronous API call: rte_ipsec_pkt_process() is sufficient for that case. .. note:: For more details about the IPsec API, please refer to the *DPDK API Reference*. The current implementation supports all four currently defined rte_security types: RTE_SECURITY_ACTION_TYPE_NONE ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In that mode the library functions perform * for inbound packets: - check SQN - prepare *rte_crypto_op* structure for each input packet - verify that integrity check and decryption performed by crypto device completed successfully - check padding data - remove outer IP header (tunnel mode) / update IP header (transport mode) - remove ESP header and trailer, padding, IV and ICV data - update SA replay window * for outbound packets: - generate SQN and IV - add outer IP header (tunnel mode) / update IP header (transport mode) - add ESP header and trailer, padding and IV data - prepare *rte_crypto_op* structure for each input packet - verify that crypto device operations (encryption, ICV generation) were completed successfully RTE_SECURITY_ACTION_TYPE_CPU_CRYPTO ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In that mode the library functions perform same operations as in ``RTE_SECURITY_ACTION_TYPE_NONE``. The only difference is that crypto operations are performed with CPU crypto synchronous API. RTE_SECURITY_ACTION_TYPE_INLINE_CRYPTO ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In that mode the library functions perform * for inbound packets: - verify that integrity check and decryption performed by *rte_security* device completed successfully - check SQN - check padding data - remove outer IP header (tunnel mode) / update IP header (transport mode) - remove ESP header and trailer, padding, IV and ICV data - update SA replay window * for outbound packets: - generate SQN and IV - add outer IP header (tunnel mode) / update IP header (transport mode) - add ESP header and trailer, padding and IV data - update *ol_flags* inside *struct rte_mbuf* to indicate that inline-crypto processing has to be performed by HW on this packet - invoke *rte_security* device specific *set_pkt_metadata()* to associate security device specific data with the packet RTE_SECURITY_ACTION_TYPE_INLINE_PROTOCOL ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In that mode the library functions perform * for inbound packets: - verify that integrity check and decryption performed by *rte_security* device completed successfully * for outbound packets: - update *ol_flags* inside *struct rte_mbuf* to indicate that inline-crypto processing has to be performed by HW on this packet - invoke *rte_security* device specific *set_pkt_metadata()* to associate security device specific data with the packet RTE_SECURITY_ACTION_TYPE_LOOKASIDE_PROTOCOL ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In that mode the library functions perform * for inbound packets: - prepare *rte_crypto_op* structure for each input packet - verify that integrity check and decryption performed by crypto device completed successfully * for outbound packets: - prepare *rte_crypto_op* structure for each input packet - verify that crypto device operations (encryption, ICV generation) were completed successfully To accommodate future custom implementations function pointers model is used for both *crypto_prepare* and *process* implementations. SA database API ---------------- SA database(SAD) is a table with pairs. Value is an opaque user provided pointer to the user defined SA data structure. According to RFC4301 each SA can be uniquely identified by a key which is either: - security parameter index(SPI) - or SPI and destination IP(DIP) - or SPI, DIP and source IP(SIP) In case of multiple matches, longest matching key will be returned. Create/destroy ~~~~~~~~~~~~~~ librte_ipsec SAD implementation provides ability to create/destroy SAD tables. To create SAD table user has to specify how many entries of each key type is required and IP protocol type (IPv4/IPv6). As an example: .. code-block:: c struct rte_ipsec_sad *sad; struct rte_ipsec_sad_conf conf; conf.socket_id = -1; conf.max_sa[RTE_IPSEC_SAD_SPI_ONLY] = some_nb_rules_spi_only; conf.max_sa[RTE_IPSEC_SAD_SPI_DIP] = some_nb_rules_spi_dip; conf.max_sa[RTE_IPSEC_SAD_SPI_DIP_SIP] = some_nb_rules_spi_dip_sip; conf.flags = RTE_IPSEC_SAD_FLAG_RW_CONCURRENCY; sad = rte_ipsec_sad_create("test", &conf); .. note:: for more information please refer to ipsec library API reference Add/delete rules ~~~~~~~~~~~~~~~~ Library also provides methods to add or delete key/value pairs from the SAD. To add user has to specify key, key type and a value which is an opaque pointer to SA. The key type reflects a set of tuple fields that will be used for lookup of the SA. As mentioned above there are 3 types of a key and the representation of a key type is: .. code-block:: c RTE_IPSEC_SAD_SPI_ONLY, RTE_IPSEC_SAD_SPI_DIP, RTE_IPSEC_SAD_SPI_DIP_SIP, As an example, to add new entry into the SAD for IPv4 addresses: .. code-block:: c struct rte_ipsec_sa *sa; union rte_ipsec_sad_key key; key.v4.spi = rte_cpu_to_be_32(spi_val); if (key_type >= RTE_IPSEC_SAD_SPI_DIP) /* DIP is optional*/ key.v4.dip = rte_cpu_to_be_32(dip_val); if (key_type == RTE_IPSEC_SAD_SPI_DIP_SIP) /* SIP is optional*/ key.v4.sip = rte_cpu_to_be_32(sip_val); rte_ipsec_sad_add(sad, &key, key_type, sa); .. note:: By performance reason it is better to keep spi/dip/sip in net byte order to eliminate byteswap on lookup To delete user has to specify key and key type. Delete code would look like: .. code-block:: c union rte_ipsec_sad_key key; key.v4.spi = rte_cpu_to_be_32(necessary_spi); if (key_type >= RTE_IPSEC_SAD_SPI_DIP) /* DIP is optional*/ key.v4.dip = rte_cpu_to_be_32(necessary_dip); if (key_type == RTE_IPSEC_SAD_SPI_DIP_SIP) /* SIP is optional*/ key.v4.sip = rte_cpu_to_be_32(necessary_sip); rte_ipsec_sad_del(sad, &key, key_type); Lookup ~~~~~~ Library provides lookup by the given {SPI,DIP,SIP} tuple of inbound ipsec packet as a key. The search key is represented by: .. code-block:: c union rte_ipsec_sad_key { struct rte_ipsec_sadv4_key v4; struct rte_ipsec_sadv6_key v6; }; where v4 is a tuple for IPv4: .. code-block:: c struct rte_ipsec_sadv4_key { uint32_t spi; uint32_t dip; uint32_t sip; }; and v6 is a tuple for IPv6: .. code-block:: c struct rte_ipsec_sadv6_key { uint32_t spi; uint8_t dip[16]; uint8_t sip[16]; }; As an example, lookup related code could look like that: .. code-block:: c int i; union rte_ipsec_sad_key keys[BURST_SZ]; const union rte_ipsec_sad_key *keys_p[BURST_SZ]; void *vals[BURST_SZ]; for (i = 0; i < BURST_SZ_MAX; i++) { keys[i].v4.spi = esp_hdr[i]->spi; keys[i].v4.dip = ipv4_hdr[i]->dst_addr; keys[i].v4.sip = ipv4_hdr[i]->src_addr; keys_p[i] = &keys[i]; } rte_ipsec_sad_lookup(sad, keys_p, vals, BURST_SZ); for (i = 0; i < BURST_SZ_MAX; i++) { if (vals[i] == NULL) printf("SA not found for key index %d\n", i); else printf("SA pointer is %p\n", vals[i]); } Supported features ------------------ * ESP protocol tunnel mode both IPv4/IPv6. * ESP protocol transport mode both IPv4/IPv6. * ESN and replay window. * algorithms: 3DES-CBC, AES-CBC, AES-CTR, AES-GCM, HMAC-SHA1, NULL. Limitations ----------- The following features are not properly supported in the current version: * Hard/soft limit for SA lifetime (time interval/byte count).