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