/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2016 Argo Technologie SA.
*/
/*
* Contains DB walker functions, which are of type `db_wfunc_t';
*
* typedef boolean_t db_wfunc_t(void *cbarg, nvlist_t *db_nvl, char *buf,
* size_t bufsize, int *errp);
*
* ipadm_rw_db() walks through the data store, one line at a time and calls
* these call back functions with:
* `cbarg' - callback argument
* `db_nvl' - representing a line from DB in nvlist_t form
* `buf' - character buffer to hold modified line
* `bufsize'- size of the buffer
* `errp' - captures any error inside the walker function.
*
* All the 'write' callback functions modify `db_nvl' based on `cbarg' and
* copy string representation of `db_nvl' (using ipadm_nvlist2str()) into `buf'.
* To delete a line from the DB, buf[0] is set to `\0'. Inside ipadm_rw_db(),
* the modified `buf' is written back into DB.
*
* All the 'read' callback functions, retrieve the information from the DB, by
* reading `db_nvl' and then populate the `cbarg'.
*/
#include <stdlib.h>
#include <strings.h>
#include <errno.h>
#include <assert.h>
#include <unistd.h>
#include "ipmgmt_impl.h"
/* SCF related property group names and property names */
/*
* flag used by ipmgmt_persist_aobjmap() to indicate address type is
* IPADM_ADDR_IPV6_ADDRCONF.
*/
extern pthread_rwlock_t ipmgmt_dbconf_lock;
/* signifies whether volatile copy of data store is in use */
/*
* Checks if the database nvl, `db_nvl' contains and matches ALL of the passed
* in private nvpairs `proto', `ifname' & `aobjname'.
*/
static boolean_t
const char *aobjname)
{
char *name;
/* walk through db_nvl and retrieve all its private nvpairs */
}
/* no intersection - different protocols. */
return (B_FALSE);
}
/* no intersection - different interfaces. */
return (B_FALSE);
}
/* no intersection - different address objects */
return (B_FALSE);
}
return (B_TRUE);
}
/*
* Checks if the database nvl, `db_nvl' and the input nvl, `in_nvl' intersects.
*/
static boolean_t
{
char *name;
/* walk through in_nvl and retrieve all its private nvpairs */
}
}
/*
* Checks if the database nvl, `db_nvl', contains and matches ANY of the passed
* in private nvpairs `proto', `ifname' & `aobjname'.
*/
static boolean_t
{
char *name;
/* walk through db_nvl and retrieve all private nvpairs */
}
return (B_FALSE);
}
return (B_FALSE);
}
return (B_FALSE);
}
return (B_TRUE);
}
/*
* Retrieves the property value from the DB. The property whose value is to be
* retrieved is in `pargp->ia_pname'.
*/
/* ARGSUSED */
int *errp)
{
char *pval;
int err = 0;
*errp = 0;
return (B_TRUE);
&pval)) == 0) {
/*
* We have retrieved what we are looking for.
* Stop the walker.
*/
} else {
err = 0;
}
return (cont);
}
/*
* Removes the property value from the DB. The property whose value is to be
* removed is in `pargp->ia_pname'.
*/
/* ARGSUSED */
int *errp)
{
*errp = 0;
return (B_TRUE);
return (B_TRUE);
/*
* We found the property in the DB. If IPMGMT_REMOVE is not set then
* delete the entry from the db. If it is set, then the property is a
* multi-valued property so just remove the specified values from DB.
*/
if (*errp != 0)
return (B_FALSE);
/*
* multi-valued properties are represented as comma separated
* values. Use string tokenizer functions to split them and
* search for the value to be removed.
*/
if (pval[0] != '\0')
}
}
} else {
else
buf[0] = '\0';
return (B_FALSE);
}
if (*errp != 0)
return (B_FALSE);
/* buffer overflow */
}
} else {
buf[0] = '\0';
}
/* stop the search */
return (B_FALSE);
}
/*
* Input arguments can have IPADM_NVP_AOBJNAME or IPADM_NVP_IFNAME. A match is
* found, when one of the following occurs first.
* - the input aobjname matches the db aobjname. Return the db address.
* - the input interface matches the db interface. Return all the
* matching db lines with addresses.
*/
/* ARGSUSED */
int *errp)
{
/* Parse db nvlist */
}
return (B_TRUE);
/* Check for a match between the aobjnames or the interface name */
} else {
}
if (add_nvl) {
if (*errp == 0)
}
return (B_TRUE);
}
/*
* This function only gets called if a volatile filesystem version
* of the configuration file has been created. This only happens in the
* extremely rare case that a request has been made to update the configuration
* file at boottime while the root filesystem was read-only. This is
* really a rare occurrence now that we don't support UFS root filesystems
* any longer. This function will periodically attempt to write the
* configuration back to its location on the root filesystem. Success
* will indicate that the filesystem is no longer read-only.
*/
/* ARGSUSED */
static void *
{
int err;
for (;;) {
(void) sleep(5);
(void) pthread_rwlock_wrlock(&ipmgmt_dbconf_lock);
if (!ipmgmt_rdonly_root)
break;
if (err == 0) {
break;
}
(void) pthread_rwlock_unlock(&ipmgmt_dbconf_lock);
}
(void) pthread_rwlock_unlock(&ipmgmt_dbconf_lock);
return (NULL);
}
/*
* This function takes the appropriate lock, read or write, based on the
* `db_op' and then calls DB walker ipadm_rw_db(). The code is complicated
* by the fact that we are not always guaranteed to have a writable root
* filesystem since it is possible that we are reading or writing during
* bootime while the root filesystem is still read-only. This is, by far,
* the exception case. Normally, this function will be called when the
* root filesystem is writable. In the unusual case where this is not
* true, the configuration file is copied to the volatile file system
* and is updated there until the root filesystem becomes writable. At
* that time the file will be moved back to its proper location by
* ipmgmt_db_restore_thread().
*/
extern int
{
int err;
if (writeop) {
(void) pthread_rwlock_wrlock(&ipmgmt_dbconf_lock);
} else {
(void) pthread_rwlock_rdlock(&ipmgmt_dbconf_lock);
mode = 0;
}
/*
* Did a previous write attempt fail? If so, don't even try to
*/
if (!ipmgmt_rdonly_root) {
goto done;
}
/*
* If we haven't already copied the file to the volatile
* file system, do so. This should only happen on a failed
* writeop(i.e., we have acquired the write lock above).
*/
if (err != 0)
goto done;
(void) pthread_attr_init(&attr);
(void) pthread_attr_setdetachstate(&attr,
NULL);
(void) pthread_attr_destroy(&attr);
if (err != 0) {
(void) unlink(IPADM_VOL_DB_FILE);
goto done;
}
}
/*
*/
done:
(void) pthread_rwlock_unlock(&ipmgmt_dbconf_lock);
return (err);
}
/*
* Used to add an entry towards the end of DB. It just returns B_TRUE for
* every line of the DB. When we reach the end, ipadm_rw_db() adds the
* line at the end.
*/
/* ARGSUSED */
{
return (B_TRUE);
}
/*
* This function is used to update or create an entry in DB. The nvlist_t,
* `in_nvl', represents the line we are looking for. Once we ensure the right
* line from DB, we update that entry.
*/
int *errp)
{
*errp = 0;
return (B_TRUE);
break;
}
return (B_TRUE);
return (B_FALSE);
/*
* If IPMGMT_APPEND is set then we are dealing with multi-valued
* properties. We append to the entry from the db, with the new value.
*/
if (flags & IPMGMT_APPEND) {
&dbstrval)) != 0)
return (B_FALSE);
instrval);
return (B_FALSE);
} else {
/* case of in-line update of a db entry */
return (B_FALSE);
}
/* buffer overflow */
}
/* we updated the DB entry, so do not continue */
return (B_FALSE);
}
/*
* For the given `cbarg->cb_ifname' interface, retrieves any persistent
* interface information (used in 'ipadm show-if')
*/
/* ARGSUSED */
int *errp)
{
char *afstr;
*errp = 0;
return (B_TRUE);
}
break;
}
return (B_FALSE); /* don't continue the walk */
}
}
} else {
}
/* Terminate the walk if we found both v4 and v6 interfaces. */
return (B_FALSE);
return (B_TRUE);
}
/*
* Deletes those entries from the database for which interface name
* matches with the given `cbarg->cb_ifname'
*/
/* ARGSUSED */
int *errp)
{
char *afstr;
char *aobjname;
*errp = 0;
return (B_TRUE);
goto delete;
return (B_TRUE);
}
/* Reset all the interface configurations for 'ifname' */
goto delete;
}
if (!isv6 &&
goto delete;
}
/*
* This must be an address property. Delete this
* line if there is a match in the address family.
*/
goto delete;
}
}
/*
* If aobjfound = B_FALSE, then this address is not
* available in active configuration. We should go ahead
* and delete it.
*/
if (!aobjfound)
goto delete;
}
/*
* If we are removing both v4 and v6 interface, then we get rid of
* all the properties for that interface. On the other hand, if we
* are deleting only v4 instance of an interface, then we delete v4
* properties only.
*/
switch (proto) {
case MOD_PROTO_IPV6:
if (isv6)
goto delete;
break;
case MOD_PROTO_IPV4:
if (!isv6)
goto delete;
break;
case MOD_PROTO_IP:
/* this should never be the case, today */
assert(0);
break;
}
}
/* Not found a match yet. Continue processing the db */
return (B_TRUE);
/* delete the line from the db */
buf[0] = '\0';
return (B_TRUE);
}
/*
* Deletes those entries from the database for which address object name
* matches with the given `cbarg->cb_aobjname'
*/
/* ARGSUSED */
int *errp)
{
*errp = 0;
return (B_TRUE);
/* delete the line from the db */
buf[0] = '\0';
return (B_TRUE);
}
/*
* Retrieves all interface props, including addresses, for given interface(s).
* `invl' contains the list of interfaces, for which information need to be
* retrieved.
*/
/* ARGSUSED */
int *errp)
{
char *db_ifname;
*errp = 0;
char *pstr;
&pstr) == 0) {
if (proto == MOD_PROTO_IPV4)
else if (proto == MOD_PROTO_IPV6)
else
} else {
else
}
}
if (*errp == 0)
}
}
return (B_TRUE);
}
/*
* helper function for ipmgmt_aobjmap_op(). Adds the node pointed by `nodep'
* into `aobjmap' structure.
*/
static int
{
return (ENOMEM);
/* Add the node at the beginning of the list */
} else {
}
return (0);
}
/*
* A recursive function to generate alphabetized number given a decimal number.
* Decimal 0 to 25 maps to 'a' to 'z' and then the counting continues with 'aa',
* 'ab', 'ac', et al.
*/
static void
{
if (num >= 26)
(*cp)++;
}
}
/*
* This function generates an `aobjname', when required, and then does
* lookup-add. If `nodep->am_aobjname' is not an empty string, then it walks
* through the `aobjmap' to check if an address object with the same
* `nodep->am_aobjname' exists. If it exists, EEXIST is returned as duplicate
* `aobjname's are not allowed.
*
* If `nodep->am_aobjname' is an empty string then the daemon generates an
* `aobjname' using the `am_nextnum', which contains the next number to be
* used to generate `aobjname'. `am_nextnum' is converted to base26 using
* `a-z' alphabets in i_ipmgmt_num2priv_aobjname().
*
* `am_nextnum' will be 0 to begin with. Every time an address object that
* needs `aobjname' is added it's incremented by 1. So for the first address
* object on net0 the `am_aobjname' will be net0/_a and `am_nextnum' will be 1.
* For the second address object on that interface `am_aobjname' will be net0/_b
* and `am_nextnum' will incremented to 2.
*/
static int
{
break;
/*
* if `aobjname' is empty, then the daemon has to generate the
* next `aobjname' for the given interface and family.
*/
return (EINVAL);
cp[0] = '\0';
return (EINVAL);
}
} else {
return (EEXIST);
}
}
return (i_ipmgmt_add_amnode(nodep));
}
/*
* Performs following operations on the global `aobjmap' linked list.
* (a) ADDROBJ_ADD: add or update address object in `aobjmap'
* (b) ADDROBJ_DELETE: delete address object from `aobjmap'
* (c) ADDROBJ_LOOKUPADD: place a stub address object in `aobjmap'
* (d) ADDROBJ_SETLIFNUM: Sets the lifnum for an address object in `aobjmap'
*/
int
{
int err = 0;
switch (op) {
case ADDROBJ_ADD:
/*
* check for stub nodes (added by ADDROBJ_LOOKUPADD) and
* update, else add the new node.
*/
/*
* For IPv6, we need to distinguish between the
* linklocal and non-linklocal nodes
*/
nodep->am_aobjname) == 0 &&
break;
}
/* update the node */
}
} else {
break;
}
head->am_nextnum);
}
break;
case ADDROBJ_DELETE:
nodep->am_aobjname) == 0) {
/*
* There could be multiple IPV6_ADDRCONF nodes,
* with same address object name, so check for
* logical number also.
*/
break;
}
}
/*
* If the address object is in both active and
* persistent configuration and the user is deleting it
* only from active configuration then mark this node
* for deletion by reseting IPMGMT_ACTIVE bit.
* With this the same address object name cannot
* be reused until it is permanently removed.
*/
/* Update flags in the in-memory map. */
/* Update info in file. */
} else {
/* otherwise delete the node */
else
}
} else {
}
break;
case ADDROBJ_LOOKUPADD:
break;
case ADDROBJ_SETLIFNUM:
break;
}
nodep->am_aobjname) == 0) {
}
}
break;
/* update the lifnum */
} else {
}
break;
default:
assert(0);
}
return (err);
}
/*
* Given a node in `aobjmap', this function converts it into nvlist_t structure.
* The content to be written to DB must be represented as nvlist_t.
*/
static int
{
int err;
goto fail;
np->am_aobjname)) != 0)
goto fail;
goto fail;
goto fail;
goto fail;
goto fail;
goto fail;
if (np->am_linklocal &&
"default")) != 0)
goto fail;
} else {
IPMGMT_STRSIZE) == NULL) {
goto fail;
}
strval)) != 0)
goto fail;
}
} else {
"")) != 0)
goto fail;
}
return (err);
fail:
nvlist_free(*nvl);
return (err);
}
/*
* Read the aobjmap data store and build the in-memory representation
* of the aobjmap. We don't need to hold any locks while building this as
* we do this in very early stage of daemon coming up, even before the door
* is opened.
*/
/* ARGSUSED */
extern boolean_t
int *errp)
{
*errp = 0;
return (B_TRUE);
sizeof (node.am_aobjname));
} else {
}
}
}
}
/* we have all the information we need, add the node */
return (B_TRUE);
}
/*
* Updates an entry from the temporary cache file, which matches the given
* address object name.
*/
/* ARGSUSED */
static boolean_t
{
*errp = 0;
return (B_TRUE);
if (flags & IPMGMT_ATYPE_V6ACONF) {
&db_lifnumstr) != 0 ||
&in_lifnumstr) != 0 ||
return (B_TRUE);
}
/* we found the match */
/* buffer overflow */
}
/* stop the walker */
return (B_FALSE);
}
/*
* Deletes an entry from the temporary cache file, which matches the given
* address object name.
*/
/* ARGSUSED */
static boolean_t
{
*errp = 0;
nodep->am_aobjname))
return (B_TRUE);
return (B_TRUE);
}
/* we found the match, delete the line from the db */
buf[0] = '\0';
/* stop the walker */
return (B_FALSE);
}
/*
* Adds or deletes aobjmap node information into a temporary cache file.
*/
extern int
{
int err;
if (op == IPADM_DB_WRITE) {
return (err);
else
} else {
}
return (err);
}
/*
* upgrades the ipadm data-store. It renames all the old private protocol
* property names which start with leading protocol names to begin with
* IPADM_PRIV_PROP_PREFIX.
*/
/* ARGSUSED */
int *errp)
{
*errp = 0;
/*
* We are interested in lines which contain protocol properties. We
* walk through other lines in the DB.
*/
return (B_TRUE);
}
/*
* extract the propname from the `db_nvl' and also extract the
* protocol from the `db_nvl'.
*/
return (B_TRUE);
} else {
return (B_TRUE);
}
}
/* if the private property is in the right format return */
strlen(IPADM_PERSIST_PRIVPROP_PREFIX)) == 0) {
return (B_TRUE);
}
/* if it's a public property move onto the next property */
&nproto) != 0) {
return (B_TRUE);
}
/* replace the old protocol with new protocol, if required */
protostr) != 0) {
return (B_TRUE);
}
}
/* replace the old property name with new property name, if required */
/* add the prefix to property name */
return (B_TRUE);
}
/* buffer overflow */
}
return (B_TRUE);
}
/*
* Called during boot.
*
* Walk through the DB and apply all the global module properties. We plow
* through the DB even if we fail to apply property.
*/
/* ARGSUSED */
static boolean_t
int *errp)
{
/*
* We could have used nvl_exists() directly, however we need several
* calls to it and each call traverses the list. Since this codepath
* is exercised during boot, let's traverse the list ourselves and do
* the necessary checks.
*/
if (IPADM_PRIV_NVP(name)) {
return (B_TRUE);
return (B_TRUE);
} else {
/* possible a property */
}
}
/* if we are here than we found a global property */
strlen(IPADM_PERSIST_PRIVPROP_PREFIX)) == 0) {
/* private protocol property */
} else {
}
IPADM_OPT_ACTIVE) != IPADM_SUCCESS) {
pname);
}
}
return (B_TRUE);
}
/* initialize global module properties */
void
{
"persisted protocol properties");
return;
}
/* ipmgmt_db_init() logs warnings if there are any issues */
}
void
{
}
/*
* It creates the necessary SCF handles and binds the given `fmri' to an
* instance. These resources are required for retrieving property value,
* creating property groups and modifying property values.
*/
int
{
return (-1);
return (-1);
}
goto failure;
goto failure;
}
/* we will create the rest of the resources on demand */
return (0);
scf_strerror(scf_error()));
return (-1);
}
/*
* persists the `pval' for a given property `pname' in SCF. The only supported
* SCF property types are INTEGER and ASTRING.
*/
static int
{
goto failure;
switch (ptype) {
case SCF_TYPE_INTEGER:
break;
case SCF_TYPE_ASTRING:
scf_strerror(scf_error()));
goto failure;
}
break;
default:
goto failure;
}
goto failure;
goto failure;
goto failure;
goto failure;
if (new) {
goto failure;
}
} else {
goto failure;
}
}
goto failure;
if (result == 0) {
goto failure;
}
goto retry;
}
if (result == -1)
goto failure;
return (0);
scf_strerror(scf_error()));
return (-1);
}
/*
* Given a `pgname'/`pname', it retrieves the value based on `ptype' and
* places it in `pval'.
*/
static int
{
if (numvals <= 0)
goto ret;
switch (ptype) {
case SCF_TYPE_INTEGER:
break;
case SCF_TYPE_ASTRING:
break;
}
ret:
return (numvals);
}
/*
* It stores the `pval' for given `pgname'/`pname' property group in SCF.
*/
static int
{
scf_strerror(scf_error()));
return (-1);
}
"Error adding property group '%s/%s': %s",
return (-1);
}
/*
* if the property group already exists, then we get the
* composed view of the property group for the given instance.
*/
scf_strerror(scf_error()));
return (-1);
}
}
}
/*
* Returns B_TRUE, if the non-global zone is being booted for the first time
* after being installed. This is required to setup the ipadm data-store for
* the first boot of the non-global zone. Please see, PSARC 2010/166,
* for more info.
*
* Note that, this API cannot be used to determine first boot post image-update.
* 'pkg image-update' clones the current BE and the existing value of
* ipmgmtd/first_boot_done will be carried forward and obviously it will be set
* to B_TRUE.
*/
{
char *strval;
/* we always err on the side of caution */
return (bval);
SCF_TYPE_ASTRING) > 0) {
} else {
/*
* IPMGMTD_PROP_FBD does not exist in the SCF. Lets create it.
* Since we err on the side of caution, we ignore the return
* error and return B_TRUE.
*/
}
return (bval);
}
/*
* Returns B_TRUE, if the data-store needs upgrade otherwise returns B_FALSE.
* Today we have to take care of, one case of, upgrading from version 0 to
* version 1, so we will use boolean_t as means to decide if upgrade is needed
* or not. Further, the upcoming projects might completely move the flatfile
* data-store into SCF and hence we shall keep this interface simple.
*/
{
&verp, SCF_TYPE_INTEGER) > 0) {
if (*verp == IPADM_DB_VERSION)
}
/*
* 'datastore_version' doesn't exist. Which means that we need to
* upgrade the datastore. We will create 'datastore_version' and set
* the version value to IPADM_DB_VERSION, after we upgrade the file.
*/
return (bval);
}
/*
* This is called after the successful upgrade of the local data-store. With
* the data-store upgraded to recent version we don't have to do anything on
* subsequent reboots.
*/
void
{
}