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 2010 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * Contains DB walker functions, which are of type `db_wfunc_t';
29  *
30  * typedef boolean_t db_wfunc_t(void *cbarg, nvlist_t *db_nvl, char *buf,
31  *				size_t bufsize, int *errp);
32  *
33  * ipadm_rw_db() walks through the data store, one line at a time and calls
34  * these call back functions with:
35  *	`cbarg'  - callback argument
36  *	`db_nvl' - representing a line from DB in nvlist_t form
37  *	`buf'	 - character buffer to hold modified line
38  *	`bufsize'- size of the buffer
39  *	`errp' - captures any error inside the walker function.
40  *
41  * All the 'write' callback functions modify `db_nvl' based on `cbarg' and
42  * copy string representation of `db_nvl' (using ipadm_nvlist2str()) into `buf'.
43  * To delete a line from the DB, buf[0] is set to `\0'. Inside ipadm_rw_db(),
44  * the modified `buf' is written back into DB.
45  *
46  * All the 'read' callback functions, retrieve the information from the DB, by
47  * reading `db_nvl' and then populate the `cbarg'.
48  */
49 
50 #include <stdlib.h>
51 #include <strings.h>
52 #include <errno.h>
53 #include <assert.h>
54 #include <sys/types.h>
55 #include <sys/socket.h>
56 #include <netinet/in.h>
57 #include <arpa/inet.h>
58 #include <unistd.h>
59 #include "ipmgmt_impl.h"
60 
61 #define	ATYPE	"_atype"		/* name of the address type nvpair */
62 #define	FLAGS	"_flags"		/* name of the flags nvpair */
63 
64 /*
65  * flag used by ipmgmt_persist_aobjmap() to indicate address type is
66  * IPADM_ADDR_IPV6_ADDRCONF.
67  */
68 #define	IPMGMT_ATYPE_V6ACONF	0x1
69 
70 extern pthread_rwlock_t ipmgmt_dbconf_lock;
71 
72 /*
73  * Checks if the database nvl, `db_nvl' contains and matches ALL of the passed
74  * in private nvpairs `proto', `ifname' & `aobjname'.
75  */
76 static boolean_t
77 ipmgmt_nvlist_match(nvlist_t *db_nvl, const char *proto, const char *ifname,
78     const char *aobjname)
79 {
80 	char		*db_proto = NULL, *db_ifname = NULL;
81 	char		*db_aobjname = NULL;
82 	nvpair_t	*nvp;
83 	char		*name;
84 
85 	/* walk through db_nvl and retrieve all its private nvpairs */
86 	for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL;
87 	    nvp = nvlist_next_nvpair(db_nvl, nvp)) {
88 		name = nvpair_name(nvp);
89 		if (strcmp(IPADM_NVP_PROTONAME, name) == 0)
90 			(void) nvpair_value_string(nvp, &db_proto);
91 		else if (strcmp(IPADM_NVP_IFNAME, name) == 0)
92 			(void) nvpair_value_string(nvp, &db_ifname);
93 		else if (strcmp(IPADM_NVP_AOBJNAME, name) == 0)
94 			(void) nvpair_value_string(nvp, &db_aobjname);
95 	}
96 
97 	if (proto != NULL && proto[0] == '\0')
98 		proto = NULL;
99 	if (ifname != NULL && ifname[0] == '\0')
100 		ifname = NULL;
101 	if (aobjname != NULL && aobjname[0] == '\0')
102 		aobjname = NULL;
103 
104 	if ((proto == NULL && db_proto != NULL) ||
105 	    (proto != NULL && db_proto == NULL) ||
106 	    strcmp(proto, db_proto) != 0) {
107 		/* no intersection - different protocols. */
108 		return (B_FALSE);
109 	}
110 	if ((ifname == NULL && db_ifname != NULL) ||
111 	    (ifname != NULL && db_ifname == NULL) ||
112 	    strcmp(ifname, db_ifname) != 0) {
113 		/* no intersection - different interfaces. */
114 		return (B_FALSE);
115 	}
116 	if ((aobjname == NULL && db_aobjname != NULL) ||
117 	    (aobjname != NULL && db_aobjname == NULL) ||
118 	    strcmp(aobjname, db_aobjname) != 0) {
119 		/* no intersection - different address objects */
120 		return (B_FALSE);
121 	}
122 
123 	return (B_TRUE);
124 }
125 
126 /*
127  * Checks if the database nvl, `db_nvl' and the input nvl, `in_nvl' intersects.
128  */
129 static boolean_t
130 ipmgmt_nvlist_intersects(nvlist_t *db_nvl, nvlist_t *in_nvl)
131 {
132 	nvpair_t	*nvp;
133 	char		*name;
134 	char		*proto = NULL, *ifname = NULL, *aobjname = NULL;
135 
136 	/* walk through in_nvl and retrieve all its private nvpairs */
137 	for (nvp = nvlist_next_nvpair(in_nvl, NULL); nvp != NULL;
138 	    nvp = nvlist_next_nvpair(in_nvl, nvp)) {
139 		name = nvpair_name(nvp);
140 		if (strcmp(IPADM_NVP_PROTONAME, name) == 0)
141 			(void) nvpair_value_string(nvp, &proto);
142 		else if (strcmp(IPADM_NVP_IFNAME, name) == 0)
143 			(void) nvpair_value_string(nvp, &ifname);
144 		else if (strcmp(IPADM_NVP_AOBJNAME, name) == 0)
145 			(void) nvpair_value_string(nvp, &aobjname);
146 	}
147 
148 	return (ipmgmt_nvlist_match(db_nvl, proto, ifname, aobjname));
149 }
150 
151 /*
152  * Checks if the database nvl, `db_nvl', contains and matches ANY of the passed
153  * in private nvpairs `proto', `ifname' & `aobjname'.
154  */
155 static boolean_t
156 ipmgmt_nvlist_contains(nvlist_t *db_nvl, const char *proto,
157     const char *ifname, char *aobjname)
158 {
159 	char		*db_ifname = NULL, *db_proto = NULL;
160 	char		*db_aobjname = NULL;
161 	nvpair_t	*nvp;
162 	char		*name;
163 
164 	/* walk through db_nvl and retrieve all private nvpairs */
165 	for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL;
166 	    nvp = nvlist_next_nvpair(db_nvl, nvp)) {
167 		name = nvpair_name(nvp);
168 		if (strcmp(IPADM_NVP_PROTONAME, name) == 0)
169 			(void) nvpair_value_string(nvp, &db_proto);
170 		else if (strcmp(IPADM_NVP_IFNAME, name) == 0)
171 			(void) nvpair_value_string(nvp, &db_ifname);
172 		else if (strcmp(IPADM_NVP_AOBJNAME, name) == 0)
173 			(void) nvpair_value_string(nvp, &db_aobjname);
174 	}
175 
176 	if (proto != NULL && proto[0] != '\0') {
177 		if ((db_proto == NULL || strcmp(proto, db_proto) != 0))
178 			return (B_FALSE);
179 	}
180 	if (ifname != NULL && ifname[0] != '\0') {
181 		if ((db_ifname == NULL || strcmp(ifname, db_ifname) != 0))
182 			return (B_FALSE);
183 	}
184 	if (aobjname != NULL && aobjname[0] != '\0') {
185 		if ((db_aobjname == NULL || strcmp(aobjname, db_aobjname) != 0))
186 			return (B_FALSE);
187 	}
188 
189 	return (B_TRUE);
190 }
191 
192 /*
193  * Retrieves the property value from the DB. The property whose value is to be
194  * retrieved is in `pargp->ia_pname'.
195  */
196 /* ARGSUSED */
197 boolean_t
198 ipmgmt_db_getprop(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
199     int *errp)
200 {
201 	ipmgmt_prop_arg_t	*pargp = arg;
202 	boolean_t		cont = B_TRUE;
203 	char			*pval;
204 	int			err = 0;
205 
206 	*errp = 0;
207 
208 	if (!ipmgmt_nvlist_match(db_nvl, pargp->ia_module,
209 	    pargp->ia_ifname, pargp->ia_aobjname))
210 		return (B_TRUE);
211 
212 	if ((err = nvlist_lookup_string(db_nvl, pargp->ia_pname,
213 	    &pval)) == 0) {
214 		(void) strlcpy(pargp->ia_pval, pval, sizeof (pargp->ia_pval));
215 		/*
216 		 * We have retrieved what we are looking for.
217 		 * Stop the walker.
218 		 */
219 		cont = B_FALSE;
220 	} else {
221 		if (err == ENOENT)
222 			err = 0;
223 		*errp = err;
224 	}
225 
226 	return (cont);
227 }
228 
229 /*
230  * Removes the property value from the DB. The property whose value is to be
231  * removed is in `pargp->ia_pname'.
232  */
233 /* ARGSUSED */
234 boolean_t
235 ipmgmt_db_resetprop(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
236     int *errp)
237 {
238 	ipmgmt_prop_arg_t	*pargp = arg;
239 
240 	*errp = 0;
241 	if (!ipmgmt_nvlist_match(db_nvl, pargp->ia_module,
242 	    pargp->ia_ifname, pargp->ia_aobjname))
243 		return (B_TRUE);
244 
245 	if (!nvlist_exists(db_nvl, pargp->ia_pname))
246 		return (B_TRUE);
247 
248 	/*
249 	 * We found the property in the DB. If IPMGMT_REMOVE is not set then
250 	 * delete the entry from the db. If it is set, then the property is a
251 	 * multi-valued property so just remove the specified values from DB.
252 	 */
253 	if (pargp->ia_flags & IPMGMT_REMOVE) {
254 		char	*dbpval = NULL;
255 		char	*inpval = pargp->ia_pval;
256 		char	pval[MAXPROPVALLEN];
257 		char	*val, *lasts;
258 
259 		*errp = nvlist_lookup_string(db_nvl, pargp->ia_pname, &dbpval);
260 		if (*errp != 0)
261 			return (B_FALSE);
262 
263 		/*
264 		 * multi-valued properties are represented as comma separated
265 		 * values. Use string tokenizer functions to split them and
266 		 * search for the value to be removed.
267 		 */
268 		bzero(pval, sizeof (pval));
269 		if ((val = strtok_r(dbpval, ",", &lasts)) != NULL) {
270 			if (strcmp(val, inpval) != 0)
271 				(void) strlcat(pval, val, MAXPROPVALLEN);
272 			while ((val = strtok_r(NULL, ",", &lasts)) != NULL) {
273 				if (strcmp(val, inpval) != 0) {
274 					if (pval[0] != '\0')
275 						(void) strlcat(pval, ",",
276 						    MAXPROPVALLEN);
277 					(void) strlcat(pval, val,
278 					    MAXPROPVALLEN);
279 				}
280 			}
281 		} else {
282 			if (strcmp(dbpval, inpval) != 0)
283 				*errp = ENOENT;
284 			else
285 				buf[0] =  '\0';
286 			return (B_FALSE);
287 		}
288 		*errp = nvlist_add_string(db_nvl, pargp->ia_pname, pval);
289 		if (*errp != 0)
290 			return (B_FALSE);
291 
292 		(void) memset(buf, 0, buflen);
293 		if (ipadm_nvlist2str(db_nvl, buf, buflen) == 0) {
294 			/* buffer overflow */
295 			*errp = ENOBUFS;
296 		}
297 	} else {
298 		buf[0] = '\0';
299 	}
300 
301 	/* stop the search */
302 	return (B_FALSE);
303 }
304 
305 /*
306  * Input arguments can have IPADM_NVP_AOBJNAME or IPADM_NVP_IFNAME. A match is
307  * found, when one of the following occurs first.
308  * - the input aobjname matches the db aobjname. Return the db address.
309  * - the input interface matches the db interface. Return all the
310  *   matching db lines with addresses.
311  */
312 /* ARGSUSED */
313 boolean_t
314 ipmgmt_db_getaddr(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
315     int *errp)
316 {
317 	ipmgmt_getaddr_cbarg_t	*cbarg = arg;
318 	char		*db_aobjname = NULL;
319 	char		*db_ifname = NULL;
320 	nvlist_t	*db_addr = NULL;
321 	char		name[IPMGMT_STRSIZE];
322 	nvpair_t	*nvp;
323 	boolean_t	add_nvl = B_FALSE;
324 
325 	/* Parse db nvlist */
326 	for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL;
327 	    nvp = nvlist_next_nvpair(db_nvl, nvp)) {
328 		if (nvpair_type(nvp) == DATA_TYPE_NVLIST)
329 			(void) nvpair_value_nvlist(nvp, &db_addr);
330 		else if (strcmp(nvpair_name(nvp), IPADM_NVP_IFNAME) == 0)
331 			(void) nvpair_value_string(nvp, &db_ifname);
332 		else if (strcmp(nvpair_name(nvp), IPADM_NVP_AOBJNAME) == 0)
333 			(void) nvpair_value_string(nvp, &db_aobjname);
334 	}
335 
336 	if (db_aobjname == NULL) /* Not an address */
337 		return (B_TRUE);
338 
339 	/* Check for a match between the aobjnames or the interface name */
340 	if (cbarg->cb_aobjname[0] != '\0') {
341 		if (strcmp(cbarg->cb_aobjname, db_aobjname) == 0)
342 			add_nvl = B_TRUE;
343 	} else if (cbarg->cb_ifname[0] != '\0') {
344 		if (strcmp(cbarg->cb_ifname, db_ifname) == 0)
345 			add_nvl = B_TRUE;
346 	} else {
347 		add_nvl = B_TRUE;
348 	}
349 
350 	if (add_nvl) {
351 		(void) snprintf(name, sizeof (name), "%s_%d", db_ifname,
352 		    cbarg->cb_ocnt);
353 		*errp = nvlist_add_nvlist(cbarg->cb_onvl, name, db_nvl);
354 		if (*errp == 0)
355 			cbarg->cb_ocnt++;
356 	}
357 	return (B_TRUE);
358 }
359 
360 /*
361  * This function takes the appropriate lock, read or write, based on the
362  * `db_op' and then calls DB walker ipadm_rw_db().
363  */
364 extern int
365 ipmgmt_db_walk(db_wfunc_t *db_walk_func, void *db_warg, ipadm_db_op_t db_op)
366 {
367 	int		err;
368 	boolean_t	writeop;
369 	mode_t		mode;
370 
371 	writeop = (db_op != IPADM_DB_READ);
372 
373 	if (writeop) {
374 		(void) pthread_rwlock_wrlock(&ipmgmt_dbconf_lock);
375 		mode = IPADM_FILE_MODE;
376 	} else {
377 		(void) pthread_rwlock_rdlock(&ipmgmt_dbconf_lock);
378 		mode = 0;
379 	}
380 
381 	err = ipadm_rw_db(db_walk_func, db_warg, IPADM_DB_FILE, mode, db_op);
382 	(void) pthread_rwlock_unlock(&ipmgmt_dbconf_lock);
383 	return (err);
384 }
385 
386 /*
387  * Used to add an entry towards the end of DB. It just returns B_TRUE for
388  * every line of the DB. When we reach the end, ipadm_rw_db() adds the
389  * line at the end.
390  */
391 /* ARGSUSED */
392 boolean_t
393 ipmgmt_db_add(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, int *errp)
394 {
395 	return (B_TRUE);
396 }
397 
398 /*
399  * This function is used to update or create an entry in DB. The nvlist_t,
400  * `in_nvl', represents the line we are looking for. Once we ensure the right
401  * line from DB, we update that entry.
402  */
403 boolean_t
404 ipmgmt_db_update(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
405     int *errp)
406 {
407 	ipadm_dbwrite_cbarg_t	*cb = arg;
408 	uint_t			flags = cb->dbw_flags;
409 	nvlist_t		*in_nvl = cb->dbw_nvl;
410 	nvpair_t		*nvp;
411 	char			*name, *instrval = NULL, *dbstrval = NULL;
412 	char			pval[MAXPROPVALLEN];
413 
414 	if (!ipmgmt_nvlist_intersects(db_nvl, in_nvl))
415 		return (B_TRUE);
416 
417 	for (nvp = nvlist_next_nvpair(in_nvl, NULL); nvp != NULL;
418 	    nvp = nvlist_next_nvpair(in_nvl, nvp)) {
419 		name = nvpair_name(nvp);
420 		if (!IPADM_PRIV_NVP(name) && nvlist_exists(db_nvl, name))
421 			break;
422 	}
423 
424 	if (nvp == NULL)
425 		return (B_TRUE);
426 
427 	assert(nvpair_type(nvp) == DATA_TYPE_STRING);
428 
429 	if ((*errp = nvpair_value_string(nvp, &instrval)) != 0)
430 		return (B_FALSE);
431 
432 	/*
433 	 * If IPMGMT_APPEND is set then we are dealing with multi-valued
434 	 * properties. We append to the entry from the db, with the new value.
435 	 */
436 	if (flags & IPMGMT_APPEND) {
437 		if ((*errp = nvlist_lookup_string(db_nvl, name,
438 		    &dbstrval)) != 0)
439 			return (B_FALSE);
440 		(void) snprintf(pval, MAXPROPVALLEN, "%s,%s", dbstrval,
441 		    instrval);
442 		if ((*errp = nvlist_add_string(db_nvl, name, pval)) != 0)
443 			return (B_FALSE);
444 	} else {
445 		/* case	of in-line update of a db entry */
446 		if ((*errp = nvlist_add_string(db_nvl, name, instrval)) != 0)
447 			return (B_FALSE);
448 	}
449 
450 	(void) memset(buf, 0, buflen);
451 	if (ipadm_nvlist2str(db_nvl, buf, buflen) == 0) {
452 		/* buffer overflow */
453 		*errp = ENOBUFS;
454 	}
455 	*errp = 0;
456 
457 	/* we updated the DB entry, so do not continue */
458 	return (B_FALSE);
459 }
460 
461 /*
462  * For the given `cbarg->cb_ifname' interface, retrieves any persistent
463  * interface information (used in 'ipadm show-if')
464  */
465 /* ARGSUSED */
466 boolean_t
467 ipmgmt_db_getif(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
468     int *errp)
469 {
470 	ipmgmt_getif_cbarg_t	*cbarg = arg;
471 	char			*ifname = cbarg->cb_ifname;
472 	char			*intf = NULL;
473 	ipadm_if_info_t		*ifp = NULL;
474 	sa_family_t		af;
475 	char			*afstr;
476 
477 	*errp = 0;
478 	if (nvlist_lookup_string(db_nvl, IPADM_NVP_FAMILY, &afstr) != 0 ||
479 	    nvlist_lookup_string(db_nvl, IPADM_NVP_IFNAME, &intf) != 0 ||
480 	    (ifname[0] != '\0' && strcmp(ifname, intf) != 0)) {
481 		return (B_TRUE);
482 	}
483 	af = atoi(afstr);
484 	for (ifp = cbarg->cb_ifinfo; ifp != NULL; ifp = ifp->ifi_next) {
485 		if (strcmp(ifp->ifi_name, intf) == 0)
486 			break;
487 	}
488 	if (ifp == NULL) {
489 		ipadm_if_info_t *new;
490 
491 		if ((new = calloc(1, sizeof (*new))) == NULL) {
492 			*errp = ENOMEM;
493 			return (B_FALSE); /* don't continue the walk */
494 		}
495 		new->ifi_next = cbarg->cb_ifinfo;
496 		cbarg->cb_ifinfo = new;
497 		ifp = new;
498 		(void) strlcpy(ifp->ifi_name, intf, sizeof (ifp->ifi_name));
499 	}
500 
501 	if (af == AF_INET) {
502 		ifp->ifi_pflags |= IFIF_IPV4;
503 	} else {
504 		assert(af == AF_INET6);
505 		ifp->ifi_pflags |= IFIF_IPV6;
506 	}
507 
508 	/* Terminate the walk if we found both v4 and v6 interfaces. */
509 	if (ifname[0] != '\0' && (ifp->ifi_pflags & IFIF_IPV4) &&
510 	    (ifp->ifi_pflags & IFIF_IPV6))
511 		return (B_FALSE);
512 
513 	return (B_TRUE);
514 }
515 
516 /*
517  * Deletes those entries from the database for which interface name
518  * matches with the given `cbarg->cb_ifname'
519  */
520 /* ARGSUSED */
521 boolean_t
522 ipmgmt_db_resetif(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
523     int *errp)
524 {
525 	ipmgmt_if_cbarg_t *cbarg = arg;
526 	boolean_t	isv6 = (cbarg->cb_family == AF_INET6);
527 	char		*ifname = cbarg->cb_ifname;
528 	char		*modstr = NULL;
529 	char		*afstr;
530 	char		*aobjname;
531 	uint_t		proto;
532 	ipmgmt_aobjmap_t *head;
533 	boolean_t	aobjfound = B_FALSE;
534 
535 	*errp = 0;
536 
537 	if (!ipmgmt_nvlist_contains(db_nvl, NULL, ifname, NULL))
538 		return (B_TRUE);
539 
540 	if (nvlist_lookup_string(db_nvl, IPADM_NVP_FAMILY, &afstr) == 0) {
541 		if (atoi(afstr) == cbarg->cb_family)
542 			goto delete;
543 		return (B_TRUE);
544 	}
545 
546 	/* Reset all the interface configurations for 'ifname' */
547 	if (isv6 && (nvlist_exists(db_nvl, IPADM_NVP_IPV6ADDR) ||
548 	    nvlist_exists(db_nvl, IPADM_NVP_INTFID))) {
549 		goto delete;
550 	}
551 	if (!isv6 &&
552 	    (nvlist_exists(db_nvl, IPADM_NVP_IPV4ADDR) ||
553 	    nvlist_exists(db_nvl, IPADM_NVP_DHCP))) {
554 		goto delete;
555 	}
556 
557 	if (nvlist_lookup_string(db_nvl, IPADM_NVP_AOBJNAME, &aobjname) == 0) {
558 		/*
559 		 * This must be an address property. Delete this
560 		 * line if there is a match in the address family.
561 		 */
562 		head = aobjmap.aobjmap_head;
563 		while (head != NULL) {
564 			if (strcmp(head->am_aobjname, aobjname) == 0) {
565 				aobjfound = B_TRUE;
566 				if (head->am_family == cbarg->cb_family)
567 					goto delete;
568 			}
569 			head = head->am_next;
570 		}
571 		/*
572 		 * If aobjfound = B_FALSE, then this address is not
573 		 * available in active configuration. We should go ahead
574 		 * and delete it.
575 		 */
576 		if (!aobjfound)
577 			goto delete;
578 	}
579 
580 	/*
581 	 * If we are removing both v4 and v6 interface, then we get rid of
582 	 * all the properties for that interface. On the other hand, if we
583 	 * are deleting only v4 instance of an interface, then we delete v4
584 	 * properties only.
585 	 */
586 	if (nvlist_lookup_string(db_nvl, IPADM_NVP_PROTONAME, &modstr) == 0) {
587 		proto = ipadm_str2proto(modstr);
588 		switch (proto) {
589 		case MOD_PROTO_IPV6:
590 			if (isv6)
591 				goto delete;
592 			break;
593 		case MOD_PROTO_IPV4:
594 			if (!isv6)
595 				goto delete;
596 			break;
597 		case MOD_PROTO_IP:
598 			/* this should never be the case, today */
599 			assert(0);
600 			break;
601 		}
602 	}
603 	/* Not found a match yet. Continue processing the db */
604 	return (B_TRUE);
605 delete:
606 	/* delete the line from the db */
607 	buf[0] = '\0';
608 	return (B_TRUE);
609 }
610 
611 /*
612  * Deletes those entries from the database for which address object name
613  * matches with the given `cbarg->cb_aobjname'
614  */
615 /* ARGSUSED */
616 boolean_t
617 ipmgmt_db_resetaddr(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
618     int *errp)
619 {
620 	ipmgmt_resetaddr_cbarg_t *cbarg = arg;
621 	char		*aobjname = cbarg->cb_aobjname;
622 
623 	*errp = 0;
624 	if (!ipmgmt_nvlist_contains(db_nvl, NULL, NULL, aobjname))
625 		return (B_TRUE);
626 
627 	/* delete the line from the db */
628 	buf[0] = '\0';
629 	return (B_TRUE);
630 }
631 
632 /*
633  * Retrieves all interface props, including addresses, for given interface(s).
634  * `invl' contains the list of interfaces, for which information need to be
635  * retrieved.
636  */
637 /* ARGSUSED */
638 boolean_t
639 ipmgmt_db_initif(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
640     int *errp)
641 {
642 	ipmgmt_initif_cbarg_t	*cbarg = arg;
643 	nvlist_t		*onvl = cbarg->cb_onvl;
644 	nvlist_t		*invl = cbarg->cb_invl;
645 	sa_family_t		in_af = cbarg->cb_family;
646 	char			*db_ifname;
647 
648 	*errp = 0;
649 	if (nvlist_lookup_string(db_nvl, IPADM_NVP_IFNAME, &db_ifname) == 0 &&
650 	    nvlist_exists(invl, db_ifname)) {
651 		char		name[IPMGMT_STRSIZE];
652 		sa_family_t	db_af = in_af;
653 		uint_t		proto;
654 		char		*pstr;
655 
656 		if (in_af != AF_UNSPEC) {
657 			if (nvlist_lookup_string(db_nvl, IPADM_NVP_PROTONAME,
658 			    &pstr) == 0) {
659 				proto = ipadm_str2proto(pstr);
660 				if (proto == MOD_PROTO_IPV4)
661 					db_af = AF_INET;
662 				else if (proto == MOD_PROTO_IPV6)
663 					db_af = AF_INET6;
664 				else
665 					db_af = in_af;
666 			} else {
667 				if (nvlist_exists(db_nvl, IPADM_NVP_IPV4ADDR) ||
668 				    nvlist_exists(db_nvl, IPADM_NVP_DHCP))
669 					db_af = AF_INET;
670 				else
671 					db_af = AF_INET6;
672 			}
673 		}
674 		if (in_af == db_af) {
675 			(void) snprintf(name, sizeof (name), "%s_%d", db_ifname,
676 			    cbarg->cb_ocnt);
677 			*errp = nvlist_add_nvlist(onvl, name, db_nvl);
678 			if (*errp == 0)
679 				cbarg->cb_ocnt++;
680 		}
681 	}
682 	return (B_TRUE);
683 }
684 
685 /*
686  * helper function for ipmgmt_aobjmap_op(). Adds the node pointed by `nodep'
687  * into `aobjmap' structure.
688  */
689 static int
690 i_ipmgmt_add_amnode(ipmgmt_aobjmap_t *nodep)
691 {
692 	ipmgmt_aobjmap_t	*new, *head;
693 
694 	head = aobjmap.aobjmap_head;
695 	if ((new = malloc(sizeof (ipmgmt_aobjmap_t))) == NULL)
696 		return (ENOMEM);
697 	*new = *nodep;
698 	new->am_next = NULL;
699 
700 	/* Add the node at the beginning of the list */
701 	if (head == NULL) {
702 		aobjmap.aobjmap_head = new;
703 	} else {
704 		new->am_next = aobjmap.aobjmap_head;
705 		aobjmap.aobjmap_head = new;
706 	}
707 	return (0);
708 }
709 
710 /*
711  * A recursive function to generate alphabetized number given a decimal number.
712  * Decimal 0 to 25 maps to 'a' to 'z' and then the counting continues with 'aa',
713  * 'ab', 'ac', et al.
714  */
715 static void
716 i_ipmgmt_num2priv_aobjname(uint32_t num, char **cp, char *endp)
717 {
718 	if (num >= 26)
719 		i_ipmgmt_num2priv_aobjname(num / 26 - 1, cp, endp);
720 	if (*cp != endp) {
721 		*cp[0] = 'a' + (num % 26);
722 		(*cp)++;
723 	}
724 }
725 
726 /*
727  * This function generates an `aobjname', when required, and then does
728  * lookup-add. If `nodep->am_aobjname' is not an empty string, then it walks
729  * through the `aobjmap' to check if an address object with the same
730  * `nodep->am_aobjname' exists. If it exists, EEXIST is returned as duplicate
731  * `aobjname's are not allowed.
732  *
733  * If `nodep->am_aobjname' is an empty string then the daemon generates an
734  * `aobjname' using the `am_nextnum', which contains the next number to be
735  * used to generate `aobjname'. `am_nextnum' is converted to base26 using
736  * `a-z' alphabets in i_ipmgmt_num2priv_aobjname().
737  *
738  * `am_nextnum' will be 0 to begin with. Every time an address object that
739  * needs `aobjname' is added it's incremented by 1. So for the first address
740  * object on net0 the `am_aobjname' will be net0/_a and `am_nextnum' will be 1.
741  * For the second address object on that interface `am_aobjname' will be net0/_b
742  * and  `am_nextnum' will incremented to 2.
743  */
744 static int
745 i_ipmgmt_lookupadd_amnode(ipmgmt_aobjmap_t *nodep)
746 {
747 	ipmgmt_aobjmap_t	*head;
748 	uint32_t		nextnum;
749 
750 	for (head = aobjmap.aobjmap_head; head != NULL; head = head->am_next)
751 		if (strcmp(head->am_ifname, nodep->am_ifname) == 0)
752 			break;
753 	nextnum = (head == NULL ? 0 : head->am_nextnum);
754 
755 	/*
756 	 * if `aobjname' is empty, then the daemon has to generate the
757 	 * next `aobjname' for the given interface and family.
758 	 */
759 	if (nodep->am_aobjname[0] == '\0') {
760 		char tmpstr[IPADM_AOBJ_USTRSIZ - 1];  /* 1 for leading  '_' */
761 		char *cp = tmpstr;
762 		char *endp = tmpstr + sizeof (tmpstr);
763 
764 		i_ipmgmt_num2priv_aobjname(nextnum, &cp, endp);
765 
766 		if (cp == endp)
767 			return (EINVAL);
768 		cp[0] = '\0';
769 
770 		if (snprintf(nodep->am_aobjname, IPADM_AOBJSIZ, "%s/_%s",
771 		    nodep->am_ifname, tmpstr) >= IPADM_AOBJSIZ) {
772 			return (EINVAL);
773 		}
774 		nodep->am_nextnum = ++nextnum;
775 	} else {
776 		for (head = aobjmap.aobjmap_head; head != NULL;
777 		    head = head->am_next) {
778 			if (strcmp(head->am_aobjname, nodep->am_aobjname) == 0)
779 				return (EEXIST);
780 		}
781 		nodep->am_nextnum = nextnum;
782 	}
783 	return (i_ipmgmt_add_amnode(nodep));
784 }
785 
786 /*
787  * Performs following operations on the global `aobjmap' linked list.
788  * (a) ADDROBJ_ADD: add or update address object in `aobjmap'
789  * (b) ADDROBJ_DELETE: delete address object from `aobjmap'
790  * (c) ADDROBJ_LOOKUPADD: place a stub address object in `aobjmap'
791  */
792 int
793 ipmgmt_aobjmap_op(ipmgmt_aobjmap_t *nodep, uint32_t op)
794 {
795 	ipmgmt_aobjmap_t	*head, *prev;
796 	boolean_t		update = B_TRUE;
797 	int			err = 0;
798 	ipadm_db_op_t		db_op;
799 
800 	(void) pthread_rwlock_wrlock(&aobjmap.aobjmap_rwlock);
801 
802 	head = aobjmap.aobjmap_head;
803 	switch (op) {
804 	case ADDROBJ_ADD:
805 		/*
806 		 * check for stub nodes (added by ADDROBJ_LOOKUPADD) and
807 		 * update, else add the new node.
808 		 */
809 		for (; head != NULL; head = head->am_next) {
810 			if (strcmp(head->am_aobjname,
811 			    nodep->am_aobjname) == 0 && head->am_lnum == -1)
812 				break;
813 		}
814 
815 		if (head != NULL) {
816 			/* update the node */
817 			(void) strlcpy(head->am_ifname, nodep->am_ifname,
818 			    sizeof (head->am_ifname));
819 			head->am_lnum = nodep->am_lnum;
820 			head->am_family = nodep->am_family;
821 			head->am_flags = nodep->am_flags;
822 			head->am_atype = nodep->am_atype;
823 			if (head->am_atype == IPADM_ADDR_IPV6_ADDRCONF) {
824 				head->am_ifid = nodep->am_ifid;
825 				head->am_linklocal = nodep->am_linklocal;
826 			}
827 		} else {
828 			for (head = aobjmap.aobjmap_head; head != NULL;
829 			    head = head->am_next) {
830 				if (strcmp(head->am_ifname,
831 				    nodep->am_ifname) == 0)
832 					break;
833 			}
834 			nodep->am_nextnum = (head == NULL ? 0 :
835 			    head->am_nextnum);
836 			err = i_ipmgmt_add_amnode(nodep);
837 		}
838 		db_op = IPADM_DB_WRITE;
839 		break;
840 	case ADDROBJ_DELETE:
841 		prev = head;
842 		while (head != NULL) {
843 			if (strcmp(head->am_aobjname,
844 			    nodep->am_aobjname) == 0) {
845 				nodep->am_atype = head->am_atype;
846 				/*
847 				 * There could be multiple IPV6_ADDRCONF nodes,
848 				 * with same address object name, so check for
849 				 * logical number also.
850 				 */
851 				if (head->am_atype !=
852 				    IPADM_ADDR_IPV6_ADDRCONF ||
853 				    nodep->am_lnum == head->am_lnum)
854 					break;
855 			}
856 			prev = head;
857 			head = head->am_next;
858 		}
859 		if (head != NULL) {
860 			/*
861 			 * If the address object is in both active and
862 			 * persistent configuration and the user is deleting it
863 			 * only from active configuration then mark this node
864 			 * for deletion by reseting IPMGMT_ACTIVE bit.
865 			 * With this the same address object name cannot
866 			 * be reused until it is permanently removed.
867 			 */
868 			if (head->am_flags == (IPMGMT_ACTIVE|IPMGMT_PERSIST) &&
869 			    nodep->am_flags == IPMGMT_ACTIVE) {
870 				/* Update flags in the in-memory map. */
871 				head->am_flags &= ~IPMGMT_ACTIVE;
872 				head->am_lnum = -1;
873 
874 				/* Update info in file. */
875 				db_op = IPADM_DB_WRITE;
876 				*nodep = *head;
877 			} else {
878 				(void) strlcpy(nodep->am_ifname,
879 				    head->am_ifname,
880 				    sizeof (nodep->am_ifname));
881 				/* otherwise delete the node */
882 				if (head == aobjmap.aobjmap_head)
883 					aobjmap.aobjmap_head = head->am_next;
884 				else
885 					prev->am_next = head->am_next;
886 				free(head);
887 				db_op = IPADM_DB_DELETE;
888 			}
889 		} else {
890 			err = ENOENT;
891 		}
892 		break;
893 	case ADDROBJ_LOOKUPADD:
894 		err = i_ipmgmt_lookupadd_amnode(nodep);
895 		update = B_FALSE;
896 		break;
897 	default:
898 		assert(0);
899 	}
900 
901 	if (err == 0 && update)
902 		err = ipmgmt_persist_aobjmap(nodep, db_op);
903 
904 	(void) pthread_rwlock_unlock(&aobjmap.aobjmap_rwlock);
905 
906 	return (err);
907 }
908 
909 /*
910  * Given a node in `aobjmap', this function converts it into nvlist_t structure.
911  * The content to be written to DB must be represented as nvlist_t.
912  */
913 static int
914 i_ipmgmt_node2nvl(nvlist_t **nvl, ipmgmt_aobjmap_t *np)
915 {
916 	int	err;
917 	char	strval[IPMGMT_STRSIZE];
918 
919 	*nvl = NULL;
920 	if ((err = nvlist_alloc(nvl, NV_UNIQUE_NAME, 0)) != 0)
921 		goto fail;
922 
923 	if ((err = nvlist_add_string(*nvl, IPADM_NVP_AOBJNAME,
924 	    np->am_aobjname)) != 0)
925 		goto fail;
926 
927 	if ((err = nvlist_add_string(*nvl, IPADM_NVP_IFNAME,
928 	    np->am_ifname)) != 0)
929 		goto fail;
930 
931 	(void) snprintf(strval, IPMGMT_STRSIZE, "%d", np->am_lnum);
932 	if ((err = nvlist_add_string(*nvl, IPADM_NVP_LIFNUM, strval)) != 0)
933 		goto fail;
934 
935 	(void) snprintf(strval, IPMGMT_STRSIZE, "%d", np->am_family);
936 	if ((err = nvlist_add_string(*nvl, IPADM_NVP_FAMILY, strval)) != 0)
937 		goto fail;
938 
939 	(void) snprintf(strval, IPMGMT_STRSIZE, "%d", np->am_flags);
940 	if ((err = nvlist_add_string(*nvl, FLAGS, strval)) != 0)
941 		goto fail;
942 
943 	(void) snprintf(strval, IPMGMT_STRSIZE, "%d", np->am_atype);
944 	if ((err = nvlist_add_string(*nvl, ATYPE, strval)) != 0)
945 		goto fail;
946 
947 	if (np->am_atype == IPADM_ADDR_IPV6_ADDRCONF) {
948 		struct sockaddr_in6	*in6;
949 
950 		in6 = (struct sockaddr_in6 *)&np->am_ifid;
951 		if (np->am_linklocal &&
952 		    IN6_IS_ADDR_UNSPECIFIED(&in6->sin6_addr)) {
953 			if ((err = nvlist_add_string(*nvl, IPADM_NVP_IPNUMADDR,
954 			    "default")) != 0)
955 				goto fail;
956 		} else {
957 			if (inet_ntop(AF_INET6, &in6->sin6_addr, strval,
958 			    IPMGMT_STRSIZE) == NULL) {
959 				err = errno;
960 				goto fail;
961 			}
962 			if ((err = nvlist_add_string(*nvl, IPADM_NVP_IPNUMADDR,
963 			    strval)) != 0)
964 				goto fail;
965 		}
966 	} else {
967 		if ((err = nvlist_add_string(*nvl, IPADM_NVP_IPNUMADDR,
968 		    "")) != 0)
969 			goto fail;
970 	}
971 	return (err);
972 fail:
973 	nvlist_free(*nvl);
974 	return (err);
975 }
976 
977 /*
978  * Read the aobjmap data store and build the in-memory representation
979  * of the aobjmap. We don't need to hold any locks while building this as
980  * we do this in very early stage of daemon coming up, even before the door
981  * is opened.
982  */
983 /* ARGSUSED */
984 extern boolean_t
985 ipmgmt_aobjmap_init(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
986     int *errp)
987 {
988 	nvpair_t		*nvp = NULL;
989 	char			*name, *strval = NULL;
990 	ipmgmt_aobjmap_t 	node;
991 	struct sockaddr_in6	*in6;
992 
993 	*errp = 0;
994 	node.am_next = NULL;
995 	for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL;
996 	    nvp = nvlist_next_nvpair(db_nvl, nvp)) {
997 		name = nvpair_name(nvp);
998 
999 		if ((*errp = nvpair_value_string(nvp, &strval)) != 0)
1000 			return (B_TRUE);
1001 		if (strcmp(IPADM_NVP_AOBJNAME, name) == 0) {
1002 			(void) strlcpy(node.am_aobjname, strval,
1003 			    sizeof (node.am_aobjname));
1004 		} else if (strcmp(IPADM_NVP_IFNAME, name) == 0) {
1005 			(void) strlcpy(node.am_ifname, strval,
1006 			    sizeof (node.am_ifname));
1007 		} else if (strcmp(IPADM_NVP_LIFNUM, name) == 0) {
1008 			node.am_lnum = atoi(strval);
1009 		} else if (strcmp(IPADM_NVP_FAMILY, name) == 0) {
1010 			node.am_family = (sa_family_t)atoi(strval);
1011 		} else if (strcmp(FLAGS, name) == 0) {
1012 			node.am_flags = atoi(strval);
1013 		} else if (strcmp(ATYPE, name) == 0) {
1014 			node.am_atype = (ipadm_addr_type_t)atoi(strval);
1015 		} else if (strcmp(IPADM_NVP_IPNUMADDR, name) == 0) {
1016 			if (node.am_atype == IPADM_ADDR_IPV6_ADDRCONF) {
1017 				in6 = (struct sockaddr_in6 *)&node.am_ifid;
1018 				if (strcmp(strval, "default") == 0) {
1019 					bzero(in6, sizeof (node.am_ifid));
1020 					node.am_linklocal = B_TRUE;
1021 				} else {
1022 					(void) inet_pton(AF_INET6, strval,
1023 					    &in6->sin6_addr);
1024 					if (IN6_IS_ADDR_UNSPECIFIED(
1025 					    &in6->sin6_addr))
1026 						node.am_linklocal = B_TRUE;
1027 				}
1028 			}
1029 		}
1030 	}
1031 
1032 	/* we have all the information we need, add the node */
1033 	*errp = i_ipmgmt_add_amnode(&node);
1034 
1035 	return (B_TRUE);
1036 }
1037 
1038 /*
1039  * Updates an entry from the temporary cache file, which matches the given
1040  * address object name.
1041  */
1042 /* ARGSUSED */
1043 static boolean_t
1044 ipmgmt_update_aobjmap(void *arg, nvlist_t *db_nvl, char *buf,
1045     size_t buflen, int *errp)
1046 {
1047 	ipadm_dbwrite_cbarg_t	*cb = arg;
1048 	nvlist_t		*in_nvl = cb->dbw_nvl;
1049 	uint32_t		flags = cb->dbw_flags;
1050 	char			*db_lifnumstr = NULL, *in_lifnumstr = NULL;
1051 
1052 	*errp = 0;
1053 	if (!ipmgmt_nvlist_intersects(db_nvl, in_nvl))
1054 		return (B_TRUE);
1055 
1056 	if (flags & IPMGMT_ATYPE_V6ACONF) {
1057 		if (nvlist_lookup_string(db_nvl, IPADM_NVP_LIFNUM,
1058 		    &db_lifnumstr) != 0 ||
1059 		    nvlist_lookup_string(in_nvl, IPADM_NVP_LIFNUM,
1060 		    &in_lifnumstr) != 0 ||
1061 		    (atoi(db_lifnumstr) != -1 && atoi(in_lifnumstr) != -1 &&
1062 		    strcmp(db_lifnumstr, in_lifnumstr) != 0))
1063 			return (B_TRUE);
1064 	}
1065 
1066 	/* we found the match */
1067 	(void) memset(buf, 0, buflen);
1068 	if (ipadm_nvlist2str(in_nvl, buf, buflen) == 0) {
1069 		/* buffer overflow */
1070 		*errp = ENOBUFS;
1071 	}
1072 
1073 	/* stop the walker */
1074 	return (B_FALSE);
1075 }
1076 
1077 /*
1078  * Deletes an entry from the temporary cache file, which matches the given
1079  * address object name.
1080  */
1081 /* ARGSUSED */
1082 static boolean_t
1083 ipmgmt_delete_aobjmap(void *arg, nvlist_t *db_nvl, char *buf,
1084     size_t buflen, int *errp)
1085 {
1086 	ipmgmt_aobjmap_t	*nodep = arg;
1087 	char			*db_lifnumstr = NULL;
1088 
1089 	*errp = 0;
1090 	if (!ipmgmt_nvlist_match(db_nvl, NULL, nodep->am_ifname,
1091 	    nodep->am_aobjname))
1092 		return (B_TRUE);
1093 
1094 	if (nodep->am_atype == IPADM_ADDR_IPV6_ADDRCONF) {
1095 		if (nvlist_lookup_string(db_nvl, IPADM_NVP_LIFNUM,
1096 		    &db_lifnumstr) != 0 || atoi(db_lifnumstr) != nodep->am_lnum)
1097 			return (B_TRUE);
1098 	}
1099 
1100 	/* we found the match, delete the line from the db */
1101 	buf[0] = '\0';
1102 
1103 	/* stop the walker */
1104 	return (B_FALSE);
1105 }
1106 
1107 /*
1108  * Adds or deletes aobjmap node information into a temporary cache file.
1109  */
1110 extern int
1111 ipmgmt_persist_aobjmap(ipmgmt_aobjmap_t *nodep, ipadm_db_op_t op)
1112 {
1113 	int			err;
1114 	ipadm_dbwrite_cbarg_t	cb;
1115 	nvlist_t		*nvl = NULL;
1116 
1117 	if (op == IPADM_DB_WRITE) {
1118 		if ((err = i_ipmgmt_node2nvl(&nvl, nodep)) != 0)
1119 			return (err);
1120 		cb.dbw_nvl = nvl;
1121 		if (nodep->am_atype == IPADM_ADDR_IPV6_ADDRCONF)
1122 			cb.dbw_flags = IPMGMT_ATYPE_V6ACONF;
1123 		else
1124 			cb.dbw_flags = 0;
1125 
1126 		err = ipadm_rw_db(ipmgmt_update_aobjmap, &cb,
1127 		    ADDROBJ_MAPPING_DB_FILE, IPADM_FILE_MODE, IPADM_DB_WRITE);
1128 		nvlist_free(nvl);
1129 	} else {
1130 		assert(op == IPADM_DB_DELETE);
1131 
1132 		err = ipadm_rw_db(ipmgmt_delete_aobjmap, nodep,
1133 		    ADDROBJ_MAPPING_DB_FILE, IPADM_FILE_MODE, IPADM_DB_DELETE);
1134 	}
1135 	return (err);
1136 }
1137