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