root/usr/src/cmd/cmd-inet/usr.sbin/routeadm/routeadm.c
/*
 * 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
 * or http://www.opensolaris.org/os/licensing.
 * 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 2006 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <limits.h>
#include <ctype.h>
#include <stropts.h>
#include <errno.h>
#include <libintl.h>
#include <locale.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <inet/ip.h>
#include <inet/nd.h>
#include <net/if.h>
#include <libscf.h>
#include <libscf_priv.h>
#include <libuutil.h>

/*
 * This program moves routing management under SMF.  We do this by giving
 * routeadm options that allow interaction with SMF services.  These include:
 * - setting the routing services routeadm will enable
 *      # routeadm -s routing-svcs="fmri [fmri...]"
 * where each fmri is an SMF routing service.
 * - changing properties of routing services
 *      # routeadm -m fmri key=value [key=value...]
 * - listing routing daemon properties
 *      # routeadm -l fmri
 * where all properties in the "routing" property group are listed.
 *
 * By providing legacy routing services (legacy-routing:ipv4 and ipv6), we
 * can also support running of routing daemons with no SMF service under SMF.
 * Specifying a routing daemon with no SMF counterpart results in the
 * daemon, it`s arguments and stop command being set in the appropriate instance
 * to be picked up by start/stop methods.
 *
 * Internally, routeadm keeps track of routing services by setting the
 * "current-routing-svc" property to "true" in the services it manages.
 * So for example, running
 *      # routeadm -s routing-svcs="route:default ripng:default"
 * sets this variable in each instance specified. If the user specifies a
 * non-SMF routing daemon via
 *      # routeadm -s ipv4-routing-daemon=/usr/sbin/mydaemon
 * the variable will be set for the legacy-routing:ipv4 instance.
 *
 * In order to ensure that the SMF versions of routing daemons are used
 * where possible, routeadm will check the daemons specified in
 * ipv4-routing-daemon/ipv6-routing-daemon to determine if there is an
 * SMF counterpart.  If so, rather than running the legacy service
 * we move configuration, specifically the associated daemon arguments
 * to the SMF counterpart.  From there,  when the daemon is enabled,  it
 * will pick up the daemon arguments setting,  transfer the argument string
 * to the appropriate properties and run the service.
 *
 * To support the semantics of routeadm -e (enable at next boot) through SMF,
 * we make use of temporary state changes,  which last only until reboot.
 * For example, if a service is disabled,  and it is to be enabled via
 * routeadm -e,  we simply change the disable to a temporary disable,
 * and set the persistent enabled value to true.  This ensures the daemon
 * will run at next boot,  but not now.  The reverse is true for disabling
 * enabled instances  (and if the daemon is enabled when we issue the enable,
 * we do nothing since it is already in the desired state).
 *
 * Since the code is quite involved,  we provide a guide to the more complex
 * actions taken in response to user commands.
 *
 * routeadm -e[d] ipv4[6]-routing[forwarding]
 *
 *      In this case,  the goal is to prepare the configured routing daemons
 *      (specified through routeadm -s routing-svcs="...") or forwarding
 *      services to switch on (-e) or of (-d) at next boot.
 *
 *      Since this operation must be applied to multiple services in the
 *      routing daemon case (as opposed to the single ipv4[6]-forwarding
 *      service),  we make use of the scf_walk_fmri() function,  which
 *      applies a callback function to all matching functions.  In the case
 *      of the routing daemons,  we pass in a NULL signifying that all
 *      instances should be walked  (we then weed out the relevant routing
 *      services through presence of the routeadm/protocol property).  In
 *      the case of enable, a routing service is enabled IFF it has the
 *      previously-mentioned property - with an appropriate value (i.e. ipv4
 *      for "routeadm -e ipv4-routing") - and it has routeadm/curr-routing-svc
 *      property set to true  (this is set by other operations such as
 *      routeadm -s routing-svcs="...").  Then,  smf_enable_instance() or
 *      smf_disable_instance() is called,  setting the temporary state to
 *      the current state of the service.  This then allows setting of
 *      general/enabled value to next-boot value.  In the case of disabling
 *      ipv4[6]-routing,  all valid ipv4[6] routing daemons are prepared
 *      for next-boot disable, not just those specified via routing-svcs (this
 *      means that if the user enables routing daemons with "svcadm enable",
 *      disabling global routing does really switch off all routing daemons).
 *
 *      This is implemented through the ra_get_set_opt_common_cb() function,
 *      called by the ra_set_persistent_opt_cb() function.  The same
 *      function can be used for both routing and forwarding options,  in the
 *      latter case we simply provide the specific FMRI of the forwarding
 *      service in question (ipv4-forwarding or ipv6-forwarding),  and dispense
 *      with the eligibility tests we need to weed out the routing services
 *      from the rest.
 *
 *      Before we initiate the "enable" however, we must check routing daemons
 *      specified via the legacy variables (ipv4-routing-daemon etc).
 *      If they map to SMF routing services,  we wish to transfer their
 *      configuration to the corresponding services and use them instead of
 *      the legacy services.  To do this,  we need to match the daemon program
 *      against the routeadm/daemon property of each routing daemon (we use
 *      scf_walk_fmri() and the routeadm/protocol property again to identify
 *      daemons).  If a match is found,  the daemon arguments are transferred
 *      to the appropriate service`s daemon-args property, to be picked up
 *      by it`s start method and converted into appropriate property values.
 *      This is accomplished by ra_check_legacy_daemons(), and the callback
 *      operation is carried out by ra_upgrade_legacy_daemons_cb().  If the
 *      daemon was not upgraded,  we need to mark the legacy-routing:ipv4[6]
 *      instance to be enabled (by routeadm -e),  since it now must run the
 *      un-upgradeable legacy daemon.
 *
 * routeadm -l fmri
 *
 *      Lists all properties and values in the routing property group associated
 *      with instance fmri.  We simply walk through the composed property
 *      group, displaying all values.  See ra_list_props_cb().
 *
 * routeadm -m fmri key=value ...
 *
 *      Modify property values in the routing property group.  If the same
 *      key is used more than once,  multiple property values are set for that
 *      property.  Properties must exist in the composed property group,  but
 *      will only ever be set at the instance level to prevent multiple
 *      instances inheriting the property in error.  See ra_modify_props_cb().
 *
 * routeadm -s var=value
 *
 *      In all cases bar the routing-svcs variable,  this simply involves
 *      setting the appropriate SMF property value for the variable.  The
 *      routing-svcs case is more complex,  since we would like operations
 *      like the following to have intuitive effects:
 *              # routeadm -s routing-svcs=route -e ipv4-routing -u
 *              # routeadm -s routing-svcs=rdisc -u
 *      i.e., in the end, rdisc is the only routing service running.  To
 *      accomplish this switchover,  we need to disable the old routing-svcs
 *      and enable the new, marking the latter with the curr-routing-svc
 *      property so that routeadm -e will pick them up.  This is carried
 *      out by the ra_update_routing_svcs() function.
 *
 * routeadm -R alt_root ...
 *
 *      Used to support use of routeadm in Custom Jumpstart scripts,  this
 *      option causes all subsequent commands to be appended to the
 *      /var/svc/profile/upgrade file,  which is run on the subsequent boot.
 *      This is done because the SMF repository is not available to make
 *      the modifications to property values required in routeadm operations.
 *
 * routeadm -u
 *
 *      Update applies the "next boot" state to the current system.  Here
 *      we simply take the persistent state (general/enabled value) and
 *      make it the current state through smf_enable_instance() or
 *      smf_disable_instance() as appropriate (these calls,  without the
 *      temporary flag set,  delete the general_ovr/enabled property).
 */

#define RA_OPT_IPV4_ROUTING     "ipv4-routing"
#define RA_OPT_IPV6_ROUTING     "ipv6-routing"
#define RA_OPT_IPV4_FORWARDING  "ipv4-forwarding"
#define RA_OPT_IPV6_FORWARDING  "ipv6-forwarding"

#define IS_ROUTING_OPT(opt)     (strcmp(opt, RA_OPT_IPV4_ROUTING) == 0 || \
                                strcmp(opt, RA_OPT_IPV6_ROUTING) == 0)

#define RA_VAR_IPV4_ROUTING_DAEMON      "ipv4-routing-daemon"
#define RA_VAR_IPV4_ROUTING_DAEMON_ARGS "ipv4-routing-daemon-args"
#define RA_VAR_IPV4_ROUTING_STOP_CMD    "ipv4-routing-stop-cmd"
#define RA_VAR_IPV6_ROUTING_DAEMON      "ipv6-routing-daemon"
#define RA_VAR_IPV6_ROUTING_DAEMON_ARGS "ipv6-routing-daemon-args"
#define RA_VAR_IPV6_ROUTING_STOP_CMD    "ipv6-routing-stop-cmd"
#define RA_VAR_ROUTING_SVCS             "routing-svcs"


#define RA_INSTANCE_ALL                 NULL
#define RA_INSTANCE_ROUTING_SETUP       "svc:/network/routing-setup:default"
#define RA_INSTANCE_IPV4_FORWARDING     "svc:/network/ipv4-forwarding:default"
#define RA_INSTANCE_IPV6_FORWARDING     "svc:/network/ipv6-forwarding:default"
#define RA_INSTANCE_LEGACY_ROUTING_IPV4 \
        "svc:/network/routing/legacy-routing:ipv4"
#define RA_INSTANCE_LEGACY_ROUTING_IPV6 \
        "svc:/network/routing/legacy-routing:ipv6"
#define RA_INSTANCE_NDP                 "svc:/network/routing/ndp:default"

#define RA_PG_ROUTEADM                  "routeadm"
#define RA_PROP_CURR_ROUTING_SVC        "current-routing-svc"
#define RA_PROP_ROUTING_SVCS            "routing-svcs"
#define RA_PROP_DEFAULT_ROUTING_SVCS    "default-routing-svcs"
#define RA_PROP_PROTO                   "protocol"
#define RA_PROP_DAEMON                  "daemon"
#define RA_PROP_DEFAULT_DAEMON          "default-daemon"
#define RA_PROP_DAEMON_ARGS             "daemon-args"
#define RA_PROP_DEFAULT_DAEMON_ARGS     "default-daemon-args"
#define RA_PROP_DAEMON_STOP_CMD         "daemon-stop-cmd"
#define RA_PROP_DEFAULT_STOP_CMD        "default-daemon"
#define RA_PROP_LEGACY_DAEMON           "legacy-daemon"
#define RA_PROP_DEFAULT_IPV4_ROUTING    "default-ipv4-routing"
#define RA_PROP_DEFAULT_IPV6_ROUTING    "default-ipv6-routing"
#define RA_PROP_DEFAULT_IPV4_FORWARDING "default-ipv4-forwarding"
#define RA_PROP_DEFAULT_IPV6_FORWARDING "default-ipv6-forwarding"
#define RA_PROP_IPV4_ROUTING_SET        "ipv4-routing-set"
#define RA_PROP_IPV6_ROUTING_SET        "ipv6-routing-set"
#define RA_PROP_ROUTING_CONF_READ       "routing-conf-read"

#define RA_PG_ROUTING                   "routing"

#define RA_PROPVAL_BOOLEAN_TRUE         "true"
#define RA_PROPVAL_BOOLEAN_FALSE        "false"
#define RA_PROPVAL_PROTO_IPV4           "ipv4"
#define RA_PROPVAL_PROTO_IPV6           "ipv6"

#define RA_SVC_FLAG_NONE                0x0
#define RA_SVC_FLAG_IPV4_ROUTING        0x1
#define RA_SVC_FLAG_IPV6_ROUTING        0x2

#define RA_SMF_UPGRADE_FILE             "/var/svc/profile/upgrade"
#define RA_SMF_UPGRADE_MSG              " # added by routeadm(8)"
#define RA_CONF_FILE                    "/etc/inet/routing.conf"
#define RA_CONF_FILE_OLD                "/etc/inet/routing.conf.old"
#define RA_MAX_CONF_LINE                256

/*
 * Option value.  Each option requires an FMRI identifying which services
 * to run the get_current/persistent scf_walk_fmri() function with,  and
 * associated flags (to ensure that in the case that multiple services
 * match, we select the correct ones). In addition, we specify the FMRI
 * and property used to set default option value.  The opt_enabled field
 * is used to hold retrieved state from get_*_opt_() callbacks and to specify
 * desired state for set_*_opt() operations.
 */

typedef struct raopt {
        const char      *opt_name;
        const char      *opt_fmri;
        int             opt_flags;
        boolean_t       opt_enabled;
        const char      *opt_default_fmri;
        const char      *opt_default_prop;
        boolean_t       opt_default_enabled;
} raopt_t;


