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