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