← Documents Documentation/networking/tls.rst GitHub 원문 ↗

Linux 6.18.37 · Networking

Kernel TLS

kTLS ULP 설정부터 TX/RX data와 control record, TLS 1.3 rekey, zero-copy option 및 namespace 통계까지 설명합니다.

Source pathDocumentation/networking/tls.rst
Source versionLinux v6.18.37
TranslationDUJINLABS 전문 번역 + 해설

요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.

1. 요약·해설

원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.

요약·해설

tls.rst:1-326

kTLS는 userspace TLS library가 handshake를 마친 뒤 대칭 암호화와 record data path를 kernel로 넘기는 interface입니다. TX와 RX key state를 독립적으로 설치하며 일반 send/recv API와 CMSG 기반 control record를 지원합니다.

TLS 1.3 KeyUpdate에서는 새 RX key가 설치될 때까지 read를 차단합니다. Zero-copy option은 immutable data나 trusted no-padding peer라는 전제가 필요하며, 위반·예측 실패는 authentication error 또는 decrypt retry로 관찰할 수 있습니다.

kTLS lifecycle
TCP connectTCP_ULP=tlsUserspace TLS handshakeTLS_TX/TLS_RX crypto_infokTLS record encrypt/decryptTCP
TLS 1.3 KeyUpdateRX pause새 TLS_RX keyRX resume

Userspace handshake 이후 kernel record layer가 맡는 흐름입니다.

2. 영어 원문 전체

번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.

