diff options
author | YASUOKA Masahiko <yasuoka@cvs.openbsd.org> | 2013-11-21 22:25:03 +0000 |
---|---|---|
committer | YASUOKA Masahiko <yasuoka@cvs.openbsd.org> | 2013-11-21 22:25:03 +0000 |
commit | 5bec62f1d03600b4000e503f17624a542cd97839 (patch) | |
tree | 4b61b0201f0009d7b3f5fe2b1cd636003681cf16 /sbin/isakmpd/ipsec.c | |
parent | bb6a21827e48f8d0eef5f5dd37cb714b6f1c2815 (diff) |
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
Diffstat (limited to 'sbin/isakmpd/ipsec.c')
-rw-r--r-- | sbin/isakmpd/ipsec.c | 30 |
1 files changed, 28 insertions, 2 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. |