18275SEric Cheng /* 28275SEric Cheng * CDDL HEADER START 38275SEric Cheng * 48275SEric Cheng * The contents of this file are subject to the terms of the 58275SEric Cheng * Common Development and Distribution License (the "License"). 68275SEric Cheng * You may not use this file except in compliance with the License. 78275SEric Cheng * 88275SEric Cheng * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 98275SEric Cheng * or http://www.opensolaris.org/os/licensing. 108275SEric Cheng * See the License for the specific language governing permissions 118275SEric Cheng * and limitations under the License. 128275SEric Cheng * 138275SEric Cheng * When distributing Covered Code, include this CDDL HEADER in each 148275SEric Cheng * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 158275SEric Cheng * If applicable, add the following below this CDDL HEADER, with the 168275SEric Cheng * fields enclosed by brackets "[]" replaced with your own identifying 178275SEric Cheng * information: Portions Copyright [yyyy] [name of copyright owner] 188275SEric Cheng * 198275SEric Cheng * CDDL HEADER END 208275SEric Cheng */ 218275SEric Cheng /* 22*8558SGirish.Moodalbail@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 238275SEric Cheng * Use is subject to license terms. 248275SEric Cheng */ 258275SEric Cheng 268275SEric Cheng #include <errno.h> 278275SEric Cheng #include <stdlib.h> 288275SEric Cheng #include <strings.h> 298275SEric Cheng #include <sys/mac_flow.h> 308275SEric Cheng #include <sys/types.h> 318275SEric Cheng #include <sys/socket.h> 328275SEric Cheng #include <netinet/in.h> 338275SEric Cheng #include <arpa/inet.h> 348275SEric Cheng #include <netdb.h> 358275SEric Cheng #include <net/if_types.h> 368275SEric Cheng #include <net/if_dl.h> 378275SEric Cheng #include <inet/ip.h> 388275SEric Cheng #include <inet/ip6.h> 398275SEric Cheng 408275SEric Cheng #include <libdladm.h> 418275SEric Cheng #include <libdlflow.h> 428275SEric Cheng #include <libdlflow_impl.h> 438275SEric Cheng 448275SEric Cheng #define V4_PART_OF_V6(v6) ((v6)._S6_un._S6_u32[3]) 458275SEric Cheng 468275SEric Cheng /* max port number for UDP, TCP & SCTP */ 478275SEric Cheng #define MAX_PORT 65535 488275SEric Cheng 498275SEric Cheng static fad_checkf_t do_check_local_ip; 508275SEric Cheng static fad_checkf_t do_check_remote_ip; 518275SEric Cheng static fad_checkf_t do_check_protocol; 528275SEric Cheng static fad_checkf_t do_check_local_port; 538275SEric Cheng 548275SEric Cheng static dladm_status_t do_check_port(char *, boolean_t, flow_desc_t *); 558275SEric Cheng 568275SEric Cheng static fattr_desc_t attr_table[] = { 578275SEric Cheng { "local_ip", do_check_local_ip }, 588275SEric Cheng { "remote_ip", do_check_remote_ip }, 598275SEric Cheng { "transport", do_check_protocol }, 608275SEric Cheng { "local_port", do_check_local_port }, 618275SEric Cheng { "dsfield", do_check_dsfield }, 628275SEric Cheng }; 638275SEric Cheng 648275SEric Cheng #define DLADM_MAX_FLOWATTRS (sizeof (attr_table) / sizeof (fattr_desc_t)) 658275SEric Cheng 668275SEric Cheng static dladm_status_t 678275SEric Cheng do_check_local_ip(char *attr_val, flow_desc_t *fdesc) 688275SEric Cheng { 698275SEric Cheng return (do_check_ip_addr(attr_val, B_TRUE, fdesc)); 708275SEric Cheng } 718275SEric Cheng 728275SEric Cheng static dladm_status_t 738275SEric Cheng do_check_remote_ip(char *attr_val, flow_desc_t *fdesc) 748275SEric Cheng { 758275SEric Cheng return (do_check_ip_addr(attr_val, B_FALSE, fdesc)); 768275SEric Cheng } 778275SEric Cheng 788275SEric Cheng dladm_status_t 798275SEric Cheng do_check_ip_addr(char *addr_str, boolean_t local, flow_desc_t *fd) 808275SEric Cheng { 818275SEric Cheng dladm_status_t status; 82*8558SGirish.Moodalbail@Sun.COM int prefix_max, prefix_len = 0; 838275SEric Cheng char *prefix_str, *endp = NULL; 848275SEric Cheng flow_mask_t mask; 858275SEric Cheng in6_addr_t *addr; 868275SEric Cheng uchar_t *netmask; 87*8558SGirish.Moodalbail@Sun.COM struct in_addr v4addr; 88*8558SGirish.Moodalbail@Sun.COM struct in6_addr v6addr; 89*8558SGirish.Moodalbail@Sun.COM int family; 908275SEric Cheng 918275SEric Cheng if ((prefix_str = strchr(addr_str, '/')) != NULL) { 928275SEric Cheng *prefix_str++ = '\0'; 938275SEric Cheng errno = 0; 948275SEric Cheng prefix_len = (int)strtol(prefix_str, &endp, 10); 958275SEric Cheng if (errno != 0 || prefix_len == 0 || *endp != '\0') 968275SEric Cheng return (DLADM_STATUS_INVALID_PREFIXLEN); 978275SEric Cheng } 98*8558SGirish.Moodalbail@Sun.COM if (inet_pton(AF_INET, addr_str, &v4addr.s_addr) == 1) { 99*8558SGirish.Moodalbail@Sun.COM family = AF_INET; 100*8558SGirish.Moodalbail@Sun.COM } else if (inet_pton(AF_INET6, addr_str, v6addr.s6_addr) == 1) { 101*8558SGirish.Moodalbail@Sun.COM family = AF_INET6; 102*8558SGirish.Moodalbail@Sun.COM } else { 1038275SEric Cheng return (DLADM_STATUS_INVALID_IP); 104*8558SGirish.Moodalbail@Sun.COM } 1058275SEric Cheng 1068275SEric Cheng mask = FLOW_IP_VERSION; 1078275SEric Cheng if (local) { 1088275SEric Cheng mask |= FLOW_IP_LOCAL; 1098275SEric Cheng addr = &fd->fd_local_addr; 1108275SEric Cheng netmask = (uchar_t *)&fd->fd_local_netmask; 1118275SEric Cheng } else { 1128275SEric Cheng mask |= FLOW_IP_REMOTE; 1138275SEric Cheng addr = &fd->fd_remote_addr; 1148275SEric Cheng netmask = (uchar_t *)&fd->fd_remote_netmask; 1158275SEric Cheng } 1168275SEric Cheng 117*8558SGirish.Moodalbail@Sun.COM if (family == AF_INET) { 118*8558SGirish.Moodalbail@Sun.COM IN6_INADDR_TO_V4MAPPED(&v4addr, addr); 1198275SEric Cheng prefix_max = IP_ABITS; 1208275SEric Cheng fd->fd_ipversion = IPV4_VERSION; 1218275SEric Cheng netmask = (uchar_t *) 1228275SEric Cheng &(V4_PART_OF_V6((*((in6_addr_t *)(void *)netmask)))); 123*8558SGirish.Moodalbail@Sun.COM } else { 124*8558SGirish.Moodalbail@Sun.COM *addr = v6addr; 1258275SEric Cheng prefix_max = IPV6_ABITS; 1268275SEric Cheng fd->fd_ipversion = IPV6_VERSION; 1278275SEric Cheng } 1288275SEric Cheng 1298275SEric Cheng if (prefix_len == 0) 1308275SEric Cheng prefix_len = prefix_max; 1318275SEric Cheng 1328275SEric Cheng status = dladm_prefixlen2mask(prefix_len, prefix_max, netmask); 1338275SEric Cheng 1348275SEric Cheng if (status != DLADM_STATUS_OK) { 1358275SEric Cheng return (DLADM_STATUS_INVALID_PREFIXLEN); 1368275SEric Cheng } 1378275SEric Cheng 1388275SEric Cheng fd->fd_mask |= mask; 1398275SEric Cheng return (DLADM_STATUS_OK); 1408275SEric Cheng } 1418275SEric Cheng 1428275SEric Cheng dladm_status_t 1438275SEric Cheng do_check_protocol(char *attr_val, flow_desc_t *fdesc) 1448275SEric Cheng { 1458275SEric Cheng uint8_t protocol; 1468275SEric Cheng 1478275SEric Cheng protocol = dladm_str2proto(attr_val); 1488275SEric Cheng 1498275SEric Cheng if (protocol != 0) { 1508275SEric Cheng fdesc->fd_mask |= FLOW_IP_PROTOCOL; 1518275SEric Cheng fdesc->fd_protocol = protocol; 1528275SEric Cheng return (DLADM_STATUS_OK); 1538275SEric Cheng } else { 1548275SEric Cheng return (DLADM_STATUS_INVALID_PROTOCOL); 1558275SEric Cheng } 1568275SEric Cheng } 1578275SEric Cheng 1588275SEric Cheng dladm_status_t 1598275SEric Cheng do_check_local_port(char *attr_val, flow_desc_t *fdesc) 1608275SEric Cheng { 1618275SEric Cheng return (do_check_port(attr_val, B_TRUE, fdesc)); 1628275SEric Cheng } 1638275SEric Cheng 1648275SEric Cheng dladm_status_t 1658275SEric Cheng do_check_port(char *attr_val, boolean_t local, flow_desc_t *fdesc) 1668275SEric Cheng { 1678275SEric Cheng char *endp = NULL; 1688275SEric Cheng long val; 1698275SEric Cheng 1708275SEric Cheng if (local) { 1718275SEric Cheng fdesc->fd_mask |= FLOW_ULP_PORT_LOCAL; 1728275SEric Cheng val = strtol(attr_val, &endp, 10); 1738275SEric Cheng if (val < 1 || val > MAX_PORT) 1748275SEric Cheng return (DLADM_STATUS_INVALID_PORT); 1758275SEric Cheng fdesc->fd_local_port = htons((uint16_t)val); 1768275SEric Cheng } else { 1778275SEric Cheng return (DLADM_STATUS_BADVAL); 1788275SEric Cheng } 1798275SEric Cheng 1808275SEric Cheng return (DLADM_STATUS_OK); 1818275SEric Cheng } 1828275SEric Cheng 1838275SEric Cheng /* 1848275SEric Cheng * Check for invalid and/or duplicate attribute specification 1858275SEric Cheng */ 1868275SEric Cheng static dladm_status_t 1878275SEric Cheng flow_attrlist_check(dladm_arg_list_t *attrlist) 1888275SEric Cheng { 1898275SEric Cheng int i, j; 1908275SEric Cheng boolean_t isset[DLADM_MAX_FLOWATTRS]; 1918275SEric Cheng boolean_t matched; 1928275SEric Cheng 1938275SEric Cheng for (j = 0; j < DLADM_MAX_FLOWATTRS; j++) 1948275SEric Cheng isset[j] = B_FALSE; 1958275SEric Cheng 1968275SEric Cheng for (i = 0; i < attrlist->al_count; i++) { 1978275SEric Cheng matched = B_FALSE; 1988275SEric Cheng for (j = 0; j < DLADM_MAX_FLOWATTRS; j++) { 1998275SEric Cheng if (strcmp(attrlist->al_info[i].ai_name, 2008275SEric Cheng attr_table[j].ad_name) == 0) { 2018275SEric Cheng if (isset[j]) 2028275SEric Cheng return (DLADM_STATUS_FLOW_INCOMPATIBLE); 2038275SEric Cheng else 2048275SEric Cheng isset[j] = B_TRUE; 2058275SEric Cheng matched = B_TRUE; 2068275SEric Cheng } 2078275SEric Cheng } 2088275SEric Cheng /* 2098275SEric Cheng * if the attribute did not match any of the attribute in 2108275SEric Cheng * attr_table, then it's an invalid attribute. 2118275SEric Cheng */ 2128275SEric Cheng if (!matched) 2138275SEric Cheng return (DLADM_STATUS_BADARG); 2148275SEric Cheng } 2158275SEric Cheng return (DLADM_STATUS_OK); 2168275SEric Cheng } 2178275SEric Cheng 2188275SEric Cheng /* 2198275SEric Cheng * Convert an attribute list to a flow_desc_t using the attribute ad_check() 2208275SEric Cheng * functions. 2218275SEric Cheng */ 2228275SEric Cheng dladm_status_t 2238275SEric Cheng dladm_flow_attrlist_extract(dladm_arg_list_t *attrlist, flow_desc_t *flowdesc) 2248275SEric Cheng { 2258275SEric Cheng dladm_status_t status = DLADM_STATUS_BADARG; 2268275SEric Cheng int i; 2278275SEric Cheng 2288275SEric Cheng for (i = 0; i < attrlist->al_count; i++) { 2298275SEric Cheng dladm_arg_info_t *aip = &attrlist->al_info[i]; 2308275SEric Cheng int j; 2318275SEric Cheng 2328275SEric Cheng for (j = 0; j < DLADM_MAX_FLOWATTRS; j++) { 2338275SEric Cheng fattr_desc_t *adp = &attr_table[j]; 2348275SEric Cheng 2358275SEric Cheng if (strcasecmp(aip->ai_name, adp->ad_name) != 0) 2368275SEric Cheng continue; 2378275SEric Cheng 2388275SEric Cheng if ((aip->ai_val == NULL) || (*aip->ai_val == NULL)) 2398275SEric Cheng return (DLADM_STATUS_BADARG); 2408275SEric Cheng 2418275SEric Cheng if (adp->ad_check != NULL) 2428275SEric Cheng status = adp->ad_check(*aip->ai_val, flowdesc); 2438275SEric Cheng else 2448275SEric Cheng status = DLADM_STATUS_BADARG; 2458275SEric Cheng 2468275SEric Cheng if (status != DLADM_STATUS_OK) 2478275SEric Cheng return (status); 2488275SEric Cheng } 2498275SEric Cheng } 2508275SEric Cheng return (status); 2518275SEric Cheng } 2528275SEric Cheng 2538275SEric Cheng void 2548275SEric Cheng dladm_free_attrs(dladm_arg_list_t *list) 2558275SEric Cheng { 2568275SEric Cheng dladm_free_args(list); 2578275SEric Cheng } 2588275SEric Cheng 2598275SEric Cheng dladm_status_t 2608275SEric Cheng dladm_parse_flow_attrs(char *str, dladm_arg_list_t **listp, boolean_t novalues) 2618275SEric Cheng { 2628275SEric Cheng 2638275SEric Cheng if (dladm_parse_args(str, listp, novalues) 2648275SEric Cheng != DLADM_STATUS_OK) 2658275SEric Cheng return (DLADM_STATUS_ATTR_PARSE_ERR); 2668275SEric Cheng 2678275SEric Cheng if (flow_attrlist_check(*listp) != DLADM_STATUS_OK) { 2688275SEric Cheng dladm_free_attrs(*listp); 2698275SEric Cheng return (DLADM_STATUS_ATTR_PARSE_ERR); 2708275SEric Cheng } 2718275SEric Cheng 2728275SEric Cheng return (DLADM_STATUS_OK); 2738275SEric Cheng } 2748275SEric Cheng 2758275SEric Cheng dladm_status_t 2768275SEric Cheng do_check_dsfield(char *str, flow_desc_t *fd) 2778275SEric Cheng { 2788275SEric Cheng char *mask_str, *endp = NULL; 2798275SEric Cheng uint_t mask = 0xff, value; 2808275SEric Cheng 2818275SEric Cheng if ((mask_str = strchr(str, ':')) != NULL) { 2828275SEric Cheng *mask_str++ = '\0'; 2838275SEric Cheng errno = 0; 2848275SEric Cheng mask = strtoul(mask_str, &endp, 16); 2858275SEric Cheng if (errno != 0 || mask == 0 || mask > 0xff || 2868275SEric Cheng *endp != '\0') 2878275SEric Cheng return (DLADM_STATUS_INVALID_DSFMASK); 2888275SEric Cheng } 2898275SEric Cheng errno = 0; 2908275SEric Cheng endp = NULL; 2918275SEric Cheng value = strtoul(str, &endp, 16); 2928275SEric Cheng if (errno != 0 || value == 0 || value > 0xff || *endp != '\0') 2938275SEric Cheng return (DLADM_STATUS_INVALID_DSF); 2948275SEric Cheng 2958275SEric Cheng fd->fd_dsfield = (uint8_t)value; 2968275SEric Cheng fd->fd_dsfield_mask = (uint8_t)mask; 2978275SEric Cheng fd->fd_mask |= FLOW_IP_DSFIELD; 2988275SEric Cheng return (DLADM_STATUS_OK); 2998275SEric Cheng } 3008275SEric Cheng 3018275SEric Cheng char * 3028275SEric Cheng dladm_proto2str(uint8_t protocol) 3038275SEric Cheng { 3048275SEric Cheng if (protocol == IPPROTO_TCP) 3058275SEric Cheng return ("tcp"); 3068275SEric Cheng if (protocol == IPPROTO_UDP) 3078275SEric Cheng return ("udp"); 3088275SEric Cheng if (protocol == IPPROTO_SCTP) 3098275SEric Cheng return ("sctp"); 3108275SEric Cheng if (protocol == IPPROTO_ICMPV6) 3118275SEric Cheng return ("icmpv6"); 3128275SEric Cheng if (protocol == IPPROTO_ICMP) 3138275SEric Cheng return ("icmp"); 3148275SEric Cheng else 3158275SEric Cheng return (""); 3168275SEric Cheng } 3178275SEric Cheng 3188275SEric Cheng uint8_t 3198275SEric Cheng dladm_str2proto(const char *protostr) 3208275SEric Cheng { 3218275SEric Cheng if (strncasecmp(protostr, "tcp", 3) == 0) 3228275SEric Cheng return (IPPROTO_TCP); 3238275SEric Cheng else if (strncasecmp(protostr, "udp", 3) == 0) 3248275SEric Cheng return (IPPROTO_UDP); 3258275SEric Cheng else if (strncasecmp(protostr, "sctp", 4) == 0) 3268275SEric Cheng return (IPPROTO_SCTP); 3278275SEric Cheng else if (strncasecmp(protostr, "icmpv6", 6) == 0) 3288275SEric Cheng return (IPPROTO_ICMPV6); 3298275SEric Cheng else if (strncasecmp(protostr, "icmp", 4) == 0) 3308275SEric Cheng return (IPPROTO_ICMP); 3318275SEric Cheng 3328275SEric Cheng return (0); 3338275SEric Cheng } 3348275SEric Cheng 3358275SEric Cheng void 3368275SEric Cheng dladm_flow_attr_ip2str(dladm_flow_attr_t *attrp, char *buf, size_t buf_len) 3378275SEric Cheng { 3388275SEric Cheng flow_desc_t fdesc = attrp->fa_flow_desc; 3398275SEric Cheng struct in_addr ipaddr; 3408275SEric Cheng int prefix_len, prefix_max; 3418275SEric Cheng char *cp, abuf[INET6_ADDRSTRLEN]; 3428275SEric Cheng 3438275SEric Cheng if (fdesc.fd_mask & FLOW_IP_LOCAL) { 3448275SEric Cheng if (fdesc.fd_ipversion == IPV6_VERSION) { 3458275SEric Cheng (void) inet_ntop(AF_INET6, &fdesc.fd_local_addr, abuf, 3468275SEric Cheng INET6_ADDRSTRLEN); 3478275SEric Cheng cp = abuf; 3488275SEric Cheng prefix_max = IPV6_ABITS; 3498275SEric Cheng } else { 3508275SEric Cheng ipaddr.s_addr = fdesc.fd_local_addr._S6_un._S6_u32[3]; 3518275SEric Cheng cp = inet_ntoa(ipaddr); 3528275SEric Cheng prefix_max = IP_ABITS; 3538275SEric Cheng } 3548275SEric Cheng (void) dladm_mask2prefixlen(&fdesc.fd_local_netmask, 3558275SEric Cheng prefix_max, &prefix_len); 3568275SEric Cheng (void) snprintf(buf, buf_len, "LCL:%s/%d ", cp, prefix_len); 3578275SEric Cheng } else if (fdesc.fd_mask & FLOW_IP_REMOTE) { 3588275SEric Cheng if (fdesc.fd_ipversion == IPV6_VERSION) { 3598275SEric Cheng (void) inet_ntop(AF_INET6, &fdesc.fd_remote_addr, abuf, 3608275SEric Cheng INET6_ADDRSTRLEN); 3618275SEric Cheng cp = abuf; 3628275SEric Cheng prefix_max = IPV6_ABITS; 3638275SEric Cheng } else { 3648275SEric Cheng ipaddr.s_addr = fdesc.fd_remote_addr._S6_un._S6_u32[3]; 3658275SEric Cheng cp = inet_ntoa(ipaddr); 3668275SEric Cheng prefix_max = IP_ABITS; 3678275SEric Cheng } 3688275SEric Cheng (void) dladm_mask2prefixlen(&fdesc.fd_remote_netmask, 3698275SEric Cheng prefix_max, &prefix_len); 3708275SEric Cheng (void) snprintf(buf, buf_len, "RMT:%s/%d ", cp, prefix_len); 3718275SEric Cheng } else { 3728275SEric Cheng buf[0] = '\0'; 3738275SEric Cheng } 3748275SEric Cheng } 3758275SEric Cheng 3768275SEric Cheng void 3778275SEric Cheng dladm_flow_attr_proto2str(dladm_flow_attr_t *attrp, char *buf, size_t buf_len) 3788275SEric Cheng { 3798275SEric Cheng flow_desc_t fdesc = attrp->fa_flow_desc; 3808275SEric Cheng 3818275SEric Cheng (void) snprintf(buf, buf_len, "%s", 3828275SEric Cheng dladm_proto2str(fdesc.fd_protocol)); 3838275SEric Cheng } 3848275SEric Cheng 3858275SEric Cheng void 3868275SEric Cheng dladm_flow_attr_port2str(dladm_flow_attr_t *attrp, char *buf, size_t buf_len) 3878275SEric Cheng { 3888275SEric Cheng flow_desc_t fdesc = attrp->fa_flow_desc; 3898275SEric Cheng 3908275SEric Cheng if (fdesc.fd_mask & FLOW_ULP_PORT_LOCAL) { 3918275SEric Cheng (void) snprintf(buf, buf_len, "%d", 3928275SEric Cheng ntohs(fdesc.fd_local_port)); 3938275SEric Cheng } else { 3948275SEric Cheng buf[0] = '\0'; 3958275SEric Cheng } 3968275SEric Cheng } 3978275SEric Cheng 3988275SEric Cheng void 3998275SEric Cheng dladm_flow_attr_dsfield2str(dladm_flow_attr_t *attrp, char *buf, size_t buf_len) 4008275SEric Cheng { 4018275SEric Cheng flow_desc_t fdesc = attrp->fa_flow_desc; 4028275SEric Cheng 4038275SEric Cheng if (fdesc.fd_mask & FLOW_IP_DSFIELD) { 4048275SEric Cheng (void) snprintf(buf, buf_len, "0x%x:0x%x", 4058275SEric Cheng fdesc.fd_dsfield, fdesc.fd_dsfield_mask); 4068275SEric Cheng } else { 4078275SEric Cheng buf[0] = '\0'; 4088275SEric Cheng } 4098275SEric Cheng } 410