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