Mailing List Archive

patch for IKE phase 1 rekey, QoS and IKE keepalives
please find below a patch that addresses three things in vpnc:

1) added IKE phase 1 rekeying. This essentially uses an older patch from this list (http://lists.unix-ag.uni-kl.de/pipermail/vpnc-devel/2010-March/003442.html). I did some changes to that patch so that it can be applied cleanly to the current source tree and made sure the corresponding phase1 rekey is actually added at the right place within vpnc_main_loop in tunip.c

2) added QoS handling for UDP encap. Make sure that TOS field of unencrypted packet is copied into TOS field of UDP encapsulated packet (outer header)

3) changed IKE keepalives behavior slightly. The original code created an 'IKE runt' on Cisco ASA logs for every keep-alive sent, tested w/ 9.4(1). According to RFC 3948, the keep-alive payload should be 0xFF which is actually reflected as v1 in the code. Probably needs more thought but the 'v1 keep-alive' made the runt log entries on the ASA go away.

This patch applies cleanly against the Ubuntu 14.04 vpnc source bundle vpnc_0.5.3r512-2ubuntu1.debian.tar.gz. I've tested this against production clusters of ASA headends and had no issues so far. YMMV.

Thanks,
-ralph


diff -Naur a/tunip.c b/tunip.c
--- a/tunip.c 2015-05-22 18:15:15.831453407 +0000
+++ b/tunip.c 2015-05-31 10:32:17.530808256 +0000
@@ -451,9 +451,15 @@
static void encap_udp_send_peer(struct sa_block *s, unsigned char *buf, unsigned int bufsize)
{
ssize_t sent;
+ struct ip *tip;
+ uint8_t tos;

buf += MAX_HEADER;

+ /* get the TOS value of the original frame */
+ tip = (struct ip *)buf;
+ tos = (bufsize < sizeof(struct ip)) ? 0 : tip->ip_tos;
+
s->ipsec.tx.buf = buf;
s->ipsec.tx.buflen = bufsize;

@@ -473,6 +479,15 @@
memset(s->ipsec.tx.buf, 0, 8);
}

+ /* set outer TOS header to the one of the original frame */
+ /* but only if it differs from the already set TOS */
+ if (tos != s->ipsec.current_udp_tos) {
+ if (setsockopt(s->esp_fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos))) {
+ logmsg(LOG_ERR, "udp setsockopt: %m");
+ }
+ s->ipsec.current_udp_tos = tos;
+ }
+
sent = send(s->esp_fd, s->ipsec.tx.buf, s->ipsec.tx.buflen, 0);
if (sent == -1) {
logmsg(LOG_ERR, "udp sendto: %m");
@@ -778,10 +793,32 @@
uint8_t *keepalive;
size_t keepalive_size;

+ /* the below code had keepalive_v2 for NATT_ACTIVE_RFC [FIXME]
+ * however, ASA 9.4(1) produced
+ * [IKEv1]: IKE Receiver: Runt ISAKMP packet discarded on Port 4500 from ip:port
+ * according to RFC 3948, the keepalive should be 0xFF
+ *
+ * 2.3. NAT-Keepalive Packet Format
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Source Port | Destination Port |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Length | Checksum |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0xFF |
+ * +-+-+-+-+-+-+-+-+
+ *
+ */
+
if (s->ipsec.natt_active_mode == NATT_ACTIVE_DRAFT_OLD) {
keepalive = keepalive_v1;
keepalive_size = sizeof(keepalive_v1);
- } else { /* active_mode is either RFC or CISCO_UDP */
+ } else if (s->ipsec.natt_active_mode == NATT_ACTIVE_RFC) {
+ keepalive = keepalive_v1;
+ keepalive_size = sizeof(keepalive_v1);
+ } else { /* active_mode is CISCO_UDP */
keepalive = keepalive_v2;
keepalive_size = sizeof(keepalive_v2);
}
@@ -857,6 +894,7 @@
keepalive_ike(s);
}
/* send nat keepalive packet */
+ DEBUG(3,printf("keepalive %d\n", (int)keepalive_size));
if (send(s->esp_fd, keepalive, keepalive_size, 0) == -1) {
logmsg(LOG_ERR, "keepalive sendto: %m");
}
@@ -876,12 +914,26 @@
}
}
}
- DEBUG(2,printf("lifetime status: %ld of %u seconds used, %u|%u of %u kbytes used\n",
+ DEBUG(2,printf("lifetime status: %ld of %u seconds used, %u|%u of %u kbytes used, ike: %ld of %u seconds used\n",
time(NULL) - s->ipsec.life.start,
s->ipsec.life.seconds,
s->ipsec.life.rx/1024,
s->ipsec.life.tx/1024,
- s->ipsec.life.kbytes));
+ s->ipsec.life.kbytes,
+ time(NULL) - s->ike.life.start,
+ s->ike.life.seconds));
+
+ if (timed_mode) {
+ time_t now = time(NULL);
+
+ /* start rekey at 80% of lifetime */
+ if ((now - s->ike.life.start) + ((s->ike.life.seconds*20)/100) > s->ike.life.seconds) {
+ DEBUG(3,printf("starting phase1 rekey at %d s\n", s->ike.life.seconds));
+ rekey_phase1(s);
+ }
+ }
+
+
} while ((presult == 0 || (presult == -1 && errno == EINTR)) && !do_kill);
if (presult == -1) {
logmsg(LOG_ERR, "select: %m");
diff -Naur a/tunip.h b/tunip.h
--- a/tunip.h 2015-05-26 08:06:51.561534499 +0000
+++ b/tunip.h 2015-05-31 10:32:28.744412231 +0000
@@ -120,6 +120,7 @@
struct ike_sa rx, tx;
struct encap_method *em;
uint16_t ip_id;
+ uint8_t current_udp_tos;
} ipsec;
};

