/* * 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) 2013, 2017 by Delphix. All rights reserved. * Copyright 2021 Tintri by DDN, Inc. All rights reserved. */ /* * This file contains routines that are used to modify/retrieve protocol or * interface property values. It also holds all the supported properties for * both IP interface and protocols in `ipadm_prop_desc_t'. Following protocols * are supported: IP, IPv4, IPv6, TCP, SCTP, UDP and ICMP. * * This file also contains walkers, which walks through the property table and * calls the callback function, of the form `ipadm_prop_wfunc_t' , for every * property in the table. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "libipadm_impl.h" #include #define IPADM_NONESTR "none" #define DEF_METRIC_VAL 0 /* default metric value */ #define A_CNT(arr) (sizeof (arr) / sizeof (arr[0])) static ipadm_status_t i_ipadm_validate_if(ipadm_handle_t, const char *, uint_t, uint_t); /* * Callback functions to retrieve property values from the kernel. These * functions, when required, translate the values from the kernel to a format * suitable for printing. For example: boolean values will be translated * to on/off. They also retrieve DEFAULT, PERM and POSSIBLE values for * a given property. */ static ipadm_pd_getf_t i_ipadm_get_prop, i_ipadm_get_ifprop_flags, i_ipadm_get_mtu, i_ipadm_get_metric, i_ipadm_get_usesrc, i_ipadm_get_forwarding, i_ipadm_get_ecnsack, i_ipadm_get_hostmodel; /* * Callback function to set property values. These functions translate the * values to a format suitable for kernel consumption, allocates the necessary * ioctl buffers and then invokes ioctl(). */ static ipadm_pd_setf_t i_ipadm_set_prop, i_ipadm_set_mtu, i_ipadm_set_ifprop_flags, i_ipadm_set_metric, i_ipadm_set_usesrc, i_ipadm_set_forwarding, i_ipadm_set_eprivport, i_ipadm_set_ecnsack, i_ipadm_set_hostmodel; /* array of protocols we support */ static int protocols[] = { MOD_PROTO_IP, MOD_PROTO_RAWIP, MOD_PROTO_TCP, MOD_PROTO_UDP, MOD_PROTO_SCTP }; /* * Supported IP protocol properties. */ static ipadm_prop_desc_t ipadm_ip_prop_table[] = { { "arp", NULL, IPADMPROP_CLASS_IF, MOD_PROTO_IPV4, 0, i_ipadm_set_ifprop_flags, i_ipadm_get_onoff, i_ipadm_get_ifprop_flags }, { "forwarding", NULL, IPADMPROP_CLASS_MODIF, MOD_PROTO_IPV4, 0, i_ipadm_set_forwarding, i_ipadm_get_onoff, i_ipadm_get_forwarding }, { "metric", NULL, IPADMPROP_CLASS_IF, MOD_PROTO_IPV4, 0, i_ipadm_set_metric, NULL, i_ipadm_get_metric }, { "mtu", NULL, IPADMPROP_CLASS_IF, MOD_PROTO_IPV4, 0, i_ipadm_set_mtu, i_ipadm_get_mtu, i_ipadm_get_mtu }, { "exchange_routes", NULL, IPADMPROP_CLASS_IF, MOD_PROTO_IPV4, 0, i_ipadm_set_ifprop_flags, i_ipadm_get_onoff, i_ipadm_get_ifprop_flags }, { "usesrc", NULL, IPADMPROP_CLASS_IF, MOD_PROTO_IPV4, 0, i_ipadm_set_usesrc, NULL, i_ipadm_get_usesrc }, { "ttl", NULL, IPADMPROP_CLASS_MODULE, MOD_PROTO_IPV4, 0, i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop }, { "forwarding", NULL, IPADMPROP_CLASS_MODIF, MOD_PROTO_IPV6, 0, i_ipadm_set_forwarding, i_ipadm_get_onoff, i_ipadm_get_forwarding }, { "hoplimit", NULL, IPADMPROP_CLASS_MODULE, MOD_PROTO_IPV6, 0, i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop }, { "metric", NULL, IPADMPROP_CLASS_IF, MOD_PROTO_IPV6, 0, i_ipadm_set_metric, NULL, i_ipadm_get_metric }, { "mtu", NULL, IPADMPROP_CLASS_IF, MOD_PROTO_IPV6, 0, i_ipadm_set_mtu, i_ipadm_get_mtu, i_ipadm_get_mtu }, { "nud", NULL, IPADMPROP_CLASS_IF, MOD_PROTO_IPV6, 0, i_ipadm_set_ifprop_flags, i_ipadm_get_onoff, i_ipadm_get_ifprop_flags }, { "exchange_routes", NULL, IPADMPROP_CLASS_IF, MOD_PROTO_IPV6, 0, i_ipadm_set_ifprop_flags, i_ipadm_get_onoff, i_ipadm_get_ifprop_flags }, { "usesrc", NULL, IPADMPROP_CLASS_IF, MOD_PROTO_IPV6, 0, i_ipadm_set_usesrc, NULL, i_ipadm_get_usesrc }, { "hostmodel", NULL, IPADMPROP_CLASS_MODULE, MOD_PROTO_IPV6, 0, i_ipadm_set_hostmodel, i_ipadm_get_hostmodel, i_ipadm_get_hostmodel }, { "hostmodel", NULL, IPADMPROP_CLASS_MODULE, MOD_PROTO_IPV4, 0, i_ipadm_set_hostmodel, i_ipadm_get_hostmodel, i_ipadm_get_hostmodel }, { "standby", NULL, IPADMPROP_CLASS_IF, MOD_PROTO_IP, 0, i_ipadm_set_ifprop_flags, i_ipadm_get_onoff, i_ipadm_get_ifprop_flags }, { NULL, NULL, 0, 0, 0, NULL, NULL, NULL } }; /* possible values for TCP properties `ecn' and `sack' */ static const char *ecn_sack_vals[] = {"never", "passive", "active", NULL}; /* Supported TCP protocol properties */ static ipadm_prop_desc_t ipadm_tcp_prop_table[] = { { "congestion_control", NULL, IPADMPROP_CLASS_MODULE, MOD_PROTO_TCP, 0, i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop }, { "ecn", NULL, IPADMPROP_CLASS_MODULE, MOD_PROTO_TCP, 0, i_ipadm_set_ecnsack, i_ipadm_get_ecnsack, i_ipadm_get_ecnsack }, { "extra_priv_ports", NULL, IPADMPROP_CLASS_MODULE, MOD_PROTO_TCP, IPADMPROP_MULVAL, i_ipadm_set_eprivport, i_ipadm_get_prop, i_ipadm_get_prop }, { "largest_anon_port", NULL, IPADMPROP_CLASS_MODULE, MOD_PROTO_TCP, 0, i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop }, { "max_buf", "_max_buf", IPADMPROP_CLASS_MODULE, MOD_PROTO_TCP, 0, i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop }, { "recv_buf", "recv_maxbuf", IPADMPROP_CLASS_MODULE, MOD_PROTO_TCP, 0, i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop }, { "sack", NULL, IPADMPROP_CLASS_MODULE, MOD_PROTO_TCP, 0, i_ipadm_set_ecnsack, i_ipadm_get_ecnsack, i_ipadm_get_ecnsack }, { "send_buf", "send_maxbuf", IPADMPROP_CLASS_MODULE, MOD_PROTO_TCP, 0, i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop }, { "smallest_anon_port", NULL, IPADMPROP_CLASS_MODULE, MOD_PROTO_TCP, 0, i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop }, { "smallest_nonpriv_port", NULL, IPADMPROP_CLASS_MODULE, MOD_PROTO_TCP, 0, i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop }, { NULL, NULL, 0, 0, 0, NULL, NULL, NULL } }; /* Supported UDP protocol properties */ static ipadm_prop_desc_t ipadm_udp_prop_table[] = { { "extra_priv_ports", NULL, IPADMPROP_CLASS_MODULE, MOD_PROTO_UDP, IPADMPROP_MULVAL, i_ipadm_set_eprivport, i_ipadm_get_prop, i_ipadm_get_prop }, { "largest_anon_port", NULL, IPADMPROP_CLASS_MODULE, MOD_PROTO_UDP, 0, i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop }, { "max_buf", "_max_buf", IPADMPROP_CLASS_MODULE, MOD_PROTO_UDP, 0, i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop }, { "recv_buf", "recv_maxbuf", IPADMPROP_CLASS_MODULE, MOD_PROTO_UDP, 0, i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop }, { "send_buf", "send_maxbuf", IPADMPROP_CLASS_MODULE, MOD_PROTO_UDP, 0, i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop }, { "smallest_anon_port", NULL, IPADMPROP_CLASS_MODULE, MOD_PROTO_UDP, 0, i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop }, { "smallest_nonpriv_port", NULL, IPADMPROP_CLASS_MODULE, MOD_PROTO_UDP, 0, i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop }, { NULL, NULL, 0, 0, 0, NULL, NULL, NULL } }; /* Supported SCTP protocol properties */ static ipadm_prop_desc_t ipadm_sctp_prop_table[] = { { "extra_priv_ports", NULL, IPADMPROP_CLASS_MODULE, MOD_PROTO_SCTP, IPADMPROP_MULVAL, i_ipadm_set_eprivport, i_ipadm_get_prop, i_ipadm_get_prop }, { "largest_anon_port", NULL, IPADMPROP_CLASS_MODULE, MOD_PROTO_SCTP, 0, i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop }, { "max_buf", "_max_buf", IPADMPROP_CLASS_MODULE, MOD_PROTO_SCTP, 0, i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop }, { "recv_buf", "recv_maxbuf", IPADMPROP_CLASS_MODULE, MOD_PROTO_SCTP, 0, i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop }, { "send_buf", "send_maxbuf", IPADMPROP_CLASS_MODULE, MOD_PROTO_SCTP, 0, i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop }, { "smallest_anon_port", NULL, IPADMPROP_CLASS_MODULE, MOD_PROTO_SCTP, 0, i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop }, { "smallest_nonpriv_port", NULL, IPADMPROP_CLASS_MODULE, MOD_PROTO_SCTP, 0, i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop }, { NULL, NULL, 0, 0, 0, NULL, NULL, NULL } }; /* Supported ICMP protocol properties */ static ipadm_prop_desc_t ipadm_icmp_prop_table[] = { { "max_buf", "_max_buf", IPADMPROP_CLASS_MODULE, MOD_PROTO_RAWIP, 0, i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop }, { "recv_buf", "recv_maxbuf", IPADMPROP_CLASS_MODULE, MOD_PROTO_RAWIP, 0, i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop }, { "send_buf", "send_maxbuf", IPADMPROP_CLASS_MODULE, MOD_PROTO_RAWIP, 0, i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop }, { NULL, NULL, 0, 0, 0, NULL, NULL, NULL } }; /* * A dummy private property structure, used while handling private * protocol properties (properties not yet supported by libipadm). */ static const ipadm_prop_desc_t ipadm_privprop_template = { NULL, NULL, IPADMPROP_CLASS_MODULE, MOD_PROTO_NONE, 0, i_ipadm_set_prop, i_ipadm_get_prop, i_ipadm_get_prop }; /* * Returns the property description table, for the given protocol */ static ipadm_prop_desc_t * i_ipadm_get_propdesc_table(uint_t proto) { switch (proto) { case MOD_PROTO_IP: case MOD_PROTO_IPV4: case MOD_PROTO_IPV6: return (ipadm_ip_prop_table); case MOD_PROTO_RAWIP: return (ipadm_icmp_prop_table); case MOD_PROTO_TCP: return (ipadm_tcp_prop_table); case MOD_PROTO_UDP: return (ipadm_udp_prop_table); case MOD_PROTO_SCTP: return (ipadm_sctp_prop_table); } return (NULL); } static ipadm_prop_desc_t * i_ipadm_get_prop_desc(const char *pname, uint_t proto, int *errp) { int err = 0; boolean_t matched_name = B_FALSE; ipadm_prop_desc_t *ipdp = NULL, *ipdtbl; if ((ipdtbl = i_ipadm_get_propdesc_table(proto)) == NULL) { err = EINVAL; goto ret; } for (ipdp = ipdtbl; ipdp->ipd_name != NULL; ipdp++) { if (strcmp(pname, ipdp->ipd_name) == 0 || (ipdp->ipd_old_name != NULL && strcmp(pname, ipdp->ipd_old_name) == 0)) { matched_name = B_TRUE; if (ipdp->ipd_proto == proto) break; } } if (ipdp->ipd_name == NULL) { err = ENOENT; /* if we matched name, but failed protocol check */ if (matched_name) err = EPROTO; ipdp = NULL; } ret: if (errp != NULL) *errp = err; return (ipdp); } char * ipadm_proto2str(uint_t proto) { switch (proto) { case MOD_PROTO_IP: return ("ip"); case MOD_PROTO_IPV4: return ("ipv4"); case MOD_PROTO_IPV6: return ("ipv6"); case MOD_PROTO_RAWIP: return ("icmp"); case MOD_PROTO_TCP: return ("tcp"); case MOD_PROTO_UDP: return ("udp"); case MOD_PROTO_SCTP: return ("sctp"); } return (NULL); } uint_t ipadm_str2proto(const char *protostr) { if (protostr == NULL) return (MOD_PROTO_NONE); if (strcmp(protostr, "tcp") == 0) return (MOD_PROTO_TCP); else if (strcmp(protostr, "udp") == 0) return (MOD_PROTO_UDP); else if (strcmp(protostr, "ip") == 0) return (MOD_PROTO_IP); else if (strcmp(protostr, "ipv4") == 0) return (MOD_PROTO_IPV4); else if (strcmp(protostr, "ipv6") == 0) return (MOD_PROTO_IPV6); else if (strcmp(protostr, "icmp") == 0) return (MOD_PROTO_RAWIP); else if (strcmp(protostr, "sctp") == 0) return (MOD_PROTO_SCTP); else if (strcmp(protostr, "arp") == 0) return (MOD_PROTO_IP); return (MOD_PROTO_NONE); } /* ARGSUSED */ static ipadm_status_t i_ipadm_set_mtu(ipadm_handle_t iph, const void *arg, ipadm_prop_desc_t *pdp, const void *pval, uint_t proto, uint_t flags) { struct lifreq lifr; char *endp; uint_t mtu; int s; const char *ifname = arg; char val[MAXPROPVALLEN]; /* to reset MTU first retrieve the default MTU and then set it */ if (flags & IPADM_OPT_DEFAULT) { ipadm_status_t status; uint_t size = MAXPROPVALLEN; status = i_ipadm_get_prop(iph, arg, pdp, val, &size, proto, MOD_PROP_DEFAULT); if (status != IPADM_SUCCESS) return (status); pval = val; } errno = 0; mtu = (uint_t)strtol(pval, &endp, 10); if (errno != 0 || *endp != '\0') return (IPADM_INVALID_ARG); bzero(&lifr, sizeof (lifr)); (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name)); lifr.lifr_mtu = mtu; s = (proto == MOD_PROTO_IPV6 ? iph->iph_sock6 : iph->iph_sock); if (ioctl(s, SIOCSLIFMTU, (caddr_t)&lifr) < 0) return (ipadm_errno2status(errno)); return (IPADM_SUCCESS); } /* ARGSUSED */ static ipadm_status_t i_ipadm_set_metric(ipadm_handle_t iph, const void *arg, ipadm_prop_desc_t *pdp, const void *pval, uint_t proto, uint_t flags) { struct lifreq lifr; char *endp; int metric; const char *ifname = arg; int s; /* if we are resetting, set the value to its default value */ if (flags & IPADM_OPT_DEFAULT) { metric = DEF_METRIC_VAL; } else { errno = 0; metric = (uint_t)strtol(pval, &endp, 10); if (errno != 0 || *endp != '\0') return (IPADM_INVALID_ARG); } bzero(&lifr, sizeof (lifr)); (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name)); lifr.lifr_metric = metric; s = (proto == MOD_PROTO_IPV6 ? iph->iph_sock6 : iph->iph_sock); if (ioctl(s, SIOCSLIFMETRIC, (caddr_t)&lifr) < 0) return (ipadm_errno2status(errno)); return (IPADM_SUCCESS); } /* ARGSUSED */ static ipadm_status_t i_ipadm_set_usesrc(ipadm_handle_t iph, const void *arg, ipadm_prop_desc_t *pdp, const void *pval, uint_t proto, uint_t flags) { struct lifreq lifr; const char *ifname = arg; int s; uint_t ifindex = 0; /* if we are resetting, set the value to its default value */ if (flags & IPADM_OPT_DEFAULT) pval = IPADM_NONESTR; /* * cannot specify logical interface name. We can also filter out other * bogus interface names here itself through i_ipadm_validate_ifname(). */ if (strcmp(pval, IPADM_NONESTR) != 0 && !i_ipadm_validate_ifname(iph, pval)) return (IPADM_INVALID_ARG); bzero(&lifr, sizeof (lifr)); (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name)); s = (proto == MOD_PROTO_IPV6 ? iph->iph_sock6 : iph->iph_sock); if (strcmp(pval, IPADM_NONESTR) != 0) { if ((ifindex = if_nametoindex(pval)) == 0) return (ipadm_errno2status(errno)); lifr.lifr_index = ifindex; } else { if (ioctl(s, SIOCGLIFUSESRC, (caddr_t)&lifr) < 0) return (ipadm_errno2status(errno)); lifr.lifr_index = 0; } if (ioctl(s, SIOCSLIFUSESRC, (caddr_t)&lifr) < 0) return (ipadm_errno2status(errno)); return (IPADM_SUCCESS); } static struct hostmodel_strval { char *esm_str; ip_hostmodel_t esm_val; } esm_arr[] = { {"weak", IP_WEAK_ES}, {"src-priority", IP_SRC_PRI_ES}, {"strong", IP_STRONG_ES}, {"custom", IP_MAXVAL_ES} }; static ip_hostmodel_t i_ipadm_hostmodel_str2val(const char *pval) { int i; for (i = 0; i < A_CNT(esm_arr); i++) { if (esm_arr[i].esm_str != NULL && strcmp(pval, esm_arr[i].esm_str) == 0) { return (esm_arr[i].esm_val); } } return (IP_MAXVAL_ES); } static char * i_ipadm_hostmodel_val2str(ip_hostmodel_t pval) { int i; for (i = 0; i < A_CNT(esm_arr); i++) { if (esm_arr[i].esm_val == pval) return (esm_arr[i].esm_str); } return (NULL); } /* ARGSUSED */ static ipadm_status_t i_ipadm_set_hostmodel(ipadm_handle_t iph, const void *arg, ipadm_prop_desc_t *pdp, const void *pval, uint_t proto, uint_t flags) { ip_hostmodel_t hostmodel; char val[11]; /* covers uint32_max as a string */ if ((flags & IPADM_OPT_DEFAULT) == 0) { hostmodel = i_ipadm_hostmodel_str2val(pval); if (hostmodel == IP_MAXVAL_ES) return (IPADM_INVALID_ARG); (void) snprintf(val, sizeof (val), "%d", hostmodel); pval = val; } return (i_ipadm_set_prop(iph, NULL, pdp, pval, proto, flags)); } /* ARGSUSED */ static ipadm_status_t i_ipadm_get_hostmodel(ipadm_handle_t iph, const void *arg, ipadm_prop_desc_t *pdp, char *buf, uint_t *bufsize, uint_t proto, uint_t valtype) { ip_hostmodel_t hostmodel; char *cp; size_t nbytes; ipadm_status_t status; switch (valtype) { case MOD_PROP_PERM: nbytes = snprintf(buf, *bufsize, "%d", MOD_PROP_PERM_RW); break; case MOD_PROP_DEFAULT: nbytes = snprintf(buf, *bufsize, "weak"); break; case MOD_PROP_ACTIVE: status = i_ipadm_get_prop(iph, arg, pdp, buf, bufsize, proto, valtype); if (status != IPADM_SUCCESS) return (status); bcopy(buf, &hostmodel, sizeof (hostmodel)); cp = i_ipadm_hostmodel_val2str(hostmodel); nbytes = snprintf(buf, *bufsize, "%s", (cp != NULL ? cp : "?")); break; case MOD_PROP_POSSIBLE: nbytes = snprintf(buf, *bufsize, "strong,src-priority,weak"); break; default: return (IPADM_INVALID_ARG); } if (nbytes >= *bufsize) { /* insufficient buffer space */ *bufsize = nbytes + 1; return (IPADM_NO_BUFS); } return (IPADM_SUCCESS); } /* ARGSUSED */ static ipadm_status_t i_ipadm_set_ifprop_flags(ipadm_handle_t iph, const void *arg, ipadm_prop_desc_t *pdp, const void *pval, uint_t proto, uint_t flags) { ipadm_status_t status = IPADM_SUCCESS; const char *ifname = arg; uint64_t on_flags = 0, off_flags = 0; boolean_t on = B_FALSE; sa_family_t af = (proto == MOD_PROTO_IPV6 ? AF_INET6 : AF_INET); /* if we are resetting, set the value to its default value */ if (flags & IPADM_OPT_DEFAULT) { if (strcmp(pdp->ipd_name, "exchange_routes") == 0 || strcmp(pdp->ipd_name, "arp") == 0 || strcmp(pdp->ipd_name, "nud") == 0) { pval = IPADM_ONSTR; } else if (strcmp(pdp->ipd_name, "forwarding") == 0 || strcmp(pdp->ipd_name, "standby") == 0) { pval = IPADM_OFFSTR; } else { return (IPADM_PROP_UNKNOWN); } } if (strcmp(pval, IPADM_ONSTR) == 0) on = B_TRUE; else if (strcmp(pval, IPADM_OFFSTR) == 0) on = B_FALSE; else return (IPADM_INVALID_ARG); if (strcmp(pdp->ipd_name, "exchange_routes") == 0) { if (on) off_flags = IFF_NORTEXCH; else on_flags = IFF_NORTEXCH; } else if (strcmp(pdp->ipd_name, "arp") == 0) { if (on) off_flags = IFF_NOARP; else on_flags = IFF_NOARP; } else if (strcmp(pdp->ipd_name, "nud") == 0) { if (on) off_flags = IFF_NONUD; else on_flags = IFF_NONUD; } else if (strcmp(pdp->ipd_name, "forwarding") == 0) { if (on) on_flags = IFF_ROUTER; else off_flags = IFF_ROUTER; } else if (strcmp(pdp->ipd_name, "standby") == 0) { if (on) on_flags = IFF_STANDBY; else off_flags = IFF_STANDBY; } if (on_flags || off_flags) { status = i_ipadm_set_flags(iph, ifname, af, on_flags, off_flags); } return (status); } /* ARGSUSED */ static ipadm_status_t i_ipadm_set_eprivport(ipadm_handle_t iph, const void *arg, ipadm_prop_desc_t *pdp, const void *pval, uint_t proto, uint_t flags) { nvlist_t *portsnvl = NULL; nvpair_t *nvp; ipadm_status_t status = IPADM_SUCCESS; uint_t count = 0; if (flags & IPADM_OPT_DEFAULT) { assert(pval == NULL); return (i_ipadm_set_prop(iph, arg, pdp, pval, proto, flags)); } if ((status = ipadm_str2nvlist(pval, &portsnvl, IPADM_NORVAL)) != 0) return (status); /* count the number of ports */ for (nvp = nvlist_next_nvpair(portsnvl, NULL); nvp != NULL; nvp = nvlist_next_nvpair(portsnvl, nvp)) { ++count; } if (iph->iph_flags & IPH_INIT) { flags |= IPADM_OPT_APPEND; } else if (count > 1) { /* * We allow only one port to be added, removed or * assigned at a time. * * However on reboot, while initializing protocol * properties, extra_priv_ports might have multiple * values. Only in that case we allow setting multiple * values. */ nvlist_free(portsnvl); return (IPADM_INVALID_ARG); } for (nvp = nvlist_next_nvpair(portsnvl, NULL); nvp != NULL; nvp = nvlist_next_nvpair(portsnvl, nvp)) { status = i_ipadm_set_prop(iph, arg, pdp, nvpair_name(nvp), proto, flags); if (status != IPADM_SUCCESS) break; } nvlist_free(portsnvl); return (status); } /* ARGSUSED */ static ipadm_status_t i_ipadm_set_forwarding(ipadm_handle_t iph, const void *arg, ipadm_prop_desc_t *pdp, const void *pval, uint_t proto, uint_t flags) { const char *ifname = arg; ipadm_status_t status; /* * if interface name is provided, then set forwarding using the * IFF_ROUTER flag */ if (ifname != NULL) { status = i_ipadm_set_ifprop_flags(iph, ifname, pdp, pval, proto, flags); } else { char *val = NULL; /* * if the caller is IPH_LEGACY, `pval' already contains * numeric values. */ if (!(flags & IPADM_OPT_DEFAULT) && !(iph->iph_flags & IPH_LEGACY)) { if (strcmp(pval, IPADM_ONSTR) == 0) val = "1"; else if (strcmp(pval, IPADM_OFFSTR) == 0) val = "0"; else return (IPADM_INVALID_ARG); pval = val; } status = i_ipadm_set_prop(iph, ifname, pdp, pval, proto, flags); } return (status); } /* ARGSUSED */ static ipadm_status_t i_ipadm_set_ecnsack(ipadm_handle_t iph, const void *arg, ipadm_prop_desc_t *pdp, const void *pval, uint_t proto, uint_t flags) { uint_t i; char val[MAXPROPVALLEN]; /* if IPH_LEGACY is set, `pval' already contains numeric values */ if (!(flags & IPADM_OPT_DEFAULT) && !(iph->iph_flags & IPH_LEGACY)) { for (i = 0; ecn_sack_vals[i] != NULL; i++) { if (strcmp(pval, ecn_sack_vals[i]) == 0) break; } if (ecn_sack_vals[i] == NULL) return (IPADM_INVALID_ARG); (void) snprintf(val, MAXPROPVALLEN, "%d", i); pval = val; } return (i_ipadm_set_prop(iph, arg, pdp, pval, proto, flags)); } /* ARGSUSED */ ipadm_status_t i_ipadm_get_ecnsack(ipadm_handle_t iph, const void *arg, ipadm_prop_desc_t *pdp, char *buf, uint_t *bufsize, uint_t proto, uint_t valtype) { ipadm_status_t status = IPADM_SUCCESS; uint_t i, nbytes = 0; switch (valtype) { case MOD_PROP_POSSIBLE: for (i = 0; ecn_sack_vals[i] != NULL; i++) { if (i == 0) nbytes += snprintf(buf + nbytes, *bufsize - nbytes, "%s", ecn_sack_vals[i]); else nbytes += snprintf(buf + nbytes, *bufsize - nbytes, ",%s", ecn_sack_vals[i]); if (nbytes >= *bufsize) break; } break; case MOD_PROP_PERM: case MOD_PROP_DEFAULT: case MOD_PROP_ACTIVE: status = i_ipadm_get_prop(iph, arg, pdp, buf, bufsize, proto, valtype); /* * If IPH_LEGACY is set, do not convert the value returned * from kernel, */ if (iph->iph_flags & IPH_LEGACY) break; /* * For current and default value, convert the value returned * from kernel to more discrete representation. */ if (status == IPADM_SUCCESS && (valtype == MOD_PROP_ACTIVE || valtype == MOD_PROP_DEFAULT)) { i = atoi(buf); assert(i < 3); nbytes = snprintf(buf, *bufsize, "%s", ecn_sack_vals[i]); } break; default: return (IPADM_INVALID_ARG); } if (nbytes >= *bufsize) { /* insufficient buffer space */ *bufsize = nbytes + 1; return (IPADM_NO_BUFS); } return (status); } /* ARGSUSED */ static ipadm_status_t i_ipadm_get_forwarding(ipadm_handle_t iph, const void *arg, ipadm_prop_desc_t *pdp, char *buf, uint_t *bufsize, uint_t proto, uint_t valtype) { const char *ifname = arg; ipadm_status_t status = IPADM_SUCCESS; /* * if interface name is provided, then get forwarding status using * SIOCGLIFFLAGS */ if (ifname != NULL) { status = i_ipadm_get_ifprop_flags(iph, ifname, pdp, buf, bufsize, pdp->ipd_proto, valtype); } else { status = i_ipadm_get_prop(iph, ifname, pdp, buf, bufsize, proto, valtype); /* * If IPH_LEGACY is set, do not convert the value returned * from kernel, */ if (iph->iph_flags & IPH_LEGACY) goto ret; if (status == IPADM_SUCCESS && (valtype == MOD_PROP_ACTIVE || valtype == MOD_PROP_DEFAULT)) { uint_t val = atoi(buf); (void) snprintf(buf, *bufsize, (val == 1 ? IPADM_ONSTR : IPADM_OFFSTR)); } } ret: return (status); } /* ARGSUSED */ static ipadm_status_t i_ipadm_get_mtu(ipadm_handle_t iph, const void *arg, ipadm_prop_desc_t *pdp, char *buf, uint_t *bufsize, uint_t proto, uint_t valtype) { struct lifreq lifr; const char *ifname = arg; size_t nbytes; int s; switch (valtype) { case MOD_PROP_PERM: nbytes = snprintf(buf, *bufsize, "%d", MOD_PROP_PERM_RW); break; case MOD_PROP_DEFAULT: case MOD_PROP_POSSIBLE: return (i_ipadm_get_prop(iph, arg, pdp, buf, bufsize, proto, valtype)); case MOD_PROP_ACTIVE: bzero(&lifr, sizeof (lifr)); (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name)); s = (proto == MOD_PROTO_IPV6 ? iph->iph_sock6 : iph->iph_sock); if (ioctl(s, SIOCGLIFMTU, (caddr_t)&lifr) < 0) return (ipadm_errno2status(errno)); nbytes = snprintf(buf, *bufsize, "%u", lifr.lifr_mtu); break; default: return (IPADM_INVALID_ARG); } if (nbytes >= *bufsize) { /* insufficient buffer space */ *bufsize = nbytes + 1; return (IPADM_NO_BUFS); } return (IPADM_SUCCESS); } /* ARGSUSED */ static ipadm_status_t i_ipadm_get_metric(ipadm_handle_t iph, const void *arg, ipadm_prop_desc_t *pdp, char *buf, uint_t *bufsize, uint_t proto, uint_t valtype) { struct lifreq lifr; const char *ifname = arg; size_t nbytes; int s, val; switch (valtype) { case MOD_PROP_PERM: val = MOD_PROP_PERM_RW; break; case MOD_PROP_DEFAULT: val = DEF_METRIC_VAL; break; case MOD_PROP_ACTIVE: bzero(&lifr, sizeof (lifr)); (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name)); s = (proto == MOD_PROTO_IPV6 ? iph->iph_sock6 : iph->iph_sock); if (ioctl(s, SIOCGLIFMETRIC, (caddr_t)&lifr) < 0) return (ipadm_errno2status(errno)); val = lifr.lifr_metric; break; default: return (IPADM_INVALID_ARG); } nbytes = snprintf(buf, *bufsize, "%d", val); if (nbytes >= *bufsize) { /* insufficient buffer space */ *bufsize = nbytes + 1; return (IPADM_NO_BUFS); } return (IPADM_SUCCESS); } /* ARGSUSED */ static ipadm_status_t i_ipadm_get_usesrc(ipadm_handle_t iph, const void *arg, ipadm_prop_desc_t *ipd, char *buf, uint_t *bufsize, uint_t proto, uint_t valtype) { struct lifreq lifr; const char *ifname = arg; int s; char if_name[IF_NAMESIZE]; size_t nbytes; switch (valtype) { case MOD_PROP_PERM: nbytes = snprintf(buf, *bufsize, "%d", MOD_PROP_PERM_RW); break; case MOD_PROP_DEFAULT: nbytes = snprintf(buf, *bufsize, "%s", IPADM_NONESTR); break; case MOD_PROP_ACTIVE: bzero(&lifr, sizeof (lifr)); (void) strlcpy(lifr.lifr_name, ifname, sizeof (lifr.lifr_name)); s = (proto == MOD_PROTO_IPV6 ? iph->iph_sock6 : iph->iph_sock); if (ioctl(s, SIOCGLIFUSESRC, (caddr_t)&lifr) < 0) return (ipadm_errno2status(errno)); if (lifr.lifr_index == 0) { /* no src address was set, so print 'none' */ (void) strlcpy(if_name, IPADM_NONESTR, sizeof (if_name)); } else if (if_indextoname(lifr.lifr_index, if_name) == NULL) { return (ipadm_errno2status(errno)); } nbytes = snprintf(buf, *bufsize, "%s", if_name); break; default: return (IPADM_INVALID_ARG); } if (nbytes >= *bufsize) { /* insufficient buffer space */ *bufsize = nbytes + 1; return (IPADM_NO_BUFS); } return (IPADM_SUCCESS); } /* ARGSUSED */ static ipadm_status_t i_ipadm_get_ifprop_flags(ipadm_handle_t iph, const void *arg, ipadm_prop_desc_t *pdp, char *buf, uint_t *bufsize, uint_t proto, uint_t valtype) { uint64_t intf_flags; char *val; size_t nbytes; const char *ifname = arg; sa_family_t af; ipadm_status_t status = IPADM_SUCCESS; switch (valtype) { case MOD_PROP_PERM: nbytes = snprintf(buf, *bufsize, "%d", MOD_PROP_PERM_RW); break; case MOD_PROP_DEFAULT: if (strcmp(pdp->ipd_name, "exchange_routes") == 0 || strcmp(pdp->ipd_name, "arp") == 0 || strcmp(pdp->ipd_name, "nud") == 0) { val = IPADM_ONSTR; } else if (strcmp(pdp->ipd_name, "forwarding") == 0 || strcmp(pdp->ipd_name, "standby") == 0) { val = IPADM_OFFSTR; } else { return (IPADM_PROP_UNKNOWN); } nbytes = snprintf(buf, *bufsize, "%s", val); break; case MOD_PROP_ACTIVE: af = (proto == MOD_PROTO_IPV6 ? AF_INET6 : AF_INET); status = i_ipadm_get_flags(iph, ifname, af, &intf_flags); if (status != IPADM_SUCCESS) return (status); val = IPADM_OFFSTR; if (strcmp(pdp->ipd_name, "exchange_routes") == 0) { if (!(intf_flags & IFF_NORTEXCH)) val = IPADM_ONSTR; } else if (strcmp(pdp->ipd_name, "forwarding") == 0) { if (intf_flags & IFF_ROUTER) val = IPADM_ONSTR; } else if (strcmp(pdp->ipd_name, "arp") == 0) { if (!(intf_flags & IFF_NOARP)) val = IPADM_ONSTR; } else if (strcmp(pdp->ipd_name, "nud") == 0) { if (!(intf_flags & IFF_NONUD)) val = IPADM_ONSTR; } else if (strcmp(pdp->ipd_name, "standby") == 0) { if (intf_flags & IFF_STANDBY) val = IPADM_ONSTR; } nbytes = snprintf(buf, *bufsize, "%s", val); break; default: return (IPADM_INVALID_ARG); } if (nbytes >= *bufsize) { /* insufficient buffer space */ *bufsize = nbytes + 1; status = IPADM_NO_BUFS; } return (status); } static void i_ipadm_perm2str(char *buf, uint_t *bufsize) { uint_t perm = atoi(buf); (void) snprintf(buf, *bufsize, "%c%c", ((perm & MOD_PROP_PERM_READ) != 0) ? 'r' : '-', ((perm & MOD_PROP_PERM_WRITE) != 0) ? 'w' : '-'); } /* ARGSUSED */ static ipadm_status_t i_ipadm_get_prop(ipadm_handle_t iph, const void *arg, ipadm_prop_desc_t *pdp, char *buf, uint_t *bufsize, uint_t proto, uint_t valtype) { ipadm_status_t status = IPADM_SUCCESS; const char *ifname = arg; mod_ioc_prop_t *mip; char *pname = pdp->ipd_name; uint_t iocsize; /* allocate sufficient ioctl buffer to retrieve value */ iocsize = sizeof (mod_ioc_prop_t) + *bufsize - 1; if ((mip = calloc(1, iocsize)) == NULL) return (IPADM_NO_BUFS); mip->mpr_version = MOD_PROP_VERSION; mip->mpr_flags = valtype; mip->mpr_proto = proto; if (ifname != NULL) { (void) strlcpy(mip->mpr_ifname, ifname, sizeof (mip->mpr_ifname)); } (void) strlcpy(mip->mpr_name, pname, sizeof (mip->mpr_name)); mip->mpr_valsize = *bufsize; if (i_ipadm_strioctl(iph->iph_sock, SIOCGETPROP, (char *)mip, iocsize) < 0) { if (errno == ENOENT) status = IPADM_PROP_UNKNOWN; else status = ipadm_errno2status(errno); } else { bcopy(mip->mpr_val, buf, *bufsize); } free(mip); return (status); } /* * Populates the ipmgmt_prop_arg_t based on the class of property. * * For private protocol properties, while persisting information in ipadm * data store, to ensure there is no collision of namespace between ipadm * private nvpair names (which also starts with '_', see ipadm_ipmgmt.h) * and private protocol property names, we will prepend IPADM_PRIV_PROP_PREFIX * to property names. */ static void i_ipadm_populate_proparg(ipmgmt_prop_arg_t *pargp, ipadm_prop_desc_t *pdp, const char *pval, const void *object) { const struct ipadm_addrobj_s *ipaddr; uint_t class = pdp->ipd_class; uint_t proto = pdp->ipd_proto; (void) strlcpy(pargp->ia_pname, pdp->ipd_name, sizeof (pargp->ia_pname)); if (pval != NULL) (void) strlcpy(pargp->ia_pval, pval, sizeof (pargp->ia_pval)); switch (class) { case IPADMPROP_CLASS_MODULE: /* if it's a private property then add the prefix. */ if (pdp->ipd_name[0] == '_') { (void) snprintf(pargp->ia_pname, sizeof (pargp->ia_pname), "_%s", pdp->ipd_name); } (void) strlcpy(pargp->ia_module, object, sizeof (pargp->ia_module)); break; case IPADMPROP_CLASS_MODIF: /* check if object is protostr or an ifname */ if (ipadm_str2proto(object) != MOD_PROTO_NONE) { (void) strlcpy(pargp->ia_module, object, sizeof (pargp->ia_module)); break; } /* it's an interface property, fall through */ /* FALLTHRU */ case IPADMPROP_CLASS_IF: (void) strlcpy(pargp->ia_ifname, object, sizeof (pargp->ia_ifname)); (void) strlcpy(pargp->ia_module, ipadm_proto2str(proto), sizeof (pargp->ia_module)); break; case IPADMPROP_CLASS_ADDR: ipaddr = object; (void) strlcpy(pargp->ia_ifname, ipaddr->ipadm_ifname, sizeof (pargp->ia_ifname)); (void) strlcpy(pargp->ia_aobjname, ipaddr->ipadm_aobjname, sizeof (pargp->ia_aobjname)); break; } } /* * Common function to retrieve property value for a given interface `ifname' or * for a given protocol `proto'. The property name is in `pname'. * * `valtype' determines the type of value that will be retrieved. * IPADM_OPT_ACTIVE - current value of the property (active config) * IPADM_OPT_PERSIST - value of the property from persistent store * IPADM_OPT_DEFAULT - default hard coded value (boot-time value) * IPADM_OPT_PERM - read/write permissions for the value * IPADM_OPT_POSSIBLE - range of values */ static ipadm_status_t i_ipadm_getprop_common(ipadm_handle_t iph, const char *ifname, const char *pname, char *buf, uint_t *bufsize, uint_t proto, uint_t valtype) { ipadm_status_t status = IPADM_SUCCESS; ipadm_prop_desc_t *pdp; char priv_propname[MAXPROPNAMELEN]; ipadm_prop_desc_t ipadm_privprop = ipadm_privprop_template; boolean_t is_if = (ifname != NULL); int err = 0; pdp = i_ipadm_get_prop_desc(pname, proto, &err); if (err == EPROTO) return (IPADM_BAD_PROTOCOL); /* there are no private interface properties */ if (is_if && err == ENOENT) return (IPADM_PROP_UNKNOWN); if (pdp != NULL) { /* * check whether the property can be * applied on an interface */ if (is_if && !(pdp->ipd_class & IPADMPROP_CLASS_IF)) return (IPADM_INVALID_ARG); /* * check whether the property can be * applied on a module */ if (!is_if && !(pdp->ipd_class & IPADMPROP_CLASS_MODULE)) return (IPADM_INVALID_ARG); } else { /* private protocol properties, pass it to kernel directly */ pdp = &ipadm_privprop; (void) strlcpy(priv_propname, pname, sizeof (priv_propname)); pdp->ipd_name = priv_propname; } switch (valtype) { case IPADM_OPT_PERM: status = pdp->ipd_get(iph, ifname, pdp, buf, bufsize, proto, MOD_PROP_PERM); if (status == IPADM_SUCCESS) i_ipadm_perm2str(buf, bufsize); break; case IPADM_OPT_ACTIVE: status = pdp->ipd_get(iph, ifname, pdp, buf, bufsize, proto, MOD_PROP_ACTIVE); break; case IPADM_OPT_DEFAULT: status = pdp->ipd_get(iph, ifname, pdp, buf, bufsize, proto, MOD_PROP_DEFAULT); break; case IPADM_OPT_POSSIBLE: if (pdp->ipd_get_range != NULL) { status = pdp->ipd_get_range(iph, ifname, pdp, buf, bufsize, proto, MOD_PROP_POSSIBLE); break; } buf[0] = '\0'; break; case IPADM_OPT_PERSIST: /* retrieve from database */ if (is_if) status = i_ipadm_get_persist_propval(iph, pdp, buf, bufsize, ifname); else status = i_ipadm_get_persist_propval(iph, pdp, buf, bufsize, ipadm_proto2str(proto)); break; default: status = IPADM_INVALID_ARG; break; } return (status); } /* * Get protocol property of the specified protocol. */ ipadm_status_t ipadm_get_prop(ipadm_handle_t iph, const char *pname, char *buf, uint_t *bufsize, uint_t proto, uint_t valtype) { /* * validate the arguments of the function. */ if (iph == NULL || pname == NULL || buf == NULL || bufsize == NULL || *bufsize == 0) { return (IPADM_INVALID_ARG); } /* * Do we support this proto, if not return error. */ if (ipadm_proto2str(proto) == NULL) return (IPADM_NOTSUP); return (i_ipadm_getprop_common(iph, NULL, pname, buf, bufsize, proto, valtype)); } /* * Get interface property of the specified interface. */ ipadm_status_t ipadm_get_ifprop(ipadm_handle_t iph, const char *ifname, const char *pname, char *buf, uint_t *bufsize, uint_t proto, uint_t valtype) { /* validate the arguments of the function. */ if (iph == NULL || pname == NULL || buf == NULL || bufsize == NULL || *bufsize == 0) { return (IPADM_INVALID_ARG); } /* Do we support this proto, if not return error. */ if (ipadm_proto2str(proto) == NULL) return (IPADM_NOTSUP); /* * check if interface name is provided for interface property and * is valid. */ if (!i_ipadm_validate_ifname(iph, ifname)) return (IPADM_INVALID_ARG); return (i_ipadm_getprop_common(iph, ifname, pname, buf, bufsize, proto, valtype)); } /* * Allocates sufficient ioctl buffers and copies property name and the * value, among other things. If the flag IPADM_OPT_DEFAULT is set, then * `pval' will be NULL and it instructs the kernel to reset the current * value to property's default value. */ static ipadm_status_t i_ipadm_set_prop(ipadm_handle_t iph, const void *arg, ipadm_prop_desc_t *pdp, const void *pval, uint_t proto, uint_t flags) { ipadm_status_t status = IPADM_SUCCESS; const char *ifname = arg; mod_ioc_prop_t *mip; char *pname = pdp->ipd_name; uint_t valsize, iocsize; uint_t iocflags = 0; if (flags & IPADM_OPT_DEFAULT) { iocflags |= MOD_PROP_DEFAULT; } else if (flags & IPADM_OPT_ACTIVE) { iocflags |= MOD_PROP_ACTIVE; if (flags & IPADM_OPT_APPEND) iocflags |= MOD_PROP_APPEND; else if (flags & IPADM_OPT_REMOVE) iocflags |= MOD_PROP_REMOVE; } if (pval != NULL) { valsize = strlen(pval); iocsize = sizeof (mod_ioc_prop_t) + valsize - 1; } else { valsize = 0; iocsize = sizeof (mod_ioc_prop_t); } if ((mip = calloc(1, iocsize)) == NULL) return (IPADM_NO_BUFS); mip->mpr_version = MOD_PROP_VERSION; mip->mpr_flags = iocflags; mip->mpr_proto = proto; if (ifname != NULL) { (void) strlcpy(mip->mpr_ifname, ifname, sizeof (mip->mpr_ifname)); } (void) strlcpy(mip->mpr_name, pname, sizeof (mip->mpr_name)); mip->mpr_valsize = valsize; if (pval != NULL) bcopy(pval, mip->mpr_val, valsize); if (i_ipadm_strioctl(iph->iph_sock, SIOCSETPROP, (char *)mip, iocsize) < 0) { if (errno == ENOENT) status = IPADM_PROP_UNKNOWN; else status = ipadm_errno2status(errno); } free(mip); return (status); } /* * Common function for modifying both protocol/interface property. * * If: * IPADM_OPT_PERSIST is set then the value is persisted. * IPADM_OPT_DEFAULT is set then the default value for the property will * be applied. */ static ipadm_status_t i_ipadm_setprop_common(ipadm_handle_t iph, const char *ifname, const char *pname, const char *buf, uint_t proto, uint_t pflags) { ipadm_status_t status = IPADM_SUCCESS; boolean_t persist = (pflags & IPADM_OPT_PERSIST); boolean_t reset = (pflags & IPADM_OPT_DEFAULT); ipadm_prop_desc_t *pdp; boolean_t is_if = (ifname != NULL); char priv_propname[MAXPROPNAMELEN]; ipadm_prop_desc_t ipadm_privprop = ipadm_privprop_template; int err = 0; /* Check that property value is within the allowed size */ if (!reset && strnlen(buf, MAXPROPVALLEN) >= MAXPROPVALLEN) return (IPADM_INVALID_ARG); pdp = i_ipadm_get_prop_desc(pname, proto, &err); if (err == EPROTO) return (IPADM_BAD_PROTOCOL); /* there are no private interface properties */ if (is_if && err == ENOENT) return (IPADM_PROP_UNKNOWN); if (pdp != NULL) { /* do some sanity checks */ if (is_if) { if (!(pdp->ipd_class & IPADMPROP_CLASS_IF)) return (IPADM_INVALID_ARG); } else { if (!(pdp->ipd_class & IPADMPROP_CLASS_MODULE)) return (IPADM_INVALID_ARG); } /* * if the property is not multi-valued and IPADM_OPT_APPEND or * IPADM_OPT_REMOVE is specified, return IPADM_INVALID_ARG. */ if (!(pdp->ipd_flags & IPADMPROP_MULVAL) && (pflags & (IPADM_OPT_APPEND|IPADM_OPT_REMOVE))) { return (IPADM_INVALID_ARG); } } else { /* private protocol property, pass it to kernel directly */ pdp = &ipadm_privprop; (void) strlcpy(priv_propname, pname, sizeof (priv_propname)); pdp->ipd_name = priv_propname; } status = pdp->ipd_set(iph, ifname, pdp, buf, proto, pflags); if (status != IPADM_SUCCESS) return (status); if (persist) { if (is_if) status = i_ipadm_persist_propval(iph, pdp, buf, ifname, pflags); else status = i_ipadm_persist_propval(iph, pdp, buf, ipadm_proto2str(proto), pflags); } return (status); } /* * Sets the property value of the specified interface */ ipadm_status_t ipadm_set_ifprop(ipadm_handle_t iph, const char *ifname, const char *pname, const char *buf, uint_t proto, uint_t pflags) { boolean_t reset = (pflags & IPADM_OPT_DEFAULT); ipadm_status_t status; /* check for solaris.network.interface.config authorization */ if (!ipadm_check_auth()) return (IPADM_EAUTH); /* * validate the arguments of the function. */ if (iph == NULL || pname == NULL || (!reset && buf == NULL) || pflags == 0 || pflags == IPADM_OPT_PERSIST || (pflags & ~(IPADM_COMMON_OPT_MASK|IPADM_OPT_DEFAULT))) { return (IPADM_INVALID_ARG); } /* * Do we support this protocol, if not return error. */ if (ipadm_proto2str(proto) == NULL) return (IPADM_NOTSUP); /* * Validate the interface and check if a persistent * operation is performed on a temporary object. */ status = i_ipadm_validate_if(iph, ifname, proto, pflags); if (status != IPADM_SUCCESS) return (status); return (i_ipadm_setprop_common(iph, ifname, pname, buf, proto, pflags)); } /* * Sets the property value of the specified protocol. */ ipadm_status_t ipadm_set_prop(ipadm_handle_t iph, const char *pname, const char *buf, uint_t proto, uint_t pflags) { boolean_t reset = (pflags & IPADM_OPT_DEFAULT); /* check for solaris.network.interface.config authorization */ if (!ipadm_check_auth()) return (IPADM_EAUTH); /* * validate the arguments of the function. */ if (iph == NULL || pname == NULL ||(!reset && buf == NULL) || pflags == 0 || pflags == IPADM_OPT_PERSIST || (pflags & ~(IPADM_COMMON_OPT_MASK|IPADM_OPT_DEFAULT| IPADM_OPT_APPEND|IPADM_OPT_REMOVE))) { return (IPADM_INVALID_ARG); } /* * Do we support this proto, if not return error. */ if (ipadm_proto2str(proto) == NULL) return (IPADM_NOTSUP); return (i_ipadm_setprop_common(iph, NULL, pname, buf, proto, pflags)); } /* helper function for ipadm_walk_proptbl */ static void i_ipadm_walk_proptbl(ipadm_prop_desc_t *pdtbl, uint_t proto, uint_t class, ipadm_prop_wfunc_t *func, void *arg) { ipadm_prop_desc_t *pdp; for (pdp = pdtbl; pdp->ipd_name != NULL; pdp++) { if (!(pdp->ipd_class & class)) continue; if (proto != MOD_PROTO_NONE && !(pdp->ipd_proto & proto)) continue; /* * we found a class specific match, call the * user callback function. */ if (func(arg, pdp->ipd_name, pdp->ipd_proto) == B_FALSE) break; } } /* * Walks through all the properties, for a given protocol and property class * (protocol or interface). * * Further if proto == MOD_PROTO_NONE, then it walks through all the supported * protocol property tables. */ ipadm_status_t ipadm_walk_proptbl(uint_t proto, uint_t class, ipadm_prop_wfunc_t *func, void *arg) { ipadm_prop_desc_t *pdtbl; ipadm_status_t status = IPADM_SUCCESS; int i; int count = A_CNT(protocols); if (func == NULL) return (IPADM_INVALID_ARG); switch (class) { case IPADMPROP_CLASS_ADDR: pdtbl = ipadm_addrprop_table; break; case IPADMPROP_CLASS_IF: case IPADMPROP_CLASS_MODULE: pdtbl = i_ipadm_get_propdesc_table(proto); if (pdtbl == NULL && proto != MOD_PROTO_NONE) return (IPADM_INVALID_ARG); break; default: return (IPADM_INVALID_ARG); } if (pdtbl != NULL) { /* * proto will be MOD_PROTO_NONE in the case of * IPADMPROP_CLASS_ADDR. */ i_ipadm_walk_proptbl(pdtbl, proto, class, func, arg); } else { /* Walk thru all the protocol tables, we support */ for (i = 0; i < count; i++) { pdtbl = i_ipadm_get_propdesc_table(protocols[i]); i_ipadm_walk_proptbl(pdtbl, protocols[i], class, func, arg); } } return (status); } /* * Given a property name, walks through all the instances of a property name. * Some properties have two instances one for v4 interfaces and another for v6 * interfaces. For example: MTU. MTU can have different values for v4 and v6. * Therefore there are two properties for 'MTU'. * * This function invokes `func' for every instance of property `pname' */ ipadm_status_t ipadm_walk_prop(const char *pname, uint_t proto, uint_t class, ipadm_prop_wfunc_t *func, void *arg) { ipadm_prop_desc_t *pdtbl, *pdp; ipadm_status_t status = IPADM_SUCCESS; boolean_t matched = B_FALSE; if (pname == NULL || func == NULL) return (IPADM_INVALID_ARG); switch (class) { case IPADMPROP_CLASS_ADDR: pdtbl = ipadm_addrprop_table; break; case IPADMPROP_CLASS_IF: case IPADMPROP_CLASS_MODULE: pdtbl = i_ipadm_get_propdesc_table(proto); break; default: return (IPADM_INVALID_ARG); } if (pdtbl == NULL) return (IPADM_INVALID_ARG); for (pdp = pdtbl; pdp->ipd_name != NULL; pdp++) { if (strcmp(pname, pdp->ipd_name) != 0) continue; if (!(pdp->ipd_proto & proto)) continue; matched = B_TRUE; /* we found a match, call the callback function */ if (func(arg, pdp->ipd_name, pdp->ipd_proto) == B_FALSE) break; } if (!matched) status = IPADM_PROP_UNKNOWN; return (status); } /* ARGSUSED */ ipadm_status_t i_ipadm_get_onoff(ipadm_handle_t iph, const void *arg, ipadm_prop_desc_t *dp, char *buf, uint_t *bufsize, uint_t proto, uint_t valtype) { (void) snprintf(buf, *bufsize, "%s,%s", IPADM_ONSTR, IPADM_OFFSTR); return (IPADM_SUCCESS); } /* * Makes a door call to ipmgmtd to retrieve the persisted property value */ ipadm_status_t i_ipadm_get_persist_propval(ipadm_handle_t iph, ipadm_prop_desc_t *pdp, char *gbuf, uint_t *gbufsize, const void *object) { ipmgmt_prop_arg_t parg; ipmgmt_getprop_rval_t rval, *rvalp; size_t nbytes; int err = 0; bzero(&parg, sizeof (parg)); parg.ia_cmd = IPMGMT_CMD_GETPROP; i_ipadm_populate_proparg(&parg, pdp, NULL, object); rvalp = &rval; err = ipadm_door_call(iph, &parg, sizeof (parg), (void **)&rvalp, sizeof (rval), B_FALSE); if (err == 0) { /* assert that rvalp was not reallocated */ assert(rvalp == &rval); /* `ir_pval' contains the property value */ nbytes = snprintf(gbuf, *gbufsize, "%s", rvalp->ir_pval); if (nbytes >= *gbufsize) { /* insufficient buffer space */ *gbufsize = nbytes + 1; err = ENOBUFS; } } return (ipadm_errno2status(err)); } /* * Persists the property value for a given property in the data store */ ipadm_status_t i_ipadm_persist_propval(ipadm_handle_t iph, ipadm_prop_desc_t *pdp, const char *pval, const void *object, uint_t flags) { ipmgmt_prop_arg_t parg; int err = 0; bzero(&parg, sizeof (parg)); i_ipadm_populate_proparg(&parg, pdp, pval, object); /* * Check if value to be persisted need to be appended or removed. This * is required for multi-valued property. */ if (flags & IPADM_OPT_APPEND) parg.ia_flags |= IPMGMT_APPEND; if (flags & IPADM_OPT_REMOVE) parg.ia_flags |= IPMGMT_REMOVE; if (flags & (IPADM_OPT_DEFAULT|IPADM_OPT_REMOVE)) parg.ia_cmd = IPMGMT_CMD_RESETPROP; else parg.ia_cmd = IPMGMT_CMD_SETPROP; err = ipadm_door_call(iph, &parg, sizeof (parg), NULL, 0, B_FALSE); /* * its fine if there were no entry in the DB to delete. The user * might be changing property value, which was not changed * persistently. */ if (err == ENOENT) err = 0; return (ipadm_errno2status(err)); } /* * This is called from ipadm_set_ifprop() to validate the set operation. * It does the following steps: * 1. Validates the interface name. * 2. In case of a persistent operation, verifies that the * interface is persistent. */ static ipadm_status_t i_ipadm_validate_if(ipadm_handle_t iph, const char *ifname, uint_t proto, uint_t flags) { sa_family_t af, other_af; ipadm_status_t status; boolean_t p_exists; boolean_t af_exists, other_af_exists, a_exists; /* Check if the interface name is valid. */ if (!i_ipadm_validate_ifname(iph, ifname)) return (IPADM_INVALID_ARG); af = (proto == MOD_PROTO_IPV6 ? AF_INET6 : AF_INET); /* Check if interface exists in the persistent configuration. */ status = i_ipadm_if_pexists(iph, ifname, af, &p_exists); if (status != IPADM_SUCCESS) return (status); /* Check if interface exists in the active configuration. */ af_exists = ipadm_if_enabled(iph, ifname, af); other_af = (af == AF_INET ? AF_INET6 : AF_INET); other_af_exists = ipadm_if_enabled(iph, ifname, other_af); a_exists = (af_exists || other_af_exists); if (!a_exists && p_exists) return (IPADM_OP_DISABLE_OBJ); if (!af_exists) return (IPADM_ENXIO); /* * If a persistent operation is requested, check if the underlying * IP interface is persistent. */ if ((flags & IPADM_OPT_PERSIST) && !p_exists) return (IPADM_TEMPORARY_OBJ); return (IPADM_SUCCESS); } /* * Private protocol properties namespace scheme: * * PSARC 2010/080 identified the private protocol property names to be the * leading protocol names. For e.g. tcp_strong_iss, ip_strict_src_multihoming, * et al,. However to be consistent with private data-link property names, * which starts with '_', private protocol property names will start with '_'. * For e.g. _strong_iss, _strict_src_multihoming, et al,. */ /* maps new private protocol property name to the old private property name */ typedef struct ipadm_oname2nname_map { char *iom_oname; char *iom_nname; uint_t iom_proto; } ipadm_oname2nname_map_t; /* * IP is a special case. It isn't straight forward to derive the legacy name * from the new name and vice versa. No set standard was followed in naming * the properties and hence we need a table to capture the mapping. */ static ipadm_oname2nname_map_t name_map[] = { { "arp_probe_delay", "_arp_probe_delay", MOD_PROTO_IP }, { "arp_fastprobe_delay", "_arp_fastprobe_delay", MOD_PROTO_IP }, { "arp_probe_interval", "_arp_probe_interval", MOD_PROTO_IP }, { "arp_fastprobe_interval", "_arp_fastprobe_interval", MOD_PROTO_IP }, { "arp_probe_count", "_arp_probe_count", MOD_PROTO_IP }, { "arp_fastprobe_count", "_arp_fastprobe_count", MOD_PROTO_IP }, { "arp_defend_interval", "_arp_defend_interval", MOD_PROTO_IP }, { "arp_defend_rate", "_arp_defend_rate", MOD_PROTO_IP }, { "arp_defend_period", "_arp_defend_period", MOD_PROTO_IP }, { "ndp_defend_interval", "_ndp_defend_interval", MOD_PROTO_IP }, { "ndp_defend_rate", "_ndp_defend_rate", MOD_PROTO_IP }, { "ndp_defend_period", "_ndp_defend_period", MOD_PROTO_IP }, { "igmp_max_version", "_igmp_max_version", MOD_PROTO_IP }, { "mld_max_version", "_mld_max_version", MOD_PROTO_IP }, { "ipsec_override_persocket_policy", "_ipsec_override_persocket_policy", MOD_PROTO_IP }, { "ipsec_policy_log_interval", "_ipsec_policy_log_interval", MOD_PROTO_IP }, { "icmp_accept_clear_messages", "_icmp_accept_clear_messages", MOD_PROTO_IP }, { "igmp_accept_clear_messages", "_igmp_accept_clear_messages", MOD_PROTO_IP }, { "pim_accept_clear_messages", "_pim_accept_clear_messages", MOD_PROTO_IP }, { "ip_respond_to_echo_multicast", "_respond_to_echo_multicast", MOD_PROTO_IPV4 }, { "ip_send_redirects", "_send_redirects", MOD_PROTO_IPV4 }, { "ip_forward_src_routed", "_forward_src_routed", MOD_PROTO_IPV4 }, { "ip_icmp_return_data_bytes", "_icmp_return_data_bytes", MOD_PROTO_IPV4 }, { "ip_ignore_redirect", "_ignore_redirect", MOD_PROTO_IPV4 }, { "ip_strict_dst_multihoming", "_strict_dst_multihoming", MOD_PROTO_IPV4 }, { "ip_reasm_timeout", "_reasm_timeout", MOD_PROTO_IPV4 }, { "ip_strict_src_multihoming", "_strict_src_multihoming", MOD_PROTO_IPV4 }, { "ipv4_dad_announce_interval", "_dad_announce_interval", MOD_PROTO_IPV4 }, { "ipv4_icmp_return_pmtu", "_icmp_return_pmtu", MOD_PROTO_IPV4 }, { "ipv6_dad_announce_interval", "_dad_announce_interval", MOD_PROTO_IPV6 }, { "ipv6_icmp_return_pmtu", "_icmp_return_pmtu", MOD_PROTO_IPV6 }, { NULL, NULL, MOD_PROTO_NONE } }; /* * Following API returns a new property name in `nname' for the given legacy * property name in `oname'. */ int ipadm_legacy2new_propname(const char *oname, char *nname, uint_t nnamelen, uint_t *proto) { const char *str; ipadm_oname2nname_map_t *ionmp; /* if it's a public property, there is nothing to return */ if (i_ipadm_get_prop_desc(oname, *proto, NULL) != NULL) return (-1); /* * we didn't find the `oname' in the table, check if the property * name begins with a leading protocol. */ str = oname; switch (*proto) { case MOD_PROTO_TCP: if (strstr(oname, "tcp_") == oname) str += strlen("tcp"); break; case MOD_PROTO_SCTP: if (strstr(oname, "sctp_") == oname) str += strlen("sctp"); break; case MOD_PROTO_UDP: if (strstr(oname, "udp_") == oname) str += strlen("udp"); break; case MOD_PROTO_RAWIP: if (strstr(oname, "icmp_") == oname) str += strlen("icmp"); break; case MOD_PROTO_IP: case MOD_PROTO_IPV4: case MOD_PROTO_IPV6: if (strstr(oname, "ip6_") == oname) { *proto = MOD_PROTO_IPV6; str += strlen("ip6"); } else { for (ionmp = name_map; ionmp->iom_oname != NULL; ionmp++) { if (strcmp(oname, ionmp->iom_oname) == 0) { str = ionmp->iom_nname; *proto = ionmp->iom_proto; break; } } if (ionmp->iom_oname != NULL) break; if (strstr(oname, "ip_") == oname) { *proto = MOD_PROTO_IP; str += strlen("ip"); } } break; default: return (-1); } (void) snprintf(nname, nnamelen, "%s", str); return (0); } /* * Following API is required for ndd.c alone. To maintain backward * compatibility with ndd output, we need to print the legacy name * for the new name. */ int ipadm_new2legacy_propname(const char *oname, char *nname, uint_t nnamelen, uint_t proto) { char *prefix; ipadm_oname2nname_map_t *ionmp; /* if it's a public property, there is nothing to prepend */ if (i_ipadm_get_prop_desc(oname, proto, NULL) != NULL) return (-1); switch (proto) { case MOD_PROTO_TCP: prefix = "tcp"; break; case MOD_PROTO_SCTP: prefix = "sctp"; break; case MOD_PROTO_UDP: prefix = "udp"; break; case MOD_PROTO_RAWIP: prefix = "icmp"; break; case MOD_PROTO_IP: case MOD_PROTO_IPV4: case MOD_PROTO_IPV6: /* handle special case for IP */ for (ionmp = name_map; ionmp->iom_oname != NULL; ionmp++) { if (strcmp(oname, ionmp->iom_nname) == 0 && ionmp->iom_proto == proto) { (void) strlcpy(nname, ionmp->iom_oname, nnamelen); return (0); } } if (proto == MOD_PROTO_IPV6) prefix = "ip6"; else prefix = "ip"; break; default: return (-1); } (void) snprintf(nname, nnamelen, "%s%s", prefix, oname); return (0); }