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
42mod_prop_info_t *
43mod_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
60static int
61prop_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 */
75int
76mod_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 */
100int
101mod_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
123int
124mod_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 */
149int
150mod_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 */
166int
167mod_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 */
189int
190mod_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 */
219static void
220mod_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 */
237int
238mod_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 */
267int
268mod_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 */
288int
289mod_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 */
345int
346mod_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 */
463int
464mod_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);
531ret:
532	if (tbytes >= psize)
533		return (ENOBUFS);
534	return (0);
535}
536