Index: sys/alpha/linux/linux.h diff -u sys/alpha/linux/linux.h.orig sys/alpha/linux/linux.h --- sys/alpha/linux/linux.h.orig Tue Oct 16 05:06:34 2001 +++ sys/alpha/linux/linux.h Sun Jan 26 03:36:45 2003 @@ -421,6 +421,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/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 Tue Sep 24 16:02:54 2002 +++ sys/compat/linux/linux_socket.c Tue Jan 28 14:43:37 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!" @@ -41,41 +42,134 @@ #include #include #include +#include #include #include +#include #include +#include #include #include #include +#ifdef INET6 +#include +#include +#endif #include #include #include #include +#include + +#ifndef M_WAITOK +#define M_WAITOK 0 +#endif + +static int do_sa_get(struct sockaddr **, const struct osockaddr *, int *, + struct malloc_type *); +static int linux_to_bsd_domain(int); + +/* + * Reads a linux sockaddr and does any necessary translation. + * Linux sockaddrs don't have a length field, only a family. + */ +static int +linux_getsockaddr(struct sockaddr **sap, const struct osockaddr *osa, int len) +{ + int osalen = len; + + return (do_sa_get(sap, osa, &osalen, M_SONAME)); +} + /* - * FreeBSD's socket calls require the sockaddr struct length to agree - * with the address family. Linux does not, so we must force it. + * Copy the osockaddr structure pointed to by osa to kernel, adjust + * family and convert to sockaddr. */ static int -linux_to_bsd_namelen(caddr_t name, int namelen) +do_sa_get(struct sockaddr **sap, const struct osockaddr *osa, int *osalen, + struct malloc_type *mtype) { - uint16_t family; /* XXX must match Linux sockaddr */ + int error=0, bdom; + struct sockaddr *sa; + struct osockaddr *kosa; + int alloclen; +#ifdef INET6 + int oldv6size; + struct sockaddr_in6 *sin6; +#endif - if (copyin(name, &family, sizeof(family))) - return namelen; + if (*osalen < 2 || *osalen > UCHAR_MAX || !osa) + return (EINVAL); - switch (family) { - case AF_INET: - return sizeof(struct sockaddr_in); - case AF_INET6: - return sizeof(struct sockaddr_in6); + 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; } - return namelen; +#endif + + MALLOC(kosa, struct osockaddr *, alloclen, mtype, 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; + + *sap = sa; + *osalen = alloclen; + return (0); + +out: + FREE(kosa, mtype); + return (error); } -#ifndef __alpha__ static int linux_to_bsd_domain(int domain) { @@ -87,6 +181,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: @@ -97,6 +193,30 @@ return (-1); } +#ifndef __alpha__ +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) { @@ -206,6 +326,65 @@ return ret_flags; } +/* + * Allocate stackgap and put the converted sockaddr 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) +{ + struct sockaddr *sa, *usa; + int alloclen, error; + + alloclen = *osalen; + error = do_sa_get(&sa, osa, &alloclen, M_TEMP); + if (error) + return (error); + + usa = (struct sockaddr *) stackgap_alloc(sgp, alloclen); + if (!usa) { + error = ENOMEM; + goto out; + } + + if ((error = copyout(sa, usa, alloclen))) + goto out; + + *sap = usa; + *osalen = alloclen; + +out: + FREE(sa, 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); +} + /* Return 0 if IP_HDRINCL is set for the given socket. */ static int linux_check_hdrincl(struct thread *td, int s) @@ -327,6 +506,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 +531,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 +547,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; }; @@ -382,25 +585,22 @@ linux_bind(struct thread *td, struct linux_bind_args *args) { struct linux_bind_args linux_args; - struct bind_args /* { - int s; - caddr_t name; - int namelen; - } */ bsd_args; + struct sockaddr *sa; int error; if ((error = copyin(args, &linux_args, sizeof(linux_args)))) 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); - return (bind(td, &bsd_args)); + error = linux_getsockaddr(&sa, linux_args.name, linux_args.namelen); + if (error) + return (error); + + return (kern_bind(td, linux_args.s, sa)); } struct linux_connect_args { int s; - struct sockaddr * name; + struct osockaddr * name; int namelen; }; int linux_connect(struct thread *, struct linux_connect_args *); @@ -410,12 +610,8 @@ linux_connect(struct thread *td, struct linux_connect_args *args) { struct linux_connect_args linux_args; - struct connect_args /* { - int s; - caddr_t name; - int namelen; - } */ bsd_args; struct socket *so; + struct sockaddr *sa; u_int fflag; int error; @@ -426,10 +622,11 @@ return (error); #endif /* __alpha__ */ - 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); - error = connect(td, &bsd_args); + error = linux_getsockaddr(&sa, linux_args.name, linux_args.namelen); + if (error) + return (error); + + error = kern_connect(td, linux_args.s, sa); if (error != EISCONN) return (error); @@ -477,7 +674,7 @@ struct linux_accept_args { int s; - struct sockaddr *addr; + struct osockaddr *addr; int *namelen; }; @@ -490,6 +687,9 @@ caddr_t name; int *anamelen; } */ bsd_args; + struct close_args /* { + int fd; + } */ c_args; struct fcntl_args /* { int fd; int cmd; @@ -506,6 +706,14 @@ error = oaccept(td, &bsd_args); if (error) return (error); + if (linux_args.addr) { + error = linux_sa_put(linux_args.addr); + if (error) { + c_args.fd = td->td_retval[0]; + (void)close(td, &c_args); + return (error); + } + } /* * linux appears not to copy flags from the parent socket to the @@ -522,7 +730,7 @@ struct linux_getsockname_args { int s; - struct sockaddr *addr; + struct osockaddr *addr; int *namelen; }; @@ -543,12 +751,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 +783,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 +903,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 +965,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 +1067,7 @@ struct msghdr *msg; int flags; } */ bsd_args; + struct msghdr msg; int error; if ((error = copyin(args, &linux_args, sizeof(linux_args)))) @@ -760,7 +1076,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 +1248,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)); } Index: sys/kern/uipc_syscalls.c diff -u sys/kern/uipc_syscalls.c.orig sys/kern/uipc_syscalls.c --- sys/kern/uipc_syscalls.c.orig Tue Jan 21 17:55:55 2003 +++ sys/kern/uipc_syscalls.c Sun Jan 26 03:36:45 2003 @@ -61,6 +61,7 @@ #include #include #include +#include #include #include #ifdef KTRACE @@ -167,28 +168,40 @@ int namelen; } */ *uap; { - struct socket *so; struct sockaddr *sa; int error; + if ((error = getsockaddr(&sa, uap->name, uap->namelen)) != 0) + return (error); + + return (kern_bind(td, uap->s, sa)); +} + +int +kern_bind(td, fd, sa) + struct thread *td; + int fd; + struct sockaddr *sa; +{ + struct socket *so; + int error; + mtx_lock(&Giant); - if ((error = fgetsock(td, uap->s, &so, NULL)) != 0) + if ((error = fgetsock(td, fd, &so, NULL)) != 0) goto done2; - if ((error = getsockaddr(&sa, uap->name, uap->namelen)) != 0) - goto done1; #ifdef MAC error = mac_check_socket_bind(td->td_ucred, so, sa); - if (error) { - FREE(sa, M_SONAME); + if (error) goto done1; - } #endif error = sobind(so, sa, td); - FREE(sa, M_SONAME); +#ifdef MAC done1: +#endif fputsock(so); done2: mtx_unlock(&Giant); + FREE(sa, M_SONAME); return (error); } @@ -442,20 +455,33 @@ int namelen; } */ *uap; { - struct socket *so; struct sockaddr *sa; + int error; + + error = getsockaddr(&sa, uap->name, uap->namelen); + if (error) + return error; + + return (kern_connect(td, uap->s, sa)); +} + + +int +kern_connect(td, fd, sa) + struct thread *td; + int fd; + struct sockaddr *sa; +{ + struct socket *so; int error, s; mtx_lock(&Giant); - if ((error = fgetsock(td, uap->s, &so, NULL)) != 0) + if ((error = fgetsock(td, fd, &so, NULL)) != 0) goto done2; if ((so->so_state & SS_NBIO) && (so->so_state & SS_ISCONNECTING)) { error = EALREADY; goto done1; } - error = getsockaddr(&sa, uap->name, uap->namelen); - if (error) - goto done1; #ifdef MAC error = mac_check_socket_connect(td->td_ucred, so, sa); if (error) @@ -465,7 +491,6 @@ if (error) goto bad; if ((so->so_state & SS_NBIO) && (so->so_state & SS_ISCONNECTING)) { - FREE(sa, M_SONAME); error = EINPROGRESS; goto done1; } @@ -482,13 +507,13 @@ splx(s); bad: so->so_state &= ~SS_ISCONNECTING; - FREE(sa, M_SONAME); if (error == ERESTART) error = EINTR; done1: fputsock(so); done2: mtx_unlock(&Giant); + FREE(sa, M_SONAME); return (error); } 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 Wed Jul 10 00:57:12 2002 +++ sys/i386/linux/linux.h Sun Jan 26 03:36:45 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 Sep 8 11:59:38 2002 +++ sys/modules/linux/Makefile Sun Jan 26 03:36:45 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 Index: sys/sys/syscallsubr.h diff -u sys/sys/syscallsubr.h.orig sys/sys/syscallsubr.h --- sys/sys/syscallsubr.h.orig Sat Oct 26 04:10:58 2002 +++ sys/sys/syscallsubr.h Sun Jan 26 03:36:46 2003 @@ -31,15 +31,19 @@ #include #include +struct sockaddr; + int kern___getcwd(struct thread *td, u_char *buf, enum uio_seg bufseg, u_int buflen); int kern_access(struct thread *td, char *path, enum uio_seg pathseg, int flags); +int kern_bind(struct thread *td, int fd, struct sockaddr *sa); int kern_chdir(struct thread *td, char *path, enum uio_seg pathseg); int kern_chmod(struct thread *td, char *path, enum uio_seg pathseg, int mode); int kern_chown(struct thread *td, char *path, enum uio_seg pathseg, int uid, int gid); +int kern_connect(struct thread *td, int fd, struct sockaddr *sa); int kern_fcntl(struct thread *td, int fd, int cmd, intptr_t arg); int kern_futimes(struct thread *td, int fd, struct timeval *tptr, enum uio_seg tptrseg);