Print this page
3942 inject sanity into ipadm tcp buffer size properties
3943 _snd_lowat_fraction tcp tunable has no effect
Reviewed by: Adam Leventhal <ahl@delphix.com>
Reviewed by: Peng Dai <peng.dai@delphix.com>
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/lib/libipadm/common/ipadm_prop.c
+++ new/usr/src/lib/libipadm/common/ipadm_prop.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
12 12 *
↓ open down ↓ |
12 lines elided |
↑ open up ↑ |
13 13 * When distributing Covered Code, include this CDDL HEADER in each
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 15 * If applicable, add the following below this CDDL HEADER, with the
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
19 19 * CDDL HEADER END
20 20 */
21 21 /*
22 22 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
23 + * Copyright (c) 2013 by Delphix. All rights reserved.
23 24 */
24 25
25 26 /*
26 27 * This file contains routines that are used to modify/retrieve protocol or
27 28 * interface property values. It also holds all the supported properties for
28 29 * both IP interface and protocols in `ipadm_prop_desc_t'. Following protocols
29 30 * are supported: IP, IPv4, IPv6, TCP, SCTP, UDP and ICMP.
30 31 *
31 32 * This file also contains walkers, which walks through the property table and
32 33 * calls the callback function, of the form `ipadm_prop_wfunc_t' , for every
33 34 * property in the table.
34 35 */
35 36
36 37 #include <unistd.h>
37 38 #include <errno.h>
38 39 #include <ctype.h>
39 40 #include <fcntl.h>
40 41 #include <strings.h>
41 42 #include <stdlib.h>
42 43 #include <netinet/in.h>
43 44 #include <arpa/inet.h>
44 45 #include <sys/sockio.h>
45 46 #include <assert.h>
46 47 #include <libdllink.h>
47 48 #include <zone.h>
48 49 #include "libipadm_impl.h"
49 50 #include <inet/tunables.h>
50 51
51 52 #define IPADM_NONESTR "none"
52 53 #define DEF_METRIC_VAL 0 /* default metric value */
53 54
54 55 #define A_CNT(arr) (sizeof (arr) / sizeof (arr[0]))
55 56
56 57 static ipadm_status_t i_ipadm_validate_if(ipadm_handle_t, const char *,
57 58 uint_t, uint_t);
58 59
59 60 /*
60 61 * Callback functions to retrieve property values from the kernel. These
61 62 * functions, when required, translate the values from the kernel to a format
62 63 * suitable for printing. For example: boolean values will be translated
63 64 * to on/off. They also retrieve DEFAULT, PERM and POSSIBLE values for
64 65 * a given property.
65 66 */
66 67 static ipadm_pd_getf_t i_ipadm_get_prop, i_ipadm_get_ifprop_flags,
67 68 i_ipadm_get_mtu, i_ipadm_get_metric,
68 69 i_ipadm_get_usesrc, i_ipadm_get_forwarding,
69 70 i_ipadm_get_ecnsack, i_ipadm_get_hostmodel;
70 71
71 72 /*
72 73 * Callback function to set property values. These functions translate the
73 74 * values to a format suitable for kernel consumption, allocates the necessary
74 75 * ioctl buffers and then invokes ioctl().
75 76 */
76 77 static ipadm_pd_setf_t i_ipadm_set_prop, i_ipadm_set_mtu,
77 78 i_ipadm_set_ifprop_flags,
78 79 i_ipadm_set_metric, i_ipadm_set_usesrc,
79 80 i_ipadm_set_forwarding, i_ipadm_set_eprivport,
80 81 i_ipadm_set_ecnsack, i_ipadm_set_hostmodel;
↓ open down ↓ |
48 lines elided |
↑ open up ↑ |
81 82
82 83 /* array of protocols we support */
83 84 static int protocols[] = { MOD_PROTO_IP, MOD_PROTO_RAWIP,
84 85 MOD_PROTO_TCP, MOD_PROTO_UDP,
85 86 MOD_PROTO_SCTP };
86 87
87 88 /*
88 89 * Supported IP protocol properties.
89 90 */
90 91 static ipadm_prop_desc_t ipadm_ip_prop_table[] = {
91 - { "arp", IPADMPROP_CLASS_IF, MOD_PROTO_IPV4, 0,
92 + { "arp", NULL, IPADMPROP_CLASS_IF, MOD_PROTO_IPV4, 0,
92 93 i_ipadm_set_ifprop_flags, i_ipadm_get_onoff,
93 94 i_ipadm_get_ifprop_flags },
94 95
95 - { "forwarding", IPADMPROP_CLASS_MODIF, MOD_PROTO_IPV4, 0,
96 + { "forwarding", NULL, IPADMPROP_CLASS_MODIF, MOD_PROTO_IPV4, 0,
96 97 i_ipadm_set_forwarding, i_ipadm_get_onoff,
97 98 i_ipadm_get_forwarding },
98 99
99 - { "metric", IPADMPROP_CLASS_IF, MOD_PROTO_IPV4, 0,
100 + { "metric", NULL, IPADMPROP_CLASS_IF, MOD_PROTO_IPV4, 0,
100 101 i_ipadm_set_metric, NULL, i_ipadm_get_metric },
101 102
102 - { "mtu", IPADMPROP_CLASS_IF, MOD_PROTO_IPV4, 0,
103 + { "mtu", NULL, IPADMPROP_CLASS_IF, MOD_PROTO_IPV4, 0,
103 104 i_ipadm_set_mtu, i_ipadm_get_mtu, i_ipadm_get_mtu },
104 105
105 - { "exchange_routes", IPADMPROP_CLASS_IF, MOD_PROTO_IPV4, 0,
106 + { "exchange_routes", NULL, IPADMPROP_CLASS_IF, MOD_PROTO_IPV4, 0,
106 107 i_ipadm_set_ifprop_flags, i_ipadm_get_onoff,
107 108 i_ipadm_get_ifprop_flags },
108 109
109 - { "usesrc", IPADMPROP_CLASS_IF, MOD_PROTO_IPV4, 0,
110 + { "usesrc", NULL, IPADMPROP_CLASS_IF, MOD_PROTO_IPV4, 0,
110 111 i_ipadm_set_usesrc, NULL, i_ipadm_get_usesrc },
111 112
112 - { "ttl", IPADMPROP_CLASS_MODULE, MOD_PROTO_IPV4, 0,
113 + { "ttl", NULL, IPADMPROP_CLASS_MODULE, MOD_PROTO_IPV4, 0,
113 114 i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
114 115
115 - { "forwarding", IPADMPROP_CLASS_MODIF, MOD_PROTO_IPV6, 0,
116 + { "forwarding", NULL, IPADMPROP_CLASS_MODIF, MOD_PROTO_IPV6, 0,
116 117 i_ipadm_set_forwarding, i_ipadm_get_onoff,
117 118 i_ipadm_get_forwarding },
118 119
119 - { "hoplimit", IPADMPROP_CLASS_MODULE, MOD_PROTO_IPV6, 0,
120 + { "hoplimit", NULL, IPADMPROP_CLASS_MODULE, MOD_PROTO_IPV6, 0,
120 121 i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
121 122
122 - { "metric", IPADMPROP_CLASS_IF, MOD_PROTO_IPV6, 0,
123 + { "metric", NULL, IPADMPROP_CLASS_IF, MOD_PROTO_IPV6, 0,
123 124 i_ipadm_set_metric, NULL, i_ipadm_get_metric },
124 125
125 - { "mtu", IPADMPROP_CLASS_IF, MOD_PROTO_IPV6, 0,
126 + { "mtu", NULL, IPADMPROP_CLASS_IF, MOD_PROTO_IPV6, 0,
126 127 i_ipadm_set_mtu, i_ipadm_get_mtu, i_ipadm_get_mtu },
127 128
128 - { "nud", IPADMPROP_CLASS_IF, MOD_PROTO_IPV6, 0,
129 + { "nud", NULL, IPADMPROP_CLASS_IF, MOD_PROTO_IPV6, 0,
129 130 i_ipadm_set_ifprop_flags, i_ipadm_get_onoff,
130 131 i_ipadm_get_ifprop_flags },
131 132
132 - { "exchange_routes", IPADMPROP_CLASS_IF, MOD_PROTO_IPV6, 0,
133 + { "exchange_routes", NULL, IPADMPROP_CLASS_IF, MOD_PROTO_IPV6, 0,
133 134 i_ipadm_set_ifprop_flags, i_ipadm_get_onoff,
134 135 i_ipadm_get_ifprop_flags },
135 136
136 - { "usesrc", IPADMPROP_CLASS_IF, MOD_PROTO_IPV6, 0,
137 + { "usesrc", NULL, IPADMPROP_CLASS_IF, MOD_PROTO_IPV6, 0,
137 138 i_ipadm_set_usesrc, NULL, i_ipadm_get_usesrc },
138 139
139 - { "hostmodel", IPADMPROP_CLASS_MODULE, MOD_PROTO_IPV6, 0,
140 + { "hostmodel", NULL, IPADMPROP_CLASS_MODULE, MOD_PROTO_IPV6, 0,
140 141 i_ipadm_set_hostmodel, i_ipadm_get_hostmodel,
141 142 i_ipadm_get_hostmodel },
142 143
143 - { "hostmodel", IPADMPROP_CLASS_MODULE, MOD_PROTO_IPV4, 0,
144 + { "hostmodel", NULL, IPADMPROP_CLASS_MODULE, MOD_PROTO_IPV4, 0,
144 145 i_ipadm_set_hostmodel, i_ipadm_get_hostmodel,
145 146 i_ipadm_get_hostmodel },
146 147
147 - { NULL, 0, 0, 0, NULL, NULL, NULL }
148 + { NULL, NULL, 0, 0, 0, NULL, NULL, NULL }
148 149 };
149 150
150 151 /* possible values for TCP properties `ecn' and `sack' */
151 152 static const char *ecn_sack_vals[] = {"never", "passive", "active", NULL};
152 153
153 154 /* Supported TCP protocol properties */
154 155 static ipadm_prop_desc_t ipadm_tcp_prop_table[] = {
155 - { "ecn", IPADMPROP_CLASS_MODULE, MOD_PROTO_TCP, 0,
156 + { "ecn", NULL, IPADMPROP_CLASS_MODULE, MOD_PROTO_TCP, 0,
156 157 i_ipadm_set_ecnsack, i_ipadm_get_ecnsack, i_ipadm_get_ecnsack },
157 158
158 - { "extra_priv_ports", IPADMPROP_CLASS_MODULE, MOD_PROTO_TCP,
159 + { "extra_priv_ports", NULL, IPADMPROP_CLASS_MODULE, MOD_PROTO_TCP,
159 160 IPADMPROP_MULVAL, i_ipadm_set_eprivport, i_ipadm_get_prop,
160 161 i_ipadm_get_prop },
161 162
162 - { "largest_anon_port", IPADMPROP_CLASS_MODULE, MOD_PROTO_TCP, 0,
163 + { "largest_anon_port", NULL, IPADMPROP_CLASS_MODULE, MOD_PROTO_TCP, 0,
163 164 i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
164 165
165 - { "recv_maxbuf", IPADMPROP_CLASS_MODULE, MOD_PROTO_TCP, 0,
166 + { "max_buf", "_max_buf", IPADMPROP_CLASS_MODULE, MOD_PROTO_TCP, 0,
166 167 i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
167 168
168 - { "sack", IPADMPROP_CLASS_MODULE, MOD_PROTO_TCP, 0,
169 + { "recv_buf", "recv_maxbuf", IPADMPROP_CLASS_MODULE, MOD_PROTO_TCP, 0,
170 + i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
171 +
172 + { "sack", NULL, IPADMPROP_CLASS_MODULE, MOD_PROTO_TCP, 0,
169 173 i_ipadm_set_ecnsack, i_ipadm_get_ecnsack, i_ipadm_get_ecnsack },
170 174
171 - { "send_maxbuf", IPADMPROP_CLASS_MODULE, MOD_PROTO_TCP, 0,
175 + { "send_buf", "send_maxbuf", IPADMPROP_CLASS_MODULE, MOD_PROTO_TCP, 0,
172 176 i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
173 177
174 - { "smallest_anon_port", IPADMPROP_CLASS_MODULE, MOD_PROTO_TCP, 0,
178 + { "smallest_anon_port", NULL, IPADMPROP_CLASS_MODULE, MOD_PROTO_TCP, 0,
175 179 i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
176 180
177 - { "smallest_nonpriv_port", IPADMPROP_CLASS_MODULE, MOD_PROTO_TCP, 0,
178 - i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
181 + { "smallest_nonpriv_port", NULL, IPADMPROP_CLASS_MODULE, MOD_PROTO_TCP,
182 + 0, i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
179 183
180 - { NULL, 0, 0, 0, NULL, NULL, NULL }
184 + { NULL, NULL, 0, 0, 0, NULL, NULL, NULL }
181 185 };
182 186
183 187 /* Supported UDP protocol properties */
184 188 static ipadm_prop_desc_t ipadm_udp_prop_table[] = {
185 - { "extra_priv_ports", IPADMPROP_CLASS_MODULE, MOD_PROTO_UDP,
189 + { "extra_priv_ports", NULL, IPADMPROP_CLASS_MODULE, MOD_PROTO_UDP,
186 190 IPADMPROP_MULVAL, i_ipadm_set_eprivport, i_ipadm_get_prop,
187 191 i_ipadm_get_prop },
188 192
189 - { "largest_anon_port", IPADMPROP_CLASS_MODULE, MOD_PROTO_UDP, 0,
193 + { "largest_anon_port", NULL, IPADMPROP_CLASS_MODULE, MOD_PROTO_UDP, 0,
190 194 i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
191 195
192 - { "recv_maxbuf", IPADMPROP_CLASS_MODULE, MOD_PROTO_UDP, 0,
196 + { "max_buf", "_max_buf", IPADMPROP_CLASS_MODULE, MOD_PROTO_UDP, 0,
193 197 i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
194 198
195 - { "send_maxbuf", IPADMPROP_CLASS_MODULE, MOD_PROTO_UDP, 0,
199 + { "recv_buf", "recv_maxbuf", IPADMPROP_CLASS_MODULE, MOD_PROTO_UDP, 0,
196 200 i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
197 201
198 - { "smallest_anon_port", IPADMPROP_CLASS_MODULE, MOD_PROTO_UDP, 0,
202 + { "send_buf", "send_maxbuf", IPADMPROP_CLASS_MODULE, MOD_PROTO_UDP, 0,
199 203 i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
200 204
201 - { "smallest_nonpriv_port", IPADMPROP_CLASS_MODULE, MOD_PROTO_UDP, 0,
205 + { "smallest_anon_port", NULL, IPADMPROP_CLASS_MODULE, MOD_PROTO_UDP, 0,
202 206 i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
203 207
204 - { NULL, 0, 0, 0, NULL, NULL, NULL }
208 + { "smallest_nonpriv_port", NULL, IPADMPROP_CLASS_MODULE, MOD_PROTO_UDP,
209 + 0, i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
210 +
211 + { NULL, NULL, 0, 0, 0, NULL, NULL, NULL }
205 212 };
206 213
207 214 /* Supported SCTP protocol properties */
208 215 static ipadm_prop_desc_t ipadm_sctp_prop_table[] = {
209 - { "extra_priv_ports", IPADMPROP_CLASS_MODULE, MOD_PROTO_SCTP,
216 + { "extra_priv_ports", NULL, IPADMPROP_CLASS_MODULE, MOD_PROTO_SCTP,
210 217 IPADMPROP_MULVAL, i_ipadm_set_eprivport, i_ipadm_get_prop,
211 218 i_ipadm_get_prop },
212 219
213 - { "largest_anon_port", IPADMPROP_CLASS_MODULE, MOD_PROTO_SCTP, 0,
220 + { "largest_anon_port", NULL, IPADMPROP_CLASS_MODULE, MOD_PROTO_SCTP, 0,
214 221 i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
215 222
216 - { "recv_maxbuf", IPADMPROP_CLASS_MODULE, MOD_PROTO_SCTP, 0,
223 + { "max_buf", "_max_buf", IPADMPROP_CLASS_MODULE, MOD_PROTO_SCTP, 0,
217 224 i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
218 225
219 - { "send_maxbuf", IPADMPROP_CLASS_MODULE, MOD_PROTO_SCTP, 0,
226 + { "recv_buf", "recv_maxbuf", IPADMPROP_CLASS_MODULE, MOD_PROTO_SCTP, 0,
220 227 i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
221 228
222 - { "smallest_anon_port", IPADMPROP_CLASS_MODULE, MOD_PROTO_SCTP, 0,
229 + { "send_buf", "send_maxbuf", IPADMPROP_CLASS_MODULE, MOD_PROTO_SCTP, 0,
223 230 i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
224 231
225 - { "smallest_nonpriv_port", IPADMPROP_CLASS_MODULE, MOD_PROTO_SCTP, 0,
232 + { "smallest_anon_port", NULL, IPADMPROP_CLASS_MODULE, MOD_PROTO_SCTP, 0,
226 233 i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
227 234
228 - { NULL, 0, 0, 0, NULL, NULL, NULL }
235 + { "smallest_nonpriv_port", NULL, IPADMPROP_CLASS_MODULE, MOD_PROTO_SCTP,
236 + 0, i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
237 +
238 + { NULL, NULL, 0, 0, 0, NULL, NULL, NULL }
229 239 };
230 240
231 241 /* Supported ICMP protocol properties */
232 242 static ipadm_prop_desc_t ipadm_icmp_prop_table[] = {
233 - { "recv_maxbuf", IPADMPROP_CLASS_MODULE, MOD_PROTO_RAWIP, 0,
243 + { "max_buf", "_max_buf", IPADMPROP_CLASS_MODULE, MOD_PROTO_RAWIP, 0,
234 244 i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
235 245
236 - { "send_maxbuf", IPADMPROP_CLASS_MODULE, MOD_PROTO_RAWIP, 0,
246 + { "recv_buf", "recv_maxbuf", IPADMPROP_CLASS_MODULE, MOD_PROTO_RAWIP, 0,
237 247 i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
238 248
239 - { NULL, 0, 0, 0, NULL, NULL, NULL }
249 + { "send_buf", "send_maxbuf", IPADMPROP_CLASS_MODULE, MOD_PROTO_RAWIP, 0,
250 + i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop },
251 +
252 + { NULL, NULL, 0, 0, 0, NULL, NULL, NULL }
240 253 };
241 254
242 255 /*
243 256 * A dummy private property structure, used while handling private
244 257 * protocol properties (properties not yet supported by libipadm).
245 258 */
246 -static ipadm_prop_desc_t ipadm_privprop =\
247 - { NULL, IPADMPROP_CLASS_MODULE, MOD_PROTO_NONE, 0,
259 +static ipadm_prop_desc_t ipadm_privprop =
260 + { NULL, NULL, IPADMPROP_CLASS_MODULE, MOD_PROTO_NONE, 0,
248 261 i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop };
249 262
250 263 /*
251 264 * Returns the property description table, for the given protocol
252 265 */
253 266 static ipadm_prop_desc_t *
254 267 i_ipadm_get_propdesc_table(uint_t proto)
255 268 {
256 269 switch (proto) {
257 270 case MOD_PROTO_IP:
258 271 case MOD_PROTO_IPV4:
259 272 case MOD_PROTO_IPV6:
260 273 return (ipadm_ip_prop_table);
261 274 case MOD_PROTO_RAWIP:
262 275 return (ipadm_icmp_prop_table);
263 276 case MOD_PROTO_TCP:
264 277 return (ipadm_tcp_prop_table);
265 278 case MOD_PROTO_UDP:
266 279 return (ipadm_udp_prop_table);
267 280 case MOD_PROTO_SCTP:
268 281 return (ipadm_sctp_prop_table);
269 282 }
270 283
271 284 return (NULL);
272 285 }
273 286
274 287 static ipadm_prop_desc_t *
↓ open down ↓ |
17 lines elided |
↑ open up ↑ |
275 288 i_ipadm_get_prop_desc(const char *pname, uint_t proto, int *errp)
276 289 {
277 290 int err = 0;
278 291 boolean_t matched_name = B_FALSE;
279 292 ipadm_prop_desc_t *ipdp = NULL, *ipdtbl;
280 293
281 294 if ((ipdtbl = i_ipadm_get_propdesc_table(proto)) == NULL) {
282 295 err = EINVAL;
283 296 goto ret;
284 297 }
298 +
285 299 for (ipdp = ipdtbl; ipdp->ipd_name != NULL; ipdp++) {
286 - if (strcmp(pname, ipdp->ipd_name) == 0) {
300 + if (strcmp(pname, ipdp->ipd_name) == 0 ||
301 + (ipdp->ipd_old_name != NULL &&
302 + strcmp(pname, ipdp->ipd_old_name) == 0)) {
287 303 matched_name = B_TRUE;
288 304 if (ipdp->ipd_proto == proto)
289 305 break;
290 306 }
291 307 }
308 +
292 309 if (ipdp->ipd_name == NULL) {
293 310 err = ENOENT;
294 311 /* if we matched name, but failed protocol check */
295 312 if (matched_name)
296 313 err = EPROTO;
297 314 ipdp = NULL;
298 315 }
299 316 ret:
300 317 if (errp != NULL)
301 318 *errp = err;
302 319 return (ipdp);
303 320 }
304 321
305 322 char *
306 323 ipadm_proto2str(uint_t proto)
307 324 {
308 325 switch (proto) {
309 326 case MOD_PROTO_IP:
310 327 return ("ip");
311 328 case MOD_PROTO_IPV4:
312 329 return ("ipv4");
313 330 case MOD_PROTO_IPV6:
314 331 return ("ipv6");
315 332 case MOD_PROTO_RAWIP:
316 333 return ("icmp");
317 334 case MOD_PROTO_TCP:
318 335 return ("tcp");
319 336 case MOD_PROTO_UDP:
320 337 return ("udp");
321 338 case MOD_PROTO_SCTP:
322 339 return ("sctp");
323 340 }
324 341
325 342 return (NULL);
326 343 }
327 344
328 345 uint_t
329 346 ipadm_str2proto(const char *protostr)
330 347 {
331 348 if (protostr == NULL)
332 349 return (MOD_PROTO_NONE);
333 350 if (strcmp(protostr, "tcp") == 0)
334 351 return (MOD_PROTO_TCP);
335 352 else if (strcmp(protostr, "udp") == 0)
336 353 return (MOD_PROTO_UDP);
337 354 else if (strcmp(protostr, "ip") == 0)
338 355 return (MOD_PROTO_IP);
339 356 else if (strcmp(protostr, "ipv4") == 0)
340 357 return (MOD_PROTO_IPV4);
341 358 else if (strcmp(protostr, "ipv6") == 0)
342 359 return (MOD_PROTO_IPV6);
343 360 else if (strcmp(protostr, "icmp") == 0)
344 361 return (MOD_PROTO_RAWIP);
345 362 else if (strcmp(protostr, "sctp") == 0)
346 363 return (MOD_PROTO_SCTP);
347 364 else if (strcmp(protostr, "arp") == 0)
348 365 return (MOD_PROTO_IP);
349 366
350 367 return (MOD_PROTO_NONE);
351 368 }
352 369
353 370 /* ARGSUSED */
354 371 static ipadm_status_t
355 372 i_ipadm_set_mtu(ipadm_handle_t iph, const void *arg,
356 373 ipadm_prop_desc_t *pdp, const void *pval, uint_t proto, uint_t flags)
357 374 {
358 375 struct lifreq lifr;
359 376 char *endp;
360 377 uint_t mtu;
361 378 int s;
362 379 const char *ifname = arg;
363 380 char val[MAXPROPVALLEN];
364 381
365 382 /* to reset MTU first retrieve the default MTU and then set it */
366 383 if (flags & IPADM_OPT_DEFAULT) {
367 384 ipadm_status_t status;
368 385 uint_t size = MAXPROPVALLEN;
369 386
370 387 status = i_ipadm_get_prop(iph, arg, pdp, val, &size,
371 388 proto, MOD_PROP_DEFAULT);
372 389 if (status != IPADM_SUCCESS)
373 390 return (status);
374 391 pval = val;
375 392 }
376 393
377 394 errno = 0;
378 395 mtu = (uint_t)strtol(pval, &endp, 10);
379 396 if (errno != 0 || *endp != '\0')
380 397 return (IPADM_INVALID_ARG);
381 398
382 399 bzero(&lifr, sizeof (lifr));
383 400 (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
384 401 lifr.lifr_mtu = mtu;
385 402
386 403 s = (proto == MOD_PROTO_IPV6 ? iph->iph_sock6 : iph->iph_sock);
387 404 if (ioctl(s, SIOCSLIFMTU, (caddr_t)&lifr) < 0)
388 405 return (ipadm_errno2status(errno));
389 406
390 407 return (IPADM_SUCCESS);
391 408 }
392 409
393 410 /* ARGSUSED */
394 411 static ipadm_status_t
395 412 i_ipadm_set_metric(ipadm_handle_t iph, const void *arg,
396 413 ipadm_prop_desc_t *pdp, const void *pval, uint_t proto, uint_t flags)
397 414 {
398 415 struct lifreq lifr;
399 416 char *endp;
400 417 int metric;
401 418 const char *ifname = arg;
402 419 int s;
403 420
404 421 /* if we are resetting, set the value to its default value */
405 422 if (flags & IPADM_OPT_DEFAULT) {
406 423 metric = DEF_METRIC_VAL;
407 424 } else {
408 425 errno = 0;
409 426 metric = (uint_t)strtol(pval, &endp, 10);
410 427 if (errno != 0 || *endp != '\0')
411 428 return (IPADM_INVALID_ARG);
412 429 }
413 430
414 431 bzero(&lifr, sizeof (lifr));
415 432 (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
416 433 lifr.lifr_metric = metric;
417 434
418 435 s = (proto == MOD_PROTO_IPV6 ? iph->iph_sock6 : iph->iph_sock);
419 436
420 437 if (ioctl(s, SIOCSLIFMETRIC, (caddr_t)&lifr) < 0)
421 438 return (ipadm_errno2status(errno));
422 439
423 440 return (IPADM_SUCCESS);
424 441 }
425 442
426 443 /* ARGSUSED */
427 444 static ipadm_status_t
428 445 i_ipadm_set_usesrc(ipadm_handle_t iph, const void *arg,
429 446 ipadm_prop_desc_t *pdp, const void *pval, uint_t proto, uint_t flags)
430 447 {
431 448 struct lifreq lifr;
432 449 const char *ifname = arg;
433 450 int s;
434 451 uint_t ifindex = 0;
435 452
436 453 /* if we are resetting, set the value to its default value */
437 454 if (flags & IPADM_OPT_DEFAULT)
438 455 pval = IPADM_NONESTR;
439 456
440 457 /*
441 458 * cannot specify logical interface name. We can also filter out other
442 459 * bogus interface names here itself through i_ipadm_validate_ifname().
443 460 */
444 461 if (strcmp(pval, IPADM_NONESTR) != 0 &&
445 462 !i_ipadm_validate_ifname(iph, pval))
446 463 return (IPADM_INVALID_ARG);
447 464
448 465 bzero(&lifr, sizeof (lifr));
449 466 (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
450 467
451 468 s = (proto == MOD_PROTO_IPV6 ? iph->iph_sock6 : iph->iph_sock);
452 469
453 470 if (strcmp(pval, IPADM_NONESTR) != 0) {
454 471 if ((ifindex = if_nametoindex(pval)) == 0)
455 472 return (ipadm_errno2status(errno));
456 473 lifr.lifr_index = ifindex;
457 474 } else {
458 475 if (ioctl(s, SIOCGLIFUSESRC, (caddr_t)&lifr) < 0)
459 476 return (ipadm_errno2status(errno));
460 477 lifr.lifr_index = 0;
461 478 }
462 479 if (ioctl(s, SIOCSLIFUSESRC, (caddr_t)&lifr) < 0)
463 480 return (ipadm_errno2status(errno));
464 481
465 482 return (IPADM_SUCCESS);
466 483 }
467 484
468 485 static struct hostmodel_strval {
469 486 char *esm_str;
470 487 ip_hostmodel_t esm_val;
471 488 } esm_arr[] = {
472 489 {"weak", IP_WEAK_ES},
473 490 {"src-priority", IP_SRC_PRI_ES},
474 491 {"strong", IP_STRONG_ES},
475 492 {"custom", IP_MAXVAL_ES}
476 493 };
477 494
478 495 static ip_hostmodel_t
479 496 i_ipadm_hostmodel_str2val(const char *pval)
480 497 {
481 498 int i;
482 499
483 500 for (i = 0; i < A_CNT(esm_arr); i++) {
484 501 if (esm_arr[i].esm_str != NULL &&
485 502 strcmp(pval, esm_arr[i].esm_str) == 0) {
486 503 return (esm_arr[i].esm_val);
487 504 }
488 505 }
489 506 return (IP_MAXVAL_ES);
490 507 }
491 508
492 509 static char *
493 510 i_ipadm_hostmodel_val2str(ip_hostmodel_t pval)
494 511 {
495 512 int i;
496 513
497 514 for (i = 0; i < A_CNT(esm_arr); i++) {
498 515 if (esm_arr[i].esm_val == pval)
499 516 return (esm_arr[i].esm_str);
500 517 }
501 518 return (NULL);
502 519 }
503 520
504 521 /* ARGSUSED */
505 522 static ipadm_status_t
506 523 i_ipadm_set_hostmodel(ipadm_handle_t iph, const void *arg,
507 524 ipadm_prop_desc_t *pdp, const void *pval, uint_t proto, uint_t flags)
508 525 {
509 526 ip_hostmodel_t hostmodel;
510 527 char val[11]; /* covers uint32_max as a string */
511 528
512 529 if ((flags & IPADM_OPT_DEFAULT) == 0) {
513 530 hostmodel = i_ipadm_hostmodel_str2val(pval);
514 531 if (hostmodel == IP_MAXVAL_ES)
515 532 return (IPADM_INVALID_ARG);
516 533 (void) snprintf(val, sizeof (val), "%d", hostmodel);
517 534 pval = val;
518 535 }
519 536 return (i_ipadm_set_prop(iph, NULL, pdp, pval, proto, flags));
520 537 }
521 538
522 539 /* ARGSUSED */
523 540 static ipadm_status_t
524 541 i_ipadm_get_hostmodel(ipadm_handle_t iph, const void *arg,
525 542 ipadm_prop_desc_t *pdp, char *buf, uint_t *bufsize, uint_t proto,
526 543 uint_t valtype)
527 544 {
528 545 ip_hostmodel_t hostmodel;
529 546 char *cp;
530 547 size_t nbytes;
531 548 ipadm_status_t status;
532 549
533 550 switch (valtype) {
534 551 case MOD_PROP_PERM:
535 552 nbytes = snprintf(buf, *bufsize, "%d", MOD_PROP_PERM_RW);
536 553 break;
537 554 case MOD_PROP_DEFAULT:
538 555 nbytes = snprintf(buf, *bufsize, "weak");
539 556 break;
540 557 case MOD_PROP_ACTIVE:
541 558 status = i_ipadm_get_prop(iph, arg, pdp, buf, bufsize, proto,
542 559 valtype);
543 560 if (status != IPADM_SUCCESS)
544 561 return (status);
545 562 bcopy(buf, &hostmodel, sizeof (hostmodel));
546 563 cp = i_ipadm_hostmodel_val2str(hostmodel);
547 564 nbytes = snprintf(buf, *bufsize, "%s",
548 565 (cp != NULL ? cp : "?"));
549 566 break;
550 567 case MOD_PROP_POSSIBLE:
551 568 nbytes = snprintf(buf, *bufsize, "strong,src-priority,weak");
552 569 break;
553 570 default:
554 571 return (IPADM_INVALID_ARG);
555 572 }
556 573 if (nbytes >= *bufsize) {
557 574 /* insufficient buffer space */
558 575 *bufsize = nbytes + 1;
559 576 return (IPADM_NO_BUFS);
560 577 }
561 578 return (IPADM_SUCCESS);
562 579 }
563 580
564 581 /* ARGSUSED */
565 582 static ipadm_status_t
566 583 i_ipadm_set_ifprop_flags(ipadm_handle_t iph, const void *arg,
567 584 ipadm_prop_desc_t *pdp, const void *pval, uint_t proto, uint_t flags)
568 585 {
569 586 ipadm_status_t status = IPADM_SUCCESS;
570 587 const char *ifname = arg;
571 588 uint64_t on_flags = 0, off_flags = 0;
572 589 boolean_t on = B_FALSE;
573 590 sa_family_t af = (proto == MOD_PROTO_IPV6 ? AF_INET6 : AF_INET);
574 591
575 592 /* if we are resetting, set the value to its default value */
576 593 if (flags & IPADM_OPT_DEFAULT) {
577 594 if (strcmp(pdp->ipd_name, "exchange_routes") == 0 ||
578 595 strcmp(pdp->ipd_name, "arp") == 0 ||
579 596 strcmp(pdp->ipd_name, "nud") == 0) {
580 597 pval = IPADM_ONSTR;
581 598 } else if (strcmp(pdp->ipd_name, "forwarding") == 0) {
582 599 pval = IPADM_OFFSTR;
583 600 } else {
584 601 return (IPADM_PROP_UNKNOWN);
585 602 }
586 603 }
587 604
588 605 if (strcmp(pval, IPADM_ONSTR) == 0)
589 606 on = B_TRUE;
590 607 else if (strcmp(pval, IPADM_OFFSTR) == 0)
591 608 on = B_FALSE;
592 609 else
593 610 return (IPADM_INVALID_ARG);
594 611
595 612 if (strcmp(pdp->ipd_name, "exchange_routes") == 0) {
596 613 if (on)
597 614 off_flags = IFF_NORTEXCH;
598 615 else
599 616 on_flags = IFF_NORTEXCH;
600 617 } else if (strcmp(pdp->ipd_name, "arp") == 0) {
601 618 if (on)
602 619 off_flags = IFF_NOARP;
603 620 else
604 621 on_flags = IFF_NOARP;
605 622 } else if (strcmp(pdp->ipd_name, "nud") == 0) {
606 623 if (on)
607 624 off_flags = IFF_NONUD;
608 625 else
609 626 on_flags = IFF_NONUD;
610 627 } else if (strcmp(pdp->ipd_name, "forwarding") == 0) {
611 628 if (on)
612 629 on_flags = IFF_ROUTER;
613 630 else
614 631 off_flags = IFF_ROUTER;
615 632 }
616 633
617 634 if (on_flags || off_flags) {
618 635 status = i_ipadm_set_flags(iph, ifname, af, on_flags,
619 636 off_flags);
620 637 }
621 638 return (status);
622 639 }
623 640
624 641 /* ARGSUSED */
625 642 static ipadm_status_t
626 643 i_ipadm_set_eprivport(ipadm_handle_t iph, const void *arg,
627 644 ipadm_prop_desc_t *pdp, const void *pval, uint_t proto, uint_t flags)
628 645 {
629 646 nvlist_t *portsnvl = NULL;
630 647 nvpair_t *nvp;
631 648 ipadm_status_t status = IPADM_SUCCESS;
632 649 int err;
633 650 uint_t count = 0;
634 651
635 652 if (flags & IPADM_OPT_DEFAULT) {
636 653 assert(pval == NULL);
637 654 return (i_ipadm_set_prop(iph, arg, pdp, pval, proto, flags));
638 655 }
639 656
640 657 if ((err = ipadm_str2nvlist(pval, &portsnvl, IPADM_NORVAL)) != 0)
641 658 return (ipadm_errno2status(err));
642 659
643 660 /* count the number of ports */
644 661 for (nvp = nvlist_next_nvpair(portsnvl, NULL); nvp != NULL;
645 662 nvp = nvlist_next_nvpair(portsnvl, nvp)) {
646 663 ++count;
647 664 }
648 665
649 666 if (iph->iph_flags & IPH_INIT) {
650 667 flags |= IPADM_OPT_APPEND;
651 668 } else if (count > 1) {
652 669 /*
653 670 * We allow only one port to be added, removed or
654 671 * assigned at a time.
655 672 *
656 673 * However on reboot, while initializing protocol
657 674 * properties, extra_priv_ports might have multiple
658 675 * values. Only in that case we allow setting multiple
659 676 * values.
660 677 */
661 678 nvlist_free(portsnvl);
662 679 return (IPADM_INVALID_ARG);
663 680 }
664 681
665 682 for (nvp = nvlist_next_nvpair(portsnvl, NULL); nvp != NULL;
666 683 nvp = nvlist_next_nvpair(portsnvl, nvp)) {
667 684 status = i_ipadm_set_prop(iph, arg, pdp, nvpair_name(nvp),
668 685 proto, flags);
669 686 if (status != IPADM_SUCCESS)
670 687 break;
671 688 }
672 689 nvlist_free(portsnvl);
673 690 return (status);
674 691 }
675 692
676 693 /* ARGSUSED */
677 694 static ipadm_status_t
678 695 i_ipadm_set_forwarding(ipadm_handle_t iph, const void *arg,
679 696 ipadm_prop_desc_t *pdp, const void *pval, uint_t proto, uint_t flags)
680 697 {
681 698 const char *ifname = arg;
682 699 ipadm_status_t status;
683 700
684 701 /*
685 702 * if interface name is provided, then set forwarding using the
686 703 * IFF_ROUTER flag
687 704 */
688 705 if (ifname != NULL) {
689 706 status = i_ipadm_set_ifprop_flags(iph, ifname, pdp, pval,
690 707 proto, flags);
691 708 } else {
692 709 char *val = NULL;
693 710
694 711 /*
695 712 * if the caller is IPH_LEGACY, `pval' already contains
696 713 * numeric values.
697 714 */
698 715 if (!(flags & IPADM_OPT_DEFAULT) &&
699 716 !(iph->iph_flags & IPH_LEGACY)) {
700 717
701 718 if (strcmp(pval, IPADM_ONSTR) == 0)
702 719 val = "1";
703 720 else if (strcmp(pval, IPADM_OFFSTR) == 0)
704 721 val = "0";
705 722 else
706 723 return (IPADM_INVALID_ARG);
707 724 pval = val;
708 725 }
709 726
710 727 status = i_ipadm_set_prop(iph, ifname, pdp, pval, proto, flags);
711 728 }
712 729
713 730 return (status);
714 731 }
715 732
716 733 /* ARGSUSED */
717 734 static ipadm_status_t
718 735 i_ipadm_set_ecnsack(ipadm_handle_t iph, const void *arg,
719 736 ipadm_prop_desc_t *pdp, const void *pval, uint_t proto, uint_t flags)
720 737 {
721 738 uint_t i;
722 739 char val[MAXPROPVALLEN];
723 740
724 741 /* if IPH_LEGACY is set, `pval' already contains numeric values */
725 742 if (!(flags & IPADM_OPT_DEFAULT) && !(iph->iph_flags & IPH_LEGACY)) {
726 743 for (i = 0; ecn_sack_vals[i] != NULL; i++) {
727 744 if (strcmp(pval, ecn_sack_vals[i]) == 0)
728 745 break;
729 746 }
730 747 if (ecn_sack_vals[i] == NULL)
731 748 return (IPADM_INVALID_ARG);
732 749 (void) snprintf(val, MAXPROPVALLEN, "%d", i);
733 750 pval = val;
734 751 }
735 752
736 753 return (i_ipadm_set_prop(iph, arg, pdp, pval, proto, flags));
737 754 }
738 755
739 756 /* ARGSUSED */
740 757 ipadm_status_t
741 758 i_ipadm_get_ecnsack(ipadm_handle_t iph, const void *arg,
742 759 ipadm_prop_desc_t *pdp, char *buf, uint_t *bufsize, uint_t proto,
743 760 uint_t valtype)
744 761 {
745 762 ipadm_status_t status = IPADM_SUCCESS;
746 763 uint_t i, nbytes = 0;
747 764
748 765 switch (valtype) {
749 766 case MOD_PROP_POSSIBLE:
750 767 for (i = 0; ecn_sack_vals[i] != NULL; i++) {
751 768 if (i == 0)
752 769 nbytes += snprintf(buf + nbytes,
753 770 *bufsize - nbytes, "%s", ecn_sack_vals[i]);
754 771 else
755 772 nbytes += snprintf(buf + nbytes,
756 773 *bufsize - nbytes, ",%s", ecn_sack_vals[i]);
757 774 if (nbytes >= *bufsize)
758 775 break;
759 776 }
760 777 break;
761 778 case MOD_PROP_PERM:
762 779 case MOD_PROP_DEFAULT:
763 780 case MOD_PROP_ACTIVE:
764 781 status = i_ipadm_get_prop(iph, arg, pdp, buf, bufsize, proto,
765 782 valtype);
766 783
767 784 /*
768 785 * If IPH_LEGACY is set, do not convert the value returned
769 786 * from kernel,
770 787 */
771 788 if (iph->iph_flags & IPH_LEGACY)
772 789 break;
773 790
774 791 /*
775 792 * For current and default value, convert the value returned
776 793 * from kernel to more discrete representation.
777 794 */
778 795 if (status == IPADM_SUCCESS && (valtype == MOD_PROP_ACTIVE ||
779 796 valtype == MOD_PROP_DEFAULT)) {
780 797 i = atoi(buf);
781 798 assert(i < 3);
782 799 nbytes = snprintf(buf, *bufsize, "%s",
783 800 ecn_sack_vals[i]);
784 801 }
785 802 break;
786 803 default:
787 804 return (IPADM_INVALID_ARG);
788 805 }
789 806 if (nbytes >= *bufsize) {
790 807 /* insufficient buffer space */
791 808 *bufsize = nbytes + 1;
792 809 return (IPADM_NO_BUFS);
793 810 }
794 811
795 812 return (status);
796 813 }
797 814
798 815 /* ARGSUSED */
799 816 static ipadm_status_t
800 817 i_ipadm_get_forwarding(ipadm_handle_t iph, const void *arg,
801 818 ipadm_prop_desc_t *pdp, char *buf, uint_t *bufsize, uint_t proto,
802 819 uint_t valtype)
803 820 {
804 821 const char *ifname = arg;
805 822 ipadm_status_t status = IPADM_SUCCESS;
806 823
807 824 /*
808 825 * if interface name is provided, then get forwarding status using
809 826 * SIOCGLIFFLAGS
810 827 */
811 828 if (ifname != NULL) {
812 829 status = i_ipadm_get_ifprop_flags(iph, ifname, pdp,
813 830 buf, bufsize, pdp->ipd_proto, valtype);
814 831 } else {
815 832 status = i_ipadm_get_prop(iph, ifname, pdp, buf,
816 833 bufsize, proto, valtype);
817 834 /*
818 835 * If IPH_LEGACY is set, do not convert the value returned
819 836 * from kernel,
820 837 */
821 838 if (iph->iph_flags & IPH_LEGACY)
822 839 goto ret;
823 840 if (status == IPADM_SUCCESS && (valtype == MOD_PROP_ACTIVE ||
824 841 valtype == MOD_PROP_DEFAULT)) {
825 842 uint_t val = atoi(buf);
826 843
827 844 (void) snprintf(buf, *bufsize,
828 845 (val == 1 ? IPADM_ONSTR : IPADM_OFFSTR));
829 846 }
830 847 }
831 848
832 849 ret:
833 850 return (status);
834 851 }
835 852
836 853 /* ARGSUSED */
837 854 static ipadm_status_t
838 855 i_ipadm_get_mtu(ipadm_handle_t iph, const void *arg,
839 856 ipadm_prop_desc_t *pdp, char *buf, uint_t *bufsize, uint_t proto,
840 857 uint_t valtype)
841 858 {
842 859 struct lifreq lifr;
843 860 const char *ifname = arg;
844 861 size_t nbytes;
845 862 int s;
846 863
847 864 switch (valtype) {
848 865 case MOD_PROP_PERM:
849 866 nbytes = snprintf(buf, *bufsize, "%d", MOD_PROP_PERM_RW);
850 867 break;
851 868 case MOD_PROP_DEFAULT:
852 869 case MOD_PROP_POSSIBLE:
853 870 return (i_ipadm_get_prop(iph, arg, pdp, buf, bufsize,
854 871 proto, valtype));
855 872 case MOD_PROP_ACTIVE:
856 873 bzero(&lifr, sizeof (lifr));
857 874 (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
858 875 s = (proto == MOD_PROTO_IPV6 ? iph->iph_sock6 : iph->iph_sock);
859 876
860 877 if (ioctl(s, SIOCGLIFMTU, (caddr_t)&lifr) < 0)
861 878 return (ipadm_errno2status(errno));
862 879 nbytes = snprintf(buf, *bufsize, "%u", lifr.lifr_mtu);
863 880 break;
864 881 default:
865 882 return (IPADM_INVALID_ARG);
866 883 }
867 884 if (nbytes >= *bufsize) {
868 885 /* insufficient buffer space */
869 886 *bufsize = nbytes + 1;
870 887 return (IPADM_NO_BUFS);
871 888 }
872 889 return (IPADM_SUCCESS);
873 890 }
874 891
875 892 /* ARGSUSED */
876 893 static ipadm_status_t
877 894 i_ipadm_get_metric(ipadm_handle_t iph, const void *arg,
878 895 ipadm_prop_desc_t *pdp, char *buf, uint_t *bufsize, uint_t proto,
879 896 uint_t valtype)
880 897 {
881 898 struct lifreq lifr;
882 899 const char *ifname = arg;
883 900 size_t nbytes;
884 901 int s, val;
885 902
886 903 switch (valtype) {
887 904 case MOD_PROP_PERM:
888 905 val = MOD_PROP_PERM_RW;
889 906 break;
890 907 case MOD_PROP_DEFAULT:
891 908 val = DEF_METRIC_VAL;
892 909 break;
893 910 case MOD_PROP_ACTIVE:
894 911 bzero(&lifr, sizeof (lifr));
895 912 (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
896 913
897 914 s = (proto == MOD_PROTO_IPV6 ? iph->iph_sock6 : iph->iph_sock);
898 915 if (ioctl(s, SIOCGLIFMETRIC, (caddr_t)&lifr) < 0)
899 916 return (ipadm_errno2status(errno));
900 917 val = lifr.lifr_metric;
901 918 break;
902 919 default:
903 920 return (IPADM_INVALID_ARG);
904 921 }
905 922 nbytes = snprintf(buf, *bufsize, "%d", val);
906 923 if (nbytes >= *bufsize) {
907 924 /* insufficient buffer space */
908 925 *bufsize = nbytes + 1;
909 926 return (IPADM_NO_BUFS);
910 927 }
911 928
912 929 return (IPADM_SUCCESS);
913 930 }
914 931
915 932 /* ARGSUSED */
916 933 static ipadm_status_t
917 934 i_ipadm_get_usesrc(ipadm_handle_t iph, const void *arg,
918 935 ipadm_prop_desc_t *ipd, char *buf, uint_t *bufsize, uint_t proto,
919 936 uint_t valtype)
920 937 {
921 938 struct lifreq lifr;
922 939 const char *ifname = arg;
923 940 int s;
924 941 char if_name[IF_NAMESIZE];
925 942 size_t nbytes;
926 943
927 944 switch (valtype) {
928 945 case MOD_PROP_PERM:
929 946 nbytes = snprintf(buf, *bufsize, "%d", MOD_PROP_PERM_RW);
930 947 break;
931 948 case MOD_PROP_DEFAULT:
932 949 nbytes = snprintf(buf, *bufsize, "%s", IPADM_NONESTR);
933 950 break;
934 951 case MOD_PROP_ACTIVE:
935 952 bzero(&lifr, sizeof (lifr));
936 953 (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name));
937 954
938 955 s = (proto == MOD_PROTO_IPV6 ? iph->iph_sock6 : iph->iph_sock);
939 956 if (ioctl(s, SIOCGLIFUSESRC, (caddr_t)&lifr) < 0)
940 957 return (ipadm_errno2status(errno));
941 958 if (lifr.lifr_index == 0) {
942 959 /* no src address was set, so print 'none' */
943 960 (void) strlcpy(if_name, IPADM_NONESTR,
944 961 sizeof (if_name));
945 962 } else if (if_indextoname(lifr.lifr_index, if_name) == NULL) {
946 963 return (ipadm_errno2status(errno));
947 964 }
948 965 nbytes = snprintf(buf, *bufsize, "%s", if_name);
949 966 break;
950 967 default:
951 968 return (IPADM_INVALID_ARG);
952 969 }
953 970 if (nbytes >= *bufsize) {
954 971 /* insufficient buffer space */
955 972 *bufsize = nbytes + 1;
956 973 return (IPADM_NO_BUFS);
957 974 }
958 975 return (IPADM_SUCCESS);
959 976 }
960 977
961 978 /* ARGSUSED */
962 979 static ipadm_status_t
963 980 i_ipadm_get_ifprop_flags(ipadm_handle_t iph, const void *arg,
964 981 ipadm_prop_desc_t *pdp, char *buf, uint_t *bufsize, uint_t proto,
965 982 uint_t valtype)
966 983 {
967 984 uint64_t intf_flags;
968 985 char *val;
969 986 size_t nbytes;
970 987 const char *ifname = arg;
971 988 sa_family_t af;
972 989 ipadm_status_t status = IPADM_SUCCESS;
973 990
974 991 switch (valtype) {
975 992 case MOD_PROP_PERM:
976 993 nbytes = snprintf(buf, *bufsize, "%d", MOD_PROP_PERM_RW);
977 994 break;
978 995 case MOD_PROP_DEFAULT:
979 996 if (strcmp(pdp->ipd_name, "exchange_routes") == 0 ||
980 997 strcmp(pdp->ipd_name, "arp") == 0 ||
981 998 strcmp(pdp->ipd_name, "nud") == 0) {
982 999 val = IPADM_ONSTR;
983 1000 } else if (strcmp(pdp->ipd_name, "forwarding") == 0) {
984 1001 val = IPADM_OFFSTR;
985 1002 } else {
986 1003 return (IPADM_PROP_UNKNOWN);
987 1004 }
988 1005 nbytes = snprintf(buf, *bufsize, "%s", val);
989 1006 break;
990 1007 case MOD_PROP_ACTIVE:
991 1008 af = (proto == MOD_PROTO_IPV6 ? AF_INET6 : AF_INET);
992 1009 status = i_ipadm_get_flags(iph, ifname, af, &intf_flags);
993 1010 if (status != IPADM_SUCCESS)
994 1011 return (status);
995 1012
996 1013 val = IPADM_OFFSTR;
997 1014 if (strcmp(pdp->ipd_name, "exchange_routes") == 0) {
998 1015 if (!(intf_flags & IFF_NORTEXCH))
999 1016 val = IPADM_ONSTR;
1000 1017 } else if (strcmp(pdp->ipd_name, "forwarding") == 0) {
1001 1018 if (intf_flags & IFF_ROUTER)
1002 1019 val = IPADM_ONSTR;
1003 1020 } else if (strcmp(pdp->ipd_name, "arp") == 0) {
1004 1021 if (!(intf_flags & IFF_NOARP))
1005 1022 val = IPADM_ONSTR;
1006 1023 } else if (strcmp(pdp->ipd_name, "nud") == 0) {
1007 1024 if (!(intf_flags & IFF_NONUD))
1008 1025 val = IPADM_ONSTR;
1009 1026 }
1010 1027 nbytes = snprintf(buf, *bufsize, "%s", val);
1011 1028 break;
1012 1029 default:
1013 1030 return (IPADM_INVALID_ARG);
1014 1031 }
1015 1032 if (nbytes >= *bufsize) {
1016 1033 /* insufficient buffer space */
1017 1034 *bufsize = nbytes + 1;
1018 1035 status = IPADM_NO_BUFS;
1019 1036 }
1020 1037
1021 1038 return (status);
1022 1039 }
1023 1040
1024 1041 static void
1025 1042 i_ipadm_perm2str(char *buf, uint_t *bufsize)
1026 1043 {
1027 1044 uint_t perm = atoi(buf);
1028 1045
1029 1046 (void) snprintf(buf, *bufsize, "%c%c",
1030 1047 ((perm & MOD_PROP_PERM_READ) != 0) ? 'r' : '-',
1031 1048 ((perm & MOD_PROP_PERM_WRITE) != 0) ? 'w' : '-');
1032 1049 }
1033 1050
1034 1051 /* ARGSUSED */
1035 1052 static ipadm_status_t
1036 1053 i_ipadm_get_prop(ipadm_handle_t iph, const void *arg,
1037 1054 ipadm_prop_desc_t *pdp, char *buf, uint_t *bufsize, uint_t proto,
1038 1055 uint_t valtype)
1039 1056 {
1040 1057 ipadm_status_t status = IPADM_SUCCESS;
1041 1058 const char *ifname = arg;
1042 1059 mod_ioc_prop_t *mip;
1043 1060 char *pname = pdp->ipd_name;
1044 1061 uint_t iocsize;
1045 1062
1046 1063 /* allocate sufficient ioctl buffer to retrieve value */
1047 1064 iocsize = sizeof (mod_ioc_prop_t) + *bufsize - 1;
1048 1065 if ((mip = calloc(1, iocsize)) == NULL)
1049 1066 return (IPADM_NO_BUFS);
1050 1067
1051 1068 mip->mpr_version = MOD_PROP_VERSION;
1052 1069 mip->mpr_flags = valtype;
1053 1070 mip->mpr_proto = proto;
1054 1071 if (ifname != NULL) {
1055 1072 (void) strlcpy(mip->mpr_ifname, ifname,
1056 1073 sizeof (mip->mpr_ifname));
1057 1074 }
1058 1075 (void) strlcpy(mip->mpr_name, pname, sizeof (mip->mpr_name));
1059 1076 mip->mpr_valsize = *bufsize;
1060 1077
1061 1078 if (i_ipadm_strioctl(iph->iph_sock, SIOCGETPROP, (char *)mip,
1062 1079 iocsize) < 0) {
1063 1080 if (errno == ENOENT)
1064 1081 status = IPADM_PROP_UNKNOWN;
1065 1082 else
1066 1083 status = ipadm_errno2status(errno);
1067 1084 } else {
1068 1085 bcopy(mip->mpr_val, buf, *bufsize);
1069 1086 }
1070 1087
1071 1088 free(mip);
1072 1089 return (status);
1073 1090 }
1074 1091
1075 1092 /*
1076 1093 * Populates the ipmgmt_prop_arg_t based on the class of property.
1077 1094 *
1078 1095 * For private protocol properties, while persisting information in ipadm
1079 1096 * data store, to ensure there is no collision of namespace between ipadm
1080 1097 * private nvpair names (which also starts with '_', see ipadm_ipmgmt.h)
1081 1098 * and private protocol property names, we will prepend IPADM_PRIV_PROP_PREFIX
1082 1099 * to property names.
1083 1100 */
1084 1101 static void
1085 1102 i_ipadm_populate_proparg(ipmgmt_prop_arg_t *pargp, ipadm_prop_desc_t *pdp,
1086 1103 const char *pval, const void *object)
1087 1104 {
1088 1105 const struct ipadm_addrobj_s *ipaddr;
1089 1106 uint_t class = pdp->ipd_class;
1090 1107 uint_t proto = pdp->ipd_proto;
1091 1108
1092 1109 (void) strlcpy(pargp->ia_pname, pdp->ipd_name,
1093 1110 sizeof (pargp->ia_pname));
1094 1111 if (pval != NULL)
1095 1112 (void) strlcpy(pargp->ia_pval, pval, sizeof (pargp->ia_pval));
1096 1113
1097 1114 switch (class) {
1098 1115 case IPADMPROP_CLASS_MODULE:
1099 1116 /* if it's a private property then add the prefix. */
1100 1117 if (pdp->ipd_name[0] == '_') {
1101 1118 (void) snprintf(pargp->ia_pname,
1102 1119 sizeof (pargp->ia_pname), "_%s", pdp->ipd_name);
1103 1120 }
1104 1121 (void) strlcpy(pargp->ia_module, object,
1105 1122 sizeof (pargp->ia_module));
1106 1123 break;
1107 1124 case IPADMPROP_CLASS_MODIF:
1108 1125 /* check if object is protostr or an ifname */
1109 1126 if (ipadm_str2proto(object) != MOD_PROTO_NONE) {
1110 1127 (void) strlcpy(pargp->ia_module, object,
1111 1128 sizeof (pargp->ia_module));
1112 1129 break;
1113 1130 }
1114 1131 /* it's an interface property, fall through */
1115 1132 /* FALLTHRU */
1116 1133 case IPADMPROP_CLASS_IF:
1117 1134 (void) strlcpy(pargp->ia_ifname, object,
1118 1135 sizeof (pargp->ia_ifname));
1119 1136 (void) strlcpy(pargp->ia_module, ipadm_proto2str(proto),
1120 1137 sizeof (pargp->ia_module));
1121 1138 break;
1122 1139 case IPADMPROP_CLASS_ADDR:
1123 1140 ipaddr = object;
1124 1141 (void) strlcpy(pargp->ia_ifname, ipaddr->ipadm_ifname,
1125 1142 sizeof (pargp->ia_ifname));
1126 1143 (void) strlcpy(pargp->ia_aobjname, ipaddr->ipadm_aobjname,
1127 1144 sizeof (pargp->ia_aobjname));
1128 1145 break;
1129 1146 }
1130 1147 }
1131 1148
1132 1149 /*
1133 1150 * Common function to retrieve property value for a given interface `ifname' or
1134 1151 * for a given protocol `proto'. The property name is in `pname'.
1135 1152 *
1136 1153 * `valtype' determines the type of value that will be retrieved.
1137 1154 * IPADM_OPT_ACTIVE - current value of the property (active config)
1138 1155 * IPADM_OPT_PERSIST - value of the property from persistent store
1139 1156 * IPADM_OPT_DEFAULT - default hard coded value (boot-time value)
1140 1157 * IPADM_OPT_PERM - read/write permissions for the value
1141 1158 * IPADM_OPT_POSSIBLE - range of values
1142 1159 */
1143 1160 static ipadm_status_t
1144 1161 i_ipadm_getprop_common(ipadm_handle_t iph, const char *ifname,
1145 1162 const char *pname, char *buf, uint_t *bufsize, uint_t proto,
1146 1163 uint_t valtype)
1147 1164 {
1148 1165 ipadm_status_t status = IPADM_SUCCESS;
1149 1166 ipadm_prop_desc_t *pdp;
1150 1167 char priv_propname[MAXPROPNAMELEN];
1151 1168 boolean_t is_if = (ifname != NULL);
1152 1169 int err = 0;
1153 1170
1154 1171 pdp = i_ipadm_get_prop_desc(pname, proto, &err);
1155 1172 if (err == EPROTO)
1156 1173 return (IPADM_BAD_PROTOCOL);
1157 1174 /* there are no private interface properties */
1158 1175 if (is_if && err == ENOENT)
1159 1176 return (IPADM_PROP_UNKNOWN);
1160 1177
1161 1178 if (pdp != NULL) {
1162 1179 /*
1163 1180 * check whether the property can be
1164 1181 * applied on an interface
1165 1182 */
1166 1183 if (is_if && !(pdp->ipd_class & IPADMPROP_CLASS_IF))
1167 1184 return (IPADM_INVALID_ARG);
1168 1185 /*
1169 1186 * check whether the property can be
1170 1187 * applied on a module
1171 1188 */
1172 1189 if (!is_if && !(pdp->ipd_class & IPADMPROP_CLASS_MODULE))
1173 1190 return (IPADM_INVALID_ARG);
1174 1191
1175 1192 } else {
1176 1193 /* private protocol properties, pass it to kernel directly */
1177 1194 pdp = &ipadm_privprop;
1178 1195 (void) strlcpy(priv_propname, pname, sizeof (priv_propname));
1179 1196 pdp->ipd_name = priv_propname;
1180 1197 }
1181 1198
1182 1199 switch (valtype) {
1183 1200 case IPADM_OPT_PERM:
1184 1201 status = pdp->ipd_get(iph, ifname, pdp, buf, bufsize, proto,
1185 1202 MOD_PROP_PERM);
1186 1203 if (status == IPADM_SUCCESS)
1187 1204 i_ipadm_perm2str(buf, bufsize);
1188 1205 break;
1189 1206 case IPADM_OPT_ACTIVE:
1190 1207 status = pdp->ipd_get(iph, ifname, pdp, buf, bufsize, proto,
1191 1208 MOD_PROP_ACTIVE);
1192 1209 break;
1193 1210 case IPADM_OPT_DEFAULT:
1194 1211 status = pdp->ipd_get(iph, ifname, pdp, buf, bufsize, proto,
1195 1212 MOD_PROP_DEFAULT);
1196 1213 break;
1197 1214 case IPADM_OPT_POSSIBLE:
1198 1215 if (pdp->ipd_get_range != NULL) {
1199 1216 status = pdp->ipd_get_range(iph, ifname, pdp, buf,
1200 1217 bufsize, proto, MOD_PROP_POSSIBLE);
1201 1218 break;
1202 1219 }
1203 1220 buf[0] = '\0';
1204 1221 break;
1205 1222 case IPADM_OPT_PERSIST:
1206 1223 /* retrieve from database */
1207 1224 if (is_if)
1208 1225 status = i_ipadm_get_persist_propval(iph, pdp, buf,
1209 1226 bufsize, ifname);
1210 1227 else
1211 1228 status = i_ipadm_get_persist_propval(iph, pdp, buf,
1212 1229 bufsize, ipadm_proto2str(proto));
1213 1230 break;
1214 1231 default:
1215 1232 status = IPADM_INVALID_ARG;
1216 1233 break;
1217 1234 }
1218 1235 return (status);
1219 1236 }
1220 1237
1221 1238 /*
1222 1239 * Get protocol property of the specified protocol.
1223 1240 */
1224 1241 ipadm_status_t
1225 1242 ipadm_get_prop(ipadm_handle_t iph, const char *pname, char *buf,
1226 1243 uint_t *bufsize, uint_t proto, uint_t valtype)
1227 1244 {
1228 1245 /*
1229 1246 * validate the arguments of the function.
1230 1247 */
1231 1248 if (iph == NULL || pname == NULL || buf == NULL ||
1232 1249 bufsize == NULL || *bufsize == 0) {
1233 1250 return (IPADM_INVALID_ARG);
1234 1251 }
1235 1252 /*
1236 1253 * Do we support this proto, if not return error.
1237 1254 */
1238 1255 if (ipadm_proto2str(proto) == NULL)
1239 1256 return (IPADM_NOTSUP);
1240 1257
1241 1258 return (i_ipadm_getprop_common(iph, NULL, pname, buf, bufsize,
1242 1259 proto, valtype));
1243 1260 }
1244 1261
1245 1262 /*
1246 1263 * Get interface property of the specified interface.
1247 1264 */
1248 1265 ipadm_status_t
1249 1266 ipadm_get_ifprop(ipadm_handle_t iph, const char *ifname, const char *pname,
1250 1267 char *buf, uint_t *bufsize, uint_t proto, uint_t valtype)
1251 1268 {
1252 1269 /* validate the arguments of the function. */
1253 1270 if (iph == NULL || pname == NULL || buf == NULL ||
1254 1271 bufsize == NULL || *bufsize == 0) {
1255 1272 return (IPADM_INVALID_ARG);
1256 1273 }
1257 1274
1258 1275 /* Do we support this proto, if not return error. */
1259 1276 if (ipadm_proto2str(proto) == NULL)
1260 1277 return (IPADM_NOTSUP);
1261 1278
1262 1279 /*
1263 1280 * check if interface name is provided for interface property and
1264 1281 * is valid.
1265 1282 */
1266 1283 if (!i_ipadm_validate_ifname(iph, ifname))
1267 1284 return (IPADM_INVALID_ARG);
1268 1285
1269 1286 return (i_ipadm_getprop_common(iph, ifname, pname, buf, bufsize,
1270 1287 proto, valtype));
1271 1288 }
1272 1289
1273 1290 /*
1274 1291 * Allocates sufficient ioctl buffers and copies property name and the
1275 1292 * value, among other things. If the flag IPADM_OPT_DEFAULT is set, then
1276 1293 * `pval' will be NULL and it instructs the kernel to reset the current
1277 1294 * value to property's default value.
1278 1295 */
1279 1296 static ipadm_status_t
1280 1297 i_ipadm_set_prop(ipadm_handle_t iph, const void *arg,
1281 1298 ipadm_prop_desc_t *pdp, const void *pval, uint_t proto, uint_t flags)
1282 1299 {
1283 1300 ipadm_status_t status = IPADM_SUCCESS;
1284 1301 const char *ifname = arg;
1285 1302 mod_ioc_prop_t *mip;
1286 1303 char *pname = pdp->ipd_name;
1287 1304 uint_t valsize, iocsize;
1288 1305 uint_t iocflags = 0;
1289 1306
1290 1307 if (flags & IPADM_OPT_DEFAULT) {
1291 1308 iocflags |= MOD_PROP_DEFAULT;
1292 1309 } else if (flags & IPADM_OPT_ACTIVE) {
1293 1310 iocflags |= MOD_PROP_ACTIVE;
1294 1311 if (flags & IPADM_OPT_APPEND)
1295 1312 iocflags |= MOD_PROP_APPEND;
1296 1313 else if (flags & IPADM_OPT_REMOVE)
1297 1314 iocflags |= MOD_PROP_REMOVE;
1298 1315 }
1299 1316
1300 1317 if (pval != NULL) {
1301 1318 valsize = strlen(pval);
1302 1319 iocsize = sizeof (mod_ioc_prop_t) + valsize - 1;
1303 1320 } else {
1304 1321 valsize = 0;
1305 1322 iocsize = sizeof (mod_ioc_prop_t);
1306 1323 }
1307 1324
1308 1325 if ((mip = calloc(1, iocsize)) == NULL)
1309 1326 return (IPADM_NO_BUFS);
1310 1327
1311 1328 mip->mpr_version = MOD_PROP_VERSION;
1312 1329 mip->mpr_flags = iocflags;
1313 1330 mip->mpr_proto = proto;
1314 1331 if (ifname != NULL) {
1315 1332 (void) strlcpy(mip->mpr_ifname, ifname,
1316 1333 sizeof (mip->mpr_ifname));
1317 1334 }
1318 1335
1319 1336 (void) strlcpy(mip->mpr_name, pname, sizeof (mip->mpr_name));
1320 1337 mip->mpr_valsize = valsize;
1321 1338 if (pval != NULL)
1322 1339 bcopy(pval, mip->mpr_val, valsize);
1323 1340
1324 1341 if (i_ipadm_strioctl(iph->iph_sock, SIOCSETPROP, (char *)mip,
1325 1342 iocsize) < 0) {
1326 1343 if (errno == ENOENT)
1327 1344 status = IPADM_PROP_UNKNOWN;
1328 1345 else
1329 1346 status = ipadm_errno2status(errno);
1330 1347 }
1331 1348 free(mip);
1332 1349 return (status);
1333 1350 }
1334 1351
1335 1352 /*
1336 1353 * Common function for modifying both protocol/interface property.
1337 1354 *
1338 1355 * If:
1339 1356 * IPADM_OPT_PERSIST is set then the value is persisted.
1340 1357 * IPADM_OPT_DEFAULT is set then the default value for the property will
1341 1358 * be applied.
1342 1359 */
1343 1360 static ipadm_status_t
1344 1361 i_ipadm_setprop_common(ipadm_handle_t iph, const char *ifname,
1345 1362 const char *pname, const char *buf, uint_t proto, uint_t pflags)
1346 1363 {
1347 1364 ipadm_status_t status = IPADM_SUCCESS;
1348 1365 boolean_t persist = (pflags & IPADM_OPT_PERSIST);
1349 1366 boolean_t reset = (pflags & IPADM_OPT_DEFAULT);
1350 1367 ipadm_prop_desc_t *pdp;
1351 1368 boolean_t is_if = (ifname != NULL);
1352 1369 char priv_propname[MAXPROPNAMELEN];
1353 1370 int err = 0;
1354 1371
1355 1372 /* Check that property value is within the allowed size */
1356 1373 if (!reset && strnlen(buf, MAXPROPVALLEN) >= MAXPROPVALLEN)
1357 1374 return (IPADM_INVALID_ARG);
1358 1375
1359 1376 pdp = i_ipadm_get_prop_desc(pname, proto, &err);
1360 1377 if (err == EPROTO)
1361 1378 return (IPADM_BAD_PROTOCOL);
1362 1379 /* there are no private interface properties */
1363 1380 if (is_if && err == ENOENT)
1364 1381 return (IPADM_PROP_UNKNOWN);
1365 1382
1366 1383 if (pdp != NULL) {
1367 1384 /* do some sanity checks */
1368 1385 if (is_if) {
1369 1386 if (!(pdp->ipd_class & IPADMPROP_CLASS_IF))
1370 1387 return (IPADM_INVALID_ARG);
1371 1388 } else {
1372 1389 if (!(pdp->ipd_class & IPADMPROP_CLASS_MODULE))
1373 1390 return (IPADM_INVALID_ARG);
1374 1391 }
1375 1392 /*
1376 1393 * if the property is not multi-valued and IPADM_OPT_APPEND or
1377 1394 * IPADM_OPT_REMOVE is specified, return IPADM_INVALID_ARG.
1378 1395 */
1379 1396 if (!(pdp->ipd_flags & IPADMPROP_MULVAL) && (pflags &
1380 1397 (IPADM_OPT_APPEND|IPADM_OPT_REMOVE))) {
1381 1398 return (IPADM_INVALID_ARG);
1382 1399 }
1383 1400 } else {
1384 1401 /* private protocol property, pass it to kernel directly */
1385 1402 pdp = &ipadm_privprop;
1386 1403 (void) strlcpy(priv_propname, pname, sizeof (priv_propname));
1387 1404 pdp->ipd_name = priv_propname;
1388 1405 }
1389 1406
1390 1407 status = pdp->ipd_set(iph, ifname, pdp, buf, proto, pflags);
1391 1408 if (status != IPADM_SUCCESS)
1392 1409 return (status);
1393 1410
1394 1411 if (persist) {
1395 1412 if (is_if)
1396 1413 status = i_ipadm_persist_propval(iph, pdp, buf, ifname,
1397 1414 pflags);
1398 1415 else
1399 1416 status = i_ipadm_persist_propval(iph, pdp, buf,
1400 1417 ipadm_proto2str(proto), pflags);
1401 1418 }
1402 1419 return (status);
1403 1420 }
1404 1421
1405 1422 /*
1406 1423 * Sets the property value of the specified interface
1407 1424 */
1408 1425 ipadm_status_t
1409 1426 ipadm_set_ifprop(ipadm_handle_t iph, const char *ifname, const char *pname,
1410 1427 const char *buf, uint_t proto, uint_t pflags)
1411 1428 {
1412 1429 boolean_t reset = (pflags & IPADM_OPT_DEFAULT);
1413 1430 ipadm_status_t status;
1414 1431
1415 1432 /* check for solaris.network.interface.config authorization */
1416 1433 if (!ipadm_check_auth())
1417 1434 return (IPADM_EAUTH);
1418 1435 /*
1419 1436 * validate the arguments of the function.
1420 1437 */
1421 1438 if (iph == NULL || pname == NULL || (!reset && buf == NULL) ||
1422 1439 pflags == 0 || pflags == IPADM_OPT_PERSIST ||
1423 1440 (pflags & ~(IPADM_COMMON_OPT_MASK|IPADM_OPT_DEFAULT))) {
1424 1441 return (IPADM_INVALID_ARG);
1425 1442 }
1426 1443
1427 1444 /*
1428 1445 * Do we support this protocol, if not return error.
1429 1446 */
1430 1447 if (ipadm_proto2str(proto) == NULL)
1431 1448 return (IPADM_NOTSUP);
1432 1449
1433 1450 /*
1434 1451 * Validate the interface and check if a persistent
1435 1452 * operation is performed on a temporary object.
1436 1453 */
1437 1454 status = i_ipadm_validate_if(iph, ifname, proto, pflags);
1438 1455 if (status != IPADM_SUCCESS)
1439 1456 return (status);
1440 1457
1441 1458 return (i_ipadm_setprop_common(iph, ifname, pname, buf, proto,
1442 1459 pflags));
1443 1460 }
1444 1461
1445 1462 /*
1446 1463 * Sets the property value of the specified protocol.
1447 1464 */
1448 1465 ipadm_status_t
1449 1466 ipadm_set_prop(ipadm_handle_t iph, const char *pname, const char *buf,
1450 1467 uint_t proto, uint_t pflags)
1451 1468 {
1452 1469 boolean_t reset = (pflags & IPADM_OPT_DEFAULT);
1453 1470
1454 1471 /* check for solaris.network.interface.config authorization */
1455 1472 if (!ipadm_check_auth())
1456 1473 return (IPADM_EAUTH);
1457 1474 /*
1458 1475 * validate the arguments of the function.
1459 1476 */
1460 1477 if (iph == NULL || pname == NULL ||(!reset && buf == NULL) ||
1461 1478 pflags == 0 || pflags == IPADM_OPT_PERSIST ||
1462 1479 (pflags & ~(IPADM_COMMON_OPT_MASK|IPADM_OPT_DEFAULT|
1463 1480 IPADM_OPT_APPEND|IPADM_OPT_REMOVE))) {
1464 1481 return (IPADM_INVALID_ARG);
1465 1482 }
1466 1483
1467 1484 /*
1468 1485 * Do we support this proto, if not return error.
1469 1486 */
1470 1487 if (ipadm_proto2str(proto) == NULL)
1471 1488 return (IPADM_NOTSUP);
1472 1489
1473 1490 return (i_ipadm_setprop_common(iph, NULL, pname, buf, proto,
1474 1491 pflags));
1475 1492 }
1476 1493
1477 1494 /* helper function for ipadm_walk_proptbl */
1478 1495 static void
1479 1496 i_ipadm_walk_proptbl(ipadm_prop_desc_t *pdtbl, uint_t proto, uint_t class,
1480 1497 ipadm_prop_wfunc_t *func, void *arg)
1481 1498 {
1482 1499 ipadm_prop_desc_t *pdp;
1483 1500
1484 1501 for (pdp = pdtbl; pdp->ipd_name != NULL; pdp++) {
1485 1502 if (!(pdp->ipd_class & class))
1486 1503 continue;
1487 1504
1488 1505 if (proto != MOD_PROTO_NONE && !(pdp->ipd_proto & proto))
1489 1506 continue;
1490 1507
1491 1508 /*
1492 1509 * we found a class specific match, call the
1493 1510 * user callback function.
1494 1511 */
1495 1512 if (func(arg, pdp->ipd_name, pdp->ipd_proto) == B_FALSE)
1496 1513 break;
1497 1514 }
1498 1515 }
1499 1516
1500 1517 /*
1501 1518 * Walks through all the properties, for a given protocol and property class
1502 1519 * (protocol or interface).
1503 1520 *
1504 1521 * Further if proto == MOD_PROTO_NONE, then it walks through all the supported
1505 1522 * protocol property tables.
1506 1523 */
1507 1524 ipadm_status_t
1508 1525 ipadm_walk_proptbl(uint_t proto, uint_t class, ipadm_prop_wfunc_t *func,
1509 1526 void *arg)
1510 1527 {
1511 1528 ipadm_prop_desc_t *pdtbl;
1512 1529 ipadm_status_t status = IPADM_SUCCESS;
1513 1530 int i;
1514 1531 int count = A_CNT(protocols);
1515 1532
1516 1533 if (func == NULL)
1517 1534 return (IPADM_INVALID_ARG);
1518 1535
1519 1536 switch (class) {
1520 1537 case IPADMPROP_CLASS_ADDR:
1521 1538 pdtbl = ipadm_addrprop_table;
1522 1539 break;
1523 1540 case IPADMPROP_CLASS_IF:
1524 1541 case IPADMPROP_CLASS_MODULE:
1525 1542 pdtbl = i_ipadm_get_propdesc_table(proto);
1526 1543 if (pdtbl == NULL && proto != MOD_PROTO_NONE)
1527 1544 return (IPADM_INVALID_ARG);
1528 1545 break;
1529 1546 default:
1530 1547 return (IPADM_INVALID_ARG);
1531 1548 }
1532 1549
1533 1550 if (pdtbl != NULL) {
1534 1551 /*
1535 1552 * proto will be MOD_PROTO_NONE in the case of
1536 1553 * IPADMPROP_CLASS_ADDR.
1537 1554 */
1538 1555 i_ipadm_walk_proptbl(pdtbl, proto, class, func, arg);
1539 1556 } else {
1540 1557 /* Walk thru all the protocol tables, we support */
1541 1558 for (i = 0; i < count; i++) {
1542 1559 pdtbl = i_ipadm_get_propdesc_table(protocols[i]);
1543 1560 i_ipadm_walk_proptbl(pdtbl, protocols[i], class, func,
1544 1561 arg);
1545 1562 }
1546 1563 }
1547 1564 return (status);
1548 1565 }
1549 1566
1550 1567 /*
1551 1568 * Given a property name, walks through all the instances of a property name.
1552 1569 * Some properties have two instances one for v4 interfaces and another for v6
1553 1570 * interfaces. For example: MTU. MTU can have different values for v4 and v6.
1554 1571 * Therefore there are two properties for 'MTU'.
1555 1572 *
1556 1573 * This function invokes `func' for every instance of property `pname'
1557 1574 */
1558 1575 ipadm_status_t
1559 1576 ipadm_walk_prop(const char *pname, uint_t proto, uint_t class,
1560 1577 ipadm_prop_wfunc_t *func, void *arg)
1561 1578 {
1562 1579 ipadm_prop_desc_t *pdtbl, *pdp;
1563 1580 ipadm_status_t status = IPADM_SUCCESS;
1564 1581 boolean_t matched = B_FALSE;
1565 1582
1566 1583 if (pname == NULL || func == NULL)
1567 1584 return (IPADM_INVALID_ARG);
1568 1585
1569 1586 switch (class) {
1570 1587 case IPADMPROP_CLASS_ADDR:
1571 1588 pdtbl = ipadm_addrprop_table;
1572 1589 break;
1573 1590 case IPADMPROP_CLASS_IF:
1574 1591 case IPADMPROP_CLASS_MODULE:
1575 1592 pdtbl = i_ipadm_get_propdesc_table(proto);
1576 1593 break;
1577 1594 default:
1578 1595 return (IPADM_INVALID_ARG);
1579 1596 }
1580 1597
1581 1598 if (pdtbl == NULL)
1582 1599 return (IPADM_INVALID_ARG);
1583 1600
1584 1601 for (pdp = pdtbl; pdp->ipd_name != NULL; pdp++) {
1585 1602 if (strcmp(pname, pdp->ipd_name) != 0)
1586 1603 continue;
1587 1604 if (!(pdp->ipd_proto & proto))
1588 1605 continue;
1589 1606 matched = B_TRUE;
1590 1607 /* we found a match, call the callback function */
1591 1608 if (func(arg, pdp->ipd_name, pdp->ipd_proto) == B_FALSE)
1592 1609 break;
1593 1610 }
1594 1611 if (!matched)
1595 1612 status = IPADM_PROP_UNKNOWN;
1596 1613 return (status);
1597 1614 }
1598 1615
1599 1616 /* ARGSUSED */
1600 1617 ipadm_status_t
1601 1618 i_ipadm_get_onoff(ipadm_handle_t iph, const void *arg, ipadm_prop_desc_t *dp,
1602 1619 char *buf, uint_t *bufsize, uint_t proto, uint_t valtype)
1603 1620 {
1604 1621 (void) snprintf(buf, *bufsize, "%s,%s", IPADM_ONSTR, IPADM_OFFSTR);
1605 1622 return (IPADM_SUCCESS);
1606 1623 }
1607 1624
1608 1625 /*
1609 1626 * Makes a door call to ipmgmtd to retrieve the persisted property value
1610 1627 */
1611 1628 ipadm_status_t
1612 1629 i_ipadm_get_persist_propval(ipadm_handle_t iph, ipadm_prop_desc_t *pdp,
1613 1630 char *gbuf, uint_t *gbufsize, const void *object)
1614 1631 {
1615 1632 ipmgmt_prop_arg_t parg;
1616 1633 ipmgmt_getprop_rval_t rval, *rvalp;
1617 1634 size_t nbytes;
1618 1635 int err = 0;
1619 1636
1620 1637 bzero(&parg, sizeof (parg));
1621 1638 parg.ia_cmd = IPMGMT_CMD_GETPROP;
1622 1639 i_ipadm_populate_proparg(&parg, pdp, NULL, object);
1623 1640
1624 1641 rvalp = &rval;
1625 1642 err = ipadm_door_call(iph, &parg, sizeof (parg), (void **)&rvalp,
1626 1643 sizeof (rval), B_FALSE);
1627 1644 if (err == 0) {
1628 1645 /* assert that rvalp was not reallocated */
1629 1646 assert(rvalp == &rval);
1630 1647
1631 1648 /* `ir_pval' contains the property value */
1632 1649 nbytes = snprintf(gbuf, *gbufsize, "%s", rvalp->ir_pval);
1633 1650 if (nbytes >= *gbufsize) {
1634 1651 /* insufficient buffer space */
1635 1652 *gbufsize = nbytes + 1;
1636 1653 err = ENOBUFS;
1637 1654 }
1638 1655 }
1639 1656 return (ipadm_errno2status(err));
1640 1657 }
1641 1658
1642 1659 /*
1643 1660 * Persists the property value for a given property in the data store
1644 1661 */
1645 1662 ipadm_status_t
1646 1663 i_ipadm_persist_propval(ipadm_handle_t iph, ipadm_prop_desc_t *pdp,
1647 1664 const char *pval, const void *object, uint_t flags)
1648 1665 {
1649 1666 ipmgmt_prop_arg_t parg;
1650 1667 int err = 0;
1651 1668
1652 1669 bzero(&parg, sizeof (parg));
1653 1670 i_ipadm_populate_proparg(&parg, pdp, pval, object);
1654 1671 /*
1655 1672 * Check if value to be persisted need to be appended or removed. This
1656 1673 * is required for multi-valued property.
1657 1674 */
1658 1675 if (flags & IPADM_OPT_APPEND)
1659 1676 parg.ia_flags |= IPMGMT_APPEND;
1660 1677 if (flags & IPADM_OPT_REMOVE)
1661 1678 parg.ia_flags |= IPMGMT_REMOVE;
1662 1679
1663 1680 if (flags & (IPADM_OPT_DEFAULT|IPADM_OPT_REMOVE))
1664 1681 parg.ia_cmd = IPMGMT_CMD_RESETPROP;
1665 1682 else
1666 1683 parg.ia_cmd = IPMGMT_CMD_SETPROP;
1667 1684
1668 1685 err = ipadm_door_call(iph, &parg, sizeof (parg), NULL, 0, B_FALSE);
1669 1686
1670 1687 /*
1671 1688 * its fine if there were no entry in the DB to delete. The user
1672 1689 * might be changing property value, which was not changed
1673 1690 * persistently.
1674 1691 */
1675 1692 if (err == ENOENT)
1676 1693 err = 0;
1677 1694 return (ipadm_errno2status(err));
1678 1695 }
1679 1696
1680 1697 /*
1681 1698 * This is called from ipadm_set_ifprop() to validate the set operation.
1682 1699 * It does the following steps:
1683 1700 * 1. Validates the interface name.
1684 1701 * 2. Fails if it is an IPMP meta-interface or an underlying interface.
1685 1702 * 3. In case of a persistent operation, verifies that the
1686 1703 * interface is persistent.
1687 1704 */
1688 1705 static ipadm_status_t
1689 1706 i_ipadm_validate_if(ipadm_handle_t iph, const char *ifname,
1690 1707 uint_t proto, uint_t flags)
1691 1708 {
1692 1709 sa_family_t af, other_af;
1693 1710 ipadm_status_t status;
1694 1711 boolean_t p_exists;
1695 1712 boolean_t af_exists, other_af_exists, a_exists;
1696 1713
1697 1714 /* Check if the interface name is valid. */
1698 1715 if (!i_ipadm_validate_ifname(iph, ifname))
1699 1716 return (IPADM_INVALID_ARG);
1700 1717
1701 1718 af = (proto == MOD_PROTO_IPV6 ? AF_INET6 : AF_INET);
1702 1719 /*
1703 1720 * Setting properties on an IPMP meta-interface or underlying
1704 1721 * interface is not supported.
1705 1722 */
1706 1723 if (i_ipadm_is_ipmp(iph, ifname) || i_ipadm_is_under_ipmp(iph, ifname))
1707 1724 return (IPADM_NOTSUP);
1708 1725
1709 1726 /* Check if interface exists in the persistent configuration. */
1710 1727 status = i_ipadm_if_pexists(iph, ifname, af, &p_exists);
1711 1728 if (status != IPADM_SUCCESS)
1712 1729 return (status);
1713 1730
1714 1731 /* Check if interface exists in the active configuration. */
1715 1732 af_exists = ipadm_if_enabled(iph, ifname, af);
1716 1733 other_af = (af == AF_INET ? AF_INET6 : AF_INET);
1717 1734 other_af_exists = ipadm_if_enabled(iph, ifname, other_af);
1718 1735 a_exists = (af_exists || other_af_exists);
1719 1736 if (!a_exists && p_exists)
1720 1737 return (IPADM_OP_DISABLE_OBJ);
1721 1738 if (!af_exists)
1722 1739 return (IPADM_ENXIO);
1723 1740
1724 1741 /*
1725 1742 * If a persistent operation is requested, check if the underlying
1726 1743 * IP interface is persistent.
1727 1744 */
1728 1745 if ((flags & IPADM_OPT_PERSIST) && !p_exists)
1729 1746 return (IPADM_TEMPORARY_OBJ);
1730 1747 return (IPADM_SUCCESS);
1731 1748 }
1732 1749
1733 1750 /*
1734 1751 * Private protocol properties namespace scheme:
1735 1752 *
1736 1753 * PSARC 2010/080 identified the private protocol property names to be the
1737 1754 * leading protocol names. For e.g. tcp_strong_iss, ip_strict_src_multihoming,
1738 1755 * et al,. However to be consistent with private data-link property names,
1739 1756 * which starts with '_', private protocol property names will start with '_'.
1740 1757 * For e.g. _strong_iss, _strict_src_multihoming, et al,.
1741 1758 */
1742 1759
1743 1760 /* maps new private protocol property name to the old private property name */
1744 1761 typedef struct ipadm_oname2nname_map {
1745 1762 char *iom_oname;
1746 1763 char *iom_nname;
1747 1764 uint_t iom_proto;
1748 1765 } ipadm_oname2nname_map_t;
1749 1766
1750 1767 /*
1751 1768 * IP is a special case. It isn't straight forward to derive the legacy name
1752 1769 * from the new name and vice versa. No set standard was followed in naming
1753 1770 * the properties and hence we need a table to capture the mapping.
1754 1771 */
1755 1772 static ipadm_oname2nname_map_t name_map[] = {
1756 1773 { "arp_probe_delay", "_arp_probe_delay",
1757 1774 MOD_PROTO_IP },
1758 1775 { "arp_fastprobe_delay", "_arp_fastprobe_delay",
1759 1776 MOD_PROTO_IP },
1760 1777 { "arp_probe_interval", "_arp_probe_interval",
1761 1778 MOD_PROTO_IP },
1762 1779 { "arp_fastprobe_interval", "_arp_fastprobe_interval",
1763 1780 MOD_PROTO_IP },
1764 1781 { "arp_probe_count", "_arp_probe_count",
1765 1782 MOD_PROTO_IP },
1766 1783 { "arp_fastprobe_count", "_arp_fastprobe_count",
1767 1784 MOD_PROTO_IP },
1768 1785 { "arp_defend_interval", "_arp_defend_interval",
1769 1786 MOD_PROTO_IP },
1770 1787 { "arp_defend_rate", "_arp_defend_rate",
1771 1788 MOD_PROTO_IP },
1772 1789 { "arp_defend_period", "_arp_defend_period",
1773 1790 MOD_PROTO_IP },
1774 1791 { "ndp_defend_interval", "_ndp_defend_interval",
1775 1792 MOD_PROTO_IP },
1776 1793 { "ndp_defend_rate", "_ndp_defend_rate",
1777 1794 MOD_PROTO_IP },
1778 1795 { "ndp_defend_period", "_ndp_defend_period",
1779 1796 MOD_PROTO_IP },
1780 1797 { "igmp_max_version", "_igmp_max_version",
1781 1798 MOD_PROTO_IP },
1782 1799 { "mld_max_version", "_mld_max_version",
1783 1800 MOD_PROTO_IP },
1784 1801 { "ipsec_override_persocket_policy", "_ipsec_override_persocket_policy",
1785 1802 MOD_PROTO_IP },
1786 1803 { "ipsec_policy_log_interval", "_ipsec_policy_log_interval",
1787 1804 MOD_PROTO_IP },
1788 1805 { "icmp_accept_clear_messages", "_icmp_accept_clear_messages",
1789 1806 MOD_PROTO_IP },
1790 1807 { "igmp_accept_clear_messages", "_igmp_accept_clear_messages",
1791 1808 MOD_PROTO_IP },
1792 1809 { "pim_accept_clear_messages", "_pim_accept_clear_messages",
1793 1810 MOD_PROTO_IP },
1794 1811 { "ip_respond_to_echo_multicast", "_respond_to_echo_multicast",
1795 1812 MOD_PROTO_IPV4 },
1796 1813 { "ip_send_redirects", "_send_redirects",
1797 1814 MOD_PROTO_IPV4 },
1798 1815 { "ip_forward_src_routed", "_forward_src_routed",
1799 1816 MOD_PROTO_IPV4 },
1800 1817 { "ip_icmp_return_data_bytes", "_icmp_return_data_bytes",
1801 1818 MOD_PROTO_IPV4 },
1802 1819 { "ip_ignore_redirect", "_ignore_redirect",
1803 1820 MOD_PROTO_IPV4 },
1804 1821 { "ip_strict_dst_multihoming", "_strict_dst_multihoming",
1805 1822 MOD_PROTO_IPV4 },
1806 1823 { "ip_reasm_timeout", "_reasm_timeout",
1807 1824 MOD_PROTO_IPV4 },
1808 1825 { "ip_strict_src_multihoming", "_strict_src_multihoming",
1809 1826 MOD_PROTO_IPV4 },
1810 1827 { "ipv4_dad_announce_interval", "_dad_announce_interval",
1811 1828 MOD_PROTO_IPV4 },
1812 1829 { "ipv4_icmp_return_pmtu", "_icmp_return_pmtu",
1813 1830 MOD_PROTO_IPV4 },
1814 1831 { "ipv6_dad_announce_interval", "_dad_announce_interval",
1815 1832 MOD_PROTO_IPV6 },
1816 1833 { "ipv6_icmp_return_pmtu", "_icmp_return_pmtu",
1817 1834 MOD_PROTO_IPV6 },
1818 1835 { NULL, NULL, MOD_PROTO_NONE }
1819 1836 };
1820 1837
1821 1838 /*
1822 1839 * Following API returns a new property name in `nname' for the given legacy
1823 1840 * property name in `oname'.
1824 1841 */
1825 1842 int
1826 1843 ipadm_legacy2new_propname(const char *oname, char *nname, uint_t nnamelen,
1827 1844 uint_t *proto)
1828 1845 {
1829 1846 const char *str;
1830 1847 ipadm_oname2nname_map_t *ionmp;
1831 1848
1832 1849 /* if it's a public property, there is nothing to return */
1833 1850 if (i_ipadm_get_prop_desc(oname, *proto, NULL) != NULL)
1834 1851 return (-1);
1835 1852
1836 1853 /*
1837 1854 * we didn't find the `oname' in the table, check if the property
1838 1855 * name begins with a leading protocol.
1839 1856 */
1840 1857 str = oname;
1841 1858 switch (*proto) {
1842 1859 case MOD_PROTO_TCP:
1843 1860 if (strstr(oname, "tcp_") == oname)
1844 1861 str += strlen("tcp");
1845 1862 break;
1846 1863 case MOD_PROTO_SCTP:
1847 1864 if (strstr(oname, "sctp_") == oname)
1848 1865 str += strlen("sctp");
1849 1866 break;
1850 1867 case MOD_PROTO_UDP:
1851 1868 if (strstr(oname, "udp_") == oname)
1852 1869 str += strlen("udp");
1853 1870 break;
1854 1871 case MOD_PROTO_RAWIP:
1855 1872 if (strstr(oname, "icmp_") == oname)
1856 1873 str += strlen("icmp");
1857 1874 break;
1858 1875 case MOD_PROTO_IP:
1859 1876 case MOD_PROTO_IPV4:
1860 1877 case MOD_PROTO_IPV6:
1861 1878 if (strstr(oname, "ip6_") == oname) {
1862 1879 *proto = MOD_PROTO_IPV6;
1863 1880 str += strlen("ip6");
1864 1881 } else {
1865 1882 for (ionmp = name_map; ionmp->iom_oname != NULL;
1866 1883 ionmp++) {
1867 1884 if (strcmp(oname, ionmp->iom_oname) == 0) {
1868 1885 str = ionmp->iom_nname;
1869 1886 *proto = ionmp->iom_proto;
1870 1887 break;
1871 1888 }
1872 1889 }
1873 1890 if (ionmp->iom_oname != NULL)
1874 1891 break;
1875 1892
1876 1893 if (strstr(oname, "ip_") == oname) {
1877 1894 *proto = MOD_PROTO_IP;
1878 1895 str += strlen("ip");
1879 1896 }
1880 1897 }
1881 1898 break;
1882 1899 default:
1883 1900 return (-1);
1884 1901 }
1885 1902 (void) snprintf(nname, nnamelen, "%s", str);
1886 1903 return (0);
1887 1904 }
1888 1905
1889 1906 /*
1890 1907 * Following API is required for ndd.c alone. To maintain backward
1891 1908 * compatibility with ndd output, we need to print the legacy name
1892 1909 * for the new name.
1893 1910 */
1894 1911 int
1895 1912 ipadm_new2legacy_propname(const char *oname, char *nname,
1896 1913 uint_t nnamelen, uint_t proto)
1897 1914 {
1898 1915 char *prefix;
1899 1916 ipadm_oname2nname_map_t *ionmp;
1900 1917
1901 1918 /* if it's a public property, there is nothing to prepend */
1902 1919 if (i_ipadm_get_prop_desc(oname, proto, NULL) != NULL)
1903 1920 return (-1);
1904 1921
1905 1922 switch (proto) {
1906 1923 case MOD_PROTO_TCP:
1907 1924 prefix = "tcp";
1908 1925 break;
1909 1926 case MOD_PROTO_SCTP:
1910 1927 prefix = "sctp";
1911 1928 break;
1912 1929 case MOD_PROTO_UDP:
1913 1930 prefix = "udp";
1914 1931 break;
1915 1932 case MOD_PROTO_RAWIP:
1916 1933 prefix = "icmp";
1917 1934 break;
1918 1935 case MOD_PROTO_IP:
1919 1936 case MOD_PROTO_IPV4:
1920 1937 case MOD_PROTO_IPV6:
1921 1938 /* handle special case for IP */
1922 1939 for (ionmp = name_map; ionmp->iom_oname != NULL; ionmp++) {
1923 1940 if (strcmp(oname, ionmp->iom_nname) == 0 &&
1924 1941 ionmp->iom_proto == proto) {
1925 1942 (void) strlcpy(nname, ionmp->iom_oname,
1926 1943 nnamelen);
1927 1944 return (0);
1928 1945 }
1929 1946 }
1930 1947 if (proto == MOD_PROTO_IPV6)
1931 1948 prefix = "ip6";
1932 1949 else
1933 1950 prefix = "ip";
1934 1951 break;
1935 1952 default:
1936 1953 return (-1);
1937 1954 }
1938 1955 (void) snprintf(nname, nnamelen, "%s%s", prefix, oname);
1939 1956 return (0);
1940 1957 }
↓ open down ↓ |
1639 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX