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 */
46static void *nat_cur_cli;
47static 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 */
56void
57ilbd_show_nat_cleanup(void)
58{
59	nat_cur_cli = NULL;
60}
61
62void
63ilbd_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 */
76ilb_status_t
77ilbd_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
183end:
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 */
196ilb_status_t
197ilbd_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
292end:
293	free(kcmd);
294	return (ret);
295}
296