diff options
Diffstat (limited to 'include')
28 files changed, 618 insertions, 71 deletions
diff --git a/include/ouroboros/atomics.h b/include/ouroboros/atomics.h new file mode 100644 index 00000000..8e667522 --- /dev/null +++ b/include/ouroboros/atomics.h @@ -0,0 +1,39 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2026 + * + * Atomic helpers + * + * 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_ATOMICS_H +#define OUROBOROS_LIB_ATOMICS_H + +#define LOAD_RELAXED(p) (__atomic_load_n(p, __ATOMIC_RELAXED)) +#define LOAD_ACQUIRE(p) (__atomic_load_n(p, __ATOMIC_ACQUIRE)) +#define LOAD(p) (__atomic_load_n(p, __ATOMIC_SEQ_CST)) + +#define STORE_RELAXED(p, v) (__atomic_store_n(p, v, __ATOMIC_RELAXED)) +#define STORE_RELEASE(p, v) (__atomic_store_n(p, v, __ATOMIC_RELEASE)) +#define STORE(p, v) (__atomic_store_n(p, v, __ATOMIC_SEQ_CST)) + +#define FETCH_ADD_RELAXED(p, v) (__atomic_fetch_add(p, v, __ATOMIC_RELAXED)) +#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)) + +#endif /* OUROBOROS_LIB_ATOMICS_H */ diff --git a/include/ouroboros/crc16.h b/include/ouroboros/crc16.h new file mode 100644 index 00000000..df4d4f57 --- /dev/null +++ b/include/ouroboros/crc16.h @@ -0,0 +1,43 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2026 + * + * 16-bit Cyclic Redundancy Check (CCITT-FALSE variant) + * + * 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/. + */ + +/* + * Polynomial: ITU-T V.41 / CCITT-FALSE, CRC-16/IBM-3740. + * reveng catalog: https://reveng.sourceforge.io/crc-catalogue + * + * Intended for medium-size header check sequences (typ. <= 4 KiB). + * Hamming distance HD=4 up to 32751 message bits. + */ + +#ifndef OUROBOROS_LIB_CRC16_H +#define OUROBOROS_LIB_CRC16_H + +#include <stddef.h> +#include <stdint.h> + +#define CRC16_HASH_LEN 2 + +void crc16_ccitt_false(uint16_t * crc, + const void * buf, + size_t len); + +#endif /* OUROBOROS_LIB_CRC16_H */ diff --git a/include/ouroboros/crc64.h b/include/ouroboros/crc64.h new file mode 100644 index 00000000..f6e407a0 --- /dev/null +++ b/include/ouroboros/crc64.h @@ -0,0 +1,44 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2026 + * + * 64-bit Cyclic Redundancy Check (NVMe variant) + * + * 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/. + */ + +/* + * Polynomial: NVM Express Base Spec, CRC-64/NVMe. + * reveng catalog: https://reveng.sourceforge.io/crc-catalogue + * + * Fold-by-N (PCLMUL/PMULL) algorithm: + * V. Gopal et al., "Fast CRC Computation for Generic Polynomials + * Using PCLMULQDQ", Intel white paper, 2009. + */ + +#ifndef OUROBOROS_LIB_CRC64_H +#define OUROBOROS_LIB_CRC64_H + +#include <stddef.h> +#include <stdint.h> + +#define CRC64_HASH_LEN 8 + +void crc64_nvme(uint64_t * crc, + const void * buf, + size_t len); + +#endif /* OUROBOROS_LIB_CRC64_H */ diff --git a/include/ouroboros/crc8.h b/include/ouroboros/crc8.h new file mode 100644 index 00000000..97502a25 --- /dev/null +++ b/include/ouroboros/crc8.h @@ -0,0 +1,43 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2026 + * + * 8-bit Cyclic Redundancy Check (AUTOSAR variant) + * + * 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/. + */ + +/* + * Polynomial: AUTOSAR SWS_CRC, CRC-8/AUTOSAR. + * reveng catalog: https://reveng.sourceforge.io/crc-catalogue + * + * Intended for short header check sequences (typ. <= 32 bytes). + * Hamming distance HD=4 up to 119 message bits, HD=3 up to 247. + */ + +#ifndef OUROBOROS_LIB_CRC8_H +#define OUROBOROS_LIB_CRC8_H + +#include <stddef.h> +#include <stdint.h> + +#define CRC8_HASH_LEN 1 + +void crc8_autosar(uint8_t * crc, + const void * buf, + size_t len); + +#endif /* OUROBOROS_LIB_CRC8_H */ diff --git a/include/ouroboros/crypt.h b/include/ouroboros/crypt.h index 806d39ab..9feaa610 100644 --- a/include/ouroboros/crypt.h +++ b/include/ouroboros/crypt.h @@ -28,19 +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 MSGBUFSZ 2048 +#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 @@ -51,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 @@ -95,16 +95,22 @@ #define X448MLKEM1024_PKSZ 1624 /* 56 + 1568 */ #define X448MLKEM1024_SKSZ 3224 /* 56 + 3168 */ +#define CRYPT_KEY_BUFSZ 4096 /* Safe buffer for key material */ + #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) +/* 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 { @@ -113,18 +119,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 */ @@ -210,9 +224,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, @@ -288,12 +314,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); @@ -302,10 +332,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); @@ -341,6 +398,10 @@ int crypt_load_pubkey_raw_file(const char * path, int crypt_load_privkey_raw_file(const char * path, 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); @@ -358,6 +419,8 @@ int crypt_check_crt_name(void * crt, int crypt_get_crt_name(void * crt, char * name); +void crypt_cleanup(void); + /* Secure memory allocation for sensitive data (keys, secrets) */ int crypt_secure_malloc_init(size_t max); diff --git a/include/ouroboros/errno.h b/include/ouroboros/errno.h index 9d84df88..eedd978f 100644 --- a/include/ouroboros/errno.h +++ b/include/ouroboros/errno.h @@ -37,5 +37,6 @@ #ifndef EAUTH /* Exists on BSD */ #define EAUTH 1009 /* Authentication error */ #endif +#define EREPLAY 1010 /* OAP replay detected */ #endif /* OUROBOROS_ERRNO_H */ diff --git a/include/ouroboros/fccntl.h b/include/ouroboros/fccntl.h index d3baea8f..e91e91dd 100644 --- a/include/ouroboros/fccntl.h +++ b/include/ouroboros/fccntl.h @@ -50,6 +50,12 @@ #define FRCTFRESCNTL 00000002 /* Feedback from receiver */ #define FRCTFLINGER 00000004 /* Send unsent data */ +/* All user-visible bits (readable via FRCTGFLAGS). */ +#define FRCTFMASK (FRCTFRTX | FRCTFRESCNTL | FRCTFLINGER) + +/* Subset writable via FRCTSFLAGS; FRCTFRTX is fixed at flow_alloc. */ +#define FRCTFSETMASK (FRCTFRESCNTL | FRCTFLINGER) + /* Flow operations */ #define FLOWSRCVTIMEO 00000001 /* Set read timeout */ #define FLOWGRCVTIMEO 00000002 /* Get read timeout */ @@ -60,10 +66,17 @@ #define FLOWGFLAGS 00000007 /* Get flags for flow */ #define FLOWGRXQLEN 00000010 /* Get queue length on rx */ #define FLOWGTXQLEN 00000011 /* Get queue length on tx */ +#define FLOWGMTU 00000012 /* Get per-packet MTU */ /* FRCT operations */ #define FRCTSFLAGS 00001000 /* Set flags for FRCT */ #define FRCTGFLAGS 00002000 /* Get flags for FRCT */ +#define FRCTSMAXSDU 00003000 /* Set max recv SDU size */ +#define FRCTGMAXSDU 00004000 /* Get max recv SDU size */ +#define FRCTSRRINGSZ 00005000 /* Set stream rcv ring sz */ +#define FRCTGRRINGSZ 00006000 /* Get stream rcv ring sz */ +#define FRCTSRTOMIN 00007000 /* Set RTO floor (ns) */ +#define FRCTGRTOMIN 00010000 /* Get RTO floor (ns) */ __BEGIN_DECLS diff --git a/include/ouroboros/flow.h b/include/ouroboros/flow.h index fe4582e7..8b096410 100644 --- a/include/ouroboros/flow.h +++ b/include/ouroboros/flow.h @@ -25,6 +25,7 @@ #include <ouroboros/qos.h> +#include <stdint.h> #include <sys/types.h> #define SYMMKEYSZ 32 @@ -50,6 +51,8 @@ struct flow_info { time_t mpl; + uint32_t mtu; /* n-1 layer MTU in bytes, 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 0838df97..c6609ffc 100644 --- a/include/ouroboros/hash.h +++ b/include/ouroboros/hash.h @@ -38,6 +38,9 @@ enum hash_algo { HASH_SHA3_512 = DIR_HASH_SHA3_512, HASH_CRC32, HASH_MD5, + HASH_CRC64, + HASH_CRC8, + HASH_CRC16, }; #define HASH_FMT32 "%02x%02x%02x%02x" @@ -86,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 93236271..d00d6f08 100644 --- a/include/ouroboros/ipcp-dev.h +++ b/include/ouroboros/ipcp-dev.h @@ -28,16 +28,23 @@ #include <ouroboros/ssm_pool.h> #include <ouroboros/utils.h> +#include <stdint.h> + 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); +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, diff --git a/include/ouroboros/ipcp.h b/include/ouroboros/ipcp.h index e29b080a..135b8fcb 100644 --- a/include/ouroboros/ipcp.h +++ b/include/ouroboros/ipcp.h @@ -124,9 +124,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 +136,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 = { diff --git a/include/ouroboros/irm.h b/include/ouroboros/irm.h index d5e4f1ab..7cb71c21 100644 --- a/include/ouroboros/irm.h +++ b/include/ouroboros/irm.h @@ -53,13 +53,13 @@ int irm_bootstrap_ipcp(pid_t pid, const struct ipcp_config * conf); int irm_connect_ipcp(pid_t pid, - const char * component, const char * dst, + const char * component, qosspec_t qs); int irm_disconnect_ipcp(pid_t pid, - const char * component, - const char * dst); + const char * dst, + const char * component); int irm_bind_program(const char * prog, const char * name, 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 6f341cfc..758b6db8 100644 --- a/include/ouroboros/np1_flow.h +++ b/include/ouroboros/np1_flow.h @@ -36,13 +36,17 @@ 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, .bandwidth = 0, .availability = 0, .loss = UINT32_MAX, .ber = UINT32_MAX, - .in_order = 0, .max_gap = UINT32_MAX, .timeout = 0 }; 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 6b0bbc17..7980ad00 100644 --- a/include/ouroboros/qos.h +++ b/include/ouroboros/qos.h @@ -28,79 +28,88 @@ #define DEFAULT_PEER_TIMEOUT 120000 +/* 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 */ +}; + typedef struct qos_spec { + uint8_t service; /* enum qos_service; gates FRCT (>0). */ 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; +/* "_safe" = integrity check (ber=0). "rt" = latency over reliability. */ + static const qosspec_t qos_raw = { + .service = SVC_RAW, .delay = UINT32_MAX, .bandwidth = 0, .availability = 0, .loss = 1, .ber = 1, - .in_order = 0, .max_gap = UINT32_MAX, - .timeout = DEFAULT_PEER_TIMEOUT + .timeout = 0 }; -static const qosspec_t qos_raw_no_errors = { +static const qosspec_t qos_raw_safe = { + .service = SVC_RAW, .delay = UINT32_MAX, .bandwidth = 0, .availability = 0, .loss = 1, .ber = 0, - .in_order = 0, .max_gap = UINT32_MAX, - .timeout = DEFAULT_PEER_TIMEOUT + .timeout = 0 }; -static const qosspec_t qos_best_effort = { - .delay = UINT32_MAX, - .bandwidth = 0, - .availability = 0, +static const qosspec_t qos_rt = { + .service = SVC_MESSAGE, + .delay = 100, + .bandwidth = UINT64_MAX, + .availability = 3, .loss = 1, - .ber = 0, - .in_order = 1, - .max_gap = UINT32_MAX, + .ber = 1, + .max_gap = 100, .timeout = DEFAULT_PEER_TIMEOUT }; -static const qosspec_t qos_video = { +static const qosspec_t qos_rt_safe = { + .service = SVC_MESSAGE, .delay = 100, .bandwidth = UINT64_MAX, .availability = 3, .loss = 1, .ber = 0, - .in_order = 1, .max_gap = 100, .timeout = DEFAULT_PEER_TIMEOUT }; -static const qosspec_t qos_voice = { - .delay = 50, - .bandwidth = 100000, - .availability = 5, - .loss = 1, +static const qosspec_t qos_msg = { + .service = SVC_MESSAGE, + .delay = 1000, + .bandwidth = 0, + .availability = 0, + .loss = 0, .ber = 0, - .in_order = 1, - .max_gap = 50, + .max_gap = 2000, .timeout = DEFAULT_PEER_TIMEOUT }; -static const qosspec_t qos_data = { +static const qosspec_t qos_stream = { + .service = SVC_STREAM, .delay = 1000, .bandwidth = 0, .availability = 0, .loss = 0, .ber = 0, - .in_order = 1, .max_gap = 2000, .timeout = DEFAULT_PEER_TIMEOUT }; 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..a5854d5b 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,10 @@ int ipcp_flow_req_arr__irm_req_ser(buffer_t * buf, const struct flow_info * flow, const 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 +69,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_pk_buff.h b/include/ouroboros/ssm_pk_buff.h index 1b779ad1..1d5597c7 100644 --- a/include/ouroboros/ssm_pk_buff.h +++ b/include/ouroboros/ssm_pk_buff.h @@ -28,25 +28,25 @@ struct ssm_pk_buff; -size_t ssm_pk_buff_get_idx(struct ssm_pk_buff * spb); +size_t ssm_pk_buff_get_off(const struct ssm_pk_buff * spb); -uint8_t * ssm_pk_buff_head(struct ssm_pk_buff * spb); +uint8_t * ssm_pk_buff_head(const struct ssm_pk_buff * spb); -uint8_t * ssm_pk_buff_tail(struct ssm_pk_buff * spb); +uint8_t * ssm_pk_buff_tail(const struct ssm_pk_buff * spb); -size_t ssm_pk_buff_len(struct ssm_pk_buff * spb); +size_t ssm_pk_buff_len(const struct ssm_pk_buff * spb); -uint8_t * ssm_pk_buff_head_alloc(struct ssm_pk_buff * spb, - size_t size); +uint8_t * ssm_pk_buff_push(struct ssm_pk_buff * spb, + size_t size); -uint8_t * ssm_pk_buff_tail_alloc(struct ssm_pk_buff * spb, - size_t size); +uint8_t * ssm_pk_buff_push_tail(struct ssm_pk_buff * spb, + size_t size); -uint8_t * ssm_pk_buff_head_release(struct ssm_pk_buff * spb, - size_t size); +uint8_t * ssm_pk_buff_pop(struct ssm_pk_buff * spb, + size_t size); -uint8_t * ssm_pk_buff_tail_release(struct ssm_pk_buff * spb, - size_t size); +uint8_t * ssm_pk_buff_pop_tail(struct ssm_pk_buff * spb, + size_t size); void ssm_pk_buff_truncate(struct ssm_pk_buff * spb, size_t len); diff --git a/include/ouroboros/ssm_pool.h b/include/ouroboros/ssm_pool.h index 89eff8eb..bba76798 100644 --- a/include/ouroboros/ssm_pool.h +++ b/include/ouroboros/ssm_pool.h @@ -32,7 +32,7 @@ struct ssm_pool; -/* Pool API: uid = 0 for GSPP (privileged), uid > 0 for PUP (per-user) */ +/* 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); @@ -46,13 +46,13 @@ 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. */ +/* Alloc count bytes, returns block offset, 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, +ssize_t ssm_pool_alloc_b(struct ssm_pool * pool, size_t count, uint8_t ** ptr, struct ssm_pk_buff ** spb, @@ -60,13 +60,13 @@ ssize_t ssm_pool_alloc_b(struct ssm_pool * pool, ssize_t ssm_pool_read(uint8_t ** dst, struct ssm_pool * pool, - size_t idx); + size_t off); struct ssm_pk_buff * ssm_pool_get(struct ssm_pool * pool, - size_t idx); + size_t off); int ssm_pool_remove(struct ssm_pool * pool, - size_t idx); + size_t off); void ssm_pool_reclaim_orphans(struct ssm_pool * pool, pid_t pid); diff --git a/include/ouroboros/ssm_rbuff.h b/include/ouroboros/ssm_rbuff.h index ffa10b8e..e77eec09 100644 --- a/include/ouroboros/ssm_rbuff.h +++ b/include/ouroboros/ssm_rbuff.h @@ -28,10 +28,12 @@ #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) */ struct ssm_rbuff; @@ -45,20 +47,23 @@ 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_bits(struct ssm_rbuff * rb, + uint32_t bits); -uint32_t ssm_rbuff_get_acl(struct ssm_rbuff * rb); +void ssm_rbuff_clr_bits(struct ssm_rbuff * rb, + uint32_t bits); + +uint32_t ssm_rbuff_get_flags(struct ssm_rbuff * rb); void ssm_rbuff_fini(struct ssm_rbuff * rb); int ssm_rbuff_mlock(struct ssm_rbuff * rb); int ssm_rbuff_write(struct ssm_rbuff * rb, - size_t idx); + size_t off); int ssm_rbuff_write_b(struct ssm_rbuff * rb, - size_t idx, + size_t off, const struct timespec * abstime); ssize_t ssm_rbuff_read(struct ssm_rbuff * rb); diff --git a/include/ouroboros/time.h b/include/ouroboros/time.h index 3d037a3c..a4136e8e 100644 --- a/include/ouroboros/time.h +++ b/include/ouroboros/time.h @@ -46,6 +46,12 @@ #define TS_TO_UINT64(ts) \ ((uint64_t)(ts).tv_sec * BILLION + (uint64_t)(ts).tv_nsec) +#define UINT64_TO_TS(ns, ts) \ + do { \ + (ts)->tv_sec = (time_t)((ns) / BILLION); \ + (ts)->tv_nsec = (long)((ns) % BILLION); \ + } while (0) + #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)} diff --git a/include/ouroboros/tpm.h b/include/ouroboros/tpm.h index c01a235c..56c04701 100644 --- a/include/ouroboros/tpm.h +++ b/include/ouroboros/tpm.h @@ -24,6 +24,7 @@ #define OUROBOROS_LIB_TPM_H #include <stdbool.h> +#include <sys/types.h> struct tpm; diff --git a/include/ouroboros/tw.h b/include/ouroboros/tw.h new file mode 100644 index 00000000..156f99db --- /dev/null +++ b/include/ouroboros/tw.h @@ -0,0 +1,77 @@ +/* + * Ouroboros - Copyright (C) 2016 - 2026 + * + * Generic deadline-ordered callback queue (timing wheel) + * + * 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_TW_H +#define OUROBOROS_TW_H + +#include <ouroboros/cdefs.h> +#include <ouroboros/list.h> + +#include <stddef.h> +#include <stdint.h> +#include <time.h> + +typedef void (*tw_fire_fn_t)(void * arg); + +struct tw_entry { + struct list_head next; + uint64_t deadline_ns; + tw_fire_fn_t fire; + void * arg; + size_t lvl; +}; + +__BEGIN_DECLS + +int tw_init(void); + +void tw_fini(void); + +void tw_init_entry(struct tw_entry * e); + +/* + * Schedule e to fire at deadline_ns. If e is already posted, + * the previous schedule is cancelled and replaced. + */ +void tw_post(struct tw_entry * e, + uint64_t deadline_ns, + tw_fire_fn_t fire, + void * arg); + +void tw_cancel(struct tw_entry * e); + +/* + * Advance the wheel and fire due callbacks. Callbacks run with the wheel + * unlocked and may call tw_post / tw_cancel on any entry, including the one + * currently firing. Concurrent tw_move from a second thread is a no-op. + */ +void tw_move(void); + +/* + * Write the absolute deadline of the earliest pending entry to *out. + * Empty wheel is signalled by out->tv_nsec == -1. + */ +void tw_next_expiry(struct timespec * out); + +__END_DECLS + +#endif /* OUROBOROS_TW_H */ 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 */ diff --git a/include/test/test.h b/include/test/test.h index 99681384..a76fe62a 100644 --- a/include/test/test.h +++ b/include/test/test.h @@ -30,6 +30,9 @@ #include <sys/wait.h> #include <sys/types.h> #include <sys/resource.h> +#ifdef __linux__ +#include <sys/prctl.h> +#endif #define TEST_RC_SUCCESS 0 #define TEST_RC_SKIP 1 @@ -86,6 +89,9 @@ static int __attribute__((unused)) test_assert_fail(int(* testfunc)(void)) #ifdef DISABLE_TESTS_CORE_DUMPS struct rlimit rl = { .rlim_cur = 0, .rlim_max = 0 }; setrlimit(RLIMIT_CORE, &rl); +#ifdef __linux__ + prctl(PR_SET_DUMPABLE, 0); +#endif #endif return testfunc(); /* should abort */ } |
