/* * 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. */ #pragma ident "%Z%%M% %I% %E% SMI" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * 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_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(1M)" #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_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); /* 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 ]\n" " %1$s [-e