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