ntp: convert to new socket API

Rework the NTP I/O code to use the new socket support. There are
differences in debug messages and handling of some errors.
This commit is contained in:
Miroslav Lichvar 2019-07-18 08:35:16 +02:00
parent 86a3ef9ed1
commit 2de24cfd82
7 changed files with 192 additions and 711 deletions

View file

@ -36,8 +36,8 @@ DESTDIR=
HASH_OBJ = @HASH_OBJ@ HASH_OBJ = @HASH_OBJ@
OBJS = array.o cmdparse.o conf.o local.o logging.o main.o memory.o \ OBJS = array.o cmdparse.o conf.o local.o logging.o main.o memory.o \
reference.o regress.o rtc.o samplefilt.o sched.o sources.o sourcestats.o stubs.o \ reference.o regress.o rtc.o samplefilt.o sched.o socket.o sources.o sourcestats.o \
smooth.o sys.o sys_null.o tempcomp.o util.o $(HASH_OBJ) stubs.o smooth.o sys.o sys_null.o tempcomp.o util.o $(HASH_OBJ)
EXTRA_OBJS=@EXTRA_OBJECTS@ EXTRA_OBJS=@EXTRA_OBJECTS@

3
main.c
View file

@ -38,6 +38,7 @@
#include "ntp_signd.h" #include "ntp_signd.h"
#include "ntp_sources.h" #include "ntp_sources.h"
#include "ntp_core.h" #include "ntp_core.h"
#include "socket.h"
#include "sources.h" #include "sources.h"
#include "sourcestats.h" #include "sourcestats.h"
#include "reference.h" #include "reference.h"
@ -118,6 +119,7 @@ MAI_CleanupAndExit(void)
NCR_Finalise(); NCR_Finalise();
NIO_Finalise(); NIO_Finalise();
CAM_Finalise(); CAM_Finalise();
SCK_Finalise();
KEY_Finalise(); KEY_Finalise();
RCL_Finalise(); RCL_Finalise();
SRC_Finalise(); SRC_Finalise();
@ -554,6 +556,7 @@ int main
SRC_Initialise(); SRC_Initialise();
RCL_Initialise(); RCL_Initialise();
KEY_Initialise(); KEY_Initialise();
SCK_Initialise();
/* Open privileged ports before dropping root */ /* Open privileged ports before dropping root */
CAM_Initialise(address_family); CAM_Initialise(address_family);

628
ntp_io.c
View file

