summaryrefslogtreecommitdiff
path: root/usr/src/lib/libdladm/common/flowattr.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/libdladm/common/flowattr.c')
-rw-r--r--usr/src/lib/libdladm/common/flowattr.c411
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';
+ }
+}