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  * Copyright (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved.
23  * Copyright (c) 1990 Mentat Inc.
24  */
25 
26 #include <sys/ib/clients/rds/rds.h>
27 #include <inet/proto_set.h>
28 
29 #define	rds_max_buf 2097152
30 opdes_t rds_opt_arr[] = {
31 
32 { SO_TYPE,	SOL_SOCKET, OA_R, OA_R, OP_NP, 0, sizeof (int), 0 },
33 { SO_SNDBUF,	SOL_SOCKET, OA_RW, OA_RW, OP_NP, 0, sizeof (int), 0 },
34 { SO_RCVBUF,	SOL_SOCKET, OA_RW, OA_RW, OP_NP, 0, sizeof (int), 0 },
35 };
36 
37 /* ARGSUSED */
38 int
rds_opt_default(queue_t * q,t_scalar_t level,t_scalar_t name,uchar_t * ptr)39 rds_opt_default(queue_t *q, t_scalar_t level, t_scalar_t name, uchar_t *ptr)
40 {
41 	/* no default value processed by protocol specific code currently */
42 	return (-1);
43 }
44 
45 /*
46  * This routine retrieves the current status of socket options.
47  * It returns the size of the option retrieved.
48  */
49 int
rds_opt_get(queue_t * q,t_scalar_t level,t_scalar_t name,uchar_t * ptr)50 rds_opt_get(queue_t *q, t_scalar_t level, t_scalar_t name, uchar_t *ptr)
51 {
52 	int	*i1 = (int *)(uintptr_t)ptr;
53 
54 	switch (level) {
55 	case SOL_SOCKET:
56 		switch (name) {
57 		case SO_TYPE:
58 			*i1 = SOCK_DGRAM;
59 			break;	/* goto sizeof (int) option return */
60 
61 		case SO_SNDBUF:
62 			*i1 = q->q_hiwat;
63 			break;	/* goto sizeof (int) option return */
64 		case SO_RCVBUF:
65 			*i1 = RD(q)->q_hiwat;
66 			break;	/* goto sizeof (int) option return */
67 		default:
68 			return (-1);
69 		}
70 		break;
71 	default:
72 		return (-1);
73 	}
74 	return (sizeof (int));
75 }
76 
77 /* This routine sets socket options. */
78 /* ARGSUSED */
79 int
rds_opt_set(queue_t * q,uint_t optset_context,int level,int name,uint_t inlen,uchar_t * invalp,uint_t * outlenp,uchar_t * outvalp,void * thisdg_attrs,cred_t * cr)80 rds_opt_set(queue_t *q, uint_t optset_context, int level,
81     int name, uint_t inlen, uchar_t *invalp, uint_t *outlenp,
82     uchar_t *outvalp, void *thisdg_attrs, cred_t *cr)
83 {
84 	int	*i1 = (int *)(uintptr_t)invalp;
85 	boolean_t checkonly;
86 
87 	switch (optset_context) {
88 	case SETFN_OPTCOM_CHECKONLY:
89 		checkonly = B_TRUE;
90 		/*
91 		 * Note: Implies T_CHECK semantics for T_OPTCOM_REQ
92 		 * inlen != 0 implies value supplied and
93 		 * 	we have to "pretend" to set it.
94 		 * inlen == 0 implies that there is no
95 		 * 	value part in T_CHECK request and just validation
96 		 * done elsewhere should be enough, we just return here.
97 		 */
98 		if (inlen == 0) {
99 			*outlenp = 0;
100 			return (0);
101 		}
102 		break;
103 	case SETFN_OPTCOM_NEGOTIATE:
104 		checkonly = B_FALSE;
105 		break;
106 	default:
107 		/*
108 		 * We should never get here
109 		 */
110 		*outlenp = 0;
111 		return (EINVAL);
112 	}
113 
114 	ASSERT((optset_context != SETFN_OPTCOM_CHECKONLY) ||
115 	    (optset_context == SETFN_OPTCOM_CHECKONLY && inlen != 0));
116 
117 	/*
118 	 * For fixed length options, no sanity check
119 	 * of passed in length is done. It is assumed *_optcom_req()
120 	 * routines do the right thing.
121 	 */
122 
123 	switch (level) {
124 	case SOL_SOCKET:
125 		switch (name) {
126 
127 		case SO_SNDBUF:
128 			if (*i1 > rds_max_buf) {
129 				*outlenp = 0;
130 				return (ENOBUFS);
131 			}
132 			if (!checkonly) {
133 				q->q_hiwat = *i1;
134 			}
135 			break;
136 		case SO_RCVBUF:
137 			if (*i1 > rds_max_buf) {
138 				*outlenp = 0;
139 				return (ENOBUFS);
140 			}
141 			if (!checkonly) {
142 				RD(q)->q_hiwat = *i1;
143 				(void) proto_set_rx_hiwat(RD(q), NULL, *i1);
144 			}
145 			break;
146 		default:
147 			*outlenp = 0;
148 			return (EINVAL);
149 		}
150 		break;
151 	default:
152 		*outlenp = 0;
153 		return (EINVAL);
154 	}
155 	/*
156 	 * Common case of OK return with outval same as inval.
157 	 */
158 	if (invalp != outvalp) {
159 		/* don't trust bcopy for identical src/dst */
160 		(void) bcopy(invalp, outvalp, inlen);
161 	}
162 	*outlenp = inlen;
163 	return (0);
164 }
165 
166 uint_t rds_max_optsize; /* initialized when RDS driver is loaded */
167 
168 #define	RDS_VALID_LEVELS_CNT	A_CNT(rds_valid_levels_arr)
169 
170 #define	RDS_OPT_ARR_CNT		A_CNT(rds_opt_arr)
171 
172 
173 optlevel_t rds_valid_levels_arr[] = {
174 	SOL_SOCKET,
175 };
176 
177 /*
178  * Initialize option database object for RDS
179  *
180  * This object represents database of options to search passed to
181  * {sock,tpi}optcom_req() interface routine to take care of option
182  * management and associated methods.
183  */
184 
185 optdb_obj_t rds_opt_obj = {
186 	rds_opt_default,	/* RDS default value function pointer */
187 	rds_opt_get,		/* RDS get function pointer */
188 	rds_opt_set,		/* RDS set function pointer */
189 	RDS_OPT_ARR_CNT,	/* RDS option database count of entries */
190 	rds_opt_arr,		/* RDS option database */
191 	RDS_VALID_LEVELS_CNT,	/* RDS valid level count of entries */
192 	rds_valid_levels_arr	/* RDS valid level array */
193 };
194