/* * 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 #include #include #include #include #include /* * VCOM_SOCKET Private definitions and functions. */ typedef struct vcom_socket_main_t_ { u8 init; /* vcom_socket pool */ vcom_socket_t *vsockets; /* Hash table for socketidx to fd mapping */ uword *sockidx_by_fd; } vcom_socket_main_t; vcom_socket_main_t vcom_socket_main; /* * Public API functions */ 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; } int vcom_socket_close_socket (int fd) { int rv; rv = libc_close (fd); if (rv == -1) rv = -errno; return rv; } 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)); vsm->init = 1; } return 0; } void vcom_socket_main_destroy (void) { vcom_socket_main_t *vsm = &vcom_socket_main; vcom_socket_t *vsock; if (VCOM_DEBUG > 0) printf ("vcom_socket_main_destroy\n"); if (vsm->init) { /* * 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); vsm->init = 0; } } void vcom_socket_main_show (void) { vcom_socket_main_t *vsm = &vcom_socket_main; vcom_socket_t *vsock; if (vsm->init) { /* from active list of vsockets, * close socket and vppcom session */ /* *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* */ } } 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; } 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; } int vcom_socket_close (int __fd) { 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; 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); 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 || __nbytes < 0) { 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); } 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_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; 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 || __n < 0) { return -EINVAL; } rv = vppcom_session_write (vsock->sid, (void *) __buf, __n); 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 * */ /* 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_GETFL: case F_SETFL: case F_GETLK: case F_SETLK: case F_SETLKW: case F_GETOWN: case F_SETOWN: return 2; #if 0 /* cmd handled by vppcom */ case F_XXXXX: return 3; #endif /* invalid cmd */ default: return 0; } return 0; } /* TBD: move it to vppcom */ static int vppcom_session_fcntl_va (int __fd, int __cmd, va_list __ap) { int rv; rv = -EINVAL; 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 = -EBADF; break; /* cmd handled by vcom socket resource */ case 2: rv = libc_vfcntl (vsock->fd, __cmd, __ap); break; /* cmd handled by vppcom */ case 3: rv = vppcom_session_fcntl_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) { int rv = -EBADF; pid_t pid = getpid (); 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; unsigned long vcom_nsid_fds = 0; int vcom_nsid = 0; /* 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 _ /* 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; } if (vcom_nsid_fds < 0) { return -EINVAL; } 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 > 0) fprintf (stderr, "[%d] vppcom_select: " "'%04d'='%04d'\n", pid, 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); 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); /* TBD: remove libc_bind code snippet * once vppcom implements vppcom_session_getsockname */ if (rv == 0) { rv = libc_bind (__fd, __addr, __len); if (rv != 0) { rv = -errno; } } return rv; } int vppcom_session_getsockname (int sid, vppcom_endpt_t * ep) { /* TBD: move it to vppcom */ return 0; } 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; if (*__len < 0) { return -EINVAL; } /* TBD: remove libc_getsockname code snippet * once vppcom implements vppcom_session_getsockname */ rv = libc_getsockname (__fd, __addr, __len); if (rv != 0) { rv = -errno; return rv; } /* TBD: use the below code snippet when vppcom * implements vppcom_session_getsockname */ #if 0 vppcom_endpt_t ep; ep.ip = (u8 *) & ((const struct sockaddr_in *) __addr)->sin_addr; rv = vppcom_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; } } } #endif 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; } int vppcom_session_getpeername (int sid, vppcom_endpt_t * ep) { /* TBD: move it to vppcom */ return 0; } 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; if (*__len < 0) { return -EINVAL; } /* DAW: hack to allow iperf3 to be happy w/ getpeername output */ { uint8_t *a; ((struct sockaddr_in *) __addr)->sin_family = AF_INET; ((struct sockaddr_in *) __addr)->sin_port = 0x1000; a = (uint8_t *) & ((struct sockaddr_in *) __addr)->sin_addr; a[0] = 0x7f; a[1] = 0x00; a[2] = 0x00; a[3] = 0x01; *__len = sizeof (struct sockaddr_in); return 0; } /* TBD: remove libc_getpeername code snippet * once vppcom implements vppcom_session_getpeername */ rv = libc_getpeername (__fd, __addr, __len); if (rv != 0) { rv = -errno; return rv; } /* TBD: use the below code snippet when vppcom * implements vppcom_session_getpeername */ #if 0 vppcom_endpt_t ep; ep.ip = (u8 *) & ((const struct sockaddr_in *) __addr)->sin_addr; rv = vppcom_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; } } } #endif 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; } ssize_t vvppcom_session_sendto (int __sid, const void *__buf, size_t __n, int __flags, __CONST_SOCKADDR_ARG __addr, socklen_t __addr_len) { int rv = -1; /* TBD add new vpp api */ /* TBD add flags parameter */ rv = vppcom_session_write (__sid, (void *) __buf, (int) __n); return rv; } ssize_t vcom_socket_sendto (int __fd, const void *__buf, size_t __n, int __flags, __CONST_SOCKADDR_ARG __addr, socklen_t __addr_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 (!__buf || __n < 0) { 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 || __addr_len < 0) { return -EDESTADDRREQ; } /* not a vppcom supported address family */ if ((__addr->sa_family != AF_INET) || (__addr->sa_family != AF_INET6)) { return -EINVAL; } } rv = vvppcom_session_sendto (vsock->sid, (void *) __buf, (int) __n, __flags, __addr, __addr_len); return rv; } /* TBD: move it to vppcom */ static ssize_t vppcom_session_recvfrom (int __sid, void *__restrict __buf, size_t __n, int __flags, __SOCKADDR_ARG __addr, socklen_t * __restrict __addr_len) { int rv = -1; /* TBD add flags parameter */ rv = vppcom_session_read (__sid, __buf, __n); 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; 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 || __n < 0) { return -EINVAL; } if (__addr || __addr_len < 0) { return -EINVAL; } rv = vppcom_session_recvfrom (vsock->sid, __buf, __n, __flags, __addr, __addr_len); return rv; } /* TBD: move it to vppcom */ static ssize_t vppcom_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 = vppcom_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 ssize_t vppcom_recvmsg (int __sid, struct msghdr *__message, int __flags) { int rv = -1; /* rv = vppcom_session_read (__sid, (void *) __message->__buf, (int)__n); */ 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 = vppcom_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 int vppcom_getsockopt (int __sid, int __level, int __optname, void *__restrict __optval, socklen_t * __restrict __optlen) { /* 1. for socket level options that are NOT socket attributes * and that has corresponding vpp options get from vppcom */ #if 0 return 0; #endif /* 2. unhandled options */ return -ENOPROTOOPT; } 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; 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 (!__optval && !__optlen) return -EFAULT; if (*__optlen < 0) { return -EINVAL; } switch (__level) { /* handle options at socket 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_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: 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: case SO_INCOMING_CPU: rv = libc_getsockopt (__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: /* 1. handle options that are NOT socket level options, * but have corresponding vpp otions. */ rv = vppcom_getsockopt (vsock->sid, __level, __optname, __optval, __optlen); return rv; #if 0 /* 2. unhandled options */ return -ENOPROTOOPT; #endif } return rv; } /* TBD: move it to vppcom */ int vppcom_setsockopt (int __fd, int __level, int __optname, const void *__optval, socklen_t __optlen) { /* 1. for socket level options that are NOT socket attributes * and that has corresponding vpp options set it from vppcom */ #if 0 return 0; #endif /* 2. unhandled options */ return -ENOPROTOOPT; } 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 < 0) || (__optlen < sizeof (int))) return -EINVAL; switch (__level) { /* handle options at socket level */ case SOL_SOCKET: switch (__optname) { /* * 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_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_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: case SO_INCOMING_CPU: 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: /* 1. handle options that are NOT socket level options, * but have corresponding vpp otions. */ rv = vppcom_setsockopt (vsock->sid, __level, __optname, __optval, __optlen); return rv; #if 0 /* 2. unhandled options */ return -ENOPROTOOPT; #endif } 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); 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 (!(!flags || (flags & (SOCK_NONBLOCK | SOCK_CLOEXEC)))) { /* TBD: return proper error code */ return -1; } /* TBD: return proper error code */ if (!vcom_socket_is_connection_mode_socket (__fd)) { 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) { 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) { 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) { 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; } } } else { /* when __addr is NULL, nothing is filled in, * in this case, __addr_len is not used, * and should also be null * */ if (__addr_len) { /* TBD: return proper error code */ return -1; } } } 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); } #ifdef __USE_GNU 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); } #endif /* TBD: move it to vppcom */ int vppcom_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 = vppcom_session_shutdown (vsock->sid, __how); return rv; break; default: return -EINVAL; break; } } return rv; } /* * fd.io coding-style-patch-verification: ON * * Local Variables: * eval: (c-set-style "gnu") * End: */