1*8275SEric Cheng /*
2*8275SEric Cheng  * CDDL HEADER START
3*8275SEric Cheng  *
4*8275SEric Cheng  * The contents of this file are subject to the terms of the
5*8275SEric Cheng  * Common Development and Distribution License (the "License").
6*8275SEric Cheng  * You may not use this file except in compliance with the License.
7*8275SEric Cheng  *
8*8275SEric Cheng  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*8275SEric Cheng  * or http://www.opensolaris.org/os/licensing.
10*8275SEric Cheng  * See the License for the specific language governing permissions
11*8275SEric Cheng  * and limitations under the License.
12*8275SEric Cheng  *
13*8275SEric Cheng  * When distributing Covered Code, include this CDDL HEADER in each
14*8275SEric Cheng  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*8275SEric Cheng  * If applicable, add the following below this CDDL HEADER, with the
16*8275SEric Cheng  * fields enclosed by brackets "[]" replaced with your own identifying
17*8275SEric Cheng  * information: Portions Copyright [yyyy] [name of copyright owner]
18*8275SEric Cheng  *
19*8275SEric Cheng  * CDDL HEADER END
20*8275SEric Cheng  */
21*8275SEric Cheng /*
22*8275SEric Cheng  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23*8275SEric Cheng  * Use is subject to license terms.
24*8275SEric Cheng  */
25*8275SEric Cheng 
26*8275SEric Cheng #include <errno.h>
27*8275SEric Cheng #include <stdlib.h>
28*8275SEric Cheng #include <strings.h>
29*8275SEric Cheng #include <sys/mac_flow.h>
30*8275SEric Cheng #include <sys/types.h>
31*8275SEric Cheng #include <sys/socket.h>
32*8275SEric Cheng #include <netinet/in.h>
33*8275SEric Cheng #include <arpa/inet.h>
34*8275SEric Cheng #include <netdb.h>
35*8275SEric Cheng #include <net/if_types.h>
36*8275SEric Cheng #include <net/if_dl.h>
37*8275SEric Cheng #include <inet/ip.h>
38*8275SEric Cheng #include <inet/ip6.h>
39*8275SEric Cheng 
40*8275SEric Cheng #include <libdladm.h>
41*8275SEric Cheng #include <libdlflow.h>
42*8275SEric Cheng #include <libdlflow_impl.h>
43*8275SEric Cheng 
44*8275SEric Cheng #define	V4_PART_OF_V6(v6)	((v6)._S6_un._S6_u32[3])
45*8275SEric Cheng 
46*8275SEric Cheng /* max port number for UDP, TCP & SCTP */
47*8275SEric Cheng #define	MAX_PORT	65535
48*8275SEric Cheng 
49*8275SEric Cheng static fad_checkf_t do_check_local_ip;
50*8275SEric Cheng static fad_checkf_t do_check_remote_ip;
51*8275SEric Cheng static fad_checkf_t do_check_protocol;
52*8275SEric Cheng static fad_checkf_t do_check_local_port;
53*8275SEric Cheng 
54*8275SEric Cheng static dladm_status_t do_check_port(char *, boolean_t, flow_desc_t *);
55*8275SEric Cheng 
56*8275SEric Cheng static fattr_desc_t	attr_table[] = {
57*8275SEric Cheng 	{ "local_ip",	do_check_local_ip },
58*8275SEric Cheng 	{ "remote_ip",	do_check_remote_ip },
59*8275SEric Cheng 	{ "transport",	do_check_protocol },
60*8275SEric Cheng 	{ "local_port",	do_check_local_port },
61*8275SEric Cheng 	{ "dsfield",	do_check_dsfield },
62*8275SEric Cheng };
63*8275SEric Cheng 
64*8275SEric Cheng #define	DLADM_MAX_FLOWATTRS	(sizeof (attr_table) / sizeof (fattr_desc_t))
65*8275SEric Cheng 
66*8275SEric Cheng static dladm_status_t
67*8275SEric Cheng do_check_local_ip(char *attr_val, flow_desc_t *fdesc)
68*8275SEric Cheng {
69*8275SEric Cheng 	return (do_check_ip_addr(attr_val, B_TRUE, fdesc));
70*8275SEric Cheng }
71*8275SEric Cheng 
72*8275SEric Cheng static dladm_status_t
73*8275SEric Cheng do_check_remote_ip(char *attr_val, flow_desc_t *fdesc)
74*8275SEric Cheng {
75*8275SEric Cheng 	return (do_check_ip_addr(attr_val, B_FALSE, fdesc));
76*8275SEric Cheng }
77*8275SEric Cheng 
78*8275SEric Cheng dladm_status_t
79*8275SEric Cheng do_check_ip_addr(char *addr_str, boolean_t local, flow_desc_t *fd)
80*8275SEric Cheng {
81*8275SEric Cheng 	struct addrinfo	*info = NULL;
82*8275SEric Cheng 	dladm_status_t	status;
83*8275SEric Cheng 	int		err, prefix_max, prefix_len = 0;
84*8275SEric Cheng 	char		*prefix_str, *endp = NULL;
85*8275SEric Cheng 	flow_mask_t	mask;
86*8275SEric Cheng 	in6_addr_t	*addr;
87*8275SEric Cheng 	uchar_t		*netmask;
88*8275SEric Cheng 
89*8275SEric Cheng 	if ((prefix_str = strchr(addr_str, '/')) != NULL) {
90*8275SEric Cheng 		*prefix_str++ = '\0';
91*8275SEric Cheng 		errno = 0;
92*8275SEric Cheng 		prefix_len = (int)strtol(prefix_str, &endp, 10);
93*8275SEric Cheng 		if (errno != 0 || prefix_len == 0 || *endp != '\0')
94*8275SEric Cheng 			return (DLADM_STATUS_INVALID_PREFIXLEN);
95*8275SEric Cheng 	}
96*8275SEric Cheng 
97*8275SEric Cheng 	err = getaddrinfo(addr_str, NULL, NULL, &info);
98*8275SEric Cheng 	if (err != 0)
99*8275SEric Cheng 		return (DLADM_STATUS_INVALID_IP);
100*8275SEric Cheng 
101*8275SEric Cheng 	mask = FLOW_IP_VERSION;
102*8275SEric Cheng 	if (local) {
103*8275SEric Cheng 		mask |= FLOW_IP_LOCAL;
104*8275SEric Cheng 		addr = &fd->fd_local_addr;
105*8275SEric Cheng 		netmask = (uchar_t *)&fd->fd_local_netmask;
106*8275SEric Cheng 	} else {
107*8275SEric Cheng 		mask |= FLOW_IP_REMOTE;
108*8275SEric Cheng 		addr = &fd->fd_remote_addr;
109*8275SEric Cheng 		netmask = (uchar_t *)&fd->fd_remote_netmask;
110*8275SEric Cheng 	}
111*8275SEric Cheng 
112*8275SEric Cheng 	if (info->ai_family == AF_INET) {
113*8275SEric Cheng 		IN6_INADDR_TO_V4MAPPED(&(((struct sockaddr_in *)
114*8275SEric Cheng 		    (void *)info->ai_addr)->sin_addr), addr);
115*8275SEric Cheng 		prefix_max = IP_ABITS;
116*8275SEric Cheng 		fd->fd_ipversion = IPV4_VERSION;
117*8275SEric Cheng 		netmask = (uchar_t *)
118*8275SEric Cheng 		    &(V4_PART_OF_V6((*((in6_addr_t *)(void *)netmask))));
119*8275SEric Cheng 	} else if (info->ai_family == AF_INET6) {
120*8275SEric Cheng 		*addr = ((struct sockaddr_in6 *)
121*8275SEric Cheng 		    (void *)info->ai_addr)->sin6_addr;
122*8275SEric Cheng 		prefix_max = IPV6_ABITS;
123*8275SEric Cheng 		fd->fd_ipversion = IPV6_VERSION;
124*8275SEric Cheng 	} else {
125*8275SEric Cheng 		freeaddrinfo(info);
126*8275SEric Cheng 		return (DLADM_STATUS_INVALID_IP);
127*8275SEric Cheng 	}
128*8275SEric Cheng 
129*8275SEric Cheng 	if (prefix_len == 0)
130*8275SEric Cheng 		prefix_len = prefix_max;
131*8275SEric Cheng 
132*8275SEric Cheng 	status = dladm_prefixlen2mask(prefix_len, prefix_max, netmask);
133*8275SEric Cheng 
134*8275SEric Cheng 	if (status != DLADM_STATUS_OK) {
135*8275SEric Cheng 		freeaddrinfo(info);
136*8275SEric Cheng 		return (DLADM_STATUS_INVALID_PREFIXLEN);
137*8275SEric Cheng 	}
138*8275SEric Cheng 
139*8275SEric Cheng 	fd->fd_mask |= mask;
140*8275SEric Cheng 	freeaddrinfo(info);
141*8275SEric Cheng 	return (DLADM_STATUS_OK);
142*8275SEric Cheng }
143*8275SEric Cheng 
144*8275SEric Cheng dladm_status_t
145*8275SEric Cheng do_check_protocol(char *attr_val, flow_desc_t *fdesc)
146*8275SEric Cheng {
147*8275SEric Cheng 	uint8_t	protocol;
148*8275SEric Cheng 
149*8275SEric Cheng 	protocol = dladm_str2proto(attr_val);
150*8275SEric Cheng 
151*8275SEric Cheng 	if (protocol != 0) {
152*8275SEric Cheng 		fdesc->fd_mask |= FLOW_IP_PROTOCOL;
153*8275SEric Cheng 		fdesc->fd_protocol = protocol;
154*8275SEric Cheng 		return (DLADM_STATUS_OK);
155*8275SEric Cheng 	} else {
156*8275SEric Cheng 		return (DLADM_STATUS_INVALID_PROTOCOL);
157*8275SEric Cheng 	}
158*8275SEric Cheng }
159*8275SEric Cheng 
160*8275SEric Cheng dladm_status_t
161*8275SEric Cheng do_check_local_port(char *attr_val, flow_desc_t *fdesc)
162*8275SEric Cheng {
163*8275SEric Cheng 	return (do_check_port(attr_val, B_TRUE, fdesc));
164*8275SEric Cheng }
165*8275SEric Cheng 
166*8275SEric Cheng dladm_status_t
167*8275SEric Cheng do_check_port(char *attr_val, boolean_t local, flow_desc_t *fdesc)
168*8275SEric Cheng {
169*8275SEric Cheng 	char	*endp = NULL;
170*8275SEric Cheng 	long	val;
171*8275SEric Cheng 
172*8275SEric Cheng 	if (local) {
173*8275SEric Cheng 		fdesc->fd_mask |= FLOW_ULP_PORT_LOCAL;
174*8275SEric Cheng 		val = strtol(attr_val, &endp, 10);
175*8275SEric Cheng 		if (val < 1 || val > MAX_PORT)
176*8275SEric Cheng 			return (DLADM_STATUS_INVALID_PORT);
177*8275SEric Cheng 		fdesc->fd_local_port = htons((uint16_t)val);
178*8275SEric Cheng 	} else {
179*8275SEric Cheng 		return (DLADM_STATUS_BADVAL);
180*8275SEric Cheng 	}
181*8275SEric Cheng 
182*8275SEric Cheng 	return (DLADM_STATUS_OK);
183*8275SEric Cheng }
184*8275SEric Cheng 
185*8275SEric Cheng /*
186*8275SEric Cheng  * Check for invalid and/or duplicate attribute specification
187*8275SEric Cheng  */
188*8275SEric Cheng static dladm_status_t
189*8275SEric Cheng flow_attrlist_check(dladm_arg_list_t *attrlist)
190*8275SEric Cheng {
191*8275SEric Cheng 	int		i, j;
192*8275SEric Cheng 	boolean_t	isset[DLADM_MAX_FLOWATTRS];
193*8275SEric Cheng 	boolean_t	matched;
194*8275SEric Cheng 
195*8275SEric Cheng 	for (j = 0; j < DLADM_MAX_FLOWATTRS; j++)
196*8275SEric Cheng 		isset[j] = B_FALSE;
197*8275SEric Cheng 
198*8275SEric Cheng 	for (i = 0; i < attrlist->al_count; i++) {
199*8275SEric Cheng 		matched = B_FALSE;
200*8275SEric Cheng 		for (j = 0; j < DLADM_MAX_FLOWATTRS; j++) {
201*8275SEric Cheng 			if (strcmp(attrlist->al_info[i].ai_name,
202*8275SEric Cheng 			    attr_table[j].ad_name) == 0) {
203*8275SEric Cheng 				if (isset[j])
204*8275SEric Cheng 					return (DLADM_STATUS_FLOW_INCOMPATIBLE);
205*8275SEric Cheng 				else
206*8275SEric Cheng 					isset[j] = B_TRUE;
207*8275SEric Cheng 				matched = B_TRUE;
208*8275SEric Cheng 			}
209*8275SEric Cheng 		}
210*8275SEric Cheng 		/*
211*8275SEric Cheng 		 * if the attribute did not match any of the attribute in
212*8275SEric Cheng 		 * attr_table, then it's an invalid attribute.
213*8275SEric Cheng 		 */
214*8275SEric Cheng 		if (!matched)
215*8275SEric Cheng 			return (DLADM_STATUS_BADARG);
216*8275SEric Cheng 	}
217*8275SEric Cheng 	return (DLADM_STATUS_OK);
218*8275SEric Cheng }
219*8275SEric Cheng 
220*8275SEric Cheng /*
221*8275SEric Cheng  * Convert an attribute list to a flow_desc_t using the attribute ad_check()
222*8275SEric Cheng  * functions.
223*8275SEric Cheng  */
224*8275SEric Cheng dladm_status_t
225*8275SEric Cheng dladm_flow_attrlist_extract(dladm_arg_list_t *attrlist, flow_desc_t *flowdesc)
226*8275SEric Cheng {
227*8275SEric Cheng 	dladm_status_t	status = DLADM_STATUS_BADARG;
228*8275SEric Cheng 	int		i;
229*8275SEric Cheng 
230*8275SEric Cheng 	for (i = 0; i < attrlist->al_count; i++) {
231*8275SEric Cheng 		dladm_arg_info_t	*aip = &attrlist->al_info[i];
232*8275SEric Cheng 		int			j;
233*8275SEric Cheng 
234*8275SEric Cheng 		for (j = 0; j < DLADM_MAX_FLOWATTRS; j++) {
235*8275SEric Cheng 			fattr_desc_t	*adp = &attr_table[j];
236*8275SEric Cheng 
237*8275SEric Cheng 			if (strcasecmp(aip->ai_name, adp->ad_name) != 0)
238*8275SEric Cheng 				continue;
239*8275SEric Cheng 
240*8275SEric Cheng 			if ((aip->ai_val == NULL) || (*aip->ai_val == NULL))
241*8275SEric Cheng 				return (DLADM_STATUS_BADARG);
242*8275SEric Cheng 
243*8275SEric Cheng 			if (adp->ad_check != NULL)
244*8275SEric Cheng 				status = adp->ad_check(*aip->ai_val, flowdesc);
245*8275SEric Cheng 			else
246*8275SEric Cheng 				status = DLADM_STATUS_BADARG;
247*8275SEric Cheng 
248*8275SEric Cheng 			if (status != DLADM_STATUS_OK)
249*8275SEric Cheng 				return (status);
250*8275SEric Cheng 		}
251*8275SEric Cheng 	}
252*8275SEric Cheng 	return (status);
253*8275SEric Cheng }
254*8275SEric Cheng 
255*8275SEric Cheng void
256*8275SEric Cheng dladm_free_attrs(dladm_arg_list_t *list)
257*8275SEric Cheng {
258*8275SEric Cheng 	dladm_free_args(list);
259*8275SEric Cheng }
260*8275SEric Cheng 
261*8275SEric Cheng dladm_status_t
262*8275SEric Cheng dladm_parse_flow_attrs(char *str, dladm_arg_list_t **listp, boolean_t novalues)
263*8275SEric Cheng {
264*8275SEric Cheng 
265*8275SEric Cheng 	if (dladm_parse_args(str, listp, novalues)
266*8275SEric Cheng 	    != DLADM_STATUS_OK)
267*8275SEric Cheng 		return (DLADM_STATUS_ATTR_PARSE_ERR);
268*8275SEric Cheng 
269*8275SEric Cheng 	if (flow_attrlist_check(*listp) != DLADM_STATUS_OK) {
270*8275SEric Cheng 		dladm_free_attrs(*listp);
271*8275SEric Cheng 		return (DLADM_STATUS_ATTR_PARSE_ERR);
272*8275SEric Cheng 	}
273*8275SEric Cheng 
274*8275SEric Cheng 	return (DLADM_STATUS_OK);
275*8275SEric Cheng }
276*8275SEric Cheng 
277*8275SEric Cheng dladm_status_t
278*8275SEric Cheng do_check_dsfield(char *str, flow_desc_t *fd)
279*8275SEric Cheng {
280*8275SEric Cheng 	char		*mask_str, *endp = NULL;
281*8275SEric Cheng 	uint_t		mask = 0xff, value;
282*8275SEric Cheng 
283*8275SEric Cheng 	if ((mask_str = strchr(str, ':')) != NULL) {
284*8275SEric Cheng 		*mask_str++ = '\0';
285*8275SEric Cheng 		errno = 0;
286*8275SEric Cheng 		mask = strtoul(mask_str, &endp, 16);
287*8275SEric Cheng 		if (errno != 0 || mask == 0 || mask > 0xff ||
288*8275SEric Cheng 		    *endp != '\0')
289*8275SEric Cheng 			return (DLADM_STATUS_INVALID_DSFMASK);
290*8275SEric Cheng 	}
291*8275SEric Cheng 	errno = 0;
292*8275SEric Cheng 	endp = NULL;
293*8275SEric Cheng 	value = strtoul(str, &endp, 16);
294*8275SEric Cheng 	if (errno != 0 || value == 0 || value > 0xff || *endp != '\0')
295*8275SEric Cheng 		return (DLADM_STATUS_INVALID_DSF);
296*8275SEric Cheng 
297*8275SEric Cheng 	fd->fd_dsfield = (uint8_t)value;
298*8275SEric Cheng 	fd->fd_dsfield_mask = (uint8_t)mask;
299*8275SEric Cheng 	fd->fd_mask |= FLOW_IP_DSFIELD;
300*8275SEric Cheng 	return (DLADM_STATUS_OK);
301*8275SEric Cheng }
302*8275SEric Cheng 
303*8275SEric Cheng char *
304*8275SEric Cheng dladm_proto2str(uint8_t protocol)
305*8275SEric Cheng {
306*8275SEric Cheng 	if (protocol == IPPROTO_TCP)
307*8275SEric Cheng 		return ("tcp");
308*8275SEric Cheng 	if (protocol == IPPROTO_UDP)
309*8275SEric Cheng 		return ("udp");
310*8275SEric Cheng 	if (protocol == IPPROTO_SCTP)
311*8275SEric Cheng 		return ("sctp");
312*8275SEric Cheng 	if (protocol == IPPROTO_ICMPV6)
313*8275SEric Cheng 		return ("icmpv6");
314*8275SEric Cheng 	if (protocol == IPPROTO_ICMP)
315*8275SEric Cheng 		return ("icmp");
316*8275SEric Cheng 	else
317*8275SEric Cheng 		return ("");
318*8275SEric Cheng }
319*8275SEric Cheng 
320*8275SEric Cheng uint8_t
321*8275SEric Cheng dladm_str2proto(const char *protostr)
322*8275SEric Cheng {
323*8275SEric Cheng 	if (strncasecmp(protostr, "tcp", 3) == 0)
324*8275SEric Cheng 		return (IPPROTO_TCP);
325*8275SEric Cheng 	else if (strncasecmp(protostr, "udp", 3) == 0)
326*8275SEric Cheng 		return (IPPROTO_UDP);
327*8275SEric Cheng 	else if (strncasecmp(protostr, "sctp", 4) == 0)
328*8275SEric Cheng 		return (IPPROTO_SCTP);
329*8275SEric Cheng 	else if (strncasecmp(protostr, "icmpv6", 6) == 0)
330*8275SEric Cheng 		return (IPPROTO_ICMPV6);
331*8275SEric Cheng 	else if (strncasecmp(protostr, "icmp", 4) == 0)
332*8275SEric Cheng 		return (IPPROTO_ICMP);
333*8275SEric Cheng 
334*8275SEric Cheng 	return (0);
335*8275SEric Cheng }
336*8275SEric Cheng 
337*8275SEric Cheng void
338*8275SEric Cheng dladm_flow_attr_ip2str(dladm_flow_attr_t *attrp, char *buf, size_t buf_len)
339*8275SEric Cheng {
340*8275SEric Cheng 	flow_desc_t	fdesc = attrp->fa_flow_desc;
341*8275SEric Cheng 	struct in_addr	ipaddr;
342*8275SEric Cheng 	int		prefix_len, prefix_max;
343*8275SEric Cheng 	char		*cp, abuf[INET6_ADDRSTRLEN];
344*8275SEric Cheng 
345*8275SEric Cheng 	if (fdesc.fd_mask & FLOW_IP_LOCAL) {
346*8275SEric Cheng 		if (fdesc.fd_ipversion == IPV6_VERSION) {
347*8275SEric Cheng 			(void) inet_ntop(AF_INET6, &fdesc.fd_local_addr, abuf,
348*8275SEric Cheng 			    INET6_ADDRSTRLEN);
349*8275SEric Cheng 			cp = abuf;
350*8275SEric Cheng 			prefix_max = IPV6_ABITS;
351*8275SEric Cheng 		} else {
352*8275SEric Cheng 			ipaddr.s_addr = fdesc.fd_local_addr._S6_un._S6_u32[3];
353*8275SEric Cheng 			cp = inet_ntoa(ipaddr);
354*8275SEric Cheng 			prefix_max = IP_ABITS;
355*8275SEric Cheng 		}
356*8275SEric Cheng 		(void) dladm_mask2prefixlen(&fdesc.fd_local_netmask,
357*8275SEric Cheng 		    prefix_max, &prefix_len);
358*8275SEric Cheng 		(void) snprintf(buf, buf_len, "LCL:%s/%d  ", cp, prefix_len);
359*8275SEric Cheng 	} else if (fdesc.fd_mask & FLOW_IP_REMOTE) {
360*8275SEric Cheng 		if (fdesc.fd_ipversion == IPV6_VERSION) {
361*8275SEric Cheng 			(void) inet_ntop(AF_INET6, &fdesc.fd_remote_addr, abuf,
362*8275SEric Cheng 			    INET6_ADDRSTRLEN);
363*8275SEric Cheng 			cp = abuf;
364*8275SEric Cheng 			prefix_max = IPV6_ABITS;
365*8275SEric Cheng 		} else {
366*8275SEric Cheng 			ipaddr.s_addr = fdesc.fd_remote_addr._S6_un._S6_u32[3];
367*8275SEric Cheng 			cp = inet_ntoa(ipaddr);
368*8275SEric Cheng 			prefix_max = IP_ABITS;
369*8275SEric Cheng 		}
370*8275SEric Cheng 		(void) dladm_mask2prefixlen(&fdesc.fd_remote_netmask,
371*8275SEric Cheng 		    prefix_max, &prefix_len);
372*8275SEric Cheng 		(void) snprintf(buf, buf_len, "RMT:%s/%d  ", cp, prefix_len);
373*8275SEric Cheng 	} else {
374*8275SEric Cheng 		buf[0] = '\0';
375*8275SEric Cheng 	}
376*8275SEric Cheng }
377*8275SEric Cheng 
378*8275SEric Cheng void
379*8275SEric Cheng dladm_flow_attr_proto2str(dladm_flow_attr_t *attrp, char *buf, size_t buf_len)
380*8275SEric Cheng {
381*8275SEric Cheng 	flow_desc_t	fdesc = attrp->fa_flow_desc;
382*8275SEric Cheng 
383*8275SEric Cheng 	(void) snprintf(buf, buf_len, "%s",
384*8275SEric Cheng 	    dladm_proto2str(fdesc.fd_protocol));
385*8275SEric Cheng }
386*8275SEric Cheng 
387*8275SEric Cheng void
388*8275SEric Cheng dladm_flow_attr_port2str(dladm_flow_attr_t *attrp, char *buf, size_t buf_len)
389*8275SEric Cheng {
390*8275SEric Cheng 	flow_desc_t	fdesc = attrp->fa_flow_desc;
391*8275SEric Cheng 
392*8275SEric Cheng 	if (fdesc.fd_mask & FLOW_ULP_PORT_LOCAL) {
393*8275SEric Cheng 		(void) snprintf(buf, buf_len, "%d",
394*8275SEric Cheng 		    ntohs(fdesc.fd_local_port));
395*8275SEric Cheng 	} else {
396*8275SEric Cheng 		buf[0] = '\0';
397*8275SEric Cheng 	}
398*8275SEric Cheng }
399*8275SEric Cheng 
400*8275SEric Cheng void
401*8275SEric Cheng dladm_flow_attr_dsfield2str(dladm_flow_attr_t *attrp, char *buf, size_t buf_len)
402*8275SEric Cheng {
403*8275SEric Cheng 	flow_desc_t	fdesc = attrp->fa_flow_desc;
404*8275SEric Cheng 
405*8275SEric Cheng 	if (fdesc.fd_mask & FLOW_IP_DSFIELD) {
406*8275SEric Cheng 		(void) snprintf(buf, buf_len, "0x%x:0x%x",
407*8275SEric Cheng 		    fdesc.fd_dsfield, fdesc.fd_dsfield_mask);
408*8275SEric Cheng 	} else {
409*8275SEric Cheng 		buf[0] = '\0';
410*8275SEric Cheng 	}
411*8275SEric Cheng }
412