1 /* $OpenBSD: dpd.c,v 1.16 2006/07/24 11:45:44 ho Exp $ */ 2 3 /* 4 * Copyright (c) 2004 H�kan Olsson. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27 #include <sys/types.h> 28 #include <stdlib.h> 29 #include <string.h> 30 31 #include "sysdep.h" 32 33 #include "conf.h" 34 #include "dpd.h" 35 #include "exchange.h" 36 #include "hash.h" 37 #include "ipsec.h" 38 #include "isakmp_fld.h" 39 #include "log.h" 40 #include "message.h" 41 #include "pf_key_v2.h" 42 #include "sa.h" 43 #include "timer.h" 44 #include "transport.h" 45 #include "util.h" 46 47 /* From RFC 3706. */ 48 #define DPD_MAJOR 0x01 49 #define DPD_MINOR 0x00 50 #define DPD_SEQNO_SZ 4 51 52 static const u_int8_t dpd_vendor_id[] = { 53 0xAF, 0xCA, 0xD7, 0x13, 0x68, 0xA1, 0xF1, /* RFC 3706 */ 54 0xC9, 0x6B, 0x86, 0x96, 0xFC, 0x77, 0x57, 55 DPD_MAJOR, 56 DPD_MINOR 57 }; 58 59 #define DPD_RETRANS_MAX 5 /* max number of retries. */ 60 #define DPD_RETRANS_WAIT 5 /* seconds between retries. */ 61 62 /* DPD Timer State */ 63 enum dpd_tstate { DPD_TIMER_NORMAL, DPD_TIMER_CHECK }; 64 65 static void dpd_check_event(void *); 66 static void dpd_event(void *); 67 static u_int32_t dpd_timer_interval(u_int32_t); 68 static void dpd_timer_reset(struct sa *, u_int32_t, enum dpd_tstate); 69 70 /* Add the DPD VENDOR ID payload. */ 71 int 72 dpd_add_vendor_payload(struct message *msg) 73 { 74 u_int8_t *buf; 75 size_t buflen = sizeof dpd_vendor_id + ISAKMP_GEN_SZ; 76 77 buf = malloc(buflen); 78 if (!buf) { 79 log_error("dpd_add_vendor_payload: malloc(%lu) failed", 80 (unsigned long)buflen); 81 return -1; 82 } 83 84 SET_ISAKMP_GEN_LENGTH(buf, buflen); 85 memcpy(buf + ISAKMP_VENDOR_ID_OFF, dpd_vendor_id, 86 sizeof dpd_vendor_id); 87 if (message_add_payload(msg, ISAKMP_PAYLOAD_VENDOR, buf, buflen, 1)) { 88 free(buf); 89 return -1; 90 } 91 92 return 0; 93 } 94 95 /* 96 * Check an incoming message for DPD capability markers. 97 */ 98 void 99 dpd_check_vendor_payload(struct message *msg, struct payload *p) 100 { 101 u_int8_t *pbuf = p->p; 102 size_t vlen; 103 104 /* Already checked? */ 105 if (msg->exchange->flags & EXCHANGE_FLAG_DPD_CAP_PEER) { 106 /* Just mark it as handled and return. */ 107 p->flags |= PL_MARK; 108 return; 109 } 110 111 vlen = GET_ISAKMP_GEN_LENGTH(pbuf) - ISAKMP_GEN_SZ; 112 if (vlen != sizeof dpd_vendor_id) { 113 LOG_DBG((LOG_EXCHANGE, 90, 114 "dpd_check_vendor_payload: bad size %lu != %lu", 115 (unsigned long)vlen, (unsigned long)sizeof dpd_vendor_id)); 116 return; 117 } 118 119 if (memcmp(dpd_vendor_id, pbuf + ISAKMP_GEN_SZ, vlen) == 0) { 120 /* This peer is DPD capable. */ 121 if (msg->isakmp_sa) { 122 msg->exchange->flags |= EXCHANGE_FLAG_DPD_CAP_PEER; 123 LOG_DBG((LOG_EXCHANGE, 10, "dpd_check_vendor_payload: " 124 "DPD capable peer detected")); 125 } 126 p->flags |= PL_MARK; 127 } 128 } 129 130 /* 131 * Arm the DPD timer 132 */ 133 void 134 dpd_start(struct sa *isakmp_sa) 135 { 136 if (dpd_timer_interval(0) != 0) { 137 LOG_DBG((LOG_EXCHANGE, 10, "dpd_enable: enabling")); 138 isakmp_sa->flags |= SA_FLAG_DPD; 139 dpd_timer_reset(isakmp_sa, 0, DPD_TIMER_NORMAL); 140 } 141 } 142 143 /* 144 * All incoming DPD Notify messages enter here. Message has been validated. 145 */ 146 void 147 dpd_handle_notify(struct message *msg, struct payload *p) 148 { 149 struct sa *isakmp_sa = msg->isakmp_sa; 150 u_int16_t notify = GET_ISAKMP_NOTIFY_MSG_TYPE(p->p); 151 u_int32_t p_seq; 152 153 /* Extract the sequence number. */ 154 memcpy(&p_seq, p->p + ISAKMP_NOTIFY_SPI_OFF + ISAKMP_HDR_COOKIES_LEN, 155 sizeof p_seq); 156 p_seq = ntohl(p_seq); 157 158 LOG_DBG((LOG_MESSAGE, 40, "dpd_handle_notify: got %s seq %u", 159 constant_name(isakmp_notify_cst, notify), p_seq)); 160 161 switch (notify) { 162 case ISAKMP_NOTIFY_STATUS_DPD_R_U_THERE: 163 /* The other peer wants to know we're alive. */ 164 if (p_seq < isakmp_sa->dpd_rseq || 165 (p_seq == isakmp_sa->dpd_rseq && 166 ++isakmp_sa->dpd_rdupcount >= DPD_RETRANS_MAX)) { 167 log_print("dpd_handle_notify: bad R_U_THERE seqno " 168 "%u <= %u", p_seq, isakmp_sa->dpd_rseq); 169 return; 170 } 171 if (isakmp_sa->dpd_rseq != p_seq) { 172 isakmp_sa->dpd_rdupcount = 0; 173 isakmp_sa->dpd_rseq = p_seq; 174 } 175 message_send_dpd_notify(isakmp_sa, 176 ISAKMP_NOTIFY_STATUS_DPD_R_U_THERE_ACK, p_seq); 177 break; 178 179 case ISAKMP_NOTIFY_STATUS_DPD_R_U_THERE_ACK: 180 /* This should be a response to a R_U_THERE we've sent. */ 181 if (isakmp_sa->dpd_seq != p_seq) { 182 log_print("dpd_handle_notify: got bad ACK seqno %u, " 183 "expected %u", p_seq, isakmp_sa->dpd_seq); 184 /* XXX Give up? Retry? */ 185 return; 186 } 187 break; 188 default: 189 break; 190 } 191 192 /* Mark handled. */ 193 p->flags |= PL_MARK; 194 195 /* The other peer is alive, so we can safely wait a while longer. */ 196 if (isakmp_sa->flags & SA_FLAG_DPD) 197 dpd_timer_reset(isakmp_sa, 0, DPD_TIMER_NORMAL); 198 } 199 200 /* Calculate the time until next DPD exchange. */ 201 static u_int32_t 202 dpd_timer_interval(u_int32_t offset) 203 { 204 int32_t v = 0; 205 206 #ifdef notyet 207 v = ...; /* XXX Per-peer specified DPD intervals? */ 208 #endif 209 if (!v) 210 v = conf_get_num("General", "DPD-check-interval", 0); 211 if (v < 1) 212 return 0; /* DPD-Check-Interval < 1 means disable DPD */ 213 214 v -= offset; 215 return v < 1 ? 1 : v; 216 } 217 218 static void 219 dpd_timer_reset(struct sa *sa, u_int32_t time_passed, enum dpd_tstate mode) 220 { 221 struct timeval tv; 222 223 if (sa->dpd_event) 224 timer_remove_event(sa->dpd_event); 225 226 gettimeofday(&tv, 0); 227 switch (mode) { 228 case DPD_TIMER_NORMAL: 229 sa->dpd_failcount = 0; 230 tv.tv_sec += dpd_timer_interval(time_passed); 231 sa->dpd_event = timer_add_event("dpd_event", dpd_event, sa, 232 &tv); 233 break; 234 case DPD_TIMER_CHECK: 235 tv.tv_sec += DPD_RETRANS_WAIT; 236 sa->dpd_event = timer_add_event("dpd_check_event", 237 dpd_check_event, sa, &tv); 238 break; 239 default: 240 break; 241 } 242 if (!sa->dpd_event) 243 log_print("dpd_timer_reset: timer_add_event failed"); 244 } 245 246 /* Helper function for dpd_exchange_finalization(). */ 247 static int 248 dpd_find_sa(struct sa *sa, void *v_sa) 249 { 250 struct sa *isakmp_sa = v_sa; 251 252 if (!isakmp_sa->id_i || !isakmp_sa->id_r) 253 return 0; 254 return (sa->phase == 2 && (sa->flags & SA_FLAG_READY) && 255 memcmp(sa->id_i, isakmp_sa->id_i, sa->id_i_len) == 0 && 256 memcmp(sa->id_r, isakmp_sa->id_r, sa->id_r_len) == 0); 257 } 258 259 struct dpd_args { 260 struct sa *isakmp_sa; 261 u_int32_t interval; 262 }; 263 264 /* Helper function for dpd_event(). */ 265 static int 266 dpd_check_time(struct sa *sa, void *v_arg) 267 { 268 struct dpd_args *args = v_arg; 269 struct sockaddr *dst; 270 struct proto *proto; 271 struct sa_kinfo *ksa; 272 struct timeval tv; 273 274 if (sa->phase == 1 || (args->isakmp_sa->flags & SA_FLAG_DPD) == 0 || 275 dpd_find_sa(sa, args->isakmp_sa) == 0) 276 return 0; 277 278 proto = TAILQ_FIRST(&sa->protos); 279 if (!proto || !proto->data) 280 return 0; 281 sa->transport->vtbl->get_src(sa->transport, &dst); 282 283 gettimeofday(&tv, 0); 284 ksa = pf_key_v2_get_kernel_sa(proto->spi[1], proto->spi_sz[1], 285 proto->proto, dst); 286 287 if (!ksa || !ksa->last_used) 288 return 0; 289 290 LOG_DBG((LOG_MESSAGE, 80, "dpd_check_time: " 291 "SA %p last use %u second(s) ago", sa, 292 (u_int32_t)(tv.tv_sec - ksa->last_used))); 293 294 if ((u_int32_t)(tv.tv_sec - ksa->last_used) < args->interval) { 295 args->interval = (u_int32_t)(tv.tv_sec - ksa->last_used); 296 return 1; 297 } 298 return 0; 299 } 300 301 /* Called by the timer. */ 302 static void 303 dpd_event(void *v_sa) 304 { 305 struct sa *isakmp_sa = v_sa; 306 struct dpd_args args; 307 struct sockaddr *dst; 308 char *addr; 309 310 isakmp_sa->dpd_event = 0; 311 312 /* Check if there's been any incoming SA activity since last time. */ 313 args.isakmp_sa = isakmp_sa; 314 args.interval = dpd_timer_interval(0); 315 if (sa_find(dpd_check_time, &args)) { 316 if (args.interval > dpd_timer_interval(0)) 317 args.interval = 0; 318 dpd_timer_reset(isakmp_sa, args.interval, DPD_TIMER_NORMAL); 319 return; 320 } 321 322 /* No activity seen, do a DPD exchange. */ 323 if (isakmp_sa->dpd_seq == 0) { 324 /* 325 * RFC 3706: first seq# should be random, with MSB zero, 326 * otherwise we just increment it. 327 */ 328 getrandom((u_int8_t *)&isakmp_sa->dpd_seq, 329 sizeof isakmp_sa->dpd_seq); 330 isakmp_sa->dpd_seq &= 0x7FFF; 331 } else 332 isakmp_sa->dpd_seq++; 333 334 isakmp_sa->transport->vtbl->get_dst(isakmp_sa->transport, &dst); 335 if (sockaddr2text(dst, &addr, 0) == -1) 336 addr = 0; 337 LOG_DBG((LOG_MESSAGE, 30, "dpd_event: sending R_U_THERE to %s seq %u", 338 addr ? addr : "<unknown>", isakmp_sa->dpd_seq)); 339 if (addr) 340 free(addr); 341 message_send_dpd_notify(isakmp_sa, ISAKMP_NOTIFY_STATUS_DPD_R_U_THERE, 342 isakmp_sa->dpd_seq); 343 344 /* And set the short timer. */ 345 dpd_timer_reset(isakmp_sa, 0, DPD_TIMER_CHECK); 346 } 347 348 /* 349 * Called by the timer. If this function is called, it means we did not 350 * received any R_U_THERE_ACK confirmation from the other peer. 351 */ 352 static void 353 dpd_check_event(void *v_sa) 354 { 355 struct sa *isakmp_sa = v_sa; 356 struct sa *sa; 357 358 isakmp_sa->dpd_event = 0; 359 360 if (++isakmp_sa->dpd_failcount < DPD_RETRANS_MAX) { 361 LOG_DBG((LOG_MESSAGE, 10, "dpd_check_event: " 362 "peer not responding, retry %u of %u", 363 isakmp_sa->dpd_failcount, DPD_RETRANS_MAX)); 364 message_send_dpd_notify(isakmp_sa, 365 ISAKMP_NOTIFY_STATUS_DPD_R_U_THERE, isakmp_sa->dpd_seq); 366 dpd_timer_reset(isakmp_sa, 0, DPD_TIMER_CHECK); 367 return; 368 } 369 370 /* 371 * Peer is considered dead. Delete all SAs created under isakmp_sa. 372 */ 373 LOG_DBG((LOG_MESSAGE, 10, "dpd_check_event: peer is dead, " 374 "deleting all SAs connected to SA %p", isakmp_sa)); 375 while ((sa = sa_find(dpd_find_sa, isakmp_sa)) != 0) { 376 LOG_DBG((LOG_MESSAGE, 30, "dpd_check_event: deleting SA %p", 377 sa)); 378 sa_delete(sa, 0); 379 } 380 LOG_DBG((LOG_MESSAGE, 30, "dpd_check_event: deleting ISAKMP SA %p", 381 isakmp_sa)); 382 sa_delete(isakmp_sa, 0); 383 } 384