1*9ddd0770Smortimer /* $OpenBSD: transport.c,v 1.39 2021/01/28 01:18:44 mortimer Exp $ */
2bcfff259Sniklas /* $EOM: transport.c,v 1.43 2000/10/10 12:36:39 provos Exp $ */
32040585eSniklas
42040585eSniklas /*
5758ee5e5Sniklas * Copyright (c) 1998, 1999 Niklas Hallqvist. All rights reserved.
6cd6bf844Sho * Copyright (c) 2001, 2004 H�kan Olsson. All rights reserved.
72040585eSniklas *
82040585eSniklas * Redistribution and use in source and binary forms, with or without
92040585eSniklas * modification, are permitted provided that the following conditions
102040585eSniklas * are met:
112040585eSniklas * 1. Redistributions of source code must retain the above copyright
122040585eSniklas * notice, this list of conditions and the following disclaimer.
132040585eSniklas * 2. Redistributions in binary form must reproduce the above copyright
142040585eSniklas * notice, this list of conditions and the following disclaimer in the
152040585eSniklas * documentation and/or other materials provided with the distribution.
162040585eSniklas *
172040585eSniklas * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
182040585eSniklas * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
192040585eSniklas * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
202040585eSniklas * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
212040585eSniklas * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
222040585eSniklas * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
232040585eSniklas * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
242040585eSniklas * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
252040585eSniklas * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
262040585eSniklas * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
272040585eSniklas */
282040585eSniklas
292040585eSniklas /*
302040585eSniklas * This code was written under funding by Ericsson Radio Systems.
312040585eSniklas */
322040585eSniklas
332040585eSniklas #include <sys/queue.h>
34349b5519Shshoexer #include <netdb.h>
35349fdaddSho #include <string.h>
362040585eSniklas
372040585eSniklas #include "conf.h"
382040585eSniklas #include "exchange.h"
392040585eSniklas #include "log.h"
402040585eSniklas #include "message.h"
412040585eSniklas #include "sa.h"
422040585eSniklas #include "timer.h"
432040585eSniklas #include "transport.h"
44cd6bf844Sho #include "virtual.h"
452040585eSniklas
46893467d6Sniklas /* If no retransmit limit is given, use this as a default. */
47893467d6Sniklas #define RETRANSMIT_DEFAULT 10
48893467d6Sniklas
492040585eSniklas LIST_HEAD(transport_method_list, transport_vtbl) transport_method_list;
502040585eSniklas
51*9ddd0770Smortimer struct transport_list transport_list;
52*9ddd0770Smortimer
53862799fcSangelos /* Call the reinit function of the various transports. */
54862799fcSangelos void
transport_reinit(void)55862799fcSangelos transport_reinit(void)
56862799fcSangelos {
57862799fcSangelos struct transport_vtbl *method;
58862799fcSangelos
59862799fcSangelos for (method = LIST_FIRST(&transport_method_list); method;
60862799fcSangelos method = LIST_NEXT(method, link))
61cd6bf844Sho if (method->reinit)
62862799fcSangelos method->reinit();
63862799fcSangelos }
64862799fcSangelos
652040585eSniklas /* Initialize the transport maintenance module. */
662040585eSniklas void
transport_init(void)672040585eSniklas transport_init(void)
682040585eSniklas {
692040585eSniklas LIST_INIT(&transport_list);
702040585eSniklas LIST_INIT(&transport_method_list);
712040585eSniklas }
722040585eSniklas
732040585eSniklas /* Register another transport T. */
742040585eSniklas void
transport_setup(struct transport * t,int toplevel)75cd6bf844Sho transport_setup(struct transport *t, int toplevel)
762040585eSniklas {
77dec6ea27Sho if (toplevel) {
78dec6ea27Sho /* Only the toplevel (virtual) transport has sendqueues. */
79dec6ea27Sho LOG_DBG((LOG_TRANSPORT, 70,
80dec6ea27Sho "transport_setup: virtual transport %p", t));
812040585eSniklas TAILQ_INIT(&t->sendq);
82867e2181Sho TAILQ_INIT(&t->prio_sendq);
83dec6ea27Sho t->refcnt = 0;
84dec6ea27Sho } else {
85dec6ea27Sho /* udp and udp_encap trp goes into the transport list. */
86dec6ea27Sho LOG_DBG((LOG_TRANSPORT, 70,
87dec6ea27Sho "transport_setup: added %p to transport list", t));
88dec6ea27Sho LIST_INSERT_HEAD(&transport_list, t, link);
89dec6ea27Sho t->refcnt = 1;
90cd6bf844Sho }
91893467d6Sniklas t->flags = 0;
92758ee5e5Sniklas }
93758ee5e5Sniklas
94758ee5e5Sniklas /* Add a referer to transport T. */
95758ee5e5Sniklas void
transport_reference(struct transport * t)96758ee5e5Sniklas transport_reference(struct transport *t)
97758ee5e5Sniklas {
98758ee5e5Sniklas t->refcnt++;
993bf15937Sho LOG_DBG((LOG_TRANSPORT, 95,
100758ee5e5Sniklas "transport_reference: transport %p now has %d references", t,
10151ca15aeSniklas t->refcnt));
102758ee5e5Sniklas }
103758ee5e5Sniklas
104758ee5e5Sniklas /*
105758ee5e5Sniklas * Remove a referer from transport T, removing all of T when no referers left.
106758ee5e5Sniklas */
107758ee5e5Sniklas void
transport_release(struct transport * t)108758ee5e5Sniklas transport_release(struct transport *t)
109758ee5e5Sniklas {
1103bf15937Sho LOG_DBG((LOG_TRANSPORT, 95,
111758ee5e5Sniklas "transport_release: transport %p had %d references", t,
11251ca15aeSniklas t->refcnt));
113758ee5e5Sniklas if (--t->refcnt)
114758ee5e5Sniklas return;
115758ee5e5Sniklas
11651ca15aeSniklas LOG_DBG((LOG_TRANSPORT, 70, "transport_release: freeing %p", t));
117758ee5e5Sniklas t->vtbl->remove(t);
118758ee5e5Sniklas }
119758ee5e5Sniklas
120758ee5e5Sniklas void
transport_report(void)121758ee5e5Sniklas transport_report(void)
122758ee5e5Sniklas {
123cd6bf844Sho struct virtual_transport *v;
124758ee5e5Sniklas struct transport *t;
125758ee5e5Sniklas struct message *msg;
126758ee5e5Sniklas
127fb9475d6Sderaadt for (t = LIST_FIRST(&transport_list); t; t = LIST_NEXT(t, link)) {
12851ca15aeSniklas LOG_DBG((LOG_REPORT, 0,
129758ee5e5Sniklas "transport_report: transport %p flags %x refcnt %d", t,
13051ca15aeSniklas t->flags, t->refcnt));
131758ee5e5Sniklas
132cd6bf844Sho /* XXX Report sth on the virtual transport? */
133758ee5e5Sniklas t->vtbl->report(t);
134758ee5e5Sniklas
135fb9475d6Sderaadt /*
136fb9475d6Sderaadt * This is the reason message_dump_raw lives outside
137fb9475d6Sderaadt * message.c.
138fb9475d6Sderaadt */
139cd6bf844Sho v = (struct virtual_transport *)t->virtual;
140cd6bf844Sho if ((v->encap_is_active && v->encap == t) ||
141cd6bf844Sho (!v->encap_is_active && v->main == t)) {
142cd6bf844Sho for (msg = TAILQ_FIRST(&t->virtual->prio_sendq); msg;
143867e2181Sho msg = TAILQ_NEXT(msg, link))
144cd6bf844Sho message_dump_raw("udp_report(prio)", msg,
145cd6bf844Sho LOG_REPORT);
146867e2181Sho
147cd6bf844Sho for (msg = TAILQ_FIRST(&t->virtual->sendq); msg;
148cd6bf844Sho msg = TAILQ_NEXT(msg, link))
149cd6bf844Sho message_dump_raw("udp_report", msg,
150cd6bf844Sho LOG_REPORT);
151cd6bf844Sho }
152758ee5e5Sniklas }
1532040585eSniklas }
1542040585eSniklas
155867e2181Sho int
transport_prio_sendqs_empty(void)156867e2181Sho transport_prio_sendqs_empty(void)
157867e2181Sho {
158867e2181Sho struct transport *t;
159867e2181Sho
160867e2181Sho for (t = LIST_FIRST(&transport_list); t; t = LIST_NEXT(t, link))
161cd6bf844Sho if (TAILQ_FIRST(&t->virtual->prio_sendq))
162867e2181Sho return 0;
163867e2181Sho return 1;
164867e2181Sho }
165867e2181Sho
1662040585eSniklas /* Register another transport method T. */
1672040585eSniklas void
transport_method_add(struct transport_vtbl * t)1682040585eSniklas transport_method_add(struct transport_vtbl *t)
1692040585eSniklas {
1702040585eSniklas LIST_INSERT_HEAD(&transport_method_list, t, link);
1712040585eSniklas }
1722040585eSniklas
1732040585eSniklas /*
174bbbf0f95Sho * Build up a file descriptor set FDS with all transport descriptors we want
1752040585eSniklas * to read from. Return the number of file descriptors select(2) needs to
1762040585eSniklas * check in order to cover the ones we setup in here.
1772040585eSniklas */
1782040585eSniklas int
transport_fd_set(fd_set * fds)1792040585eSniklas transport_fd_set(fd_set * fds)
1802040585eSniklas {
181cd6bf844Sho struct transport *t;
1822040585eSniklas int n;
1832040585eSniklas int max = -1;
1842040585eSniklas
1852040585eSniklas for (t = LIST_FIRST(&transport_list); t; t = LIST_NEXT(t, link))
186cd6bf844Sho if (t->virtual->flags & TRANSPORT_LISTEN) {
187aa584aacSho n = t->vtbl->fd_set(t, fds, 1);
1882040585eSniklas if (n > max)
1892040585eSniklas max = n;
190cd6bf844Sho
191cd6bf844Sho LOG_DBG((LOG_TRANSPORT, 95, "transport_fd_set: "
192cd6bf844Sho "transport %p (virtual %p) fd %d", t,
193cd6bf844Sho t->virtual, n));
1942040585eSniklas }
1952040585eSniklas return max + 1;
1962040585eSniklas }
1972040585eSniklas
1982040585eSniklas /*
199bbbf0f95Sho * Build up a file descriptor set FDS with all the descriptors belonging to
2002040585eSniklas * transport where messages are queued for transmittal. Return the number
2012040585eSniklas * of file descriptors select(2) needs to check in order to cover the ones
2022040585eSniklas * we setup in here.
2032040585eSniklas */
2042040585eSniklas int
transport_pending_wfd_set(fd_set * fds)2052040585eSniklas transport_pending_wfd_set(fd_set * fds)
2062040585eSniklas {
207cd6bf844Sho struct transport *t;
2082040585eSniklas int n;
2092040585eSniklas int max = -1;
2102040585eSniklas
211fb9475d6Sderaadt for (t = LIST_FIRST(&transport_list); t; t = LIST_NEXT(t, link)) {
212cd6bf844Sho if (TAILQ_FIRST(&t->virtual->sendq) ||
213cd6bf844Sho TAILQ_FIRST(&t->virtual->prio_sendq)) {
214aa584aacSho n = t->vtbl->fd_set(t, fds, 1);
215dec6ea27Sho LOG_DBG((LOG_TRANSPORT, 95,
216dec6ea27Sho "transport_pending_wfd_set: "
217cd6bf844Sho "transport %p (virtual %p) fd %d pending", t,
218cd6bf844Sho t->virtual, n));
2192040585eSniklas if (n > max)
2202040585eSniklas max = n;
2212040585eSniklas }
2222040585eSniklas }
2232040585eSniklas return max + 1;
2242040585eSniklas }
2252040585eSniklas
2262040585eSniklas /*
2272040585eSniklas * For each transport with a file descriptor in FDS, try to get an
2282040585eSniklas * incoming message and start processing it.
2292040585eSniklas */
2302040585eSniklas void
transport_handle_messages(fd_set * fds)2312040585eSniklas transport_handle_messages(fd_set *fds)
2322040585eSniklas {
2332040585eSniklas struct transport *t;
2342040585eSniklas
235cd6bf844Sho for (t = LIST_FIRST(&transport_list); t; t = LIST_NEXT(t, link)) {
23650eea14cSho if ((t->flags & TRANSPORT_LISTEN) &&
237aa584aacSho (*t->vtbl->fd_isset)(t, fds)) {
238aa584aacSho (*t->virtual->vtbl->handle_message)(t);
239aa584aacSho (*t->vtbl->fd_set)(t, fds, 0);
240cd6bf844Sho }
241cd6bf844Sho }
2422040585eSniklas }
2432040585eSniklas
2442040585eSniklas /*
245a6341f1eSniklas * Send the first queued message on the transports found whose file
246a6341f1eSniklas * descriptor is in FDS and has messages queued. Remove the fd bit from
247a6341f1eSniklas * FDS as soon as one message has been sent on it so other transports
248a6341f1eSniklas * sharing the socket won't get service without an intervening select
249a6341f1eSniklas * call. Perhaps a fairness strategy should be implemented between
250a6341f1eSniklas * such transports. Now early transports in the list will potentially
251a6341f1eSniklas * be favoured to later ones sharing the file descriptor.
2522040585eSniklas */
2532040585eSniklas void
transport_send_messages(fd_set * fds)2542040585eSniklas transport_send_messages(fd_set * fds)
2552040585eSniklas {
256a6341f1eSniklas struct transport *t, *next;
2572040585eSniklas struct message *msg;
258a6341f1eSniklas struct exchange *exchange;
259349b5519Shshoexer struct sockaddr *dst;
2606ee513e5Sjca struct timespec expiration;
261a6341f1eSniklas int expiry, ok_to_drop_message;
262349b5519Shshoexer char peer[NI_MAXHOST], peersv[NI_MAXSERV];
2632040585eSniklas
264fb9475d6Sderaadt /*
265fb9475d6Sderaadt * Reference all transports first so noone will disappear while in
266fb9475d6Sderaadt * use.
267fb9475d6Sderaadt */
268dec6ea27Sho for (t = LIST_FIRST(&transport_list); t; t = LIST_NEXT(t, link))
269cd6bf844Sho transport_reference(t->virtual);
270a6341f1eSniklas
271fb9475d6Sderaadt for (t = LIST_FIRST(&transport_list); t; t = LIST_NEXT(t, link)) {
272cd6bf844Sho if ((TAILQ_FIRST(&t->virtual->sendq) ||
273cd6bf844Sho TAILQ_FIRST(&t->virtual->prio_sendq)) &&
274aa584aacSho t->vtbl->fd_isset(t, fds)) {
275cd6bf844Sho /* Remove fd bit. */
276aa584aacSho t->vtbl->fd_set(t, fds, 0);
277867e2181Sho
278867e2181Sho /* Prefer a message from the prioritized sendq. */
279cd6bf844Sho if (TAILQ_FIRST(&t->virtual->prio_sendq)) {
280cd6bf844Sho msg = TAILQ_FIRST(&t->virtual->prio_sendq);
281cd6bf844Sho TAILQ_REMOVE(&t->virtual->prio_sendq, msg,
282cd6bf844Sho link);
283fb9475d6Sderaadt } else {
284cd6bf844Sho msg = TAILQ_FIRST(&t->virtual->sendq);
285cd6bf844Sho TAILQ_REMOVE(&t->virtual->sendq, msg, link);
286867e2181Sho }
287867e2181Sho
288893467d6Sniklas msg->flags &= ~MSG_IN_TRANSIT;
289a6341f1eSniklas exchange = msg->exchange;
290a6341f1eSniklas exchange->in_transit = 0;
2912040585eSniklas
2922040585eSniklas /*
29350eea14cSho * We disregard the potential error message here,
29450eea14cSho * hoping that the retransmit will go better.
2952040585eSniklas * XXX Consider a retry/fatal error discriminator.
2962040585eSniklas */
297cd6bf844Sho t->virtual->vtbl->send_message(msg, 0);
2982040585eSniklas msg->xmits++;
2992040585eSniklas
300a6341f1eSniklas /*
30150eea14cSho * This piece of code has been proven to be quite
302cd6bf844Sho * delicate. Think twice for before altering.
303cd6bf844Sho * Here's an outline:
304cd6bf844Sho *
305cd6bf844Sho * If this message is not the one which finishes an
306cd6bf844Sho * exchange, check if we have reached the number of
307cd6bf844Sho * retransmit before queuing it up for another.
308a6341f1eSniklas *
30950eea14cSho * If it is a finishing message we still may have to
31050eea14cSho * keep it around for an on-demand retransmit when
31150eea14cSho * seeing a duplicate of our peer's previous message.
312a6341f1eSniklas */
313fb9475d6Sderaadt if ((msg->flags & MSG_LAST) == 0) {
31420c653ebSyasuoka if (msg->flags & MSG_DONTRETRANSMIT)
31520c653ebSyasuoka exchange->last_sent = 0;
31620c653ebSyasuoka else if (msg->xmits > conf_get_num("General",
31750eea14cSho "retransmits", RETRANSMIT_DEFAULT)) {
318349b5519Shshoexer t->virtual->vtbl->get_dst(t->virtual, &dst);
319c506f982Shshoexer if (getnameinfo(dst, SA_LEN(dst), peer,
320c506f982Shshoexer sizeof peer, peersv, sizeof peersv,
321349b5519Shshoexer NI_NUMERICHOST | NI_NUMERICSERV)) {
322349b5519Shshoexer strlcpy(peer, "<unknown>", sizeof peer);
323349b5519Shshoexer strlcpy(peersv, "<?>", sizeof peersv);
324fdf8d522Sho }
325349b5519Shshoexer log_print("transport_send_messages: "
326349b5519Shshoexer "giving up on exchange %s, no "
327349b5519Shshoexer "response from peer %s:%s",
328349b5519Shshoexer exchange->name ? exchange->name :
329349b5519Shshoexer "<unnamed>", peer, peersv);
330349b5519Shshoexer
331a6341f1eSniklas exchange->last_sent = 0;
332dec6ea27Sho #ifdef notyet
333dec6ea27Sho exchange_free(exchange);
334dec6ea27Sho exchange = 0;
335dec6ea27Sho #endif
336fb9475d6Sderaadt } else {
3376ee513e5Sjca clock_gettime(CLOCK_MONOTONIC,
3386ee513e5Sjca &expiration);
3392040585eSniklas
3402040585eSniklas /*
34150eea14cSho * XXX Calculate from round trip
34250eea14cSho * timings and a backoff func.
3432040585eSniklas */
3442040585eSniklas expiry = msg->xmits * 2 + 5;
3452040585eSniklas expiration.tv_sec += expiry;
34651ca15aeSniklas LOG_DBG((LOG_TRANSPORT, 30,
34750eea14cSho "transport_send_messages: "
34850eea14cSho "message %p scheduled for "
34950eea14cSho "retransmission %d in %d secs",
35051ca15aeSniklas msg, msg->xmits, expiry));
3517638e87cSniklas if (msg->retrans)
3527638e87cSniklas timer_remove_event(msg->retrans);
353a6341f1eSniklas msg->retrans
354bcfff259Sniklas = timer_add_event("message_send_expire",
355bcfff259Sniklas (void (*) (void *)) message_send_expire,
3562040585eSniklas msg, &expiration);
357fb9475d6Sderaadt /*
358fb9475d6Sderaadt * If we cannot retransmit, we
359fb9475d6Sderaadt * cannot...
360fb9475d6Sderaadt */
36150eea14cSho exchange->last_sent =
36250eea14cSho msg->retrans ? msg : 0;
363a6341f1eSniklas }
364fb9475d6Sderaadt } else
36550eea14cSho exchange->last_sent =
36650eea14cSho exchange->last_received ? msg : 0;
367a6341f1eSniklas
368a6341f1eSniklas /*
36950eea14cSho * If this message is not referred to for later
37050eea14cSho * retransmission it will be ok for us to drop it
37150eea14cSho * after the post-send function. But as the post-send
37250eea14cSho * function may remove the exchange, we need to
37350eea14cSho * remember this fact here.
374a6341f1eSniklas */
375a6341f1eSniklas ok_to_drop_message = exchange->last_sent == 0;
376a6341f1eSniklas
377a6341f1eSniklas /*
37850eea14cSho * If this is not a retransmit call post-send
37950eea14cSho * functions that allows parallel work to be done
380cd6bf844Sho * while the network and peer does their share of
381cd6bf844Sho * the job. Note that a post-send function may take
382cd6bf844Sho * away the exchange we belong to, but only if no
38350eea14cSho * retransmits are possible.
384a6341f1eSniklas */
385a6341f1eSniklas if (msg->xmits == 1)
386a6341f1eSniklas message_post_send(msg);
387a6341f1eSniklas
388a6341f1eSniklas if (ok_to_drop_message)
3892040585eSniklas message_free(msg);
3902040585eSniklas }
391567bb3dbSniklas }
392567bb3dbSniklas
393fb9475d6Sderaadt for (t = LIST_FIRST(&transport_list); t; t = next) {
394a6341f1eSniklas next = LIST_NEXT(t, link);
395cd6bf844Sho transport_release(t->virtual);
3962040585eSniklas }
3972040585eSniklas }
3982040585eSniklas
3992040585eSniklas /*
4002040585eSniklas * Textual search after the transport method denoted by NAME, then create
4012040585eSniklas * a transport connected to the peer with address ADDR, given in a transport-
4022040585eSniklas * specific string format.
4032040585eSniklas */
4042040585eSniklas struct transport *
transport_create(char * name,char * addr)4052040585eSniklas transport_create(char *name, char *addr)
4062040585eSniklas {
4072040585eSniklas struct transport_vtbl *method;
4082040585eSniklas
4092040585eSniklas for (method = LIST_FIRST(&transport_method_list); method;
4102040585eSniklas method = LIST_NEXT(method, link))
4112040585eSniklas if (strcmp(method->name, name) == 0)
4122040585eSniklas return (*method->create) (addr);
4132040585eSniklas return 0;
4142040585eSniklas }
415