/* * Intel IXP4xx NPE-C crypto driver * * Copyright (C) 2008 Christian Hohnstaedt * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License * as published by the Free Software Foundation. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define MAX_KEYLEN 32 /* hash: cfgword + 2 * digestlen; crypt: keylen + cfgword */ #define NPE_CTX_LEN 80 #define AES_BLOCK128 16 #define NPE_OP_HASH_VERIFY 0x01 #define NPE_OP_CCM_ENABLE 0x04 #define NPE_OP_CRYPT_ENABLE 0x08 #define NPE_OP_HASH_ENABLE 0x10 #define NPE_OP_NOT_IN_PLACE 0x20 #define NPE_OP_HMAC_DISABLE 0x40 #define NPE_OP_CRYPT_ENCRYPT 0x80 #define NPE_OP_CCM_GEN_MIC 0xcc #define NPE_OP_HASH_GEN_ICV 0x50 #define NPE_OP_ENC_GEN_KEY 0xc9 #define MOD_ECB 0x0000 #define MOD_CTR 0x1000 #define MOD_CBC_ENC 0x2000 #define MOD_CBC_DEC 0x3000 #define MOD_CCM_ENC 0x4000 #define MOD_CCM_DEC 0x5000 #define KEYLEN_128 4 #define KEYLEN_192 6 #define KEYLEN_256 8 #define CIPH_DECR 0x0000 #define CIPH_ENCR 0x0400 #define MOD_DES 0x0000 #define MOD_TDEA2 0x0100 #define MOD_3DES 0x0200 #define MOD_AES 0x0800 #define MOD_AES128 (0x0800 | KEYLEN_128) #define MOD_AES192 (0x0900 | KEYLEN_192) #define MOD_AES256 (0x0a00 | KEYLEN_256) #define MAX_IVLEN 16 #define NPE_ID 2 /* NPE C */ #define NPE_QLEN 16 /* Space for registering when the first * NPE_QLEN crypt_ctl are busy */ #define NPE_QLEN_TOTAL 64 #define SEND_QID 29 #define RECV_QID 30 #define CTL_FLAG_UNUSED 0x0000 #define CTL_FLAG_USED 0x1000 #define CTL_FLAG_PERFORM_ABLK 0x0001 #define CTL_FLAG_GEN_ICV 0x0002 #define CTL_FLAG_GEN_REVAES 0x0004 #define CTL_FLAG_PERFORM_AEAD 0x0008 #define CTL_FLAG_MASK 0x000f #define HMAC_IPAD_VALUE 0x36 #define HMAC_OPAD_VALUE 0x5C #define HMAC_PAD_BLOCKLEN SHA1_BLOCK_SIZE #define MD5_DIGEST_SIZE 16 struct buffer_desc { u32 phys_next; #ifdef __ARMEB__ u16 buf_len; u16 pkt_len; #else u16 pkt_len; u16 buf_len; #endif u32 phys_addr; u32 __reserved[4]; struct buffer_desc *next; enum dma_data_direction dir; }; struct crypt_ctl { #ifdef __ARMEB__ u8 mode; /* NPE_OP_* operation mode */ u8 init_len; u16 reserved; #else u16 reserved; u8 init_len; u8 mode; /* NPE_OP_* operation mode */ #endif u8 iv[MAX_IVLEN]; /* IV for CBC mode or CTR IV for CTR mode */ u32 icv_rev_aes; /* icv or rev aes */ u32 src_buf; u32 dst_buf; #ifdef __ARMEB__ u16 auth_offs; /* Authentication start offset */ u16 auth_len; /* Authentication data length */ u16 crypt_offs; /* Cryption start offset */ u16 crypt_len; /* Cryption data length */ #else u16 auth_len; /* Authentication data length */ u16 auth_offs; /* Authentication start offset */ u16 crypt_len; /* Cryption data length */ u16 crypt_offs; /* Cryption start offset */ #endif u32 aadAddr; /* Additional Auth Data Addr for CCM mode */ u32 crypto_ctx; /* NPE Crypto Param structure address */ /* Used by Host: 4*4 bytes*/ unsigned ctl_flags; union { struct ablkcipher_request *ablk_req; struct aead_request *aead_req; struct crypto_tfm *tfm; } data; struct buffer_desc *regist_buf; u8 *regist_ptr; }; struct ablk_ctx { struct buffer_desc *src; struct buffer_desc *dst; }; struct aead_ctx { struct buffer_desc *src; struct buffer_desc *dst; struct scatterlist ivlist; /* used when the hmac is not on one sg entry */ u8 *hmac_virt; int encrypt; }; struct ix_hash_algo { u32 cfgword; unsigned char *icv; }; struct ix_sa_dir { unsigned char *npe_ctx; dma_addr_t npe_ctx_phys; int npe_ctx_idx; u8 npe_mode; }; struct ixp_ctx { struct ix_sa_dir encrypt; struct ix_sa_dir decrypt; int authkey_len; u8 authkey[MAX_KEYLEN]; int enckey_len; u8 enckey[MAX_KEYLEN]; u8 salt[MAX_IVLEN]; u8 nonce[CTR_RFC3686_NONCE_SIZE]; unsigned salted; atomic_t configuring; struct completion completion; }; struct ixp_alg { struct crypto_alg crypto; const struct ix_hash_algo *hash; u32 cfg_enc; u32 cfg_dec; int registered; }; struct ixp_aead_alg { struct aead_alg crypto; const struct ix_hash_algo *hash; u32 cfg_enc; u32 cfg_dec; int registered; }; static const struct ix_hash_algo hash_alg_md5 = { .cfgword = 0xAA010004, .icv = "\x01\x23\x45\x67\x89\xAB\xCD\xEF" "\xFE\xDC\xBA\x98\x76\x54\x32\x10", }; static const struct ix_hash_algo hash_alg_sha1 = { .cfgword = 0x00000005, .icv = "\x67\x45\x23\x01\xEF\xCD\xAB\x89\x98\xBA" "\xDC\xFE\x10\x32\x54\x76\xC3\xD2\xE1\xF0", }; static struct npe *npe_c; static struct dma_pool *buffer_pool = NULL; static struct dma_pool *ctx_pool = NULL; static struct crypt_ctl *crypt_virt = NULL; static dma_addr_t crypt_phys; static int support_aes = 1; #define DRIVER_NAME "ixp4xx_crypto" static struct platform_device *pdev; static inline dma_addr_t crypt_virt2phys(struct crypt_ctl *virt) { return crypt_phys + (virt - crypt_virt) * sizeof(struct crypt_ctl); } static inline struct crypt_ctl *crypt_phys2virt(dma_addr_t phys) { return crypt_virt + (phys - crypt_phys) / sizeof(struct crypt_ctl); } static inline u32 cipher_cfg_enc(struct crypto_tfm *tfm) { return container_of(tfm->__crt_alg, struct ixp_alg,crypto)->cfg_enc; } static inline u32 cipher_cfg_dec(struct crypto_tfm *tfm) { return container_of(tfm->__crt_alg, struct ixp_alg,crypto)->cfg_dec; } static inline const struct ix_hash_algo *ix_hash(struct crypto_tfm *tfm) { return container_of(tfm->__crt_alg, struct ixp_alg, crypto)->hash; } static int setup_crypt_desc(void) { struct device *dev = &pdev->dev; BUILD_BUG_ON(sizeof(struct crypt_ctl) != 64); crypt_virt = dma_alloc_coherent(dev, NPE_QLEN * sizeof(struct crypt_ctl), &crypt_phys, GFP_ATOMIC); if (!crypt_virt) return -ENOMEM; memset(crypt_virt, 0, NPE_QLEN * sizeof(struct crypt_ctl)); return 0; } static spinlock_t desc_lock; static struct crypt_ctl *get_crypt_desc(void) { int i; static int idx = 0; unsigned long flags; spin_lock_irqsave(&desc_lock, flags); if (unlikely(!crypt_virt)) setup_crypt_desc(); if (unlikely(!crypt_virt)) { spin_unlock_irqrestore(&desc_lock, flags); return NULL; } i = idx; if (crypt_virt[i].ctl_flags == CTL_FLAG_UNUSED) { if (++idx >= NPE_QLEN) idx = 0; crypt_virt[i].ctl_flags = CTL_FLAG_USED; spin_unlock_irqrestore(&desc_lock, flags); return crypt_virt +i; } else { spin_unlock_irqrestore(&desc_lock, flags); return NULL; } } static spinlock_t emerg_lock; static struct crypt_ctl *get_crypt_desc_emerg(void) { int i; static int idx = NPE_QLEN; struct crypt_ctl *desc; unsigned long flags; desc = get_crypt_desc(); if (desc) return desc; if (unlikely(!crypt_virt)) return NULL; spin_lock_irqsave(&emerg_lock, flags); i = idx; if (crypt_virt[i].ctl_flags == CTL_FLAG_UNUSED) { if (++idx >= NPE_QLEN_TOTAL) idx = NPE_QLEN; crypt_virt[i].ctl_flags = CTL_FLAG_USED; spin_unlock_irqrestore(&emerg_lock, flags); return crypt_virt +i; } else { spin_unlock_irqrestore(&emerg_lock, flags); return NULL; } } static void free_buf_chain(struct device *dev, struct buffer_desc *buf,u32 phys) { while (buf) { struct buffer_desc *buf1; u32 phys1; buf1 = buf->next; phys1 = buf->phys_next; dma_unmap_single(dev, buf->phys_next, buf->buf_len, buf->dir); dma_pool_free(buffer_pool, buf, phys); buf = buf1; phys = phys1; } } static struct tasklet_struct crypto_done_tasklet; static void finish_scattered_hmac(struct crypt_ctl *crypt) { struct aead_request *req = crypt->data.aead_req; struct aead_ctx *req_ctx = aead_request_ctx(req); struct crypto_aead *tfm = crypto_aead_reqtfm(req); int authsize = crypto_aead_authsize(tfm); int decryptlen = req->assoclen + req->cryptlen - authsize; if (req_ctx->encrypt) { scatterwalk_map_and_copy(req_ctx->hmac_virt, req->dst, decryptlen, authsize, 1); } dma_pool_free(buffer_pool, req_ctx->hmac_virt, crypt->icv_rev_aes); } static void one_packet(dma_addr_t phys) { struct device *dev = &pdev->dev; struct crypt_ctl *crypt; struct ixp_ctx *ctx; int failed; failed = phys & 0x1 ? -EBADMSG : 0; phys &= ~0x3; crypt = crypt
.. _DPDKpktgen: https://github.com/Pktgen/Pktgen-DPDK/
.. _rfc2544: https://www.ietf.org/rfc/rfc2544.txt


