Skip to content

Commit bd2e431

Browse files
mayastor-borsdsharma-dc
mayastor-bors
andcommitted
Merge #1824
1824: feat(encryption): add crypto vbdev r=dsharma-dc a=dsharma-dc This adds a crypto vbdev support in Mayastor. A crypto vbdev is always created with a base bdev underneath e.g an aio bdev. A crypto vbdev needs an associated key and crypto opts, that are attached to the vbdev for its lifetime. Co-authored-by: Diwakar Sharma <diwakar.sharma@datacore.com>
2 parents a2758a1 + dfb5699 commit bd2e431

File tree

20 files changed

+526
-13
lines changed

20 files changed

+526
-13
lines changed

io-engine-tests/src/pool.rs

+2
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,8 @@ impl PoolBuilderLocal {
272272
cluster_size: None,
273273
md_args: None,
274274
backend: Default::default(),
275+
enc_key: None,
276+
crypto_vbdev_name: None,
275277
})
276278
.await?;
277279
Ok(lvs)

io-engine/examples/lvs-eval/main.rs

+2
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,8 @@ async fn create_lvs(args: &CliArgs) -> Lvs {
136136
md_resv_ratio: args.md_resv_ratio,
137137
}),
138138
backend: Default::default(),
139+
enc_key: None,
140+
crypto_vbdev_name: None,
139141
};
140142

141143
Lvs::create_or_import(lvs_args.clone()).await.unwrap()

io-engine/src/bdev/crypto.rs

+266
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
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+
}

io-engine/src/bdev/lvs.rs

+8
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ use crate::{
3434
pool_backend::{PoolArgs, PoolBackend},
3535
};
3636

37+
use super::crypto::EncryptionKey;
38+
3739
/// An lvol specified via URI.
3840
pub(super) struct Lvol {
3941
/// Name of the lvol.
@@ -50,6 +52,8 @@ struct Lvs {
5052
disk: String,
5153
/// The lvs creation mode.
5254
mode: LvsMode,
55+
// Encryption key - if the lvs is encrypted.
56+
key: Option<EncryptionKey>,
5357
}
5458

5559
impl Debug for Lvol {
@@ -144,6 +148,7 @@ impl TryFrom<String> for Lvs {
144148
name: uri.path()[1..].into(),
145149
disk,
146150
mode,
151+
key: None,
147152
})
148153
}
149154
}
@@ -209,6 +214,9 @@ impl Lvs {
209214
cluster_size: None,
210215
md_args: None,
211216
backend: PoolBackend::Lvs,
217+
enc_key: self.key.clone(),
218+
// XXX: Is this path ever exercised apart from test, or casperf perhaps?
219+
crypto_vbdev_name: self.key.as_ref().map(|_| format!("crypto_{}", self.name)),
212220
};
213221
match &self.mode {
214222
LvsMode::Create => match crate::lvs::Lvs::import_from_args(args.clone()).await {

io-engine/src/bdev/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ pub(crate) mod dev;
1010
use crate::core::{MayastorEnvironment, PtplProps};
1111
pub(crate) use dev::uri;
1212

13+
pub mod crypto;
1314
pub(crate) mod device;
1415
mod ftl;
1516
mod loopback;

0 commit comments

Comments
 (0)