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