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*12309SMichael.Lim@Sun.COM  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
238275SEric Cheng  */
248275SEric Cheng 
258275SEric Cheng #include <errno.h>
268275SEric Cheng #include <stdlib.h>
278275SEric Cheng #include <strings.h>
288275SEric Cheng #include <sys/mac_flow.h>
298275SEric Cheng #include <sys/types.h>
308275SEric Cheng #include <sys/socket.h>
318275SEric Cheng #include <netinet/in.h>
328275SEric Cheng #include <arpa/inet.h>
338275SEric Cheng #include <netdb.h>
348275SEric Cheng #include <net/if_types.h>
358275SEric Cheng #include <net/if_dl.h>
368275SEric Cheng #include <inet/ip.h>
378275SEric Cheng #include <inet/ip6.h>
388275SEric Cheng 
398275SEric Cheng #include <libdladm.h>
408275SEric Cheng #include <libdlflow.h>
418275SEric Cheng #include <libdlflow_impl.h>
428275SEric Cheng 
438275SEric Cheng /* max port number for UDP, TCP & SCTP */
448275SEric Cheng #define	MAX_PORT	65535
458275SEric Cheng 
468275SEric Cheng static fad_checkf_t do_check_local_ip;
478275SEric Cheng static fad_checkf_t do_check_remote_ip;
488275SEric Cheng static fad_checkf_t do_check_protocol;
498275SEric Cheng static fad_checkf_t do_check_local_port;
5010734SEric Cheng static fad_checkf_t do_check_remote_port;
518275SEric Cheng 
528275SEric Cheng static dladm_status_t do_check_port(char *, boolean_t, flow_desc_t *);
538275SEric Cheng 
548275SEric Cheng static fattr_desc_t	attr_table[] = {
5510734SEric Cheng 	{ "local_ip",		do_check_local_ip },
5610734SEric Cheng 	{ "remote_ip",		do_check_remote_ip },
5710734SEric Cheng 	{ "transport",		do_check_protocol },
5810734SEric Cheng 	{ "local_port",		do_check_local_port },
5910734SEric Cheng 	{ "remote_port",	do_check_remote_port },
6010734SEric Cheng 	{ "dsfield",		do_check_dsfield },
618275SEric Cheng };
628275SEric Cheng 
638275SEric Cheng #define	DLADM_MAX_FLOWATTRS	(sizeof (attr_table) / sizeof (fattr_desc_t))
648275SEric Cheng 
658275SEric Cheng static dladm_status_t
668275SEric Cheng do_check_local_ip(char *attr_val, flow_desc_t *fdesc)
678275SEric Cheng {
688275SEric Cheng 	return (do_check_ip_addr(attr_val, B_TRUE, fdesc));
698275SEric Cheng }
708275SEric Cheng 
718275SEric Cheng static dladm_status_t
728275SEric Cheng do_check_remote_ip(char *attr_val, flow_desc_t *fdesc)
738275SEric Cheng {
748275SEric Cheng 	return (do_check_ip_addr(attr_val, B_FALSE, fdesc));
758275SEric Cheng }
768275SEric Cheng 
778275SEric Cheng dladm_status_t
788275SEric Cheng do_check_ip_addr(char *addr_str, boolean_t local, flow_desc_t *fd)
798275SEric Cheng {
808275SEric Cheng 	dladm_status_t	status;
818558SGirish.Moodalbail@Sun.COM 	int		prefix_max, prefix_len = 0;
828275SEric Cheng 	char		*prefix_str, *endp = NULL;
838275SEric Cheng 	flow_mask_t	mask;
848275SEric Cheng 	in6_addr_t	*addr;
858275SEric Cheng 	uchar_t		*netmask;
868558SGirish.Moodalbail@Sun.COM 	struct in_addr	v4addr;
878558SGirish.Moodalbail@Sun.COM 	struct in6_addr	v6addr;
888558SGirish.Moodalbail@Sun.COM 	int		family;
898275SEric Cheng 
908275SEric Cheng 	if ((prefix_str = strchr(addr_str, '/')) != NULL) {
918275SEric Cheng 		*prefix_str++ = '\0';
928275SEric Cheng 		errno = 0;
938275SEric Cheng 		prefix_len = (int)strtol(prefix_str, &endp, 10);
948275SEric Cheng 		if (errno != 0 || prefix_len == 0 || *endp != '\0')
958275SEric Cheng 			return (DLADM_STATUS_INVALID_PREFIXLEN);
968275SEric Cheng 	}
978558SGirish.Moodalbail@Sun.COM 	if (inet_pton(AF_INET, addr_str, &v4addr.s_addr) == 1) {
988558SGirish.Moodalbail@Sun.COM 		family = AF_INET;
998558SGirish.Moodalbail@Sun.COM 	} else if (inet_pton(AF_INET6, addr_str, v6addr.s6_addr) == 1) {
1008558SGirish.Moodalbail@Sun.COM 		family = AF_INET6;
1018558SGirish.Moodalbail@Sun.COM 	} else {
1028275SEric Cheng 		return (DLADM_STATUS_INVALID_IP);
1038558SGirish.Moodalbail@Sun.COM 	}
1048275SEric Cheng 
1058275SEric Cheng 	mask = FLOW_IP_VERSION;
1068275SEric Cheng 	if (local) {
1078275SEric Cheng 		mask |= FLOW_IP_LOCAL;
1088275SEric Cheng 		addr = &fd->fd_local_addr;
1098275SEric Cheng 		netmask = (uchar_t *)&fd->fd_local_netmask;
1108275SEric Cheng 	} else {
1118275SEric Cheng 		mask |= FLOW_IP_REMOTE;
1128275SEric Cheng 		addr = &fd->fd_remote_addr;
1138275SEric Cheng 		netmask = (uchar_t *)&fd->fd_remote_netmask;
1148275SEric Cheng 	}
1158275SEric Cheng 
1168558SGirish.Moodalbail@Sun.COM 	if (family == AF_INET) {
1178558SGirish.Moodalbail@Sun.COM 		IN6_INADDR_TO_V4MAPPED(&v4addr, addr);
1188275SEric Cheng 		prefix_max = IP_ABITS;
1198275SEric Cheng 		fd->fd_ipversion = IPV4_VERSION;
1208275SEric Cheng 		netmask = (uchar_t *)
1218275SEric Cheng 		    &(V4_PART_OF_V6((*((in6_addr_t *)(void *)netmask))));
1228558SGirish.Moodalbail@Sun.COM 	} else {
1238558SGirish.Moodalbail@Sun.COM 		*addr = v6addr;
1248275SEric Cheng 		prefix_max = IPV6_ABITS;
1258275SEric Cheng 		fd->fd_ipversion = IPV6_VERSION;
1268275SEric Cheng 	}
1278275SEric Cheng 
1288275SEric Cheng 	if (prefix_len == 0)
1298275SEric Cheng 		prefix_len = prefix_max;
1308275SEric Cheng 
1318275SEric Cheng 	status = dladm_prefixlen2mask(prefix_len, prefix_max, netmask);
1328275SEric Cheng 
1338275SEric Cheng 	if (status != DLADM_STATUS_OK) {
1348275SEric Cheng 		return (DLADM_STATUS_INVALID_PREFIXLEN);
1358275SEric Cheng 	}
1368275SEric Cheng 
1378275SEric Cheng 	fd->fd_mask |= mask;
1388275SEric Cheng 	return (DLADM_STATUS_OK);
1398275SEric Cheng }
1408275SEric Cheng 
1418275SEric Cheng dladm_status_t
1428275SEric Cheng do_check_protocol(char *attr_val, flow_desc_t *fdesc)
1438275SEric Cheng {
1448275SEric Cheng 	uint8_t	protocol;
1458275SEric Cheng 
1468275SEric Cheng 	protocol = dladm_str2proto(attr_val);
1478275SEric Cheng 
1488275SEric Cheng 	if (protocol != 0) {
1498275SEric Cheng 		fdesc->fd_mask |= FLOW_IP_PROTOCOL;
1508275SEric Cheng 		fdesc->fd_protocol = protocol;
1518275SEric Cheng 		return (DLADM_STATUS_OK);
1528275SEric Cheng 	} else {
1538275SEric Cheng 		return (DLADM_STATUS_INVALID_PROTOCOL);
1548275SEric Cheng 	}
1558275SEric Cheng }
1568275SEric Cheng 
1578275SEric Cheng dladm_status_t
1588275SEric Cheng do_check_local_port(char *attr_val, flow_desc_t *fdesc)
1598275SEric Cheng {
1608275SEric Cheng 	return (do_check_port(attr_val, B_TRUE, fdesc));
1618275SEric Cheng }
1628275SEric Cheng 
1638275SEric Cheng dladm_status_t
16410734SEric Cheng do_check_remote_port(char *attr_val, flow_desc_t *fdesc)
16510734SEric Cheng {
16610734SEric Cheng 	return (do_check_port(attr_val, B_FALSE, fdesc));
16710734SEric Cheng }
16810734SEric Cheng 
16910734SEric Cheng dladm_status_t
1708275SEric Cheng do_check_port(char *attr_val, boolean_t local, flow_desc_t *fdesc)
1718275SEric Cheng {
1728275SEric Cheng 	char	*endp = NULL;
1738275SEric Cheng 	long	val;
1748275SEric Cheng 
17510734SEric Cheng 	val = strtol(attr_val, &endp, 10);
17610734SEric Cheng 	if (val < 1 || val > MAX_PORT || *endp != '\0')
17710734SEric Cheng 		return (DLADM_STATUS_INVALID_PORT);
1788275SEric Cheng 	if (local) {
1798275SEric Cheng 		fdesc->fd_mask |= FLOW_ULP_PORT_LOCAL;
1808275SEric Cheng 		fdesc->fd_local_port = htons((uint16_t)val);
1818275SEric Cheng 	} else {
18210734SEric Cheng 		fdesc->fd_mask |= FLOW_ULP_PORT_REMOTE;
18310734SEric Cheng 		fdesc->fd_remote_port = htons((uint16_t)val);
1848275SEric Cheng 	}
1858275SEric Cheng 
1868275SEric Cheng 	return (DLADM_STATUS_OK);
1878275SEric Cheng }
1888275SEric Cheng 
1898275SEric Cheng /*
1908275SEric Cheng  * Check for invalid and/or duplicate attribute specification
1918275SEric Cheng  */
1928275SEric Cheng static dladm_status_t
1938275SEric Cheng flow_attrlist_check(dladm_arg_list_t *attrlist)
1948275SEric Cheng {
1958275SEric Cheng 	int		i, j;
1968275SEric Cheng 	boolean_t	isset[DLADM_MAX_FLOWATTRS];
1978275SEric Cheng 	boolean_t	matched;
1988275SEric Cheng 
1998275SEric Cheng 	for (j = 0; j < DLADM_MAX_FLOWATTRS; j++)
2008275SEric Cheng 		isset[j] = B_FALSE;
2018275SEric Cheng 
2028275SEric Cheng 	for (i = 0; i < attrlist->al_count; i++) {
2038275SEric Cheng 		matched = B_FALSE;
2048275SEric Cheng 		for (j = 0; j < DLADM_MAX_FLOWATTRS; j++) {
2058275SEric Cheng 			if (strcmp(attrlist->al_info[i].ai_name,
2068275SEric Cheng 			    attr_table[j].ad_name) == 0) {
2078275SEric Cheng 				if (isset[j])
2088275SEric Cheng 					return (DLADM_STATUS_FLOW_INCOMPATIBLE);
2098275SEric Cheng 				else
2108275SEric Cheng 					isset[j] = B_TRUE;
2118275SEric Cheng 				matched = B_TRUE;
2128275SEric Cheng 			}
2138275SEric Cheng 		}
2148275SEric Cheng 		/*
2158275SEric Cheng 		 * if the attribute did not match any of the attribute in
2168275SEric Cheng 		 * attr_table, then it's an invalid attribute.
2178275SEric Cheng 		 */
2188275SEric Cheng 		if (!matched)
2198275SEric Cheng 			return (DLADM_STATUS_BADARG);
2208275SEric Cheng 	}
2218275SEric Cheng 	return (DLADM_STATUS_OK);
2228275SEric Cheng }
2238275SEric Cheng 
2248275SEric Cheng /*
2258275SEric Cheng  * Convert an attribute list to a flow_desc_t using the attribute ad_check()
2268275SEric Cheng  * functions.
2278275SEric Cheng  */
2288275SEric Cheng dladm_status_t
2298275SEric Cheng dladm_flow_attrlist_extract(dladm_arg_list_t *attrlist, flow_desc_t *flowdesc)
2308275SEric Cheng {
2318275SEric Cheng 	dladm_status_t	status = DLADM_STATUS_BADARG;
2328275SEric Cheng 	int		i;
2338275SEric Cheng 
2348275SEric Cheng 	for (i = 0; i < attrlist->al_count; i++) {
2358275SEric Cheng 		dladm_arg_info_t	*aip = &attrlist->al_info[i];
2368275SEric Cheng 		int			j;
2378275SEric Cheng 
2388275SEric Cheng 		for (j = 0; j < DLADM_MAX_FLOWATTRS; j++) {
2398275SEric Cheng 			fattr_desc_t	*adp = &attr_table[j];
2408275SEric Cheng 
2418275SEric Cheng 			if (strcasecmp(aip->ai_name, adp->ad_name) != 0)
2428275SEric Cheng 				continue;
2438275SEric Cheng 
2448275SEric Cheng 			if ((aip->ai_val == NULL) || (*aip->ai_val == NULL))
2458275SEric Cheng 				return (DLADM_STATUS_BADARG);
2468275SEric Cheng 
2478275SEric Cheng 			if (adp->ad_check != NULL)
2488275SEric Cheng 				status = adp->ad_check(*aip->ai_val, flowdesc);
2498275SEric Cheng 			else
2508275SEric Cheng 				status = DLADM_STATUS_BADARG;
2518275SEric Cheng 
2528275SEric Cheng 			if (status != DLADM_STATUS_OK)
2538275SEric Cheng 				return (status);
2548275SEric Cheng 		}
2558275SEric Cheng 	}
256*12309SMichael.Lim@Sun.COM 
257*12309SMichael.Lim@Sun.COM 	/*
258*12309SMichael.Lim@Sun.COM 	 * Make sure protocol is specified if either local or
259*12309SMichael.Lim@Sun.COM 	 * remote port is specified.
260*12309SMichael.Lim@Sun.COM 	 */
261*12309SMichael.Lim@Sun.COM 	if ((flowdesc->fd_mask &
262*12309SMichael.Lim@Sun.COM 	    (FLOW_ULP_PORT_LOCAL | FLOW_ULP_PORT_REMOTE)) != 0 &&
263*12309SMichael.Lim@Sun.COM 	    (flowdesc->fd_mask & FLOW_IP_PROTOCOL) == 0)
264*12309SMichael.Lim@Sun.COM 		return (DLADM_STATUS_PORT_NOPROTO);
265*12309SMichael.Lim@Sun.COM 
2668275SEric Cheng 	return (status);
2678275SEric Cheng }
2688275SEric Cheng 
2698275SEric Cheng void
2708275SEric Cheng dladm_free_attrs(dladm_arg_list_t *list)
2718275SEric Cheng {
2728275SEric Cheng 	dladm_free_args(list);
2738275SEric Cheng }
2748275SEric Cheng 
2758275SEric Cheng dladm_status_t
2768275SEric Cheng dladm_parse_flow_attrs(char *str, dladm_arg_list_t **listp, boolean_t novalues)
2778275SEric Cheng {
2788275SEric Cheng 
2798275SEric Cheng 	if (dladm_parse_args(str, listp, novalues)
2808275SEric Cheng 	    != DLADM_STATUS_OK)
2818275SEric Cheng 		return (DLADM_STATUS_ATTR_PARSE_ERR);
2828275SEric Cheng 
2839055SMichael.Lim@Sun.COM 	if (*listp != NULL && flow_attrlist_check(*listp)
2849055SMichael.Lim@Sun.COM 	    != DLADM_STATUS_OK) {
2858275SEric Cheng 		dladm_free_attrs(*listp);
2868275SEric Cheng 		return (DLADM_STATUS_ATTR_PARSE_ERR);
2878275SEric Cheng 	}
2888275SEric Cheng 
2898275SEric Cheng 	return (DLADM_STATUS_OK);
2908275SEric Cheng }
2918275SEric Cheng 
2928275SEric Cheng dladm_status_t
2938275SEric Cheng do_check_dsfield(char *str, flow_desc_t *fd)
2948275SEric Cheng {
2958275SEric Cheng 	char		*mask_str, *endp = NULL;
2968275SEric Cheng 	uint_t		mask = 0xff, value;
2978275SEric Cheng 
2988275SEric Cheng 	if ((mask_str = strchr(str, ':')) != NULL) {
2998275SEric Cheng 		*mask_str++ = '\0';
3008275SEric Cheng 		errno = 0;
3018275SEric Cheng 		mask = strtoul(mask_str, &endp, 16);
3028275SEric Cheng 		if (errno != 0 || mask == 0 || mask > 0xff ||
3038275SEric Cheng 		    *endp != '\0')
3048275SEric Cheng 			return (DLADM_STATUS_INVALID_DSFMASK);
3058275SEric Cheng 	}
3068275SEric Cheng 	errno = 0;
3078275SEric Cheng 	endp = NULL;
3088275SEric Cheng 	value = strtoul(str, &endp, 16);
3098275SEric Cheng 	if (errno != 0 || value == 0 || value > 0xff || *endp != '\0')
3108275SEric Cheng 		return (DLADM_STATUS_INVALID_DSF);
3118275SEric Cheng 
3128275SEric Cheng 	fd->fd_dsfield = (uint8_t)value;
3138275SEric Cheng 	fd->fd_dsfield_mask = (uint8_t)mask;
3148275SEric Cheng 	fd->fd_mask |= FLOW_IP_DSFIELD;
3158275SEric Cheng 	return (DLADM_STATUS_OK);
3168275SEric Cheng }
3178275SEric Cheng 
3188275SEric Cheng char *
3198275SEric Cheng dladm_proto2str(uint8_t protocol)
3208275SEric Cheng {
3218275SEric Cheng 	if (protocol == IPPROTO_TCP)
3228275SEric Cheng 		return ("tcp");
3238275SEric Cheng 	if (protocol == IPPROTO_UDP)
3248275SEric Cheng 		return ("udp");
3258275SEric Cheng 	if (protocol == IPPROTO_SCTP)
3268275SEric Cheng 		return ("sctp");
3278275SEric Cheng 	if (protocol == IPPROTO_ICMPV6)
3288275SEric Cheng 		return ("icmpv6");
3298275SEric Cheng 	if (protocol == IPPROTO_ICMP)
3308275SEric Cheng 		return ("icmp");
3318275SEric Cheng 	else
3328275SEric Cheng 		return ("");
3338275SEric Cheng }
3348275SEric Cheng 
3358275SEric Cheng uint8_t
3368275SEric Cheng dladm_str2proto(const char *protostr)
3378275SEric Cheng {
3388275SEric Cheng 	if (strncasecmp(protostr, "tcp", 3) == 0)
3398275SEric Cheng 		return (IPPROTO_TCP);
3408275SEric Cheng 	else if (strncasecmp(protostr, "udp", 3) == 0)
3418275SEric Cheng 		return (IPPROTO_UDP);
3428275SEric Cheng 	else if (strncasecmp(protostr, "sctp", 4) == 0)
3438275SEric Cheng 		return (IPPROTO_SCTP);
3448275SEric Cheng 	else if (strncasecmp(protostr, "icmpv6", 6) == 0)
3458275SEric Cheng 		return (IPPROTO_ICMPV6);
3468275SEric Cheng 	else if (strncasecmp(protostr, "icmp", 4) == 0)
3478275SEric Cheng 		return (IPPROTO_ICMP);
3488275SEric Cheng 
3498275SEric Cheng 	return (0);
3508275SEric Cheng }
3518275SEric Cheng 
3528275SEric Cheng void
3538275SEric Cheng dladm_flow_attr_ip2str(dladm_flow_attr_t *attrp, char *buf, size_t buf_len)
3548275SEric Cheng {
3558275SEric Cheng 	flow_desc_t	fdesc = attrp->fa_flow_desc;
3568275SEric Cheng 	struct in_addr	ipaddr;
3578275SEric Cheng 	int		prefix_len, prefix_max;
3588275SEric Cheng 	char		*cp, abuf[INET6_ADDRSTRLEN];
3598275SEric Cheng 
3608275SEric Cheng 	if (fdesc.fd_mask & FLOW_IP_LOCAL) {
3618275SEric Cheng 		if (fdesc.fd_ipversion == IPV6_VERSION) {
3628275SEric Cheng 			(void) inet_ntop(AF_INET6, &fdesc.fd_local_addr, abuf,
3638275SEric Cheng 			    INET6_ADDRSTRLEN);
3648275SEric Cheng 			cp = abuf;
3658275SEric Cheng 			prefix_max = IPV6_ABITS;
3668275SEric Cheng 		} else {
3678275SEric Cheng 			ipaddr.s_addr = fdesc.fd_local_addr._S6_un._S6_u32[3];
3688275SEric Cheng 			cp = inet_ntoa(ipaddr);
3698275SEric Cheng 			prefix_max = IP_ABITS;
3708275SEric Cheng 		}
3718275SEric Cheng 		(void) dladm_mask2prefixlen(&fdesc.fd_local_netmask,
3728275SEric Cheng 		    prefix_max, &prefix_len);
3738275SEric Cheng 		(void) snprintf(buf, buf_len, "LCL:%s/%d  ", cp, prefix_len);
3748275SEric Cheng 	} else if (fdesc.fd_mask & FLOW_IP_REMOTE) {
3758275SEric Cheng 		if (fdesc.fd_ipversion == IPV6_VERSION) {
3768275SEric Cheng 			(void) inet_ntop(AF_INET6, &fdesc.fd_remote_addr, abuf,
3778275SEric Cheng 			    INET6_ADDRSTRLEN);
3788275SEric Cheng 			cp = abuf;
3798275SEric Cheng 			prefix_max = IPV6_ABITS;
3808275SEric Cheng 		} else {
3818275SEric Cheng 			ipaddr.s_addr = fdesc.fd_remote_addr._S6_un._S6_u32[3];
3828275SEric Cheng 			cp = inet_ntoa(ipaddr);
3838275SEric Cheng 			prefix_max = IP_ABITS;
3848275SEric Cheng 		}
3858275SEric Cheng 		(void) dladm_mask2prefixlen(&fdesc.fd_remote_netmask,
3868275SEric Cheng 		    prefix_max, &prefix_len);
3878275SEric Cheng 		(void) snprintf(buf, buf_len, "RMT:%s/%d  ", cp, prefix_len);
3888275SEric Cheng 	} else {
3898275SEric Cheng 		buf[0] = '\0';
3908275SEric Cheng 	}
3918275SEric Cheng }
3928275SEric Cheng 
3938275SEric Cheng void
3948275SEric Cheng dladm_flow_attr_proto2str(dladm_flow_attr_t *attrp, char *buf, size_t buf_len)
3958275SEric Cheng {
3968275SEric Cheng 	flow_desc_t	fdesc = attrp->fa_flow_desc;
3978275SEric Cheng 
3988275SEric Cheng 	(void) snprintf(buf, buf_len, "%s",
3998275SEric Cheng 	    dladm_proto2str(fdesc.fd_protocol));
4008275SEric Cheng }
4018275SEric Cheng 
4028275SEric Cheng void
4038275SEric Cheng dladm_flow_attr_port2str(dladm_flow_attr_t *attrp, char *buf, size_t buf_len)
4048275SEric Cheng {
4058275SEric Cheng 	flow_desc_t	fdesc = attrp->fa_flow_desc;
4068275SEric Cheng 
4078275SEric Cheng 	if (fdesc.fd_mask & FLOW_ULP_PORT_LOCAL) {
4088275SEric Cheng 		(void) snprintf(buf, buf_len, "%d",
4098275SEric Cheng 		    ntohs(fdesc.fd_local_port));
41010734SEric Cheng 	} else if (fdesc.fd_mask & FLOW_ULP_PORT_REMOTE) {
41110734SEric Cheng 		(void) snprintf(buf, buf_len, "%d",
41210734SEric Cheng 		    ntohs(fdesc.fd_remote_port));
4138275SEric Cheng 	} else {
4148275SEric Cheng 		buf[0] = '\0';
4158275SEric Cheng 	}
4168275SEric Cheng }
4178275SEric Cheng 
4188275SEric Cheng void
4198275SEric Cheng dladm_flow_attr_dsfield2str(dladm_flow_attr_t *attrp, char *buf, size_t buf_len)
4208275SEric Cheng {
4218275SEric Cheng 	flow_desc_t	fdesc = attrp->fa_flow_desc;
4228275SEric Cheng 
4238275SEric Cheng 	if (fdesc.fd_mask & FLOW_IP_DSFIELD) {
4248275SEric Cheng 		(void) snprintf(buf, buf_len, "0x%x:0x%x",
4258275SEric Cheng 		    fdesc.fd_dsfield, fdesc.fd_dsfield_mask);
4268275SEric Cheng 	} else {
4278275SEric Cheng 		buf[0] = '\0';
4288275SEric Cheng 	}
4298275SEric Cheng }
430