Compare commits

..
Author SHA1 Message Date
Vitaliy Filippov 32721046b8 WIP Switch back to TCP when RDMA send fails (does not work) 2026-05-01 22:26:41 +00:00
Vitaliy Filippov f8de201625 Close TCP connections for RDMA clients 2026-05-01 22:13:29 +00:00
Vitaliy Filippov e7f30c4939 Also check for "Received garbage" during test 2026-05-01 16:01:28 +03:00
Vitaliy Filippov 8e441f4173 Add a CI test with RDMA 2026-05-01 15:57:15 +03:00
Vitaliy Filippov 80a05c052a Add request size validation to prevent OOMDoS 2026-05-01 15:57:15 +03:00
Vitaliy Filippov a972c13571 Implement OSD-side authorization for operations 2026-04-29 02:42:23 +03:00
Vitaliy Filippov 9372df4beb Remove tls_ from option names 2026-04-27 15:41:00 +03:00
Vitaliy Filippov 24c27386b6 Remove TLS support (superseded by direct GCM) 2026-04-27 15:41:00 +03:00
Vitaliy Filippov 394de5e9aa Implement TLS 1.3-like handshake manually 2026-04-27 15:24:44 +03:00
Vitaliy Filippov be61e76df0 Extract openssl-related code, wire ssl implementation back 2026-04-27 15:24:44 +03:00
Vitaliy Filippov 5cad126987 Coalesce entries in send_out_buf 2026-04-27 15:24:44 +03:00
Vitaliy Filippov afa50a05b8 Support isa-l_crypto for AES-XTS too 2026-04-27 15:24:44 +03:00
Vitaliy Filippov 9ff833178e Fix xts+rdma encrypt errors 2026-04-27 15:24:44 +03:00
Vitaliy Filippov b152baa733 Fix "use-after-realloc" warning 2026-04-27 15:24:44 +03:00
Vitaliy Filippov 8dbbf4097a Support isa-l_crypto for AES-GCM 2026-04-27 15:24:44 +03:00
Vitaliy Filippov 17cb875e10 Remove WITH_OPENSSL from all files except http_client, always require OpenSSL 2026-04-27 15:24:44 +03:00
Vitaliy Filippov 03e15338e5 Use pools for GCM contexts 2026-04-27 15:24:44 +03:00
Vitaliy Filippov dccc549e33 Try to run tests with AES-GCM 2026-04-27 15:24:44 +03:00
Vitaliy Filippov 440a8bc33a Implement direct AES-256-GCM with a static key for benchmark 2026-04-27 15:24:44 +03:00
Vitaliy Filippov 78a21542da Make sure to send all TLS data before continuing 2026-04-27 15:24:44 +03:00
Vitaliy Filippov 75ba9509ad Parse standard TLS record headers 2026-04-27 15:24:44 +03:00
Vitaliy Filippov 8b58fe9808 Omit msgr_tls_record_hdr_t for non-tls data 2026-04-27 15:24:44 +03:00
Vitaliy Filippov 331f345a78 Allow 2 and 4 byte per block chain_info (allow more than 255 snapshots with encryption) 2026-04-27 15:24:44 +03:00
Vitaliy Filippov d0a4ac914b Implement OSD TLS support 2026-04-27 15:24:44 +03:00
Vitaliy Filippov e42b812323 Allow to skip checksums for headers 2026-04-27 15:24:44 +03:00
Vitaliy Filippov ac5c753735 Implement protocol-level checksums (xxhash3) 2026-04-27 15:24:44 +03:00
Vitaliy Filippov f9eb497105 Include xxhash3 x86dispatch 2026-04-27 15:24:44 +03:00
Vitaliy Filippov 3489c99640 Do not use scrap_buffer in the client (it would block protocol checksum support) 2026-04-27 15:24:44 +03:00
Vitaliy Filippov a21dcd4480 Support TLS CN authentication and per-image permissions in vitastor-cli serve 2026-04-27 15:24:44 +03:00
Vitaliy Filippov 340849248b Add VitastorAuthFilter 2026-04-27 15:24:44 +03:00
Vitaliy Filippov ee462c4375 Implement vitastor-cli ls-user, modify-user, remove-user commands 2026-04-27 15:24:44 +03:00
Vitaliy Filippov 0053a0d825 Add image owner/owner_group/reader_group support (for antietcd VitastorAuthFilter) 2026-04-27 15:24:44 +03:00
Vitaliy Filippov cdeecead8d Add security parameter documentation 2026-04-27 15:24:44 +03:00
Vitaliy Filippov c0e34b19c9 Support inline (string PEM) certificates and pkeys 2026-04-27 15:24:44 +03:00
Vitaliy Filippov e8abe99205 Show encryption keys (only IDs) in the listing 2026-04-27 15:24:44 +03:00
Vitaliy Filippov 8225b37f53 Support storing image encryption keys in Vault 2026-04-27 15:24:44 +03:00
Vitaliy Filippov 62b29b03c5 Prefer local etcd addresses and correctly cycle over them even when they need resolving
Seems slightly overcomplicated...
2026-04-27 15:24:44 +03:00
Vitaliy Filippov ae2f570b58 Support DNS resolving via libc-ares 2026-04-27 15:24:44 +03:00
Vitaliy Filippov d7f854ab3e Batch handle_immediate_ops more 2026-04-27 15:24:44 +03:00
Vitaliy Filippov 29640d1aac Add vitastor-cli create & modify --enc-key parameter 2026-04-27 15:24:44 +03:00
Vitaliy Filippov 66a016f8a1 Support reading from snapshots encrypted with different keys 2026-04-27 15:24:44 +03:00
Vitaliy Filippov 34d01f44db Support decryption with multiple keys 2026-04-27 15:24:44 +03:00
Vitaliy Filippov 3956982112 Allow to return chain_info in response to reads 2026-04-27 15:24:44 +03:00
Vitaliy Filippov 713b17e574 Add basic AES-XTS client-side encryption support 2026-04-27 15:24:44 +03:00
Vitaliy Filippov 4efa48c9ca Rework msgr send/receive to allow encryption support 2026-04-27 15:24:44 +03:00
Vitaliy Filippov 26d5e1ed00 Move fromhexstr() to str_util 2026-04-27 15:24:44 +03:00
Vitaliy Filippov 9ba3444cfa Add openapi description 2026-04-27 15:24:44 +03:00
Vitaliy Filippov 96c0001127 Slightly fix API return and input types 2026-04-27 15:24:44 +03:00
Vitaliy Filippov 56a14cc0bd Implement vitastor-cli serve command to serve simple HTTP API 2026-04-27 15:24:44 +03:00
Vitaliy Filippov 6ca1b9eca9 Implement HTTP server support O_o 2026-04-27 15:24:44 +03:00
Vitaliy Filippov 876dbd730a Rename http_response_t to http_message_t 2026-04-27 15:24:44 +03:00
Vitaliy Filippov 4398ee0377 Extract common HTTP context 2026-04-27 15:24:44 +03:00
Vitaliy Filippov 2c673a90bd Support xxhash 32-bit checksums (data_csum_type=xxh3_32) 2026-04-27 15:24:44 +03:00
Vitaliy Filippov a8371498e1 Detect block checksums using csum_block_size, not data_csum_type 2026-04-27 15:24:43 +03:00
Vitaliy Filippov 2a093cbfb5 Add client certificate support 2026-04-27 15:24:43 +03:00
Vitaliy Filippov 094a181491 Do not re-initialize TLS context every connection 2026-04-27 15:24:43 +03:00
Vitaliy Filippov fe30783135 Add https support to antietcd 2026-04-27 15:24:43 +03:00
Vitaliy Filippov e9a6c58418 Implement etcd SSL support via OpenSSL
Maybe I should remove all of this and use libwebsockets :)
2026-04-27 15:24:43 +03:00
Vitaliy Filippov 4acfe149cb Release 3.0.10
Important bug fixes (new store):
- Fix OSDs possibly refusing to start with "write metadata failed at offset xxx: Invalid argument"
  (fix buffer alignment during initial garbage collection)
- Rollback change from 3.0.4 - on-disk garbage entries are not skipped on start again. This change
  doesn't have any impact normally, but OSDs originally running 3.0.0-3.0.2 and then upgraded
  to 3.0.9 may hit a bug where 3.0.9 refuses to start due to entries marked as garbage too
  early and flushed to disk in 3.0.0-3.0.2.

Other changes:
- Auto-select the only RDMA device/port if there is only one
- Rollback one 3.0.9 change - there was no actual use-after-free :)
  (the problem was only relevant to an unstable development version)
