diff options
Diffstat (limited to 'include')
| -rw-r--r-- | include/ouroboros/atomics.h | 2 | ||||
| -rw-r--r-- | include/ouroboros/crypt.h | 113 | ||||
| -rw-r--r-- | include/ouroboros/fccntl.h | 2 | ||||
| -rw-r--r-- | include/ouroboros/flow.h | 2 | ||||
| -rw-r--r-- | include/ouroboros/fqueue.h | 3 | ||||
| -rw-r--r-- | include/ouroboros/hash.h | 3 | ||||
| -rw-r--r-- | include/ouroboros/ipcp-dev.h | 126 | ||||
| -rw-r--r-- | include/ouroboros/ipcp.h | 105 | ||||
| -rw-r--r-- | include/ouroboros/irm.h | 25 | ||||
| -rw-r--r-- | include/ouroboros/logs.h | 7 | ||||
| -rw-r--r-- | include/ouroboros/name.h | 6 | ||||
| -rw-r--r-- | include/ouroboros/np1_flow.h | 4 | ||||
| -rw-r--r-- | include/ouroboros/protobuf.h | 30 | ||||
| -rw-r--r-- | include/ouroboros/pthread.h | 6 | ||||
| -rw-r--r-- | include/ouroboros/qos.h | 6 | ||||
| -rw-r--r-- | include/ouroboros/rcu.h | 110 | ||||
| -rw-r--r-- | include/ouroboros/serdes-irm.h | 31 | ||||
| -rw-r--r-- | include/ouroboros/ssm_rbuff.h | 32 | ||||
| -rw-r--r-- | include/test/certs/ecdsa.h | 37 |
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 */ |
