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