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 /*
23 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /*
28 * Copyright 2012 David Hoeppner. All rights reserved.
29 */
30
31 /*
32 * This file contains function related to the socket interface.
33 */
34
35 #include <sys/types.h>
36 #include <sys/strlog.h>
37 #include <sys/policy.h>
38 #include <sys/sockio.h>
39 #include <sys/strsubr.h>
40 #include <sys/strsun.h>
41 #define _SUN_TPI_VERSION 2
42 #include <sys/tihdr.h>
43 #include <sys/squeue_impl.h>
44 #include <sys/squeue.h>
45 #include <sys/socketvar.h>
46
47 #include <inet/common.h>
48 #include <inet/dccp_impl.h>
49 #include <inet/dccp_stack.h>
50 #include <inet/proto_set.h>
51 #include <inet/ip.h>
52
53 #include <sys/cmn_err.h>
54
55 static void dccp_activate(sock_lower_handle_t, sock_upper_handle_t,
56 sock_upcalls_t *, int, cred_t *);
57 static int dccp_accept(sock_lower_handle_t, sock_lower_handle_t,
58 sock_upper_handle_t, cred_t *);
59 static int dccp_bind(sock_lower_handle_t, struct sockaddr *,
60 socklen_t, cred_t *);
61 static int dccp_listen(sock_lower_handle_t, int, cred_t *);
62 static int dccp_connect(sock_lower_handle_t, const struct sockaddr *,
63 socklen_t, sock_connid_t *, cred_t *);
64 static int dccp_getpeername(sock_lower_handle_t, struct sockaddr *,
65 socklen_t *, cred_t *);
66 static int dccp_getsockname(sock_lower_handle_t, struct sockaddr *,
67 socklen_t *, cred_t *);
68 static int dccp_getsockopt(sock_lower_handle_t, int, int, void *,
69 socklen_t *, cred_t *);
70 static int dccp_setsockopt(sock_lower_handle_t, int, int, const void *,
71 socklen_t, cred_t *);
72 static int dccp_send(sock_lower_handle_t, mblk_t *, struct nmsghdr *,
73 cred_t *);
74 static int dccp_shutdown(sock_lower_handle_t, int, cred_t *);
75 static void dccp_clr_flowctrl(sock_lower_handle_t);
76 static int dccp_ioctl(sock_lower_handle_t, int, intptr_t, int, int32_t *,
77 cred_t *);
78 static int dccp_close(sock_lower_handle_t, int, cred_t *);
79
80 sock_downcalls_t sock_dccp_downcalls = {
81 dccp_activate, /* sd_activate */
82 dccp_accept, /* sd_accept */
83 dccp_bind, /* sd_bind */
84 dccp_listen, /* sd_listen */
85 dccp_connect, /* sd_connect */
86 dccp_getpeername, /* sd_getpeername */
87 dccp_getsockname, /* sd_getsockname */
88 dccp_getsockopt, /* sd_getsockopt */
89 dccp_setsockopt, /* sd_setsockopt */
90 dccp_send, /* sd_send */
91 NULL, /* sd_send_uio */
92 NULL, /* sd_recv_uio */
93 NULL, /* sd_poll */
94 dccp_shutdown, /* sd_shutdown */
95 dccp_clr_flowctrl, /* sd_setflowctrl */
96 dccp_ioctl, /* sd_ioctl */
97 dccp_close, /* sd_close */
98 };
99
100 /* ARGSUSED */
101 static void
102 dccp_activate(sock_lower_handle_t proto_handle, sock_upper_handle_t sock_handle,
103 sock_upcalls_t *sock_upcalls, int flags, cred_t *cr)
104 {
105 conn_t *connp = (conn_t *)proto_handle;
106 struct sock_proto_props sopp;
107 extern struct module_info dccp_rinfo;
108
109 cmn_err(CE_NOTE, "dccp_socket.c: dccp_activate");
110
111 ASSERT(connp->conn_upper_handle == NULL);
112
113 /* All Solaris components should pass a cred for this operation */
114 ASSERT(cr != NULL);
115
116 sopp.sopp_flags = SOCKOPT_RCVHIWAT | SOCKOPT_RCVLOWAT |
117 SOCKOPT_MAXPSZ | SOCKOPT_MAXBLK | SOCKOPT_RCVTIMER |
118 SOCKOPT_RCVTHRESH | SOCKOPT_MAXADDRLEN | SOCKOPT_MINPSZ;
119
120 sopp.sopp_rxhiwat = SOCKET_RECVHIWATER;
121 sopp.sopp_rxlowat = SOCKET_RECVLOWATER;
122 sopp.sopp_maxpsz = INFPSZ;
123 sopp.sopp_maxblk = INFPSZ;
124 sopp.sopp_rcvtimer = SOCKET_TIMER_INTERVAL;
125 sopp.sopp_rcvthresh = SOCKET_RECVHIWATER >> 3;
126 sopp.sopp_maxaddrlen = sizeof (sin6_t);
127 sopp.sopp_minpsz = (dccp_rinfo.mi_minpsz == 1) ? 0 :
128 dccp_rinfo.mi_minpsz;
129
130 connp->conn_upcalls = sock_upcalls;
131 connp->conn_upper_handle = sock_handle;
132
133 /* XXX conn_rcvbuf */
134 (*connp->conn_upcalls->su_set_proto_props)(connp->conn_upper_handle,
135 &sopp);
136 }
137
138 /*ARGSUSED*/
139 static int
140 dccp_accept(sock_lower_handle_t lproto_handle,
141 sock_lower_handle_t eproto_handle, sock_upper_handle_t sock_handle,
142 cred_t *cr)
143 {
144 conn_t *lconnp, *econnp;
145 dccp_t *listener, *eager;
146
147 cmn_err(CE_NOTE, "dccp_socket.c: dccp_accept");
148
149 econnp = (conn_t *)eproto_handle;
150 eager = econnp->conn_dccp;
151 ASSERT(IPCL_IS_NONSTR(econnp));
152 ASSERT(eager->dccp_listener != NULL);
153 listener = eager->dccp_listener;
154 lconnp = (conn_t *)listener->dccp_connp;
155 ASSERT(listener->dccp_state == DCCPS_LISTEN);
156 ASSERT(lconnp->conn_upper_handle != NULL);
157
158 ASSERT(econnp->conn_upper_handle == NULL ||
159 econnp->conn_upper_handle == sock_handle);
160 ASSERT(econnp->conn_upcalls == NULL ||
161 econnp->conn_upcalls == lconnp->conn_upcalls);
162 econnp->conn_upper_handle = sock_handle;
163 econnp->conn_upcalls = lconnp->conn_upcalls;
164
165 ASSERT(econnp->conn_netstack ==
166 listener->dccp_connp->conn_netstack);
167 ASSERT(eager->dccp_dccps == listener->dccp_dccps);
168
169 ASSERT(econnp->conn_ref >= 2);
170 eager->dccp_listener = NULL; /* XXX */
171 CONN_DEC_REF(listener->dccp_connp);
172
173 return (0);
174 }
175
176 static int
177 dccp_bind(sock_lower_handle_t proto_handle, struct sockaddr *sa,
178 socklen_t len, cred_t *cr)
179 {
180 conn_t *connp = (conn_t *)proto_handle;
181 int error;
182
183 cmn_err(CE_NOTE, "dccp_socket.c: dccp_bind");
184
185 ASSERT(connp->conn_upper_handle != NULL);
186
187 /* All Solaris components should pass a cred for this operation */
188 ASSERT(cr != NULL);
189
190 error = squeue_synch_enter(connp, NULL);
191 if (error != 0) {
192 /* Failed to enter */
193 return (ENOSR);
194 }
195
196 /* Binding to NULL address means unbind */
197 if (sa == NULL) {
198 if (connp->conn_dccp->dccp_state < DCCPS_LISTEN) {
199 error = dccp_do_unbind(connp);
200 } else {
201 error = EINVAL;
202 }
203 } else {
204 error = dccp_do_bind(connp, sa, len, cr, B_TRUE);
205 }
206
207 squeue_synch_exit(connp);
208
209 if (error < 0) {
210 if (error == -TOUTSTATE) {
211 error = EINVAL;
212 } else {
213 error = proto_tlitosyserr(-error);
214 }
215 }
216
217 return (error);
218 }
219
220 /* ARGSUSED */
221 static int
222 dccp_listen(sock_lower_handle_t proto_handle, int backlog, cred_t *cr)
223 {
224 conn_t *connp = (conn_t *)proto_handle;
225 dccp_t *dccp = connp->conn_dccp;
226 int error;
227
228 cmn_err(CE_NOTE, "dccp_socket.c: dccp_listen");
229
230 ASSERT(connp->conn_upper_handle != NULL);
231
232 /* All Solaris components should pass a cred for this operation */
233 ASSERT(cr != NULL);
234
235 error = squeue_synch_enter(connp, NULL);
236 if (error != 0) {
237 /* Failed to enter */
238 return (ENOBUFS);
239 }
240
241 error = dccp_do_listen(connp, NULL, 0, backlog, cr, B_FALSE);
242 if (error == 0) {
243 /* XXX:DCCP */
244 (*connp->conn_upcalls->su_opctl)(connp->conn_upper_handle,
245 SOCK_OPCTL_ENAB_ACCEPT,
246 (uintptr_t)(10));
247 } else if (error < 0) {
248 if (error == -TOUTSTATE) {
249 error = EINVAL;
250 } else {
251 error = proto_tlitosyserr(-error);
252 }
253 }
254
255 squeue_synch_exit(connp);
256
257 return (error);
258 }
259
260 static int
261 dccp_connect(sock_lower_handle_t proto_handle, const struct sockaddr *sa,
262 socklen_t len, sock_connid_t *id, cred_t *cr)
263 {
264 conn_t *connp = (conn_t *)proto_handle;
265 int error;
266
267 cmn_err(CE_NOTE, "dccp_socket.c: dccp_connect");
268
269 ASSERT(connp->conn_upper_handle != NULL);
270
271 /* All Solaris components should pass a cred for this operation */
272 ASSERT(cr != NULL);
273
274 error = proto_verify_ip_addr(connp->conn_family, sa, len);
275 if (error != 0) {
276 return (error);
277 }
278
279 error = squeue_synch_enter(connp, NULL);
280 if (error != 0) {
281 /* Failed to enter */
282 return (ENOSR);
283 }
284
285 error = dccp_do_connect(connp, sa, len, cr, curproc->p_pid);
286 if (error == 0) {
287 *id = connp->conn_dccp->dccp_connid;
288 } else if (error < 0) {
289 if (error == -TOUTSTATE) {
290 switch (connp->conn_dccp->dccp_state) {
291 case DCCPS_REQUEST:
292 error = EALREADY;
293 break;
294 case DCCPS_PARTOPEN:
295 error = EISCONN;
296 break;
297 case DCCPS_LISTEN:
298 error = EOPNOTSUPP;
299 break;
300 default:
301 error = EINVAL;
302 break;
303 }
304 } else {
305 error = proto_tlitosyserr(-error);
306 }
307 }
308
309 squeue_synch_exit(connp);
310
311 cmn_err(CE_NOTE, "dccp_connect.c: exit %d", error);
312 return ((error == 0) ? EINPROGRESS : error);
313 }
314
315 /* ARGSUSED3 */
316 static int
317 dccp_getpeername(sock_lower_handle_t proto_handle, struct sockaddr *addr,
318 socklen_t *addrlenp, cred_t *cr)
319 {
320 conn_t *connp = (conn_t *)proto_handle;
321 dccp_t *dccp = connp->conn_dccp;
322
323 cmn_err(CE_NOTE, "dccp_socket.c: dccp_getpeername");
324
325 /* All Solaris components should pass a cred for this operation */
326 ASSERT(cr != NULL);
327
328 ASSERT(dccp != NULL);
329 if (dccp->dccp_state < DCCPS_OPEN) {
330 return (ENOTCONN);
331 }
332
333 return (conn_getpeername(connp, addr, addrlenp));
334 }
335
336 /* ARGSUSED3 */
337 static int
338 dccp_getsockname(sock_lower_handle_t proto_handle, struct sockaddr *addr,
339 socklen_t *addrlenp, cred_t *cr)
340 {
341 conn_t *connp = (conn_t *)proto_handle;
342 int error;
343
344 cmn_err(CE_NOTE, "dccp_socket.c: dccp_getsockname");
345
346 /* All Solaris components should pass a cred for this operation */
347 ASSERT(cr != NULL);
348
349 /* XXX UDP has locks here, TCP not */
350 mutex_enter(&connp->conn_lock);
351 error = conn_getsockname(connp, addr, addrlenp);
352 mutex_exit(&connp->conn_lock);
353
354 return (error);
355 }
356
357 static int
358 dccp_getsockopt(sock_lower_handle_t proto_handle, int level, int option_name,
359 void *optvalp, socklen_t *optlen, cred_t *cr)
360 {
361 conn_t *connp = (conn_t *)proto_handle;
362 void *optvalp_buf;
363 t_uscalar_t max_optbuf_len;
364 int len;
365 int error;
366
367 cmn_err(CE_NOTE, "dccp_socket.c: dccp_getsockopt");
368
369 ASSERT(connp->conn_upper_handle != NULL);
370
371 /* All Solaris components should pass a cred for this operation */
372 ASSERT(cr != NULL);
373
374 error = proto_opt_check(level, option_name, *optlen, &max_optbuf_len,
375 dccp_opt_obj.odb_opt_des_arr,
376 dccp_opt_obj.odb_opt_arr_cnt,
377 B_FALSE, B_TRUE, cr);
378 if (error != 0) {
379 if (error < 0) {
380 error = proto_tlitosyserr(-error);
381 }
382 return (error);
383 }
384
385 optvalp_buf = kmem_alloc(max_optbuf_len, KM_SLEEP);
386 if (optvalp_buf == NULL) {
387 return (ENOMEM);
388 }
389
390 error = squeue_synch_enter(connp, NULL);
391 if (error == ENOMEM) {
392 kmem_free(optvalp_buf, max_optbuf_len);
393 return (ENOMEM);
394 }
395
396 len = dccp_opt_get(connp, level, option_name, optvalp_buf);
397 squeue_synch_exit(connp);
398
399 if (len == -1) {
400 kmem_free(optvalp_buf, max_optbuf_len);
401 return (EINVAL);
402 }
403
404 t_uscalar_t size = MIN(len, *optlen);
405
406 bcopy(optvalp_buf, optvalp, size);
407 bcopy(&size, optlen, sizeof (size));
408
409 kmem_free(optvalp_buf, max_optbuf_len);
410
411 return (0);
412 }
413
414 static int
415 dccp_setsockopt(sock_lower_handle_t proto_handle, int level, int option_name,
416 const void *optvalp, socklen_t optlen, cred_t *cr)
417 {
418 conn_t *connp = (conn_t *)proto_handle;
419 int error;
420
421 cmn_err(CE_NOTE, "dccp_socket.c: dccp_setsockopt");
422
423 ASSERT(connp->conn_upper_handle != NULL);
424
425 /* All Solaris components should pass a cred for this operation */
426 ASSERT(cr != NULL);
427
428 error = squeue_synch_enter(connp, NULL);
429 if (error == ENOMEM) {
430 return (ENOMEM);
431 }
432
433 error = proto_opt_check(level, option_name, optlen, NULL,
434 dccp_opt_obj.odb_opt_des_arr,
435 dccp_opt_obj.odb_opt_arr_cnt,
436 B_TRUE, B_FALSE, cr);
437 if (error != 0) {
438 if (error < 0) {
439 error = proto_tlitosyserr(-error);
440 }
441 squeue_synch_exit(connp);
442 return (error);
443 }
444
445 error = dccp_opt_set(connp, SETFN_OPTCOM_NEGOTIATE, level, option_name,
446 optlen, (uchar_t *)optvalp, (uint_t *)&optlen, (uchar_t *)optvalp,
447 NULL, cr);
448 squeue_synch_exit(connp);
449
450 ASSERT(error >= 0);
451
452 return (error);
453 }
454
455 /* ARGSUSED */
456 static int
457 dccp_send(sock_lower_handle_t proto_handle, mblk_t *mp, struct nmsghdr *msg,
458 cred_t *cr)
459 {
460 conn_t *connp = (conn_t *)proto_handle;
461 dccp_t *dccp;
462 uint32_t msize;
463 int32_t dccpstate;
464
465 cmn_err(CE_NOTE, "dccp_socket.c: dccp_send");
466
467 /* All Solaris components should pass a cred for this operation */
468 ASSERT(cr != NULL);
469
470 ASSERT(connp->conn_ref >= 2);
471 ASSERT(connp->conn_upper_handle != NULL);
472
473 if (msg->msg_controllen != 0) {
474 freemsg(mp);
475 return (EOPNOTSUPP);
476 }
477
478 switch (DB_TYPE(mp)) {
479 case M_DATA:
480 dccp = connp->conn_dccp;
481 ASSERT(dccp != NULL);
482
483 dccpstate = dccp->dccp_state;
484 if (dccpstate < DCCPS_OPEN) {
485 freemsg(mp);
486
487 return ((dccpstate == DCCPS_REQUEST) ? ENOTCONN :
488 ((dccp->dccp_connid > 0) ? EPIPE : ENOTCONN));
489 } else if (dccpstate > DCCPS_CLOSING) {
490 freemsg(mp);
491 return (EPIPE);
492 }
493
494 /* XXX */
495
496 msize = msgdsize(mp);
497
498 CONN_INC_REF(connp);
499
500 if (msg->msg_flags & MSG_OOB) {
501 SQUEUE_ENTER_ONE(connp->conn_sqp, mp, dccp_output_urgent,
502 connp, NULL, dccp_squeue_flag, SQTAG_DCCP_OUTPUT);
503 } else {
504 SQUEUE_ENTER_ONE(connp->conn_sqp, mp, dccp_output,
505 connp, NULL, dccp_squeue_flag, SQTAG_DCCP_OUTPUT);
506 }
507
508 return (0);
509
510 default:
511 ASSERT(0);
512 }
513
514 freemsg(mp);
515
516 return (0);
517 }
518
519 /* ARGSUSED */
520 static int
521 dccp_shutdown(sock_lower_handle_t proto_handle, int how, cred_t *cr)
522 {
523 conn_t *connp = (conn_t *)proto_handle;
524 dccp_t *dccp = connp->conn_dccp;
525
526 cmn_err(CE_NOTE, "dccp_socket.c: dccp_shutdown");
527
528 /* All Solaris components should pass a cred for this operation. */
529 ASSERT(cr != NULL);
530
531 ASSERT(connp->conn_upper_handle != NULL);
532
533
534 return (ENOTSUP);
535 }
536
537 static void
538 dccp_clr_flowctrl(sock_lower_handle_t proto_handle)
539 {
540 conn_t *connp = (conn_t *)proto_handle;
541 dccp_t *dccp = connp->conn_dccp;
542 mblk_t *mp;
543 int error;
544
545 ASSERT(connp->conn_upper_handle != NULL);
546
547 cmn_err(CE_NOTE, "dccp_socket.c: dccp_clr_flowctrl");
548
549 error = squeue_synch_enter(connp, mp);
550
551 squeue_synch_exit(connp);
552 }
553
554 /* ARGSUSED */
555 static int
556 dccp_ioctl(sock_lower_handle_t proto_handle, int cmd, intptr_t arg,
557 int mode, int32_t *rvalp, cred_t *cr)
558 {
559 conn_t *connp = (conn_t *)proto_handle;
560 int error;
561
562 cmn_err(CE_NOTE, "dccp_socket.c: dccp_ioctl");
563
564 ASSERT(connp->conn_upper_handle != NULL);
565
566 /* All Solaris components should pass a cred for this operation. */
567 ASSERT(cr != NULL);
568
569 return (ENOTSUP);
570 }
571
572 /* ARGSUSED */
573 static int
574 dccp_close(sock_lower_handle_t proto_handle, int flags, cred_t *cr)
575 {
576 conn_t *connp = (conn_t *)proto_handle;
577
578 cmn_err(CE_NOTE, "dccp_socket.c: dccp_close\n");
579
580 ASSERT(connp->conn_upper_handle != NULL);
581
582 /* All Solaris components should pass a cred for this operation */
583 ASSERT(cr != NULL);
584
585 dccp_close_common(connp, flags);
586
587 ip_free_helper_stream(connp);
588
589 CONN_DEC_REF(connp);
590
591 /*
592 * EINPROGRESS tells sockfs to wait for a 'closed' upcall before
593 * freeing the socket.
594 */
595 return (EINPROGRESS);
596 }
597
598
599 /*
600 * Socket create function.
601 */
602 sock_lower_handle_t
603 dccp_create(int family, int type, int proto, sock_downcalls_t **sockdowncalls,
604 uint_t *smodep, int *errorp, int flags, cred_t *credp)
605 {
606 conn_t *connp;
607 boolean_t isv6;
608
609 /* XXX (type != SOCK_STREAM */
610 if ((family != AF_INET && family != AF_INET6) ||
611 (proto != 0 && proto != IPPROTO_DCCP)) {
612 *errorp = EPROTONOSUPPORT;
613 return (NULL);
614 }
615
616 cmn_err(CE_NOTE, "dccp_socket: dccp_create\n");
617
618 isv6 = family == AF_INET6 ? B_TRUE: B_FALSE;
619 connp = dccp_create_common(credp, isv6, B_TRUE, errorp);
620 if (connp == NULL) {
621 return (NULL);
622 }
623
624 /*
625 * Increment ref for DCCP connection.
626 */
627 mutex_enter(&connp->conn_lock);
628 CONN_INC_REF_LOCKED(connp);
629 ASSERT(connp->conn_ref == 2);
630 connp->conn_state_flags &= ~CONN_INCIPIENT;
631 connp->conn_flags |= IPCL_NONSTR;
632 mutex_exit(&connp->conn_lock);
633
634 ASSERT(errorp != NULL);
635 *errorp = 0;
636 *sockdowncalls = &sock_dccp_downcalls;
637 *smodep = SM_CONNREQUIRED | SM_EXDATA | SM_ACCEPTSUPP |
638 SM_SENDFILESUPP;
639
640 return ((sock_lower_handle_t)connp);
641 }
642
643 int
644 dccp_fallback(sock_lower_handle_t proto_handle, queue_t *q,
645 boolean_t issocket, so_proto_quiesced_cb_t quiesced_cb,
646 sock_quiesce_arg_t *arg)
647 {
648 cmn_err(CE_NOTE, "dccp_socket: dccp_fallback\n");
649
650 return (0);
651 }
652
653 /*
654 * Notifies a non-STREAMS based listener about a new connection. This
655 * function is executed on the *eager*'s squeue once the 3 way handshake
656 * has completed. Note that the behavior differs from STREAMS, where the
657 * T_CONN_IND is sent up by tcp_send_conn_ind() while on the *listener*'s
658 * squeue.
659 *
660 * Returns B_TRUE if the notification succeeded and an upper handle was
661 * obtained. `tcp' should be closed on failure.
662 */
663 boolean_t
664 dccp_newconn_notify(dccp_t *dccp, ip_recv_attr_t *ira)
665 {
666 dccp_t *listener = dccp->dccp_listener;
667 dccp_t *tail;
668 conn_t *lconnp = listener->dccp_connp;
669 conn_t *econnp = dccp->dccp_connp;
670 ipaddr_t *addr_cache;
671 sock_upper_handle_t upper;
672 struct sock_proto_props sopp;
673
674 cmn_err(CE_NOTE, "dccp_socket.c: dccp_newconn_notify");
675
676 if (lconnp->conn_upcalls) {
677 cmn_err(CE_NOTE, "NOT NULL");
678 } else {
679 cmn_err(CE_NOTE, "ISSSSSS NULL");
680 }
681
682 if ((upper = (*lconnp->conn_upcalls->su_newconn)
683 (lconnp->conn_upper_handle, (sock_lower_handle_t)econnp,
684 &sock_dccp_downcalls, ira->ira_cred, ira->ira_cpid,
685 &econnp->conn_upcalls)) == NULL) {
686 return (B_FALSE);
687 }
688 econnp->conn_upper_handle = upper;
689
690 (*econnp->conn_upcalls->su_set_proto_props)
691 (econnp->conn_upper_handle, &sopp);
692
693 return (B_TRUE);
694 }