raopt_t ra_opts[] = {
        { RA_OPT_IPV4_ROUTING, RA_INSTANCE_ALL, RA_SVC_FLAG_IPV4_ROUTING,
        B_FALSE, RA_INSTANCE_ROUTING_SETUP, RA_PROP_DEFAULT_IPV4_ROUTING,
        B_FALSE },
        { RA_OPT_IPV6_ROUTING, RA_INSTANCE_ALL, RA_SVC_FLAG_IPV6_ROUTING,
        B_FALSE, RA_INSTANCE_ROUTING_SETUP, RA_PROP_DEFAULT_IPV6_ROUTING,
        B_FALSE },
        { RA_OPT_IPV4_FORWARDING, RA_INSTANCE_IPV4_FORWARDING, RA_SVC_FLAG_NONE,
        B_FALSE, RA_INSTANCE_IPV4_FORWARDING, RA_PROP_DEFAULT_IPV4_FORWARDING,
        B_FALSE },
        { RA_OPT_IPV6_FORWARDING, RA_INSTANCE_IPV6_FORWARDING, RA_SVC_FLAG_NONE,
        B_FALSE, RA_INSTANCE_IPV6_FORWARDING, RA_PROP_DEFAULT_IPV6_FORWARDING,
        B_FALSE },
        { NULL, NULL, RA_SVC_FLAG_NONE, B_FALSE, NULL, NULL, B_FALSE }
};

typedef enum option_values {
        OPT_INVALID, OPT_ENABLED, OPT_DISABLED, OPT_DEFAULT, OPT_UNKNOWN
} oval_t;

typedef struct ra_var {
        const char      *var_name;
        const char      *var_fmri;
        const char      *var_prop;
        char            *var_value;
        const char      *var_default_fmri;
        const char      *var_default_prop;
        char            *var_default_value;
} ravar_t;

ravar_t ra_vars[] = {
        { RA_VAR_IPV4_ROUTING_DAEMON, RA_INSTANCE_LEGACY_ROUTING_IPV4,
        RA_PROP_DAEMON, NULL, RA_INSTANCE_LEGACY_ROUTING_IPV4,
        RA_PROP_DEFAULT_DAEMON, NULL},
        { RA_VAR_IPV4_ROUTING_DAEMON_ARGS, RA_INSTANCE_LEGACY_ROUTING_IPV4,
        RA_PROP_DAEMON_ARGS, NULL, RA_INSTANCE_LEGACY_ROUTING_IPV4,
        RA_PROP_DEFAULT_DAEMON_ARGS, NULL },
        { RA_VAR_IPV4_ROUTING_STOP_CMD, RA_INSTANCE_LEGACY_ROUTING_IPV4,
        RA_PROP_DAEMON_STOP_CMD, NULL, RA_INSTANCE_LEGACY_ROUTING_IPV4,
        RA_PROP_DEFAULT_STOP_CMD, NULL },
        { RA_VAR_IPV6_ROUTING_DAEMON, RA_INSTANCE_LEGACY_ROUTING_IPV6,
        RA_PROP_DAEMON, NULL, RA_INSTANCE_LEGACY_ROUTING_IPV6,
        RA_PROP_DEFAULT_DAEMON, NULL },
        { RA_VAR_IPV6_ROUTING_DAEMON_ARGS, RA_INSTANCE_LEGACY_ROUTING_IPV6,
        RA_PROP_DAEMON_ARGS, NULL, RA_INSTANCE_LEGACY_ROUTING_IPV6,
        RA_PROP_DEFAULT_DAEMON_ARGS, NULL },
        { RA_VAR_IPV6_ROUTING_STOP_CMD, RA_INSTANCE_LEGACY_ROUTING_IPV6,
        RA_PROP_DAEMON_STOP_CMD, NULL, RA_INSTANCE_LEGACY_ROUTING_IPV6,
        RA_PROP_DEFAULT_STOP_CMD, NULL },
        { RA_VAR_ROUTING_SVCS, RA_INSTANCE_ROUTING_SETUP,
        RA_PROP_ROUTING_SVCS, NULL, RA_INSTANCE_ROUTING_SETUP,
        RA_PROP_DEFAULT_ROUTING_SVCS, NULL },
        { NULL, NULL, NULL, NULL, NULL, NULL, NULL }
};

char *v_opt[] = {
#define IPV4_ROUTING_DAEMON                     0
        RA_VAR_IPV4_ROUTING_DAEMON,
#define IPV4_ROUTING_DAEMON_ARGS                1
        RA_VAR_IPV4_ROUTING_DAEMON_ARGS,
#define IPV4_ROUTING_STOP_CMD                   2
        RA_VAR_IPV4_ROUTING_STOP_CMD,
#define IPV6_ROUTING_DAEMON                     3
        RA_VAR_IPV6_ROUTING_DAEMON,
#define IPV6_ROUTING_DAEMON_ARGS                4
        RA_VAR_IPV6_ROUTING_DAEMON_ARGS,
#define IPV6_ROUTING_STOP_CMD                   5
        RA_VAR_IPV6_ROUTING_STOP_CMD,
#define ROUTING_SVCS                            6
        RA_VAR_ROUTING_SVCS,
        NULL
};

#define IS_IPV4_VAR(varname)    (strncmp(varname, "ipv4", 4) == 0)
#define IS_IPV6_VAR(varname)    (strncmp(varname, "ipv6", 4) == 0)
#define VAR_PROTO_MATCH(varname, proto) (strncmp(varname, proto, 4) == 0)
#define IPV4_VARS_UNSET \
        (strtok(ra_vars[IPV4_ROUTING_DAEMON].var_value, " \t") == NULL && \
        strtok(ra_vars[IPV4_ROUTING_DAEMON_ARGS].var_value, " \t") == NULL && \
        strtok(ra_vars[IPV4_ROUTING_STOP_CMD].var_value, " \t") == NULL)

#define IPV6_VARS_UNSET \
        (strtok(ra_vars[IPV6_ROUTING_DAEMON].var_value, " \t") == NULL && \
        strtok(ra_vars[IPV6_ROUTING_DAEMON_ARGS].var_value, " \t") == NULL && \
        strtok(ra_vars[IPV6_ROUTING_STOP_CMD].var_value, " \t") == NULL)

/*
 * Structure used in modify operations to tie property name and multiple values
 * together.
 */
typedef struct ra_prop {
        char    *prop_name;
        char    **prop_values;
        int     prop_numvalues;
} ra_prop_t;

typedef int (*ra_smf_cb_t)(void *, scf_walkinfo_t *);

/* Used to store program name */
static const char       *myname;

static void usage(void);

static int ra_check_legacy_daemons(void);
static int ra_upgrade_legacy_daemons(void);
static int ra_upgrade_cmd(char, int, char **);
static int ra_update(void);
static int ra_update_routing_svcs(char *);
static int ra_report(boolean_t, const char *);
static int ra_smf_cb(ra_smf_cb_t, const char *, void *);
static int ra_upgrade_from_legacy_conf(void);
static int ra_numv6intfs(void);
static int ra_parseconf(void);
static int ra_parseopt(char *, int, raopt_t *);
static int ra_parsevar(char *, ravar_t *);
static oval_t ra_str2oval(const char *);
static raopt_t *ra_str2opt(const char *);
static void ra_resetopts(void);
static ravar_t *ra_str2var(const char *);
static void ra_resetvars(const char *);
static char *ra_intloptname(const char *);

/* Callback for upgrade of legacy daemons */
static int ra_upgrade_legacy_daemons_cb(void *, scf_walkinfo_t *);

/* Callbacks used to set/retieve routing options */
static int ra_set_current_opt_cb(void *, scf_walkinfo_t *);
static int ra_set_persistent_opt_cb(void *, scf_walkinfo_t *);
static int ra_set_default_opt_cb(void *, scf_walkinfo_t *);
static int ra_get_current_opt_cb(void *, scf_walkinfo_t *);
static int ra_get_persistent_opt_cb(void *, scf_walkinfo_t *);
static int ra_get_default_opt_cb(void *, scf_walkinfo_t *);
static int ra_get_set_opt_common_cb(raopt_t *, scf_walkinfo_t *, boolean_t,
    boolean_t);
static int ra_routing_opt_set_cb(void *, scf_walkinfo_t *);
static int ra_routing_opt_unset_cb(void *, scf_walkinfo_t *);
static int ra_routing_opt_set_unset_cb(raopt_t *, scf_walkinfo_t *, boolean_t);

/* Callbacks used to set/retrieve routing variables */
static int ra_set_persistent_var_cb(void *, scf_walkinfo_t *);
static int ra_get_persistent_var_cb(void *, scf_walkinfo_t *);
static int ra_get_default_var_cb(void *, scf_walkinfo_t *);
static int ra_mark_routing_svcs_cb(void *, scf_walkinfo_t *);

/* Callbacks used to list/set daemon properties and list daemons and states. */
static int ra_list_props_cb(void *, scf_walkinfo_t *);
static int ra_modify_props_cb(void *, scf_walkinfo_t *);
static int ra_print_state_cb(void *, scf_walkinfo_t *);

/* Utility functions for SMF operations */
static int ra_get_pg(scf_handle_t *, scf_instance_t *, const char *,
    boolean_t, boolean_t, scf_propertygroup_t **);
static int ra_get_boolean_prop(scf_handle_t *, scf_instance_t *,
    const char *, const char *,  boolean_t, boolean_t, boolean_t *);
static int ra_get_single_prop_as_string(scf_handle_t *, scf_instance_t *,
    const char *, const char *, boolean_t, boolean_t, scf_type_t *, char **);
static int ra_get_prop_as_string(scf_handle_t *, scf_instance_t *,
    const char *, const char *, boolean_t, boolean_t, scf_type_t *, int *,
    char ***);
static void ra_free_prop_values(int, char **);
static int ra_set_boolean_prop(scf_handle_t *, scf_instance_t *,
    const char *, const char *, boolean_t, boolean_t);
static int ra_set_prop_from_string(scf_handle_t *, scf_instance_t *,
    const char *, const char *, scf_type_t, boolean_t, int,
    const char **);

static void
usage(void)
{
        (void) fprintf(stderr, gettext(
            "usage: %1$s [-p] [-R <root-dir>]\n"
            "       %1$s [-e <option>] [-d <option>] [-r <option>]\n"
            "           [-l <FMRI>] [-m <FMRI> key=value [...]]\n"
            "           [-s <var>=<val>] [-R <root-dir>]\n"
            "       %1$s -u\n\n"
            "       <option> is one of:\n"
            "       ipv4-forwarding\n"
            "       ipv4-routing\n"
            "       ipv6-forwarding\n"
            "       ipv6-routing\n\n"
            "       <var> is one of:\n"
            "       ipv4-routing-daemon\n"
            "       ipv4-routing-daemon-args\n"
            "       ipv4-routing-stop-cmd\n"
            "       ipv6-routing-daemon\n"
            "       ipv6-routing-daemon-args\n"
            "       ipv6-routing-stop-cmd\n"
            "       routing-svcs\n"), myname);
}

