When the RX duration is known (HW timestamping), save the PTP correction from received PTP messages in the local RX timestamp.
634 lines
17 KiB
C
634 lines
17 KiB
C
/*
|
|
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
|
|
|
**********************************************************************
|
|
* Copyright (C) Richard P. Curnow 1997-2003
|
|
* Copyright (C) Timo Teras 2009
|
|
* Copyright (C) Miroslav Lichvar 2009, 2013-2016, 2018-2021
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of version 2 of the GNU General Public License as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
**********************************************************************
|
|
|
|
=======================================================================
|
|
|
|
This file deals with the IO aspects of reading and writing NTP packets
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "sysincl.h"
|
|
|
|
#include "memory.h"
|
|
#include "ntp_io.h"
|
|
#include "ntp_core.h"
|
|
#include "ntp_sources.h"
|
|
#include "ptp.h"
|
|
#include "sched.h"
|
|
#include "socket.h"
|
|
#include "local.h"
|
|
#include "logging.h"
|
|
#include "conf.h"
|
|
#include "privops.h"
|
|
#include "util.h"
|
|
|
|
#ifdef HAVE_LINUX_TIMESTAMPING
|
|
#include "ntp_io_linux.h"
|
|
#endif
|
|
|
|
#define INVALID_SOCK_FD -1
|
|
|
|
/* The server/peer and client sockets for IPv4 and IPv6 */
|
|
static int server_sock_fd4;
|
|
static int server_sock_fd6;
|
|
static int client_sock_fd4;
|
|
static int client_sock_fd6;
|
|
|
|
/* Reference counters for server sockets to keep them open only when needed */
|
|
static int server_sock_ref4;
|
|
static int server_sock_ref6;
|
|
|
|
/* Flag indicating we create a new connected client socket for each
|
|
server instead of sharing client_sock_fd4 and client_sock_fd6 */
|
|
static int separate_client_sockets;
|
|
|
|
/* Flag indicating the server sockets are not created dynamically when needed,
|
|
either to have a socket for client requests when separate client sockets
|
|
are disabled and client port is equal to server port, or the server port is
|
|
disabled */
|
|
static int permanent_server_sockets;
|
|
|
|
/* Flag indicating the server IPv4 socket is bound to an address */
|
|
static int bound_server_sock_fd4;
|
|
|
|
/* PTP event port, or 0 if disabled */
|
|
static int ptp_port;
|
|
|
|
/* Shared server/client sockets for NTP-over-PTP */
|
|
static int ptp_sock_fd4;
|
|
static int ptp_sock_fd6;
|
|
|
|
/* Buffer for transmitted NTP-over-PTP messages */
|
|
static PTP_NtpMessage *ptp_message;
|
|
|
|
/* Flag indicating that we have been initialised */
|
|
static int initialised=0;
|
|
|
|
/* ================================================== */
|
|
|
|
/* Forward prototypes */
|
|
static void read_from_socket(int sock_fd, int event, void *anything);
|
|
|
|
/* ================================================== */
|
|
|
|
static int
|
|
open_socket(int family, int local_port, int client_only, IPSockAddr *remote_addr)
|
|
{
|
|
int sock_fd, sock_flags, dscp, events = SCH_FILE_INPUT;
|
|
IPSockAddr local_addr;
|
|
char *iface;
|
|
|
|
if (!SCK_IsIpFamilyEnabled(family))
|
|
return INVALID_SOCK_FD;
|
|
|
|
if (!client_only) {
|
|
CNF_GetBindAddress(family, &local_addr.ip_addr);
|
|
iface = CNF_GetBindNtpInterface();
|
|
} else {
|
|
CNF_GetBindAcquisitionAddress(family, &local_addr.ip_addr);
|
|
iface = CNF_GetBindAcquisitionInterface();
|
|
}
|
|
|
|
local_addr.port = local_port;
|
|
|
|
sock_flags = SCK_FLAG_RX_DEST_ADDR | SCK_FLAG_PRIV_BIND;
|
|
if (!client_only)
|
|
sock_flags |= SCK_FLAG_BROADCAST;
|
|
|
|
sock_fd = SCK_OpenUdpSocket(remote_addr, &local_addr, iface, sock_flags);
|
|
if (sock_fd < 0) {
|
|
if (!client_only)
|
|
LOG(LOGS_ERR, "Could not open NTP socket on %s", UTI_IPSockAddrToString(&local_addr));
|
|
return INVALID_SOCK_FD;
|
|
}
|
|
|
|
dscp = CNF_GetNtpDscp();
|
|
if (dscp > 0 && dscp < 64) {
|
|
#ifdef IP_TOS
|
|
if (family == IPADDR_INET4)
|
|
if (!SCK_SetIntOption(sock_fd, IPPROTO_IP, IP_TOS, dscp << 2))
|
|
;
|
|
#endif
|
|
#if defined(FEAT_IPV6) && defined(IPV6_TCLASS)
|
|
if (family == IPADDR_INET6)
|
|
if (!SCK_SetIntOption(sock_fd, IPPROTO_IPV6, IPV6_TCLASS, dscp << 2))
|
|
;
|
|
#endif
|
|
}
|
|
|
|
if (!client_only && family == IPADDR_INET4 && local_addr.port > 0)
|
|
bound_server_sock_fd4 = local_addr.ip_addr.addr.in4 != INADDR_ANY;
|
|
|
|
/* Enable kernel/HW timestamping of packets */
|
|
#ifdef HAVE_LINUX_TIMESTAMPING
|
|
if (!NIO_Linux_SetTimestampSocketOptions(sock_fd, client_only, &events))
|
|
#endif
|
|
if (!SCK_EnableKernelRxTimestamping(sock_fd))
|
|
;
|
|
|
|
/* Register handler for read and possibly exception events on the socket */
|
|
SCH_AddFileHandler(sock_fd, events, read_from_socket, NULL);
|
|
|
|
return sock_fd;
|
|
}
|
|
|
|
/* ================================================== */
|
|
|
|
static int
|
|
open_separate_client_socket(IPSockAddr *remote_addr)
|
|
{
|
|
return open_socket(remote_addr->ip_addr.family, 0, 1, remote_addr);
|
|
}
|
|
|
|
/* ================================================== */
|
|
|
|
static void
|
|
close_socket(int sock_fd)
|
|
{
|
|
if (sock_fd == INVALID_SOCK_FD)
|
|
return;
|
|
|
|
SCH_RemoveFileHandler(sock_fd);
|
|
SCK_CloseSocket(sock_fd);
|
|
}
|
|
|
|
/* ================================================== */
|
|
|
|
void
|
|
NIO_Initialise(void)
|
|
{
|
|
int server_port, client_port;
|
|
|
|
assert(!initialised);
|
|
initialised = 1;
|
|
|
|
#ifdef PRIVOPS_BINDSOCKET
|
|
SCK_SetPrivBind(PRV_BindSocket);
|
|
#endif
|
|
|
|
#ifdef HAVE_LINUX_TIMESTAMPING
|
|
NIO_Linux_Initialise();
|
|
#else
|
|
if (1) {
|
|
CNF_HwTsInterface *conf_iface;
|
|
if (CNF_GetHwTsInterface(0, &conf_iface))
|
|
LOG_FATAL("HW timestamping not supported");
|
|
}
|
|
#endif
|
|
|
|
server_port = CNF_GetNTPPort();
|
|
client_port = CNF_GetAcquisitionPort();
|
|
|
|
/* Use separate connected sockets if client port is negative */
|
|
separate_client_sockets = client_port < 0;
|
|
if (client_port < 0)
|
|
client_port = 0;
|
|
|
|
permanent_server_sockets = !server_port || (!separate_client_sockets &&
|
|
client_port == server_port);
|
|
|
|
server_sock_fd4 = INVALID_SOCK_FD;
|
|
server_sock_fd6 = INVALID_SOCK_FD;
|
|
client_sock_fd4 = INVALID_SOCK_FD;
|
|
client_sock_fd6 = INVALID_SOCK_FD;
|
|
server_sock_ref4 = 0;
|
|
server_sock_ref6 = 0;
|
|
|
|
if (permanent_server_sockets && server_port) {
|
|
server_sock_fd4 = open_socket(IPADDR_INET4, server_port, 0, NULL);
|
|
server_sock_fd6 = open_socket(IPADDR_INET6, server_port, 0, NULL);
|
|
}
|
|
|
|
if (!separate_client_sockets) {
|
|
if (client_port != server_port || !server_port) {
|
|
client_sock_fd4 = open_socket(IPADDR_INET4, client_port, 1, NULL);
|
|
client_sock_fd6 = open_socket(IPADDR_INET6, client_port, 1, NULL);
|
|
} else {
|
|
client_sock_fd4 = server_sock_fd4;
|
|
client_sock_fd6 = server_sock_fd6;
|
|
}
|
|
}
|
|
|
|
if ((server_port && permanent_server_sockets &&
|
|
server_sock_fd4 == INVALID_SOCK_FD && server_sock_fd6 == INVALID_SOCK_FD) ||
|
|
(!separate_client_sockets &&
|
|
client_sock_fd4 == INVALID_SOCK_FD && client_sock_fd6 == INVALID_SOCK_FD)) {
|
|
LOG_FATAL("Could not open NTP sockets");
|
|
}
|
|
|
|
ptp_port = CNF_GetPtpPort();
|
|
ptp_sock_fd4 = INVALID_SOCK_FD;
|
|
ptp_sock_fd6 = INVALID_SOCK_FD;
|
|
ptp_message = NULL;
|
|
|
|
if (ptp_port > 0) {
|
|
ptp_sock_fd4 = open_socket(IPADDR_INET4, ptp_port, 0, NULL);
|
|
ptp_sock_fd6 = open_socket(IPADDR_INET6, ptp_port, 0, NULL);
|
|
ptp_message = MallocNew(PTP_NtpMessage);
|
|
}
|
|
}
|
|
|
|
/* ================================================== */
|
|
|
|
void
|
|
NIO_Finalise(void)
|
|
{
|
|
if (server_sock_fd4 != client_sock_fd4)
|
|
close_socket(client_sock_fd4);
|
|
close_socket(server_sock_fd4);
|
|
server_sock_fd4 = client_sock_fd4 = INVALID_SOCK_FD;
|
|
|
|
if (server_sock_fd6 != client_sock_fd6)
|
|
close_socket(client_sock_fd6);
|
|
close_socket(server_sock_fd6);
|
|
server_sock_fd6 = client_sock_fd6 = INVALID_SOCK_FD;
|
|
|
|
close_socket(ptp_sock_fd4);
|
|
close_socket(ptp_sock_fd6);
|
|
ptp_sock_fd4 = ptp_sock_fd6 = INVALID_SOCK_FD;
|
|
Free(ptp_message);
|
|
|
|
#ifdef HAVE_LINUX_TIMESTAMPING
|
|
NIO_Linux_Finalise();
|
|
#endif
|
|
|
|
initialised = 0;
|
|
}
|
|
|
|
/* ================================================== */
|
|
|
|
int
|
|
NIO_IsHwTsEnabled(void)
|
|
{
|
|
#ifdef HAVE_LINUX_TIMESTAMPING
|
|
return NIO_Linux_IsHwTsEnabled();
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
/* ================================================== */
|
|
|
|
int
|
|
NIO_OpenClientSocket(NTP_Remote_Address *remote_addr)
|
|
{
|
|
switch (remote_addr->ip_addr.family) {
|
|
case IPADDR_INET4:
|
|
if (ptp_port > 0 && remote_addr->port == ptp_port)
|
|
return ptp_sock_fd4;
|
|
if (separate_client_sockets)
|
|
return open_separate_client_socket(remote_addr);
|
|
return client_sock_fd4;
|
|
case IPADDR_INET6:
|
|
if (ptp_port > 0 && remote_addr->port == ptp_port)
|
|
return ptp_sock_fd6;
|
|
if (separate_client_sockets)
|
|
return open_separate_client_socket(remote_addr);
|
|
return client_sock_fd6;
|
|
default:
|
|
return INVALID_SOCK_FD;
|
|
}
|
|
}
|
|
|
|
/* ================================================== */
|
|
|
|
int
|
|
NIO_OpenServerSocket(NTP_Remote_Address *remote_addr)
|
|
{
|
|
switch (remote_addr->ip_addr.family) {
|
|
case IPADDR_INET4:
|
|
if (ptp_port > 0 && remote_addr->port == ptp_port)
|
|
return ptp_sock_fd4;
|
|
if (permanent_server_sockets)
|
|
return server_sock_fd4;
|
|
if (server_sock_fd4 == INVALID_SOCK_FD)
|
|
server_sock_fd4 = open_socket(IPADDR_INET4, CNF_GetNTPPort(), 0, NULL);
|
|
if (server_sock_fd4 != INVALID_SOCK_FD)
|
|
server_sock_ref4++;
|
|
return server_sock_fd4;
|
|
case IPADDR_INET6:
|
|
if (ptp_port > 0 && remote_addr->port == ptp_port)
|
|
return ptp_sock_fd6;
|
|
if (permanent_server_sockets)
|
|
return server_sock_fd6;
|
|
if (server_sock_fd6 == INVALID_SOCK_FD)
|
|
server_sock_fd6 = open_socket(IPADDR_INET6, CNF_GetNTPPort(), 0, NULL);
|
|
if (server_sock_fd6 != INVALID_SOCK_FD)
|
|
server_sock_ref6++;
|
|
return server_sock_fd6;
|
|
default:
|
|
return INVALID_SOCK_FD;
|
|
}
|
|
}
|
|
|
|
/* ================================================== */
|
|
|
|
static int
|
|
is_ptp_socket(int sock_fd)
|
|
{
|
|
return ptp_port > 0 && sock_fd != INVALID_SOCK_FD &&
|
|
(sock_fd == ptp_sock_fd4 || sock_fd == ptp_sock_fd6);
|
|
}
|
|
|
|
/* ================================================== */
|
|
|
|
void
|
|
NIO_CloseClientSocket(int sock_fd)
|
|
{
|
|
if (is_ptp_socket(sock_fd))
|
|
return;
|
|
|
|
if (separate_client_sockets)
|
|
close_socket(sock_fd);
|
|
}
|
|
|
|
/* ================================================== */
|
|
|
|
void
|
|
NIO_CloseServerSocket(int sock_fd)
|
|
{
|
|
if (permanent_server_sockets || sock_fd == INVALID_SOCK_FD || is_ptp_socket(sock_fd))
|
|
return;
|
|
|
|
if (sock_fd == server_sock_fd4) {
|
|
if (--server_sock_ref4 <= 0) {
|
|
close_socket(server_sock_fd4);
|
|
server_sock_fd4 = INVALID_SOCK_FD;
|
|
}
|
|
} else if (sock_fd == server_sock_fd6) {
|
|
if (--server_sock_ref6 <= 0) {
|
|
close_socket(server_sock_fd6);
|
|
server_sock_fd6 = INVALID_SOCK_FD;
|
|
}
|
|
} else {
|
|
assert(0);
|
|
}
|
|
}
|
|
|
|
/* ================================================== */
|
|
|
|
int
|
|
NIO_IsServerSocket(int sock_fd)
|
|
{
|
|
return sock_fd != INVALID_SOCK_FD &&
|
|
(sock_fd == server_sock_fd4 || sock_fd == server_sock_fd6 || is_ptp_socket(sock_fd));
|
|
}
|
|
|
|
/* ================================================== */
|
|
|
|
int
|
|
NIO_IsServerSocketOpen(void)
|
|
{
|
|
return server_sock_fd4 != INVALID_SOCK_FD || server_sock_fd6 != INVALID_SOCK_FD ||
|
|
ptp_sock_fd4 != INVALID_SOCK_FD || ptp_sock_fd6 != INVALID_SOCK_FD;
|
|
}
|
|
|
|
/* ================================================== */
|
|
|
|
int
|
|
NIO_IsServerConnectable(NTP_Remote_Address *remote_addr)
|
|
{
|
|
int sock_fd;
|
|
|
|
sock_fd = open_separate_client_socket(remote_addr);
|
|
if (sock_fd == INVALID_SOCK_FD)
|
|
return 0;
|
|
|
|
close_socket(sock_fd);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* ================================================== */
|
|
|
|
static void
|
|
process_message(SCK_Message *message, int sock_fd, int event)
|
|
{
|
|
NTP_Local_Address local_addr;
|
|
NTP_Local_Timestamp local_ts;
|
|
struct timespec sched_ts;
|
|
|
|
SCH_GetLastEventTime(&local_ts.ts, &local_ts.err, NULL);
|
|
local_ts.source = NTP_TS_DAEMON;
|
|
local_ts.rx_duration = 0.0;
|
|
local_ts.net_correction = 0.0;
|
|
|
|
sched_ts = local_ts.ts;
|
|
|
|
if (message->addr_type != SCK_ADDR_IP) {
|
|
DEBUG_LOG("Unexpected address type");
|
|
return;
|
|
}
|
|
|
|
local_addr.ip_addr = message->local_addr.ip;
|
|
local_addr.if_index = message->if_index;;
|
|
local_addr.sock_fd = sock_fd;
|
|
|
|
#ifdef HAVE_LINUX_TIMESTAMPING
|
|
if (NIO_Linux_ProcessMessage(message, &local_addr, &local_ts, event))
|
|
return;
|
|
#else
|
|
if (!UTI_IsZeroTimespec(&message->timestamp.kernel)) {
|
|
LCL_CookTime(&message->timestamp.kernel, &local_ts.ts, &local_ts.err);
|
|
local_ts.source = NTP_TS_KERNEL;
|
|
}
|
|
#endif
|
|
|
|
if (local_ts.source != NTP_TS_DAEMON)
|
|
DEBUG_LOG("Updated RX timestamp delay=%.9f tss=%u",
|
|
UTI_DiffTimespecsToDouble(&sched_ts, &local_ts.ts), local_ts.source);
|
|
|
|
if (!NIO_UnwrapMessage(message, sock_fd, &local_ts.net_correction))
|
|
return;
|
|
|
|
/* Just ignore the packet if it's not of a recognized length */
|
|
if (message->length < NTP_HEADER_LENGTH || message->length > sizeof (NTP_Packet)) {
|
|
DEBUG_LOG("Unexpected length");
|
|
return;
|
|
}
|
|
|
|
NSR_ProcessRx(&message->remote_addr.ip, &local_addr, &local_ts, message->data, message->length);
|
|
}
|
|
|
|
/* ================================================== */
|
|
|
|
static void
|
|
read_from_socket(int sock_fd, int event, void *anything)
|
|
{
|
|
SCK_Message *messages;
|
|
int i, received, flags = 0;
|
|
|
|
if (event == SCH_FILE_EXCEPTION) {
|
|
#ifdef HAVE_LINUX_TIMESTAMPING
|
|
flags |= SCK_FLAG_MSG_ERRQUEUE;
|
|
#else
|
|
assert(0);
|
|
#endif
|
|
}
|
|
|
|
messages = SCK_ReceiveMessages(sock_fd, flags, &received);
|
|
if (!messages)
|
|
return;
|
|
|
|
for (i = 0; i < received; i++)
|
|
process_message(&messages[i], sock_fd, event);
|
|
}
|
|
|
|
/* ================================================== */
|
|
|
|
int
|
|
NIO_UnwrapMessage(SCK_Message *message, int sock_fd, double *net_correction)
|
|
{
|
|
double ptp_correction;
|
|
PTP_NtpMessage *msg;
|
|
|
|
if (!is_ptp_socket(sock_fd))
|
|
return 1;
|
|
|
|
if (message->length <= PTP_NTP_PREFIX_LENGTH) {
|
|
DEBUG_LOG("Unexpected length");
|
|
return 0;
|
|
}
|
|
|
|
msg = message->data;
|
|
|
|
if (msg->header.type != PTP_TYPE_DELAY_REQ || msg->header.version != PTP_VERSION ||
|
|
ntohs(msg->header.length) != message->length ||
|
|
msg->header.domain != PTP_DOMAIN_NTP ||
|
|
ntohs(msg->header.flags) != PTP_FLAG_UNICAST ||
|
|
ntohs(msg->tlv_header.type) != PTP_TLV_NTP ||
|
|
ntohs(msg->tlv_header.length) != message->length - PTP_NTP_PREFIX_LENGTH) {
|
|
DEBUG_LOG("Unexpected PTP message");
|
|
return 0;
|
|
}
|
|
|
|
message->data = (char *)message->data + PTP_NTP_PREFIX_LENGTH;
|
|
message->length -= PTP_NTP_PREFIX_LENGTH;
|
|
|
|
ptp_correction = UTI_Integer64NetworkToHost(*(Integer64 *)msg->header.correction) /
|
|
((1 << 16) * 1.0e9);
|
|
|
|
/* Use the correction only if the RX duration is known (i.e. HW timestamp) */
|
|
if (*net_correction > 0.0)
|
|
*net_correction += ptp_correction;
|
|
|
|
DEBUG_LOG("Unwrapped PTP->NTP len=%d corr=%.9f", message->length, ptp_correction);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* ================================================== */
|
|
|
|
static int
|
|
wrap_message(SCK_Message *message, int sock_fd)
|
|
{
|
|
static uint16_t sequence_id = 0;
|
|
|
|
assert(PTP_NTP_PREFIX_LENGTH == 48);
|
|
|
|
if (!is_ptp_socket(sock_fd))
|
|
return 1;
|
|
|
|
if (!ptp_message)
|
|
return 0;
|
|
|
|
if (message->length < NTP_HEADER_LENGTH ||
|
|
message->length + PTP_NTP_PREFIX_LENGTH > sizeof (*ptp_message)) {
|
|
DEBUG_LOG("Unexpected length");
|
|
return 0;
|
|
}
|
|
|
|
memset(ptp_message, 0, PTP_NTP_PREFIX_LENGTH);
|
|
ptp_message->header.type = PTP_TYPE_DELAY_REQ;
|
|
ptp_message->header.version = PTP_VERSION;
|
|
ptp_message->header.length = htons(PTP_NTP_PREFIX_LENGTH + message->length);
|
|
ptp_message->header.domain = PTP_DOMAIN_NTP;
|
|
ptp_message->header.flags = htons(PTP_FLAG_UNICAST);
|
|
ptp_message->header.sequence_id = htons(sequence_id++);
|
|
ptp_message->tlv_header.type = htons(PTP_TLV_NTP);
|
|
ptp_message->tlv_header.length = htons(message->length);
|
|
memcpy((char *)ptp_message + PTP_NTP_PREFIX_LENGTH, message->data, message->length);
|
|
|
|
message->data = ptp_message;
|
|
message->length += PTP_NTP_PREFIX_LENGTH;
|
|
|
|
DEBUG_LOG("Wrapped NTP->PTP len=%d", message->length - PTP_NTP_PREFIX_LENGTH);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* ================================================== */
|
|
/* Send a packet to remote address from local address */
|
|
|
|
int
|
|
NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr,
|
|
NTP_Local_Address *local_addr, int length, int process_tx)
|
|
{
|
|
SCK_Message message;
|
|
|
|
assert(initialised);
|
|
|
|
if (local_addr->sock_fd == INVALID_SOCK_FD) {
|
|
DEBUG_LOG("No socket to send to %s", UTI_IPSockAddrToString(remote_addr));
|
|
return 0;
|
|
}
|
|
|
|
SCK_InitMessage(&message, SCK_ADDR_IP);
|
|
|
|
message.data = packet;
|
|
message.length = length;
|
|
|
|
if (!wrap_message(&message, local_addr->sock_fd))
|
|
return 0;
|
|
|
|
/* Specify remote address if the socket is not connected */
|
|
if (NIO_IsServerSocket(local_addr->sock_fd) || !separate_client_sockets) {
|
|
message.remote_addr.ip.ip_addr = remote_addr->ip_addr;
|
|
message.remote_addr.ip.port = remote_addr->port;
|
|
}
|
|
|
|
message.local_addr.ip = local_addr->ip_addr;
|
|
|
|
/* Don't require responses to non-link-local addresses to use the same
|
|
interface */
|
|
message.if_index = SCK_IsLinkLocalIPAddress(&message.remote_addr.ip.ip_addr) ?
|
|
local_addr->if_index : INVALID_IF_INDEX;
|
|
|
|
#if !defined(HAVE_IN_PKTINFO) && defined(IP_SENDSRCADDR)
|
|
/* On FreeBSD a local IPv4 address cannot be specified on bound socket */
|
|
if (message.local_addr.ip.family == IPADDR_INET4 &&
|
|
(bound_server_sock_fd4 || !NIO_IsServerSocket(local_addr->sock_fd)))
|
|
message.local_addr.ip.family = IPADDR_UNSPEC;
|
|
#endif
|
|
|
|
#ifdef HAVE_LINUX_TIMESTAMPING
|
|
if (process_tx)
|
|
NIO_Linux_RequestTxTimestamp(&message, local_addr->sock_fd);
|
|
#endif
|
|
|
|
if (!SCK_SendMessage(local_addr->sock_fd, &message, 0))
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|