1*8275SEric Cheng /* 2*8275SEric Cheng * CDDL HEADER START 3*8275SEric Cheng * 4*8275SEric Cheng * The contents of this file are subject to the terms of the 5*8275SEric Cheng * Common Development and Distribution License (the "License"). 6*8275SEric Cheng * You may not use this file except in compliance with the License. 7*8275SEric Cheng * 8*8275SEric Cheng * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9*8275SEric Cheng * or http://www.opensolaris.org/os/licensing. 10*8275SEric Cheng * See the License for the specific language governing permissions 11*8275SEric Cheng * and limitations under the License. 12*8275SEric Cheng * 13*8275SEric Cheng * When distributing Covered Code, include this CDDL HEADER in each 14*8275SEric Cheng * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15*8275SEric Cheng * If applicable, add the following below this CDDL HEADER, with the 16*8275SEric Cheng * fields enclosed by brackets "[]" replaced with your own identifying 17*8275SEric Cheng * information: Portions Copyright [yyyy] [name of copyright owner] 18*8275SEric Cheng * 19*8275SEric Cheng * CDDL HEADER END 20*8275SEric Cheng */ 21*8275SEric Cheng /* 22*8275SEric Cheng * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23*8275SEric Cheng * Use is subject to license terms. 24*8275SEric Cheng */ 25*8275SEric Cheng 26*8275SEric Cheng #include <errno.h> 27*8275SEric Cheng #include <stdlib.h> 28*8275SEric Cheng #include <strings.h> 29*8275SEric Cheng #include <sys/mac_flow.h> 30*8275SEric Cheng #include <sys/types.h> 31*8275SEric Cheng #include <sys/socket.h> 32*8275SEric Cheng #include <netinet/in.h> 33*8275SEric Cheng #include <arpa/inet.h> 34*8275SEric Cheng #include <netdb.h> 35*8275SEric Cheng #include <net/if_types.h> 36*8275SEric Cheng #include <net/if_dl.h> 37*8275SEric Cheng #include <inet/ip.h> 38*8275SEric Cheng #include <inet/ip6.h> 39*8275SEric Cheng 40*8275SEric Cheng #include <libdladm.h> 41*8275SEric Cheng #include <libdlflow.h> 42*8275SEric Cheng #include <libdlflow_impl.h> 43*8275SEric Cheng 44*8275SEric Cheng #define V4_PART_OF_V6(v6) ((v6)._S6_un._S6_u32[3]) 45*8275SEric Cheng 46*8275SEric Cheng /* max port number for UDP, TCP & SCTP */ 47*8275SEric Cheng #define MAX_PORT 65535 48*8275SEric Cheng 49*8275SEric Cheng static fad_checkf_t do_check_local_ip; 50*8275SEric Cheng static fad_checkf_t do_check_remote_ip; 51*8275SEric Cheng static fad_checkf_t do_check_protocol; 52*8275SEric Cheng static fad_checkf_t do_check_local_port; 53*8275SEric Cheng 54*8275SEric Cheng static dladm_status_t do_check_port(char *, boolean_t, flow_desc_t *); 55*8275SEric Cheng 56*8275SEric Cheng static fattr_desc_t attr_table[] = { 57*8275SEric Cheng { "local_ip", do_check_local_ip }, 58*8275SEric Cheng { "remote_ip", do_check_remote_ip }, 59*8275SEric Cheng { "transport", do_check_protocol }, 60*8275SEric Cheng { "local_port", do_check_local_port }, 61*8275SEric Cheng { "dsfield", do_check_dsfield }, 62*8275SEric Cheng }; 63*8275SEric Cheng 64*8275SEric Cheng #define DLADM_MAX_FLOWATTRS (sizeof (attr_table) / sizeof (fattr_desc_t)) 65*8275SEric Cheng 66*8275SEric Cheng static dladm_status_t 67*8275SEric Cheng do_check_local_ip(char *attr_val, flow_desc_t *fdesc) 68*8275SEric Cheng { 69*8275SEric Cheng return (do_check_ip_addr(attr_val, B_TRUE, fdesc)); 70*8275SEric Cheng } 71*8275SEric Cheng 72*8275SEric Cheng static dladm_status_t 73*8275SEric Cheng do_check_remote_ip(char *attr_val, flow_desc_t *fdesc) 74*8275SEric Cheng { 75*8275SEric Cheng return (do_check_ip_addr(attr_val, B_FALSE, fdesc)); 76*8275SEric Cheng } 77*8275SEric Cheng 78*8275SEric Cheng dladm_status_t 79*8275SEric Cheng do_check_ip_addr(char *addr_str, boolean_t local, flow_desc_t *fd) 80*8275SEric Cheng { 81*8275SEric Cheng struct addrinfo *info = NULL; 82*8275SEric Cheng dladm_status_t status; 83*8275SEric Cheng int err, prefix_max, prefix_len = 0; 84*8275SEric Cheng char *prefix_str, *endp = NULL; 85*8275SEric Cheng flow_mask_t mask; 86*8275SEric Cheng in6_addr_t *addr; 87*8275SEric Cheng uchar_t *netmask; 88*8275SEric Cheng 89*8275SEric Cheng if ((prefix_str = strchr(addr_str, '/')) != NULL) { 90*8275SEric Cheng *prefix_str++ = '\0'; 91*8275SEric Cheng errno = 0; 92*8275SEric Cheng prefix_len = (int)strtol(prefix_str, &endp, 10); 93*8275SEric Cheng if (errno != 0 || prefix_len == 0 || *endp != '\0') 94*8275SEric Cheng return (DLADM_STATUS_INVALID_PREFIXLEN); 95*8275SEric Cheng } 96*8275SEric Cheng 97*8275SEric Cheng err = getaddrinfo(addr_str, NULL, NULL, &info); 98*8275SEric Cheng if (err != 0) 99*8275SEric Cheng return (DLADM_STATUS_INVALID_IP); 100*8275SEric Cheng 101*8275SEric Cheng mask = FLOW_IP_VERSION; 102*8275SEric Cheng if (local) { 103*8275SEric Cheng mask |= FLOW_IP_LOCAL; 104*8275SEric Cheng addr = &fd->fd_local_addr; 105*8275SEric Cheng netmask = (uchar_t *)&fd->fd_local_netmask; 106*8275SEric Cheng } else { 107*8275SEric Cheng mask |= FLOW_IP_REMOTE; 108*8275SEric Cheng addr = &fd->fd_remote_addr; 109*8275SEric Cheng netmask = (uchar_t *)&fd->fd_remote_netmask; 110*8275SEric Cheng } 111*8275SEric Cheng 112*8275SEric Cheng if (info->ai_family == AF_INET) { 113*8275SEric Cheng IN6_INADDR_TO_V4MAPPED(&(((struct sockaddr_in *) 114*8275SEric Cheng (void *)info->ai_addr)->sin_addr), addr); 115*8275SEric Cheng prefix_max = IP_ABITS; 116*8275SEric Cheng fd->fd_ipversion = IPV4_VERSION; 117*8275SEric Cheng netmask = (uchar_t *) 118*8275SEric Cheng &(V4_PART_OF_V6((*((in6_addr_t *)(void *)netmask)))); 119*8275SEric Cheng } else if (info->ai_family == AF_INET6) { 120*8275SEric Cheng *addr = ((struct sockaddr_in6 *) 121*8275SEric Cheng (void *)info->ai_addr)->sin6_addr; 122*8275SEric Cheng prefix_max = IPV6_ABITS; 123*8275SEric Cheng fd->fd_ipversion = IPV6_VERSION; 124*8275SEric Cheng } else { 125*8275SEric Cheng freeaddrinfo(info); 126*8275SEric Cheng return (DLADM_STATUS_INVALID_IP); 127*8275SEric Cheng } 128*8275SEric Cheng 129*8275SEric Cheng if (prefix_len == 0) 130*8275SEric Cheng prefix_len = prefix_max; 131*8275SEric Cheng 132*8275SEric Cheng status = dladm_prefixlen2mask(prefix_len, prefix_max, netmask); 133*8275SEric Cheng 134*8275SEric Cheng if (status != DLADM_STATUS_OK) { 135*8275SEric Cheng freeaddrinfo(info); 136*8275SEric Cheng return (DLADM_STATUS_INVALID_PREFIXLEN); 137*8275SEric Cheng } 138*8275SEric Cheng 139*8275SEric Cheng fd->fd_mask |= mask; 140*8275SEric Cheng freeaddrinfo(info); 141*8275SEric Cheng return (DLADM_STATUS_OK); 142*8275SEric Cheng } 143*8275SEric Cheng 144*8275SEric Cheng dladm_status_t 145*8275SEric Cheng do_check_protocol(char *attr_val, flow_desc_t *fdesc) 146*8275SEric Cheng { 147*8275SEric Cheng uint8_t protocol; 148*8275SEric Cheng 149*8275SEric Cheng protocol = dladm_str2proto(attr_val); 150*8275SEric Cheng 151*8275SEric Cheng if (protocol != 0) { 152*8275SEric Cheng fdesc->fd_mask |= FLOW_IP_PROTOCOL; 153*8275SEric Cheng fdesc->fd_protocol = protocol; 154*8275SEric Cheng return (DLADM_STATUS_OK); 155*8275SEric Cheng } else { 156*8275SEric Cheng return (DLADM_STATUS_INVALID_PROTOCOL); 157*8275SEric Cheng } 158*8275SEric Cheng } 159*8275SEric Cheng 160*8275SEric Cheng dladm_status_t 161*8275SEric Cheng do_check_local_port(char *attr_val, flow_desc_t *fdesc) 162*8275SEric Cheng { 163*8275SEric Cheng return (do_check_port(attr_val, B_TRUE, fdesc)); 164*8275SEric Cheng } 165*8275SEric Cheng 166*8275SEric Cheng dladm_status_t 167*8275SEric Cheng do_check_port(char *attr_val, boolean_t local, flow_desc_t *fdesc) 168*8275SEric Cheng { 169*8275SEric Cheng char *endp = NULL; 170*8275SEric Cheng long val; 171*8275SEric Cheng 172*8275SEric Cheng if (local) { 173*8275SEric Cheng fdesc->fd_mask |= FLOW_ULP_PORT_LOCAL; 174*8275SEric Cheng val = strtol(attr_val, &endp, 10); 175*8275SEric Cheng if (val < 1 || val > MAX_PORT) 176*8275SEric Cheng return (DLADM_STATUS_INVALID_PORT); 177*8275SEric Cheng fdesc->fd_local_port = htons((uint16_t)val); 178*8275SEric Cheng } else { 179*8275SEric Cheng return (DLADM_STATUS_BADVAL); 180*8275SEric Cheng } 181*8275SEric Cheng 182*8275SEric Cheng return (DLADM_STATUS_OK); 183*8275SEric Cheng } 184*8275SEric Cheng 185*8275SEric Cheng /* 186*8275SEric Cheng * Check for invalid and/or duplicate attribute specification 187*8275SEric Cheng */ 188*8275SEric Cheng static dladm_status_t 189*8275SEric Cheng flow_attrlist_check(dladm_arg_list_t *attrlist) 190*8275SEric Cheng { 191*8275SEric Cheng int i, j; 192*8275SEric Cheng boolean_t isset[DLADM_MAX_FLOWATTRS]; 193*8275SEric Cheng boolean_t matched; 194*8275SEric Cheng 195*8275SEric Cheng for (j = 0; j < DLADM_MAX_FLOWATTRS; j++) 196*8275SEric Cheng isset[j] = B_FALSE; 197*8275SEric Cheng 198*8275SEric Cheng for (i = 0; i < attrlist->al_count; i++) { 199*8275SEric Cheng matched = B_FALSE; 200*8275SEric Cheng for (j = 0; j < DLADM_MAX_FLOWATTRS; j++) { 201*8275SEric Cheng if (strcmp(attrlist->al_info[i].ai_name, 202*8275SEric Cheng attr_table[j].ad_name) == 0) { 203*8275SEric Cheng if (isset[j]) 204*8275SEric Cheng return (DLADM_STATUS_FLOW_INCOMPATIBLE); 205*8275SEric Cheng else 206*8275SEric Cheng isset[j] = B_TRUE; 207*8275SEric Cheng matched = B_TRUE; 208*8275SEric Cheng } 209*8275SEric Cheng } 210*8275SEric Cheng /* 211*8275SEric Cheng * if the attribute did not match any of the attribute in 212*8275SEric Cheng * attr_table, then it's an invalid attribute. 213*8275SEric Cheng */ 214*8275SEric Cheng if (!matched) 215*8275SEric Cheng return (DLADM_STATUS_BADARG); 216*8275SEric Cheng } 217*8275SEric Cheng return (DLADM_STATUS_OK); 218*8275SEric Cheng } 219*8275SEric Cheng 220*8275SEric Cheng /* 221*8275SEric Cheng * Convert an attribute list to a flow_desc_t using the attribute ad_check() 222*8275SEric Cheng * functions. 223*8275SEric Cheng */ 224*8275SEric Cheng dladm_status_t 225*8275SEric Cheng dladm_flow_attrlist_extract(dladm_arg_list_t *attrlist, flow_desc_t *flowdesc) 226*8275SEric Cheng { 227*8275SEric Cheng dladm_status_t status = DLADM_STATUS_BADARG; 228*8275SEric Cheng int i; 229*8275SEric Cheng 230*8275SEric Cheng for (i = 0; i < attrlist->al_count; i++) { 231*8275SEric Cheng dladm_arg_info_t *aip = &attrlist->al_info[i]; 232*8275SEric Cheng int j; 233*8275SEric Cheng 234*8275SEric Cheng for (j = 0; j < DLADM_MAX_FLOWATTRS; j++) { 235*8275SEric Cheng fattr_desc_t *adp = &attr_table[j]; 236*8275SEric Cheng 237*8275SEric Cheng if (strcasecmp(aip->ai_name, adp->ad_name) != 0) 238*8275SEric Cheng continue; 239*8275SEric Cheng 240*8275SEric Cheng if ((aip->ai_val == NULL) || (*aip->ai_val == NULL)) 241*8275SEric Cheng return (DLADM_STATUS_BADARG); 242*8275SEric Cheng 243*8275SEric Cheng if (adp->ad_check != NULL) 244*8275SEric Cheng status = adp->ad_check(*aip->ai_val, flowdesc); 245*8275SEric Cheng else 246*8275SEric Cheng status = DLADM_STATUS_BADARG; 247*8275SEric Cheng 248*8275SEric Cheng if (status != DLADM_STATUS_OK) 249*8275SEric Cheng return (status); 250*8275SEric Cheng } 251*8275SEric Cheng } 252*8275SEric Cheng return (status); 253*8275SEric Cheng } 254*8275SEric Cheng 255*8275SEric Cheng void 256*8275SEric Cheng dladm_free_attrs(dladm_arg_list_t *list) 257*8275SEric Cheng { 258*8275SEric Cheng dladm_free_args(list); 259*8275SEric Cheng } 260*8275SEric Cheng 261*8275SEric Cheng dladm_status_t 262*8275SEric Cheng dladm_parse_flow_attrs(char *str, dladm_arg_list_t **listp, boolean_t novalues) 263*8275SEric Cheng { 264*8275SEric Cheng 265*8275SEric Cheng if (dladm_parse_args(str, listp, novalues) 266*8275SEric Cheng != DLADM_STATUS_OK) 267*8275SEric Cheng return (DLADM_STATUS_ATTR_PARSE_ERR); 268*8275SEric Cheng 269*8275SEric Cheng if (flow_attrlist_check(*listp) != DLADM_STATUS_OK) { 270*8275SEric Cheng dladm_free_attrs(*listp); 271*8275SEric Cheng return (DLADM_STATUS_ATTR_PARSE_ERR); 272*8275SEric Cheng } 273*8275SEric Cheng 274*8275SEric Cheng return (DLADM_STATUS_OK); 275*8275SEric Cheng } 276*8275SEric Cheng 277*8275SEric Cheng dladm_status_t 278*8275SEric Cheng do_check_dsfield(char *str, flow_desc_t *fd) 279*8275SEric Cheng { 280*8275SEric Cheng char *mask_str, *endp = NULL; 281*8275SEric Cheng uint_t mask = 0xff, value; 282*8275SEric Cheng 283*8275SEric Cheng if ((mask_str = strchr(str, ':')) != NULL) { 284*8275SEric Cheng *mask_str++ = '\0'; 285*8275SEric Cheng errno = 0; 286*8275SEric Cheng mask = strtoul(mask_str, &endp, 16); 287*8275SEric Cheng if (errno != 0 || mask == 0 || mask > 0xff || 288*8275SEric Cheng *endp != '\0') 289*8275SEric Cheng return (DLADM_STATUS_INVALID_DSFMASK); 290*8275SEric Cheng } 291*8275SEric Cheng errno = 0; 292*8275SEric Cheng endp = NULL; 293*8275SEric Cheng value = strtoul(str, &endp, 16); 294*8275SEric Cheng if (errno != 0 || value == 0 || value > 0xff || *endp != '\0') 295*8275SEric Cheng return (DLADM_STATUS_INVALID_DSF); 296*8275SEric Cheng 297*8275SEric Cheng fd->fd_dsfield = (uint8_t)value; 298*8275SEric Cheng fd->fd_dsfield_mask = (uint8_t)mask; 299*8275SEric Cheng fd->fd_mask |= FLOW_IP_DSFIELD; 300*8275SEric Cheng return (DLADM_STATUS_OK); 301*8275SEric Cheng } 302*8275SEric Cheng 303*8275SEric Cheng char * 304*8275SEric Cheng dladm_proto2str(uint8_t protocol) 305*8275SEric Cheng { 306*8275SEric Cheng if (protocol == IPPROTO_TCP) 307*8275SEric Cheng return ("tcp"); 308*8275SEric Cheng if (protocol == IPPROTO_UDP) 309*8275SEric Cheng return ("udp"); 310*8275SEric Cheng if (protocol == IPPROTO_SCTP) 311*8275SEric Cheng return ("sctp"); 312*8275SEric Cheng if (protocol == IPPROTO_ICMPV6) 313*8275SEric Cheng return ("icmpv6"); 314*8275SEric Cheng if (protocol == IPPROTO_ICMP) 315*8275SEric Cheng return ("icmp"); 316*8275SEric Cheng else 317*8275SEric Cheng return (""); 318*8275SEric Cheng } 319*8275SEric Cheng 320*8275SEric Cheng uint8_t 321*8275SEric Cheng dladm_str2proto(const char *protostr) 322*8275SEric Cheng { 323*8275SEric Cheng if (strncasecmp(protostr, "tcp", 3) == 0) 324*8275SEric Cheng return (IPPROTO_TCP); 325*8275SEric Cheng else if (strncasecmp(protostr, "udp", 3) == 0) 326*8275SEric Cheng return (IPPROTO_UDP); 327*8275SEric Cheng else if (strncasecmp(protostr, "sctp", 4) == 0) 328*8275SEric Cheng return (IPPROTO_SCTP); 329*8275SEric Cheng else if (strncasecmp(protostr, "icmpv6", 6) == 0) 330*8275SEric Cheng return (IPPROTO_ICMPV6); 331*8275SEric Cheng else if (strncasecmp(protostr, "icmp", 4) == 0) 332*8275SEric Cheng return (IPPROTO_ICMP); 333*8275SEric Cheng 334*8275SEric Cheng return (0); 335*8275SEric Cheng } 336*8275SEric Cheng 337*8275SEric Cheng void 338*8275SEric Cheng dladm_flow_attr_ip2str(dladm_flow_attr_t *attrp, char *buf, size_t buf_len) 339*8275SEric Cheng { 340*8275SEric Cheng flow_desc_t fdesc = attrp->fa_flow_desc; 341*8275SEric Cheng struct in_addr ipaddr; 342*8275SEric Cheng int prefix_len, prefix_max; 343*8275SEric Cheng char *cp, abuf[INET6_ADDRSTRLEN]; 344*8275SEric Cheng 345*8275SEric Cheng if (fdesc.fd_mask & FLOW_IP_LOCAL) { 346*8275SEric Cheng if (fdesc.fd_ipversion == IPV6_VERSION) { 347*8275SEric Cheng (void) inet_ntop(AF_INET6, &fdesc.fd_local_addr, abuf, 348*8275SEric Cheng INET6_ADDRSTRLEN); 349*8275SEric Cheng cp = abuf; 350*8275SEric Cheng prefix_max = IPV6_ABITS; 351*8275SEric Cheng } else { 352*8275SEric Cheng ipaddr.s_addr = fdesc.fd_local_addr._S6_un._S6_u32[3]; 353*8275SEric Cheng cp = inet_ntoa(ipaddr); 354*8275SEric Cheng prefix_max = IP_ABITS; 355*8275SEric Cheng } 356*8275SEric Cheng (void) dladm_mask2prefixlen(&fdesc.fd_local_netmask, 357*8275SEric Cheng prefix_max, &prefix_len); 358*8275SEric Cheng (void) snprintf(buf, buf_len, "LCL:%s/%d ", cp, prefix_len); 359*8275SEric Cheng } else if (fdesc.fd_mask & FLOW_IP_REMOTE) { 360*8275SEric Cheng if (fdesc.fd_ipversion == IPV6_VERSION) { 361*8275SEric Cheng (void) inet_ntop(AF_INET6, &fdesc.fd_remote_addr, abuf, 362*8275SEric Cheng INET6_ADDRSTRLEN); 363*8275SEric Cheng cp = abuf; 364*8275SEric Cheng prefix_max = IPV6_ABITS; 365*8275SEric Cheng } else { 366*8275SEric Cheng ipaddr.s_addr = fdesc.fd_remote_addr._S6_un._S6_u32[3]; 367*8275SEric Cheng cp = inet_ntoa(ipaddr); 368*8275SEric Cheng prefix_max = IP_ABITS; 369*8275SEric Cheng } 370*8275SEric Cheng (void) dladm_mask2prefixlen(&fdesc.fd_remote_netmask, 371*8275SEric Cheng prefix_max, &prefix_len); 372*8275SEric Cheng (void) snprintf(buf, buf_len, "RMT:%s/%d ", cp, prefix_len); 373*8275SEric Cheng } else { 374*8275SEric Cheng buf[0] = '\0'; 375*8275SEric Cheng } 376*8275SEric Cheng } 377*8275SEric Cheng 378*8275SEric Cheng void 379*8275SEric Cheng dladm_flow_attr_proto2str(dladm_flow_attr_t *attrp, char *buf, size_t buf_len) 380*8275SEric Cheng { 381*8275SEric Cheng flow_desc_t fdesc = attrp->fa_flow_desc; 382*8275SEric Cheng 383*8275SEric Cheng (void) snprintf(buf, buf_len, "%s", 384*8275SEric Cheng dladm_proto2str(fdesc.fd_protocol)); 385*8275SEric Cheng } 386*8275SEric Cheng 387*8275SEric Cheng void 388*8275SEric Cheng dladm_flow_attr_port2str(dladm_flow_attr_t *attrp, char *buf, size_t buf_len) 389*8275SEric Cheng { 390*8275SEric Cheng flow_desc_t fdesc = attrp->fa_flow_desc; 391*8275SEric Cheng 392*8275SEric Cheng if (fdesc.fd_mask & FLOW_ULP_PORT_LOCAL) { 393*8275SEric Cheng (void) snprintf(buf, buf_len, "%d", 394*8275SEric Cheng ntohs(fdesc.fd_local_port)); 395*8275SEric Cheng } else { 396*8275SEric Cheng buf[0] = '\0'; 397*8275SEric Cheng } 398*8275SEric Cheng } 399*8275SEric Cheng 400*8275SEric Cheng void 401*8275SEric Cheng dladm_flow_attr_dsfield2str(dladm_flow_attr_t *attrp, char *buf, size_t buf_len) 402*8275SEric Cheng { 403*8275SEric Cheng flow_desc_t fdesc = attrp->fa_flow_desc; 404*8275SEric Cheng 405*8275SEric Cheng if (fdesc.fd_mask & FLOW_IP_DSFIELD) { 406*8275SEric Cheng (void) snprintf(buf, buf_len, "0x%x:0x%x", 407*8275SEric Cheng fdesc.fd_dsfield, fdesc.fd_dsfield_mask); 408*8275SEric Cheng } else { 409*8275SEric Cheng buf[0] = '\0'; 410*8275SEric Cheng } 411*8275SEric Cheng } 412