int
main(int argc, char *argv[])
{
        int             opt, opt_index, numargs, status = 0;
        int             numvalues, i;
        ssize_t         keylen;
        boolean_t       modify = B_FALSE, report = B_TRUE, update = B_FALSE;
        boolean_t       booting = B_FALSE, alt_root_set = B_FALSE;
        boolean_t       parseable = B_FALSE;
        char            *key, *nk, *keyend, *val, **vals, *options, *fmri;
        char            *parseopt = NULL;
        raopt_t         *raopt;
        ravar_t         *ravar;
        ra_prop_t       raprop;

        myname = argv[0];

        (void) setlocale(LC_ALL, "");

#if     !defined(TEXT_DOMAIN)   /* Should be defined by cc -D */
#define TEXT_DOMAIN     "SYS_TEST"
#endif

        (void) textdomain(TEXT_DOMAIN);

        /*
         * Before processing any options, we parse /etc/inet/routing.conf
         * (if present) and transfer values to SMF.
         */
        if (ra_upgrade_from_legacy_conf() == -1)
                exit(EXIT_FAILURE);
        while ((opt = getopt(argc, argv, ":bd:e:l:m:p:R:r:s:u")) != EOF) {
                switch (opt) {
                case 'b':
                        /*
                         * Project-private option that tells us enable/disable
                         * operations should not set ipv4(6)-routing-set
                         * property.  Used in routing-setup service method
                         * to change default routing state, and, if
                         * no explicit enable/disable operations have been
                         * carried out, change current ipv4 routing state.
                         */
                        booting = B_TRUE;
                        break;
                case 'd':
                case 'e':
                case 'r':
                        if (alt_root_set) {
                                if (ra_upgrade_cmd(opt, 1, &optarg) != 0)
                                        exit(EXIT_FAILURE);
                                modify = B_TRUE;
                                break;
                        }
                        if ((raopt = ra_str2opt(optarg)) != NULL) {
                                /* Set current value appropriately */
                                switch (opt) {
                                case 'd':
                                        raopt->opt_enabled = B_FALSE;
                                        break;
                                case 'e':
                                        /*
                                         * Check legacy daemons, mark
                                         * routing-svcs.
                                         */
                                        if (IS_ROUTING_OPT(optarg) &&
                                            ra_check_legacy_daemons() == -1)
                                                exit(EXIT_FAILURE);
                                        raopt->opt_enabled = B_TRUE;
                                        break;
                                case 'r':
                                        /*
                                         * This callback sets opt_enabled to
                                         * the default value.
                                         */
                                        ra_resetopts();
                                        if (ra_smf_cb(ra_get_default_opt_cb,
                                            raopt->opt_default_fmri, raopt)
                                            == -1)
                                                exit(EXIT_FAILURE);
                                        if (raopt->opt_enabled &&
                                            IS_ROUTING_OPT(optarg) &&
                                            ra_check_legacy_daemons() == -1)
                                                exit(EXIT_FAILURE);
                                        /* set value to default */
                                        raopt->opt_enabled =
                                            raopt->opt_default_enabled;
                                        break;
                                }
                                if (ra_smf_cb(ra_set_persistent_opt_cb,
                                    raopt->opt_fmri, raopt) == -1)
                                        exit(EXIT_FAILURE);
                                /*
                                 * ipv4(6)-routing explicitly enabled/disabled,
                                 * need to set ipv4(6)-routing-set property
                                 * for routing-setup service.  Once this
                                 * is set, routing-setup will not override
                                 * administrator action and will not enable
                                 * ipv4-routing in the case that no default
                                 * route can be determined.  If ipv4(6)-routing
                                 * is reverted to its default value,  set
                                 * ipv4(6)-routing-set back to false.
                                 */
                                if (!booting && (raopt->opt_flags &
                                    (RA_SVC_FLAG_IPV4_ROUTING |
                                    RA_SVC_FLAG_IPV6_ROUTING))) {
                                        if (ra_smf_cb(opt == 'r' ?
                                            ra_routing_opt_unset_cb :
                                            ra_routing_opt_set_cb,
                                            raopt->opt_default_fmri, raopt)
                                            == -1)
                                                exit(EXIT_FAILURE);
                                }
                        } else if ((ravar = ra_str2var(optarg)) != NULL) {
                                if (opt != 'r') {
                                        usage();
                                        exit(EXIT_FAILURE);
                                }
                                /* set current value to default */
                                ra_resetopts();
                                if (ra_smf_cb(ra_get_default_var_cb,
                                    ravar->var_default_fmri, ravar) == -1)
                                        exit(EXIT_FAILURE);
                                /* Need special case for routing-svcs var */
                                if (strcmp(ravar->var_name, RA_VAR_ROUTING_SVCS)
                                    == 0) {
                                        if (ra_update_routing_svcs(
                                            ravar->var_default_value) == -1)
                                                exit(EXIT_FAILURE);
                                } else if (ra_smf_cb(ra_set_persistent_var_cb,
                                    ravar->var_fmri, ravar) == -1)
                                        exit(EXIT_FAILURE);
                        } else {
                                (void) fprintf(stderr, gettext(
                                    "%1$s: invalid option: %2$s\n"), myname,
                                    optarg);
                                usage();
                                exit(EXIT_FAILURE);
                        }
                        modify = B_TRUE;
                        break;
                case 'l':
                        if (ra_smf_cb(ra_list_props_cb, optarg, NULL) == -1)
                                exit(EXIT_FAILURE);
                        report = B_FALSE;
                        break;
                case 'm':
                        fmri = optarg;
                        modify = B_TRUE;
                        /*
                         * Argument list of key=value pairs, we need to
                         * collate all matching keys to set multiple values.
                         */
                        numargs = 1;
                        i = optind;
                        for (numargs = 1; argv[i] != NULL && argv[i][0] != '-';
                            numargs++)
                                i++;
                        if (numargs == 1) {
                                (void) fprintf(stderr, gettext(
                                    "%s: key=value required for "
                                    "property change\n"), myname);
                                usage();
                                exit(EXIT_FAILURE);
                        }
                        if (alt_root_set) {
                                if (ra_upgrade_cmd(opt, numargs,
                                    &argv[optind - 1]) == -1)
                                        exit(EXIT_FAILURE);
                                optind += numargs - 1;
                                break;
                        }
                        /*
                         * Collect all key=value pairs which use same key
                         * so we can add multiple property values.
                         */
                        for (key = argv[optind]; key != NULL && key[0] != '-';
                            key = argv[++optind]) {
                                if (key[0] == '\0')
                                        continue;
                                vals = malloc(sizeof (char *));
                                if ((vals[0] = strchr(key, '=')) == NULL) {
                                        (void) fprintf(stderr, gettext(
                                            "%s: Malformed name=value "
                                            "pair %s\n"), myname, key);
                                        exit(EXIT_FAILURE);
                                }
                                numvalues = 1;
                                *(vals[0]) = '\0';
                                (vals[0])++;
                                i = optind + 1;
                                for (nk = argv[i];
                                    nk != NULL && nk[0] != '-';
                                    nk = argv[++i]) {
                                        if (nk[0] == '\0')
                                                continue;
                                        if ((keyend = strchr(nk, '='))
                                            == NULL) {
                                                (void) fprintf(stderr, gettext(
                                                    "%s: Malformed name=value "
                                                    " pair %s\n"), myname, nk);
                                                exit(EXIT_FAILURE);
                                        }
                                        if ((keylen = keyend - nk) !=
                                            strlen(key))
                                                continue;
                                        if (strncmp(key, nk, keylen) == 0) {
                                                vals = realloc(vals, ++numvalues
                                                    * sizeof (char *));
                                                vals[numvalues - 1] = ++keyend;
                                                nk[0] = '\0';
                                                optind++;
                                        }
                                }
                                raprop.prop_name = key;
                                raprop.prop_values = vals;
                                raprop.prop_numvalues = numvalues;
                                if (ra_smf_cb(ra_modify_props_cb, fmri,
                                    &raprop) == -1)
                                        exit(EXIT_FAILURE);
                        }
                        break;
                case 'p':
                        parseable = B_TRUE;
                        parseopt = optarg;
                        break;
                case 'R':
                        if (chroot(optarg) == -1) {
                                (void) fprintf(stderr, gettext(
                                    "%1$s: failed to chroot to %2$s: %3$s\n"),
                                    myname, optarg, strerror(errno));
                                exit(EXIT_FAILURE);
                        }
                        alt_root_set = B_TRUE;
                        report = B_FALSE;
                        break;
                case 's':
                        if (alt_root_set) {
                                if (ra_upgrade_cmd(opt, 1, &optarg) == -1)
                                        exit(EXIT_FAILURE);
                                modify = B_TRUE;
                                break;
                        }
                        options = optarg;
                        while (*options != '\0') {
                                opt_index = getsubopt(&options, v_opt, &val);
                                if (val == NULL) {
                                        usage();
                                        exit(EXIT_FAILURE);
                                }
                                if (opt_index == -1) {
                                        (void) fprintf(stderr, gettext(
                                            "%1$s: invalid variable: %2$s\n"),
                                            myname, optarg);
                                        usage();
                                        exit(EXIT_FAILURE);
                                }
                                ravar = &ra_vars[opt_index];
                                /* Need special case for routing-svcs var */
                                if (strcmp(ravar->var_name, RA_VAR_ROUTING_SVCS)
                                    == 0) {
                                        if (ra_update_routing_svcs(val) == -1)
                                                return (-1);
                                } else {
                                        ravar->var_value = strdup(val);
                                        if (ra_smf_cb(ra_set_persistent_var_cb,
                                            ravar->var_fmri, ravar) == -1)
                                                exit(EXIT_FAILURE);
                                }
                        }
                        modify = B_TRUE;
                        break;
                case 'u':
                        update = B_TRUE;
                        break;
                case ':':
                        /* if not 'p', usage failure */
                        if (strcmp(argv[optind - 1], "-p") != 0) {
                                (void) fprintf(stderr, gettext(
                                    "%s: option requires an argument -%s\n"),
                                    myname, argv[optind - 1]);
                                usage();
                                exit(EXIT_FAILURE);
                        }
                        parseable = B_TRUE;
                        break;
                case '?':
                        usage();
                        exit(EXIT_FAILURE);
                }
        }

        if (argc > optind) {
                /* There shouldn't be any extra args. */
                usage();
                exit(EXIT_FAILURE);
        }

        if (parseable && (update || modify)) {
                (void) fprintf(stderr, gettext("%s: the -p option cannot be "
                    "used with any of -demrsu\n"), myname);
                usage();
                exit(EXIT_FAILURE);
        }

        if (update && ! alt_root_set)
                status = ra_update();

        if (report && !modify && !update)
                status = ra_report(parseable, parseopt);

        return (status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}

/*
 * Upgrade legacy daemons,  mark to-be-enabled routing services.
 */
static int
ra_check_legacy_daemons(void)
{
        ravar_t         *routing_svcs = ra_str2var(RA_VAR_ROUTING_SVCS);
        ravar_t         *v4d = ra_str2var(RA_VAR_IPV4_ROUTING_DAEMON);
        ravar_t         *v6d = ra_str2var(RA_VAR_IPV6_ROUTING_DAEMON);
        char            *fmri, *nextfmri;
        boolean_t       mark = B_FALSE;

        if (ra_smf_cb(ra_get_persistent_var_cb, routing_svcs->var_fmri,
            routing_svcs) == -1)
                return (-1);

        /* First unmark all services */
        if (ra_smf_cb(ra_mark_routing_svcs_cb, NULL, &mark) == -1)
                return (-1);

        mark = B_TRUE;
        if (routing_svcs->var_value != NULL) {
                /*
                 * For routing-svcs variable, mark each named
                 * service as a current-routing-svc.
                 */
                if ((fmri = strdup(routing_svcs->var_value)) == NULL) {
                        (void) fprintf(stderr, gettext(
                            "%s: out of memory\n"), myname);
                        return (-1);
                }
                /* Now, mark each service named in routing-svcs. */
                for (nextfmri = strtok(fmri, " \t");
                    nextfmri != NULL;
                    nextfmri = strtok(NULL, " \t")) {
                        if (ra_smf_cb(ra_mark_routing_svcs_cb, nextfmri,
                            &mark) == -1) {
                                free(fmri);
                                return (-1);
                        }
                }
                free(fmri);
        }

        /*
         * Now check if legacy variables (if specified) map to SMF routing
         * daemons.  If so, transfer associated daemon arguments.
         */
        if (ra_upgrade_legacy_daemons() == -1)
                return (-1);

        ra_resetvars(NULL);
        /*
         * At this point, if the legacy services still have ipv4/ipv6
         * routing daemons specified, we know they weren`t upgraded, so
         * we mark them also.
         */
        if (ra_smf_cb(ra_get_persistent_var_cb, v4d->var_fmri, v4d) == -1 ||
            ra_smf_cb(ra_get_persistent_var_cb, v6d->var_fmri, v6d) == -1)
                return (-1);

        if (v4d->var_value != NULL && strtok(v4d->var_value, " \t") != NULL &&
            ra_smf_cb(ra_mark_routing_svcs_cb, RA_INSTANCE_LEGACY_ROUTING_IPV4,
            &mark) == -1)
                return (-1);
        if (v6d->var_value != NULL && strtok(v6d->var_value, " \t") != NULL &&
            ra_smf_cb(ra_mark_routing_svcs_cb, RA_INSTANCE_LEGACY_ROUTING_IPV6,
            &mark) == -1)
                return (-1);

        return (0);
}

/*
 * Retrieve legacy daemon variables,  and check if any SMF routing daemons
 * run the daemons specified.  If so, the legacy configuration (arguments
 * to the daemon) is transferred to the routeadm/daemon-args property
 * of the corresponding instance.  From there,  the instance picks up the
 * value and will transfer the daemon arguments to individiual properties
 * when enabled.
 */
static int
ra_upgrade_legacy_daemons(void)
{
        ravar_t *v4d = ra_str2var(RA_VAR_IPV4_ROUTING_DAEMON);
        ravar_t *v6d = ra_str2var(RA_VAR_IPV6_ROUTING_DAEMON);
        ravar_t *v4args = ra_str2var(RA_VAR_IPV4_ROUTING_DAEMON_ARGS);
        ravar_t *v6args = ra_str2var(RA_VAR_IPV6_ROUTING_DAEMON_ARGS);
        ravar_t *v4stop = ra_str2var(RA_VAR_IPV4_ROUTING_STOP_CMD);
        ravar_t *v6stop = ra_str2var(RA_VAR_IPV6_ROUTING_STOP_CMD);

        if (ra_smf_cb(ra_get_persistent_var_cb, v4d->var_fmri, v4d) == -1 ||
            ra_smf_cb(ra_get_persistent_var_cb, v6d->var_fmri, v6d) == -1 ||
            ra_smf_cb(ra_get_persistent_var_cb, v4args->var_fmri, v4args)
            == -1 ||
            ra_smf_cb(ra_get_persistent_var_cb, v6args->var_fmri, v6args)
            == -1 ||
            ra_smf_cb(ra_get_persistent_var_cb, v4stop->var_fmri, v4stop)
            == -1 ||
            ra_smf_cb(ra_get_persistent_var_cb, v6stop->var_fmri, v6stop)
            == -1)
                return (-1);

        return (ra_smf_cb(ra_upgrade_legacy_daemons_cb, NULL, NULL));
}

/*
 * Determine if service runs the same daemon as that which is specified
 * in ipv4-routing-daemon or ipv6-routing-daemon.  If so, the associated
 * daemon arguments are transferred to the service.
 */

/* ARGSUSED0 */
static int
ra_upgrade_legacy_daemons_cb(void *data, scf_walkinfo_t *wip)
{
        const char      *inst_fmri = wip->fmri;
        scf_instance_t  *inst = wip->inst;
        scf_handle_t    *h = scf_instance_handle(inst);
        char            *daemon, *l_daemon = NULL;
        ravar_t         *v4d = ra_str2var(RA_VAR_IPV4_ROUTING_DAEMON);
        ravar_t         *v6d = ra_str2var(RA_VAR_IPV6_ROUTING_DAEMON);
        ravar_t         *v4args = ra_str2var(RA_VAR_IPV4_ROUTING_DAEMON_ARGS);
        ravar_t         *v6args = ra_str2var(RA_VAR_IPV6_ROUTING_DAEMON_ARGS);
        ravar_t         *v4stop = ra_str2var(RA_VAR_IPV4_ROUTING_STOP_CMD);
        ravar_t         *v6stop = ra_str2var(RA_VAR_IPV6_ROUTING_STOP_CMD);
        ravar_t         *routing_svcs = ra_str2var(RA_VAR_ROUTING_SVCS);
        boolean_t       mark, marked;
        char            *new_routing_svcs;

        /*
         * Ensure instance is a routing service, and not one of the
         * legacy instances - if it is, the daemon property is already
         * set to the legacy daemon.
         */
        if (ra_get_single_prop_as_string(h, inst, RA_PG_ROUTEADM,
            RA_PROP_DAEMON, B_TRUE, B_FALSE, NULL, &daemon) == -1 ||
            strcmp(RA_INSTANCE_LEGACY_ROUTING_IPV4, inst_fmri) == 0 ||
            strcmp(RA_INSTANCE_LEGACY_ROUTING_IPV6, inst_fmri) == 0)
                return (0);

        /* A legacy daemon may be defined */
        (void) ra_get_single_prop_as_string(h, inst, RA_PG_ROUTEADM,
            RA_PROP_LEGACY_DAEMON, B_TRUE, B_FALSE, NULL, &l_daemon);

        /*
         * If we match daemon/legacy_daemon with ipv4-routing-daemon or
         * ipv6-routing-daemon values, transfer daemon-args value
         * to the matching service.
         */
        if (v4d->var_value != NULL && (strcmp(v4d->var_value, daemon) == 0 ||
            (l_daemon != NULL && strcmp(v4d->var_value, l_daemon) == 0))) {
                (void) printf(gettext("%s: migrating daemon configuration "
                    "for %s to %s\n"), myname, l_daemon != NULL ?
                    l_daemon : daemon, inst_fmri);
                /* Transfer daemon-args value, clear legacy v4 values */
                if (ra_set_prop_from_string(h, inst, RA_PG_ROUTEADM,
                    RA_PROP_DAEMON_ARGS, SCF_TYPE_ASTRING, B_TRUE, 1,
                    (const char **)&(v4args->var_value)) == -1)
                        return (-1);
                ra_resetvars(RA_PROPVAL_PROTO_IPV4);
                if (ra_smf_cb(ra_set_persistent_var_cb,
                    RA_INSTANCE_LEGACY_ROUTING_IPV4, v4d) == -1 ||
                    ra_smf_cb(ra_set_persistent_var_cb,
                    RA_INSTANCE_LEGACY_ROUTING_IPV4, v4args) == -1 ||
                    ra_smf_cb(ra_set_persistent_var_cb,
                    RA_INSTANCE_LEGACY_ROUTING_IPV4, v4stop) == -1)
                        return (-1);
        } else if (v6d->var_value != NULL && (strcmp(v6d->var_value, daemon)
            == 0 ||
            (l_daemon != NULL && strcmp(v6d->var_value, l_daemon) == 0))) {
                (void) printf(gettext("%s: migrating daemon configuration "
                    "for %s to %s\n"), myname, l_daemon != NULL ?
                    l_daemon : daemon, inst_fmri);
                /* Transfer daemon-args value, clear legacy v6 values */
                if (ra_set_prop_from_string(h, inst, RA_PG_ROUTEADM,
                    RA_PROP_DAEMON_ARGS, SCF_TYPE_ASTRING, B_TRUE, 1,
                    (const char **)&(v6args->var_value)) == -1)
                        return (-1);
                ra_resetvars(RA_PROPVAL_PROTO_IPV6);
                if (ra_smf_cb(ra_set_persistent_var_cb,
                    RA_INSTANCE_LEGACY_ROUTING_IPV6, v6d) == -1 ||
                    ra_smf_cb(ra_set_persistent_var_cb,
                    RA_INSTANCE_LEGACY_ROUTING_IPV6, v6args) == -1 ||
                    ra_smf_cb(ra_set_persistent_var_cb,
                    RA_INSTANCE_LEGACY_ROUTING_IPV6, v6stop) == -1)
                        return (-1);
        } else
                return (0);

        /*
         * If service is unmarked at this point, add it to routing-svcs and
         * mark it.
         */
        if (ra_get_boolean_prop(h, inst, RA_PG_ROUTEADM,
            RA_PROP_CURR_ROUTING_SVC, B_FALSE, B_FALSE, &marked) == -1 ||
            marked == B_FALSE) {
                mark = B_TRUE;
                if (ra_smf_cb(ra_mark_routing_svcs_cb, inst_fmri, &mark)
                    == -1 ||
                    ra_smf_cb(ra_get_persistent_var_cb, routing_svcs->var_fmri,
                    routing_svcs) == -1)
                        return (-1);
                if ((new_routing_svcs =
                    malloc(strlen(routing_svcs->var_value) +
                    strlen(inst_fmri) + 2)) == NULL) {
                        (void) fprintf(stderr, gettext(
                            "%s: out of memory"), myname);
                        return (-1);
                }
                if (strlen(routing_svcs->var_value) == 0)
                        (void) snprintf(new_routing_svcs,
                            strlen(inst_fmri) + 1, "%s", inst_fmri);
                else
                        (void) snprintf(new_routing_svcs,
                            strlen(routing_svcs->var_value) +
                            strlen(inst_fmri) + 2, "%s %s",
                            routing_svcs->var_value, inst_fmri);
                free(routing_svcs->var_value);
                routing_svcs->var_value = new_routing_svcs;
                (void) smf_refresh_instance(inst_fmri);
                return (ra_smf_cb(ra_set_persistent_var_cb,
                    routing_svcs->var_fmri, routing_svcs));
        }
        (void) smf_refresh_instance(inst_fmri);
        return (0);
}

/*
 * If we are upgrading,  append operation to <alt_root>/var/svc/profile/upgrade.
 */
static int
ra_upgrade_cmd(char opt, int argc, char **argv)
{
        FILE    *fp;
        int     i;

        if ((fp = fopen(RA_SMF_UPGRADE_FILE, "a+")) == NULL) {
                (void) fprintf(stderr, gettext(
                    "%1$s: failed to open %2$s: %3$s\n"),
                    myname, RA_SMF_UPGRADE_FILE, strerror(errno));
                return (-1);
        }
        (void) fprintf(fp, "/sbin/routeadm -%c ", opt);
        if (argv != NULL) {
                for (i = 0; i < argc; i++)
                        (void) fprintf(fp, "%s ", argv[i]);
        }
        (void) fprintf(fp, "%s\n", RA_SMF_UPGRADE_MSG);
        (void) fclose(fp);
        return (0);
}

/*
 * Set current state to "next boot" state, i.e. if general/enabled
 * value is overlaid by a general_ovr/enabled value, set the current state
 * to the value of the latter.  Doing this applies "next boot" changes to
 * the current setup.  If any IPv6 interfaces are present, also start in.ndpd.
 */
static int
ra_update(void)
{
        int     i;

        if (ra_check_legacy_daemons() == -1)
                return (-1);
        for (i = 0; ra_opts[i].opt_name != NULL; i++) {
                if (ra_smf_cb(ra_set_current_opt_cb, ra_opts[i].opt_fmri,
                    &ra_opts[i]) == -1) {
                        return (-1);
                }
        }
        /*
         * If in.ndpd isn't already running, then we start it here, regardless
         * of global IPv6 routing status (provided there are IPv6 interfaces
         * present).
         */
        if (ra_numv6intfs() > 0)
                return (smf_enable_instance(RA_INSTANCE_NDP, SMF_TEMPORARY));
        return (0);
}

/*
 * Here we catch the special case where ipv4/ipv6 routing was enabled,
 * and the user updates the routing-svcs list.  The problem is that
 * the enabled state is the result of services on the old routing-svcs list
 * being enabled, and we want to support users doing something like this:
 *
 * # routeadm -s routing-svcs=route -e ipv4-routing -u
 *
 * followed by
 *
 * # routeadm -s routing-svcs=rdisc -u
 *
 * To do this, we need to:
 *      - cache the old ipv4-routing/ipv6-routing values.
 *      - persistently disable the old routing-svcs list.
 *      - if ipv4-routing was enabled, mark and persistently enable all the new
 *      v4 routing-svcs
 *      - if ipv6-routing was enabled, mark and persistently enable all the new
 *      v6 routing-svcs.
 * This will result in the next "-u" switching on the new routing-svcs, and
 * switching off the old ones,  as the user would expect.
 */
static int
ra_update_routing_svcs(char *routing_svcs_new)
{
        raopt_t         *v4opt = ra_str2opt(RA_OPT_IPV4_ROUTING);
        raopt_t         *v6opt = ra_str2opt(RA_OPT_IPV6_ROUTING);
        ravar_t         *routing_svcs = ra_str2var(RA_VAR_ROUTING_SVCS);
        char            *routing_svcs_old, *fmri;
        boolean_t       v4_old, v6_old, mark = B_FALSE;

        ra_resetopts();
        if (ra_smf_cb(ra_get_persistent_opt_cb, v4opt->opt_fmri, v4opt) == -1 ||
            ra_smf_cb(ra_get_persistent_opt_cb, v6opt->opt_fmri, v6opt) == -1 ||
            ra_smf_cb(ra_get_persistent_var_cb, routing_svcs->var_fmri,
            routing_svcs) == -1)
                return (-1);
        v4_old = v4opt->opt_enabled;
        v6_old = v6opt->opt_enabled;
        routing_svcs_old = routing_svcs->var_value;
        routing_svcs->var_value = routing_svcs_new;

        if (ra_smf_cb(ra_set_persistent_var_cb, routing_svcs->var_fmri,
            routing_svcs) == -1) {
                free(routing_svcs_old);
                return (-1);
        }

        if (!v4_old && !v6_old) {
                /* We don`t need to do anything, since services were disabled */
                free(routing_svcs_old);
                return (0);
        }
        v4opt->opt_enabled = B_FALSE;
        v6opt->opt_enabled = B_FALSE;

        /* Persistently disable each old v4/v6 "routing-svc" */
        for (fmri = strtok(routing_svcs_old, " \t"); fmri != NULL;
            fmri = strtok(NULL, " \t")) {
                if (ra_smf_cb(ra_mark_routing_svcs_cb, fmri, &mark) == -1) {
                        free(routing_svcs_old);
                        return (-1);
                }
                if (v4_old &&
                    ra_smf_cb(ra_set_persistent_opt_cb, fmri, v4opt) == -1) {
                        free(routing_svcs_old);
                        return (-1);
                }
                if (v6_old &&
                    ra_smf_cb(ra_set_persistent_opt_cb, fmri, v6opt) == -1) {
                        free(routing_svcs_old);
                        return (-1);
                }
        }
        free(routing_svcs_old);
        v4opt->opt_enabled = v4_old;
        v6opt->opt_enabled = v6_old;

        /* Persistently enable each new v4/v6 "routing-svc" */
        mark = B_TRUE;
        for (fmri = strtok(routing_svcs_new, " \t"); fmri != NULL;
            fmri = strtok(NULL, " \t")) {
                if (ra_smf_cb(ra_mark_routing_svcs_cb, fmri, &mark) == -1)
                        return (-1);
                if (v4_old &&
                    ra_smf_cb(ra_set_persistent_opt_cb, fmri, v4opt) == -1)
                        return (-1);
                if (v6_old &&
                    ra_smf_cb(ra_set_persistent_opt_cb, fmri, v6opt) == -1)
                        return (-1);
        }
        return (0);
}

/*
 * Display status,  in parseable form if required.  If param is
 * specified,  only the named option/variable is displayed  (this option is
 * for parseable display only).
 */
static int
ra_report(boolean_t parseable, const char *param)
{
        int             i;
        char            *c_state, *d_state, *p_state, *p_var, *d_var;
        char            *enabled = "enabled";
        char            *disabled = "disabled";
        boolean_t       param_found = B_FALSE;

        if (!parseable) {
                (void) printf(gettext(
                    "              Configuration   Current              "
                    "Current\n"
                    "                     Option   Configuration        "
                    "System State\n"
                    "---------------------------------------------------"
                    "------------\n"));
        }
        for (i = 0; ra_opts[i].opt_name != NULL; i++) {
                if (param != NULL) {
                        if (strcmp(ra_opts[i].opt_name, param) == 0)
                                param_found = B_TRUE;
                        else
                                continue;
                }
                if (ra_smf_cb(ra_get_current_opt_cb,
                    ra_opts[i].opt_fmri, &ra_opts[i]) == -1)
                        return (-1);
                c_state = ra_opts[i].opt_enabled ? enabled : disabled;
                ra_resetopts();
                if (ra_smf_cb(ra_get_persistent_opt_cb,
                    ra_opts[i].opt_fmri, &ra_opts[i]) == -1)
                        return (-1);
                p_state = ra_opts[i].opt_enabled ? enabled : disabled;
                ra_resetopts();
                if (ra_smf_cb(ra_get_default_opt_cb,
                    ra_opts[i].opt_default_fmri, &ra_opts[i]) == -1)
                        return (-1);
                d_state = ra_opts[i].opt_default_enabled ? enabled : disabled;
                ra_resetopts();
                if (parseable) {
                        if (param == NULL)
                                (void) printf("%s ", ra_opts[i].opt_name);
                        (void) printf("persistent=%s default=%s "
                            "current=%s\n", p_state, d_state, c_state);
                } else {
                        (void) printf(gettext("%1$27s   %2$-21s%3$s\n"),
                            ra_intloptname(ra_opts[i].opt_name),
                            p_state, c_state);
                }
        }
        if (!parseable)
                (void) printf("\n");

        ra_resetvars(NULL);

        /* Gather persistent/default variable values */
        for (i = 0; ra_vars[i].var_name != NULL; i++) {
                if (ra_smf_cb(ra_get_persistent_var_cb,
                    ra_vars[i].var_fmri, &ra_vars[i]) == -1 ||
                    ra_smf_cb(ra_get_default_var_cb,
                    ra_vars[i].var_default_fmri, &ra_vars[i]) == -1)
                        return (-1);

        }
        for (i = 0; ra_vars[i].var_name != NULL; i++) {
                if (param != NULL) {
                        if (strcmp(ra_vars[i].var_name, param) == 0)
                                param_found = B_TRUE;
                        else
                                continue;
                }
                p_var = ra_vars[i].var_value == NULL ? "":
                    ra_vars[i].var_value;
                d_var = ra_vars[i].var_default_value == NULL ?
                    "": ra_vars[i].var_default_value;
                if (parseable) {
                        if (param == NULL)
                                (void) printf("%s ", ra_vars[i].var_name);
                        (void) printf("persistent=\"%s\" "
                            "default=\"%s\" \n", p_var, d_var);
                } else {
                        /* If daemon variables are not set, do not display. */
                        if ((IS_IPV4_VAR(ra_vars[i].var_name) &&
                            IPV4_VARS_UNSET) ||
                            (IS_IPV6_VAR(ra_vars[i].var_name) &&
                            IPV6_VARS_UNSET))
                                continue;
                        (void) printf(gettext("%1$27s   \"%2$s\"\n"),
                            ra_intloptname(ra_vars[i].var_name), p_var);
                }
        }

        if (param != NULL && !param_found) {
                (void) fprintf(stderr, gettext(
                    "%s: no such option/variable %s\n"), myname, param);
                return (-1);
        }
        if (parseable)
                return (0);
        (void) printf(gettext("\nRouting daemons:\n"));
        (void) printf("\n                      %s   %s\n", "STATE", "FMRI");
        if (ra_smf_cb(ra_print_state_cb, NULL, NULL) == -1)
                return (-1);
        return (0);
}

/*
 * Call scf_walk_fmri() with appropriate function, fmri, and data.
 * A NULL fmri causes scf_walk_fmri() to run on all instances.  We make
 * use of this many times in applying changes to the routing services.
 */
static int
ra_smf_cb(ra_smf_cb_t cbfunc, const char *fmri, void *data)
{
        scf_handle_t    *h;
        int             exit_status = 0;

        if ((h = scf_handle_create(SCF_VERSION)) == NULL ||
            scf_handle_bind(h) == -1) {
                (void) fprintf(stderr, gettext(
                    "%s: cannot connect to SMF repository\n"), myname);
                return (-1);
        }
        return (scf_walk_fmri(h, fmri == NULL ? 0 : 1,
            fmri == NULL ? NULL : (char **)&fmri, 0,
            cbfunc, data, &exit_status, uu_die));
}

/*
 * Applies persistent configuration settings to current setup.
 */
static int
ra_set_current_opt_cb(void *data, scf_walkinfo_t *wip)
{
        return (ra_get_set_opt_common_cb(data, wip, B_FALSE, B_FALSE));
}

/*
 * Sets persistent value for option,  to be applied on next boot
 * or by "routeadm -u".
 */
static int
ra_set_persistent_opt_cb(void *data, scf_walkinfo_t *wip)
{
        return (ra_get_set_opt_common_cb(data, wip, B_TRUE, B_FALSE));
}

static int
ra_get_current_opt_cb(void *data, scf_walkinfo_t *wip)
{
        return (ra_get_set_opt_common_cb(data, wip, B_FALSE, B_TRUE));
}

static int
ra_get_persistent_opt_cb(void *data, scf_walkinfo_t *wip)
{
        return (ra_get_set_opt_common_cb(data, wip, B_TRUE, B_TRUE));
}

static int
ra_routing_opt_set_cb(void *data, scf_walkinfo_t *wip)
{
        return (ra_routing_opt_set_unset_cb(data, wip, B_TRUE));
}

static int
ra_routing_opt_unset_cb(void *data, scf_walkinfo_t *wip)
{
        return (ra_routing_opt_set_unset_cb(data, wip, B_FALSE));
}

/*
 * Notify network/routing-setup service that administrator has explicitly
 * set/reset ipv4(6)-routing value.  If no explicit setting of this value is
 * done,  ipv4-routing can be enabled in the situation when no default route can
 * be determined.
 */
static int
ra_routing_opt_set_unset_cb(raopt_t *raopt, scf_walkinfo_t *wip, boolean_t set)
{
        scf_instance_t  *inst = wip->inst;
        scf_handle_t    *h = scf_instance_handle(inst);

        return (ra_set_boolean_prop(h, inst, RA_PG_ROUTEADM,
            raopt->opt_flags & RA_SVC_FLAG_IPV4_ROUTING ?
            RA_PROP_IPV4_ROUTING_SET : RA_PROP_IPV6_ROUTING_SET,
            B_FALSE, set));
}

/*
 * Shared function that either sets or determines persistent or current
 * state. Setting persistent state (for next boot) involves setting
 * the general_ovr/enabled value to the current service state, and
 * the general/enabled value to the desired (next-boot) state.
 * Setting current state involves removing the temporary state
 * setting so the persistent state has effect.
 *
 * Persistent state is reported as being enabled if any of the
 * candidate services have a general/enabled value set to true,
 * while current state is reported as being enabled if any of the
 * candidate services has a general_ovr/enabled or general/enabled
 * value set to true.
 */
static int
ra_get_set_opt_common_cb(raopt_t *raopt, scf_walkinfo_t *wip,
    boolean_t persistent, boolean_t get)
{
        const char              *inst_fmri = wip->fmri;
        scf_instance_t          *inst = wip->inst;
        scf_handle_t            *h = scf_instance_handle(inst);
        scf_propertygroup_t     *routeadm_pg;
        boolean_t               persistent_state_enabled;
        boolean_t               temporary_state_enabled;
        boolean_t               current_state_enabled;
        boolean_t               curr_svc = B_TRUE;
        boolean_t               found_proto;
        char                    **protolist = NULL;
        int                     i, ret, numvalues = 0;

        /*
         * Ensure we are dealing with a routeadm-managed service.  If
         * the FMRI used for walking instances is NULL,  it is reasonable
         * that a service not have a routeadm property group as we will
         * check all services in this case.
         */
        if (ra_get_pg(h, inst, RA_PG_ROUTEADM, B_TRUE, raopt->opt_fmri != NULL,
            &routeadm_pg) == -1) {
                        /* Not a routing service, not an error. */
                        if (scf_error() == SCF_ERROR_NOT_FOUND &&
                            raopt->opt_fmri == NULL)
                                return (0);
                        return (-1);
        }
        scf_pg_destroy(routeadm_pg);

        /* Services with no "protocol" property are not routing daemons */
        if (raopt->opt_fmri == NULL && ra_get_prop_as_string(h, inst,
            RA_PG_ROUTEADM, RA_PROP_PROTO, B_TRUE, B_FALSE, NULL, &numvalues,
            &protolist) == -1) {
                if (scf_error() == SCF_ERROR_NOT_FOUND)
                        return (0);
                return (-1);
        }

        /*
         * Skip invalid services based on flag settings.  Flags are used when
         * we run callback functions on all instances to identify
         * the correct instances to operate on.
         */
        if (raopt->opt_flags & RA_SVC_FLAG_IPV4_ROUTING) {
                found_proto = B_FALSE;
                if (protolist != NULL) {
                        /* Check if protolist contains "ipv4" */
                        for (i = 0; i < numvalues; i++) {
                                if (protolist[i] != NULL && strcmp(
                                    protolist[i], RA_PROPVAL_PROTO_IPV4) == 0)
                                        found_proto = B_TRUE;
                        }
                }
                /* If not an ipv4 routing service, skip. */
                if (protolist == NULL || !found_proto) {
                        ra_free_prop_values(numvalues, protolist);
                        return (0);
                }
        }
        if (raopt->opt_flags & RA_SVC_FLAG_IPV6_ROUTING) {
                found_proto = B_FALSE;
                if (protolist != NULL) {
                        /* Check if protolist contains "ipv6" */
                        for (i = 0; i < numvalues; i++) {
                                if (protolist[i] != NULL && strcmp(
                                    protolist[i], RA_PROPVAL_PROTO_IPV6) == 0)
                                        found_proto = B_TRUE;
                        }
                }
                /* If not an ipv6 routing service, skip. */
                if (protolist == NULL || !found_proto) {
                        ra_free_prop_values(numvalues, protolist);
                        return (0);
                }
                /*
                 * If no IPv6 interfaces are configured, do not apply
                 * the "enable" state change to this IPv6 routing service.
                 */
                if (raopt->opt_enabled && ra_numv6intfs() < 1)
                        return (0);
        }
        ra_free_prop_values(numvalues, protolist);

        /* If enabling routing services, select only current routing services */
        if (raopt->opt_fmri == NULL && !get && raopt->opt_enabled) {
                if (ra_get_boolean_prop(h, inst, RA_PG_ROUTEADM,
                    RA_PROP_CURR_ROUTING_SVC, B_FALSE, B_FALSE,
                    &curr_svc) == -1)
                        return (0);
                else if (!curr_svc && persistent) {
                        /*
                         * We apply "current" routing changes to all routing
                         * daemons, whether current or not, so bail if
                         * we are trying to make a persistent update to a
                         * non-"routing-svc".
                         */
                        return (0);
                }
        }
        if (ra_get_boolean_prop(h, inst, SCF_PG_GENERAL, SCF_PROPERTY_ENABLED,
            B_FALSE, B_TRUE, &persistent_state_enabled) == -1)
                return (-1);

        current_state_enabled = persistent_state_enabled;

        if (ra_get_boolean_prop(h, inst, SCF_PG_GENERAL_OVR,
            SCF_PROPERTY_ENABLED, B_FALSE, B_FALSE, &temporary_state_enabled)
            == 0)
                current_state_enabled = temporary_state_enabled;

        if (get) {
                /*
                 * Persistent state is enabled if any services are
                 * persistently enabled, i.e. general/enabled == true).
                 * current state is enabled if any services
                 * services are currently enabled, i.e. if defined,
                 * general_ovr/enabled == true, if not, general/enabled == true.
                 */
                if (persistent)
                        raopt->opt_enabled = raopt->opt_enabled ||
                            persistent_state_enabled;
                else
                        raopt->opt_enabled = raopt->opt_enabled ||
                            current_state_enabled;
        } else {
                if (persistent) {
                        /*
                         * For peristent state changes, from -e/-d,
                         * we set the general_ovr/enabled value to the
                         * current state (to ensure it is preserved),
                         * while setting the general/enabled value to
                         * the desired value.  This has the effect of
                         * the desired value coming into effect on next boot.
                         */
                        ret = current_state_enabled ?
                            smf_enable_instance(inst_fmri, SMF_TEMPORARY) :
                            smf_disable_instance(inst_fmri, SMF_TEMPORARY);
                        if (ret != 0) {
                                (void) fprintf(stderr, gettext(
                                    "%s: unexpected libscf error: %s\n"),
                                    myname, scf_strerror(scf_error()));
                                return (-1);
                        }
                        /*
                         * Refresh here so general_ovr/enabled state overrides
                         * general/enabled state.
                         */
                        (void) smf_refresh_instance(inst_fmri);
                        /*
                         * Now we can safely set the general/enabled value
                         * to the value we require on next boot (or
                         * "routeadm -u").
                         */
                        ret = ra_set_boolean_prop(h, inst, SCF_PG_GENERAL,
                            SCF_PROPERTY_ENABLED, B_FALSE, raopt->opt_enabled);
                        if (ret != 0)
                                return (-1);
                        /*
                         * Refresh here so general/enabled value is set.
                         */
                        (void) smf_refresh_instance(inst_fmri);
                        if (raopt->opt_fmri != NULL)
                                return (0);
                        (void) smf_refresh_instance(RA_INSTANCE_ROUTING_SETUP);
                } else {
                        /*
                         * Refresh here to get latest property values prior
                         * to starting daemon.
                         */
                        (void) smf_refresh_instance(inst_fmri);
                        /*
                         * For current changes (result of -u), we
                         * enable/disable depending on persistent value
                         * stored in general/enabled.  Here we disable
                         * old routing-svcs (identified by a current-routing-svc
                         * value of false) also.
                         */
                        ret = persistent_state_enabled && curr_svc ?
                            smf_enable_instance(inst_fmri, 0) :
                            smf_disable_instance(inst_fmri, 0);
                        if (ret != 0) {
                                (void) fprintf(stderr, gettext(
                                    "%s: unexpected libscf error: %s\n"),
                                    myname, scf_strerror(scf_error()));
                                return (-1);
                        }
                        if (current_state_enabled && persistent_state_enabled) {
                                /*
                                 * Instance was already enabled, so we restart
                                 * to get latest property values.  This covers
                                 * the case where users update properties
                                 * via routeadm -m, and issue an update.  The
                                 * daemon should be running with the latest
                                 * property values.
                                 */
                                (void) smf_restart_instance(inst_fmri);
                        }
                }
        }
        return (0);
}

static int
ra_set_default_opt_cb(void *data, scf_walkinfo_t *wip)
{
        scf_instance_t          *inst = wip->inst;
        scf_handle_t            *h = scf_instance_handle(inst);
        raopt_t                 *raopt = data;

        return (ra_set_boolean_prop(h, inst, RA_PG_ROUTEADM,
            raopt->opt_default_prop, B_FALSE, raopt->opt_default_enabled));
}

static int
ra_get_default_opt_cb(void *data, scf_walkinfo_t *wip)
{
        scf_instance_t          *inst = wip->inst;
        scf_handle_t            *h = scf_instance_handle(inst);
        raopt_t                 *raopt = data;

        return (ra_get_boolean_prop(h, inst, RA_PG_ROUTEADM,
            raopt->opt_default_prop, B_TRUE, B_TRUE,
            &(raopt->opt_default_enabled)));
}

/*
 * Callbacks to set/retrieve persistent/default routing variable values.
 * The set functions use the value stored in the var_value/var_default_value
 * field of the associated ra_var_t, while the retrieval functions store
 * the value retrieved in that field.
 */
static int
ra_get_persistent_var_cb(void *data, scf_walkinfo_t *wip)
{
        scf_instance_t          *inst = wip->inst;
        scf_handle_t            *h = scf_instance_handle(inst);
        ravar_t                 *ravar = data;

        return (ra_get_single_prop_as_string(h, inst, RA_PG_ROUTEADM,
            ravar->var_prop, B_TRUE, B_TRUE, NULL, &ravar->var_value));
}

static int
ra_set_persistent_var_cb(void *data, scf_walkinfo_t *wip)
{
        scf_instance_t          *inst = wip->inst;
        scf_handle_t            *h = scf_instance_handle(inst);
        ravar_t                 *ravar = data;

        return (ra_set_prop_from_string(h, inst, RA_PG_ROUTEADM,
            ravar->var_prop, SCF_TYPE_INVALID, B_FALSE, 1,
            (const char **)&ravar->var_value));
}

static int
ra_get_default_var_cb(void *data, scf_walkinfo_t *wip)
{
        scf_instance_t          *inst = wip->inst;
        scf_handle_t            *h = scf_instance_handle(inst);
        ravar_t                 *ravar = data;

        return (ra_get_single_prop_as_string(h, inst, RA_PG_ROUTEADM,
            ravar->var_default_prop, B_TRUE, B_TRUE, NULL,
            &ravar->var_default_value));
}

/*
 * Depending on the value of the boolean_t * passed in,  this callback
 * either marks the relevant service(s) as current-routing-svcs (or unmarking)
 * by setting that property to true or false.  When routing services
 * are to be enabled,  the a current-routing-svc value of true flags the
 * service as one to be enabled.
 */
static int
ra_mark_routing_svcs_cb(void *data, scf_walkinfo_t *wip)
{
        scf_instance_t          *inst = wip->inst;
        scf_handle_t            *h = scf_instance_handle(inst);
        boolean_t               *mark = data;
        boolean_t               marked;
        int                     numvalues = 0;
        char                    **protolist = NULL;

        /* Check we are dealing with a routing daemon service */
        if (ra_get_prop_as_string(h, inst, RA_PG_ROUTEADM, RA_PROP_PROTO,
            B_TRUE, B_FALSE, NULL, &numvalues, &protolist) == -1)
                return (0);
        ra_free_prop_values(numvalues, protolist);
        if (*mark)
                return (ra_set_boolean_prop(h, inst, RA_PG_ROUTEADM,
                    RA_PROP_CURR_ROUTING_SVC, B_TRUE, B_TRUE));
        /* Unmark service. */
        if (ra_get_boolean_prop(h, inst, RA_PG_ROUTEADM,
            RA_PROP_CURR_ROUTING_SVC, B_TRUE, B_FALSE, &marked) == 0 && marked)
                return (ra_set_boolean_prop(h, inst, RA_PG_ROUTEADM,
                    RA_PROP_CURR_ROUTING_SVC, B_TRUE, B_FALSE));
        return (0);
}

/*
 * List property values for all properties in the "routing" property
 * group of the routing service instance.
 */

/* ARGSUSED0 */
static int
ra_list_props_cb(void *data, scf_walkinfo_t *wip)
{
        const char              *inst_fmri = wip->fmri;
        scf_instance_t          *inst = wip->inst;
        scf_handle_t            *h = scf_instance_handle(inst);
        scf_iter_t              *propiter, *valiter;
        scf_propertygroup_t     *pg;
        scf_property_t          *prop;
        scf_value_t             *val;
        char                    **protolist = NULL, *pnamebuf, *valbuf;
        ssize_t                 pnamelen, vallen;
        int                     numvalues = 0;
        int                     propiterret, valiterret, retval = 0;

        /* Services with no "protocol" property are not routing daemons */
        if (ra_get_prop_as_string(h, inst, RA_PG_ROUTEADM, RA_PROP_PROTO,
            B_TRUE, B_FALSE, NULL, &numvalues, &protolist) == -1) {
                if (scf_error() == SCF_ERROR_NOT_FOUND)
                        (void) fprintf(stderr,
                            gettext("%s: %s is not a routing daemon service\n"),
                            myname, inst_fmri);
                else
                        (void) fprintf(stderr,
                            gettext("%s: unexpected libscf error: %s\n"),
                            myname, scf_strerror(scf_error()));
                ra_free_prop_values(numvalues, protolist);
                return (-1);
        }
        ra_free_prop_values(numvalues, protolist);

        if (ra_get_pg(h, inst, RA_PG_ROUTING, B_TRUE, B_FALSE, &pg) == -1) {
                if (scf_error() == SCF_ERROR_NOT_FOUND) {
                        (void) printf("%s: no %s property group for %s\n",
                            myname, RA_PG_ROUTING, inst_fmri);
                        return (0);
                }
                (void) fprintf(stderr,
                    gettext("%s: unexpected libscf error: %s\n"),
                    myname, scf_strerror(scf_error()));
                return (-1);
        }

        (void) printf("%s:\n", inst_fmri);

        /* Create an iterator to walk through all properties */
        if ((propiter = scf_iter_create(h)) == NULL ||
            (prop = scf_property_create(h)) == NULL ||
            scf_iter_pg_properties(propiter, pg) != 0) {
                (void) fprintf(stderr, gettext
                    ("%s: could not iterate through properties for %s: %s\n"),
                    myname, inst_fmri, scf_strerror(scf_error()));
        }
        while ((propiterret = scf_iter_next_property(propiter, prop)) == 1) {
                if ((pnamelen = scf_property_get_name(prop, NULL, 0) + 1)
                    == 0) {
                        (void) fprintf(stderr, gettext("%s: could not retrieve "
                            "property name for instance %s: %s\n"), myname,
                            inst_fmri, scf_strerror(scf_error()));
                        retval = -1;
                        break;
                }
                if ((pnamebuf = malloc(pnamelen)) == NULL) {
                        (void) fprintf(stderr,
                            gettext("%s: out of memory\n"), myname);
                        retval = -1;
                        break;
                }
                (void) scf_property_get_name(prop, pnamebuf,
                    pnamelen);
                (void) printf("\t%s = ", pnamebuf);
                if ((valiter = scf_iter_create(h)) == NULL ||
                    (val = scf_value_create(h)) == NULL ||
                    scf_iter_property_values(valiter, prop)
                    != 0) {
                        (void) fprintf(stderr, gettext
                            ("%s: could not iterate through "
                            "properties for %s: %s\n"), myname, inst_fmri,
                            scf_strerror(scf_error()));
                        scf_value_destroy(val);
                        scf_iter_destroy(valiter);
                        free(pnamebuf);
                        retval = -1;
                        break;
                }
                while ((valiterret = scf_iter_next_value(valiter, val)) == 1) {
                        if ((vallen = scf_value_get_as_string
                            (val, NULL, 0) + 1) == 0) {
                                (void) fprintf(stderr, gettext
                                    ("%s: could not retrieve "
                                    "property value for instance %s, "
                                    "property %s: %s\n"), myname, inst_fmri,
                                    pnamebuf, scf_strerror(scf_error()));
                                retval = -1;
                        } else if ((valbuf = malloc(vallen)) == NULL) {
                                (void) fprintf(stderr,
                                    gettext("%s: out of memory\n"), myname);
                                retval = -1;
                        }
                        if (retval == -1) {
                                scf_iter_destroy(valiter);
                                scf_value_destroy(val);
                                free(pnamebuf);
                                goto out;
                        }
                        (void) scf_value_get_as_string(val, valbuf, vallen);
                        (void) printf("%s ", valbuf);
                        free(valbuf);
                }
                (void) printf("\n");
                scf_iter_destroy(valiter);
                scf_value_destroy(val);
                free(pnamebuf);
                if (valiterret == -1) {
                        (void) fprintf(stderr,
                            gettext("%s: could not iterate through"
                            "properties for %s: %s\n"), myname, inst_fmri,
                            scf_strerror(scf_error()));
                        retval = -1;
                        break;
                }
        }
out:
        scf_iter_destroy(propiter);
        scf_property_destroy(prop);
        scf_pg_destroy(pg);
        if (propiterret == -1)
                (void) fprintf(stderr, gettext
                    ("%s: could not iterate through properties for %s: %s\n"),
                    myname, inst_fmri, scf_strerror(scf_error()));
        return (retval);
}

/*
 * Modify property with name stored in passed-in ra_prop_t to have
 * the assocatied values.  Only works for existing properties in
 * the "routing" property group for routing daemon services,  so all
 * routing daemons should place configurable options in that group.
 */
static int
ra_modify_props_cb(void *data, scf_walkinfo_t *wip)
{
        const char              *inst_fmri = wip->fmri;
        scf_instance_t          *inst = wip->inst;
        scf_handle_t            *h = scf_instance_handle(inst);
        ra_prop_t               *raprop = data;
        int                     numvalues = 0;
        char                    **protolist = NULL;

        /* Services with no "protocol" property are not routing daemons */
        if (ra_get_prop_as_string(h, inst, RA_PG_ROUTEADM, RA_PROP_PROTO,
            B_TRUE, B_FALSE, NULL, &numvalues, &protolist) == -1) {
                if (scf_error() == SCF_ERROR_NOT_FOUND)
                        (void) fprintf(stderr,
                            gettext("%s: %s is not a routing daemon service\n"),
                            myname, inst_fmri);
                else
                        (void) fprintf(stderr,
                            gettext("%s: unexpected libscf error: %s\n"),
                            myname, scf_strerror(scf_error()));
                ra_free_prop_values(numvalues, protolist);
                return (-1);
        }
        ra_free_prop_values(numvalues, protolist);

        if (ra_set_prop_from_string(h, inst, RA_PG_ROUTING, raprop->prop_name,
            SCF_TYPE_INVALID, B_FALSE, raprop->prop_numvalues,
            (const char **)raprop->prop_values) == -1)
                return (-1);

        (void) smf_refresh_instance(inst_fmri);
        return (0);
}

/*
 * Display FMRI, state for each routing daemon service.
 */

/* ARGSUSED0 */
static int
ra_print_state_cb(void *data, scf_walkinfo_t *wip)
{
        const char              *inst_fmri = wip->fmri;
        scf_instance_t          *inst = wip->inst;
        scf_handle_t            *h = scf_instance_handle(inst);
        char                    *inst_state, **protolist = NULL;
        int                     numvalues = 0;

        /* Ensure service is a routing daemon */
        if (ra_get_prop_as_string(h, inst, RA_PG_ROUTEADM, RA_PROP_PROTO,
            B_TRUE, B_FALSE, NULL, &numvalues, &protolist) == -1)
                return (0);
        ra_free_prop_values(numvalues, protolist);

        if ((inst_state = smf_get_state(inst_fmri)) == NULL) {
                (void) fprintf(stderr,
                    gettext("%s: could not retrieve state for %s: %s\n"),
                    myname, inst_fmri, scf_strerror(scf_error()));
                return (-1);
        }
        (void) printf("%27s   %2s\n", inst_state, inst_fmri);
        free(inst_state);

        return (0);
}

static int
ra_get_pg(scf_handle_t *h, scf_instance_t *inst, const char *pgname,
    boolean_t composed, boolean_t required, scf_propertygroup_t **pg)
{
        /* Retrieve (possibly composed) property group for instance */
        if ((*pg = scf_pg_create(h)) == NULL || (composed &&
            scf_instance_get_pg_composed(inst, NULL, pgname, *pg) != 0) ||
            (!composed && scf_instance_get_pg(inst, pgname, *pg) != 0)) {
                if (scf_error() == SCF_ERROR_NOT_FOUND) {
                        if (required)
                                (void) fprintf(stderr, gettext(
                                    "%s: no such property group %s\n"),
                                    myname, pgname);
                        return (-1);
                }
                if (required)
                        (void) fprintf(stderr, gettext(
                            "%s: unexpected libscf error: %s\n"), myname,
                            scf_strerror(scf_error()));
                return (-1);
        }
        return (0);
}

static int
ra_get_boolean_prop(scf_handle_t *h, scf_instance_t *inst,
    const char *pgname, const char *propname, boolean_t composed,
    boolean_t required, boolean_t *val)
{
        char    *valstr;

        if (ra_get_single_prop_as_string(h, inst, pgname, propname,
            composed, required, NULL, &valstr) != 0)
                return (-1);
        *val = strcmp(valstr, RA_PROPVAL_BOOLEAN_TRUE) == 0;
        free(valstr);
        return (0);
}

static int
ra_get_single_prop_as_string(scf_handle_t *h, scf_instance_t *inst,
    const char *pgname, const char *propname, boolean_t composed,
    boolean_t required, scf_type_t *type, char **value)
{
        char    **values;
        int     numvalues = 1;

        if (ra_get_prop_as_string(h, inst, pgname, propname, composed, required,
            type, &numvalues, &values) == -1)
                return (-1);
        *value = values[0];
        free(values);
        return (0);
}

/*
 * Retrieve property named in propname,  possibly using the composed
 * property group view (union of instance and service-level properties,
 * where instance-level properties override service-level values).
 */
static int
ra_get_prop_as_string(scf_handle_t *h, scf_instance_t *inst,
    const char *pgname, const char *propname, boolean_t composed,
    boolean_t required, scf_type_t *type, int *numvalues, char ***values)
{
        scf_propertygroup_t     *pg = NULL;
        scf_property_t          *prop = NULL;
        scf_iter_t              *valiter = NULL;
        scf_value_t             *val = NULL;
        ssize_t                 vallen = 0;
        int                     valiterret, i, numvalues_retrieved, ret = 0;

        if (ra_get_pg(h, inst, pgname, composed, required, &pg) == -1)
                return (-1);

        *values = NULL;
        /*
         * Retrieve values. All values routeadm needs to retrieve
         * (bar those gathered by routeadm -l), are known to be single-valued.
         */
        if ((prop = scf_property_create(h)) == NULL)
                goto error;
        if (scf_pg_get_property(pg, propname, prop) != 0) {
                *numvalues = 0;
                if (scf_error() == SCF_ERROR_NOT_FOUND) {
                        if (required)
                                (void) fprintf(stderr, gettext(
                                    "%s: property %s/%s not found\n"),
                                    myname, pgname, propname);
                        ret = -1;
                        goto out;
                }
                goto error;
        }
        if ((val = scf_value_create(h)) == NULL &&
            scf_property_get_value(prop, val) != 0 ||
            (valiter = scf_iter_create(h)) == NULL ||
            scf_iter_property_values(valiter, prop) != 0)
                goto error;
        /* retrieve each value */
        for (numvalues_retrieved = 0;
            (valiterret = scf_iter_next_value(valiter, val)) == 1;
            numvalues_retrieved++) {
                if ((vallen = scf_value_get_as_string
                    (val, NULL, 0) + 1) == 0)
                        goto error;
                if ((*values = realloc(*values,
                    sizeof (*values) + sizeof (char *))) == NULL ||
                    ((*values)[numvalues_retrieved] = malloc(vallen)) == NULL) {
                        (void) fprintf(stderr, gettext(
                            "%s: out of memory\n"), myname);
                        ret = -1;
                        goto out;
                }
                (void) scf_value_get_as_string(val,
                    (*values)[numvalues_retrieved], vallen);
        }
        if (valiterret == -1)
                goto error;
        /*
         * if *numvalues != 0, it holds expected number of values.  If a
         * different number are found, it is an error.
         */
        if (*numvalues != 0 && *numvalues != numvalues_retrieved) {
                (void) fprintf(stderr, gettext(
                    "%s: got %d values for property %s/%s, expected %d\n"),
                    myname, numvalues_retrieved, pgname, propname, *numvalues);
                ret = -1;
                goto out;
        }
        *numvalues = numvalues_retrieved;

        /* Retrieve property type if required. */
        if (type != NULL)
                (void) scf_property_type(prop, type);

        goto out;
error:
        if (scf_error() == SCF_ERROR_NOT_FOUND) {
                (void) fprintf(stderr, gettext(
                    "%s: property %s not found"), myname, propname);
        } else {
                (void) fprintf(stderr, gettext(
                    "%s: unexpected libscf error: %s, "), myname);
        }
        for (i = 0; i < numvalues_retrieved; i++)
                free((*values)[i]);
        if (*values != NULL)
                free(*values);

        ret = -1;
out:
        if (val != NULL)
                scf_value_destroy(val);
        if (valiter != NULL)
                scf_iter_destroy(valiter);
        if (prop != NULL)
                scf_property_destroy(prop);
        if (pg != NULL)
                scf_pg_destroy(pg);
        return (ret);
}

static void
ra_free_prop_values(int numvalues, char **values)
{
        int     i;
        if (values != NULL) {
                for (i = 0; i < numvalues; i++)
                        free(values[i]);
                free(values);
        }
}

static int
ra_set_boolean_prop(scf_handle_t *h, scf_instance_t *inst, const char *pgname,
    const char *prop, boolean_t create, boolean_t propval)
{
        const char      *val = propval ? RA_PROPVAL_BOOLEAN_TRUE :
            RA_PROPVAL_BOOLEAN_FALSE;

        return (ra_set_prop_from_string(h, inst, pgname, prop, SCF_TYPE_BOOLEAN,
            create, 1, &val));
}

/*
 * Set the property named in propname to the values passed in in the propvals
 * array.  Only create a new property if "create" is true.
 */
static int
ra_set_prop_from_string(scf_handle_t *h, scf_instance_t *inst,
    const char *pgname, const char *propname, scf_type_t proptype,
    boolean_t create, int numpropvals, const char **propvals)
{
        scf_propertygroup_t     *instpg = NULL, *cpg = NULL;
        scf_type_t              oldproptype, newproptype = proptype;
        scf_property_t          *prop = NULL;
        scf_value_t             **values = NULL;
        scf_transaction_t       *tx = NULL;
        scf_transaction_entry_t *ent = NULL;
        boolean_t               new = B_FALSE;
        int                     i, retval, numvalues = 0, ret = 0;
        char                    *pgtype = NULL, **ovalues;
        ssize_t                 typelen;

        /* Firstly, does property exist? If not, and create is false, bail */
        if (ra_get_prop_as_string(h, inst, pgname, propname, B_TRUE,
            B_FALSE, &oldproptype, &numvalues, &ovalues) == -1) {
                if (scf_error() != SCF_ERROR_NOT_FOUND)
                        goto error;
                if (!create) {
                        (void) fprintf(stderr, gettext(
                            "%s: no such property %s/%s\n"), myname, pgname,
                            propname);
                        return (-1);
                }
        } else
                ra_free_prop_values(numvalues, ovalues);

        /* Use old property type */
        if (proptype == SCF_TYPE_INVALID)
                newproptype = oldproptype;

        /*
         * Does property group exist at instance level?  If not, we need to
         * create it,  since the composed view of the property group did
         * contain the property.  We never modify properties at the service
         * level,  as it`s possible that multiple instances will inherit those
         * settings.
         */
        if (ra_get_pg(h, inst, pgname, B_FALSE, B_FALSE, &instpg) == -1) {
                if (scf_error() != SCF_ERROR_NOT_FOUND)
                        goto error;
                /* Ensure pg exists at service level, get composed pg */
                if (ra_get_pg(h, inst, pgname, B_TRUE, B_FALSE, &cpg) == -1)
                        goto error;

                /* Create instance-level property group */
                if ((typelen = scf_pg_get_type(cpg, NULL, 0) + 1) == 0)
                        goto error;
                if ((pgtype = malloc(typelen)) == NULL) {
                        (void) fprintf(stderr, gettext(
                            "%s: out of memory\n"), myname);
                        goto error;
                }
                (void) scf_pg_get_type(cpg, pgtype, typelen);
                if ((instpg = scf_pg_create(h)) == NULL ||
                    scf_instance_add_pg(inst, pgname, pgtype, 0, instpg)
                    == -1) {
                        (void) fprintf(stderr, gettext(
                            "%s: could not create property group %s\n"),
                            myname, pgname);
                        goto error;
                }
        }
        if ((prop = scf_property_create(h)) == NULL)
                goto error;
        if ((values = calloc(numpropvals, sizeof (scf_value_t *))) == NULL) {
                (void) fprintf(stderr, gettext("%s: out of memory"), myname);
                goto error;
        }
        if (scf_pg_get_property(instpg, propname, prop) != 0) {
                /* New property? */
                if (scf_error() == SCF_ERROR_NOT_FOUND)
                        new = B_TRUE;
                else
                        goto error;
        }
        if ((tx = scf_transaction_create(h)) == NULL ||
            (ent = scf_entry_create(h)) == NULL)
                goto error;
retry:
        if (scf_transaction_start(tx, instpg) == -1)
                goto error;
        if (new) {
                if (scf_transaction_property_new(tx, ent, propname,
                    newproptype) == -1)
                        goto error;
        } else if (scf_transaction_property_change(tx, ent, propname,
            newproptype) == -1)
                goto error;
        for (i = 0; i < numpropvals; i++) {
                if ((values[i] = scf_value_create(h)) == NULL ||
                    scf_value_set_from_string(values[i], newproptype,
                    propvals[i] == NULL ? "": propvals[i]) == -1 ||
                    scf_entry_add_value(ent, values[i]) != 0)
                        goto error;
        }
        retval = scf_transaction_commit(tx);
        if (retval == 0) {
                scf_transaction_reset(tx);
                if (scf_pg_update(instpg) == -1)
                        goto error;
                goto retry;
        }
        if (retval == -1)
                goto error;
        goto out;
error:
        switch (scf_error()) {
        case SCF_ERROR_INVALID_ARGUMENT:
                (void) fprintf(stderr, gettext(
                    "%s: invalid value for property %s/%s\n"), myname,
                    pgname, propname);
                break;
        case SCF_ERROR_NOT_FOUND:
                (void) fprintf(stderr, gettext(
                    "%s: no such property %s/%s\n"), myname,
                    pgname, propname);
                break;
        default:
                (void) fprintf(stderr, gettext(
                    "%s: unexpected libscf error: %s\n"), myname,
                    scf_strerror(scf_error()));
                break;
        }
        ret = -1;
out:
        if (tx != NULL)
                scf_transaction_destroy(tx);
        if (ent != NULL)
                scf_entry_destroy(ent);
        if (values != NULL) {
                for (i = 0; i < numpropvals; i++) {
                        if (values[i] != NULL)
                                scf_value_destroy(values[i]);
                }
                free(values);
        }
        if (prop != NULL)
                scf_property_destroy(prop);
        if (cpg != NULL)
                scf_pg_destroy(cpg);
        if (instpg != NULL)
                scf_pg_destroy(instpg);
        if (pgtype != NULL)
                free(pgtype);
        return (ret);
}

/*
 * This function gathers configuration from the legacy /etc/inet/routing.conf,
 * if any, and sets the appropriate variable values accordingly.  Once
 * these are set,  the legacy daemons are checked to see if they have
 * SMF counterparts (ra_check_legacy_daemons()).  If they do, the
 * configuration is upgraded.  Finally,  the legacy option settings are
 * applied,  enabling/disabling the routing/forwarding services as
 * appropriate.
 */
static int
ra_upgrade_from_legacy_conf(void)
{
        scf_handle_t    *h = NULL;
        scf_instance_t  *inst = NULL;
        int             ret = 0, i, r;
        boolean_t       old_conf_read;
        ravar_t         *routing_svcs = ra_str2var(RA_VAR_ROUTING_SVCS);

        /*
         * First, determine if we have already upgraded - if "routing-conf-read"
         * is true, we bail.  The use of a boolean property indicating if
         * routing.conf has been read and applied might seem a lot more
         * work than simply copying routing.conf aside,  but leaving the
         * file in place allows users to downgrade and have their old
         * routing configuration still in place.
         */
        if ((h = scf_handle_create(SCF_VERSION)) == NULL ||
            scf_handle_bind(h) == -1) {
                (void) fprintf(stderr, gettext(
                    "%s: cannot connect to SMF repository\n"), myname);
                ret = -1;
                goto out;
        }
        if ((inst = scf_instance_create(h)) == NULL ||
            scf_handle_decode_fmri(h, RA_INSTANCE_ROUTING_SETUP,
            NULL, NULL, inst, NULL, NULL, SCF_DECODE_FMRI_EXACT) == -1) {
                (void) fprintf(stderr, gettext(
                    "%s: unexpected libscf error: %s\n"), myname,
                    scf_strerror(scf_error()));
                ret = -1;
                goto out;
        }
        if (ra_get_boolean_prop(h, inst, RA_PG_ROUTEADM,
            RA_PROP_ROUTING_CONF_READ, B_TRUE, B_TRUE, &old_conf_read) == -1) {
                ret = -1;
                goto out;
        }

        if (old_conf_read)
                goto out;

        /*
         * Now set "routing-conf-read" to true so we don`t reimport legacy
         * configuration again.
         */
        if (ra_set_boolean_prop(h, inst, RA_PG_ROUTEADM,
            RA_PROP_ROUTING_CONF_READ, B_FALSE, B_TRUE) == -1)
                return (-1);
        (void) smf_refresh_instance(RA_INSTANCE_ROUTING_SETUP);

        ra_resetvars(NULL);

        /* First, gather values from routing.conf */
        if ((r = ra_parseconf()) == -1) {
                ret = -1;
                goto out;
        }
        /* No routing.conf file found */
        if (r == 0)
                goto out;
        /*
         * Now, set the options/variables gathered.  We set variables first,
         * as we cannot enable routing before we determine the daemons
         * to enable.
         */

        for (i = 0; ra_vars[i].var_name != NULL; i++) {
                /* Skip routing-svcs var, not featured in legacy config */
                if (strcmp(ra_vars[i].var_name, RA_VAR_ROUTING_SVCS) == 0)
                        continue;
                if (ra_smf_cb(ra_set_persistent_var_cb, ra_vars[i].var_fmri,
                    &(ra_vars[i])) == -1) {
                        ret = -1;
                        goto out;
                }
        }
        /* Clear routing-svcs value */
        if (ra_smf_cb(ra_set_persistent_var_cb, routing_svcs->var_fmri,
            routing_svcs) == -1) {
                ret = -1;
                goto out;
        }

        if (ra_check_legacy_daemons() == -1) {
                ret = -1;
                goto out;
        }

        for (i = 0; ra_opts[i].opt_name != NULL; i++) {
                if (ra_smf_cb(ra_set_persistent_opt_cb, ra_opts[i].opt_fmri,
                    &(ra_opts[i])) == -1 ||
                    ra_smf_cb(ra_set_default_opt_cb,
                    ra_opts[i].opt_default_fmri, &(ra_opts[i])) == -1) {
                        ret = -1;
                        break;
                }
        }
out:
        if (inst != NULL)
                scf_instance_destroy(inst);
        if (h != NULL)
                scf_handle_destroy(h);

        return (ret);
}

/*
 *
 * Return the number of IPv6 addresses configured.  This answers the
 * generic question, "is IPv6 configured?".  We only start in.ndpd if IPv6
 * is configured, and we also only enable IPv6 routing daemons if IPv6 is
 * enabled.
 */
static int
ra_numv6intfs(void)
{
        static int      num = -1;
        int             ipsock;
        struct lifnum   lifn;

        if (num != -1)
                return (num);

        if ((ipsock = socket(PF_INET6, SOCK_DGRAM, 0)) == -1) {
                (void) fprintf(stderr,
                    gettext("%1$s: unable to open %2$s: %3$s\n"),
                    myname, IP_DEV_NAME, strerror(errno));
                return (0);
        }
        lifn.lifn_family = AF_INET6;
        lifn.lifn_flags = 0;

        if (ioctl(ipsock, SIOCGLIFNUM, &lifn) == -1) {
                (void) close(ipsock);
                return (0);
        }
        (void) close(ipsock);

        return (num = lifn.lifn_count);
}

/*
 * Parse the configuration file and fill the ra_opts array with opt_value
 * and opt_default_value values, and the ra_vars array with var_value and
 * var_default_value values.  Then copy aside routing.conf so it will not
 * be read by future invokations of routeadm.
 */
static int
ra_parseconf(void)
{
        FILE    *fp;
        uint_t  lineno;
        char    line[RA_MAX_CONF_LINE];
        char    *cp, *confstr;
        raopt_t *raopt;
        ravar_t *ravar;

        if ((fp = fopen(RA_CONF_FILE, "r")) == NULL) {
                /*
                 * There's no config file, so we simply return as there
                 * is no work to do.
                 */
                return (0);
        }

        for (lineno = 1; fgets(line, sizeof (line), fp) != NULL; lineno++) {
                if (line[strlen(line) - 1] == '\n')
                        line[strlen(line) - 1] = '\0';

                cp = line;

                /* Skip leading whitespace */
                while (isspace(*cp))
                        cp++;

                /* Skip comment lines and empty lines */
                if (*cp == '#' || *cp == '\0')
                        continue;

                /*
                 * Anything else must be of the form:
                 * <option> <value> <default_value>
                 */
                if ((confstr = strtok(cp, " ")) == NULL) {
                        (void) fprintf(stderr,
                            gettext("%1$s: %2$s: invalid entry on line %3$d\n"),
                            myname, RA_CONF_FILE, lineno);
                        continue;
                }

                if ((raopt = ra_str2opt(confstr)) != NULL) {
                        if (ra_parseopt(confstr, lineno, raopt) != 0) {
                                (void) fclose(fp);
                                return (-1);
                        }
                } else if ((ravar = ra_str2var(confstr)) != NULL) {
                        if (ra_parsevar(confstr, ravar) != 0) {
                                (void) fclose(fp);
                                return (-1);
                        }
                } else {
                        (void) fprintf(stderr,
                            gettext("%1$s: %2$s: invalid option name on "
                                "line %3$d\n"),
                            myname, RA_CONF_FILE, lineno);
                        continue;
                }
        }

        (void) fclose(fp);

        return (1);
}

static int
ra_parseopt(char *confstr, int lineno, raopt_t *raopt)
{
        oval_t oval, d_oval;

        if ((confstr = strtok(NULL, " ")) == NULL) {
                (void) fprintf(stderr,
                    gettext("%1$s: %2$s: missing value on line %3$d\n"),
                    myname, RA_CONF_FILE, lineno);
                return (0);
        }
        if ((oval = ra_str2oval(confstr)) == OPT_INVALID) {
                (void) fprintf(stderr,
                    gettext("%1$s: %2$s: invalid option "
                        "value on line %3$d\n"),
                    myname, RA_CONF_FILE, lineno);
                return (0);
        }
        if (oval != OPT_DEFAULT)
                raopt->opt_enabled = oval == OPT_ENABLED;

        if ((confstr = strtok(NULL, " ")) == NULL) {
                (void) fprintf(stderr,
                    gettext("%1$s: %2$s: missing revert "
                        "value on line %3$d\n"),
                    myname, RA_CONF_FILE, lineno);
                return (0);
        }
        if ((d_oval = ra_str2oval(confstr)) == OPT_INVALID) {
                (void) fprintf(stderr,
                    gettext("%1$s: %2$s: invalid revert "
                        "value on line %3$d\n"),
                    myname, RA_CONF_FILE, lineno, confstr);
                return (0);
        }
        raopt->opt_default_enabled = d_oval == OPT_ENABLED;
        if (oval == OPT_DEFAULT)
                raopt->opt_enabled = d_oval == OPT_ENABLED;

        /*
         * Set ipv4(6)-routing-set property as appropriate on upgrading
         * routing.conf.  If option was default, set this value to false,
         * as this indicates the administrator has not explicitly enabled
         * or disabled ipv4(6)-routing.  The ipv4-routing-set value is used
         * in the routing-setup service, and if it is false, ipv4-routing
         * is enabled in the case where no default route can be determined.
         */
        if (raopt->opt_flags & (RA_SVC_FLAG_IPV4_ROUTING |
            RA_SVC_FLAG_IPV6_ROUTING)) {
                if (ra_smf_cb(oval == OPT_DEFAULT ? ra_routing_opt_unset_cb :
                    ra_routing_opt_set_cb, raopt->opt_default_fmri, raopt)
                    == -1)
                        return (-1);
        }
        return (0);
}

static int
ra_parsevar(char *confstr, ravar_t *ravar)
{
        confstr = strtok(NULL, "=");
        if (confstr == NULL) {
                /*
                 * This isn't an error condition, it simply means that the
                 * variable has no value.
                 */
                ravar->var_value = NULL;
                return (0);
        }

        if ((ravar->var_value = strdup(confstr)) == NULL) {
                (void) fprintf(stderr, gettext("%s: "
                    "unable to allocate memory\n"), myname);
                return (-1);
        }
        return (0);
}

/* Convert a string to an option value. */
static oval_t
ra_str2oval(const char *valstr)
{
        if (strcmp(valstr, "enabled") == 0)
                return (OPT_ENABLED);
        else if (strcmp(valstr, "disabled") == 0)
                return (OPT_DISABLED);
        else if (strcmp(valstr, "default") == 0)
                return (OPT_DEFAULT);
        return (OPT_INVALID);
}

static raopt_t *
ra_str2opt(const char *optnamestr)
{
        int     i;

        for (i = 0; ra_opts[i].opt_name != NULL; i++) {
                if (strcmp(optnamestr, ra_opts[i].opt_name) == 0)
                        break;
        }
        if (ra_opts[i].opt_name == NULL)
                return (NULL);
        return (&ra_opts[i]);
}

/*
 * Reset all option values previously gathered to B_FALSE.
 */
static void
ra_resetopts(void)
{
        int     i;

        for (i = 0; ra_opts[i].opt_name != NULL; i++) {
                ra_opts[i].opt_enabled = B_FALSE;
                ra_opts[i].opt_default_enabled = B_FALSE;
        }
}

static ravar_t *
ra_str2var(const char *varnamestr)
{
        int     i;
        for (i = 0; ra_vars[i].var_name != NULL; i++) {
                if (strcmp(varnamestr, ra_vars[i].var_name) == 0)
                        break;
        }
        if (ra_vars[i].var_name == NULL)
                return (NULL);
        return (&ra_vars[i]);
}

/*
 * Reset variable values previously gathered to NULL.
 */
static void
ra_resetvars(const char *proto)
{
        int     i;
        for (i = 0; ra_vars[i].var_name != NULL; i++) {
                if (proto != NULL &&
                    !VAR_PROTO_MATCH(ra_vars[i].var_name, proto))
                        continue;
                if (ra_vars[i].var_value != NULL)
                        free(ra_vars[i].var_value);
                ra_vars[i].var_value = NULL;
                if (ra_vars[i].var_default_value != NULL)
                        free(ra_vars[i].var_default_value);
                ra_vars[i].var_default_value = NULL;
        }
}

/*
 * Given an option name, this function provides an internationalized, human
 * readable version of the option name.
 */
static char *
ra_intloptname(const char *optname)
{
        if (strcmp(optname, RA_OPT_IPV4_FORWARDING) == 0)
                return (gettext("IPv4 forwarding"));
        else if (strcmp(optname, RA_OPT_IPV4_ROUTING) == 0)
                return (gettext("IPv4 routing"));
        else if (strcmp(optname, RA_OPT_IPV6_FORWARDING) == 0)
                return (gettext("IPv6 forwarding"));
        else if (strcmp(optname, RA_OPT_IPV6_ROUTING) == 0)
                return (gettext("IPv6 routing"));
        else if (strcmp(optname, RA_VAR_IPV4_ROUTING_DAEMON) == 0)
                return (gettext("IPv4 routing daemon"));
        else if (strcmp(optname, RA_VAR_IPV4_ROUTING_DAEMON_ARGS) == 0)
                return (gettext("IPv4 routing daemon args"));
        else if (strcmp(optname, RA_VAR_IPV4_ROUTING_STOP_CMD) == 0)
                return (gettext("IPv4 routing daemon stop"));
        else if (strcmp(optname, RA_VAR_IPV6_ROUTING_DAEMON) == 0)
                return (gettext("IPv6 routing daemon"));
        else if (strcmp(optname, RA_VAR_IPV6_ROUTING_DAEMON_ARGS) == 0)
                return (gettext("IPv6 routing daemon args"));
        else if (strcmp(optname, RA_VAR_IPV6_ROUTING_STOP_CMD) == 0)
                return (gettext("IPv6 routing daemon stop"));
        else if (strcmp(optname, RA_VAR_ROUTING_SVCS) == 0)
                return (gettext("Routing services"));
        /*
         * If we get here, there's a bug and someone should trip over this
         * NULL pointer.
         */
        return (NULL);
}