Index: sys/compat/linux/linux_socket.c diff -u sys/compat/linux/linux_socket.c.orig sys/compat/linux/linux_socket.c --- sys/compat/linux/linux_socket.c.orig Wed Oct 9 02:34:23 2002 +++ sys/compat/linux/linux_socket.c Mon Jan 20 00:33:34 2003 @@ -30,6 +30,7 @@ /* XXX we use functions that might not exist. */ #include "opt_compat.h" +#include "opt_inet6.h" #ifndef COMPAT_43 #error "Unable to compile Linux-emulator due to missing COMPAT_43 option!" @@ -44,36 +45,27 @@ #include #include #include +#include +#include #include #include #include +#ifdef INET6 +#include +#include +#endif #include #include #include #include -/* - * FreeBSD's socket calls require the sockaddr struct length to agree - * with the address family. Linux does not, so we must force it. - */ -static int -linux_to_bsd_namelen(caddr_t name, int namelen) -{ - uint16_t family; /* XXX must match Linux sockaddr */ +#include - if (copyin(name, &family, sizeof(family))) - return namelen; - - switch (family) { - case AF_INET: - return sizeof(struct sockaddr_in); - case AF_INET6: - return sizeof(struct sockaddr_in6); - } - return namelen; -} +static int linux_sa_get(caddr_t *, struct sockaddr **, + const struct osockaddr *, int *); +static int linux_sa_put(struct osockaddr *); #ifndef __alpha__ static int @@ -87,6 +79,8 @@ return (AF_LOCAL); case LINUX_AF_INET: return (AF_INET); + case LINUX_AF_INET6: + return (AF_INET6); case LINUX_AF_AX25: return (AF_CCITT); case LINUX_AF_IPX: @@ -98,6 +92,29 @@ } static int +bsd_to_linux_domain(int domain) +{ + + switch (domain) { + case AF_UNSPEC: + return (LINUX_AF_UNSPEC); + case AF_LOCAL: + return (LINUX_AF_UNIX); + case AF_INET: + return (LINUX_AF_INET); + case AF_INET6: + return (LINUX_AF_INET6); + case AF_CCITT: + return (LINUX_AF_AX25); + case AF_IPX: + return (LINUX_AF_IPX); + case AF_APPLETALK: + return (LINUX_AF_APPLETALK); + } + return (-1); +} + +static int linux_to_bsd_sockopt_level(int level) { @@ -327,6 +344,13 @@ int type; int protocol; } */ bsd_args; + struct setsockopt_args /* { + int s; + int level; + int name; + caddr_t val; + int valsize; + } */ bsd_setsockopt_args; int error; int retval_socket; @@ -345,13 +369,6 @@ && bsd_args.domain == AF_INET && retval_socket >= 0) { /* It's a raw IP socket: set the IP_HDRINCL option. */ - struct setsockopt_args /* { - int s; - int level; - int name; - caddr_t val; - int valsize; - } */ bsd_setsockopt_args; caddr_t sg; int *hdrincl; @@ -368,13 +385,37 @@ /* Copy back the return value from socket() */ td->td_retval[0] = bsd_setsockopt_args.s; } +#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 (bsd_args.domain == PF_INET6 && retval_socket >= 0 && ip6_v6only) { + caddr_t sg; + int *v6only; + + sg = stackgap_init(); + v6only = (int *)stackgap_alloc(&sg, sizeof(*v6only)); + *v6only = 0; + bsd_setsockopt_args.s = td->td_retval[0]; + bsd_setsockopt_args.level = IPPROTO_IPV6; + bsd_setsockopt_args.name = IPV6_V6ONLY; + bsd_setsockopt_args.val = (caddr_t)v6only; + bsd_setsockopt_args.valsize = sizeof(*v6only); + /* We ignore any error returned by setsockopt() */ + setsockopt(td, &bsd_setsockopt_args); + /* Copy back the return value from socket() */ + td->td_retval[0] = bsd_setsockopt_args.s; + } +#endif return (retval_socket); } struct linux_bind_args { int s; - struct sockaddr *name; + struct osockaddr *name; int namelen; }; @@ -387,20 +428,27 @@ caddr_t name; int namelen; } */ bsd_args; - int error; + caddr_t sg = stackgap_init(); + struct sockaddr *sa; + int namelen, error; if ((error = copyin(args, &linux_args, sizeof(linux_args)))) return (error); + namelen = linux_args.namelen; + error = linux_sa_get(&sg, &sa, linux_args.name, &namelen); + if (error) + return (error); + bsd_args.s = linux_args.s; - bsd_args.name = (caddr_t)linux_args.name; - bsd_args.namelen = linux_to_bsd_namelen(bsd_args.name, linux_args.namelen); + bsd_args.name = (caddr_t) sa; + bsd_args.namelen = (unsigned int) namelen; return (bind(td, &bsd_args)); } struct linux_connect_args { int s; - struct sockaddr * name; + struct osockaddr * name; int namelen; }; int linux_connect(struct thread *, struct linux_connect_args *); @@ -415,9 +463,11 @@ caddr_t name; int namelen; } */ bsd_args; + caddr_t sg = stackgap_init(); + struct sockaddr *sa; struct socket *so; u_int fflag; - int error; + int namelen, error; #ifdef __alpha__ bcopy(args, &linux_args, sizeof(linux_args)); @@ -426,9 +476,14 @@ return (error); #endif /* __alpha__ */ + namelen = linux_args.namelen; + error = linux_sa_get(&sg, &sa, linux_args.name, &namelen); + if (error) + return (error); + bsd_args.s = linux_args.s; - bsd_args.name = (caddr_t)linux_args.name; - bsd_args.namelen = linux_to_bsd_namelen(bsd_args.name, linux_args.namelen); + bsd_args.name = (caddr_t) sa; + bsd_args.namelen = (unsigned int) namelen; error = connect(td, &bsd_args); if (error != EISCONN) return (error); @@ -477,7 +532,7 @@ struct linux_accept_args { int s; - struct sockaddr *addr; + struct osockaddr *addr; int *namelen; }; @@ -506,6 +561,11 @@ error = oaccept(td, &bsd_args); if (error) return (error); + if (linux_args.addr) { + error = linux_sa_put(linux_args.addr); + if (error) + return (error); + } /* * linux appears not to copy flags from the parent socket to the @@ -522,7 +582,7 @@ struct linux_getsockname_args { int s; - struct sockaddr *addr; + struct osockaddr *addr; int *namelen; }; @@ -543,12 +603,18 @@ bsd_args.fdes = linux_args.s; bsd_args.asa = (caddr_t) linux_args.addr; bsd_args.alen = linux_args.namelen; - return (ogetsockname(td, &bsd_args)); + error = ogetsockname(td, &bsd_args); + if (error) + return (error); + error = linux_sa_put(linux_args.addr); + if (error) + return (error); + return (0); } struct linux_getpeername_args { int s; - struct sockaddr *addr; + struct osockaddr *addr; int *namelen; }; @@ -569,7 +635,13 @@ bsd_args.fdes = linux_args.s; bsd_args.asa = (caddr_t) linux_args.addr; bsd_args.alen = linux_args.namelen; - return (ogetpeername(td, &bsd_args)); + error = ogetpeername(td, &bsd_args); + if (error) + return (error); + error = linux_sa_put(linux_args.addr); + if (error) + return (error); + return (0); } struct linux_socketpair_args { @@ -683,17 +755,28 @@ caddr_t to; int tolen; } */ bsd_args; - int error; + caddr_t sg = stackgap_init(); + struct sockaddr *to; + int tolen, error; if ((error = copyin(args, &linux_args, sizeof(linux_args)))) return (error); + tolen = linux_args.tolen; + if (linux_args.to) { + error = linux_sa_get(&sg, &to, + (struct osockaddr *) linux_args.to, &tolen); + if (error) + return (error); + } else + to = NULL; + bsd_args.s = linux_args.s; bsd_args.buf = linux_args.msg; bsd_args.len = linux_args.len; bsd_args.flags = linux_args.flags; - bsd_args.to = linux_args.to; - bsd_args.tolen = linux_args.tolen; + bsd_args.to = (caddr_t) to; + bsd_args.tolen = (unsigned int) tolen; if (linux_check_hdrincl(td, linux_args.s) == 0) /* IP_HDRINCL set, tweak the packet before sending */ @@ -734,7 +817,91 @@ bsd_args.flags = linux_to_bsd_msg_flags(linux_args.flags); bsd_args.from = linux_args.from; bsd_args.fromlenaddr = linux_args.fromlen; - return (orecvfrom(td, &bsd_args)); + error = orecvfrom(td, &bsd_args); + if (error) + return (error); + if (linux_args.from) { + error = linux_sa_put((struct osockaddr *) linux_args.from); + if (error) + return (error); + } + return (0); +} + +struct linux_sendmsg_args { + int s; + const struct msghdr *msg; + int flags; +}; + +static int +linux_sendmsg(struct thread *td, struct linux_sendmsg_args *args) +{ + struct linux_sendmsg_args linux_args; + struct sendmsg_args /* { + int s; + const struct msghdr *msg; + int flags; + } */ bsd_args; + struct msghdr msg; + struct msghdr *nmsg = NULL; + int error; + int level; + caddr_t control; + + if ((error = copyin(args, &linux_args, sizeof(linux_args)))) + return (error); + + error = copyin(linux_args.msg, (caddr_t) &msg, sizeof(msg)); + if (error) + return (error); + + if (msg.msg_name) { + struct sockaddr *sa; + caddr_t sg = stackgap_init(); + + nmsg = (struct msghdr *) stackgap_alloc(&sg, + sizeof(struct msghdr)); + if (!nmsg) + return (ENOMEM); + + error = linux_sa_get(&sg, &sa, + (struct osockaddr *) msg.msg_name, &msg.msg_namelen); + if (error) + return (error); + + msg.msg_name = (struct sockaddr *) sa; + error = copyout(&msg, nmsg, sizeof(struct msghdr)); + if (error) + return (error); + } + + error = copyin(&linux_args.msg->msg_control, &control, + sizeof(caddr_t)); + if (error) + return (error); + + if (control == NULL) + goto done; + + error = copyin(&((struct cmsghdr*)control)->cmsg_level, &level, + sizeof(int)); + if (error) + return (error); + + if (level == 1) { + /* + * Linux thinks that SOL_SOCKET is 1; we know + * that it's really 0xffff, of course. + */ + level = SOL_SOCKET; + error = copyout(&level, + &((struct cmsghdr *)control)->cmsg_level, sizeof(int)); + if (error) + return (error); + } +done: + return (sendmsg(td, &bsd_args)); } struct linux_recvmsg_args { @@ -752,6 +919,7 @@ struct msghdr *msg; int flags; } */ bsd_args; + struct msghdr msg; int error; if ((error = copyin(args, &linux_args, sizeof(linux_args)))) @@ -760,7 +928,16 @@ bsd_args.s = linux_args.s; bsd_args.msg = linux_args.msg; bsd_args.flags = linux_to_bsd_msg_flags(linux_args.flags); - return (recvmsg(td, &bsd_args)); + error = recvmsg(td, &bsd_args); + if (error) + return (error); + + error = copyin((caddr_t)linux_args.msg, (caddr_t)&msg, sizeof(msg)); + if (error) + return (error); + if (msg.msg_name && msg.msg_namelen > 2) + error = linux_sa_put(msg.msg_name); + return (error); } struct linux_shutdown_args { @@ -923,44 +1100,7 @@ case LINUX_GETSOCKOPT: return (linux_getsockopt(td, arg)); case LINUX_SENDMSG: - do { - int error; - int level; - caddr_t control; - struct { - int s; - const struct msghdr *msg; - int flags; - } *uap = arg; - - error = copyin(&uap->msg->msg_control, &control, - sizeof(caddr_t)); - if (error) - return (error); - - if (control == NULL) - goto done; - - error = copyin(&((struct cmsghdr*)control)->cmsg_level, - &level, sizeof(int)); - if (error) - return (error); - - if (level == 1) { - /* - * Linux thinks that SOL_SOCKET is 1; we know - * that it's really 0xffff, of course. - */ - level = SOL_SOCKET; - error = copyout(&level, - &((struct cmsghdr *)control)->cmsg_level, - sizeof(int)); - if (error) - return (error); - } - done: - return (sendmsg(td, arg)); - } while (0); + return (linux_sendmsg(td, arg)); case LINUX_RECVMSG: return (linux_recvmsg(td, arg)); } @@ -969,3 +1109,124 @@ return (ENOSYS); } #endif /*!__alpha__*/ + +/* + * Copy the osockaddr structure pointed to by osa to kernel, adjust + * family and convert to sockaddr, allocate stackgap and put the + * the converted structure there, address on stackgap returned in sap. + */ +static int +linux_sa_get(caddr_t *sgp, struct sockaddr **sap, + const struct osockaddr *osa, int *osalen) +{ + int error=0, bdom; + struct sockaddr *sa, *usa; + struct osockaddr *kosa = (struct osockaddr *) &sa; + int alloclen; +#ifdef INET6 + int oldv6size; + struct sockaddr_in6 *sin6; +#endif + + if (*osalen < 2 || *osalen > UCHAR_MAX || !osa) + return (EINVAL); + + alloclen = *osalen; +#ifdef INET6 + oldv6size = 0; + /* + * Check for old (pre-RFC2553) sockaddr_in6. We may accept it + * if it's a v4-mapped address, so reserve the proper space + * for it. + */ + if (alloclen == sizeof (struct sockaddr_in6) - sizeof (u_int32_t)) { + alloclen = sizeof (struct sockaddr_in6); + oldv6size = 1; + } +#endif + + kosa = (struct osockaddr *) malloc(alloclen, M_TEMP, M_WAITOK); + + if ((error = copyin(osa, (caddr_t) kosa, *osalen))) + goto out; + + bdom = linux_to_bsd_domain(kosa->sa_family); + if (bdom == -1) { + error = EINVAL; + goto out; + } + +#ifdef INET6 + /* + * 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 (oldv6size && bdom == AF_INET6) { + sin6 = (struct sockaddr_in6 *)kosa; + 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))) { + sin6->sin6_scope_id = 0; + } else { + log(LOG_DEBUG, + "obsolete pre-RFC2553 sockaddr_in6 rejected"); + error = EINVAL; + goto out; + } + } else +#endif + if (bdom == AF_INET) + alloclen = sizeof(struct sockaddr_in); + + sa = (struct sockaddr *) kosa; + sa->sa_family = bdom; + sa->sa_len = alloclen; + + usa = (struct sockaddr *) stackgap_alloc(sgp, alloclen); + if (!usa) { + error = ENOMEM; + goto out; + } + + if ((error = copyout(sa, usa, alloclen))) + goto out; + + *sap = usa; + +out: + *osalen = alloclen; + free(kosa, M_TEMP); + return (error); +} + +static int +linux_sa_put(struct osockaddr *osa) +{ + struct osockaddr sa; + int error, bdom; + + /* + * Only read/write the osockaddr family part, the rest is + * not changed. + */ + error = copyin((caddr_t) osa, (caddr_t) &sa, sizeof(sa.sa_family)); + if (error) + return (error); + + bdom = bsd_to_linux_domain(sa.sa_family); + if (bdom == -1) + return (EINVAL); + + sa.sa_family = bdom; + error = copyout(&sa, osa, sizeof(sa.sa_family)); + if (error) + return (error); + + return (0); +} Index: sys/i386/linux/linux.h diff -u sys/i386/linux/linux.h.orig sys/i386/linux/linux.h --- sys/i386/linux/linux.h.orig Sat Jul 13 13:01:30 2002 +++ sys/i386/linux/linux.h Sun Jan 19 00:54:43 2003 @@ -584,6 +584,7 @@ #define LINUX_AF_AX25 3 #define LINUX_AF_IPX 4 #define LINUX_AF_APPLETALK 5 +#define LINUX_AF_INET6 10 #define LINUX_SOL_SOCKET 1 #define LINUX_SOL_IP 0 Index: sys/modules/linux/Makefile diff -u sys/modules/linux/Makefile.orig sys/modules/linux/Makefile --- sys/modules/linux/Makefile.orig Sun Jan 19 21:44:15 2003 +++ sys/modules/linux/Makefile Sun Jan 19 21:44:51 2003 @@ -8,8 +8,8 @@ SRCS= linux_dummy.c linux_file.c linux_getcwd.c linux_ioctl.c linux_ipc.c \ linux_machdep.c linux_mib.c linux_misc.c linux_signal.c linux_socket.c \ linux_stats.c linux_sysctl.c linux_sysent.c linux_sysvec.c \ - linux_util.c opt_compat.h opt_linux.h opt_mac.h opt_vmpage.h \ - vnode_if.h + linux_util.c opt_compat.h opt_inet6.h opt_linux.h opt_mac.h \ + opt_vmpage.h vnode_if.h OBJS= linux_locore.o .if ${MACHINE_ARCH} == "i386" @@ -41,5 +41,8 @@ opt_compat.h: echo "#define COMPAT_43 1" > opt_compat.h + +opt_inet6.h: + echo "#define INET6 1" > opt_inet6.h .include