1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #include <sys/types.h> 28 #include <libilb.h> 29 #include <inet/ilb.h> 30 #include <stddef.h> 31 #include <stdlib.h> 32 #include <strings.h> 33 #include <errno.h> 34 #include <assert.h> 35 #include <macros.h> 36 #include "libilb_impl.h" 37 #include "ilbd.h" 38 39 /* 40 * We only allow one show nat/persist command running at any time. Note that 41 * there is no lock for this since ilbd is single threaded. And we only care 42 * about the pointer value of client, not its type. 43 * 44 * The following variables store the current client making the request. 45 */ 46 static void *nat_cur_cli; 47 static void *sticky_cur_cli; 48 49 /* Maximum number of NAT/sticky entries to request from kernel. */ 50 #define NUM_ENTRIES 500 51 52 /* 53 * Clear the current requesting client. This will allow a new client 54 * to make a request. 55 */ 56 void 57 ilbd_show_nat_cleanup(void) 58 { 59 nat_cur_cli = NULL; 60 } 61 62 void 63 ilbd_show_sticky_cleanup(void) 64 { 65 sticky_cur_cli = NULL; 66 } 67 68 /* 69 * To show the kernel NAT table. 70 * 71 * cli: the client pointer making the request. 72 * ic: the client request. 73 * rbuf: reply buffer to be filled in. 74 * rbufsz: reply buffer size. 75 */ 76 ilb_status_t 77 ilbd_show_nat(void *cli, const ilb_comm_t *ic, uint32_t *rbuf, size_t *rbufsz) 78 { 79 ilb_show_info_t *req_si = (ilb_show_info_t *)&ic->ic_data; 80 ilb_list_nat_cmd_t *kcmd; 81 boolean_t start; 82 size_t tmp_rbufsz, kbufsz; 83 uint32_t max_num; 84 ilb_status_t ret; 85 int i; 86 ilb_show_info_t *reply; 87 ilb_nat_info_t *nat_ret; 88 89 /* For new client request, start from the beginning of the table. */ 90 if (nat_cur_cli == NULL) { 91 nat_cur_cli = cli; 92 start = B_TRUE; 93 } else if (cli == nat_cur_cli) { 94 /* 95 * Another request from client. If the client does not 96 * want to continue, reset the current client and reply OK. 97 */ 98 if (ic->ic_flags & ILB_COMM_END) { 99 ilbd_show_nat_cleanup(); 100 ilbd_reply_ok(rbuf, rbufsz); 101 return (ILB_STATUS_OK); 102 } 103 start = B_FALSE; 104 } else { 105 /* A request is on-going, so reject a new client. */ 106 return (ILB_STATUS_INPROGRESS); 107 } 108 109 tmp_rbufsz = *rbufsz; 110 ilbd_reply_ok(rbuf, rbufsz); 111 reply = (ilb_show_info_t *)&((ilb_comm_t *)rbuf)->ic_data; 112 113 /* 114 * Calculate the max number of ilb_nat_info_t can be fitted in the 115 * reply. 116 */ 117 *rbufsz += sizeof (ilb_show_info_t *); 118 tmp_rbufsz -= *rbufsz; 119 max_num = tmp_rbufsz / sizeof (ilb_nat_info_t); 120 121 /* 122 * Calculate the exact number of entries we should request from kernel. 123 */ 124 max_num = min(req_si->sn_num, min(NUM_ENTRIES, max_num)); 125 126 kbufsz = max_num * sizeof (ilb_nat_entry_t) + 127 offsetof(ilb_list_nat_cmd_t, entries); 128 if ((kcmd = malloc(kbufsz)) == NULL) { 129 logdebug("ilbd_show_nat: malloc(cmd)"); 130 ilbd_reply_err(rbuf, rbufsz, ILB_STATUS_ENOMEM); 131 return (ILB_STATUS_ENOMEM); 132 } 133 134 kcmd->cmd = ILB_LIST_NAT_TABLE; 135 kcmd->flags = start ? ILB_LIST_BEGIN : ILB_LIST_CONT; 136 kcmd->num_nat = max_num; 137 if ((ret = do_ioctl(kcmd, kbufsz)) != ILB_STATUS_OK) { 138 logperror("ilbd_show_nat: ioctl(ILB_LIST_NAT_TABLE)"); 139 ilbd_reply_err(rbuf, rbufsz, ret); 140 free(kcmd); 141 return (ret); 142 } 143 144 reply->sn_num = kcmd->num_nat; 145 *rbufsz += reply->sn_num * sizeof (ilb_nat_info_t); 146 147 /* 148 * It is the end of table, let the client know. And the transaction 149 * is done. 150 */ 151 if (kcmd->flags & ILB_LIST_END) { 152 nat_cur_cli = NULL; 153 } else { 154 /* 155 * ilbd_reply_ok() sets ic_flags to ILB_COMM_END by default. 156 * Need to clear it here. 157 */ 158 ((ilb_comm_t *)rbuf)->ic_flags = 0; 159 } 160 161 nat_ret = (ilb_nat_info_t *)&reply->sn_data; 162 163 for (i = 0; i < kcmd->num_nat; i++) { 164 ilb_nat_entry_t *nat; 165 166 nat = &kcmd->entries[i]; 167 168 nat_ret->nat_proto = nat->proto; 169 170 nat_ret->nat_in_local = nat->in_local; 171 nat_ret->nat_in_global = nat->in_global; 172 nat_ret->nat_out_local = nat->out_local; 173 nat_ret->nat_out_global = nat->out_global; 174 175 nat_ret->nat_in_local_port = nat->in_local_port; 176 nat_ret->nat_in_global_port = nat->in_global_port; 177 nat_ret->nat_out_local_port = nat->out_local_port; 178 nat_ret->nat_out_global_port = nat->out_global_port; 179 180 nat_ret++; 181 } 182 183 free(kcmd); 184 return (ret); 185 } 186 187 /* 188 * To show the kernel sticky table. 189 * 190 * cli: the client pointer making the request. 191 * req_si: information about the show-persist request. 192 * rbuf: reply buffer to be filled in. 193 * rbufsz: reply buffer size. 194 */ 195 ilb_status_t 196 ilbd_show_sticky(void *cli, const ilb_comm_t *ic, uint32_t *rbuf, 197 size_t *rbufsz) 198 { 199 ilb_show_info_t *req_si = (ilb_show_info_t *)&ic->ic_data; 200 ilb_list_sticky_cmd_t *kcmd; 201 boolean_t start; 202 size_t tmp_rbufsz, kbufsz; 203 uint32_t max_num; 204 ilb_status_t ret; 205 int i; 206 ilb_show_info_t *reply; 207 ilb_persist_info_t *st_ret; 208 209 /* For new client request, start from the beginning of the table. */ 210 if (sticky_cur_cli == NULL) { 211 sticky_cur_cli = cli; 212 start = B_TRUE; 213 } else if (cli == sticky_cur_cli) { 214 /* 215 * Another request from client. If the client does not 216 * want to continue, reset the current client and reply OK. 217 */ 218 if (ic->ic_flags & ILB_COMM_END) { 219 ilbd_show_sticky_cleanup(); 220 ilbd_reply_ok(rbuf, rbufsz); 221 return (ILB_STATUS_OK); 222 } 223 start = B_FALSE; 224 } else { 225 /* A request is on-going, so reject a new client. */ 226 return (ILB_STATUS_INPROGRESS); 227 } 228 229 tmp_rbufsz = *rbufsz; 230 ilbd_reply_ok(rbuf, rbufsz); 231 reply = (ilb_show_info_t *)&((ilb_comm_t *)rbuf)->ic_data; 232 233 /* 234 * Calculate the max number of ilb_persist_info_t can be fitted in the 235 * reply. 236 */ 237 *rbufsz += sizeof (ilb_show_info_t *); 238 tmp_rbufsz -= *rbufsz; 239 max_num = tmp_rbufsz / sizeof (ilb_persist_info_t); 240 241 /* 242 * Calculate the exact number of entries we should request from kernel. 243 */ 244 max_num = min(req_si->sn_num, min(NUM_ENTRIES, max_num)); 245 246 kbufsz = max_num * sizeof (ilb_sticky_entry_t) + 247 offsetof(ilb_list_sticky_cmd_t, entries); 248 if ((kcmd = malloc(kbufsz)) == NULL) { 249 logdebug("ilbd_show_nat: malloc(cmd)"); 250 ilbd_reply_err(rbuf, rbufsz, ILB_STATUS_ENOMEM); 251 return (ILB_STATUS_ENOMEM); 252 } 253 254 kcmd->cmd = ILB_LIST_STICKY_TABLE; 255 kcmd->flags = start ? ILB_LIST_BEGIN : ILB_LIST_CONT; 256 kcmd->num_sticky = max_num; 257 if ((ret = do_ioctl(kcmd, kbufsz)) != ILB_STATUS_OK) { 258 logperror("ilbd_show_nat: ioctl(ILB_LIST_STICKY_TABLE)"); 259 ilbd_reply_err(rbuf, rbufsz, ret); 260 free(kcmd); 261 return (ret); 262 } 263 264 reply->sn_num = kcmd->num_sticky; 265 *rbufsz += reply->sn_num * sizeof (ilb_persist_info_t); 266 267 if (kcmd->flags & ILB_LIST_END) { 268 sticky_cur_cli = NULL; 269 } else { 270 /* 271 * ilbd_reply_ok() sets ic_flags to ILB_COMM_END by default. 272 * Need to clear it here. 273 */ 274 ((ilb_comm_t *)rbuf)->ic_flags = 0; 275 } 276 277 st_ret = (ilb_persist_info_t *)&reply->sn_data; 278 279 for (i = 0; i < kcmd->num_sticky; i++) { 280 ilb_sticky_entry_t *st; 281 282 st = &kcmd->entries[i]; 283 284 (void) strlcpy(st_ret->persist_rule_name, st->rule_name, 285 ILB_NAMESZ); 286 st_ret->persist_req_addr = st->req_addr; 287 st_ret->persist_srv_addr = st->srv_addr; 288 st_ret++; 289 } 290 291 free(kcmd); 292 return (ret); 293 } 294