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