- Fix `vitastor-nfs --trace` option
- Fix an unintended 1 second sleep in vitastor-cli rm-data
- Fix inode statistics not being cleared for a deleted pool
- Fix print to stdout in client
2026-04-27 13:53:48 +03:00
Vitaliy Filippov 008ed5b269 Rollback 25ecca7625 - there was no actual use-after-free :) 2026-04-22 01:35:38 +03:00
Vitaliy Filippov 4fffe0f032 Fix vitastor-nfs trace option 2026-04-20 21:51:47 +03:00
Vitaliy Filippov a76d5ccc0d Wakeup callers in rm-data 2026-04-17 13:53:32 +03:00
Vitaliy Filippov 8ed1e180e0 Clear inode_stats in mon 2026-04-14 02:44:46 +03:00
Vitaliy Filippov 8832fc3b14 Fix print to stdout in client 2026-04-14 02:43:50 +03:00
Vitaliy Filippov 0134934c99 Do not skip garbage entries on start (rollback change from 3.0.4) 2026-04-11 12:05:02 +03:00
Vitaliy Filippov 2e36f292bd Fix buffer alignment during metadata clearing on init 2026-04-10 21:56:12 +03:00
Vitaliy Filippov bcc6419760 Auto-select the only RDMA device/port if there is only one 2026-04-08 15:46:10 +03:00
91 changed files with 3187 additions and 1450 deletions
+18
View File
@@ -1278,6 +1278,24 @@ jobs:
echo ""
done
test_heal_ec_rdma:
runs-on: ubuntu-latest
needs: build
container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps:
- name: Run test
id: test
timeout-minutes: 10
run: TEST_NAME=ec_rdma RDMA=1 SCHEME=ec /root/vitastor/tests/test_heal.sh
- name: Print logs
if: always() && steps.test.outcome == 'failure'
run: |
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
echo "-------- $i --------"
cat $i
echo ""
done
test_checksum:
runs-on: ubuntu-latest
needs: build
+1 -1
View File
@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 2.8...3.30)
project(vitastor)
set(VITASTOR_VERSION "3.0.9")
set(VITASTOR_VERSION "3.0.10")
include(CTest)
+1 -1
View File
@@ -1,4 +1,4 @@
VITASTOR_VERSION ?= v3.0.9
VITASTOR_VERSION ?= v3.0.10
all: build push
+1 -1
View File
@@ -49,7 +49,7 @@ spec:
capabilities:
add: ["SYS_ADMIN"]
allowPrivilegeEscalation: true
image: vitalif/vitastor-csi:v3.0.9
image: vitalif/vitastor-csi:v3.0.10
args:
- "--node=$(NODE_ID)"
- "--endpoint=$(CSI_ENDPOINT)"
+1 -1
View File
@@ -121,7 +121,7 @@ spec:
privileged: true
capabilities:
add: ["SYS_ADMIN"]
image: vitalif/vitastor-csi:v3.0.9
image: vitalif/vitastor-csi:v3.0.10
args:
- "--node=$(NODE_ID)"
- "--endpoint=$(CSI_ENDPOINT)"
+1 -1
View File
@@ -5,7 +5,7 @@ package vitastor
const (
vitastorCSIDriverName = "csi.vitastor.io"
vitastorCSIDriverVersion = "3.0.9"
vitastorCSIDriverVersion = "3.0.10"
)
// Config struct fills the parameters of request or user input
+1 -1
View File
@@ -1,4 +1,4 @@
vitastor (3.0.9-1) unstable; urgency=medium
vitastor (3.0.10-1) unstable; urgency=medium
* Bugfixes
+6 -8
View File
@@ -12,20 +12,18 @@ ARG REL=
WORKDIR /root
RUN set -e -x; \
if [ "$REL" = "buster" ]; then \
perl -i -pe 's/deb.debian.org/archive.debian.org/' /etc/apt/sources.list; \
apt-get update; \
apt-get -y install wget; \
wget https://vitastor.io/debian/pubkey.gpg -O /etc/apt/trusted.gpg.d/vitastor.gpg; \
echo "deb https://vitastor.io/debian $REL main" >> /etc/apt/sources.list; \
fi; \
perl -i -pe 's/deb.debian.org/archive.debian.org/' /etc/apt/sources.list; \
apt-get update; \
apt-get -y install wget; \
wget https://vitastor.io/debian/pubkey.gpg -O /etc/apt/trusted.gpg.d/vitastor.gpg; \
echo "deb https://vitastor.io/debian $REL main" >> /etc/apt/sources.list; \
grep '^deb ' /etc/apt/sources.list | perl -pe 's/^deb/deb-src/' >> /etc/apt/sources.list; \
perl -i -pe 's/Types: deb$/Types: deb deb-src/' /etc/apt/sources.list.d/*.sources || true; \
echo 'APT::Install-Recommends false;' >> /etc/apt/apt.conf; \
echo 'APT::Install-Suggests false;' >> /etc/apt/apt.conf
RUN apt-get update && \
apt-get -y install fio libgoogle-perftools-dev devscripts libjerasure-dev cmake libc-ares-dev \
apt-get -y install fio libgoogle-perftools-dev devscripts libjerasure-dev cmake libc-ares-dev libisal-crypto-dev \
libibverbs-dev librdmacm-dev libisal-dev libnl-3-dev libnl-genl-3-dev curl nodejs npm node-nan node-bindings && \
apt-get -y build-dep fio && \
apt-get --download-only source fio
+1 -1
View File
@@ -1,4 +1,4 @@
VITASTOR_VERSION ?= v3.0.9
VITASTOR_VERSION ?= v3.0.10
all: build push
+1 -1
View File
@@ -4,7 +4,7 @@
#
# Desired Vitastor version
VITASTOR_VERSION=v3.0.9
VITASTOR_VERSION=v3.0.10
# Additional arguments for all containers
# For example, you may want to specify a custom logging driver here
+4 -4
View File
@@ -25,7 +25,7 @@ Most of them can be set in /etc/vitastor/vitastor.conf and in etcd, but don't su
- [vault_timeout_ms](#vault_timeout_ms)
- [vault_error_timeout_sec](#vault_error_timeout_sec)
- [vault_refresh_leeway_sec](#vault_refresh_leeway_sec)
- [max_aes_xts_pool_size](#max_aes_xts_pool_size)
- [max_cipher_pool_size](#max_cipher_pool_size)
## etcd_client_cert
@@ -141,10 +141,10 @@ Time (in seconds) to wait before retrying after receiving an error from Vault.
Extra time (in seconds) before real Vault token lease_timeout to refresh it, just
in case of system clock drift.
## max_aes_xts_pool_size
## max_cipher_pool_size
- Type: integer
- Default: 256
Maximum number of OpenSSL encryption contexts cached in OSD memory. Probably
doesn't require modification.
Maximum number of OpenSSL cipher contexts cached in OSD memory, counted separately
for each cipher and for encryption/decryption. Probably doesn't require modification.
+4 -4
View File
@@ -27,7 +27,7 @@ OSD, мониторами и клиентами.
- [vault_timeout_ms](#vault_timeout_ms)
- [vault_error_timeout_sec](#vault_error_timeout_sec)
- [vault_refresh_leeway_sec](#vault_refresh_leeway_sec)
- [max_aes_xts_pool_size](#max_aes_xts_pool_size)
- [max_cipher_pool_size](#max_cipher_pool_size)
## etcd_client_cert
@@ -145,10 +145,10 @@ OSD, клиенты и мониторы должны иметь разные п
Зазор времени (в секундах), чтобы обновлять токены Vault чуть раньше их реального
lease_timeout, на случай "ухода" системных часов.
## max_aes_xts_pool_size
## max_cipher_pool_size
- Тип: целое число
- Значение по умолчанию: 256
Максимальное количество кэшируемых в памяти OSD контекстов шифрования OpenSSL.
Вряд ли требует изменения.
Максимальное количество кэшируемых в памяти OSD контекстов шифра OpenSSL, учитываемое
отдельно для каждого шифра и для шифрования и расшифровки. Вряд ли требует изменения.
+5 -5
View File
@@ -120,12 +120,12 @@
info_ru: |
Зазор времени (в секундах), чтобы обновлять токены Vault чуть раньше их реального
lease_timeout, на случай "ухода" системных часов.
- name: max_aes_xts_pool_size
- name: max_cipher_pool_size
type: int
default: 256
info: |
Maximum number of OpenSSL encryption contexts cached in OSD memory. Probably
doesn't require modification.
Maximum number of OpenSSL cipher contexts cached in OSD memory, counted separately
for each cipher and for encryption/decryption. Probably doesn't require modification.
info_ru: |
Максимальное количество кэшируемых в памяти OSD контекстов шифрования OpenSSL.
Вряд ли требует изменения.
Максимальное количество кэшируемых в памяти OSD контекстов шифра OpenSSL, учитываемое
отдельно для каждого шифра и для шифрования и расшифровки. Вряд ли требует изменения.
+2 -2
View File
@@ -26,9 +26,9 @@ at Vitastor Kubernetes operator: https://github.com/Antilles7227/vitastor-operat
The instruction is very simple.
1. Download a Docker image of the desired version: \
`docker pull vitalif/vitastor:v3.0.9`
`docker pull vitalif/vitastor:v3.0.10`
2. Install scripts to the host system: \
`docker run --rm -it -v /etc:/host-etc -v /usr/bin:/host-bin vitalif/vitastor:v3.0.9 install.sh`
`docker run --rm -it -v /etc:/host-etc -v /usr/bin:/host-bin vitalif/vitastor:v3.0.10 install.sh`
3. Reload udev rules: \
`udevadm control --reload-rules`
4. Enable the vitastor-host service: \
+2 -2
View File
@@ -25,9 +25,9 @@ Vitastor можно установить в Docker/Podman. При этом etcd,
Инструкция по установке максимально простая.
1. Скачайте Docker-образ желаемой версии: \
`docker pull vitalif/vitastor:v3.0.9`
`docker pull vitalif/vitastor:v3.0.10`
2. Установите скрипты в хост-систему командой: \
`docker run --rm -it -v /etc:/host-etc -v /usr/bin:/host-bin vitalif/vitastor:v3.0.9 install.sh`
`docker run --rm -it -v /etc:/host-etc -v /usr/bin:/host-bin vitalif/vitastor:v3.0.10 install.sh`
3. Перезагрузите правила udev: \
`udevadm control --reload-rules`
4. Включите сервис vitastor-host: \
+1
View File
@@ -262,3 +262,4 @@ Options:
| `--logfile <FILE>` | log to the specified file |
| `--enforce 1` | enforce permissions at the server side (no by default) |
| `--foreground 1` | stay in foreground, do not daemonize |
| `--trace` | trace all NFS requests |
+1
View File
@@ -274,3 +274,4 @@ VitastorFS из GPUDirect.
| `--logfile <FILE>` | записывать логи в заданный файл |
| `--enforce 1` | проверять права доступа на стороне сервера (по умолчанию нет) |
| `--foreground 1` | не уходить в фон после запуска |
| `--trace` | логгировать все запросы NFS |
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "vitastor-mon",
"version": "3.0.9",
"version": "3.0.10",
"description": "Vitastor SDS monitor service",
"main": "mon-main.js",
"scripts": {
+1
View File
@@ -37,6 +37,7 @@ function derive_osd_stats(st, prev, prev_diff)
const n = c.count - BigInt(pr && pr.count||0);
diff.recovery_stats[op] = { ...c, bps: n > 0 ? b*1000n/timediff : 0n, iops: n > 0 ? n*1000n/timediff : 0n };
}
diff.inode_stats = {};
for (const pool_id in st.inode_stats||{})
{
diff.inode_stats[pool_id] = {};
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "vitastor",
"version": "3.0.9",
"version": "3.0.10",
"description": "Low-level native bindings to Vitastor client library",
"main": "index.js",
"keywords": [
+1 -1
View File
@@ -50,7 +50,7 @@ from cinder.volume import configuration
from cinder.volume import driver
from cinder.volume import volume_utils
VITASTOR_VERSION = '3.0.9'
VITASTOR_VERSION = '3.0.10'
LOG = logging.getLogger(__name__)
+2 -2
View File
@@ -1,11 +1,11 @@
Name: vitastor
Version: 3.0.9
Version: 3.0.10
Release: 1%{?dist}
Summary: Vitastor, a fast software-defined clustered block storage
License: Vitastor Network Public License 1.1
URL: https://vitastor.io/
Source0: vitastor-3.0.9.el10.tar.gz
Source0: vitastor-3.0.10.el10.tar.gz
BuildRequires: gperftools-devel
BuildRequires: gcc-c++
+2 -2
View File
@@ -1,11 +1,11 @@
Name: vitastor
Version: 3.0.9
Version: 3.0.10
Release: 1%{?dist}
Summary: Vitastor, a fast software-defined clustered block storage
License: Vitastor Network Public License 1.1
URL: https://vitastor.io/
Source0: vitastor-3.0.9.el7.tar.gz
Source0: vitastor-3.0.10.el7.tar.gz
BuildRequires: gperftools-devel
BuildRequires: devtoolset-9-gcc-c++
+2 -2
View File
@@ -1,11 +1,11 @@
Name: vitastor
Version: 3.0.9
Version: 3.0.10
Release: 1%{?dist}
Summary: Vitastor, a fast software-defined clustered block storage
License: Vitastor Network Public License 1.1
URL: https://vitastor.io/
Source0: vitastor-3.0.9.el8.tar.gz
Source0: vitastor-3.0.10.el8.tar.gz
BuildRequires: gperftools-devel
BuildRequires: gcc-toolset-9-gcc-c++
+2 -2
View File
@@ -1,11 +1,11 @@
Name: vitastor
Version: 3.0.9
Version: 3.0.10
Release: 1%{?dist}
Summary: Vitastor, a fast software-defined clustered block storage
License: Vitastor Network Public License 1.1
URL: https://vitastor.io/
Source0: vitastor-3.0.9.el9.tar.gz
Source0: vitastor-3.0.10.el9.tar.gz
BuildRequires: gperftools-devel
BuildRequires: gcc-c++
+6 -4
View File
@@ -20,7 +20,7 @@ if("${CMAKE_INSTALL_PREFIX}" MATCHES "^/usr/local/?$")
endif()
set(ENABLE_COVERAGE false CACHE BOOL "Enable code coverage")
add_definitions(-DVITASTOR_VERSION="3.0.9")
add_definitions(-DVITASTOR_VERSION="3.0.10")
add_definitions(-D_GNU_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -Wall -Wno-sign-compare -Wno-comment -Wno-parentheses -Wno-pointer-arith -fdiagnostics-color=always -fno-omit-frame-pointer -fvisibility=hidden -I ${CMAKE_SOURCE_DIR}/src)
add_link_options(-fno-omit-frame-pointer)
if (${WITH_ASAN})
@@ -69,15 +69,17 @@ pkg_check_modules(ISAL libisal)
if (ISAL_LIBRARIES)
add_definitions(-DWITH_ISAL)
endif (ISAL_LIBRARIES)
pkg_check_modules(ISAL_CRYPTO libisal_crypto)
if (ISAL_CRYPTO_LIBRARIES)
add_definitions(-DWITH_ISAL_CRYPTO)
endif (ISAL_CRYPTO_LIBRARIES)
pkg_check_modules(RDMACM librdmacm)
if (RDMACM_LIBRARIES)
add_definitions(-DWITH_RDMACM)
endif (RDMACM_LIBRARIES)
find_package(OpenSSL REQUIRED)
if (OPENSSL_FOUND)
add_definitions(-DWITH_OPENSSL)
endif (OPENSSL_FOUND)
add_definitions(-DWITH_OPENSSL)
pkg_check_modules(CARES REQUIRED libcares)
include_directories(${CARES_INCLUDE_DIRS})
+6 -13
View File
@@ -344,19 +344,12 @@ corrupted_block:
block_num, block_offset, wr->size, sizeof(heap_entry_t));
goto corrupted_block;
}
if (wr->is_garbage())
{
// Garbage collection is only performed when writing new entries into the block
// because it needs a fake LSN and modified blocks require consecutive modified LSNs
// That's why garbage entries may persist on disk
if (log_level > 5)
{
fprintf(stderr, "Notice: skipping garbage entry %jx:%jx v%ju l%ju in metadata block %u at %u\n",
wr->inode, wr->stripe, wr->version, wr->lsn, block_num, block_offset);
}
block_offset += wr->size;
continue;
}
// Garbage collection is only performed when writing new entries into the block
// because it needs a fake LSN and modified blocks require consecutive modified LSNs
// At the same time, further modifications _after_ putting new entries into the block,
// but _before_ writing it, may mark some entries in it as garbage. That's why garbage
// entries may still be present on disk.
wr->entry_type &= ~BS_HEAP_GARBAGE;
if ((wr->entry_type & BS_HEAP_TYPE) < BS_HEAP_BIG_WRITE ||
(wr->entry_type & BS_HEAP_TYPE) > BS_HEAP_ROLLBACK ||
(wr->entry_type & ~(BS_HEAP_TYPE|BS_HEAP_STABLE)) ||
+1 -1
View File
@@ -311,7 +311,7 @@ resume_8:
uint32_t block_num = recheck_mod[i];
uint64_t block_offset = bs->dsk.meta_offset + (uint64_t)(block_num+1) * bs->dsk.meta_block_size;
data = ((ring_data_t*)sqe->user_data);
uint8_t *buf = (uint8_t*)malloc_or_die(bs->dsk.meta_block_size);
uint8_t *buf = (uint8_t*)memalign_or_die(MEM_ALIGNMENT, bs->dsk.meta_block_size);
bs->heap->get_meta_block(block_num, buf);
data->iov = { buf, bs->dsk.meta_block_size };
data->callback = [this, buf, block_offset](ring_data_t *data)
+6 -5
View File
@@ -12,11 +12,11 @@ if (RDMACM_LIBRARIES)
set(MSGR_RDMACM "msgr_rdmacm.cpp")
endif (RDMACM_LIBRARIES)
add_library(vitastor_common STATIC
../util/epoll_manager.cpp etcd_state_client.cpp messenger.cpp ../util/addr_util.cpp ../util/xxh_x86dispatch.c
msgr_encrypt.cpp msgr_stop.cpp msgr_op.cpp msgr_send.cpp msgr_receive.cpp ../util/ringloop.cpp ../../json11/json11.cpp
../util/epoll_manager.cpp etcd_state_client.cpp messenger.cpp ../util/addr_util.cpp ../util/xxh_x86dispatch.c ../util/openssl_util.cpp
msgr_encrypt.cpp msgr_handshake.cpp msgr_stop.cpp msgr_op.cpp msgr_send.cpp msgr_receive.cpp ../util/ringloop.cpp ../../json11/json11.cpp
http_client.cpp osd_ops.cpp pg_states.cpp ../util/timerfd_manager.cpp ../util/str_util.cpp ../util/json_util.cpp ${MSGR_RDMA} ${MSGR_RDMACM}
)
target_link_libraries(vitastor_common pthread ${OPENSSL_LIBRARIES} ${CARES_LIBRARIES})
target_link_libraries(vitastor_common pthread ${OPENSSL_LIBRARIES} ${CARES_LIBRARIES} ${ISAL_CRYPTO_LIBRARIES})
target_compile_options(vitastor_common PUBLIC -fPIC)
# libvitastor_client.so
@@ -35,6 +35,7 @@ target_link_libraries(vitastor_client
${IBVERBS_LIBRARIES}
${RDMACM_LIBRARIES}
${OPENSSL_LIBRARIES}
${ISAL_CRYPTO_LIBRARIES}
)
set_target_properties(vitastor_client PROPERTIES VERSION ${VITASTOR_VERSION} SOVERSION 0)
configure_file(vitastor.pc.in vitastor.pc @ONLY)
@@ -101,9 +102,9 @@ add_executable(test_cluster_client
EXCLUDE_FROM_ALL
../test/test_cluster_client.cpp
pg_states.cpp osd_ops.cpp cluster_client.cpp cluster_client_list.cpp cluster_client_wb.cpp cluster_client_icache.cpp msgr_op.cpp ../test/mock/messenger.cpp msgr_stop.cpp msgr_encrypt.cpp
etcd_state_client.cpp ../util/timerfd_manager.cpp ../util/addr_util.cpp ../util/str_util.cpp ../util/json_util.cpp ../util/xxh_x86dispatch.c ../../json11/json11.cpp
etcd_state_client.cpp ../util/timerfd_manager.cpp ../util/addr_util.cpp ../util/str_util.cpp ../util/json_util.cpp ../util/xxh_x86dispatch.c ../util/openssl_util.cpp ../../json11/json11.cpp
)
target_link_libraries(test_cluster_client ${OPENSSL_LIBRARIES})
target_link_libraries(test_cluster_client ${OPENSSL_LIBRARIES} ${ISAL_CRYPTO_LIBRARIES})
target_compile_definitions(test_cluster_client PUBLIC -D__MOCK__)
target_include_directories(test_cluster_client BEFORE PUBLIC ${CMAKE_SOURCE_DIR}/src/test/mock)
add_dependencies(build_tests test_cluster_client)
+14 -13
View File
@@ -51,7 +51,7 @@ cluster_client_t::cluster_client_t(ring_loop_t *ringloop, timerfd_manager_t *tfd
msgr.stop_client(op->client_id);
delete op;
};
msgr.parse_config(config);
msgr.parse_config(config, true);
st_cli.tfd = tfd;
st_cli.on_load_config_hook = [this](json11::Json::object & cfg) { on_load_config_hook(cfg); };
@@ -481,7 +481,7 @@ void cluster_client_t::on_load_config_hook(json11::Json::object & etcd_global_co
}
// vault
vault_parse_config();
msgr.parse_config(config);
msgr.parse_config(config, false);
st_cli.parse_config(config);
st_cli.load_pgs();
}
@@ -590,7 +590,7 @@ void cluster_client_t::on_change_pool_config_hook()
{
if (log_level > 2 && pg_counts[pool_item.first])
{
printf("Pool %u (%s) PG count changed from %lu to %lu\n", pool_item.first, pool_item.second.name.c_str(),
fprintf(stderr, "Pool %u (%s) PG count changed from %lu to %lu\n", pool_item.first, pool_item.second.name.c_str(),
pg_counts[pool_item.first], pool_item.second.real_pg_count);
}
// At this point, all pool operations should have been suspended
@@ -1294,8 +1294,8 @@ void cluster_client_t::slice_rw(cluster_op_t *op)
unsigned bitmap_mem = object_bitmap_size +
op->parts.size() * pg_data_size *
(pool_cfg.data_block_size / pool_cfg.bitmap_granularity / 8
// read chain info - 1 byte per block
+ (op->enc ? op->len/pool_cfg.bitmap_granularity : 0));
// read chain_info - max 4 bytes per block
+ (op->enc ? osd_op_rw_t::chain_info_bytes(op->enc->chain_size)*op->len/pool_cfg.bitmap_granularity : 0));
if (!op->bitmap_buf || op->bitmap_buf_size < bitmap_mem)
{
op->bitmap_buf = realloc_or_die(op->bitmap_buf, bitmap_mem);
@@ -1452,7 +1452,10 @@ int cluster_client_t::try_send(cluster_op_t *op, int i, std::function<void(osd_o
op->inflight_count++;
uint32_t pg_data_size = (pool_cfg.scheme == POOL_SCHEME_REPLICATED ? 1 : pool_cfg.pg_size-pool_cfg.parity_chunks);
uint64_t pg_bitmap_size = pg_data_size * (pool_cfg.data_block_size / pool_cfg.bitmap_granularity / 8
+ (op->opcode == OSD_OP_READ && op->enc ? pool_cfg.data_block_size/pool_cfg.bitmap_granularity : 0));
// read chain_info - max 4 bytes per block
+ (op->opcode == OSD_OP_READ && op->enc
? osd_op_rw_t::chain_info_bytes(op->enc->chain_size)*pool_cfg.data_block_size/pool_cfg.bitmap_granularity
: 0));
uint64_t meta_rev = 0;
if (op->opcode != OSD_OP_READ_BITMAP && op->opcode != OSD_OP_DELETE && !op->deoptimise_snapshot)
{
@@ -1597,6 +1600,9 @@ static inline void mem_or(void *res, const void *r2, unsigned int len)
}
}
// Error priority: others > EPERM > EIO > ENOSPC > ETIMEDOUT > EPIPE
#define ERR_PRIO(e) (((e) == -EPERM ? 5 : ((e) == -EIO ? 4 : ((e) == -ENOSPC ? 3 : ((e) == -ETIMEDOUT ? 2 : ((e) == -EPIPE ? 1 : (!(e) ? 0 : 10)))))))
void cluster_client_t::handle_op_part(cluster_op_part_t *part)
{
cluster_op_t *op = part->parent;
@@ -1605,15 +1611,10 @@ void cluster_client_t::handle_op_part(cluster_op_part_t *part)
{
// Operation failed, retry
part->flags |= PART_ERROR;
if (!op->retval || op->retval == -EPIPE ||
part->op.reply.hdr.retval == -ENOSPC && op->retval == -ETIMEDOUT ||
part->op.reply.hdr.retval == -EIO)
{
// Error priority: EIO > ENOSPC > ETIMEDOUT > EPIPE
if (ERR_PRIO(part->op.reply.hdr.retval) > ERR_PRIO(op->retval))
op->retval = part->op.reply.hdr.retval;
}
uint64_t stop_client_id = 0;
if (op->retval != -EINTR && op->retval != -EIO && op->retval != -ENOSPC)
if (op->retval != -EINTR && op->retval != -EIO && op->retval != -ENOSPC && op->retval != -EPERM)
{
stop_client_id = part->op.client_id;
if (op->retval != -EPIPE || log_level > 0)
+1 -1
View File
@@ -84,7 +84,7 @@ class writeback_cache_t;
struct inode_cache_t
{
std::vector<inode_t> chain;
std::vector<inode_t> chain; // only parents from the same pool
uint8_t *key_data = NULL;
osd_op_enc_t *op_enc = NULL;
bool readonly = false;
+87 -7
View File
@@ -12,6 +12,7 @@
#include "http_client.h"
#endif
#include "str_util.h"
#include "json_util.h"
etcd_state_client_t::~etcd_state_client_t()
{
@@ -80,6 +81,46 @@ std::vector<std::string> etcd_state_client_t::get_addresses()
return addrs;
}
std::shared_ptr<user_info_t> etcd_state_client_t::get_user(const std::string & username)
{
auto user_it = user_info.find(username);
if (user_it != user_info.end())
{
return user_it->second;
}
auto inf = std::make_shared<user_info_t>();
inf->name = username;
return inf;
}
bool etcd_state_client_t::check_image_perm(const std::shared_ptr<user_info_t> & user_info, inode_t inode_num, bool write)
{
if (user_info->type == user_type_t::ADMIN)
{
return true;
}
auto cache_it = user_info->perm_cache.find(inode_num);
if (cache_it != user_info->perm_cache.end() &&
cache_it->second.mod_revision == user_perm_cache_revision)
{
return write ? (cache_it->second.perm == user_perm_t::OWNER) : (cache_it->second.perm != user_perm_t::DENY);
}
auto inode_it = inode_config.find(inode_num);
if (inode_it == inode_config.end())
{
return false;
}
// FIXME Implement cache reset after reworking etcd interaction to not keep everything in memory
auto & perm_item = user_info->perm_cache[inode_num];
perm_item.mod_revision = user_perm_cache_revision;
perm_item.perm = (user_info->name == inode_it->second.owner || inode_it->second.owner_group != "" &&
user_info->groups.find(inode_it->second.owner_group) != user_info->groups.end()
? user_perm_t::OWNER : (inode_it->second.reader_group != "" &&
user_info->groups.find(inode_it->second.reader_group) != user_info->groups.end()
? user_perm_t::READER : user_perm_t::DENY));
return write ? (perm_item.perm == user_perm_t::OWNER) : (perm_item.perm != user_perm_t::DENY);
}
http_context_t *etcd_state_client_t::get_http_ctx()
{
if (!http_ctx)
@@ -252,17 +293,21 @@ void etcd_state_client_t::parse_config(const json11::Json & config)
}
if (this->osd_num)
{
this->etcd_client_cert = config["osd_etcd_client_cert"].string_value();
this->etcd_client_key = config["osd_etcd_client_key"].string_value();
this->etcd_client_cert = config["osd_cert"].string_value();
this->etcd_client_key = config["osd_pkey"].string_value();
}
else
{
this->etcd_client_cert = config["cert"].string_value();
this->etcd_client_key = config["pkey"].string_value();
}
if (this->etcd_client_cert == "")
{
this->etcd_client_cert = config["etcd_client_cert"].string_value();
this->etcd_client_key = config["etcd_client_key"].string_value();
}
this->etcd_ca = config["etcd_ca"].string_value();
this->etcd_prefix = config["etcd_prefix"].string_value();
this->use_auth = config["use_auth"].bool_value();
if (this->etcd_prefix == "")
{
this->etcd_prefix = "/vitastor";
@@ -1339,6 +1384,10 @@ void etcd_state_client_t::parse_state(const etcd_kv_t & kv)
{
on_inode_change_hook(inode_num, true);
}
if (this->inode_config.find(inode_num) != this->inode_config.end())
{
user_perm_cache_revision = kv.mod_revision;
}
this->inode_config.erase(inode_num);
}
else
@@ -1354,13 +1403,39 @@ void etcd_state_client_t::parse_state(const etcd_kv_t & kv)
if (on_change_node_placement_hook)
on_change_node_placement_hook();
}
else if (use_auth && key.substr(0, etcd_prefix.length()+13) == etcd_prefix+"/config/user/")
else if (key.substr(0, etcd_prefix.length()+13) == etcd_prefix+"/config/user/")
{
// <etcd_prefix>/config/user/<username>
auto name = key.substr(etcd_prefix.length()+13);
auto & inf = user_info[name];
if (!value.is_object())
user_info.erase(key.substr(etcd_prefix.length()+13));
{
if (inf)
{
inf->type = user_type_t::CLIENT;
inf->groups.clear();
inf->perm_cache.clear();
}
user_info.erase(name);
}
else
user_info[key.substr(etcd_prefix.length()+13)] = value;
{
if (!inf)
{
inf = std::make_shared<user_info_t>();
inf->name = name;
}
inf->type = value["type"] == "admin" ? user_type_t::ADMIN :
(value["type"] == "mon" ? user_type_t::MON :
(value["type"] == "osd" ? user_type_t::OSD : user_type_t::CLIENT));
inf->groups.clear();
for (auto & group: value["groups"].array_items())
{
if (group.string_value() != "")
inf->groups.insert(group.string_value());
}
inf->perm_cache.clear();
}
}
}
@@ -1384,7 +1459,12 @@ uint32_t etcd_state_client_t::parse_scheme(const std::string & scheme)
void etcd_state_client_t::insert_inode_config(const inode_config_t & cfg)
{
this->inode_config[cfg.num] = cfg;
auto & cfg_ref = this->inode_config[cfg.num];
if (cfg_ref.mod_revision != cfg.mod_revision)
{
user_perm_cache_revision = cfg.mod_revision;
}
cfg_ref = cfg;
if (cfg.name != "")
{
this->inode_by_name[cfg.name] = cfg.num;
+31 -2
View File
@@ -9,6 +9,7 @@
#include "json11/json11.hpp"
#include "object_id.h"
#include "timerfd_manager.h"
#include "../util/robin_hood.h"
#define ETCD_CONFIG_WATCH_ID 1
#define ETCD_OSD_STATE_WATCH_ID 2
@@ -110,6 +111,32 @@ struct http_url_t
std::string path;
};
enum class user_type_t
{
CLIENT = 0,
ADMIN = 1,
MON = 2,
OSD = 3,
};
struct user_perm_t
{
enum class perm_type_t: uint8_t;
constexpr static perm_type_t DENY = (perm_type_t)0;
constexpr static perm_type_t READER = (perm_type_t)1;
constexpr static perm_type_t OWNER = (perm_type_t)2;
uint64_t mod_revision = 0;
perm_type_t perm = DENY;
};
struct user_info_t
{
std::string name;
user_type_t type;
robin_hood::unordered_flat_set<std::string> groups;
robin_hood::unordered_flat_map<inode_t, user_perm_t> perm_cache;
};
struct http_co_t;
struct http_context_t;
@@ -145,7 +172,6 @@ public:
int etcd_slow_timeout = 5000;
int etcd_min_reload_interval = 1000;
bool infinite_start = true;
bool use_auth = false;
uint64_t global_block_size = DEFAULT_BLOCK_SIZE;
uint32_t global_bitmap_granularity = DEFAULT_BITMAP_GRANULARITY;
uint32_t global_immediate_commit = IMMEDIATE_NONE;
@@ -171,7 +197,8 @@ public:
std::set<osd_num_t> seen_peers;
std::map<inode_t, inode_config_t> inode_config;
std::map<std::string, inode_t> inode_by_name;
std::map<std::string, json11::Json> user_info;
robin_hood::unordered_flat_map<std::string, std::shared_ptr<user_info_t>> user_info;
uint64_t user_perm_cache_revision = 0;
json11::Json node_placement;
std::function<void(std::map<std::string, etcd_kv_t> &)> on_change_hook;
@@ -193,6 +220,8 @@ public:
inode_config_t deserialize_inode_cfg(uint64_t inode_num, json11::Json value, uint64_t mod_revision);
etcd_kv_t parse_etcd_kv(const json11::Json & kv_json);
std::vector<std::string> get_addresses();
std::shared_ptr<user_info_t> get_user(const std::string & username);
bool check_image_perm(const std::shared_ptr<user_info_t> & user_info, inode_t inode_num, bool write);
http_context_t *get_http_ctx();
void etcd_call_oneshot(const std::string & etcd_address, const std::string & api, json11::Json payload, int timeout, std::function<void(std::string, json11::Json)> callback);
void etcd_call(const std::string & api, json11::Json payload, int timeout, int retries, int interval, std::function<void(std::string, json11::Json)> callback);
+1 -98
View File
@@ -19,6 +19,7 @@
#include <openssl/err.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include "openssl_util.h"
#endif
// libc-ares
@@ -163,104 +164,6 @@ void http_ares_cb(void *data, ares_socket_t socket_fd, int readable, int writabl
});
}
#ifdef WITH_OPENSSL
bool openssl_ctx_add_ca(SSL_CTX *ssl_ctx, const std::string & file_or_pem)
{
BIO *bio = NULL;
if (file_or_pem.substr(0, 5) != "-----")
{
std::string pem = read_file(file_or_pem);
bio = BIO_new_mem_buf(pem.data(), pem.size());
}
else
bio = BIO_new_mem_buf(file_or_pem.data(), file_or_pem.size());
if (!bio)
return false;
X509 *x509 = PEM_read_bio_X509(bio, NULL, 0, NULL);
bool ok = !!x509;
if (x509)
{
X509_STORE *store = SSL_CTX_get_cert_store(ssl_ctx);
X509_STORE_add_cert(store, x509);
X509_free(x509);
}
BIO_free(bio);
return ok;
}
bool openssl_ctx_use_ca(SSL_CTX *ssl_ctx, const std::string & file_or_pem)
{
if (file_or_pem.substr(0, 5) == "-----")
{
return openssl_ctx_add_ca(ssl_ctx, file_or_pem);
}
return file_or_pem.empty()
? !!SSL_CTX_set_default_verify_paths(ssl_ctx)
: !!SSL_CTX_load_verify_locations(ssl_ctx, file_or_pem.c_str(), NULL);
}
std::string openssl_get_cn(X509 *x509)
{
X509_NAME* subj = X509_get_subject_name(x509);
int pos = X509_NAME_get_index_by_NID(subj, NID_commonName, -1);
if (pos != -1)
{
X509_NAME_ENTRY* cn = X509_NAME_get_entry(subj, pos);
ASN1_STRING* str = X509_NAME_ENTRY_get_data(cn);
return std::string((const char*)ASN1_STRING_get0_data(str), ASN1_STRING_length(str));
}
return "";
}
bool openssl_ctx_use_cert(SSL_CTX *ssl_ctx, const std::string & file_or_pem, std::string & common_name)
{
BIO *bio = NULL;
std::string contents;
if (file_or_pem.substr(0, 5) == "-----")
bio = BIO_new_mem_buf(file_or_pem.data(), file_or_pem.size());
else
{
contents = read_file(file_or_pem);
if (!contents.size())
return false;
bio = BIO_new_mem_buf(contents.data(), contents.size());
}
if (!bio)
return false;
X509 *x509 = PEM_read_bio_X509(bio, NULL, 0, NULL);
bool ok = !!x509;
if (x509)
{
ok = SSL_CTX_use_certificate(ssl_ctx, x509);
if (ok)
common_name = openssl_get_cn(x509);
X509_free(x509);
}
BIO_free(bio);
return ok;
}
bool openssl_ctx_use_key(SSL_CTX *ssl_ctx, const std::string & file_or_pem)
{
if (file_or_pem.substr(0, 5) == "-----")
{
BIO *bio = BIO_new_mem_buf(file_or_pem.data(), file_or_pem.size());
if (!bio)
return false;
EVP_PKEY *pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
bool ok = !!pkey;
if (pkey)
{
ok = SSL_CTX_use_PrivateKey(ssl_ctx, pkey);
EVP_PKEY_free(pkey);
}
BIO_free(bio);
return ok;
}
return !!SSL_CTX_use_PrivateKey_file(ssl_ctx, file_or_pem.c_str(), SSL_FILETYPE_PEM);
}
#endif
http_context_t* http_context_init(timerfd_manager_t *tfd, const std::string & ssl_cert, const std::string & ssl_key,
const std::string & ssl_ca, bool verify_peer, std::string & error)
{
-12
View File
@@ -8,10 +8,6 @@
#include <functional>
#include "json11/json11.hpp"
#ifdef WITH_OPENSSL
#include <openssl/types.h>
#endif
#define WS_CONTINUATION 0
#define WS_TEXT 1
#define WS_BINARY 2
@@ -73,11 +69,3 @@ void http_close(http_co_t *co);
void http_destroy(http_co_t *co);
#pragma GCC visibility pop
#ifdef WITH_OPENSSL
bool openssl_ctx_add_ca(SSL_CTX *ssl_ctx, const std::string & file_or_pem);
bool openssl_ctx_use_ca(SSL_CTX *ssl_ctx, const std::string & file_or_pem);
std::string openssl_get_cn(X509 *x509);
bool openssl_ctx_use_cert(SSL_CTX *ssl_ctx, const std::string & file_or_pem, std::string & common_name);
bool openssl_ctx_use_key(SSL_CTX *ssl_ctx, const std::string & file_or_pem);
#endif
+121 -134
View File
@@ -10,17 +10,11 @@
#include <stdexcept>
#include "addr_util.h"
#include "str_util.h"
#include "messenger.h"
#ifdef WITH_RDMA
#include "msgr_rdma.h"
#endif
#include "http_client.h"
#ifdef WITH_OPENSSL
#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#endif
#include <sys/poll.h>
@@ -124,43 +118,7 @@ void msgr_iothread_t::run()
void osd_messenger_t::init()
{
if (!tls_cert.empty() || !tls_key.empty() || !osd_tls_ca.empty() || !client_tls_ca.empty())
{
// Initialize TLS context
// FIXME: require OpenSSL
#ifndef WITH_OPENSSL
fprintf(stderr, "Vitastor is built without OpenSSL support\n");
exit(1);
#else
if (tls_cert.empty() || tls_key.empty() || osd_tls_ca.empty() || osd_num && client_tls_ca.empty())
{
if (osd_num)
fprintf(stderr, "Vitastor OSD TLS requires osd_tls_cert, osd_tls_key, osd_tls_ca, client_tls_ca\n");
else
fprintf(stderr, "Vitastor client TLS requires tls_cert, tls_key and osd_tls_ca\n");
exit(1);
}
else
{
ssl_ctx = SSL_CTX_new(TLS_method());
SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL);
bool ok = SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_3_VERSION);
ok = ok && openssl_ctx_add_ca(ssl_ctx, osd_tls_ca);
if (osd_num)
{
// OSD uses 2 separate root certificates to distinguish between clients and peer OSDs
ok = ok && openssl_ctx_add_ca(ssl_ctx, client_tls_ca);
}
ok = ok && openssl_ctx_use_cert(ssl_ctx, tls_cert, tls_cn);
ok = ok && openssl_ctx_use_key(ssl_ctx, tls_key);
if (!ok)
{
SSL_CTX_free(ssl_ctx);
ssl_ctx = NULL;
}
}
#endif
}
init_tls();
#ifdef WITH_RDMACM
if (use_rdmacm)
{
@@ -233,9 +191,12 @@ void osd_messenger_t::init()
{
auto cl = cl_it->second;
cl_it++;
if (!cl->osd_num && !cl->in_osd_num || cl->peer_state != PEER_CONNECTED && cl->peer_state != PEER_RDMA)
if (!cl->osd_num && !cl->in_osd_num ||
cl->peer_state != PEER_CONNECTED &&
cl->peer_state != PEER_RDMA_CONNECTING_OUT &&
cl->peer_state != PEER_RDMA)
{
// Do not run keepalive on regular clients
// Do not run keepalive on regular and unconnected clients
continue;
}
if (cl->ping_time_remaining > 0)
@@ -243,10 +204,21 @@ void osd_messenger_t::init()
cl->ping_time_remaining--;
if (!cl->ping_time_remaining)
{
// Ping timed out, stop the client
fprintf(stderr, "Ping timed out for OSD %ju (client %ju), disconnecting peer\n",
cl->in_osd_num ? cl->in_osd_num : cl->osd_num, cl->client_id);
clients_to_stop.push_back(cl->client_id);
// Ping timed out
if (cl->peer_state == PEER_RDMA_CONNECTING_OUT && cl->peer_fd >= 0)
{
fprintf(stderr, "Ping timed out for OSD %ju over RDMA (client %ju), switching back to TCP\n",
cl->in_osd_num ? cl->in_osd_num : cl->osd_num, cl->client_id);
delete cl->rdma_conn;
cl->rdma_conn = NULL;
cl->peer_state = PEER_CONNECTED;
}
else
{
fprintf(stderr, "Ping timed out for OSD %ju (client %ju), disconnecting peer\n",
cl->in_osd_num ? cl->in_osd_num : cl->osd_num, cl->client_id);
clients_to_stop.push_back(cl->client_id);
}
}
}
else if (cl->idle_time_remaining > 0)
@@ -264,7 +236,7 @@ void osd_messenger_t::init()
.opcode = OSD_OP_PING,
},
};
op->callback = [this, cl](osd_op_t *op)
op->callback = [this](osd_op_t *op)
{
auto cl_it = clients.find(op->client_id);
if (cl_it == clients.end())
@@ -273,14 +245,25 @@ void osd_messenger_t::init()
delete op;
return;
}
uint64_t fail_client_id = (op->reply.hdr.retval != 0 ? op->client_id : 0);
auto fail_osd_num = cl->in_osd_num ? cl->in_osd_num : cl->osd_num;
auto cl = cl_it->second;
bool failed = (op->reply.hdr.retval != 0);
cl->ping_time_remaining = 0;
delete op;
if (fail_client_id)
if (failed)
{
fprintf(stderr, "Ping failed for OSD %ju (client %ju), disconnecting peer\n", fail_osd_num, fail_client_id);
stop_client(fail_client_id);
auto fail_osd_num = cl->in_osd_num ? cl->in_osd_num : cl->osd_num;
if (cl->peer_state == PEER_RDMA_CONNECTING_OUT && cl->peer_fd >= 0)
{
fprintf(stderr, "Ping failed for OSD %ju over RDMA (client %ju), switching back to TCP\n", fail_osd_num, cl->client_id);
delete cl->rdma_conn;
cl->rdma_conn = NULL;
cl->peer_state = PEER_CONNECTED;
}
else
{
fprintf(stderr, "Ping failed for OSD %ju (client %ju), disconnecting peer\n", fail_osd_num, cl->client_id);
stop_client(cl->client_id);
}
}
};
cl->ping_time_remaining = osd_ping_timeout;
@@ -339,25 +322,52 @@ osd_messenger_t::~osd_messenger_t()
rdmacm_evch = NULL;
}
#endif
for (auto encrypt_ctx: encrypt_ctx_pool)
for (auto encrypt_ctx: encrypt_xts_pool)
{
destroy_aes_xts_encrypt(encrypt_ctx);
}
for (auto decrypt_ctx: decrypt_ctx_pool)
for (auto decrypt_ctx: decrypt_xts_pool)
{
destroy_aes_xts_decrypt(decrypt_ctx);
}
#ifdef WITH_OPENSSL
if (ssl_ctx)
{
SSL_CTX_free(ssl_ctx);
ssl_ctx = NULL;
}
#endif
destroy_tls();
}
void osd_messenger_t::parse_config(const json11::Json & config)
void osd_messenger_t::parse_config(const json11::Json & config, bool init)
{
this->max_cipher_pool_size = config["max_cipher_pool_size"].uint64_value();
if (!this->max_cipher_pool_size)
this->max_cipher_pool_size = 256;
if (config["proto_checksums"].is_null())
this->use_proto_checksums = MSGR_CSUM_PAYLOAD;
else if (config["proto_checksums"].is_bool())
this->use_proto_checksums = config["proto_checksums"].bool_value() ? MSGR_CSUM_FULL : 0;
else if (config["proto_checksums"].string_value() != "")
this->use_proto_checksums = config["proto_checksums"].string_value() == "full" ? MSGR_CSUM_FULL : MSGR_CSUM_PAYLOAD;
else
this->use_proto_checksums = 0;
this->receive_buffer_size = (uint32_t)config["tcp_header_buffer_size"].uint64_value();
if (!this->receive_buffer_size || this->receive_buffer_size > 1024*1024*1024)
this->receive_buffer_size = 65536;
this->min_zerocopy_send_size = config["min_zerocopy_send_size"].is_null()
? DEFAULT_MIN_ZEROCOPY_SEND_SIZE
: (int)config["min_zerocopy_send_size"].int64_value();
this->peer_connect_interval = config["peer_connect_interval"].uint64_value();
if (!this->peer_connect_interval)
this->peer_connect_interval = 5;
this->peer_connect_timeout = config["peer_connect_timeout"].uint64_value();
if (!this->peer_connect_timeout)
this->peer_connect_timeout = 5;
this->osd_idle_timeout = config["osd_idle_timeout"].uint64_value();
if (!this->osd_idle_timeout)
this->osd_idle_timeout = 5;
this->osd_ping_timeout = config["osd_ping_timeout"].uint64_value();
if (!this->osd_ping_timeout)
this->osd_ping_timeout = 5;
this->log_level = config["log_level"].uint64_value();
// All other parameters are only set on init
if (!init)
return;
#ifdef WITH_RDMA
if (!config["use_rdma"].is_null())
{
@@ -388,55 +398,25 @@ void osd_messenger_t::parse_config(const json11::Json & config)
if (!this->rdma_max_msg || this->rdma_max_msg > 128*1024*1024)
this->rdma_max_msg = 129*1024;
#endif
this->max_aes_xts_pool_size = config["max_aes_xts_pool_size"].uint64_value();
if (!this->max_aes_xts_pool_size)
this->max_aes_xts_pool_size = 256;
if (config["proto_checksums"].is_null())
this->use_proto_checksums = MSGR_CSUM_PAYLOAD;
else if (config["proto_checksums"].is_bool())
this->use_proto_checksums = config["proto_checksums"].bool_value() ? MSGR_CSUM_FULL : 0;
else if (config["proto_checksums"].string_value() != "")
this->use_proto_checksums = config["proto_checksums"].string_value() == "full" ? MSGR_CSUM_FULL : MSGR_CSUM_PAYLOAD;
else
this->use_proto_checksums = 0;
if (!osd_num)
{
tls_cert = config["tls_cert"].string_value();
tls_key = config["tls_key"].string_value();
osd_tls_ca = config["osd_tls_ca"].string_value();
tls_cert = config["cert"].string_value();
tls_key = config["pkey"].string_value();
osd_tls_ca = config["osd_ca"].string_value();
}
else
{
tls_cert = config["osd_tls_cert"].string_value();
tls_key = config["osd_tls_key"].string_value();
osd_tls_ca = config["osd_tls_ca"].string_value();
client_tls_ca = config["client_tls_ca"].string_value();
tls_cert = config["osd_cert"].string_value();
tls_key = config["osd_pkey"].string_value();
osd_tls_ca = config["osd_ca"].string_value();
client_tls_ca = config["client_ca"].string_value();
}
if (!osd_num)
this->iothread_count = (uint32_t)config["client_iothread_count"].uint64_value();
else
this->iothread_count = (uint32_t)config["osd_iothread_count"].uint64_value();
this->receive_buffer_size = (uint32_t)config["tcp_header_buffer_size"].uint64_value();
if (!this->receive_buffer_size || this->receive_buffer_size > 1024*1024*1024)
this->receive_buffer_size = 65536;
this->use_sync_send_recv = config["use_sync_send_recv"].bool_value() ||
config["use_sync_send_recv"].uint64_value();
this->min_zerocopy_send_size = config["min_zerocopy_send_size"].is_null()
? DEFAULT_MIN_ZEROCOPY_SEND_SIZE
: (int)config["min_zerocopy_send_size"].int64_value();
this->peer_connect_interval = config["peer_connect_interval"].uint64_value();
if (!this->peer_connect_interval)
this->peer_connect_interval = 5;
this->peer_connect_timeout = config["peer_connect_timeout"].uint64_value();
if (!this->peer_connect_timeout)
this->peer_connect_timeout = 5;
this->osd_idle_timeout = config["osd_idle_timeout"].uint64_value();
if (!this->osd_idle_timeout)
this->osd_idle_timeout = 5;
this->osd_ping_timeout = config["osd_ping_timeout"].uint64_value();
if (!this->osd_ping_timeout)
this->osd_ping_timeout = 5;
this->log_level = config["log_level"].uint64_value();
config["use_sync_send_recv"].uint64_value() || !ringloop;
// OSD public & cluster networks
this->osd_networks.clear();
if (config["osd_network"].is_string())
@@ -642,10 +622,7 @@ void osd_messenger_t::handle_connect_epoll(int peer_fd)
handle_peer_epoll(peer_fd, epoll_events);
});
// Check OSD number
if (!tls_cert.empty())
{
ssl_init(cl, false);
}
init_tls_client(cl);
check_peer_config(cl);
}
@@ -656,6 +633,17 @@ void osd_messenger_t::handle_peer_epoll(int peer_fd, int epoll_events)
if (epoll_events & EPOLLRDHUP)
{
// Stop client
if (cl->rdma_close_tcp && (cl->peer_state == PEER_RDMA ||
cl->peer_state == PEER_RDMA_CONNECTING_IN ||
cl->peer_state == PEER_RDMA_CONNECTING_OUT))
{
// It's allowed to stop the TCP socket during RDMA transition
tfd->set_fd_handler(cl->peer_fd, false, NULL);
clients_by_fd.erase(cl->peer_fd);
close(cl->peer_fd);
cl->peer_fd = -1;
return;
}
if (log_level > 0)
{
fprintf(stderr, "[OSD %ju] client %ju disconnected\n", this->osd_num, cl->client_id);
@@ -735,7 +723,10 @@ void osd_messenger_t::check_peer_config(osd_client_t *cl)
// Inform that we're OSD <osd_num>
payload["osd_num"] = osd_num;
}
auto features = json11::Json::object{ { "check_sequencing", true } };
auto features = json11::Json::object{
{ "check_sequencing", true },
{ "rdma_close_tcp", true },
};
if (use_proto_checksums)
{
features["proto_checksums"] = use_proto_checksums;
@@ -780,6 +771,11 @@ void osd_messenger_t::check_peer_config(osd_client_t *cl)
err = true;
fprintf(stderr, "Failed to get config from OSD %ju (retval=%jd), disconnecting peer\n", cl->osd_num, op->reply.hdr.retval);
}
else if (cl->gcm_enabled && !cl->hs_result.peer_is_osd)
{
err = true;
fprintf(stderr, "Client %ju is not authenticated as an OSD, disconnecting peer\n", cl->client_id);
}
else
{
config = json11::Json::parse(std::string((char*)op->buf), json_err);
@@ -824,6 +820,10 @@ void osd_messenger_t::check_peer_config(osd_client_t *cl)
cl->proto_csum_status = MSGR_CSUM_PAYLOAD;
}
#ifdef WITH_RDMA
if (config["features"]["rdma_close_tcp"].bool_value())
{
cl->rdma_close_tcp = true;
}
if (!use_rdmacm && cl->rdma_conn && config["rdma_address"].is_string())
{
msgr_rdma_address_t addr;
@@ -848,9 +848,14 @@ void osd_messenger_t::check_peer_config(osd_client_t *cl)
{
fprintf(stderr, "Connected to OSD %ju using RDMA\n", cl->osd_num);
}
cl->peer_state = PEER_RDMA;
cl->peer_state = (cl->rdma_close_tcp ? PEER_RDMA_CONNECTING_OUT : PEER_RDMA);
// Add the initial receive request
init_recv_rdma(cl);
// Check the connection by using a ping
cl->ping_time_remaining = osd_ping_timeout;
cl->idle_time_remaining = 0;
delete op;
return;
}
}
#endif
@@ -887,10 +892,7 @@ void osd_messenger_t::accept_connections(int listen_fd)
cl->peer_fd = peer_fd;
cl->peer_state = PEER_CONNECTED;
cl->in_buf = (uint8_t*)malloc_or_die(receive_buffer_size);
if (!tls_cert.empty())
{
ssl_init(cl, true);
}
init_tls_client(cl);
// Add FD to epoll
tfd->set_fd_handler(peer_fd, false, [this](int peer_fd, int epoll_events)
{
@@ -905,26 +907,6 @@ void osd_messenger_t::accept_connections(int listen_fd)
}
}
void osd_messenger_t::ssl_init(osd_client_t *cl, bool server_mode)
{
#ifdef WITH_OPENSSL
cl->write_to_ssl = BIO_new(BIO_s_mem());
cl->read_from_ssl = BIO_new(BIO_s_mem());
cl->ssl_cli = SSL_new(ssl_ctx);
if (server_mode)
{
SSL_set_accept_state(cl->ssl_cli);
}
else
{
SSL_set_connect_state(cl->ssl_cli);
}
SSL_set_bio(cl->ssl_cli, cl->write_to_ssl, cl->read_from_ssl);
bool ok = ssl_do_handshake(cl);
assert(ok);
#endif
}
#ifdef WITH_RDMA
msgr_rdma_context_t* osd_messenger_t::choose_rdma_context(osd_client_t *cl)
{
@@ -953,6 +935,11 @@ bool osd_messenger_t::is_use_rdmacm()
}
#endif
bool osd_messenger_t::is_encryption_enabled()
{
return tls_cert != "" || tls_key != "" || osd_tls_ca != "";
}
json11::Json::object osd_messenger_t::read_config(const json11::Json & config)
{
json11::Json::object file_config;
+81 -44
View File
@@ -12,8 +12,10 @@
#include <deque>
#include <vector>
#ifdef WITH_OPENSSL
#include <openssl/types.h>
#ifdef WITH_ISAL_CRYPTO
#include <isa-l_crypto/aes_gcm.h>
#endif
#include "../util/xxh_x86dispatch.h"
@@ -23,6 +25,7 @@
#include "msgr_op.h"
#include "timerfd_manager.h"
#include "addr_util.h"
#include "msgr_handshake.h"
#include <ringloop.h>
#define CL_READ_HDR 1
@@ -32,9 +35,10 @@
#define PEER_CONNECTING 1
#define PEER_CONNECTED 2
#define PEER_RDMA_CONNECTING 3
#define PEER_RDMA 4
#define PEER_STOPPED 5
#define PEER_RDMA_CONNECTING_IN 3
#define PEER_RDMA_CONNECTING_OUT 4
#define PEER_RDMA 5
#define PEER_STOPPED 6
#define MSGR_CSUM_PAYLOAD 1
#define MSGR_CSUM_FULL 2
@@ -44,6 +48,11 @@
#define DEFAULT_MIN_ZEROCOPY_SEND_SIZE 32*1024
#define AES_256_GCM_KEY_SIZE 32
#define AES_256_GCM_IV_SIZE 12
#define MAX_SIMPLE_PAYLOAD_SIZE 1048576
struct msgr_sendp_t
{
osd_op_t *op;
@@ -60,11 +69,7 @@ struct op_aes_xts_decrypt_t;
void destroy_aes_xts_encrypt(op_aes_xts_encrypt_t *encrypt_ctx);
void destroy_aes_xts_decrypt(op_aes_xts_decrypt_t *decrypt_ctx);
struct __attribute__((__packed__)) msgr_tls_record_hdr_t
{
uint8_t encrypted;
uint32_t size;
};
struct user_info_t;
struct osd_client_t
{
@@ -88,18 +93,30 @@ struct osd_client_t
msgr_rdma_connection_t *rdma_conn = NULL;
#endif
#ifdef WITH_OPENSSL
SSL *ssl_cli = NULL;
BIO *write_to_ssl = NULL;
// FIXME: use custom bio to avoid 1 more memory copy?
BIO *read_from_ssl = NULL;
uint8_t *ssl_out_buf = NULL;
size_t ssl_out_buf_size = 0, ssl_out_buf_cap = 0;
bool ssl_handshake_done = false;
bool ssl_want_write = false;
bool gcm_enabled = false;
msgr_handshake_i *hs = NULL;
msgr_handshake_result_t hs_result;
std::shared_ptr<user_info_t> user_info;
std::vector<uint8_t> my_key, peer_key;
uint64_t my_iv_ctr = 0, peer_iv_ctr = 0;
#ifdef WITH_ISAL_CRYPTO
isal_gcm_key_data my_key_isal, peer_key_isal;
isal_gcm_context_data *enc_ctx = NULL;
isal_gcm_context_data *dec_ctx = NULL;
#else
EVP_CIPHER_CTX *enc_ctx = NULL;
EVP_CIPHER_CTX *dec_ctx = NULL;
#endif
uint8_t enc_tag[16];
size_t enc_tag_size = 0;
uint8_t dec_tag[16];
size_t dec_tag_size = 0;
// Read state
bool io_error = false;
int read_ready = 0;
osd_op_t *read_op = NULL;
size_t read_op_size = 0;
@@ -107,11 +124,12 @@ struct osd_client_t
iovec read_iov = { 0 };
msghdr read_msg = { 0 };
std::vector<iovec> recv_list;
size_t recv_list_size = 0;
std::vector<int> recv_flags;
uint64_t read_op_id = 1;
bool check_sequencing = false;
bool rdma_close_tcp = false;
bool enable_pg_locks = false;
op_aes_xts_decrypt_t *decrypt_ctx = NULL;
op_aes_xts_decrypt_t *xts_dec_ctx = NULL;
size_t read_op_inline_decrypt_pos = 0;
size_t read_op_inline_decrypt_in = 0;
int proto_csum_status = 0;
@@ -137,7 +155,7 @@ struct osd_client_t
size_t send_list_size = 0;
std::deque<osd_op_t*> send_free_ops;
std::vector<osd_op_t*> zc_free_list;
op_aes_xts_encrypt_t *encrypt_ctx = NULL;
op_aes_xts_encrypt_t *xts_enc_ctx = NULL;
XXH3_state_t* write_csum_state = NULL;
~osd_client_t();
@@ -216,9 +234,19 @@ struct osd_messenger_t;
struct rdmacm_connecting_t;
#endif
class msgr_op_reader_t;
class msgr_op_writer_t;
struct __attribute__((visibility("default"))) osd_messenger_t
{
protected:
friend class copy_op_reader_t;
friend class gcm_op_reader_t;
friend class get_op_reader_t;
friend class copy_op_writer_t;
friend class gcm_op_writer_t;
friend class get_op_writer_t;
int keepalive_timer_id = -1;
uint32_t receive_buffer_size = 0;
@@ -230,7 +258,7 @@ protected:
bool use_sync_send_recv = false;
int min_zerocopy_send_size = DEFAULT_MIN_ZEROCOPY_SEND_SIZE;
int iothread_count = 0;
int max_aes_xts_pool_size = 256;
int max_cipher_pool_size = 256;
std::string tls_cert;
std::string tls_key;
@@ -253,18 +281,14 @@ protected:
robin_hood::unordered_flat_map<rdma_cm_id*, rdmacm_connecting_t*> rdmacm_connecting;
#endif
#ifdef WITH_OPENSSL
SSL_CTX *ssl_ctx = NULL;
std::string tls_cn;
bool gcm_enabled = false;
msgr_handshake_ctx_i *hs_ctx = NULL;
void ssl_init(osd_client_t *cl, bool server_mode);
bool ssl_do_handshake(osd_client_t *cl);
bool ssl_do_encrypt(osd_client_t *cl);
size_t ssl_do_encrypt_to(osd_client_t *cl, uint8_t *buf, size_t size);
bool ssl_op_write_buf(osd_client_t *cl, uint8_t *src, size_t src_len, bool skip_csum, size_t & from, size_t & done);
size_t ssl_op_copy_to(osd_client_t *cl, uint8_t *dst, size_t dst_len);
void ssl_op_get_write_buffers(osd_client_t *cl, std::vector<iovec> & lst);
#endif
void init_tls();
void destroy_tls();
void init_tls_client(osd_client_t *cl);
bool do_tls_handshake(osd_client_t *cl, bool from_recv = false);
bool derive_aes_keys(osd_client_t *cl, bool update_my, bool update_peer);
std::vector<msgr_iothread_t*> iothreads;
std::vector<uint64_t> read_ready_clients;
@@ -272,16 +296,27 @@ protected:
// We don't use ringloop->set_immediate here because we may have no ringloop in client :)
std::deque<osd_op_t*> set_immediate_ops;
std::vector<op_aes_xts_encrypt_t*> encrypt_ctx_pool;
std::vector<op_aes_xts_decrypt_t*> decrypt_ctx_pool;
std::vector<op_aes_xts_encrypt_t*> encrypt_xts_pool;
std::vector<op_aes_xts_decrypt_t*> decrypt_xts_pool;
#ifdef WITH_ISAL_CRYPTO
std::vector<isal_gcm_context_data*> encrypt_gcm_pool;
std::vector<isal_gcm_context_data*> decrypt_gcm_pool;
#else
std::vector<EVP_CIPHER_CTX*> encrypt_gcm_pool;
std::vector<EVP_CIPHER_CTX*> decrypt_gcm_pool;
#endif
public:
timerfd_manager_t *tfd = NULL;
ring_loop_t *ringloop = NULL;
bool has_sendmsg_zc = false;
// osd_num_t is only for logging and asserts
uint64_t next_client_id = 1;
osd_num_t osd_num;
// osd_num = 0 for client messenger, osd_num > 0 for OSD messenger
osd_num_t osd_num = 0;
uint32_t clean_entry_bitmap_size = 0;
uint32_t bs_block_size = 0;
uint32_t max_write_request_size = 0;
robin_hood::unordered_flat_map<uint64_t, osd_client_t*> clients;
robin_hood::unordered_flat_map<uint64_t, osd_client_t*> osd_peers;
robin_hood::unordered_flat_map<int, osd_client_t*> clients_by_fd;
@@ -297,7 +332,7 @@ public:
osd_op_stats_t stats, recovery_stats;
void init();
void parse_config(const json11::Json & config);
void parse_config(const json11::Json & config, bool init);
void connect_peer(uint64_t osd_num, json11::Json peer_state);
void stop_client(uint64_t client_id, bool force_delete = false);
void destroy_client(osd_client_t *cl);
@@ -306,6 +341,7 @@ public:
std::function<void(osd_num_t)> repeer_pgs;
std::function<void(osd_num_t)> break_pg_locks;
std::function<bool(osd_client_t*, json11::Json)> check_config_hook;
std::function<void(osd_client_t*)> handshake_hook;
void read_requests();
void send_replies();
void accept_connections(int listen_fd);
@@ -326,6 +362,7 @@ public:
rdma_cm_id *rdmacm_listen(const std::string & bind_address, int rdmacm_port, int *bound_port, int log_level);
void rdmacm_destroy_listener(rdma_cm_id *listener);
#endif
bool is_encryption_enabled();
void inc_op_stats(osd_op_stats_t & stats, uint64_t opcode, timespec & tv_begin, timespec & tv_end, uint64_t len);
void measure_exec(osd_op_t *cur_op);
@@ -342,32 +379,32 @@ protected:
bool try_send(osd_client_t *cl);
void handle_send(int result, bool prev, bool more, osd_client_t *cl);
size_t op_copy_to(osd_client_t *cl, uint8_t *dst, size_t dst_len);
void op_get_write_buffers(osd_client_t *cl, std::vector<iovec> & lst);
bool op_write_to(osd_client_t *cl, msgr_op_writer_t & wr);
void next_write_op(osd_client_t *cl);
bool op_write_buf(osd_client_t *cl, uint8_t *src, size_t src_len, uint8_t *dst, size_t dst_len, bool skip_csum, size_t & from, size_t & done);
bool op_copy_data_to(osd_client_t *cl, uint8_t *dst, size_t dst_len, size_t & from, size_t & done);
size_t copy_ops_to(osd_client_t *cl, uint8_t *dst, size_t dst_len);
template<typename T> size_t copy_ops_to_with(osd_client_t *cl, uint8_t *dst, size_t dst_len);
void handle_read(int result, osd_client_t *cl);
bool handle_read_buffer(osd_client_t *cl, uint8_t *curbuf, size_t bufsize);
template<typename T> bool handle_buffer_with(osd_client_t *cl, uint8_t *curbuf, size_t bufsize);
bool handle_hdr(osd_client_t *cl);
bool allocate_op_buffers(osd_client_t *cl);
bool allocate_reply_buffers(osd_client_t *cl, osd_op_t *op);
bool op_copy_from(osd_client_t *cl, uint8_t *src, size_t src_len, size_t & done);
void op_get_read_buffers(osd_client_t *cl, std::vector<iovec> & lst);
void op_alloc_temp_buffers(osd_op_t *op, int i);
bool op_read_from(osd_client_t *cl, msgr_op_reader_t & rdr);
bool handle_finished_op(osd_client_t *cl);
void handle_immediate_ops();
bool op_encrypted_copy_data_to(osd_client_t* cl, uint8_t *buf, size_t len, size_t from, size_t & done);
bool op_decrypted_copy_data_from(osd_client_t* cl, uint8_t *buf, size_t len, size_t from, size_t & done);
void op_encrypted_copy_buf(osd_client_t *cl, uint8_t *enc_buf, size_t enc_len, uint8_t *plain, size_t plain_len, size_t & done_plain, size_t & done_enc);
void op_encrypt_free(osd_client_t* cl);
void op_decrypted_copy_buf(osd_client_t *cl, uint8_t *enc_buf, size_t enc_len, uint8_t *plain, size_t plain_len, size_t & done_plain, size_t & done_enc);
void op_decrypt_start(osd_client_t* cl);
void op_decrypt_inline(osd_client_t* cl);
void op_decrypt_free(osd_client_t* cl);
#ifdef WITH_RDMA
void try_send_rdma(osd_client_t *cl);
int try_send_rdma_copy(osd_client_t *cl, uint8_t *dst, int dst_len);
bool init_recv_rdma(osd_client_t *cl);
void handle_rdma_events(msgr_rdma_context_t *rdma_context);
msgr_rdma_context_t* choose_rdma_context(osd_client_t *cl);
+228 -111
View File
@@ -3,13 +3,23 @@
#include <assert.h>
#ifdef WITH_ISAL_CRYPTO
#include <isa-l_crypto/isal_crypto_api.h>
#endif
#include "str_util.h"
#include "etcd_state_client.h"
#include "messenger.h"
#include "msgr_encrypt.h"
#include "http_client.h"
#include "openssl_util.h"
#include <openssl/ssl.h>
#include <openssl/err.h>
op_aes_xts_encrypt_t::op_aes_xts_encrypt_t()
{
#ifdef WITH_OPENSSL
#ifndef WITH_ISAL_CRYPTO
if (!(ctx = EVP_CIPHER_CTX_new()))
{
ERR_print_errors_fp(stderr);
@@ -21,16 +31,13 @@ op_aes_xts_encrypt_t::op_aes_xts_encrypt_t()
ERR_print_errors_fp(stderr);
abort();
}
#else
fprintf(stderr, "Error: Vitastor is built without encryption support\n");
abort();
#endif
}
op_aes_xts_encrypt_t::~op_aes_xts_encrypt_t()
{
assert(!encrypted);
#ifdef WITH_OPENSSL
#ifndef WITH_ISAL_CRYPTO
EVP_CIPHER_CTX_free(ctx);
#endif
if (tmp)
@@ -52,7 +59,7 @@ void op_aes_xts_encrypt_t::start(uint8_t *key, uint64_t start_offset, size_t blo
tmp = NULL;
tmp_size = 0;
}
#ifdef WITH_OPENSSL
#ifndef WITH_ISAL_CRYPTO
if (EVP_EncryptInit_ex(ctx, NULL, NULL, key, NULL) != 1)
{
ERR_print_errors_fp(stderr);
@@ -63,9 +70,12 @@ void op_aes_xts_encrypt_t::start(uint8_t *key, uint64_t start_offset, size_t blo
void op_aes_xts_encrypt_t::encrypt_block(uint8_t *in, uint8_t *out)
{
#ifdef WITH_OPENSSL
uint8_t iv[16] = { 0 };
*((uint64_t*)iv) = start_offset + offset - offset%block_size;
#ifdef WITH_ISAL_CRYPTO
int r = isal_aes_xts_enc_256(key+32, key, iv, block_size, in, out);
assert(r == 0 || r == ISAL_CRYPTO_ERR_XTS_SAME_KEYS);
#else
if (EVP_EncryptInit_ex(ctx, NULL, NULL, NULL, iv) != 1)
{
ERR_print_errors_fp(stderr);
@@ -98,7 +108,10 @@ void op_aes_xts_encrypt_t::update(uint8_t *in, size_t max_in, uint8_t *out, size
done_out += max_out;
tmp_pos += max_out;
if (tmp_pos >= block_size)
{
encrypted = false;
done_in += 1;
}
}
else if (max_in < block_size - offset%block_size)
{
@@ -126,7 +139,7 @@ void op_aes_xts_encrypt_t::update(uint8_t *in, size_t max_in, uint8_t *out, size
encrypted = true;
memcpy(out, tmp, max_out);
tmp_pos = max_out;
done_in += max_in;
done_in += max_in-1;
offset += max_in;
done_out += max_out;
}
@@ -158,7 +171,7 @@ void destroy_aes_xts_encrypt(op_aes_xts_encrypt_t *encrypt_ctx)
op_aes_xts_decrypt_t::op_aes_xts_decrypt_t()
{
#ifdef WITH_OPENSSL
#ifndef WITH_ISAL_CRYPTO
if (!(ctx = EVP_CIPHER_CTX_new()))
{
ERR_print_errors_fp(stderr);
@@ -170,29 +183,27 @@ op_aes_xts_decrypt_t::op_aes_xts_decrypt_t()
ERR_print_errors_fp(stderr);
abort();
}
#else
fprintf(stderr, "Error: Vitastor is built without encryption support\n");
abort();
#endif
}
op_aes_xts_decrypt_t::~op_aes_xts_decrypt_t()
{
assert(!decrypted);
#ifdef WITH_OPENSSL
#ifndef WITH_ISAL_CRYPTO
EVP_CIPHER_CTX_free(ctx);
#endif
if (tmp)
free(tmp);
}
void op_aes_xts_decrypt_t::start(uint8_t **key_chain, size_t chain_size, uint8_t *key_indexes, uint64_t start_offset, size_t block_size)
void op_aes_xts_decrypt_t::start(uint8_t **key_chain, size_t chain_size, void *key_indexes, uint64_t start_offset, size_t block_size)
{
assert(!decrypted);
this->start_offset = start_offset;
this->key_chain = chain_size > 1 ? key_chain : 0;
this->chain_size = chain_size > 1 ? chain_size : 0;
this->key_indexes = chain_size > 1 ? key_indexes : NULL;
this->key_chain = key_chain;
this->chain_size = chain_size;
this->key_indexes = key_indexes;
this->key_index_bytes = osd_op_rw_t::chain_info_bytes(chain_size);
assert(chain_size <= 1 || key_indexes != NULL);
this->block_size = block_size;
this->offset = 0;
@@ -203,7 +214,7 @@ void op_aes_xts_decrypt_t::start(uint8_t **key_chain, size_t chain_size, uint8_t
tmp = NULL;
tmp_size = 0;
}
#ifdef WITH_OPENSSL
#ifndef WITH_ISAL_CRYPTO
if (chain_size == 1 && key_chain[0] && EVP_DecryptInit_ex(ctx, NULL, NULL, key_chain[0], NULL) != 1)
{
ERR_print_errors_fp(stderr);
@@ -215,21 +226,35 @@ void op_aes_xts_decrypt_t::start(uint8_t **key_chain, size_t chain_size, uint8_t
void op_aes_xts_decrypt_t::decrypt_block(uint8_t *in, uint8_t *out)
{
uint8_t *key = NULL;
if (chain_size)
if (chain_size > 1)
{
assert(key_indexes[offset/block_size] < chain_size);
key = key_chain[key_indexes[offset/block_size]];
if (!key)
{
if (in != out)
memcpy(out, in, block_size);
return;
}
uint32_t key_index = key_index_bytes == 1
? ((uint8_t*)key_indexes)[offset/block_size]
: (key_index_bytes == 2
? ((uint16_t*)key_indexes)[offset/block_size]
: (key_index_bytes == 4
? ((uint32_t*)key_indexes)[offset/block_size]
: UINT32_MAX));
assert(key_index < chain_size);
key = key_chain[key_index];
}
else
{
key = key_chain[0];
}
if (!key)
{
if (in != out)
memcpy(out, in, block_size);
return;
}
#ifdef WITH_OPENSSL
uint8_t iv[16] = { 0 };
*((uint64_t*)iv) = start_offset + offset - offset%block_size;
if (EVP_DecryptInit_ex(ctx, NULL, NULL, key, iv) != 1)
#ifdef WITH_ISAL_CRYPTO
int r = isal_aes_xts_dec_256(key+32, key, iv, block_size, in, out);
assert(r == 0 || r == ISAL_CRYPTO_ERR_XTS_SAME_KEYS);
#else
if (EVP_DecryptInit_ex(ctx, NULL, NULL, chain_size == 1 ? NULL : key, iv) != 1)
{
ERR_print_errors_fp(stderr);
abort();
@@ -264,7 +289,10 @@ void op_aes_xts_decrypt_t::update(uint8_t *in, size_t max_in, uint8_t *out, size
done_out += max_out;
tmp_pos += max_out;
if (tmp_pos >= block_size)
{
decrypted = false;
done_in += 1;
}
}
else if (max_in < block_size - offset%block_size)
{
@@ -293,7 +321,7 @@ void op_aes_xts_decrypt_t::update(uint8_t *in, size_t max_in, uint8_t *out, size
if (out)
memcpy(out, tmp, max_out);
tmp_pos = max_out;
done_in += max_in;
done_in += max_in-1;
offset += max_in;
done_out += max_out;
}
@@ -325,101 +353,65 @@ void destroy_aes_xts_decrypt(op_aes_xts_decrypt_t *decrypt_ctx)
delete decrypt_ctx;
}
bool osd_messenger_t::op_encrypted_copy_data_to(osd_client_t* cl, uint8_t *enc_buf, size_t enc_len, size_t from, size_t & done)
void osd_messenger_t::op_encrypted_copy_buf(osd_client_t *cl, uint8_t *enc_buf, size_t enc_len, uint8_t *plain, size_t plain_len, size_t & done_plain, size_t & done_enc)
{
auto op = cl->write_op;
auto & op_pos = cl->write_op_pos;
assert(op->req.hdr.opcode == OSD_OP_WRITE);
if (!from)
if (!cl->xts_enc_ctx)
{
if (!cl->encrypt_ctx)
if (encrypt_xts_pool.size())
{
if (encrypt_ctx_pool.size())
{
cl->encrypt_ctx = encrypt_ctx_pool.back();
encrypt_ctx_pool.pop_back();
}
else
cl->encrypt_ctx = new op_aes_xts_encrypt_t();
cl->xts_enc_ctx = encrypt_xts_pool.back();
encrypt_xts_pool.pop_back();
}
assert(op->enc->key_chain[0]);
cl->encrypt_ctx->start(op->enc->key_chain[0], op->req.rw.offset, op->enc->bitmap_granularity);
}
for (int i = 0; i < op->iov.count; i++)
{
uint8_t *plain = (uint8_t*)op->iov.buf[i].iov_base;
size_t plain_len = op->iov.buf[i].iov_len;
while (from < plain_len || cl->encrypt_ctx->has_buffered())
{
if (done >= enc_len)
return false;
size_t done_in = 0;
size_t done_out = 0;
cl->encrypt_ctx->update(plain+from, plain_len-from, enc_buf+done, enc_len-done, done_in, done_out);
if (cl->write_csum_state && done_out > 0)
XXH3_64bits_update(cl->write_csum_state, enc_buf+done, done_out);
done += done_out;
op_pos += done_in;
from += done_in;
}
from -= plain_len;
}
if (cl->encrypt_ctx)
{
if (encrypt_ctx_pool.size() > max_aes_xts_pool_size)
delete cl->encrypt_ctx;
else
encrypt_ctx_pool.push_back(cl->encrypt_ctx);
cl->encrypt_ctx = NULL;
cl->xts_enc_ctx = new op_aes_xts_encrypt_t();
assert(cl->write_op->enc->key_chain[0]);
cl->xts_enc_ctx->start(cl->write_op->enc->key_chain[0], cl->write_op->req.rw.offset, cl->write_op->enc->bitmap_granularity);
}
while (done_plain < plain_len && done_enc < enc_len)
{
size_t done_in = 0;
size_t done_out = 0;
cl->xts_enc_ctx->update(plain+done_plain, plain_len-done_plain, enc_buf+done_enc, enc_len-done_enc, done_in, done_out);
if (cl->write_csum_state && done_out > 0)
XXH3_64bits_update(cl->write_csum_state, enc_buf+done_enc, done_out);
done_enc += done_out;
cl->write_op_pos += done_in;
done_plain += done_in;
}
return true;
}
bool osd_messenger_t::op_decrypted_copy_data_from(osd_client_t* cl, uint8_t *enc_buf, size_t enc_len, size_t from, size_t & done)
void osd_messenger_t::op_decrypted_copy_buf(osd_client_t *cl, uint8_t *enc_buf, size_t enc_len, uint8_t *plain, size_t plain_len, size_t & done_plain, size_t & done_enc)
{
op_decrypt_start(cl);
auto op = cl->read_op;
assert(op->req.hdr.opcode == OSD_OP_READ);
for (int i = 0; i < op->iov.count; i++)
while (done_plain < plain_len && done_enc < enc_len)
{
uint8_t *plain = (uint8_t*)op->iov.buf[i].iov_base;
size_t plain_len = op->iov.buf[i].iov_len;
while (from < plain_len)
{
if (done >= enc_len)
return false;
size_t done_in = 0;
size_t done_out = 0;
// plain == NULL means skip output
cl->decrypt_ctx->update(enc_buf+done, enc_len-done, plain ? plain+from : NULL, plain_len-from, done_in, done_out);
if (cl->read_csum_state && done_in > 0)
XXH3_64bits_update(cl->read_csum_state, enc_buf+done, done_in);
done += done_in;
cl->read_op_pos += done_out;
cl->read_op_inline_decrypt_in += done_in;
from += done_out;
if (!done_out)
return false;
}
from -= plain_len;
size_t done_in = 0;
size_t done_out = 0;
// plain == NULL means skip output
cl->xts_dec_ctx->update(enc_buf+done_enc, enc_len-done_enc, plain ? plain+done_plain : NULL, plain_len-done_plain, done_in, done_out);
if (cl->read_csum_state && done_in > 0)
XXH3_64bits_update(cl->read_csum_state, enc_buf+done_enc, done_in);
done_enc += done_in;
cl->read_op_pos += done_out;
cl->read_op_inline_decrypt_in += done_in;
done_plain += done_out;
}
op_decrypt_free(cl);
return true;
}
void osd_messenger_t::op_decrypt_start(osd_client_t* cl)
{
if (!cl->decrypt_ctx)
if (!cl->xts_dec_ctx)
{
if (decrypt_ctx_pool.size())
if (decrypt_xts_pool.size())
{
cl->decrypt_ctx = decrypt_ctx_pool.back();
decrypt_ctx_pool.pop_back();
cl->xts_dec_ctx = decrypt_xts_pool.back();
decrypt_xts_pool.pop_back();
}
else
cl->decrypt_ctx = new op_aes_xts_decrypt_t();
cl->xts_dec_ctx = new op_aes_xts_decrypt_t();
auto & enc = cl->read_op->enc;
cl->decrypt_ctx->start(enc->key_chain, enc->chain_size,
assert(cl->read_op->req.hdr.opcode == OSD_OP_READ);
cl->xts_dec_ctx->start(enc->key_chain, enc->chain_size,
(cl->read_op->req.rw.flags & OSD_OP_RETURN_CHAIN) ? (uint8_t*)cl->read_op->bitmap + enc->read_chain_bitmap_pos : 0,
cl->read_op->req.rw.offset, enc->bitmap_granularity);
}
@@ -451,7 +443,7 @@ void osd_messenger_t::op_decrypt_inline(osd_client_t* cl)
size_t out_len = op->iov.buf[j].iov_len - from_out;
size_t done_in = 0;
size_t done_out = 0;
cl->decrypt_ctx->update(in, in_len, out, out_len, done_in, done_out);
cl->xts_dec_ctx->update(in, in_len, out, out_len, done_in, done_out);
if (done_in >= in_len)
{
i++;
@@ -468,17 +460,142 @@ void osd_messenger_t::op_decrypt_inline(osd_client_t* cl)
from_out += done_out;
}
assert(j >= op->iov.count);
op_decrypt_free(cl);
}
void osd_messenger_t::op_decrypt_free(osd_client_t* cl)
{
if (cl->decrypt_ctx)
if (cl->xts_dec_ctx)
{
if (decrypt_ctx_pool.size() > max_aes_xts_pool_size)
delete cl->decrypt_ctx;
if (decrypt_xts_pool.size() > max_cipher_pool_size)
delete cl->xts_dec_ctx;
else
decrypt_ctx_pool.push_back(cl->decrypt_ctx);
cl->decrypt_ctx = NULL;
decrypt_xts_pool.push_back(cl->xts_dec_ctx);
cl->xts_dec_ctx = NULL;
}
}
void osd_messenger_t::op_encrypt_free(osd_client_t* cl)
{
if (cl->xts_enc_ctx)
{
if (encrypt_xts_pool.size() > max_cipher_pool_size)
delete cl->xts_enc_ctx;
else
encrypt_xts_pool.push_back(cl->xts_enc_ctx);
cl->xts_enc_ctx = NULL;
}
}
bool osd_messenger_t::derive_aes_keys(osd_client_t *cl, bool update_my, bool update_peer)
{
if (!cl->hs_result.shared_secret.size())
{
cl->hs_result = cl->hs->get_result();
if (handshake_hook)
{
handshake_hook(cl);
}
}
std::vector<uint8_t> old_my = cl->my_key, old_peer = cl->peer_key;
// Both keys include AES key and iv + xxhash3 secret
const auto len = AES_256_GCM_KEY_SIZE + AES_256_GCM_IV_SIZE + XXH_SECRET_DEFAULT_SIZE;
cl->my_key.resize(len);
cl->peer_key.resize(len);
bool ok = true;
if (update_my || !old_my.size())
{
ok = ok && hs_ctx->derive_kdf(cl->hs_result.shared_secret.data(), cl->hs_result.shared_secret.size(),
old_my.size() ? old_my.data() : NULL, old_my.size(),
cl->is_incoming ? "server key" : "client key", cl->my_key.data(), len);
#ifdef WITH_ISAL_CRYPTO
if (ok)
isal_aes_gcm_pre_256(cl->my_key.data(), &cl->my_key_isal);
#endif
cl->my_iv_ctr = 0;
}
if (update_peer || !old_peer.size())
{
ok = ok && hs_ctx->derive_kdf(cl->hs_result.shared_secret.data(), cl->hs_result.shared_secret.size(),
old_peer.size() ? old_peer.data() : NULL, old_peer.size(),
!cl->is_incoming ? "server key" : "client key", cl->peer_key.data(), len);
#ifdef WITH_ISAL_CRYPTO
if (ok)
isal_aes_gcm_pre_256(cl->peer_key.data(), &cl->peer_key_isal);
#endif
cl->peer_iv_ctr = 0;
}
return ok;
}
void osd_messenger_t::init_tls()
{
if (!tls_cert.empty() || !tls_key.empty() || !osd_tls_ca.empty() || !client_tls_ca.empty())
{
if (tls_cert.empty() || tls_key.empty() || osd_tls_ca.empty() || osd_num && client_tls_ca.empty())
{
if (osd_num)
fprintf(stderr, "Vitastor transport encryption requires osd_cert, osd_pkey, osd_ca, client_ca options for OSDs\n");
else
fprintf(stderr, "Vitastor transport encryption requires cert, pkey and osd_ca options\n");
exit(1);
}
else
{
#ifndef __MOCK__
gcm_enabled = true;
hs_ctx = msgr_handshake_ctx_i::create_ctx();
if (!hs_ctx->init(tls_cert, tls_key, osd_tls_ca, client_tls_ca))
{
fprintf(stderr, "Error: %s\n", hs_ctx->get_error().c_str());
exit(1);
}
#endif
}
}
}
void osd_messenger_t::init_tls_client(osd_client_t *cl)
{
if (gcm_enabled)
{
cl->gcm_enabled = true;
cl->hs = hs_ctx->create();
cl->hs->init(cl->is_incoming);
if (cl->hs->get_out().size())
{
if (cl->write_state == 0)
{
cl->write_state = CL_WRITE_READY;
write_ready_clients.push_back(cl->client_id);
}
}
}
}
void osd_messenger_t::destroy_tls()
{
#ifdef WITH_ISAL_CRYPTO
for (isal_gcm_context_data *ctx: encrypt_gcm_pool)
{
free(ctx);
}
for (isal_gcm_context_data *ctx: decrypt_gcm_pool)
{
free(ctx);
}
#else
for (EVP_CIPHER_CTX *ctx: encrypt_gcm_pool)
{
EVP_CIPHER_CTX_free(ctx);
}
for (EVP_CIPHER_CTX *ctx: decrypt_gcm_pool)
{
EVP_CIPHER_CTX_free(ctx);
}
#endif
if (hs_ctx)
{
delete hs_ctx;
hs_ctx = NULL;
}
}
+9 -9
View File
@@ -3,17 +3,18 @@
#include <stdint.h>
#ifdef WITH_ISAL_CRYPTO
#include <isa-l_crypto/aes_xts.h>
#endif
#include "../util/xxh_x86dispatch.h"
// WITH_OPENSSL is left to possibly support other crypto libraries
#ifdef WITH_OPENSSL
#include <openssl/conf.h>
#include <openssl/evp.h>
#include <openssl/err.h>
#endif
class op_aes_xts_encrypt_t
{
#ifdef WITH_OPENSSL
#ifndef WITH_ISAL_CRYPTO
EVP_CIPHER_CTX *ctx = NULL;
#endif
uint64_t start_offset = 0;
@@ -31,7 +32,6 @@ public:
op_aes_xts_encrypt_t();
~op_aes_xts_encrypt_t();
inline bool has_buffered() { return encrypted; };
void start(uint8_t *key, uint64_t start_offset, size_t block_size);
void update(uint8_t *in, size_t max_in, uint8_t *out, size_t max_out, size_t & done_in, size_t & done_out);
};
@@ -40,13 +40,14 @@ void destroy_aes_xts_encrypt(op_aes_xts_encrypt_t *encrypt_ctx);
class op_aes_xts_decrypt_t
{
#ifdef WITH_OPENSSL
#ifndef WITH_ISAL_CRYPTO
EVP_CIPHER_CTX *ctx = NULL;
#endif
uint64_t start_offset = 0;
uint8_t **key_chain = NULL;
size_t chain_size = 0;
uint8_t *key_indexes = NULL;
void *key_indexes = NULL;
int key_index_bytes = 0;
size_t offset = 0;
size_t block_size = 0;
uint8_t *tmp = NULL;
@@ -60,8 +61,7 @@ public:
op_aes_xts_decrypt_t();
~op_aes_xts_decrypt_t();
inline bool has_buffered() { return decrypted; };
void start(uint8_t **key_chain, size_t chain_size, uint8_t *key_indexes, uint64_t start_offset, size_t block_size);
void start(uint8_t **key_chain, size_t chain_size, void *key_indexes, uint64_t start_offset, size_t block_size);
void update(uint8_t *in, size_t max_in, uint8_t *out, size_t max_out, size_t & done_in, size_t & done_out);
};
+765
View File
@@ -0,0 +1,765 @@
// Copyright (c) Vitaliy Filippov, 2026+
// License: VNPL-1.1 or GNU GPL-2.0+ (see README.md for details)
#include <stdint.h>
#include <assert.h>
#include <string>
#include <vector>
#include <memory>
#include <openssl/conf.h>
#include <openssl/evp.h>
#include <openssl/obj_mac.h>
#include <openssl/ec.h>
#include <openssl/kdf.h>
#include <openssl/bio.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#include "msgr_handshake.h"
#include "openssl_util.h"
#include "str_util.h"
#define AES_256_GCM_KEY_SIZE 32
#define AES_256_GCM_IV_SIZE 12
// TLS 1.3-like handshake
// 1. Client->server: EC public key
// 2. Server->client: EC public key, encrypted certificate and digital signature of the handshake
// 3. Client->server: encrypted certificate and digital signature of the handshake
// "vitaECDH" interleaved
#define MSGR_HS_MAGIC 0x4861447443694576l
#define MSGR_HS_MAX_LEN 131072
#define MSGR_HS_SERVER_INIT 0
#define MSGR_HS_CLIENT_INIT 1
#define MSGR_HS_SERVER_REPLY 2
#define MSGR_HS_CLIENT_REPLY 3
#define MSGR_HS_DONE 100
#define MSGR_HS_ERROR 101
struct __attribute__((__packed__)) msgr_handshake_hdr_t
{
uint32_t msg_len;
uint64_t magic;
uint32_t type;
};
class msgr_handshake_ctx_t: public msgr_handshake_ctx_i
{
friend class msgr_handshake_t;
EVP_PKEY_CTX *pctx = NULL;
EVP_PKEY *params = NULL;
X509_STORE *ca = NULL;
X509 *osd_ca = NULL;
X509 *client_ca = NULL;
std::string my_cert_pem;
X509 *my_cert = NULL;
EVP_PKEY *my_pubkey = NULL;
const EVP_MD *md = NULL;
EVP_PKEY *my_privkey = NULL;
EVP_KDF_CTX* kdf_ctx = NULL;
std::string error;
bool on_error(const std::string & prefix);
public:
~msgr_handshake_ctx_t();
msgr_handshake_i* create() override;
bool init(const std::string & pem_cert, const std::string & pem_key,
const std::string & pem_osd_ca, const std::string & pem_client_ca) override;
std::string get_error() override;
bool derive_kdf(const uint8_t* insecret, size_t insecret_len,
const uint8_t* salt, size_t salt_len, const char *label, uint8_t *outsecret, size_t outsize) override;
};
class msgr_handshake_t: public msgr_handshake_i
{
msgr_handshake_ctx_t *ctx = NULL;
bool is_server = false;
std::vector<uint8_t> full_handshake;
std::vector<uint8_t> in_buf;
std::vector<uint8_t> out_buf;
std::string error;
int state = 0;
EVP_PKEY *ec_key = NULL;
X509 *peer_cert = NULL;
bool peer_is_osd = false;
std::vector<uint8_t> shared_secret;
std::vector<uint8_t> hs_key, peer_hs_key;
msgr_handshake_hdr_t *cur_hdr = NULL;
uint8_t *cur_buf = NULL;
size_t cur_left = 0;
bool on_error(const std::string & prefix);
bool derive_shared_secret(EVP_PKEY *peer_ec_key);
bool derive_hs_keys(const uint8_t *encoded_peer_key, size_t encoded_key_len);
bool sign(std::vector<uint8_t> & out);
bool verify(const uint8_t *signature, size_t signature_len);
bool encrypt(const uint8_t* src, size_t len, uint8_t* dest);
bool decrypt(const uint8_t* src, size_t & len, uint8_t* dest);
bool make_client_init();
bool make_server_reply();
bool make_client_reply();
bool verify_peer(const uint8_t *peer_cert_pem, size_t peer_cert_len);
ssize_t start_msg(uint8_t* src, size_t len, uint32_t expected_type);
bool read_with_len(const uint8_t* & dst, uint32_t & dst_len);
bool handle_client_init();
bool handle_server_reply();
bool handle_client_reply();
bool handle_peer_cert(const uint8_t *key, uint32_t key_len);
void complete();
public:
// Workflow: create -> init -> handle -> get_result/get_error -> destruct
msgr_handshake_t(msgr_handshake_ctx_t *ctx): ctx(ctx) {}
~msgr_handshake_t();
bool init(bool server_mode) override;
ssize_t handle(uint8_t* in_buf, size_t in_size) override;
bool done() override;
std::vector<uint8_t>& get_out() override;
msgr_handshake_result_t get_result() override;
std::string get_error() override;
};
msgr_handshake_ctx_i* msgr_handshake_ctx_i::create_ctx()
{
return new msgr_handshake_ctx_t();
}
msgr_handshake_i* msgr_handshake_ctx_t::create()
{
return new msgr_handshake_t(this);
}
bool msgr_handshake_ctx_t::init(const std::string & pem_cert, const std::string & pem_key,
const std::string & pem_osd_ca, const std::string & pem_client_ca)
{
if (pem_cert.substr(0, 5) == "-----")
my_cert_pem = pem_cert;
else
{
my_cert_pem = read_file(pem_cert);
if (my_cert_pem.empty())
{
error = "Failed to load certificate file";
return false;
}
}
{
BIO *bio = BIO_new_mem_buf(my_cert_pem.data(), my_cert_pem.size());
if (!bio)
return on_error("BIO_new_mem_buf: ");
my_cert = PEM_read_bio_X509(bio, NULL, 0, NULL);
BIO_free(bio);
if (!my_cert)
return on_error("Failed to load certificate: ");
}
if (!(my_pubkey = X509_get0_pubkey(my_cert)))
return on_error("X509_get0_pubkey: ");
if (!(md = EVP_get_digestbynid(NID_sha384)))
return on_error("EVP_get_digestbynid SHA384: ");
if (!(my_privkey = openssl_load_key(pem_key)))
return on_error("Failed to load private key: ");
if (!(ca = X509_STORE_new()))
return on_error("X509_STORE_CTX_new: ");
if (!(osd_ca = openssl_load_cert(pem_osd_ca)))
return on_error("Failed to load OSD CA certificate: ");
if (X509_STORE_add_cert(ca, osd_ca) <= 0)
return on_error("X509_STORE_add_cert OSD CA: ");
if (!pem_client_ca.empty() && !(client_ca = openssl_load_cert(pem_client_ca)))
return on_error("Failed to load client CA certificate: ");
if (client_ca && X509_STORE_add_cert(ca, client_ca) <= 0)
return on_error("X509_STORE_add_cert client CA: ");
if (!(pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL)))
return on_error("EVP_PKEY_CTX_new_id: ");
if (EVP_PKEY_paramgen_init(pctx) <= 0)
return on_error("EVP_PKEY_paramgen_init: ");
if (EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, /*NID_X9_62_prime256v1*/NID_secp384r1) <= 0)
return on_error("EVP_PKEY_CTX_set_ec_paramgen_curve_nid: ");
if (EVP_PKEY_paramgen(pctx, &params) <= 0)
return on_error("EVP_PKEY_paramgen: ");
EVP_KDF *kdf = EVP_KDF_fetch(NULL, "hkdf", NULL);
if (!kdf)
return on_error("EVP_KDF_fetch: ");
kdf_ctx = EVP_KDF_CTX_new(kdf);
EVP_KDF_free(kdf);
return true;
}
std::string msgr_handshake_ctx_t::get_error()
{
return error;
}
msgr_handshake_ctx_t::~msgr_handshake_ctx_t()
{
if (pctx)
EVP_PKEY_CTX_free(pctx);
if (params)
EVP_PKEY_free(params);
if (ca)
X509_STORE_free(ca);
if (osd_ca)
X509_free(osd_ca);
if (client_ca)
X509_free(client_ca);
my_pubkey = NULL;
if (my_cert)
X509_free(my_cert);
if (my_privkey)
EVP_PKEY_free(my_privkey);
if (kdf_ctx)
EVP_KDF_CTX_free(kdf_ctx);
}
bool msgr_handshake_ctx_t::on_error(const std::string & prefix)
{
error = prefix+ERR_error_string(ERR_get_error(), NULL);
return false;
}
bool msgr_handshake_ctx_t::derive_kdf(const uint8_t* insecret, size_t insecret_len,
const uint8_t* salt, size_t salt_len, const char *label, uint8_t *outsecret, size_t outsize)
{
OSSL_PARAM params[5];
int n = 0;
params[n++] = OSSL_PARAM_construct_utf8_string("digest", (char*)"sha384", (size_t)7);
params[n++] = OSSL_PARAM_construct_octet_string("key", (void*)insecret, insecret_len);
params[n++] = OSSL_PARAM_construct_octet_string("info", (void*)label, strlen(label)+1);
params[n++] = OSSL_PARAM_construct_octet_string("salt", (salt ? (void*)salt : (void*)""), salt_len);
params[n++] = OSSL_PARAM_construct_end();
assert(n <= sizeof(params)/sizeof(OSSL_PARAM));
if (EVP_KDF_CTX_set_params(kdf_ctx, params) <= 0)
return false;
if (EVP_KDF_derive(kdf_ctx, outsecret, outsize, NULL) <= 0)
return false;
return true;
}
msgr_handshake_t::~msgr_handshake_t()
{
if (peer_cert)
X509_free(peer_cert);
if (ec_key)
EVP_PKEY_free(ec_key);
}
bool msgr_handshake_t::on_error(const std::string & prefix)
{
error = prefix+ERR_error_string(ERR_get_error(), NULL);
state = MSGR_HS_ERROR;
return false;
}
bool msgr_handshake_t::init(bool server_mode)
{
this->ctx = ctx;
std::unique_ptr<EVP_PKEY_CTX, decltype(&EVP_PKEY_CTX_free)> kctx(EVP_PKEY_CTX_new(ctx->params, NULL), EVP_PKEY_CTX_free);
if (!kctx)
return on_error("EVP_PKEY_CTX_new with EC params: ");
if (EVP_PKEY_keygen_init(kctx.get()) <= 0)
return on_error("EVP_PKEY_keygen_init: ");
if (EVP_PKEY_keygen(kctx.get(), &ec_key) <= 0)
return on_error("EVP_PKEY_keygen: ");
if (!server_mode)
{
// Send initial message - only the EC public key
if (!make_client_init())
return false;
}
this->is_server = server_mode;
this->state = server_mode ? MSGR_HS_SERVER_INIT : MSGR_HS_CLIENT_INIT;
return true;
}
static void copy_to(std::vector<uint8_t> & buf, const void* src, uint32_t len)
{
size_t old_size = buf.size();
buf.resize(buf.size() + len);
memcpy(buf.data() + old_size, src, len);
}
static void copy_to_with_len(std::vector<uint8_t> & buf, const void* src, uint32_t len)
{
copy_to(buf, &len, sizeof(len));
copy_to(buf, src, len);
}
bool msgr_handshake_t::derive_shared_secret(EVP_PKEY *peer_ec_key)
{
EVP_PKEY_CTX *dh_ctx = NULL;
if (!(dh_ctx = EVP_PKEY_CTX_new(ec_key, NULL)))
return on_error("EVP_PKEY_CTX_new for ECDH: ");
if (!EVP_PKEY_derive_init(dh_ctx))
{
EVP_PKEY_CTX_free(dh_ctx);
return on_error("EVP_PKEY_derive_init: ");
}
if (!EVP_PKEY_derive_set_peer(dh_ctx, peer_ec_key))
{
EVP_PKEY_CTX_free(dh_ctx);
return on_error("EVP_PKEY_derive_set_peer: ");
}
size_t len = 0;
if (!EVP_PKEY_derive(dh_ctx, NULL, &len))
{
EVP_PKEY_CTX_free(dh_ctx);
return on_error("EVP_PKEY_derive get length: ");
}
shared_secret.resize(len);
assert(len == 48);
if (!EVP_PKEY_derive(dh_ctx, shared_secret.data(), &len))
{
EVP_PKEY_CTX_free(dh_ctx);
return on_error("EVP_PKEY_derive: ");
}
assert(len == shared_secret.size());
shared_secret.resize(len);
EVP_PKEY_CTX_free(dh_ctx);
return true;
}
bool msgr_handshake_t::derive_hs_keys(const uint8_t *encoded_peer_key, size_t encoded_key_len)
{
std::unique_ptr<EVP_PKEY, decltype(&EVP_PKEY_free)> peer_ec_key(EVP_PKEY_new(), EVP_PKEY_free);
if (!peer_ec_key)
return on_error("EVP_PKEY_new: ");
if (EVP_PKEY_copy_parameters(peer_ec_key.get(), ec_key) <= 0)
return on_error("EVP_PKEY_copy_parameters: ");
if (EVP_PKEY_set1_encoded_public_key(peer_ec_key.get(), encoded_peer_key, encoded_key_len) <= 0)
return on_error("Invalid handshake peer key: ");
if (!derive_shared_secret(peer_ec_key.get()))
return false;
hs_key.resize(AES_256_GCM_KEY_SIZE + AES_256_GCM_IV_SIZE);
peer_hs_key.resize(AES_256_GCM_KEY_SIZE + AES_256_GCM_IV_SIZE);
if (!ctx->derive_kdf(shared_secret.data(), shared_secret.size(),
NULL, 0, (state == MSGR_HS_SERVER_INIT ? "server hs key" : "client hs key"),
hs_key.data(), hs_key.size()))
return on_error("derive_kdf: ");
if (!ctx->derive_kdf(shared_secret.data(), shared_secret.size(),
NULL, 0, (state != MSGR_HS_SERVER_INIT ? "server hs key" : "client hs key"),
peer_hs_key.data(), peer_hs_key.size()))
return on_error("derive_kdf: ");
return true;
}
bool msgr_handshake_t::sign(std::vector<uint8_t> & out)
{
std::unique_ptr<EVP_MD_CTX, decltype(&EVP_MD_CTX_free)> md_ctx(EVP_MD_CTX_new(), EVP_MD_CTX_free);
if (!md_ctx)
return on_error("EVP_MD_CTX_create: ");
if (EVP_DigestSignInit(md_ctx.get(), NULL, ctx->md, NULL, ctx->my_privkey) <= 0)
return on_error("EVP_DigestSignInit: ");
if (EVP_DigestSignUpdate(md_ctx.get(), full_handshake.data(), full_handshake.size()) <= 0)
return on_error("EVP_DigestSignUpdate: ");
size_t siglen = 0;
if (EVP_DigestSignFinal(md_ctx.get(), NULL, &siglen) <= 0)
return on_error("EVP_DigestSignFinal get length: ");
size_t oldsize = out.size();
out.resize(oldsize + siglen);
if (EVP_DigestSignFinal(md_ctx.get(), out.data() + oldsize, &siglen) <= 0)
return on_error("EVP_DigestSignFinal: ");
out.resize(oldsize + siglen);
return true;
}
bool msgr_handshake_t::verify(const uint8_t *signature, size_t signature_len)
{
std::unique_ptr<EVP_MD_CTX, decltype(&EVP_MD_CTX_free)> md_ctx(EVP_MD_CTX_new(), EVP_MD_CTX_free);
if (!md_ctx)
return on_error("EVP_MD_CTX_create: ");
if (EVP_DigestVerifyInit(md_ctx.get(), NULL, ctx->md, NULL, X509_get0_pubkey(peer_cert)) <= 0)
return on_error("EVP_DigestVerifyInit: ");
if (EVP_DigestVerifyUpdate(md_ctx.get(), full_handshake.data(), full_handshake.size()) <= 0)
return on_error("EVP_DigestVerifyUpdate: ");
if (EVP_DigestVerifyFinal(md_ctx.get(), signature, signature_len) <= 0)
return false;
return true;
}
bool msgr_handshake_t::encrypt(const uint8_t* src, size_t len, uint8_t* dest)
{
std::unique_ptr<EVP_CIPHER_CTX, decltype(&EVP_CIPHER_CTX_free)> enc_ctx(EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free);
if (!ctx)
return on_error("EVP_CIPHER_CTX_new: ");
if (EVP_EncryptInit_ex(enc_ctx.get(), EVP_aes_256_gcm(), NULL, hs_key.data(), hs_key.data() + AES_256_GCM_KEY_SIZE) <= 0)
return on_error("EVP_EncryptInit AES-256-GCM: ");
int actual_out;
if (EVP_EncryptUpdate(enc_ctx.get(), dest, &actual_out, src, len) <= 0)
return on_error("EVP_EncryptUpdate: ");
assert(actual_out == len);
if (EVP_EncryptFinal_ex(enc_ctx.get(), NULL, &actual_out) <= 0)
return on_error("EVP_EncryptFinal: ");
if (EVP_CIPHER_CTX_ctrl(enc_ctx.get(), EVP_CTRL_GCM_GET_TAG, 16, dest+len) <= 0)
return on_error("EVP_CTRL_GCM_GET_TAG: ");
(*(uint64_t*)(hs_key.data() + AES_256_GCM_KEY_SIZE))++; // change IV
return true;
}
bool msgr_handshake_t::decrypt(const uint8_t *src, size_t & len, uint8_t* dest)
{
if (len <= 16) // only tag?!
{
len = 0;
error = "Handshake decryption failed";
state = MSGR_HS_ERROR;
return false;
}
std::unique_ptr<EVP_CIPHER_CTX, decltype(&EVP_CIPHER_CTX_free)> dec_ctx(EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free);
if (!ctx)
return on_error("EVP_CIPHER_CTX_new: ");
if (EVP_DecryptInit_ex(dec_ctx.get(), EVP_aes_256_gcm(), NULL, peer_hs_key.data(), peer_hs_key.data() + AES_256_GCM_KEY_SIZE) <= 0)
return on_error("EVP_DecryptInit AES-256-GCM: ");
int actual_out;
len -= 16;
if (EVP_DecryptUpdate(dec_ctx.get(), dest, &actual_out, src, len) <= 0)
return on_error("EVP_DecryptUpdate: ");
assert(actual_out == len);
if (EVP_CIPHER_CTX_ctrl(dec_ctx.get(), EVP_CTRL_GCM_SET_TAG, 16, (void*)(src+len)) <= 0)
return on_error("EVP_CTRL_GCM_SET_TAG: ");
if (EVP_DecryptFinal_ex(dec_ctx.get(), NULL, &actual_out) <= 0)
{
error = "Handshake decryption failed";
state = MSGR_HS_ERROR;
return false;
}
(*(uint64_t*)(peer_hs_key.data() + AES_256_GCM_KEY_SIZE))++; // change IV
return true;
}
bool msgr_handshake_t::make_client_init()
{
uint8_t *key = NULL;
size_t key_len = EVP_PKEY_get1_encoded_public_key(ec_key, &key);
if (!key_len)
return on_error("EVP_PKEY_get1_encoded_public_key: ");
const size_t old_len = out_buf.size();
out_buf.resize(out_buf.size() + key_len + sizeof(msgr_handshake_hdr_t));
uint8_t *buf = out_buf.data() + old_len;
msgr_handshake_hdr_t *hdr = (msgr_handshake_hdr_t *)buf;
hdr->msg_len = key_len + sizeof(msgr_handshake_hdr_t);
hdr->magic = MSGR_HS_MAGIC;
hdr->type = MSGR_HS_CLIENT_INIT;
memcpy(buf + sizeof(msgr_handshake_hdr_t), key, key_len);
copy_to(full_handshake, &hdr->type, sizeof(hdr->type));
copy_to_with_len(full_handshake, key, key_len);
OPENSSL_free(key);
return true;
}
bool msgr_handshake_t::make_server_reply()
{
uint8_t *key = NULL;
size_t key_len = EVP_PKEY_get1_encoded_public_key(ec_key, &key);
if (!key_len)
return on_error("EVP_PKEY_get1_encoded_public_key: ");
// Append type, key and raw certificate to signed data and sign it
msgr_handshake_hdr_t hdr = { .magic = MSGR_HS_MAGIC, .type = MSGR_HS_SERVER_REPLY };
copy_to(full_handshake, &hdr.type, sizeof(hdr.type));
copy_to_with_len(full_handshake, key, key_len);
copy_to_with_len(full_handshake, ctx->my_cert_pem.data(), ctx->my_cert_pem.size());
std::vector<uint8_t> signature;
if (!sign(signature))
{
OPENSSL_free(key);
return false;
}
// Encrypt certificate and signature
std::vector<uint8_t> encrypt_data;
copy_to_with_len(encrypt_data, ctx->my_cert_pem.data(), ctx->my_cert_pem.size());
copy_to_with_len(encrypt_data, signature.data(), signature.size());
encrypt_data.resize(encrypt_data.size()+16);
if (!encrypt(encrypt_data.data(), encrypt_data.size()-16, encrypt_data.data()))
{
OPENSSL_free(key);
return false;
}
// Construct message
hdr.msg_len = sizeof(msgr_handshake_hdr_t) + 4 + key_len + encrypt_data.size();
copy_to(out_buf, &hdr, sizeof(hdr));
copy_to_with_len(out_buf, key, key_len);
copy_to(out_buf, encrypt_data.data(), encrypt_data.size());
OPENSSL_free(key);
return true;
}
bool msgr_handshake_t::make_client_reply()
{
// Append type and raw certificate to signed data and sign it
msgr_handshake_hdr_t hdr = { .magic = MSGR_HS_MAGIC, .type = MSGR_HS_CLIENT_REPLY };
copy_to(full_handshake, &hdr.type, sizeof(hdr.type));
copy_to_with_len(full_handshake, ctx->my_cert_pem.data(), ctx->my_cert_pem.size());
std::vector<uint8_t> signature;
if (!sign(signature))
return false;
// Encrypt certificate and signature
std::vector<uint8_t> encrypt_data;
copy_to_with_len(encrypt_data, ctx->my_cert_pem.data(), ctx->my_cert_pem.size());
copy_to_with_len(encrypt_data, signature.data(), signature.size());
encrypt_data.resize(encrypt_data.size()+16);
if (!encrypt(encrypt_data.data(), encrypt_data.size()-16, encrypt_data.data()))
return false;
// Construct message
hdr.msg_len = sizeof(msgr_handshake_hdr_t) + encrypt_data.size();
copy_to(out_buf, &hdr, sizeof(hdr));
copy_to(out_buf, encrypt_data.data(), encrypt_data.size());
return true;
}
bool msgr_handshake_t::verify_peer(const uint8_t *peer_cert_pem, size_t peer_cert_len)
{
BIO *bio = BIO_new_mem_buf(peer_cert_pem, peer_cert_len);
if (!bio)
return on_error("BIO_new_mem_buf: ");
peer_cert = PEM_read_bio_X509(bio, NULL, 0, NULL);
BIO_free(bio);
if (!peer_cert)
{
error = "Invalid peer certificate";
state = MSGR_HS_ERROR;
return false;
}
std::unique_ptr<X509_STORE_CTX, decltype(&X509_STORE_CTX_free)> ca_ctx(X509_STORE_CTX_new(), X509_STORE_CTX_free);
if (!ca_ctx)
return on_error("X509_STORE_CTX_new: ");
if (X509_STORE_CTX_init(ca_ctx.get(), ctx->ca, peer_cert, NULL) <= 0)
return on_error("X509_STORE_CTX_init: ");
// Maybe use X509_VERIFY_PARAM_set_auth_level(X509_STORE_CTX_get0_param(ca_ctx.get()), 2) ?
X509_STORE_CTX_set_default(ca_ctx.get(), is_server ? "ssl_client" : "ssl_server");
if (X509_verify_cert(ca_ctx.get()) <= 0)
{
error = "Peer certificate verification failed: ";
error += X509_verify_cert_error_string(X509_STORE_CTX_get_error(ca_ctx.get()));
state = MSGR_HS_ERROR;
return false;
}
peer_is_osd = (X509_verify(peer_cert, X509_get0_pubkey(ctx->osd_ca)) > 0);
if (!is_server && !peer_is_osd)
{
error = "Peer is not an OSD";
state = MSGR_HS_ERROR;
return false;
}
return true;
}
ssize_t msgr_handshake_t::start_msg(uint8_t* src, size_t len, uint32_t expected_type)
{
size_t orig_len = len;
size_t to_buffer = (len < sizeof(msgr_handshake_hdr_t)-in_buf.size()
? len : sizeof(msgr_handshake_hdr_t)-in_buf.size());
in_buf.insert(in_buf.end(), src, src+to_buffer);
len -= to_buffer;
src += to_buffer;
if (in_buf.size() < sizeof(msgr_handshake_hdr_t))
return 0;
cur_hdr = (msgr_handshake_hdr_t *)in_buf.data();
if (cur_hdr->magic != MSGR_HS_MAGIC ||
cur_hdr->type != expected_type ||
cur_hdr->msg_len <= sizeof(msgr_handshake_hdr_t) ||
cur_hdr->msg_len >= MSGR_HS_MAX_LEN)
{
error = "Invalid handshake packet magic, type or size";
state = MSGR_HS_ERROR;
return -1;
}
to_buffer = (len < cur_hdr->msg_len-in_buf.size()
? len : cur_hdr->msg_len-in_buf.size());
in_buf.insert(in_buf.end(), src, src+to_buffer);
cur_hdr = (msgr_handshake_hdr_t *)in_buf.data();
len -= to_buffer;
src += to_buffer;
if (in_buf.size() < cur_hdr->msg_len)
return 0;
cur_left = cur_hdr->msg_len - sizeof(msgr_handshake_hdr_t);
cur_buf = in_buf.data() + sizeof(msgr_handshake_hdr_t);
return orig_len - len;
}
bool msgr_handshake_t::read_with_len(const uint8_t* & dst, uint32_t & dst_len)
{
if (cur_left < 4)
{
error = "Handshake packet too short";
state = MSGR_HS_ERROR;
return false;
}
dst_len = *(uint32_t*)cur_buf;
cur_buf += 4;
cur_left -= 4;
if (cur_left < dst_len)
{
error = "Handshake packet too short";
state = MSGR_HS_ERROR;
return false;
}
dst = cur_buf;
cur_buf += dst_len;
cur_left -= dst_len;
return true;
}
bool msgr_handshake_t::handle_client_init()
{
// Derive shared secret and handshake keys
if (!derive_hs_keys(cur_buf, cur_left))
return false;
// Add type and key to full_handshake
copy_to(full_handshake, &cur_hdr->type, sizeof(cur_hdr->type));
copy_to_with_len(full_handshake, cur_buf, cur_left);
in_buf.clear();
return true;
}
// Decrypt and check peer certificate
bool msgr_handshake_t::handle_peer_cert(const uint8_t *key, uint32_t key_len)
{
if (!decrypt(cur_buf, cur_left, cur_buf))
return false;
const uint8_t *peer_cert_pem = NULL;
uint32_t peer_cert_len = 0;
if (!read_with_len(peer_cert_pem, peer_cert_len))
return false;
// Parse and verify certificate
if (!verify_peer(peer_cert_pem, peer_cert_len))
return false;
// Verify signature
copy_to(full_handshake, &cur_hdr->type, sizeof(cur_hdr->type));
if (key)
copy_to_with_len(full_handshake, key, key_len);
copy_to_with_len(full_handshake, peer_cert_pem, peer_cert_len);
const uint8_t *signature = NULL;
uint32_t signature_len = 0;
if (!read_with_len(signature, signature_len))
return false;
if (!verify(signature, signature_len))
return false;
return true;
}
bool msgr_handshake_t::handle_server_reply()
{
// Derive shared secret and handshake keys
const uint8_t *key = NULL;
uint32_t key_len = 0;
if (!read_with_len(key, key_len))
return false;
if (!derive_hs_keys(key, key_len))
return false;
// Decrypt and check peer certificate
if (!handle_peer_cert(key, key_len))
return false;
in_buf.clear();
return true;
}
bool msgr_handshake_t::handle_client_reply()
{
// Decrypt and check peer certificate
if (!handle_peer_cert(NULL, 0))
return false;
in_buf.clear();
return true;
}
void msgr_handshake_t::complete()
{
state = MSGR_HS_DONE;
hs_key.clear();
peer_hs_key.clear();
full_handshake.clear();
}
ssize_t msgr_handshake_t::handle(uint8_t* in_buf, size_t in_size)
{
if (state == MSGR_HS_SERVER_INIT)
{
ssize_t r = start_msg(in_buf, in_size, MSGR_HS_CLIENT_INIT);
if (r < 0)
return r;
if (r == 0)
return in_size;
if (!handle_client_init())
return -1;
// Send encrypted & signed response
if (!make_server_reply())
return -1;
state = MSGR_HS_SERVER_REPLY;
return r;
}
else if (state == MSGR_HS_CLIENT_INIT)
{
ssize_t r = start_msg(in_buf, in_size, MSGR_HS_SERVER_REPLY);
if (r < 0)
return r;
if (r == 0)
return in_size;
if (!handle_server_reply())
return -1;
// Verification passed, send certificate to the server
if (!make_client_reply())
return -1;
// Finished!
complete();
return r;
}
else if (state == MSGR_HS_SERVER_REPLY)
{
ssize_t r = start_msg(in_buf, in_size, MSGR_HS_CLIENT_REPLY);
if (r < 0)
return r;
if (r == 0)
return in_size;
if (!handle_client_reply())
return -1;
// Verification passed
// Finished!
complete();
return r;
}
else if (state == MSGR_HS_DONE)
{
return 0;
}
else if (state != MSGR_HS_ERROR)
{
error = "Unexpected handshake state: "+std::to_string(state);
}
return -1;
}
bool msgr_handshake_t::done()
{
return (state == MSGR_HS_DONE);
}
std::vector<uint8_t>& msgr_handshake_t::get_out()
{
return out_buf;
}
msgr_handshake_result_t msgr_handshake_t::get_result()
{
if (state != MSGR_HS_DONE)
return msgr_handshake_result_t{};
X509_up_ref(peer_cert);
return msgr_handshake_result_t{
.peer_cert = peer_cert,
.peer_is_osd = peer_is_osd,
.shared_secret = shared_secret,
};
}
std::string msgr_handshake_t::get_error()
{
return error;
}
+54
View File
@@ -0,0 +1,54 @@
// Copyright (c) Vitaliy Filippov, 2026+
// License: VNPL-1.1 or GNU GPL-2.0+ (see README.md for details)
#pragma once
#include <stdint.h>
#include <string>
#include <vector>
#include <openssl/types.h>
#define AES_256_GCM_KEY_SIZE 32
#define AES_256_GCM_IV_SIZE 12
#define AES_256_GCM_MAX_IV_CTR ((uint64_t)1 << 32)
// TLS 1.3-like handshake
// 1. Client->server: EC public key
// 2. Server->client: EC public key, encrypted certificate and digital signature of the handshake
// 3. Client->server: encrypted certificate and digital signature of the handshake
struct msgr_handshake_result_t
{
X509 *peer_cert = NULL;
bool peer_is_osd = false;
std::vector<uint8_t> shared_secret;
};
class msgr_handshake_i
{
public:
// Workflow: create -> init -> handle_msg -> get_result/get_error -> destruct
virtual ~msgr_handshake_i() = default;
virtual bool init(bool server_mode) = 0;
virtual ssize_t handle(uint8_t* in_buf, size_t in_size) = 0;
virtual bool done() = 0;
virtual std::vector<uint8_t> & get_out() = 0;
virtual msgr_handshake_result_t get_result() = 0;
virtual std::string get_error() = 0;
};
class msgr_handshake_ctx_i
{
public:
static msgr_handshake_ctx_i* create_ctx();
virtual ~msgr_handshake_ctx_i() = default;
virtual msgr_handshake_i* create() = 0;
virtual bool init(const std::string & pem_cert, const std::string & pem_key,
const std::string & pem_osd_ca, const std::string & pem_client_ca) = 0;
virtual std::string get_error() = 0;
virtual bool derive_kdf(const uint8_t* insecret, size_t insecret_len,
const uint8_t* salt, size_t salt_len, const char *label, uint8_t *outsecret, size_t outsize) = 0;
};
+1 -4
View File
@@ -8,6 +8,7 @@
osd_op_t::~osd_op_t()
{
assert(!bs_op);
assert(!op_data);
if (bitmap_buf)
{
free(bitmap_buf);
@@ -26,10 +27,6 @@ osd_op_t::~osd_op_t()
{
free(enc_buf);
}
if (op_data)
{
free(op_data);
}
}
bool osd_op_t::is_recovery_related()
+2 -2
View File
@@ -181,8 +181,8 @@ struct __attribute__((visibility("default"))) osd_op_t
// bitmap, bitmap_len, bmp_data are only meaningful for reads
void *bitmap = NULL;
unsigned bitmap_len = 0;
unsigned bmp_data = 0;
void *bitmap_buf = NULL;
size_t bmp_data = 0;
uint8_t *bitmap_buf = NULL;
void *rmw_buf = NULL;
std::shared_ptr<osd_op_enc_t> enc;
uint8_t *enc_buf = NULL;
+36 -26
View File
@@ -541,7 +541,7 @@ bool osd_messenger_t::connect_rdma(uint64_t client_id, std::string rdma_address,
{
// Remember connection, but switch to RDMA only after sending the configuration response
cl->rdma_conn = rdma_conn;
cl->peer_state = PEER_RDMA_CONNECTING;
cl->peer_state = PEER_RDMA_CONNECTING_IN;
return true;
}
}
@@ -568,29 +568,6 @@ static void try_send_rdma_wr(osd_client_t *cl, ibv_sge *sge, int op_sge)
cl->rdma_conn->cur_send++;
}
int osd_messenger_t::try_send_rdma_copy(osd_client_t *cl, uint8_t *dst, int dst_len)
{
int total_dst_len = dst_len;
while (dst_len > 0 && (cl->write_op || cl->write_ops.size()))
{
next_write_op(cl);
osd_op_t *op = cl->write_op;
size_t copied = op_copy_to(cl, dst, dst_len);
if (!copied)
{
break;
}
dst += copied;
dst_len -= copied;
if (!cl->write_op && op->op_type == OSD_OP_IN)
{
// this is a reply, free the op after sending it
cl->send_free_ops.push_back(op);
}
}
return total_dst_len-dst_len;
}
void osd_messenger_t::try_send_rdma(osd_client_t *cl)
{
auto rc = cl->rdma_conn;
@@ -616,7 +593,12 @@ void osd_messenger_t::try_send_rdma(osd_client_t *cl)
dst_len = (rc->send_out_pos < rc->send_out_size ? rc->send_out_size-rc->send_out_pos : rc->send_done_pos-rc->send_out_pos);
if (dst_len > rc->max_msg)
dst_len = rc->max_msg;
copied = try_send_rdma_copy(cl, dst, dst_len);
copied = copy_ops_to(cl, dst, dst_len);
if (cl->io_error)
{
stop_client(cl->client_id);
return;
}
if (copied > 0)
{
rc->send_out_pos += copied;
@@ -661,6 +643,7 @@ static void try_recv_rdma_wr(osd_client_t *cl, void *buf)
bool osd_messenger_t::init_recv_rdma(osd_client_t *cl)
{
return true;
auto rc = cl->rdma_conn;
assert(!rc->recv_buf.buf);
rc->recv_buf.buf = (uint8_t*)malloc_or_die(rc->max_msg * rc->max_recv);
@@ -716,9 +699,14 @@ void osd_messenger_t::handle_rdma_events(msgr_rdma_context_t *rdma_context)
continue;
}
auto rc = cl->rdma_conn;
if (!rc)
{
// Connection destroyed (fallback to TCP)
continue;
}
if (wc[i].status != IBV_WC_SUCCESS)
{
fprintf(stderr, "RDMA work request failed for client %ju", client_id);
fprintf(stderr, "RDMA work request (%s) failed for client %ju", is_send ? "send" : "recv", client_id);
if (cl->osd_num)
{
fprintf(stderr, " (OSD %ju)", cl->osd_num);
@@ -729,6 +717,28 @@ void osd_messenger_t::handle_rdma_events(msgr_rdma_context_t *rdma_context)
}
if (!is_send)
{
if (cl->peer_state == PEER_RDMA_CONNECTING_OUT)
{
// First message received over RDMA - TCP socket can be closed now
if (log_level > 0)
{
fprintf(stderr, "Successfully connected with client %ju using RDMA\n", cl->client_id);
}
cl->peer_state = PEER_RDMA;
if (cl->peer_fd >= 0 && cl->rdma_close_tcp)
{
// TCP socket is not needed anymore
tfd->set_fd_handler(cl->peer_fd, false, NULL);
clients_by_fd.erase(cl->peer_fd);
close(cl->peer_fd);
cl->peer_fd = -1;
}
if (cl->osd_num)
{
osd_peers[cl->osd_num] = cl;
on_connect_peer(cl->osd_num, 0, cl->client_id);
}
}
rc->cur_recv--;
if (!handle_read_buffer(cl, rc->recv_buffers[rc->next_recv_buf], wc[i].byte_len))
{
+1 -1
View File
@@ -79,7 +79,7 @@ struct msgr_rdma_connection_t
msgr_rdma_buf_t recv_buf;
std::deque<uint64_t> send_sizes;
msgr_rdma_buf_t send_out;
int send_out_pos = 0, send_done_pos = 0, send_out_size = 0;
size_t send_out_pos = 0, send_done_pos = 0, send_out_size = 0;
bool send_out_full = false;
~msgr_rdma_connection_t();
+4
View File
@@ -19,6 +19,7 @@ struct rdmacm_connecting_t
int tcp_port = 0;
int timeout_ms = 0;
int timeout_id = -1;
bool is_incoming = false;
msgr_rdma_context_t *rdma_context = NULL;
};
@@ -292,6 +293,7 @@ void osd_messenger_t::rdmacm_accept(rdma_cm_event *ev)
conn->client_id = next_client_id++;
conn->parsed_addr = *(sockaddr_storage*)rdma_get_peer_addr(ev->id);
conn->rdma_context = rdma_context;
conn->is_incoming = true;
rdmacm_set_conn_timeout(conn);
rdmacm_connecting[ev->id] = conn;
fprintf(stderr, "[OSD %ju] new client %ju: connection from %s via RDMA-CM\n", this->osd_num, conn->client_id,
@@ -492,11 +494,13 @@ void osd_messenger_t::rdmacm_established(rdma_cm_event *ev)
cl->peer_addr = conn->parsed_addr;
cl->peer_port = conn->rdmacm_port;
cl->client_id = conn->client_id;
cl->is_incoming = conn->is_incoming;
cl->peer_state = PEER_RDMA;
cl->connect_timeout_id = -1;
cl->osd_num = peer_osd;
cl->in_buf = (uint8_t*)malloc_or_die(receive_buffer_size);
cl->rdma_conn = rc;
init_tls_client(cl);
clients[conn->client_id] = cl;
if (conn->timeout_id >= 0)
{
File diff suppressed because it is too large Load Diff
+635 -484
View File
File diff suppressed because it is too large Load Diff
+41 -23
View File
@@ -9,12 +9,8 @@
#ifdef WITH_RDMA
#include "msgr_rdma.h"
#endif
#ifdef WITH_OPENSSL
#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#endif
void osd_client_t::cancel_ops()
{
@@ -86,21 +82,21 @@ void osd_messenger_t::stop_client(uint64_t client_id, bool force_delete)
fprintf(stderr, "[OSD %ju] Stopping client %ju (regular client)\n", osd_num, client_id);
}
}
if (cl->encrypt_ctx)
if (cl->xts_enc_ctx)
{
if (encrypt_ctx_pool.size() > max_aes_xts_pool_size)
destroy_aes_xts_encrypt(cl->encrypt_ctx);
if (encrypt_xts_pool.size() > max_cipher_pool_size)
destroy_aes_xts_encrypt(cl->xts_enc_ctx);
else
encrypt_ctx_pool.push_back(cl->encrypt_ctx);
cl->encrypt_ctx = NULL;
encrypt_xts_pool.push_back(cl->xts_enc_ctx);
cl->xts_enc_ctx = NULL;
}
if (cl->decrypt_ctx)
if (cl->xts_dec_ctx)
{
if (decrypt_ctx_pool.size() > max_aes_xts_pool_size)
destroy_aes_xts_decrypt(cl->decrypt_ctx);
if (decrypt_xts_pool.size() > max_cipher_pool_size)
destroy_aes_xts_decrypt(cl->xts_dec_ctx);
else
decrypt_ctx_pool.push_back(cl->decrypt_ctx);
cl->decrypt_ctx = NULL;
decrypt_xts_pool.push_back(cl->xts_dec_ctx);
cl->xts_dec_ctx = NULL;
}
// First set state to STOPPED so another stop_client() call doesn't try to free it again
cl->refs++;
@@ -223,7 +219,10 @@ osd_client_t::~osd_client_t()
{
if (op)
{
delete op;
if (!((size_t)op & 7))
delete op;
else
free((void*)((size_t)op & ~(size_t)7));
}
}
#ifndef __MOCK__
@@ -245,18 +244,37 @@ osd_client_t::~osd_client_t()
XXH3_freeState(write_csum_state);
write_csum_state = NULL;
}
#ifdef WITH_OPENSSL
if (ssl_cli)
if (enc_ctx)
{
SSL_free(ssl_cli);
ssl_cli = NULL;
write_to_ssl = NULL;
read_from_ssl = NULL;
#ifdef WITH_ISAL_CRYPTO
free(enc_ctx);
#else
EVP_CIPHER_CTX_free(enc_ctx);
#endif
enc_ctx = NULL;
}
if (dec_ctx)
{
#ifdef WITH_ISAL_CRYPTO
free(dec_ctx);
#else
EVP_CIPHER_CTX_free(dec_ctx);
#endif
dec_ctx = NULL;
}
if (ssl_out_buf)
{
free(ssl_out_buf);
ssl_out_buf = NULL;
}
#endif
if (hs)
{
delete hs;
hs = NULL;
}
if (hs_result.peer_cert)
{
X509_free(hs_result.peer_cert);
hs_result.peer_cert = NULL;
}
}
+13
View File
@@ -231,12 +231,25 @@ struct __attribute__((__packed__)) osd_op_rw_t
uint32_t len;
// flags
// OSD_OP_RETURN_CHAIN for chained reads: return parent number in chain for each block
// read_chain size comes after bitmap, takes 0 bytes / 1 byte / 2 byte / 4 byte per each block,
// depending on the number of parent inodes (0 parents = 0 bytes, up to 255 parents = 1 byte, etc)
uint32_t flags;
// inode metadata revision for chained reads
uint64_t meta_revision;
// object version for atomic "CAS" (compare-and-set) writes
// writes and deletes fail with -EINTR if object version differs from (version-1)
uint64_t version;
static inline size_t chain_info_bytes(size_t chain_size)
{
if (chain_size <= 1)
return 0;
if (chain_size <= 256)
return 1;
if (chain_size <= 65536)
return 2;
return 4;
}
};
struct __attribute__((__packed__)) osd_reply_rw_t
+1 -1
View File
@@ -6,7 +6,7 @@ includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
Name: Vitastor
Description: Vitastor client library
Version: 3.0.9
Version: 3.0.10
Libs: -L${libdir} -lvitastor_client
Cflags: -I${includedir}
+3 -3
View File
@@ -259,9 +259,9 @@ static const char* help_text =
" Start HTTP server able to handle CLI commands over a REST API. Options:\n"
" --bind_address ADDR Specify server IP address or addresses, separated by space. Default is 127.0.0.1.\n"
" --port 8080 Specify server port.\n"
" --ssl_cert FILE Path to server SSL certificate file (PEM format).\n"
" --ssl_key FILE Path to server SSL private key file.\n"
" --ssl_ca FILE Path to file with SSL CA certificates used to validate client connections.\n"
" --server_cert FILE Path to server TLS certificate file (PEM format).\n"
" --server_key FILE Path to server TLS private key file.\n"
" --client_ca FILE Path to file with TLS CA certificates used to validate client connections.\n"
"\n"
"Use vitastor-cli --help <command> for command details or vitastor-cli --help --all for all details.\n"
"\n"
+2 -7
View File
@@ -27,12 +27,7 @@ struct cli_result_t
json11::Json data;
};
struct cli_user_t
{
std::string name;
std::string type;
std::set<std::string> groups;
};
struct user_info_t;
class cli_tool_t
{
@@ -45,7 +40,7 @@ public:
bool is_command_line = false;
bool color = false;
std::unique_ptr<cli_user_t> user; // for http mode
std::shared_ptr<user_info_t> user; // for http mode
ring_loop_t *ringloop = NULL;
epoll_manager_t *epmgr = NULL;
+1 -1
View File
@@ -9,7 +9,7 @@
bool cli_tool_t::check_image_perm(const inode_config_t & cfg, bool write)
{
return !user ||
user->type == "admin" ||
user->type == user_type_t::ADMIN ||
user->name == cfg.owner ||
cfg.owner_group != "" && user->groups.find(cfg.owner_group) != user->groups.end() ||
!write && cfg.reader_group != "" && user->groups.find(cfg.reader_group) != user->groups.end();
+1 -5
View File
@@ -1,9 +1,7 @@
// Copyright (c) Vitaliy Filippov, 2019+
// License: VNPL-1.1 (see README.md for details)
#ifdef WITH_OPENSSL
#include <openssl/rand.h>
#endif
#include <ctype.h>
#include "cli.h"
@@ -123,7 +121,7 @@ struct image_creator_t
bool check_pool_permission()
{
if (!parent->user || parent->user->type == "admin")
if (!parent->user || parent->user->type == user_type_t::ADMIN)
{
return true;
}
@@ -628,14 +626,12 @@ std::function<bool(cli_result_t &)> cli_tool_t::start_create(json11::Json cfg)
if (!cfg["enc_key"].is_null())
{
image_creator->set_key = true;
#ifdef WITH_OPENSSL
if (image_creator->enc_key == "random")
{
uint8_t newkey[64];
RAND_bytes(newkey, 64);
image_creator->enc_key = tohexstr(newkey, 64);
}
#endif
else
{
image_creator->enc_key = cfg["enc_key"].string_value();
+1 -1
View File
@@ -147,7 +147,7 @@ resume_1:
inode_t inode_num = INODE_WITH_POOL(pool_id, only_inode_num);
uint64_t used_size = kv.value["raw_used"].uint64_value();
auto stat_it = stats.find(inode_num);
if (parent->user && parent->user->type != "admin" && stat_it == stats.end())
if (parent->user && parent->user->type != user_type_t::ADMIN && stat_it == stats.end())
{
continue;
}
+2
View File
@@ -286,6 +286,8 @@ struct rm_inode_t
.data = data,
};
}
// Wakeup callers (otherwise they wake up only on next loop())
parent->ringloop->wakeup();
}
in_continue = false;
}
+33 -25
View File
@@ -76,6 +76,7 @@ struct cli_serve_t
int port = 0;
int listen_backlog = 0;
bool ssl = false;
bool use_auth = false;
std::vector<int> listen_fds;
http_context_t *http_ctx = NULL;
std::set<cli_serve_conn_t*> connections;
@@ -111,19 +112,34 @@ struct cli_serve_t
listen_backlog = options["listen_backlog"].uint64_value();
if (!listen_backlog)
listen_backlog = 128;
ssl = json_is_true(options["ssl"]);
if (ssl)
{
std::string ssl_cert = options["ssl_cert"].string_value();
std::string ssl_key = options["ssl_key"].string_value();
std::string ssl_ca = options["ssl_ca"].string_value();
std::string error;
http_ctx = http_context_init(parent->epmgr->tfd, ssl_cert, ssl_key, ssl_ca, ssl_ca != "", error);
if (error != "")
std::string tls_cert = (parent->cli->config.find("server_cert") != parent->cli->config.end()
? parent->cli->config["server_cert"].string_value() : "");
std::string tls_key = (parent->cli->config.find("server_key") != parent->cli->config.end()
? parent->cli->config["server_key"].string_value() : "");
std::string tls_ca = (parent->cli->config.find("client_ca") != parent->cli->config.end()
? parent->cli->config["client_ca"].string_value() : "");
if (tls_cert != "" || tls_key != "" || tls_ca != "")
{
result = (cli_result_t){ .err = EINVAL, .text = error };
state = 100;
return;
ssl = true;
if (tls_cert == "" || tls_key == "")
{
result = (cli_result_t){ .err = EINVAL, .text = "server_cert and server_key are required to serve HTTPS" };
state = 100;
return;
}
// use_auth is enabled by default when client_ca is set
use_auth = (parent->cli->config["use_auth"].is_null()
? (tls_ca != "")
: json_is_true(parent->cli->config["use_auth"]));
std::string error;
http_ctx = http_context_init(parent->epmgr->tfd, tls_cert, tls_key, tls_ca, tls_ca != "", error);
if (error != "")
{
result = (cli_result_t){ .err = EINVAL, .text = error };
state = 100;
return;
}
}
}
for (auto & bind_address: bind_addresses)
@@ -344,17 +360,9 @@ struct cli_serve_t
conn->request_path = std::move(req_line[1]);
conn->request_body = std::move(msg->body);
conn->response_type = "";
if (parent->cli->st_cli.use_auth)
if (use_auth)
{
auto user = std::make_unique<cli_user_t>();
user->name = msg->headers["_tls_common_name"];
auto user_it = parent->cli->st_cli.user_info.find(user->name);
auto userinfo = user_it == parent->cli->st_cli.user_info.end() ? user_it->second : json11::Json();
user->type = user->name == "root" ? "admin" : userinfo["type"].string_value();
for (auto & gr: userinfo["groups"].array_items())
{
user->groups.insert(gr.string_value());
}
conn->p->user = parent->cli->st_cli.get_user(msg->headers["_tls_common_name"]);
}
auto ctype = msg->headers["content-type"];
if (conn->request_method != "GET" && conn->request_method != "POST")
@@ -383,10 +391,10 @@ struct cli_serve_t
{
conn->response_type = "application/json";
conn->result = { .text = openapi_description };
if (parent->cli->st_cli.use_auth)
if (use_auth)
{
// Filter available paths by privileges
if (conn->p->user->type == "client")
if (conn->p->user->type == user_type_t::CLIENT)
{
std::string error;
auto openapi = json11::Json::parse(openapi_description, error).object_items();
@@ -403,7 +411,7 @@ struct cli_serve_t
conn->response_type = "application/json";
conn->result = { .text = json11::Json(openapi).dump() };
}
else if (conn->p->user->type != "admin")
else if (conn->p->user->type != user_type_t::ADMIN)
{
conn->response_type = "";
conn->result = { .err = EACCES, .text = "Access denied" };
@@ -418,7 +426,7 @@ struct cli_serve_t
{
conn->result = { .err = ENOSYS, .text = "method /"+uri[0]+" only allows POST requests" };
}
else if (parent->cli->st_cli.use_auth && conn->p->user->type == "client" && !cmd_it->second.allow_client)
else if (use_auth && conn->p->user->type == user_type_t::CLIENT && !cmd_it->second.allow_client)
{
conn->result = { .err = EACCES, .text = "Access denied" };
}
+2 -1
View File
@@ -121,6 +121,7 @@ static const char* help_text =
" --logfile <FILE> log to the specified file\n"
" --enforce 1 enforce permissions at the server side (default is disabled)\n"
" --foreground 1 stay in foreground, do not daemonize\n"
" --trace trace all NFS requests\n"
"\n"
"NFS proxy is stateless if you use immediate_commit=all in your cluster and if\n"
"you do not use client_enable_writeback=true, so you can freely use multiple\n"
@@ -158,7 +159,7 @@ json11::Json::object nfs_proxy_t::parse_args(int narg, const char *args[])
{
const char *opt = args[i]+2;
cfg[str_replace(opt, "-", "_")] = !strcmp(opt, "json") || !strcmp(opt, "block") ||
!strcmp(opt, "dry-run") || !strcmp(opt, "recalc-stats") ||
!strcmp(opt, "dry-run") || !strcmp(opt, "recalc-stats") || !strcmp(opt, "trace") ||
!strcmp(opt, "include-empty") || !strcmp(opt, "no-rm") || i == narg-1 ? "1" : args[++i];
}
else
+14 -1
View File
@@ -14,6 +14,7 @@
#include "http_client.h"
#include "str_util.h"
#include "json_util.h"
#include "openssl_util.h"
osd_t::osd_t(const json11::Json & config, ring_loop_t *ringloop)
{
@@ -67,6 +68,11 @@ osd_t::osd_t(const json11::Json & config, ring_loop_t *ringloop)
msgr.repeer_pgs = [this](osd_num_t peer_osd) { repeer_pgs(peer_osd); };
msgr.break_pg_locks = [this](osd_num_t peer_osd) { break_pg_locks(peer_osd); };
msgr.check_config_hook = [this](osd_client_t *cl, json11::Json conf) { return check_peer_config(cl, conf); };
msgr.handshake_hook = [this](osd_client_t *cl)
{
if (!cl->hs_result.peer_is_osd)
cl->user_info = st_cli.get_user(openssl_get_cn(cl->hs_result.peer_cert));
};
msgr.init();
init_cluster();
@@ -170,9 +176,13 @@ void osd_t::parse_config(bool init)
bs->parse_config(bs_cfg);
}
st_cli.parse_config(config);
msgr.parse_config(config);
msgr.parse_config(config, init);
if (init)
{
// use_auth is enabled by default when encryption is enabled
use_auth = (config["use_auth"].is_null()
? msgr.is_encryption_enabled()
: json_is_true(config["use_auth"]));
// Vital Blockstore parameters
bs_block_size = config["block_size"].uint64_value();
if (!bs_block_size)
@@ -181,6 +191,9 @@ void osd_t::parse_config(bool init)
if (!bs_bitmap_granularity)
bs_bitmap_granularity = DEFAULT_BITMAP_GRANULARITY;
clean_entry_bitmap_size = bs_block_size / bs_bitmap_granularity / 8;
msgr.bs_block_size = bs_block_size;
msgr.clean_entry_bitmap_size = clean_entry_bitmap_size;
msgr.max_write_request_size = MAX_DATA_BLOCK_SIZE; // will be changed after pool config
// immediate_commit
if (config["immediate_commit"] == "all")
immediate_commit = IMMEDIATE_ALL;
+3
View File
@@ -30,6 +30,8 @@
#define OSD_RECOVERING 0x10
#define OSD_SCRUBBING 0x20
#define SELF_CLIENT 0
#define MAX_AUTOSYNC_INTERVAL 3600
#define DEFAULT_AUTOSYNC_INTERVAL 5
#define DEFAULT_AUTOSYNC_WRITES 128
@@ -155,6 +157,7 @@ class osd_t
etcd_state_client_t st_cli;
osd_messenger_t msgr;
bool use_auth = false;
int etcd_failed_attempts = 0;
std::string etcd_lease_id;
json11::Json self_state;
+15
View File
@@ -428,6 +428,21 @@ void osd_t::on_change_pool_config_hook()
{
apply_pg_locks_localize_only();
}
msgr.max_write_request_size = 0;
for (auto & pc: st_cli.pool_config)
{
auto & pool_cfg = pc.second;
uint32_t pg_data_size = (pool_cfg.scheme == POOL_SCHEME_REPLICATED ? 1 : pool_cfg.pg_size-pool_cfg.parity_chunks);
uint32_t pg_block_size = pool_cfg.data_block_size * pg_data_size;
if (msgr.max_write_request_size < pg_block_size)
{
msgr.max_write_request_size = pg_block_size;
}
}
if (!msgr.max_write_request_size)
{
msgr.max_write_request_size = MAX_DATA_BLOCK_SIZE;
}
}
void osd_t::apply_pg_locks_localize_only()
+2 -1
View File
@@ -4,7 +4,8 @@
#include "osd.h"
#define FLUSH_BATCH 512
#define SELF_CLIENT 0
static_assert(FLUSH_BATCH <= MAX_SIMPLE_PAYLOAD_SIZE / sizeof(obj_ver_id));
void osd_t::submit_pg_flush_ops(pg_t & pg)
{
-2
View File
@@ -9,8 +9,6 @@
#include "str_util.h"
#include "osd.h"
#define SELF_CLIENT 0
// Peering loop
void osd_t::handle_peers()
{
+29 -14
View File
@@ -71,6 +71,21 @@ bool osd_t::prepare_primary_rw(osd_op_t *cur_op)
finish_op(cur_op, -EINVAL);
return false;
}
if (use_auth && cur_op->client_id != SELF_CLIENT)
{
osd_client_t *cl = msgr.clients.at(cur_op->client_id);
if (cl->hs_result.peer_is_osd)
{
// OSDs are not allowed to execute "primary" operations
finish_op(cur_op, -EPERM);
return false;
}
if (!st_cli.check_image_perm(cl->user_info, cur_op->req.rw.inode, (cur_op->req.hdr.opcode != OSD_OP_READ)))
{
finish_op(cur_op, -EPERM);
return false;
}
}
int stripe_count = (cur_op->req.hdr.opcode == OSD_OP_SCRUB ? 0 :
(pool_cfg.scheme == POOL_SCHEME_REPLICATED ? 1 : pg_it->second.pg_size));
int chain_size = 0;
@@ -96,7 +111,7 @@ bool osd_t::prepare_primary_rw(osd_op_t *cur_op)
if (inode_it->second.parent_id == cur_op->req.rw.inode ||
inode_it->second.parent_id == inode_it->second.num ||
chain_size > st_cli.inode_config.size() ||
chain_size > 255)
chain_size > UINT32_MAX)
{
printf("Inode %ju from pool %u has too many parents, returning EINVAL in response to read\n",
INODE_NO_POOL(cur_op->req.rw.inode), INODE_POOL(cur_op->req.rw.inode));
@@ -111,7 +126,7 @@ bool osd_t::prepare_primary_rw(osd_op_t *cur_op)
// Add the original inode
chain_size++;
chain_info_len = (cur_op->req.rw.flags & OSD_OP_RETURN_CHAIN
? (cur_op->req.rw.len / bs_bitmap_granularity)
? osd_op_rw_t::chain_info_bytes(chain_size) * (cur_op->req.rw.len / bs_bitmap_granularity)
: 0);
}
}
@@ -130,9 +145,7 @@ bool osd_t::prepare_primary_rw(osd_op_t *cur_op)
stripe_count * clean_entry_bitmap_size +
// - 'missing' flags for chained reads
(pool_cfg.scheme == POOL_SCHEME_REPLICATED ? 0 : pg_it->second.pg_size)
) +
// read chain info
chain_info_len
)
);
void *data_buf = (uint8_t*)op_data + sizeof(osd_primary_op_data_t);
op_data->pg_num = pg_num;
@@ -140,6 +153,7 @@ bool osd_t::prepare_primary_rw(osd_op_t *cur_op)
op_data->oid = oid;
op_data->stripes = (osd_rmw_stripe_t*)data_buf;
op_data->stripe_count = stripe_count;
op_data->chain_info = NULL;
data_buf = (uint8_t*)data_buf + sizeof(osd_rmw_stripe_t) * stripe_count;
cur_op->op_data = op_data;
if (cur_op->req.hdr.opcode != OSD_OP_SCRUB)
@@ -147,10 +161,17 @@ bool osd_t::prepare_primary_rw(osd_op_t *cur_op)
split_stripes(pg_data_size, bs_block_size, (uint32_t)(cur_op->req.rw.offset - oid.stripe), cur_op->req.rw.len, op_data->stripes);
// Resulting bitmaps have to survive op_data and be freed with the op itself
assert(!cur_op->bitmap_buf);
cur_op->bitmap_buf = calloc_or_die(1, clean_entry_bitmap_size * stripe_count);
cur_op->bitmap_buf = (uint8_t*)calloc_or_die(1, clean_entry_bitmap_size*stripe_count + chain_info_len);
uint8_t *buf = cur_op->bitmap_buf;
for (int i = 0; i < stripe_count; i++)
{
op_data->stripes[i].bmp_buf = (uint8_t*)cur_op->bitmap_buf + clean_entry_bitmap_size * i;
op_data->stripes[i].bmp_buf = buf;
buf += clean_entry_bitmap_size;
if (i == pg_data_size-1 && chain_info_len)
{
op_data->chain_info = buf;
buf += chain_info_len;
}
}
}
op_data->chain_size = chain_size;
@@ -164,11 +185,6 @@ bool osd_t::prepare_primary_rw(osd_op_t *cur_op)
data_buf = (uint8_t*)data_buf + chain_size * stripe_count * clean_entry_bitmap_size;
op_data->missing_flags = (uint8_t*)data_buf;
data_buf = (uint8_t*)data_buf + chain_size * (pool_cfg.scheme == POOL_SCHEME_REPLICATED ? 0 : pg_it->second.pg_size);
if (chain_info_len)
{
op_data->chain_info = (uint8_t*)data_buf;
data_buf = (uint8_t*)data_buf + chain_info_len;
}
// Copy chain
int chain_num = 0;
op_data->read_chain[chain_num] = cur_op->req.rw.inode;
@@ -319,6 +335,7 @@ resume_2:
}
cur_op->reply.rw.version = op_data->fact_ver;
cur_op->reply.rw.bitmap_len = (pg ? pg->pg_data_size : 1) * clean_entry_bitmap_size;
cur_op->bitmap = op_data->stripes[0].bmp_buf;
if (op_data->degraded)
{
// Reconstruct missing stripes
@@ -331,7 +348,6 @@ resume_2:
{
reconstruct_stripes_ec(stripes, pg->pg_size, pg->pg_data_size, clean_entry_bitmap_size);
}
cur_op->iov.push_back(op_data->stripes[0].bmp_buf, cur_op->reply.rw.bitmap_len);
for (int role = 0; role < pg->pg_size; role++)
{
if (stripes[role].req_end != 0)
@@ -346,7 +362,6 @@ resume_2:
}
else
{
cur_op->iov.push_back(op_data->stripes[0].bmp_buf, cur_op->reply.rw.bitmap_len);
cur_op->iov.push_back(cur_op->buf, cur_op->req.rw.len);
}
finish_op(cur_op, cur_op->req.rw.len);
+1 -1
View File
@@ -56,7 +56,7 @@ struct osd_primary_op_data_t
int chain_size;
osd_chain_read_t *chain_reads;
int chain_read_count;
uint8_t *chain_info;
void *chain_info;
};
};
};
+10 -3
View File
@@ -582,11 +582,10 @@ void osd_t::send_chained_read_results(pg_t *pg, osd_op_t *cur_op)
}
}
// Send bitmap
cur_op->bitmap = op_data->stripes[0].bmp_buf;
cur_op->reply.rw.bitmap_len = (pg ? pg->pg_data_size : 1) * clean_entry_bitmap_size;
cur_op->iov.push_back(op_data->stripes[0].bmp_buf, cur_op->reply.rw.bitmap_len);
if (cur_op->req.rw.flags & OSD_OP_RETURN_CHAIN)
{
cur_op->iov.push_back(op_data->chain_info, (cur_op->req.rw.len / bs_bitmap_granularity));
cur_op->reply.rw.bitmap_len += (cur_op->req.rw.len / bs_bitmap_granularity);
}
// And finally compose the result
@@ -596,6 +595,7 @@ void osd_t::send_chained_read_results(pg_t *pg, osd_op_t *cur_op)
int prev = (cur_op->req.rw.offset - op_data->oid.stripe) / bs_bitmap_granularity;
int end = prev + cur_op->req.rw.len/bs_bitmap_granularity;
int cur = prev;
size_t key_index_bytes = osd_op_rw_t::chain_info_bytes(op_data->chain_size);
while (cur <= end)
{
bool has_bit = false;
@@ -607,7 +607,14 @@ void osd_t::send_chained_read_results(pg_t *pg, osd_op_t *cur_op)
if (has_bit)
{
if (op_data->chain_info)
op_data->chain_info[cur] = pos;
{
if (key_index_bytes == 1)
((uint8_t*)op_data->chain_info)[cur] = pos;
else if (key_index_bytes == 2)
((uint16_t*)op_data->chain_info)[cur] = pos;
else
((uint32_t*)op_data->chain_info)[cur] = pos;
}
break;
}
}
+15
View File
@@ -91,6 +91,21 @@ static void scan_lists(std::vector<unclean_list_t> & lists, uint64_t limit, desc
// Describe unclean objects
void osd_t::continue_primary_describe(osd_op_t *cur_op)
{
if (use_auth)
{
osd_client_t *cl = msgr.clients.at(cur_op->client_id);
if (cl->hs_result.peer_is_osd)
{
// OSDs are not allowed to execute "primary" operations
finish_op(cur_op, -EPERM);
return;
}
if (cl->user_info->type != user_type_t::ADMIN)
{
finish_op(cur_op, -EPERM);
return;
}
}
auto & desc = cur_op->req.describe;
if (!desc.object_state)
desc.object_state = ~desc.object_state;
+2 -2
View File
@@ -3,8 +3,6 @@
#include "osd_primary.h"
#define SELF_CLIENT 0
void osd_t::autosync()
{
if (immediate_commit != IMMEDIATE_ALL && !autosync_op)
@@ -83,6 +81,8 @@ void osd_t::finish_op(osd_op_t *cur_op, int retval)
rm_inflight(pg);
}
assert(!cur_op->op_data->subops);
free(cur_op->op_data);
cur_op->op_data = NULL;
}
cur_op->reply.hdr.magic = SECONDARY_OSD_REPLY_MAGIC;
cur_op->reply.hdr.id = cur_op->req.hdr.id;
+10
View File
@@ -8,6 +8,16 @@ void osd_t::continue_primary_sync(osd_op_t *cur_op)
{
if (!cur_op->op_data)
{
if (use_auth && cur_op->client_id != SELF_CLIENT)
{
osd_client_t *cl = msgr.clients.at(cur_op->client_id);
if (cl->hs_result.peer_is_osd)
{
// OSDs are not allowed to execute "primary" operations
finish_op(cur_op, -EPERM);
return;
}
}
cur_op->op_data = (osd_primary_op_data_t*)calloc_or_die(1, sizeof(osd_primary_op_data_t));
}
osd_primary_op_data_t *op_data = cur_op->op_data;
+1 -3
View File
@@ -3,8 +3,6 @@
#include "osd_primary.h"
#define SELF_CLIENT 0
void osd_t::scrub_list(pool_pg_num_t pg_id, osd_num_t role_osd, object_id min_oid)
{
pool_id_t pool_id = pg_id.pool_id;
@@ -413,7 +411,7 @@ void osd_t::submit_scrub_subops(osd_op_t *cur_op)
}
}
assert(!cur_op->bitmap_buf);
cur_op->bitmap_buf = calloc_or_die(1, clean_entry_bitmap_size * op_data->stripe_count);
cur_op->bitmap_buf = (uint8_t*)calloc_or_die(1, clean_entry_bitmap_size * op_data->stripe_count);
for (int i = 0; i < op_data->stripe_count; i++)
{
op_data->stripes[i].bmp_buf = (uint8_t*)cur_op->bitmap_buf + clean_entry_bitmap_size * i;
+35 -3
View File
@@ -1,12 +1,13 @@
// Copyright (c) Vitaliy Filippov, 2019+
// License: VNPL-1.1 (see README.md for details)
#include "json11/json11.hpp"
#include "osd.h"
#ifdef WITH_RDMA
#include "msgr_rdma.h"
#endif
#include "json11/json11.hpp"
#include "openssl_util.h"
void osd_t::secondary_op_callback(osd_op_t *op)
{
@@ -112,6 +113,31 @@ bool osd_t::sec_check_pg_lock(osd_num_t primary_osd, const object_id &oid, uint3
void osd_t::exec_secondary_real(osd_op_t *cur_op)
{
osd_client_t *cl = msgr.clients.at(cur_op->client_id);
if (use_auth && !cl->hs_result.peer_is_osd)
{
// Non-OSDs are not allowed to execute "secondary" operations except LIST
bool allowed = false;
if (cur_op->req.hdr.opcode == OSD_OP_SEC_LIST)
{
if (cl->user_info->type == user_type_t::ADMIN)
{
// Admin is allowed to execute arbitrary listings
allowed = true;
}
else if (cl->user_info->type == user_type_t::CLIENT && cur_op->req.sec_list.min_inode &&
cur_op->req.sec_list.min_inode == cur_op->req.sec_list.max_inode)
{
// Clients are only allowed to execute listings for readable inodes
allowed = st_cli.check_image_perm(cl->user_info, cur_op->req.sec_list.min_inode, false);
}
}
if (!allowed)
{
finish_op(cur_op, -EPERM);
return;
}
}
if (cur_op->req.hdr.opcode == OSD_OP_SEC_LIST &&
(cur_op->req.sec_list.flags & OSD_LIST_PRIMARY))
{
@@ -128,7 +154,6 @@ void osd_t::exec_secondary_real(osd_op_t *cur_op)
exec_sec_lock(cur_op);
return;
}
osd_client_t *cl = msgr.clients.at(cur_op->client_id);
cur_op->bs_op = new blockstore_op_t();
cur_op->bs_op->callback = [this, cur_op](blockstore_op_t* bs_op) { secondary_op_callback(cur_op); };
cur_op->bs_op->opcode = (cur_op->req.hdr.opcode == OSD_OP_SEC_READ ? BS_OP_READ
@@ -348,6 +373,13 @@ void osd_t::exec_show_config(osd_op_t *cur_op)
cl->read_op_id = cur_op->req.hdr.id + 1;
}
auto features = json11::Json::object{ { "pg_locks", true } };
#ifdef WITH_RDMA
if (msgr.is_rdma_enabled() && req_json["features"]["rdma_close_tcp"].bool_value())
{
cl->rdma_close_tcp = true;
features["rdma_close_tcp"] = true;
}
#endif
if (msgr.use_proto_checksums)
{
auto peer_csums = req_json["features"]["proto_checksums"].uint64_value();
-8
View File
@@ -62,14 +62,6 @@ target_link_libraries(test_cas
vitastor_client
)
# http_hello
add_executable(http_hello
http_hello.cpp
)
target_link_libraries(http_hello
vitastor_client
)
# test_crc32
add_executable(test_crc32
test_crc32.cpp
-61
View File
@@ -1,61 +0,0 @@
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>
#include <stdexcept>
#include "http_client.h"
#include "ringloop.h"
#include "epoll_manager.h"
#include "addr_util.h"
int main(int narg, char *args[])
{
ring_consumer_t looper;
ring_loop_t *ringloop = new ring_loop_t(RINGLOOP_DEFAULT_SIZE);
epoll_manager_t *epmgr = new epoll_manager_t(ringloop);
// Accept new connections
int listen_fd = create_and_bind_socket("0.0.0.0", 8085, 128, NULL);
fcntl(listen_fd, F_SETFL, fcntl(listen_fd, F_GETFL, 0) | O_NONBLOCK);
std::string error;
auto http_ctx = http_context_init(epmgr->tfd, "", "", "", false, error);
epmgr->set_fd_handler(listen_fd, false, [http_ctx](int listen_fd, int events)
{
sockaddr_storage addr;
socklen_t peer_addr_size = sizeof(addr);
int peer_fd;
while ((peer_fd = accept(listen_fd, (sockaddr*)&addr, &peer_addr_size)) >= 0)
{
assert(peer_fd != 0);
fcntl(peer_fd, F_SETFL, fcntl(peer_fd, F_GETFL, 0) | O_NONBLOCK);
int one = 1;
setsockopt(peer_fd, SOL_TCP, TCP_NODELAY, &one, sizeof(one));
auto co = http_init(http_ctx);
http_serve(co, peer_fd, (http_options_t){}, [co](http_message_t *msg)
{
if (msg->eof)
{
http_destroy(co);
return;
}
http_reply(co, "HTTP/1.1 200 OK\r\nConnection: keep-alive\r\nContent-Length: 13\r\n\r\nHello, world!");
});
}
});
while (true)
{
ringloop->loop();
ringloop->wait();
}
delete epmgr;
delete ringloop;
return 0;
}
+1 -1
View File
@@ -26,7 +26,7 @@ void osd_messenger_t::outbox_push(osd_op_t *cur_op)
cl->sent_ops[cur_op->req.hdr.id] = cur_op;
}
void osd_messenger_t::parse_config(const json11::Json & config)
void osd_messenger_t::parse_config(const json11::Json & config, bool init)
{
}
+1 -1
View File
@@ -39,7 +39,7 @@ int main(int narg, char *args[])
msgr->repeer_pgs = [](osd_num_t) {};
msgr->exec_op = [msgr](osd_op_t *op) { stub_exec_op(msgr, op); };
json11::Json config = json11::Json::object { { "log_level", 1 } };
msgr->parse_config(config);
msgr->parse_config(config, true);
// Accept new connections
int listen_fd = create_and_bind_socket("0.0.0.0", 11203, 128, NULL);
fcntl(listen_fd, F_SETFL, fcntl(listen_fd, F_GETFL, 0) | O_NONBLOCK);
+5 -11
View File
@@ -1,9 +1,7 @@
// Copyright (c) Vitaliy Filippov, 2019+
// License: VNPL-1.1 (see README.md for details)
#ifdef WITH_OPENSSL
#include <openssl/rand.h>
#endif
#include <stdio.h>
#include <stdlib.h>
@@ -556,7 +554,6 @@ void test_writeback_merge()
printf("[ok] writeback merge test\n");
}
#ifdef WITH_OPENSSL
void test_msgr_encrypt()
{
const size_t sz = 1048576;
@@ -595,7 +592,7 @@ void test_msgr_encrypt()
enc->start(key, 4096 * 114, 4096);
in_pos = out_pos = 0;
enc->update(src+4096, 4096, crypt2, 4095, in_pos, out_pos);
assert(in_pos == 4096);
assert(in_pos == 4095);
assert(out_pos == 4095);
enc->update(src+4096+in_pos, 4096-in_pos, crypt2+out_pos, 4096-out_pos, in_pos, out_pos);
assert(in_pos == 4096);
@@ -626,10 +623,10 @@ void test_msgr_encrypt()
assert(in_pos == 3000);
assert(out_pos == 0);
dec->update(crypt+4096+3000, 3000, decrypt, 500, in_pos, out_pos);
assert(in_pos == 4096);
assert(in_pos == 4095);
assert(out_pos == 500);
dec->update(crypt+4096+in_pos, 6000-in_pos, decrypt+out_pos, 3000, in_pos, out_pos);
assert(in_pos == 4096);
assert(in_pos == 4095);
assert(out_pos == 3500);
dec->update(crypt+4096+in_pos, 6000-in_pos, decrypt+out_pos, 1000, in_pos, out_pos);
assert(in_pos == 4096);
@@ -638,10 +635,10 @@ void test_msgr_encrypt()
assert(in_pos == 6000);
assert(out_pos == 4096);
dec->update(crypt+4096+in_pos, 8192-in_pos, decrypt+out_pos, 4500-out_pos, in_pos, out_pos);
assert(in_pos == 8192);
assert(in_pos == 8191);
assert(out_pos == 4500);
dec->update(crypt+4096+in_pos, 8192-in_pos, decrypt+out_pos, 7500-out_pos, in_pos, out_pos);
assert(in_pos == 8192);
assert(in_pos == 8191);
assert(out_pos == 7500);
dec->update(crypt+4096+in_pos, 8192-in_pos, decrypt+out_pos, 8192-out_pos, in_pos, out_pos);
assert(in_pos == 8192);
@@ -725,7 +722,6 @@ void test_msgr_decrypt_chain()
free(src);
printf("[ok] msgr aes-xts chained decrypt\n");
}
#endif
void test_vault()
{
@@ -794,10 +790,8 @@ int main(int narg, char *args[])
test2();
test_writeback();
test_writeback_merge();
#ifdef WITH_OPENSSL
test_msgr_encrypt();
test_msgr_decrypt_chain();
#endif
test_vault();
return 0;
}
+2 -1
View File
@@ -1893,7 +1893,8 @@ void test_big_intent_csums()
heap.finish_recheck();
auto mod = heap.get_recheck_modified_blocks();
assert(mod.size() == 0);
assert(mod.size() == 1);
assert(mod[0] == 0);
// read object 1 - big_intent should be there
object_id oid = { .inode = INODE_WITH_POOL(1, 1), .stripe = 0 };
+114
View File
@@ -0,0 +1,114 @@
// Copyright (c) Vitaliy Filippov, 2019+
// License: VNPL-1.1 or GNU GPL-2.0+ (see README.md for details)
#include "openssl_util.h"
#include "str_util.h"
#include <openssl/ssl.h>
X509 *openssl_load_cert(const std::string & file_or_pem)
{
std::string pem;
BIO *bio = NULL;
if (file_or_pem.substr(0, 5) != "-----")
{
pem = read_file(file_or_pem);
bio = BIO_new_mem_buf(pem.data(), pem.size());
}
else
bio = BIO_new_mem_buf(file_or_pem.data(), file_or_pem.size());
if (!bio)
return NULL;
X509 *x509 = PEM_read_bio_X509(bio, NULL, 0, NULL);
BIO_free(bio);
return x509;
}
EVP_PKEY *openssl_load_key(const std::string & file_or_pem)
{
std::string pem;
BIO *bio = NULL;
if (file_or_pem.substr(0, 5) != "-----")
{
pem = read_file(file_or_pem);
bio = BIO_new_mem_buf(pem.data(), pem.size());
}
else
bio = BIO_new_mem_buf(file_or_pem.data(), file_or_pem.size());
if (!bio)
return NULL;
EVP_PKEY *pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
BIO_free(bio);
return pkey;
}
bool openssl_ctx_add_ca(SSL_CTX *ssl_ctx, const std::string & file_or_pem)
{
X509 *cert = openssl_load_cert(file_or_pem);
bool ok = !!cert;
if (cert)
{
X509_STORE *store = SSL_CTX_get_cert_store(ssl_ctx);
X509_STORE_add_cert(store, cert);
X509_free(cert);
}
return ok;
}
bool openssl_ctx_use_ca(SSL_CTX *ssl_ctx, const std::string & file_or_pem)
{
if (file_or_pem.substr(0, 5) == "-----")
{
return openssl_ctx_add_ca(ssl_ctx, file_or_pem);
}
return file_or_pem.empty()
? !!SSL_CTX_set_default_verify_paths(ssl_ctx)
: !!SSL_CTX_load_verify_locations(ssl_ctx, file_or_pem.c_str(), NULL);
}
std::string openssl_get_cn(X509 *x509)
{
X509_NAME* subj = X509_get_subject_name(x509);
int pos = X509_NAME_get_index_by_NID(subj, NID_commonName, -1);
if (pos != -1)
{
X509_NAME_ENTRY* cn = X509_NAME_get_entry(subj, pos);
ASN1_STRING* str = X509_NAME_ENTRY_get_data(cn);
return std::string((const char*)ASN1_STRING_get0_data(str), ASN1_STRING_length(str));
}
return "";
}
bool openssl_ctx_use_cert(SSL_CTX *ssl_ctx, const std::string & file_or_pem, std::string & common_name)
{
X509 *cert = openssl_load_cert(file_or_pem);
bool ok = false;
if (cert)
{
common_name = openssl_get_cn(cert);
ok = SSL_CTX_use_certificate(ssl_ctx, cert);
X509_free(cert);
}
return ok;
}
bool openssl_ctx_use_key(SSL_CTX *ssl_ctx, const std::string & file_or_pem)
{
EVP_PKEY *pkey = openssl_load_key(file_or_pem);
bool ok = false;
if (pkey)
{
ok = SSL_CTX_use_PrivateKey(ssl_ctx, pkey);
EVP_PKEY_free(pkey);
}
return ok;
}
bool openssl_bio_nonempty(BIO *bio)
{
// SSL_ERROR_WANT_WRITE is absolutely non-informative with memory BIO, it basically never happens
// So we have to check memory BIO for outstanding data
char *bio_buf = NULL;
size_t bio_sz = BIO_get_mem_data(bio, &bio_buf);
return bio_sz > 0;
}
+19
View File
@@ -0,0 +1,19 @@
// Copyright (c) Vitaliy Filippov, 2019+
// License: VNPL-1.1 or GNU GPL-2.0+ (see README.md for details)
#pragma once
#include <string>
#ifdef WITH_OPENSSL
#include <openssl/types.h>
#endif
X509 *openssl_load_cert(const std::string & file_or_pem);
EVP_PKEY *openssl_load_key(const std::string & file_or_pem);
bool openssl_ctx_add_ca(SSL_CTX *ssl_ctx, const std::string & file_or_pem);
bool openssl_ctx_use_ca(SSL_CTX *ssl_ctx, const std::string & file_or_pem);
std::string openssl_get_cn(X509 *x509);
bool openssl_ctx_use_cert(SSL_CTX *ssl_ctx, const std::string & file_or_pem, std::string & common_name);
bool openssl_ctx_use_key(SSL_CTX *ssl_ctx, const std::string & file_or_pem);
bool openssl_bio_nonempty(BIO *bio);
+9 -4
View File
@@ -535,12 +535,12 @@ std::string urldecode(const std::string & orig)
return res;
}
size_t fromhexstr(const std::string & from, size_t bytes, uint8_t *to)
size_t fromhexstr(const char *from, size_t from_len, uint8_t *to, size_t to_len)
{
if (bytes > from.size()/2)
bytes = from.size()/2;
if (to_len > from_len/2)
to_len = from_len/2;
size_t i = 0;
while (i < bytes)
while (i < to_len)
{
uint8_t x = fromhexchar(from[2*i], 16);
uint8_t y = fromhexchar(from[2*i+1], 16);
@@ -552,6 +552,11 @@ size_t fromhexstr(const std::string & from, size_t bytes, uint8_t *to)
return i;
}
size_t fromhexstr(const std::string & from, size_t bytes, uint8_t *to)
{
return fromhexstr(from.data(), from.size(), to, bytes);
}
std::string tohexstr(const uint8_t *from, size_t bytes)
{
std::string res;
+1
View File
@@ -35,6 +35,7 @@ std::string realpath_str(std::string path, bool nofail = true);
std::string format_datetime(uint64_t unixtime);
bool is_zero(void *buf, size_t size);
std::string urldecode(const std::string & orig);
size_t fromhexstr(const char *from, size_t from_len, uint8_t *to, size_t to_len);
size_t fromhexstr(const std::string & from, size_t bytes, uint8_t *to);
std::string tohexstr(const uint8_t *from, size_t bytes);
bool ishexstr(const std::string & str);
+2
View File
@@ -34,6 +34,8 @@ extern "C" {
# define XXH_NOESCAPE
#endif
#define XXH_SECRET_DEFAULT_SIZE 192
typedef enum {
XXH_OK = 0,
XXH_ERROR
+22
View File
@@ -27,6 +27,8 @@ ETCD_COUNT=${ETCD_COUNT:-1}
ANTIETCD=${ANTIETCD}
USE_RAMDISK=${USE_RAMDISK}
ETCD_SCHEME=${ETCD_SCHEME:-http}
OSD_TLS=${OSD_TLS:-1}
RDMA=${RDMA:-0}
RAMDISK=/run/user/$(id -u)
findmnt $RAMDISK >/dev/null || (sudo mkdir -p $RAMDISK && sudo mount -t tmpfs tmpfs $RAMDISK)
@@ -124,6 +126,26 @@ VITASTOR_CFG='"etcd_address":"'$ETCD_URL'"'"$VITASTOR_CFG"
if [[ "$ETCD_SCHEME" = "https" ]]; then
VITASTOR_CFG="$VITASTOR_CFG"',"etcd_ca":"'$(pwd)'/testdata/etcd.crt"'
fi
VITASTOR_CFG="$VITASTOR_CFG"',"osd_network":"'$ETCD_IP'/32"'
VITASTOR_CFG="$VITASTOR_CFG"',"use_rdma":"'$RDMA'"'
if [[ "$OSD_TLS" = "1" ]]; then
cd ./testdata
openssl req -days 3650 -x509 -new -newkey rsa:4096 -nodes -keyout client_ca.key -out client_ca.crt \
-subj '/C=RU/ST=Russia/L=Moscow/O=VitastorClientCA'
openssl req -days 3650 -x509 -new -newkey rsa:4096 -nodes -keyout osd.key -out osd.crt \
-subj '/C=RU/ST=Russia/L=Moscow/O=VitastorOSD' -addext "extendedKeyUsage = serverAuth, clientAuth"
openssl req -subj '/CN=test' -nodes -new -keyout cli.key -out cli.csr -addext "extendedKeyUsage = clientAuth"
openssl x509 -req -days 3650 -CA client_ca.crt -CAkey client_ca.key -CAcreateserial -in cli.csr -out cli.crt
rm cli.csr
cd $(dirname $0)/..
VITASTOR_CFG="$VITASTOR_CFG"',"osd_cert":"'$(pwd)'/testdata/osd.crt"'
VITASTOR_CFG="$VITASTOR_CFG"',"osd_pkey":"'$(pwd)'/testdata/osd.key"'
VITASTOR_CFG="$VITASTOR_CFG"',"osd_ca":"'$(pwd)'/testdata/osd.crt"'
VITASTOR_CFG="$VITASTOR_CFG"',"client_ca":"'$(pwd)'/testdata/client_ca.crt"'
VITASTOR_CFG="$VITASTOR_CFG"',"cert":"'$(pwd)'/testdata/cli.crt"'
VITASTOR_CFG="$VITASTOR_CFG"',"pkey":"'$(pwd)'/testdata/cli.key"'
VITASTOR_CFG="$VITASTOR_CFG"',"use_auth":false'
fi
echo "{$VITASTOR_CFG}" > ./testdata/vitastor.conf
VITASTOR_CFG=./testdata/vitastor.conf
VITASTOR_CLI="build/src/cmd/vitastor-cli --config_path $VITASTOR_CFG"
+9 -1
View File
@@ -40,7 +40,7 @@ start_osd_on()
{
local i=$1
local dev=$2
build/src/osd/vitastor-osd --osd_num $i --bind_address $ETCD_IP $NO_SAME $OSD_ARGS \
build/src/osd/vitastor-osd --osd_num $i $NO_SAME $OSD_ARGS \
$(build/src/disk_tool/vitastor-disk simple-offsets --format options $OFFSET_ARGS $dev $OFFSET_ARGS 2>/dev/null) \
>>./testdata/osd$i.log 2>&1 &
eval OSD${i}_PID=$!
@@ -119,6 +119,14 @@ if [[ $OSD_COUNT -gt 0 ]]; then
wait_up 120
fi
if [[ "$RDMA" = 1 ]]; then
for i in $(seq 1 $OSD_COUNT); do
if ! grep 'RDMA initialized successfully' ./testdata/osd$i.log; then
format_error "OSD $i failed to initialize RDMA, please try to enable rdma-rxe on the host"
fi
done
fi
try_reweight()
{
osd=$1
+1
View File
@@ -100,6 +100,7 @@ PG_SIZE=2 ./test_heal.sh
TEST_NAME=local_read POOLCFG='"local_reads":"random",' ./test_heal.sh
SCHEME=ec ./test_heal.sh
ANTIETCD=1 ./test_heal.sh
TEST_NAME=ec_rdma RDMA=1 SCHEME=ec ./test_heal.sh
./test_checksum.sh
TEST_NAME=xxhash OSD_ARGS="--data_csum_type xxh3_32" ./test_checksum.sh
+1 -1
View File
@@ -62,7 +62,7 @@ if ! diff -q ./testdata/bin/read.bin ./testdata/bin/mirror.bin; then
format_error Data lost during self-heal
fi
if grep -qP 'Checksum mismatch|BUG' ./testdata/osd*.log; then
if grep -qP 'Checksum mismatch|BUG|Received garbage' ./testdata/osd*.log; then
format_error Checksum mismatches or BUGs detected during test
fi
+1 -1
View File
@@ -9,7 +9,7 @@ OSD_COUNT=5
OSD_ARGS="$OSD_ARGS"
for i in $(seq 1 $OSD_COUNT); do
dd if=/dev/zero of=./testdata/bin/test_osd$i.bin bs=1024 count=1 seek=$((OSD_SIZE*1024-1))
build/src/osd/vitastor-osd --log_level 10 --osd_num $i --bind_address 127.0.0.1 --etcd_stats_interval 5 $OSD_ARGS \
build/src/osd/vitastor-osd --log_level 10 --osd_num $i --etcd_stats_interval 5 $OSD_ARGS \
$(build/src/disk_tool/vitastor-disk simple-offsets --format options ./testdata/bin/test_osd$i.bin $OFFSET_ARGS 2>/dev/null) >>./testdata/osd$i.log 2>&1 &
eval OSD${i}_PID=$!
done
+1 -1
View File
@@ -70,7 +70,7 @@ done
$ETCDCTL del --prefix /vitastor/osd/state/
for i in $(seq 1 $OSD_COUNT); do
build/src/osd/vitastor-osd --osd_num $i --bind_address 127.0.0.1 $NO_SAME $OSD_ARGS \
build/src/osd/vitastor-osd --osd_num $i $NO_SAME $OSD_ARGS \
--meta_format $meta_format \
--data_device ./testdata/bin/test_osd$i.bin \
--meta_offset 0 \
+1 -1
View File
@@ -21,7 +21,7 @@ $VITASTOR_FIO -bs=4k -direct=1 -iodepth=1 -fsync=1 \
# Kill OSD 2, start OSD 1
kill $OSD2_PID
build/src/osd/vitastor-osd --osd_num 1 --bind_address 127.0.0.1 $NO_SAME $OSD_ARGS \
build/src/osd/vitastor-osd --osd_num 1 $NO_SAME $OSD_ARGS \
$(build/src/disk_tool/vitastor-disk simple-offsets --format options --device ./testdata/bin/test_osd2.bin $OFFSET_ARGS 2>/dev/null) >>./testdata/osd2.log 2>&1 &
sleep 2
+1 -1
View File
@@ -15,7 +15,7 @@ OSD_COUNT=3
OSD_ARGS="$OSD_ARGS"
OFFSET_ARGS="$OFFSET_ARGS"
for i in $(seq 1 $OSD_COUNT); do
build/src/osd/vitastor-osd --osd_num $i --bind_address 127.0.0.1 $OSD_ARGS \
build/src/osd/vitastor-osd --osd_num $i $OSD_ARGS \
$(build/src/disk_tool/vitastor-disk simple-offsets --format options ./testdata/bin/test_osd$i.bin $OFFSET_ARGS 2>/dev/null) >>./testdata/osd$i.log 2>&1 &
eval OSD${i}_PID=$!
done