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 <stdlib.h>
28#include <strings.h>
29#include <sys/types.h>
30#include <sys/socket.h>
31#include <netinet/in.h>
32#include <stddef.h>
33#include <libilb_impl.h>
34#include <libilb.h>
35
36static ilb_status_t
37i_ilb_addrem_sg(ilb_handle_t h, const char *sgname, ilbd_cmd_t cmd)
38{
39	ilb_status_t	rc;
40	ilb_comm_t	*ic;
41	size_t		ic_sz;
42
43	if (h == ILB_INVALID_HANDLE || sgname == NULL || *sgname == '\0')
44		return (ILB_STATUS_EINVAL);
45
46	if (strlen(sgname) > ILB_SGNAME_SZ - 1)
47		return (ILB_STATUS_NAMETOOLONG);
48
49	if ((ic = i_ilb_alloc_req(cmd, &ic_sz)) == NULL)
50		return (ILB_STATUS_ENOMEM);
51
52	(void) strlcpy((char *)&ic->ic_data, sgname, sizeof (ilbd_name_t));
53
54	rc = i_ilb_do_comm(h, ic, ic_sz, ic, &ic_sz);
55	if (rc != ILB_STATUS_OK)
56		goto out;
57
58	if (ic->ic_cmd != ILBD_CMD_OK)
59		rc = *(ilb_status_t *)&ic->ic_data;
60out:
61	free(ic);
62	return (rc);
63}
64
65ilb_status_t
66ilb_destroy_servergroup(ilb_handle_t h, const char *sgname)
67{
68	return (i_ilb_addrem_sg(h, sgname, ILBD_DESTROY_SERVERGROUP));
69}
70
71ilb_status_t
72ilb_create_servergroup(ilb_handle_t h, const char *sgname)
73{
74	return (i_ilb_addrem_sg(h, sgname, ILBD_CREATE_SERVERGROUP));
75}
76
77static ilb_status_t
78i_ilb_addrem_server_to_group(ilb_handle_t h, const char *sgname,
79    ilb_server_data_t *srv, ilbd_cmd_t cmd)
80{
81	ilb_status_t		rc = ILB_STATUS_OK;
82	ilb_sg_info_t		*sg;
83	ilb_sg_srv_t		*sgs;
84	in_port_t		h_maxport, h_minport;
85	ilb_comm_t		*ic;
86	size_t			ic_sz;
87
88	if (h == ILB_INVALID_HANDLE || sgname == NULL ||
89	    *sgname == '\0' || srv == NULL)
90		return (ILB_STATUS_EINVAL);
91
92	if (strlen(sgname) > ILB_SGNAME_SZ - 1)
93		return (ILB_STATUS_NAMETOOLONG);
94
95	/* now all the checks have passed, we can pass on the goods */
96	if ((ic = i_ilb_alloc_req(cmd, &ic_sz)) == NULL)
97		return (ILB_STATUS_ENOMEM);
98
99	sg = (ilb_sg_info_t *)&ic->ic_data;
100	sg->sg_srvcount = 1;
101	(void) strlcpy(sg->sg_name, sgname, sizeof (sg->sg_name));
102
103	sgs = &sg->sg_servers[0];
104
105	IP_COPY_CLI_2_IMPL(&srv->sd_addr, &sgs->sgs_addr);
106	h_minport = ntohs(srv->sd_minport);
107	h_maxport = ntohs(srv->sd_maxport);
108	sgs->sgs_minport = srv->sd_minport;
109	if (h_minport != 0 && h_maxport < h_minport)
110		sgs->sgs_maxport = srv->sd_minport;
111	else
112		sgs->sgs_maxport = srv->sd_maxport;
113
114	sgs->sgs_flags = srv->sd_flags;
115	if (srv->sd_srvID[0] == ILB_SRVID_PREFIX)
116		(void) strlcpy(sgs->sgs_srvID, srv->sd_srvID,
117		    sizeof (sgs->sgs_srvID));
118
119	rc = i_ilb_do_comm(h, ic, ic_sz, ic, &ic_sz);
120	if (rc != ILB_STATUS_OK)
121		goto out;
122
123	if (ic->ic_cmd != ILBD_CMD_OK)
124		rc = *(ilb_status_t *)&ic->ic_data;
125
126out:
127	free(ic);
128	return (rc);
129}
130
131ilb_status_t
132ilb_add_server_to_group(ilb_handle_t h, const char *sgname,
133    ilb_server_data_t *srv)
134{
135	return (i_ilb_addrem_server_to_group(h, sgname, srv,
136	    ILBD_ADD_SERVER_TO_GROUP));
137}
138
139ilb_status_t
140ilb_rem_server_from_group(ilb_handle_t h, const char *sgname,
141    ilb_server_data_t *srv)
142{
143	return (i_ilb_addrem_server_to_group(h, sgname, srv,
144	    ILBD_REM_SERVER_FROM_GROUP));
145}
146
147static ilb_status_t
148i_ilb_retrieve_sg_names(ilb_handle_t h, ilb_comm_t **rbuf, size_t *rbufsz)
149{
150	ilb_status_t	rc;
151	ilb_comm_t	ic, *tmp_rbuf;
152
153	*rbufsz = ILBD_MSG_SIZE;
154	if ((tmp_rbuf = malloc(*rbufsz)) == NULL)
155		return (ILB_STATUS_ENOMEM);
156
157	ic.ic_cmd = ILBD_RETRIEVE_SG_NAMES;
158	rc = i_ilb_do_comm(h, &ic, sizeof (ic), tmp_rbuf, rbufsz);
159	if (rc != ILB_STATUS_OK)
160		goto out;
161
162	if (tmp_rbuf->ic_cmd == ILBD_CMD_OK) {
163		*rbuf = tmp_rbuf;
164		return (rc);
165	}
166	rc = *(ilb_status_t *)&tmp_rbuf->ic_data;
167out:
168	free(tmp_rbuf);
169	*rbuf = NULL;
170	return (rc);
171}
172
173static ilb_status_t
174i_ilb_retrieve_sg_hosts(ilb_handle_t h, const char *sgname, ilb_comm_t **rbuf,
175    size_t *rbufsz)
176{
177	ilb_status_t	rc;
178	ilb_comm_t	*ic, *tmp_rbuf;
179	size_t		ic_sz;
180
181	if ((ic = i_ilb_alloc_req(ILBD_RETRIEVE_SG_HOSTS, &ic_sz)) == NULL)
182		return (ILB_STATUS_ENOMEM);
183	*rbufsz = ILBD_MSG_SIZE;
184	if ((tmp_rbuf = malloc(*rbufsz)) == NULL) {
185		free(ic);
186		*rbuf = NULL;
187		return (ILB_STATUS_ENOMEM);
188	}
189
190	(void) strlcpy((char *)&ic->ic_data, sgname, sizeof (ilbd_name_t));
191	rc = i_ilb_do_comm(h, ic, ic_sz, tmp_rbuf, rbufsz);
192	if (rc != ILB_STATUS_OK)
193		goto out;
194
195	if (tmp_rbuf->ic_cmd == ILBD_CMD_OK) {
196		*rbuf = tmp_rbuf;
197		free(ic);
198		return (rc);
199	}
200	rc = *(ilb_status_t *)&tmp_rbuf->ic_data;
201out:
202	free(ic);
203	free(tmp_rbuf);
204	*rbuf = NULL;
205	return (rc);
206}
207
208typedef enum {
209	walk_servers,
210	walk_sg
211} sgwalk_t;
212
213/*
214 * "walks" one sg (retrieves data) and depending on "walktype" argument
215 * call servergroup function once per sg or server function once
216 * for every server. in both cases, the argument "f" is cast to
217 * be the proper function pointer type
218 */
219static ilb_status_t
220i_ilb_walk_one_sg(ilb_handle_t h, void *f, const char *sgname, void *arg,
221    sgwalk_t walktype)
222{
223	ilb_status_t	rc = ILB_STATUS_OK;
224	ilb_sg_info_t	*sg_info;
225	ilb_sg_srv_t	*srv;
226	int		i;
227	ilb_comm_t	*rbuf;
228	size_t		rbufsz;
229
230	rc = i_ilb_retrieve_sg_hosts(h, sgname, &rbuf, &rbufsz);
231	if (rc != ILB_STATUS_OK)
232		return (rc);
233	sg_info = (ilb_sg_info_t *)&rbuf->ic_data;
234
235	if (walktype == walk_sg) {
236		sg_walkerfunc_t	sg_func = (sg_walkerfunc_t)f;
237		ilb_sg_data_t	sgd;
238
239		(void) strlcpy(sgd.sgd_name, sg_info->sg_name,
240		    sizeof (sgd.sgd_name));
241		sgd.sgd_srvcount = sg_info->sg_srvcount;
242		sgd.sgd_flags = sg_info->sg_flags;
243		rc = sg_func(h, &sgd, arg);
244		goto out;
245	}
246
247	for (i = 0; i < sg_info->sg_srvcount; i++) {
248		srv_walkerfunc_t srv_func = (srv_walkerfunc_t)f;
249		ilb_server_data_t	 sd;
250
251		srv = &sg_info->sg_servers[i];
252		IP_COPY_IMPL_2_CLI(&srv->sgs_addr, &sd.sd_addr);
253		sd.sd_minport = srv->sgs_minport;
254		sd.sd_maxport = srv->sgs_maxport;
255		sd.sd_flags = srv->sgs_flags;
256		(void) strlcpy(sd.sd_srvID, srv->sgs_srvID,
257		    sizeof (sd.sd_srvID));
258
259		rc = srv_func(h, &sd, sg_info->sg_name, arg);
260		if (rc != ILB_STATUS_OK)
261			break;
262	}
263
264out:
265	free(rbuf);
266	return (rc);
267}
268
269/*
270 * wrapper function for i_walk_one_sg; if necessary, gets list of
271 * SG names and calles i_walk_one_sg with every name
272 */
273static ilb_status_t
274i_walk_sgs(ilb_handle_t h, void *f, const char *sgname,
275    void *arg, sgwalk_t walktype)
276{
277	ilb_status_t	rc;
278	ilbd_namelist_t	*sgl;
279	ilb_comm_t	*rbuf;
280	size_t		rbufsz;
281	int		i;
282
283	if (sgname != NULL) {
284		rc = i_ilb_walk_one_sg(h, f, sgname, arg, walktype);
285		return (rc);
286	}
287
288	rc = i_ilb_retrieve_sg_names(h, &rbuf, &rbufsz);
289	if (rc != ILB_STATUS_OK)
290		return (rc);
291	sgl = (ilbd_namelist_t *)&rbuf->ic_data;
292
293	for (i = 0; i < sgl->ilbl_count; i++) {
294		rc = i_ilb_walk_one_sg(h, f, sgl->ilbl_name[i], arg, walktype);
295		/*
296		 * The server group may have been removed by another
297		 * process, just continue.
298		 */
299		if (rc == ILB_STATUS_SGUNAVAIL) {
300			rc = ILB_STATUS_OK;
301			continue;
302		}
303		if (rc != ILB_STATUS_OK)
304			break;
305	}
306	free(rbuf);
307	return (rc);
308}
309
310ilb_status_t
311ilb_walk_servergroups(ilb_handle_t h, sg_walkerfunc_t f, const char *sgname,
312    void *arg)
313{
314	return (i_walk_sgs(h, (void *)f, sgname, arg, walk_sg));
315}
316
317ilb_status_t
318ilb_walk_servers(ilb_handle_t h, srv_walkerfunc_t f, const char *sgname,
319    void *arg)
320{
321	return (i_walk_sgs(h, (void *)f, sgname, arg, walk_servers));
322}
323
324static ilb_status_t
325ilb_Xable_server(ilb_handle_t h, ilb_server_data_t *srv, void *reserved,
326    ilbd_cmd_t cmd)
327{
328	ilb_status_t	rc;
329	ilb_sg_info_t	*sg_info;
330	ilb_sg_srv_t	*sgs;
331	in_port_t	h_maxport, h_minport;
332	ilb_comm_t	*ic;
333	size_t		ic_sz;
334
335	if (h == NULL)
336		return (ILB_STATUS_EINVAL);
337
338	/*
339	 * In this implementation, this needs to be NULL, so
340	 * there's no ugly surprises with old apps once we attach
341	 * meaning to this parameter.
342	 */
343	if (reserved != NULL)
344		return (ILB_STATUS_EINVAL);
345
346	/* now all the checks have passed, we can pass on the goods */
347	if ((ic = i_ilb_alloc_req(cmd, &ic_sz)) == NULL)
348		return (ILB_STATUS_ENOMEM);
349
350	sg_info = (ilb_sg_info_t *)&ic->ic_data;
351	sg_info->sg_srvcount = 1;
352
353	sgs = &sg_info->sg_servers[0];
354
355	/* make sure min_port <= max_port; comparison in host byte order! */
356	h_maxport = ntohs(srv->sd_maxport);
357	h_minport = ntohs(srv->sd_minport);
358	if (h_maxport != 0 && h_maxport < h_minport)
359		sgs->sgs_maxport = sgs->sgs_minport;
360	else
361		sgs->sgs_maxport = srv->sd_maxport;
362	sgs->sgs_minport = srv->sd_minport;
363
364	sgs->sgs_flags = srv->sd_flags;
365	(void) strlcpy(sgs->sgs_srvID, srv->sd_srvID, sizeof (sgs->sgs_srvID));
366	IP_COPY_CLI_2_IMPL(&srv->sd_addr, &sgs->sgs_addr);
367
368	rc = i_ilb_do_comm(h, ic, ic_sz, ic, &ic_sz);
369	if (rc != ILB_STATUS_OK)
370		goto out;
371
372	if (ic->ic_cmd != ILBD_CMD_OK)
373		rc = *(ilb_status_t *)&ic->ic_data;
374out:
375	free(ic);
376	return (rc);
377}
378
379ilb_status_t
380ilb_enable_server(ilb_handle_t h, ilb_server_data_t *srv, void *reserved)
381{
382	return (ilb_Xable_server(h, srv, reserved, ILBD_ENABLE_SERVER));
383}
384
385ilb_status_t
386ilb_disable_server(ilb_handle_t h, ilb_server_data_t *srv, void *reserved)
387{
388	return (ilb_Xable_server(h, srv, reserved, ILBD_DISABLE_SERVER));
389}
390
391static ilb_status_t
392i_ilb_fillin_srvdata(ilb_handle_t h, ilb_server_data_t *srv, const char *sgname,
393    ilbd_cmd_t cmd)
394{
395	ilb_status_t	rc;
396	ilb_sg_info_t	*sg_info;
397	ilb_sg_srv_t	*sgs;
398	ilb_comm_t	*ic;
399	size_t		ic_sz;
400	ilb_comm_t	*rbuf;
401	size_t		rbufsz;
402
403	if (h == ILB_INVALID_HANDLE || sgname == NULL ||
404	    *sgname == '\0' || srv == NULL)
405		return (ILB_STATUS_EINVAL);
406
407	if (cmd == ILBD_SRV_ID2ADDR && srv->sd_srvID[0] == '\0')
408		return (ILB_STATUS_EINVAL);
409	if (cmd == ILBD_SRV_ADDR2ID && !IS_AF_VALID(srv->sd_addr.ia_af))
410		return (ILB_STATUS_EINVAL);
411
412	if ((ic = i_ilb_alloc_req(cmd, &ic_sz)) == NULL)
413		return (ILB_STATUS_ENOMEM);
414	rbufsz = sizeof (ilb_comm_t) + sizeof (ilb_sg_srv_t);
415	if ((rbuf = malloc(rbufsz)) == NULL) {
416		free(ic);
417		return (ILB_STATUS_ENOMEM);
418	}
419
420	sg_info = (ilb_sg_info_t *)&ic->ic_data;
421	sg_info->sg_srvcount = 1;
422	(void) strlcpy(sg_info->sg_name, sgname, sizeof (sg_info->sg_name));
423
424	sgs = &sg_info->sg_servers[0];
425
426	if (cmd == ILBD_SRV_ID2ADDR) {
427		(void) strlcpy(sgs->sgs_srvID, srv->sd_srvID,
428		    sizeof (sgs->sgs_srvID));
429	} else {
430		IP_COPY_CLI_2_IMPL(&srv->sd_addr, &sgs->sgs_addr);
431	}
432
433	rc = i_ilb_do_comm(h, ic, ic_sz, rbuf, &rbufsz);
434	if (rc != ILB_STATUS_OK)
435		goto out;
436
437	if (rbuf->ic_cmd == ILBD_CMD_OK) {
438		sgs = (ilb_sg_srv_t *)&rbuf->ic_data;
439		if (cmd == ILBD_SRV_ID2ADDR) {
440			IP_COPY_IMPL_2_CLI(&sgs->sgs_addr, &srv->sd_addr);
441		} else {
442			(void) strlcpy(srv->sd_srvID, sgs->sgs_srvID,
443			    sizeof (sgs->sgs_srvID));
444		}
445		return (rc);
446	}
447
448	rc = *(ilb_status_t *)&rbuf->ic_data;
449out:
450	free(ic);
451	return (rc);
452}
453
454ilb_status_t
455ilb_srvID_to_address(ilb_handle_t h, ilb_server_data_t *srv, const char *sgname)
456{
457	return (i_ilb_fillin_srvdata(h, srv, sgname, ILBD_SRV_ID2ADDR));
458
459}
460
461ilb_status_t
462ilb_address_to_srvID(ilb_handle_t h, ilb_server_data_t *srv, const char *sgname)
463{
464	return (i_ilb_fillin_srvdata(h, srv, sgname, ILBD_SRV_ADDR2ID));
465}
466