1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
//! See [`Compatibilities::probe`].
use std::io;
use std::net::{TcpListener, TcpStream};
use bitfield_struct::bitfield;
use crate::setup::{setup_ulp, TlsCryptoInfoTx};
use crate::tls::{AeadKey, ConnectionTrafficSecrets, ProtocolVersion};
#[non_exhaustive]
#[derive(Debug, Clone, Copy)]
/// The current Linux kernel's kTLS cipher suites compatibility.
pub struct Compatibilities {
/// TLS 1.2 cipher suite compatibility.
pub tls12: Compatibility,
/// TLS 1.3 cipher suite compatibility.
pub tls13: Compatibility,
}
impl Compatibilities {
/// Probes the current Linux kernel for kTLS cipher suites compatibility.
///
/// Returns `Ok(None)` if the running kernel does not support kTLS, e.g.,
/// `tls` module is not available. If the kernel is modern enough (at least
/// 5.4), it should support kTLS but might not have `tls` module loaded.
///
/// The caller can cache the successful result of this method, while the
/// supporting cipher suites are unlikely to change during the program's
/// lifetime. Note that the attempt to configure TLS ULP still might fail
/// due to kTLS being unsupported, while the `tls` module can be loaded
/// / unloaded at runtime.
///
/// The caller may check the returned `Compatibility`s with
/// [`Compatibility::is_unsupported`] to see whether the desired TLS
/// version is totally unsupported.
///
/// ## Errors
///
/// [`io::Error`].
pub fn probe() -> io::Result<Option<Self>> {
let listener = TcpListener::bind("127.0.0.1:0")?;
let local_addr = listener.local_addr()?;
let mut tls12 = Compatibility::new();
let mut tls13 = Compatibility::new();
macro_rules! test {
($ver:ident, $cipher:ident, $c:ident, $m:ident) => {{
let stream = TcpStream::connect(local_addr)?;
match setup_ulp(&stream) {
Ok(_) => {}
Err(err) if err.is_ktls_unsupported() => return Ok(None),
Err(err) => {
crate::error!(
"Failed to probe compatibility of {} {}",
stringify!($ver),
stringify!($cipher)
);
return Err(err.into());
}
}
if TlsCryptoInfoTx::new(
ProtocolVersion::$ver,
ConnectionTrafficSecrets::$cipher {
key: AeadKey::new(Default::default()),
iv: Default::default(),
salt: Default::default(),
},
0,
)
.unwrap()
.set(&stream)
.inspect_err(|_err| {
crate::trace!(
"{} {}: Not suitable: {}",
stringify!($ver),
stringify!($cipher),
_err
);
})
.is_ok()
{
$c.$m(true);
}
}};
}
test!(TLSv1_2, Aes128Gcm, tls12, set_aes_128_gcm);
test!(TLSv1_2, Aes256Gcm, tls12, set_aes_256_gcm);
test!(TLSv1_2, Chacha20Poly1305, tls12, set_chacha20_poly1305);
test!(TLSv1_2, Aes128Ccm, tls12, set_aes_128_ccm);
test!(TLSv1_2, Sm4Gcm, tls12, set_sm4_gcm);
test!(TLSv1_2, Sm4Ccm, tls12, set_sm4_ccm);
test!(TLSv1_2, Aria128Gcm, tls12, set_aria_128_gcm);
test!(TLSv1_2, Aria256Gcm, tls12, set_aria_256_gcm);
test!(TLSv1_3, Aes128Gcm, tls13, set_aes_128_gcm);
test!(TLSv1_3, Aes256Gcm, tls13, set_aes_256_gcm);
test!(TLSv1_3, Chacha20Poly1305, tls13, set_chacha20_poly1305);
test!(TLSv1_3, Aes128Ccm, tls13, set_aes_128_ccm);
test!(TLSv1_3, Sm4Gcm, tls13, set_sm4_gcm);
test!(TLSv1_3, Sm4Ccm, tls13, set_sm4_ccm);
test!(TLSv1_3, Aria128Gcm, tls13, set_aria_128_gcm);
test!(TLSv1_3, Aria256Gcm, tls13, set_aria_256_gcm);
if tls12.is_unsupported() && tls13.is_unsupported() {
crate::error!("All cipher suites are unsupported while kTLS seems to be supported");
return Ok(None);
}
let this = Self { tls12, tls13 };
crate::trace!("kTLS compatibilities probed: {:#?}", this);
Ok(Some(this))
}
}
#[bitfield(u8)]
/// Represents the compatibility of various TLS cipher suites with kernel TLS.
pub struct Compatibility {
/// AES-128-GCM cipher suite support.
pub aes_128_gcm: bool,
/// AES-256-GCM cipher suite support.
pub aes_256_gcm: bool,
/// ChaCha20-Poly1305 cipher suite support.
pub chacha20_poly1305: bool,
/// AES-128-CCM cipher suite support.
pub aes_128_ccm: bool,
/// SM4-GCM cipher suite support.
pub sm4_gcm: bool,
/// SM4-CCM cipher suite support.
pub sm4_ccm: bool,
/// ARIA-128-GCM cipher suite support.
pub aria_128_gcm: bool,
/// ARIA-256-GCM cipher suite support.
pub aria_256_gcm: bool,
}
impl Compatibility {
/// Returns whether no cipher suites are supported (the corresponding TLS
/// version is unsupported).
#[must_use]
pub const fn is_unsupported(&self) -> bool {
self.0 == 0
}
}