1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #include <sys/errno.h> 28 #include <sys/sysmacros.h> 29 #include <sys/debug.h> 30 #include <sys/kmem.h> 31 #include <sys/list.h> 32 #include <net/if.h> 33 #include <netinet/in.h> 34 #include <inet/ilb.h> 35 #include "ilb_impl.h" 36 #include "ilb_alg.h" 37 38 typedef struct { 39 ilb_server_t *server; 40 boolean_t enabled; 41 list_node_t list; 42 } rr_server_t; 43 44 typedef struct rr_s { 45 kmutex_t rr_lock; 46 list_t rr_servers; 47 rr_server_t *rr_next; 48 } rr_t; 49 50 static void rr_fini(ilb_alg_data_t **); 51 52 /* ARGSUSED */ 53 static boolean_t 54 rr_lb(in6_addr_t *saddr, in_port_t sport, in6_addr_t *daddr, 55 in_port_t dport, void *alg_data, ilb_server_t **ret_server) 56 { 57 rr_t *rr_alg = (rr_t *)alg_data; 58 list_t *servers; 59 rr_server_t *start; 60 61 ASSERT(ret_server != NULL); 62 *ret_server = NULL; 63 64 mutex_enter(&rr_alg->rr_lock); 65 servers = &rr_alg->rr_servers; 66 if (list_is_empty(servers)) { 67 mutex_exit(&rr_alg->rr_lock); 68 return (B_FALSE); 69 } 70 if (rr_alg->rr_next == NULL) 71 rr_alg->rr_next = list_head(servers); 72 start = rr_alg->rr_next; 73 while (!rr_alg->rr_next->enabled) { 74 rr_alg->rr_next = list_next(servers, rr_alg->rr_next); 75 if (rr_alg->rr_next == NULL) 76 rr_alg->rr_next = list_head(servers); 77 if (rr_alg->rr_next == start) { 78 mutex_exit(&rr_alg->rr_lock); 79 return (B_FALSE); 80 } 81 } 82 83 *ret_server = rr_alg->rr_next->server; 84 rr_alg->rr_next = list_next(servers, rr_alg->rr_next); 85 mutex_exit(&rr_alg->rr_lock); 86 return (B_TRUE); 87 } 88 89 static int 90 rr_server_del(ilb_server_t *host, void *alg_data) 91 { 92 rr_t *rr_alg = (rr_t *)alg_data; 93 list_t *servers = &rr_alg->rr_servers; 94 rr_server_t *tmp_server; 95 96 mutex_enter(&rr_alg->rr_lock); 97 for (tmp_server = list_head(servers); tmp_server != NULL; 98 tmp_server = list_next(servers, tmp_server)) { 99 if (tmp_server->server == host) { 100 if (rr_alg->rr_next == tmp_server) { 101 rr_alg->rr_next = list_next(servers, 102 tmp_server); 103 } 104 list_remove(servers, tmp_server); 105 break; 106 } 107 } 108 mutex_exit(&rr_alg->rr_lock); 109 if (tmp_server == NULL) 110 return (EINVAL); 111 kmem_free(tmp_server, sizeof (rr_server_t)); 112 113 ILB_SERVER_REFRELE(host); 114 return (0); 115 } 116 117 static int 118 rr_server_add(ilb_server_t *host, void *alg_data) 119 { 120 rr_t *rr_alg = (rr_t *)alg_data; 121 rr_server_t *new_server; 122 123 new_server = kmem_alloc(sizeof (rr_server_t), KM_NOSLEEP); 124 if (new_server == NULL) 125 return (ENOMEM); 126 new_server->server = host; 127 new_server->enabled = host->iser_enabled; 128 129 mutex_enter(&rr_alg->rr_lock); 130 list_insert_head(&rr_alg->rr_servers, new_server); 131 mutex_exit(&rr_alg->rr_lock); 132 133 ILB_SERVER_REFHOLD(host); 134 return (0); 135 } 136 137 static int 138 rr_server_toggle(list_t *servers, ilb_server_t *host, boolean_t value) 139 { 140 rr_server_t *tmp_server; 141 142 if (list_is_empty(servers)) 143 return (EINVAL); 144 145 for (tmp_server = list_head(servers); tmp_server != NULL; 146 tmp_server = list_next(servers, tmp_server)) { 147 if (tmp_server->server == host) { 148 tmp_server->enabled = value; 149 break; 150 } 151 } 152 if (tmp_server != NULL) 153 return (0); 154 else 155 return (EINVAL); 156 } 157 158 static int 159 rr_server_enable(ilb_server_t *host, void *alg_data) 160 { 161 rr_t *rr_alg = (rr_t *)alg_data; 162 list_t *servers; 163 int ret; 164 165 mutex_enter(&rr_alg->rr_lock); 166 servers = &rr_alg->rr_servers; 167 ret = rr_server_toggle(servers, host, B_TRUE); 168 mutex_exit(&rr_alg->rr_lock); 169 return (ret); 170 } 171 172 static int 173 rr_server_disable(ilb_server_t *host, void *alg_data) 174 { 175 rr_t *rr_alg = (rr_t *)alg_data; 176 list_t *servers; 177 int ret; 178 179 mutex_enter(&rr_alg->rr_lock); 180 servers = &rr_alg->rr_servers; 181 ret = rr_server_toggle(servers, host, B_FALSE); 182 mutex_exit(&rr_alg->rr_lock); 183 return (ret); 184 } 185 186 /* ARGSUSED */ 187 ilb_alg_data_t * 188 ilb_alg_rr_init(ilb_rule_t *rule, void *arg) 189 { 190 ilb_alg_data_t *alg; 191 rr_t *rr_alg; 192 193 if ((alg = kmem_alloc(sizeof (ilb_alg_data_t), KM_NOSLEEP)) == NULL) 194 return (NULL); 195 if ((rr_alg = kmem_alloc(sizeof (rr_t), KM_NOSLEEP)) == NULL) { 196 kmem_free(alg, sizeof (ilb_alg_data_t)); 197 return (NULL); 198 } 199 200 alg->ilb_alg_lb = rr_lb; 201 alg->ilb_alg_server_del = rr_server_del; 202 alg->ilb_alg_server_add = rr_server_add; 203 alg->ilb_alg_server_enable = rr_server_enable; 204 alg->ilb_alg_server_disable = rr_server_disable; 205 alg->ilb_alg_fini = rr_fini; 206 alg->ilb_alg_data = rr_alg; 207 208 mutex_init(&rr_alg->rr_lock, NULL, MUTEX_DEFAULT, NULL); 209 list_create(&rr_alg->rr_servers, sizeof (rr_server_t), 210 offsetof(rr_server_t, list)); 211 rr_alg->rr_next = NULL; 212 213 return (alg); 214 } 215 216 static void 217 rr_fini(ilb_alg_data_t **alg) 218 { 219 rr_t *rr_alg; 220 rr_server_t *tmp_server; 221 list_t *servers; 222 223 rr_alg = (*alg)->ilb_alg_data; 224 servers = &rr_alg->rr_servers; 225 while ((tmp_server = list_head(servers)) != NULL) { 226 list_remove(servers, tmp_server); 227 ILB_SERVER_REFRELE(tmp_server->server); 228 kmem_free(tmp_server, sizeof (rr_server_t)); 229 } 230 list_destroy(servers); 231 kmem_free(rr_alg, sizeof (rr_t)); 232 kmem_free(*alg, sizeof (ilb_alg_data_t)); 233 *alg = NULL; 234 } 235