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