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