Home | History | Annotate | Download | only in common
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 #include <stdio.h>
     27 #include <sys/types.h>
     28 #include <sys/socket.h>
     29 #include <sys/ethernet.h>
     30 #include <netinet/in.h>
     31 #include <arpa/inet.h>
     32 #include <sys/stat.h>
     33 #include <sys/dld_ioc.h>
     34 #include <string.h>
     35 #include <fcntl.h>
     36 #include <unistd.h>
     37 #include <stropts.h>
     38 #include <stdlib.h>
     39 #include <errno.h>
     40 #include <strings.h>
     41 #include <libintl.h>
     42 #include <netdb.h>
     43 #include <net/if_types.h>
     44 #include <net/if_dl.h>
     45 #include <inet/ip.h>
     46 #include <inet/ip6.h>
     47 #include <libdlflow.h>
     48 #include <libdlflow_impl.h>
     49 #include <libdladm_impl.h>
     50 
     51 /* minimum buffer size for DLDIOCWALKFLOW */
     52 #define	MIN_INFO_SIZE	(4 * 1024)
     53 
     54 #define	DLADM_FLOW_DB		"/etc/dladm/flowadm.conf"
     55 #define	DLADM_FLOW_DB_TMP	"/etc/dladm/flowadm.conf.new"
     56 #define	DLADM_FLOW_DB_LOCK	"/tmp/flowadm.conf.lock"
     57 
     58 #define	DLADM_FLOW_DB_PERMS	S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
     59 #define	DLADM_FLOW_DB_OWNER	UID_DLADM
     60 #define	DLADM_FLOW_DB_GROUP	GID_SYS
     61 
     62 #define	BLANK_LINE(s)	((s[0] == '\0') || (s[0] == '#') || (s[0] == '\n'))
     63 #define	MAXLINELEN	1024
     64 #define	MAXPATHLEN	1024
     65 
     66 #define	V4_PART_OF_V6(v6)	((v6)._S6_un._S6_u32[3])
     67 
     68 /* database file parameters */
     69 static const char *BW_LIMIT = "bw_limit";
     70 static const char *PRIORITY = "priority";
     71 static const char *LOCAL_IP_ADDR = "local_ip";
     72 static const char *REMOTE_IP_ADDR = "remote_ip";
     73 static const char *TRANSPORT = "transport";
     74 static const char *LOCAL_PORT = "local_port";
     75 static const char *REMOTE_PORT = "remote_port";
     76 static const char *DSFIELD = "dsfield";
     77 
     78 /*
     79  * Open and lock the flowadm configuration file lock. The lock is
     80  * acquired as a reader (F_RDLCK) or writer (F_WRLCK).
     81  */
     82 static int
     83 i_dladm_flow_lock_db(short type)
     84 {
     85 	int lock_fd;
     86 	struct flock lock;
     87 
     88 	if ((lock_fd = open(DLADM_FLOW_DB_LOCK, O_RDWR | O_CREAT | O_TRUNC,
     89 	    DLADM_FLOW_DB_PERMS)) < 0)
     90 		return (-1);
     91 
     92 	lock.l_type = type;
     93 	lock.l_whence = SEEK_SET;
     94 	lock.l_start = 0;
     95 	lock.l_len = 0;
     96 
     97 	if (fcntl(lock_fd, F_SETLKW, &lock) < 0) {
     98 		(void) close(lock_fd);
     99 		(void) unlink(DLADM_FLOW_DB_LOCK);
    100 		return (-1);
    101 	}
    102 	return (lock_fd);
    103 }
    104 
    105 /*
    106  * Unlock and close the specified file.
    107  */
    108 static void
    109 i_dladm_flow_unlock_db(int fd)
    110 {
    111 	struct flock lock;
    112 
    113 	if (fd < 0)
    114 		return;
    115 
    116 	lock.l_type = F_UNLCK;
    117 	lock.l_whence = SEEK_SET;
    118 	lock.l_start = 0;
    119 	lock.l_len = 0;
    120 
    121 	(void) fcntl(fd, F_SETLKW, &lock);
    122 	(void) close(fd);
    123 	(void) unlink(DLADM_FLOW_DB_LOCK);
    124 }
    125 
    126 /*
    127  * Parse one line of the link flowadm DB
    128  * Returns -1 on failure, 0 on success.
    129  */
    130 dladm_status_t
    131 dladm_flow_parse_db(char *line, dld_flowinfo_t *attr)
    132 {
    133 	char		*token;
    134 	char		*value, *name = NULL;
    135 	char		*lasts = NULL;
    136 	dladm_status_t	status = DLADM_STATUS_FLOW_DB_PARSE_ERR;
    137 
    138 	bzero(attr, sizeof (*attr));
    139 
    140 	/* flow name */
    141 	if ((token = strtok_r(line, " \t", &lasts)) == NULL)
    142 		goto done;
    143 
    144 	if (strlcpy(attr->fi_flowname, token, MAXFLOWNAMELEN) >= MAXFLOWNAMELEN)
    145 		goto done;
    146 
    147 	/* resource control and flow descriptor parameters */
    148 	while ((token = strtok_r(NULL, " \t", &lasts)) != NULL) {
    149 		if ((name = strdup(token)) == NULL)
    150 			goto done;
    151 
    152 		(void) strtok(name, "=");
    153 		value = strtok(NULL, "=");
    154 		if (value == NULL)
    155 			goto done;
    156 
    157 		if (strcmp(name, "linkid") == 0) {
    158 			if ((attr->fi_linkid =
    159 			    (uint32_t)strtol(value, NULL, 10)) ==
    160 			    DATALINK_INVALID_LINKID)
    161 				goto done;
    162 
    163 		} else if (strcmp(name, BW_LIMIT) == 0) {
    164 			attr->fi_resource_props.mrp_mask |=
    165 			    MRP_MAXBW;
    166 			attr->fi_resource_props.mrp_maxbw =
    167 			    (uint64_t)strtol(value, NULL, 0);
    168 
    169 		} else if (strcmp(name, PRIORITY) == 0) {
    170 			attr->fi_resource_props.mrp_mask |= MRP_PRIORITY;
    171 			status = dladm_str2pri(value,
    172 			    &attr->fi_resource_props.mrp_priority);
    173 			if (status != DLADM_STATUS_OK)
    174 				goto done;
    175 
    176 		} else if (strcmp(name, DSFIELD) == 0) {
    177 			status = do_check_dsfield(value,
    178 			    &attr->fi_flow_desc);
    179 			if (status != DLADM_STATUS_OK)
    180 				goto done;
    181 
    182 		} else if (strcmp(name, LOCAL_IP_ADDR) == 0) {
    183 			status = do_check_ip_addr(value, B_TRUE,
    184 			    &attr->fi_flow_desc);
    185 			if (status != DLADM_STATUS_OK)
    186 				goto done;
    187 
    188 		} else if (strcmp(name, REMOTE_IP_ADDR) == 0) {
    189 			status = do_check_ip_addr(value, B_FALSE,
    190 			    &attr->fi_flow_desc);
    191 			if (status != DLADM_STATUS_OK)
    192 				goto done;
    193 
    194 		} else if (strcmp(name, TRANSPORT) == 0) {
    195 			attr->fi_flow_desc.fd_mask |= FLOW_IP_PROTOCOL;
    196 			attr->fi_flow_desc.fd_protocol =
    197 			    (uint8_t)strtol(value, NULL, 0);
    198 
    199 		} else if (strcmp(name, LOCAL_PORT) == 0) {
    200 			attr->fi_flow_desc.fd_mask |= FLOW_ULP_PORT_LOCAL;
    201 			attr->fi_flow_desc.fd_local_port =
    202 			    (uint16_t)strtol(value, NULL, 10);
    203 			attr->fi_flow_desc.fd_local_port =
    204 			    htons(attr->fi_flow_desc.fd_local_port);
    205 		} else if (strcmp(name, REMOTE_PORT) == 0) {
    206 			attr->fi_flow_desc.fd_mask |= FLOW_ULP_PORT_REMOTE;
    207 			attr->fi_flow_desc.fd_remote_port =
    208 			    (uint16_t)strtol(value, NULL, 10);
    209 			attr->fi_flow_desc.fd_remote_port =
    210 			    htons(attr->fi_flow_desc.fd_remote_port);
    211 		}
    212 		free(name);
    213 		name = NULL;
    214 	}
    215 	if (attr->fi_linkid != DATALINK_INVALID_LINKID)
    216 		status = DLADM_STATUS_OK;
    217 done:
    218 	free(name);
    219 	return (status);
    220 }
    221 
    222 #define	FPRINTF_ERR(fcall) if ((fcall) < 0) return (-1);
    223 
    224 /*
    225  * Write the attribute of a group to the specified file. Returns 0 on
    226  * success, -1 on failure.
    227  */
    228 static int
    229 i_dladm_flow_fput_grp(FILE *fp, dld_flowinfo_t *attr)
    230 {
    231 
    232 	FPRINTF_ERR(fprintf(fp, "%s\tlinkid=%d\t",
    233 	    attr->fi_flowname, attr->fi_linkid));
    234 
    235 	/* flow policy */
    236 	if (attr->fi_resource_props.mrp_mask & MRP_MAXBW)
    237 		FPRINTF_ERR(fprintf(fp, "%s=%" PRIu64 "\t", BW_LIMIT,
    238 		    attr->fi_resource_props.mrp_maxbw));
    239 
    240 	if (attr->fi_resource_props.mrp_mask & MRP_PRIORITY)
    241 		FPRINTF_ERR(fprintf(fp, "%s=%d\t", PRIORITY,
    242 		    attr->fi_resource_props.mrp_priority));
    243 
    244 	/* flow descriptor */
    245 	if (attr->fi_flow_desc.fd_mask & FLOW_IP_DSFIELD)
    246 		FPRINTF_ERR(fprintf(fp, "%s=%x:%x\t", DSFIELD,
    247 		    attr->fi_flow_desc.fd_dsfield,
    248 		    attr->fi_flow_desc.fd_dsfield_mask));
    249 
    250 	if (attr->fi_flow_desc.fd_mask & FLOW_IP_LOCAL) {
    251 		char abuf[INET6_ADDRSTRLEN], *ap;
    252 		struct in_addr ipaddr;
    253 		int prefix_len, prefix_max;
    254 
    255 		if (attr->fi_flow_desc.fd_ipversion != 6) {
    256 			ipaddr.s_addr =
    257 			    attr->fi_flow_desc.
    258 			    fd_local_addr._S6_un._S6_u32[3];
    259 
    260 			ap = inet_ntoa(ipaddr);
    261 			prefix_max = IP_ABITS;
    262 		} else {
    263 			(void) inet_ntop(AF_INET6,
    264 			    &attr->fi_flow_desc.fd_local_addr,
    265 			    abuf, INET6_ADDRSTRLEN);
    266 
    267 			ap = abuf;
    268 			prefix_max = IPV6_ABITS;
    269 		}
    270 		(void) dladm_mask2prefixlen(
    271 		    &attr->fi_flow_desc.fd_local_netmask, prefix_max,
    272 		    &prefix_len);
    273 
    274 		FPRINTF_ERR(fprintf(fp, "%s=%s/%d\t", LOCAL_IP_ADDR,
    275 		    ap, prefix_len));
    276 	}
    277 	if (attr->fi_flow_desc.fd_mask & FLOW_IP_REMOTE) {
    278 		char abuf[INET6_ADDRSTRLEN], *ap;
    279 		struct in_addr ipaddr;
    280 		int prefix_len, prefix_max;
    281 
    282 		if (attr->fi_flow_desc.fd_ipversion != 6) {
    283 			ipaddr.s_addr =
    284 			    attr->fi_flow_desc.
    285 			    fd_remote_addr._S6_un._S6_u32[3];
    286 
    287 			ap = inet_ntoa(ipaddr);
    288 			prefix_max = IP_ABITS;
    289 		} else {
    290 			(void) inet_ntop(AF_INET6,
    291 			    &(attr->fi_flow_desc.fd_remote_addr),
    292 			    abuf, INET6_ADDRSTRLEN);
    293 
    294 			ap = abuf;
    295 			prefix_max = IPV6_ABITS;
    296 		}
    297 		(void) dladm_mask2prefixlen(
    298 		    &attr->fi_flow_desc.fd_remote_netmask, prefix_max,
    299 		    &prefix_len);
    300 
    301 		FPRINTF_ERR(fprintf(fp, "%s=%s/%d\t", REMOTE_IP_ADDR,
    302 		    ap, prefix_len));
    303 	}
    304 	if (attr->fi_flow_desc.fd_mask & FLOW_IP_PROTOCOL)
    305 		FPRINTF_ERR(fprintf(fp, "%s=%d\t", TRANSPORT,
    306 		    attr->fi_flow_desc.fd_protocol));
    307 
    308 	if (attr->fi_flow_desc.fd_mask & FLOW_ULP_PORT_LOCAL)
    309 		FPRINTF_ERR(fprintf(fp, "%s=%d\t", LOCAL_PORT,
    310 		    ntohs(attr->fi_flow_desc.fd_local_port)));
    311 
    312 	if (attr->fi_flow_desc.fd_mask & FLOW_ULP_PORT_REMOTE)
    313 		FPRINTF_ERR(fprintf(fp, "%s=%d\t", REMOTE_PORT,
    314 		    ntohs(attr->fi_flow_desc.fd_remote_port)));
    315 
    316 	FPRINTF_ERR(fprintf(fp, "\n"));
    317 
    318 	return (0);
    319 
    320 }
    321 
    322 static dladm_status_t
    323 i_dladm_flow_walk_rw_db(int (*fn)(void *, dld_flowinfo_t *),
    324     void *arg,
    325     const char *root)
    326 {
    327 	FILE *fp, *nfp;
    328 	int nfd, fn_rc, lock_fd;
    329 	char line[MAXLINELEN];
    330 	dld_flowinfo_t attr;
    331 	char *db_file, *tmp_db_file;
    332 	char db_file_buf[MAXPATHLEN];
    333 	char tmp_db_file_buf[MAXPATHLEN];
    334 	dladm_status_t	status = DLADM_STATUS_FLOW_DB_ERR;
    335 
    336 	if (root == NULL) {
    337 		db_file = DLADM_FLOW_DB;
    338 		tmp_db_file = DLADM_FLOW_DB_TMP;
    339 	} else {
    340 		(void) snprintf(db_file_buf, MAXPATHLEN, "%s%s", root,
    341 		    DLADM_FLOW_DB);
    342 		(void) snprintf(tmp_db_file_buf, MAXPATHLEN, "%s%s", root,
    343 		    DLADM_FLOW_DB_TMP);
    344 		db_file = db_file_buf;
    345 		tmp_db_file = tmp_db_file_buf;
    346 	}
    347 
    348 	if ((lock_fd = i_dladm_flow_lock_db(F_WRLCK)) < 0)
    349 		return (DLADM_STATUS_FLOW_DB_ERR);
    350 
    351 	if ((fp = fopen(db_file, "r")) == NULL) {
    352 		i_dladm_flow_unlock_db(lock_fd);
    353 		return (DLADM_STATUS_FLOW_DB_OPEN_ERR);
    354 	}
    355 
    356 	if ((nfd = open(tmp_db_file, O_WRONLY|O_CREAT|O_TRUNC,
    357 	    DLADM_FLOW_DB_PERMS)) == -1) {
    358 		(void) fclose(fp);
    359 		i_dladm_flow_unlock_db(lock_fd);
    360 		return (DLADM_STATUS_FLOW_DB_OPEN_ERR);
    361 	}
    362 
    363 	if ((nfp = fdopen(nfd, "w")) == NULL) {
    364 		(void) close(nfd);
    365 		(void) fclose(fp);
    366 		(void) unlink(tmp_db_file);
    367 		i_dladm_flow_unlock_db(lock_fd);
    368 		return (DLADM_STATUS_FLOW_DB_OPEN_ERR);
    369 	}
    370 
    371 	while (fgets(line, MAXLINELEN, fp) != NULL) {
    372 
    373 		/* skip comments */
    374 		if (BLANK_LINE(line)) {
    375 			if (fputs(line, nfp) == EOF)
    376 				goto failed;
    377 			continue;
    378 		}
    379 		(void) strtok(line, " \n");
    380 
    381 		if ((status = dladm_flow_parse_db(line, &attr)) !=
    382 		    DLADM_STATUS_OK)
    383 			goto failed;
    384 
    385 		fn_rc = fn(arg, &attr);
    386 
    387 		switch (fn_rc) {
    388 		case -1:
    389 			/* failure, stop walking */
    390 			goto failed;
    391 		case 0:
    392 			/*
    393 			 * Success, write group attributes, which could
    394 			 * have been modified by fn().
    395 			 */
    396 			if (i_dladm_flow_fput_grp(nfp, &attr) != 0)
    397 				goto failed;
    398 			break;
    399 		case 1:
    400 			/* skip current group */
    401 			break;
    402 		}
    403 	}
    404 	if (fchmod(nfd, DLADM_FLOW_DB_PERMS) == -1)
    405 		goto failed;
    406 
    407 	if (fchown(nfd, DLADM_FLOW_DB_OWNER, DLADM_FLOW_DB_GROUP) == -1)
    408 		goto failed;
    409 
    410 	if (fflush(nfp) == EOF)
    411 		goto failed;
    412 
    413 	(void) fclose(fp);
    414 	(void) fclose(nfp);
    415 
    416 	if (rename(tmp_db_file, db_file) == -1) {
    417 		(void) unlink(tmp_db_file);
    418 		i_dladm_flow_unlock_db(lock_fd);
    419 		return (DLADM_STATUS_FLOW_DB_ERR);
    420 	}
    421 	i_dladm_flow_unlock_db(lock_fd);
    422 	return (DLADM_STATUS_OK);
    423 
    424 failed:
    425 	(void) fclose(fp);
    426 	(void) fclose(nfp);
    427 	(void) unlink(tmp_db_file);
    428 	i_dladm_flow_unlock_db(lock_fd);
    429 
    430 	return (status);
    431 }
    432 
    433 /*
    434  * Remove existing flow from DB.
    435  */
    436 
    437 typedef struct remove_db_state {
    438 	dld_flowinfo_t	rs_newattr;
    439 	dld_flowinfo_t	rs_oldattr;
    440 	boolean_t	rs_found;
    441 } remove_db_state_t;
    442 
    443 static int
    444 i_dladm_flow_remove_db_fn(void *arg, dld_flowinfo_t *grp)
    445 {
    446 	remove_db_state_t *state = (remove_db_state_t *)arg;
    447 	dld_flowinfo_t *attr = &state->rs_newattr;
    448 
    449 	if ((strcmp(grp->fi_flowname, attr->fi_flowname)) != 0)
    450 		return (0);
    451 	else {
    452 		bcopy(grp, &state->rs_oldattr,
    453 		    sizeof (dld_flowinfo_t));
    454 		state->rs_found = B_TRUE;
    455 		return (1);
    456 	}
    457 }
    458 
    459 /* ARGSUSED */
    460 static int
    461 i_dladm_flow_remove_db(remove_db_state_t *state, const char *root)
    462 {
    463 	if (i_dladm_flow_walk_rw_db(i_dladm_flow_remove_db_fn, state, root)
    464 	    != 0)
    465 		return (-1);
    466 
    467 	if (!state->rs_found) {
    468 		errno = ENOENT;
    469 		return (-1);
    470 	}
    471 
    472 	return (0);
    473 }
    474 
    475 /*
    476  * Create a flow in the DB.
    477  */
    478 
    479 typedef struct modify_db_state {
    480 	dld_flowinfo_t	ms_newattr;
    481 	dld_flowinfo_t	ms_oldattr;
    482 	boolean_t	ms_found;
    483 } modify_db_state_t;
    484 
    485 static dladm_status_t
    486 i_dladm_flow_create_db(dld_flowinfo_t *attr, const char *root)
    487 {
    488 	FILE 	*fp;
    489 	char 	line[MAXLINELEN];
    490 	char	*db_file;
    491 	char	db_file_buf[MAXPATHLEN];
    492 	int	lock_fd;
    493 	dladm_status_t	status = DLADM_STATUS_OK;
    494 
    495 	if (root == NULL) {
    496 		db_file = DLADM_FLOW_DB;
    497 	} else {
    498 		(void) snprintf(db_file_buf, MAXPATHLEN, "%s%s", root,
    499 		    DLADM_FLOW_DB);
    500 		db_file = db_file_buf;
    501 	}
    502 
    503 	if ((lock_fd = i_dladm_flow_lock_db(F_WRLCK)) < 0)
    504 		return (DLADM_STATUS_FLOW_DB_ERR);
    505 
    506 	if ((fp = fopen(db_file, "r+")) == NULL &&
    507 	    (fp = fopen(db_file, "w")) == NULL) {
    508 		i_dladm_flow_unlock_db(lock_fd);
    509 		return (DLADM_STATUS_FLOW_DB_OPEN_ERR);
    510 	}
    511 
    512 	/* look for existing group with same flowname */
    513 	while (fgets(line, MAXLINELEN, fp) != NULL) {
    514 		char *holder, *lasts;
    515 
    516 		/* skip comments */
    517 		if (BLANK_LINE(line))
    518 			continue;
    519 
    520 		/* ignore corrupted lines */
    521 		holder = strtok_r(line, " \t", &lasts);
    522 		if (holder == NULL)
    523 			continue;
    524 
    525 		/* flow id */
    526 		if (strcmp(holder, attr->fi_flowname) == 0) {
    527 			/* group with flow id already exists */
    528 			status = DLADM_STATUS_PERSIST_FLOW_EXISTS;
    529 			goto failed;
    530 		}
    531 	}
    532 	/*
    533 	 * If we get here, we've verified that no existing group with
    534 	 * the same flow id already exists. Its now time to add the new
    535 	 * group to the DB.
    536 	 */
    537 	if (i_dladm_flow_fput_grp(fp, attr) != 0)
    538 		status = DLADM_STATUS_FLOW_DB_PARSE_ERR;
    539 
    540 failed:
    541 	(void) fclose(fp);
    542 	i_dladm_flow_unlock_db(lock_fd);
    543 	return (status);
    544 }
    545 
    546 static dladm_status_t
    547 i_dladm_flow_add(dladm_handle_t handle, char *flowname, datalink_id_t linkid,
    548     flow_desc_t *flowdesc, mac_resource_props_t *mrp)
    549 {
    550 	dld_ioc_addflow_t	attr;
    551 
    552 	/* create flow */
    553 	bzero(&attr, sizeof (attr));
    554 	bcopy(flowdesc, &attr.af_flow_desc, sizeof (flow_desc_t));
    555 	if (mrp != NULL) {
    556 		bcopy(mrp, &attr.af_resource_props,
    557 		    sizeof (mac_resource_props_t));
    558 	}
    559 
    560 	(void) strlcpy(attr.af_name, flowname, sizeof (attr.af_name));
    561 	attr.af_linkid = linkid;
    562 
    563 	if (ioctl(dladm_dld_fd(handle), DLDIOC_ADDFLOW, &attr) < 0)
    564 		return (dladm_errno2status(errno));
    565 
    566 	return (DLADM_STATUS_OK);
    567 }
    568 
    569 static dladm_status_t
    570 i_dladm_flow_remove(dladm_handle_t handle, char *flowname)
    571 {
    572 	dld_ioc_removeflow_t	attr;
    573 	dladm_status_t		status = DLADM_STATUS_OK;
    574 
    575 	(void) strlcpy(attr.rf_name, flowname,
    576 	    sizeof (attr.rf_name));
    577 
    578 	if (ioctl(dladm_dld_fd(handle), DLDIOC_REMOVEFLOW, &attr) < 0)
    579 		status = dladm_errno2status(errno);
    580 
    581 	return (status);
    582 }
    583 
    584 
    585 /* ARGSUSED */
    586 dladm_status_t
    587 dladm_flow_add(dladm_handle_t handle, datalink_id_t linkid,
    588     dladm_arg_list_t *attrlist, dladm_arg_list_t *proplist, char *flowname,
    589     boolean_t tempop, const char *root)
    590 {
    591 	dld_flowinfo_t		db_attr;
    592 	flow_desc_t		flowdesc;
    593 	mac_resource_props_t	mrp;
    594 	dladm_status_t		status;
    595 
    596 	/* Extract flow attributes from attrlist */
    597 	bzero(&flowdesc, sizeof (flow_desc_t));
    598 	if (attrlist != NULL && (status = dladm_flow_attrlist_extract(attrlist,
    599 	    &flowdesc)) != DLADM_STATUS_OK) {
    600 		return (status);
    601 	}
    602 
    603 	/* Extract resource_ctl and cpu_list from proplist */
    604 	bzero(&mrp, sizeof (mac_resource_props_t));
    605 	if (proplist != NULL && (status = dladm_flow_proplist_extract(proplist,
    606 	    &mrp)) != DLADM_STATUS_OK) {
    607 		return (status);
    608 	}
    609 
    610 	/* Add flow in kernel */
    611 	status = i_dladm_flow_add(handle, flowname, linkid, &flowdesc, &mrp);
    612 	if (status != DLADM_STATUS_OK)
    613 		return (status);
    614 
    615 	/* Add flow to DB */
    616 	if (!tempop) {
    617 		bzero(&db_attr, sizeof (db_attr));
    618 		bcopy(&flowdesc, &db_attr.fi_flow_desc, sizeof (flow_desc_t));
    619 		(void) strlcpy(db_attr.fi_flowname, flowname,
    620 		    sizeof (db_attr.fi_flowname));
    621 		db_attr.fi_linkid = linkid;
    622 
    623 		if ((status = i_dladm_flow_create_db(&db_attr, root)) !=
    624 		    DLADM_STATUS_OK) {
    625 			(void) i_dladm_flow_remove(handle, flowname);
    626 			return (status);
    627 		}
    628 		/* set flow properties */
    629 		if (proplist != NULL) {
    630 			status = i_dladm_set_flow_proplist_db(handle, flowname,
    631 			    proplist);
    632 			if (status != DLADM_STATUS_OK) {
    633 				(void) i_dladm_flow_remove(handle, flowname);
    634 				return (status);
    635 			}
    636 		}
    637 	}
    638 	return (status);
    639 }
    640 
    641 /*
    642  * Remove a flow.
    643  */
    644 /* ARGSUSED */
    645 dladm_status_t
    646 dladm_flow_remove(dladm_handle_t handle, char *flowname, boolean_t tempop,
    647     const char *root)
    648 {
    649 	remove_db_state_t		state;
    650 	dladm_status_t			status = DLADM_STATUS_OK;
    651 	dladm_status_t			s = DLADM_STATUS_OK;
    652 
    653 	/* remove flow */
    654 	status = i_dladm_flow_remove(handle, flowname);
    655 	if ((status != DLADM_STATUS_OK) &&
    656 	    (tempop || status != DLADM_STATUS_NOTFOUND))
    657 		goto done;
    658 
    659 	/* remove flow from DB */
    660 	if (!tempop) {
    661 		bzero(&state, sizeof (state));
    662 		(void) strlcpy(state.rs_newattr.fi_flowname, flowname,
    663 		    sizeof (state.rs_newattr.fi_flowname));
    664 		state.rs_found = B_FALSE;
    665 
    666 		/* flow DB */
    667 		if (i_dladm_flow_remove_db(&state, root) < 0) {
    668 			s = dladm_errno2status(errno);
    669 			goto done;
    670 		}
    671 
    672 		/* flow prop DB */
    673 		s = dladm_set_flowprop(handle, flowname, NULL, NULL, 0,
    674 		    DLADM_OPT_PERSIST, NULL);
    675 	}
    676 
    677 done:
    678 	if (!tempop) {
    679 		if (s == DLADM_STATUS_OK) {
    680 			if (status == DLADM_STATUS_NOTFOUND)
    681 				status = s;
    682 		} else {
    683 			if (s != DLADM_STATUS_NOTFOUND)
    684 				status = s;
    685 		}
    686 	}
    687 	return (status);
    688 }
    689 
    690 /*
    691  * Get an existing flow in the DB.
    692  */
    693 
    694 typedef struct get_db_state {
    695 	int		(*gs_fn)(dladm_handle_t, dladm_flow_attr_t *, void *);
    696 	void		*gs_arg;
    697 	datalink_id_t	gs_linkid;
    698 } get_db_state_t;
    699 
    700 /*
    701  * For each flow which matches the linkid, copy all flow information
    702  * to a new dladm_flow_attr_t structure and call the provided
    703  * function.  This is used to display perisistent flows from
    704  * the database.
    705  */
    706 
    707 static int
    708 i_dladm_flow_get_db_fn(void *arg, dld_flowinfo_t *grp)
    709 {
    710 	get_db_state_t		*state = (get_db_state_t *)arg;
    711 	dladm_flow_attr_t	attr;
    712 	dladm_handle_t		handle = NULL;
    713 
    714 	if (grp->fi_linkid == state->gs_linkid) {
    715 		attr.fa_linkid = state->gs_linkid;
    716 		bcopy(grp->fi_flowname, &attr.fa_flowname,
    717 		    sizeof (attr.fa_flowname));
    718 		bcopy(&grp->fi_flow_desc, &attr.fa_flow_desc,
    719 		    sizeof (attr.fa_flow_desc));
    720 		bcopy(&grp->fi_resource_props, &attr.fa_resource_props,
    721 		    sizeof (attr.fa_resource_props));
    722 		(void) state->gs_fn(handle, &attr, state->gs_arg);
    723 	}
    724 	return (0);
    725 }
    726 
    727 /*
    728  * Walk through the flows defined on the system and for each flow
    729  * invoke <fn>(<arg>, <flow>);
    730  * Currently used for show-flow.
    731  */
    732 /* ARGSUSED */
    733 dladm_status_t
    734 dladm_walk_flow(int (*fn)(dladm_handle_t, dladm_flow_attr_t *, void *),
    735     dladm_handle_t handle, datalink_id_t linkid, void *arg, boolean_t persist)
    736 {
    737 	dld_flowinfo_t		*flow;
    738 	int			i, bufsize;
    739 	dld_ioc_walkflow_t	*ioc = NULL;
    740 	dladm_flow_attr_t 	attr;
    741 	dladm_status_t		status = DLADM_STATUS_OK;
    742 
    743 	if (fn == NULL)
    744 		return (DLADM_STATUS_BADARG);
    745 
    746 	if (persist) {
    747 		get_db_state_t state;
    748 
    749 		bzero(&state, sizeof (state));
    750 
    751 		state.gs_linkid = linkid;
    752 		state.gs_fn = fn;
    753 		state.gs_arg = arg;
    754 		status = i_dladm_flow_walk_rw_db(i_dladm_flow_get_db_fn,
    755 		    &state, NULL);
    756 		if (status != DLADM_STATUS_OK)
    757 			return (status);
    758 	} else {
    759 		bufsize = MIN_INFO_SIZE;
    760 		if ((ioc = calloc(1, bufsize)) == NULL) {
    761 			status = dladm_errno2status(errno);
    762 			return (status);
    763 		}
    764 
    765 		ioc->wf_linkid = linkid;
    766 		ioc->wf_len = bufsize - sizeof (*ioc);
    767 
    768 		while (ioctl(dladm_dld_fd(handle), DLDIOC_WALKFLOW, ioc) < 0) {
    769 			if (errno == ENOSPC) {
    770 				bufsize *= 2;
    771 				ioc = realloc(ioc, bufsize);
    772 				if (ioc != NULL) {
    773 					ioc->wf_linkid = linkid;
    774 					ioc->wf_len = bufsize - sizeof (*ioc);
    775 					continue;
    776 				}
    777 			}
    778 			goto bail;
    779 		}
    780 
    781 		flow = (dld_flowinfo_t *)(void *)(ioc + 1);
    782 		for (i = 0; i < ioc->wf_nflows; i++, flow++) {
    783 			bzero(&attr, sizeof (attr));
    784 
    785 			attr.fa_linkid = flow->fi_linkid;
    786 			bcopy(&flow->fi_flowname, &attr.fa_flowname,
    787 			    sizeof (attr.fa_flowname));
    788 			bcopy(&flow->fi_flow_desc, &attr.fa_flow_desc,
    789 			    sizeof (attr.fa_flow_desc));
    790 			bcopy(&flow->fi_resource_props, &attr.fa_resource_props,
    791 			    sizeof (attr.fa_resource_props));
    792 
    793 			if (fn(handle, &attr, arg) == DLADM_WALK_TERMINATE)
    794 				break;
    795 		}
    796 	}
    797 
    798 bail:
    799 	free(ioc);
    800 	return (status);
    801 }
    802 
    803 dladm_status_t
    804 dladm_flow_init(dladm_handle_t handle)
    805 {
    806 	flow_desc_t		flowdesc;
    807 	datalink_id_t		linkid;
    808 	dladm_status_t		s, status = DLADM_STATUS_OK;
    809 	char			name[MAXFLOWNAMELEN];
    810 	char			line[MAXLINELEN];
    811 	dld_flowinfo_t		attr;
    812 	FILE			*fp;
    813 
    814 	if ((fp = fopen(DLADM_FLOW_DB, "r")) == NULL)
    815 		return (DLADM_STATUS_DB_NOTFOUND);
    816 
    817 	while (fgets(line, MAXLINELEN, fp) != NULL) {
    818 		/* skip comments */
    819 		if (BLANK_LINE(line))
    820 			continue;
    821 
    822 		(void) strtok(line, " \n");
    823 
    824 		s = dladm_flow_parse_db(line, &attr);
    825 		if (s != DLADM_STATUS_OK) {
    826 			status = s;
    827 			continue;
    828 		}
    829 		bzero(&flowdesc, sizeof (flowdesc));
    830 		bcopy(&attr.fi_flow_desc, &flowdesc, sizeof (flow_desc_t));
    831 		(void) strlcpy(name, attr.fi_flowname,
    832 		    sizeof (attr.fi_flowname));
    833 		linkid = attr.fi_linkid;
    834 
    835 		s = i_dladm_flow_add(handle, name, linkid, &flowdesc, NULL);
    836 		if (s != DLADM_STATUS_OK)
    837 			status = s;
    838 	}
    839 	s = i_dladm_init_flowprop_db(handle);
    840 	if (s != DLADM_STATUS_OK)
    841 		status = s;
    842 
    843 	(void) fclose(fp);
    844 	return (status);
    845 }
    846 
    847 dladm_status_t
    848 dladm_prefixlen2mask(int prefixlen, int maxlen, uchar_t *mask)
    849 {
    850 	if (prefixlen < 0 || prefixlen > maxlen)
    851 		return (DLADM_STATUS_BADARG);
    852 
    853 	while (prefixlen > 0) {
    854 		if (prefixlen >= 8) {
    855 			*mask++ = 0xFF;
    856 			prefixlen -= 8;
    857 			continue;
    858 		}
    859 		*mask |= 1 << (8 - prefixlen);
    860 		prefixlen--;
    861 	}
    862 	return (DLADM_STATUS_OK);
    863 }
    864 
    865 dladm_status_t
    866 dladm_mask2prefixlen(in6_addr_t *mask, int plen, int *prefixlen)
    867 {
    868 	int		bits;
    869 	int		i, end;
    870 
    871 	switch (plen) {
    872 	case IP_ABITS:
    873 		end = 3;
    874 		break;
    875 	case IPV6_ABITS:
    876 		end = 0;
    877 		break;
    878 	default:
    879 		return (DLADM_STATUS_BADARG);
    880 	}
    881 
    882 	for (i = 3; i >= end; i--) {
    883 		if (mask->_S6_un._S6_u32[i] == 0) {
    884 			plen -= 32;
    885 			continue;
    886 		}
    887 		bits = ffs(ntohl(mask->_S6_un._S6_u32[i])) - 1;
    888 		if (bits == 0)
    889 			break;
    890 		plen -= bits;
    891 	}
    892 	*prefixlen = plen;
    893 	return (DLADM_STATUS_OK);
    894 }
    895