337 lines
8.9 KiB
C
337 lines
8.9 KiB
C
/*
|
|
* Copyright(c) 2006 to 2018 ADLINK Technology Limited and others
|
|
*
|
|
* This program and the accompanying materials are made available under the
|
|
* terms of the Eclipse Public License v. 2.0 which is available at
|
|
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
|
|
* v. 1.0 which is available at
|
|
* http://www.eclipse.org/org/documents/edl-v10.php.
|
|
*
|
|
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
|
|
*/
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
#include "os/os.h"
|
|
#include "ddsi/ddsi_tran.h"
|
|
#include "ddsi/ddsi_udp.h"
|
|
#include "ddsi/q_nwif.h"
|
|
#include "ddsi/q_config.h"
|
|
#include "ddsi/q_log.h"
|
|
#include "ddsi/q_pcap.h"
|
|
|
|
extern void ddsi_factory_conn_init (ddsi_tran_factory_t factory, ddsi_tran_conn_t conn);
|
|
|
|
typedef struct ddsi_tran_factory * ddsi_udp_factory_t;
|
|
|
|
typedef struct ddsi_udp_config
|
|
{
|
|
struct nn_group_membership *mship;
|
|
}
|
|
* ddsi_udp_config_t;
|
|
|
|
typedef struct ddsi_udp_conn
|
|
{
|
|
struct ddsi_tran_conn m_base;
|
|
os_socket m_sock;
|
|
#if defined _WIN32 && !defined WINCE
|
|
WSAEVENT m_sockEvent;
|
|
#endif
|
|
int m_diffserv;
|
|
}
|
|
* ddsi_udp_conn_t;
|
|
|
|
static struct ddsi_udp_config ddsi_udp_config_g;
|
|
static struct ddsi_tran_factory ddsi_udp_factory_g;
|
|
static os_atomic_uint32_t ddsi_udp_init_g = OS_ATOMIC_UINT32_INIT(0);
|
|
|
|
static ssize_t ddsi_udp_conn_read (ddsi_tran_conn_t conn, unsigned char * buf, size_t len)
|
|
{
|
|
int err;
|
|
ssize_t ret;
|
|
struct msghdr msghdr;
|
|
os_sockaddr_storage src;
|
|
struct iovec msg_iov;
|
|
socklen_t srclen = (socklen_t) sizeof (src);
|
|
|
|
msg_iov.iov_base = (void*) buf;
|
|
msg_iov.iov_len = len;
|
|
|
|
memset (&msghdr, 0, sizeof (msghdr));
|
|
|
|
msghdr.msg_name = &src;
|
|
msghdr.msg_namelen = srclen;
|
|
msghdr.msg_iov = &msg_iov;
|
|
msghdr.msg_iovlen = 1;
|
|
|
|
do {
|
|
ret = recvmsg(((ddsi_udp_conn_t) conn)->m_sock, &msghdr, 0);
|
|
err = (ret == -1) ? os_getErrno() : 0;
|
|
} while (err == os_sockEINTR);
|
|
|
|
if (ret > 0)
|
|
{
|
|
/* Check for udp packet truncation */
|
|
if ((((size_t) ret) > len)
|
|
#if SYSDEPS_MSGHDR_FLAGS
|
|
|| (msghdr.msg_flags & MSG_TRUNC)
|
|
#endif
|
|
)
|
|
{
|
|
char addrbuf[INET6_ADDRSTRLEN_EXTENDED];
|
|
sockaddr_to_string_with_port (addrbuf, &src);
|
|
NN_WARNING ("%s => %d truncated to %d\n", addrbuf, (int)ret, (int)len);
|
|
}
|
|
}
|
|
else if (err != os_sockENOTSOCK && err != os_sockECONNRESET)
|
|
{
|
|
NN_ERROR ("UDP recvmsg sock %d: ret %d errno %d\n", (int) ((ddsi_udp_conn_t) conn)->m_sock, (int) ret, err);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static ssize_t ddsi_udp_conn_write (ddsi_tran_conn_t conn, const struct msghdr * msg, size_t len, uint32_t flags)
|
|
{
|
|
int err;
|
|
ssize_t ret;
|
|
unsigned retry = 2;
|
|
int sendflags = 0;
|
|
(void) flags;
|
|
(void) len;
|
|
#ifdef MSG_NOSIGNAL
|
|
sendflags |= MSG_NOSIGNAL;
|
|
#endif
|
|
do {
|
|
ddsi_udp_conn_t uc = (ddsi_udp_conn_t) conn;
|
|
ret = sendmsg (uc->m_sock, msg, sendflags);
|
|
err = (ret == -1) ? os_getErrno() : 0;
|
|
#if defined _WIN32 && !defined WINCE
|
|
if (err == os_sockEWOULDBLOCK) {
|
|
WSANETWORKEVENTS ev;
|
|
WaitForSingleObject(uc->m_sockEvent, INFINITE);
|
|
WSAEnumNetworkEvents(uc->m_sock, uc->m_sockEvent, &ev);
|
|
}
|
|
#endif
|
|
} while (err == os_sockEINTR || err == os_sockEWOULDBLOCK || (err == os_sockEPERM && retry-- > 0));
|
|
if (ret > 0 && gv.pcap_fp)
|
|
{
|
|
os_sockaddr_storage sa;
|
|
socklen_t alen = sizeof (sa);
|
|
if (getsockname (((ddsi_udp_conn_t) conn)->m_sock, (struct sockaddr *) &sa, &alen) == -1)
|
|
memset(&sa, 0, sizeof(sa));
|
|
write_pcap_sent (gv.pcap_fp, now (), &sa, msg, (size_t) ret);
|
|
}
|
|
else if (ret == -1)
|
|
{
|
|
switch (err)
|
|
{
|
|
case os_sockEPERM:
|
|
case os_sockECONNRESET:
|
|
#ifdef os_sockENETUNREACH
|
|
case os_sockENETUNREACH:
|
|
#endif
|
|
#ifdef os_sockEHOSTUNREACH
|
|
case os_sockEHOSTUNREACH:
|
|
#endif
|
|
break;
|
|
default:
|
|
NN_ERROR("ddsi_udp_conn_write failed with error code %d", err);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static os_handle ddsi_udp_conn_handle (ddsi_tran_base_t base)
|
|
{
|
|
return ((ddsi_udp_conn_t) base)->m_sock;
|
|
}
|
|
|
|
static bool ddsi_udp_supports (int32_t kind)
|
|
{
|
|
return
|
|
(
|
|
(!config.useIpv6 && (kind == NN_LOCATOR_KIND_UDPv4))
|
|
#if OS_SOCKET_HAS_IPV6
|
|
|| (config.useIpv6 && (kind == NN_LOCATOR_KIND_UDPv6))
|
|
#endif
|
|
);
|
|
}
|
|
|
|
static int ddsi_udp_conn_locator (ddsi_tran_base_t base, nn_locator_t *loc)
|
|
{
|
|
int ret = -1;
|
|
ddsi_udp_conn_t uc = (ddsi_udp_conn_t) base;
|
|
os_sockaddr_storage * addr = &gv.extip;
|
|
|
|
memset (loc, 0, sizeof (*loc));
|
|
if (uc->m_sock != Q_INVALID_SOCKET)
|
|
{
|
|
loc->kind = ddsi_udp_factory_g.m_kind;
|
|
loc->port = uc->m_base.m_base.m_port;
|
|
|
|
if (loc->kind == NN_LOCATOR_KIND_UDPv4)
|
|
{
|
|
memcpy (loc->address + 12, &((os_sockaddr_in*) addr)->sin_addr, 4);
|
|
}
|
|
else
|
|
{
|
|
memcpy (loc->address, &((os_sockaddr_in6*) addr)->sin6_addr, 16);
|
|
}
|
|
ret = 0;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static ddsi_tran_conn_t ddsi_udp_create_conn
|
|
(
|
|
uint32_t port,
|
|
ddsi_tran_qos_t qos
|
|
)
|
|
{
|
|
int ret;
|
|
os_socket sock;
|
|
ddsi_udp_conn_t uc = NULL;
|
|
bool mcast = (bool) (qos ? qos->m_multicast : false);
|
|
|
|
/* If port is zero, need to create dynamic port */
|
|
|
|
ret = make_socket
|
|
(
|
|
&sock,
|
|
(unsigned short) port,
|
|
false,
|
|
mcast
|
|
);
|
|
|
|
if (ret == 0)
|
|
{
|
|
uc = (ddsi_udp_conn_t) os_malloc (sizeof (*uc));
|
|
memset (uc, 0, sizeof (*uc));
|
|
|
|
uc->m_sock = sock;
|
|
uc->m_diffserv = qos ? qos->m_diffserv : 0;
|
|
#if defined _WIN32 && !defined WINCE
|
|
uc->m_sockEvent = WSACreateEvent();
|
|
WSAEventSelect(uc->m_sock, uc->m_sockEvent, FD_WRITE);
|
|
#endif
|
|
|
|
ddsi_factory_conn_init (&ddsi_udp_factory_g, &uc->m_base);
|
|
uc->m_base.m_base.m_port = get_socket_port (sock);
|
|
uc->m_base.m_base.m_trantype = DDSI_TRAN_CONN;
|
|
uc->m_base.m_base.m_multicast = mcast;
|
|
uc->m_base.m_base.m_handle_fn = ddsi_udp_conn_handle;
|
|
uc->m_base.m_base.m_locator_fn = ddsi_udp_conn_locator;
|
|
|
|
uc->m_base.m_read_fn = ddsi_udp_conn_read;
|
|
uc->m_base.m_write_fn = ddsi_udp_conn_write;
|
|
|
|
nn_log
|
|
(
|
|
LC_INFO,
|
|
"ddsi_udp_create_conn %s socket %"PRIsock" port %u\n",
|
|
mcast ? "multicast" : "unicast",
|
|
uc->m_sock,
|
|
uc->m_base.m_base.m_port
|
|
);
|
|
#ifdef DDSI_INCLUDE_NETWORK_CHANNELS
|
|
if ((uc->m_diffserv != 0) && (ddsi_udp_factory_g.m_kind == NN_LOCATOR_KIND_UDPv4))
|
|
{
|
|
set_socket_diffserv (uc->m_sock, uc->m_diffserv);
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
if (config.participantIndex != PARTICIPANT_INDEX_AUTO)
|
|
{
|
|
NN_ERROR
|
|
(
|
|
"UDP make_socket failed for %s port %u\n",
|
|
mcast ? "multicast" : "unicast",
|
|
port
|
|
);
|
|
}
|
|
}
|
|
|
|
return uc ? &uc->m_base : NULL;
|
|
}
|
|
|
|
static int ddsi_udp_join_mc (ddsi_tran_conn_t conn, const nn_locator_t *srcloc, const nn_locator_t *mcloc)
|
|
{
|
|
ddsi_udp_conn_t uc = (ddsi_udp_conn_t) conn;
|
|
os_sockaddr_storage mcip, srcip;
|
|
nn_loc_to_address (&mcip, mcloc);
|
|
if (srcloc)
|
|
nn_loc_to_address (&srcip, srcloc);
|
|
return join_mcgroups (ddsi_udp_config_g.mship, uc->m_sock, srcloc ? &srcip : NULL, &mcip);
|
|
}
|
|
|
|
static int ddsi_udp_leave_mc (ddsi_tran_conn_t conn, const nn_locator_t *srcloc, const nn_locator_t *mcloc)
|
|
{
|
|
ddsi_udp_conn_t uc = (ddsi_udp_conn_t) conn;
|
|
os_sockaddr_storage mcip, srcip;
|
|
nn_loc_to_address (&mcip, mcloc);
|
|
if (srcloc)
|
|
nn_loc_to_address (&srcip, srcloc);
|
|
return leave_mcgroups (ddsi_udp_config_g.mship, uc->m_sock, srcloc ? &srcip : NULL, &mcip);
|
|
}
|
|
|
|
static void ddsi_udp_release_conn (ddsi_tran_conn_t conn)
|
|
{
|
|
ddsi_udp_conn_t uc = (ddsi_udp_conn_t) conn;
|
|
nn_log
|
|
(
|
|
LC_INFO,
|
|
"ddsi_udp_release_conn %s socket %"PRIsock" port %u\n",
|
|
conn->m_base.m_multicast ? "multicast" : "unicast",
|
|
uc->m_sock,
|
|
uc->m_base.m_base.m_port
|
|
);
|
|
os_sockFree (uc->m_sock);
|
|
#if defined _WIN32 && !defined WINCE
|
|
WSACloseEvent(uc->m_sockEvent);
|
|
#endif
|
|
os_free (conn);
|
|
}
|
|
|
|
void ddsi_udp_fini (void)
|
|
{
|
|
if(os_atomic_dec32_nv (&ddsi_udp_init_g) == 0) {
|
|
free_group_membership(ddsi_udp_config_g.mship);
|
|
memset (&ddsi_udp_factory_g, 0, sizeof (ddsi_udp_factory_g));
|
|
nn_log (LC_INFO | LC_CONFIG, "udp finalized\n");
|
|
}
|
|
}
|
|
|
|
int ddsi_udp_init (void)
|
|
{
|
|
/* TODO: proper init_once. Either the call doesn't need it, in which case
|
|
* this can be removed. Or the call does, in which case it should be done right.
|
|
* The lack of locking suggests it isn't needed.
|
|
*/
|
|
if (os_atomic_inc32_nv (&ddsi_udp_init_g) == 1)
|
|
{
|
|
memset (&ddsi_udp_factory_g, 0, sizeof (ddsi_udp_factory_g));
|
|
ddsi_udp_factory_g.m_kind = NN_LOCATOR_KIND_UDPv4;
|
|
ddsi_udp_factory_g.m_typename = "udp";
|
|
ddsi_udp_factory_g.m_connless = true;
|
|
ddsi_udp_factory_g.m_supports_fn = ddsi_udp_supports;
|
|
ddsi_udp_factory_g.m_create_conn_fn = ddsi_udp_create_conn;
|
|
ddsi_udp_factory_g.m_release_conn_fn = ddsi_udp_release_conn;
|
|
ddsi_udp_factory_g.m_free_fn = ddsi_udp_fini;
|
|
ddsi_udp_factory_g.m_join_mc_fn = ddsi_udp_join_mc;
|
|
ddsi_udp_factory_g.m_leave_mc_fn = ddsi_udp_leave_mc;
|
|
#if OS_SOCKET_HAS_IPV6
|
|
if (config.useIpv6)
|
|
{
|
|
ddsi_udp_factory_g.m_kind = NN_LOCATOR_KIND_UDPv6;
|
|
}
|
|
#endif
|
|
|
|
ddsi_udp_config_g.mship = new_group_membership();
|
|
|
|
ddsi_factory_add (&ddsi_udp_factory_g);
|
|
|
|
nn_log (LC_INFO | LC_CONFIG, "udp initialized\n");
|
|
}
|
|
return 0;
|
|
}
|