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