diff options
Diffstat (limited to 'keyexchange/isakmpd-20041012/transport.c')
-rw-r--r-- | keyexchange/isakmpd-20041012/transport.c | 431 |
1 files changed, 431 insertions, 0 deletions
diff --git a/keyexchange/isakmpd-20041012/transport.c b/keyexchange/isakmpd-20041012/transport.c new file mode 100644 index 0000000..023e819 --- /dev/null +++ b/keyexchange/isakmpd-20041012/transport.c @@ -0,0 +1,431 @@ +/* $OpenBSD: transport.c,v 1.30 2004/08/08 19:11:06 deraadt 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, 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. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#include <sys/param.h> +#include <sys/queue.h> +#include <string.h> + +#include "sysdep.h" + +#include "conf.h" +#include "exchange.h" +#include "log.h" +#include "message.h" +#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 + +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) +{ + struct transport_vtbl *method; + + for (method = LIST_FIRST(&transport_method_list); method; + method = LIST_NEXT(method, link)) + if (method->reinit) + method->reinit(); +} + +/* Initialize the transport maintenance module. */ +void +transport_init(void) +{ + LIST_INIT(&transport_list); + LIST_INIT(&transport_method_list); +} + +/* Register another transport T. */ +void +transport_setup(struct transport *t, int toplevel) +{ + if (toplevel) { + /* Only the toplevel (virtual) transport has sendqueues. */ + LOG_DBG((LOG_TRANSPORT, 70, + "transport_setup: virtual transport %p", t)); + TAILQ_INIT(&t->sendq); + TAILQ_INIT(&t->prio_sendq); + t->refcnt = 0; + } else { + /* udp and udp_encap trp goes into the transport list. */ + LOG_DBG((LOG_TRANSPORT, 70, + "transport_setup: added %p to transport list", t)); + LIST_INSERT_HEAD(&transport_list, t, link); + t->refcnt = 1; + } + t->flags = 0; +} + +/* Add a referer to transport T. */ +void +transport_reference(struct transport *t) +{ + t->refcnt++; + LOG_DBG((LOG_TRANSPORT, 95, + "transport_reference: transport %p now has %d references", t, + t->refcnt)); +} + +/* + * Remove a referer from transport T, removing all of T when no referers left. + */ +void +transport_release(struct transport *t) +{ + LOG_DBG((LOG_TRANSPORT, 95, + "transport_release: transport %p had %d references", t, + t->refcnt)); + if (--t->refcnt) + return; + + LOG_DBG((LOG_TRANSPORT, 70, "transport_release: freeing %p", t)); + t->vtbl->remove(t); +} + +void +transport_report(void) +{ + struct virtual_transport *v; + struct transport *t; + struct message *msg; + + for (t = LIST_FIRST(&transport_list); t; t = LIST_NEXT(t, link)) { + LOG_DBG((LOG_REPORT, 0, + "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. + */ + 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); + } + } +} + +int +transport_prio_sendqs_empty(void) +{ + struct transport *t; + + for (t = LIST_FIRST(&transport_list); t; t = LIST_NEXT(t, link)) + if (TAILQ_FIRST(&t->virtual->prio_sendq)) + return 0; + return 1; +} + +/* Register another transport method T. */ +void +transport_method_add(struct transport_vtbl *t) +{ + LIST_INSERT_HEAD(&transport_method_list, t, link); +} + +/* Apply a function FUNC on all registered (non-toplevel) transports. */ +void +transport_map(void (*func) (struct transport *)) +{ + struct transport *t; + + for (t = LIST_FIRST(&transport_list); t; t = LIST_NEXT(t, link)) + (*func) (t); +} + +/* + * Build up a file descriptor set FDS with all transport descriptors we want + * to read from. Return the number of file descriptors select(2) needs to + * check in order to cover the ones we setup in here. + */ +int +transport_fd_set(fd_set * fds) +{ + struct transport *t; + int n; + int max = -1; + + for (t = LIST_FIRST(&transport_list); t; t = LIST_NEXT(t, link)) + if (t->virtual->flags & TRANSPORT_LISTEN) { + n = t->vtbl->fd_set(t, 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; +} + +/* + * Build up a file descriptor set FDS with all the descriptors belonging to + * transport where messages are queued for transmittal. Return the number + * of file descriptors select(2) needs to check in order to cover the ones + * we setup in here. + */ +int +transport_pending_wfd_set(fd_set * fds) +{ + struct transport *t; + int n; + int max = -1; + + for (t = LIST_FIRST(&transport_list); t; t = LIST_NEXT(t, link)) { + if (TAILQ_FIRST(&t->virtual->sendq) || + TAILQ_FIRST(&t->virtual->prio_sendq)) { + n = t->vtbl->fd_set(t, 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; + } + } + return max + 1; +} + +/* + * For each transport with a file descriptor in FDS, try to get an + * incoming message and start processing it. + */ +void +transport_handle_messages(fd_set *fds) +{ + struct transport *t; + + for (t = LIST_FIRST(&transport_list); t; t = LIST_NEXT(t, link)) { + if ((t->flags & TRANSPORT_LISTEN) && + (*t->vtbl->fd_isset)(t, fds)) { + (*t->virtual->vtbl->handle_message)(t); + (*t->vtbl->fd_set)(t, fds, 0); + } + } +} + +/* + * Send the first queued message on the transports found whose file + * descriptor is in FDS and has messages queued. Remove the fd bit from + * FDS as soon as one message has been sent on it so other transports + * sharing the socket won't get service without an intervening select + * call. Perhaps a fairness strategy should be implemented between + * such transports. Now early transports in the list will potentially + * be favoured to later ones sharing the file descriptor. + */ +void +transport_send_messages(fd_set * fds) +{ + struct transport *t, *next; + struct message *msg; + struct exchange *exchange; + struct timeval expiration; + int expiry, ok_to_drop_message; + + /* + * Reference all transports first so noone will disappear while in + * use. + */ + for (t = LIST_FIRST(&transport_list); t; t = LIST_NEXT(t, link)) + transport_reference(t->virtual); + + for (t = LIST_FIRST(&transport_list); t; t = LIST_NEXT(t, link)) { + if ((TAILQ_FIRST(&t->virtual->sendq) || + TAILQ_FIRST(&t->virtual->prio_sendq)) && + t->vtbl->fd_isset(t, fds)) { + /* Remove fd bit. */ + t->vtbl->fd_set(t, fds, 0); + + /* Prefer a message from the prioritized sendq. */ + 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->virtual->sendq); + TAILQ_REMOVE(&t->virtual->sendq, msg, link); + } + + msg->flags &= ~MSG_IN_TRANSIT; + exchange = msg->exchange; + exchange->in_transit = 0; + + /* + * We disregard the potential error message here, + * hoping that the retransmit will go better. + * XXX Consider a retry/fatal error discriminator. + */ + 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. + * + * 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 ((msg->flags & MSG_LAST) == 0) { + if (msg->xmits > conf_get_num("General", + "retransmits", RETRANSMIT_DEFAULT)) { + log_print("transport_send_messages: " + "giving up on message %p, " + "exchange %s", msg, + exchange->name ? exchange->name : + "<unnamed>"); + /* Be more verbose here. */ + if (exchange->phase == 1) { + log_print( + "transport_send_messages: " + "either this message did " + "not reach the other " + "peer"); + if (exchange->initiator) + log_print("transport_send_messages: " + "or the response" + "message did not " + "reach us back"); + else + log_print("transport_send_messages: " + "or this is an " + "attempted IKE " + "scan"); + } + exchange->last_sent = 0; +#ifdef notyet + exchange_free(exchange); + exchange = 0; +#endif + } else { + gettimeofday(&expiration, 0); + + /* + * XXX Calculate from round trip + * timings and a backoff func. + */ + expiry = msg->xmits * 2 + 5; + expiration.tv_sec += expiry; + LOG_DBG((LOG_TRANSPORT, 30, + "transport_send_messages: " + "message %p scheduled for " + "retransmission %d in %d secs", + msg, msg->xmits, expiry)); + if (msg->retrans) + timer_remove_event(msg->retrans); + msg->retrans + = timer_add_event("message_send_expire", + (void (*) (void *)) message_send_expire, + msg, &expiration); + /* + * If we cannot retransmit, we + * cannot... + */ + exchange->last_sent = + msg->retrans ? msg : 0; + } + } else + exchange->last_sent = + exchange->last_received ? msg : 0; + + /* + * If this message is not referred to for later + * retransmission it will be ok for us to drop it + * after the post-send function. But as the post-send + * function may remove the exchange, we need to + * remember this fact here. + */ + ok_to_drop_message = exchange->last_sent == 0; + + /* + * 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 + * retransmits are possible. + */ + if (msg->xmits == 1) + message_post_send(msg); + + if (ok_to_drop_message) + message_free(msg); + } + } + + for (t = LIST_FIRST(&transport_list); t; t = next) { + next = LIST_NEXT(t, link); + transport_release(t->virtual); + } +} + +/* + * Textual search after the transport method denoted by NAME, then create + * a transport connected to the peer with address ADDR, given in a transport- + * specific string format. + */ +struct transport * +transport_create(char *name, char *addr) +{ + struct transport_vtbl *method; + + for (method = LIST_FIRST(&transport_method_list); method; + method = LIST_NEXT(method, link)) + if (strcmp(method->name, name) == 0) + return (*method->create) (addr); + return 0; +} |