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