Home | History | Annotate | Download | only in common
      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