/* * Copyright (C) 2010 Miroslav Lichvar * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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, see . */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef SO_TIMESTAMPING #include #include #endif #include "protocol.h" #include "client_fuzz.c" /* first node in first subnet is 192.168.123.1 */ #define BASE_ADDR 0xc0a87b00 #define NETMASK 0xffffff00 #define NODE_ADDR(subnet, node) (BASE_ADDR + 0x100 * (subnet) + (node) + 1) #define BROADCAST_ADDR(subnet) (NODE_ADDR(subnet, 0) | 0xff) #define NODE_FROM_ADDR(addr) (((addr) & ~NETMASK) - 1) #define SUBNET_FROM_ADDR(addr) ((((addr) & NETMASK) - BASE_ADDR) / 0x100) #define PTP_PRIMARY_MCAST_ADDR 0xe0000181 /* 224.0.1.129 */ #define PTP_PDELAY_MCAST_ADDR 0xe000006b /* 224.0.0.107 */ #define REFCLK_FD 1000 #define REFCLK_ID ((~(clockid_t)REFCLK_FD << 3) | 3) #define REFCLK_PHC_INDEX 0 #define SYSCLK_FD 1001 #define SYSCLK_CLOCKID ((~(clockid_t)SYSCLK_FD << 3) | 3) #define SYSCLK_PHC_INDEX 1 #define MAX_SOCKETS 20 #define BASE_SOCKET_FD 100 #define BASE_SOCKET_DEFAULT_PORT 60000 #define MAX_TIMERS 40 #define BASE_TIMER_ID 0xC1230123 #define BASE_TIMER_FD 200 #define URANDOM_FILE (void *)0xD1230123 static FILE *(*_fopen)(const char *path, const char *mode); static size_t (*_fread)(void *ptr, size_t size, size_t nmemb, FILE *stream); static int (*_fileno)(FILE *stream); static int (*_fclose)(FILE *fp); static int (*_open)(const char *pathname, int flags); static int (*_close)(int fd); static int (*_socket)(int domain, int type, int protocol); static int (*_connect)(int sockfd, const struct sockaddr *addr, socklen_t addrlen); static ssize_t (*_recvmsg)(int sockfd, struct msghdr *msg, int flags); static ssize_t (*_send)(int sockfd, const void *buf, size_t len, int flags); static int (*_usleep)(useconds_t usec); static void (*_srandom)(unsigned int seed); static int (*_shmget)(key_t key, size_t size, int shmflg); static void *(*_shmat)(int shmid, const void *shmaddr, int shmflg); static unsigned int node; static int initialized = 0; static int clknetsim_fd; static int precision_hack = 1; static unsigned int random_seed = 0; static int recv_multiply = 1; static int timestamping = 1; enum { IFACE_NONE = 0, IFACE_LO, IFACE_ALL, IFACE_ETH0, }; struct ts_message { char data[MAX_PACKET_SIZE]; unsigned int len; unsigned int subnet; unsigned int to; unsigned int port; }; struct socket { int used; int type; int port; int iface; int remote_node; int remote_port; int broadcast; int pkt_info; int time_stamping; struct ts_message last_ts_msg; }; static struct socket sockets[MAX_SOCKETS]; static int subnets; static double real_time = 0.0; static double monotonic_time = 0.0; static double network_time = 0.0; static int local_time_valid = 0; static time_t system_time_offset = 1262304000; /* 2010-01-01 0:00 UTC */ #define TIMER_TYPE_SIGNAL 1 #define TIMER_TYPE_FD 2 struct timer { int used; int armed; int type; clockid_t clock_id; double timeout; double interval; }; static struct timer timers[MAX_TIMERS]; static timer_t itimer_real_id; #define SHM_KEY 0x4e545030 #define SHM_REFCLOCKS 4 static struct shmTime { int mode; int count; time_t clockTimeStampSec; int clockTimeStampUSec; time_t receiveTimeStampSec; int receiveTimeStampUSec; int leap; int precision; int nsamples; int valid; int clockTimeStampNSec; int receiveTimeStampNSec; int dummy[8]; } shm_time[SHM_REFCLOCKS]; static int shm_refclocks = 0; static double shm_refclock_time = 0.0; static struct Reply_getrefoffsets refclock_offsets; static int refclock_offsets_used = REPLY_GETREFOFFSETS_SIZE; static void make_request(int request_id, const void *request_data, int reqlen, void *reply, int replylen); __attribute__((constructor)) static void init(void) { struct Request_register req; struct Reply_register rep; struct sockaddr_un s = {AF_UNIX, "clknetsim.sock"}; const char *env; unsigned int connect_retries = 100; /* 10 seconds */ if (initialized) return; _fopen = (FILE *(*)(const char *path, const char *mode))dlsym(RTLD_NEXT, "fopen"); _fread = (size_t (*)(void *ptr, size_t size, size_t nmemb, FILE *stream))dlsym(RTLD_NEXT, "fread"); _fileno = (int (*)(FILE *stream))dlsym(RTLD_NEXT, "fileno"); _fclose = (int (*)(FILE *fp))dlsym(RTLD_NEXT, "fclose"); _open = (int (*)(const char *pathname, int flags))dlsym(RTLD_NEXT, "open"); _close = (int (*)(int fd))dlsym(RTLD_NEXT, "close"); _socket = (int (*)(int domain, int type, int protocol))dlsym(RTLD_NEXT, "socket"); _connect = (int (*)(int sockfd, const struct sockaddr *addr, socklen_t addrlen))dlsym(RTLD_NEXT, "connect"); _recvmsg = (ssize_t (*)(int sockfd, struct msghdr *msg, int flags))dlsym(RTLD_NEXT, "recvmsg"); _send = (ssize_t (*)(int sockfd, const void *buf, size_t len, int flags))dlsym(RTLD_NEXT, "send"); _usleep = (int (*)(useconds_t usec))dlsym(RTLD_NEXT, "usleep"); _srandom = (void (*)(unsigned int seed))dlsym(RTLD_NEXT, "srandom"); _shmget = (int (*)(key_t key, size_t size, int shmflg))dlsym(RTLD_NEXT, "shmget"); _shmat = (void *(*)(int shmid, const void *shmaddr, int shmflg))dlsym(RTLD_NEXT, "shmat"); env = getenv("CLKNETSIM_START_DATE"); if (env) system_time_offset = atol(env); env = getenv("CLKNETSIM_RANDOM_SEED"); if (env) random_seed = atoi(env); env = getenv("CLKNETSIM_RECV_MULTIPLY"); if (env) recv_multiply = atoi(env); env = getenv("CLKNETSIM_TIMESTAMPING"); if (env) timestamping = atoi(env); if (fuzz_init()) { node = 0; subnets = 1; initialized = 1; return; } env = getenv("CLKNETSIM_NODE"); if (!env) { fprintf(stderr, "clknetsim: CLKNETSIM_NODE variable not set.\n"); exit(1); } node = atoi(env) - 1; env = getenv("CLKNETSIM_SOCKET"); if (env) snprintf(s.sun_path, sizeof (s.sun_path), "%s", env); env = getenv("CLKNETSIM_CONNECT_TIMEOUT"); if (env) connect_retries = 10 * atoi(env); clknetsim_fd = _socket(AF_UNIX, SOCK_SEQPACKET, 0); assert(clknetsim_fd >= 0); while (_connect(clknetsim_fd, (struct sockaddr *)&s, sizeof (s)) < 0) { if (!--connect_retries) { fprintf(stderr, "clknetsim: could not connect to server.\n"); exit(1); } _usleep(100000); } /* this requires the node variable to be already set */ srandom(0); initialized = 1; req.node = node; make_request(REQ_REGISTER, &req, sizeof (req), &rep, sizeof (rep)); subnets = rep.subnets; } __attribute__((destructor)) static void fini(void) { if (initialized) make_request(REQ_DEREGISTER, NULL, 0, NULL, 0); } static void make_request(int request_id, const void *request_data, int reqlen, void *reply, int replylen) { struct Request_packet request; int sent, received = 0; assert(initialized); if (fuzz_mode) { fuzz_process_reply(request_id, request_data, reply, replylen); return; } request.header.request = request_id; request.header._pad = 0; assert(offsetof(struct Request_packet, data) + reqlen <= sizeof (request)); if (request_data) memcpy(&request.data, request_data, reqlen); reqlen += offsetof(struct Request_packet, data); if ((sent = _send(clknetsim_fd, &request, reqlen, 0)) <= 0 || (reply && (received = recv(clknetsim_fd, reply, replylen, 0)) <= 0)) { fprintf(stderr, "clknetsim: server connection closed.\n"); initialized = 0; exit(1); } assert(sent == reqlen); if (!reply) return; /* check reply length */ switch (request_id) { case REQ_RECV: /* reply with variable length */ assert(received >= offsetof(struct Reply_recv, data)); assert(offsetof(struct Reply_recv, data) + ((struct Reply_recv *)reply)->len <= received); break; default: assert(received == replylen); } } static void fetch_time(void) { struct Reply_gettime r; if (!local_time_valid) { make_request(REQ_GETTIME, NULL, 0, &r, sizeof (r)); real_time = r.real_time; monotonic_time = r.monotonic_time; network_time = r.network_time; local_time_valid = 1; } } static double get_real_time(void) { fetch_time(); return real_time; } static double get_monotonic_time(void) { fetch_time(); return monotonic_time; } static double get_refclock_offset(void) { if (refclock_offsets_used >= REPLY_GETREFOFFSETS_SIZE) { make_request(REQ_GETREFOFFSETS, NULL, 0, &refclock_offsets, sizeof (refclock_offsets)); refclock_offsets_used = 0; } return refclock_offsets.offsets[refclock_offsets_used++]; } static double get_refclock_time(void) { fetch_time(); return network_time - get_refclock_offset(); } static void settime(double time) { struct Request_settime req; req.time = time; make_request(REQ_SETTIME, &req, sizeof (req), NULL, 0); local_time_valid = 0; } static void fill_refclock_sample(void) { struct Reply_getrefsample r; double clock_time, receive_time, round_corr; int i; if (!shm_refclocks) return; make_request(REQ_GETREFSAMPLE, NULL, 0, &r, sizeof (r)); if (r.time == shm_refclock_time || !r.valid) return; shm_refclock_time = r.time; for (i = 0; i < shm_refclocks; i++) { if (shm_refclocks == 1) { clock_time = r.time - r.offset; receive_time = r.time; } else { clock_time = get_refclock_time(); receive_time = get_real_time(); } round_corr = (clock_time * 1e6 - floor(clock_time * 1e6) + 0.5) / 1e6; clock_time -= round_corr; receive_time -= round_corr; shm_time[i].count++; shm_time[i].clockTimeStampSec = floor(clock_time); shm_time[i].clockTimeStampUSec = (clock_time - shm_time[i].clockTimeStampSec) * 1e6; shm_time[i].clockTimeStampNSec = (clock_time - shm_time[i].clockTimeStampSec) * 1e9; shm_time[i].clockTimeStampSec += system_time_offset; shm_time[i].receiveTimeStampSec = floor(receive_time); shm_time[i].receiveTimeStampUSec = (receive_time - shm_time[i].receiveTimeStampSec) * 1e6; shm_time[i].receiveTimeStampNSec = (receive_time - shm_time[i].receiveTimeStampSec) * 1e9; shm_time[i].receiveTimeStampSec += system_time_offset; shm_time[i].leap = 0; shm_time[i].valid = 1; } } static int socket_in_subnet(int socket, int subnet) { switch (sockets[socket].iface) { case IFACE_LO: return 0; case IFACE_NONE: case IFACE_ALL: return 1; default: return sockets[socket].iface - IFACE_ETH0 == subnet; } } static void get_target(int socket, uint32_t addr, unsigned int *subnet, unsigned int *node) { if (addr == PTP_PRIMARY_MCAST_ADDR || addr == PTP_PDELAY_MCAST_ADDR) { assert(sockets[socket].iface >= IFACE_ETH0); *subnet = sockets[socket].iface - IFACE_ETH0; *node = -1; /* multicast as broadcast */ } else { *subnet = SUBNET_FROM_ADDR(addr); assert(*subnet >= 0 && *subnet < subnets); assert(socket_in_subnet(socket, *subnet)); if (addr == BROADCAST_ADDR(*subnet)) *node = -1; /* broadcast */ else *node = NODE_FROM_ADDR(addr); } } static int get_network_from_iface(const char *iface) { if (strncmp(iface, "eth", 3)) return -1; return atoi(iface + 3); } static int get_free_socket(void) { int i; for (i = 0; i < MAX_SOCKETS; i++) { if (!sockets[i].used) return i; } return -1; } static int get_socket_from_fd(int fd) { int s = fd - BASE_SOCKET_FD; if (s >= 0 && s < MAX_SOCKETS && sockets[s].used) return s; return -1; } static int get_socket_fd(int s) { return s + BASE_SOCKET_FD; } static int find_recv_socket(int subnet, int port, int broadcast) { int i, s = -1; for (i = 0; i < MAX_SOCKETS; i++) { if (!sockets[i].used || !socket_in_subnet(i, subnet) || sockets[i].type != SOCK_DGRAM || (port && sockets[i].port != port)) continue; if (s < 0 || sockets[s].iface < sockets[i].iface || (broadcast && sockets[i].broadcast) || (!broadcast && sockets[s].broadcast && !sockets[i].broadcast)) s = i; } return s; } static int get_free_timer(void) { int i; for (i = 0; i < MAX_TIMERS; i++) { if (!timers[i].used) return i; } return -1; } static timer_t get_timerid(int timer) { return (timer_t)((long)timer + BASE_TIMER_ID); } static int get_timer_from_id(timer_t timerid) { int t = (long)timerid - BASE_TIMER_ID; if (t >= 0 && t < MAX_TIMERS && timers[t].used) return t; return -1; } static int get_timerfd(int timer) { return timer + BASE_TIMER_FD; } static int get_timer_from_fd(int fd) { int t = fd - BASE_TIMER_FD; if (t >= 0 && t < MAX_TIMERS && timers[t].used) return t; return -1; } static int get_first_timer(fd_set *timerfds) { int i, r = -1; for (i = 0; i < MAX_TIMERS; i++) { if (!timers[i].used || !timers[i].armed) continue; if (timers[i].type == TIMER_TYPE_FD && !(timerfds && FD_ISSET(get_timerfd(i), timerfds))) continue; if (r < 0 || timers[r].timeout > timers[i].timeout) r = i; } return r; } static void rearm_timer(int timer) { assert(timers[timer].armed); if (timers[timer].interval > 0.0) timers[timer].timeout += timers[timer].interval; else timers[timer].armed = 0; } static void time_to_timeval(double d, struct timeval *tv) { tv->tv_sec = floor(d); tv->tv_usec = (d - tv->tv_sec) * 1e6; } static void time_to_timespec(double d, struct timespec *tp) { tp->tv_sec = floor(d); tp->tv_nsec = (d - tp->tv_sec) * 1e9; } static double timeval_to_time(const struct timeval *tv, time_t offset) { return tv->tv_sec + offset + tv->tv_usec / 1e6; } static double timespec_to_time(const struct timespec *tp, time_t offset) { return tp->tv_sec + offset + tp->tv_nsec / 1e9; } int gettimeofday(struct timeval *tv, struct timezone *tz) { double time; time = get_real_time() + 0.5e-6; time_to_timeval(time, tv); tv->tv_sec += system_time_offset; /* chrony clock precision routine hack */ if (precision_hack) tv->tv_usec += random() % 2; return 0; } int clock_gettime(clockid_t which_clock, struct timespec *tp) { double time; switch (which_clock) { case CLOCK_REALTIME: case SYSCLK_CLOCKID: time = get_real_time(); break; case CLOCK_MONOTONIC: time = get_monotonic_time(); break; case REFCLK_ID: time = get_refclock_time(); break; default: assert(0); } time += 0.5e-9; time_to_timespec(time, tp); if (which_clock == CLOCK_REALTIME || which_clock == REFCLK_ID) tp->tv_sec += system_time_offset; /* ntpd clock precision routine hack */ if (precision_hack) { static int x = 0; tp->tv_nsec += x++ * 101; } return 0; } time_t time(time_t *t) { time_t time; time = floor(get_real_time()); time += system_time_offset; if (t) *t = time; return time; } int settimeofday(const struct timeval *tv, const struct timezone *tz) { assert(tv); settime(timeval_to_time(tv, -system_time_offset)); return 0; } int clock_settime(clockid_t which_clock, const struct timespec *tp) { assert(tp && which_clock == CLOCK_REALTIME); settime(timespec_to_time(tp, -system_time_offset)); return 0; } int adjtimex(struct timex *buf) { struct Request_adjtimex req; struct Reply_adjtimex rep; if (buf->modes & ADJ_SETOFFSET) local_time_valid = 0; req.timex = *buf; make_request(REQ_ADJTIMEX, &req, sizeof (req), &rep, sizeof (rep)); *buf = rep.timex; if (rep.ret < 0) errno = EINVAL; return rep.ret; } int ntp_adjtime(struct timex *buf) { return adjtimex(buf); } int clock_adjtime(clockid_t id, struct timex *tx) { assert(id == CLOCK_REALTIME || id == SYSCLK_CLOCKID || id == REFCLK_ID); if (id == SYSCLK_CLOCKID) { /* allow large frequency adjustment by setting ticks */ long hz, base_tick, scaled_ppm_per_tick; int r; hz = sysconf(_SC_CLK_TCK); assert(hz > 0); base_tick = (1000000 + hz / 2) / hz; scaled_ppm_per_tick = 65536 * hz; if (tx->modes & ADJ_FREQUENCY && !(tx->modes & ADJ_TICK)) tx->tick = base_tick, tx->modes |= ADJ_TICK; tx->tick += tx->freq / scaled_ppm_per_tick; tx->freq = tx->freq % scaled_ppm_per_tick; r = adjtimex(tx); tx->freq += (tx->tick - base_tick) * scaled_ppm_per_tick; tx->tick = base_tick; return r; } else if (id == REFCLK_ID) { if (tx->modes) { errno = EINVAL; return -1; } memset(tx, 0, sizeof (*tx)); return 0; } return adjtimex(tx); } int adjtime(const struct timeval *delta, struct timeval *olddelta) { struct Request_adjtime req; struct Reply_adjtime rep; if (delta) req.tv = *delta; else time_to_timeval(0.0, &req.tv); make_request(REQ_ADJTIME, &req, sizeof (req), &rep, sizeof (rep)); if (olddelta) *olddelta = rep.tv; if (!delta) { req.tv = rep.tv; make_request(REQ_ADJTIME, &req, sizeof (req), &rep, sizeof (rep)); } return 0; } int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) { struct Request_select req; struct Reply_select rep; int i, timer, s, recv_fd = -1; double elapsed = 0.0; if (writefds) FD_ZERO(writefds); if (exceptfds) { /* chronyd waiting for TX timestamp from the error queue */ for (i = 0; i < nfds; i++) { if (!FD_ISSET(i, exceptfds) || get_socket_from_fd(i) < 0 || !sockets[get_socket_from_fd(i)].last_ts_msg.len) continue; if (readfds) FD_ZERO(readfds); FD_ZERO(exceptfds); FD_SET(i, exceptfds); return 1; } FD_ZERO(exceptfds); } req.read = 0; req._pad = 0; /* unknown reading fds are always ready (e.g. chronyd waiting for name resolving notification, or OpenSSL waiting for /dev/urandom) */ if (readfds) { for (i = 0; i < nfds; i++) { if (!FD_ISSET(i, readfds)) continue; if (get_socket_from_fd(i) < 0 && get_timer_from_fd(i) < 0) { FD_ZERO(readfds); FD_SET(i, readfds); return 1; } req.read = 1; } } timer = get_first_timer(readfds); assert((timeout && (timeout->tv_sec > 0 || timeout->tv_usec > 0)) || timer >= 0 || find_recv_socket(0, 0, 0) >= 0); fetch_time(); if (timeout) req.timeout = timeout->tv_sec + (timeout->tv_usec + 1) / 1e6; else req.timeout = 1e20; try_again: if (timer >= 0 && timers[timer].timeout <= monotonic_time) { /* avoid unnecessary requests */ rep.ret = REPLY_SELECT_TIMEOUT; } else { if (timer >= 0 && monotonic_time + req.timeout > timers[timer].timeout) req.timeout = timers[timer].timeout - monotonic_time; make_request(REQ_SELECT, &req, sizeof (req), &rep, sizeof (rep)); elapsed += rep.time.monotonic_time - monotonic_time; req.timeout -= rep.time.monotonic_time - monotonic_time; real_time = rep.time.real_time; monotonic_time = rep.time.monotonic_time; network_time = rep.time.network_time; local_time_valid = 1; fill_refclock_sample(); if (monotonic_time >= 0.1 || timer >= 0 || rep.ret != REPLY_SELECT_TIMEOUT) precision_hack = 0; } switch (rep.ret) { case REPLY_SELECT_TERMINATE: kill(getpid(), SIGTERM); errno = EINTR; return -1; case REPLY_SELECT_TIMEOUT: if (timer >= 0 && monotonic_time >= timers[timer].timeout) { rearm_timer(timer); switch (timers[timer].type) { case TIMER_TYPE_SIGNAL: kill(getpid(), SIGALRM); errno = EINTR; return -1; case TIMER_TYPE_FD: recv_fd = get_timerfd(timer); break; default: assert(0); } } else recv_fd = 0; break; case REPLY_SELECT_NORMAL: case REPLY_SELECT_BROADCAST: s = find_recv_socket(rep.subnet, rep.dst_port, rep.ret == REPLY_SELECT_BROADCAST); recv_fd = s >= 0 ? get_socket_fd(s) : 0; /* fetch and drop the packet if no fd is waiting for it */ if (!readfds || !recv_fd || !FD_ISSET(recv_fd, readfds)) { struct Reply_recv recv_rep; make_request(REQ_RECV, NULL, 0, &recv_rep, sizeof (recv_rep)); if (rep.ret != REPLY_SELECT_BROADCAST) fprintf(stderr, "clknetsim: dropped packet from " "node %d on port %d in subnet %d\n", recv_rep.from + 1, recv_rep.dst_port, recv_rep.subnet + 1); goto try_again; } break; default: assert(0); return 0; } assert(!recv_fd || (readfds && FD_ISSET(recv_fd, readfds))); assert(!recv_fd || (recv_fd >= BASE_SOCKET_FD && recv_fd < BASE_SOCKET_FD + MAX_SOCKETS) || (recv_fd >= BASE_TIMER_FD && recv_fd < BASE_TIMER_FD + MAX_TIMERS)); if (readfds) { FD_ZERO(readfds); if (recv_fd) FD_SET(recv_fd, readfds); } if (timeout) { time_to_timeval(timeval_to_time(timeout, 0) - elapsed, timeout); if (timeout->tv_sec < 0) { timeout->tv_sec = 0; timeout->tv_usec = 0; } } return recv_fd ? 1 : 0; } #ifndef CLKNETSIM_DISABLE_POLL int poll(struct pollfd *fds, nfds_t nfds, int timeout) { struct timeval tv, *ptv = NULL; int r, maxfd = 0; nfds_t i; fd_set rfds; /* ptp4l waiting for tx SO_TIMESTAMPING */ if (nfds == 1 && fds[0].events != POLLOUT && get_socket_from_fd(fds[0].fd) >= 0 && sockets[get_socket_from_fd(fds[0].fd)].time_stamping & (SOF_TIMESTAMPING_TX_SOFTWARE | SOF_TIMESTAMPING_TX_HARDWARE)) { if (!fds[0].events) { fds[0].revents = POLLERR; return 1; } else if (fds[0].events == POLLPRI) { /* SO_SELECT_ERR_QUEUE option enabled */ fds[0].revents = POLLPRI; return 1; } } /* pmc waiting to send packet */ if (nfds == 2 && (fds[1].events & POLLOUT) && get_socket_from_fd(fds[1].fd) >= 0) { fds[0].revents = 0; fds[1].revents = POLLOUT; return 1; } FD_ZERO(&rfds); for (i = 0; i < nfds; i++) if (fds[i].fd >= 0 && fds[i].events & POLLIN) { FD_SET(fds[i].fd, &rfds); if (maxfd < fds[i].fd) maxfd = fds[i].fd; } if (timeout >= 0) { tv.tv_sec = timeout / 1000; tv.tv_usec = (timeout % 1000) * 1000; ptv = &tv; } r = select(maxfd + 1, &rfds, NULL, NULL, ptv); for (i = 0; i < nfds; i++) fds[i].revents = r > 0 && fds[i].fd >= 0 && FD_ISSET(fds[i].fd, &rfds) ? POLLIN : 0; return r; } int __poll_chk(struct pollfd *fds, nfds_t nfds, int timeout, size_t fdslen) { return poll(fds, nfds, timeout); } #endif int usleep(useconds_t usec) { struct timeval tv; int r; tv.tv_sec = usec / 1000000; tv.tv_usec = usec % 1000000; r = select(0, NULL, NULL, NULL, &tv); assert(r == 0); return 0; } int nanosleep(const struct timespec *req, struct timespec *rem) { struct timeval tv; int r; tv.tv_sec = req->tv_sec; tv.tv_usec = req->tv_nsec / 1000 + 1; r = select(0, NULL, NULL, NULL, &tv); assert(r <= 0); if (r < 0) { assert(!rem); return r; } if (rem) rem->tv_sec = rem->tv_nsec = 0; return 0; } int clock_nanosleep(clockid_t clock_id, int flags, const struct timespec *request, struct timespec *remain) { assert(clock_id == CLOCK_MONOTONIC || clock_id == CLOCK_REALTIME); return nanosleep(request, remain); } FILE *fopen(const char *path, const char *mode) { if (!strcmp(path, "/proc/net/if_inet6")) { errno = ENOENT; return NULL; } else if (!strcmp(path, "/dev/urandom")) { return URANDOM_FILE; } /* make sure _fopen is initialized in case it is called from another constructor (e.g. OpenSSL's libcrypto) */ init(); return _fopen(path, mode); } size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream) { if (stream == URANDOM_FILE) { size_t i, l = size * nmemb; long r; assert(RAND_MAX >= 0xffffff); for (i = r = 0; i < l; i++) { if (i % 3) r >>= 8; else r = random(); ((unsigned char *)ptr)[i] = r; } return nmemb; } return _fread(ptr, size, nmemb, stream); } int fileno(FILE *stream) { if (stream == URANDOM_FILE) return -1; return _fileno(stream); } int fclose(FILE *fp) { if (fp == URANDOM_FILE) return 0; return _fclose(fp); } int open(const char *pathname, int flags) { int r; assert(REFCLK_PHC_INDEX == 0 && SYSCLK_PHC_INDEX == 1); if (!strcmp(pathname, "/dev/ptp0")) return REFCLK_FD; else if (!strcmp(pathname, "/dev/ptp1")) return SYSCLK_FD; r = _open(pathname, flags); assert(r < 0 || (r < BASE_SOCKET_FD && r < BASE_TIMER_FD)); return r; } int close(int fd) { int t, s; if (fd == REFCLK_FD || fd == SYSCLK_FD) { return 0; } else if ((t = get_timer_from_fd(fd)) >= 0) { return timer_delete(get_timerid(t)); } else if ((s = get_socket_from_fd(fd)) >= 0) { sockets[s].used = 0; return 0; } return _close(fd); } int socket(int domain, int type, int protocol) { int s; if (domain != AF_INET || (type != SOCK_DGRAM && type != SOCK_STREAM)) { errno = EINVAL; return -1; } s = get_free_socket(); if (s < 0) { assert(0); errno = ENOMEM; return -1; } memset(sockets + s, 0, sizeof (struct socket)); sockets[s].used = 1; sockets[s].type = type; sockets[s].port = BASE_SOCKET_DEFAULT_PORT + s; sockets[s].remote_node = -1; return get_socket_fd(s); } int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) { /* ntpd uses connect() and getsockname() to find the interface which will be used to send packets to an address */ int s = get_socket_from_fd(sockfd), port; unsigned int node, subnet; uint32_t a; if (s < 0 || addr->sa_family != AF_INET) { errno = EINVAL; return -1; } port = ntohs(((const struct sockaddr_in *)addr)->sin_port); a = ntohl(((const struct sockaddr_in *)addr)->sin_addr.s_addr); get_target(s, a, &subnet, &node); sockets[s].iface = IFACE_ETH0 + subnet; sockets[s].remote_node = node; sockets[s].remote_port = port; return 0; } int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) { int s = get_socket_from_fd(sockfd), port; uint32_t a; if (s < 0 || addr->sa_family != AF_INET) { errno = EINVAL; return -1; } port = ntohs(((struct sockaddr_in *)addr)->sin_port); a = ntohl(((struct sockaddr_in *)addr)->sin_addr.s_addr); if (port) sockets[s].port = port; if (a == INADDR_ANY) sockets[s].iface = IFACE_ALL; else if (a == INADDR_LOOPBACK) sockets[s].iface = IFACE_LO; else { int subnet = SUBNET_FROM_ADDR(a); assert(subnet >= 0 && subnet < subnets); if (a == NODE_ADDR(subnet, node)) sockets[s].iface = IFACE_ETH0 + subnet; else if (a == BROADCAST_ADDR(subnet)) { sockets[s].iface = IFACE_ETH0 + subnet; sockets[s].broadcast = 1; } else assert(0); } return 0; } int getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen) { int s = get_socket_from_fd(sockfd); uint32_t a; if (s < 0) { errno = EINVAL; return -1; } struct sockaddr_in *in; in = (struct sockaddr_in *)addr; assert(*addrlen >= sizeof (*in)); *addrlen = sizeof (*in); in->sin_family = AF_INET; in->sin_port = htons(sockets[s].port); switch (sockets[s].iface) { case IFACE_NONE: case IFACE_ALL: a = INADDR_ANY; break; case IFACE_LO: a = INADDR_LOOPBACK; break; default: assert(sockets[s].iface - IFACE_ETH0 < subnets); a = sockets[s].broadcast ? BROADCAST_ADDR(sockets[s].iface - IFACE_ETH0) : NODE_ADDR(sockets[s].iface - IFACE_ETH0, node); } in->sin_addr.s_addr = htonl(a); return 0; } int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen) { int subnet, s = get_socket_from_fd(sockfd); if (s < 0) { errno = EINVAL; return -1; } if (level == SOL_SOCKET && optname == SO_BINDTODEVICE) { if (!strcmp(optval, "lo")) sockets[s].iface = IFACE_LO; else if ((subnet = get_network_from_iface(optval)) >= 0) sockets[s].iface = IFACE_ETH0 + subnet; else { errno = EINVAL; return -1; } } else if (level == IPPROTO_IP && optname == IP_PKTINFO && optlen == sizeof (int)) sockets[s].pkt_info = !!(int *)optval; #ifdef SO_TIMESTAMPING else if (level == SOL_SOCKET && optname == SO_TIMESTAMPING && optlen == sizeof (int)) { if (!timestamping) { errno = EINVAL; return -1; } sockets[s].time_stamping = *(int *)optval; } #endif /* unhandled options succeed too */ return 0; } int fcntl(int fd, int cmd, ...) { return 0; } int ioctl(int fd, unsigned long request, ...) { va_list ap; struct ifconf *conf; struct ifreq *req; int i, subnet, ret = 0, s = get_socket_from_fd(fd); va_start(ap, request); if (request == SIOCGIFCONF) { conf = va_arg(ap, struct ifconf *); assert(conf->ifc_len >= sizeof (struct ifreq) * (1 + subnets)); conf->ifc_len = sizeof (struct ifreq) * (1 + subnets); sprintf(conf->ifc_req[0].ifr_name, "lo"); ((struct sockaddr_in*)&conf->ifc_req[0].ifr_addr)->sin_addr.s_addr = htonl(INADDR_LOOPBACK); conf->ifc_req[0].ifr_addr.sa_family = AF_INET; for (i = 0; i < subnets; i++) { sprintf(conf->ifc_req[i + 1].ifr_name, "eth%d", i); ((struct sockaddr_in*)&conf->ifc_req[i + 1].ifr_addr)->sin_addr.s_addr = htonl(NODE_ADDR(i, node)); conf->ifc_req[i + 1].ifr_addr.sa_family = AF_INET; } } else if (request == SIOCGIFINDEX) { req = va_arg(ap, struct ifreq *); if (!strcmp(req->ifr_name, "lo")) req->ifr_ifindex = 0; else if ((subnet = get_network_from_iface(req->ifr_name)) >= 0) req->ifr_ifindex = subnet + 1; else ret = -1, errno = EINVAL; } else if (request == SIOCGIFFLAGS) { req = va_arg(ap, struct ifreq *); if (!strcmp(req->ifr_name, "lo")) req->ifr_flags = IFF_UP | IFF_LOOPBACK; else if (get_network_from_iface(req->ifr_name) >= 0) req->ifr_flags = IFF_UP | IFF_BROADCAST; else ret = -1, errno = EINVAL; } else if (request == SIOCGIFBRDADDR) { req = va_arg(ap, struct ifreq *); if ((subnet = get_network_from_iface(req->ifr_name)) >= 0) ((struct sockaddr_in*)&req->ifr_broadaddr)->sin_addr.s_addr = htonl(BROADCAST_ADDR(subnet)); else ret = -1, errno = EINVAL; req->ifr_broadaddr.sa_family = AF_INET; } else if (request == SIOCGIFNETMASK) { req = va_arg(ap, struct ifreq *); if (!strcmp(req->ifr_name, "lo")) ((struct sockaddr_in*)&req->ifr_netmask)->sin_addr.s_addr = htonl(0xff000000); else if (get_network_from_iface(req->ifr_name) >= 0) ((struct sockaddr_in*)&req->ifr_netmask)->sin_addr.s_addr = htonl(NETMASK); else ret = -1, errno = EINVAL; req->ifr_netmask.sa_family = AF_INET; } else if (request == SIOCGIFHWADDR) { req = va_arg(ap, struct ifreq *); if (!strcmp(req->ifr_name, "lo")) memset((&req->ifr_hwaddr)->sa_data, 0, IFHWADDRLEN); else if ((subnet = get_network_from_iface(req->ifr_name)) >= 0) { char mac[IFHWADDRLEN] = {0x12, 0x34, 0x56, 0x78, subnet + 1, node + 1}; memcpy((&req->ifr_hwaddr)->sa_data, mac, sizeof (mac)); } else ret = -1, errno = EINVAL; req->ifr_netmask.sa_family = AF_UNSPEC; #ifdef ETHTOOL_GET_TS_INFO } else if (request == SIOCETHTOOL) { struct ethtool_ts_info *info; req = va_arg(ap, struct ifreq *); info = (struct ethtool_ts_info *)req->ifr_data; memset(info, 0, sizeof (*info)); if (get_network_from_iface(req->ifr_name) >= 0) { info->phc_index = timestamping > 1 ? REFCLK_PHC_INDEX : SYSCLK_PHC_INDEX; info->so_timestamping = SOF_TIMESTAMPING_SOFTWARE | SOF_TIMESTAMPING_TX_SOFTWARE | SOF_TIMESTAMPING_RX_SOFTWARE | SOF_TIMESTAMPING_RAW_HARDWARE | SOF_TIMESTAMPING_TX_HARDWARE | SOF_TIMESTAMPING_RX_HARDWARE; info->tx_types = HWTSTAMP_TX_ON; info->rx_filters = 1 << HWTSTAMP_FILTER_NONE | 1 << HWTSTAMP_FILTER_ALL; } else ret = -1, errno = EINVAL; #endif #ifdef PTP_CLOCK_GETCAPS } else if (request == PTP_CLOCK_GETCAPS && (fd == REFCLK_FD || fd == SYSCLK_FD)) { struct ptp_clock_caps *caps = va_arg(ap, struct ptp_clock_caps *); memset(caps, 0, sizeof (*caps)); /* maximum frequency in 32-bit timex.freq */ caps->max_adj = 32767999; #endif #ifdef PTP_SYS_OFFSET } else if (request == PTP_SYS_OFFSET && fd == REFCLK_FD) { struct ptp_sys_offset *sys_off = va_arg(ap, struct ptp_sys_offset *); struct timespec ts; int i; if (sys_off->n_samples > PTP_MAX_SAMPLES) sys_off->n_samples = PTP_MAX_SAMPLES; clock_gettime(REFCLK_ID, &ts); for (i = 0; i < sys_off->n_samples; i++) { sys_off->ts[2 * i + 1].sec = ts.tv_sec; sys_off->ts[2 * i + 1].nsec = ts.tv_nsec; } clock_gettime(CLOCK_REALTIME, &ts); for (i = 0; i < sys_off->n_samples + 1; i++) { sys_off->ts[2 * i].sec = ts.tv_sec; sys_off->ts[2 * i].nsec = ts.tv_nsec; } #endif #ifdef PTP_SYS_OFFSET_PRECISE } else if (request == PTP_SYS_OFFSET_PRECISE && fd == REFCLK_FD) { struct ptp_sys_offset_precise *sys_off = va_arg(ap, struct ptp_sys_offset_precise *); struct timespec ts; clock_gettime(REFCLK_ID, &ts); sys_off->device.sec = ts.tv_sec; sys_off->device.nsec = ts.tv_nsec; clock_gettime(CLOCK_REALTIME, &ts); sys_off->sys_realtime.sec = ts.tv_sec; sys_off->sys_realtime.nsec = ts.tv_nsec; #endif #ifdef SIOCSHWTSTAMP } else if (request == SIOCSHWTSTAMP && s >= 0) { #endif } else { ret = -1; errno = EINVAL; } va_end(ap); return ret; } int getifaddrs(struct ifaddrs **ifap) { struct iface { struct ifaddrs ifaddrs; struct sockaddr_in addr, netmask, broadaddr; char name[11]; } *ifaces; int i; ifaces = malloc(sizeof (struct iface) * (1 + subnets)); ifaces[0].ifaddrs = (struct ifaddrs){ .ifa_next = &ifaces[1].ifaddrs, .ifa_name = "lo", .ifa_flags = IFF_UP | IFF_LOOPBACK | IFF_RUNNING, .ifa_addr = (struct sockaddr *)&ifaces[0].addr, .ifa_netmask = (struct sockaddr *)&ifaces[0].netmask, .ifa_broadaddr = (struct sockaddr *)&ifaces[0].broadaddr }; ifaces[0].addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); ifaces[0].netmask.sin_addr.s_addr = htonl(0xff000000); ifaces[0].broadaddr.sin_addr.s_addr = 0; for (i = 0; i < subnets; i++) { ifaces[i + 1].ifaddrs = (struct ifaddrs){ .ifa_next = &ifaces[i + 2].ifaddrs, .ifa_flags = IFF_UP | IFF_BROADCAST | IFF_RUNNING, .ifa_addr = (struct sockaddr *)&ifaces[i + 1].addr, .ifa_netmask = (struct sockaddr *)&ifaces[i + 1].netmask, .ifa_broadaddr = (struct sockaddr *)&ifaces[i + 1].broadaddr }; ifaces[i + 1].ifaddrs.ifa_name = ifaces[i + 1].name; snprintf(ifaces[i + 1].name, sizeof (ifaces[i + 1].name), "eth%d", i); ifaces[i + 1].addr.sin_addr.s_addr = htonl(NODE_ADDR(i, node)); ifaces[i + 1].netmask.sin_addr.s_addr = htonl(NETMASK); ifaces[i + 1].broadaddr.sin_addr.s_addr = htonl(BROADCAST_ADDR(i)); } ifaces[i].ifaddrs.ifa_next = NULL; for (i = 0; i < 1 + subnets; i++) { ifaces[i].addr.sin_family = AF_INET; ifaces[i].netmask.sin_family = AF_INET; ifaces[i].broadaddr.sin_family = AF_INET; } *ifap = (struct ifaddrs *)ifaces; return 0; } void freeifaddrs(struct ifaddrs *ifa) { free(ifa); } ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags) { struct Request_send req; struct sockaddr_in connected_sa, *sa; int s = get_socket_from_fd(sockfd); if (s < 0 || sockets[s].type != SOCK_DGRAM) { assert(0); errno = EINVAL; return -1; } if (sockets[s].remote_node >= 0) { if (msg->msg_name) { errno = EISCONN; return -1; } sa = &connected_sa; sa->sin_family = AF_INET; sa->sin_port = htons(sockets[s].remote_port); sa->sin_addr.s_addr = htonl(NODE_ADDR(sockets[s].iface - IFACE_ETH0, sockets[s].remote_node)); } else { sa = msg->msg_name; assert(sa && msg->msg_namelen >= sizeof (struct sockaddr_in)); assert(sa->sin_family == AF_INET); } assert(msg->msg_iovlen == 1); assert(msg->msg_iov[0].iov_len <= sizeof (req.data)); get_target(s, ntohl(sa->sin_addr.s_addr), &req.subnet, &req.to); req.src_port = sockets[s].port; req.dst_port = ntohs(sa->sin_port); assert(req.src_port && req.dst_port); req.len = msg->msg_iov[0].iov_len; memcpy(req.data, msg->msg_iov[0].iov_base, req.len); make_request(REQ_SEND, &req, offsetof(struct Request_send, data) + req.len, NULL, 0); if (sockets[s].time_stamping & (SOF_TIMESTAMPING_TX_SOFTWARE | SOF_TIMESTAMPING_TX_HARDWARE)) { struct ts_message *last_ts_msg = &sockets[s].last_ts_msg; assert(req.len <= sizeof (last_ts_msg->data)); memcpy(last_ts_msg->data, req.data, req.len); last_ts_msg->len = req.len; last_ts_msg->subnet = req.subnet; last_ts_msg->to = req.to; last_ts_msg->port = req.dst_port; } return req.len; } ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen) { struct msghdr msg; struct iovec iov; iov.iov_base = (void *)buf; iov.iov_len = len; msg.msg_name = (void *)dest_addr; msg.msg_namelen = addrlen; msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = NULL; msg.msg_controllen = 0; msg.msg_flags = 0; return sendmsg(sockfd, &msg, flags); } ssize_t send(int sockfd, const void *buf, size_t len, int flags) { return sendto(sockfd, buf, len, flags, NULL, 0); } int recvmmsg(int sockfd, struct mmsghdr *msgvec, unsigned int vlen, int flags, #if !defined(__GLIBC_PREREQ) || !(__GLIBC_PREREQ(2, 20)) const #endif struct timespec *timeout) { ssize_t len; int i, n; assert(vlen > 0); len = recvmsg(sockfd, &msgvec[0].msg_hdr, flags); if (len < 0) return -1; msgvec[0].msg_len = len; if (recv_multiply <= 1 || vlen <= 1) return 1; n = random() % recv_multiply + 1; if (n > vlen) n = vlen; for (i = 1; i < n; i++) { struct msghdr *src = &msgvec[0].msg_hdr, *dst = &msgvec[i].msg_hdr; if (dst->msg_name) { memcpy(dst->msg_name, src->msg_name, src->msg_namelen); dst->msg_namelen = src->msg_namelen; } assert(dst->msg_iovlen == 1 && dst->msg_iov[0].iov_len >= len); memcpy(dst->msg_iov[0].iov_base, src->msg_iov[0].iov_base, len); if (dst->msg_control) { assert(dst->msg_controllen >= src->msg_controllen); memcpy(dst->msg_control, src->msg_control, src->msg_controllen); dst->msg_controllen = src->msg_controllen; } dst->msg_flags = src->msg_flags; msgvec[i].msg_len = msgvec[0].msg_len; } return n; } ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags) { struct Reply_recv rep; struct sockaddr_in *sa; struct cmsghdr *cmsg; int msglen, cmsglen, s = get_socket_from_fd(sockfd); if (sockfd == clknetsim_fd) return _recvmsg(sockfd, msg, flags); assert(s >= 0 && sockets[s].type == SOCK_DGRAM); if (sockets[s].time_stamping & (SOF_TIMESTAMPING_TX_SOFTWARE | SOF_TIMESTAMPING_TX_HARDWARE) && flags & MSG_ERRQUEUE) { struct ts_message *last_ts_msg; uint32_t addr; uint16_t port; /* last message looped back to the error queue */ last_ts_msg = &sockets[s].last_ts_msg; assert(last_ts_msg->len); msg->msg_flags = MSG_ERRQUEUE; rep.subnet = last_ts_msg->subnet; rep.from = last_ts_msg->to; rep.src_port = last_ts_msg->port; rep.dst_port = sockets[s].port; addr = htonl(NODE_ADDR(rep.subnet, rep.from)); port = htons(rep.src_port); /* put the message in an Ethernet frame */ memset(rep.data, 0, 42); rep.data[12] = 0x08; rep.data[14] = 0x45; rep.data[23] = 17; memcpy(rep.data + 30, &addr, sizeof (addr)); memcpy(rep.data + 36, &port, sizeof (port)); assert(last_ts_msg->len + 42 <= sizeof (rep.data)); memcpy(rep.data + 42, last_ts_msg->data, last_ts_msg->len); rep.len = 42 + last_ts_msg->len; last_ts_msg->len = 0; } else make_request(REQ_RECV, NULL, 0, &rep, sizeof (rep)); if (rep.len == 0) { errno = EWOULDBLOCK; return -1; } assert(socket_in_subnet(s, rep.subnet)); assert(sockets[s].port == rep.dst_port); assert(!sockets[s].remote_port || sockets[s].remote_port == rep.src_port); if (msg->msg_name) { assert(msg->msg_namelen >= sizeof (struct sockaddr_in)); sa = msg->msg_name; sa->sin_family = AF_INET; sa->sin_port = htons(rep.src_port); sa->sin_addr.s_addr = htonl(NODE_ADDR(rep.subnet, rep.from)); msg->msg_namelen = sizeof (struct sockaddr_in); } assert(msg->msg_iovlen == 1); msglen = msg->msg_iov[0].iov_len < rep.len ? msg->msg_iov[0].iov_len : rep.len; memcpy(msg->msg_iov[0].iov_base, rep.data, msglen); cmsglen = 0; if (sockets[s].pkt_info) { struct in_pktinfo ipi; cmsglen = CMSG_SPACE(sizeof (ipi)); assert(msg->msg_control && msg->msg_controllen >= cmsglen); cmsg = CMSG_FIRSTHDR(msg); memset(cmsg, 0, sizeof (*cmsg)); cmsg->cmsg_level = IPPROTO_IP; cmsg->cmsg_type = IP_PKTINFO; cmsg->cmsg_len = CMSG_LEN(sizeof (ipi)); memset(&ipi, 0, sizeof (ipi)); ipi.ipi_spec_dst.s_addr = htonl(NODE_ADDR(rep.subnet, node)); ipi.ipi_addr.s_addr = ipi.ipi_spec_dst.s_addr; ipi.ipi_ifindex = rep.subnet + 1; memcpy(CMSG_DATA(cmsg), &ipi, sizeof (ipi)); } #ifdef SO_TIMESTAMPING if ((sockets[s].time_stamping & (SOF_TIMESTAMPING_TX_SOFTWARE | SOF_TIMESTAMPING_TX_HARDWARE) && flags & MSG_ERRQUEUE) || (sockets[s].time_stamping & (SOF_TIMESTAMPING_RX_SOFTWARE | SOF_TIMESTAMPING_RX_HARDWARE) && !(flags & MSG_ERRQUEUE))) { struct timespec ts; /* don't use CMSG_NXTHDR as it's buggy in glibc */ cmsg = (struct cmsghdr *)((char *)CMSG_FIRSTHDR(msg) + cmsglen); cmsglen += CMSG_SPACE(3 * sizeof (ts)); assert(msg->msg_control && msg->msg_controllen >= cmsglen); memset(cmsg, 0, CMSG_SPACE(3 * sizeof (ts))); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SO_TIMESTAMPING; cmsg->cmsg_len = CMSG_LEN(3 * sizeof (ts)); if (sockets[s].time_stamping & SOF_TIMESTAMPING_SOFTWARE) { clock_gettime(CLOCK_REALTIME, &ts); memcpy((struct timespec *)CMSG_DATA(cmsg), &ts, sizeof (ts)); } if (sockets[s].time_stamping & SOF_TIMESTAMPING_RAW_HARDWARE) { clock_gettime(timestamping > 1 ? REFCLK_ID : CLOCK_REALTIME, &ts); memcpy((struct timespec *)CMSG_DATA(cmsg) + 2, &ts, sizeof (ts)); } } #endif msg->msg_controllen = cmsglen; return msglen; } ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen) { ssize_t ret; struct msghdr msg; struct iovec iov; iov.iov_base = (void *)buf; iov.iov_len = len; /* needed for compatibility with old glibc recvmsg() */ memset(&msg, 0, sizeof (msg)); msg.msg_name = (void *)src_addr; msg.msg_namelen = *addrlen; msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = NULL; msg.msg_controllen = 0; msg.msg_flags = 0; ret = recvmsg(sockfd, &msg, flags); *addrlen = msg.msg_namelen; return ret; } ssize_t recv(int sockfd, void *buf, size_t len, int flags) { struct sockaddr_in sa; socklen_t addrlen = sizeof (sa); return recvfrom(sockfd, buf, len, flags, (struct sockaddr *)&sa, &addrlen); } int timer_create(clockid_t which_clock, struct sigevent *timer_event_spec, timer_t *created_timer_id) { int t; assert(which_clock == CLOCK_REALTIME && timer_event_spec == NULL); t = get_free_timer(); if (t < 0) { assert(0); errno = ENOMEM; return -1; } timers[t].used = 1; timers[t].armed = 0; timers[t].type = TIMER_TYPE_SIGNAL; timers[t].clock_id = which_clock; *created_timer_id = get_timerid(t); return 0; } int timer_delete(timer_t timerid) { int t = get_timer_from_id(timerid); if (t < 0) { errno = EINVAL; return -1; } timers[t].used = 0; return 0; } int timer_settime(timer_t timerid, int flags, const struct itimerspec *value, struct itimerspec *ovalue) { int t = get_timer_from_id(timerid); if (t < 0) { errno = EINVAL; return -1; } assert(value && ovalue == NULL && (flags == 0 || (flags == TIMER_ABSTIME && timers[t].clock_id == CLOCK_MONOTONIC))); if (value->it_value.tv_sec || value->it_value.tv_nsec) { timers[t].armed = 1; timers[t].timeout = timespec_to_time(&value->it_value, 0); if (!(flags & TIMER_ABSTIME)) timers[t].timeout += get_monotonic_time(); timers[t].interval = timespec_to_time(&value->it_interval, 0); } else { timers[t].armed = 0; } return 0; } int timer_gettime(timer_t timerid, struct itimerspec *value) { double timeout; int t = get_timer_from_id(timerid); if (t < 0) { errno = EINVAL; return -1; } if (timers[t].armed) { timeout = timers[t].timeout - get_monotonic_time(); time_to_timespec(timeout, &value->it_value); } else { value->it_value.tv_sec = 0; value->it_value.tv_nsec = 0; } time_to_timespec(timers[t].interval, &value->it_interval); return 0; } #ifndef CLKNETSIM_DISABLE_ITIMER int setitimer(__itimer_which_t which, const struct itimerval *new_value, struct itimerval *old_value) { struct itimerspec timerspec; assert(which == ITIMER_REAL && old_value == NULL); if (get_timer_from_id(itimer_real_id) < 0) timer_create(CLOCK_REALTIME, NULL, &itimer_real_id); timerspec.it_interval.tv_sec = new_value->it_interval.tv_sec; timerspec.it_interval.tv_nsec = new_value->it_interval.tv_usec * 1000; timerspec.it_value.tv_sec = new_value->it_value.tv_sec; timerspec.it_value.tv_nsec = new_value->it_value.tv_usec * 1000; return timer_settime(itimer_real_id, 0, &timerspec, NULL); } int getitimer(__itimer_which_t which, struct itimerval *curr_value) { struct itimerspec timerspec; assert(which == ITIMER_REAL); if (timer_gettime(itimer_real_id, &timerspec)) return -1; curr_value->it_interval.tv_sec = timerspec.it_interval.tv_sec; curr_value->it_interval.tv_usec = timerspec.it_interval.tv_nsec / 1000; curr_value->it_value.tv_sec = timerspec.it_value.tv_sec; curr_value->it_value.tv_usec = timerspec.it_value.tv_nsec / 1000; return 0; } #endif int timerfd_create(int clockid, int flags) { int t; assert((clockid == CLOCK_REALTIME || clockid == CLOCK_MONOTONIC) && !flags); t = get_free_timer(); if (t < 0) { assert(0); errno = ENOMEM; return -1; } timers[t].used = 1; timers[t].armed = 0; timers[t].type = TIMER_TYPE_FD; timers[t].clock_id = clockid; return get_timerfd(t); } int timerfd_settime(int fd, int flags, const struct itimerspec *new_value, struct itimerspec *old_value) { if (flags == TFD_TIMER_ABSTIME) flags = TIMER_ABSTIME; else assert(!flags); return timer_settime(get_timerid(get_timer_from_fd(fd)), flags, new_value, old_value); } int timerfd_gettime(int fd, struct itimerspec *curr_value) { return timer_gettime(get_timerid(get_timer_from_fd(fd)), curr_value); } int shmget(key_t key, size_t size, int shmflg) { if (fuzz_mode) return _shmget(key, size, shmflg); if (key >= SHM_KEY && key < SHM_KEY + SHM_REFCLOCKS) return key; return -1; } void *shmat(int shmid, const void *shmaddr, int shmflg) { if (fuzz_mode) return _shmat(shmid, shmaddr, shmflg); assert(shmid >= SHM_KEY && shmid < SHM_KEY + SHM_REFCLOCKS); if (shm_refclocks < shmid - SHM_KEY + 1) shm_refclocks = shmid - SHM_KEY + 1; memset(&shm_time[shmid - SHM_KEY], 0, sizeof (shm_time[0])); shm_time[shmid - SHM_KEY].mode = 1; shm_time[shmid - SHM_KEY].precision = -20; /* don't wait for select() with starting of the refclock generator */ fill_refclock_sample(); return &shm_time[shmid - SHM_KEY]; } int shmdt(const void *shmaddr) { assert(shmaddr >= (void *)&shm_time[0] && shmaddr < (void *)&shm_time[SHM_REFCLOCKS]); return 0; } uid_t getuid(void) { return 0; } int uname(struct utsname *buf) { memset(buf, 0, sizeof (*buf)); sprintf(buf->sysname, "Linux (clknetsim)"); sprintf(buf->release, "4.19"); return 0; } int gethostname(char *name, size_t len) { snprintf(name, len, "clknetsim-node%d", node + 1); return 0; } void openlog(const char *ident, int option, int facility) { } void __syslog_chk(int priority, int flag, const char *format, ...) { va_list ap; va_start(ap, format); vfprintf(stderr, format, ap); va_end(ap); fprintf(stderr, "\n"); } void syslog(int priority, const char *format, ...) { va_list ap; va_start(ap, format); vfprintf(stderr, format, ap); va_end(ap); fprintf(stderr, "\n"); } void closelog(void) { } #ifndef CLKNETSIM_DISABLE_SYSCALL long syscall(long number, ...) { va_list ap; long r; struct timex *timex; clockid_t clock_id; va_start(ap, number); switch (number) { #ifdef __NR_clock_adjtime case __NR_clock_adjtime: clock_id = va_arg(ap, clockid_t); timex = va_arg(ap, struct timex *); r = clock_adjtime(clock_id, timex); break; #endif default: assert(0); } va_end(ap); return r; } #endif ssize_t getrandom(void *buf, size_t length, unsigned int flags) { errno = ENOTSUP; return -1; } void srandom(unsigned int seed) { FILE *f; /* override the seed to the fixed seed if set or make it truly random in case it's based on the simulated time */ if (random_seed) { seed = random_seed + node; } else if ((f = _fopen("/dev/urandom", "r"))) { if (fread(&seed, sizeof (seed), 1, f) != 1) ; fclose(f); } _srandom(seed); } struct passwd *getpwnam(const char *name) { static struct passwd pw = { .pw_name = "", .pw_passwd = "", .pw_uid = 0, .pw_gid = 0, .pw_gecos = "", .pw_dir = "", .pw_shell = "" }; return &pw; } int initgroups(const char *user, gid_t group) { return 0; } int setgroups(size_t size, const gid_t *list) { return 0; } int setegid(gid_t gid) { return 0; } int setgid(gid_t gid) { return 0; } int seteuid(uid_t uid) { return 0; } int setuid(uid_t uid) { return 0; } int cap_set_proc() { return 0; }