원문 전체 펼치기
1 .. _kernel_tls:
2
3 ==========
4 Kernel TLS
5 ==========
6
7 Overview
8 ========
9
10 Transport Layer Security (TLS) is a Upper Layer Protocol (ULP) that runs over
11 TCP. TLS provides end-to-end data integrity and confidentiality.
12
13 User interface
14 ==============
15
16 Creating a TLS connection
17 -------------------------
18
19 First create a new TCP socket and once the connection is established set the
20 TLS ULP.
21
22 .. code-block:: c
23
24 sock = socket(AF_INET, SOCK_STREAM, 0);
25 connect(sock, addr, addrlen);
26 setsockopt(sock, SOL_TCP, TCP_ULP, "tls", sizeof("tls"));
27
28 Setting the TLS ULP allows us to set/get TLS socket options. Currently
29 only the symmetric encryption is handled in the kernel. After the TLS
30 handshake is complete, we have all the parameters required to move the
31 data-path to the kernel. There is a separate socket option for moving
32 the transmit and the receive into the kernel.
33
34 .. code-block:: c
35
36 /* From linux/tls.h */
37 struct tls_crypto_info {
38 unsigned short version;
39 unsigned short cipher_type;
40 };
41
42 struct tls12_crypto_info_aes_gcm_128 {
43 struct tls_crypto_info info;
44 unsigned char iv[TLS_CIPHER_AES_GCM_128_IV_SIZE];
45 unsigned char key[TLS_CIPHER_AES_GCM_128_KEY_SIZE];
46 unsigned char salt[TLS_CIPHER_AES_GCM_128_SALT_SIZE];
47 unsigned char rec_seq[TLS_CIPHER_AES_GCM_128_REC_SEQ_SIZE];
48 };
49
50
51 struct tls12_crypto_info_aes_gcm_128 crypto_info;
52
53 crypto_info.info.version = TLS_1_2_VERSION;
54 crypto_info.info.cipher_type = TLS_CIPHER_AES_GCM_128;
55 memcpy(crypto_info.iv, iv_write, TLS_CIPHER_AES_GCM_128_IV_SIZE);
56 memcpy(crypto_info.rec_seq, seq_number_write,
57 TLS_CIPHER_AES_GCM_128_REC_SEQ_SIZE);
58 memcpy(crypto_info.key, cipher_key_write, TLS_CIPHER_AES_GCM_128_KEY_SIZE);
59 memcpy(crypto_info.salt, implicit_iv_write, TLS_CIPHER_AES_GCM_128_SALT_SIZE);
60
61 setsockopt(sock, SOL_TLS, TLS_TX, &crypto_info, sizeof(crypto_info));
62
63 Transmit and receive are set separately, but the setup is the same, using either
64 TLS_TX or TLS_RX.
65
66 Sending TLS application data
67 ----------------------------
68
69 After setting the TLS_TX socket option all application data sent over this
70 socket is encrypted using TLS and the parameters provided in the socket option.
71 For example, we can send an encrypted hello world record as follows:
72
73 .. code-block:: c
74
75 const char *msg = "hello world\n";
76 send(sock, msg, strlen(msg));
77
78 send() data is directly encrypted from the userspace buffer provided
79 to the encrypted kernel send buffer if possible.
80
81 The sendfile system call will send the file's data over TLS records of maximum
82 length (2^14).
83
84 .. code-block:: c
85
86 file = open(filename, O_RDONLY);
87 fstat(file, &stat);
88 sendfile(sock, file, &offset, stat.st_size);
89
90 TLS records are created and sent after each send() call, unless
91 MSG_MORE is passed. MSG_MORE will delay creation of a record until
92 MSG_MORE is not passed, or the maximum record size is reached.
93
94 The kernel will need to allocate a buffer for the encrypted data.
95 This buffer is allocated at the time send() is called, such that
96 either the entire send() call will return -ENOMEM (or block waiting
97 for memory), or the encryption will always succeed. If send() returns
98 -ENOMEM and some data was left on the socket buffer from a previous
99 call using MSG_MORE, the MSG_MORE data is left on the socket buffer.
100
101 Receiving TLS application data
102 ------------------------------
103
104 After setting the TLS_RX socket option, all recv family socket calls
105 are decrypted using TLS parameters provided. A full TLS record must
106 be received before decryption can happen.
107
108 .. code-block:: c
109
110 char buffer[16384];
111 recv(sock, buffer, 16384);
112
113 Received data is decrypted directly in to the user buffer if it is
114 large enough, and no additional allocations occur. If the userspace
115 buffer is too small, data is decrypted in the kernel and copied to
116 userspace.
117
118 ``EINVAL`` is returned if the TLS version in the received message does not
119 match the version passed in setsockopt.
120
121 ``EMSGSIZE`` is returned if the received message is too big.
122
123 ``EBADMSG`` is returned if decryption failed for any other reason.
124
125 Send TLS control messages
126 -------------------------
127
128 Other than application data, TLS has control messages such as alert
129 messages (record type 21) and handshake messages (record type 22), etc.
130 These messages can be sent over the socket by providing the TLS record type
131 via a CMSG. For example the following function sends @data of @length bytes
132 using a record of type @record_type.
133
134 .. code-block:: c
135
136 /* send TLS control message using record_type */
137 static int klts_send_ctrl_message(int sock, unsigned char record_type,
138 void *data, size_t length)
139 {
140 struct msghdr msg = {0};
141 int cmsg_len = sizeof(record_type);
142 struct cmsghdr *cmsg;
143 char buf[CMSG_SPACE(cmsg_len)];
144 struct iovec msg_iov; /* Vector of data to send/receive into. */
145
146 msg.msg_control = buf;
147 msg.msg_controllen = sizeof(buf);
148 cmsg = CMSG_FIRSTHDR(&msg);
149 cmsg->cmsg_level = SOL_TLS;
150 cmsg->cmsg_type = TLS_SET_RECORD_TYPE;
151 cmsg->cmsg_len = CMSG_LEN(cmsg_len);
152 *CMSG_DATA(cmsg) = record_type;
153 msg.msg_controllen = cmsg->cmsg_len;
154
155 msg_iov.iov_base = data;
156 msg_iov.iov_len = length;
157 msg.msg_iov = &msg_iov;
158 msg.msg_iovlen = 1;
159
160 return sendmsg(sock, &msg, 0);
161 }
162
163 Control message data should be provided unencrypted, and will be
164 encrypted by the kernel.
165
166 Receiving TLS control messages
167 ------------------------------
168
169 TLS control messages are passed in the userspace buffer, with message
170 type passed via cmsg. If no cmsg buffer is provided, an error is
171 returned if a control message is received. Data messages may be
172 received without a cmsg buffer set.
173
174 .. code-block:: c
175
176 char buffer[16384];
177 char cmsg[CMSG_SPACE(sizeof(unsigned char))];
178 struct msghdr msg = {0};
179 msg.msg_control = cmsg;
180 msg.msg_controllen = sizeof(cmsg);
181
182 struct iovec msg_iov;
183 msg_iov.iov_base = buffer;
184 msg_iov.iov_len = 16384;
185
186 msg.msg_iov = &msg_iov;
187 msg.msg_iovlen = 1;
188
189 int ret = recvmsg(sock, &msg, 0 /* flags */);
190
191 struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
192 if (cmsg->cmsg_level == SOL_TLS &&
193 cmsg->cmsg_type == TLS_GET_RECORD_TYPE) {
194 int record_type = *((unsigned char *)CMSG_DATA(cmsg));
195 // Do something with record_type, and control message data in
196 // buffer.
197 //
198 // Note that record_type may be == to application data (23).
199 } else {
200 // Buffer contains application data.
201 }
202
203 recv will never return data from mixed types of TLS records.
204
205 TLS 1.3 Key Updates
206 -------------------
207
208 In TLS 1.3, KeyUpdate handshake messages signal that the sender is
209 updating its TX key. Any message sent after a KeyUpdate will be
210 encrypted using the new key. The userspace library can pass the new
211 key to the kernel using the TLS_TX and TLS_RX socket options, as for
212 the initial keys. TLS version and cipher cannot be changed.
213
214 To prevent attempting to decrypt incoming records using the wrong key,
215 decryption will be paused when a KeyUpdate message is received by the
216 kernel, until the new key has been provided using the TLS_RX socket
217 option. Any read occurring after the KeyUpdate has been read and
218 before the new key is provided will fail with EKEYEXPIRED. poll() will
219 not report any read events from the socket until the new key is
220 provided. There is no pausing on the transmit side.
221
222 Userspace should make sure that the crypto_info provided has been set
223 properly. In particular, the kernel will not check for key/nonce
224 reuse.
225
226 The number of successful and failed key updates is tracked in the
227 ``TlsTxRekeyOk``, ``TlsRxRekeyOk``, ``TlsTxRekeyError``,
228 ``TlsRxRekeyError`` statistics. The ``TlsRxRekeyReceived`` statistic
229 counts KeyUpdate handshake messages that have been received.
230
231 Integrating in to userspace TLS library
232 ---------------------------------------
233
234 At a high level, the kernel TLS ULP is a replacement for the record
235 layer of a userspace TLS library.
236
237 A patchset to OpenSSL to use ktls as the record layer is
238 `here <https://github.com/Mellanox/openssl/commits/tls_rx2>`_.
239
240 `An example <https://github.com/ktls/af_ktls-tool/commits/RX>`_
241 of calling send directly after a handshake using gnutls.
242 Since it doesn't implement a full record layer, control
243 messages are not supported.
244
245 Optional optimizations
246 ----------------------
247
248 There are certain condition-specific optimizations the TLS ULP can make,
249 if requested. Those optimizations are either not universally beneficial
250 or may impact correctness, hence they require an opt-in.
251 All options are set per-socket using setsockopt(), and their
252 state can be checked using getsockopt() and via socket diag (``ss``).
253
254 TLS_TX_ZEROCOPY_RO
255 ~~~~~~~~~~~~~~~~~~
256
257 For device offload only. Allow sendfile() data to be transmitted directly
258 to the NIC without making an in-kernel copy. This allows true zero-copy
259 behavior when device offload is enabled.
260
261 The application must make sure that the data is not modified between being
262 submitted and transmission completing. In other words this is mostly
263 applicable if the data sent on a socket via sendfile() is read-only.
264
265 Modifying the data may result in different versions of the data being used
266 for the original TCP transmission and TCP retransmissions. To the receiver
267 this will look like TLS records had been tampered with and will result
268 in record authentication failures.
269
270 TLS_RX_EXPECT_NO_PAD
271 ~~~~~~~~~~~~~~~~~~~~
272
273 TLS 1.3 only. Expect the sender to not pad records. This allows the data
274 to be decrypted directly into user space buffers with TLS 1.3.
275
276 This optimization is safe to enable only if the remote end is trusted,
277 otherwise it is an attack vector to doubling the TLS processing cost.
278
279 If the record decrypted turns out to had been padded or is not a data
280 record it will be decrypted again into a kernel buffer without zero copy.
281 Such events are counted in the ``TlsDecryptRetry`` statistic.
282
283 Statistics
284 ==========
285
286 TLS implementation exposes the following per-namespace statistics
287 (``/proc/net/tls_stat``):
288
289 - ``TlsCurrTxSw``, ``TlsCurrRxSw`` -
290 number of TX and RX sessions currently installed where host handles
291 cryptography
292
293 - ``TlsCurrTxDevice``, ``TlsCurrRxDevice`` -
294 number of TX and RX sessions currently installed where NIC handles
295 cryptography
296
297 - ``TlsTxSw``, ``TlsRxSw`` -
298 number of TX and RX sessions opened with host cryptography
299
300 - ``TlsTxDevice``, ``TlsRxDevice`` -
301 number of TX and RX sessions opened with NIC cryptography
302
303 - ``TlsDecryptError`` -
304 record decryption failed (e.g. due to incorrect authentication tag)
305
306 - ``TlsDeviceRxResync`` -
307 number of RX resyncs sent to NICs handling cryptography
308
309 - ``TlsDecryptRetry`` -
310 number of RX records which had to be re-decrypted due to
311 ``TLS_RX_EXPECT_NO_PAD`` mis-prediction. Note that this counter will
312 also increment for non-data records.
313
314 - ``TlsRxNoPadViolation`` -
315 number of data RX records which had to be re-decrypted due to
316 ``TLS_RX_EXPECT_NO_PAD`` mis-prediction.
317
318 - ``TlsTxRekeyOk``, ``TlsRxRekeyOk`` -
319 number of successful rekeys on existing sessions for TX and RX
320
321 - ``TlsTxRekeyError``, ``TlsRxRekeyError`` -
322 number of failed rekeys on existing sessions for TX and RX
323
324 - ``TlsRxRekeyReceived`` -
325 number of received KeyUpdate handshake messages, requiring userspace
326 to provide a new RX key
327

