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