xref: /titanic_44/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_persist.c (revision 6a634c9dca3093f3922e4b7ab826d7bdf17bf78e)
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 /*
23  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
24  */
25 
26 /*
27  * Contains DB walker functions, which are of type `db_wfunc_t';
28  *
29  * typedef boolean_t db_wfunc_t(void *cbarg, nvlist_t *db_nvl, char *buf,
30  *				size_t bufsize, int *errp);
31  *
32  * ipadm_rw_db() walks through the data store, one line at a time and calls
33  * these call back functions with:
34  *	`cbarg'  - callback argument
35  *	`db_nvl' - representing a line from DB in nvlist_t form
36  *	`buf'	 - character buffer to hold modified line
37  *	`bufsize'- size of the buffer
38  *	`errp' - captures any error inside the walker function.
39  *
40  * All the 'write' callback functions modify `db_nvl' based on `cbarg' and
41  * copy string representation of `db_nvl' (using ipadm_nvlist2str()) into `buf'.
42  * To delete a line from the DB, buf[0] is set to `\0'. Inside ipadm_rw_db(),
43  * the modified `buf' is written back into DB.
44  *
45  * All the 'read' callback functions, retrieve the information from the DB, by
46  * reading `db_nvl' and then populate the `cbarg'.
47  */
48 
49 #include <stdlib.h>
50 #include <strings.h>
51 #include <errno.h>
52 #include <assert.h>
53 #include <sys/types.h>
54 #include <sys/socket.h>
55 #include <netinet/in.h>
56 #include <arpa/inet.h>
57 #include <unistd.h>
58 #include "ipmgmt_impl.h"
59 
60 /* SCF related property group names and property names */
61 #define	IPMGMTD_APP_PG		"ipmgmtd"
62 #define	IPMGMTD_PROP_FBD	"first_boot_done"
63 #define	IPMGMTD_PROP_DBVER	"datastore_version"
64 #define	IPMGMTD_TRUESTR		"true"
65 
66 #define	ATYPE	"_atype"		/* name of the address type nvpair */
67 #define	FLAGS	"_flags"		/* name of the flags nvpair */
68 
69 /*
70  * flag used by ipmgmt_persist_aobjmap() to indicate address type is
71  * IPADM_ADDR_IPV6_ADDRCONF.
72  */
73 #define	IPMGMT_ATYPE_V6ACONF	0x1
74 
75 extern pthread_rwlock_t ipmgmt_dbconf_lock;
76 
77 /* signifies whether volatile copy of data store is in use */
78 static boolean_t ipmgmt_rdonly_root = B_FALSE;
79 
80 /*
81  * Checks if the database nvl, `db_nvl' contains and matches ALL of the passed
82  * in private nvpairs `proto', `ifname' & `aobjname'.
83  */
84 static boolean_t
ipmgmt_nvlist_match(nvlist_t * db_nvl,const char * proto,const char * ifname,const char * aobjname)85 ipmgmt_nvlist_match(nvlist_t *db_nvl, const char *proto, const char *ifname,
86     const char *aobjname)
87 {
88 	char		*db_proto = NULL, *db_ifname = NULL;
89 	char		*db_aobjname = NULL;
90 	nvpair_t	*nvp;
91 	char		*name;
92 
93 	/* walk through db_nvl and retrieve all its private nvpairs */
94 	for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL;
95 	    nvp = nvlist_next_nvpair(db_nvl, nvp)) {
96 		name = nvpair_name(nvp);
97 		if (strcmp(IPADM_NVP_PROTONAME, name) == 0)
98 			(void) nvpair_value_string(nvp, &db_proto);
99 		else if (strcmp(IPADM_NVP_IFNAME, name) == 0)
100 			(void) nvpair_value_string(nvp, &db_ifname);
101 		else if (strcmp(IPADM_NVP_AOBJNAME, name) == 0)
102 			(void) nvpair_value_string(nvp, &db_aobjname);
103 	}
104 
105 	if (proto != NULL && proto[0] == '\0')
106 		proto = NULL;
107 	if (ifname != NULL && ifname[0] == '\0')
108 		ifname = NULL;
109 	if (aobjname != NULL && aobjname[0] == '\0')
110 		aobjname = NULL;
111 
112 	if ((proto == NULL && db_proto != NULL) ||
113 	    (proto != NULL && db_proto == NULL) ||
114 	    strcmp(proto, db_proto) != 0) {
115 		/* no intersection - different protocols. */
116 		return (B_FALSE);
117 	}
118 	if ((ifname == NULL && db_ifname != NULL) ||
119 	    (ifname != NULL && db_ifname == NULL) ||
120 	    strcmp(ifname, db_ifname) != 0) {
121 		/* no intersection - different interfaces. */
122 		return (B_FALSE);
123 	}
124 	if ((aobjname == NULL && db_aobjname != NULL) ||
125 	    (aobjname != NULL && db_aobjname == NULL) ||
126 	    strcmp(aobjname, db_aobjname) != 0) {
127 		/* no intersection - different address objects */
128 		return (B_FALSE);
129 	}
130 
131 	return (B_TRUE);
132 }
133 
134 /*
135  * Checks if the database nvl, `db_nvl' and the input nvl, `in_nvl' intersects.
136  */
137 static boolean_t
ipmgmt_nvlist_intersects(nvlist_t * db_nvl,nvlist_t * in_nvl)138 ipmgmt_nvlist_intersects(nvlist_t *db_nvl, nvlist_t *in_nvl)
139 {
140 	nvpair_t	*nvp;
141 	char		*name;
142 	char		*proto = NULL, *ifname = NULL, *aobjname = NULL;
143 
144 	/* walk through in_nvl and retrieve all its private nvpairs */
145 	for (nvp = nvlist_next_nvpair(in_nvl, NULL); nvp != NULL;
146 	    nvp = nvlist_next_nvpair(in_nvl, nvp)) {
147 		name = nvpair_name(nvp);
148 		if (strcmp(IPADM_NVP_PROTONAME, name) == 0)
149 			(void) nvpair_value_string(nvp, &proto);
150 		else if (strcmp(IPADM_NVP_IFNAME, name) == 0)
151 			(void) nvpair_value_string(nvp, &ifname);
152 		else if (strcmp(IPADM_NVP_AOBJNAME, name) == 0)
153 			(void) nvpair_value_string(nvp, &aobjname);
154 	}
155 
156 	return (ipmgmt_nvlist_match(db_nvl, proto, ifname, aobjname));
157 }
158 
159 /*
160  * Checks if the database nvl, `db_nvl', contains and matches ANY of the passed
161  * in private nvpairs `proto', `ifname' & `aobjname'.
162  */
163 static boolean_t
ipmgmt_nvlist_contains(nvlist_t * db_nvl,const char * proto,const char * ifname,char * aobjname)164 ipmgmt_nvlist_contains(nvlist_t *db_nvl, const char *proto,
165     const char *ifname, char *aobjname)
166 {
167 	char		*db_ifname = NULL, *db_proto = NULL;
168 	char		*db_aobjname = NULL;
169 	nvpair_t	*nvp;
170 	char		*name;
171 
172 	/* walk through db_nvl and retrieve all private nvpairs */
173 	for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL;
174 	    nvp = nvlist_next_nvpair(db_nvl, nvp)) {
175 		name = nvpair_name(nvp);
176 		if (strcmp(IPADM_NVP_PROTONAME, name) == 0)
177 			(void) nvpair_value_string(nvp, &db_proto);
178 		else if (strcmp(IPADM_NVP_IFNAME, name) == 0)
179 			(void) nvpair_value_string(nvp, &db_ifname);
180 		else if (strcmp(IPADM_NVP_AOBJNAME, name) == 0)
181 			(void) nvpair_value_string(nvp, &db_aobjname);
182 	}
183 
184 	if (proto != NULL && proto[0] != '\0') {
185 		if ((db_proto == NULL || strcmp(proto, db_proto) != 0))
186 			return (B_FALSE);
187 	}
188 	if (ifname != NULL && ifname[0] != '\0') {
189 		if ((db_ifname == NULL || strcmp(ifname, db_ifname) != 0))
190 			return (B_FALSE);
191 	}
192 	if (aobjname != NULL && aobjname[0] != '\0') {
193 		if ((db_aobjname == NULL || strcmp(aobjname, db_aobjname) != 0))
194 			return (B_FALSE);
195 	}
196 
197 	return (B_TRUE);
198 }
199 
200 /*
201  * Retrieves the property value from the DB. The property whose value is to be
202  * retrieved is in `pargp->ia_pname'.
203  */
204 /* ARGSUSED */
205 boolean_t
ipmgmt_db_getprop(void * arg,nvlist_t * db_nvl,char * buf,size_t buflen,int * errp)206 ipmgmt_db_getprop(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
207     int *errp)
208 {
209 	ipmgmt_prop_arg_t	*pargp = arg;
210 	boolean_t		cont = B_TRUE;
211 	char			*pval;
212 	int			err = 0;
213 
214 	*errp = 0;
215 
216 	if (!ipmgmt_nvlist_match(db_nvl, pargp->ia_module,
217 	    pargp->ia_ifname, pargp->ia_aobjname))
218 		return (B_TRUE);
219 
220 	if ((err = nvlist_lookup_string(db_nvl, pargp->ia_pname,
221 	    &pval)) == 0) {
222 		(void) strlcpy(pargp->ia_pval, pval, sizeof (pargp->ia_pval));
223 		/*
224 		 * We have retrieved what we are looking for.
225 		 * Stop the walker.
226 		 */
227 		cont = B_FALSE;
228 	} else {
229 		if (err == ENOENT)
230 			err = 0;
231 		*errp = err;
232 	}
233 
234 	return (cont);
235 }
236 
237 /*
238  * Removes the property value from the DB. The property whose value is to be
239  * removed is in `pargp->ia_pname'.
240  */
241 /* ARGSUSED */
242 boolean_t
ipmgmt_db_resetprop(void * arg,nvlist_t * db_nvl,char * buf,size_t buflen,int * errp)243 ipmgmt_db_resetprop(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
244     int *errp)
245 {
246 	ipmgmt_prop_arg_t	*pargp = arg;
247 
248 	*errp = 0;
249 	if (!ipmgmt_nvlist_match(db_nvl, pargp->ia_module,
250 	    pargp->ia_ifname, pargp->ia_aobjname))
251 		return (B_TRUE);
252 
253 	if (!nvlist_exists(db_nvl, pargp->ia_pname))
254 		return (B_TRUE);
255 
256 	/*
257 	 * We found the property in the DB. If IPMGMT_REMOVE is not set then
258 	 * delete the entry from the db. If it is set, then the property is a
259 	 * multi-valued property so just remove the specified values from DB.
260 	 */
261 	if (pargp->ia_flags & IPMGMT_REMOVE) {
262 		char	*dbpval = NULL;
263 		char	*inpval = pargp->ia_pval;
264 		char	pval[MAXPROPVALLEN];
265 		char	*val, *lasts;
266 
267 		*errp = nvlist_lookup_string(db_nvl, pargp->ia_pname, &dbpval);
268 		if (*errp != 0)
269 			return (B_FALSE);
270 
271 		/*
272 		 * multi-valued properties are represented as comma separated
273 		 * values. Use string tokenizer functions to split them and
274 		 * search for the value to be removed.
275 		 */
276 		bzero(pval, sizeof (pval));
277 		if ((val = strtok_r(dbpval, ",", &lasts)) != NULL) {
278 			if (strcmp(val, inpval) != 0)
279 				(void) strlcat(pval, val, MAXPROPVALLEN);
280 			while ((val = strtok_r(NULL, ",", &lasts)) != NULL) {
281 				if (strcmp(val, inpval) != 0) {
282 					if (pval[0] != '\0')
283 						(void) strlcat(pval, ",",
284 						    MAXPROPVALLEN);
285 					(void) strlcat(pval, val,
286 					    MAXPROPVALLEN);
287 				}
288 			}
289 		} else {
290 			if (strcmp(dbpval, inpval) != 0)
291 				*errp = ENOENT;
292 			else
293 				buf[0] =  '\0';
294 			return (B_FALSE);
295 		}
296 		*errp = nvlist_add_string(db_nvl, pargp->ia_pname, pval);
297 		if (*errp != 0)
298 			return (B_FALSE);
299 
300 		(void) memset(buf, 0, buflen);
301 		if (ipadm_nvlist2str(db_nvl, buf, buflen) == 0) {
302 			/* buffer overflow */
303 			*errp = ENOBUFS;
304 		}
305 	} else {
306 		buf[0] = '\0';
307 	}
308 
309 	/* stop the search */
310 	return (B_FALSE);
311 }
312 
313 /*
314  * Input arguments can have IPADM_NVP_AOBJNAME or IPADM_NVP_IFNAME. A match is
315  * found, when one of the following occurs first.
316  * - the input aobjname matches the db aobjname. Return the db address.
317  * - the input interface matches the db interface. Return all the
318  *   matching db lines with addresses.
319  */
320 /* ARGSUSED */
321 boolean_t
ipmgmt_db_getaddr(void * arg,nvlist_t * db_nvl,char * buf,size_t buflen,int * errp)322 ipmgmt_db_getaddr(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
323     int *errp)
324 {
325 	ipmgmt_getaddr_cbarg_t	*cbarg = arg;
326 	char		*db_aobjname = NULL;
327 	char		*db_ifname = NULL;
328 	nvlist_t	*db_addr = NULL;
329 	char		name[IPMGMT_STRSIZE];
330 	nvpair_t	*nvp;
331 	boolean_t	add_nvl = B_FALSE;
332 
333 	/* Parse db nvlist */
334 	for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL;
335 	    nvp = nvlist_next_nvpair(db_nvl, nvp)) {
336 		if (nvpair_type(nvp) == DATA_TYPE_NVLIST)
337 			(void) nvpair_value_nvlist(nvp, &db_addr);
338 		else if (strcmp(nvpair_name(nvp), IPADM_NVP_IFNAME) == 0)
339 			(void) nvpair_value_string(nvp, &db_ifname);
340 		else if (strcmp(nvpair_name(nvp), IPADM_NVP_AOBJNAME) == 0)
341 			(void) nvpair_value_string(nvp, &db_aobjname);
342 	}
343 
344 	if (db_aobjname == NULL) /* Not an address */
345 		return (B_TRUE);
346 
347 	/* Check for a match between the aobjnames or the interface name */
348 	if (cbarg->cb_aobjname[0] != '\0') {
349 		if (strcmp(cbarg->cb_aobjname, db_aobjname) == 0)
350 			add_nvl = B_TRUE;
351 	} else if (cbarg->cb_ifname[0] != '\0') {
352 		if (strcmp(cbarg->cb_ifname, db_ifname) == 0)
353 			add_nvl = B_TRUE;
354 	} else {
355 		add_nvl = B_TRUE;
356 	}
357 
358 	if (add_nvl) {
359 		(void) snprintf(name, sizeof (name), "%s_%d", db_ifname,
360 		    cbarg->cb_ocnt);
361 		*errp = nvlist_add_nvlist(cbarg->cb_onvl, name, db_nvl);
362 		if (*errp == 0)
363 			cbarg->cb_ocnt++;
364 	}
365 	return (B_TRUE);
366 }
367 
368 /*
369  * This function only gets called if a volatile filesystem version
370  * of the configuration file has been created. This only happens in the
371  * extremely rare case that a request has been made to update the configuration
372  * file at boottime while the root filesystem was read-only. This is
373  * really a rare occurrence now that we don't support UFS root filesystems
374  * any longer. This function will periodically attempt to write the
375  * configuration back to its location on the root filesystem. Success
376  * will indicate that the filesystem is no longer read-only.
377  */
378 /* ARGSUSED */
379 static void *
ipmgmt_db_restore_thread(void * arg)380 ipmgmt_db_restore_thread(void *arg)
381 {
382 	int err;
383 
384 	for (;;) {
385 		(void) sleep(5);
386 		(void) pthread_rwlock_wrlock(&ipmgmt_dbconf_lock);
387 		if (!ipmgmt_rdonly_root)
388 			break;
389 		err = ipmgmt_cpfile(IPADM_VOL_DB_FILE, IPADM_DB_FILE, B_FALSE);
390 		if (err == 0) {
391 			ipmgmt_rdonly_root = B_FALSE;
392 			break;
393 		}
394 		(void) pthread_rwlock_unlock(&ipmgmt_dbconf_lock);
395 	}
396 	(void) pthread_rwlock_unlock(&ipmgmt_dbconf_lock);
397 	return (NULL);
398 }
399 
400 /*
401  * This function takes the appropriate lock, read or write, based on the
402  * `db_op' and then calls DB walker ipadm_rw_db(). The code is complicated
403  * by the fact that we are not always guaranteed to have a writable root
404  * filesystem since it is possible that we are reading or writing during
405  * bootime while the root filesystem is still read-only. This is, by far,
406  * the exception case. Normally, this function will be called when the
407  * root filesystem is writable. In the unusual case where this is not
408  * true, the configuration file is copied to the volatile file system
409  * and is updated there until the root filesystem becomes writable. At
410  * that time the file will be moved back to its proper location by
411  * ipmgmt_db_restore_thread().
412  */
413 extern int
ipmgmt_db_walk(db_wfunc_t * db_walk_func,void * db_warg,ipadm_db_op_t db_op)414 ipmgmt_db_walk(db_wfunc_t *db_walk_func, void *db_warg, ipadm_db_op_t db_op)
415 {
416 	int		err;
417 	boolean_t	writeop;
418 	mode_t		mode;
419 	pthread_t	tid;
420 	pthread_attr_t	attr;
421 
422 	writeop = (db_op != IPADM_DB_READ);
423 	if (writeop) {
424 		(void) pthread_rwlock_wrlock(&ipmgmt_dbconf_lock);
425 		mode = IPADM_FILE_MODE;
426 	} else {
427 		(void) pthread_rwlock_rdlock(&ipmgmt_dbconf_lock);
428 		mode = 0;
429 	}
430 
431 	/*
432 	 * Did a previous write attempt fail? If so, don't even try to
433 	 * read/write to IPADM_DB_FILE.
434 	 */
435 	if (!ipmgmt_rdonly_root) {
436 		err = ipadm_rw_db(db_walk_func, db_warg, IPADM_DB_FILE,
437 		    mode, db_op);
438 		if (err != EROFS)
439 			goto done;
440 	}
441 
442 	/*
443 	 * If we haven't already copied the file to the volatile
444 	 * file system, do so. This should only happen on a failed
445 	 * writeop(i.e., we have acquired the write lock above).
446 	 */
447 	if (access(IPADM_VOL_DB_FILE, F_OK) != 0) {
448 		assert(writeop);
449 		err = ipmgmt_cpfile(IPADM_DB_FILE, IPADM_VOL_DB_FILE, B_TRUE);
450 		if (err != 0)
451 			goto done;
452 		(void) pthread_attr_init(&attr);
453 		(void) pthread_attr_setdetachstate(&attr,
454 		    PTHREAD_CREATE_DETACHED);
455 		err = pthread_create(&tid, &attr, ipmgmt_db_restore_thread,
456 		    NULL);
457 		(void) pthread_attr_destroy(&attr);
458 		if (err != 0) {
459 			(void) unlink(IPADM_VOL_DB_FILE);
460 			goto done;
461 		}
462 		ipmgmt_rdonly_root = B_TRUE;
463 	}
464 
465 	/*
466 	 * Read/write from the volatile copy.
467 	 */
468 	err = ipadm_rw_db(db_walk_func, db_warg, IPADM_VOL_DB_FILE,
469 	    mode, db_op);
470 done:
471 	(void) pthread_rwlock_unlock(&ipmgmt_dbconf_lock);
472 	return (err);
473 }
474 
475 /*
476  * Used to add an entry towards the end of DB. It just returns B_TRUE for
477  * every line of the DB. When we reach the end, ipadm_rw_db() adds the
478  * line at the end.
479  */
480 /* ARGSUSED */
481 boolean_t
ipmgmt_db_add(void * arg,nvlist_t * db_nvl,char * buf,size_t buflen,int * errp)482 ipmgmt_db_add(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, int *errp)
483 {
484 	return (B_TRUE);
485 }
486 
487 /*
488  * This function is used to update or create an entry in DB. The nvlist_t,
489  * `in_nvl', represents the line we are looking for. Once we ensure the right
490  * line from DB, we update that entry.
491  */
492 boolean_t
ipmgmt_db_update(void * arg,nvlist_t * db_nvl,char * buf,size_t buflen,int * errp)493 ipmgmt_db_update(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
494     int *errp)
495 {
496 	ipadm_dbwrite_cbarg_t	*cb = arg;
497 	uint_t			flags = cb->dbw_flags;
498 	nvlist_t		*in_nvl = cb->dbw_nvl;
499 	nvpair_t		*nvp;
500 	char			*name, *instrval = NULL, *dbstrval = NULL;
501 	char			pval[MAXPROPVALLEN];
502 
503 	*errp = 0;
504 	if (!ipmgmt_nvlist_intersects(db_nvl, in_nvl))
505 		return (B_TRUE);
506 
507 	for (nvp = nvlist_next_nvpair(in_nvl, NULL); nvp != NULL;
508 	    nvp = nvlist_next_nvpair(in_nvl, nvp)) {
509 		name = nvpair_name(nvp);
510 		if (!IPADM_PRIV_NVP(name) && nvlist_exists(db_nvl, name))
511 			break;
512 	}
513 
514 	if (nvp == NULL)
515 		return (B_TRUE);
516 
517 	assert(nvpair_type(nvp) == DATA_TYPE_STRING);
518 
519 	if ((*errp = nvpair_value_string(nvp, &instrval)) != 0)
520 		return (B_FALSE);
521 
522 	/*
523 	 * If IPMGMT_APPEND is set then we are dealing with multi-valued
524 	 * properties. We append to the entry from the db, with the new value.
525 	 */
526 	if (flags & IPMGMT_APPEND) {
527 		if ((*errp = nvlist_lookup_string(db_nvl, name,
528 		    &dbstrval)) != 0)
529 			return (B_FALSE);
530 		(void) snprintf(pval, MAXPROPVALLEN, "%s,%s", dbstrval,
531 		    instrval);
532 		if ((*errp = nvlist_add_string(db_nvl, name, pval)) != 0)
533 			return (B_FALSE);
534 	} else {
535 		/* case	of in-line update of a db entry */
536 		if ((*errp = nvlist_add_string(db_nvl, name, instrval)) != 0)
537 			return (B_FALSE);
538 	}
539 
540 	(void) memset(buf, 0, buflen);
541 	if (ipadm_nvlist2str(db_nvl, buf, buflen) == 0) {
542 		/* buffer overflow */
543 		*errp = ENOBUFS;
544 	}
545 
546 	/* we updated the DB entry, so do not continue */
547 	return (B_FALSE);
548 }
549 
550 /*
551  * For the given `cbarg->cb_ifname' interface, retrieves any persistent
552  * interface information (used in 'ipadm show-if')
553  */
554 /* ARGSUSED */
555 boolean_t
ipmgmt_db_getif(void * arg,nvlist_t * db_nvl,char * buf,size_t buflen,int * errp)556 ipmgmt_db_getif(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
557     int *errp)
558 {
559 	ipmgmt_getif_cbarg_t	*cbarg = arg;
560 	char			*ifname = cbarg->cb_ifname;
561 	char			*intf = NULL;
562 	ipadm_if_info_t		*ifp = NULL;
563 	sa_family_t		af;
564 	char			*afstr;
565 
566 	*errp = 0;
567 	if (nvlist_lookup_string(db_nvl, IPADM_NVP_FAMILY, &afstr) != 0 ||
568 	    nvlist_lookup_string(db_nvl, IPADM_NVP_IFNAME, &intf) != 0 ||
569 	    (ifname[0] != '\0' && strcmp(ifname, intf) != 0)) {
570 		return (B_TRUE);
571 	}
572 	af = atoi(afstr);
573 	for (ifp = cbarg->cb_ifinfo; ifp != NULL; ifp = ifp->ifi_next) {
574 		if (strcmp(ifp->ifi_name, intf) == 0)
575 			break;
576 	}
577 	if (ifp == NULL) {
578 		ipadm_if_info_t *new;
579 
580 		if ((new = calloc(1, sizeof (*new))) == NULL) {
581 			*errp = ENOMEM;
582 			return (B_FALSE); /* don't continue the walk */
583 		}
584 		new->ifi_next = cbarg->cb_ifinfo;
585 		cbarg->cb_ifinfo = new;
586 		ifp = new;
587 		(void) strlcpy(ifp->ifi_name, intf, sizeof (ifp->ifi_name));
588 	}
589 
590 	if (af == AF_INET) {
591 		ifp->ifi_pflags |= IFIF_IPV4;
592 	} else {
593 		assert(af == AF_INET6);
594 		ifp->ifi_pflags |= IFIF_IPV6;
595 	}
596 
597 	/* Terminate the walk if we found both v4 and v6 interfaces. */
598 	if (ifname[0] != '\0' && (ifp->ifi_pflags & IFIF_IPV4) &&
599 	    (ifp->ifi_pflags & IFIF_IPV6))
600 		return (B_FALSE);
601 
602 	return (B_TRUE);
603 }
604 
605 /*
606  * Deletes those entries from the database for which interface name
607  * matches with the given `cbarg->cb_ifname'
608  */
609 /* ARGSUSED */
610 boolean_t
ipmgmt_db_resetif(void * arg,nvlist_t * db_nvl,char * buf,size_t buflen,int * errp)611 ipmgmt_db_resetif(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
612     int *errp)
613 {
614 	ipmgmt_if_cbarg_t *cbarg = arg;
615 	boolean_t	isv6 = (cbarg->cb_family == AF_INET6);
616 	char		*ifname = cbarg->cb_ifname;
617 	char		*modstr = NULL;
618 	char		*afstr;
619 	char		*aobjname;
620 	uint_t		proto;
621 	ipmgmt_aobjmap_t *head;
622 	boolean_t	aobjfound = B_FALSE;
623 
624 	*errp = 0;
625 
626 	if (!ipmgmt_nvlist_contains(db_nvl, NULL, ifname, NULL))
627 		return (B_TRUE);
628 
629 	if (nvlist_lookup_string(db_nvl, IPADM_NVP_FAMILY, &afstr) == 0) {
630 		if (atoi(afstr) == cbarg->cb_family)
631 			goto delete;
632 		return (B_TRUE);
633 	}
634 
635 	/* Reset all the interface configurations for 'ifname' */
636 	if (isv6 && (nvlist_exists(db_nvl, IPADM_NVP_IPV6ADDR) ||
637 	    nvlist_exists(db_nvl, IPADM_NVP_INTFID))) {
638 		goto delete;
639 	}
640 	if (!isv6 &&
641 	    (nvlist_exists(db_nvl, IPADM_NVP_IPV4ADDR) ||
642 	    nvlist_exists(db_nvl, IPADM_NVP_DHCP))) {
643 		goto delete;
644 	}
645 
646 	if (nvlist_lookup_string(db_nvl, IPADM_NVP_AOBJNAME, &aobjname) == 0) {
647 		/*
648 		 * This must be an address property. Delete this
649 		 * line if there is a match in the address family.
650 		 */
651 		head = aobjmap.aobjmap_head;
652 		while (head != NULL) {
653 			if (strcmp(head->am_aobjname, aobjname) == 0) {
654 				aobjfound = B_TRUE;
655 				if (head->am_family == cbarg->cb_family)
656 					goto delete;
657 			}
658 			head = head->am_next;
659 		}
660 		/*
661 		 * If aobjfound = B_FALSE, then this address is not
662 		 * available in active configuration. We should go ahead
663 		 * and delete it.
664 		 */
665 		if (!aobjfound)
666 			goto delete;
667 	}
668 
669 	/*
670 	 * If we are removing both v4 and v6 interface, then we get rid of
671 	 * all the properties for that interface. On the other hand, if we
672 	 * are deleting only v4 instance of an interface, then we delete v4
673 	 * properties only.
674 	 */
675 	if (nvlist_lookup_string(db_nvl, IPADM_NVP_PROTONAME, &modstr) == 0) {
676 		proto = ipadm_str2proto(modstr);
677 		switch (proto) {
678 		case MOD_PROTO_IPV6:
679 			if (isv6)
680 				goto delete;
681 			break;
682 		case MOD_PROTO_IPV4:
683 			if (!isv6)
684 				goto delete;
685 			break;
686 		case MOD_PROTO_IP:
687 			/* this should never be the case, today */
688 			assert(0);
689 			break;
690 		}
691 	}
692 	/* Not found a match yet. Continue processing the db */
693 	return (B_TRUE);
694 delete:
695 	/* delete the line from the db */
696 	buf[0] = '\0';
697 	return (B_TRUE);
698 }
699 
700 /*
701  * Deletes those entries from the database for which address object name
702  * matches with the given `cbarg->cb_aobjname'
703  */
704 /* ARGSUSED */
705 boolean_t
ipmgmt_db_resetaddr(void * arg,nvlist_t * db_nvl,char * buf,size_t buflen,int * errp)706 ipmgmt_db_resetaddr(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
707     int *errp)
708 {
709 	ipmgmt_resetaddr_cbarg_t *cbarg = arg;
710 	char		*aobjname = cbarg->cb_aobjname;
711 
712 	*errp = 0;
713 	if (!ipmgmt_nvlist_contains(db_nvl, NULL, NULL, aobjname))
714 		return (B_TRUE);
715 
716 	/* delete the line from the db */
717 	buf[0] = '\0';
718 	return (B_TRUE);
719 }
720 
721 /*
722  * Retrieves all interface props, including addresses, for given interface(s).
723  * `invl' contains the list of interfaces, for which information need to be
724  * retrieved.
725  */
726 /* ARGSUSED */
727 boolean_t
ipmgmt_db_initif(void * arg,nvlist_t * db_nvl,char * buf,size_t buflen,int * errp)728 ipmgmt_db_initif(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
729     int *errp)
730 {
731 	ipmgmt_initif_cbarg_t	*cbarg = arg;
732 	nvlist_t		*onvl = cbarg->cb_onvl;
733 	nvlist_t		*invl = cbarg->cb_invl;
734 	sa_family_t		in_af = cbarg->cb_family;
735 	char			*db_ifname;
736 
737 	*errp = 0;
738 	if (nvlist_lookup_string(db_nvl, IPADM_NVP_IFNAME, &db_ifname) == 0 &&
739 	    nvlist_exists(invl, db_ifname)) {
740 		char		name[IPMGMT_STRSIZE];
741 		sa_family_t	db_af = in_af;
742 		uint_t		proto;
743 		char		*pstr;
744 
745 		if (in_af != AF_UNSPEC) {
746 			if (nvlist_lookup_string(db_nvl, IPADM_NVP_PROTONAME,
747 			    &pstr) == 0) {
748 				proto = ipadm_str2proto(pstr);
749 				if (proto == MOD_PROTO_IPV4)
750 					db_af = AF_INET;
751 				else if (proto == MOD_PROTO_IPV6)
752 					db_af = AF_INET6;
753 				else
754 					db_af = in_af;
755 			} else {
756 				if (nvlist_exists(db_nvl, IPADM_NVP_IPV4ADDR) ||
757 				    nvlist_exists(db_nvl, IPADM_NVP_DHCP))
758 					db_af = AF_INET;
759 				else
760 					db_af = AF_INET6;
761 			}
762 		}
763 		if (in_af == db_af) {
764 			(void) snprintf(name, sizeof (name), "%s_%d", db_ifname,
765 			    cbarg->cb_ocnt);
766 			*errp = nvlist_add_nvlist(onvl, name, db_nvl);
767 			if (*errp == 0)
768 				cbarg->cb_ocnt++;
769 		}
770 	}
771 	return (B_TRUE);
772 }
773 
774 /*
775  * helper function for ipmgmt_aobjmap_op(). Adds the node pointed by `nodep'
776  * into `aobjmap' structure.
777  */
778 static int
i_ipmgmt_add_amnode(ipmgmt_aobjmap_t * nodep)779 i_ipmgmt_add_amnode(ipmgmt_aobjmap_t *nodep)
780 {
781 	ipmgmt_aobjmap_t	*new, *head;
782 
783 	head = aobjmap.aobjmap_head;
784 	if ((new = malloc(sizeof (ipmgmt_aobjmap_t))) == NULL)
785 		return (ENOMEM);
786 	*new = *nodep;
787 	new->am_next = NULL;
788 
789 	/* Add the node at the beginning of the list */
790 	if (head == NULL) {
791 		aobjmap.aobjmap_head = new;
792 	} else {
793 		new->am_next = aobjmap.aobjmap_head;
794 		aobjmap.aobjmap_head = new;
795 	}
796 	return (0);
797 }
798 
799 /*
800  * A recursive function to generate alphabetized number given a decimal number.
801  * Decimal 0 to 25 maps to 'a' to 'z' and then the counting continues with 'aa',
802  * 'ab', 'ac', et al.
803  */
804 static void
i_ipmgmt_num2priv_aobjname(uint32_t num,char ** cp,char * endp)805 i_ipmgmt_num2priv_aobjname(uint32_t num, char **cp, char *endp)
806 {
807 	if (num >= 26)
808 		i_ipmgmt_num2priv_aobjname(num / 26 - 1, cp, endp);
809 	if (*cp != endp) {
810 		*cp[0] = 'a' + (num % 26);
811 		(*cp)++;
812 	}
813 }
814 
815 /*
816  * This function generates an `aobjname', when required, and then does
817  * lookup-add. If `nodep->am_aobjname' is not an empty string, then it walks
818  * through the `aobjmap' to check if an address object with the same
819  * `nodep->am_aobjname' exists. If it exists, EEXIST is returned as duplicate
820  * `aobjname's are not allowed.
821  *
822  * If `nodep->am_aobjname' is an empty string then the daemon generates an
823  * `aobjname' using the `am_nextnum', which contains the next number to be
824  * used to generate `aobjname'. `am_nextnum' is converted to base26 using
825  * `a-z' alphabets in i_ipmgmt_num2priv_aobjname().
826  *
827  * `am_nextnum' will be 0 to begin with. Every time an address object that
828  * needs `aobjname' is added it's incremented by 1. So for the first address
829  * object on net0 the `am_aobjname' will be net0/_a and `am_nextnum' will be 1.
830  * For the second address object on that interface `am_aobjname' will be net0/_b
831  * and  `am_nextnum' will incremented to 2.
832  */
833 static int
i_ipmgmt_lookupadd_amnode(ipmgmt_aobjmap_t * nodep)834 i_ipmgmt_lookupadd_amnode(ipmgmt_aobjmap_t *nodep)
835 {
836 	ipmgmt_aobjmap_t	*head;
837 	uint32_t		nextnum;
838 
839 	for (head = aobjmap.aobjmap_head; head != NULL; head = head->am_next)
840 		if (strcmp(head->am_ifname, nodep->am_ifname) == 0)
841 			break;
842 	nextnum = (head == NULL ? 0 : head->am_nextnum);
843 
844 	/*
845 	 * if `aobjname' is empty, then the daemon has to generate the
846 	 * next `aobjname' for the given interface and family.
847 	 */
848 	if (nodep->am_aobjname[0] == '\0') {
849 		char tmpstr[IPADM_AOBJ_USTRSIZ - 1];  /* 1 for leading  '_' */
850 		char *cp = tmpstr;
851 		char *endp = tmpstr + sizeof (tmpstr);
852 
853 		i_ipmgmt_num2priv_aobjname(nextnum, &cp, endp);
854 
855 		if (cp == endp)
856 			return (EINVAL);
857 		cp[0] = '\0';
858 
859 		if (snprintf(nodep->am_aobjname, IPADM_AOBJSIZ, "%s/_%s",
860 		    nodep->am_ifname, tmpstr) >= IPADM_AOBJSIZ) {
861 			return (EINVAL);
862 		}
863 		nodep->am_nextnum = ++nextnum;
864 	} else {
865 		for (head = aobjmap.aobjmap_head; head != NULL;
866 		    head = head->am_next) {
867 			if (strcmp(head->am_aobjname, nodep->am_aobjname) == 0)
868 				return (EEXIST);
869 		}
870 		nodep->am_nextnum = nextnum;
871 	}
872 	return (i_ipmgmt_add_amnode(nodep));
873 }
874 
875 /*
876  * Performs following operations on the global `aobjmap' linked list.
877  * (a) ADDROBJ_ADD: add or update address object in `aobjmap'
878  * (b) ADDROBJ_DELETE: delete address object from `aobjmap'
879  * (c) ADDROBJ_LOOKUPADD: place a stub address object in `aobjmap'
880  * (d) ADDROBJ_SETLIFNUM: Sets the lifnum for an address object in `aobjmap'
881  */
882 int
ipmgmt_aobjmap_op(ipmgmt_aobjmap_t * nodep,uint32_t op)883 ipmgmt_aobjmap_op(ipmgmt_aobjmap_t *nodep, uint32_t op)
884 {
885 	ipmgmt_aobjmap_t	*head, *prev, *matched = NULL;
886 	boolean_t		update = B_TRUE;
887 	int			err = 0;
888 	ipadm_db_op_t		db_op;
889 
890 	(void) pthread_rwlock_wrlock(&aobjmap.aobjmap_rwlock);
891 
892 	head = aobjmap.aobjmap_head;
893 	switch (op) {
894 	case ADDROBJ_ADD:
895 		/*
896 		 * check for stub nodes (added by ADDROBJ_LOOKUPADD) and
897 		 * update, else add the new node.
898 		 */
899 		for (; head != NULL; head = head->am_next) {
900 			/*
901 			 * For IPv6, we need to distinguish between the
902 			 * linklocal and non-linklocal nodes
903 			 */
904 			if (strcmp(head->am_aobjname,
905 			    nodep->am_aobjname) == 0 &&
906 			    (head->am_atype != IPADM_ADDR_IPV6_ADDRCONF ||
907 			    head->am_linklocal == nodep->am_linklocal))
908 				break;
909 		}
910 
911 		if (head != NULL) {
912 			/* update the node */
913 			(void) strlcpy(head->am_ifname, nodep->am_ifname,
914 			    sizeof (head->am_ifname));
915 			head->am_lnum = nodep->am_lnum;
916 			head->am_family = nodep->am_family;
917 			head->am_flags = nodep->am_flags;
918 			head->am_atype = nodep->am_atype;
919 			if (head->am_atype == IPADM_ADDR_IPV6_ADDRCONF) {
920 				head->am_ifid = nodep->am_ifid;
921 				head->am_linklocal = nodep->am_linklocal;
922 			}
923 		} else {
924 			for (head = aobjmap.aobjmap_head; head != NULL;
925 			    head = head->am_next) {
926 				if (strcmp(head->am_ifname,
927 				    nodep->am_ifname) == 0)
928 					break;
929 			}
930 			nodep->am_nextnum = (head == NULL ? 0 :
931 			    head->am_nextnum);
932 			err = i_ipmgmt_add_amnode(nodep);
933 		}
934 		db_op = IPADM_DB_WRITE;
935 		break;
936 	case ADDROBJ_DELETE:
937 		prev = head;
938 		while (head != NULL) {
939 			if (strcmp(head->am_aobjname,
940 			    nodep->am_aobjname) == 0) {
941 				nodep->am_atype = head->am_atype;
942 				/*
943 				 * There could be multiple IPV6_ADDRCONF nodes,
944 				 * with same address object name, so check for
945 				 * logical number also.
946 				 */
947 				if (head->am_atype !=
948 				    IPADM_ADDR_IPV6_ADDRCONF ||
949 				    nodep->am_lnum == head->am_lnum)
950 					break;
951 			}
952 			prev = head;
953 			head = head->am_next;
954 		}
955 		if (head != NULL) {
956 			/*
957 			 * If the address object is in both active and
958 			 * persistent configuration and the user is deleting it
959 			 * only from active configuration then mark this node
960 			 * for deletion by reseting IPMGMT_ACTIVE bit.
961 			 * With this the same address object name cannot
962 			 * be reused until it is permanently removed.
963 			 */
964 			if (head->am_flags == (IPMGMT_ACTIVE|IPMGMT_PERSIST) &&
965 			    nodep->am_flags == IPMGMT_ACTIVE) {
966 				/* Update flags in the in-memory map. */
967 				head->am_flags &= ~IPMGMT_ACTIVE;
968 				head->am_lnum = -1;
969 
970 				/* Update info in file. */
971 				db_op = IPADM_DB_WRITE;
972 				*nodep = *head;
973 			} else {
974 				(void) strlcpy(nodep->am_ifname,
975 				    head->am_ifname,
976 				    sizeof (nodep->am_ifname));
977 				/* otherwise delete the node */
978 				if (head == aobjmap.aobjmap_head)
979 					aobjmap.aobjmap_head = head->am_next;
980 				else
981 					prev->am_next = head->am_next;
982 				free(head);
983 				db_op = IPADM_DB_DELETE;
984 			}
985 		} else {
986 			err = ENOENT;
987 		}
988 		break;
989 	case ADDROBJ_LOOKUPADD:
990 		err = i_ipmgmt_lookupadd_amnode(nodep);
991 		update = B_FALSE;
992 		break;
993 	case ADDROBJ_SETLIFNUM:
994 		update = B_FALSE;
995 		for (; head != NULL; head = head->am_next) {
996 			if (strcmp(head->am_ifname,
997 			    nodep->am_ifname) == 0 &&
998 			    head->am_family == nodep->am_family &&
999 			    head->am_lnum == nodep->am_lnum) {
1000 				err = EEXIST;
1001 				break;
1002 			}
1003 			if (strcmp(head->am_aobjname,
1004 			    nodep->am_aobjname) == 0) {
1005 				matched = head;
1006 			}
1007 		}
1008 		if (err == EEXIST)
1009 			break;
1010 		if (matched != NULL) {
1011 			/* update the lifnum */
1012 			matched->am_lnum = nodep->am_lnum;
1013 		} else {
1014 			err = ENOENT;
1015 		}
1016 		break;
1017 	default:
1018 		assert(0);
1019 	}
1020 
1021 	if (err == 0 && update)
1022 		err = ipmgmt_persist_aobjmap(nodep, db_op);
1023 
1024 	(void) pthread_rwlock_unlock(&aobjmap.aobjmap_rwlock);
1025 
1026 	return (err);
1027 }
1028 
1029 /*
1030  * Given a node in `aobjmap', this function converts it into nvlist_t structure.
1031  * The content to be written to DB must be represented as nvlist_t.
1032  */
1033 static int
i_ipmgmt_node2nvl(nvlist_t ** nvl,ipmgmt_aobjmap_t * np)1034 i_ipmgmt_node2nvl(nvlist_t **nvl, ipmgmt_aobjmap_t *np)
1035 {
1036 	int	err;
1037 	char	strval[IPMGMT_STRSIZE];
1038 
1039 	*nvl = NULL;
1040 	if ((err = nvlist_alloc(nvl, NV_UNIQUE_NAME, 0)) != 0)
1041 		goto fail;
1042 
1043 	if ((err = nvlist_add_string(*nvl, IPADM_NVP_AOBJNAME,
1044 	    np->am_aobjname)) != 0)
1045 		goto fail;
1046 
1047 	if ((err = nvlist_add_string(*nvl, IPADM_NVP_IFNAME,
1048 	    np->am_ifname)) != 0)
1049 		goto fail;
1050 
1051 	(void) snprintf(strval, IPMGMT_STRSIZE, "%d", np->am_lnum);
1052 	if ((err = nvlist_add_string(*nvl, IPADM_NVP_LIFNUM, strval)) != 0)
1053 		goto fail;
1054 
1055 	(void) snprintf(strval, IPMGMT_STRSIZE, "%d", np->am_family);
1056 	if ((err = nvlist_add_string(*nvl, IPADM_NVP_FAMILY, strval)) != 0)
1057 		goto fail;
1058 
1059 	(void) snprintf(strval, IPMGMT_STRSIZE, "%d", np->am_flags);
1060 	if ((err = nvlist_add_string(*nvl, FLAGS, strval)) != 0)
1061 		goto fail;
1062 
1063 	(void) snprintf(strval, IPMGMT_STRSIZE, "%d", np->am_atype);
1064 	if ((err = nvlist_add_string(*nvl, ATYPE, strval)) != 0)
1065 		goto fail;
1066 
1067 	if (np->am_atype == IPADM_ADDR_IPV6_ADDRCONF) {
1068 		struct sockaddr_in6	*in6;
1069 
1070 		in6 = (struct sockaddr_in6 *)&np->am_ifid;
1071 		if (np->am_linklocal &&
1072 		    IN6_IS_ADDR_UNSPECIFIED(&in6->sin6_addr)) {
1073 			if ((err = nvlist_add_string(*nvl, IPADM_NVP_IPNUMADDR,
1074 			    "default")) != 0)
1075 				goto fail;
1076 		} else {
1077 			if (inet_ntop(AF_INET6, &in6->sin6_addr, strval,
1078 			    IPMGMT_STRSIZE) == NULL) {
1079 				err = errno;
1080 				goto fail;
1081 			}
1082 			if ((err = nvlist_add_string(*nvl, IPADM_NVP_IPNUMADDR,
1083 			    strval)) != 0)
1084 				goto fail;
1085 		}
1086 	} else {
1087 		if ((err = nvlist_add_string(*nvl, IPADM_NVP_IPNUMADDR,
1088 		    "")) != 0)
1089 			goto fail;
1090 	}
1091 	return (err);
1092 fail:
1093 	nvlist_free(*nvl);
1094 	return (err);
1095 }
1096 
1097 /*
1098  * Read the aobjmap data store and build the in-memory representation
1099  * of the aobjmap. We don't need to hold any locks while building this as
1100  * we do this in very early stage of daemon coming up, even before the door
1101  * is opened.
1102  */
1103 /* ARGSUSED */
1104 extern boolean_t
ipmgmt_aobjmap_init(void * arg,nvlist_t * db_nvl,char * buf,size_t buflen,int * errp)1105 ipmgmt_aobjmap_init(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
1106     int *errp)
1107 {
1108 	nvpair_t		*nvp = NULL;
1109 	char			*name, *strval = NULL;
1110 	ipmgmt_aobjmap_t 	node;
1111 	struct sockaddr_in6	*in6;
1112 
1113 	*errp = 0;
1114 	node.am_next = NULL;
1115 	for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL;
1116 	    nvp = nvlist_next_nvpair(db_nvl, nvp)) {
1117 		name = nvpair_name(nvp);
1118 
1119 		if ((*errp = nvpair_value_string(nvp, &strval)) != 0)
1120 			return (B_TRUE);
1121 		if (strcmp(IPADM_NVP_AOBJNAME, name) == 0) {
1122 			(void) strlcpy(node.am_aobjname, strval,
1123 			    sizeof (node.am_aobjname));
1124 		} else if (strcmp(IPADM_NVP_IFNAME, name) == 0) {
1125 			(void) strlcpy(node.am_ifname, strval,
1126 			    sizeof (node.am_ifname));
1127 		} else if (strcmp(IPADM_NVP_LIFNUM, name) == 0) {
1128 			node.am_lnum = atoi(strval);
1129 		} else if (strcmp(IPADM_NVP_FAMILY, name) == 0) {
1130 			node.am_family = (sa_family_t)atoi(strval);
1131 		} else if (strcmp(FLAGS, name) == 0) {
1132 			node.am_flags = atoi(strval);
1133 		} else if (strcmp(ATYPE, name) == 0) {
1134 			node.am_atype = (ipadm_addr_type_t)atoi(strval);
1135 		} else if (strcmp(IPADM_NVP_IPNUMADDR, name) == 0) {
1136 			if (node.am_atype == IPADM_ADDR_IPV6_ADDRCONF) {
1137 				in6 = (struct sockaddr_in6 *)&node.am_ifid;
1138 				if (strcmp(strval, "default") == 0) {
1139 					bzero(in6, sizeof (node.am_ifid));
1140 					node.am_linklocal = B_TRUE;
1141 				} else {
1142 					(void) inet_pton(AF_INET6, strval,
1143 					    &in6->sin6_addr);
1144 					if (IN6_IS_ADDR_UNSPECIFIED(
1145 					    &in6->sin6_addr))
1146 						node.am_linklocal = B_TRUE;
1147 				}
1148 			}
1149 		}
1150 	}
1151 
1152 	/* we have all the information we need, add the node */
1153 	*errp = i_ipmgmt_add_amnode(&node);
1154 
1155 	return (B_TRUE);
1156 }
1157 
1158 /*
1159  * Updates an entry from the temporary cache file, which matches the given
1160  * address object name.
1161  */
1162 /* ARGSUSED */
1163 static boolean_t
ipmgmt_update_aobjmap(void * arg,nvlist_t * db_nvl,char * buf,size_t buflen,int * errp)1164 ipmgmt_update_aobjmap(void *arg, nvlist_t *db_nvl, char *buf,
1165     size_t buflen, int *errp)
1166 {
1167 	ipadm_dbwrite_cbarg_t	*cb = arg;
1168 	nvlist_t		*in_nvl = cb->dbw_nvl;
1169 	uint32_t		flags = cb->dbw_flags;
1170 	char			*db_lifnumstr = NULL, *in_lifnumstr = NULL;
1171 
1172 	*errp = 0;
1173 	if (!ipmgmt_nvlist_intersects(db_nvl, in_nvl))
1174 		return (B_TRUE);
1175 
1176 	if (flags & IPMGMT_ATYPE_V6ACONF) {
1177 		if (nvlist_lookup_string(db_nvl, IPADM_NVP_LIFNUM,
1178 		    &db_lifnumstr) != 0 ||
1179 		    nvlist_lookup_string(in_nvl, IPADM_NVP_LIFNUM,
1180 		    &in_lifnumstr) != 0 ||
1181 		    (atoi(db_lifnumstr) != -1 && atoi(in_lifnumstr) != -1 &&
1182 		    strcmp(db_lifnumstr, in_lifnumstr) != 0))
1183 			return (B_TRUE);
1184 	}
1185 
1186 	/* we found the match */
1187 	(void) memset(buf, 0, buflen);
1188 	if (ipadm_nvlist2str(in_nvl, buf, buflen) == 0) {
1189 		/* buffer overflow */
1190 		*errp = ENOBUFS;
1191 	}
1192 
1193 	/* stop the walker */
1194 	return (B_FALSE);
1195 }
1196 
1197 /*
1198  * Deletes an entry from the temporary cache file, which matches the given
1199  * address object name.
1200  */
1201 /* ARGSUSED */
1202 static boolean_t
ipmgmt_delete_aobjmap(void * arg,nvlist_t * db_nvl,char * buf,size_t buflen,int * errp)1203 ipmgmt_delete_aobjmap(void *arg, nvlist_t *db_nvl, char *buf,
1204     size_t buflen, int *errp)
1205 {
1206 	ipmgmt_aobjmap_t	*nodep = arg;
1207 	char			*db_lifnumstr = NULL;
1208 
1209 	*errp = 0;
1210 	if (!ipmgmt_nvlist_match(db_nvl, NULL, nodep->am_ifname,
1211 	    nodep->am_aobjname))
1212 		return (B_TRUE);
1213 
1214 	if (nodep->am_atype == IPADM_ADDR_IPV6_ADDRCONF) {
1215 		if (nvlist_lookup_string(db_nvl, IPADM_NVP_LIFNUM,
1216 		    &db_lifnumstr) != 0 || atoi(db_lifnumstr) != nodep->am_lnum)
1217 			return (B_TRUE);
1218 	}
1219 
1220 	/* we found the match, delete the line from the db */
1221 	buf[0] = '\0';
1222 
1223 	/* stop the walker */
1224 	return (B_FALSE);
1225 }
1226 
1227 /*
1228  * Adds or deletes aobjmap node information into a temporary cache file.
1229  */
1230 extern int
ipmgmt_persist_aobjmap(ipmgmt_aobjmap_t * nodep,ipadm_db_op_t op)1231 ipmgmt_persist_aobjmap(ipmgmt_aobjmap_t *nodep, ipadm_db_op_t op)
1232 {
1233 	int			err;
1234 	ipadm_dbwrite_cbarg_t	cb;
1235 	nvlist_t		*nvl = NULL;
1236 
1237 	if (op == IPADM_DB_WRITE) {
1238 		if ((err = i_ipmgmt_node2nvl(&nvl, nodep)) != 0)
1239 			return (err);
1240 		cb.dbw_nvl = nvl;
1241 		if (nodep->am_atype == IPADM_ADDR_IPV6_ADDRCONF)
1242 			cb.dbw_flags = IPMGMT_ATYPE_V6ACONF;
1243 		else
1244 			cb.dbw_flags = 0;
1245 
1246 		err = ipadm_rw_db(ipmgmt_update_aobjmap, &cb,
1247 		    ADDROBJ_MAPPING_DB_FILE, IPADM_FILE_MODE, IPADM_DB_WRITE);
1248 		nvlist_free(nvl);
1249 	} else {
1250 		assert(op == IPADM_DB_DELETE);
1251 
1252 		err = ipadm_rw_db(ipmgmt_delete_aobjmap, nodep,
1253 		    ADDROBJ_MAPPING_DB_FILE, IPADM_FILE_MODE, IPADM_DB_DELETE);
1254 	}
1255 	return (err);
1256 }
1257 
1258 /*
1259  * upgrades the ipadm data-store. It renames all the old private protocol
1260  * property names which start with leading protocol names to begin with
1261  * IPADM_PRIV_PROP_PREFIX.
1262  */
1263 /* ARGSUSED */
1264 boolean_t
ipmgmt_db_upgrade(void * arg,nvlist_t * db_nvl,char * buf,size_t buflen,int * errp)1265 ipmgmt_db_upgrade(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
1266     int *errp)
1267 {
1268 	nvpair_t	*nvp;
1269 	char		*name, *pname = NULL, *protostr = NULL, *pval = NULL;
1270 	uint_t		proto, nproto;
1271 	char		nname[IPMGMT_STRSIZE], tmpstr[IPMGMT_STRSIZE];
1272 
1273 	*errp = 0;
1274 	/*
1275 	 * We are interested in lines which contain protocol properties. We
1276 	 * walk through other lines in the DB.
1277 	 */
1278 	if (nvlist_exists(db_nvl, IPADM_NVP_IFNAME) ||
1279 	    nvlist_exists(db_nvl, IPADM_NVP_AOBJNAME)) {
1280 		return (B_TRUE);
1281 	}
1282 	assert(nvlist_exists(db_nvl, IPADM_NVP_PROTONAME));
1283 
1284 	/*
1285 	 * extract the propname from the `db_nvl' and also extract the
1286 	 * protocol from the `db_nvl'.
1287 	 */
1288 	for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL;
1289 	    nvp = nvlist_next_nvpair(db_nvl, nvp)) {
1290 		name = nvpair_name(nvp);
1291 		if (strcmp(name, IPADM_NVP_PROTONAME) == 0) {
1292 			if (nvpair_value_string(nvp, &protostr) != 0)
1293 				return (B_TRUE);
1294 		} else {
1295 			assert(!IPADM_PRIV_NVP(name));
1296 			pname = name;
1297 			if (nvpair_value_string(nvp, &pval) != 0)
1298 				return (B_TRUE);
1299 		}
1300 	}
1301 
1302 	/* if the private property is in the right format return */
1303 	if (strncmp(pname, IPADM_PERSIST_PRIVPROP_PREFIX,
1304 	    strlen(IPADM_PERSIST_PRIVPROP_PREFIX)) == 0) {
1305 		return (B_TRUE);
1306 	}
1307 	/* if it's a public property move onto the next property */
1308 	nproto = proto = ipadm_str2proto(protostr);
1309 	if (ipadm_legacy2new_propname(pname, nname, sizeof (nname),
1310 	    &nproto) != 0) {
1311 		return (B_TRUE);
1312 	}
1313 
1314 	/* replace the old protocol with new protocol, if required */
1315 	if (nproto != proto) {
1316 		protostr = ipadm_proto2str(nproto);
1317 		if (nvlist_add_string(db_nvl, IPADM_NVP_PROTONAME,
1318 		    protostr) != 0) {
1319 			return (B_TRUE);
1320 		}
1321 	}
1322 
1323 	/* replace the old property name with new property name, if required */
1324 	/* add the prefix to property name */
1325 	(void) snprintf(tmpstr, sizeof (tmpstr), "_%s", nname);
1326 	if (nvlist_add_string(db_nvl, tmpstr, pval) != 0 ||
1327 	    nvlist_remove(db_nvl, pname, DATA_TYPE_STRING) != 0) {
1328 		return (B_TRUE);
1329 	}
1330 	(void) memset(buf, 0, buflen);
1331 	if (ipadm_nvlist2str(db_nvl, buf, buflen) == 0) {
1332 		/* buffer overflow */
1333 		*errp = ENOBUFS;
1334 	}
1335 	return (B_TRUE);
1336 }
1337 
1338 /*
1339  * Called during boot.
1340  *
1341  * Walk through the DB and apply all the global module properties. We plow
1342  * through the DB even if we fail to apply property.
1343  */
1344 /* ARGSUSED */
1345 static boolean_t
ipmgmt_db_init(void * cbarg,nvlist_t * db_nvl,char * buf,size_t buflen,int * errp)1346 ipmgmt_db_init(void *cbarg, nvlist_t *db_nvl, char *buf, size_t buflen,
1347     int *errp)
1348 {
1349 	ipadm_handle_t	iph = cbarg;
1350 	nvpair_t	*nvp, *pnvp;
1351 	char		*strval = NULL, *name, *mod = NULL, *pname;
1352 	char		tmpstr[IPMGMT_STRSIZE];
1353 	uint_t		proto;
1354 
1355 	/*
1356 	 * We could have used nvl_exists() directly, however we need several
1357 	 * calls to it and each call traverses the list. Since this codepath
1358 	 * is exercised during boot, let's traverse the list ourselves and do
1359 	 * the necessary checks.
1360 	 */
1361 	for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL;
1362 	    nvp = nvlist_next_nvpair(db_nvl, nvp)) {
1363 		name = nvpair_name(nvp);
1364 		if (IPADM_PRIV_NVP(name)) {
1365 			if (strcmp(name, IPADM_NVP_IFNAME) == 0 ||
1366 			    strcmp(name, IPADM_NVP_AOBJNAME) == 0)
1367 				return (B_TRUE);
1368 			else if (strcmp(name, IPADM_NVP_PROTONAME) == 0 &&
1369 			    nvpair_value_string(nvp, &mod) != 0)
1370 				return (B_TRUE);
1371 		} else {
1372 			/* possible a property */
1373 			pnvp = nvp;
1374 		}
1375 	}
1376 
1377 	/* if we are here than we found a global property */
1378 	assert(mod != NULL);
1379 	assert(nvpair_type(pnvp) == DATA_TYPE_STRING);
1380 
1381 	proto = ipadm_str2proto(mod);
1382 	name = nvpair_name(pnvp);
1383 	if (nvpair_value_string(pnvp, &strval) == 0) {
1384 		if (strncmp(name, IPADM_PERSIST_PRIVPROP_PREFIX,
1385 		    strlen(IPADM_PERSIST_PRIVPROP_PREFIX)) == 0) {
1386 			/* private protocol property */
1387 			pname = &name[1];
1388 		} else if (ipadm_legacy2new_propname(name, tmpstr,
1389 		    sizeof (tmpstr), &proto) == 0) {
1390 			pname = tmpstr;
1391 		} else {
1392 			pname = name;
1393 		}
1394 		if (ipadm_set_prop(iph, pname, strval, proto,
1395 		    IPADM_OPT_ACTIVE) != IPADM_SUCCESS) {
1396 			ipmgmt_log(LOG_WARNING, "Failed to reapply property %s",
1397 			    pname);
1398 		}
1399 	}
1400 
1401 	return (B_TRUE);
1402 }
1403 
1404 /* initialize global module properties */
1405 void
ipmgmt_init_prop()1406 ipmgmt_init_prop()
1407 {
1408 	ipadm_handle_t	iph = NULL;
1409 
1410 	if (ipadm_open(&iph, IPH_INIT) != IPADM_SUCCESS) {
1411 		ipmgmt_log(LOG_WARNING, "Could not reapply any of the "
1412 		    "persisted protocol properties");
1413 		return;
1414 	}
1415 	/* ipmgmt_db_init() logs warnings if there are any issues */
1416 	(void) ipmgmt_db_walk(ipmgmt_db_init, iph, IPADM_DB_READ);
1417 	ipadm_close(iph);
1418 }
1419 
1420 void
ipmgmt_release_scf_resources(scf_resources_t * res)1421 ipmgmt_release_scf_resources(scf_resources_t *res)
1422 {
1423 	scf_entry_destroy(res->sr_ent);
1424 	scf_transaction_destroy(res->sr_tx);
1425 	scf_value_destroy(res->sr_val);
1426 	scf_property_destroy(res->sr_prop);
1427 	scf_pg_destroy(res->sr_pg);
1428 	scf_instance_destroy(res->sr_inst);
1429 	(void) scf_handle_unbind(res->sr_handle);
1430 	scf_handle_destroy(res->sr_handle);
1431 }
1432 
1433 /*
1434  * It creates the necessary SCF handles and binds the given `fmri' to an
1435  * instance. These resources are required for retrieving property value,
1436  * creating property groups and modifying property values.
1437  */
1438 int
ipmgmt_create_scf_resources(const char * fmri,scf_resources_t * res)1439 ipmgmt_create_scf_resources(const char *fmri, scf_resources_t *res)
1440 {
1441 	res->sr_tx = NULL;
1442 	res->sr_ent = NULL;
1443 	res->sr_inst = NULL;
1444 	res->sr_pg = NULL;
1445 	res->sr_prop = NULL;
1446 	res->sr_val = NULL;
1447 
1448 	if ((res->sr_handle = scf_handle_create(SCF_VERSION)) == NULL)
1449 		return (-1);
1450 
1451 	if (scf_handle_bind(res->sr_handle) != 0) {
1452 		scf_handle_destroy(res->sr_handle);
1453 		return (-1);
1454 	}
1455 	if ((res->sr_inst = scf_instance_create(res->sr_handle)) == NULL)
1456 		goto failure;
1457 	if (scf_handle_decode_fmri(res->sr_handle, fmri, NULL, NULL,
1458 	    res->sr_inst, NULL, NULL, SCF_DECODE_FMRI_REQUIRE_INSTANCE) != 0) {
1459 		goto failure;
1460 	}
1461 	/* we will create the rest of the resources on demand */
1462 	return (0);
1463 
1464 failure:
1465 	ipmgmt_log(LOG_WARNING, "failed to create scf resources: %s",
1466 	    scf_strerror(scf_error()));
1467 	ipmgmt_release_scf_resources(res);
1468 	return (-1);
1469 }
1470 
1471 /*
1472  * persists the `pval' for a given property `pname' in SCF. The only supported
1473  * SCF property types are INTEGER and ASTRING.
1474  */
1475 static int
ipmgmt_set_scfprop_value(scf_resources_t * res,const char * pname,void * pval,scf_type_t ptype)1476 ipmgmt_set_scfprop_value(scf_resources_t *res, const char *pname, void *pval,
1477     scf_type_t ptype)
1478 {
1479 	int result = -1;
1480 	boolean_t new;
1481 
1482 	if ((res->sr_val = scf_value_create(res->sr_handle)) == NULL)
1483 		goto failure;
1484 	switch (ptype) {
1485 	case SCF_TYPE_INTEGER:
1486 		scf_value_set_integer(res->sr_val, *(int64_t *)pval);
1487 		break;
1488 	case SCF_TYPE_ASTRING:
1489 		if (scf_value_set_astring(res->sr_val, (char *)pval) != 0) {
1490 			ipmgmt_log(LOG_WARNING, "Error setting string value %s "
1491 			    "for property %s: %s", pval, pname,
1492 			    scf_strerror(scf_error()));
1493 			goto failure;
1494 		}
1495 		break;
1496 	default:
1497 		goto failure;
1498 	}
1499 
1500 	if ((res->sr_tx = scf_transaction_create(res->sr_handle)) == NULL)
1501 		goto failure;
1502 	if ((res->sr_ent = scf_entry_create(res->sr_handle)) == NULL)
1503 		goto failure;
1504 	if ((res->sr_prop = scf_property_create(res->sr_handle)) == NULL)
1505 		goto failure;
1506 
1507 retry:
1508 	new = (scf_pg_get_property(res->sr_pg, pname, res->sr_prop) != 0);
1509 	if (scf_transaction_start(res->sr_tx, res->sr_pg) == -1)
1510 		goto failure;
1511 	if (new) {
1512 		if (scf_transaction_property_new(res->sr_tx, res->sr_ent,
1513 		    pname, ptype) == -1) {
1514 			goto failure;
1515 		}
1516 	} else {
1517 		if (scf_transaction_property_change(res->sr_tx, res->sr_ent,
1518 		    pname, ptype) == -1) {
1519 			goto failure;
1520 		}
1521 	}
1522 
1523 	if (scf_entry_add_value(res->sr_ent, res->sr_val) != 0)
1524 		goto failure;
1525 
1526 	result = scf_transaction_commit(res->sr_tx);
1527 	if (result == 0) {
1528 		scf_transaction_reset(res->sr_tx);
1529 		if (scf_pg_update(res->sr_pg) == -1) {
1530 			goto failure;
1531 		}
1532 		goto retry;
1533 	}
1534 	if (result == -1)
1535 		goto failure;
1536 	return (0);
1537 
1538 failure:
1539 	ipmgmt_log(LOG_WARNING, "failed to save the data in SCF: %s",
1540 	    scf_strerror(scf_error()));
1541 	return (-1);
1542 }
1543 
1544 /*
1545  * Given a `pgname'/`pname', it retrieves the value based on `ptype' and
1546  * places it in `pval'.
1547  */
1548 static int
ipmgmt_get_scfprop(scf_resources_t * res,const char * pgname,const char * pname,void * pval,scf_type_t ptype)1549 ipmgmt_get_scfprop(scf_resources_t *res, const char *pgname, const char *pname,
1550     void *pval, scf_type_t ptype)
1551 {
1552 	ssize_t		numvals;
1553 	scf_simple_prop_t *prop;
1554 
1555 	prop = scf_simple_prop_get(res->sr_handle, IPMGMTD_FMRI, pgname, pname);
1556 	numvals = scf_simple_prop_numvalues(prop);
1557 	if (numvals <= 0)
1558 		goto ret;
1559 	switch (ptype) {
1560 	case SCF_TYPE_INTEGER:
1561 		*(int64_t **)pval = scf_simple_prop_next_integer(prop);
1562 		break;
1563 	case SCF_TYPE_ASTRING:
1564 		*(char **)pval = scf_simple_prop_next_astring(prop);
1565 		break;
1566 	}
1567 ret:
1568 	scf_simple_prop_free(prop);
1569 	return (numvals);
1570 }
1571 
1572 /*
1573  * It stores the `pval' for given `pgname'/`pname' property group in SCF.
1574  */
1575 static int
ipmgmt_set_scfprop(scf_resources_t * res,const char * pgname,const char * pname,void * pval,scf_type_t ptype)1576 ipmgmt_set_scfprop(scf_resources_t *res, const char *pgname, const char *pname,
1577     void *pval, scf_type_t ptype)
1578 {
1579 	scf_error_t		err;
1580 
1581 	if ((res->sr_pg = scf_pg_create(res->sr_handle)) == NULL) {
1582 		ipmgmt_log(LOG_WARNING, "failed to create property group: %s",
1583 		    scf_strerror(scf_error()));
1584 		return (-1);
1585 	}
1586 
1587 	if (scf_instance_add_pg(res->sr_inst, pgname, SCF_GROUP_APPLICATION,
1588 	    0, res->sr_pg) != 0) {
1589 		if ((err = scf_error()) != SCF_ERROR_EXISTS) {
1590 			ipmgmt_log(LOG_WARNING,
1591 			    "Error adding property group '%s/%s': %s",
1592 			    pgname, pname, scf_strerror(err));
1593 			return (-1);
1594 		}
1595 		/*
1596 		 * if the property group already exists, then we get the
1597 		 * composed view of the property group for the given instance.
1598 		 */
1599 		if (scf_instance_get_pg_composed(res->sr_inst, NULL, pgname,
1600 		    res->sr_pg) != 0) {
1601 			ipmgmt_log(LOG_WARNING, "Error getting composed view "
1602 			    "of the property group '%s/%s': %s", pgname, pname,
1603 			    scf_strerror(scf_error()));
1604 			return (-1);
1605 		}
1606 	}
1607 
1608 	return (ipmgmt_set_scfprop_value(res, pname, pval, ptype));
1609 }
1610 
1611 /*
1612  * Returns B_TRUE, if the non-global zone is being booted for the first time
1613  * after being installed. This is required to setup the ipadm data-store for
1614  * the first boot of the non-global zone. Please see, PSARC 2010/166,
1615  * for more info.
1616  *
1617  * Note that, this API cannot be used to determine first boot post image-update.
1618  * 'pkg image-update' clones the current BE and the existing value of
1619  * ipmgmtd/first_boot_done will be carried forward and obviously it will be set
1620  * to B_TRUE.
1621  */
1622 boolean_t
ipmgmt_ngz_firstboot_postinstall()1623 ipmgmt_ngz_firstboot_postinstall()
1624 {
1625 	scf_resources_t	res;
1626 	boolean_t	bval = B_TRUE;
1627 	char		*strval;
1628 
1629 	/* we always err on the side of caution */
1630 	if (ipmgmt_create_scf_resources(IPMGMTD_FMRI, &res) != 0)
1631 		return (bval);
1632 
1633 	if (ipmgmt_get_scfprop(&res, IPMGMTD_APP_PG, IPMGMTD_PROP_FBD, &strval,
1634 	    SCF_TYPE_ASTRING) > 0) {
1635 		bval = (strcmp(strval, IPMGMTD_TRUESTR) == 0 ?
1636 		    B_FALSE : B_TRUE);
1637 	} else {
1638 		/*
1639 		 * IPMGMTD_PROP_FBD does not exist in the SCF. Lets create it.
1640 		 * Since we err on the side of caution, we ignore the return
1641 		 * error and return B_TRUE.
1642 		 */
1643 		(void) ipmgmt_set_scfprop(&res, IPMGMTD_APP_PG,
1644 		    IPMGMTD_PROP_FBD, IPMGMTD_TRUESTR, SCF_TYPE_ASTRING);
1645 	}
1646 	ipmgmt_release_scf_resources(&res);
1647 	return (bval);
1648 }
1649 
1650 /*
1651  * Returns B_TRUE, if the data-store needs upgrade otherwise returns B_FALSE.
1652  * Today we have to take care of, one case of, upgrading from version 0 to
1653  * version 1, so we will use boolean_t as means to decide if upgrade is needed
1654  * or not. Further, the upcoming projects might completely move the flatfile
1655  * data-store into SCF and hence we shall keep this interface simple.
1656  */
1657 boolean_t
ipmgmt_needs_upgrade(scf_resources_t * res)1658 ipmgmt_needs_upgrade(scf_resources_t *res)
1659 {
1660 	boolean_t	bval = B_TRUE;
1661 	int64_t		*verp;
1662 
1663 	if (ipmgmt_get_scfprop(res, IPMGMTD_APP_PG, IPMGMTD_PROP_DBVER,
1664 	    &verp, SCF_TYPE_INTEGER) > 0) {
1665 		if (*verp == IPADM_DB_VERSION)
1666 			bval = B_FALSE;
1667 	}
1668 	/*
1669 	 * 'datastore_version' doesn't exist. Which means that we need to
1670 	 * upgrade the datastore. We will create 'datastore_version' and set
1671 	 * the version value to IPADM_DB_VERSION, after we upgrade the file.
1672 	 */
1673 	return (bval);
1674 }
1675 
1676 /*
1677  * This is called after the successful upgrade of the local data-store. With
1678  * the data-store upgraded to recent version we don't have to do anything on
1679  * subsequent reboots.
1680  */
1681 void
ipmgmt_update_dbver(scf_resources_t * res)1682 ipmgmt_update_dbver(scf_resources_t *res)
1683 {
1684 	int64_t		version = IPADM_DB_VERSION;
1685 
1686 	(void) ipmgmt_set_scfprop(res, IPMGMTD_APP_PG,
1687 	    IPMGMTD_PROP_DBVER, &version, SCF_TYPE_INTEGER);
1688 }
1689