3. 한국어 전문 번역

영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.

개요

1-12

Transport Layer Security(TLS)는 TCP 위에서 실행되는 Upper Layer Protocol(ULP)입니다. TLS는 종단 간 데이터 무결성과 기밀성을 제공합니다.

이 문서는 kernel TLS(kTLS)의 userspace interface를 설명합니다. TCP connection에 TLS ULP를 결합한 뒤 handshake에서 얻은 대칭 암호화 상태를 kernel data path에 넘기는 방법을 다룹니다.

.. _kernel_tls:

==========
Kernel TLS
==========

Overview
========

Transport Layer Security (TLS) is a Upper Layer Protocol (ULP) that runs over
TCP. TLS provides end-to-end data integrity and confidentiality.

TLS connection 생성과 crypto state 설치

13-65

먼저 새 TCP socket을 만들고 connection이 확립되면 `TCP_ULP` socket option에 `"tls"`를 지정해 TLS ULP를 설정합니다. 이 설정을 마쳐야 TLS 전용 socket option을 읽고 쓸 수 있습니다.

현재 kernel은 대칭 암호화만 처리합니다. TLS handshake 자체는 userspace library가 수행하며, handshake가 끝나 key, IV, salt, record sequence를 확보한 뒤 transmit과 receive data path를 kernel로 각각 이동합니다.

`struct tls_crypto_info`의 `version`과 `cipher_type`은 TLS version과 cipher를 식별합니다. AES-GCM-128을 사용하는 `struct tls12_crypto_info_aes_gcm_128`에는 `iv`, `key`, `salt`, `rec_seq`가 포함됩니다. 예제는 `TLS_1_2_VERSION`과 `TLS_CIPHER_AES_GCM_128`을 지정하고 write 방향의 값을 복사합니다.

구성한 structure를 `setsockopt(sock, SOL_TLS, TLS_TX, ...)`로 설치합니다. Transmit과 receive는 독립적으로 설정하며, receive 방향은 같은 절차에서 `TLS_RX`를 사용합니다.

kTLS connection 설정
단계주체핵심 동작
TCP 연결Applicationsocket()과 connect()
TLS handshakeUserspace TLS libraryKey와 record state 협상
ULP 연결Kernel TCPTCP_ULP="tls"
TX/RX 설치Application → kTLSSOL_TLS의 TLS_TX 또는 TLS_RX

Handshake와 kernel data path의 책임을 나눕니다.

User interface
==============

Creating a TLS connection
-------------------------

First create a new TCP socket and once the connection is established set the
TLS ULP.

.. code-block:: c

  sock = socket(AF_INET, SOCK_STREAM, 0);
  connect(sock, addr, addrlen);
  setsockopt(sock, SOL_TCP, TCP_ULP, "tls", sizeof("tls"));

Setting the TLS ULP allows us to set/get TLS socket options. Currently
only the symmetric encryption is handled in the kernel.  After the TLS
handshake is complete, we have all the parameters required to move the
data-path to the kernel. There is a separate socket option for moving
the transmit and the receive into the kernel.

.. code-block:: c

  /* From linux/tls.h */
  struct tls_crypto_info {
          unsigned short version;
          unsigned short cipher_type;
  };

  struct tls12_crypto_info_aes_gcm_128 {
          struct tls_crypto_info info;
          unsigned char iv[TLS_CIPHER_AES_GCM_128_IV_SIZE];
          unsigned char key[TLS_CIPHER_AES_GCM_128_KEY_SIZE];
          unsigned char salt[TLS_CIPHER_AES_GCM_128_SALT_SIZE];
          unsigned char rec_seq[TLS_CIPHER_AES_GCM_128_REC_SEQ_SIZE];
  };


  struct tls12_crypto_info_aes_gcm_128 crypto_info;

  crypto_info.info.version = TLS_1_2_VERSION;
  crypto_info.info.cipher_type = TLS_CIPHER_AES_GCM_128;
  memcpy(crypto_info.iv, iv_write, TLS_CIPHER_AES_GCM_128_IV_SIZE);
  memcpy(crypto_info.rec_seq, seq_number_write,
                                        TLS_CIPHER_AES_GCM_128_REC_SEQ_SIZE);
  memcpy(crypto_info.key, cipher_key_write, TLS_CIPHER_AES_GCM_128_KEY_SIZE);
  memcpy(crypto_info.salt, implicit_iv_write, TLS_CIPHER_AES_GCM_128_SALT_SIZE);

  setsockopt(sock, SOL_TLS, TLS_TX, &crypto_info, sizeof(crypto_info));

Transmit and receive are set separately, but the setup is the same, using either
TLS_TX or TLS_RX.

TLS application data 송신

66-100

`TLS_TX` socket option을 설정한 뒤 이 socket으로 보내는 모든 application data는 제공한 TLS parameter로 암호화됩니다. 일반 `send()` 호출을 그대로 사용할 수 있으며, 가능한 경우 userspace buffer에서 kernel의 encrypted send buffer로 직접 암호화합니다.

`sendfile()`은 file data를 최대 길이 `2^14`인 TLS record로 나누어 전송합니다.

기본적으로 각 `send()` 호출 뒤 TLS record를 만들어 전송합니다. `MSG_MORE`를 넘기면 다음 호출을 기다려 record 생성을 미루며, `MSG_MORE`가 빠지거나 최대 record size에 도달하면 record가 완성됩니다.

Kernel은 `send()` 시점에 encrypted data buffer를 할당합니다. 따라서 호출 전체가 `-ENOMEM`으로 실패하거나 memory를 기다리며 block하고, 할당에 성공했다면 encryption은 항상 성공합니다. 이전 `MSG_MORE` 호출의 data가 socket buffer에 남은 상태에서 새 `send()`가 `-ENOMEM`을 반환해도 그 기존 data는 그대로 유지됩니다.

TLS TX data path
send()/sendfile()MSG_MORE 조합TLS record 최대 2^14kTLS encryptionTCP send buffer

일반 socket API 호출이 record 생성과 암호화로 이어집니다.

Sending TLS application data
----------------------------

After setting the TLS_TX socket option all application data sent over this
socket is encrypted using TLS and the parameters provided in the socket option.
For example, we can send an encrypted hello world record as follows:

.. code-block:: c

  const char *msg = "hello world\n";
  send(sock, msg, strlen(msg));

send() data is directly encrypted from the userspace buffer provided
to the encrypted kernel send buffer if possible.

The sendfile system call will send the file's data over TLS records of maximum
length (2^14).

.. code-block:: c

  file = open(filename, O_RDONLY);
  fstat(file, &stat);
  sendfile(sock, file, &offset, stat.st_size);

TLS records are created and sent after each send() call, unless
MSG_MORE is passed.  MSG_MORE will delay creation of a record until
MSG_MORE is not passed, or the maximum record size is reached.

The kernel will need to allocate a buffer for the encrypted data.
This buffer is allocated at the time send() is called, such that
either the entire send() call will return -ENOMEM (or block waiting
for memory), or the encryption will always succeed.  If send() returns
-ENOMEM and some data was left on the socket buffer from a previous
call using MSG_MORE, the MSG_MORE data is left on the socket buffer.

TLS application data 수신

101-124

`TLS_RX` socket option을 설정하면 `recv` 계열 socket call이 제공된 TLS parameter로 data를 복호화합니다. 복호화를 시작하려면 완전한 TLS record 하나가 도착해야 합니다.

Userspace buffer가 충분히 크면 수신 data를 그 buffer에 직접 복호화하므로 추가 allocation이 없습니다. Buffer가 너무 작으면 kernel buffer에 먼저 복호화한 뒤 userspace로 복사합니다.

수신 message의 TLS version이 `setsockopt()`로 설치한 version과 다르면 `EINVAL`, message가 너무 크면 `EMSGSIZE`, 그 밖의 이유로 복호화가 실패하면 `EBADMSG`를 반환합니다.

TLS RX 오류
errno의미
EINVAL수신 TLS version 불일치
EMSGSIZE수신 message가 너무 큼
EBADMSG그 밖의 decrypt/authentication 실패

수신 record가 거부되는 원인을 errno로 구분합니다.

Receiving TLS application data
------------------------------

After setting the TLS_RX socket option, all recv family socket calls
are decrypted using TLS parameters provided.  A full TLS record must
be received before decryption can happen.

.. code-block:: c

  char buffer[16384];
  recv(sock, buffer, 16384);

Received data is decrypted directly in to the user buffer if it is
large enough, and no additional allocations occur.  If the userspace
buffer is too small, data is decrypted in the kernel and copied to
userspace.

``EINVAL`` is returned if the TLS version in the received message does not
match the version passed in setsockopt.

``EMSGSIZE`` is returned if the received message is too big.

``EBADMSG`` is returned if decryption failed for any other reason.

TLS control message 송신

125-165

Application data 외에도 TLS에는 alert message(record type 21), handshake message(record type 22) 같은 control message가 있습니다. `sendmsg()`의 CMSG로 TLS record type을 전달해 이런 message를 보낼 수 있습니다.

예제 함수 `klts_send_ctrl_message()`는 ancillary data의 level을 `SOL_TLS`, type을 `TLS_SET_RECORD_TYPE`으로 설정하고 한 byte의 `record_type`을 `CMSG_DATA`에 기록합니다. 실제 payload는 `iovec`으로 전달합니다.

Control message payload는 암호화하지 않은 상태로 제공해야 합니다. Kernel이 지정한 record type의 TLS record를 만들고 payload를 암호화합니다.

Control record 송신
Plaintext control datasendmsg iovec
TLS_SET_RECORD_TYPE CMSGSOL_TLS
kTLS record 생성·암호화TCP

Record type은 CMSG, payload는 iovec으로 전달합니다.

Send TLS control messages
-------------------------

Other than application data, TLS has control messages such as alert
messages (record type 21) and handshake messages (record type 22), etc.
These messages can be sent over the socket by providing the TLS record type
via a CMSG. For example the following function sends @data of @length bytes
using a record of type @record_type.

