Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ping to check connection #84

Closed
wants to merge 5 commits into from

Conversation

kathrynfejer
Copy link
Contributor

Currently enftun can determine if there has been a connectivity change on the client's end and reconnects, but if the router or ENF is down, enftun does not recognize it for some time. By pinging the network we are connected to at configurable intervals, we can monitor our connectivity and increase availability.

This pings the network at the configured timeouts and whenever a new address or new link netlink event occurs. If one of these two occurs at the same time or within the reply timeout, only one echo request is sent.

Fixes #6 and Connected to #7

Copy link
Contributor

@drbild drbild left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is looking good, but I think redefining the responsibilities of the various components (enftun_conn_state, enftun_echo, enftun_tls, and enftun_netlink) can make the code more clear.

  1. enftun_tls is now aware of the conn_state internals to call things like tls->conn_state->echo.send_ping and tls->conn_state->send_request_cb(...). This tight coupling makes the code hard to reason about (changing the internal of conn_state might break the tls internals).

    Instead, let's look for a more generic mechanism. One option is to define a callbacktypedef void (*enftun_tls_on_receive )(). conn_state can register a function of this type with tls, which call it everytime a packet is received. (It would actually be better to define this on the channel, so that is could be used for any transport, not just tls.)

    However, we already have a generic mechanism to examine incoming packets --- the ingress chain. conn_state can register a handler at the head of the ingress chain. It will be called for each incoming packet. Rather than parsing or consuming the packet, it can just reset the echo request timer.

  2. The enftun_echo module is fairly anemic. This isn't necessarily a bad thing. However, here it can lead to some problems.

    For example, it is not safe to call the enftun_echo_send_request function if there is already an outstanding echo request queued. However, this is not enforced. The caller must track that state and manage it. If there are multiple callers, who knows...

    Extending the responsibilities of this module and making the public API more high-level will help. Specifically, move the request and reply timers out of conn_state into this module.

    So this module becomes responsible for sending heartbeats periodically and on-demand, waiting for a reply to the outstanding heartbeat, and notifying a registered callback if a heartbeat reply is not received. Renaming to enftun_heartbeat will better reflect this expanding functionality.

    • enftun_heartbeat_init(..., int (*on_timeout)() ) - on_timeout is called if a echo reply is not received
    • enftun_heartbeat_start(...) - start sending heartbeats periodically
    • enftun_heartbeat_restart(...) - reset the timer for periodic heartbeats
    • enftun_heartbeat_stop(...) - stop sending heartbeats; cancel any outstanding crbs.
    • enftun_heartbeat_now(...) - send a heartbeat immediately (or schedule to send one, if one is already in flight)
  3. The enftun_netlink module is now aware of the heartbeat stuff and has to return a special error code NEEDS_PING. That sort of coupling belongs in the conn_state module, which ties everything together. enftun_netlink_read_message is not longer properly returning the number of bytes read, etc.

    I suggest not modifying enftun_netlink_read_message. Instead, add helper function(s) in conn_state for parsing the message. conn_state can call both enftun_netlink_read_message and the helper functions to determine if it wants to do something with the message. The helper function(s) will need to #include the netlink headers, but that's ok.

src/config.c Outdated Show resolved Hide resolved
src/config.c Outdated Show resolved Hide resolved
src/icmp.c Outdated Show resolved Hide resolved
src/echo.h Outdated Show resolved Hide resolved
src/echo.h Outdated Show resolved Hide resolved
src/icmp.c Outdated Show resolved Hide resolved
src/ip.h Outdated Show resolved Hide resolved
src/heartbeat.c Outdated
send_heartbeat(struct enftun_heartbeat* heartbeat)
{
heartbeat->hb_inflight = true;
heartbeat->reply_recieved = false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

recieved -> received

src/heartbeat.c Outdated
}

heartbeat->hb_inflight = false;
enftun_log_info("Recieved ping reply\n");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make this enftun_log_debug.

src/heartbeat.h Outdated
{
struct enftun_channel* chan;

const struct in6_addr* addr;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of addr, call this source or source_addr.

Also, add a similar member,dest or dest_addr.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, change the dest address from all_routers/nodes to fe80::1.

src/heartbeat.h Outdated
enftun_heartbeat_init(struct enftun_heartbeat* heartbeat,
uv_loop_t* loop,
struct enftun_channel* chan,
const struct in6_addr* ipv6,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of ipv6, call this source or source_addr. Also, add the dest or dest_addr parameter.

src/heartbeat.c Outdated
if (!icmph)
goto pass;

heartbeat->reply_recieved = true;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to cancel the reply timer here and start the request timer again.

{
if (crb->status)
enftun_log_error("PING: failed to send heartbeat reply: %d\n",
crb->status);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than ignoring the error, we should have enftun reconnect.

@drbild
Copy link
Contributor

drbild commented Aug 16, 2019

@kathrynfejer Could you please rebase this on the latest master (with the just merged psock stuff).

Copy link
Contributor

@drbild drbild left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few comments, but this is looking pretty good.

Unfortunately, I don't have time to fully review the main reconnect logic today, to get this merged. Could you open three new PRs with the following subsets of changes --- those I can get merged today.

  1. Just the change to reserve 2 bytes in the enftun_packet_reset, rather than in enftun_crb_read.
  2. Just the addition of icmp6 echo request/reply creating and parsing (enftun_icmp6_echo_reply_pull and enftun_icmp6_echo_request in icmp.h/c).
  3. Just the addition of the heartbeat service (heartbeat.h,c and the associated parameters in config.h/c).

Those three I can approve and merge today.

Then a final PR with the changes to conn_state.c, ip.h,c and enftun.c. That one won't get merged until next week, when I can spend more time reviewing the logic in conn_state.

ctx->config.fwmark)) < 0)
if ((rc = enftun_conn_state_prepare(
&ctx->conn_state, &ctx->loop, trigger_reconnect, (void*) ctx,
ctx->config.fwmark, &ctx->tlschan, &ctx->ipv6, &ip6_self,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ip6_self has the correct address, has a misleading name.

Instead, let's create a new constant ip6_enf_router in ip.h,c with the same value and use it instead. Add a comment stating that enftun and the ENF router share the same IP since they split the duties of a traditional IPv6 router.

@@ -102,6 +120,10 @@ enftun_conn_state_prepare(struct enftun_conn_state* conn_state,
int
enftun_conn_state_close(struct enftun_conn_state* conn_state)
{
int rc = enftun_heartbeat_free(&conn_state->heartbeat);
if (0 != rc)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still need to close netlink, even if the hearbeat free fails. Just log a warning and continue.

@drbild
Copy link
Contributor

drbild commented Mar 24, 2021

Replaced by #137. The core of this PR was cherry-picked into it.

@drbild drbild closed this Mar 24, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Use ping to detect connection status
2 participants