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