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/list.h> 30 #include <net/if.h> 31 #include <netinet/in.h> 32 #include <inet/ilb.h> 33 #include "ilb_impl.h" 34 #include "ilb_alg.h" 35 36 typedef struct { 37 ilb_server_t *server; 38 boolean_t enabled; 39 list_node_t list; 40 } rr_server_t; 41 42 typedef struct rr_s { 43 kmutex_t rr_lock; 44 list_t rr_servers; 45 rr_server_t *rr_next; 46 } rr_t; 47 48 static void rr_fini(ilb_alg_data_t **); 49 50 /* ARGSUSED */ 51 static boolean_t 52 rr_lb(in6_addr_t *saddr, in_port_t sport, in6_addr_t *daddr, 53 in_port_t dport, void *alg_data, ilb_server_t **ret_server) 54 { 55 rr_t *rr_alg = (rr_t *)alg_data; 56 list_t *servers; 57 rr_server_t *start; 58 59 ASSERT(ret_server != NULL); 60 *ret_server = NULL; 61 62 mutex_enter(&rr_alg->rr_lock); 63 servers = &rr_alg->rr_servers; 64 if (list_is_empty(servers)) { 65 mutex_exit(&rr_alg->rr_lock); 66 return (B_FALSE); 67 } 68 if (rr_alg->rr_next == NULL) 69 rr_alg->rr_next = list_head(servers); 70 start = rr_alg->rr_next; 71 while (!rr_alg->rr_next->enabled) { 72 rr_alg->rr_next = list_next(servers, rr_alg->rr_next); 73 if (rr_alg->rr_next == NULL) 74 rr_alg->rr_next = list_head(servers); 75 if (rr_alg->rr_next == start) { 76 mutex_exit(&rr_alg->rr_lock); 77 return (B_FALSE); 78 } 79 } 80 81 *ret_server = rr_alg->rr_next->server; 82 rr_alg->rr_next = list_next(servers, rr_alg->rr_next); 83 mutex_exit(&rr_alg->rr_lock); 84 return (B_TRUE); 85 } 86 87 static int 88 rr_server_del(ilb_server_t *host, void *alg_data) 89 { 90 rr_t *rr_alg = (rr_t *)alg_data; 91 list_t *servers = &rr_alg->rr_servers; 92 rr_server_t *tmp_server; 93 94 mutex_enter(&rr_alg->rr_lock); 95 for (tmp_server = list_head(servers); tmp_server != NULL; 96 tmp_server = list_next(servers, tmp_server)) { 97 if (tmp_server->server == host) { 98 if (rr_alg->rr_next == tmp_server) { 99 rr_alg->rr_next = list_next(servers, 100 tmp_server); 101 } 102 list_remove(servers, tmp_server); 103 break; 104 } 105 } 106 mutex_exit(&rr_alg->rr_lock); 107 if (tmp_server == NULL) 108 return (EINVAL); 109 kmem_free(tmp_server, sizeof (rr_server_t)); 110 111 ILB_SERVER_REFRELE(host); 112 return (0); 113 } 114 115 static int 116 rr_server_add(ilb_server_t *host, void *alg_data) 117 { 118 rr_t *rr_alg = (rr_t *)alg_data; 119 rr_server_t *new_server; 120 121 new_server = kmem_alloc(sizeof (rr_server_t), KM_NOSLEEP); 122 if (new_server == NULL) 123 return (ENOMEM); 124 new_server->server = host; 125 new_server->enabled = host->iser_enabled; 126 127 mutex_enter(&rr_alg->rr_lock); 128 list_insert_head(&rr_alg->rr_servers, new_server); 129 mutex_exit(&rr_alg->rr_lock); 130 131 ILB_SERVER_REFHOLD(host); 132 return (0); 133 } 134 135 static int 136 rr_server_toggle(list_t *servers, ilb_server_t *host, boolean_t value) 137 { 138 rr_server_t *tmp_server; 139 140 if (list_is_empty(servers)) 141 return (EINVAL); 142 143 for (tmp_server = list_head(servers); tmp_server != NULL; 144 tmp_server = list_next(servers, tmp_server)) { 145 if (tmp_server->server == host) { 146 tmp_server->enabled = value; 147 break; 148 } 149 } 150 if (tmp_server != NULL) 151 return (0); 152 else 153 return (EINVAL); 154 } 155 156 static int 157 rr_server_enable(ilb_server_t *host, void *alg_data) 158 { 159 rr_t *rr_alg = (rr_t *)alg_data; 160 list_t *servers; 161 int ret; 162 163 mutex_enter(&rr_alg->rr_lock); 164 servers = &rr_alg->rr_servers; 165 ret = rr_server_toggle(servers, host, B_TRUE); 166 mutex_exit(&rr_alg->rr_lock); 167 return (ret); 168 } 169 170 static int 171 rr_server_disable(ilb_server_t *host, void *alg_data) 172 { 173 rr_t *rr_alg = (rr_t *)alg_data; 174 list_t *servers; 175 int ret; 176 177 mutex_enter(&rr_alg->rr_lock); 178 servers = &rr_alg->rr_servers; 179 ret = rr_server_toggle(servers, host, B_FALSE); 180 mutex_exit(&rr_alg->rr_lock); 181 return (ret); 182 } 183 184 /* ARGSUSED */ 185 ilb_alg_data_t * 186 ilb_alg_rr_init(ilb_rule_t *rule, void *arg) 187 { 188 ilb_alg_data_t *alg; 189 rr_t *rr_alg; 190 191 if ((alg = kmem_alloc(sizeof (ilb_alg_data_t), KM_NOSLEEP)) == NULL) 192 return (NULL); 193 if ((rr_alg = kmem_alloc(sizeof (rr_t), KM_NOSLEEP)) == NULL) { 194 kmem_free(alg, sizeof (ilb_alg_data_t)); 195 return (NULL); 196 } 197 198 alg->ilb_alg_lb = rr_lb; 199 alg->ilb_alg_server_del = rr_server_del; 200 alg->ilb_alg_server_add = rr_server_add; 201 alg->ilb_alg_server_enable = rr_server_enable; 202 alg->ilb_alg_server_disable = rr_server_disable; 203 alg->ilb_alg_fini = rr_fini; 204 alg->ilb_alg_data = rr_alg; 205 206 mutex_init(&rr_alg->rr_lock, NULL, MUTEX_DEFAULT, NULL); 207 list_create(&rr_alg->rr_servers, sizeof (rr_server_t), 208 offsetof(rr_server_t, list)); 209 rr_alg->rr_next = NULL; 210 211 return (alg); 212 } 213 214 static void 215 rr_fini(ilb_alg_data_t **alg) 216 { 217 rr_t *rr_alg; 218 rr_server_t *tmp_server; 219 list_t *servers; 220 221 rr_alg = (*alg)->ilb_alg_data; 222 servers = &rr_alg->rr_servers; 223 while ((tmp_server = list_head(servers)) != NULL) { 224 list_remove(servers, tmp_server); 225 ILB_SERVER_REFRELE(tmp_server->server); 226 kmem_free(tmp_server, sizeof (rr_server_t)); 227 } 228 list_destroy(servers); 229 kmem_free(rr_alg, sizeof (rr_t)); 230 kmem_free(*alg, sizeof (ilb_alg_data_t)); 231 *alg = NULL; 232 } 233