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.
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.
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]
23 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright (c) 2015, Joyent, Inc. All rights reserved.
27 * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
30 #include <sys/cmn_err.h>
31 #include <sys/strsun.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>
44 #include <netinet/arp.h>
45 #include <netinet/udp.h>
46 #include <netinet/dhcp.h>
47 #include <netinet/dhcp6.h>
50 * Implementation overview for DHCP address detection
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.
57 * A DHCP handshake between client and server typically requires at least
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
64 * 3. REQUEST - client requests the offered address.
65 * 4. ACK - server verifies that the requested address matches
68 * DHCPv6 behaves pretty much the same way aside from different message names.
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
77 * Each DHCP message has a transaction ID. We use this transaction ID to match
78 * REQUESTs with ACKs received from servers.
80 * For IPv4, the process to acquire a DHCP-assigned address is as follows:
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
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).
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
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.
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.
114 * For IPv6, the process to acquire a DHCPv6-assigned address is as follows:
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).
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).
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.
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.
138 * The data structures used for v6 are slightly different because a v6 client
139 * may have multiple addresses associated with it.
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.
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
;
153 * DHCPv4 transaction. It may be added to three different tables
154 * (keyed by different fields).
156 typedef struct dhcpv4_txn
{
158 hrtime_t dt_timestamp
;
159 uint8_t dt_cid
[DHCP_MAX_OPT_SIZE
];
163 avl_node_t dt_ipnode
;
164 struct dhcpv4_txn
*dt_next
;
168 * DHCPv6 address. May be added to mci_v6_dyn_ip.
169 * It is always pointed to by its parent dhcpv6_cid_t structure.
171 typedef struct dhcpv6_addr
{
174 struct dhcpv6_addr
*da_next
;
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.
181 typedef struct dhcpv6_cid
{
184 dhcpv6_addr_t
*dc_addr
;
190 * DHCPv6 transaction. Unlike its v4 counterpart, this object gets freed up
191 * as soon as the transaction completes or expires.
193 typedef struct dhcpv6_txn
{
195 hrtime_t dt_timestamp
;
196 dhcpv6_cid_t
*dt_cid
;
198 struct dhcpv6_txn
*dt_next
;
202 * Stateless address autoconfiguration (SLAAC) address. May be added to
205 typedef struct slaac_addr
{
206 in6_addr_t sla_prefix
;
211 static void start_txn_cleanup_timer(mac_client_impl_t
*);
212 static boolean_t
allowed_ips_set(mac_resource_props_t
*, uint32_t);
214 #define BUMP_STAT(m, s) (m)->mci_misc_stat.mms_##s++
217 * Comparison functions for the 3 AVL trees used:
218 * mci_v4_pending_txn, mci_v4_completed_txn, mci_v4_dyn_ip
221 compare_dhcpv4_xid(const void *arg1
, const void *arg2
)
223 const dhcpv4_txn_t
*txn1
= arg1
, *txn2
= arg2
;
225 if (txn1
->dt_xid
< txn2
->dt_xid
)
227 else if (txn1
->dt_xid
> txn2
->dt_xid
)
234 compare_dhcpv4_cid(const void *arg1
, const void *arg2
)
236 const dhcpv4_txn_t
*txn1
= arg1
, *txn2
= arg2
;
239 if (txn1
->dt_cid_len
< txn2
->dt_cid_len
)
241 else if (txn1
->dt_cid_len
> txn2
->dt_cid_len
)
244 if (txn1
->dt_cid_len
== 0)
247 ret
= memcmp(txn1
->dt_cid
, txn2
->dt_cid
, txn1
->dt_cid_len
);
257 compare_dhcpv4_ip(const void *arg1
, const void *arg2
)
259 const dhcpv4_txn_t
*txn1
= arg1
, *txn2
= arg2
;
261 if (txn1
->dt_ipaddr
< txn2
->dt_ipaddr
)
263 else if (txn1
->dt_ipaddr
> txn2
->dt_ipaddr
)
270 * Find the specified DHCPv4 option.
273 get_dhcpv4_option(struct dhcp
*dh4
, uchar_t
*end
, uint8_t type
,
274 uchar_t
**opt
, uint8_t *opt_len
)
276 uchar_t
*start
= (uchar_t
*)dh4
->options
;
279 while (start
< end
) {
280 if (*start
== CD_PAD
) {
284 if (*start
== CD_END
)
289 if (otype
== type
&& olen
> 0) {
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.
308 get_dhcpv4_info(ipha_t
*ipha
, uchar_t
*end
, struct dhcp
**dh4
)
310 uint16_t offset_and_flags
, client
, server
;
311 boolean_t first_frag
= B_FALSE
;
315 if (ipha
->ipha_protocol
!= IPPROTO_UDP
)
318 offset_and_flags
= ntohs(ipha
->ipha_fragment_offset_and_flags
);
319 if ((offset_and_flags
& (IPH_MF
| IPH_OFFSET
)) != 0) {
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
326 if (((offset_and_flags
<< 3) & 0xffff) != 0)
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
)
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
)
341 /* drop dhcp fragments */
345 dh
= (uchar_t
*)&udph
[1];
346 if (dh
+ BASE_PKT_SIZE
> end
)
349 *dh4
= (struct dhcp
*)dh
;
354 * Wrappers for accesses to avl trees to improve readability.
355 * Their purposes are fairly self-explanatory.
357 static dhcpv4_txn_t
*
358 find_dhcpv4_pending_txn(mac_client_impl_t
*mcip
, uint32_t xid
)
360 dhcpv4_txn_t tmp_txn
;
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
));
368 insert_dhcpv4_pending_txn(mac_client_impl_t
*mcip
, dhcpv4_txn_t
*txn
)
372 ASSERT(MUTEX_HELD(&mcip
->mci_protect_lock
));
373 if (avl_find(&mcip
->mci_v4_pending_txn
, txn
, &where
) != NULL
)
376 if (avl_numnodes(&mcip
->mci_v4_pending_txn
) >= dhcp_max_pending_txn
) {
377 BUMP_STAT(mcip
, dhcpdropped
);
380 avl_insert(&mcip
->mci_v4_pending_txn
, txn
, where
);
385 remove_dhcpv4_pending_txn(mac_client_impl_t
*mcip
, dhcpv4_txn_t
*txn
)
387 ASSERT(MUTEX_HELD(&mcip
->mci_protect_lock
));
388 avl_remove(&mcip
->mci_v4_pending_txn
, txn
);
391 static dhcpv4_txn_t
*
392 find_dhcpv4_completed_txn(mac_client_impl_t
*mcip
, uint8_t *cid
,
395 dhcpv4_txn_t tmp_txn
;
397 ASSERT(MUTEX_HELD(&mcip
->mci_protect_lock
));
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
));
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.
411 insert_dhcpv4_completed_txn(mac_client_impl_t
*mcip
, dhcpv4_txn_t
*txn
)
415 ASSERT(MUTEX_HELD(&mcip
->mci_protect_lock
));
416 if (avl_find(&mcip
->mci_v4_completed_txn
, txn
, &where
) != NULL
)
419 if (avl_numnodes(&mcip
->mci_v4_completed_txn
) >=
420 dhcp_max_completed_txn
) {
421 BUMP_STAT(mcip
, dhcpdropped
);
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
);
430 avl_insert(&mcip
->mci_v4_dyn_ip
, txn
, where
);
435 remove_dhcpv4_completed_txn(mac_client_impl_t
*mcip
, dhcpv4_txn_t
*txn
)
439 ASSERT(MUTEX_HELD(&mcip
->mci_protect_lock
));
440 if ((ctxn
= avl_find(&mcip
->mci_v4_dyn_ip
, txn
, NULL
)) != NULL
&&
442 avl_remove(&mcip
->mci_v4_dyn_ip
, txn
);
444 avl_remove(&mcip
->mci_v4_completed_txn
, txn
);
448 * Check whether an IP address is in the dyn-ip table.
451 check_dhcpv4_dyn_ip(mac_client_impl_t
*mcip
, ipaddr_t ipaddr
)
453 dhcpv4_txn_t tmp_txn
, *txn
;
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
);
463 * Create/destroy a DHCPv4 transaction.
465 static dhcpv4_txn_t
*
466 create_dhcpv4_txn(uint32_t xid
, uint8_t *cid
, uint8_t cid_len
, ipaddr_t ipaddr
)
470 if ((txn
= kmem_zalloc(sizeof (*txn
), KM_NOSLEEP
)) == NULL
)
474 txn
->dt_timestamp
= gethrtime();
476 bcopy(cid
, &txn
->dt_cid
, cid_len
);
477 txn
->dt_cid_len
= cid_len
;
478 txn
->dt_ipaddr
= ipaddr
;
483 free_dhcpv4_txn(dhcpv4_txn_t
*txn
)
485 kmem_free(txn
, sizeof (*txn
));
489 * Clean up all v4 tables.
492 flush_dhcpv4(mac_client_impl_t
*mcip
)
497 ASSERT(MUTEX_HELD(&mcip
->mci_protect_lock
));
498 while ((txn
= avl_destroy_nodes(&mcip
->mci_v4_dyn_ip
,
501 * No freeing needed here because the same txn exists
502 * in the mci_v4_completed_txn table as well.
506 while ((txn
= avl_destroy_nodes(&mcip
->mci_v4_completed_txn
,
508 free_dhcpv4_txn(txn
);
511 while ((txn
= avl_destroy_nodes(&mcip
->mci_v4_pending_txn
,
513 free_dhcpv4_txn(txn
);
518 * Cleanup stale DHCPv4 transactions.
521 txn_cleanup_v4(mac_client_impl_t
*mcip
)
523 dhcpv4_txn_t
*txn
, *ctxn
, *next
, *txn_list
= NULL
;
526 * Find stale pending transactions and place them on a list
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
);
536 txn
->dt_next
= txn_list
;
542 * Remove and free stale pending transactions and completed
543 * transactions with the same client IDs as the stale transactions.
545 for (txn
= txn_list
; txn
!= NULL
; txn
= next
) {
546 avl_remove(&mcip
->mci_v4_pending_txn
, txn
);
548 ctxn
= find_dhcpv4_completed_txn(mcip
, txn
->dt_cid
,
551 DTRACE_PROBE2(removing__completed__txn
,
552 mac_client_impl_t
*, mcip
,
553 dhcpv4_txn_t
*, ctxn
);
555 remove_dhcpv4_completed_txn(mcip
, ctxn
);
556 free_dhcpv4_txn(ctxn
);
561 DTRACE_PROBE2(freeing__txn
, mac_client_impl_t
*, mcip
,
562 dhcpv4_txn_t
*, txn
);
563 free_dhcpv4_txn(txn
);
568 * Core logic for intercepting outbound DHCPv4 packets.
571 intercept_dhcpv4_outbound(mac_client_impl_t
*mcip
, ipha_t
*ipha
, uchar_t
*end
)
575 dhcpv4_txn_t
*txn
, *ctxn
;
577 uint8_t opt_len
, mtype
, cid
[DHCP_MAX_OPT_SIZE
], cid_len
;
578 mac_resource_props_t
*mrp
= MCIP_RESOURCE_PROPS(mcip
);
580 if (get_dhcpv4_info(ipha
, end
, &dh4
) != 0)
583 /* ip_nospoof/allowed-ips and DHCP are mutually exclusive by default */
584 if (allowed_ips_set(mrp
, IPV4_VERSION
))
587 if (get_dhcpv4_option(dh4
, end
, CD_DHCP_TYPE
, &opt
, &opt_len
) != 0 ||
589 DTRACE_PROBE2(mtype__not__found
, mac_client_impl_t
*, mcip
,
594 if (mtype
!= REQUEST
&& mtype
!= RELEASE
) {
595 DTRACE_PROBE3(ignored__mtype
, mac_client_impl_t
*, mcip
,
596 struct dhcp
*, dh4
, uint8_t, mtype
);
600 /* client ID is optional for IPv4 */
601 if (get_dhcpv4_option(dh4
, end
, CD_CLIENT_ID
, &opt
, &opt_len
) == 0 &&
603 bcopy(opt
, cid
, opt_len
);
606 bzero(cid
, DHCP_MAX_OPT_SIZE
);
610 mutex_enter(&mcip
->mci_protect_lock
);
611 if (mtype
== RELEASE
) {
612 DTRACE_PROBE2(release
, mac_client_impl_t
*, mcip
,
615 /* flush any completed txn with this cid */
616 ctxn
= find_dhcpv4_completed_txn(mcip
, cid
, cid_len
);
618 DTRACE_PROBE2(release__successful
, mac_client_impl_t
*,
619 mcip
, struct dhcp
*, dh4
);
621 remove_dhcpv4_completed_txn(mcip
, ctxn
);
622 free_dhcpv4_txn(ctxn
);
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.
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();
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
,
645 bcopy(opt
, &ipaddr
, sizeof (ipaddr
));
646 if ((txn
= create_dhcpv4_txn(dh4
->xid
, cid
, cid_len
, ipaddr
)) == NULL
)
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
);
655 start_txn_cleanup_timer(mcip
);
657 DTRACE_PROBE2(txn__pending
, mac_client_impl_t
*, mcip
,
658 dhcpv4_txn_t
*, txn
);
661 mutex_exit(&mcip
->mci_protect_lock
);
666 * Core logic for intercepting inbound DHCPv4 packets.
669 intercept_dhcpv4_inbound(mac_client_impl_t
*mcip
, uchar_t
*end
,
673 dhcpv4_txn_t
*txn
, *ctxn
;
674 uint8_t opt_len
, mtype
;
676 if (get_dhcpv4_option(dh4
, end
, CD_DHCP_TYPE
, &opt
, &opt_len
) != 0 ||
678 DTRACE_PROBE2(mtype__not__found
, mac_client_impl_t
*, mcip
,
683 if (mtype
!= ACK
&& mtype
!= NAK
) {
684 DTRACE_PROBE3(ignored__mtype
, mac_client_impl_t
*, mcip
,
685 struct dhcp
*, dh4
, uint8_t, mtype
);
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
,
695 remove_dhcpv4_pending_txn(mcip
, txn
);
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.
702 ctxn
= find_dhcpv4_completed_txn(mcip
, txn
->dt_cid
, txn
->dt_cid_len
);
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
);
710 DTRACE_PROBE2(nak__received
, mac_client_impl_t
*, mcip
,
711 dhcpv4_txn_t
*, txn
);
712 free_dhcpv4_txn(txn
);
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
);
721 DTRACE_PROBE2(txn__completed
, mac_client_impl_t
*, mcip
,
722 dhcpv4_txn_t
*, txn
);
725 mutex_exit(&mcip
->mci_protect_lock
);
730 * Comparison functions for the DHCPv6 AVL trees.
733 compare_dhcpv6_xid(const void *arg1
, const void *arg2
)
735 const dhcpv6_txn_t
*txn1
= arg1
, *txn2
= arg2
;
737 if (txn1
->dt_xid
< txn2
->dt_xid
)
739 else if (txn1
->dt_xid
> txn2
->dt_xid
)
746 compare_dhcpv6_ip(const void *arg1
, const void *arg2
)
748 const dhcpv6_addr_t
*ip1
= arg1
, *ip2
= arg2
;
751 ret
= memcmp(&ip1
->da_addr
, &ip2
->da_addr
, sizeof (in6_addr_t
));
761 compare_dhcpv6_cid(const void *arg1
, const void *arg2
)
763 const dhcpv6_cid_t
*cid1
= arg1
, *cid2
= arg2
;
766 if (cid1
->dc_cid_len
< cid2
->dc_cid_len
)
768 else if (cid1
->dc_cid_len
> cid2
->dc_cid_len
)
771 if (cid1
->dc_cid_len
== 0)
774 ret
= memcmp(cid1
->dc_cid
, cid2
->dc_cid
, cid1
->dc_cid_len
);
784 compare_slaac_ip(const void *arg1
, const void *arg2
)
786 const slaac_addr_t
*ip1
= arg1
, *ip2
= arg2
;
789 ret
= memcmp(&ip1
->sla_addr
, &ip2
->sla_addr
, sizeof (in6_addr_t
));
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.
807 get_dhcpv6_info(ip6_t
*ip6h
, uchar_t
*end
, dhcpv6_message_t
**dh6
)
809 uint16_t hdrlen
, client
, server
;
810 boolean_t first_frag
= B_FALSE
;
811 ip6_frag_t
*frag
= NULL
;
816 if (!mac_ip_hdr_length_v6(ip6h
, end
, &hdrlen
, &proto
, &frag
))
819 if (proto
!= IPPROTO_UDP
)
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
829 if ((ntohs(frag
->ip6f_offlg
) & ~7) != 0)
833 /* drop packets without a udp header */
834 udph
= (struct udphdr
*)((uchar_t
*)ip6h
+ hdrlen
);
835 if ((uchar_t
*)&udph
[1] > end
)
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
)
844 /* drop dhcp fragments */
848 dh
= (uchar_t
*)&udph
[1];
849 if (dh
+ sizeof (dhcpv6_message_t
) > end
)
852 *dh6
= (dhcpv6_message_t
*)dh
;
857 get_ra_info(ip6_t
*ip6h
, uchar_t
*end
, nd_router_advert_t
**ra
)
860 ip6_frag_t
*frag
= NULL
;
863 struct icmp6_hdr
*icmp
;
865 if (!mac_ip_hdr_length_v6(ip6h
, end
, &hdrlen
, &proto
, &frag
))
868 if (proto
!= IPPROTO_ICMPV6
)
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
878 if ((ntohs(frag
->ip6f_offlg
) & ~7) != 0)
884 * Ensure that the ICMP header falls w/in packet boundaries, in case
885 * we've received a malicious packet that reports incorrect lengths.
887 hdrp
= (uchar_t
*)ip6h
+ hdrlen
;
888 if ((hdrp
+ sizeof (struct icmp6_hdr
)) > end
) {
891 icmp
= (struct icmp6_hdr
*)hdrp
;
893 if (icmp
->icmp6_type
!= ND_ROUTER_ADVERT
||
894 icmp
->icmp6_code
!= 0)
897 *ra
= (nd_router_advert_t
*)icmp
;
902 * Find the specified DHCPv6 option.
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
)
912 codenum
= htons(codenum
);
914 while (buflen
>= sizeof (dhcpv6_option_t
)) {
915 bcopy(bp
, &d6o
, sizeof (d6o
));
916 olen
= ntohs(d6o
.d6o_len
) + sizeof (d6o
);
919 if (d6o
.d6o_code
!= codenum
|| d6o
.d6o_len
== 0 ||
920 (oldopt
!= NULL
&& bp
<= (uchar_t
*)oldopt
)) {
927 /* LINTED : alignment */
928 return ((dhcpv6_option_t
*)bp
);
934 * Get the status code from a reply message.
937 get_dhcpv6_status(dhcpv6_message_t
*dh6
, uchar_t
*end
, uint16_t *status
)
939 dhcpv6_option_t
*d6o
;
943 d6o
= get_dhcpv6_option(&dh6
[1], end
- (uchar_t
*)&dh6
[1], NULL
,
944 DHCPV6_OPT_STATUS_CODE
, &olen
);
946 /* Success is implied if status code is missing */
948 *status
= DHCPV6_STAT_SUCCESS
;
951 if ((uchar_t
*)d6o
+ olen
> end
)
954 olen
-= sizeof (*d6o
);
955 if (olen
< sizeof (s
))
958 bcopy(&d6o
[1], &s
, sizeof (s
));
964 * Get the addresses from a reply message.
967 get_dhcpv6_addrs(dhcpv6_message_t
*dh6
, uchar_t
*end
, dhcpv6_cid_t
*cid
)
969 dhcpv6_option_t
*d6o
;
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
;
982 if (olen
< sizeof (dhcpv6_ia_na_t
) ||
983 (uchar_t
*)d6o
+ olen
> end
)
986 obase
= (uchar_t
*)d6o
+ sizeof (dhcpv6_ia_na_t
);
987 olen
-= sizeof (dhcpv6_ia_na_t
);
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
)
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)
1002 if ((*addrp
= kmem_zalloc(sizeof (dhcpv6_addr_t
),
1003 KM_NOSLEEP
)) == NULL
)
1006 bcopy(&d6ia
.d6ia_addr
, &(*addrp
)->da_addr
,
1007 sizeof (in6_addr_t
));
1011 if (cid
->dc_addrcnt
== 0)
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
));
1022 ASSERT(cid
->dc_addrcnt
== 0);
1028 * Before this gets called the caller must ensure that all the
1029 * addresses are removed from the mci_v6_dyn_ip table.
1032 free_dhcpv6_cid(dhcpv6_cid_t
*cid
)
1034 dhcpv6_addr_t
*addr
, *next
;
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
));
1043 ASSERT(cnt
== cid
->dc_addrcnt
);
1044 kmem_free(cid
, sizeof (*cid
));
1048 * Extract the DUID from a message. The associated addresses will be
1049 * extracted later from the reply message.
1051 static dhcpv6_cid_t
*
1052 create_dhcpv6_cid(dhcpv6_message_t
*dh6
, uchar_t
*end
)
1054 dhcpv6_option_t
*d6o
;
1057 uint_t olen
, rawcidlen
;
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
)
1064 rawcidlen
= olen
- sizeof (*d6o
);
1065 if ((rawcid
= kmem_zalloc(rawcidlen
, KM_NOSLEEP
)) == NULL
)
1067 bcopy(d6o
+ 1, rawcid
, rawcidlen
);
1069 if ((cid
= kmem_zalloc(sizeof (*cid
), KM_NOSLEEP
)) == NULL
) {
1070 kmem_free(rawcid
, rawcidlen
);
1073 cid
->dc_cid
= rawcid
;
1074 cid
->dc_cid_len
= rawcidlen
;
1079 * Remove a cid from mci_v6_cid. The addresses owned by the cid
1080 * are also removed from mci_v6_dyn_ip.
1083 remove_dhcpv6_cid(mac_client_impl_t
*mcip
, dhcpv6_cid_t
*cid
)
1085 dhcpv6_addr_t
*addr
, *tmp_addr
;
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
);
1097 * Find and remove a matching cid and associated addresses from
1098 * their respective tables.
1101 release_dhcpv6_cid(mac_client_impl_t
*mcip
, dhcpv6_cid_t
*cid
)
1103 dhcpv6_cid_t
*oldcid
;
1105 ASSERT(MUTEX_HELD(&mcip
->mci_protect_lock
));
1106 if ((oldcid
= avl_find(&mcip
->mci_v6_cid
, cid
, NULL
)) == NULL
)
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.
1113 ASSERT(oldcid
!= cid
);
1114 remove_dhcpv6_cid(mcip
, oldcid
);
1115 free_dhcpv6_cid(oldcid
);
1119 * Insert cid into mci_v6_cid.
1122 insert_dhcpv6_cid(mac_client_impl_t
*mcip
, dhcpv6_cid_t
*cid
)
1125 dhcpv6_addr_t
*addr
;
1127 ASSERT(MUTEX_HELD(&mcip
->mci_protect_lock
));
1128 if (avl_find(&mcip
->mci_v6_cid
, cid
, &where
) != NULL
)
1131 if (avl_numnodes(&mcip
->mci_v6_cid
) >= dhcp_max_completed_txn
) {
1132 BUMP_STAT(mcip
, dhcpdropped
);
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
)
1140 avl_insert(&mcip
->mci_v6_dyn_ip
, addr
, where
);
1145 remove_dhcpv6_cid(mcip
, cid
);
1150 * Check whether an IP address is in the dyn-ip table.
1153 check_dhcpv6_dyn_ip(mac_client_impl_t
*mcip
, in6_addr_t
*addr
)
1155 dhcpv6_addr_t tmp_addr
, *a
;
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
);
1164 static dhcpv6_txn_t
*
1165 find_dhcpv6_pending_txn(mac_client_impl_t
*mcip
, uint32_t xid
)
1167 dhcpv6_txn_t tmp_txn
;
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
));
1175 remove_dhcpv6_pending_txn(mac_client_impl_t
*mcip
, dhcpv6_txn_t
*txn
)
1177 ASSERT(MUTEX_HELD(&mcip
->mci_protect_lock
));
1178 avl_remove(&mcip
->mci_v6_pending_txn
, txn
);
1181 static dhcpv6_txn_t
*
1182 create_dhcpv6_txn(uint32_t xid
, dhcpv6_cid_t
*cid
)
1186 if ((txn
= kmem_zalloc(sizeof (dhcpv6_txn_t
), KM_NOSLEEP
)) == NULL
)
1191 txn
->dt_timestamp
= gethrtime();
1196 free_dhcpv6_txn(dhcpv6_txn_t
*txn
)
1198 if (txn
->dt_cid
!= NULL
)
1199 free_dhcpv6_cid(txn
->dt_cid
);
1200 kmem_free(txn
, sizeof (dhcpv6_txn_t
));
1204 insert_dhcpv6_pending_txn(mac_client_impl_t
*mcip
, dhcpv6_txn_t
*txn
)
1208 ASSERT(MUTEX_HELD(&mcip
->mci_protect_lock
));
1209 if (avl_find(&mcip
->mci_v6_pending_txn
, txn
, &where
) != NULL
)
1212 if (avl_numnodes(&mcip
->mci_v6_pending_txn
) >= dhcp_max_pending_txn
) {
1213 BUMP_STAT(mcip
, dhcpdropped
);
1216 avl_insert(&mcip
->mci_v6_pending_txn
, txn
, where
);
1221 * Clean up all v6 tables.
1224 flush_dhcpv6(mac_client_impl_t
*mcip
)
1226 void *cookie
= NULL
;
1230 ASSERT(MUTEX_HELD(&mcip
->mci_protect_lock
));
1231 while (avl_destroy_nodes(&mcip
->mci_v6_dyn_ip
, &cookie
) != NULL
) {
1234 while ((cid
= avl_destroy_nodes(&mcip
->mci_v6_cid
, &cookie
)) != NULL
) {
1235 free_dhcpv6_cid(cid
);
1238 while ((txn
= avl_destroy_nodes(&mcip
->mci_v6_pending_txn
,
1239 &cookie
)) != NULL
) {
1240 free_dhcpv6_txn(txn
);
1245 flush_slaac(mac_client_impl_t
*mcip
)
1247 void *cookie
= NULL
;
1248 slaac_addr_t
*addr
= NULL
;
1250 while ((addr
= avl_destroy_nodes(&mcip
->mci_v6_slaac_ip
, &cookie
)) !=
1252 kmem_free(addr
, sizeof (slaac_addr_t
));
1257 * Cleanup stale DHCPv6 transactions.
1260 txn_cleanup_v6(mac_client_impl_t
*mcip
)
1262 dhcpv6_txn_t
*txn
, *next
, *txn_list
= NULL
;
1265 * Find stale pending transactions and place them on a list
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
);
1275 txn
->dt_next
= txn_list
;
1281 * Remove and free stale pending transactions.
1282 * Release any existing cids matching the stale transactions.
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
;
1290 DTRACE_PROBE2(freeing__txn
, mac_client_impl_t
*, mcip
,
1291 dhcpv6_txn_t
*, txn
);
1292 free_dhcpv6_txn(txn
);
1298 * Core logic for intercepting outbound DHCPv6 packets.
1301 intercept_dhcpv6_outbound(mac_client_impl_t
*mcip
, ip6_t
*ip6h
, uchar_t
*end
)
1303 dhcpv6_message_t
*dh6
;
1305 dhcpv6_cid_t
*cid
= NULL
;
1308 mac_resource_props_t
*mrp
= MCIP_RESOURCE_PROPS(mcip
);
1310 if (get_dhcpv6_info(ip6h
, end
, &dh6
) != 0)
1313 /* ip_nospoof/allowed-ips and DHCP are mutually exclusive by default */
1314 if (allowed_ips_set(mrp
, IPV6_VERSION
))
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.
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
)
1329 if ((cid
= create_dhcpv6_cid(dh6
, end
)) == NULL
)
1332 mutex_enter(&mcip
->mci_protect_lock
);
1333 if (mtype
== DHCPV6_MSG_RELEASE
) {
1334 release_dhcpv6_cid(mcip
, cid
);
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();
1344 if ((txn
= create_dhcpv6_txn(xid
, 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
);
1354 start_txn_cleanup_timer(mcip
);
1356 DTRACE_PROBE2(txn__pending
, mac_client_impl_t
*, mcip
,
1357 dhcpv6_txn_t
*, txn
);
1361 free_dhcpv6_cid(cid
);
1363 mutex_exit(&mcip
->mci_protect_lock
);
1368 * Core logic for intercepting inbound DHCPv6 packets.
1371 intercept_dhcpv6_inbound(mac_client_impl_t
*mcip
, uchar_t
*end
,
1372 dhcpv6_message_t
*dh6
)
1379 mtype
= dh6
->d6m_msg_type
;
1380 if (mtype
!= DHCPV6_MSG_REPLY
)
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
);
1390 remove_dhcpv6_pending_txn(mcip
, txn
);
1391 release_dhcpv6_cid(mcip
, txn
->dt_cid
);
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
);
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
);
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
);
1409 DTRACE_PROBE2(txn__completed
, mac_client_impl_t
*, mcip
,
1410 dhcpv6_txn_t
*, txn
);
1416 free_dhcpv6_txn(txn
);
1417 mutex_exit(&mcip
->mci_protect_lock
);
1421 * Check whether an IP address is in the SLAAC table.
1424 check_slaac_ip(mac_client_impl_t
*mcip
, in6_addr_t
*addr
)
1426 slaac_addr_t tmp_addr
, *a
;
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
);
1436 insert_slaac_ip(avl_tree_t
*tree
, in6_addr_t
*token
, slaac_addr_t
*addr
)
1440 in6_addr_t
*prefix
= &addr
->sla_prefix
;
1441 in6_addr_t
*in6p
= &addr
->sla_addr
;
1443 bcopy(prefix
, in6p
, sizeof (struct in6_addr
));
1445 for (i
= 0; i
< 4; i
++) {
1446 in6p
->s6_addr32
[i
] = token
->s6_addr32
[i
] |
1450 DTRACE_PROBE1(generated__addr
, in6_addr_t
*, in6p
);
1452 if (avl_find(tree
, addr
, &where
) != NULL
)
1455 avl_insert(tree
, addr
, where
);
1460 insert_slaac_prefix(mac_client_impl_t
*mcip
, nd_opt_prefix_info_t
*po
)
1462 slaac_addr_t
*addr
= NULL
;
1463 in6_addr_t
*token
= &mcip
->mci_v6_mac_token
;
1465 ASSERT(MUTEX_HELD(&mcip
->mci_protect_lock
));
1467 if (avl_numnodes(&mcip
->mci_v6_slaac_ip
) >= slaac_max_allowed
) {
1468 DTRACE_PROBE(limit__reached
);
1472 if ((addr
= kmem_zalloc(sizeof (slaac_addr_t
),
1473 KM_NOSLEEP
| KM_NORMALPRI
)) == NULL
)
1476 bcopy(&po
->nd_opt_pi_prefix
, &addr
->sla_prefix
,
1477 sizeof (struct in6_addr
));
1479 if (!insert_slaac_ip(&mcip
->mci_v6_slaac_ip
, token
, addr
)) {
1480 kmem_free(addr
, sizeof (slaac_addr_t
));
1485 intercept_prefix_info(mac_client_impl_t
*mcip
, nd_opt_prefix_info_t
*po
)
1487 if (8 * po
->nd_opt_pi_len
!= sizeof (nd_opt_prefix_info_t
)) {
1488 DTRACE_PROBE(invalid__length
);
1492 if (po
->nd_opt_pi_prefix_len
> 128) {
1493 DTRACE_PROBE(invalid__plen
);
1497 if (IN6_IS_ADDR_LINKLOCAL(&po
->nd_opt_pi_prefix
)) {
1498 DTRACE_PROBE(link__local
);
1502 if ((po
->nd_opt_pi_flags_reserved
& ND_OPT_PI_FLAG_AUTO
) == 0)
1505 mutex_enter(&mcip
->mci_protect_lock
);
1506 insert_slaac_prefix(mcip
, po
);
1507 mutex_exit(&mcip
->mci_protect_lock
);
1511 * If we receive a Router Advertisement carrying prefix information and
1512 * indicating that SLAAC should be performed, then track the prefix.
1515 intercept_ra_inbound(mac_client_impl_t
*mcip
, ip6_t
*ip6h
, uchar_t
*end
,
1516 nd_router_advert_t
*ra
)
1518 struct nd_opt_hdr
*opt
;
1521 if (ip6h
->ip6_hlim
!= 255) {
1522 DTRACE_PROBE1(invalid__hoplimit
, uint8_t, ip6h
->ip6_hlim
);
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;
1532 if (optlen
< sizeof (struct nd_opt_hdr
) ||
1533 ((uchar_t
*)opt
+ optlen
) > end
) {
1534 DTRACE_PROBE(invalid__length
);
1538 if (opt
->nd_opt_type
== ND_OPT_PREFIX_INFORMATION
) {
1539 intercept_prefix_info(mcip
,
1540 (nd_opt_prefix_info_t
*)opt
);
1543 opt
= (struct nd_opt_hdr
*)((char *)opt
+ optlen
);
1549 * Timer for cleaning up stale transactions.
1552 txn_cleanup_timer(void *arg
)
1554 mac_client_impl_t
*mcip
= arg
;
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
);
1562 mcip
->mci_txn_cleanup_tid
= 0;
1564 txn_cleanup_v4(mcip
);
1565 txn_cleanup_v6(mcip
);
1568 * Restart timer if pending transactions still exist.
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
);
1574 mcip
->mci_txn_cleanup_tid
= timeout(txn_cleanup_timer
, mcip
,
1575 drv_usectohz(txn_cleanup_interval
/ (NANOSEC
/ MICROSEC
)));
1577 mutex_exit(&mcip
->mci_protect_lock
);
1581 start_txn_cleanup_timer(mac_client_impl_t
*mcip
)
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
)));
1591 cancel_txn_cleanup_timer(mac_client_impl_t
*mcip
)
1595 ASSERT(MUTEX_HELD(&mcip
->mci_protect_lock
));
1598 * This needs to be a while loop because the timer could get
1599 * rearmed during untimeout().
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
);
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.
1614 get_l3_info(mblk_t
*mp
, size_t hdrsize
, uchar_t
**start
, uchar_t
**end
,
1618 mblk_t
*newmp
= NULL
;
1621 * Pullup if necessary but reject packets that do not have
1622 * a proper mac header.
1624 s
= mp
->b_rptr
+ hdrsize
;
1630 if (!OK_32PTR(s
) || mp
->b_cont
!= NULL
) {
1632 * Temporarily adjust mp->b_rptr to ensure proper
1633 * alignment of IP header in newmp.
1635 DTRACE_PROBE1(pullup__needed
, mblk_t
*, mp
);
1637 mp
->b_rptr
+= hdrsize
;
1638 newmp
= msgpullup(mp
, -1);
1639 mp
->b_rptr
-= hdrsize
;
1655 mac_protect_intercept_dynamic_one(mac_client_impl_t
*mcip
, mblk_t
*mp
)
1657 mac_impl_t
*mip
= mcip
->mci_mip
;
1658 uchar_t
*start
, *end
;
1660 mac_header_info_t mhi
;
1663 err
= mac_vlan_header_info((mac_handle_t
)mip
, mp
, &mhi
);
1665 DTRACE_PROBE2(invalid__header
, mac_client_impl_t
*, mcip
,
1670 err
= get_l3_info(mp
, mhi
.mhi_hdrsize
, &start
, &end
, &nmp
);
1672 DTRACE_PROBE2(invalid__l3
, mac_client_impl_t
*, mcip
,
1677 switch (mhi
.mhi_bindsap
) {
1678 case ETHERTYPE_IP
: {
1680 ipha_t
*ipha
= (ipha_t
*)start
;
1682 if (start
+ sizeof (ipha_t
) > end
)
1685 if (get_dhcpv4_info(ipha
, end
, &dh4
) == 0) {
1686 intercept_dhcpv4_inbound(mcip
, end
, dh4
);
1690 case ETHERTYPE_IPV6
: {
1691 dhcpv6_message_t
*dh6
;
1692 nd_router_advert_t
*ra
;
1693 ip6_t
*ip6h
= (ip6_t
*)start
;
1695 if (start
+ sizeof (ip6_t
) > end
)
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
);
1711 mac_protect_intercept_dynamic(mac_client_impl_t
*mcip
, mblk_t
*mp
)
1714 * Skip checks if we are part of an aggr.
1716 if ((mcip
->mci_state_flags
& MCIS_IS_AGGR_PORT
) != 0)
1719 for (; mp
!= NULL
; mp
= mp
->b_next
)
1720 mac_protect_intercept_dynamic_one(mcip
, mp
);
1724 mac_protect_flush_dynamic(mac_client_impl_t
*mcip
)
1726 mutex_enter(&mcip
->mci_protect_lock
);
1730 mutex_exit(&mcip
->mci_protect_lock
);
1734 mac_protect_cancel_timer(mac_client_impl_t
*mcip
)
1736 mutex_enter(&mcip
->mci_protect_lock
);
1737 cancel_txn_cleanup_timer(mcip
);
1738 mutex_exit(&mcip
->mci_protect_lock
);
1742 * Check if addr is in the 'allowed-ips' list.
1747 ipnospoof_check_v4(mac_client_impl_t
*mcip
, mac_protect_t
*protect
,
1753 * The unspecified address is allowed.
1755 if (*addr
== INADDR_ANY
)
1758 for (i
= 0; i
< protect
->mp_ipaddrcnt
; i
++) {
1759 mac_ipaddr_t
*v4addr
= &protect
->mp_ipaddrs
[i
];
1761 if (v4addr
->ip_version
== IPV4_VERSION
) {
1764 /* LINTED E_SUSPICIOUS_COMPARISON */
1765 ASSERT(v4addr
->ip_netmask
>= 0 &&
1766 v4addr
->ip_netmask
<= 32);
1767 mask
= 0xFFFFFFFFu
<< (32 - v4addr
->ip_netmask
);
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.
1773 if (htonl(V4_PART_OF_V6(v4addr
->ip_addr
)) ==
1774 (htonl(*addr
) & mask
))
1778 return (protect
->mp_ipaddrcnt
== 0 ?
1779 check_dhcpv4_dyn_ip(mcip
, *addr
) : B_FALSE
);
1783 ipnospoof_check_v6(mac_client_impl_t
*mcip
, mac_protect_t
*protect
,
1789 * The unspecified address and the v6 link local address are allowed.
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
)))
1797 for (i
= 0; i
< protect
->mp_ipaddrcnt
; i
++) {
1798 mac_ipaddr_t
*v6addr
= &protect
->mp_ipaddrs
[i
];
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
))
1807 if (protect
->mp_ipaddrcnt
== 0) {
1808 return (check_slaac_ip(mcip
, addr
) ||
1809 check_dhcpv6_dyn_ip(mcip
, addr
));
1816 * Checks various fields within an IPv6 NDP packet.
1819 ipnospoof_check_ndp(mac_client_impl_t
*mcip
, mac_protect_t
*protect
,
1820 ip6_t
*ip6h
, uchar_t
*end
)
1822 icmp6_t
*icmp_nd
= (icmp6_t
*)&ip6h
[1];
1823 int hdrlen
, optlen
, opttype
, len
;
1824 uint_t addrlen
, maclen
;
1827 struct nd_opt_lla
*lla
= NULL
;
1830 * NDP packets do not have extension headers so the ICMPv6 header
1831 * must immediately follow the IPv6 header.
1833 if (ip6h
->ip6_nxt
!= IPPROTO_ICMPV6
)
1836 /* ICMPv6 header missing */
1837 if ((uchar_t
*)&icmp_nd
[1] > end
)
1840 len
= end
- (uchar_t
*)icmp_nd
;
1841 type
= icmp_nd
->icmp6_type
;
1844 case ND_ROUTER_SOLICIT
:
1845 hdrlen
= sizeof (nd_router_solicit_t
);
1847 case ND_ROUTER_ADVERT
:
1848 hdrlen
= sizeof (nd_router_advert_t
);
1850 case ND_NEIGHBOR_SOLICIT
:
1851 hdrlen
= sizeof (nd_neighbor_solicit_t
);
1853 case ND_NEIGHBOR_ADVERT
:
1854 hdrlen
= sizeof (nd_neighbor_advert_t
);
1857 hdrlen
= sizeof (nd_redirect_t
);
1866 /* SLLA option checking is needed for RS/RA/NS */
1867 opttype
= ND_OPT_SOURCE_LINKADDR
;
1870 case ND_NEIGHBOR_ADVERT
: {
1871 nd_neighbor_advert_t
*na
= (nd_neighbor_advert_t
*)icmp_nd
;
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
);
1879 /* TLLA option for NA */
1880 opttype
= ND_OPT_TARGET_LINKADDR
;
1884 /* option checking not needed for RD */
1891 if (len
== hdrlen
) {
1892 /* no options, we're done */
1895 opt
= (nd_opt_hdr_t
*)((uchar_t
*)icmp_nd
+ hdrlen
);
1896 optlen
= len
- hdrlen
;
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
;
1904 optlen
-= 8 * opt
->nd_opt_len
;
1905 opt
= (nd_opt_hdr_t
*)
1906 ((uchar_t
*)opt
+ 8 * opt
->nd_opt_len
);
1911 addrlen
= lla
->nd_opt_lla_len
* 8 - sizeof (nd_opt_hdr_t
);
1912 maclen
= mcip
->mci_mip
->mi_info
.mi_addr_length
;
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
);
1922 DTRACE_PROBE2(ndp__lla__ok
, mac_client_impl_t
*, mcip
, ip6_t
*, ip6h
);
1927 * Enforce ip-nospoof protection.
1930 ipnospoof_check(mac_client_impl_t
*mcip
, mac_protect_t
*protect
,
1931 mblk_t
*mp
, mac_header_info_t
*mhip
)
1933 size_t hdrsize
= mhip
->mhi_hdrsize
;
1934 uint32_t sap
= mhip
->mhi_bindsap
;
1935 uchar_t
*start
, *end
;
1939 err
= get_l3_info(mp
, hdrsize
, &start
, &end
, &nmp
);
1941 DTRACE_PROBE2(invalid__l3
, mac_client_impl_t
*, mcip
,
1948 case ETHERTYPE_IP
: {
1949 ipha_t
*ipha
= (ipha_t
*)start
;
1951 if (start
+ sizeof (ipha_t
) > end
)
1954 if (!ipnospoof_check_v4(mcip
, protect
, &ipha
->ipha_src
))
1957 if (!intercept_dhcpv4_outbound(mcip
, ipha
, end
))
1961 case ETHERTYPE_ARP
: {
1962 arh_t
*arh
= (arh_t
*)start
;
1963 uint32_t maclen
, hlen
, plen
, arplen
;
1967 if (start
+ sizeof (arh_t
) > end
)
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
))
1977 arplen
= sizeof (arh_t
) + 2 * hlen
+ 2 * plen
;
1978 if (start
+ arplen
> end
)
1981 shaddr
= start
+ sizeof (arh_t
);
1983 bcmp(mcip
->mci_unicast
->ma_addr
, shaddr
, maclen
) != 0)
1986 bcopy(shaddr
+ hlen
, &spaddr
, sizeof (spaddr
));
1987 if (!ipnospoof_check_v4(mcip
, protect
, &spaddr
))
1991 case ETHERTYPE_IPV6
: {
1992 ip6_t
*ip6h
= (ip6_t
*)start
;
1994 if (start
+ sizeof (ip6_t
) > end
)
1997 if (!ipnospoof_check_v6(mcip
, protect
, &ip6h
->ip6_src
))
2000 if (!ipnospoof_check_ndp(mcip
, protect
, ip6h
, end
))
2003 if (!intercept_dhcpv6_outbound(mcip
, ip6h
, end
))
2017 dhcpnospoof_check_cid(mac_protect_t
*p
, uchar_t
*cid
, uint_t cidlen
)
2021 for (i
= 0; i
< p
->mp_cidcnt
; i
++) {
2022 mac_dhcpcid_t
*dcid
= &p
->mp_cids
[i
];
2024 if (dcid
->dc_len
== cidlen
&&
2025 bcmp(dcid
->dc_id
, cid
, cidlen
) == 0)
2032 dhcpnospoof_check_v4(mac_client_impl_t
*mcip
, mac_protect_t
*p
,
2033 ipha_t
*ipha
, uchar_t
*end
)
2037 uint_t maclen
, cidlen
= 0;
2041 if ((err
= get_dhcpv4_info(ipha
, end
, &dh4
)) != 0)
2042 return (err
== EINVAL
);
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) {
2049 if (get_dhcpv4_option(dh4
, end
, CD_CLIENT_ID
, &cid
, &optlen
) == 0)
2055 if (*cid
== ARPHRD_ETHER
&& cidlen
- 1 == maclen
&&
2056 bcmp(mcip
->mci_unicast
->ma_addr
, cid
+ 1, maclen
) == 0)
2059 return (dhcpnospoof_check_cid(p
, cid
, cidlen
));
2063 dhcpnospoof_check_v6(mac_client_impl_t
*mcip
, mac_protect_t
*p
,
2064 ip6_t
*ip6h
, uchar_t
*end
)
2066 dhcpv6_message_t
*dh6
;
2067 dhcpv6_option_t
*d6o
;
2069 uchar_t
*cid
, *lladdr
= NULL
;
2070 uint_t cidlen
, maclen
, addrlen
= 0;
2074 if ((err
= get_dhcpv6_info(ip6h
, end
, &dh6
)) != 0)
2075 return (err
== EINVAL
);
2078 * We only check client-generated messages.
2080 mtype
= dh6
->d6m_msg_type
;
2081 if (mtype
== DHCPV6_MSG_ADVERTISE
|| mtype
== DHCPV6_MSG_REPLY
||
2082 mtype
== DHCPV6_MSG_RECONFIGURE
)
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
)
2090 cid
= (uchar_t
*)&d6o
[1];
2091 cidlen
-= sizeof (*d6o
);
2092 if (cidlen
< sizeof (cidtype
))
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
);
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
);
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) {
2110 return (dhcpnospoof_check_cid(p
, cid
, cidlen
));
2114 * Enforce dhcp-nospoof protection.
2117 dhcpnospoof_check(mac_client_impl_t
*mcip
, mac_protect_t
*protect
,
2118 mblk_t
*mp
, mac_header_info_t
*mhip
)
2120 size_t hdrsize
= mhip
->mhi_hdrsize
;
2121 uint32_t sap
= mhip
->mhi_bindsap
;
2122 uchar_t
*start
, *end
;
2126 err
= get_l3_info(mp
, hdrsize
, &start
, &end
, &nmp
);
2128 DTRACE_PROBE2(invalid__l3
, mac_client_impl_t
*, mcip
,
2135 case ETHERTYPE_IP
: {
2136 ipha_t
*ipha
= (ipha_t
*)start
;
2138 if (start
+ sizeof (ipha_t
) > end
)
2141 if (!dhcpnospoof_check_v4(mcip
, protect
, ipha
, end
))
2146 case ETHERTYPE_IPV6
: {
2147 ip6_t
*ip6h
= (ip6_t
*)start
;
2149 if (start
+ sizeof (ip6_t
) > end
)
2152 if (!dhcpnospoof_check_v6(mcip
, protect
, ip6h
, end
))
2162 /* increment dhcpnospoof stat here */
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.
2172 mac_protect_update_v6_local_addr(mac_client_impl_t
*mcip
)
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
= IN6ADDR_INITIALIZER(V6_LINKLOCAL
, 0x0,
2180 for (i
= 0; i
< 4; i
++) {
2181 v6addr
->s6_addr32
[i
] = token
->s6_addr32
[i
] |
2182 ll_template
.s6_addr32
[i
];
2184 mcip
->mci_protect_flags
|= MPT_FLAG_V6_LOCAL_ADDR_SET
;
2188 * This is called whenever the mac client's mac address changes, to make sure
2189 * that any existing addresses gained via SLAAC are appropriately updated.
2192 mac_protect_update_v6_slaac_addr(mac_client_impl_t
*mcip
)
2194 void *cookie
= NULL
;
2195 avl_tree_t temp_tree
;
2196 avl_tree_t
*ttp
= &temp_tree
, *sip
= &mcip
->mci_v6_slaac_ip
;
2197 in6_addr_t
*token
= &mcip
->mci_v6_mac_token
;
2198 slaac_addr_t
*addr
= NULL
;
2200 avl_create(ttp
, compare_slaac_ip
, sizeof (slaac_addr_t
),
2201 offsetof(slaac_addr_t
, sla_node
));
2203 /* Copy everything over to the temporary tree, and fix the IP address */
2204 while ((addr
= avl_destroy_nodes(sip
, &cookie
)) != NULL
) {
2205 VERIFY(insert_slaac_ip(ttp
, token
, addr
) == B_TRUE
);
2209 * Now that the tempory tree has all of the modified addresses, we can
2210 * swap them over to the original tree once it's reset.
2213 avl_create(sip
, compare_slaac_ip
, sizeof (slaac_addr_t
),
2214 offsetof(slaac_addr_t
, sla_node
));
2219 * After the unicast MAC address changes, we need to update the derived token,
2220 * and update the IPv6 addresses that use the token.
2223 mac_protect_update_mac_token(mac_client_impl_t
*mcip
)
2225 uint_t media
= mcip
->mci_mip
->mi_info
.mi_media
;
2226 uint8_t *p
, *macaddr
= mcip
->mci_unicast
->ma_addr
;
2227 in6_addr_t
*token
= &mcip
->mci_v6_mac_token
;
2229 bzero(token
, sizeof (in6_addr_t
));
2230 p
= (uint8_t *)&token
->s6_addr32
[2];
2234 bcopy(macaddr
, p
, 3);
2238 bcopy(macaddr
+ 3, p
+ 5, 3);
2241 ASSERT(mcip
->mci_mip
->mi_info
.mi_addr_length
== 20);
2242 bcopy(macaddr
+ 12, p
, 8);
2247 * We do not need to generate the local address for link types
2248 * that do not support link protection. Wifi pretends to be
2249 * Ethernet so it is covered by the DL_ETHER case (note the
2250 * use of mi_media instead of mi_nativemedia).
2255 mac_protect_update_v6_local_addr(mcip
);
2256 mac_protect_update_v6_slaac_addr(mcip
);
2262 * Enforce link protection on one packet.
2265 mac_protect_check_one(mac_client_impl_t
*mcip
, mblk_t
*mp
)
2267 mac_impl_t
*mip
= mcip
->mci_mip
;
2268 mac_resource_props_t
*mrp
= MCIP_RESOURCE_PROPS(mcip
);
2269 mac_protect_t
*protect
;
2270 mac_header_info_t mhi
;
2274 ASSERT(mp
->b_next
== NULL
);
2275 ASSERT(mrp
!= NULL
);
2277 err
= mac_vlan_header_info((mac_handle_t
)mip
, mp
, &mhi
);
2279 DTRACE_PROBE2(invalid__header
, mac_client_impl_t
*, mcip
,
2283 protect
= &mrp
->mrp_protect
;
2284 types
= protect
->mp_types
;
2286 if ((types
& MPT_MACNOSPOOF
) != 0) {
2287 if (mhi
.mhi_saddr
!= NULL
&&
2288 bcmp(mcip
->mci_unicast
->ma_addr
, mhi
.mhi_saddr
,
2289 mip
->mi_info
.mi_addr_length
) != 0) {
2290 BUMP_STAT(mcip
, macspoofed
);
2291 DTRACE_PROBE2(mac__nospoof__fail
,
2292 mac_client_impl_t
*, mcip
, mblk_t
*, mp
);
2296 if ((types
& MPT_RESTRICTED
) != 0) {
2297 uint32_t vid
= VLAN_ID(mhi
.mhi_tci
);
2298 uint32_t sap
= mhi
.mhi_bindsap
;
2301 * ETHERTYPE_VLAN packets are allowed through, provided that
2302 * the vid is not spoofed.
2304 if (vid
!= 0 && !mac_client_check_flow_vid(mcip
, vid
)) {
2305 BUMP_STAT(mcip
, restricted
);
2306 DTRACE_PROBE2(restricted__vid__invalid
,
2307 mac_client_impl_t
*, mcip
, mblk_t
*, mp
);
2311 if (sap
!= ETHERTYPE_IP
&& sap
!= ETHERTYPE_IPV6
&&
2312 sap
!= ETHERTYPE_ARP
) {
2313 BUMP_STAT(mcip
, restricted
);
2314 DTRACE_PROBE2(restricted__fail
,
2315 mac_client_impl_t
*, mcip
, mblk_t
*, mp
);
2319 if ((types
& MPT_IPNOSPOOF
) != 0) {
2320 if ((err
= ipnospoof_check(mcip
, protect
, mp
, &mhi
)) != 0) {
2321 BUMP_STAT(mcip
, ipspoofed
);
2322 DTRACE_PROBE2(ip__nospoof__fail
,
2323 mac_client_impl_t
*, mcip
, mblk_t
*, mp
);
2327 if ((types
& MPT_DHCPNOSPOOF
) != 0) {
2328 if ((err
= dhcpnospoof_check(mcip
, protect
, mp
, &mhi
)) != 0) {
2329 BUMP_STAT(mcip
, dhcpspoofed
);
2330 DTRACE_PROBE2(dhcp__nospoof__fail
,
2331 mac_client_impl_t
*, mcip
, mblk_t
*, mp
);
2339 * Enforce link protection on a packet chain.
2340 * Packets that pass the checks are returned back to the caller.
2343 mac_protect_check(mac_client_handle_t mch
, mblk_t
*mp
)
2345 mac_client_impl_t
*mcip
= (mac_client_impl_t
*)mch
;
2346 mblk_t
*ret_mp
= NULL
, **tailp
= &ret_mp
, *next
;
2349 * Skip checks if we are part of an aggr.
2351 if ((mcip
->mci_state_flags
& MCIS_IS_AGGR_PORT
) != 0)
2354 for (; mp
!= NULL
; mp
= next
) {
2358 if (mac_protect_check_one(mcip
, mp
) == 0) {
2360 tailp
= &mp
->b_next
;
2369 * Check if a particular protection type is enabled.
2372 mac_protect_enabled(mac_client_handle_t mch
, uint32_t type
)
2374 return (MAC_PROTECT_ENABLED((mac_client_impl_t
*)mch
, type
));
2378 validate_ips(mac_protect_t
*p
)
2382 if (p
->mp_ipaddrcnt
== MPT_RESET
)
2385 if (p
->mp_ipaddrcnt
> MPT_MAXIPADDR
)
2388 for (i
= 0; i
< p
->mp_ipaddrcnt
; i
++) {
2389 mac_ipaddr_t
*addr
= &p
->mp_ipaddrs
[i
];
2392 * The unspecified address is implicitly allowed so there's no
2393 * need to add it to the list. Also, validate that the netmask,
2394 * if any, is sane for the specific version of IP. A mask of
2395 * some kind is always required.
2397 if (addr
->ip_netmask
== 0)
2400 if (addr
->ip_version
== IPV4_VERSION
) {
2401 if (V4_PART_OF_V6(addr
->ip_addr
) == INADDR_ANY
)
2403 if (addr
->ip_netmask
> 32)
2405 } else if (addr
->ip_version
== IPV6_VERSION
) {
2406 if (IN6_IS_ADDR_UNSPECIFIED(&addr
->ip_addr
))
2409 if (IN6_IS_ADDR_V4MAPPED_ANY(&addr
->ip_addr
))
2412 if (addr
->ip_netmask
> 128)
2415 /* invalid ip version */
2419 for (j
= 0; j
< p
->mp_ipaddrcnt
; j
++) {
2420 mac_ipaddr_t
*addr1
= &p
->mp_ipaddrs
[j
];
2422 if (i
== j
|| addr
->ip_version
!= addr1
->ip_version
)
2425 /* found a duplicate */
2426 if ((addr
->ip_version
== IPV4_VERSION
&&
2427 V4_PART_OF_V6(addr
->ip_addr
) ==
2428 V4_PART_OF_V6(addr1
->ip_addr
)) ||
2429 IN6_ARE_ADDR_EQUAL(&addr
->ip_addr
,
2439 validate_cids(mac_protect_t
*p
)
2443 if (p
->mp_cidcnt
== MPT_RESET
)
2446 if (p
->mp_cidcnt
> MPT_MAXCID
)
2449 for (i
= 0; i
< p
->mp_cidcnt
; i
++) {
2450 mac_dhcpcid_t
*cid
= &p
->mp_cids
[i
];
2452 if (cid
->dc_len
> MPT_MAXCIDLEN
||
2453 (cid
->dc_form
!= CIDFORM_TYPED
&&
2454 cid
->dc_form
!= CIDFORM_HEX
&&
2455 cid
->dc_form
!= CIDFORM_STR
))
2458 for (j
= 0; j
< p
->mp_cidcnt
; j
++) {
2459 mac_dhcpcid_t
*cid1
= &p
->mp_cids
[j
];
2461 if (i
== j
|| cid
->dc_len
!= cid1
->dc_len
)
2464 /* found a duplicate */
2465 if (bcmp(cid
->dc_id
, cid1
->dc_id
, cid
->dc_len
) == 0)
2473 * Sanity-checks parameters given by userland.
2476 mac_protect_validate(mac_resource_props_t
*mrp
)
2478 mac_protect_t
*p
= &mrp
->mrp_protect
;
2481 /* check for invalid types */
2482 if (p
->mp_types
!= MPT_RESET
&& (p
->mp_types
& ~MPT_ALL
) != 0)
2485 if ((err
= validate_ips(p
)) != 0)
2488 if ((err
= validate_cids(p
)) != 0)
2495 * Enable/disable link protection.
2498 mac_protect_set(mac_client_handle_t mch
, mac_resource_props_t
*mrp
)
2500 mac_client_impl_t
*mcip
= (mac_client_impl_t
*)mch
;
2501 mac_impl_t
*mip
= mcip
->mci_mip
;
2502 uint_t media
= mip
->mi_info
.mi_nativemedia
;
2505 ASSERT(MAC_PERIM_HELD((mac_handle_t
)mip
));
2507 /* tunnels are not supported */
2508 if (media
== DL_IPV4
|| media
== DL_IPV6
|| media
== DL_6TO4
)
2511 if ((err
= mac_protect_validate(mrp
)) != 0)
2517 mac_update_resources(mrp
, MCIP_RESOURCE_PROPS(mcip
), B_FALSE
);
2518 i_mac_notify(((mcip
->mci_state_flags
& MCIS_IS_VNIC
) != 0 ?
2519 mcip
->mci_upper_mip
: mip
), MAC_NOTE_ALLOWED_IPS
);
2524 mac_protect_update(mac_resource_props_t
*new, mac_resource_props_t
*curr
)
2526 mac_protect_t
*np
= &new->mrp_protect
;
2527 mac_protect_t
*cp
= &curr
->mrp_protect
;
2528 uint32_t types
= np
->mp_types
;
2530 if (types
== MPT_RESET
) {
2532 curr
->mrp_mask
&= ~MRP_PROTECT
;
2535 cp
->mp_types
= types
;
2536 curr
->mrp_mask
|= MRP_PROTECT
;
2539 if (np
->mp_ipaddrcnt
!= 0) {
2540 if (np
->mp_ipaddrcnt
<= MPT_MAXIPADDR
) {
2541 bcopy(np
->mp_ipaddrs
, cp
->mp_ipaddrs
,
2542 sizeof (cp
->mp_ipaddrs
));
2543 cp
->mp_ipaddrcnt
= np
->mp_ipaddrcnt
;
2544 } else if (np
->mp_ipaddrcnt
== MPT_RESET
) {
2545 bzero(cp
->mp_ipaddrs
, sizeof (cp
->mp_ipaddrs
));
2546 cp
->mp_ipaddrcnt
= 0;
2549 if (np
->mp_cidcnt
!= 0) {
2550 if (np
->mp_cidcnt
<= MPT_MAXCID
) {
2551 bcopy(np
->mp_cids
, cp
->mp_cids
, sizeof (cp
->mp_cids
));
2552 cp
->mp_cidcnt
= np
->mp_cidcnt
;
2553 } else if (np
->mp_cidcnt
== MPT_RESET
) {
2554 bzero(cp
->mp_cids
, sizeof (cp
->mp_cids
));
2561 mac_protect_init(mac_client_impl_t
*mcip
)
2563 mutex_init(&mcip
->mci_protect_lock
, NULL
, MUTEX_DRIVER
, NULL
);
2564 mcip
->mci_protect_flags
= 0;
2565 mcip
->mci_txn_cleanup_tid
= 0;
2566 avl_create(&mcip
->mci_v4_pending_txn
, compare_dhcpv4_xid
,
2567 sizeof (dhcpv4_txn_t
), offsetof(dhcpv4_txn_t
, dt_node
));
2568 avl_create(&mcip
->mci_v4_completed_txn
, compare_dhcpv4_cid
,
2569 sizeof (dhcpv4_txn_t
), offsetof(dhcpv4_txn_t
, dt_node
));
2570 avl_create(&mcip
->mci_v4_dyn_ip
, compare_dhcpv4_ip
,
2571 sizeof (dhcpv4_txn_t
), offsetof(dhcpv4_txn_t
, dt_ipnode
));
2572 avl_create(&mcip
->mci_v6_pending_txn
, compare_dhcpv6_xid
,
2573 sizeof (dhcpv6_txn_t
), offsetof(dhcpv6_txn_t
, dt_node
));
2574 avl_create(&mcip
->mci_v6_cid
, compare_dhcpv6_cid
,
2575 sizeof (dhcpv6_cid_t
), offsetof(dhcpv6_cid_t
, dc_node
));
2576 avl_create(&mcip
->mci_v6_dyn_ip
, compare_dhcpv6_ip
,
2577 sizeof (dhcpv6_addr_t
), offsetof(dhcpv6_addr_t
, da_node
));
2578 avl_create(&mcip
->mci_v6_slaac_ip
, compare_slaac_ip
,
2579 sizeof (slaac_addr_t
), offsetof(slaac_addr_t
, sla_node
));
2581 if (mcip
->mci_state_flags
& MCIS_IS_VNIC
)
2582 mcip
->mci_protect_flags
|= MPT_FLAG_PROMISC_FILTERED
;
2586 mac_protect_fini(mac_client_impl_t
*mcip
)
2588 avl_destroy(&mcip
->mci_v6_dyn_ip
);
2589 avl_destroy(&mcip
->mci_v6_cid
);
2590 avl_destroy(&mcip
->mci_v6_pending_txn
);
2591 avl_destroy(&mcip
->mci_v4_dyn_ip
);
2592 avl_destroy(&mcip
->mci_v4_completed_txn
);
2593 avl_destroy(&mcip
->mci_v4_pending_txn
);
2594 avl_destroy(&mcip
->mci_v6_slaac_ip
);
2595 mcip
->mci_txn_cleanup_tid
= 0;
2596 mcip
->mci_protect_flags
= 0;
2597 mutex_destroy(&mcip
->mci_protect_lock
);
2601 allowed_ips_set(mac_resource_props_t
*mrp
, uint32_t af
)
2605 for (i
= 0; i
< mrp
->mrp_protect
.mp_ipaddrcnt
; i
++) {
2606 if (mrp
->mrp_protect
.mp_ipaddrs
[i
].ip_version
== af
)
2613 mac_protect_get(mac_handle_t mh
)
2615 mac_impl_t
*mip
= (mac_impl_t
*)mh
;
2617 return (&mip
->mi_resource_props
.mrp_protect
);