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
sctp_accept_comm(sctp_t * listener,sctp_t * acceptor,mblk_t * cr_pkt,uint_t ip_hdr_len,sctp_init_chunk_t * iack)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;
64d7ab25acSkp conn_t *aconnp;
6545916cd2Sjpk conn_t *lconnp;
665dd46ab5SKacheong Poon 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) &&
93f4b3ec61Sdh 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
125f4b3ec61Sdh sctp_conn_hash_insert(&sctps->sctps_conn_fanout[
126bd670b35SErik Nordmark SCTP_CONN_HASH(sctps, aconnp->conn_ports)], acceptor, 0);
127f4b3ec61Sdh 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 *
sctp_conn_request(sctp_t * sctp,mblk_t * mp,uint_t ifindex,uint_t ip_hdr_len,sctp_init_chunk_t * iack,ip_recv_attr_t * ira)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;
143f4b3ec61Sdh 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
1605dd46ab5SKacheong Poon connp = sctp->sctp_connp;
1615dd46ab5SKacheong Poon 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
1711a5e258fSJosef 'Jeff' Sipek if (atomic_inc_32_nv(&slc->slc_cnt) > slc->slc_max + 1) {
1725dd46ab5SKacheong Poon now = ddi_get_lbolt64();
1731a5e258fSJosef '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
1907c478bd9Sstevel@tonic-gate if ((eager = sctp_create_eager(sctp)) == NULL) {
1915dd46ab5SKacheong Poon if (slc_set)
1921a5e258fSJosef 'Jeff' Sipek atomic_dec_32(&slc->slc_cnt);
1937c478bd9Sstevel@tonic-gate return (NULL);
1947c478bd9Sstevel@tonic-gate }
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
2971d8c4025Svi /*
2981d8c4025Svi * On a clustered note send this notification to the clustering
2991d8c4025Svi * subsystem.
3001d8c4025Svi */
3011d8c4025Svi if (cl_sctp_connect != NULL) {
3021d8c4025Svi uchar_t *slist;
3031d8c4025Svi uchar_t *flist;
3041d8c4025Svi size_t fsize;
3051d8c4025Svi size_t ssize;
3061d8c4025Svi
3071d8c4025Svi fsize = sizeof (in6_addr_t) * eager->sctp_nfaddrs;
3081d8c4025Svi ssize = sizeof (in6_addr_t) * eager->sctp_nsaddrs;
3091d8c4025Svi slist = kmem_alloc(ssize, KM_NOSLEEP);
3101d8c4025Svi flist = kmem_alloc(fsize, KM_NOSLEEP);
3111d8c4025Svi if (slist == NULL || flist == NULL) {
3121d8c4025Svi if (slist != NULL)
3131d8c4025Svi kmem_free(slist, ssize);
3141d8c4025Svi if (flist != NULL)
3151d8c4025Svi kmem_free(flist, fsize);
3161d8c4025Svi sctp_close_eager(eager);
3175dd46ab5SKacheong Poon SCTPS_BUMP_MIB(sctps, sctpListenDrop);
318f4b3ec61Sdh SCTP_KSTAT(sctps, sctp_cl_connect);
3191d8c4025Svi return (NULL);
3201d8c4025Svi }
3211d8c4025Svi /* The clustering module frees these list */
3221d8c4025Svi sctp_get_saddr_list(eager, slist, ssize);
3231d8c4025Svi 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,
3271d8c4025Svi (cl_sctp_handle_t)eager);
3281d8c4025Svi }
3291d8c4025Svi
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
sctp_connect(sctp_t * sctp,const struct sockaddr * dst,uint32_t addrlen,cred_t * cr,pid_t pid)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;
363f4b3ec61Sdh 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)) {
431f551bb10Svi 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",
440b34b8d1aSkcpoon 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: {
4751d8c4025Svi struct sockaddr_storage ss;
4761d8c4025Svi
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
4881d8c4025Svi bzero(&ss, sizeof (ss));
489bd670b35SErik Nordmark ss.ss_family = connp->conn_family;
4901d8c4025Svi WAKE_SCTP(sctp);
4911d8c4025Svi if ((err = sctp_bind(sctp, (struct sockaddr *)&ss,
4921d8c4025Svi sizeof (ss))) != 0) {
4937c478bd9Sstevel@tonic-gate return (err);
4947c478bd9Sstevel@tonic-gate }
4951d8c4025Svi RUN_SCTP(sctp);
4967c478bd9Sstevel@tonic-gate }
497*a9f62b1aSToomas Soome /* FALLTHROUGH */
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);
513f4b3ec61Sdh 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);
573f4b3ec61Sdh }
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;
593f551bb10Svi /*
594f551bb10Svi * sctp_init_mp() could result in modifying the source
595f551bb10Svi * address list, so take the hash lock.
596f551bb10Svi */
597f551bb10Svi mutex_enter(&tbf->tf_lock);
598bd670b35SErik Nordmark initmp = sctp_init_mp(sctp, cur_fp);
5997c478bd9Sstevel@tonic-gate if (initmp == NULL) {
600f551bb10Svi 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);
609c31292eeSkcpoon WAKE_SCTP(sctp);
610c31292eeSkcpoon return (EADDRNOTAVAIL);
611c31292eeSkcpoon }
612c31292eeSkcpoon
613c31292eeSkcpoon /* Otherwise, let the retransmission timer retry */
6147c478bd9Sstevel@tonic-gate WAKE_SCTP(sctp);
615c31292eeSkcpoon goto notify_ulp;
6167c478bd9Sstevel@tonic-gate }
617f551bb10Svi mutex_exit(&tbf->tf_lock);
618c31292eeSkcpoon
6191d8c4025Svi /*
6201d8c4025Svi * On a clustered note send this notification to the clustering
6211d8c4025Svi * subsystem.
6221d8c4025Svi */
6231d8c4025Svi if (cl_sctp_connect != NULL) {
6241d8c4025Svi uchar_t *slist;
6251d8c4025Svi uchar_t *flist;
6261d8c4025Svi size_t ssize;
6271d8c4025Svi size_t fsize;
6281d8c4025Svi
6291d8c4025Svi fsize = sizeof (in6_addr_t) * sctp->sctp_nfaddrs;
6301d8c4025Svi ssize = sizeof (in6_addr_t) * sctp->sctp_nsaddrs;
6311d8c4025Svi slist = kmem_alloc(ssize, KM_SLEEP);
6321d8c4025Svi flist = kmem_alloc(fsize, KM_SLEEP);
6331d8c4025Svi /* The clustering module frees the lists */
6341d8c4025Svi sctp_get_saddr_list(sctp, slist, ssize);
6351d8c4025Svi 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,
6391d8c4025Svi B_TRUE, (cl_sctp_handle_t)sctp);
6401d8c4025Svi }
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