@ -30,11 +30,11 @@
#include "sysincl.h" #include "sysincl.h"
#include "array.h"
#include "ntp_io.h" #include "ntp_io.h"
#include "ntp_core.h" #include "ntp_core.h"
#include "ntp_sources.h" #include "ntp_sources.h"
#include "sched.h" #include "sched.h"
#include "socket.h"
#include "local.h" #include "local.h"
#include "logging.h" #include "logging.h"
#include "conf.h" #include "conf.h"
@ -46,54 +46,16 @@
#endif #endif
#define INVALID_SOCK_FD -1 #define INVALID_SOCK_FD -1
#define CMSGBUF_SIZE 256
union sockaddr_in46 {
struct sockaddr_in in4;
#ifdef FEAT_IPV6
struct sockaddr_in6 in6;
#endif
struct sockaddr u;
};
struct Message {
union sockaddr_in46 name;
struct iovec iov;
NTP_Receive_Buffer buf;
/* Aligned buffer for control messages */
struct cmsghdr cmsgbuf[CMSGBUF_SIZE / sizeof (struct cmsghdr)];
};
#ifdef HAVE_RECVMMSG
#define MAX_RECV_MESSAGES 4
#define MessageHeader mmsghdr
#else
/* Compatible with mmsghdr */
struct MessageHeader {
struct msghdr msg_hdr;
unsigned int msg_len;
};
#define MAX_RECV_MESSAGES 1
#endif
/* Arrays of Message and MessageHeader */
static ARR_Instance recv_messages;
static ARR_Instance recv_headers;
/* The server/peer and client sockets for IPv4 and IPv6 */ /* The server/peer and client sockets for IPv4 and IPv6 */
static int server_sock_fd4; static int server_sock_fd4;
static int client_sock_fd4;
#ifdef FEAT_IPV6
static int server_sock_fd6; static int server_sock_fd6;
static int client_sock_fd4;
static int client_sock_fd6; static int client_sock_fd6;
#endif
/* Reference counters for server sockets to keep them open only when needed */ /* Reference counters for server sockets to keep them open only when needed */
static int server_sock_ref4; static int server_sock_ref4;
#ifdef FEAT_IPV6
static int server_sock_ref6; static int server_sock_ref6;
#endif
/* Flag indicating we create a new connected client socket for each /* Flag indicating we create a new connected client socket for each
server instead of sharing client_sock_fd4 and client_sock_fd6 */ server instead of sharing client_sock_fd4 and client_sock_fd6 */
@ -119,162 +81,42 @@ static void read_from_socket(int sock_fd, int event, void *anything);
/* ================================================== */ /* ================================================== */
static int static int
prepare_socket(int family, int port_number, int client_only) open_socket(int family, int local_port, int client_only, IPSockAddr *remote_addr)
{ {
union sockaddr_in46 my_addr; int sock_fd, sock_flags, events = SCH_FILE_INPUT;
socklen_t my_addr_len; IPSockAddr local_addr;
int sock_fd;
IPAddr bind_address;
int events = SCH_FILE_INPUT, on_off = 1;
/* Open Internet domain UDP socket for NTP message transmissions */ if (!client_only)
CNF_GetBindAddress(family, &local_addr.ip_addr);
else
CNF_GetBindAcquisitionAddress(family, &local_addr.ip_addr);
sock_fd = socket(family, SOCK_DGRAM, 0); if (local_addr.ip_addr.family != family)
SCK_GetAnyLocalIPAddress(family, &local_addr.ip_addr);
local_addr.port = local_port;
sock_flags = SCK_FLAG_RX_DEST_ADDR | SCK_FLAG_PRIV_BIND;
if (!client_only)
sock_flags |= SCK_FLAG_NONBLOCK | SCK_FLAG_BROADCAST;
sock_fd = SCK_OpenUdpSocket(remote_addr, &local_addr, sock_flags);
if (sock_fd < 0) { if (sock_fd < 0) {
if (!client_only) { if (!client_only)
LOG(LOGS_ERR, "Could not open %s NTP socket : %s", LOG(LOGS_ERR, "Could not open NTP socket on %s", UTI_IPSockAddrToString(&local_addr));
UTI_SockaddrFamilyToString(family), strerror(errno));
} else {
DEBUG_LOG("Could not open %s NTP socket : %s",
UTI_SockaddrFamilyToString(family), strerror(errno));
}
return INVALID_SOCK_FD; return INVALID_SOCK_FD;
} }
/* Close on exec */ if (!client_only && family == IPADDR_INET4 && local_addr.port > 0)
UTI_FdSetCloexec(sock_fd); bound_server_sock_fd4 = local_addr.ip_addr.addr.in4 != INADDR_ANY;
/* Enable non-blocking mode on server sockets */
if (!client_only && fcntl(sock_fd, F_SETFL, O_NONBLOCK))
DEBUG_LOG("Could not set O_NONBLOCK : %s", strerror(errno));
/* Prepare local address */
memset(&my_addr, 0, sizeof (my_addr));
my_addr_len = 0;
switch (family) {
case AF_INET:
if (!client_only)
CNF_GetBindAddress(IPADDR_INET4, &bind_address);
else
CNF_GetBindAcquisitionAddress(IPADDR_INET4, &bind_address);
if (bind_address.family == IPADDR_INET4)
my_addr.in4.sin_addr.s_addr = htonl(bind_address.addr.in4);
else if (port_number)
my_addr.in4.sin_addr.s_addr = htonl(INADDR_ANY);
else
break;
my_addr.in4.sin_family = family;
my_addr.in4.sin_port = htons(port_number);
my_addr_len = sizeof (my_addr.in4);
if (!client_only)
bound_server_sock_fd4 = my_addr.in4.sin_addr.s_addr != htonl(INADDR_ANY);
break;
#ifdef FEAT_IPV6
case AF_INET6:
if (!client_only)
CNF_GetBindAddress(IPADDR_INET6, &bind_address);
else
CNF_GetBindAcquisitionAddress(IPADDR_INET6, &bind_address);
if (bind_address.family == IPADDR_INET6)
memcpy(my_addr.in6.sin6_addr.s6_addr, bind_address.addr.in6,
sizeof (my_addr.in6.sin6_addr.s6_addr));
else if (port_number)
my_addr.in6.sin6_addr = in6addr_any;
else
break;
my_addr.in6.sin6_family = family;
my_addr.in6.sin6_port = htons(port_number);
my_addr_len = sizeof (my_addr.in6);
break;
#endif
default:
assert(0);
}
/* Make the socket capable of re-using an old address if binding to a specific port */
if (port_number &&
setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, "Could not set %s socket option", "SO_REUSEADDR");
/* Don't quit - we might survive anyway */
}
/* Make the socket capable of sending broadcast pkts - needed for NTP broadcast mode */
if (!client_only &&
setsockopt(sock_fd, SOL_SOCKET, SO_BROADCAST, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, "Could not set %s socket option", "SO_BROADCAST");
/* Don't quit - we might survive anyway */
}
/* Enable kernel/HW timestamping of packets */ /* Enable kernel/HW timestamping of packets */
#ifdef HAVE_LINUX_TIMESTAMPING #ifdef HAVE_LINUX_TIMESTAMPING
if (!NIO_Linux_SetTimestampSocketOptions(sock_fd, client_only, &events)) if (!NIO_Linux_SetTimestampSocketOptions(sock_fd, client_only, &events))
#endif #endif
#ifdef SO_TIMESTAMPNS if (!SCK_EnableKernelRxTimestamping(sock_fd))
if (setsockopt(sock_fd, SOL_SOCKET, SO_TIMESTAMPNS, (char *)&on_off, sizeof(on_off)) < 0)
#endif
#ifdef SO_TIMESTAMP
if (setsockopt(sock_fd, SOL_SOCKET, SO_TIMESTAMP, (char *)&on_off, sizeof(on_off)) < 0)
LOG(LOGS_ERR, "Could not set %s socket option", "SO_TIMESTAMP");
#endif
; ;
#ifdef IP_FREEBIND
/* Allow binding to address that doesn't exist yet */
if (my_addr_len > 0 &&
setsockopt(sock_fd, IPPROTO_IP, IP_FREEBIND, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, "Could not set %s socket option", "IP_FREEBIND");
}
#endif
if (family == AF_INET) {
#ifdef HAVE_IN_PKTINFO
if (setsockopt(sock_fd, IPPROTO_IP, IP_PKTINFO, (char *)&on_off, sizeof(on_off)) < 0)
LOG(LOGS_ERR, "Could not set %s socket option", "IP_PKTINFO");
#elif defined(IP_RECVDSTADDR)
if (setsockopt(sock_fd, IPPROTO_IP, IP_RECVDSTADDR, (char *)&on_off, sizeof(on_off)) < 0)
LOG(LOGS_ERR, "Could not set %s socket option", "IP_RECVDSTADDR");
#endif
}
#ifdef FEAT_IPV6
else if (family == AF_INET6) {
#ifdef IPV6_V6ONLY
/* Receive IPv6 packets only */
if (setsockopt(sock_fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, "Could not set %s socket option", "IPV6_V6ONLY");
}
#endif
#ifdef HAVE_IN6_PKTINFO
#ifdef IPV6_RECVPKTINFO
if (setsockopt(sock_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, "Could not set %s socket option", "IPV6_RECVPKTINFO");
}
#else
if (setsockopt(sock_fd, IPPROTO_IPV6, IPV6_PKTINFO, (char *)&on_off, sizeof(on_off)) < 0) {
LOG(LOGS_ERR, "Could not set %s socket option", "IPV6_PKTINFO");
}
#endif
#endif
}
#endif
/* Bind the socket if a port or address was specified */
if (my_addr_len > 0 && PRV_BindSocket(sock_fd, &my_addr.u, my_addr_len) < 0) {
LOG(LOGS_ERR, "Could not bind %s NTP socket : %s",
UTI_SockaddrFamilyToString(family), strerror(errno));
close(sock_fd);
return INVALID_SOCK_FD;
}
/* Register handler for read and possibly exception events on the socket */ /* Register handler for read and possibly exception events on the socket */
SCH_AddFileHandler(sock_fd, events, read_from_socket, NULL); SCH_AddFileHandler(sock_fd, events, read_from_socket, NULL);
@ -284,40 +126,9 @@ prepare_socket(int family, int port_number, int client_only)
/* ================================================== */ /* ================================================== */
static int static int
prepare_separate_client_socket(int family) open_separate_client_socket(IPSockAddr *remote_addr)
{ {
switch (family) { return open_socket(remote_addr->ip_addr.family, 0, 1, remote_addr);
case IPADDR_INET4:
return prepare_socket(AF_INET, 0, 1);
#ifdef FEAT_IPV6
case IPADDR_INET6:
return prepare_socket(AF_INET6, 0, 1);
#endif
default:
return INVALID_SOCK_FD;
}
}
/* ================================================== */
static int
connect_socket(int sock_fd, NTP_Remote_Address *remote_addr)
{
union sockaddr_in46 addr;
socklen_t addr_len;
addr_len = UTI_IPAndPortToSockaddr(&remote_addr->ip_addr, remote_addr->port, &addr.u);
assert(addr_len);
if (connect(sock_fd, &addr.u, addr_len) < 0) {
DEBUG_LOG("Could not connect NTP socket to %s:%d : %s",
UTI_IPToString(&remote_addr->ip_addr), remote_addr->port,
strerror(errno));
return 0;
}
return 1;
} }
/* ================================================== */ /* ================================================== */
@ -332,33 +143,7 @@ close_socket(int sock_fd)
NIO_Linux_NotifySocketClosing(sock_fd); NIO_Linux_NotifySocketClosing(sock_fd);
#endif #endif
SCH_RemoveFileHandler(sock_fd); SCH_RemoveFileHandler(sock_fd);
close(sock_fd); SCK_CloseSocket(sock_fd);
}
/* ================================================== */
static void
prepare_buffers(unsigned int n)
{
struct MessageHeader *hdr;
struct Message *msg;
unsigned int i;
for (i = 0; i < n; i++) {
msg = ARR_GetElement(recv_messages, i);
hdr = ARR_GetElement(recv_headers, i);
msg->iov.iov_base = &msg->buf;
msg->iov.iov_len = sizeof (msg->buf);
hdr->msg_hdr.msg_name = &msg->name;
hdr->msg_hdr.msg_namelen = sizeof (msg->name);
hdr->msg_hdr.msg_iov = &msg->iov;
hdr->msg_hdr.msg_iovlen = 1;
hdr->msg_hdr.msg_control = &msg->cmsgbuf;
hdr->msg_hdr.msg_controllen = sizeof (msg->cmsgbuf);
hdr->msg_hdr.msg_flags = 0;
hdr->msg_len = 0;
}
} }
/* ================================================== */ /* ================================================== */
@ -371,6 +156,10 @@ NIO_Initialise(int family)
assert(!initialised); assert(!initialised);
initialised = 1; initialised = 1;
#ifdef PRIVOPS_BINDSOCKET
SCK_SetPrivBind(PRV_BindSocket);
#endif
#ifdef HAVE_LINUX_TIMESTAMPING #ifdef HAVE_LINUX_TIMESTAMPING
NIO_Linux_Initialise(); NIO_Linux_Initialise();
#else #else
@ -381,12 +170,6 @@ NIO_Initialise(int family)
} }
#endif #endif
recv_messages = ARR_CreateInstance(sizeof (struct Message));
ARR_SetSize(recv_messages, MAX_RECV_MESSAGES);
recv_headers = ARR_CreateInstance(sizeof (struct MessageHeader));
ARR_SetSize(recv_headers, MAX_RECV_MESSAGES);
prepare_buffers(MAX_RECV_MESSAGES);
server_port = CNF_GetNTPPort(); server_port = CNF_GetNTPPort();
client_port = CNF_GetAcquisitionPort(); client_port = CNF_GetAcquisitionPort();
@ -399,47 +182,38 @@ NIO_Initialise(int family)
client_port == server_port); client_port == server_port);
server_sock_fd4 = INVALID_SOCK_FD; server_sock_fd4 = INVALID_SOCK_FD;
client_sock_fd4 = INVALID_SOCK_FD;
server_sock_ref4 = 0;
#ifdef FEAT_IPV6
server_sock_fd6 = INVALID_SOCK_FD; server_sock_fd6 = INVALID_SOCK_FD;
client_sock_fd4 = INVALID_SOCK_FD;
client_sock_fd6 = INVALID_SOCK_FD; client_sock_fd6 = INVALID_SOCK_FD;
server_sock_ref4 = 0;
server_sock_ref6 = 0; server_sock_ref6 = 0;
#endif
if (family == IPADDR_UNSPEC || family == IPADDR_INET4) { if (family == IPADDR_UNSPEC || family == IPADDR_INET4) {
if (permanent_server_sockets && server_port) if (permanent_server_sockets && server_port)
server_sock_fd4 = prepare_socket(AF_INET, server_port, 0); server_sock_fd4 = open_socket(IPADDR_INET4, server_port, 0, NULL);
if (!separate_client_sockets) { if (!separate_client_sockets) {
if (client_port != server_port || !server_port) if (client_port != server_port || !server_port)
client_sock_fd4 = prepare_socket(AF_INET, client_port, 1); client_sock_fd4 = open_socket(IPADDR_INET4, client_port, 1, NULL);
else else
client_sock_fd4 = server_sock_fd4; client_sock_fd4 = server_sock_fd4;
} }
} }
#ifdef FEAT_IPV6
if (family == IPADDR_UNSPEC || family == IPADDR_INET6) { if (family == IPADDR_UNSPEC || family == IPADDR_INET6) {
if (permanent_server_sockets && server_port) if (permanent_server_sockets && server_port)
server_sock_fd6 = prepare_socket(AF_INET6, server_port, 0); server_sock_fd6 = open_socket(IPADDR_INET6, server_port, 0, NULL);
if (!separate_client_sockets) { if (!separate_client_sockets) {
if (client_port != server_port || !server_port) if (client_port != server_port || !server_port)
client_sock_fd6 = prepare_socket(AF_INET6, client_port, 1); client_sock_fd6 = open_socket(IPADDR_INET6, client_port, 1, NULL);
else else
client_sock_fd6 = server_sock_fd6; client_sock_fd6 = server_sock_fd6;
} }
} }
#endif
if ((server_port && server_sock_fd4 == INVALID_SOCK_FD && if ((server_port && permanent_server_sockets &&
permanent_server_sockets server_sock_fd4 == INVALID_SOCK_FD && server_sock_fd6 == INVALID_SOCK_FD) ||
#ifdef FEAT_IPV6 (!separate_client_sockets &&
&& server_sock_fd6 == INVALID_SOCK_FD client_sock_fd4 == INVALID_SOCK_FD && client_sock_fd6 == INVALID_SOCK_FD)) {
#endif
) || (!separate_client_sockets && client_sock_fd4 == INVALID_SOCK_FD
#ifdef FEAT_IPV6
&& client_sock_fd6 == INVALID_SOCK_FD
#endif
)) {
LOG_FATAL("Could not open NTP sockets"); LOG_FATAL("Could not open NTP sockets");
} }
} }
@ -453,14 +227,11 @@ NIO_Finalise(void)
close_socket(client_sock_fd4); close_socket(client_sock_fd4);
close_socket(server_sock_fd4); close_socket(server_sock_fd4);
server_sock_fd4 = client_sock_fd4 = INVALID_SOCK_FD; server_sock_fd4 = client_sock_fd4 = INVALID_SOCK_FD;
#ifdef FEAT_IPV6
if (server_sock_fd6 != client_sock_fd6) if (server_sock_fd6 != client_sock_fd6)
close_socket(client_sock_fd6); close_socket(client_sock_fd6);
close_socket(server_sock_fd6); close_socket(server_sock_fd6);
server_sock_fd6 = client_sock_fd6 = INVALID_SOCK_FD; server_sock_fd6 = client_sock_fd6 = INVALID_SOCK_FD;
#endif
ARR_DestroyInstance(recv_headers);
ARR_DestroyInstance(recv_messages);
#ifdef HAVE_LINUX_TIMESTAMPING #ifdef HAVE_LINUX_TIMESTAMPING
NIO_Linux_Finalise(); NIO_Linux_Finalise();
@ -475,25 +246,13 @@ int
NIO_OpenClientSocket(NTP_Remote_Address *remote_addr) NIO_OpenClientSocket(NTP_Remote_Address *remote_addr)
{ {
if (separate_client_sockets) { if (separate_client_sockets) {
int sock_fd = prepare_separate_client_socket(remote_addr->ip_addr.family); return open_separate_client_socket(remote_addr);
if (sock_fd == INVALID_SOCK_FD)
return INVALID_SOCK_FD;
if (!connect_socket(sock_fd, remote_addr)) {
close_socket(sock_fd);
return INVALID_SOCK_FD;
}
return sock_fd;
} else { } else {
switch (remote_addr->ip_addr.family) { switch (remote_addr->ip_addr.family) {
case IPADDR_INET4: case IPADDR_INET4:
return client_sock_fd4; return client_sock_fd4;
#ifdef FEAT_IPV6
case IPADDR_INET6: case IPADDR_INET6:
return client_sock_fd6; return client_sock_fd6;
#endif
default: default:
return INVALID_SOCK_FD; return INVALID_SOCK_FD;
} }
@ -510,20 +269,18 @@ NIO_OpenServerSocket(NTP_Remote_Address *remote_addr)
if (permanent_server_sockets) if (permanent_server_sockets)
return server_sock_fd4; return server_sock_fd4;
if (server_sock_fd4 == INVALID_SOCK_FD) if (server_sock_fd4 == INVALID_SOCK_FD)
server_sock_fd4 = prepare_socket(AF_INET, CNF_GetNTPPort(), 0); server_sock_fd4 = open_socket(IPADDR_INET4, CNF_GetNTPPort(), 0, NULL);
if (server_sock_fd4 != INVALID_SOCK_FD) if (server_sock_fd4 != INVALID_SOCK_FD)
server_sock_ref4++; server_sock_ref4++;
return server_sock_fd4; return server_sock_fd4;
#ifdef FEAT_IPV6
case IPADDR_INET6: case IPADDR_INET6:
if (permanent_server_sockets) if (permanent_server_sockets)
return server_sock_fd6; return server_sock_fd6;
if (server_sock_fd6 == INVALID_SOCK_FD) if (server_sock_fd6 == INVALID_SOCK_FD)
server_sock_fd6 = prepare_socket(AF_INET6, CNF_GetNTPPort(), 0); server_sock_fd6 = open_socket(IPADDR_INET6, CNF_GetNTPPort(), 0, NULL);
if (server_sock_fd6 != INVALID_SOCK_FD) if (server_sock_fd6 != INVALID_SOCK_FD)
server_sock_ref6++; server_sock_ref6++;
return server_sock_fd6; return server_sock_fd6;
#endif
default: default:
return INVALID_SOCK_FD; return INVALID_SOCK_FD;
} }
@ -551,16 +308,12 @@ NIO_CloseServerSocket(int sock_fd)
close_socket(server_sock_fd4); close_socket(server_sock_fd4);
server_sock_fd4 = INVALID_SOCK_FD; server_sock_fd4 = INVALID_SOCK_FD;
} }
} } else if (sock_fd == server_sock_fd6) {
#ifdef FEAT_IPV6
else if (sock_fd == server_sock_fd6) {
if (--server_sock_ref6 <= 0) { if (--server_sock_ref6 <= 0) {
close_socket(server_sock_fd6); close_socket(server_sock_fd6);
server_sock_fd6 = INVALID_SOCK_FD; server_sock_fd6 = INVALID_SOCK_FD;
} }
} } else {
#endif
else {
assert(0); assert(0);
} }
} }
@ -571,11 +324,7 @@ int
NIO_IsServerSocket(int sock_fd) NIO_IsServerSocket(int sock_fd)
{ {
return sock_fd != INVALID_SOCK_FD && return sock_fd != INVALID_SOCK_FD &&
(sock_fd == server_sock_fd4 (sock_fd == server_sock_fd4 || sock_fd == server_sock_fd6);
#ifdef FEAT_IPV6
|| sock_fd == server_sock_fd6
#endif
);
} }
/* ================================================== */ /* ================================================== */
@ -583,132 +332,61 @@ NIO_IsServerSocket(int sock_fd)
int int
NIO_IsServerConnectable(NTP_Remote_Address *remote_addr) NIO_IsServerConnectable(NTP_Remote_Address *remote_addr)
{ {
int sock_fd, r; int sock_fd;
sock_fd = prepare_separate_client_socket(remote_addr->ip_addr.family); sock_fd = open_separate_client_socket(remote_addr);
if (sock_fd == INVALID_SOCK_FD) if (sock_fd == INVALID_SOCK_FD)
return 0; return 0;
r = connect_socket(sock_fd, remote_addr);
close_socket(sock_fd); close_socket(sock_fd);
return r; return 1;
} }
/* ================================================== */ /* ================================================== */
static void static void
process_message(struct msghdr *hdr, int length, int sock_fd) process_message(SCK_Message *message, int sock_fd, int event)
{ {
NTP_Remote_Address remote_addr;
NTP_Local_Address local_addr; NTP_Local_Address local_addr;
NTP_Local_Timestamp local_ts; NTP_Local_Timestamp local_ts;
struct timespec sched_ts; struct timespec sched_ts;
struct cmsghdr *cmsg;
SCH_GetLastEventTime(&local_ts.ts, &local_ts.err, NULL); SCH_GetLastEventTime(&local_ts.ts, &local_ts.err, NULL);
local_ts.source = NTP_TS_DAEMON; local_ts.source = NTP_TS_DAEMON;
sched_ts = local_ts.ts; sched_ts = local_ts.ts;
if (hdr->msg_namelen > sizeof (union sockaddr_in46)) { if (message->addr_type != SCK_ADDR_IP) {
DEBUG_LOG("Truncated source address"); DEBUG_LOG("Unexpected address type");
return; return;
} }
if (hdr->msg_namelen >= sizeof (((struct sockaddr *)hdr->msg_name)->sa_family)) { local_addr.ip_addr = message->local_addr.ip;
UTI_SockaddrToIPAndPort((struct sockaddr *)hdr->msg_name, local_addr.if_index = message->if_index;;
&remote_addr.ip_addr, &remote_addr.port);
} else {
remote_addr.ip_addr.family = IPADDR_UNSPEC;
remote_addr.port = 0;
}
local_addr.ip_addr.family = IPADDR_UNSPEC;
local_addr.if_index = INVALID_IF_INDEX;
local_addr.sock_fd = sock_fd; local_addr.sock_fd = sock_fd;
if (hdr->msg_flags & MSG_TRUNC) {
DEBUG_LOG("Received truncated message from %s:%d",
UTI_IPToString(&remote_addr.ip_addr), remote_addr.port);
return;
}
if (hdr->msg_flags & MSG_CTRUNC) {
DEBUG_LOG("Truncated control message");
/* Continue */
}
for (cmsg = CMSG_FIRSTHDR(hdr); cmsg; cmsg = CMSG_NXTHDR(hdr, cmsg)) {
#ifdef HAVE_IN_PKTINFO
if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) {
struct in_pktinfo ipi;
memcpy(&ipi, CMSG_DATA(cmsg), sizeof(ipi));
local_addr.ip_addr.addr.in4 = ntohl(ipi.ipi_addr.s_addr);
local_addr.ip_addr.family = IPADDR_INET4;
local_addr.if_index = ipi.ipi_ifindex;
}
#elif defined(IP_RECVDSTADDR)
if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_RECVDSTADDR) {
struct in_addr addr;
memcpy(&addr, CMSG_DATA(cmsg), sizeof (addr));
local_addr.ip_addr.addr.in4 = ntohl(addr.s_addr);
local_addr.ip_addr.family = IPADDR_INET4;
}
#endif
#ifdef HAVE_IN6_PKTINFO
if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) {
struct in6_pktinfo ipi;
memcpy(&ipi, CMSG_DATA(cmsg), sizeof(ipi));
memcpy(&local_addr.ip_addr.addr.in6, &ipi.ipi6_addr.s6_addr,
sizeof (local_addr.ip_addr.addr.in6));
local_addr.ip_addr.family = IPADDR_INET6;
local_addr.if_index = ipi.ipi6_ifindex;
}
#endif
#ifdef SCM_TIMESTAMP
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMP) {
struct timeval tv;
struct timespec ts;
memcpy(&tv, CMSG_DATA(cmsg), sizeof(tv));
UTI_TimevalToTimespec(&tv, &ts);
LCL_CookTime(&ts, &local_ts.ts, &local_ts.err);
local_ts.source = NTP_TS_KERNEL;
}
#endif
#ifdef SCM_TIMESTAMPNS
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPNS) {
struct timespec ts;
memcpy(&ts, CMSG_DATA(cmsg), sizeof (ts));
LCL_CookTime(&ts, &local_ts.ts, &local_ts.err);
local_ts.source = NTP_TS_KERNEL;
}
#endif
}
#ifdef HAVE_LINUX_TIMESTAMPING #ifdef HAVE_LINUX_TIMESTAMPING
if (NIO_Linux_ProcessMessage(&remote_addr, &local_addr, &local_ts, hdr, length)) if (NIO_Linux_ProcessMessage(message, &local_addr, &local_ts, event))
return; 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 #endif
DEBUG_LOG("Received %d bytes from %s:%d to %s fd=%d if=%d tss=%u delay=%.9f", if (local_ts.source != NTP_TS_DAEMON)
length, UTI_IPToString(&remote_addr.ip_addr), remote_addr.port, DEBUG_LOG("Updated RX timestamp delay=%.9f tss=%u",
UTI_IPToString(&local_addr.ip_addr), local_addr.sock_fd, local_addr.if_index, UTI_DiffTimespecsToDouble(&sched_ts, &local_ts.ts), local_ts.source);
local_ts.source, UTI_DiffTimespecsToDouble(&sched_ts, &local_ts.ts));
/* Just ignore the packet if it's not of a recognized length */ /* Just ignore the packet if it's not of a recognized length */
if (length < NTP_NORMAL_PACKET_LENGTH || length > sizeof (NTP_Receive_Buffer)) if (message->length < NTP_NORMAL_PACKET_LENGTH ||
message->length > sizeof (NTP_Receive_Buffer)) {
DEBUG_LOG("Unexpected length");
return; return;
}
NSR_ProcessRx(&remote_addr, &local_addr, &local_ts, NSR_ProcessRx(&message->remote_addr.ip, &local_addr, &local_ts, message->data, message->length);
(NTP_Packet *)hdr->msg_iov[0].iov_base, length);
} }
/* ================================================== */ /* ================================================== */
@ -719,65 +397,28 @@ read_from_socket(int sock_fd, int event, void *anything)
/* This should only be called when there is something /* This should only be called when there is something
to read, otherwise it may block */ to read, otherwise it may block */
struct MessageHeader *hdr; SCK_Message messages[SCK_MAX_RECV_MESSAGES];
unsigned int i, n; int i, received, flags = 0;
int status, flags = 0;
#ifdef HAVE_LINUX_TIMESTAMPING #ifdef HAVE_LINUX_TIMESTAMPING
if (NIO_Linux_ProcessEvent(sock_fd, event)) if (NIO_Linux_ProcessEvent(sock_fd, event))
return; return;
#endif #endif
hdr = ARR_GetElements(recv_headers);
n = ARR_GetSize(recv_headers);
assert(n >= 1);
if (event == SCH_FILE_EXCEPTION) { if (event == SCH_FILE_EXCEPTION) {
#ifdef HAVE_LINUX_TIMESTAMPING #ifdef HAVE_LINUX_TIMESTAMPING
flags |= MSG_ERRQUEUE; flags |= SCK_FLAG_MSG_ERRQUEUE;
#else #else
assert(0); assert(0);
#endif #endif
} }
#ifdef HAVE_RECVMMSG received = SCK_ReceiveMessages(sock_fd, messages, SCK_MAX_RECV_MESSAGES, flags);
status = recvmmsg(sock_fd, hdr, n, flags | MSG_DONTWAIT, NULL); if (received <= 0)
if (status >= 0)
n = status;
#else
n = 1;
status = recvmsg(sock_fd, &hdr[0].msg_hdr, flags);
if (status >= 0)
hdr[0].msg_len = status;
#endif
if (status < 0) {
#ifdef HAVE_LINUX_TIMESTAMPING
/* If reading from the error queue failed, the exception should be
for a socket error. Clear the error to avoid a busy loop. */
if (flags & MSG_ERRQUEUE) {
int error = 0;
socklen_t len = sizeof (error);
if (getsockopt(sock_fd, SOL_SOCKET, SO_ERROR, &error, &len))
DEBUG_LOG("Could not get SO_ERROR");
if (error)
errno = error;
}
#endif
DEBUG_LOG("Could not receive from fd %d : %s", sock_fd,
strerror(errno));
return; return;
}
for (i = 0; i < n; i++) { for (i = 0; i < received; i++)
hdr = ARR_GetElement(recv_headers, i); process_message(&messages[i], sock_fd, event);
process_message(&hdr->msg_hdr, hdr->msg_len, sock_fd);
}
/* Restore the buffers to their original state */
prepare_buffers(n);
} }
/* ================================================== */ /* ================================================== */
@ -787,12 +428,7 @@ int
NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr, NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr,
NTP_Local_Address *local_addr, int length, int process_tx) NTP_Local_Address *local_addr, int length, int process_tx)
{ {
union sockaddr_in46 remote; SCK_Message message;
struct msghdr msg;
struct iovec iov;
struct cmsghdr *cmsg, cmsgbuf[CMSGBUF_SIZE / sizeof (struct cmsghdr)];
int cmsglen;
socklen_t addrlen = 0;
assert(initialised); assert(initialised);
@ -802,108 +438,34 @@ NIO_SendPacket(NTP_Packet *packet, NTP_Remote_Address *remote_addr,
return 0; return 0;
} }
/* Don't set address with connected socket */ SCK_InitMessage(&message, SCK_ADDR_IP);
message.data = packet;
message.length = length;
/* Specify remote address if the socket is not connected */
if (NIO_IsServerSocket(local_addr->sock_fd) || !separate_client_sockets) { if (NIO_IsServerSocket(local_addr->sock_fd) || !separate_client_sockets) {
addrlen = UTI_IPAndPortToSockaddr(&remote_addr->ip_addr, remote_addr->port, message.remote_addr.ip.ip_addr = remote_addr->ip_addr;
&remote.u); message.remote_addr.ip.port = remote_addr->port;
if (!addrlen)
return 0;
} }
if (addrlen) { message.if_index = local_addr->if_index;
msg.msg_name = &remote.u; message.local_addr.ip = local_addr->ip_addr;
msg.msg_namelen = addrlen;
} else {
msg.msg_name = NULL;
msg.msg_namelen = 0;
}
iov.iov_base = packet; #if !defined(HAVE_IN_PKTINFO) && defined(IP_SENDSRCADDR)
iov.iov_len = length; /* On FreeBSD a local IPv4 address cannot be specified on bound socket */
msg.msg_iov = &iov; if (message.local_addr.ip.family == IPADDR_INET4 &&
msg.msg_iovlen = 1; (local_addr->sock_fd != server_sock_fd4 || bound_server_sock_fd4))
msg.msg_control = cmsgbuf; message.local_addr.ip.family = IPADDR_UNSPEC;
msg.msg_controllen = sizeof(cmsgbuf);
msg.msg_flags = 0;
cmsglen = 0;
#ifdef HAVE_IN_PKTINFO
if (local_addr->ip_addr.family == IPADDR_INET4) {
struct in_pktinfo *ipi;
cmsg = CMSG_FIRSTHDR(&msg);
memset(cmsg, 0, CMSG_SPACE(sizeof(struct in_pktinfo)));
cmsglen += CMSG_SPACE(sizeof(struct in_pktinfo));
cmsg->cmsg_level = IPPROTO_IP;
cmsg->cmsg_type = IP_PKTINFO;
cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
ipi = (struct in_pktinfo *) CMSG_DATA(cmsg);
ipi->ipi_spec_dst.s_addr = htonl(local_addr->ip_addr.addr.in4);
if (local_addr->if_index != INVALID_IF_INDEX)
ipi->ipi_ifindex = local_addr->if_index;
}
#elif defined(IP_SENDSRCADDR)
/* Specify the IPv4 source address only if the socket is not bound */
if (local_addr->ip_addr.family == IPADDR_INET4 &&
local_addr->sock_fd == server_sock_fd4 && !bound_server_sock_fd4) {
struct in_addr *addr;
cmsg = CMSG_FIRSTHDR(&msg);
memset(cmsg, 0, CMSG_SPACE(sizeof (struct in_addr)));
cmsglen += CMSG_SPACE(sizeof (struct in_addr));
cmsg->cmsg_level = IPPROTO_IP;
cmsg->cmsg_type = IP_SENDSRCADDR;
cmsg->cmsg_len = CMSG_LEN(sizeof (struct in_addr));
addr = (struct in_addr *)CMSG_DATA(cmsg);
addr->s_addr = htonl(local_addr->ip_addr.addr.in4);
}
#endif
#ifdef HAVE_IN6_PKTINFO
if (local_addr->ip_addr.family == IPADDR_INET6) {
struct in6_pktinfo *ipi;
cmsg = CMSG_FIRSTHDR(&msg);
memset(cmsg, 0, CMSG_SPACE(sizeof(struct in6_pktinfo)));
cmsglen += CMSG_SPACE(sizeof(struct in6_pktinfo));
cmsg->cmsg_level = IPPROTO_IPV6;
cmsg->cmsg_type = IPV6_PKTINFO;
cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
ipi = (struct in6_pktinfo *) CMSG_DATA(cmsg);
memcpy(&ipi->ipi6_addr.s6_addr, &local_addr->ip_addr.addr.in6,
sizeof(ipi->ipi6_addr.s6_addr));
if (local_addr->if_index != INVALID_IF_INDEX)
ipi->ipi6_ifindex = local_addr->if_index;
}
#endif #endif
#ifdef HAVE_LINUX_TIMESTAMPING #ifdef HAVE_LINUX_TIMESTAMPING
if (process_tx) if (process_tx)
cmsglen = NIO_Linux_RequestTxTimestamp(&msg, cmsglen, local_addr->sock_fd); NIO_Linux_RequestTxTimestamp(&message, local_addr->sock_fd);
#endif #endif
msg.msg_controllen = cmsglen; if (!SCK_SendMessage(local_addr->sock_fd, &message, 0))
/* This is apparently required on some systems */
if (!cmsglen)
msg.msg_control = NULL;
if (sendmsg(local_addr->sock_fd, &msg, 0) < 0) {
DEBUG_LOG("Could not send to %s:%d from %s fd %d : %s",
UTI_IPToString(&remote_addr->ip_addr), remote_addr->port,
UTI_IPToString(&local_addr->ip_addr), local_addr->sock_fd,
strerror(errno));
return 0; return 0;
}
DEBUG_LOG("Sent %d bytes to %s:%d from %s fd %d", length,
UTI_IPToString(&remote_addr->ip_addr), remote_addr->port,
UTI_IPToString(&local_addr->ip_addr), local_addr->sock_fd);
return 1; return 1;
} }

