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