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