1 /* $OpenBSD: transport.c,v 1.39 2021/01/28 01:18:44 mortimer Exp $ */
2 /* $EOM: transport.c,v 1.43 2000/10/10 12:36:39 provos Exp $ */
3
4 /*
5 * Copyright (c) 1998, 1999 Niklas Hallqvist. All rights reserved.
6 * Copyright (c) 2001, 2004 H�kan Olsson. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 /*
30 * This code was written under funding by Ericsson Radio Systems.
31 */
32
33 #include <sys/queue.h>
34 #include <netdb.h>
35 #include <string.h>
36
37 #include "conf.h"
38 #include "exchange.h"
39 #include "log.h"
40 #include "message.h"
41 #include "sa.h"
42 #include "timer.h"
43 #include "transport.h"
44 #include "virtual.h"
45
46 /* If no retransmit limit is given, use this as a default. */
47 #define RETRANSMIT_DEFAULT 10
48
49 LIST_HEAD(transport_method_list, transport_vtbl) transport_method_list;
50
51 struct transport_list transport_list;
52
53 /* Call the reinit function of the various transports. */
54 void
transport_reinit(void)55 transport_reinit(void)
56 {
57 struct transport_vtbl *method;
58
59 for (method = LIST_FIRST(&transport_method_list); method;
60 method = LIST_NEXT(method, link))
61 if (method->reinit)
62 method->reinit();
63 }
64
65 /* Initialize the transport maintenance module. */
66 void
transport_init(void)67 transport_init(void)
68 {
69 LIST_INIT(&transport_list);
70 LIST_INIT(&transport_method_list);
71 }
72
73 /* Register another transport T. */
74 void
transport_setup(struct transport * t,int toplevel)75 transport_setup(struct transport *t, int toplevel)
76 {
77 if (toplevel) {
78 /* Only the toplevel (virtual) transport has sendqueues. */
79 LOG_DBG((LOG_TRANSPORT, 70,
80 "transport_setup: virtual transport %p", t));
81 TAILQ_INIT(&t->sendq);
82 TAILQ_INIT(&t->prio_sendq);
83 t->refcnt = 0;
84 } else {
85 /* udp and udp_encap trp goes into the transport list. */
86 LOG_DBG((LOG_TRANSPORT, 70,
87 "transport_setup: added %p to transport list", t));
88 LIST_INSERT_HEAD(&transport_list, t, link);
89 t->refcnt = 1;
90 }
91 t->flags = 0;
92 }
93
94 /* Add a referer to transport T. */
95 void
transport_reference(struct transport * t)96 transport_reference(struct transport *t)
97 {
98 t->refcnt++;
99 LOG_DBG((LOG_TRANSPORT, 95,
100 "transport_reference: transport %p now has %d references", t,
101 t->refcnt));
102 }
103
104 /*
105 * Remove a referer from transport T, removing all of T when no referers left.
106 */
107 void
transport_release(struct transport * t)108 transport_release(struct transport *t)
109 {
110 LOG_DBG((LOG_TRANSPORT, 95,
111 "transport_release: transport %p had %d references", t,
112 t->refcnt));
113 if (--t->refcnt)
114 return;
115
116 LOG_DBG((LOG_TRANSPORT, 70, "transport_release: freeing %p", t));
117 t->vtbl->remove(t);
118 }
119
120 void
transport_report(void)121 transport_report(void)
122 {
123 struct virtual_transport *v;
124 struct transport *t;
125 struct message *msg;
126
127 for (t = LIST_FIRST(&transport_list); t; t = LIST_NEXT(t, link)) {
128 LOG_DBG((LOG_REPORT, 0,
129 "transport_report: transport %p flags %x refcnt %d", t,
130 t->flags, t->refcnt));
131
132 /* XXX Report sth on the virtual transport? */
133 t->vtbl->report(t);
134
135 /*
136 * This is the reason message_dump_raw lives outside
137 * message.c.
138 */
139 v = (struct virtual_transport *)t->virtual;
140 if ((v->encap_is_active && v->encap == t) ||
141 (!v->encap_is_active && v->main == t)) {
142 for (msg = TAILQ_FIRST(&t->virtual->prio_sendq); msg;
143 msg = TAILQ_NEXT(msg, link))
144 message_dump_raw("udp_report(prio)", msg,
145 LOG_REPORT);
146
147 for (msg = TAILQ_FIRST(&t->virtual->sendq); msg;
148 msg = TAILQ_NEXT(msg, link))
149 message_dump_raw("udp_report", msg,
150 LOG_REPORT);
151 }
152 }
153 }
154
155 int
transport_prio_sendqs_empty(void)156 transport_prio_sendqs_empty(void)
157 {
158 struct transport *t;
159
160 for (t = LIST_FIRST(&transport_list); t; t = LIST_NEXT(t, link))
161 if (TAILQ_FIRST(&t->virtual->prio_sendq))
162 return 0;
163 return 1;
164 }
165
166 /* Register another transport method T. */
167 void
transport_method_add(struct transport_vtbl * t)168 transport_method_add(struct transport_vtbl *t)
169 {
170 LIST_INSERT_HEAD(&transport_method_list, t, link);
171 }
172
173 /*
174 * Build up a file descriptor set FDS with all transport descriptors we want
175 * to read from. Return the number of file descriptors select(2) needs to
176 * check in order to cover the ones we setup in here.
177 */
178 int
transport_fd_set(fd_set * fds)179 transport_fd_set(fd_set * fds)
180 {
181 struct transport *t;
182 int n;
183 int max = -1;
184
185 for (t = LIST_FIRST(&transport_list); t; t = LIST_NEXT(t, link))
186 if (t->virtual->flags & TRANSPORT_LISTEN) {
187 n = t->vtbl->fd_set(t, fds, 1);
188 if (n > max)
189 max = n;
190
191 LOG_DBG((LOG_TRANSPORT, 95, "transport_fd_set: "
192 "transport %p (virtual %p) fd %d", t,
193 t->virtual, n));
194 }
195 return max + 1;
196 }
197
198 /*
199 * Build up a file descriptor set FDS with all the descriptors belonging to
200 * transport where messages are queued for transmittal. Return the number
201 * of file descriptors select(2) needs to check in order to cover the ones
202 * we setup in here.
203 */
204 int
transport_pending_wfd_set(fd_set * fds)205 transport_pending_wfd_set(fd_set * fds)
206 {
207 struct transport *t;
208 int n;
209 int max = -1;
210
211 for (t = LIST_FIRST(&transport_list); t; t = LIST_NEXT(t, link)) {
212 if (TAILQ_FIRST(&t->virtual->sendq) ||
213 TAILQ_FIRST(&t->virtual->prio_sendq)) {
214 n = t->vtbl->fd_set(t, fds, 1);
215 LOG_DBG((LOG_TRANSPORT, 95,
216 "transport_pending_wfd_set: "
217 "transport %p (virtual %p) fd %d pending", t,
218 t->virtual, n));
219 if (n > max)
220 max = n;
221 }
222 }
223 return max + 1;
224 }
225
226 /*
227 * For each transport with a file descriptor in FDS, try to get an
228 * incoming message and start processing it.
229 */
230 void
transport_handle_messages(fd_set * fds)231 transport_handle_messages(fd_set *fds)
232 {
233 struct transport *t;
234
235 for (t = LIST_FIRST(&transport_list); t; t = LIST_NEXT(t, link)) {
236 if ((t->flags & TRANSPORT_LISTEN) &&
237 (*t->vtbl->fd_isset)(t, fds)) {
238 (*t->virtual->vtbl->handle_message)(t);
239 (*t->vtbl->fd_set)(t, fds, 0);
240 }
241 }
242 }
243
244 /*
245 * Send the first queued message on the transports found whose file
246 * descriptor is in FDS and has messages queued. Remove the fd bit from
247 * FDS as soon as one message has been sent on it so other transports
248 * sharing the socket won't get service without an intervening select
249 * call. Perhaps a fairness strategy should be implemented between
250 * such transports. Now early transports in the list will potentially
251 * be favoured to later ones sharing the file descriptor.
252 */
253 void
transport_send_messages(fd_set * fds)254 transport_send_messages(fd_set * fds)
255 {
256 struct transport *t, *next;
257 struct message *msg;
258 struct exchange *exchange;
259 struct sockaddr *dst;
260 struct timespec expiration;
261 int expiry, ok_to_drop_message;
262 char peer[NI_MAXHOST], peersv[NI_MAXSERV];
263
264 /*
265 * Reference all transports first so noone will disappear while in
266 * use.
267 */
268 for (t = LIST_FIRST(&transport_list); t; t = LIST_NEXT(t, link))
269 transport_reference(t->virtual);
270
271 for (t = LIST_FIRST(&transport_list); t; t = LIST_NEXT(t, link)) {
272 if ((TAILQ_FIRST(&t->virtual->sendq) ||
273 TAILQ_FIRST(&t->virtual->prio_sendq)) &&
274 t->vtbl->fd_isset(t, fds)) {
275 /* Remove fd bit. */
276 t->vtbl->fd_set(t, fds, 0);
277
278 /* Prefer a message from the prioritized sendq. */
279 if (TAILQ_FIRST(&t->virtual->prio_sendq)) {
280 msg = TAILQ_FIRST(&t->virtual->prio_sendq);
281 TAILQ_REMOVE(&t->virtual->prio_sendq, msg,
282 link);
283 } else {
284 msg = TAILQ_FIRST(&t->virtual->sendq);
285 TAILQ_REMOVE(&t->virtual->sendq, msg, link);
286 }
287
288 msg->flags &= ~MSG_IN_TRANSIT;
289 exchange = msg->exchange;
290 exchange->in_transit = 0;
291
292 /*
293 * We disregard the potential error message here,
294 * hoping that the retransmit will go better.
295 * XXX Consider a retry/fatal error discriminator.
296 */
297 t->virtual->vtbl->send_message(msg, 0);
298 msg->xmits++;
299
300 /*
301 * This piece of code has been proven to be quite
302 * delicate. Think twice for before altering.
303 * Here's an outline:
304 *
305 * If this message is not the one which finishes an
306 * exchange, check if we have reached the number of
307 * retransmit before queuing it up for another.
308 *
309 * If it is a finishing message we still may have to
310 * keep it around for an on-demand retransmit when
311 * seeing a duplicate of our peer's previous message.
312 */
313 if ((msg->flags & MSG_LAST) == 0) {
314 if (msg->flags & MSG_DONTRETRANSMIT)
315 exchange->last_sent = 0;
316 else if (msg->xmits > conf_get_num("General",
317 "retransmits", RETRANSMIT_DEFAULT)) {
318 t->virtual->vtbl->get_dst(t->virtual, &dst);
319 if (getnameinfo(dst, SA_LEN(dst), peer,
320 sizeof peer, peersv, sizeof peersv,
321 NI_NUMERICHOST | NI_NUMERICSERV)) {
322 strlcpy(peer, "<unknown>", sizeof peer);
323 strlcpy(peersv, "<?>", sizeof peersv);
324 }
325 log_print("transport_send_messages: "
326 "giving up on exchange %s, no "
327 "response from peer %s:%s",
328 exchange->name ? exchange->name :
329 "<unnamed>", peer, peersv);
330
331 exchange->last_sent = 0;
332 #ifdef notyet
333 exchange_free(exchange);
334 exchange = 0;
335 #endif
336 } else {
337 clock_gettime(CLOCK_MONOTONIC,
338 &expiration);
339
340 /*
341 * XXX Calculate from round trip
342 * timings and a backoff func.
343 */
344 expiry = msg->xmits * 2 + 5;
345 expiration.tv_sec += expiry;
346 LOG_DBG((LOG_TRANSPORT, 30,
347 "transport_send_messages: "
348 "message %p scheduled for "
349 "retransmission %d in %d secs",
350 msg, msg->xmits, expiry));
351 if (msg->retrans)
352 timer_remove_event(msg->retrans);
353 msg->retrans
354 = timer_add_event("message_send_expire",
355 (void (*) (void *)) message_send_expire,
356 msg, &expiration);
357 /*
358 * If we cannot retransmit, we
359 * cannot...
360 */
361 exchange->last_sent =
362 msg->retrans ? msg : 0;
363 }
364 } else
365 exchange->last_sent =
366 exchange->last_received ? msg : 0;
367
368 /*
369 * If this message is not referred to for later
370 * retransmission it will be ok for us to drop it
371 * after the post-send function. But as the post-send
372 * function may remove the exchange, we need to
373 * remember this fact here.
374 */
375 ok_to_drop_message = exchange->last_sent == 0;
376
377 /*
378 * If this is not a retransmit call post-send
379 * functions that allows parallel work to be done
380 * while the network and peer does their share of
381 * the job. Note that a post-send function may take
382 * away the exchange we belong to, but only if no
383 * retransmits are possible.
384 */
385 if (msg->xmits == 1)
386 message_post_send(msg);
387
388 if (ok_to_drop_message)
389 message_free(msg);
390 }
391 }
392
393 for (t = LIST_FIRST(&transport_list); t; t = next) {
394 next = LIST_NEXT(t, link);
395 transport_release(t->virtual);
396 }
397 }
398
399 /*
400 * Textual search after the transport method denoted by NAME, then create
401 * a transport connected to the peer with address ADDR, given in a transport-
402 * specific string format.
403 */
404 struct transport *
transport_create(char * name,char * addr)405 transport_create(char *name, char *addr)
406 {
407 struct transport_vtbl *method;
408
409 for (method = LIST_FIRST(&transport_method_list); method;
410 method = LIST_NEXT(method, link))
411 if (strcmp(method->name, name) == 0)
412 return (*method->create) (addr);
413 return 0;
414 }
415