/* * Copyright (c) 2016 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #define __need_IOV_MAX #include #include #include #include #include #include #include #include #include #include #ifndef IOV_MAX #define IOV_MAX __IOV_MAX #endif /* * VCOM_SOCKET Private definitions and functions. */ typedef struct vcom_socket_main_t_ { u8 init; clib_time_t clib_time; pid_t my_pid; /* vcom_socket pool */ vcom_socket_t *vsockets; /* Hash table for socketidx to fd mapping */ uword *sockidx_by_fd; /* vcom_epoll pool */ vcom_epoll_t *vepolls; /* Hash table for epollidx to epfd mapping */ uword *epollidx_by_epfd; /* common epitem poll for all epfd */ /* TBD: epitem poll per epfd */ /* vcom_epitem pool */ vcom_epitem_t *vepitems; /* Hash table for epitemidx to epfdfd mapping */ uword *epitemidx_by_epfdfd; /* Hash table - key:epfd, value:vec of epitemidx */ uword *epitemidxs_by_epfd; /* Hash table - key:fd, value:vec of epitemidx */ uword *epitemidxs_by_fd; } vcom_socket_main_t; vcom_socket_main_t vcom_socket_main; static int vcom_socket_open_socket (int domain, int type, int protocol) { int rv = -1; /* handle domains implemented by vpp */ switch (domain) { case AF_INET: case AF_INET6: /* get socket type and * handle the socket types supported by vpp */ switch (type & ~(SOCK_CLOEXEC | SOCK_NONBLOCK)) { case SOCK_STREAM: case SOCK_DGRAM: /* the type argument serves a second purpose, * in addition to specifying a socket type, * it may include the bitwise OR of any of * SOCK_NONBLOCK and SOCK_CLOEXEC, to modify * the behavior of socket. */ rv = libc_socket (domain, type, protocol); if (rv == -1) rv = -errno; break; default: break; } break; default: break; } return rv; } static int vcom_socket_open_epoll (int flags) { int rv = -1; if (flags < 0) { return -EINVAL; } if (flags && (flags & ~EPOLL_CLOEXEC)) { return -EINVAL; } /* flags can be either zero or EPOLL_CLOEXEC */ rv = libc_epoll_create1 (flags); if (rv == -1) rv = -errno; return rv; } static int vcom_socket_close_socket (int fd) { int rv; rv = libc_close (fd); if (rv == -1) rv = -errno; return rv; } static int vcom_socket_close_epoll (int epfd) { int rv; rv = libc_close (epfd); if (rv == -1) rv = -errno; return rv; } /* * Public API functions */ int vcom_socket_is_vcom_fd (int fd) { vcom_socket_main_t *vsm = &vcom_socket_main; uword *p; vcom_socket_t *vsock; p = hash_get (vsm->sockidx_by_fd, fd); if (p) { vsock = pool_elt_at_index (vsm->vsockets, p[0]); if (vsock && vsock->type == SOCKET_TYPE_VPPCOM_BOUND) return 1; } return 0; } int vcom_socket_is_vcom_epfd (int epfd) { vcom_socket_main_t *vsm = &vcom_socket_main; uword *p; vcom_epoll_t *vepoll; p = hash_get (vsm->epollidx_by_epfd, epfd); if (p) { vepoll = pool_elt_at_index (vsm->vepolls, p[0]); if (vepoll && vepoll->type == EPOLL_TYPE_VPPCOM_BOUND) return 1; } return 0; } static inline int vcom_socket_get_sid (int fd) { vcom_socket_main_t *vsm = &vcom_socket_main; uword *p; vcom_socket_t *vsock; p = hash_get (vsm->sockidx_by_fd, fd); if (p) { vsock = pool_elt_at_index (vsm->vsockets, p[0]); if (vsock && vsock->type == SOCKET_TYPE_VPPCOM_BOUND) return vsock->sid; } return INVALID_SESSION_ID; } static inline int vcom_socket_get_vep_idx (int epfd) { vcom_socket_main_t *vsm = &vcom_socket_main; uword *p; vcom_epoll_t *vepoll; p = hash_get (vsm->epollidx_by_epfd, epfd); if (p) { vepoll = pool_elt_at_index (vsm->vepolls, p[0]); if (vepoll && vepoll->type == EPOLL_TYPE_VPPCOM_BOUND) return vepoll->vep_idx; } return INVALID_VEP_IDX; } static inline int vcom_socket_get_sid_and_vsock (int fd, vcom_socket_t ** vsockp) { vcom_socket_main_t *vsm = &vcom_socket_main; uword *p; vcom_socket_t *vsock; p = hash_get (vsm->sockidx_by_fd, fd); if (p) { vsock = pool_elt_at_index (vsm->vsockets, p[0]); if (vsock && vsock->type == SOCKET_TYPE_VPPCOM_BOUND) { *vsockp = vsock; return vsock->sid; } } return INVALID_SESSION_ID; } static inline int vcom_socket_get_vep_idx_and_vepoll (int epfd, vcom_epoll_t ** vepollp) { vcom_socket_main_t *vsm = &vcom_socket_main; uword *p; vcom_epoll_t *vepoll; p = hash_get (vsm->epollidx_by_epfd, epfd); if (p) { vepoll = pool_elt_at_index (vsm->vepolls, p[0]); if (vepoll && vepoll->type == EPOLL_TYPE_VPPCOM_BOUND) { *vepollp = vepoll; return vepoll->vep_idx; } } return INVALID_VEP_IDX; } static int vcom_socket_close_vepoll (int epfd) { int rv = -1; vcom_socket_main_t *vsm = &vcom_socket_main; uword *p; vcom_epoll_t *vepoll; p = hash_get (vsm->epollidx_by_epfd, epfd); if (!p) return -EBADF; vepoll = pool_elt_at_index (vsm->vepolls, p[0]); if (!vepoll) return -EBADF; if (vepoll->type != EPOLL_TYPE_VPPCOM_BOUND) return -EINVAL; if (vepoll->count) { if (!vepoll->close) { vepoll->close = 1; return 0; } else { return -EBADF; } } /* count is zero */ rv = vppcom_session_close (vepoll->vep_idx); rv = vcom_socket_close_epoll (vepoll->epfd); vepoll_init (vepoll); hash_unset (vsm->epollidx_by_epfd, epfd); pool_put (vsm->vepolls, vepoll); return rv; } static int vcom_socket_close_vsock (int fd) { int rv = -1; vcom_socket_main_t *vsm = &vcom_socket_main; uword *p; vcom_socket_t *vsock; vcom_epitem_t *vepitem; i32 *vepitemidxs = 0; i32 *vepitemidxs_var = 0; p = hash_get (vsm->sockidx_by_fd, fd); if (!p) return -EBADF; vsock = pool_elt_at_index (vsm->vsockets, p[0]); if (!vsock) return -ENOTSOCK; if (vsock->type != SOCKET_TYPE_VPPCOM_BOUND) return -EINVAL; rv = vppcom_session_close (vsock->sid); rv = vcom_socket_close_socket (vsock->fd); vsocket_init (vsock); hash_unset (vsm->sockidx_by_fd, fd); pool_put (vsm->vsockets, vsock); /* * NOTE: * Before calling close(), user should remove * this fd from the epoll-set of all epoll instances, * otherwise resource(epitems) leaks ensues. */ /* * 00. close all epoll instances that are marked as "close" * of which this fd is the "last" remaining member. * 01. epitems associated with this fd are intentionally * not removed, see NOTE: above. * */ /* does this fd participate in epoll */ p = hash_get (vsm->epitemidxs_by_fd, fd); if (p) { vepitemidxs = *(i32 **) p; vec_foreach (vepitemidxs_var, vepitemidxs) { vepitem = pool_elt_at_index (vsm->vepitems, vepitemidxs_var[0]); if (vepitem && vepitem->fd == fd && vepitem->type == FD_TYPE_VCOM_SOCKET) { i32 vep_idx; vcom_epoll_t *vepoll; if ((vep_idx = vcom_socket_get_vep_idx_and_vepoll (vepitem->epfd, &vepoll)) != INVALID_VEP_IDX) { if (vepoll->close) { if (vepoll->count == 1) { /* * force count to zero and * close this epoll instance * */ vepoll->count = 0; vcom_socket_close_vepoll (vepoll->epfd); } else { vepoll->count -= 1; } } } } } } return rv; } int vcom_socket_close (int __fd) { int rv; if (vcom_socket_is_vcom_fd (__fd)) { rv = vcom_socket_close_vsock (__fd); } else if (vcom_socket_is_vcom_epfd (__fd)) { rv = vcom_socket_close_vepoll (__fd); } else { rv = -EBADF; } return rv; } ssize_t vcom_socket_read (int __fd, void *__buf, size_t __nbytes) { int rv = -1; vcom_socket_main_t *vsm = &vcom_socket_main; uword *p; vcom_socket_t *vsock; p = hash_get (vsm->sockidx_by_fd, __fd); if (!p) return -EBADF; vsock = pool_elt_at_index (vsm->vsockets, p[0]); if (!vsock) return -ENOTSOCK; if (vsock->type != SOCKET_TYPE_VPPCOM_BOUND) return -EINVAL; if (!__buf) { return -EINVAL; } rv = vcom_fcntl (__fd, F_GETFL, 0); if (rv < 0) { return rv; } /* is blocking */ if (!(rv & O_NONBLOCK)) { do { rv = vppcom_session_read (vsock->sid, __buf, __nbytes); } /* coverity[CONSTANT_EXPRESSION_RESULT] */ while (rv == -EAGAIN || rv == -EWOULDBLOCK); return rv; } /* The file descriptor refers to a socket and has been * marked nonblocking(O_NONBLOCK) and the read would * block. * */ /* is non blocking */ rv = vppcom_session_read (vsock->sid, __buf, __nbytes); return rv; } ssize_t vcom_socket_readv (int __fd, const struct iovec * __iov, int __iovcnt) { int rv; vcom_socket_main_t *vsm = &vcom_socket_main; uword *p; vcom_socket_t *vsock; ssize_t total = 0, len = 0; int i; p = hash_get (vsm->sockidx_by_fd, __fd); if (!p) return -EBADF; vsock = pool_elt_at_index (vsm->vsockets, p[0]); if (!vsock) return -ENOTSOCK; if (vsock->type != SOCKET_TYPE_VPPCOM_BOUND) return -EINVAL; if (__iov == 0 || __iovcnt == 0 || __iovcnt > IOV_MAX) return -EINVAL; /* Sanity check */ for (i = 0; i < __iovcnt; ++i) { if (SSIZE_MAX - len < __iov[i].iov_len) return -EINVAL; len += __iov[i].iov_len; } rv = vcom_fcntl (__fd, F_GETFL, 0); if (rv < 0) { return rv; } /* is blocking */ if (!(rv & O_NONBLOCK)) { do { for (i = 0; i < __iovcnt; ++i) { rv = vppcom_session_read (vsock->sid, __iov[i].iov_base, __iov[i].iov_len); if (rv < 0) break; else { total += rv; if (rv < __iov[i].iov_len) /* Read less than buffer provided, no point to continue */ break; } } } /* coverity[CONSTANT_EXPRESSION_RESULT] */ while ((rv == -EAGAIN || rv == -EWOULDBLOCK) && total == 0); return total; } /* is non blocking */ for (i = 0; i < __iovcnt; ++i) { rv = vppcom_session_read (vsock->sid, __iov[i].iov_base, __iov[i].iov_len); if (rv < 0) { if (total > 0) break; else { errno = rv; return rv; } } else { total += rv; if (rv < __iov[i].iov_len) /* Read less than buffer provided, no point to continue */ break; } } return total; } ssize_t vcom_socket_write (int __fd, const void *__buf, size_t __n) { int rv = -1; vcom_socket_main_t *vsm = &vcom_socket_main; uword *p; vcom_socket_t *vsock; if (!__buf) { return -EINVAL; } p = hash_get (vsm->sockidx_by_fd, __fd); if (!p) return -EBADF; vsock = pool_elt_at_index (vsm->vsockets, p[0]); if (!vsock) return -ENOTSOCK; if (vsock->type != SOCKET_TYPE_VPPCOM_BOUND) return -EINVAL; rv = vppcom_session_write (vsock->sid, (void *) __buf, __n); return rv; } ssize_t vcom_socket_writev (int __fd, const struct iovec * __iov, int __iovcnt) { int rv = -1; ssize_t total = 0; vcom_socket_main_t *vsm = &vcom_socket_main; uword *p; vcom_socket_t *vsock; int i; p = hash_get (vsm->sockidx_by_fd, __fd); if (!p) return -EBADF; vsock = pool_elt_at_index (vsm->vsockets, p[0]); if (!vsock) return -ENOTSOCK; if (vsock->type != SOCKET_TYPE_VPPCOM_BOUND) return -EINVAL; if (__iov == 0 || __iovcnt == 0 || __iovcnt > IOV_MAX) return -EINVAL; for (i = 0; i < __iovcnt; ++i) { rv = vppcom_session_write (vsock->sid, __iov[i].iov_base, __iov[i].iov_len); if (rv < 0) { if (total > 0) break; else return rv; } else total += rv; } return total; } /* * RETURN: 0 - invalid cmd * 1 - cmd not handled by vcom and vppcom * 2 - cmd handled by vcom socket resource * 3 - cmd handled by vppcom * */ /* TBD: incomplete list of cmd */ static int vcom_socket_check_fcntl_cmd (int __cmd) { switch (__cmd) { /*cmd not handled by vcom and vppcom */ /* Fallthrough */ case F_DUPFD: case F_DUPFD_CLOEXEC: return 1; /* cmd handled by vcom socket resource */ /* Fallthrough */ case F_GETFD: case F_SETFD: case F_GETLK: case F_SETLK: case F_SETLKW: case F_GETOWN: case F_SETOWN: return 2; /* cmd handled by vcom and vppcom */ case F_SETFL: case F_GETFL: return 3; /* cmd not handled by vcom and vppcom */ default: return 1; } return 0; } static inline int vcom_session_fcntl_va (int __sid, int __cmd, va_list __ap) { int flags = va_arg (__ap, int); int rv = -EOPNOTSUPP; uint32_t size; size = sizeof (flags); if (__cmd == F_SETFL) { rv = vppcom_session_attr (__sid, VPPCOM_ATTR_SET_FLAGS, &flags, &size); } else if (__cmd == F_GETFL) { rv = vppcom_session_attr (__sid, VPPCOM_ATTR_GET_FLAGS, &flags, &size); if (rv == VPPCOM_OK) rv = flags; } return rv; } int vcom_socket_fcntl_va (int __fd, int __cmd, va_list __ap) { int rv = -EBADF; vcom_socket_main_t *vsm = &vcom_socket_main; uword *p; vcom_socket_t *vsock; p = hash_get (vsm->sockidx_by_fd, __fd); if (!p) return -EBADF; vsock = pool_elt_at_index (vsm->vsockets, p[0]); if (!vsock) return -ENOTSOCK; if (vsock->type != SOCKET_TYPE_VPPCOM_BOUND) return -EINVAL; switch (vcom_socket_check_fcntl_cmd (__cmd)) { /* invalid cmd */ case 0: rv = -EBADF; break; /*cmd not handled by vcom and vppcom */ case 1: rv = libc_vfcntl (vsock->fd, __cmd, __ap); break; /* cmd handled by vcom socket resource */ case 2: rv = libc_vfcntl (vsock->fd, __cmd, __ap); break; /* cmd handled by vppcom */ case 3: rv = vcom_session_fcntl_va (vsock->sid, __cmd, __ap); break; default: rv = -EINVAL; break; } return rv; } /* * RETURN: 0 - invalid cmd * 1 - cmd not handled by vcom and vppcom * 2 - cmd handled by vcom socket resource * 3 - cmd handled by vppcom */ static int vcom_socket_check_ioctl_cmd (unsigned long int __cmd) { int rc; switch (__cmd) { /* cmd handled by vppcom */ case FIONREAD: rc = 3; break; /* cmd not handled by vcom and vppcom */ default: rc = 1; break; } return rc; } static inline int vcom_session_ioctl_va (int __sid, int __cmd, va_list __ap) { int rv; switch (__cmd) { case FIONREAD: rv = vppcom_session_attr (__sid, VPPCOM_ATTR_GET_NREAD, 0, 0); break; case FIONBIO: { u32 flags = va_arg (__ap, int) ? O_NONBLOCK : 0; u32 len = sizeof (flags); rv = vppcom_session_attr (__sid, VPPCOM_ATTR_SET_FLAGS, &flags, &len); } break; default: rv = -EOPNOTSUPP; break; } return rv; } int vcom_socket_ioctl_va (int __fd, unsigned long int __cmd, va_list __ap) { int rv = -EBADF; vcom_socket_main_t *vsm = &vcom_socket_main; uword *p; vcom_socket_t *vsock; p = hash_get (vsm->sockidx_by_fd, __fd); if (!p) return -EBADF; vsock = pool_elt_at_index (vsm->vsockets, p[0]); if (!vsock) return -ENOTSOCK; if (vsock->type != SOCKET_TYPE_VPPCOM_BOUND) return -EINVAL; switch (vcom_socket_check_ioctl_cmd (__cmd)) { /* Not supported cmd */ case 0: rv = -EOPNOTSUPP; break; /* cmd not handled by vcom and vppcom */ case 1: rv = libc_vioctl (vsock->fd, __cmd, __ap); break; /* cmd handled by vcom socket resource */ case 2: rv = libc_vioctl (vsock->fd, __cmd, __ap); break; /* cmd handled by vppcom */ case 3: rv = vcom_session_ioctl_va (vsock->sid, __cmd, __ap); break; default: rv = -EINVAL; break; } return rv; } static inline int vcom_socket_fds_2_sid_fds ( /* dest */ int *vcom_nsid_fds, fd_set * __restrict vcom_rd_sid_fds, fd_set * __restrict vcom_wr_sid_fds, fd_set * __restrict vcom_ex_sid_fds, /* src */ int vcom_nfds, fd_set * __restrict vcom_readfds, fd_set * __restrict vcom_writefds, fd_set * __restrict vcom_exceptfds) { int rv = 0; int fd; int sid; /* invalid max_sid is -1 */ int max_sid = -1; int nsid = 0; /* * set sid in sid sets corresponding to fd's in fd sets * compute nsid and vcom_nsid_fds from sid sets */ for (fd = 0; fd < vcom_nfds; fd++) { /* * F fd set, src * S sid set, dest */ #define _(S,F) \ if ((F) && (S) && FD_ISSET (fd, (F))) \ { \ sid = vcom_socket_get_sid (fd); \ if (sid != INVALID_SESSION_ID) \ { \ FD_SET (sid, (S)); \ if (sid > max_sid) \ { \ max_sid = sid; \ } \ ++nsid; \ } \ else \ { \ rv = -EBADFD; \ goto done; \ } \ } _(vcom_rd_sid_fds, vcom_readfds); _(vcom_wr_sid_fds, vcom_writefds); _(vcom_ex_sid_fds, vcom_exceptfds); #undef _ } *vcom_nsid_fds = max_sid != -1 ? max_sid + 1 : 0; rv = nsid; done: return rv; } /* * PRE: 00. sid sets were derived from fd sets * 01. sid sets were updated with sids that actually changed * status * 02. fd sets still has watched fds * * This function will modify in place fd sets to indicate which fd's * actually changed status(inferred from sid sets) */ static inline int vcom_socket_sid_fds_2_fds ( /* dest */ int *new_vcom_nfds, int vcom_nfds, fd_set * __restrict vcom_readfds, fd_set * __restrict vcom_writefds, fd_set * __restrict vcom_exceptfds, /* src */ int vcom_nsid_fds, fd_set * __restrict vcom_rd_sid_fds, fd_set * __restrict vcom_wr_sid_fds, fd_set * __restrict vcom_ex_sid_fds) { int rv = 0; int fd; int sid; /* invalid max_fd is -1 */ int max_fd = -1; int nfd = 0; /* * modify in place fd sets to indicate which fd's * actually changed status(inferred from sid sets) */ for (fd = 0; fd < vcom_nfds; fd++) { /* * F fd set, dest * S sid set, src */ #define _(S,F) \ if ((F) && (S) && FD_ISSET (fd, (F))) \ { \ sid = vcom_socket_get_sid (fd); \ if (sid != INVALID_SESSION_ID) \ { \ if (!FD_ISSET (sid, (S))) \ { \ FD_CLR(fd, (F)); \ } \ } \ else \ { \ rv = -EBADFD; \ goto done; \ } \ } _(vcom_rd_sid_fds, vcom_readfds); _(vcom_wr_sid_fds, vcom_writefds); _(vcom_ex_sid_fds, vcom_exceptfds); #undef _ } /* * compute nfd and new_vcom_nfds from fd sets */ for (fd = 0; fd < vcom_nfds; fd++) { #define _(F) \ if ((F) && FD_ISSET (fd, (F))) \ { \ if (fd > max_fd) \ { \ max_fd = fd; \ } \ ++nfd; \ } _(vcom_readfds); _(vcom_writefds); _(vcom_exceptfds); #undef _ } *new_vcom_nfds = max_fd != -1 ? max_fd + 1 : 0; rv = nfd; done: return rv; } /* * PRE: * vom_socket_select is always called with * timeout->tv_sec and timeout->tv_usec set to zero. * hence vppcom_select return immediately. */ /* * TBD: do{body;} while(timeout conditional); timeout loop */ int vcom_socket_select (int vcom_nfds, fd_set * __restrict vcom_readfds, fd_set * __restrict vcom_writefds, fd_set * __restrict vcom_exceptfds, struct timeval *__restrict timeout) { static unsigned long vcom_nsid_fds = 0; int vcom_nsid = 0; int rv = -EBADF; int new_vcom_nfds = 0; int new_vcom_nfd = 0; /* vcom sid fds */ fd_set vcom_rd_sid_fds; fd_set vcom_wr_sid_fds; fd_set vcom_ex_sid_fds; /* in seconds eg. 3.123456789 seconds */ double time_to_wait = (double) 0; /* validate inputs */ if (vcom_nfds < 0) { return -EINVAL; } /* convert timeval timeout to double time_to_wait */ if (timeout) { if (timeout->tv_sec == 0 && timeout->tv_usec == 0) { /* polling: vppcom_select returns immediately */ time_to_wait = (double) 0; } else { /*TBD: use timeval api */ time_to_wait = (double) timeout->tv_sec + (double) timeout->tv_usec / (double) 1000000 + (double) (timeout->tv_usec % 1000000) / (double) 1000000; } } else { /* * no timeout: vppcom_select can block indefinitely * waiting for a file descriptor to become ready * */ /* set to a phantom value */ time_to_wait = ~0; } /* zero the sid_sets */ /* * F fd set * S sid set */ #define _(S,F) \ if ((F)) \ { \ FD_ZERO ((S)); \ } _(&vcom_rd_sid_fds, vcom_readfds); _(&vcom_wr_sid_fds, vcom_writefds); _(&vcom_ex_sid_fds, vcom_exceptfds); #undef _ if (vcom_nfds == 0) { if (time_to_wait > 0) { if (VCOM_DEBUG > 0) fprintf (stderr, "[%d] vcom_socket_select called to " "emulate delay_ns()!\n", getpid ()); rv = vppcom_select (0, NULL, NULL, NULL, time_to_wait); } else { fprintf (stderr, "[%d] vcom_socket_select called vcom_nfds = 0 " "and invalid time_to_wait (%f)!\n", getpid (), time_to_wait); } return 0; } /* populate read, write and except sid_sets */ vcom_nsid = vcom_socket_fds_2_sid_fds ( /* dest */ vcom_readfds || vcom_writefds || vcom_exceptfds ? (int *) &vcom_nsid_fds : NULL, vcom_readfds ? &vcom_rd_sid_fds : NULL, vcom_writefds ? &vcom_wr_sid_fds : NULL, vcom_exceptfds ? &vcom_ex_sid_fds : NULL, /* src */ vcom_nfds, vcom_readfds, vcom_writefds, vcom_exceptfds); if (vcom_nsid < 0) { return vcom_nsid; } rv = vppcom_select (vcom_nsid_fds, vcom_readfds ? (unsigned long *) &vcom_rd_sid_fds : NULL, vcom_writefds ? (unsigned long *) &vcom_wr_sid_fds : NULL, vcom_exceptfds ? (unsigned long *) &vcom_ex_sid_fds : NULL, time_to_wait); if (VCOM_DEBUG > 2) fprintf (stderr, "[%d] called vppcom_select(): " "'%04d'='%04d'\n", getpid (), rv, (int) vcom_nsid_fds); /* check if any file descriptors changed status */ if (rv > 0) { /* * on exit, sets are modified in place to indicate which * file descriptors actually changed status * */ /* * comply with pre-condition * do not clear vcom fd sets befor calling * vcom_socket_sid_fds_2_fds */ new_vcom_nfd = vcom_socket_sid_fds_2_fds ( /* dest */ &new_vcom_nfds, vcom_nfds, vcom_readfds, vcom_writefds, vcom_exceptfds, /* src */ vcom_nsid_fds, vcom_readfds ? &vcom_rd_sid_fds : NULL, vcom_writefds ? &vcom_wr_sid_fds : NULL, vcom_exceptfds ? &vcom_ex_sid_fds : NULL); if (new_vcom_nfd < 0) { return new_vcom_nfd; } if (new_vcom_nfds < 0) { return -EINVAL; } rv = new_vcom_nfd; } return rv; } int vcom_socket_socket (int __domain, int __type, int __protocol) { int rv = -1; vcom_socket_main_t *vsm = &vcom_socket_main; vcom_socket_t *vsock; i32 fd; i32 sid; i32 sockidx; u8 is_nonblocking = __type & SOCK_NONBLOCK ? 1 : 0; int type = __type & ~(SOCK_NONBLOCK | SOCK_CLOEXEC); fd = vcom_socket_open_socket (__domain, __type, __protocol); if (fd < 0) { rv = fd; goto out; } sid = vppcom_session_create (VPPCOM_VRF_DEFAULT, (type == SOCK_DGRAM) ? VPPCOM_PROTO_UDP : VPPCOM_PROTO_TCP, is_nonblocking); if (sid < 0) { rv = sid; goto out_close_socket; } pool_get (vsm->vsockets, vsock); vsocket_init (vsock); sockidx = vsock - vsm->vsockets; hash_set (vsm->sockidx_by_fd, fd, sockidx); vsocket_set (vsock, fd, sid, SOCKET_TYPE_VPPCOM_BOUND); return fd; out_close_socket: vcom_socket_close_socket (fd); out: return rv; } int vcom_socket_socketpair (int __domain, int __type, int __protocol, int __fds[2]) { /* TBD: */ return 0; } int vcom_socket_bind (int __fd, __CONST_SOCKADDR_ARG __addr, socklen_t __len) { int rv = -1; vcom_socket_main_t *vsm = &vcom_socket_main; uword *p; vcom_socket_t *vsock; vppcom_endpt_t ep; p = hash_get (vsm->sockidx_by_fd, __fd); if (!p) return -EBADF; vsock = pool_elt_at_index (vsm->vsockets, p[0]); if (!vsock) return -ENOTSOCK; if (vsock->type != SOCKET_TYPE_VPPCOM_BOUND) return -EINVAL; if (!__addr) { return -EINVAL; } ep.vrf = VPPCOM_VRF_DEFAULT; switch (__addr->sa_family) { case AF_INET: if (__len != sizeof (struct sockaddr_in)) { return -EINVAL; } ep.is_ip4 = VPPCOM_IS_IP4; ep.ip = (u8 *) & ((const struct sockaddr_in *) __addr)->sin_addr; ep.port = (u16) ((const struct sockaddr_in *) __addr)->sin_port; break; case AF_INET6: if (__len != sizeof (struct sockaddr_in6)) { return -EINVAL; } ep.is_ip4 = VPPCOM_IS_IP6; ep.ip = (u8 *) & ((const struct sockaddr_in6 *) __addr)->sin6_addr; ep.port = (u16) ((const struct sockaddr_in6 *) __addr)->sin6_port; break; default: return -1; break; } rv = vppcom_session_bind (vsock->sid, &ep); return rv; } static inline int vcom_session_getsockname (int sid, vppcom_endpt_t * ep) { int rv; uint32_t size = sizeof (*ep); rv = vppcom_session_attr (sid, VPPCOM_ATTR_GET_LCL_ADDR, ep, &size); return rv; } int vcom_socket_getsockname (int __fd, __SOCKADDR_ARG __addr, socklen_t * __restrict __len) { int rv = -1; vcom_socket_main_t *vsm = &vcom_socket_main; uword *p; vcom_socket_t *vsock; p = hash_get (vsm->sockidx_by_fd, __fd); if (!p) return -EBADF; vsock = pool_elt_at_index (vsm->vsockets, p[0]); if (!vsock) return -ENOTSOCK; if (vsock->type != SOCKET_TYPE_VPPCOM_BOUND) return -EINVAL; if (!__addr || !__len) return -EFAULT; vppcom_endpt_t ep; ep.ip = (u8 *) & ((const struct sockaddr_in *) __addr)->sin_addr; rv = vcom_session_getsockname (vsock->sid, &ep); if (rv == 0) { if (ep.vrf == VPPCOM_VRF_DEFAULT) { __addr->sa_family = ep.is_ip4 == VPPCOM_IS_IP4 ? AF_INET : AF_INET6; switch (__addr->sa_family) { case AF_INET: ((struct sockaddr_in *) __addr)->sin_port = ep.port; *__len = sizeof (struct sockaddr_in); break; case AF_INET6: ((struct sockaddr_in6 *) __addr)->sin6_port = ep.port; *__len = sizeof (struct sockaddr_in6); break; default: break; } } } return rv; } int vcom_socket_connect (int __fd, __CONST_SOCKADDR_ARG __addr, socklen_t __len) { int rv = -1; vcom_socket_main_t *vsm = &vcom_socket_main; uword *p; vcom_socket_t *vsock; vppcom_endpt_t ep; p = hash_get (vsm->sockidx_by_fd, __fd); if (p) { vsock = pool_elt_at_index (vsm->vsockets, p[0]); ep.vrf = VPPCOM_VRF_DEFAULT; switch (__addr->sa_family) { case AF_INET: ep.is_ip4 = VPPCOM_IS_IP4; ep.ip = (uint8_t *) & ((const struct sockaddr_in *) __addr)->sin_addr; ep.port = (uint16_t) ((const struct sockaddr_in *) __addr)->sin_port; break; case AF_INET6: ep.is_ip4 = VPPCOM_IS_IP6; ep.ip = (uint8_t *) & ((const struct sockaddr_in6 *) __addr)->sin6_addr; ep.port = (uint16_t) ((const struct sockaddr_in6 *) __addr)->sin6_port; break; default: return -1; break; } rv = vppcom_session_connect (vsock->sid, &ep); } return rv; } static inline int vcom_session_getpeername (int sid, vppcom_endpt_t * ep) { int rv; uint32_t size = sizeof (*ep); rv = vppcom_session_attr (sid, VPPCOM_ATTR_GET_PEER_ADDR, ep, &size); return rv; } int vcom_socket_getpeername (int __fd, __SOCKADDR_ARG __addr, socklen_t * __restrict __len) { int rv = -1; vcom_socket_main_t *vsm = &vcom_socket_main; uword *p; vcom_socket_t *vsock; p = hash_get (vsm->sockidx_by_fd, __fd); if (!p) return -EBADF; vsock = pool_elt_at_index (vsm->vsockets, p[0]); if (!vsock) return -ENOTSOCK; if (vsock->type != SOCKET_TYPE_VPPCOM_BOUND) return -EINVAL; if (!__addr || !__len) return -EFAULT; vppcom_endpt_t ep; ep.ip = (u8 *) & ((const struct sockaddr_in *) __addr)->sin_addr; rv = vcom_session_getpeername (vsock->sid, &ep); if (rv == 0) { if (ep.vrf == VPPCOM_VRF_DEFAULT) { __addr->sa_family = ep.is_ip4 == VPPCOM_IS_IP4 ? AF_INET : AF_INET6; switch (__addr->sa_family) { case AF_INET: ((struct sockaddr_in *) __addr)->sin_port = ep.port; *__len = sizeof (struct sockaddr_in); break; case AF_INET6: ((struct sockaddr_in6 *) __addr)->sin6_port = ep.port; *__len = sizeof (struct sockaddr_in6); break; default: break; } } } return rv; } ssize_t vcom_socket_send (int __fd, const void *__buf, size_t __n, int __flags) { return vcom_socket_sendto (__fd, __buf, __n, __flags, NULL, 0); } ssize_t vcom_socket_recv (int __fd, void *__buf, size_t __n, int __flags) { int rv = -1; rv = vcom_socket_recvfrom (__fd, __buf, __n, __flags, NULL, 0); return rv; } /* * RETURN 1 if __fd is (SOCK_STREAM, SOCK_SEQPACKET), * 0 otherwise * */ int vcom_socket_is_connection_mode_socket (int __fd) { int rv = -1; /* TBD define new vppcom api */ vcom_socket_main_t *vsm = &vcom_socket_main; uword *p; vcom_socket_t *vsock; int type; socklen_t optlen; p = hash_get (vsm->sockidx_by_fd, __fd); if (p) { vsock = pool_elt_at_index (vsm->vsockets, p[0]); if (vsock && vsock->type == SOCKET_TYPE_VPPCOM_BOUND) { optlen = sizeof (type); rv = libc_getsockopt (__fd, SOL_SOCKET, SO_TYPE, &type, &optlen); if (rv != 0) { return 0; } /* get socket type */ switch (type & ~(SOCK_CLOEXEC | SOCK_NONBLOCK)) { case SOCK_STREAM: case SOCK_SEQPACKET: return 1; break; default: return 0; break; } } } return 0; } static inline ssize_t vcom_session_sendto (int __sid, void *__buf, size_t __n, int __flags, __CONST_SOCKADDR_ARG __addr, socklen_t __addr_len) { vppcom_endpt_t *ep = 0; vppcom_endpt_t _ep; if (__addr) { ep = &_ep; ep->vrf = VPPCOM_VRF_DEFAULT; switch (__addr->sa_family) { case AF_INET: ep->is_ip4 = VPPCOM_IS_IP4; ep->ip = (uint8_t *) & ((const struct sockaddr_in *) __addr)->sin_addr; ep->port = (uint16_t) ((const struct sockaddr_in *) __addr)->sin_port; break; case AF_INET6: ep->is_ip4 = VPPCOM_IS_IP6; ep->ip = (uint8_t *) & ((const struct sockaddr_in6 *) __addr)->sin6_addr; ep->port = (uint16_t) ((const struct sockaddr_in6 *) __addr)->sin6_port; break; default: return -EAFNOSUPPORT; } } return vppcom_session_sendto (__sid, __buf, __n, __flags, ep);; } ssize_t vcom_socket_sendto (int __fd, const void *__buf, size_t __n, int __flags, __CONST_SOCKADDR_ARG __addr, socklen_t __addr_len) { vcom_socket_main_t *vsm = &vcom_socket_main; uword *p; vcom_socket_t *vsock; if (!__buf) { return -EINVAL; } p = hash_get (vsm->sockidx_by_fd, __fd); if (!p) return -EBADF; vsock = pool_elt_at_index (vsm->vsockets, p[0]); if (!vsock) return -ENOTSOCK; if (vsock->type != SOCKET_TYPE_VPPCOM_BOUND) { return -EINVAL; } if (vcom_socket_is_connection_mode_socket (__fd)) { /* ignore __addr and _addr_len */ /* and EISCONN may be returned when they are not NULL and 0 */ if ((__addr != NULL) || (__addr_len != 0)) { return -EISCONN; } } else { if (!__addr) { return -EDESTADDRREQ; } /* not a vppcom supported address family */ if (!((__addr->sa_family == AF_INET) || (__addr->sa_family == AF_INET6))) { return -EINVAL; } } return vcom_session_sendto (vsock->sid, (void *) __buf, (int) __n, __flags, __addr, __addr_len); } static inline ssize_t vcom_session_recvfrom (int __sid, void *__restrict __buf, size_t __n, int __flags, __SOCKADDR_ARG __addr, socklen_t * __restrict __addr_len) { int rv; vppcom_endpt_t ep; u8 src_addr[sizeof (struct sockaddr_in6)]; if (__addr) { ep.ip = src_addr; rv = vppcom_session_recvfrom (__sid, __buf, __n, __flags, &ep); if (rv > 0) { if (ep.vrf == VPPCOM_VRF_DEFAULT) { __addr->sa_family = ep.is_ip4 == VPPCOM_IS_IP4 ? AF_INET : AF_INET6; switch (__addr->sa_family) { case AF_INET: ((struct sockaddr_in *) __addr)->sin_port = ep.port; memcpy (&((struct sockaddr_in *) __addr)->sin_addr, src_addr, sizeof (struct in_addr)); *__addr_len = sizeof (struct sockaddr_in); break; case AF_INET6: ((struct sockaddr_in6 *) __addr)->sin6_port = ep.port; memcpy (((struct sockaddr_in6 *) __addr)->sin6_addr. __in6_u.__u6_addr8, src_addr, sizeof (struct in6_addr)); *__addr_len = sizeof (struct sockaddr_in6); break; default: rv = -EAFNOSUPPORT; break; } } else rv = -1; } } else rv = vppcom_session_recvfrom (__sid, __buf, __n, __flags, NULL); return rv; } ssize_t vcom_socket_recvfrom (int __fd, void *__restrict __buf, size_t __n, int __flags, __SOCKADDR_ARG __addr, socklen_t * __restrict __addr_len) { int rv = -1; vcom_socket_main_t *vsm = &vcom_socket_main; uword *p; vcom_socket_t *vsock; if (__addr && !__addr_len) return -EINVAL; p = hash_get (vsm->sockidx_by_fd, __fd); if (!p) return -EBADF; vsock = pool_elt_at_index (vsm->vsockets, p[0]); if (!vsock) return -ENOTSOCK; if (vsock->type != SOCKET_TYPE_VPPCOM_BOUND) { return -EINVAL; } rv = vcom_session_recvfrom (vsock->sid, __buf, __n, __flags, __addr, __addr_len); return rv; } /* TBD: move it to vppcom */ static inline ssize_t vcom_session_sendmsg (int __sid, const struct msghdr *__message, int __flags) { int rv = -1; /* rv = vppcom_session_write (__sid, (void *) __message->__buf, (int)__n); */ return rv; } ssize_t vcom_socket_sendmsg (int __fd, const struct msghdr * __message, int __flags) { int rv = -1; vcom_socket_main_t *vsm = &vcom_socket_main; uword *p; vcom_socket_t *vsock; p = hash_get (vsm->sockidx_by_fd, __fd); if (!p) return -EBADF; vsock = pool_elt_at_index (vsm->vsockets, p[0]); if (!vsock) return -ENOTSOCK; if (vcom_socket_is_connection_mode_socket (__fd)) { /* ignore __addr and _addr_len */ /* and EISCONN may be returned when they are not NULL and 0 */ if ((__message->msg_name != NULL) || (__message->msg_namelen != 0)) { return -EISCONN; } } else { /* TBD: validate __message->msg_name and __message->msg_namelen * and return -EINVAL on validation error * */ ; } rv = vcom_session_sendmsg (vsock->sid, __message, __flags); return rv; } #ifdef __USE_GNU int vcom_socket_sendmmsg (int __fd, struct mmsghdr *__vmessages, unsigned int __vlen, int __flags) { /* TBD: define a new vppcom api */ return 0; } #endif /* TBD: move it to vppcom */ static inline ssize_t vcom_session_recvmsg (int __sid, struct msghdr *__message, int __flags) { int rv = -1; /* rv = vppcom_session_read (__sid, (void *) __message->__buf, (int)__n); */ rv = -EOPNOTSUPP; return rv; } ssize_t vcom_socket_recvmsg (int __fd, struct msghdr * __message, int __flags) { int rv = -1; vcom_socket_main_t *vsm = &vcom_socket_main; uword *p; vcom_socket_t *vsock; p = hash_get (vsm->sockidx_by_fd, __fd); if (!p) return -EBADF; vsock = pool_elt_at_index (vsm->vsockets, p[0]); if (!vsock) return -ENOTSOCK; if (vsock->type != SOCKET_TYPE_VPPCOM_BOUND) return -EINVAL; if (!__message) { return -EINVAL; } /* validate __flags */ rv = vcom_session_recvmsg (vsock->sid, __message, __flags); return rv; } #ifdef __USE_GNU int vcom_socket_recvmmsg (int __fd, struct mmsghdr *__vmessages, unsigned int __vlen, int __flags, struct timespec *__tmo) { /* TBD: define a new vppcom api */ return 0; } #endif /* TBD: move it to vppcom */ static inline int vcom_session_get_sockopt (int __sid, int __level, int __optname, void *__restrict __optval, socklen_t * __restrict __optlen) { int rv = 0; /* 1. for socket level options that are NOT socket attributes * and that has corresponding vpp options get from vppcom */ switch (__level) { case SOL_SOCKET: switch (__optname) { case SO_ERROR: *(int *) __optval = 0; break; default: break; } default: break; } /* 2. unhandled options */ return rv; } int vcom_socket_getsockopt (int __fd, int __level, int __optname, void *__restrict __optval, socklen_t * __restrict __optlen) { int rv = -1; vcom_socket_main_t *vsm = &vcom_socket_main; uword *p; vcom_socket_t *vsock; if (!__optval || !__optlen) return -EINVAL; p = hash_get (vsm->sockidx_by_fd, __fd); if (!p) return -EBADF; vsock = pool_elt_at_index (vsm->vsockets, p[0]); if (!vsock) return -ENOTSOCK; if (vsock->type != SOCKET_TYPE_VPPCOM_BOUND) return -EINVAL; switch (__level) { case SOL_SOCKET: switch (__optname) { /* * 1. for socket level options that are socket attributes, * get from libc_getsockopt. * 2. for socket level options that are NOT socket * attributes and that has corresponding vpp options * get from vppcom. * 3. for socket level options unimplemented * return -ENOPROTOOPT */ case SO_DEBUG: case SO_DONTROUTE: case SO_BROADCAST: case SO_SNDBUF: case SO_RCVBUF: case SO_REUSEADDR: case SO_REUSEPORT: case SO_KEEPALIVE: case SO_TYPE: case SO_PROTOCOL: case SO_DOMAIN: case SO_OOBINLINE: case SO_NO_CHECK: case SO_PRIORITY: case SO_LINGER: case SO_BSDCOMPAT: case SO_TIMESTAMP: case SO_TIMESTAMPNS: case SO_TIMESTAMPING: case SO_RCVTIMEO: case SO_SNDTIMEO: case SO_RCVLOWAT: case SO_SNDLOWAT: case SO_PASSCRED: case SO_PEERCRED: case SO_PEERNAME: case SO_ACCEPTCONN: case SO_PASSSEC: case SO_PEERSEC: case SO_MARK: case SO_RXQ_OVFL: case SO_WIFI_STATUS: case SO_PEEK_OFF: case SO_NOFCS: case SO_BINDTODEVICE: case SO_GET_FILTER: case SO_LOCK_FILTER: case SO_BPF_EXTENSIONS: case SO_SELECT_ERR_QUEUE: #ifdef CONFIG_NET_RX_BUSY_POLL case SO_BUSY_POLL: #endif case SO_MAX_PACING_RATE: #ifdef SO_INCOMING_CPU case SO_INCOMING_CPU: #endif rv = libc_getsockopt (__fd, __level, __optname, __optval, __optlen); if (rv != 0) { rv = -errno; return rv; } break; case SO_ERROR: rv = vcom_session_get_sockopt (vsock->sid, __level, __optname, __optval, __optlen); break; default: /* We implement the SO_SNDLOWAT etc to not be settable * (1003.1g 7). */ return -ENOPROTOOPT; } break; default: /* 1. handle options that are NOT socket level options, * but have corresponding vpp otions. */ rv = vcom_session_get_sockopt (vsock->sid, __level, __optname, __optval, __optlen); break; } return rv; } /* TBD: move it to vppcom */ static inline int vcom_session_setsockopt (int __sid, int __level, int __optname, const void *__optval, socklen_t __optlen) { int rv = -EOPNOTSUPP; switch (__level) { case SOL_TCP: switch (__optname) { case TCP_KEEPIDLE: rv = vppcom_session_attr (__sid, VPPCOM_ATTR_SET_TCP_KEEPIDLE, 0, 0); break; case TCP_KEEPINTVL: rv = vppcom_session_attr (__sid, VPPCOM_ATTR_SET_TCP_KEEPINTVL, 0, 0); break; default: break; } break; case SOL_IPV6: switch (__optname) { case IPV6_V6ONLY: rv = vppcom_session_attr (__sid, VPPCOM_ATTR_SET_V6ONLY, 0, 0); break; default: break; } break; case SOL_SOCKET: switch (__optname) { case SO_KEEPALIVE: rv = vppcom_session_attr (__sid, VPPCOM_ATTR_SET_KEEPALIVE, 0, 0); break; case SO_REUSEADDR: rv = vppcom_session_attr (__sid, VPPCOM_ATTR_SET_REUSEADDR, 0, 0); break; case SO_BROADCAST: rv = vppcom_session_attr (__sid, VPPCOM_ATTR_SET_BROADCAST, 0, 0); break; default: break; } break; default: break; } return rv; } int vcom_socket_setsockopt (int __fd, int __level, int __optname, const void *__optval, socklen_t __optlen) { int rv = -1; vcom_socket_main_t *vsm = &vcom_socket_main; uword *p; vcom_socket_t *vsock; p = hash_get (vsm->sockidx_by_fd, __fd); if (!p) return -EBADF; vsock = pool_elt_at_index (vsm->vsockets, p[0]); if (!vsock) return -ENOTSOCK; if (vsock->type != SOCKET_TYPE_VPPCOM_BOUND) return -EINVAL; /* * Options without arguments */ if (__optname == SO_BINDTODEVICE) { rv = libc_setsockopt (__fd, __level, __optname, __optval, __optlen); if (rv != 0) { rv = -errno; } return rv; } if (!__optval) return -EFAULT; if (__optlen < sizeof (int)) return -EINVAL; switch (__level) { case SOL_IPV6: switch (__optname) { case IPV6_V6ONLY: rv = vcom_session_setsockopt (vsock->sid, __level, __optname, __optval, __optlen); break; default: return -EOPNOTSUPP; } break; case SOL_TCP: switch (__optname) { case TCP_NODELAY: return 0; case TCP_KEEPIDLE: case TCP_KEEPINTVL: rv = vcom_session_setsockopt (vsock->sid, __level, __optname, __optval, __optlen); break; default: return -EOPNOTSUPP; } break; /* handle options at socket level */ case SOL_SOCKET: switch (__optname) { case SO_REUSEADDR: case SO_BROADCAST: case SO_KEEPALIVE: rv = vcom_session_setsockopt (vsock->sid, __level, __optname, __optval, __optlen); break; /* * 1. for socket level options that are socket attributes, * set it from libc_getsockopt * 2. for socket level options that are NOT socket * attributes and that has corresponding vpp options * set it from vppcom * 3. for socket level options unimplemented * return -ENOPROTOOPT */ case SO_DEBUG: case SO_DONTROUTE: case SO_SNDBUF: case SO_RCVBUF: case SO_REUSEPORT: case SO_TYPE: case SO_PROTOCOL: case SO_DOMAIN: case SO_ERROR: case SO_OOBINLINE: case SO_NO_CHECK: case SO_PRIORITY: case SO_LINGER: case SO_BSDCOMPAT: case SO_TIMESTAMP: case SO_TIMESTAMPNS: case SO_TIMESTAMPING: case SO_RCVTIMEO: case SO_SNDTIMEO: case SO_RCVLOWAT: case SO_SNDLOWAT: case SO_PASSCRED: case SO_PEERCRED: case SO_PEERNAME: case SO_ACCEPTCONN: case SO_PASSSEC: case SO_PEERSEC: case SO_MARK: case SO_RXQ_OVFL: case SO_WIFI_STATUS: case SO_PEEK_OFF: case SO_NOFCS: /* * SO_BINDTODEVICE already handled as * "Options without arguments" */ /* case SO_BINDTODEVICE: */ case SO_GET_FILTER: case SO_LOCK_FILTER: case SO_BPF_EXTENSIONS: case SO_SELECT_ERR_QUEUE: #ifdef CONFIG_NET_RX_BUSY_POLL case SO_BUSY_POLL: #endif case SO_MAX_PACING_RATE: #ifdef SO_INCOMING_CPU case SO_INCOMING_CPU: #endif rv = libc_setsockopt (__fd, __level, __optname, __optval, __optlen); if (rv != 0) { rv = -errno; return rv; } break; default: /* We implement the SO_SNDLOWAT etc to not be settable * (1003.1g 7). */ return -ENOPROTOOPT; } break; default: return -ENOPROTOOPT; } return rv; } int vcom_socket_listen (int __fd, int __n) { int rv = -1; vcom_socket_main_t *vsm = &vcom_socket_main; uword *p; vcom_socket_t *vsock; p = hash_get (vsm->sockidx_by_fd, __fd); if (p) { vsock = pool_elt_at_index (vsm->vsockets, p[0]); /* TBD vppcom to accept __n parameter */ rv = vppcom_session_listen (vsock->sid, __n); } return rv; } static int vcom_socket_connected_socket (int __fd, int __sid, int *__domain, int *__type, int *__protocol, int flags) { int rv = -1; vcom_socket_main_t *vsm = &vcom_socket_main; vcom_socket_t *vsock; i32 fd; i32 sockidx; socklen_t optlen; optlen = sizeof (*__domain); rv = libc_getsockopt (__fd, SOL_SOCKET, SO_DOMAIN, __domain, &optlen); if (rv != 0) { rv = -errno; goto out; } optlen = sizeof (*__type); rv = libc_getsockopt (__fd, SOL_SOCKET, SO_TYPE, __type, &optlen); if (rv != 0) { rv = -errno; goto out; } optlen = sizeof (*__protocol); rv = libc_getsockopt (__fd, SOL_SOCKET, SO_PROTOCOL, __protocol, &optlen); if (rv != 0) { rv = -errno; goto out; } fd = vcom_socket_open_socket (*__domain, *__type | flags, *__protocol); if (fd < 0) { rv = fd; goto out; } pool_get (vsm->vsockets, vsock); vsocket_init (vsock); sockidx = vsock - vsm->vsockets; hash_set (vsm->sockidx_by_fd, fd, sockidx); vsocket_set (vsock, fd, __sid, SOCKET_TYPE_VPPCOM_BOUND); return fd; out: return rv; } /* If flag is 0, then accept4() is the same as accept(). * SOCK_NONBLOCK and SOCK_CLOEXEC can be bitwise ORed in flags */ static int vcom_socket_accept_flags (int __fd, __SOCKADDR_ARG __addr, socklen_t * __restrict __addr_len, int flags) { int rv = -1; vcom_socket_main_t *vsm = &vcom_socket_main; uword *p; vcom_socket_t *vsock; int fd; int sid; int domain; int type; int protocol; uint8_t addr8[sizeof (struct in6_addr)]; vppcom_endpt_t ep; ep.ip = addr8; /* validate flags */ /* * for documentation * switch (flags) * { * case 0: * case SOCK_NONBLOCK: * case SOCK_CLOEXEC: * case SOCK_NONBLOCK | SOCK_CLOEXEC: * break; * * default: * return -1; * } */ /* flags can be 0 or can be bitwise OR * of any of SOCK_NONBLOCK and SOCK_CLOEXEC */ if (VCOM_DEBUG > 2) fprintf (stderr, "[%d] vcom_socket_accept_flags: " "fd = %d, __addr = %p, __addr_len = %p flags = %d (0x%x)\n", getpid (), __fd, __addr, __addr_len, flags, flags); if (!(!flags || (flags & (SOCK_NONBLOCK | SOCK_CLOEXEC)))) { /* TBD: return proper error code */ fprintf (stderr, "[%d] ERROR: vcom_socket_accept_flags: " "invalid flags = %d (0x%x)\n", getpid (), flags, flags); return -1; } /* TBD: return proper error code */ if (!vcom_socket_is_connection_mode_socket (__fd)) { fprintf (stderr, "[%d] ERROR: vcom_socket_accept_flags: " "connection mode socket support TBD!\n", getpid ()); return -EOPNOTSUPP; } p = hash_get (vsm->sockidx_by_fd, __fd); if (p) { vsock = pool_elt_at_index (vsm->vsockets, p[0]); rv = vcom_fcntl (vsock->fd, F_GETFL, 0); if (rv < 0) { fprintf (stderr, "[%d] ERROR: vcom_socket_accept_flags: " "vcom_fcnt() returned %d!\n", getpid (), rv); return rv; } /* is blocking */ if (!(rv & O_NONBLOCK)) { /* socket is not marked as nonblocking * and no pending connections are present * on the queue, accept () blocks the caller * until a connection is present. */ rv = vppcom_session_accept (vsock->sid, &ep, -1.0 /* wait forever */ ); } else { /* The file descriptor refers to a socket and has been * marked nonblocking(O_NONBLOCK) and the accept would * block. * */ /* is non blocking */ rv = vppcom_session_accept (vsock->sid, &ep, 0); /* If the socket is marked nonblocking and * no pending connections are present on the * queue, accept fails with the error * EAGAIN or EWOULDBLOCK */ if (rv == VPPCOM_ETIMEDOUT) { rv = VPPCOM_EAGAIN; } } if (rv < 0) { if (rv != VPPCOM_EAGAIN) fprintf (stderr, "[%d] ERROR: vcom_socket_accept_flags: " "vppcom_session_accept() returned %d!", getpid (), rv); return rv; } sid = rv; /* create a new connected socket resource and set flags * on the new file descriptor. * update vsockets and sockidx_by_fd table * */ fd = vcom_socket_connected_socket (__fd, sid, &domain, &type, &protocol, flags); if (fd < 0) { fprintf (stderr, "[%d] ERROR: vcom_socket_accept_flags: " "vcom_socket_connected_socket() returned %d!", getpid (), rv); return fd; } rv = fd; /* TBD populate __addr and __addr_len */ /* TBD: The returned address is truncated if the buffer * provided is too small, in this case, __addr_len will * return a value greater than was supplied to the call.*/ if (__addr) { if (ep.is_cut_thru) { /* TBD populate __addr and __addr_len */ switch (domain) { case AF_INET: ((struct sockaddr_in *) __addr)->sin_family = AF_INET; ((struct sockaddr_in *) __addr)->sin_port = ep.port; memcpy (&((struct sockaddr_in *) __addr)->sin_addr, addr8, sizeof (struct in_addr)); /* TBD: populate __addr_len */ if (__addr_len) { *__addr_len = sizeof (struct sockaddr_in); } break; case AF_INET6: ((struct sockaddr_in6 *) __addr)->sin6_family = AF_INET6; ((struct sockaddr_in6 *) __addr)->sin6_port = ep.port; memcpy (((struct sockaddr_in6 *) __addr)->sin6_addr. __in6_u.__u6_addr8, addr8, sizeof (struct in6_addr)); /* TBD: populate __addr_len */ if (__addr_len) { *__addr_len = sizeof (struct sockaddr_in6); } break; default: return -EAFNOSUPPORT; } } else { switch (ep.is_ip4) { case VPPCOM_IS_IP4: ((struct sockaddr_in *) __addr)->sin_family = AF_INET; ((struct sockaddr_in *) __addr)->sin_port = ep.port; memcpy (&((struct sockaddr_in *) __addr)->sin_addr, addr8, sizeof (struct in_addr)); /* TBD: populate __addr_len */ if (__addr_len) { *__addr_len = sizeof (struct sockaddr_in); } break; case VPPCOM_IS_IP6: ((struct sockaddr_in6 *) __addr)->sin6_family = AF_INET6; ((struct sockaddr_in6 *) __addr)->sin6_port = ep.port; memcpy (((struct sockaddr_in6 *) __addr)->sin6_addr. __in6_u.__u6_addr8, addr8, sizeof (struct in6_addr)); /* TBD: populate __addr_len */ if (__addr_len) { *__addr_len = sizeof (struct sockaddr_in6); } break; default: return -EAFNOSUPPORT; } } } } return rv; } int vcom_socket_accept (int __fd, __SOCKADDR_ARG __addr, socklen_t * __restrict __addr_len) { /* set flags to 0 for accept() */ return vcom_socket_accept_flags (__fd, __addr, __addr_len, 0); } int vcom_socket_accept4 (int __fd, __SOCKADDR_ARG __addr, socklen_t * __restrict __addr_len, int __flags) { /* SOCK_NONBLOCK and SOCK_CLOEXEC can be bitwise ORed in flags */ return vcom_socket_accept_flags (__fd, __addr, __addr_len, __flags); } /* TBD: move it to vppcom */ static inline int vcom_session_shutdown (int __fd, int __how) { return 0; } int vcom_socket_shutdown (int __fd, int __how) { int rv = -1; vcom_socket_main_t *vsm = &vcom_socket_main; uword *p; vcom_socket_t *vsock; p = hash_get (vsm->sockidx_by_fd, __fd); if (p) { vsock = pool_elt_at_index (vsm->vsockets, p[0]); switch (__how) { case SHUT_RD: case SHUT_WR: case SHUT_RDWR: rv = vcom_session_shutdown (vsock->sid, __how); return rv; break; default: return -EINVAL; break; } } return rv; } int vcom_socket_epoll_create1 (int __flags) { int rv = -1; vcom_socket_main_t *vsm = &vcom_socket_main; vcom_epoll_t *vepoll; i32 epfd; i32 vep_idx; i32 epollidx; epfd = vcom_socket_open_epoll (__flags); if (epfd < 0) { rv = epfd; goto out; } vep_idx = vppcom_epoll_create (); if (vep_idx < 0) { rv = vep_idx; goto out_close_epoll; } pool_get (vsm->vepolls, vepoll); vepoll_init (vepoll); epollidx = vepoll - vsm->vepolls; hash_set (vsm->epollidx_by_epfd, epfd, epollidx); vepoll_set (vepoll, epfd, vep_idx, EPOLL_TYPE_VPPCOM_BOUND, __flags, 0, 0); return epfd; out_close_epoll: vcom_socket_close_epoll (epfd); out: return rv; } /* * PRE: vppcom_epoll_ctl() is successful * free_vepitem_on_del : 0 - no_pool_put, 1 - pool_put */ int vcom_socket_ctl_vepitem (int __epfd, int __op, int __fd, struct epoll_event *__event, i32 vep_idx, vcom_epoll_t * vepoll, i32 vfd_id, void *vfd, vcom_fd_type_t type, int free_vepitem_on_del) { int rv = -1; vcom_socket_main_t *vsm = &vcom_socket_main; vcom_epitem_t *vepitem; vcom_epitem_key_t epfdfd = {.epfd = __epfd,.fd = __fd }; uword *p; i32 vepitemidx; i32 *vepitemidxs = 0; struct epoll_event revent = {.events = 0,.data.fd = INVALID_FD }; i32 vec_idx; /* perform control operations on the epoll instance */ switch (__op) { case EPOLL_CTL_ADD: /* * supplied file descriptor is already * registered with this epoll instance * */ /* vepitem exists */ p = hash_get (vsm->epitemidx_by_epfdfd, epfdfd.key); if (p) { rv = -EEXIST; goto out; } /* add a new vepitem */ pool_get (vsm->vepitems, vepitem); vepitem_init (vepitem); vepitemidx = vepitem - vsm->vepitems; hash_set (vsm->epitemidx_by_epfdfd, epfdfd.key, vepitemidx); vepitem_set (vepitem, __epfd, __fd, __fd, __fd, type, *__event, revent); /* update epitemidxs */ /* by_epfd */ p = hash_get (vsm->epitemidxs_by_epfd, __epfd); if (!p) /* not exist */ { vepitemidxs = 0; vec_add1 (vepitemidxs, vepitemidx); hash_set (vsm->epitemidxs_by_epfd, __epfd, vepitemidxs); } else /* exists */ { vepitemidxs = *(i32 **) p; vec_add1 (vepitemidxs, vepitemidx); hash_set3 (vsm->epitemidxs_by_epfd, __epfd, vepitemidxs, 0); } /* update epitemidxs */ /* by_fd */ p = hash_get (vsm->epitemidxs_by_fd, __fd); if (!p) /* not exist */ { vepitemidxs = 0; vec_add1 (vepitemidxs, vepitemidx); hash_set (vsm->epitemidxs_by_fd, __fd, vepitemidxs); } else /* exists */ { vepitemidxs = *(i32 **) p; vec_add1 (vepitemidxs, vepitemidx); hash_set3 (vsm->epitemidxs_by_fd, __fd, vepitemidxs, 0); } /* increment vepoll fd count by 1 */ vepoll->count += 1; rv = 0; goto out; break; case EPOLL_CTL_MOD: /* * supplied file descriptor is not * registered with this epoll instance * */ /* vepitem not exist */ p = hash_get (vsm->epitemidx_by_epfdfd, epfdfd.key); if (!p) { rv = -ENOENT; goto out; } vepitem = pool_elt_at_index (vsm->vepitems, p[0]); if (vepitem) { vepitem->event = *__event; vepitem->revent = revent; } rv = 0; goto out; break; case EPOLL_CTL_DEL: /* * supplied file descriptor is not * registered with this epoll instance * */ /* vepitem not exist */ p = hash_get (vsm->epitemidx_by_epfdfd, epfdfd.key); if (!p) { rv = -ENOENT; goto out; } vepitemidx = *(i32 *) p; hash_unset (vsm->epitemidx_by_epfdfd, epfdfd.key); /* update epitemidxs */ /* by_epfd */ p = hash_get (vsm->epitemidxs_by_epfd, __epfd); if (!p) /* not exist */ { rv = -ENOENT; goto out; } else /* exists */ { vepitemidxs = *(i32 **) p; vec_idx = vec_search (vepitemidxs, vepitemidx); if (vec_idx != ~0) { vec_del1 (vepitemidxs, vec_idx); if (!vec_len (vepitemidxs)) { vec_free (vepitemidxs); hash_unset (vsm->epitemidxs_by_epfd, __epfd); } } } /* update epitemidxs */ /* by_fd */ p = hash_get (vsm->epitemidxs_by_fd, __fd); if (!p) /* not exist */ { rv = -ENOENT; goto out; } else /* exists */ { vepitemidxs = *(i32 **) p; vec_idx = vec_search (vepitemidxs, vepitemidx); if (vec_idx != ~0) { vec_del1 (vepitemidxs, vec_idx); if (!vec_len (vepitemidxs)) { vec_free (vepitemidxs); hash_unset (vsm->epitemidxs_by_fd, __fd); } } } /* pool put vepitem */ vepitem = pool_elt_at_index (vsm->vepitems, vepitemidx); if (free_vepitem_on_del) { if (!vepitem) { rv = -ENOENT; goto out; } vepitem_init (vepitem); pool_put (vsm->vepitems, vepitem); } else { if (!vepitem) { vepitem_init (vepitem); } } /* decrement vepoll fd count by 1 */ vepoll->count -= 1; rv = 0; goto out; break; default: rv = -EINVAL; goto out; break; } out: return rv; } /* * PRE: 00. null pointer check on __event * 01. all other parameters are validated */ static int vcom_socket_epoll_ctl_internal (int __epfd, int __op, int __fd, struct epoll_event *__event, int free_vepitem_on_del) { int rv = -1; i32 cnt; vcom_epoll_t *vepoll; vcom_socket_t *vfd_vsock; i32 vep_idx; i32 sid; /* get vep_idx and vepoll */ vep_idx = vcom_socket_get_vep_idx_and_vepoll (__epfd, &vepoll); if (vep_idx == INVALID_VEP_IDX) { return -EBADF; } /* get vcom fd type, vfd_id and vfd */ sid = vcom_socket_get_sid_and_vsock (__fd, &vfd_vsock); if ((sid != INVALID_SESSION_ID) && vcom_socket_type_is_vppcom_bound (vfd_vsock->type)) { rv = vppcom_epoll_ctl (vep_idx, __op, sid, __event); if (rv == VPPCOM_OK) { cnt = ((__op == EPOLL_CTL_ADD) ? 1 : (__op == EPOLL_CTL_DEL) ? -1 : 0); vepoll->count += cnt; vepoll->vcl_cnt += cnt; } if (VCOM_DEBUG > 0) fprintf (stderr, "[%d] vcom_socket_epoll_ctl_i: vppcom_epoll_ctl() " "returned %d\n\tepfd %d, vep_idx %d, fd %d sid %d op %d" "\n\tcount %d, vcl_cnt %d, libc_cnt %d\n", getpid (), rv, __epfd, vep_idx, __fd, sid, __op, vepoll->count, vepoll->vcl_cnt, vepoll->libc_cnt); } else { rv = libc_epoll_ctl (__epfd, __op, __fd, __event); if (rv == 0) { cnt = ((__op == EPOLL_CTL_ADD) ? 1 : (__op == EPOLL_CTL_DEL) ? -1 : 0); vepoll->count += cnt; vepoll->libc_cnt += cnt; } if (VCOM_DEBUG > 0) fprintf (stderr, "[%d] vcom_socket_epoll_ctl_i: libc_epoll_ctl() " "returned %d\n\tepfd %d, vep_idx %d, fd %d sid %d op %d" "\n\tcount %d, vcl_cnt %d, libc_cnt %d\n", getpid (), rv, __epfd, vep_idx, __fd, sid, __op, vepoll->count, vepoll->vcl_cnt, vepoll->libc_cnt); } return rv; } int vcom_socket_epoll_ctl (int __epfd, int __op, int __fd, struct epoll_event *__event) { int rv = -1; rv = vcom_socket_epoll_ctl_internal (__epfd, __op, __fd, __event, 1); return rv; } static int vcom_socket_epoll_ctl1 (int __epfd, int __op, int __fd, struct epoll_event *__event) { int rv = -1; rv = vcom_socket_epoll_ctl_internal (__epfd, __op, __fd, __event, 0); return rv; } int vcom_socket_epoll_pwait (int __epfd, struct epoll_event *__events, int __maxevents, int __timeout, const __sigset_t * __ss) { vcom_socket_main_t *vsm = &vcom_socket_main; int rv = -EBADF; int rv2; double time_to_wait = (double) 0; double timeout, now = 0; vcom_epoll_t *vepoll; i32 vep_idx; static struct epoll_event *libc_ev = 0; /* validate __event */ if (!__events || (__timeout < -1)) { fprintf (stderr, "[%d] ERROR: vcom_socket_epoll_pwait: " "Bad args __events %p, __timeout %d\n", getpid (), __events, __timeout); rv = -EFAULT; goto out; } time_to_wait = ((__timeout >= 0) ? (double) __timeout / (double) 1000 : 0); vep_idx = vcom_socket_get_vep_idx_and_vepoll (__epfd, &vepoll); if (vep_idx == INVALID_VEP_IDX) { fprintf (stderr, "[%d] ERROR: vcom_socket_epoll_pwait: " "Bad epoll fd %d\n", getpid (), __epfd); return -EBADF; } if (vepoll->count <= 0) { fprintf (stderr, "[%d] ERROR: vcom_socket_epoll_pwait: No events" " in epfd!\n\tcount %d, vcl_cnt %d, libc_cnt %d\n", getpid (), vepoll->count, vepoll->vcl_cnt, vepoll->libc_cnt); rv = -EINVAL; goto out; } if (vepoll->libc_cnt == 0) { if (VCOM_DEBUG > 2) fprintf (stderr, "[%d] vcom_socket_epoll_pwait: libc_cnt = 0, " "calling vppcom_epoll_wait() time_to_wait = %f\n", getpid (), time_to_wait); rv = vppcom_epoll_wait (vep_idx, __events, __maxevents, time_to_wait); } else if (vepoll->vcl_cnt == 0) { if (VCOM_DEBUG > 2) fprintf (stderr, "[%d] vcom_socket_epoll_pwait: vcl_cnt = 0, " "calling libc_epoll_pwait()\n", getpid ()); rv = libc_epoll_pwait (__epfd, __events, __maxevents, __timeout, __ss); } else { if (VCOM_DEBUG > 2) fprintf (stderr, "[%d] vcom_socket_epoll_pwait: vcl_cnt = %d, " "libc_cnt = %d -> mixed polling (time_to_wait = %f, " "__timeout = %d)\n", getpid (), vepoll->vcl_cnt, vepoll->libc_cnt, time_to_wait, __timeout); vec_validate (libc_ev, __maxevents); timeout = clib_time_now (&vsm->clib_time) + time_to_wait; do { rv = vppcom_epoll_wait (vep_idx, __events, __maxevents, 0); rv2 = libc_epoll_pwait (__epfd, libc_ev, __maxevents, 1, __ss); if (VCOM_DEBUG == 666) fprintf (stderr, "[%d] vcom_socket_epoll_pwait: " "rv = %d, rv2 = %d, timeout = %f, now = %f\n", getpid (), rv, rv2, timeout, now); if ((rv > 0) || (rv2 > 0)) { if (VCOM_DEBUG > 2) fprintf (stderr, "[%d] vcom_socket_epoll_pwait: " "rv = %d, rv2 = %d\n", getpid (), rv, rv2); int n = __maxevents - rv; n = rv2 <= n ? rv2 : n; rv = (rv > 0) ? rv : 0; clib_memcpy (&__events[rv], libc_ev, n * sizeof (*libc_ev)); rv += rv2; goto out; } else if ((rv < 0) || (rv2 < 0)) { if (rv < 0) fprintf (stderr, "[%d] ERROR: vppcom_epoll_wait() returned %d\n", getpid (), rv); if (rv2 < 0) { fprintf (stderr, "[%d] ERROR: libc_epoll_wait() failed, errno %d\n", getpid (), errno); rv = (rv < 0) ? rv : -errno; } goto out; } if (__timeout != -1) now = clib_time_now (&vsm->clib_time); } while (now < timeout); } out: vec_reset_length (libc_ev); return rv; } static inline void vcom_pollfds_2_selectfds ( /* src */ struct pollfd *__fds, nfds_t __nfds, /* dest */ int vcom_nfds, fd_set * __restrict vcom_readfds, fd_set * __restrict vcom_writefds, fd_set * __restrict vcom_exceptfds) { nfds_t fds_idx = 0; for (fds_idx = 0; fds_idx < __nfds; fds_idx++) { /* ignore negative fds */ if (__fds[fds_idx].fd < 0) { continue; } /* for POLLRDHUP, POLLERR, POLLHUP and POLLNVAL */ FD_SET (__fds[fds_idx].fd, vcom_exceptfds); /* requested events */ if (__fds[fds_idx].events) { if (__fds[fds_idx].events & POLLIN) { FD_SET (__fds[fds_idx].fd, vcom_readfds); } if (__fds[fds_idx].events & POLLPRI) { FD_SET (__fds[fds_idx].fd, vcom_readfds); } if (__fds[fds_idx].events & POLLOUT) { FD_SET (__fds[fds_idx].fd, vcom_writefds); } #if defined __USE_XOPEN || defined __USE_XOPEN2K8 if (__fds[fds_idx].events & POLLRDNORM) { FD_SET (__fds[fds_idx].fd, vcom_readfds); } if (__fds[fds_idx].events & POLLRDBAND) { FD_SET (__fds[fds_idx].fd, vcom_readfds); } if (__fds[fds_idx].events & POLLWRNORM) { FD_SET (__fds[fds_idx].fd, vcom_writefds); } if (__fds[fds_idx].events & POLLWRBAND) { FD_SET (__fds[fds_idx].fd, vcom_writefds); } #endif } } /* for (fds_idx = 0; fds_idx < __nfds; fds_idx++) */ } static inline void vcom_selectfds_2_pollfds ( /* dest */ struct pollfd *__fds, nfds_t __nfds, int *nfd, /* src */ int vcom_nfds, fd_set * __restrict vcom_readfds, fd_set * __restrict vcom_writefds, fd_set * __restrict vcom_exceptfds) { nfds_t fds_idx = 0; for (fds_idx = 0; fds_idx < __nfds; fds_idx++) { /* ignore negative fds */ if (__fds[fds_idx].fd < 0) { __fds[fds_idx].revents = 0; } /* for POLLRDHUP, POLLERR, POLLHUP and POLLNVAL */ if (FD_ISSET (__fds[fds_idx].fd, vcom_exceptfds)) { /* * TBD: for now any select exception * is flagged as POLLERR * */ __fds[fds_idx].revents |= POLLERR; } /* requested events */ if (__fds[fds_idx].events & POLLIN) { if (FD_ISSET (__fds[fds_idx].fd, vcom_readfds)) { __fds[fds_idx].revents |= POLLIN; } } if (__fds[fds_idx].events & POLLPRI) { if (FD_ISSET (__fds[fds_idx].fd, vcom_readfds)) { __fds[fds_idx].revents |= POLLIN; } } if (__fds[fds_idx].events & POLLOUT) { if (FD_ISSET (__fds[fds_idx].fd, vcom_writefds)) { __fds[fds_idx].revents |= POLLOUT; } } #if defined __USE_XOPEN || defined __USE_XOPEN2K8 if (__fds[fds_idx].events & POLLRDNORM) { if (FD_ISSET (__fds[fds_idx].fd, vcom_readfds)) { __fds[fds_idx].revents |= POLLRDNORM; } } if (__fds[fds_idx].events & POLLRDBAND) { if (FD_ISSET (__fds[fds_idx].fd, vcom_readfds)) { __fds[fds_idx].revents |= POLLRDBAND; } } if (__fds[fds_idx].events & POLLWRNORM) { if (FD_ISSET (__fds[fds_idx].fd, vcom_writefds)) { __fds[fds_idx].revents |= POLLWRNORM; } } if (__fds[fds_idx].events & POLLWRBAND) { if (FD_ISSET (__fds[fds_idx].fd, vcom_writefds)) { __fds[fds_idx].revents |= POLLWRBAND; } } #endif } /* for (fds_idx = 0; fds_idx < __nfds; fds_idx++) */ /* * nfd: * the number of structures which have nonzero revents fields * (in other words, those descriptors with events or * errors reported) * */ *nfd = 0; for (fds_idx = 0; fds_idx < __nfds; fds_idx++) { /* ignore negative fds */ if (__fds[fds_idx].fd < 0) { continue; } if (__fds[fds_idx].revents) { (*nfd)++; } } } /* * PRE: parameters are validated, * vcom_socket_poll is always called with __timeout set to zero * hence returns immediately * * ACTION: handle non negative validated vcom fds and ignore rest */ /* * implements vcom_socket_poll () interface * * internally uses vcom_socket_select () * to realize the behavior * */ int vcom_socket_poll_select_impl (struct pollfd *__fds, nfds_t __nfds, int __timeout) { int rv; nfds_t fds_idx = 0; int nfd = 0; /* vcom */ int vcom_nfds = 0; fd_set vcom_readfds; fd_set vcom_writefds; fd_set vcom_exceptfds; int vcom_nfd = -1; /* invalid max_vcom_fd is -1 */ int max_vcom_fd = -1; /* __timeout is zero to get ready events and return immediately */ struct timeval tv = {.tv_sec = 0,.tv_usec = 0 }; /* validate __nfds from select perspective */ if (__nfds > FD_SETSIZE) { rv = -EINVAL; goto poll_done; } /* zero vcom fd sets */ /* * V vcom fd set */ #define _(V) \ FD_ZERO ((V)) _(&vcom_readfds); _(&vcom_writefds); _(&vcom_exceptfds); #undef _ vcom_nfds = 0; vcom_nfd = -1; for (fds_idx = 0; fds_idx < __nfds; fds_idx++) { /* ignore negative fds */ if (__fds[fds_idx].fd < 0) { continue; } /* non negative validated vcom fds */ if (__fds[fds_idx].fd > FD_SETSIZE) { rv = -EINVAL; goto poll_done; } /* max_vcom_fd and vcom_nfd */ if (__fds[fds_idx].fd > max_vcom_fd) { /* requested events */ if (__fds[fds_idx].events) { max_vcom_fd = __fds[fds_idx].fd; } } ++vcom_nfd; } vcom_nfds = max_vcom_fd != -1 ? max_vcom_fd + 1 : 0; if (!vcom_nfds) { rv = vcom_nfds; goto poll_done; } vcom_pollfds_2_selectfds ( /* src */ __fds, __nfds, /* dest */ vcom_nfds, &vcom_readfds, &vcom_writefds, &vcom_exceptfds); /* select on vcom fds */ vcom_nfd = vcom_socket_select (vcom_nfds, &vcom_readfds, &vcom_writefds, &vcom_exceptfds, &tv); if (VCOM_DEBUG > 2) fprintf (stderr, "[%d] vcom_socket_select: " "'%04d'='%04d'\n", getpid (), vcom_nfd, vcom_nfds); if (vcom_nfd < 0) { rv = vcom_nfd; goto poll_done; } vcom_selectfds_2_pollfds ( /* dest */ __fds, __nfds, &nfd, /* src */ vcom_nfds, &vcom_readfds, &vcom_writefds, &vcom_exceptfds); rv = nfd; poll_done: return rv; } /* * TBD: remove this static function once vppcom * has an implementation in place * * ACTION: */ static int vppcom_poll (struct pollfd *__fds, nfds_t __nfds, double time_to_wait) { return -EOPNOTSUPP; } int vcom_socket_poll_vppcom_impl (struct pollfd *__fds, nfds_t __nfds, int __timeout) { nfds_t fds_idx = 0; /* in seconds eg. 3.123456789 seconds */ double time_to_wait = (double) 0; i32 sid; i32 vep_idx; /* replace vcom fd with session idx */ for (fds_idx = 0; fds_idx < __nfds; fds_idx++) { /* ignore negative fds */ if (__fds[fds_idx].fd < 0) { continue; } /* non negative validated vcom fds */ sid = vcom_socket_get_sid (__fds[fds_idx].fd); if (sid != INVALID_SESSION_ID) { __fds[fds_idx].fd = sid; } else { /* get vep_idx */ vep_idx = vcom_socket_get_vep_idx (__fds[fds_idx].fd); if (vep_idx != INVALID_VEP_IDX) { __fds[fds_idx].fd = vep_idx; } else { return -EBADF; } } } /* validate __timeout */ if (__timeout > 0) { time_to_wait = (double) __timeout / (double) 1000; } else if (__timeout == 0) { time_to_wait = (double) 0; } else { time_to_wait = ~0; } return vppcom_poll (__fds, __nfds, time_to_wait); } int vcom_socket_poll (struct pollfd *__fds, nfds_t __nfds, int __timeout) { /* select an implementation */ /* return vcom_socket_poll_vppcom_impl (__fds, __nfds, __timeout); */ return vcom_socket_poll_select_impl (__fds, __nfds, __timeout); } #ifdef __USE_GNU int vcom_socket_ppoll (struct pollfd *__fds, nfds_t __nfds, const struct timespec *__timeout, const __sigset_t * __ss) { return -EOPNOTSUPP; } #endif int vcom_socket_main_init (void) { vcom_socket_main_t *vsm = &vcom_socket_main; if (VCOM_DEBUG > 0) printf ("vcom_socket_main_init\n"); if (!vsm->init) { /* TBD: define FD_MAXSIZE and use it here */ pool_alloc (vsm->vsockets, FD_SETSIZE); vsm->sockidx_by_fd = hash_create (0, sizeof (i32)); pool_alloc (vsm->vepolls, FD_SETSIZE); vsm->epollidx_by_epfd = hash_create (0, sizeof (i32)); pool_alloc (vsm->vepitems, FD_SETSIZE); vsm->epitemidx_by_epfdfd = hash_create (0, sizeof (i32)); vsm->epitemidxs_by_epfd = hash_create (0, sizeof (i32 *)); vsm->epitemidxs_by_fd = hash_create (0, sizeof (i32 *)); clib_time_init (&vsm->clib_time); vsm->init = 1; } return 0; } void vcom_socket_main_show (void) { vcom_socket_main_t *vsm = &vcom_socket_main; vcom_socket_t *vsock; vcom_epoll_t *vepoll; vcom_epitem_t *vepitem; i32 epfd; i32 fd; i32 *vepitemidxs, *vepitemidxs_var; if (vsm->init) { /* from active list of vsockets show vsock */ /* *INDENT-OFF* */ pool_foreach (vsock, vsm->vsockets, ({ printf( "fd='%04d', sid='%08x',type='%-30s'\n", vsock->fd, vsock->sid, vcom_socket_type_str (vsock->type)); })); /* *INDENT-ON* */ /* from active list of vepolls, show vepoll */ /* *INDENT-OFF* */ pool_foreach (vepoll, vsm->vepolls, ({ printf( "epfd='%04d', vep_idx='%08x', " "type='%-30s', " "flags='%d', count='%d', close='%d'\n", vepoll->epfd, vepoll->vep_idx, vcom_socket_epoll_type_str (vepoll->type), vepoll->flags, vepoll->count, vepoll->close); })); /* *INDENT-ON* */ /* from active list of vepitems, show vepitem */ /* *INDENT-OFF* */ pool_foreach (vepitem, vsm->vepitems, ({ printf( "epfd='%04d', fd='%04d', " "next_fd='%04d', prev_fd='%04d', " "type='%-30s', " "events='%04x', revents='%04x'\n", vepitem->epfd, vepitem->fd, vepitem->next_fd, vepitem->prev_fd, vcom_socket_vcom_fd_type_str (vepitem->type), vepitem->event.events, vepitem->revent.events); })); /* *INDENT-ON* */ /* show epitemidxs for epfd */ /* *INDENT-OFF* */ hash_foreach (epfd, vepitemidxs, vsm->epitemidxs_by_epfd, ({ printf("\n[ '%04d': ", epfd); vec_foreach (vepitemidxs_var,vepitemidxs) { printf("'%04d' ", (int)vepitemidxs_var[0]); } printf("]\n"); })); /* *INDENT-ON* */ /* show epitemidxs for fd */ /* *INDENT-OFF* */ hash_foreach (fd, vepitemidxs, vsm->epitemidxs_by_fd, ({ printf("\n{ '%04d': ", fd); vec_foreach (vepitemidxs_var,vepitemidxs) { printf("'%04d' ", (int)vepitemidxs_var[0]); } printf("}\n"); })); /* *INDENT-ON* */ } } void vcom_socket_main_destroy (void) { vcom_socket_main_t *vsm = &vcom_socket_main; vcom_socket_t *vsock; vcom_epoll_t *vepoll; vcom_epitem_t *vepitem; i32 epfd; i32 fd; i32 *vepitemidxs; if (VCOM_DEBUG > 0) printf ("vcom_socket_main_destroy\n"); if (vsm->init) { /* * from active list of vepitems, * remove all "vepitem" elements from the pool in a safe way * */ /* *INDENT-OFF* */ pool_flush (vepitem, vsm->vepitems, ({ if ((vepitem->type == FD_TYPE_EPOLL) || (vepitem->type == FD_TYPE_VCOM_SOCKET)) { vcom_socket_epoll_ctl1 (vepitem->epfd, EPOLL_CTL_DEL, vepitem->fd, NULL); vepitem_init (vepitem); } })); /* *INDENT-ON* */ pool_free (vsm->vepitems); hash_free (vsm->epitemidx_by_epfdfd); /* free vepitemidxs for each epfd */ /* *INDENT-OFF* */ hash_foreach (epfd, vepitemidxs, vsm->epitemidxs_by_epfd, ({ vec_free (vepitemidxs); })); /* *INDENT-ON* */ hash_free (vsm->epitemidxs_by_epfd); /* free vepitemidxs for each fd */ /* *INDENT-OFF* */ hash_foreach (fd, vepitemidxs, vsm->epitemidxs_by_fd, ({ vec_free (vepitemidxs); })); /* *INDENT-ON* */ hash_free (vsm->epitemidxs_by_fd); /* * from active list of vsockets, * close socket and vppcom session * */ /* *INDENT-OFF* */ pool_foreach (vsock, vsm->vsockets, ({ if (vsock->type == SOCKET_TYPE_VPPCOM_BOUND) { vppcom_session_close (vsock->sid); vcom_socket_close_socket (vsock->fd); vsocket_init (vsock); } })); /* *INDENT-ON* */ /* * return vsocket element to the pool * */ /* *INDENT-OFF* */ pool_flush (vsock, vsm->vsockets, ({ // vsocket_init(vsock); ; })); /* *INDENT-ON* */ pool_free (vsm->vsockets); hash_free (vsm->sockidx_by_fd); /* * from active list of vepolls, * close epoll and vppcom_epoll * */ /* *INDENT-OFF* */ pool_foreach (vepoll, vsm->vepolls, ({ if (vepoll->type == EPOLL_TYPE_VPPCOM_BOUND) { vppcom_session_close (vepoll->vep_idx); vcom_socket_close_epoll (vepoll->epfd); /* TBD: */ vepoll_init (vepoll); } })); /* *INDENT-ON* */ /* * return vepoll element to the pool * */ /* *INDENT-OFF* */ pool_flush (vepoll, vsm->vepolls, ({ // vepoll_init(vepoll); ; })); /* *INDENT-ON* */ pool_free (vsm->vepolls); hash_free (vsm->epollidx_by_epfd); vsm->init = 0; } } /* * fd.io coding-style-patch-verification: ON * * Local Variables: * eval: (c-set-style "gnu") * End: */