From 296e43726af7240ad61013d007264b8effec9333 Mon Sep 17 00:00:00 2001 From: Hakan Olsson Date: Sun, 20 Jun 2004 15:24:06 +0000 Subject: NAT-Traversal for isakmpd. Work in progress... hshoexer@ ok. --- sbin/isakmpd/Makefile | 14 +- sbin/isakmpd/exchange.h | 12 +- sbin/isakmpd/features/nat_traversal | 54 +++ sbin/isakmpd/ike_phase_1.c | 49 ++- sbin/isakmpd/init.c | 23 +- sbin/isakmpd/ipsec.c | 50 +-- sbin/isakmpd/isakmp.h | 15 +- sbin/isakmpd/isakmp_fld.fld | 14 +- sbin/isakmpd/message.c | 103 ++++-- sbin/isakmpd/nat_traversal.c | 384 ++++++++++++++++++++ sbin/isakmpd/nat_traversal.h | 36 ++ sbin/isakmpd/policy.c | 19 +- sbin/isakmpd/transport.c | 151 +++++--- sbin/isakmpd/transport.h | 27 +- sbin/isakmpd/udp.c | 531 ++++++--------------------- sbin/isakmpd/udp.h | 16 +- sbin/isakmpd/udp_encap.c | 461 +++++++++++++++++++++++ sbin/isakmpd/udp_encap.h | 37 ++ sbin/isakmpd/util.c | 19 +- sbin/isakmpd/util.h | 5 +- sbin/isakmpd/virtual.c | 706 ++++++++++++++++++++++++++++++++++++ sbin/isakmpd/virtual.h | 42 +++ 22 files changed, 2206 insertions(+), 562 deletions(-) create mode 100644 sbin/isakmpd/features/nat_traversal create mode 100644 sbin/isakmpd/nat_traversal.c create mode 100644 sbin/isakmpd/nat_traversal.h create mode 100644 sbin/isakmpd/udp_encap.c create mode 100644 sbin/isakmpd/udp_encap.h create mode 100644 sbin/isakmpd/virtual.c create mode 100644 sbin/isakmpd/virtual.h 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 .include + +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 +#include + +#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; @@ -273,171 +267,6 @@ udp_bind(const struct sockaddr *addr) return udp_make(src); } -/* - * 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. @@ -445,16 +274,19 @@ udp_bind_if(char *ifname, struct sockaddr *if_addr, void *arg) 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 : "")); + LOG_DBG((LOG_REPORT, 0, "udp_report: fd %d src %s:%u dst %s:%u", u->s, + src, ntohs(sport), dst ? dst : "", ntohs(dport))); ret: if (dst) @@ -550,165 +409,6 @@ ret: free(src); } -/* - * 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. @@ -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, "", 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, "", 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 +#include +#include +#ifndef linux +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 = ▮ + 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 +#include +#include +#ifndef linux +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 : "", + if_addr->sa_family == AF_INET ? "v4" : + (if_addr->sa_family == AF_INET6 ? "v6" : ""), + addr_str ? addr_str : "")); + 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_ */ -- cgit v1.2.3