diff options
Diffstat (limited to 'link')
| -rw-r--r-- | link/link-protocol.c | 2 | ||||
| -rw-r--r-- | link/link-protocol.h | 3 | ||||
| -rw-r--r-- | link/link.c | 78 |
3 files changed, 57 insertions, 26 deletions
diff --git a/link/link-protocol.c b/link/link-protocol.c index 03c3abc..fe62b5f 100644 --- a/link/link-protocol.c +++ b/link/link-protocol.c @@ -1063,6 +1063,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; } +unsigned pure_link_sender_probes(const PureLinkSender *l) { return l->pto_count; } + void pure_link_sender_epochs(PureLinkSender *l, uint64_t every_ns, uint64_t bytes) { l->epoch_every = every_ns; l->epoch_most = bytes; diff --git a/link/link-protocol.h b/link/link-protocol.h index c6ccc23..16fd683 100644 --- a/link/link-protocol.h +++ b/link/link-protocol.h @@ -161,6 +161,9 @@ size_t pure_link_sender_datagram(const PureLinkSender *); void pure_link_sender_refused(PureLinkSender *, uint64_t number); /* When the next timer is due (ns), or 0 for none. */ uint64_t pure_link_sender_due(const PureLinkSender *); +/* Probes in a row that nothing answered (0 once anything is): the path, or + * the receiver where it was, may be gone. */ +unsigned pure_link_sender_probes(const PureLinkSender *); /* How often it moves on to a new epoch (ns), and after how many bytes sent * (an hour and a GiB at first). */ void pure_link_sender_epochs(PureLinkSender *, uint64_t every_ns, uint64_t bytes); diff --git a/link/link.c b/link/link.c index 7b1b8d7..7027236 100644 --- a/link/link.c +++ b/link/link.c @@ -132,6 +132,7 @@ struct PureLink { uint64_t burst_pn[BURST]; size_t segment; /* the datagram's size the bursts are cut at */ bool gso, no_gso; + unsigned looked; /* the probes unanswered when it last looked for the receiver */ }; PureLink *pure_link_open(const char *url, const char *source, uint32_t shape, const PureSealKey *own, @@ -276,6 +277,46 @@ static void send_packets(PureLink *l, uint64_t now) { flush(l); } +/* Where the receiver's name is now, a socket connected there in place of + * the one before: so a link follows a receiver moved to another address, + * and goes out a new socket when its own network changed. False (the + * socket as it was) when the name or a socket fails. */ +static bool look(PureLink *l, char *error, size_t size) { + struct addrinfo hints = {.ai_family = AF_UNSPEC, .ai_socktype = SOCK_DGRAM}, *found = NULL; + int resolved = getaddrinfo(l->host, l->port, &hints, &found); + if (resolved) { + snprintf(error, size, "%s: %s", l->host, gai_strerror(resolved)); + return false; + } + int fd = socket(found->ai_family, SOCK_DGRAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0), family = found->ai_family; + bool ok = fd >= 0 && !connect(fd, found->ai_addr, found->ai_addrlen); + int failed = errno, buffer = 4 << 20; + freeaddrinfo(found); + if (!ok) { + if (fd >= 0) close(fd); + snprintf(error, size, "pure://%s:%s/%s: %s", l->host, l->port, l->input, strerror(failed)); + return false; + } + setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &buffer, sizeof(buffer)); + setsockopt(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(fd, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &probe6, sizeof(probe6)); + else setsockopt(fd, IPPROTO_IP, IP_MTU_DISCOVER, &probe, sizeof(probe)); + /* GSO where the system has it: asked for each burst, never for the socket. */ + int segment = DATAGRAM, none = 0; + l->gso = !l->no_gso && !setsockopt(fd, SOL_UDP, UDP_SEGMENT, &segment, sizeof(segment)) && + !setsockopt(fd, SOL_UDP, UDP_SEGMENT, &none, sizeof(none)); + if (l->fd >= 0) close(l->fd); + l->fd = fd; + return true; +} + +/* A link whose probes go unanswered three times in a row looks for its + * receiver again, and at each probe after (a second apart at most). */ +enum { LOOK_PROBES = 3 }; + static void *run_link(void *context) { PureLink *l = context; for (;;) { @@ -297,6 +338,13 @@ static void *run_link(void *context) { now = now_ns(); if (waits[0].revents) take_answers(l, now); pure_link_sender_tick(l->sender, now); + unsigned probes = pure_link_sender_probes(l->sender); + if (probes >= LOOK_PROBES && probes != l->looked) { /* before the probe goes, so its answer comes to the new socket */ + pthread_mutex_unlock(&l->lock); /* a name looked up may take its time: the owner queues meanwhile */ + look(l, NULL, 0); + pthread_mutex_lock(&l->lock); + } + l->looked = probes; send_packets(l, now); uint64_t stored = pure_link_sender_stored(l->sender); bool told = stored != l->told; @@ -312,36 +360,14 @@ bool pure_link_start(PureLink *l, void (*notify)(void *), void *context, char *e snprintf(error, size, "pure://%s:%s/%s: no receiver's key (--seal)", l->host, l->port, l->input); return false; } - struct addrinfo hints = {.ai_family = AF_UNSPEC, .ai_socktype = SOCK_DGRAM}, *found = NULL; - int resolved = getaddrinfo(l->host, l->port, &hints, &found); - if (resolved) { - snprintf(error, size, "%s: %s", l->host, gai_strerror(resolved)); - return false; - } - 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); - 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)); - } - /* GSO where the system has it: asked for each burst, never for the socket. */ - int segment = DATAGRAM, none = 0; + if (!look(l, error, size)) return false; 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; + bool 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; if (!ok || pthread_create(&l->thread, NULL, run_link, l)) { - snprintf(error, size, "pure://%s:%s/%s: cannot start (a socket, the keys)", l->host, l->port, l->input); + snprintf(error, size, "pure://%s:%s/%s: cannot start (the keys, memory)", l->host, l->port, l->input); return false; } pthread_setname_np(l->thread, "pure-link"); /* a link's CPU, seen apart */ |
