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
ilbd_show_nat_cleanup(void)57 ilbd_show_nat_cleanup(void)
58 {
59 nat_cur_cli = NULL;
60 }
61
62 void
ilbd_show_sticky_cleanup(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
ilbd_show_nat(void * cli,const ilb_comm_t * ic,uint32_t * rbuf,size_t * rbufsz)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
ilbd_show_sticky(void * cli,const ilb_comm_t * ic,uint32_t * rbuf,size_t * rbufsz)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