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 = ▮
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 = ▮
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