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