Skip to content

Commit a280680

Browse files
committed
feat(encryption): add crypto vbdev
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. Signed-off-by: Diwakar Sharma <diwakar.sharma@datacore.com>
1 parent 7c2ec38 commit a280680

File tree

6 files changed

+281
-1
lines changed

6 files changed

+281
-1
lines changed

io-engine/src/bdev/crypto.rs

+269
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,269 @@
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, Serialize, Deserialize)]
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+
write!(
122+
f,
123+
"{}",
124+
match self {
125+
Self::AccelDpdkCryptodevAesniMb => "crypto_aesni_mb",
126+
Self::_AccelDpdkCryptodevQat => "crypto_qat",
127+
Self::_AccelDpdkCryptodevMlx5Pci => "mlx5_pci",
128+
Self::_AccelDpdkCryptodevUadk => "crypto_uadk",
129+
Self::_AccelDpdkCryptodevLast => "crypto_driver_invalid",
130+
}
131+
)
132+
}
133+
}
134+
135+
// This function enables the dpdk_cryptodev module with aesni_mb driver in spdk,
136+
// and assigns the encrypt and decrypt operation to it, which by default are assigned to
137+
// software module initially.
138+
#[allow(dead_code)]
139+
pub fn enable_dpdk_cryptodev_accel_module() -> Result<(), CoreError> {
140+
info!("Enabling accel dpdk_cryptodev module");
141+
unsafe {
142+
accel_dpdk_cryptodev_enable();
143+
let setdrv_ret = accel_dpdk_cryptodev_set_driver(
144+
AccelDpdkCryptodevDriverType::AccelDpdkCryptodevAesniMb
145+
.to_string()
146+
.into_cstring()
147+
.as_ptr(),
148+
);
149+
if setdrv_ret != 0 {
150+
return Err(CoreError::InitCryptoModule {
151+
reason: format!("Unsupported driver: {:?}", setdrv_ret),
152+
});
153+
}
154+
let err_enc = spdk_accel_assign_opc(8, "dpdk_cryptodev".into_cstring().as_ptr());
155+
let err_dec = spdk_accel_assign_opc(9, "dpdk_cryptodev".into_cstring().as_ptr());
156+
info!("assign opc err_enc: {err_enc}, err_dec: {err_dec}");
157+
if err_enc != 0 || err_dec != 0 {
158+
return Err(CoreError::InitCryptoModule {
159+
reason: format!(
160+
"opcode assignment failed. err_enc {err_enc:?}, err_dec {err_dec:?}"
161+
),
162+
});
163+
}
164+
Ok(())
165+
}
166+
}
167+
168+
/// A simple wrapper around `spdk_accel_crypto_key_create`. The keys' strings here are
169+
/// in hexlified format.
170+
fn create_crypto_key(key_params: &EncryptionKey) -> Result<*mut spdk_accel_crypto_key, BdevError> {
171+
let cipher = key_params.cipher.to_string().into_cstring();
172+
let key = key_params.key.as_str().into_cstring();
173+
let key_name = key_params.key_name.as_str().into_cstring();
174+
let hex_key2_str = key_params.key2.as_deref().map(|k2| k2.into_cstring());
175+
let key2 = if let Some(ref k2) = hex_key2_str {
176+
k2.as_ptr()
177+
} else {
178+
std::ptr::null_mut()
179+
};
180+
181+
debug!(
182+
"Creating {:?} key of size {}",
183+
key_params.cipher.to_string(),
184+
key_params.key_len,
185+
);
186+
187+
let create_key_params = spdk_accel_crypto_key_create_param {
188+
cipher: cipher.as_ptr() as *mut c_char,
189+
hex_key: key.as_ptr() as *mut c_char,
190+
hex_key2: key2 as *mut c_char,
191+
key_name: key_name.as_ptr() as *mut c_char,
192+
tweak_mode: std::ptr::null_mut(),
193+
};
194+
195+
unsafe {
196+
let ret = spdk_accel_crypto_key_create(&create_key_params);
197+
if ret != 0 {
198+
Err(BdevError::CreateBdevFailed {
199+
source: Errno::from_raw(ret.abs()),
200+
name: format!("Key create failed: {:?}", create_key_params.key_name),
201+
})
202+
} else {
203+
let key_ptr = spdk_accel_crypto_key_get(create_key_params.key_name);
204+
trace!("Key '{:?}' created.", key_params.key_name);
205+
Ok(key_ptr)
206+
}
207+
}
208+
}
209+
210+
/// callback when operation has been performed on crypto vbdev
211+
extern "C" fn crypto_vbdev_op_cb(arg: *mut std::os::raw::c_void, errno: i32) {
212+
let sender = unsafe { Box::from_raw(arg as *mut oneshot::Sender<i32>) };
213+
trace!("crypto_vbdev_op_cb: errno {errno}");
214+
sender.send(errno).unwrap();
215+
}
216+
217+
/// Destroy a crypto vbdev.
218+
pub async fn destroy_crypto_vbdev(vbdev_name: String) -> Result<(), BdevError> {
219+
let (s, r) = pair::<i32>();
220+
unsafe {
221+
delete_crypto_disk(
222+
vbdev_name.as_ptr() as *mut std::os::raw::c_char,
223+
Some(crypto_vbdev_op_cb),
224+
cb_arg(s),
225+
);
226+
}
227+
228+
r.await
229+
.expect("callback gone while deleting crypto disk")
230+
.to_result(|e| BdevError::DestroyBdevFailed {
231+
source: Errno::from_raw(e),
232+
name: vbdev_name.to_string(),
233+
})?;
234+
235+
info!("crypto vbdev {vbdev_name} destroyed successfully");
236+
Ok(())
237+
}
238+
/// The primary function to create a crypto vbdev in spdk.
239+
pub fn create_crypto_vbdev_on_base_bdev(
240+
crypto_vbdev_name: &str,
241+
base_bdev_name: &str,
242+
key_params: &EncryptionKey,
243+
) -> Result<(), BdevError> {
244+
// Create the key.
245+
let key = create_crypto_key(key_params)?;
246+
247+
// Setup the crypto opts using the key now.
248+
let crypto_opts_ptr = unsafe {
249+
create_crypto_opts_by_name(
250+
crypto_vbdev_name.into_cstring().as_ptr() as *mut c_char,
251+
base_bdev_name.into_cstring().as_ptr() as *mut c_char,
252+
key,
253+
true,
254+
)
255+
};
256+
257+
// Now create the crypto vbdev.
258+
let ret = unsafe { create_crypto_disk(crypto_opts_ptr) };
259+
260+
if ret != 0 {
261+
Err(BdevError::CreateBdevFailed {
262+
source: Errno::from_raw(ret),
263+
name: crypto_vbdev_name.to_string(),
264+
})
265+
} else {
266+
info!("crypto vbdev {crypto_vbdev_name} created on base bdev {base_bdev_name}");
267+
Ok(())
268+
}
269+
}

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;

