xref: /titanic_52/usr/src/cmd/cmd-inet/usr.sbin/ilbadm/ilbadm_rules.c (revision 68c47f65208790c466e5e484f2293d3baed71c6a)
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 <stdio.h>
28 #include <unistd.h>
29 #include <stdlib.h>
30 #include <strings.h>
31 #include <errno.h>
32 #include <sys/types.h>
33 #include <sys/socket.h>
34 #include <netinet/in.h>
35 #include <arpa/inet.h>
36 #include <sys/list.h>
37 #include <netdb.h>
38 #include <ofmt.h>
39 #include <assert.h>
40 #include <libilb.h>
41 #include "ilbadm.h"
42 
43 static ilbadm_key_name_t rl_incoming_keys[] = {
44 	{ILB_KEY_VIP, "vip", ""},
45 	{ILB_KEY_PORT, "port", ""},
46 	{ILB_KEY_PROTOCOL, "protocol", "prot"},
47 	{ILB_KEY_BAD, "", ""}
48 };
49 static ilbadm_key_name_t rl_method_keys[] = {
50 	{ILB_KEY_ALGORITHM, "lbalg", "algo"},
51 	{ILB_KEY_TYPE, "type", "topo"},
52 	{ILB_KEY_SRC, "proxy-src", "nat-src"},
53 	{ILB_KEY_STICKY, "pmask", "persist"},
54 	{ILB_KEY_BAD, "", ""}
55 };
56 static ilbadm_key_name_t rl_outgoing_keys[] = {
57 	{ILB_KEY_SERVERGROUP, "servergroup", "sg"},
58 	{ILB_KEY_BAD, "", ""}
59 };
60 static ilbadm_key_name_t rl_healthchk_keys[] = {
61 	{ILB_KEY_HEALTHCHECK, "hc-name", "hcn"},
62 	{ILB_KEY_HCPORT, "hc-port", "hcp"},
63 	{ILB_KEY_BAD, "", ""}
64 };
65 static ilbadm_key_name_t rl_timer_keys[] = {
66 	{ILB_KEY_CONNDRAIN, "conn-drain", ""},
67 	{ILB_KEY_NAT_TO, "nat-timeout", ""},
68 	{ILB_KEY_STICKY_TO, "persist-timeout", ""},
69 	{ILB_KEY_BAD, "", ""}
70 };
71 
72 static ilbadm_key_name_t *all_keys[] = {
73 	rl_incoming_keys, rl_method_keys, rl_outgoing_keys,
74 	rl_healthchk_keys, rl_timer_keys, NULL
75 };
76 
77 
78 /* field ids for of_* functions */
79 #define	OF_IP_VIP		0
80 #define	OF_IP_PROXYSRC		1
81 #define	OF_IP_STICKYMASK	2
82 
83 #define	OF_STR_RNAME		0
84 #define	OF_STR_HCNAME		1
85 #define	OF_STR_SGNAME		2
86 #define	OF_STR_INTERFACE	3
87 
88 #define	OF_PORT			0
89 #define	OF_HCPORT		1
90 
91 #define	OF_T_CONN		0
92 #define	OF_T_NAT		1
93 #define	OF_T_STICKY		2
94 
95 #define	OF_SRV_ID		0
96 #define	OF_SRV_ADDR		1
97 #define	OF_SRV_PORT		2
98 #define	OF_SRV_STATUS		3
99 #define	OF_SRV_RNAME		4
100 #define	OF_SRV_SGNAME		5
101 #define	OF_SRV_HOSTNAME		6
102 
103 /* some field sizes of ofmt_field_t arrays */
104 #define	IPv4_FIELDWIDTH		16
105 #define	IPv6_FIELDWIDTH		39
106 #define	ILB_HOSTNAMELEN		20
107 #define	ILB_STATUSFIELD_LEN	7
108 
109 typedef struct arg_struct {
110 	int		flags;
111 	char		*o_str;
112 	ofmt_field_t	*o_fields;
113 	ofmt_handle_t	oh;
114 } ilbadm_sh_rl_arg_t;
115 
116 typedef struct ilbadm_rl_exp_arg {
117 	FILE	*fp;
118 } ilbadm_rl_exp_arg_t;
119 
120 typedef struct ilbadm_rl_list_arg {
121 	ilb_handle_t	h;
122 	ilb_rule_data_t	*rd;
123 } ilbadm_rl_list_arg_t;
124 
125 typedef struct ilbadm_rl_srvlist_arg {
126 	char		*sgname;
127 	ilb_server_data_t	*sd;
128 	ilb_rule_data_t	*rd;
129 	int		flags;
130 	char		*o_str;
131 	ofmt_field_t	*o_fields;
132 	ofmt_handle_t	oh;
133 } ilbadm_rl_srvlist_arg_t;
134 
135 static ofmt_cb_t of_algo;
136 static ofmt_cb_t of_proto;
137 static ofmt_cb_t of_rl_ip;
138 static ofmt_cb_t of_rl_mask;
139 static ofmt_cb_t of_rport;
140 static ofmt_cb_t of_rstatus;
141 static ofmt_cb_t of_str;
142 static ofmt_cb_t of_time;
143 static ofmt_cb_t of_topo;
144 static ofmt_cb_t of_rl_srvlist;
145 
146 static boolean_t of_srv2str(ofmt_arg_t *, char *, uint_t);
147 static boolean_t of_port2str(in_port_t, in_port_t, char *, uint_t);
148 
149 static ofmt_field_t rfields_v4[] = {
150 	{"RULENAME",	ILB_NAMESZ,	OF_STR_RNAME,	of_str},
151 	{"STATUS",	ILB_STATUSFIELD_LEN,	0,	of_rstatus},
152 	{"PORT",	10,		OF_PORT,	of_rport},
153 	{"PROTOCOL",	5,		0,	of_proto},
154 	{"LBALG",	12,		0,	of_algo},
155 	{"TYPE",	8,		0,	of_topo},
156 	{"PROXY-SRC",	2*IPv4_FIELDWIDTH+1,	OF_IP_PROXYSRC,	of_rl_ip},
157 	{"PMASK",	6,	OF_IP_STICKYMASK, of_rl_mask},
158 	{"HC-NAME",	ILB_NAMESZ,	OF_STR_HCNAME,	of_str},
159 	{"HC-PORT",	8,		OF_HCPORT,	of_rport},
160 	{"CONN-DRAIN",	11,		OF_T_CONN,	of_time},
161 	{"NAT-TIMEOUT",	12,		OF_T_NAT,	of_time},
162 	{"PERSIST-TIMEOUT",		16,	OF_T_STICKY,	of_time},
163 	{"SERVERGROUP",	ILB_SGNAME_SZ,	OF_STR_SGNAME,	of_str},
164 	{"VIP",		IPv4_FIELDWIDTH,	OF_IP_VIP,	of_rl_ip},
165 	{"SERVERS",	20,		0,	of_rl_srvlist},
166 	{NULL,		0,		0,	NULL}
167 };
168 
169 static ofmt_field_t rfields_v6[] = {
170 	{"RULENAME",	ILB_NAMESZ,	OF_STR_RNAME,	of_str},
171 	{"STATUS",	ILB_STATUSFIELD_LEN,	0,	of_rstatus},
172 	{"PORT",	10,		OF_PORT,	of_rport},
173 	{"PROTOCOL",	5,		0,	of_proto},
174 	{"LBALG",	12,		0,	of_algo},
175 	{"TYPE",	8,		0,	of_topo},
176 	{"PROXY-SRC",	IPv6_FIELDWIDTH,	OF_IP_PROXYSRC,	of_rl_ip},
177 	{"PMASK",	6,		OF_IP_STICKYMASK, of_rl_mask},
178 	{"HC-NAME",	ILB_NAMESZ,	OF_STR_HCNAME,	of_str},
179 	{"HC-PORT",	8,		OF_HCPORT,	of_rport},
180 	{"CONN-DRAIN",	11,		OF_T_CONN,	of_time},
181 	{"NAT-TIMEOUT",	12,		OF_T_NAT,	of_time},
182 	{"PERSIST-TIMEOUT",		16,	OF_T_STICKY,	of_time},
183 	{"SERVERGROUP",	ILB_SGNAME_SZ,	OF_STR_SGNAME,	of_str},
184 	{"VIP",		IPv6_FIELDWIDTH,	OF_IP_VIP,	of_rl_ip},
185 	{"SERVERS",	20,		0,	of_rl_srvlist},
186 	{NULL,		0,		0,	NULL}
187 };
188 
189 static ofmt_field_t ssfields_v4[] = {
190 	{"SERVERID",	ILB_NAMESZ,	OF_SRV_ID,	of_srv2str},
191 	{"ADDRESS",	IPv4_FIELDWIDTH,	OF_SRV_ADDR,	of_srv2str},
192 	{"PORT",	5,			OF_SRV_PORT,	of_srv2str},
193 	{"RULENAME",	ILB_NAMESZ,	OF_SRV_RNAME,	of_srv2str},
194 	{"STATUS",	ILB_STATUSFIELD_LEN,	OF_SRV_STATUS,	of_srv2str},
195 	{"SERVERGROUP",	ILB_SGNAME_SZ,	OF_SRV_SGNAME,	of_srv2str},
196 	{"HOSTNAME",	ILB_HOSTNAMELEN,	OF_SRV_HOSTNAME, of_srv2str},
197 	{NULL,		0,		0,	NULL}
198 };
199 
200 static ofmt_field_t ssfields_v6[] = {
201 	{"SERVERID",	ILB_NAMESZ,	OF_SRV_ID,	of_srv2str},
202 	{"ADDRESS",	IPv6_FIELDWIDTH,	OF_SRV_ADDR,	of_srv2str},
203 	{"PORT",	5,			OF_SRV_PORT,	of_srv2str},
204 	{"RULENAME",	ILB_NAMESZ,	OF_SRV_RNAME,	of_srv2str},
205 	{"STATUS",	ILB_STATUSFIELD_LEN,	OF_SRV_STATUS,	of_srv2str},
206 	{"SERVERGROUP",	ILB_SGNAME_SZ,	OF_SRV_SGNAME,	of_srv2str},
207 	{"HOSTNAME",	ILB_HOSTNAMELEN,	OF_SRV_HOSTNAME, of_srv2str},
208 	{NULL,		0,		0,	NULL}
209 };
210 
211 extern int	optind, optopt, opterr;
212 extern char	*optarg;
213 
214 extern ilbadm_val_type_t algo_types[];
215 extern ilbadm_val_type_t topo_types[];
216 
217 static char *
218 i_key_to_opt(ilbadm_key_name_t *n, ilbadm_key_code_t k)
219 {
220 	int i;
221 
222 	for (i = 0; n[i].k_key != ILB_KEY_BAD; i++)
223 		if (n[i].k_key == k)
224 			break;
225 
226 	return (n[i].k_name);
227 }
228 
229 char *
230 ilbadm_key_to_opt(ilbadm_key_code_t k)
231 {
232 	char 	*name;
233 	int	i;
234 
235 	for (i = 0; all_keys[i] != NULL; i++) {
236 		name = i_key_to_opt(all_keys[i], k);
237 		if (*name != '\0')
238 			return (name);
239 	}
240 
241 	return (NULL);
242 }
243 
244 /*
245  * ports are in HOST byte order
246  */
247 static void
248 ports2str(short port1, short port2, char *buf, const int sz)
249 {
250 	if (port2 <= port1)
251 		(void) snprintf(buf, sz, "port=%d", port1);
252 	else
253 		(void) snprintf(buf, sz, "port=%d-%d", port1, port2);
254 }
255 
256 static void
257 proto2str(short proto, char *buf, int sz)
258 {
259 	struct protoent *pe;
260 
261 	pe = getprotobynumber((int)proto);
262 	if (pe != NULL)
263 		(void) snprintf(buf, sz, "protocol=%s", pe->p_name);
264 	else
265 		(void) sprintf(buf, "(bad proto %d)", proto);
266 }
267 
268 static void
269 algo2str(ilb_algo_t algo, char *buf, int sz)
270 {
271 	char 	*s = i_str_from_val((int)algo, &algo_types[0]);
272 
273 	(void) snprintf(buf, sz, "lbalg=%s", (s && *s) ? s : "(bad algo)");
274 }
275 
276 static int
277 algo2bare_str(ilb_algo_t algo, char *buf, int sz)
278 {
279 	char 	*s = i_str_from_val((int)algo, &algo_types[0]);
280 
281 	return (snprintf(buf, sz, "%s", (s && *s) ? s : ""));
282 }
283 
284 static void
285 topo2str(ilb_topo_t topo, char *buf, int sz)
286 {
287 	char 	*s = i_str_from_val((int)topo, &topo_types[0]);
288 
289 	(void) snprintf(buf, sz, "type=%s", (s && *s) ? s : "(bad type)");
290 }
291 
292 static int
293 topo2bare_str(ilb_topo_t topo, char *buf, int sz)
294 {
295 	char 	*s = i_str_from_val((int)topo, &topo_types[0]);
296 
297 	return (snprintf(buf, sz, "%s", (s && *s) ? s : ""));
298 }
299 
300 static boolean_t
301 of_str(ofmt_arg_t *of_arg, char *buf, uint_t bufsize)
302 {
303 	ilbadm_rl_list_arg_t *ra = (ilbadm_rl_list_arg_t *)of_arg->ofmt_cbarg;
304 	ilb_rule_data_t	*rd = (ilb_rule_data_t *)ra->rd;
305 
306 	switch (of_arg->ofmt_id) {
307 	case OF_STR_RNAME:
308 		(void) strlcpy(buf, rd->r_name, bufsize);
309 		break;
310 	case OF_STR_SGNAME:
311 		(void) strlcpy(buf, rd->r_sgname, bufsize);
312 		break;
313 	case OF_STR_HCNAME:
314 		if (rd->r_hcname != NULL && *(rd->r_hcname) != '\0')
315 			(void) strlcpy(buf, rd->r_hcname, bufsize);
316 		break;
317 	}
318 	return (B_TRUE);
319 }
320 
321 /* ARGSUSED */
322 static boolean_t
323 of_proto(ofmt_arg_t *of_arg, char *buf, uint_t bufsize)
324 {
325 	ilbadm_rl_list_arg_t *ra = (ilbadm_rl_list_arg_t *)of_arg->ofmt_cbarg;
326 	ilb_rule_data_t	*rd = (ilb_rule_data_t *)ra->rd;
327 
328 	if (rd->r_proto == IPPROTO_TCP)
329 		(void) strlcpy(buf, "TCP", bufsize);
330 	else if (rd->r_proto == IPPROTO_UDP)
331 		(void) strlcpy(buf, "UDP", bufsize);
332 	else
333 		return (B_FALSE);
334 	return (B_TRUE);
335 }
336 
337 static boolean_t
338 of_rl_ip(ofmt_arg_t *of_arg, char *buf, uint_t bufsize)
339 {
340 	ilbadm_rl_list_arg_t *ra = (ilbadm_rl_list_arg_t *)of_arg->ofmt_cbarg;
341 	ilb_rule_data_t	*rd = (ilb_rule_data_t *)ra->rd;
342 	ilb_ip_addr_t	*ip = NULL, *ip2 = NULL;
343 
344 	switch (of_arg->ofmt_id) {
345 	case OF_IP_VIP:
346 		ip = &rd->r_vip;
347 		break;
348 	case OF_IP_PROXYSRC:
349 		ip = &rd->r_nat_src_start;
350 		ip2 = &rd->r_nat_src_end;
351 		break;
352 	case OF_IP_STICKYMASK:
353 		ip = &rd->r_stickymask;
354 		break;
355 	}
356 
357 	/* only print something valid */
358 	if (ip != NULL && (ip->ia_af == AF_INET || ip->ia_af == AF_INET6))
359 		ip2str(ip, buf, bufsize, V6_ADDRONLY);
360 	if (ip2 != NULL && (ip2->ia_af == AF_INET || ip2->ia_af == AF_INET6) &&
361 	    buf[0] != '\0') {
362 		int	sl = strlen(buf);
363 
364 		buf += sl; bufsize -= sl;
365 		*buf++ = '-'; bufsize--;
366 		ip2str(ip2, buf, bufsize, V6_ADDRONLY);
367 	}
368 
369 	return (B_TRUE);
370 }
371 
372 static boolean_t
373 of_rl_mask(ofmt_arg_t *of_arg, char *buf, uint_t bufsize)
374 {
375 	ilbadm_rl_list_arg_t *ra = (ilbadm_rl_list_arg_t *)of_arg->ofmt_cbarg;
376 	ilb_rule_data_t	*rd = (ilb_rule_data_t *)ra->rd;
377 	ilb_ip_addr_t	*ip = NULL;
378 
379 	assert(of_arg->ofmt_id == OF_IP_STICKYMASK);
380 	if (!(rd->r_flags & ILB_FLAGS_RULE_STICKY))
381 		return (B_TRUE);
382 	ip = &rd->r_stickymask;
383 
384 	(void) snprintf(buf, bufsize, "/%d", ilbadm_mask_to_prefixlen(ip));
385 	return (B_TRUE);
386 }
387 
388 static void
389 hcport_print(ilb_rule_data_t *rd, char *buf, uint_t bufsize)
390 {
391 	if (rd->r_hcport != 0)
392 		(void) snprintf(buf, bufsize, "%d", ntohs(rd->r_hcport));
393 	else if (rd->r_hcpflag == ILB_HCI_PROBE_ANY)
394 		(void) snprintf(buf, bufsize, "ANY");
395 	else
396 		buf[0] = '\0';
397 }
398 static boolean_t
399 of_rport(ofmt_arg_t *of_arg, char *buf, uint_t bufsize)
400 {
401 	ilbadm_rl_list_arg_t *ra = (ilbadm_rl_list_arg_t *)of_arg->ofmt_cbarg;
402 	ilb_rule_data_t	*rd = (ilb_rule_data_t *)ra->rd;
403 
404 	if (of_arg->ofmt_id == OF_PORT)
405 		return (of_port2str(rd->r_minport, rd->r_maxport, buf,
406 		    bufsize));
407 
408 	/* only print a hcport if there's a hc name as well */
409 	if (of_arg->ofmt_id == OF_HCPORT && rd->r_hcname[0] != '\0')
410 		hcport_print(rd, buf, bufsize);
411 
412 	return (B_TRUE);
413 }
414 
415 /* ARGSUSED */
416 static boolean_t
417 of_rstatus(ofmt_arg_t *of_arg, char *buf, uint_t bufsize)
418 {
419 	ilbadm_rl_list_arg_t *ra = (ilbadm_rl_list_arg_t *)of_arg->ofmt_cbarg;
420 	ilb_rule_data_t	*rd = (ilb_rule_data_t *)ra->rd;
421 
422 	if ((rd->r_flags & ILB_FLAGS_RULE_ENABLED) == ILB_FLAGS_RULE_ENABLED)
423 		buf[0] = 'E';
424 	else
425 		buf[0] = 'D';
426 	buf[1] = '\0';
427 	return (B_TRUE);
428 }
429 
430 static boolean_t
431 of_algo(ofmt_arg_t *of_arg, char *buf, uint_t bufsize)
432 {
433 	ilbadm_rl_list_arg_t *ra = (ilbadm_rl_list_arg_t *)of_arg->ofmt_cbarg;
434 	ilb_rule_data_t	*rd = (ilb_rule_data_t *)ra->rd;
435 
436 	if (algo2bare_str(rd->r_algo, buf, bufsize) == 0)
437 		return (B_FALSE);
438 	return (B_TRUE);
439 }
440 
441 static boolean_t
442 of_topo(ofmt_arg_t *of_arg, char *buf, uint_t bufsize)
443 {
444 	ilbadm_rl_list_arg_t *ra = (ilbadm_rl_list_arg_t *)of_arg->ofmt_cbarg;
445 	ilb_rule_data_t	*rd = (ilb_rule_data_t *)ra->rd;
446 
447 	if (topo2bare_str(rd->r_topo, buf, bufsize) == 0)
448 		return (B_FALSE);
449 	return (B_TRUE);
450 }
451 
452 static boolean_t
453 of_time(ofmt_arg_t *of_arg, char *buf, uint_t bufsize)
454 {
455 	ilbadm_rl_list_arg_t *ra = (ilbadm_rl_list_arg_t *)of_arg->ofmt_cbarg;
456 	ilb_rule_data_t	*rd = (ilb_rule_data_t *)ra->rd;
457 
458 	switch (of_arg->ofmt_id) {
459 	case OF_T_CONN:
460 		(void) snprintf(buf, bufsize, "%u", rd->r_conndrain);
461 		break;
462 	case OF_T_NAT:
463 		(void) snprintf(buf, bufsize, "%u", rd->r_nat_timeout);
464 		break;
465 	case OF_T_STICKY:
466 		(void) snprintf(buf, bufsize, "%u", rd->r_sticky_timeout);
467 		break;
468 	}
469 	return (B_TRUE);
470 }
471 
472 typedef struct rl_showlist_arg {
473 	char	*buf;
474 	uint_t	bufsize;
475 } rl_showlist_arg_t;
476 
477 /* ARGSUSED */
478 /* called by ilb_walk_servers(), cannot get rid of unused args */
479 static ilb_status_t
480 srv2srvID(ilb_handle_t h, ilb_server_data_t *sd, const char *sgname, void *arg)
481 {
482 	rl_showlist_arg_t	*sla = (rl_showlist_arg_t *)arg;
483 	int			len;
484 
485 	(void) snprintf(sla->buf, sla->bufsize, "%s,", sd->sd_srvID);
486 	len = strlen(sd->sd_srvID) + 1;
487 	sla->buf += len;
488 	sla->bufsize -= len;
489 
490 	return (ILB_STATUS_OK);
491 }
492 
493 static boolean_t
494 of_rl_srvlist(ofmt_arg_t *of_arg, char *buf, uint_t bufsize)
495 {
496 	ilbadm_rl_list_arg_t *ra = (ilbadm_rl_list_arg_t *)of_arg->ofmt_cbarg;
497 	ilb_rule_data_t	*rd = (ilb_rule_data_t *)ra->rd;
498 	rl_showlist_arg_t	sla;
499 
500 	sla.buf = buf;
501 	sla.bufsize = bufsize;
502 
503 	(void) ilb_walk_servers(ra->h, srv2srvID, rd->r_sgname,
504 	    (void *)&sla);
505 	/* we're trailing a ',' which we need to remove */
506 	*--sla.buf = '\0';
507 
508 	return (B_TRUE);
509 }
510 
511 #define	RMAXCOLS 120	/* enough? */
512 #define	SERVER_WIDTH	(ILB_NAMESZ+1)	/* 1st guess */
513 
514 static boolean_t
515 of_port2str(in_port_t minport, in_port_t maxport, char *buf, uint_t bufsize)
516 {
517 	in_port_t	h_min, h_max;
518 	int		len;
519 
520 	h_min = ntohs(minport);
521 	h_max = ntohs(maxport);
522 
523 	if (h_min == 0)
524 		return (B_FALSE); /* print "unspec" == "all ports" */
525 
526 	len = snprintf(buf, bufsize, "%d", h_min);
527 	if (h_max > h_min)
528 		(void) snprintf(buf + len, bufsize - len, "-%d", h_max);
529 	return (B_TRUE);
530 }
531 
532 static ilbadm_status_t
533 ip2hostname(ilb_ip_addr_t *ip, char *buf, uint_t bufsize)
534 {
535 	int		ret;
536 	struct hostent	*he;
537 
538 	switch (ip->ia_af) {
539 	case AF_INET:
540 		he = getipnodebyaddr((char *)&ip->ia_v4, sizeof (ip->ia_v4),
541 		    ip->ia_af, &ret);
542 		break;
543 	case AF_INET6:
544 		he = getipnodebyaddr((char *)&ip->ia_v6, sizeof (ip->ia_v6),
545 		    ip->ia_af, &ret);
546 		break;
547 	default: return (ILBADM_INVAL_AF);
548 	}
549 
550 	/* if we can't resolve this, just return an empty name */
551 	if (he == NULL)
552 		buf[0] = '\0';
553 	else
554 		(void) strlcpy(buf, he->h_name, bufsize);
555 
556 	return (ILBADM_OK);
557 }
558 
559 /* ARGSUSED */
560 /*
561  * Since this function is used by libilb routine ilb_walk_rules()
562  * it must return libilb errors
563  */
564 static ilb_status_t
565 ilbadm_show_onerule(ilb_handle_t h, ilb_rule_data_t *rd, void *arg)
566 {
567 	ilbadm_sh_rl_arg_t	*larg = (ilbadm_sh_rl_arg_t *)arg;
568 	ofmt_status_t	oerr;
569 	int		oflags = 0;
570 	int		ocols = RMAXCOLS;
571 	ilbadm_rl_list_arg_t	ra;
572 	static ofmt_handle_t	oh = (ofmt_handle_t)NULL;
573 	ofmt_field_t	*fields;
574 	boolean_t	r_enabled = rd->r_flags & ILB_FLAGS_RULE_ENABLED;
575 
576 	if (larg->o_str == NULL) {
577 		ilbadm_err(gettext("internal error"));
578 		return (ILB_STATUS_GENERIC);
579 	}
580 
581 	/*
582 	 * only print rules (enabled/dis-) we're asked to
583 	 * note: both LIST_**ABLED flags can be set at the same time,
584 	 * whereas a rule has one state only. therefore the complicated
585 	 * statement.
586 	 */
587 	if (!((r_enabled && (larg->flags & ILBADM_LIST_ENABLED)) ||
588 	    (!r_enabled && (larg->flags & ILBADM_LIST_DISABLED))))
589 		return (ILB_STATUS_OK);
590 
591 	if (larg->flags & ILBADM_LIST_PARSE)
592 		oflags |= OFMT_PARSABLE;
593 
594 	if (larg->flags & ILBADM_LIST_FULL)
595 		oflags |= OFMT_MULTILINE;
596 
597 	bzero(&ra, sizeof (ra));
598 	ra.rd = rd;
599 	ra.h = h;
600 
601 	if (oh == NULL) {
602 		if (rd->r_vip.ia_af == AF_INET)
603 			fields = rfields_v4;
604 		else
605 			fields = rfields_v6;
606 
607 		oerr = ofmt_open(larg->o_str, fields, oflags, ocols, &oh);
608 		if (oerr != OFMT_SUCCESS) {
609 			char	e[80];
610 
611 			ilbadm_err(gettext("ofmt_open failed: %s"),
612 			    ofmt_strerror(oh, oerr, e, sizeof (e)));
613 			return (ILB_STATUS_GENERIC);
614 		}
615 	}
616 
617 	ofmt_print(oh, &ra);
618 
619 	return (ILB_STATUS_OK);
620 }
621 
622 static char *full_list_rule_hdrs =
623 	"RULENAME,STATUS,PORT,PROTOCOL,LBALG,TYPE,PROXY-SRC,PMASK,"
624 	"HC-NAME,HC-PORT,CONN-DRAIN,NAT-TIMEOUT,"
625 	"PERSIST-TIMEOUT,SERVERGROUP,VIP,SERVERS";
626 static char *def_list_rule_hdrs =
627 	"RULENAME,STATUS,LBALG,TYPE,PROTOCOL,VIP,PORT";
628 
629 /* ARGSUSED */
630 ilbadm_status_t
631 ilbadm_show_rules(int argc, char *argv[])
632 {
633 	ilb_handle_t	h = ILB_INVALID_HANDLE;
634 	int		c;
635 	ilb_status_t	rclib = ILB_STATUS_OK;
636 	ilbadm_status_t	rc = ILBADM_OK;
637 	boolean_t	o_opt = B_FALSE, p_opt = B_FALSE;
638 	boolean_t	f_opt = B_FALSE;
639 	ilbadm_sh_rl_arg_t	larg = {0, NULL, NULL, NULL};
640 
641 	larg.flags = ILBADM_LIST_ENABLED | ILBADM_LIST_DISABLED;
642 	while ((c = getopt(argc, argv, ":fpedo:")) != -1) {
643 		switch ((char)c) {
644 		case 'f': larg.flags |= ILBADM_LIST_FULL;
645 			larg.o_str = full_list_rule_hdrs;
646 			f_opt = B_TRUE;
647 			break;
648 		case 'p': larg.flags |= ILBADM_LIST_PARSE;
649 			p_opt = B_TRUE;
650 			break;
651 		case 'o': larg.o_str = optarg;
652 			o_opt = B_TRUE;
653 			break;
654 		/* -e and -d may be repeated - make sure the last one wins */
655 		case 'e': larg.flags &= ILBADM_LIST_NODISABLED;
656 			larg.flags |= ILBADM_LIST_ENABLED;
657 			break;
658 		case 'd': larg.flags &= ILBADM_LIST_NOENABLED;
659 			larg.flags |= ILBADM_LIST_DISABLED;
660 			break;
661 		case ':': ilbadm_err(gettext("missing option argument for %c"),
662 			    (char)optopt);
663 			rc = ILBADM_LIBERR;
664 			goto out;
665 			/* not reached */
666 			break;
667 		case '?':
668 		default:
669 			unknown_opt(argv, optind-1);
670 			/* not reached */
671 			break;
672 		}
673 	}
674 
675 	if (f_opt && o_opt) {
676 		ilbadm_err(gettext("options -o and -f are mutually"
677 		    " exclusive"));
678 		exit(1);
679 	}
680 
681 	if (p_opt && !o_opt) {
682 		ilbadm_err(gettext("option -p requires -o"));
683 		exit(1);
684 	}
685 
686 	if (p_opt && larg.o_str != NULL &&
687 	    (strcasecmp(larg.o_str, "all") == 0)) {
688 		ilbadm_err(gettext("option -p requires explicit field"
689 		    " names for -o"));
690 		exit(1);
691 	}
692 
693 	/* no -o option, so we use std. fields */
694 	if (!o_opt && !f_opt)
695 		larg.o_str = def_list_rule_hdrs;
696 
697 	rclib = ilb_open(&h);
698 	if (rclib != ILB_STATUS_OK)
699 		goto out;
700 
701 	if (optind >= argc) {
702 		rclib = ilb_walk_rules(h, ilbadm_show_onerule, NULL,
703 		    (void*)&larg);
704 	} else {
705 		while (optind < argc) {
706 			rclib = ilb_walk_rules(h, ilbadm_show_onerule,
707 			    argv[optind++], (void*)&larg);
708 			if (rclib != ILB_STATUS_OK)
709 				break;
710 		}
711 	}
712 out:
713 	if (h != ILB_INVALID_HANDLE)
714 		(void) ilb_close(h);
715 
716 	if (rclib != ILB_STATUS_OK) {
717 		/*
718 		 * The show function returns ILB_STATUS_GENERIC after printing
719 		 * out an error message.  So we don't need to print it again.
720 		 */
721 		if (rclib != ILB_STATUS_GENERIC)
722 			ilbadm_err(ilb_errstr(rclib));
723 		rc = ILBADM_LIBERR;
724 	}
725 	return (rc);
726 }
727 
728 static boolean_t
729 of_srv2str(ofmt_arg_t *of_arg, char *buf, uint_t bufsize)
730 {
731 	ilbadm_rl_srvlist_arg_t  *larg =
732 	    (ilbadm_rl_srvlist_arg_t *)of_arg->ofmt_cbarg;
733 	ilb_server_data_t	*sd = larg->sd;
734 	uint_t		op = of_arg->ofmt_id;
735 	boolean_t	ret = B_TRUE;
736 	ilbadm_status_t	rc;
737 
738 	if (sd == NULL)
739 		return (B_FALSE);
740 
741 	switch (op) {
742 	case OF_SRV_ID:
743 		(void) strlcpy(buf, sd->sd_srvID, bufsize);
744 		break;
745 	case OF_SRV_STATUS:
746 		if (ILB_IS_SRV_ENABLED(sd->sd_flags))
747 			buf[0] = 'E';
748 		else
749 			buf[0] = 'D';
750 		buf[1] = '\0';
751 		break;
752 	case OF_SRV_RNAME:
753 		(void) strlcpy(buf, larg->rd->r_name, bufsize);
754 		break;
755 	case OF_SRV_SGNAME:
756 		(void) strlcpy(buf, larg->sgname, bufsize);
757 		break;
758 	case OF_SRV_HOSTNAME:
759 		rc = ip2hostname(&sd->sd_addr, buf, bufsize);
760 		if (rc != ILBADM_OK) {
761 			buf[0] = '\0';
762 			ret = B_FALSE;
763 		}
764 		break;
765 	case OF_SRV_PORT:
766 		ret = of_port2str(sd->sd_minport, sd->sd_maxport,
767 		    buf, bufsize);
768 		break;
769 	case OF_SRV_ADDR:
770 		ip2str(&sd->sd_addr, buf, bufsize, V6_ADDRONLY);
771 		break;
772 	}
773 
774 	return (ret);
775 }
776 
777 /* ARGSUSED */
778 static ilb_status_t
779 i_show_rl_srv(ilb_handle_t h, ilb_server_data_t *sd, const char *sgname,
780     void *arg)
781 {
782 	ilbadm_rl_srvlist_arg_t	*larg = (ilbadm_rl_srvlist_arg_t *)arg;
783 
784 	larg->sd = sd;
785 	ofmt_print(larg->oh, larg);
786 	return (ILB_STATUS_OK);
787 }
788 
789 /* ARGSUSED */
790 /*
791  * Since this function is used by libilb routine ilb_walk_rules()
792  * it must return libilb errors
793  */
794 ilb_status_t
795 ilbadm_show_rl_servers(ilb_handle_t h, ilb_rule_data_t *rd, void *arg)
796 {
797 	ofmt_status_t	oerr;
798 	int		oflags = 0;
799 	int		ocols = RMAXCOLS;
800 	ofmt_field_t	*fields;
801 	static ofmt_handle_t	oh = (ofmt_handle_t)NULL;
802 	ilbadm_rl_srvlist_arg_t	*larg = (ilbadm_rl_srvlist_arg_t *)arg;
803 
804 	/*
805 	 * in full mode, we currently re-open ofmt() for every rule; we use
806 	 * a variable number of lines, as we print one for every server
807 	 * attached to a rule.
808 	 */
809 	if (larg->o_str == NULL) {
810 		ilbadm_err(gettext("internal error"));
811 		return (ILB_STATUS_GENERIC);
812 	}
813 
814 	if (larg->flags & ILBADM_LIST_PARSE)
815 		oflags |= OFMT_PARSABLE;
816 
817 	if (rd->r_vip.ia_af == AF_INET)
818 		fields = ssfields_v4;
819 	else
820 		fields = ssfields_v6;
821 
822 	if (oh == NULL) {
823 		oerr = ofmt_open(larg->o_str, fields, oflags, ocols, &oh);
824 		if (oerr != OFMT_SUCCESS) {
825 			char	e[80];
826 
827 			ilbadm_err(gettext("ofmt_open failed: %s"),
828 			    ofmt_strerror(oh, oerr, e, sizeof (e)));
829 			return (ILB_STATUS_GENERIC);
830 		}
831 		larg->oh = oh;
832 	}
833 
834 	larg->rd = rd;
835 	larg->sgname = rd->r_sgname;
836 
837 	return (ilb_walk_servers(h, i_show_rl_srv, rd->r_sgname, (void *)larg));
838 }
839 
840 static char *def_show_srv_hdrs =
841 	"SERVERID,ADDRESS,PORT,RULENAME,STATUS,SERVERGROUP";
842 
843 /* ARGSUSED */
844 ilbadm_status_t
845 ilbadm_show_server(int argc, char *argv[])
846 {
847 	ilb_handle_t	h = ILB_INVALID_HANDLE;
848 	int		c;
849 	ilb_status_t	rclib = ILB_STATUS_OK;
850 	ilbadm_status_t	rc = ILBADM_OK;
851 	boolean_t	o_opt = B_FALSE, p_opt = B_FALSE;
852 	ilbadm_rl_srvlist_arg_t	larg;
853 
854 	bzero(&larg, sizeof (larg));
855 	while ((c = getopt(argc, argv, ":po:")) != -1) {
856 		switch ((char)c) {
857 		case 'p': larg.flags |= ILBADM_LIST_PARSE;
858 			p_opt = B_TRUE;
859 			break;
860 		case 'o': larg.o_str = optarg;
861 			o_opt = B_TRUE;
862 			break;
863 		case ':': ilbadm_err(gettext("missing option argument for %c"),
864 			    (char)optopt);
865 			rc = ILBADM_LIBERR;
866 			goto out;
867 			/* not reached */
868 			break;
869 		case '?':
870 		default:
871 			unknown_opt(argv, optind-1);
872 			/* not reached */
873 			break;
874 		}
875 	}
876 
877 	if (p_opt && !o_opt) {
878 		ilbadm_err(gettext("option -p requires -o"));
879 		exit(1);
880 	}
881 
882 	if (p_opt && larg.o_str != NULL &&
883 	    (strcasecmp(larg.o_str, "all") == 0)) {
884 		ilbadm_err(gettext("option -p requires explicit"
885 		    "  field names for -o"));
886 		exit(1);
887 	}
888 
889 	/* no -o option, so we use default fields */
890 	if (!o_opt)
891 		larg.o_str = def_show_srv_hdrs;
892 
893 	rclib = ilb_open(&h);
894 	if (rclib != ILB_STATUS_OK)
895 		goto out;
896 
897 	if (optind >= argc) {
898 		rclib = ilb_walk_rules(h, ilbadm_show_rl_servers, NULL,
899 		    (void*)&larg);
900 	} else {
901 		while (optind < argc) {
902 			rclib = ilb_walk_rules(h, ilbadm_show_rl_servers,
903 			    argv[optind++], (void*)&larg);
904 			if (rclib != ILB_STATUS_OK)
905 				break;
906 		}
907 	}
908 out:
909 	if (h != ILB_INVALID_HANDLE)
910 		(void) ilb_close(h);
911 
912 	if (rclib != ILB_STATUS_OK) {
913 		/*
914 		 * The show function returns ILB_STATUS_GENERIC after printing
915 		 * out an error message.  So we don't need to print it again.
916 		 */
917 		if (rclib != ILB_STATUS_GENERIC)
918 			ilbadm_err(ilb_errstr(rclib));
919 		rc = ILBADM_LIBERR;
920 	}
921 	return (rc);
922 }
923 
924 static ilbadm_status_t
925 i_parse_rl_arg(char *arg, ilb_rule_data_t *rd, ilbadm_key_name_t *keylist)
926 {
927 	ilbadm_status_t	rc;
928 
929 	rc = i_parse_optstring(arg, (void *) rd, keylist,
930 	    OPT_PORTS, NULL);
931 	return (rc);
932 }
933 
934 static void
935 i_ilbadm_alloc_rule(ilb_rule_data_t **rdp)
936 {
937 	ilb_rule_data_t	*rd;
938 
939 	*rdp = rd = (ilb_rule_data_t *)calloc(sizeof (*rd), 1);
940 	if (rd == NULL)
941 		return;
942 	rd->r_proto = IPPROTO_TCP;
943 }
944 
945 static void
946 i_ilbadm_free_rule(ilb_rule_data_t *rd)
947 {
948 	free(rd);
949 }
950 
951 /* ARGSUSED */
952 ilbadm_status_t
953 ilbadm_destroy_rule(int argc, char *argv[])
954 {
955 	ilb_handle_t	h = ILB_INVALID_HANDLE;
956 	ilbadm_status_t	rc = ILBADM_OK;
957 	ilb_status_t	rclib = ILB_STATUS_OK;
958 	boolean_t	all_rules = B_FALSE;
959 	int		c, i;
960 
961 	while ((c = getopt(argc, argv, ":a")) != -1) {
962 		switch ((char)c) {
963 		case 'a':
964 			all_rules = B_TRUE;
965 			break;
966 		case '?':
967 		default:
968 			unknown_opt(argv, optind-1);
969 			/* not reached */
970 			break;
971 		}
972 	}
973 
974 	if (optind >= argc && !all_rules) {
975 		ilbadm_err(gettext("usage: delete-rule -a | name"));
976 		return (ILBADM_LIBERR);
977 	}
978 
979 	/* either "-a" or rulename, not both */
980 	if (optind < argc && all_rules) {
981 		rc = ILBADM_INVAL_ARGS;
982 		goto out;
983 	}
984 
985 	rclib = ilb_open(&h);
986 	if (rclib != ILB_STATUS_OK)
987 		goto out;
988 
989 	if (all_rules) {
990 		rclib = ilb_destroy_rule(h, NULL);
991 		goto out;
992 	}
993 
994 	for (i = optind; i < argc && rclib == ILB_STATUS_OK; i++)
995 		rclib = ilb_destroy_rule(h, argv[i]);
996 
997 out:
998 	if (h != ILB_INVALID_HANDLE)
999 		(void) ilb_close(h);
1000 
1001 	/* This prints the specific errors */
1002 	if (rclib != ILB_STATUS_OK) {
1003 		ilbadm_err(ilb_errstr(rclib));
1004 		rc = ILBADM_LIBERR;
1005 	}
1006 	/* This prints the generic errors */
1007 	if ((rc != ILBADM_OK) && (rc != ILBADM_LIBERR))
1008 		ilbadm_err(ilbadm_errstr(rc));
1009 	return (rc);
1010 }
1011 
1012 /* ARGSUSED */
1013 static ilbadm_status_t
1014 ilbadm_Xable_rule(int argc, char *argv[], ilbadm_cmd_t cmd)
1015 {
1016 	ilb_handle_t	h = ILB_INVALID_HANDLE;
1017 	ilb_status_t	rclib = ILB_STATUS_OK;
1018 	ilbadm_status_t	rc = ILBADM_OK;
1019 	int		i;
1020 
1021 	rclib = ilb_open(&h);
1022 	if (rclib != ILB_STATUS_OK)
1023 		goto out;
1024 	/*
1025 	 * by default, en/disable-rule mean "all", and not using
1026 	 * a rule name will cause this behaviour to kick in
1027 	 */
1028 	if (argc < 2) {
1029 		if (cmd == cmd_enable_rule)
1030 			rclib = ilb_enable_rule(h, NULL);
1031 		else
1032 			rclib = ilb_disable_rule(h, NULL);
1033 	} else {
1034 
1035 		for (i = optind; i < argc && rc == ILBADM_OK; i++) {
1036 			if (cmd == cmd_enable_rule)
1037 				rclib = ilb_enable_rule(h, argv[i]);
1038 			else
1039 				rclib = ilb_disable_rule(h, argv[i]);
1040 		}
1041 	}
1042 out:
1043 	if (h != ILB_INVALID_HANDLE)
1044 		(void) ilb_close(h);
1045 
1046 	if (rclib != ILB_STATUS_OK) {
1047 		ilbadm_err(ilb_errstr(rclib));
1048 		rc = ILBADM_LIBERR;
1049 	}
1050 	return (rc);
1051 }
1052 
1053 ilbadm_status_t
1054 ilbadm_enable_rule(int argc, char *argv[])
1055 {
1056 
1057 	return (ilbadm_Xable_rule(argc, argv, cmd_enable_rule));
1058 }
1059 
1060 ilbadm_status_t
1061 ilbadm_disable_rule(int argc, char *argv[])
1062 {
1063 	return (ilbadm_Xable_rule(argc, argv, cmd_disable_rule));
1064 }
1065 
1066 /*
1067  * parse and create a rule
1068  */
1069 ilbadm_status_t
1070 ilbadm_create_rule(int argc, char *argv[])
1071 {
1072 	ilb_handle_t	h = ILB_INVALID_HANDLE;
1073 	int		c;
1074 	ilb_status_t	rclib = ILB_STATUS_OK;
1075 	ilbadm_status_t	rc = ILBADM_OK;
1076 	ilb_rule_data_t	*rd;
1077 	boolean_t	p_opt = B_FALSE;
1078 
1079 	i_ilbadm_alloc_rule(&rd);
1080 
1081 	while ((c = getopt(argc, argv, ":ei:m:o:t:h:p")) != -1) {
1082 		switch ((char)c) {
1083 		case 'e':
1084 			rd->r_flags |= ILB_FLAGS_RULE_ENABLED;
1085 			break;
1086 		case 'h':
1087 			/*
1088 			 * Default value of of r_hcpflag means that if there
1089 			 * is a port range, probe any port.  If there is only
1090 			 * one port, probe that port.
1091 			 */
1092 			rd->r_hcpflag = ILB_HCI_PROBE_ANY;
1093 			rc = i_parse_rl_arg(optarg, rd, &rl_healthchk_keys[0]);
1094 			break;
1095 		case 'o':
1096 			rc = i_parse_rl_arg(optarg, rd, &rl_outgoing_keys[0]);
1097 			break;
1098 		case 'm':
1099 			rc = i_parse_rl_arg(optarg, rd, &rl_method_keys[0]);
1100 			break;
1101 		case 't':
1102 			rc = i_parse_rl_arg(optarg, rd, &rl_timer_keys[0]);
1103 			break;
1104 		case 'i':
1105 			rc = i_parse_rl_arg(optarg, rd, &rl_incoming_keys[0]);
1106 			break;
1107 		case 'p':
1108 			p_opt = B_TRUE;
1109 			break;
1110 		case ':':
1111 			ilbadm_err(gettext("missing option-argument"
1112 			    " for %c"), (char)optopt);
1113 			rc = ILBADM_LIBERR;
1114 			break;
1115 		case '?':
1116 		default:
1117 			unknown_opt(argv, optind-1);
1118 			/* not reached */
1119 			break;
1120 
1121 		}
1122 		if (rc != ILBADM_OK)
1123 			goto out;
1124 	}
1125 
1126 	if (optind >= argc) {
1127 		ilbadm_err(gettext("missing mandatory arguments - please refer"
1128 		    " to 'ilbadm create-rule' subcommand description in"
1129 		    " ilbadm(1M)"));
1130 		rc = ILBADM_LIBERR;
1131 		goto out;
1132 
1133 	}
1134 
1135 	if (p_opt) {
1136 		/*
1137 		 * if user hasn't specified a mask, apply default
1138 		 */
1139 		if ((rd->r_flags & ILB_FLAGS_RULE_STICKY) == 0) {
1140 			char 	*maskstr;
1141 
1142 			switch (rd->r_vip.ia_af) {
1143 			case AF_INET:
1144 				maskstr = "32";
1145 				break;
1146 			case AF_INET6:
1147 				maskstr = "128";
1148 				break;
1149 			}
1150 			rc = ilbadm_set_netmask(maskstr, &rd->r_stickymask,
1151 			    rd->r_vip.ia_af);
1152 			if (rc != ILBADM_OK) {
1153 				ilbadm_err(gettext("trouble seting default"
1154 				    " persistence mask"));
1155 				rc = ILBADM_LIBERR;
1156 				goto out;
1157 			}
1158 		}
1159 	} else {
1160 		/* use of sticky mask currently mandates "-p" */
1161 		if ((rd->r_flags & ILB_FLAGS_RULE_STICKY) != 0) {
1162 			ilbadm_err(gettext("use of stickymask requires"
1163 			    " -p option"));
1164 			rc = ILBADM_LIBERR;
1165 			goto out;
1166 		}
1167 	}
1168 
1169 	if (strlen(argv[optind]) > ILBD_NAMESZ -1) {
1170 		ilbadm_err(gettext("rule name %s is too long -"
1171 		    " must not exceed %d chars"), argv[optind],
1172 		    ILBD_NAMESZ - 1);
1173 		rc = ILBADM_LIBERR;
1174 		goto out;
1175 	}
1176 
1177 	(void) strlcpy(rd->r_name, argv[optind], sizeof (rd->r_name));
1178 
1179 	rc = i_check_rule_spec(rd);
1180 	if (rc != ILBADM_OK)
1181 		goto out;
1182 
1183 	rclib = ilb_open(&h);
1184 	if (rclib != ILB_STATUS_OK)
1185 		goto out;
1186 
1187 	rclib = ilb_create_rule(h, rd);
1188 
1189 out:
1190 	i_ilbadm_free_rule(rd);
1191 
1192 	if (h != ILB_INVALID_HANDLE)
1193 		(void) ilb_close(h);
1194 
1195 	if (rclib != ILB_STATUS_OK) {
1196 		ilbadm_err(ilb_errstr(rclib));
1197 		rc = ILBADM_LIBERR;
1198 	}
1199 	if ((rc != ILBADM_OK) && (rc != ILBADM_LIBERR))
1200 		ilbadm_err(ilbadm_errstr(rc));
1201 
1202 	return (rc);
1203 }
1204 
1205 /* ARGSUSED */
1206 
1207 /*
1208  * Since this function is used by libilb function, ilb_walk_rules()
1209  * it must return libilb errors
1210  */
1211 static ilb_status_t
1212 ilbadm_export_rl(ilb_handle_t h, ilb_rule_data_t *rd, void *arg)
1213 {
1214 	char	linebuf[128];	/* should be enough */
1215 	int	sz = sizeof (linebuf);
1216 	FILE	*fp = ((ilbadm_rl_exp_arg_t *)arg)->fp;
1217 	uint32_t	conndrain, nat_timeout, sticky_timeout;
1218 
1219 	(void) fprintf(fp, "create-rule ");
1220 	if (rd->r_flags & ILB_FLAGS_RULE_ENABLED)
1221 		(void) fprintf(fp, "-e ");
1222 	if (rd->r_flags & ILB_FLAGS_RULE_STICKY)
1223 		(void) fprintf(fp, "-p ");
1224 
1225 	ip2str(&rd->r_vip, linebuf, sz, V6_ADDRONLY);
1226 	(void) fprintf(fp, "-i vip=%s,", linebuf);
1227 
1228 	(void) ports2str(ntohs(rd->r_minport), ntohs(rd->r_maxport),
1229 	    linebuf, sz);
1230 	(void) fprintf(fp, "%s,", linebuf);
1231 
1232 	proto2str(rd->r_proto, linebuf, sz);
1233 	(void) fprintf(fp, "%s ", linebuf);
1234 
1235 	algo2str(rd->r_algo, linebuf, sz);
1236 	(void) fprintf(fp, "-m %s,", linebuf);
1237 
1238 	topo2str(rd->r_topo, linebuf, sz);
1239 	(void) fprintf(fp, "%s", linebuf);
1240 
1241 	if (rd->r_nat_src_start.ia_af != AF_UNSPEC) {
1242 		ip2str(&rd->r_nat_src_start, linebuf, sz, V6_ADDRONLY);
1243 		/* if the address is unspecified, skip it */
1244 		if (linebuf[0] != '\0') {
1245 			(void) fprintf(fp, ",proxy-src=%s", linebuf);
1246 			ip2str(&rd->r_nat_src_end, linebuf, sz, V6_ADDRONLY);
1247 			(void) fprintf(fp, "-%s", linebuf);
1248 		}
1249 	}
1250 
1251 	if (rd->r_flags & ILB_FLAGS_RULE_STICKY) {
1252 		(void) fprintf(fp, ",pmask=/%d",
1253 		    ilbadm_mask_to_prefixlen(&rd->r_stickymask));
1254 	}
1255 
1256 	(void) fprintf(fp, " ");
1257 
1258 	if (*rd->r_hcname != '\0') {
1259 		(void) fprintf(fp, "-h hc-name=%s", rd->r_hcname);
1260 		hcport_print(rd, linebuf, sizeof (linebuf));
1261 
1262 		if (linebuf[0] != '\0')
1263 			(void) fprintf(fp, ",hc-port=%s", linebuf);
1264 		(void) fprintf(fp, " ");
1265 	}
1266 
1267 	conndrain = rd->r_conndrain;
1268 	nat_timeout = rd->r_nat_timeout;
1269 	sticky_timeout = rd->r_sticky_timeout;
1270 	if (conndrain != 0 || nat_timeout != 0 || sticky_timeout != 0) {
1271 		int	cnt = 0;
1272 
1273 		(void) fprintf(fp, "-t ");
1274 		if (conndrain != 0) {
1275 			cnt++;
1276 			(void) fprintf(fp, "conn-drain=%u", conndrain);
1277 		}
1278 		if (nat_timeout != 0) {
1279 			if (cnt > 0)
1280 				(void) fprintf(fp, ",");
1281 			cnt++;
1282 			(void) fprintf(fp, "nat-timeout=%u", nat_timeout);
1283 		}
1284 		if (sticky_timeout != 0) {
1285 			if (cnt > 0)
1286 				(void) fprintf(fp, ",");
1287 			(void) fprintf(fp, "persist-timeout=%u",
1288 			    sticky_timeout);
1289 		}
1290 		(void) fprintf(fp, " ");
1291 	}
1292 
1293 	if (fprintf(fp, "-o servergroup=%s %s\n", rd->r_sgname, rd->r_name)
1294 	    < 0 || fflush(fp) == EOF)
1295 		return (ILB_STATUS_WRITE);
1296 
1297 	return (ILB_STATUS_OK);
1298 }
1299 
1300 ilbadm_status_t
1301 ilbadm_export_rules(ilb_handle_t h, FILE *fp)
1302 {
1303 	ilb_status_t	rclib;
1304 	ilbadm_status_t	rc = ILBADM_OK;
1305 	ilbadm_rl_exp_arg_t	arg;
1306 
1307 	arg.fp = fp;
1308 
1309 	rclib = ilb_walk_rules(h, ilbadm_export_rl, NULL, (void *)&arg);
1310 	if (rclib != ILB_STATUS_OK)
1311 		rc = ILBADM_LIBERR;
1312 	return (rc);
1313 }
1314