summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoryasuoka <yasuoka@openbsd.org>2013-11-21 22:25:01 +0000
committeryasuoka <yasuoka@openbsd.org>2013-11-21 22:25:01 +0000
commitdeca704397748fa7a2befe5d639f3da71d1c61aa (patch)
tree2d6e2b8da81082763b81a2166448648181579a5e
parentsync (diff)
downloadwireguard-openbsd-deca704397748fa7a2befe5d639f3da71d1c61aa.tar.xz
wireguard-openbsd-deca704397748fa7a2befe5d639f3da71d1c61aa.zip
Keep the flow until last IPsec SA is deleted, if the flow is shared by
multiple IPsec SAs in NAT-T case. This fixes a problem that L2TP/IPsec connections are disconnected improper in case multiple Windows clients are connected from behind one NAT. ok markus
-rw-r--r--sbin/isakmpd/ipsec.c30
-rw-r--r--sbin/isakmpd/sa.c19
-rw-r--r--sbin/isakmpd/sa.h8
3 files changed, 52 insertions, 5 deletions
diff --git a/sbin/isakmpd/ipsec.c b/sbin/isakmpd/ipsec.c
index 343b4bc9e95..e5cbc7e215b 100644
--- a/sbin/isakmpd/ipsec.c
+++ b/sbin/isakmpd/ipsec.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ipsec.c,v 1.138 2012/06/30 14:51:31 naddy Exp $ */
+/* $OpenBSD: ipsec.c,v 1.139 2013/11/21 22:25:01 yasuoka Exp $ */
/* $EOM: ipsec.c,v 1.143 2000/12/11 23:57:42 niklas Exp $ */
/*
@@ -132,6 +132,8 @@ static int ipsec_validate_notification(u_int16_t);
static int ipsec_validate_proto(u_int8_t);
static int ipsec_validate_situation(u_int8_t *, size_t *, size_t);
static int ipsec_validate_transform_id(u_int8_t, u_int8_t);
+static int ipsec_sa_check_flow(struct sa *, void *);
+static int ipsec_sa_check_flow_any(struct sa *, void *);
static int ipsec_sa_tag(struct exchange *, struct sa *, struct sa *);
static struct doi ipsec_doi = {
@@ -254,11 +256,20 @@ ipsec_sa_lookup(struct sockaddr *dst, u_int32_t spi, u_int8_t proto)
static int
ipsec_sa_check_flow(struct sa *sa, void *v_arg)
{
+ if ((sa->flags & (SA_FLAG_READY | SA_FLAG_REPLACED)) != SA_FLAG_READY)
+ return 0;
+
+ return ipsec_sa_check_flow_any(sa, v_arg);
+}
+
+static int
+ipsec_sa_check_flow_any(struct sa *sa, void *v_arg)
+{
struct sa *sa2 = v_arg;
struct ipsec_sa *isa = sa->data, *isa2 = sa2->data;
if (sa == sa2 || sa->phase != 2 ||
- (sa->flags & (SA_FLAG_READY | SA_FLAG_REPLACED)) != SA_FLAG_READY)
+ (sa->flags & SA_FLAG_READY) != SA_FLAG_READY)
return 0;
if (isa->tproto != isa2->tproto || isa->sport != isa2->sport ||
@@ -1561,9 +1572,24 @@ ipsec_decode_transform(struct message *msg, struct sa *sa, struct proto *proto,
static void
ipsec_delete_spi(struct sa *sa, struct proto *proto, int incoming)
{
+ struct sa *new_sa;
+ struct ipsec_proto *iproto;
+
if (sa->phase == 1)
return;
+ iproto = proto->data;
+ /*
+ * If the SA is using UDP encap and it replaced other SA,
+ * enable the other SA to keep the flow for the other SAs.
+ */
+ if ((iproto->encap_mode == IPSEC_ENCAP_UDP_ENCAP_TRANSPORT ||
+ iproto->encap_mode == IPSEC_ENCAP_UDP_ENCAP_TRANSPORT_DRAFT) &&
+ (sa->flags & SA_FLAG_REPLACED) == 0 &&
+ (new_sa = sa_find(ipsec_sa_check_flow_any, sa)) != NULL &&
+ new_sa->flags & SA_FLAG_REPLACED)
+ sa_replace(sa, new_sa);
+
/*
* If the SA was not replaced and was not one acquired through the
* kernel (ACQUIRE message), remove the flow associated with it.
diff --git a/sbin/isakmpd/sa.c b/sbin/isakmpd/sa.c
index cc61d97bbf5..92a98a2f983 100644
--- a/sbin/isakmpd/sa.c
+++ b/sbin/isakmpd/sa.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sa.c,v 1.117 2012/06/30 14:51:31 naddy Exp $ */
+/* $OpenBSD: sa.c,v 1.118 2013/11/21 22:25:01 yasuoka Exp $ */
/* $EOM: sa.c,v 1.112 2000/12/12 00:22:52 niklas Exp $ */
/*
@@ -48,6 +48,7 @@
#include "connection.h"
#include "cookie.h"
#include "doi.h"
+#include "dpd.h"
#include "exchange.h"
#include "isakmp.h"
#include "log.h"
@@ -1330,6 +1331,22 @@ sa_mark_replaced(struct sa *sa)
sa->flags |= SA_FLAG_REPLACED;
}
+/* Replace SA */
+void
+sa_replace(struct sa *sa, struct sa *new_sa)
+{
+ LOG_DBG((LOG_SA, 60, "sa_replace: SA %p (%s) is replaced by SA %p (%s)",
+ sa, sa->name ? sa->name : "unnamed",
+ new_sa, new_sa->name ? new_sa->name : "unnamed"));
+ sa_mark_replaced(sa);
+ if (new_sa->flags & SA_FLAG_REPLACED) {
+ /* enable the dpd */
+ if ((new_sa->flags & SA_FLAG_DPD) == SA_FLAG_DPD)
+ dpd_start(new_sa);
+ new_sa->flags &= ~SA_FLAG_REPLACED;
+ }
+}
+
/*
* Setup expiration timers for SA. This is used for ISAKMP SAs, but also
* possible to use for application SAs if the application does not deal
diff --git a/sbin/isakmpd/sa.h b/sbin/isakmpd/sa.h
index 02bb4faa897..6331f0eba04 100644
--- a/sbin/isakmpd/sa.h
+++ b/sbin/isakmpd/sa.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: sa.h,v 1.49 2006/11/24 13:52:14 reyk Exp $ */
+/* $OpenBSD: sa.h,v 1.50 2013/11/21 22:25:02 yasuoka Exp $ */
/* $EOM: sa.h,v 1.58 2000/10/10 12:39:01 provos Exp $ */
/*
@@ -223,7 +223,10 @@ struct sa {
/* Establish the SA when it is needed. */
#define SA_FLAG_ONDEMAND 0x04
-/* This SA has been replaced by another newer one. */
+/*
+ * This SA has been replaced by another newer one or the SA for another
+ * client behind same NAT.
+ */
#define SA_FLAG_REPLACED 0x08
/* This SA has seen a soft timeout and wants to be renegotiated on use. */
@@ -263,6 +266,7 @@ extern struct sa *sa_lookup_by_name(char *, int);
extern struct sa *sa_lookup_from_icookie(u_int8_t *);
extern struct sa *sa_lookup_isakmp_sa(struct sockaddr *, u_int8_t *);
extern void sa_mark_replaced(struct sa *);
+extern void sa_replace(struct sa *, struct sa *);
extern void sa_reference(struct sa *);
extern void sa_release(struct sa *);
extern void sa_remove(struct sa *);