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