Home | History | Annotate | Download | only in inet
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 #include <sys/types.h>
     27 #include <inet/common.h>
     28 #include <sys/stream.h>
     29 #include <sys/stropts.h>
     30 #include <sys/strsun.h>
     31 #include <sys/sysmacros.h>
     32 #include <sys/stropts.h>
     33 #include <sys/strsubr.h>
     34 #include <sys/tpicommon.h>
     35 #include <sys/socket_proto.h>
     36 #include <sys/policy.h>
     37 #include <inet/optcom.h>
     38 #include <inet/ipclassifier.h>
     39 
     40 boolean_t
     41 proto_set_rx_hiwat(queue_t *q, conn_t *connp, size_t size)
     42 {
     43 
     44 	if (connp != NULL && IPCL_IS_NONSTR(connp)) {
     45 		struct sock_proto_props sopp;
     46 
     47 		sopp.sopp_flags = SOCKOPT_RCVHIWAT;
     48 		sopp.sopp_rxhiwat = size;
     49 		(*connp->conn_upcalls->su_set_proto_props)
     50 		    (connp->conn_upper_handle, &sopp);
     51 	} else {
     52 		MBLKP	mp;
     53 		struct stroptions *stropt;
     54 
     55 		if (!(mp = allocb(sizeof (*stropt), BPRI_LO)))
     56 			return (B_FALSE);
     57 		mp->b_datap->db_type = M_SETOPTS;
     58 		mp->b_wptr += sizeof (*stropt);
     59 		stropt = (struct stroptions *)mp->b_rptr;
     60 		stropt->so_flags = SO_HIWAT;
     61 		stropt->so_hiwat = size;
     62 		putnext(q, mp);
     63 	}
     64 	return (B_TRUE);
     65 }
     66 
     67 boolean_t
     68 proto_set_rx_lowat(queue_t *q, conn_t *connp, size_t size)
     69 {
     70 
     71 	if (connp != NULL && IPCL_IS_NONSTR(connp)) {
     72 		struct sock_proto_props sopp;
     73 
     74 		sopp.sopp_flags = SOCKOPT_RCVLOWAT;
     75 		sopp.sopp_rxlowat = size;
     76 		(*connp->conn_upcalls->su_set_proto_props)
     77 		    (connp->conn_upper_handle, &sopp);
     78 	} else {
     79 		MBLKP	mp;
     80 		struct stroptions *stropt;
     81 
     82 		if (!(mp = allocb(sizeof (*stropt), BPRI_LO)))
     83 			return (B_FALSE);
     84 		mp->b_datap->db_type = M_SETOPTS;
     85 		mp->b_wptr += sizeof (*stropt);
     86 		stropt = (struct stroptions *)mp->b_rptr;
     87 		stropt->so_flags = SO_LOWAT;
     88 		stropt->so_lowat = size;
     89 		putnext(q, mp);
     90 	}
     91 	return (B_TRUE);
     92 }
     93 
     94 /*
     95  * Set maximum packet size. This is the maximum amount of data the protocol
     96  * wants to be given at any time, Larger data needs to be broken in multiples
     97  * of maximum packet size and given to the protocol one at a time.
     98  */
     99 boolean_t
    100 proto_set_maxpsz(queue_t *q, conn_t *connp, size_t size)
    101 {
    102 	if (connp != NULL && IPCL_IS_NONSTR(connp)) {
    103 		struct sock_proto_props sopp;
    104 
    105 		sopp.sopp_flags = SOCKOPT_MAXPSZ;
    106 		sopp.sopp_maxpsz = size;
    107 		(*connp->conn_upcalls->su_set_proto_props)
    108 		    (connp->conn_upper_handle, &sopp);
    109 		return (B_TRUE);
    110 	} else {
    111 		struct stdata	*stp;
    112 		queue_t		*wq;
    113 		stp = STREAM(q);
    114 
    115 		/*
    116 		 * At this point change of a queue parameter is not allowed
    117 		 * when a multiplexor is sitting on top.
    118 		 */
    119 		if (stp == NULL || stp->sd_flag & STPLEX)
    120 			return (B_FALSE);
    121 
    122 		claimstr(stp->sd_wrq);
    123 		wq = stp->sd_wrq->q_next;
    124 		ASSERT(wq != NULL);
    125 		(void) strqset(wq, QMAXPSZ, 0, size);
    126 		releasestr(stp->sd_wrq);
    127 		return (B_TRUE);
    128 	}
    129 }
    130 
    131 /* ARGSUSED */
    132 boolean_t
    133 proto_set_tx_maxblk(queue_t *q, conn_t *connp, ssize_t size)
    134 {
    135 	if (connp != NULL && IPCL_IS_NONSTR(connp)) {
    136 		struct sock_proto_props sopp;
    137 
    138 		sopp.sopp_flags = SOCKOPT_MAXBLK;
    139 		sopp.sopp_maxblk = size;
    140 		(*connp->conn_upcalls->su_set_proto_props)
    141 		    (connp->conn_upper_handle, &sopp);
    142 	} else {
    143 		MBLKP	mp;
    144 		struct stroptions *stropt;
    145 
    146 		if (!(mp = allocb(sizeof (*stropt), BPRI_LO)))
    147 			return (B_FALSE);
    148 		mp->b_datap->db_type = M_SETOPTS;
    149 		mp->b_wptr += sizeof (*stropt);
    150 		stropt = (struct stroptions *)mp->b_rptr;
    151 		stropt->so_flags = SO_MAXBLK;
    152 		stropt->so_maxblk = size;
    153 		putnext(q, mp);
    154 	}
    155 	return (B_TRUE);
    156 }
    157 
    158 boolean_t
    159 proto_set_tx_copyopt(queue_t *q, conn_t *connp, int copyopt)
    160 {
    161 	if (connp != NULL && IPCL_IS_NONSTR(connp)) {
    162 		struct sock_proto_props sopp;
    163 
    164 		sopp.sopp_flags = SOCKOPT_ZCOPY;
    165 		sopp.sopp_zcopyflag = (ushort_t)copyopt;
    166 		(*connp->conn_upcalls->su_set_proto_props)
    167 		    (connp->conn_upper_handle, &sopp);
    168 	} else {
    169 		MBLKP	mp;
    170 		struct stroptions *stropt;
    171 
    172 		if (!(mp = allocb(sizeof (*stropt), BPRI_LO)))
    173 			return (B_FALSE);
    174 		mp->b_datap->db_type = M_SETOPTS;
    175 		mp->b_wptr += sizeof (*stropt);
    176 		stropt = (struct stroptions *)mp->b_rptr;
    177 		stropt->so_flags = SO_COPYOPT;
    178 		stropt->so_copyopt = (ushort_t)copyopt;
    179 		putnext(q, mp);
    180 	}
    181 	return (B_TRUE);
    182 }
    183 
    184 boolean_t
    185 proto_set_tx_wroff(queue_t *q, conn_t *connp, size_t size)
    186 {
    187 	if (connp != NULL && IPCL_IS_NONSTR(connp)) {
    188 		struct sock_proto_props sopp;
    189 
    190 		sopp.sopp_flags = SOCKOPT_WROFF;
    191 		sopp.sopp_wroff = size;
    192 
    193 		/* XXX workaround for CR6757374 */
    194 		if (connp->conn_upper_handle != NULL)
    195 			(*connp->conn_upcalls->su_set_proto_props)
    196 			    (connp->conn_upper_handle, &sopp);
    197 	} else {
    198 
    199 		MBLKP	mp;
    200 		struct stroptions *stropt;
    201 		if (!(mp = allocb(sizeof (*stropt), BPRI_LO)))
    202 			return (B_FALSE);
    203 		mp->b_datap->db_type = M_SETOPTS;
    204 		mp->b_wptr += sizeof (*stropt);
    205 		stropt = (struct stroptions *)mp->b_rptr;
    206 		stropt->so_flags = SO_WROFF;
    207 		stropt->so_wroff = (ushort_t)size;
    208 		putnext(q, mp);
    209 	}
    210 	return (B_TRUE);
    211 }
    212 
    213 /*
    214  * set OOBINLINE processing on the socket
    215  */
    216 void
    217 proto_set_rx_oob_opt(conn_t *connp, boolean_t onoff)
    218 {
    219 	struct sock_proto_props sopp;
    220 
    221 	ASSERT(IPCL_IS_NONSTR(connp));
    222 
    223 	sopp.sopp_flags = SOCKOPT_OOBINLINE;
    224 	sopp.sopp_oobinline = onoff;
    225 	(*connp->conn_upcalls->su_set_proto_props)
    226 	    (connp->conn_upper_handle, &sopp);
    227 }
    228 
    229 /*
    230  * Translate a TLI(/XTI) error into a system error as best we can.
    231  */
    232 static const int tli_errs[] = {
    233 		0,		/* no error	*/
    234 		EADDRNOTAVAIL,  /* TBADADDR	*/
    235 		ENOPROTOOPT,	/* TBADOPT	*/
    236 		EACCES,		/* TACCES	*/
    237 		EBADF,		/* TBADF	*/
    238 		EADDRNOTAVAIL,	/* TNOADDR	*/
    239 		EPROTO,		/* TOUTSTATE	*/
    240 		ECONNABORTED,	/* TBADSEQ	*/
    241 		0,		/* TSYSERR - will never get	*/
    242 		EPROTO,		/* TLOOK - should never be sent by transport */
    243 		EMSGSIZE,	/* TBADDATA	*/
    244 		EMSGSIZE,	/* TBUFOVFLW	*/
    245 		EPROTO,		/* TFLOW	*/
    246 		EWOULDBLOCK,	/* TNODATA	*/
    247 		EPROTO,		/* TNODIS	*/
    248 		EPROTO,		/* TNOUDERR	*/
    249 		EINVAL,		/* TBADFLAG	*/
    250 		EPROTO,		/* TNOREL	*/
    251 		EOPNOTSUPP,	/* TNOTSUPPORT	*/
    252 		EPROTO,		/* TSTATECHNG	*/
    253 		/* following represent error namespace expansion with XTI */
    254 		EPROTO,		/* TNOSTRUCTYPE - never sent by transport */
    255 		EPROTO,		/* TBADNAME - never sent by transport */
    256 		EPROTO,		/* TBADQLEN - never sent by transport */
    257 		EADDRINUSE,	/* TADDRBUSY	*/
    258 		EBADF,		/* TINDOUT	*/
    259 		EBADF,		/* TPROVMISMATCH */
    260 		EBADF,		/* TRESQLEN	*/
    261 		EBADF,		/* TRESADDR	*/
    262 		EPROTO,		/* TQFULL - never sent by transport */
    263 		EPROTO,		/* TPROTO	*/
    264 };
    265 
    266 int
    267 proto_tlitosyserr(int terr)
    268 {
    269 	ASSERT(terr != TSYSERR);
    270 	if (terr >= (sizeof (tli_errs) / sizeof (tli_errs[0])))
    271 		return (EPROTO);
    272 	else
    273 		return (tli_errs[terr]);
    274 }
    275 
    276 /*
    277  * Verify that address is suitable for connect/sendmsg and is aligned properly
    278  * Since this is a generic function we do not test for port being zero
    279  * as some protocols like icmp do not require a port
    280  */
    281 int
    282 proto_verify_ip_addr(int family, const struct sockaddr *name, socklen_t namelen)
    283 {
    284 
    285 	if (name == NULL || !OK_32PTR((char *)name))
    286 		return (EINVAL);
    287 
    288 	switch (family) {
    289 	case AF_INET:
    290 		if (name->sa_family != AF_INET) {
    291 			return (EAFNOSUPPORT);
    292 		}
    293 
    294 		if (namelen != (socklen_t)sizeof (struct sockaddr_in)) {
    295 			return (EINVAL);
    296 		}
    297 		break;
    298 	case AF_INET6: {
    299 #ifdef DEBUG
    300 		struct sockaddr_in6 *sin6;
    301 #endif /* DEBUG */
    302 
    303 		if (name->sa_family != AF_INET6) {
    304 			return (EAFNOSUPPORT);
    305 		}
    306 		if (namelen != (socklen_t)sizeof (struct sockaddr_in6)) {
    307 			return (EINVAL);
    308 		}
    309 #ifdef DEBUG
    310 		/* Verify that apps don't forget to clear sin6_scope_id etc */
    311 		sin6 = (struct sockaddr_in6 *)name;
    312 		if (sin6->sin6_scope_id != 0 &&
    313 		    !IN6_IS_ADDR_LINKSCOPE(&sin6->sin6_addr)) {
    314 			zcmn_err(getzoneid(), CE_WARN,
    315 			    "connect/send* with uninitialized sin6_scope_id "
    316 			    "(%d) on socket. Pid = %d\n",
    317 			    (int)sin6->sin6_scope_id, (int)curproc->p_pid);
    318 		}
    319 #endif /* DEBUG */
    320 		break;
    321 	}
    322 	default:
    323 		return (EINVAL);
    324 	}
    325 
    326 	return (0);
    327 }
    328 
    329 /*
    330  * Do a lookup of the options in the array.
    331  * Rerurn NULL if there isn't a match.
    332  */
    333 opdes_t *
    334 proto_opt_lookup(t_uscalar_t level, t_uscalar_t name, opdes_t *opt_arr,
    335     uint_t opt_arr_cnt)
    336 {
    337 	opdes_t		*optd;
    338 
    339 	for (optd = opt_arr; optd < &opt_arr[opt_arr_cnt];
    340 	    optd++) {
    341 		if (level == (uint_t)optd->opdes_level &&
    342 		    name == (uint_t)optd->opdes_name)
    343 			return (optd);
    344 	}
    345 	return (NULL);
    346 }
    347 
    348 /*
    349  * Do a lookup of the options in the array and do permission and length checking
    350  * Returns zero if there is no error (note: for non-tpi-providers not being able
    351  * to find the option is not an error). TPI errors are returned as negative
    352  * numbers and errnos as positive numbers.
    353  * If max_len is set we update it based on the max length of the option.
    354  */
    355 int
    356 proto_opt_check(int level, int name, int len, t_uscalar_t *max_len,
    357     opdes_t *opt_arr, uint_t opt_arr_cnt, boolean_t negotiate, boolean_t check,
    358     cred_t *cr)
    359 {
    360 	opdes_t *optd;
    361 
    362 	/* Find the option in the opt_arr. */
    363 	optd = proto_opt_lookup(level, name, opt_arr, opt_arr_cnt);
    364 	if (optd == NULL)
    365 		return (-TBADOPT);
    366 
    367 	/* Additional checks dependent on operation. */
    368 	if (negotiate) {
    369 		/* Cannot be true at the same time */
    370 		ASSERT(check == B_FALSE);
    371 
    372 		if (!OA_WRITE_OR_EXECUTE(optd, cr)) {
    373 			/* can't negotiate option */
    374 			if (!(OA_MATCHED_PRIV(optd, cr)) &&
    375 			    OA_WX_ANYPRIV(optd)) {
    376 				/*
    377 				 * not privileged but privilege
    378 				 * will help negotiate option.
    379 				 */
    380 				return (-TACCES);
    381 			} else {
    382 				return (-TBADOPT);
    383 			}
    384 		}
    385 		/*
    386 		 * Verify size for options
    387 		 * Note: For retaining compatibility with historical
    388 		 * behavior, variable lengths options will have their
    389 		 * length verified in the setfn() processing.
    390 		 * In order to be compatible with SunOS 4.X we return
    391 		 * EINVAL errors for bad lengths.
    392 		 */
    393 		if (!(optd->opdes_props & OP_VARLEN)) {
    394 			/* fixed length - size must match */
    395 			if (len != optd->opdes_size) {
    396 				return (EINVAL);
    397 			}
    398 		}
    399 	} else {
    400 		if (check) {
    401 			if (!OA_RWX_ANYPRIV(optd))
    402 				/* any of "rwx" permission but not none */
    403 				return (-TBADOPT);
    404 		}
    405 		/*
    406 		 * XXX Since T_CURRENT was not there in TLI and the
    407 		 * official TLI inspired TPI standard, getsockopt()
    408 		 * API uses T_CHECK (for T_CURRENT semantics)
    409 		 * Thus T_CHECK includes the T_CURRENT semantics due to that
    410 		 * historical use.
    411 		 */
    412 		if (!OA_READ_PERMISSION(optd, cr)) {
    413 			/* can't read option value */
    414 			if (!(OA_MATCHED_PRIV(optd, cr)) &&
    415 			    OA_R_ANYPRIV(optd)) {
    416 				/*
    417 				 * not privileged but privilege
    418 				 * will help in reading option value.
    419 				 */
    420 				return (-TACCES);
    421 			} else {
    422 				return (-TBADOPT);
    423 			}
    424 		}
    425 	}
    426 	if (max_len != NULL)
    427 		*max_len = optd->opdes_size;
    428 
    429 	/* We liked it.  Keep going. */
    430 	return (0);
    431 }
    432