.. code-block:: c

  /* send TLS control message using record_type */
  static int klts_send_ctrl_message(int sock, unsigned char record_type,
                                    void *data, size_t length)
  {
        struct msghdr msg = {0};
        int cmsg_len = sizeof(record_type);
        struct cmsghdr *cmsg;
        char buf[CMSG_SPACE(cmsg_len)];
        struct iovec msg_iov;   /* Vector of data to send/receive into.  */

        msg.msg_control = buf;
        msg.msg_controllen = sizeof(buf);
        cmsg = CMSG_FIRSTHDR(&msg);
        cmsg->cmsg_level = SOL_TLS;
        cmsg->cmsg_type = TLS_SET_RECORD_TYPE;
        cmsg->cmsg_len = CMSG_LEN(cmsg_len);
        *CMSG_DATA(cmsg) = record_type;
        msg.msg_controllen = cmsg->cmsg_len;

        msg_iov.iov_base = data;
        msg_iov.iov_len = length;
        msg.msg_iov = &msg_iov;
        msg.msg_iovlen = 1;

        return sendmsg(sock, &msg, 0);
  }

Control message data should be provided unencrypted, and will be
encrypted by the kernel.

TLS control message 수신

166-204

TLS control message payload는 userspace buffer로 전달되고 message type은 CMSG로 전달됩니다. Control message가 도착했는데 caller가 CMSG buffer를 제공하지 않았다면 오류를 반환합니다. Application data는 CMSG buffer 없이도 받을 수 있습니다.

예제는 `recvmsg()`에 control buffer와 data `iovec`을 함께 넘긴 뒤 첫 CMSG가 `SOL_TLS`와 `TLS_GET_RECORD_TYPE`인지 확인합니다. 맞으면 `CMSG_DATA`에서 record type을 읽고 payload buffer를 해당 type에 맞게 처리합니다. Record type은 application data를 뜻하는 23일 수도 있습니다.

CMSG가 없으면 buffer에는 application data가 들어 있습니다. `recv`는 서로 다른 TLS record type의 data를 한 번의 반환값에 섞지 않습니다.

TLS record type 전달
수신 항목위치
Record payloadmsg_iov userspace buffer
Record typeSOL_TLS/TLS_GET_RECORD_TYPE CMSG
Application dataType 23 또는 CMSG 없는 일반 recv

Payload와 type metadata가 분리되어 userspace에 도착합니다.

Receiving TLS control messages
------------------------------

TLS control messages are passed in the userspace buffer, with message
type passed via cmsg.  If no cmsg buffer is provided, an error is
returned if a control message is received.  Data messages may be
received without a cmsg buffer set.

.. code-block:: c

  char buffer[16384];
  char cmsg[CMSG_SPACE(sizeof(unsigned char))];
  struct msghdr msg = {0};
  msg.msg_control = cmsg;
  msg.msg_controllen = sizeof(cmsg);

  struct iovec msg_iov;
  msg_iov.iov_base = buffer;
  msg_iov.iov_len = 16384;

  msg.msg_iov = &msg_iov;
  msg.msg_iovlen = 1;

  int ret = recvmsg(sock, &msg, 0 /* flags */);

  struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
  if (cmsg->cmsg_level == SOL_TLS &&
      cmsg->cmsg_type == TLS_GET_RECORD_TYPE) {
      int record_type = *((unsigned char *)CMSG_DATA(cmsg));
      // Do something with record_type, and control message data in
      // buffer.
      //
      // Note that record_type may be == to application data (23).
  } else {
      // Buffer contains application data.
  }

recv will never return data from mixed types of TLS records.

TLS 1.3 KeyUpdate

205-230

TLS 1.3의 KeyUpdate handshake message는 sender가 TX key를 갱신한다는 신호입니다. KeyUpdate 뒤에 보내는 모든 message는 새 key로 암호화됩니다. Userspace library는 초기 key와 마찬가지로 `TLS_TX`와 `TLS_RX` socket option으로 새 key를 kernel에 전달합니다. TLS version과 cipher는 바꿀 수 없습니다.

잘못된 key로 incoming record 복호화를 시도하지 않도록 kernel이 KeyUpdate를 받으면 새 `TLS_RX` key가 제공될 때까지 RX decryption을 일시 중지합니다. KeyUpdate를 읽은 뒤 새 key를 설치하기 전의 read는 `EKEYEXPIRED`로 실패하고, `poll()`도 socket의 read event를 보고하지 않습니다. TX 방향은 중지되지 않습니다.

Userspace는 전달하는 `crypto_info`가 올바른지 보장해야 합니다. 특히 kernel은 key나 nonce 재사용을 검사하지 않습니다.

성공·실패한 rekey는 `TlsTxRekeyOk`, `TlsRxRekeyOk`, `TlsTxRekeyError`, `TlsRxRekeyError`에 기록합니다. 수신한 KeyUpdate handshake message 수는 `TlsRxRekeyReceived`가 셉니다.

