/* * 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 (c) 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2018, Joyent, Inc. * Copyright 2017 Gary Mills * Copyright (c) 2016, Chris Fraire . * Copyright 2021 Tintri by DDN, Inc. All rights reserved. * Copyright 2021 OmniOS Community Edition (OmniOSce) Association. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define STR_UNKNOWN_VAL "?" #define LIFC_DEFAULT (LIFC_NOXMIT | LIFC_TEMPORARY | LIFC_ALLZONES |\ LIFC_UNDER_IPMP) static void do_create_ip_common(int, char **, const char *, uint32_t); typedef void cmdfunc_t(int, char **, const char *); static cmdfunc_t do_help; static cmdfunc_t do_create_ip, do_delete_ip; static cmdfunc_t do_create_ipmp, do_add_ipmp, do_remove_ipmp; static cmdfunc_t do_disable_if, do_enable_if, do_show_if; static cmdfunc_t do_set_ifprop, do_reset_ifprop, do_show_ifprop; static cmdfunc_t do_create_addr, do_delete_addr, do_show_addr, do_refresh_addr; static cmdfunc_t do_disable_addr, do_enable_addr, do_down_addr, do_up_addr; static cmdfunc_t do_set_addrprop, do_reset_addrprop, do_show_addrprop; static cmdfunc_t do_set_prop, do_reset_prop, do_show_prop; typedef struct cmd { char *c_name; cmdfunc_t *c_fn; const char *c_usage; } cmd_t; static cmd_t cmds[] = { { "help", do_help, NULL }, /* interface management related sub-commands */ { "create-if", do_create_ip, "\tcreate-if\t[-t] " }, { "create-ip", do_create_ip, "\tcreate-ip\t[-t] " }, { "delete-if", do_delete_ip, "\tdelete-if\t\n" }, { "delete-ip", do_delete_ip, "\tdelete-ip\t\n" }, { "create-ipmp", do_create_ipmp, "\tcreate-ipmp\t[-t] [-i [,]...] " "" }, { "delete-ipmp", do_delete_ip, "\tdelete-ipmp\t" }, { "add-ipmp", do_add_ipmp, "\tadd-ipmp\t[-t] -i [,]... " "" }, { "remove-ipmp", do_remove_ipmp, "\tremove-ipmp\t[-t] -i [,]... " "\n" }, { "disable-if", do_disable_if, "\tdisable-if\t-t " }, { "enable-if", do_enable_if, "\tenable-if\t-t " }, { "show-if", do_show_if, "\tshow-if\t\t[[-p] -o ,...] []\n" }, { "set-ifprop", do_set_ifprop, "\tset-ifprop\t[-t] -p = -m " "" }, { "reset-ifprop", do_reset_ifprop, "\treset-ifprop\t[-t] -p -m " }, { "show-ifprop", do_show_ifprop, "\tshow-ifprop\t[[-c] -o ,...] [-p ,...]\n" "\t\t\t[-m ] [interface]\n" }, /* address management related sub-commands */ { "create-addr", do_create_addr, "\tcreate-addr\t[-t] -T static [-d] " "-a{local|remote}=addr[/prefixlen]\n\t\t\t\n" "\tcreate-addr\t[-t] -T dhcp [-w | forever]\n" "\t\t\t[-1] [-h ] \n" "\tcreate-addr\t[-t] -T addrconf [-i interface-id]\n" "\t\t\t[-p {stateful|stateless}={yes|no}] " }, { "delete-addr", do_delete_addr, "\tdelete-addr\t[-r] " }, { "show-addr", do_show_addr, "\tshow-addr\t[[-p] -o ,...] []" }, { "refresh-addr", do_refresh_addr, "\trefresh-addr\t[-i] " }, { "down-addr", do_down_addr, "\tdown-addr\t[-t] " }, { "up-addr", do_up_addr, "\tup-addr\t\t[-t] " }, { "disable-addr", do_disable_addr, "\tdisable-addr\t-t " }, { "enable-addr", do_enable_addr, "\tenable-addr\t-t \n" }, { "set-addrprop", do_set_addrprop, "\tset-addrprop\t[-t] -p = " }, { "reset-addrprop", do_reset_addrprop, "\treset-addrprop\t[-t] -p " }, { "show-addrprop", do_show_addrprop, "\tshow-addrprop\t[[-c] -o ,...] [-p ,...] " "\n" }, /* protocol properties related sub-commands */ { "set-prop", do_set_prop, "\tset-prop\t[-t] -p [+|-]= " }, { "reset-prop", do_reset_prop, "\treset-prop\t[-t] -p " }, { "show-prop", do_show_prop, "\tshow-prop\t[[-c] -o ,...] [-p ,...]" " [protocol]" } }; static const struct option if_longopts[] = { {"temporary", no_argument, 0, 't' }, { 0, 0, 0, 0 } }; static const struct option show_prop_longopts[] = { {"parsable", no_argument, 0, 'c' }, {"prop", required_argument, 0, 'p' }, {"output", required_argument, 0, 'o' }, { 0, 0, 0, 0 } }; static const struct option show_ifprop_longopts[] = { {"module", required_argument, 0, 'm' }, {"parsable", no_argument, 0, 'c' }, {"prop", required_argument, 0, 'p' }, {"output", required_argument, 0, 'o' }, { 0, 0, 0, 0 } }; static const struct option set_prop_longopts[] = { {"prop", required_argument, 0, 'p' }, {"temporary", no_argument, 0, 't' }, { 0, 0, 0, 0 } }; static const struct option set_ifprop_longopts[] = { {"module", required_argument, 0, 'm' }, {"prop", required_argument, 0, 'p' }, {"temporary", no_argument, 0, 't' }, { 0, 0, 0, 0 } }; static const struct option addr_misc_longopts[] = { {"inform", no_argument, 0, 'i' }, {"release", no_argument, 0, 'r' }, {"temporary", no_argument, 0, 't' }, { 0, 0, 0, 0 } }; static const struct option addr_longopts[] = { {"address", required_argument, 0, 'a' }, {"down", no_argument, 0, 'd' }, {"interface-id", required_argument, 0, 'i' }, {"primary", no_argument, 0, '1' }, {"prop", required_argument, 0, 'p' }, {"reqhost", required_argument, 0, 'h' }, {"temporary", no_argument, 0, 't' }, {"type", required_argument, 0, 'T' }, {"wait", required_argument, 0, 'w' }, { 0, 0, 0, 0 } }; static const struct option show_addr_longopts[] = { {"parsable", no_argument, 0, 'p' }, {"output", required_argument, 0, 'o' }, { 0, 0, 0, 0 } }; static const struct option show_if_longopts[] = { {"parsable", no_argument, 0, 'p' }, {"output", required_argument, 0, 'o' }, { 0, 0, 0, 0 } }; /* callback functions to print show-* subcommands output */ static ofmt_cb_t print_prop_cb; static ofmt_cb_t print_sa_cb; static ofmt_cb_t print_si_cb; /* structures for 'ipadm show-*' subcommands */ typedef enum { IPADM_PROPFIELD_IFNAME, IPADM_PROPFIELD_PROTO, IPADM_PROPFIELD_ADDROBJ, IPADM_PROPFIELD_PROPERTY, IPADM_PROPFIELD_PERM, IPADM_PROPFIELD_CURRENT, IPADM_PROPFIELD_PERSISTENT, IPADM_PROPFIELD_DEFAULT, IPADM_PROPFIELD_POSSIBLE } ipadm_propfield_index_t; static ofmt_field_t intfprop_fields[] = { /* name, field width, index, callback */ { "IFNAME", 12, IPADM_PROPFIELD_IFNAME, print_prop_cb}, { "PROPERTY", 16, IPADM_PROPFIELD_PROPERTY, print_prop_cb}, { "PROTO", 6, IPADM_PROPFIELD_PROTO, print_prop_cb}, { "PERM", 5, IPADM_PROPFIELD_PERM, print_prop_cb}, { "CURRENT", 11, IPADM_PROPFIELD_CURRENT, print_prop_cb}, { "PERSISTENT", 11, IPADM_PROPFIELD_PERSISTENT, print_prop_cb}, { "DEFAULT", 11, IPADM_PROPFIELD_DEFAULT, print_prop_cb}, { "POSSIBLE", 16, IPADM_PROPFIELD_POSSIBLE, print_prop_cb}, { NULL, 0, 0, NULL} }; static ofmt_field_t modprop_fields[] = { /* name, field width, index, callback */ { "PROTO", 6, IPADM_PROPFIELD_PROTO, print_prop_cb}, { "PROPERTY", 22, IPADM_PROPFIELD_PROPERTY, print_prop_cb}, { "PERM", 5, IPADM_PROPFIELD_PERM, print_prop_cb}, { "CURRENT", 13, IPADM_PROPFIELD_CURRENT, print_prop_cb}, { "PERSISTENT", 13, IPADM_PROPFIELD_PERSISTENT, print_prop_cb}, { "DEFAULT", 13, IPADM_PROPFIELD_DEFAULT, print_prop_cb}, { "POSSIBLE", 15, IPADM_PROPFIELD_POSSIBLE, print_prop_cb}, { NULL, 0, 0, NULL} }; static ofmt_field_t addrprop_fields[] = { /* name, field width, index, callback */ { "ADDROBJ", 18, IPADM_PROPFIELD_ADDROBJ, print_prop_cb}, { "PROPERTY", 11, IPADM_PROPFIELD_PROPERTY, print_prop_cb}, { "PERM", 5, IPADM_PROPFIELD_PERM, print_prop_cb}, { "CURRENT", 16, IPADM_PROPFIELD_CURRENT, print_prop_cb}, { "PERSISTENT", 16, IPADM_PROPFIELD_PERSISTENT, print_prop_cb}, { "DEFAULT", 16, IPADM_PROPFIELD_DEFAULT, print_prop_cb}, { "POSSIBLE", 15, IPADM_PROPFIELD_POSSIBLE, print_prop_cb}, { NULL, 0, 0, NULL} }; typedef struct show_prop_state { char sps_ifname[LIFNAMSIZ]; char sps_aobjname[IPADM_AOBJSIZ]; const char *sps_pname; uint_t sps_proto; char *sps_propval; nvlist_t *sps_proplist; boolean_t sps_parsable; boolean_t sps_addrprop; boolean_t sps_ifprop; boolean_t sps_modprop; ipadm_status_t sps_status; ipadm_status_t sps_retstatus; ofmt_handle_t sps_ofmt; } show_prop_state_t; typedef struct show_addr_state { boolean_t sa_parsable; boolean_t sa_persist; ofmt_handle_t sa_ofmt; } show_addr_state_t; typedef struct show_if_state { boolean_t si_parsable; ofmt_handle_t si_ofmt; } show_if_state_t; typedef struct show_addr_args_s { show_addr_state_t *sa_state; ipadm_addr_info_t *sa_info; } show_addr_args_t; typedef struct show_if_args_s { show_if_state_t *si_state; ipadm_if_info_t *si_info; } show_if_args_t; typedef enum { SA_ADDROBJ, SA_TYPE, SA_STATE, SA_CURRENT, SA_PERSISTENT, SA_ADDR } sa_field_index_t; typedef enum { SI_IFNAME, SI_IFCLASS, SI_STATE, SI_CURRENT, SI_PERSISTENT } si_field_index_t; static ofmt_field_t show_addr_fields[] = { /* name, field width, id, callback */ { "ADDROBJ", 18, SA_ADDROBJ, print_sa_cb}, { "TYPE", 9, SA_TYPE, print_sa_cb}, { "STATE", 13, SA_STATE, print_sa_cb}, { "CURRENT", 8, SA_CURRENT, print_sa_cb}, { "PERSISTENT", 11, SA_PERSISTENT, print_sa_cb}, { "ADDR", 46, SA_ADDR, print_sa_cb}, { NULL, 0, 0, NULL} }; static ofmt_field_t show_if_fields[] = { /* name, field width, id, callback */ { "IFNAME", 11, SI_IFNAME, print_si_cb}, { "CLASS", 10, SI_IFCLASS, print_si_cb}, { "STATE", 9, SI_STATE, print_si_cb}, { "CURRENT", 13, SI_CURRENT, print_si_cb}, { "PERSISTENT", 11, SI_PERSISTENT, print_si_cb}, { NULL, 0, 0, NULL} }; #define IPADM_ALL_BITS ((uint_t)-1) typedef struct intf_mask { char *name; uint64_t bits; uint64_t mask; } fmask_t; typedef enum { IPMP_ADD_MEMBER, IPMP_REMOVE_MEMBER } ipmp_action_t; /* * Handle to libipadm. Opened in main() before the sub-command specific * function is called and is closed before the program exits. */ ipadm_handle_t iph = NULL; /* * Opaque ipadm address object. Used by all the address management subcommands. */ ipadm_addrobj_t ipaddr = NULL; static char *progname; static void warn(const char *, ...); static void die(const char *, ...) __NORETURN; static void die_opterr(int, int, const char *) __NORETURN; static void warn_ipadmerr(ipadm_status_t, const char *, ...); static void ipadm_check_propstr(const char *, boolean_t, const char *); static void process_misc_addrargs(int, char **, const char *, int *, uint32_t *); static void do_action_ipmp(char *, ipadm_ipmp_members_t *, ipmp_action_t, uint32_t); static void usage(int ret) { int i; cmd_t *cmdp; (void) fprintf(stderr, gettext("usage: ipadm ...\n")); for (i = 0; i < sizeof (cmds) / sizeof (cmds[0]); i++) { cmdp = &cmds[i]; if (cmdp->c_usage != NULL) (void) fprintf(stderr, "%s\n", gettext(cmdp->c_usage)); } ipadm_destroy_addrobj(ipaddr); ipadm_close(iph); exit(ret); } static void do_help(int argc __unused, char **argv __unused, const char *use __unused) { usage(0); } int main(int argc, char *argv[]) { int i; cmd_t *cmdp; ipadm_status_t status; (void) setlocale(LC_ALL, ""); (void) textdomain(TEXT_DOMAIN); if ((progname = strrchr(argv[0], '/')) == NULL) progname = argv[0]; else progname++; if (argc < 2) { argv[1] = "show-addr"; argc = 2; } status = ipadm_open(&iph, 0); if (status != IPADM_SUCCESS) { die("Could not open handle to library - %s", ipadm_status2str(status)); } for (i = 0; i < sizeof (cmds) / sizeof (cmds[0]); i++) { cmdp = &cmds[i]; if (strcmp(argv[1], cmdp->c_name) == 0) { cmdp->c_fn(argc - 1, &argv[1], gettext(cmdp->c_usage)); ipadm_destroy_addrobj(ipaddr); ipadm_close(iph); exit(0); } } (void) fprintf(stderr, gettext("%s: unknown subcommand '%s'\n"), progname, argv[1]); usage(1); return (0); } /* * Create regular IP interface or IPMP group interface */ static void do_create_ip_common(int argc, char *argv[], const char *use, uint32_t flags) { ipadm_status_t status; int option; opterr = 0; while ((option = getopt_long(argc, argv, ":t", if_longopts, NULL)) != -1) { switch (option) { case 't': /* * "ifconfig" mode - plumb interface, but do not * restore settings that may exist in db. */ flags &= ~IPADM_OPT_PERSIST; break; default: die_opterr(optopt, option, use); } } if (optind != (argc - 1)) { if (use != NULL) die("usage: %s", use); else die(NULL); } status = ipadm_create_if(iph, argv[optind], AF_UNSPEC, flags); if (status != IPADM_SUCCESS) { die("Could not create %s : %s", argv[optind], ipadm_status2str(status)); } } /* * Helpers to parse options into ipadm_ipmp_members_t list, and to free it. */ static ipadm_ipmp_members_t * get_ipmp_members(int argc, char *argv[], const char *use, uint32_t *flags) { ipadm_ipmp_members_t *members = NULL; ipadm_ipmp_member_t *member; char *ifname; int option; opterr = 0; while ((option = getopt_long(argc, argv, ":i:", if_longopts, NULL)) != -1) { switch (option) { case 't': *flags &= ~IPADM_OPT_PERSIST; break; case 'i': if (members == NULL) { members = calloc(1, sizeof (ipadm_ipmp_members_t)); if (members == NULL) die("insufficient memory"); list_create(members, sizeof (ipadm_ipmp_member_t), offsetof(ipadm_ipmp_member_t, node)); } for (ifname = strtok(optarg, ","); ifname != NULL; ifname = strtok(NULL, ",")) { if ((member = calloc(1, sizeof (ipadm_ipmp_member_t))) == NULL) die("insufficient memory"); if (strlcpy(member->if_name, ifname, sizeof (member->if_name)) >= LIFNAMSIZ) die("Incorrect length of interface " "name: %s", ifname); list_insert_tail(members, member); } break; default: die_opterr(optopt, option, use); } } if (optind != (argc - 1)) die("Usage: %s", use); if (members != NULL && list_is_empty(members)) { free(members); members = NULL; } return (members); } static void free_ipmp_members(ipadm_ipmp_members_t *members) { ipadm_ipmp_member_t *member; while ((member = list_remove_head(members)) != NULL) free(member); list_destroy(members); free(members); } /* * Create an IPMP group interface for which no saved configuration * exists in the persistent store. */ static void do_create_ipmp(int argc, char *argv[], const char *use) { uint32_t flags = IPADM_OPT_PERSIST | IPADM_OPT_ACTIVE | IPADM_OPT_IPMP; ipadm_ipmp_members_t *members = NULL; ipmp_handle_t ipmp_handle; int retval; retval = ipmp_open(&ipmp_handle); if (retval != IPMP_SUCCESS) { die("Could not create IPMP handle: %s", ipadm_status2str(retval)); } retval = ipmp_ping_daemon(ipmp_handle); ipmp_close(ipmp_handle); if (retval != IPMP_SUCCESS) { die("Cannot ping in.mpathd: %s", ipmp_errmsg(retval)); } members = get_ipmp_members(argc, argv, use, &flags); do_create_ip_common(argc, argv, use, flags); if (members != NULL) { do_action_ipmp(argv[optind], members, IPMP_ADD_MEMBER, flags); free_ipmp_members(members); } } static void do_add_ipmp(int argc, char *argv[], const char *use) { uint32_t flags = IPADM_OPT_PERSIST | IPADM_OPT_ACTIVE; ipadm_ipmp_members_t *members; members = get_ipmp_members(argc, argv, use, &flags); if (members == NULL) die_opterr(optopt, ':', use); do_action_ipmp(argv[optind], members, IPMP_ADD_MEMBER, flags); free_ipmp_members(members); } static void do_remove_ipmp(int argc, char *argv[], const char *use) { uint32_t flags = IPADM_OPT_PERSIST | IPADM_OPT_ACTIVE; ipadm_ipmp_members_t *members; members = get_ipmp_members(argc, argv, use, &flags); if (members == NULL) die_opterr(optopt, ':', use); do_action_ipmp(argv[optind], members, IPMP_REMOVE_MEMBER, flags); free_ipmp_members(members); } static void do_action_ipmp(char *ipmp, ipadm_ipmp_members_t *members, ipmp_action_t action, uint32_t flags) { ipadm_status_t (*func)(ipadm_handle_t, const char *, const char *, uint32_t); ipadm_status_t status; ipadm_ipmp_member_t *member; char *ifname; const char *msg; if (action == IPMP_ADD_MEMBER) { func = ipadm_add_ipmp_member; msg = "Cannot add interface '%s' to IPMP interface '%s': %s"; } else { func = ipadm_remove_ipmp_member; msg = "Cannot remove interface '%s' from IPMP interface '%s': " "%s"; } while ((member = list_remove_head(members)) != NULL) { ifname = member->if_name; status = func(iph, ipmp, ifname, flags); if (status != IPADM_SUCCESS) die(msg, ifname, ipmp, ipadm_status2str(status)); } } /* * Create an IP interface for which no saved configuration exists in the * persistent store. */ static void do_create_ip(int argc, char *argv[], const char *use) { do_create_ip_common(argc, argv, use, IPADM_OPT_PERSIST | IPADM_OPT_ACTIVE); } /* * Enable an IP interface based on the persistent configuration for * that interface. */ static void do_enable_if(int argc, char *argv[], const char *use) { ipadm_status_t status; int index; uint32_t flags = IPADM_OPT_ACTIVE|IPADM_OPT_PERSIST; process_misc_addrargs(argc, argv, use, &index, &flags); if (flags & IPADM_OPT_PERSIST) die("persistent operation not supported for enable-if"); status = ipadm_enable_if(iph, argv[index], flags); if (status == IPADM_ALL_ADDRS_NOT_ENABLED) { warn_ipadmerr(status, ""); } else if (status != IPADM_SUCCESS) { die("Could not enable %s : %s", argv[optind], ipadm_status2str(status)); } } /* * Remove an IP interface from both active and persistent configuration. */ static void do_delete_ip(int argc, char *argv[], const char *use) { ipadm_status_t status; uint32_t flags = IPADM_OPT_ACTIVE|IPADM_OPT_PERSIST; if (argc != 2) die("Usage: %s", use); status = ipadm_delete_if(iph, argv[1], AF_UNSPEC, flags); if (status != IPADM_SUCCESS) { die("Could not delete %s: %s", argv[optind], ipadm_status2str(status)); } } /* * Disable an IP interface by removing it from active configuration. */ static void do_disable_if(int argc, char *argv[], const char *use) { ipadm_status_t status; int index; uint32_t flags = IPADM_OPT_ACTIVE|IPADM_OPT_PERSIST; process_misc_addrargs(argc, argv, use, &index, &flags); if (flags & IPADM_OPT_PERSIST) die("persistent operation not supported for disable-if"); status = ipadm_disable_if(iph, argv[index], flags); if (status != IPADM_SUCCESS) { die("Could not disable %s: %s", argv[optind], ipadm_status2str(status)); } } /* * Print individual columns for the show-*prop subcommands. */ static void print_prop(show_prop_state_t *statep, uint_t flags, char *buf, size_t bufsize) { const char *prop_name = statep->sps_pname; char *ifname = statep->sps_ifname; char *propval = statep->sps_propval; uint_t proto = statep->sps_proto; size_t propsize = MAXPROPVALLEN; ipadm_status_t status; if (statep->sps_ifprop) { status = ipadm_get_ifprop(iph, ifname, prop_name, propval, &propsize, proto, flags); } else if (statep->sps_modprop) { status = ipadm_get_prop(iph, prop_name, propval, &propsize, proto, flags); } else { status = ipadm_get_addrprop(iph, prop_name, propval, &propsize, statep->sps_aobjname, flags); } if (status != IPADM_SUCCESS) { if ((status == IPADM_NOTFOUND && (flags & IPADM_OPT_PERSIST)) || status == IPADM_ENXIO) { propval[0] = '\0'; goto cont; } statep->sps_status = status; statep->sps_retstatus = status; return; } cont: statep->sps_status = IPADM_SUCCESS; (void) snprintf(buf, bufsize, "%s", propval); } /* * Callback function for show-*prop subcommands. */ static boolean_t print_prop_cb(ofmt_arg_t *ofarg, char *buf, size_t bufsize) { show_prop_state_t *statep = ofarg->ofmt_cbarg; const char *propname = statep->sps_pname; uint_t proto = statep->sps_proto; boolean_t cont = _B_TRUE; /* * Fail retrieving remaining fields, if you fail * to retrieve a field. */ if (statep->sps_status != IPADM_SUCCESS) return (_B_FALSE); switch (ofarg->ofmt_id) { case IPADM_PROPFIELD_IFNAME: (void) snprintf(buf, bufsize, "%s", statep->sps_ifname); break; case IPADM_PROPFIELD_PROTO: (void) snprintf(buf, bufsize, "%s", ipadm_proto2str(proto)); break; case IPADM_PROPFIELD_ADDROBJ: (void) snprintf(buf, bufsize, "%s", statep->sps_aobjname); break; case IPADM_PROPFIELD_PROPERTY: (void) snprintf(buf, bufsize, "%s", propname); break; case IPADM_PROPFIELD_PERM: print_prop(statep, IPADM_OPT_PERM, buf, bufsize); break; case IPADM_PROPFIELD_CURRENT: print_prop(statep, IPADM_OPT_ACTIVE, buf, bufsize); break; case IPADM_PROPFIELD_PERSISTENT: print_prop(statep, IPADM_OPT_PERSIST, buf, bufsize); break; case IPADM_PROPFIELD_DEFAULT: print_prop(statep, IPADM_OPT_DEFAULT, buf, bufsize); break; case IPADM_PROPFIELD_POSSIBLE: print_prop(statep, IPADM_OPT_POSSIBLE, buf, bufsize); break; } if (statep->sps_status != IPADM_SUCCESS) cont = _B_FALSE; return (cont); } /* * Callback function called by the property walker (ipadm_walk_prop() or * ipadm_walk_proptbl()), for every matched property. This function in turn * calls ofmt_print() to print property information. */ boolean_t show_property(void *arg, const char *pname, uint_t proto) { show_prop_state_t *statep = arg; statep->sps_pname = pname; statep->sps_proto = proto; statep->sps_status = IPADM_SUCCESS; ofmt_print(statep->sps_ofmt, arg); /* * if an object is not found or operation is not supported then * stop the walker. */ if (statep->sps_status == IPADM_NOTFOUND || statep->sps_status == IPADM_NOTSUP) return (_B_FALSE); return (_B_TRUE); } /* * Properties to be displayed is in `statep->sps_proplist'. If it is NULL, * for all the properties for the specified object, display relevant * information. Otherwise, for the selected property set, display relevant * information */ static void show_properties(void *arg, int prop_class) { show_prop_state_t *statep = arg; nvlist_t *nvl = statep->sps_proplist; uint_t proto = statep->sps_proto; nvpair_t *curr_nvp; char *buf, *name; ipadm_status_t status; /* allocate sufficient buffer to hold a property value */ if ((buf = malloc(MAXPROPVALLEN)) == NULL) die("insufficient memory"); statep->sps_propval = buf; /* if no properties were specified, display all the properties */ if (nvl == NULL) { (void) ipadm_walk_proptbl(proto, prop_class, show_property, statep); } else { for (curr_nvp = nvlist_next_nvpair(nvl, NULL); curr_nvp; curr_nvp = nvlist_next_nvpair(nvl, curr_nvp)) { name = nvpair_name(curr_nvp); status = ipadm_walk_prop(name, proto, prop_class, show_property, statep); if (status == IPADM_PROP_UNKNOWN) (void) show_property(statep, name, proto); } } free(buf); } /* * Display information for all or specific interface properties, either for a * given interface or for all the interfaces in the system. */ static void do_show_ifprop(int argc, char **argv, const char *use) { int option; nvlist_t *proplist = NULL; char *fields_str = NULL; char *ifname; ofmt_handle_t ofmt; ofmt_status_t oferr; uint_t ofmtflags = 0; uint_t proto; boolean_t m_arg = _B_FALSE; char *protostr; ipadm_if_info_t *ifinfo, *ifp; ipadm_status_t status; show_prop_state_t state; protostr = "ip"; opterr = 0; bzero(&state, sizeof (state)); state.sps_propval = NULL; state.sps_parsable = _B_FALSE; state.sps_ifprop = _B_TRUE; state.sps_status = state.sps_retstatus = IPADM_SUCCESS; while ((option = getopt_long(argc, argv, ":p:m:co:", show_ifprop_longopts, NULL)) != -1) { switch (option) { case 'p': if (ipadm_str2nvlist(optarg, &proplist, IPADM_NORVAL) != 0) die("invalid interface properties specified"); break; case 'c': state.sps_parsable = _B_TRUE; break; case 'o': fields_str = optarg; break; case 'm': if (m_arg) die("cannot specify more than one -m"); m_arg = _B_TRUE; protostr = optarg; break; default: die_opterr(optopt, option, use); break; } } if (optind == argc - 1) ifname = argv[optind]; else if (optind != argc) die("Usage: %s", use); else ifname = NULL; if ((proto = ipadm_str2proto(protostr)) == MOD_PROTO_NONE) die("invalid protocol '%s' specified", protostr); state.sps_proto = proto; state.sps_proplist = proplist; if (state.sps_parsable) ofmtflags |= OFMT_PARSABLE; oferr = ofmt_open(fields_str, intfprop_fields, ofmtflags, 0, &ofmt); ofmt_check(oferr, state.sps_parsable, ofmt, die, warn); state.sps_ofmt = ofmt; /* retrieve interface(s) and print the properties */ status = ipadm_if_info(iph, ifname, &ifinfo, 0, LIFC_DEFAULT); if (ifname != NULL && status == IPADM_ENXIO) die("no such object '%s': %s", ifname, ipadm_status2str(status)); if (status != IPADM_SUCCESS) die("Error retrieving interface(s): %s", ipadm_status2str(status)); for (ifp = ifinfo; ifp != NULL; ifp = ifp->ifi_next) { (void) strlcpy(state.sps_ifname, ifp->ifi_name, LIFNAMSIZ); state.sps_proto = proto; show_properties(&state, IPADMPROP_CLASS_IF); } if (ifinfo) ipadm_free_if_info(ifinfo); nvlist_free(proplist); ofmt_close(ofmt); if (state.sps_retstatus != IPADM_SUCCESS) { ipadm_close(iph); exit(EXIT_FAILURE); } } /* * set/reset the interface property for a given interface. */ static void set_ifprop(int argc, char **argv, boolean_t reset, const char *use) { int option; ipadm_status_t status = IPADM_SUCCESS; boolean_t p_arg = _B_FALSE; boolean_t m_arg = _B_FALSE; char *ifname, *nv, *protostr; char *prop_name, *prop_val; uint_t flags = IPADM_OPT_PERSIST; uint_t proto; nv = NULL; protostr = NULL; opterr = 0; while ((option = getopt_long(argc, argv, ":m:p:t", set_ifprop_longopts, NULL)) != -1) { switch (option) { case 'p': if (p_arg) die("-p must be specified once only"); p_arg = _B_TRUE; ipadm_check_propstr(optarg, reset, use); nv = optarg; break; case 'm': if (m_arg) die("-m must be specified once only"); m_arg = _B_TRUE; protostr = optarg; break; case 't': flags &= ~IPADM_OPT_PERSIST; break; default: die_opterr(optopt, option, use); } } if (!m_arg || !p_arg || optind != argc - 1) die("Usage: %s", use); ifname = argv[optind]; prop_name = nv; prop_val = strchr(nv, '='); if (prop_val != NULL) *prop_val++ = '\0'; if ((proto = ipadm_str2proto(protostr)) == MOD_PROTO_NONE) die("invalid protocol '%s' specified", protostr); if (reset) flags |= IPADM_OPT_DEFAULT; else flags |= IPADM_OPT_ACTIVE; status = ipadm_set_ifprop(iph, ifname, prop_name, prop_val, proto, flags); if (status != IPADM_SUCCESS) { if (reset) die("reset-ifprop: %s: %s", prop_name, ipadm_status2str(status)); else die("set-ifprop: %s: %s", prop_name, ipadm_status2str(status)); } } static void do_set_ifprop(int argc, char **argv, const char *use) { set_ifprop(argc, argv, _B_FALSE, use); } static void do_reset_ifprop(int argc, char **argv, const char *use) { set_ifprop(argc, argv, _B_TRUE, use); } /* * Display information for all or specific protocol properties, either for a * given protocol or for supported protocols (IP/IPv4/IPv6/TCP/UDP/SCTP) */ static void do_show_prop(int argc, char **argv, const char *use) { char option; nvlist_t *proplist = NULL; char *fields_str = NULL; char *protostr; show_prop_state_t state; ofmt_handle_t ofmt; ofmt_status_t oferr; uint_t ofmtflags = 0; uint_t proto; boolean_t p_arg = _B_FALSE; opterr = 0; bzero(&state, sizeof (state)); state.sps_propval = NULL; state.sps_parsable = _B_FALSE; state.sps_modprop = _B_TRUE; state.sps_status = state.sps_retstatus = IPADM_SUCCESS; while ((option = getopt_long(argc, argv, ":p:co:", show_prop_longopts, NULL)) != -1) { switch (option) { case 'p': if (p_arg) die("-p must be specified once only"); p_arg = _B_TRUE; if (ipadm_str2nvlist(optarg, &proplist, IPADM_NORVAL) != 0) die("invalid protocol properties specified"); break; case 'c': state.sps_parsable = _B_TRUE; break; case 'o': fields_str = optarg; break; default: die_opterr(optopt, option, use); break; } } if (optind == argc - 1) { protostr = argv[optind]; if ((proto = ipadm_str2proto(protostr)) == MOD_PROTO_NONE) die("invalid protocol '%s' specified", protostr); state.sps_proto = proto; } else if (optind != argc) { die("Usage: %s", use); } else { if (p_arg) die("protocol must be specified when " "property name is used"); state.sps_proto = MOD_PROTO_NONE; } state.sps_proplist = proplist; if (state.sps_parsable) ofmtflags |= OFMT_PARSABLE; else ofmtflags |= OFMT_WRAP; oferr = ofmt_open(fields_str, modprop_fields, ofmtflags, 0, &ofmt); ofmt_check(oferr, state.sps_parsable, ofmt, die, warn); state.sps_ofmt = ofmt; /* handles all the errors */ show_properties(&state, IPADMPROP_CLASS_MODULE); nvlist_free(proplist); ofmt_close(ofmt); if (state.sps_retstatus != IPADM_SUCCESS) { ipadm_close(iph); exit(EXIT_FAILURE); } } /* * Checks to see if there are any modifiers, + or -. If there are modifiers * then sets IPADM_OPT_APPEND or IPADM_OPT_REMOVE, accordingly. */ static void parse_modifiers(const char *pstr, uint_t *flags, const char *use) { char *p; if ((p = strchr(pstr, '=')) == NULL) return; if (p == pstr) die("Invalid prop=val specified\n%s", use); --p; if (*p == '+') *flags |= IPADM_OPT_APPEND; else if (*p == '-') *flags |= IPADM_OPT_REMOVE; } /* * set/reset the protocol property for a given protocol. */ static void set_prop(int argc, char **argv, boolean_t reset, const char *use) { int option; ipadm_status_t status = IPADM_SUCCESS; char *protostr, *nv, *prop_name, *prop_val; boolean_t p_arg = _B_FALSE; uint_t proto; uint_t flags = IPADM_OPT_PERSIST; nv = NULL; opterr = 0; while ((option = getopt_long(argc, argv, ":p:t", set_prop_longopts, NULL)) != -1) { switch (option) { case 'p': if (p_arg) die("-p must be specified once only"); p_arg = _B_TRUE; ipadm_check_propstr(optarg, reset, use); nv = optarg; break; case 't': flags &= ~IPADM_OPT_PERSIST; break; default: die_opterr(optopt, option, use); } } if (!p_arg || optind != argc - 1) die("Usage: %s", use); parse_modifiers(nv, &flags, use); prop_name = nv; prop_val = strchr(nv, '='); if (prop_val != NULL) { if (flags & (IPADM_OPT_APPEND|IPADM_OPT_REMOVE)) *(prop_val - 1) = '\0'; *prop_val++ = '\0'; } protostr = argv[optind]; if ((proto = ipadm_str2proto(protostr)) == MOD_PROTO_NONE) die("invalid protocol '%s' specified", protostr); if (reset) flags |= IPADM_OPT_DEFAULT; else flags |= IPADM_OPT_ACTIVE; status = ipadm_set_prop(iph, prop_name, prop_val, proto, flags); if (status != IPADM_SUCCESS) { if (reset) die("reset-prop: %s: %s", prop_name, ipadm_status2str(status)); else die("set-prop: %s: %s", prop_name, ipadm_status2str(status)); } } static void do_set_prop(int argc, char **argv, const char *use) { set_prop(argc, argv, _B_FALSE, use); } static void do_reset_prop(int argc, char **argv, const char *use) { set_prop(argc, argv, _B_TRUE, use); } /* PRINTFLIKE1 */ static void warn(const char *format, ...) { va_list alist; format = gettext(format); (void) fprintf(stderr, gettext("%s: warning: "), progname); va_start(alist, format); (void) vfprintf(stderr, format, alist); va_end(alist); (void) fprintf(stderr, "\n"); } /* PRINTFLIKE1 */ static void die(const char *format, ...) { va_list alist; if (format != NULL) { format = gettext(format); (void) fprintf(stderr, "%s: ", progname); va_start(alist, format); (void) vfprintf(stderr, format, alist); va_end(alist); (void) putchar('\n'); } ipadm_destroy_addrobj(ipaddr); ipadm_close(iph); exit(EXIT_FAILURE); } static void die_opterr(int opt, int opterr, const char *usage) { switch (opterr) { case ':': die("option '-%c' requires a value\nusage: %s", opt, gettext(usage)); break; case '?': default: die("unrecognized option '-%c'\nusage: %s", opt, gettext(usage)); break; } } /* PRINTFLIKE2 */ static void warn_ipadmerr(ipadm_status_t err, const char *format, ...) { va_list alist; format = gettext(format); (void) fprintf(stderr, gettext("%s: warning: "), progname); va_start(alist, format); (void) vfprintf(stderr, format, alist); va_end(alist); (void) fprintf(stderr, "%s\n", ipadm_status2str(err)); } static void process_static_addrargs(const char *use, char *addrarg, const char *aobjname) { int option; char *val; char *laddr = NULL; char *raddr = NULL; char *save_input_arg = addrarg; boolean_t found_mismatch = _B_FALSE; ipadm_status_t status; enum { A_LOCAL, A_REMOTE }; static char *addr_optstr[] = { "local", "remote", NULL, }; while (*addrarg != '\0') { option = getsubopt(&addrarg, addr_optstr, &val); switch (option) { case A_LOCAL: if (laddr != NULL) die("Multiple local addresses provided"); laddr = val; break; case A_REMOTE: if (raddr != NULL) die("Multiple remote addresses provided"); raddr = val; break; default: if (found_mismatch) die("Invalid address provided\nusage: %s", use); found_mismatch = _B_TRUE; break; } } if (raddr != NULL && laddr == NULL) die("Missing local address\nusage: %s", use); /* If only one address is provided, it is assumed a local address. */ if (laddr == NULL) { if (found_mismatch) laddr = save_input_arg; else die("Missing local address\nusage: %s", use); } /* Initialize the addrobj for static addresses. */ status = ipadm_create_addrobj(IPADM_ADDR_STATIC, aobjname, &ipaddr); if (status != IPADM_SUCCESS) { die("Error in creating address object: %s", ipadm_status2str(status)); } /* Set the local and remote addresses */ status = ipadm_set_addr(ipaddr, laddr, AF_UNSPEC); if (status != IPADM_SUCCESS) { die("Error in setting local address: %s", ipadm_status2str(status)); } if (raddr != NULL) { status = ipadm_set_dst_addr(ipaddr, raddr, AF_UNSPEC); if (status != IPADM_SUCCESS) { die("Error in setting remote address: %s", ipadm_status2str(status)); } } } static void process_addrconf_addrargs(const char *use, char *addrarg) { int option; char *val; enum { P_STATELESS, P_STATEFUL }; static char *addr_optstr[] = { "stateless", "stateful", NULL, }; boolean_t stateless = _B_FALSE; boolean_t stateless_arg = _B_FALSE; boolean_t stateful = _B_FALSE; boolean_t stateful_arg = _B_FALSE; ipadm_status_t status; while (*addrarg != '\0') { option = getsubopt(&addrarg, addr_optstr, &val); switch (option) { case P_STATELESS: if (stateless_arg) die("Duplicate option"); if (val == NULL) die("Invalid argument"); if (strcmp(val, "yes") == 0) stateless = _B_TRUE; else if (strcmp(val, "no") == 0) stateless = _B_FALSE; else die("Invalid argument"); stateless_arg = _B_TRUE; break; case P_STATEFUL: if (stateful_arg) die("Duplicate option"); if (val == NULL) die("Invalid argument"); if (strcmp(val, "yes") == 0) stateful = _B_TRUE; else if (strcmp(val, "no") == 0) stateful = _B_FALSE; else die("Invalid argument"); stateful_arg = _B_TRUE; break; default: die_opterr(optopt, option, use); } } if (!stateless_arg && !stateful_arg) die("Invalid arguments for option -p"); /* Set the addrobj fields for addrconf */ if (stateless_arg) { status = ipadm_set_stateless(ipaddr, stateless); if (status != IPADM_SUCCESS) { die("Error in setting stateless option: %s", ipadm_status2str(status)); } } if (stateful_arg) { status = ipadm_set_stateful(ipaddr, stateful); if (status != IPADM_SUCCESS) { die("Error in setting stateful option: %s", ipadm_status2str(status)); } } } /* * Creates static, dhcp or addrconf addresses and associates the created * addresses with the specified address object name. */ static void do_create_addr(int argc, char *argv[], const char *use) { ipadm_status_t status; int option; uint32_t flags = IPADM_OPT_PERSIST|IPADM_OPT_ACTIVE|IPADM_OPT_UP|IPADM_OPT_V46; char *cp; char *atype = NULL; char *static_arg = NULL; char *addrconf_arg = NULL; char *interface_id = NULL; char *wait = NULL; char *reqhost = NULL; boolean_t s_opt = _B_FALSE; /* static addr options */ boolean_t auto_opt = _B_FALSE; /* Addrconf options */ boolean_t dhcp_opt = _B_FALSE; /* dhcp options */ boolean_t primary_opt = _B_FALSE; /* dhcp primary option */ opterr = 0; while ((option = getopt_long(argc, argv, ":1T:a:dh:i:p:w:t", addr_longopts, NULL)) != -1) { switch (option) { case '1': primary_opt = _B_TRUE; break; case 'T': atype = optarg; break; case 'a': static_arg = optarg; s_opt = _B_TRUE; break; case 'd': flags &= ~IPADM_OPT_UP; s_opt = _B_TRUE; break; case 'h': reqhost = optarg; break; case 'i': interface_id = optarg; auto_opt = _B_TRUE; break; case 'p': addrconf_arg = optarg; auto_opt = _B_TRUE; break; case 'w': wait = optarg; dhcp_opt = _B_TRUE; break; case 't': flags &= ~IPADM_OPT_PERSIST; break; default: die_opterr(optopt, option, use); } } if (atype == NULL || optind != (argc - 1)) { die("Invalid arguments\nusage: %s", use); } else if ((cp = strchr(argv[optind], '/')) == NULL || strlen(++cp) == 0) { die("invalid address object name: %s\nusage: %s", argv[optind], use); } /* * Allocate and initialize the addrobj based on the address type. */ if (strcmp(atype, "static") == 0) { if (static_arg == NULL || auto_opt || dhcp_opt || reqhost != NULL || primary_opt) { die("Invalid arguments for type %s\nusage: %s", atype, use); } process_static_addrargs(use, static_arg, argv[optind]); } else if (strcmp(atype, "dhcp") == 0) { if (auto_opt || s_opt) { die("Invalid arguments for type %s\nusage: %s", atype, use); } /* Initialize the addrobj for dhcp addresses. */ status = ipadm_create_addrobj(IPADM_ADDR_DHCP, argv[optind], &ipaddr); if (status != IPADM_SUCCESS) { die("Error in creating address object: %s", ipadm_status2str(status)); } if (wait != NULL) { int32_t ipadm_wait; if (strcmp(wait, "forever") == 0) { ipadm_wait = IPADM_DHCP_WAIT_FOREVER; } else { char *end; long timeout = strtol(wait, &end, 10); if (*end != '\0' || timeout < 0) die("Invalid argument"); ipadm_wait = (int32_t)timeout; } status = ipadm_set_wait_time(ipaddr, ipadm_wait); if (status != IPADM_SUCCESS) { die("Error in setting wait time: %s", ipadm_status2str(status)); } } if (primary_opt) { status = ipadm_set_primary(ipaddr, _B_TRUE); if (status != IPADM_SUCCESS) { die("Error in setting primary flag: %s", ipadm_status2str(status)); } } if (reqhost != NULL) { status = ipadm_set_reqhost(ipaddr, reqhost); if (status != IPADM_SUCCESS) { die("Error in setting reqhost: %s", ipadm_status2str(status)); } } } else if (strcmp(atype, "addrconf") == 0) { if (dhcp_opt || s_opt || reqhost != NULL || primary_opt) { die("Invalid arguments for type %s\nusage: %s", atype, use); } /* Initialize the addrobj for ipv6-addrconf addresses. */ status = ipadm_create_addrobj(IPADM_ADDR_IPV6_ADDRCONF, argv[optind], &ipaddr); if (status != IPADM_SUCCESS) { die("Error in creating address object: %s", ipadm_status2str(status)); } if (interface_id != NULL) { status = ipadm_set_interface_id(ipaddr, interface_id); if (status != IPADM_SUCCESS) { die("Error in setting interface ID: %s", ipadm_status2str(status)); } } if (addrconf_arg) process_addrconf_addrargs(use, addrconf_arg); } else { die("Invalid address type %s", atype); } status = ipadm_create_addr(iph, ipaddr, flags); if (status == IPADM_DHCP_IPC_TIMEOUT) warn_ipadmerr(status, ""); else if (status != IPADM_SUCCESS) die("Could not create address: %s", ipadm_status2str(status)); } /* * Used by some address management functions to parse the command line * arguments and create `ipaddr' address object. */ static void process_misc_addrargs(int argc, char *argv[], const char *use, int *index, uint32_t *flags) { int option; opterr = 0; while ((option = getopt_long(argc, argv, ":t", addr_misc_longopts, NULL)) != -1) { switch (option) { case 't': *flags &= ~IPADM_OPT_PERSIST; break; default: die_opterr(optopt, option, use); } } if (optind != (argc - 1)) die("Usage: %s", use); *index = optind; } /* * Remove an addrobj from both active and persistent configuration. */ static void do_delete_addr(int argc, char *argv[], const char *use) { ipadm_status_t status; uint32_t flags = IPADM_OPT_ACTIVE|IPADM_OPT_PERSIST; int option; opterr = 0; while ((option = getopt_long(argc, argv, ":r", addr_misc_longopts, NULL)) != -1) { switch (option) { case 'r': flags |= IPADM_OPT_RELEASE; break; default: die_opterr(optopt, option, use); } } if (optind != (argc - 1)) die("Usage: %s", use); status = ipadm_delete_addr(iph, argv[optind], flags); if (status != IPADM_SUCCESS) { die("could not delete address: %s", ipadm_status2str(status)); } } /* * Enable an IP address based on the persistent configuration for that * IP address */ static void do_enable_addr(int argc, char *argv[], const char *use) { ipadm_status_t status; int index; uint32_t flags = IPADM_OPT_ACTIVE|IPADM_OPT_PERSIST; process_misc_addrargs(argc, argv, use, &index, &flags); if (flags & IPADM_OPT_PERSIST) die("persistent operation not supported for enable-addr"); status = ipadm_enable_addr(iph, argv[index], flags); if (status != IPADM_SUCCESS) die("could not enable address: %s", ipadm_status2str(status)); } /* * Mark the address identified by addrobj 'up' */ static void do_up_addr(int argc, char *argv[], const char *use) { ipadm_status_t status; int index; uint32_t flags = IPADM_OPT_ACTIVE|IPADM_OPT_PERSIST; process_misc_addrargs(argc, argv, use, &index, &flags); status = ipadm_up_addr(iph, argv[index], flags); if (status != IPADM_SUCCESS) { die("Could not mark the address up: %s", ipadm_status2str(status)); } } /* * Disable the specified addrobj by removing it from active cofiguration */ static void do_disable_addr(int argc, char *argv[], const char *use) { ipadm_status_t status; int index; uint32_t flags = IPADM_OPT_ACTIVE|IPADM_OPT_PERSIST; process_misc_addrargs(argc, argv, use, &index, &flags); if (flags & IPADM_OPT_PERSIST) die("persistent operation not supported for disable-addr"); status = ipadm_disable_addr(iph, argv[index], flags); if (status != IPADM_SUCCESS) { die("could not disable address: %s", ipadm_status2str(status)); } } /* * Mark the address identified by addrobj 'down' */ static void do_down_addr(int argc, char *argv[], const char *use) { ipadm_status_t status; int index; uint32_t flags = IPADM_OPT_ACTIVE|IPADM_OPT_PERSIST; process_misc_addrargs(argc, argv, use, &index, &flags); status = ipadm_down_addr(iph, argv[index], flags); if (status != IPADM_SUCCESS) die("Could not mark the address down: %s", ipadm_status2str(status)); } /* * Restart DAD for static address. Extend lease duration for DHCP addresses */ static void do_refresh_addr(int argc, char *argv[], const char *use) { ipadm_status_t status; int option; uint32_t flags = 0; opterr = 0; while ((option = getopt_long(argc, argv, ":i", addr_misc_longopts, NULL)) != -1) { switch (option) { case 'i': flags |= IPADM_OPT_INFORM; break; default: die_opterr(optopt, option, use); } } if (optind != (argc - 1)) die("Usage: %s", use); status = ipadm_refresh_addr(iph, argv[optind], flags); if (status == IPADM_DHCP_IPC_TIMEOUT) warn_ipadmerr(status, ""); else if (status != IPADM_SUCCESS) die("could not refresh address %s", ipadm_status2str(status)); } static void sockaddr2str(const struct sockaddr_storage *ssp, char *buf, uint_t bufsize) { socklen_t socklen; struct sockaddr *sp = (struct sockaddr *)ssp; switch (ssp->ss_family) { case AF_INET: socklen = sizeof (struct sockaddr_in); break; case AF_INET6: socklen = sizeof (struct sockaddr_in6); break; default: (void) strlcpy(buf, STR_UNKNOWN_VAL, bufsize); return; } (void) getnameinfo(sp, socklen, buf, bufsize, NULL, 0, (NI_NOFQDN | NI_NUMERICHOST)); } static void flags2str(uint64_t flags, fmask_t *tbl, boolean_t is_bits, char *buf, uint_t bufsize) { int i; boolean_t first = _B_TRUE; if (is_bits) { for (i = 0; tbl[i].name; i++) { if ((flags & tbl[i].mask) == tbl[i].bits) (void) strlcat(buf, tbl[i].name, bufsize); else (void) strlcat(buf, "-", bufsize); } } else { for (i = 0; tbl[i].name; i++) { if ((flags & tbl[i].mask) == tbl[i].bits) { if (!first) (void) strlcat(buf, ",", bufsize); (void) strlcat(buf, tbl[i].name, bufsize); first = _B_FALSE; } } } } /* * return true if the address for lifname comes to us from the global zone * with 'allowed-ips' constraints. */ static boolean_t is_from_gz(const char *lifname) { ipadm_if_info_t *if_info; char phyname[LIFNAMSIZ], *cp; boolean_t ret = _B_FALSE; ipadm_status_t status; zoneid_t zoneid; ushort_t zflags; if ((zoneid = getzoneid()) == GLOBAL_ZONEID) return (_B_FALSE); /* from-gz only makes sense in a NGZ */ if (zone_getattr(zoneid, ZONE_ATTR_FLAGS, &zflags, sizeof (zflags)) < 0) return (_B_FALSE); if (!(zflags & ZF_NET_EXCL)) return (_B_TRUE); /* everything is from the GZ for shared-ip */ (void) strncpy(phyname, lifname, sizeof (phyname)); if ((cp = strchr(phyname, ':')) != NULL) *cp = '\0'; status = ipadm_if_info(iph, phyname, &if_info, 0, LIFC_DEFAULT); if (status != IPADM_SUCCESS) return (ret); if (if_info->ifi_cflags & IFIF_L3PROTECT) ret = _B_TRUE; ipadm_free_if_info(if_info); return (ret); } static boolean_t print_sa_cb(ofmt_arg_t *ofarg, char *buf, uint_t bufsize) { show_addr_args_t *arg = ofarg->ofmt_cbarg; ipadm_addr_info_t *ainfo = arg->sa_info; char interface[LIFNAMSIZ]; char addrbuf[MAXPROPVALLEN]; char dstbuf[MAXPROPVALLEN]; char prefixlenstr[MAXPROPVALLEN]; int prefixlen; struct sockaddr_in *sin; struct sockaddr_in6 *sin6; sa_family_t af; char *phyname = NULL; struct ifaddrs *ifa = &ainfo->ia_ifa; fmask_t cflags_mask[] = { { "U", IA_UP, IA_UP }, { "u", IA_UNNUMBERED, IA_UNNUMBERED }, { "p", IA_PRIVATE, IA_PRIVATE }, { "t", IA_TEMPORARY, IA_TEMPORARY }, { "d", IA_DEPRECATED, IA_DEPRECATED }, { NULL, 0, 0 } }; fmask_t pflags_mask[] = { { "U", IA_UP, IA_UP }, { "p", IA_PRIVATE, IA_PRIVATE }, { "d", IA_DEPRECATED, IA_DEPRECATED }, { NULL, 0, 0 } }; fmask_t type[] = { { "static", IPADM_ADDR_STATIC, IPADM_ALL_BITS}, { "addrconf", IPADM_ADDR_IPV6_ADDRCONF, IPADM_ALL_BITS}, { "dhcp", IPADM_ADDR_DHCP, IPADM_ALL_BITS}, { NULL, 0, 0 } }; fmask_t addr_state[] = { { "disabled", IFA_DISABLED, IPADM_ALL_BITS}, { "duplicate", IFA_DUPLICATE, IPADM_ALL_BITS}, { "down", IFA_DOWN, IPADM_ALL_BITS}, { "tentative", IFA_TENTATIVE, IPADM_ALL_BITS}, { "ok", IFA_OK, IPADM_ALL_BITS}, { "inaccessible", IFA_INACCESSIBLE, IPADM_ALL_BITS}, { NULL, 0, 0 } }; buf[0] = '\0'; switch (ofarg->ofmt_id) { case SA_ADDROBJ: if (ainfo->ia_aobjname[0] == '\0') { (void) strncpy(interface, ifa->ifa_name, LIFNAMSIZ); phyname = strrchr(interface, ':'); if (phyname) *phyname = '\0'; (void) snprintf(buf, bufsize, "%s/%s", interface, STR_UNKNOWN_VAL); } else { (void) snprintf(buf, bufsize, "%s", ainfo->ia_aobjname); } break; case SA_STATE: flags2str(ainfo->ia_state, addr_state, _B_FALSE, buf, bufsize); break; case SA_TYPE: if (is_from_gz(ifa->ifa_name)) (void) snprintf(buf, bufsize, "from-gz"); else flags2str(ainfo->ia_atype, type, _B_FALSE, buf, bufsize); break; case SA_CURRENT: flags2str(ainfo->ia_cflags, cflags_mask, _B_TRUE, buf, bufsize); break; case SA_PERSISTENT: flags2str(ainfo->ia_pflags, pflags_mask, _B_TRUE, buf, bufsize); break; case SA_ADDR: af = ifa->ifa_addr->sa_family; /* * If the address is 0.0.0.0 or :: and the origin is DHCP, * print STR_UNKNOWN_VAL. */ if (ainfo->ia_atype == IPADM_ADDR_DHCP) { sin = (struct sockaddr_in *)ifa->ifa_addr; sin6 = (struct sockaddr_in6 *)ifa->ifa_addr; if ((af == AF_INET && sin->sin_addr.s_addr == INADDR_ANY) || (af == AF_INET6 && IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr))) { (void) snprintf(buf, bufsize, STR_UNKNOWN_VAL); break; } } if (ifa->ifa_netmask == NULL) prefixlen = 0; else prefixlen = mask2plen(ifa->ifa_netmask); bzero(prefixlenstr, sizeof (prefixlenstr)); if (prefixlen > 0) { (void) snprintf(prefixlenstr, sizeof (prefixlenstr), "/%d", prefixlen); } bzero(addrbuf, sizeof (addrbuf)); bzero(dstbuf, sizeof (dstbuf)); if (ainfo->ia_atype == IPADM_ADDR_STATIC) { /* * Print the hostname fields if the address is not * in active configuration. */ if (ainfo->ia_state == IFA_DISABLED) { (void) snprintf(buf, bufsize, "%s", ainfo->ia_sname); if (ainfo->ia_dname[0] != '\0') { (void) snprintf(dstbuf, sizeof (dstbuf), "->%s", ainfo->ia_dname); (void) strlcat(buf, dstbuf, bufsize); } else { (void) strlcat(buf, prefixlenstr, bufsize); } break; } } /* * For the non-persistent case, we need to show the * currently configured addresses for source and * destination. */ sockaddr2str((struct sockaddr_storage *)ifa->ifa_addr, addrbuf, sizeof (addrbuf)); if (ifa->ifa_flags & IFF_POINTOPOINT) { sockaddr2str( (struct sockaddr_storage *)ifa->ifa_dstaddr, dstbuf, sizeof (dstbuf)); (void) snprintf(buf, bufsize, "%s->%s", addrbuf, dstbuf); } else { (void) snprintf(buf, bufsize, "%s%s", addrbuf, prefixlenstr); } break; default: die("invalid input"); break; } return (_B_TRUE); } /* * Display address information, either for the given address or * for all the addresses managed by ipadm. */ static void do_show_addr(int argc, char *argv[], const char *use) { ipadm_status_t status; show_addr_state_t state; char *def_fields_str = "addrobj,type,state,addr"; char *fields_str = NULL; ipadm_addr_info_t *ainfo; ipadm_addr_info_t *ptr; show_addr_args_t sargs; int option; ofmt_handle_t ofmt; ofmt_status_t oferr; uint_t ofmtflags = 0; char *aname; char *ifname = NULL; char *cp; boolean_t found = _B_FALSE; opterr = 0; state.sa_parsable = _B_FALSE; state.sa_persist = _B_FALSE; while ((option = getopt_long(argc, argv, "po:", show_addr_longopts, NULL)) != -1) { switch (option) { case 'p': state.sa_parsable = _B_TRUE; break; case 'o': fields_str = optarg; break; default: die_opterr(optopt, option, use); break; } } if (state.sa_parsable && fields_str == NULL) die("-p requires -o"); if (optind == argc - 1) { aname = argv[optind]; if ((cp = strchr(aname, '/')) == NULL) die("Invalid address object name provided"); if (*(cp + 1) == '\0') { ifname = aname; *cp = '\0'; aname = NULL; } } else if (optind == argc) { aname = NULL; } else { die("Usage: %s", use); } if (state.sa_parsable) ofmtflags |= OFMT_PARSABLE; if (fields_str == NULL) fields_str = def_fields_str; oferr = ofmt_open(fields_str, show_addr_fields, ofmtflags, 0, &ofmt); ofmt_check(oferr, state.sa_parsable, ofmt, die, warn); state.sa_ofmt = ofmt; status = ipadm_addr_info(iph, ifname, &ainfo, 0, LIFC_DEFAULT); /* * Return without printing any error, if no addresses were found, * for the case where all addresses are requested. */ if (status != IPADM_SUCCESS) die("Could not get address: %s", ipadm_status2str(status)); if (ainfo == NULL) { ofmt_close(ofmt); return; } bzero(&sargs, sizeof (sargs)); sargs.sa_state = &state; for (ptr = ainfo; ptr != NULL; ptr = IA_NEXT(ptr)) { sargs.sa_info = ptr; if (aname != NULL) { if (strcmp(sargs.sa_info->ia_aobjname, aname) != 0) continue; found = _B_TRUE; } ofmt_print(state.sa_ofmt, &sargs); } if (ainfo) ipadm_free_addr_info(ainfo); if (aname != NULL && !found) die("Address object not found"); } static boolean_t print_si_cb(ofmt_arg_t *ofarg, char *buf, uint_t bufsize) { show_if_args_t *arg = ofarg->ofmt_cbarg; ipadm_if_info_t *ifinfo = arg->si_info; char *ifname = ifinfo->ifi_name; fmask_t intf_state[] = { { "ok", IFIS_OK, IPADM_ALL_BITS}, { "down", IFIS_DOWN, IPADM_ALL_BITS}, { "disabled", IFIS_DISABLED, IPADM_ALL_BITS}, { "failed", IFIS_FAILED, IPADM_ALL_BITS}, { "offline", IFIS_OFFLINE, IPADM_ALL_BITS}, { NULL, 0, 0 } }; fmask_t intf_pflags[] = { { "s", IFIF_STANDBY, IFIF_STANDBY }, { "4", IFIF_IPV4, IFIF_IPV4 }, { "6", IFIF_IPV6, IFIF_IPV6 }, { NULL, 0, 0 } }; fmask_t intf_cflags[] = { { "b", IFIF_BROADCAST, IFIF_BROADCAST }, { "m", IFIF_MULTICAST, IFIF_MULTICAST }, { "p", IFIF_POINTOPOINT, IFIF_POINTOPOINT}, { "v", IFIF_VIRTUAL, IFIF_VIRTUAL }, { "I", IFIF_IPMP, IFIF_IPMP }, { "s", IFIF_STANDBY, IFIF_STANDBY }, { "i", IFIF_INACTIVE, IFIF_INACTIVE }, { "V", IFIF_VRRP, IFIF_VRRP }, { "a", IFIF_NOACCEPT, IFIF_NOACCEPT }, { "Z", IFIF_L3PROTECT, IFIF_L3PROTECT }, { "4", IFIF_IPV4, IFIF_IPV4 }, { "6", IFIF_IPV6, IFIF_IPV6 }, { NULL, 0, 0 } }; fmask_t intf_class[] = { { "IP", IPADM_IF_CLASS_REGULAR, IPADM_ALL_BITS}, { "IPMP", IPADM_IF_CLASS_IPMP, IPADM_ALL_BITS}, { "VIRTUAL", IPADM_IF_CLASS_VIRTUAL, IPADM_ALL_BITS}, { "UNKNOWN", IPADM_IF_CLASS_UNKNOWN, IPADM_ALL_BITS}, { NULL, 0, 0} }; buf[0] = '\0'; switch (ofarg->ofmt_id) { case SI_IFNAME: (void) snprintf(buf, bufsize, "%s", ifname); break; case SI_IFCLASS: flags2str(ifinfo->ifi_class, intf_class, _B_FALSE, buf, bufsize); break; case SI_STATE: flags2str(ifinfo->ifi_state, intf_state, _B_FALSE, buf, bufsize); break; case SI_CURRENT: flags2str(ifinfo->ifi_cflags, intf_cflags, _B_TRUE, buf, bufsize); break; case SI_PERSISTENT: flags2str(ifinfo->ifi_pflags, intf_pflags, _B_TRUE, buf, bufsize); break; default: die("invalid input"); break; } return (_B_TRUE); } /* * Display interface information, either for the given interface or * for all the interfaces in the system. */ static void do_show_if(int argc, char *argv[], const char *use) { ipadm_status_t status; show_if_state_t state; char *fields_str = NULL; ipadm_if_info_t *if_info, *ptr; show_if_args_t sargs; int option; ofmt_handle_t ofmt; ofmt_status_t oferr; uint_t ofmtflags = 0; char *ifname = NULL; opterr = 0; state.si_parsable = _B_FALSE; while ((option = getopt_long(argc, argv, "po:", show_if_longopts, NULL)) != -1) { switch (option) { case 'p': state.si_parsable = _B_TRUE; break; case 'o': fields_str = optarg; break; default: die_opterr(optopt, option, use); break; } } if (optind == argc - 1) ifname = argv[optind]; else if (optind != argc) die("Usage: %s", use); if (state.si_parsable) ofmtflags |= OFMT_PARSABLE; oferr = ofmt_open(fields_str, show_if_fields, ofmtflags, 0, &ofmt); ofmt_check(oferr, state.si_parsable, ofmt, die, warn); state.si_ofmt = ofmt; bzero(&sargs, sizeof (sargs)); sargs.si_state = &state; status = ipadm_if_info(iph, ifname, &if_info, 0, LIFC_DEFAULT); /* * Return without printing any error, if no addresses were found. */ if (status != IPADM_SUCCESS) { die("Could not get interface(s): %s", ipadm_status2str(status)); } for (ptr = if_info; ptr != NULL; ptr = ptr->ifi_next) { sargs.si_info = ptr; ofmt_print(state.si_ofmt, &sargs); } if (if_info) ipadm_free_if_info(if_info); } /* * set/reset the address property for a given address */ static void set_addrprop(int argc, char **argv, boolean_t reset, const char *use) { int option; ipadm_status_t status = IPADM_SUCCESS; boolean_t p_arg = _B_FALSE; char *nv, *aobjname; char *prop_name, *prop_val; uint_t flags = IPADM_OPT_ACTIVE|IPADM_OPT_PERSIST; nv = NULL; opterr = 0; while ((option = getopt_long(argc, argv, ":i:p:t", set_ifprop_longopts, NULL)) != -1) { switch (option) { case 'p': if (p_arg) die("-p must be specified once only"); p_arg = _B_TRUE; ipadm_check_propstr(optarg, reset, use); nv = optarg; break; case 't': flags &= ~IPADM_OPT_PERSIST; break; default: die_opterr(optopt, option, use); } } if (!p_arg || optind != (argc - 1)) die("Usage: %s", use); prop_name = nv; prop_val = strchr(nv, '='); if (prop_val != NULL) *prop_val++ = '\0'; aobjname = argv[optind]; if (reset) flags |= IPADM_OPT_DEFAULT; status = ipadm_set_addrprop(iph, prop_name, prop_val, aobjname, flags); if (status != IPADM_SUCCESS) { if (reset) die("reset-addrprop: %s: %s", prop_name, ipadm_status2str(status)); else die("set-addrprop: %s: %s", prop_name, ipadm_status2str(status)); } } /* * Sets a property on an address object. */ static void do_set_addrprop(int argc, char **argv, const char *use) { set_addrprop(argc, argv, _B_FALSE, use); } /* * Resets a property to its default value on an address object. */ static void do_reset_addrprop(int argc, char **argv, const char *use) { set_addrprop(argc, argv, _B_TRUE, use); } /* * Display information for all or specific address properties, either for a * given address or for all the addresses in the system. */ static void do_show_addrprop(int argc, char *argv[], const char *use) { int option; nvlist_t *proplist = NULL; char *fields_str = NULL; show_prop_state_t state; ofmt_handle_t ofmt; ofmt_status_t oferr; uint_t ofmtflags = 0; char *aobjname = NULL; char *ifname = NULL; char *cp; ipadm_addr_info_t *ainfop = NULL; ipadm_addr_info_t *ptr; ipadm_status_t status; boolean_t found = _B_FALSE; opterr = 0; bzero(&state, sizeof (state)); state.sps_propval = NULL; state.sps_parsable = _B_FALSE; state.sps_addrprop = _B_TRUE; state.sps_proto = MOD_PROTO_NONE; state.sps_status = state.sps_retstatus = IPADM_SUCCESS; while ((option = getopt_long(argc, argv, ":p:i:cPo:", show_prop_longopts, NULL)) != -1) { switch (option) { case 'p': if (ipadm_str2nvlist(optarg, &proplist, IPADM_NORVAL) != 0) die("invalid addrobj properties specified"); break; case 'c': state.sps_parsable = _B_TRUE; break; case 'o': fields_str = optarg; break; default: die_opterr(optopt, option, use); break; } } if (optind == argc - 1) { aobjname = argv[optind]; cp = strchr(aobjname, '/'); if (cp == NULL) die("invalid addrobj name provided"); if (*(cp + 1) == '\0') { ifname = aobjname; *cp = '\0'; aobjname = NULL; } } else if (optind != argc) { die("Usage: %s", use); } state.sps_proplist = proplist; if (state.sps_parsable) ofmtflags |= OFMT_PARSABLE; oferr = ofmt_open(fields_str, addrprop_fields, ofmtflags, 0, &ofmt); ofmt_check(oferr, state.sps_parsable, ofmt, die, warn); state.sps_ofmt = ofmt; status = ipadm_addr_info(iph, ifname, &ainfop, 0, LIFC_DEFAULT); /* Return without printing any error, if no addresses were found */ if (status == IPADM_NOTFOUND) return; if (status != IPADM_SUCCESS) die("error retrieving address: %s", ipadm_status2str(status)); for (ptr = ainfop; ptr != NULL; ptr = IA_NEXT(ptr)) { char *taobjname = ptr->ia_aobjname; if (taobjname[0] == '\0') continue; if (aobjname != NULL) { if (strcmp(aobjname, taobjname) == 0) found = _B_TRUE; else continue; } if (ptr->ia_atype == IPADM_ADDR_IPV6_ADDRCONF) { if (found) break; else continue; } (void) strlcpy(state.sps_aobjname, taobjname, sizeof (state.sps_aobjname)); show_properties(&state, IPADMPROP_CLASS_ADDR); if (found) break; } ipadm_free_addr_info(ainfop); if (aobjname != NULL && !found) die("addrobj not found: %s", aobjname); nvlist_free(proplist); ofmt_close(ofmt); if (state.sps_retstatus != IPADM_SUCCESS) { ipadm_close(iph); exit(EXIT_FAILURE); } } /* * check if the `pstr' adheres to following syntax * - prop= (for set) * - prop (for reset) */ static void ipadm_check_propstr(const char *pstr, boolean_t reset, const char *use) { char *nv; nv = strchr(pstr, '='); if (reset) { if (nv != NULL) die("incorrect syntax used for -p.\n%s", use); } else { if (nv == NULL || *++nv == '\0') die("please specify the value to be set.\n%s", use); nv = strchr(nv, '='); /* cannot have multiple 'prop=val' for single -p */ if (nv != NULL) die("cannot specify more than one prop=val at " "a time.\n%s", use); } }