/*-
* Copyright (c) 1995, 1998, 2008 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Frank van der Linden and Eric Haszlakiewicz.
*
* 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
*/
/*
* Functions in multiarch:
* linux_sys_socketcall : linux_socketcall.c
*/
#ifdef DEBUG_LINUX
#define DPRINTF(a) uprintf a
#else
#define DPRINTF(a)
#endif
/*
* The calls in this file are entered either via the linux_socketcall()
* interface or, on the Alpha, as individual syscalls. The
* linux_socketcall function does any massaging of arguments so that all
* the calls in here need not think that they are anything other
* than a normal syscall.
*/
static int linux_to_bsd_domain(int);
static int bsd_to_linux_domain(int);
static int linux_to_bsd_type(int);
int linux_to_bsd_sopt_level(int);
int linux_to_bsd_so_sockopt(int);
int linux_to_bsd_ip_sockopt(int);
int linux_to_bsd_ipv6_sockopt(int);
int linux_to_bsd_tcp_sockopt(int);
int linux_to_bsd_udp_sockopt(int);
int linux_getifname(struct lwp *, register_t *, void *);
int linux_getifconf(struct lwp *, register_t *, void *);
int linux_getifhwaddr(struct lwp *, register_t *, u_int, void *);
static int linux_get_sa(struct lwp *, int, struct sockaddr_big *,
const struct osockaddr *, socklen_t);
static int linux_sa_put(struct osockaddr *osa);
static int linux_to_bsd_msg_flags(int);
static int bsd_to_linux_msg_flags(int);
static void linux_to_bsd_msghdr(const struct linux_msghdr *, struct msghdr *);
static void bsd_to_linux_msghdr(const struct msghdr *, struct linux_msghdr *);
SCARG(&bsa, protocol) = SCARG(uap, protocol);
SCARG(&bsa, domain) = linux_to_bsd_domain(SCARG(uap, domain));
if (SCARG(&bsa, domain) == -1)
return EINVAL;
SCARG(&bsa, type) = linux_to_bsd_type(SCARG(uap, type));
if (SCARG(&bsa, type) == -1)
return EINVAL;
/*
* Apparently linux uses this to talk to ISDN sockets. If we fail
* now programs seems to handle it, but if we don't we are going
* to fail when we bind and programs don't handle this well.
*/
if (SCARG(&bsa, domain) == AF_ROUTE && SCARG(&bsa, type) == SOCK_RAW)
return ENOTSUP;
error = sys___socket30(l, &bsa, retval);
#ifdef INET6
/*
* Linux AF_INET6 socket has IPV6_V6ONLY setsockopt set to 0 by
* default and some apps depend on this. So, set V6ONLY to 0
* for Linux apps if the sysctl value is set to 1.
*/
if (!error && ip6_v6only && SCARG(&bsa, domain) == PF_INET6) {
struct socket *so;
if (fd_getsock(*retval, &so) == 0) {
int val = 0;
/* ignore error */
(void)so_setsockopt(l, so, IPPROTO_IPV6, IPV6_V6ONLY,
&val, sizeof(val));
l_cc = LINUX_CMSG_FIRSTHDR(&lmsg);
do {
error = copyin(l_cc, &l_cmsg, sizeof(l_cmsg));
if (error)
goto done;
/*
* Sanity check the control message length.
*/
if (l_cmsg.cmsg_len > resid
|| l_cmsg.cmsg_len < sizeof l_cmsg) {
error = EINVAL;
goto done;
}
/*
* Refuse unsupported control messages, and
* translate fields as appropriate.
*/
switch (l_cmsg.cmsg_level) {
case LINUX_SOL_SOCKET:
/* It only differs on some archs */
if (LINUX_SOL_SOCKET != SOL_SOCKET)
l_cmsg.cmsg_level = SOL_SOCKET;
switch(l_cmsg.cmsg_type) {
case LINUX_SCM_RIGHTS:
/* Linux SCM_RIGHTS is same as NetBSD */
break;
case LINUX_SCM_CREDENTIALS:
/* no native equivalent, just drop it */
if (control != mtod(ctl_mbuf, void *))
free(control, M_MBUF);
m_free(ctl_mbuf);
ctl_mbuf = NULL;
msg.msg_control = NULL;
msg.msg_controllen = 0;
goto skipcmsg;
default:
/* other types not supported */
error = EINVAL;
goto done;
}
break;
default:
/* pray and leave intact */
break;
}
for (m = control; m != NULL; ) {
cmsg = mtod(m, struct cmsghdr *);
/*
* Fixup cmsg. We handle two things:
* 0. different sizeof cmsg_len.
* 1. different values for level/type on some archs
* 2. different alignment of CMSG_DATA on some archs
*/
memset(&linux_cmsg, 0, sizeof(linux_cmsg));
linux_cmsg.cmsg_len = cmsg->cmsg_len - LINUX_CMSG_ALIGN_DELTA;
linux_cmsg.cmsg_level = cmsg->cmsg_level;
linux_cmsg.cmsg_type = cmsg->cmsg_type;
dlen = q_end - q;
if (linux_cmsg.cmsg_len > dlen) {
/* Not enough room for the parameter */
dlen -= sizeof linux_cmsg;
if (dlen <= 0)
/* Discard if header wont fit */
break;
mp->msg_flags |= MSG_CTRUNC;
if (linux_cmsg.cmsg_level == SOL_SOCKET
&& linux_cmsg.cmsg_type == SCM_RIGHTS)
/* Do not truncate me ... */
break;
} else
dlen = linux_cmsg.cmsg_len - sizeof linux_cmsg;
switch (linux_cmsg.cmsg_level) {
case SOL_SOCKET:
linux_cmsg.cmsg_level = LINUX_SOL_SOCKET;
switch (linux_cmsg.cmsg_type) {
case SCM_RIGHTS:
/* Linux SCM_RIGHTS is same as NetBSD */
break;
default:
/* other types not supported */
error = EINVAL;
goto done;
}
/* machine dependent ! */
break;
default:
/* pray and leave intact */
break;
}
/* There can be padding between the header and data... */
error = copyout(&linux_cmsg, q, sizeof linux_cmsg);
if (error != 0) {
error = copyout(CCMSG_DATA(cmsg), q + sizeof linux_cmsg,
dlen);
}
if (error != 0) {
/* We must free all the SCM_RIGHTS */
m = control;
break;
}
m = m->m_next;
if (m == NULL || q + LINUX_CMSG_SPACE(dlen) > q_end) {
q += LINUX_CMSG_LEN(dlen);
break;
}
q += LINUX_CMSG_SPACE(dlen);
}
if (error == 0) {
msg.msg_flags = bsd_to_linux_msg_flags(msg.msg_flags);
if (msg.msg_flags < 0)
/* Some flag unsupported by Linux */
error = EINVAL;
else {
ktrkuser("msghdr", &msg, sizeof(msg));
bsd_to_linux_msghdr(&msg, &lmsg);
error = copyout(&lmsg, SCARG(uap, msg), sizeof(lmsg));
}
}
return (error);
}
/*
* Convert socket option level from Linux to NetBSD value. Only SOL_SOCKET
* is different, the rest matches IPPROTO_* on both systems.
*/
int
linux_to_bsd_sopt_level(int llevel)
{
switch (llevel) {
case LINUX_SOL_SOCKET:
return SOL_SOCKET;
case LINUX_SOL_IP:
return IPPROTO_IP;
#ifdef INET6
case LINUX_SOL_IPV6:
return IPPROTO_IPV6;
#endif
case LINUX_SOL_TCP:
return IPPROTO_TCP;
case LINUX_SOL_UDP:
return IPPROTO_UDP;
default:
return -1;
}
}
/*
* Convert Linux socket level socket option numbers to NetBSD values.
*/
int
linux_to_bsd_so_sockopt(int lopt)
{
switch (lopt) {
case LINUX_SO_DEBUG:
return SO_DEBUG;
case LINUX_SO_REUSEADDR:
/*
* Linux does not implement SO_REUSEPORT, but allows reuse of
* a host:port pair through SO_REUSEADDR even if the address
* is not a multicast-address. Effectively, this means that we
* should use SO_REUSEPORT to allow Linux applications to not
* exit with EADDRINUSE
*/
return SO_REUSEPORT;
case LINUX_SO_TYPE:
return SO_TYPE;
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_NO_CHECK:
case LINUX_SO_PRIORITY:
return -1;
case LINUX_SO_LINGER:
return SO_LINGER;
case LINUX_SO_BSDCOMPAT:
case LINUX_SO_PASSCRED:
case LINUX_SO_PEERCRED:
return -1;
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_SECURITY_AUTHENTICATION:
case LINUX_SO_SECURITY_ENCRYPTION_TRANSPORT:
case LINUX_SO_SECURITY_ENCRYPTION_NETWORK:
case LINUX_SO_BINDTODEVICE:
case LINUX_SO_ATTACH_FILTER:
case LINUX_SO_DETACH_FILTER:
case LINUX_SO_PEERNAME:
return -1;
case LINUX_SO_TIMESTAMP:
return SO_TIMESTAMP;
case LINUX_SO_ACCEPTCONN:
case LINUX_SO_PEERSEC:
case LINUX_SO_SNDBUFFORCE:
case LINUX_SO_RCVBUFFORCE:
case LINUX_SO_PASSSEC:
case LINUX_SO_TIMESTAMPNS:
case LINUX_SO_MARK:
case LINUX_SO_TIMESTAMPING:
case LINUX_SO_PROTOCOL:
case LINUX_SO_DOMAIN:
case LINUX_SO_RXQ_OVFL:
case LINUX_SO_WIFI_STATUS:
case LINUX_SO_PEEK_OFF:
case LINUX_SO_NOFCS:
default:
return -1;
}
}
/*
* Convert Linux IP level socket option number to NetBSD values.
*/
int
linux_to_bsd_ip_sockopt(int lopt)
{
switch (lopt) {
case LINUX_IP_TOS:
return IP_TOS;
case LINUX_IP_TTL:
return IP_TTL;
case LINUX_IP_RETOPTS:
return IP_RETOPTS;
case LINUX_IP_PKTINFO:
return IP_PKTINFO;
case LINUX_IP_RECVOPTS:
return IP_RECVOPTS;
case LINUX_IP_HDRINCL:
return IP_HDRINCL;
case LINUX_IP_MULTICAST_TTL:
return IP_MULTICAST_TTL;
case LINUX_IP_MULTICAST_LOOP:
return IP_MULTICAST_LOOP;
case LINUX_IP_MULTICAST_IF:
return IP_MULTICAST_IF;
case LINUX_IP_ADD_MEMBERSHIP:
return IP_ADD_MEMBERSHIP;
case LINUX_IP_DROP_MEMBERSHIP:
return IP_DROP_MEMBERSHIP;
case LINUX_IP_RECVERR:
case LINUX_IP_FREEBIND:
return -2; /* ignored */
case LINUX_IP_MULTICAST_ALL:
return -3; /* noprotoopt */
default:
return -1;
}
}
/*
* Convert Linux IPV6 level socket option number to NetBSD values.
*/
#ifdef INET6
int
linux_to_bsd_ipv6_sockopt(int lopt)
{
switch (lopt) {
case LINUX_IPV6_V6ONLY:
return IPV6_V6ONLY;
case LINUX_IPV6_MULTICAST_HOPS:
return IPV6_MULTICAST_HOPS;
case LINUX_IPV6_MULTICAST_ALL:
return -3; /* noprotoopt */
default:
return -1;
}
}
#endif
/*
* Convert Linux TCP level socket option number to NetBSD values.
*/
int
linux_to_bsd_tcp_sockopt(int lopt)
{
switch (lopt) {
case LINUX_TCP_NODELAY:
return TCP_NODELAY;
case LINUX_TCP_MAXSEG:
return TCP_MAXSEG;
default:
return -1;
}
}
/*
* Convert Linux UDP level socket option number to NetBSD values.
*/
int
linux_to_bsd_udp_sockopt(int lopt)
{
switch (lopt) {
default:
return -1;
}
}
/*
* Another reasonably straightforward function: setsockopt(2).
* The level and option numbers are converted; the values passed
* are not (yet) converted, the ones currently implemented don't
* need conversion, as they are the same on both systems.
*/
int
linux_sys_setsockopt(struct lwp *l, const struct linux_sys_setsockopt_args *uap, register_t *retval)
{
/* {
syscallarg(int) s;
syscallarg(int) level;
syscallarg(int) optname;
syscallarg(void *) optval;
syscallarg(int) optlen;
} */
struct sys_setsockopt_args bsa;
int name;
/*
* Linux supports only SOL_SOCKET for AF_LOCAL domain sockets
* and returns EOPNOTSUPP for other levels
*/
if (SCARG(&bsa, level) != SOL_SOCKET) {
struct socket *so;
int error, family;
/* fd_getsock() will use the descriptor for us */
if ((error = fd_getsock(SCARG(&bsa, s), &so)) != 0)
return error;
family = so->so_proto->pr_domain->dom_family;
fd_putfile(SCARG(&bsa, s));
if (family == AF_LOCAL)
return EOPNOTSUPP;
}
switch (SCARG(&bsa, level)) {
case SOL_SOCKET:
name = linux_to_bsd_so_sockopt(SCARG(uap, optname));
break;
case IPPROTO_IP:
name = linux_to_bsd_ip_sockopt(SCARG(uap, optname));
break;
#ifdef INET6
case IPPROTO_IPV6:
name = linux_to_bsd_ipv6_sockopt(SCARG(uap, optname));
break;
#endif
case IPPROTO_TCP:
name = linux_to_bsd_tcp_sockopt(SCARG(uap, optname));
break;
case IPPROTO_UDP:
name = linux_to_bsd_udp_sockopt(SCARG(uap, optname));
break;
default:
return EINVAL;
}
switch (name) {
case -1:
return EINVAL;
case -2:
return 0;
case -3:
return ENOPROTOOPT;
}
SCARG(&bsa, name) = name;
return sys_setsockopt(l, &bsa, retval);
}
/*
* getsockopt(2) is very much the same as setsockopt(2) (see above)
*/
int
linux_sys_getsockopt(struct lwp *l, const struct linux_sys_getsockopt_args *uap, register_t *retval)
{
/* {
syscallarg(int) s;
syscallarg(int) level;
syscallarg(int) optname;
syscallarg(void *) optval;
syscallarg(int *) optlen;
} */
struct sys_getsockopt_args bga;
int name;
switch (SCARG(&bga, level)) {
case SOL_SOCKET:
name = linux_to_bsd_so_sockopt(SCARG(uap, optname));
break;
case IPPROTO_IP:
name = linux_to_bsd_ip_sockopt(SCARG(uap, optname));
break;
#ifdef INET6
case IPPROTO_IPV6:
name = linux_to_bsd_ipv6_sockopt(SCARG(uap, optname));
break;
#endif
case IPPROTO_TCP:
name = linux_to_bsd_tcp_sockopt(SCARG(uap, optname));
break;
case IPPROTO_UDP:
name = linux_to_bsd_udp_sockopt(SCARG(uap, optname));
break;
default:
return EINVAL;
}
switch (name) {
case -1:
case -2: /* we can't ignore, since we don't know what to return */
return EINVAL;
case -3:
return ENOPROTOOPT;
}
SCARG(&bga, name) = name;
return sys_getsockopt(l, &bga, retval);
}
int
linux_getifname(struct lwp *l, register_t *retval, void *data)
{
struct ifnet *ifp;
struct linux_ifreq ifr;
int error;
int s;
error = copyin(data, &ifr, sizeof(ifr));
if (error)
return error;
s = pserialize_read_enter();
ifp = if_byindex(ifr.ifr_ifru.ifru_ifindex);
if (ifp == NULL) {
pserialize_read_exit(s);
return ENODEV;
}
int
linux_getifhwaddr(struct lwp *l, register_t *retval, u_int fd,
void *data)
{
/* Not the full structure, just enough to map what we do here */
struct linux_ifreq lreq;
file_t *fp;
struct ifaddr *ifa;
struct ifnet *ifp;
struct sockaddr_dl *sadl;
int error, found;
int index, ifnum;
int s;
/*
* We can't emulate this ioctl by calling sys_ioctl() to run
* SIOCGIFCONF, because the user buffer is not of the right
* type to take those results. We can't use kernel buffers to
* receive the results, as the implementation of sys_ioctl()
* and ifconf() [which implements SIOCGIFCONF] use
* copyin()/copyout() which will fail on kernel addresses.
*
* So, we must duplicate code from sys_ioctl() and ifconf(). Ugh.
*/
if ((fp = fd_getfile(fd)) == NULL)
return (EBADF);
error = copyin(data, &lreq, sizeof(lreq));
if (error)
goto out;
lreq.ifr_name[LINUX_IFNAMSIZ-1] = '\0'; /* just in case */
/*
* Try real interface name first, then fake "ethX"
*/
found = 0;
s = pserialize_read_enter();
IFNET_READER_FOREACH(ifp) {
if (found)
break;
if (strcmp(lreq.ifr_name, ifp->if_xname))
/* not this interface */
continue;
found=1;
if (IFADDR_READER_EMPTY(ifp)) {
pserialize_read_exit(s);
error = ENODEV;
goto out;
}
IFADDR_READER_FOREACH(ifa, ifp) {
sadl = satosdl(ifa->ifa_addr);
/* only return ethernet addresses */
/* XXX what about FDDI, etc. ? */
if (sadl->sdl_family != AF_LINK ||
sadl->sdl_type != IFT_ETHER)
continue;
memcpy(&lreq.ifr_hwaddr.sa_data, CLLADDR(sadl),
MIN(sadl->sdl_alen,
sizeof(lreq.ifr_hwaddr.sa_data)));
lreq.ifr_hwaddr.sa_family =
sadl->sdl_family;
pserialize_read_exit(s);
/*
* Don't try to interpret socket ioctl calls that are done
* on a device filedescriptor, just pass them through, to
* emulate Linux behaviour. Use PTIOCLINUX so that the
* device will only handle these if it's prepared to do
* so, to avoid unexpected things from happening.
*/
if (isdev) {
dosys = 0;
ioctlf = fp->f_ops->fo_ioctl;
pt.com = SCARG(uap, com);
pt.data = SCARG(uap, data);
error = ioctlf(fp, PTIOCLINUX, &pt);
/*
* XXX hack: if the function returns EJUSTRETURN,
* it has stuffed a sysctl return value in pt.data.
*/
if (error == EJUSTRETURN) {
retval[0] = (register_t)pt.data;
error = 0;
}
goto out;
}
com = SCARG(uap, com);
retval[0] = 0;
switch (com) {
case LINUX_SIOCGIFNAME:
error = linux_getifname(l, retval, SCARG(uap, data));
dosys = 0;
break;
case LINUX_SIOCGIFCONF:
error = linux_getifconf(l, retval, SCARG(uap, data));
dosys = 0;
break;
case LINUX_SIOCGIFFLAGS:
SCARG(&ia, com) = OSIOCGIFFLAGS;
break;
case LINUX_SIOCSIFFLAGS:
SCARG(&ia, com) = OSIOCSIFFLAGS;
break;
case LINUX_SIOCGIFADDR:
SCARG(&ia, com) = OOSIOCGIFADDR;
break;
case LINUX_SIOCGIFDSTADDR:
SCARG(&ia, com) = OOSIOCGIFDSTADDR;
break;
case LINUX_SIOCGIFBRDADDR:
SCARG(&ia, com) = OOSIOCGIFBRDADDR;
break;
case LINUX_SIOCGIFNETMASK:
SCARG(&ia, com) = OOSIOCGIFNETMASK;
break;
case LINUX_SIOCGIFMTU:
SCARG(&ia, com) = OSIOCGIFMTU;
break;
case LINUX_SIOCADDMULTI:
SCARG(&ia, com) = OSIOCADDMULTI;
break;
case LINUX_SIOCDELMULTI:
SCARG(&ia, com) = OSIOCDELMULTI;
break;
case LINUX_SIOCGIFHWADDR:
error = linux_getifhwaddr(l, retval, SCARG(uap, fd),
SCARG(uap, data));
dosys = 0;
break;
default:
error = EINVAL;
}
if (error == EISCONN) {
struct socket *so;
int state, prflags;
/* fd_getsock() will use the descriptor for us */
if (fd_getsock(SCARG(uap, s), &so) != 0)
return EISCONN;
solock(so);
state = so->so_state;
prflags = so->so_proto->pr_flags;
sounlock(so);
fd_putfile(SCARG(uap, s));
/*
* We should only let this call succeed once per
* non-blocking connect; however we don't have
* a convenient place to keep that state..
*/
if ((state & (SS_ISCONNECTED|SS_NBIO)) ==
(SS_ISCONNECTED|SS_NBIO) &&
(prflags & PR_CONNREQUIRED))
return 0;
}
if ((error = linux_sa_put((struct osockaddr *)SCARG(uap, asa))))
return (error);
return (0);
}
/*
* Copy the osockaddr structure pointed to by name to sb, adjust
* family and convert to sockaddr.
*/
static int
linux_get_sa(struct lwp *l, int s, struct sockaddr_big *sb,
const struct osockaddr *name, socklen_t namelen)
{
int error, bdom;
error = copyin(name, sb, namelen);
if (error)
return error;
bdom = linux_to_bsd_domain(sb->sb_family);
if (bdom == -1)
return EINVAL;
/*
* If the family is unspecified, use address family of the socket.
* This avoid triggering strict family checks in netinet/in_pcb.c et.al.
*/
if (bdom == AF_UNSPEC) {
struct socket *so;
/* fd_getsock() will use the descriptor for us */
if ((error = fd_getsock(s, &so)) != 0)
return error;
/*
* Older Linux IPv6 code uses obsolete RFC2133 struct sockaddr_in6,
* which lacks the scope id compared with RFC2553 one. If we detect
* the situation, reject the address and write a message to system log.
*
* Still accept addresses for which the scope id is not used.
*/
if (bdom == AF_INET6 &&
namelen == sizeof(struct sockaddr_in6) - sizeof(uint32_t)) {
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sb;
if (!IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr) &&
(IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr) ||
IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr) ||
IN6_IS_ADDR_V4COMPAT(&sin6->sin6_addr) ||
IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr) ||
IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr))) {
struct proc *p = l->l_proc;
int uid = l->l_cred ? kauth_cred_geteuid(l->l_cred) : -1;
/*
* Linux is less strict than NetBSD and permits namelen to be larger
* than valid struct sockaddr_in*. If this is the case, truncate
* the value to the correct size, so that NetBSD networking does not
* return an error.
*/
switch (bdom) {
case AF_INET:
namelen = MIN(namelen, sizeof(struct sockaddr_in));
break;
case AF_INET6:
namelen = MIN(namelen, sizeof(struct sockaddr_in6));
break;
}
static int
linux_sa_put(struct osockaddr *osa)
{
struct sockaddr sa;
struct osockaddr *kosa;
int error, bdom, len;
/*
* Only read/write the sockaddr family and length part, the rest is
* not changed.
*/
len = sizeof(sa.sa_len) + sizeof(sa.sa_family);
error = copyin(osa, &sa, len);
if (error)
return (error);
bdom = bsd_to_linux_domain(sa.sa_family);
if (bdom == -1)
return (EINVAL);
/* Note: we convert from sockaddr to osockaddr here, too */
kosa = (struct osockaddr *) &sa;
kosa->sa_family = bdom;
error = copyout(kosa, osa, len);
if (error)
return (error);
if (SCARG(uap, timeout)) {
getnanotime(&now);
timespecsub(&now, &ts, &now);
if (now.tv_sec > 0)
break;
}
if (flags & MSG_WAITFORONE)
flags |= MSG_DONTWAIT;
}
if (from != NULL)
m_free(from);
*retval = dg;
/*
* If we succeeded at least once, return 0, hopefully so->so_rerror
* will catch it next time.
*/
if (error && dg > 0) {
so->so_rerror = error;
error = 0;
}