110946SSangeeta.Misra@Sun.COM /*
210946SSangeeta.Misra@Sun.COM * CDDL HEADER START
310946SSangeeta.Misra@Sun.COM *
410946SSangeeta.Misra@Sun.COM * The contents of this file are subject to the terms of the
510946SSangeeta.Misra@Sun.COM * Common Development and Distribution License (the "License").
610946SSangeeta.Misra@Sun.COM * You may not use this file except in compliance with the License.
710946SSangeeta.Misra@Sun.COM *
810946SSangeeta.Misra@Sun.COM * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
910946SSangeeta.Misra@Sun.COM * or http://www.opensolaris.org/os/licensing.
1010946SSangeeta.Misra@Sun.COM * See the License for the specific language governing permissions
1110946SSangeeta.Misra@Sun.COM * and limitations under the License.
1210946SSangeeta.Misra@Sun.COM *
1310946SSangeeta.Misra@Sun.COM * When distributing Covered Code, include this CDDL HEADER in each
1410946SSangeeta.Misra@Sun.COM * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1510946SSangeeta.Misra@Sun.COM * If applicable, add the following below this CDDL HEADER, with the
1610946SSangeeta.Misra@Sun.COM * fields enclosed by brackets "[]" replaced with your own identifying
1710946SSangeeta.Misra@Sun.COM * information: Portions Copyright [yyyy] [name of copyright owner]
1810946SSangeeta.Misra@Sun.COM *
1910946SSangeeta.Misra@Sun.COM * CDDL HEADER END
2010946SSangeeta.Misra@Sun.COM */
2110946SSangeeta.Misra@Sun.COM
2210946SSangeeta.Misra@Sun.COM /*
2310946SSangeeta.Misra@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
2410946SSangeeta.Misra@Sun.COM * Use is subject to license terms.
2510946SSangeeta.Misra@Sun.COM */
2610946SSangeeta.Misra@Sun.COM
2710946SSangeeta.Misra@Sun.COM #include <sys/kmem.h>
2810946SSangeeta.Misra@Sun.COM #include <sys/ksynch.h>
2910946SSangeeta.Misra@Sun.COM #include <sys/systm.h>
3010946SSangeeta.Misra@Sun.COM #include <sys/socket.h>
3110946SSangeeta.Misra@Sun.COM #include <sys/disp.h>
3210946SSangeeta.Misra@Sun.COM #include <sys/taskq.h>
3310946SSangeeta.Misra@Sun.COM #include <sys/cmn_err.h>
3410946SSangeeta.Misra@Sun.COM #include <sys/strsun.h>
3510946SSangeeta.Misra@Sun.COM #include <sys/sdt.h>
3610946SSangeeta.Misra@Sun.COM #include <sys/atomic.h>
3710946SSangeeta.Misra@Sun.COM #include <netinet/in.h>
3810946SSangeeta.Misra@Sun.COM #include <inet/ip.h>
3910946SSangeeta.Misra@Sun.COM #include <inet/ip6.h>
4010946SSangeeta.Misra@Sun.COM #include <inet/tcp.h>
4110946SSangeeta.Misra@Sun.COM #include <inet/udp_impl.h>
4210946SSangeeta.Misra@Sun.COM #include <inet/kstatcom.h>
4310946SSangeeta.Misra@Sun.COM
4410946SSangeeta.Misra@Sun.COM #include <inet/ilb_ip.h>
4510946SSangeeta.Misra@Sun.COM #include "ilb_alg.h"
4610946SSangeeta.Misra@Sun.COM #include "ilb_nat.h"
4710946SSangeeta.Misra@Sun.COM #include "ilb_conn.h"
4810946SSangeeta.Misra@Sun.COM
4910946SSangeeta.Misra@Sun.COM /* ILB kmem cache flag */
5010946SSangeeta.Misra@Sun.COM int ilb_kmem_flags = 0;
5110946SSangeeta.Misra@Sun.COM
5210946SSangeeta.Misra@Sun.COM /*
5310946SSangeeta.Misra@Sun.COM * The default size for the different hash tables. Global for all stacks.
5410946SSangeeta.Misra@Sun.COM * But each stack has its own table, just that their sizes are the same.
5510946SSangeeta.Misra@Sun.COM */
5610946SSangeeta.Misra@Sun.COM static size_t ilb_rule_hash_size = 2048;
5710946SSangeeta.Misra@Sun.COM
5810946SSangeeta.Misra@Sun.COM static size_t ilb_conn_hash_size = 262144;
5910946SSangeeta.Misra@Sun.COM
6010946SSangeeta.Misra@Sun.COM static size_t ilb_sticky_hash_size = 262144;
6110946SSangeeta.Misra@Sun.COM
6210946SSangeeta.Misra@Sun.COM /* This should be a prime number. */
6310946SSangeeta.Misra@Sun.COM static size_t ilb_nat_src_hash_size = 97;
6410946SSangeeta.Misra@Sun.COM
6510946SSangeeta.Misra@Sun.COM /* Default NAT cache entry expiry time. */
6610946SSangeeta.Misra@Sun.COM static uint32_t ilb_conn_tcp_expiry = 120;
6710946SSangeeta.Misra@Sun.COM static uint32_t ilb_conn_udp_expiry = 60;
6810946SSangeeta.Misra@Sun.COM
6910946SSangeeta.Misra@Sun.COM /* Default sticky entry expiry time. */
7010946SSangeeta.Misra@Sun.COM static uint32_t ilb_sticky_expiry = 60;
7110946SSangeeta.Misra@Sun.COM
7210946SSangeeta.Misra@Sun.COM /* addr is assumed to be a uint8_t * to an ipaddr_t. */
7310946SSangeeta.Misra@Sun.COM #define ILB_RULE_HASH(addr, hash_size) \
7410946SSangeeta.Misra@Sun.COM ((*((addr) + 3) * 29791 + *((addr) + 2) * 961 + *((addr) + 1) * 31 + \
7510946SSangeeta.Misra@Sun.COM *(addr)) & ((hash_size) - 1))
7610946SSangeeta.Misra@Sun.COM
7710946SSangeeta.Misra@Sun.COM /*
7810946SSangeeta.Misra@Sun.COM * Note on ILB delayed processing
7910946SSangeeta.Misra@Sun.COM *
8010946SSangeeta.Misra@Sun.COM * To avoid in line removal on some of the data structures, such as rules,
8110946SSangeeta.Misra@Sun.COM * servers and ilb_conn_hash entries, ILB delays such processing to a taskq.
8210946SSangeeta.Misra@Sun.COM * There are three types of ILB taskq:
8310946SSangeeta.Misra@Sun.COM *
8410946SSangeeta.Misra@Sun.COM * 1. rule handling: created at stack initialialization time, ilb_stack_init()
8510946SSangeeta.Misra@Sun.COM * 2. conn hash handling: created at conn hash initialization time,
8610946SSangeeta.Misra@Sun.COM * ilb_conn_hash_init()
8710946SSangeeta.Misra@Sun.COM * 3. sticky hash handling: created at sticky hash initialization time,
8810946SSangeeta.Misra@Sun.COM * ilb_sticky_hash_init()
8910946SSangeeta.Misra@Sun.COM *
9010946SSangeeta.Misra@Sun.COM * The rule taskq is for processing rule and server removal. When a user
9110946SSangeeta.Misra@Sun.COM * land rule/server removal request comes in, a taskq is dispatched after
9210946SSangeeta.Misra@Sun.COM * removing the rule/server from all related hashes. This taskq will wait
9310946SSangeeta.Misra@Sun.COM * until all references to the rule/server are gone before removing it.
9410946SSangeeta.Misra@Sun.COM * So the user land thread requesting the removal does not need to wait
9510946SSangeeta.Misra@Sun.COM * for the removal completion.
9610946SSangeeta.Misra@Sun.COM *
9710946SSangeeta.Misra@Sun.COM * The conn hash/sticky hash taskq is for processing ilb_conn_hash and
9810946SSangeeta.Misra@Sun.COM * ilb_sticky_hash table entry removal. There are ilb_conn_timer_size timers
9910946SSangeeta.Misra@Sun.COM * and ilb_sticky_timer_size timers running for ilb_conn_hash and
10010946SSangeeta.Misra@Sun.COM * ilb_sticky_hash cleanup respectively. Each timer is responsible for one
10110946SSangeeta.Misra@Sun.COM * portion (same size) of the hash table. When a timer fires, it dispatches
10210946SSangeeta.Misra@Sun.COM * a conn hash taskq to clean up its portion of the table. This avoids in
10310946SSangeeta.Misra@Sun.COM * line processing of the removal.
10410946SSangeeta.Misra@Sun.COM *
10510946SSangeeta.Misra@Sun.COM * There is another delayed processing, the clean up of NAT source address
10610946SSangeeta.Misra@Sun.COM * table. We just use the timer to directly handle it instead of using
10710946SSangeeta.Misra@Sun.COM * a taskq. The reason is that the table is small so it is OK to use the
10810946SSangeeta.Misra@Sun.COM * timer.
10910946SSangeeta.Misra@Sun.COM */
11010946SSangeeta.Misra@Sun.COM
11110946SSangeeta.Misra@Sun.COM /* ILB rule taskq constants. */
11210946SSangeeta.Misra@Sun.COM #define ILB_RULE_TASKQ_NUM_THR 20
11310946SSangeeta.Misra@Sun.COM
11410946SSangeeta.Misra@Sun.COM /* Argument passed to ILB rule taskq routines. */
11510946SSangeeta.Misra@Sun.COM typedef struct {
11610946SSangeeta.Misra@Sun.COM ilb_stack_t *ilbs;
11710946SSangeeta.Misra@Sun.COM ilb_rule_t *rule;
11810946SSangeeta.Misra@Sun.COM } ilb_rule_tq_t;
11910946SSangeeta.Misra@Sun.COM
12010946SSangeeta.Misra@Sun.COM /* kstat handling routines. */
12110946SSangeeta.Misra@Sun.COM static kstat_t *ilb_kstat_g_init(netstackid_t, ilb_stack_t *);
12210946SSangeeta.Misra@Sun.COM static void ilb_kstat_g_fini(netstackid_t, ilb_stack_t *);
12310946SSangeeta.Misra@Sun.COM static kstat_t *ilb_rule_kstat_init(netstackid_t, ilb_rule_t *);
12410946SSangeeta.Misra@Sun.COM static kstat_t *ilb_server_kstat_init(netstackid_t, ilb_rule_t *,
12510946SSangeeta.Misra@Sun.COM ilb_server_t *);
12610946SSangeeta.Misra@Sun.COM
12710946SSangeeta.Misra@Sun.COM /* Rule hash handling routines. */
12810946SSangeeta.Misra@Sun.COM static void ilb_rule_hash_init(ilb_stack_t *);
12910946SSangeeta.Misra@Sun.COM static void ilb_rule_hash_fini(ilb_stack_t *);
13010946SSangeeta.Misra@Sun.COM static void ilb_rule_hash_add(ilb_stack_t *, ilb_rule_t *, const in6_addr_t *);
13110946SSangeeta.Misra@Sun.COM static void ilb_rule_hash_del(ilb_rule_t *);
13210946SSangeeta.Misra@Sun.COM static ilb_rule_t *ilb_rule_hash(ilb_stack_t *, int, int, in6_addr_t *,
13310946SSangeeta.Misra@Sun.COM in_port_t, zoneid_t, uint32_t, boolean_t *);
13410946SSangeeta.Misra@Sun.COM
13510946SSangeeta.Misra@Sun.COM static void ilb_rule_g_add(ilb_stack_t *, ilb_rule_t *);
13610946SSangeeta.Misra@Sun.COM static void ilb_rule_g_del(ilb_stack_t *, ilb_rule_t *);
13710946SSangeeta.Misra@Sun.COM static void ilb_del_rule_common(ilb_stack_t *, ilb_rule_t *);
13810946SSangeeta.Misra@Sun.COM static ilb_rule_t *ilb_find_rule_locked(ilb_stack_t *, zoneid_t, const char *,
13910946SSangeeta.Misra@Sun.COM int *);
14010946SSangeeta.Misra@Sun.COM static boolean_t ilb_match_rule(ilb_stack_t *, zoneid_t, const char *, int,
14110946SSangeeta.Misra@Sun.COM int, in_port_t, in_port_t, const in6_addr_t *);
14210946SSangeeta.Misra@Sun.COM
14310946SSangeeta.Misra@Sun.COM /* Back end server handling routines. */
14410946SSangeeta.Misra@Sun.COM static void ilb_server_free(ilb_server_t *);
14510946SSangeeta.Misra@Sun.COM
14610946SSangeeta.Misra@Sun.COM /* Network stack handling routines. */
14710946SSangeeta.Misra@Sun.COM static void *ilb_stack_init(netstackid_t, netstack_t *);
14810946SSangeeta.Misra@Sun.COM static void ilb_stack_shutdown(netstackid_t, void *);
14910946SSangeeta.Misra@Sun.COM static void ilb_stack_fini(netstackid_t, void *);
15010946SSangeeta.Misra@Sun.COM
15110946SSangeeta.Misra@Sun.COM /* Sticky connection handling routines. */
15210946SSangeeta.Misra@Sun.COM static void ilb_rule_sticky_init(ilb_rule_t *);
15310946SSangeeta.Misra@Sun.COM static void ilb_rule_sticky_fini(ilb_rule_t *);
15410946SSangeeta.Misra@Sun.COM
15510946SSangeeta.Misra@Sun.COM /* Handy macro to check for unspecified address. */
15610946SSangeeta.Misra@Sun.COM #define IS_ADDR_UNSPEC(addr) \
15710946SSangeeta.Misra@Sun.COM (IN6_IS_ADDR_V4MAPPED(addr) ? IN6_IS_ADDR_V4MAPPED_ANY(addr) : \
15810946SSangeeta.Misra@Sun.COM IN6_IS_ADDR_UNSPECIFIED(addr))
15910946SSangeeta.Misra@Sun.COM
16010946SSangeeta.Misra@Sun.COM /*
16110946SSangeeta.Misra@Sun.COM * Global kstat instance counter. When a rule is created, its kstat instance
16210946SSangeeta.Misra@Sun.COM * number is assigned by ilb_kstat_instance and ilb_kstat_instance is
16310946SSangeeta.Misra@Sun.COM * incremented.
16410946SSangeeta.Misra@Sun.COM */
16510946SSangeeta.Misra@Sun.COM static uint_t ilb_kstat_instance = 0;
16610946SSangeeta.Misra@Sun.COM
16710946SSangeeta.Misra@Sun.COM /*
16810946SSangeeta.Misra@Sun.COM * The ILB global kstat has name ILB_G_KS_NAME and class name ILB_G_KS_CNAME.
16910946SSangeeta.Misra@Sun.COM * A rule's kstat has ILB_RULE_KS_CNAME class name.
17010946SSangeeta.Misra@Sun.COM */
17110946SSangeeta.Misra@Sun.COM #define ILB_G_KS_NAME "global"
17210946SSangeeta.Misra@Sun.COM #define ILB_G_KS_CNAME "kstat"
17310946SSangeeta.Misra@Sun.COM #define ILB_RULE_KS_CNAME "rulestat"
17410946SSangeeta.Misra@Sun.COM
17510946SSangeeta.Misra@Sun.COM static kstat_t *
ilb_kstat_g_init(netstackid_t stackid,ilb_stack_t * ilbs)17610946SSangeeta.Misra@Sun.COM ilb_kstat_g_init(netstackid_t stackid, ilb_stack_t *ilbs)
17710946SSangeeta.Misra@Sun.COM {
17810946SSangeeta.Misra@Sun.COM kstat_t *ksp;
17910946SSangeeta.Misra@Sun.COM ilb_g_kstat_t template = {
18010946SSangeeta.Misra@Sun.COM { "num_rules", KSTAT_DATA_UINT64, 0 },
18110946SSangeeta.Misra@Sun.COM { "ip_frag_in", KSTAT_DATA_UINT64, 0 },
18210946SSangeeta.Misra@Sun.COM { "ip_frag_dropped", KSTAT_DATA_UINT64, 0 }
18310946SSangeeta.Misra@Sun.COM };
18410946SSangeeta.Misra@Sun.COM
18510946SSangeeta.Misra@Sun.COM ksp = kstat_create_netstack(ILB_KSTAT_MOD_NAME, 0, ILB_G_KS_NAME,
18610946SSangeeta.Misra@Sun.COM ILB_G_KS_CNAME, KSTAT_TYPE_NAMED, NUM_OF_FIELDS(ilb_g_kstat_t),
18710946SSangeeta.Misra@Sun.COM KSTAT_FLAG_VIRTUAL, stackid);
18810946SSangeeta.Misra@Sun.COM if (ksp == NULL)
18910946SSangeeta.Misra@Sun.COM return (NULL);
19010946SSangeeta.Misra@Sun.COM bcopy(&template, ilbs->ilbs_kstat, sizeof (template));
19110946SSangeeta.Misra@Sun.COM ksp->ks_data = ilbs->ilbs_kstat;
19210946SSangeeta.Misra@Sun.COM ksp->ks_private = (void *)(uintptr_t)stackid;
19310946SSangeeta.Misra@Sun.COM
19410946SSangeeta.Misra@Sun.COM kstat_install(ksp);
19510946SSangeeta.Misra@Sun.COM return (ksp);
19610946SSangeeta.Misra@Sun.COM }
19710946SSangeeta.Misra@Sun.COM
19810946SSangeeta.Misra@Sun.COM static void
ilb_kstat_g_fini(netstackid_t stackid,ilb_stack_t * ilbs)19910946SSangeeta.Misra@Sun.COM ilb_kstat_g_fini(netstackid_t stackid, ilb_stack_t *ilbs)
20010946SSangeeta.Misra@Sun.COM {
20110946SSangeeta.Misra@Sun.COM if (ilbs->ilbs_ksp != NULL) {
20210946SSangeeta.Misra@Sun.COM ASSERT(stackid == (netstackid_t)(uintptr_t)
20310946SSangeeta.Misra@Sun.COM ilbs->ilbs_ksp->ks_private);
20410946SSangeeta.Misra@Sun.COM kstat_delete_netstack(ilbs->ilbs_ksp, stackid);
20510946SSangeeta.Misra@Sun.COM ilbs->ilbs_ksp = NULL;
20610946SSangeeta.Misra@Sun.COM }
20710946SSangeeta.Misra@Sun.COM }
20810946SSangeeta.Misra@Sun.COM
20910946SSangeeta.Misra@Sun.COM static kstat_t *
ilb_rule_kstat_init(netstackid_t stackid,ilb_rule_t * rule)21010946SSangeeta.Misra@Sun.COM ilb_rule_kstat_init(netstackid_t stackid, ilb_rule_t *rule)
21110946SSangeeta.Misra@Sun.COM {
21210946SSangeeta.Misra@Sun.COM kstat_t *ksp;
21310946SSangeeta.Misra@Sun.COM ilb_rule_kstat_t template = {
21410946SSangeeta.Misra@Sun.COM { "num_servers", KSTAT_DATA_UINT64, 0 },
21510946SSangeeta.Misra@Sun.COM { "bytes_not_processed", KSTAT_DATA_UINT64, 0 },
21610946SSangeeta.Misra@Sun.COM { "pkt_not_processed", KSTAT_DATA_UINT64, 0 },
21710946SSangeeta.Misra@Sun.COM { "bytes_dropped", KSTAT_DATA_UINT64, 0 },
21810946SSangeeta.Misra@Sun.COM { "pkt_dropped", KSTAT_DATA_UINT64, 0 },
21910946SSangeeta.Misra@Sun.COM { "nomem_bytes_dropped", KSTAT_DATA_UINT64, 0 },
22010946SSangeeta.Misra@Sun.COM { "nomem_pkt_dropped", KSTAT_DATA_UINT64, 0 },
22110946SSangeeta.Misra@Sun.COM { "noport_bytes_dropped", KSTAT_DATA_UINT64, 0 },
22210946SSangeeta.Misra@Sun.COM { "noport_pkt_dropped", KSTAT_DATA_UINT64, 0 },
22310946SSangeeta.Misra@Sun.COM { "icmp_echo_processed", KSTAT_DATA_UINT64, 0 },
22410946SSangeeta.Misra@Sun.COM { "icmp_dropped", KSTAT_DATA_UINT64, 0 },
22510946SSangeeta.Misra@Sun.COM { "icmp_too_big_processed", KSTAT_DATA_UINT64, 0 },
22610946SSangeeta.Misra@Sun.COM { "icmp_too_big_dropped", KSTAT_DATA_UINT64, 0 }
22710946SSangeeta.Misra@Sun.COM };
22810946SSangeeta.Misra@Sun.COM
22910946SSangeeta.Misra@Sun.COM ksp = kstat_create_netstack(ILB_KSTAT_MOD_NAME, rule->ir_ks_instance,
23010946SSangeeta.Misra@Sun.COM rule->ir_name, ILB_RULE_KS_CNAME, KSTAT_TYPE_NAMED,
23110946SSangeeta.Misra@Sun.COM NUM_OF_FIELDS(ilb_rule_kstat_t), KSTAT_FLAG_VIRTUAL, stackid);
23210946SSangeeta.Misra@Sun.COM if (ksp == NULL)
23310946SSangeeta.Misra@Sun.COM return (NULL);
23410946SSangeeta.Misra@Sun.COM
23510946SSangeeta.Misra@Sun.COM bcopy(&template, &rule->ir_kstat, sizeof (template));
23610946SSangeeta.Misra@Sun.COM ksp->ks_data = &rule->ir_kstat;
23710946SSangeeta.Misra@Sun.COM ksp->ks_private = (void *)(uintptr_t)stackid;
23810946SSangeeta.Misra@Sun.COM
23910946SSangeeta.Misra@Sun.COM kstat_install(ksp);
24010946SSangeeta.Misra@Sun.COM return (ksp);
24110946SSangeeta.Misra@Sun.COM }
24210946SSangeeta.Misra@Sun.COM
24310946SSangeeta.Misra@Sun.COM static kstat_t *
ilb_server_kstat_init(netstackid_t stackid,ilb_rule_t * rule,ilb_server_t * server)24410946SSangeeta.Misra@Sun.COM ilb_server_kstat_init(netstackid_t stackid, ilb_rule_t *rule,
24510946SSangeeta.Misra@Sun.COM ilb_server_t *server)
24610946SSangeeta.Misra@Sun.COM {
24710946SSangeeta.Misra@Sun.COM kstat_t *ksp;
24810946SSangeeta.Misra@Sun.COM ilb_server_kstat_t template = {
24910946SSangeeta.Misra@Sun.COM { "bytes_processed", KSTAT_DATA_UINT64, 0 },
25010946SSangeeta.Misra@Sun.COM { "pkt_processed", KSTAT_DATA_UINT64, 0 },
25110946SSangeeta.Misra@Sun.COM { "ip_address", KSTAT_DATA_STRING, 0 }
25210946SSangeeta.Misra@Sun.COM };
25310946SSangeeta.Misra@Sun.COM char cname_buf[KSTAT_STRLEN];
25410946SSangeeta.Misra@Sun.COM
25510946SSangeeta.Misra@Sun.COM /* 7 is "-sstat" */
25610946SSangeeta.Misra@Sun.COM ASSERT(strlen(rule->ir_name) + 7 < KSTAT_STRLEN);
25710946SSangeeta.Misra@Sun.COM (void) sprintf(cname_buf, "%s-sstat", rule->ir_name);
25810946SSangeeta.Misra@Sun.COM ksp = kstat_create_netstack(ILB_KSTAT_MOD_NAME, rule->ir_ks_instance,
25910946SSangeeta.Misra@Sun.COM server->iser_name, cname_buf, KSTAT_TYPE_NAMED,
26010946SSangeeta.Misra@Sun.COM NUM_OF_FIELDS(ilb_server_kstat_t), KSTAT_FLAG_VIRTUAL, stackid);
26110946SSangeeta.Misra@Sun.COM if (ksp == NULL)
26210946SSangeeta.Misra@Sun.COM return (NULL);
26310946SSangeeta.Misra@Sun.COM
26410946SSangeeta.Misra@Sun.COM bcopy(&template, &server->iser_kstat, sizeof (template));
26510946SSangeeta.Misra@Sun.COM ksp->ks_data = &server->iser_kstat;
26610946SSangeeta.Misra@Sun.COM ksp->ks_private = (void *)(uintptr_t)stackid;
26710946SSangeeta.Misra@Sun.COM
26810946SSangeeta.Misra@Sun.COM kstat_named_setstr(&server->iser_kstat.ip_address,
26910946SSangeeta.Misra@Sun.COM server->iser_ip_addr);
27010946SSangeeta.Misra@Sun.COM /* We never change the IP address */
27110946SSangeeta.Misra@Sun.COM ksp->ks_data_size += strlen(server->iser_ip_addr) + 1;
27210946SSangeeta.Misra@Sun.COM
27310946SSangeeta.Misra@Sun.COM kstat_install(ksp);
27410946SSangeeta.Misra@Sun.COM return (ksp);
27510946SSangeeta.Misra@Sun.COM }
27610946SSangeeta.Misra@Sun.COM
27710946SSangeeta.Misra@Sun.COM /* Initialize the rule hash table. */
27810946SSangeeta.Misra@Sun.COM static void
ilb_rule_hash_init(ilb_stack_t * ilbs)27910946SSangeeta.Misra@Sun.COM ilb_rule_hash_init(ilb_stack_t *ilbs)
28010946SSangeeta.Misra@Sun.COM {
28110946SSangeeta.Misra@Sun.COM int i;
28210946SSangeeta.Misra@Sun.COM
28310946SSangeeta.Misra@Sun.COM /*
28410946SSangeeta.Misra@Sun.COM * If ilbs->ilbs_rule_hash_size is not a power of 2, bump it up to
28510946SSangeeta.Misra@Sun.COM * the next power of 2.
28610946SSangeeta.Misra@Sun.COM */
28710946SSangeeta.Misra@Sun.COM if (ilbs->ilbs_rule_hash_size & (ilbs->ilbs_rule_hash_size - 1)) {
28810946SSangeeta.Misra@Sun.COM for (i = 0; i < 31; i++) {
28910946SSangeeta.Misra@Sun.COM if (ilbs->ilbs_rule_hash_size < (1 << i))
29010946SSangeeta.Misra@Sun.COM break;
29110946SSangeeta.Misra@Sun.COM }
29210946SSangeeta.Misra@Sun.COM ilbs->ilbs_rule_hash_size = 1 << i;
29310946SSangeeta.Misra@Sun.COM }
29410946SSangeeta.Misra@Sun.COM ilbs->ilbs_g_hash = kmem_zalloc(sizeof (ilb_hash_t) *
29510946SSangeeta.Misra@Sun.COM ilbs->ilbs_rule_hash_size, KM_SLEEP);
29610946SSangeeta.Misra@Sun.COM for (i = 0; i < ilbs->ilbs_rule_hash_size; i++) {
29710946SSangeeta.Misra@Sun.COM mutex_init(&ilbs->ilbs_g_hash[i].ilb_hash_lock, NULL,
29810946SSangeeta.Misra@Sun.COM MUTEX_DEFAULT, NULL);
29910946SSangeeta.Misra@Sun.COM }
30010946SSangeeta.Misra@Sun.COM }
30110946SSangeeta.Misra@Sun.COM
30210946SSangeeta.Misra@Sun.COM /* Clean up the rule hash table. */
30310946SSangeeta.Misra@Sun.COM static void
ilb_rule_hash_fini(ilb_stack_t * ilbs)30410946SSangeeta.Misra@Sun.COM ilb_rule_hash_fini(ilb_stack_t *ilbs)
30510946SSangeeta.Misra@Sun.COM {
30610946SSangeeta.Misra@Sun.COM if (ilbs->ilbs_g_hash == NULL)
30710946SSangeeta.Misra@Sun.COM return;
30810946SSangeeta.Misra@Sun.COM kmem_free(ilbs->ilbs_g_hash, sizeof (ilb_hash_t) *
30910946SSangeeta.Misra@Sun.COM ilbs->ilbs_rule_hash_size);
31010946SSangeeta.Misra@Sun.COM }
31110946SSangeeta.Misra@Sun.COM
31210946SSangeeta.Misra@Sun.COM /* Add a rule to the rule hash table. */
31310946SSangeeta.Misra@Sun.COM static void
ilb_rule_hash_add(ilb_stack_t * ilbs,ilb_rule_t * rule,const in6_addr_t * addr)31410946SSangeeta.Misra@Sun.COM ilb_rule_hash_add(ilb_stack_t *ilbs, ilb_rule_t *rule, const in6_addr_t *addr)
31510946SSangeeta.Misra@Sun.COM {
31610946SSangeeta.Misra@Sun.COM int i;
31710946SSangeeta.Misra@Sun.COM
31810946SSangeeta.Misra@Sun.COM i = ILB_RULE_HASH((uint8_t *)&addr->s6_addr32[3],
31910946SSangeeta.Misra@Sun.COM ilbs->ilbs_rule_hash_size);
32010946SSangeeta.Misra@Sun.COM DTRACE_PROBE2(ilb__rule__hash__add, ilb_rule_t *, rule, int, i);
32110946SSangeeta.Misra@Sun.COM mutex_enter(&ilbs->ilbs_g_hash[i].ilb_hash_lock);
32210946SSangeeta.Misra@Sun.COM rule->ir_hash_next = ilbs->ilbs_g_hash[i].ilb_hash_rule;
32310946SSangeeta.Misra@Sun.COM if (ilbs->ilbs_g_hash[i].ilb_hash_rule != NULL)
32410946SSangeeta.Misra@Sun.COM ilbs->ilbs_g_hash[i].ilb_hash_rule->ir_hash_prev = rule;
32510946SSangeeta.Misra@Sun.COM rule->ir_hash_prev = NULL;
32610946SSangeeta.Misra@Sun.COM ilbs->ilbs_g_hash[i].ilb_hash_rule = rule;
32710946SSangeeta.Misra@Sun.COM
32810946SSangeeta.Misra@Sun.COM rule->ir_hash = &ilbs->ilbs_g_hash[i];
32910946SSangeeta.Misra@Sun.COM mutex_exit(&ilbs->ilbs_g_hash[i].ilb_hash_lock);
33010946SSangeeta.Misra@Sun.COM }
33110946SSangeeta.Misra@Sun.COM
33210946SSangeeta.Misra@Sun.COM /*
33310946SSangeeta.Misra@Sun.COM * Remove a rule from the rule hash table. Note that the rule is not freed
33410946SSangeeta.Misra@Sun.COM * in this routine.
33510946SSangeeta.Misra@Sun.COM */
33610946SSangeeta.Misra@Sun.COM static void
ilb_rule_hash_del(ilb_rule_t * rule)33710946SSangeeta.Misra@Sun.COM ilb_rule_hash_del(ilb_rule_t *rule)
33810946SSangeeta.Misra@Sun.COM {
33910946SSangeeta.Misra@Sun.COM mutex_enter(&rule->ir_hash->ilb_hash_lock);
34010946SSangeeta.Misra@Sun.COM if (rule->ir_hash->ilb_hash_rule == rule) {
34110946SSangeeta.Misra@Sun.COM rule->ir_hash->ilb_hash_rule = rule->ir_hash_next;
34210946SSangeeta.Misra@Sun.COM if (rule->ir_hash_next != NULL)
34310946SSangeeta.Misra@Sun.COM rule->ir_hash_next->ir_hash_prev = NULL;
34410946SSangeeta.Misra@Sun.COM } else {
34510946SSangeeta.Misra@Sun.COM if (rule->ir_hash_prev != NULL)
34610946SSangeeta.Misra@Sun.COM rule->ir_hash_prev->ir_hash_next =
34710946SSangeeta.Misra@Sun.COM rule->ir_hash_next;
34810946SSangeeta.Misra@Sun.COM if (rule->ir_hash_next != NULL) {
34910946SSangeeta.Misra@Sun.COM rule->ir_hash_next->ir_hash_prev =
35010946SSangeeta.Misra@Sun.COM rule->ir_hash_prev;
35110946SSangeeta.Misra@Sun.COM }
35210946SSangeeta.Misra@Sun.COM }
35310946SSangeeta.Misra@Sun.COM mutex_exit(&rule->ir_hash->ilb_hash_lock);
35410946SSangeeta.Misra@Sun.COM
35510946SSangeeta.Misra@Sun.COM rule->ir_hash_next = NULL;
35610946SSangeeta.Misra@Sun.COM rule->ir_hash_prev = NULL;
35710946SSangeeta.Misra@Sun.COM rule->ir_hash = NULL;
35810946SSangeeta.Misra@Sun.COM }
35910946SSangeeta.Misra@Sun.COM
36010946SSangeeta.Misra@Sun.COM /*
36110946SSangeeta.Misra@Sun.COM * Given the info of a packet, look for a match in the rule hash table.
36210946SSangeeta.Misra@Sun.COM */
36310946SSangeeta.Misra@Sun.COM static ilb_rule_t *
ilb_rule_hash(ilb_stack_t * ilbs,int l3,int l4,in6_addr_t * addr,in_port_t port,zoneid_t zoneid,uint32_t len,boolean_t * busy)36410946SSangeeta.Misra@Sun.COM ilb_rule_hash(ilb_stack_t *ilbs, int l3, int l4, in6_addr_t *addr,
36510946SSangeeta.Misra@Sun.COM in_port_t port, zoneid_t zoneid, uint32_t len, boolean_t *busy)
36610946SSangeeta.Misra@Sun.COM {
36710946SSangeeta.Misra@Sun.COM int i;
36810946SSangeeta.Misra@Sun.COM ilb_rule_t *rule;
36910946SSangeeta.Misra@Sun.COM ipaddr_t v4_addr;
37010946SSangeeta.Misra@Sun.COM
37110946SSangeeta.Misra@Sun.COM *busy = B_FALSE;
37210946SSangeeta.Misra@Sun.COM IN6_V4MAPPED_TO_IPADDR(addr, v4_addr);
37310946SSangeeta.Misra@Sun.COM i = ILB_RULE_HASH((uint8_t *)&v4_addr, ilbs->ilbs_rule_hash_size);
37410946SSangeeta.Misra@Sun.COM port = ntohs(port);
37510946SSangeeta.Misra@Sun.COM
37610946SSangeeta.Misra@Sun.COM mutex_enter(&ilbs->ilbs_g_hash[i].ilb_hash_lock);
37710946SSangeeta.Misra@Sun.COM for (rule = ilbs->ilbs_g_hash[i].ilb_hash_rule; rule != NULL;
37810946SSangeeta.Misra@Sun.COM rule = rule->ir_hash_next) {
37910946SSangeeta.Misra@Sun.COM if (!rule->ir_port_range) {
38010946SSangeeta.Misra@Sun.COM if (rule->ir_min_port != port)
38110946SSangeeta.Misra@Sun.COM continue;
38210946SSangeeta.Misra@Sun.COM } else {
38310946SSangeeta.Misra@Sun.COM if (port < rule->ir_min_port ||
38410946SSangeeta.Misra@Sun.COM port > rule->ir_max_port) {
38510946SSangeeta.Misra@Sun.COM continue;
38610946SSangeeta.Misra@Sun.COM }
38710946SSangeeta.Misra@Sun.COM }
38810946SSangeeta.Misra@Sun.COM if (rule->ir_ipver != l3 || rule->ir_proto != l4 ||
38910946SSangeeta.Misra@Sun.COM rule->ir_zoneid != zoneid) {
39010946SSangeeta.Misra@Sun.COM continue;
39110946SSangeeta.Misra@Sun.COM }
39210946SSangeeta.Misra@Sun.COM
39310946SSangeeta.Misra@Sun.COM if (l3 == IPPROTO_IP) {
39410946SSangeeta.Misra@Sun.COM if (rule->ir_target_v4 != INADDR_ANY &&
39510946SSangeeta.Misra@Sun.COM rule->ir_target_v4 != v4_addr) {
39610946SSangeeta.Misra@Sun.COM continue;
39710946SSangeeta.Misra@Sun.COM }
39810946SSangeeta.Misra@Sun.COM } else {
39910946SSangeeta.Misra@Sun.COM if (!IN6_IS_ADDR_UNSPECIFIED(&rule->ir_target_v6) &&
40010946SSangeeta.Misra@Sun.COM !IN6_ARE_ADDR_EQUAL(addr, &rule->ir_target_v6)) {
40110946SSangeeta.Misra@Sun.COM continue;
40210946SSangeeta.Misra@Sun.COM }
40310946SSangeeta.Misra@Sun.COM }
40410946SSangeeta.Misra@Sun.COM
40510946SSangeeta.Misra@Sun.COM /*
40610946SSangeeta.Misra@Sun.COM * Just update the stats if the rule is disabled.
40710946SSangeeta.Misra@Sun.COM */
40810946SSangeeta.Misra@Sun.COM mutex_enter(&rule->ir_lock);
40910946SSangeeta.Misra@Sun.COM if (!(rule->ir_flags & ILB_RULE_ENABLED)) {
41010946SSangeeta.Misra@Sun.COM ILB_R_KSTAT(rule, pkt_not_processed);
41110946SSangeeta.Misra@Sun.COM ILB_R_KSTAT_UPDATE(rule, bytes_not_processed, len);
41210946SSangeeta.Misra@Sun.COM mutex_exit(&rule->ir_lock);
41310946SSangeeta.Misra@Sun.COM rule = NULL;
41410946SSangeeta.Misra@Sun.COM break;
41510946SSangeeta.Misra@Sun.COM } else if (rule->ir_flags & ILB_RULE_BUSY) {
41610946SSangeeta.Misra@Sun.COM /*
41710946SSangeeta.Misra@Sun.COM * If we are busy...
41810946SSangeeta.Misra@Sun.COM *
41910946SSangeeta.Misra@Sun.COM * XXX we should have a queue to postpone the
42010946SSangeeta.Misra@Sun.COM * packet processing. But this requires a
42110946SSangeeta.Misra@Sun.COM * mechanism in IP to re-start the packet
42210946SSangeeta.Misra@Sun.COM * processing. So for now, just drop the packet.
42310946SSangeeta.Misra@Sun.COM */
42410946SSangeeta.Misra@Sun.COM ILB_R_KSTAT(rule, pkt_dropped);
42510946SSangeeta.Misra@Sun.COM ILB_R_KSTAT_UPDATE(rule, bytes_dropped, len);
42610946SSangeeta.Misra@Sun.COM mutex_exit(&rule->ir_lock);
42710946SSangeeta.Misra@Sun.COM *busy = B_TRUE;
42810946SSangeeta.Misra@Sun.COM rule = NULL;
42910946SSangeeta.Misra@Sun.COM break;
43010946SSangeeta.Misra@Sun.COM } else {
43110946SSangeeta.Misra@Sun.COM rule->ir_refcnt++;
43210946SSangeeta.Misra@Sun.COM ASSERT(rule->ir_refcnt != 1);
43310946SSangeeta.Misra@Sun.COM mutex_exit(&rule->ir_lock);
43410946SSangeeta.Misra@Sun.COM break;
43510946SSangeeta.Misra@Sun.COM }
43610946SSangeeta.Misra@Sun.COM }
43710946SSangeeta.Misra@Sun.COM mutex_exit(&ilbs->ilbs_g_hash[i].ilb_hash_lock);
43810946SSangeeta.Misra@Sun.COM return (rule);
43910946SSangeeta.Misra@Sun.COM }
44010946SSangeeta.Misra@Sun.COM
44110946SSangeeta.Misra@Sun.COM /*
44210946SSangeeta.Misra@Sun.COM * Add a rule to the global rule list. This list is for finding all rules
44310946SSangeeta.Misra@Sun.COM * in an IP stack. The caller is assumed to hold the ilbs_g_lock.
44410946SSangeeta.Misra@Sun.COM */
44510946SSangeeta.Misra@Sun.COM static void
ilb_rule_g_add(ilb_stack_t * ilbs,ilb_rule_t * rule)44610946SSangeeta.Misra@Sun.COM ilb_rule_g_add(ilb_stack_t *ilbs, ilb_rule_t *rule)
44710946SSangeeta.Misra@Sun.COM {
44810946SSangeeta.Misra@Sun.COM ASSERT(mutex_owned(&ilbs->ilbs_g_lock));
44910946SSangeeta.Misra@Sun.COM rule->ir_next = ilbs->ilbs_rule_head;
45010946SSangeeta.Misra@Sun.COM ilbs->ilbs_rule_head = rule;
45110946SSangeeta.Misra@Sun.COM ILB_KSTAT_UPDATE(ilbs, num_rules, 1);
45210946SSangeeta.Misra@Sun.COM }
45310946SSangeeta.Misra@Sun.COM
45410946SSangeeta.Misra@Sun.COM /* The call is assumed to hold the ilbs_g_lock. */
45510946SSangeeta.Misra@Sun.COM static void
ilb_rule_g_del(ilb_stack_t * ilbs,ilb_rule_t * rule)45610946SSangeeta.Misra@Sun.COM ilb_rule_g_del(ilb_stack_t *ilbs, ilb_rule_t *rule)
45710946SSangeeta.Misra@Sun.COM {
45810946SSangeeta.Misra@Sun.COM ilb_rule_t *tmp_rule;
45910946SSangeeta.Misra@Sun.COM ilb_rule_t *prev_rule;
46010946SSangeeta.Misra@Sun.COM
46110946SSangeeta.Misra@Sun.COM ASSERT(mutex_owned(&ilbs->ilbs_g_lock));
46210946SSangeeta.Misra@Sun.COM prev_rule = NULL;
46310946SSangeeta.Misra@Sun.COM for (tmp_rule = ilbs->ilbs_rule_head; tmp_rule != NULL;
46410946SSangeeta.Misra@Sun.COM prev_rule = tmp_rule, tmp_rule = tmp_rule->ir_next) {
46510946SSangeeta.Misra@Sun.COM if (tmp_rule == rule)
46610946SSangeeta.Misra@Sun.COM break;
46710946SSangeeta.Misra@Sun.COM }
46810946SSangeeta.Misra@Sun.COM if (tmp_rule == NULL) {
46910946SSangeeta.Misra@Sun.COM mutex_exit(&ilbs->ilbs_g_lock);
47010946SSangeeta.Misra@Sun.COM return;
47110946SSangeeta.Misra@Sun.COM }
47210946SSangeeta.Misra@Sun.COM if (prev_rule == NULL)
47310946SSangeeta.Misra@Sun.COM ilbs->ilbs_rule_head = tmp_rule->ir_next;
47410946SSangeeta.Misra@Sun.COM else
47510946SSangeeta.Misra@Sun.COM prev_rule->ir_next = tmp_rule->ir_next;
47610946SSangeeta.Misra@Sun.COM ILB_KSTAT_UPDATE(ilbs, num_rules, -1);
47710946SSangeeta.Misra@Sun.COM }
47810946SSangeeta.Misra@Sun.COM
47910946SSangeeta.Misra@Sun.COM /*
48010946SSangeeta.Misra@Sun.COM * Helper routine to calculate how many source addresses are in a given
48110946SSangeeta.Misra@Sun.COM * range.
48210946SSangeeta.Misra@Sun.COM */
48310946SSangeeta.Misra@Sun.COM static int64_t
num_nat_src_v6(const in6_addr_t * a1,const in6_addr_t * a2)48410946SSangeeta.Misra@Sun.COM num_nat_src_v6(const in6_addr_t *a1, const in6_addr_t *a2)
48510946SSangeeta.Misra@Sun.COM {
48610946SSangeeta.Misra@Sun.COM int64_t ret;
48710946SSangeeta.Misra@Sun.COM uint32_t addr1, addr2;
48810946SSangeeta.Misra@Sun.COM
48910946SSangeeta.Misra@Sun.COM /*
49010946SSangeeta.Misra@Sun.COM * Here we assume that the max number of NAT source cannot be
49110946SSangeeta.Misra@Sun.COM * large such that the most significant 2 s6_addr32 must be
49210946SSangeeta.Misra@Sun.COM * equal.
49310946SSangeeta.Misra@Sun.COM */
49410946SSangeeta.Misra@Sun.COM addr1 = ntohl(a1->s6_addr32[3]);
49510946SSangeeta.Misra@Sun.COM addr2 = ntohl(a2->s6_addr32[3]);
49610946SSangeeta.Misra@Sun.COM if (a1->s6_addr32[0] != a2->s6_addr32[0] ||
49710946SSangeeta.Misra@Sun.COM a1->s6_addr32[1] != a2->s6_addr32[1] ||
49810946SSangeeta.Misra@Sun.COM a1->s6_addr32[2] > a2->s6_addr32[2] ||
49910946SSangeeta.Misra@Sun.COM (a1->s6_addr32[2] == a2->s6_addr32[2] && addr1 > addr2)) {
50010946SSangeeta.Misra@Sun.COM return (-1);
50110946SSangeeta.Misra@Sun.COM }
50210946SSangeeta.Misra@Sun.COM if (a1->s6_addr32[2] == a2->s6_addr32[2]) {
50310946SSangeeta.Misra@Sun.COM return (addr2 - addr1 + 1);
50410946SSangeeta.Misra@Sun.COM } else {
50510946SSangeeta.Misra@Sun.COM ret = (ntohl(a2->s6_addr32[2]) - ntohl(a1->s6_addr32[2]));
50610946SSangeeta.Misra@Sun.COM ret <<= 32;
50710946SSangeeta.Misra@Sun.COM ret = ret + addr1 - addr2;
50810946SSangeeta.Misra@Sun.COM return (ret + 1);
50910946SSangeeta.Misra@Sun.COM }
51010946SSangeeta.Misra@Sun.COM }
51110946SSangeeta.Misra@Sun.COM
51210946SSangeeta.Misra@Sun.COM /*
51310946SSangeeta.Misra@Sun.COM * Add an ILB rule.
51410946SSangeeta.Misra@Sun.COM */
51510946SSangeeta.Misra@Sun.COM int
ilb_rule_add(ilb_stack_t * ilbs,zoneid_t zoneid,const ilb_rule_cmd_t * cmd)51610946SSangeeta.Misra@Sun.COM ilb_rule_add(ilb_stack_t *ilbs, zoneid_t zoneid, const ilb_rule_cmd_t *cmd)
51710946SSangeeta.Misra@Sun.COM {
51810946SSangeeta.Misra@Sun.COM ilb_rule_t *rule;
51910946SSangeeta.Misra@Sun.COM netstackid_t stackid;
52010946SSangeeta.Misra@Sun.COM int ret;
52110946SSangeeta.Misra@Sun.COM in_port_t min_port, max_port;
52210946SSangeeta.Misra@Sun.COM int64_t num_src;
52310946SSangeeta.Misra@Sun.COM
52410946SSangeeta.Misra@Sun.COM /* Sanity checks. */
52510946SSangeeta.Misra@Sun.COM if (cmd->ip_ver != IPPROTO_IP && cmd->ip_ver != IPPROTO_IPV6)
52610946SSangeeta.Misra@Sun.COM return (EINVAL);
52710946SSangeeta.Misra@Sun.COM
52810946SSangeeta.Misra@Sun.COM /* Need to support SCTP... */
52910946SSangeeta.Misra@Sun.COM if (cmd->proto != IPPROTO_TCP && cmd->proto != IPPROTO_UDP)
53010946SSangeeta.Misra@Sun.COM return (EINVAL);
53110946SSangeeta.Misra@Sun.COM
53210946SSangeeta.Misra@Sun.COM /* For full NAT, the NAT source must be supplied. */
53310946SSangeeta.Misra@Sun.COM if (cmd->topo == ILB_TOPO_IMPL_NAT) {
53410946SSangeeta.Misra@Sun.COM if (IS_ADDR_UNSPEC(&cmd->nat_src_start) ||
53510946SSangeeta.Misra@Sun.COM IS_ADDR_UNSPEC(&cmd->nat_src_end)) {
53610946SSangeeta.Misra@Sun.COM return (EINVAL);
53710946SSangeeta.Misra@Sun.COM }
53810946SSangeeta.Misra@Sun.COM }
53910946SSangeeta.Misra@Sun.COM
54010946SSangeeta.Misra@Sun.COM /* Check invalid mask */
54110946SSangeeta.Misra@Sun.COM if ((cmd->flags & ILB_RULE_STICKY) &&
54210946SSangeeta.Misra@Sun.COM IS_ADDR_UNSPEC(&cmd->sticky_mask)) {
54310946SSangeeta.Misra@Sun.COM return (EINVAL);
54410946SSangeeta.Misra@Sun.COM }
54510946SSangeeta.Misra@Sun.COM
54610946SSangeeta.Misra@Sun.COM /* Port is passed in network byte order. */
54710946SSangeeta.Misra@Sun.COM min_port = ntohs(cmd->min_port);
54810946SSangeeta.Misra@Sun.COM max_port = ntohs(cmd->max_port);
54910946SSangeeta.Misra@Sun.COM if (min_port > max_port)
55010946SSangeeta.Misra@Sun.COM return (EINVAL);
55110946SSangeeta.Misra@Sun.COM
55210946SSangeeta.Misra@Sun.COM /* min_port == 0 means "all ports". Make it so */
55310946SSangeeta.Misra@Sun.COM if (min_port == 0) {
55410946SSangeeta.Misra@Sun.COM min_port = 1;
55510946SSangeeta.Misra@Sun.COM max_port = 65535;
55610946SSangeeta.Misra@Sun.COM }
55710946SSangeeta.Misra@Sun.COM
55810946SSangeeta.Misra@Sun.COM /* Funny address checking. */
55910946SSangeeta.Misra@Sun.COM if (cmd->ip_ver == IPPROTO_IP) {
56010946SSangeeta.Misra@Sun.COM in_addr_t v4_addr1, v4_addr2;
56110946SSangeeta.Misra@Sun.COM
56210946SSangeeta.Misra@Sun.COM v4_addr1 = cmd->vip.s6_addr32[3];
56310946SSangeeta.Misra@Sun.COM if ((*(uchar_t *)&v4_addr1) == IN_LOOPBACKNET ||
56410946SSangeeta.Misra@Sun.COM CLASSD(v4_addr1) || v4_addr1 == INADDR_BROADCAST ||
56510946SSangeeta.Misra@Sun.COM v4_addr1 == INADDR_ANY ||
56610946SSangeeta.Misra@Sun.COM !IN6_IS_ADDR_V4MAPPED(&cmd->vip)) {
56710946SSangeeta.Misra@Sun.COM return (EINVAL);
56810946SSangeeta.Misra@Sun.COM }
56910946SSangeeta.Misra@Sun.COM
57010946SSangeeta.Misra@Sun.COM if (cmd->topo == ILB_TOPO_IMPL_NAT) {
57110946SSangeeta.Misra@Sun.COM v4_addr1 = ntohl(cmd->nat_src_start.s6_addr32[3]);
57210946SSangeeta.Misra@Sun.COM v4_addr2 = ntohl(cmd->nat_src_end.s6_addr32[3]);
57310946SSangeeta.Misra@Sun.COM if ((*(uchar_t *)&v4_addr1) == IN_LOOPBACKNET ||
57410946SSangeeta.Misra@Sun.COM (*(uchar_t *)&v4_addr2) == IN_LOOPBACKNET ||
57510946SSangeeta.Misra@Sun.COM v4_addr1 == INADDR_BROADCAST ||
57610946SSangeeta.Misra@Sun.COM v4_addr2 == INADDR_BROADCAST ||
57710946SSangeeta.Misra@Sun.COM v4_addr1 == INADDR_ANY || v4_addr2 == INADDR_ANY ||
57810946SSangeeta.Misra@Sun.COM CLASSD(v4_addr1) || CLASSD(v4_addr2) ||
57910946SSangeeta.Misra@Sun.COM !IN6_IS_ADDR_V4MAPPED(&cmd->nat_src_start) ||
58010946SSangeeta.Misra@Sun.COM !IN6_IS_ADDR_V4MAPPED(&cmd->nat_src_end)) {
58110946SSangeeta.Misra@Sun.COM return (EINVAL);
58210946SSangeeta.Misra@Sun.COM }
58310946SSangeeta.Misra@Sun.COM
58410946SSangeeta.Misra@Sun.COM num_src = v4_addr2 - v4_addr1 + 1;
58510946SSangeeta.Misra@Sun.COM if (v4_addr1 > v4_addr2 || num_src > ILB_MAX_NAT_SRC)
58610946SSangeeta.Misra@Sun.COM return (EINVAL);
58710946SSangeeta.Misra@Sun.COM }
58810946SSangeeta.Misra@Sun.COM } else {
58910946SSangeeta.Misra@Sun.COM if (IN6_IS_ADDR_LOOPBACK(&cmd->vip) ||
59010946SSangeeta.Misra@Sun.COM IN6_IS_ADDR_MULTICAST(&cmd->vip) ||
59110946SSangeeta.Misra@Sun.COM IN6_IS_ADDR_UNSPECIFIED(&cmd->vip) ||
59210946SSangeeta.Misra@Sun.COM IN6_IS_ADDR_V4MAPPED(&cmd->vip)) {
59310946SSangeeta.Misra@Sun.COM return (EINVAL);
59410946SSangeeta.Misra@Sun.COM }
59510946SSangeeta.Misra@Sun.COM
59610946SSangeeta.Misra@Sun.COM if (cmd->topo == ILB_TOPO_IMPL_NAT) {
59710946SSangeeta.Misra@Sun.COM if (IN6_IS_ADDR_LOOPBACK(&cmd->nat_src_start) ||
59810946SSangeeta.Misra@Sun.COM IN6_IS_ADDR_LOOPBACK(&cmd->nat_src_end) ||
59910946SSangeeta.Misra@Sun.COM IN6_IS_ADDR_MULTICAST(&cmd->nat_src_start) ||
60010946SSangeeta.Misra@Sun.COM IN6_IS_ADDR_MULTICAST(&cmd->nat_src_end) ||
60110946SSangeeta.Misra@Sun.COM IN6_IS_ADDR_UNSPECIFIED(&cmd->nat_src_start) ||
60210946SSangeeta.Misra@Sun.COM IN6_IS_ADDR_UNSPECIFIED(&cmd->nat_src_end) ||
60310946SSangeeta.Misra@Sun.COM IN6_IS_ADDR_V4MAPPED(&cmd->nat_src_start) ||
60410946SSangeeta.Misra@Sun.COM IN6_IS_ADDR_V4MAPPED(&cmd->nat_src_end)) {
60510946SSangeeta.Misra@Sun.COM return (EINVAL);
60610946SSangeeta.Misra@Sun.COM }
60710946SSangeeta.Misra@Sun.COM
60810946SSangeeta.Misra@Sun.COM if ((num_src = num_nat_src_v6(&cmd->nat_src_start,
60910946SSangeeta.Misra@Sun.COM &cmd->nat_src_end)) < 0 ||
61010946SSangeeta.Misra@Sun.COM num_src > ILB_MAX_NAT_SRC) {
61110946SSangeeta.Misra@Sun.COM return (EINVAL);
61210946SSangeeta.Misra@Sun.COM }
61310946SSangeeta.Misra@Sun.COM }
61410946SSangeeta.Misra@Sun.COM }
61510946SSangeeta.Misra@Sun.COM
61610946SSangeeta.Misra@Sun.COM mutex_enter(&ilbs->ilbs_g_lock);
61710946SSangeeta.Misra@Sun.COM if (ilbs->ilbs_g_hash == NULL)
61810946SSangeeta.Misra@Sun.COM ilb_rule_hash_init(ilbs);
61910946SSangeeta.Misra@Sun.COM if (ilbs->ilbs_c2s_conn_hash == NULL) {
62010946SSangeeta.Misra@Sun.COM ASSERT(ilbs->ilbs_s2c_conn_hash == NULL);
62110946SSangeeta.Misra@Sun.COM ilb_conn_hash_init(ilbs);
62210946SSangeeta.Misra@Sun.COM ilb_nat_src_init(ilbs);
62310946SSangeeta.Misra@Sun.COM }
62410946SSangeeta.Misra@Sun.COM
62510946SSangeeta.Misra@Sun.COM /* Make sure that the new rule does not duplicate an existing one. */
62610946SSangeeta.Misra@Sun.COM if (ilb_match_rule(ilbs, zoneid, cmd->name, cmd->ip_ver, cmd->proto,
62710946SSangeeta.Misra@Sun.COM min_port, max_port, &cmd->vip)) {
62810946SSangeeta.Misra@Sun.COM mutex_exit(&ilbs->ilbs_g_lock);
62910946SSangeeta.Misra@Sun.COM return (EEXIST);
63010946SSangeeta.Misra@Sun.COM }
63110946SSangeeta.Misra@Sun.COM
63210946SSangeeta.Misra@Sun.COM rule = kmem_zalloc(sizeof (ilb_rule_t), KM_NOSLEEP);
63310946SSangeeta.Misra@Sun.COM if (rule == NULL) {
63410946SSangeeta.Misra@Sun.COM mutex_exit(&ilbs->ilbs_g_lock);
63510946SSangeeta.Misra@Sun.COM return (ENOMEM);
63610946SSangeeta.Misra@Sun.COM }
63710946SSangeeta.Misra@Sun.COM
63810946SSangeeta.Misra@Sun.COM /* ir_name is all 0 to begin with */
63910946SSangeeta.Misra@Sun.COM (void) memcpy(rule->ir_name, cmd->name, ILB_RULE_NAMESZ - 1);
64010946SSangeeta.Misra@Sun.COM
64110946SSangeeta.Misra@Sun.COM rule->ir_ks_instance = atomic_add_int_nv(&ilb_kstat_instance, 1);
64210946SSangeeta.Misra@Sun.COM stackid = (netstackid_t)(uintptr_t)ilbs->ilbs_ksp->ks_private;
64310946SSangeeta.Misra@Sun.COM if ((rule->ir_ksp = ilb_rule_kstat_init(stackid, rule)) == NULL) {
64410946SSangeeta.Misra@Sun.COM ret = ENOMEM;
64510946SSangeeta.Misra@Sun.COM goto error;
64610946SSangeeta.Misra@Sun.COM }
64710946SSangeeta.Misra@Sun.COM
64810946SSangeeta.Misra@Sun.COM if (cmd->topo == ILB_TOPO_IMPL_NAT) {
64910946SSangeeta.Misra@Sun.COM rule->ir_nat_src_start = cmd->nat_src_start;
65010946SSangeeta.Misra@Sun.COM rule->ir_nat_src_end = cmd->nat_src_end;
65110946SSangeeta.Misra@Sun.COM }
65210946SSangeeta.Misra@Sun.COM
65310946SSangeeta.Misra@Sun.COM rule->ir_ipver = cmd->ip_ver;
65410946SSangeeta.Misra@Sun.COM rule->ir_proto = cmd->proto;
65510946SSangeeta.Misra@Sun.COM rule->ir_topo = cmd->topo;
65610946SSangeeta.Misra@Sun.COM
65710946SSangeeta.Misra@Sun.COM rule->ir_min_port = min_port;
65810946SSangeeta.Misra@Sun.COM rule->ir_max_port = max_port;
65910946SSangeeta.Misra@Sun.COM if (rule->ir_min_port != rule->ir_max_port)
66010946SSangeeta.Misra@Sun.COM rule->ir_port_range = B_TRUE;
66110946SSangeeta.Misra@Sun.COM else
66210946SSangeeta.Misra@Sun.COM rule->ir_port_range = B_FALSE;
66310946SSangeeta.Misra@Sun.COM
66410946SSangeeta.Misra@Sun.COM rule->ir_zoneid = zoneid;
66510946SSangeeta.Misra@Sun.COM
66610946SSangeeta.Misra@Sun.COM rule->ir_target_v6 = cmd->vip;
66710946SSangeeta.Misra@Sun.COM rule->ir_servers = NULL;
66810946SSangeeta.Misra@Sun.COM
66910946SSangeeta.Misra@Sun.COM /*
67010946SSangeeta.Misra@Sun.COM * The default connection drain timeout is indefinite (value 0),
67110946SSangeeta.Misra@Sun.COM * meaning we will wait for all connections to finish. So we
67210946SSangeeta.Misra@Sun.COM * can assign cmd->conn_drain_timeout to it directly.
67310946SSangeeta.Misra@Sun.COM */
67410946SSangeeta.Misra@Sun.COM rule->ir_conn_drain_timeout = cmd->conn_drain_timeout;
67510946SSangeeta.Misra@Sun.COM if (cmd->nat_expiry != 0) {
67610946SSangeeta.Misra@Sun.COM rule->ir_nat_expiry = cmd->nat_expiry;
67710946SSangeeta.Misra@Sun.COM } else {
67810946SSangeeta.Misra@Sun.COM switch (rule->ir_proto) {
67910946SSangeeta.Misra@Sun.COM case IPPROTO_TCP:
68010946SSangeeta.Misra@Sun.COM rule->ir_nat_expiry = ilb_conn_tcp_expiry;
68110946SSangeeta.Misra@Sun.COM break;
68210946SSangeeta.Misra@Sun.COM case IPPROTO_UDP:
68310946SSangeeta.Misra@Sun.COM rule->ir_nat_expiry = ilb_conn_udp_expiry;
68410946SSangeeta.Misra@Sun.COM break;
68510946SSangeeta.Misra@Sun.COM default:
68610946SSangeeta.Misra@Sun.COM cmn_err(CE_PANIC, "data corruption: wrong ir_proto: %p",
68710946SSangeeta.Misra@Sun.COM (void *)rule);
68810946SSangeeta.Misra@Sun.COM break;
68910946SSangeeta.Misra@Sun.COM }
69010946SSangeeta.Misra@Sun.COM }
69110946SSangeeta.Misra@Sun.COM if (cmd->sticky_expiry != 0)
69210946SSangeeta.Misra@Sun.COM rule->ir_sticky_expiry = cmd->sticky_expiry;
69310946SSangeeta.Misra@Sun.COM else
69410946SSangeeta.Misra@Sun.COM rule->ir_sticky_expiry = ilb_sticky_expiry;
69510946SSangeeta.Misra@Sun.COM
69610946SSangeeta.Misra@Sun.COM if (cmd->flags & ILB_RULE_STICKY) {
69710946SSangeeta.Misra@Sun.COM rule->ir_flags |= ILB_RULE_STICKY;
69810946SSangeeta.Misra@Sun.COM rule->ir_sticky_mask = cmd->sticky_mask;
69910946SSangeeta.Misra@Sun.COM if (ilbs->ilbs_sticky_hash == NULL)
70010946SSangeeta.Misra@Sun.COM ilb_sticky_hash_init(ilbs);
70110946SSangeeta.Misra@Sun.COM }
70210946SSangeeta.Misra@Sun.COM if (cmd->flags & ILB_RULE_ENABLED)
70310946SSangeeta.Misra@Sun.COM rule->ir_flags |= ILB_RULE_ENABLED;
70410946SSangeeta.Misra@Sun.COM
70510946SSangeeta.Misra@Sun.COM mutex_init(&rule->ir_lock, NULL, MUTEX_DEFAULT, NULL);
70610946SSangeeta.Misra@Sun.COM cv_init(&rule->ir_cv, NULL, CV_DEFAULT, NULL);
70710946SSangeeta.Misra@Sun.COM
70810946SSangeeta.Misra@Sun.COM rule->ir_refcnt = 1;
70910946SSangeeta.Misra@Sun.COM
71010946SSangeeta.Misra@Sun.COM switch (cmd->algo) {
71110946SSangeeta.Misra@Sun.COM case ILB_ALG_IMPL_ROUNDROBIN:
71210946SSangeeta.Misra@Sun.COM if ((rule->ir_alg = ilb_alg_rr_init(rule, NULL)) == NULL) {
71310946SSangeeta.Misra@Sun.COM ret = ENOMEM;
71410946SSangeeta.Misra@Sun.COM goto error;
71510946SSangeeta.Misra@Sun.COM }
71610946SSangeeta.Misra@Sun.COM rule->ir_alg_type = ILB_ALG_IMPL_ROUNDROBIN;
71710946SSangeeta.Misra@Sun.COM break;
71810946SSangeeta.Misra@Sun.COM case ILB_ALG_IMPL_HASH_IP:
71910946SSangeeta.Misra@Sun.COM case ILB_ALG_IMPL_HASH_IP_SPORT:
72010946SSangeeta.Misra@Sun.COM case ILB_ALG_IMPL_HASH_IP_VIP:
72110946SSangeeta.Misra@Sun.COM if ((rule->ir_alg = ilb_alg_hash_init(rule,
72210946SSangeeta.Misra@Sun.COM &cmd->algo)) == NULL) {
72310946SSangeeta.Misra@Sun.COM ret = ENOMEM;
72410946SSangeeta.Misra@Sun.COM goto error;
72510946SSangeeta.Misra@Sun.COM }
72610946SSangeeta.Misra@Sun.COM rule->ir_alg_type = cmd->algo;
72710946SSangeeta.Misra@Sun.COM break;
72810946SSangeeta.Misra@Sun.COM default:
72910946SSangeeta.Misra@Sun.COM ret = EINVAL;
73010946SSangeeta.Misra@Sun.COM goto error;
73110946SSangeeta.Misra@Sun.COM }
73210946SSangeeta.Misra@Sun.COM
73310946SSangeeta.Misra@Sun.COM /* Add it to the global list and hash array at the end. */
73410946SSangeeta.Misra@Sun.COM ilb_rule_g_add(ilbs, rule);
73510946SSangeeta.Misra@Sun.COM ilb_rule_hash_add(ilbs, rule, &cmd->vip);
73610946SSangeeta.Misra@Sun.COM
73710946SSangeeta.Misra@Sun.COM mutex_exit(&ilbs->ilbs_g_lock);
73810946SSangeeta.Misra@Sun.COM
73910946SSangeeta.Misra@Sun.COM return (0);
74010946SSangeeta.Misra@Sun.COM
74110946SSangeeta.Misra@Sun.COM error:
74210946SSangeeta.Misra@Sun.COM mutex_exit(&ilbs->ilbs_g_lock);
74310946SSangeeta.Misra@Sun.COM if (rule->ir_ksp != NULL) {
74410946SSangeeta.Misra@Sun.COM /* stackid must be initialized if ir_ksp != NULL */
74510946SSangeeta.Misra@Sun.COM kstat_delete_netstack(rule->ir_ksp, stackid);
74610946SSangeeta.Misra@Sun.COM }
74710946SSangeeta.Misra@Sun.COM kmem_free(rule, sizeof (ilb_rule_t));
74810946SSangeeta.Misra@Sun.COM return (ret);
74910946SSangeeta.Misra@Sun.COM }
75010946SSangeeta.Misra@Sun.COM
75110946SSangeeta.Misra@Sun.COM /*
75210946SSangeeta.Misra@Sun.COM * The final part in deleting a rule. Either called directly or by the
75310946SSangeeta.Misra@Sun.COM * taskq dispatched.
75410946SSangeeta.Misra@Sun.COM */
75510946SSangeeta.Misra@Sun.COM static void
ilb_rule_del_common(ilb_stack_t * ilbs,ilb_rule_t * tmp_rule)75610946SSangeeta.Misra@Sun.COM ilb_rule_del_common(ilb_stack_t *ilbs, ilb_rule_t *tmp_rule)
75710946SSangeeta.Misra@Sun.COM {
75810946SSangeeta.Misra@Sun.COM netstackid_t stackid;
75910946SSangeeta.Misra@Sun.COM ilb_server_t *server;
76010946SSangeeta.Misra@Sun.COM
76110946SSangeeta.Misra@Sun.COM stackid = (netstackid_t)(uintptr_t)ilbs->ilbs_ksp->ks_private;
76210946SSangeeta.Misra@Sun.COM
76310946SSangeeta.Misra@Sun.COM /*
76410946SSangeeta.Misra@Sun.COM * Let the algorithm know that the rule is going away. The
76510946SSangeeta.Misra@Sun.COM * algorithm fini routine will free all its resources with this
76610946SSangeeta.Misra@Sun.COM * rule.
76710946SSangeeta.Misra@Sun.COM */
76810946SSangeeta.Misra@Sun.COM tmp_rule->ir_alg->ilb_alg_fini(&tmp_rule->ir_alg);
76910946SSangeeta.Misra@Sun.COM
77010946SSangeeta.Misra@Sun.COM while ((server = tmp_rule->ir_servers) != NULL) {
77110946SSangeeta.Misra@Sun.COM mutex_enter(&server->iser_lock);
77210946SSangeeta.Misra@Sun.COM ilb_destroy_nat_src(&server->iser_nat_src);
77310946SSangeeta.Misra@Sun.COM if (tmp_rule->ir_conn_drain_timeout != 0) {
77410946SSangeeta.Misra@Sun.COM /*
77510946SSangeeta.Misra@Sun.COM * The garbage collection thread checks this value
77610946SSangeeta.Misra@Sun.COM * without grabing a lock. So we need to use
77710946SSangeeta.Misra@Sun.COM * atomic_swap_64() to make sure that the value seen
77810946SSangeeta.Misra@Sun.COM * by gc thread is intact.
77910946SSangeeta.Misra@Sun.COM */
78010946SSangeeta.Misra@Sun.COM (void) atomic_swap_64(
781*11066Srafael.vanoni@sun.com (uint64_t *)&server->iser_die_time,
782*11066Srafael.vanoni@sun.com ddi_get_lbolt64() +
78310946SSangeeta.Misra@Sun.COM SEC_TO_TICK(tmp_rule->ir_conn_drain_timeout));
78410946SSangeeta.Misra@Sun.COM }
78510946SSangeeta.Misra@Sun.COM while (server->iser_refcnt > 1)
78610946SSangeeta.Misra@Sun.COM cv_wait(&server->iser_cv, &server->iser_lock);
78710946SSangeeta.Misra@Sun.COM tmp_rule->ir_servers = server->iser_next;
78810946SSangeeta.Misra@Sun.COM kstat_delete_netstack(server->iser_ksp, stackid);
78910946SSangeeta.Misra@Sun.COM kmem_free(server, sizeof (ilb_server_t));
79010946SSangeeta.Misra@Sun.COM }
79110946SSangeeta.Misra@Sun.COM
79210946SSangeeta.Misra@Sun.COM ASSERT(tmp_rule->ir_ksp != NULL);
79310946SSangeeta.Misra@Sun.COM kstat_delete_netstack(tmp_rule->ir_ksp, stackid);
79410946SSangeeta.Misra@Sun.COM
79510946SSangeeta.Misra@Sun.COM kmem_free(tmp_rule, sizeof (ilb_rule_t));
79610946SSangeeta.Misra@Sun.COM }
79710946SSangeeta.Misra@Sun.COM
79810946SSangeeta.Misra@Sun.COM /* The routine executed by the delayed rule taskq. */
79910946SSangeeta.Misra@Sun.COM static void
ilb_rule_del_tq(void * arg)80010946SSangeeta.Misra@Sun.COM ilb_rule_del_tq(void *arg)
80110946SSangeeta.Misra@Sun.COM {
80210946SSangeeta.Misra@Sun.COM ilb_stack_t *ilbs = ((ilb_rule_tq_t *)arg)->ilbs;
80310946SSangeeta.Misra@Sun.COM ilb_rule_t *rule = ((ilb_rule_tq_t *)arg)->rule;
80410946SSangeeta.Misra@Sun.COM
80510946SSangeeta.Misra@Sun.COM mutex_enter(&rule->ir_lock);
80610946SSangeeta.Misra@Sun.COM while (rule->ir_refcnt > 1)
80710946SSangeeta.Misra@Sun.COM cv_wait(&rule->ir_cv, &rule->ir_lock);
80810946SSangeeta.Misra@Sun.COM ilb_rule_del_common(ilbs, rule);
80910946SSangeeta.Misra@Sun.COM kmem_free(arg, sizeof (ilb_rule_tq_t));
81010946SSangeeta.Misra@Sun.COM }
81110946SSangeeta.Misra@Sun.COM
81210946SSangeeta.Misra@Sun.COM /* Routine to delete a rule. */
81310946SSangeeta.Misra@Sun.COM int
ilb_rule_del(ilb_stack_t * ilbs,zoneid_t zoneid,const char * name)81410946SSangeeta.Misra@Sun.COM ilb_rule_del(ilb_stack_t *ilbs, zoneid_t zoneid, const char *name)
81510946SSangeeta.Misra@Sun.COM {
81610946SSangeeta.Misra@Sun.COM ilb_rule_t *tmp_rule;
81710946SSangeeta.Misra@Sun.COM ilb_rule_tq_t *arg;
81810946SSangeeta.Misra@Sun.COM int err;
81910946SSangeeta.Misra@Sun.COM
82010946SSangeeta.Misra@Sun.COM mutex_enter(&ilbs->ilbs_g_lock);
82110946SSangeeta.Misra@Sun.COM if ((tmp_rule = ilb_find_rule_locked(ilbs, zoneid, name,
82210946SSangeeta.Misra@Sun.COM &err)) == NULL) {
82310946SSangeeta.Misra@Sun.COM mutex_exit(&ilbs->ilbs_g_lock);
82410946SSangeeta.Misra@Sun.COM return (err);
82510946SSangeeta.Misra@Sun.COM }
82610946SSangeeta.Misra@Sun.COM
82710946SSangeeta.Misra@Sun.COM /*
82810946SSangeeta.Misra@Sun.COM * First remove the rule from the hash array and the global list so
82910946SSangeeta.Misra@Sun.COM * that no one can find this rule any more.
83010946SSangeeta.Misra@Sun.COM */
83110946SSangeeta.Misra@Sun.COM ilb_rule_hash_del(tmp_rule);
83210946SSangeeta.Misra@Sun.COM ilb_rule_g_del(ilbs, tmp_rule);
83310946SSangeeta.Misra@Sun.COM mutex_exit(&ilbs->ilbs_g_lock);
83410946SSangeeta.Misra@Sun.COM ILB_RULE_REFRELE(tmp_rule);
83510946SSangeeta.Misra@Sun.COM
83610946SSangeeta.Misra@Sun.COM /*
83710946SSangeeta.Misra@Sun.COM * Now no one can find this rule, we can remove it once all
83810946SSangeeta.Misra@Sun.COM * references to it are dropped and all references to the list
83910946SSangeeta.Misra@Sun.COM * of servers are dropped. So dispatch a task to finish the deletion.
84010946SSangeeta.Misra@Sun.COM * We do this instead of letting the last one referencing the
84110946SSangeeta.Misra@Sun.COM * rule do it. The reason is that the last one may be the
84210946SSangeeta.Misra@Sun.COM * interrupt thread. We want to minimize the work it needs to
84310946SSangeeta.Misra@Sun.COM * do. Rule deletion is not a critical task so it can be delayed.
84410946SSangeeta.Misra@Sun.COM */
84510946SSangeeta.Misra@Sun.COM arg = kmem_alloc(sizeof (ilb_rule_tq_t), KM_SLEEP);
84610946SSangeeta.Misra@Sun.COM arg->ilbs = ilbs;
84710946SSangeeta.Misra@Sun.COM arg->rule = tmp_rule;
84810946SSangeeta.Misra@Sun.COM (void) taskq_dispatch(ilbs->ilbs_rule_taskq, ilb_rule_del_tq, arg,
84910946SSangeeta.Misra@Sun.COM TQ_SLEEP);
85010946SSangeeta.Misra@Sun.COM
85110946SSangeeta.Misra@Sun.COM return (0);
85210946SSangeeta.Misra@Sun.COM }
85310946SSangeeta.Misra@Sun.COM
85410946SSangeeta.Misra@Sun.COM /*
85510946SSangeeta.Misra@Sun.COM * Given an IP address, check to see if there is a rule using this
85610946SSangeeta.Misra@Sun.COM * as the VIP. It can be used to check if we need to drop a fragment.
85710946SSangeeta.Misra@Sun.COM */
85810946SSangeeta.Misra@Sun.COM boolean_t
ilb_rule_match_vip_v6(ilb_stack_t * ilbs,in6_addr_t * vip,ilb_rule_t ** ret_rule)85910946SSangeeta.Misra@Sun.COM ilb_rule_match_vip_v6(ilb_stack_t *ilbs, in6_addr_t *vip, ilb_rule_t **ret_rule)
86010946SSangeeta.Misra@Sun.COM {
86110946SSangeeta.Misra@Sun.COM int i;
86210946SSangeeta.Misra@Sun.COM ilb_rule_t *rule;
86310946SSangeeta.Misra@Sun.COM boolean_t ret = B_FALSE;
86410946SSangeeta.Misra@Sun.COM
86510946SSangeeta.Misra@Sun.COM i = ILB_RULE_HASH((uint8_t *)&vip->s6_addr32[3],
86610946SSangeeta.Misra@Sun.COM ilbs->ilbs_rule_hash_size);
86710946SSangeeta.Misra@Sun.COM mutex_enter(&ilbs->ilbs_g_hash[i].ilb_hash_lock);
86810946SSangeeta.Misra@Sun.COM for (rule = ilbs->ilbs_g_hash[i].ilb_hash_rule; rule != NULL;
86910946SSangeeta.Misra@Sun.COM rule = rule->ir_hash_next) {
87010946SSangeeta.Misra@Sun.COM if (IN6_ARE_ADDR_EQUAL(vip, &rule->ir_target_v6)) {
87110946SSangeeta.Misra@Sun.COM mutex_enter(&rule->ir_lock);
87210946SSangeeta.Misra@Sun.COM if (rule->ir_flags & ILB_RULE_BUSY) {
87310946SSangeeta.Misra@Sun.COM mutex_exit(&rule->ir_lock);
87410946SSangeeta.Misra@Sun.COM break;
87510946SSangeeta.Misra@Sun.COM }
87610946SSangeeta.Misra@Sun.COM if (ret_rule != NULL) {
87710946SSangeeta.Misra@Sun.COM rule->ir_refcnt++;
87810946SSangeeta.Misra@Sun.COM mutex_exit(&rule->ir_lock);
87910946SSangeeta.Misra@Sun.COM *ret_rule = rule;
88010946SSangeeta.Misra@Sun.COM } else {
88110946SSangeeta.Misra@Sun.COM mutex_exit(&rule->ir_lock);
88210946SSangeeta.Misra@Sun.COM }
88310946SSangeeta.Misra@Sun.COM ret = B_TRUE;
88410946SSangeeta.Misra@Sun.COM break;
88510946SSangeeta.Misra@Sun.COM }
88610946SSangeeta.Misra@Sun.COM }
88710946SSangeeta.Misra@Sun.COM mutex_exit(&ilbs->ilbs_g_hash[i].ilb_hash_lock);
88810946SSangeeta.Misra@Sun.COM return (ret);
88910946SSangeeta.Misra@Sun.COM }
89010946SSangeeta.Misra@Sun.COM
89110946SSangeeta.Misra@Sun.COM boolean_t
ilb_rule_match_vip_v4(ilb_stack_t * ilbs,ipaddr_t addr,ilb_rule_t ** ret_rule)89210946SSangeeta.Misra@Sun.COM ilb_rule_match_vip_v4(ilb_stack_t *ilbs, ipaddr_t addr, ilb_rule_t **ret_rule)
89310946SSangeeta.Misra@Sun.COM {
89410946SSangeeta.Misra@Sun.COM int i;
89510946SSangeeta.Misra@Sun.COM ilb_rule_t *rule;
89610946SSangeeta.Misra@Sun.COM boolean_t ret = B_FALSE;
89710946SSangeeta.Misra@Sun.COM
89810946SSangeeta.Misra@Sun.COM i = ILB_RULE_HASH((uint8_t *)&addr, ilbs->ilbs_rule_hash_size);
89910946SSangeeta.Misra@Sun.COM mutex_enter(&ilbs->ilbs_g_hash[i].ilb_hash_lock);
90010946SSangeeta.Misra@Sun.COM for (rule = ilbs->ilbs_g_hash[i].ilb_hash_rule; rule != NULL;
90110946SSangeeta.Misra@Sun.COM rule = rule->ir_hash_next) {
90210946SSangeeta.Misra@Sun.COM if (rule->ir_target_v6.s6_addr32[3] == addr) {
90310946SSangeeta.Misra@Sun.COM mutex_enter(&rule->ir_lock);
90410946SSangeeta.Misra@Sun.COM if (rule->ir_flags & ILB_RULE_BUSY) {
90510946SSangeeta.Misra@Sun.COM mutex_exit(&rule->ir_lock);
90610946SSangeeta.Misra@Sun.COM break;
90710946SSangeeta.Misra@Sun.COM }
90810946SSangeeta.Misra@Sun.COM if (ret_rule != NULL) {
90910946SSangeeta.Misra@Sun.COM rule->ir_refcnt++;
91010946SSangeeta.Misra@Sun.COM mutex_exit(&rule->ir_lock);
91110946SSangeeta.Misra@Sun.COM *ret_rule = rule;
91210946SSangeeta.Misra@Sun.COM } else {
91310946SSangeeta.Misra@Sun.COM mutex_exit(&rule->ir_lock);
91410946SSangeeta.Misra@Sun.COM }
91510946SSangeeta.Misra@Sun.COM ret = B_TRUE;
91610946SSangeeta.Misra@Sun.COM break;
91710946SSangeeta.Misra@Sun.COM }
91810946SSangeeta.Misra@Sun.COM }
91910946SSangeeta.Misra@Sun.COM mutex_exit(&ilbs->ilbs_g_hash[i].ilb_hash_lock);
92010946SSangeeta.Misra@Sun.COM return (ret);
92110946SSangeeta.Misra@Sun.COM }
92210946SSangeeta.Misra@Sun.COM
92310946SSangeeta.Misra@Sun.COM static ilb_rule_t *
ilb_find_rule_locked(ilb_stack_t * ilbs,zoneid_t zoneid,const char * name,int * err)92410946SSangeeta.Misra@Sun.COM ilb_find_rule_locked(ilb_stack_t *ilbs, zoneid_t zoneid, const char *name,
92510946SSangeeta.Misra@Sun.COM int *err)
92610946SSangeeta.Misra@Sun.COM {
92710946SSangeeta.Misra@Sun.COM ilb_rule_t *tmp_rule;
92810946SSangeeta.Misra@Sun.COM
92910946SSangeeta.Misra@Sun.COM ASSERT(mutex_owned(&ilbs->ilbs_g_lock));
93010946SSangeeta.Misra@Sun.COM
93110946SSangeeta.Misra@Sun.COM for (tmp_rule = ilbs->ilbs_rule_head; tmp_rule != NULL;
93210946SSangeeta.Misra@Sun.COM tmp_rule = tmp_rule->ir_next) {
93310946SSangeeta.Misra@Sun.COM if (tmp_rule->ir_zoneid != zoneid)
93410946SSangeeta.Misra@Sun.COM continue;
93510946SSangeeta.Misra@Sun.COM if (strcasecmp(tmp_rule->ir_name, name) == 0) {
93610946SSangeeta.Misra@Sun.COM mutex_enter(&tmp_rule->ir_lock);
93710946SSangeeta.Misra@Sun.COM if (tmp_rule->ir_flags & ILB_RULE_BUSY) {
93810946SSangeeta.Misra@Sun.COM mutex_exit(&tmp_rule->ir_lock);
93910946SSangeeta.Misra@Sun.COM *err = EINPROGRESS;
94010946SSangeeta.Misra@Sun.COM return (NULL);
94110946SSangeeta.Misra@Sun.COM }
94210946SSangeeta.Misra@Sun.COM tmp_rule->ir_refcnt++;
94310946SSangeeta.Misra@Sun.COM mutex_exit(&tmp_rule->ir_lock);
94410946SSangeeta.Misra@Sun.COM *err = 0;
94510946SSangeeta.Misra@Sun.COM return (tmp_rule);
94610946SSangeeta.Misra@Sun.COM }
94710946SSangeeta.Misra@Sun.COM }
94810946SSangeeta.Misra@Sun.COM *err = ENOENT;
94910946SSangeeta.Misra@Sun.COM return (NULL);
95010946SSangeeta.Misra@Sun.COM }
95110946SSangeeta.Misra@Sun.COM
95210946SSangeeta.Misra@Sun.COM /* To find a rule with a given name and zone in the global rule list. */
95310946SSangeeta.Misra@Sun.COM ilb_rule_t *
ilb_find_rule(ilb_stack_t * ilbs,zoneid_t zoneid,const char * name,int * err)95410946SSangeeta.Misra@Sun.COM ilb_find_rule(ilb_stack_t *ilbs, zoneid_t zoneid, const char *name,
95510946SSangeeta.Misra@Sun.COM int *err)
95610946SSangeeta.Misra@Sun.COM {
95710946SSangeeta.Misra@Sun.COM ilb_rule_t *tmp_rule;
95810946SSangeeta.Misra@Sun.COM
95910946SSangeeta.Misra@Sun.COM mutex_enter(&ilbs->ilbs_g_lock);
96010946SSangeeta.Misra@Sun.COM tmp_rule = ilb_find_rule_locked(ilbs, zoneid, name, err);
96110946SSangeeta.Misra@Sun.COM mutex_exit(&ilbs->ilbs_g_lock);
96210946SSangeeta.Misra@Sun.COM return (tmp_rule);
96310946SSangeeta.Misra@Sun.COM }
96410946SSangeeta.Misra@Sun.COM
96510946SSangeeta.Misra@Sun.COM /* Try to match the given packet info and zone ID with a rule. */
96610946SSangeeta.Misra@Sun.COM static boolean_t
ilb_match_rule(ilb_stack_t * ilbs,zoneid_t zoneid,const char * name,int l3,int l4,in_port_t min_port,in_port_t max_port,const in6_addr_t * addr)96710946SSangeeta.Misra@Sun.COM ilb_match_rule(ilb_stack_t *ilbs, zoneid_t zoneid, const char *name, int l3,
96810946SSangeeta.Misra@Sun.COM int l4, in_port_t min_port, in_port_t max_port, const in6_addr_t *addr)
96910946SSangeeta.Misra@Sun.COM {
97010946SSangeeta.Misra@Sun.COM ilb_rule_t *tmp_rule;
97110946SSangeeta.Misra@Sun.COM
97210946SSangeeta.Misra@Sun.COM ASSERT(mutex_owned(&ilbs->ilbs_g_lock));
97310946SSangeeta.Misra@Sun.COM
97410946SSangeeta.Misra@Sun.COM for (tmp_rule = ilbs->ilbs_rule_head; tmp_rule != NULL;
97510946SSangeeta.Misra@Sun.COM tmp_rule = tmp_rule->ir_next) {
97610946SSangeeta.Misra@Sun.COM if (tmp_rule->ir_zoneid != zoneid)
97710946SSangeeta.Misra@Sun.COM continue;
97810946SSangeeta.Misra@Sun.COM
97910946SSangeeta.Misra@Sun.COM /*
98010946SSangeeta.Misra@Sun.COM * We don't allow the same name in different rules even if all
98110946SSangeeta.Misra@Sun.COM * the other rule components are different.
98210946SSangeeta.Misra@Sun.COM */
98310946SSangeeta.Misra@Sun.COM if (strcasecmp(tmp_rule->ir_name, name) == 0)
98410946SSangeeta.Misra@Sun.COM return (B_TRUE);
98510946SSangeeta.Misra@Sun.COM
98610946SSangeeta.Misra@Sun.COM if (tmp_rule->ir_ipver != l3 || tmp_rule->ir_proto != l4)
98710946SSangeeta.Misra@Sun.COM continue;
98810946SSangeeta.Misra@Sun.COM
98910946SSangeeta.Misra@Sun.COM /*
99010946SSangeeta.Misra@Sun.COM * ir_min_port and ir_max_port are the same if ir_port_range
99110946SSangeeta.Misra@Sun.COM * is false. In this case, if the ir_min|max_port (same) is
99210946SSangeeta.Misra@Sun.COM * outside of the given port range, it is OK. In other cases,
99310946SSangeeta.Misra@Sun.COM * check if min and max port are outside a rule's range.
99410946SSangeeta.Misra@Sun.COM */
99510946SSangeeta.Misra@Sun.COM if (tmp_rule->ir_max_port < min_port ||
99610946SSangeeta.Misra@Sun.COM tmp_rule->ir_min_port > max_port) {
99710946SSangeeta.Misra@Sun.COM continue;
99810946SSangeeta.Misra@Sun.COM }
99910946SSangeeta.Misra@Sun.COM
100010946SSangeeta.Misra@Sun.COM /*
100110946SSangeeta.Misra@Sun.COM * If l3 is IPv4, the addr passed in is assumed to be
100210946SSangeeta.Misra@Sun.COM * mapped address.
100310946SSangeeta.Misra@Sun.COM */
100410946SSangeeta.Misra@Sun.COM if (V6_OR_V4_INADDR_ANY(*addr) ||
100510946SSangeeta.Misra@Sun.COM V6_OR_V4_INADDR_ANY(tmp_rule->ir_target_v6) ||
100610946SSangeeta.Misra@Sun.COM IN6_ARE_ADDR_EQUAL(addr, &tmp_rule->ir_target_v6)) {
100710946SSangeeta.Misra@Sun.COM return (B_TRUE);
100810946SSangeeta.Misra@Sun.COM }
100910946SSangeeta.Misra@Sun.COM }
101010946SSangeeta.Misra@Sun.COM return (B_FALSE);
101110946SSangeeta.Misra@Sun.COM }
101210946SSangeeta.Misra@Sun.COM
101310946SSangeeta.Misra@Sun.COM int
ilb_rule_enable(ilb_stack_t * ilbs,zoneid_t zoneid,const char * rule_name,ilb_rule_t * in_rule)101410946SSangeeta.Misra@Sun.COM ilb_rule_enable(ilb_stack_t *ilbs, zoneid_t zoneid,
101510946SSangeeta.Misra@Sun.COM const char *rule_name, ilb_rule_t *in_rule)
101610946SSangeeta.Misra@Sun.COM {
101710946SSangeeta.Misra@Sun.COM ilb_rule_t *rule;
101810946SSangeeta.Misra@Sun.COM int err;
101910946SSangeeta.Misra@Sun.COM
102010946SSangeeta.Misra@Sun.COM ASSERT((in_rule == NULL && rule_name != NULL) ||
102110946SSangeeta.Misra@Sun.COM (in_rule != NULL && rule_name == NULL));
102210946SSangeeta.Misra@Sun.COM if ((rule = in_rule) == NULL) {
102310946SSangeeta.Misra@Sun.COM if ((rule = ilb_find_rule(ilbs, zoneid, rule_name,
102410946SSangeeta.Misra@Sun.COM &err)) == NULL) {
102510946SSangeeta.Misra@Sun.COM return (err);
102610946SSangeeta.Misra@Sun.COM }
102710946SSangeeta.Misra@Sun.COM }
102810946SSangeeta.Misra@Sun.COM mutex_enter(&rule->ir_lock);
102910946SSangeeta.Misra@Sun.COM rule->ir_flags |= ILB_RULE_ENABLED;
103010946SSangeeta.Misra@Sun.COM mutex_exit(&rule->ir_lock);
103110946SSangeeta.Misra@Sun.COM
103210946SSangeeta.Misra@Sun.COM /* Only refrele if the rule is passed in. */
103310946SSangeeta.Misra@Sun.COM if (in_rule == NULL)
103410946SSangeeta.Misra@Sun.COM ILB_RULE_REFRELE(rule);
103510946SSangeeta.Misra@Sun.COM return (0);
103610946SSangeeta.Misra@Sun.COM }
103710946SSangeeta.Misra@Sun.COM
103810946SSangeeta.Misra@Sun.COM int
ilb_rule_disable(ilb_stack_t * ilbs,zoneid_t zoneid,const char * rule_name,ilb_rule_t * in_rule)103910946SSangeeta.Misra@Sun.COM ilb_rule_disable(ilb_stack_t *ilbs, zoneid_t zoneid,
104010946SSangeeta.Misra@Sun.COM const char *rule_name, ilb_rule_t *in_rule)
104110946SSangeeta.Misra@Sun.COM {
104210946SSangeeta.Misra@Sun.COM ilb_rule_t *rule;
104310946SSangeeta.Misra@Sun.COM int err;
104410946SSangeeta.Misra@Sun.COM
104510946SSangeeta.Misra@Sun.COM ASSERT((in_rule == NULL && rule_name != NULL) ||
104610946SSangeeta.Misra@Sun.COM (in_rule != NULL && rule_name == NULL));
104710946SSangeeta.Misra@Sun.COM if ((rule = in_rule) == NULL) {
104810946SSangeeta.Misra@Sun.COM if ((rule = ilb_find_rule(ilbs, zoneid, rule_name,
104910946SSangeeta.Misra@Sun.COM &err)) == NULL) {
105010946SSangeeta.Misra@Sun.COM return (err);
105110946SSangeeta.Misra@Sun.COM }
105210946SSangeeta.Misra@Sun.COM }
105310946SSangeeta.Misra@Sun.COM mutex_enter(&rule->ir_lock);
105410946SSangeeta.Misra@Sun.COM rule->ir_flags &= ~ILB_RULE_ENABLED;
105510946SSangeeta.Misra@Sun.COM mutex_exit(&rule->ir_lock);
105610946SSangeeta.Misra@Sun.COM
105710946SSangeeta.Misra@Sun.COM /* Only refrele if the rule is passed in. */
105810946SSangeeta.Misra@Sun.COM if (in_rule == NULL)
105910946SSangeeta.Misra@Sun.COM ILB_RULE_REFRELE(rule);
106010946SSangeeta.Misra@Sun.COM return (0);
106110946SSangeeta.Misra@Sun.COM }
106210946SSangeeta.Misra@Sun.COM
106310946SSangeeta.Misra@Sun.COM /*
106410946SSangeeta.Misra@Sun.COM * XXX We should probably have a walker function to walk all rules. For
106510946SSangeeta.Misra@Sun.COM * now, just add a simple loop for enable/disable/del.
106610946SSangeeta.Misra@Sun.COM */
106710946SSangeeta.Misra@Sun.COM void
ilb_rule_enable_all(ilb_stack_t * ilbs,zoneid_t zoneid)106810946SSangeeta.Misra@Sun.COM ilb_rule_enable_all(ilb_stack_t *ilbs, zoneid_t zoneid)
106910946SSangeeta.Misra@Sun.COM {
107010946SSangeeta.Misra@Sun.COM ilb_rule_t *rule;
107110946SSangeeta.Misra@Sun.COM
107210946SSangeeta.Misra@Sun.COM mutex_enter(&ilbs->ilbs_g_lock);
107310946SSangeeta.Misra@Sun.COM for (rule = ilbs->ilbs_rule_head; rule != NULL; rule = rule->ir_next) {
107410946SSangeeta.Misra@Sun.COM if (rule->ir_zoneid != zoneid)
107510946SSangeeta.Misra@Sun.COM continue;
107610946SSangeeta.Misra@Sun.COM /*
107710946SSangeeta.Misra@Sun.COM * No need to hold the rule as we are holding the global
107810946SSangeeta.Misra@Sun.COM * lock so it won't go away. Ignore the return value here
107910946SSangeeta.Misra@Sun.COM * as the rule is provided so the call cannot fail.
108010946SSangeeta.Misra@Sun.COM */
108110946SSangeeta.Misra@Sun.COM (void) ilb_rule_enable(ilbs, zoneid, NULL, rule);
108210946SSangeeta.Misra@Sun.COM }
108310946SSangeeta.Misra@Sun.COM mutex_exit(&ilbs->ilbs_g_lock);
108410946SSangeeta.Misra@Sun.COM }
108510946SSangeeta.Misra@Sun.COM
108610946SSangeeta.Misra@Sun.COM void
ilb_rule_disable_all(ilb_stack_t * ilbs,zoneid_t zoneid)108710946SSangeeta.Misra@Sun.COM ilb_rule_disable_all(ilb_stack_t *ilbs, zoneid_t zoneid)
108810946SSangeeta.Misra@Sun.COM {
108910946SSangeeta.Misra@Sun.COM ilb_rule_t *rule;
109010946SSangeeta.Misra@Sun.COM
109110946SSangeeta.Misra@Sun.COM mutex_enter(&ilbs->ilbs_g_lock);
109210946SSangeeta.Misra@Sun.COM for (rule = ilbs->ilbs_rule_head; rule != NULL;
109310946SSangeeta.Misra@Sun.COM rule = rule->ir_next) {
109410946SSangeeta.Misra@Sun.COM if (rule->ir_zoneid != zoneid)
109510946SSangeeta.Misra@Sun.COM continue;
109610946SSangeeta.Misra@Sun.COM (void) ilb_rule_disable(ilbs, zoneid, NULL, rule);
109710946SSangeeta.Misra@Sun.COM }
109810946SSangeeta.Misra@Sun.COM mutex_exit(&ilbs->ilbs_g_lock);
109910946SSangeeta.Misra@Sun.COM }
110010946SSangeeta.Misra@Sun.COM
110110946SSangeeta.Misra@Sun.COM void
ilb_rule_del_all(ilb_stack_t * ilbs,zoneid_t zoneid)110210946SSangeeta.Misra@Sun.COM ilb_rule_del_all(ilb_stack_t *ilbs, zoneid_t zoneid)
110310946SSangeeta.Misra@Sun.COM {
110410946SSangeeta.Misra@Sun.COM ilb_rule_t *rule;
110510946SSangeeta.Misra@Sun.COM ilb_rule_tq_t *arg;
110610946SSangeeta.Misra@Sun.COM
110710946SSangeeta.Misra@Sun.COM mutex_enter(&ilbs->ilbs_g_lock);
110810946SSangeeta.Misra@Sun.COM while ((rule = ilbs->ilbs_rule_head) != NULL) {
110910946SSangeeta.Misra@Sun.COM if (rule->ir_zoneid != zoneid)
111010946SSangeeta.Misra@Sun.COM continue;
111110946SSangeeta.Misra@Sun.COM ilb_rule_hash_del(rule);
111210946SSangeeta.Misra@Sun.COM ilb_rule_g_del(ilbs, rule);
111310946SSangeeta.Misra@Sun.COM mutex_exit(&ilbs->ilbs_g_lock);
111410946SSangeeta.Misra@Sun.COM
111510946SSangeeta.Misra@Sun.COM arg = kmem_alloc(sizeof (ilb_rule_tq_t), KM_SLEEP);
111610946SSangeeta.Misra@Sun.COM arg->ilbs = ilbs;
111710946SSangeeta.Misra@Sun.COM arg->rule = rule;
111810946SSangeeta.Misra@Sun.COM (void) taskq_dispatch(ilbs->ilbs_rule_taskq, ilb_rule_del_tq,
111910946SSangeeta.Misra@Sun.COM arg, TQ_SLEEP);
112010946SSangeeta.Misra@Sun.COM
112110946SSangeeta.Misra@Sun.COM mutex_enter(&ilbs->ilbs_g_lock);
112210946SSangeeta.Misra@Sun.COM }
112310946SSangeeta.Misra@Sun.COM mutex_exit(&ilbs->ilbs_g_lock);
112410946SSangeeta.Misra@Sun.COM }
112510946SSangeeta.Misra@Sun.COM
112610946SSangeeta.Misra@Sun.COM /*
112710946SSangeeta.Misra@Sun.COM * This is just an optimization, so don't grab the global lock. The
112810946SSangeeta.Misra@Sun.COM * worst case is that we missed a couple packets.
112910946SSangeeta.Misra@Sun.COM */
113010946SSangeeta.Misra@Sun.COM boolean_t
ilb_has_rules(ilb_stack_t * ilbs)113110946SSangeeta.Misra@Sun.COM ilb_has_rules(ilb_stack_t *ilbs)
113210946SSangeeta.Misra@Sun.COM {
113310946SSangeeta.Misra@Sun.COM return (ilbs->ilbs_rule_head != NULL);
113410946SSangeeta.Misra@Sun.COM }
113510946SSangeeta.Misra@Sun.COM
113610946SSangeeta.Misra@Sun.COM
113710946SSangeeta.Misra@Sun.COM static int
ilb_server_toggle(ilb_stack_t * ilbs,zoneid_t zoneid,const char * rule_name,ilb_rule_t * rule,in6_addr_t * addr,boolean_t enable)113810946SSangeeta.Misra@Sun.COM ilb_server_toggle(ilb_stack_t *ilbs, zoneid_t zoneid, const char *rule_name,
113910946SSangeeta.Misra@Sun.COM ilb_rule_t *rule, in6_addr_t *addr, boolean_t enable)
114010946SSangeeta.Misra@Sun.COM {
114110946SSangeeta.Misra@Sun.COM ilb_server_t *tmp_server;
114210946SSangeeta.Misra@Sun.COM int ret;
114310946SSangeeta.Misra@Sun.COM
114410946SSangeeta.Misra@Sun.COM ASSERT((rule == NULL && rule_name != NULL) ||
114510946SSangeeta.Misra@Sun.COM (rule != NULL && rule_name == NULL));
114610946SSangeeta.Misra@Sun.COM
114710946SSangeeta.Misra@Sun.COM if (rule == NULL) {
114810946SSangeeta.Misra@Sun.COM if ((rule = ilb_find_rule(ilbs, zoneid, rule_name,
114910946SSangeeta.Misra@Sun.COM &ret)) == NULL) {
115010946SSangeeta.Misra@Sun.COM return (ret);
115110946SSangeeta.Misra@Sun.COM }
115210946SSangeeta.Misra@Sun.COM }
115310946SSangeeta.Misra@Sun.COM
115410946SSangeeta.Misra@Sun.COM /* Once we get a hold on the rule, no server can be added/deleted. */
115510946SSangeeta.Misra@Sun.COM for (tmp_server = rule->ir_servers; tmp_server != NULL;
115610946SSangeeta.Misra@Sun.COM tmp_server = tmp_server->iser_next) {
115710946SSangeeta.Misra@Sun.COM if (IN6_ARE_ADDR_EQUAL(&tmp_server->iser_addr_v6, addr))
115810946SSangeeta.Misra@Sun.COM break;
115910946SSangeeta.Misra@Sun.COM }
116010946SSangeeta.Misra@Sun.COM if (tmp_server == NULL) {
116110946SSangeeta.Misra@Sun.COM ret = ENOENT;
116210946SSangeeta.Misra@Sun.COM goto done;
116310946SSangeeta.Misra@Sun.COM }
116410946SSangeeta.Misra@Sun.COM
116510946SSangeeta.Misra@Sun.COM if (enable) {
116610946SSangeeta.Misra@Sun.COM ret = rule->ir_alg->ilb_alg_server_enable(tmp_server,
116710946SSangeeta.Misra@Sun.COM rule->ir_alg->ilb_alg_data);
116810946SSangeeta.Misra@Sun.COM if (ret == 0) {
116910946SSangeeta.Misra@Sun.COM tmp_server->iser_enabled = B_TRUE;
117010946SSangeeta.Misra@Sun.COM tmp_server->iser_die_time = 0;
117110946SSangeeta.Misra@Sun.COM }
117210946SSangeeta.Misra@Sun.COM } else {
117310946SSangeeta.Misra@Sun.COM ret = rule->ir_alg->ilb_alg_server_disable(tmp_server,
117410946SSangeeta.Misra@Sun.COM rule->ir_alg->ilb_alg_data);
117510946SSangeeta.Misra@Sun.COM if (ret == 0) {
117610946SSangeeta.Misra@Sun.COM tmp_server->iser_enabled = B_FALSE;
117710946SSangeeta.Misra@Sun.COM if (rule->ir_conn_drain_timeout != 0) {
117810946SSangeeta.Misra@Sun.COM (void) atomic_swap_64(
117910946SSangeeta.Misra@Sun.COM (uint64_t *)&tmp_server->iser_die_time,
1180*11066Srafael.vanoni@sun.com ddi_get_lbolt64() + SEC_TO_TICK(
118110946SSangeeta.Misra@Sun.COM rule->ir_conn_drain_timeout));
118210946SSangeeta.Misra@Sun.COM }
118310946SSangeeta.Misra@Sun.COM }
118410946SSangeeta.Misra@Sun.COM }
118510946SSangeeta.Misra@Sun.COM
118610946SSangeeta.Misra@Sun.COM done:
118710946SSangeeta.Misra@Sun.COM if (rule_name != NULL)
118810946SSangeeta.Misra@Sun.COM ILB_RULE_REFRELE(rule);
118910946SSangeeta.Misra@Sun.COM return (ret);
119010946SSangeeta.Misra@Sun.COM }
119110946SSangeeta.Misra@Sun.COM int
ilb_server_enable(ilb_stack_t * ilbs,zoneid_t zoneid,const char * name,ilb_rule_t * rule,in6_addr_t * addr)119210946SSangeeta.Misra@Sun.COM ilb_server_enable(ilb_stack_t *ilbs, zoneid_t zoneid, const char *name,
119310946SSangeeta.Misra@Sun.COM ilb_rule_t *rule, in6_addr_t *addr)
119410946SSangeeta.Misra@Sun.COM {
119510946SSangeeta.Misra@Sun.COM return (ilb_server_toggle(ilbs, zoneid, name, rule, addr, B_TRUE));
119610946SSangeeta.Misra@Sun.COM }
119710946SSangeeta.Misra@Sun.COM
119810946SSangeeta.Misra@Sun.COM int
ilb_server_disable(ilb_stack_t * ilbs,zoneid_t zoneid,const char * name,ilb_rule_t * rule,in6_addr_t * addr)119910946SSangeeta.Misra@Sun.COM ilb_server_disable(ilb_stack_t *ilbs, zoneid_t zoneid, const char *name,
120010946SSangeeta.Misra@Sun.COM ilb_rule_t *rule, in6_addr_t *addr)
120110946SSangeeta.Misra@Sun.COM {
120210946SSangeeta.Misra@Sun.COM return (ilb_server_toggle(ilbs, zoneid, name, rule, addr, B_FALSE));
120310946SSangeeta.Misra@Sun.COM }
120410946SSangeeta.Misra@Sun.COM
120510946SSangeeta.Misra@Sun.COM /*
120610946SSangeeta.Misra@Sun.COM * Add a back end server to a rule. If the address is IPv4, it is assumed
120710946SSangeeta.Misra@Sun.COM * to be passed in as a mapped address.
120810946SSangeeta.Misra@Sun.COM */
120910946SSangeeta.Misra@Sun.COM int
ilb_server_add(ilb_stack_t * ilbs,ilb_rule_t * rule,ilb_server_info_t * info)121010946SSangeeta.Misra@Sun.COM ilb_server_add(ilb_stack_t *ilbs, ilb_rule_t *rule, ilb_server_info_t *info)
121110946SSangeeta.Misra@Sun.COM {
121210946SSangeeta.Misra@Sun.COM ilb_server_t *server;
121310946SSangeeta.Misra@Sun.COM netstackid_t stackid;
121410946SSangeeta.Misra@Sun.COM int ret = 0;
121510946SSangeeta.Misra@Sun.COM in_port_t min_port, max_port;
121610946SSangeeta.Misra@Sun.COM in_port_t range;
121710946SSangeeta.Misra@Sun.COM
121810946SSangeeta.Misra@Sun.COM /* Port is passed in network byte order. */
121910946SSangeeta.Misra@Sun.COM min_port = ntohs(info->min_port);
122010946SSangeeta.Misra@Sun.COM max_port = ntohs(info->max_port);
122110946SSangeeta.Misra@Sun.COM if (min_port > max_port)
122210946SSangeeta.Misra@Sun.COM return (EINVAL);
122310946SSangeeta.Misra@Sun.COM
122410946SSangeeta.Misra@Sun.COM /* min_port == 0 means "all ports". Make it so */
122510946SSangeeta.Misra@Sun.COM if (min_port == 0) {
122610946SSangeeta.Misra@Sun.COM min_port = 1;
122710946SSangeeta.Misra@Sun.COM max_port = 65535;
122810946SSangeeta.Misra@Sun.COM }
122910946SSangeeta.Misra@Sun.COM range = max_port - min_port;
123010946SSangeeta.Misra@Sun.COM
123110946SSangeeta.Misra@Sun.COM mutex_enter(&rule->ir_lock);
123210946SSangeeta.Misra@Sun.COM /* If someone is already doing server add/del, sleeps and wait. */
123310946SSangeeta.Misra@Sun.COM while (rule->ir_flags & ILB_RULE_BUSY) {
123410946SSangeeta.Misra@Sun.COM if (cv_wait_sig(&rule->ir_cv, &rule->ir_lock) == 0) {
123510946SSangeeta.Misra@Sun.COM mutex_exit(&rule->ir_lock);
123610946SSangeeta.Misra@Sun.COM return (EINTR);
123710946SSangeeta.Misra@Sun.COM }
123810946SSangeeta.Misra@Sun.COM }
123910946SSangeeta.Misra@Sun.COM
124010946SSangeeta.Misra@Sun.COM /*
124110946SSangeeta.Misra@Sun.COM * Set the rule to be busy to make sure that no new packet can
124210946SSangeeta.Misra@Sun.COM * use this rule.
124310946SSangeeta.Misra@Sun.COM */
124410946SSangeeta.Misra@Sun.COM rule->ir_flags |= ILB_RULE_BUSY;
124510946SSangeeta.Misra@Sun.COM
124610946SSangeeta.Misra@Sun.COM /* Now wait for all other guys to finish their work. */
124710946SSangeeta.Misra@Sun.COM while (rule->ir_refcnt > 2) {
124810946SSangeeta.Misra@Sun.COM if (cv_wait_sig(&rule->ir_cv, &rule->ir_lock) == 0) {
124910946SSangeeta.Misra@Sun.COM mutex_exit(&rule->ir_lock);
125010946SSangeeta.Misra@Sun.COM ret = EINTR;
125110946SSangeeta.Misra@Sun.COM goto end;
125210946SSangeeta.Misra@Sun.COM }
125310946SSangeeta.Misra@Sun.COM }
125410946SSangeeta.Misra@Sun.COM mutex_exit(&rule->ir_lock);
125510946SSangeeta.Misra@Sun.COM
125610946SSangeeta.Misra@Sun.COM /* Sanity checks... */
125710946SSangeeta.Misra@Sun.COM if ((IN6_IS_ADDR_V4MAPPED(&info->addr) &&
125810946SSangeeta.Misra@Sun.COM rule->ir_ipver != IPPROTO_IP) ||
125910946SSangeeta.Misra@Sun.COM (!IN6_IS_ADDR_V4MAPPED(&info->addr) &&
126010946SSangeeta.Misra@Sun.COM rule->ir_ipver != IPPROTO_IPV6)) {
126110946SSangeeta.Misra@Sun.COM ret = EINVAL;
126210946SSangeeta.Misra@Sun.COM goto end;
126310946SSangeeta.Misra@Sun.COM }
126410946SSangeeta.Misra@Sun.COM
126510946SSangeeta.Misra@Sun.COM /*
126610946SSangeeta.Misra@Sun.COM * Check for valid port range.
126710946SSangeeta.Misra@Sun.COM *
126810946SSangeeta.Misra@Sun.COM * For DSR, there can be no port shifting. Hence the server
126910946SSangeeta.Misra@Sun.COM * specification must be the same as the rule's.
127010946SSangeeta.Misra@Sun.COM *
127110946SSangeeta.Misra@Sun.COM * For half-NAT/NAT, the range must either be 0 (port collapsing) or
127210946SSangeeta.Misra@Sun.COM * it must be equal to the same value as the rule port range.
127310946SSangeeta.Misra@Sun.COM *
127410946SSangeeta.Misra@Sun.COM */
127510946SSangeeta.Misra@Sun.COM if (rule->ir_topo == ILB_TOPO_IMPL_DSR) {
127610946SSangeeta.Misra@Sun.COM if (rule->ir_max_port != max_port ||
127710946SSangeeta.Misra@Sun.COM rule->ir_min_port != min_port) {
127810946SSangeeta.Misra@Sun.COM ret = EINVAL;
127910946SSangeeta.Misra@Sun.COM goto end;
128010946SSangeeta.Misra@Sun.COM }
128110946SSangeeta.Misra@Sun.COM } else {
128210946SSangeeta.Misra@Sun.COM if ((range != rule->ir_max_port - rule->ir_min_port) &&
128310946SSangeeta.Misra@Sun.COM range != 0) {
128410946SSangeeta.Misra@Sun.COM ret = EINVAL;
128510946SSangeeta.Misra@Sun.COM goto end;
128610946SSangeeta.Misra@Sun.COM }
128710946SSangeeta.Misra@Sun.COM }
128810946SSangeeta.Misra@Sun.COM
128910946SSangeeta.Misra@Sun.COM /* Check for duplicate. */
129010946SSangeeta.Misra@Sun.COM for (server = rule->ir_servers; server != NULL;
129110946SSangeeta.Misra@Sun.COM server = server->iser_next) {
129210946SSangeeta.Misra@Sun.COM if (IN6_ARE_ADDR_EQUAL(&server->iser_addr_v6, &info->addr) ||
129310946SSangeeta.Misra@Sun.COM strcasecmp(server->iser_name, info->name) == 0) {
129410946SSangeeta.Misra@Sun.COM break;
129510946SSangeeta.Misra@Sun.COM }
129610946SSangeeta.Misra@Sun.COM }
129710946SSangeeta.Misra@Sun.COM if (server != NULL) {
129810946SSangeeta.Misra@Sun.COM ret = EEXIST;
129910946SSangeeta.Misra@Sun.COM goto end;
130010946SSangeeta.Misra@Sun.COM }
130110946SSangeeta.Misra@Sun.COM
130210946SSangeeta.Misra@Sun.COM if ((server = kmem_zalloc(sizeof (ilb_server_t), KM_NOSLEEP)) == NULL) {
130310946SSangeeta.Misra@Sun.COM ret = ENOMEM;
130410946SSangeeta.Misra@Sun.COM goto end;
130510946SSangeeta.Misra@Sun.COM }
130610946SSangeeta.Misra@Sun.COM
130710946SSangeeta.Misra@Sun.COM (void) memcpy(server->iser_name, info->name, ILB_SERVER_NAMESZ - 1);
130810946SSangeeta.Misra@Sun.COM (void) inet_ntop(AF_INET6, &info->addr, server->iser_ip_addr,
130910946SSangeeta.Misra@Sun.COM sizeof (server->iser_ip_addr));
131010946SSangeeta.Misra@Sun.COM stackid = (netstackid_t)(uintptr_t)ilbs->ilbs_ksp->ks_private;
131110946SSangeeta.Misra@Sun.COM server->iser_ksp = ilb_server_kstat_init(stackid, rule, server);
131210946SSangeeta.Misra@Sun.COM if (server->iser_ksp == NULL) {
131310946SSangeeta.Misra@Sun.COM kmem_free(server, sizeof (ilb_server_t));
131410946SSangeeta.Misra@Sun.COM ret = EINVAL;
131510946SSangeeta.Misra@Sun.COM goto end;
131610946SSangeeta.Misra@Sun.COM }
131710946SSangeeta.Misra@Sun.COM
131810946SSangeeta.Misra@Sun.COM server->iser_stackid = stackid;
131910946SSangeeta.Misra@Sun.COM server->iser_addr_v6 = info->addr;
132010946SSangeeta.Misra@Sun.COM server->iser_min_port = min_port;
132110946SSangeeta.Misra@Sun.COM server->iser_max_port = max_port;
132210946SSangeeta.Misra@Sun.COM if (min_port != max_port)
132310946SSangeeta.Misra@Sun.COM server->iser_port_range = B_TRUE;
132410946SSangeeta.Misra@Sun.COM else
132510946SSangeeta.Misra@Sun.COM server->iser_port_range = B_FALSE;
132610946SSangeeta.Misra@Sun.COM
132710946SSangeeta.Misra@Sun.COM /*
132810946SSangeeta.Misra@Sun.COM * If the rule uses NAT, find/create the NAT source entry to use
132910946SSangeeta.Misra@Sun.COM * for this server.
133010946SSangeeta.Misra@Sun.COM */
133110946SSangeeta.Misra@Sun.COM if (rule->ir_topo == ILB_TOPO_IMPL_NAT) {
133210946SSangeeta.Misra@Sun.COM in_port_t port;
133310946SSangeeta.Misra@Sun.COM
133410946SSangeeta.Misra@Sun.COM /*
133510946SSangeeta.Misra@Sun.COM * If the server uses a port range, our port allocation
133610946SSangeeta.Misra@Sun.COM * scheme needs to treat it as a wildcard. Refer to the
133710946SSangeeta.Misra@Sun.COM * comments in ilb_nat.c about the scheme.
133810946SSangeeta.Misra@Sun.COM */
133910946SSangeeta.Misra@Sun.COM if (server->iser_port_range)
134010946SSangeeta.Misra@Sun.COM port = 0;
134110946SSangeeta.Misra@Sun.COM else
134210946SSangeeta.Misra@Sun.COM port = server->iser_min_port;
134310946SSangeeta.Misra@Sun.COM
134410946SSangeeta.Misra@Sun.COM if ((ret = ilb_create_nat_src(ilbs, &server->iser_nat_src,
134510946SSangeeta.Misra@Sun.COM &server->iser_addr_v6, port, &rule->ir_nat_src_start,
134610946SSangeeta.Misra@Sun.COM num_nat_src_v6(&rule->ir_nat_src_start,
134710946SSangeeta.Misra@Sun.COM &rule->ir_nat_src_end))) != 0) {
134810946SSangeeta.Misra@Sun.COM kstat_delete_netstack(server->iser_ksp, stackid);
134910946SSangeeta.Misra@Sun.COM kmem_free(server, sizeof (ilb_server_t));
135010946SSangeeta.Misra@Sun.COM goto end;
135110946SSangeeta.Misra@Sun.COM }
135210946SSangeeta.Misra@Sun.COM }
135310946SSangeeta.Misra@Sun.COM
135410946SSangeeta.Misra@Sun.COM /*
135510946SSangeeta.Misra@Sun.COM * The iser_lock is only used to protect iser_refcnt. All the other
135610946SSangeeta.Misra@Sun.COM * fields in ilb_server_t should not change, except for iser_enabled.
135710946SSangeeta.Misra@Sun.COM * The worst thing that can happen if iser_enabled is messed up is
135810946SSangeeta.Misra@Sun.COM * that one or two packets may not be load balanced to a server
135910946SSangeeta.Misra@Sun.COM * correctly.
136010946SSangeeta.Misra@Sun.COM */
136110946SSangeeta.Misra@Sun.COM server->iser_refcnt = 1;
136210946SSangeeta.Misra@Sun.COM server->iser_enabled = info->flags & ILB_SERVER_ENABLED ? B_TRUE :
136310946SSangeeta.Misra@Sun.COM B_FALSE;
136410946SSangeeta.Misra@Sun.COM mutex_init(&server->iser_lock, NULL, MUTEX_DEFAULT, NULL);
136510946SSangeeta.Misra@Sun.COM cv_init(&server->iser_cv, NULL, CV_DEFAULT, NULL);
136610946SSangeeta.Misra@Sun.COM
136710946SSangeeta.Misra@Sun.COM /* Let the load balancing algorithm know about the addition. */
136810946SSangeeta.Misra@Sun.COM ASSERT(rule->ir_alg != NULL);
136910946SSangeeta.Misra@Sun.COM if ((ret = rule->ir_alg->ilb_alg_server_add(server,
137010946SSangeeta.Misra@Sun.COM rule->ir_alg->ilb_alg_data)) != 0) {
137110946SSangeeta.Misra@Sun.COM kstat_delete_netstack(server->iser_ksp, stackid);
137210946SSangeeta.Misra@Sun.COM kmem_free(server, sizeof (ilb_server_t));
137310946SSangeeta.Misra@Sun.COM goto end;
137410946SSangeeta.Misra@Sun.COM }
137510946SSangeeta.Misra@Sun.COM
137610946SSangeeta.Misra@Sun.COM /*
137710946SSangeeta.Misra@Sun.COM * No need to hold ir_lock since no other thread should manipulate
137810946SSangeeta.Misra@Sun.COM * the following fields until ILB_RULE_BUSY is cleared.
137910946SSangeeta.Misra@Sun.COM */
138010946SSangeeta.Misra@Sun.COM if (rule->ir_servers == NULL) {
138110946SSangeeta.Misra@Sun.COM server->iser_next = NULL;
138210946SSangeeta.Misra@Sun.COM } else {
138310946SSangeeta.Misra@Sun.COM server->iser_next = rule->ir_servers;
138410946SSangeeta.Misra@Sun.COM }
138510946SSangeeta.Misra@Sun.COM rule->ir_servers = server;
138610946SSangeeta.Misra@Sun.COM ILB_R_KSTAT(rule, num_servers);
138710946SSangeeta.Misra@Sun.COM
138810946SSangeeta.Misra@Sun.COM end:
138910946SSangeeta.Misra@Sun.COM mutex_enter(&rule->ir_lock);
139010946SSangeeta.Misra@Sun.COM rule->ir_flags &= ~ILB_RULE_BUSY;
139110946SSangeeta.Misra@Sun.COM cv_signal(&rule->ir_cv);
139210946SSangeeta.Misra@Sun.COM mutex_exit(&rule->ir_lock);
139310946SSangeeta.Misra@Sun.COM return (ret);
139410946SSangeeta.Misra@Sun.COM }
139510946SSangeeta.Misra@Sun.COM
139610946SSangeeta.Misra@Sun.COM /* The routine executed by the delayed rule processing taskq. */
139710946SSangeeta.Misra@Sun.COM static void
ilb_server_del_tq(void * arg)139810946SSangeeta.Misra@Sun.COM ilb_server_del_tq(void *arg)
139910946SSangeeta.Misra@Sun.COM {
140010946SSangeeta.Misra@Sun.COM ilb_server_t *server = (ilb_server_t *)arg;
140110946SSangeeta.Misra@Sun.COM
140210946SSangeeta.Misra@Sun.COM mutex_enter(&server->iser_lock);
140310946SSangeeta.Misra@Sun.COM while (server->iser_refcnt > 1)
140410946SSangeeta.Misra@Sun.COM cv_wait(&server->iser_cv, &server->iser_lock);
140510946SSangeeta.Misra@Sun.COM kstat_delete_netstack(server->iser_ksp, server->iser_stackid);
140610946SSangeeta.Misra@Sun.COM kmem_free(server, sizeof (ilb_server_t));
140710946SSangeeta.Misra@Sun.COM }
140810946SSangeeta.Misra@Sun.COM
140910946SSangeeta.Misra@Sun.COM /*
141010946SSangeeta.Misra@Sun.COM * Delete a back end server from a rule. If the address is IPv4, it is assumed
141110946SSangeeta.Misra@Sun.COM * to be passed in as a mapped address.
141210946SSangeeta.Misra@Sun.COM */
141310946SSangeeta.Misra@Sun.COM int
ilb_server_del(ilb_stack_t * ilbs,zoneid_t zoneid,const char * rule_name,ilb_rule_t * rule,in6_addr_t * addr)141410946SSangeeta.Misra@Sun.COM ilb_server_del(ilb_stack_t *ilbs, zoneid_t zoneid, const char *rule_name,
141510946SSangeeta.Misra@Sun.COM ilb_rule_t *rule, in6_addr_t *addr)
141610946SSangeeta.Misra@Sun.COM {
141710946SSangeeta.Misra@Sun.COM ilb_server_t *server;
141810946SSangeeta.Misra@Sun.COM ilb_server_t *prev_server;
141910946SSangeeta.Misra@Sun.COM int ret = 0;
142010946SSangeeta.Misra@Sun.COM
142110946SSangeeta.Misra@Sun.COM ASSERT((rule == NULL && rule_name != NULL) ||
142210946SSangeeta.Misra@Sun.COM (rule != NULL && rule_name == NULL));
142310946SSangeeta.Misra@Sun.COM if (rule == NULL) {
142410946SSangeeta.Misra@Sun.COM if ((rule = ilb_find_rule(ilbs, zoneid, rule_name,
142510946SSangeeta.Misra@Sun.COM &ret)) == NULL) {
142610946SSangeeta.Misra@Sun.COM return (ret);
142710946SSangeeta.Misra@Sun.COM }
142810946SSangeeta.Misra@Sun.COM }
142910946SSangeeta.Misra@Sun.COM
143010946SSangeeta.Misra@Sun.COM mutex_enter(&rule->ir_lock);
143110946SSangeeta.Misra@Sun.COM /* If someone is already doing server add/del, sleeps and wait. */
143210946SSangeeta.Misra@Sun.COM while (rule->ir_flags & ILB_RULE_BUSY) {
143310946SSangeeta.Misra@Sun.COM if (cv_wait_sig(&rule->ir_cv, &rule->ir_lock) == 0) {
143410946SSangeeta.Misra@Sun.COM if (rule_name != NULL) {
143510946SSangeeta.Misra@Sun.COM if (--rule->ir_refcnt <= 2)
143610946SSangeeta.Misra@Sun.COM cv_signal(&rule->ir_cv);
143710946SSangeeta.Misra@Sun.COM }
143810946SSangeeta.Misra@Sun.COM mutex_exit(&rule->ir_lock);
143910946SSangeeta.Misra@Sun.COM return (EINTR);
144010946SSangeeta.Misra@Sun.COM }
144110946SSangeeta.Misra@Sun.COM }
144210946SSangeeta.Misra@Sun.COM /*
144310946SSangeeta.Misra@Sun.COM * Set the rule to be busy to make sure that no new packet can
144410946SSangeeta.Misra@Sun.COM * use this rule.
144510946SSangeeta.Misra@Sun.COM */
144610946SSangeeta.Misra@Sun.COM rule->ir_flags |= ILB_RULE_BUSY;
144710946SSangeeta.Misra@Sun.COM
144810946SSangeeta.Misra@Sun.COM /* Now wait for all other guys to finish their work. */
144910946SSangeeta.Misra@Sun.COM while (rule->ir_refcnt > 2) {
145010946SSangeeta.Misra@Sun.COM if (cv_wait_sig(&rule->ir_cv, &rule->ir_lock) == 0) {
145110946SSangeeta.Misra@Sun.COM mutex_exit(&rule->ir_lock);
145210946SSangeeta.Misra@Sun.COM ret = EINTR;
145310946SSangeeta.Misra@Sun.COM goto end;
145410946SSangeeta.Misra@Sun.COM }
145510946SSangeeta.Misra@Sun.COM }
145610946SSangeeta.Misra@Sun.COM mutex_exit(&rule->ir_lock);
145710946SSangeeta.Misra@Sun.COM
145810946SSangeeta.Misra@Sun.COM prev_server = NULL;
145910946SSangeeta.Misra@Sun.COM for (server = rule->ir_servers; server != NULL;
146010946SSangeeta.Misra@Sun.COM prev_server = server, server = server->iser_next) {
146110946SSangeeta.Misra@Sun.COM if (IN6_ARE_ADDR_EQUAL(&server->iser_addr_v6, addr))
146210946SSangeeta.Misra@Sun.COM break;
146310946SSangeeta.Misra@Sun.COM }
146410946SSangeeta.Misra@Sun.COM if (server == NULL) {
146510946SSangeeta.Misra@Sun.COM ret = ENOENT;
146610946SSangeeta.Misra@Sun.COM goto end;
146710946SSangeeta.Misra@Sun.COM }
146810946SSangeeta.Misra@Sun.COM
146910946SSangeeta.Misra@Sun.COM /*
147010946SSangeeta.Misra@Sun.COM * Let the load balancing algorithm know about the removal.
147110946SSangeeta.Misra@Sun.COM * The algorithm may disallow the removal...
147210946SSangeeta.Misra@Sun.COM */
147310946SSangeeta.Misra@Sun.COM if ((ret = rule->ir_alg->ilb_alg_server_del(server,
147410946SSangeeta.Misra@Sun.COM rule->ir_alg->ilb_alg_data)) != 0) {
147510946SSangeeta.Misra@Sun.COM goto end;
147610946SSangeeta.Misra@Sun.COM }
147710946SSangeeta.Misra@Sun.COM
147810946SSangeeta.Misra@Sun.COM if (prev_server == NULL)
147910946SSangeeta.Misra@Sun.COM rule->ir_servers = server->iser_next;
148010946SSangeeta.Misra@Sun.COM else
148110946SSangeeta.Misra@Sun.COM prev_server->iser_next = server->iser_next;
148210946SSangeeta.Misra@Sun.COM
148310946SSangeeta.Misra@Sun.COM ILB_R_KSTAT_UPDATE(rule, num_servers, -1);
148410946SSangeeta.Misra@Sun.COM
148510946SSangeeta.Misra@Sun.COM /*
148610946SSangeeta.Misra@Sun.COM * Mark the server as disabled so that if there is any sticky cache
148710946SSangeeta.Misra@Sun.COM * using this server around, it won't be used.
148810946SSangeeta.Misra@Sun.COM */
148910946SSangeeta.Misra@Sun.COM server->iser_enabled = B_FALSE;
149010946SSangeeta.Misra@Sun.COM
149110946SSangeeta.Misra@Sun.COM mutex_enter(&server->iser_lock);
149210946SSangeeta.Misra@Sun.COM
149310946SSangeeta.Misra@Sun.COM /*
149410946SSangeeta.Misra@Sun.COM * De-allocate the NAT source array. The indiviual ilb_nat_src_entry_t
149510946SSangeeta.Misra@Sun.COM * may not go away if there is still a conn using it. The NAT source
149610946SSangeeta.Misra@Sun.COM * timer will do the garbage collection.
149710946SSangeeta.Misra@Sun.COM */
149810946SSangeeta.Misra@Sun.COM ilb_destroy_nat_src(&server->iser_nat_src);
149910946SSangeeta.Misra@Sun.COM
150010946SSangeeta.Misra@Sun.COM /* If there is a hard limit on when a server should die, set it. */
150110946SSangeeta.Misra@Sun.COM if (rule->ir_conn_drain_timeout != 0) {
150210946SSangeeta.Misra@Sun.COM (void) atomic_swap_64((uint64_t *)&server->iser_die_time,
1503*11066Srafael.vanoni@sun.com ddi_get_lbolt64() +
1504*11066Srafael.vanoni@sun.com SEC_TO_TICK(rule->ir_conn_drain_timeout));
150510946SSangeeta.Misra@Sun.COM }
150610946SSangeeta.Misra@Sun.COM
150710946SSangeeta.Misra@Sun.COM if (server->iser_refcnt > 1) {
150810946SSangeeta.Misra@Sun.COM (void) taskq_dispatch(ilbs->ilbs_rule_taskq, ilb_server_del_tq,
150910946SSangeeta.Misra@Sun.COM server, TQ_SLEEP);
151010946SSangeeta.Misra@Sun.COM mutex_exit(&server->iser_lock);
151110946SSangeeta.Misra@Sun.COM } else {
151210946SSangeeta.Misra@Sun.COM kstat_delete_netstack(server->iser_ksp, server->iser_stackid);
151310946SSangeeta.Misra@Sun.COM kmem_free(server, sizeof (ilb_server_t));
151410946SSangeeta.Misra@Sun.COM }
151510946SSangeeta.Misra@Sun.COM
151610946SSangeeta.Misra@Sun.COM end:
151710946SSangeeta.Misra@Sun.COM mutex_enter(&rule->ir_lock);
151810946SSangeeta.Misra@Sun.COM rule->ir_flags &= ~ILB_RULE_BUSY;
151910946SSangeeta.Misra@Sun.COM if (rule_name != NULL)
152010946SSangeeta.Misra@Sun.COM rule->ir_refcnt--;
152110946SSangeeta.Misra@Sun.COM cv_signal(&rule->ir_cv);
152210946SSangeeta.Misra@Sun.COM mutex_exit(&rule->ir_lock);
152310946SSangeeta.Misra@Sun.COM return (ret);
152410946SSangeeta.Misra@Sun.COM }
152510946SSangeeta.Misra@Sun.COM
152610946SSangeeta.Misra@Sun.COM /*
152710946SSangeeta.Misra@Sun.COM * First check if the destination of the ICMP message matches a VIP of
152810946SSangeeta.Misra@Sun.COM * a rule. If it does not, just return ILB_PASSED.
152910946SSangeeta.Misra@Sun.COM *
153010946SSangeeta.Misra@Sun.COM * If the destination matches a VIP:
153110946SSangeeta.Misra@Sun.COM *
153210946SSangeeta.Misra@Sun.COM * For ICMP_ECHO_REQUEST, generate a response on behalf of the back end
153310946SSangeeta.Misra@Sun.COM * server.
153410946SSangeeta.Misra@Sun.COM *
153510946SSangeeta.Misra@Sun.COM * For ICMP_DEST_UNREACHABLE fragmentation needed, check inside the payload
153610946SSangeeta.Misra@Sun.COM * and see which back end server we should send this message to. And we
153710946SSangeeta.Misra@Sun.COM * need to do NAT on both the payload message and the outside IP packet.
153810946SSangeeta.Misra@Sun.COM *
153910946SSangeeta.Misra@Sun.COM * For other ICMP messages, drop them.
154010946SSangeeta.Misra@Sun.COM */
154110946SSangeeta.Misra@Sun.COM /* ARGSUSED */
154210946SSangeeta.Misra@Sun.COM static int
ilb_icmp_v4(ilb_stack_t * ilbs,ill_t * ill,mblk_t * mp,ipha_t * ipha,icmph_t * icmph,ipaddr_t * lb_dst)154310946SSangeeta.Misra@Sun.COM ilb_icmp_v4(ilb_stack_t *ilbs, ill_t *ill, mblk_t *mp, ipha_t *ipha,
154410946SSangeeta.Misra@Sun.COM icmph_t *icmph, ipaddr_t *lb_dst)
154510946SSangeeta.Misra@Sun.COM {
154610946SSangeeta.Misra@Sun.COM ipaddr_t vip;
154710946SSangeeta.Misra@Sun.COM ilb_rule_t *rule;
154810946SSangeeta.Misra@Sun.COM in6_addr_t addr6;
154910946SSangeeta.Misra@Sun.COM
155010946SSangeeta.Misra@Sun.COM if (!ilb_rule_match_vip_v4(ilbs, ipha->ipha_dst, &rule))
155110946SSangeeta.Misra@Sun.COM return (ILB_PASSED);
155210946SSangeeta.Misra@Sun.COM
155310946SSangeeta.Misra@Sun.COM
155410946SSangeeta.Misra@Sun.COM if ((uint8_t *)icmph + sizeof (icmph_t) > mp->b_wptr) {
155510946SSangeeta.Misra@Sun.COM ILB_R_KSTAT(rule, icmp_dropped);
155610946SSangeeta.Misra@Sun.COM ILB_RULE_REFRELE(rule);
155710946SSangeeta.Misra@Sun.COM return (ILB_DROPPED);
155810946SSangeeta.Misra@Sun.COM }
155910946SSangeeta.Misra@Sun.COM
156010946SSangeeta.Misra@Sun.COM switch (icmph->icmph_type) {
156110946SSangeeta.Misra@Sun.COM case ICMP_ECHO_REQUEST:
156210946SSangeeta.Misra@Sun.COM ILB_R_KSTAT(rule, icmp_echo_processed);
156310946SSangeeta.Misra@Sun.COM ILB_RULE_REFRELE(rule);
156410946SSangeeta.Misra@Sun.COM
156510946SSangeeta.Misra@Sun.COM icmph->icmph_type = ICMP_ECHO_REPLY;
156610946SSangeeta.Misra@Sun.COM icmph->icmph_checksum = 0;
156710946SSangeeta.Misra@Sun.COM icmph->icmph_checksum = IP_CSUM(mp, IPH_HDR_LENGTH(ipha), 0);
156810946SSangeeta.Misra@Sun.COM ipha->ipha_ttl =
156910946SSangeeta.Misra@Sun.COM ilbs->ilbs_netstack->netstack_ip->ips_ip_def_ttl;
157010946SSangeeta.Misra@Sun.COM *lb_dst = ipha->ipha_src;
157110946SSangeeta.Misra@Sun.COM vip = ipha->ipha_dst;
157210946SSangeeta.Misra@Sun.COM ipha->ipha_dst = ipha->ipha_src;
157310946SSangeeta.Misra@Sun.COM ipha->ipha_src = vip;
157410946SSangeeta.Misra@Sun.COM return (ILB_BALANCED);
157510946SSangeeta.Misra@Sun.COM case ICMP_DEST_UNREACHABLE: {
157610946SSangeeta.Misra@Sun.COM int ret;
157710946SSangeeta.Misra@Sun.COM
157810946SSangeeta.Misra@Sun.COM if (icmph->icmph_code != ICMP_FRAGMENTATION_NEEDED) {
157910946SSangeeta.Misra@Sun.COM ILB_R_KSTAT(rule, icmp_dropped);
158010946SSangeeta.Misra@Sun.COM ILB_RULE_REFRELE(rule);
158110946SSangeeta.Misra@Sun.COM return (ILB_DROPPED);
158210946SSangeeta.Misra@Sun.COM }
158310946SSangeeta.Misra@Sun.COM if (ilb_check_icmp_conn(ilbs, mp, IPPROTO_IP, ipha, icmph,
158410946SSangeeta.Misra@Sun.COM &addr6)) {
158510946SSangeeta.Misra@Sun.COM ILB_R_KSTAT(rule, icmp_2big_processed);
158610946SSangeeta.Misra@Sun.COM ret = ILB_BALANCED;
158710946SSangeeta.Misra@Sun.COM } else {
158810946SSangeeta.Misra@Sun.COM ILB_R_KSTAT(rule, icmp_2big_dropped);
158910946SSangeeta.Misra@Sun.COM ret = ILB_DROPPED;
159010946SSangeeta.Misra@Sun.COM }
159110946SSangeeta.Misra@Sun.COM ILB_RULE_REFRELE(rule);
159210946SSangeeta.Misra@Sun.COM IN6_V4MAPPED_TO_IPADDR(&addr6, *lb_dst);
159310946SSangeeta.Misra@Sun.COM return (ret);
159410946SSangeeta.Misra@Sun.COM }
159510946SSangeeta.Misra@Sun.COM default:
159610946SSangeeta.Misra@Sun.COM ILB_R_KSTAT(rule, icmp_dropped);
159710946SSangeeta.Misra@Sun.COM ILB_RULE_REFRELE(rule);
159810946SSangeeta.Misra@Sun.COM return (ILB_DROPPED);
159910946SSangeeta.Misra@Sun.COM }
160010946SSangeeta.Misra@Sun.COM }
160110946SSangeeta.Misra@Sun.COM
160210946SSangeeta.Misra@Sun.COM /* ARGSUSED */
160310946SSangeeta.Misra@Sun.COM static int
ilb_icmp_v6(ilb_stack_t * ilbs,ill_t * ill,mblk_t * mp,ip6_t * ip6h,icmp6_t * icmp6,in6_addr_t * lb_dst)160410946SSangeeta.Misra@Sun.COM ilb_icmp_v6(ilb_stack_t *ilbs, ill_t *ill, mblk_t *mp, ip6_t *ip6h,
160510946SSangeeta.Misra@Sun.COM icmp6_t *icmp6, in6_addr_t *lb_dst)
160610946SSangeeta.Misra@Sun.COM {
160710946SSangeeta.Misra@Sun.COM ilb_rule_t *rule;
160810946SSangeeta.Misra@Sun.COM
160910946SSangeeta.Misra@Sun.COM if (!ilb_rule_match_vip_v6(ilbs, &ip6h->ip6_dst, &rule))
161010946SSangeeta.Misra@Sun.COM return (ILB_PASSED);
161110946SSangeeta.Misra@Sun.COM
161210946SSangeeta.Misra@Sun.COM if ((uint8_t *)icmp6 + sizeof (icmp6_t) > mp->b_wptr) {
161310946SSangeeta.Misra@Sun.COM ILB_R_KSTAT(rule, icmp_dropped);
161410946SSangeeta.Misra@Sun.COM ILB_RULE_REFRELE(rule);
161510946SSangeeta.Misra@Sun.COM return (ILB_DROPPED);
161610946SSangeeta.Misra@Sun.COM }
161710946SSangeeta.Misra@Sun.COM
161810946SSangeeta.Misra@Sun.COM switch (icmp6->icmp6_type) {
161910946SSangeeta.Misra@Sun.COM case ICMP6_ECHO_REQUEST: {
162010946SSangeeta.Misra@Sun.COM int hdr_len;
162110946SSangeeta.Misra@Sun.COM
162210946SSangeeta.Misra@Sun.COM ILB_R_KSTAT(rule, icmp_echo_processed);
162310946SSangeeta.Misra@Sun.COM ILB_RULE_REFRELE(rule);
162410946SSangeeta.Misra@Sun.COM
162510946SSangeeta.Misra@Sun.COM icmp6->icmp6_type = ICMP6_ECHO_REPLY;
162610946SSangeeta.Misra@Sun.COM icmp6->icmp6_cksum = ip6h->ip6_plen;
162710946SSangeeta.Misra@Sun.COM hdr_len = (char *)icmp6 - (char *)ip6h;
162810946SSangeeta.Misra@Sun.COM icmp6->icmp6_cksum = IP_CSUM(mp, hdr_len,
162910946SSangeeta.Misra@Sun.COM ilb_pseudo_sum_v6(ip6h, IPPROTO_ICMPV6));
163010946SSangeeta.Misra@Sun.COM ip6h->ip6_vcf &= ~IPV6_FLOWINFO_FLOWLABEL;
163110946SSangeeta.Misra@Sun.COM ip6h->ip6_hops =
163210946SSangeeta.Misra@Sun.COM ilbs->ilbs_netstack->netstack_ip->ips_ipv6_def_hops;
163310946SSangeeta.Misra@Sun.COM *lb_dst = ip6h->ip6_src;
163410946SSangeeta.Misra@Sun.COM ip6h->ip6_src = ip6h->ip6_dst;
163510946SSangeeta.Misra@Sun.COM ip6h->ip6_dst = *lb_dst;
163610946SSangeeta.Misra@Sun.COM return (ILB_BALANCED);
163710946SSangeeta.Misra@Sun.COM }
163810946SSangeeta.Misra@Sun.COM case ICMP6_PACKET_TOO_BIG: {
163910946SSangeeta.Misra@Sun.COM int ret;
164010946SSangeeta.Misra@Sun.COM
164110946SSangeeta.Misra@Sun.COM if (ilb_check_icmp_conn(ilbs, mp, IPPROTO_IPV6, ip6h, icmp6,
164210946SSangeeta.Misra@Sun.COM lb_dst)) {
164310946SSangeeta.Misra@Sun.COM ILB_R_KSTAT(rule, icmp_2big_processed);
164410946SSangeeta.Misra@Sun.COM ret = ILB_BALANCED;
164510946SSangeeta.Misra@Sun.COM } else {
164610946SSangeeta.Misra@Sun.COM ILB_R_KSTAT(rule, icmp_2big_dropped);
164710946SSangeeta.Misra@Sun.COM ret = ILB_DROPPED;
164810946SSangeeta.Misra@Sun.COM }
164910946SSangeeta.Misra@Sun.COM ILB_RULE_REFRELE(rule);
165010946SSangeeta.Misra@Sun.COM return (ret);
165110946SSangeeta.Misra@Sun.COM }
165210946SSangeeta.Misra@Sun.COM default:
165310946SSangeeta.Misra@Sun.COM ILB_R_KSTAT(rule, icmp_dropped);
165410946SSangeeta.Misra@Sun.COM ILB_RULE_REFRELE(rule);
165510946SSangeeta.Misra@Sun.COM return (ILB_DROPPED);
165610946SSangeeta.Misra@Sun.COM }
165710946SSangeeta.Misra@Sun.COM }
165810946SSangeeta.Misra@Sun.COM
165910946SSangeeta.Misra@Sun.COM /*
166010946SSangeeta.Misra@Sun.COM * Common routine to check an incoming packet and decide what to do with it.
166110946SSangeeta.Misra@Sun.COM * called by ilb_check_v4|v6().
166210946SSangeeta.Misra@Sun.COM */
166310946SSangeeta.Misra@Sun.COM static int
ilb_check(ilb_stack_t * ilbs,ill_t * ill,mblk_t * mp,in6_addr_t * src,in6_addr_t * dst,int l3,int l4,void * iph,uint8_t * tph,uint32_t pkt_len,in6_addr_t * lb_dst)166410946SSangeeta.Misra@Sun.COM ilb_check(ilb_stack_t *ilbs, ill_t *ill, mblk_t *mp, in6_addr_t *src,
166510946SSangeeta.Misra@Sun.COM in6_addr_t *dst, int l3, int l4, void *iph, uint8_t *tph, uint32_t pkt_len,
166610946SSangeeta.Misra@Sun.COM in6_addr_t *lb_dst)
166710946SSangeeta.Misra@Sun.COM {
166810946SSangeeta.Misra@Sun.COM in_port_t sport, dport;
166910946SSangeeta.Misra@Sun.COM tcpha_t *tcph;
167010946SSangeeta.Misra@Sun.COM udpha_t *udph;
167110946SSangeeta.Misra@Sun.COM ilb_rule_t *rule;
167210946SSangeeta.Misra@Sun.COM ilb_server_t *server;
167310946SSangeeta.Misra@Sun.COM boolean_t balanced;
167410946SSangeeta.Misra@Sun.COM struct ilb_sticky_s *s = NULL;
167510946SSangeeta.Misra@Sun.COM int ret;
167610946SSangeeta.Misra@Sun.COM uint32_t ip_sum, tp_sum;
167710946SSangeeta.Misra@Sun.COM ilb_nat_info_t info;
167810946SSangeeta.Misra@Sun.COM uint16_t nat_src_idx;
167910946SSangeeta.Misra@Sun.COM boolean_t busy;
168010946SSangeeta.Misra@Sun.COM
168110946SSangeeta.Misra@Sun.COM /*
168210946SSangeeta.Misra@Sun.COM * We don't really need to switch here since both protocols's
168310946SSangeeta.Misra@Sun.COM * ports are at the same offset. Just prepare for future protocol
168410946SSangeeta.Misra@Sun.COM * specific processing.
168510946SSangeeta.Misra@Sun.COM */
168610946SSangeeta.Misra@Sun.COM switch (l4) {
168710946SSangeeta.Misra@Sun.COM case IPPROTO_TCP:
168810946SSangeeta.Misra@Sun.COM if (tph + TCP_MIN_HEADER_LENGTH > mp->b_wptr)
168910946SSangeeta.Misra@Sun.COM return (ILB_DROPPED);
169010946SSangeeta.Misra@Sun.COM tcph = (tcpha_t *)tph;
169110946SSangeeta.Misra@Sun.COM sport = tcph->tha_lport;
169210946SSangeeta.Misra@Sun.COM dport = tcph->tha_fport;
169310946SSangeeta.Misra@Sun.COM break;
169410946SSangeeta.Misra@Sun.COM case IPPROTO_UDP:
169510946SSangeeta.Misra@Sun.COM if (tph + sizeof (udpha_t) > mp->b_wptr)
169610946SSangeeta.Misra@Sun.COM return (ILB_DROPPED);
169710946SSangeeta.Misra@Sun.COM udph = (udpha_t *)tph;
169810946SSangeeta.Misra@Sun.COM sport = udph->uha_src_port;
169910946SSangeeta.Misra@Sun.COM dport = udph->uha_dst_port;
170010946SSangeeta.Misra@Sun.COM break;
170110946SSangeeta.Misra@Sun.COM default:
170210946SSangeeta.Misra@Sun.COM return (ILB_PASSED);
170310946SSangeeta.Misra@Sun.COM }
170410946SSangeeta.Misra@Sun.COM
170510946SSangeeta.Misra@Sun.COM /* Fast path, there is an existing conn. */
170610946SSangeeta.Misra@Sun.COM if (ilb_check_conn(ilbs, l3, iph, l4, tph, src, dst, sport, dport,
170710946SSangeeta.Misra@Sun.COM pkt_len, lb_dst)) {
170810946SSangeeta.Misra@Sun.COM return (ILB_BALANCED);
170910946SSangeeta.Misra@Sun.COM }
171010946SSangeeta.Misra@Sun.COM
171110946SSangeeta.Misra@Sun.COM /*
171210946SSangeeta.Misra@Sun.COM * If there is no existing connection for the incoming packet, check
171310946SSangeeta.Misra@Sun.COM * to see if the packet matches a rule. If not, just let IP decide
171410946SSangeeta.Misra@Sun.COM * what to do with it.
171510946SSangeeta.Misra@Sun.COM *
171610946SSangeeta.Misra@Sun.COM * Note: a reply from back end server should not match a rule. A
171710946SSangeeta.Misra@Sun.COM * reply should match one existing conn.
171810946SSangeeta.Misra@Sun.COM */
171910946SSangeeta.Misra@Sun.COM rule = ilb_rule_hash(ilbs, l3, l4, dst, dport, ill->ill_zoneid,
172010946SSangeeta.Misra@Sun.COM pkt_len, &busy);
172110946SSangeeta.Misra@Sun.COM if (rule == NULL) {
172210946SSangeeta.Misra@Sun.COM /* If the rule is busy, just drop the packet. */
172310946SSangeeta.Misra@Sun.COM if (busy)
172410946SSangeeta.Misra@Sun.COM return (ILB_DROPPED);
172510946SSangeeta.Misra@Sun.COM else
172610946SSangeeta.Misra@Sun.COM return (ILB_PASSED);
172710946SSangeeta.Misra@Sun.COM }
172810946SSangeeta.Misra@Sun.COM
172910946SSangeeta.Misra@Sun.COM /*
173010946SSangeeta.Misra@Sun.COM * The packet matches a rule, use the rule load balance algorithm
173110946SSangeeta.Misra@Sun.COM * to find a server.
173210946SSangeeta.Misra@Sun.COM */
173310946SSangeeta.Misra@Sun.COM balanced = rule->ir_alg->ilb_alg_lb(src, sport, dst, dport,
173410946SSangeeta.Misra@Sun.COM rule->ir_alg->ilb_alg_data, &server);
173510946SSangeeta.Misra@Sun.COM /*
173610946SSangeeta.Misra@Sun.COM * This can only happen if there is no server in a rule or all
173710946SSangeeta.Misra@Sun.COM * the servers are currently disabled.
173810946SSangeeta.Misra@Sun.COM */
173910946SSangeeta.Misra@Sun.COM if (!balanced)
174010946SSangeeta.Misra@Sun.COM goto no_server;
174110946SSangeeta.Misra@Sun.COM
174210946SSangeeta.Misra@Sun.COM /*
174310946SSangeeta.Misra@Sun.COM * If the rule is sticky enabled, we need to check the sticky table.
174410946SSangeeta.Misra@Sun.COM * If there is a sticky entry for the client, use the previous server
174510946SSangeeta.Misra@Sun.COM * instead of the one found above (note that both can be the same).
174610946SSangeeta.Misra@Sun.COM * If there is no entry for that client, add an entry to the sticky
174710946SSangeeta.Misra@Sun.COM * table. Both the find and add are done in ilb_sticky_find_add()
174810946SSangeeta.Misra@Sun.COM * to avoid checking for duplicate when adding an entry.
174910946SSangeeta.Misra@Sun.COM */
175010946SSangeeta.Misra@Sun.COM if (rule->ir_flags & ILB_RULE_STICKY) {
175110946SSangeeta.Misra@Sun.COM in6_addr_t addr;
175210946SSangeeta.Misra@Sun.COM
175310946SSangeeta.Misra@Sun.COM V6_MASK_COPY(*src, rule->ir_sticky_mask, addr);
175410946SSangeeta.Misra@Sun.COM if ((server = ilb_sticky_find_add(ilbs, rule, &addr, server,
175510946SSangeeta.Misra@Sun.COM &s, &nat_src_idx)) == NULL) {
175610946SSangeeta.Misra@Sun.COM ILB_R_KSTAT(rule, nomem_pkt_dropped);
175710946SSangeeta.Misra@Sun.COM ILB_R_KSTAT_UPDATE(rule, nomem_bytes_dropped, pkt_len);
175810946SSangeeta.Misra@Sun.COM goto no_server;
175910946SSangeeta.Misra@Sun.COM }
176010946SSangeeta.Misra@Sun.COM }
176110946SSangeeta.Misra@Sun.COM
176210946SSangeeta.Misra@Sun.COM /*
176310946SSangeeta.Misra@Sun.COM * We are holding a reference on the rule, so the server
176410946SSangeeta.Misra@Sun.COM * cannot go away.
176510946SSangeeta.Misra@Sun.COM */
176610946SSangeeta.Misra@Sun.COM *lb_dst = server->iser_addr_v6;
176710946SSangeeta.Misra@Sun.COM ILB_S_KSTAT(server, pkt_processed);
176810946SSangeeta.Misra@Sun.COM ILB_S_KSTAT_UPDATE(server, bytes_processed, pkt_len);
176910946SSangeeta.Misra@Sun.COM
177010946SSangeeta.Misra@Sun.COM switch (rule->ir_topo) {
177110946SSangeeta.Misra@Sun.COM case ILB_TOPO_IMPL_NAT: {
177210946SSangeeta.Misra@Sun.COM ilb_nat_src_entry_t *src_ent;
177310946SSangeeta.Misra@Sun.COM uint16_t *src_idx;
177410946SSangeeta.Misra@Sun.COM
177510946SSangeeta.Misra@Sun.COM /*
177610946SSangeeta.Misra@Sun.COM * We create a cache even if it is not a SYN segment.
177710946SSangeeta.Misra@Sun.COM * The server should return a RST. When we see the
177810946SSangeeta.Misra@Sun.COM * RST, we will destroy this cache. But by having
177910946SSangeeta.Misra@Sun.COM * a cache, we know how to NAT the returned RST.
178010946SSangeeta.Misra@Sun.COM */
178110946SSangeeta.Misra@Sun.COM info.vip = *dst;
178210946SSangeeta.Misra@Sun.COM info.dport = dport;
178310946SSangeeta.Misra@Sun.COM info.src = *src;
178410946SSangeeta.Misra@Sun.COM info.sport = sport;
178510946SSangeeta.Misra@Sun.COM
178610946SSangeeta.Misra@Sun.COM /* If stickiness is enabled, use the same source address */
178710946SSangeeta.Misra@Sun.COM if (s != NULL)
178810946SSangeeta.Misra@Sun.COM src_idx = &nat_src_idx;
178910946SSangeeta.Misra@Sun.COM else
179010946SSangeeta.Misra@Sun.COM src_idx = NULL;
179110946SSangeeta.Misra@Sun.COM
179210946SSangeeta.Misra@Sun.COM if ((src_ent = ilb_alloc_nat_addr(server->iser_nat_src,
179310946SSangeeta.Misra@Sun.COM &info.nat_src, &info.nat_sport, src_idx)) == NULL) {
179410946SSangeeta.Misra@Sun.COM if (s != NULL)
179510946SSangeeta.Misra@Sun.COM ilb_sticky_refrele(s);
179610946SSangeeta.Misra@Sun.COM ILB_R_KSTAT(rule, pkt_dropped);
179710946SSangeeta.Misra@Sun.COM ILB_R_KSTAT_UPDATE(rule, bytes_dropped, pkt_len);
179810946SSangeeta.Misra@Sun.COM ILB_R_KSTAT(rule, noport_pkt_dropped);
179910946SSangeeta.Misra@Sun.COM ILB_R_KSTAT_UPDATE(rule, noport_bytes_dropped, pkt_len);
180010946SSangeeta.Misra@Sun.COM ret = ILB_DROPPED;
180110946SSangeeta.Misra@Sun.COM break;
180210946SSangeeta.Misra@Sun.COM }
180310946SSangeeta.Misra@Sun.COM info.src_ent = src_ent;
180410946SSangeeta.Misra@Sun.COM info.nat_dst = server->iser_addr_v6;
180510946SSangeeta.Misra@Sun.COM if (rule->ir_port_range && server->iser_port_range) {
180610946SSangeeta.Misra@Sun.COM info.nat_dport = htons(ntohs(dport) -
180710946SSangeeta.Misra@Sun.COM rule->ir_min_port + server->iser_min_port);
180810946SSangeeta.Misra@Sun.COM } else {
180910946SSangeeta.Misra@Sun.COM info.nat_dport = htons(server->iser_min_port);
181010946SSangeeta.Misra@Sun.COM }
181110946SSangeeta.Misra@Sun.COM
181210946SSangeeta.Misra@Sun.COM /*
181310946SSangeeta.Misra@Sun.COM * If ilb_conn_add() fails, it will release the reference on
181410946SSangeeta.Misra@Sun.COM * sticky info and de-allocate the NAT source port allocated
181510946SSangeeta.Misra@Sun.COM * above.
181610946SSangeeta.Misra@Sun.COM */
181710946SSangeeta.Misra@Sun.COM if (ilb_conn_add(ilbs, rule, server, src, sport, dst,
181810946SSangeeta.Misra@Sun.COM dport, &info, &ip_sum, &tp_sum, s) != 0) {
181910946SSangeeta.Misra@Sun.COM ILB_R_KSTAT(rule, pkt_dropped);
182010946SSangeeta.Misra@Sun.COM ILB_R_KSTAT_UPDATE(rule, bytes_dropped, pkt_len);
182110946SSangeeta.Misra@Sun.COM ILB_R_KSTAT(rule, nomem_pkt_dropped);
182210946SSangeeta.Misra@Sun.COM ILB_R_KSTAT_UPDATE(rule, nomem_bytes_dropped, pkt_len);
182310946SSangeeta.Misra@Sun.COM ret = ILB_DROPPED;
182410946SSangeeta.Misra@Sun.COM break;
182510946SSangeeta.Misra@Sun.COM }
182610946SSangeeta.Misra@Sun.COM ilb_full_nat(l3, iph, l4, tph, &info, ip_sum, tp_sum, B_TRUE);
182710946SSangeeta.Misra@Sun.COM ret = ILB_BALANCED;
182810946SSangeeta.Misra@Sun.COM break;
182910946SSangeeta.Misra@Sun.COM }
183010946SSangeeta.Misra@Sun.COM case ILB_TOPO_IMPL_HALF_NAT:
183110946SSangeeta.Misra@Sun.COM info.vip = *dst;
183210946SSangeeta.Misra@Sun.COM info.nat_dst = server->iser_addr_v6;
183310946SSangeeta.Misra@Sun.COM info.dport = dport;
183410946SSangeeta.Misra@Sun.COM if (rule->ir_port_range && server->iser_port_range) {
183510946SSangeeta.Misra@Sun.COM info.nat_dport = htons(ntohs(dport) -
183610946SSangeeta.Misra@Sun.COM rule->ir_min_port + server->iser_min_port);
183710946SSangeeta.Misra@Sun.COM } else {
183810946SSangeeta.Misra@Sun.COM info.nat_dport = htons(server->iser_min_port);
183910946SSangeeta.Misra@Sun.COM }
184010946SSangeeta.Misra@Sun.COM
184110946SSangeeta.Misra@Sun.COM if (ilb_conn_add(ilbs, rule, server, src, sport, dst,
184210946SSangeeta.Misra@Sun.COM dport, &info, &ip_sum, &tp_sum, s) != 0) {
184310946SSangeeta.Misra@Sun.COM ILB_R_KSTAT(rule, pkt_dropped);
184410946SSangeeta.Misra@Sun.COM ILB_R_KSTAT_UPDATE(rule, bytes_dropped, pkt_len);
184510946SSangeeta.Misra@Sun.COM ILB_R_KSTAT(rule, nomem_pkt_dropped);
184610946SSangeeta.Misra@Sun.COM ILB_R_KSTAT_UPDATE(rule, nomem_bytes_dropped, pkt_len);
184710946SSangeeta.Misra@Sun.COM ret = ILB_DROPPED;
184810946SSangeeta.Misra@Sun.COM break;
184910946SSangeeta.Misra@Sun.COM }
185010946SSangeeta.Misra@Sun.COM ilb_half_nat(l3, iph, l4, tph, &info, ip_sum, tp_sum, B_TRUE);
185110946SSangeeta.Misra@Sun.COM
185210946SSangeeta.Misra@Sun.COM ret = ILB_BALANCED;
185310946SSangeeta.Misra@Sun.COM break;
185410946SSangeeta.Misra@Sun.COM case ILB_TOPO_IMPL_DSR:
185510946SSangeeta.Misra@Sun.COM /*
185610946SSangeeta.Misra@Sun.COM * By decrementing the sticky refcnt, the period of
185710946SSangeeta.Misra@Sun.COM * stickiness (life time of ilb_sticky_t) will be
185810946SSangeeta.Misra@Sun.COM * from now to (now + default expiry time).
185910946SSangeeta.Misra@Sun.COM */
186010946SSangeeta.Misra@Sun.COM if (s != NULL)
186110946SSangeeta.Misra@Sun.COM ilb_sticky_refrele(s);
186210946SSangeeta.Misra@Sun.COM ret = ILB_BALANCED;
186310946SSangeeta.Misra@Sun.COM break;
186410946SSangeeta.Misra@Sun.COM default:
186510946SSangeeta.Misra@Sun.COM cmn_err(CE_PANIC, "data corruption unknown topology: %p",
186610946SSangeeta.Misra@Sun.COM (void *) rule);
186710946SSangeeta.Misra@Sun.COM break;
186810946SSangeeta.Misra@Sun.COM }
186910946SSangeeta.Misra@Sun.COM ILB_RULE_REFRELE(rule);
187010946SSangeeta.Misra@Sun.COM return (ret);
187110946SSangeeta.Misra@Sun.COM
187210946SSangeeta.Misra@Sun.COM no_server:
187310946SSangeeta.Misra@Sun.COM /* This can only happen if there is no server available. */
187410946SSangeeta.Misra@Sun.COM ILB_R_KSTAT(rule, pkt_dropped);
187510946SSangeeta.Misra@Sun.COM ILB_R_KSTAT_UPDATE(rule, bytes_dropped, pkt_len);
187610946SSangeeta.Misra@Sun.COM ILB_RULE_REFRELE(rule);
187710946SSangeeta.Misra@Sun.COM return (ILB_DROPPED);
187810946SSangeeta.Misra@Sun.COM }
187910946SSangeeta.Misra@Sun.COM
188010946SSangeeta.Misra@Sun.COM int
ilb_check_v4(ilb_stack_t * ilbs,ill_t * ill,mblk_t * mp,ipha_t * ipha,int l4,uint8_t * tph,ipaddr_t * lb_dst)188110946SSangeeta.Misra@Sun.COM ilb_check_v4(ilb_stack_t *ilbs, ill_t *ill, mblk_t *mp, ipha_t *ipha, int l4,
188210946SSangeeta.Misra@Sun.COM uint8_t *tph, ipaddr_t *lb_dst)
188310946SSangeeta.Misra@Sun.COM {
188410946SSangeeta.Misra@Sun.COM in6_addr_t v6_src, v6_dst, v6_lb_dst;
188510946SSangeeta.Misra@Sun.COM int ret;
188610946SSangeeta.Misra@Sun.COM
188710946SSangeeta.Misra@Sun.COM ASSERT(DB_REF(mp) == 1);
188810946SSangeeta.Misra@Sun.COM
188910946SSangeeta.Misra@Sun.COM if (l4 == IPPROTO_ICMP) {
189010946SSangeeta.Misra@Sun.COM return (ilb_icmp_v4(ilbs, ill, mp, ipha, (icmph_t *)tph,
189110946SSangeeta.Misra@Sun.COM lb_dst));
189210946SSangeeta.Misra@Sun.COM }
189310946SSangeeta.Misra@Sun.COM
189410946SSangeeta.Misra@Sun.COM IN6_IPADDR_TO_V4MAPPED(ipha->ipha_src, &v6_src);
189510946SSangeeta.Misra@Sun.COM IN6_IPADDR_TO_V4MAPPED(ipha->ipha_dst, &v6_dst);
189610946SSangeeta.Misra@Sun.COM ret = ilb_check(ilbs, ill, mp, &v6_src, &v6_dst, IPPROTO_IP, l4, ipha,
189710946SSangeeta.Misra@Sun.COM tph, ntohs(ipha->ipha_length), &v6_lb_dst);
189810946SSangeeta.Misra@Sun.COM if (ret == ILB_BALANCED)
189910946SSangeeta.Misra@Sun.COM IN6_V4MAPPED_TO_IPADDR(&v6_lb_dst, *lb_dst);
190010946SSangeeta.Misra@Sun.COM return (ret);
190110946SSangeeta.Misra@Sun.COM }
190210946SSangeeta.Misra@Sun.COM
190310946SSangeeta.Misra@Sun.COM int
ilb_check_v6(ilb_stack_t * ilbs,ill_t * ill,mblk_t * mp,ip6_t * ip6h,int l4,uint8_t * tph,in6_addr_t * lb_dst)190410946SSangeeta.Misra@Sun.COM ilb_check_v6(ilb_stack_t *ilbs, ill_t *ill, mblk_t *mp, ip6_t *ip6h, int l4,
190510946SSangeeta.Misra@Sun.COM uint8_t *tph, in6_addr_t *lb_dst)
190610946SSangeeta.Misra@Sun.COM {
190710946SSangeeta.Misra@Sun.COM uint32_t pkt_len;
190810946SSangeeta.Misra@Sun.COM
190910946SSangeeta.Misra@Sun.COM ASSERT(DB_REF(mp) == 1);
191010946SSangeeta.Misra@Sun.COM
191110946SSangeeta.Misra@Sun.COM if (l4 == IPPROTO_ICMPV6) {
191210946SSangeeta.Misra@Sun.COM return (ilb_icmp_v6(ilbs, ill, mp, ip6h, (icmp6_t *)tph,
191310946SSangeeta.Misra@Sun.COM lb_dst));
191410946SSangeeta.Misra@Sun.COM }
191510946SSangeeta.Misra@Sun.COM
191610946SSangeeta.Misra@Sun.COM pkt_len = ntohs(ip6h->ip6_plen) + IPV6_HDR_LEN;
191710946SSangeeta.Misra@Sun.COM return (ilb_check(ilbs, ill, mp, &ip6h->ip6_src, &ip6h->ip6_dst,
191810946SSangeeta.Misra@Sun.COM IPPROTO_IPV6, l4, ip6h, tph, pkt_len, lb_dst));
191910946SSangeeta.Misra@Sun.COM }
192010946SSangeeta.Misra@Sun.COM
192110946SSangeeta.Misra@Sun.COM void
ilb_get_num_rules(ilb_stack_t * ilbs,zoneid_t zoneid,uint32_t * num_rules)192210946SSangeeta.Misra@Sun.COM ilb_get_num_rules(ilb_stack_t *ilbs, zoneid_t zoneid, uint32_t *num_rules)
192310946SSangeeta.Misra@Sun.COM {
192410946SSangeeta.Misra@Sun.COM ilb_rule_t *tmp_rule;
192510946SSangeeta.Misra@Sun.COM
192610946SSangeeta.Misra@Sun.COM mutex_enter(&ilbs->ilbs_g_lock);
192710946SSangeeta.Misra@Sun.COM *num_rules = 0;
192810946SSangeeta.Misra@Sun.COM for (tmp_rule = ilbs->ilbs_rule_head; tmp_rule != NULL;
192910946SSangeeta.Misra@Sun.COM tmp_rule = tmp_rule->ir_next) {
193010946SSangeeta.Misra@Sun.COM if (tmp_rule->ir_zoneid == zoneid)
193110946SSangeeta.Misra@Sun.COM *num_rules += 1;
193210946SSangeeta.Misra@Sun.COM }
193310946SSangeeta.Misra@Sun.COM mutex_exit(&ilbs->ilbs_g_lock);
193410946SSangeeta.Misra@Sun.COM }
193510946SSangeeta.Misra@Sun.COM
193610946SSangeeta.Misra@Sun.COM int
ilb_get_num_servers(ilb_stack_t * ilbs,zoneid_t zoneid,const char * name,uint32_t * num_servers)193710946SSangeeta.Misra@Sun.COM ilb_get_num_servers(ilb_stack_t *ilbs, zoneid_t zoneid, const char *name,
193810946SSangeeta.Misra@Sun.COM uint32_t *num_servers)
193910946SSangeeta.Misra@Sun.COM {
194010946SSangeeta.Misra@Sun.COM ilb_rule_t *rule;
194110946SSangeeta.Misra@Sun.COM int err;
194210946SSangeeta.Misra@Sun.COM
194310946SSangeeta.Misra@Sun.COM if ((rule = ilb_find_rule(ilbs, zoneid, name, &err)) == NULL)
194410946SSangeeta.Misra@Sun.COM return (err);
194510946SSangeeta.Misra@Sun.COM *num_servers = rule->ir_kstat.num_servers.value.ui64;
194610946SSangeeta.Misra@Sun.COM ILB_RULE_REFRELE(rule);
194710946SSangeeta.Misra@Sun.COM return (0);
194810946SSangeeta.Misra@Sun.COM }
194910946SSangeeta.Misra@Sun.COM
195010946SSangeeta.Misra@Sun.COM int
ilb_get_servers(ilb_stack_t * ilbs,zoneid_t zoneid,const char * name,ilb_server_info_t * servers,uint32_t * num_servers)195110946SSangeeta.Misra@Sun.COM ilb_get_servers(ilb_stack_t *ilbs, zoneid_t zoneid, const char *name,
195210946SSangeeta.Misra@Sun.COM ilb_server_info_t *servers, uint32_t *num_servers)
195310946SSangeeta.Misra@Sun.COM {
195410946SSangeeta.Misra@Sun.COM ilb_rule_t *rule;
195510946SSangeeta.Misra@Sun.COM ilb_server_t *server;
195610946SSangeeta.Misra@Sun.COM size_t cnt;
195710946SSangeeta.Misra@Sun.COM int err;
195810946SSangeeta.Misra@Sun.COM
195910946SSangeeta.Misra@Sun.COM if ((rule = ilb_find_rule(ilbs, zoneid, name, &err)) == NULL)
196010946SSangeeta.Misra@Sun.COM return (err);
196110946SSangeeta.Misra@Sun.COM for (server = rule->ir_servers, cnt = *num_servers;
196210946SSangeeta.Misra@Sun.COM server != NULL && cnt > 0;
196310946SSangeeta.Misra@Sun.COM server = server->iser_next, cnt--, servers++) {
196410946SSangeeta.Misra@Sun.COM (void) memcpy(servers->name, server->iser_name,
196510946SSangeeta.Misra@Sun.COM ILB_SERVER_NAMESZ);
196610946SSangeeta.Misra@Sun.COM servers->addr = server->iser_addr_v6;
196710946SSangeeta.Misra@Sun.COM servers->min_port = htons(server->iser_min_port);
196810946SSangeeta.Misra@Sun.COM servers->max_port = htons(server->iser_max_port);
196910946SSangeeta.Misra@Sun.COM servers->flags = server->iser_enabled ? ILB_SERVER_ENABLED : 0;
197010946SSangeeta.Misra@Sun.COM servers->err = 0;
197110946SSangeeta.Misra@Sun.COM }
197210946SSangeeta.Misra@Sun.COM ILB_RULE_REFRELE(rule);
197310946SSangeeta.Misra@Sun.COM *num_servers -= cnt;
197410946SSangeeta.Misra@Sun.COM
197510946SSangeeta.Misra@Sun.COM return (0);
197610946SSangeeta.Misra@Sun.COM }
197710946SSangeeta.Misra@Sun.COM
197810946SSangeeta.Misra@Sun.COM void
ilb_get_rulenames(ilb_stack_t * ilbs,zoneid_t zoneid,uint32_t * num_names,char * buf)197910946SSangeeta.Misra@Sun.COM ilb_get_rulenames(ilb_stack_t *ilbs, zoneid_t zoneid, uint32_t *num_names,
198010946SSangeeta.Misra@Sun.COM char *buf)
198110946SSangeeta.Misra@Sun.COM {
198210946SSangeeta.Misra@Sun.COM ilb_rule_t *tmp_rule;
198310946SSangeeta.Misra@Sun.COM int cnt;
198410946SSangeeta.Misra@Sun.COM
198510946SSangeeta.Misra@Sun.COM if (*num_names == 0)
198610946SSangeeta.Misra@Sun.COM return;
198710946SSangeeta.Misra@Sun.COM
198810946SSangeeta.Misra@Sun.COM mutex_enter(&ilbs->ilbs_g_lock);
198910946SSangeeta.Misra@Sun.COM for (cnt = 0, tmp_rule = ilbs->ilbs_rule_head; tmp_rule != NULL;
199010946SSangeeta.Misra@Sun.COM tmp_rule = tmp_rule->ir_next) {
199110946SSangeeta.Misra@Sun.COM if (tmp_rule->ir_zoneid != zoneid)
199210946SSangeeta.Misra@Sun.COM continue;
199310946SSangeeta.Misra@Sun.COM
199410946SSangeeta.Misra@Sun.COM (void) memcpy(buf, tmp_rule->ir_name, ILB_RULE_NAMESZ);
199510946SSangeeta.Misra@Sun.COM buf += ILB_RULE_NAMESZ;
199610946SSangeeta.Misra@Sun.COM if (++cnt == *num_names)
199710946SSangeeta.Misra@Sun.COM break;
199810946SSangeeta.Misra@Sun.COM }
199910946SSangeeta.Misra@Sun.COM mutex_exit(&ilbs->ilbs_g_lock);
200010946SSangeeta.Misra@Sun.COM *num_names = cnt;
200110946SSangeeta.Misra@Sun.COM }
200210946SSangeeta.Misra@Sun.COM
200310946SSangeeta.Misra@Sun.COM int
ilb_rule_list(ilb_stack_t * ilbs,zoneid_t zoneid,ilb_rule_cmd_t * cmd)200410946SSangeeta.Misra@Sun.COM ilb_rule_list(ilb_stack_t *ilbs, zoneid_t zoneid, ilb_rule_cmd_t *cmd)
200510946SSangeeta.Misra@Sun.COM {
200610946SSangeeta.Misra@Sun.COM ilb_rule_t *rule;
200710946SSangeeta.Misra@Sun.COM int err;
200810946SSangeeta.Misra@Sun.COM
200910946SSangeeta.Misra@Sun.COM if ((rule = ilb_find_rule(ilbs, zoneid, cmd->name, &err)) == NULL) {
201010946SSangeeta.Misra@Sun.COM return (err);
201110946SSangeeta.Misra@Sun.COM }
201210946SSangeeta.Misra@Sun.COM
201310946SSangeeta.Misra@Sun.COM /*
201410946SSangeeta.Misra@Sun.COM * Except the enabled flags, none of the following will change
201510946SSangeeta.Misra@Sun.COM * in the life time of a rule. So we don't hold the mutex when
201610946SSangeeta.Misra@Sun.COM * reading them. The worst is to report a wrong enabled flags.
201710946SSangeeta.Misra@Sun.COM */
201810946SSangeeta.Misra@Sun.COM cmd->ip_ver = rule->ir_ipver;
201910946SSangeeta.Misra@Sun.COM cmd->proto = rule->ir_proto;
202010946SSangeeta.Misra@Sun.COM cmd->min_port = htons(rule->ir_min_port);
202110946SSangeeta.Misra@Sun.COM cmd->max_port = htons(rule->ir_max_port);
202210946SSangeeta.Misra@Sun.COM
202310946SSangeeta.Misra@Sun.COM cmd->vip = rule->ir_target_v6;
202410946SSangeeta.Misra@Sun.COM cmd->algo = rule->ir_alg_type;
202510946SSangeeta.Misra@Sun.COM cmd->topo = rule->ir_topo;
202610946SSangeeta.Misra@Sun.COM
202710946SSangeeta.Misra@Sun.COM cmd->nat_src_start = rule->ir_nat_src_start;
202810946SSangeeta.Misra@Sun.COM cmd->nat_src_end = rule->ir_nat_src_end;
202910946SSangeeta.Misra@Sun.COM
203010946SSangeeta.Misra@Sun.COM cmd->conn_drain_timeout = rule->ir_conn_drain_timeout;
203110946SSangeeta.Misra@Sun.COM cmd->nat_expiry = rule->ir_nat_expiry;
203210946SSangeeta.Misra@Sun.COM cmd->sticky_expiry = rule->ir_sticky_expiry;
203310946SSangeeta.Misra@Sun.COM
203410946SSangeeta.Misra@Sun.COM cmd->flags = 0;
203510946SSangeeta.Misra@Sun.COM if (rule->ir_flags & ILB_RULE_ENABLED)
203610946SSangeeta.Misra@Sun.COM cmd->flags |= ILB_RULE_ENABLED;
203710946SSangeeta.Misra@Sun.COM if (rule->ir_flags & ILB_RULE_STICKY) {
203810946SSangeeta.Misra@Sun.COM cmd->flags |= ILB_RULE_STICKY;
203910946SSangeeta.Misra@Sun.COM cmd->sticky_mask = rule->ir_sticky_mask;
204010946SSangeeta.Misra@Sun.COM }
204110946SSangeeta.Misra@Sun.COM
204210946SSangeeta.Misra@Sun.COM ILB_RULE_REFRELE(rule);
204310946SSangeeta.Misra@Sun.COM return (0);
204410946SSangeeta.Misra@Sun.COM }
204510946SSangeeta.Misra@Sun.COM
204610946SSangeeta.Misra@Sun.COM static void *
ilb_stack_init(netstackid_t stackid,netstack_t * ns)204710946SSangeeta.Misra@Sun.COM ilb_stack_init(netstackid_t stackid, netstack_t *ns)
204810946SSangeeta.Misra@Sun.COM {
204910946SSangeeta.Misra@Sun.COM ilb_stack_t *ilbs;
205010946SSangeeta.Misra@Sun.COM char tq_name[TASKQ_NAMELEN];
205110946SSangeeta.Misra@Sun.COM
205210946SSangeeta.Misra@Sun.COM ilbs = kmem_alloc(sizeof (ilb_stack_t), KM_SLEEP);
205310946SSangeeta.Misra@Sun.COM ilbs->ilbs_netstack = ns;
205410946SSangeeta.Misra@Sun.COM
205510946SSangeeta.Misra@Sun.COM ilbs->ilbs_rule_head = NULL;
205610946SSangeeta.Misra@Sun.COM ilbs->ilbs_g_hash = NULL;
205710946SSangeeta.Misra@Sun.COM mutex_init(&ilbs->ilbs_g_lock, NULL, MUTEX_DEFAULT, NULL);
205810946SSangeeta.Misra@Sun.COM
205910946SSangeeta.Misra@Sun.COM ilbs->ilbs_kstat = kmem_alloc(sizeof (ilb_g_kstat_t), KM_SLEEP);
206010946SSangeeta.Misra@Sun.COM if ((ilbs->ilbs_ksp = ilb_kstat_g_init(stackid, ilbs)) == NULL) {
206110946SSangeeta.Misra@Sun.COM kmem_free(ilbs, sizeof (ilb_stack_t));
206210946SSangeeta.Misra@Sun.COM return (NULL);
206310946SSangeeta.Misra@Sun.COM }
206410946SSangeeta.Misra@Sun.COM
206510946SSangeeta.Misra@Sun.COM /*
206610946SSangeeta.Misra@Sun.COM * ilbs_conn/sticky_hash related info is initialized in
206710946SSangeeta.Misra@Sun.COM * ilb_conn/sticky_hash_init().
206810946SSangeeta.Misra@Sun.COM */
206910946SSangeeta.Misra@Sun.COM ilbs->ilbs_conn_taskq = NULL;
207010946SSangeeta.Misra@Sun.COM ilbs->ilbs_rule_hash_size = ilb_rule_hash_size;
207110946SSangeeta.Misra@Sun.COM ilbs->ilbs_conn_hash_size = ilb_conn_hash_size;
207210946SSangeeta.Misra@Sun.COM ilbs->ilbs_c2s_conn_hash = NULL;
207310946SSangeeta.Misra@Sun.COM ilbs->ilbs_s2c_conn_hash = NULL;
207410946SSangeeta.Misra@Sun.COM ilbs->ilbs_conn_timer_list = NULL;
207510946SSangeeta.Misra@Sun.COM
207610946SSangeeta.Misra@Sun.COM ilbs->ilbs_sticky_hash = NULL;
207710946SSangeeta.Misra@Sun.COM ilbs->ilbs_sticky_hash_size = ilb_sticky_hash_size;
207810946SSangeeta.Misra@Sun.COM ilbs->ilbs_sticky_timer_list = NULL;
207910946SSangeeta.Misra@Sun.COM ilbs->ilbs_sticky_taskq = NULL;
208010946SSangeeta.Misra@Sun.COM
208110946SSangeeta.Misra@Sun.COM /* The allocation is done later when there is a rule using NAT mode. */
208210946SSangeeta.Misra@Sun.COM ilbs->ilbs_nat_src = NULL;
208310946SSangeeta.Misra@Sun.COM ilbs->ilbs_nat_src_hash_size = ilb_nat_src_hash_size;
208410946SSangeeta.Misra@Sun.COM mutex_init(&ilbs->ilbs_nat_src_lock, NULL, MUTEX_DEFAULT, NULL);
208510946SSangeeta.Misra@Sun.COM ilbs->ilbs_nat_src_tid = 0;
208610946SSangeeta.Misra@Sun.COM
208710946SSangeeta.Misra@Sun.COM /* For listing the conn hash table */
208810946SSangeeta.Misra@Sun.COM mutex_init(&ilbs->ilbs_conn_list_lock, NULL, MUTEX_DEFAULT, NULL);
208910946SSangeeta.Misra@Sun.COM cv_init(&ilbs->ilbs_conn_list_cv, NULL, CV_DEFAULT, NULL);
209010946SSangeeta.Misra@Sun.COM ilbs->ilbs_conn_list_busy = B_FALSE;
209110946SSangeeta.Misra@Sun.COM ilbs->ilbs_conn_list_cur = 0;
209210946SSangeeta.Misra@Sun.COM ilbs->ilbs_conn_list_connp = NULL;
209310946SSangeeta.Misra@Sun.COM
209410946SSangeeta.Misra@Sun.COM /* For listing the sticky hash table */
209510946SSangeeta.Misra@Sun.COM mutex_init(&ilbs->ilbs_sticky_list_lock, NULL, MUTEX_DEFAULT, NULL);
209610946SSangeeta.Misra@Sun.COM cv_init(&ilbs->ilbs_sticky_list_cv, NULL, CV_DEFAULT, NULL);
209710946SSangeeta.Misra@Sun.COM ilbs->ilbs_sticky_list_busy = B_FALSE;
209810946SSangeeta.Misra@Sun.COM ilbs->ilbs_sticky_list_cur = 0;
209910946SSangeeta.Misra@Sun.COM ilbs->ilbs_sticky_list_curp = NULL;
210010946SSangeeta.Misra@Sun.COM
210110973SSangeeta.Misra@Sun.COM (void) snprintf(tq_name, sizeof (tq_name), "ilb_rule_taskq_%p",
210210973SSangeeta.Misra@Sun.COM (void *)ns);
210310946SSangeeta.Misra@Sun.COM ilbs->ilbs_rule_taskq = taskq_create(tq_name, ILB_RULE_TASKQ_NUM_THR,
210410946SSangeeta.Misra@Sun.COM minclsyspri, 1, INT_MAX, TASKQ_PREPOPULATE|TASKQ_DYNAMIC);
210510946SSangeeta.Misra@Sun.COM
210610946SSangeeta.Misra@Sun.COM return (ilbs);
210710946SSangeeta.Misra@Sun.COM }
210810946SSangeeta.Misra@Sun.COM
210910946SSangeeta.Misra@Sun.COM /* ARGSUSED */
211010946SSangeeta.Misra@Sun.COM static void
ilb_stack_shutdown(netstackid_t stackid,void * arg)211110946SSangeeta.Misra@Sun.COM ilb_stack_shutdown(netstackid_t stackid, void *arg)
211210946SSangeeta.Misra@Sun.COM {
211310946SSangeeta.Misra@Sun.COM ilb_stack_t *ilbs = (ilb_stack_t *)arg;
211410946SSangeeta.Misra@Sun.COM ilb_rule_t *tmp_rule;
211510946SSangeeta.Misra@Sun.COM
211610946SSangeeta.Misra@Sun.COM ilb_sticky_hash_fini(ilbs);
211710946SSangeeta.Misra@Sun.COM ilb_conn_hash_fini(ilbs);
211810946SSangeeta.Misra@Sun.COM mutex_enter(&ilbs->ilbs_g_lock);
211910946SSangeeta.Misra@Sun.COM while ((tmp_rule = ilbs->ilbs_rule_head) != NULL) {
212010946SSangeeta.Misra@Sun.COM ilb_rule_hash_del(tmp_rule);
212110946SSangeeta.Misra@Sun.COM ilb_rule_g_del(ilbs, tmp_rule);
212210946SSangeeta.Misra@Sun.COM mutex_exit(&ilbs->ilbs_g_lock);
212310946SSangeeta.Misra@Sun.COM ilb_rule_del_common(ilbs, tmp_rule);
212410946SSangeeta.Misra@Sun.COM mutex_enter(&ilbs->ilbs_g_lock);
212510946SSangeeta.Misra@Sun.COM }
212610946SSangeeta.Misra@Sun.COM mutex_exit(&ilbs->ilbs_g_lock);
212710946SSangeeta.Misra@Sun.COM if (ilbs->ilbs_nat_src != NULL)
212810946SSangeeta.Misra@Sun.COM ilb_nat_src_fini(ilbs);
212910946SSangeeta.Misra@Sun.COM }
213010946SSangeeta.Misra@Sun.COM
213110946SSangeeta.Misra@Sun.COM static void
ilb_stack_fini(netstackid_t stackid,void * arg)213210946SSangeeta.Misra@Sun.COM ilb_stack_fini(netstackid_t stackid, void * arg)
213310946SSangeeta.Misra@Sun.COM {
213410946SSangeeta.Misra@Sun.COM ilb_stack_t *ilbs = (ilb_stack_t *)arg;
213510946SSangeeta.Misra@Sun.COM
213610946SSangeeta.Misra@Sun.COM ilb_rule_hash_fini(ilbs);
213710946SSangeeta.Misra@Sun.COM taskq_destroy(ilbs->ilbs_rule_taskq);
213810946SSangeeta.Misra@Sun.COM ilb_kstat_g_fini(stackid, ilbs);
213910946SSangeeta.Misra@Sun.COM kmem_free(ilbs->ilbs_kstat, sizeof (ilb_g_kstat_t));
214010946SSangeeta.Misra@Sun.COM kmem_free(ilbs, sizeof (ilb_stack_t));
214110946SSangeeta.Misra@Sun.COM }
214210946SSangeeta.Misra@Sun.COM
214310946SSangeeta.Misra@Sun.COM void
ilb_ddi_g_init(void)214410946SSangeeta.Misra@Sun.COM ilb_ddi_g_init(void)
214510946SSangeeta.Misra@Sun.COM {
214610946SSangeeta.Misra@Sun.COM netstack_register(NS_ILB, ilb_stack_init, ilb_stack_shutdown,
214710946SSangeeta.Misra@Sun.COM ilb_stack_fini);
214810946SSangeeta.Misra@Sun.COM }
214910946SSangeeta.Misra@Sun.COM
215010946SSangeeta.Misra@Sun.COM void
ilb_ddi_g_destroy(void)215110946SSangeeta.Misra@Sun.COM ilb_ddi_g_destroy(void)
215210946SSangeeta.Misra@Sun.COM {
215310946SSangeeta.Misra@Sun.COM netstack_unregister(NS_ILB);
215410946SSangeeta.Misra@Sun.COM ilb_conn_cache_fini();
215510946SSangeeta.Misra@Sun.COM ilb_sticky_cache_fini();
215610946SSangeeta.Misra@Sun.COM }
2157