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