When new ISAKMP is required, allow incoming reverse connection to take From: Timo Teras over pending phase1:s. Useful when the other party is firewalled or NATted. --- src/racoon/admin.c | 12 ++++++++++++ src/racoon/evt.c | 13 +++++++++++++ src/racoon/evt.h | 3 +++ src/racoon/handler.c | 28 +++++++++++++++++++++------- src/racoon/isakmp.c | 39 ++++++++++++++++++++++++++++++++++----- 5 files changed, 83 insertions(+), 12 deletions(-) diff --git a/src/racoon/admin.c b/src/racoon/admin.c index b67e545..710c9bf 100644 --- a/src/racoon/admin.c +++ b/src/racoon/admin.c @@ -414,11 +414,23 @@ admin_process(so2, combuf) struct sockaddr *dst; struct sockaddr *src; char *name = NULL; + char *loc, *rem; ndx = (struct admin_com_indexes *) ((caddr_t)com + sizeof(*com)); src = (struct sockaddr *) &ndx->src; dst = (struct sockaddr *) &ndx->dst; + loc = racoon_strdup(saddr2str(src)); + rem = racoon_strdup(saddr2str(dst)); + STRDUP_FATAL(loc); + STRDUP_FATAL(rem); + + plog(LLV_INFO, LOCATION, NULL, + "admin establish-sa %x %s %s\n", + com->ac_proto, loc, rem); + racoon_free(loc); + racoon_free(rem); + if (com->ac_cmd == ADMIN_ESTABLISH_SA && com->ac_len > sizeof(*com) + sizeof(*ndx)) name = (char *) ((caddr_t) ndx + sizeof(*ndx)); diff --git a/src/racoon/evt.c b/src/racoon/evt.c index 4ce1334..000c1f8 100644 --- a/src/racoon/evt.c +++ b/src/racoon/evt.c @@ -396,4 +396,17 @@ evt_list_cleanup(list) evt_unsubscribe(LIST_FIRST(list)); } +void +evt_list_move(from, to) + struct evt_listener_list *from, *to; +{ + struct evt_listener *l; + + while (!LIST_EMPTY(from)) { + l = LIST_FIRST(from); + LIST_REMOVE(l, ll_chain); + LIST_INSERT_HEAD(to, l, ll_chain); + } +} + #endif /* ENABLE_ADMINPORT */ diff --git a/src/racoon/evt.h b/src/racoon/evt.h index 0ce65bd..ba7fb57 100644 --- a/src/racoon/evt.h +++ b/src/racoon/evt.h @@ -124,6 +124,8 @@ void evt_phase2 __P((const struct ph2handle *ph2, int type, vchar_t *optdata)); vchar_t *evt_dump __P((void)); int evt_subscribe __P((struct evt_listener_list *list, int fd)); +void evt_list_move __P((struct evt_listener_list *from, + struct evt_listener_list *to)); void evt_list_init __P((struct evt_listener_list *list)); void evt_list_cleanup __P((struct evt_listener_list *list)); @@ -136,6 +138,7 @@ void evt_list_cleanup __P((struct evt_listener_list *list)); #define evt_phase2(ph2, type, optdata) ; #define evt_subscribe(eventlist, fd) ; +#deifne evt_list_move(from, to) ; #define evt_list_init(eventlist) ; #define evt_list_cleanup(eventlist) ; #define evt_get_fdmask(nfds, fdset) nfds diff --git a/src/racoon/handler.c b/src/racoon/handler.c index b33986f..9fd3817 100644 --- a/src/racoon/handler.c +++ b/src/racoon/handler.c @@ -269,26 +269,40 @@ migrate_ph12(old_iph1, new_iph1) } /* - * the iph1 is new, migrate all phase2s that belong to a dying or dead ph1 + * the iph1 is new, migrate all phase2s that belong to a dying or dead ph1. */ void migrate_dying_ph12(iph1) struct ph1handle *iph1; { - struct ph1handle *p; + struct ph1handle *p, *next; - LIST_FOREACH(p, &ph1tree, chain) { + for (p = LIST_FIRST(&ph1tree); p; p = next) { + next = LIST_NEXT(p, chain); if (p == iph1) continue; - if (p->status < PHASE1ST_DYING) + + /* Same remote? */ + if (cmpsaddr(iph1->local, p->local) > CMPSADDR_WOP_MATCH || + cmpsaddr(iph1->remote, p->remote) > CMPSADDR_WOP_MATCH || + iph1->rmconf != p->rmconf) continue; - if (cmpsaddr(iph1->local, p->local) == 0 - && cmpsaddr(iph1->remote, p->remote) == 0) + /* migrate phase2:s from expiring entries */ + if (p->status >= PHASE1ST_DYING) migrate_ph12(p, iph1); + + /* and allow reverse connections to release + * pending connections that do not work due + * to firewall or nat */ + if (iph1->side == RESPONDER && p->side == INITIATOR && + p->status < PHASE1ST_MSG3RECEIVED) { + /* Do not delete ph1, since if the node is not NATted, + * and we delete it we might get phase2's lost */ + evt_list_move(&p->evt_listeners, &iph1->evt_listeners); + } } } - /* * dump isakmp-sa */ diff --git a/src/racoon/isakmp.c b/src/racoon/isakmp.c index 0de16d1..2dfda2f 100644 --- a/src/racoon/isakmp.c +++ b/src/racoon/isakmp.c @@ -2138,13 +2138,33 @@ isakmp_ph2delete(iph2) remph2(iph2); delph2(iph2); - - return; } /* %%% * Interface between PF_KEYv2 and ISAKMP */ + +static void +isakmp_chkph2there(p) + struct sched *p; +{ + struct ph2handle *iph2 = container_of(p, struct ph2handle, sce); + struct ph2handle *tmp; + + /* Check if a similar phase2 appared meanwhile */ + remph2(iph2); + tmp = getph2byid(iph2->src, iph2->dst, iph2->spid); + if (tmp == NULL) { + /* Nope, lets start this then */ + insph2(iph2); + isakmp_chkph1there(iph2); + } else { + /* Yes, delete this initiation attempt as redundant */ + evt_phase2(iph2, EVT_PHASE2_UP, NULL); + delph2(iph2); + } +} + /* * receive ACQUIRE from kernel, and begin either phase1 or phase2. * if phase1 has been finished, begin phase2. @@ -2220,8 +2240,14 @@ isakmp_post_acquire(iph2) /*NOTREACHED*/ } - /* found established ISAKMP-SA */ - /* i.e. iph1->status == PHASE1ST_ESTABLISHED */ + /* found established ISAKMP-SA, if this is a RESPONDER ISAKMP-SA + * add a small delay; this will make sure the initiator gets + * an first attempt at rekeying, and usually avoids duplicate ph2:s */ + if (iph1->side == RESPONDER) { + iph2->retry_checkph1 = 1; + sched_schedule(&iph2->sce, 1, isakmp_chkph2there); + return 0; + } /* found ISAKMP-SA. */ plog(LLV_DEBUG, LOCATION, NULL, "begin QUICK mode.\n"); @@ -2388,7 +2414,10 @@ isakmp_chkph1there(iph2) plog(LLV_DEBUG2, LOCATION, NULL, "dst: %s\n", saddr2str(iph2->dst)); /* begin quick mode */ - (void)isakmp_ph2begin_i(iph1, iph2); + if (isakmp_ph2begin_i(iph1, iph2)) { + remph2(iph2); + delph2(iph2); + } return; }