xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.lib/ilbd/ilbd_rules.c (revision b40e8a892f9b1015a0a51175e62b4e16280f6d98)
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 
49 static ilb_status_t ilbd_disable_one_rule(ilbd_rule_t *, boolean_t);
50 static 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 
60 list_t		ilbd_rule_hlist;
61 
62 static ilb_algo_t
63 algo_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 
78 static ilb_topo_t
79 topo_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 
92 ilb_algo_impl_t
93 algo_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 
108 ilb_topo_impl_t
109 topo_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  */
127 ilb_status_t
128 i_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 
221 void
222 i_setup_rule_hlist(void)
223 {
224 	list_create(&ilbd_rule_hlist, sizeof (ilbd_rule_t),
225 	    offsetof(ilbd_rule_t, irl_link));
226 }
227 
228 ilb_status_t
229 i_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  */
251 static ilbd_rule_t *
252 i_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 
263 static ilbd_rule_t *
264 i_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  */
281 static ilb_status_t
282 ilb_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 
314 ilb_status_t
315 ilbd_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 
358 static ilb_status_t
359 ilbd_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  */
409 static ilb_status_t
410 ilbd_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 
451 static ilb_status_t
452 ilbd_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  */
499 static void
500 ilbd_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 
689 static ilb_status_t
690 i_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 
721 static ilb_cmd_t
722 i_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 
743 static ilbd_cmd_t
744 get_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 
763 static ilb_status_t
764 i_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 
832 rollback_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 	}
859 out:
860 	return (rc);
861 }
862 
863 ilb_status_t
864 ilbd_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 
870 ilb_status_t
871 ilbd_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 
878 ilb_status_t
879 ilbd_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  */
889 static ilb_rule_cmd_t *
890 i_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  */
924 static ilb_status_t
925 adjust_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  */
956 static ilb_status_t
957 i_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 */
1021 void
1022 ilbd_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 
1039 ilb_status_t
1040 ilbd_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 
1143 rollback_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 
1154 rollback_kcmd:
1155 	free(kcmd);
1156 rollback_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);
1160 out:
1161 	ilbd_audit_rule_event(NULL, rl, ILBD_CREATE_RULE, rc, ucredp);
1162 	free(irl);
1163 	return (rc);
1164 }
1165 
1166 static uint32_t
1167 i_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  */
1182 ilb_status_t
1183 i_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 
1257 rollback:
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  */
1280 ilb_status_t
1281 i_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 
1328 rollback:
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