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