diff options
| author | eunhhu <[email protected]> | 2026-09-29 09:27:49 +0100 |
|---|---|---|
| committer | eunhhu <[email protected]> | 2026-09-29 09:27:49 +0100 |
| commit | f26f5fbd0a487a9a2848f819361b4e6fdd7a9cb1 (patch) | |
| tree | 624028b8a84299842bd3d3456be5962dd7dd420c /link | |
| parent | a6c825b8613177e32e9bf43e2abd6ac820bc8321 (diff) | |
| download | pure-f26f5fbd0a487a9a2848f819361b4e6fdd7a9cb1.tar.gz pure-f26f5fbd0a487a9a2848f819361b4e6fdd7a9cb1.zip | |
Find how large a packet the path carries: 1,452 bytes once a probe is acknowledged
A link starts at 1,200 bytes; once an RTT is known it sends a PING padded
to 1,452 (DPLPMTUD, RFC 8899), alone and with the don't-fragment bit,
and uses that size once it is acknowledged. A probe lost is no
congestion; it is tried again a second on, three times, then ten minutes
on. GSO is asked for each burst (a control message), not the socket, so
a packet sent alone is never cut. CUBIC counts in the datagram.
On loopback packets fall by 19-27%; the CPU a record falls 13% at 1 KiB
and 7% at 16 KiB; in the simulator of a 50 Mbit/s path with 1% loss the
goodput rises from 6.1 to 7.0 Mbit/s (5%: 2.3 to 3.0).
Diffstat (limited to 'link')
| -rw-r--r-- | link/link-protocol.c | 68 | ||||
| -rw-r--r-- | link/link-protocol.h | 19 | ||||
| -rw-r--r-- | link/link.c | 63 | ||||
| -rw-r--r-- | link/test-link.c | 4 |
4 files changed, 114 insertions, 40 deletions
diff --git a/link/link-protocol.c b/link/link-protocol.c index fbbdcef..226d238 100644 --- a/link/link-protocol.c +++ b/link/link-protocol.c @@ -14,6 +14,7 @@ #include <unistd.h> enum { VERSION = 0x70, HEADER = 21, DATAGRAM = PURE_LINK_DATAGRAM, BODY = DATAGRAM - HEADER - PURE_AEAD_TAG }; +enum { DATAGRAM_MOST = PURE_LINK_DATAGRAM_MOST, BODY_MOST = DATAGRAM_MOST - HEADER - PURE_AEAD_TAG }; /* A record that does not fit what is left of a packet goes in part, when * at least this much of it fits. */ enum { PART_LEAST = 64 }; @@ -24,6 +25,9 @@ enum { WINDOW_FIRST = 10 * DATAGRAM, WINDOW_LEAST = 2 * DATAGRAM }; /* Reordering awaited (see PureLinkSender): at first and at most; congestion * events with no late packet before the patience halves. */ enum { REORDER_FIRST = 3, REORDER_PACKETS_MOST = 256, REORDER_QUARTERS_MOST = 8, CALM = 16 }; +/* A probe of a larger datagram lost is tried again a second on, three + * times, then ten minutes on. */ +enum { REACH_TRIES = 3 }; /* CUBIC's constants (RFC 9438): the cubic's scale, and the window kept at a * congestion event. */ static const double CUBIC_C = 0.4, CUBIC_BETA = 0.7; @@ -32,7 +36,8 @@ static const double CUBIC_C = 0.4, CUBIC_BETA = 0.7; * 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. */ 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 = UINT64_C(64) << 20, HELD_MOST = UINT64_C(16) << 20, + REACH_AGAIN = 1000 * MS, REACH_RARELY = 600000 * MS; static void put16(unsigned char *p, uint32_t v) { p[0] = (unsigned char)v, p[1] = (unsigned char)(v >> 8); } static void put32(unsigned char *p, uint32_t v) { for (int i = 0; i < 4; ++i) p[i] = (unsigned char)(v >> 8 * i); } @@ -167,6 +172,7 @@ typedef struct { uint64_t episode; /* lost: the congestion episode it counted in */ uint32_t bytes; bool eliciting, done; /* acknowledged or lost */ + bool reach; /* a probe of a larger datagram */ bool lost, by_time; /* called lost (by time, or by later ones acknowledged), and not acknowledged since */ uint16_t count; Unit units[UNITS]; @@ -215,6 +221,12 @@ struct PureLinkSender { /* The window went unused (nothing to send, or no credit) when the * sender last looked: it does not grow then (RFC 9002 §7.8). */ bool unused; + /* The datagram's size now; a probe of the largest flies; when the next + * may go, and how many were lost (RFC 8899). */ + uint32_t datagram; + bool reaching; + uint64_t reach_at; + unsigned reach_lost; uint64_t received; /* parts received and not yet stored */ uint64_t unsent_from; /* no record before this number has a part unsent */ unsigned pto_count, probes; @@ -391,6 +403,11 @@ static void settle(PureLinkSender *l, Flight *f) { /* A packet lost: what it carried and was not received otherwise goes again. */ static void lose(PureLinkSender *l, Flight *f, uint64_t now, bool by_time) { settle(l, f); + if (f->reach) { /* the path does not carry it (yet): tried again a while on, three times, then rarely */ + l->reaching = false; + l->reach_at = now + (++l->reach_lost < REACH_TRIES ? REACH_AGAIN : REACH_RARELY); + return; + } f->lost = true; f->by_time = by_time; f->largest_then = l->largest_acked; @@ -407,13 +424,13 @@ static void lose(PureLinkSender *l, Flight *f, uint64_t now, bool by_time) { l->undo_ok = true; } l->recovery_start = now; - double w = (double)l->window / DATAGRAM; + double w = (double)l->window / l->datagram; l->cubic_max = w < l->cubic_max ? w * (1 + CUBIC_BETA) / 2 : w; /* fast convergence: another flow came */ l->window = most((uint64_t)((double)l->window * CUBIC_BETA), WINDOW_LEAST); l->threshold = l->window; l->cubic_k = cbrt(l->cubic_max * (1 - CUBIC_BETA) / CUBIC_C); l->cubic_epoch = now; - l->cubic_reno = (double)l->window / DATAGRAM; + l->cubic_reno = (double)l->window / l->datagram; if (++l->calm_events >= CALM) { /* many events, none of them late packets: less patience */ l->reorder_packets = most(REORDER_FIRST, l->reorder_packets / 2); l->reorder_quarters /= 2; @@ -498,7 +515,7 @@ static void grow(PureLinkSender *l, uint64_t bytes, uint64_t now) { l->window += bytes; return; } - double w = (double)l->window / DATAGRAM, acked = (double)bytes / DATAGRAM; + double w = (double)l->window / l->datagram, acked = (double)bytes / l->datagram; if (!l->cubic_epoch) { /* no event yet: the cubic starts where the window is */ l->cubic_epoch = now; l->cubic_max = l->cubic_reno = w; @@ -511,7 +528,7 @@ static void grow(PureLinkSender *l, uint64_t bytes, uint64_t now) { double target = CUBIC_C * d * d * d + l->cubic_max; target = target < w ? w : target > 1.5 * w ? 1.5 * w : target; double next = at < l->cubic_reno ? l->cubic_reno : w + (target - w) * acked / w; - l->window = most(l->window, (uint64_t)(next * DATAGRAM)); + l->window = most(l->window, (uint64_t)(next * l->datagram)); } static void on_ack(PureLinkSender *l, const unsigned char *p, size_t size, uint64_t now) { @@ -529,6 +546,7 @@ static void on_ack(PureLinkSender *l, const unsigned char *p, size_t size, uint6 if (!f || f->done) continue; settle(l, f); ++l->counts.acknowledged; + if (f->reach) l->datagram = DATAGRAM_MOST, l->reaching = false; /* the path carries it */ for (uint16_t u = 0; u < f->count; ++u) carried(l, &f->units[u], f->pn, true, now); if (f->eliciting && f->sent > l->recovery_start && !l->unused) grow(l, f->bytes, now); if (pn == largest) top = f; @@ -578,8 +596,8 @@ static void on_stored(PureLinkSender *l, const unsigned char *p, size_t size, ui } void pure_link_sender_take(PureLinkSender *l, const unsigned char *packet, size_t n, uint64_t now) { - unsigned char plain[DATAGRAM], nonce[PURE_AEAD_NONCE]; - if (!l->out || n < HEADER + PURE_AEAD_TAG || n > DATAGRAM || packet[0] != VERSION || get32(packet + 1) != l->peer_id) + unsigned char plain[DATAGRAM_MOST], nonce[PURE_AEAD_NONCE]; + if (!l->out || n < HEADER + PURE_AEAD_TAG || n > DATAGRAM_MOST || packet[0] != VERSION || get32(packet + 1) != l->peer_id) return; /* not ours, or an error from an earlier send (nobody listening) */ uint64_t run = get64(packet + 5), pn = get64(packet + 13); if (!l->in || l->in_run != run) { @@ -611,7 +629,7 @@ void pure_link_sender_take(PureLinkSender *l, const unsigned char *packet, size_ /* A packet of what is not sent yet, in order, within the receiver's credit: * its body's size, or 0 when there is none. */ static size_t fill(PureLinkSender *l, unsigned char *body, Flight *f, bool pad) { - size_t at = 0; + size_t at = 0, most = l->datagram - HEADER - PURE_AEAD_TAG; body[at++] = F_STREAM; at += put_names(body + at, l->source, l->input); put32(body + at, l->shape); @@ -634,7 +652,7 @@ static size_t fill(PureLinkSender *l, unsigned char *body, Flight *f, bool pad) if (r->seq > base + l->credit) break; bool full = false, fresh = !r->more && r->one.state == UNSENT, joins = run && r->seq == next; size_t whole = (joins ? 0 : RECORDS_HEAD) + varint_size(r->size) + r->size; - if (fresh && at + whole <= BODY) { + if (fresh && at + whole <= most) { if (!joins) { body[at] = F_RECORDS; put64(body + at + 1, r->seq); @@ -655,7 +673,7 @@ static size_t fill(PureLinkSender *l, unsigned char *body, Flight *f, bool pad) for (uint32_t i = 0; i < r->parts; ++i) { Part *p = part_of(r, i); if (p->state != UNSENT) continue; - size_t room = at + RECORD_HEAD < BODY ? BODY - at - RECORD_HEAD : 0; + size_t room = at + RECORD_HEAD < most ? most - at - RECORD_HEAD : 0; if (f->count == UNITS || (p->length > room && (room < PART_LEAST || !cut(r, i, (uint32_t)room)))) { full = true; break; @@ -678,8 +696,8 @@ static size_t fill(PureLinkSender *l, unsigned char *body, Flight *f, bool pad) before = before && !full; if (full) { /* the packet is full: whole, so it can go in a burst */ if (pad && f->count) { - memset(body + at, F_PADDING, BODY - at); - at = BODY; + memset(body + at, F_PADDING, most - at); + at = most; } break; } @@ -724,13 +742,24 @@ static size_t transmit(PureLinkSender *l, Flight *f, const unsigned char *body, return total; } -size_t pure_link_sender_packet(PureLinkSender *l, unsigned char out[PURE_LINK_DATAGRAM], bool whole, uint64_t now, +size_t pure_link_sender_packet(PureLinkSender *l, unsigned char out[PURE_LINK_DATAGRAM_MOST], bool whole, uint64_t now, uint64_t *number) { bool probe = l->probes > 0; - if (!probe && l->in_flight + DATAGRAM > l->window) l->unused = false; - if (!l->out || (!probe && l->in_flight + DATAGRAM > l->window) || !flight_room(l)) return 0; - unsigned char body[BODY]; + if (!probe && l->in_flight + l->datagram > l->window) l->unused = false; + if (!l->out || (!probe && l->in_flight + l->datagram > l->window) || !flight_room(l)) return 0; + unsigned char body[BODY_MOST]; Flight f = {.pn = l->next_pn, .eliciting = true}; + if (l->datagram < DATAGRAM_MOST && !l->reaching && l->sampled && now >= l->reach_at && + l->in_flight + DATAGRAM_MOST <= l->window) { /* the path may carry more: a PING of the most, padded */ + body[0] = F_PING; + memset(body + 1, F_PADDING, BODY_MOST - 1); + f.reach = true; + size_t total = transmit(l, &f, body, BODY_MOST, out, now); + if (!total) return 0; + l->reaching = true; + *number = f.pn; + return total; + } size_t size = fill(l, body, &f, whole); if (!size) { l->unused = true; @@ -812,6 +841,7 @@ PureLinkSender *pure_link_sender(const char *source, const char *input, uint32_t l->threshold = UINT64_MAX; l->credit = CREDIT; l->reorder_packets = REORDER_FIRST; + l->datagram = DATAGRAM; return l; } @@ -836,6 +866,8 @@ bool pure_link_sender_shared(PureLinkSender *l, const unsigned char peer[PURE_SE uint64_t pure_link_sender_queued(const PureLinkSender *l) { return l->queued; } +size_t pure_link_sender_datagram(const PureLinkSender *l) { return l->datagram; } + uint64_t pure_link_sender_stored(const PureLinkSender *l) { return l->stored; } void pure_link_sender_counts(const PureLinkSender *l, PureLinkCounts *out) { @@ -1545,7 +1577,7 @@ static bool take_from(PureLinkReceiver *s, Peer *peer, const unsigned char *pack Run made = {.active = a, .run = run, .seen = now, .used = true}; bool keyed = r || (derive(a->secret, peer->key, s->own_public, run, 0, made.in_key, made.in_base) && derive(a->secret, s->own_public, peer->key, s->run, run, made.out_key, made.out_base)); - unsigned char plain[DATAGRAM], nonce[PURE_AEAD_NONCE]; + unsigned char plain[DATAGRAM_MOST], nonce[PURE_AEAD_NONCE]; nonce_of(nonce, r ? r->in_base : made.in_base, pn); size_t size = n - HEADER - PURE_AEAD_TAG; const unsigned char *key = r ? r->in_key : made.in_key, *give = key; @@ -1616,7 +1648,7 @@ static bool take_from(PureLinkReceiver *s, Peer *peer, const unsigned char *pack void pure_link_receiver_take(PureLinkReceiver *s, const unsigned char *packet, size_t n, const void *from, size_t from_size, uint64_t now) { - if (n < HEADER + PURE_AEAD_TAG || n > DATAGRAM || packet[0] != VERSION || !s->index_size) return; + if (n < HEADER + PURE_AEAD_TAG || n > DATAGRAM_MOST || packet[0] != VERSION || !s->index_size) return; uint32_t id = get32(packet + 1); size_t mask = s->index_size - 1; uint64_t run = get64(packet + 5); diff --git a/link/link-protocol.h b/link/link-protocol.h index e82e599..f51260b 100644 --- a/link/link-protocol.h +++ b/link/link-protocol.h @@ -51,10 +51,12 @@ * Losses are detected as RFC 9002 says; congestion control is its NewReno * (RFC 9002 §7). */ -/* A packet is at most PURE_LINK_DATAGRAM bytes; a source's or an input's - * name is 1 to PURE_LINK_NAME bytes; a receiver keeps where a packet came - * from (the socket's address) in PURE_LINK_ADDRESS bytes. */ -enum { PURE_LINK_DATAGRAM = 1200, PURE_LINK_NAME = 64, PURE_LINK_ADDRESS = 28 }; +/* A packet is PURE_LINK_DATAGRAM bytes at most at first, and up to + * PURE_LINK_DATAGRAM_MOST once the path is found to carry that many (a + * probe of that size acknowledged: DPLPMTUD, RFC 8899); a source's or an + * input's name is 1 to PURE_LINK_NAME bytes; a receiver keeps where a + * packet came from (the socket's address) in PURE_LINK_ADDRESS bytes. */ +enum { PURE_LINK_DATAGRAM = 1200, PURE_LINK_DATAGRAM_MOST = 1452, PURE_LINK_NAME = 64, PURE_LINK_ADDRESS = 28 }; /* Counts for a host's report: packets and records sent, records sent again, * packets lost and acknowledged, packets received, deliveries handed over, @@ -91,10 +93,13 @@ void pure_link_sender_take(PureLinkSender *, const unsigned char *packet, size_t void pure_link_sender_tick(PureLinkSender *, uint64_t now); /* The next packet to send into out, and its number: its size, or 0 when * there is none (nothing to send, or the window is full). whole: a full - * packet is padded to PURE_LINK_DATAGRAM (so packets may go in a burst of - * whole datagrams, as UDP GSO sends them). */ -size_t pure_link_sender_packet(PureLinkSender *, unsigned char out[PURE_LINK_DATAGRAM], bool whole, uint64_t now, + * packet is padded to the sender's datagram (so packets may go in a burst + * of whole datagrams, as UDP GSO sends them). A probe of a larger size is + * larger than the datagram: it goes alone. */ +size_t pure_link_sender_packet(PureLinkSender *, unsigned char out[PURE_LINK_DATAGRAM_MOST], bool whole, uint64_t now, uint64_t *number); +/* The size of a full packet now. */ +size_t pure_link_sender_datagram(const PureLinkSender *); /* A packet made but not sent (the socket refused it): what it carried is * sent again, and it counts as never sent. */ void pure_link_sender_refused(PureLinkSender *, uint64_t number); diff --git a/link/link.c b/link/link.c index 449ea60..3cac405 100644 --- a/link/link.c +++ b/link/link.c @@ -16,10 +16,11 @@ #include <time.h> #include <unistd.h> -enum { DATAGRAM = PURE_LINK_DATAGRAM }; +enum { DATAGRAM = PURE_LINK_DATAGRAM, DATAGRAM_MOST = PURE_LINK_DATAGRAM_MOST }; /* Packets sent at once with UDP GSO: each but the last is a whole datagram - * (a full one padded), and 54 of them fit a UDP datagram's 65,507 bytes. */ -enum { BURST = 54 }; + * (a full one padded), as many as fit a UDP datagram's 65,507 bytes (54 of + * 1,200 bytes). */ +enum { BURST = 54, BURST_BYTES = 65507 }; #ifndef UDP_SEGMENT #define UDP_SEGMENT 103 #endif @@ -35,7 +36,7 @@ static uint64_t now_ns(void) { } /* Datagrams taken from a socket at once (recvmmsg). */ -enum { TAKE = 64, SLOT = DATAGRAM + 64 }; +enum { TAKE = 64, SLOT = DATAGRAM_MOST + 64 }; typedef struct { unsigned char bytes[TAKE][SLOT]; struct sockaddr_storage from[TAKE]; @@ -112,6 +113,7 @@ struct PureLink { size_t burst_size; unsigned burst_count; uint64_t burst_pn[BURST]; + size_t segment; /* the datagram's size the bursts are cut at */ bool gso, no_gso; }; @@ -197,15 +199,31 @@ static void take_answers(PureLink *l, uint64_t now) { static bool transient(void) { return errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS || errno == EINTR; } +/* The burst in one call, cut into datagrams of the segment's size by the + * system (UDP GSO, asked for this call alone: a packet sent by itself is + * never cut, however large). */ +static ssize_t send_burst(PureLink *l) { + _Alignas(8) unsigned char control[CMSG_SPACE(sizeof(uint16_t))] = {0}; + struct iovec part = {l->burst, l->burst_size}; + struct msghdr m = {.msg_iov = &part, .msg_iovlen = 1, .msg_control = control, .msg_controllen = sizeof(control)}; + struct cmsghdr *c = CMSG_FIRSTHDR(&m); + c->cmsg_level = SOL_UDP; + c->cmsg_type = UDP_SEGMENT; + c->cmsg_len = CMSG_LEN(sizeof(uint16_t)); + uint16_t segment = (uint16_t)l->segment; + memcpy(CMSG_DATA(c), &segment, sizeof(segment)); + return sendmsg(l->fd, &m, 0); +} + /* The packets of the burst to the socket (each but the last a whole * datagram): with GSO in one call, else one by one. */ static void flush(PureLink *l) { unsigned n = l->burst_count, sent = 0; bool whole = n > 1 && l->gso; - if (whole && send(l->fd, l->burst, l->burst_size, 0) >= 0) sent = n; + if (whole && send_burst(l) >= 0) sent = n; else if (whole && !transient()) l->gso = false, whole = false; /* no GSO here: one by one from now on */ for (size_t at = 0; !whole && sent < n; ++sent) { - size_t size = sent + 1 < n ? DATAGRAM : l->burst_size - at; + size_t size = sent + 1 < n ? l->segment : l->burst_size - at; if (send(l->fd, l->burst + at, size, 0) < 0 && transient()) break; at += size; } @@ -215,17 +233,28 @@ static void flush(PureLink *l) { } /* Packets while the sender has them (up to 128 a turn), into bursts: a - * short packet ends one. */ + * short packet ends one, and a probe of a larger datagram goes alone. */ static void send_packets(PureLink *l, uint64_t now) { l->queued_new = false; /* all queued is looked at now */ + l->segment = pure_link_sender_datagram(l->sender); /* larger once the path is found to carry it */ + unsigned most = BURST_BYTES / l->segment < BURST ? (unsigned)(BURST_BYTES / l->segment) : BURST; for (unsigned n = 0; n < 128; ++n) { - if (l->burst_count && l->burst_size != (size_t)l->burst_count * DATAGRAM) flush(l); /* the last was short */ + if (l->burst_count && l->burst_size != (size_t)l->burst_count * l->segment) flush(l); /* the last was short */ uint64_t pn; size_t size = pure_link_sender_packet(l->sender, l->burst + l->burst_size, l->gso, now, &pn); if (!size) break; + if (size > l->segment) { /* a probe: the burst before it, then it alone */ + unsigned char probe[DATAGRAM_MOST]; + memcpy(probe, l->burst + l->burst_size, size); + flush(l); + memcpy(l->burst, probe, size); + l->burst_pn[0] = pn, l->burst_count = 1, l->burst_size = size; + flush(l); + continue; + } l->burst_pn[l->burst_count++] = pn; l->burst_size += size; - if (l->burst_count == BURST) flush(l); + if (l->burst_count == most) flush(l); } flush(l); } @@ -274,15 +303,23 @@ bool pure_link_start(PureLink *l, void (*notify)(void *), void *context, char *e } l->fd = socket(found->ai_family, SOCK_DGRAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); bool ok = l->fd >= 0 && !connect(l->fd, found->ai_addr, found->ai_addrlen); + int buffer = 4 << 20, family = found->ai_family; freeaddrinfo(found); - int buffer = 4 << 20; if (ok) { setsockopt(l->fd, SOL_SOCKET, SO_SNDBUF, &buffer, sizeof(buffer)); setsockopt(l->fd, SOL_SOCKET, SO_RCVBUF, &buffer, sizeof(buffer)); + /* Never cut up on the way (the don't-fragment bit): a packet the + * path does not carry is lost, as a probe of a larger size must be. */ + int probe = IP_PMTUDISC_PROBE, probe6 = IPV6_PMTUDISC_PROBE; + if (family == AF_INET6) setsockopt(l->fd, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &probe6, sizeof(probe6)); + else setsockopt(l->fd, IPPROTO_IP, IP_MTU_DISCOVER, &probe, sizeof(probe)); } - int segment = DATAGRAM; - l->gso = ok && !l->no_gso && !setsockopt(l->fd, SOL_UDP, UDP_SEGMENT, &segment, sizeof(segment)); - ok = ok && l->keyed && (l->burst = malloc((size_t)BURST * DATAGRAM)) && (l->taking = malloc(sizeof(*l->taking))) && + /* GSO where the system has it: asked for each burst, never for the socket. */ + int segment = DATAGRAM, none = 0; + l->segment = DATAGRAM; + l->gso = ok && !l->no_gso && !setsockopt(l->fd, SOL_UDP, UDP_SEGMENT, &segment, sizeof(segment)) && + !setsockopt(l->fd, SOL_UDP, UDP_SEGMENT, &none, sizeof(none)); + ok = ok && l->keyed && (l->burst = malloc(BURST_BYTES + DATAGRAM_MOST)) && (l->taking = malloc(sizeof(*l->taking))) && (l->wake = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC)) >= 0; l->notify = notify; l->context = context; diff --git a/link/test-link.c b/link/test-link.c index 8be32a9..a05f8bc 100644 --- a/link/test-link.c +++ b/link/test-link.c @@ -45,7 +45,7 @@ typedef struct { uint64_t at; bool to_receiver; size_t size; - unsigned char bytes[PURE_LINK_DATAGRAM]; + unsigned char bytes[PURE_LINK_DATAGRAM_MOST]; } Packet; typedef struct { @@ -185,7 +185,7 @@ static Outcome run(const Case *c, const char *dir) { PureLinkReceiver *r = receiver_of(&w, b, public_b, public_a, memo, c->idle_ms); PureLinkCounts sent_before = {0}, heard_before = {0}; bool receiver_restarted = !c->receiver_restart_after, sender_restarted = !c->sender_restart_after; - unsigned char out[PURE_LINK_DATAGRAM]; + unsigned char out[PURE_LINK_DATAGRAM_MOST]; while (w.heard < c->records || pure_link_sender_stored(s) < c->records) { CHECK(w.now < 600000 * MS, "%s: not done after ten minutes (heard %llu of %llu)", c->name, (unsigned long long)w.heard, (unsigned long long)c->records); |
