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 /* 23 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 24 * Copyright (c) 2015, Joyent, Inc. All rights reserved. 25 */ 26 /* 27 * Copyright 2014 Nexenta Systems, Inc. All rights reserved. 28 */ 29 30 #include <sys/cmn_err.h> 31 #include <sys/strsun.h> 32 #include <sys/sdt.h> 33 #include <sys/mac.h> 34 #include <sys/mac_impl.h> 35 #include <sys/mac_client_impl.h> 36 #include <sys/mac_client_priv.h> 37 #include <sys/ethernet.h> 38 #include <sys/vlan.h> 39 #include <sys/dlpi.h> 40 #include <sys/avl.h> 41 #include <inet/ip.h> 42 #include <inet/ip6.h> 43 #include <inet/arp.h> 44 #include <netinet/arp.h> 45 #include <netinet/udp.h> 46 #include <netinet/dhcp.h> 47 #include <netinet/dhcp6.h> 48 49 /* 50 * Implementation overview for DHCP address detection 51 * 52 * The purpose of DHCP address detection is to relieve the user of having to 53 * manually configure static IP addresses when ip-nospoof protection is turned 54 * on. To achieve this, the mac layer needs to intercept DHCP packets to 55 * determine the assigned IP addresses. 56 * 57 * A DHCP handshake between client and server typically requires at least 58 * 4 messages: 59 * 60 * 1. DISCOVER - client attempts to locate DHCP servers via a 61 * broadcast message to its subnet. 62 * 2. OFFER - server responds to client with an IP address and 63 * other parameters. 64 * 3. REQUEST - client requests the offered address. 65 * 4. ACK - server verifies that the requested address matches 66 * the one it offered. 67 * 68 * DHCPv6 behaves pretty much the same way aside from different message names. 69 * 70 * Address information is embedded in either the OFFER or REQUEST message. 71 * We chose to intercept REQUEST because this is at the last part of the 72 * handshake and it indicates that the client intends to keep the address. 73 * Intercepting OFFERs is unreliable because the client may receive multiple 74 * offers from different servers, and we can't tell which address the client 75 * will keep. 76 * 77 * Each DHCP message has a transaction ID. We use this transaction ID to match 78 * REQUESTs with ACKs received from servers. 79 * 80 * For IPv4, the process to acquire a DHCP-assigned address is as follows: 81 * 82 * 1. Client sends REQUEST. a new dhcpv4_txn_t object is created and inserted 83 * in the the mci_v4_pending_txn table (keyed by xid). This object represents 84 * a new transaction. It contains the xid, the client ID and requested IP 85 * address. 86 * 87 * 2. Server responds with an ACK. The xid from this ACK is used to lookup the 88 * pending transaction from the mci_v4_pending_txn table. Once the object is 89 * found, it is removed from the pending table and inserted into the 90 * completed table (mci_v4_completed_txn, keyed by client ID) and the dynamic 91 * IP table (mci_v4_dyn_ip, keyed by IP address). 92 * 93 * 3. An outgoing packet that goes through the ip-nospoof path will be checked 94 * against the dynamic IP table. Packets that have the assigned DHCP address 95 * as the source IP address will pass the check and be admitted onto the 96 * network. 97 * 98 * IPv4 notes: 99 * 100 * If the server never responds with an ACK, there is a timer that is set after 101 * the insertion of the transaction into the pending table. When the timer 102 * fires, it will check whether the transaction is old (by comparing current 103 * time and the txn's timestamp), if so the transaction will be freed. along 104 * with this, any transaction in the completed/dyn-ip tables matching the client 105 * ID of this stale transaction will also be freed. If the client fails to 106 * extend a lease, we want to stop the client from using any IP addresses that 107 * were granted previously. 108 * 109 * A RELEASE message from the client will not cause a transaction to be created. 110 * The client ID in the RELEASE message will be used for finding and removing 111 * transactions in the completed and dyn-ip tables. 112 * 113 * 114 * For IPv6, the process to acquire a DHCPv6-assigned address is as follows: 115 * 116 * 1. Client sends REQUEST. The DUID is extracted and stored into a dhcpv6_cid_t 117 * structure. A new transaction structure (dhcpv6_txn_t) is also created and 118 * it will point to the dhcpv6_cid_t. If an existing transaction with a 119 * matching xid is not found, this dhcpv6_txn_t will be inserted into the 120 * mci_v6_pending_txn table (keyed by xid). 121 * 122 * 2. Server responds with a REPLY. If a pending transaction is found, the 123 * addresses in the reply will be placed into the dhcpv6_cid_t pointed to by 124 * the transaction. The dhcpv6_cid_t will then be moved to the mci_v6_cid 125 * table (keyed by cid). The associated addresses will be added to the 126 * mci_v6_dyn_ip table (while still being pointed to by the dhcpv6_cid_t). 127 * 128 * 3. IPv6 ip-nospoof will now check mci_v6_dyn_ip for matching packets. 129 * Packets with a source address matching one of the DHCPv6-assigned 130 * addresses will be allowed through. 131 * 132 * IPv6 notes: 133 * 134 * The v6 code shares the same timer as v4 for scrubbing stale transactions. 135 * Just like v4, as part of removing an expired transaction, a RELEASE will be 136 * be triggered on the cid associated with the expired transaction. 137 * 138 * The data structures used for v6 are slightly different because a v6 client 139 * may have multiple addresses associated with it. 140 */ 141 142 /* 143 * These are just arbitrary limits meant for preventing abuse (e.g. a user 144 * flooding the network with bogus transactions). They are not meant to be 145 * user-modifiable so they are not exposed as linkprops. 146 */ 147 static ulong_t dhcp_max_pending_txn = 512; 148 static ulong_t dhcp_max_completed_txn = 512; 149 static ulong_t slaac_max_allowed = 512; 150 static hrtime_t txn_cleanup_interval = 60 * NANOSEC; 151 152 /* 153 * DHCPv4 transaction. It may be added to three different tables 154 * (keyed by different fields). 155 */ 156 typedef struct dhcpv4_txn { 157 uint32_t dt_xid; 158 hrtime_t dt_timestamp; 159 uint8_t dt_cid[DHCP_MAX_OPT_SIZE]; 160 uint8_t dt_cid_len; 161 ipaddr_t dt_ipaddr; 162 avl_node_t dt_node; 163 avl_node_t dt_ipnode; 164 struct dhcpv4_txn *dt_next; 165 } dhcpv4_txn_t; 166 167 /* 168 * DHCPv6 address. May be added to mci_v6_dyn_ip. 169 * It is always pointed to by its parent dhcpv6_cid_t structure. 170 */ 171 typedef struct dhcpv6_addr { 172 in6_addr_t da_addr; 173 avl_node_t da_node; 174 struct dhcpv6_addr *da_next; 175 } dhcpv6_addr_t; 176 177 /* 178 * DHCPv6 client ID. May be added to mci_v6_cid. 179 * No dhcpv6_txn_t should be pointing to it after it is added to mci_v6_cid. 180 */ 181 typedef struct dhcpv6_cid { 182 uchar_t *dc_cid; 183 uint_t dc_cid_len; 184 dhcpv6_addr_t *dc_addr; 185 uint_t dc_addrcnt; 186 avl_node_t dc_node; 187 } dhcpv6_cid_t; 188 189 /* 190 * DHCPv6 transaction. Unlike its v4 counterpart, this object gets freed up 191 * as soon as the transaction completes or expires. 192 */ 193 typedef struct dhcpv6_txn { 194 uint32_t dt_xid; 195 hrtime_t dt_timestamp; 196 dhcpv6_cid_t *dt_cid; 197 avl_node_t dt_node; 198 struct dhcpv6_txn *dt_next; 199 } dhcpv6_txn_t; 200 201 /* 202 * Stateless address autoconfiguration (SLAAC) address. May be added to 203 * mci_v6_slaac_ip. 204 */ 205 typedef struct slaac_addr { 206 in6_addr_t sla_prefix; 207 in6_addr_t sla_addr; 208 avl_node_t sla_node; 209 } slaac_addr_t; 210 211 static void start_txn_cleanup_timer(mac_client_impl_t *); 212 static boolean_t allowed_ips_set(mac_resource_props_t *, uint32_t); 213 214 #define BUMP_STAT(m, s) (m)->mci_misc_stat.mms_##s++ 215 216 /* 217 * Comparison functions for the 3 AVL trees used: 218 * mci_v4_pending_txn, mci_v4_completed_txn, mci_v4_dyn_ip 219 */ 220 static int 221 compare_dhcpv4_xid(const void *arg1, const void *arg2) 222 { 223 const dhcpv4_txn_t *txn1 = arg1, *txn2 = arg2; 224 225 if (txn1->dt_xid < txn2->dt_xid) 226 return (-1); 227 else if (txn1->dt_xid > txn2->dt_xid) 228 return (1); 229 else 230 return (0); 231 } 232 233 static int 234 compare_dhcpv4_cid(const void *arg1, const void *arg2) 235 { 236 const dhcpv4_txn_t *txn1 = arg1, *txn2 = arg2; 237 int ret; 238 239 if (txn1->dt_cid_len < txn2->dt_cid_len) 240 return (-1); 241 else if (txn1->dt_cid_len > txn2->dt_cid_len) 242 return (1); 243 244 if (txn1->dt_cid_len == 0) 245 return (0); 246 247 ret = memcmp(txn1->dt_cid, txn2->dt_cid, txn1->dt_cid_len); 248 if (ret < 0) 249 return (-1); 250 else if (ret > 0) 251 return (1); 252 else 253 return (0); 254 } 255 256 static int 257 compare_dhcpv4_ip(const void *arg1, const void *arg2) 258 { 259 const dhcpv4_txn_t *txn1 = arg1, *txn2 = arg2; 260 261 if (txn1->dt_ipaddr < txn2->dt_ipaddr) 262 return (-1); 263 else if (txn1->dt_ipaddr > txn2->dt_ipaddr) 264 return (1); 265 else 266 return (0); 267 } 268 269 /* 270 * Find the specified DHCPv4 option. 271 */ 272 static int 273 get_dhcpv4_option(struct dhcp *dh4, uchar_t *end, uint8_t type, 274 uchar_t **opt, uint8_t *opt_len) 275 { 276 uchar_t *start = (uchar_t *)dh4->options; 277 uint8_t otype, olen; 278 279 while (start < end) { 280 if (*start == CD_PAD) { 281 start++; 282 continue; 283 } 284 if (*start == CD_END) 285 break; 286 287 otype = *start++; 288 olen = *start++; 289 if (otype == type && olen > 0) { 290 *opt = start; 291 *opt_len = olen; 292 return (0); 293 } 294 start += olen; 295 } 296 return (ENOENT); 297 } 298 299 /* 300 * Locate the start of a DHCPv4 header. 301 * The possible return values and associated meanings are: 302 * 0 - packet is DHCP and has a DHCP header. 303 * EINVAL - packet is not DHCP. the recommended action is to let it pass. 304 * ENOSPC - packet is a initial fragment that is DHCP or is unidentifiable. 305 * the recommended action is to drop it. 306 */ 307 static int 308 get_dhcpv4_info(ipha_t *ipha, uchar_t *end, struct dhcp **dh4) 309 { 310 uint16_t offset_and_flags, client, server; 311 boolean_t first_frag = B_FALSE; 312 struct udphdr *udph; 313 uchar_t *dh; 314 315 if (ipha->ipha_protocol != IPPROTO_UDP) 316 return (EINVAL); 317 318 offset_and_flags = ntohs(ipha->ipha_fragment_offset_and_flags); 319 if ((offset_and_flags & (IPH_MF | IPH_OFFSET)) != 0) { 320 /* 321 * All non-initial fragments may pass because we cannot 322 * identify their type. It's safe to let them through 323 * because reassembly will fail if we decide to drop the 324 * initial fragment. 325 */ 326 if (((offset_and_flags << 3) & 0xffff) != 0) 327 return (EINVAL); 328 first_frag = B_TRUE; 329 } 330 /* drop packets without a udp header */ 331 udph = (struct udphdr *)((uchar_t *)ipha + IPH_HDR_LENGTH(ipha)); 332 if ((uchar_t *)&udph[1] > end) 333 return (ENOSPC); 334 335 client = htons(IPPORT_BOOTPC); 336 server = htons(IPPORT_BOOTPS); 337 if (udph->uh_sport != client && udph->uh_sport != server && 338 udph->uh_dport != client && udph->uh_dport != server) 339 return (EINVAL); 340 341 /* drop dhcp fragments */ 342 if (first_frag) 343 return (ENOSPC); 344 345 dh = (uchar_t *)&udph[1]; 346 if (dh + BASE_PKT_SIZE > end) 347 return (EINVAL); 348 349 *dh4 = (struct dhcp *)dh; 350 return (0); 351 } 352 353 /* 354 * Wrappers for accesses to avl trees to improve readability. 355 * Their purposes are fairly self-explanatory. 356 */ 357 static dhcpv4_txn_t * 358 find_dhcpv4_pending_txn(mac_client_impl_t *mcip, uint32_t xid) 359 { 360 dhcpv4_txn_t tmp_txn; 361 362 ASSERT(MUTEX_HELD(&mcip->mci_protect_lock)); 363 tmp_txn.dt_xid = xid; 364 return (avl_find(&mcip->mci_v4_pending_txn, &tmp_txn, NULL)); 365 } 366 367 static int 368 insert_dhcpv4_pending_txn(mac_client_impl_t *mcip, dhcpv4_txn_t *txn) 369 { 370 avl_index_t where; 371 372 ASSERT(MUTEX_HELD(&mcip->mci_protect_lock)); 373 if (avl_find(&mcip->mci_v4_pending_txn, txn, &where) != NULL) 374 return (EEXIST); 375 376 if (avl_numnodes(&mcip->mci_v4_pending_txn) >= dhcp_max_pending_txn) { 377 BUMP_STAT(mcip, dhcpdropped); 378 return (EAGAIN); 379 } 380 avl_insert(&mcip->mci_v4_pending_txn, txn, where); 381 return (0); 382 } 383 384 static void 385 remove_dhcpv4_pending_txn(mac_client_impl_t *mcip, dhcpv4_txn_t *txn) 386 { 387 ASSERT(MUTEX_HELD(&mcip->mci_protect_lock)); 388 avl_remove(&mcip->mci_v4_pending_txn, txn); 389 } 390 391 static dhcpv4_txn_t * 392 find_dhcpv4_completed_txn(mac_client_impl_t *mcip, uint8_t *cid, 393 uint8_t cid_len) 394 { 395 dhcpv4_txn_t tmp_txn; 396 397 ASSERT(MUTEX_HELD(&mcip->mci_protect_lock)); 398 if (cid_len > 0) 399 bcopy(cid, tmp_txn.dt_cid, cid_len); 400 tmp_txn.dt_cid_len = cid_len; 401 return (avl_find(&mcip->mci_v4_completed_txn, &tmp_txn, NULL)); 402 } 403 404 /* 405 * After a pending txn is removed from the pending table, it is inserted 406 * into both the completed and dyn-ip tables. These two insertions are 407 * done together because a client ID must have 1:1 correspondence with 408 * an IP address and IP addresses must be unique in the dyn-ip table. 409 */ 410 static int 411 insert_dhcpv4_completed_txn(mac_client_impl_t *mcip, dhcpv4_txn_t *txn) 412 { 413 avl_index_t where; 414 415 ASSERT(MUTEX_HELD(&mcip->mci_protect_lock)); 416 if (avl_find(&mcip->mci_v4_completed_txn, txn, &where) != NULL) 417 return (EEXIST); 418 419 if (avl_numnodes(&mcip->mci_v4_completed_txn) >= 420 dhcp_max_completed_txn) { 421 BUMP_STAT(mcip, dhcpdropped); 422 return (EAGAIN); 423 } 424 425 avl_insert(&mcip->mci_v4_completed_txn, txn, where); 426 if (avl_find(&mcip->mci_v4_dyn_ip, txn, &where) != NULL) { 427 avl_remove(&mcip->mci_v4_completed_txn, txn); 428 return (EEXIST); 429 } 430 avl_insert(&mcip->mci_v4_dyn_ip, txn, where); 431 return (0); 432 } 433 434 static void 435 remove_dhcpv4_completed_txn(mac_client_impl_t *mcip, dhcpv4_txn_t *txn) 436 { 437 dhcpv4_txn_t *ctxn; 438 439 ASSERT(MUTEX_HELD(&mcip->mci_protect_lock)); 440 if ((ctxn = avl_find(&mcip->mci_v4_dyn_ip, txn, NULL)) != NULL && 441 ctxn == txn) 442 avl_remove(&mcip->mci_v4_dyn_ip, txn); 443 444 avl_remove(&mcip->mci_v4_completed_txn, txn); 445 } 446 447 /* 448 * Check whether an IP address is in the dyn-ip table. 449 */ 450 static boolean_t 451 check_dhcpv4_dyn_ip(mac_client_impl_t *mcip, ipaddr_t ipaddr) 452 { 453 dhcpv4_txn_t tmp_txn, *txn; 454 455 mutex_enter(&mcip->mci_protect_lock); 456 tmp_txn.dt_ipaddr = ipaddr; 457 txn = avl_find(&mcip->mci_v4_dyn_ip, &tmp_txn, NULL); 458 mutex_exit(&mcip->mci_protect_lock); 459 return (txn != NULL); 460 } 461 462 /* 463 * Create/destroy a DHCPv4 transaction. 464 */ 465 static dhcpv4_txn_t * 466 create_dhcpv4_txn(uint32_t xid, uint8_t *cid, uint8_t cid_len, ipaddr_t ipaddr) 467 { 468 dhcpv4_txn_t *txn; 469 470 if ((txn = kmem_zalloc(sizeof (*txn), KM_NOSLEEP)) == NULL) 471 return (NULL); 472 473 txn->dt_xid = xid; 474 txn->dt_timestamp = gethrtime(); 475 if (cid_len > 0) 476 bcopy(cid, &txn->dt_cid, cid_len); 477 txn->dt_cid_len = cid_len; 478 txn->dt_ipaddr = ipaddr; 479 return (txn); 480 } 481 482 static void 483 free_dhcpv4_txn(dhcpv4_txn_t *txn) 484 { 485 kmem_free(txn, sizeof (*txn)); 486 } 487 488 /* 489 * Clean up all v4 tables. 490 */ 491 static void 492 flush_dhcpv4(mac_client_impl_t *mcip) 493 { 494 void *cookie = NULL; 495 dhcpv4_txn_t *txn; 496 497 ASSERT(MUTEX_HELD(&mcip->mci_protect_lock)); 498 while ((txn = avl_destroy_nodes(&mcip->mci_v4_dyn_ip, 499 &cookie)) != NULL) { 500 /* 501 * No freeing needed here because the same txn exists 502 * in the mci_v4_completed_txn table as well. 503 */ 504 } 505 cookie = NULL; 506 while ((txn = avl_destroy_nodes(&mcip->mci_v4_completed_txn, 507 &cookie)) != NULL) { 508 free_dhcpv4_txn(txn); 509 } 510 cookie = NULL; 511 while ((txn = avl_destroy_nodes(&mcip->mci_v4_pending_txn, 512 &cookie)) != NULL) { 513 free_dhcpv4_txn(txn); 514 } 515 } 516 517 /* 518 * Cleanup stale DHCPv4 transactions. 519 */ 520 static void 521 txn_cleanup_v4(mac_client_impl_t *mcip) 522 { 523 dhcpv4_txn_t *txn, *ctxn, *next, *txn_list = NULL; 524 525 /* 526 * Find stale pending transactions and place them on a list 527 * to be removed. 528 */ 529 for (txn = avl_first(&mcip->mci_v4_pending_txn); txn != NULL; 530 txn = avl_walk(&mcip->mci_v4_pending_txn, txn, AVL_AFTER)) { 531 if (gethrtime() - txn->dt_timestamp > txn_cleanup_interval) { 532 DTRACE_PROBE2(found__expired__txn, 533 mac_client_impl_t *, mcip, 534 dhcpv4_txn_t *, txn); 535 536 txn->dt_next = txn_list; 537 txn_list = txn; 538 } 539 } 540 541 /* 542 * Remove and free stale pending transactions and completed 543 * transactions with the same client IDs as the stale transactions. 544 */ 545 for (txn = txn_list; txn != NULL; txn = next) { 546 avl_remove(&mcip->mci_v4_pending_txn, txn); 547 548 ctxn = find_dhcpv4_completed_txn(mcip, txn->dt_cid, 549 txn->dt_cid_len); 550 if (ctxn != NULL) { 551 DTRACE_PROBE2(removing__completed__txn, 552 mac_client_impl_t *, mcip, 553 dhcpv4_txn_t *, ctxn); 554 555 remove_dhcpv4_completed_txn(mcip, ctxn); 556 free_dhcpv4_txn(ctxn); 557 } 558 next = txn->dt_next; 559 txn->dt_next = NULL; 560 561 DTRACE_PROBE2(freeing__txn, mac_client_impl_t *, mcip, 562 dhcpv4_txn_t *, txn); 563 free_dhcpv4_txn(txn); 564 } 565 } 566 567 /* 568 * Core logic for intercepting outbound DHCPv4 packets. 569 */ 570 static boolean_t 571 intercept_dhcpv4_outbound(mac_client_impl_t *mcip, ipha_t *ipha, uchar_t *end) 572 { 573 struct dhcp *dh4; 574 uchar_t *opt; 575 dhcpv4_txn_t *txn, *ctxn; 576 ipaddr_t ipaddr; 577 uint8_t opt_len, mtype, cid[DHCP_MAX_OPT_SIZE], cid_len; 578 mac_resource_props_t *mrp = MCIP_RESOURCE_PROPS(mcip); 579 580 if (get_dhcpv4_info(ipha, end, &dh4) != 0) 581 return (B_TRUE); 582 583 /* ip_nospoof/allowed-ips and DHCP are mutually exclusive by default */ 584 if (allowed_ips_set(mrp, IPV4_VERSION)) 585 return (B_FALSE); 586 587 if (get_dhcpv4_option(dh4, end, CD_DHCP_TYPE, &opt, &opt_len) != 0 || 588 opt_len != 1) { 589 DTRACE_PROBE2(mtype__not__found, mac_client_impl_t *, mcip, 590 struct dhcp *, dh4); 591 return (B_TRUE); 592 } 593 mtype = *opt; 594 if (mtype != REQUEST && mtype != RELEASE) { 595 DTRACE_PROBE3(ignored__mtype, mac_client_impl_t *, mcip, 596 struct dhcp *, dh4, uint8_t, mtype); 597 return (B_TRUE); 598 } 599 600 /* client ID is optional for IPv4 */ 601 if (get_dhcpv4_option(dh4, end, CD_CLIENT_ID, &opt, &opt_len) == 0 && 602 opt_len >= 2) { 603 bcopy(opt, cid, opt_len); 604 cid_len = opt_len; 605 } else { 606 bzero(cid, DHCP_MAX_OPT_SIZE); 607 cid_len = 0; 608 } 609 610 mutex_enter(&mcip->mci_protect_lock); 611 if (mtype == RELEASE) { 612 DTRACE_PROBE2(release, mac_client_impl_t *, mcip, 613 struct dhcp *, dh4); 614 615 /* flush any completed txn with this cid */ 616 ctxn = find_dhcpv4_completed_txn(mcip, cid, cid_len); 617 if (ctxn != NULL) { 618 DTRACE_PROBE2(release__successful, mac_client_impl_t *, 619 mcip, struct dhcp *, dh4); 620 621 remove_dhcpv4_completed_txn(mcip, ctxn); 622 free_dhcpv4_txn(ctxn); 623 } 624 goto done; 625 } 626 627 /* 628 * If a pending txn already exists, we'll update its timestamp so 629 * it won't get flushed by the timer. We don't need to create new 630 * txns for retransmissions. 631 */ 632 if ((txn = find_dhcpv4_pending_txn(mcip, dh4->xid)) != NULL) { 633 DTRACE_PROBE2(update, mac_client_impl_t *, mcip, 634 dhcpv4_txn_t *, txn); 635 txn->dt_timestamp = gethrtime(); 636 goto done; 637 } 638 639 if (get_dhcpv4_option(dh4, end, CD_REQUESTED_IP_ADDR, 640 &opt, &opt_len) != 0 || opt_len != sizeof (ipaddr)) { 641 DTRACE_PROBE2(ipaddr__not__found, mac_client_impl_t *, mcip, 642 struct dhcp *, dh4); 643 goto done; 644 } 645 bcopy(opt, &ipaddr, sizeof (ipaddr)); 646 if ((txn = create_dhcpv4_txn(dh4->xid, cid, cid_len, ipaddr)) == NULL) 647 goto done; 648 649 if (insert_dhcpv4_pending_txn(mcip, txn) != 0) { 650 DTRACE_PROBE2(insert__failed, mac_client_impl_t *, mcip, 651 dhcpv4_txn_t *, txn); 652 free_dhcpv4_txn(txn); 653 goto done; 654 } 655 start_txn_cleanup_timer(mcip); 656 657 DTRACE_PROBE2(txn__pending, mac_client_impl_t *, mcip, 658 dhcpv4_txn_t *, txn); 659 660 done: 661 mutex_exit(&mcip->mci_protect_lock); 662 return (B_TRUE); 663 } 664 665 /* 666 * Core logic for intercepting inbound DHCPv4 packets. 667 */ 668 static void 669 intercept_dhcpv4_inbound(mac_client_impl_t *mcip, uchar_t *end, 670 struct dhcp *dh4) 671 { 672 uchar_t *opt; 673 dhcpv4_txn_t *txn, *ctxn; 674 uint8_t opt_len, mtype; 675 676 if (get_dhcpv4_option(dh4, end, CD_DHCP_TYPE, &opt, &opt_len) != 0 || 677 opt_len != 1) { 678 DTRACE_PROBE2(mtype__not__found, mac_client_impl_t *, mcip, 679 struct dhcp *, dh4); 680 return; 681 } 682 mtype = *opt; 683 if (mtype != ACK && mtype != NAK) { 684 DTRACE_PROBE3(ignored__mtype, mac_client_impl_t *, mcip, 685 struct dhcp *, dh4, uint8_t, mtype); 686 return; 687 } 688 689 mutex_enter(&mcip->mci_protect_lock); 690 if ((txn = find_dhcpv4_pending_txn(mcip, dh4->xid)) == NULL) { 691 DTRACE_PROBE2(txn__not__found, mac_client_impl_t *, mcip, 692 struct dhcp *, dh4); 693 goto done; 694 } 695 remove_dhcpv4_pending_txn(mcip, txn); 696 697 /* 698 * We're about to move a txn from the pending table to the completed/ 699 * dyn-ip tables. If there is an existing completed txn with the 700 * same cid as our txn, we need to remove and free it. 701 */ 702 ctxn = find_dhcpv4_completed_txn(mcip, txn->dt_cid, txn->dt_cid_len); 703 if (ctxn != NULL) { 704 DTRACE_PROBE2(replacing__old__txn, mac_client_impl_t *, mcip, 705 dhcpv4_txn_t *, ctxn); 706 remove_dhcpv4_completed_txn(mcip, ctxn); 707 free_dhcpv4_txn(ctxn); 708 } 709 if (mtype == NAK) { 710 DTRACE_PROBE2(nak__received, mac_client_impl_t *, mcip, 711 dhcpv4_txn_t *, txn); 712 free_dhcpv4_txn(txn); 713 goto done; 714 } 715 if (insert_dhcpv4_completed_txn(mcip, txn) != 0) { 716 DTRACE_PROBE2(insert__failed, mac_client_impl_t *, mcip, 717 dhcpv4_txn_t *, txn); 718 free_dhcpv4_txn(txn); 719 goto done; 720 } 721 DTRACE_PROBE2(txn__completed, mac_client_impl_t *, mcip, 722 dhcpv4_txn_t *, txn); 723 724 done: 725 mutex_exit(&mcip->mci_protect_lock); 726 } 727 728 729 /* 730 * Comparison functions for the DHCPv6 AVL trees. 731 */ 732 static int 733 compare_dhcpv6_xid(const void *arg1, const void *arg2) 734 { 735 const dhcpv6_txn_t *txn1 = arg1, *txn2 = arg2; 736 737 if (txn1->dt_xid < txn2->dt_xid) 738 return (-1); 739 else if (txn1->dt_xid > txn2->dt_xid) 740 return (1); 741 else 742 return (0); 743 } 744 745 static int 746 compare_dhcpv6_ip(const void *arg1, const void *arg2) 747 { 748 const dhcpv6_addr_t *ip1 = arg1, *ip2 = arg2; 749 int ret; 750 751 ret = memcmp(&ip1->da_addr, &ip2->da_addr, sizeof (in6_addr_t)); 752 if (ret < 0) 753 return (-1); 754 else if (ret > 0) 755 return (1); 756 else 757 return (0); 758 } 759 760 static int 761 compare_dhcpv6_cid(const void *arg1, const void *arg2) 762 { 763 const dhcpv6_cid_t *cid1 = arg1, *cid2 = arg2; 764 int ret; 765 766 if (cid1->dc_cid_len < cid2->dc_cid_len) 767 return (-1); 768 else if (cid1->dc_cid_len > cid2->dc_cid_len) 769 return (1); 770 771 if (cid1->dc_cid_len == 0) 772 return (0); 773 774 ret = memcmp(cid1->dc_cid, cid2->dc_cid, cid1->dc_cid_len); 775 if (ret < 0) 776 return (-1); 777 else if (ret > 0) 778 return (1); 779 else 780 return (0); 781 } 782 783 static int 784 compare_slaac_ip(const void *arg1, const void *arg2) 785 { 786 const slaac_addr_t *ip1 = arg1, *ip2 = arg2; 787 int ret; 788 789 ret = memcmp(&ip1->sla_addr, &ip2->sla_addr, sizeof (in6_addr_t)); 790 if (ret < 0) 791 return (-1); 792 else if (ret > 0) 793 return (1); 794 else 795 return (0); 796 } 797 798 /* 799 * Locate the start of a DHCPv6 header. 800 * The possible return values and associated meanings are: 801 * 0 - packet is DHCP and has a DHCP header. 802 * EINVAL - packet is not DHCP. the recommended action is to let it pass. 803 * ENOSPC - packet is a initial fragment that is DHCP or is unidentifiable. 804 * the recommended action is to drop it. 805 */ 806 static int 807 get_dhcpv6_info(ip6_t *ip6h, uchar_t *end, dhcpv6_message_t **dh6) 808 { 809 uint16_t hdrlen, client, server; 810 boolean_t first_frag = B_FALSE; 811 ip6_frag_t *frag = NULL; 812 uint8_t proto; 813 struct udphdr *udph; 814 uchar_t *dh; 815 816 if (!mac_ip_hdr_length_v6(ip6h, end, &hdrlen, &proto, &frag)) 817 return (ENOSPC); 818 819 if (proto != IPPROTO_UDP) 820 return (EINVAL); 821 822 if (frag != NULL) { 823 /* 824 * All non-initial fragments may pass because we cannot 825 * identify their type. It's safe to let them through 826 * because reassembly will fail if we decide to drop the 827 * initial fragment. 828 */ 829 if ((ntohs(frag->ip6f_offlg) & ~7) != 0) 830 return (EINVAL); 831 first_frag = B_TRUE; 832 } 833 /* drop packets without a udp header */ 834 udph = (struct udphdr *)((uchar_t *)ip6h + hdrlen); 835 if ((uchar_t *)&udph[1] > end) 836 return (ENOSPC); 837 838 client = htons(IPPORT_DHCPV6C); 839 server = htons(IPPORT_DHCPV6S); 840 if (udph->uh_sport != client && udph->uh_sport != server && 841 udph->uh_dport != client && udph->uh_dport != server) 842 return (EINVAL); 843 844 /* drop dhcp fragments */ 845 if (first_frag) 846 return (ENOSPC); 847 848 dh = (uchar_t *)&udph[1]; 849 if (dh + sizeof (dhcpv6_message_t) > end) 850 return (EINVAL); 851 852 *dh6 = (dhcpv6_message_t *)dh; 853 return (0); 854 } 855 856 static int 857 get_ra_info(ip6_t *ip6h, uchar_t *end, nd_router_advert_t **ra) 858 { 859 uint16_t hdrlen; 860 ip6_frag_t *frag = NULL; 861 uint8_t proto; 862 uchar_t *hdrp; 863 struct icmp6_hdr *icmp; 864 865 if (!mac_ip_hdr_length_v6(ip6h, end, &hdrlen, &proto, &frag)) 866 return (ENOSPC); 867 868 if (proto != IPPROTO_ICMPV6) 869 return (EINVAL); 870 871 if (frag != NULL) { 872 /* 873 * All non-initial fragments may pass because we cannot 874 * identify their type. It's safe to let them through 875 * because reassembly will fail if we decide to drop the 876 * initial fragment. 877 */ 878 if ((ntohs(frag->ip6f_offlg) & ~7) != 0) 879 return (EINVAL); 880 return (ENOSPC); 881 } 882 883 /* 884 * Ensure that the ICMP header falls w/in packet boundaries, in case 885 * we've received a malicious packet that reports incorrect lengths. 886 */ 887 hdrp = (uchar_t *)ip6h + hdrlen; 888 if ((hdrp + sizeof (struct icmp6_hdr)) > end) { 889 return (EINVAL); 890 } 891 icmp = (struct icmp6_hdr *)hdrp; 892 893 if (icmp->icmp6_type != ND_ROUTER_ADVERT || 894 icmp->icmp6_code != 0) 895 return (EINVAL); 896 897 *ra = (nd_router_advert_t *)icmp; 898 return (0); 899 } 900 901 /* 902 * Find the specified DHCPv6 option. 903 */ 904 static dhcpv6_option_t * 905 get_dhcpv6_option(void *buf, size_t buflen, dhcpv6_option_t *oldopt, 906 uint16_t codenum, uint_t *retlenp) 907 { 908 uchar_t *bp; 909 dhcpv6_option_t d6o; 910 uint_t olen; 911 912 codenum = htons(codenum); 913 bp = buf; 914 while (buflen >= sizeof (dhcpv6_option_t)) { 915 bcopy(bp, &d6o, sizeof (d6o)); 916 olen = ntohs(d6o.d6o_len) + sizeof (d6o); 917 if (olen > buflen) 918 break; 919 if (d6o.d6o_code != codenum || d6o.d6o_len == 0 || 920 (oldopt != NULL && bp <= (uchar_t *)oldopt)) { 921 bp += olen; 922 buflen -= olen; 923 continue; 924 } 925 if (retlenp != NULL) 926 *retlenp = olen; 927 /* LINTED : alignment */ 928 return ((dhcpv6_option_t *)bp); 929 } 930 return (NULL); 931 } 932 933 /* 934 * Get the status code from a reply message. 935 */ 936 static int 937 get_dhcpv6_status(dhcpv6_message_t *dh6, uchar_t *end, uint16_t *status) 938 { 939 dhcpv6_option_t *d6o; 940 uint_t olen; 941 uint16_t s; 942 943 d6o = get_dhcpv6_option(&dh6[1], end - (uchar_t *)&dh6[1], NULL, 944 DHCPV6_OPT_STATUS_CODE, &olen); 945 946 /* Success is implied if status code is missing */ 947 if (d6o == NULL) { 948 *status = DHCPV6_STAT_SUCCESS; 949 return (0); 950 } 951 if ((uchar_t *)d6o + olen > end) 952 return (EINVAL); 953 954 olen -= sizeof (*d6o); 955 if (olen < sizeof (s)) 956 return (EINVAL); 957 958 bcopy(&d6o[1], &s, sizeof (s)); 959 *status = ntohs(s); 960 return (0); 961 } 962 963 /* 964 * Get the addresses from a reply message. 965 */ 966 static int 967 get_dhcpv6_addrs(dhcpv6_message_t *dh6, uchar_t *end, dhcpv6_cid_t *cid) 968 { 969 dhcpv6_option_t *d6o; 970 dhcpv6_addr_t *next; 971 uint_t olen; 972 973 d6o = NULL; 974 while ((d6o = get_dhcpv6_option(&dh6[1], end - (uchar_t *)&dh6[1], 975 d6o, DHCPV6_OPT_IA_NA, &olen)) != NULL) { 976 dhcpv6_option_t *d6so; 977 dhcpv6_iaaddr_t d6ia; 978 dhcpv6_addr_t **addrp; 979 uchar_t *obase; 980 uint_t solen; 981 982 if (olen < sizeof (dhcpv6_ia_na_t) || 983 (uchar_t *)d6o + olen > end) 984 goto fail; 985 986 obase = (uchar_t *)d6o + sizeof (dhcpv6_ia_na_t); 987 olen -= sizeof (dhcpv6_ia_na_t); 988 d6so = NULL; 989 while ((d6so = get_dhcpv6_option(obase, olen, d6so, 990 DHCPV6_OPT_IAADDR, &solen)) != NULL) { 991 if (solen < sizeof (dhcpv6_iaaddr_t) || 992 (uchar_t *)d6so + solen > end) 993 goto fail; 994 995 bcopy(d6so, &d6ia, sizeof (d6ia)); 996 for (addrp = &cid->dc_addr; *addrp != NULL; 997 addrp = &(*addrp)->da_next) { 998 if (bcmp(&(*addrp)->da_addr, &d6ia.d6ia_addr, 999 sizeof (in6_addr_t)) == 0) 1000 goto fail; 1001 } 1002 if ((*addrp = kmem_zalloc(sizeof (dhcpv6_addr_t), 1003 KM_NOSLEEP)) == NULL) 1004 goto fail; 1005 1006 bcopy(&d6ia.d6ia_addr, &(*addrp)->da_addr, 1007 sizeof (in6_addr_t)); 1008 cid->dc_addrcnt++; 1009 } 1010 } 1011 if (cid->dc_addrcnt == 0) 1012 return (ENOENT); 1013 1014 return (0); 1015 1016 fail: 1017 for (; cid->dc_addr != NULL; cid->dc_addr = next) { 1018 next = cid->dc_addr->da_next; 1019 kmem_free(cid->dc_addr, sizeof (dhcpv6_addr_t)); 1020 cid->dc_addrcnt--; 1021 } 1022 ASSERT(cid->dc_addrcnt == 0); 1023 return (EINVAL); 1024 } 1025 1026 /* 1027 * Free a cid. 1028 * Before this gets called the caller must ensure that all the 1029 * addresses are removed from the mci_v6_dyn_ip table. 1030 */ 1031 static void 1032 free_dhcpv6_cid(dhcpv6_cid_t *cid) 1033 { 1034 dhcpv6_addr_t *addr, *next; 1035 uint_t cnt = 0; 1036 1037 kmem_free(cid->dc_cid, cid->dc_cid_len); 1038 for (addr = cid->dc_addr; addr != NULL; addr = next) { 1039 next = addr->da_next; 1040 kmem_free(addr, sizeof (*addr)); 1041 cnt++; 1042 } 1043 ASSERT(cnt == cid->dc_addrcnt); 1044 kmem_free(cid, sizeof (*cid)); 1045 } 1046 1047 /* 1048 * Extract the DUID from a message. The associated addresses will be 1049 * extracted later from the reply message. 1050 */ 1051 static dhcpv6_cid_t * 1052 create_dhcpv6_cid(dhcpv6_message_t *dh6, uchar_t *end) 1053 { 1054 dhcpv6_option_t *d6o; 1055 dhcpv6_cid_t *cid; 1056 uchar_t *rawcid; 1057 uint_t olen, rawcidlen; 1058 1059 d6o = get_dhcpv6_option(&dh6[1], end - (uchar_t *)&dh6[1], NULL, 1060 DHCPV6_OPT_CLIENTID, &olen); 1061 if (d6o == NULL || (uchar_t *)d6o + olen > end) 1062 return (NULL); 1063 1064 rawcidlen = olen - sizeof (*d6o); 1065 if ((rawcid = kmem_zalloc(rawcidlen, KM_NOSLEEP)) == NULL) 1066 return (NULL); 1067 bcopy(d6o + 1, rawcid, rawcidlen); 1068 1069 if ((cid = kmem_zalloc(sizeof (*cid), KM_NOSLEEP)) == NULL) { 1070 kmem_free(rawcid, rawcidlen); 1071 return (NULL); 1072 } 1073 cid->dc_cid = rawcid; 1074 cid->dc_cid_len = rawcidlen; 1075 return (cid); 1076 } 1077 1078 /* 1079 * Remove a cid from mci_v6_cid. The addresses owned by the cid 1080 * are also removed from mci_v6_dyn_ip. 1081 */ 1082 static void 1083 remove_dhcpv6_cid(mac_client_impl_t *mcip, dhcpv6_cid_t *cid) 1084 { 1085 dhcpv6_addr_t *addr, *tmp_addr; 1086 1087 ASSERT(MUTEX_HELD(&mcip->mci_protect_lock)); 1088 avl_remove(&mcip->mci_v6_cid, cid); 1089 for (addr = cid->dc_addr; addr != NULL; addr = addr->da_next) { 1090 tmp_addr = avl_find(&mcip->mci_v6_dyn_ip, addr, NULL); 1091 if (tmp_addr == addr) 1092 avl_remove(&mcip->mci_v6_dyn_ip, addr); 1093 } 1094 } 1095 1096 /* 1097 * Find and remove a matching cid and associated addresses from 1098 * their respective tables. 1099 */ 1100 static void 1101 release_dhcpv6_cid(mac_client_impl_t *mcip, dhcpv6_cid_t *cid) 1102 { 1103 dhcpv6_cid_t *oldcid; 1104 1105 ASSERT(MUTEX_HELD(&mcip->mci_protect_lock)); 1106 if ((oldcid = avl_find(&mcip->mci_v6_cid, cid, NULL)) == NULL) 1107 return; 1108 1109 /* 1110 * Since cid belongs to a pending txn, it can't possibly be in 1111 * mci_v6_cid. Anything that's found must be an existing cid. 1112 */ 1113 ASSERT(oldcid != cid); 1114 remove_dhcpv6_cid(mcip, oldcid); 1115 free_dhcpv6_cid(oldcid); 1116 } 1117 1118 /* 1119 * Insert cid into mci_v6_cid. 1120 */ 1121 static int 1122 insert_dhcpv6_cid(mac_client_impl_t *mcip, dhcpv6_cid_t *cid) 1123 { 1124 avl_index_t where; 1125 dhcpv6_addr_t *addr; 1126 1127 ASSERT(MUTEX_HELD(&mcip->mci_protect_lock)); 1128 if (avl_find(&mcip->mci_v6_cid, cid, &where) != NULL) 1129 return (EEXIST); 1130 1131 if (avl_numnodes(&mcip->mci_v6_cid) >= dhcp_max_completed_txn) { 1132 BUMP_STAT(mcip, dhcpdropped); 1133 return (EAGAIN); 1134 } 1135 avl_insert(&mcip->mci_v6_cid, cid, where); 1136 for (addr = cid->dc_addr; addr != NULL; addr = addr->da_next) { 1137 if (avl_find(&mcip->mci_v6_dyn_ip, addr, &where) != NULL) 1138 goto fail; 1139 1140 avl_insert(&mcip->mci_v6_dyn_ip, addr, where); 1141 } 1142 return (0); 1143 1144 fail: 1145 remove_dhcpv6_cid(mcip, cid); 1146 return (EEXIST); 1147 } 1148 1149 /* 1150 * Check whether an IP address is in the dyn-ip table. 1151 */ 1152 static boolean_t 1153 check_dhcpv6_dyn_ip(mac_client_impl_t *mcip, in6_addr_t *addr) 1154 { 1155 dhcpv6_addr_t tmp_addr, *a; 1156 1157 mutex_enter(&mcip->mci_protect_lock); 1158 bcopy(addr, &tmp_addr.da_addr, sizeof (in6_addr_t)); 1159 a = avl_find(&mcip->mci_v6_dyn_ip, &tmp_addr, NULL); 1160 mutex_exit(&mcip->mci_protect_lock); 1161 return (a != NULL); 1162 } 1163 1164 static dhcpv6_txn_t * 1165 find_dhcpv6_pending_txn(mac_client_impl_t *mcip, uint32_t xid) 1166 { 1167 dhcpv6_txn_t tmp_txn; 1168 1169 ASSERT(MUTEX_HELD(&mcip->mci_protect_lock)); 1170 tmp_txn.dt_xid = xid; 1171 return (avl_find(&mcip->mci_v6_pending_txn, &tmp_txn, NULL)); 1172 } 1173 1174 static void 1175 remove_dhcpv6_pending_txn(mac_client_impl_t *mcip, dhcpv6_txn_t *txn) 1176 { 1177 ASSERT(MUTEX_HELD(&mcip->mci_protect_lock)); 1178 avl_remove(&mcip->mci_v6_pending_txn, txn); 1179 } 1180 1181 static dhcpv6_txn_t * 1182 create_dhcpv6_txn(uint32_t xid, dhcpv6_cid_t *cid) 1183 { 1184 dhcpv6_txn_t *txn; 1185 1186 if ((txn = kmem_zalloc(sizeof (dhcpv6_txn_t), KM_NOSLEEP)) == NULL) 1187 return (NULL); 1188 1189 txn->dt_xid = xid; 1190 txn->dt_cid = cid; 1191 txn->dt_timestamp = gethrtime(); 1192 return (txn); 1193 } 1194 1195 static void 1196 free_dhcpv6_txn(dhcpv6_txn_t *txn) 1197 { 1198 if (txn->dt_cid != NULL) 1199 free_dhcpv6_cid(txn->dt_cid); 1200 kmem_free(txn, sizeof (dhcpv6_txn_t)); 1201 } 1202 1203 static int 1204 insert_dhcpv6_pending_txn(mac_client_impl_t *mcip, dhcpv6_txn_t *txn) 1205 { 1206 avl_index_t where; 1207 1208 ASSERT(MUTEX_HELD(&mcip->mci_protect_lock)); 1209 if (avl_find(&mcip->mci_v6_pending_txn, txn, &where) != NULL) 1210 return (EEXIST); 1211 1212 if (avl_numnodes(&mcip->mci_v6_pending_txn) >= dhcp_max_pending_txn) { 1213 BUMP_STAT(mcip, dhcpdropped); 1214 return (EAGAIN); 1215 } 1216 avl_insert(&mcip->mci_v6_pending_txn, txn, where); 1217 return (0); 1218 } 1219 1220 /* 1221 * Clean up all v6 tables. 1222 */ 1223 static void 1224 flush_dhcpv6(mac_client_impl_t *mcip) 1225 { 1226 void *cookie = NULL; 1227 dhcpv6_cid_t *cid; 1228 dhcpv6_txn_t *txn; 1229 1230 ASSERT(MUTEX_HELD(&mcip->mci_protect_lock)); 1231 while (avl_destroy_nodes(&mcip->mci_v6_dyn_ip, &cookie) != NULL) { 1232 } 1233 cookie = NULL; 1234 while ((cid = avl_destroy_nodes(&mcip->mci_v6_cid, &cookie)) != NULL) { 1235 free_dhcpv6_cid(cid); 1236 } 1237 cookie = NULL; 1238 while ((txn = avl_destroy_nodes(&mcip->mci_v6_pending_txn, 1239 &cookie)) != NULL) { 1240 free_dhcpv6_txn(txn); 1241 } 1242 } 1243 1244 void 1245 flush_slaac(mac_client_impl_t *mcip) 1246 { 1247 void *cookie = NULL; 1248 slaac_addr_t *addr = NULL; 1249 1250 while ((addr = avl_destroy_nodes(&mcip->mci_v6_slaac_ip, &cookie)) != 1251 NULL) { 1252 kmem_free(addr, sizeof (slaac_addr_t)); 1253 } 1254 } 1255 1256 /* 1257 * Cleanup stale DHCPv6 transactions. 1258 */ 1259 static void 1260 txn_cleanup_v6(mac_client_impl_t *mcip) 1261 { 1262 dhcpv6_txn_t *txn, *next, *txn_list = NULL; 1263 1264 /* 1265 * Find stale pending transactions and place them on a list 1266 * to be removed. 1267 */ 1268 for (txn = avl_first(&mcip->mci_v6_pending_txn); txn != NULL; 1269 txn = avl_walk(&mcip->mci_v6_pending_txn, txn, AVL_AFTER)) { 1270 if (gethrtime() - txn->dt_timestamp > txn_cleanup_interval) { 1271 DTRACE_PROBE2(found__expired__txn, 1272 mac_client_impl_t *, mcip, 1273 dhcpv6_txn_t *, txn); 1274 1275 txn->dt_next = txn_list; 1276 txn_list = txn; 1277 } 1278 } 1279 1280 /* 1281 * Remove and free stale pending transactions. 1282 * Release any existing cids matching the stale transactions. 1283 */ 1284 for (txn = txn_list; txn != NULL; txn = next) { 1285 avl_remove(&mcip->mci_v6_pending_txn, txn); 1286 release_dhcpv6_cid(mcip, txn->dt_cid); 1287 next = txn->dt_next; 1288 txn->dt_next = NULL; 1289 1290 DTRACE_PROBE2(freeing__txn, mac_client_impl_t *, mcip, 1291 dhcpv6_txn_t *, txn); 1292 free_dhcpv6_txn(txn); 1293 } 1294 1295 } 1296 1297 /* 1298 * Core logic for intercepting outbound DHCPv6 packets. 1299 */ 1300 static boolean_t 1301 intercept_dhcpv6_outbound(mac_client_impl_t *mcip, ip6_t *ip6h, uchar_t *end) 1302 { 1303 dhcpv6_message_t *dh6; 1304 dhcpv6_txn_t *txn; 1305 dhcpv6_cid_t *cid = NULL; 1306 uint32_t xid; 1307 uint8_t mtype; 1308 mac_resource_props_t *mrp = MCIP_RESOURCE_PROPS(mcip); 1309 1310 if (get_dhcpv6_info(ip6h, end, &dh6) != 0) 1311 return (B_TRUE); 1312 1313 /* ip_nospoof/allowed-ips and DHCP are mutually exclusive by default */ 1314 if (allowed_ips_set(mrp, IPV6_VERSION)) 1315 return (B_FALSE); 1316 1317 /* 1318 * We want to act on packets that result in DHCPv6 Reply messages, or 1319 * on packets that give up an IPv6 address. For example, a Request or 1320 * Solicit (w/ the Rapid Commit option) will cause the server to send a 1321 * Reply, ending the transaction. 1322 */ 1323 mtype = dh6->d6m_msg_type; 1324 if (mtype != DHCPV6_MSG_SOLICIT && mtype != DHCPV6_MSG_REQUEST && 1325 mtype != DHCPV6_MSG_RENEW && mtype != DHCPV6_MSG_REBIND && 1326 mtype != DHCPV6_MSG_RELEASE) 1327 return (B_TRUE); 1328 1329 if ((cid = create_dhcpv6_cid(dh6, end)) == NULL) 1330 return (B_TRUE); 1331 1332 mutex_enter(&mcip->mci_protect_lock); 1333 if (mtype == DHCPV6_MSG_RELEASE) { 1334 release_dhcpv6_cid(mcip, cid); 1335 goto done; 1336 } 1337 xid = DHCPV6_GET_TRANSID(dh6); 1338 if ((txn = find_dhcpv6_pending_txn(mcip, xid)) != NULL) { 1339 DTRACE_PROBE2(update, mac_client_impl_t *, mcip, 1340 dhcpv6_txn_t *, txn); 1341 txn->dt_timestamp = gethrtime(); 1342 goto done; 1343 } 1344 if ((txn = create_dhcpv6_txn(xid, cid)) == NULL) 1345 goto done; 1346 1347 cid = NULL; 1348 if (insert_dhcpv6_pending_txn(mcip, txn) != 0) { 1349 DTRACE_PROBE2(insert__failed, mac_client_impl_t *, mcip, 1350 dhcpv6_txn_t *, txn); 1351 free_dhcpv6_txn(txn); 1352 goto done; 1353 } 1354 start_txn_cleanup_timer(mcip); 1355 1356 DTRACE_PROBE2(txn__pending, mac_client_impl_t *, mcip, 1357 dhcpv6_txn_t *, txn); 1358 1359 done: 1360 if (cid != NULL) 1361 free_dhcpv6_cid(cid); 1362 1363 mutex_exit(&mcip->mci_protect_lock); 1364 return (B_TRUE); 1365 } 1366 1367 /* 1368 * Core logic for intercepting inbound DHCPv6 packets. 1369 */ 1370 static void 1371 intercept_dhcpv6_inbound(mac_client_impl_t *mcip, uchar_t *end, 1372 dhcpv6_message_t *dh6) 1373 { 1374 dhcpv6_txn_t *txn; 1375 uint32_t xid; 1376 uint8_t mtype; 1377 uint16_t status; 1378 1379 mtype = dh6->d6m_msg_type; 1380 if (mtype != DHCPV6_MSG_REPLY) 1381 return; 1382 1383 mutex_enter(&mcip->mci_protect_lock); 1384 xid = DHCPV6_GET_TRANSID(dh6); 1385 if ((txn = find_dhcpv6_pending_txn(mcip, xid)) == NULL) { 1386 DTRACE_PROBE2(txn__not__found, mac_client_impl_t *, mcip, 1387 dhcpv6_message_t *, dh6); 1388 goto done; 1389 } 1390 remove_dhcpv6_pending_txn(mcip, txn); 1391 release_dhcpv6_cid(mcip, txn->dt_cid); 1392 1393 if (get_dhcpv6_status(dh6, end, &status) != 0 || 1394 status != DHCPV6_STAT_SUCCESS) { 1395 DTRACE_PROBE2(error__status, mac_client_impl_t *, mcip, 1396 dhcpv6_txn_t *, txn); 1397 goto done; 1398 } 1399 if (get_dhcpv6_addrs(dh6, end, txn->dt_cid) != 0) { 1400 DTRACE_PROBE2(no__addrs, mac_client_impl_t *, mcip, 1401 dhcpv6_txn_t *, txn); 1402 goto done; 1403 } 1404 if (insert_dhcpv6_cid(mcip, txn->dt_cid) != 0) { 1405 DTRACE_PROBE2(insert__failed, mac_client_impl_t *, mcip, 1406 dhcpv6_txn_t *, txn); 1407 goto done; 1408 } 1409 DTRACE_PROBE2(txn__completed, mac_client_impl_t *, mcip, 1410 dhcpv6_txn_t *, txn); 1411 1412 txn->dt_cid = NULL; 1413 1414 done: 1415 if (txn != NULL) 1416 free_dhcpv6_txn(txn); 1417 mutex_exit(&mcip->mci_protect_lock); 1418 } 1419 1420 /* 1421 * Check whether an IP address is in the SLAAC table. 1422 */ 1423 static boolean_t 1424 check_slaac_ip(mac_client_impl_t *mcip, in6_addr_t *addr) 1425 { 1426 slaac_addr_t tmp_addr, *a; 1427 1428 mutex_enter(&mcip->mci_protect_lock); 1429 bcopy(addr, &tmp_addr.sla_addr, sizeof (in6_addr_t)); 1430 a = avl_find(&mcip->mci_v6_slaac_ip, &tmp_addr, NULL); 1431 mutex_exit(&mcip->mci_protect_lock); 1432 return (a != NULL); 1433 } 1434 1435 static boolean_t 1436 insert_slaac_ip(avl_tree_t *tree, in6_addr_t *token, slaac_addr_t *addr) 1437 { 1438 uint_t i; 1439 avl_index_t where; 1440 in6_addr_t *prefix = &addr->sla_prefix; 1441 in6_addr_t *in6p = &addr->sla_addr; 1442 1443 bcopy(prefix, in6p, sizeof (struct in6_addr)); 1444 1445 for (i = 0; i < 4; i++) { 1446 in6p->s6_addr32[i] = token->s6_addr32[i] | 1447 in6p->s6_addr32[i]; 1448 } 1449 1450 DTRACE_PROBE1(generated__addr, in6_addr_t *, in6p); 1451 1452 if (avl_find(tree, addr, &where) != NULL) 1453 return (B_FALSE); 1454 1455 avl_insert(tree, addr, where); 1456 return (B_TRUE); 1457 } 1458 1459 static void 1460 insert_slaac_prefix(mac_client_impl_t *mcip, nd_opt_prefix_info_t *po) 1461 { 1462 slaac_addr_t *addr = NULL; 1463 in6_addr_t *token = &mcip->mci_v6_mac_token; 1464 1465 ASSERT(MUTEX_HELD(&mcip->mci_protect_lock)); 1466 1467 if (avl_numnodes(&mcip->mci_v6_slaac_ip) >= slaac_max_allowed) { 1468 DTRACE_PROBE(limit__reached); 1469 return; 1470 } 1471 1472 if ((addr = kmem_zalloc(sizeof (slaac_addr_t), 1473 KM_NOSLEEP | KM_NORMALPRI)) == NULL) 1474 return; 1475 1476 bcopy(&po->nd_opt_pi_prefix, &addr->sla_prefix, 1477 sizeof (struct in6_addr)); 1478 1479 if (!insert_slaac_ip(&mcip->mci_v6_slaac_ip, token, addr)) { 1480 kmem_free(addr, sizeof (slaac_addr_t)); 1481 } 1482 } 1483 1484 static void 1485 intercept_prefix_info(mac_client_impl_t *mcip, nd_opt_prefix_info_t *po) 1486 { 1487 if (8 * po->nd_opt_pi_len != sizeof (nd_opt_prefix_info_t)) { 1488 DTRACE_PROBE(invalid__length); 1489 return; 1490 } 1491 1492 if (po->nd_opt_pi_prefix_len > 128) { 1493 DTRACE_PROBE(invalid__plen); 1494 return; 1495 } 1496 1497 if (IN6_IS_ADDR_LINKLOCAL(&po->nd_opt_pi_prefix)) { 1498 DTRACE_PROBE(link__local); 1499 return; 1500 } 1501 1502 if ((po->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_AUTO) == 0) 1503 return; 1504 1505 mutex_enter(&mcip->mci_protect_lock); 1506 insert_slaac_prefix(mcip, po); 1507 mutex_exit(&mcip->mci_protect_lock); 1508 } 1509 1510 /* 1511 * If we receive a Router Advertisement carrying prefix information and 1512 * indicating that SLAAC should be performed, then track the prefix. 1513 */ 1514 static void 1515 intercept_ra_inbound(mac_client_impl_t *mcip, ip6_t *ip6h, uchar_t *end, 1516 nd_router_advert_t *ra) 1517 { 1518 struct nd_opt_hdr *opt; 1519 int len, optlen; 1520 1521 if (ip6h->ip6_hlim != 255) { 1522 DTRACE_PROBE1(invalid__hoplimit, uint8_t, ip6h->ip6_hlim); 1523 return; 1524 } 1525 1526 len = ip6h->ip6_plen - sizeof (nd_router_advert_t); 1527 opt = (struct nd_opt_hdr *)&ra[1]; 1528 while (len >= sizeof (struct nd_opt_hdr) && 1529 ((uchar_t *)opt + sizeof (struct nd_opt_hdr)) <= end) { 1530 optlen = opt->nd_opt_len * 8; 1531 1532 if (optlen < sizeof (struct nd_opt_hdr) || 1533 ((uchar_t *)opt + optlen) > end) { 1534 DTRACE_PROBE(invalid__length); 1535 return; 1536 } 1537 1538 if (opt->nd_opt_type == ND_OPT_PREFIX_INFORMATION) { 1539 intercept_prefix_info(mcip, 1540 (nd_opt_prefix_info_t *)opt); 1541 } 1542 1543 opt = (struct nd_opt_hdr *)((char *)opt + optlen); 1544 len -= optlen; 1545 } 1546 } 1547 1548 /* 1549 * Timer for cleaning up stale transactions. 1550 */ 1551 static void 1552 txn_cleanup_timer(void *arg) 1553 { 1554 mac_client_impl_t *mcip = arg; 1555 1556 mutex_enter(&mcip->mci_protect_lock); 1557 if (mcip->mci_txn_cleanup_tid == 0) { 1558 /* do nothing if timer got cancelled */ 1559 mutex_exit(&mcip->mci_protect_lock); 1560 return; 1561 } 1562 mcip->mci_txn_cleanup_tid = 0; 1563 1564 txn_cleanup_v4(mcip); 1565 txn_cleanup_v6(mcip); 1566 1567 /* 1568 * Restart timer if pending transactions still exist. 1569 */ 1570 if (!avl_is_empty(&mcip->mci_v4_pending_txn) || 1571 !avl_is_empty(&mcip->mci_v6_pending_txn)) { 1572 DTRACE_PROBE1(restarting__timer, mac_client_impl_t *, mcip); 1573 1574 mcip->mci_txn_cleanup_tid = timeout(txn_cleanup_timer, mcip, 1575 drv_usectohz(txn_cleanup_interval / (NANOSEC / MICROSEC))); 1576 } 1577 mutex_exit(&mcip->mci_protect_lock); 1578 } 1579 1580 static void 1581 start_txn_cleanup_timer(mac_client_impl_t *mcip) 1582 { 1583 ASSERT(MUTEX_HELD(&mcip->mci_protect_lock)); 1584 if (mcip->mci_txn_cleanup_tid == 0) { 1585 mcip->mci_txn_cleanup_tid = timeout(txn_cleanup_timer, mcip, 1586 drv_usectohz(txn_cleanup_interval / (NANOSEC / MICROSEC))); 1587 } 1588 } 1589 1590 static void 1591 cancel_txn_cleanup_timer(mac_client_impl_t *mcip) 1592 { 1593 timeout_id_t tid; 1594 1595 ASSERT(MUTEX_HELD(&mcip->mci_protect_lock)); 1596 1597 /* 1598 * This needs to be a while loop because the timer could get 1599 * rearmed during untimeout(). 1600 */ 1601 while ((tid = mcip->mci_txn_cleanup_tid) != 0) { 1602 mcip->mci_txn_cleanup_tid = 0; 1603 mutex_exit(&mcip->mci_protect_lock); 1604 (void) untimeout(tid); 1605 mutex_enter(&mcip->mci_protect_lock); 1606 } 1607 } 1608 1609 /* 1610 * Get the start/end pointers of an L3 packet and also do pullup if needed. 1611 * pulled-up packet needs to be freed by the caller. 1612 */ 1613 static int 1614 get_l3_info(mblk_t *mp, size_t hdrsize, uchar_t **start, uchar_t **end, 1615 mblk_t **nmp) 1616 { 1617 uchar_t *s, *e; 1618 mblk_t *newmp = NULL; 1619 1620 /* 1621 * Pullup if necessary but reject packets that do not have 1622 * a proper mac header. 1623 */ 1624 s = mp->b_rptr + hdrsize; 1625 e = mp->b_wptr; 1626 1627 if (s > mp->b_wptr) 1628 return (EINVAL); 1629 1630 if (!OK_32PTR(s) || mp->b_cont != NULL) { 1631 /* 1632 * Temporarily adjust mp->b_rptr to ensure proper 1633 * alignment of IP header in newmp. 1634 */ 1635 DTRACE_PROBE1(pullup__needed, mblk_t *, mp); 1636 1637 mp->b_rptr += hdrsize; 1638 newmp = msgpullup(mp, -1); 1639 mp->b_rptr -= hdrsize; 1640 1641 if (newmp == NULL) 1642 return (ENOMEM); 1643 1644 s = newmp->b_rptr; 1645 e = newmp->b_wptr; 1646 } 1647 1648 *start = s; 1649 *end = e; 1650 *nmp = newmp; 1651 return (0); 1652 } 1653 1654 void 1655 mac_protect_intercept_dynamic_one(mac_client_impl_t *mcip, mblk_t *mp) 1656 { 1657 mac_impl_t *mip = mcip->mci_mip; 1658 uchar_t *start, *end; 1659 mblk_t *nmp = NULL; 1660 mac_header_info_t mhi; 1661 int err; 1662 1663 err = mac_vlan_header_info((mac_handle_t)mip, mp, &mhi); 1664 if (err != 0) { 1665 DTRACE_PROBE2(invalid__header, mac_client_impl_t *, mcip, 1666 mblk_t *, mp); 1667 return; 1668 } 1669 1670 err = get_l3_info(mp, mhi.mhi_hdrsize, &start, &end, &nmp); 1671 if (err != 0) { 1672 DTRACE_PROBE2(invalid__l3, mac_client_impl_t *, mcip, 1673 mblk_t *, mp); 1674 return; 1675 } 1676 1677 switch (mhi.mhi_bindsap) { 1678 case ETHERTYPE_IP: { 1679 struct dhcp *dh4; 1680 ipha_t *ipha = (ipha_t *)start; 1681 1682 if (start + sizeof (ipha_t) > end) 1683 return; 1684 1685 if (get_dhcpv4_info(ipha, end, &dh4) == 0) { 1686 intercept_dhcpv4_inbound(mcip, end, dh4); 1687 } 1688 break; 1689 } 1690 case ETHERTYPE_IPV6: { 1691 dhcpv6_message_t *dh6; 1692 nd_router_advert_t *ra; 1693 ip6_t *ip6h = (ip6_t *)start; 1694 1695 if (start + sizeof (ip6_t) > end) 1696 return; 1697 1698 if (get_dhcpv6_info(ip6h, end, &dh6) == 0) { 1699 intercept_dhcpv6_inbound(mcip, end, dh6); 1700 } else if (get_ra_info(ip6h, end, &ra) == 0) { 1701 intercept_ra_inbound(mcip, ip6h, end, ra); 1702 } 1703 1704 break; 1705 } 1706 } 1707 freemsg(nmp); 1708 } 1709 1710 void 1711 mac_protect_intercept_dynamic(mac_client_impl_t *mcip, mblk_t *mp) 1712 { 1713 /* 1714 * Skip checks if we are part of an aggr. 1715 */ 1716 if ((mcip->mci_state_flags & MCIS_IS_AGGR_PORT) != 0) 1717 return; 1718 1719 for (; mp != NULL; mp = mp->b_next) 1720 mac_protect_intercept_dynamic_one(mcip, mp); 1721 } 1722 1723 void 1724 mac_protect_flush_dynamic(mac_client_impl_t *mcip) 1725 { 1726 mutex_enter(&mcip->mci_protect_lock); 1727 flush_dhcpv4(mcip); 1728 flush_dhcpv6(mcip); 1729 flush_slaac(mcip); 1730 mutex_exit(&mcip->mci_protect_lock); 1731 } 1732 1733 void 1734 mac_protect_cancel_timer(mac_client_impl_t *mcip) 1735 { 1736 mutex_enter(&mcip->mci_protect_lock); 1737 cancel_txn_cleanup_timer(mcip); 1738 mutex_exit(&mcip->mci_protect_lock); 1739 } 1740 1741 /* 1742 * Check if addr is in the 'allowed-ips' list. 1743 */ 1744 1745 /* ARGSUSED */ 1746 static boolean_t 1747 ipnospoof_check_v4(mac_client_impl_t *mcip, mac_protect_t *protect, 1748 ipaddr_t *addr) 1749 { 1750 uint_t i; 1751 1752 /* 1753 * The unspecified address is allowed. 1754 */ 1755 if (*addr == INADDR_ANY) 1756 return (B_TRUE); 1757 1758 for (i = 0; i < protect->mp_ipaddrcnt; i++) { 1759 mac_ipaddr_t *v4addr = &protect->mp_ipaddrs[i]; 1760 1761 if (v4addr->ip_version == IPV4_VERSION) { 1762 uint32_t mask; 1763 1764 /* LINTED E_SUSPICIOUS_COMPARISON */ 1765 ASSERT(v4addr->ip_netmask >= 0 && 1766 v4addr->ip_netmask <= 32); 1767 mask = 0xFFFFFFFFu << (32 - v4addr->ip_netmask); 1768 /* 1769 * Since we have a netmask we know this entry 1770 * signifies the entire subnet. Check if the 1771 * given address is on the subnet. 1772 */ 1773 if (htonl(V4_PART_OF_V6(v4addr->ip_addr)) == 1774 (htonl(*addr) & mask)) 1775 return (B_TRUE); 1776 } 1777 } 1778 return (protect->mp_ipaddrcnt == 0 ? 1779 check_dhcpv4_dyn_ip(mcip, *addr) : B_FALSE); 1780 } 1781 1782 static boolean_t 1783 ipnospoof_check_v6(mac_client_impl_t *mcip, mac_protect_t *protect, 1784 in6_addr_t *addr) 1785 { 1786 uint_t i; 1787 1788 /* 1789 * The unspecified address and the v6 link local address are allowed. 1790 */ 1791 if (IN6_IS_ADDR_UNSPECIFIED(addr) || 1792 ((mcip->mci_protect_flags & MPT_FLAG_V6_LOCAL_ADDR_SET) != 0 && 1793 IN6_ARE_ADDR_EQUAL(&mcip->mci_v6_local_addr, addr))) 1794 return (B_TRUE); 1795 1796 1797 for (i = 0; i < protect->mp_ipaddrcnt; i++) { 1798 mac_ipaddr_t *v6addr = &protect->mp_ipaddrs[i]; 1799 1800 if (v6addr->ip_version == IPV6_VERSION && 1801 /* LINTED E_SUSPICIOUS_COMPARISON */ 1802 IN6_ARE_PREFIXEDADDR_EQUAL(&v6addr->ip_addr, addr, 1803 v6addr->ip_netmask)) 1804 return (B_TRUE); 1805 } 1806 1807 if (protect->mp_ipaddrcnt == 0) { 1808 return (check_slaac_ip(mcip, addr) || 1809 check_dhcpv6_dyn_ip(mcip, addr)); 1810 } else { 1811 return (B_FALSE); 1812 } 1813 } 1814 1815 /* 1816 * Checks various fields within an IPv6 NDP packet. 1817 */ 1818 static boolean_t 1819 ipnospoof_check_ndp(mac_client_impl_t *mcip, mac_protect_t *protect, 1820 ip6_t *ip6h, uchar_t *end) 1821 { 1822 icmp6_t *icmp_nd = (icmp6_t *)&ip6h[1]; 1823 int hdrlen, optlen, opttype, len; 1824 uint_t addrlen, maclen; 1825 uint8_t type; 1826 nd_opt_hdr_t *opt; 1827 struct nd_opt_lla *lla = NULL; 1828 1829 /* 1830 * NDP packets do not have extension headers so the ICMPv6 header 1831 * must immediately follow the IPv6 header. 1832 */ 1833 if (ip6h->ip6_nxt != IPPROTO_ICMPV6) 1834 return (B_TRUE); 1835 1836 /* ICMPv6 header missing */ 1837 if ((uchar_t *)&icmp_nd[1] > end) 1838 return (B_FALSE); 1839 1840 len = end - (uchar_t *)icmp_nd; 1841 type = icmp_nd->icmp6_type; 1842 1843 switch (type) { 1844 case ND_ROUTER_SOLICIT: 1845 hdrlen = sizeof (nd_router_solicit_t); 1846 break; 1847 case ND_ROUTER_ADVERT: 1848 hdrlen = sizeof (nd_router_advert_t); 1849 break; 1850 case ND_NEIGHBOR_SOLICIT: 1851 hdrlen = sizeof (nd_neighbor_solicit_t); 1852 break; 1853 case ND_NEIGHBOR_ADVERT: 1854 hdrlen = sizeof (nd_neighbor_advert_t); 1855 break; 1856 case ND_REDIRECT: 1857 hdrlen = sizeof (nd_redirect_t); 1858 break; 1859 default: 1860 return (B_TRUE); 1861 } 1862 1863 if (len < hdrlen) 1864 return (B_FALSE); 1865 1866 /* SLLA option checking is needed for RS/RA/NS */ 1867 opttype = ND_OPT_SOURCE_LINKADDR; 1868 1869 switch (type) { 1870 case ND_NEIGHBOR_ADVERT: { 1871 nd_neighbor_advert_t *na = (nd_neighbor_advert_t *)icmp_nd; 1872 1873 if (!ipnospoof_check_v6(mcip, protect, &na->nd_na_target)) { 1874 DTRACE_PROBE2(ndp__na__fail, 1875 mac_client_impl_t *, mcip, ip6_t *, ip6h); 1876 return (B_FALSE); 1877 } 1878 1879 /* TLLA option for NA */ 1880 opttype = ND_OPT_TARGET_LINKADDR; 1881 break; 1882 } 1883 case ND_REDIRECT: { 1884 /* option checking not needed for RD */ 1885 return (B_TRUE); 1886 } 1887 default: 1888 break; 1889 } 1890 1891 if (len == hdrlen) { 1892 /* no options, we're done */ 1893 return (B_TRUE); 1894 } 1895 opt = (nd_opt_hdr_t *)((uchar_t *)icmp_nd + hdrlen); 1896 optlen = len - hdrlen; 1897 1898 /* find the option header we need */ 1899 while (optlen > sizeof (nd_opt_hdr_t)) { 1900 if (opt->nd_opt_type == opttype) { 1901 lla = (struct nd_opt_lla *)opt; 1902 break; 1903 } 1904 optlen -= 8 * opt->nd_opt_len; 1905 opt = (nd_opt_hdr_t *) 1906 ((uchar_t *)opt + 8 * opt->nd_opt_len); 1907 } 1908 if (lla == NULL) 1909 return (B_TRUE); 1910 1911 addrlen = lla->nd_opt_lla_len * 8 - sizeof (nd_opt_hdr_t); 1912 maclen = mcip->mci_mip->mi_info.mi_addr_length; 1913 1914 if (addrlen != maclen || 1915 bcmp(mcip->mci_unicast->ma_addr, 1916 lla->nd_opt_lla_hdw_addr, maclen) != 0) { 1917 DTRACE_PROBE2(ndp__lla__fail, 1918 mac_client_impl_t *, mcip, ip6_t *, ip6h); 1919 return (B_FALSE); 1920 } 1921 1922 DTRACE_PROBE2(ndp__lla__ok, mac_client_impl_t *, mcip, ip6_t *, ip6h); 1923 return (B_TRUE); 1924 } 1925 1926 /* 1927 * Enforce ip-nospoof protection. 1928 */ 1929 static int 1930 ipnospoof_check(mac_client_impl_t *mcip, mac_protect_t *protect, 1931 mblk_t *mp, mac_header_info_t *mhip) 1932 { 1933 size_t hdrsize = mhip->mhi_hdrsize; 1934 uint32_t sap = mhip->mhi_bindsap; 1935 uchar_t *start, *end; 1936 mblk_t *nmp = NULL; 1937 int err; 1938 1939 err = get_l3_info(mp, hdrsize, &start, &end, &nmp); 1940 if (err != 0) { 1941 DTRACE_PROBE2(invalid__l3, mac_client_impl_t *, mcip, 1942 mblk_t *, mp); 1943 return (err); 1944 } 1945 err = EINVAL; 1946 1947 switch (sap) { 1948 case ETHERTYPE_IP: { 1949 ipha_t *ipha = (ipha_t *)start; 1950 1951 if (start + sizeof (ipha_t) > end) 1952 goto fail; 1953 1954 if (!ipnospoof_check_v4(mcip, protect, &ipha->ipha_src)) 1955 goto fail; 1956 1957 if (!intercept_dhcpv4_outbound(mcip, ipha, end)) 1958 goto fail; 1959 break; 1960 } 1961 case ETHERTYPE_ARP: { 1962 arh_t *arh = (arh_t *)start; 1963 uint32_t maclen, hlen, plen, arplen; 1964 ipaddr_t spaddr; 1965 uchar_t *shaddr; 1966 1967 if (start + sizeof (arh_t) > end) 1968 goto fail; 1969 1970 maclen = mcip->mci_mip->mi_info.mi_addr_length; 1971 hlen = arh->arh_hlen; 1972 plen = arh->arh_plen; 1973 if ((hlen != 0 && hlen != maclen) || 1974 plen != sizeof (ipaddr_t)) 1975 goto fail; 1976 1977 arplen = sizeof (arh_t) + 2 * hlen + 2 * plen; 1978 if (start + arplen > end) 1979 goto fail; 1980 1981 shaddr = start + sizeof (arh_t); 1982 if (hlen != 0 && 1983 bcmp(mcip->mci_unicast->ma_addr, shaddr, maclen) != 0) 1984 goto fail; 1985 1986 bcopy(shaddr + hlen, &spaddr, sizeof (spaddr)); 1987 if (!ipnospoof_check_v4(mcip, protect, &spaddr)) 1988 goto fail; 1989 break; 1990 } 1991 case ETHERTYPE_IPV6: { 1992 ip6_t *ip6h = (ip6_t *)start; 1993 1994 if (start + sizeof (ip6_t) > end) 1995 goto fail; 1996 1997 if (!ipnospoof_check_v6(mcip, protect, &ip6h->ip6_src)) 1998 goto fail; 1999 2000 if (!ipnospoof_check_ndp(mcip, protect, ip6h, end)) 2001 goto fail; 2002 2003 if (!intercept_dhcpv6_outbound(mcip, ip6h, end)) 2004 goto fail; 2005 break; 2006 } 2007 } 2008 freemsg(nmp); 2009 return (0); 2010 2011 fail: 2012 freemsg(nmp); 2013 return (err); 2014 } 2015 2016 static boolean_t 2017 dhcpnospoof_check_cid(mac_protect_t *p, uchar_t *cid, uint_t cidlen) 2018 { 2019 int i; 2020 2021 for (i = 0; i < p->mp_cidcnt; i++) { 2022 mac_dhcpcid_t *dcid = &p->mp_cids[i]; 2023 2024 if (dcid->dc_len == cidlen && 2025 bcmp(dcid->dc_id, cid, cidlen) == 0) 2026 return (B_TRUE); 2027 } 2028 return (B_FALSE); 2029 } 2030 2031 static boolean_t 2032 dhcpnospoof_check_v4(mac_client_impl_t *mcip, mac_protect_t *p, 2033 ipha_t *ipha, uchar_t *end) 2034 { 2035 struct dhcp *dh4; 2036 uchar_t *cid; 2037 uint_t maclen, cidlen = 0; 2038 uint8_t optlen; 2039 int err; 2040 2041 if ((err = get_dhcpv4_info(ipha, end, &dh4)) != 0) 2042 return (err == EINVAL); 2043 2044 maclen = mcip->mci_mip->mi_info.mi_addr_length; 2045 if (dh4->hlen == maclen && 2046 bcmp(mcip->mci_unicast->ma_addr, dh4->chaddr, maclen) != 0) { 2047 return (B_FALSE); 2048 } 2049 if (get_dhcpv4_option(dh4, end, CD_CLIENT_ID, &cid, &optlen) == 0) 2050 cidlen = optlen; 2051 2052 if (cidlen == 0) 2053 return (B_TRUE); 2054 2055 if (*cid == ARPHRD_ETHER && cidlen - 1 == maclen && 2056 bcmp(mcip->mci_unicast->ma_addr, cid + 1, maclen) == 0) 2057 return (B_TRUE); 2058 2059 return (dhcpnospoof_check_cid(p, cid, cidlen)); 2060 } 2061 2062 static boolean_t 2063 dhcpnospoof_check_v6(mac_client_impl_t *mcip, mac_protect_t *p, 2064 ip6_t *ip6h, uchar_t *end) 2065 { 2066 dhcpv6_message_t *dh6; 2067 dhcpv6_option_t *d6o; 2068 uint8_t mtype; 2069 uchar_t *cid, *lladdr = NULL; 2070 uint_t cidlen, maclen, addrlen = 0; 2071 uint16_t cidtype; 2072 int err; 2073 2074 if ((err = get_dhcpv6_info(ip6h, end, &dh6)) != 0) 2075 return (err == EINVAL); 2076 2077 /* 2078 * We only check client-generated messages. 2079 */ 2080 mtype = dh6->d6m_msg_type; 2081 if (mtype == DHCPV6_MSG_ADVERTISE || mtype == DHCPV6_MSG_REPLY || 2082 mtype == DHCPV6_MSG_RECONFIGURE) 2083 return (B_TRUE); 2084 2085 d6o = get_dhcpv6_option(&dh6[1], end - (uchar_t *)&dh6[1], NULL, 2086 DHCPV6_OPT_CLIENTID, &cidlen); 2087 if (d6o == NULL || (uchar_t *)d6o + cidlen > end) 2088 return (B_TRUE); 2089 2090 cid = (uchar_t *)&d6o[1]; 2091 cidlen -= sizeof (*d6o); 2092 if (cidlen < sizeof (cidtype)) 2093 return (B_TRUE); 2094 2095 bcopy(cid, &cidtype, sizeof (cidtype)); 2096 cidtype = ntohs(cidtype); 2097 if (cidtype == DHCPV6_DUID_LLT && cidlen >= sizeof (duid_llt_t)) { 2098 lladdr = cid + sizeof (duid_llt_t); 2099 addrlen = cidlen - sizeof (duid_llt_t); 2100 } 2101 if (cidtype == DHCPV6_DUID_LL && cidlen >= sizeof (duid_ll_t)) { 2102 lladdr = cid + sizeof (duid_ll_t); 2103 addrlen = cidlen - sizeof (duid_ll_t); 2104 } 2105 maclen = mcip->mci_mip->mi_info.mi_addr_length; 2106 if (lladdr != NULL && addrlen == maclen && 2107 bcmp(mcip->mci_unicast->ma_addr, lladdr, maclen) == 0) { 2108 return (B_TRUE); 2109 } 2110 return (dhcpnospoof_check_cid(p, cid, cidlen)); 2111 } 2112 2113 /* 2114 * Enforce dhcp-nospoof protection. 2115 */ 2116 static int 2117 dhcpnospoof_check(mac_client_impl_t *mcip, mac_protect_t *protect, 2118 mblk_t *mp, mac_header_info_t *mhip) 2119 { 2120 size_t hdrsize = mhip->mhi_hdrsize; 2121 uint32_t sap = mhip->mhi_bindsap; 2122 uchar_t *start, *end; 2123 mblk_t *nmp = NULL; 2124 int err; 2125 2126 err = get_l3_info(mp, hdrsize, &start, &end, &nmp); 2127 if (err != 0) { 2128 DTRACE_PROBE2(invalid__l3, mac_client_impl_t *, mcip, 2129 mblk_t *, mp); 2130 return (err); 2131 } 2132 err = EINVAL; 2133 2134 switch (sap) { 2135 case ETHERTYPE_IP: { 2136 ipha_t *ipha = (ipha_t *)start; 2137 2138 if (start + sizeof (ipha_t) > end) 2139 goto fail; 2140 2141 if (!dhcpnospoof_check_v4(mcip, protect, ipha, end)) 2142 goto fail; 2143 2144 break; 2145 } 2146 case ETHERTYPE_IPV6: { 2147 ip6_t *ip6h = (ip6_t *)start; 2148 2149 if (start + sizeof (ip6_t) > end) 2150 goto fail; 2151 2152 if (!dhcpnospoof_check_v6(mcip, protect, ip6h, end)) 2153 goto fail; 2154 2155 break; 2156 } 2157 } 2158 freemsg(nmp); 2159 return (0); 2160 2161 fail: 2162 /* increment dhcpnospoof stat here */ 2163 freemsg(nmp); 2164 return (err); 2165 } 2166 2167 /* 2168 * This is called whenever the mac client's mac address changes, to make sure 2169 * we allow use of the new link-local address. 2170 */ 2171 static void 2172 mac_protect_update_v6_local_addr(mac_client_impl_t *mcip) 2173 { 2174 uint_t i; 2175 in6_addr_t *token = &mcip->mci_v6_mac_token; 2176 in6_addr_t *v6addr = &mcip->mci_v6_local_addr; 2177 in6_addr_t ll_template = {{{ (uint32_t)V6_LINKLOCAL, 0, 0, 0 }}}; 2178 2179 for (i = 0; i < 4; i++) { 2180 v6addr->s6_addr32[i] = token->s6_addr32[i] | 2181 ll_template.s6_addr32[i]; 2182 } 2183 mcip->mci_protect_flags |= MPT_FLAG_V6_LOCAL_ADDR_SET; 2184 } 2185 2186 /* 2187 * This is called whenever the mac client's mac address changes, to make sure 2188 * that any existing addresses gained via SLAAC are appropriately updated. 2189 */ 2190 static void 2191 mac_protect_update_v6_slaac_addr(mac_client_impl_t *mcip) 2192 { 2193 void *cookie = NULL; 2194 avl_tree_t temp_tree; 2195 avl_tree_t *ttp = &temp_tree, *sip = &mcip->mci_v6_slaac_ip; 2196 in6_addr_t *token = &mcip->mci_v6_mac_token; 2197 slaac_addr_t *addr = NULL; 2198 2199 avl_create(ttp, compare_slaac_ip, sizeof (slaac_addr_t), 2200 offsetof(slaac_addr_t, sla_node)); 2201 2202 /* Copy everything over to the temporary tree, and fix the IP address */ 2203 while ((addr = avl_destroy_nodes(sip, &cookie)) != NULL) { 2204 VERIFY(insert_slaac_ip(ttp, token, addr) == B_TRUE); 2205 } 2206 2207 /* 2208 * Now that the tempory tree has all of the modified addresses, we can 2209 * swap them over to the original tree once it's reset. 2210 */ 2211 avl_destroy(sip); 2212 avl_create(sip, compare_slaac_ip, sizeof (slaac_addr_t), 2213 offsetof(slaac_addr_t, sla_node)); 2214 avl_swap(ttp, sip); 2215 } 2216 2217 /* 2218 * After the unicast MAC address changes, we need to update the derived token, 2219 * and update the IPv6 addresses that use the token. 2220 */ 2221 void 2222 mac_protect_update_mac_token(mac_client_impl_t *mcip) 2223 { 2224 uint_t media = mcip->mci_mip->mi_info.mi_media; 2225 uint8_t *p, *macaddr = mcip->mci_unicast->ma_addr; 2226 in6_addr_t *token = &mcip->mci_v6_mac_token; 2227 2228 bzero(token, sizeof (in6_addr_t)); 2229 p = (uint8_t *)&token->s6_addr32[2]; 2230 2231 switch (media) { 2232 case DL_ETHER: 2233 bcopy(macaddr, p, 3); 2234 p[0] ^= 0x2; 2235 p[3] = 0xff; 2236 p[4] = 0xfe; 2237 bcopy(macaddr + 3, p + 5, 3); 2238 break; 2239 case DL_IB: 2240 ASSERT(mcip->mci_mip->mi_info.mi_addr_length == 20); 2241 bcopy(macaddr + 12, p, 8); 2242 p[0] |= 2; 2243 break; 2244 default: 2245 /* 2246 * We do not need to generate the local address for link types 2247 * that do not support link protection. Wifi pretends to be 2248 * Ethernet so it is covered by the DL_ETHER case (note the 2249 * use of mi_media instead of mi_nativemedia). 2250 */ 2251 return; 2252 } 2253 2254 mac_protect_update_v6_local_addr(mcip); 2255 mac_protect_update_v6_slaac_addr(mcip); 2256 } 2257 2258 2259 2260 /* 2261 * Enforce link protection on one packet. 2262 */ 2263 static int 2264 mac_protect_check_one(mac_client_impl_t *mcip, mblk_t *mp) 2265 { 2266 mac_impl_t *mip = mcip->mci_mip; 2267 mac_resource_props_t *mrp = MCIP_RESOURCE_PROPS(mcip); 2268 mac_protect_t *protect; 2269 mac_header_info_t mhi; 2270 uint32_t types; 2271 int err; 2272 2273 ASSERT(mp->b_next == NULL); 2274 ASSERT(mrp != NULL); 2275 2276 err = mac_vlan_header_info((mac_handle_t)mip, mp, &mhi); 2277 if (err != 0) { 2278 DTRACE_PROBE2(invalid__header, mac_client_impl_t *, mcip, 2279 mblk_t *, mp); 2280 return (err); 2281 } 2282 protect = &mrp->mrp_protect; 2283 types = protect->mp_types; 2284 2285 if ((types & MPT_MACNOSPOOF) != 0) { 2286 if (mhi.mhi_saddr != NULL && 2287 bcmp(mcip->mci_unicast->ma_addr, mhi.mhi_saddr, 2288 mip->mi_info.mi_addr_length) != 0) { 2289 BUMP_STAT(mcip, macspoofed); 2290 DTRACE_PROBE2(mac__nospoof__fail, 2291 mac_client_impl_t *, mcip, mblk_t *, mp); 2292 return (EINVAL); 2293 } 2294 } 2295 if ((types & MPT_RESTRICTED) != 0) { 2296 uint32_t vid = VLAN_ID(mhi.mhi_tci); 2297 uint32_t sap = mhi.mhi_bindsap; 2298 2299 /* 2300 * ETHERTYPE_VLAN packets are allowed through, provided that 2301 * the vid is not spoofed. 2302 */ 2303 if (vid != 0 && !mac_client_check_flow_vid(mcip, vid)) { 2304 BUMP_STAT(mcip, restricted); 2305 DTRACE_PROBE2(restricted__vid__invalid, 2306 mac_client_impl_t *, mcip, mblk_t *, mp); 2307 return (EINVAL); 2308 } 2309 2310 if (sap != ETHERTYPE_IP && sap != ETHERTYPE_IPV6 && 2311 sap != ETHERTYPE_ARP) { 2312 BUMP_STAT(mcip, restricted); 2313 DTRACE_PROBE2(restricted__fail, 2314 mac_client_impl_t *, mcip, mblk_t *, mp); 2315 return (EINVAL); 2316 } 2317 } 2318 if ((types & MPT_IPNOSPOOF) != 0) { 2319 if ((err = ipnospoof_check(mcip, protect, mp, &mhi)) != 0) { 2320 BUMP_STAT(mcip, ipspoofed); 2321 DTRACE_PROBE2(ip__nospoof__fail, 2322 mac_client_impl_t *, mcip, mblk_t *, mp); 2323 return (err); 2324 } 2325 } 2326 if ((types & MPT_DHCPNOSPOOF) != 0) { 2327 if ((err = dhcpnospoof_check(mcip, protect, mp, &mhi)) != 0) { 2328 BUMP_STAT(mcip, dhcpspoofed); 2329 DTRACE_PROBE2(dhcp__nospoof__fail, 2330 mac_client_impl_t *, mcip, mblk_t *, mp); 2331 return (err); 2332 } 2333 } 2334 return (0); 2335 } 2336 2337 /* 2338 * Enforce link protection on a packet chain. 2339 * Packets that pass the checks are returned back to the caller. 2340 */ 2341 mblk_t * 2342 mac_protect_check(mac_client_handle_t mch, mblk_t *mp) 2343 { 2344 mac_client_impl_t *mcip = (mac_client_impl_t *)mch; 2345 mblk_t *ret_mp = NULL, **tailp = &ret_mp, *next; 2346 2347 /* 2348 * Skip checks if we are part of an aggr. 2349 */ 2350 if ((mcip->mci_state_flags & MCIS_IS_AGGR_PORT) != 0) 2351 return (mp); 2352 2353 for (; mp != NULL; mp = next) { 2354 next = mp->b_next; 2355 mp->b_next = NULL; 2356 2357 if (mac_protect_check_one(mcip, mp) == 0) { 2358 *tailp = mp; 2359 tailp = &mp->b_next; 2360 } else { 2361 freemsg(mp); 2362 } 2363 } 2364 return (ret_mp); 2365 } 2366 2367 /* 2368 * Check if a particular protection type is enabled. 2369 */ 2370 boolean_t 2371 mac_protect_enabled(mac_client_handle_t mch, uint32_t type) 2372 { 2373 return (MAC_PROTECT_ENABLED((mac_client_impl_t *)mch, type)); 2374 } 2375 2376 static int 2377 validate_ips(mac_protect_t *p) 2378 { 2379 uint_t i, j; 2380 2381 if (p->mp_ipaddrcnt == MPT_RESET) 2382 return (0); 2383 2384 if (p->mp_ipaddrcnt > MPT_MAXIPADDR) 2385 return (EINVAL); 2386 2387 for (i = 0; i < p->mp_ipaddrcnt; i++) { 2388 mac_ipaddr_t *addr = &p->mp_ipaddrs[i]; 2389 2390 /* 2391 * The unspecified address is implicitly allowed so there's no 2392 * need to add it to the list. Also, validate that the netmask, 2393 * if any, is sane for the specific version of IP. A mask of 2394 * some kind is always required. 2395 */ 2396 if (addr->ip_netmask == 0) 2397 return (EINVAL); 2398 2399 if (addr->ip_version == IPV4_VERSION) { 2400 if (V4_PART_OF_V6(addr->ip_addr) == INADDR_ANY) 2401 return (EINVAL); 2402 if (addr->ip_netmask > 32) 2403 return (EINVAL); 2404 } else if (addr->ip_version == IPV6_VERSION) { 2405 if (IN6_IS_ADDR_UNSPECIFIED(&addr->ip_addr)) 2406 return (EINVAL); 2407 2408 if (IN6_IS_ADDR_V4MAPPED_ANY(&addr->ip_addr)) 2409 return (EINVAL); 2410 2411 if (addr->ip_netmask > 128) 2412 return (EINVAL); 2413 } else { 2414 /* invalid ip version */ 2415 return (EINVAL); 2416 } 2417 2418 for (j = 0; j < p->mp_ipaddrcnt; j++) { 2419 mac_ipaddr_t *addr1 = &p->mp_ipaddrs[j]; 2420 2421 if (i == j || addr->ip_version != addr1->ip_version) 2422 continue; 2423 2424 /* found a duplicate */ 2425 if ((addr->ip_version == IPV4_VERSION && 2426 V4_PART_OF_V6(addr->ip_addr) == 2427 V4_PART_OF_V6(addr1->ip_addr)) || 2428 IN6_ARE_ADDR_EQUAL(&addr->ip_addr, 2429 &addr1->ip_addr)) 2430 return (EINVAL); 2431 } 2432 } 2433 return (0); 2434 } 2435 2436 /* ARGSUSED */ 2437 static int 2438 validate_cids(mac_protect_t *p) 2439 { 2440 uint_t i, j; 2441 2442 if (p->mp_cidcnt == MPT_RESET) 2443 return (0); 2444 2445 if (p->mp_cidcnt > MPT_MAXCID) 2446 return (EINVAL); 2447 2448 for (i = 0; i < p->mp_cidcnt; i++) { 2449 mac_dhcpcid_t *cid = &p->mp_cids[i]; 2450 2451 if (cid->dc_len > MPT_MAXCIDLEN || 2452 (cid->dc_form != CIDFORM_TYPED && 2453 cid->dc_form != CIDFORM_HEX && 2454 cid->dc_form != CIDFORM_STR)) 2455 return (EINVAL); 2456 2457 for (j = 0; j < p->mp_cidcnt; j++) { 2458 mac_dhcpcid_t *cid1 = &p->mp_cids[j]; 2459 2460 if (i == j || cid->dc_len != cid1->dc_len) 2461 continue; 2462 2463 /* found a duplicate */ 2464 if (bcmp(cid->dc_id, cid1->dc_id, cid->dc_len) == 0) 2465 return (EINVAL); 2466 } 2467 } 2468 return (0); 2469 } 2470 2471 /* 2472 * Sanity-checks parameters given by userland. 2473 */ 2474 int 2475 mac_protect_validate(mac_resource_props_t *mrp) 2476 { 2477 mac_protect_t *p = &mrp->mrp_protect; 2478 int err; 2479 2480 /* check for invalid types */ 2481 if (p->mp_types != MPT_RESET && (p->mp_types & ~MPT_ALL) != 0) 2482 return (EINVAL); 2483 2484 if ((err = validate_ips(p)) != 0) 2485 return (err); 2486 2487 if ((err = validate_cids(p)) != 0) 2488 return (err); 2489 2490 return (0); 2491 } 2492 2493 /* 2494 * Enable/disable link protection. 2495 */ 2496 int 2497 mac_protect_set(mac_client_handle_t mch, mac_resource_props_t *mrp) 2498 { 2499 mac_client_impl_t *mcip = (mac_client_impl_t *)mch; 2500 mac_impl_t *mip = mcip->mci_mip; 2501 uint_t media = mip->mi_info.mi_nativemedia; 2502 int err; 2503 2504 ASSERT(MAC_PERIM_HELD((mac_handle_t)mip)); 2505 2506 /* tunnels are not supported */ 2507 if (media == DL_IPV4 || media == DL_IPV6 || media == DL_6TO4) 2508 return (ENOTSUP); 2509 2510 if ((err = mac_protect_validate(mrp)) != 0) 2511 return (err); 2512 2513 if (err != 0) 2514 return (err); 2515 2516 mac_update_resources(mrp, MCIP_RESOURCE_PROPS(mcip), B_FALSE); 2517 i_mac_notify(((mcip->mci_state_flags & MCIS_IS_VNIC) != 0 ? 2518 mcip->mci_upper_mip : mip), MAC_NOTE_ALLOWED_IPS); 2519 return (0); 2520 } 2521 2522 void 2523 mac_protect_update(mac_resource_props_t *new, mac_resource_props_t *curr) 2524 { 2525 mac_protect_t *np = &new->mrp_protect; 2526 mac_protect_t *cp = &curr->mrp_protect; 2527 uint32_t types = np->mp_types; 2528 2529 if (types == MPT_RESET) { 2530 cp->mp_types = 0; 2531 curr->mrp_mask &= ~MRP_PROTECT; 2532 } else { 2533 if (types != 0) { 2534 cp->mp_types = types; 2535 curr->mrp_mask |= MRP_PROTECT; 2536 } 2537 } 2538 if (np->mp_ipaddrcnt != 0) { 2539 if (np->mp_ipaddrcnt <= MPT_MAXIPADDR) { 2540 bcopy(np->mp_ipaddrs, cp->mp_ipaddrs, 2541 sizeof (cp->mp_ipaddrs)); 2542 cp->mp_ipaddrcnt = np->mp_ipaddrcnt; 2543 } else if (np->mp_ipaddrcnt == MPT_RESET) { 2544 bzero(cp->mp_ipaddrs, sizeof (cp->mp_ipaddrs)); 2545 cp->mp_ipaddrcnt = 0; 2546 } 2547 } 2548 if (np->mp_cidcnt != 0) { 2549 if (np->mp_cidcnt <= MPT_MAXCID) { 2550 bcopy(np->mp_cids, cp->mp_cids, sizeof (cp->mp_cids)); 2551 cp->mp_cidcnt = np->mp_cidcnt; 2552 } else if (np->mp_cidcnt == MPT_RESET) { 2553 bzero(cp->mp_cids, sizeof (cp->mp_cids)); 2554 cp->mp_cidcnt = 0; 2555 } 2556 } 2557 } 2558 2559 void 2560 mac_protect_init(mac_client_impl_t *mcip) 2561 { 2562 mutex_init(&mcip->mci_protect_lock, NULL, MUTEX_DRIVER, NULL); 2563 mcip->mci_protect_flags = 0; 2564 mcip->mci_txn_cleanup_tid = 0; 2565 avl_create(&mcip->mci_v4_pending_txn, compare_dhcpv4_xid, 2566 sizeof (dhcpv4_txn_t), offsetof(dhcpv4_txn_t, dt_node)); 2567 avl_create(&mcip->mci_v4_completed_txn, compare_dhcpv4_cid, 2568 sizeof (dhcpv4_txn_t), offsetof(dhcpv4_txn_t, dt_node)); 2569 avl_create(&mcip->mci_v4_dyn_ip, compare_dhcpv4_ip, 2570 sizeof (dhcpv4_txn_t), offsetof(dhcpv4_txn_t, dt_ipnode)); 2571 avl_create(&mcip->mci_v6_pending_txn, compare_dhcpv6_xid, 2572 sizeof (dhcpv6_txn_t), offsetof(dhcpv6_txn_t, dt_node)); 2573 avl_create(&mcip->mci_v6_cid, compare_dhcpv6_cid, 2574 sizeof (dhcpv6_cid_t), offsetof(dhcpv6_cid_t, dc_node)); 2575 avl_create(&mcip->mci_v6_dyn_ip, compare_dhcpv6_ip, 2576 sizeof (dhcpv6_addr_t), offsetof(dhcpv6_addr_t, da_node)); 2577 avl_create(&mcip->mci_v6_slaac_ip, compare_slaac_ip, 2578 sizeof (slaac_addr_t), offsetof(slaac_addr_t, sla_node)); 2579 } 2580 2581 void 2582 mac_protect_fini(mac_client_impl_t *mcip) 2583 { 2584 avl_destroy(&mcip->mci_v6_dyn_ip); 2585 avl_destroy(&mcip->mci_v6_cid); 2586 avl_destroy(&mcip->mci_v6_pending_txn); 2587 avl_destroy(&mcip->mci_v4_dyn_ip); 2588 avl_destroy(&mcip->mci_v4_completed_txn); 2589 avl_destroy(&mcip->mci_v4_pending_txn); 2590 avl_destroy(&mcip->mci_v6_slaac_ip); 2591 mcip->mci_txn_cleanup_tid = 0; 2592 mcip->mci_protect_flags = 0; 2593 mutex_destroy(&mcip->mci_protect_lock); 2594 } 2595 2596 static boolean_t 2597 allowed_ips_set(mac_resource_props_t *mrp, uint32_t af) 2598 { 2599 int i; 2600 2601 for (i = 0; i < mrp->mrp_protect.mp_ipaddrcnt; i++) { 2602 if (mrp->mrp_protect.mp_ipaddrs[i].ip_version == af) 2603 return (B_TRUE); 2604 } 2605 return (B_FALSE); 2606 } 2607 2608 mac_protect_t * 2609 mac_protect_get(mac_handle_t mh) 2610 { 2611 mac_impl_t *mip = (mac_impl_t *)mh; 2612 2613 return (&mip->mi_resource_props.mrp_protect); 2614 }