summaryrefslogtreecommitdiff
path: root/include
diff options
context:
space:
mode:
Diffstat (limited to 'include')
-rw-r--r--include/CMakeLists.txt1
-rw-r--r--include/ouroboros/CMakeLists.txt24
-rw-r--r--include/ouroboros/bitmap.h8
-rw-r--r--include/ouroboros/btree.h2
-rw-r--r--include/ouroboros/cdefs.h2
-rw-r--r--include/ouroboros/cep.h (renamed from include/ouroboros/cacep.h)25
-rw-r--r--include/ouroboros/crc32.h8
-rw-r--r--include/ouroboros/crypt.h368
-rw-r--r--include/ouroboros/dev.h5
-rw-r--r--include/ouroboros/endian.h11
-rw-r--r--include/ouroboros/errno.h8
-rw-r--r--include/ouroboros/fccntl.h2
-rw-r--r--include/ouroboros/flow.h58
-rw-r--r--include/ouroboros/fqueue.h5
-rw-r--r--include/ouroboros/hash.h80
-rw-r--r--include/ouroboros/ipcp-dev.h51
-rw-r--r--include/ouroboros/ipcp.h338
-rw-r--r--include/ouroboros/irm.h33
-rw-r--r--include/ouroboros/list.h4
-rw-r--r--include/ouroboros/local-dev.h16
-rw-r--r--include/ouroboros/lockfile.h8
-rw-r--r--include/ouroboros/logs.h107
-rw-r--r--include/ouroboros/md5.h2
-rw-r--r--include/ouroboros/name.h50
-rw-r--r--include/ouroboros/notifier.h2
-rw-r--r--include/ouroboros/np1_flow.h29
-rw-r--r--include/ouroboros/proc.h45
-rw-r--r--include/ouroboros/proto.h2
-rw-r--r--include/ouroboros/protobuf.h130
-rw-r--r--include/ouroboros/pthread.h20
-rw-r--r--include/ouroboros/qos.h87
-rw-r--r--include/ouroboros/qoscube.h8
-rw-r--r--include/ouroboros/random.h8
-rw-r--r--include/ouroboros/rib.h8
-rw-r--r--include/ouroboros/serdes-irm.h80
-rw-r--r--include/ouroboros/serdes-oep.h69
-rw-r--r--include/ouroboros/sha3.h2
-rw-r--r--include/ouroboros/shm_rdrbuff.h67
-rw-r--r--include/ouroboros/sockets.h.in51
-rw-r--r--include/ouroboros/ssm_flow_set.h (renamed from include/ouroboros/shm_flow_set.h)37
-rw-r--r--include/ouroboros/ssm_pk_buff.h (renamed from include/ouroboros/shm_du_buff.h)32
-rw-r--r--include/ouroboros/ssm_pool.h74
-rw-r--r--include/ouroboros/ssm_rbuff.h (renamed from include/ouroboros/shm_rbuff.h)39
-rw-r--r--include/ouroboros/time.h (renamed from include/ouroboros/time_utils.h)37
-rw-r--r--include/ouroboros/tpm.h8
-rw-r--r--include/ouroboros/utils.h53
-rw-r--r--include/ouroboros/version.h.in2
-rw-r--r--include/test/certs.h125
-rw-r--r--include/test/certs_pqc.h656
-rw-r--r--include/test/test.h109
50 files changed, 2506 insertions, 490 deletions
diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt
deleted file mode 100644
index 8895c582..00000000
--- a/include/CMakeLists.txt
+++ /dev/null
@@ -1 +0,0 @@
-add_subdirectory(ouroboros)
diff --git a/include/ouroboros/CMakeLists.txt b/include/ouroboros/CMakeLists.txt
deleted file mode 100644
index 8f248710..00000000
--- a/include/ouroboros/CMakeLists.txt
+++ /dev/null
@@ -1,24 +0,0 @@
-configure_file("${CMAKE_CURRENT_SOURCE_DIR}/version.h.in"
- "${CMAKE_CURRENT_BINARY_DIR}/version.h" @ONLY)
-
-set(SOCK_BUF_SIZE 10240 CACHE STRING
- "Size of the buffer used by the UNIX sockets for local IPC")
-
-configure_file("${CMAKE_CURRENT_SOURCE_DIR}/sockets.h.in"
- "${CMAKE_CURRENT_BINARY_DIR}/sockets.h" @ONLY)
-
-set(HEADER_FILES
- cacep.h
- cdefs.h
- dev.h
- errno.h
- fccntl.h
- fqueue.h
- ipcp.h
- irm.h
- proto.h
- qos.h
- ${CMAKE_CURRENT_BINARY_DIR}/version.h
- )
-
-install(FILES ${HEADER_FILES} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/ouroboros)
diff --git a/include/ouroboros/bitmap.h b/include/ouroboros/bitmap.h
index b557b3d1..04467a8a 100644
--- a/include/ouroboros/bitmap.h
+++ b/include/ouroboros/bitmap.h
@@ -1,5 +1,5 @@
/*
- * Ouroboros - Copyright (C) 2016 - 2021
+ * Ouroboros - Copyright (C) 2016 - 2024
*
* Bitmap implementation
*
@@ -20,8 +20,8 @@
* Foundation, Inc., http://www.fsf.org/about/contact/.
*/
-#ifndef OUROBOROS_BITMAP_H
-#define OUROBOROS_BITMAP_H
+#ifndef OUROBOROS_LIB_BITMAP_H
+#define OUROBOROS_LIB_BITMAP_H
#include <stddef.h>
#include <unistd.h>
@@ -45,4 +45,4 @@ bool bmp_is_id_valid(struct bmp * bmp,
bool bmp_is_id_used(struct bmp * bmp,
ssize_t id);
-#endif /* OUROBOROS_BITMAP_H */
+#endif /* OUROBOROS_LIB_BITMAP_H */
diff --git a/include/ouroboros/btree.h b/include/ouroboros/btree.h
index c692ae9e..cf982856 100644
--- a/include/ouroboros/btree.h
+++ b/include/ouroboros/btree.h
@@ -1,5 +1,5 @@
/*
- * Ouroboros - Copyright (C) 2016 - 2021
+ * Ouroboros - Copyright (C) 2016 - 2024
*
* B-trees
*
diff --git a/include/ouroboros/cdefs.h b/include/ouroboros/cdefs.h
index 5764fb41..f4a5dc65 100644
--- a/include/ouroboros/cdefs.h
+++ b/include/ouroboros/cdefs.h
@@ -1,5 +1,5 @@
/*
- * Ouroboros - Copyright (C) 2016 - 2021
+ * Ouroboros - Copyright (C) 2016 - 2024
*
* C Definitions
*
diff --git a/include/ouroboros/cacep.h b/include/ouroboros/cep.h
index 6c11b701..4c1737f0 100644
--- a/include/ouroboros/cacep.h
+++ b/include/ouroboros/cep.h
@@ -1,7 +1,7 @@
/*
- * Ouroboros - Copyright (C) 2016 - 2021
+ * Ouroboros - Copyright (C) 2016 - 2024
*
- * The Common Application Connection Establishment Protocol
+ * The Ouroboros Connection Establishment Protocol
*
* Dimitri Staessens <dimitri@ouroboros.rocks>
* Sander Vrijders <sander@ouroboros.rocks>
@@ -20,20 +20,19 @@
* Foundation, Inc., http://www.fsf.org/about/contact/.
*/
-#ifndef OUROBOROS_CACEP_H
-#define OUROBOROS_CACEP_H
+#ifndef OUROBOROS_CEP_H
+#define OUROBOROS_CEP_H
#include <ouroboros/cdefs.h>
#include <ouroboros/proto.h>
#include <stdint.h>
-#include <sys/types.h>
-#define CACEP_BUF_STRLEN 64
+#define OCEP_BUF_STRLEN 128
struct conn_info {
- char comp_name[CACEP_BUF_STRLEN + 1];
- char protocol[CACEP_BUF_STRLEN + 1];
+ char comp_name[OCEP_BUF_STRLEN + 1];
+ char protocol[OCEP_BUF_STRLEN + 1];
uint32_t pref_version;
enum proto_concrete_syntax pref_syntax;
struct proto_field fixed_conc_syntax[PROTO_MAX_FIELDS];
@@ -43,12 +42,12 @@ struct conn_info {
__BEGIN_DECLS
-int cacep_snd(int fd,
- const struct conn_info * in);
+int cep_snd(int fd,
+ const struct conn_info * in);
-int cacep_rcv(int fd,
- struct conn_info * out);
+int cep_rcv(int fd,
+ struct conn_info * out);
__END_DECLS
-#endif /* OUROBOROS_CACEP_H */
+#endif /* OUROBOROS_CEP_H */
diff --git a/include/ouroboros/crc32.h b/include/ouroboros/crc32.h
index 4a2abafa..eb610797 100644
--- a/include/ouroboros/crc32.h
+++ b/include/ouroboros/crc32.h
@@ -1,5 +1,5 @@
/*
- * Ouroboros - Copyright (C) 2016 - 2021
+ * Ouroboros - Copyright (C) 2016 - 2024
*
* 32-bit Cyclic Redundancy Check
*
@@ -20,8 +20,8 @@
* Foundation, Inc., http://www.fsf.org/about/contact/.
*/
-#ifndef OUROBOROS_CRC32_H
-#define OUROBOROS_CRC32_H
+#ifndef OUROBOROS_LIB_CRC32_H
+#define OUROBOROS_LIB_CRC32_H
#include <stdint.h>
#include <stddef.h>
@@ -32,4 +32,4 @@ void crc32(uint32_t * crc,
const void * buf,
size_t len);
-#endif /* OUROBOROS_CRC32_H */
+#endif /* OUROBOROS_LIB_CRC32_H */
diff --git a/include/ouroboros/crypt.h b/include/ouroboros/crypt.h
new file mode 100644
index 00000000..5b39fb5f
--- /dev/null
+++ b/include/ouroboros/crypt.h
@@ -0,0 +1,368 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2024
+ *
+ * Cryptography
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+#ifndef OUROBOROS_LIB_CRYPT_H
+#define OUROBOROS_LIB_CRYPT_H
+
+#include <ouroboros/ssm_pk_buff.h>
+#include <ouroboros/utils.h>
+
+#include <assert.h>
+
+#define IVSZ 16
+#define SYMMKEYSZ 32
+#define MAX_HASH_SIZE 64 /* SHA-512/BLAKE2b max */
+#define KEX_ALGO_BUFSZ 32
+#define KEX_CIPHER_BUFSZ 32
+#define MSGBUFSZ 2048
+
+/*
+ * On OSX the OpenSSL NIDs are automatically loaded with evp.h.
+ * Some have a different spelling. This header avoids the double definitions.
+ */
+
+ #define NID_undef 0
+
+/* Cipher NIDs (match OpenSSL values) */
+#define NID_aes_128_gcm 895
+#define NID_aes_192_gcm 898
+#define NID_aes_256_gcm 901
+#define NID_aes_128_ctr 904
+#define NID_aes_192_ctr 905
+#define NID_aes_256_ctr 906
+#define NID_chacha20_poly1305 1018
+
+ #if !defined (__APPLE__) || !defined ( HAVE_OPENSSL )
+/* KEX algorithm NIDs (match OpenSSL values) */
+#define NID_X9_62_prime256v1 415
+#define NID_secp384r1 715
+#define NID_secp521r1 716
+#define NID_X25519 1034
+#define NID_X448 1035
+#define NID_ffdhe2048 1126
+#define NID_ffdhe3072 1127
+#define NID_ffdhe4096 1128
+#endif /* __APPLE__ */
+#define NID_MLKEM512 1454
+#define NID_MLKEM768 1455
+#define NID_MLKEM1024 1456
+#define NID_X25519MLKEM768 2053 /* !! not in OpenSSL */
+#define NID_X448MLKEM1024 2054 /* !! not in OpenSSL */
+
+/* KDF NIDs (match OpenSSL values) */
+#define NID_hkdf 1036
+#define NID_sha256 672
+#define NID_sha384 673
+#define NID_sha512 674
+#if !defined (__APPLE__) || !defined ( HAVE_OPENSSL )
+#define NID_sha3_256 1096
+#define NID_sha3_384 1097
+#define NID_sha3_512 1098
+#endif /* __APPLE__ */
+#define NID_blake2b512 1056
+#define NID_blake2s256 1057
+
+
+#define IS_KEM_ALGORITHM(algo) \
+ (strstr(algo, "ML-KEM") != NULL || strstr(algo, "MLKEM") != NULL)
+
+#define IS_HYBRID_KEM(algo) \
+ ((strstr(algo, "X25519") != NULL || strstr(algo, "X448") != NULL) && \
+ strstr(algo, "MLKEM") != NULL)
+
+#define X25519MLKEM768_PKSZ 1216 /* 32 + 1184 */
+#define X25519MLKEM768_CTSZ 1120 /* 32 + 1088 */
+#define X25519MLKEM768_SKSZ 2432 /* 32 + 2400 */
+#define X448MLKEM1024_PKSZ 1624 /* 56 + 1568 */
+#define X448MLKEM1024_SKSZ 3224 /* 56 + 3168 */
+
+#define KEM_MODE_SERVER_ENCAP 0 /* Server encapsulates (default) */
+#define KEM_MODE_CLIENT_ENCAP 1 /* Client encapsulates */
+#define IS_KEX_ALGO_SET(cfg) ((cfg)->x.nid != NID_undef)
+#define IS_KEX_CIPHER_SET(cfg) ((cfg)->c.nid != NID_undef)
+
+
+struct crypt_sk {
+ int nid;
+ uint8_t * key;
+ uint8_t rot_bit; /* Rotation bit to control epoch */
+};
+
+struct sec_config {
+ struct {
+ const char * str;
+ int nid;
+ int mode;
+ } x; /* key exchange */
+ struct {
+ const char * str;
+ int nid;
+ } k; /* kdf */
+ struct {
+ const char * str;
+ int nid;
+ } c; /* cipher */
+ struct {
+ const char * str;
+ int nid;
+ } d; /* digest */
+};
+
+/* Helper macros to set sec_config fields consistently */
+#define SET_KEX_ALGO(cfg, algo_str) do { \
+ (cfg)->x.nid = kex_str_to_nid(algo_str); \
+ (cfg)->x.str = kex_nid_to_str((cfg)->x.nid); \
+ assert((cfg)->x.nid != NID_undef || (cfg)->x.str == NULL); \
+} while (0)
+
+#define SET_KEX_ALGO_NID(cfg, nid_val) do { \
+ (cfg)->x.nid = (nid_val); \
+ (cfg)->x.str = kex_nid_to_str((cfg)->x.nid); \
+ assert((cfg)->x.nid != NID_undef || (cfg)->x.str == NULL); \
+} while (0)
+
+#define SET_KEX_KEM_MODE(cfg, mode_val) do { \
+ (cfg)->x.mode = (mode_val); \
+} while (0)
+
+#define SET_KEX_KDF(cfg, kdf_str) do { \
+ (cfg)->k.nid = md_str_to_nid(kdf_str); \
+ (cfg)->k.str = md_nid_to_str((cfg)->k.nid); \
+ assert((cfg)->k.nid != NID_undef || (cfg)->k.str == NULL); \
+} while (0)
+
+#define SET_KEX_KDF_NID(cfg, nid_val) do { \
+ (cfg)->k.nid = (nid_val); \
+ (cfg)->k.str = md_nid_to_str((cfg)->k.nid); \
+ assert((cfg)->k.nid != NID_undef || (cfg)->k.str == NULL); \
+} while (0)
+
+#define SET_KEX_CIPHER(cfg, cipher_str) do { \
+ (cfg)->c.nid = crypt_str_to_nid(cipher_str); \
+ (cfg)->c.str = crypt_nid_to_str((cfg)->c.nid); \
+ assert((cfg)->c.nid != NID_undef || (cfg)->c.str == NULL); \
+} while (0)
+
+#define SET_KEX_CIPHER_NID(cfg, nid_val) do { \
+ (cfg)->c.nid = (nid_val); \
+ (cfg)->c.str = crypt_nid_to_str((cfg)->c.nid); \
+ assert((cfg)->c.nid != NID_undef || (cfg)->c.str == NULL); \
+} while (0)
+
+#define SET_KEX_DIGEST(cfg, digest_str) do { \
+ (cfg)->d.nid = md_str_to_nid(digest_str); \
+ (cfg)->d.str = md_nid_to_str((cfg)->d.nid); \
+ assert((cfg)->d.nid != NID_undef || (cfg)->d.str == NULL); \
+} while (0)
+
+#define SET_KEX_DIGEST_NID(cfg, nid_val) do { \
+ (cfg)->d.nid = (nid_val); \
+ (cfg)->d.str = md_nid_to_str((cfg)->d.nid); \
+ assert((cfg)->d.nid != NID_undef || (cfg)->d.str == NULL); \
+} while (0)
+
+#define CLEAR_KEX_ALGO(cfg) do { \
+ (cfg)->x.nid = NID_undef; \
+ (cfg)->x.str = NULL; \
+} while (0)
+
+#define CLEAR_KEX_KDF(cfg) do { \
+ (cfg)->k.nid = NID_undef; \
+ (cfg)->k.str = NULL; \
+} while (0)
+
+#define CLEAR_KEX_CIPHER(cfg) do { \
+ (cfg)->c.nid = NID_undef; \
+ (cfg)->c.str = NULL; \
+} while (0)
+
+#define CLEAR_KEX_DIGEST(cfg) do { \
+ (cfg)->d.nid = NID_undef; \
+ (cfg)->d.str = NULL; \
+} while (0)
+
+struct auth_ctx;
+struct crypt_ctx;
+
+struct auth_ctx * auth_create_ctx(void);
+
+void auth_destroy_ctx(struct auth_ctx * ctx);
+
+int auth_add_crt_to_store(struct auth_ctx * ctx,
+ void * crt);
+
+int auth_verify_crt(struct auth_ctx * ctx,
+ void * crt);
+
+int auth_sign(void * pkp,
+ int md_nid,
+ buffer_t msg,
+ buffer_t * sig);
+
+int auth_verify_sig(void * pk,
+ int md_nid,
+ buffer_t msg,
+ buffer_t sig);
+
+int load_sec_config_file(struct sec_config * cfg,
+ const char * path);
+
+int kex_pkp_create(struct sec_config * cfg,
+ void ** pkp,
+ uint8_t * pk);
+
+void kex_pkp_destroy(void * pkp);
+
+int kex_dhe_derive(struct sec_config * cfg,
+ void * pkp,
+ buffer_t pk,
+ uint8_t * s);
+
+ssize_t kex_kem_encap(buffer_t pk,
+ uint8_t * ct,
+ int kdf_nid,
+ uint8_t * s);
+
+ssize_t kex_kem_encap_raw(buffer_t pk,
+ uint8_t * ct,
+ int kdf_nid,
+ uint8_t * s);
+
+int kex_kem_decap(void * pkp,
+ buffer_t ct,
+ int kdf_nid,
+ uint8_t * s);
+
+int kex_get_algo_from_pk_der(buffer_t pk,
+ char * algo);
+
+int kex_get_algo_from_pk_raw(buffer_t pk,
+ char * algo);
+
+int kex_validate_algo(const char * algo);
+
+int kex_validate_nid(int nid);
+
+const char * kex_nid_to_str(uint16_t nid);
+
+uint16_t kex_str_to_nid(const char * algo);
+
+struct crypt_ctx * crypt_create_ctx(struct crypt_sk * sk);
+
+void crypt_destroy_ctx(struct crypt_ctx * ctx);
+
+int crypt_validate_nid(int nid);
+
+const char * crypt_nid_to_str(uint16_t nid);
+
+uint16_t crypt_str_to_nid(const char * cipher);
+
+int md_validate_nid(int nid);
+
+const char * md_nid_to_str(uint16_t nid);
+
+uint16_t md_str_to_nid(const char * kdf);
+
+ssize_t md_digest(int md_nid,
+ buffer_t in,
+ uint8_t * out);
+
+ssize_t md_len(int md_nid);
+
+int crypt_encrypt(struct crypt_ctx * ctx,
+ buffer_t in,
+ buffer_t * out);
+
+int crypt_decrypt(struct crypt_ctx * ctx,
+ buffer_t in,
+ buffer_t * out);
+
+int crypt_get_ivsz(struct crypt_ctx * ctx);
+
+int crypt_get_tagsz(struct crypt_ctx * ctx);
+
+int crypt_load_crt_file(const char * path,
+ void ** crt);
+
+int crypt_load_crt_str(const char * str,
+ void ** crt);
+
+int crypt_load_crt_der(buffer_t buf,
+ void ** crt);
+
+int crypt_get_pubkey_crt(void * crt,
+ void ** pk);
+
+void crypt_free_crt(void * crt);
+
+int crypt_load_privkey_file(const char * path,
+ void ** key);
+
+int crypt_load_privkey_str(const char * str,
+ void ** key);
+
+int crypt_load_pubkey_str(const char * str,
+ void ** key);
+
+int crypt_load_pubkey_file(const char * path,
+ void ** key);
+
+int crypt_load_pubkey_file_to_der(const char * path,
+ buffer_t * buf);
+
+int crypt_load_pubkey_raw_file(const char * path,
+ buffer_t * buf);
+
+int crypt_load_privkey_raw_file(const char * path,
+ void ** key);
+
+int crypt_cmp_key(const void * key1,
+ const void * key2);
+
+void crypt_free_key(void * key);
+
+int crypt_crt_str(const void * crt,
+ char * buf);
+
+int crypt_crt_der(const void * crt,
+ buffer_t * buf);
+
+int crypt_check_crt_name(void * crt,
+ const char * name);
+
+int crypt_get_crt_name(void * crt,
+ char * name);
+
+/* Secure memory allocation for sensitive data (keys, secrets) */
+int crypt_secure_malloc_init(size_t max);
+
+void crypt_secure_malloc_fini(void);
+
+void * crypt_secure_malloc(size_t size);
+
+void crypt_secure_free(void * ptr,
+ size_t size);
+
+void crypt_secure_clear(void * ptr,
+ size_t size);
+
+#endif /* OUROBOROS_LIB_CRYPT_H */
diff --git a/include/ouroboros/dev.h b/include/ouroboros/dev.h
index c402abd6..61464fbf 100644
--- a/include/ouroboros/dev.h
+++ b/include/ouroboros/dev.h
@@ -1,5 +1,5 @@
/*
- * Ouroboros - Copyright (C) 2016 - 2021
+ * Ouroboros - Copyright (C) 2016 - 2024
*
* API for applications
*
@@ -40,9 +40,8 @@ int flow_alloc(const char * dst_name,
int flow_accept(qosspec_t * qs,
const struct timespec * timeo);
-/* Returns flow descriptor, qs updates to supplied QoS. */
+/* Returns flow descriptor. */
int flow_join(const char * bc,
- qosspec_t * qs,
const struct timespec * timeo);
int flow_dealloc(int fd);
diff --git a/include/ouroboros/endian.h b/include/ouroboros/endian.h
index 530f66ba..6c3493d9 100644
--- a/include/ouroboros/endian.h
+++ b/include/ouroboros/endian.h
@@ -1,5 +1,5 @@
/*
- * Ouroboros - Copyright (C) 2016 - 2021
+ * Ouroboros - Copyright (C) 2016 - 2024
*
* Endianness
*
@@ -20,9 +20,8 @@
* Foundation, Inc., http://www.fsf.org/about/contact/.
*/
-#ifndef OUROBOROS_ENDIAN_H
-#define OUROBOROS_ENDIAN_H
-
+#ifndef OUROBOROS_LIB_ENDIAN_H
+#define OUROBOROS_LIB_ENDIAN_H
#if defined(__linux__) || defined(__CYGWIN__) || \
(defined(__MACH__) && !defined(__APPLE__))
@@ -67,10 +66,10 @@
#endif
#define hton64(x) htobe64(x)
-#define hton32(x) htobe32(x)
#define ntoh64(x) betoh64(x)
+#define hton32(x) htobe32(x)
#define ntoh32(x) betoh32(x)
#define hton16(x) htobe16(x)
#define ntoh16(x) betoh16(x)
-#endif /* OUROBOROS_ENDIAN_H */
+#endif /* OUROBOROS_LIB_ENDIAN_H */
diff --git a/include/ouroboros/errno.h b/include/ouroboros/errno.h
index 06f33bef..6b808241 100644
--- a/include/ouroboros/errno.h
+++ b/include/ouroboros/errno.h
@@ -1,5 +1,5 @@
/*
- * Ouroboros - Copyright (C) 2016 - 2021
+ * Ouroboros - Copyright (C) 2016 - 2024
*
* Ouroboros specific error numbers
*
@@ -31,7 +31,11 @@
#define EIPCP 1003 /* Failed to communicate with IPCP */
#define EIPCPSTATE 1004 /* Target in wrong state */
#define EFLOWDOWN 1005 /* Flow is down */
-#define ECRYPT 1006 /* Encryption error */
+#define EFLOWPEER 1006 /* Flow is down (peer timed out) */
#define ENAME 1007 /* Naming error */
+#define ECRYPT 1008 /* Encryption error */
+#ifndef EAUTH /* Exists on BSD */
+#define EAUTH 1009 /* Authentication error */
+#endif
#endif /* OUROBOROS_ERRNO_H */
diff --git a/include/ouroboros/fccntl.h b/include/ouroboros/fccntl.h
index e9f979f3..aa2b0d14 100644
--- a/include/ouroboros/fccntl.h
+++ b/include/ouroboros/fccntl.h
@@ -1,5 +1,5 @@
/*
- * Ouroboros - Copyright (C) 2016 - 2021
+ * Ouroboros - Copyright (C) 2016 - 2024
*
* Flow and FRCT connection control
*
diff --git a/include/ouroboros/flow.h b/include/ouroboros/flow.h
new file mode 100644
index 00000000..6b3dcde4
--- /dev/null
+++ b/include/ouroboros/flow.h
@@ -0,0 +1,58 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2024
+ *
+ * Flows
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+#ifndef OUROBOROS_LIB_FLOW_H
+#define OUROBOROS_LIB_FLOW_H
+
+#include <ouroboros/qos.h>
+
+#include <sys/types.h>
+
+#define SYMMKEYSZ 32
+
+enum flow_state { /* DO NOT CHANGE ORDER! */
+ FLOW_INIT = 0,
+ FLOW_ALLOC_PENDING,
+ FLOW_ACCEPT_PENDING,
+ FLOW_ALLOCATED,
+ FLOW_DEALLOC_PENDING,
+ FLOW_DEALLOCATED,
+ FLOW_DESTROY, /* TODO: REMOVE! */
+ FLOW_NULL
+};
+
+struct flow_info {
+ int id;
+
+ pid_t n_pid;
+ pid_t n_1_pid;
+
+ uid_t uid; /* 0 = privileged (GSPP), > 0 = PUP uid */
+
+ time_t mpl;
+
+ struct qos_spec qs;
+
+ enum flow_state state;
+};
+
+#endif /* OUROBOROS_LIB_FLOW_H */
diff --git a/include/ouroboros/fqueue.h b/include/ouroboros/fqueue.h
index f6828a4d..8eb2ff50 100644
--- a/include/ouroboros/fqueue.h
+++ b/include/ouroboros/fqueue.h
@@ -1,5 +1,5 @@
/*
- * Ouroboros - Copyright (C) 2016 - 2021
+ * Ouroboros - Copyright (C) 2016 - 2024
*
* Flow queues
*
@@ -33,7 +33,8 @@ enum fqtype {
FLOW_DOWN = (1 << 1),
FLOW_UP = (1 << 2),
FLOW_ALLOC = (1 << 3),
- FLOW_DEALLOC = (1 << 4)
+ FLOW_DEALLOC = (1 << 4),
+ FLOW_PEER = (1 << 5)
};
struct flow_set;
diff --git a/include/ouroboros/hash.h b/include/ouroboros/hash.h
index 917856a1..c44c2c8a 100644
--- a/include/ouroboros/hash.h
+++ b/include/ouroboros/hash.h
@@ -1,5 +1,5 @@
/*
- * Ouroboros - Copyright (C) 2016 - 2021
+ * Ouroboros - Copyright (C) 2016 - 2024
*
* Hashing functions
*
@@ -25,49 +25,65 @@
#include "config.h"
-#include <ouroboros/endian.h>
+#include <ouroboros/ipcp.h>
-#ifdef HAVE_LIBGCRYPT
-#include <gcrypt.h>
-#endif
#include <stdint.h>
#include <stddef.h>
/* Hash algorithms */
enum hash_algo {
-#ifdef HAVE_LIBGCRYPT
- HASH_CRC32 = GCRY_MD_CRC32,
- HASH_MD5 = GCRY_MD_MD5,
- HASH_SHA3_224 = GCRY_MD_SHA3_224,
- HASH_SHA3_256 = GCRY_MD_SHA3_256,
- HASH_SHA3_384 = GCRY_MD_SHA3_384,
- HASH_SHA3_512 = GCRY_MD_SHA3_512
-#else
- HASH_CRC32 = 0,
+ HASH_SHA3_224 = DIR_HASH_SHA3_224,
+ HASH_SHA3_256 = DIR_HASH_SHA3_256,
+ HASH_SHA3_384 = DIR_HASH_SHA3_384,
+ HASH_SHA3_512 = DIR_HASH_SHA3_512,
+ HASH_CRC32,
HASH_MD5,
- HASH_SHA3_224,
- HASH_SHA3_256,
- HASH_SHA3_384,
- HASH_SHA3_512
-#endif
};
-#define HASH_FMT "%02x%02x%02x%02x"
-#define HASH_VAL(hash) \
- (betoh32(*(uint32_t *) hash) & 0xFF000000) >> 24, \
- (betoh32(*(uint32_t *) hash) & 0x00FF0000) >> 16, \
- (betoh32(*(uint32_t *) hash) & 0x0000FF00) >> 8, \
- (betoh32(*(uint32_t *) hash) & 0x000000FF)
+#define HASH_FMT32 "%02x%02x%02x%02x"
+#define HASH_VAL32(hash) \
+ ((uint8_t *) hash)[0], ((uint8_t *) hash)[1], \
+ ((uint8_t *) hash)[2], ((uint8_t *) hash)[3]
+
+#define HASH_FMT64 HASH_FMT32 HASH_FMT32
+#define HASH_VAL64(hash64) \
+ HASH_VAL32(hash64), HASH_VAL32(hash64 + 4)
+
+#define HASH_FMT128 HASH_FMT64 HASH_FMT64
+#define HASH_VAL128(hash128) \
+ HASH_VAL64(hash128), HASH_VAL64(hash128 + 8)
+
+#define HASH_FMT192 HASH_FMT128 HASH_FMT64
+#define HASH_VAL192(hash192) \
+ HASH_VAL128(hash192), HASH_VAL64(hash192 + 16)
+
+#define HASH_FMT224 HASH_FMT128 HASH_FMT64 HASH_FMT32
+#define HASH_VAL224(hash224) \
+ HASH_VAL128(hash224), HASH_VAL64(hash224 + 16), \
+ HASH_VAL32(hash224 + 24)
+
+#define HASH_FMT256 HASH_FMT128 HASH_FMT128
+#define HASH_VAL256(hash256) \
+ HASH_VAL128(hash256), HASH_VAL128(hash256 + 16)
+
+#define HASH_FMT384 HASH_FMT256 HASH_FMT128
+#define HASH_VAL384(hash384) \
+ HASH_VAL256(hash384), HASH_VAL128(hash384 + 32)
+
+#define HASH_FMT512 HASH_FMT256 HASH_FMT256
+#define HASH_VAL512(hash512) \
+ HASH_VAL256(hash512), HASH_VAL256(hash512 + 32)
+
uint16_t hash_len(enum hash_algo algo);
-void mem_hash(enum hash_algo algo,
- void * dst,
- const uint8_t * buf,
- size_t len);
+void mem_hash(enum hash_algo algo,
+ void * dst,
+ const uint8_t * buf,
+ size_t len);
-void str_hash(enum hash_algo algo,
- void * dst,
- const char * str);
+void str_hash(enum hash_algo algo,
+ void * dst,
+ const char * str);
#endif /* OUROBOROS_LIB_HASH_H */
diff --git a/include/ouroboros/ipcp-dev.h b/include/ouroboros/ipcp-dev.h
index 3cd40771..37c8064f 100644
--- a/include/ouroboros/ipcp-dev.h
+++ b/include/ouroboros/ipcp-dev.h
@@ -1,5 +1,5 @@
/*
- * Ouroboros - Copyright (C) 2016 - 2021
+ * Ouroboros - Copyright (C) 2016 - 2024
*
* Additional API for IPCPs
*
@@ -20,30 +20,41 @@
* Foundation, Inc., http://www.fsf.org/about/contact/.
*/
-#include <ouroboros/shm_rdrbuff.h>
-#include <ouroboros/qoscube.h>
+#ifndef OUROBOROS_LIB_IPCP_DEV_H
+#define OUROBOROS_LIB_IPCP_DEV_H
-#ifndef OUROBOROS_IPCP_DEV_H
-#define OUROBOROS_IPCP_DEV_H
+#include <ouroboros/ipcp.h>
+#include <ouroboros/qoscube.h>
+#include <ouroboros/ssm_pool.h>
+#include <ouroboros/utils.h>
-int ipcp_create_r(int result);
+int ipcp_create_r(const struct ipcp_info * info);
-int ipcp_flow_req_arr(const uint8_t * dst,
- size_t len,
- qosspec_t qs,
- const void * data,
- size_t dlen);
+int ipcp_flow_req_arr(const buffer_t * dst,
+ qosspec_t qs,
+ time_t mpl,
+ const buffer_t * data);
-int ipcp_flow_alloc_reply(int fd,
- int response,
- const void * data,
- size_t len);
+int ipcp_flow_alloc_reply(int fd,
+ int response,
+ time_t mpl,
+ const buffer_t * data);
int ipcp_flow_read(int fd,
- struct shm_du_buff ** sdb);
+ struct ssm_pk_buff ** spb);
int ipcp_flow_write(int fd,
- struct shm_du_buff * sdb);
+ struct ssm_pk_buff * spb);
+
+int np1_flow_read(int fd,
+ struct ssm_pk_buff ** spb,
+ struct ssm_pool * pool);
+
+int np1_flow_write(int fd,
+ struct ssm_pk_buff * spb,
+ struct ssm_pool * pool);
+
+int ipcp_flow_dealloc(int fd);
int ipcp_flow_fini(int fd);
@@ -52,9 +63,9 @@ int ipcp_flow_get_qoscube(int fd,
size_t ipcp_flow_queued(int fd);
-int ipcp_sdb_reserve(struct shm_du_buff ** sdb,
+int ipcp_spb_reserve(struct ssm_pk_buff ** spb,
size_t len);
-void ipcp_sdb_release(struct shm_du_buff * sdb);
+void ipcp_spb_release(struct ssm_pk_buff * spb);
-#endif /* OUROBOROS_IPCP_DEV_H */
+#endif /* OUROBOROS_LIB_IPCP_DEV_H */
diff --git a/include/ouroboros/ipcp.h b/include/ouroboros/ipcp.h
index a1bdae70..c397f250 100644
--- a/include/ouroboros/ipcp.h
+++ b/include/ouroboros/ipcp.h
@@ -1,5 +1,5 @@
/*
- * Ouroboros - Copyright (C) 2016 - 2021
+ * Ouroboros - Copyright (C) 2016 - 2024
*
* IPCP definitions and policies
*
@@ -26,77 +26,349 @@
#include <stdint.h>
#include <unistd.h>
#include <stdbool.h>
+#include <netinet/in.h>
+#include <sys/types.h>
+#define IPCP_NAME_SIZE 255
#define LAYER_NAME_SIZE 255
+#define DEV_NAME_SIZE 255
-/*
- * NOTE: the IRMd uses this order to select an IPCP
- * for flow allocation.
- */
-enum ipcp_type {
+/* TODO: Move state to ipcpd/ipcp.h, requires small change to reg/ipcp.c */
+enum ipcp_state {
+ IPCP_NULL = 0,
+ IPCP_INIT,
+ IPCP_BOOT,
+ IPCP_BOOTSTRAPPED,
+ IPCP_ENROLLED,
+ IPCP_OPERATIONAL,
+ IPCP_SHUTDOWN
+};
+
+enum ipcp_type { /* IRMd uses order to select an IPCP for flow allocation. */
IPCP_LOCAL = 0,
IPCP_UNICAST,
IPCP_BROADCAST,
IPCP_ETH_LLC,
IPCP_ETH_DIX,
- IPCP_UDP,
+ IPCP_UDP4,
+ IPCP_UDP6,
IPCP_INVALID
};
+struct ipcp_info {
+ enum ipcp_type type;
+ pid_t pid;
+ char name[IPCP_NAME_SIZE + 1];
+ enum ipcp_state state; /* TODO: remove. */
+};
+
+/* Unicast IPCP components. */
+#define DT_COMP "Data Transfer"
+#define MGMT_COMP "Management"
+
/* Unicast IPCP policies */
enum pol_addr_auth {
- ADDR_AUTH_FLAT_RANDOM = 0
+ ADDR_AUTH_FLAT_RANDOM = 0,
+ ADDR_AUTH_INVALID
+};
+
+enum pol_link_state {
+ LS_SIMPLE = 0,
+ LS_LFA,
+ LS_ECMP,
+ LS_INVALID
+};
+
+struct ls_config {
+ enum pol_link_state pol; /* Link state policy */
+ time_t t_recalc; /* Time to recalculate PFF (s) */
+ time_t t_update; /* Time between updates (s) */
+ time_t t_timeo; /* Link timeout (s) */
+};
+
+static const struct ls_config default_ls_config = {
+ .pol = LS_SIMPLE,
+ .t_recalc = 4,
+ .t_update = 15,
+ .t_timeo = 60
};
enum pol_routing {
ROUTING_LINK_STATE = 0,
- ROUTING_LINK_STATE_LFA,
- ROUTING_LINK_STATE_ECMP
+ ROUTING_INVALID
+};
+
+struct routing_config {
+ enum pol_routing pol; /* Routing policy */
+ union {
+ struct ls_config ls; /* Link state config */
+ /* struct pv_config pv */ /* Path vector config */
+ };
+};
+
+static const struct routing_config default_routing_config = {
+ .pol = ROUTING_LINK_STATE,
+ .ls = {
+ .pol = LS_SIMPLE,
+ .t_recalc = 4,
+ .t_update = 15,
+ .t_timeo = 60
+ }
};
enum pol_cong_avoid {
CA_NONE = 0,
- CA_MB_ECN
+ CA_MB_ECN,
+ CA_INVALID
+};
+
+struct dt_config {
+ struct {
+ uint8_t addr_size;
+ uint8_t eid_size;
+ uint8_t max_ttl;
+ };
+ struct routing_config routing; /* Routing policy */
+};
+
+static const struct dt_config default_dt_config = {
+ .addr_size = 4,
+ .eid_size = 8,
+ .max_ttl = 60,
+ .routing = {
+ .pol = ROUTING_LINK_STATE,
+ .ls = {
+ .pol = LS_SIMPLE,
+ .t_recalc = 4,
+ .t_update = 15,
+ .t_timeo = 60
+ }
+ }
+};
+
+enum pol_dir {
+ DIR_DHT = 0,
+ DIR_INVALID
};
enum pol_dir_hash {
- DIR_HASH_SHA3_224 = 0,
+ DIR_HASH_SHA3_224,
DIR_HASH_SHA3_256,
DIR_HASH_SHA3_384,
- DIR_HASH_SHA3_512
+ DIR_HASH_SHA3_512,
+ DIR_HASH_INVALID
+};
+
+enum dir_dht_config_limits {
+ DHT_ALPHA_MIN = 1,
+ DHT_K_MIN = 1,
+ DHT_T_EXPIRE_MIN = 10,
+ DHT_T_REFRESH_MIN = 3,
+ DHT_T_REPLICATE_MIN = 3,
+
+ DHT_ALPHA_MAX = 10,
+ DHT_K_MAX = 20,
+ DHT_T_EXPIRE_MAX = 86400,
+ DHT_T_REFRESH_MAX = 3600,
+ DHT_T_REPLICATE_MAX = 3600,
};
-/* Info reported back to the IRMd about the layer on enrollment */
+struct dir_dht_config {
+ struct {
+ uint32_t alpha; /* Parallel search factor */
+ uint32_t k; /* Replication factor */
+ uint32_t t_expire; /* Expire time (s) */
+ uint32_t t_refresh; /* Refresh time (s) */
+ uint32_t t_replicate; /* Replication time (s) */
+ } params;
+ uint64_t peer; /* Initial peer address */
+};
+
+static const struct dir_dht_config default_dht_config = {
+ .params = {
+ .alpha = 3, /* Proven optimal value */
+ .k = 8, /* MDHT value */
+ .t_expire = 86400, /* Expire after 1 day */
+ .t_refresh = 900, /* MDHT value. */
+ .t_replicate = 900 /* MDHT value. */
+ }
+};
+
+/* TODO: Move hash algorithm in directory config */
+struct dir_config {
+ enum pol_dir pol;
+ union {
+ struct dir_dht_config dht;
+ };
+};
+
+static const struct dir_config default_dir_config = {
+ .pol = DIR_DHT,
+ .dht = {
+ .params = {
+ .alpha = 3,
+ .k = 8,
+ .t_expire = 86400,
+ .t_refresh = 900,
+ .t_replicate = 900
+ }
+ }
+};
+
+/* IPCP configuration */
+struct uni_config {
+ struct dt_config dt;
+ struct dir_config dir;
+ enum pol_addr_auth addr_auth_type;
+ enum pol_cong_avoid cong_avoid;
+};
+
+static const struct uni_config default_uni_config = {
+ .dt = {
+ .addr_size = 4,
+ .eid_size = 8,
+ .max_ttl = 60,
+ .routing = {
+ .pol = ROUTING_LINK_STATE,
+ .ls = {
+ .pol = LS_SIMPLE,
+ .t_recalc = 4,
+ .t_update = 15,
+ .t_timeo = 60
+ }
+ }
+ },
+ .dir = {
+ .pol = DIR_DHT,
+ .dht = {
+ .params = {
+ .alpha = 3,
+ .k = 8,
+ .t_expire = 86400,
+ .t_refresh = 900,
+ .t_replicate = 900
+ }
+ }
+ },
+ .addr_auth_type = ADDR_AUTH_FLAT_RANDOM,
+ .cong_avoid = CA_MB_ECN
+};
+
+struct eth_config {
+ char dev[DEV_NAME_SIZE + 1];
+ uint16_t ethertype; /* DIX only*/
+};
+
+struct udp4_config {
+ struct in_addr ip_addr;
+ struct in_addr dns_addr;
+ uint16_t port;
+};
+
+struct udp6_config {
+ struct in6_addr ip_addr;
+ struct in6_addr dns_addr;
+ uint16_t port;
+};
+
+/* Layers */
struct layer_info {
- char layer_name[LAYER_NAME_SIZE + 1];
- int dir_hash_algo;
+ char name[LAYER_NAME_SIZE + 1];
+ /* TODO: Move this to directory info ? */
+ enum pol_dir_hash dir_hash_algo;
};
/* Structure to configure the first IPCP */
struct ipcp_config {
- struct layer_info layer_info;
+ struct layer_info layer_info;
+ enum ipcp_type type;
- enum ipcp_type type;
+ union {
+ struct uni_config unicast;
+ struct udp4_config udp4;
+ struct udp6_config udp6;
+ struct eth_config eth;
+ };
+};
- /* Unicast */
- uint8_t addr_size;
- uint8_t eid_size;
- uint8_t max_ttl;
+/* default configurations */
+static const struct ipcp_config local_default_conf = {
+ .type = IPCP_LOCAL,
+ .layer_info = {
+ .dir_hash_algo = DIR_HASH_SHA3_256
+ }
+};
- enum pol_addr_auth addr_auth_type;
- enum pol_routing routing_type;
- enum pol_cong_avoid cong_avoid;
+static const struct ipcp_config eth_dix_default_conf = {
+ .type = IPCP_ETH_DIX,
+ .layer_info = {
+ .dir_hash_algo = DIR_HASH_SHA3_256
+ },
+ .eth = {
+ .ethertype=0xA000,
+ }
+};
+
+static const struct ipcp_config eth_llc_default_conf = {
+ .type = IPCP_ETH_LLC,
+ .layer_info = {
+ .dir_hash_algo = DIR_HASH_SHA3_256
+ }
+};
- /* UDP */
- uint32_t ip_addr;
- uint32_t dns_addr;
- uint16_t port;
+static const struct ipcp_config udp4_default_conf = {
+ .type = IPCP_UDP4,
+ .udp4 = {
+ .port = 3435
+ }
+};
- /* Ethernet */
- char * dev;
+static const struct ipcp_config udp6_default_conf = {
+ .type = IPCP_UDP6,
+ .udp6 = {
+ .port = 3435
+ }
+};
+
+static const struct ipcp_config uni_default_conf = {
+ .type = IPCP_UNICAST,
+ .layer_info = {
+ .dir_hash_algo = DIR_HASH_SHA3_256
+ },
+ .unicast = {
+ .dt = {
+ .addr_size = 4,
+ .eid_size = 8,
+ .max_ttl = 60,
+ .routing = {
+ .pol = ROUTING_LINK_STATE,
+ .ls = {
+ .pol = LS_SIMPLE,
+ .t_recalc = 4,
+ .t_update = 15,
+ .t_timeo = 60
+ }
+ }
+ },
+ .dir = {
+ .pol = DIR_DHT,
+ .dht = {
+ .params = {
+ .alpha = 3,
+ .k = 8,
+ .t_expire = 86400,
+ .t_refresh = 900,
+ .t_replicate = 900
+ }
+ }
+ },
+ .addr_auth_type = ADDR_AUTH_FLAT_RANDOM,
+ .cong_avoid = CA_MB_ECN
+ }
+};
- /* Ethernet DIX */
- uint16_t ethertype;
+static const struct ipcp_config bc_default_conf = {
+ .type = IPCP_BROADCAST
};
#endif /* OUROBOROS_IPCP_H */
diff --git a/include/ouroboros/irm.h b/include/ouroboros/irm.h
index d2a4c263..70a21ed7 100644
--- a/include/ouroboros/irm.h
+++ b/include/ouroboros/irm.h
@@ -1,5 +1,5 @@
/*
- * Ouroboros - Copyright (C) 2016 - 2021
+ * Ouroboros - Copyright (C) 2016 - 2024
*
* The API to instruct the IPC Resource Manager
*
@@ -25,36 +25,16 @@
#include <ouroboros/cdefs.h>
#include <ouroboros/ipcp.h>
+#include <ouroboros/name.h>
#include <ouroboros/qos.h>
#include <sys/types.h>
-/* Unicast IPCP components. */
-#define DT_COMP "Data Transfer"
-#define MGMT_COMP "Management"
-
-/* Name binding options. */
-#define BIND_AUTO 0x01
-
-#define NAME_SIZE 256
-#define LAYER_SIZE LAYER_NAME_SIZE
-
-/* Load balancing policy for incoming flows. */
-enum pol_balance {
- LB_RR = 0,
- LB_SPILL
-};
-
-struct ipcp_info {
+struct ipcp_list_info {
pid_t pid;
enum ipcp_type type;
char name[NAME_SIZE];
- char layer[LAYER_SIZE];
-};
-
-struct name_info {
- char name[NAME_SIZE];
- enum pol_balance pol_lb;
+ char layer[LAYER_NAME_SIZE];
};
__BEGIN_DECLS
@@ -64,7 +44,7 @@ pid_t irm_create_ipcp(const char * name,
int irm_destroy_ipcp(pid_t pid);
-ssize_t irm_list_ipcps(struct ipcp_info ** ipcps);
+ssize_t irm_list_ipcps(struct ipcp_list_info ** ipcps);
int irm_enroll_ipcp(pid_t pid,
const char * dst);
@@ -96,8 +76,7 @@ int irm_bind_process(pid_t pid,
int irm_unbind_process(pid_t pid,
const char * name);
-int irm_create_name(const char * name,
- enum pol_balance pol);
+int irm_create_name(struct name_info * info);
int irm_destroy_name(const char * name);
diff --git a/include/ouroboros/list.h b/include/ouroboros/list.h
index 408aa64e..f3ea0e46 100644
--- a/include/ouroboros/list.h
+++ b/include/ouroboros/list.h
@@ -1,5 +1,5 @@
/*
- * Ouroboros - Copyright (C) 2016 - 2021
+ * Ouroboros - Copyright (C) 2016 - 2024
*
* Simple doubly linked list implementation.
*
@@ -61,6 +61,6 @@ void list_del(struct list_head * e);
void list_move(struct list_head * dst,
struct list_head * src);
-bool list_is_empty(struct list_head * h);
+bool list_is_empty(const struct list_head * h);
#endif /* OUROBOROS_LIB_LIST_H */
diff --git a/include/ouroboros/local-dev.h b/include/ouroboros/local-dev.h
index 103fe37b..cd0298d3 100644
--- a/include/ouroboros/local-dev.h
+++ b/include/ouroboros/local-dev.h
@@ -1,5 +1,5 @@
/*
- * Ouroboros - Copyright (C) 2016 - 2021
+ * Ouroboros - Copyright (C) 2016 - 2024
*
* Optimized calls for the local IPCPs
*
@@ -20,12 +20,14 @@
* Foundation, Inc., http://www.fsf.org/about/contact/.
*/
-#ifndef OUROBOROS_LOCAL_DEV_H
-#define OUROBOROS_LOCAL_DEV_H
+#ifndef OUROBOROS_LIB_LOCAL_DEV_H
+#define OUROBOROS_LIB_LOCAL_DEV_H
-ssize_t local_flow_read(int fd);
+#include <ouroboros/ssm_pool.h>
-int local_flow_write(int fd,
- size_t idx);
+int local_flow_transfer(int src_fd,
+ int dst_fd,
+ struct ssm_pool * src_pool,
+ struct ssm_pool * dst_pool);
-#endif /* OUROBOROS_LOCAL_DEV_H */
+#endif /* OUROBOROS_LIB_LOCAL_DEV_H */
diff --git a/include/ouroboros/lockfile.h b/include/ouroboros/lockfile.h
index b188d2b4..85a57313 100644
--- a/include/ouroboros/lockfile.h
+++ b/include/ouroboros/lockfile.h
@@ -1,5 +1,5 @@
/*
- * Ouroboros - Copyright (C) 2016 - 2021
+ * Ouroboros - Copyright (C) 2016 - 2024
*
* Lockfile for Ouroboros
*
@@ -20,8 +20,8 @@
* Foundation, Inc., http://www.fsf.org/about/contact/.
*/
-#ifndef OUROBOROS_LOCKFILE_H
-#define OUROBOROS_LOCKFILE_H
+#ifndef OUROBOROS_LIB_LOCKFILE_H
+#define OUROBOROS_LIB_LOCKFILE_H
#include <sys/types.h>
@@ -37,4 +37,4 @@ void lockfile_destroy(struct lockfile * lf);
pid_t lockfile_owner(struct lockfile * lf);
-#endif
+#endif /* OUROBOROS_LIB_LOCKFILE_H */
diff --git a/include/ouroboros/logs.h b/include/ouroboros/logs.h
index bffba477..31f11836 100644
--- a/include/ouroboros/logs.h
+++ b/include/ouroboros/logs.h
@@ -1,5 +1,5 @@
/*
- * Ouroboros - Copyright (C) 2016 - 2021
+ * Ouroboros - Copyright (C) 2016 - 2024
*
* Logging facilities
*
@@ -20,56 +20,105 @@
* Foundation, Inc., http://www.fsf.org/about/contact/.
*/
-#ifndef OUROBOROS_LOGS_H
-#define OUROBOROS_LOGS_H
-
-#include <unistd.h>
-#include <stdio.h>
-#include <stdbool.h>
-#include <syslog.h>
+#ifndef OUROBOROS_LIB_LOGS_H
+#define OUROBOROS_LIB_LOGS_H
#ifndef OUROBOROS_PREFIX
#error You must define OUROBOROS_PREFIX before including this file
#endif
-void log_init(bool sysout);
+#include <ouroboros/hash.h>
-void log_fini(void);
+#include <unistd.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <syslog.h>
#define CLR_RED "\x1b[31m"
#define CLR_GREEN "\x1b[32m"
#define CLR_YELLOW "\x1b[33m"
+#define CLR_BLUE "\x1b[34m"
#define CLR_RESET "\x1b[0m"
#define DEBUG_CODE "DB"
#define ERROR_CODE "EE"
#define WARN_CODE "WW"
#define INFO_CODE "II"
+#define PROTO_CODE "PP"
extern bool log_syslog;
-#define __olog(CLR, LVL, SYSLVL, ...) \
- do { \
- if (log_syslog) { \
- syslog(SYSLVL, OUROBOROS_PREFIX ": " \
- __VA_ARGS__); \
- } else { \
- printf(CLR "==%05d== " OUROBOROS_PREFIX \
- "(" LVL "): ", getpid()); \
- printf(__VA_ARGS__); \
- printf(CLR_RESET "\n"); \
- fflush(stdout); \
- } \
+void log_init(bool sysout);
+
+void log_fini(void);
+
+
+#define __olog(CLR, LVL, SYSLVL, ...) \
+ do { \
+ if (log_syslog) { \
+ syslog(SYSLVL, __VA_ARGS__); \
+ } else { \
+ printf(CLR "==%05d== " OUROBOROS_PREFIX \
+ "(" LVL "): ", getpid()); \
+ printf(__VA_ARGS__); \
+ printf(CLR_RESET "\n"); \
+ fflush(stdout); \
+ } \
} while (0)
-#define log_err(...) __olog(CLR_RED, ERROR_CODE, LOG_ERR, __VA_ARGS__)
-#define log_warn(...) __olog(CLR_YELLOW, WARN_CODE, LOG_WARNING, __VA_ARGS__)
-#define log_info(...) __olog(CLR_GREEN, INFO_CODE, LOG_INFO, __VA_ARGS__)
+#define __olog_id(CLR, LVL, SYSLVL, id, fmt, ...) \
+ do { \
+ if (log_syslog) { \
+ syslog(SYSLVL, "[" HASH_FMT64 "] " fmt, \
+ HASH_VAL64(id), ## __VA_ARGS__); \
+ } else { \
+ printf(CLR "==%05d== " OUROBOROS_PREFIX \
+ "(" LVL "): ", getpid()); \
+ printf("[" HASH_FMT64 "] " fmt, \
+ HASH_VAL64(id), ## __VA_ARGS__); \
+ printf(CLR_RESET "\n"); \
+ fflush(stdout); \
+ } \
+ } while (0)
-#ifdef CONFIG_OUROBOROS_DEBUG
-#define log_dbg(...) __olog("", DEBUG_CODE, LOG_DEBUG, __VA_ARGS__)
-#else
+#ifndef OUROBOROS_DISABLE_LOGGING
+#define log_err(...) \
+ __olog(CLR_RED, ERROR_CODE, LOG_ERR, __VA_ARGS__)
+#define log_warn(...) \
+ __olog(CLR_YELLOW, WARN_CODE, LOG_WARNING, __VA_ARGS__)
+#define log_info(...) \
+ __olog(CLR_GREEN, INFO_CODE, LOG_INFO, __VA_ARGS__)
+
+
+#define log_err_id(id, fmt, ...) \
+ __olog_id(CLR_RED, ERROR_CODE, LOG_ERR, id, fmt, ## __VA_ARGS__)
+#define log_warn_id(id, fmt, ...) \
+ __olog_id(CLR_YELLOW, WARN_CODE, LOG_WARNING, id, fmt, ## __VA_ARGS__)
+#define log_info_id(id, fmt, ...) \
+ __olog_id(CLR_GREEN, INFO_CODE, LOG_INFO, id, fmt, ## __VA_ARGS__)
+#else /* OUROBOROS_DISABLE_LOGGING: all logging disabled */
+#define log_err(...) do { } while (0)
+#define log_warn(...) do { } while (0)
+#define log_info(...) do { } while (0)
+
+#define log_err_id(id, fmt, ...) do { (void)(id); } while (0)
+#define log_warn_id(id, fmt, ...) do { (void)(id); } while (0)
+#define log_info_id(id, fmt, ...) do { (void)(id); } while (0)
+
+#endif /* OUROBOROS_DISABLE_LOGGING */
+
+#if defined(OUROBOROS_DISABLE_LOGGING) || !defined(CONFIG_OUROBOROS_DEBUG)
#define log_dbg(...) do { } while (0)
+#define log_dbg_id(id, ...) do { (void)(id); } while (0)
+#define log_proto(...) do { } while (0)
+#define log_proto_id(id, ...) do { (void)(id); } while (0)
+#else
+#define log_dbg(...) __olog("", DEBUG_CODE, LOG_DEBUG, __VA_ARGS__)
+#define log_dbg_id(id, fmt, ...) \
+ __olog_id("", DEBUG_CODE, LOG_DEBUG, id, fmt, ## __VA_ARGS__)
+#define log_proto(...) __olog(CLR_BLUE, PROTO_CODE, LOG_DEBUG, __VA_ARGS__)
+#define log_proto_id(id, fmt, ...) \
+ __olog_id(CLR_BLUE, INFO_CODE, LOG_INFO, id, fmt, ## __VA_ARGS__)
#endif
-#endif /* OUROBOROS_LOGS_H */
+#endif /* OUROBOROS_LIB_LOGS_H */
diff --git a/include/ouroboros/md5.h b/include/ouroboros/md5.h
index ab01996d..85a22544 100644
--- a/include/ouroboros/md5.h
+++ b/include/ouroboros/md5.h
@@ -1,5 +1,5 @@
/*
- * Ouroboros - Copyright (C) 2016 - 2021
+ * Ouroboros - Copyright (C) 2016 - 2024
*
* MD5 algorithm
*
diff --git a/include/ouroboros/name.h b/include/ouroboros/name.h
new file mode 100644
index 00000000..892f2a1f
--- /dev/null
+++ b/include/ouroboros/name.h
@@ -0,0 +1,50 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2024
+ *
+ * Names
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+#ifndef OUROBOROS_NAME_H
+#define OUROBOROS_NAME_H
+
+#define NAME_SIZE 255
+#define NAME_PATH_SIZE (NAME_SIZE + 256)
+#define BIND_AUTO 0x01
+
+enum pol_balance {
+ LB_RR = 0,
+ LB_SPILL,
+ LB_INVALID
+};
+
+struct name_sec_paths {
+ char enc[NAME_PATH_SIZE + 1]; /* path to crypt for this name */
+ char key[NAME_PATH_SIZE + 1]; /* path to key for this name */
+ char crt[NAME_PATH_SIZE + 1]; /* path to crt for this name */
+};
+
+struct name_info {
+ char name[NAME_SIZE + 1];
+ enum pol_balance pol_lb;
+
+ struct name_sec_paths s; /* server */
+ struct name_sec_paths c; /* client */
+};
+
+#endif /* OUROBOROS_NAME_H */
diff --git a/include/ouroboros/notifier.h b/include/ouroboros/notifier.h
index 429a8d97..db945f1e 100644
--- a/include/ouroboros/notifier.h
+++ b/include/ouroboros/notifier.h
@@ -1,5 +1,5 @@
/*
- * Ouroboros - Copyright (C) 2016 - 2021
+ * Ouroboros - Copyright (C) 2016 - 2024
*
* Notifier event system using callbacks
*
diff --git a/include/ouroboros/np1_flow.h b/include/ouroboros/np1_flow.h
index b764de91..4110ab6a 100644
--- a/include/ouroboros/np1_flow.h
+++ b/include/ouroboros/np1_flow.h
@@ -1,5 +1,5 @@
/*
- * Ouroboros - Copyright (C) 2016 - 2021
+ * Ouroboros - Copyright (C) 2016 - 2024
*
* Adapter functions for N + 1 flow descriptors
*
@@ -20,20 +20,31 @@
* Foundation, Inc., http://www.fsf.org/about/contact/.
*/
-#ifndef OUROBOROS_NP1_FLOW_H
-#define OUROBOROS_NP1_FLOW_H
+#ifndef OUROBOROS_LIB_NP1_FLOW_H
+#define OUROBOROS_LIB_NP1_FLOW_H
#include <ouroboros/qos.h>
#include <unistd.h>
-int np1_flow_alloc(pid_t n_pid,
- int flow_id,
- qosspec_t qs);
+int np1_flow_alloc(pid_t n_pid,
+ int flow_id);
-int np1_flow_resp(int flow_id);
+int np1_flow_resp(int flow_id,
+ int resp);
-int np1_flow_dealloc(int flow_id,
+int np1_flow_dealloc(int flow_id,
time_t timeo);
-#endif /* OUROBOROS_NP1_FLOW_H */
+static const qosspec_t qos_np1 = {
+ .delay = UINT32_MAX,
+ .bandwidth = 0,
+ .availability = 0,
+ .loss = UINT32_MAX,
+ .ber = UINT32_MAX,
+ .in_order = 0,
+ .max_gap = UINT32_MAX,
+ .timeout = 0
+};
+
+#endif /* OUROBOROS_LIB_NP1_FLOW_H */
diff --git a/include/ouroboros/proc.h b/include/ouroboros/proc.h
new file mode 100644
index 00000000..0e27362e
--- /dev/null
+++ b/include/ouroboros/proc.h
@@ -0,0 +1,45 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2024
+ *
+ * Processes and Programs
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+#ifndef OUROBOROS_LIB_PROC_H
+#define OUROBOROS_LIB_PROC_H
+
+#include <sys/types.h>
+
+#define PROG_NAME_SIZE 255
+#define PROG_PATH_SIZE 255
+
+/* Processes */
+struct proc_info {
+ pid_t pid;
+ char prog[PROG_NAME_SIZE + 1];
+ uid_t uid;
+ gid_t gid;
+};
+
+/* Programs */
+struct prog_info {
+ char name[PROG_NAME_SIZE + 1];
+ char path[PROG_PATH_SIZE + 1];
+};
+
+#endif /* OUROBOROS_LIB_PROC_H */
diff --git a/include/ouroboros/proto.h b/include/ouroboros/proto.h
index f289e761..5c863c8b 100644
--- a/include/ouroboros/proto.h
+++ b/include/ouroboros/proto.h
@@ -1,5 +1,5 @@
/*
- * Ouroboros - Copyright (C) 2016 - 2021
+ * Ouroboros - Copyright (C) 2016 - 2024
*
* Protocol syntax definitions
*
diff --git a/include/ouroboros/protobuf.h b/include/ouroboros/protobuf.h
new file mode 100644
index 00000000..780d58dc
--- /dev/null
+++ b/include/ouroboros/protobuf.h
@@ -0,0 +1,130 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2024
+ *
+ * Protobuf syntax conversion
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+#ifndef OUROBOROS_LIB_PROTOBUF_H
+#define OUROBOROS_LIB_PROTOBUF_H
+
+#include <ouroboros/flow.h>
+#include <ouroboros/qos.h>
+#include <ouroboros/ipcp.h>
+#include <ouroboros/irm.h>
+#include <ouroboros/serdes-irm.h>
+#include <ouroboros/serdes-oep.h>
+
+#include "ipcp_config.pb-c.h"
+typedef IpcpConfigMsg ipcp_config_msg_t;
+typedef LsConfigMsg ls_config_msg_t;
+typedef RoutingConfigMsg routing_config_msg_t;
+typedef DtConfigMsg dt_config_msg_t;
+typedef DirConfigMsg dir_config_msg_t;
+typedef DirDhtConfigMsg dir_dht_config_msg_t;
+typedef EthConfigMsg eth_config_msg_t;
+typedef Udp4ConfigMsg udp4_config_msg_t;
+typedef Udp6ConfigMsg udp6_config_msg_t;
+typedef UniConfigMsg uni_config_msg_t;
+
+#include "ipcp.pb-c.h"
+typedef IpcpMsg ipcp_msg_t;
+
+#include "irm.pb-c.h"
+typedef IrmMsg irm_msg_t;
+typedef TimespecMsg timespec_msg_t;
+typedef IpcpInfoMsg ipcp_info_msg_t;
+typedef IpcpListMsg ipcp_list_msg_t;
+
+#include "model.pb-c.h"
+typedef FlowInfoMsg flow_info_msg_t;
+typedef NameInfoMsg name_info_msg_t;
+typedef LayerInfoMsg layer_info_msg_t;
+typedef QosspecMsg qosspec_msg_t;
+
+#include "enroll.pb-c.h"
+typedef EnrollReqMsg enroll_req_msg_t;
+typedef EnrollRespMsg enroll_resp_msg_t;
+typedef EnrollAckMsg enroll_ack_msg_t;
+
+/* IPCP configuration */
+timespec_msg_t * timespec_s_to_msg(const struct timespec * s);
+
+struct timespec timespec_msg_to_s(timespec_msg_t * msg);
+
+flow_info_msg_t * flow_info_s_to_msg(const struct flow_info * s);
+
+struct flow_info flow_info_msg_to_s(const flow_info_msg_t * msg);
+
+name_info_msg_t * name_info_s_to_msg(const struct name_info * s);
+
+struct name_info name_info_msg_to_s(const name_info_msg_t * msg);
+
+layer_info_msg_t * layer_info_s_to_msg(const struct layer_info * s);
+
+struct layer_info layer_info_msg_to_s(const layer_info_msg_t * msg);
+
+ipcp_info_msg_t * ipcp_info_s_to_msg(const struct ipcp_info * s);
+
+struct ipcp_info ipcp_info_msg_to_s(const ipcp_info_msg_t * msg);
+
+dt_config_msg_t * dt_config_s_to_msg(const struct dt_config * s);
+
+struct dt_config dt_config_msg_to_s(const dt_config_msg_t * msg);
+
+uni_config_msg_t * uni_config_s_to_msg(const struct uni_config * s);
+
+struct uni_config uni_config_msg_to_s(const uni_config_msg_t * msg);
+
+eth_config_msg_t * eth_config_s_to_msg(const struct eth_config * s);
+
+struct eth_config eth_config_msg_to_s(const eth_config_msg_t * msg);
+
+udp4_config_msg_t * udp4_config_s_to_msg(const struct udp4_config * s);
+
+struct udp4_config udp4_config_msg_to_s(const udp4_config_msg_t * msg);
+
+udp6_config_msg_t * udp6_config_s_to_msg(const struct udp6_config * s);
+
+struct udp6_config udp6_config_msg_to_s(const udp6_config_msg_t * msg);
+
+ipcp_config_msg_t * ipcp_config_s_to_msg(const struct ipcp_config * s);
+
+struct ipcp_config ipcp_config_msg_to_s(const ipcp_config_msg_t * msg);
+
+/* QoS */
+
+qosspec_msg_t * qos_spec_s_to_msg(const struct qos_spec * s);
+
+struct qos_spec qos_spec_msg_to_s(const qosspec_msg_t * msg);
+
+/* Enrollment */
+
+enroll_req_msg_t * enroll_req_s_to_msg(const struct enroll_req * s);
+
+struct enroll_req enroll_req_msg_to_s(const enroll_req_msg_t * msg);
+
+enroll_resp_msg_t * enroll_resp_s_to_msg(const struct enroll_resp * s);
+
+struct enroll_resp enroll_resp_msg_to_s(const enroll_resp_msg_t * msg);
+
+enroll_ack_msg_t * enroll_ack_s_to_msg(const struct enroll_ack * s);
+
+struct enroll_ack enroll_ack_msg_to_s(const enroll_ack_msg_t * msg);
+
+#endif /* OUROBOROS_LIB_PROTOBUF_H */
diff --git a/include/ouroboros/pthread.h b/include/ouroboros/pthread.h
index 735557d2..7044cb5e 100644
--- a/include/ouroboros/pthread.h
+++ b/include/ouroboros/pthread.h
@@ -1,5 +1,5 @@
/*
- * Ouroboros - Copyright (C) 2016 - 2021
+ * Ouroboros - Copyright (C) 2016 - 2024
*
* Useful cleanup functions for pthreads
*
@@ -20,20 +20,32 @@
* Foundation, Inc., http://www.fsf.org/about/contact/.
*/
-#ifndef OUROBOROS_PTHREAD_H
-#define OUROBOROS_PTHREAD_H
+#ifndef OUROBOROS_LIB_PTHREAD_H
+#define OUROBOROS_LIB_PTHREAD_H
#include <pthread.h>
+static int __attribute__((unused)) __timedwait(pthread_cond_t * cond,
+ pthread_mutex_t * mtx,
+ const struct timespec * abstime)
+{
+ if (abstime == NULL)
+ return pthread_cond_wait(cond, mtx);
+
+ return pthread_cond_timedwait(cond, mtx, abstime);
+}
+
+#if defined (_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112L
/* various cleanup functions for pthread_cleanup_push */
static void __attribute__((unused)) __cleanup_rwlock_unlock(void * rwlock)
{
pthread_rwlock_unlock((pthread_rwlock_t *) rwlock);
}
+#endif
static void __attribute__((unused)) __cleanup_mutex_unlock(void * mutex)
{
pthread_mutex_unlock((pthread_mutex_t *) mutex);
}
-#endif /* OUROBOROS_PTHREAD_H */
+#endif /* OUROBOROS_LIB_PTHREAD_H */
diff --git a/include/ouroboros/qos.h b/include/ouroboros/qos.h
index 6391347a..2be31305 100644
--- a/include/ouroboros/qos.h
+++ b/include/ouroboros/qos.h
@@ -1,5 +1,5 @@
/*
- * Ouroboros - Copyright (C) 2016 - 2021
+ * Ouroboros - Copyright (C) 2016 - 2024
*
* Quality of Service specification
*
@@ -26,15 +26,17 @@
#include <stdint.h>
#include <stdbool.h>
+#define DEFAULT_PEER_TIMEOUT 120000
+
typedef struct qos_spec {
- uint32_t delay; /* In ms */
- uint64_t bandwidth; /* In bits/s */
- uint8_t availability; /* Class of 9s */
- uint32_t loss; /* Packet loss */
- uint32_t ber; /* Bit error rate, errors per billion bits */
- uint8_t in_order; /* In-order delivery, enables FRCT */
- uint32_t max_gap; /* In ms */
- uint16_t cypher_s; /* Cypher strength, 0 = no encryption */
+ uint32_t delay; /* In ms. */
+ uint64_t bandwidth; /* In bits/s. */
+ uint8_t availability; /* Class of 9s. */
+ uint32_t loss; /* Packet loss. */
+ uint32_t ber; /* Bit error rate, errors per billion bits. */
+ uint8_t in_order; /* In-order delivery, enables FRCT. */
+ uint32_t max_gap; /* In ms. */
+ uint32_t timeout; /* Peer timeout time, in ms, 0 = no timeout. */
} qosspec_t;
static const qosspec_t qos_raw = {
@@ -45,7 +47,7 @@ static const qosspec_t qos_raw = {
.ber = 1,
.in_order = 0,
.max_gap = UINT32_MAX,
- .cypher_s = 0
+ .timeout = DEFAULT_PEER_TIMEOUT
};
static const qosspec_t qos_raw_no_errors = {
@@ -56,18 +58,7 @@ static const qosspec_t qos_raw_no_errors = {
.ber = 0,
.in_order = 0,
.max_gap = UINT32_MAX,
- .cypher_s = 0
-};
-
-static const qosspec_t qos_raw_crypt = {
- .delay = UINT32_MAX,
- .bandwidth = 0,
- .availability = 0,
- .loss = 1,
- .ber = 0,
- .in_order = 0,
- .max_gap = UINT32_MAX,
- .cypher_s = 256
+ .timeout = DEFAULT_PEER_TIMEOUT
};
static const qosspec_t qos_best_effort = {
@@ -78,18 +69,7 @@ static const qosspec_t qos_best_effort = {
.ber = 0,
.in_order = 1,
.max_gap = UINT32_MAX,
- .cypher_s = 0
-};
-
-static const qosspec_t qos_best_effort_crypt = {
- .delay = UINT32_MAX,
- .bandwidth = 0,
- .availability = 0,
- .loss = 1,
- .ber = 0,
- .in_order = 1,
- .max_gap = UINT32_MAX,
- .cypher_s = 256
+ .timeout = DEFAULT_PEER_TIMEOUT
};
static const qosspec_t qos_video = {
@@ -100,18 +80,7 @@ static const qosspec_t qos_video = {
.ber = 0,
.in_order = 1,
.max_gap = 100,
- .cypher_s = 0
-};
-
-static const qosspec_t qos_video_crypt = {
- .delay = 100,
- .bandwidth = UINT64_MAX,
- .availability = 3,
- .loss = 1,
- .ber = 0,
- .in_order = 1,
- .max_gap = 100,
- .cypher_s = 256
+ .timeout = DEFAULT_PEER_TIMEOUT
};
static const qosspec_t qos_voice = {
@@ -122,18 +91,7 @@ static const qosspec_t qos_voice = {
.ber = 0,
.in_order = 1,
.max_gap = 50,
- .cypher_s = 0
-};
-
-static const qosspec_t qos_voice_crypt = {
- .delay = 50,
- .bandwidth = 100000,
- .availability = 5,
- .loss = 1,
- .ber = 0,
- .in_order = 1,
- .max_gap = 50,
- .cypher_s = 256
+ .timeout = DEFAULT_PEER_TIMEOUT
};
static const qosspec_t qos_data = {
@@ -144,18 +102,7 @@ static const qosspec_t qos_data = {
.ber = 0,
.in_order = 1,
.max_gap = 2000,
- .cypher_s = 0
-};
-
-static const qosspec_t qos_data_crypt = {
- .delay = 1000,
- .bandwidth = 0,
- .availability = 0,
- .loss = 0,
- .ber = 0,
- .in_order = 1,
- .max_gap = 2000,
- .cypher_s = 256
+ .timeout = DEFAULT_PEER_TIMEOUT
};
#endif /* OUROBOROS_QOS_H */
diff --git a/include/ouroboros/qoscube.h b/include/ouroboros/qoscube.h
index 48705ffc..ed20484c 100644
--- a/include/ouroboros/qoscube.h
+++ b/include/ouroboros/qoscube.h
@@ -1,5 +1,5 @@
/*
- * Ouroboros - Copyright (C) 2016 - 2021
+ * Ouroboros - Copyright (C) 2016 - 2024
*
* Quality of Service cubes
*
@@ -20,8 +20,8 @@
* Foundation, Inc., http://www.fsf.org/about/contact/.
*/
-#ifndef OUROBOROS_QOSCUBE_H
-#define OUROBOROS_QOSCUBE_H
+#ifndef OUROBOROS_LIB_QOSCUBE_H
+#define OUROBOROS_LIB_QOSCUBE_H
#include <ouroboros/qos.h>
@@ -35,4 +35,4 @@ typedef enum qos_cube {
qoscube_t qos_spec_to_cube(qosspec_t qs);
qosspec_t qos_cube_to_spec(qoscube_t qc);
-#endif
+#endif /* OUROBOROS_LIB_QOSCUBE_H */
diff --git a/include/ouroboros/random.h b/include/ouroboros/random.h
index 1f1ab057..e1b25e5d 100644
--- a/include/ouroboros/random.h
+++ b/include/ouroboros/random.h
@@ -1,5 +1,5 @@
/*
- * Ouroboros - Copyright (C) 2016 - 2021
+ * Ouroboros - Copyright (C) 2016 - 2024
*
* Pseudo random generator
*
@@ -20,12 +20,12 @@
* Foundation, Inc., http://www.fsf.org/about/contact/.
*/
-#ifndef OUROBOROS_RANDOM_H
-#define OUROBOROS_RANDOM_H
+#ifndef OUROBOROS_LIB_RANDOM_H
+#define OUROBOROS_LIB_RANDOM_H
#include <sys/types.h>
int random_buffer(void * buf,
size_t len);
-#endif /* OUROBOROS_RANDOM_H */
+#endif /* OUROBOROS_LIB_RANDOM_H */
diff --git a/include/ouroboros/rib.h b/include/ouroboros/rib.h
index 9eab6334..cdc5a9d5 100644
--- a/include/ouroboros/rib.h
+++ b/include/ouroboros/rib.h
@@ -1,5 +1,5 @@
/*
- * Ouroboros - Copyright (C) 2016 - 2021
+ * Ouroboros - Copyright (C) 2016 - 2024
*
* RIB export using FUSE
*
@@ -23,8 +23,10 @@
#ifndef OUROBOROS_LIB_RIB_H
#define OUROBOROS_LIB_RIB_H
-#define RIB_PATH_LEN 128
+#define RIB_PATH_LEN 300
#define RIB_SEPARATOR "/"
+#define RIB_TM_STRLEN 26
+#define RIB_TM_FORMAT "%F %T (UTC)"
#include <sys/types.h>
@@ -53,4 +55,6 @@ int rib_reg(const char * path,
void rib_unreg(const char * path);
+void rib_cleanup(const char * mnt);
+
#endif /* OUROBOROS_LIB_RIB_H */
diff --git a/include/ouroboros/serdes-irm.h b/include/ouroboros/serdes-irm.h
new file mode 100644
index 00000000..bd04fc57
--- /dev/null
+++ b/include/ouroboros/serdes-irm.h
@@ -0,0 +1,80 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2024
+ *
+ * Ouroboros IRM Protocol - serialization/deserialization
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+#ifndef OUROBOROS_LIB_SERDES_IRM_H
+#define OUROBOROS_LIB_SERDES_IRM_H
+
+#include <ouroboros/crypt.h>
+#include <ouroboros/flow.h>
+#include <ouroboros/ipcp.h>
+#include <ouroboros/proc.h>
+#include <ouroboros/time.h>
+#include <ouroboros/utils.h>
+
+#include <inttypes.h>
+
+int flow_alloc__irm_req_ser(buffer_t * buf,
+ const struct flow_info * flow,
+ const char * dst,
+ const struct timespec * timeo);
+
+int flow_join__irm_req_ser(buffer_t * buf,
+ const struct flow_info * flow,
+ const char * dst,
+ const struct timespec * timeo);
+
+int flow_accept__irm_req_ser(buffer_t * buf,
+ const struct flow_info * flow,
+ const struct timespec * timeo);
+
+int ipcp_flow_req_arr__irm_req_ser(buffer_t * buf,
+ const buffer_t * dst,
+ const struct flow_info * flow,
+ const buffer_t * data);
+
+int ipcp_flow_alloc_reply__irm_msg_ser(buffer_t * buf,
+ const struct flow_info * flow,
+ int response,
+ const buffer_t * data);
+
+int flow__irm_result_des(buffer_t * buf,
+ struct flow_info * flow,
+ struct crypt_sk * sk);
+
+int flow_dealloc__irm_req_ser(buffer_t * buf,
+ const struct flow_info * flow,
+ const struct timespec * timeo);
+
+int ipcp_flow_dealloc__irm_req_ser(buffer_t * buf,
+ const struct flow_info * info);
+
+int ipcp_create_r__irm_req_ser(buffer_t * buf,
+ const struct ipcp_info * ipcp);
+
+int proc_announce__irm_req_ser(buffer_t * buf,
+ const struct proc_info * proc);
+
+int proc_exit__irm_req_ser(buffer_t * buf);
+
+int irm__irm_result_des(buffer_t * buf);
+
+#endif /* OUROBOROS_LIB_SERDES_IRM_H*/
diff --git a/include/ouroboros/serdes-oep.h b/include/ouroboros/serdes-oep.h
new file mode 100644
index 00000000..f7d8561a
--- /dev/null
+++ b/include/ouroboros/serdes-oep.h
@@ -0,0 +1,69 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2024
+ *
+ * Ouroboros Enrollment Protocol - serialization/deserialization
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+#ifndef OUROBOROS_LIB_SERDES_OEP_H
+#define OUROBOROS_LIB_SERDES_OEP_H
+
+#include <ouroboros/ipcp.h>
+#include <ouroboros/utils.h>
+
+#include <sys/time.h>
+
+/* Enrollment */
+
+#define ENROLL_ID_LEN 8
+
+struct enroll_req {
+ uint8_t id[ENROLL_ID_LEN];
+};
+
+struct enroll_resp {
+ uint8_t id[ENROLL_ID_LEN];
+ struct timespec t;
+ int response;
+ struct ipcp_config conf;
+};
+
+struct enroll_ack {
+ uint8_t id[ENROLL_ID_LEN];
+ int result;
+};
+
+ssize_t enroll_req_ser(const struct enroll_req * req,
+ buffer_t buf);
+
+int enroll_req_des(struct enroll_req * req,
+ const buffer_t buf);
+
+ssize_t enroll_resp_ser(const struct enroll_resp * resp,
+ buffer_t buf);
+
+int enroll_resp_des(struct enroll_resp * resp,
+ buffer_t buf);
+
+ssize_t enroll_ack_ser(const struct enroll_ack * ack,
+ buffer_t buf);
+
+int enroll_ack_des(struct enroll_ack * ack,
+ const buffer_t buf);
+
+#endif /* OUROBOROS_LIB_SERDES_OEP_H*/
diff --git a/include/ouroboros/sha3.h b/include/ouroboros/sha3.h
index 41967599..04871f3b 100644
--- a/include/ouroboros/sha3.h
+++ b/include/ouroboros/sha3.h
@@ -1,5 +1,5 @@
/*
- * Ouroboros - Copyright (C) 2016 - 2021
+ * Ouroboros - Copyright (C) 2016 - 2024
*
* SHA3 algorithm
*
diff --git a/include/ouroboros/shm_rdrbuff.h b/include/ouroboros/shm_rdrbuff.h
deleted file mode 100644
index 0bce681b..00000000
--- a/include/ouroboros/shm_rdrbuff.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Ouroboros - Copyright (C) 2016 - 2021
- *
- * Random Deletion Ring Buffer for Data Units
- *
- * Dimitri Staessens <dimitri@ouroboros.rocks>
- * Sander Vrijders <sander@ouroboros.rocks>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * version 2.1 as published by the Free Software Foundation.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., http://www.fsf.org/about/contact/.
- */
-
-#ifndef OUROBOROS_SHM_RDRBUFF_H
-#define OUROBOROS_SHM_RDRBUFF_H
-
-#include <ouroboros/shm_du_buff.h>
-#include <ouroboros/time_utils.h>
-
-#include <stdint.h>
-#include <pthread.h>
-#include <sys/types.h>
-
-struct shm_rdrbuff;
-
-struct shm_rdrbuff * shm_rdrbuff_create(void);
-
-struct shm_rdrbuff * shm_rdrbuff_open(void);
-
-void shm_rdrbuff_close(struct shm_rdrbuff * rdrb);
-
-void shm_rdrbuff_destroy(struct shm_rdrbuff * rdrb);
-
-void shm_rdrbuff_purge(void);
-
-/* Returns block index, a ptr and du_buff. */
-ssize_t shm_rdrbuff_alloc(struct shm_rdrbuff * rdrb,
- size_t count,
- uint8_t ** ptr,
- struct shm_du_buff ** sdb);
-
-ssize_t shm_rdrbuff_alloc_b(struct shm_rdrbuff * rdrb,
- size_t count,
- uint8_t ** ptr,
- struct shm_du_buff ** sdb,
- const struct timespec * abstime);
-
-ssize_t shm_rdrbuff_read(uint8_t ** dst,
- struct shm_rdrbuff * rdrb,
- size_t idx);
-
-struct shm_du_buff * shm_rdrbuff_get(struct shm_rdrbuff * rdrb,
- size_t idx);
-
-int shm_rdrbuff_remove(struct shm_rdrbuff * rdrb,
- size_t idx);
-
-#endif /* OUROBOROS_SHM_RDRBUFF_H */
diff --git a/include/ouroboros/sockets.h.in b/include/ouroboros/sockets.h.in
index f1162163..1a6974ac 100644
--- a/include/ouroboros/sockets.h.in
+++ b/include/ouroboros/sockets.h.in
@@ -1,5 +1,5 @@
/*
- * Ouroboros - Copyright (C) 2016 - 2021
+ * Ouroboros - Copyright (C) 2016 - 2024
*
* The sockets layer to communicate between daemons
*
@@ -20,52 +20,37 @@
* Foundation, Inc., http://www.fsf.org/about/contact/.
*/
-#ifndef OUROBOROS_SOCKETS_H
-#define OUROBOROS_SOCKETS_H
+#ifndef OUROBOROS_LIB_SOCKETS_H
+#define OUROBOROS_LIB_SOCKETS_H
-#include <ouroboros/qos.h>
+#include <ouroboros/protobuf.h>
#include <sys/types.h>
-#include "ipcp_config.pb-c.h"
-typedef IpcpConfigMsg ipcp_config_msg_t;
-typedef LayerInfoMsg layer_info_msg_t;
-
-#include "irmd_messages.pb-c.h"
-typedef IrmMsg irm_msg_t;
-typedef IpcpInfoMsg ipcp_info_msg_t;
-typedef NameInfoMsg name_info_msg_t;
-
-#include "ipcpd_messages.pb-c.h"
-typedef IpcpMsg ipcp_msg_t;
-
-#include "qosspec.pb-c.h"
-typedef QosspecMsg qosspec_msg_t;
-
-#define SOCK_PATH "/var/run/ouroboros/"
-#define SOCK_PATH_SUFFIX ".sock"
+#ifndef OUROBOROS_TEST
+ #define SOCK_PATH "/var/run/ouroboros/"
+#else
+ #define SOCK_PATH "/tmp/"
+#endif
+#define SOCK_PATH_SUFFIX ".sock"
-#define IRM_SOCK_PATH SOCK_PATH "irm" SOCK_PATH_SUFFIX
-#define IPCP_SOCK_PATH_PREFIX SOCK_PATH "ipcp"
+#define IRM_SOCK_PATH SOCK_PATH "irm" SOCK_PATH_SUFFIX
+#define IPCP_SOCK_PATH_PREFIX SOCK_PATH "ipcp."
-#define SOCK_BUF_SIZE @SOCK_BUF_SIZE@
+#define SOCK_BUF_SIZE @SOCK_BUF_SIZE@
-/* Returns the full socket path of an IPCP */
-char * ipcp_sock_path(pid_t pid);
+char * sock_path(pid_t pid,
+ const char * path);
int server_socket_open(char * file_name);
int client_socket_open(char * file_name);
-irm_msg_t * send_recv_irm_msg(irm_msg_t * msg);
-
-
-/* qos message conversion needed in different components */
-qosspec_msg_t spec_to_msg(const qosspec_t * qs);
+int send_recv_msg(buffer_t * buf);
-qosspec_t msg_to_spec(const qosspec_msg_t * msg);
+irm_msg_t * send_recv_irm_msg(irm_msg_t * msg);
/* cleanup socket when cancelling thread */
void __cleanup_close_ptr(void * o);
-#endif
+#endif /* OUROBOROS_LIB_SOCKETS_H */
diff --git a/include/ouroboros/shm_flow_set.h b/include/ouroboros/ssm_flow_set.h
index ba085aef..02a5cb71 100644
--- a/include/ouroboros/shm_flow_set.h
+++ b/include/ouroboros/ssm_flow_set.h
@@ -1,5 +1,5 @@
/*
- * Ouroboros - Copyright (C) 2016 - 2021
+ * Ouroboros - Copyright (C) 2016 - 2024
*
* Management of flow_sets for fqueue
*
@@ -20,45 +20,50 @@
* Foundation, Inc., http://www.fsf.org/about/contact/.
*/
-#ifndef OUROBOROS_SHM_FLOW_SET_H
-#define OUROBOROS_SHM_FLOW_SET_H
+#ifndef OUROBOROS_LIB_SSM_FLOW_SET_H
+#define OUROBOROS_LIB_SSM_FLOW_SET_H
#include <ouroboros/fqueue.h>
#include <sys/time.h>
-struct shm_flow_set;
+struct flowevent {
+ int flow_id;
+ int event;
+};
-struct shm_flow_set * shm_flow_set_create(pid_t pid);
+struct ssm_flow_set;
-void shm_flow_set_destroy(struct shm_flow_set * set);
+struct ssm_flow_set * ssm_flow_set_create(pid_t pid);
-struct shm_flow_set * shm_flow_set_open(pid_t pid);
+void ssm_flow_set_destroy(struct ssm_flow_set * set);
-void shm_flow_set_close(struct shm_flow_set * set);
+struct ssm_flow_set * ssm_flow_set_open(pid_t pid);
-void shm_flow_set_zero(struct shm_flow_set * shm_set,
+void ssm_flow_set_close(struct ssm_flow_set * set);
+
+void ssm_flow_set_zero(struct ssm_flow_set * set,
size_t idx);
-int shm_flow_set_add(struct shm_flow_set * shm_set,
+int ssm_flow_set_add(struct ssm_flow_set * set,
size_t idx,
int flow_id);
-int shm_flow_set_has(struct shm_flow_set * shm_set,
+int ssm_flow_set_has(struct ssm_flow_set * set,
size_t idx,
int flow_id);
-void shm_flow_set_del(struct shm_flow_set * shm_set,
+void ssm_flow_set_del(struct ssm_flow_set * set,
size_t idx,
int flow_id);
-void shm_flow_set_notify(struct shm_flow_set * set,
+void ssm_flow_set_notify(struct ssm_flow_set * set,
int flow_id,
int event);
-ssize_t shm_flow_set_wait(const struct shm_flow_set * shm_set,
+ssize_t ssm_flow_set_wait(const struct ssm_flow_set * set,
size_t idx,
- int * fqueue,
+ struct flowevent * fqueue,
const struct timespec * abstime);
-#endif /* OUROBOROS_SHM_FLOW_SET_H */
+#endif /* OUROBOROS_LIB_SSM_FLOW_SET_H */
diff --git a/include/ouroboros/shm_du_buff.h b/include/ouroboros/ssm_pk_buff.h
index da350055..9b82ede3 100644
--- a/include/ouroboros/shm_du_buff.h
+++ b/include/ouroboros/ssm_pk_buff.h
@@ -1,5 +1,5 @@
/*
- * Ouroboros - Copyright (C) 2016 - 2021
+ * Ouroboros - Copyright (C) 2016 - 2024
*
* Data Buffer element in Random Deletion Ring Buffer
*
@@ -20,37 +20,39 @@
* Foundation, Inc., http://www.fsf.org/about/contact/.
*/
-#ifndef OUROBOROS_SHM_DU_BUFF_H
-#define OUROBOROS_SHM_DU_BUFF_H
+#ifndef OUROBOROS_LIB_SSM_PK_BUFF_H
+#define OUROBOROS_LIB_SSM_PK_BUFF_H
#include <sys/types.h>
#include <stdint.h>
-struct shm_du_buff;
+struct ssm_pk_buff;
-size_t shm_du_buff_get_idx(struct shm_du_buff * sdb);
+size_t ssm_pk_buff_get_idx(struct ssm_pk_buff * spb);
-uint8_t * shm_du_buff_head(struct shm_du_buff * sdb);
+uint8_t * ssm_pk_buff_head(struct ssm_pk_buff * spb);
-uint8_t * shm_du_buff_tail(struct shm_du_buff * sdb);
+uint8_t * ssm_pk_buff_tail(struct ssm_pk_buff * spb);
-uint8_t * shm_du_buff_head_alloc(struct shm_du_buff * sdb,
+size_t ssm_pk_buff_len(struct ssm_pk_buff * spb);
+
+uint8_t * ssm_pk_buff_head_alloc(struct ssm_pk_buff * spb,
size_t size);
-uint8_t * shm_du_buff_tail_alloc(struct shm_du_buff * sdb,
+uint8_t * ssm_pk_buff_tail_alloc(struct ssm_pk_buff * spb,
size_t size);
-uint8_t * shm_du_buff_head_release(struct shm_du_buff * sdb,
+uint8_t * ssm_pk_buff_head_release(struct ssm_pk_buff * spb,
size_t size);
-uint8_t * shm_du_buff_tail_release(struct shm_du_buff * sdb,
+uint8_t * ssm_pk_buff_tail_release(struct ssm_pk_buff * spb,
size_t size);
-void shm_du_buff_truncate(struct shm_du_buff * sdb,
+void ssm_pk_buff_truncate(struct ssm_pk_buff * spb,
size_t len);
-int shm_du_buff_wait_ack(struct shm_du_buff * sdb);
+int ssm_pk_buff_wait_ack(struct ssm_pk_buff * spb);
-int shm_du_buff_ack(struct shm_du_buff * sdb);
+int ssm_pk_buff_ack(struct ssm_pk_buff * spb);
-#endif /* OUROBOROS_SHM_DU_BUFF_H */
+#endif /* OUROBOROS_LIB_SSM_PK_BUFF_H */
diff --git a/include/ouroboros/ssm_pool.h b/include/ouroboros/ssm_pool.h
new file mode 100644
index 00000000..4becbdf5
--- /dev/null
+++ b/include/ouroboros/ssm_pool.h
@@ -0,0 +1,74 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2024
+ *
+ * Secure Shared Memory infrastructure (SSM) Packet Buffer
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+#ifndef OUROBOROS_LIB_SSM_POOL_H
+#define OUROBOROS_LIB_SSM_POOL_H
+
+#include <ouroboros/ssm_pk_buff.h>
+#include <ouroboros/time.h>
+
+#include <pthread.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+struct ssm_pool;
+
+/* Pool API: uid = 0 for GSPP (privileged), uid > 0 for PUP (per-user) */
+struct ssm_pool * ssm_pool_create(uid_t uid,
+ gid_t gid);
+
+struct ssm_pool * ssm_pool_open(uid_t uid);
+
+void ssm_pool_close(struct ssm_pool * pool);
+
+void ssm_pool_destroy(struct ssm_pool * pool);
+
+int ssm_pool_mlock(struct ssm_pool * pool);
+
+void ssm_pool_gspp_purge(void);
+
+/* Alloc count bytes, returns block index, a ptr and pk_buff. */
+ssize_t ssm_pool_alloc(struct ssm_pool * pool,
+ size_t count,
+ uint8_t ** ptr,
+ struct ssm_pk_buff ** spb);
+
+ssize_t ssm_pool_alloc_b(struct ssm_pool * pool,
+ size_t count,
+ uint8_t ** ptr,
+ struct ssm_pk_buff ** spb,
+ const struct timespec * abstime);
+
+ssize_t ssm_pool_read(uint8_t ** dst,
+ struct ssm_pool * pool,
+ size_t idx);
+
+struct ssm_pk_buff * ssm_pool_get(struct ssm_pool * pool,
+ size_t idx);
+
+int ssm_pool_remove(struct ssm_pool * pool,
+ size_t idx);
+
+void ssm_pool_reclaim_orphans(struct ssm_pool * pool,
+ pid_t pid);
+
+#endif /* OUROBOROS_LIB_SSM_POOL_H */
diff --git a/include/ouroboros/shm_rbuff.h b/include/ouroboros/ssm_rbuff.h
index e853e487..791befa2 100644
--- a/include/ouroboros/shm_rbuff.h
+++ b/include/ouroboros/ssm_rbuff.h
@@ -1,5 +1,5 @@
/*
- * Ouroboros - Copyright (C) 2016 - 2021
+ * Ouroboros - Copyright (C) 2016 - 2024
*
* Ring buffer for incoming packets
*
@@ -20,8 +20,8 @@
* Foundation, Inc., http://www.fsf.org/about/contact/.
*/
-#ifndef OUROBOROS_SHM_RBUFF_H
-#define OUROBOROS_SHM_RBUFF_H
+#ifndef OUROBOROS_LIB_SSM_RBUFF_H
+#define OUROBOROS_LIB_SSM_RBUFF_H
#include <sys/types.h>
#include <sys/time.h>
@@ -31,38 +31,41 @@
#define ACL_RDWR 0000
#define ACL_RDONLY 0001
#define ACL_FLOWDOWN 0002
+#define ACL_FLOWPEER 0004
-struct shm_rbuff;
+struct ssm_rbuff;
-struct shm_rbuff * shm_rbuff_create(pid_t pid,
+struct ssm_rbuff * ssm_rbuff_create(pid_t pid,
int flow_id);
-struct shm_rbuff * shm_rbuff_open(pid_t pid,
- int flow_id);
+void ssm_rbuff_destroy(struct ssm_rbuff * rb);
-void shm_rbuff_close(struct shm_rbuff * rb);
+struct ssm_rbuff * ssm_rbuff_open(pid_t pid,
+ int flow_id);
-void shm_rbuff_destroy(struct shm_rbuff * rb);
+void ssm_rbuff_close(struct ssm_rbuff * rb);
-void shm_rbuff_set_acl(struct shm_rbuff * rb,
+void ssm_rbuff_set_acl(struct ssm_rbuff * rb,
uint32_t flags);
-uint32_t shm_rbuff_get_acl(struct shm_rbuff * rb);
+uint32_t ssm_rbuff_get_acl(struct ssm_rbuff * rb);
+
+void ssm_rbuff_fini(struct ssm_rbuff * rb);
-void shm_rbuff_fini(struct shm_rbuff * rb);
+int ssm_rbuff_mlock(struct ssm_rbuff * rb);
-int shm_rbuff_write(struct shm_rbuff * rb,
+int ssm_rbuff_write(struct ssm_rbuff * rb,
size_t idx);
-int shm_rbuff_write_b(struct shm_rbuff * rb,
+int ssm_rbuff_write_b(struct ssm_rbuff * rb,
size_t idx,
const struct timespec * abstime);
-ssize_t shm_rbuff_read(struct shm_rbuff * rb);
+ssize_t ssm_rbuff_read(struct ssm_rbuff * rb);
-ssize_t shm_rbuff_read_b(struct shm_rbuff * rb,
+ssize_t ssm_rbuff_read_b(struct ssm_rbuff * rb,
const struct timespec * abstime);
-size_t shm_rbuff_queued(struct shm_rbuff * rb);
+size_t ssm_rbuff_queued(struct ssm_rbuff * rb);
-#endif /* OUROBOROS_SHM_RBUFF_H */
+#endif /* OUROBOROS_LIB_SSM_RBUFF_H */
diff --git a/include/ouroboros/time_utils.h b/include/ouroboros/time.h
index 6e51f305..3bd6a257 100644
--- a/include/ouroboros/time_utils.h
+++ b/include/ouroboros/time.h
@@ -1,5 +1,5 @@
/*
- * Ouroboros - Copyright (C) 2016 - 2021
+ * Ouroboros - Copyright (C) 2016 - 2024
*
* Time utilities
*
@@ -20,8 +20,8 @@
* Foundation, Inc., http://www.fsf.org/about/contact/.
*/
-#ifndef OUROBOROS_TIME_UTILS_H
-#define OUROBOROS_TIME_UTILS_H
+#ifndef OUROBOROS_LIB_TIME_H
+#define OUROBOROS_LIB_TIME_H
#ifdef MILLION
#undef MILLION
@@ -31,25 +31,38 @@
#undef BILLION
#endif
-#define MILLION 1000000L
-#define BILLION 1000000000L
+#define MILLION 1000000LL
+#define BILLION 1000000000LL
#include <time.h>
#include <sys/time.h>
+#include <sys/types.h>
+
+#define TIMESPEC_INIT_S(s) {(s), 0}
+#define TIMESPEC_INIT_MS(ms) {(ms) / 1000, ((ms) % 1000) * MILLION}
+#define TIMESPEC_INIT_US(us) {(us) / MILLION, ((us) % MILLION) * 1000}
+#define TIMESPEC_INIT_NS(ns) {(ns) / BILLION, ((ns) % BILLION)}
+
+#define TS_TO_UINT64(ts) \
+ ((uint64_t)(ts).tv_sec * BILLION + (uint64_t)(ts).tv_nsec)
+
+#define TIMEVAL_INIT_S(s) {(s), 0}
+#define TIMEVAL_INIT_MS(ms) {(ms) / 1000, ((ms) % 1000) * 1000}
+#define TIMEVAL_INIT_US(us) {(us) / MILLION, ((us) % MILLION)}
/* functions for timespecs */
-#define ts_diff_ns(t0, tx) (((tx)->tv_sec - (t0)->tv_sec) * BILLION \
+#define ts_diff_ns(tx, t0) (((tx)->tv_sec - (t0)->tv_sec) * BILLION \
+ ((tx)->tv_nsec - (t0)->tv_nsec))
-#define ts_diff_us(t0, tx) (((tx)->tv_sec - (t0)->tv_sec) * MILLION \
+#define ts_diff_us(tx, t0) (((tx)->tv_sec - (t0)->tv_sec) * MILLION \
+ ((tx)->tv_nsec - (t0)->tv_nsec) / 1000L)
-#define ts_diff_ms(t0, tx) (((tx)->tv_sec - (t0)->tv_sec) * 1000L \
+#define ts_diff_ms(tx, t0) (((tx)->tv_sec - (t0)->tv_sec) * 1000L \
+ ((tx)->tv_nsec - (t0)->tv_nsec) / MILLION)
/* functions for timevals are the same */
-#define tv_diff_us(t0, tx) (((tx)->tv_sec - (t0)->tv_sec) * MILLION \
+#define tv_diff_us(tx, t0) (((tx)->tv_sec - (t0)->tv_sec) * MILLION \
+ + ((tx)->tv_usec - (t0)->tv_usec))
+#define tv_diff_ms(tx, t0) (((tx)->tv_sec - (t0)->tv_sec) * 1000L \
+ ((tx)->tv_usec - (t0)->tv_usec) / 1000L)
-#define tv_diff_ms(t0, tx) (((tx)->tv_sec - (t0)->tv_sec) * 1000L \
- + ((tx)->tv_usec - (t0)->tv_usec) / MILLION)
/* functions for timespecs */
@@ -118,4 +131,4 @@
(tv)->tv_usec = (ts)->tv_nsec / 1000L; \
} while (0);
-#endif /* OUROBOROS_TIME_UTILS_H */
+#endif /* OUROBOROS_LIB_TIME_H */
diff --git a/include/ouroboros/tpm.h b/include/ouroboros/tpm.h
index a69549d2..3fb49b88 100644
--- a/include/ouroboros/tpm.h
+++ b/include/ouroboros/tpm.h
@@ -1,5 +1,5 @@
/*
- * Ouroboros - Copyright (C) 2016 - 2021
+ * Ouroboros - Copyright (C) 2016 - 2024
*
* Threadpool management
*
@@ -38,8 +38,10 @@ int tpm_start(struct tpm * tpm);
void tpm_stop(struct tpm * tpm);
-void tpm_dec(struct tpm * tpm);
+void tpm_begin_work(struct tpm * tpm);
-void tpm_inc(struct tpm * tpm);
+void tpm_wait_work(struct tpm * tpm);
+
+void tpm_end_work(struct tpm * tpm);
#endif /* OUROBOROS_LIB_TPM_H */
diff --git a/include/ouroboros/utils.h b/include/ouroboros/utils.h
index 426a143c..f53361eb 100644
--- a/include/ouroboros/utils.h
+++ b/include/ouroboros/utils.h
@@ -1,5 +1,5 @@
/*
- * Ouroboros - Copyright (C) 2016 - 2021
+ * Ouroboros - Copyright (C) 2016 - 2024
*
* Handy utilities
*
@@ -20,38 +20,67 @@
* Foundation, Inc., http://www.fsf.org/about/contact/.
*/
-#ifndef OUROBOROS_UTILS_H
-#define OUROBOROS_UTILS_H
+#ifndef OUROBOROS_LIB_UTILS_H
+#define OUROBOROS_LIB_UTILS_H
+#include <stdbool.h>
#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
#include <unistd.h>
#define MIN(a,b) (((a) < (b)) ? (a) : (b))
#define MAX(a,b) (((a) > (b)) ? (a) : (b))
#define ABS(a) ((a) > 0 ? (a) : -(a))
+#define clrbuf(buf) do { memset(&(buf), 0, sizeof(buf)); } while (0)
+#define freebuf(buf) do { free((buf).data); clrbuf(buf); } while (0)
+#define BUF_INIT { 0, NULL }
+#define BUF_IS_EMPTY(buf) ((buf)->data == NULL && (buf)->len == 0)
typedef struct {
- uint8_t * data;
size_t len;
+ uint8_t * data;
} buffer_t;
+int bufcmp(const buffer_t * a,
+ const buffer_t * b);
+
/*
* Returns the number of characters a uint would
* need when represented as a string
*/
int n_digits(unsigned i);
-/* gets the application name */
-char * path_strip(char * src);
+char * path_strip(const char * src);
+
+char * trim_whitespace(char * str);
+
+bool is_ouroboros_member_uid(uid_t uid);
+
+bool is_ouroboros_member(void);
+
+/* functions for copying and destroying arguments list */
+size_t argvlen(const char ** argv);
+
+char ** argvdup(char ** argv);
+
+void argvfree(char ** argv);
/* destroy a ** */
#define freepp(type, ptr, len) \
do { \
- if (len == 0) \
- break; \
- while (len > 0) \
- free(((type **) ptr)[--len]); \
+ while (len-- > 0) \
+ free(((type **) ptr)[len]); \
+ free(ptr); \
+ } while (0)
+
+/* destroys an array of buffers */
+#define freebufs(ptr, len) \
+ do { \
+ while ((len)-- > 0) \
+ freebuf((ptr)[len]); \
free(ptr); \
- } while (0);
+ } while (0)
-#endif /* OUROBOROS_UTILS_H */
+#endif /* OUROBOROS_LIB_UTILS_H */
diff --git a/include/ouroboros/version.h.in b/include/ouroboros/version.h.in
index 4306b239..c006a095 100644
--- a/include/ouroboros/version.h.in
+++ b/include/ouroboros/version.h.in
@@ -1,5 +1,5 @@
/*
- * Ouroboros - Copyright (C) 2016 - 2021
+ * Ouroboros - Copyright (C) 2016 - 2024
*
* Ouroboros version
*
diff --git a/include/test/certs.h b/include/test/certs.h
new file mode 100644
index 00000000..1b847406
--- /dev/null
+++ b/include/test/certs.h
@@ -0,0 +1,125 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2026
+ *
+ * Test certificates - ECDSA/P-256 signed certificates
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+#ifndef TEST_CERTS_H
+#define TEST_CERTS_H
+
+/*
+* Certificates created following the guide
+* Building an openssl certificate authority
+* on
+* https://community.f5.com/kb/technicalarticles/
+*/
+
+/* Root certificate for CA ca.unittest.o7s */
+static const char * root_ca_crt_ec = \
+"-----BEGIN CERTIFICATE-----\n"
+"MIICXTCCAgOgAwIBAgIURlENlCOy1OsA/AXFscPUQ2li8OYwCgYIKoZIzj0EAwIw\n"
+"fDELMAkGA1UEBhMCQkUxDDAKBgNVBAgMA09WTDEOMAwGA1UEBwwFR2hlbnQxDDAK\n"
+"BgNVBAoMA283czEVMBMGA1UECwwMdW5pdHRlc3QubzdzMRgwFgYDVQQDDA9jYS51\n"
+"bml0dGVzdC5vN3MxEDAOBgkqhkiG9w0BCQEWASAwHhcNMjUwODAzMTg1MzE1WhcN\n"
+"NDUwNzI5MTg1MzE1WjB8MQswCQYDVQQGEwJCRTEMMAoGA1UECAwDT1ZMMQ4wDAYD\n"
+"VQQHDAVHaGVudDEMMAoGA1UECgwDbzdzMRUwEwYDVQQLDAx1bml0dGVzdC5vN3Mx\n"
+"GDAWBgNVBAMMD2NhLnVuaXR0ZXN0Lm83czEQMA4GCSqGSIb3DQEJARYBIDBZMBMG\n"
+"ByqGSM49AgEGCCqGSM49AwEHA0IABEPMseCScbd/d5TlHmyYVszn/YGVeNdUCnFR\n"
+"naOr95WlTNo3MyKKBuoiEFwHhjPASgXr/VDVjJLSyM3JUPebAcGjYzBhMB0GA1Ud\n"
+"DgQWBBQkxjMILHH6lZ+rnCMnD/63GO3y1zAfBgNVHSMEGDAWgBQkxjMILHH6lZ+r\n"
+"nCMnD/63GO3y1zAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAKBggq\n"
+"hkjOPQQDAgNIADBFAiEA1jVJWW4idkCgAYv0m2LT9C33Dq42aLyRkJ+9YdzDqLwC\n"
+"IHT6MS4I0k52YP/hxoqWVBbpOW79PKYMRLyXTk1r7+Fa\n"
+"-----END CERTIFICATE-----\n";
+
+/* Certificate for intermediary im.unittest.o7s used for signing */
+static const char * im_ca_crt_ec = \
+"-----BEGIN CERTIFICATE-----\n"
+"MIICbTCCAhOgAwIBAgICEAMwCgYIKoZIzj0EAwIwfDELMAkGA1UEBhMCQkUxDDAK\n"
+"BgNVBAgMA09WTDEOMAwGA1UEBwwFR2hlbnQxDDAKBgNVBAoMA283czEVMBMGA1UE\n"
+"CwwMdW5pdHRlc3QubzdzMRgwFgYDVQQDDA9jYS51bml0dGVzdC5vN3MxEDAOBgkq\n"
+"hkiG9w0BCQEWASAwHhcNMjUwODAzMTkwMjU3WhcNNDUwNzI5MTkwMjU3WjBaMQsw\n"
+"CQYDVQQGEwJCRTEMMAoGA1UECAwDT1ZMMQwwCgYDVQQKDANvN3MxFTATBgNVBAsM\n"
+"DHVuaXR0ZXN0Lm83czEYMBYGA1UEAwwPaW0udW5pdHRlc3QubzdzMFkwEwYHKoZI\n"
+"zj0CAQYIKoZIzj0DAQcDQgAEdlra08XItIPtVl5veaq4UF6LIcBXj2mZFqKNEXFh\n"
+"l9uAz6UAbIc+FUPNfom6dwKbg/AjQ82a100eh6K/jCY7eKOBpjCBozAdBgNVHQ4E\n"
+"FgQUy8Go8BIO6i0lJ+mgBr9lvh2L0eswHwYDVR0jBBgwFoAUJMYzCCxx+pWfq5wj\n"
+"Jw/+txjt8tcwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwEQYD\n"
+"VR0fBAowCDAGoASgAoYAMCoGCCsGAQUFBwEBBB4wHDAMBggrBgEFBQcwAoYAMAwG\n"
+"CCsGAQUFBzABhgAwCgYIKoZIzj0EAwIDSAAwRQIhAN3ZYhqu6mVLGidmONsbANk5\n"
+"rzT6aHJcmvj19OxMusaXAiBKy0gBFCri/GLizi4wZo09wf31yZMqfr8IrApvPaLw\n"
+"qA==\n"
+"-----END CERTIFICATE-----\n";
+
+/* Server test-1.unittest.o7s private-public key pair */
+static const char * server_pkp_ec = \
+"-----BEGIN EC PRIVATE KEY-----\n"
+"MHcCAQEEIA4/bcmquVvGrY4+TtfnFSy1SpXs896r5xJjGuD6NmGRoAoGCCqGSM49\n"
+"AwEHoUQDQgAE4BSOhv36q4bCMLSkJaCvzwZ3pPy2M0YzRKFKeV48tG5eD+MBaTrT\n"
+"eoHUcRfpz0EO/inq3FVDzEoAQ2NWpnz0kA==\n"
+"-----END EC PRIVATE KEY-----\n";
+
+/* Public key for the Private key */
+static __attribute__((unused)) const char * server_pk_ec = \
+"-----BEGIN PUBLIC KEY-----\n"
+"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE4BSOhv36q4bCMLSkJaCvzwZ3pPy2\n"
+"M0YzRKFKeV48tG5eD+MBaTrTeoHUcRfpz0EO/inq3FVDzEoAQ2NWpnz0kA==\n"
+"-----END PUBLIC KEY-----\n";
+
+/* Valid signed server certificate for test-1.unittest.o7s */
+#define SSC_TEXT_SIZE 2295 /* size of cleartext certificate */
+static const char * signed_server_crt_ec = \
+"-----BEGIN CERTIFICATE-----\n"
+"MIIDiTCCAy+gAwIBAgICEAUwCgYIKoZIzj0EAwIwWjELMAkGA1UEBhMCQkUxDDAK\n"
+"BgNVBAgMA09WTDEMMAoGA1UECgwDbzdzMRUwEwYDVQQLDAx1bml0dGVzdC5vN3Mx\n"
+"GDAWBgNVBAMMD2ltLnVuaXR0ZXN0Lm83czAeFw0yNTA4MDgxODQ4NTNaFw00NTA4\n"
+"MDMxODQ4NTNaMG4xCzAJBgNVBAYTAkJFMQwwCgYDVQQIDANPVkwxDjAMBgNVBAcM\n"
+"BUdoZW50MQwwCgYDVQQKDANvN3MxFTATBgNVBAsMDHVuaXR0ZXN0Lm83czEcMBoG\n"
+"A1UEAwwTdGVzdC0xLnVuaXR0ZXN0Lm83czBZMBMGByqGSM49AgEGCCqGSM49AwEH\n"
+"A0IABOAUjob9+quGwjC0pCWgr88Gd6T8tjNGM0ShSnlePLRuXg/jAWk603qB1HEX\n"
+"6c9BDv4p6txVQ8xKAENjVqZ89JCjggHPMIIByzAJBgNVHRMEAjAAMBEGCWCGSAGG\n"
+"+EIBAQQEAwIGQDA4BglghkgBhvhCAQ0EKxYpbzdzIHVuaXR0ZXN0IEdlbmVyYXRl\n"
+"ZCBTZXJ2ZXIgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFI+htsK0xxy6e1CqCyxn7mqi\n"
+"wRrpMIGoBgNVHSMEgaAwgZ2AFMvBqPASDuotJSfpoAa/Zb4di9HroYGApH4wfDEL\n"
+"MAkGA1UEBhMCQkUxDDAKBgNVBAgMA09WTDEOMAwGA1UEBwwFR2hlbnQxDDAKBgNV\n"
+"BAoMA283czEVMBMGA1UECwwMdW5pdHRlc3QubzdzMRgwFgYDVQQDDA9jYS51bml0\n"
+"dGVzdC5vN3MxEDAOBgkqhkiG9w0BCQEWASCCAhADMA4GA1UdDwEB/wQEAwIFoDAT\n"
+"BgNVHSUEDDAKBggrBgEFBQcDATAoBgNVHR8EITAfMB2gG6AZhhdodHRwczovL291\n"
+"cm9ib3Jvcy5yb2NrczBYBggrBgEFBQcBAQRMMEowIwYIKwYBBQUHMAKGF2h0dHBz\n"
+"Oi8vb3Vyb2Jvcm9zLnJvY2tzMCMGCCsGAQUFBzABhhdodHRwczovL291cm9ib3Jv\n"
+"cy5yb2NrczAKBggqhkjOPQQDAgNIADBFAiBZuw/Yb2pq925H7pEiOXr4fMo0wknz\n"
+"ktkxoHAFbjQEPQIhAMInHI7lvRmS0IMw1wBF/WlUZWKvhyU/TeMIZfk/JGCS\n"
+"-----END CERTIFICATE-----\n";
+
+/* Self-signed by server test-1.unittest.o7s using its key */
+static __attribute__((unused)) const char * server_crt_ec = \
+"-----BEGIN CERTIFICATE-----\n"
+"MIIBfjCCASWgAwIBAgIUB5VYxp7i+sgYjvLiwfpf0W5NfqQwCgYIKoZIzj0EAwIw\n"
+"HjEcMBoGA1UEAwwTdGVzdC0xLnVuaXR0ZXN0Lm83czAeFw0yNTA4MDMxOTI4MzVa\n"
+"Fw00NTA3MjkxOTI4MzVaMB4xHDAaBgNVBAMME3Rlc3QtMS51bml0dGVzdC5vN3Mw\n"
+"WTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATgFI6G/fqrhsIwtKQloK/PBnek/LYz\n"
+"RjNEoUp5Xjy0bl4P4wFpOtN6gdRxF+nPQQ7+KercVUPMSgBDY1amfPSQo0EwPzAe\n"
+"BgNVHREEFzAVghN0ZXN0LTEudW5pdHRlc3QubzdzMB0GA1UdDgQWBBSPobbCtMcc\n"
+"untQqgssZ+5qosEa6TAKBggqhkjOPQQDAgNHADBEAiAoFC/rqgrRXmMUx4y5cPbv\n"
+"jOKpoL3FpehRgGkPatmL/QIgMRHc2TSGo6q1SG22Xt1dHAIBsaN2AlSfhjKULMH5\n"
+"gRo=\n"
+"-----END CERTIFICATE-----\n";
+
+#endif /* TEST_CERTS_H */
+
diff --git a/include/test/certs_pqc.h b/include/test/certs_pqc.h
new file mode 100644
index 00000000..b533ca60
--- /dev/null
+++ b/include/test/certs_pqc.h
@@ -0,0 +1,656 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2026
+ *
+ * Test certificates - ML-DSA-65 (post-quantum) signed certificates
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+#ifndef TEST_CERTS_PQC_H
+#define TEST_CERTS_PQC_H
+
+/*
+ * ML-DSA-65 certificates for testing post-quantum cryptography
+ * Root CA: ca.unittest.o7s
+ * Intermediate CA: im.unittest.o7s (pathlen:0)
+ * Server: test-1.unittest.o7s
+ */
+
+/* PEM certificate strings will go here */
+static const char * root_ca_crt_ml = \
+"-----BEGIN CERTIFICATE-----\n"
+"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\n"
+"-----END CERTIFICATE-----\n";
+
+static const char * im_ca_crt_ml = \
+"-----BEGIN CERTIFICATE-----\n"
+"MIIVyDCCCMWgAwIBAgICEAAwCwYJYIZIAWUDBAMSMFMxCzAJBgNVBAYTAkJFMQww\n"
+"CgYDVQQIDANPVkwxDjAMBgNVBAcMBUdoZW50MQwwCgYDVQQKDANvN3MxGDAWBgNV\n"
+"BAMMD2NhLnVuaXR0ZXN0Lm83czAeFw0yNjAxMDUxOTE1MjJaFw0zNjAxMDMxOTE1\n"
+"MjJaMBoxGDAWBgNVBAMMD2ltLnVuaXR0ZXN0Lm83czCCB7IwCwYJYIZIAWUDBAMS\n"
+"A4IHoQA5Wkc2Mz19rGlaczLiVz+WqRHexHpdzgKP/wXNiduzFZtYxbXVPJHPybkX\n"
+"Jx8p18vxsTHBsUz1ST+nOqLKH0RAqqEQWBLmf2bg0To7aLkN53lg40xcZ8XszXBg\n"
+"H0cjl/agLFSvPoSpLv5R25FeR5mK5pkpaCzY29Wg2xppeAFeMPL1YBUtsxiDoLWG\n"
+"xnquhrDRWrFCHDcpcG+qFCrwSk30EJgIEFz8zAucZk++MWXINmm0rKzqAZHkh9zo\n"
+"IXHTulI6zFGxFDP19eRoke4AzmC6xJyw9W+WIU84TyU9o4bSfQtJjLFn7qk410a2\n"
+"/zm3mXS9fQiqqZm8M6mAk6UTFVHHr6/SzzwFutn652qz8xhSDkOlsd8jz9XixEpk\n"
+"wXQiZZMaai9MTW9m5Hzi+8mFMcMmGwsIgTWo/G0os8d8hxPWcRXO4lXfuHa83dUP\n"
+"KWHEcroEBjdN67R3qepCONhvq8VSHbw1/tkm8VjzTz2VCQUjFSkn/rxqJAi1cIoh\n"
+"ZEBuDuJ2Ilx3DlFAQTrMl6L6HwIqetYYyYoMUSL09MtAA1EZTIJm6CHM+gxnef20\n"
+"mLIvWDaXicm01FpBaxJwGqWD0fS+2Ii4+XwnxYh3kKLD1x1O4guIqPDPG3NVBdu5\n"
+"pKxYLAgcE9OJc3I7SfgimQCweGuEUGwHSqMjcYKYWiAodlo2z4+GtAN08Np+Av0/\n"
+"fVTOHW6aFMnvxfcpsZv0fBcwgOm+vvuxmKdIOTjJiQnLPqWWAykt3LM5AaoBT0v5\n"
+"Pd3OU7z4p/XXwkYgQXFV58nOttFHHE0ANKiLUF7iBmu2Vz3+n8ewTW/nYABUEZlC\n"
+"W+VfnhHwYhgmtg2krCBW9KNI1Vvfw9WutJxT/8+lAN3P/ZqyvF6vS9rECfn1zFIr\n"
+"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\n"
+"AAAAAAAABQoQFyIo\n"
+"-----END CERTIFICATE-----\n";
+
+static const char * server_pkp_ml = \
+"-----BEGIN PRIVATE KEY-----\n"
+"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\n"
+"DngTH/g89SEnGxqKXe7RxVof\n"
+"-----END PRIVATE KEY-----\n";
+
+static __attribute__((unused)) const char * server_pk_ml = \
+"-----BEGIN PUBLIC KEY-----\n"
+"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\n"
+"k1M8ksQo\n"
+"-----END PUBLIC KEY-----\n";
+
+static const char * signed_server_crt_ml = \
+"-----BEGIN CERTIFICATE-----\n"
+"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\n"
+"AAAAAAAAAAAAAAAABgkOExgc\n"
+"-----END CERTIFICATE-----\n";
+
+static __attribute__((unused)) const char * server_crt_ml = \
+"-----BEGIN CERTIFICATE-----\n"
+"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\n"
+"-----END CERTIFICATE-----\n";
+
+#endif /* TEST_CERTS_PQC_H */
+
diff --git a/include/test/test.h b/include/test/test.h
new file mode 100644
index 00000000..306e737a
--- /dev/null
+++ b/include/test/test.h
@@ -0,0 +1,109 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2024
+ *
+ * Test macros
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+#ifndef OUROBOROS_LIB_TEST_H
+#define OUROBOROS_LIB_TEST_H
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <sys/resource.h>
+
+#define TEST_RC_SUCCESS 0
+#define TEST_RC_SKIP 1
+#define TEST_RC_FAIL -1
+
+#define TEST_START(...) \
+ do { \
+ printf("%s", __func__); \
+ if (sizeof(#__VA_ARGS__) > 1) \
+ printf(" " __VA_ARGS__); \
+ printf(" started.\n"); \
+ fflush(stdout); \
+ } while (0)
+
+#define TEST_SUCCESS(...) \
+ do { \
+ printf("\x1b[32m%s", __func__); \
+ if (sizeof(#__VA_ARGS__) > 1) \
+ printf(" " __VA_ARGS__); \
+ printf(" succeeded.\x1b[0m\n"); \
+ fflush(stdout); \
+ } while (0)
+
+#define TEST_SKIPPED() \
+ do { \
+ printf("\x1b[33m%s skipped.\x1b[0m\n", __func__); \
+ fflush(stdout); \
+ } while (0)
+
+#define TEST_FAIL(...) \
+ do { \
+ printf("\x1b[31m%s", __func__); \
+ if (sizeof(#__VA_ARGS__) > 1) \
+ printf(" " __VA_ARGS__); \
+ printf(" failed.\x1b[0m\n"); \
+ fflush(stdout); \
+ } while (0)
+
+#define TEST_END(result) \
+ do { if (result == 0) TEST_SUCCESS(); else TEST_FAIL(); } while (0)
+
+static int __attribute__((unused)) test_assert_fail(int(* testfunc)(void))
+{
+ pid_t pid;
+ int wstatus;
+
+ pid = fork();
+ if (pid == -1) {
+ printf("Failed to fork: %s.\n", strerror(errno));
+ return TEST_RC_FAIL;
+ }
+
+ if (pid == 0) {
+#ifdef DISABLE_TESTS_CORE_DUMPS
+ struct rlimit rl = { .rlim_cur = 0, .rlim_max = 0 };
+ setrlimit(RLIMIT_CORE, &rl);
+#endif
+ return testfunc(); /* should abort */
+ }
+
+ waitpid(pid, &wstatus, 0);
+#ifdef CONFIG_OUROBOROS_DEBUG
+ if (WIFSIGNALED(wstatus) && (wstatus == 134 || wstatus == 6))
+ return TEST_RC_SUCCESS;
+
+ printf("Process did not abort, status: %d.\n", wstatus);
+#else
+ if (WIFEXITED(wstatus) && wstatus == 0)
+ return TEST_RC_SUCCESS;
+
+ printf("Process did not exit, status: %d.\n", wstatus);
+#endif
+
+ return TEST_RC_FAIL;
+}
+
+#endif /* OUROBOROS_LIB_TEST_H */