|
| 1 | +//! |
| 2 | +//! This modules deals with managing crypto vbdev. The crypto vbdev is |
| 3 | +//! a virtual bdev that needs a base bdev for it to be created. The crypto vbdev |
| 4 | +//! concept can be applied to encrypt the complete lvolstore or a particular lvol. |
| 5 | +//! For encrypting lvolstore(Mayastor diskpool), the crypto vbdev is created on top |
| 6 | +//! of base bdev of the disk pool e.g. aio bdev. For lvol level encryption, the |
| 7 | +//! crypto vbdev is to be created on top of the lvol vbdev. |
| 8 | +//! |
| 9 | +//! A general workflow example for creating a spdk crypto vbdev for encrypted lvolstore is as below: |
| 10 | +//! |
| 11 | +//! sudo $SPDK_SCRIPT_PATH/rpc.py accel_crypto_key_create -c AES_CBC -k 01234567891234560123456789deadc0 -n dskey_aes128_cbc_1 |
| 12 | +//! sudo $SPDK_SCRIPT_PATH/rpc.py bdev_aio_create /dev/loop0 MyBaseBdev 512 |
| 13 | +//! sudo $SPDK_SCRIPT_PATH/rpc.py bdev_crypto_create MyBaseBdev MyCryptoVbdev -n key_aes128_cbc |
| 14 | +//! |
| 15 | +//! The vbdev MyCryptoVbdev can now be used to create lvolstore on it. |
| 16 | +//! |
| 17 | +
|
| 18 | +use futures::channel::oneshot; |
| 19 | +use io_engine_api::v1 as v1rpc; |
| 20 | +use nix::errno::Errno; |
| 21 | +use spdk_rs::{ |
| 22 | + ffihelper::IntoCString, |
| 23 | + libspdk::{ |
| 24 | + accel_dpdk_cryptodev_enable, accel_dpdk_cryptodev_set_driver, create_crypto_disk, |
| 25 | + create_crypto_opts_by_name, delete_crypto_disk, spdk_accel_assign_opc, |
| 26 | + spdk_accel_crypto_key, spdk_accel_crypto_key_create, spdk_accel_crypto_key_create_param, |
| 27 | + spdk_accel_crypto_key_get, |
| 28 | + }, |
| 29 | +}; |
| 30 | +use std::{ |
| 31 | + fmt::{Debug, Display, Formatter}, |
| 32 | + os::raw::c_char, |
| 33 | +}; |
| 34 | + |
| 35 | +use crate::{ |
| 36 | + bdev_api::BdevError, |
| 37 | + core::CoreError, |
| 38 | + ffihelper::{cb_arg, pair, FfiResult}, |
| 39 | +}; |
| 40 | + |
| 41 | +/// Representation of a crypto vbdev to be created in SPDK. |
| 42 | +/// TODO: Currently we don't fit crypto vbdev into the uri based bdev management |
| 43 | +/// as the information required to completely setup the crypto vbdev isn't part of the uri, |
| 44 | +/// and also the crypto uri isn't provided by user rather we'll have to do some masking |
| 45 | +/// using crypto:// scheme. |
| 46 | +#[allow(dead_code)] |
| 47 | +#[derive(Default)] |
| 48 | +pub(crate) struct Crypto { |
| 49 | + // Name of the crypto vbdev. |
| 50 | + pub name: String, |
| 51 | + // Name of the base bdev. |
| 52 | + pub base_bdev_name: String, |
| 53 | + // Encryption key params. |
| 54 | + pub key: EncryptionKey, |
| 55 | +} |
| 56 | + |
| 57 | +#[derive(Copy, Clone, Debug, Default, PartialEq, Serialize, Deserialize)] |
| 58 | +/// Cipher mode for the encryption enablement. The default is kept as AES_XTS here |
| 59 | +/// because operations are default assigned to software module, which only supports |
| 60 | +/// AES_XTS. We don't expect to use default though. |
| 61 | +pub enum Cipher { |
| 62 | + AesCbc, |
| 63 | + #[default] |
| 64 | + AesXts, |
| 65 | +} |
| 66 | + |
| 67 | +#[derive(Clone, Debug, Default, PartialEq)] |
| 68 | +/// A struct that represents the parameters required to create a key. |
| 69 | +pub struct EncryptionKey { |
| 70 | + pub cipher: Cipher, |
| 71 | + pub key_name: String, |
| 72 | + pub key: String, |
| 73 | + pub key_len: u32, |
| 74 | + pub key2: Option<String>, |
| 75 | + pub key2_len: Option<u32>, |
| 76 | +} |
| 77 | + |
| 78 | +impl Debug for Crypto { |
| 79 | + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { |
| 80 | + write!( |
| 81 | + f, |
| 82 | + "crypto vbdev '{}', 'base_bdev: {}', keyname '{}', cipher: '{:?}'", |
| 83 | + self.name, self.base_bdev_name, self.key.key_name, self.key.cipher |
| 84 | + ) |
| 85 | + } |
| 86 | +} |
| 87 | + |
| 88 | +impl Display for Cipher { |
| 89 | + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { |
| 90 | + let c = match self { |
| 91 | + Self::AesCbc => "AES_CBC", |
| 92 | + Self::AesXts => "AES_XTS", |
| 93 | + }; |
| 94 | + write!(f, "{c}") |
| 95 | + } |
| 96 | +} |
| 97 | + |
| 98 | +impl From<v1rpc::common::Cipher> for Cipher { |
| 99 | + fn from(value: v1rpc::common::Cipher) -> Self { |
| 100 | + match value { |
| 101 | + v1rpc::common::Cipher::AesCbc => Self::AesCbc, |
| 102 | + v1rpc::common::Cipher::AesXts => Self::AesXts, |
| 103 | + } |
| 104 | + } |
| 105 | +} |
| 106 | + |
| 107 | +/// Driver types for dpdk_cryptodev module. We won't be using any other |
| 108 | +/// than aesni-mb. |
| 109 | +#[derive(Debug, Default)] |
| 110 | +enum AccelDpdkCryptodevDriverType { |
| 111 | + #[default] |
| 112 | + AccelDpdkCryptodevAesniMb, |
| 113 | + _AccelDpdkCryptodevQat, |
| 114 | + _AccelDpdkCryptodevMlx5Pci, |
| 115 | + _AccelDpdkCryptodevUadk, |
| 116 | + _AccelDpdkCryptodevLast, |
| 117 | +} |
| 118 | + |
| 119 | +impl Display for AccelDpdkCryptodevDriverType { |
| 120 | + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { |
| 121 | + let drv = match self { |
| 122 | + Self::AccelDpdkCryptodevAesniMb => "crypto_aesni_mb", |
| 123 | + Self::_AccelDpdkCryptodevQat => "crypto_qat", |
| 124 | + Self::_AccelDpdkCryptodevMlx5Pci => "mlx5_pci", |
| 125 | + Self::_AccelDpdkCryptodevUadk => "crypto_uadk", |
| 126 | + Self::_AccelDpdkCryptodevLast => "crypto_driver_invalid", |
| 127 | + }; |
| 128 | + write!(f, "{drv}") |
| 129 | + } |
| 130 | +} |
| 131 | + |
| 132 | +// This function enables the dpdk_cryptodev module with aesni_mb driver in spdk, |
| 133 | +// and assigns the encrypt and decrypt operation to it, which by default are assigned to |
| 134 | +// software module initially. |
| 135 | +#[allow(dead_code)] |
| 136 | +pub fn enable_dpdk_cryptodev_accel_module() -> Result<(), CoreError> { |
| 137 | + info!("Enabling accel dpdk_cryptodev module"); |
| 138 | + unsafe { |
| 139 | + accel_dpdk_cryptodev_enable(); |
| 140 | + let setdrv_ret = accel_dpdk_cryptodev_set_driver( |
| 141 | + AccelDpdkCryptodevDriverType::AccelDpdkCryptodevAesniMb |
| 142 | + .to_string() |
| 143 | + .into_cstring() |
| 144 | + .as_ptr(), |
| 145 | + ); |
| 146 | + if setdrv_ret != 0 { |
| 147 | + return Err(CoreError::InitCryptoModule { |
| 148 | + reason: format!("Unsupported driver: {setdrv_ret:?}"), |
| 149 | + }); |
| 150 | + } |
| 151 | + let err_enc = spdk_accel_assign_opc(8, "dpdk_cryptodev".into_cstring().as_ptr()); |
| 152 | + let err_dec = spdk_accel_assign_opc(9, "dpdk_cryptodev".into_cstring().as_ptr()); |
| 153 | + trace!("assign opc err_enc: {err_enc}, err_dec: {err_dec}"); |
| 154 | + if err_enc != 0 || err_dec != 0 { |
| 155 | + return Err(CoreError::InitCryptoModule { |
| 156 | + reason: format!( |
| 157 | + "opcode assignment failed. err_enc {err_enc:?}, err_dec {err_dec:?}" |
| 158 | + ), |
| 159 | + }); |
| 160 | + } |
| 161 | + Ok(()) |
| 162 | + } |
| 163 | +} |
| 164 | + |
| 165 | +/// A simple wrapper around `spdk_accel_crypto_key_create`. The keys' strings here are |
| 166 | +/// in hexlified format. |
| 167 | +fn create_crypto_key(key_params: &EncryptionKey) -> Result<*mut spdk_accel_crypto_key, BdevError> { |
| 168 | + let cipher = key_params.cipher.to_string().into_cstring(); |
| 169 | + let key = key_params.key.as_str().into_cstring(); |
| 170 | + let key_name = key_params.key_name.as_str().into_cstring(); |
| 171 | + let hex_key2_str = key_params.key2.as_deref().map(|k2| k2.into_cstring()); |
| 172 | + let key2 = if let Some(ref k2) = hex_key2_str { |
| 173 | + k2.as_ptr() |
| 174 | + } else { |
| 175 | + std::ptr::null_mut() |
| 176 | + }; |
| 177 | + |
| 178 | + debug!( |
| 179 | + "Creating {:?} key of size {}", |
| 180 | + key_params.cipher.to_string(), |
| 181 | + key_params.key_len, |
| 182 | + ); |
| 183 | + |
| 184 | + let create_key_params = spdk_accel_crypto_key_create_param { |
| 185 | + cipher: cipher.as_ptr() as *mut c_char, |
| 186 | + hex_key: key.as_ptr() as *mut c_char, |
| 187 | + hex_key2: key2 as *mut c_char, |
| 188 | + key_name: key_name.as_ptr() as *mut c_char, |
| 189 | + tweak_mode: std::ptr::null_mut(), |
| 190 | + }; |
| 191 | + |
| 192 | + unsafe { |
| 193 | + let ret = spdk_accel_crypto_key_create(&create_key_params); |
| 194 | + if ret != 0 { |
| 195 | + Err(BdevError::CreateBdevFailed { |
| 196 | + source: Errno::from_raw(ret.abs()), |
| 197 | + name: format!("Key create failed: {:?}", create_key_params.key_name), |
| 198 | + }) |
| 199 | + } else { |
| 200 | + let key_ptr = spdk_accel_crypto_key_get(create_key_params.key_name); |
| 201 | + trace!("Key '{:?}' created", key_params.key_name); |
| 202 | + Ok(key_ptr) |
| 203 | + } |
| 204 | + } |
| 205 | +} |
| 206 | + |
| 207 | +/// callback when operation has been performed on crypto vbdev |
| 208 | +extern "C" fn crypto_vbdev_op_cb(arg: *mut std::os::raw::c_void, errno: i32) { |
| 209 | + let sender = unsafe { Box::from_raw(arg as *mut oneshot::Sender<i32>) }; |
| 210 | + trace!("crypto_vbdev_op_cb: errno {errno}"); |
| 211 | + sender.send(errno).unwrap(); |
| 212 | +} |
| 213 | + |
| 214 | +/// Destroy a crypto vbdev. |
| 215 | +pub async fn destroy_crypto_vbdev(vbdev_name: String) -> Result<(), BdevError> { |
| 216 | + let (s, r) = pair::<i32>(); |
| 217 | + unsafe { |
| 218 | + delete_crypto_disk( |
| 219 | + vbdev_name.as_ptr() as *mut std::os::raw::c_char, |
| 220 | + Some(crypto_vbdev_op_cb), |
| 221 | + cb_arg(s), |
| 222 | + ); |
| 223 | + } |
| 224 | + |
| 225 | + r.await |
| 226 | + .expect("callback gone while deleting crypto disk") |
| 227 | + .to_result(|e| BdevError::DestroyBdevFailed { |
| 228 | + source: Errno::from_raw(e), |
| 229 | + name: vbdev_name.to_string(), |
| 230 | + })?; |
| 231 | + |
| 232 | + info!("crypto vbdev {vbdev_name} destroyed successfully"); |
| 233 | + Ok(()) |
| 234 | +} |
| 235 | +/// The primary function to create a crypto vbdev in spdk. |
| 236 | +pub fn create_crypto_vbdev_on_base_bdev( |
| 237 | + crypto_vbdev_name: &str, |
| 238 | + base_bdev_name: &str, |
| 239 | + key_params: &EncryptionKey, |
| 240 | +) -> Result<(), BdevError> { |
| 241 | + // Create the key. |
| 242 | + let key = create_crypto_key(key_params)?; |
| 243 | + |
| 244 | + // Setup the crypto opts using the key now. |
| 245 | + let crypto_opts_ptr = unsafe { |
| 246 | + create_crypto_opts_by_name( |
| 247 | + crypto_vbdev_name.into_cstring().as_ptr() as *mut c_char, |
| 248 | + base_bdev_name.into_cstring().as_ptr() as *mut c_char, |
| 249 | + key, |
| 250 | + true, |
| 251 | + ) |
| 252 | + }; |
| 253 | + |
| 254 | + // Now create the crypto vbdev. |
| 255 | + let ret = unsafe { create_crypto_disk(crypto_opts_ptr) }; |
| 256 | + |
| 257 | + if ret != 0 { |
| 258 | + Err(BdevError::CreateBdevFailed { |
| 259 | + source: Errno::from_raw(ret), |
| 260 | + name: crypto_vbdev_name.to_string(), |
| 261 | + }) |
| 262 | + } else { |
| 263 | + info!("crypto vbdev {crypto_vbdev_name} created on base bdev {base_bdev_name}"); |
| 264 | + Ok(()) |
| 265 | + } |
| 266 | +} |
0 commit comments