*************************************
Yardstick Test Case Description TC006
*************************************
+-----------------------------------------------------------------------------+
|Network Performance                                                          |
+==============+==============================================================+
|test case id  | OPNFV_YARDSTICK_TC006_Virtual Traffic Classifier Data Plane  |
|              | Throughput Benchmarking Test.                                |
|              |                                                              |
+--------------+--------------------------------------------------------------+
|metric        | Throughput                                                   |
|              |                                                              |
+--------------+--------------------------------------------------------------+
|test purpose  | To measure the throughput supported by the virtual Traffic   |
|              | Classifier according to the RFC2544 methodology for a        |
|              | user-defined set of vTC deployment configurations.           |
|              |                                                              |
+--------------+--------------------------------------------------------------+
|configuration | file: file: opnfv_yardstick_tc006.yaml                       |
|              |                                                              |
|              | packet_size: size of the packets to be used during the       |
|              |      throughput calculation.                                 |
|              |      Allowe values: [64, 128, 256, 512, 1024, 1280, 1518]    |
|              |                                                              |
|              | vnic_type: type of VNIC to be used.                          |
|              |      Allowed values are:                                     |
|              |           - normal: for default OvS port configuration       |
|              |           - direct: for SR-IOV port configuration            |
|              |      Default value: None                                     |
|              |                                                              |
|              | vtc_flavor: OpenStack flavor to be used for the vTC          |
|              |      Default available values are: m1.small, m1.medium,      |
|              |      and m1.large, but the user can create his/her own       |
|              |      flavor and give it as input                             |
|              |      Default value: None                                     |
|              |                                                              |
|              | vlan_sender: vlan tag of the network on which the vTC will   |
|              |      receive traffic (VLAN Network 1).                       |
|              |      Allowed values: range (1, 4096)                         |
|              |                                                              |
|              | vlan_receiver: vlan tag of the network on which the vTC      |
|              |      will send traffic back to the packet generator          |
|              |      (VLAN Network 2).                                       |
|              |      Allowed values: range (1, 4096)                         |
|              |                                                              |
|              | default_net_name: neutron name of the defaul network that    |
|              |      is used for access to the internet from the vTC         |
|              |      (vNIC 1).                                               |
|              |                                                              |
|              | default_subnet_name: subnet name for vNIC1                   |
|              |      (information available through Neutron).                |
|              |                                                              |
|              | vlan_net_1_name: Neutron Name for VLAN Network 1             |
|              |      (information available through Neutron).                |
|              |                                                              |
|              | vlan_subnet_1_name: Subnet Neutron name for VLAN Network 1   |
|              |      (information available through Neutron).                |
|              |                                                              |
|              | vlan_net_2_name: Neutron Name for VLAN Network 2             |
|              |      (information available through Neutron).                |
|              |                                                              |
|              | vlan_subnet_2_name: Subnet Neutron name for VLAN Network 2   |
|              |      (information available through Neutron).                |
|              |                                                              |
+--------------+--------------------------------------------------------------+
|test tool     | DPDK pktgen                                                  |
|              |                                                              |
|              | DPDK Pktgen is not part of a Linux distribution,             |
|              | hence it needs to be installed by the user.                  |
|              |                                                              |
+--------------+--------------------------------------------------------------+
|references    | DPDK Pktgen: DPDKpktgen_                                     |
|              | ETSI-NFV-TST001                                              |
|              | RFC 2544: rfc2544_                                           |
|              |                                                              |
+--------------+--------------------------------------------------------------+
|applicability | Test can be configured with different flavors, vNIC type     |
|              | and packet sizes. Default values exist as specified above.   |
|              | The vNIC type and flavor MUST be specified by the user.      |
|              |                                                              |
+--------------+--------------------------------------------------------------+
|pre-test      | The vTC has been successfully instantiated and configured.   |
|              | The user has correctly assigned the values to the deployment |
|              |  configuration parameters.                                   |
|              |                                                              |
|              | - Multicast traffic MUST be enabled on the network.          |
|              |      The Data network switches need to be configured in      |
|              |      order to manage multicast traffic.                      |
|              | - In the case of SR-IOV vNICs use, SR-IOV compatible NICs    |
|              |      must be used on the compute node.                       |
|              | - Yarsdtick needs to be installed on a host connected to the |
|              |      data network and the host must have 2 DPDK-compatible   |
|              |      NICs. Proper configuration of DPDK and DPDK pktgen is   |
|              |      required before to run the test case.                   |
|              |      (For further instructions please refer to the ApexLake  |
|              |      documentation).                                         |
|              |                                                              |
+-----------------------------------------------------------------------------+
|test sequence | Description and expected results                             |
|              |                                                              |
+-----------------------------------------------------------------------------+
|step  1       | The vTC is deployed, according to the user-defined           |
|              | configuration                                                |
|              |                                                              |
+--------------+--------------------------------------------------------------+
|step  2       | The vTC is correctly deployed and configured as necessary    |
|              | The initialization script has been correctly executed and    |
|              | vTC is ready to receive and process the traffic.             |
|              |                                                              |
+--------------+--------------------------------------------------------------+
|step  3       | Test case is executed with the selected parameters:          |
|              | - vTC flavor                                                 |
|              | - vNIC type                                                  |
|              | - packet size                                                |
|              | The traffic is sent to the vTC using the maximum available   |
|              | traffic rate for 60 seconds.                                 |
|              |                                                              |
+--------------+--------------------------------------------------------------+
|step 4        | The vTC instance forwards all the packets back to the packet |
|              | generator for 60 seconds, as specified by RFC 2544.          |
|              |                                                              |
|              | Steps 3 and 4 are executed different times, with different   |
|              | rates in order to find the maximum supported traffic rate    |
|              | according to the current definition of throughput in RFC     |
|              | 2544.                                                        |
|              |                                                              |
+--------------+--------------------------------------------------------------+
|test verdict  |  The result of the test is a number between 0 and 100 which  |
|              |  represents the throughput in terms of percentage of the     |
|              |  available pktgen NIC bandwidth.                             |
|              |                                                              |
+--------------+--------------------------------------------------------------+
}, .cfg_enc = CIPH_ENCR | MOD_AES | MOD_CTR, .cfg_dec = CIPH_ENCR | MOD_AES | MOD_CTR, }, { .crypto = { .cra_name = "rfc3686(ctr(aes))", .cra_blocksize = AES_BLOCK_SIZE, .cra_u = { .ablkcipher = { .min_keysize = AES_MIN_KEY_SIZE, .max_keysize = AES_MAX_KEY_SIZE, .ivsize = AES_BLOCK_SIZE, .geniv = "eseqiv", .setkey = ablk_rfc3686_setkey, .encrypt = ablk_rfc3686_crypt, .decrypt = ablk_rfc3686_crypt } } }, .cfg_enc = CIPH_ENCR | MOD_AES | MOD_CTR, .cfg_dec = CIPH_ENCR | MOD_AES | MOD_CTR, } }; static struct ixp_aead_alg ixp4xx_aeads[] = { { .crypto = { .base = { .cra_name = "authenc(hmac(md5),cbc(des))", .cra_blocksize = DES_BLOCK_SIZE, }, .ivsize = DES_BLOCK_SIZE, .maxauthsize = MD5_DIGEST_SIZE, }, .hash = &hash_alg_md5, .cfg_enc = CIPH_ENCR | MOD_DES | MOD_CBC_ENC | KEYLEN_192, .cfg_dec = CIPH_DECR | MOD_DES | MOD_CBC_DEC | KEYLEN_192, }, { .crypto = { .base = { .cra_name = "authenc(hmac(md5),cbc(des3_ede))", .cra_blocksize = DES3_EDE_BLOCK_SIZE, }, .ivsize = DES3_EDE_BLOCK_SIZE, .maxauthsize = MD5_DIGEST_SIZE, }, .hash = &hash_alg_md5, .cfg_enc = CIPH_ENCR | MOD_3DES | MOD_CBC_ENC | KEYLEN_192, .cfg_dec = CIPH_DECR | MOD_3DES | MOD_CBC_DEC | KEYLEN_192, }, { .crypto = { .base = { .cra_name = "authenc(hmac(sha1),cbc(des))", .cra_blocksize = DES_BLOCK_SIZE, }, .ivsize = DES_BLOCK_SIZE, .maxauthsize = SHA1_DIGEST_SIZE, }, .hash = &hash_alg_sha1, .cfg_enc = CIPH_ENCR | MOD_DES | MOD_CBC_ENC | KEYLEN_192, .cfg_dec = CIPH_DECR | MOD_DES | MOD_CBC_DEC | KEYLEN_192, }, { .crypto = { .base = { .cra_name = "authenc(hmac(sha1),cbc(des3_ede))", .cra_blocksize = DES3_EDE_BLOCK_SIZE, }, .ivsize = DES3_EDE_BLOCK_SIZE, .maxauthsize = SHA1_DIGEST_SIZE, }, .hash = &hash_alg_sha1, .cfg_enc = CIPH_ENCR | MOD_3DES | MOD_CBC_ENC | KEYLEN_192, .cfg_dec = CIPH_DECR | MOD_3DES | MOD_CBC_DEC | KEYLEN_192, }, { .crypto = { .base = { .cra_name = "authenc(hmac(md5),cbc(aes))", .cra_blocksize = AES_BLOCK_SIZE, }, .ivsize = AES_BLOCK_SIZE, .maxauthsize = MD5_DIGEST_SIZE, }, .hash = &hash_alg_md5, .cfg_enc = CIPH_ENCR | MOD_AES | MOD_CBC_ENC, .cfg_dec = CIPH_DECR | MOD_AES | MOD_CBC_DEC, }, { .crypto = { .base = { .cra_name = "authenc(hmac(sha1),cbc(aes))", .cra_blocksize = AES_BLOCK_SIZE, }, .ivsize = AES_BLOCK_SIZE, .maxauthsize = SHA1_DIGEST_SIZE, }, .hash = &hash_alg_sha1, .cfg_enc = CIPH_ENCR | MOD_AES | MOD_CBC_ENC, .cfg_dec = CIPH_DECR | MOD_AES | MOD_CBC_DEC, } }; #define IXP_POSTFIX "-ixp4xx" static const struct platform_device_info ixp_dev_info __initdata = { .name = DRIVER_NAME, .id = 0, .dma_mask = DMA_BIT_MASK(32), }; static int __init ixp_module_init(void) { int num = ARRAY_SIZE(ixp4xx_algos); int i, err; pdev = platform_device_register_full(&ixp_dev_info); if (IS_ERR(pdev)) return PTR_ERR(pdev); spin_lock_init(&desc_lock); spin_lock_init(&emerg_lock); err = init_ixp_crypto(&pdev->dev); if (err) { platform_device_unregister(pdev); return err; } for (i=0; i< num; i++) { struct crypto_alg *cra = &ixp4xx_algos[i].crypto; if (snprintf(cra->cra_driver_name, CRYPTO_MAX_ALG_NAME, "%s"IXP_POSTFIX, cra->cra_name) >= CRYPTO_MAX_ALG_NAME) { continue; } if (!support_aes && (ixp4xx_algos[i].cfg_enc & MOD_AES)) { continue; } /* block ciphers */ cra->cra_type = &crypto_ablkcipher_type; cra->cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_KERN_DRIVER_ONLY | CRYPTO_ALG_ASYNC; if (!cra->cra_ablkcipher.setkey) cra->cra_ablkcipher.setkey = ablk_setkey; if (!cra->cra_ablkcipher.encrypt) cra->cra_ablkcipher.encrypt = ablk_encrypt; if (!cra->cra_ablkcipher.decrypt) cra->cra_ablkcipher.decrypt = ablk_decrypt; cra->cra_init = init_tfm_ablk; cra->cra_ctxsize = sizeof(struct ixp_ctx); cra->cra_module = THIS_MODULE; cra->cra_alignmask = 3; cra->cra_priority = 300; cra->cra_exit = exit_tfm; if (crypto_register_alg(cra)) printk(KERN_ERR "Failed to register '%s'\n", cra->cra_name); else ixp4xx_algos[i].registered = 1; } for (i = 0; i < ARRAY_SIZE(ixp4xx_aeads); i++) { struct aead_alg *cra = &ixp4xx_aeads[i].crypto; if (snprintf(cra->base.cra_driver_name, CRYPTO_MAX_ALG_NAME, "%s"IXP_POSTFIX, cra->base.cra_name) >= CRYPTO_MAX_ALG_NAME) continue; if (!support_aes && (ixp4xx_algos[i].cfg_enc & MOD_AES)) continue; /* authenc */ cra->base.cra_flags = CRYPTO_ALG_KERN_DRIVER_ONLY | CRYPTO_ALG_ASYNC; cra->setkey = aead_setkey; cra->setauthsize = aead_setauthsize; cra->encrypt = aead_encrypt; cra->decrypt = aead_decrypt; cra->init = init_tfm_aead; cra->exit = exit_tfm_aead; cra->base.cra_ctxsize = sizeof(struct ixp_ctx); cra->base.cra_module = THIS_MODULE; cra->base.cra_alignmask = 3; cra->base.cra_priority = 300; if (crypto_register_aead(cra)) printk(KERN_ERR "Failed to register '%s'\n", cra->base.cra_driver_name); else ixp4xx_aeads[i].registered = 1; } return 0; } static void __exit ixp_module_exit(void) { int num = ARRAY_SIZE(ixp4xx_algos); int i; for (i = 0; i < ARRAY_SIZE(ixp4xx_aeads); i++) { if (ixp4xx_aeads[i].registered) crypto_unregister_aead(&ixp4xx_aeads[i].crypto); } for (i=0; i< num; i++) { if (ixp4xx_algos[i].registered) crypto_unregister_alg(&ixp4xx_algos[i].crypto); } release_ixp_crypto(&pdev->dev); platform_device_unregister(pdev); } module_init(ixp_module_init); module_exit(ixp_module_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Christian Hohnstaedt "); MODULE_DESCRIPTION("IXP4xx hardware crypto");