View file

@ -29,7 +29,6 @@
#include "sysincl.h" #include "sysincl.h"
#include <ifaddrs.h> #include <ifaddrs.h>
#include <linux/errqueue.h>
#include <linux/ethtool.h> #include <linux/ethtool.h>
#include <linux/net_tstamp.h> #include <linux/net_tstamp.h>
#include <linux/sockios.h> #include <linux/sockios.h>
@ -45,17 +44,10 @@
#include "ntp_io_linux.h" #include "ntp_io_linux.h"
#include "ntp_sources.h" #include "ntp_sources.h"
#include "sched.h" #include "sched.h"
#include "socket.h"
#include "sys_linux.h" #include "sys_linux.h"
#include "util.h" #include "util.h"
union sockaddr_in46 {
struct sockaddr_in in4;
#ifdef FEAT_IPV6
struct sockaddr_in6 in6;
#endif
struct sockaddr u;
};
struct Interface { struct Interface {
char name[IF_NAMESIZE]; char name[IF_NAMESIZE];
int if_index; int if_index;
@ -133,7 +125,7 @@ add_interface(CNF_HwTsInterface *conf_iface)
return 1; return 1;
} }
sock_fd = socket(AF_INET, SOCK_DGRAM, 0); sock_fd = SCK_OpenUdpSocket(NULL, NULL, 0);
if (sock_fd < 0) if (sock_fd < 0)
return 0; return 0;
@ -142,13 +134,13 @@ add_interface(CNF_HwTsInterface *conf_iface)
if (snprintf(req.ifr_name, sizeof (req.ifr_name), "%s", conf_iface->name) >= if (snprintf(req.ifr_name, sizeof (req.ifr_name), "%s", conf_iface->name) >=
sizeof (req.ifr_name)) { sizeof (req.ifr_name)) {
close(sock_fd); SCK_CloseSocket(sock_fd);
return 0; return 0;
} }
if (ioctl(sock_fd, SIOCGIFINDEX, &req)) { if (ioctl(sock_fd, SIOCGIFINDEX, &req)) {
DEBUG_LOG("ioctl(%s) failed : %s", "SIOCGIFINDEX", strerror(errno)); DEBUG_LOG("ioctl(%s) failed : %s", "SIOCGIFINDEX", strerror(errno));
close(sock_fd); SCK_CloseSocket(sock_fd);
return 0; return 0;
} }
@ -159,7 +151,7 @@ add_interface(CNF_HwTsInterface *conf_iface)
if (ioctl(sock_fd, SIOCETHTOOL, &req)) { if (ioctl(sock_fd, SIOCETHTOOL, &req)) {
DEBUG_LOG("ioctl(%s) failed : %s", "SIOCETHTOOL", strerror(errno)); DEBUG_LOG("ioctl(%s) failed : %s", "SIOCETHTOOL", strerror(errno));
close(sock_fd); SCK_CloseSocket(sock_fd);
return 0; return 0;
} }
@ -167,13 +159,13 @@ add_interface(CNF_HwTsInterface *conf_iface)
SOF_TIMESTAMPING_RAW_HARDWARE; SOF_TIMESTAMPING_RAW_HARDWARE;
if ((ts_info.so_timestamping & req_hwts_flags) != req_hwts_flags) { if ((ts_info.so_timestamping & req_hwts_flags) != req_hwts_flags) {
DEBUG_LOG("HW timestamping not supported on %s", req.ifr_name); DEBUG_LOG("HW timestamping not supported on %s", req.ifr_name);
close(sock_fd); SCK_CloseSocket(sock_fd);
return 0; return 0;
} }
if (ts_info.phc_index < 0) { if (ts_info.phc_index < 0) {
DEBUG_LOG("PHC missing on %s", req.ifr_name); DEBUG_LOG("PHC missing on %s", req.ifr_name);
close(sock_fd); SCK_CloseSocket(sock_fd);
return 0; return 0;
} }
@ -219,12 +211,12 @@ add_interface(CNF_HwTsInterface *conf_iface)
ts_config.tx_type != HWTSTAMP_TX_ON || ts_config.rx_filter != rx_filter) ts_config.tx_type != HWTSTAMP_TX_ON || ts_config.rx_filter != rx_filter)
#endif #endif
{ {
close(sock_fd); SCK_CloseSocket(sock_fd);
return 0; return 0;
} }
} }
close(sock_fd); SCK_CloseSocket(sock_fd);
phc_fd = SYS_Linux_OpenPHC(NULL, ts_info.phc_index); phc_fd = SYS_Linux_OpenPHC(NULL, ts_info.phc_index);
if (phc_fd < 0) if (phc_fd < 0)
@ -293,7 +285,7 @@ update_interface_speed(struct Interface *iface)
struct ifreq req; struct ifreq req;
int sock_fd, link_speed; int sock_fd, link_speed;
sock_fd = socket(AF_INET, SOCK_DGRAM, 0); sock_fd = SCK_OpenUdpSocket(NULL, NULL, 0);
if (sock_fd < 0) if (sock_fd < 0)
return; return;
@ -306,11 +298,11 @@ update_interface_speed(struct Interface *iface)
if (ioctl(sock_fd, SIOCETHTOOL, &req)) { if (ioctl(sock_fd, SIOCETHTOOL, &req)) {
DEBUG_LOG("ioctl(%s) failed : %s", "SIOCETHTOOL", strerror(errno)); DEBUG_LOG("ioctl(%s) failed : %s", "SIOCETHTOOL", strerror(errno));
close(sock_fd); SCK_CloseSocket(sock_fd);
return; return;
} }
close(sock_fd); SCK_CloseSocket(sock_fd);
link_speed = ethtool_cmd_speed(&cmd); link_speed = ethtool_cmd_speed(&cmd);
@ -328,17 +320,17 @@ check_timestamping_option(int option)
{ {
int sock_fd; int sock_fd;
sock_fd = socket(AF_INET, SOCK_DGRAM, 0); sock_fd = SCK_OpenUdpSocket(NULL, NULL, 0);
if (sock_fd < 0) if (sock_fd < 0)
return 0; return 0;
if (setsockopt(sock_fd, SOL_SOCKET, SO_TIMESTAMPING, &option, sizeof (option)) < 0) { if (setsockopt(sock_fd, SOL_SOCKET, SO_TIMESTAMPING, &option, sizeof (option)) < 0) {
DEBUG_LOG("Could not enable timestamping option %x", (unsigned int)option); DEBUG_LOG("Could not enable timestamping option %x", (unsigned int)option);
close(sock_fd); SCK_CloseSocket(sock_fd);
return 0; return 0;
} }
close(sock_fd); SCK_CloseSocket(sock_fd);
return 1; return 1;
} }
#endif #endif
@ -350,19 +342,15 @@ open_dummy_socket(void)
{ {
int sock_fd, events = 0; int sock_fd, events = 0;
if ((sock_fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 sock_fd = SCK_OpenUdpSocket(NULL, NULL, 0);
#ifdef FEAT_IPV6 if (sock_fd < 0)
&& (sock_fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0
#endif
)
return INVALID_SOCK_FD; return INVALID_SOCK_FD;
if (!NIO_Linux_SetTimestampSocketOptions(sock_fd, 1, &events)) { if (!NIO_Linux_SetTimestampSocketOptions(sock_fd, 1, &events)) {
close(sock_fd); SCK_CloseSocket(sock_fd);
return INVALID_SOCK_FD; return INVALID_SOCK_FD;
} }
UTI_FdSetCloexec(sock_fd);
return sock_fd; return sock_fd;
} }
@ -432,7 +420,7 @@ NIO_Linux_Finalise(void)
unsigned int i; unsigned int i;
if (dummy_rxts_socket != INVALID_SOCK_FD) if (dummy_rxts_socket != INVALID_SOCK_FD)
close(dummy_rxts_socket); SCK_CloseSocket(dummy_rxts_socket);
for (i = 0; i < ARR_GetSize(interfaces); i++) { for (i = 0; i < ARR_GetSize(interfaces); i++) {
iface = ARR_GetElement(interfaces, i); iface = ARR_GetElement(interfaces, i);
@ -462,14 +450,12 @@ NIO_Linux_SetTimestampSocketOptions(int sock_fd, int client_only, int *events)
if (client_only || permanent_ts_options) if (client_only || permanent_ts_options)
flags |= ts_tx_flags; flags |= ts_tx_flags;
if (setsockopt(sock_fd, SOL_SOCKET, SO_SELECT_ERR_QUEUE, &val, sizeof (val)) < 0) { if (!SCK_SetIntOption(sock_fd, SOL_SOCKET, SO_SELECT_ERR_QUEUE, val)) {
LOG(LOGS_ERR, "Could not set %s socket option", "SO_SELECT_ERR_QUEUE");
ts_flags = 0; ts_flags = 0;
return 0; return 0;
} }
if (setsockopt(sock_fd, SOL_SOCKET, SO_TIMESTAMPING, &flags, sizeof (flags)) < 0) { if (!SCK_SetIntOption(sock_fd, SOL_SOCKET, SO_TIMESTAMPING, flags)) {
LOG(LOGS_ERR, "Could not set %s socket option", "SO_TIMESTAMPING");
ts_flags = 0; ts_flags = 0;
return 0; return 0;
} }
@ -633,7 +619,6 @@ static int
extract_udp_data(unsigned char *msg, NTP_Remote_Address *remote_addr, int len) extract_udp_data(unsigned char *msg, NTP_Remote_Address *remote_addr, int len)
{ {
unsigned char *msg_start = msg; unsigned char *msg_start = msg;
union sockaddr_in46 addr;
remote_addr->ip_addr.family = IPADDR_UNSPEC; remote_addr->ip_addr.family = IPADDR_UNSPEC;
remote_addr->port = 0; remote_addr->port = 0;
@ -656,19 +641,21 @@ extract_udp_data(unsigned char *msg, NTP_Remote_Address *remote_addr, int len)
/* Parse destination address and port from IPv4/IPv6 and UDP headers */ /* Parse destination address and port from IPv4/IPv6 and UDP headers */
if (len >= 20 && msg[0] >> 4 == 4) { if (len >= 20 && msg[0] >> 4 == 4) {
int ihl = (msg[0] & 0xf) * 4; int ihl = (msg[0] & 0xf) * 4;
uint32_t addr;
if (len < ihl + 8 || msg[9] != 17) if (len < ihl + 8 || msg[9] != 17)
return 0; return 0;
memcpy(&addr.in4.sin_addr.s_addr, msg + 16, sizeof (uint32_t)); memcpy(&addr, msg + 16, sizeof (addr));
addr.in4.sin_port = *(uint16_t *)(msg + ihl + 2); remote_addr->ip_addr.addr.in4 = ntohl(addr);
addr.in4.sin_family = AF_INET; remote_addr->port = ntohs(*(uint16_t *)(msg + ihl + 2));
remote_addr->ip_addr.family = IPADDR_INET4;
len -= ihl + 8, msg += ihl + 8; len -= ihl + 8, msg += ihl + 8;
#ifdef FEAT_IPV6 #ifdef FEAT_IPV6
} else if (len >= 48 && msg[0] >> 4 == 6) { } else if (len >= 48 && msg[0] >> 4 == 6) {
int eh_len, next_header = msg[6]; int eh_len, next_header = msg[6];
memcpy(&addr.in6.sin6_addr.s6_addr, msg + 24, 16); memcpy(&remote_addr->ip_addr.addr.in6, msg + 24, sizeof (remote_addr->ip_addr.addr.in6));
len -= 40, msg += 40; len -= 40, msg += 40;
/* Skip IPv6 extension headers if present */ /* Skip IPv6 extension headers if present */
@ -700,16 +687,14 @@ extract_udp_data(unsigned char *msg, NTP_Remote_Address *remote_addr, int len)
len -= eh_len, msg += eh_len; len -= eh_len, msg += eh_len;
} }
addr.in6.sin6_port = *(uint16_t *)(msg + 2); remote_addr->port = ntohs(*(uint16_t *)(msg + 2));
addr.in6.sin6_family = AF_INET6; remote_addr->ip_addr.family = IPADDR_INET6;
len -= 8, msg += 8; len -= 8, msg += 8;
#endif #endif
} else { } else {
return 0; return 0;
} }
UTI_SockaddrToIPAndPort(&addr.u, &remote_addr->ip_addr, &remote_addr->port);
/* Move the message to fix alignment of its fields */ /* Move the message to fix alignment of its fields */
if (len > 0) if (len > 0)
memmove(msg_start, msg, len); memmove(msg_start, msg, len);
@ -720,42 +705,25 @@ extract_udp_data(unsigned char *msg, NTP_Remote_Address *remote_addr, int len)
/* ================================================== */ /* ================================================== */
int int
NIO_Linux_ProcessMessage(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, NIO_Linux_ProcessMessage(SCK_Message *message, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *local_ts, struct msghdr *hdr, int length) NTP_Local_Timestamp *local_ts, int event)
{ {
struct Interface *iface; struct Interface *iface;
struct cmsghdr *cmsg;
int is_tx, ts_if_index, l2_length; int is_tx, ts_if_index, l2_length;
is_tx = hdr->msg_flags & MSG_ERRQUEUE; is_tx = event == SCH_FILE_EXCEPTION;
iface = NULL; iface = NULL;
ts_if_index = local_addr->if_index;
l2_length = 0;
for (cmsg = CMSG_FIRSTHDR(hdr); cmsg; cmsg = CMSG_NXTHDR(hdr, cmsg)) { ts_if_index = message->timestamp.if_index;
#ifdef HAVE_LINUX_TIMESTAMPING_OPT_PKTINFO if (ts_if_index == INVALID_IF_INDEX)
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPING_PKTINFO) { ts_if_index = message->if_index;
struct scm_ts_pktinfo ts_pktinfo; l2_length = message->timestamp.l2_length;
memcpy(&ts_pktinfo, CMSG_DATA(cmsg), sizeof (ts_pktinfo)); if (!UTI_IsZeroTimespec(&message->timestamp.hw)) {
ts_if_index = ts_pktinfo.if_index;
l2_length = ts_pktinfo.pkt_length;
DEBUG_LOG("Received HW timestamp info if=%d length=%d", ts_if_index, l2_length);
}
#endif
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPING) {
struct scm_timestamping ts3;
memcpy(&ts3, CMSG_DATA(cmsg), sizeof (ts3));
if (!UTI_IsZeroTimespec(&ts3.ts[2])) {
iface = get_interface(ts_if_index); iface = get_interface(ts_if_index);
if (iface) { if (iface) {
process_hw_timestamp(iface, &ts3.ts[2], local_ts, !is_tx ? length : 0, process_hw_timestamp(iface, &message->timestamp.hw, local_ts, !is_tx ? message->length : 0,
remote_addr->ip_addr.family, l2_length); message->remote_addr.ip.ip_addr.family, l2_length);
} else { } else {
DEBUG_LOG("HW clock not found for interface %d", ts_if_index); DEBUG_LOG("HW clock not found for interface %d", ts_if_index);
} }
@ -766,27 +734,11 @@ NIO_Linux_ProcessMessage(NTP_Remote_Address *remote_addr, NTP_Local_Address *loc
resume_socket(local_addr->sock_fd); resume_socket(local_addr->sock_fd);
} }
if (local_ts->source == NTP_TS_DAEMON && !UTI_IsZeroTimespec(&ts3.ts[0]) && if (local_ts->source == NTP_TS_DAEMON && !UTI_IsZeroTimespec(&message->timestamp.kernel) &&
(!is_tx || UTI_IsZeroTimespec(&ts3.ts[2]))) { (!is_tx || UTI_IsZeroTimespec(&message->timestamp.hw))) {
LCL_CookTime(&ts3.ts[0], &local_ts->ts, &local_ts->err); LCL_CookTime(&message->timestamp.kernel, &local_ts->ts, &local_ts->err);
local_ts->source = NTP_TS_KERNEL; local_ts->source = NTP_TS_KERNEL;
} }
}
if ((cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_RECVERR) ||
(cmsg->cmsg_level == SOL_IPV6 && cmsg->cmsg_type == IPV6_RECVERR)) {
struct sock_extended_err err;
memcpy(&err, CMSG_DATA(cmsg), sizeof (err));
if (err.ee_errno != ENOMSG || err.ee_info != SCM_TSTAMP_SND ||
err.ee_origin != SO_EE_ORIGIN_TIMESTAMPING) {
DEBUG_LOG("Unknown extended error");
/* Drop the message */
return 1;
}
}
}
/* If the kernel is slow with enabling RX timestamping, open a dummy /* If the kernel is slow with enabling RX timestamping, open a dummy
socket to keep the kernel RX timestamping permanently enabled */ socket to keep the kernel RX timestamping permanently enabled */
@ -803,19 +755,19 @@ NIO_Linux_ProcessMessage(NTP_Remote_Address *remote_addr, NTP_Local_Address *loc
/* The data from the error queue includes all layers up to UDP. We have to /* The data from the error queue includes all layers up to UDP. We have to
extract the UDP data and also the destination address with port as there extract the UDP data and also the destination address with port as there
currently doesn't seem to be a better way to get them both. */ currently doesn't seem to be a better way to get them both. */
l2_length = length; l2_length = message->length;
length = extract_udp_data(hdr->msg_iov[0].iov_base, remote_addr, length); message->length = extract_udp_data(message->data, &message->remote_addr.ip, message->length);
DEBUG_LOG("Received %d (%d) bytes from error queue for %s:%d fd=%d if=%d tss=%u", DEBUG_LOG("Extracted message for %s fd=%d len=%u",
l2_length, length, UTI_IPToString(&remote_addr->ip_addr), remote_addr->port, UTI_IPSockAddrToString(&message->remote_addr.ip),
local_addr->sock_fd, local_addr->if_index, local_ts->source); local_addr->sock_fd, message->length);
/* Update assumed position of UDP data at layer 2 for next received packet */ /* Update assumed position of UDP data at layer 2 for next received packet */
if (iface && length) { if (iface && message->length) {
if (remote_addr->ip_addr.family == IPADDR_INET4) if (message->remote_addr.ip.ip_addr.family == IPADDR_INET4)
iface->l2_udp4_ntp_start = l2_length - length; iface->l2_udp4_ntp_start = l2_length - message->length;
else if (remote_addr->ip_addr.family == IPADDR_INET6) else if (message->remote_addr.ip.ip_addr.family == IPADDR_INET6)
iface->l2_udp6_ntp_start = l2_length - length; iface->l2_udp6_ntp_start = l2_length - message->length;
} }
/* Drop the message if it has no timestamp or its processing failed */ /* Drop the message if it has no timestamp or its processing failed */
@ -824,24 +776,21 @@ NIO_Linux_ProcessMessage(NTP_Remote_Address *remote_addr, NTP_Local_Address *loc
return 1; return 1;
} }
if (length < NTP_NORMAL_PACKET_LENGTH) if (message->length < NTP_NORMAL_PACKET_LENGTH)
return 1; return 1;
NSR_ProcessTx(remote_addr, local_addr, local_ts, NSR_ProcessTx(&message->remote_addr.ip, local_addr, local_ts, message->data, message->length);
(NTP_Packet *)hdr->msg_iov[0].iov_base, length);
return 1; return 1;
} }
/* ================================================== */ /* ================================================== */
int void
NIO_Linux_RequestTxTimestamp(struct msghdr *msg, int cmsglen, int sock_fd) NIO_Linux_RequestTxTimestamp(SCK_Message *message, int sock_fd)
{ {
struct cmsghdr *cmsg;
if (!ts_flags) if (!ts_flags)
return cmsglen; return;
/* If a HW transmit timestamp is requested on a client socket, monitor /* If a HW transmit timestamp is requested on a client socket, monitor
events on the socket in order to avoid processing of a fast response events on the socket in order to avoid processing of a fast response
@ -851,27 +800,9 @@ NIO_Linux_RequestTxTimestamp(struct msghdr *msg, int cmsglen, int sock_fd)
/* Check if TX timestamping is disabled on this socket */ /* Check if TX timestamping is disabled on this socket */
if (permanent_ts_options || !NIO_IsServerSocket(sock_fd)) if (permanent_ts_options || !NIO_IsServerSocket(sock_fd))
return cmsglen; return;
/* Add control message that will enable TX timestamping for this message. message->timestamp.tx_flags = ts_tx_flags;
Don't use CMSG_NXTHDR as the one in glibc is buggy for creating new
control messages. */
cmsg = CMSG_FIRSTHDR(msg);
if (!cmsg || cmsglen + CMSG_SPACE(sizeof (ts_tx_flags)) > msg->msg_controllen)
return cmsglen;
cmsg = (struct cmsghdr *)((char *)cmsg + cmsglen);
memset(cmsg, 0, CMSG_SPACE(sizeof (ts_tx_flags)));
cmsglen += CMSG_SPACE(sizeof (ts_tx_flags));
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SO_TIMESTAMPING;
cmsg->cmsg_len = CMSG_LEN(sizeof (ts_tx_flags));
memcpy(CMSG_DATA(cmsg), &ts_tx_flags, sizeof (ts_tx_flags));
return cmsglen;
} }
/* ================================================== */ /* ================================================== */

View file

@ -27,6 +27,8 @@
#ifndef GOT_NTP_IO_LINUX_H #ifndef GOT_NTP_IO_LINUX_H
#define GOT_NTP_IO_LINUX_H #define GOT_NTP_IO_LINUX_H
#include "socket.h"
extern void NIO_Linux_Initialise(void); extern void NIO_Linux_Initialise(void);
extern void NIO_Linux_Finalise(void); extern void NIO_Linux_Finalise(void);
@ -35,10 +37,10 @@ extern int NIO_Linux_SetTimestampSocketOptions(int sock_fd, int client_only, int
extern int NIO_Linux_ProcessEvent(int sock_fd, int event); extern int NIO_Linux_ProcessEvent(int sock_fd, int event);
extern int NIO_Linux_ProcessMessage(NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, extern int NIO_Linux_ProcessMessage(SCK_Message *message, NTP_Local_Address *local_addr,
NTP_Local_Timestamp *local_ts, struct msghdr *hdr, int length); NTP_Local_Timestamp *local_ts, int event);
extern int NIO_Linux_RequestTxTimestamp(struct msghdr *msg, int cmsglen, int sock_fd); extern void NIO_Linux_RequestTxTimestamp(SCK_Message *message, int sock_fd);
extern void NIO_Linux_NotifySocketClosing(int sock_fd); extern void NIO_Linux_NotifySocketClosing(int sock_fd);

View file

@ -34,6 +34,7 @@
#include "ntp_io.h" #include "ntp_io.h"
#include "ntp_signd.h" #include "ntp_signd.h"
#include "sched.h" #include "sched.h"
#include "socket.h"
#include "util.h" #include "util.h"
/* Declarations per samba/source4/librpc/idl/ntp_signd.idl */ /* Declarations per samba/source4/librpc/idl/ntp_signd.idl */
@ -90,7 +91,7 @@ static ARR_Instance queue;
static unsigned int queue_head; static unsigned int queue_head;
static unsigned int queue_tail; static unsigned int queue_tail;
#define INVALID_SOCK_FD -1 #define INVALID_SOCK_FD (-6)
/* Unix domain socket connected to ntp_signd */ /* Unix domain socket connected to ntp_signd */
static int sock_fd; static int sock_fd;
@ -116,7 +117,7 @@ static void
close_socket(void) close_socket(void)
{ {
SCH_RemoveFileHandler(sock_fd); SCH_RemoveFileHandler(sock_fd);
close(sock_fd); SCK_CloseSocket(sock_fd);
sock_fd = INVALID_SOCK_FD; sock_fd = INVALID_SOCK_FD;
/* Empty the queue */ /* Empty the queue */
@ -128,35 +129,23 @@ close_socket(void)
static int static int
open_socket(void) open_socket(void)
{ {
struct sockaddr_un s; char path[1024];
if (sock_fd >= 0) if (sock_fd != INVALID_SOCK_FD)
return 1; return 1;
sock_fd = socket(AF_UNIX, SOCK_STREAM, 0); if (snprintf(path, sizeof (path), "%s/socket", CNF_GetNtpSigndSocket()) >= sizeof (path)) {
if (sock_fd < 0) {
DEBUG_LOG("Could not open signd socket : %s", strerror(errno));
return 0;
}
UTI_FdSetCloexec(sock_fd);
SCH_AddFileHandler(sock_fd, SCH_FILE_INPUT, read_write_socket, NULL);
s.sun_family = AF_UNIX;
if (snprintf(s.sun_path, sizeof (s.sun_path), "%s/socket",
CNF_GetNtpSigndSocket()) >= sizeof (s.sun_path)) {
DEBUG_LOG("signd socket path too long"); DEBUG_LOG("signd socket path too long");
close_socket();
return 0; return 0;
} }
if (connect(sock_fd, (struct sockaddr *)&s, sizeof (s)) < 0) { sock_fd = SCK_OpenUnixStreamSocket(path, NULL, 0);
DEBUG_LOG("Could not connect to signd : %s", strerror(errno)); if (sock_fd < 0) {
close_socket(); sock_fd = INVALID_SOCK_FD;
return 0; return 0;
} }
DEBUG_LOG("Connected to signd"); SCH_AddFileHandler(sock_fd, SCH_FILE_INPUT, read_write_socket, NULL);
return 1; return 1;
} }
@ -218,16 +207,14 @@ read_write_socket(int sock_fd, int event, void *anything)
if (!inst->sent) if (!inst->sent)
SCH_GetLastEventTime(NULL, NULL, &inst->request_ts); SCH_GetLastEventTime(NULL, NULL, &inst->request_ts);
s = send(sock_fd, (char *)&inst->request + inst->sent, s = SCK_Send(sock_fd, (char *)&inst->request + inst->sent,
inst->request_length - inst->sent, 0); inst->request_length - inst->sent, 0);
if (s < 0) { if (s < 0) {
DEBUG_LOG("signd socket error: %s", strerror(errno));
close_socket(); close_socket();
return; return;
} }
DEBUG_LOG("Sent %d bytes to signd", s);
inst->sent += s; inst->sent += s;
/* Try again later if the request is not complete yet */ /* Try again later if the request is not complete yet */
@ -246,20 +233,14 @@ read_write_socket(int sock_fd, int event, void *anything)
} }
assert(inst->received < sizeof (inst->response)); assert(inst->received < sizeof (inst->response));
s = recv(sock_fd, (char *)&inst->response + inst->received, s = SCK_Receive(sock_fd, (char *)&inst->response + inst->received,
sizeof (inst->response) - inst->received, 0); sizeof (inst->response) - inst->received, 0);
if (s <= 0) { if (s <= 0) {
if (s < 0)
DEBUG_LOG("signd socket error: %s", strerror(errno));
else
DEBUG_LOG("signd socket closed");
close_socket(); close_socket();
return; return;
} }
DEBUG_LOG("Received %d bytes from signd", s);
inst->received += s; inst->received += s;
if (inst->received < sizeof (inst->response.length)) if (inst->received < sizeof (inst->response.length))

View file

@ -27,8 +27,10 @@ acquisitionport 123"; do
if check_config_h 'FEAT_DEBUG 1'; then if check_config_h 'FEAT_DEBUG 1'; then
check_log_messages "HW clock samples" 190 200 || test_fail check_log_messages "HW clock samples" 190 200 || test_fail
check_log_messages "HW clock reset" 0 0 || test_fail check_log_messages "HW clock reset" 0 0 || test_fail
check_log_messages "Received.*tss=1" 1 1 || test_fail check_log_messages "Received message.*tss=KH" 195 200 || test_fail
check_log_messages "Received.*tss=2" 390 400 || test_fail check_log_messages "Received error.*message.*tss=KH" 195 200 || test_fail
check_log_messages "Updated RX timestamp.*tss=1" 1 1 || test_fail
check_log_messages "Updated RX timestamp.*tss=2" 195 200 || test_fail
check_log_messages "update_tx_timestamp.*Updated" 50 140 || test_fail check_log_messages "update_tx_timestamp.*Updated" 50 140 || test_fail
check_log_messages "update_tx_timestamp.*Unacceptable" 50 140 || test_fail check_log_messages "update_tx_timestamp.*Unacceptable" 50 140 || test_fail
fi fi