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 mac_protect_t *
2299 mac_protect_get(mac_handle_t mh)
2300 {
2301         mac_impl_t *mip = (mac_impl_t *)mh;
2302 
2303         return (&mip->mi_resource_props.mrp_protect);
2304 }