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