18275SEric Cheng /*
28275SEric Cheng  * CDDL HEADER START
38275SEric Cheng  *
48275SEric Cheng  * The contents of this file are subject to the terms of the
58275SEric Cheng  * Common Development and Distribution License (the "License").
68275SEric Cheng  * You may not use this file except in compliance with the License.
78275SEric Cheng  *
88275SEric Cheng  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
98275SEric Cheng  * or http://www.opensolaris.org/os/licensing.
108275SEric Cheng  * See the License for the specific language governing permissions
118275SEric Cheng  * and limitations under the License.
128275SEric Cheng  *
138275SEric Cheng  * When distributing Covered Code, include this CDDL HEADER in each
148275SEric Cheng  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
158275SEric Cheng  * If applicable, add the following below this CDDL HEADER, with the
168275SEric Cheng  * fields enclosed by brackets "[]" replaced with your own identifying
178275SEric Cheng  * information: Portions Copyright [yyyy] [name of copyright owner]
188275SEric Cheng  *
198275SEric Cheng  * CDDL HEADER END
208275SEric Cheng  */
218275SEric Cheng /*
22*8558SGirish.Moodalbail@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
238275SEric Cheng  * Use is subject to license terms.
248275SEric Cheng  */
258275SEric Cheng 
268275SEric Cheng #include <stdio.h>
278275SEric Cheng #include <sys/types.h>
288275SEric Cheng #include <sys/socket.h>
298275SEric Cheng #include <sys/ethernet.h>
308275SEric Cheng #include <netinet/in.h>
318275SEric Cheng #include <arpa/inet.h>
328275SEric Cheng #include <sys/stat.h>
338275SEric Cheng #include <string.h>
348275SEric Cheng #include <fcntl.h>
358275SEric Cheng #include <unistd.h>
368275SEric Cheng #include <stropts.h>
378275SEric Cheng #include <stdlib.h>
388275SEric Cheng #include <errno.h>
398275SEric Cheng #include <strings.h>
408275SEric Cheng #include <libintl.h>
418275SEric Cheng #include <netdb.h>
428275SEric Cheng #include <net/if_types.h>
438275SEric Cheng #include <net/if_dl.h>
448275SEric Cheng #include <inet/ip.h>
458275SEric Cheng #include <inet/ip6.h>
468275SEric Cheng #include <libdlflow.h>
478275SEric Cheng #include <libdlflow_impl.h>
488275SEric Cheng #include <libdladm_impl.h>
498275SEric Cheng 
508275SEric Cheng /* minimum buffer size for DLDIOCWALKFLOW */
518275SEric Cheng #define	MIN_INFO_SIZE	(4 * 1024)
528275SEric Cheng 
538275SEric Cheng #define	DLADM_FLOW_DB		"/etc/dladm/flowadm.conf"
548275SEric Cheng #define	DLADM_FLOW_DB_TMP	"/etc/dladm/flowadm.conf.new"
558275SEric Cheng #define	DLADM_FLOW_DB_LOCK	"/tmp/flowadm.conf.lock"
568275SEric Cheng 
578275SEric Cheng #define	DLADM_FLOW_DB_PERMS	S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
588275SEric Cheng #define	DLADM_FLOW_DB_OWNER	UID_DLADM
598275SEric Cheng #define	DLADM_FLOW_DB_GROUP	GID_SYS
608275SEric Cheng 
618275SEric Cheng #define	BLANK_LINE(s)	((s[0] == '\0') || (s[0] == '#') || (s[0] == '\n'))
628275SEric Cheng #define	MAXLINELEN	1024
638275SEric Cheng #define	MAXPATHLEN	1024
648275SEric Cheng 
658275SEric Cheng #define	V4_PART_OF_V6(v6)	((v6)._S6_un._S6_u32[3])
668275SEric Cheng 
678275SEric Cheng /* database file parameters */
688275SEric Cheng static const char *BW_LIMIT = "bw_limit";
698275SEric Cheng static const char *PRIORITY = "priority";
708275SEric Cheng static const char *LOCAL_IP_ADDR = "local_ip";
718275SEric Cheng static const char *REMOTE_IP_ADDR = "remote_ip";
728275SEric Cheng static const char *TRANSPORT = "transport";
738275SEric Cheng static const char *LOCAL_PORT = "local_port";
748275SEric Cheng static const char *DSFIELD = "dsfield";
758275SEric Cheng 
768275SEric Cheng /*
778275SEric Cheng  * Open and lock the flowadm configuration file lock. The lock is
788275SEric Cheng  * acquired as a reader (F_RDLCK) or writer (F_WRLCK).
798275SEric Cheng  */
808275SEric Cheng static int
818275SEric Cheng i_dladm_flow_lock_db(short type)
828275SEric Cheng {
838275SEric Cheng 	int lock_fd;
848275SEric Cheng 	struct flock lock;
858275SEric Cheng 
868275SEric Cheng 	if ((lock_fd = open(DLADM_FLOW_DB_LOCK, O_RDWR | O_CREAT | O_TRUNC,
878275SEric Cheng 	    DLADM_FLOW_DB_PERMS)) < 0)
888275SEric Cheng 		return (-1);
898275SEric Cheng 
908275SEric Cheng 	lock.l_type = type;
918275SEric Cheng 	lock.l_whence = SEEK_SET;
928275SEric Cheng 	lock.l_start = 0;
938275SEric Cheng 	lock.l_len = 0;
948275SEric Cheng 
958275SEric Cheng 	if (fcntl(lock_fd, F_SETLKW, &lock) < 0) {
968275SEric Cheng 		(void) close(lock_fd);
978275SEric Cheng 		(void) unlink(DLADM_FLOW_DB_LOCK);
988275SEric Cheng 		return (-1);
998275SEric Cheng 	}
1008275SEric Cheng 	return (lock_fd);
1018275SEric Cheng }
1028275SEric Cheng 
1038275SEric Cheng /*
1048275SEric Cheng  * Unlock and close the specified file.
1058275SEric Cheng  */
1068275SEric Cheng static void
1078275SEric Cheng i_dladm_flow_unlock_db(int fd)
1088275SEric Cheng {
1098275SEric Cheng 	struct flock lock;
1108275SEric Cheng 
1118275SEric Cheng 	if (fd < 0)
1128275SEric Cheng 		return;
1138275SEric Cheng 
1148275SEric Cheng 	lock.l_type = F_UNLCK;
1158275SEric Cheng 	lock.l_whence = SEEK_SET;
1168275SEric Cheng 	lock.l_start = 0;
1178275SEric Cheng 	lock.l_len = 0;
1188275SEric Cheng 
1198275SEric Cheng 	(void) fcntl(fd, F_SETLKW, &lock);
1208275SEric Cheng 	(void) close(fd);
1218275SEric Cheng 	(void) unlink(DLADM_FLOW_DB_LOCK);
1228275SEric Cheng }
1238275SEric Cheng 
1248275SEric Cheng /*
1258275SEric Cheng  * Parse one line of the link flowadm DB
1268275SEric Cheng  * Returns -1 on failure, 0 on success.
1278275SEric Cheng  */
1288275SEric Cheng dladm_status_t
1298275SEric Cheng dladm_flow_parse_db(char *line, dld_flowinfo_t *attr)
1308275SEric Cheng {
1318275SEric Cheng 	char		*token;
1328275SEric Cheng 	char		*value, *name = NULL;
1338275SEric Cheng 	char		*endp = NULL;
1348275SEric Cheng 	char		*lasts = NULL;
1358275SEric Cheng 	dladm_status_t	status = DLADM_STATUS_FLOW_DB_PARSE_ERR;
1368275SEric Cheng 
1378275SEric Cheng 	bzero(attr, sizeof (*attr));
1388275SEric Cheng 
1398275SEric Cheng 	/* flow name */
1408275SEric Cheng 	if ((token = strtok_r(line, " \t", &lasts)) == NULL)
1418275SEric Cheng 		goto done;
1428275SEric Cheng 
143*8558SGirish.Moodalbail@Sun.COM 	if (strlcpy(attr->fi_flowname, token, MAXFLOWNAMELEN) >= MAXFLOWNAMELEN)
1448275SEric Cheng 		goto done;
1458275SEric Cheng 
1468275SEric Cheng 	/* resource control and flow descriptor parameters */
1478275SEric Cheng 	while ((token = strtok_r(NULL, " \t", &lasts)) != NULL) {
1488275SEric Cheng 		if ((name = strdup(token)) == NULL)
1498275SEric Cheng 			goto done;
1508275SEric Cheng 
1518275SEric Cheng 		(void) strtok(name, "=");
1528275SEric Cheng 		value = strtok(NULL, "=");
1538275SEric Cheng 		if (value == NULL)
1548275SEric Cheng 			goto done;
1558275SEric Cheng 
1568275SEric Cheng 		if (strcmp(name, "linkid") == 0) {
1578275SEric Cheng 			if ((attr->fi_linkid =
1588275SEric Cheng 			    (uint32_t)strtol(value, &endp, 10)) ==
1598275SEric Cheng 			    DATALINK_INVALID_LINKID)
1608275SEric Cheng 				goto done;
1618275SEric Cheng 
1628275SEric Cheng 		} else if (strcmp(name, BW_LIMIT) == 0) {
1638275SEric Cheng 			attr->fi_resource_props.mrp_mask |=
1648275SEric Cheng 			    MRP_MAXBW;
1658275SEric Cheng 			attr->fi_resource_props.mrp_maxbw =
1668275SEric Cheng 			    (uint64_t)strtol(value, &endp, 0);
1678275SEric Cheng 
1688275SEric Cheng 		} else if (strcmp(name, PRIORITY) == 0) {
1698275SEric Cheng 			attr->fi_resource_props.mrp_mask |= MRP_PRIORITY;
1708275SEric Cheng 			status = dladm_str2pri(value,
1718275SEric Cheng 			    &attr->fi_resource_props.mrp_priority);
1728275SEric Cheng 			if (status != DLADM_STATUS_OK)
1738275SEric Cheng 				goto done;
1748275SEric Cheng 
1758275SEric Cheng 		} else if (strcmp(name, DSFIELD) == 0) {
1768275SEric Cheng 			status = do_check_dsfield(value,
1778275SEric Cheng 			    &attr->fi_flow_desc);
1788275SEric Cheng 			if (status != DLADM_STATUS_OK)
1798275SEric Cheng 				goto done;
1808275SEric Cheng 
1818275SEric Cheng 		} else if (strcmp(name, LOCAL_IP_ADDR) == 0) {
1828275SEric Cheng 			status = do_check_ip_addr(value, B_TRUE,
1838275SEric Cheng 			    &attr->fi_flow_desc);
1848275SEric Cheng 			if (status != DLADM_STATUS_OK)
1858275SEric Cheng 				goto done;
1868275SEric Cheng 
1878275SEric Cheng 		} else if (strcmp(name, REMOTE_IP_ADDR) == 0) {
1888275SEric Cheng 			status = do_check_ip_addr(value, B_FALSE,
1898275SEric Cheng 			    &attr->fi_flow_desc);
1908275SEric Cheng 			if (status != DLADM_STATUS_OK)
1918275SEric Cheng 				goto done;
1928275SEric Cheng 
1938275SEric Cheng 		} else if (strcmp(name, TRANSPORT) == 0) {
1948275SEric Cheng 			attr->fi_flow_desc.fd_mask |= FLOW_IP_PROTOCOL;
1958275SEric Cheng 			attr->fi_flow_desc.fd_protocol =
1968275SEric Cheng 			    (uint8_t)strtol(value, &endp, 0);
1978275SEric Cheng 
1988275SEric Cheng 		} else if (strcmp(name, LOCAL_PORT) == 0) {
1998275SEric Cheng 			attr->fi_flow_desc.fd_mask |= FLOW_ULP_PORT_LOCAL;
2008275SEric Cheng 			attr->fi_flow_desc.fd_local_port =
2018275SEric Cheng 			    (uint16_t)strtol(value, &endp, 10);
2028275SEric Cheng 			attr->fi_flow_desc.fd_local_port =
2038275SEric Cheng 			    htons(attr->fi_flow_desc.fd_local_port);
2048275SEric Cheng 		}
2058275SEric Cheng 		free(name);
2068275SEric Cheng 		name = NULL;
2078275SEric Cheng 	}
2088275SEric Cheng 	if (attr->fi_linkid != DATALINK_INVALID_LINKID)
2098275SEric Cheng 		status = DLADM_STATUS_OK;
2108275SEric Cheng done:
2118275SEric Cheng 	free(name);
2128275SEric Cheng 	return (status);
2138275SEric Cheng }
2148275SEric Cheng 
2158275SEric Cheng #define	FPRINTF_ERR(fcall) if ((fcall) < 0) return (-1);
2168275SEric Cheng 
2178275SEric Cheng /*
2188275SEric Cheng  * Write the attribute of a group to the specified file. Returns 0 on
2198275SEric Cheng  * success, -1 on failure.
2208275SEric Cheng  */
2218275SEric Cheng static int
2228275SEric Cheng i_dladm_flow_fput_grp(FILE *fp, dld_flowinfo_t *attr)
2238275SEric Cheng {
2248275SEric Cheng 
2258275SEric Cheng 	FPRINTF_ERR(fprintf(fp, "%s\tlinkid=%d\t",
2268275SEric Cheng 	    attr->fi_flowname, attr->fi_linkid));
2278275SEric Cheng 
2288275SEric Cheng 	/* flow policy */
2298275SEric Cheng 	if (attr->fi_resource_props.mrp_mask & MRP_MAXBW)
2308275SEric Cheng 		FPRINTF_ERR(fprintf(fp, "%s=%" PRIu64 "\t", BW_LIMIT,
2318275SEric Cheng 		    attr->fi_resource_props.mrp_maxbw));
2328275SEric Cheng 
2338275SEric Cheng 	if (attr->fi_resource_props.mrp_mask & MRP_PRIORITY)
2348275SEric Cheng 		FPRINTF_ERR(fprintf(fp, "%s=%d\t", PRIORITY,
2358275SEric Cheng 		    attr->fi_resource_props.mrp_priority));
2368275SEric Cheng 
2378275SEric Cheng 	/* flow descriptor */
2388275SEric Cheng 	if (attr->fi_flow_desc.fd_mask & FLOW_IP_DSFIELD)
2398275SEric Cheng 		FPRINTF_ERR(fprintf(fp, "%s=%x:%x\t", DSFIELD,
2408275SEric Cheng 		    attr->fi_flow_desc.fd_dsfield,
2418275SEric Cheng 		    attr->fi_flow_desc.fd_dsfield_mask));
2428275SEric Cheng 
2438275SEric Cheng 	if (attr->fi_flow_desc.fd_mask & FLOW_IP_LOCAL) {
2448275SEric Cheng 		char abuf[INET6_ADDRSTRLEN], *ap;
2458275SEric Cheng 		struct in_addr ipaddr;
2468275SEric Cheng 		int prefix_len, prefix_max;
2478275SEric Cheng 
2488275SEric Cheng 		if (attr->fi_flow_desc.fd_ipversion != 6) {
2498275SEric Cheng 			ipaddr.s_addr =
2508275SEric Cheng 			    attr->fi_flow_desc.
2518275SEric Cheng 			    fd_local_addr._S6_un._S6_u32[3];
2528275SEric Cheng 
2538275SEric Cheng 			ap = inet_ntoa(ipaddr);
2548275SEric Cheng 			prefix_max = IP_ABITS;
2558275SEric Cheng 		} else {
2568275SEric Cheng 			(void) inet_ntop(AF_INET6,
2578275SEric Cheng 			    &attr->fi_flow_desc.fd_local_addr,
2588275SEric Cheng 			    abuf, INET6_ADDRSTRLEN);
2598275SEric Cheng 
2608275SEric Cheng 			ap = abuf;
2618275SEric Cheng 			prefix_max = IPV6_ABITS;
2628275SEric Cheng 		}
2638275SEric Cheng 		(void) dladm_mask2prefixlen(
2648275SEric Cheng 		    &attr->fi_flow_desc.fd_local_netmask, prefix_max,
2658275SEric Cheng 		    &prefix_len);
2668275SEric Cheng 
2678275SEric Cheng 		FPRINTF_ERR(fprintf(fp, "%s=%s/%d\t", LOCAL_IP_ADDR,
2688275SEric Cheng 		    ap, prefix_len));
2698275SEric Cheng 	}
2708275SEric Cheng 	if (attr->fi_flow_desc.fd_mask & FLOW_IP_REMOTE) {
2718275SEric Cheng 		char abuf[INET6_ADDRSTRLEN], *ap;
2728275SEric Cheng 		struct in_addr ipaddr;
2738275SEric Cheng 		int prefix_len, prefix_max;
2748275SEric Cheng 
2758275SEric Cheng 		if (attr->fi_flow_desc.fd_ipversion != 6) {
2768275SEric Cheng 			ipaddr.s_addr =
2778275SEric Cheng 			    attr->fi_flow_desc.
2788275SEric Cheng 			    fd_remote_addr._S6_un._S6_u32[3];
2798275SEric Cheng 
2808275SEric Cheng 			ap = inet_ntoa(ipaddr);
2818275SEric Cheng 			prefix_max = IP_ABITS;
2828275SEric Cheng 		} else {
2838275SEric Cheng 			(void) inet_ntop(AF_INET6,
2848275SEric Cheng 			    &(attr->fi_flow_desc.fd_remote_addr),
2858275SEric Cheng 			    abuf, INET6_ADDRSTRLEN);
2868275SEric Cheng 
2878275SEric Cheng 			ap = abuf;
2888275SEric Cheng 			prefix_max = IPV6_ABITS;
2898275SEric Cheng 		}
2908275SEric Cheng 		(void) dladm_mask2prefixlen(
2918275SEric Cheng 		    &attr->fi_flow_desc.fd_remote_netmask, prefix_max,
2928275SEric Cheng 		    &prefix_len);
2938275SEric Cheng 
2948275SEric Cheng 		FPRINTF_ERR(fprintf(fp, "%s=%s/%d\t", REMOTE_IP_ADDR,
2958275SEric Cheng 		    ap, prefix_len));
2968275SEric Cheng 	}
2978275SEric Cheng 	if (attr->fi_flow_desc.fd_mask & FLOW_IP_PROTOCOL)
2988275SEric Cheng 		FPRINTF_ERR(fprintf(fp, "%s=%d\t", TRANSPORT,
2998275SEric Cheng 		    attr->fi_flow_desc.fd_protocol));
3008275SEric Cheng 
3018275SEric Cheng 	if (attr->fi_flow_desc.fd_mask & FLOW_ULP_PORT_LOCAL)
3028275SEric Cheng 		FPRINTF_ERR(fprintf(fp, "%s=%d\t", LOCAL_PORT,
3038275SEric Cheng 		    ntohs(attr->fi_flow_desc.fd_local_port)));
3048275SEric Cheng 
3058275SEric Cheng 	FPRINTF_ERR(fprintf(fp, "\n"));
3068275SEric Cheng 
3078275SEric Cheng 	return (0);
3088275SEric Cheng 
3098275SEric Cheng }
3108275SEric Cheng 
3118275SEric Cheng static dladm_status_t
3128275SEric Cheng i_dladm_flow_walk_rw_db(int (*fn)(void *, dld_flowinfo_t *),
3138275SEric Cheng     void *arg,
3148275SEric Cheng     const char *root)
3158275SEric Cheng {
3168275SEric Cheng 	FILE *fp, *nfp;
3178275SEric Cheng 	int nfd, fn_rc, lock_fd;
3188275SEric Cheng 	char line[MAXLINELEN];
3198275SEric Cheng 	dld_flowinfo_t attr;
3208275SEric Cheng 	char *db_file, *tmp_db_file;
3218275SEric Cheng 	char db_file_buf[MAXPATHLEN];
3228275SEric Cheng 	char tmp_db_file_buf[MAXPATHLEN];
3238275SEric Cheng 	dladm_status_t	status = DLADM_STATUS_FLOW_DB_ERR;
3248275SEric Cheng 
3258275SEric Cheng 	if (root == NULL) {
3268275SEric Cheng 		db_file = DLADM_FLOW_DB;
3278275SEric Cheng 		tmp_db_file = DLADM_FLOW_DB_TMP;
3288275SEric Cheng 	} else {
3298275SEric Cheng 		(void) snprintf(db_file_buf, MAXPATHLEN, "%s%s", root,
3308275SEric Cheng 		    DLADM_FLOW_DB);
3318275SEric Cheng 		(void) snprintf(tmp_db_file_buf, MAXPATHLEN, "%s%s", root,
3328275SEric Cheng 		    DLADM_FLOW_DB_TMP);
3338275SEric Cheng 		db_file = db_file_buf;
3348275SEric Cheng 		tmp_db_file = tmp_db_file_buf;
3358275SEric Cheng 	}
3368275SEric Cheng 
3378275SEric Cheng 	if ((lock_fd = i_dladm_flow_lock_db(F_WRLCK)) < 0)
3388275SEric Cheng 		return (DLADM_STATUS_FLOW_DB_ERR);
3398275SEric Cheng 
3408275SEric Cheng 	if ((fp = fopen(db_file, "r")) == NULL) {
3418275SEric Cheng 		i_dladm_flow_unlock_db(lock_fd);
3428275SEric Cheng 		return (DLADM_STATUS_FLOW_DB_OPEN_ERR);
3438275SEric Cheng 	}
3448275SEric Cheng 
3458275SEric Cheng 	if ((nfd = open(tmp_db_file, O_WRONLY|O_CREAT|O_TRUNC,
3468275SEric Cheng 	    DLADM_FLOW_DB_PERMS)) == -1) {
3478275SEric Cheng 		(void) fclose(fp);
3488275SEric Cheng 		i_dladm_flow_unlock_db(lock_fd);
3498275SEric Cheng 		return (DLADM_STATUS_FLOW_DB_OPEN_ERR);
3508275SEric Cheng 	}
3518275SEric Cheng 
3528275SEric Cheng 	if ((nfp = fdopen(nfd, "w")) == NULL) {
3538275SEric Cheng 		(void) close(nfd);
3548275SEric Cheng 		(void) fclose(fp);
3558275SEric Cheng 		(void) unlink(tmp_db_file);
3568275SEric Cheng 		i_dladm_flow_unlock_db(lock_fd);
3578275SEric Cheng 		return (DLADM_STATUS_FLOW_DB_OPEN_ERR);
3588275SEric Cheng 	}
3598275SEric Cheng 
3608275SEric Cheng 	while (fgets(line, MAXLINELEN, fp) != NULL) {
3618275SEric Cheng 
3628275SEric Cheng 		/* skip comments */
3638275SEric Cheng 		if (BLANK_LINE(line)) {
3648275SEric Cheng 			if (fputs(line, nfp) == EOF)
3658275SEric Cheng 				goto failed;
3668275SEric Cheng 			continue;
3678275SEric Cheng 		}
3688275SEric Cheng 		(void) strtok(line, " \n");
3698275SEric Cheng 
3708275SEric Cheng 		if ((status = dladm_flow_parse_db(line, &attr)) !=
3718275SEric Cheng 		    DLADM_STATUS_OK)
3728275SEric Cheng 			goto failed;
3738275SEric Cheng 
3748275SEric Cheng 		fn_rc = fn(arg, &attr);
3758275SEric Cheng 
3768275SEric Cheng 		switch (fn_rc) {
3778275SEric Cheng 		case -1:
3788275SEric Cheng 			/* failure, stop walking */
3798275SEric Cheng 			goto failed;
3808275SEric Cheng 		case 0:
3818275SEric Cheng 			/*
3828275SEric Cheng 			 * Success, write group attributes, which could
3838275SEric Cheng 			 * have been modified by fn().
3848275SEric Cheng 			 */
3858275SEric Cheng 			if (i_dladm_flow_fput_grp(nfp, &attr) != 0)
3868275SEric Cheng 				goto failed;
3878275SEric Cheng 			break;
3888275SEric Cheng 		case 1:
3898275SEric Cheng 			/* skip current group */
3908275SEric Cheng 			break;
3918275SEric Cheng 		}
3928275SEric Cheng 	}
3938275SEric Cheng 	if (fchmod(nfd, DLADM_FLOW_DB_PERMS) == -1)
3948275SEric Cheng 		goto failed;
3958275SEric Cheng 
3968275SEric Cheng 	if (fchown(nfd, DLADM_FLOW_DB_OWNER, DLADM_FLOW_DB_GROUP) == -1)
3978275SEric Cheng 		goto failed;
3988275SEric Cheng 
3998275SEric Cheng 	if (fflush(nfp) == EOF)
4008275SEric Cheng 		goto failed;
4018275SEric Cheng 
4028275SEric Cheng 	(void) fclose(fp);
4038275SEric Cheng 	(void) fclose(nfp);
4048275SEric Cheng 
4058275SEric Cheng 	if (rename(tmp_db_file, db_file) == -1) {
4068275SEric Cheng 		(void) unlink(tmp_db_file);
4078275SEric Cheng 		i_dladm_flow_unlock_db(lock_fd);
4088275SEric Cheng 		return (DLADM_STATUS_FLOW_DB_ERR);
4098275SEric Cheng 	}
4108275SEric Cheng 	i_dladm_flow_unlock_db(lock_fd);
4118275SEric Cheng 	return (DLADM_STATUS_OK);
4128275SEric Cheng 
4138275SEric Cheng failed:
4148275SEric Cheng 	(void) fclose(fp);
4158275SEric Cheng 	(void) fclose(nfp);
4168275SEric Cheng 	(void) unlink(tmp_db_file);
4178275SEric Cheng 	i_dladm_flow_unlock_db(lock_fd);
4188275SEric Cheng 
4198275SEric Cheng 	return (status);
4208275SEric Cheng }
4218275SEric Cheng 
4228275SEric Cheng /*
4238275SEric Cheng  * Remove existing flow from DB.
4248275SEric Cheng  */
4258275SEric Cheng 
4268275SEric Cheng typedef struct remove_db_state {
4278275SEric Cheng 	dld_flowinfo_t	rs_newattr;
4288275SEric Cheng 	dld_flowinfo_t	rs_oldattr;
4298275SEric Cheng 	boolean_t	rs_found;
4308275SEric Cheng } remove_db_state_t;
4318275SEric Cheng 
4328275SEric Cheng static int
4338275SEric Cheng i_dladm_flow_remove_db_fn(void *arg, dld_flowinfo_t *grp)
4348275SEric Cheng {
4358275SEric Cheng 	remove_db_state_t *state = (remove_db_state_t *)arg;
4368275SEric Cheng 	dld_flowinfo_t *attr = &state->rs_newattr;
4378275SEric Cheng 
4388275SEric Cheng 	if ((strcmp(grp->fi_flowname, attr->fi_flowname)) != 0)
4398275SEric Cheng 		return (0);
4408275SEric Cheng 	else {
4418275SEric Cheng 		bcopy(grp, &state->rs_oldattr,
4428275SEric Cheng 		    sizeof (dld_flowinfo_t));
4438275SEric Cheng 		state->rs_found = B_TRUE;
4448275SEric Cheng 		return (1);
4458275SEric Cheng 	}
4468275SEric Cheng }
4478275SEric Cheng 
4488275SEric Cheng /* ARGSUSED */
4498275SEric Cheng static int
4508275SEric Cheng i_dladm_flow_remove_db(remove_db_state_t *state, const char *root)
4518275SEric Cheng {
4528275SEric Cheng 	if (i_dladm_flow_walk_rw_db(i_dladm_flow_remove_db_fn, state, root)
4538275SEric Cheng 	    != 0)
4548275SEric Cheng 		return (-1);
4558275SEric Cheng 
4568275SEric Cheng 	if (!state->rs_found) {
4578275SEric Cheng 		errno = ENOENT;
4588275SEric Cheng 		return (-1);
4598275SEric Cheng 	}
4608275SEric Cheng 
4618275SEric Cheng 	return (0);
4628275SEric Cheng }
4638275SEric Cheng 
4648275SEric Cheng /*
4658275SEric Cheng  * Create a flow in the DB.
4668275SEric Cheng  */
4678275SEric Cheng 
4688275SEric Cheng typedef struct modify_db_state {
4698275SEric Cheng 	dld_flowinfo_t	ms_newattr;
4708275SEric Cheng 	dld_flowinfo_t	ms_oldattr;
4718275SEric Cheng 	boolean_t	ms_found;
4728275SEric Cheng } modify_db_state_t;
4738275SEric Cheng 
4748275SEric Cheng static dladm_status_t
4758275SEric Cheng i_dladm_flow_create_db(dld_flowinfo_t *attr, const char *root)
4768275SEric Cheng {
4778275SEric Cheng 	FILE 	*fp;
4788275SEric Cheng 	char 	line[MAXLINELEN];
4798275SEric Cheng 	char	*db_file;
4808275SEric Cheng 	char	db_file_buf[MAXPATHLEN];
4818275SEric Cheng 	int	lock_fd;
4828275SEric Cheng 	dladm_status_t	status = DLADM_STATUS_OK;
4838275SEric Cheng 
4848275SEric Cheng 	if (root == NULL) {
4858275SEric Cheng 		db_file = DLADM_FLOW_DB;
4868275SEric Cheng 	} else {
4878275SEric Cheng 		(void) snprintf(db_file_buf, MAXPATHLEN, "%s%s", root,
4888275SEric Cheng 		    DLADM_FLOW_DB);
4898275SEric Cheng 		db_file = db_file_buf;
4908275SEric Cheng 	}
4918275SEric Cheng 
4928275SEric Cheng 	if ((lock_fd = i_dladm_flow_lock_db(F_WRLCK)) < 0)
4938275SEric Cheng 		return (DLADM_STATUS_FLOW_DB_ERR);
4948275SEric Cheng 
4958275SEric Cheng 	if ((fp = fopen(db_file, "r+")) == NULL &&
4968275SEric Cheng 	    (fp = fopen(db_file, "w")) == NULL) {
4978275SEric Cheng 		i_dladm_flow_unlock_db(lock_fd);
4988275SEric Cheng 		return (DLADM_STATUS_FLOW_DB_OPEN_ERR);
4998275SEric Cheng 	}
5008275SEric Cheng 
5018275SEric Cheng 	/* look for existing group with same flowname */
5028275SEric Cheng 	while (fgets(line, MAXLINELEN, fp) != NULL) {
5038275SEric Cheng 		char *holder, *lasts;
5048275SEric Cheng 
5058275SEric Cheng 		/* skip comments */
5068275SEric Cheng 		if (BLANK_LINE(line))
5078275SEric Cheng 			continue;
5088275SEric Cheng 
5098275SEric Cheng 		/* ignore corrupted lines */
5108275SEric Cheng 		holder = strtok_r(line, " \t", &lasts);
5118275SEric Cheng 		if (holder == NULL)
5128275SEric Cheng 			continue;
5138275SEric Cheng 
5148275SEric Cheng 		/* flow id */
5158275SEric Cheng 		if (strcmp(holder, attr->fi_flowname) == 0) {
5168275SEric Cheng 			/* group with flow id already exists */
5178275SEric Cheng 			status = DLADM_STATUS_PERSIST_FLOW_EXISTS;
5188275SEric Cheng 			goto failed;
5198275SEric Cheng 		}
5208275SEric Cheng 	}
5218275SEric Cheng 	/*
5228275SEric Cheng 	 * If we get here, we've verified that no existing group with
5238275SEric Cheng 	 * the same flow id already exists. Its now time to add the new
5248275SEric Cheng 	 * group to the DB.
5258275SEric Cheng 	 */
5268275SEric Cheng 	if (i_dladm_flow_fput_grp(fp, attr) != 0)
5278275SEric Cheng 		status = DLADM_STATUS_FLOW_DB_PARSE_ERR;
5288275SEric Cheng 
5298275SEric Cheng failed:
5308275SEric Cheng 	(void) fclose(fp);
5318275SEric Cheng 	i_dladm_flow_unlock_db(lock_fd);
5328275SEric Cheng 	return (status);
5338275SEric Cheng }
5348275SEric Cheng 
5358275SEric Cheng static dladm_status_t
5368453SAnurag.Maskey@Sun.COM i_dladm_flow_add(dladm_handle_t handle, char *flowname, datalink_id_t linkid,
5378453SAnurag.Maskey@Sun.COM     flow_desc_t *flowdesc, mac_resource_props_t *mrp)
5388275SEric Cheng {
5398275SEric Cheng 	dld_ioc_addflow_t	attr;
5408275SEric Cheng 
5418275SEric Cheng 	/* create flow */
5428275SEric Cheng 	bzero(&attr, sizeof (attr));
5438275SEric Cheng 	bcopy(flowdesc, &attr.af_flow_desc, sizeof (flow_desc_t));
5448275SEric Cheng 	if (mrp != NULL) {
5458275SEric Cheng 		bcopy(mrp, &attr.af_resource_props,
5468275SEric Cheng 		    sizeof (mac_resource_props_t));
5478275SEric Cheng 	}
5488275SEric Cheng 
5498275SEric Cheng 	(void) strlcpy(attr.af_name, flowname, sizeof (attr.af_name));
5508275SEric Cheng 	attr.af_linkid = linkid;
5518275SEric Cheng 
5528453SAnurag.Maskey@Sun.COM 	if (ioctl(dladm_dld_fd(handle), DLDIOC_ADDFLOW, &attr) < 0)
5538275SEric Cheng 		return (dladm_errno2status(errno));
5548275SEric Cheng 
5558275SEric Cheng 	return (DLADM_STATUS_OK);
5568275SEric Cheng }
5578275SEric Cheng 
5588275SEric Cheng static dladm_status_t
5598453SAnurag.Maskey@Sun.COM i_dladm_flow_remove(dladm_handle_t handle, char *flowname)
5608275SEric Cheng {
5618275SEric Cheng 	dld_ioc_removeflow_t	attr;
5628275SEric Cheng 	dladm_status_t		status = DLADM_STATUS_OK;
5638275SEric Cheng 
5648275SEric Cheng 	(void) strlcpy(attr.rf_name, flowname,
5658275SEric Cheng 	    sizeof (attr.rf_name));
5668275SEric Cheng 
5678453SAnurag.Maskey@Sun.COM 	if (ioctl(dladm_dld_fd(handle), DLDIOC_REMOVEFLOW, &attr) < 0)
5688275SEric Cheng 		status = dladm_errno2status(errno);
5698275SEric Cheng 
5708275SEric Cheng 	return (status);
5718275SEric Cheng }
5728275SEric Cheng 
5738275SEric Cheng 
5748275SEric Cheng /* ARGSUSED */
5758275SEric Cheng dladm_status_t
5768453SAnurag.Maskey@Sun.COM dladm_flow_add(dladm_handle_t handle, datalink_id_t linkid,
5778453SAnurag.Maskey@Sun.COM     dladm_arg_list_t *attrlist, dladm_arg_list_t *proplist, char *flowname,
5788453SAnurag.Maskey@Sun.COM     boolean_t tempop, const char *root)
5798275SEric Cheng {
5808275SEric Cheng 	dld_flowinfo_t		db_attr;
5818275SEric Cheng 	flow_desc_t		flowdesc;
5828275SEric Cheng 	mac_resource_props_t	mrp;
5838275SEric Cheng 	dladm_status_t		status;
5848275SEric Cheng 
5858275SEric Cheng 	/* Extract flow attributes from attrlist */
5868275SEric Cheng 	bzero(&flowdesc, sizeof (flow_desc_t));
5878275SEric Cheng 	if (attrlist != NULL && (status = dladm_flow_attrlist_extract(attrlist,
5888275SEric Cheng 	    &flowdesc)) != DLADM_STATUS_OK) {
5898275SEric Cheng 		return (status);
5908275SEric Cheng 	}
5918275SEric Cheng 
5928275SEric Cheng 	/* Extract resource_ctl and cpu_list from proplist */
5938275SEric Cheng 	bzero(&mrp, sizeof (mac_resource_props_t));
5948275SEric Cheng 	if (proplist != NULL && (status = dladm_flow_proplist_extract(proplist,
5958275SEric Cheng 	    &mrp)) != DLADM_STATUS_OK) {
5968275SEric Cheng 		return (status);
5978275SEric Cheng 	}
5988275SEric Cheng 
5998275SEric Cheng 	/* Add flow in kernel */
6008453SAnurag.Maskey@Sun.COM 	status = i_dladm_flow_add(handle, flowname, linkid, &flowdesc, &mrp);
6018275SEric Cheng 	if (status != DLADM_STATUS_OK)
6028275SEric Cheng 		return (status);
6038275SEric Cheng 
6048275SEric Cheng 	/* Add flow to DB */
6058275SEric Cheng 	if (!tempop) {
6068275SEric Cheng 		bzero(&db_attr, sizeof (db_attr));
6078275SEric Cheng 		bcopy(&flowdesc, &db_attr.fi_flow_desc, sizeof (flow_desc_t));
6088275SEric Cheng 		(void) strlcpy(db_attr.fi_flowname, flowname,
6098275SEric Cheng 		    sizeof (db_attr.fi_flowname));
6108275SEric Cheng 		db_attr.fi_linkid = linkid;
6118275SEric Cheng 
6128275SEric Cheng 		if ((status = i_dladm_flow_create_db(&db_attr, root)) !=
6138275SEric Cheng 		    DLADM_STATUS_OK) {
6148453SAnurag.Maskey@Sun.COM 			(void) i_dladm_flow_remove(handle, flowname);
6158275SEric Cheng 			return (status);
6168275SEric Cheng 		}
6178275SEric Cheng 		/* set flow properties */
6188275SEric Cheng 		if (proplist != NULL) {
6198453SAnurag.Maskey@Sun.COM 			status = i_dladm_set_flow_proplist_db(handle, flowname,
6208275SEric Cheng 			    proplist);
6218275SEric Cheng 			if (status != DLADM_STATUS_OK) {
6228453SAnurag.Maskey@Sun.COM 				(void) i_dladm_flow_remove(handle, flowname);
6238275SEric Cheng 				return (status);
6248275SEric Cheng 			}
6258275SEric Cheng 		}
6268275SEric Cheng 	}
6278275SEric Cheng 	return (status);
6288275SEric Cheng }
6298275SEric Cheng 
6308275SEric Cheng /*
6318275SEric Cheng  * Remove a flow.
6328275SEric Cheng  */
6338275SEric Cheng /* ARGSUSED */
6348275SEric Cheng dladm_status_t
6358453SAnurag.Maskey@Sun.COM dladm_flow_remove(dladm_handle_t handle, char *flowname, boolean_t tempop,
6368275SEric Cheng     const char *root)
6378275SEric Cheng {
6388275SEric Cheng 	remove_db_state_t		state;
6398275SEric Cheng 	dladm_status_t			status = DLADM_STATUS_OK;
6408275SEric Cheng 	dladm_status_t			s = DLADM_STATUS_OK;
6418275SEric Cheng 
6428275SEric Cheng 	/* remove flow */
6438453SAnurag.Maskey@Sun.COM 	status = i_dladm_flow_remove(handle, flowname);
6448275SEric Cheng 	if ((status != DLADM_STATUS_OK) &&
6458275SEric Cheng 	    (tempop || status != DLADM_STATUS_NOTFOUND))
6468275SEric Cheng 		goto done;
6478275SEric Cheng 
6488275SEric Cheng 	/* remove flow from DB */
6498275SEric Cheng 	if (!tempop) {
6508275SEric Cheng 		bzero(&state, sizeof (state));
6518275SEric Cheng 		(void) strlcpy(state.rs_newattr.fi_flowname, flowname,
6528275SEric Cheng 		    sizeof (state.rs_newattr.fi_flowname));
6538275SEric Cheng 		state.rs_found = B_FALSE;
6548275SEric Cheng 
6558275SEric Cheng 		/* flow DB */
6568275SEric Cheng 		if (i_dladm_flow_remove_db(&state, root) < 0) {
6578275SEric Cheng 			s = dladm_errno2status(errno);
6588275SEric Cheng 			goto done;
6598275SEric Cheng 		}
6608275SEric Cheng 
6618275SEric Cheng 		/* flow prop DB */
6628453SAnurag.Maskey@Sun.COM 		s = dladm_set_flowprop(handle, flowname, NULL, NULL, 0,
6638275SEric Cheng 		    DLADM_OPT_PERSIST, NULL);
6648275SEric Cheng 	}
6658275SEric Cheng 
6668275SEric Cheng done:
6678275SEric Cheng 	if (!tempop) {
6688275SEric Cheng 		if (s == DLADM_STATUS_OK) {
6698275SEric Cheng 			if (status == DLADM_STATUS_NOTFOUND)
6708275SEric Cheng 				status = s;
6718275SEric Cheng 		} else {
6728275SEric Cheng 			if (s != DLADM_STATUS_NOTFOUND)
6738275SEric Cheng 				status = s;
6748275SEric Cheng 		}
6758275SEric Cheng 	}
6768275SEric Cheng 	return (status);
6778275SEric Cheng }
6788275SEric Cheng 
6798275SEric Cheng /*
6808275SEric Cheng  * Get an existing flow in the DB.
6818275SEric Cheng  */
6828275SEric Cheng 
6838275SEric Cheng typedef struct get_db_state {
6848275SEric Cheng 	int		(*gs_fn)(dladm_flow_attr_t *, void *);
6858275SEric Cheng 	void		*gs_arg;
6868275SEric Cheng 	datalink_id_t	gs_linkid;
6878275SEric Cheng } get_db_state_t;
6888275SEric Cheng 
6898275SEric Cheng /*
6908275SEric Cheng  * For each flow which matches the linkid, copy all flow information
6918275SEric Cheng  * to a new dladm_flow_attr_t structure and call the provided
6928275SEric Cheng  * function.  This is used to display perisistent flows from
6938275SEric Cheng  * the database.
6948275SEric Cheng  */
6958275SEric Cheng 
6968275SEric Cheng static int
6978275SEric Cheng i_dladm_flow_get_db_fn(void *arg, dld_flowinfo_t *grp)
6988275SEric Cheng {
6998275SEric Cheng 	get_db_state_t		*state = (get_db_state_t *)arg;
7008275SEric Cheng 	dladm_flow_attr_t	attr;
7018275SEric Cheng 
7028275SEric Cheng 	if (grp->fi_linkid == state->gs_linkid) {
7038275SEric Cheng 		attr.fa_linkid = state->gs_linkid;
7048275SEric Cheng 		bcopy(grp->fi_flowname, &attr.fa_flowname,
7058275SEric Cheng 		    sizeof (attr.fa_flowname));
7068275SEric Cheng 		bcopy(&grp->fi_flow_desc, &attr.fa_flow_desc,
7078275SEric Cheng 		    sizeof (attr.fa_flow_desc));
7088275SEric Cheng 		bcopy(&grp->fi_resource_props, &attr.fa_resource_props,
7098275SEric Cheng 		    sizeof (attr.fa_resource_props));
7108275SEric Cheng 		(void) state->gs_fn(&attr, state->gs_arg);
7118275SEric Cheng 	}
7128275SEric Cheng 	return (0);
7138275SEric Cheng }
7148275SEric Cheng 
7158275SEric Cheng /*
7168275SEric Cheng  * Walk through the flows defined on the system and for each flow
7178275SEric Cheng  * invoke <fn>(<arg>, <flow>);
7188275SEric Cheng  * Currently used for show-flow.
7198275SEric Cheng  */
7208275SEric Cheng /* ARGSUSED */
7218275SEric Cheng dladm_status_t
7228453SAnurag.Maskey@Sun.COM dladm_walk_flow(int (*fn)(dladm_flow_attr_t *, void *), dladm_handle_t handle,
7238275SEric Cheng     datalink_id_t linkid, void *arg, boolean_t persist)
7248275SEric Cheng {
7258275SEric Cheng 	dld_flowinfo_t		*flow;
7268453SAnurag.Maskey@Sun.COM 	int			i, bufsize;
7278275SEric Cheng 	dld_ioc_walkflow_t	*ioc = NULL;
7288275SEric Cheng 	dladm_flow_attr_t 	attr;
7298275SEric Cheng 	dladm_status_t		status = DLADM_STATUS_OK;
7308275SEric Cheng 
7318275SEric Cheng 	if (fn == NULL)
7328275SEric Cheng 		return (DLADM_STATUS_BADARG);
7338275SEric Cheng 
7348275SEric Cheng 	if (persist) {
7358275SEric Cheng 		get_db_state_t state;
7368275SEric Cheng 
7378275SEric Cheng 		bzero(&state, sizeof (state));
7388275SEric Cheng 
7398275SEric Cheng 		state.gs_linkid = linkid;
7408275SEric Cheng 		state.gs_fn = fn;
7418275SEric Cheng 		state.gs_arg = arg;
7428275SEric Cheng 		status = i_dladm_flow_walk_rw_db(i_dladm_flow_get_db_fn,
7438275SEric Cheng 		    &state, NULL);
7448275SEric Cheng 		if (status != DLADM_STATUS_OK)
7458275SEric Cheng 			return (status);
7468275SEric Cheng 	} else {
7478275SEric Cheng 		bufsize = MIN_INFO_SIZE;
7488275SEric Cheng 		if ((ioc = calloc(1, bufsize)) == NULL) {
7498275SEric Cheng 			status = dladm_errno2status(errno);
7508275SEric Cheng 			return (status);
7518275SEric Cheng 		}
7528275SEric Cheng 
7538275SEric Cheng 		ioc->wf_linkid = linkid;
7548275SEric Cheng 		ioc->wf_len = bufsize - sizeof (*ioc);
7558275SEric Cheng 
7568453SAnurag.Maskey@Sun.COM 		while (ioctl(dladm_dld_fd(handle), DLDIOC_WALKFLOW, ioc) < 0) {
7578275SEric Cheng 			if (errno == ENOSPC) {
7588275SEric Cheng 				bufsize *= 2;
7598275SEric Cheng 				ioc = realloc(ioc, bufsize);
7608275SEric Cheng 				if (ioc != NULL) {
7618275SEric Cheng 					ioc->wf_linkid = linkid;
7628275SEric Cheng 					ioc->wf_len = bufsize - sizeof (*ioc);
7638275SEric Cheng 					continue;
7648275SEric Cheng 				}
7658275SEric Cheng 			}
7668275SEric Cheng 			goto bail;
7678275SEric Cheng 		}
7688275SEric Cheng 
7698275SEric Cheng 		flow = (dld_flowinfo_t *)(void *)(ioc + 1);
7708275SEric Cheng 		for (i = 0; i < ioc->wf_nflows; i++, flow++) {
7718275SEric Cheng 			bzero(&attr, sizeof (attr));
7728275SEric Cheng 
7738275SEric Cheng 			attr.fa_linkid = flow->fi_linkid;
7748275SEric Cheng 			bcopy(&flow->fi_flowname, &attr.fa_flowname,
7758275SEric Cheng 			    sizeof (attr.fa_flowname));
7768275SEric Cheng 			bcopy(&flow->fi_flow_desc, &attr.fa_flow_desc,
7778275SEric Cheng 			    sizeof (attr.fa_flow_desc));
7788275SEric Cheng 			bcopy(&flow->fi_resource_props, &attr.fa_resource_props,
7798275SEric Cheng 			    sizeof (attr.fa_resource_props));
7808275SEric Cheng 
7818275SEric Cheng 			if (fn(&attr, arg) == DLADM_WALK_TERMINATE)
7828275SEric Cheng 				break;
7838275SEric Cheng 		}
7848275SEric Cheng 	}
7858275SEric Cheng 
7868275SEric Cheng bail:
7878275SEric Cheng 	free(ioc);
7888275SEric Cheng 	return (status);
7898275SEric Cheng }
7908275SEric Cheng 
7918275SEric Cheng dladm_status_t
7928453SAnurag.Maskey@Sun.COM dladm_flow_init(dladm_handle_t handle)
7938275SEric Cheng {
7948275SEric Cheng 	flow_desc_t		flowdesc;
7958275SEric Cheng 	datalink_id_t		linkid;
7968275SEric Cheng 	dladm_status_t		s, status = DLADM_STATUS_OK;
797*8558SGirish.Moodalbail@Sun.COM 	char			name[MAXFLOWNAMELEN];
7988275SEric Cheng 	char			line[MAXLINELEN];
7998275SEric Cheng 	dld_flowinfo_t		attr;
8008275SEric Cheng 	FILE			*fp;
8018275SEric Cheng 
8028275SEric Cheng 	if ((fp = fopen(DLADM_FLOW_DB, "r")) == NULL)
8038275SEric Cheng 		return (DLADM_STATUS_DB_NOTFOUND);
8048275SEric Cheng 
8058275SEric Cheng 	while (fgets(line, MAXLINELEN, fp) != NULL) {
8068275SEric Cheng 		/* skip comments */
8078275SEric Cheng 		if (BLANK_LINE(line))
8088275SEric Cheng 			continue;
8098275SEric Cheng 
8108275SEric Cheng 		(void) strtok(line, " \n");
8118275SEric Cheng 
8128275SEric Cheng 		s = dladm_flow_parse_db(line, &attr);
8138275SEric Cheng 		if (s != DLADM_STATUS_OK) {
8148275SEric Cheng 			status = s;
8158275SEric Cheng 			continue;
8168275SEric Cheng 		}
8178275SEric Cheng 		bzero(&flowdesc, sizeof (flowdesc));
8188275SEric Cheng 		bcopy(&attr.fi_flow_desc, &flowdesc, sizeof (flow_desc_t));
8198275SEric Cheng 		(void) strlcpy(name, attr.fi_flowname,
8208275SEric Cheng 		    sizeof (attr.fi_flowname));
8218275SEric Cheng 		linkid = attr.fi_linkid;
8228275SEric Cheng 
8238453SAnurag.Maskey@Sun.COM 		s = i_dladm_flow_add(handle, name, linkid, &flowdesc, NULL);
8248275SEric Cheng 		if (s != DLADM_STATUS_OK)
8258275SEric Cheng 			status = s;
8268275SEric Cheng 	}
8278453SAnurag.Maskey@Sun.COM 	s = i_dladm_init_flowprop_db(handle);
8288275SEric Cheng 	if (s != DLADM_STATUS_OK)
8298275SEric Cheng 		status = s;
8308275SEric Cheng 
8318275SEric Cheng 	(void) fclose(fp);
8328275SEric Cheng 	return (status);
8338275SEric Cheng }
8348275SEric Cheng 
8358275SEric Cheng dladm_status_t
8368275SEric Cheng dladm_prefixlen2mask(int prefixlen, int maxlen, uchar_t *mask)
8378275SEric Cheng {
8388275SEric Cheng 	if (prefixlen < 0 || prefixlen > maxlen)
8398275SEric Cheng 		return (DLADM_STATUS_BADARG);
8408275SEric Cheng 
8418275SEric Cheng 	while (prefixlen > 0) {
8428275SEric Cheng 		if (prefixlen >= 8) {
8438275SEric Cheng 			*mask++ = 0xFF;
8448275SEric Cheng 			prefixlen -= 8;
8458275SEric Cheng 			continue;
8468275SEric Cheng 		}
8478275SEric Cheng 		*mask |= 1 << (8 - prefixlen);
8488275SEric Cheng 		prefixlen--;
8498275SEric Cheng 	}
8508275SEric Cheng 	return (DLADM_STATUS_OK);
8518275SEric Cheng }
8528275SEric Cheng 
8538275SEric Cheng dladm_status_t
8548275SEric Cheng dladm_mask2prefixlen(in6_addr_t *mask, int plen, int *prefixlen)
8558275SEric Cheng {
8568275SEric Cheng 	int		bits;
8578275SEric Cheng 	int		i, end;
8588275SEric Cheng 
8598275SEric Cheng 	switch (plen) {
8608275SEric Cheng 	case IP_ABITS:
8618275SEric Cheng 		end = 3;
8628275SEric Cheng 		break;
8638275SEric Cheng 	case IPV6_ABITS:
8648275SEric Cheng 		end = 0;
8658275SEric Cheng 		break;
8668275SEric Cheng 	default:
8678275SEric Cheng 		return (DLADM_STATUS_BADARG);
8688275SEric Cheng 	}
8698275SEric Cheng 
8708275SEric Cheng 	for (i = 3; i >= end; i--) {
8718275SEric Cheng 		if (mask->_S6_un._S6_u32[i] == 0) {
8728275SEric Cheng 			plen -= 32;
8738275SEric Cheng 			continue;
8748275SEric Cheng 		}
8758275SEric Cheng 		bits = ffs(ntohl(mask->_S6_un._S6_u32[i])) - 1;
8768275SEric Cheng 		if (bits == 0)
8778275SEric Cheng 			break;
8788275SEric Cheng 		plen -= bits;
8798275SEric Cheng 	}
8808275SEric Cheng 	*prefixlen = plen;
8818275SEric Cheng 	return (DLADM_STATUS_OK);
8828275SEric Cheng }
883