#include <sys/queue.h>
#include <netdb.h>
#include <string.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"
#define RETRANSMIT_DEFAULT 10
LIST_HEAD(transport_method_list, transport_vtbl) transport_method_list;
struct transport_list transport_list;
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();
}
void
transport_init(void)
{
LIST_INIT(&transport_list);
LIST_INIT(&transport_method_list);
}
void
transport_setup(struct transport *t, int toplevel)
{
if (toplevel) {
LOG_DBG((LOG_TRANSPORT, 70,
"transport_setup: virtual transport %p", t));
TAILQ_INIT(&t->sendq);
TAILQ_INIT(&t->prio_sendq);
t->refcnt = 0;
} else {
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;
}
void
transport_reference(struct transport *t)
{
t->refcnt++;
LOG_DBG((LOG_TRANSPORT, 95,
"transport_reference: transport %p now has %d references", t,
t->refcnt));
}
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));
t->vtbl->report(t);
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;
}
void
transport_method_add(struct transport_vtbl *t)
{
LIST_INSERT_HEAD(&transport_method_list, t, link);
}
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;
}
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;
}
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);
}
}
}
void
transport_send_messages(fd_set * fds)
{
struct transport *t, *next;
struct message *msg;
struct exchange *exchange;
struct sockaddr *dst;
struct timespec expiration;
int expiry, ok_to_drop_message;
char peer[NI_MAXHOST], peersv[NI_MAXSERV];
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)) {
t->vtbl->fd_set(t, fds, 0);
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;
t->virtual->vtbl->send_message(msg, 0);
msg->xmits++;
if ((msg->flags & MSG_LAST) == 0) {
if (msg->flags & MSG_DONTRETRANSMIT)
exchange->last_sent = 0;
else if (msg->xmits > conf_get_num("General",
"retransmits", RETRANSMIT_DEFAULT)) {
t->virtual->vtbl->get_dst(t->virtual, &dst);
if (getnameinfo(dst, SA_LEN(dst), peer,
sizeof peer, peersv, sizeof peersv,
NI_NUMERICHOST | NI_NUMERICSERV)) {
strlcpy(peer, "<unknown>", sizeof peer);
strlcpy(peersv, "<?>", sizeof peersv);
}
log_print("transport_send_messages: "
"giving up on exchange %s, no "
"response from peer %s:%s",
exchange->name ? exchange->name :
"<unnamed>", peer, peersv);
exchange->last_sent = 0;
#ifdef notyet
exchange_free(exchange);
exchange = 0;
#endif
} else {
clock_gettime(CLOCK_MONOTONIC,
&expiration);
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);
exchange->last_sent =
msg->retrans ? msg : 0;
}
} else
exchange->last_sent =
exchange->last_received ? msg : 0;
ok_to_drop_message = exchange->last_sent == 0;
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);
}
}
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;
}