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