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 end: 184 free(kcmd); 185 return (ret); 186 } 187 188 /* 189 * To show the kernel sticky table. 190 * 191 * cli: the client pointer making the request. 192 * req_si: information about the show-persist request. 193 * rbuf: reply buffer to be filled in. 194 * rbufsz: reply buffer size. 195 */ 196 ilb_status_t 197 ilbd_show_sticky(void *cli, const ilb_comm_t *ic, uint32_t *rbuf, 198 size_t *rbufsz) 199 { 200 ilb_show_info_t *req_si = (ilb_show_info_t *)&ic->ic_data; 201 ilb_list_sticky_cmd_t *kcmd; 202 boolean_t start; 203 size_t tmp_rbufsz, kbufsz; 204 uint32_t max_num; 205 ilb_status_t ret; 206 int i; 207 ilb_show_info_t *reply; 208 ilb_persist_info_t *st_ret; 209 210 /* For new client request, start from the beginning of the table. */ 211 if (sticky_cur_cli == NULL) { 212 sticky_cur_cli = cli; 213 start = B_TRUE; 214 } else if (cli == sticky_cur_cli) { 215 /* 216 * Another request from client. If the client does not 217 * want to continue, reset the current client and reply OK. 218 */ 219 if (ic->ic_flags & ILB_COMM_END) { 220 ilbd_show_sticky_cleanup(); 221 ilbd_reply_ok(rbuf, rbufsz); 222 return (ILB_STATUS_OK); 223 } 224 start = B_FALSE; 225 } else { 226 /* A request is on-going, so reject a new client. */ 227 return (ILB_STATUS_INPROGRESS); 228 } 229 230 tmp_rbufsz = *rbufsz; 231 ilbd_reply_ok(rbuf, rbufsz); 232 reply = (ilb_show_info_t *)&((ilb_comm_t *)rbuf)->ic_data; 233 234 /* 235 * Calculate the max number of ilb_persist_info_t can be fitted in the 236 * reply. 237 */ 238 *rbufsz += sizeof (ilb_show_info_t *); 239 tmp_rbufsz -= *rbufsz; 240 max_num = tmp_rbufsz / sizeof (ilb_persist_info_t); 241 242 /* 243 * Calculate the exact number of entries we should request from kernel. 244 */ 245 max_num = min(req_si->sn_num, min(NUM_ENTRIES, max_num)); 246 247 kbufsz = max_num * sizeof (ilb_sticky_entry_t) + 248 offsetof(ilb_list_sticky_cmd_t, entries); 249 if ((kcmd = malloc(kbufsz)) == NULL) { 250 logdebug("ilbd_show_nat: malloc(cmd)"); 251 ilbd_reply_err(rbuf, rbufsz, ILB_STATUS_ENOMEM); 252 return (ILB_STATUS_ENOMEM); 253 } 254 255 kcmd->cmd = ILB_LIST_STICKY_TABLE; 256 kcmd->flags = start ? ILB_LIST_BEGIN : ILB_LIST_CONT; 257 kcmd->num_sticky = max_num; 258 if ((ret = do_ioctl(kcmd, kbufsz)) != ILB_STATUS_OK) { 259 logperror("ilbd_show_nat: ioctl(ILB_LIST_STICKY_TABLE)"); 260 ilbd_reply_err(rbuf, rbufsz, ret); 261 free(kcmd); 262 return (ret); 263 } 264 265 reply->sn_num = kcmd->num_sticky; 266 *rbufsz += reply->sn_num * sizeof (ilb_persist_info_t); 267 268 if (kcmd->flags & ILB_LIST_END) { 269 sticky_cur_cli = NULL; 270 } else { 271 /* 272 * ilbd_reply_ok() sets ic_flags to ILB_COMM_END by default. 273 * Need to clear it here. 274 */ 275 ((ilb_comm_t *)rbuf)->ic_flags = 0; 276 } 277 278 st_ret = (ilb_persist_info_t *)&reply->sn_data; 279 280 for (i = 0; i < kcmd->num_sticky; i++) { 281 ilb_sticky_entry_t *st; 282 283 st = &kcmd->entries[i]; 284 285 (void) strlcpy(st_ret->persist_rule_name, st->rule_name, 286 ILB_NAMESZ); 287 st_ret->persist_req_addr = st->req_addr; 288 st_ret->persist_srv_addr = st->srv_addr; 289 st_ret++; 290 } 291 292 end: 293 free(kcmd); 294 return (ret); 295 } 296