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 <inet/tunables.h>
27 #include <sys/md5.h>
28 #include <inet/common.h>
29 #include <inet/ip.h>
30 #include <inet/ip6.h>
31 #include <netinet/icmp6.h>
32 #include <inet/ip_stack.h>
33 #include <inet/rawip_impl.h>
34 #include <inet/tcp_stack.h>
35 #include <inet/tcp_impl.h>
36 #include <inet/udp_impl.h>
37 #include <inet/dccp/dccp_stack.h>
38 #include <inet/dccp/dccp_impl.h>
39 #include <inet/sctp/sctp_stack.h>
40 #include <inet/sctp/sctp_impl.h>
41 #include <inet/tunables.h>
42
43 static int
44 prop_perm2const(mod_prop_info_t *pinfo)
45 {
46 if (pinfo->mpi_setf == NULL)
47 return (MOD_PROP_PERM_READ);
48 if (pinfo->mpi_getf == NULL)
49 return (MOD_PROP_PERM_WRITE);
50 return (MOD_PROP_PERM_RW);
51 }
52
53 /*
54 * Modifies the value of the property to default value or to the `pval'
55 * specified by the user.
56 */
57 /* ARGSUSED */
58 int
59 mod_set_boolean(void *cbarg, cred_t *cr, mod_prop_info_t *pinfo,
60 const char *ifname, const void* pval, uint_t flags)
61 {
62 char *end;
63 unsigned long new_value;
64
65 if (flags & MOD_PROP_DEFAULT) {
66 pinfo->prop_cur_bval = pinfo->prop_def_bval;
67 return (0);
68 }
69
70 if (ddi_strtoul(pval, &end, 10, &new_value) != 0 || *end != '\0')
71 return (EINVAL);
72 if (new_value != B_TRUE && new_value != B_FALSE)
73 return (EINVAL);
74 pinfo->prop_cur_bval = new_value;
75 return (0);
76 }
77
78 /*
79 * Retrieves property permission, default value, current value or possible
80 * values for those properties whose value type is boolean_t.
81 */
82 /* ARGSUSED */
83 int
84 mod_get_boolean(void *cbarg, mod_prop_info_t *pinfo, const char *ifname,
85 void *pval, uint_t psize, uint_t flags)
86 {
87 boolean_t get_def = (flags & MOD_PROP_DEFAULT);
88 boolean_t get_perm = (flags & MOD_PROP_PERM);
89 boolean_t get_range = (flags & MOD_PROP_POSSIBLE);
90 size_t nbytes;
91
92 bzero(pval, psize);
93 if (get_perm)
94 nbytes = snprintf(pval, psize, "%u", prop_perm2const(pinfo));
95 else if (get_range)
96 nbytes = snprintf(pval, psize, "%u,%u", B_FALSE, B_TRUE);
97 else if (get_def)
98 nbytes = snprintf(pval, psize, "%u", pinfo->prop_def_bval);
99 else
100 nbytes = snprintf(pval, psize, "%u", pinfo->prop_cur_bval);
101 if (nbytes >= psize)
102 return (ENOBUFS);
103 return (0);
104 }
105
106 int
107 mod_uint32_value(const void *pval, mod_prop_info_t *pinfo, uint_t flags,
108 ulong_t *new_value)
109 {
110 char *end;
111
112 if (flags & MOD_PROP_DEFAULT) {
113 *new_value = pinfo->prop_def_uval;
114 return (0);
115 }
116
117 if (ddi_strtoul(pval, &end, 10, (ulong_t *)new_value) != 0 ||
118 *end != '\0')
119 return (EINVAL);
120 if (*new_value < pinfo->prop_min_uval ||
121 *new_value > pinfo->prop_max_uval) {
122 return (ERANGE);
123 }
124 return (0);
125 }
126
127 /*
128 * Modifies the value of the property to default value or to the `pval'
129 * specified by the user.
130 */
131 /* ARGSUSED */
132 int
133 mod_set_uint32(void *cbarg, cred_t *cr, mod_prop_info_t *pinfo,
134 const char *ifname, const void *pval, uint_t flags)
135 {
136 unsigned long new_value;
137 int err;
138
139 if ((err = mod_uint32_value(pval, pinfo, flags, &new_value)) != 0)
140 return (err);
141 pinfo->prop_cur_uval = (uint32_t)new_value;
142 return (0);
143 }
144
145 /*
146 * Rounds up the value to make it multiple of 8.
147 */
148 /* ARGSUSED */
149 int
150 mod_set_aligned(void *cbarg, cred_t *cr, mod_prop_info_t *pinfo,
151 const char *ifname, const void* pval, uint_t flags)
152 {
153 int err;
154
155 if ((err = mod_set_uint32(cbarg, cr, pinfo, ifname, pval, flags)) != 0)
156 return (err);
157
158 /* if required, align the value to multiple of 8 */
159 if (pinfo->prop_cur_uval & 0x7) {
160 pinfo->prop_cur_uval &= ~0x7;
161 pinfo->prop_cur_uval += 0x8;
162 }
163
164 return (0);
165 }
166
167 /*
168 * Retrieves property permission, default value, current value or possible
169 * values for those properties whose value type is uint32_t.
170 */
171 /* ARGSUSED */
172 int
173 mod_get_uint32(void *cbarg, mod_prop_info_t *pinfo, const char *ifname,
174 void *pval, uint_t psize, uint_t flags)
175 {
176 boolean_t get_def = (flags & MOD_PROP_DEFAULT);
177 boolean_t get_perm = (flags & MOD_PROP_PERM);
178 boolean_t get_range = (flags & MOD_PROP_POSSIBLE);
179 size_t nbytes;
180
181 bzero(pval, psize);
182 if (get_perm)
183 nbytes = snprintf(pval, psize, "%u", prop_perm2const(pinfo));
184 else if (get_range)
185 nbytes = snprintf(pval, psize, "%u-%u",
186 pinfo->prop_min_uval, pinfo->prop_max_uval);
187 else if (get_def)
188 nbytes = snprintf(pval, psize, "%u", pinfo->prop_def_uval);
189 else
190 nbytes = snprintf(pval, psize, "%u", pinfo->prop_cur_uval);
191 if (nbytes >= psize)
192 return (ENOBUFS);
193 return (0);
194 }
195
196 /*
197 * Implements /sbin/ndd -get /dev/ip ?, for all the modules. Needed for
198 * backward compatibility with /sbin/ndd.
199 */
200 /* ARGSUSED */
201 int
202 mod_get_allprop(void *cbarg, mod_prop_info_t *pinfo, const char *ifname,
203 void *val, uint_t psize, uint_t flags)
204 {
205 char *pval = val;
206 mod_prop_info_t *ptbl, *prop;
207 ip_stack_t *ipst;
208 tcp_stack_t *tcps;
209 sctp_stack_t *sctps;
210 dccp_stack_t *dccps;
211 udp_stack_t *us;
212 icmp_stack_t *is;
213 uint_t size;
214 size_t nbytes = 0, tbytes = 0;
215
216 bzero(pval, psize);
217 size = psize;
218
219 switch (pinfo->mpi_proto) {
220 case MOD_PROTO_IP:
221 case MOD_PROTO_IPV4:
222 case MOD_PROTO_IPV6:
223 ipst = (ip_stack_t *)cbarg;
224 ptbl = ipst->ips_propinfo_tbl;
225 break;
226 case MOD_PROTO_RAWIP:
227 is = (icmp_stack_t *)cbarg;
228 ptbl = is->is_propinfo_tbl;
229 break;
230 case MOD_PROTO_TCP:
231 tcps = (tcp_stack_t *)cbarg;
232 ptbl = tcps->tcps_propinfo_tbl;
233 break;
234 case MOD_PROTO_UDP:
235 us = (udp_stack_t *)cbarg;
236 ptbl = us->us_propinfo_tbl;
237 break;
238 case MOD_PROTO_SCTP:
239 sctps = (sctp_stack_t *)cbarg;
240 ptbl = sctps->sctps_propinfo_tbl;
241 break;
242 case MOD_PROTO_DCCP:
243 dccps = (dccp_stack_t *)cbarg;
244 ptbl = dccps->dccps_propinfo_tbl;
245 break;
246 default:
247 return (EINVAL);
248 }
249
250 for (prop = ptbl; prop->mpi_name != NULL; prop++) {
251 if (prop->mpi_name[0] == '\0' ||
252 strcmp(prop->mpi_name, "?") == 0) {
253 continue;
254 }
255 nbytes = snprintf(pval, size, "%s %d %d", prop->mpi_name,
256 prop->mpi_proto, prop_perm2const(prop));
257 size -= nbytes + 1;
258 pval += nbytes + 1;
259 tbytes += nbytes + 1;
260 if (tbytes >= psize) {
261 /* Buffer overflow, stop copying information */
262 return (ENOBUFS);
263 }
264 }
265 return (0);
266 }
267
268 /*
269 * Hold a lock while changing *_epriv_ports to prevent multiple
270 * threads from changing it at the same time.
271 */
272 /* ARGSUSED */
273 int
274 mod_set_extra_privports(void *cbarg, cred_t *cr, mod_prop_info_t *pinfo,
275 const char *ifname, const void* val, uint_t flags)
276 {
277 uint_t proto = pinfo->mpi_proto;
278 tcp_stack_t *tcps;
279 sctp_stack_t *sctps;
280 udp_stack_t *us;
281 unsigned long new_value;
282 char *end;
283 kmutex_t *lock;
284 uint_t i, nports;
285 in_port_t *ports;
286 boolean_t def = (flags & MOD_PROP_DEFAULT);
287 const char *pval = val;
288
289 if (!def) {
290 if (ddi_strtoul(pval, &end, 10, &new_value) != 0 ||
291 *end != '\0') {
292 return (EINVAL);
293 }
294
295 if (new_value < pinfo->prop_min_uval ||
296 new_value > pinfo->prop_max_uval) {
297 return (ERANGE);
298 }
299 }
300
301 switch (proto) {
302 case MOD_PROTO_TCP:
303 tcps = (tcp_stack_t *)cbarg;
304 lock = &tcps->tcps_epriv_port_lock;
305 ports = tcps->tcps_g_epriv_ports;
306 nports = tcps->tcps_g_num_epriv_ports;
307 break;
308 case MOD_PROTO_UDP:
309 us = (udp_stack_t *)cbarg;
310 lock = &us->us_epriv_port_lock;
311 ports = us->us_epriv_ports;
312 nports = us->us_num_epriv_ports;
313 break;
314 case MOD_PROTO_SCTP:
315 sctps = (sctp_stack_t *)cbarg;
316 lock = &sctps->sctps_epriv_port_lock;
317 ports = sctps->sctps_g_epriv_ports;
318 nports = sctps->sctps_g_num_epriv_ports;
319 break;
320 default:
321 return (ENOTSUP);
322 }
323
324 mutex_enter(lock);
325
326 /* if MOD_PROP_DEFAULT is set then reset the ports list to default */
327 if (def) {
328 for (i = 0; i < nports; i++)
329 ports[i] = 0;
330 ports[0] = ULP_DEF_EPRIV_PORT1;
331 ports[1] = ULP_DEF_EPRIV_PORT2;
332 mutex_exit(lock);
333 return (0);
334 }
335
336 /* Check if the value is already in the list */
337 for (i = 0; i < nports; i++) {
338 if (new_value == ports[i])
339 break;
340 }
341
342 if (flags & MOD_PROP_REMOVE) {
343 if (i == nports) {
344 mutex_exit(lock);
345 return (ESRCH);
346 }
347 /* Clear the value */
348 ports[i] = 0;
349 } else if (flags & MOD_PROP_APPEND) {
350 if (i != nports) {
351 mutex_exit(lock);
352 return (EEXIST);
353 }
354
355 /* Find an empty slot */
356 for (i = 0; i < nports; i++) {
357 if (ports[i] == 0)
358 break;
359 }
360 if (i == nports) {
361 mutex_exit(lock);
362 return (EOVERFLOW);
363 }
364 /* Set the new value */
365 ports[i] = (in_port_t)new_value;
366 } else {
367 /*
368 * If the user used 'assignment' modifier.
369 * For eg:
370 * # ipadm set-prop -p extra_priv_ports=3001 tcp
371 *
372 * We clear all the ports and then just add 3001.
373 */
374 ASSERT(flags == MOD_PROP_ACTIVE);
375 for (i = 0; i < nports; i++)
376 ports[i] = 0;
377 ports[0] = (in_port_t)new_value;
378 }
379
380 mutex_exit(lock);
381 return (0);
382 }
383
384 /*
385 * Note: No locks are held when inspecting *_epriv_ports
386 * but instead the code relies on:
387 * - the fact that the address of the array and its size never changes
388 * - the atomic assignment of the elements of the array
389 */
390 /* ARGSUSED */
391 int
392 mod_get_extra_privports(void *cbarg, mod_prop_info_t *pinfo, const char *ifname,
393 void *val, uint_t psize, uint_t flags)
394 {
395 uint_t proto = pinfo->mpi_proto;
396 tcp_stack_t *tcps;
397 sctp_stack_t *sctps;
398 udp_stack_t *us;
399 uint_t i, nports, size;
400 in_port_t *ports;
401 char *pval = val;
402 size_t nbytes = 0, tbytes = 0;
403 boolean_t get_def = (flags & MOD_PROP_DEFAULT);
404 boolean_t get_perm = (flags & MOD_PROP_PERM);
405 boolean_t get_range = (flags & MOD_PROP_POSSIBLE);
406
407 bzero(pval, psize);
408 size = psize;
409
410 if (get_def) {
411 tbytes = snprintf(pval, psize, "%u,%u", ULP_DEF_EPRIV_PORT1,
412 ULP_DEF_EPRIV_PORT2);
413 goto ret;
414 } else if (get_perm) {
415 tbytes = snprintf(pval, psize, "%u", MOD_PROP_PERM_RW);
416 goto ret;
417 }
418
419 switch (proto) {
420 case MOD_PROTO_TCP:
421 tcps = (tcp_stack_t *)cbarg;
422 ports = tcps->tcps_g_epriv_ports;
423 nports = tcps->tcps_g_num_epriv_ports;
424 break;
425 case MOD_PROTO_UDP:
426 us = (udp_stack_t *)cbarg;
427 ports = us->us_epriv_ports;
428 nports = us->us_num_epriv_ports;
429 break;
430 case MOD_PROTO_SCTP:
431 sctps = (sctp_stack_t *)cbarg;
432 ports = sctps->sctps_g_epriv_ports;
433 nports = sctps->sctps_g_num_epriv_ports;
434 break;
435 default:
436 return (ENOTSUP);
437 }
438
439 if (get_range) {
440 tbytes = snprintf(pval, psize, "%u-%u", pinfo->prop_min_uval,
441 pinfo->prop_max_uval);
442 goto ret;
443 }
444
445 for (i = 0; i < nports; i++) {
446 if (ports[i] != 0) {
447 if (psize == size)
448 nbytes = snprintf(pval, size, "%u", ports[i]);
449 else
450 nbytes = snprintf(pval, size, ",%u", ports[i]);
451 size -= nbytes;
452 pval += nbytes;
453 tbytes += nbytes;
454 if (tbytes >= psize)
455 return (ENOBUFS);
456 }
457 }
458 return (0);
459 ret:
460 if (tbytes >= psize)
461 return (ENOBUFS);
462 return (0);
463 }