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