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