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