Package: vpnc Version: 0.3.3+SVN20051028-3 Severity: important Architecture: i386 Debian Release: Stable (etch)
vpnc doesn't correctly implement keepalives. The code ignores the user supplied keepalive time (seconds) and uses an internal poll timeout (which is 2 seconds). As a result, keep alives are sent every 2 seconds.
I'm attaching a patch (against the _ORIGINAL_ tunip.c file), which also incorporates the debian specific patch to the same file. The fix is quick and easy and updates the keepalive variable with the current time after every keep alive packet is sent, thus refreshing the next keep alive cycle to the user supplied value.
The exact two lines that need to change are: // Save the new timestamp keepalive = rekey = time(NULL); (this is in the vpnc_main_loop function in tunip.c). Please also forward this patch upstream to the vpnc codebase. Thanks. Narayanan.
*** tunip.c 2007-04-12 01:53:47.000000000 -0400 --- tunip.c.orig 2007-04-12 01:53:47.000000000 -0400 *************** static int tun_send_ip(struct encap_meth *** 335,348 **** return 1; } ! static int encap_esp_new(struct encap_method *encap, int encap_fd) { #ifdef IP_HDRINCL int hincl = 1; #endif ! encap->fd = encap_fd; #ifdef IP_HDRINCL if (setsockopt(encap->fd, IPPROTO_IP, IP_HDRINCL, &hincl, sizeof(hincl)) == -1) { --- 335,352 ---- return 1; } ! static int encap_esp_new(struct encap_method *encap, unsigned char proto) { #ifdef IP_HDRINCL int hincl = 1; #endif ! encap->fd = socket(PF_INET, SOCK_RAW, proto); + if (encap->fd == -1) { + perror("socket(SOCK_RAW)"); + return -1; + } #ifdef IP_HDRINCL if (setsockopt(encap->fd, IPPROTO_IP, IP_HDRINCL, &hincl, sizeof(hincl)) == -1) { *************** int encap_esp_recv_peer(struct encap_met *** 764,779 **** } static uint8_t volatile do_kill; ! #define VPNC_POLL_TIMEOUT 2000 ! ! static int vpnc_main_loop(struct peer_desc *peer, struct encap_method *meth, int tun_fd, const char *pidfile) { struct pollfd pollfds[2]; - time_t keepalive, rekey, now; - int rv = 1; - keepalive = rekey = time(NULL); pollfds[0].fd = tun_fd; pollfds[0].events = POLLIN; pollfds[1].fd = encap_get_fd(meth); --- 768,782 ---- } static uint8_t volatile do_kill; + static uint8_t *kill_packet; + static size_t kill_packet_size; + static struct sockaddr *kill_dest; ! static void vpnc_main_loop(struct peer_desc *peer, struct encap_method *meth, int tun_fd, const char *pidfile) { + int sock; struct pollfd pollfds[2]; pollfds[0].fd = tun_fd; pollfds[0].events = POLLIN; pollfds[1].fd = encap_get_fd(meth); *************** static int vpnc_main_loop(struct peer_de *** 783,810 **** int presult; do { ! presult = poll(pollfds, sizeof(pollfds) / sizeof(pollfds[0]), VPNC_POLL_TIMEOUT); } while (presult == -1 && errno == EINTR && !do_kill); if (presult == -1) { syslog(LOG_ERR, "poll: %m"); continue; } - now = time(NULL); - if (opt_nat_keepalive && (now - keepalive) >= opt_nat_keepalive) { - const char *payload = "\xff"; - sendto(meth->fd, payload, 1, 0, - (struct sockaddr *)&peer->remote_sa->dest, - sizeof(peer->remote_sa->dest)); - // Save the new timestamp - keepalive = rekey = time(NULL); - } - - if (opt_rekey_interval && now-rekey >= opt_rekey_interval) { - rv = 0; - do_kill = 255; - } - if (pollfds[0].revents & POLLIN) { int pack; --- 786,798 ---- int presult; do { ! presult = poll(pollfds, sizeof(pollfds) / sizeof(pollfds[0]), -1); } while (presult == -1 && errno == EINTR && !do_kill); if (presult == -1) { syslog(LOG_ERR, "poll: %m"); continue; } if (pollfds[0].revents & POLLIN) { int pack; *************** static int vpnc_main_loop(struct peer_de *** 877,894 **** } } ! if (rv != 0) { ! tun_close(oursa->tun_fd, oursa->tun_name); ! if (pidfile) ! unlink(pidfile); /* ignore errors */ ! syslog(LOG_NOTICE, "terminated"); ! } ! return rv; } static void killit(int signum) { do_kill = signum; /* unused */ } static void write_pidfile(const char *pidfile) --- 865,886 ---- } } ! sock = socket(AF_INET, SOCK_DGRAM, 0); ! if (sock >= 0) { ! sendto(sock, kill_packet, kill_packet_size, 0, ! kill_dest, sizeof(struct sockaddr_in)); ! close(sock); ! } ! tun_close(oursa->tun_fd, oursa->tun_name); ! if (pidfile) ! unlink(pidfile); /* ignore errors */ ! syslog(LOG_NOTICE, "terminated"); } static void killit(int signum) { do_kill = signum; /* unused */ + do_kill = 1; } static void write_pidfile(const char *pidfile) *************** static void write_pidfile(const char *pi *** 908,914 **** fclose(pf); } ! int vpnc_doit(unsigned long tous_spi, const unsigned char *tous_key, struct sockaddr_in *tous_dest, --- 900,906 ---- fclose(pf); } ! void vpnc_doit(unsigned long tous_spi, const unsigned char *tous_key, struct sockaddr_in *tous_dest, *************** vpnc_doit(unsigned long tous_spi, *** 916,922 **** const unsigned char *tothem_key, struct sockaddr_in *tothem_dest, int tun_fd, int md_algo, int cry_algo, ! uint16_t encap_mode, int encap_fd, const char *pidfile) { struct encap_method meth; --- 908,916 ---- const unsigned char *tothem_key, struct sockaddr_in *tothem_dest, int tun_fd, int md_algo, int cry_algo, ! uint8_t * kill_packet_p, size_t kill_packet_size_p, ! struct sockaddr *kill_dest_p, ! uint16_t encap_mode, int udp_fd, const char *pidfile) { struct encap_method meth; *************** vpnc_doit(unsigned long tous_spi, *** 926,938 **** switch (encap_mode) { case IPSEC_ENCAP_TUNNEL: ! if (encap_esp_new(&meth, encap_fd) == -1) exit(1); - opt_nat_keepalive = 0; break; case IPSEC_ENCAP_UDP_TUNNEL: case IPSEC_ENCAP_UDP_TUNNEL_OLD: ! if (encap_udp_new(&meth, encap_fd) == -1) exit(1); break; default: --- 920,931 ---- switch (encap_mode) { case IPSEC_ENCAP_TUNNEL: ! if (encap_esp_new(&meth, IPPROTO_ESP) == -1) exit(1); break; case IPSEC_ENCAP_UDP_TUNNEL: case IPSEC_ENCAP_UDP_TUNNEL_OLD: ! if (encap_udp_new(&meth, udp_fd) == -1) exit(1); break; default: *************** vpnc_doit(unsigned long tous_spi, *** 993,998 **** --- 986,996 ---- vpnpeer.local_sa = &tous_sa; vpnpeer.remote_sa = &tothem_sa; + do_kill = 0; + kill_packet = kill_packet_p; + kill_packet_size = kill_packet_size_p; + kill_dest = kill_dest_p; + signal(SIGHUP, killit); signal(SIGINT, killit); signal(SIGTERM, killit); *************** vpnc_doit(unsigned long tous_spi, *** 1004,1010 **** chdir("/"); setsid(); ! if (do_kill == 0 && !opt_nd) { /* do_kill != 0 -> rekeying */ pid_t pid; if ((pid = fork()) < 0) { fprintf(stderr, "Warning, could not fork the child process!\n"); --- 1002,1008 ---- chdir("/"); setsid(); ! if (!opt_nd) { pid_t pid; if ((pid = fork()) < 0) { fprintf(stderr, "Warning, could not fork the child process!\n"); *************** vpnc_doit(unsigned long tous_spi, *** 1023,1029 **** openlog("vpnc", LOG_PID, LOG_DAEMON); } ! do_kill = 0; ! ! return vpnc_main_loop(&vpnpeer, &meth, tun_fd, (!opt_nd) ? pidfile : NULL); } --- 1021,1025 ---- openlog("vpnc", LOG_PID, LOG_DAEMON); } ! vpnc_main_loop(&vpnpeer, &meth, tun_fd, (!opt_nd) ? pidfile : NULL); }