TLS 1.3 RX KeyUpdate
KeyUpdate record 수신RX decrypt pauseread → EKEYEXPIREDpoll read event 없음TLS_RX 새 key 설치복호화 재개

새 RX key가 설치될 때까지 read readiness를 차단합니다.

TLS 1.3 Key Updates
-------------------

In TLS 1.3, KeyUpdate handshake messages signal that the sender is
updating its TX key. Any message sent after a KeyUpdate will be
encrypted using the new key. The userspace library can pass the new
key to the kernel using the TLS_TX and TLS_RX socket options, as for
the initial keys. TLS version and cipher cannot be changed.

To prevent attempting to decrypt incoming records using the wrong key,
decryption will be paused when a KeyUpdate message is received by the
kernel, until the new key has been provided using the TLS_RX socket
option. Any read occurring after the KeyUpdate has been read and
before the new key is provided will fail with EKEYEXPIRED. poll() will
not report any read events from the socket until the new key is
provided. There is no pausing on the transmit side.

Userspace should make sure that the crypto_info provided has been set
properly. In particular, the kernel will not check for key/nonce
reuse.

The number of successful and failed key updates is tracked in the
``TlsTxRekeyOk``, ``TlsRxRekeyOk``, ``TlsTxRekeyError``,
``TlsRxRekeyError`` statistics. The ``TlsRxRekeyReceived`` statistic
counts KeyUpdate handshake messages that have been received.

Userspace TLS library 통합과 opt-in option

231-253

높은 수준에서 kernel TLS ULP는 userspace TLS library의 record layer를 대체합니다.

문서에는 kTLS를 record layer로 사용하는 OpenSSL patchset과, GnuTLS handshake 뒤 `send()`를 직접 호출하는 예제가 연결되어 있습니다. GnuTLS 예제는 완전한 record layer를 구현하지 않으므로 control message를 지원하지 않습니다.

TLS ULP에는 특정 조건에서만 유효한 optimization이 있습니다. 모든 환경에서 이득이 되지 않거나 correctness에 영향을 줄 수 있으므로 명시적으로 opt-in해야 합니다. Option은 socket별 `setsockopt()`으로 설정하고 `getsockopt()` 및 socket diagnostics인 `ss`로 상태를 확인합니다.

Integrating in to userspace TLS library
---------------------------------------

At a high level, the kernel TLS ULP is a replacement for the record
layer of a userspace TLS library.

A patchset to OpenSSL to use ktls as the record layer is
`here <https://github.com/Mellanox/openssl/commits/tls_rx2>`_.

`An example <https://github.com/ktls/af_ktls-tool/commits/RX>`_
of calling send directly after a handshake using gnutls.
Since it doesn't implement a full record layer, control
messages are not supported.

Optional optimizations
----------------------

There are certain condition-specific optimizations the TLS ULP can make,
if requested. Those optimizations are either not universally beneficial
or may impact correctness, hence they require an opt-in.
All options are set per-socket using setsockopt(), and their
state can be checked using getsockopt() and via socket diag (``ss``).

Zero-copy와 no-padding optimization

254-282

`TLS_TX_ZEROCOPY_RO`는 device offload 전용 option입니다. Kernel 내부 복사 없이 `sendfile()` data를 NIC로 직접 보내 device offload에서 실제 zero-copy를 가능하게 합니다.

Application은 제출한 뒤 transmission이 끝날 때까지 data가 변경되지 않도록 보장해야 합니다. 따라서 주로 `sendfile()`로 보내는 data가 read-only일 때 적합합니다.

전송 중 data를 수정하면 최초 TCP transmission과 retransmission에 서로 다른 내용이 쓰일 수 있습니다. Receiver에는 TLS record가 변조된 것으로 보이며 record authentication failure가 발생합니다.

`TLS_RX_EXPECT_NO_PAD`는 TLS 1.3 전용 option으로, sender가 record padding을 하지 않을 것이라고 가정해 data를 userspace buffer에 직접 복호화합니다.

이 optimization은 remote endpoint를 신뢰할 때만 안전합니다. 신뢰하지 않는 peer가 padding을 사용하면 TLS processing cost를 두 배로 만드는 공격 경로가 될 수 있습니다.

실제로 복호화한 record에 padding이 있거나 data record가 아니면 zero-copy를 포기하고 kernel buffer에 다시 복호화합니다. 이런 retry는 `TlsDecryptRetry` statistic에 기록됩니다.

kTLS optional optimization
Option이득필수 조건·실패 결과
TLS_TX_ZEROCOPY_ROsendfile data를 NIC로 직접 전송완료 전 immutable, 위반 시 authentication failure
TLS_RX_EXPECT_NO_PADTLS 1.3 userspace buffer 직접 복호화Trusted peer, padding이면 kernel buffer로 retry

성능 이득과 caller가 보장해야 할 조건입니다.

TLS_TX_ZEROCOPY_RO
~~~~~~~~~~~~~~~~~~

