diff options
Diffstat (limited to 'usr/src/lib/libdladm/common/flowattr.c')
| -rw-r--r-- | usr/src/lib/libdladm/common/flowattr.c | 411 |
1 files changed, 411 insertions, 0 deletions
diff --git a/usr/src/lib/libdladm/common/flowattr.c b/usr/src/lib/libdladm/common/flowattr.c new file mode 100644 index 0000000000..4fb578e5bc --- /dev/null +++ b/usr/src/lib/libdladm/common/flowattr.c @@ -0,0 +1,411 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include <errno.h> +#include <stdlib.h> +#include <strings.h> +#include <sys/mac_flow.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <net/if_types.h> +#include <net/if_dl.h> +#include <inet/ip.h> +#include <inet/ip6.h> + +#include <libdladm.h> +#include <libdlflow.h> +#include <libdlflow_impl.h> + +#define V4_PART_OF_V6(v6) ((v6)._S6_un._S6_u32[3]) + +/* max port number for UDP, TCP & SCTP */ +#define MAX_PORT 65535 + +static fad_checkf_t do_check_local_ip; +static fad_checkf_t do_check_remote_ip; +static fad_checkf_t do_check_protocol; +static fad_checkf_t do_check_local_port; + +static dladm_status_t do_check_port(char *, boolean_t, flow_desc_t *); + +static fattr_desc_t attr_table[] = { + { "local_ip", do_check_local_ip }, + { "remote_ip", do_check_remote_ip }, + { "transport", do_check_protocol }, + { "local_port", do_check_local_port }, + { "dsfield", do_check_dsfield }, +}; + +#define DLADM_MAX_FLOWATTRS (sizeof (attr_table) / sizeof (fattr_desc_t)) + +static dladm_status_t +do_check_local_ip(char *attr_val, flow_desc_t *fdesc) +{ + return (do_check_ip_addr(attr_val, B_TRUE, fdesc)); +} + +static dladm_status_t +do_check_remote_ip(char *attr_val, flow_desc_t *fdesc) +{ + return (do_check_ip_addr(attr_val, B_FALSE, fdesc)); +} + +dladm_status_t +do_check_ip_addr(char *addr_str, boolean_t local, flow_desc_t *fd) +{ + struct addrinfo *info = NULL; + dladm_status_t status; + int err, prefix_max, prefix_len = 0; + char *prefix_str, *endp = NULL; + flow_mask_t mask; + in6_addr_t *addr; + uchar_t *netmask; + + if ((prefix_str = strchr(addr_str, '/')) != NULL) { + *prefix_str++ = '\0'; + errno = 0; + prefix_len = (int)strtol(prefix_str, &endp, 10); + if (errno != 0 || prefix_len == 0 || *endp != '\0') + return (DLADM_STATUS_INVALID_PREFIXLEN); + } + + err = getaddrinfo(addr_str, NULL, NULL, &info); + if (err != 0) + return (DLADM_STATUS_INVALID_IP); + + mask = FLOW_IP_VERSION; + if (local) { + mask |= FLOW_IP_LOCAL; + addr = &fd->fd_local_addr; + netmask = (uchar_t *)&fd->fd_local_netmask; + } else { + mask |= FLOW_IP_REMOTE; + addr = &fd->fd_remote_addr; + netmask = (uchar_t *)&fd->fd_remote_netmask; + } + + if (info->ai_family == AF_INET) { + IN6_INADDR_TO_V4MAPPED(&(((struct sockaddr_in *) + (void *)info->ai_addr)->sin_addr), addr); + prefix_max = IP_ABITS; + fd->fd_ipversion = IPV4_VERSION; + netmask = (uchar_t *) + &(V4_PART_OF_V6((*((in6_addr_t *)(void *)netmask)))); + } else if (info->ai_family == AF_INET6) { + *addr = ((struct sockaddr_in6 *) + (void *)info->ai_addr)->sin6_addr; + prefix_max = IPV6_ABITS; + fd->fd_ipversion = IPV6_VERSION; + } else { + freeaddrinfo(info); + return (DLADM_STATUS_INVALID_IP); + } + + if (prefix_len == 0) + prefix_len = prefix_max; + + status = dladm_prefixlen2mask(prefix_len, prefix_max, netmask); + + if (status != DLADM_STATUS_OK) { + freeaddrinfo(info); + return (DLADM_STATUS_INVALID_PREFIXLEN); + } + + fd->fd_mask |= mask; + freeaddrinfo(info); + return (DLADM_STATUS_OK); +} + +dladm_status_t +do_check_protocol(char *attr_val, flow_desc_t *fdesc) +{ + uint8_t protocol; + + protocol = dladm_str2proto(attr_val); + + if (protocol != 0) { + fdesc->fd_mask |= FLOW_IP_PROTOCOL; + fdesc->fd_protocol = protocol; + return (DLADM_STATUS_OK); + } else { + return (DLADM_STATUS_INVALID_PROTOCOL); + } +} + +dladm_status_t +do_check_local_port(char *attr_val, flow_desc_t *fdesc) +{ + return (do_check_port(attr_val, B_TRUE, fdesc)); +} + +dladm_status_t +do_check_port(char *attr_val, boolean_t local, flow_desc_t *fdesc) +{ + char *endp = NULL; + long val; + + if (local) { + fdesc->fd_mask |= FLOW_ULP_PORT_LOCAL; + val = strtol(attr_val, &endp, 10); + if (val < 1 || val > MAX_PORT) + return (DLADM_STATUS_INVALID_PORT); + fdesc->fd_local_port = htons((uint16_t)val); + } else { + return (DLADM_STATUS_BADVAL); + } + + return (DLADM_STATUS_OK); +} + +/* + * Check for invalid and/or duplicate attribute specification + */ +static dladm_status_t +flow_attrlist_check(dladm_arg_list_t *attrlist) +{ + int i, j; + boolean_t isset[DLADM_MAX_FLOWATTRS]; + boolean_t matched; + + for (j = 0; j < DLADM_MAX_FLOWATTRS; j++) + isset[j] = B_FALSE; + + for (i = 0; i < attrlist->al_count; i++) { + matched = B_FALSE; + for (j = 0; j < DLADM_MAX_FLOWATTRS; j++) { + if (strcmp(attrlist->al_info[i].ai_name, + attr_table[j].ad_name) == 0) { + if (isset[j]) + return (DLADM_STATUS_FLOW_INCOMPATIBLE); + else + isset[j] = B_TRUE; + matched = B_TRUE; + } + } + /* + * if the attribute did not match any of the attribute in + * attr_table, then it's an invalid attribute. + */ + if (!matched) + return (DLADM_STATUS_BADARG); + } + return (DLADM_STATUS_OK); +} + +/* + * Convert an attribute list to a flow_desc_t using the attribute ad_check() + * functions. + */ +dladm_status_t +dladm_flow_attrlist_extract(dladm_arg_list_t *attrlist, flow_desc_t *flowdesc) +{ + dladm_status_t status = DLADM_STATUS_BADARG; + int i; + + for (i = 0; i < attrlist->al_count; i++) { + dladm_arg_info_t *aip = &attrlist->al_info[i]; + int j; + + for (j = 0; j < DLADM_MAX_FLOWATTRS; j++) { + fattr_desc_t *adp = &attr_table[j]; + + if (strcasecmp(aip->ai_name, adp->ad_name) != 0) + continue; + + if ((aip->ai_val == NULL) || (*aip->ai_val == NULL)) + return (DLADM_STATUS_BADARG); + + if (adp->ad_check != NULL) + status = adp->ad_check(*aip->ai_val, flowdesc); + else + status = DLADM_STATUS_BADARG; + + if (status != DLADM_STATUS_OK) + return (status); + } + } + return (status); +} + +void +dladm_free_attrs(dladm_arg_list_t *list) +{ + dladm_free_args(list); +} + +dladm_status_t +dladm_parse_flow_attrs(char *str, dladm_arg_list_t **listp, boolean_t novalues) +{ + + if (dladm_parse_args(str, listp, novalues) + != DLADM_STATUS_OK) + return (DLADM_STATUS_ATTR_PARSE_ERR); + + if (flow_attrlist_check(*listp) != DLADM_STATUS_OK) { + dladm_free_attrs(*listp); + return (DLADM_STATUS_ATTR_PARSE_ERR); + } + + return (DLADM_STATUS_OK); +} + +dladm_status_t +do_check_dsfield(char *str, flow_desc_t *fd) +{ + char *mask_str, *endp = NULL; + uint_t mask = 0xff, value; + + if ((mask_str = strchr(str, ':')) != NULL) { + *mask_str++ = '\0'; + errno = 0; + mask = strtoul(mask_str, &endp, 16); + if (errno != 0 || mask == 0 || mask > 0xff || + *endp != '\0') + return (DLADM_STATUS_INVALID_DSFMASK); + } + errno = 0; + endp = NULL; + value = strtoul(str, &endp, 16); + if (errno != 0 || value == 0 || value > 0xff || *endp != '\0') + return (DLADM_STATUS_INVALID_DSF); + + fd->fd_dsfield = (uint8_t)value; + fd->fd_dsfield_mask = (uint8_t)mask; + fd->fd_mask |= FLOW_IP_DSFIELD; + return (DLADM_STATUS_OK); +} + +char * +dladm_proto2str(uint8_t protocol) +{ + if (protocol == IPPROTO_TCP) + return ("tcp"); + if (protocol == IPPROTO_UDP) + return ("udp"); + if (protocol == IPPROTO_SCTP) + return ("sctp"); + if (protocol == IPPROTO_ICMPV6) + return ("icmpv6"); + if (protocol == IPPROTO_ICMP) + return ("icmp"); + else + return (""); +} + +uint8_t +dladm_str2proto(const char *protostr) +{ + if (strncasecmp(protostr, "tcp", 3) == 0) + return (IPPROTO_TCP); + else if (strncasecmp(protostr, "udp", 3) == 0) + return (IPPROTO_UDP); + else if (strncasecmp(protostr, "sctp", 4) == 0) + return (IPPROTO_SCTP); + else if (strncasecmp(protostr, "icmpv6", 6) == 0) + return (IPPROTO_ICMPV6); + else if (strncasecmp(protostr, "icmp", 4) == 0) + return (IPPROTO_ICMP); + + return (0); +} + +void +dladm_flow_attr_ip2str(dladm_flow_attr_t *attrp, char *buf, size_t buf_len) +{ + flow_desc_t fdesc = attrp->fa_flow_desc; + struct in_addr ipaddr; + int prefix_len, prefix_max; + char *cp, abuf[INET6_ADDRSTRLEN]; + + if (fdesc.fd_mask & FLOW_IP_LOCAL) { + if (fdesc.fd_ipversion == IPV6_VERSION) { + (void) inet_ntop(AF_INET6, &fdesc.fd_local_addr, abuf, + INET6_ADDRSTRLEN); + cp = abuf; + prefix_max = IPV6_ABITS; + } else { + ipaddr.s_addr = fdesc.fd_local_addr._S6_un._S6_u32[3]; + cp = inet_ntoa(ipaddr); + prefix_max = IP_ABITS; + } + (void) dladm_mask2prefixlen(&fdesc.fd_local_netmask, + prefix_max, &prefix_len); + (void) snprintf(buf, buf_len, "LCL:%s/%d ", cp, prefix_len); + } else if (fdesc.fd_mask & FLOW_IP_REMOTE) { + if (fdesc.fd_ipversion == IPV6_VERSION) { + (void) inet_ntop(AF_INET6, &fdesc.fd_remote_addr, abuf, + INET6_ADDRSTRLEN); + cp = abuf; + prefix_max = IPV6_ABITS; + } else { + ipaddr.s_addr = fdesc.fd_remote_addr._S6_un._S6_u32[3]; + cp = inet_ntoa(ipaddr); + prefix_max = IP_ABITS; + } + (void) dladm_mask2prefixlen(&fdesc.fd_remote_netmask, + prefix_max, &prefix_len); + (void) snprintf(buf, buf_len, "RMT:%s/%d ", cp, prefix_len); + } else { + buf[0] = '\0'; + } +} + +void +dladm_flow_attr_proto2str(dladm_flow_attr_t *attrp, char *buf, size_t buf_len) +{ + flow_desc_t fdesc = attrp->fa_flow_desc; + + (void) snprintf(buf, buf_len, "%s", + dladm_proto2str(fdesc.fd_protocol)); +} + +void +dladm_flow_attr_port2str(dladm_flow_attr_t *attrp, char *buf, size_t buf_len) +{ + flow_desc_t fdesc = attrp->fa_flow_desc; + + if (fdesc.fd_mask & FLOW_ULP_PORT_LOCAL) { + (void) snprintf(buf, buf_len, "%d", + ntohs(fdesc.fd_local_port)); + } else { + buf[0] = '\0'; + } +} + +void +dladm_flow_attr_dsfield2str(dladm_flow_attr_t *attrp, char *buf, size_t buf_len) +{ + flow_desc_t fdesc = attrp->fa_flow_desc; + + if (fdesc.fd_mask & FLOW_IP_DSFIELD) { + (void) snprintf(buf, buf_len, "0x%x:0x%x", + fdesc.fd_dsfield, fdesc.fd_dsfield_mask); + } else { + buf[0] = '\0'; + } +} |
