2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A#include <errno.h>
2N/A#include <stdlib.h>
2N/A#include <strings.h>
2N/A#include <sys/mac_flow.h>
2N/A#include <sys/types.h>
2N/A#include <sys/socket.h>
2N/A#include <netinet/in.h>
2N/A#include <arpa/inet.h>
2N/A#include <netdb.h>
2N/A#include <net/if_types.h>
2N/A#include <net/if_dl.h>
2N/A#include <inet/ip.h>
2N/A#include <inet/ip6.h>
2N/A
2N/A#include <libdladm.h>
2N/A#include <libdlflow.h>
2N/A#include <libdlflow_impl.h>
2N/A
2N/A/* max port number for UDP, TCP & SCTP */
2N/A#define MAX_PORT 65535
2N/A
2N/Astatic fad_checkf_t do_check_local_ip;
2N/Astatic fad_checkf_t do_check_remote_ip;
2N/Astatic fad_checkf_t do_check_protocol;
2N/Astatic fad_checkf_t do_check_local_port;
2N/Astatic fad_checkf_t do_check_remote_port;
2N/Astatic fad_checkf_t do_check_dsfield;
2N/A
2N/Astatic dladm_status_t do_check_port(char *, boolean_t, flow_desc_t *);
2N/A
2N/Astatic fattr_desc_t attr_table[] = {
2N/A { "local_ip", do_check_local_ip },
2N/A { "remote_ip", do_check_remote_ip },
2N/A { "transport", do_check_protocol },
2N/A { "local_port", do_check_local_port },
2N/A { "remote_port", do_check_remote_port },
2N/A { "dsfield", do_check_dsfield },
2N/A};
2N/A
2N/A#define DLADM_MAX_FLOWATTRS (sizeof (attr_table) / sizeof (fattr_desc_t))
2N/Astatic dladm_status_t
2N/Ado_check_dsfield(char *attr_val, flow_desc_t *fdesc)
2N/A{
2N/A return (dladm_check_dsfield(attr_val, fdesc));
2N/A}
2N/A
2N/Astatic dladm_status_t
2N/Ado_check_local_ip(char *attr_val, flow_desc_t *fdesc)
2N/A{
2N/A return (dladm_check_ip_addr(attr_val, B_TRUE, fdesc));
2N/A}
2N/A
2N/Astatic dladm_status_t
2N/Ado_check_remote_ip(char *attr_val, flow_desc_t *fdesc)
2N/A{
2N/A return (dladm_check_ip_addr(attr_val, B_FALSE, fdesc));
2N/A}
2N/A
2N/Adladm_status_t
2N/Adladm_check_ip_addr(char *addr_str, boolean_t local, flow_desc_t *fd)
2N/A{
2N/A dladm_status_t status;
2N/A int prefix_max, prefix_len = 0;
2N/A char *prefix_str, *endp = NULL;
2N/A flow_mask_t mask;
2N/A in6_addr_t *addr;
2N/A uchar_t *netmask;
2N/A struct in_addr v4addr;
2N/A struct in6_addr v6addr;
2N/A int family;
2N/A
2N/A if ((prefix_str = strchr(addr_str, '/')) != NULL) {
2N/A *prefix_str++ = '\0';
2N/A errno = 0;
2N/A prefix_len = (int)strtol(prefix_str, &endp, 10);
2N/A if (errno != 0 || prefix_len == 0 || *endp != '\0')
2N/A return (DLADM_STATUS_INVALID_PREFIXLEN);
2N/A }
2N/A if (inet_pton(AF_INET, addr_str, &v4addr.s_addr) == 1) {
2N/A family = AF_INET;
2N/A } else if (inet_pton(AF_INET6, addr_str, v6addr.s6_addr) == 1) {
2N/A family = AF_INET6;
2N/A } else {
2N/A return (DLADM_STATUS_INVALID_IP);
2N/A }
2N/A
2N/A mask = FLOW_IP_VERSION;
2N/A if (local) {
2N/A mask |= FLOW_IP_LOCAL;
2N/A addr = &fd->fd_local_addr;
2N/A netmask = (uchar_t *)&fd->fd_local_netmask;
2N/A } else {
2N/A mask |= FLOW_IP_REMOTE;
2N/A addr = &fd->fd_remote_addr;
2N/A netmask = (uchar_t *)&fd->fd_remote_netmask;
2N/A }
2N/A
2N/A if (family == AF_INET) {
2N/A IN6_INADDR_TO_V4MAPPED(&v4addr, addr);
2N/A prefix_max = IP_ABITS;
2N/A fd->fd_ipversion = IPV4_VERSION;
2N/A netmask = (uchar_t *)
2N/A &(V4_PART_OF_V6((*((in6_addr_t *)(void *)netmask))));
2N/A } else {
2N/A *addr = v6addr;
2N/A prefix_max = IPV6_ABITS;
2N/A fd->fd_ipversion = IPV6_VERSION;
2N/A }
2N/A
2N/A if (prefix_len == 0)
2N/A prefix_len = prefix_max;
2N/A
2N/A status = dladm_prefixlen2mask(prefix_len, prefix_max, netmask);
2N/A
2N/A if (status != DLADM_STATUS_OK) {
2N/A return (DLADM_STATUS_INVALID_PREFIXLEN);
2N/A }
2N/A
2N/A fd->fd_mask |= mask;
2N/A return (DLADM_STATUS_OK);
2N/A}
2N/A
2N/Adladm_status_t
2N/Ado_check_protocol(char *attr_val, flow_desc_t *fdesc)
2N/A{
2N/A uint8_t protocol;
2N/A
2N/A protocol = dladm_str2proto(attr_val);
2N/A
2N/A if (protocol != 0) {
2N/A fdesc->fd_mask |= FLOW_IP_PROTOCOL;
2N/A fdesc->fd_protocol = protocol;
2N/A return (DLADM_STATUS_OK);
2N/A } else {
2N/A return (DLADM_STATUS_INVALID_PROTOCOL);
2N/A }
2N/A}
2N/A
2N/Adladm_status_t
2N/Ado_check_local_port(char *attr_val, flow_desc_t *fdesc)
2N/A{
2N/A return (do_check_port(attr_val, B_TRUE, fdesc));
2N/A}
2N/A
2N/Adladm_status_t
2N/Ado_check_remote_port(char *attr_val, flow_desc_t *fdesc)
2N/A{
2N/A return (do_check_port(attr_val, B_FALSE, fdesc));
2N/A}
2N/A
2N/Adladm_status_t
2N/Ado_check_port(char *attr_val, boolean_t local, flow_desc_t *fdesc)
2N/A{
2N/A char *endp = NULL;
2N/A long val;
2N/A
2N/A val = strtol(attr_val, &endp, 10);
2N/A if (val < 1 || val > MAX_PORT || *endp != '\0')
2N/A return (DLADM_STATUS_INVALID_PORT);
2N/A if (local) {
2N/A fdesc->fd_mask |= FLOW_ULP_PORT_LOCAL;
2N/A fdesc->fd_local_port = htons((uint16_t)val);
2N/A } else {
2N/A fdesc->fd_mask |= FLOW_ULP_PORT_REMOTE;
2N/A fdesc->fd_remote_port = htons((uint16_t)val);
2N/A }
2N/A
2N/A return (DLADM_STATUS_OK);
2N/A}
2N/A
2N/A/*
2N/A * Check for invalid and/or duplicate attribute specification
2N/A */
2N/Astatic dladm_status_t
2N/Aflow_attrlist_check(dladm_arg_list_t *attrlist)
2N/A{
2N/A int i, j;
2N/A boolean_t isset[DLADM_MAX_FLOWATTRS];
2N/A boolean_t matched;
2N/A dladm_status_t status = DLADM_STATUS_OK;
2N/A
2N/A for (j = 0; j < DLADM_MAX_FLOWATTRS; j++)
2N/A isset[j] = B_FALSE;
2N/A
2N/A for (i = 0; i < attrlist->al_count; i++) {
2N/A matched = B_FALSE;
2N/A for (j = 0; j < DLADM_MAX_FLOWATTRS; j++) {
2N/A if (strcmp(attrlist->al_info[i].ai_name,
2N/A attr_table[j].ad_name) == 0) {
2N/A if (isset[j])
2N/A return (DLADM_STATUS_FLOW_INCOMPATIBLE);
2N/A else
2N/A isset[j] = B_TRUE;
2N/A matched = B_TRUE;
2N/A }
2N/A }
2N/A /*
2N/A * if the attribute did not match any of the attribute in
2N/A * attr_table, then it's an invalid attribute.
2N/A */
2N/A if (!matched) {
2N/A status = DLADM_STATUS_BADVAL;
2N/A attrlist->al_info[i].ai_flags |= DLADM_ARG_BADARG;
2N/A }
2N/A }
2N/A return (status);
2N/A}
2N/A
2N/A/*
2N/A * Convert an attribute list to a flow_desc_t using the attribute ad_check()
2N/A * functions.
2N/A */
2N/Adladm_status_t
2N/Adladm_flow_attrlist_extract(dladm_arg_list_t *attrlist, flow_desc_t *flowdesc)
2N/A{
2N/A dladm_status_t status = DLADM_STATUS_BADARG;
2N/A int i;
2N/A
2N/A for (i = 0; i < attrlist->al_count; i++) {
2N/A dladm_arg_info_t *aip = &attrlist->al_info[i];
2N/A int j;
2N/A
2N/A for (j = 0; j < DLADM_MAX_FLOWATTRS; j++) {
2N/A fattr_desc_t *adp = &attr_table[j];
2N/A
2N/A if (strcasecmp(aip->ai_name, adp->ad_name) != 0)
2N/A continue;
2N/A
2N/A if ((aip->ai_val == NULL) || (*aip->ai_val == NULL))
2N/A return (DLADM_STATUS_BADARG);
2N/A
2N/A if (adp->ad_check != NULL)
2N/A status = adp->ad_check(*aip->ai_val, flowdesc);
2N/A else
2N/A status = DLADM_STATUS_BADARG;
2N/A
2N/A if (status != DLADM_STATUS_OK)
2N/A return (status);
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * Make sure protocol is specified if either local or
2N/A * remote port is specified.
2N/A */
2N/A if ((flowdesc->fd_mask &
2N/A (FLOW_ULP_PORT_LOCAL | FLOW_ULP_PORT_REMOTE)) != 0 &&
2N/A (flowdesc->fd_mask & FLOW_IP_PROTOCOL) == 0)
2N/A return (DLADM_STATUS_PORT_NOPROTO);
2N/A
2N/A return (status);
2N/A}
2N/A
2N/Avoid
2N/Adladm_free_attrs(dladm_arg_list_t *list)
2N/A{
2N/A dladm_free_args(list);
2N/A}
2N/A
2N/Adladm_status_t
2N/Adladm_parse_flow_attrs(char *str, dladm_arg_list_t **listp, boolean_t novalues)
2N/A{
2N/A
2N/A if (dladm_parse_args(str, listp, novalues)
2N/A != DLADM_STATUS_OK)
2N/A return (DLADM_STATUS_ATTR_PARSE_ERR);
2N/A
2N/A if (*listp != NULL && flow_attrlist_check(*listp)
2N/A != DLADM_STATUS_OK) {
2N/A dladm_free_attrs(*listp);
2N/A return (DLADM_STATUS_ATTR_PARSE_ERR);
2N/A }
2N/A
2N/A return (DLADM_STATUS_OK);
2N/A}
2N/A
2N/Adladm_status_t
2N/Adladm_check_dsfield(char *str, flow_desc_t *fd)
2N/A{
2N/A char *mask_str, *endp = NULL;
2N/A uint_t mask = 0xff, value;
2N/A
2N/A if ((mask_str = strchr(str, ':')) != NULL) {
2N/A *mask_str++ = '\0';
2N/A errno = 0;
2N/A mask = strtoul(mask_str, &endp, 16);
2N/A if (errno != 0 || mask == 0 || mask > 0xff ||
2N/A *endp != '\0')
2N/A return (DLADM_STATUS_INVALID_DSFMASK);
2N/A }
2N/A errno = 0;
2N/A endp = NULL;
2N/A value = strtoul(str, &endp, 16);
2N/A if (errno != 0 || value == 0 || value > 0xff || *endp != '\0')
2N/A return (DLADM_STATUS_INVALID_DSF);
2N/A
2N/A fd->fd_dsfield = (uint8_t)value;
2N/A fd->fd_dsfield_mask = (uint8_t)mask;
2N/A fd->fd_mask |= FLOW_IP_DSFIELD;
2N/A return (DLADM_STATUS_OK);
2N/A}
2N/A
2N/Achar *
2N/Adladm_proto2str(uint8_t protocol)
2N/A{
2N/A if (protocol == IPPROTO_TCP)
2N/A return ("tcp");
2N/A if (protocol == IPPROTO_UDP)
2N/A return ("udp");
2N/A if (protocol == IPPROTO_SCTP)
2N/A return ("sctp");
2N/A if (protocol == IPPROTO_ICMPV6)
2N/A return ("icmpv6");
2N/A if (protocol == IPPROTO_ICMP)
2N/A return ("icmp");
2N/A else
2N/A return ("");
2N/A}
2N/A
2N/Auint8_t
2N/Adladm_str2proto(const char *protostr)
2N/A{
2N/A if (strncasecmp(protostr, "tcp", 3) == 0)
2N/A return (IPPROTO_TCP);
2N/A else if (strncasecmp(protostr, "udp", 3) == 0)
2N/A return (IPPROTO_UDP);
2N/A else if (strncasecmp(protostr, "sctp", 4) == 0)
2N/A return (IPPROTO_SCTP);
2N/A else if (strncasecmp(protostr, "icmpv6", 6) == 0)
2N/A return (IPPROTO_ICMPV6);
2N/A else if (strncasecmp(protostr, "icmp", 4) == 0)
2N/A return (IPPROTO_ICMP);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Avoid
2N/Adladm_flow_attr_ip2str(dladm_flow_attr_t *attrp, char *buf, size_t buf_len)
2N/A{
2N/A flow_desc_t fdesc = attrp->fa_flow_desc;
2N/A struct in_addr ipaddr;
2N/A int prefix_len, prefix_max;
2N/A char *cp, abuf[INET6_ADDRSTRLEN];
2N/A
2N/A if (fdesc.fd_mask & FLOW_IP_LOCAL) {
2N/A if (fdesc.fd_ipversion == IPV6_VERSION) {
2N/A (void) inet_ntop(AF_INET6, &fdesc.fd_local_addr, abuf,
2N/A INET6_ADDRSTRLEN);
2N/A cp = abuf;
2N/A prefix_max = IPV6_ABITS;
2N/A } else {
2N/A ipaddr.s_addr = fdesc.fd_local_addr._S6_un._S6_u32[3];
2N/A cp = inet_ntoa(ipaddr);
2N/A prefix_max = IP_ABITS;
2N/A }
2N/A (void) dladm_mask2prefixlen(&fdesc.fd_local_netmask,
2N/A prefix_max, &prefix_len);
2N/A (void) snprintf(buf, buf_len, "LCL:%s/%d ", cp, prefix_len);
2N/A } else if (fdesc.fd_mask & FLOW_IP_REMOTE) {
2N/A if (fdesc.fd_ipversion == IPV6_VERSION) {
2N/A (void) inet_ntop(AF_INET6, &fdesc.fd_remote_addr, abuf,
2N/A INET6_ADDRSTRLEN);
2N/A cp = abuf;
2N/A prefix_max = IPV6_ABITS;
2N/A } else {
2N/A ipaddr.s_addr = fdesc.fd_remote_addr._S6_un._S6_u32[3];
2N/A cp = inet_ntoa(ipaddr);
2N/A prefix_max = IP_ABITS;
2N/A }
2N/A (void) dladm_mask2prefixlen(&fdesc.fd_remote_netmask,
2N/A prefix_max, &prefix_len);
2N/A (void) snprintf(buf, buf_len, "RMT:%s/%d ", cp, prefix_len);
2N/A } else {
2N/A buf[0] = '\0';
2N/A }
2N/A}
2N/A
2N/Avoid
2N/Adladm_flow_attr_proto2str(dladm_flow_attr_t *attrp, char *buf, size_t buf_len)
2N/A{
2N/A flow_desc_t fdesc = attrp->fa_flow_desc;
2N/A
2N/A (void) snprintf(buf, buf_len, "%s",
2N/A dladm_proto2str(fdesc.fd_protocol));
2N/A}
2N/A
2N/Avoid
2N/Adladm_flow_attr_port2str(dladm_flow_attr_t *attrp, char *buf, size_t buf_len)
2N/A{
2N/A flow_desc_t fdesc = attrp->fa_flow_desc;
2N/A
2N/A if (fdesc.fd_mask & FLOW_ULP_PORT_LOCAL) {
2N/A (void) snprintf(buf, buf_len, "%d",
2N/A ntohs(fdesc.fd_local_port));
2N/A } else if (fdesc.fd_mask & FLOW_ULP_PORT_REMOTE) {
2N/A (void) snprintf(buf, buf_len, "%d",
2N/A ntohs(fdesc.fd_remote_port));
2N/A } else {
2N/A buf[0] = '\0';
2N/A }
2N/A}
2N/A
2N/Avoid
2N/Adladm_flow_attr_dsfield2str(dladm_flow_attr_t *attrp, char *buf, size_t buf_len)
2N/A{
2N/A flow_desc_t fdesc = attrp->fa_flow_desc;
2N/A
2N/A if (fdesc.fd_mask & FLOW_IP_DSFIELD) {
2N/A (void) snprintf(buf, buf_len, "0x%x:0x%x",
2N/A fdesc.fd_dsfield, fdesc.fd_dsfield_mask);
2N/A } else {
2N/A buf[0] = '\0';
2N/A }
2N/A}