xref: /openbsd-src/sbin/isakmpd/udp_encap.c (revision 0ebca8ea015d751661294b64a80c5f47baec051b)
1*0ebca8eaSnaddy /*	$OpenBSD: udp_encap.c,v 1.24 2022/01/16 14:30:11 naddy Exp $	*/
2cd6bf844Sho 
3cd6bf844Sho /*
4cd6bf844Sho  * Copyright (c) 1998, 1999, 2001 Niklas Hallqvist.  All rights reserved.
5cd6bf844Sho  * Copyright (c) 2000 Angelos D. Keromytis.  All rights reserved.
6cd6bf844Sho  * Copyright (c) 2004 H�kan Olsson.  All rights reserved.
7cd6bf844Sho  *
8cd6bf844Sho  * Redistribution and use in source and binary forms, with or without
9cd6bf844Sho  * modification, are permitted provided that the following conditions
10cd6bf844Sho  * are met:
11cd6bf844Sho  * 1. Redistributions of source code must retain the above copyright
12cd6bf844Sho  *    notice, this list of conditions and the following disclaimer.
13cd6bf844Sho  * 2. Redistributions in binary form must reproduce the above copyright
14cd6bf844Sho  *    notice, this list of conditions and the following disclaimer in the
15cd6bf844Sho  *    documentation and/or other materials provided with the distribution.
16cd6bf844Sho  *
17cd6bf844Sho  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18cd6bf844Sho  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19cd6bf844Sho  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20cd6bf844Sho  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21cd6bf844Sho  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22cd6bf844Sho  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23cd6bf844Sho  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24cd6bf844Sho  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25cd6bf844Sho  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26cd6bf844Sho  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27cd6bf844Sho  */
28cd6bf844Sho 
29cd6bf844Sho #include <sys/types.h>
30cd6bf844Sho #include <sys/ioctl.h>
31cd6bf844Sho #include <sys/socket.h>
32cd6bf844Sho #include <sys/sockio.h>
33cd6bf844Sho #include <net/if.h>
34cd6bf844Sho #include <netinet/in.h>
35cd6bf844Sho #include <arpa/inet.h>
36cd6bf844Sho #include <ctype.h>
37cd6bf844Sho #include <limits.h>
38cd6bf844Sho #include <netdb.h>
39cd6bf844Sho #include <stdlib.h>
40cd6bf844Sho #include <string.h>
41cd6bf844Sho #include <unistd.h>
42cd6bf844Sho 
43cd6bf844Sho #include "conf.h"
44cd6bf844Sho #include "if.h"
45cd6bf844Sho #include "ipsec_doi.h"
46cd6bf844Sho #include "isakmp.h"
47cd6bf844Sho #include "log.h"
48cd6bf844Sho #include "message.h"
49cd6bf844Sho #include "monitor.h"
50cd6bf844Sho #include "transport.h"
51cd6bf844Sho #include "udp.h"
52cd6bf844Sho #include "udp_encap.h"
53cd6bf844Sho #include "util.h"
54cd6bf844Sho #include "virtual.h"
55cd6bf844Sho 
56cd6bf844Sho #define UDP_SIZE 65536
57cd6bf844Sho 
58cd6bf844Sho /* If a system doesn't have SO_REUSEPORT, SO_REUSEADDR will have to do.  */
59cd6bf844Sho #ifndef SO_REUSEPORT
60cd6bf844Sho #define SO_REUSEPORT SO_REUSEADDR
61cd6bf844Sho #endif
62cd6bf844Sho 
63cd6bf844Sho /* Reused, from udp.c */
64cd6bf844Sho struct transport *udp_clone(struct transport *, struct sockaddr *);
65aa584aacSho int		  udp_fd_set(struct transport *, fd_set *, int);
66aa584aacSho int		  udp_fd_isset(struct transport *, fd_set *);
67cd6bf844Sho void		  udp_get_dst(struct transport *, struct sockaddr **);
68cd6bf844Sho void		  udp_get_src(struct transport *, struct sockaddr **);
69cd6bf844Sho char		 *udp_decode_ids(struct transport *);
70dec6ea27Sho void		  udp_remove(struct transport *);
71cd6bf844Sho 
72cd6bf844Sho static struct transport *udp_encap_create(char *);
73cd6bf844Sho static void		 udp_encap_report(struct transport *);
74cd6bf844Sho static void		 udp_encap_handle_message(struct transport *);
75cd6bf844Sho static struct transport *udp_encap_make(struct sockaddr *);
76cd6bf844Sho static int		 udp_encap_send_message(struct message *,
77cd6bf844Sho     struct transport *);
78cd6bf844Sho 
79cd6bf844Sho static struct transport_vtbl udp_encap_transport_vtbl = {
80cd6bf844Sho 	{ 0 }, "udp_encap",
81cd6bf844Sho 	udp_encap_create,
82cd6bf844Sho 	0,
83dec6ea27Sho 	udp_remove,
84cd6bf844Sho 	udp_encap_report,
85aa584aacSho 	udp_fd_set,
86aa584aacSho 	udp_fd_isset,
87cd6bf844Sho 	udp_encap_handle_message,
88cd6bf844Sho 	udp_encap_send_message,
89cd6bf844Sho 	udp_get_dst,
90cd6bf844Sho 	udp_get_src,
91cd6bf844Sho 	udp_decode_ids,
92cd6bf844Sho 	udp_clone,
93cd6bf844Sho 	0
94cd6bf844Sho };
95cd6bf844Sho 
96cd6bf844Sho char	 *udp_encap_default_port = 0;
97cd6bf844Sho 
98cd6bf844Sho void
udp_encap_init(void)99cd6bf844Sho udp_encap_init(void)
100cd6bf844Sho {
101cd6bf844Sho 	transport_method_add(&udp_encap_transport_vtbl);
102cd6bf844Sho }
103cd6bf844Sho 
104cd6bf844Sho /* Create a UDP transport structure bound to LADDR just for listening.  */
105cd6bf844Sho static struct transport *
udp_encap_make(struct sockaddr * laddr)106cd6bf844Sho udp_encap_make(struct sockaddr *laddr)
107cd6bf844Sho {
108cd6bf844Sho 	struct udp_transport *t = 0;
109cd6bf844Sho 	int	 s, on, wildcardaddress = 0;
110cd6bf844Sho 	char	*tstr;
111cd6bf844Sho 
112cd6bf844Sho 	t = calloc(1, sizeof *t);
113cd6bf844Sho 	if (!t) {
114cd6bf844Sho 		log_print("udp_encap_make: malloc(%lu) failed",
115cd6bf844Sho 		    (unsigned long)sizeof *t);
116df8cdaffShshoexer 		free(laddr);
117cd6bf844Sho 		return 0;
118cd6bf844Sho 	}
119df8cdaffShshoexer 	t->src = laddr;
120cd6bf844Sho 
121cd6bf844Sho 	s = socket(laddr->sa_family, SOCK_DGRAM, IPPROTO_UDP);
122cd6bf844Sho 	if (s == -1) {
123cd6bf844Sho 		log_error("udp_encap_make: socket (%d, %d, %d)",
124cd6bf844Sho 		    laddr->sa_family, SOCK_DGRAM, IPPROTO_UDP);
125cd6bf844Sho 		goto err;
126cd6bf844Sho 	}
127cd6bf844Sho 
128cd6bf844Sho 	/* Make sure we don't get our traffic encrypted.  */
129cd6bf844Sho 	if (sysdep_cleartext(s, laddr->sa_family) == -1)
130cd6bf844Sho 		goto err;
131cd6bf844Sho 
132cd6bf844Sho 	/* Wildcard address ?  */
133cd6bf844Sho 	switch (laddr->sa_family) {
134cd6bf844Sho 	case AF_INET:
135cd6bf844Sho 		if (((struct sockaddr_in *)laddr)->sin_addr.s_addr
136cd6bf844Sho 		    == INADDR_ANY)
137cd6bf844Sho 			wildcardaddress = 1;
138cd6bf844Sho 		break;
139cd6bf844Sho 	case AF_INET6:
140cd6bf844Sho 		if (IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6 *)laddr)->sin6_addr))
141cd6bf844Sho 			wildcardaddress = 1;
142cd6bf844Sho 		break;
143cd6bf844Sho 	}
144cd6bf844Sho 
145cd6bf844Sho 	/*
146cd6bf844Sho 	 * In order to have several bound specific address-port combinations
147cd6bf844Sho 	 * with the same port SO_REUSEADDR is needed.
148cd6bf844Sho 	 * If this is a wildcard socket and we are not listening there, but
149cd6bf844Sho 	 * only sending from it make sure it is entirely reuseable with
150cd6bf844Sho 	 * SO_REUSEPORT.
151cd6bf844Sho 	 */
152cd6bf844Sho 	on = 1;
153cd6bf844Sho 	if (setsockopt(s, SOL_SOCKET,
154cd6bf844Sho 	    wildcardaddress ? SO_REUSEPORT : SO_REUSEADDR,
155cd6bf844Sho 	    (void *)&on, sizeof on) == -1) {
156cd6bf844Sho 		log_error("udp_encap_make: setsockopt (%d, %d, %d, %p, %lu)",
157cd6bf844Sho 		    s, SOL_SOCKET,
158cd6bf844Sho 		    wildcardaddress ? SO_REUSEPORT : SO_REUSEADDR, &on,
159cd6bf844Sho 		    (unsigned long)sizeof on);
160cd6bf844Sho 		goto err;
161cd6bf844Sho 	}
162cd6bf844Sho 
163cd6bf844Sho 	t->transport.vtbl = &udp_encap_transport_vtbl;
164c506f982Shshoexer 	if (monitor_bind(s, t->src, SA_LEN(t->src))) {
165cd6bf844Sho 		if (sockaddr2text(t->src, &tstr, 0))
166cd6bf844Sho 			log_error("udp_encap_make: bind (%d, %p, %lu)", s,
167cd6bf844Sho 			    &t->src, (unsigned long)sizeof t->src);
168cd6bf844Sho 		else {
169cd6bf844Sho 			log_error("udp_encap_make: bind (%d, %s, %lu)", s,
170cd6bf844Sho 			    tstr, (unsigned long)sizeof t->src);
171cd6bf844Sho 			free(tstr);
172cd6bf844Sho 		}
173cd6bf844Sho 		goto err;
174cd6bf844Sho 	}
175cd6bf844Sho 
176cd6bf844Sho 	t->s = s;
177cd6bf844Sho 	if (sockaddr2text(t->src, &tstr, 0))
178cd6bf844Sho 		LOG_DBG((LOG_MISC, 20, "udp_encap_make: "
179cd6bf844Sho 		    "transport %p socket %d family %d", t, s,
180cd6bf844Sho 		    t->src->sa_family == AF_INET ? 4 : 6));
181cd6bf844Sho 	else {
182cd6bf844Sho 		LOG_DBG((LOG_MISC, 20, "udp_encap_make: "
183cd6bf844Sho 		    "transport %p socket %d ip %s port %d", t, s,
184cd6bf844Sho 		    tstr, ntohs(sockaddr_port(t->src))));
185cd6bf844Sho 		free(tstr);
186cd6bf844Sho 	}
187cd6bf844Sho 	transport_setup(&t->transport, 0);
188cd6bf844Sho 	t->transport.flags |= TRANSPORT_LISTEN;
189cd6bf844Sho 	return &t->transport;
190cd6bf844Sho 
191cd6bf844Sho err:
192cd6bf844Sho 	if (s >= 0)
193cd6bf844Sho 		close (s);
194cd6bf844Sho 	if (t) {
195cd6bf844Sho 		/* Already closed.  */
196cd6bf844Sho 		t->s = -1;
197dec6ea27Sho 		udp_remove(&t->transport);
198cd6bf844Sho 	}
199cd6bf844Sho 	return 0;
200cd6bf844Sho }
201cd6bf844Sho 
202cd6bf844Sho /*
203cd6bf844Sho  * Initialize an object of the UDP transport class.  Fill in the local
204cd6bf844Sho  * IP address and port information and create a server socket bound to
205cd6bf844Sho  * that specific port.  Add the polymorphic transport structure to the
206cd6bf844Sho  * system-wide pools of known ISAKMP transports.
207cd6bf844Sho  */
208cd6bf844Sho struct transport *
udp_encap_bind(const struct sockaddr * addr)209cd6bf844Sho udp_encap_bind(const struct sockaddr *addr)
210cd6bf844Sho {
211df8cdaffShshoexer 	struct sockaddr	*src;
212cd6bf844Sho 
213c506f982Shshoexer 	src = malloc(SA_LEN(addr));
214cd6bf844Sho 	if (!src)
215cd6bf844Sho 		return 0;
216cd6bf844Sho 
217c506f982Shshoexer 	memcpy(src, addr, SA_LEN(addr));
218cd6bf844Sho 	return udp_encap_make(src);
219cd6bf844Sho }
220cd6bf844Sho 
221cd6bf844Sho /*
222cd6bf844Sho  * NAME is a section name found in the config database.  Setup and return
223cd6bf844Sho  * a transport useable to talk to the peer specified by that name.
224cd6bf844Sho  */
225cd6bf844Sho static struct transport *
udp_encap_create(char * name)226cd6bf844Sho udp_encap_create(char *name)
227cd6bf844Sho {
228cd6bf844Sho 	struct virtual_transport *v;
229cd6bf844Sho 	struct udp_transport	*u;
230*0ebca8eaSnaddy 	struct transport	*rv;
231cd6bf844Sho 	struct sockaddr		*dst, *addr;
232cd6bf844Sho 	struct conf_list	*addr_list = 0;
233cd6bf844Sho 	struct conf_list_node	*addr_node;
234cd6bf844Sho 	char	*addr_str, *port_str;
235cd6bf844Sho 
236cd6bf844Sho 	port_str = conf_get_str(name, "Port"); /* XXX "Encap-port" ? */
237cd6bf844Sho 	if (!port_str)
238cd6bf844Sho 		port_str = udp_encap_default_port;
239cd6bf844Sho 	if (!port_str)
240cd6bf844Sho 		port_str = UDP_ENCAP_DEFAULT_PORT_STR;
241cd6bf844Sho 
242cd6bf844Sho 	addr_str = conf_get_str(name, "Address");
243cd6bf844Sho 	if (!addr_str) {
244cd6bf844Sho 		log_print("udp_encap_create: no address configured "
245cd6bf844Sho 		    "for \"%s\"", name);
246cd6bf844Sho 		return 0;
247cd6bf844Sho 	}
248e3283cbfSmcbride 	if (text2sockaddr(addr_str, port_str, &dst, 0, 0)) {
249cd6bf844Sho 		log_print("udp_encap_create: address \"%s\" not understood",
250cd6bf844Sho 		    addr_str);
251cd6bf844Sho 		return 0;
252cd6bf844Sho 	}
253cd6bf844Sho 
254cd6bf844Sho 	addr_str = conf_get_str(name, "Local-address");
255cd6bf844Sho 	if (!addr_str)
256cd6bf844Sho 		addr_list = conf_get_list("General", "Listen-on");
257cd6bf844Sho 	if (!addr_str && !addr_list) {
258cd6bf844Sho 		v = virtual_get_default(dst->sa_family);
259cd6bf844Sho 		u = (struct udp_transport *)v->encap;
260cd6bf844Sho 
261cd6bf844Sho 		if (!u) {
262cd6bf844Sho 			log_print("udp_encap_create: no default transport");
263cd6bf844Sho 			rv = 0;
264cd6bf844Sho 			goto ret;
265cd6bf844Sho 		} else {
266cd6bf844Sho 			rv = udp_clone((struct transport *)u, dst);
267cd6bf844Sho 			if (rv)
268cd6bf844Sho 				rv->vtbl = &udp_encap_transport_vtbl;
269cd6bf844Sho 			goto ret;
270cd6bf844Sho 		}
271cd6bf844Sho 	}
272cd6bf844Sho 
273cd6bf844Sho 	if (addr_list) {
274cd6bf844Sho 		for (addr_node = TAILQ_FIRST(&addr_list->fields);
275cd6bf844Sho 		    addr_node; addr_node = TAILQ_NEXT(addr_node, link))
276cd6bf844Sho 			if (text2sockaddr(addr_node->field, port_str,
277e3283cbfSmcbride 			    &addr, 0, 0) == 0) {
278cd6bf844Sho 				v = virtual_listen_lookup(addr);
279cd6bf844Sho 				free(addr);
280cd6bf844Sho 				if (v) {
281cd6bf844Sho 					addr_str = addr_node->field;
282cd6bf844Sho 					break;
283cd6bf844Sho 				}
284cd6bf844Sho 			}
285cd6bf844Sho 		if (!addr_str) {
286cd6bf844Sho 			log_print("udp_encap_create: "
287cd6bf844Sho 			    "no matching listener found");
288cd6bf844Sho 			rv = 0;
289cd6bf844Sho 			goto ret;
290cd6bf844Sho 		}
291cd6bf844Sho 	}
292e3283cbfSmcbride 	if (text2sockaddr(addr_str, port_str, &addr, 0, 0)) {
293cd6bf844Sho 		log_print("udp_encap_create: "
294cd6bf844Sho 		    "address \"%s\" not understood", addr_str);
295cd6bf844Sho 		rv = 0;
296cd6bf844Sho 		goto ret;
297cd6bf844Sho 	}
298cd6bf844Sho 	v = virtual_listen_lookup(addr);
299cd6bf844Sho 	free(addr);
300cd6bf844Sho 	if (!v) {
301cd6bf844Sho 		log_print("udp_encap_create: "
302cd6bf844Sho 		    "%s:%s must exist as a listener too", addr_str, port_str);
303cd6bf844Sho 		rv = 0;
304cd6bf844Sho 		goto ret;
305cd6bf844Sho 	}
306cd6bf844Sho 	rv = udp_clone(v->encap, dst);
307dec6ea27Sho 	if (rv)
308dec6ea27Sho 		rv->vtbl = &udp_encap_transport_vtbl;
309cd6bf844Sho 
310cd6bf844Sho ret:
311cd6bf844Sho 	if (addr_list)
312cd6bf844Sho 		conf_free_list(addr_list);
313cd6bf844Sho 	free(dst);
314cd6bf844Sho 	return rv;
315cd6bf844Sho }
316cd6bf844Sho 
317cd6bf844Sho /* Report transport-method specifics of the T transport.  */
318cd6bf844Sho void
udp_encap_report(struct transport * t)319cd6bf844Sho udp_encap_report(struct transport *t)
320cd6bf844Sho {
321cd6bf844Sho 	struct udp_transport *u = (struct udp_transport *)t;
322df8cdaffShshoexer 	char	 *src = NULL, *dst = NULL;
323cd6bf844Sho 	in_port_t sport, dport;
324cd6bf844Sho 
325cd6bf844Sho 	if (sockaddr2text(u->src, &src, 0))
3263fb44e3bShshoexer 		return;
327cd6bf844Sho 	sport = sockaddr_port(u->src);
328cd6bf844Sho 
329cd6bf844Sho 	if (!u->dst || sockaddr2text(u->dst, &dst, 0))
330cd6bf844Sho 		dst = 0;
331cd6bf844Sho 	dport = dst ? sockaddr_port(u->dst) : 0;
332cd6bf844Sho 
333cd6bf844Sho 	LOG_DBG ((LOG_REPORT, 0, "udp_encap_report: fd %d src %s:%u dst %s:%u",
334cd6bf844Sho 	    u->s, src, ntohs(sport), dst ? dst : "*", ntohs(dport)));
335cd6bf844Sho 
336cd6bf844Sho 	free(dst);
337cd6bf844Sho 	free(src);
338cd6bf844Sho }
339cd6bf844Sho 
340cd6bf844Sho /*
341cd6bf844Sho  * A message has arrived on transport T's socket.  If T is single-ended,
342cd6bf844Sho  * clone it into a double-ended transport which we will use from now on.
343cd6bf844Sho  * Package the message as we want it and continue processing in the message
344cd6bf844Sho  * module.
345cd6bf844Sho  */
346cd6bf844Sho static void
udp_encap_handle_message(struct transport * t)347cd6bf844Sho udp_encap_handle_message(struct transport *t)
348cd6bf844Sho {
349cd6bf844Sho 	struct udp_transport	*u = (struct udp_transport *)t;
350cd6bf844Sho 	struct sockaddr_storage	 from;
351cd6bf844Sho 	struct message		*msg;
352cd6bf844Sho 	u_int32_t	len = sizeof from;
353cd6bf844Sho 	ssize_t		n;
354cd6bf844Sho 	u_int8_t	buf[UDP_SIZE];
355cd6bf844Sho 
356cd6bf844Sho 	n = recvfrom(u->s, buf, UDP_SIZE, 0, (struct sockaddr *)&from, &len);
357cd6bf844Sho 	if (n == -1) {
358cd6bf844Sho 		log_error("recvfrom (%d, %p, %d, %d, %p, %p)", u->s, buf,
359cd6bf844Sho 		    UDP_SIZE, 0, &from, &len);
360cd6bf844Sho 		return;
361cd6bf844Sho 	}
362cd6bf844Sho 
363440df6e7Smarkus 	if (t->virtual == (struct transport *)virtual_get_default(AF_INET) ||
364440df6e7Smarkus 	    t->virtual == (struct transport *)virtual_get_default(AF_INET6)) {
365440df6e7Smarkus 		t->virtual->vtbl->reinit();
366440df6e7Smarkus 
367440df6e7Smarkus 		/*
368440df6e7Smarkus 		 * As we don't know the actual destination address of the
369440df6e7Smarkus 		 * packet, we can't really deal with it. So, just ignore it
370440df6e7Smarkus 		 * and hope we catch the retransmission.
371440df6e7Smarkus 		 */
372440df6e7Smarkus 		return;
373440df6e7Smarkus 	}
374440df6e7Smarkus 
375cd6bf844Sho 	/*
376cd6bf844Sho 	 * Make a specialized UDP transport structure out of the incoming
377cd6bf844Sho 	 * transport and the address information we got from recvfrom(2).
378cd6bf844Sho 	 */
379cd6bf844Sho 	t = t->virtual->vtbl->clone(t->virtual, (struct sockaddr *)&from);
380cd6bf844Sho 	if (!t)
381cd6bf844Sho 		return;
382cd6bf844Sho 
383cd6bf844Sho 	/* Check NULL-ESP marker.  */
38478fae5f3Shshoexer 	if (n < (ssize_t)sizeof(u_int32_t) || *(u_int32_t *)buf != 0) {
385cd6bf844Sho 		/* Should never happen.  */
386cd6bf844Sho 		log_print("udp_encap_handle_message: "
387cd6bf844Sho 		    "Null-ESP marker not NULL or short message");
388cd6bf844Sho 		return;
389cd6bf844Sho 	}
390cd6bf844Sho 
391780409beSho 	/* NAT-Keepalive messages should not be processed further.  */
392780409beSho 	n -= sizeof(u_int32_t);
393780409beSho 	if (n == 1 && buf[sizeof(u_int32_t)] == 0xFF)
394780409beSho 		return;
395780409beSho 
396780409beSho 	msg = message_alloc(t, buf + sizeof (u_int32_t), n);
397cd6bf844Sho 	if (!msg) {
398cd6bf844Sho 		log_error("failed to allocate message structure, dropping "
399cd6bf844Sho 		    "packet received on transport %p", u);
400cd6bf844Sho 		return;
401cd6bf844Sho 	}
4022fe09b3dShshoexer 
4032fe09b3dShshoexer 	msg->flags |= MSG_NATT;
4042fe09b3dShshoexer 
405cd6bf844Sho 	message_recv(msg);
406cd6bf844Sho }
407cd6bf844Sho 
408adfd2491Sho /*
409adfd2491Sho  * Physically send the message MSG over its associated transport.
410adfd2491Sho  * Special: if 'msg' is NULL, send a NAT-T keepalive message.
411adfd2491Sho  */
412cd6bf844Sho static int
udp_encap_send_message(struct message * msg,struct transport * t)413cd6bf844Sho udp_encap_send_message(struct message *msg, struct transport *t)
414cd6bf844Sho {
415cd6bf844Sho 	struct udp_transport *u = (struct udp_transport *)t;
416cd6bf844Sho 	struct msghdr	 m;
417adfd2491Sho 	struct iovec	*new_iov = 0, keepalive;
418cd6bf844Sho 	ssize_t		 n;
419cd6bf844Sho 	u_int32_t	 marker = 0;			/* NULL-ESP Marker */
420cd6bf844Sho 
421adfd2491Sho 	if (msg) {
422cd6bf844Sho 		/* Construct new iov array, prefixing NULL-ESP Marker.  */
4235ae94ef8Sderaadt 		new_iov = calloc(msg->iovlen + 1, sizeof *new_iov);
424cd6bf844Sho 		if (!new_iov) {
425adfd2491Sho 			log_error ("udp_encap_send_message: "
426adfd2491Sho 			    "calloc(%lu, %lu) failed",
427cd6bf844Sho 			    (unsigned long)msg->iovlen + 1,
428cd6bf844Sho 			    (unsigned long)sizeof *new_iov);
429cd6bf844Sho 			return -1;
430cd6bf844Sho 		}
431cd6bf844Sho 		new_iov[0].iov_base = &marker;
432cd6bf844Sho 		new_iov[0].iov_len = IPSEC_SPI_SIZE;
433aa584aacSho 		memcpy (new_iov + 1, msg->iov, msg->iovlen * sizeof *new_iov);
434adfd2491Sho 	} else {
435adfd2491Sho 		marker = ~marker;
436adfd2491Sho 		keepalive.iov_base = &marker;
437adfd2491Sho 		keepalive.iov_len = 1;
438adfd2491Sho 	}
439cd6bf844Sho 
440cd6bf844Sho 	/*
441cd6bf844Sho 	 * Sending on connected sockets requires that no destination address is
442cd6bf844Sho 	 * given, or else EISCONN will occur.
443cd6bf844Sho 	 */
444cd6bf844Sho 	m.msg_name = (caddr_t)u->dst;
445c506f982Shshoexer 	m.msg_namelen = SA_LEN(u->dst);
446adfd2491Sho 	m.msg_iov = msg ? new_iov : &keepalive;
447adfd2491Sho 	m.msg_iovlen = msg ? msg->iovlen + 1 : 1;
448cd6bf844Sho 	m.msg_control = 0;
449cd6bf844Sho 	m.msg_controllen = 0;
450cd6bf844Sho 	m.msg_flags = 0;
451cd6bf844Sho 	n = sendmsg (u->s, &m, 0);
452adfd2491Sho 	if (msg)
453adfd2491Sho 		free (new_iov);
454cd6bf844Sho 	if (n == -1) {
455cd6bf844Sho 		/* XXX We should check whether the address has gone away */
456cd6bf844Sho 		log_error ("sendmsg (%d, %p, %d)", u->s, &m, 0);
457cd6bf844Sho 		return -1;
458cd6bf844Sho 	}
459cd6bf844Sho 	return 0;
460cd6bf844Sho }
461