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