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.
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.
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]
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #include <sys/types.h>
28 #include <sys/cmn_err.h>
29 #include <netinet/in.h>
32 #include <sys/crc32.h>
38 #define HASH_IP_V4(hash, addr, size) \
40 CRC32((hash), &(addr), sizeof (in_addr_t), -1U, crc32_table); \
43 #define HASH_IP_V6(hash, addr, size) \
44 HASH_IP_V4((hash), (addr)->s6_addr32[3], (size))
46 #define HASH_IP_PORT_V4(hash, addr, port, size) \
48 uint32_t val = (addr) ^ ((port) << 16) ^ (port); \
49 CRC32((hash), &val, sizeof (uint32_t), -1U, crc32_table); \
52 #define HASH_IP_PORT_V6(hash, addr, port, size) \
53 HASH_IP_PORT_V4((hash), (addr)->s6_addr32[3], (port), (size))
55 #define HASH_IP_VIP_V4(hash, saddr, daddr, size) \
57 uint32_t val = (saddr) ^ (daddr); \
58 CRC32((hash), &val, sizeof (uint32_t), -1U, crc32_table); \
61 #define HASH_IP_VIP_V6(hash, saddr, daddr, size) \
62 HASH_IP_VIP_V4((hash), (saddr)->s6_addr32[3], (daddr)->s6_addr32[3], \
65 #define INIT_HASH_TBL_SIZE 10
73 * There are two hash tables. The hash_tbl holds all servers, both enabled
74 * and disabled. The hash_enabled_tbl only holds enabled servers. Having
75 * two tables allows the hash on a client request remains the same even when
76 * some servers are disabled. If a server is disabled and a client's request
77 * hashes to it, we will do another hash. This time the has is on the enabled
80 typedef struct hash_s
{
82 size_t hash_servers
; /* Total # of servers */
83 size_t hash_tbl_size
; /* All server table size */
84 size_t hash_enabled_servers
; /* # of enabled servers */
85 size_t hash_enabled_tbl_size
; /* Enabled server table size */
86 hash_server_t
*hash_tbl
;
87 hash_server_t
*hash_enabled_tbl
;
88 ilb_algo_impl_t hash_type
;
91 static void hash_fini(ilb_alg_data_t
**);
95 hash_lb(in6_addr_t
*saddr
, in_port_t sport
, in6_addr_t
*daddr
,
96 in_port_t dport
, void *alg_data
, ilb_server_t
**ret_server
)
98 hash_t
*hash_alg
= (hash_t
*)alg_data
;
101 ASSERT(ret_server
!= NULL
);
104 mutex_enter(&hash_alg
->hash_lock
);
106 if (hash_alg
->hash_servers
== 0) {
107 mutex_exit(&hash_alg
->hash_lock
);
111 switch (hash_alg
->hash_type
) {
112 case ILB_ALG_IMPL_HASH_IP
:
113 HASH_IP_V6(i
, saddr
, hash_alg
->hash_servers
);
115 case ILB_ALG_IMPL_HASH_IP_SPORT
:
116 HASH_IP_PORT_V6(i
, saddr
, sport
, hash_alg
->hash_servers
);
118 case ILB_ALG_IMPL_HASH_IP_VIP
:
119 HASH_IP_VIP_V6(i
, saddr
, daddr
, hash_alg
->hash_servers
);
122 mutex_exit(&hash_alg
->hash_lock
);
125 if (hash_alg
->hash_tbl
[i
].enabled
) {
126 *ret_server
= hash_alg
->hash_tbl
[i
].server
;
127 mutex_exit(&hash_alg
->hash_lock
);
131 if (hash_alg
->hash_enabled_servers
== 0) {
132 mutex_exit(&hash_alg
->hash_lock
);
136 switch (hash_alg
->hash_type
) {
137 case ILB_ALG_IMPL_HASH_IP
:
138 HASH_IP_V6(i
, saddr
, hash_alg
->hash_enabled_servers
);
140 case ILB_ALG_IMPL_HASH_IP_SPORT
:
141 HASH_IP_PORT_V6(i
, saddr
, sport
,
142 hash_alg
->hash_enabled_servers
);
144 case ILB_ALG_IMPL_HASH_IP_VIP
:
145 HASH_IP_VIP_V6(i
, saddr
, daddr
,
146 hash_alg
->hash_enabled_servers
);
152 *ret_server
= hash_alg
->hash_enabled_tbl
[i
].server
;
153 mutex_exit(&hash_alg
->hash_lock
);
158 del_server(hash_server_t
*tbl
, size_t hash_size
, ilb_server_t
*host
)
162 for (i
= 0; i
< hash_size
; i
++) {
163 if (tbl
[i
].server
== host
) {
164 if (i
== hash_size
- 1)
166 for (j
= i
; j
< hash_size
- 1; j
++)
174 tbl
[hash_size
- 1].server
= NULL
;
175 tbl
[hash_size
- 1].enabled
= B_FALSE
;
180 hash_server_del(ilb_server_t
*host
, void *alg_data
)
182 hash_t
*hash_alg
= (hash_t
*)alg_data
;
185 mutex_enter(&hash_alg
->hash_lock
);
187 ret
= del_server(hash_alg
->hash_tbl
, hash_alg
->hash_servers
, host
);
189 mutex_exit(&hash_alg
->hash_lock
);
192 hash_alg
->hash_servers
--;
194 /* The server may not be enabled. */
195 ret
= del_server(hash_alg
->hash_enabled_tbl
,
196 hash_alg
->hash_enabled_servers
, host
);
198 hash_alg
->hash_enabled_servers
--;
200 mutex_exit(&hash_alg
->hash_lock
);
201 ILB_SERVER_REFRELE(host
);
206 grow_tbl(hash_server_t
**hash_tbl
, size_t *tbl_size
)
209 hash_server_t
*new_tbl
;
211 if ((new_tbl
= kmem_zalloc(sizeof (hash_server_t
) *
212 (*tbl_size
+ INIT_HASH_TBL_SIZE
), KM_NOSLEEP
)) == NULL
) {
215 mem_size
= *tbl_size
* sizeof (hash_server_t
);
216 bcopy(*hash_tbl
, new_tbl
, mem_size
);
217 kmem_free(*hash_tbl
, mem_size
);
219 *tbl_size
+= INIT_HASH_TBL_SIZE
;
224 hash_server_add(ilb_server_t
*host
, void *alg_data
)
226 hash_t
*hash_alg
= (hash_t
*)alg_data
;
229 mutex_enter(&hash_alg
->hash_lock
);
231 /* First add the server to the hash_tbl. */
232 new_size
= hash_alg
->hash_servers
+ 1;
233 if (new_size
> hash_alg
->hash_tbl_size
) {
234 if (grow_tbl(&hash_alg
->hash_tbl
, &hash_alg
->hash_tbl_size
) !=
236 mutex_exit(&hash_alg
->hash_lock
);
241 hash_alg
->hash_tbl
[hash_alg
->hash_servers
].server
= host
;
242 hash_alg
->hash_tbl
[hash_alg
->hash_servers
].enabled
= host
->iser_enabled
;
243 hash_alg
->hash_servers
++;
245 if (!host
->iser_enabled
) {
246 mutex_exit(&hash_alg
->hash_lock
);
247 ILB_SERVER_REFHOLD(host
);
251 /* If the server is enabled, add it to the hasn_enabled_tbl. */
252 new_size
= hash_alg
->hash_enabled_servers
+ 1;
253 if (new_size
> hash_alg
->hash_enabled_tbl_size
) {
254 if (grow_tbl(&hash_alg
->hash_enabled_tbl
,
255 &hash_alg
->hash_enabled_tbl_size
) != 0) {
256 mutex_exit(&hash_alg
->hash_lock
);
260 hash_alg
->hash_enabled_tbl
[hash_alg
->hash_enabled_servers
].server
=
262 hash_alg
->hash_enabled_tbl
[hash_alg
->hash_enabled_servers
].enabled
=
264 hash_alg
->hash_enabled_servers
++;
266 mutex_exit(&hash_alg
->hash_lock
);
267 ILB_SERVER_REFHOLD(host
);
272 hash_server_enable(ilb_server_t
*host
, void *alg_data
)
274 hash_t
*alg
= (hash_t
*)alg_data
;
277 mutex_enter(&alg
->hash_lock
);
279 for (i
= 0; i
< alg
->hash_servers
; i
++) {
280 if (alg
->hash_tbl
[i
].server
== host
) {
281 if (alg
->hash_tbl
[i
].enabled
) {
282 mutex_exit(&alg
->hash_lock
);
289 if (i
== alg
->hash_servers
) {
290 mutex_exit(&alg
->hash_lock
);
295 /* The server should not be in the enabled tabled. */
299 for (j
= 0; j
< alg
->hash_enabled_servers
; j
++) {
300 if (alg
->hash_enabled_tbl
[j
].server
== host
) {
301 cmn_err(CE_PANIC
, "Corrupted ILB enabled hash "
308 new_size
= alg
->hash_enabled_servers
+ 1;
309 if (new_size
> alg
->hash_enabled_tbl_size
) {
310 if (grow_tbl(&alg
->hash_enabled_tbl
,
311 &alg
->hash_enabled_tbl_size
) != 0) {
312 mutex_exit(&alg
->hash_lock
);
316 alg
->hash_tbl
[i
].enabled
= B_TRUE
;
317 alg
->hash_enabled_tbl
[alg
->hash_enabled_servers
].server
= host
;
318 alg
->hash_enabled_tbl
[alg
->hash_enabled_servers
].enabled
= B_TRUE
;
319 alg
->hash_enabled_servers
++;
321 mutex_exit(&alg
->hash_lock
);
326 hash_server_disable(ilb_server_t
*host
, void *alg_data
)
328 hash_t
*alg
= (hash_t
*)alg_data
;
331 mutex_enter(&alg
->hash_lock
);
333 for (i
= 0; i
< alg
->hash_servers
; i
++) {
334 if (alg
->hash_tbl
[i
].server
== host
) {
335 if (!alg
->hash_tbl
[i
].enabled
) {
336 mutex_exit(&alg
->hash_lock
);
343 if (i
== alg
->hash_servers
) {
344 mutex_exit(&alg
->hash_lock
);
348 alg
->hash_tbl
[i
].enabled
= B_FALSE
;
350 ASSERT(del_server(alg
->hash_enabled_tbl
, alg
->hash_enabled_servers
,
353 (void) del_server(alg
->hash_enabled_tbl
, alg
->hash_enabled_servers
,
356 alg
->hash_enabled_servers
--;
358 mutex_exit(&alg
->hash_lock
);
364 ilb_alg_hash_init(ilb_rule_t
*rule
, const void *arg
)
368 int flags
= *(int *)arg
;
370 if ((alg
= kmem_alloc(sizeof (ilb_alg_data_t
), KM_NOSLEEP
)) == NULL
)
372 if ((hash_alg
= kmem_alloc(sizeof (hash_t
), KM_NOSLEEP
)) == NULL
) {
373 kmem_free(alg
, sizeof (ilb_alg_data_t
));
376 alg
->ilb_alg_lb
= hash_lb
;
377 alg
->ilb_alg_server_del
= hash_server_del
;
378 alg
->ilb_alg_server_add
= hash_server_add
;
379 alg
->ilb_alg_server_enable
= hash_server_enable
;
380 alg
->ilb_alg_server_disable
= hash_server_disable
;
381 alg
->ilb_alg_fini
= hash_fini
;
382 alg
->ilb_alg_data
= hash_alg
;
384 mutex_init(&hash_alg
->hash_lock
, NULL
, MUTEX_DEFAULT
, NULL
);
385 hash_alg
->hash_type
= flags
;
387 /* Table of all servers */
388 hash_alg
->hash_servers
= 0;
389 hash_alg
->hash_tbl_size
= INIT_HASH_TBL_SIZE
;
390 hash_alg
->hash_tbl
= kmem_zalloc(sizeof (hash_server_t
) *
391 INIT_HASH_TBL_SIZE
, KM_NOSLEEP
);
392 if (hash_alg
->hash_tbl
== NULL
) {
393 kmem_free(hash_alg
, sizeof (hash_t
));
394 kmem_free(alg
, sizeof (ilb_alg_data_t
));
398 /* Table of only enabled servers */
399 hash_alg
->hash_enabled_servers
= 0;
400 hash_alg
->hash_enabled_tbl_size
= INIT_HASH_TBL_SIZE
;
401 hash_alg
->hash_enabled_tbl
= kmem_zalloc(sizeof (hash_server_t
) *
402 INIT_HASH_TBL_SIZE
, KM_NOSLEEP
);
403 if (hash_alg
->hash_tbl
== NULL
) {
404 kmem_free(hash_alg
->hash_tbl
, INIT_HASH_TBL_SIZE
*
405 sizeof (ilb_server_t
*));
406 kmem_free(hash_alg
, sizeof (hash_t
));
407 kmem_free(alg
, sizeof (ilb_alg_data_t
));
415 hash_fini(ilb_alg_data_t
**alg
)
420 hash_alg
= (*alg
)->ilb_alg_data
;
421 for (i
= 0; i
< hash_alg
->hash_servers
; i
++)
422 ILB_SERVER_REFRELE(hash_alg
->hash_tbl
[i
].server
);
424 kmem_free(hash_alg
->hash_tbl
, sizeof (hash_server_t
) *
425 hash_alg
->hash_tbl_size
);
426 kmem_free(hash_alg
->hash_enabled_tbl
, sizeof (hash_server_t
) *
427 hash_alg
->hash_enabled_tbl_size
);
428 kmem_free(hash_alg
, sizeof (hash_t
));
429 kmem_free(*alg
, sizeof (ilb_alg_data_t
));