Skip to content

Commit

Permalink
Implement ESP keepalive and periodic reconnect attempt
Browse files Browse the repository at this point in the history
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
  • Loading branch information
David Woodhouse authored and David Woodhouse committed Feb 2, 2015
1 parent 627e557 commit 2c70008
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 18 deletions.
80 changes: 66 additions & 14 deletions esp.c
Expand Up @@ -165,6 +165,19 @@ static int esp_send_probes(struct openconnect_info *vpninfo)
struct pkt *pkt;
int pktlen;

if (vpninfo->dtls_fd == -1) {
int fd = udp_connect(vpninfo);
if (fd < 0)
return fd;

/* We are not connected until we get an ESP packet back */
vpninfo->dtls_state = DTLS_SLEEPING;
vpninfo->dtls_fd = fd;
monitor_fd_new(vpninfo, dtls);
monitor_read_fd(vpninfo, dtls);
monitor_except_fd(vpninfo, dtls);
}

pkt = malloc(sizeof(*pkt) + 1 + vpninfo->pkt_trailer);
if (!pkt)
return -ENOMEM;
Expand All @@ -180,33 +193,28 @@ static int esp_send_probes(struct openconnect_info *vpninfo)
send(vpninfo->dtls_fd, (void *)&pkt->esp, pktlen, 0);

free(pkt);
time(&vpninfo->new_dtls_started);

vpninfo->dtls_times.last_tx = time(&vpninfo->new_dtls_started);

return 0;
};

int esp_setup(struct openconnect_info *vpninfo, int dtls_attempt_period)
{
int fd;

if (vpninfo->dtls_state == DTLS_DISABLED ||
vpninfo->dtls_state == DTLS_NOSECRET)
return -EINVAL;

fd = udp_connect(vpninfo);
if (fd < 0)
return fd;
if (vpninfo->esp_ssl_fallback)
vpninfo->dtls_times.dpd = vpninfo->esp_ssl_fallback;
else
vpninfo->dtls_times.dpd = dtls_attempt_period;

vpninfo->dtls_attempt_period = dtls_attempt_period;

print_esp_keys(vpninfo, _("incoming"), &vpninfo->esp_in[vpninfo->current_esp_in]);
print_esp_keys(vpninfo, _("outgoing"), &vpninfo->esp_out);

/* We are not connected until we get an ESP packet back */
vpninfo->dtls_state = DTLS_SLEEPING;
vpninfo->dtls_fd = fd;
monitor_fd_new(vpninfo, dtls);
monitor_read_fd(vpninfo, dtls);
monitor_except_fd(vpninfo, dtls);

esp_send_probes(vpninfo);

return 0;
Expand All @@ -220,6 +228,19 @@ int esp_mainloop(struct openconnect_info *vpninfo, int *timeout)
int work_done = 0;
int ret;

if (vpninfo->dtls_state == DTLS_SLEEPING) {
int when = vpninfo->new_dtls_started + vpninfo->dtls_attempt_period - time(NULL);
if (when <= 0) {
vpn_progress(vpninfo, PRG_DEBUG, _("Send ESP probes\n"));
esp_send_probes(vpninfo);
when = vpninfo->dtls_attempt_period;
}
if (*timeout > when * 1000)
*timeout = when * 1000;
}
if (vpninfo->dtls_fd == -1)
return 0;

while (1) {
int len = vpninfo->ip_info.mtu + vpninfo->pkt_trailer;
struct pkt *pkt;
Expand Down Expand Up @@ -279,11 +300,13 @@ int esp_mainloop(struct openconnect_info *vpninfo, int *timeout)
}
/* XXX: Actually check the padding bytes too. */
pkt->len = len - 2 - pkt->data[len - 2];
vpninfo->dtls_times.last_rx = time(NULL);

if (pkt->len == 1 && pkt->data[0] == 0) {
if (vpninfo->dtls_state == DTLS_SLEEPING) {
vpn_progress(vpninfo, PRG_INFO,
_("ESP session established with server\n"));
queue_esp_control(vpninfo, 1);
vpninfo->dtls_state = DTLS_CONNECTING;
}
continue;
Expand Down Expand Up @@ -317,6 +340,31 @@ int esp_mainloop(struct openconnect_info *vpninfo, int *timeout)
if (vpninfo->dtls_state != DTLS_CONNECTED)
return 0;

switch (keepalive_action(&vpninfo->dtls_times, timeout)) {
case KA_REKEY:
vpn_progress(vpninfo, PRG_ERR, _("Rekey not implemented for ESP\n"));
break;

case KA_DPD_DEAD:
vpn_progress(vpninfo, PRG_ERR, _("ESP detected dead peer\n"));
queue_esp_control(vpninfo, 0);
esp_close(vpninfo);
esp_send_probes(vpninfo);
return 1;

case KA_DPD:
vpn_progress(vpninfo, PRG_DEBUG, _("Send ESP probes for DPD\n"));
esp_send_probes(vpninfo);
work_done = 1;
break;

case KA_KEEPALIVE:
vpn_progress(vpninfo, PRG_ERR, _("Keepalive not implemented for ESP\n"));
break;

case KA_NONE:
break;
}
unmonitor_write_fd(vpninfo, dtls);
while ((this = dequeue_packet(&vpninfo->outgoing_queue))) {
int len;
Expand All @@ -337,9 +385,12 @@ int esp_mainloop(struct openconnect_info *vpninfo, int *timeout)
_("Failed to send ESP packet: %s\n"),
strerror(errno));
}
} else
} else {
vpninfo->dtls_times.last_tx = time(NULL);

vpn_progress(vpninfo, PRG_TRACE, _("Sent ESP packet of %d bytes\n"),
len);
}
} else {
/* XXX: Fall back to TCP transport? */
}
Expand All @@ -360,6 +411,7 @@ void esp_close(struct openconnect_info *vpninfo)
unmonitor_write_fd(vpninfo, dtls);
unmonitor_except_fd(vpninfo, dtls);
}
vpninfo->dtls_state = DTLS_SLEEPING;
}

