@@ -1549,6 +1549,12 @@ F: doc/guides/tools/pdump.rst
F: app/dumpcap/
F: doc/guides/tools/dumpcap.rst
+PDCP - EXPERIMENTAL
+M: Anoob Joseph <anoobj@marvell.com>
+M: Volodymyr Fialko <vfialko@marvell.com>
+T: git://dpdk.org/next/dpdk-next-crypto
+F: lib/pdcp/
+
Packet Framework
----------------
@@ -128,7 +128,8 @@ The public API headers are grouped by topics:
[eCPRI](@ref rte_ecpri.h),
[L2TPv2](@ref rte_l2tpv2.h),
[PPP](@ref rte_ppp.h),
- [PDCP hdr](@ref rte_pdcp_hdr.h)
+ [PDCP hdr](@ref rte_pdcp_hdr.h),
+ [PDCP](@ref rte_pdcp.h)
- **QoS**:
[metering](@ref rte_meter.h),
@@ -62,6 +62,7 @@ INPUT = @TOPDIR@/doc/api/doxy-api-index.md \
@TOPDIR@/lib/net \
@TOPDIR@/lib/pcapng \
@TOPDIR@/lib/pci \
+ @TOPDIR@/lib/pdcp \
@TOPDIR@/lib/pdump \
@TOPDIR@/lib/pipeline \
@TOPDIR@/lib/port \
@@ -64,6 +64,7 @@ libraries = [
'flow_classify', # flow_classify lib depends on pkt framework table lib
'graph',
'node',
+ 'pdcp', # pdcp lib depends on crypto and security
]
optional_libs = [
new file mode 100644
@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(C) 2023 Marvell.
+
+if is_windows
+ build = false
+ reason = 'not supported on Windows'
+ subdir_done()
+endif
+
+sources = files(
+ 'pdcp_crypto.c',
+ 'pdcp_process.c',
+ 'rte_pdcp.c',
+ )
+headers = files('rte_pdcp.h')
+
+deps += ['mbuf', 'net', 'cryptodev', 'security']
new file mode 100644
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2023 Marvell.
+ */
+
+#include <rte_pdcp.h>
+
+#include "pdcp_crypto.h"
+
+int
+pdcp_crypto_sess_create(struct rte_pdcp_entity *entity, const struct rte_pdcp_entity_conf *conf)
+{
+ RTE_SET_USED(entity);
+ RTE_SET_USED(conf);
+ return 0;
+}
+
+void
+pdcp_crypto_sess_destroy(struct rte_pdcp_entity *entity)
+{
+ RTE_SET_USED(entity);
+}
new file mode 100644
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2023 Marvell.
+ */
+
+#ifndef PDCP_CRYPTO_H
+#define PDCP_CRYPTO_H
+
+#include <rte_pdcp.h>
+
+int pdcp_crypto_sess_create(struct rte_pdcp_entity *entity,
+ const struct rte_pdcp_entity_conf *conf);
+
+void pdcp_crypto_sess_destroy(struct rte_pdcp_entity *entity);
+
+#endif /* PDCP_CRYPTO_H */
new file mode 100644
@@ -0,0 +1,113 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2023 Marvell.
+ */
+
+#ifndef PDCP_ENTITY_H
+#define PDCP_ENTITY_H
+
+#include <rte_common.h>
+#include <rte_crypto_sym.h>
+#include <rte_mempool.h>
+#include <rte_pdcp.h>
+#include <rte_security.h>
+
+struct entity_priv;
+
+/* IV generation function based on the entity configuration */
+typedef void (*iv_gen_t)(struct rte_crypto_op *cop, const struct entity_priv *en_priv,
+ uint32_t count);
+
+struct entity_state {
+ uint32_t rx_next;
+ uint32_t tx_next;
+ uint32_t rx_deliv;
+ uint32_t rx_reord;
+};
+
+/*
+ * Layout of PDCP entity: [rte_pdcp_entity] [entity_priv] [entity_dl/ul]
+ */
+
+struct entity_priv {
+ /** Crypto sym session. */
+ struct rte_cryptodev_sym_session *crypto_sess;
+ /** Entity specific IV generation function. */
+ iv_gen_t iv_gen;
+ /** Entity state variables. */
+ struct entity_state state;
+ /** Flags. */
+ struct {
+ /** PDCP PDU has 4 byte MAC-I. */
+ uint64_t is_authenticated : 1;
+ /** Cipher offset & length in bits. */
+ uint64_t is_cipher_in_bits : 1;
+ /** Auth offset & length in bits. */
+ uint64_t is_auth_in_bits : 1;
+ /** Is UL/transmitting PDCP entity. */
+ uint64_t is_ul_entity : 1;
+ /** Is NULL auth. */
+ uint64_t is_null_auth : 1;
+ } flags;
+ /** Crypto op pool. */
+ struct rte_mempool *cop_pool;
+ /** PDCP header size. */
+ uint8_t hdr_sz;
+ /** PDCP AAD size. For AES-CMAC, additional message is prepended for the operation. */
+ uint8_t aad_sz;
+ /** Device ID of the device to be used for offload. */
+ uint8_t dev_id;
+};
+
+struct entity_priv_dl_part {
+ /* NOTE: when in-order-delivery is supported, post PDCP packets would need to cached. */
+ uint8_t dummy;
+};
+
+struct entity_priv_ul_part {
+ /*
+ * NOTE: when re-establish is supported, plain PDCP packets & COUNT values need to be
+ * cached.
+ */
+ uint8_t dummy;
+};
+
+static inline struct entity_priv *
+entity_priv_get(const struct rte_pdcp_entity *entity) {
+ return RTE_PTR_ADD(entity, sizeof(struct rte_pdcp_entity));
+}
+
+static inline struct entity_priv_dl_part *
+entity_dl_part_get(const struct rte_pdcp_entity *entity) {
+ return RTE_PTR_ADD(entity, sizeof(struct rte_pdcp_entity) + sizeof(struct entity_priv));
+}
+
+static inline struct entity_priv_ul_part *
+entity_ul_part_get(const struct rte_pdcp_entity *entity) {
+ return RTE_PTR_ADD(entity, sizeof(struct rte_pdcp_entity) + sizeof(struct entity_priv));
+}
+
+static inline int
+pdcp_hdr_size_get(enum rte_security_pdcp_sn_size sn_size)
+{
+ return RTE_ALIGN_MUL_CEIL(sn_size, 8) / 8;
+}
+
+static inline uint32_t
+pdcp_sn_mask_get(enum rte_security_pdcp_sn_size sn_size)
+{
+ return (1 << sn_size) - 1;
+}
+
+static inline uint32_t
+pdcp_hfn_mask_get(enum rte_security_pdcp_sn_size sn_size)
+{
+ return ~pdcp_sn_mask_get(sn_size);
+}
+
+static inline uint32_t
+pdcp_count_from_hfn_sn_get(uint32_t hfn, uint32_t sn, enum rte_security_pdcp_sn_size sn_size)
+{
+ return (((hfn << sn_size) & pdcp_hfn_mask_get(sn_size)) | (sn & pdcp_sn_mask_get(sn_size)));
+}
+
+#endif /* PDCP_ENTITY_H */
new file mode 100644
@@ -0,0 +1,138 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2023 Marvell.
+ */
+
+#include <rte_crypto.h>
+#include <rte_crypto_sym.h>
+#include <rte_cryptodev.h>
+#include <rte_memcpy.h>
+#include <rte_pdcp.h>
+#include <rte_pdcp_hdr.h>
+
+#include "pdcp_crypto.h"
+#include "pdcp_entity.h"
+#include "pdcp_process.h"
+
+static int
+pdcp_crypto_xfrm_get(const struct rte_pdcp_entity_conf *conf, struct rte_crypto_sym_xform **c_xfrm,
+ struct rte_crypto_sym_xform **a_xfrm)
+{
+ *c_xfrm = NULL;
+ *a_xfrm = NULL;
+
+ if (conf->crypto_xfrm == NULL)
+ return -EINVAL;
+
+ if (conf->crypto_xfrm->type == RTE_CRYPTO_SYM_XFORM_CIPHER) {
+ *c_xfrm = conf->crypto_xfrm;
+ *a_xfrm = conf->crypto_xfrm->next;
+ } else if (conf->crypto_xfrm->type == RTE_CRYPTO_SYM_XFORM_AUTH) {
+ *a_xfrm = conf->crypto_xfrm;
+ *c_xfrm = conf->crypto_xfrm->next;
+ } else {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int
+pdcp_entity_priv_populate(struct entity_priv *en_priv, const struct rte_pdcp_entity_conf *conf)
+{
+ struct rte_crypto_sym_xform *c_xfrm, *a_xfrm;
+ int ret;
+
+ ret = pdcp_crypto_xfrm_get(conf, &c_xfrm, &a_xfrm);
+ if (ret)
+ return ret;
+
+ /**
+ * flags.is_authenticated
+ *
+ * MAC-I would be added in case of control plane packets and when authentication
+ * transform is not NULL.
+ */
+
+ if ((conf->pdcp_xfrm.domain == RTE_SECURITY_PDCP_MODE_CONTROL) && (a_xfrm == NULL))
+ return -EINVAL;
+
+ if (a_xfrm != NULL)
+ en_priv->flags.is_authenticated = 1;
+
+ /**
+ * flags.is_cipher_in_bits
+ *
+ * For ZUC & SNOW3G cipher algos, offset & length need to be provided in bits.
+ */
+
+ if ((c_xfrm->cipher.algo == RTE_CRYPTO_CIPHER_SNOW3G_UEA2) ||
+ (c_xfrm->cipher.algo == RTE_CRYPTO_CIPHER_ZUC_EEA3))
+ en_priv->flags.is_cipher_in_bits = 1;
+
+ /**
+ * flags.is_auth_in_bits
+ *
+ * For ZUC & SNOW3G authentication algos, offset & length need to be provided in bits.
+ */
+
+ if (a_xfrm != NULL) {
+ if ((a_xfrm->auth.algo == RTE_CRYPTO_AUTH_SNOW3G_UIA2) ||
+ (a_xfrm->auth.algo == RTE_CRYPTO_AUTH_ZUC_EIA3))
+ en_priv->flags.is_auth_in_bits = 1;
+ }
+
+ /**
+ * flags.is_ul_entity
+ *
+ * Indicate whether the entity is UL/transmitting PDCP entity.
+ */
+ if (conf->pdcp_xfrm.pkt_dir == RTE_SECURITY_PDCP_UPLINK)
+ en_priv->flags.is_ul_entity = 1;
+
+ /**
+ * flags.is_null_auth
+ *
+ * For NULL auth, 4B zeros need to be added by lib PDCP. Indicate that
+ * algo is NULL auth to perform the same.
+ */
+ if (a_xfrm != NULL && a_xfrm->auth.algo == RTE_CRYPTO_AUTH_NULL)
+ en_priv->flags.is_null_auth = 1;
+
+ /**
+ * hdr_sz
+ *
+ * PDCP header size of the entity
+ */
+ en_priv->hdr_sz = pdcp_hdr_size_get(conf->pdcp_xfrm.sn_size);
+
+ /**
+ * aad_sz
+ *
+ * For AES-CMAC, additional message is prepended for processing. Need to be trimmed after
+ * crypto processing is done.
+ */
+ if (a_xfrm != NULL && a_xfrm->auth.algo == RTE_CRYPTO_AUTH_AES_CMAC)
+ en_priv->aad_sz = 8;
+ else
+ en_priv->aad_sz = 0;
+
+ return 0;
+}
+
+int
+pdcp_process_func_set(struct rte_pdcp_entity *entity, const struct rte_pdcp_entity_conf *conf)
+{
+ struct entity_priv *en_priv;
+ int ret;
+
+ if (entity == NULL || conf == NULL)
+ return -EINVAL;
+
+ en_priv = entity_priv_get(entity);
+
+ ret = pdcp_entity_priv_populate(en_priv, conf);
+ if (ret)
+ return ret;
+
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2023 Marvell.
+ */
+
+#ifndef PDCP_PROCESS_H
+#define PDCP_PROCESS_H
+
+#include <rte_pdcp.h>
+
+int
+pdcp_process_func_set(struct rte_pdcp_entity *entity, const struct rte_pdcp_entity_conf *conf);
+
+#endif /* PDCP_PROCESS_H */
new file mode 100644
@@ -0,0 +1,141 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2023 Marvell.
+ */
+
+#include <rte_errno.h>
+#include <rte_pdcp.h>
+#include <rte_malloc.h>
+
+#include "pdcp_crypto.h"
+#include "pdcp_entity.h"
+#include "pdcp_process.h"
+
+static int
+pdcp_entity_size_get(const struct rte_pdcp_entity_conf *conf)
+{
+ int size;
+
+ size = sizeof(struct rte_pdcp_entity) + sizeof(struct entity_priv);
+
+ if (conf->pdcp_xfrm.pkt_dir == RTE_SECURITY_PDCP_DOWNLINK)
+ size += sizeof(struct entity_priv_dl_part);
+ else if (conf->pdcp_xfrm.pkt_dir == RTE_SECURITY_PDCP_UPLINK)
+ size += sizeof(struct entity_priv_ul_part);
+ else
+ return -EINVAL;
+
+ return RTE_ALIGN_CEIL(size, RTE_CACHE_LINE_SIZE);
+}
+
+struct rte_pdcp_entity *
+rte_pdcp_entity_establish(const struct rte_pdcp_entity_conf *conf)
+{
+ struct rte_pdcp_entity *entity = NULL;
+ struct entity_priv *en_priv;
+ int ret, entity_size;
+ uint32_t count;
+
+ if (conf == NULL || conf->cop_pool == NULL) {
+ rte_errno = EINVAL;
+ return NULL;
+ }
+
+ if (conf->pdcp_xfrm.en_ordering || conf->pdcp_xfrm.remove_duplicates || conf->is_slrb ||
+ conf->en_sec_offload) {
+ rte_errno = ENOTSUP;
+ return NULL;
+ }
+
+ /*
+ * 6.3.2 PDCP SN
+ * Length: 12 or 18 bits as indicated in table 6.3.2-1. The length of the PDCP SN is
+ * configured by upper layers (pdcp-SN-SizeUL, pdcp-SN-SizeDL, or sl-PDCP-SN-Size in
+ * TS 38.331 [3])
+ */
+ if ((conf->pdcp_xfrm.sn_size != RTE_SECURITY_PDCP_SN_SIZE_12) &&
+ (conf->pdcp_xfrm.sn_size != RTE_SECURITY_PDCP_SN_SIZE_18)) {
+ rte_errno = ENOTSUP;
+ return NULL;
+ }
+
+ if (conf->pdcp_xfrm.hfn_threshold) {
+ rte_errno = EINVAL;
+ return NULL;
+ }
+
+ entity_size = pdcp_entity_size_get(conf);
+ if (entity_size < 0) {
+ rte_errno = EINVAL;
+ return NULL;
+ }
+
+ entity = rte_zmalloc_socket("pdcp_entity", entity_size, RTE_CACHE_LINE_SIZE, SOCKET_ID_ANY);
+ if (entity == NULL) {
+ rte_errno = ENOMEM;
+ return NULL;
+ }
+
+ en_priv = entity_priv_get(entity);
+
+ count = pdcp_count_from_hfn_sn_get(conf->pdcp_xfrm.hfn, conf->sn, conf->pdcp_xfrm.sn_size);
+
+ en_priv->state.rx_deliv = count;
+ en_priv->state.tx_next = count;
+ en_priv->cop_pool = conf->cop_pool;
+
+ /* Setup crypto session */
+ ret = pdcp_crypto_sess_create(entity, conf);
+ if (ret)
+ goto entity_free;
+
+ ret = pdcp_process_func_set(entity, conf);
+ if (ret)
+ goto crypto_sess_destroy;
+
+ return entity;
+
+crypto_sess_destroy:
+ pdcp_crypto_sess_destroy(entity);
+entity_free:
+ rte_free(entity);
+ rte_errno = -ret;
+ return NULL;
+}
+
+int
+rte_pdcp_entity_release(struct rte_pdcp_entity *pdcp_entity, struct rte_mbuf *out_mb[])
+{
+ if (pdcp_entity == NULL)
+ return -EINVAL;
+
+ /* Teardown crypto sessions */
+ pdcp_crypto_sess_destroy(pdcp_entity);
+
+ rte_free(pdcp_entity);
+
+ RTE_SET_USED(out_mb);
+ return 0;
+}
+
+int
+rte_pdcp_entity_suspend(struct rte_pdcp_entity *pdcp_entity,
+ struct rte_mbuf *out_mb[])
+{
+ struct entity_priv *en_priv;
+
+ if (pdcp_entity == NULL)
+ return -EINVAL;
+
+ en_priv = entity_priv_get(pdcp_entity);
+
+ if (en_priv->flags.is_ul_entity) {
+ en_priv->state.tx_next = 0;
+ } else {
+ en_priv->state.rx_next = 0;
+ en_priv->state.rx_deliv = 0;
+ }
+
+ RTE_SET_USED(out_mb);
+
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,167 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2023 Marvell.
+ */
+
+#ifndef RTE_PDCP_H
+#define RTE_PDCP_H
+
+/**
+ * @file rte_pdcp.h
+ *
+ * RTE PDCP support.
+ *
+ * A framework for PDCP protocol processing.
+ */
+
+#include <rte_compat.h>
+#include <rte_common.h>
+#include <rte_mempool.h>
+#include <rte_security.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * PDCP entity.
+ *
+ * 4.2.2 PDCP entities
+ *
+ * The PDCP entities are located in the PDCP sublayer. Several PDCP entities may
+ * be defined for a UE. Each PDCP entity is carrying the data of one radio
+ * bearer. A PDCP entity is associated either to the control plane or the user
+ * plane depending on which radio bearer it is carrying data for.
+ */
+struct rte_pdcp_entity {
+ /**
+ * PDCP entities may hold packets for purposes of in-order delivery (in
+ * case of receiving PDCP entity) and re-transmission (in case of
+ * transmitting PDCP entity).
+ *
+ * The field 'max_pkt_cache' would be used to indicate the maximum
+ * number of packets that may be cached in an entity at any point of
+ * time. When application provides buffers to receive packets from
+ * PDCP entity, the size of the buffer should be such that it can
+ * hold additionally 'max_pkt_cache' number of packets.
+ */
+ uint32_t max_pkt_cache;
+} __rte_cache_aligned;
+
+/**
+ * PDCP entity configuration to be used for establishing an entity.
+ */
+/* Structure rte_pdcp_entity_conf 8< */
+struct rte_pdcp_entity_conf {
+ /** PDCP transform for the entity. */
+ struct rte_security_pdcp_xform pdcp_xfrm;
+ /** Crypto transform applicable for the entity. */
+ struct rte_crypto_sym_xform *crypto_xfrm;
+ /** Mempool for crypto symmetric session. */
+ struct rte_mempool *sess_mpool;
+ /** Crypto op pool.*/
+ struct rte_mempool *cop_pool;
+ /**
+ * SN value to be used. 32 bit count value to be used for the first
+ * packet would be derived based on HFN (`rte_security_pdcp_xform.hfn`)
+ * and SN.
+ */
+ uint32_t sn;
+ /**
+ * Indicate whether the PDCP entity belongs to Side Link Radio Bearer.
+ */
+ bool is_slrb;
+ /** Enable security offload on the device specified. */
+ bool en_sec_offload;
+ /** Device on which security/crypto session need to be created. */
+ uint8_t dev_id;
+ /**
+ * Reverse direction during IV generation. Can be used to simulate UE
+ * crypto processing.
+ */
+ bool reverse_iv_direction;
+};
+/* >8 End of structure rte_pdcp_entity_conf. */
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * 5.1.1 PDCP entity establishment
+ *
+ * Establish PDCP entity based on provided input configuration.
+ *
+ * @param conf
+ * Parameters to be used for initializing PDCP entity object.
+ * @return
+ * - Valid handle if success
+ * - NULL in case of failure. rte_errno will be set to error code
+ */
+__rte_experimental
+struct rte_pdcp_entity *
+rte_pdcp_entity_establish(const struct rte_pdcp_entity_conf *conf);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * 5.1.3 PDCP entity release
+ *
+ * Release PDCP entity.
+ *
+ * For UL/transmitting PDCP entity, all stored PDCP SDUs would be dropped.
+ * For DL/receiving PDCP entity, the stored PDCP SDUs would be returned in
+ * *out_mb* buffer. The buffer should be large enough to hold all cached
+ * packets in the entity.
+ *
+ * Entity release would result in freeing all memory associated with the PDCP
+ * entity as well as any crypto/security sessions created.
+ *
+ * @param pdcp_entity
+ * Pointer to the PDCP entity to be released.
+ * @param[out] out_mb
+ * The address of an array that can hold up to *rte_pdcp_entity.max_pkt_cache*
+ * pointers to *rte_mbuf* structures.
+ * @return
+ * - 0: Success and no cached packets to return
+ * - >0: Success and the number of packets returned in out_mb
+ * - <0: Error code in case of failures
+ */
+__rte_experimental
+int
+rte_pdcp_entity_release(struct rte_pdcp_entity *pdcp_entity,
+ struct rte_mbuf *out_mb[]);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * 5.1.4 PDCP entity suspend
+ *
+ * Suspend PDCP entity.
+ *
+ * For DL/receiving PDCP entity, the stored PDCP SDUs would be returned in
+ * *out_mb* buffer. The buffer should be large enough to hold all cached
+ * packets in the entity.
+ *
+ * For UL/transmitting PDCP entity, *out_mb* buffer would be unused.
+ *
+ * @param pdcp_entity
+ * Pointer to the PDCP entity to be suspended.
+ * @param[out] out_mb
+ * The address of an array that can hold up to *rte_pdcp_entity.max_pkt_cache*
+ * pointers to *rte_mbuf* structures.
+ * @return
+ * - 0: Success and no cached packets to return
+ * - >0: Success and the number of packets returned in out_mb
+ * - <0: Error code in case of failures
+ */
+__rte_experimental
+int
+rte_pdcp_entity_suspend(struct rte_pdcp_entity *pdcp_entity,
+ struct rte_mbuf *out_mb[]);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* RTE_PDCP_H */
new file mode 100644
@@ -0,0 +1,10 @@
+EXPERIMENTAL {
+ global:
+
+ # added in 23.07
+ rte_pdcp_entity_establish;
+ rte_pdcp_entity_release;
+ rte_pdcp_entity_suspend;
+
+ local: *;
+};