summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sbin/isakmpd/Makefile14
-rw-r--r--sbin/isakmpd/exchange.h12
-rw-r--r--sbin/isakmpd/features/nat_traversal54
-rw-r--r--sbin/isakmpd/ike_phase_1.c49
-rw-r--r--sbin/isakmpd/init.c23
-rw-r--r--sbin/isakmpd/ipsec.c50
-rw-r--r--sbin/isakmpd/isakmp.h15
-rw-r--r--sbin/isakmpd/isakmp_fld.fld14
-rw-r--r--sbin/isakmpd/message.c103
-rw-r--r--sbin/isakmpd/nat_traversal.c384
-rw-r--r--sbin/isakmpd/nat_traversal.h36
-rw-r--r--sbin/isakmpd/policy.c19
-rw-r--r--sbin/isakmpd/transport.c151
-rw-r--r--sbin/isakmpd/transport.h27
-rw-r--r--sbin/isakmpd/udp.c531
-rw-r--r--sbin/isakmpd/udp.h16
-rw-r--r--sbin/isakmpd/udp_encap.c461
-rw-r--r--sbin/isakmpd/udp_encap.h37
-rw-r--r--sbin/isakmpd/util.c19
-rw-r--r--sbin/isakmpd/util.h5
-rw-r--r--sbin/isakmpd/virtual.c706
-rw-r--r--sbin/isakmpd/virtual.h42
22 files changed, 2206 insertions, 562 deletions
diff --git a/sbin/isakmpd/Makefile b/sbin/isakmpd/Makefile
index 1d474c513d2..b1a9ff6e95f 100644
--- a/sbin/isakmpd/Makefile
+++ b/sbin/isakmpd/Makefile
@@ -1,4 +1,4 @@
-# $OpenBSD: Makefile,v 1.52 2004/04/07 22:43:14 ho Exp $
+# $OpenBSD: Makefile,v 1.53 2004/06/20 15:24:05 ho Exp $
# $EOM: Makefile,v 1.78 2000/10/15 21:33:42 niklas Exp $
#
@@ -47,10 +47,10 @@ OS= openbsd
# Compile-time configuration of otherwise optional features
#FEATURES= tripledes des blowfish cast aes
#FEATURES+= policy x509 ec aggressive debug gmp
-#FEATURES+= rawkey isakmp_cfg dnssec privsep
+#FEATURES+= rawkey isakmp_cfg dnssec privsep dpd nat_traversal
FEATURES= tripledes des blowfish cast aes
FEATURES+= policy x509 ec aggressive debug
-FEATURES+= rawkey isakmp_cfg privsep
+FEATURES+= rawkey isakmp_cfg privsep
.PATH: ${.CURDIR}/sysdep/${OS}
@@ -63,7 +63,8 @@ SRCS= app.c attribute.c cert.c connection.c constants.c conf.c \
ike_phase_1.c ike_quick_mode.c init.c ipsec.c ipsec_fld.c \
ipsec_num.c isakmpd.c isakmp_doi.c isakmp_fld.c isakmp_num.c \
key.c libcrypto.c log.c message.c math_2n.c math_group.c \
- prf.c sa.c sysdep.c timer.c transport.c udp.c ui.c util.c
+ prf.c sa.c sysdep.c timer.c transport.c virtual.c udp.c \
+ ui.c util.c
GENERATED= exchange_num.h ipsec_fld.h ipsec_num.h isakmp_fld.h \
isakmp_num.h
@@ -178,7 +179,7 @@ DPADD+= ${LIBDES}
.endif
SRCS+= ${IPSEC_SRCS} ${X509} ${POLICY} ${EC} ${AGGRESSIVE} ${DNSSEC} \
- ${ISAKMP_CFG} ${PRIVSEP}
+ ${ISAKMP_CFG} ${PRIVSEP} ${DPD} ${NAT_TRAVERSAL}
CFLAGS+= ${IPSEC_CFLAGS} ${DNSSEC_CFLAGS}
LDADD+= ${DESLIB} ${LWRESLIB}
@@ -203,3 +204,6 @@ ${PROG} beforedepend: ${GENERATED}
.include <bsd.prog.mk>
.include <bsd.subdir.mk>
+
+debug:
+ (cd ${.CURDIR}; ${MAKE} DEBUG="-g -Werror")
diff --git a/sbin/isakmpd/exchange.h b/sbin/isakmpd/exchange.h
index 96d71e796bc..b3b5cc68650 100644
--- a/sbin/isakmpd/exchange.h
+++ b/sbin/isakmpd/exchange.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: exchange.h,v 1.26 2004/05/03 21:23:51 hshoexer Exp $ */
+/* $OpenBSD: exchange.h,v 1.27 2004/06/20 15:24:05 ho Exp $ */
/* $EOM: exchange.h,v 1.28 2000/09/28 12:54:28 niklas Exp $ */
/*
@@ -212,11 +212,15 @@ struct exchange {
};
/* The flag bits. */
-#define EXCHANGE_FLAG_I_COMMITTED 1
-#define EXCHANGE_FLAG_HE_COMMITTED 2
+#define EXCHANGE_FLAG_I_COMMITTED 0x01
+#define EXCHANGE_FLAG_HE_COMMITTED 0x02
#define EXCHANGE_FLAG_COMMITTED (EXCHANGE_FLAG_I_COMMITTED \
| EXCHANGE_FLAG_HE_COMMITTED)
-#define EXCHANGE_FLAG_ENCRYPT 4
+#define EXCHANGE_FLAG_ENCRYPT 0x04
+#define EXCHANGE_FLAG_NAT_T_CAP_PEER 0x08 /* Peer is NAT capable. */
+#define EXCHANGE_FLAG_NAT_T_ENABLE 0x10 /* We are doing NAT-T. */
+#define EXCHANGE_FLAG_NAT_T_KEEPALIVE 0x20 /* We are the NAT:ed peer. */
+#define EXCHANGE_FLAG_DPD_CAP_PEER 0x40 /* Peer is DPD capable. */
extern int exchange_add_certs(struct message *);
extern void exchange_finalize(struct message *);
diff --git a/sbin/isakmpd/features/nat_traversal b/sbin/isakmpd/features/nat_traversal
new file mode 100644
index 00000000000..91136cc223f
--- /dev/null
+++ b/sbin/isakmpd/features/nat_traversal
@@ -0,0 +1,54 @@
+# $OpenBSD: nat_traversal,v 1.1 2004/06/20 15:24:05 ho Exp $
+
+#
+# Copyright (c) 2004 Håkan Olsson. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+NAT_TRAVERSAL= nat_traversal.c udp_encap.c
+# $OpenBSD: nat_traversal,v 1.1 2004/06/20 15:24:05 ho Exp $
+
+#
+# Copyright (c) 2004 Håkan Olsson. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+NAT_TRAVERSAL= nat_traversal.c udp_encap.c
diff --git a/sbin/isakmpd/ike_phase_1.c b/sbin/isakmpd/ike_phase_1.c
index 010d71cdd18..6077b6ed3cd 100644
--- a/sbin/isakmpd/ike_phase_1.c
+++ b/sbin/isakmpd/ike_phase_1.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ike_phase_1.c,v 1.50 2004/06/14 09:55:41 ho Exp $ */
+/* $OpenBSD: ike_phase_1.c,v 1.51 2004/06/20 15:24:05 ho Exp $ */
/* $EOM: ike_phase_1.c,v 1.31 2000/12/11 23:47:56 niklas Exp $ */
/*
@@ -45,6 +45,9 @@
#include "crypto.h"
#include "dh.h"
#include "doi.h"
+#ifdef USE_DPD
+#include "dpd.h"
+#endif
#include "exchange.h"
#include "hash.h"
#include "ike_auth.h"
@@ -55,6 +58,9 @@
#include "log.h"
#include "math_group.h"
#include "message.h"
+#if defined (USE_NAT_TRAVERSAL)
+#include "nat_traversal.h"
+#endif
#include "prf.h"
#include "sa.h"
#include "transport.h"
@@ -359,6 +365,18 @@ ike_phase_1_initiator_send_SA(struct message *msg)
transforms_len += transform_len[i];
}
+#if defined (USE_NAT_TRAVERSAL)
+ /* Advertise NAT-T capability. */
+ if (nat_t_add_vendor_payloads(msg))
+ goto bail_out;
+#endif
+
+#if defined (USE_DPD)
+ /* Advertise DPD capability. */
+ if (dpd_add_vendor_payload(msg))
+ goto bail_out;
+#endif
+
conf_free_list(conf);
free(transform);
free(transform_len);
@@ -511,7 +529,21 @@ int
ike_phase_1_responder_send_SA(struct message *msg)
{
/* Add the SA payload with the transform that was chosen. */
- return message_add_sa_payload(msg);
+ if (message_add_sa_payload(msg))
+ return -1;
+
+#if defined (USE_NAT_TRAVERSAL)
+ /* Advertise NAT-T capability. */
+ if (nat_t_add_vendor_payloads(msg))
+ return -1;
+#endif
+
+#if defined (USE_DPD)
+ /* Advertise DPD capability. */
+ if (dpd_add_vendor_payload(msg))
+ return -1;
+#endif
+ return 0;
}
/* Send our public DH value and a nonce to the peer. */
@@ -533,6 +565,14 @@ ike_phase_1_send_KE_NONCE(struct message *msg, size_t nonce_sz)
/* XXX Log? */
return -1;
}
+#if defined (USE_NAT_TRAVERSAL)
+ /* If this exchange uses NAT-Traversal, add NAT-D payloads now. */
+ if (msg->exchange->flags & EXCHANGE_FLAG_NAT_T_CAP_PEER)
+ if (nat_t_exchange_add_nat_d(msg)) {
+ /* XXX Log? */
+ return -1;
+ }
+#endif
return 0;
}
@@ -555,6 +595,11 @@ ike_phase_1_recv_KE_NONCE(struct message *msg)
/* XXX How to log and notify peer? */
return -1;
}
+#if defined (USE_NAT_TRAVERSAL)
+ /* Check NAT-D payloads and contents. */
+ if (msg->exchange->flags & EXCHANGE_FLAG_NAT_T_CAP_PEER)
+ (void)nat_t_exchange_check_nat_d(msg);
+#endif
return 0;
}
diff --git a/sbin/isakmpd/init.c b/sbin/isakmpd/init.c
index 929bf07d3e5..b6fa322e96c 100644
--- a/sbin/isakmpd/init.c
+++ b/sbin/isakmpd/init.c
@@ -1,10 +1,10 @@
-/* $OpenBSD: init.c,v 1.29 2004/04/15 18:39:25 deraadt Exp $ */
+/* $OpenBSD: init.c,v 1.30 2004/06/20 15:24:05 ho Exp $ */
/* $EOM: init.c,v 1.25 2000/03/30 14:27:24 ho Exp $ */
/*
* Copyright (c) 1998, 1999, 2000 Niklas Hallqvist. All rights reserved.
* Copyright (c) 2000 Angelos D. Keromytis. All rights reserved.
- * Copyright (c) 2003 Håkan Olsson. All rights reserved.
+ * Copyright (c) 2003, 2004 Håkan Olsson. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -50,14 +50,20 @@
#include "sa.h"
#include "timer.h"
#include "transport.h"
+#include "virtual.h"
#include "udp.h"
#include "ui.h"
#include "util.h"
-#ifdef USE_POLICY
+#if defined (USE_POLICY)
#include "policy.h"
#endif
+#if defined (USE_NAT_TRAVERSAL)
+#include "nat_traversal.h"
+#include "udp_encap.h"
+#endif
+
void
init(void)
{
@@ -78,7 +84,7 @@ init(void)
/* This depends on conf_init, thus check as soon as possible. */
log_reinit();
-#ifdef USE_POLICY
+#if defined (USE_POLICY)
/* policy_init depends on conf_init having run. */
policy_init();
#endif
@@ -89,7 +95,12 @@ init(void)
sa_init();
transport_init();
+ virtual_init();
udp_init();
+#if defined (USE_NAT_TRAVERSAL)
+ nat_t_init();
+ udp_encap_init();
+#endif
ui_init();
}
@@ -116,7 +127,7 @@ reinit(void)
log_reinit();
-#ifdef USE_POLICY
+#if defined (USE_POLICY)
/* Reread the policies. */
policy_init();
#endif
@@ -129,7 +140,7 @@ reinit(void)
connection_reinit();
/*
- * Rescan interfaces.
+ * Rescan interfaces (call reinit() in all transports).
*/
transport_reinit();
diff --git a/sbin/isakmpd/ipsec.c b/sbin/isakmpd/ipsec.c
index cd524495aff..f8033a3c527 100644
--- a/sbin/isakmpd/ipsec.c
+++ b/sbin/isakmpd/ipsec.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ipsec.c,v 1.96 2004/06/17 19:39:38 hshoexer Exp $ */
+/* $OpenBSD: ipsec.c,v 1.97 2004/06/20 15:24:05 ho Exp $ */
/* $EOM: ipsec.c,v 1.143 2000/12/11 23:57:42 niklas Exp $ */
/*
@@ -1119,20 +1119,21 @@ ipsec_is_attribute_incompatible(u_int16_t type, u_int8_t *value, u_int16_t len,
void *vmsg)
{
struct message *msg = vmsg;
+ u_int16_t dv = decode_16(value);
if (msg->exchange->phase == 1) {
switch (type) {
case IKE_ATTR_ENCRYPTION_ALGORITHM:
- return !crypto_get(from_ike_crypto(decode_16(value)));
+ return !crypto_get(from_ike_crypto(dv));
case IKE_ATTR_HASH_ALGORITHM:
- return !hash_get(from_ike_hash(decode_16(value)));
+ return !hash_get(from_ike_hash(dv));
case IKE_ATTR_AUTHENTICATION_METHOD:
- return !ike_auth_get(decode_16(value));
+ return !ike_auth_get(dv);
case IKE_ATTR_GROUP_DESCRIPTION:
- return (decode_16(value) < IKE_GROUP_DESC_MODP_768
- || decode_16(value) > IKE_GROUP_DESC_MODP_1536)
- && (decode_16(value) < IKE_GROUP_DESC_MODP_2048
- || decode_16(value) > IKE_GROUP_DESC_MODP_8192);
+ return (dv < IKE_GROUP_DESC_MODP_768
+ || dv > IKE_GROUP_DESC_MODP_1536)
+ && (dv < IKE_GROUP_DESC_MODP_2048
+ || dv > IKE_GROUP_DESC_MODP_8192);
case IKE_ATTR_GROUP_TYPE:
return 1;
case IKE_ATTR_GROUP_PRIME:
@@ -1146,8 +1147,8 @@ ipsec_is_attribute_incompatible(u_int16_t type, u_int8_t *value, u_int16_t len,
case IKE_ATTR_GROUP_CURVE_B:
return 1;
case IKE_ATTR_LIFE_TYPE:
- return decode_16(value) < IKE_DURATION_SECONDS
- || decode_16(value) > IKE_DURATION_KILOBYTES;
+ return dv < IKE_DURATION_SECONDS
+ || dv > IKE_DURATION_KILOBYTES;
case IKE_ATTR_LIFE_DURATION:
return len != 2 && len != 4;
case IKE_ATTR_PRF:
@@ -1157,7 +1158,7 @@ ipsec_is_attribute_incompatible(u_int16_t type, u_int8_t *value, u_int16_t len,
* Our crypto routines only allows key-lengths which
* are multiples of an octet.
*/
- return decode_16(value) % 8 != 0;
+ return dv % 8 != 0;
case IKE_ATTR_FIELD_SIZE:
return 1;
case IKE_ATTR_GROUP_ORDER:
@@ -1166,21 +1167,28 @@ ipsec_is_attribute_incompatible(u_int16_t type, u_int8_t *value, u_int16_t len,
} else {
switch (type) {
case IPSEC_ATTR_SA_LIFE_TYPE:
- return decode_16(value) < IPSEC_DURATION_SECONDS
- || decode_16(value) > IPSEC_DURATION_KILOBYTES;
+ return dv < IPSEC_DURATION_SECONDS
+ || dv > IPSEC_DURATION_KILOBYTES;
case IPSEC_ATTR_SA_LIFE_DURATION:
return len != 2 && len != 4;
case IPSEC_ATTR_GROUP_DESCRIPTION:
- return (decode_16(value) < IKE_GROUP_DESC_MODP_768
- || decode_16(value) > IKE_GROUP_DESC_MODP_1536)
- && (decode_16(value) < IKE_GROUP_DESC_MODP_2048
- || IKE_GROUP_DESC_MODP_8192 < decode_16(value));
+ return (dv < IKE_GROUP_DESC_MODP_768
+ || dv > IKE_GROUP_DESC_MODP_1536)
+ && (dv < IKE_GROUP_DESC_MODP_2048
+ || IKE_GROUP_DESC_MODP_8192 < dv);
case IPSEC_ATTR_ENCAPSULATION_MODE:
- return decode_16(value) < IPSEC_ENCAP_TUNNEL
- || decode_16(value) > IPSEC_ENCAP_TRANSPORT;
+#if defined (USE_NAT_TRAVERSAL)
+ return dv != IPSEC_ENCAP_TUNNEL
+ && dv != IPSEC_ENCAP_TRANSPORT
+ && dv != IPSEC_ENCAP_UDP_ENCAP_TUNNEL
+ && dv != IPSEC_ENCAP_UDP_ENCAP_TRANSPORT;
+#else
+ return dv < IPSEC_ENCAP_TUNNEL
+ || dv > IPSEC_ENCAP_TRANSPORT;
+#endif /* USE_NAT_TRAVERSAL */
case IPSEC_ATTR_AUTHENTICATION_ALGORITHM:
- return decode_16(value) < IPSEC_AUTH_HMAC_MD5
- || decode_16(value) > IPSEC_AUTH_HMAC_RIPEMD;
+ return dv < IPSEC_AUTH_HMAC_MD5
+ || dv > IPSEC_AUTH_HMAC_RIPEMD;
case IPSEC_ATTR_KEY_LENGTH:
/*
* XXX Blowfish needs '0'. Others appear to disregard
diff --git a/sbin/isakmpd/isakmp.h b/sbin/isakmpd/isakmp.h
index 11051fb7c15..4ab6869648d 100644
--- a/sbin/isakmpd/isakmp.h
+++ b/sbin/isakmpd/isakmp.h
@@ -1,8 +1,9 @@
-/* $OpenBSD: isakmp.h,v 1.6 2004/04/15 18:39:26 deraadt Exp $ */
+/* $OpenBSD: isakmp.h,v 1.7 2004/06/20 15:24:05 ho Exp $ */
/* $EOM: isakmp.h,v 1.11 2000/07/05 10:48:43 ho Exp $ */
/*
* Copyright (c) 1998 Niklas Hallqvist. All rights reserved.
+ * Copyright (c) 2004 Håkan Olsson. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -35,11 +36,15 @@
#include "isakmp_fld.h"
#include "isakmp_num.h"
-/* ISAKMP default transport */
-#define ISAKMP_DEFAULT_TRANSPORT "udp"
-
/* IANA assigned port */
-#define UDP_DEFAULT_PORT 500
+#define UDP_DEFAULT_PORT 500
+#define UDP_DEFAULT_PORT_STR "500"
+
+#define ISAKMP_DEFAULT_TRANSPORT "udp"
+
+/* draft-ietf-ipsec-nat-t-ike-07.txt */
+#define UDP_ENCAP_DEFAULT_PORT 4500
+#define UDP_ENCAP_DEFAULT_PORT_STR "4500"
/* ISAKMP header extras defines */
#define ISAKMP_HDR_COOKIES_OFF ISAKMP_HDR_ICOOKIE_OFF
diff --git a/sbin/isakmpd/isakmp_fld.fld b/sbin/isakmpd/isakmp_fld.fld
index a92c54b1f90..05eeda98561 100644
--- a/sbin/isakmpd/isakmp_fld.fld
+++ b/sbin/isakmpd/isakmp_fld.fld
@@ -1,4 +1,4 @@
-# $OpenBSD: isakmp_fld.fld,v 1.7 2003/06/03 14:28:16 ho Exp $
+# $OpenBSD: isakmp_fld.fld,v 1.8 2004/06/20 15:24:05 ho Exp $
# $EOM: isakmp_fld.fld,v 1.5 1999/04/25 13:38:22 niklas Exp $
#
@@ -150,3 +150,15 @@ ISAKMP_ATTRIBUTE : ISAKMP_GEN
ID num 2
ATTRS raw
.
+
+# NAT Discovery payload.
+ISAKMP_NAT_D : ISAKMP_GEN
+ DATA raw
+.
+
+# NAT Original Address payload.
+ISAKMP_NAT_OA : ISAKMP_GEN
+ TYPE num 1
+ RESERVED ign 3
+ DATA raw
+.
diff --git a/sbin/isakmpd/message.c b/sbin/isakmpd/message.c
index dba5b806510..30bcd0e50be 100644
--- a/sbin/isakmpd/message.c
+++ b/sbin/isakmpd/message.c
@@ -1,10 +1,10 @@
-/* $OpenBSD: message.c,v 1.80 2004/06/20 15:11:29 ho Exp $ */
+/* $OpenBSD: message.c,v 1.81 2004/06/20 15:24:05 ho Exp $ */
/* $EOM: message.c,v 1.156 2000/10/10 12:36:39 provos Exp $ */
/*
* Copyright (c) 1998, 1999, 2000, 2001 Niklas Hallqvist. All rights reserved.
* Copyright (c) 1999 Angelos D. Keromytis. All rights reserved.
- * Copyright (c) 1999, 2000, 2001 Håkan Olsson. All rights reserved.
+ * Copyright (c) 1999, 2000, 2001, 2004 Håkan Olsson. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -45,6 +45,9 @@
#include "constants.h"
#include "crypto.h"
#include "doi.h"
+#ifdef USE_DPD
+#include "dpd.h"
+#endif
#include "exchange.h"
#include "field.h"
#include "hash.h"
@@ -53,6 +56,9 @@
#include "isakmp.h"
#include "log.h"
#include "message.h"
+#if defined (USE_NAT_TRAVERSAL)
+#include "nat_traversal.h"
+#endif
#include "prf.h"
#include "sa.h"
#include "timer.h"
@@ -85,6 +91,8 @@ static int message_validate_delete(struct message *, struct payload *);
static int message_validate_hash(struct message *, struct payload *);
static int message_validate_id(struct message *, struct payload *);
static int message_validate_key_exch(struct message *, struct payload *);
+static int message_validate_nat_d(struct message *, struct payload *);
+static int message_validate_nat_oa(struct message *, struct payload *);
static int message_validate_nonce(struct message *, struct payload *);
static int message_validate_notify(struct message *, struct payload *);
static int message_validate_proposal(struct message *, struct payload *);
@@ -102,14 +110,16 @@ static int (*message_validate_payload[])(struct message *, struct payload *) =
message_validate_id, message_validate_cert, message_validate_cert_req,
message_validate_hash, message_validate_sig, message_validate_nonce,
message_validate_notify, message_validate_delete,
- message_validate_vendor, message_validate_attribute
+ message_validate_vendor, message_validate_attribute,
+ message_validate_nat_d, message_validate_nat_oa
};
static struct field *fields[] = {
isakmp_sa_fld, isakmp_prop_fld, isakmp_transform_fld, isakmp_ke_fld,
isakmp_id_fld, isakmp_cert_fld, isakmp_certreq_fld, isakmp_hash_fld,
isakmp_sig_fld, isakmp_nonce_fld, isakmp_notify_fld, isakmp_delete_fld,
- isakmp_vendor_fld, isakmp_attribute_fld
+ isakmp_vendor_fld, isakmp_attribute_fld, isakmp_nat_d_fld,
+ isakmp_nat_oa_fld
};
/*
@@ -211,12 +221,9 @@ message_free(struct message *msg)
link);
/* If we are on the send queue, remove us from there. */
- if (msg->flags & MSG_IN_TRANSIT) {
- if (msg->flags & MSG_PRIORITIZED)
- TAILQ_REMOVE(&msg->transport->prio_sendq, msg, link);
- else
- TAILQ_REMOVE(&msg->transport->sendq, msg, link);
- }
+ if (msg->flags & MSG_IN_TRANSIT)
+ TAILQ_REMOVE(msg->transport->vtbl->get_queue(msg), msg, link);
+
transport_release(msg->transport);
if (msg->isakmp_sa)
@@ -478,8 +485,8 @@ message_validate_cert_req(struct message *msg, struct payload *p)
return -1;
}
/*
- * Check the certificate types we support and if an acceptable authority
- * is included in the payload check if it can be decoded
+ * Check the certificate types we support and if an acceptable
+ * authority is included in the payload check if it can be decoded
*/
cert = cert_get(GET_ISAKMP_CERTREQ_TYPE(p->p));
if (!cert || (len && !cert->certreq_validate(p->p +
@@ -745,6 +752,53 @@ message_validate_key_exch(struct message *msg, struct payload *p)
return 0;
}
+/* Validate the NAT-D payload P in message MSG. */
+static int
+message_validate_nat_d(struct message *msg, struct payload *p)
+{
+ struct exchange *exchange = msg->exchange;
+
+ if (!exchange) {
+ /* We should have an exchange at this point. */
+ log_print("message_validate_nat_d: payload out of sequence");
+ message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 1, 1);
+ return -1;
+ }
+
+ if (exchange->phase != 1) {
+ log_print("message_validate_nat_d: "
+ "NAT-D payload must be in phase 1");
+ message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 1, 1);
+ return -1;
+ }
+
+ /* Mark as handled. */
+ p->flags |= PL_MARK;
+
+ return 0;
+}
+
+/* Validate the NAT-OA payload P in message MSG. */
+static int
+message_validate_nat_oa(struct message *msg, struct payload *p)
+{
+ struct exchange *exchange = msg->exchange;
+
+ if (!exchange) {
+ /* We should have an exchange at this point. */
+ log_print("message_validate_nat_d: payload out of sequence");
+ message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 1, 1);
+ return -1;
+ }
+
+#ifdef notyet /* XXX Probably never, due to patent issues. */
+ /* Mark as handled. */
+ p->flags |= PL_MARK;
+#endif
+
+ return 0;
+}
+
/* Validate the nonce payload P in message MSG. */
static int
message_validate_nonce(struct message *msg, struct payload *p)
@@ -1020,7 +1074,15 @@ message_validate_vendor(struct message *msg, struct payload *p)
message_drop(msg, ISAKMP_NOTIFY_INVALID_PAYLOAD_TYPE, 0, 1, 1);
return -1;
}
- LOG_DBG((LOG_MESSAGE, 40, "message_validate_vendor: vendor ID seen"));
+#if defined (USE_DPD)
+ dpd_check_vendor_payload(msg, p);
+#endif
+#if defined (USE_NAT_TRAVERSAL)
+ nat_t_check_vendor_payload(msg, p);
+#endif
+ if (!(p->flags & PL_MARK))
+ LOG_DBG((LOG_MESSAGE, 40, "message_validate_vendor: "
+ "vendor ID seen"));
return 0;
}
@@ -1134,7 +1196,7 @@ message_recv(struct message *msg)
* made nicer.
*/
setup_isakmp_sa = zero_test(buf + ISAKMP_HDR_RCOOKIE_OFF,
- ISAKMP_HDR_RCOOKIE_LEN);
+ ISAKMP_HDR_RCOOKIE_LEN);
if (setup_isakmp_sa) {
/*
* This might be a retransmission of a former ISAKMP SA setup
@@ -1445,9 +1507,7 @@ message_send(struct message *msg)
* has left the queue, don't queue it again, as it will result
* in a circular list.
*/
- q = msg->flags & MSG_PRIORITIZED ? &msg->transport->prio_sendq :
- &msg->transport->sendq;
-
+ q = msg->transport->vtbl->get_queue(msg);
for (m = TAILQ_FIRST(q); m; m = TAILQ_NEXT(m, link))
if (m == msg) {
LOG_DBG((LOG_MESSAGE, 60,
@@ -1880,12 +1940,9 @@ message_check_duplicate(struct message *msg)
*/
if (exchange->last_sent) {
if (exchange->last_sent == exchange->in_transit) {
- if (exchange->in_transit->flags & MSG_PRIORITIZED)
- TAILQ_REMOVE(&exchange->in_transit->transport->prio_sendq,
- exchange->in_transit, link);
- else
- TAILQ_REMOVE(&exchange->in_transit->transport->sendq,
- exchange->in_transit, link);
+ struct message *m = exchange->in_transit;
+ TAILQ_REMOVE(m->transport->vtbl->get_queue(m), m,
+ link);
exchange->in_transit = 0;
}
message_free(exchange->last_sent);
diff --git a/sbin/isakmpd/nat_traversal.c b/sbin/isakmpd/nat_traversal.c
new file mode 100644
index 00000000000..83713df201f
--- /dev/null
+++ b/sbin/isakmpd/nat_traversal.c
@@ -0,0 +1,384 @@
+/* $OpenBSD: nat_traversal.c,v 1.1 2004/06/20 15:24:05 ho Exp $ */
+
+/*
+ * Copyright (c) 2004 Håkan Olsson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <stdlib.h>
+
+#include "sysdep.h"
+
+#include "exchange.h"
+#include "hash.h"
+#include "ipsec.h"
+#include "isakmp_fld.h"
+#include "isakmp_num.h"
+#include "ipsec_num.h"
+#include "hash.h"
+#include "log.h"
+#include "message.h"
+#include "nat_traversal.h"
+#include "prf.h"
+#include "sa.h"
+#include "transport.h"
+#include "util.h"
+
+/*
+ * XXX According to draft-ietf-ipsec-nat-t-ike-07.txt, the NAT-T
+ * capability of the other peer is determined by a particular vendor ID
+ * sent as the first message. This vendor ID string is supposed to be a
+ * MD5 hash of "RFC XXXX", where XXXX is the future RFC number.
+ *
+ * These seem to be the "well" known variants of this string in use by
+ * products today.
+ */
+static const char *isakmp_nat_t_cap_text[] = {
+ "draft-ietf-ipsec-nat-t-ike-00", /* V1 (XXX: may be obsolete) */
+ "draft-ietf-ipsec-nat-t-ike-02\n", /* V2 */
+ "draft-ietf-ipsec-nat-t-ike-03", /* V3 */
+#ifdef notyet
+ "RFC XXXX",
+#endif
+};
+
+/* The MD5 hashes of the above strings is put in this array. */
+static char **nat_t_hashes;
+static size_t nat_t_hashsize;
+
+static int nat_t_setup_hashes(void);
+static int nat_t_add_vendor_payload(struct message *, char *);
+static int nat_t_add_nat_d(struct message *, struct sockaddr *);
+static int nat_t_match_nat_d_payload(struct message *, struct sockaddr *);
+
+void
+nat_t_init(void)
+{
+ nat_t_hashes = (char **)NULL;
+}
+
+/* Generate the NAT-T capability marker hashes. Executed only once. */
+static int
+nat_t_setup_hashes(void)
+{
+ struct hash *hash;
+ int n = sizeof isakmp_nat_t_cap_text / sizeof isakmp_nat_t_cap_text[0];
+ int i;
+
+ /* The draft says to use MD5. */
+ hash = hash_get(HASH_MD5);
+ if (!hash) {
+ /* Should never happen. */
+ log_print("nat_t_setup_hashes: "
+ "could not find MD5 hash structure!");
+ return -1;
+ }
+ nat_t_hashsize = hash->hashsize;
+
+ /* Allocate one more than is necessary, i.e NULL terminated. */
+ nat_t_hashes = (char **)calloc((size_t)(n + 1), sizeof(char *));
+ if (!nat_t_hashes) {
+ log_error("nat_t_setup_hashes: calloc (%lu,%lu) failed",
+ (unsigned long)n, (unsigned long)sizeof(char *));
+ return -1;
+ }
+
+ /* Populate with hashes. */
+ for (i = 0; i < n; i++) {
+ nat_t_hashes[i] = (char *)malloc(nat_t_hashsize);
+ if (!nat_t_hashes[i]) {
+ log_error("nat_t_setup_hashes: malloc (%lu) failed",
+ (unsigned long)nat_t_hashsize);
+ goto errout;
+ }
+
+ hash->Init(hash->ctx);
+ hash->Update(hash->ctx,
+ (unsigned char *)isakmp_nat_t_cap_text[i],
+ strlen(isakmp_nat_t_cap_text[i]));
+ hash->Final(nat_t_hashes[i], hash->ctx);
+
+ LOG_DBG((LOG_EXCHANGE, 50, "nat_t_setup_hashes: "
+ "MD5(\"%s\") (%d bytes)", isakmp_nat_t_cap_text[i],
+ nat_t_hashsize));
+ LOG_DBG_BUF((LOG_EXCHANGE, 50, "nat_t_setup_hashes",
+ nat_t_hashes[i], nat_t_hashsize));
+ }
+
+ return 0;
+
+ errout:
+ for (i = 0; i < n; i++)
+ if (nat_t_hashes[i])
+ free(nat_t_hashes[i]);
+ free(nat_t_hashes);
+ nat_t_hashes = NULL;
+ return -1;
+}
+
+/* Add one NAT-T VENDOR payload. */
+static int
+nat_t_add_vendor_payload(struct message *msg, char *hash)
+{
+ size_t buflen = nat_t_hashsize + ISAKMP_GEN_SZ;
+ u_int8_t *buf;
+
+ buf = malloc(buflen);
+ if (!buf) {
+ log_error("nat_t_add_vendor_payload: malloc (%lu) failed",
+ (unsigned long)buflen);
+ return -1;
+ }
+
+ SET_ISAKMP_GEN_LENGTH(buf, buflen);
+ memcpy(buf + ISAKMP_VENDOR_ID_OFF, hash, nat_t_hashsize);
+ if (message_add_payload(msg, ISAKMP_PAYLOAD_VENDOR, buf, buflen, 1)) {
+ free(buf);
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Add the NAT-T capability markers (VENDOR payloads). */
+int
+nat_t_add_vendor_payloads(struct message *msg)
+{
+ int i = 0;
+
+ if (!nat_t_hashes)
+ if (nat_t_setup_hashes())
+ return 0; /* XXX should this be an error? */
+
+ while (nat_t_hashes[i])
+ if (nat_t_add_vendor_payload(msg, nat_t_hashes[i++]))
+ return -1;
+
+ return 0;
+}
+
+/*
+ * Check an incoming message for NAT-T capability markers.
+ */
+void
+nat_t_check_vendor_payload(struct message *msg, struct payload *p)
+{
+ u_int8_t *pbuf = p->p;
+ size_t vlen;
+ int i = 0;
+
+ /* Already checked? */
+ if (p->flags & PL_MARK ||
+ msg->exchange->flags & EXCHANGE_FLAG_NAT_T_CAP_PEER)
+ return;
+
+ if (!nat_t_hashes)
+ if (nat_t_setup_hashes())
+ return;
+
+ vlen = GET_ISAKMP_GEN_LENGTH(pbuf) - ISAKMP_GEN_SZ;
+ if (vlen != nat_t_hashsize) {
+ LOG_DBG((LOG_EXCHANGE, 50, "nat_t_check_vendor_payload: "
+ "bad size %d != %d", vlen, nat_t_hashsize));
+ return;
+ }
+
+ while (nat_t_hashes[i])
+ if (memcmp(nat_t_hashes[i++], pbuf + ISAKMP_GEN_SZ,
+ vlen) == 0) {
+ /* This peer is NAT-T capable. */
+ msg->exchange->flags |= EXCHANGE_FLAG_NAT_T_CAP_PEER;
+ LOG_DBG((LOG_EXCHANGE, 10,
+ "nat_t_check_vendor_payload: "
+ "NAT-T capable peer detected"));
+ p->flags |= PL_MARK;
+ return;
+ }
+
+ return;
+}
+
+/* Generate the NAT-D payload hash : HASH(CKY-I | CKY-R | IP | Port). */
+static u_int8_t *
+nat_t_generate_nat_d_hash(struct message *msg, struct sockaddr *sa,
+ size_t *hashlen)
+{
+ struct ipsec_exch *ie = (struct ipsec_exch *)msg->exchange->data;
+ struct hash *hash;
+ struct prf *prf;
+ u_int8_t *res;
+ in_port_t port;
+ int prf_type = PRF_HMAC; /* XXX */
+
+ hash = hash_get(ie->hash->type);
+ if (hash == NULL) {
+ log_print ("nat_t_generate_nat_d_hash: no hash");
+ return NULL;
+ }
+
+ prf = prf_alloc(prf_type, hash->type, msg->exchange->cookies,
+ ISAKMP_HDR_COOKIES_LEN);
+ if(!prf) {
+ log_print("nat_t_generate_nat_d_hash: prf_alloc failed");
+ return NULL;
+ }
+
+ *hashlen = prf->blocksize;
+ res = (u_int8_t *)malloc((unsigned long)*hashlen);
+ if (!res) {
+ log_print("nat_t_generate_nat_d_hash: malloc (%lu) failed",
+ (unsigned long)*hashlen);
+ prf_free(prf);
+ *hashlen = 0;
+ return NULL;
+ }
+
+ port = sockaddr_port(sa);
+ memset(res, 0, *hashlen);
+
+ prf->Update(prf->prfctx, sockaddr_addrdata(sa), sockaddr_addrlen(sa));
+ prf->Update(prf->prfctx, (unsigned char *)&port, sizeof port);
+ prf->Final(res, prf->prfctx);
+ prf_free (prf);
+
+ return res;
+}
+
+/* Add a NAT-D payload to our message. */
+static int
+nat_t_add_nat_d(struct message *msg, struct sockaddr *sa)
+{
+ u_int8_t *hbuf, *buf;
+ size_t hbuflen, buflen;
+
+ hbuf = nat_t_generate_nat_d_hash(msg, sa, &hbuflen);
+ if (!hbuf) {
+ log_print("nat_t_add_nat_d: NAT-D hash gen failed");
+ return -1;
+ }
+
+ buflen = ISAKMP_NAT_D_DATA_OFF + hbuflen;
+ buf = malloc(buflen);
+ if (!buf) {
+ log_error("nat_t_add_nat_d: malloc (%lu) failed",
+ (unsigned long)buflen);
+ free(hbuf);
+ return -1;
+ }
+
+ SET_ISAKMP_GEN_LENGTH(buf, buflen);
+ memcpy(buf + ISAKMP_NAT_D_DATA_OFF, hbuf, hbuflen);
+ free(hbuf);
+
+ if (message_add_payload(msg, ISAKMP_PAYLOAD_NAT_D, buf, buflen, 1)) {
+ free(buf);
+ return -1;
+ }
+
+ return 0;
+}
+
+/* We add two NAT-D payloads, one each for src and dst. */
+int
+nat_t_exchange_add_nat_d(struct message *msg)
+{
+ struct sockaddr *sa;
+
+ msg->transport->vtbl->get_src(msg->transport, &sa);
+ if (nat_t_add_nat_d(msg, sa))
+ return -1;
+
+ msg->transport->vtbl->get_dst(msg->transport, &sa);
+ if (nat_t_add_nat_d(msg, sa))
+ return -1;
+
+ return 0;
+}
+
+/* Generate and match a NAT-D hash against the NAT-D payload (pl.) data. */
+static int
+nat_t_match_nat_d_payload(struct message *msg, struct sockaddr *sa)
+{
+ struct payload *p;
+ u_int8_t *hbuf;
+ size_t hbuflen;
+ int found = 0;
+
+ hbuf = nat_t_generate_nat_d_hash(msg, sa, &hbuflen);
+ if (!hbuf)
+ return 0;
+
+ for (p = TAILQ_FIRST(&msg->payload[ISAKMP_PAYLOAD_NAT_D]); p;
+ p = TAILQ_NEXT(p, link)) {
+ if (GET_ISAKMP_GEN_LENGTH (p->p) !=
+ hbuflen + ISAKMP_NAT_D_DATA_OFF)
+ continue;
+
+ if (memcmp(p->p + ISAKMP_NAT_D_DATA_OFF, hbuf, hbuflen) == 0) {
+ found++;
+ break;
+ }
+ }
+ free(hbuf);
+ return found;
+}
+
+/*
+ * Check if we need to activate NAT-T, and if we need to send keepalive
+ * messages to the other side, i.e if we are a nat:ed peer.
+ */
+int
+nat_t_exchange_check_nat_d(struct message *msg)
+{
+ struct sockaddr *sa;
+ int outgoing_path_is_clear, incoming_path_is_clear;
+
+ /* Assume trouble, i.e NAT-boxes in our path. */
+ outgoing_path_is_clear = incoming_path_is_clear = 0;
+
+ msg->transport->vtbl->get_src(msg->transport, &sa);
+ if (nat_t_match_nat_d_payload(msg, sa))
+ outgoing_path_is_clear = 1;
+
+ msg->transport->vtbl->get_dst(msg->transport, &sa);
+ if (nat_t_match_nat_d_payload(msg, sa))
+ incoming_path_is_clear = 1;
+
+ if (outgoing_path_is_clear && incoming_path_is_clear) {
+ LOG_DBG((LOG_EXCHANGE, 40, "nat_t_exchange_check_nat_d: "
+ "no NAT"));
+ return 0; /* No NAT-T required. */
+ }
+
+ /* NAT-T handling required. */
+ msg->exchange->flags |= EXCHANGE_FLAG_NAT_T_ENABLE;
+
+ if (!outgoing_path_is_clear) {
+ msg->exchange->flags |= EXCHANGE_FLAG_NAT_T_KEEPALIVE;
+ LOG_DBG((LOG_EXCHANGE, 10, "nat_t_exchange_check_nat_d: "
+ "NAT detected, we're behind it"));
+ } else
+ LOG_DBG ((LOG_EXCHANGE, 10,
+ "nat_t_exchange_check_nat_d: NAT detected"));
+ return 1;
+}
diff --git a/sbin/isakmpd/nat_traversal.h b/sbin/isakmpd/nat_traversal.h
new file mode 100644
index 00000000000..ecb5d6008b1
--- /dev/null
+++ b/sbin/isakmpd/nat_traversal.h
@@ -0,0 +1,36 @@
+/* $OpenBSD: nat_traversal.h,v 1.1 2004/06/20 15:24:05 ho Exp $ */
+
+/*
+ * Copyright (c) 2004 Håkan Olsson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _NAT_TRAVERSAL_H_
+#define _NAT_TRAVERSAL_H_
+
+void nat_t_init(void);
+int nat_t_add_vendor_payloads(struct message *);
+void nat_t_check_vendor_payload(struct message *, struct payload *);
+int nat_t_exchange_add_nat_d(struct message *);
+int nat_t_exchange_check_nat_d(struct message *);
+
+#endif /* _NAT_TRAVERSAL_H_ */
diff --git a/sbin/isakmpd/policy.c b/sbin/isakmpd/policy.c
index 5dc1752997d..c9ff0eab6d5 100644
--- a/sbin/isakmpd/policy.c
+++ b/sbin/isakmpd/policy.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: policy.c,v 1.74 2004/06/14 09:55:42 ho Exp $ */
+/* $OpenBSD: policy.c,v 1.75 2004/06/20 15:24:05 ho Exp $ */
/* $EOM: policy.c,v 1.49 2000/10/24 13:33:39 niklas Exp $ */
/*
@@ -509,6 +509,23 @@ policy_callback(char *name)
comp_encapsulation = "tunnel";
break;
}
+#if defined (USE_NAT_TRAVERSAL)
+ else if (decode_16(value) == IPSEC_ENCAP_UDP_ENCAP_TUNNEL)
+ switch (proto->proto) {
+ case IPSEC_PROTO_IPSEC_AH:
+ ah_encapsulation = "udp-encap-tunnel";
+ break;
+
+ case IPSEC_PROTO_IPSEC_ESP:
+ esp_encapsulation = "udp-encap-tunnel";
+ break;
+
+ case IPSEC_PROTO_IPCOMP:
+ comp_encapsulation = "udp-encap-tunnel";
+ break;
+ }
+ /* XXX IPSEC_ENCAP_UDP_ENCAP_TRANSPORT */
+#endif
else
switch (proto->proto) {
case IPSEC_PROTO_IPSEC_AH:
diff --git a/sbin/isakmpd/transport.c b/sbin/isakmpd/transport.c
index 98608a3648f..989d6a5ebaa 100644
--- a/sbin/isakmpd/transport.c
+++ b/sbin/isakmpd/transport.c
@@ -1,9 +1,9 @@
-/* $OpenBSD: transport.c,v 1.26 2004/06/14 09:55:42 ho Exp $ */
+/* $OpenBSD: transport.c,v 1.27 2004/06/20 15:24:05 ho Exp $ */
/* $EOM: transport.c,v 1.43 2000/10/10 12:36:39 provos Exp $ */
/*
* Copyright (c) 1998, 1999 Niklas Hallqvist. All rights reserved.
- * Copyright (c) 2001 Håkan Olsson. All rights reserved.
+ * Copyright (c) 2001, 2004 Håkan Olsson. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -43,6 +43,7 @@
#include "sa.h"
#include "timer.h"
#include "transport.h"
+#include "virtual.h"
/* If no retransmit limit is given, use this as a default. */
#define RETRANSMIT_DEFAULT 10
@@ -51,14 +52,15 @@ LIST_HEAD(transport_list, transport) transport_list;
LIST_HEAD(transport_method_list, transport_vtbl) transport_method_list;
/* Call the reinit function of the various transports. */
- void
- transport_reinit(void)
+void
+transport_reinit(void)
{
struct transport_vtbl *method;
for (method = LIST_FIRST(&transport_method_list); method;
method = LIST_NEXT(method, link))
- method->reinit();
+ if (method->reinit)
+ method->reinit();
}
/* Initialize the transport maintenance module. */
@@ -71,12 +73,16 @@ transport_init(void)
/* Register another transport T. */
void
-transport_add(struct transport *t)
+transport_setup(struct transport *t, int toplevel)
{
- LOG_DBG((LOG_TRANSPORT, 70, "transport_add: adding %p", t));
- TAILQ_INIT(&t->sendq);
- TAILQ_INIT(&t->prio_sendq);
- LIST_INSERT_HEAD(&transport_list, t, link);
+ LOG_DBG((LOG_TRANSPORT, 70, "transport_setup: adding %p", t));
+ if (toplevel == 0)
+ LIST_INSERT_HEAD(&transport_list, t, link);
+ else {
+ /* Only the toplevel (virtual) transport has the sendqueues. */
+ TAILQ_INIT(&t->sendq);
+ TAILQ_INIT(&t->prio_sendq);
+ }
t->flags = 0;
t->refcnt = 0;
}
@@ -87,8 +93,8 @@ transport_reference(struct transport *t)
{
t->refcnt++;
LOG_DBG((LOG_TRANSPORT, 95,
- "transport_reference: transport %p now has %d references", t,
- t->refcnt));
+ "transport_reference: transport %p now has %d references", t,
+ t->refcnt));
}
/*
@@ -98,19 +104,28 @@ void
transport_release(struct transport *t)
{
LOG_DBG((LOG_TRANSPORT, 95,
- "transport_release: transport %p had %d references", t,
- t->refcnt));
+ "transport_release: transport %p had %d references", t,
+ t->refcnt));
if (--t->refcnt)
return;
LOG_DBG((LOG_TRANSPORT, 70, "transport_release: freeing %p", t));
- LIST_REMOVE(t, link);
+ if (t->virtual) {
+ struct virtual_transport *v =
+ (struct virtual_transport *)t->virtual;
+ if (v->main == t)
+ v->main = 0;
+ else
+ v->encap = 0;
+ LIST_REMOVE(t, link);
+ }
t->vtbl->remove(t);
}
void
transport_report(void)
{
+ struct virtual_transport *v;
struct transport *t;
struct message *msg;
@@ -119,18 +134,26 @@ transport_report(void)
"transport_report: transport %p flags %x refcnt %d", t,
t->flags, t->refcnt));
+ /* XXX Report sth on the virtual transport? */
t->vtbl->report(t);
/*
* This is the reason message_dump_raw lives outside
* message.c.
*/
- for (msg = TAILQ_FIRST(&t->prio_sendq); msg;
- msg = TAILQ_NEXT(msg, link))
- message_dump_raw("udp_report", msg, LOG_REPORT);
-
- for (msg = TAILQ_FIRST(&t->sendq); msg; msg = TAILQ_NEXT(msg, link))
- message_dump_raw("udp_report", msg, LOG_REPORT);
+ v = (struct virtual_transport *)t->virtual;
+ if ((v->encap_is_active && v->encap == t) ||
+ (!v->encap_is_active && v->main == t)) {
+ for (msg = TAILQ_FIRST(&t->virtual->prio_sendq); msg;
+ msg = TAILQ_NEXT(msg, link))
+ message_dump_raw("udp_report(prio)", msg,
+ LOG_REPORT);
+
+ for (msg = TAILQ_FIRST(&t->virtual->sendq); msg;
+ msg = TAILQ_NEXT(msg, link))
+ message_dump_raw("udp_report", msg,
+ LOG_REPORT);
+ }
}
}
@@ -140,7 +163,7 @@ transport_prio_sendqs_empty(void)
struct transport *t;
for (t = LIST_FIRST(&transport_list); t; t = LIST_NEXT(t, link))
- if (TAILQ_FIRST(&t->prio_sendq))
+ if (TAILQ_FIRST(&t->virtual->prio_sendq))
return 0;
return 1;
}
@@ -152,7 +175,7 @@ transport_method_add(struct transport_vtbl *t)
LIST_INSERT_HEAD(&transport_method_list, t, link);
}
-/* Apply a function FUNC on all registered transports. */
+/* Apply a function FUNC on all registered (non-toplevel) transports. */
void
transport_map(void (*func) (struct transport *))
{
@@ -170,15 +193,19 @@ transport_map(void (*func) (struct transport *))
int
transport_fd_set(fd_set * fds)
{
- int n;
- int max = -1;
struct transport *t;
+ int n;
+ int max = -1;
for (t = LIST_FIRST(&transport_list); t; t = LIST_NEXT(t, link))
- if (t->flags & TRANSPORT_LISTEN) {
- n = t->vtbl->fd_set(t, fds, 1);
+ if (t->virtual->flags & TRANSPORT_LISTEN) {
+ n = t->virtual->vtbl->fd_set(t->virtual, fds, 1);
if (n > max)
max = n;
+
+ LOG_DBG((LOG_TRANSPORT,95,"transport_fd_set: "
+ "transport %p (virtual %p) fd %d", t,
+ t->virtual, n));
}
return max + 1;
}
@@ -192,13 +219,17 @@ transport_fd_set(fd_set * fds)
int
transport_pending_wfd_set(fd_set * fds)
{
- int n;
- int max = -1;
struct transport *t;
+ int n;
+ int max = -1;
for (t = LIST_FIRST(&transport_list); t; t = LIST_NEXT(t, link)) {
- if (TAILQ_FIRST(&t->sendq) || TAILQ_FIRST(&t->prio_sendq)) {
- n = t->vtbl->fd_set(t, fds, 1);
+ if (TAILQ_FIRST(&t->virtual->sendq) ||
+ TAILQ_FIRST(&t->virtual->prio_sendq)) {
+ n = t->virtual->vtbl->fd_set(t->virtual, fds, 1);
+ LOG_DBG((LOG_TRANSPORT,95,"transport_pending_wfd_set: "
+ "transport %p (virtual %p) fd %d pending", t,
+ t->virtual, n));
if (n > max)
max = n;
}
@@ -211,14 +242,17 @@ transport_pending_wfd_set(fd_set * fds)
* incoming message and start processing it.
*/
void
-transport_handle_messages(fd_set * fds)
+transport_handle_messages(fd_set *fds)
{
struct transport *t;
- for (t = LIST_FIRST(&transport_list); t; t = LIST_NEXT(t, link))
+ for (t = LIST_FIRST(&transport_list); t; t = LIST_NEXT(t, link)) {
if ((t->flags & TRANSPORT_LISTEN) &&
- (*t->vtbl->fd_isset) (t, fds))
- (*t->vtbl->handle_message) (t);
+ (*t->virtual->vtbl->fd_isset)(t->virtual, fds)) {
+ (*t->virtual->vtbl->handle_message)(t->virtual);
+ (*t->virtual->vtbl->fd_set)(t->virtual, fds, 0);
+ }
+ }
}
/*
@@ -243,21 +277,26 @@ transport_send_messages(fd_set * fds)
* Reference all transports first so noone will disappear while in
* use.
*/
- for (t = LIST_FIRST(&transport_list); t; t = LIST_NEXT(t, link))
+ for (t = LIST_FIRST(&transport_list); t; t = LIST_NEXT(t, link)) {
+ transport_reference(t->virtual);
transport_reference(t);
-
+ }
+
for (t = LIST_FIRST(&transport_list); t; t = LIST_NEXT(t, link)) {
- if ((TAILQ_FIRST(&t->sendq) || TAILQ_FIRST(&t->prio_sendq))
- && t->vtbl->fd_isset(t, fds)) {
- t->vtbl->fd_set(t, fds, 0);
+ if ((TAILQ_FIRST(&t->virtual->sendq) ||
+ TAILQ_FIRST(&t->virtual->prio_sendq)) &&
+ t->virtual->vtbl->fd_isset(t->virtual, fds)) {
+ /* Remove fd bit. */
+ t->virtual->vtbl->fd_set(t->virtual, fds, 0);
/* Prefer a message from the prioritized sendq. */
- if (TAILQ_FIRST(&t->prio_sendq)) {
- msg = TAILQ_FIRST(&t->prio_sendq);
- TAILQ_REMOVE(&t->prio_sendq, msg, link);
+ if (TAILQ_FIRST(&t->virtual->prio_sendq)) {
+ msg = TAILQ_FIRST(&t->virtual->prio_sendq);
+ TAILQ_REMOVE(&t->virtual->prio_sendq, msg,
+ link);
} else {
- msg = TAILQ_FIRST(&t->sendq);
- TAILQ_REMOVE(&t->sendq, msg, link);
+ msg = TAILQ_FIRST(&t->virtual->sendq);
+ TAILQ_REMOVE(&t->virtual->sendq, msg, link);
}
msg->flags &= ~MSG_IN_TRANSIT;
@@ -269,23 +308,22 @@ transport_send_messages(fd_set * fds)
* hoping that the retransmit will go better.
* XXX Consider a retry/fatal error discriminator.
*/
- t->vtbl->send_message(msg);
+ t->virtual->vtbl->send_message(msg, 0);
msg->xmits++;
/*
* This piece of code has been proven to be quite
- * delicate. Think twice for before altering. Here's
- * an outline: If this message is not the one which
- * finishes an exchange, check if we have reached the
- * number of retransmit before queuing it up for
- * another.
+ * delicate. Think twice for before altering.
+ * Here's an outline:
+ *
+ * If this message is not the one which finishes an
+ * exchange, check if we have reached the number of
+ * retransmit before queuing it up for another.
*
* If it is a finishing message we still may have to
* keep it around for an on-demand retransmit when
* seeing a duplicate of our peer's previous message.
*
- * If we have no previous message from our peer, we
- * need not to keep the message around.
*/
if ((msg->flags & MSG_LAST) == 0) {
if (msg->xmits > conf_get_num("General",
@@ -357,9 +395,9 @@ transport_send_messages(fd_set * fds)
/*
* If this is not a retransmit call post-send
* functions that allows parallel work to be done
- * while the network and peer does their share of the
- * job. Note that a post-send function may take away
- * the exchange we belong to, but only if no
+ * while the network and peer does their share of
+ * the job. Note that a post-send function may take
+ * away the exchange we belong to, but only if no
* retransmits are possible.
*/
if (msg->xmits == 1)
@@ -372,6 +410,7 @@ transport_send_messages(fd_set * fds)
for (t = LIST_FIRST(&transport_list); t; t = next) {
next = LIST_NEXT(t, link);
+ transport_release(t->virtual);
transport_release(t);
}
}
diff --git a/sbin/isakmpd/transport.h b/sbin/isakmpd/transport.h
index 809dea118ca..0a68c73fd24 100644
--- a/sbin/isakmpd/transport.h
+++ b/sbin/isakmpd/transport.h
@@ -1,9 +1,9 @@
-/* $OpenBSD: transport.h,v 1.14 2004/04/15 18:39:26 deraadt Exp $ */
+/* $OpenBSD: transport.h,v 1.15 2004/06/20 15:24:05 ho Exp $ */
/* $EOM: transport.h,v 1.16 2000/07/17 18:57:59 provos Exp $ */
/*
* Copyright (c) 1998, 1999 Niklas Hallqvist. All rights reserved.
- * Copyright (c) 2001 Håkan Olsson. All rights reserved.
+ * Copyright (c) 2001, 2004 Håkan Olsson. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -80,7 +80,7 @@ struct transport_vtbl {
void (*handle_message) (struct transport *);
/* Send a message through the outgoing pipe. */
- int (*send_message) (struct message *);
+ int (*send_message) (struct message *, struct transport *);
/*
* Fill out a sockaddr structure with the transport's destination end's
@@ -98,6 +98,16 @@ struct transport_vtbl {
* Return a string with decoded src and dst information
*/
char *(*decode_ids) (struct transport *);
+
+ /*
+ * Clone a transport for outbound use.
+ */
+ struct transport *(*clone) (struct transport *, struct sockaddr *);
+
+ /*
+ * Locate the correct sendq to use for outbound messages.
+ */
+ struct msg_head *(*get_queue) (struct message *);
};
struct transport {
@@ -120,8 +130,11 @@ struct transport {
/* Flags describing the transport. */
int flags;
- /* References counter. */
+ /* Reference counter. */
int refcnt;
+
+ /* Pointer to parent virtual transport, if any. */
+ struct transport *virtual;
};
/* Set if this is a transport we want to listen on. */
@@ -129,7 +142,6 @@ struct transport {
/* Used for mark-and-sweep-type garbage collection of transports */
#define TRANSPORT_MARK 2
-extern void transport_add(struct transport *);
extern struct transport *transport_create(char *, char *);
extern int transport_fd_set(fd_set *);
extern void transport_handle_messages(fd_set *);
@@ -137,10 +149,11 @@ extern void transport_init(void);
extern void transport_map(void (*) (struct transport *));
extern void transport_method_add(struct transport_vtbl *);
extern int transport_pending_wfd_set(fd_set *);
+extern int transport_prio_sendqs_empty(void);
extern void transport_reference(struct transport *);
+extern void transport_reinit(void);
extern void transport_release(struct transport *);
extern void transport_report(void);
extern void transport_send_messages(fd_set *);
-extern void transport_reinit(void);
-extern int transport_prio_sendqs_empty(void);
+extern void transport_setup(struct transport *, int);
#endif /* _TRANSPORT_H_ */
diff --git a/sbin/isakmpd/udp.c b/sbin/isakmpd/udp.c
index 42e5e736595..cee6630c6b6 100644
--- a/sbin/isakmpd/udp.c
+++ b/sbin/isakmpd/udp.c
@@ -1,9 +1,10 @@
-/* $OpenBSD: udp.c,v 1.74 2004/06/17 19:36:36 hshoexer Exp $ */
+/* $OpenBSD: udp.c,v 1.75 2004/06/20 15:24:05 ho Exp $ */
/* $EOM: udp.c,v 1.57 2001/01/26 10:09:57 niklas Exp $ */
/*
* Copyright (c) 1998, 1999, 2001 Niklas Hallqvist. All rights reserved.
* Copyright (c) 2000 Angelos D. Keromytis. All rights reserved.
+ * Copyright (c) 2003, 2004 Håkan Olsson. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -59,6 +60,7 @@
#include "transport.h"
#include "udp.h"
#include "util.h"
+#include "virtual.h"
#define UDP_SIZE 65536
@@ -67,65 +69,47 @@
#define SO_REUSEPORT SO_REUSEADDR
#endif
-struct udp_transport {
- struct transport transport;
- struct sockaddr *src, *dst;
- int s;
- LIST_ENTRY(udp_transport) link;
-};
+/* These are reused by udp_encap.c, thus not 'static' here. */
+struct transport *udp_clone(struct transport *, struct sockaddr *);
+void udp_get_dst(struct transport *, struct sockaddr **);
+void udp_get_src(struct transport *, struct sockaddr **);
+char *udp_decode_ids(struct transport *);
-static struct transport *udp_clone(struct udp_transport *, struct sockaddr *);
static struct transport *udp_create(char *);
-static void udp_reinit(void);
static void udp_remove(struct transport *);
static void udp_report(struct transport *);
-static int udp_fd_set(struct transport *, fd_set *, int);
-static int udp_fd_isset(struct transport *, fd_set *);
static void udp_handle_message(struct transport *);
static struct transport *udp_make(struct sockaddr *);
-static int udp_send_message(struct message *);
-static void udp_get_dst(struct transport *, struct sockaddr **);
-static void udp_get_src(struct transport *, struct sockaddr **);
-static char *udp_decode_ids(struct transport *);
+static int udp_send_message(struct message *, struct transport *);
#if 0
static in_port_t udp_decode_port(char *);
#endif
static struct transport_vtbl udp_transport_vtbl = {
- {0}, "udp",
+ {0}, "udp_physical",
udp_create,
- udp_reinit,
+ 0,
udp_remove,
udp_report,
- udp_fd_set,
- udp_fd_isset,
+ 0,
+ 0,
udp_handle_message,
udp_send_message,
udp_get_dst,
udp_get_src,
- udp_decode_ids
+ udp_decode_ids,
+ udp_clone,
+ 0
};
-/* A list of UDP transports we listen for messages on. */
-static
-LIST_HEAD(udp_listen_list, udp_transport) udp_listen_list;
-
-static struct transport *default_transport, *default_transport6;
char *udp_default_port = 0;
char *udp_bind_port = 0;
int bind_family = 0;
-/* Find an UDP transport listening on ADDR:PORT. */
-static struct udp_transport *
-udp_listen_lookup(struct sockaddr *addr)
+void
+udp_init(void)
{
- struct udp_transport *u;
-
- for (u = LIST_FIRST(&udp_listen_list); u; u = LIST_NEXT(u, link))
- if (sysdep_sa_len(u->src) == sysdep_sa_len(addr) &&
- memcmp(u->src, addr, sysdep_sa_len(addr)) == 0)
- return u;
- return 0;
+ transport_method_add(&udp_transport_vtbl);
}
/* Create a UDP transport structure bound to LADDR just for listening. */
@@ -133,11 +117,12 @@ static struct transport *
udp_make(struct sockaddr *laddr)
{
struct udp_transport *t = 0;
- int s, on, wildcardaddress = 0;
+ int s, on, wildcardaddress = 0;
+ char *tstr;
t = calloc(1, sizeof *t);
if (!t) {
- log_print("udp_make: malloc (%lu) failed",
+ log_print("udp_make: calloc (1, %lu) failed",
(unsigned long)sizeof *t);
free(laddr);
return 0;
@@ -184,8 +169,6 @@ udp_make(struct sockaddr *laddr)
}
t->transport.vtbl = &udp_transport_vtbl;
if (monitor_bind(s, t->src, sysdep_sa_len(t->src))) {
- char *tstr;
-
if (sockaddr2text(t->src, &tstr, 0))
log_error("udp_make: bind (%d, %p, %lu)", s, &t->src,
(unsigned long)sizeof t->src);
@@ -197,7 +180,17 @@ udp_make(struct sockaddr *laddr)
goto err;
}
t->s = s;
- transport_add(&t->transport);
+ if (sockaddr2text(t->src, &tstr, 0))
+ LOG_DBG((LOG_MISC, 20, "udp_make: "
+ "transport %p socket %d family %d", t, s,
+ t->src->sa_family == AF_INET ? 4 : 6));
+ else {
+ LOG_DBG((LOG_MISC, 20, "udp_make: "
+ "transport %p socket %d ip %s port %d", t, s,
+ tstr, ntohs(sockaddr_port(t->src))));
+ free (tstr);
+ }
+ transport_setup(&t->transport, 0);
transport_reference(&t->transport);
t->transport.flags |= TRANSPORT_LISTEN;
return &t->transport;
@@ -214,9 +207,10 @@ err:
}
/* Clone a listen transport U, record a destination RADDR for outbound use. */
-static struct transport *
-udp_clone(struct udp_transport *u, struct sockaddr *raddr)
+struct transport *
+udp_clone(struct transport *ut, struct sockaddr *raddr)
{
+ struct udp_transport *u = (struct udp_transport *)ut;
struct udp_transport *u2;
struct transport *t;
@@ -250,7 +244,7 @@ udp_clone(struct udp_transport *u, struct sockaddr *raddr)
memcpy(u2->dst, raddr, sysdep_sa_len(raddr));
t->flags &= ~TRANSPORT_LISTEN;
- transport_add(t);
+ transport_setup(t, 0);
return t;
}
@@ -260,7 +254,7 @@ udp_clone(struct udp_transport *u, struct sockaddr *raddr)
* that specific port. Add the polymorphic transport structure to the
* system-wide pools of known ISAKMP transports.
*/
-static struct transport *
+struct transport *
udp_bind(const struct sockaddr *addr)
{
struct sockaddr *src;
@@ -274,187 +268,25 @@ udp_bind(const struct sockaddr *addr)
}
/*
- * When looking at a specific network interface address, if it's an INET one,
- * create an UDP server socket bound to it.
- * Return 0 if successful, -1 otherwise.
- */
-static int
-udp_bind_if(char *ifname, struct sockaddr *if_addr, void *arg)
-{
- char *port = (char *) arg, *addr_str, *ep;
- struct sockaddr_storage saddr_st;
- struct sockaddr *saddr = (struct sockaddr *) & saddr_st;
- struct conf_list *listen_on;
- struct udp_transport *u;
- struct conf_list_node *address;
- struct sockaddr *addr;
- struct transport *t;
- struct ifreq flags_ifr;
- int s, error;
- long lport;
-
- /*
- * Well, UDP is an internet protocol after all so drop other ifreqs.
- */
- if ((if_addr->sa_family != AF_INET ||
- sysdep_sa_len(if_addr) != sizeof(struct sockaddr_in)) &&
- (if_addr->sa_family != AF_INET6 ||
- sysdep_sa_len(if_addr) != sizeof(struct sockaddr_in6)))
- return 0;
-
- /*
- * Only create sockets for families we should listen to.
- */
- if (bind_family) {
- switch (if_addr->sa_family) {
- case AF_INET:
- if ((bind_family & BIND_FAMILY_INET4) == 0)
- return 0;
- break;
- case AF_INET6:
- if ((bind_family & BIND_FAMILY_INET6) == 0)
- return 0;
- break;
- default:
- return 0;
- }
- }
-
- /*
- * These special addresses are not useable as they have special meaning
- * in the IP stack.
- */
- if (if_addr->sa_family == AF_INET &&
- (((struct sockaddr_in *)if_addr)->sin_addr.s_addr == INADDR_ANY ||
- (((struct sockaddr_in *)if_addr)->sin_addr.s_addr == INADDR_NONE)))
- return 0;
-
- /*
- * Go through the list of transports and see if we already have this
- * address bound. If so, unmark the transport and skip it; this allows
- * us to call this function when we suspect a new address has appeared.
- */
- if (sysdep_sa_len(if_addr) > sizeof saddr_st)
- return 0;
- memcpy(saddr, if_addr, sysdep_sa_len(if_addr));
- switch (saddr->sa_family) { /* Add the port number to the sockaddr. */
- case AF_INET:
- ((struct sockaddr_in *)saddr)->sin_port =
- htons(strtol(port, &ep, 10));
- break;
- case AF_INET6:
- ((struct sockaddr_in6 *)saddr)->sin6_port =
- htons(strtol(port, &ep, 10));
- break;
- }
-
- if ((u = udp_listen_lookup(saddr)) != 0) {
- u->transport.flags &= ~TRANSPORT_MARK;
- return 0;
- }
- /* Don't bother with interfaces that are down. */
- s = socket(if_addr->sa_family, SOCK_DGRAM, 0);
- if (s == -1) {
- log_error("udp_bind_if: socket (%d, SOCK_DGRAM, 0) failed",
- if_addr->sa_family);
- return -1;
- }
- strlcpy(flags_ifr.ifr_name, ifname, sizeof flags_ifr.ifr_name);
- if (ioctl(s, SIOCGIFFLAGS, (caddr_t) & flags_ifr) == -1) {
- log_error("udp_bind_if: ioctl (%d, SIOCGIFFLAGS, ...) failed",
- s);
- return -1;
- }
- close(s);
- if (!(flags_ifr.ifr_flags & IFF_UP))
- return 0;
-
- /*
- * Set port.
- * XXX Use getservbyname too.
- */
- lport = strtol(port, &ep, 10);
- if (*ep != '\0' || lport < (long) 0 || lport > (long) USHRT_MAX) {
- log_print("udp_bind_if: "
- "port string \"%s\" not convertible to in_port_t", port);
- return -1;
- }
- switch (if_addr->sa_family) {
- case AF_INET:
- ((struct sockaddr_in *)if_addr)->sin_port = htons(lport);
- break;
- case AF_INET6:
- ((struct sockaddr_in6 *)if_addr)->sin6_port = htons(lport);
- break;
- default:
- log_print("udp_bind_if: unsupported protocol family %d",
- if_addr->sa_family);
- break;
- }
-
- /*
- * If we are explicit about what addresses we can listen to, be sure
- * to respect that option.
- * This is quite wasteful redoing the list-run for every interface,
- * but who cares? This is not an operation that needs to be fast.
- */
- listen_on = conf_get_list("General", "Listen-on");
- if (listen_on) {
- for (address = TAILQ_FIRST(&listen_on->fields); address;
- address = TAILQ_NEXT(address, link)) {
- if (text2sockaddr(address->field, port, &addr)) {
- log_print("udp_bind_if: invalid address %s "
- "in \"Listen-on\"", address->field);
- continue;
- }
- /* If found, take the easy way out. */
- if (memcmp(addr, if_addr, sysdep_sa_len(addr)) == 0) {
- free(addr);
- break;
- }
- free(addr);
- }
- conf_free_list(listen_on);
-
- /*
- * If address is zero then we did not find the address among
- * the ones we should listen to.
- * XXX We do not discover if we do not find our listen
- * addresses... Maybe this should be the other way round.
- */
- if (!address)
- return 0;
- }
- t = udp_bind(if_addr);
- if (!t) {
- error = sockaddr2text(if_addr, &addr_str, 0);
- log_print("udp_bind_if: failed to create a socket on %s:%s",
- error ? "unknown" : addr_str, port);
- if (!error)
- free(addr_str);
- return -1;
- }
- LIST_INSERT_HEAD(&udp_listen_list, (struct udp_transport *)t, link);
- return 0;
-}
-
-/*
* NAME is a section name found in the config database. Setup and return
* a transport useable to talk to the peer specified by that name.
*/
static struct transport *
udp_create(char *name)
{
+ struct virtual_transport *v;
struct udp_transport *u;
- struct transport *rv;
- struct sockaddr *dst, *addr;
+ struct transport *rv, *t;
+ struct sockaddr *dst, *addr;
char *addr_str, *port_str;
+ struct conf_list *addr_list = 0;
+ struct conf_list_node *addr_node;
port_str = conf_get_str(name, "Port");
if (!port_str)
port_str = udp_default_port;
if (!port_str)
- port_str = "500";
+ port_str = UDP_DEFAULT_PORT_STR;
addr_str = conf_get_str(name, "Address");
if (!addr_str) {
@@ -469,18 +301,38 @@ udp_create(char *name)
}
addr_str = conf_get_str(name, "Local-address");
if (!addr_str)
- addr_str = conf_get_str("General", "Listen-on");
- if (!addr_str) {
- if ((dst->sa_family == AF_INET && !default_transport) ||
- (dst->sa_family == AF_INET6 && !default_transport6)) {
+ addr_list = conf_get_list("General", "Listen-on");
+ if (!addr_str && !addr_list) {
+ v = virtual_get_default(dst->sa_family);
+ u = (struct udp_transport *)v->main;
+
+ if (!u) {
log_print("udp_create: no default transport");
rv = 0;
goto ret;
} else {
- /* XXX Ugly! */
- rv = udp_clone((struct udp_transport *)
- (dst->sa_family == AF_INET ? default_transport :
- default_transport6), dst);
+ rv = udp_clone((struct transport *)u, dst);
+ if (rv)
+ rv->vtbl = &udp_transport_vtbl;
+ goto ret;
+ }
+ }
+
+ if (addr_list) {
+ for (addr_node = TAILQ_FIRST(&addr_list->fields);
+ addr_node; addr_node = TAILQ_NEXT(addr_node, link))
+ if (text2sockaddr(addr_node->field, port_str, &addr)
+ == 0) {
+ v = virtual_listen_lookup(addr);
+ free(addr);
+ if (v) {
+ addr_str = addr_node->field;
+ break;
+ }
+ }
+ if (!addr_str) {
+ log_print("udp_create: no matching listener found");
+ rv = 0;
goto ret;
}
}
@@ -490,17 +342,25 @@ udp_create(char *name)
rv = 0;
goto ret;
}
- u = udp_listen_lookup(addr);
+
+ v = virtual_listen_lookup(addr);
free(addr);
- if (!u) {
+ if (!v) {
log_print("udp_create: %s:%s must exist as a listener too",
addr_str, port_str);
rv = 0;
goto ret;
}
- rv = udp_clone(u, dst);
+ t = (struct transport *)v;
+ rv = udp_clone(v->main, dst);
+ if (rv) {
+ rv->vtbl = &udp_transport_vtbl;
+ transport_reference(rv->virtual);
+ }
ret:
+ if (addr_list)
+ conf_free_list(addr_list);
free(dst);
return rv;
}
@@ -517,10 +377,6 @@ udp_remove(struct transport *t)
if (t->flags & TRANSPORT_LISTEN) {
if (u->s >= 0)
close(u->s);
- if (t == default_transport)
- default_transport = 0;
- else if (t == default_transport6)
- default_transport6 = 0;
if (u->link.le_prev)
LIST_REMOVE(u, link);
}
@@ -532,16 +388,19 @@ void
udp_report(struct transport *t)
{
struct udp_transport *u = (struct udp_transport *)t;
- char *src = NULL, *dst = NULL;
+ char *src = NULL, *dst = NULL;
+ in_port_t sport, dport;
if (sockaddr2text(u->src, &src, 0))
goto ret;
+ sport = sockaddr_port(u->src);
if (!u->dst || sockaddr2text(u->dst, &dst, 0))
dst = 0;
+ dport = dst ? sockaddr_port(u->dst) : 0;
- LOG_DBG((LOG_REPORT, 0, "udp_report: fd %d src %s dst %s", u->s, src,
- dst ? dst : "<none>"));
+ LOG_DBG((LOG_REPORT, 0, "udp_report: fd %d src %s:%u dst %s:%u", u->s,
+ src, ntohs(sport), dst ? dst : "<none>", ntohs(dport)));
ret:
if (dst)
@@ -551,165 +410,6 @@ ret:
}
/*
- * Probe the interface list and determine what new interfaces have
- * appeared.
- *
- * At the same time, we try to determine whether existing interfaces have
- * been rendered invalid; we do this by marking all UDP transports before
- * we call udp_bind_if () through if_map (), and then releasing those
- * transports that have not been unmarked.
- */
-static void
-udp_reinit(void)
-{
- struct udp_transport *u, *u2;
- char *port;
-
- /* Initialize the protocol and port numbers. */
- port = udp_default_port ? udp_default_port : "500";
-
- /* Mark all UDP transports, except the default ones. */
- for (u = LIST_FIRST(&udp_listen_list); u; u = LIST_NEXT(u, link))
- if (&u->transport != default_transport &&
- &u->transport != default_transport6)
- u->transport.flags |= TRANSPORT_MARK;
-
- /* Re-probe interface list. */
- if (if_map(udp_bind_if, port) == -1)
- log_print("udp_init: Could not bind the ISAKMP UDP port %s "
- "on all interfaces", port);
-
- /*
- * Release listening transports for local addresses that no
- * longer exist. udp_bind_if () will have left those still marked.
- */
- u = LIST_FIRST(&udp_listen_list);
- while (u) {
- u2 = LIST_NEXT(u, link);
-
- if (u->transport.flags & TRANSPORT_MARK) {
- LIST_REMOVE(u, link);
- transport_release(&u->transport);
- }
- u = u2;
- }
-}
-
-/*
- * Find out the magic numbers for the UDP protocol as well as the UDP port
- * to use. Setup an UDP server for each address of this machine, and one
- * for the generic case when we are the initiator.
- */
-void
-udp_init(void)
-{
- struct sockaddr_storage dflt_stor;
- struct sockaddr_in *dflt = (struct sockaddr_in *) & dflt_stor;
- struct conf_list *listen_on;
- char *port;
- long lport;
- char *ep;
-
- /* Initialize the protocol and port numbers. */
- port = udp_default_port ? udp_default_port : "500";
-
- LIST_INIT(&udp_listen_list);
-
- transport_method_add(&udp_transport_vtbl);
-
- /* Bind the ISAKMP UDP port on all network interfaces we have. */
- if (if_map(udp_bind_if, port) == -1)
- log_fatal("udp_init: Could not bind the ISAKMP UDP port %s "
- "on all interfaces", port);
-
- /* Only listen to the specified address if Listen-on is configured */
- listen_on = conf_get_list("General", "Listen-on");
- if (listen_on) {
- LOG_DBG((LOG_TRANSPORT, 50,
- "udp_init: not binding ISAKMP UDP port to INADDR_ANY"));
- conf_free_list(listen_on);
- return;
- }
- /*
- * Get port.
- * XXX Use getservbyname too.
- */
- lport = strtol(port, &ep, 10);
- if (*ep != '\0' || lport < (long) 0 || lport > (long) USHRT_MAX) {
- log_print("udp_init: port string \"%s\" not convertible to "
- "in_port_t", port);
- return;
- }
- /*
- * Bind to INADDR_ANY in case of new addresses popping up. Packet
- * reception on this transport is taken as a hint to reprobe the
- * interface list.
- */
- if (!bind_family || (bind_family & BIND_FAMILY_INET4)) {
- memset(&dflt_stor, 0, sizeof dflt_stor);
- dflt->sin_family = AF_INET;
-#if !defined (LINUX_IPSEC)
- ((struct sockaddr_in *)dflt)->sin_len =
- sizeof(struct sockaddr_in);
-#endif
- ((struct sockaddr_in *)dflt)->sin_port = htons(lport);
-
- default_transport = udp_bind((struct sockaddr *)&dflt_stor);
- if (!default_transport) {
- log_error("udp_init: could not allocate default "
- "IPv4 ISAKMP UDP port");
- return;
- }
- LIST_INSERT_HEAD(&udp_listen_list,
- (struct udp_transport *)default_transport, link);
- }
- if (!bind_family || (bind_family & BIND_FAMILY_INET6)) {
- memset(&dflt_stor, 0, sizeof dflt_stor);
- dflt->sin_family = AF_INET6;
-#if !defined (LINUX_IPSEC)
- ((struct sockaddr_in6 *)dflt)->sin6_len =
- sizeof(struct sockaddr_in6);
-#endif
- ((struct sockaddr_in6 *)dflt)->sin6_port = htons(lport);
-
- default_transport6 = udp_bind((struct sockaddr *)&dflt_stor);
- if (!default_transport6) {
- log_error("udp_init: could not allocate default "
- "IPv6 ISAKMP UDP port");
- return;
- }
- LIST_INSERT_HEAD(&udp_listen_list,
- (struct udp_transport *)default_transport6, link);
- }
-}
-
-/*
- * Set transport T's socket in FDS, return a value useable by select(2)
- * as the number of file descriptors to check.
- */
-static int
-udp_fd_set(struct transport *t, fd_set *fds, int bit)
-{
- struct udp_transport *u = (struct udp_transport *)t;
-
- if (bit)
- FD_SET(u->s, fds);
- else
- FD_CLR(u->s, fds);
-
- return u->s + 1;
-}
-
-/* Check if transport T's socket is set in FDS. */
-static int
-udp_fd_isset(struct transport *t, fd_set *fds)
-{
- struct udp_transport *u = (struct udp_transport *)t;
-
- return FD_ISSET(u->s, fds);
-}
-
-/*
* A message has arrived on transport T's socket. If T is single-ended,
* clone it into a double-ended transport which we will use from now on.
* Package the message as we want it and continue processing in the message
@@ -718,38 +418,24 @@ udp_fd_isset(struct transport *t, fd_set *fds)
static void
udp_handle_message(struct transport *t)
{
- struct udp_transport *u = (struct udp_transport *) t;
+ struct udp_transport *u = (struct udp_transport *)t;
u_int8_t buf[UDP_SIZE];
struct sockaddr_storage from;
u_int32_t len = sizeof from;
ssize_t n;
struct message *msg;
- n = recvfrom(u->s, buf, UDP_SIZE, 0, (struct sockaddr *) & from, &len);
+ n = recvfrom(u->s, buf, UDP_SIZE, 0, (struct sockaddr *)&from, &len);
if (n == -1) {
log_error("recvfrom (%d, %p, %d, %d, %p, %p)", u->s, buf,
UDP_SIZE, 0, &from, &len);
return;
}
/*
- * If we received the packet over the default transports, reprobe the
- * interfaces.
- */
- if (t == default_transport || t == default_transport6) {
- udp_reinit();
-
- /*
- * As we don't know the actual destination address of the
- * packet, we can't really deal with it. So, just ignore it
- * and hope we catch the retransmission.
- */
- return;
- }
- /*
* Make a specialized UDP transport structure out of the incoming
* transport and the address information we got from recvfrom(2).
*/
- t = udp_clone(u, (struct sockaddr *)&from);
+ t = t->virtual->vtbl->clone(t->virtual, (struct sockaddr *)&from);
if (!t)
return;
@@ -757,6 +443,7 @@ udp_handle_message(struct transport *t)
if (!msg) {
log_error("failed to allocate message structure, dropping "
"packet received on transport %p", u);
+ t->vtbl->remove(t);
return;
}
message_recv(msg);
@@ -764,9 +451,9 @@ udp_handle_message(struct transport *t)
/* Physically send the message MSG over its associated transport. */
static int
-udp_send_message(struct message *msg)
+udp_send_message(struct message *msg, struct transport *t)
{
- struct udp_transport *u = (struct udp_transport *)msg->transport;
+ struct udp_transport *u = (struct udp_transport *)t;
ssize_t n;
struct msghdr m;
@@ -794,7 +481,7 @@ udp_send_message(struct message *msg)
* Get transport T's peer address and stuff it into the sockaddr pointed
* to by DST.
*/
-static void
+void
udp_get_dst(struct transport *t, struct sockaddr **dst)
{
*dst = ((struct udp_transport *)t)->dst;
@@ -804,37 +491,37 @@ udp_get_dst(struct transport *t, struct sockaddr **dst)
* Get transport T's local address and stuff it into the sockaddr pointed
* to by SRC. Put its length into SRC_LEN.
*/
-static void
+void
udp_get_src(struct transport *t, struct sockaddr **src)
{
*src = ((struct udp_transport *)t)->src;
}
-static char *
+char *
udp_decode_ids(struct transport *t)
{
+ struct sockaddr *src, *dst;
static char result[1024];
char idsrc[256], iddst[256];
+ t->vtbl->get_src(t, &src);
+ t->vtbl->get_dst(t, &dst);
+
#ifdef HAVE_GETNAMEINFO
- if (getnameinfo(((struct udp_transport *)t)->src,
- sysdep_sa_len(((struct udp_transport *)t)->src),
- idsrc, sizeof idsrc, NULL, 0, NI_NUMERICHOST) != 0) {
+ if (getnameinfo(src, sysdep_sa_len(src), idsrc, sizeof idsrc, NULL, 0,
+ NI_NUMERICHOST) != 0) {
log_print("udp_decode_ids: getnameinfo () failed for 'src'");
strlcpy(idsrc, "<error>", 256);
}
- if (getnameinfo(((struct udp_transport *)t)->dst,
- sysdep_sa_len(((struct udp_transport *)t)->dst),
- iddst, sizeof iddst, NULL, 0, NI_NUMERICHOST) != 0) {
+ if (getnameinfo(dst, sysdep_sa_len(dst), iddst, sizeof iddst, NULL, 0,
+ NI_NUMERICHOST) != 0) {
log_print("udp_decode_ids: getnameinfo () failed for 'dst'");
strlcpy(iddst, "<error>", 256);
}
#else
- strlcpy(idsrc, inet_ntoa(((struct udp_transport *)t)->src.sin_addr),
- 256);
- strlcpy(iddst, inet_ntoa(((struct udp_transport *)t)->dst.sin_addr),
- 256);
-#endif /* HAVE_GETNAMEINFO */
+ strlcpy(idsrc, inet_ntoa(src->sin_addr), 256);
+ strlcpy(iddst, inet_ntoa(dst->sin_addr), 256);
+#endif /* HAVE_GETNAMEINFO */
snprintf(result, sizeof result, "src: %s dst: %s", idsrc, iddst);
return result;
diff --git a/sbin/isakmpd/udp.h b/sbin/isakmpd/udp.h
index 923461f3af7..44916ccc044 100644
--- a/sbin/isakmpd/udp.h
+++ b/sbin/isakmpd/udp.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: udp.h,v 1.8 2004/04/15 18:39:26 deraadt Exp $ */
+/* $OpenBSD: udp.h,v 1.9 2004/06/20 15:24:05 ho Exp $ */
/* $EOM: udp.h,v 1.4 1998/12/22 02:23:43 niklas Exp $ */
/*
@@ -39,9 +39,15 @@ extern int bind_family;
#define BIND_FAMILY_INET4 0x0001
#define BIND_FAMILY_INET6 0x0002
-#if 0
-extern in_port_t udp_decode_port(char *);
-#endif
-extern void udp_init(void);
+struct transport *udp_bind(const struct sockaddr *);
+void udp_init(void);
+
+struct udp_transport {
+ struct transport transport;
+ struct sockaddr *src;
+ struct sockaddr *dst;
+ int s;
+ LIST_ENTRY(udp_transport) link;
+};
#endif /* _UDP_H_ */
diff --git a/sbin/isakmpd/udp_encap.c b/sbin/isakmpd/udp_encap.c
new file mode 100644
index 00000000000..aa5ba003726
--- /dev/null
+++ b/sbin/isakmpd/udp_encap.c
@@ -0,0 +1,461 @@
+/* $OpenBSD: udp_encap.c,v 1.1 2004/06/20 15:24:05 ho Exp $ */
+
+/*
+ * Copyright (c) 1998, 1999, 2001 Niklas Hallqvist. All rights reserved.
+ * Copyright (c) 2000 Angelos D. Keromytis. All rights reserved.
+ * Copyright (c) 2004 Håkan Olsson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#ifndef linux
+#include <sys/sockio.h>
+#endif
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <ctype.h>
+#include <err.h>
+#include <limits.h>
+#include <netdb.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "sysdep.h"
+
+#include "conf.h"
+#include "if.h"
+#include "ipsec_doi.h"
+#include "isakmp.h"
+#include "log.h"
+#include "message.h"
+#include "monitor.h"
+#include "sysdep.h"
+#include "transport.h"
+#include "udp.h"
+#include "udp_encap.h"
+#include "util.h"
+#include "virtual.h"
+
+#define UDP_SIZE 65536
+
+/* If a system doesn't have SO_REUSEPORT, SO_REUSEADDR will have to do. */
+#ifndef SO_REUSEPORT
+#define SO_REUSEPORT SO_REUSEADDR
+#endif
+
+/* Reused, from udp.c */
+struct transport *udp_clone (struct transport *, struct sockaddr *);
+void udp_get_dst (struct transport *, struct sockaddr **);
+void udp_get_src (struct transport *, struct sockaddr **);
+char *udp_decode_ids (struct transport *);
+
+static struct transport *udp_encap_create (char *);
+static void udp_encap_remove (struct transport *);
+static void udp_encap_report (struct transport *);
+static void udp_encap_handle_message (struct transport *);
+static struct transport *udp_encap_make (struct sockaddr *);
+static int udp_encap_send_message (struct message *,
+ struct transport *);
+
+static struct transport_vtbl udp_encap_transport_vtbl = {
+ { 0 }, "udp_encap",
+ udp_encap_create,
+ 0,
+ udp_encap_remove,
+ udp_encap_report,
+ 0,
+ 0,
+ udp_encap_handle_message,
+ udp_encap_send_message,
+ udp_get_dst,
+ udp_get_src,
+ udp_decode_ids,
+ udp_clone,
+ 0
+};
+
+char *udp_encap_default_port = 0;
+char *udp_encap_bind_port = 0;
+
+void
+udp_encap_init(void)
+{
+ transport_method_add(&udp_encap_transport_vtbl);
+}
+
+/* Create a UDP transport structure bound to LADDR just for listening. */
+static struct transport *
+udp_encap_make(struct sockaddr *laddr)
+{
+ struct udp_transport *t = 0;
+ int s, on, wildcardaddress = 0;
+ char *tstr;
+
+ t = calloc(1, sizeof *t);
+ if (!t) {
+ log_print("udp_encap_make: malloc (%lu) failed",
+ (unsigned long)sizeof *t);
+ return 0;
+ }
+
+ s = socket(laddr->sa_family, SOCK_DGRAM, IPPROTO_UDP);
+ if (s == -1) {
+ log_error("udp_encap_make: socket (%d, %d, %d)",
+ laddr->sa_family, SOCK_DGRAM, IPPROTO_UDP);
+ goto err;
+ }
+
+ /* Make sure we don't get our traffic encrypted. */
+ if (sysdep_cleartext(s, laddr->sa_family) == -1)
+ goto err;
+
+ /* Wildcard address ? */
+ switch (laddr->sa_family) {
+ case AF_INET:
+ if (((struct sockaddr_in *)laddr)->sin_addr.s_addr
+ == INADDR_ANY)
+ wildcardaddress = 1;
+ break;
+ case AF_INET6:
+ if (IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6 *)laddr)->sin6_addr))
+ wildcardaddress = 1;
+ break;
+ }
+
+ /*
+ * In order to have several bound specific address-port combinations
+ * with the same port SO_REUSEADDR is needed.
+ * If this is a wildcard socket and we are not listening there, but
+ * only sending from it make sure it is entirely reuseable with
+ * SO_REUSEPORT.
+ */
+ on = 1;
+ if (setsockopt(s, SOL_SOCKET,
+ wildcardaddress ? SO_REUSEPORT : SO_REUSEADDR,
+ (void *)&on, sizeof on) == -1) {
+ log_error("udp_encap_make: setsockopt (%d, %d, %d, %p, %lu)",
+ s, SOL_SOCKET,
+ wildcardaddress ? SO_REUSEPORT : SO_REUSEADDR, &on,
+ (unsigned long)sizeof on);
+ goto err;
+ }
+
+ t->transport.vtbl = &udp_encap_transport_vtbl;
+ t->src = laddr;
+ if (monitor_bind(s, t->src, sysdep_sa_len (t->src))) {
+ if (sockaddr2text(t->src, &tstr, 0))
+ log_error("udp_encap_make: bind (%d, %p, %lu)", s,
+ &t->src, (unsigned long)sizeof t->src);
+ else {
+ log_error("udp_encap_make: bind (%d, %s, %lu)", s,
+ tstr, (unsigned long)sizeof t->src);
+ free(tstr);
+ }
+ goto err;
+ }
+
+ t->s = s;
+ if (sockaddr2text(t->src, &tstr, 0))
+ LOG_DBG((LOG_MISC, 20, "udp_encap_make: "
+ "transport %p socket %d family %d", t, s,
+ t->src->sa_family == AF_INET ? 4 : 6));
+ else {
+ LOG_DBG((LOG_MISC, 20, "udp_encap_make: "
+ "transport %p socket %d ip %s port %d", t, s,
+ tstr, ntohs(sockaddr_port(t->src))));
+ free(tstr);
+ }
+ transport_setup(&t->transport, 0);
+ transport_reference(&t->transport);
+ t->transport.flags |= TRANSPORT_LISTEN;
+ return &t->transport;
+
+ err:
+ if (s >= 0)
+ close (s);
+ if (t) {
+ /* Already closed. */
+ t->s = -1;
+ udp_encap_remove(&t->transport);
+ }
+ return 0;
+}
+
+/*
+ * Initialize an object of the UDP transport class. Fill in the local
+ * IP address and port information and create a server socket bound to
+ * that specific port. Add the polymorphic transport structure to the
+ * system-wide pools of known ISAKMP transports.
+ */
+struct transport *
+udp_encap_bind(const struct sockaddr *addr)
+{
+ struct sockaddr *src =
+ malloc(sysdep_sa_len((struct sockaddr *)addr));
+
+ if (!src)
+ return 0;
+
+ memcpy(src, addr, sysdep_sa_len((struct sockaddr *)addr));
+ return udp_encap_make(src);
+}
+
+/*
+ * NAME is a section name found in the config database. Setup and return
+ * a transport useable to talk to the peer specified by that name.
+ */
+static struct transport *
+udp_encap_create(char *name)
+{
+ struct virtual_transport *v;
+ struct udp_transport *u;
+ struct transport *rv, *t;
+ struct sockaddr *dst, *addr;
+ struct conf_list *addr_list = 0;
+ struct conf_list_node *addr_node;
+ char *addr_str, *port_str;
+
+ port_str = conf_get_str(name, "Port"); /* XXX "Encap-port" ? */
+ if (!port_str)
+ port_str = udp_encap_default_port;
+ if (!port_str)
+ port_str = UDP_ENCAP_DEFAULT_PORT_STR;
+
+ addr_str = conf_get_str(name, "Address");
+ if (!addr_str) {
+ log_print("udp_encap_create: no address configured "
+ "for \"%s\"", name);
+ return 0;
+ }
+ if (text2sockaddr(addr_str, port_str, &dst)) {
+ log_print("udp_encap_create: address \"%s\" not understood",
+ addr_str);
+ return 0;
+ }
+
+ addr_str = conf_get_str(name, "Local-address");
+ if (!addr_str)
+ addr_list = conf_get_list("General", "Listen-on");
+ if (!addr_str && !addr_list) {
+ v = virtual_get_default(dst->sa_family);
+ u = (struct udp_transport *)v->encap;
+
+ if (!u) {
+ log_print("udp_encap_create: no default transport");
+ rv = 0;
+ goto ret;
+ } else {
+ rv = udp_clone((struct transport *)u, dst);
+ if (rv)
+ rv->vtbl = &udp_encap_transport_vtbl;
+ goto ret;
+ }
+ }
+
+ if (addr_list) {
+ for (addr_node = TAILQ_FIRST(&addr_list->fields);
+ addr_node; addr_node = TAILQ_NEXT(addr_node, link))
+ if (text2sockaddr(addr_node->field, port_str,
+ &addr) == 0) {
+ v = virtual_listen_lookup(addr);
+ free(addr);
+ if (v) {
+ addr_str = addr_node->field;
+ break;
+ }
+ }
+ if (!addr_str) {
+ log_print("udp_encap_create: "
+ "no matching listener found");
+ rv = 0;
+ goto ret;
+ }
+ }
+ if (text2sockaddr(addr_str, port_str, &addr)) {
+ log_print("udp_encap_create: "
+ "address \"%s\" not understood", addr_str);
+ rv = 0;
+ goto ret;
+ }
+ v = virtual_listen_lookup(addr);
+ free(addr);
+ if (!v) {
+ log_print("udp_encap_create: "
+ "%s:%s must exist as a listener too", addr_str, port_str);
+ rv = 0;
+ goto ret;
+ }
+ t = (struct transport *)v;
+ rv = udp_clone(v->encap, dst);
+ if (rv) {
+ rv->vtbl = &udp_encap_transport_vtbl; /* XXX Necessary? */
+ transport_reference(rv->virtual);
+ }
+
+ ret:
+ if (addr_list)
+ conf_free_list(addr_list);
+ free(dst);
+ return rv;
+}
+
+void
+udp_encap_remove(struct transport *t)
+{
+ struct udp_transport *u = (struct udp_transport *)t;
+
+ if (u->src)
+ free (u->src);
+ if (u->dst)
+ free (u->dst);
+ if (t->flags & TRANSPORT_LISTEN) {
+ if (u->s >= 0)
+ close (u->s);
+ if (u->link.le_prev)
+ LIST_REMOVE (u, link);
+ }
+ free (t);
+}
+
+/* Report transport-method specifics of the T transport. */
+void
+udp_encap_report(struct transport *t)
+{
+ struct udp_transport *u = (struct udp_transport *)t;
+ char *src, *dst;
+ in_port_t sport, dport;
+
+ if (sockaddr2text(u->src, &src, 0))
+ goto ret;
+ sport = sockaddr_port(u->src);
+
+ if (!u->dst || sockaddr2text(u->dst, &dst, 0))
+ dst = 0;
+ dport = dst ? sockaddr_port(u->dst) : 0;
+
+ LOG_DBG ((LOG_REPORT, 0, "udp_encap_report: fd %d src %s:%u dst %s:%u",
+ u->s, src, ntohs(sport), dst ? dst : "*", ntohs(dport)));
+
+ ret:
+ if (dst)
+ free(dst);
+ if (src)
+ free(src);
+}
+
+/*
+ * A message has arrived on transport T's socket. If T is single-ended,
+ * clone it into a double-ended transport which we will use from now on.
+ * Package the message as we want it and continue processing in the message
+ * module.
+ */
+static void
+udp_encap_handle_message(struct transport *t)
+{
+ struct udp_transport *u = (struct udp_transport *)t;
+ struct sockaddr_storage from;
+ struct message *msg;
+ u_int32_t len = sizeof from;
+ ssize_t n;
+ u_int8_t buf[UDP_SIZE];
+
+ n = recvfrom(u->s, buf, UDP_SIZE, 0, (struct sockaddr *)&from, &len);
+ if (n == -1) {
+ log_error("recvfrom (%d, %p, %d, %d, %p, %p)", u->s, buf,
+ UDP_SIZE, 0, &from, &len);
+ return;
+ }
+
+ /*
+ * Make a specialized UDP transport structure out of the incoming
+ * transport and the address information we got from recvfrom(2).
+ */
+ t = t->virtual->vtbl->clone(t->virtual, (struct sockaddr *)&from);
+ if (!t)
+ return;
+
+ /* Check NULL-ESP marker. */
+ if (n < sizeof (u_int32_t) || *(u_int32_t *)buf != 0) {
+ /* Should never happen. */
+ log_print ("udp_encap_handle_message: "
+ "Null-ESP marker not NULL or short message");
+ return;
+ }
+
+ msg = message_alloc(t, buf + sizeof (u_int32_t),
+ n - sizeof (u_int32_t));
+ if (!msg) {
+ log_error ("failed to allocate message structure, dropping "
+ "packet received on transport %p", u);
+ return;
+ }
+ message_recv (msg);
+}
+
+/* Physically send the message MSG over its associated transport. */
+static int
+udp_encap_send_message(struct message *msg, struct transport *t)
+{
+ struct udp_transport *u = (struct udp_transport *)t;
+ struct msghdr m;
+ struct iovec *new_iov;
+ ssize_t n;
+ u_int32_t marker = 0; /* NULL-ESP Marker */
+
+ /* Construct new iov array, prefixing NULL-ESP Marker. */
+ new_iov = (struct iovec *)calloc (msg->iovlen + 1, sizeof *new_iov);
+ if (!new_iov) {
+ log_error ("udp_encap_send_message: calloc (%lu, %lu) failed",
+ (unsigned long)msg->iovlen + 1,
+ (unsigned long)sizeof *new_iov);
+ return -1;
+ }
+ new_iov[0].iov_base = &marker;
+ new_iov[0].iov_len = IPSEC_SPI_SIZE;
+ memcpy (new_iov + sizeof *new_iov, msg->iov,
+ msg->iovlen * sizeof *new_iov);
+
+ /*
+ * Sending on connected sockets requires that no destination address is
+ * given, or else EISCONN will occur.
+ */
+ m.msg_name = (caddr_t)u->dst;
+ m.msg_namelen = sysdep_sa_len (u->dst);
+ m.msg_iov = new_iov;
+ m.msg_iovlen = msg->iovlen + 1;
+ m.msg_control = 0;
+ m.msg_controllen = 0;
+ m.msg_flags = 0;
+ n = sendmsg (u->s, &m, 0);
+ if (n == -1) {
+ /* XXX We should check whether the address has gone away */
+ log_error ("sendmsg (%d, %p, %d)", u->s, &m, 0);
+ free (new_iov);
+ return -1;
+ }
+ free (new_iov);
+ return 0;
+}
diff --git a/sbin/isakmpd/udp_encap.h b/sbin/isakmpd/udp_encap.h
new file mode 100644
index 00000000000..bb08db2d3c4
--- /dev/null
+++ b/sbin/isakmpd/udp_encap.h
@@ -0,0 +1,37 @@
+/* $OpenBSD: udp_encap.h,v 1.1 2004/06/20 15:24:05 ho Exp $ */
+
+/*
+ * Copyright (c) 1998 Niklas Hallqvist. All rights reserved.
+ * Copyright (c) 2004 Håkan Olsson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _UDP_ENCAP_H_
+#define _UDP_ENCAP_H_
+
+struct transport *udp_encap_bind (const struct sockaddr *);
+void udp_encap_init (void);
+
+extern char *udp_encap_default_port;
+extern char *udp_encap_bind_port;
+
+#endif /* _UDP_H_ */
diff --git a/sbin/isakmpd/util.c b/sbin/isakmpd/util.c
index ae89c0be534..f94c503f9ee 100644
--- a/sbin/isakmpd/util.c
+++ b/sbin/isakmpd/util.c
@@ -1,9 +1,9 @@
-/* $OpenBSD: util.c,v 1.42 2004/06/16 15:08:20 hshoexer Exp $ */
+/* $OpenBSD: util.c,v 1.43 2004/06/20 15:24:05 ho Exp $ */
/* $EOM: util.c,v 1.23 2000/11/23 12:22:08 niklas Exp $ */
/*
* Copyright (c) 1998, 1999, 2001 Niklas Hallqvist. All rights reserved.
- * Copyright (c) 2000, 2001 Håkan Olsson. All rights reserved.
+ * Copyright (c) 2000, 2001, 2004 Håkan Olsson. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -453,6 +453,21 @@ sockaddr_port(struct sockaddr *sa)
}
}
+/* Utility function used to set the port of a sockaddr. */
+void
+sockaddr_set_port(struct sockaddr *sa, in_port_t port)
+{
+ switch (sa->sa_family) {
+ case AF_INET:
+ ((struct sockaddr_in *)sa)->sin_port = htons (port);
+ break;
+
+ case AF_INET6:
+ ((struct sockaddr_in6 *)sa)->sin6_port = htons (port);
+ break;
+ }
+}
+
/*
* Convert network address to text. The network address does not need
* to be properly aligned.
diff --git a/sbin/isakmpd/util.h b/sbin/isakmpd/util.h
index 4d94788ecd8..92dab9a7d82 100644
--- a/sbin/isakmpd/util.h
+++ b/sbin/isakmpd/util.h
@@ -1,9 +1,9 @@
-/* $OpenBSD: util.h,v 1.19 2004/05/23 16:14:22 deraadt Exp $ */
+/* $OpenBSD: util.h,v 1.20 2004/06/20 15:24:05 ho Exp $ */
/* $EOM: util.h,v 1.10 2000/10/24 13:33:39 niklas Exp $ */
/*
* Copyright (c) 1998 Niklas Hallqvist. All rights reserved.
- * Copyright (c) 2001 Håkan Olsson. All rights reserved.
+ * Copyright (c) 2001, 2004 Håkan Olsson. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -63,6 +63,7 @@ extern int sockaddr2text(struct sockaddr *, char **, int);
extern u_int8_t *sockaddr_addrdata(struct sockaddr *);
extern int sockaddr_addrlen(struct sockaddr *);
extern in_port_t sockaddr_port(struct sockaddr *);
+extern void sockaddr_set_port(struct sockaddr *, in_port_t);
extern int text2sockaddr(char *, char *, struct sockaddr **);
extern void util_ntoa(char **, int, u_int8_t *);
extern int zero_test(const u_int8_t *, size_t);
diff --git a/sbin/isakmpd/virtual.c b/sbin/isakmpd/virtual.c
new file mode 100644
index 00000000000..5bcb2d76c06
--- /dev/null
+++ b/sbin/isakmpd/virtual.c
@@ -0,0 +1,706 @@
+/* $OpenBSD: virtual.c,v 1.1 2004/06/20 15:24:05 ho Exp $ */
+
+/*
+ * Copyright (c) 2004 Håkan Olsson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#ifndef linux
+#include <sys/sockio.h>
+#endif
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <ctype.h>
+#include <err.h>
+#include <limits.h>
+#include <netdb.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "conf.h"
+#include "if.h"
+#include "exchange.h"
+#include "log.h"
+#include "transport.h"
+#include "virtual.h"
+#include "udp.h"
+#include "util.h"
+
+#if defined (USE_NAT_TRAVERSAL)
+#include "udp_encap.h"
+#endif
+
+static struct transport *virtual_bind(const struct sockaddr *);
+static struct transport *virtual_bind_ADDR_ANY(sa_family_t);
+static int virtual_bind_if(char *, struct sockaddr *, void *);
+static struct transport *virtual_clone(struct transport *, struct sockaddr *);
+static struct transport *virtual_create(char *);
+static char *virtual_decode_ids (struct transport *);
+static int virtual_fd_set(struct transport *, fd_set *, int);
+static int virtual_fd_isset(struct transport *, fd_set *);
+static void virtual_get_dst(struct transport *,
+ struct sockaddr **);
+static struct msg_head *virtual_get_queue(struct message *);
+static void virtual_get_src(struct transport *,
+ struct sockaddr **);
+static void virtual_handle_message(struct transport *);
+static void virtual_reinit(void);
+static void virtual_remove(struct transport *);
+static void virtual_report(struct transport *);
+static int virtual_send_message(struct message *,
+ struct transport *);
+
+static struct transport_vtbl virtual_transport_vtbl = {
+ { 0 }, "udp",
+ virtual_create,
+ virtual_reinit,
+ virtual_remove,
+ virtual_report,
+ virtual_fd_set,
+ virtual_fd_isset,
+ virtual_handle_message,
+ virtual_send_message,
+ virtual_get_dst,
+ virtual_get_src,
+ virtual_decode_ids,
+ virtual_clone,
+ virtual_get_queue
+};
+
+static LIST_HEAD (virtual_listen_list, virtual_transport) virtual_listen_list;
+static struct transport *default_transport, *default_transport6;
+
+void
+virtual_init(void)
+{
+ struct conf_list *listen_on;
+
+ LIST_INIT(&virtual_listen_list);
+
+ transport_method_add(&virtual_transport_vtbl);
+
+ /* Bind the ISAKMP port(s) on all network interfaces we have. */
+ if (if_map(virtual_bind_if, 0) == -1)
+ log_fatal("virtual_init: "
+ "could not bind the ISAKMP port(s) on all interfaces");
+
+ /* Only listen to the specified address if Listen-on is configured */
+ listen_on = conf_get_list("General", "Listen-on");
+ if (listen_on) {
+ LOG_DBG((LOG_TRANSPORT, 50,
+ "virtual_init: not binding ISAKMP port(s) to ADDR_ANY"));
+ conf_free_list(listen_on);
+ return;
+ }
+
+ /*
+ * Bind to INADDR_ANY in case of new addresses popping up.
+ * Packet reception on this transport is taken as a hint to reprobe the
+ * interface list.
+ */
+ if (!bind_family || (bind_family & BIND_FAMILY_INET4)) {
+ default_transport = virtual_bind_ADDR_ANY(AF_INET);
+ if (!default_transport)
+ return;
+ LIST_INSERT_HEAD(&virtual_listen_list,
+ (struct virtual_transport *)default_transport, link);
+ }
+
+ if (!bind_family || (bind_family & BIND_FAMILY_INET6)) {
+ default_transport6 = virtual_bind_ADDR_ANY(AF_INET6);
+ if (!default_transport6)
+ return;
+ LIST_INSERT_HEAD(&virtual_listen_list,
+ (struct virtual_transport *)default_transport6, link);
+ }
+}
+
+struct virtual_transport *
+virtual_get_default(sa_family_t af)
+{
+ switch (af) {
+ case AF_INET:
+ return (struct virtual_transport *)default_transport;
+ case AF_INET6:
+ return (struct virtual_transport *)default_transport6;
+ default:
+ return 0;
+ }
+}
+
+/*
+ * Probe the interface list and determine what new interfaces have
+ * appeared.
+ *
+ * At the same time, we try to determine whether existing interfaces have
+ * been rendered invalid; we do this by marking all virtual transports before
+ * we call virtual_bind_if () through if_map (), and then releasing those
+ * transports that have not been unmarked.
+ */
+void
+virtual_reinit(void)
+{
+ struct virtual_transport *v, *v2;
+
+ /* Mark all UDP transports, except the default ones. */
+ for (v = LIST_FIRST(&virtual_listen_list); v; v = LIST_NEXT(v, link))
+ if (&v->transport != default_transport
+ && &v->transport != default_transport6)
+ v->transport.flags |= TRANSPORT_MARK;
+
+ /* Re-probe interface list. */
+ if (if_map(virtual_bind_if, 0) == -1)
+ log_print("virtual_init: "
+ "could not bind the ISAKMP port(s) on all interfaces");
+
+ /*
+ * Release listening transports for local addresses that no
+ * longer exist. virtual_bind_if () will have left those still marked.
+ */
+ v = LIST_FIRST(&virtual_listen_list);
+ while (v) {
+ v2 = LIST_NEXT(v, link);
+ if (v->transport.flags & TRANSPORT_MARK) {
+ LIST_REMOVE(v, link);
+ transport_release(&v->transport);
+ }
+ v = v2;
+ }
+}
+
+struct virtual_transport *
+virtual_listen_lookup(struct sockaddr *addr)
+{
+ struct virtual_transport *v;
+ struct udp_transport *u;
+
+ for (v = LIST_FIRST(&virtual_listen_list); v;
+ v = LIST_NEXT(v, link)) {
+ u = (struct udp_transport *)v->main;
+ if (u->src->sa_family == addr->sa_family
+ && sockaddr_addrlen(u->src) == sockaddr_addrlen(addr)
+ && memcmp(sockaddr_addrdata (u->src),
+ sockaddr_addrdata(addr),
+ sockaddr_addrlen(addr)) == 0)
+ return v;
+ }
+
+ return 0;
+}
+
+/*
+ * Initialize an object of the VIRTUAL transport class.
+ */
+static struct transport *
+virtual_bind(const struct sockaddr *addr)
+{
+ struct virtual_transport *v;
+ struct sockaddr_storage tmp_sa;
+ char *port;
+ char *ep;
+ long lport;
+
+ v = (struct virtual_transport *)calloc(1, sizeof *v);
+ if (!v) {
+ log_error("virtual_bind: calloc(1, %lu) failed",
+ (unsigned long)sizeof *v);
+ return 0;
+ }
+
+ v->transport.vtbl = &virtual_transport_vtbl;
+
+ memcpy(&tmp_sa, addr, sysdep_sa_len((struct sockaddr *)addr));
+
+ /*
+ * Get port.
+ * XXX Use getservbyname too.
+ */
+ port = udp_default_port ? udp_default_port : UDP_DEFAULT_PORT_STR;
+ lport = strtol(port, &ep, 10);
+ if (*ep != '\0' || lport < 0 || lport > USHRT_MAX) {
+ log_print("virtual_bind: "
+ "port string \"%s\" not convertible to in_port_t", port);
+ return 0;
+ }
+
+ sockaddr_set_port((struct sockaddr *)&tmp_sa, (in_port_t)lport);
+ v->main = udp_bind((struct sockaddr *)&tmp_sa);
+ ((struct transport *)v->main)->virtual = (struct transport *)v;
+
+#if defined (USE_NAT_TRAVERSAL)
+ memcpy(&tmp_sa, addr, sysdep_sa_len((struct sockaddr *)addr));
+
+ /*
+ * Get port.
+ * XXX Use getservbyname too.
+ */
+ port = udp_encap_default_port
+ ? udp_encap_default_port : UDP_ENCAP_DEFAULT_PORT_STR;
+ lport = strtol(port, &ep, 10);
+ if (*ep != '\0' || lport < 0 || lport > USHRT_MAX) {
+ log_print("virtual_bind: "
+ "port string \"%s\" not convertible to in_port_t", port);
+ return 0;
+ }
+
+ sockaddr_set_port((struct sockaddr *)&tmp_sa, (in_port_t)lport);
+ v->encap = udp_encap_bind((struct sockaddr *)&tmp_sa);
+ ((struct transport *)v->encap)->virtual = (struct transport *)v;
+#endif
+ v->encap_is_active = 0;
+
+ transport_setup(&v->transport, 1);
+ transport_reference(&v->transport);
+ v->transport.flags |= TRANSPORT_LISTEN;
+
+ return (struct transport *)v;
+}
+
+static struct transport *
+virtual_bind_ADDR_ANY(sa_family_t af)
+{
+ struct sockaddr_storage dflt_stor;
+ struct sockaddr_in *d4 = (struct sockaddr_in *)&dflt_stor;
+ struct sockaddr_in6 *d6 = (struct sockaddr_in6 *)&dflt_stor;
+ struct transport *t;
+ struct in6_addr in6addr_any = IN6ADDR_ANY_INIT;
+
+ memset(&dflt_stor, 0, sizeof dflt_stor);
+ switch (af) {
+ case AF_INET:
+ d4->sin_family = af;
+#if !defined (LINUX_IPSEC)
+ d4->sin_len = sizeof(struct sockaddr_in);
+#endif
+ d4->sin_addr.s_addr = INADDR_ANY;
+ break;
+
+ case AF_INET6:
+ d6->sin6_family = af;
+#if !defined (LINUX_IPSEC)
+ d6->sin6_len = sizeof(struct sockaddr_in6);
+#endif
+ memcpy(&d6->sin6_addr.s6_addr, &in6addr_any,
+ sizeof in6addr_any);
+ break;
+ }
+
+ t = virtual_bind((struct sockaddr *)&dflt_stor);
+ if (!t)
+ log_error("virtual_bind_ADDR_ANY: "
+ "could not allocate default IPv%s ISAKMP port(s)",
+ af == AF_INET ? "4" : "6");
+ return t;
+}
+
+static int
+virtual_bind_if(char *ifname, struct sockaddr *if_addr, void *arg)
+{
+ struct conf_list *listen_on;
+ struct virtual_transport *v;
+ struct conf_list_node *address;
+ struct sockaddr *addr;
+ struct transport *t;
+ struct ifreq flags_ifr;
+ char *addr_str;
+ int s, error;
+
+#if defined (USE_DEBUG)
+ if (sockaddr2text(if_addr, &addr_str, 0))
+ addr_str = 0;
+
+ LOG_DBG((LOG_TRANSPORT, 90,
+ "virtual_bind_if: interface %s family %s address %s",
+ ifname ? ifname : "<unknown>",
+ if_addr->sa_family == AF_INET ? "v4" :
+ (if_addr->sa_family == AF_INET6 ? "v6" : "<unknown>"),
+ addr_str ? addr_str : "<invalid>"));
+ if (addr_str)
+ free(addr_str);
+#endif
+
+ /*
+ * Drop non-Internet stuff.
+ */
+ if ((if_addr->sa_family != AF_INET
+ || sysdep_sa_len(if_addr) != sizeof (struct sockaddr_in))
+ && (if_addr->sa_family != AF_INET6
+ || sysdep_sa_len(if_addr) != sizeof (struct sockaddr_in6)))
+ return 0;
+
+ /*
+ * Only create sockets for families we should listen to.
+ */
+ if (bind_family)
+ switch (if_addr->sa_family) {
+ case AF_INET:
+ if ((bind_family & BIND_FAMILY_INET4) == 0)
+ return 0;
+ break;
+ case AF_INET6:
+ if ((bind_family & BIND_FAMILY_INET6) == 0)
+ return 0;
+ break;
+ default:
+ return 0;
+ }
+
+ /*
+ * These special addresses are not useable as they have special meaning
+ * in the IP stack.
+ */
+ if (if_addr->sa_family == AF_INET
+ && (((struct sockaddr_in *)if_addr)->sin_addr.s_addr == INADDR_ANY
+ || (((struct sockaddr_in *)if_addr)->sin_addr.s_addr
+ == INADDR_NONE)))
+ return 0;
+
+ /*
+ * Go through the list of transports and see if we already have this
+ * address bound. If so, unmark the transport and skip it; this allows
+ * us to call this function when we suspect a new address has appeared.
+ */
+ if ((v = virtual_listen_lookup(if_addr)) != 0) {
+ LOG_DBG ((LOG_TRANSPORT, 90, "virtual_bind_if: "
+ "already bound"));
+ v->transport.flags &= ~TRANSPORT_MARK;
+ return 0;
+ }
+
+ /*
+ * Don't bother with interfaces that are down.
+ * Note: This socket is only used to collect the interface status.
+ */
+ s = socket(if_addr->sa_family, SOCK_DGRAM, 0);
+ if (s == -1) {
+ log_error("virtual_bind_if: "
+ "socket (%d, SOCK_DGRAM, 0) failed", if_addr->sa_family);
+ return -1;
+ }
+ strlcpy(flags_ifr.ifr_name, ifname, sizeof flags_ifr.ifr_name);
+ if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&flags_ifr) == -1) {
+ log_error("virtual_bind_if: "
+ "ioctl (%d, SIOCGIFFLAGS, ...) failed", s);
+ return -1;
+ }
+ close(s);
+ if (!(flags_ifr.ifr_flags & IFF_UP))
+ return 0;
+
+ /* Set the port number to zero. */
+ switch (if_addr->sa_family) {
+ case AF_INET:
+ ((struct sockaddr_in *)if_addr)->sin_port = htons(0);
+ break;
+ case AF_INET6:
+ ((struct sockaddr_in6 *)if_addr)->sin6_port = htons(0);
+ break;
+ default:
+ log_print("virtual_bind_if: unsupported protocol family %d",
+ if_addr->sa_family);
+ break;
+ }
+
+ /*
+ * If we are explicit about what addresses we can listen to, be sure
+ * to respect that option.
+ * This is quite wasteful redoing the list-run for every interface,
+ * but who cares? This is not an operation that needs to be fast.
+ */
+ listen_on = conf_get_list("General", "Listen-on");
+ if (listen_on) {
+ for (address = TAILQ_FIRST(&listen_on->fields); address;
+ address = TAILQ_NEXT(address, link)) {
+ if (text2sockaddr(address->field, 0, &addr)) {
+ log_print("virtual_bind_if: "
+ "invalid address %s in \"Listen-on\"",
+ address->field);
+ continue;
+ }
+
+ /* If found, take the easy way out. */
+ if (memcmp(addr, if_addr,
+ sysdep_sa_len(addr)) == 0) {
+ free(addr);
+ break;
+ }
+ free(addr);
+ }
+ conf_free_list(listen_on);
+
+ /*
+ * If address is zero then we did not find the address among
+ * the ones we should listen to.
+ * XXX We do not discover if we do not find our listen
+ * addresses. Maybe this should be the other way round.
+ */
+ if (!address)
+ return 0;
+ }
+
+ t = virtual_bind(if_addr);
+ if (!t) {
+ error = sockaddr2text(if_addr, &addr_str, 0);
+ log_print("virtual_bind_if: failed to create a socket on %s",
+ error ? "unknown" : addr_str);
+ if (!error)
+ free(addr_str);
+ return -1;
+ }
+ LIST_INSERT_HEAD(&virtual_listen_list, (struct virtual_transport *)t,
+ link);
+ return 0;
+}
+
+static struct transport *
+virtual_clone(struct transport *vt, struct sockaddr *raddr)
+{
+ struct virtual_transport *v = (struct virtual_transport *)vt;
+ struct virtual_transport *v2;
+ struct transport *t;
+
+ t = malloc(sizeof *v);
+ if (!t) {
+ log_error("virtual_clone: malloc(%lu) failed",
+ (unsigned long)sizeof *v);
+ return 0;
+ }
+ v2 = (struct virtual_transport *)t;
+
+ memcpy(v2, v, sizeof *v);
+
+ if (v->encap_is_active) {
+ v2->main = 0;
+ v2->encap = v->encap->vtbl->clone(v->encap, raddr);
+ v2->encap->virtual = (struct transport *)v2;
+ } else {
+ v2->main = v->main->vtbl->clone(v->main, raddr);
+ v2->main->virtual = (struct transport *)v2;
+ v2->encap = 0;
+ }
+ LOG_DBG((LOG_TRANSPORT, 50, "virtual_clone: old %p new %p (->%s %p)",
+ v, t, v->encap_is_active ? "encap" : "main",
+ v->encap_is_active ? v2->encap : v2->main));
+
+ t->flags &= ~TRANSPORT_LISTEN;
+ transport_setup(t, 1);
+
+ transport_reference(t);
+ transport_reference(v->encap_is_active ? v2->encap : v2->main);
+
+ return t;
+}
+
+static struct transport *
+virtual_create(char *name)
+{
+ struct virtual_transport *v;
+ struct transport *t, *t2;
+
+ t = transport_create("udp_physical", name);
+ if (!t)
+ return 0;
+
+#if defined (USE_NAT_TRAVERSAL)
+ t2 = transport_create("udp_encap", name);
+ if (!t2) {
+ t->vtbl->remove(t);
+ return 0;
+ }
+#else
+ t2 = 0;
+#endif
+
+ v = (struct virtual_transport *)calloc(1, sizeof *v);
+ if (!v) {
+ log_error("virtual_create: calloc(1, %lu) failed",
+ (unsigned long)sizeof *v);
+ t->vtbl->remove(t);
+ if (t2)
+ t2->vtbl->remove(t2);
+ return 0;
+ }
+
+ memcpy(v, t, sizeof *t);
+ v->transport.virtual = 0;
+ v->main = t;
+ v->encap = t2;
+ v->transport.vtbl = &virtual_transport_vtbl;
+ t->virtual = (struct transport *)v;
+ if (t2)
+ t2->virtual = (struct transport *)v;
+ transport_setup(&v->transport, 1);
+ LIST_INSERT_HEAD(&virtual_listen_list, v, link);
+
+ return (struct transport *)v;
+}
+
+static void
+virtual_remove(struct transport *t)
+{
+ struct virtual_transport *v = (struct virtual_transport *)t;
+
+ if (v->encap)
+ v->encap->vtbl->remove(v->encap);
+ if (v->main)
+ v->main->vtbl->remove(v->main);
+ if (v->link.le_prev)
+ LIST_REMOVE(v, link);
+
+ free(t);
+}
+
+static void
+virtual_report(struct transport *t)
+{
+ return;
+}
+
+static void
+virtual_handle_message(struct transport *t)
+{
+ struct virtual_transport *v = (struct virtual_transport *)t;
+
+ if (t == default_transport || t == default_transport6) {
+ /* XXX drain pending message. See udp_handle_message(). */
+
+ virtual_reinit();
+
+ /*
+ * As we don't know the actual destination address of the
+ * packet, we can't really deal with it. So, just ignore it
+ * and hope we catch the retransmission.
+ */
+ return;
+ }
+
+ if (v->encap_is_active)
+ v->encap->vtbl->handle_message(v->encap);
+ else
+ v->main->vtbl->handle_message(v->main);
+}
+
+static int
+virtual_send_message(struct message *msg, struct transport *t)
+{
+ struct virtual_transport *v =
+ (struct virtual_transport *)msg->transport;
+
+ /* XXX Debug */
+ if (t)
+ log_print("virtual_send_message: called with "
+ "transport %p != NULL", t);
+
+#if defined (USE_NAT_TRAVERSAL)
+ if (v->encap_is_active == 0 &&
+ (msg->exchange->flags & EXCHANGE_FLAG_NAT_T_ENABLE)) {
+ LOG_DBG((LOG_MESSAGE, 10, "virtual_send_message: "
+ "switching to ENCAP"));
+ v->encap_is_active++;
+ }
+#endif
+
+ if (v->encap_is_active)
+ return v->encap->vtbl->send_message(msg, v->encap);
+ else
+ return v->main->vtbl->send_message(msg, v->main);
+}
+
+static int
+virtual_fd_set(struct transport *t, fd_set *fds, int bit)
+{
+ struct virtual_transport *v = (struct virtual_transport *)t;
+ struct udp_transport *u;
+
+ if (v->encap_is_active)
+ u = (struct udp_transport *)v->encap;
+ else
+ u = (struct udp_transport *)v->main;
+
+ if (bit)
+ FD_SET(u->s, fds);
+ else
+ FD_CLR(u->s, fds);
+
+ return u->s + 1;
+}
+
+static int
+virtual_fd_isset(struct transport *t, fd_set *fds)
+{
+ struct virtual_transport *v = (struct virtual_transport *)t;
+ struct udp_transport *u;
+
+ if (v->encap_is_active)
+ u = (struct udp_transport *)v->encap;
+ else
+ u = (struct udp_transport *)v->main;
+
+ return FD_ISSET(u->s, fds);
+}
+
+static void
+virtual_get_src(struct transport *t, struct sockaddr **s)
+{
+ struct virtual_transport *v = (struct virtual_transport *)t;
+
+ if (v->encap_is_active)
+ v->encap->vtbl->get_src(v->encap, s);
+ else
+ v->main->vtbl->get_src(v->main, s);
+}
+
+static void
+virtual_get_dst(struct transport *t, struct sockaddr **s)
+{
+ struct virtual_transport *v = (struct virtual_transport *)t;
+
+ if (v->encap_is_active)
+ v->encap->vtbl->get_dst(v->encap, s);
+ else
+ v->main->vtbl->get_dst(v->main, s);
+}
+
+static char *
+virtual_decode_ids(struct transport *t)
+{
+ struct virtual_transport *v = (struct virtual_transport *)t;
+
+ if (v->encap_is_active)
+ return v->encap->vtbl->decode_ids(t);
+ else
+ return v->main->vtbl->decode_ids(t);
+}
+
+static struct msg_head *
+virtual_get_queue(struct message *msg)
+{
+ if (msg->flags & MSG_PRIORITIZED)
+ return &msg->transport->prio_sendq;
+ else
+ return &msg->transport->sendq;
+}
diff --git a/sbin/isakmpd/virtual.h b/sbin/isakmpd/virtual.h
new file mode 100644
index 00000000000..7a3bd21cc6c
--- /dev/null
+++ b/sbin/isakmpd/virtual.h
@@ -0,0 +1,42 @@
+/* $OpenBSD: virtual.h,v 1.1 2004/06/20 15:24:05 ho Exp $ */
+
+/*
+ * Copyright (c) 2004 Håkan Olsson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _TRP_VIRTUAL_H_
+#define _TRP_VIRTUAL_H_
+
+struct virtual_transport {
+ struct transport transport;
+ struct transport *main; /* Normally this transport is used. */
+ struct transport *encap; /* Or this, depending on 'encap_is_active'. */
+ int encap_is_active;
+ LIST_ENTRY (virtual_transport) link;
+};
+
+void virtual_init(void);
+struct virtual_transport *virtual_get_default(sa_family_t);
+struct virtual_transport *virtual_listen_lookup(struct sockaddr *);
+
+#endif /* _TRP_VIRTUAL_H_ */