2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A
2N/A/*
2N/A * Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A#include <stdio.h>
2N/A#include <sys/types.h>
2N/A#include <sys/socket.h>
2N/A#include <sys/ethernet.h>
2N/A#include <netinet/in.h>
2N/A#include <arpa/inet.h>
2N/A#include <sys/stat.h>
2N/A#include <sys/dld_ioc.h>
2N/A#include <string.h>
2N/A#include <fcntl.h>
2N/A#include <unistd.h>
2N/A#include <stropts.h>
2N/A#include <stdlib.h>
2N/A#include <errno.h>
2N/A#include <strings.h>
2N/A#include <libintl.h>
2N/A#include <netdb.h>
2N/A#include <net/if_types.h>
2N/A#include <net/if_dl.h>
2N/A#include <inet/ip.h>
2N/A#include <inet/ip6.h>
2N/A#include <libdlflow.h>
2N/A#include <libdlflow_impl.h>
2N/A#include <libdladm_impl.h>
2N/A#include <libdllink.h>
2N/A#include <zone.h>
2N/A
2N/A/* minimum buffer size for DLDIOCWALKFLOW */
2N/A#define MIN_INFO_SIZE (4 * 1024)
2N/A
2N/A#define DLADM_FLOW_DB "/etc/dladm/datalink.conf"
2N/A#define DLADM_FLOW_DB_TMP "/etc/dladm/datalink.conf.new"
2N/A#define DLADM_FLOW_DB_LOCK "/tmp/datalink.conf.lock"
2N/A
2N/A#define DLADM_FLOW_DB_PERMS S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
2N/A#define DLADM_FLOW_DB_OWNER UID_DLADM
2N/A#define DLADM_FLOW_DB_GROUP GID_NETADM
2N/A
2N/A#define BLANK_LINE(s) ((s[0] == '\0') || (s[0] == '#') || (s[0] == '\n'))
2N/A#define MAXLINELEN 1024
2N/A#define MAXPATHLEN 1024
2N/A
2N/A#define FPRINTF_ERR(fcall) if ((fcall) < 0) return (-1);
2N/A
2N/A
2N/A/*
2N/A * Convert a diagnostic returned by the kernel to dladm_status_t
2N/A */
2N/Astatic dladm_status_t
2N/Adladm_flow_diag2status(flow_ioc_diag_t ioc_diag)
2N/A{
2N/A switch (ioc_diag) {
2N/A case FLOW_IOC_DIAG_NONE:
2N/A return (DLADM_STATUS_OK);
2N/A case FLOW_IOC_DIAG_IP_LOCAL:
2N/A return (DLADM_STATUS_FLOW_IP_LOCAL);
2N/A case FLOW_IOC_DIAG_IP_REMOTE:
2N/A return (DLADM_STATUS_FLOW_IP_REMOTE);
2N/A case FLOW_IOC_DIAG_DSFIELD:
2N/A return (DLADM_STATUS_FLOW_DSFIELD);
2N/A case FLOW_IOC_DIAG_PROTO:
2N/A return (DLADM_STATUS_FLOW_PROTO);
2N/A case FLOW_IOC_DIAG_PROTO_LOCAL:
2N/A return (DLADM_STATUS_FLOW_PROTO_LOCAL);
2N/A case FLOW_IOC_DIAG_PROTO_REMOTE:
2N/A return (DLADM_STATUS_FLOW_PROTO_REMOTE);
2N/A case FLOW_IOC_DIAG_ATTR_INVALID:
2N/A return (DLADM_STATUS_FLOW_ATTR_INVALID);
2N/A default:
2N/A return (DLADM_STATUS_FAILED);
2N/A }
2N/A}
2N/A
2N/A
2N/A/* Remove a flow in the DB */
2N/Astatic int
2N/Ai_dladm_flow_remove_db(dladm_handle_t handle, const char *name,
2N/A const char *root)
2N/A{
2N/A if (dladm_remove_flowconf(handle, name, root) != DLADM_STATUS_OK)
2N/A return (-1);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Astatic char *
2N/Adladm_ipaddr_to_str(flow_desc_t *flow_desc, boolean_t localaddr)
2N/A{
2N/A char abuf[INET6_ADDRSTRLEN], *ap;
2N/A static char ret_str[INET6_ADDRSTRLEN + 4];
2N/A /* add 4 byte to store '/' and the net mask */
2N/A struct in_addr ipaddr;
2N/A int prefix_len, prefix_max;
2N/A
2N/A if (flow_desc->fd_ipversion != 6) {
2N/A if (localaddr)
2N/A ipaddr.s_addr =
2N/A flow_desc->fd_local_addr._S6_un._S6_u32[3];
2N/A else
2N/A ipaddr.s_addr =
2N/A flow_desc->fd_remote_addr._S6_un._S6_u32[3];
2N/A
2N/A ap = inet_ntoa(ipaddr);
2N/A prefix_max = IP_ABITS;
2N/A } else {
2N/A if (localaddr)
2N/A (void) inet_ntop(AF_INET6, &flow_desc->fd_local_addr,
2N/A abuf, INET6_ADDRSTRLEN);
2N/A else
2N/A (void) inet_ntop(AF_INET6, &flow_desc->fd_remote_addr,
2N/A abuf, INET6_ADDRSTRLEN);
2N/A
2N/A ap = abuf;
2N/A prefix_max = IPV6_ABITS;
2N/A }
2N/A
2N/A if (localaddr)
2N/A (void) dladm_mask2prefixlen(&flow_desc->fd_local_netmask,
2N/A prefix_max, &prefix_len);
2N/A else
2N/A (void) dladm_mask2prefixlen(&flow_desc->fd_remote_netmask,
2N/A prefix_max, &prefix_len);
2N/A (void) snprintf(ret_str, sizeof (ret_str), "%s/%d", ap,
2N/A prefix_len);
2N/A return (ret_str);
2N/A}
2N/A
2N/A/* Add flow to the DB */
2N/Astatic dladm_status_t
2N/Ai_dladm_flow_create_db(dladm_handle_t handle, const char *name,
2N/A dld_flowinfo_t *attrp, const char *root)
2N/A{
2N/A dladm_status_t status;
2N/A char linkover[MAXLINKNAMELEN];
2N/A
2N/A if ((status = dladm_create_flowconf(handle, name, attrp->fi_linkid))
2N/A != DLADM_STATUS_OK)
2N/A return (status);
2N/A
2N/A /* set link name over which the flow resides */
2N/A if (attrp->fi_linkid != DATALINK_INVALID_LINKID) {
2N/A status = dladm_datalink_id2info(handle, attrp->fi_linkid, NULL,
2N/A NULL, NULL, linkover, sizeof (linkover));
2N/A if (status != DLADM_STATUS_OK)
2N/A goto done;
2N/A status = dladm_set_flowconf_field(handle, name, FLINKOVER,
2N/A DLADM_TYPE_STR, linkover);
2N/A if (status != DLADM_STATUS_OK)
2N/A goto done;
2N/A }
2N/A
2N/A /* flow policy */
2N/A if (attrp->fi_resource_props.mrp_mask & MRP_MAXBW) {
2N/A uint64_t maxbw = attrp->fi_resource_props.mrp_maxbw;
2N/A status = dladm_set_flowconf_field(handle, name, FMAXBW,
2N/A DLADM_TYPE_UINT64, &maxbw);
2N/A if (status != DLADM_STATUS_OK)
2N/A goto done;
2N/A }
2N/A
2N/A /* flow descriptor */
2N/A if (attrp->fi_flow_desc.fd_mask & FLOW_IP_DSFIELD) {
2N/A status = dladm_set_flowconf_field(handle, name, FDSFIELD,
2N/A DLADM_TYPE_UINT64, &attrp->fi_flow_desc.fd_dsfield);
2N/A if (status != DLADM_STATUS_OK)
2N/A goto done;
2N/A
2N/A status = dladm_set_flowconf_field(handle, name, FDSFIELD_MASK,
2N/A DLADM_TYPE_UINT64, &attrp->fi_flow_desc.fd_dsfield_mask);
2N/A if (status != DLADM_STATUS_OK)
2N/A goto done;
2N/A }
2N/A
2N/A if (attrp->fi_flow_desc.fd_mask & FLOW_IP_LOCAL) {
2N/A char *addr_str;
2N/A
2N/A addr_str = dladm_ipaddr_to_str(&attrp->fi_flow_desc, B_TRUE);
2N/A status = dladm_set_flowconf_field(handle, name, FLOCAL_IP_ADDR,
2N/A DLADM_TYPE_STR, addr_str);
2N/A if (status != DLADM_STATUS_OK)
2N/A goto done;
2N/A }
2N/A
2N/A if (attrp->fi_flow_desc.fd_mask & FLOW_IP_REMOTE) {
2N/A /* add 4 byte to store '/' and the net mask */
2N/A char *addr_str;
2N/A
2N/A addr_str = dladm_ipaddr_to_str(&attrp->fi_flow_desc, B_FALSE);
2N/A status = dladm_set_flowconf_field(handle, name, FREMOTE_IP_ADDR,
2N/A DLADM_TYPE_STR, addr_str);
2N/A if (status != DLADM_STATUS_OK)
2N/A goto done;
2N/A }
2N/A
2N/A if (attrp->fi_flow_desc.fd_mask & FLOW_IP_PROTOCOL) {
2N/A uint64_t prot = attrp->fi_flow_desc.fd_protocol;
2N/A
2N/A status = dladm_set_flowconf_field(handle, name, FTRANSPORT,
2N/A DLADM_TYPE_UINT64, &prot);
2N/A if (status != DLADM_STATUS_OK)
2N/A goto done;
2N/A }
2N/A
2N/A if (attrp->fi_flow_desc.fd_mask & FLOW_ULP_PORT_LOCAL) {
2N/A uint64_t lport = attrp->fi_flow_desc.fd_local_port;
2N/A
2N/A status = dladm_set_flowconf_field(handle, name, FLOCAL_PORT,
2N/A DLADM_TYPE_UINT64, &lport);
2N/A if (status != DLADM_STATUS_OK)
2N/A goto done;
2N/A
2N/A }
2N/A
2N/A if (attrp->fi_flow_desc.fd_mask & FLOW_ULP_PORT_REMOTE) {
2N/A uint64_t rport = attrp->fi_flow_desc.fd_remote_port;
2N/A
2N/A status = dladm_set_flowconf_field(handle, name, FREMOTE_PORT,
2N/A DLADM_TYPE_UINT64, &rport);
2N/A if (status != DLADM_STATUS_OK)
2N/A goto done;
2N/A }
2N/A
2N/A /*
2N/A * Commit the flow configuration.
2N/A */
2N/A status = dladm_write_flowconf(handle, name, root);
2N/Adone:
2N/A return (status);
2N/A}
2N/A
2N/A/* Add flow to kernel */
2N/Adladm_status_t
2N/Ai_dladm_flow_add(dladm_handle_t handle, char *flowname, datalink_id_t linkid,
2N/A flow_desc_t *flowdesc, mac_resource_props_t *mrp)
2N/A{
2N/A dld_ioc_addflow_t ioc;
2N/A int rc;
2N/A dladm_status_t status = DLADM_STATUS_OK;
2N/A
2N/A /* create flow */
2N/A bzero(&ioc, sizeof (ioc));
2N/A bcopy(flowdesc, &ioc.af_flow_desc, sizeof (flow_desc_t));
2N/A if (mrp != NULL) {
2N/A bcopy(mrp, &ioc.af_resource_props,
2N/A sizeof (mac_resource_props_t));
2N/A }
2N/A
2N/A (void) strlcpy(ioc.af_name, flowname, sizeof (ioc.af_name));
2N/A ioc.af_linkid = linkid;
2N/A
2N/A rc = ioctl(dladm_dld_fd(handle), DLDIOC_ADDFLOW, &ioc);
2N/A if (rc < 0)
2N/A status = dladm_errno2status(errno);
2N/A
2N/A if (status != DLADM_STATUS_OK) {
2N/A if (ioc.af_diag != FLOW_IOC_DIAG_NONE)
2N/A status = dladm_flow_diag2status(ioc.af_diag);
2N/A }
2N/A return (status);
2N/A}
2N/A
2N/A/* Remove flow from kernel */
2N/Adladm_status_t
2N/Ai_dladm_flow_remove(dladm_handle_t handle, char *flowname, zoneid_t zoneid)
2N/A{
2N/A dld_ioc_removeflow_t attr;
2N/A dladm_status_t status = DLADM_STATUS_OK;
2N/A
2N/A (void) strlcpy(attr.rf_name, flowname, sizeof (attr.rf_name));
2N/A attr.rf_zoneid = zoneid;
2N/A
2N/A if (ioctl(dladm_dld_fd(handle), DLDIOC_REMOVEFLOW, &attr) < 0)
2N/A status = dladm_errno2status(errno);
2N/A
2N/A return (status);
2N/A}
2N/A
2N/Adladm_status_t
2N/Adladm_flow_create_ngz_kstat(dladm_handle_t handle, char *flowname,
2N/A zoneid_t zoneid)
2N/A{
2N/A dld_ioc_createkstat_t attr;
2N/A dladm_status_t status = DLADM_STATUS_OK;
2N/A
2N/A (void) strlcpy(attr.ck_flow, flowname, sizeof (attr.ck_flow));
2N/A attr.ck_zoneid = zoneid;
2N/A
2N/A if (ioctl(dladm_dld_fd(handle), DLDIOC_CREATEKSTAT, &attr) < 0)
2N/A status = dladm_errno2status(errno);
2N/A return (status);
2N/A}
2N/A
2N/A/* ARGSUSED */
2N/Adladm_status_t
2N/Adladm_flow_add(dladm_handle_t handle, datalink_id_t linkid,
2N/A dladm_arg_list_t *attrlist, dladm_arg_list_t *proplist, char *flowname,
2N/A boolean_t tempop, const char *root)
2N/A{
2N/A dld_flowinfo_t db_attr;
2N/A flow_desc_t flowdesc;
2N/A mac_resource_props_t mrp;
2N/A dladm_status_t status;
2N/A zoneid_t zoneid = getzoneid();
2N/A
2N/A /* Extract flow attributes from attrlist */
2N/A bzero(&flowdesc, sizeof (flow_desc_t));
2N/A if (attrlist != NULL && (status = dladm_flow_attrlist_extract(attrlist,
2N/A &flowdesc)) != DLADM_STATUS_OK)
2N/A return (status);
2N/A
2N/A /* Extract resource_ctl and cpu_list from proplist */
2N/A bzero(&mrp, sizeof (mac_resource_props_t));
2N/A if (proplist != NULL && (status = dladm_flow_proplist_extract(proplist,
2N/A &mrp)) != DLADM_STATUS_OK)
2N/A return (status);
2N/A
2N/A flowdesc.fd_zoneid = zoneid;
2N/A /* Add flow in kernel */
2N/A status = i_dladm_flow_add(handle, flowname, linkid, &flowdesc, &mrp);
2N/A if (status != DLADM_STATUS_OK || tempop)
2N/A return (status);
2N/A
2N/A /* Add flow to DB */
2N/A bzero(&db_attr, sizeof (db_attr));
2N/A bcopy(&flowdesc, &db_attr.fi_flow_desc, sizeof (flow_desc_t));
2N/A bcopy(&mrp, &db_attr.fi_resource_props, sizeof (mac_resource_props_t));
2N/A (void) strlcpy(db_attr.fi_flowname, flowname,
2N/A sizeof (db_attr.fi_flowname));
2N/A db_attr.fi_linkid = linkid;
2N/A
2N/A if ((status = i_dladm_flow_create_db(handle, flowname, &db_attr, root))
2N/A != DLADM_STATUS_OK) {
2N/A /* if write to DB failed, remove flow from kernel */
2N/A (void) i_dladm_flow_remove(handle, flowname, zoneid);
2N/A return (status);
2N/A }
2N/A
2N/Adone:
2N/A return (status);
2N/A}
2N/A
2N/A/*
2N/A * Remove a flow.
2N/A */
2N/A/* ARGSUSED */
2N/Adladm_status_t
2N/Adladm_flow_remove(dladm_handle_t handle, char *flowname, boolean_t tempop,
2N/A const char *root)
2N/A{
2N/A dladm_status_t status = DLADM_STATUS_OK;
2N/A dladm_status_t s = DLADM_STATUS_OK;
2N/A char fname[MAXFLOWNAMELEN], zonename[ZONENAME_MAX];
2N/A zoneid_t zoneid = getzoneid(), inputzoneid;
2N/A boolean_t onloan = B_FALSE;
2N/A
2N/A /*
2N/A * Check if this zone owns this flow or not. Flows can only be removed
2N/A * by the owner zone or the global zone.
2N/A */
2N/A inputzoneid = dladm_extra_names(flowname, fname, zonename);
2N/A if (zoneid != GLOBAL_ZONEID && zoneid != inputzoneid)
2N/A return (DLADM_STATUS_FLOW_WRONG_ZONE);
2N/A
2N/A /* Before remove, check if the flow is onloan to another zone */
2N/A status = dladm_read_flowconf(handle, fname, &onloan);
2N/A if (status == DLADM_STATUS_OK && onloan)
2N/A return (DLADM_STATUS_FLOW_WRONG_ZONE);
2N/A
2N/A /* Remove flow from kernel */
2N/A status = i_dladm_flow_remove(handle, fname, zoneid);
2N/A if (tempop)
2N/A return (status);
2N/A
2N/A /* Remove flow from DB */
2N/A if (!tempop) {
2N/A /* flow DB */
2N/A if (i_dladm_flow_remove_db(handle, fname, root) < 0)
2N/A s = dladm_errno2status(errno);
2N/A }
2N/A
2N/Adone:
2N/A if (!tempop) {
2N/A if (s == DLADM_STATUS_OK) {
2N/A if (status == DLADM_STATUS_NOTFOUND)
2N/A status = s;
2N/A } else {
2N/A if (s != DLADM_STATUS_NOTFOUND)
2N/A status = s;
2N/A }
2N/A }
2N/A return (status);
2N/A}
2N/A
2N/A/*
2N/A * Get an existing flow in the DB.
2N/A */
2N/A
2N/Atypedef struct get_db_state {
2N/A int (*gs_fn)(dladm_handle_t, dladm_flow_attr_t *, void *);
2N/A void *gs_arg;
2N/A datalink_id_t gs_linkid;
2N/A} get_db_state_t;
2N/A
2N/A/*
2N/A * Walk through the flows defined on the system and for each flow
2N/A * invoke <fn>(<arg>, <flow>);
2N/A * Currently used for show-flow and set-linkprop -p zone.
2N/A */
2N/A/* ARGSUSED */
2N/Adladm_status_t
2N/Adladm_walk_flow(int (*fn)(dladm_handle_t, dladm_flow_attr_t *, void *),
2N/A dladm_handle_t handle, datalink_id_t linkid, void *arg, boolean_t persist)
2N/A{
2N/A dld_flowinfo_t *flow;
2N/A int i, bufsize;
2N/A dld_ioc_walkflow_t *ioc = NULL;
2N/A dladm_flow_attr_t attr;
2N/A dladm_status_t status = DLADM_STATUS_OK;
2N/A zoneid_t cur_zoneid = getzoneid();
2N/A char zonename[ZONENAME_MAX];
2N/A ssize_t s;
2N/A boolean_t onloan = B_FALSE;
2N/A
2N/A if (fn == NULL)
2N/A return (DLADM_STATUS_BADARG);
2N/A
2N/A if (persist) {
2N/A dladm_status_t status;
2N/A dld_flowinfo_t flowinfo;
2N/A int ret_err = 0, zindex = 0;
2N/A uint_t numzone = 0;
2N/A char flowname[MAXFLOWNAMELEN];
2N/A zoneid_t *zids = NULL, next_zid = cur_zoneid;
2N/A
2N/A bzero(flowname, MAXFLOWNAMELEN);
2N/A if (cur_zoneid == GLOBAL_ZONEID)
2N/A if (zone_get_zoneids(&zids, &numzone) != 0)
2N/A return (DLADM_STATUS_ZONE_ERR);
2N/A
2N/A while (ret_err != ENOENT) {
2N/A s = getzonenamebyid(next_zid, zonename, ZONENAME_MAX);
2N/A /*
2N/A * Global zone needs to walks through all zones, if the
2N/A * zone that has been walked is not activate, go to
2N/A * the next one.
2N/A */
2N/A if (s < 0 && cur_zoneid == GLOBAL_ZONEID) {
2N/A next_zid = zids[++zindex];
2N/A if (zindex > numzone - 1)
2N/A break;
2N/A continue;
2N/A }
2N/A
2N/A bzero(&flowinfo, sizeof (flowinfo));
2N/A status = dladm_get_flownext(handle, flowname, next_zid,
2N/A &flowinfo, &ret_err);
2N/A if (status != DLADM_STATUS_OK &&
2N/A cur_zoneid != GLOBAL_ZONEID)
2N/A return (status);
2N/A if (status == DLADM_STATUS_OK) {
2N/A zoneid_t link_zid;
2N/A
2N/A (void) strlcpy(flowname, flowinfo.fi_flowname,
2N/A MAXFLOWNAMELEN);
2N/A
2N/A /*
2N/A * Check if flow's underlying link is still
2N/A * present as gz may re-assign the link to
2N/A * another zone
2N/A */
2N/A if (flowinfo.fi_linkid !=
2N/A DATALINK_INVALID_LINKID) {
2N/A status = dladm_datalink_id2linkinfo
2N/A (handle, flowinfo.fi_linkid, NULL,
2N/A NULL, NULL, NULL, 0, &link_zid);
2N/A if (status == DLADM_STATUS_NOTFOUND)
2N/A flowinfo.fi_linkid =
2N/A DATALINK_INVALID_LINKID;
2N/A }
2N/A
2N/A if (flowinfo.fi_linkid != linkid &&
2N/A flowinfo.fi_linkid !=
2N/A DATALINK_INVALID_LINKID)
2N/A continue;
2N/A } else if (status == DLADM_STATUS_NOTFOUND &&
2N/A cur_zoneid == GLOBAL_ZONEID) {
2N/A next_zid = zids[++zindex];
2N/A if (zindex > numzone - 1)
2N/A break;
2N/A bzero(flowname, sizeof (flowname));
2N/A continue;
2N/A }
2N/A
2N/A bzero(&attr, sizeof (attr));
2N/A if (next_zid != cur_zoneid) {
2N/A (void) snprintf(attr.fa_flowname,
2N/A sizeof (attr.fa_flowname), "%s/%s",
2N/A zonename, flowinfo.fi_flowname);
2N/A } else {
2N/A /* ngz, onloan flow shows as global/flowname */
2N/A status = dladm_read_flowconf(handle, flowname,
2N/A &onloan);
2N/A if (status != DLADM_STATUS_OK)
2N/A continue;
2N/A if (onloan && cur_zoneid != GLOBAL_ZONEID)
2N/A (void) snprintf(attr.fa_flowname,
2N/A sizeof (attr.fa_flowname), "%s/%s",
2N/A "global", flowinfo.fi_flowname);
2N/A else
2N/A bcopy(flowinfo.fi_flowname,
2N/A &attr.fa_flowname,
2N/A sizeof (attr.fa_flowname));
2N/A }
2N/A
2N/A attr.fa_linkid = flowinfo.fi_linkid;
2N/A bcopy(&flowinfo.fi_flow_desc, &attr.fa_flow_desc,
2N/A sizeof (attr.fa_flow_desc));
2N/A
2N/A bcopy(&flowinfo.fi_resource_props,
2N/A &attr.fa_resource_props,
2N/A sizeof (attr.fa_resource_props));
2N/A if (fn(handle, &attr, arg) == DLADM_WALK_TERMINATE)
2N/A break;
2N/A }
2N/A } else {
2N/A bufsize = MIN_INFO_SIZE;
2N/A if ((ioc = calloc(1, bufsize)) == NULL) {
2N/A status = dladm_errno2status(errno);
2N/A return (status);
2N/A }
2N/A
2N/A ioc->wf_linkid = linkid;
2N/A ioc->wf_len = bufsize - sizeof (*ioc);
2N/A
2N/A while (ioctl(dladm_dld_fd(handle), DLDIOC_WALKFLOW, ioc) < 0) {
2N/A if (errno == ENOSPC) {
2N/A bufsize *= 2;
2N/A ioc = realloc(ioc, bufsize);
2N/A if (ioc != NULL) {
2N/A ioc->wf_linkid = linkid;
2N/A ioc->wf_len = bufsize - sizeof (*ioc);
2N/A continue;
2N/A }
2N/A }
2N/A goto bail;
2N/A }
2N/A
2N/A flow = (dld_flowinfo_t *)(void *)(ioc + 1);
2N/A for (i = 0; i < ioc->wf_nflows; i++, flow++) {
2N/A zoneid_t zid;
2N/A
2N/A zid = flow->fi_flow_desc.fd_zoneid;
2N/A bzero(&attr, sizeof (attr));
2N/A
2N/A (void) dladm_read_flowconf(handle, flow->fi_flowname,
2N/A &onloan);
2N/A if (zid != cur_zoneid) {
2N/A char zonename[ZONENAME_MAX];
2N/A ssize_t s = 0;
2N/A
2N/A if (zid == GLOBAL_ZONEID)
2N/A (void) strlcpy(zonename, "global",
2N/A ZONENAME_MAX);
2N/A else
2N/A s = getzonenamebyid(zid, zonename,
2N/A ZONENAME_MAX);
2N/A if (!onloan && s < 0)
2N/A continue;
2N/A (void) snprintf(attr.fa_flowname,
2N/A sizeof (attr.fa_flowname), "%s/%s",
2N/A zonename, flow->fi_flowname);
2N/A } else {
2N/A bcopy(&flow->fi_flowname, &attr.fa_flowname,
2N/A sizeof (attr.fa_flowname));
2N/A }
2N/A attr.fa_linkid = flow->fi_linkid;
2N/A bcopy(&flow->fi_flow_desc, &attr.fa_flow_desc,
2N/A sizeof (attr.fa_flow_desc));
2N/A bcopy(&flow->fi_resource_props, &attr.fa_resource_props,
2N/A sizeof (attr.fa_resource_props));
2N/A if (fn(handle, &attr, arg) == DLADM_WALK_TERMINATE)
2N/A break;
2N/A }
2N/A }
2N/A
2N/Abail:
2N/A free(ioc);
2N/A return (status);
2N/A}
2N/A
2N/Adladm_status_t
2N/Adladm_flow_init(dladm_handle_t handle, zoneid_t zoneid)
2N/A{
2N/A dladm_status_t status = DLADM_STATUS_OK;
2N/A dld_flowinfo_t flowinfo;
2N/A int ret_err = 0;
2N/A char flowname[MAXFLOWNAMELEN];
2N/A
2N/A bzero(flowname, sizeof (flowname));
2N/A bzero(&flowinfo, sizeof (flowinfo));
2N/A while (ret_err == 0) {
2N/A bzero(&flowinfo, sizeof (flowinfo));
2N/A status = dladm_get_flownext(handle, flowname, zoneid, &flowinfo,
2N/A &ret_err);
2N/A if (status != DLADM_STATUS_OK) {
2N/A return (status == DLADM_STATUS_NOTFOUND ?
2N/A DLADM_STATUS_OK : status);
2N/A }
2N/A if (flowinfo.fi_flow_desc.fd_zoneid != zoneid)
2N/A return (DLADM_STATUS_OK);
2N/A
2N/A /*
2N/A * Flows can exist for removed links. In such cases,
2N/A * i_dladm_flow_add() will return DLADM_STATUS_NOTFOUND.
2N/A * Ensure that such failures do not prevent other flows from
2N/A * being added.
2N/A */
2N/A if ((status = i_dladm_flow_add(handle, flowinfo.fi_flowname,
2N/A flowinfo.fi_linkid, &flowinfo.fi_flow_desc,
2N/A &flowinfo.fi_resource_props)) == DLADM_STATUS_NOTFOUND)
2N/A status = DLADM_STATUS_OK;
2N/A if (status != DLADM_STATUS_OK)
2N/A return (status);
2N/A (void) strlcpy(flowname, flowinfo.fi_flowname, MAXFLOWNAMELEN);
2N/A }
2N/A return (status);
2N/A}
2N/A
2N/Avoid
2N/Adladm_flow_fini(dladm_handle_t handle, zoneid_t zoneid)
2N/A{
2N/A dladm_status_t status = DLADM_STATUS_OK;
2N/A dld_flowinfo_t flowinfo;
2N/A int ret_err = 0;
2N/A char flowname[MAXFLOWNAMELEN];
2N/A
2N/A bzero(flowname, sizeof (flowname));
2N/A while (ret_err == 0) {
2N/A bzero(&flowinfo, sizeof (flowinfo));
2N/A
2N/A status = dladm_get_flownext(handle, flowname, zoneid, &flowinfo,
2N/A &ret_err);
2N/A if (status != DLADM_STATUS_OK)
2N/A break;
2N/A if (flowinfo.fi_flow_desc.fd_zoneid != zoneid)
2N/A break;
2N/A
2N/A (void) i_dladm_flow_remove(handle, flowinfo.fi_flowname,
2N/A zoneid);
2N/A
2N/A (void) strlcpy(flowname, flowinfo.fi_flowname, MAXFLOWNAMELEN);
2N/A }
2N/A}
2N/A
2N/Adladm_status_t
2N/Adladm_prefixlen2mask(int prefixlen, int maxlen, uchar_t *mask)
2N/A{
2N/A if (prefixlen < 0 || prefixlen > maxlen)
2N/A return (DLADM_STATUS_BADARG);
2N/A
2N/A while (prefixlen > 0) {
2N/A if (prefixlen >= 8) {
2N/A *mask++ = 0xFF;
2N/A prefixlen -= 8;
2N/A continue;
2N/A }
2N/A *mask |= 1 << (8 - prefixlen);
2N/A prefixlen--;
2N/A }
2N/A return (DLADM_STATUS_OK);
2N/A}
2N/A
2N/Adladm_status_t
2N/Adladm_mask2prefixlen(in6_addr_t *mask, int plen, int *prefixlen)
2N/A{
2N/A int bits;
2N/A int i, end;
2N/A
2N/A switch (plen) {
2N/A case IP_ABITS:
2N/A end = 3;
2N/A break;
2N/A case IPV6_ABITS:
2N/A end = 0;
2N/A break;
2N/A default:
2N/A return (DLADM_STATUS_BADARG);
2N/A }
2N/A
2N/A for (i = 3; i >= end; i--) {
2N/A if (mask->_S6_un._S6_u32[i] == 0) {
2N/A plen -= 32;
2N/A continue;
2N/A }
2N/A bits = ffs(ntohl(mask->_S6_un._S6_u32[i])) - 1;
2N/A if (bits == 0)
2N/A break;
2N/A plen -= bits;
2N/A }
2N/A *prefixlen = plen;
2N/A return (DLADM_STATUS_OK);
2N/A}