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