void esp_shutdown(struct openconnect_info *vpninfo)
Expand Down
27 changes: 23 additions & 4 deletions oncp.c
Expand Up @@ -962,6 +962,18 @@ static const struct pkt esp_enable_pkt = {
.len = 13
};

int queue_esp_control(struct openconnect_info *vpninfo, int enable)
{
struct pkt *new = malloc(sizeof(*new) + 13);
if (!new)
return -ENOMEM;

memcpy(new, &esp_enable_pkt, sizeof(*new) + 13);
new->data[12] = enable;
queue_packet(&vpninfo->oncp_control_queue, new);
return 0;
}

static int check_kmp_header(struct openconnect_info *vpninfo, unsigned char *bytes, int pktlen)
{
if (pktlen < 20 || memcmp(bytes, kmp_head, sizeof(kmp_head)) ||
Expand Down Expand Up @@ -1641,6 +1653,17 @@ int oncp_mainloop(struct openconnect_info *vpninfo, int *timeout)
if (vpninfo->dtls_state == DTLS_CONNECTING)
vpninfo->dtls_state = DTLS_CONNECTED;
} else {
/* Only set the ESP state to connected and actually start
sending packets on it once the enable message has been
*sent* over the TCP channel. */
if (vpninfo->dtls_state == DTLS_CONNECTING &&
vpninfo->current_ssl_pkt->len == 13 &&
load_be16(&vpninfo->current_ssl_pkt->oncp.hdr[8]) == 0x12f &&
vpninfo->current_ssl_pkt->data[12]) {
vpn_progress(vpninfo, PRG_TRACE,
_("Sent ESP enable control packet\n"));
vpninfo->dtls_state = DTLS_CONNECTED;
}
free(vpninfo->current_ssl_pkt);
}
vpninfo->current_ssl_pkt = NULL;
Expand Down Expand Up @@ -1720,10 +1743,6 @@ int oncp_mainloop(struct openconnect_info *vpninfo, int *timeout)
if (vpninfo->current_ssl_pkt)
goto handle_outgoing;

if (vpninfo->dtls_state == DTLS_CONNECTING) {
vpninfo->current_ssl_pkt = (struct pkt *)&esp_enable_pkt;
goto handle_outgoing;
}
/* Service outgoing packet queue, if no DTLS */
while (vpninfo->dtls_state != DTLS_CONNECTED &&
(vpninfo->current_ssl_pkt = dequeue_packet(&vpninfo->outgoing_queue))) {
Expand Down
1 change: 1 addition & 0 deletions openconnect-internal.h
Expand Up @@ -764,6 +764,7 @@ int decompress_and_queue_packet(struct openconnect_info *vpninfo,
int compress_packet(struct openconnect_info *vpninfo, int compr_type, struct pkt *this);

/* oncp.c */
int queue_esp_control(struct openconnect_info *vpninfo, int enable);
int oncp_obtain_cookie(struct openconnect_info *vpninfo);
int oncp_connect(struct openconnect_info *vpninfo);
int oncp_mainloop(struct openconnect_info *vpninfo, int *timeout);
Expand Down

0 comments on commit 2c70008

Please sign in to comment.