For device offload only. Allow sendfile() data to be transmitted directly
to the NIC without making an in-kernel copy. This allows true zero-copy
behavior when device offload is enabled.

The application must make sure that the data is not modified between being
submitted and transmission completing. In other words this is mostly
applicable if the data sent on a socket via sendfile() is read-only.

Modifying the data may result in different versions of the data being used
for the original TCP transmission and TCP retransmissions. To the receiver
this will look like TLS records had been tampered with and will result
in record authentication failures.

TLS_RX_EXPECT_NO_PAD
~~~~~~~~~~~~~~~~~~~~

TLS 1.3 only. Expect the sender to not pad records. This allows the data
to be decrypted directly into user space buffers with TLS 1.3.

This optimization is safe to enable only if the remote end is trusted,
otherwise it is an attack vector to doubling the TLS processing cost.

If the record decrypted turns out to had been padded or is not a data
record it will be decrypted again into a kernel buffer without zero copy.
Such events are counted in the ``TlsDecryptRetry`` statistic.

Per-network-namespace 통계

283-326

TLS 구현은 network namespace별 통계를 `/proc/net/tls_stat`에 공개합니다.

`TlsCurrTxSw`와 `TlsCurrRxSw`는 host가 cryptography를 처리하며 현재 설치된 TX/RX session 수이고, `TlsCurrTxDevice`와 `TlsCurrRxDevice`는 NIC가 cryptography를 처리하며 현재 설치된 session 수입니다.

`TlsTxSw`와 `TlsRxSw`는 host cryptography로 연 TX/RX session의 누적 수이고, `TlsTxDevice`와 `TlsRxDevice`는 NIC cryptography로 연 session의 누적 수입니다.

`TlsDecryptError`는 잘못된 authentication tag 등의 record decryption 실패를 셉니다. `TlsDeviceRxResync`는 cryptography를 처리하는 NIC에 보낸 RX resync 수입니다.

`TlsDecryptRetry`는 `TLS_RX_EXPECT_NO_PAD` 예측 오류 때문에 다시 복호화한 RX record 수이며 non-data record도 포함합니다. `TlsRxNoPadViolation`은 같은 예측 오류 때문에 다시 복호화한 data RX record만 셉니다.

`TlsTxRekeyOk`와 `TlsRxRekeyOk`는 기존 TX/RX session의 성공한 rekey 수이고, `TlsTxRekeyError`와 `TlsRxRekeyError`는 실패한 rekey 수입니다. `TlsRxRekeyReceived`는 userspace가 새 RX key를 제공해야 하는 수신 KeyUpdate handshake message 수입니다.

/proc/net/tls_stat 분류
분류Counter
현재 softwareTlsCurrTxSw, TlsCurrRxSw
현재 deviceTlsCurrTxDevice, TlsCurrRxDevice
누적 software/deviceTlsTxSw, TlsRxSw, TlsTxDevice, TlsRxDevice
복호화·resyncTlsDecryptError, TlsDeviceRxResync, TlsDecryptRetry, TlsRxNoPadViolation
RekeyTlsTxRekeyOk, TlsRxRekeyOk, TlsTxRekeyError, TlsRxRekeyError, TlsRxRekeyReceived

현재 상태, 누적 session, 오류와 rekey를 구분합니다.

Statistics
==========

TLS implementation exposes the following per-namespace statistics
(``/proc/net/tls_stat``):

- ``TlsCurrTxSw``, ``TlsCurrRxSw`` -
  number of TX and RX sessions currently installed where host handles
  cryptography

- ``TlsCurrTxDevice``, ``TlsCurrRxDevice`` -
  number of TX and RX sessions currently installed where NIC handles
  cryptography

- ``TlsTxSw``, ``TlsRxSw`` -
  number of TX and RX sessions opened with host cryptography

- ``TlsTxDevice``, ``TlsRxDevice`` -
  number of TX and RX sessions opened with NIC cryptography

- ``TlsDecryptError`` -
  record decryption failed (e.g. due to incorrect authentication tag)

- ``TlsDeviceRxResync`` -
  number of RX resyncs sent to NICs handling cryptography

- ``TlsDecryptRetry`` -
  number of RX records which had to be re-decrypted due to
  ``TLS_RX_EXPECT_NO_PAD`` mis-prediction. Note that this counter will
  also increment for non-data records.

- ``TlsRxNoPadViolation`` -
  number of data RX records which had to be re-decrypted due to
  ``TLS_RX_EXPECT_NO_PAD`` mis-prediction.

- ``TlsTxRekeyOk``, ``TlsRxRekeyOk`` -
  number of successful rekeys on existing sessions for TX and RX

- ``TlsTxRekeyError``, ``TlsRxRekeyError`` -
  number of failed rekeys on existing sessions for TX and RX

- ``TlsRxRekeyReceived`` -
  number of received KeyUpdate handshake messages, requiring userspace
  to provide a new RX key