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