aboutsummaryrefslogtreecommitdiffstats
path: root/link
diff options
context:
space:
mode:
authoreunhhu <[email protected]>2026-09-29 16:41:24 +0100
committereunhhu <[email protected]>2026-09-29 16:41:24 +0100
commit3bd9b6c924c801dc5033510391ac251a7309219f (patch)
treed0465bb16850b822e4b2a09a73cf370993fd4f16 /link
parent590cbb9d0c73c9e48ac4a637c2e88a7e99c7965f (diff)
downloadpure-3bd9b6c924c801dc5033510391ac251a7309219f.tar.gz
pure-3bd9b6c924c801dc5033510391ac251a7309219f.zip
Take no send larger than a receiver holds: 16 MiB, not 64
A sender took sends up to 64 MiB, but a receiver holds 16 MiB of a stream not yet stored, so a send between the two went again forever. The most a send may be is now PURE_LINK_RECORD_MOST, what a receiver holds; test-link sends one that large whole and is refused one byte more.
Diffstat (limited to 'link')
-rw-r--r--link/link-protocol.c5
-rw-r--r--link/link-protocol.h10
-rw-r--r--link/test-link.c59
3 files changed, 69 insertions, 5 deletions
diff --git a/link/link-protocol.c b/link/link-protocol.c
index 7bc4b00..62def6f 100644
--- a/link/link-protocol.c
+++ b/link/link-protocol.c
@@ -51,9 +51,10 @@ static const double CUBIC_C = 0.4, CUBIC_BETA = 0.7, RENO_PACE = 3 * (1 - 0.7) /
#define MS UINT64_C(1000000)
/* RFC 9002's values, but a first RTT of 100 ms (a LAN's, not the 333 ms of
* the Internet at large); a probe at least every second while a receiver is
- * away; a record received but not stored after a second is sent again. */
+ * away; a record received but not stored after a second is sent again. A
+ * receiver holds as much of a stream not yet stored as a record may be. */
static const uint64_t FIRST_RTT = 100 * MS, ACK_DELAY_MOST = 25 * MS, GRANULARITY = 1 * MS, PTO_MOST = 1000 * MS,
- STORED_WAIT = 1000 * MS, RECORD_MOST = UINT64_C(64) << 20, HELD_MOST = UINT64_C(16) << 20,
+ STORED_WAIT = 1000 * MS, RECORD_MOST = PURE_LINK_RECORD_MOST, HELD_MOST = RECORD_MOST,
REACH_AGAIN = 1000 * MS, REACH_RARELY = 600000 * MS;
/* An epoch lasts an hour or a GiB sent; a sender that hears nothing for 2 s
* with packets out, and four probes, takes its epoch for lost. */
diff --git a/link/link-protocol.h b/link/link-protocol.h
index 25ff12f..b9a3eeb 100644
--- a/link/link-protocol.h
+++ b/link/link-protocol.h
@@ -118,8 +118,11 @@
* goes back to the first size when the larger stop coming, ยง4.3); a
* source's or an input's name is 1 to PURE_LINK_NAME bytes; a receiver
* keeps a packet's path (where it came from, and the address of its own it
- * came to: link/link.c answers from there) in PURE_LINK_ADDRESS bytes. */
-enum { PURE_LINK_DATAGRAM = 1200, PURE_LINK_DATAGRAM_MOST = 1452, PURE_LINK_NAME = 64, PURE_LINK_ADDRESS = 48 };
+ * came to: link/link.c answers from there) in PURE_LINK_ADDRESS bytes; a
+ * send is PURE_LINK_RECORD_MOST bytes at most (what a receiver holds of a
+ * stream not yet stored). */
+enum { PURE_LINK_DATAGRAM = 1200, PURE_LINK_DATAGRAM_MOST = 1452, PURE_LINK_NAME = 64, PURE_LINK_ADDRESS = 48,
+ PURE_LINK_RECORD_MOST = 16 << 20 };
/* Counts for a host's report: packets and records sent, records sent again,
* packets lost and acknowledged, packets received, deliveries handed over,
@@ -150,7 +153,8 @@ bool pure_link_sender_peer(PureLinkSender *, const unsigned char peer[PURE_SEAL_
* a key kept in hardware, say, or a test's many keys): no X25519 here. */
bool pure_link_sender_shared(PureLinkSender *, const unsigned char peer[PURE_SEAL_KEY], const unsigned char secret[32]);
/* A saved send, numbered one past the last queued (any number when none
- * waits); false when it cannot be kept. */
+ * waits); false when it cannot be kept (or is larger than
+ * PURE_LINK_RECORD_MOST). */
bool pure_link_sender_queue(PureLinkSender *, uint64_t seq, const unsigned char *bytes, size_t size);
/* A packet that came (the receiver's answers; others are let be). */
void pure_link_sender_take(PureLinkSender *, const unsigned char *packet, size_t size, uint64_t now);
diff --git a/link/test-link.c b/link/test-link.c
index 1d84804..9b709ce 100644
--- a/link/test-link.c
+++ b/link/test-link.c
@@ -652,6 +652,64 @@ static void flood(const char *dir) {
free(w.packets);
}
+/* The largest send (PURE_LINK_RECORD_MOST) goes whole, over a receiver that
+ * never heard of the stream before; one byte more is not taken to send. */
+typedef struct { World *w; PureLinkSender *s; } Largest;
+static bool deliver_largest(void *context, const PureLinkDelivery *d) {
+ World *w = context;
+ uint32_t size = (uint32_t)d->records[0] | (uint32_t)d->records[1] << 8 | (uint32_t)d->records[2] << 16 |
+ (uint32_t)d->records[3] << 24;
+ bool same = d->first == 1 && d->count == 1 && size == PURE_LINK_RECORD_MOST;
+ for (size_t i = 0; same && i < size; i += 4099) same = d->records[4 + i] == record_byte(1, i);
+ CHECK(same, "largest: a delivery of %u records from %llu, the first %u bytes", d->count,
+ (unsigned long long)d->first, size);
+ w->heard = same ? 1 : w->heard;
+ w->done[w->done_count++] = (PureLinkDone){d->token, 1, true, false};
+ return true;
+}
+static bool answer_largest(void *context, const unsigned char *packet, size_t size, const unsigned char *to,
+ size_t to_size) {
+ (void)to, (void)to_size;
+ Largest *l = context;
+ pure_link_sender_take(l->s, packet, size, l->w->now);
+ return true;
+}
+static void largest(const char *dir) {
+ unsigned char public_a[PURE_SEAL_KEY], public_b[PURE_SEAL_KEY], out[PURE_LINK_DATAGRAM_MOST];
+ char error[256];
+ PureSealKey *a = key(dir, "a.key", public_a), *b = key(dir, "b.key", public_b);
+ World w = {0};
+ PureLinkReceiver *r = pure_link_receiver(b, public_b, &(PureLinkHost){input_of, deliver_largest, &w}, error,
+ sizeof(error));
+ PureLinkSender *s = pure_link_sender("test.ordered", "order", 0x5eed, a, public_a);
+ unsigned char *bytes = malloc(PURE_LINK_RECORD_MOST + 1);
+ if (!r || !s || !bytes || !pure_link_allow(r, "test.ordered", public_a, error, sizeof(error)) ||
+ !pure_link_sender_peer(s, public_b))
+ exit(1);
+ for (size_t i = 0; i <= PURE_LINK_RECORD_MOST; ++i) bytes[i] = record_byte(1, i);
+ CHECK(!pure_link_sender_queue(s, 1, bytes, PURE_LINK_RECORD_MOST + 1), "largest: a send past the most was taken");
+ CHECK(pure_link_sender_queue(s, 1, bytes, PURE_LINK_RECORD_MOST), "largest: the most a send may be was not taken");
+ free(bytes);
+ Largest l = {&w, s};
+ uint64_t number;
+ for (; w.now < 60000 * MS && pure_link_sender_stored(s) < 1; w.now += MS) {
+ for (size_t size; (size = pure_link_sender_packet(s, out, false, w.now, &number));)
+ pure_link_receiver_take(r, out, size, "sender0", 7, w.now);
+ size_t told = w.done_count;
+ memcpy(w.told, w.done, told * sizeof(*w.done));
+ w.done_count = 0;
+ pure_link_receiver_turn(r, w.told, told, w.now, answer_largest, &l);
+ pure_link_sender_tick(s, w.now);
+ }
+ CHECK(w.heard == 1 && pure_link_sender_stored(s) == 1, "largest: heard %llu, stored %llu after %llu ms",
+ (unsigned long long)w.heard, (unsigned long long)pure_link_sender_stored(s), (unsigned long long)(w.now / MS));
+ printf("{\"case\":\"largest\",\"bytes\":%d,\"virtual_ms\":%llu}\n", PURE_LINK_RECORD_MOST,
+ (unsigned long long)(w.now / MS));
+ pure_link_sender_free(s);
+ pure_link_receiver_free(r);
+ pure_seal_key_free(a), pure_seal_key_free(b);
+}
+
int main(void) {
char dir[] = "/tmp/pure-test-link-XXXXXX", error[256];
if (!mkdtemp(dir) || !pure_seal_ready(error, sizeof(error))) {
@@ -710,6 +768,7 @@ int main(void) {
o.hole ? "opened" : "never opened", o.datagram);
impostors(dir);
flood(dir);
+ largest(dir);
char command[600];
snprintf(command, sizeof(command), "rm -rf %s", dir);
if (system(command)) { /* left behind in /tmp */ }