summaryrefslogtreecommitdiff
path: root/include
diff options
context:
space:
mode:
Diffstat (limited to 'include')
-rw-r--r--include/ouroboros/atomics.h2
-rw-r--r--include/ouroboros/crypt.h113
-rw-r--r--include/ouroboros/fccntl.h2
-rw-r--r--include/ouroboros/flow.h2
-rw-r--r--include/ouroboros/fqueue.h3
-rw-r--r--include/ouroboros/hash.h3
-rw-r--r--include/ouroboros/ipcp-dev.h126
-rw-r--r--include/ouroboros/ipcp.h105
-rw-r--r--include/ouroboros/irm.h25
-rw-r--r--include/ouroboros/logs.h7
-rw-r--r--include/ouroboros/name.h6
-rw-r--r--include/ouroboros/np1_flow.h4
-rw-r--r--include/ouroboros/protobuf.h30
-rw-r--r--include/ouroboros/pthread.h6
-rw-r--r--include/ouroboros/qos.h6
-rw-r--r--include/ouroboros/rcu.h110
-rw-r--r--include/ouroboros/serdes-irm.h31
-rw-r--r--include/ouroboros/ssm_rbuff.h32
-rw-r--r--include/test/certs/ecdsa.h37
19 files changed, 513 insertions, 137 deletions
diff --git a/include/ouroboros/atomics.h b/include/ouroboros/atomics.h
index 8e667522..9783fc78 100644
--- a/include/ouroboros/atomics.h
+++ b/include/ouroboros/atomics.h
@@ -35,5 +35,7 @@
#define FETCH_SUB_RELAXED(p, v) (__atomic_fetch_sub(p, v, __ATOMIC_RELAXED))
#define FETCH_ADD(p, v) (__atomic_fetch_add(p, v, __ATOMIC_SEQ_CST))
#define FETCH_SUB(p, v) (__atomic_fetch_sub(p, v, __ATOMIC_SEQ_CST))
+#define FETCH_OR(p, v) (__atomic_fetch_or(p, v, __ATOMIC_SEQ_CST))
+#define FETCH_AND(p, v) (__atomic_fetch_and(p, v, __ATOMIC_SEQ_CST))
#endif /* OUROBOROS_LIB_ATOMICS_H */
diff --git a/include/ouroboros/crypt.h b/include/ouroboros/crypt.h
index 5e082bb9..b567bf8d 100644
--- a/include/ouroboros/crypt.h
+++ b/include/ouroboros/crypt.h
@@ -28,18 +28,19 @@
#include <assert.h>
-#define IVSZ 16
+#define NONCESZ 16
#define SYMMKEYSZ 32
#define MAX_HASH_SIZE 64 /* SHA-512/BLAKE2b max */
#define KEX_ALGO_BUFSZ 32
#define KEX_CIPHER_BUFSZ 32
+#define CACERT_PATH_BUFSZ 256
/*
* 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
+#define NID_undef 0
/* Cipher NIDs (match OpenSSL values) */
#define NID_aes_128_gcm 895
@@ -50,7 +51,7 @@
#define NID_aes_256_ctr 906
#define NID_chacha20_poly1305 1018
- #if !defined (__APPLE__) || !defined ( HAVE_OPENSSL )
+#if !defined (__APPLE__) || !defined ( HAVE_OPENSSL )
/* KEX algorithm NIDs (match OpenSSL values) */
#define NID_X9_62_prime256v1 415
#define NID_secp384r1 715
@@ -64,8 +65,14 @@
#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 */
+
+/* Hybrid KEM NIDs: project-assigned range, OpenSSL defines none */
+#define NID_X25519MLKEM768 2053
+#define NID_X448MLKEM1024 2054
+#define NID_SecP256r1MLKEM768 2055
+#define NID_SecP384r1MLKEM1024 2056
+#define NID_HYBRID_KEM_MIN NID_X25519MLKEM768
+#define NID_HYBRID_KEM_MAX NID_SecP384r1MLKEM1024
/* KDF NIDs (match OpenSSL values) */
#define NID_hkdf 1036
@@ -84,15 +91,8 @@
#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 IS_HYBRID_KEM_NID(nid) kex_nid_is_hybrid(nid)
+#define IS_HYBRID_KEM(algo) kex_nid_is_hybrid(kex_str_to_nid(algo))
#define CRYPT_KEY_BUFSZ 4096 /* Safe buffer for key material */
@@ -101,11 +101,15 @@
#define IS_KEX_ALGO_SET(cfg) ((cfg)->x.nid != NID_undef)
#define IS_KEX_CIPHER_SET(cfg) ((cfg)->c.nid != NID_undef)
+/* Flow role: forks the per-direction keys so each end's TX = peer's RX. */
+#define CRYPT_ROLE_INIT 0 /* flow allocator / OAP client */
+#define CRYPT_ROLE_RESP 1 /* flow acceptor / OAP server */
struct crypt_sk {
int nid;
uint8_t * key;
- uint8_t rot_bit; /* Rotation bit to control epoch */
+ uint8_t epoch; /* installed batch epoch */
+ uint8_t role; /* CRYPT_ROLE_INIT / _RESP */
};
struct sec_config {
@@ -114,18 +118,26 @@ struct sec_config {
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 */
+
+ struct {
+ bool req; /* require peer auth */
+ char cacert[CACERT_PATH_BUFSZ]; /* pinned CA, "" = any */
+ } a; /* authentication */
};
/* Helper macros to set sec_config fields consistently */
@@ -211,9 +223,21 @@ void auth_destroy_ctx(struct auth_ctx * ctx);
int auth_add_crt_to_store(struct auth_ctx * ctx,
void * crt);
+/* Untrusted intermediates: used to build a path, never as trust anchors */
+int auth_add_crt_to_chain(struct auth_ctx * ctx,
+ void * crt);
+
int auth_verify_crt(struct auth_ctx * ctx,
void * crt);
+/* As auth_verify_crt, pin must be in the verified chain (NULL: any) */
+int auth_verify_crt_pin(struct auth_ctx * ctx,
+ void * crt,
+ void * pin);
+
+/* False for PQC keys: their signature digest is intrinsic */
+bool crypt_pk_requires_md(const void * pk);
+
int auth_sign(void * pkp,
int md_nid,
buffer_t msg,
@@ -243,10 +267,11 @@ ssize_t kex_kem_encap(buffer_t pk,
int kdf_nid,
uint8_t * s);
-ssize_t kex_kem_encap_raw(buffer_t pk,
- uint8_t * ct,
- int kdf_nid,
- uint8_t * s);
+ssize_t kex_kem_encap_raw(const char * algo,
+ buffer_t pk,
+ uint8_t * ct,
+ int kdf_nid,
+ uint8_t * s);
int kex_kem_decap(void * pkp,
buffer_t ct,
@@ -256,13 +281,12 @@ int kex_kem_decap(void * pkp,
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);
+bool kex_nid_is_hybrid(uint16_t nid);
+
const char * kex_nid_to_str(uint16_t nid);
uint16_t kex_str_to_nid(const char * algo);
@@ -289,12 +313,16 @@ 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_digest(int md_nid,
+ buffer_t in,
+ uint8_t * out);
ssize_t md_len(int md_nid);
+int crypt_hkdf_expand(buffer_t key,
+ buffer_t info,
+ buffer_t out);
+
int crypt_encrypt(struct crypt_ctx * ctx,
buffer_t in,
buffer_t * out);
@@ -303,10 +331,37 @@ int crypt_decrypt(struct crypt_ctx * ctx,
buffer_t in,
buffer_t * out);
-int crypt_get_ivsz(struct crypt_ctx * ctx);
+/* One-shot AEAD over an explicit key/nonce. out = ciphertext ‖ tag. */
+int crypt_oneshot_seal(int nid,
+ const uint8_t * key,
+ const uint8_t * nonce,
+ buffer_t aad,
+ buffer_t in,
+ buffer_t * out);
+
+int crypt_oneshot_open(int nid,
+ const uint8_t * key,
+ const uint8_t * nonce,
+ buffer_t aad,
+ buffer_t in,
+ buffer_t * out);
+
+int crypt_get_headsz(struct crypt_ctx * ctx);
int crypt_get_tagsz(struct crypt_ctx * ctx);
+int crypt_rekey(struct crypt_ctx * ctx,
+ struct crypt_sk * sk);
+
+/* Nodes remaining in the TX batch (re-key watermark). */
+int crypt_nodes_left(struct crypt_ctx * ctx);
+
+/* 1 once the peer has been observed on the current generation. */
+int crypt_peer_synced(struct crypt_ctx * ctx);
+
+/* Switch TX to the installed (new) batch (after peer synced/grace). */
+void crypt_tx_promote(struct crypt_ctx * ctx);
+
int crypt_load_crt_file(const char * path,
void ** crt);
@@ -337,11 +392,17 @@ int crypt_load_pubkey_file_to_der(const char * path,
buffer_t * buf);
int crypt_load_pubkey_raw_file(const char * path,
+ const char * algo,
buffer_t * buf);
int crypt_load_privkey_raw_file(const char * path,
+ const char * algo,
void ** key);
+int crypt_ct_cmp(const void * a,
+ const void * b,
+ size_t len);
+
int crypt_cmp_key(const void * key1,
const void * key2);
diff --git a/include/ouroboros/fccntl.h b/include/ouroboros/fccntl.h
index e91e91dd..9a048095 100644
--- a/include/ouroboros/fccntl.h
+++ b/include/ouroboros/fccntl.h
@@ -67,6 +67,8 @@
#define FLOWGRXQLEN 00000010 /* Get queue length on rx */
#define FLOWGTXQLEN 00000011 /* Get queue length on tx */
#define FLOWGMTU 00000012 /* Get per-packet MTU */
+#define FLOWSTXQDLY 00000013 /* Set tx queueing delay */
+#define FLOWGTXQDLY 00000014 /* Get tx queueing delay */
/* FRCT operations */
#define FRCTSFLAGS 00001000 /* Set flags for FRCT */
diff --git a/include/ouroboros/flow.h b/include/ouroboros/flow.h
index 8b096410..3d3c74ec 100644
--- a/include/ouroboros/flow.h
+++ b/include/ouroboros/flow.h
@@ -53,6 +53,8 @@ struct flow_info {
uint32_t mtu; /* n-1 layer MTU in bytes, 0 = unknown */
+ uint32_t max_rtt; /* declared max path RTT (ms), 0 = unknown */
+
struct qos_spec qs;
enum flow_state state;
diff --git a/include/ouroboros/fqueue.h b/include/ouroboros/fqueue.h
index 2546c79d..322da3ea 100644
--- a/include/ouroboros/fqueue.h
+++ b/include/ouroboros/fqueue.h
@@ -34,7 +34,8 @@ enum fqtype {
FLOW_UP = (1 << 2),
FLOW_ALLOC = (1 << 3),
FLOW_DEALLOC = (1 << 4),
- FLOW_PEER = (1 << 5)
+ FLOW_PEER = (1 << 5),
+ FLOW_UPD = (1 << 6)
};
struct flow_set;
diff --git a/include/ouroboros/hash.h b/include/ouroboros/hash.h
index 17ab98ac..c6609ffc 100644
--- a/include/ouroboros/hash.h
+++ b/include/ouroboros/hash.h
@@ -89,4 +89,7 @@ void str_hash(enum hash_algo algo,
void * dst,
const char * str);
+/* Non-cryptographic finalizer for hashing an integer key to a table index. */
+uint64_t hash_mix64(uint64_t key);
+
#endif /* OUROBOROS_LIB_HASH_H */
diff --git a/include/ouroboros/ipcp-dev.h b/include/ouroboros/ipcp-dev.h
index d23f757e..c0899c2b 100644
--- a/include/ouroboros/ipcp-dev.h
+++ b/include/ouroboros/ipcp-dev.h
@@ -24,52 +24,120 @@
#define OUROBOROS_LIB_IPCP_DEV_H
#include <ouroboros/ipcp.h>
+#include <ouroboros/qos.h>
#include <ouroboros/qoscube.h>
#include <ouroboros/ssm_pool.h>
#include <ouroboros/utils.h>
+#include <stdbool.h>
#include <stdint.h>
+#include <time.h>
-int ipcp_create_r(const struct ipcp_info * info);
+int ipcp_create_r(const struct ipcp_info * info);
-int ipcp_flow_req_arr(const buffer_t * dst,
- qosspec_t qs,
- time_t mpl,
- uint32_t mtu,
- const buffer_t * data);
+void ipcp_flow_set_max_rtt(uint32_t max_rtt);
-int ipcp_flow_alloc_reply(int fd,
- int response,
- time_t mpl,
- uint32_t mtu,
- const buffer_t * data);
+int ipcp_flow_req_arr(const buffer_t * dst,
+ qosspec_t qs,
+ time_t mpl,
+ uint32_t mtu,
+ const buffer_t * data);
+
+int ipcp_flow_update_arr(int flow_id,
+ const buffer_t * data);
+
+int ipcp_flow_alloc_reply(int fd,
+ int response,
+ time_t mpl,
+ uint32_t mtu,
+ const buffer_t * data);
+
+int ipcp_flow_read(int fd,
+ struct ssm_pk_buff ** spb);
+
+int ipcp_flow_write(int fd,
+ struct ssm_pk_buff * spb);
-int ipcp_flow_read(int fd,
- struct ssm_pk_buff ** spb);
+int np1_flow_read(int fd,
+ struct ssm_pk_buff ** spb,
+ struct ssm_pool * pool);
-int ipcp_flow_write(int fd,
- struct ssm_pk_buff * spb);
+int np1_flow_write(int fd,
+ struct ssm_pk_buff * spb,
+ struct ssm_pool * pool);
-int np1_flow_read(int fd,
- struct ssm_pk_buff ** spb,
- struct ssm_pool * pool);
+int ipcp_flow_dealloc(int fd);
+
+int ipcp_flow_fini(int fd);
+
+int ipcp_flow_get_qoscube(int fd,
+ qoscube_t * cube);
+
+/* Bytes queued in the transmit path of the flow. */
+size_t ipcp_flow_queued(int fd);
+
+/* Mean size of a packet written to the flow, bytes. */
+size_t ipcp_flow_mean_len(int fd);
+
+/*
+ * Capacity estimator of the transmit queue behind the flow.
+ * Flows attached to one point of attachment share a queue and
+ * share the estimator. Armed by the IPCP; unarmed flows read 0.
+ */
+int ipcp_flow_cap_arm(int fd);
-int np1_flow_write(int fd,
- struct ssm_pk_buff * spb,
- struct ssm_pool * pool);
+void ipcp_flow_cap_update(int fd,
+ size_t qlen,
+ size_t len);
-int ipcp_flow_dealloc(int fd);
+/* Estimated capacity of the transmit queue (bytes/s), 0 = unknown. */
+uint64_t ipcp_flow_cap(int fd);
-int ipcp_flow_fini(int fd);
+int ipcp_spb_reserve(struct ssm_pk_buff ** spb,
+ size_t len);
-int ipcp_flow_get_qoscube(int fd,
- qoscube_t * cube);
+void ipcp_spb_release(struct ssm_pk_buff * spb);
-size_t ipcp_flow_queued(int fd);
+/* PoA lifecycle; an IPCP owns its PoAs, applications have none. */
+int poa_init(const char * name);
-int ipcp_spb_reserve(struct ssm_pk_buff ** spb,
- size_t len);
+int poa_start(void);
+
+void poa_stop(void);
+
+void poa_fini(void);
+
+/* Also answer name queries for the layer; set at bootstrap/enroll. */
+int poa_set_layer(const char * layer);
+
+/* Attach or release a PoA; the type in the spec picks the transport. */
+int poa_attach(const struct poa_spec * poa);
+
+int poa_detach(const struct poa_spec * poa);
+
+/* The PoAs this process has attached. */
+ssize_t poa_list(struct poa_spec * specs,
+ size_t max);
+
+/* Allocate a flow to dst over the PoA that carries addr. */
+int poa_flow_alloc(const char * dst,
+ const struct poa_addr * addr,
+ qosspec_t * qs,
+ const struct timespec * timeo);
+
+/* Resolve dst on the attached PoAs; no flow is created. */
+int poa_query(const char * dst,
+ const struct timespec * timeo,
+ struct poa_addr * addr);
+
+/* PoA counterparts of the ipcp_flow_* operations */
+int poa_flow_alloc_resp(int flow_id,
+ int response,
+ const buffer_t * data);
+
+int poa_flow_update(int flow_id,
+ const buffer_t * data);
-void ipcp_spb_release(struct ssm_pk_buff * spb);
+int poa_flow_dealloc(int flow_id);
#endif /* OUROBOROS_LIB_IPCP_DEV_H */
diff --git a/include/ouroboros/ipcp.h b/include/ouroboros/ipcp.h
index e29b080a..4da3b81d 100644
--- a/include/ouroboros/ipcp.h
+++ b/include/ouroboros/ipcp.h
@@ -48,13 +48,19 @@ 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_UDP4,
- IPCP_UDP6,
IPCP_INVALID
};
+/* The medium a PoA sits on. */
+enum poa_type {
+ POA_UDP4 = 0,
+ POA_UDP6,
+ /* A name to resolve; the family is DNS's pick. Port in udp4. */
+ POA_UDP,
+ POA_ETH,
+ POA_INVALID
+};
+
struct ipcp_info {
enum ipcp_type type;
pid_t pid;
@@ -124,9 +130,10 @@ enum pol_cong_avoid {
struct dt_config {
struct {
- uint8_t addr_size;
- uint8_t eid_size;
- uint8_t max_ttl;
+ uint8_t addr_size;
+ uint8_t eid_size;
+ uint8_t max_ttl;
+ uint16_t max_rtt; /* Declared max layer RTT (ms) */
};
struct routing_config routing; /* Routing policy */
};
@@ -135,6 +142,7 @@ static const struct dt_config default_dt_config = {
.addr_size = 4,
.eid_size = 8,
.max_ttl = 60,
+ .max_rtt = 200,
.routing = {
.pol = ROUTING_LINK_STATE,
.ls = {
@@ -254,23 +262,52 @@ static const struct uni_config default_uni_config = {
.cong_avoid = CA_MB_ECN
};
-struct eth_config {
+#define POA_UDP_PORT 3435 /* default UDP PoA port */
+#define POA_ETHERTYPE 0xA000 /* default Ethertype */
+#define POA_MAC_SIZE 6
+#define POA_HOST_STRLEN 255
+
+struct eth_poa {
char dev[DEV_NAME_SIZE + 1];
- uint16_t ethertype; /* DIX only*/
+ uint16_t ethertype;
+ uint8_t mac[POA_MAC_SIZE];
};
-struct udp4_config {
+struct udp4_poa {
struct in_addr ip_addr;
- struct in_addr dns_addr;
uint16_t port;
};
-struct udp6_config {
+struct udp6_poa {
struct in6_addr ip_addr;
- struct in6_addr dns_addr;
uint16_t port;
};
+/* PoA a unicast or broadcast IPCP attaches to. */
+struct poa_spec {
+ enum poa_type type;
+ union {
+ struct udp4_poa udp4;
+ struct udp6_poa udp6;
+ struct eth_poa eth;
+ };
+};
+
+
+/* Peer PoA to connect to. */
+struct poa_addr {
+ enum poa_type type;
+ char hostname[POA_HOST_STRLEN + 1]; /* resolved by IRMd */
+ union {
+ struct udp4_poa udp4;
+ struct udp6_poa udp6;
+ struct {
+ struct eth_poa src;
+ struct eth_poa dst;
+ } eth;
+ };
+};
+
/* Layers */
struct layer_info {
char name[LAYER_NAME_SIZE + 1];
@@ -283,58 +320,22 @@ struct ipcp_config {
struct layer_info layer_info;
enum ipcp_type type;
- union {
- struct uni_config unicast;
- struct udp4_config udp4;
- struct udp6_config udp6;
- struct eth_config eth;
- };
+ struct uni_config unicast;
};
/* default configurations */
static const struct ipcp_config local_default_conf = {
- .type = IPCP_LOCAL,
- .layer_info = {
- .dir_hash_algo = DIR_HASH_SHA3_256
- }
-};
-
-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
- }
-};
-
-static const struct ipcp_config udp4_default_conf = {
- .type = IPCP_UDP4,
- .udp4 = {
- .port = 3435
- }
-};
-
-static const struct ipcp_config udp6_default_conf = {
- .type = IPCP_UDP6,
- .udp6 = {
- .port = 3435
- }
+ .type = IPCP_LOCAL
};
static const struct ipcp_config uni_default_conf = {
- .type = IPCP_UNICAST,
.layer_info = {
.dir_hash_algo = DIR_HASH_SHA3_256
},
+ .type = IPCP_UNICAST,
.unicast = {
.dt = {
.addr_size = 4,
diff --git a/include/ouroboros/irm.h b/include/ouroboros/irm.h
index 7cb71c21..3e8a24af 100644
--- a/include/ouroboros/irm.h
+++ b/include/ouroboros/irm.h
@@ -46,16 +46,29 @@ int irm_destroy_ipcp(pid_t pid);
ssize_t irm_list_ipcps(struct ipcp_list_info ** ipcps);
-int irm_enroll_ipcp(pid_t pid,
- const char * dst);
+int irm_enroll_ipcp(pid_t pid,
+ const char * dst,
+ const struct poa_addr * addr);
int irm_bootstrap_ipcp(pid_t pid,
const struct ipcp_config * conf);
-int irm_connect_ipcp(pid_t pid,
- const char * dst,
- const char * component,
- qosspec_t qs);
+/* Attach an IPCP to a medium; repeat for more than one PoA. */
+int irm_attach_ipcp(pid_t pid,
+ const struct poa_spec * poa);
+
+int irm_detach_ipcp(pid_t pid,
+ const struct poa_spec * poa);
+
+/* Caller frees *poas. */
+ssize_t irm_list_poas(pid_t pid,
+ struct poa_spec ** poas);
+
+int irm_connect_ipcp(pid_t pid,
+ const char * dst,
+ const char * component,
+ qosspec_t qs,
+ const struct poa_addr * addr);
int irm_disconnect_ipcp(pid_t pid,
const char * dst,
diff --git a/include/ouroboros/logs.h b/include/ouroboros/logs.h
index 58494531..1ae77673 100644
--- a/include/ouroboros/logs.h
+++ b/include/ouroboros/logs.h
@@ -29,6 +29,7 @@
#include <ouroboros/hash.h>
+#include <pthread.h>
#include <unistd.h>
#include <stdio.h>
#include <stdbool.h>
@@ -55,6 +56,8 @@ void log_fini(void);
#define __olog(CLR, LVL, SYSLVL, ...) \
do { \
+ int __cs; \
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &__cs); \
if (log_syslog) { \
syslog(SYSLVL, __VA_ARGS__); \
} else { \
@@ -64,10 +67,13 @@ void log_fini(void);
printf(CLR_RESET "\n"); \
fflush(stdout); \
} \
+ pthread_setcancelstate(__cs, NULL); \
} while (0)
#define __olog_id(CLR, LVL, SYSLVL, id, fmt, ...) \
do { \
+ int __cs; \
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &__cs); \
if (log_syslog) { \
syslog(SYSLVL, "[" HASH_FMT64 "] " fmt, \
HASH_VAL64(id), ## __VA_ARGS__); \
@@ -79,6 +85,7 @@ void log_fini(void);
printf(CLR_RESET "\n"); \
fflush(stdout); \
} \
+ pthread_setcancelstate(__cs, NULL); \
} while (0)
#ifndef OUROBOROS_DISABLE_LOGGING
diff --git a/include/ouroboros/name.h b/include/ouroboros/name.h
index a9393820..a3aac8c4 100644
--- a/include/ouroboros/name.h
+++ b/include/ouroboros/name.h
@@ -34,9 +34,9 @@ enum pol_balance {
};
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 */
+ char sec[NAME_PATH_SIZE + 1]; /* path to sec.conf 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 {
diff --git a/include/ouroboros/np1_flow.h b/include/ouroboros/np1_flow.h
index 309d01c2..758b6db8 100644
--- a/include/ouroboros/np1_flow.h
+++ b/include/ouroboros/np1_flow.h
@@ -36,6 +36,10 @@ int np1_flow_resp(int flow_id,
int np1_flow_dealloc(int flow_id,
time_t timeo);
+int np1_flow_fd(int flow_id);
+
+int np1_flow_id(int fd);
+
static const qosspec_t qos_np1 = {
.service = SVC_RAW,
.delay = UINT32_MAX,
diff --git a/include/ouroboros/protobuf.h b/include/ouroboros/protobuf.h
index 951e8fd6..7937f86b 100644
--- a/include/ouroboros/protobuf.h
+++ b/include/ouroboros/protobuf.h
@@ -24,9 +24,9 @@
#define OUROBOROS_LIB_PROTOBUF_H
#include <ouroboros/flow.h>
-#include <ouroboros/qos.h>
#include <ouroboros/ipcp.h>
#include <ouroboros/irm.h>
+#include <ouroboros/qos.h>
#include <ouroboros/serdes-irm.h>
#include <ouroboros/serdes-oep.h>
@@ -37,9 +37,10 @@ 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 EthPoaMsg eth_poa_msg_t;
+typedef Udp4PoaMsg udp4_poa_msg_t;
+typedef PoaSpecMsg poa_spec_msg_t;
+typedef Udp6PoaMsg udp6_poa_msg_t;
typedef UniConfigMsg uni_config_msg_t;
#include "ipcp.pb-c.h"
@@ -56,6 +57,7 @@ typedef FlowInfoMsg flow_info_msg_t;
typedef NameInfoMsg name_info_msg_t;
typedef LayerInfoMsg layer_info_msg_t;
typedef QosspecMsg qosspec_msg_t;
+typedef PoaAddrMsg poa_addr_msg_t;
#include "enroll.pb-c.h"
typedef EnrollReqMsg enroll_req_msg_t;
@@ -79,6 +81,10 @@ 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);
+poa_addr_msg_t * poa_addr_s_to_msg(const struct poa_addr * s);
+
+struct poa_addr poa_addr_msg_to_s(const poa_addr_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);
@@ -91,20 +97,24 @@ 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);
+eth_poa_msg_t * eth_poa_s_to_msg(const struct eth_poa * s);
-struct eth_config eth_config_msg_to_s(const eth_config_msg_t * msg);
+struct eth_poa eth_poa_msg_to_s(const eth_poa_msg_t * msg);
-udp4_config_msg_t * udp4_config_s_to_msg(const struct udp4_config * s);
+udp4_poa_msg_t * udp4_poa_s_to_msg(const struct udp4_poa * s);
-struct udp4_config udp4_config_msg_to_s(const udp4_config_msg_t * msg);
+struct udp4_poa udp4_poa_msg_to_s(const udp4_poa_msg_t * msg);
-udp6_config_msg_t * udp6_config_s_to_msg(const struct udp6_config * s);
+udp6_poa_msg_t * udp6_poa_s_to_msg(const struct udp6_poa * s);
-struct udp6_config udp6_config_msg_to_s(const udp6_config_msg_t * msg);
+struct udp6_poa udp6_poa_msg_to_s(const udp6_poa_msg_t * msg);
ipcp_config_msg_t * ipcp_config_s_to_msg(const struct ipcp_config * s);
+poa_spec_msg_t * poa_spec_s_to_msg(const struct poa_spec * s);
+
+struct poa_spec poa_spec_msg_to_s(const poa_spec_msg_t * msg);
+
struct ipcp_config ipcp_config_msg_to_s(const ipcp_config_msg_t * msg);
/* QoS */
diff --git a/include/ouroboros/pthread.h b/include/ouroboros/pthread.h
index cd500795..3ca79d10 100644
--- a/include/ouroboros/pthread.h
+++ b/include/ouroboros/pthread.h
@@ -24,6 +24,7 @@
#define OUROBOROS_LIB_PTHREAD_H
#include <pthread.h>
+#include <stdio.h>
static int __attribute__((unused)) __timedwait(pthread_cond_t * cond,
pthread_mutex_t * mtx,
@@ -48,4 +49,9 @@ static void __attribute__((unused)) __cleanup_mutex_unlock(void * mutex)
pthread_mutex_unlock((pthread_mutex_t *) mutex);
}
+static void __attribute__((unused)) __cleanup_fclose(void * fp)
+{
+ fclose((FILE *) fp);
+}
+
#endif /* OUROBOROS_LIB_PTHREAD_H */
diff --git a/include/ouroboros/qos.h b/include/ouroboros/qos.h
index 7980ad00..26fc2637 100644
--- a/include/ouroboros/qos.h
+++ b/include/ouroboros/qos.h
@@ -30,9 +30,9 @@
/* qos_spec.service: framing / reliability class. */
enum qos_service {
- SVC_RAW = 0, /* No FRCT; best-effort raw messages */
- SVC_MESSAGE = 1, /* FRCT, reliable ordered messages */
- SVC_STREAM = 2, /* FRCT, reliable ordered byte stream */
+ SVC_RAW = 0, /* No FRCT; best-effort raw messages */
+ SVC_MESSAGE = 1, /* FRCT, ordered messages, optional reliability */
+ SVC_STREAM = 2, /* FRCT, reliable ordered byte stream */
};
typedef struct qos_spec {
diff --git a/include/ouroboros/rcu.h b/include/ouroboros/rcu.h
new file mode 100644
index 00000000..b4e7d27c
--- /dev/null
+++ b/include/ouroboros/rcu.h
@@ -0,0 +1,110 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2026
+ *
+ * Read-mostly pointer publication (RCU, with a locked fallback)
+ *
+ * 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_RCU_H
+#define OUROBOROS_LIB_RCU_H
+
+/*
+ * Lock-free reads of published pointers via liburcu (urcu-bp) when
+ * available; a per-object rwlock fallback otherwise.
+ * Include config.h before this header so HAVE_LIBURCU is defined.
+ *
+ * Embed a struct rcu_guard in the object. A reader brackets its access
+ * with rcu_rdlock/rcu_rdunlock and reads published pointers via rcu_deref.
+ * A writer serialises with rcu_wrlock/rcu_wrunlock and publishes via
+ * rcu_assign; after unlock it reclaims a now-unreachable object with
+ * rcu_reclaim (waits out live readers) before freeing it. rcu_drain waits
+ * out all readers at teardown.
+ */
+
+#include <ouroboros/pthread.h>
+
+#ifdef HAVE_LIBURCU
+
+#include <urcu-bp.h>
+
+struct rcu_guard {
+ pthread_mutex_t w; /* serialises writers; readers use RCU */
+};
+
+#define rcu_guard_init(g) pthread_mutex_init(&(g)->w, NULL)
+#define rcu_guard_fini(g) pthread_mutex_destroy(&(g)->w)
+#define rcu_rdlock(g) ((void) (g), rcu_read_lock())
+#define rcu_rdunlock(g) ((void) (g), rcu_read_unlock())
+#define rcu_wrlock(g) pthread_mutex_lock(&(g)->w)
+#define rcu_wrunlock(g) pthread_mutex_unlock(&(g)->w)
+#define rcu_deref(p) rcu_dereference(p)
+#define rcu_assign(p, v) rcu_assign_pointer(p, v)
+#define rcu_reclaim(g) ((void) (g), synchronize_rcu())
+#define rcu_drain(g) ((void) (g), synchronize_rcu())
+
+/* TSan can miss the publish/consume barrier under urcu. */
+#if defined(__SANITIZE_THREAD__)
+#define RCU_TSAN_ANNOTATE
+#endif
+#if defined(__has_feature)
+#if __has_feature(thread_sanitizer)
+#define RCU_TSAN_ANNOTATE
+#endif
+#endif
+
+/*
+ * Publish/consume annotations re-expose liburcu's rcu_assign/rcu_deref edge to
+ * TSan, which cannot see liburcu's barriers. Call rcu_publish(p) before
+ * publishing p with rcu_assign, and rcu_consume(p) after reading it with
+ * rcu_deref. No-op without liburcu (the rwlock fallback already gives TSan the
+ * edge) or without TSan.
+ */
+#ifdef RCU_TSAN_ANNOTATE
+#include <sanitizer/tsan_interface.h>
+#define rcu_publish(p) __tsan_release(p)
+#define rcu_consume(p) __tsan_acquire(p)
+#else
+#define rcu_publish(p) ((void) (p))
+#define rcu_consume(p) ((void) (p))
+#endif
+
+#else /* !HAVE_LIBURCU : per-object rwlock fallback */
+
+struct rcu_guard {
+ pthread_rwlock_t rw; /* readers rd, writers wr */
+};
+
+#define rcu_guard_init(g) pthread_rwlock_init(&(g)->rw, NULL)
+#define rcu_guard_fini(g) pthread_rwlock_destroy(&(g)->rw)
+#define rcu_rdlock(g) pthread_rwlock_rdlock(&(g)->rw)
+#define rcu_rdunlock(g) pthread_rwlock_unlock(&(g)->rw)
+#define rcu_wrlock(g) pthread_rwlock_wrlock(&(g)->rw)
+#define rcu_wrunlock(g) pthread_rwlock_unlock(&(g)->rw)
+#define rcu_deref(p) (p)
+#define rcu_assign(p, v) ((p) = (v))
+#define rcu_reclaim(g) ((void) (g)) /* wrlock already excluded readers */
+#define rcu_drain(g) (pthread_rwlock_wrlock(&(g)->rw), \
+ pthread_rwlock_unlock(&(g)->rw))
+
+/* rwlock already gives TSan the publish/consume edge; no annotation. */
+#define rcu_publish(p) ((void) (p))
+#define rcu_consume(p) ((void) (p))
+
+#endif /* HAVE_LIBURCU */
+
+#endif /* OUROBOROS_LIB_RCU_H */
diff --git a/include/ouroboros/serdes-irm.h b/include/ouroboros/serdes-irm.h
index 1dfff4d9..bc3c46d2 100644
--- a/include/ouroboros/serdes-irm.h
+++ b/include/ouroboros/serdes-irm.h
@@ -31,6 +31,7 @@
#include <ouroboros/utils.h>
#include <inttypes.h>
+#include <stdbool.h>
int flow_alloc__irm_req_ser(buffer_t * buf,
const struct flow_info * flow,
@@ -51,6 +52,27 @@ int ipcp_flow_req_arr__irm_req_ser(buffer_t * buf,
const struct flow_info * flow,
const buffer_t * data);
+int poa_flow_alloc__irm_req_ser(buffer_t * buf,
+ const struct flow_info * flow,
+ const char * dst);
+
+int poa_flow_alloc_r__irm_req_ser(buffer_t * buf,
+ const struct flow_info * flow,
+ const buffer_t * data,
+ int response);
+
+int ipcp_poa_flow_req_arr__irm_req_ser(buffer_t * buf,
+ const struct flow_info * flow,
+ const buffer_t * data);
+
+int poa_flow__irm_result_des(buffer_t * buf,
+ struct flow_info * flow,
+ buffer_t * data);
+
+int ipcp_flow_update_arr__irm_req_ser(buffer_t * buf,
+ 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,
@@ -64,6 +86,15 @@ int flow_dealloc__irm_req_ser(buffer_t * buf,
const struct flow_info * flow,
const struct timespec * timeo);
+int flow_update__irm_req_ser(buffer_t * buf,
+ const struct flow_info * flow,
+ bool rekey);
+
+int flow_rekey__irm_result_des(buffer_t * buf,
+ struct crypt_sk * sk,
+ bool * has_key,
+ bool * initiator);
+
int ipcp_flow_dealloc__irm_req_ser(buffer_t * buf,
const struct flow_info * info);
diff --git a/include/ouroboros/ssm_rbuff.h b/include/ouroboros/ssm_rbuff.h
index 2443b63d..297aad9f 100644
--- a/include/ouroboros/ssm_rbuff.h
+++ b/include/ouroboros/ssm_rbuff.h
@@ -28,10 +28,14 @@
#include <stdint.h>
-#define ACL_RDWR 0000
-#define ACL_RDONLY 0001
-#define ACL_FLOWDOWN 0002
-#define ACL_FLOWPEER 0004
+#define RB_RD 0001 /* read permitted (0 = no access) */
+#define RB_WR 0002 /* write permitted (0 = no access) */
+#define RB_RDWR (RB_RD | RB_WR)
+#define RB_FLOWDOWN 0004
+#define RB_FLOWPEER 0010
+#define RB_REKEY 0020 /* re-key seed parked (out-of-band signal) */
+
+#define SSM_RBUFF_TXQ_MAX_DELAY 1000000000LL
struct ssm_rbuff;
@@ -45,10 +49,21 @@ struct ssm_rbuff * ssm_rbuff_open(pid_t pid,
void ssm_rbuff_close(struct ssm_rbuff * rb);
-void ssm_rbuff_set_acl(struct ssm_rbuff * rb,
- uint32_t flags);
+void ssm_rbuff_set_flags(struct ssm_rbuff * rb,
+ uint32_t flags);
+
+void ssm_rbuff_clr_flags(struct ssm_rbuff * rb,
+ uint32_t flags);
+
+uint32_t ssm_rbuff_get_flags(struct ssm_rbuff * rb);
-uint32_t ssm_rbuff_get_acl(struct ssm_rbuff * rb);
+size_t ssm_rbuff_get_limit(struct ssm_rbuff * rb);
+
+void ssm_rbuff_set_txq_target(struct ssm_rbuff * rb,
+ const struct timespec * ts);
+
+void ssm_rbuff_get_txq_target(struct ssm_rbuff * rb,
+ struct timespec * ts);
void ssm_rbuff_fini(struct ssm_rbuff * rb);
@@ -57,6 +72,9 @@ int ssm_rbuff_mlock(struct ssm_rbuff * rb);
int ssm_rbuff_write(struct ssm_rbuff * rb,
size_t off);
+int ssm_rbuff_write_prio(struct ssm_rbuff * rb,
+ size_t off);
+
int ssm_rbuff_write_b(struct ssm_rbuff * rb,
size_t off,
const struct timespec * abstime);
diff --git a/include/test/certs/ecdsa.h b/include/test/certs/ecdsa.h
index 1d61a3f8..cbc4ed06 100644
--- a/include/test/certs/ecdsa.h
+++ b/include/test/certs/ecdsa.h
@@ -107,6 +107,23 @@ static const char * signed_server_crt_ec = \
"ktkxoHAFbjQEPQIhAMInHI7lvRmS0IMw1wBF/WlUZWKvhyU/TeMIZfk/JGCS\n"
"-----END CERTIFICATE-----\n";
+/* Valid CA outside the test chain, for cacert= pin mismatch */
+static __attribute__((unused)) const char * other_ca_crt_ec = \
+"-----BEGIN CERTIFICATE-----\n"
+"MIICNjCCAdugAwIBAgIUTZcZ9hKXyCT/VgTw8TD1TB2mzrgwCgYIKoZIzj0EAwIw\n"
+"cDELMAkGA1UEBhMCQkUxDDAKBgNVBAgMA09WTDEOMAwGA1UEBwwFR2hlbnQxDDAK\n"
+"BgNVBAoMA283czEVMBMGA1UECwwMdW5pdHRlc3QubzdzMR4wHAYDVQQDDBVvdGhl\n"
+"ci1jYS51bml0dGVzdC5vN3MwHhcNMjYwNjEyMTU1MjAzWhcNNDYwNjA3MTU1MjAz\n"
+"WjBwMQswCQYDVQQGEwJCRTEMMAoGA1UECAwDT1ZMMQ4wDAYDVQQHDAVHaGVudDEM\n"
+"MAoGA1UECgwDbzdzMRUwEwYDVQQLDAx1bml0dGVzdC5vN3MxHjAcBgNVBAMMFW90\n"
+"aGVyLWNhLnVuaXR0ZXN0Lm83czBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABNtu\n"
+"FghMww2kQ6a+Coe6VPzfBRUZlm7y6/RfbRFPvErowOqKLQP+wCs8Rq46VmHCYTbB\n"
+"OlRwzJKcNoSeJ4MNWUqjUzBRMB0GA1UdDgQWBBTmEP8W6fgViKIjw8CpTuQwyuOi\n"
+"kTAfBgNVHSMEGDAWgBTmEP8W6fgViKIjw8CpTuQwyuOikTAPBgNVHRMBAf8EBTAD\n"
+"AQH/MAoGCCqGSM49BAMCA0kAMEYCIQDQOCfFcOJm49R975RBPfVMy0pXGx/YeQcy\n"
+"6WKAeLuTowIhAISdVZ6KxsgkwuswMtDWAkCBujep0XSBGXtXmi4959DH\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"
@@ -121,5 +138,25 @@ static __attribute__((unused)) const char * server_crt_ec = \
"gRo=\n"
"-----END CERTIFICATE-----\n";
+/*
+ * Name-confusion fixture: real CN is "attacker.unittest.o7s", but the
+ * O field value is "CN=victim.unittest.o7s" so the oneline subject is
+ * "/O=CN=victim.unittest.o7s/CN=attacker.unittest.o7s". A strstr("CN=")
+ * scan latches onto the decoy. The real CN must win.
+ */
+static __attribute__((unused)) const char * confused_crt_ec = \
+"-----BEGIN CERTIFICATE-----\n"
+"MIIB1jCCAX2gAwIBAgIUCfXJzDQ3Sx5qcyVB9Rb4/FdZ+QowCgYIKoZIzj0EAwIw\n"
+"QTEfMB0GA1UECgwWQ049dmljdGltLnVuaXR0ZXN0Lm83czEeMBwGA1UEAwwVYXR0\n"
+"YWNrZXIudW5pdHRlc3QubzdzMB4XDTI2MDYxNDE5MDcwMVoXDTQ2MDYwOTE5MDcw\n"
+"MVowQTEfMB0GA1UECgwWQ049dmljdGltLnVuaXR0ZXN0Lm83czEeMBwGA1UEAwwV\n"
+"YXR0YWNrZXIudW5pdHRlc3QubzdzMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE\n"
+"oLwrbLs3diGcjyY2ErvO/U6CoyyKfl/8e1nxBKXHSOkO5xVmFu+EobEQVFvabxE/\n"
+"x4RttKcGJqUe8vlyQexQq6NTMFEwHQYDVR0OBBYEFGBaOBzTsCakjBN61x0ZnHSk\n"
+"04T3MB8GA1UdIwQYMBaAFGBaOBzTsCakjBN61x0ZnHSk04T3MA8GA1UdEwEB/wQF\n"
+"MAMBAf8wCgYIKoZIzj0EAwIDRwAwRAIgFtBeVxlRuI7y9Bo/Dh97ajTbHJXYMkc6\n"
+"ZqflSN3Q/uACIHWoCVn6u6+JjF+Kj9zubFJ49RIQJthSeP8xj7yTeV17\n"
+"-----END CERTIFICATE-----\n";
+
#endif /* TEST_CERTS_H */