diff options
author | meem <none@none> | 2008-05-08 16:56:26 -0700 |
---|---|---|
committer | meem <none@none> | 2008-05-08 16:56:26 -0700 |
commit | ca9327a6de44d69ddab3668cc1e143ce781387a3 (patch) | |
tree | e8f80a575694596c82ba7326781eca8437ecc661 /usr/src | |
parent | 7975540170826a816320fa0cf07ba06a66c4252d (diff) | |
download | illumos-gate-ca9327a6de44d69ddab3668cc1e143ce781387a3.tar.gz |
PSARC/2008/265 STREAMS _I_CMD and pfiles TLI support
4960773 pfiles should show info for TLI endpoints
Diffstat (limited to 'usr/src')
-rw-r--r-- | usr/src/cmd/mdb/common/modules/genunix/streams.c | 55 | ||||
-rw-r--r-- | usr/src/cmd/ptools/pfiles/pfiles.c | 78 | ||||
-rw-r--r-- | usr/src/uts/common/inet/tcp/tcp.c | 226 | ||||
-rw-r--r-- | usr/src/uts/common/inet/udp/udp.c | 251 | ||||
-rw-r--r-- | usr/src/uts/common/io/dedump.c | 17 | ||||
-rw-r--r-- | usr/src/uts/common/os/streamio.c | 190 | ||||
-rw-r--r-- | usr/src/uts/common/sys/stream.h | 14 | ||||
-rw-r--r-- | usr/src/uts/common/sys/stropts.h | 37 | ||||
-rw-r--r-- | usr/src/uts/common/sys/strsubr.h | 3 |
9 files changed, 629 insertions, 242 deletions
diff --git a/usr/src/cmd/mdb/common/modules/genunix/streams.c b/usr/src/cmd/mdb/common/modules/genunix/streams.c index 56d633157d..c000bc4ecc 100644 --- a/usr/src/cmd/mdb/common/modules/genunix/streams.c +++ b/usr/src/cmd/mdb/common/modules/genunix/streams.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -152,7 +151,7 @@ static const struct str_flags stdf[] = { { SF(0x00020000), "unused" }, { SF(0x00040000), "unused" }, { SF(STRTOSTOP), "block background writes" }, - { SF(0x00100000), "unused" }, + { SF(STRCMDWAIT), "someone is doing an _I_CMD" }, { SF(0x00200000), "unused" }, { SF(STRMOUNT), "stream is mounted" }, { SF(STRNOTATMARK), "Not at mark (when empty read q)" }, @@ -211,6 +210,7 @@ static const strtypes_t mbt[] = { { "M_STARTI", M_STARTI, "restart reception after stop" }, { "M_PCEVENT", M_PCEVENT, "Obsoleted: do not use" }, { "M_UNHANGUP", M_UNHANGUP, "line reconnect" }, + { "M_CMD", M_CMD, "out-of-band ioctl command" }, { NULL, 0, NULL } }; @@ -1255,17 +1255,17 @@ mblk_prt(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) quiet = TRUE; if (mdb_getopts(argc, argv, - 'v', MDB_OPT_SETBITS, TRUE, &verbose, - 'q', MDB_OPT_SETBITS, TRUE, &quiet, - 'f', MDB_OPT_STR, &flag, - 'F', MDB_OPT_STR, ¬_flag, - 't', MDB_OPT_STR, &typ, - 'T', MDB_OPT_STR, ¬_typ, - 'l', MDB_OPT_UINT64, &len, - 'L', MDB_OPT_UINT64, &llen, - 'G', MDB_OPT_UINT64, &glen, - 'b', MDB_OPT_UINT64, &blen, - 'd', MDB_OPT_UINTPTR, &dbaddr, + 'v', MDB_OPT_SETBITS, TRUE, &verbose, + 'q', MDB_OPT_SETBITS, TRUE, &quiet, + 'f', MDB_OPT_STR, &flag, + 'F', MDB_OPT_STR, ¬_flag, + 't', MDB_OPT_STR, &typ, + 'T', MDB_OPT_STR, ¬_typ, + 'l', MDB_OPT_UINT64, &len, + 'L', MDB_OPT_UINT64, &llen, + 'G', MDB_OPT_UINT64, &glen, + 'b', MDB_OPT_UINT64, &blen, + 'd', MDB_OPT_UINTPTR, &dbaddr, NULL) != argc) return (DCMD_USAGE); @@ -1328,16 +1328,17 @@ mblk_prt(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) db_type = dblk.db_type; /* M_DATA is 0, so tmask has special value 0xff for it */ - if ((tmask != 0) && - (((tmask == M_DATA_T) && (db_type != M_DATA)) || - ((tmask != M_DATA_T) && (db_type != tmask)))) - return (DCMD_OK); - + if (tmask != 0) { + if ((tmask == M_DATA_T && db_type != M_DATA) || + (tmask != M_DATA_T && db_type != tmask)) + return (DCMD_OK); + } - if ((not_tmask != 0) && - (((not_tmask == M_DATA_T) && (db_type == M_DATA)) || - (db_type == not_tmask))) - return (DCMD_OK); + if (not_tmask != 0) { + if ((not_tmask == M_DATA_T && db_type == M_DATA) || + (db_type == not_tmask)) + return (DCMD_OK); + } if (dbaddr != 0 && (uintptr_t)mblk.b_datap != dbaddr) return (DCMD_OK); @@ -1577,9 +1578,9 @@ queue_help(void) " q2wrq: given a queue addr print write queue pointer\n" " q2otherq: given a queue addr print other queue pointer\n" " q2syncq: given a queue addr print syncq pointer" - " (::help syncq)\n" + " (::help syncq)\n" " q2stream: given a queue addr print its stream pointer\n" - "\t\t(see ::help stream and ::help stdata)\n\n" + "\t\t(see ::help stream and ::help stdata)\n\n" "To walk q_next pointer of the queue use\n" " queue_addr::walk qnext\n"); } diff --git a/usr/src/cmd/ptools/pfiles/pfiles.c b/usr/src/cmd/ptools/pfiles/pfiles.c index 891a370c7d..e23c9fd756 100644 --- a/usr/src/cmd/ptools/pfiles/pfiles.c +++ b/usr/src/cmd/ptools/pfiles/pfiles.c @@ -20,7 +20,7 @@ */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -33,22 +33,20 @@ #include <ctype.h> #include <string.h> #include <signal.h> -#include <errno.h> #include <dirent.h> #include <limits.h> #include <door.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/stat.h> -#include <sys/mman.h> #include <sys/mkdev.h> +#include <sys/stropts.h> +#include <sys/timod.h> #include <sys/un.h> -#include <netdb.h> #include <libproc.h> #include <netinet/in.h> #include <netinet/udp.h> #include <arpa/inet.h> -#include <netdb.h> #define copyflock(dst, src) \ (dst).l_type = (src).l_type; \ @@ -66,6 +64,7 @@ static boolean_t nflag = B_FALSE; static void intr(int); static void dofcntl(struct ps_prochandle *, int, int, int); static void dosocket(struct ps_prochandle *, int); +static void dotli(struct ps_prochandle *, int); static void show_files(struct ps_prochandle *); static void show_fileflags(int); static void show_door(struct ps_prochandle *, int); @@ -196,6 +195,7 @@ show_files(struct ps_prochandle *Pr) { DIR *dirp; struct dirent *dentp; + const char *dev; char pname[100]; char fname[PATH_MAX]; struct stat64 statb; @@ -305,13 +305,34 @@ show_files(struct ps_prochandle *Pr) (void) sprintf(pname, "/proc/%d/path/%d", (int)pid, fd); - if ((ret = readlink(pname, fname, PATH_MAX - 1)) > 0) { - fname[ret] = '\0'; - (void) printf(" %s\n", fname); + if ((ret = readlink(pname, fname, PATH_MAX - 1)) <= 0) + continue; + + fname[ret] = '\0'; + + if ((statb.st_mode & S_IFMT) == S_IFCHR && + (dev = strrchr(fname, ':')) != NULL) { + /* + * There's no elegant way to determine if a + * character device supports TLI, so we lame + * out and just check a hardcoded list of + * known TLI devices. + */ + int i; + const char *tlidevs[] = + { "tcp", "tcp6", "udp", "udp6", NULL }; + + dev++; /* skip past the `:' */ + for (i = 0; tlidevs[i] != NULL; i++) { + if (strcmp(dev, tlidevs[i]) == 0) { + dotli(Pr, fd); + break; + } + } } + (void) printf(" %s\n", fname); } } - (void) closedir(dirp); } @@ -469,13 +490,15 @@ show_door(struct ps_prochandle *Pr, int fd) (void) printf("pid %d", (int)door_info.di_target); } +/* + * Print out the socket address pointed to by `sa'. `len' is only + * needed for AF_UNIX sockets. + */ static void show_sockaddr(const char *str, struct sockaddr *sa, socklen_t len) { - /* LINTED pointer assignment */ - struct sockaddr_in *so_in = (struct sockaddr_in *)sa; - /* LINTED pointer assignment */ - struct sockaddr_in6 *so_in6 = (struct sockaddr_in6 *)sa; + struct sockaddr_in *so_in = (struct sockaddr_in *)(void *)sa; + struct sockaddr_in6 *so_in6 = (struct sockaddr_in6 *)(void *)sa; struct sockaddr_un *so_un = (struct sockaddr_un *)sa; char abuf[INET6_ADDRSTRLEN]; const char *p; @@ -484,14 +507,12 @@ show_sockaddr(const char *str, struct sockaddr *sa, socklen_t len) default: return; case AF_INET: - (void) printf("\t%s: AF_INET %s port: %u\n", - str, + (void) printf("\t%s: AF_INET %s port: %u\n", str, inet_ntop(AF_INET, &so_in->sin_addr, abuf, sizeof (abuf)), ntohs(so_in->sin_port)); return; case AF_INET6: - (void) printf("\t%s: AF_INET6 %s port: %u\n", - str, + (void) printf("\t%s: AF_INET6 %s port: %u\n", str, inet_ntop(AF_INET6, &so_in6->sin6_addr, abuf, sizeof (abuf)), ntohs(so_in->sin_port)); @@ -500,7 +521,7 @@ show_sockaddr(const char *str, struct sockaddr *sa, socklen_t len) if (len >= sizeof (so_un->sun_family)) { /* Null terminate */ len -= sizeof (so_un->sun_family); - so_un->sun_path[len] = NULL; + so_un->sun_path[len] = '\0'; (void) printf("\t%s: AF_UNIX %s\n", str, so_un->sun_path); } @@ -632,8 +653,7 @@ dosocket(struct ps_prochandle *Pr, int fd) int type, tlen; tlen = sizeof (type); - if (pr_getsockopt(Pr, fd, SOL_SOCKET, SO_TYPE, &type, &tlen) - == 0) + if (pr_getsockopt(Pr, fd, SOL_SOCKET, SO_TYPE, &type, &tlen) == 0) show_socktype((uint_t)type); show_sockopts(Pr, fd); @@ -646,3 +666,21 @@ dosocket(struct ps_prochandle *Pr, int fd) if (pr_getpeername(Pr, fd, sa, &len) == 0) show_sockaddr("peername", sa, len); } + +/* the file is a TLI endpoint */ +static void +dotli(struct ps_prochandle *Pr, int fd) +{ + struct strcmd strcmd; + + strcmd.sc_len = STRCMDBUFSIZE; + strcmd.sc_timeout = 5; + + strcmd.sc_cmd = TI_GETMYNAME; + if (pr_ioctl(Pr, fd, _I_CMD, &strcmd, sizeof (strcmd)) == 0) + show_sockaddr("sockname", (void *)&strcmd.sc_buf, 0); + + strcmd.sc_cmd = TI_GETPEERNAME; + if (pr_ioctl(Pr, fd, _I_CMD, &strcmd, sizeof (strcmd)) == 0) + show_sockaddr("peername", (void *)&strcmd.sc_buf, 0); +} diff --git a/usr/src/uts/common/inet/tcp/tcp.c b/usr/src/uts/common/inet/tcp/tcp.c index 12b781c0bc..9f443745c3 100644 --- a/usr/src/uts/common/inet/tcp/tcp.c +++ b/usr/src/uts/common/inet/tcp/tcp.c @@ -18493,6 +18493,119 @@ no_more_eagers: } } +static int +tcp_getmyname(tcp_t *tcp, struct sockaddr *sa, uint_t *salenp) +{ + sin_t *sin = (sin_t *)sa; + sin6_t *sin6 = (sin6_t *)sa; + + switch (tcp->tcp_family) { + case AF_INET: + ASSERT(tcp->tcp_ipversion == IPV4_VERSION); + + if (*salenp < sizeof (sin_t)) + return (EINVAL); + + *sin = sin_null; + sin->sin_family = AF_INET; + sin->sin_port = tcp->tcp_lport; + sin->sin_addr.s_addr = tcp->tcp_ipha->ipha_src; + break; + + case AF_INET6: + if (*salenp < sizeof (sin6_t)) + return (EINVAL); + + *sin6 = sin6_null; + sin6->sin6_family = AF_INET6; + sin6->sin6_port = tcp->tcp_lport; + if (tcp->tcp_ipversion == IPV4_VERSION) { + IN6_IPADDR_TO_V4MAPPED(tcp->tcp_ipha->ipha_src, + &sin6->sin6_addr); + } else { + sin6->sin6_addr = tcp->tcp_ip6h->ip6_src; + } + break; + } + + return (0); +} + +static int +tcp_getpeername(tcp_t *tcp, struct sockaddr *sa, uint_t *salenp) +{ + sin_t *sin = (sin_t *)sa; + sin6_t *sin6 = (sin6_t *)sa; + + if (tcp->tcp_state < TCPS_SYN_RCVD) + return (ENOTCONN); + + switch (tcp->tcp_family) { + case AF_INET: + ASSERT(tcp->tcp_ipversion == IPV4_VERSION); + + if (*salenp < sizeof (sin_t)) + return (EINVAL); + + *sin = sin_null; + sin->sin_family = AF_INET; + sin->sin_port = tcp->tcp_fport; + IN6_V4MAPPED_TO_IPADDR(&tcp->tcp_remote_v6, + sin->sin_addr.s_addr); + break; + + case AF_INET6: + if (*salenp < sizeof (sin6_t)) + return (EINVAL); + + *sin6 = sin6_null; + sin6->sin6_family = AF_INET6; + sin6->sin6_port = tcp->tcp_fport; + sin6->sin6_addr = tcp->tcp_remote_v6; + if (tcp->tcp_ipversion == IPV6_VERSION) { + sin6->sin6_flowinfo = tcp->tcp_ip6h->ip6_vcf & + ~IPV6_VERS_AND_FLOW_MASK; + } + break; + } + + return (0); +} + +/* + * Handle special out-of-band ioctl requests (see PSARC/2008/265). + */ +static void +tcp_wput_cmdblk(queue_t *q, mblk_t *mp) +{ + void *data; + mblk_t *datamp = mp->b_cont; + tcp_t *tcp = Q_TO_TCP(q); + cmdblk_t *cmdp = (cmdblk_t *)mp->b_rptr; + + if (datamp == NULL || MBLKL(datamp) < cmdp->cb_len) { + cmdp->cb_error = EPROTO; + qreply(q, mp); + return; + } + + data = datamp->b_rptr; + + switch (cmdp->cb_cmd) { + case TI_GETPEERNAME: + cmdp->cb_error = tcp_getpeername(tcp, data, &cmdp->cb_len); + break; + case TI_GETMYNAME: + cmdp->cb_error = tcp_getmyname(tcp, data, &cmdp->cb_len); + break; + default: + cmdp->cb_error = EINVAL; + break; + } + + qreply(q, mp); +} + void tcp_wput(queue_t *q, mblk_t *mp) { @@ -18525,6 +18638,11 @@ tcp_wput(queue_t *q, mblk_t *mp) (*tcp_squeue_wput_proc)(connp->conn_sqp, mp, tcp_output, connp, SQTAG_TCP_OUTPUT); return; + + case M_CMD: + tcp_wput_cmdblk(q, mp); + return; + case M_PROTO: case M_PCPROTO: /* @@ -18573,14 +18691,6 @@ tcp_wput(queue_t *q, mblk_t *mp) tcp_ioctl_abort_conn(q, mp); return; case TI_GETPEERNAME: - if (tcp->tcp_state < TCPS_SYN_RCVD) { - iocp->ioc_error = ENOTCONN; - iocp->ioc_count = 0; - mp->b_datap->db_type = M_IOCACK; - qreply(q, mp); - return; - } - /* FALLTHRU */ case TI_GETMYNAME: mi_copyin(q, mp, NULL, SIZEOF_STRUCT(strbuf, iocp->ioc_flag)); @@ -21897,16 +22007,14 @@ static void tcp_wput_iocdata(tcp_t *tcp, mblk_t *mp) { mblk_t *mp1; + struct iocblk *iocp = (struct iocblk *)mp->b_rptr; STRUCT_HANDLE(strbuf, sb); - uint16_t port; - queue_t *q = tcp->tcp_wq; - in6_addr_t v6addr; - ipaddr_t v4addr; - uint32_t flowinfo = 0; - int addrlen; + queue_t *q = tcp->tcp_wq; + int error; + uint_t addrlen; /* Make sure it is one of ours. */ - switch (((struct iocblk *)mp->b_rptr)->ioc_cmd) { + switch (iocp->ioc_cmd) { case TI_GETMYNAME: case TI_GETPEERNAME: break; @@ -21937,93 +22045,35 @@ tcp_wput_iocdata(tcp_t *tcp, mblk_t *mp) return; } - STRUCT_SET_HANDLE(sb, ((struct iocblk *)mp->b_rptr)->ioc_flag, - (void *)mp1->b_rptr); + STRUCT_SET_HANDLE(sb, iocp->ioc_flag, (void *)mp1->b_rptr); addrlen = tcp->tcp_family == AF_INET ? sizeof (sin_t) : sizeof (sin6_t); - if (STRUCT_FGET(sb, maxlen) < addrlen) { mi_copy_done(q, mp, EINVAL); return; } - switch (((struct iocblk *)mp->b_rptr)->ioc_cmd) { + + mp1 = mi_copyout_alloc(q, mp, STRUCT_FGETP(sb, buf), addrlen, B_TRUE); + if (mp1 == NULL) + return; + + switch (iocp->ioc_cmd) { case TI_GETMYNAME: - if (tcp->tcp_family == AF_INET) { - if (tcp->tcp_ipversion == IPV4_VERSION) { - v4addr = tcp->tcp_ipha->ipha_src; - } else { - /* can't return an address in this case */ - v4addr = 0; - } - } else { - /* tcp->tcp_family == AF_INET6 */ - if (tcp->tcp_ipversion == IPV4_VERSION) { - IN6_IPADDR_TO_V4MAPPED(tcp->tcp_ipha->ipha_src, - &v6addr); - } else { - v6addr = tcp->tcp_ip6h->ip6_src; - } - } - port = tcp->tcp_lport; + error = tcp_getmyname(tcp, (void *)mp1->b_rptr, &addrlen); break; case TI_GETPEERNAME: - if (tcp->tcp_family == AF_INET) { - if (tcp->tcp_ipversion == IPV4_VERSION) { - IN6_V4MAPPED_TO_IPADDR(&tcp->tcp_remote_v6, - v4addr); - } else { - /* can't return an address in this case */ - v4addr = 0; - } - } else { - /* tcp->tcp_family == AF_INET6) */ - v6addr = tcp->tcp_remote_v6; - if (tcp->tcp_ipversion == IPV6_VERSION) { - /* - * No flowinfo if tcp->tcp_ipversion is v4. - * - * flowinfo was already initialized to zero - * where it was declared above, so only - * set it if ipversion is v6. - */ - flowinfo = tcp->tcp_ip6h->ip6_vcf & - ~IPV6_VERS_AND_FLOW_MASK; - } - } - port = tcp->tcp_fport; + error = tcp_getpeername(tcp, (void *)mp1->b_rptr, &addrlen); break; - default: - mi_copy_done(q, mp, EPROTO); - return; } - mp1 = mi_copyout_alloc(q, mp, STRUCT_FGETP(sb, buf), addrlen, B_TRUE); - if (!mp1) - return; - if (tcp->tcp_family == AF_INET) { - sin_t *sin; - - STRUCT_FSET(sb, len, (int)sizeof (sin_t)); - sin = (sin_t *)mp1->b_rptr; - mp1->b_wptr = (uchar_t *)&sin[1]; - *sin = sin_null; - sin->sin_family = AF_INET; - sin->sin_addr.s_addr = v4addr; - sin->sin_port = port; + if (error != 0) { + mi_copy_done(q, mp, error); } else { - /* tcp->tcp_family == AF_INET6 */ - sin6_t *sin6; + mp1->b_wptr += addrlen; + STRUCT_FSET(sb, len, addrlen); - STRUCT_FSET(sb, len, (int)sizeof (sin6_t)); - sin6 = (sin6_t *)mp1->b_rptr; - mp1->b_wptr = (uchar_t *)&sin6[1]; - *sin6 = sin6_null; - sin6->sin6_family = AF_INET6; - sin6->sin6_flowinfo = flowinfo; - sin6->sin6_addr = v6addr; - sin6->sin6_port = port; + /* Copy out the address */ + mi_copyout(q, mp); } - /* Copy out the address */ - mi_copyout(q, mp); } /* diff --git a/usr/src/uts/common/inet/udp/udp.c b/usr/src/uts/common/inet/udp/udp.c index d4d99f187d..2282373476 100644 --- a/usr/src/uts/common/inet/udp/udp.c +++ b/usr/src/uts/common/inet/udp/udp.c @@ -7273,6 +7273,142 @@ done: return (mp); } + +static int +udp_getpeername(udp_t *udp, struct sockaddr *sa, uint_t *salenp) +{ + sin_t *sin = (sin_t *)sa; + sin6_t *sin6 = (sin6_t *)sa; + + ASSERT(RW_LOCK_HELD(&udp->udp_rwlock)); + + if (udp->udp_state != TS_DATA_XFER) + return (ENOTCONN); + + switch (udp->udp_family) { + case AF_INET: + ASSERT(udp->udp_ipversion == IPV4_VERSION); + + if (*salenp < sizeof (sin_t)) + return (EINVAL); + + *salenp = sizeof (sin_t); + *sin = sin_null; + sin->sin_family = AF_INET; + sin->sin_port = udp->udp_dstport; + sin->sin_addr.s_addr = V4_PART_OF_V6(udp->udp_v6dst); + break; + + case AF_INET6: + if (*salenp < sizeof (sin6_t)) + return (EINVAL); + + *salenp = sizeof (sin6_t); + *sin6 = sin6_null; + sin6->sin6_family = AF_INET6; + sin6->sin6_port = udp->udp_dstport; + sin6->sin6_addr = udp->udp_v6dst; + sin6->sin6_flowinfo = udp->udp_flowinfo; + break; + } + + return (0); +} + +static int +udp_getmyname(udp_t *udp, struct sockaddr *sa, uint_t *salenp) +{ + sin_t *sin = (sin_t *)sa; + sin6_t *sin6 = (sin6_t *)sa; + + ASSERT(RW_LOCK_HELD(&udp->udp_rwlock)); + + switch (udp->udp_family) { + case AF_INET: + ASSERT(udp->udp_ipversion == IPV4_VERSION); + + if (*salenp < sizeof (sin_t)) + return (EINVAL); + + *salenp = sizeof (sin_t); + *sin = sin_null; + sin->sin_family = AF_INET; + sin->sin_port = udp->udp_port; + + /* + * If udp_v6src is unspecified, we might be bound to broadcast + * / multicast. Use udp_bound_v6src as local address instead + * (that could also still be unspecified). + */ + if (!IN6_IS_ADDR_V4MAPPED_ANY(&udp->udp_v6src) && + !IN6_IS_ADDR_UNSPECIFIED(&udp->udp_v6src)) { + sin->sin_addr.s_addr = V4_PART_OF_V6(udp->udp_v6src); + } else { + sin->sin_addr.s_addr = + V4_PART_OF_V6(udp->udp_bound_v6src); + } + break; + + case AF_INET6: + if (*salenp < sizeof (sin6_t)) + return (EINVAL); + + *salenp = sizeof (sin6_t); + *sin6 = sin6_null; + sin6->sin6_family = AF_INET6; + sin6->sin6_port = udp->udp_port; + sin6->sin6_flowinfo = udp->udp_flowinfo; + + /* + * If udp_v6src is unspecified, we might be bound to broadcast + * / multicast. Use udp_bound_v6src as local address instead + * (that could also still be unspecified). + */ + if (!IN6_IS_ADDR_UNSPECIFIED(&udp->udp_v6src)) + sin6->sin6_addr = udp->udp_v6src; + else + sin6->sin6_addr = udp->udp_bound_v6src; + break; + } + + return (0); +} + +/* + * Handle special out-of-band ioctl requests (see PSARC/2008/265). + */ +static void +udp_wput_cmdblk(queue_t *q, mblk_t *mp) +{ + void *data; + mblk_t *datamp = mp->b_cont; + udp_t *udp = Q_TO_UDP(q); + cmdblk_t *cmdp = (cmdblk_t *)mp->b_rptr; + + if (datamp == NULL || MBLKL(datamp) < cmdp->cb_len) { + cmdp->cb_error = EPROTO; + qreply(q, mp); + return; + } + data = datamp->b_rptr; + + rw_enter(&udp->udp_rwlock, RW_READER); + switch (cmdp->cb_cmd) { + case TI_GETPEERNAME: + cmdp->cb_error = udp_getpeername(udp, data, &cmdp->cb_len); + break; + case TI_GETMYNAME: + cmdp->cb_error = udp_getmyname(udp, data, &cmdp->cb_len); + break; + default: + cmdp->cb_error = EINVAL; + break; + } + rw_exit(&udp->udp_rwlock); + + qreply(q, mp); +} + static void udp_wput_other(queue_t *q, mblk_t *mp) { @@ -7293,6 +7429,10 @@ udp_wput_other(queue_t *q, mblk_t *mp) cr = DB_CREDDEF(mp, connp->conn_cred); switch (db->db_type) { + case M_CMD: + udp_wput_cmdblk(q, mp); + return; + case M_PROTO: case M_PCPROTO: if (mp->b_wptr - rptr < sizeof (t_scalar_t)) { @@ -7492,16 +7632,14 @@ static void udp_wput_iocdata(queue_t *q, mblk_t *mp) { mblk_t *mp1; + struct iocblk *iocp = (struct iocblk *)mp->b_rptr; STRUCT_HANDLE(strbuf, sb); - uint16_t port; - in6_addr_t v6addr; - ipaddr_t v4addr; - uint32_t flowinfo = 0; - int addrlen; - udp_t *udp = Q_TO_UDP(q); + udp_t *udp = Q_TO_UDP(q); + int error; + uint_t addrlen; /* Make sure it is one of ours. */ - switch (((struct iocblk *)mp->b_rptr)->ioc_cmd) { + switch (iocp->ioc_cmd) { case TI_GETMYNAME: case TI_GETPEERNAME: break; @@ -7544,102 +7682,39 @@ udp_wput_iocdata(queue_t *q, mblk_t *mp) * and TI_GETPEERNAME. Next we copyout the requested * address and then we'll copyout the strbuf. */ - STRUCT_SET_HANDLE(sb, ((struct iocblk *)mp->b_rptr)->ioc_flag, - (void *)mp1->b_rptr); - if (udp->udp_family == AF_INET) - addrlen = sizeof (sin_t); - else - addrlen = sizeof (sin6_t); - + STRUCT_SET_HANDLE(sb, iocp->ioc_flag, (void *)mp1->b_rptr); + addrlen = udp->udp_family == AF_INET ? sizeof (sin_t) : sizeof (sin6_t); if (STRUCT_FGET(sb, maxlen) < addrlen) { mi_copy_done(q, mp, EINVAL); return; } - switch (((struct iocblk *)mp->b_rptr)->ioc_cmd) { + + mp1 = mi_copyout_alloc(q, mp, STRUCT_FGETP(sb, buf), addrlen, B_TRUE); + if (mp1 == NULL) + return; + + rw_enter(&udp->udp_rwlock, RW_READER); + switch (iocp->ioc_cmd) { case TI_GETMYNAME: - if (udp->udp_family == AF_INET) { - ASSERT(udp->udp_ipversion == IPV4_VERSION); - if (!IN6_IS_ADDR_V4MAPPED_ANY(&udp->udp_v6src) && - !IN6_IS_ADDR_UNSPECIFIED(&udp->udp_v6src)) { - v4addr = V4_PART_OF_V6(udp->udp_v6src); - } else { - /* - * INADDR_ANY - * udp_v6src is not set, we might be bound to - * broadcast/multicast. Use udp_bound_v6src as - * local address instead (that could - * also still be INADDR_ANY) - */ - v4addr = V4_PART_OF_V6(udp->udp_bound_v6src); - } - } else { - /* udp->udp_family == AF_INET6 */ - if (!IN6_IS_ADDR_UNSPECIFIED(&udp->udp_v6src)) { - v6addr = udp->udp_v6src; - } else { - /* - * UNSPECIFIED - * udp_v6src is not set, we might be bound to - * broadcast/multicast. Use udp_bound_v6src as - * local address instead (that could - * also still be UNSPECIFIED) - */ - v6addr = udp->udp_bound_v6src; - } - } - port = udp->udp_port; + error = udp_getmyname(udp, (void *)mp1->b_rptr, &addrlen); break; case TI_GETPEERNAME: - if (udp->udp_state != TS_DATA_XFER) { - mi_copy_done(q, mp, ENOTCONN); - return; - } - if (udp->udp_family == AF_INET) { - ASSERT(udp->udp_ipversion == IPV4_VERSION); - v4addr = V4_PART_OF_V6(udp->udp_v6dst); - } else { - /* udp->udp_family == AF_INET6) */ - v6addr = udp->udp_v6dst; - flowinfo = udp->udp_flowinfo; - } - port = udp->udp_dstport; + error = udp_getpeername(udp, (void *)mp1->b_rptr, &addrlen); break; - default: - mi_copy_done(q, mp, EPROTO); - return; } - mp1 = mi_copyout_alloc(q, mp, STRUCT_FGETP(sb, buf), addrlen, B_TRUE); - if (!mp1) - return; - - if (udp->udp_family == AF_INET) { - sin_t *sin; + rw_exit(&udp->udp_rwlock); - STRUCT_FSET(sb, len, (int)sizeof (sin_t)); - sin = (sin_t *)mp1->b_rptr; - mp1->b_wptr = (uchar_t *)&sin[1]; - *sin = sin_null; - sin->sin_family = AF_INET; - sin->sin_addr.s_addr = v4addr; - sin->sin_port = port; + if (error != 0) { + mi_copy_done(q, mp, error); } else { - /* udp->udp_family == AF_INET6 */ - sin6_t *sin6; + mp1->b_wptr += addrlen; + STRUCT_FSET(sb, len, addrlen); - STRUCT_FSET(sb, len, (int)sizeof (sin6_t)); - sin6 = (sin6_t *)mp1->b_rptr; - mp1->b_wptr = (uchar_t *)&sin6[1]; - *sin6 = sin6_null; - sin6->sin6_family = AF_INET6; - sin6->sin6_flowinfo = flowinfo; - sin6->sin6_addr = v6addr; - sin6->sin6_port = port; + /* Copy out the address */ + mi_copyout(q, mp); } - /* Copy out the address */ - mi_copyout(q, mp); } - static int udp_unitdata_opt_process(queue_t *q, mblk_t *mp, int *errorp, udpattrs_t *udpattrs) diff --git a/usr/src/uts/common/io/dedump.c b/usr/src/uts/common/io/dedump.c index 16c52c500d..4c68621cf4 100644 --- a/usr/src/uts/common/io/dedump.c +++ b/usr/src/uts/common/io/dedump.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -125,6 +124,15 @@ dedump_ssize(mblk_t *mp) } static void +dedump_cmdblk(mblk_t *mp) +{ + struct cmdblk *cbp = (struct cmdblk *)mp->b_rptr; + + (void) printf("%s cmd %x cred %p len %u error %d\n", hdr, cbp->cb_cmd, + (void *)cbp->cb_cr, cbp->cb_len, cbp->cb_error); +} + +static void dedump_iocblk(mblk_t *mp) { struct iocblk *ic = (struct iocblk *)mp->b_rptr; @@ -205,6 +213,7 @@ static msgfmt_t msgfmt[256] = { { M_STARTI, "M_STARTI ", dedump_raw }, { M_PCEVENT, "M_PCEVENT ", dedump_raw }, { M_UNHANGUP, "M_UNHANGUP", dedump_raw }, + { M_CMD, "M_CMD ", dedump_cmdblk }, }; /*ARGSUSED1*/ diff --git a/usr/src/uts/common/os/streamio.c b/usr/src/uts/common/os/streamio.c index d80fa67f56..3fcbf8634b 100644 --- a/usr/src/uts/common/os/streamio.c +++ b/usr/src/uts/common/os/streamio.c @@ -143,6 +143,7 @@ static uint32_t ioc_id; static void putback(struct stdata *, queue_t *, mblk_t *, int); static void strcleanall(struct vnode *); static int strwsrv(queue_t *); +static int strdocmd(struct stdata *, struct strcmd *, cred_t *); /* * qinit and module_info structures for stream head read and write queues @@ -391,6 +392,7 @@ ckreturn: stp->sd_wroff = 0; stp->sd_tail = 0; stp->sd_iocblk = NULL; + stp->sd_cmdblk = NULL; stp->sd_pushcnt = 0; stp->sd_qn_minpsz = 0; stp->sd_qn_maxpsz = INFPSZ - 1; /* used to check for initialization */ @@ -811,6 +813,8 @@ strclose(struct vnode *vp, int flag, cred_t *crp) vp->v_stream = NULL; mutex_exit(&vp->v_lock); mutex_enter(&stp->sd_lock); + freemsg(stp->sd_cmdblk); + stp->sd_cmdblk = NULL; stp->sd_flag &= ~STRCLOSE; cv_broadcast(&stp->sd_monitor); mutex_exit(&stp->sd_lock); @@ -2124,6 +2128,24 @@ strrput_nondata(queue_t *q, mblk_t *bp) freemsg(bp); return (0); + case M_CMD: + if (MBLKL(bp) != sizeof (cmdblk_t)) { + freemsg(bp); + return (0); + } + + mutex_enter(&stp->sd_lock); + if (stp->sd_flag & STRCMDWAIT) { + ASSERT(stp->sd_cmdblk == NULL); + stp->sd_cmdblk = bp; + cv_broadcast(&stp->sd_monitor); + mutex_exit(&stp->sd_lock); + } else { + mutex_exit(&stp->sd_lock); + freemsg(bp); + } + return (0); + case M_FLUSH: /* * Flush queues. The indication of which queues to flush @@ -3180,6 +3202,7 @@ strioctl(struct vnode *vp, int cmd, intptr_t arg, int flag, int copyflag, cred_t *crp, int *rvalp) { struct stdata *stp; + struct strcmd *scp; struct strioctl strioc; struct uio uio; struct iovec iov; @@ -3552,6 +3575,39 @@ strioctl(struct vnode *vp, int cmd, intptr_t arg, int flag, int copyflag, } return (error); + case _I_CMD: + /* + * Like I_STR, but without using M_IOC* messages and without + * copyins/copyouts beyond the passed-in argument. + */ + if (stp->sd_flag & STRHUP) + return (ENXIO); + + if ((scp = kmem_alloc(sizeof (strcmd_t), KM_NOSLEEP)) == NULL) + return (ENOMEM); + + if (copyin((void *)arg, scp, sizeof (strcmd_t))) { + kmem_free(scp, sizeof (strcmd_t)); + return (EFAULT); + } + + access = job_control_type(scp->sc_cmd); + mutex_enter(&stp->sd_lock); + if (access != -1 && (error = i_straccess(stp, access)) != 0) { + mutex_exit(&stp->sd_lock); + kmem_free(scp, sizeof (strcmd_t)); + return (error); + } + mutex_exit(&stp->sd_lock); + + *rvalp = 0; + if ((error = strdocmd(stp, scp, crp)) == 0) { + if (copyout(scp, (void *)arg, sizeof (strcmd_t))) + error = EFAULT; + } + kmem_free(scp, sizeof (strcmd_t)); + return (error); + case I_NREAD: /* * Return number of bytes of data in first message @@ -6321,6 +6377,140 @@ waitioc: } /* + * Send an M_CMD message downstream and wait for a reply. This is a ptools + * special used to retrieve information from modules/drivers a stream without + * being subjected to flow control or interfering with pending messages on the + * stream (e.g. an ioctl in flight). + */ +int +strdocmd(struct stdata *stp, struct strcmd *scp, cred_t *crp) +{ + mblk_t *mp; + struct cmdblk *cmdp; + int error = 0; + int errs = STRHUP|STRDERR|STWRERR|STPLEX; + clock_t rval, timeout = STRTIMOUT; + + if (scp->sc_len < 0 || scp->sc_len > sizeof (scp->sc_buf) || + scp->sc_timeout < -1) + return (EINVAL); + + if (scp->sc_timeout > 0) + timeout = scp->sc_timeout * MILLISEC; + + if ((mp = allocb_cred(sizeof (struct cmdblk), crp)) == NULL) + return (ENOMEM); + + crhold(crp); + + cmdp = (struct cmdblk *)mp->b_wptr; + cmdp->cb_cr = crp; + cmdp->cb_cmd = scp->sc_cmd; + cmdp->cb_len = scp->sc_len; + cmdp->cb_error = 0; + mp->b_wptr += sizeof (struct cmdblk); + + DB_TYPE(mp) = M_CMD; + DB_CPID(mp) = curproc->p_pid; + + /* + * Copy in the payload. + */ + if (cmdp->cb_len > 0) { + mp->b_cont = allocb_cred(sizeof (scp->sc_buf), crp); + if (mp->b_cont == NULL) { + error = ENOMEM; + goto out; + } + + /* cb_len comes from sc_len, which has already been checked */ + ASSERT(cmdp->cb_len <= sizeof (scp->sc_buf)); + (void) bcopy(scp->sc_buf, mp->b_cont->b_wptr, cmdp->cb_len); + mp->b_cont->b_wptr += cmdp->cb_len; + DB_CPID(mp->b_cont) = curproc->p_pid; + } + + /* + * Since this mechanism is strictly for ptools, and since only one + * process can be grabbed at a time, we simply fail if there's + * currently an operation pending. + */ + mutex_enter(&stp->sd_lock); + if (stp->sd_flag & STRCMDWAIT) { + mutex_exit(&stp->sd_lock); + error = EBUSY; + goto out; + } + stp->sd_flag |= STRCMDWAIT; + ASSERT(stp->sd_cmdblk == NULL); + mutex_exit(&stp->sd_lock); + + putnext(stp->sd_wrq, mp); + mp = NULL; + + /* + * Timed wait for acknowledgment. If the reply has already arrived, + * don't sleep. If awakened from the sleep, fail only if the reply + * has not arrived by then. Otherwise, process the reply. + */ + mutex_enter(&stp->sd_lock); + while (stp->sd_cmdblk == NULL) { + if (stp->sd_flag & errs) { + if ((error = strgeterr(stp, errs, 0)) != 0) + goto waitout; + } + + rval = str_cv_wait(&stp->sd_monitor, &stp->sd_lock, timeout, 0); + if (stp->sd_cmdblk != NULL) + break; + + if (rval <= 0) { + error = (rval == 0) ? EINTR : ETIME; + goto waitout; + } + } + + /* + * We received a reply. + */ + mp = stp->sd_cmdblk; + stp->sd_cmdblk = NULL; + ASSERT(mp != NULL && DB_TYPE(mp) == M_CMD); + ASSERT(stp->sd_flag & STRCMDWAIT); + stp->sd_flag &= ~STRCMDWAIT; + mutex_exit(&stp->sd_lock); + + cmdp = (struct cmdblk *)mp->b_rptr; + if ((error = cmdp->cb_error) != 0) + goto out; + + /* + * Data may have been returned in the reply (cb_len > 0). + * If so, copy it out to the user's buffer. + */ + if (cmdp->cb_len > 0) { + if (mp->b_cont == NULL || MBLKL(mp->b_cont) < cmdp->cb_len) { + error = EPROTO; + goto out; + } + + cmdp->cb_len = MIN(cmdp->cb_len, sizeof (scp->sc_buf)); + (void) bcopy(mp->b_cont->b_rptr, scp->sc_buf, cmdp->cb_len); + } + scp->sc_len = cmdp->cb_len; +out: + freemsg(mp); + crfree(crp); + return (error); +waitout: + ASSERT(stp->sd_cmdblk == NULL); + stp->sd_flag &= ~STRCMDWAIT; + mutex_exit(&stp->sd_lock); + crfree(crp); + return (error); +} + +/* * For the SunOS keyboard driver. * Return the next available "ioctl" sequence number. * Exported, so that streams modules can send "ioctl" messages diff --git a/usr/src/uts/common/sys/stream.h b/usr/src/uts/common/sys/stream.h index 7142a1f19d..3eca2fefdf 100644 --- a/usr/src/uts/common/sys/stream.h +++ b/usr/src/uts/common/sys/stream.h @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -469,6 +469,7 @@ typedef struct bcache { #define M_STARTI 0x90 /* restart reception after stop */ #define M_PCEVENT 0x91 /* Obsoleted: do not use */ #define M_UNHANGUP 0x92 /* line reconnect, sigh */ +#define M_CMD 0x93 /* out-of-band ioctl command */ /* * Queue message class definitions. @@ -588,7 +589,6 @@ union ioctypes { * Options structure for M_SETOPTS message. This is sent upstream * by a module or driver to set stream head options. */ - struct stroptions { uint_t so_flags; /* options to set */ short so_readopt; /* read option */ @@ -664,6 +664,16 @@ typedef struct infod { #define INFOD_COUNT 0x08 /* return count of mblk(s) */ #define INFOD_COPYOUT 0x10 /* copyout any M_DATA mblk(s) */ +/* + * Structure used by _I_CMD mechanism, similar in spirit to iocblk. + */ +typedef struct cmdblk { + int cb_cmd; /* ioctl command type */ + cred_t *cb_cr; /* full credentials */ + uint_t cb_len; /* payload size */ + int cb_error; /* error code */ +} cmdblk_t; + #endif /* _KERNEL */ /* diff --git a/usr/src/uts/common/sys/stropts.h b/usr/src/uts/common/sys/stropts.h index 3d5cc9617c..354de4b186 100644 --- a/usr/src/uts/common/sys/stropts.h +++ b/usr/src/uts/common/sys/stropts.h @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -24,7 +23,7 @@ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -278,14 +277,12 @@ extern "C" { * IOCTLs (STR|050) - (STR|055) are available for use. */ -#define _I_MUXID2FD (STR|056) /* Private, get a fd from a muxid */ -#define _I_INSERT (STR|057) /* Private, insert a module */ -#define _I_REMOVE (STR|060) /* Private, remove a module */ - -#define _I_GETPEERCRED (STR|061) /* Private, get peer cred */ - -/* Private Layered Driver ioctl's */ -#define _I_PLINK_LH (STR|062) +#define _I_MUXID2FD (STR|056) /* Private: get a fd from a muxid */ +#define _I_INSERT (STR|057) /* Private: insert a module */ +#define _I_REMOVE (STR|060) /* Private: remove a module */ +#define _I_GETPEERCRED (STR|061) /* Private: get peer cred */ +#define _I_PLINK_LH (STR|062) /* Private: Layered Driver ioctl */ +#define _I_CMD (STR|063) /* Private: send ioctl via M_CMD */ /* * User level ioctl format for ioctls that go downstream (I_STR) @@ -316,6 +313,22 @@ struct strioctl32 { #define INFTIM _INFTIM #endif +#if !defined(__XOPEN_OR_POSIX) || defined(__EXTENSIONS__) +/* + * For _I_CMD: similar to strioctl, but with included buffer (to avoid copyin/ + * copyout from another address space). NOTE: the size of this structure must + * be less than libproc.h`MAXARGL for pr_ioctl() to handle it. + */ +#define STRCMDBUFSIZE 2048 +typedef struct strcmd { + int sc_cmd; /* ioctl command */ + int sc_timeout; /* timeout value (in seconds) */ + int sc_len; /* length of data */ + int sc_pad; + char sc_buf[STRCMDBUFSIZE]; /* data buffer */ +} strcmd_t; +#endif + /* * Stream buffer structure for putmsg and getmsg system calls */ diff --git a/usr/src/uts/common/sys/strsubr.h b/usr/src/uts/common/sys/strsubr.h index 6be0519425..df489c3dff 100644 --- a/usr/src/uts/common/sys/strsubr.h +++ b/usr/src/uts/common/sys/strsubr.h @@ -244,6 +244,7 @@ typedef struct stdata { kcondvar_t sd_zcopy_wait; uint_t sd_copyflag; /* copy-related flags */ zoneid_t sd_anchorzone; /* Allow removal from same zone only */ + struct msgb *sd_cmdblk; /* reply from _I_CMD */ } stdata_t; /* @@ -278,7 +279,7 @@ typedef struct stdata { /* 0x00020000 unused */ /* 0x00040000 unused */ #define STRTOSTOP 0x00080000 /* block background writes */ - /* 0x00100000 unused */ +#define STRCMDWAIT 0x00100000 /* someone is doing an _I_CMD */ /* 0x00200000 unused */ #define STRMOUNT 0x00400000 /* stream is mounted */ #define STRNOTATMARK 0x00800000 /* Not at mark (when empty read q) */ |