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