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 2008 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 -ve. 352 */ 353 int 354 proto_opt_check(int level, int name, int len, t_uscalar_t *max_len, 355 opdes_t *opt_arr, uint_t opt_arr_cnt, boolean_t topmost_tpiprovider, 356 boolean_t negotiate, boolean_t check, cred_t *cr) 357 { 358 opdes_t *optd; 359 360 /* Find the option in the opt_arr. */ 361 if ((optd = proto_opt_lookup(level, name, opt_arr, opt_arr_cnt)) == 362 NULL) { 363 /* 364 * Not found, that is a bad thing if 365 * the caller is a tpi provider 366 */ 367 if (topmost_tpiprovider) 368 return (-TBADOPT); 369 else 370 return (0); /* skip unmodified */ 371 } 372 373 /* Additional checks dependent on operation. */ 374 if (negotiate) { 375 /* Cannot be true at the same time */ 376 ASSERT(check == B_FALSE); 377 378 if (!OA_WRITE_OR_EXECUTE(optd, cr)) { 379 /* can't negotiate option */ 380 if (!(OA_MATCHED_PRIV(optd, cr)) && 381 OA_WX_ANYPRIV(optd)) { 382 /* 383 * not privileged but privilege 384 * will help negotiate option. 385 */ 386 return (-TACCES); 387 } else { 388 return (-TBADOPT); 389 } 390 } 391 /* 392 * Verify size for options 393 * Note: For retaining compatibility with historical 394 * behavior, variable lengths options will have their 395 * length verified in the setfn() processing. 396 * In order to be compatible with SunOS 4.X we return 397 * EINVAL errors for bad lengths. 398 */ 399 if (!(optd->opdes_props & OP_VARLEN)) { 400 /* fixed length - size must match */ 401 if (len != optd->opdes_size) { 402 return (EINVAL); 403 } 404 } 405 } else { 406 if (check) { 407 if (!OA_RWX_ANYPRIV(optd)) 408 /* any of "rwx" permission but not none */ 409 return (-TBADOPT); 410 } 411 /* 412 * XXX Change the comments. 413 * 414 * XXX Since T_CURRENT was not there in TLI and the 415 * official TLI inspired TPI standard, getsockopt() 416 * API uses T_CHECK (for T_CURRENT semantics) 417 * The following fallthru makes sense because of its 418 * historical use as semantic equivalent to T_CURRENT. 419 */ 420 /* FALLTHRU */ 421 if (!OA_READ_PERMISSION(optd, cr)) { 422 /* can't read option value */ 423 if (!(OA_MATCHED_PRIV(optd, cr)) && 424 OA_R_ANYPRIV(optd)) { 425 /* 426 * not privileged but privilege 427 * will help in reading option value. 428 */ 429 return (-TACCES); 430 } else { 431 return (-TBADOPT); 432 } 433 } 434 } 435 if (max_len != NULL) 436 *max_len = optd->opdes_size; 437 438 /* We liked it. Keep going. */ 439 return (0); 440 } 441