1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
  24  * Copyright (c) 2015, Joyent, Inc.  All rights reserved.
  25  */
  26 /*
  27  * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
  28  */
  29 
  30 #include <sys/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 
 816         if (!mac_ip_hdr_length_v6(ip6h, end, &hdrlen, &proto, &frag))
 817                 return (ENOSPC);
 818 
 819         if (proto != IPPROTO_UDP)
 820                 return (EINVAL);
 821 
 822         if (frag != NULL) {
 823                 /*
 824                  * All non-initial fragments may pass because we cannot
 825                  * identify their type. It's safe to let them through
 826                  * because reassembly will fail if we decide to drop the
 827                  * initial fragment.
 828                  */
 829                 if ((ntohs(frag->ip6f_offlg) & ~7) != 0)
 830                         return (EINVAL);
 831                 first_frag = B_TRUE;
 832         }
 833         /* drop packets without a udp header */
 834         udph = (struct udphdr *)((uchar_t *)ip6h + hdrlen);
 835         if ((uchar_t *)&udph[1] > end)
 836                 return (ENOSPC);
 837 
 838         client = htons(IPPORT_DHCPV6C);
 839         server = htons(IPPORT_DHCPV6S);
 840         if (udph->uh_sport != client && udph->uh_sport != server &&
 841             udph->uh_dport != client && udph->uh_dport != server)
 842                 return (EINVAL);
 843 
 844         /* drop dhcp fragments */
 845         if (first_frag)
 846                 return (ENOSPC);
 847 
 848         dh = (uchar_t *)&udph[1];
 849         if (dh + sizeof (dhcpv6_message_t) > end)
 850                 return (EINVAL);
 851 
 852         *dh6 = (dhcpv6_message_t *)dh;
 853         return (0);
 854 }
 855 
 856 static int
 857 get_ra_info(ip6_t *ip6h, uchar_t *end, nd_router_advert_t **ra)
 858 {
 859         uint16_t                hdrlen;
 860         ip6_frag_t              *frag = NULL;
 861         uint8_t                 proto;
 862         uchar_t                 *hdrp;
 863         struct icmp6_hdr        *icmp;
 864 
 865         if (!mac_ip_hdr_length_v6(ip6h, end, &hdrlen, &proto, &frag))
 866                 return (ENOSPC);
 867 
 868         if (proto != IPPROTO_ICMPV6)
 869                 return (EINVAL);
 870 
 871         if (frag != NULL) {
 872                 /*
 873                  * All non-initial fragments may pass because we cannot
 874                  * identify their type. It's safe to let them through
 875                  * because reassembly will fail if we decide to drop the
 876                  * initial fragment.
 877                  */
 878                 if ((ntohs(frag->ip6f_offlg) & ~7) != 0)
 879                         return (EINVAL);
 880                 return (ENOSPC);
 881         }
 882 
 883         /*
 884          * Ensure that the ICMP header falls w/in packet boundaries, in case
 885          * we've received a malicious packet that reports incorrect lengths.
 886          */
 887         hdrp = (uchar_t *)ip6h + hdrlen;
 888         if ((hdrp + sizeof (struct icmp6_hdr)) > end) {
 889                 return (EINVAL);
 890         }
 891         icmp = (struct icmp6_hdr *)hdrp;
 892 
 893         if (icmp->icmp6_type != ND_ROUTER_ADVERT ||
 894             icmp->icmp6_code != 0)
 895                 return (EINVAL);
 896 
 897         *ra = (nd_router_advert_t *)icmp;
 898         return (0);
 899 }
 900 
 901 /*
 902  * Find the specified DHCPv6 option.
 903  */
 904 static dhcpv6_option_t *
 905 get_dhcpv6_option(void *buf, size_t buflen, dhcpv6_option_t *oldopt,
 906     uint16_t codenum, uint_t *retlenp)
 907 {
 908         uchar_t         *bp;
 909         dhcpv6_option_t d6o;
 910         uint_t          olen;
 911 
 912         codenum = htons(codenum);
 913         bp = buf;
 914         while (buflen >= sizeof (dhcpv6_option_t)) {
 915                 bcopy(bp, &d6o, sizeof (d6o));
 916                 olen = ntohs(d6o.d6o_len) + sizeof (d6o);
 917                 if (olen > buflen)
 918                         break;
 919                 if (d6o.d6o_code != codenum || d6o.d6o_len == 0 ||
 920                     (oldopt != NULL && bp <= (uchar_t *)oldopt)) {
 921                         bp += olen;
 922                         buflen -= olen;
 923                         continue;
 924                 }
 925                 if (retlenp != NULL)
 926                         *retlenp = olen;
 927                 /* LINTED : alignment */
 928                 return ((dhcpv6_option_t *)bp);
 929         }
 930         return (NULL);
 931 }
 932 
 933 /*
 934  * Get the status code from a reply message.
 935  */
 936 static int
 937 get_dhcpv6_status(dhcpv6_message_t *dh6, uchar_t *end, uint16_t *status)
 938 {
 939         dhcpv6_option_t *d6o;
 940         uint_t          olen;
 941         uint16_t        s;
 942 
 943         d6o = get_dhcpv6_option(&dh6[1], end - (uchar_t *)&dh6[1], NULL,
 944             DHCPV6_OPT_STATUS_CODE, &olen);
 945 
 946         /* Success is implied if status code is missing */
 947         if (d6o == NULL) {
 948                 *status = DHCPV6_STAT_SUCCESS;
 949                 return (0);
 950         }
 951         if ((uchar_t *)d6o + olen > end)
 952                 return (EINVAL);
 953 
 954         olen -= sizeof (*d6o);
 955         if (olen < sizeof (s))
 956                 return (EINVAL);
 957 
 958         bcopy(&d6o[1], &s, sizeof (s));
 959         *status = ntohs(s);
 960         return (0);
 961 }
 962 
 963 /*
 964  * Get the addresses from a reply message.
 965  */
 966 static int
 967 get_dhcpv6_addrs(dhcpv6_message_t *dh6, uchar_t *end, dhcpv6_cid_t *cid)
 968 {
 969         dhcpv6_option_t         *d6o;
 970         dhcpv6_addr_t           *next;
 971         uint_t                  olen;
 972 
 973         d6o = NULL;
 974         while ((d6o = get_dhcpv6_option(&dh6[1], end - (uchar_t *)&dh6[1],
 975             d6o, DHCPV6_OPT_IA_NA, &olen)) != NULL) {
 976                 dhcpv6_option_t         *d6so;
 977                 dhcpv6_iaaddr_t         d6ia;
 978                 dhcpv6_addr_t           **addrp;
 979                 uchar_t                 *obase;
 980                 uint_t                  solen;
 981 
 982                 if (olen < sizeof (dhcpv6_ia_na_t) ||
 983                     (uchar_t *)d6o + olen > end)
 984                         goto fail;
 985 
 986                 obase = (uchar_t *)d6o + sizeof (dhcpv6_ia_na_t);
 987                 olen -= sizeof (dhcpv6_ia_na_t);
 988                 d6so = NULL;
 989                 while ((d6so = get_dhcpv6_option(obase, olen, d6so,
 990                     DHCPV6_OPT_IAADDR, &solen)) != NULL) {
 991                         if (solen < sizeof (dhcpv6_iaaddr_t) ||
 992                             (uchar_t *)d6so + solen > end)
 993                                 goto fail;
 994 
 995                         bcopy(d6so, &d6ia, sizeof (d6ia));
 996                         for (addrp = &cid->dc_addr; *addrp != NULL;
 997                             addrp = &(*addrp)->da_next) {
 998                                 if (bcmp(&(*addrp)->da_addr, &d6ia.d6ia_addr,
 999                                     sizeof (in6_addr_t)) == 0)
1000                                         goto fail;
1001                         }
1002                         if ((*addrp = kmem_zalloc(sizeof (dhcpv6_addr_t),
1003                             KM_NOSLEEP)) == NULL)
1004                                 goto fail;
1005 
1006                         bcopy(&d6ia.d6ia_addr, &(*addrp)->da_addr,
1007                             sizeof (in6_addr_t));
1008                         cid->dc_addrcnt++;
1009                 }
1010         }
1011         if (cid->dc_addrcnt == 0)
1012                 return (ENOENT);
1013 
1014         return (0);
1015 
1016 fail:
1017         for (; cid->dc_addr != NULL; cid->dc_addr = next) {
1018                 next = cid->dc_addr->da_next;
1019                 kmem_free(cid->dc_addr, sizeof (dhcpv6_addr_t));
1020                 cid->dc_addrcnt--;
1021         }
1022         ASSERT(cid->dc_addrcnt == 0);
1023         return (EINVAL);
1024 }
1025 
1026 /*
1027  * Free a cid.
1028  * Before this gets called the caller must ensure that all the
1029  * addresses are removed from the mci_v6_dyn_ip table.
1030  */
1031 static void
1032 free_dhcpv6_cid(dhcpv6_cid_t *cid)
1033 {
1034         dhcpv6_addr_t   *addr, *next;
1035         uint_t          cnt = 0;
1036 
1037         kmem_free(cid->dc_cid, cid->dc_cid_len);
1038         for (addr = cid->dc_addr; addr != NULL; addr = next) {
1039                 next = addr->da_next;
1040                 kmem_free(addr, sizeof (*addr));
1041                 cnt++;
1042         }
1043         ASSERT(cnt == cid->dc_addrcnt);
1044         kmem_free(cid, sizeof (*cid));
1045 }
1046 
1047 /*
1048  * Extract the DUID from a message. The associated addresses will be
1049  * extracted later from the reply message.
1050  */
1051 static dhcpv6_cid_t *
1052 create_dhcpv6_cid(dhcpv6_message_t *dh6, uchar_t *end)
1053 {
1054         dhcpv6_option_t         *d6o;
1055         dhcpv6_cid_t            *cid;
1056         uchar_t                 *rawcid;
1057         uint_t                  olen, rawcidlen;
1058 
1059         d6o = get_dhcpv6_option(&dh6[1], end - (uchar_t *)&dh6[1], NULL,
1060             DHCPV6_OPT_CLIENTID, &olen);
1061         if (d6o == NULL || (uchar_t *)d6o + olen > end)
1062                 return (NULL);
1063 
1064         rawcidlen = olen - sizeof (*d6o);
1065         if ((rawcid = kmem_zalloc(rawcidlen, KM_NOSLEEP)) == NULL)
1066                 return (NULL);
1067         bcopy(d6o + 1, rawcid, rawcidlen);
1068 
1069         if ((cid = kmem_zalloc(sizeof (*cid), KM_NOSLEEP)) == NULL) {
1070                 kmem_free(rawcid, rawcidlen);
1071                 return (NULL);
1072         }
1073         cid->dc_cid = rawcid;
1074         cid->dc_cid_len = rawcidlen;
1075         return (cid);
1076 }
1077 
1078 /*
1079  * Remove a cid from mci_v6_cid. The addresses owned by the cid
1080  * are also removed from mci_v6_dyn_ip.
1081  */
1082 static void
1083 remove_dhcpv6_cid(mac_client_impl_t *mcip, dhcpv6_cid_t *cid)
1084 {
1085         dhcpv6_addr_t   *addr, *tmp_addr;
1086 
1087         ASSERT(MUTEX_HELD(&mcip->mci_protect_lock));
1088         avl_remove(&mcip->mci_v6_cid, cid);
1089         for (addr = cid->dc_addr; addr != NULL; addr = addr->da_next) {
1090                 tmp_addr = avl_find(&mcip->mci_v6_dyn_ip, addr, NULL);
1091                 if (tmp_addr == addr)
1092                         avl_remove(&mcip->mci_v6_dyn_ip, addr);
1093         }
1094 }
1095 
1096 /*
1097  * Find and remove a matching cid and associated addresses from
1098  * their respective tables.
1099  */
1100 static void
1101 release_dhcpv6_cid(mac_client_impl_t *mcip, dhcpv6_cid_t *cid)
1102 {
1103         dhcpv6_cid_t    *oldcid;
1104 
1105         ASSERT(MUTEX_HELD(&mcip->mci_protect_lock));
1106         if ((oldcid = avl_find(&mcip->mci_v6_cid, cid, NULL)) == NULL)
1107                 return;
1108 
1109         /*
1110          * Since cid belongs to a pending txn, it can't possibly be in
1111          * mci_v6_cid. Anything that's found must be an existing cid.
1112          */
1113         ASSERT(oldcid != cid);
1114         remove_dhcpv6_cid(mcip, oldcid);
1115         free_dhcpv6_cid(oldcid);
1116 }
1117 
1118 /*
1119  * Insert cid into mci_v6_cid.
1120  */
1121 static int
1122 insert_dhcpv6_cid(mac_client_impl_t *mcip, dhcpv6_cid_t *cid)
1123 {
1124         avl_index_t     where;
1125         dhcpv6_addr_t   *addr;
1126 
1127         ASSERT(MUTEX_HELD(&mcip->mci_protect_lock));
1128         if (avl_find(&mcip->mci_v6_cid, cid, &where) != NULL)
1129                 return (EEXIST);
1130 
1131         if (avl_numnodes(&mcip->mci_v6_cid) >= dhcp_max_completed_txn) {
1132                 BUMP_STAT(mcip, dhcpdropped);
1133                 return (EAGAIN);
1134         }
1135         avl_insert(&mcip->mci_v6_cid, cid, where);
1136         for (addr = cid->dc_addr; addr != NULL; addr = addr->da_next) {
1137                 if (avl_find(&mcip->mci_v6_dyn_ip, addr, &where) != NULL)
1138                         goto fail;
1139 
1140                 avl_insert(&mcip->mci_v6_dyn_ip, addr, where);
1141         }
1142         return (0);
1143 
1144 fail:
1145         remove_dhcpv6_cid(mcip, cid);
1146         return (EEXIST);
1147 }
1148 
1149 /*
1150  * Check whether an IP address is in the dyn-ip table.
1151  */
1152 static boolean_t
1153 check_dhcpv6_dyn_ip(mac_client_impl_t *mcip, in6_addr_t *addr)
1154 {
1155         dhcpv6_addr_t   tmp_addr, *a;
1156 
1157         mutex_enter(&mcip->mci_protect_lock);
1158         bcopy(addr, &tmp_addr.da_addr, sizeof (in6_addr_t));
1159         a = avl_find(&mcip->mci_v6_dyn_ip, &tmp_addr, NULL);
1160         mutex_exit(&mcip->mci_protect_lock);
1161         return (a != NULL);
1162 }
1163 
1164 static dhcpv6_txn_t *
1165 find_dhcpv6_pending_txn(mac_client_impl_t *mcip, uint32_t xid)
1166 {
1167         dhcpv6_txn_t    tmp_txn;
1168 
1169         ASSERT(MUTEX_HELD(&mcip->mci_protect_lock));
1170         tmp_txn.dt_xid = xid;
1171         return (avl_find(&mcip->mci_v6_pending_txn, &tmp_txn, NULL));
1172 }
1173 
1174 static void
1175 remove_dhcpv6_pending_txn(mac_client_impl_t *mcip, dhcpv6_txn_t *txn)
1176 {
1177         ASSERT(MUTEX_HELD(&mcip->mci_protect_lock));
1178         avl_remove(&mcip->mci_v6_pending_txn, txn);
1179 }
1180 
1181 static dhcpv6_txn_t *
1182 create_dhcpv6_txn(uint32_t xid, dhcpv6_cid_t *cid)
1183 {
1184         dhcpv6_txn_t    *txn;
1185 
1186         if ((txn = kmem_zalloc(sizeof (dhcpv6_txn_t), KM_NOSLEEP)) == NULL)
1187                 return (NULL);
1188 
1189         txn->dt_xid = xid;
1190         txn->dt_cid = cid;
1191         txn->dt_timestamp = gethrtime();
1192         return (txn);
1193 }
1194 
1195 static void
1196 free_dhcpv6_txn(dhcpv6_txn_t *txn)
1197 {
1198         if (txn->dt_cid != NULL)
1199                 free_dhcpv6_cid(txn->dt_cid);
1200         kmem_free(txn, sizeof (dhcpv6_txn_t));
1201 }
1202 
1203 static int
1204 insert_dhcpv6_pending_txn(mac_client_impl_t *mcip, dhcpv6_txn_t *txn)
1205 {
1206         avl_index_t     where;
1207 
1208         ASSERT(MUTEX_HELD(&mcip->mci_protect_lock));
1209         if (avl_find(&mcip->mci_v6_pending_txn, txn, &where) != NULL)
1210                 return (EEXIST);
1211 
1212         if (avl_numnodes(&mcip->mci_v6_pending_txn) >= dhcp_max_pending_txn) {
1213                 BUMP_STAT(mcip, dhcpdropped);
1214                 return (EAGAIN);
1215         }
1216         avl_insert(&mcip->mci_v6_pending_txn, txn, where);
1217         return (0);
1218 }
1219 
1220 /*
1221  * Clean up all v6 tables.
1222  */
1223 static void
1224 flush_dhcpv6(mac_client_impl_t *mcip)
1225 {
1226         void            *cookie = NULL;
1227         dhcpv6_cid_t    *cid;
1228         dhcpv6_txn_t    *txn;
1229 
1230         ASSERT(MUTEX_HELD(&mcip->mci_protect_lock));
1231         while (avl_destroy_nodes(&mcip->mci_v6_dyn_ip, &cookie) != NULL) {
1232         }
1233         cookie = NULL;
1234         while ((cid = avl_destroy_nodes(&mcip->mci_v6_cid, &cookie)) != NULL) {
1235                 free_dhcpv6_cid(cid);
1236         }
1237         cookie = NULL;
1238         while ((txn = avl_destroy_nodes(&mcip->mci_v6_pending_txn,
1239             &cookie)) != NULL) {
1240                 free_dhcpv6_txn(txn);
1241         }
1242 }
1243 
1244 void
1245 flush_slaac(mac_client_impl_t *mcip)
1246 {
1247         void            *cookie = NULL;
1248         slaac_addr_t    *addr = NULL;
1249 
1250         while ((addr = avl_destroy_nodes(&mcip->mci_v6_slaac_ip, &cookie)) !=
1251             NULL) {
1252                 kmem_free(addr, sizeof (slaac_addr_t));
1253         }
1254 }
1255 
1256 /*
1257  * Cleanup stale DHCPv6 transactions.
1258  */
1259 static void
1260 txn_cleanup_v6(mac_client_impl_t *mcip)
1261 {
1262         dhcpv6_txn_t            *txn, *next, *txn_list = NULL;
1263 
1264         /*
1265          * Find stale pending transactions and place them on a list
1266          * to be removed.
1267          */
1268         for (txn = avl_first(&mcip->mci_v6_pending_txn); txn != NULL;
1269             txn = avl_walk(&mcip->mci_v6_pending_txn, txn, AVL_AFTER)) {
1270                 if (gethrtime() - txn->dt_timestamp > txn_cleanup_interval) {
1271                         DTRACE_PROBE2(found__expired__txn,
1272                             mac_client_impl_t *, mcip,
1273                             dhcpv6_txn_t *, txn);
1274 
1275                         txn->dt_next = txn_list;
1276                         txn_list = txn;
1277                 }
1278         }
1279 
1280         /*
1281          * Remove and free stale pending transactions.
1282          * Release any existing cids matching the stale transactions.
1283          */
1284         for (txn = txn_list; txn != NULL; txn = next) {
1285                 avl_remove(&mcip->mci_v6_pending_txn, txn);
1286                 release_dhcpv6_cid(mcip, txn->dt_cid);
1287                 next = txn->dt_next;
1288                 txn->dt_next = NULL;
1289 
1290                 DTRACE_PROBE2(freeing__txn, mac_client_impl_t *, mcip,
1291                     dhcpv6_txn_t *, txn);
1292                 free_dhcpv6_txn(txn);
1293         }
1294 
1295 }
1296 
1297 /*
1298  * Core logic for intercepting outbound DHCPv6 packets.
1299  */
1300 static boolean_t
1301 intercept_dhcpv6_outbound(mac_client_impl_t *mcip, ip6_t *ip6h, uchar_t *end)
1302 {
1303         dhcpv6_message_t        *dh6;
1304         dhcpv6_txn_t            *txn;
1305         dhcpv6_cid_t            *cid = NULL;
1306         uint32_t                xid;
1307         uint8_t                 mtype;
1308         mac_resource_props_t *mrp = MCIP_RESOURCE_PROPS(mcip);
1309 
1310         if (get_dhcpv6_info(ip6h, end, &dh6) != 0)
1311                 return (B_TRUE);
1312 
1313         /* ip_nospoof/allowed-ips and DHCP are mutually exclusive by default */
1314         if (allowed_ips_set(mrp, IPV6_VERSION))
1315                 return (B_FALSE);
1316 
1317         /*
1318          * We want to act on packets that result in DHCPv6 Reply messages, or
1319          * on packets that give up an IPv6 address. For example, a Request or
1320          * Solicit (w/ the Rapid Commit option) will cause the server to send a
1321          * Reply, ending the transaction.
1322          */
1323         mtype = dh6->d6m_msg_type;
1324         if (mtype != DHCPV6_MSG_SOLICIT && mtype != DHCPV6_MSG_REQUEST &&
1325             mtype != DHCPV6_MSG_RENEW && mtype != DHCPV6_MSG_REBIND &&
1326             mtype != DHCPV6_MSG_RELEASE)
1327                 return (B_TRUE);
1328 
1329         if ((cid = create_dhcpv6_cid(dh6, end)) == NULL)
1330                 return (B_TRUE);
1331 
1332         mutex_enter(&mcip->mci_protect_lock);
1333         if (mtype == DHCPV6_MSG_RELEASE) {
1334                 release_dhcpv6_cid(mcip, cid);
1335                 goto done;
1336         }
1337         xid = DHCPV6_GET_TRANSID(dh6);
1338         if ((txn = find_dhcpv6_pending_txn(mcip, xid)) != NULL) {
1339                 DTRACE_PROBE2(update, mac_client_impl_t *, mcip,
1340                     dhcpv6_txn_t *, txn);
1341                 txn->dt_timestamp = gethrtime();
1342                 goto done;
1343         }
1344         if ((txn = create_dhcpv6_txn(xid, cid)) == NULL)
1345                 goto done;
1346 
1347         cid = NULL;
1348         if (insert_dhcpv6_pending_txn(mcip, txn) != 0) {
1349                 DTRACE_PROBE2(insert__failed, mac_client_impl_t *, mcip,
1350                     dhcpv6_txn_t *, txn);
1351                 free_dhcpv6_txn(txn);
1352                 goto done;
1353         }
1354         start_txn_cleanup_timer(mcip);
1355 
1356         DTRACE_PROBE2(txn__pending, mac_client_impl_t *, mcip,
1357             dhcpv6_txn_t *, txn);
1358 
1359 done:
1360         if (cid != NULL)
1361                 free_dhcpv6_cid(cid);
1362 
1363         mutex_exit(&mcip->mci_protect_lock);
1364         return (B_TRUE);
1365 }
1366 
1367 /*
1368  * Core logic for intercepting inbound DHCPv6 packets.
1369  */
1370 static void
1371 intercept_dhcpv6_inbound(mac_client_impl_t *mcip, uchar_t *end,
1372     dhcpv6_message_t *dh6)
1373 {
1374         dhcpv6_txn_t            *txn;
1375         uint32_t                xid;
1376         uint8_t                 mtype;
1377         uint16_t                status;
1378 
1379         mtype = dh6->d6m_msg_type;
1380         if (mtype != DHCPV6_MSG_REPLY)
1381                 return;
1382 
1383         mutex_enter(&mcip->mci_protect_lock);
1384         xid = DHCPV6_GET_TRANSID(dh6);
1385         if ((txn = find_dhcpv6_pending_txn(mcip, xid)) == NULL) {
1386                 DTRACE_PROBE2(txn__not__found, mac_client_impl_t *, mcip,
1387                     dhcpv6_message_t *, dh6);
1388                 goto done;
1389         }
1390         remove_dhcpv6_pending_txn(mcip, txn);
1391         release_dhcpv6_cid(mcip, txn->dt_cid);
1392 
1393         if (get_dhcpv6_status(dh6, end, &status) != 0 ||
1394             status != DHCPV6_STAT_SUCCESS) {
1395                 DTRACE_PROBE2(error__status, mac_client_impl_t *, mcip,
1396                     dhcpv6_txn_t *, txn);
1397                 goto done;
1398         }
1399         if (get_dhcpv6_addrs(dh6, end, txn->dt_cid) != 0) {
1400                 DTRACE_PROBE2(no__addrs, mac_client_impl_t *, mcip,
1401                     dhcpv6_txn_t *, txn);
1402                 goto done;
1403         }
1404         if (insert_dhcpv6_cid(mcip, txn->dt_cid) != 0) {
1405                 DTRACE_PROBE2(insert__failed, mac_client_impl_t *, mcip,
1406                     dhcpv6_txn_t *, txn);
1407                 goto done;
1408         }
1409         DTRACE_PROBE2(txn__completed, mac_client_impl_t *, mcip,
1410             dhcpv6_txn_t *, txn);
1411 
1412         txn->dt_cid = NULL;
1413 
1414 done:
1415         if (txn != NULL)
1416                 free_dhcpv6_txn(txn);
1417         mutex_exit(&mcip->mci_protect_lock);
1418 }
1419 
1420 /*
1421  * Check whether an IP address is in the SLAAC table.
1422  */
1423 static boolean_t
1424 check_slaac_ip(mac_client_impl_t *mcip, in6_addr_t *addr)
1425 {
1426         slaac_addr_t    tmp_addr, *a;
1427 
1428         mutex_enter(&mcip->mci_protect_lock);
1429         bcopy(addr, &tmp_addr.sla_addr, sizeof (in6_addr_t));
1430         a = avl_find(&mcip->mci_v6_slaac_ip, &tmp_addr, NULL);
1431         mutex_exit(&mcip->mci_protect_lock);
1432         return (a != NULL);
1433 }
1434 
1435 static boolean_t
1436 insert_slaac_ip(avl_tree_t *tree, in6_addr_t *token, slaac_addr_t *addr)
1437 {
1438         uint_t          i;
1439         avl_index_t     where;
1440         in6_addr_t      *prefix = &addr->sla_prefix;
1441         in6_addr_t      *in6p = &addr->sla_addr;
1442 
1443         bcopy(prefix, in6p, sizeof (struct in6_addr));
1444 
1445         for (i = 0; i < 4; i++) {
1446                 in6p->s6_addr32[i] = token->s6_addr32[i] |
1447                     in6p->s6_addr32[i];
1448         }
1449 
1450         DTRACE_PROBE1(generated__addr, in6_addr_t *, in6p);
1451 
1452         if (avl_find(tree, addr, &where) != NULL)
1453                 return (B_FALSE);
1454 
1455         avl_insert(tree, addr, where);
1456         return (B_TRUE);
1457 }
1458 
1459 static void
1460 insert_slaac_prefix(mac_client_impl_t *mcip, nd_opt_prefix_info_t *po)
1461 {
1462         slaac_addr_t    *addr = NULL;
1463         in6_addr_t      *token = &mcip->mci_v6_mac_token;
1464 
1465         ASSERT(MUTEX_HELD(&mcip->mci_protect_lock));
1466 
1467         if (avl_numnodes(&mcip->mci_v6_slaac_ip) >= slaac_max_allowed) {
1468                 DTRACE_PROBE(limit__reached);
1469                 return;
1470         }
1471 
1472         if ((addr = kmem_zalloc(sizeof (slaac_addr_t),
1473             KM_NOSLEEP | KM_NORMALPRI)) == NULL)
1474                 return;
1475 
1476         bcopy(&po->nd_opt_pi_prefix, &addr->sla_prefix,
1477             sizeof (struct in6_addr));
1478 
1479         if (!insert_slaac_ip(&mcip->mci_v6_slaac_ip, token, addr)) {
1480                 kmem_free(addr, sizeof (slaac_addr_t));
1481         }
1482 }
1483 
1484 static void
1485 intercept_prefix_info(mac_client_impl_t *mcip, nd_opt_prefix_info_t *po)
1486 {
1487         if (8 * po->nd_opt_pi_len != sizeof (nd_opt_prefix_info_t)) {
1488                 DTRACE_PROBE(invalid__length);
1489                 return;
1490         }
1491 
1492         if (po->nd_opt_pi_prefix_len > 128) {
1493                 DTRACE_PROBE(invalid__plen);
1494                 return;
1495         }
1496 
1497         if (IN6_IS_ADDR_LINKLOCAL(&po->nd_opt_pi_prefix)) {
1498                 DTRACE_PROBE(link__local);
1499                 return;
1500         }
1501 
1502         if ((po->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_AUTO) == 0)
1503                 return;
1504 
1505         mutex_enter(&mcip->mci_protect_lock);
1506         insert_slaac_prefix(mcip, po);
1507         mutex_exit(&mcip->mci_protect_lock);
1508 }
1509 
1510 /*
1511  * If we receive a Router Advertisement carrying prefix information and
1512  * indicating that SLAAC should be performed, then track the prefix.
1513  */
1514 static void
1515 intercept_ra_inbound(mac_client_impl_t *mcip, ip6_t *ip6h, uchar_t *end,
1516     nd_router_advert_t *ra)
1517 {
1518         struct nd_opt_hdr *opt;
1519         int len, optlen;
1520 
1521         if (ip6h->ip6_hlim != 255) {
1522                 DTRACE_PROBE1(invalid__hoplimit, uint8_t, ip6h->ip6_hlim);
1523                 return;
1524         }
1525 
1526         len = ip6h->ip6_plen - sizeof (nd_router_advert_t);
1527         opt = (struct nd_opt_hdr *)&ra[1];
1528         while (len >= sizeof (struct nd_opt_hdr) &&
1529             ((uchar_t *)opt + sizeof (struct nd_opt_hdr)) <= end) {
1530                 optlen = opt->nd_opt_len * 8;
1531 
1532                 if (optlen < sizeof (struct nd_opt_hdr) ||
1533                     ((uchar_t *)opt + optlen) > end) {
1534                         DTRACE_PROBE(invalid__length);
1535                         return;
1536                 }
1537 
1538                 if (opt->nd_opt_type == ND_OPT_PREFIX_INFORMATION) {
1539                         intercept_prefix_info(mcip,
1540                             (nd_opt_prefix_info_t *)opt);
1541                 }
1542 
1543                 opt = (struct nd_opt_hdr *)((char *)opt + optlen);
1544                 len -= optlen;
1545         }
1546 }
1547 
1548 /*
1549  * Timer for cleaning up stale transactions.
1550  */
1551 static void
1552 txn_cleanup_timer(void *arg)
1553 {
1554         mac_client_impl_t       *mcip = arg;
1555 
1556         mutex_enter(&mcip->mci_protect_lock);
1557         if (mcip->mci_txn_cleanup_tid == 0) {
1558                 /* do nothing if timer got cancelled */
1559                 mutex_exit(&mcip->mci_protect_lock);
1560                 return;
1561         }
1562         mcip->mci_txn_cleanup_tid = 0;
1563 
1564         txn_cleanup_v4(mcip);
1565         txn_cleanup_v6(mcip);
1566 
1567         /*
1568          * Restart timer if pending transactions still exist.
1569          */
1570         if (!avl_is_empty(&mcip->mci_v4_pending_txn) ||
1571             !avl_is_empty(&mcip->mci_v6_pending_txn)) {
1572                 DTRACE_PROBE1(restarting__timer, mac_client_impl_t *, mcip);
1573 
1574                 mcip->mci_txn_cleanup_tid = timeout(txn_cleanup_timer, mcip,
1575                     drv_usectohz(txn_cleanup_interval / (NANOSEC / MICROSEC)));
1576         }
1577         mutex_exit(&mcip->mci_protect_lock);
1578 }
1579 
1580 static void
1581 start_txn_cleanup_timer(mac_client_impl_t *mcip)
1582 {
1583         ASSERT(MUTEX_HELD(&mcip->mci_protect_lock));
1584         if (mcip->mci_txn_cleanup_tid == 0) {
1585                 mcip->mci_txn_cleanup_tid = timeout(txn_cleanup_timer, mcip,
1586                     drv_usectohz(txn_cleanup_interval / (NANOSEC / MICROSEC)));
1587         }
1588 }
1589 
1590 static void
1591 cancel_txn_cleanup_timer(mac_client_impl_t *mcip)
1592 {
1593         timeout_id_t    tid;
1594 
1595         ASSERT(MUTEX_HELD(&mcip->mci_protect_lock));
1596 
1597         /*
1598          * This needs to be a while loop because the timer could get
1599          * rearmed during untimeout().
1600          */
1601         while ((tid = mcip->mci_txn_cleanup_tid) != 0) {
1602                 mcip->mci_txn_cleanup_tid = 0;
1603                 mutex_exit(&mcip->mci_protect_lock);
1604                 (void) untimeout(tid);
1605                 mutex_enter(&mcip->mci_protect_lock);
1606         }
1607 }
1608 
1609 /*
1610  * Get the start/end pointers of an L3 packet and also do pullup if needed.
1611  * pulled-up packet needs to be freed by the caller.
1612  */
1613 static int
1614 get_l3_info(mblk_t *mp, size_t hdrsize, uchar_t **start, uchar_t **end,
1615     mblk_t **nmp)
1616 {
1617         uchar_t *s, *e;
1618         mblk_t  *newmp = NULL;
1619 
1620         /*
1621          * Pullup if necessary but reject packets that do not have
1622          * a proper mac header.
1623          */
1624         s = mp->b_rptr + hdrsize;
1625         e = mp->b_wptr;
1626 
1627         if (s > mp->b_wptr)
1628                 return (EINVAL);
1629 
1630         if (!OK_32PTR(s) || mp->b_cont != NULL) {
1631                 /*
1632                  * Temporarily adjust mp->b_rptr to ensure proper
1633                  * alignment of IP header in newmp.
1634                  */
1635                 DTRACE_PROBE1(pullup__needed, mblk_t *, mp);
1636 
1637                 mp->b_rptr += hdrsize;
1638                 newmp = msgpullup(mp, -1);
1639                 mp->b_rptr -= hdrsize;
1640 
1641                 if (newmp == NULL)
1642                         return (ENOMEM);
1643 
1644                 s = newmp->b_rptr;
1645                 e = newmp->b_wptr;
1646         }
1647 
1648         *start = s;
1649         *end = e;
1650         *nmp = newmp;
1651         return (0);
1652 }
1653 
1654 void
1655 mac_protect_intercept_dynamic_one(mac_client_impl_t *mcip, mblk_t *mp)
1656 {
1657         mac_impl_t              *mip = mcip->mci_mip;
1658         uchar_t                 *start, *end;
1659         mblk_t                  *nmp = NULL;
1660         mac_header_info_t       mhi;
1661         int                     err;
1662 
1663         err = mac_vlan_header_info((mac_handle_t)mip, mp, &mhi);
1664         if (err != 0) {
1665                 DTRACE_PROBE2(invalid__header, mac_client_impl_t *, mcip,
1666                     mblk_t *, mp);
1667                 return;
1668         }
1669 
1670         err = get_l3_info(mp, mhi.mhi_hdrsize, &start, &end, &nmp);
1671         if (err != 0) {
1672                 DTRACE_PROBE2(invalid__l3, mac_client_impl_t *, mcip,
1673                     mblk_t *, mp);
1674                 return;
1675         }
1676 
1677         switch (mhi.mhi_bindsap) {
1678         case ETHERTYPE_IP: {
1679                 struct dhcp     *dh4;
1680                 ipha_t          *ipha = (ipha_t *)start;
1681 
1682                 if (start + sizeof (ipha_t) > end)
1683                         return;
1684 
1685                 if (get_dhcpv4_info(ipha, end, &dh4) == 0) {
1686                         intercept_dhcpv4_inbound(mcip, end, dh4);
1687                 }
1688                 break;
1689         }
1690         case ETHERTYPE_IPV6: {
1691                 dhcpv6_message_t        *dh6;
1692                 nd_router_advert_t      *ra;
1693                 ip6_t                   *ip6h = (ip6_t *)start;
1694 
1695                 if (start + sizeof (ip6_t) > end)
1696                         return;
1697 
1698                 if (get_dhcpv6_info(ip6h, end, &dh6) == 0) {
1699                         intercept_dhcpv6_inbound(mcip, end, dh6);
1700                 } else if (get_ra_info(ip6h, end, &ra) == 0) {
1701                         intercept_ra_inbound(mcip, ip6h, end, ra);
1702                 }
1703 
1704                 break;
1705         }
1706         }
1707         freemsg(nmp);
1708 }
1709 
1710 void
1711 mac_protect_intercept_dynamic(mac_client_impl_t *mcip, mblk_t *mp)
1712 {
1713         /*
1714          * Skip checks if we are part of an aggr.
1715          */
1716         if ((mcip->mci_state_flags & MCIS_IS_AGGR_PORT) != 0)
1717                 return;
1718 
1719         for (; mp != NULL; mp = mp->b_next)
1720                 mac_protect_intercept_dynamic_one(mcip, mp);
1721 }
1722 
1723 void
1724 mac_protect_flush_dynamic(mac_client_impl_t *mcip)
1725 {
1726         mutex_enter(&mcip->mci_protect_lock);
1727         flush_dhcpv4(mcip);
1728         flush_dhcpv6(mcip);
1729         flush_slaac(mcip);
1730         mutex_exit(&mcip->mci_protect_lock);
1731 }
1732 
1733 void
1734 mac_protect_cancel_timer(mac_client_impl_t *mcip)
1735 {
1736         mutex_enter(&mcip->mci_protect_lock);
1737         cancel_txn_cleanup_timer(mcip);
1738         mutex_exit(&mcip->mci_protect_lock);
1739 }
1740 
1741 /*
1742  * Check if addr is in the 'allowed-ips' list.
1743  */
1744 
1745 /* ARGSUSED */
1746 static boolean_t
1747 ipnospoof_check_v4(mac_client_impl_t *mcip, mac_protect_t *protect,
1748     ipaddr_t *addr)
1749 {
1750         uint_t  i;
1751 
1752         /*
1753          * The unspecified address is allowed.
1754          */
1755         if (*addr == INADDR_ANY)
1756                 return (B_TRUE);
1757 
1758         for (i = 0; i < protect->mp_ipaddrcnt; i++) {
1759                 mac_ipaddr_t    *v4addr = &protect->mp_ipaddrs[i];
1760 
1761                 if (v4addr->ip_version == IPV4_VERSION) {
1762                         uint32_t mask;
1763 
1764                         /* LINTED E_SUSPICIOUS_COMPARISON */
1765                         ASSERT(v4addr->ip_netmask >= 0 &&
1766                             v4addr->ip_netmask <= 32);
1767                         mask = 0xFFFFFFFFu << (32 - v4addr->ip_netmask);
1768                         /*
1769                          * Since we have a netmask we know this entry
1770                          * signifies the entire subnet. Check if the
1771                          * given address is on the subnet.
1772                          */
1773                         if (htonl(V4_PART_OF_V6(v4addr->ip_addr)) ==
1774                             (htonl(*addr) & mask))
1775                                 return (B_TRUE);
1776                 }
1777         }
1778         return (protect->mp_ipaddrcnt == 0 ?
1779             check_dhcpv4_dyn_ip(mcip, *addr) : B_FALSE);
1780 }
1781 
1782 static boolean_t
1783 ipnospoof_check_v6(mac_client_impl_t *mcip, mac_protect_t *protect,
1784     in6_addr_t *addr)
1785 {
1786         uint_t  i;
1787 
1788         /*
1789          * The unspecified address and the v6 link local address are allowed.
1790          */
1791         if (IN6_IS_ADDR_UNSPECIFIED(addr) ||
1792             ((mcip->mci_protect_flags & MPT_FLAG_V6_LOCAL_ADDR_SET) != 0 &&
1793             IN6_ARE_ADDR_EQUAL(&mcip->mci_v6_local_addr, addr)))
1794                 return (B_TRUE);
1795 
1796 
1797         for (i = 0; i < protect->mp_ipaddrcnt; i++) {
1798                 mac_ipaddr_t    *v6addr = &protect->mp_ipaddrs[i];
1799 
1800                 if (v6addr->ip_version == IPV6_VERSION &&
1801                     /* LINTED E_SUSPICIOUS_COMPARISON */
1802                     IN6_ARE_PREFIXEDADDR_EQUAL(&v6addr->ip_addr, addr,
1803                     v6addr->ip_netmask))
1804                         return (B_TRUE);
1805         }
1806 
1807         if (protect->mp_ipaddrcnt == 0) {
1808                 return (check_slaac_ip(mcip, addr) ||
1809                     check_dhcpv6_dyn_ip(mcip, addr));
1810         } else {
1811                 return (B_FALSE);
1812         }
1813 }
1814 
1815 /*
1816  * Checks various fields within an IPv6 NDP packet.
1817  */
1818 static boolean_t
1819 ipnospoof_check_ndp(mac_client_impl_t *mcip, mac_protect_t *protect,
1820     ip6_t *ip6h, uchar_t *end)
1821 {
1822         icmp6_t                 *icmp_nd = (icmp6_t *)&ip6h[1];
1823         int                     hdrlen, optlen, opttype, len;
1824         uint_t                  addrlen, maclen;
1825         uint8_t                 type;
1826         nd_opt_hdr_t            *opt;
1827         struct nd_opt_lla       *lla = NULL;
1828 
1829         /*
1830          * NDP packets do not have extension headers so the ICMPv6 header
1831          * must immediately follow the IPv6 header.
1832          */
1833         if (ip6h->ip6_nxt != IPPROTO_ICMPV6)
1834                 return (B_TRUE);
1835 
1836         /* ICMPv6 header missing */
1837         if ((uchar_t *)&icmp_nd[1] > end)
1838                 return (B_FALSE);
1839 
1840         len = end - (uchar_t *)icmp_nd;
1841         type = icmp_nd->icmp6_type;
1842 
1843         switch (type) {
1844         case ND_ROUTER_SOLICIT:
1845                 hdrlen = sizeof (nd_router_solicit_t);
1846                 break;
1847         case ND_ROUTER_ADVERT:
1848                 hdrlen = sizeof (nd_router_advert_t);
1849                 break;
1850         case ND_NEIGHBOR_SOLICIT:
1851                 hdrlen = sizeof (nd_neighbor_solicit_t);
1852                 break;
1853         case ND_NEIGHBOR_ADVERT:
1854                 hdrlen = sizeof (nd_neighbor_advert_t);
1855                 break;
1856         case ND_REDIRECT:
1857                 hdrlen = sizeof (nd_redirect_t);
1858                 break;
1859         default:
1860                 return (B_TRUE);
1861         }
1862 
1863         if (len < hdrlen)
1864                 return (B_FALSE);
1865 
1866         /* SLLA option checking is needed for RS/RA/NS */
1867         opttype = ND_OPT_SOURCE_LINKADDR;
1868 
1869         switch (type) {
1870         case ND_NEIGHBOR_ADVERT: {
1871                 nd_neighbor_advert_t    *na = (nd_neighbor_advert_t *)icmp_nd;
1872 
1873                 if (!ipnospoof_check_v6(mcip, protect, &na->nd_na_target)) {
1874                         DTRACE_PROBE2(ndp__na__fail,
1875                             mac_client_impl_t *, mcip, ip6_t *, ip6h);
1876                         return (B_FALSE);
1877                 }
1878 
1879                 /* TLLA option for NA */
1880                 opttype = ND_OPT_TARGET_LINKADDR;
1881                 break;
1882         }
1883         case ND_REDIRECT: {
1884                 /* option checking not needed for RD */
1885                 return (B_TRUE);
1886         }
1887         default:
1888                 break;
1889         }
1890 
1891         if (len == hdrlen) {
1892                 /* no options, we're done */
1893                 return (B_TRUE);
1894         }
1895         opt = (nd_opt_hdr_t *)((uchar_t *)icmp_nd + hdrlen);
1896         optlen = len - hdrlen;
1897 
1898         /* find the option header we need */
1899         while (optlen > sizeof (nd_opt_hdr_t)) {
1900                 if (opt->nd_opt_type == opttype) {
1901                         lla = (struct nd_opt_lla *)opt;
1902                         break;
1903                 }
1904                 optlen -= 8 * opt->nd_opt_len;
1905                 opt = (nd_opt_hdr_t *)
1906                     ((uchar_t *)opt + 8 * opt->nd_opt_len);
1907         }
1908         if (lla == NULL)
1909                 return (B_TRUE);
1910 
1911         addrlen = lla->nd_opt_lla_len * 8 - sizeof (nd_opt_hdr_t);
1912         maclen = mcip->mci_mip->mi_info.mi_addr_length;
1913 
1914         if (addrlen != maclen ||
1915             bcmp(mcip->mci_unicast->ma_addr,
1916             lla->nd_opt_lla_hdw_addr, maclen) != 0) {
1917                 DTRACE_PROBE2(ndp__lla__fail,
1918                     mac_client_impl_t *, mcip, ip6_t *, ip6h);
1919                 return (B_FALSE);
1920         }
1921 
1922         DTRACE_PROBE2(ndp__lla__ok, mac_client_impl_t *, mcip, ip6_t *, ip6h);
1923         return (B_TRUE);
1924 }
1925 
1926 /*
1927  * Enforce ip-nospoof protection.
1928  */
1929 static int
1930 ipnospoof_check(mac_client_impl_t *mcip, mac_protect_t *protect,
1931     mblk_t *mp, mac_header_info_t *mhip)
1932 {
1933         size_t          hdrsize = mhip->mhi_hdrsize;
1934         uint32_t        sap = mhip->mhi_bindsap;
1935         uchar_t         *start, *end;
1936         mblk_t          *nmp = NULL;
1937         int             err;
1938 
1939         err = get_l3_info(mp, hdrsize, &start, &end, &nmp);
1940         if (err != 0) {
1941                 DTRACE_PROBE2(invalid__l3, mac_client_impl_t *, mcip,
1942                     mblk_t *, mp);
1943                 return (err);
1944         }
1945         err = EINVAL;
1946 
1947         switch (sap) {
1948         case ETHERTYPE_IP: {
1949                 ipha_t  *ipha = (ipha_t *)start;
1950 
1951                 if (start + sizeof (ipha_t) > end)
1952                         goto fail;
1953 
1954                 if (!ipnospoof_check_v4(mcip, protect, &ipha->ipha_src))
1955                         goto fail;
1956 
1957                 if (!intercept_dhcpv4_outbound(mcip, ipha, end))
1958                         goto fail;
1959                 break;
1960         }
1961         case ETHERTYPE_ARP: {
1962                 arh_t           *arh = (arh_t *)start;
1963                 uint32_t        maclen, hlen, plen, arplen;
1964                 ipaddr_t        spaddr;
1965                 uchar_t         *shaddr;
1966 
1967                 if (start + sizeof (arh_t) > end)
1968                         goto fail;
1969 
1970                 maclen = mcip->mci_mip->mi_info.mi_addr_length;
1971                 hlen = arh->arh_hlen;
1972                 plen = arh->arh_plen;
1973                 if ((hlen != 0 && hlen != maclen) ||
1974                     plen != sizeof (ipaddr_t))
1975                         goto fail;
1976 
1977                 arplen = sizeof (arh_t) + 2 * hlen + 2 * plen;
1978                 if (start + arplen > end)
1979                         goto fail;
1980 
1981                 shaddr = start + sizeof (arh_t);
1982                 if (hlen != 0 &&
1983                     bcmp(mcip->mci_unicast->ma_addr, shaddr, maclen) != 0)
1984                         goto fail;
1985 
1986                 bcopy(shaddr + hlen, &spaddr, sizeof (spaddr));
1987                 if (!ipnospoof_check_v4(mcip, protect, &spaddr))
1988                         goto fail;
1989                 break;
1990         }
1991         case ETHERTYPE_IPV6: {
1992                 ip6_t           *ip6h = (ip6_t *)start;
1993 
1994                 if (start + sizeof (ip6_t) > end)
1995                         goto fail;
1996 
1997                 if (!ipnospoof_check_v6(mcip, protect, &ip6h->ip6_src))
1998                         goto fail;
1999 
2000                 if (!ipnospoof_check_ndp(mcip, protect, ip6h, end))
2001                         goto fail;
2002 
2003                 if (!intercept_dhcpv6_outbound(mcip, ip6h, end))
2004                         goto fail;
2005                 break;
2006         }
2007         }
2008         freemsg(nmp);
2009         return (0);
2010 
2011 fail:
2012         freemsg(nmp);
2013         return (err);
2014 }
2015 
2016 static boolean_t
2017 dhcpnospoof_check_cid(mac_protect_t *p, uchar_t *cid, uint_t cidlen)
2018 {
2019         int     i;
2020 
2021         for (i = 0; i < p->mp_cidcnt; i++) {
2022                 mac_dhcpcid_t   *dcid = &p->mp_cids[i];
2023 
2024                 if (dcid->dc_len == cidlen &&
2025                     bcmp(dcid->dc_id, cid, cidlen) == 0)
2026                         return (B_TRUE);
2027         }
2028         return (B_FALSE);
2029 }
2030 
2031 static boolean_t
2032 dhcpnospoof_check_v4(mac_client_impl_t *mcip, mac_protect_t *p,
2033     ipha_t *ipha, uchar_t *end)
2034 {
2035         struct dhcp     *dh4;
2036         uchar_t         *cid;
2037         uint_t          maclen, cidlen = 0;
2038         uint8_t         optlen;
2039         int             err;
2040 
2041         if ((err = get_dhcpv4_info(ipha, end, &dh4)) != 0)
2042                 return (err == EINVAL);
2043 
2044         maclen = mcip->mci_mip->mi_info.mi_addr_length;
2045         if (dh4->hlen == maclen &&
2046             bcmp(mcip->mci_unicast->ma_addr, dh4->chaddr, maclen) != 0) {
2047                 return (B_FALSE);
2048         }
2049         if (get_dhcpv4_option(dh4, end, CD_CLIENT_ID, &cid, &optlen) == 0)
2050                 cidlen = optlen;
2051 
2052         if (cidlen == 0)
2053                 return (B_TRUE);
2054 
2055         if (*cid == ARPHRD_ETHER && cidlen - 1 == maclen &&
2056             bcmp(mcip->mci_unicast->ma_addr, cid + 1, maclen) == 0)
2057                 return (B_TRUE);
2058 
2059         return (dhcpnospoof_check_cid(p, cid, cidlen));
2060 }
2061 
2062 static boolean_t
2063 dhcpnospoof_check_v6(mac_client_impl_t *mcip, mac_protect_t *p,
2064     ip6_t *ip6h, uchar_t *end)
2065 {
2066         dhcpv6_message_t        *dh6;
2067         dhcpv6_option_t         *d6o;
2068         uint8_t                 mtype;
2069         uchar_t                 *cid, *lladdr = NULL;
2070         uint_t                  cidlen, maclen, addrlen = 0;
2071         uint16_t                cidtype;
2072         int                     err;
2073 
2074         if ((err = get_dhcpv6_info(ip6h, end, &dh6)) != 0)
2075                 return (err == EINVAL);
2076 
2077         /*
2078          * We only check client-generated messages.
2079          */
2080         mtype = dh6->d6m_msg_type;
2081         if (mtype == DHCPV6_MSG_ADVERTISE || mtype == DHCPV6_MSG_REPLY ||
2082             mtype == DHCPV6_MSG_RECONFIGURE)
2083                 return (B_TRUE);
2084 
2085         d6o = get_dhcpv6_option(&dh6[1], end - (uchar_t *)&dh6[1], NULL,
2086             DHCPV6_OPT_CLIENTID, &cidlen);
2087         if (d6o == NULL || (uchar_t *)d6o + cidlen > end)
2088                 return (B_TRUE);
2089 
2090         cid = (uchar_t *)&d6o[1];
2091         cidlen -= sizeof (*d6o);
2092         if (cidlen < sizeof (cidtype))
2093                 return (B_TRUE);
2094 
2095         bcopy(cid, &cidtype, sizeof (cidtype));
2096         cidtype = ntohs(cidtype);
2097         if (cidtype == DHCPV6_DUID_LLT && cidlen >= sizeof (duid_llt_t)) {
2098                 lladdr = cid + sizeof (duid_llt_t);
2099                 addrlen = cidlen - sizeof (duid_llt_t);
2100         }
2101         if (cidtype == DHCPV6_DUID_LL && cidlen >= sizeof (duid_ll_t)) {
2102                 lladdr = cid + sizeof (duid_ll_t);
2103                 addrlen = cidlen - sizeof (duid_ll_t);
2104         }
2105         maclen = mcip->mci_mip->mi_info.mi_addr_length;
2106         if (lladdr != NULL && addrlen == maclen &&
2107             bcmp(mcip->mci_unicast->ma_addr, lladdr, maclen) == 0) {
2108                 return (B_TRUE);
2109         }
2110         return (dhcpnospoof_check_cid(p, cid, cidlen));
2111 }
2112 
2113 /*
2114  * Enforce dhcp-nospoof protection.
2115  */
2116 static int
2117 dhcpnospoof_check(mac_client_impl_t *mcip, mac_protect_t *protect,
2118     mblk_t *mp, mac_header_info_t *mhip)
2119 {
2120         size_t          hdrsize = mhip->mhi_hdrsize;
2121         uint32_t        sap = mhip->mhi_bindsap;
2122         uchar_t         *start, *end;
2123         mblk_t          *nmp = NULL;
2124         int             err;
2125 
2126         err = get_l3_info(mp, hdrsize, &start, &end, &nmp);
2127         if (err != 0) {
2128                 DTRACE_PROBE2(invalid__l3, mac_client_impl_t *, mcip,
2129                     mblk_t *, mp);
2130                 return (err);
2131         }
2132         err = EINVAL;
2133 
2134         switch (sap) {
2135         case ETHERTYPE_IP: {
2136                 ipha_t  *ipha = (ipha_t *)start;
2137 
2138                 if (start + sizeof (ipha_t) > end)
2139                         goto fail;
2140 
2141                 if (!dhcpnospoof_check_v4(mcip, protect, ipha, end))
2142                         goto fail;
2143 
2144                 break;
2145         }
2146         case ETHERTYPE_IPV6: {
2147                 ip6_t           *ip6h = (ip6_t *)start;
2148 
2149                 if (start + sizeof (ip6_t) > end)
2150                         goto fail;
2151 
2152                 if (!dhcpnospoof_check_v6(mcip, protect, ip6h, end))
2153                         goto fail;
2154 
2155                 break;
2156         }
2157         }
2158         freemsg(nmp);
2159         return (0);
2160 
2161 fail:
2162         /* increment dhcpnospoof stat here */
2163         freemsg(nmp);
2164         return (err);
2165 }
2166 
2167 /*
2168  * This is called whenever the mac client's mac address changes, to make sure
2169  * we allow use of the new link-local address.
2170  */
2171 static void
2172 mac_protect_update_v6_local_addr(mac_client_impl_t *mcip)
2173 {
2174         uint_t          i;
2175         in6_addr_t      *token = &mcip->mci_v6_mac_token;
2176         in6_addr_t      *v6addr = &mcip->mci_v6_local_addr;
2177         in6_addr_t      ll_template = {(uint32_t)V6_LINKLOCAL, 0x0, 0x0, 0x0};
2178 
2179         for (i = 0; i < 4; i++) {
2180                 v6addr->s6_addr32[i] = token->s6_addr32[i] |
2181                     ll_template.s6_addr32[i];
2182         }
2183         mcip->mci_protect_flags |= MPT_FLAG_V6_LOCAL_ADDR_SET;
2184 }
2185 
2186 /*
2187  * This is called whenever the mac client's mac address changes, to make sure
2188  * that any existing addresses gained via SLAAC are appropriately updated.
2189  */
2190 static void
2191 mac_protect_update_v6_slaac_addr(mac_client_impl_t *mcip)
2192 {
2193         void            *cookie = NULL;
2194         avl_tree_t      temp_tree;
2195         avl_tree_t      *ttp = &temp_tree, *sip = &mcip->mci_v6_slaac_ip;
2196         in6_addr_t      *token = &mcip->mci_v6_mac_token;
2197         slaac_addr_t    *addr = NULL;
2198 
2199         avl_create(ttp, compare_slaac_ip, sizeof (slaac_addr_t),
2200             offsetof(slaac_addr_t, sla_node));
2201 
2202         /* Copy everything over to the temporary tree, and fix the IP address */
2203         while ((addr = avl_destroy_nodes(sip, &cookie)) != NULL) {
2204                 VERIFY(insert_slaac_ip(ttp, token, addr) == B_TRUE);
2205         }
2206 
2207         /*
2208          * Now that the tempory tree has all of the modified addresses, we can
2209          * swap them over to the original tree once it's reset.
2210          */
2211         avl_destroy(sip);
2212         avl_create(sip, compare_slaac_ip, sizeof (slaac_addr_t),
2213             offsetof(slaac_addr_t, sla_node));
2214         avl_swap(ttp, sip);
2215 }
2216 
2217 /*
2218  * After the unicast MAC address changes, we need to update the derived token,
2219  * and update the IPv6 addresses that use the token.
2220  */
2221 void
2222 mac_protect_update_mac_token(mac_client_impl_t *mcip)
2223 {
2224         uint_t          media = mcip->mci_mip->mi_info.mi_media;
2225         uint8_t         *p, *macaddr = mcip->mci_unicast->ma_addr;
2226         in6_addr_t      *token = &mcip->mci_v6_mac_token;
2227 
2228         bzero(token, sizeof (in6_addr_t));
2229         p = (uint8_t *)&token->s6_addr32[2];
2230 
2231         switch (media) {
2232         case DL_ETHER:
2233                 bcopy(macaddr, p, 3);
2234                 p[0] ^= 0x2;
2235                 p[3] = 0xff;
2236                 p[4] = 0xfe;
2237                 bcopy(macaddr + 3, p + 5, 3);
2238                 break;
2239         case DL_IB:
2240                 ASSERT(mcip->mci_mip->mi_info.mi_addr_length == 20);
2241                 bcopy(macaddr + 12, p, 8);
2242                 p[0] |= 2;
2243                 break;
2244         default:
2245                 /*
2246                  * We do not need to generate the local address for link types
2247                  * that do not support link protection. Wifi pretends to be
2248                  * Ethernet so it is covered by the DL_ETHER case (note the
2249                  * use of mi_media instead of mi_nativemedia).
2250                  */
2251                 return;
2252         }
2253 
2254         mac_protect_update_v6_local_addr(mcip);
2255         mac_protect_update_v6_slaac_addr(mcip);
2256 }
2257 
2258 
2259 
2260 /*
2261  * Enforce link protection on one packet.
2262  */
2263 static int
2264 mac_protect_check_one(mac_client_impl_t *mcip, mblk_t *mp)
2265 {
2266         mac_impl_t              *mip = mcip->mci_mip;
2267         mac_resource_props_t    *mrp = MCIP_RESOURCE_PROPS(mcip);
2268         mac_protect_t           *protect;
2269         mac_header_info_t       mhi;
2270         uint32_t                types;
2271         int                     err;
2272 
2273         ASSERT(mp->b_next == NULL);
2274         ASSERT(mrp != NULL);
2275 
2276         err = mac_vlan_header_info((mac_handle_t)mip, mp, &mhi);
2277         if (err != 0) {
2278                 DTRACE_PROBE2(invalid__header, mac_client_impl_t *, mcip,
2279                     mblk_t *, mp);
2280                 return (err);
2281         }
2282         protect = &mrp->mrp_protect;
2283         types = protect->mp_types;
2284 
2285         if ((types & MPT_MACNOSPOOF) != 0) {
2286                 if (mhi.mhi_saddr != NULL &&
2287                     bcmp(mcip->mci_unicast->ma_addr, mhi.mhi_saddr,
2288                     mip->mi_info.mi_addr_length) != 0) {
2289                         BUMP_STAT(mcip, macspoofed);
2290                         DTRACE_PROBE2(mac__nospoof__fail,
2291                             mac_client_impl_t *, mcip, mblk_t *, mp);
2292                         return (EINVAL);
2293                 }
2294         }
2295         if ((types & MPT_RESTRICTED) != 0) {
2296                 uint32_t        vid = VLAN_ID(mhi.mhi_tci);
2297                 uint32_t        sap = mhi.mhi_bindsap;
2298 
2299                 /*
2300                  * ETHERTYPE_VLAN packets are allowed through, provided that
2301                  * the vid is not spoofed.
2302                  */
2303                 if (vid != 0 && !mac_client_check_flow_vid(mcip, vid)) {
2304                         BUMP_STAT(mcip, restricted);
2305                         DTRACE_PROBE2(restricted__vid__invalid,
2306                             mac_client_impl_t *, mcip, mblk_t *, mp);
2307                         return (EINVAL);
2308                 }
2309 
2310                 if (sap != ETHERTYPE_IP && sap != ETHERTYPE_IPV6 &&
2311                     sap != ETHERTYPE_ARP) {
2312                         BUMP_STAT(mcip, restricted);
2313                         DTRACE_PROBE2(restricted__fail,
2314                             mac_client_impl_t *, mcip, mblk_t *, mp);
2315                         return (EINVAL);
2316                 }
2317         }
2318         if ((types & MPT_IPNOSPOOF) != 0) {
2319                 if ((err = ipnospoof_check(mcip, protect, mp, &mhi)) != 0) {
2320                         BUMP_STAT(mcip, ipspoofed);
2321                         DTRACE_PROBE2(ip__nospoof__fail,
2322                             mac_client_impl_t *, mcip, mblk_t *, mp);
2323                         return (err);
2324                 }
2325         }
2326         if ((types & MPT_DHCPNOSPOOF) != 0) {
2327                 if ((err = dhcpnospoof_check(mcip, protect, mp, &mhi)) != 0) {
2328                         BUMP_STAT(mcip, dhcpspoofed);
2329                         DTRACE_PROBE2(dhcp__nospoof__fail,
2330                             mac_client_impl_t *, mcip, mblk_t *, mp);
2331                         return (err);
2332                 }
2333         }
2334         return (0);
2335 }
2336 
2337 /*
2338  * Enforce link protection on a packet chain.
2339  * Packets that pass the checks are returned back to the caller.
2340  */
2341 mblk_t *
2342 mac_protect_check(mac_client_handle_t mch, mblk_t *mp)
2343 {
2344         mac_client_impl_t       *mcip = (mac_client_impl_t *)mch;
2345         mblk_t                  *ret_mp = NULL, **tailp = &ret_mp, *next;
2346 
2347         /*
2348          * Skip checks if we are part of an aggr.
2349          */
2350         if ((mcip->mci_state_flags & MCIS_IS_AGGR_PORT) != 0)
2351                 return (mp);
2352 
2353         for (; mp != NULL; mp = next) {
2354                 next = mp->b_next;
2355                 mp->b_next = NULL;
2356 
2357                 if (mac_protect_check_one(mcip, mp) == 0) {
2358                         *tailp = mp;
2359                         tailp = &mp->b_next;
2360                 } else {
2361                         freemsg(mp);
2362                 }
2363         }
2364         return (ret_mp);
2365 }
2366 
2367 /*
2368  * Check if a particular protection type is enabled.
2369  */
2370 boolean_t
2371 mac_protect_enabled(mac_client_handle_t mch, uint32_t type)
2372 {
2373         return (MAC_PROTECT_ENABLED((mac_client_impl_t *)mch, type));
2374 }
2375 
2376 static int
2377 validate_ips(mac_protect_t *p)
2378 {
2379         uint_t          i, j;
2380 
2381         if (p->mp_ipaddrcnt == MPT_RESET)
2382                 return (0);
2383 
2384         if (p->mp_ipaddrcnt > MPT_MAXIPADDR)
2385                 return (EINVAL);
2386 
2387         for (i = 0; i < p->mp_ipaddrcnt; i++) {
2388                 mac_ipaddr_t    *addr = &p->mp_ipaddrs[i];
2389 
2390                 /*
2391                  * The unspecified address is implicitly allowed so there's no
2392                  * need to add it to the list. Also, validate that the netmask,
2393                  * if any, is sane for the specific version of IP. A mask of
2394                  * some kind is always required.
2395                  */
2396                 if (addr->ip_netmask == 0)
2397                         return (EINVAL);
2398 
2399                 if (addr->ip_version == IPV4_VERSION) {
2400                         if (V4_PART_OF_V6(addr->ip_addr) == INADDR_ANY)
2401                                 return (EINVAL);
2402                         if (addr->ip_netmask > 32)
2403                                 return (EINVAL);
2404                 } else if (addr->ip_version == IPV6_VERSION) {
2405                         if (IN6_IS_ADDR_UNSPECIFIED(&addr->ip_addr))
2406                                 return (EINVAL);
2407 
2408                         if (IN6_IS_ADDR_V4MAPPED_ANY(&addr->ip_addr))
2409                                 return (EINVAL);
2410 
2411                         if (addr->ip_netmask > 128)
2412                                 return (EINVAL);
2413                 } else {
2414                         /* invalid ip version */
2415                         return (EINVAL);
2416                 }
2417 
2418                 for (j = 0; j < p->mp_ipaddrcnt; j++) {
2419                         mac_ipaddr_t    *addr1 = &p->mp_ipaddrs[j];
2420 
2421                         if (i == j || addr->ip_version != addr1->ip_version)
2422                                 continue;
2423 
2424                         /* found a duplicate */
2425                         if ((addr->ip_version == IPV4_VERSION &&
2426                             V4_PART_OF_V6(addr->ip_addr) ==
2427                             V4_PART_OF_V6(addr1->ip_addr)) ||
2428                             IN6_ARE_ADDR_EQUAL(&addr->ip_addr,
2429                             &addr1->ip_addr))
2430                                 return (EINVAL);
2431                 }
2432         }
2433         return (0);
2434 }
2435 
2436 /* ARGSUSED */
2437 static int
2438 validate_cids(mac_protect_t *p)
2439 {
2440         uint_t          i, j;
2441 
2442         if (p->mp_cidcnt == MPT_RESET)
2443                 return (0);
2444 
2445         if (p->mp_cidcnt > MPT_MAXCID)
2446                 return (EINVAL);
2447 
2448         for (i = 0; i < p->mp_cidcnt; i++) {
2449                 mac_dhcpcid_t   *cid = &p->mp_cids[i];
2450 
2451                 if (cid->dc_len > MPT_MAXCIDLEN ||
2452                     (cid->dc_form != CIDFORM_TYPED &&
2453                     cid->dc_form != CIDFORM_HEX &&
2454                     cid->dc_form != CIDFORM_STR))
2455                         return (EINVAL);
2456 
2457                 for (j = 0; j < p->mp_cidcnt; j++) {
2458                         mac_dhcpcid_t   *cid1 = &p->mp_cids[j];
2459 
2460                         if (i == j || cid->dc_len != cid1->dc_len)
2461                                 continue;
2462 
2463                         /* found a duplicate */
2464                         if (bcmp(cid->dc_id, cid1->dc_id, cid->dc_len) == 0)
2465                                 return (EINVAL);
2466                 }
2467         }
2468         return (0);
2469 }
2470 
2471 /*
2472  * Sanity-checks parameters given by userland.
2473  */
2474 int
2475 mac_protect_validate(mac_resource_props_t *mrp)
2476 {
2477         mac_protect_t   *p = &mrp->mrp_protect;
2478         int             err;
2479 
2480         /* check for invalid types */
2481         if (p->mp_types != MPT_RESET && (p->mp_types & ~MPT_ALL) != 0)
2482                 return (EINVAL);
2483 
2484         if ((err = validate_ips(p)) != 0)
2485                 return (err);
2486 
2487         if ((err = validate_cids(p)) != 0)
2488                 return (err);
2489 
2490         return (0);
2491 }
2492 
2493 /*
2494  * Enable/disable link protection.
2495  */
2496 int
2497 mac_protect_set(mac_client_handle_t mch, mac_resource_props_t *mrp)
2498 {
2499         mac_client_impl_t       *mcip = (mac_client_impl_t *)mch;
2500         mac_impl_t              *mip = mcip->mci_mip;
2501         uint_t                  media = mip->mi_info.mi_nativemedia;
2502         int                     err;
2503 
2504         ASSERT(MAC_PERIM_HELD((mac_handle_t)mip));
2505 
2506         /* tunnels are not supported */
2507         if (media == DL_IPV4 || media == DL_IPV6 || media == DL_6TO4)
2508                 return (ENOTSUP);
2509 
2510         if ((err = mac_protect_validate(mrp)) != 0)
2511                 return (err);
2512 
2513         if (err != 0)
2514                 return (err);
2515 
2516         mac_update_resources(mrp, MCIP_RESOURCE_PROPS(mcip), B_FALSE);
2517         i_mac_notify(((mcip->mci_state_flags & MCIS_IS_VNIC) != 0 ?
2518             mcip->mci_upper_mip : mip), MAC_NOTE_ALLOWED_IPS);
2519         return (0);
2520 }
2521 
2522 void
2523 mac_protect_update(mac_resource_props_t *new, mac_resource_props_t *curr)
2524 {
2525         mac_protect_t   *np = &new->mrp_protect;
2526         mac_protect_t   *cp = &curr->mrp_protect;
2527         uint32_t        types = np->mp_types;
2528 
2529         if (types == MPT_RESET) {
2530                 cp->mp_types = 0;
2531                 curr->mrp_mask &= ~MRP_PROTECT;
2532         } else {
2533                 if (types != 0) {
2534                         cp->mp_types = types;
2535                         curr->mrp_mask |= MRP_PROTECT;
2536                 }
2537         }
2538         if (np->mp_ipaddrcnt != 0) {
2539                 if (np->mp_ipaddrcnt <= MPT_MAXIPADDR) {
2540                         bcopy(np->mp_ipaddrs, cp->mp_ipaddrs,
2541                             sizeof (cp->mp_ipaddrs));
2542                         cp->mp_ipaddrcnt = np->mp_ipaddrcnt;
2543                 } else if (np->mp_ipaddrcnt == MPT_RESET) {
2544                         bzero(cp->mp_ipaddrs, sizeof (cp->mp_ipaddrs));
2545                         cp->mp_ipaddrcnt = 0;
2546                 }
2547         }
2548         if (np->mp_cidcnt != 0) {
2549                 if (np->mp_cidcnt <= MPT_MAXCID) {
2550                         bcopy(np->mp_cids, cp->mp_cids, sizeof (cp->mp_cids));
2551                         cp->mp_cidcnt = np->mp_cidcnt;
2552                 } else if (np->mp_cidcnt == MPT_RESET) {
2553                         bzero(cp->mp_cids, sizeof (cp->mp_cids));
2554                         cp->mp_cidcnt = 0;
2555                 }
2556         }
2557 }
2558 
2559 void
2560 mac_protect_init(mac_client_impl_t *mcip)
2561 {
2562         mutex_init(&mcip->mci_protect_lock, NULL, MUTEX_DRIVER, NULL);
2563         mcip->mci_protect_flags = 0;
2564         mcip->mci_txn_cleanup_tid = 0;
2565         avl_create(&mcip->mci_v4_pending_txn, compare_dhcpv4_xid,
2566             sizeof (dhcpv4_txn_t), offsetof(dhcpv4_txn_t, dt_node));
2567         avl_create(&mcip->mci_v4_completed_txn, compare_dhcpv4_cid,
2568             sizeof (dhcpv4_txn_t), offsetof(dhcpv4_txn_t, dt_node));
2569         avl_create(&mcip->mci_v4_dyn_ip, compare_dhcpv4_ip,
2570             sizeof (dhcpv4_txn_t), offsetof(dhcpv4_txn_t, dt_ipnode));
2571         avl_create(&mcip->mci_v6_pending_txn, compare_dhcpv6_xid,
2572             sizeof (dhcpv6_txn_t), offsetof(dhcpv6_txn_t, dt_node));
2573         avl_create(&mcip->mci_v6_cid, compare_dhcpv6_cid,
2574             sizeof (dhcpv6_cid_t), offsetof(dhcpv6_cid_t, dc_node));
2575         avl_create(&mcip->mci_v6_dyn_ip, compare_dhcpv6_ip,
2576             sizeof (dhcpv6_addr_t), offsetof(dhcpv6_addr_t, da_node));
2577         avl_create(&mcip->mci_v6_slaac_ip, compare_slaac_ip,
2578             sizeof (slaac_addr_t), offsetof(slaac_addr_t, sla_node));
2579 }
2580 
2581 void
2582 mac_protect_fini(mac_client_impl_t *mcip)
2583 {
2584         avl_destroy(&mcip->mci_v6_dyn_ip);
2585         avl_destroy(&mcip->mci_v6_cid);
2586         avl_destroy(&mcip->mci_v6_pending_txn);
2587         avl_destroy(&mcip->mci_v4_dyn_ip);
2588         avl_destroy(&mcip->mci_v4_completed_txn);
2589         avl_destroy(&mcip->mci_v4_pending_txn);
2590         avl_destroy(&mcip->mci_v6_slaac_ip);
2591         mcip->mci_txn_cleanup_tid = 0;
2592         mcip->mci_protect_flags = 0;
2593         mutex_destroy(&mcip->mci_protect_lock);
2594 }
2595 
2596 static boolean_t
2597 allowed_ips_set(mac_resource_props_t *mrp, uint32_t af)
2598 {
2599         int i;
2600 
2601         for (i = 0; i < mrp->mrp_protect.mp_ipaddrcnt; i++) {
2602                 if (mrp->mrp_protect.mp_ipaddrs[i].ip_version == af)
2603                         return (B_TRUE);
2604         }
2605         return (B_FALSE);
2606 }
2607 
2608 mac_protect_t *
2609 mac_protect_get(mac_handle_t mh)
2610 {
2611         mac_impl_t *mip = (mac_impl_t *)mh;
2612 
2613         return (&mip->mi_resource_props.mrp_protect);
2614 }