xref: /openbsd/sbin/isakmpd/transport.c (revision 9ddd0770)
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