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