ilbd_rules.c revision 10946:324bab2b3370
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 <stddef.h>
30#include <unistd.h>
31#include <sys/types.h>
32#include <sys/socket.h>
33#include <netinet/in.h>
34#include <arpa/inet.h>
35#include <sys/list.h>
36#include <net/if.h>
37#include <assert.h>
38#include <errno.h>
39#include <libintl.h>
40#include <libilb.h>
41#include <inet/ilb.h>
42#include "libilb_impl.h"
43#include "ilbd.h"
44
45/* until we all use AF_* macros ... */
46#define	AF_2_IPPROTO(_af)	(_af == AF_INET)?IPPROTO_IP:IPPROTO_IPV6
47#define	IPPROTO_2_AF(_i)	(_i == IPPROTO_IP)?AF_INET:AF_INET6
48
49static ilb_status_t ilbd_disable_one_rule(ilbd_rule_t *, boolean_t);
50static uint32_t i_flags_d2k(int);
51
52#define	ILB_SGSRV_2_KSRV(s, k)			\
53	(k)->addr  = (s)->sgs_addr;		\
54	(k)->min_port = (s)->sgs_minport;	\
55	(k)->max_port = (s)->sgs_maxport;	\
56	(k)->flags = i_flags_d2k((s)->sgs_flags);	\
57	(k)->err = 0;				\
58	(void) strlcpy((k)->name, (s)->sgs_srvID, sizeof ((k)->name))
59
60list_t		ilbd_rule_hlist;
61
62static ilb_algo_t
63algo_impl2lib(ilb_algo_impl_t a)
64{
65	switch (a) {
66	case ILB_ALG_IMPL_ROUNDROBIN:
67		return (ILB_ALG_ROUNDROBIN);
68	case ILB_ALG_IMPL_HASH_IP:
69		return (ILB_ALG_HASH_IP);
70	case ILB_ALG_IMPL_HASH_IP_SPORT:
71		return (ILB_ALG_HASH_IP_SPORT);
72	case ILB_ALG_IMPL_HASH_IP_VIP:
73		return (ILB_ALG_HASH_IP_VIP);
74	}
75	return (0);
76}
77
78static ilb_topo_t
79topo_impl2lib(ilb_topo_impl_t t)
80{
81	switch (t) {
82	case ILB_TOPO_IMPL_DSR:
83		return (ILB_TOPO_DSR);
84	case ILB_TOPO_IMPL_NAT:
85		return (ILB_TOPO_NAT);
86	case ILB_TOPO_IMPL_HALF_NAT:
87		return (ILB_TOPO_HALF_NAT);
88	}
89	return (0);
90}
91
92ilb_algo_impl_t
93algo_lib2impl(ilb_algo_t a)
94{
95	switch (a) {
96	case ILB_ALG_ROUNDROBIN:
97		return (ILB_ALG_IMPL_ROUNDROBIN);
98	case ILB_ALG_HASH_IP:
99		return (ILB_ALG_IMPL_HASH_IP);
100	case ILB_ALG_HASH_IP_SPORT:
101		return (ILB_ALG_IMPL_HASH_IP_SPORT);
102	case ILB_ALG_HASH_IP_VIP:
103		return (ILB_ALG_IMPL_HASH_IP_VIP);
104	}
105	return (0);
106}
107
108ilb_topo_impl_t
109topo_lib2impl(ilb_topo_t t)
110{
111	switch (t) {
112	case ILB_TOPO_DSR:
113		return (ILB_TOPO_IMPL_DSR);
114	case ILB_TOPO_NAT:
115		return (ILB_TOPO_IMPL_NAT);
116	case ILB_TOPO_HALF_NAT:
117		return (ILB_TOPO_IMPL_HALF_NAT);
118	}
119	return (0);
120}
121
122/*
123 * Walk the list of rules and check if its safe to add the
124 * the server to the rule (this is a list of rules hanging
125 * off of a server group)
126 */
127ilb_status_t
128i_check_srv2rules(list_t *rlist, ilb_sg_srv_t *srv)
129{
130	ilb_status_t	rc = ILB_STATUS_OK;
131	ilbd_rule_t	*rl;
132	int		server_portrange, rule_portrange;
133	int		srv_minport, srv_maxport;
134	int		r_minport, r_maxport;
135
136	if (srv == NULL)
137		return (ILB_STATUS_OK);
138
139	srv_minport = ntohs(srv->sgs_minport);
140	srv_maxport = ntohs(srv->sgs_maxport);
141
142	for (rl = list_head(rlist); rl != NULL; rl = list_next(rlist, rl)) {
143		r_minport = ntohs(rl->irl_minport);
144		r_maxport = ntohs(rl->irl_maxport);
145
146		if ((srv_minport != 0) && (srv_minport == srv_maxport)) {
147			/* server has single port */
148			if (rl->irl_topo == ILB_TOPO_DSR) {
149				/*
150				 * either we have a DSR rule with a port
151				 * range, or both server and rule
152				 * have single ports but their values
153				 * don't match - this is incompatible
154				 */
155				if (r_maxport > r_minport) {
156					rc = ILB_STATUS_INVAL_SRVR;
157					break;
158				} else if (srv_minport != r_minport) {
159					rc = ILB_STATUS_BADPORT;
160					break;
161				}
162			}
163			if (rl->irl_hcpflag == ILB_HCI_PROBE_FIX &&
164			    rl->irl_hcport != srv_minport) {
165				rc = ILB_STATUS_BADPORT;
166				break;
167			}
168		} else if (srv_maxport > srv_minport) {
169			/* server has a port range */
170			if ((rl->irl_topo == ILB_TOPO_DSR) &&
171			    (r_maxport > r_minport)) {
172				if ((r_minport != srv_minport) ||
173				    (r_maxport != srv_maxport)) {
174					/*
175					 * we have a DSR rule with a port range
176					 * and its min and max port values
177					 * does not meet that of server's
178					 * - this is incompatible
179					 */
180					rc = ILB_STATUS_BADPORT;
181					break;
182				}
183			} else if ((rl->irl_topo == ILB_TOPO_DSR) &&
184			    (r_maxport == r_minport)) {
185					/*
186					 * we have a DSR rule with a single
187					 * port and a server with a port range
188					 * - this is incompatible
189					 */
190					rc = ILB_STATUS_INVAL_SRVR;
191					break;
192			} else if (((rl->irl_topo == ILB_TOPO_NAT) ||
193			    (rl->irl_topo == ILB_TOPO_HALF_NAT)) &&
194			    (r_maxport > r_minport)) {
195				server_portrange = srv_maxport - srv_minport;
196				rule_portrange = r_maxport - r_minport;
197				if (rule_portrange != server_portrange) {
198					/*
199					 * we have a NAT/Half-NAT rule with
200					 * a port range and server with a port
201					 * range and there is a mismatch in the
202					 * sizes of the port ranges - this is
203					 * incompatible
204					 */
205					rc = ILB_STATUS_INVAL_SRVR;
206					break;
207				}
208			}
209			if (rl->irl_hcpflag == ILB_HCI_PROBE_FIX &&
210			    (rl->irl_hcport > srv_maxport ||
211			    rl->irl_hcport < srv_minport)) {
212				rc = ILB_STATUS_BADPORT;
213				break;
214			}
215		}
216	}
217
218	return (rc);
219}
220
221void
222i_setup_rule_hlist(void)
223{
224	list_create(&ilbd_rule_hlist, sizeof (ilbd_rule_t),
225	    offsetof(ilbd_rule_t, irl_link));
226}
227
228ilb_status_t
229i_ilbd_save_rule(ilbd_rule_t *irl, ilbd_scf_cmd_t scf_cmd)
230{
231	boolean_t enable = irl->irl_flags & ILB_FLAGS_RULE_ENABLED;
232
233	switch (scf_cmd) {
234	case ILBD_SCF_CREATE:
235		return (ilbd_create_pg(ILBD_SCF_RULE, (void *)irl));
236	case ILBD_SCF_DESTROY:
237		return (ilbd_destroy_pg(ILBD_SCF_RULE, irl->irl_name));
238	case ILBD_SCF_ENABLE_DISABLE:
239		return (ilbd_change_prop(ILBD_SCF_RULE, irl->irl_name,
240		    "status", &enable));
241	default:
242		logdebug("i_ilbd_save_rule: invalid scf cmd %d", scf_cmd);
243		return (ILB_STATUS_INVAL_CMD);
244	}
245}
246
247/*
248 * allocate a new daemon-specific rule from the "template" passed
249 * in in *r
250 */
251static ilbd_rule_t *
252i_alloc_ilbd_rule(ilb_rule_info_t *r)
253{
254	ilbd_rule_t	*rl;
255
256	rl = calloc(sizeof (*rl), 1);
257	if (rl != NULL && r != NULL)
258		bcopy(r, &rl->irl_info, sizeof (*r));
259
260	return (rl);
261}
262
263static ilbd_rule_t *
264i_find_rule_byname(const char *name)
265{
266	ilbd_rule_t	*rl;
267
268	/* find position of rule in list */
269	rl = list_head(&ilbd_rule_hlist);
270	while (rl != NULL &&
271	    strncmp(rl->irl_name, name, sizeof (rl->irl_name)) != 0) {
272		rl = list_next(&ilbd_rule_hlist, rl);
273	}
274
275	return (rl);
276}
277
278/*
279 * get exactly one rule (named in rl->irl_name) data from kernel
280 */
281static ilb_status_t
282ilb_get_krule(ilb_rule_info_t *rl)
283{
284	ilb_status_t	rc;
285	ilb_rule_cmd_t	kcmd;
286
287	kcmd.cmd = ILB_LIST_RULE;
288	(void) strlcpy(kcmd.name, rl->rl_name, sizeof (kcmd.name));
289	kcmd.flags = 0;
290
291	rc = do_ioctl(&kcmd, 0);
292	if (rc != ILB_STATUS_OK)
293		return (rc);
294
295	rl->rl_flags = kcmd.flags;
296	rl->rl_ipversion = IPPROTO_2_AF(kcmd.ip_ver);
297	rl->rl_vip = kcmd.vip;
298	rl->rl_proto = kcmd.proto;
299	rl->rl_minport = kcmd.min_port;
300	rl->rl_maxport = kcmd.max_port;
301	rl->rl_algo = algo_impl2lib(kcmd.algo);
302	rl->rl_topo = topo_impl2lib(kcmd.topo);
303	rl->rl_stickymask = kcmd.sticky_mask;
304	rl->rl_nat_src_start = kcmd.nat_src_start;
305	rl->rl_nat_src_end = kcmd.nat_src_end;
306	(void) strlcpy(rl->rl_name, kcmd.name, sizeof (rl->rl_name));
307	rl->rl_conndrain = kcmd.conn_drain_timeout;
308	rl->rl_nat_timeout = kcmd.nat_expiry;
309	rl->rl_sticky_timeout = kcmd.sticky_expiry;
310
311	return (ILB_STATUS_OK);
312}
313
314ilb_status_t
315ilbd_retrieve_rule(ilbd_name_t rl_name, uint32_t *rbuf, size_t *rbufsz)
316{
317	ilbd_rule_t	*irl = NULL;
318	ilb_status_t	rc;
319	ilb_rule_info_t	*rinfo;
320
321	irl = i_find_rule_byname(rl_name);
322	if (irl == NULL)
323		return (ILB_STATUS_ENOENT);
324
325	ilbd_reply_ok(rbuf, rbufsz);
326	rinfo = (ilb_rule_info_t *)&((ilb_comm_t *)rbuf)->ic_data;
327	bcopy(&irl->irl_info, rinfo, sizeof (*rinfo));
328
329	/*
330	 * Check if the various timeout values are 0.  If one is, get the
331	 * default values from kernel.
332	 */
333	if (rinfo->rl_conndrain == 0 || rinfo->rl_nat_timeout == 0 ||
334	    rinfo->rl_sticky_timeout == 0) {
335		ilb_rule_info_t tmp_info;
336
337		(void) strcpy(tmp_info.rl_name, rinfo->rl_name);
338		rc = ilb_get_krule(&tmp_info);
339		if (rc != ILB_STATUS_OK)
340			return (rc);
341		if (rinfo->rl_conndrain == 0)
342			rinfo->rl_conndrain = tmp_info.rl_conndrain;
343		if ((rinfo->rl_topo == ILB_TOPO_IMPL_NAT ||
344		    rinfo->rl_topo == ILB_TOPO_IMPL_HALF_NAT) &&
345		    rinfo->rl_nat_timeout == 0) {
346			rinfo->rl_nat_timeout = tmp_info.rl_nat_timeout;
347		}
348		if ((rinfo->rl_flags & ILB_FLAGS_RULE_STICKY) &&
349		    rinfo->rl_sticky_timeout == 0) {
350			rinfo->rl_sticky_timeout = tmp_info.rl_sticky_timeout;
351		}
352	}
353	*rbufsz += sizeof (ilb_rule_info_t);
354
355	return (ILB_STATUS_OK);
356}
357
358static ilb_status_t
359ilbd_destroy_one_rule(ilbd_rule_t *irl)
360{
361	ilb_status_t	rc;
362	ilb_name_cmd_t	kcmd;
363
364	/*
365	 * as far as talking to the kernel is concerned, "all rules"
366	 * is handled in one go somewhere else, so we only
367	 * tell the kernel about single rules here.
368	 */
369	if ((irl->irl_flags & ILB_FLAGS_RULE_ALLRULES) == 0) {
370		kcmd.cmd = ILB_DESTROY_RULE;
371		(void) strlcpy(kcmd.name, irl->irl_name, sizeof (kcmd.name));
372		kcmd.flags = 0;
373
374		rc = do_ioctl(&kcmd, 0);
375		if (rc != ILB_STATUS_OK)
376			return (rc);
377
378	}
379	list_remove(&irl->irl_sg->isg_rulelist, irl);
380	list_remove(&ilbd_rule_hlist, irl);
381
382	/*
383	 * When dissociating a rule, only two errors can happen.  The hc
384	 * name is incorrect or the rule is not associated with the hc
385	 * object.  Both should not happen....  The check is for debugging
386	 * purpose.
387	 */
388	if (RULE_HAS_HC(irl) && (rc = ilbd_hc_dissociate_rule(irl)) !=
389	    ILB_STATUS_OK) {
390		logerr("ilbd_destroy_one_rule: cannot "
391		    "dissociate %s from hc object %s: %d",
392		    irl->irl_name, irl->irl_hcname, rc);
393	}
394
395	rc = i_ilbd_save_rule(irl, ILBD_SCF_DESTROY);
396	if (rc != ILB_STATUS_OK)
397		logdebug("ilbd_destroy_rule: save rule failed");
398
399	free(irl);
400	return (rc);
401}
402
403/*
404 * the following two functions are the other's opposite, and can
405 * call into each other for roll back purposes in case of error.
406 * To avoid endless recursion, the 'is_rollback' parameter must be
407 * set to B_TRUE in the roll back case.
408 */
409static ilb_status_t
410ilbd_enable_one_rule(ilbd_rule_t *irl, boolean_t is_rollback)
411{
412	ilb_status_t	rc = ILB_STATUS_OK;
413	ilb_name_cmd_t	kcmd;
414
415	/* no use sending a no-op to the kernel */
416	if ((irl->irl_flags & ILB_FLAGS_RULE_ENABLED) != 0)
417		return (ILB_STATUS_OK);
418
419	irl->irl_flags |= ILB_FLAGS_RULE_ENABLED;
420
421	/* "all rules" is handled in one go somewhere else, not here */
422	if ((irl->irl_flags & ILB_FLAGS_RULE_ALLRULES) == 0) {
423		kcmd.cmd = ILB_ENABLE_RULE;
424		(void) strlcpy(kcmd.name, irl->irl_name, sizeof (kcmd.name));
425		kcmd.flags = 0;
426
427		rc = do_ioctl(&kcmd, 0);
428		if (rc != ILB_STATUS_OK)
429			return (rc);
430	}
431	if (RULE_HAS_HC(irl) && (rc = ilbd_hc_enable_rule(irl)) !=
432	    ILB_STATUS_OK) {
433		/* Undo the kernel work */
434		kcmd.cmd = ILB_DISABLE_RULE;
435		/* Cannot do much if ioctl fails... */
436		(void) do_ioctl(&kcmd, 0);
437		return (rc);
438	}
439
440	if (!is_rollback) {
441		if (rc == ILB_STATUS_OK)
442			rc = i_ilbd_save_rule(irl, ILBD_SCF_ENABLE_DISABLE);
443		if (rc != ILB_STATUS_OK)
444			/* ignore rollback return code */
445			(void) ilbd_disable_one_rule(irl, B_TRUE);
446	}
447
448	return (rc);
449}
450
451static ilb_status_t
452ilbd_disable_one_rule(ilbd_rule_t *irl, boolean_t is_rollback)
453{
454	ilb_status_t	rc = ILB_STATUS_OK;
455	ilb_name_cmd_t	kcmd;
456
457	/* no use sending a no-op to the kernel */
458	if ((irl->irl_flags & ILB_FLAGS_RULE_ENABLED) == 0)
459		return (ILB_STATUS_OK);
460
461	irl->irl_flags &= ~ILB_FLAGS_RULE_ENABLED;
462
463	/* "all rules" is handled in one go somewhere else, not here */
464	if ((irl->irl_flags & ILB_FLAGS_RULE_ALLRULES) == 0) {
465		kcmd.cmd = ILB_DISABLE_RULE;
466		(void) strlcpy(kcmd.name, irl->irl_name, sizeof (kcmd.name));
467		kcmd.flags = 0;
468
469		rc = do_ioctl(&kcmd, 0);
470		if (rc != ILB_STATUS_OK)
471			return (rc);
472	}
473
474	if (RULE_HAS_HC(irl) && (rc = ilbd_hc_disable_rule(irl)) !=
475	    ILB_STATUS_OK) {
476		/* Undo the kernel work */
477		kcmd.cmd = ILB_ENABLE_RULE;
478		/* Cannot do much if ioctl fails... */
479		(void) do_ioctl(&kcmd, 0);
480		return (rc);
481	}
482
483	if (!is_rollback) {
484		if (rc == ILB_STATUS_OK)
485			rc = i_ilbd_save_rule(irl, ILBD_SCF_ENABLE_DISABLE);
486		if (rc != ILB_STATUS_OK)
487			/* ignore rollback return code */
488			(void) ilbd_enable_one_rule(irl, B_TRUE);
489	}
490
491	return (rc);
492}
493
494/*
495 * Generates an audit record for a supplied rule name
496 * Used for enable_rule, disable_rule, delete_rule,
497 * and create_rule subcommands
498 */
499static void
500ilbd_audit_rule_event(const char *audit_rule_name,
501    ilb_rule_info_t *rlinfo, ilbd_cmd_t cmd, ilb_status_t rc,
502    ucred_t *ucredp)
503{
504	adt_session_data_t	*ah;
505	adt_event_data_t	*event;
506	au_event_t		flag;
507	int			scf_val_len = ILBD_MAX_VALUE_LEN;
508	char			aobuf[scf_val_len]; /* algo:topo */
509	char			pbuf[scf_val_len]; /* protocol */
510	char			pxbuf[scf_val_len]; /* prxy src range */
511	char			hcpbuf[scf_val_len]; /* hcport: num or "ANY" */
512	char			valstr1[scf_val_len];
513	char			valstr2[scf_val_len];
514	char			addrstr_buf[INET6_ADDRSTRLEN];
515	char			addrstr_buf1[INET6_ADDRSTRLEN];
516	int			audit_error;
517
518	if ((ucredp == NULL) && (cmd == ILBD_CREATE_RULE))  {
519		/*
520		 * we came here from the path where ilbd incorporates
521		 * the configuration that is listed in SCF :
522		 * i_ilbd_read_config->ilbd_walk_rule_pgs->
523		 *    ->ilbd_scf_instance_walk_pg->ilbd_create_rule
524		 * We skip auditing in that case
525		 */
526		return;
527	}
528	if (adt_start_session(&ah, NULL, 0) != 0) {
529		logerr("ilbd_audit_rule_event: adt_start_session failed");
530		exit(EXIT_FAILURE);
531	}
532	if (adt_set_from_ucred(ah, ucredp, ADT_NEW) != 0) {
533		(void) adt_end_session(ah);
534		logerr("ilbd_audit_rule_event: adt_set_from_ucred failed");
535		exit(EXIT_FAILURE);
536	}
537	if (cmd == ILBD_ENABLE_RULE)
538		flag = ADT_ilb_enable_rule;
539	else if (cmd == ILBD_DISABLE_RULE)
540		flag = ADT_ilb_disable_rule;
541	else if (cmd == ILBD_DESTROY_RULE)
542		flag = ADT_ilb_delete_rule;
543	else if (cmd == ILBD_CREATE_RULE)
544		flag = ADT_ilb_create_rule;
545
546	if ((event = adt_alloc_event(ah, flag)) == NULL) {
547		logerr("ilbd_audit_rule_event: adt_alloc_event failed");
548		exit(EXIT_FAILURE);
549	}
550
551	(void) memset((char *)event, 0, sizeof (adt_event_data_t));
552
553	switch (cmd) {
554	case ILBD_DESTROY_RULE:
555		event->adt_ilb_delete_rule.auth_used = NET_ILB_CONFIG_AUTH;
556		event->adt_ilb_delete_rule.rule_name = (char *)audit_rule_name;
557		break;
558	case ILBD_ENABLE_RULE:
559		event->adt_ilb_enable_rule.auth_used = NET_ILB_ENABLE_AUTH;
560		event->adt_ilb_enable_rule.rule_name = (char *)audit_rule_name;
561		break;
562	case ILBD_DISABLE_RULE:
563		event->adt_ilb_disable_rule.auth_used = NET_ILB_ENABLE_AUTH;
564		event->adt_ilb_disable_rule.rule_name = (char *)audit_rule_name;
565		break;
566	case ILBD_CREATE_RULE:
567		aobuf[0] = '\0';
568		pbuf[0] = '\0';
569		valstr1[0] = '\0';
570		valstr2[0] = '\0';
571		hcpbuf[0] = '\0';
572
573		event->adt_ilb_create_rule.auth_used = NET_ILB_CONFIG_AUTH;
574
575		/* Fill in virtual IP address */
576		addrstr_buf[0] = '\0';
577		ilbd_addr2str(&rlinfo->rl_vip, addrstr_buf,
578		    sizeof (addrstr_buf));
579		event->adt_ilb_create_rule.virtual_ipaddress = addrstr_buf;
580
581		/* Fill in port - could be a single value or a range */
582		event->adt_ilb_create_rule.min_port = ntohs(rlinfo->rl_minport);
583		if (ntohs(rlinfo->rl_maxport) > ntohs(rlinfo->rl_minport)) {
584			/* port range */
585			event->adt_ilb_create_rule.max_port =
586			    ntohs(rlinfo->rl_maxport);
587		} else {
588			/* in audit record, max=min when single port */
589			event->adt_ilb_create_rule.max_port =
590			    ntohs(rlinfo->rl_minport);
591		}
592
593		/*
594		 * Fill in  protocol - if user does not specify it,
595		 * its TCP by default
596		 */
597		if (rlinfo->rl_proto == IPPROTO_UDP)
598			(void) snprintf(pbuf, sizeof (pbuf), "UDP");
599		else
600			(void) snprintf(pbuf, sizeof (pbuf), "TCP");
601		event->adt_ilb_create_rule.protocol = pbuf;
602
603		/* Fill in algorithm and operation type */
604		ilbd_algo_to_str(rlinfo->rl_algo, valstr1);
605		ilbd_topo_to_str(rlinfo->rl_topo, valstr2);
606		(void) snprintf(aobuf, sizeof (aobuf), "%s:%s",
607		    valstr1, valstr2);
608		event->adt_ilb_create_rule.algo_optype = aobuf;
609
610		/* Fill in proxy-src for the NAT case */
611		if (rlinfo->rl_topo == ILB_TOPO_NAT)  {
612			ilbd_addr2str(&rlinfo->rl_nat_src_start, addrstr_buf,
613			    sizeof (addrstr_buf));
614			if (&rlinfo->rl_nat_src_end == 0) {
615				/* Single address */
616				(void) snprintf(pxbuf, sizeof (pxbuf),
617				    "%s", addrstr_buf);
618			} else {
619				/* address range */
620				ilbd_addr2str(&rlinfo->rl_nat_src_end,
621				    addrstr_buf1, sizeof (addrstr_buf1));
622				(void) snprintf(pxbuf, sizeof (pxbuf),
623				    "%s-%s", addrstr_buf, addrstr_buf1);
624			}
625			event->adt_ilb_create_rule.proxy_src = pxbuf;
626		}
627
628		/*
629		 * Fill in pmask if user has specified one - 0 means
630		 * no persistence
631		 */
632		valstr1[0] = '\0';
633		ilbd_ip_to_str(rlinfo->rl_ipversion, &rlinfo->rl_stickymask,
634		    valstr1);
635			event->adt_ilb_create_rule.persist_mask = valstr1;
636
637		/* If there is a hcname */
638		if (rlinfo->rl_hcname[0] != '\0')
639			event->adt_ilb_create_rule.hcname = rlinfo->rl_hcname;
640
641		/* Fill in hcport */
642		if (rlinfo->rl_hcpflag == ILB_HCI_PROBE_FIX) {
643			/* hcport is specified by user */
644			(void) snprintf(hcpbuf, sizeof (hcpbuf), "%d",
645			    rlinfo->rl_hcport);
646			event->adt_ilb_create_rule.hcport = hcpbuf;
647		} else if (rlinfo->rl_hcpflag == ILB_HCI_PROBE_ANY) {
648			/* user has specified "ANY" */
649			(void) snprintf(hcpbuf, sizeof (hcpbuf), "ANY");
650			event->adt_ilb_create_rule.hcport = hcpbuf;
651		}
652
653		/*
654		 * Fill out the conndrain, nat_timeout and persist_timeout
655		 * If the user does not specify them, the default value
656		 * is set in the kernel. Userland does not know what
657		 * the values are. So if the user
658		 * does not specify these values they will show up as
659		 * 0 in the audit record.
660		 */
661		event->adt_ilb_create_rule.conndrain_timeout =
662		    rlinfo->rl_conndrain;
663		event->adt_ilb_create_rule.nat_timeout =
664		    rlinfo->rl_nat_timeout;
665		event->adt_ilb_create_rule.persist_timeout =
666		    rlinfo->rl_sticky_timeout;
667
668		/* Fill out servergroup and rule name */
669		event->adt_ilb_create_rule.server_group = rlinfo->rl_sgname;
670		event->adt_ilb_create_rule.rule_name = rlinfo->rl_name;
671		break;
672	}
673	if (rc == ILB_STATUS_OK) {
674		if (adt_put_event(event, ADT_SUCCESS, ADT_SUCCESS) != 0) {
675			logerr("ilbd_audit_rule_event:adt_put_event failed");
676			exit(EXIT_FAILURE);
677		}
678	} else {
679		audit_error = ilberror2auditerror(rc);
680		if (adt_put_event(event, ADT_FAILURE, audit_error) != 0) {
681			logerr("ilbd_audit_rule_event: adt_put_event failed");
682			exit(EXIT_FAILURE);
683		}
684	}
685	adt_free_event(event);
686	(void) adt_end_session(ah);
687}
688
689static ilb_status_t
690i_ilbd_action_switch(ilbd_rule_t *irl, ilbd_cmd_t cmd,
691    boolean_t is_rollback, ucred_t *ucredp)
692{
693	ilb_status_t    rc;
694
695	switch (cmd) {
696	case ILBD_DESTROY_RULE:
697		rc = ilbd_destroy_one_rule(irl);
698		if (!is_rollback) {
699			ilbd_audit_rule_event(irl->irl_name, NULL,
700			    cmd, rc, ucredp);
701		}
702		return (rc);
703	case ILBD_ENABLE_RULE:
704		rc = ilbd_enable_one_rule(irl, is_rollback);
705		if (!is_rollback) {
706			ilbd_audit_rule_event(irl->irl_name, NULL, cmd,
707			    rc, ucredp);
708		}
709		return (rc);
710	case ILBD_DISABLE_RULE:
711		rc = ilbd_disable_one_rule(irl, is_rollback);
712		if (!is_rollback) {
713			ilbd_audit_rule_event(irl->irl_name, NULL, cmd,
714			    rc, ucredp);
715		}
716		return (rc);
717	}
718	return (ILB_STATUS_INVAL_CMD);
719}
720
721static ilb_cmd_t
722i_ilbd2ilb_cmd(ilbd_cmd_t c)
723{
724	ilb_cmd_t	r;
725
726	switch (c) {
727	case ILBD_CREATE_RULE:
728		r = ILB_CREATE_RULE;
729		break;
730	case ILBD_DESTROY_RULE:
731		r = ILB_DESTROY_RULE;
732		break;
733	case ILBD_ENABLE_RULE:
734		r = ILB_ENABLE_RULE;
735		break;
736	case ILBD_DISABLE_RULE:
737		r = ILB_DISABLE_RULE;
738		break;
739	}
740	return (r);
741}
742
743static ilbd_cmd_t
744get_undo_cmd(ilbd_cmd_t cmd)
745{
746	ilbd_cmd_t	u_cmd;
747
748	switch (cmd) {
749	case ILBD_DESTROY_RULE:
750		u_cmd = ILBD_BAD_CMD;
751		break;
752	case ILBD_ENABLE_RULE:
753		u_cmd = ILBD_DISABLE_RULE;
754		break;
755	case ILBD_DISABLE_RULE:
756		u_cmd = ILBD_ENABLE_RULE;
757		break;
758	}
759
760	return (u_cmd);
761}
762
763static ilb_status_t
764i_ilbd_rule_action(const char *rule_name, const struct passwd *ps,
765    ilbd_cmd_t cmd, ucred_t *ucredp)
766{
767	ilbd_rule_t	*irl, *irl_next;
768	boolean_t	is_all_rules = B_FALSE;
769	ilb_status_t	rc = ILB_STATUS_OK;
770	ilb_name_cmd_t	kcmd;
771	ilbd_cmd_t	u_cmd;
772	char    rulename[ILB_NAMESZ];
773
774	if (ps != NULL) {
775		if ((cmd == ILBD_ENABLE_RULE) || (cmd == ILBD_DISABLE_RULE))
776			rc = ilbd_check_client_enable_auth(ps);
777		else
778			rc = ilbd_check_client_config_auth(ps);
779		/* generate the audit record before bailing out */
780		if (rc != ILB_STATUS_OK) {
781			if (rule_name != '\0') {
782				ilbd_audit_rule_event(rule_name, NULL,
783				    cmd, rc, ucredp);
784			} else {
785				(void) snprintf(rulename, sizeof (rulename),
786				    "all");
787				ilbd_audit_rule_event(rulename, NULL, cmd, rc,
788				    ucredp);
789			}
790			goto out;
791		}
792	}
793	is_all_rules = rule_name[0] == 0;
794
795	/* just one rule */
796	if (!is_all_rules) {
797		irl = i_find_rule_byname(rule_name);
798		if (irl == NULL) {
799			rc = ILB_STATUS_ENORULE;
800			ilbd_audit_rule_event(rule_name, NULL, cmd, rc, ucredp);
801			goto out;
802		}
803		/* auditing will be done by i_ilbd_action_switch() */
804		rc = i_ilbd_action_switch(irl, cmd, B_FALSE, ucredp);
805		goto out;
806	}
807
808	/* all rules: first tell the kernel, then walk the daemon's list */
809	kcmd.cmd = i_ilbd2ilb_cmd(cmd);
810	kcmd.flags = ILB_RULE_ALLRULES;
811
812	rc = do_ioctl(&kcmd, 0);
813	if (rc != ILB_STATUS_OK) {
814		(void) snprintf(rulename, sizeof (rulename), "all");
815		ilbd_audit_rule_event(rulename, NULL, cmd, rc, ucredp);
816		goto out;
817	}
818
819	irl = list_head(&ilbd_rule_hlist);
820	while (irl != NULL) {
821		irl_next = list_next(&ilbd_rule_hlist, irl);
822		irl->irl_flags |= ILB_FLAGS_RULE_ALLRULES;
823		/* auditing will be done by i_ilbd_action_switch() */
824		rc = i_ilbd_action_switch(irl, cmd, B_FALSE, ucredp);
825		irl->irl_flags &= ~ILB_FLAGS_RULE_ALLRULES;
826		if (rc != ILB_STATUS_OK)
827			goto rollback_list;
828		irl = irl_next;
829	}
830	return (rc);
831
832rollback_list:
833	u_cmd = get_undo_cmd(cmd);
834	if (u_cmd == ILBD_BAD_CMD)
835		return (rc);
836
837	if (is_all_rules) {
838		kcmd.cmd = i_ilbd2ilb_cmd(u_cmd);
839		(void) do_ioctl(&kcmd, 0);
840	}
841	/* current list element failed, so we start with previous one */
842	irl = list_prev(&ilbd_rule_hlist, irl);
843	while (irl != NULL) {
844		if (is_all_rules)
845			irl->irl_flags |= ILB_FLAGS_RULE_ALLRULES;
846
847		/*
848		 * When the processing of a command consists of
849		 * multiple sequential steps, and one of them fails,
850		 * ilbd performs rollback to undo the steps taken before the
851		 * failing step. Since ilbd is initiating these steps
852		 * there is not need to audit them.
853		 */
854		rc = i_ilbd_action_switch(irl, u_cmd, B_TRUE, NULL);
855		irl->irl_flags &= ~ILB_FLAGS_RULE_ALLRULES;
856
857		irl = list_prev(&ilbd_rule_hlist, irl);
858	}
859out:
860	return (rc);
861}
862
863ilb_status_t
864ilbd_destroy_rule(ilbd_name_t rule_name, const struct passwd *ps,
865    ucred_t *ucredp)
866{
867	return (i_ilbd_rule_action(rule_name, ps, ILBD_DESTROY_RULE, ucredp));
868}
869
870ilb_status_t
871ilbd_enable_rule(ilbd_name_t rule_name, const struct passwd *ps,
872    ucred_t *ucredp)
873{
874	return (i_ilbd_rule_action(rule_name, ps, ILBD_ENABLE_RULE, ucredp));
875
876}
877
878ilb_status_t
879ilbd_disable_rule(ilbd_name_t rule_name, const struct passwd *ps,
880    ucred_t *ucredp)
881{
882	return (i_ilbd_rule_action(rule_name, ps, ILBD_DISABLE_RULE, ucredp));
883}
884
885/*
886 * allocate storage for a kernel rule command and fill from
887 * "template" irl, if non-NULL
888 */
889static ilb_rule_cmd_t *
890i_alloc_kernel_rule_cmd(ilbd_rule_t *irl)
891{
892	ilb_rule_cmd_t *kcmd;
893
894	kcmd = (ilb_rule_cmd_t *)malloc(sizeof (*kcmd));
895	if (kcmd == NULL)
896		return (kcmd);
897
898	bzero(kcmd, sizeof (*kcmd));
899
900	if (irl != NULL) {
901		kcmd->flags = irl->irl_flags;
902		kcmd->ip_ver = AF_2_IPPROTO(irl->irl_ipversion);
903		kcmd->vip = irl->irl_vip;
904		kcmd->proto = irl->irl_proto;
905		kcmd->min_port = irl->irl_minport;
906		kcmd->max_port = irl->irl_maxport;
907		kcmd->algo = algo_lib2impl(irl->irl_algo);
908		kcmd->topo = topo_lib2impl(irl->irl_topo);
909		kcmd->sticky_mask = irl->irl_stickymask;
910		kcmd->nat_src_start = irl->irl_nat_src_start;
911		kcmd->nat_src_end = irl->irl_nat_src_end;
912		kcmd->conn_drain_timeout = irl->irl_conndrain;
913		kcmd->nat_expiry = irl->irl_nat_timeout;
914		kcmd->sticky_expiry = irl->irl_sticky_timeout;
915		(void) strlcpy(kcmd->name, irl->irl_name,
916		    sizeof (kcmd->name));
917	}
918	return (kcmd);
919}
920
921/*
922 * ncount is the next to be used index into (*kcmdp)->servers
923 */
924static ilb_status_t
925adjust_srv_info_cmd(ilb_servers_info_cmd_t **kcmdp, int index)
926{
927	ilb_servers_info_cmd_t	*kcmd = *kcmdp;
928	size_t			sz;
929
930	if (kcmd != NULL && kcmd->num_servers > index + 1)
931		return (ILB_STATUS_OK);
932
933	/*
934	 * the first ilb_server_info_t is part of *kcmd, so
935	 * by using index (which is one less than the total needed) here,
936	 * we allocate exactly the amount we need.
937	 */
938	sz = sizeof (*kcmd) + (index * sizeof (ilb_server_info_t));
939	kcmd = (ilb_servers_info_cmd_t *)realloc(kcmd, sz);
940	if (kcmd == NULL)
941		return (ILB_STATUS_ENOMEM);
942
943	/*
944	 * we don't count the slot we newly allocated yet.
945	 */
946	kcmd->num_servers = index;
947	*kcmdp = kcmd;
948
949	return (ILB_STATUS_OK);
950}
951
952/*
953 * this function adds all servers in srvlist to the kernel(!) rule
954 * the name of which is passed as argument.
955 */
956static ilb_status_t
957i_update_ksrv_rules(char *name, ilbd_sg_t *sg, ilbd_rule_t *rl)
958{
959	ilb_status_t		rc;
960	ilbd_srv_t		*srvp;
961	ilb_servers_info_cmd_t	*kcmd = NULL;
962	int			i;
963
964	/*
965	 * If the servergroup doesn't have any servers associated with
966	 * it yet, there's nothing more to do here.
967	 */
968	if (sg->isg_srvcount == 0)
969		return (ILB_STATUS_OK);
970
971	/*
972	 * walk the list of servers attached to this SG
973	 */
974	srvp = list_head(&sg->isg_srvlist);
975	for (i = 0; srvp != NULL; srvp = list_next(&sg->isg_srvlist, srvp)) {
976		rc = adjust_srv_info_cmd(&kcmd, i);
977		if (rc != ILB_STATUS_OK)
978			return (rc);
979
980		ILB_SGSRV_2_KSRV(&srvp->isv_srv, &kcmd->servers[i]);
981		/*
982		 * "no port" means "copy rule's port" (for kernel rule)
983		 */
984		if (kcmd->servers[i].min_port == 0) {
985			kcmd->servers[i].min_port = rl->irl_minport;
986			kcmd->servers[i].max_port = rl->irl_maxport;
987		}
988		i++;
989	}
990
991	kcmd->cmd = ILB_ADD_SERVERS;
992	kcmd->num_servers = i;
993	(void) strlcpy(kcmd->name, name, sizeof (kcmd->name));
994
995	rc = do_ioctl(kcmd, 0);
996	if (rc != ILB_STATUS_OK)
997		return (rc);
998
999	for (i = 0; i < kcmd->num_servers; i++) {
1000		int e;
1001
1002		if ((e = kcmd->servers[i].err) != 0) {
1003			logerr("i_update_ksrv_rules "
1004			    "ioctl indicates failure: %s", strerror(e));
1005			rc = ilb_map_errno2ilbstat(e);
1006			/*
1007			 * if adding even a single server failed, we need to
1008			 * roll back the whole wad. We ignore any errors and
1009			 * return the one that was returned by the first ioctl.
1010			 */
1011			kcmd->cmd = ILB_DEL_SERVERS;
1012			(void) do_ioctl(kcmd, 0);
1013			return (rc);
1014		}
1015	}
1016
1017	return (ILB_STATUS_OK);
1018}
1019
1020/* convert a struct in6_addr to valstr */
1021void
1022ilbd_ip_to_str(uint16_t ipversion, struct in6_addr *addr, char *valstr)
1023{
1024	size_t	vallen;
1025	ilb_ip_addr_t	ipaddr;
1026	void	*addrptr;
1027
1028	vallen = (ipversion == AF_INET) ? INET_ADDRSTRLEN : INET6_ADDRSTRLEN;
1029
1030	IP_COPY_IMPL_2_CLI(addr, &ipaddr);
1031	addrptr = (ipversion == AF_INET) ?
1032	    (void *)&ipaddr.ia_v4 : (void *)&ipaddr.ia_v6;
1033	if (inet_ntop(ipversion, (void *)addrptr, valstr, vallen == NULL))
1034		logerr("ilbd_ip_to_str: inet_ntop failed");
1035	return;
1036
1037}
1038
1039ilb_status_t
1040ilbd_create_rule(ilb_rule_info_t *rl, int ev_port,
1041    const struct passwd *ps, ucred_t *ucredp)
1042{
1043	ilb_status_t	rc;
1044	ilbd_rule_t	*irl = NULL;
1045	ilbd_sg_t	*sg;
1046	ilb_rule_cmd_t	*kcmd = NULL;
1047
1048	if (ps != NULL) {
1049		if ((rc = ilbd_check_client_config_auth(ps)) != ILB_STATUS_OK)
1050			goto out;
1051	}
1052
1053	if (i_find_rule_byname(rl->rl_name) != NULL) {
1054		logdebug("ilbd_create_rule: rule %s"
1055		    " already exists", rl->rl_name);
1056		ilbd_audit_rule_event(NULL, rl, ILBD_CREATE_RULE,
1057		    ILB_STATUS_DUP_RULE, ucredp);
1058		return (ILB_STATUS_DUP_RULE);
1059	}
1060
1061	sg = i_find_sg_byname(rl->rl_sgname);
1062	if (sg == NULL) {
1063		logdebug("ilbd_create_rule: rule %s uses non-existent"
1064		    " servergroup name %s", rl->rl_name, rl->rl_sgname);
1065		ilbd_audit_rule_event(NULL, rl, ILBD_CREATE_RULE,
1066		    ILB_STATUS_SGUNAVAIL, ucredp);
1067		return (ILB_STATUS_SGUNAVAIL);
1068	}
1069
1070	if ((rc = ilbd_sg_check_rule_port(sg, rl)) != ILB_STATUS_OK) {
1071		ilbd_audit_rule_event(NULL, rl, ILBD_CREATE_RULE, rc, ucredp);
1072		return (rc);
1073	}
1074
1075	/* allocs and copies contents of arg (if != NULL) into new rule */
1076	irl = i_alloc_ilbd_rule(rl);
1077	if (irl == NULL) {
1078		ilbd_audit_rule_event(NULL, rl, ILBD_CREATE_RULE,
1079		    ILB_STATUS_ENOMEM, ucredp);
1080		return (ILB_STATUS_ENOMEM);
1081	}
1082
1083	/* make sure rule's IPversion (via vip) and SG's match */
1084	if (sg->isg_srvcount > 0) {
1085		ilbd_srv_t	*srv = list_head(&sg->isg_srvlist);
1086		int32_t		r_af = rl->rl_ipversion;
1087		int32_t		s_af = GET_AF(&srv->isv_addr);
1088
1089		if (r_af != s_af) {
1090			logdebug("address family mismatch with servergroup");
1091			rc = ILB_STATUS_MISMATCHSG;
1092			goto out;
1093		}
1094	}
1095	irl->irl_sg = sg;
1096
1097	/* Try associating the rule with the given hc oject. */
1098	if (RULE_HAS_HC(irl)) {
1099		if ((rc = ilbd_hc_associate_rule(irl, ev_port)) !=
1100		    ILB_STATUS_OK)
1101			goto out;
1102	}
1103
1104	/*
1105	 * checks are done, now:
1106	 * 1. create rule in kernel
1107	 * 2. tell it about the backend server (which we maintain in SG)
1108	 * 3. attach the rule in memory
1109	 */
1110	/* 1. */
1111	/* allocs and copies contents of arg (if != NULL) into new rule */
1112	kcmd = i_alloc_kernel_rule_cmd(irl);
1113	if (kcmd == NULL) {
1114		rc = ILB_STATUS_ENOMEM;
1115		goto rollback_hc;
1116	}
1117	kcmd->cmd = ILB_CREATE_RULE;
1118
1119	rc = do_ioctl(kcmd, 0);
1120	if (rc != ILB_STATUS_OK)
1121		goto rollback_kcmd;
1122
1123	/* 2. */
1124	rc = i_update_ksrv_rules(kcmd->name, sg, irl);
1125	if (rc != ILB_STATUS_OK)
1126		goto rollback_kcmd;
1127
1128	/* 3. */
1129	(void) i_attach_rule2sg(sg, irl);
1130	list_insert_tail(&ilbd_rule_hlist, irl);
1131
1132	if (ps != NULL) {
1133		rc = i_ilbd_save_rule(irl, ILBD_SCF_CREATE);
1134		if (rc != ILB_STATUS_OK)
1135			goto rollback_rule;
1136	}
1137
1138	free(kcmd);
1139	ilbd_audit_rule_event(NULL, rl, ILBD_CREATE_RULE,
1140	    ILB_STATUS_OK, ucredp);
1141	return (ILB_STATUS_OK);
1142
1143rollback_rule:
1144	/*
1145	 * ilbd_destroy_one_rule() also frees irl, as well as dissociate
1146	 * rule and HC, so all we need to do afterwards is free the kcmd
1147	 * and return.
1148	 */
1149	(void) ilbd_destroy_one_rule(irl);
1150	ilbd_audit_rule_event(NULL, rl, ILBD_CREATE_RULE, rc, ucredp);
1151	free(kcmd);
1152	return (rc);
1153
1154rollback_kcmd:
1155	free(kcmd);
1156rollback_hc:
1157	/* Cannot fail since the rule is just associated with the hc object. */
1158	if (RULE_HAS_HC(irl))
1159		(void) ilbd_hc_dissociate_rule(irl);
1160out:
1161	ilbd_audit_rule_event(NULL, rl, ILBD_CREATE_RULE, rc, ucredp);
1162	free(irl);
1163	return (rc);
1164}
1165
1166static uint32_t
1167i_flags_d2k(int f)
1168{
1169	uint32_t	r = 0;
1170
1171	if (ILB_IS_SRV_ENABLED(f))
1172		r |= ILB_SERVER_ENABLED;
1173	/* more as they are defined */
1174
1175	return (r);
1176}
1177
1178/*
1179 * walk the list of rules and add srv to the *kernel* rule
1180 * (this is a list of rules hanging off of a server group)
1181 */
1182ilb_status_t
1183i_add_srv2krules(list_t *rlist, ilb_sg_srv_t *srv, int ev_port)
1184{
1185	ilb_status_t		rc = ILB_STATUS_OK;
1186	ilbd_rule_t		*rl, *del_rl;
1187	ilb_servers_info_cmd_t	kcmd;
1188	ilb_servers_cmd_t	del_kcmd;
1189
1190	kcmd.cmd = ILB_ADD_SERVERS;
1191	kcmd.num_servers = 1;
1192	kcmd.servers[0].err = 0;
1193	kcmd.servers[0].addr = srv->sgs_addr;
1194	kcmd.servers[0].flags = i_flags_d2k(srv->sgs_flags);
1195	(void) strlcpy(kcmd.servers[0].name, srv->sgs_srvID,
1196	    sizeof (kcmd.servers[0].name));
1197
1198	/*
1199	 * a note about rollback: since we need to start rollback with the
1200	 * current list element in some case, and with the previous one
1201	 * in others, we must "go back" in this latter case before
1202	 * we jump to the rollback code.
1203	 */
1204	for (rl = list_head(rlist); rl != NULL; rl = list_next(rlist, rl)) {
1205		(void) strlcpy(kcmd.name, rl->irl_name, sizeof (kcmd.name));
1206		/*
1207		 * sgs_minport == 0 means "no port specified"; this
1208		 * indicates that the server matches anything the rule
1209		 * provides.
1210		 * NOTE: this can be different for different rules
1211		 * using the same server group, therefore we don't modify
1212		 * this information in the servergroup, but *only* in
1213		 * the kernel's rule.
1214		 */
1215		if (srv->sgs_minport == 0) {
1216			kcmd.servers[0].min_port = rl->irl_minport;
1217			kcmd.servers[0].max_port = rl->irl_maxport;
1218		} else {
1219			kcmd.servers[0].min_port = srv->sgs_minport;
1220			kcmd.servers[0].max_port = srv->sgs_maxport;
1221		}
1222		rc = do_ioctl((void *)&kcmd, 0);
1223		if (rc != ILB_STATUS_OK) {
1224			logdebug("i_add_srv2krules: do_ioctl call failed");
1225			del_rl = list_prev(rlist, rl);
1226			goto rollback;
1227		}
1228
1229		/*
1230		 * if ioctl() returns != 0, it doesn't perform the copyout
1231		 * necessary to indicate *which* server failed (we could be
1232		 * adding more than one); therefore we must check this
1233		 * 'err' field even if ioctl() returns 0.
1234		 */
1235		if (kcmd.servers[0].err != 0) {
1236			logerr("i_add_srv2krules: SIOCILB ioctl returned"
1237			    " error %d", kcmd.servers[0].err);
1238			rc = ilb_map_errno2ilbstat(kcmd.servers[0].err);
1239			del_rl = list_prev(rlist, rl);
1240			goto rollback;
1241		}
1242		if (RULE_HAS_HC(rl)) {
1243			if ((rc = ilbd_hc_add_server(rl, srv, ev_port)) !=
1244			    ILB_STATUS_OK) {
1245				logerr("i_add_srv2krules: cannot start timer "
1246				    " for rules %s server %s", rl->irl_name,
1247				    srv->sgs_srvID);
1248
1249				del_rl = rl;
1250				goto rollback;
1251			}
1252		}
1253	}
1254
1255	return (rc);
1256
1257rollback:
1258	/*
1259	 * this is almost, but not quite, the same as i_rem_srv_frm_krules()
1260	 * therefore we keep it seperate.
1261	 */
1262	del_kcmd.cmd = ILB_DEL_SERVERS;
1263	del_kcmd.num_servers = 1;
1264	del_kcmd.servers[0].addr = srv->sgs_addr;
1265	while (del_rl != NULL) {
1266		if (RULE_HAS_HC(del_rl))
1267			(void) ilbd_hc_del_server(del_rl, srv);
1268		(void) strlcpy(del_kcmd.name, del_rl->irl_name,
1269		    sizeof (del_kcmd.name));
1270		(void) do_ioctl((void *)&del_kcmd, 0);
1271		del_rl = list_prev(rlist, del_rl);
1272	}
1273
1274	return (rc);
1275}
1276
1277/*
1278 * ev_port is only used for rollback purposes in this function
1279 */
1280ilb_status_t
1281i_rem_srv_frm_krules(list_t *rlist, ilb_sg_srv_t *srv, int ev_port)
1282{
1283	ilb_status_t		rc = ILB_STATUS_OK;
1284	ilbd_rule_t		*rl, *add_rl;
1285	ilb_servers_cmd_t	kcmd;
1286	ilb_servers_info_cmd_t	add_kcmd;
1287
1288	kcmd.cmd = ILB_DEL_SERVERS;
1289	kcmd.num_servers = 1;
1290	kcmd.servers[0].err = 0;
1291	kcmd.servers[0].addr = srv->sgs_addr;
1292
1293	for (rl = list_head(rlist); rl != NULL; rl = list_next(rlist, rl)) {
1294		(void) strlcpy(kcmd.name, rl->irl_name, sizeof (kcmd.name));
1295		rc = do_ioctl((void *)&kcmd, 0);
1296		if (rc != ILB_STATUS_OK) {
1297			logdebug("i_rem_srv_frm_krules: do_ioctl"
1298			    "call failed");
1299			add_rl = list_prev(rlist, rl);
1300			goto rollback;
1301		}
1302		/*
1303		 * if ioctl() returns != 0, it doesn't perform the copyout
1304		 * necessary to indicate *which* server failed (we could be
1305		 * removing more than one); therefore we must check this
1306		 * 'err' field even if ioctl() returns 0.
1307		 */
1308		if (kcmd.servers[0].err != 0) {
1309			logerr("i_rem_srv_frm_krules: SIOCILB ioctl"
1310			    " returned error %s",
1311			    strerror(kcmd.servers[0].err));
1312			rc = ilb_map_errno2ilbstat(kcmd.servers[0].err);
1313			add_rl = list_prev(rlist, rl);
1314			goto rollback;
1315		}
1316		if (RULE_HAS_HC(rl) &&
1317		    (rc = ilbd_hc_del_server(rl, srv)) != ILB_STATUS_OK) {
1318			logerr("i_rem_srv_frm_krules: cannot delete "
1319			    "timer for rules %s server %s", rl->irl_name,
1320			    srv->sgs_srvID);
1321			add_rl = rl;
1322			goto rollback;
1323		}
1324	}
1325
1326	return (rc);
1327
1328rollback:
1329	/* Don't do roll back if ev_port == -1. */
1330	if (ev_port == -1)
1331		return (rc);
1332
1333	add_kcmd.cmd = ILB_ADD_SERVERS;
1334	add_kcmd.num_servers = 1;
1335	add_kcmd.servers[0].err = 0;
1336	add_kcmd.servers[0].addr = srv->sgs_addr;
1337	add_kcmd.servers[0].flags = i_flags_d2k(srv->sgs_flags);
1338	(void) strlcpy(add_kcmd.servers[0].name, srv->sgs_srvID,
1339	    sizeof (add_kcmd.servers[0].name));
1340	while (add_rl != NULL) {
1341		if (srv->sgs_minport == 0) {
1342			add_kcmd.servers[0].min_port = add_rl->irl_minport;
1343			add_kcmd.servers[0].max_port = add_rl->irl_maxport;
1344		} else {
1345			add_kcmd.servers[0].min_port = srv->sgs_minport;
1346			add_kcmd.servers[0].max_port = srv->sgs_maxport;
1347		}
1348		if (RULE_HAS_HC(add_rl))
1349			(void) ilbd_hc_add_server(add_rl, srv, ev_port);
1350		(void) strlcpy(add_kcmd.name, add_rl->irl_name,
1351		    sizeof (add_kcmd.name));
1352		(void) do_ioctl((void *)&add_kcmd, 0);
1353		add_rl = list_prev(rlist, add_rl);
1354	}
1355
1356	return (rc);
1357}
1358