io-engine/src/core/mod.rs

+5
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,10 @@ pub enum CoreError {
273273
WipeFailed {
274274
source: wiper::Error,
275275
},
276+
#[snafu(display("Failed to init crypto module: {reason}"))]
277+
InitCryptoModule {
278+
reason: String,
279+
},
276280
}
277281

278282
/// Represent error as Errno value.
@@ -318,6 +322,7 @@ impl ToErrno for CoreError {
318322
Self::Ptpl { .. } => Errno::EIO,
319323
Self::SnapshotCreate { source, .. } => source,
320324
Self::WipeFailed { .. } => Errno::EIO,
325+
Self::InitCryptoModule { .. } => Errno::EINVAL,
321326
}
322327
}
323328
}

io-engine/src/lvs/lvs_error.rs

+3
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ pub enum BsError {
4949
OutOfMetadata {},
5050
#[snafu(display(": capacity overflow"))]
5151
CapacityOverflow {},
52+
#[snafu(display(": crypto vbdev error"))]
53+
LvsCryptoVbdev {},
5254
}
5355

5456
impl BsError {
@@ -101,6 +103,7 @@ impl ToErrno for BsError {
101103
Self::NoSpace {} => Errno::ENOSPC,
102104
Self::OutOfMetadata {} => Errno::EMFILE,
103105
Self::CapacityOverflow {} => Errno::EOVERFLOW,
106+
Self::LvsCryptoVbdev {} => Errno::EINVAL,
104107
}
105108
}
106109
}

nix/pkgs/io-engine/cargo-package.nix

+2
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
, versions
2929
, systemdMinimal
3030
, rdma-core
31+
, keyutils
3132
, cargoBuildFlags ? [ ]
3233
, pname ? "io-engine"
3334
, rustFlags
@@ -91,6 +92,7 @@ let
9192
systemdMinimal.dev
9293
utillinux.dev
9394
rdma-core
95+
keyutils
9496
];
9597
cargoLock = {
9698
lockFile = ../../../Cargo.lock;

0 commit comments

Comments
 (0)