17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 545916cd2Sjpk * Common Development and Distribution License (the "License"). 645916cd2Sjpk * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 2177c67f2fSkcpoon 227c478bd9Sstevel@tonic-gate /* 23481845d8SGeorge Shepherd * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. 247c478bd9Sstevel@tonic-gate */ 257c478bd9Sstevel@tonic-gate 267c478bd9Sstevel@tonic-gate #include <sys/types.h> 277c478bd9Sstevel@tonic-gate #include <sys/systm.h> 287c478bd9Sstevel@tonic-gate #include <sys/stream.h> 297c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h> 307c478bd9Sstevel@tonic-gate #include <sys/kmem.h> 317c478bd9Sstevel@tonic-gate #define _SUN_TPI_VERSION 2 327c478bd9Sstevel@tonic-gate #include <sys/tihdr.h> 337c478bd9Sstevel@tonic-gate #include <sys/stropts.h> 347c478bd9Sstevel@tonic-gate #include <sys/strsubr.h> 357c478bd9Sstevel@tonic-gate #include <sys/socket.h> 3645916cd2Sjpk #include <sys/tsol/tndb.h> 377c478bd9Sstevel@tonic-gate 387c478bd9Sstevel@tonic-gate #include <netinet/in.h> 397c478bd9Sstevel@tonic-gate #include <netinet/ip6.h> 407c478bd9Sstevel@tonic-gate 417c478bd9Sstevel@tonic-gate #include <inet/common.h> 427c478bd9Sstevel@tonic-gate #include <inet/ip.h> 437c478bd9Sstevel@tonic-gate #include <inet/ip6.h> 447c478bd9Sstevel@tonic-gate #include <inet/ipclassifier.h> 457c478bd9Sstevel@tonic-gate #include <inet/ipsec_impl.h> 467c478bd9Sstevel@tonic-gate 477c478bd9Sstevel@tonic-gate #include "sctp_impl.h" 487c478bd9Sstevel@tonic-gate #include "sctp_addr.h" 497c478bd9Sstevel@tonic-gate 507c478bd9Sstevel@tonic-gate /* 517c478bd9Sstevel@tonic-gate * Common accept code. Called by sctp_conn_request. 527c478bd9Sstevel@tonic-gate * cr_pkt is the INIT / INIT ACK packet. 537c478bd9Sstevel@tonic-gate */ 547c478bd9Sstevel@tonic-gate static int 557c478bd9Sstevel@tonic-gate sctp_accept_comm(sctp_t *listener, sctp_t *acceptor, mblk_t *cr_pkt, 567c478bd9Sstevel@tonic-gate uint_t ip_hdr_len, sctp_init_chunk_t *iack) 577c478bd9Sstevel@tonic-gate { 587c478bd9Sstevel@tonic-gate 597c478bd9Sstevel@tonic-gate sctp_hdr_t *sctph; 607c478bd9Sstevel@tonic-gate sctp_chunk_hdr_t *ich; 617c478bd9Sstevel@tonic-gate sctp_init_chunk_t *init; 627c478bd9Sstevel@tonic-gate int err; 637c478bd9Sstevel@tonic-gate uint_t sctp_options; 64d7ab25acSkp158701 conn_t *aconnp; 6545916cd2Sjpk conn_t *lconnp; 66f4b3ec61Sdh155122 sctp_stack_t *sctps = listener->sctp_sctps; 677c478bd9Sstevel@tonic-gate 687c478bd9Sstevel@tonic-gate sctph = (sctp_hdr_t *)(cr_pkt->b_rptr + ip_hdr_len); 697c478bd9Sstevel@tonic-gate ASSERT(OK_32PTR(sctph)); 707c478bd9Sstevel@tonic-gate 71bd670b35SErik Nordmark aconnp = acceptor->sctp_connp; 72bd670b35SErik Nordmark lconnp = listener->sctp_connp; 73bd670b35SErik Nordmark aconnp->conn_lport = lconnp->conn_lport; 74bd670b35SErik Nordmark aconnp->conn_fport = sctph->sh_sport; 757c478bd9Sstevel@tonic-gate 767c478bd9Sstevel@tonic-gate ich = (sctp_chunk_hdr_t *)(iack + 1); 777c478bd9Sstevel@tonic-gate init = (sctp_init_chunk_t *)(ich + 1); 787c478bd9Sstevel@tonic-gate 797c478bd9Sstevel@tonic-gate /* acceptor isn't in any fanouts yet, so don't need to hold locks */ 807c478bd9Sstevel@tonic-gate ASSERT(acceptor->sctp_faddrs == NULL); 817c478bd9Sstevel@tonic-gate err = sctp_get_addrparams(acceptor, listener, cr_pkt, ich, 827c478bd9Sstevel@tonic-gate &sctp_options); 837c478bd9Sstevel@tonic-gate if (err != 0) 847c478bd9Sstevel@tonic-gate return (err); 857c478bd9Sstevel@tonic-gate 8677c67f2fSkcpoon if ((err = sctp_set_hdraddrs(acceptor)) != 0) 8745916cd2Sjpk return (err); 8845916cd2Sjpk 89bd670b35SErik Nordmark if ((err = sctp_build_hdrs(acceptor, KM_NOSLEEP)) != 0) 90bd670b35SErik Nordmark return (err); 91bd670b35SErik Nordmark 927c478bd9Sstevel@tonic-gate if ((sctp_options & SCTP_PRSCTP_OPTION) && 93f4b3ec61Sdh155122 listener->sctp_prsctp_aware && sctps->sctps_prsctp_enabled) { 947c478bd9Sstevel@tonic-gate acceptor->sctp_prsctp_aware = B_TRUE; 957c478bd9Sstevel@tonic-gate } else { 967c478bd9Sstevel@tonic-gate acceptor->sctp_prsctp_aware = B_FALSE; 977c478bd9Sstevel@tonic-gate } 987c478bd9Sstevel@tonic-gate 997c478bd9Sstevel@tonic-gate /* Get initial TSNs */ 1007c478bd9Sstevel@tonic-gate acceptor->sctp_ltsn = ntohl(iack->sic_inittsn); 1017c478bd9Sstevel@tonic-gate acceptor->sctp_recovery_tsn = acceptor->sctp_lastack_rxd = 1027c478bd9Sstevel@tonic-gate acceptor->sctp_ltsn - 1; 1037c478bd9Sstevel@tonic-gate acceptor->sctp_adv_pap = acceptor->sctp_lastack_rxd; 1047c478bd9Sstevel@tonic-gate /* Serial numbers are initialized to the same value as the TSNs */ 1057c478bd9Sstevel@tonic-gate acceptor->sctp_lcsn = acceptor->sctp_ltsn; 1067c478bd9Sstevel@tonic-gate 1077c478bd9Sstevel@tonic-gate if (!sctp_initialize_params(acceptor, init, iack)) 1087c478bd9Sstevel@tonic-gate return (ENOMEM); 1097c478bd9Sstevel@tonic-gate 1107c478bd9Sstevel@tonic-gate /* 1117c478bd9Sstevel@tonic-gate * Copy sctp_secret from the listener in case we need to validate 1127c478bd9Sstevel@tonic-gate * a possibly delayed cookie. 1137c478bd9Sstevel@tonic-gate */ 1147c478bd9Sstevel@tonic-gate bcopy(listener->sctp_secret, acceptor->sctp_secret, SCTP_SECRET_LEN); 1157c478bd9Sstevel@tonic-gate bcopy(listener->sctp_old_secret, acceptor->sctp_old_secret, 1167c478bd9Sstevel@tonic-gate SCTP_SECRET_LEN); 117d3d50737SRafael Vanoni acceptor->sctp_last_secret_update = ddi_get_lbolt64(); 1187c478bd9Sstevel@tonic-gate 1197c478bd9Sstevel@tonic-gate /* 1207c478bd9Sstevel@tonic-gate * After acceptor is inserted in the hash list, it can be found. 1217c478bd9Sstevel@tonic-gate * So we need to lock it here. 1227c478bd9Sstevel@tonic-gate */ 1237c478bd9Sstevel@tonic-gate RUN_SCTP(acceptor); 1247c478bd9Sstevel@tonic-gate 125f4b3ec61Sdh155122 sctp_conn_hash_insert(&sctps->sctps_conn_fanout[ 126bd670b35SErik Nordmark SCTP_CONN_HASH(sctps, aconnp->conn_ports)], acceptor, 0); 127f4b3ec61Sdh155122 sctp_bind_hash_insert(&sctps->sctps_bind_fanout[ 128bd670b35SErik Nordmark SCTP_BIND_HASH(ntohs(aconnp->conn_lport))], acceptor, 0); 1297c478bd9Sstevel@tonic-gate 1305dd46ab5SKacheong Poon SCTP_ASSOC_EST(sctps, acceptor); 1317c478bd9Sstevel@tonic-gate return (0); 1327c478bd9Sstevel@tonic-gate } 1337c478bd9Sstevel@tonic-gate 1347c478bd9Sstevel@tonic-gate /* Process the COOKIE packet, mp, directed at the listener 'sctp' */ 1357c478bd9Sstevel@tonic-gate sctp_t * 1367c478bd9Sstevel@tonic-gate sctp_conn_request(sctp_t *sctp, mblk_t *mp, uint_t ifindex, uint_t ip_hdr_len, 137bd670b35SErik Nordmark sctp_init_chunk_t *iack, ip_recv_attr_t *ira) 1387c478bd9Sstevel@tonic-gate { 1397c478bd9Sstevel@tonic-gate sctp_t *eager; 1407c478bd9Sstevel@tonic-gate ip6_t *ip6h; 1417c478bd9Sstevel@tonic-gate int err; 1427c478bd9Sstevel@tonic-gate conn_t *connp, *econnp; 143f4b3ec61Sdh155122 sctp_stack_t *sctps; 144de8c4a14SErik Nordmark cred_t *cr; 145de8c4a14SErik Nordmark pid_t cpid; 146bd670b35SErik Nordmark in6_addr_t faddr, laddr; 147bd670b35SErik Nordmark ip_xmit_attr_t *ixa; 1485dd46ab5SKacheong Poon sctp_listen_cnt_t *slc = sctp->sctp_listen_cnt; 1495dd46ab5SKacheong Poon boolean_t slc_set = B_FALSE; 1507c478bd9Sstevel@tonic-gate 1517c478bd9Sstevel@tonic-gate /* 1527c478bd9Sstevel@tonic-gate * No need to check for duplicate as this is the listener 1537c478bd9Sstevel@tonic-gate * and we are holding the lock. This means that no new 1547c478bd9Sstevel@tonic-gate * connection can be created out of it. And since the 1557c478bd9Sstevel@tonic-gate * fanout already done cannot find a match, it means that 1567c478bd9Sstevel@tonic-gate * there is no duplicate. 1577c478bd9Sstevel@tonic-gate */ 1587c478bd9Sstevel@tonic-gate ASSERT(OK_32PTR(mp->b_rptr)); 1597c478bd9Sstevel@tonic-gate 1607c478bd9Sstevel@tonic-gate connp = sctp->sctp_connp; 161f4b3ec61Sdh155122 sctps = sctp->sctp_sctps; 1625dd46ab5SKacheong Poon 1635dd46ab5SKacheong Poon /* 1645dd46ab5SKacheong Poon * Enforce the limit set on the number of connections per listener. 1655dd46ab5SKacheong Poon * Note that tlc_cnt starts with 1. So need to add 1 to tlc_max 1665dd46ab5SKacheong Poon * for comparison. 1675dd46ab5SKacheong Poon */ 1685dd46ab5SKacheong Poon if (slc != NULL) { 1695dd46ab5SKacheong Poon int64_t now; 1705dd46ab5SKacheong Poon 171*1a5e258fSJosef 'Jeff' Sipek if (atomic_inc_32_nv(&slc->slc_cnt) > slc->slc_max + 1) { 1725dd46ab5SKacheong Poon now = ddi_get_lbolt64(); 173*1a5e258fSJosef 'Jeff' Sipek atomic_dec_32(&slc->slc_cnt); 1745dd46ab5SKacheong Poon SCTP_KSTAT(sctps, sctp_listen_cnt_drop); 1755dd46ab5SKacheong Poon slc->slc_drop++; 1765dd46ab5SKacheong Poon if (now - slc->slc_report_time > 1775dd46ab5SKacheong Poon MSEC_TO_TICK(SCTP_SLC_REPORT_INTERVAL)) { 1785dd46ab5SKacheong Poon zcmn_err(connp->conn_zoneid, CE_WARN, 1795dd46ab5SKacheong Poon "SCTP listener (port %d) association max " 1805dd46ab5SKacheong Poon "(%u) reached: %u attempts dropped total\n", 1815dd46ab5SKacheong Poon ntohs(connp->conn_lport), 1825dd46ab5SKacheong Poon slc->slc_max, slc->slc_drop); 1835dd46ab5SKacheong Poon slc->slc_report_time = now; 1845dd46ab5SKacheong Poon } 1855dd46ab5SKacheong Poon return (NULL); 1865dd46ab5SKacheong Poon } 1875dd46ab5SKacheong Poon slc_set = B_TRUE; 1885dd46ab5SKacheong Poon } 1895dd46ab5SKacheong Poon 1905dd46ab5SKacheong Poon if ((eager = sctp_create_eager(sctp)) == NULL) { 1915dd46ab5SKacheong Poon if (slc_set) 192*1a5e258fSJosef 'Jeff' Sipek atomic_dec_32(&slc->slc_cnt); 1935dd46ab5SKacheong Poon return (NULL); 1945dd46ab5SKacheong Poon } 1957c478bd9Sstevel@tonic-gate econnp = eager->sctp_connp; 1967c478bd9Sstevel@tonic-gate 1977c478bd9Sstevel@tonic-gate if (connp->conn_policy != NULL) { 198bd670b35SErik Nordmark /* Inherit the policy from the listener; use actions from ira */ 199bd670b35SErik Nordmark if (!ip_ipsec_policy_inherit(econnp, connp, ira)) { 2007c478bd9Sstevel@tonic-gate sctp_close_eager(eager); 2015dd46ab5SKacheong Poon SCTPS_BUMP_MIB(sctps, sctpListenDrop); 2027c478bd9Sstevel@tonic-gate return (NULL); 2037c478bd9Sstevel@tonic-gate } 2047c478bd9Sstevel@tonic-gate } 2057c478bd9Sstevel@tonic-gate 206bd670b35SErik Nordmark ip6h = (ip6_t *)mp->b_rptr; 207bd670b35SErik Nordmark if (ira->ira_flags & IXAF_IS_IPV4) { 208bd670b35SErik Nordmark ipha_t *ipha; 209bd670b35SErik Nordmark 210bd670b35SErik Nordmark ipha = (ipha_t *)ip6h; 211bd670b35SErik Nordmark IN6_IPADDR_TO_V4MAPPED(ipha->ipha_dst, &laddr); 212bd670b35SErik Nordmark IN6_IPADDR_TO_V4MAPPED(ipha->ipha_src, &faddr); 213bd670b35SErik Nordmark } else { 214bd670b35SErik Nordmark laddr = ip6h->ip6_dst; 215bd670b35SErik Nordmark faddr = ip6h->ip6_src; 216bd670b35SErik Nordmark } 217bd670b35SErik Nordmark 218bd670b35SErik Nordmark if (ira->ira_flags & IRAF_IPSEC_SECURE) { 2197c478bd9Sstevel@tonic-gate /* 2207c478bd9Sstevel@tonic-gate * XXX need to fix the cached policy issue here. 221bd670b35SErik Nordmark * We temporarily set the conn_laddr/conn_faddr here so 2227c478bd9Sstevel@tonic-gate * that IPsec can use it for the latched policy 2237c478bd9Sstevel@tonic-gate * selector. This is obvioursly wrong as SCTP can 2247c478bd9Sstevel@tonic-gate * use different addresses... 2257c478bd9Sstevel@tonic-gate */ 226bd670b35SErik Nordmark econnp->conn_laddr_v6 = laddr; 227bd670b35SErik Nordmark econnp->conn_faddr_v6 = faddr; 228bd670b35SErik Nordmark econnp->conn_saddr_v6 = laddr; 2297c478bd9Sstevel@tonic-gate } 230bd670b35SErik Nordmark if (ipsec_conn_cache_policy(econnp, 231bd670b35SErik Nordmark (ira->ira_flags & IRAF_IS_IPV4) != 0) != 0) { 2327c478bd9Sstevel@tonic-gate sctp_close_eager(eager); 2335dd46ab5SKacheong Poon SCTPS_BUMP_MIB(sctps, sctpListenDrop); 2347c478bd9Sstevel@tonic-gate return (NULL); 2357c478bd9Sstevel@tonic-gate } 2367c478bd9Sstevel@tonic-gate 237de8c4a14SErik Nordmark /* Save for getpeerucred */ 238bd670b35SErik Nordmark cr = ira->ira_cred; 239bd670b35SErik Nordmark cpid = ira->ira_cpid; 240bd670b35SErik Nordmark 241bd670b35SErik Nordmark if (is_system_labeled()) { 242bd670b35SErik Nordmark ip_xmit_attr_t *ixa = econnp->conn_ixa; 243bd670b35SErik Nordmark 244bd670b35SErik Nordmark ASSERT(ira->ira_tsl != NULL); 245bd670b35SErik Nordmark 246bd670b35SErik Nordmark /* Discard any old label */ 247bd670b35SErik Nordmark if (ixa->ixa_free_flags & IXA_FREE_TSL) { 248bd670b35SErik Nordmark ASSERT(ixa->ixa_tsl != NULL); 249bd670b35SErik Nordmark label_rele(ixa->ixa_tsl); 250bd670b35SErik Nordmark ixa->ixa_free_flags &= ~IXA_FREE_TSL; 251bd670b35SErik Nordmark ixa->ixa_tsl = NULL; 252bd670b35SErik Nordmark } 253bd670b35SErik Nordmark 254bd670b35SErik Nordmark if ((connp->conn_mlp_type != mlptSingle || 255bd670b35SErik Nordmark connp->conn_mac_mode != CONN_MAC_DEFAULT) && 256bd670b35SErik Nordmark ira->ira_tsl != NULL) { 257bd670b35SErik Nordmark /* 258bd670b35SErik Nordmark * If this is an MLP connection or a MAC-Exempt 259bd670b35SErik Nordmark * connection with an unlabeled node, packets are to be 260bd670b35SErik Nordmark * exchanged using the security label of the received 261bd670b35SErik Nordmark * Cookie packet instead of the server application's 262bd670b35SErik Nordmark * label. 263bd670b35SErik Nordmark * tsol_check_dest called from ip_set_destination 264bd670b35SErik Nordmark * might later update TSF_UNLABELED by replacing 265bd670b35SErik Nordmark * ixa_tsl with a new label. 266bd670b35SErik Nordmark */ 267bd670b35SErik Nordmark label_hold(ira->ira_tsl); 268bd670b35SErik Nordmark ip_xmit_attr_replace_tsl(ixa, ira->ira_tsl); 269bd670b35SErik Nordmark } else { 270bd670b35SErik Nordmark ixa->ixa_tsl = crgetlabel(econnp->conn_cred); 271bd670b35SErik Nordmark } 272bd670b35SErik Nordmark } 273de8c4a14SErik Nordmark 2747c478bd9Sstevel@tonic-gate err = sctp_accept_comm(sctp, eager, mp, ip_hdr_len, iack); 275bd670b35SErik Nordmark if (err != 0) { 2767c478bd9Sstevel@tonic-gate sctp_close_eager(eager); 2775dd46ab5SKacheong Poon SCTPS_BUMP_MIB(sctps, sctpListenDrop); 2787c478bd9Sstevel@tonic-gate return (NULL); 2797c478bd9Sstevel@tonic-gate } 2807c478bd9Sstevel@tonic-gate 2816be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India ASSERT(eager->sctp_current->sf_ixa != NULL); 282bd670b35SErik Nordmark 2836be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India ixa = eager->sctp_current->sf_ixa; 284bd670b35SErik Nordmark if (!(ira->ira_flags & IXAF_IS_IPV4)) { 285bd670b35SErik Nordmark ASSERT(!(ixa->ixa_flags & IXAF_IS_IPV4)); 286bd670b35SErik Nordmark 287bd670b35SErik Nordmark if (IN6_IS_ADDR_LINKLOCAL(&ip6h->ip6_src) || 288bd670b35SErik Nordmark IN6_IS_ADDR_LINKLOCAL(&ip6h->ip6_dst)) { 289bd670b35SErik Nordmark eager->sctp_linklocal = 1; 290bd670b35SErik Nordmark 291bd670b35SErik Nordmark ixa->ixa_flags |= IXAF_SCOPEID_SET; 292bd670b35SErik Nordmark ixa->ixa_scopeid = ifindex; 293bd670b35SErik Nordmark econnp->conn_incoming_ifindex = ifindex; 294bd670b35SErik Nordmark } 295bd670b35SErik Nordmark } 296bd670b35SErik Nordmark 2971d8c4025Svi117747 /* 2981d8c4025Svi117747 * On a clustered note send this notification to the clustering 2991d8c4025Svi117747 * subsystem. 3001d8c4025Svi117747 */ 3011d8c4025Svi117747 if (cl_sctp_connect != NULL) { 3021d8c4025Svi117747 uchar_t *slist; 3031d8c4025Svi117747 uchar_t *flist; 3041d8c4025Svi117747 size_t fsize; 3051d8c4025Svi117747 size_t ssize; 3061d8c4025Svi117747 3071d8c4025Svi117747 fsize = sizeof (in6_addr_t) * eager->sctp_nfaddrs; 3081d8c4025Svi117747 ssize = sizeof (in6_addr_t) * eager->sctp_nsaddrs; 3091d8c4025Svi117747 slist = kmem_alloc(ssize, KM_NOSLEEP); 3101d8c4025Svi117747 flist = kmem_alloc(fsize, KM_NOSLEEP); 3111d8c4025Svi117747 if (slist == NULL || flist == NULL) { 3121d8c4025Svi117747 if (slist != NULL) 3131d8c4025Svi117747 kmem_free(slist, ssize); 3141d8c4025Svi117747 if (flist != NULL) 3151d8c4025Svi117747 kmem_free(flist, fsize); 3161d8c4025Svi117747 sctp_close_eager(eager); 3175dd46ab5SKacheong Poon SCTPS_BUMP_MIB(sctps, sctpListenDrop); 318f4b3ec61Sdh155122 SCTP_KSTAT(sctps, sctp_cl_connect); 3191d8c4025Svi117747 return (NULL); 3201d8c4025Svi117747 } 3211d8c4025Svi117747 /* The clustering module frees these list */ 3221d8c4025Svi117747 sctp_get_saddr_list(eager, slist, ssize); 3231d8c4025Svi117747 sctp_get_faddr_list(eager, flist, fsize); 324bd670b35SErik Nordmark (*cl_sctp_connect)(econnp->conn_family, slist, 325bd670b35SErik Nordmark eager->sctp_nsaddrs, econnp->conn_lport, flist, 326bd670b35SErik Nordmark eager->sctp_nfaddrs, econnp->conn_fport, B_FALSE, 3271d8c4025Svi117747 (cl_sctp_handle_t)eager); 3281d8c4025Svi117747 } 3291d8c4025Svi117747 3307c478bd9Sstevel@tonic-gate /* Connection established, so send up the conn_ind */ 3317c478bd9Sstevel@tonic-gate if ((eager->sctp_ulpd = sctp->sctp_ulp_newconn(sctp->sctp_ulpd, 332de8c4a14SErik Nordmark (sock_lower_handle_t)eager, NULL, cr, cpid, 3330f1702c5SYu Xiangning &eager->sctp_upcalls)) == NULL) { 3347c478bd9Sstevel@tonic-gate sctp_close_eager(eager); 3355dd46ab5SKacheong Poon SCTPS_BUMP_MIB(sctps, sctpListenDrop); 3367c478bd9Sstevel@tonic-gate return (NULL); 3377c478bd9Sstevel@tonic-gate } 3387c478bd9Sstevel@tonic-gate ASSERT(SCTP_IS_DETACHED(eager)); 3397c478bd9Sstevel@tonic-gate eager->sctp_detached = B_FALSE; 3407c478bd9Sstevel@tonic-gate return (eager); 3417c478bd9Sstevel@tonic-gate } 3427c478bd9Sstevel@tonic-gate 3437c478bd9Sstevel@tonic-gate /* 3447c478bd9Sstevel@tonic-gate * Connect to a peer - this function inserts the sctp in the 3457c478bd9Sstevel@tonic-gate * bind and conn fanouts, sends the INIT, and replies to the client 3467c478bd9Sstevel@tonic-gate * with an OK ack. 3477c478bd9Sstevel@tonic-gate */ 3487c478bd9Sstevel@tonic-gate int 349bd670b35SErik Nordmark sctp_connect(sctp_t *sctp, const struct sockaddr *dst, uint32_t addrlen, 350bd670b35SErik Nordmark cred_t *cr, pid_t pid) 3517c478bd9Sstevel@tonic-gate { 3527c478bd9Sstevel@tonic-gate sin_t *sin; 3537c478bd9Sstevel@tonic-gate sin6_t *sin6; 3547c478bd9Sstevel@tonic-gate in6_addr_t dstaddr; 3557c478bd9Sstevel@tonic-gate in_port_t dstport; 3567c478bd9Sstevel@tonic-gate mblk_t *initmp; 3577c478bd9Sstevel@tonic-gate sctp_tf_t *tbf; 3587c478bd9Sstevel@tonic-gate sctp_t *lsctp; 3597c478bd9Sstevel@tonic-gate char buf[INET6_ADDRSTRLEN]; 3607c478bd9Sstevel@tonic-gate int sleep = sctp->sctp_cansleep ? KM_SLEEP : KM_NOSLEEP; 36145916cd2Sjpk int err; 3627c478bd9Sstevel@tonic-gate sctp_faddr_t *cur_fp; 363f4b3ec61Sdh155122 sctp_stack_t *sctps = sctp->sctp_sctps; 364bd670b35SErik Nordmark conn_t *connp = sctp->sctp_connp; 365bd670b35SErik Nordmark uint_t scope_id = 0; 366bd670b35SErik Nordmark ip_xmit_attr_t *ixa; 3677c478bd9Sstevel@tonic-gate 3687c478bd9Sstevel@tonic-gate /* 3697c478bd9Sstevel@tonic-gate * Determine packet type based on type of address passed in 3707c478bd9Sstevel@tonic-gate * the request should contain an IPv4 or IPv6 address. 3717c478bd9Sstevel@tonic-gate * Make sure that address family matches the type of 372bd670b35SErik Nordmark * family of the address passed down. 3737c478bd9Sstevel@tonic-gate */ 3747c478bd9Sstevel@tonic-gate if (addrlen < sizeof (sin_t)) { 3757c478bd9Sstevel@tonic-gate return (EINVAL); 3767c478bd9Sstevel@tonic-gate } 3777c478bd9Sstevel@tonic-gate switch (dst->sa_family) { 3787c478bd9Sstevel@tonic-gate case AF_INET: 3797c478bd9Sstevel@tonic-gate sin = (sin_t *)dst; 3807c478bd9Sstevel@tonic-gate 3817c478bd9Sstevel@tonic-gate /* Check for attempt to connect to non-unicast */ 382c4f4b3c8Skcpoon if (CLASSD(sin->sin_addr.s_addr) || 3837c478bd9Sstevel@tonic-gate (sin->sin_addr.s_addr == INADDR_BROADCAST)) { 3847c478bd9Sstevel@tonic-gate ip0dbg(("sctp_connect: non-unicast\n")); 3857c478bd9Sstevel@tonic-gate return (EINVAL); 3867c478bd9Sstevel@tonic-gate } 387bd670b35SErik Nordmark if (connp->conn_ipv6_v6only) 3887c478bd9Sstevel@tonic-gate return (EAFNOSUPPORT); 3897c478bd9Sstevel@tonic-gate 3907c478bd9Sstevel@tonic-gate /* convert to v6 mapped */ 3917c478bd9Sstevel@tonic-gate /* Check for attempt to connect to INADDR_ANY */ 3927c478bd9Sstevel@tonic-gate if (sin->sin_addr.s_addr == INADDR_ANY) { 3937c478bd9Sstevel@tonic-gate struct in_addr v4_addr; 3947c478bd9Sstevel@tonic-gate /* 3957c478bd9Sstevel@tonic-gate * SunOS 4.x and 4.3 BSD allow an application 3967c478bd9Sstevel@tonic-gate * to connect a TCP socket to INADDR_ANY. 3977c478bd9Sstevel@tonic-gate * When they do this, the kernel picks the 3987c478bd9Sstevel@tonic-gate * address of one interface and uses it 3997c478bd9Sstevel@tonic-gate * instead. The kernel usually ends up 4007c478bd9Sstevel@tonic-gate * picking the address of the loopback 4017c478bd9Sstevel@tonic-gate * interface. This is an undocumented feature. 4027c478bd9Sstevel@tonic-gate * However, we provide the same thing here 4037c478bd9Sstevel@tonic-gate * in case any TCP apps that use this feature 4047c478bd9Sstevel@tonic-gate * are being ported to SCTP... 4057c478bd9Sstevel@tonic-gate */ 4067c478bd9Sstevel@tonic-gate v4_addr.s_addr = htonl(INADDR_LOOPBACK); 4077c478bd9Sstevel@tonic-gate IN6_INADDR_TO_V4MAPPED(&v4_addr, &dstaddr); 4087c478bd9Sstevel@tonic-gate } else { 4097c478bd9Sstevel@tonic-gate IN6_INADDR_TO_V4MAPPED(&sin->sin_addr, &dstaddr); 4107c478bd9Sstevel@tonic-gate } 4117c478bd9Sstevel@tonic-gate dstport = sin->sin_port; 4127c478bd9Sstevel@tonic-gate break; 4137c478bd9Sstevel@tonic-gate case AF_INET6: 4147c478bd9Sstevel@tonic-gate sin6 = (sin6_t *)dst; 4157c478bd9Sstevel@tonic-gate /* Check for attempt to connect to non-unicast. */ 4167c478bd9Sstevel@tonic-gate if ((addrlen < sizeof (sin6_t)) || 4177c478bd9Sstevel@tonic-gate IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) { 4187c478bd9Sstevel@tonic-gate ip0dbg(("sctp_connect: non-unicast\n")); 4197c478bd9Sstevel@tonic-gate return (EINVAL); 4207c478bd9Sstevel@tonic-gate } 421bd670b35SErik Nordmark if (connp->conn_ipv6_v6only && 4227c478bd9Sstevel@tonic-gate IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { 4237c478bd9Sstevel@tonic-gate return (EAFNOSUPPORT); 4247c478bd9Sstevel@tonic-gate } 4257c478bd9Sstevel@tonic-gate /* check for attempt to connect to unspec */ 4267c478bd9Sstevel@tonic-gate if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { 4277c478bd9Sstevel@tonic-gate dstaddr = ipv6_loopback; 4287c478bd9Sstevel@tonic-gate } else { 4297c478bd9Sstevel@tonic-gate dstaddr = sin6->sin6_addr; 430bd670b35SErik Nordmark if (IN6_IS_ADDR_LINKLOCAL(&dstaddr)) { 431f551bb10Svi117747 sctp->sctp_linklocal = 1; 432bd670b35SErik Nordmark scope_id = sin6->sin6_scope_id; 433bd670b35SErik Nordmark } 4347c478bd9Sstevel@tonic-gate } 4357c478bd9Sstevel@tonic-gate dstport = sin6->sin6_port; 436bd670b35SErik Nordmark connp->conn_flowinfo = sin6->sin6_flowinfo; 4377c478bd9Sstevel@tonic-gate break; 4387c478bd9Sstevel@tonic-gate default: 4397c478bd9Sstevel@tonic-gate dprint(1, ("sctp_connect: unknown family %d\n", 4407c478bd9Sstevel@tonic-gate dst->sa_family)); 4417c478bd9Sstevel@tonic-gate return (EAFNOSUPPORT); 4427c478bd9Sstevel@tonic-gate } 4437c478bd9Sstevel@tonic-gate 4447c478bd9Sstevel@tonic-gate (void) inet_ntop(AF_INET6, &dstaddr, buf, sizeof (buf)); 4457c478bd9Sstevel@tonic-gate dprint(1, ("sctp_connect: attempting connect to %s...\n", buf)); 4467c478bd9Sstevel@tonic-gate 4477c478bd9Sstevel@tonic-gate RUN_SCTP(sctp); 4487c478bd9Sstevel@tonic-gate 449bd670b35SErik Nordmark if (connp->conn_family != dst->sa_family || 450bd670b35SErik Nordmark (connp->conn_state_flags & CONN_CLOSING)) { 4517c478bd9Sstevel@tonic-gate WAKE_SCTP(sctp); 4527c478bd9Sstevel@tonic-gate return (EINVAL); 4537c478bd9Sstevel@tonic-gate } 4547c478bd9Sstevel@tonic-gate 455bd670b35SErik Nordmark /* We update our cred/cpid based on the caller of connect */ 456bd670b35SErik Nordmark if (connp->conn_cred != cr) { 457bd670b35SErik Nordmark crhold(cr); 458bd670b35SErik Nordmark crfree(connp->conn_cred); 459bd670b35SErik Nordmark connp->conn_cred = cr; 460bd670b35SErik Nordmark } 461bd670b35SErik Nordmark connp->conn_cpid = pid; 462bd670b35SErik Nordmark 463bd670b35SErik Nordmark /* Cache things in conn_ixa without any refhold */ 464bd670b35SErik Nordmark ixa = connp->conn_ixa; 465be4c8f74SErik Nordmark ASSERT(!(ixa->ixa_free_flags & IXA_FREE_CRED)); 466bd670b35SErik Nordmark ixa->ixa_cred = cr; 467bd670b35SErik Nordmark ixa->ixa_cpid = pid; 468bd670b35SErik Nordmark if (is_system_labeled()) { 469bd670b35SErik Nordmark /* We need to restart with a label based on the cred */ 470bd670b35SErik Nordmark ip_xmit_attr_restore_tsl(ixa, ixa->ixa_cred); 471bd670b35SErik Nordmark } 472bd670b35SErik Nordmark 4737c478bd9Sstevel@tonic-gate switch (sctp->sctp_state) { 4747c478bd9Sstevel@tonic-gate case SCTPS_IDLE: { 4751d8c4025Svi117747 struct sockaddr_storage ss; 4761d8c4025Svi117747 4777c478bd9Sstevel@tonic-gate /* 4787c478bd9Sstevel@tonic-gate * We support a quick connect capability here, allowing 4797c478bd9Sstevel@tonic-gate * clients to transition directly from IDLE to COOKIE_WAIT. 4807c478bd9Sstevel@tonic-gate * sctp_bindi will pick an unused port, insert the connection 4817c478bd9Sstevel@tonic-gate * in the bind hash and transition to BOUND state. SCTP 4827c478bd9Sstevel@tonic-gate * picks and uses what it considers the optimal local address 4837c478bd9Sstevel@tonic-gate * set (just like specifiying INADDR_ANY to bind()). 4847c478bd9Sstevel@tonic-gate */ 4857c478bd9Sstevel@tonic-gate dprint(1, ("sctp_connect: idle, attempting bind...\n")); 4867c478bd9Sstevel@tonic-gate ASSERT(sctp->sctp_nsaddrs == 0); 4877c478bd9Sstevel@tonic-gate 4881d8c4025Svi117747 bzero(&ss, sizeof (ss)); 489bd670b35SErik Nordmark ss.ss_family = connp->conn_family; 4907c478bd9Sstevel@tonic-gate WAKE_SCTP(sctp); 4911d8c4025Svi117747 if ((err = sctp_bind(sctp, (struct sockaddr *)&ss, 4921d8c4025Svi117747 sizeof (ss))) != 0) { 4937c478bd9Sstevel@tonic-gate return (err); 4947c478bd9Sstevel@tonic-gate } 4951d8c4025Svi117747 RUN_SCTP(sctp); 4967c478bd9Sstevel@tonic-gate /* FALLTHRU */ 4977c478bd9Sstevel@tonic-gate } 4987c478bd9Sstevel@tonic-gate 4997c478bd9Sstevel@tonic-gate case SCTPS_BOUND: 5007c478bd9Sstevel@tonic-gate ASSERT(sctp->sctp_nsaddrs > 0); 5017c478bd9Sstevel@tonic-gate 5027c478bd9Sstevel@tonic-gate /* do the connect */ 5037c478bd9Sstevel@tonic-gate /* XXX check for attempt to connect to self */ 504bd670b35SErik Nordmark connp->conn_fport = dstport; 5057c478bd9Sstevel@tonic-gate 5067c478bd9Sstevel@tonic-gate /* 5077c478bd9Sstevel@tonic-gate * Don't allow this connection to completely duplicate 5087c478bd9Sstevel@tonic-gate * an existing connection. 5097c478bd9Sstevel@tonic-gate * 5107c478bd9Sstevel@tonic-gate * Ensure that the duplicate check and insertion is atomic. 5117c478bd9Sstevel@tonic-gate */ 5127c478bd9Sstevel@tonic-gate sctp_conn_hash_remove(sctp); 513f4b3ec61Sdh155122 tbf = &sctps->sctps_conn_fanout[SCTP_CONN_HASH(sctps, 514bd670b35SErik Nordmark connp->conn_ports)]; 5157c478bd9Sstevel@tonic-gate mutex_enter(&tbf->tf_lock); 516bd670b35SErik Nordmark lsctp = sctp_lookup(sctp, &dstaddr, tbf, &connp->conn_ports, 5177c478bd9Sstevel@tonic-gate SCTPS_COOKIE_WAIT); 5187c478bd9Sstevel@tonic-gate if (lsctp != NULL) { 5197c478bd9Sstevel@tonic-gate /* found a duplicate connection */ 5207c478bd9Sstevel@tonic-gate mutex_exit(&tbf->tf_lock); 5217c478bd9Sstevel@tonic-gate SCTP_REFRELE(lsctp); 5227c478bd9Sstevel@tonic-gate WAKE_SCTP(sctp); 5237c478bd9Sstevel@tonic-gate return (EADDRINUSE); 5247c478bd9Sstevel@tonic-gate } 525bd670b35SErik Nordmark 5267c478bd9Sstevel@tonic-gate /* 5277c478bd9Sstevel@tonic-gate * OK; set up the peer addr (this may grow after we get 5287c478bd9Sstevel@tonic-gate * the INIT ACK from the peer with additional addresses). 5297c478bd9Sstevel@tonic-gate */ 53077c67f2fSkcpoon if ((err = sctp_add_faddr(sctp, &dstaddr, sleep, 53177c67f2fSkcpoon B_FALSE)) != 0) { 5327c478bd9Sstevel@tonic-gate mutex_exit(&tbf->tf_lock); 5337c478bd9Sstevel@tonic-gate WAKE_SCTP(sctp); 53445916cd2Sjpk return (err); 5357c478bd9Sstevel@tonic-gate } 536c31292eeSkcpoon cur_fp = sctp->sctp_faddrs; 5376be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India ASSERT(cur_fp->sf_ixa != NULL); 538c31292eeSkcpoon 5397c478bd9Sstevel@tonic-gate /* No valid src addr, return. */ 5406be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India if (cur_fp->sf_state == SCTP_FADDRS_UNREACH) { 5417c478bd9Sstevel@tonic-gate mutex_exit(&tbf->tf_lock); 5427c478bd9Sstevel@tonic-gate WAKE_SCTP(sctp); 5437c478bd9Sstevel@tonic-gate return (EADDRNOTAVAIL); 5447c478bd9Sstevel@tonic-gate } 545c31292eeSkcpoon 546c31292eeSkcpoon sctp->sctp_primary = cur_fp; 547c31292eeSkcpoon sctp->sctp_current = cur_fp; 5486be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India sctp->sctp_mss = cur_fp->sf_pmss; 5497c478bd9Sstevel@tonic-gate sctp_conn_hash_insert(tbf, sctp, 1); 5507c478bd9Sstevel@tonic-gate mutex_exit(&tbf->tf_lock); 5517c478bd9Sstevel@tonic-gate 5526be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India ixa = cur_fp->sf_ixa; 553bd670b35SErik Nordmark ASSERT(ixa->ixa_cred != NULL); 554bd670b35SErik Nordmark 555bd670b35SErik Nordmark if (scope_id != 0) { 556bd670b35SErik Nordmark ixa->ixa_flags |= IXAF_SCOPEID_SET; 557bd670b35SErik Nordmark ixa->ixa_scopeid = scope_id; 558bd670b35SErik Nordmark } else { 559bd670b35SErik Nordmark ixa->ixa_flags &= ~IXAF_SCOPEID_SET; 560bd670b35SErik Nordmark } 561bd670b35SErik Nordmark 5627c478bd9Sstevel@tonic-gate /* initialize composite headers */ 56377c67f2fSkcpoon if ((err = sctp_set_hdraddrs(sctp)) != 0) { 56445916cd2Sjpk sctp_conn_hash_remove(sctp); 56545916cd2Sjpk WAKE_SCTP(sctp); 56645916cd2Sjpk return (err); 56745916cd2Sjpk } 5687c478bd9Sstevel@tonic-gate 569bd670b35SErik Nordmark if ((err = sctp_build_hdrs(sctp, KM_SLEEP)) != 0) { 570bd670b35SErik Nordmark sctp_conn_hash_remove(sctp); 571bd670b35SErik Nordmark WAKE_SCTP(sctp); 572bd670b35SErik Nordmark return (err); 573f4b3ec61Sdh155122 } 5747c478bd9Sstevel@tonic-gate 5757c478bd9Sstevel@tonic-gate /* 5767c478bd9Sstevel@tonic-gate * Turn off the don't fragment bit on the (only) faddr, 5777c478bd9Sstevel@tonic-gate * so that if one of the messages exchanged during the 5787c478bd9Sstevel@tonic-gate * initialization sequence exceeds the path mtu, it 5797c478bd9Sstevel@tonic-gate * at least has a chance to get there. SCTP does no 5807c478bd9Sstevel@tonic-gate * fragmentation of initialization messages. The DF bit 5817c478bd9Sstevel@tonic-gate * will be turned on again in sctp_send_cookie_echo() 5827c478bd9Sstevel@tonic-gate * (but the cookie echo will still be sent with the df bit 5837c478bd9Sstevel@tonic-gate * off). 5847c478bd9Sstevel@tonic-gate */ 5856be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India cur_fp->sf_df = B_FALSE; 5867c478bd9Sstevel@tonic-gate 5877c478bd9Sstevel@tonic-gate /* Mark this address as alive */ 5886be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India cur_fp->sf_state = SCTP_FADDRS_ALIVE; 5897c478bd9Sstevel@tonic-gate 5907c478bd9Sstevel@tonic-gate /* Send the INIT to the peer */ 5916be61d4eSchandrasekar marimuthu - Sun Microsystems - Bangalore India SCTP_FADDR_TIMER_RESTART(sctp, cur_fp, cur_fp->sf_rto); 592c31292eeSkcpoon sctp->sctp_state = SCTPS_COOKIE_WAIT; 593f551bb10Svi117747 /* 594f551bb10Svi117747 * sctp_init_mp() could result in modifying the source 595f551bb10Svi117747 * address list, so take the hash lock. 596f551bb10Svi117747 */ 597f551bb10Svi117747 mutex_enter(&tbf->tf_lock); 598bd670b35SErik Nordmark initmp = sctp_init_mp(sctp, cur_fp); 5997c478bd9Sstevel@tonic-gate if (initmp == NULL) { 600f551bb10Svi117747 mutex_exit(&tbf->tf_lock); 601c31292eeSkcpoon /* 602c31292eeSkcpoon * It may happen that all the source addresses 603c31292eeSkcpoon * (loopback/link local) are removed. In that case, 604c31292eeSkcpoon * faile the connect. 605c31292eeSkcpoon */ 606c31292eeSkcpoon if (sctp->sctp_nsaddrs == 0) { 607c31292eeSkcpoon sctp_conn_hash_remove(sctp); 608c31292eeSkcpoon SCTP_FADDR_TIMER_STOP(cur_fp); 6097c478bd9Sstevel@tonic-gate WAKE_SCTP(sctp); 610c31292eeSkcpoon return (EADDRNOTAVAIL); 611c31292eeSkcpoon } 612c31292eeSkcpoon 613c31292eeSkcpoon /* Otherwise, let the retransmission timer retry */ 614c31292eeSkcpoon WAKE_SCTP(sctp); 615c31292eeSkcpoon goto notify_ulp; 6167c478bd9Sstevel@tonic-gate } 617f551bb10Svi117747 mutex_exit(&tbf->tf_lock); 618c31292eeSkcpoon 6191d8c4025Svi117747 /* 6201d8c4025Svi117747 * On a clustered note send this notification to the clustering 6211d8c4025Svi117747 * subsystem. 6221d8c4025Svi117747 */ 6231d8c4025Svi117747 if (cl_sctp_connect != NULL) { 6241d8c4025Svi117747 uchar_t *slist; 6251d8c4025Svi117747 uchar_t *flist; 6261d8c4025Svi117747 size_t ssize; 6271d8c4025Svi117747 size_t fsize; 6281d8c4025Svi117747 6291d8c4025Svi117747 fsize = sizeof (in6_addr_t) * sctp->sctp_nfaddrs; 6301d8c4025Svi117747 ssize = sizeof (in6_addr_t) * sctp->sctp_nsaddrs; 6311d8c4025Svi117747 slist = kmem_alloc(ssize, KM_SLEEP); 6321d8c4025Svi117747 flist = kmem_alloc(fsize, KM_SLEEP); 6331d8c4025Svi117747 /* The clustering module frees the lists */ 6341d8c4025Svi117747 sctp_get_saddr_list(sctp, slist, ssize); 6351d8c4025Svi117747 sctp_get_faddr_list(sctp, flist, fsize); 636bd670b35SErik Nordmark (*cl_sctp_connect)(connp->conn_family, slist, 637bd670b35SErik Nordmark sctp->sctp_nsaddrs, connp->conn_lport, 638bd670b35SErik Nordmark flist, sctp->sctp_nfaddrs, connp->conn_fport, 6391d8c4025Svi117747 B_TRUE, (cl_sctp_handle_t)sctp); 6401d8c4025Svi117747 } 641bd670b35SErik Nordmark ASSERT(ixa->ixa_cred != NULL); 642bd670b35SErik Nordmark ASSERT(ixa->ixa_ire != NULL); 643bd670b35SErik Nordmark 644bd670b35SErik Nordmark (void) conn_ip_output(initmp, ixa); 6457c478bd9Sstevel@tonic-gate BUMP_LOCAL(sctp->sctp_opkts); 646bd670b35SErik Nordmark WAKE_SCTP(sctp); 6477c478bd9Sstevel@tonic-gate 648c31292eeSkcpoon notify_ulp: 649bd670b35SErik Nordmark sctp_set_ulp_prop(sctp); 6507c478bd9Sstevel@tonic-gate 6517c478bd9Sstevel@tonic-gate return (0); 6527c478bd9Sstevel@tonic-gate default: 6537c478bd9Sstevel@tonic-gate ip0dbg(("sctp_connect: invalid state. %d\n", sctp->sctp_state)); 6547c478bd9Sstevel@tonic-gate WAKE_SCTP(sctp); 6557c478bd9Sstevel@tonic-gate return (EINVAL); 6567c478bd9Sstevel@tonic-gate } 6577c478bd9Sstevel@tonic-gate } 658