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