/* * Copyright (c) 2010 Kip Macy. All rights reserved. * Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * Derived in part from libplebnet's pn_syscall_wrapper.c. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ff_api.h" #include "ff_host_interface.h" /* setsockopt/getsockopt define start */ #define LINUX_SOL_SOCKET 1 #define LINUX_SO_DEBUG 1 #define LINUX_SO_REUSEADDR 2 #define LINUX_SO_ERROR 4 #define LINUX_SO_DONTROUTE 5 #define LINUX_SO_BROADCAST 6 #define LINUX_SO_SNDBUF 7 #define LINUX_SO_RCVBUF 8 #define LINUX_SO_KEEPALIVE 9 #define LINUX_SO_OOBINLINE 10 #define LINUX_SO_LINGER 13 #define LINUX_SO_REUSEPORT 15 #define LINUX_SO_RCVLOWAT 18 #define LINUX_SO_SNDLOWAT 19 #define LINUX_SO_RCVTIMEO 20 #define LINUX_SO_SNDTIMEO 21 #define LINUX_SO_ACCEPTCONN 30 #define LINUX_SO_PROTOCOL 38 #define LINUX_IP_TOS 1 #define LINUX_IP_TTL 2 #define LINUX_IP_HDRINCL 3 #define LINUX_IP_OPTIONS 4 #define LINUX_IP_RECVTTL 12 #define LINUX_IP_RECVTOS 13 #define LINUX_IP_TRANSPARENT 19 #define LINUX_IP_MINTTL 21 #define LINUX_IP_MULTICAST_IF 32 #define LINUX_IP_MULTICAST_TTL 33 #define LINUX_IP_MULTICAST_LOOP 34 #define LINUX_IP_ADD_MEMBERSHIP 35 #define LINUX_IP_DROP_MEMBERSHIP 36 #define LINUX_IPV6_V6ONLY 26 #define LINUX_IPV6_RECVPKTINFO 49 #define LINUX_IPV6_TRANSPARENT 75 #define LINUX_TCP_NODELAY 1 #define LINUX_TCP_MAXSEG 2 #define LINUX_TCP_KEEPIDLE 4 #define LINUX_TCP_KEEPINTVL 5 #define LINUX_TCP_KEEPCNT 6 #define LINUX_TCP_INFO 11 #define LINUX_TCP_MD5SIG 14 /* setsockopt/getsockopt define end */ /* ioctl define start */ #define LINUX_TIOCEXCL 0x540C #define LINUX_TIOCNXCL 0x540D #define LINUX_TIOCSCTTY 0x540E #define LINUX_TIOCGPGRP 0x540F #define LINUX_TIOCSPGRP 0x5410 #define LINUX_TIOCOUTQ 0x5411 #define LINUX_TIOCSTI 0x5412 #define LINUX_TIOCGWINSZ 0x5413 #define LINUX_TIOCSWINSZ 0x5414 #define LINUX_TIOCMGET 0x5415 #define LINUX_TIOCMBIS 0x5416 #define LINUX_TIOCMBIC 0x5417 #define LINUX_TIOCMSET 0x5418 #define LINUX_FIONREAD 0x541B #define LINUX_TIOCCONS 0x541D #define LINUX_TIOCPKT 0x5420 #define LINUX_FIONBIO 0x5421 #define LINUX_TIOCNOTTY 0x5422 #define LINUX_TIOCSETD 0x5423 #define LINUX_TIOCGETD 0x5424 #define LINUX_TIOCSBRK 0x5427 #define LINUX_TIOCCBRK 0x5428 #define LINUX_TIOCGSID 0x5429 #define LINUX_FIONCLEX 0x5450 #define LINUX_FIOCLEX 0x5451 #define LINUX_FIOASYNC 0x5452 #define LINUX_TIOCPKT_DATA 0 #define LINUX_TIOCPKT_FLUSHREAD 1 #define LINUX_TIOCPKT_FLUSHWRITE 2 #define LINUX_TIOCPKT_STOP 4 #define LINUX_TIOCPKT_START 8 #define LINUX_TIOCPKT_NOSTOP 16 #define LINUX_TIOCPKT_DOSTOP 32 #define LINUX_TIOCPKT_IOCTL 64 #define LINUX_SIOCGIFCONF 0x8912 #define LINUX_SIOCGIFFLAGS 0x8913 #define LINUX_SIOCSIFFLAGS 0x8914 #define LINUX_SIOCGIFADDR 0x8915 #define LINUX_SIOCSIFADDR 0x8916 #define LINUX_SIOCGIFDSTADDR 0x8917 #define LINUX_SIOCSIFDSTADDR 0x8918 #define LINUX_SIOCGIFBRDADDR 0x8919 #define LINUX_SIOCSIFBRDADDR 0x891a #define LINUX_SIOCGIFNETMASK 0x891b #define LINUX_SIOCSIFNETMASK 0x891c #define LINUX_SIOCGIFMETRIC 0x891d #define LINUX_SIOCSIFMETRIC 0x891e #define LINUX_SIOCGIFMTU 0x8921 #define LINUX_SIOCSIFMTU 0x8922 #define LINUX_SIOCSIFNAME 0x8923 #define LINUX_SIOCADDMULTI 0x8931 #define LINUX_SIOCDELMULTI 0x8932 #define LINUX_SIOCGIFINDEX 0x8933 #define LINUX_SIOCDIFADDR 0x8936 /* ioctl define end */ /* af define start */ #define LINUX_AF_INET6 10 /* af define end */ /* msghdr define start */ static /*__thread*/ struct iovec msg_iov_tmp[UIO_MAXIOV]; static /*__thread*/ size_t msg_iovlen_tmp; struct linux_msghdr { void *msg_name; /* Address to send to/receive from. */ socklen_t msg_namelen; /* Length of address data. */ struct iovec *msg_iov; /* Vector of data to send/receive into. */ size_t msg_iovlen; /* Number of elements in the vector. */ void *msg_control; /* Ancillary data (eg BSD filedesc passing). */ size_t msg_controllen; /* Ancillary data buffer length. !! The type should be socklen_t but the definition of the kernel is incompatible with this. */ int msg_flags; /* Flags on received message. */ }; /* msghdr define end */ /* cmsghdr define start */ struct linux_cmsghdr { size_t cmsg_len; /* Length of data in cmsg_data plus length of cmsghdr structure. !! The type should be socklen_t but the definition of the kernel is incompatible with this. */ int cmsg_level; /* Originating protocol. */ int cmsg_type; /* Protocol specific type. */ }; /* * LINUX_CMSG_XXXX has the same effect as FreeBSD's CMSG_XXXX, * because aligned to 8 bytes, but still redefine them. */ #define LINUX_CMSG_DATA(cmsg) ((unsigned char *)(cmsg) + \ _ALIGN(sizeof(struct linux_cmsghdr))) #define LINUX_CMSG_SPACE(l) (_ALIGN(sizeof(struct linux_cmsghdr)) + _ALIGN(l)) #define LINUX_CMSG_LEN(l) (_ALIGN(sizeof(struct linux_cmsghdr)) + (l)) #define LINUX_CMSG_FIRSTHDR(mhdr) \ ((mhdr)->msg_controllen >= sizeof(struct linux_cmsghdr) ? \ (struct linux_cmsghdr *)(mhdr)->msg_control : \ (struct linux_cmsghdr *)0) #define LINUX_CMSG_NXTHDR(mhdr, cmsg) \ ((char *)(cmsg) == (char *)0 ? LINUX_CMSG_FIRSTHDR(mhdr) : \ ((char *)(cmsg) + _ALIGN(((struct linux_cmsghdr *)(cmsg))->cmsg_len) + \ _ALIGN(sizeof(struct linux_cmsghdr)) > \ (char *)(mhdr)->msg_control + (mhdr)->msg_controllen) ? \ (struct linux_cmsghdr *)0 : \ (struct linux_cmsghdr *)(void *)((char *)(cmsg) + \ _ALIGN(((struct linux_cmsghdr *)(cmsg))->cmsg_len))) /* cmsghdr define end */ extern int sendit(struct thread *td, int s, struct msghdr *mp, int flags); static long linux2freebsd_ioctl(unsigned long request) { switch(request) { case LINUX_TIOCEXCL: return TIOCEXCL; case LINUX_TIOCNXCL: return TIOCNXCL; case LINUX_TIOCSCTTY: return TIOCSCTTY; case LINUX_TIOCGPGRP: return TIOCGPGRP; case LINUX_TIOCSPGRP: return TIOCSPGRP; case LINUX_TIOCOUTQ: return TIOCOUTQ; case LINUX_TIOCSTI: return TIOCSTI; case LINUX_TIOCGWINSZ: return TIOCGWINSZ; case LINUX_TIOCSWINSZ: return TIOCSWINSZ; case LINUX_TIOCMGET: return TIOCMGET; case LINUX_TIOCMBIS: return TIOCMBIS; case LINUX_TIOCMBIC: return TIOCMBIC; case LINUX_TIOCMSET: return TIOCMSET; case LINUX_FIONREAD: return FIONREAD; case LINUX_TIOCCONS: return TIOCCONS; case LINUX_TIOCPKT: return TIOCPKT; case LINUX_FIONBIO: return FIONBIO; case LINUX_TIOCNOTTY: return TIOCNOTTY; case LINUX_TIOCSETD: return TIOCSETD; case LINUX_TIOCGETD: return TIOCGETD; case LINUX_TIOCSBRK: return TIOCSBRK; case LINUX_TIOCCBRK: return TIOCCBRK; case LINUX_TIOCGSID: return TIOCGSID; case LINUX_FIONCLEX: return FIONCLEX; case LINUX_FIOCLEX: return FIOCLEX; case LINUX_FIOASYNC: return FIOASYNC; case LINUX_TIOCPKT_DATA: return TIOCPKT_DATA; case LINUX_TIOCPKT_FLUSHREAD: return TIOCPKT_FLUSHREAD; case LINUX_TIOCPKT_FLUSHWRITE: return TIOCPKT_FLUSHWRITE; case LINUX_TIOCPKT_STOP: return TIOCPKT_STOP; case LINUX_TIOCPKT_START: return TIOCPKT_START; case LINUX_TIOCPKT_NOSTOP: return TIOCPKT_NOSTOP; case LINUX_TIOCPKT_DOSTOP: return TIOCPKT_DOSTOP; case LINUX_TIOCPKT_IOCTL: return TIOCPKT_IOCTL; case LINUX_SIOCGIFCONF: return SIOCGIFCONF; case LINUX_SIOCGIFFLAGS: return SIOCGIFFLAGS; case LINUX_SIOCSIFFLAGS: return SIOCSIFFLAGS; case LINUX_SIOCGIFADDR: return SIOCGIFADDR; case LINUX_SIOCSIFADDR: return SIOCSIFADDR; case LINUX_SIOCGIFDSTADDR: return SIOCGIFDSTADDR; case LINUX_SIOCSIFDSTADDR: return SIOCSIFDSTADDR; case LINUX_SIOCGIFBRDADDR: return SIOCGIFBRDADDR; case LINUX_SIOCSIFBRDADDR: return SIOCSIFBRDADDR; case LINUX_SIOCGIFNETMASK: return SIOCGIFNETMASK; case LINUX_SIOCSIFNETMASK: return SIOCSIFNETMASK; case LINUX_SIOCGIFMETRIC: return SIOCGIFMETRIC; case LINUX_SIOCSIFMETRIC: return SIOCSIFMETRIC; case LINUX_SIOCGIFMTU: return SIOCGIFMTU; case LINUX_SIOCSIFMTU: return SIOCSIFMTU; case LINUX_SIOCSIFNAME: return SIOCSIFNAME; case LINUX_SIOCADDMULTI: return SIOCADDMULTI; case LINUX_SIOCDELMULTI: return SIOCDELMULTI; case LINUX_SIOCGIFINDEX: return SIOCGIFINDEX; case LINUX_SIOCDIFADDR: return SIOCDIFADDR; default: return -1; } } static int so_opt_convert(int optname) { switch(optname) { case LINUX_SO_DEBUG: return SO_DEBUG; case LINUX_SO_REUSEADDR: return SO_REUSEADDR; case LINUX_SO_ERROR: return SO_ERROR; case LINUX_SO_DONTROUTE: return SO_DONTROUTE; case LINUX_SO_BROADCAST: return SO_BROADCAST; case LINUX_SO_SNDBUF: return SO_SNDBUF; case LINUX_SO_RCVBUF: return SO_RCVBUF; case LINUX_SO_KEEPALIVE: return SO_KEEPALIVE; case LINUX_SO_OOBINLINE: return SO_OOBINLINE; case LINUX_SO_LINGER: return SO_LINGER; case LINUX_SO_REUSEPORT: return SO_REUSEPORT; case LINUX_SO_RCVLOWAT: return SO_RCVLOWAT; case LINUX_SO_SNDLOWAT: return SO_SNDLOWAT; case LINUX_SO_RCVTIMEO: return SO_RCVTIMEO; case LINUX_SO_SNDTIMEO: return SO_SNDTIMEO; case LINUX_SO_ACCEPTCONN: return SO_ACCEPTCONN; case LINUX_SO_PROTOCOL: return SO_PROTOCOL; default: return -1; } } static int ip_opt_convert(int optname) { switch(optname) { case LINUX_IP_TOS: return IP_TOS; case LINUX_IP_TTL: return IP_TTL; case LINUX_IP_HDRINCL: return IP_HDRINCL; case LINUX_IP_OPTIONS: return IP_OPTIONS; case LINUX_IP_MULTICAST_IF: return IP_MULTICAST_IF; case LINUX_IP_MULTICAST_TTL: return IP_MULTICAST_TTL; case LINUX_IP_MULTICAST_LOOP: return IP_MULTICAST_LOOP; case LINUX_IP_ADD_MEMBERSHIP: return IP_ADD_MEMBERSHIP; case LINUX_IP_DROP_MEMBERSHIP: return IP_DROP_MEMBERSHIP; case LINUX_IP_RECVTTL: return IP_RECVTTL; case LINUX_IP_RECVTOS: return IP_RECVTOS; case LINUX_IP_TRANSPARENT: return IP_BINDANY; case LINUX_IP_MINTTL: return IP_MINTTL; default: return optname; } } static int ip6_opt_convert(int optname) { switch(optname) { case LINUX_IPV6_V6ONLY: return IPV6_V6ONLY; case LINUX_IPV6_RECVPKTINFO: return IPV6_RECVPKTINFO; case LINUX_IPV6_TRANSPARENT: return IPV6_BINDANY; default: return optname; } } static int tcp_opt_convert(int optname) { switch(optname) { case LINUX_TCP_NODELAY: return TCP_NODELAY; case LINUX_TCP_MAXSEG: return TCP_MAXSEG; case LINUX_TCP_KEEPIDLE: return TCP_KEEPIDLE; case LINUX_TCP_KEEPINTVL: return TCP_KEEPINTVL; case LINUX_TCP_KEEPCNT: return TCP_KEEPCNT; case LINUX_TCP_INFO: return TCP_INFO; case LINUX_TCP_MD5SIG: return TCP_MD5SIG; default: return -1; } } static int linux2freebsd_opt(int level, int optname) { switch(level) { case SOL_SOCKET: return so_opt_convert(optname); case IPPROTO_IP: return ip_opt_convert(optname); case IPPROTO_IPV6: return ip6_opt_convert(optname); case IPPROTO_TCP: return tcp_opt_convert(optname); default: return -1; } } static void linux2freebsd_sockaddr(const struct linux_sockaddr *linux, socklen_t addrlen, struct sockaddr *freebsd) { if (linux == NULL || freebsd == NULL) { return; } /* #linux and #freebsd may point to the same address */ freebsd->sa_family = linux->sa_family == LINUX_AF_INET6 ? AF_INET6 : linux->sa_family; freebsd->sa_len = addrlen; if (linux->sa_data != freebsd->sa_data) { bcopy(linux->sa_data, freebsd->sa_data, addrlen - sizeof(linux->sa_family)); } } static void freebsd2linux_sockaddr(struct linux_sockaddr *linux, struct sockaddr *freebsd) { if (linux == NULL || freebsd == NULL) { return; } /* #linux and #freebsd may point to the same address */ if (linux->sa_data != freebsd->sa_data) { bcopy(freebsd->sa_data, linux->sa_data, freebsd->sa_len - sizeof(linux->sa_family)); } linux->sa_family = freebsd->sa_family == AF_INET6 ? LINUX_AF_INET6 : freebsd->sa_family; } static inline int freebsd2linux_cmsghdr(struct linux_msghdr *linux_msg, const struct msghdr *freebsd_msg) { struct cmsghdr *freebsd_cmsg = CMSG_FIRSTHDR(freebsd_msg); struct linux_cmsghdr *linux_cmsg = LINUX_CMSG_FIRSTHDR(linux_msg); while (freebsd_cmsg && linux_cmsg) { unsigned char *freebsd_optval = CMSG_DATA(freebsd_cmsg); unsigned char *linux_optval = LINUX_CMSG_DATA(linux_cmsg); /* * The address of linux_cmsg and freebsd_cmsg coincides while recvmsg, * but the position of the variable pointer is different, * and the assignment must be reversed. * * Although sizeof(struct linux_msghdr) and sizeof(struct msghdr) have different lengths, * but cmsg_data both skip the same 16 bytes,both aligned to 8 bytes. */ linux_cmsg->cmsg_type = freebsd_cmsg->cmsg_type; linux_cmsg->cmsg_level = freebsd_cmsg->cmsg_level; linux_cmsg->cmsg_len = LINUX_CMSG_LEN(freebsd_cmsg->cmsg_len - CMSG_ALIGN(sizeof(struct cmsghdr))); /* * The freebsd_msg's cmsg_level and cmsg_type has been moddied while recvmsg, * must use linux_cmsg to judge and calculate data length. * And don't copy other the bytes that used aligned. */ switch (linux_cmsg->cmsg_level) { case IPPROTO_IP: switch (linux_cmsg->cmsg_type) { case IP_RECVTOS: linux_cmsg->cmsg_type = LINUX_IP_TOS; *linux_optval = *freebsd_optval; break; case IP_RECVTTL: linux_cmsg->cmsg_len = LINUX_CMSG_LEN(sizeof(int)); linux_cmsg->cmsg_type = LINUX_IP_TTL; *(int *)linux_optval = *freebsd_optval; break; /*case XXXX: break;*/ default: memcpy(linux_optval, freebsd_optval, linux_cmsg->cmsg_len - sizeof(struct linux_cmsghdr)); break; } break; default: memcpy(linux_optval, freebsd_optval, linux_cmsg->cmsg_len - sizeof(struct linux_cmsghdr)); break; } linux_cmsg = LINUX_CMSG_NXTHDR(linux_msg, linux_cmsg); freebsd_cmsg = CMSG_NXTHDR(freebsd_msg, freebsd_cmsg); } return 0; } static inline int linux2freebsd_cmsg(const struct linux_msghdr *linux_msg, struct msghdr *freebsd_msg) { struct cmsghdr *freebsd_cmsg = CMSG_FIRSTHDR(freebsd_msg); struct linux_cmsghdr *linux_cmsg = LINUX_CMSG_FIRSTHDR(linux_msg); while (freebsd_cmsg && linux_cmsg) { unsigned char *freebsd_optval = CMSG_DATA(freebsd_cmsg); unsigned char *linux_optval = LINUX_CMSG_DATA(linux_cmsg); freebsd_cmsg->cmsg_type = linux_cmsg->cmsg_type; freebsd_cmsg->cmsg_level = linux_cmsg->cmsg_level; freebsd_cmsg->cmsg_len = CMSG_LEN(linux_cmsg->cmsg_len - CMSG_ALIGN(sizeof(struct linux_cmsghdr))); switch (linux_cmsg->cmsg_level) { case IPPROTO_IP: switch (linux_cmsg->cmsg_type) { case LINUX_IP_TOS: freebsd_cmsg->cmsg_type = IP_TOS; freebsd_cmsg->cmsg_len = CMSG_LEN(sizeof(char)); if (linux_cmsg->cmsg_len == LINUX_CMSG_LEN(sizeof(int))) { *freebsd_optval = *(int *)linux_optval; } else if (linux_cmsg->cmsg_len == LINUX_CMSG_LEN(sizeof(char))) { *freebsd_optval = *linux_optval; } break; case LINUX_IP_TTL: freebsd_cmsg->cmsg_type = IP_TTL; freebsd_cmsg->cmsg_len = CMSG_LEN(sizeof(char)); *freebsd_optval = *(int *)linux_optval; break; /*case XXXX: break;*/ default: memcpy(freebsd_optval, linux_optval, linux_cmsg->cmsg_len - sizeof(struct linux_cmsghdr)); break; } break; default: memcpy(freebsd_optval, linux_optval, linux_cmsg->cmsg_len - sizeof(struct linux_cmsghdr)); break; } linux_cmsg = LINUX_CMSG_NXTHDR(linux_msg, linux_cmsg); freebsd_cmsg = CMSG_NXTHDR(freebsd_msg, freebsd_cmsg); } return 0; } /* * While sendmsg, need convert msg_name and msg_control from Linux to FreeBSD. * While recvmsg, need convert msg_name and msg_control from FreeBSD to Linux. * Note: linux2freebsd_msghdr and freebsd2linux_msghdr must be called in sequence and in pairs. */ static int freebsd2linux_msghdr(struct linux_msghdr *linux_msg, struct msghdr *freebsd_msg, int send_flag) { if (linux_msg == NULL || freebsd_msg == NULL) { return -1; } if (linux_msg->msg_name && freebsd_msg->msg_name && !send_flag) { linux_msg->msg_name = freebsd_msg->msg_name; freebsd2linux_sockaddr(linux_msg->msg_name, freebsd_msg->msg_name); linux_msg->msg_namelen = freebsd_msg->msg_namelen; } linux_msg->msg_iov = freebsd_msg->msg_iov; linux_msg->msg_iovlen = freebsd_msg->msg_iovlen; /* Restore the old iov pointer, compatible with the Linux interface */ memcpy(linux_msg->msg_iov, msg_iov_tmp, msg_iovlen_tmp * sizeof(struct iovec)); if(freebsd_msg->msg_control && linux_msg->msg_control && !send_flag) { freebsd2linux_cmsghdr(linux_msg, freebsd_msg); linux_msg->msg_controllen = freebsd_msg->msg_controllen; } linux_msg->msg_flags = freebsd_msg->msg_flags; return 0; } static int linux2freebsd_msghdr(const struct linux_msghdr *linux_msg, struct msghdr *freebsd_msg, int send_flag) { int ret = 0; if (linux_msg == NULL || freebsd_msg == NULL) { return -1;; } if (linux_msg->msg_name && freebsd_msg->msg_name && send_flag) { linux2freebsd_sockaddr(linux_msg->msg_name, linux_msg->msg_namelen, freebsd_msg->msg_name); } else { freebsd_msg->msg_name = linux_msg->msg_name; } freebsd_msg->msg_namelen = linux_msg->msg_namelen; /* Save the old iov pointer, compatible with the Linux interface */ msg_iovlen_tmp = linux_msg->msg_iovlen; if (msg_iovlen_tmp > UIO_MAXIOV) { return -1; // EMSGSIZE; } memcpy(msg_iov_tmp, linux_msg->msg_iov, msg_iovlen_tmp * sizeof(struct iovec)); freebsd_msg->msg_iov = linux_msg->msg_iov; freebsd_msg->msg_iovlen = linux_msg->msg_iovlen; freebsd_msg->msg_controllen = linux_msg->msg_controllen; if (linux_msg->msg_control && send_flag) { ret = linux2freebsd_cmsg(linux_msg, freebsd_msg); if(ret < 0) { return ret; } } else { freebsd_msg->msg_control = linux_msg->msg_control; } freebsd_msg->msg_flags = linux_msg->msg_flags; return 0; } int ff_socket(int domain, int type, int protocol) { int rc; struct socket_args sa; sa.domain = domain == LINUX_AF_INET6 ? AF_INET6 : domain; sa.type = type; sa.protocol = protocol; if ((rc = sys_socket(curthread, &sa))) goto kern_fail; return curthread->td_retval[0]; kern_fail: ff_os_errno(rc); return (-1); } int ff_getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen) { int rc; if (level == LINUX_SOL_SOCKET) level = SOL_SOCKET; optname = linux2freebsd_opt(level, optname); if (optname < 0) { rc = EINVAL; goto kern_fail; } if ((rc = kern_getsockopt(curthread, s, level, optname, optval, UIO_USERSPACE, optlen))) goto kern_fail; return (rc); kern_fail: ff_os_errno(rc); return (-1); } int ff_getsockopt_freebsd(int s, int level, int optname, void *optval, socklen_t *optlen) { int rc; if ((rc = kern_getsockopt(curthread, s, level, optname, optval, UIO_USERSPACE, optlen))) goto kern_fail; return (rc); kern_fail: ff_os_errno(rc); return (-1); } int ff_setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen) { int rc; if (level == LINUX_SOL_SOCKET) level = SOL_SOCKET; optname = linux2freebsd_opt(level, optname); if (optname < 0) { rc = EINVAL; goto kern_fail; } if ((rc = kern_setsockopt(curthread, s, level, optname, __DECONST(void *, optval), UIO_USERSPACE, optlen))) goto kern_fail; return (rc); kern_fail: ff_os_errno(rc); return (-1); } int ff_setsockopt_freebsd(int s, int level, int optname, const void *optval, socklen_t optlen) { int rc; if ((rc = kern_setsockopt(curthread, s, level, optname, __DECONST(void *, optval), UIO_USERSPACE, optlen))) goto kern_fail; return (rc); kern_fail: ff_os_errno(rc); return (-1); } int ff_ioctl(int fd, unsigned long request, ...) { int rc; va_list ap; caddr_t argp; long req = linux2freebsd_ioctl(request); if (req < 0) { rc = EINVAL; goto kern_fail; } va_start(ap, request); argp = va_arg(ap, caddr_t); va_end(ap); if ((rc = kern_ioctl(curthread, fd, req, argp))) goto kern_fail; return (rc); kern_fail: ff_os_errno(rc); return (-1); } int ff_ioctl_freebsd(int fd, unsigned long request, ...) { int rc; va_list ap; caddr_t argp; va_start(ap, request); argp = va_arg(ap, caddr_t); va_end(ap); if ((rc = kern_ioctl(curthread, fd, request, argp))) goto kern_fail; return (rc); kern_fail: ff_os_errno(rc); return (-1); } int ff_close(int fd) { int rc; if ((rc = kern_close(curthread, fd))) goto kern_fail; return (rc); kern_fail: ff_os_errno(rc); return (-1); } ssize_t ff_read(int fd, void *buf, size_t nbytes) { struct uio auio; struct iovec aiov; int rc; if (nbytes > INT_MAX) { rc = EINVAL; goto kern_fail; } aiov.iov_base = buf; aiov.iov_len = nbytes; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_resid = nbytes; auio.uio_segflg = UIO_SYSSPACE; if ((rc = kern_readv(curthread, fd, &auio))) goto kern_fail; rc = curthread->td_retval[0]; return (rc); kern_fail: ff_os_errno(rc); return (-1); } ssize_t ff_readv(int fd, const struct iovec *iov, int iovcnt) { struct uio auio; int rc, len, i; len = 0; for (i = 0; i < iovcnt; i++) len += iov[i].iov_len; auio.uio_iov = __DECONST(struct iovec *, iov); auio.uio_iovcnt = iovcnt; auio.uio_resid = len; auio.uio_segflg = UIO_SYSSPACE; if ((rc = kern_readv(curthread, fd, &auio))) goto kern_fail; rc = curthread->td_retval[0]; return (rc); kern_fail: ff_os_errno(rc); return (-1); } ssize_t ff_write(int fd, const void *buf, size_t nbytes) { struct uio auio; struct iovec aiov; int rc; if (nbytes > INT_MAX) { rc = EINVAL; goto kern_fail; } aiov.iov_base = (void *)(uintptr_t)buf; aiov.iov_len = nbytes; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_resid = nbytes; auio.uio_segflg = UIO_SYSSPACE; if ((rc = kern_writev(curthread, fd, &auio))) goto kern_fail; rc = curthread->td_retval[0]; return (rc); kern_fail: ff_os_errno(rc); return (-1); } ssize_t ff_writev(int fd, const struct iovec *iov, int iovcnt) { struct uio auio; int i, rc, len; len = 0; for (i = 0; i < iovcnt; i++) len += iov[i].iov_len; auio.uio_iov = __DECONST(struct iovec *, iov); auio.uio_iovcnt = iovcnt; auio.uio_resid = len; auio.uio_segflg = UIO_SYSSPACE; if ((rc = kern_writev(curthread, fd, &auio))) goto kern_fail; rc = curthread->td_retval[0]; return (rc); kern_fail: ff_os_errno(rc); return (-1); } ssize_t ff_send(int s, const void *buf, size_t len, int flags) { return (ff_sendto(s, buf, len, flags, NULL, 0)); } ssize_t ff_sendto(int s, const void *buf, size_t len, int flags, const struct linux_sockaddr *to, socklen_t tolen) { struct msghdr msg; struct iovec aiov; int rc; struct sockaddr_storage bsdaddr; struct sockaddr *pf = (struct sockaddr *)&bsdaddr; if (to) { linux2freebsd_sockaddr(to, tolen, pf); } else { pf = NULL; } msg.msg_name = pf; msg.msg_namelen = tolen; msg.msg_iov = &aiov; msg.msg_iovlen = 1; msg.msg_control = 0; aiov.iov_base = __DECONST(void *, buf); aiov.iov_len = len; if ((rc = sendit(curthread, s, &msg, flags))) goto kern_fail; rc = curthread->td_retval[0]; return (rc); kern_fail: ff_os_errno(rc); return (-1); } ssize_t ff_sendmsg(int s, const struct msghdr *msg, int flags) { int rc, ret; struct sockaddr_storage freebsd_sa; struct msghdr freebsd_msg; struct cmsghdr *freebsd_cmsg = NULL; freebsd_msg.msg_name = &freebsd_sa; if ((__DECONST(struct linux_msghdr *, msg))->msg_control) { freebsd_cmsg = malloc((__DECONST(struct linux_msghdr *, msg))->msg_controllen, NULL, 0); if (freebsd_cmsg == NULL) { rc = ENOMEM; goto kern_fail; } } freebsd_msg.msg_control = freebsd_cmsg; ret = linux2freebsd_msghdr((const struct linux_msghdr *)msg, &freebsd_msg, 1); if (ret < 0) { rc = EINVAL; goto kern_fail; } rc = sendit(curthread, s, &freebsd_msg, flags); if (rc) goto kern_fail; rc = curthread->td_retval[0]; freebsd2linux_msghdr(__DECONST(struct linux_msghdr *, msg), &freebsd_msg, 1); if (freebsd_cmsg) { free(freebsd_cmsg, NULL); } return (rc); kern_fail: ff_os_errno(rc); return (-1); } ssize_t ff_recv(int s, void *buf, size_t len, int flags) { return (ff_recvfrom(s, buf, len, flags, NULL, 0)); } ssize_t ff_recvfrom(int s, void *buf, size_t len, int flags, struct linux_sockaddr *from, socklen_t *fromlen) { struct msghdr msg; struct iovec aiov; int rc; struct sockaddr_storage bsdaddr; if (fromlen != NULL) msg.msg_namelen = *fromlen; else msg.msg_namelen = 0; msg.msg_name = &bsdaddr; msg.msg_iov = &aiov; msg.msg_iovlen = 1; aiov.iov_base = buf; aiov.iov_len = len; msg.msg_control = 0; msg.msg_flags = flags; if ((rc = kern_recvit(curthread, s, &msg, UIO_SYSSPACE, NULL))) goto kern_fail; rc = curthread->td_retval[0]; if (fromlen != NULL) *fromlen = msg.msg_namelen; if (from && msg.msg_namelen != 0) freebsd2linux_sockaddr(from, (struct sockaddr *)&bsdaddr); return (rc); kern_fail: ff_os_errno(rc); return (-1); } /* * It is considered here that the upper 4 bytes of * msg->iovlen and msg->msg_controllen in linux_msghdr are 0. */ ssize_t ff_recvmsg(int s, struct msghdr *msg, int flags) { int rc, ret; struct msghdr freebsd_msg; ret = linux2freebsd_msghdr((struct linux_msghdr *)msg, &freebsd_msg, 0); if (ret < 0) { rc = EINVAL; goto kern_fail; } freebsd_msg.msg_flags = flags; if ((rc = kern_recvit(curthread, s, &freebsd_msg, UIO_SYSSPACE, NULL))) { goto kern_fail; } rc = curthread->td_retval[0]; freebsd2linux_msghdr((struct linux_msghdr *)msg, &freebsd_msg, 0); return (rc); kern_fail: ff_os_errno(rc); return (-1); } int ff_fcntl(int fd, int cmd, ...) { int rc; va_list ap; uintptr_t argp; va_start(ap, cmd); argp = va_arg(ap, uintptr_t); va_end(ap); if ((rc = kern_fcntl(curthread, fd, cmd, argp))) goto kern_fail; rc = curthread->td_retval[0]; return (rc); kern_fail: ff_os_errno(rc); return (-1); } int ff_accept(int s, struct linux_sockaddr * addr, socklen_t * addrlen) { int rc; struct file *fp; struct sockaddr *pf = NULL; socklen_t socklen = sizeof(struct sockaddr_storage); if ((rc = kern_accept(curthread, s, &pf, &socklen, &fp))) goto kern_fail; rc = curthread->td_retval[0]; fdrop(fp, curthread); if (addr && pf) freebsd2linux_sockaddr(addr, pf); if (addrlen) *addrlen = pf->sa_len; if(pf != NULL) free(pf, M_SONAME); return (rc); kern_fail: if(pf != NULL) free(pf, M_SONAME); ff_os_errno(rc); return (-1); } int ff_listen(int s, int backlog) { int rc; struct listen_args la = { .s = s, .backlog = backlog, }; if ((rc = sys_listen(curthread, &la))) goto kern_fail; return (rc); kern_fail: ff_os_errno(rc); return (-1); } int ff_bind(int s, const struct linux_sockaddr *addr, socklen_t addrlen) { int rc; struct sockaddr_storage bsdaddr; linux2freebsd_sockaddr(addr, addrlen, (struct sockaddr *)&bsdaddr); if ((rc = kern_bindat(curthread, AT_FDCWD, s, (struct sockaddr *)&bsdaddr))) goto kern_fail; return (rc); kern_fail: ff_os_errno(rc); return (-1); } int ff_connect(int s, const struct linux_sockaddr *name, socklen_t namelen) { int rc; struct sockaddr_storage bsdaddr; linux2freebsd_sockaddr(name, namelen, (struct sockaddr *)&bsdaddr); if ((rc = kern_connectat(curthread, AT_FDCWD, s, (struct sockaddr *)&bsdaddr))) goto kern_fail; return (rc); kern_fail: ff_os_errno(rc); return (-1); } int ff_getpeername(int s, struct linux_sockaddr * name, socklen_t *namelen) { int rc; struct sockaddr *pf = NULL; if ((rc = kern_getpeername(curthread, s, &pf, namelen))) goto kern_fail; if (name && pf) freebsd2linux_sockaddr(name, pf); if(pf != NULL) free(pf, M_SONAME); return (rc); kern_fail: if(pf != NULL) free(pf, M_SONAME); ff_os_errno(rc); return (-1); } int ff_getsockname(int s, struct linux_sockaddr *name, socklen_t *namelen) { int rc; struct sockaddr *pf = NULL; if ((rc = kern_getsockname(curthread, s, &pf, namelen))) goto kern_fail; if (name && pf) freebsd2linux_sockaddr(name, pf); if(pf != NULL) free(pf, M_SONAME); return (rc); kern_fail: if(pf != NULL) free(pf, M_SONAME); ff_os_errno(rc); return (-1); } int ff_shutdown(int s, int how) { int rc; struct shutdown_args sa = { .s = s, .how = how, }; if ((rc = sys_shutdown(curthread, &sa))) goto kern_fail; return (rc); kern_fail: ff_os_errno(rc); return (-1); } int ff_sysctl(const int *name, u_int namelen, void *oldp, size_t *oldlenp, const void *newp, size_t newlen) { int rc; size_t retval; rc = userland_sysctl(curthread, __DECONST(int *, name), namelen, oldp, oldlenp, 1, __DECONST(void *, newp), newlen, &retval, 0); if (rc) goto kern_fail; if (oldlenp) *oldlenp = retval; return (0); kern_fail: ff_os_errno(rc); return (-1); } int ff_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) { int rc; rc = kern_select(curthread, nfds, readfds, writefds, exceptfds, timeout, 64); if (rc) goto kern_fail; rc = curthread->td_retval[0]; return (rc); kern_fail: ff_os_errno(rc); return (-1); } int ff_poll(struct pollfd fds[], nfds_t nfds, int timeout) { int rc; struct timespec ts; ts.tv_sec = 0; ts.tv_nsec = 0; if ((rc = kern_poll(curthread, fds, nfds, &ts, NULL))) goto kern_fail; rc = curthread->td_retval[0]; return (rc); kern_fail: ff_os_errno(rc); return (-1); } int ff_kqueue(void) { int rc; if ((rc = kern_kqueue(curthread, 0, NULL))) goto kern_fail; rc = curthread->td_retval[0]; return (rc); kern_fail: ff_os_errno(rc); return (-1); } struct sys_kevent_args { int fd; const struct kevent *changelist; int nchanges; void *eventlist; int nevents; const struct timespec *timeout; void (*do_each)(void **, struct kevent *); }; static int kevent_copyout(void *arg, struct kevent *kevp, int count) { int i; struct kevent *ke; struct sys_kevent_args *uap; uap = (struct sys_kevent_args *)arg; if (!uap->do_each) { bcopy(kevp, uap->eventlist, count * sizeof *kevp); uap->eventlist = (void *)((struct kevent *)(uap->eventlist) + count); } else { for (ke = kevp, i = 0; i < count; i++, ke++) { uap->do_each(&(uap->eventlist), ke); } } return (0); } /* * Copy 'count' items from the list pointed to by uap->changelist. */ static int kevent_copyin(void *arg, struct kevent *kevp, int count) { struct sys_kevent_args *uap; uap = (struct sys_kevent_args *)arg; bcopy(uap->changelist, kevp, count * sizeof *kevp); uap->changelist += count; return (0); } int ff_kevent_do_each(int kq, const struct kevent *changelist, int nchanges, void *eventlist, int nevents, const struct timespec *timeout, void (*do_each)(void **, struct kevent *)) { int rc; struct timespec ts; ts.tv_sec = 0; ts.tv_nsec = 0; struct sys_kevent_args ska = { kq, changelist, nchanges, eventlist, nevents, &ts, do_each }; struct kevent_copyops k_ops = { &ska, kevent_copyout, kevent_copyin }; if ((rc = kern_kevent(curthread, kq, nchanges, nevents, &k_ops, &ts))) goto kern_fail; rc = curthread->td_retval[0]; return (rc); kern_fail: ff_os_errno(rc); return (-1); } int ff_kevent(int kq, const struct kevent *changelist, int nchanges, struct kevent *eventlist, int nevents, const struct timespec *timeout) { return ff_kevent_do_each(kq, changelist, nchanges, eventlist, nevents, timeout, NULL); } int #if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 31) ff_gettimeofday(struct timeval *tv, void *tz) #else ff_gettimeofday(struct timeval *tv, struct timezone *tz) #endif { long nsec; ff_get_current_time(&(tv->tv_sec), &nsec); tv->tv_usec = nsec/1000; return 0; } int ff_dup(int oldfd) { int rc; struct dup_args da = { .fd = oldfd, }; if ((rc = sys_dup(curthread, &da))) goto kern_fail; rc = curthread->td_retval[0]; return (rc); kern_fail: ff_os_errno(rc); return (-1); } int ff_dup2(int oldfd, int newfd) { int rc; struct dup2_args da = { .from = oldfd, .to = newfd }; if ((rc = sys_dup2(curthread, &da))) goto kern_fail; rc = curthread->td_retval[0]; return (rc); kern_fail: ff_os_errno(rc); return (-1); } int ff_route_ctl(enum FF_ROUTE_CTL req, enum FF_ROUTE_FLAG flag, struct linux_sockaddr *dst, struct linux_sockaddr *gw, struct linux_sockaddr *netmask) { struct sockaddr_storage sa_gw, sa_dst, sa_nm; struct sockaddr *psa_gw, *psa_dst, *psa_nm; int rtreq, rtflag; int rc; struct rt_addrinfo info; struct rib_cmd_info rci; switch (req) { case FF_ROUTE_ADD: rtreq = RTM_ADD; break; case FF_ROUTE_DEL: rtreq = RTM_DELETE; break; case FF_ROUTE_CHANGE: rtreq = RTM_CHANGE; break; default: rc = EINVAL; goto kern_fail; } switch (flag) { case FF_RTF_HOST: rtflag = RTF_HOST; break; case FF_RTF_GATEWAY: rtflag = RTF_GATEWAY; break; default: rc = EINVAL; goto kern_fail; }; bzero((caddr_t)&info, sizeof(info)); info.rti_flags = rtflag; if (gw != NULL) { psa_gw = (struct sockaddr *)&sa_gw; linux2freebsd_sockaddr(gw, sizeof(*gw), psa_gw); info.rti_info[RTAX_GATEWAY] = psa_gw; } else { psa_gw = NULL; } if (dst != NULL) { psa_dst = (struct sockaddr *)&sa_dst; linux2freebsd_sockaddr(dst, sizeof(*dst), psa_dst); info.rti_info[RTAX_DST] = psa_dst; } else { psa_dst = NULL; } if (netmask != NULL) { psa_nm = (struct sockaddr *)&sa_nm; linux2freebsd_sockaddr(netmask, sizeof(*netmask), psa_nm); info.rti_info[RTAX_NETMASK] = psa_nm; } else { psa_nm = NULL; } rc = rib_action(RT_DEFAULT_FIB, rtreq, &info, &rci); if (rc != 0) goto kern_fail; return (rc); kern_fail: ff_os_errno(rc); return (-1); }