5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26 #include <sys/strsun.h>
27 #include <sys/sdt.h>
28 #include <sys/mac.h>
29 #include <sys/mac_impl.h>
30 #include <sys/mac_client_impl.h>
31 #include <sys/mac_client_priv.h>
32 #include <sys/ethernet.h>
33 #include <sys/vlan.h>
34 #include <sys/dlpi.h>
35 #include <sys/avl.h>
36 #include <inet/ip.h>
37 #include <inet/ip6.h>
38 #include <inet/arp.h>
39 #include <netinet/arp.h>
40 #include <netinet/udp.h>
41 #include <netinet/dhcp.h>
42 #include <netinet/dhcp6.h>
43
44 /*
124 * Packets with a source address matching one of the DHCPv6-assigned
125 * addresses will be allowed through.
126 *
127 * IPv6 notes:
128 *
129 * The v6 code shares the same timer as v4 for scrubbing stale transactions.
130 * Just like v4, as part of removing an expired transaction, a RELEASE will be
131 * be triggered on the cid associated with the expired transaction.
132 *
133 * The data structures used for v6 are slightly different because a v6 client
134 * may have multiple addresses associated with it.
135 */
136
137 /*
138 * These are just arbitrary limits meant for preventing abuse (e.g. a user
139 * flooding the network with bogus transactions). They are not meant to be
140 * user-modifiable so they are not exposed as linkprops.
141 */
142 static ulong_t dhcp_max_pending_txn = 512;
143 static ulong_t dhcp_max_completed_txn = 512;
144 static time_t txn_cleanup_interval = 60;
145
146 /*
147 * DHCPv4 transaction. It may be added to three different tables
148 * (keyed by different fields).
149 */
150 typedef struct dhcpv4_txn {
151 uint32_t dt_xid;
152 time_t dt_timestamp;
153 uint8_t dt_cid[DHCP_MAX_OPT_SIZE];
154 uint8_t dt_cid_len;
155 ipaddr_t dt_ipaddr;
156 avl_node_t dt_node;
157 avl_node_t dt_ipnode;
158 struct dhcpv4_txn *dt_next;
159 } dhcpv4_txn_t;
160
161 /*
162 * DHCPv6 address. May be added to mci_v6_dyn_ip.
163 * It is always pointed to by its parent dhcpv6_cid_t structure.
164 */
165 typedef struct dhcpv6_addr {
166 in6_addr_t da_addr;
167 avl_node_t da_node;
168 struct dhcpv6_addr *da_next;
169 } dhcpv6_addr_t;
170
171 /*
172 * DHCPv6 client ID. May be added to mci_v6_cid.
173 * No dhcpv6_txn_t should be pointing to it after it is added to mci_v6_cid.
174 */
175 typedef struct dhcpv6_cid {
176 uchar_t *dc_cid;
177 uint_t dc_cid_len;
178 dhcpv6_addr_t *dc_addr;
179 uint_t dc_addrcnt;
180 avl_node_t dc_node;
181 } dhcpv6_cid_t;
182
183 /*
184 * DHCPv6 transaction. Unlike its v4 counterpart, this object gets freed up
185 * as soon as the transaction completes or expires.
186 */
187 typedef struct dhcpv6_txn {
188 uint32_t dt_xid;
189 time_t dt_timestamp;
190 dhcpv6_cid_t *dt_cid;
191 avl_node_t dt_node;
192 struct dhcpv6_txn *dt_next;
193 } dhcpv6_txn_t;
194
195 static void start_txn_cleanup_timer(mac_client_impl_t *);
196 static boolean_t allowed_ips_set(mac_resource_props_t *, uint32_t);
197
198 #define BUMP_STAT(m, s) (m)->mci_misc_stat.mms_##s++
199
200 /*
201 * Comparison functions for the 3 AVL trees used:
202 * mci_v4_pending_txn, mci_v4_completed_txn, mci_v4_dyn_ip
203 */
204 static int
205 compare_dhcpv4_xid(const void *arg1, const void *arg2)
206 {
207 const dhcpv4_txn_t *txn1 = arg1, *txn2 = arg2;
208
209 if (txn1->dt_xid < txn2->dt_xid)
438
439 mutex_enter(&mcip->mci_protect_lock);
440 tmp_txn.dt_ipaddr = ipaddr;
441 txn = avl_find(&mcip->mci_v4_dyn_ip, &tmp_txn, NULL);
442 mutex_exit(&mcip->mci_protect_lock);
443 return (txn != NULL);
444 }
445
446 /*
447 * Create/destroy a DHCPv4 transaction.
448 */
449 static dhcpv4_txn_t *
450 create_dhcpv4_txn(uint32_t xid, uint8_t *cid, uint8_t cid_len, ipaddr_t ipaddr)
451 {
452 dhcpv4_txn_t *txn;
453
454 if ((txn = kmem_zalloc(sizeof (*txn), KM_NOSLEEP)) == NULL)
455 return (NULL);
456
457 txn->dt_xid = xid;
458 txn->dt_timestamp = ddi_get_time();
459 if (cid_len > 0)
460 bcopy(cid, &txn->dt_cid, cid_len);
461 txn->dt_cid_len = cid_len;
462 txn->dt_ipaddr = ipaddr;
463 return (txn);
464 }
465
466 static void
467 free_dhcpv4_txn(dhcpv4_txn_t *txn)
468 {
469 kmem_free(txn, sizeof (*txn));
470 }
471
472 /*
473 * Clean up all v4 tables.
474 */
475 static void
476 flush_dhcpv4(mac_client_impl_t *mcip)
477 {
478 void *cookie = NULL;
495 while ((txn = avl_destroy_nodes(&mcip->mci_v4_pending_txn,
496 &cookie)) != NULL) {
497 free_dhcpv4_txn(txn);
498 }
499 }
500
501 /*
502 * Cleanup stale DHCPv4 transactions.
503 */
504 static void
505 txn_cleanup_v4(mac_client_impl_t *mcip)
506 {
507 dhcpv4_txn_t *txn, *ctxn, *next, *txn_list = NULL;
508
509 /*
510 * Find stale pending transactions and place them on a list
511 * to be removed.
512 */
513 for (txn = avl_first(&mcip->mci_v4_pending_txn); txn != NULL;
514 txn = avl_walk(&mcip->mci_v4_pending_txn, txn, AVL_AFTER)) {
515 if (ddi_get_time() - txn->dt_timestamp >
516 txn_cleanup_interval) {
517 DTRACE_PROBE2(found__expired__txn,
518 mac_client_impl_t *, mcip,
519 dhcpv4_txn_t *, txn);
520
521 txn->dt_next = txn_list;
522 txn_list = txn;
523 }
524 }
525
526 /*
527 * Remove and free stale pending transactions and completed
528 * transactions with the same client IDs as the stale transactions.
529 */
530 for (txn = txn_list; txn != NULL; txn = next) {
531 avl_remove(&mcip->mci_v4_pending_txn, txn);
532
533 ctxn = find_dhcpv4_completed_txn(mcip, txn->dt_cid,
534 txn->dt_cid_len);
535 if (ctxn != NULL) {
536 DTRACE_PROBE2(removing__completed__txn,
600 /* flush any completed txn with this cid */
601 ctxn = find_dhcpv4_completed_txn(mcip, cid, cid_len);
602 if (ctxn != NULL) {
603 DTRACE_PROBE2(release__successful, mac_client_impl_t *,
604 mcip, struct dhcp *, dh4);
605
606 remove_dhcpv4_completed_txn(mcip, ctxn);
607 free_dhcpv4_txn(ctxn);
608 }
609 goto done;
610 }
611
612 /*
613 * If a pending txn already exists, we'll update its timestamp so
614 * it won't get flushed by the timer. We don't need to create new
615 * txns for retransmissions.
616 */
617 if ((txn = find_dhcpv4_pending_txn(mcip, dh4->xid)) != NULL) {
618 DTRACE_PROBE2(update, mac_client_impl_t *, mcip,
619 dhcpv4_txn_t *, txn);
620 txn->dt_timestamp = ddi_get_time();
621 goto done;
622 }
623
624 if (get_dhcpv4_option(dh4, end, CD_REQUESTED_IP_ADDR,
625 &opt, &opt_len) != 0 || opt_len != sizeof (ipaddr)) {
626 DTRACE_PROBE2(ipaddr__not__found, mac_client_impl_t *, mcip,
627 struct dhcp *, dh4);
628 goto done;
629 }
630 bcopy(opt, &ipaddr, sizeof (ipaddr));
631 if ((txn = create_dhcpv4_txn(dh4->xid, cid, cid_len, ipaddr)) == NULL)
632 goto done;
633
634 if (insert_dhcpv4_pending_txn(mcip, txn) != 0) {
635 DTRACE_PROBE2(insert__failed, mac_client_impl_t *, mcip,
636 dhcpv4_txn_t *, txn);
637 free_dhcpv4_txn(txn);
638 goto done;
639 }
640 start_txn_cleanup_timer(mcip);
1099 return (avl_find(&mcip->mci_v6_pending_txn, &tmp_txn, NULL));
1100 }
1101
1102 static void
1103 remove_dhcpv6_pending_txn(mac_client_impl_t *mcip, dhcpv6_txn_t *txn)
1104 {
1105 ASSERT(MUTEX_HELD(&mcip->mci_protect_lock));
1106 avl_remove(&mcip->mci_v6_pending_txn, txn);
1107 }
1108
1109 static dhcpv6_txn_t *
1110 create_dhcpv6_txn(uint32_t xid, dhcpv6_cid_t *cid)
1111 {
1112 dhcpv6_txn_t *txn;
1113
1114 if ((txn = kmem_zalloc(sizeof (dhcpv6_txn_t), KM_NOSLEEP)) == NULL)
1115 return (NULL);
1116
1117 txn->dt_xid = xid;
1118 txn->dt_cid = cid;
1119 txn->dt_timestamp = ddi_get_time();
1120 return (txn);
1121 }
1122
1123 static void
1124 free_dhcpv6_txn(dhcpv6_txn_t *txn)
1125 {
1126 if (txn->dt_cid != NULL)
1127 free_dhcpv6_cid(txn->dt_cid);
1128 kmem_free(txn, sizeof (dhcpv6_txn_t));
1129 }
1130
1131 static int
1132 insert_dhcpv6_pending_txn(mac_client_impl_t *mcip, dhcpv6_txn_t *txn)
1133 {
1134 avl_index_t where;
1135
1136 ASSERT(MUTEX_HELD(&mcip->mci_protect_lock));
1137 if (avl_find(&mcip->mci_v6_pending_txn, txn, &where) != NULL)
1138 return (EEXIST);
1139
1166 while ((txn = avl_destroy_nodes(&mcip->mci_v6_pending_txn,
1167 &cookie)) != NULL) {
1168 free_dhcpv6_txn(txn);
1169 }
1170 }
1171
1172 /*
1173 * Cleanup stale DHCPv6 transactions.
1174 */
1175 static void
1176 txn_cleanup_v6(mac_client_impl_t *mcip)
1177 {
1178 dhcpv6_txn_t *txn, *next, *txn_list = NULL;
1179
1180 /*
1181 * Find stale pending transactions and place them on a list
1182 * to be removed.
1183 */
1184 for (txn = avl_first(&mcip->mci_v6_pending_txn); txn != NULL;
1185 txn = avl_walk(&mcip->mci_v6_pending_txn, txn, AVL_AFTER)) {
1186 if (ddi_get_time() - txn->dt_timestamp >
1187 txn_cleanup_interval) {
1188 DTRACE_PROBE2(found__expired__txn,
1189 mac_client_impl_t *, mcip,
1190 dhcpv6_txn_t *, txn);
1191
1192 txn->dt_next = txn_list;
1193 txn_list = txn;
1194 }
1195 }
1196
1197 /*
1198 * Remove and free stale pending transactions.
1199 * Release any existing cids matching the stale transactions.
1200 */
1201 for (txn = txn_list; txn != NULL; txn = next) {
1202 avl_remove(&mcip->mci_v6_pending_txn, txn);
1203 release_dhcpv6_cid(mcip, txn->dt_cid);
1204 next = txn->dt_next;
1205 txn->dt_next = NULL;
1206
1207 DTRACE_PROBE2(freeing__txn, mac_client_impl_t *, mcip,
1231 if (allowed_ips_set(mrp, IPV6_VERSION))
1232 return (B_FALSE);
1233
1234 mtype = dh6->d6m_msg_type;
1235 if (mtype != DHCPV6_MSG_REQUEST && mtype != DHCPV6_MSG_RENEW &&
1236 mtype != DHCPV6_MSG_REBIND && mtype != DHCPV6_MSG_RELEASE)
1237 return (B_TRUE);
1238
1239 if ((cid = create_dhcpv6_cid(dh6, end)) == NULL)
1240 return (B_TRUE);
1241
1242 mutex_enter(&mcip->mci_protect_lock);
1243 if (mtype == DHCPV6_MSG_RELEASE) {
1244 release_dhcpv6_cid(mcip, cid);
1245 goto done;
1246 }
1247 xid = DHCPV6_GET_TRANSID(dh6);
1248 if ((txn = find_dhcpv6_pending_txn(mcip, xid)) != NULL) {
1249 DTRACE_PROBE2(update, mac_client_impl_t *, mcip,
1250 dhcpv6_txn_t *, txn);
1251 txn->dt_timestamp = ddi_get_time();
1252 goto done;
1253 }
1254 if ((txn = create_dhcpv6_txn(xid, cid)) == NULL)
1255 goto done;
1256
1257 cid = NULL;
1258 if (insert_dhcpv6_pending_txn(mcip, txn) != 0) {
1259 DTRACE_PROBE2(insert__failed, mac_client_impl_t *, mcip,
1260 dhcpv6_txn_t *, txn);
1261 free_dhcpv6_txn(txn);
1262 goto done;
1263 }
1264 start_txn_cleanup_timer(mcip);
1265
1266 DTRACE_PROBE2(txn__pending, mac_client_impl_t *, mcip,
1267 dhcpv6_txn_t *, txn);
1268
1269 done:
1270 if (cid != NULL)
1271 free_dhcpv6_cid(cid);
1340
1341 mutex_enter(&mcip->mci_protect_lock);
1342 if (mcip->mci_txn_cleanup_tid == 0) {
1343 /* do nothing if timer got cancelled */
1344 mutex_exit(&mcip->mci_protect_lock);
1345 return;
1346 }
1347 mcip->mci_txn_cleanup_tid = 0;
1348
1349 txn_cleanup_v4(mcip);
1350 txn_cleanup_v6(mcip);
1351
1352 /*
1353 * Restart timer if pending transactions still exist.
1354 */
1355 if (!avl_is_empty(&mcip->mci_v4_pending_txn) ||
1356 !avl_is_empty(&mcip->mci_v6_pending_txn)) {
1357 DTRACE_PROBE1(restarting__timer, mac_client_impl_t *, mcip);
1358
1359 mcip->mci_txn_cleanup_tid = timeout(txn_cleanup_timer, mcip,
1360 drv_usectohz(txn_cleanup_interval * 1000000));
1361 }
1362 mutex_exit(&mcip->mci_protect_lock);
1363 }
1364
1365 static void
1366 start_txn_cleanup_timer(mac_client_impl_t *mcip)
1367 {
1368 ASSERT(MUTEX_HELD(&mcip->mci_protect_lock));
1369 if (mcip->mci_txn_cleanup_tid == 0) {
1370 mcip->mci_txn_cleanup_tid = timeout(txn_cleanup_timer, mcip,
1371 drv_usectohz(txn_cleanup_interval * 1000000));
1372 }
1373 }
1374
1375 static void
1376 cancel_txn_cleanup_timer(mac_client_impl_t *mcip)
1377 {
1378 timeout_id_t tid;
1379
1380 ASSERT(MUTEX_HELD(&mcip->mci_protect_lock));
1381
1382 /*
1383 * This needs to be a while loop because the timer could get
1384 * rearmed during untimeout().
1385 */
1386 while ((tid = mcip->mci_txn_cleanup_tid) != 0) {
1387 mcip->mci_txn_cleanup_tid = 0;
1388 mutex_exit(&mcip->mci_protect_lock);
1389 (void) untimeout(tid);
1390 mutex_enter(&mcip->mci_protect_lock);
1391 }
|
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25 /*
26 * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
27 */
28
29 #include <sys/strsun.h>
30 #include <sys/sdt.h>
31 #include <sys/mac.h>
32 #include <sys/mac_impl.h>
33 #include <sys/mac_client_impl.h>
34 #include <sys/mac_client_priv.h>
35 #include <sys/ethernet.h>
36 #include <sys/vlan.h>
37 #include <sys/dlpi.h>
38 #include <sys/avl.h>
39 #include <inet/ip.h>
40 #include <inet/ip6.h>
41 #include <inet/arp.h>
42 #include <netinet/arp.h>
43 #include <netinet/udp.h>
44 #include <netinet/dhcp.h>
45 #include <netinet/dhcp6.h>
46
47 /*
127 * Packets with a source address matching one of the DHCPv6-assigned
128 * addresses will be allowed through.
129 *
130 * IPv6 notes:
131 *
132 * The v6 code shares the same timer as v4 for scrubbing stale transactions.
133 * Just like v4, as part of removing an expired transaction, a RELEASE will be
134 * be triggered on the cid associated with the expired transaction.
135 *
136 * The data structures used for v6 are slightly different because a v6 client
137 * may have multiple addresses associated with it.
138 */
139
140 /*
141 * These are just arbitrary limits meant for preventing abuse (e.g. a user
142 * flooding the network with bogus transactions). They are not meant to be
143 * user-modifiable so they are not exposed as linkprops.
144 */
145 static ulong_t dhcp_max_pending_txn = 512;
146 static ulong_t dhcp_max_completed_txn = 512;
147 static hrtime_t txn_cleanup_interval = 60 * NANOSEC;
148
149 /*
150 * DHCPv4 transaction. It may be added to three different tables
151 * (keyed by different fields).
152 */
153 typedef struct dhcpv4_txn {
154 uint32_t dt_xid;
155 hrtime_t dt_timestamp;
156 uint8_t dt_cid[DHCP_MAX_OPT_SIZE];
157 uint8_t dt_cid_len;
158 ipaddr_t dt_ipaddr;
159 avl_node_t dt_node;
160 avl_node_t dt_ipnode;
161 struct dhcpv4_txn *dt_next;
162 } dhcpv4_txn_t;
163
164 /*
165 * DHCPv6 address. May be added to mci_v6_dyn_ip.
166 * It is always pointed to by its parent dhcpv6_cid_t structure.
167 */
168 typedef struct dhcpv6_addr {
169 in6_addr_t da_addr;
170 avl_node_t da_node;
171 struct dhcpv6_addr *da_next;
172 } dhcpv6_addr_t;
173
174 /*
175 * DHCPv6 client ID. May be added to mci_v6_cid.
176 * No dhcpv6_txn_t should be pointing to it after it is added to mci_v6_cid.
177 */
178 typedef struct dhcpv6_cid {
179 uchar_t *dc_cid;
180 uint_t dc_cid_len;
181 dhcpv6_addr_t *dc_addr;
182 uint_t dc_addrcnt;
183 avl_node_t dc_node;
184 } dhcpv6_cid_t;
185
186 /*
187 * DHCPv6 transaction. Unlike its v4 counterpart, this object gets freed up
188 * as soon as the transaction completes or expires.
189 */
190 typedef struct dhcpv6_txn {
191 uint32_t dt_xid;
192 hrtime_t dt_timestamp;
193 dhcpv6_cid_t *dt_cid;
194 avl_node_t dt_node;
195 struct dhcpv6_txn *dt_next;
196 } dhcpv6_txn_t;
197
198 static void start_txn_cleanup_timer(mac_client_impl_t *);
199 static boolean_t allowed_ips_set(mac_resource_props_t *, uint32_t);
200
201 #define BUMP_STAT(m, s) (m)->mci_misc_stat.mms_##s++
202
203 /*
204 * Comparison functions for the 3 AVL trees used:
205 * mci_v4_pending_txn, mci_v4_completed_txn, mci_v4_dyn_ip
206 */
207 static int
208 compare_dhcpv4_xid(const void *arg1, const void *arg2)
209 {
210 const dhcpv4_txn_t *txn1 = arg1, *txn2 = arg2;
211
212 if (txn1->dt_xid < txn2->dt_xid)
441
442 mutex_enter(&mcip->mci_protect_lock);
443 tmp_txn.dt_ipaddr = ipaddr;
444 txn = avl_find(&mcip->mci_v4_dyn_ip, &tmp_txn, NULL);
445 mutex_exit(&mcip->mci_protect_lock);
446 return (txn != NULL);
447 }
448
449 /*
450 * Create/destroy a DHCPv4 transaction.
451 */
452 static dhcpv4_txn_t *
453 create_dhcpv4_txn(uint32_t xid, uint8_t *cid, uint8_t cid_len, ipaddr_t ipaddr)
454 {
455 dhcpv4_txn_t *txn;
456
457 if ((txn = kmem_zalloc(sizeof (*txn), KM_NOSLEEP)) == NULL)
458 return (NULL);
459
460 txn->dt_xid = xid;
461 txn->dt_timestamp = gethrtime();
462 if (cid_len > 0)
463 bcopy(cid, &txn->dt_cid, cid_len);
464 txn->dt_cid_len = cid_len;
465 txn->dt_ipaddr = ipaddr;
466 return (txn);
467 }
468
469 static void
470 free_dhcpv4_txn(dhcpv4_txn_t *txn)
471 {
472 kmem_free(txn, sizeof (*txn));
473 }
474
475 /*
476 * Clean up all v4 tables.
477 */
478 static void
479 flush_dhcpv4(mac_client_impl_t *mcip)
480 {
481 void *cookie = NULL;
498 while ((txn = avl_destroy_nodes(&mcip->mci_v4_pending_txn,
499 &cookie)) != NULL) {
500 free_dhcpv4_txn(txn);
501 }
502 }
503
504 /*
505 * Cleanup stale DHCPv4 transactions.
506 */
507 static void
508 txn_cleanup_v4(mac_client_impl_t *mcip)
509 {
510 dhcpv4_txn_t *txn, *ctxn, *next, *txn_list = NULL;
511
512 /*
513 * Find stale pending transactions and place them on a list
514 * to be removed.
515 */
516 for (txn = avl_first(&mcip->mci_v4_pending_txn); txn != NULL;
517 txn = avl_walk(&mcip->mci_v4_pending_txn, txn, AVL_AFTER)) {
518 if (gethrtime() - txn->dt_timestamp > txn_cleanup_interval) {
519 DTRACE_PROBE2(found__expired__txn,
520 mac_client_impl_t *, mcip,
521 dhcpv4_txn_t *, txn);
522
523 txn->dt_next = txn_list;
524 txn_list = txn;
525 }
526 }
527
528 /*
529 * Remove and free stale pending transactions and completed
530 * transactions with the same client IDs as the stale transactions.
531 */
532 for (txn = txn_list; txn != NULL; txn = next) {
533 avl_remove(&mcip->mci_v4_pending_txn, txn);
534
535 ctxn = find_dhcpv4_completed_txn(mcip, txn->dt_cid,
536 txn->dt_cid_len);
537 if (ctxn != NULL) {
538 DTRACE_PROBE2(removing__completed__txn,
602 /* flush any completed txn with this cid */
603 ctxn = find_dhcpv4_completed_txn(mcip, cid, cid_len);
604 if (ctxn != NULL) {
605 DTRACE_PROBE2(release__successful, mac_client_impl_t *,
606 mcip, struct dhcp *, dh4);
607
608 remove_dhcpv4_completed_txn(mcip, ctxn);
609 free_dhcpv4_txn(ctxn);
610 }
611 goto done;
612 }
613
614 /*
615 * If a pending txn already exists, we'll update its timestamp so
616 * it won't get flushed by the timer. We don't need to create new
617 * txns for retransmissions.
618 */
619 if ((txn = find_dhcpv4_pending_txn(mcip, dh4->xid)) != NULL) {
620 DTRACE_PROBE2(update, mac_client_impl_t *, mcip,
621 dhcpv4_txn_t *, txn);
622 txn->dt_timestamp = gethrtime();
623 goto done;
624 }
625
626 if (get_dhcpv4_option(dh4, end, CD_REQUESTED_IP_ADDR,
627 &opt, &opt_len) != 0 || opt_len != sizeof (ipaddr)) {
628 DTRACE_PROBE2(ipaddr__not__found, mac_client_impl_t *, mcip,
629 struct dhcp *, dh4);
630 goto done;
631 }
632 bcopy(opt, &ipaddr, sizeof (ipaddr));
633 if ((txn = create_dhcpv4_txn(dh4->xid, cid, cid_len, ipaddr)) == NULL)
634 goto done;
635
636 if (insert_dhcpv4_pending_txn(mcip, txn) != 0) {
637 DTRACE_PROBE2(insert__failed, mac_client_impl_t *, mcip,
638 dhcpv4_txn_t *, txn);
639 free_dhcpv4_txn(txn);
640 goto done;
641 }
642 start_txn_cleanup_timer(mcip);
1101 return (avl_find(&mcip->mci_v6_pending_txn, &tmp_txn, NULL));
1102 }
1103
1104 static void
1105 remove_dhcpv6_pending_txn(mac_client_impl_t *mcip, dhcpv6_txn_t *txn)
1106 {
1107 ASSERT(MUTEX_HELD(&mcip->mci_protect_lock));
1108 avl_remove(&mcip->mci_v6_pending_txn, txn);
1109 }
1110
1111 static dhcpv6_txn_t *
1112 create_dhcpv6_txn(uint32_t xid, dhcpv6_cid_t *cid)
1113 {
1114 dhcpv6_txn_t *txn;
1115
1116 if ((txn = kmem_zalloc(sizeof (dhcpv6_txn_t), KM_NOSLEEP)) == NULL)
1117 return (NULL);
1118
1119 txn->dt_xid = xid;
1120 txn->dt_cid = cid;
1121 txn->dt_timestamp = gethrtime();
1122 return (txn);
1123 }
1124
1125 static void
1126 free_dhcpv6_txn(dhcpv6_txn_t *txn)
1127 {
1128 if (txn->dt_cid != NULL)
1129 free_dhcpv6_cid(txn->dt_cid);
1130 kmem_free(txn, sizeof (dhcpv6_txn_t));
1131 }
1132
1133 static int
1134 insert_dhcpv6_pending_txn(mac_client_impl_t *mcip, dhcpv6_txn_t *txn)
1135 {
1136 avl_index_t where;
1137
1138 ASSERT(MUTEX_HELD(&mcip->mci_protect_lock));
1139 if (avl_find(&mcip->mci_v6_pending_txn, txn, &where) != NULL)
1140 return (EEXIST);
1141
1168 while ((txn = avl_destroy_nodes(&mcip->mci_v6_pending_txn,
1169 &cookie)) != NULL) {
1170 free_dhcpv6_txn(txn);
1171 }
1172 }
1173
1174 /*
1175 * Cleanup stale DHCPv6 transactions.
1176 */
1177 static void
1178 txn_cleanup_v6(mac_client_impl_t *mcip)
1179 {
1180 dhcpv6_txn_t *txn, *next, *txn_list = NULL;
1181
1182 /*
1183 * Find stale pending transactions and place them on a list
1184 * to be removed.
1185 */
1186 for (txn = avl_first(&mcip->mci_v6_pending_txn); txn != NULL;
1187 txn = avl_walk(&mcip->mci_v6_pending_txn, txn, AVL_AFTER)) {
1188 if (gethrtime() - txn->dt_timestamp > txn_cleanup_interval) {
1189 DTRACE_PROBE2(found__expired__txn,
1190 mac_client_impl_t *, mcip,
1191 dhcpv6_txn_t *, txn);
1192
1193 txn->dt_next = txn_list;
1194 txn_list = txn;
1195 }
1196 }
1197
1198 /*
1199 * Remove and free stale pending transactions.
1200 * Release any existing cids matching the stale transactions.
1201 */
1202 for (txn = txn_list; txn != NULL; txn = next) {
1203 avl_remove(&mcip->mci_v6_pending_txn, txn);
1204 release_dhcpv6_cid(mcip, txn->dt_cid);
1205 next = txn->dt_next;
1206 txn->dt_next = NULL;
1207
1208 DTRACE_PROBE2(freeing__txn, mac_client_impl_t *, mcip,
1232 if (allowed_ips_set(mrp, IPV6_VERSION))
1233 return (B_FALSE);
1234
1235 mtype = dh6->d6m_msg_type;
1236 if (mtype != DHCPV6_MSG_REQUEST && mtype != DHCPV6_MSG_RENEW &&
1237 mtype != DHCPV6_MSG_REBIND && mtype != DHCPV6_MSG_RELEASE)
1238 return (B_TRUE);
1239
1240 if ((cid = create_dhcpv6_cid(dh6, end)) == NULL)
1241 return (B_TRUE);
1242
1243 mutex_enter(&mcip->mci_protect_lock);
1244 if (mtype == DHCPV6_MSG_RELEASE) {
1245 release_dhcpv6_cid(mcip, cid);
1246 goto done;
1247 }
1248 xid = DHCPV6_GET_TRANSID(dh6);
1249 if ((txn = find_dhcpv6_pending_txn(mcip, xid)) != NULL) {
1250 DTRACE_PROBE2(update, mac_client_impl_t *, mcip,
1251 dhcpv6_txn_t *, txn);
1252 txn->dt_timestamp = gethrtime();
1253 goto done;
1254 }
1255 if ((txn = create_dhcpv6_txn(xid, cid)) == NULL)
1256 goto done;
1257
1258 cid = NULL;
1259 if (insert_dhcpv6_pending_txn(mcip, txn) != 0) {
1260 DTRACE_PROBE2(insert__failed, mac_client_impl_t *, mcip,
1261 dhcpv6_txn_t *, txn);
1262 free_dhcpv6_txn(txn);
1263 goto done;
1264 }
1265 start_txn_cleanup_timer(mcip);
1266
1267 DTRACE_PROBE2(txn__pending, mac_client_impl_t *, mcip,
1268 dhcpv6_txn_t *, txn);
1269
1270 done:
1271 if (cid != NULL)
1272 free_dhcpv6_cid(cid);
1341
1342 mutex_enter(&mcip->mci_protect_lock);
1343 if (mcip->mci_txn_cleanup_tid == 0) {
1344 /* do nothing if timer got cancelled */
1345 mutex_exit(&mcip->mci_protect_lock);
1346 return;
1347 }
1348 mcip->mci_txn_cleanup_tid = 0;
1349
1350 txn_cleanup_v4(mcip);
1351 txn_cleanup_v6(mcip);
1352
1353 /*
1354 * Restart timer if pending transactions still exist.
1355 */
1356 if (!avl_is_empty(&mcip->mci_v4_pending_txn) ||
1357 !avl_is_empty(&mcip->mci_v6_pending_txn)) {
1358 DTRACE_PROBE1(restarting__timer, mac_client_impl_t *, mcip);
1359
1360 mcip->mci_txn_cleanup_tid = timeout(txn_cleanup_timer, mcip,
1361 drv_usectohz(txn_cleanup_interval / (NANOSEC / MICROSEC)));
1362 }
1363 mutex_exit(&mcip->mci_protect_lock);
1364 }
1365
1366 static void
1367 start_txn_cleanup_timer(mac_client_impl_t *mcip)
1368 {
1369 ASSERT(MUTEX_HELD(&mcip->mci_protect_lock));
1370 if (mcip->mci_txn_cleanup_tid == 0) {
1371 mcip->mci_txn_cleanup_tid = timeout(txn_cleanup_timer, mcip,
1372 drv_usectohz(txn_cleanup_interval / (NANOSEC / MICROSEC)));
1373 }
1374 }
1375
1376 static void
1377 cancel_txn_cleanup_timer(mac_client_impl_t *mcip)
1378 {
1379 timeout_id_t tid;
1380
1381 ASSERT(MUTEX_HELD(&mcip->mci_protect_lock));
1382
1383 /*
1384 * This needs to be a while loop because the timer could get
1385 * rearmed during untimeout().
1386 */
1387 while ((tid = mcip->mci_txn_cleanup_tid) != 0) {
1388 mcip->mci_txn_cleanup_tid = 0;
1389 mutex_exit(&mcip->mci_protect_lock);
1390 (void) untimeout(tid);
1391 mutex_enter(&mcip->mci_protect_lock);
1392 }
|