diff -Naur a/vpnc.c b/vpnc.c
--- a/vpnc.c 2015-05-26 08:09:14.637042578 +0000
+++ b/vpnc.c 2015-05-31 10:32:51.511794145 +0000
@@ -750,6 +750,7 @@

gcry_create_nonce((uint8_t *) & msgid, sizeof(msgid));
sendrecv_phase2(s, NULL, ISAKMP_EXCHANGE_INFORMATIONAL, msgid, 1, 0, 0, 0, 0);
+ DEBUG(3, printf("sent IKE keepalive\n"));
}

static void send_dpd(struct sa_block *s, int isack, uint32_t seqno)
@@ -805,6 +806,7 @@
s->ike.dpd_sent = now;
send_dpd(s, 0, s->ike.dpd_seqno);
}
+ DEBUG(3, printf("sent DPD packet\n"));
}

static void send_delete_ipsec(struct sa_block *s)
@@ -832,7 +834,7 @@
}
}

-static void send_delete_isakmp(struct sa_block *s)
+static void send_delete_isakmp_cookie(struct sa_block *s, uint8_t *i_cookie, uint8_t *r_cookie)
{
DEBUGTOP(2, printf("S7.11 send isakmp termination message\n"));
{
@@ -847,15 +849,21 @@
d_isakmp->u.d.num_spi = 1;
d_isakmp->u.d.spi = xallocc(1 * sizeof(uint8_t *));
d_isakmp->u.d.spi[0] = xallocc(2 * ISAKMP_COOKIE_LENGTH);
- memcpy(d_isakmp->u.d.spi[0] + ISAKMP_COOKIE_LENGTH * 0, s->ike.i_cookie,
+ memcpy(d_isakmp->u.d.spi[0] + ISAKMP_COOKIE_LENGTH * 0, i_cookie,
ISAKMP_COOKIE_LENGTH);
- memcpy(d_isakmp->u.d.spi[0] + ISAKMP_COOKIE_LENGTH * 1, s->ike.r_cookie,
+ memcpy(d_isakmp->u.d.spi[0] + ISAKMP_COOKIE_LENGTH * 1, r_cookie,
ISAKMP_COOKIE_LENGTH);
sendrecv_phase2(s, d_isakmp, ISAKMP_EXCHANGE_INFORMATIONAL,
del_msgid, 1, NULL, 0, NULL, 0);
}
}

+static void send_delete_isakmp(struct sa_block *s)
+{
+ DEBUGTOP(2, printf("S7.11 send isakmp termination message\n"));
+ send_delete_isakmp_cookie(s, s->ike.i_cookie, s->ike.r_cookie);
+}
+
static void phase2_fatal(struct sa_block *s, const char *msg, int id)
{
struct isakmp_payload *pl;
@@ -1976,6 +1984,7 @@
default:
abort();
}
+ DEBUG(1, printf("NAT-T mode: %d\n", natt_draft));
if (natt_draft >= 2) {
s->ipsec.natt_active_mode = NATT_ACTIVE_RFC;
close(s->ike_fd);
@@ -2003,7 +2012,7 @@
}
}

-static void do_phase1_am_packet3(struct sa_block *s)
+static void do_phase1_am_packet3(struct sa_block *s, int re_key)
{
DEBUGTOP(2, printf("S4.5 AM_packet3\n"));
/* Send final phase 1 packet. */
@@ -2020,16 +2029,18 @@
p2->isakmp_version = ISAKMP_VERSION;
p2->exchange_type = ISAKMP_EXCHANGE_AGGRESSIVE;
/* XXX CERT Add id(?), cert and sig here in case of cert auth */
- p2->payload = new_isakmp_data_payload(ISAKMP_PAYLOAD_HASH,
+ p2->payload = pl = new_isakmp_data_payload(ISAKMP_PAYLOAD_HASH,
s->ike.returned_hash, s->ike.md_len);
- p2->payload->next = pl = new_isakmp_payload(ISAKMP_PAYLOAD_N);
- pl->u.n.doi = ISAKMP_DOI_IPSEC;
- pl->u.n.protocol = ISAKMP_IPSEC_PROTO_ISAKMP;
- pl->u.n.type = ISAKMP_N_IPSEC_INITIAL_CONTACT;
- pl->u.n.spi_length = 2 * ISAKMP_COOKIE_LENGTH;
- pl->u.n.spi = xallocc(2 * ISAKMP_COOKIE_LENGTH);
- memcpy(pl->u.n.spi + ISAKMP_COOKIE_LENGTH * 0, s->ike.i_cookie, ISAKMP_COOKIE_LENGTH);
- memcpy(pl->u.n.spi + ISAKMP_COOKIE_LENGTH * 1, s->ike.r_cookie, ISAKMP_COOKIE_LENGTH);
+ if (!re_key) {
+ p2->payload->next = pl = new_isakmp_payload(ISAKMP_PAYLOAD_N);
+ pl->u.n.doi = ISAKMP_DOI_IPSEC;
+ pl->u.n.protocol = ISAKMP_IPSEC_PROTO_ISAKMP;
+ pl->u.n.type = ISAKMP_N_IPSEC_INITIAL_CONTACT;
+ pl->u.n.spi_length = 2 * ISAKMP_COOKIE_LENGTH;
+ pl->u.n.spi = xallocc(2 * ISAKMP_COOKIE_LENGTH);
+ memcpy(pl->u.n.spi + ISAKMP_COOKIE_LENGTH * 0, s->ike.i_cookie, ISAKMP_COOKIE_LENGTH);
+ memcpy(pl->u.n.spi + ISAKMP_COOKIE_LENGTH * 1, s->ike.r_cookie, ISAKMP_COOKIE_LENGTH);
+ }

/* send PSK-hash if hybrid authentication is negotiated */
if (s->ike.auth_algo == IKE_AUTH_HybridInitRSA ||
@@ -2095,17 +2106,17 @@
s->ike.returned_hash = NULL;
}

-static void do_phase1_am(const char *key_id, const char *shared_key, struct sa_block *s)
+static void do_phase1_am(const char *key_id, const char *shared_key, struct sa_block *s, int re_key)
{
do_phase1_am_init(s);
do_phase1_am_packet1(s, key_id);
do_phase1_am_packet2(s, shared_key);
- do_phase1_am_packet3(s);
+ do_phase1_am_packet3(s, re_key);
do_phase1_am_cleanup(s);
}

static int do_phase2_notice_check(struct sa_block *s, struct isakmp_packet **r_p,
- const uint8_t * nonce, size_t nonce_size)
+ const uint8_t * nonce, size_t nonce_size, int life_only)
{
int reject = 0;
struct isakmp_packet *r;
@@ -2148,6 +2159,7 @@
lifetime_ipsec_process(s, r->payload->next->u.n.attributes);
else
DEBUG(2, printf("got unknown lifetime notice, ignoring..\n"));
+ if (life_only) return 0;
r_length = sendrecv(s, r_packet, sizeof(r_packet), NULL, 0, 0);
continue;
} else if (r->payload->next->u.n.type == ISAKMP_N_IPSEC_INITIAL_CONTACT) {
@@ -2196,7 +2208,7 @@
/* recv and check for notices */
if (r) free_isakmp_packet(r);
r = NULL;
- reject = do_phase2_notice_check(s, &r, NULL, 0);
+ reject = do_phase2_notice_check(s, &r, NULL, 0, 0);
if (reject == -1) {
if (r) free_isakmp_packet(r);
return 1;
@@ -2378,7 +2390,7 @@
ISAKMP_EXCHANGE_MODECFG_TRANSACTION,
r->message_id, 0, 0, 0, 0, 0);

- reject = do_phase2_notice_check(s, &r, NULL, 0);
+ reject = do_phase2_notice_check(s, &r, NULL, 0, 0);
if (reject == -1) {
free_isakmp_packet(r);
return 1;
@@ -2469,7 +2481,7 @@

DEBUGTOP(2, printf("S6.2 phase2_config receive modecfg\n"));
/* recv and check for notices */
- reject = do_phase2_notice_check(s, &r, NULL, 0);
+ reject = do_phase2_notice_check(s, &r, NULL, 0, 0);
if (reject == -1) {
if (r) free_isakmp_packet(r);
return 1;
@@ -2617,7 +2629,7 @@
msgid, 0, 0, 0, 0, 0);

DEBUGTOP(2, printf("S7.3 QM_packet2 validate type\n"));
- reject = do_phase2_notice_check(s, &r, nonce_i, sizeof(nonce_i)); /* FIXME: LEAK */
+ reject = do_phase2_notice_check(s, &r, nonce_i, sizeof(nonce_i), 0); /* FIXME: LEAK */

/* Check the transaction type & message ID are OK. */
if (reject == 0 && r->message_id != msgid)
@@ -3063,14 +3075,22 @@

void process_late_ike(struct sa_block *s, uint8_t *r_packet, ssize_t r_length)
{
- int reject;
+ int reject = 0;
struct isakmp_packet *r;
struct isakmp_payload *rp;

DEBUG(2,printf("got late ike packet: %zd bytes\n", r_length));
- /* we should ignore resent packets here.
- * unpack_verify_phase2 will fail to decode them probably */
- reject = unpack_verify_phase2(s, r_packet, r_length, &r, NULL, 0);
+
+ if (r_length > ISAKMP_PAYLOAD_O && r_packet[ISAKMP_EXCHANGE_TYPE_O] == ISAKMP_EXCHANGE_AGGRESSIVE) {
+ DEBUG(2,printf("can't respond to phase1 aggressive... terminating\n"));
+ do_kill = -1;
+ return;
+ } else {
+ /* we should ignore resent pakets here.
+ * unpack_verify_phase2 will fail to decode them probably */
+ DEBUG(2,printf("processing as phase2\n"));
+ reject = unpack_verify_phase2(s, r_packet, r_length, &r, NULL, 0);
+ }

/* just ignore broken stuff for now */
if (reject != 0) {
@@ -3101,6 +3121,7 @@
}

if (r->exchange_type == ISAKMP_EXCHANGE_INFORMATIONAL) {
+ DEBUG(3, printf("got ISAKMP_EXCHANGE_INFORMATIONAL\n"));
/* Search for notify payloads */
for (rp = r->payload->next; rp; rp = rp->next) {
if (rp->type != ISAKMP_PAYLOAD_N)
@@ -3134,6 +3155,14 @@
continue;
}
DEBUG(2, printf("got r-u-there ack\n"));
+ } else if (rp->u.n.type == ISAKMP_N_IPSEC_RESPONDER_LIFETIME) {
+ if (r->payload->next->u.n.protocol == ISAKMP_IPSEC_PROTO_ISAKMP)
+ lifetime_ike_process(s, r->payload->next->u.n.attributes);
+ else if (r->payload->next->u.n.protocol == ISAKMP_IPSEC_PROTO_IPSEC_ESP)
+ lifetime_ipsec_process(s, r->payload->next->u.n.attributes);
+ else
+ DEBUG(2, printf("got unknown lifetime notice, ignoring..\n"));
+
}
}
}
@@ -3202,6 +3231,9 @@
s->ipsec.encap_mode = IPSEC_ENCAP_TUNNEL;
s->ike.timeout = 1000; /* 1 second */

+ /* initialize last set TOS value in case UDP encap used */
+ s->ipsec.current_udp_tos = 0;
+
do_config(argc, argv);

DEBUG(1, printf("\nvpnc version " VERSION "\n"));
@@ -3220,7 +3252,7 @@
do_load_balance = 0;
do {
DEBUGTOP(2, printf("S4 do_phase1_am\n"));
- do_phase1_am(config[CONFIG_IPSEC_ID], config[CONFIG_IPSEC_SECRET], s);
+ do_phase1_am(config[CONFIG_IPSEC_ID], config[CONFIG_IPSEC_SECRET], s, 0);
DEBUGTOP(2, printf("S5 do_phase2_xauth\n"));
/* FIXME: Create and use a generic function in supp.[hc] */
if (s->ike.auth_algo >= IKE_AUTH_HybridInitRSA)
@@ -3250,3 +3282,26 @@

return 0;
}
+
+void rekey_phase1(struct sa_block *s) {
+ struct isakmp_packet *r;
+ uint8_t i_cookie[ISAKMP_COOKIE_LENGTH], r_cookie[ISAKMP_COOKIE_LENGTH];
+
+ memcpy(i_cookie, s->ike.i_cookie, ISAKMP_COOKIE_LENGTH);
+ memcpy(r_cookie, s->ike.r_cookie, ISAKMP_COOKIE_LENGTH);
+
+ do_phase1_am(config[CONFIG_IPSEC_ID], config[CONFIG_IPSEC_SECRET], s, 1);
+
+ (void)do_phase2_xauth(s);
+
+ DEBUG(3, printf("notice_checks\n"));
+ r_length = sendrecv(s, r_packet, sizeof(r_packet), NULL, 0, 0);
+ do_phase2_notice_check(s, &r, NULL, 0, 1);
+ if (r) free_isakmp_packet(r);
+ DEBUG(3, printf("notice_checks done\n"));
+
+ /* delete the old SA and read the response */
+ DEBUG(3, printf("delete old cookie\n"));
+ send_delete_isakmp_cookie(s, i_cookie, r_cookie);
+}
+
diff -Naur a/vpnc.h b/vpnc.h
--- a/vpnc.h 2012-01-10 21:18:18.000000000 +0000
+++ b/vpnc.h 2015-05-30 18:50:25.153239900 +0000
@@ -27,5 +27,7 @@
void keepalive_ike(struct sa_block *s);
void dpd_ike(struct sa_block *s);
void print_vid(const unsigned char *vid, uint16_t len);
+void rekey_phase1(struct sa_block *s);
+

#endif


_______________________________________________
vpnc-devel mailing list
vpnc-devel@unix-ag.uni-kl.de
https://lists.unix-ag.uni-kl.de/mailman/listinfo/vpnc-devel
http://www.unix-ag.uni-kl.de/~massar/vpnc/