1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #include <sys/types.h> 28 #include <sys/conf.h> 29 #include <sys/time.h> 30 #include <sys/taskq.h> 31 #include <sys/cmn_err.h> 32 #include <sys/sdt.h> 33 #include <sys/atomic.h> 34 #include <netinet/in.h> 35 #include <inet/ip.h> 36 #include <inet/ip6.h> 37 #include <inet/tcp.h> 38 #include <inet/udp_impl.h> 39 #include <inet/ilb.h> 40 41 #include "ilb_stack.h" 42 #include "ilb_impl.h" 43 #include "ilb_conn.h" 44 #include "ilb_nat.h" 45 46 /* 47 * Timer struct for ilb_conn_t and ilb_sticky_t garbage collection 48 * 49 * start: starting index into the hash table to do gc 50 * end: ending index into the hash table to do gc 51 * ilbs: pointer to the ilb_stack_t of the IP stack 52 * tid_lock: mutex to protect the timer id. 53 * tid: timer id of the timer 54 */ 55 typedef struct ilb_timer_s { 56 uint32_t start; 57 uint32_t end; 58 ilb_stack_t *ilbs; 59 kmutex_t tid_lock; 60 timeout_id_t tid; 61 } ilb_timer_t; 62 63 /* Hash macro for finding the index to the conn hash table */ 64 #define ILB_CONN_HASH(saddr, sport, daddr, dport, hash_size) \ 65 (((*((saddr) + 3) ^ *((daddr) + 3)) * 50653 + \ 66 (*((saddr) + 2) ^ *((daddr) + 2)) * 1369 + \ 67 (*((saddr) + 1) ^ *((daddr) + 1)) * 37 + \ 68 (*(saddr) ^ *(daddr)) + (sport) * 37 + (dport)) & \ 69 ((hash_size) - 1)) 70 71 /* Kmem cache for the conn hash entry */ 72 static struct kmem_cache *ilb_conn_cache = NULL; 73 74 /* 75 * There are 60 timers running to do conn cache garbage collection. Each 76 * gc thread is responsible for 1/60 of the conn hash table. 77 */ 78 static int ilb_conn_timer_size = 60; 79 80 /* Each of the above gc timers wake up every 15s to do the gc. */ 81 static int ilb_conn_cache_timeout = 15; 82 83 #define ILB_STICKY_HASH(saddr, rule, hash_size) \ 84 (((*((saddr) + 3) ^ ((rule) >> 24)) * 29791 + \ 85 (*((saddr) + 2) ^ ((rule) >> 16)) * 961 + \ 86 (*((saddr) + 1) ^ ((rule) >> 8)) * 31 + \ 87 (*(saddr) ^ (rule))) & ((hash_size) - 1)) 88 89 static struct kmem_cache *ilb_sticky_cache = NULL; 90 91 /* 92 * There are 60 timers running to do sticky cache garbage collection. Each 93 * gc thread is responsible for 1/60 of the sticky hash table. 94 */ 95 static int ilb_sticky_timer_size = 60; 96 97 /* Each of the above gc timers wake up every 15s to do the gc. */ 98 static int ilb_sticky_timeout = 15; 99 100 #define ILB_STICKY_REFRELE(s) \ 101 { \ 102 mutex_enter(&(s)->hash->sticky_lock); \ 103 (s)->refcnt--; \ 104 (s)->atime = lbolt64; \ 105 mutex_exit(&s->hash->sticky_lock); \ 106 } 107 108 109 static void 110 ilb_conn_cache_init(void) 111 { 112 ilb_conn_cache = kmem_cache_create("ilb_conn_cache", 113 sizeof (ilb_conn_t), 0, NULL, NULL, NULL, NULL, NULL, 114 ilb_kmem_flags); 115 } 116 117 void 118 ilb_conn_cache_fini(void) 119 { 120 if (ilb_conn_cache != NULL) { 121 kmem_cache_destroy(ilb_conn_cache); 122 ilb_conn_cache = NULL; 123 } 124 } 125 126 static void 127 ilb_conn_remove_common(ilb_conn_t *connp, boolean_t c2s) 128 { 129 ilb_conn_hash_t *hash; 130 ilb_conn_t **next, **prev; 131 ilb_conn_t **next_prev, **prev_next; 132 133 if (c2s) { 134 hash = connp->conn_c2s_hash; 135 ASSERT(MUTEX_HELD(&hash->ilb_conn_hash_lock)); 136 next = &connp->conn_c2s_next; 137 prev = &connp->conn_c2s_prev; 138 if (*next != NULL) 139 next_prev = &(*next)->conn_c2s_prev; 140 if (*prev != NULL) 141 prev_next = &(*prev)->conn_c2s_next; 142 } else { 143 hash = connp->conn_s2c_hash; 144 ASSERT(MUTEX_HELD(&hash->ilb_conn_hash_lock)); 145 next = &connp->conn_s2c_next; 146 prev = &connp->conn_s2c_prev; 147 if (*next != NULL) 148 next_prev = &(*next)->conn_s2c_prev; 149 if (*prev != NULL) 150 prev_next = &(*prev)->conn_s2c_next; 151 } 152 153 if (hash->ilb_connp == connp) { 154 hash->ilb_connp = *next; 155 if (*next != NULL) 156 *next_prev = NULL; 157 } else { 158 if (*prev != NULL) 159 *prev_next = *next; 160 if (*next != NULL) 161 *next_prev = *prev; 162 } 163 ASSERT(hash->ilb_conn_cnt > 0); 164 hash->ilb_conn_cnt--; 165 166 *next = NULL; 167 *prev = NULL; 168 } 169 170 static void 171 ilb_conn_remove(ilb_conn_t *connp) 172 { 173 ASSERT(MUTEX_HELD(&connp->conn_c2s_hash->ilb_conn_hash_lock)); 174 ilb_conn_remove_common(connp, B_TRUE); 175 ASSERT(MUTEX_HELD(&connp->conn_s2c_hash->ilb_conn_hash_lock)); 176 ilb_conn_remove_common(connp, B_FALSE); 177 178 if (connp->conn_rule_cache.topo == ILB_TOPO_IMPL_NAT) { 179 in_port_t port; 180 181 port = ntohs(connp->conn_rule_cache.info.nat_sport); 182 vmem_free(connp->conn_rule_cache.info.src_ent->nse_port_arena, 183 (void *)(uintptr_t)port, 1); 184 } 185 186 if (connp->conn_sticky != NULL) 187 ILB_STICKY_REFRELE(connp->conn_sticky); 188 ILB_SERVER_REFRELE(connp->conn_server); 189 kmem_cache_free(ilb_conn_cache, connp); 190 } 191 192 /* 193 * Routine to do periodic garbage collection of conn hash entries. When 194 * a conn hash timer fires, it dispatches a taskq to call this function 195 * to do the gc. Note that each taskq is responisble for a portion of 196 * the table. The portion is stored in timer->start, timer->end. 197 */ 198 static void 199 ilb_conn_cleanup(void *arg) 200 { 201 ilb_timer_t *timer = (ilb_timer_t *)arg; 202 uint32_t i; 203 ilb_stack_t *ilbs; 204 ilb_conn_hash_t *c2s_hash, *s2c_hash; 205 ilb_conn_t *connp, *nxt_connp; 206 int64_t now; 207 int64_t expiry; 208 boolean_t die_now; 209 210 ilbs = timer->ilbs; 211 c2s_hash = ilbs->ilbs_c2s_conn_hash; 212 ASSERT(c2s_hash != NULL); 213 214 now = lbolt64; 215 for (i = timer->start; i < timer->end; i++) { 216 mutex_enter(&c2s_hash[i].ilb_conn_hash_lock); 217 if ((connp = c2s_hash[i].ilb_connp) == NULL) { 218 ASSERT(c2s_hash[i].ilb_conn_cnt == 0); 219 mutex_exit(&c2s_hash[i].ilb_conn_hash_lock); 220 continue; 221 } 222 do { 223 ASSERT(c2s_hash[i].ilb_conn_cnt > 0); 224 ASSERT(connp->conn_c2s_hash == &c2s_hash[i]); 225 nxt_connp = connp->conn_c2s_next; 226 expiry = now - SEC_TO_TICK(connp->conn_expiry); 227 if (connp->conn_server->iser_die_time != 0 && 228 connp->conn_server->iser_die_time < now) 229 die_now = B_TRUE; 230 else 231 die_now = B_FALSE; 232 s2c_hash = connp->conn_s2c_hash; 233 mutex_enter(&s2c_hash->ilb_conn_hash_lock); 234 235 if (connp->conn_gc || die_now || 236 (connp->conn_c2s_atime < expiry && 237 connp->conn_s2c_atime < expiry)) { 238 /* Need to update the nat list cur_connp */ 239 if (connp == ilbs->ilbs_conn_list_connp) { 240 ilbs->ilbs_conn_list_connp = 241 connp->conn_c2s_next; 242 } 243 ilb_conn_remove(connp); 244 goto nxt_connp; 245 } 246 247 if (connp->conn_l4 != IPPROTO_TCP) 248 goto nxt_connp; 249 250 /* Update and check TCP related conn info */ 251 if (connp->conn_c2s_tcp_fin_sent && 252 SEQ_GT(connp->conn_s2c_tcp_ack, 253 connp->conn_c2s_tcp_fss)) { 254 connp->conn_c2s_tcp_fin_acked = B_TRUE; 255 } 256 if (connp->conn_s2c_tcp_fin_sent && 257 SEQ_GT(connp->conn_c2s_tcp_ack, 258 connp->conn_s2c_tcp_fss)) { 259 connp->conn_s2c_tcp_fin_acked = B_TRUE; 260 } 261 if (connp->conn_c2s_tcp_fin_acked && 262 connp->conn_s2c_tcp_fin_acked) { 263 ilb_conn_remove(connp); 264 } 265 nxt_connp: 266 mutex_exit(&s2c_hash->ilb_conn_hash_lock); 267 connp = nxt_connp; 268 } while (connp != NULL); 269 mutex_exit(&c2s_hash[i].ilb_conn_hash_lock); 270 } 271 } 272 273 /* Conn hash timer routine. It dispatches a taskq and restart the timer */ 274 static void 275 ilb_conn_timer(void *arg) 276 { 277 ilb_timer_t *timer = (ilb_timer_t *)arg; 278 279 (void) taskq_dispatch(timer->ilbs->ilbs_conn_taskq, ilb_conn_cleanup, 280 arg, TQ_SLEEP); 281 mutex_enter(&timer->tid_lock); 282 if (timer->tid == 0) { 283 mutex_exit(&timer->tid_lock); 284 } else { 285 timer->tid = timeout(ilb_conn_timer, arg, 286 SEC_TO_TICK(ilb_conn_cache_timeout)); 287 mutex_exit(&timer->tid_lock); 288 } 289 } 290 291 void 292 ilb_conn_hash_init(ilb_stack_t *ilbs) 293 { 294 extern pri_t minclsyspri; 295 int i, part; 296 ilb_timer_t *tm; 297 char tq_name[TASKQ_NAMELEN]; 298 299 /* 300 * If ilbs->ilbs_conn_hash_size is not a power of 2, bump it up to 301 * the next power of 2. 302 */ 303 if (ilbs->ilbs_conn_hash_size & (ilbs->ilbs_conn_hash_size - 1)) { 304 for (i = 0; i < 31; i++) { 305 if (ilbs->ilbs_conn_hash_size < (1 << i)) 306 break; 307 } 308 ilbs->ilbs_conn_hash_size = 1 << i; 309 } 310 311 /* 312 * Can sleep since this should be called when a rule is being added, 313 * hence we are not in interrupt context. 314 */ 315 ilbs->ilbs_c2s_conn_hash = kmem_zalloc(sizeof (ilb_conn_hash_t) * 316 ilbs->ilbs_conn_hash_size, KM_SLEEP); 317 ilbs->ilbs_s2c_conn_hash = kmem_zalloc(sizeof (ilb_conn_hash_t) * 318 ilbs->ilbs_conn_hash_size, KM_SLEEP); 319 320 for (i = 0; i < ilbs->ilbs_conn_hash_size; i++) { 321 mutex_init(&ilbs->ilbs_c2s_conn_hash[i].ilb_conn_hash_lock, 322 NULL, MUTEX_DEFAULT, NULL); 323 } 324 for (i = 0; i < ilbs->ilbs_conn_hash_size; i++) { 325 mutex_init(&ilbs->ilbs_s2c_conn_hash[i].ilb_conn_hash_lock, 326 NULL, MUTEX_DEFAULT, NULL); 327 } 328 329 if (ilb_conn_cache == NULL) 330 ilb_conn_cache_init(); 331 332 (void) snprintf(tq_name, sizeof (tq_name), "ilb_conn_taskq_%p", 333 ilbs->ilbs_netstack); 334 ASSERT(ilbs->ilbs_conn_taskq == NULL); 335 ilbs->ilbs_conn_taskq = taskq_create(tq_name, 336 ilb_conn_timer_size * 2, minclsyspri, ilb_conn_timer_size, 337 ilb_conn_timer_size * 2, TASKQ_PREPOPULATE|TASKQ_DYNAMIC); 338 339 ASSERT(ilbs->ilbs_conn_timer_list == NULL); 340 ilbs->ilbs_conn_timer_list = kmem_zalloc(sizeof (ilb_timer_t) * 341 ilb_conn_timer_size, KM_SLEEP); 342 343 /* 344 * The hash table is divided in equal partition for those timers 345 * to do garbage collection. 346 */ 347 part = ilbs->ilbs_conn_hash_size / ilb_conn_timer_size + 1; 348 for (i = 0; i < ilb_conn_timer_size; i++) { 349 tm = ilbs->ilbs_conn_timer_list + i; 350 tm->start = i * part; 351 tm->end = i * part + part; 352 if (tm->end > ilbs->ilbs_conn_hash_size) 353 tm->end = ilbs->ilbs_conn_hash_size; 354 tm->ilbs = ilbs; 355 mutex_init(&tm->tid_lock, NULL, MUTEX_DEFAULT, NULL); 356 /* Spread out the starting execution time of all the timers. */ 357 tm->tid = timeout(ilb_conn_timer, tm, 358 SEC_TO_TICK(ilb_conn_cache_timeout + i)); 359 } 360 } 361 362 void 363 ilb_conn_hash_fini(ilb_stack_t *ilbs) 364 { 365 uint32_t i; 366 ilb_conn_t *connp; 367 368 if (ilbs->ilbs_c2s_conn_hash == NULL) { 369 ASSERT(ilbs->ilbs_s2c_conn_hash == NULL); 370 return; 371 } 372 373 /* Stop all the timers first. */ 374 for (i = 0; i < ilb_conn_timer_size; i++) { 375 timeout_id_t tid; 376 377 /* Setting tid to 0 tells the timer handler not to restart. */ 378 mutex_enter(&ilbs->ilbs_conn_timer_list[i].tid_lock); 379 tid = ilbs->ilbs_conn_timer_list[i].tid; 380 ilbs->ilbs_conn_timer_list[i].tid = 0; 381 mutex_exit(&ilbs->ilbs_conn_timer_list[i].tid_lock); 382 (void) untimeout(tid); 383 } 384 kmem_free(ilbs->ilbs_conn_timer_list, sizeof (ilb_timer_t) * 385 ilb_conn_timer_size); 386 taskq_destroy(ilbs->ilbs_conn_taskq); 387 ilbs->ilbs_conn_taskq = NULL; 388 389 /* Then remove all the conns. */ 390 for (i = 0; i < ilbs->ilbs_conn_hash_size; i++) { 391 while ((connp = ilbs->ilbs_s2c_conn_hash->ilb_connp) != NULL) { 392 ilbs->ilbs_s2c_conn_hash->ilb_connp = 393 connp->conn_s2c_next; 394 ILB_SERVER_REFRELE(connp->conn_server); 395 if (connp->conn_rule_cache.topo == ILB_TOPO_IMPL_NAT) { 396 ilb_nat_src_entry_t *ent; 397 in_port_t port; 398 399 /* 400 * src_ent will be freed in ilb_nat_src_fini(). 401 */ 402 port = ntohs( 403 connp->conn_rule_cache.info.nat_sport); 404 ent = connp->conn_rule_cache.info.src_ent; 405 vmem_free(ent->nse_port_arena, 406 (void *)(uintptr_t)port, 1); 407 } 408 kmem_cache_free(ilb_conn_cache, connp); 409 } 410 } 411 kmem_free(ilbs->ilbs_c2s_conn_hash, sizeof (ilb_conn_hash_t) * 412 ilbs->ilbs_conn_hash_size); 413 kmem_free(ilbs->ilbs_s2c_conn_hash, sizeof (ilb_conn_hash_t) * 414 ilbs->ilbs_conn_hash_size); 415 } 416 417 /* 418 * Internet checksum adjustment calculation routines. We pre-calculate 419 * checksum adjustment so that we don't need to compute the checksum on 420 * the whole packet when we change address/port in the packet. 421 */ 422 423 static void 424 hnat_cksum_v4(uint16_t *oaddr, uint16_t *naddr, in_port_t old_port, 425 in_port_t new_port, uint32_t *adj_sum) 426 { 427 uint32_t sum; 428 429 sum = *oaddr + *(oaddr + 1) + old_port; 430 while ((sum >> 16) != 0) 431 sum = (sum & 0xffff) + (sum >> 16); 432 *adj_sum = (uint16_t)~sum + *naddr + *(naddr + 1) + new_port; 433 } 434 435 static void 436 hnat_cksum_v6(uint16_t *oaddr, uint16_t *naddr, in_port_t old_port, 437 in_port_t new_port, uint32_t *adj_sum) 438 { 439 uint32_t sum = 0; 440 441 sum = *oaddr + *(oaddr + 1) + *(oaddr + 2) + *(oaddr + 3) + 442 *(oaddr + 4) + *(oaddr + 5) + *(oaddr + 6) + *(oaddr + 7) + 443 old_port; 444 while ((sum >> 16) != 0) 445 sum = (sum & 0xffff) + (sum >> 16); 446 *adj_sum = (uint16_t)~sum + *naddr + *(naddr + 1) + 447 *(naddr + 2) + *(naddr + 3) + *(naddr + 4) + *(naddr + 5) + 448 *(naddr + 6) + *(naddr + 7) + new_port; 449 } 450 451 static void 452 fnat_cksum_v4(uint16_t *oaddr1, uint16_t *oaddr2, uint16_t *naddr1, 453 uint16_t *naddr2, in_port_t old_port1, in_port_t old_port2, 454 in_port_t new_port1, in_port_t new_port2, uint32_t *adj_sum) 455 { 456 uint32_t sum; 457 458 sum = *oaddr1 + *(oaddr1 + 1) + old_port1 + *oaddr2 + *(oaddr2 + 1) + 459 old_port2; 460 while ((sum >> 16) != 0) 461 sum = (sum & 0xffff) + (sum >> 16); 462 *adj_sum = (uint16_t)~sum + *naddr1 + *(naddr1 + 1) + new_port1 + 463 *naddr2 + *(naddr2 + 1) + new_port2; 464 } 465 466 static void 467 fnat_cksum_v6(uint16_t *oaddr1, uint16_t *oaddr2, uint16_t *naddr1, 468 uint16_t *naddr2, in_port_t old_port1, in_port_t old_port2, 469 in_port_t new_port1, in_port_t new_port2, uint32_t *adj_sum) 470 { 471 uint32_t sum = 0; 472 473 sum = *oaddr1 + *(oaddr1 + 1) + *(oaddr1 + 2) + *(oaddr1 + 3) + 474 *(oaddr1 + 4) + *(oaddr1 + 5) + *(oaddr1 + 6) + *(oaddr1 + 7) + 475 old_port1; 476 sum += *oaddr2 + *(oaddr2 + 1) + *(oaddr2 + 2) + *(oaddr2 + 3) + 477 *(oaddr2 + 4) + *(oaddr2 + 5) + *(oaddr2 + 6) + *(oaddr2 + 7) + 478 old_port2; 479 while ((sum >> 16) != 0) 480 sum = (sum & 0xffff) + (sum >> 16); 481 sum = (uint16_t)~sum + *naddr1 + *(naddr1 + 1) + *(naddr1 + 2) + 482 *(naddr1 + 3) + *(naddr1 + 4) + *(naddr1 + 5) + *(naddr1 + 6) + 483 *(naddr1 + 7) + new_port1; 484 *adj_sum = sum + *naddr2 + *(naddr2 + 1) + *(naddr2 + 2) + 485 *(naddr2 + 3) + *(naddr2 + 4) + *(naddr2 + 5) + *(naddr2 + 6) + 486 *(naddr2 + 7) + new_port2; 487 } 488 489 /* 490 * Add a conn hash entry to the tables. Note that a conn hash entry 491 * (ilb_conn_t) contains info on both directions. And there are two hash 492 * tables, one for client to server and the other for server to client. 493 * So the same entry is added to both tables and can be ccessed by two 494 * thread simultaneously. But each thread will only access data on one 495 * direction, so there is no conflict. 496 */ 497 int 498 ilb_conn_add(ilb_stack_t *ilbs, ilb_rule_t *rule, ilb_server_t *server, 499 in6_addr_t *src, in_port_t sport, in6_addr_t *dst, in_port_t dport, 500 ilb_nat_info_t *info, uint32_t *ip_sum, uint32_t *tp_sum, ilb_sticky_t *s) 501 { 502 ilb_conn_t *connp; 503 ilb_conn_hash_t *hash; 504 int i; 505 506 connp = kmem_cache_alloc(ilb_conn_cache, KM_NOSLEEP); 507 if (connp == NULL) { 508 if (s != NULL) { 509 if (rule->ir_topo == ILB_TOPO_IMPL_NAT) { 510 ilb_nat_src_entry_t **entry; 511 512 entry = s->server->iser_nat_src->src_list; 513 vmem_free(entry[s->nat_src_idx]->nse_port_arena, 514 (void *)(uintptr_t)ntohs(info->nat_sport), 515 1); 516 } 517 ILB_STICKY_REFRELE(s); 518 } 519 return (ENOMEM); 520 } 521 522 connp->conn_l4 = rule->ir_proto; 523 524 connp->conn_server = server; 525 ILB_SERVER_REFHOLD(server); 526 connp->conn_sticky = s; 527 528 connp->conn_rule_cache.topo = rule->ir_topo; 529 connp->conn_rule_cache.info = *info; 530 531 connp->conn_gc = B_FALSE; 532 533 connp->conn_expiry = rule->ir_nat_expiry; 534 connp->conn_cr_time = lbolt64; 535 536 /* Client to server info. */ 537 connp->conn_c2s_saddr = *src; 538 connp->conn_c2s_sport = sport; 539 connp->conn_c2s_daddr = *dst; 540 connp->conn_c2s_dport = dport; 541 542 connp->conn_c2s_atime = lbolt64; 543 /* The packet ths triggers this creation should be counted */ 544 connp->conn_c2s_pkt_cnt = 1; 545 connp->conn_c2s_tcp_fin_sent = B_FALSE; 546 connp->conn_c2s_tcp_fin_acked = B_FALSE; 547 548 /* Server to client info, before NAT */ 549 switch (rule->ir_topo) { 550 case ILB_TOPO_IMPL_HALF_NAT: 551 connp->conn_s2c_saddr = info->nat_dst; 552 connp->conn_s2c_sport = info->nat_dport; 553 connp->conn_s2c_daddr = *src; 554 connp->conn_s2c_dport = sport; 555 556 /* Pre-calculate checksum changes for both directions */ 557 if (rule->ir_ipver == IPPROTO_IP) { 558 hnat_cksum_v4((uint16_t *)&dst->s6_addr32[3], 559 (uint16_t *)&info->nat_dst.s6_addr32[3], 0, 0, 560 &connp->conn_c2s_ip_sum); 561 hnat_cksum_v4((uint16_t *)&dst->s6_addr32[3], 562 (uint16_t *)&info->nat_dst.s6_addr32[3], dport, 563 info->nat_dport, &connp->conn_c2s_tp_sum); 564 *ip_sum = connp->conn_c2s_ip_sum; 565 *tp_sum = connp->conn_c2s_tp_sum; 566 567 hnat_cksum_v4( 568 (uint16_t *)&info->nat_dst.s6_addr32[3], 569 (uint16_t *)&dst->s6_addr32[3], 0, 0, 570 &connp->conn_s2c_ip_sum); 571 hnat_cksum_v4( 572 (uint16_t *)&info->nat_dst.s6_addr32[3], 573 (uint16_t *)&dst->s6_addr32[3], 574 info->nat_dport, dport, 575 &connp->conn_s2c_tp_sum); 576 } else { 577 connp->conn_c2s_ip_sum = 0; 578 hnat_cksum_v6((uint16_t *)dst, 579 (uint16_t *)&info->nat_dst, dport, 580 info->nat_dport, &connp->conn_c2s_tp_sum); 581 *ip_sum = 0; 582 *tp_sum = connp->conn_c2s_tp_sum; 583 584 connp->conn_s2c_ip_sum = 0; 585 hnat_cksum_v6((uint16_t *)&info->nat_dst, 586 (uint16_t *)dst, info->nat_dport, dport, 587 &connp->conn_s2c_tp_sum); 588 } 589 break; 590 case ILB_TOPO_IMPL_NAT: 591 connp->conn_s2c_saddr = info->nat_dst; 592 connp->conn_s2c_sport = info->nat_dport; 593 connp->conn_s2c_daddr = info->nat_src; 594 connp->conn_s2c_dport = info->nat_sport; 595 596 if (rule->ir_ipver == IPPROTO_IP) { 597 fnat_cksum_v4((uint16_t *)&src->s6_addr32[3], 598 (uint16_t *)&dst->s6_addr32[3], 599 (uint16_t *)&info->nat_src.s6_addr32[3], 600 (uint16_t *)&info->nat_dst.s6_addr32[3], 601 0, 0, 0, 0, &connp->conn_c2s_ip_sum); 602 fnat_cksum_v4((uint16_t *)&src->s6_addr32[3], 603 (uint16_t *)&dst->s6_addr32[3], 604 (uint16_t *)&info->nat_src.s6_addr32[3], 605 (uint16_t *)&info->nat_dst.s6_addr32[3], 606 sport, dport, info->nat_sport, 607 info->nat_dport, &connp->conn_c2s_tp_sum); 608 *ip_sum = connp->conn_c2s_ip_sum; 609 *tp_sum = connp->conn_c2s_tp_sum; 610 611 fnat_cksum_v4( 612 (uint16_t *)&info->nat_src.s6_addr32[3], 613 (uint16_t *)&info->nat_dst.s6_addr32[3], 614 (uint16_t *)&src->s6_addr32[3], 615 (uint16_t *)&dst->s6_addr32[3], 616 0, 0, 0, 0, &connp->conn_s2c_ip_sum); 617 fnat_cksum_v4( 618 (uint16_t *)&info->nat_src.s6_addr32[3], 619 (uint16_t *)&info->nat_dst.s6_addr32[3], 620 (uint16_t *)&src->s6_addr32[3], 621 (uint16_t *)&dst->s6_addr32[3], 622 info->nat_sport, info->nat_dport, 623 sport, dport, &connp->conn_s2c_tp_sum); 624 } else { 625 fnat_cksum_v6((uint16_t *)src, (uint16_t *)dst, 626 (uint16_t *)&info->nat_src, 627 (uint16_t *)&info->nat_dst, 628 sport, dport, info->nat_sport, 629 info->nat_dport, &connp->conn_c2s_tp_sum); 630 connp->conn_c2s_ip_sum = 0; 631 *ip_sum = 0; 632 *tp_sum = connp->conn_c2s_tp_sum; 633 634 fnat_cksum_v6((uint16_t *)&info->nat_src, 635 (uint16_t *)&info->nat_dst, (uint16_t *)src, 636 (uint16_t *)dst, info->nat_sport, 637 info->nat_dport, sport, dport, 638 &connp->conn_s2c_tp_sum); 639 connp->conn_s2c_ip_sum = 0; 640 } 641 break; 642 } 643 644 connp->conn_s2c_atime = lbolt64; 645 connp->conn_s2c_pkt_cnt = 1; 646 connp->conn_s2c_tcp_fin_sent = B_FALSE; 647 connp->conn_s2c_tcp_fin_acked = B_FALSE; 648 649 /* Add it to the s2c hash table. */ 650 hash = ilbs->ilbs_s2c_conn_hash; 651 i = ILB_CONN_HASH((uint8_t *)&connp->conn_s2c_saddr.s6_addr32[3], 652 ntohs(connp->conn_s2c_sport), 653 (uint8_t *)&connp->conn_s2c_daddr.s6_addr32[3], 654 ntohs(connp->conn_s2c_dport), ilbs->ilbs_conn_hash_size); 655 connp->conn_s2c_hash = &hash[i]; 656 DTRACE_PROBE2(ilb__conn__hash__add__s2c, ilb_conn_t *, connp, int, i); 657 658 mutex_enter(&hash[i].ilb_conn_hash_lock); 659 hash[i].ilb_conn_cnt++; 660 connp->conn_s2c_next = hash[i].ilb_connp; 661 if (hash[i].ilb_connp != NULL) 662 hash[i].ilb_connp->conn_s2c_prev = connp; 663 connp->conn_s2c_prev = NULL; 664 hash[i].ilb_connp = connp; 665 mutex_exit(&hash[i].ilb_conn_hash_lock); 666 667 /* Add it to the c2s hash table. */ 668 hash = ilbs->ilbs_c2s_conn_hash; 669 i = ILB_CONN_HASH((uint8_t *)&src->s6_addr32[3], ntohs(sport), 670 (uint8_t *)&dst->s6_addr32[3], ntohs(dport), 671 ilbs->ilbs_conn_hash_size); 672 connp->conn_c2s_hash = &hash[i]; 673 DTRACE_PROBE2(ilb__conn__hash__add__c2s, ilb_conn_t *, connp, int, i); 674 675 mutex_enter(&hash[i].ilb_conn_hash_lock); 676 hash[i].ilb_conn_cnt++; 677 connp->conn_c2s_next = hash[i].ilb_connp; 678 if (hash[i].ilb_connp != NULL) 679 hash[i].ilb_connp->conn_c2s_prev = connp; 680 connp->conn_c2s_prev = NULL; 681 hash[i].ilb_connp = connp; 682 mutex_exit(&hash[i].ilb_conn_hash_lock); 683 684 return (0); 685 } 686 687 /* 688 * If a connection is using TCP, we keep track of simple TCP state transition 689 * so that we know when to clean up an entry. 690 */ 691 static boolean_t 692 update_conn_tcp(ilb_conn_t *connp, void *iph, tcpha_t *tcpha, int32_t pkt_len, 693 boolean_t c2s) 694 { 695 uint32_t ack, seq; 696 int32_t seg_len; 697 698 if (tcpha->tha_flags & TH_RST) 699 return (B_FALSE); 700 701 seg_len = pkt_len - ((uint8_t *)tcpha - (uint8_t *)iph) - 702 TCP_HDR_LENGTH((tcph_t *)tcpha); 703 704 if (tcpha->tha_flags & TH_ACK) 705 ack = ntohl(tcpha->tha_ack); 706 seq = ntohl(tcpha->tha_seq); 707 if (c2s) { 708 ASSERT(MUTEX_HELD(&connp->conn_c2s_hash->ilb_conn_hash_lock)); 709 if (tcpha->tha_flags & TH_FIN) { 710 connp->conn_c2s_tcp_fss = seq + seg_len; 711 connp->conn_c2s_tcp_fin_sent = B_TRUE; 712 } 713 connp->conn_c2s_tcp_ack = ack; 714 715 /* Port reuse by the client, restart the conn. */ 716 if (connp->conn_c2s_tcp_fin_sent && 717 SEQ_GT(seq, connp->conn_c2s_tcp_fss + 1)) { 718 connp->conn_c2s_tcp_fin_sent = B_FALSE; 719 connp->conn_c2s_tcp_fin_acked = B_FALSE; 720 } 721 } else { 722 ASSERT(MUTEX_HELD(&connp->conn_s2c_hash->ilb_conn_hash_lock)); 723 if (tcpha->tha_flags & TH_FIN) { 724 connp->conn_s2c_tcp_fss = seq + seg_len; 725 connp->conn_s2c_tcp_fin_sent = B_TRUE; 726 } 727 connp->conn_s2c_tcp_ack = ack; 728 729 /* Port reuse by the client, restart the conn. */ 730 if (connp->conn_s2c_tcp_fin_sent && 731 SEQ_GT(seq, connp->conn_s2c_tcp_fss + 1)) { 732 connp->conn_s2c_tcp_fin_sent = B_FALSE; 733 connp->conn_s2c_tcp_fin_acked = B_FALSE; 734 } 735 } 736 737 return (B_TRUE); 738 } 739 740 /* 741 * Helper routint to find conn hash entry given some packet information and 742 * the traffic direction (c2s, client to server?) 743 */ 744 static boolean_t 745 ilb_find_conn(ilb_stack_t *ilbs, void *iph, void *tph, int l4, in6_addr_t *src, 746 in_port_t sport, in6_addr_t *dst, in_port_t dport, 747 ilb_rule_info_t *rule_cache, uint32_t *ip_sum, uint32_t *tp_sum, 748 int32_t pkt_len, boolean_t c2s) 749 { 750 ilb_conn_hash_t *hash; 751 uint_t i; 752 ilb_conn_t *connp; 753 boolean_t tcp_alive; 754 boolean_t ret = B_FALSE; 755 756 i = ILB_CONN_HASH((uint8_t *)&src->s6_addr32[3], ntohs(sport), 757 (uint8_t *)&dst->s6_addr32[3], ntohs(dport), 758 ilbs->ilbs_conn_hash_size); 759 if (c2s) { 760 hash = ilbs->ilbs_c2s_conn_hash; 761 mutex_enter(&hash[i].ilb_conn_hash_lock); 762 for (connp = hash[i].ilb_connp; connp != NULL; 763 connp = connp->conn_c2s_next) { 764 if (connp->conn_l4 == l4 && 765 connp->conn_c2s_dport == dport && 766 connp->conn_c2s_sport == sport && 767 IN6_ARE_ADDR_EQUAL(src, &connp->conn_c2s_saddr) && 768 IN6_ARE_ADDR_EQUAL(dst, &connp->conn_c2s_daddr)) { 769 connp->conn_c2s_atime = lbolt64; 770 connp->conn_c2s_pkt_cnt++; 771 *rule_cache = connp->conn_rule_cache; 772 *ip_sum = connp->conn_c2s_ip_sum; 773 *tp_sum = connp->conn_c2s_tp_sum; 774 ret = B_TRUE; 775 break; 776 } 777 } 778 } else { 779 hash = ilbs->ilbs_s2c_conn_hash; 780 mutex_enter(&hash[i].ilb_conn_hash_lock); 781 for (connp = hash[i].ilb_connp; connp != NULL; 782 connp = connp->conn_s2c_next) { 783 if (connp->conn_l4 == l4 && 784 connp->conn_s2c_dport == dport && 785 connp->conn_s2c_sport == sport && 786 IN6_ARE_ADDR_EQUAL(src, &connp->conn_s2c_saddr) && 787 IN6_ARE_ADDR_EQUAL(dst, &connp->conn_s2c_daddr)) { 788 connp->conn_s2c_atime = lbolt64; 789 connp->conn_s2c_pkt_cnt++; 790 *rule_cache = connp->conn_rule_cache; 791 *ip_sum = connp->conn_s2c_ip_sum; 792 *tp_sum = connp->conn_s2c_tp_sum; 793 ret = B_TRUE; 794 break; 795 } 796 } 797 } 798 if (ret) { 799 ILB_S_KSTAT(connp->conn_server, pkt_processed); 800 ILB_S_KSTAT_UPDATE(connp->conn_server, bytes_processed, 801 pkt_len); 802 803 switch (l4) { 804 case (IPPROTO_TCP): 805 tcp_alive = update_conn_tcp(connp, iph, tph, pkt_len, 806 c2s); 807 if (!tcp_alive) { 808 connp->conn_gc = B_TRUE; 809 } 810 break; 811 default: 812 break; 813 } 814 } 815 mutex_exit(&hash[i].ilb_conn_hash_lock); 816 817 return (ret); 818 } 819 820 /* 821 * To check if a give packet matches an existing conn hash entry. If it 822 * does, return the information about this entry so that the caller can 823 * do the proper NAT. 824 */ 825 boolean_t 826 ilb_check_conn(ilb_stack_t *ilbs, int l3, void *iph, int l4, void *tph, 827 in6_addr_t *src, in6_addr_t *dst, in_port_t sport, in_port_t dport, 828 uint32_t pkt_len, in6_addr_t *lb_dst) 829 { 830 ilb_rule_info_t rule_cache; 831 uint32_t adj_ip_sum, adj_tp_sum; 832 boolean_t ret; 833 834 /* Check the incoming hash table. */ 835 if (ilb_find_conn(ilbs, iph, tph, l4, src, sport, dst, dport, 836 &rule_cache, &adj_ip_sum, &adj_tp_sum, pkt_len, B_TRUE)) { 837 switch (rule_cache.topo) { 838 case ILB_TOPO_IMPL_NAT: 839 *lb_dst = rule_cache.info.nat_dst; 840 ilb_full_nat(l3, iph, l4, tph, &rule_cache.info, 841 adj_ip_sum, adj_tp_sum, B_TRUE); 842 ret = B_TRUE; 843 break; 844 case ILB_TOPO_IMPL_HALF_NAT: 845 *lb_dst = rule_cache.info.nat_dst; 846 ilb_half_nat(l3, iph, l4, tph, &rule_cache.info, 847 adj_ip_sum, adj_tp_sum, B_TRUE); 848 ret = B_TRUE; 849 break; 850 default: 851 ret = B_FALSE; 852 break; 853 } 854 return (ret); 855 } 856 if (ilb_find_conn(ilbs, iph, tph, l4, src, sport, dst, dport, 857 &rule_cache, &adj_ip_sum, &adj_tp_sum, pkt_len, B_FALSE)) { 858 switch (rule_cache.topo) { 859 case ILB_TOPO_IMPL_NAT: 860 *lb_dst = rule_cache.info.src; 861 ilb_full_nat(l3, iph, l4, tph, &rule_cache.info, 862 adj_ip_sum, adj_tp_sum, B_FALSE); 863 ret = B_TRUE; 864 break; 865 case ILB_TOPO_IMPL_HALF_NAT: 866 *lb_dst = *dst; 867 ilb_half_nat(l3, iph, l4, tph, &rule_cache.info, 868 adj_ip_sum, adj_tp_sum, B_FALSE); 869 ret = B_TRUE; 870 break; 871 default: 872 ret = B_FALSE; 873 break; 874 } 875 return (ret); 876 } 877 878 return (B_FALSE); 879 } 880 881 /* 882 * To check if an ICMP packet belongs to a connection in one of the conn 883 * hash entries. 884 */ 885 boolean_t 886 ilb_check_icmp_conn(ilb_stack_t *ilbs, mblk_t *mp, int l3, void *out_iph, 887 void *icmph, in6_addr_t *lb_dst) 888 { 889 ilb_conn_hash_t *hash; 890 ipha_t *in_iph4; 891 ip6_t *in_iph6; 892 icmph_t *icmph4; 893 icmp6_t *icmph6; 894 in6_addr_t *in_src_p, *in_dst_p; 895 in_port_t *sport, *dport; 896 int l4; 897 uint_t i; 898 ilb_conn_t *connp; 899 ilb_rule_info_t rule_cache; 900 uint32_t adj_ip_sum; 901 boolean_t full_nat; 902 903 if (l3 == IPPROTO_IP) { 904 in6_addr_t in_src, in_dst; 905 906 icmph4 = (icmph_t *)icmph; 907 in_iph4 = (ipha_t *)&icmph4[1]; 908 909 if ((uint8_t *)in_iph4 + IPH_HDR_LENGTH(in_iph4) + 910 ICMP_MIN_TP_HDR_LEN > mp->b_wptr) { 911 return (B_FALSE); 912 } 913 914 IN6_IPADDR_TO_V4MAPPED(in_iph4->ipha_src, &in_src); 915 in_src_p = &in_src; 916 IN6_IPADDR_TO_V4MAPPED(in_iph4->ipha_dst, &in_dst); 917 in_dst_p = &in_dst; 918 919 l4 = in_iph4->ipha_protocol; 920 if (l4 != IPPROTO_TCP && l4 != IPPROTO_UDP) 921 return (B_FALSE); 922 923 sport = (in_port_t *)((char *)in_iph4 + 924 IPH_HDR_LENGTH(in_iph4)); 925 dport = sport + 1; 926 927 DTRACE_PROBE4(ilb__chk__icmp__conn__v4, uint32_t, 928 in_iph4->ipha_src, uint32_t, in_iph4->ipha_dst, uint16_t, 929 ntohs(*sport), uint16_t, ntohs(*dport)); 930 } else { 931 ASSERT(l3 == IPPROTO_IPV6); 932 933 icmph6 = (icmp6_t *)icmph; 934 in_iph6 = (ip6_t *)&icmph6[1]; 935 in_src_p = &in_iph6->ip6_src; 936 in_dst_p = &in_iph6->ip6_dst; 937 938 if ((uint8_t *)in_iph6 + sizeof (ip6_t) + 939 ICMP_MIN_TP_HDR_LEN > mp->b_wptr) { 940 return (B_FALSE); 941 } 942 943 l4 = in_iph6->ip6_nxt; 944 /* We don't go deep inside an IPv6 packet yet. */ 945 if (l4 != IPPROTO_TCP && l4 != IPPROTO_UDP) 946 return (B_FALSE); 947 948 sport = (in_port_t *)&in_iph6[1]; 949 dport = sport + 1; 950 951 DTRACE_PROBE4(ilb__chk__icmp__conn__v6, in6_addr_t *, 952 &in_iph6->ip6_src, in6_addr_t *, &in_iph6->ip6_dst, 953 uint16_t, ntohs(*sport), uint16_t, ntohs(*dport)); 954 } 955 956 i = ILB_CONN_HASH((uint8_t *)&in_dst_p->s6_addr32[3], ntohs(*dport), 957 (uint8_t *)&in_src_p->s6_addr32[3], ntohs(*sport), 958 ilbs->ilbs_conn_hash_size); 959 hash = ilbs->ilbs_c2s_conn_hash; 960 961 mutex_enter(&hash[i].ilb_conn_hash_lock); 962 for (connp = hash[i].ilb_connp; connp != NULL; 963 connp = connp->conn_c2s_next) { 964 if (connp->conn_l4 == l4 && 965 connp->conn_c2s_dport == *sport && 966 connp->conn_c2s_sport == *dport && 967 IN6_ARE_ADDR_EQUAL(in_dst_p, &connp->conn_c2s_saddr) && 968 IN6_ARE_ADDR_EQUAL(in_src_p, &connp->conn_c2s_daddr)) { 969 connp->conn_c2s_atime = lbolt64; 970 connp->conn_c2s_pkt_cnt++; 971 rule_cache = connp->conn_rule_cache; 972 adj_ip_sum = connp->conn_c2s_ip_sum; 973 break; 974 } 975 } 976 mutex_exit(&hash[i].ilb_conn_hash_lock); 977 978 if (connp == NULL) { 979 DTRACE_PROBE(ilb__chk__icmp__conn__failed); 980 return (B_FALSE); 981 } 982 983 switch (rule_cache.topo) { 984 case ILB_TOPO_IMPL_NAT: 985 full_nat = B_TRUE; 986 break; 987 case ILB_TOPO_IMPL_HALF_NAT: 988 full_nat = B_FALSE; 989 break; 990 default: 991 return (B_FALSE); 992 } 993 994 *lb_dst = rule_cache.info.nat_dst; 995 if (l3 == IPPROTO_IP) { 996 ilb_nat_icmpv4(mp, out_iph, icmph4, in_iph4, sport, dport, 997 &rule_cache.info, adj_ip_sum, full_nat); 998 } else { 999 ilb_nat_icmpv6(mp, out_iph, icmph6, in_iph6, sport, dport, 1000 &rule_cache.info, full_nat); 1001 } 1002 return (B_TRUE); 1003 } 1004 1005 /* 1006 * This routine sends up the conn hash table to user land. Note that the 1007 * request is an ioctl, hence we cannot really differentiate requests 1008 * from different clients. There is no context shared between different 1009 * ioctls. Here we make the assumption that the user land ilbd will 1010 * only allow one client to show the conn hash table at any time. 1011 * Otherwise, the results will be "very" inconsistent. 1012 * 1013 * In each ioctl, a flag (ILB_LIST_BEGIN) indicates whether the client wants 1014 * to read from the beginning of the able. After a certain entries 1015 * are reported, the kernel remembers the position of the last returned 1016 * entry. When the next ioctl comes in with the ILB_LIST_BEGIN flag, 1017 * it will return entries starting from where it was left off. When 1018 * the end of table is reached, a flag (ILB_LIST_END) is set to tell 1019 * the client that there is no more entry. 1020 * 1021 * It is assumed that the caller has checked the size of nat so that it 1022 * can hold num entries. 1023 */ 1024 /* ARGSUSED */ 1025 int 1026 ilb_list_nat(ilb_stack_t *ilbs, zoneid_t zoneid, ilb_nat_entry_t *nat, 1027 uint32_t *num, uint32_t *flags) 1028 { 1029 ilb_conn_hash_t *hash; 1030 ilb_conn_t *cur_connp; 1031 uint32_t i, j; 1032 int ret = 0; 1033 1034 mutex_enter(&ilbs->ilbs_conn_list_lock); 1035 while (ilbs->ilbs_conn_list_busy) { 1036 if (cv_wait_sig(&ilbs->ilbs_conn_list_cv, 1037 &ilbs->ilbs_conn_list_lock) == 0) { 1038 mutex_exit(&ilbs->ilbs_conn_list_lock); 1039 return (EINTR); 1040 } 1041 } 1042 if ((hash = ilbs->ilbs_c2s_conn_hash) == NULL) { 1043 ASSERT(ilbs->ilbs_s2c_conn_hash == NULL); 1044 mutex_exit(&ilbs->ilbs_conn_list_lock); 1045 *num = 0; 1046 *flags |= ILB_LIST_END; 1047 return (0); 1048 } 1049 ilbs->ilbs_conn_list_busy = B_TRUE; 1050 mutex_exit(&ilbs->ilbs_conn_list_lock); 1051 1052 if (*flags & ILB_LIST_BEGIN) { 1053 i = 0; 1054 mutex_enter(&hash[0].ilb_conn_hash_lock); 1055 cur_connp = hash[0].ilb_connp; 1056 } else if (*flags & ILB_LIST_CONT) { 1057 if (ilbs->ilbs_conn_list_cur == ilbs->ilbs_conn_hash_size) { 1058 *num = 0; 1059 *flags |= ILB_LIST_END; 1060 goto done; 1061 } 1062 i = ilbs->ilbs_conn_list_cur; 1063 mutex_enter(&hash[i].ilb_conn_hash_lock); 1064 cur_connp = ilbs->ilbs_conn_list_connp; 1065 } else { 1066 ret = EINVAL; 1067 goto done; 1068 } 1069 1070 j = 0; 1071 while (j < *num) { 1072 if (cur_connp == NULL) { 1073 mutex_exit(&hash[i].ilb_conn_hash_lock); 1074 if (++i == ilbs->ilbs_conn_hash_size) { 1075 *flags |= ILB_LIST_END; 1076 break; 1077 } 1078 mutex_enter(&hash[i].ilb_conn_hash_lock); 1079 cur_connp = hash[i].ilb_connp; 1080 continue; 1081 } 1082 nat[j].proto = cur_connp->conn_l4; 1083 1084 nat[j].in_global = cur_connp->conn_c2s_daddr; 1085 nat[j].in_global_port = cur_connp->conn_c2s_dport; 1086 nat[j].out_global = cur_connp->conn_c2s_saddr; 1087 nat[j].out_global_port = cur_connp->conn_c2s_sport; 1088 1089 nat[j].in_local = cur_connp->conn_s2c_saddr; 1090 nat[j].in_local_port = cur_connp->conn_s2c_sport; 1091 nat[j].out_local = cur_connp->conn_s2c_daddr; 1092 nat[j].out_local_port = cur_connp->conn_s2c_dport; 1093 1094 nat[j].create_time = TICK_TO_MSEC(cur_connp->conn_cr_time); 1095 nat[j].last_access_time = 1096 TICK_TO_MSEC(cur_connp->conn_c2s_atime); 1097 1098 /* 1099 * The conn_s2c_pkt_cnt may not be accurate since we are not 1100 * holding the s2c hash lock. 1101 */ 1102 nat[j].pkt_cnt = cur_connp->conn_c2s_pkt_cnt + 1103 cur_connp->conn_s2c_pkt_cnt; 1104 j++; 1105 1106 cur_connp = cur_connp->conn_c2s_next; 1107 } 1108 ilbs->ilbs_conn_list_connp = cur_connp; 1109 if (j == *num) 1110 mutex_exit(&hash[i].ilb_conn_hash_lock); 1111 1112 ilbs->ilbs_conn_list_cur = i; 1113 1114 *num = j; 1115 done: 1116 mutex_enter(&ilbs->ilbs_conn_list_lock); 1117 ilbs->ilbs_conn_list_busy = B_FALSE; 1118 cv_signal(&ilbs->ilbs_conn_list_cv); 1119 mutex_exit(&ilbs->ilbs_conn_list_lock); 1120 1121 return (ret); 1122 } 1123 1124 1125 /* 1126 * Stickiness (persistence) handling routines. 1127 */ 1128 1129 1130 static void 1131 ilb_sticky_cache_init(void) 1132 { 1133 ilb_sticky_cache = kmem_cache_create("ilb_sticky_cache", 1134 sizeof (ilb_sticky_t), 0, NULL, NULL, NULL, NULL, NULL, 1135 ilb_kmem_flags); 1136 } 1137 1138 void 1139 ilb_sticky_cache_fini(void) 1140 { 1141 if (ilb_sticky_cache != NULL) { 1142 kmem_cache_destroy(ilb_sticky_cache); 1143 ilb_sticky_cache = NULL; 1144 } 1145 } 1146 1147 void 1148 ilb_sticky_refrele(ilb_sticky_t *s) 1149 { 1150 ILB_STICKY_REFRELE(s); 1151 } 1152 1153 static ilb_sticky_t * 1154 ilb_sticky_lookup(ilb_sticky_hash_t *hash, ilb_rule_t *rule, in6_addr_t *src) 1155 { 1156 ilb_sticky_t *s; 1157 1158 ASSERT(mutex_owned(&hash->sticky_lock)); 1159 1160 for (s = list_head(&hash->sticky_head); s != NULL; 1161 s = list_next(&hash->sticky_head, s)) { 1162 if (s->rule_instance == rule->ir_ks_instance) { 1163 if (IN6_ARE_ADDR_EQUAL(src, &s->src)) 1164 return (s); 1165 } 1166 } 1167 return (NULL); 1168 } 1169 1170 static ilb_sticky_t * 1171 ilb_sticky_add(ilb_sticky_hash_t *hash, ilb_rule_t *rule, ilb_server_t *server, 1172 in6_addr_t *src) 1173 { 1174 ilb_sticky_t *s; 1175 1176 ASSERT(mutex_owned(&hash->sticky_lock)); 1177 1178 if ((s = kmem_cache_alloc(ilb_sticky_cache, KM_NOSLEEP)) == NULL) 1179 return (NULL); 1180 1181 /* 1182 * The rule instance is for handling the scenario when the same 1183 * client talks to different rules at the same time. Stickiness 1184 * is per rule so we can use the rule instance to differentiate 1185 * the client's request. 1186 */ 1187 s->rule_instance = rule->ir_ks_instance; 1188 /* 1189 * Copy the rule name for listing all sticky cache entry. ir_name 1190 * is guaranteed to be NULL terminated. 1191 */ 1192 (void) strcpy(s->rule_name, rule->ir_name); 1193 s->server = server; 1194 1195 /* 1196 * Grab a ref cnt on the server so that it won't go away while 1197 * it is still in the sticky table. 1198 */ 1199 ILB_SERVER_REFHOLD(server); 1200 s->src = *src; 1201 s->expiry = rule->ir_sticky_expiry; 1202 s->refcnt = 1; 1203 s->hash = hash; 1204 1205 /* 1206 * There is no need to set atime here since the refcnt is not 1207 * zero. A sticky entry is removed only when the refcnt is 1208 * zero. But just set it here for debugging purpose. The 1209 * atime is set when a refrele is done on a sticky entry. 1210 */ 1211 s->atime = lbolt64; 1212 1213 list_insert_head(&hash->sticky_head, s); 1214 hash->sticky_cnt++; 1215 return (s); 1216 } 1217 1218 /* 1219 * This routine checks if there is an existing sticky entry which matches 1220 * a given packet. If there is one, return it. If there is not, create 1221 * a sticky entry using the packet's info. 1222 */ 1223 ilb_server_t * 1224 ilb_sticky_find_add(ilb_stack_t *ilbs, ilb_rule_t *rule, in6_addr_t *src, 1225 ilb_server_t *server, ilb_sticky_t **res, uint16_t *src_ent_idx) 1226 { 1227 int i; 1228 ilb_sticky_hash_t *hash; 1229 ilb_sticky_t *s; 1230 1231 ASSERT(server != NULL); 1232 1233 *res = NULL; 1234 1235 i = ILB_STICKY_HASH((uint8_t *)&src->s6_addr32[3], 1236 (uint32_t)(uintptr_t)rule, ilbs->ilbs_sticky_hash_size); 1237 hash = &ilbs->ilbs_sticky_hash[i]; 1238 1239 /* First check if there is already an entry. */ 1240 mutex_enter(&hash->sticky_lock); 1241 s = ilb_sticky_lookup(hash, rule, src); 1242 1243 /* No sticky entry, add one. */ 1244 if (s == NULL) { 1245 add_new_entry: 1246 s = ilb_sticky_add(hash, rule, server, src); 1247 if (s == NULL) { 1248 mutex_exit(&hash->sticky_lock); 1249 return (NULL); 1250 } 1251 /* 1252 * Find a source for this server. All subseqent requests from 1253 * the same client matching this sticky entry will use this 1254 * source address in doing NAT. The current algorithm is 1255 * simple, rotate the source address. Note that the 1256 * source address array does not change after it's created, so 1257 * it is OK to just increment the cur index. 1258 */ 1259 if (server->iser_nat_src != NULL) { 1260 /* It is a hint, does not need to be atomic. */ 1261 *src_ent_idx = (server->iser_nat_src->cur++ % 1262 server->iser_nat_src->num_src); 1263 s->nat_src_idx = *src_ent_idx; 1264 } 1265 mutex_exit(&hash->sticky_lock); 1266 *res = s; 1267 return (server); 1268 } 1269 1270 /* 1271 * We don't hold any lock accessing iser_enabled. Refer to the 1272 * comment in ilb_server_add() about iser_lock. 1273 */ 1274 if (!s->server->iser_enabled) { 1275 /* 1276 * s->server == server can only happen if there is a race in 1277 * toggling the iser_enabled flag (we don't hold a lock doing 1278 * that) so that the load balance algorithm still returns a 1279 * disabled server. In this case, just drop the packet... 1280 */ 1281 if (s->server == server) { 1282 mutex_exit(&hash->sticky_lock); 1283 return (NULL); 1284 } 1285 1286 /* 1287 * The old server is disabled and there is a new server, use 1288 * the new one to create a sticky entry. Since we will 1289 * add the entry at the beginning, subsequent lookup will 1290 * find this new entry instead of the old one. 1291 */ 1292 goto add_new_entry; 1293 } 1294 1295 s->refcnt++; 1296 *res = s; 1297 mutex_exit(&hash->sticky_lock); 1298 if (server->iser_nat_src != NULL) 1299 *src_ent_idx = s->nat_src_idx; 1300 return (s->server); 1301 } 1302 1303 static void 1304 ilb_sticky_cleanup(void *arg) 1305 { 1306 ilb_timer_t *timer = (ilb_timer_t *)arg; 1307 uint32_t i; 1308 ilb_stack_t *ilbs; 1309 ilb_sticky_hash_t *hash; 1310 ilb_sticky_t *s, *nxt_s; 1311 int64_t now, expiry; 1312 1313 ilbs = timer->ilbs; 1314 hash = ilbs->ilbs_sticky_hash; 1315 ASSERT(hash != NULL); 1316 1317 now = lbolt64; 1318 for (i = timer->start; i < timer->end; i++) { 1319 mutex_enter(&hash[i].sticky_lock); 1320 for (s = list_head(&hash[i].sticky_head); s != NULL; 1321 s = nxt_s) { 1322 nxt_s = list_next(&hash[i].sticky_head, s); 1323 if (s->refcnt != 0) 1324 continue; 1325 expiry = now - SEC_TO_TICK(s->expiry); 1326 if (s->atime < expiry) { 1327 ILB_SERVER_REFRELE(s->server); 1328 list_remove(&hash[i].sticky_head, s); 1329 kmem_cache_free(ilb_sticky_cache, s); 1330 hash[i].sticky_cnt--; 1331 } 1332 } 1333 mutex_exit(&hash[i].sticky_lock); 1334 } 1335 } 1336 1337 static void 1338 ilb_sticky_timer(void *arg) 1339 { 1340 ilb_timer_t *timer = (ilb_timer_t *)arg; 1341 1342 (void) taskq_dispatch(timer->ilbs->ilbs_sticky_taskq, 1343 ilb_sticky_cleanup, arg, TQ_SLEEP); 1344 mutex_enter(&timer->tid_lock); 1345 if (timer->tid == 0) { 1346 mutex_exit(&timer->tid_lock); 1347 } else { 1348 timer->tid = timeout(ilb_sticky_timer, arg, 1349 SEC_TO_TICK(ilb_sticky_timeout)); 1350 mutex_exit(&timer->tid_lock); 1351 } 1352 } 1353 1354 void 1355 ilb_sticky_hash_init(ilb_stack_t *ilbs) 1356 { 1357 extern pri_t minclsyspri; 1358 int i, part; 1359 char tq_name[TASKQ_NAMELEN]; 1360 ilb_timer_t *tm; 1361 1362 if (ilbs->ilbs_sticky_hash_size & (ilbs->ilbs_sticky_hash_size - 1)) { 1363 for (i = 0; i < 31; i++) { 1364 if (ilbs->ilbs_sticky_hash_size < (1 << i)) 1365 break; 1366 } 1367 ilbs->ilbs_sticky_hash_size = 1 << i; 1368 } 1369 1370 ilbs->ilbs_sticky_hash = kmem_zalloc(sizeof (ilb_sticky_hash_t) * 1371 ilbs->ilbs_sticky_hash_size, KM_SLEEP); 1372 for (i = 0; i < ilbs->ilbs_sticky_hash_size; i++) { 1373 mutex_init(&ilbs->ilbs_sticky_hash[i].sticky_lock, NULL, 1374 MUTEX_DEFAULT, NULL); 1375 list_create(&ilbs->ilbs_sticky_hash[i].sticky_head, 1376 sizeof (ilb_sticky_t), 1377 offsetof(ilb_sticky_t, list)); 1378 } 1379 1380 if (ilb_sticky_cache == NULL) 1381 ilb_sticky_cache_init(); 1382 1383 (void) snprintf(tq_name, sizeof (tq_name), "ilb_sticky_taskq_%p", 1384 ilbs->ilbs_netstack); 1385 ASSERT(ilbs->ilbs_sticky_taskq == NULL); 1386 ilbs->ilbs_sticky_taskq = taskq_create(tq_name, 1387 ilb_sticky_timer_size * 2, minclsyspri, ilb_sticky_timer_size, 1388 ilb_sticky_timer_size * 2, TASKQ_PREPOPULATE|TASKQ_DYNAMIC); 1389 1390 ASSERT(ilbs->ilbs_sticky_timer_list == NULL); 1391 ilbs->ilbs_sticky_timer_list = kmem_zalloc(sizeof (ilb_timer_t) * 1392 ilb_sticky_timer_size, KM_SLEEP); 1393 part = ilbs->ilbs_sticky_hash_size / ilb_sticky_timer_size + 1; 1394 for (i = 0; i < ilb_sticky_timer_size; i++) { 1395 tm = ilbs->ilbs_sticky_timer_list + i; 1396 tm->start = i * part; 1397 tm->end = i * part + part; 1398 if (tm->end > ilbs->ilbs_sticky_hash_size) 1399 tm->end = ilbs->ilbs_sticky_hash_size; 1400 tm->ilbs = ilbs; 1401 mutex_init(&tm->tid_lock, NULL, MUTEX_DEFAULT, NULL); 1402 /* Spread out the starting execution time of all the timers. */ 1403 tm->tid = timeout(ilb_sticky_timer, tm, 1404 SEC_TO_TICK(ilb_sticky_timeout + i)); 1405 } 1406 } 1407 1408 void 1409 ilb_sticky_hash_fini(ilb_stack_t *ilbs) 1410 { 1411 int i; 1412 ilb_sticky_t *s; 1413 1414 if (ilbs->ilbs_sticky_hash == NULL) 1415 return; 1416 1417 /* Stop all the timers first. */ 1418 for (i = 0; i < ilb_sticky_timer_size; i++) { 1419 timeout_id_t tid; 1420 1421 /* Setting tid to 0 tells the timer handler not to restart. */ 1422 mutex_enter(&ilbs->ilbs_sticky_timer_list[i].tid_lock); 1423 tid = ilbs->ilbs_sticky_timer_list[i].tid; 1424 ilbs->ilbs_sticky_timer_list[i].tid = 0; 1425 mutex_exit(&ilbs->ilbs_sticky_timer_list[i].tid_lock); 1426 (void) untimeout(tid); 1427 } 1428 kmem_free(ilbs->ilbs_sticky_timer_list, sizeof (ilb_timer_t) * 1429 ilb_sticky_timer_size); 1430 taskq_destroy(ilbs->ilbs_sticky_taskq); 1431 ilbs->ilbs_sticky_taskq = NULL; 1432 1433 for (i = 0; i < ilbs->ilbs_sticky_hash_size; i++) { 1434 while ((s = list_head(&ilbs->ilbs_sticky_hash[i].sticky_head)) 1435 != NULL) { 1436 list_remove(&ilbs->ilbs_sticky_hash[i].sticky_head, s); 1437 ILB_SERVER_REFRELE(s->server); 1438 kmem_free(s, sizeof (ilb_sticky_t)); 1439 } 1440 } 1441 kmem_free(ilbs->ilbs_sticky_hash, ilbs->ilbs_sticky_hash_size * 1442 sizeof (ilb_sticky_hash_t)); 1443 } 1444 1445 /* 1446 * This routine sends up the sticky hash table to user land. Refer to 1447 * the comments before ilb_list_nat(). Both routines assume similar 1448 * conditions. 1449 * 1450 * It is assumed that the caller has checked the size of st so that it 1451 * can hold num entries. 1452 */ 1453 /* ARGSUSED */ 1454 int 1455 ilb_list_sticky(ilb_stack_t *ilbs, zoneid_t zoneid, ilb_sticky_entry_t *st, 1456 uint32_t *num, uint32_t *flags) 1457 { 1458 ilb_sticky_hash_t *hash; 1459 ilb_sticky_t *curp; 1460 uint32_t i, j; 1461 int ret = 0; 1462 1463 mutex_enter(&ilbs->ilbs_sticky_list_lock); 1464 while (ilbs->ilbs_sticky_list_busy) { 1465 if (cv_wait_sig(&ilbs->ilbs_sticky_list_cv, 1466 &ilbs->ilbs_sticky_list_lock) == 0) { 1467 mutex_exit(&ilbs->ilbs_sticky_list_lock); 1468 return (EINTR); 1469 } 1470 } 1471 if ((hash = ilbs->ilbs_sticky_hash) == NULL) { 1472 mutex_exit(&ilbs->ilbs_sticky_list_lock); 1473 *num = 0; 1474 *flags |= ILB_LIST_END; 1475 return (0); 1476 } 1477 ilbs->ilbs_sticky_list_busy = B_TRUE; 1478 mutex_exit(&ilbs->ilbs_sticky_list_lock); 1479 1480 if (*flags & ILB_LIST_BEGIN) { 1481 i = 0; 1482 mutex_enter(&hash[0].sticky_lock); 1483 curp = list_head(&hash[0].sticky_head); 1484 } else if (*flags & ILB_LIST_CONT) { 1485 if (ilbs->ilbs_sticky_list_cur == ilbs->ilbs_sticky_hash_size) { 1486 *num = 0; 1487 *flags |= ILB_LIST_END; 1488 goto done; 1489 } 1490 i = ilbs->ilbs_sticky_list_cur; 1491 mutex_enter(&hash[i].sticky_lock); 1492 curp = ilbs->ilbs_sticky_list_curp; 1493 } else { 1494 ret = EINVAL; 1495 goto done; 1496 } 1497 1498 j = 0; 1499 while (j < *num) { 1500 if (curp == NULL) { 1501 mutex_exit(&hash[i].sticky_lock); 1502 if (++i == ilbs->ilbs_sticky_hash_size) { 1503 *flags |= ILB_LIST_END; 1504 break; 1505 } 1506 mutex_enter(&hash[i].sticky_lock); 1507 curp = list_head(&hash[i].sticky_head); 1508 continue; 1509 } 1510 (void) strcpy(st[j].rule_name, curp->rule_name); 1511 st[j].req_addr = curp->src; 1512 st[j].srv_addr = curp->server->iser_addr_v6; 1513 st[j].expiry_time = TICK_TO_MSEC(curp->expiry); 1514 j++; 1515 curp = list_next(&hash[i].sticky_head, curp); 1516 } 1517 ilbs->ilbs_sticky_list_curp = curp; 1518 if (j == *num) 1519 mutex_exit(&hash[i].sticky_lock); 1520 1521 ilbs->ilbs_sticky_list_cur = i; 1522 1523 *num = j; 1524 done: 1525 mutex_enter(&ilbs->ilbs_sticky_list_lock); 1526 ilbs->ilbs_sticky_list_busy = B_FALSE; 1527 cv_signal(&ilbs->ilbs_sticky_list_cv); 1528 mutex_exit(&ilbs->ilbs_sticky_list_lock); 1529 1530 return (ret); 1531 } 1532