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 /* 228558SGirish.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; 53*10734SEric Cheng static fad_checkf_t do_check_remote_port; 548275SEric Cheng 558275SEric Cheng static dladm_status_t do_check_port(char *, boolean_t, flow_desc_t *); 568275SEric Cheng 578275SEric Cheng static fattr_desc_t attr_table[] = { 58*10734SEric Cheng { "local_ip", do_check_local_ip }, 59*10734SEric Cheng { "remote_ip", do_check_remote_ip }, 60*10734SEric Cheng { "transport", do_check_protocol }, 61*10734SEric Cheng { "local_port", do_check_local_port }, 62*10734SEric Cheng { "remote_port", do_check_remote_port }, 63*10734SEric Cheng { "dsfield", do_check_dsfield }, 648275SEric Cheng }; 658275SEric Cheng 668275SEric Cheng #define DLADM_MAX_FLOWATTRS (sizeof (attr_table) / sizeof (fattr_desc_t)) 678275SEric Cheng 688275SEric Cheng static dladm_status_t 698275SEric Cheng do_check_local_ip(char *attr_val, flow_desc_t *fdesc) 708275SEric Cheng { 718275SEric Cheng return (do_check_ip_addr(attr_val, B_TRUE, fdesc)); 728275SEric Cheng } 738275SEric Cheng 748275SEric Cheng static dladm_status_t 758275SEric Cheng do_check_remote_ip(char *attr_val, flow_desc_t *fdesc) 768275SEric Cheng { 778275SEric Cheng return (do_check_ip_addr(attr_val, B_FALSE, fdesc)); 788275SEric Cheng } 798275SEric Cheng 808275SEric Cheng dladm_status_t 818275SEric Cheng do_check_ip_addr(char *addr_str, boolean_t local, flow_desc_t *fd) 828275SEric Cheng { 838275SEric Cheng dladm_status_t status; 848558SGirish.Moodalbail@Sun.COM int prefix_max, prefix_len = 0; 858275SEric Cheng char *prefix_str, *endp = NULL; 868275SEric Cheng flow_mask_t mask; 878275SEric Cheng in6_addr_t *addr; 888275SEric Cheng uchar_t *netmask; 898558SGirish.Moodalbail@Sun.COM struct in_addr v4addr; 908558SGirish.Moodalbail@Sun.COM struct in6_addr v6addr; 918558SGirish.Moodalbail@Sun.COM int family; 928275SEric Cheng 938275SEric Cheng if ((prefix_str = strchr(addr_str, '/')) != NULL) { 948275SEric Cheng *prefix_str++ = '\0'; 958275SEric Cheng errno = 0; 968275SEric Cheng prefix_len = (int)strtol(prefix_str, &endp, 10); 978275SEric Cheng if (errno != 0 || prefix_len == 0 || *endp != '\0') 988275SEric Cheng return (DLADM_STATUS_INVALID_PREFIXLEN); 998275SEric Cheng } 1008558SGirish.Moodalbail@Sun.COM if (inet_pton(AF_INET, addr_str, &v4addr.s_addr) == 1) { 1018558SGirish.Moodalbail@Sun.COM family = AF_INET; 1028558SGirish.Moodalbail@Sun.COM } else if (inet_pton(AF_INET6, addr_str, v6addr.s6_addr) == 1) { 1038558SGirish.Moodalbail@Sun.COM family = AF_INET6; 1048558SGirish.Moodalbail@Sun.COM } else { 1058275SEric Cheng return (DLADM_STATUS_INVALID_IP); 1068558SGirish.Moodalbail@Sun.COM } 1078275SEric Cheng 1088275SEric Cheng mask = FLOW_IP_VERSION; 1098275SEric Cheng if (local) { 1108275SEric Cheng mask |= FLOW_IP_LOCAL; 1118275SEric Cheng addr = &fd->fd_local_addr; 1128275SEric Cheng netmask = (uchar_t *)&fd->fd_local_netmask; 1138275SEric Cheng } else { 1148275SEric Cheng mask |= FLOW_IP_REMOTE; 1158275SEric Cheng addr = &fd->fd_remote_addr; 1168275SEric Cheng netmask = (uchar_t *)&fd->fd_remote_netmask; 1178275SEric Cheng } 1188275SEric Cheng 1198558SGirish.Moodalbail@Sun.COM if (family == AF_INET) { 1208558SGirish.Moodalbail@Sun.COM IN6_INADDR_TO_V4MAPPED(&v4addr, addr); 1218275SEric Cheng prefix_max = IP_ABITS; 1228275SEric Cheng fd->fd_ipversion = IPV4_VERSION; 1238275SEric Cheng netmask = (uchar_t *) 1248275SEric Cheng &(V4_PART_OF_V6((*((in6_addr_t *)(void *)netmask)))); 1258558SGirish.Moodalbail@Sun.COM } else { 1268558SGirish.Moodalbail@Sun.COM *addr = v6addr; 1278275SEric Cheng prefix_max = IPV6_ABITS; 1288275SEric Cheng fd->fd_ipversion = IPV6_VERSION; 1298275SEric Cheng } 1308275SEric Cheng 1318275SEric Cheng if (prefix_len == 0) 1328275SEric Cheng prefix_len = prefix_max; 1338275SEric Cheng 1348275SEric Cheng status = dladm_prefixlen2mask(prefix_len, prefix_max, netmask); 1358275SEric Cheng 1368275SEric Cheng if (status != DLADM_STATUS_OK) { 1378275SEric Cheng return (DLADM_STATUS_INVALID_PREFIXLEN); 1388275SEric Cheng } 1398275SEric Cheng 1408275SEric Cheng fd->fd_mask |= mask; 1418275SEric Cheng return (DLADM_STATUS_OK); 1428275SEric Cheng } 1438275SEric Cheng 1448275SEric Cheng dladm_status_t 1458275SEric Cheng do_check_protocol(char *attr_val, flow_desc_t *fdesc) 1468275SEric Cheng { 1478275SEric Cheng uint8_t protocol; 1488275SEric Cheng 1498275SEric Cheng protocol = dladm_str2proto(attr_val); 1508275SEric Cheng 1518275SEric Cheng if (protocol != 0) { 1528275SEric Cheng fdesc->fd_mask |= FLOW_IP_PROTOCOL; 1538275SEric Cheng fdesc->fd_protocol = protocol; 1548275SEric Cheng return (DLADM_STATUS_OK); 1558275SEric Cheng } else { 1568275SEric Cheng return (DLADM_STATUS_INVALID_PROTOCOL); 1578275SEric Cheng } 1588275SEric Cheng } 1598275SEric Cheng 1608275SEric Cheng dladm_status_t 1618275SEric Cheng do_check_local_port(char *attr_val, flow_desc_t *fdesc) 1628275SEric Cheng { 1638275SEric Cheng return (do_check_port(attr_val, B_TRUE, fdesc)); 1648275SEric Cheng } 1658275SEric Cheng 1668275SEric Cheng dladm_status_t 167*10734SEric Cheng do_check_remote_port(char *attr_val, flow_desc_t *fdesc) 168*10734SEric Cheng { 169*10734SEric Cheng return (do_check_port(attr_val, B_FALSE, fdesc)); 170*10734SEric Cheng } 171*10734SEric Cheng 172*10734SEric Cheng dladm_status_t 1738275SEric Cheng do_check_port(char *attr_val, boolean_t local, flow_desc_t *fdesc) 1748275SEric Cheng { 1758275SEric Cheng char *endp = NULL; 1768275SEric Cheng long val; 1778275SEric Cheng 178*10734SEric Cheng val = strtol(attr_val, &endp, 10); 179*10734SEric Cheng if (val < 1 || val > MAX_PORT || *endp != '\0') 180*10734SEric Cheng return (DLADM_STATUS_INVALID_PORT); 1818275SEric Cheng if (local) { 1828275SEric Cheng fdesc->fd_mask |= FLOW_ULP_PORT_LOCAL; 1838275SEric Cheng fdesc->fd_local_port = htons((uint16_t)val); 1848275SEric Cheng } else { 185*10734SEric Cheng fdesc->fd_mask |= FLOW_ULP_PORT_REMOTE; 186*10734SEric Cheng fdesc->fd_remote_port = htons((uint16_t)val); 1878275SEric Cheng } 1888275SEric Cheng 1898275SEric Cheng return (DLADM_STATUS_OK); 1908275SEric Cheng } 1918275SEric Cheng 1928275SEric Cheng /* 1938275SEric Cheng * Check for invalid and/or duplicate attribute specification 1948275SEric Cheng */ 1958275SEric Cheng static dladm_status_t 1968275SEric Cheng flow_attrlist_check(dladm_arg_list_t *attrlist) 1978275SEric Cheng { 1988275SEric Cheng int i, j; 1998275SEric Cheng boolean_t isset[DLADM_MAX_FLOWATTRS]; 2008275SEric Cheng boolean_t matched; 2018275SEric Cheng 2028275SEric Cheng for (j = 0; j < DLADM_MAX_FLOWATTRS; j++) 2038275SEric Cheng isset[j] = B_FALSE; 2048275SEric Cheng 2058275SEric Cheng for (i = 0; i < attrlist->al_count; i++) { 2068275SEric Cheng matched = B_FALSE; 2078275SEric Cheng for (j = 0; j < DLADM_MAX_FLOWATTRS; j++) { 2088275SEric Cheng if (strcmp(attrlist->al_info[i].ai_name, 2098275SEric Cheng attr_table[j].ad_name) == 0) { 2108275SEric Cheng if (isset[j]) 2118275SEric Cheng return (DLADM_STATUS_FLOW_INCOMPATIBLE); 2128275SEric Cheng else 2138275SEric Cheng isset[j] = B_TRUE; 2148275SEric Cheng matched = B_TRUE; 2158275SEric Cheng } 2168275SEric Cheng } 2178275SEric Cheng /* 2188275SEric Cheng * if the attribute did not match any of the attribute in 2198275SEric Cheng * attr_table, then it's an invalid attribute. 2208275SEric Cheng */ 2218275SEric Cheng if (!matched) 2228275SEric Cheng return (DLADM_STATUS_BADARG); 2238275SEric Cheng } 2248275SEric Cheng return (DLADM_STATUS_OK); 2258275SEric Cheng } 2268275SEric Cheng 2278275SEric Cheng /* 2288275SEric Cheng * Convert an attribute list to a flow_desc_t using the attribute ad_check() 2298275SEric Cheng * functions. 2308275SEric Cheng */ 2318275SEric Cheng dladm_status_t 2328275SEric Cheng dladm_flow_attrlist_extract(dladm_arg_list_t *attrlist, flow_desc_t *flowdesc) 2338275SEric Cheng { 2348275SEric Cheng dladm_status_t status = DLADM_STATUS_BADARG; 2358275SEric Cheng int i; 2368275SEric Cheng 2378275SEric Cheng for (i = 0; i < attrlist->al_count; i++) { 2388275SEric Cheng dladm_arg_info_t *aip = &attrlist->al_info[i]; 2398275SEric Cheng int j; 2408275SEric Cheng 2418275SEric Cheng for (j = 0; j < DLADM_MAX_FLOWATTRS; j++) { 2428275SEric Cheng fattr_desc_t *adp = &attr_table[j]; 2438275SEric Cheng 2448275SEric Cheng if (strcasecmp(aip->ai_name, adp->ad_name) != 0) 2458275SEric Cheng continue; 2468275SEric Cheng 2478275SEric Cheng if ((aip->ai_val == NULL) || (*aip->ai_val == NULL)) 2488275SEric Cheng return (DLADM_STATUS_BADARG); 2498275SEric Cheng 2508275SEric Cheng if (adp->ad_check != NULL) 2518275SEric Cheng status = adp->ad_check(*aip->ai_val, flowdesc); 2528275SEric Cheng else 2538275SEric Cheng status = DLADM_STATUS_BADARG; 2548275SEric Cheng 2558275SEric Cheng if (status != DLADM_STATUS_OK) 2568275SEric Cheng return (status); 2578275SEric Cheng } 2588275SEric Cheng } 2598275SEric Cheng return (status); 2608275SEric Cheng } 2618275SEric Cheng 2628275SEric Cheng void 2638275SEric Cheng dladm_free_attrs(dladm_arg_list_t *list) 2648275SEric Cheng { 2658275SEric Cheng dladm_free_args(list); 2668275SEric Cheng } 2678275SEric Cheng 2688275SEric Cheng dladm_status_t 2698275SEric Cheng dladm_parse_flow_attrs(char *str, dladm_arg_list_t **listp, boolean_t novalues) 2708275SEric Cheng { 2718275SEric Cheng 2728275SEric Cheng if (dladm_parse_args(str, listp, novalues) 2738275SEric Cheng != DLADM_STATUS_OK) 2748275SEric Cheng return (DLADM_STATUS_ATTR_PARSE_ERR); 2758275SEric Cheng 2769055SMichael.Lim@Sun.COM if (*listp != NULL && flow_attrlist_check(*listp) 2779055SMichael.Lim@Sun.COM != DLADM_STATUS_OK) { 2788275SEric Cheng dladm_free_attrs(*listp); 2798275SEric Cheng return (DLADM_STATUS_ATTR_PARSE_ERR); 2808275SEric Cheng } 2818275SEric Cheng 2828275SEric Cheng return (DLADM_STATUS_OK); 2838275SEric Cheng } 2848275SEric Cheng 2858275SEric Cheng dladm_status_t 2868275SEric Cheng do_check_dsfield(char *str, flow_desc_t *fd) 2878275SEric Cheng { 2888275SEric Cheng char *mask_str, *endp = NULL; 2898275SEric Cheng uint_t mask = 0xff, value; 2908275SEric Cheng 2918275SEric Cheng if ((mask_str = strchr(str, ':')) != NULL) { 2928275SEric Cheng *mask_str++ = '\0'; 2938275SEric Cheng errno = 0; 2948275SEric Cheng mask = strtoul(mask_str, &endp, 16); 2958275SEric Cheng if (errno != 0 || mask == 0 || mask > 0xff || 2968275SEric Cheng *endp != '\0') 2978275SEric Cheng return (DLADM_STATUS_INVALID_DSFMASK); 2988275SEric Cheng } 2998275SEric Cheng errno = 0; 3008275SEric Cheng endp = NULL; 3018275SEric Cheng value = strtoul(str, &endp, 16); 3028275SEric Cheng if (errno != 0 || value == 0 || value > 0xff || *endp != '\0') 3038275SEric Cheng return (DLADM_STATUS_INVALID_DSF); 3048275SEric Cheng 3058275SEric Cheng fd->fd_dsfield = (uint8_t)value; 3068275SEric Cheng fd->fd_dsfield_mask = (uint8_t)mask; 3078275SEric Cheng fd->fd_mask |= FLOW_IP_DSFIELD; 3088275SEric Cheng return (DLADM_STATUS_OK); 3098275SEric Cheng } 3108275SEric Cheng 3118275SEric Cheng char * 3128275SEric Cheng dladm_proto2str(uint8_t protocol) 3138275SEric Cheng { 3148275SEric Cheng if (protocol == IPPROTO_TCP) 3158275SEric Cheng return ("tcp"); 3168275SEric Cheng if (protocol == IPPROTO_UDP) 3178275SEric Cheng return ("udp"); 3188275SEric Cheng if (protocol == IPPROTO_SCTP) 3198275SEric Cheng return ("sctp"); 3208275SEric Cheng if (protocol == IPPROTO_ICMPV6) 3218275SEric Cheng return ("icmpv6"); 3228275SEric Cheng if (protocol == IPPROTO_ICMP) 3238275SEric Cheng return ("icmp"); 3248275SEric Cheng else 3258275SEric Cheng return (""); 3268275SEric Cheng } 3278275SEric Cheng 3288275SEric Cheng uint8_t 3298275SEric Cheng dladm_str2proto(const char *protostr) 3308275SEric Cheng { 3318275SEric Cheng if (strncasecmp(protostr, "tcp", 3) == 0) 3328275SEric Cheng return (IPPROTO_TCP); 3338275SEric Cheng else if (strncasecmp(protostr, "udp", 3) == 0) 3348275SEric Cheng return (IPPROTO_UDP); 3358275SEric Cheng else if (strncasecmp(protostr, "sctp", 4) == 0) 3368275SEric Cheng return (IPPROTO_SCTP); 3378275SEric Cheng else if (strncasecmp(protostr, "icmpv6", 6) == 0) 3388275SEric Cheng return (IPPROTO_ICMPV6); 3398275SEric Cheng else if (strncasecmp(protostr, "icmp", 4) == 0) 3408275SEric Cheng return (IPPROTO_ICMP); 3418275SEric Cheng 3428275SEric Cheng return (0); 3438275SEric Cheng } 3448275SEric Cheng 3458275SEric Cheng void 3468275SEric Cheng dladm_flow_attr_ip2str(dladm_flow_attr_t *attrp, char *buf, size_t buf_len) 3478275SEric Cheng { 3488275SEric Cheng flow_desc_t fdesc = attrp->fa_flow_desc; 3498275SEric Cheng struct in_addr ipaddr; 3508275SEric Cheng int prefix_len, prefix_max; 3518275SEric Cheng char *cp, abuf[INET6_ADDRSTRLEN]; 3528275SEric Cheng 3538275SEric Cheng if (fdesc.fd_mask & FLOW_IP_LOCAL) { 3548275SEric Cheng if (fdesc.fd_ipversion == IPV6_VERSION) { 3558275SEric Cheng (void) inet_ntop(AF_INET6, &fdesc.fd_local_addr, abuf, 3568275SEric Cheng INET6_ADDRSTRLEN); 3578275SEric Cheng cp = abuf; 3588275SEric Cheng prefix_max = IPV6_ABITS; 3598275SEric Cheng } else { 3608275SEric Cheng ipaddr.s_addr = fdesc.fd_local_addr._S6_un._S6_u32[3]; 3618275SEric Cheng cp = inet_ntoa(ipaddr); 3628275SEric Cheng prefix_max = IP_ABITS; 3638275SEric Cheng } 3648275SEric Cheng (void) dladm_mask2prefixlen(&fdesc.fd_local_netmask, 3658275SEric Cheng prefix_max, &prefix_len); 3668275SEric Cheng (void) snprintf(buf, buf_len, "LCL:%s/%d ", cp, prefix_len); 3678275SEric Cheng } else if (fdesc.fd_mask & FLOW_IP_REMOTE) { 3688275SEric Cheng if (fdesc.fd_ipversion == IPV6_VERSION) { 3698275SEric Cheng (void) inet_ntop(AF_INET6, &fdesc.fd_remote_addr, abuf, 3708275SEric Cheng INET6_ADDRSTRLEN); 3718275SEric Cheng cp = abuf; 3728275SEric Cheng prefix_max = IPV6_ABITS; 3738275SEric Cheng } else { 3748275SEric Cheng ipaddr.s_addr = fdesc.fd_remote_addr._S6_un._S6_u32[3]; 3758275SEric Cheng cp = inet_ntoa(ipaddr); 3768275SEric Cheng prefix_max = IP_ABITS; 3778275SEric Cheng } 3788275SEric Cheng (void) dladm_mask2prefixlen(&fdesc.fd_remote_netmask, 3798275SEric Cheng prefix_max, &prefix_len); 3808275SEric Cheng (void) snprintf(buf, buf_len, "RMT:%s/%d ", cp, prefix_len); 3818275SEric Cheng } else { 3828275SEric Cheng buf[0] = '\0'; 3838275SEric Cheng } 3848275SEric Cheng } 3858275SEric Cheng 3868275SEric Cheng void 3878275SEric Cheng dladm_flow_attr_proto2str(dladm_flow_attr_t *attrp, char *buf, size_t buf_len) 3888275SEric Cheng { 3898275SEric Cheng flow_desc_t fdesc = attrp->fa_flow_desc; 3908275SEric Cheng 3918275SEric Cheng (void) snprintf(buf, buf_len, "%s", 3928275SEric Cheng dladm_proto2str(fdesc.fd_protocol)); 3938275SEric Cheng } 3948275SEric Cheng 3958275SEric Cheng void 3968275SEric Cheng dladm_flow_attr_port2str(dladm_flow_attr_t *attrp, char *buf, size_t buf_len) 3978275SEric Cheng { 3988275SEric Cheng flow_desc_t fdesc = attrp->fa_flow_desc; 3998275SEric Cheng 4008275SEric Cheng if (fdesc.fd_mask & FLOW_ULP_PORT_LOCAL) { 4018275SEric Cheng (void) snprintf(buf, buf_len, "%d", 4028275SEric Cheng ntohs(fdesc.fd_local_port)); 403*10734SEric Cheng } else if (fdesc.fd_mask & FLOW_ULP_PORT_REMOTE) { 404*10734SEric Cheng (void) snprintf(buf, buf_len, "%d", 405*10734SEric Cheng ntohs(fdesc.fd_remote_port)); 4068275SEric Cheng } else { 4078275SEric Cheng buf[0] = '\0'; 4088275SEric Cheng } 4098275SEric Cheng } 4108275SEric Cheng 4118275SEric Cheng void 4128275SEric Cheng dladm_flow_attr_dsfield2str(dladm_flow_attr_t *attrp, char *buf, size_t buf_len) 4138275SEric Cheng { 4148275SEric Cheng flow_desc_t fdesc = attrp->fa_flow_desc; 4158275SEric Cheng 4168275SEric Cheng if (fdesc.fd_mask & FLOW_IP_DSFIELD) { 4178275SEric Cheng (void) snprintf(buf, buf_len, "0x%x:0x%x", 4188275SEric Cheng fdesc.fd_dsfield, fdesc.fd_dsfield_mask); 4198275SEric Cheng } else { 4208275SEric Cheng buf[0] = '\0'; 4218275SEric Cheng } 4228275SEric Cheng } 423