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 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
ilbd_show_sticky(void * cli,const ilb_comm_t * ic,uint32_t * rbuf,size_t * rbufsz)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