# # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. # # Copyright (c) 2018, Joyent, Inc. PROG= ilbadm OBJS = ilbadm.o ilbadm_sg.o ilbadm_rules.o ilbadm_hc.o OBJS += ilbadm_subr.o ilbadm_import.o ilbadm_nat.o ilbadm_stats.o SRCS= $(OBJS:.o=.c) include ../../../Makefile.cmd include ../../../Makefile.ctf include ../../Makefile.cmd-inet LDLIBS += -lcmdutils -lsocket -lnsl -lilb -lkstat -lofmt CPPFLAGS += -I$(SRC)/lib/libilb/common -I$(SRC)/uts/common CERRWARN += -Wno-switch CERRWARN += -Wno-parentheses CERRWARN += $(CNOWARN_UNINIT) # Hammerhead: Suppress pointer type mismatch warnings in legacy code CERRWARN += -Wno-incompatible-pointer-types # not linted SMATCH=off CSTD = $(CSTD_GNU99) POFILES = $(OBJS:%.o=%.po) POFILE = $(PROG)_all.po .KEEP_STATE: .PARALLEL: all: $(PROG) $(PROG): $(OBJS) $(LINK.c) -o $@ $(OBJS) $(LDLIBS) $(POST_PROCESS) $(POFILE): $(POFILES) $(RM) $@ cat $(POFILES) > $@ install: all $(ROOTUSRSBINPROG) clean: $(RM) $(OBJS) $(POFILES) lint: $(SRCS) $(LINT.c) $(SRCS) $(LDLIBS) check: $(SRCS) $(PROG).h $(CSTYLE) -pP $(SRCS) $(PROG).h $(HDRCHK) $(PROG).h include ../../../Makefile.targ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include #include #include "ilbadm.h" /* * Error strings for error values returned by ilbadm functions */ const char * ilbadm_errstr(ilbadm_status_t rc) { switch (rc) { case ILBADM_OK: return (gettext("no error")); case ILBADM_FAIL: return (gettext("processing of command failed")); case ILBADM_ENOMEM: return (gettext("memory allocation failure")); case ILBADM_EINVAL: return (gettext("invalid value - refer to ilbadm(8)")); case ILBADM_HCPRINT: return (gettext("failed to print healthcheck values")); case ILBADM_INVAL_AF: return (gettext("address family is invalid")); case ILBADM_INVAL_PORT: return (gettext("port value is invalid")); case ILBADM_INVAL_SRVID: return (gettext("server ID is invalid")); case ILBADM_INVAL_ADDR: return (gettext("address is invalid")); case ILBADM_INVAL_ARGS: return (gettext("invalid/incompatible keywords - refer to" " ilbadm(8)")); case ILBADM_ENOSGNAME: return (gettext("servergroup name missing")); case ILBADM_ENORULE: return (gettext("rule name missing or specified" " rule not found")); case ILBADM_ENOSERVER: return (gettext("server name missing or specified" " server not found")); case ILBADM_INVAL_ALG: return (gettext("LB algorithm is invalid")); case ILBADM_ENOPROTO: return (gettext("protocol does not exist in" " protocol database")); case ILBADM_ENOSERVICE: return (gettext("servicename does not exist in nameservices")); case ILBADM_INVAL_OPER: return (gettext("operation type is invalid")); case ILBADM_INVAL_KEYWORD: return (gettext("keyword is invalid - please refer" " to ilbadm(8)")); case ILBADM_ASSIGNREQ: return (gettext("assignment '=' missing")); case ILBADM_NORECURSIVE: return (gettext("recursive import not allowed")); case ILBADM_INVAL_COMMAND: return (gettext("subcommand is invalid - please refer" " to ilbadm(8)")); case ILBADM_ENOPROXY: return (gettext("proxy-src is missing")); case ILBADM_INVAL_PROXY: return (gettext("proxy-src not allowed")); case ILBADM_ENOOPTION: return (gettext("mandatory argument(s) missing - refer" " to ilbadm(8)")); case ILBADM_TOOMANYIPADDR: return (gettext("address range contains more than 255" " IP addresses")); case ILBADM_EXPORTFAIL: return (gettext("could not export servergroup because" " of lack of space")); case ILBADM_INVAL_SYNTAX: return (gettext("syntax failure - refer to ilbadm(8)")); case ILBADM_NOKEYWORD_VAL: return (gettext("missing value")); case ILBADM_LIBERR: return (gettext("library error")); default: return (gettext("unknown error")); } } /* PRINTFLIKE1 */ void ilbadm_err(const char *format, ...) { /* similar to warn() of dladm.c */ va_list alist; (void) fprintf(stderr, "ilbadm: "); va_start(alist, format); (void) vfprintf(stderr, format, alist); va_end(alist); (void) fprintf(stderr, "\n"); } void Usage(char *name) { (void) fprintf(stderr, gettext("Usage:\n")); print_cmdlist_short(basename(name), stderr); exit(1); } static void print_version(char *name) { (void) printf("%s %s\n", basename(name), ILBADM_VERSION); (void) printf(gettext(ILBADM_COPYRIGHT)); exit(0); } void unknown_opt(char **argv, int optind) { ilbadm_err(gettext("bad or misplaced option %s"), argv[optind]); exit(1); } void incomplete_cmdline(char *name) { ilbadm_err(gettext("the command line is incomplete " "(more arguments expected)")); Usage(name); } static void bad_importfile(char *name, char *filename) { ilbadm_err(gettext("file %s cannot be opened for reading"), filename); Usage(name); } int main(int argc, char *argv[]) { ilbadm_status_t rc; int c; int fd = -1; int flags = 0; (void) setlocale(LC_ALL, ""); #if !defined(TEXT_DOMAIN) #define TEXT_DOMAIN "SYS_TEST" #endif (void) textdomain(TEXT_DOMAIN); /* handle global options (-?, -V) first */ while ((c = getopt(argc, argv, ":V:?")) != -1) { switch ((char)c) { case 'V': print_version(argv[0]); /* not reached */ break; case '?': Usage(argv[0]); /* not reached */ break; default: unknown_opt(argv, optind - 1); /* not reached */ break; } } if (optind >= argc) incomplete_cmdline(argv[0]); /* * we can import from a given file (argv[2]) or from * stdin (if no file given) */ if (strcasecmp(argv[1], "import-config") == 0 || strcasecmp(argv[1], "import-cf") == 0) { int shift = 0; if (argc > 2 && strcmp(argv[2], "-p") == 0) { shift++; flags |= ILBADM_IMPORT_PRESERVE; } if (argc - shift < 3) fd = 0; else if ((fd = open(argv[2+shift], O_RDONLY)) == -1) bad_importfile(argv[0], argv[2+shift]); } argv++; argc--; /* * re-set optind for next callers of getopt() - they all believe they're * the first. */ optind = 1; optopt = 0; rc = ilbadm_import(fd, argc, argv, flags); /* * The error messages have been printed out, using * ilbadm_errstr() and ilb_errstr(), before we get here. * So just set the exit value */ if (rc != ILBADM_OK) return (1); /* success */ return (0); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _ILBADM_H #define _ILBADM_H #ifdef __cplusplus extern "C" { #endif #include #include #include #include #include #include #include #include #define ILBADM_VERSION "1.0" #define ILBADM_COPYRIGHT \ "Copyright 2009 Sun Microsystems, Inc. All rights reserved.\n" \ "Use is subject to license terms.\n" /* * flag values */ #define OPT_VALUE_LIST 0x0001 #define OPT_IP_RANGE 0x0002 #define OPT_PORTS 0x0004 #define OPT_PORTS_ONLY 0x0008 #define OPT_NAT 0x0010 #define OPT_NUMERIC_ONLY 0x0020 #define ILBD_BAD_VAL (-1) #define ILBADM_LIST_FULL 0x0001 #define ILBADM_LIST_PARSE 0x0002 #define ILBADM_LIST_ENABLED 0x0004 #define ILBADM_LIST_NOENABLED (~ILBADM_LIST_ENABLED) #define ILBADM_LIST_DISABLED 0x0008 #define ILBADM_LIST_NODISABLED (~ILBADM_LIST_DISABLED) #define ILBADM_IMPORT_PRESERVE 0x1000 #define V6_ADDRONLY 0x1 /* don't print surrounding "[]"s */ #define ILB_SRVID_SZ (ILB_NAMESZ - 5) #define ILBD_NAMESZ ILB_NAMESZ #define ILB_MAX_PORT UINT16_MAX typedef enum { ILBADM_OK = 0, ILBADM_ASSIGNREQ, /* assignment '=' required */ ILBADM_EINVAL, /* invalid value */ ILBADM_ENOMEM, /* malloc failed */ ILBADM_ENOOPTION, /* mandatory option missing */ ILBADM_ENOPROTO, /* protocol not found in database */ ILBADM_ENOPROXY, /* proxy-src is missing */ ILBADM_ENOSERVICE, /* servicename not found in database */ ILBADM_ENOSGNAME, /* servergroup name missing */ ILBADM_ENORULE, /* rulename missing or no such rule */ ILBADM_ENOSERVER, /* rulename missing or no such rule */ ILBADM_EXPORTFAIL, /* too little space to do export servergroup */ ILBADM_FAIL, /* processing of command failed */ ILBADM_HCPRINT, /* failed to print healthcheck */ ILBADM_INVAL_ADDR, /* invalid address */ ILBADM_INVAL_AF, /* invalid address family */ ILBADM_INVAL_ALG, /* LB algorithm failure */ ILBADM_INVAL_ARGS, /* invalid arguments to command */ ILBADM_INVAL_COMMAND, /* invalid command */ ILBADM_INVAL_KEYWORD, /* invalid keyword */ ILBADM_INVAL_OPER, /* invalid operation type */ ILBADM_INVAL_PORT, /* invalid value specified for port */ ILBADM_INVAL_PROXY, /* proxy-src not allowed */ ILBADM_INVAL_SYNTAX, /* syntax error */ ILBADM_INVAL_SRVID, /* server id is invalid (missing "_" ?) */ ILBADM_LIBERR, /* translation of libilb errors. We also */ /* set it in ilbadm fuctions to indicate */ /* printing of non-generic error messages */ ILBADM_NORECURSIVE, /* recursive import not allowed */ ILBADM_TOOMANYIPADDR, /* too many addresses */ ILBADM_NOKEYWORD_VAL /* no value specified for a keyword */ } ilbadm_status_t; typedef enum { ILB_KEY_BAD = -1, ILB_KEY_SERVER, ILB_KEY_SERVRANGE, /* pseudo-key for SG creation */ ILB_KEY_SERVERID, ILB_KEY_VIP, ILB_KEY_PORT, ILB_KEY_PROTOCOL, ILB_KEY_IPVERSION, ILB_KEY_ALGORITHM, ILB_KEY_TYPE, ILB_KEY_SERVERGROUP, ILB_KEY_HEALTHCHECK, ILB_KEY_HCPORT, ILB_KEY_SRC, ILB_KEY_STICKY, ILB_KEY_CONNDRAIN, /* otional timers ... */ ILB_KEY_NAT_TO, ILB_KEY_STICKY_TO, ILB_KEY_HC_TEST, ILB_KEY_HC_COUNT, ILB_KEY_HC_INTERVAL, ILB_KEY_HC_TIMEOUT } ilbadm_key_code_t; /* * we need a few codes for commands, can't use libilb ones */ typedef enum { cmd_create_sg, cmd_add_srv, cmd_rem_srv, cmd_enable_rule, cmd_disable_rule, cmd_enable_server, cmd_disable_server } ilbadm_cmd_t; /* filched from snoop_ether.c */ typedef struct val_type { int v_type; char v_name[20]; char v_alias[8]; /* undocumented */ } ilbadm_val_type_t; typedef struct key_names { ilbadm_key_code_t k_key; char k_name[20]; char k_alias[12]; /* undocumented */ } ilbadm_key_name_t; typedef struct servnode { list_node_t s_link; ilb_server_data_t s_spec; } ilbadm_servnode_t; typedef struct sgroup { list_t sg_serv_list; /* list of servnode_t elements */ int sg_count; char *sg_name; } ilbadm_sgroup_t; typedef struct cmd_hlp { char *h_help; } ilbadm_cmd_help_t; typedef ilbadm_status_t (* cmdfunc_t)(int, char **); typedef struct cmd_names { char c_name[25]; char c_alias[20]; /* undocumented */ cmdfunc_t c_action; ilbadm_cmd_help_t *c_help; /* for "usage" */ } ilbadm_cmd_desc_t; ilbadm_status_t ilbadm_add_server_to_group(int, char **); ilbadm_status_t ilbadm_create_servergroup(int, char **); ilbadm_status_t ilbadm_destroy_servergroup(int, char **); ilbadm_status_t ilbadm_rem_server_from_group(int, char **); ilbadm_status_t ilbadm_create_rule(int, char **); ilbadm_status_t ilbadm_destroy_rule(int, char **); ilbadm_status_t ilbadm_enable_rule(int, char **); ilbadm_status_t ilbadm_disable_rule(int, char **); ilbadm_status_t ilbadm_show_server(int, char **); ilbadm_status_t ilbadm_enable_server(int, char **); ilbadm_status_t ilbadm_disable_server(int, char **); ilbadm_status_t ilbadm_show_servergroups(int, char **); ilbadm_status_t ilbadm_show_rules(int, char **); ilbadm_status_t ilbadm_show_stats(int, char **); ilbadm_status_t ilbadm_create_hc(int, char **); ilbadm_status_t ilbadm_destroy_hc(int, char **); ilbadm_status_t ilbadm_show_hc(int, char **); ilbadm_status_t ilbadm_show_hc_result(int, char **); ilbadm_status_t ilbadm_noimport(int, char **); ilbadm_status_t ilbadm_show_nat(int, char **); ilbadm_status_t ilbadm_show_persist(int, char **); ilbadm_status_t i_parse_optstring(char *, void *, ilbadm_key_name_t *, int, int *); ilbadm_servnode_t *i_new_sg_elem(ilbadm_sgroup_t *); ilbadm_status_t ilbadm_import(int, int, char *[], int); ilbadm_status_t ilbadm_export(int, char *[]); ilbadm_status_t ilbadm_export_servergroups(ilb_handle_t h, FILE *); ilbadm_status_t ilbadm_export_hc(ilb_handle_t h, FILE *); ilbadm_status_t ilbadm_export_rules(ilb_handle_t h, FILE *); ilbadm_status_t i_check_rule_spec(ilb_rule_data_t *); ilbadm_status_t ilbadm_set_netmask(char *, ilb_ip_addr_t *, int); int ilbadm_mask_to_prefixlen(ilb_ip_addr_t *); void print_cmdlist_short(char *, FILE *); /* Hammerhead: Use int64_t to match library declaration in libilb.h */ extern int ilb_cmp_ipaddr(ilb_ip_addr_t *, ilb_ip_addr_t *, int64_t *); void ip2str(ilb_ip_addr_t *, char *, size_t, int); char *i_str_from_val(int, ilbadm_val_type_t *); char *ilbadm_key_to_opt(ilbadm_key_code_t); void Usage(char *); void unknown_opt(char **, int); const char *ilbadm_errstr(ilbadm_status_t); void ilbadm_err(const char *format, ...); #ifdef __cplusplus } #endif #endif /* _ILBADM_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include #include #include #include "ilbadm.h" extern int optind, optopt, opterr; extern char *optarg; typedef struct hc_export_arg { FILE *fp; } hc_export_arg_t; /* Maximum columns for printing hc output. */ #define SHOW_HC_COLS 80 /* OFMT call back to print out a hc server result field. */ static boolean_t print_hc_result(ofmt_arg_t *, char *, uint_t); /* ID to indicate which field to be printed. */ enum hc_print_id { hc_of_rname, hc_of_hname, hc_of_sname, hc_of_status, hc_of_fail_cnt, hc_of_lasttime, hc_of_nexttime, hc_of_rtt, hc_of_name, hc_of_timeout, hc_of_count, hc_of_interval, hc_of_def_ping, hc_of_test }; /* * Fields of a hc server result. The sum of all fields' width is SHOW_HC_COLS. */ static ofmt_field_t hc_results[] = { {"RULENAME", 14, hc_of_rname, print_hc_result}, {"HCNAME", 14, hc_of_hname, print_hc_result}, {"SERVERID", 14, hc_of_sname, print_hc_result}, {"STATUS", 9, hc_of_status, print_hc_result}, {"FAIL", 5, hc_of_fail_cnt, print_hc_result}, {"LAST", 9, hc_of_lasttime, print_hc_result}, {"NEXT", 9, hc_of_nexttime, print_hc_result}, {"RTT", 6, hc_of_rtt, print_hc_result}, {NULL, 0, 0, NULL} }; /* OFMT call back to print out a hc info field. */ static boolean_t print_hc(ofmt_arg_t *, char *, uint_t); /* * Fields of a hc info. The sume of all fields' width is SHOW_HC_COLS. */ static ofmt_field_t hc_fields[] = { {"HCNAME", 14, hc_of_name, print_hc}, {"TIMEOUT", 8, hc_of_timeout, print_hc}, {"COUNT", 8, hc_of_count, print_hc}, {"INTERVAL", 9, hc_of_interval, print_hc}, {"DEF_PING", 9, hc_of_def_ping, print_hc}, {"TEST", 32, hc_of_test, print_hc}, {NULL, 0, 0, NULL} }; static boolean_t print_hc(ofmt_arg_t *of_arg, char *buf, uint_t bufsize) { enum hc_print_id id = of_arg->ofmt_id; ilb_hc_info_t *info = (ilb_hc_info_t *)of_arg->ofmt_cbarg; switch (id) { case hc_of_name: (void) strlcpy(buf, info->hci_name, bufsize); break; case hc_of_timeout: (void) snprintf(buf, bufsize, "%d", info->hci_timeout); break; case hc_of_count: (void) snprintf(buf, bufsize, "%d", info->hci_count); break; case hc_of_interval: (void) snprintf(buf, bufsize, "%d", info->hci_interval); break; case hc_of_def_ping: (void) snprintf(buf, bufsize, "%c", info->hci_def_ping ? 'Y' : 'N'); break; case hc_of_test: (void) snprintf(buf, bufsize, "%s", info->hci_test); break; } return (B_TRUE); } /* Call back to ilb_walk_hc(). */ /* ARGSUSED */ static ilb_status_t ilbadm_print_hc(ilb_handle_t h, ilb_hc_info_t *hc_info, void *arg) { ofmt_handle_t ofmt_h = arg; ofmt_print(ofmt_h, hc_info); return (ILB_STATUS_OK); } /* * Print out health check objects given their name. * Or print out all health check objects if no name given. */ /* ARGSUSED */ ilbadm_status_t ilbadm_show_hc(int argc, char *argv[]) { ilb_handle_t h = ILB_INVALID_HANDLE; ilb_status_t rclib; ofmt_handle_t ofmt_h; ofmt_status_t ofmt_ret; if ((ofmt_ret = ofmt_open("all", hc_fields, 0, SHOW_HC_COLS, &ofmt_h)) != OFMT_SUCCESS) { char err_buf[SHOW_HC_COLS]; ilbadm_err(gettext("ofmt_open failed: %s"), ofmt_strerror(ofmt_h, ofmt_ret, err_buf, SHOW_HC_COLS)); return (ILBADM_LIBERR); } rclib = ilb_open(&h); if (rclib != ILB_STATUS_OK) goto out; if (argc == 1) { rclib = ilb_walk_hc(h, ilbadm_print_hc, ofmt_h); } else { ilb_hc_info_t hc_info; int i; for (i = 1; i < argc; i++) { rclib = ilb_get_hc_info(h, argv[i], &hc_info); if (rclib == ILB_STATUS_OK) ofmt_print(ofmt_h, &hc_info); else break; } } out: ofmt_close(ofmt_h); if (h != ILB_INVALID_HANDLE) (void) ilb_close(h); if (rclib != ILB_STATUS_OK) { ilbadm_err(ilb_errstr(rclib)); return (ILBADM_LIBERR); } return (ILBADM_OK); } static boolean_t print_hc_result(ofmt_arg_t *of_arg, char *buf, uint_t bufsize) { enum hc_print_id id = of_arg->ofmt_id; ilb_hc_srv_t *srv = (ilb_hc_srv_t *)of_arg->ofmt_cbarg; struct tm tv; switch (id) { case hc_of_rname: (void) strlcpy(buf, srv->hcs_rule_name, bufsize); break; case hc_of_hname: (void) strlcpy(buf, srv->hcs_hc_name, bufsize); break; case hc_of_sname: (void) strlcpy(buf, srv->hcs_ID, bufsize); break; case hc_of_status: switch (srv->hcs_status) { case ILB_HCS_UNINIT: (void) strlcpy(buf, "un-init", bufsize); break; case ILB_HCS_UNREACH: (void) strlcpy(buf, "unreach", bufsize); break; case ILB_HCS_ALIVE: (void) strlcpy(buf, "alive", bufsize); break; case ILB_HCS_DEAD: (void) strlcpy(buf, "dead", bufsize); break; case ILB_HCS_DISABLED: (void) strlcpy(buf, "disabled", bufsize); break; } break; case hc_of_fail_cnt: (void) snprintf(buf, bufsize, "%u", srv->hcs_fail_cnt); break; case hc_of_lasttime: if (localtime_r(&srv->hcs_lasttime, &tv) == NULL) return (B_FALSE); (void) snprintf(buf, bufsize, "%02d:%02d:%02d", tv.tm_hour, tv.tm_min, tv.tm_sec); break; case hc_of_nexttime: if (srv->hcs_status == ILB_HCS_DISABLED) break; if (localtime_r(&srv->hcs_nexttime, &tv) == NULL) return (B_FALSE); (void) snprintf(buf, bufsize, "%02d:%02d:%02d", tv.tm_hour, tv.tm_min, tv.tm_sec); break; case hc_of_rtt: (void) snprintf(buf, bufsize, "%u", srv->hcs_rtt); break; } return (B_TRUE); } /* Call back to ilbd_walk_hc_srvs(). */ /* ARGSUSED */ static ilb_status_t ilbadm_print_hc_result(ilb_handle_t h, ilb_hc_srv_t *srv, void *arg) { ofmt_handle_t ofmt_h = arg; ofmt_print(ofmt_h, srv); return (ILB_STATUS_OK); } /* * Output hc result of a specified rule or all rules. */ ilbadm_status_t ilbadm_show_hc_result(int argc, char *argv[]) { ilb_handle_t h = ILB_INVALID_HANDLE; ilb_status_t rclib = ILB_STATUS_OK; int i; ofmt_handle_t ofmt_h; ofmt_status_t ofmt_ret; /* ilbadm show-hc-result [rule-name] */ if (argc < 1) { ilbadm_err(gettext("usage: ilbadm show-hc-result" " [rule-name]")); return (ILBADM_LIBERR); } if ((ofmt_ret = ofmt_open("all", hc_results, 0, SHOW_HC_COLS, &ofmt_h)) != OFMT_SUCCESS) { char err_buf[SHOW_HC_COLS]; ilbadm_err(gettext("ofmt_open failed: %s"), ofmt_strerror(ofmt_h, ofmt_ret, err_buf, SHOW_HC_COLS)); return (ILBADM_LIBERR); } rclib = ilb_open(&h); if (rclib != ILB_STATUS_OK) goto out; /* If no rule name is given, show results for all rules. */ if (argc == 1) { rclib = ilb_walk_hc_srvs(h, ilbadm_print_hc_result, NULL, ofmt_h); } else { for (i = 1; i < argc; i++) { rclib = ilb_walk_hc_srvs(h, ilbadm_print_hc_result, argv[i], ofmt_h); if (rclib != ILB_STATUS_OK) break; } } out: ofmt_close(ofmt_h); if (h != ILB_INVALID_HANDLE) (void) ilb_close(h); if (rclib != ILB_STATUS_OK) { ilbadm_err(ilb_errstr(rclib)); return (ILBADM_LIBERR); } return (ILBADM_OK); } #define ILBADM_DEF_HC_COUNT 3 #define ILBADM_DEF_HC_INTERVAL 30 /* in sec */ #define ILBADM_DEF_HC_TIMEOUT 5 /* in sec */ static ilbadm_key_name_t hc_parse_keys[] = { {ILB_KEY_HC_TEST, "hc-test", "hc-test"}, {ILB_KEY_HC_COUNT, "hc-count", "hc-count"}, {ILB_KEY_HC_TIMEOUT, "hc-timeout", "hc-tout"}, {ILB_KEY_HC_INTERVAL, "hc-interval", "hc-intl"}, {ILB_KEY_BAD, "", ""} }; static ilbadm_status_t ilbadm_hc_parse_arg(char *arg, ilb_hc_info_t *hc) { ilbadm_status_t ret; /* set default value for count, interval, timeout */ hc->hci_count = ILBADM_DEF_HC_COUNT; hc->hci_interval = ILBADM_DEF_HC_INTERVAL; hc->hci_timeout = ILBADM_DEF_HC_TIMEOUT; hc->hci_test[0] = '\0'; ret = i_parse_optstring(arg, hc, hc_parse_keys, 0, NULL); if (ret != ILBADM_OK && ret != ILBADM_LIBERR) { ilbadm_err(ilbadm_errstr(ret)); return (ILBADM_LIBERR); } if (hc->hci_test[0] == '\0' && ret != ILBADM_LIBERR) { ilbadm_err("hc-test: missing"); return (ILBADM_LIBERR); } return (ret); } /* ARGSUSED */ ilbadm_status_t ilbadm_create_hc(int argc, char *argv[]) { ilb_handle_t h = ILB_INVALID_HANDLE; ilb_hc_info_t hc_info; ilbadm_status_t ret = ILBADM_OK; ilb_status_t rclib; int c; hc_info.hci_def_ping = B_TRUE; while ((c = getopt(argc, argv, ":h:n")) != -1) { if (c == 'h') { ret = ilbadm_hc_parse_arg(optarg, &hc_info); if (ret != ILBADM_OK) return (ret); } else if (c == 'n') { hc_info.hci_def_ping = B_FALSE; } else { ilbadm_err(gettext("bad argument %c"), c); return (ILBADM_LIBERR); } } if (optind >= argc) { ilbadm_err(gettext("usage: ilbadm" " create-healthcheck [-n] -h" " hc-test=val[,hc-timeout=val][,hc-count=va]" "[,hc-interval=val] hc-name")); return (ILBADM_FAIL); } if (strlen(argv[optind]) > ILBD_NAMESZ - 1) { ilbadm_err(gettext("health check object name %s is too long - " "must not exceed %d chars"), argv[optind], ILBD_NAMESZ - 1); return (ILBADM_FAIL); } if (((strcasecmp(hc_info.hci_test, ILB_HC_STR_UDP) == 0) || (strcasecmp(hc_info.hci_test, ILB_HC_STR_PING) == 0)) && !(hc_info.hci_def_ping)) { ilbadm_err(gettext("cannot disable default PING" " for this test")); return (ILBADM_LIBERR); } rclib = ilb_open(&h); if (rclib != ILB_STATUS_OK) goto out; (void) strlcpy(hc_info.hci_name, argv[optind], sizeof (hc_info.hci_name)); rclib = ilb_create_hc(h, &hc_info); out: if (h != ILB_INVALID_HANDLE) (void) ilb_close(h); if (rclib != ILB_STATUS_OK) { ilbadm_err(ilb_errstr(rclib)); ret = ILBADM_LIBERR; } return (ret); } ilbadm_status_t ilbadm_destroy_hc(int argc, char *argv[]) { ilb_handle_t h = ILB_INVALID_HANDLE; ilb_status_t rclib; ilbadm_status_t ret = ILBADM_OK; int i; if (argc < 2) { ilbadm_err(gettext("usage: ilbadm" " delete-healthcheck hc-name ...")); return (ILBADM_LIBERR); } rclib = ilb_open(&h); if (rclib != ILB_STATUS_OK) goto out; for (i = 1; i < argc; i++) { rclib = ilb_destroy_hc(h, argv[i]); if (rclib != ILB_STATUS_OK) break; } out: if (h != ILB_INVALID_HANDLE) (void) ilb_close(h); if (rclib != ILB_STATUS_OK) { ilbadm_err(ilb_errstr(rclib)); ret = ILBADM_LIBERR; } return (ret); } /* * Since this function is used by libilb function, it * must return libilb errors */ /* ARGSUSED */ ilb_status_t ilbadm_export_hcinfo(ilb_handle_t h, ilb_hc_info_t *hc_info, void *arg) { FILE *fp = ((hc_export_arg_t *)arg)->fp; int count = 0; int ret; /* * a test name "PING" implies "no default ping", so we only * print -n if the test is NOT "PING" */ if (hc_info->hci_def_ping == B_FALSE && strncasecmp(hc_info->hci_test, "PING", 5) != 0) (void) fprintf(fp, "create-healthcheck -n -h "); else (void) fprintf(fp, "create-healthcheck -h "); if (*hc_info->hci_test != '\0') { (void) fprintf(fp, "hc-test=%s", hc_info->hci_test); count++; } if (hc_info->hci_timeout != 0) { if (count++ > 0) (void) fprintf(fp, ","); (void) fprintf(fp, "hc-timeout=%d", hc_info->hci_timeout); } if (hc_info->hci_count != 0) { if (count++ > 0) (void) fprintf(fp, ","); (void) fprintf(fp, "hc-count=%d", hc_info->hci_count); } if (hc_info->hci_interval != 0) { if (count > 0) (void) fprintf(fp, ","); (void) fprintf(fp, "hc-interval=%d", hc_info->hci_interval); } /* * if any of the above writes fails, then, we assume, so will * this one; so it's sufficient to test once */ ret = fprintf(fp, " %s\n", hc_info->hci_name); if (ret < 0) goto out_fail; ret = fflush(fp); out_fail: if (ret < 0) return (ILB_STATUS_WRITE); return (ILB_STATUS_OK); } ilbadm_status_t ilbadm_export_hc(ilb_handle_t h, FILE *fp) { ilb_status_t rclib; ilbadm_status_t ret = ILBADM_OK; hc_export_arg_t arg; arg.fp = fp; rclib = ilb_walk_hc(h, ilbadm_export_hcinfo, (void *)&arg); if (rclib != ILB_STATUS_OK) { ilbadm_err(ilb_errstr(rclib)); ret = ILBADM_LIBERR; } return (ret); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include #include #include #include "ilbadm.h" static ilbadm_cmd_help_t create_sg_help = { "[-s server=hostspec[:portspec...]] groupname" }; static ilbadm_cmd_help_t create_rule_help = { "[-e] [-p] -i vip=value,port=value[,protocol=value] \n" \ " -m lbalg=value,type=value[,proxy-src=ip-range][,pmask=mask] \n"\ " -h hc-name=value[,hc-port=value]] \n" \ " [-t [conn-drain=N][,nat-timeout=N][,persist-timeout=N]] \n" \ " -o servergroup=value name" }; static ilbadm_cmd_help_t destroy_rule_help = { "-a | name ..." }; static ilbadm_cmd_help_t add_server_help = { "-s server=value[,value ...] servergroup" }; static ilbadm_cmd_help_t remove_server_help = { "-s server=value[,value ...] servergroup" }; static ilbadm_cmd_help_t disable_server_help = { "server ... " }; static ilbadm_cmd_help_t enable_server_help = { "server ..." }; static ilbadm_cmd_help_t enable_rule_help = { "[name ... ]" }; static ilbadm_cmd_help_t disable_rule_help = { "[name ... ]" }; static ilbadm_cmd_help_t show_server_help = { "[[-p] -o field[,field...]] [rulename ... ]" }; static ilbadm_cmd_help_t showstats_help = { "[-p] -o field[,...]] [-tdAvi]\n" \ " [-r rulename|-s servername] [interval [count]]" }; static ilbadm_cmd_help_t show_nat_help = { "[count]" }; static ilbadm_cmd_help_t show_persist_help = { "[count]" }; static ilbadm_cmd_help_t show_hc_help = { "[hc-name]" }; static ilbadm_cmd_help_t create_hc_help = { "[-n] -h hc-test=value[,hc-timeout=value]\n" \ " [,hc-count=value][,hc-interval=value] hcname" }; static ilbadm_cmd_help_t destroy_hc_help = { "name ..." }; static ilbadm_cmd_help_t show_hc_result_help = { "[rule-name]" }; static ilbadm_cmd_help_t show_rule_help = { "[-e|-d] [-f |[-p] -o key[,key ...]] [name ...]" }; static ilbadm_cmd_help_t destroy_servergroup_help = { "groupname" }; static ilbadm_cmd_help_t show_servergroup_help = { "[[-p] -o field[,field]] [name]" }; static ilbadm_cmd_help_t export_config_help = { "[filename]" }; static ilbadm_cmd_help_t import_config_help = { "[-p] [filename]" }; static ilbadm_cmd_desc_t ilbadm_cmds[] = { {"create-rule", "create-rl", ilbadm_create_rule, &create_rule_help}, {"delete-rule", "delete-rl", ilbadm_destroy_rule, &destroy_rule_help}, {"enable-rule", "enable-rl", ilbadm_enable_rule, &enable_rule_help}, {"disable-rule", "disable-rl", ilbadm_disable_rule, &disable_rule_help}, {"show-rule", "show-rl", ilbadm_show_rules, &show_rule_help}, {"create-servergroup", "create-sg", ilbadm_create_servergroup, &create_sg_help}, {"delete-servergroup", "delete-sg", ilbadm_destroy_servergroup, &destroy_servergroup_help}, {"show-servergroup", "show-sg", ilbadm_show_servergroups, &show_servergroup_help}, {"add-server", "add-srv", ilbadm_add_server_to_group, &add_server_help}, {"remove-server", "remove-srv", ilbadm_rem_server_from_group, &remove_server_help}, {"disable-server", "disable-srv", ilbadm_disable_server, &disable_server_help}, {"enable-server", "enable-srv", ilbadm_enable_server, &enable_server_help}, {"show-server", "show-srv", ilbadm_show_server, &show_server_help}, {"show-healthcheck", "show-hc", ilbadm_show_hc, &show_hc_help}, {"create-healthcheck", "create-hc", ilbadm_create_hc, &create_hc_help}, {"delete-healthcheck", "delete-hc", ilbadm_destroy_hc, &destroy_hc_help}, {"show-hc-result", "show-hc-res", ilbadm_show_hc_result, &show_hc_result_help}, {"export-config", "export-cf", ilbadm_export, &export_config_help}, {"import-config", "import-cf", ilbadm_noimport, &import_config_help}, {"show-statistics", "show-stats", ilbadm_show_stats, &showstats_help}, {"show-nat", "show-nat", ilbadm_show_nat, &show_nat_help}, {"show-persist", "show-pt", ilbadm_show_persist, &show_persist_help}, {"", "", NULL, NULL} }; /* ARGSUSED */ ilbadm_status_t ilbadm_noimport(int argc, char *argv[]) { ilbadm_err(ilbadm_errstr(ILBADM_NORECURSIVE)); return (ILBADM_LIBERR); } static void print_cmd_short(char *name, FILE *fp, ilbadm_cmd_desc_t *cmd) { char *h; while (cmd->c_name[0] != '\0') { if (cmd->c_help != NULL && (h = cmd->c_help->h_help) != NULL) (void) fprintf(fp, "%s %s|%s %s\n", name, cmd->c_name, cmd->c_alias, h); else (void) fprintf(fp, "%s %s|%s\n", name, cmd->c_name, cmd->c_alias); cmd++; } } void print_cmdlist_short(char *name, FILE *fp) { print_cmd_short(name, fp, ilbadm_cmds); } #define IMPORT_FILE 0x1 static void match_cmd(char *name, ilbadm_cmd_desc_t *cmds, cmdfunc_t *action, int flags) { ilbadm_cmd_desc_t *cmd; if ((flags & IMPORT_FILE) == IMPORT_FILE) { if (strcasecmp(name, "export-config") == 0 || strcasecmp(name, "export-cf") == 0) { ilbadm_err(gettext("export from import file" " not allowed")); exit(1); } } for (cmd = &cmds[0]; cmd->c_name[0] != '\0'; cmd++) { if (strncasecmp(cmd->c_name, name, sizeof (cmd->c_name)) == 0 || strncasecmp(cmd->c_alias, name, sizeof (cmd->c_alias)) == 0) break; } *action = cmd->c_action; } /* * read and parse commandline */ static ilbadm_status_t ilb_import_cmdline(int argc, char *argv[], int flags) { ilbadm_status_t rc = ILBADM_OK; cmdfunc_t cmd; match_cmd(argv[0], ilbadm_cmds, &cmd, flags); if (*cmd != NULL) { rc = cmd(argc, argv); } else { rc = ILBADM_INVAL_COMMAND; ilbadm_err(ilbadm_errstr(rc)); } return (rc); } #define CHUNK 10 #define LINESZ 1024 typedef struct { int listsz; char *arglist[1]; } arg_t; static int i_getln_to_argv(FILE *fp, arg_t **ap) { static char *linebuf = NULL; char *stringp, *currp; char delim[] = " \t\n"; int i; arg_t *a = *ap; #define STR_DIFF(s1, s2) (int)((char *)s2 - (char *)s1) #define STR_ADJ_SZ(sz, buf, s) (sz - STR_DIFF(buf, s)) if (linebuf == NULL) if ((linebuf = (char *)malloc(LINESZ)) == NULL) return (0); stringp = currp = linebuf; i = 0; read_next: if (fgets(currp, STR_ADJ_SZ(LINESZ, linebuf, currp), fp) == NULL) return (i); /* ignore lines starting with a # character */ if (*currp == '#') goto read_next; for (; stringp != NULL && currp != NULL; i++) { currp = strsep(&stringp, delim); /* * if there's more than one adjacent delimiters ... */ if (*currp == '\0') { i--; continue; } /* * if we find a '\' at the end of a line, treat * it as a continuation character. */ if (*currp == '\\' && stringp == NULL) { stringp = currp; goto read_next; } if (a == NULL) { a = (arg_t *)malloc(sizeof (*a)); bzero(a, sizeof (*a)); } if (a->listsz <= i) { int sz; a->listsz += CHUNK; sz = sizeof (*a) + ((a->listsz - 1) * sizeof (a->arglist)); a = (arg_t *)realloc(a, sz); *ap = a; } a->arglist[i] = currp; } return (i); } static ilbadm_status_t ilb_import_file(int fd, int flags) { FILE *fp; arg_t *a = NULL; int argcount; ilbadm_status_t rc = ILBADM_OK; if ((fp = fdopen(fd, "r")) == NULL) { ilbadm_err(gettext("cannot import file for reading")); exit(1); } if ((flags & ILBADM_IMPORT_PRESERVE) == 0) { ilb_handle_t h = ILB_INVALID_HANDLE; ilb_status_t rclib; rclib = ilb_open(&h); if (rclib == ILB_STATUS_OK) (void) ilb_reset_config(h); if (h != ILB_INVALID_HANDLE) (void) ilb_close(h); } while ((argcount = i_getln_to_argv(fp, &a)) > 0) { optind = 1; rc = ilb_import_cmdline(argcount, a->arglist, IMPORT_FILE); if (rc != ILBADM_OK) break; } return (rc); } /* * this is the wrapper around everything to do with importing and * parsing either commandline or persistent storage. * if (fd == -1), parse commandline, otherwise use the given fd as input. */ /* ARGSUSED */ ilbadm_status_t ilbadm_import(int fd, int argc, char *argv[], int flags) { ilbadm_status_t rc; if (fd == -1) rc = ilb_import_cmdline(argc, argv, 0); else rc = ilb_import_file(fd, flags); return (rc); } ilbadm_status_t ilbadm_export(int argc, char *argv[]) { ilb_handle_t h = ILB_INVALID_HANDLE; ilbadm_status_t rc = ILBADM_OK; ilb_status_t rclib = ILB_STATUS_OK; int fd; FILE *fp; char *fname = NULL; char tmpfile[MAXPATHLEN]; if (argc < 2) { fd = 1; /* stdout */ *tmpfile = '\0'; } else { fname = argv[1]; (void) snprintf(tmpfile, sizeof (tmpfile), "%sXXXXXX", fname); fd = mkstemp(tmpfile); if (fd == -1) { ilbadm_err(gettext("cannot create working file")); exit(1); } } fp = fdopen(fd, "w"); if (fp == NULL) { ilbadm_err(gettext("cannot open file for writing"), fd); exit(1); } rclib = ilb_open(&h); if (rclib != ILB_STATUS_OK) goto out; rc = ilbadm_export_servergroups(h, fp); if (rc != ILBADM_OK) goto out; rc = ilbadm_export_hc(h, fp); if (rc != ILBADM_OK) goto out; rc = ilbadm_export_rules(h, fp); if (rc != ILBADM_OK) goto out; if (fname != NULL) { if (rename(tmpfile, fname) == -1) { ilbadm_err(gettext("cannot create %s: %s"), fname, strerror(errno)); exit(1); } *tmpfile = '\0'; } out: if (h != ILB_INVALID_HANDLE) (void) ilb_close(h); if ((rc != ILBADM_OK) && (rc != ILBADM_LIBERR)) ilbadm_err(ilbadm_errstr(rc)); (void) fclose(fp); if (*tmpfile != '\0') (void) unlink(tmpfile); return (rc); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include "ilbadm.h" /* * For each iteration through the kernel table, ask for at most NUM_ENTRIES * entries to be returned. */ #define NUM_ENTRIES 500 static void print_nat_info(ilb_nat_info_t *info) { char *tmp; ipaddr_t addr_v4; char addr[INET6_ADDRSTRLEN]; if (info->nat_proto == IPPROTO_TCP) tmp = "TCP"; else if (info->nat_proto == IPPROTO_UDP) tmp = "UDP"; else tmp = "Unknown"; (void) printf("%4s: ", tmp); if (IN6_IS_ADDR_V4MAPPED(&info->nat_out_global)) { IN6_V4MAPPED_TO_IPADDR(&info->nat_out_global, addr_v4); (void) printf("%s.%d > ", inet_ntop(AF_INET, &addr_v4, addr, INET6_ADDRSTRLEN), ntohs(info->nat_out_global_port)); IN6_V4MAPPED_TO_IPADDR(&info->nat_in_global, addr_v4); (void) printf("%s.%d >>> ", inet_ntop(AF_INET, &addr_v4, addr, INET6_ADDRSTRLEN), ntohs(info->nat_in_global_port)); IN6_V4MAPPED_TO_IPADDR(&info->nat_out_local, addr_v4); (void) printf("%s.%d > ", inet_ntop(AF_INET, &addr_v4, addr, INET6_ADDRSTRLEN), ntohs(info->nat_out_local_port)); IN6_V4MAPPED_TO_IPADDR(&info->nat_in_local, addr_v4); (void) printf("%s.%d\n", inet_ntop(AF_INET, &addr_v4, addr, INET6_ADDRSTRLEN), ntohs(info->nat_in_local_port)); } else { (void) printf("%s.%d > ", inet_ntop(AF_INET6, &info->nat_out_global, addr, INET6_ADDRSTRLEN), ntohs(info->nat_out_global_port)); (void) printf("%s.%d >>> ", inet_ntop(AF_INET6, &info->nat_in_global, addr, INET6_ADDRSTRLEN), ntohs(info->nat_in_global_port)); (void) printf("%s.%d > ", inet_ntop(AF_INET6, &info->nat_out_local, addr, INET6_ADDRSTRLEN), ntohs(info->nat_out_local_port)); (void) printf("%s.%d\n", inet_ntop(AF_INET6, &info->nat_in_local, addr, INET6_ADDRSTRLEN), ntohs(info->nat_in_local_port)); } } static void print_persist_info(ilb_persist_info_t *info) { char addr[INET6_ADDRSTRLEN]; (void) printf("%s: ", info->persist_rule_name); if (IN6_IS_ADDR_V4MAPPED(&info->persist_req_addr)) { ipaddr_t addr_v4; IN6_V4MAPPED_TO_IPADDR(&info->persist_req_addr, addr_v4); (void) printf("%s --> ", inet_ntop(AF_INET, &addr_v4, addr, INET6_ADDRSTRLEN)); IN6_V4MAPPED_TO_IPADDR(&info->persist_srv_addr, addr_v4); (void) printf("%s\n", inet_ntop(AF_INET, &addr_v4, addr, INET6_ADDRSTRLEN)); } else { (void) printf("%s --> ", inet_ntop(AF_INET6, &info->persist_req_addr, addr, INET6_ADDRSTRLEN)); (void) printf("%s\n", inet_ntop(AF_INET6, &info->persist_srv_addr, addr, INET6_ADDRSTRLEN)); } } /* Tell ilbadm_show_info() which table to show. */ enum which_tbl { show_nat = 1, show_persist }; typedef union { ilb_nat_info_t *nbuf; ilb_persist_info_t *pbuf; char *buf; } show_buf_t; static ilbadm_status_t ilbadm_show_info(int argc, char *argv[], enum which_tbl tbl) { ilb_handle_t h = ILB_INVALID_HANDLE; show_buf_t buf; ilb_status_t rclib = ILB_STATUS_OK; ilbadm_status_t rc = ILBADM_OK; int32_t i, num_entries; size_t num; boolean_t end; size_t entry_sz; /* * If the user does not specify a count, return the whole table. * This requires setting the fourth param to ilb_show_nat/persist() * end to B_FALSE. Otherwise, set end to B_TRUE; */ switch (argc) { case 1: num_entries = -1; end = B_FALSE; break; case 2: num_entries = atoi(argv[1]); if (num_entries < 1) { rc = ILBADM_EINVAL; goto out; } end = B_TRUE; break; default: rc = ILBADM_EINVAL; goto out; } if (tbl == show_nat) entry_sz = sizeof (ilb_nat_info_t); else entry_sz = sizeof (ilb_persist_info_t); if ((buf.buf = malloc((num_entries > 0 ? num_entries : NUM_ENTRIES) * entry_sz)) == NULL) { rc = ILBADM_ENOMEM; goto out; } rclib = ilb_open(&h); if (rclib != ILB_STATUS_OK) goto out; do { num = num_entries > 0 ? num_entries : NUM_ENTRIES; bzero(buf.buf, num * entry_sz); if (tbl == show_nat) rclib = ilb_show_nat(h, buf.nbuf, &num, &end); else rclib = ilb_show_persist(h, buf.pbuf, &num, &end); if (rclib != ILB_STATUS_OK) break; for (i = 0; i < num; i++) { if (tbl == show_nat) print_nat_info(&buf.nbuf[i]); else print_persist_info(&buf.pbuf[i]); } if (num_entries > 0) { num_entries -= num; if (num_entries <= 0) break; } } while (!end); free(buf.buf); out: if (h != ILB_INVALID_HANDLE) (void) ilb_close(h); if (rclib != ILB_STATUS_OK) { ilbadm_err(ilb_errstr(rclib)); rc = ILBADM_LIBERR; } if ((rc != ILBADM_OK) && (rc != ILBADM_LIBERR)) ilbadm_err(ilbadm_errstr(rc)); return (rc); } ilbadm_status_t ilbadm_show_nat(int argc, char *argv[]) { return (ilbadm_show_info(argc, argv, show_nat)); } ilbadm_status_t ilbadm_show_persist(int argc, char *argv[]) { return (ilbadm_show_info(argc, argv, show_persist)); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * Copyright 2012 Milan Jurik. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ilbadm.h" static ilbadm_key_name_t rl_incoming_keys[] = { {ILB_KEY_VIP, "vip", ""}, {ILB_KEY_PORT, "port", ""}, {ILB_KEY_PROTOCOL, "protocol", "prot"}, {ILB_KEY_BAD, "", ""} }; static ilbadm_key_name_t rl_method_keys[] = { {ILB_KEY_ALGORITHM, "lbalg", "algo"}, {ILB_KEY_TYPE, "type", "topo"}, {ILB_KEY_SRC, "proxy-src", "nat-src"}, {ILB_KEY_STICKY, "pmask", "persist"}, {ILB_KEY_BAD, "", ""} }; static ilbadm_key_name_t rl_outgoing_keys[] = { {ILB_KEY_SERVERGROUP, "servergroup", "sg"}, {ILB_KEY_BAD, "", ""} }; static ilbadm_key_name_t rl_healthchk_keys[] = { {ILB_KEY_HEALTHCHECK, "hc-name", "hcn"}, {ILB_KEY_HCPORT, "hc-port", "hcp"}, {ILB_KEY_BAD, "", ""} }; static ilbadm_key_name_t rl_timer_keys[] = { {ILB_KEY_CONNDRAIN, "conn-drain", ""}, {ILB_KEY_NAT_TO, "nat-timeout", ""}, {ILB_KEY_STICKY_TO, "persist-timeout", ""}, {ILB_KEY_BAD, "", ""} }; static ilbadm_key_name_t *all_keys[] = { rl_incoming_keys, rl_method_keys, rl_outgoing_keys, rl_healthchk_keys, rl_timer_keys, NULL }; /* field ids for of_* functions */ #define OF_IP_VIP 0 #define OF_IP_PROXYSRC 1 #define OF_IP_STICKYMASK 2 #define OF_STR_RNAME 0 #define OF_STR_HCNAME 1 #define OF_STR_SGNAME 2 #define OF_STR_INTERFACE 3 #define OF_PORT 0 #define OF_HCPORT 1 #define OF_T_CONN 0 #define OF_T_NAT 1 #define OF_T_STICKY 2 #define OF_SRV_ID 0 #define OF_SRV_ADDR 1 #define OF_SRV_PORT 2 #define OF_SRV_STATUS 3 #define OF_SRV_RNAME 4 #define OF_SRV_SGNAME 5 #define OF_SRV_HOSTNAME 6 /* some field sizes of ofmt_field_t arrays */ #define IPv4_FIELDWIDTH 16 #define IPv6_FIELDWIDTH 39 #define ILB_HOSTNAMELEN 20 #define ILB_STATUSFIELD_LEN 7 typedef struct arg_struct { int flags; char *o_str; ofmt_field_t *o_fields; ofmt_handle_t oh; } ilbadm_sh_rl_arg_t; typedef struct ilbadm_rl_exp_arg { FILE *fp; } ilbadm_rl_exp_arg_t; typedef struct ilbadm_rl_list_arg { ilb_handle_t h; ilb_rule_data_t *rd; } ilbadm_rl_list_arg_t; typedef struct ilbadm_rl_srvlist_arg { char *sgname; ilb_server_data_t *sd; ilb_rule_data_t *rd; int flags; char *o_str; ofmt_field_t *o_fields; ofmt_handle_t oh; } ilbadm_rl_srvlist_arg_t; static ofmt_cb_t of_algo; static ofmt_cb_t of_proto; static ofmt_cb_t of_rl_ip; static ofmt_cb_t of_rl_mask; static ofmt_cb_t of_rport; static ofmt_cb_t of_rstatus; static ofmt_cb_t of_str; static ofmt_cb_t of_time; static ofmt_cb_t of_topo; static ofmt_cb_t of_rl_srvlist; static boolean_t of_srv2str(ofmt_arg_t *, char *, uint_t); static boolean_t of_port2str(in_port_t, in_port_t, char *, uint_t); static ofmt_field_t rfields_v4[] = { {"RULENAME", ILB_NAMESZ, OF_STR_RNAME, of_str}, {"STATUS", ILB_STATUSFIELD_LEN, 0, of_rstatus}, {"PORT", 10, OF_PORT, of_rport}, {"PROTOCOL", 5, 0, of_proto}, {"LBALG", 12, 0, of_algo}, {"TYPE", 8, 0, of_topo}, {"PROXY-SRC", 2*IPv4_FIELDWIDTH+1, OF_IP_PROXYSRC, of_rl_ip}, {"PMASK", 6, OF_IP_STICKYMASK, of_rl_mask}, {"HC-NAME", ILB_NAMESZ, OF_STR_HCNAME, of_str}, {"HC-PORT", 8, OF_HCPORT, of_rport}, {"CONN-DRAIN", 11, OF_T_CONN, of_time}, {"NAT-TIMEOUT", 12, OF_T_NAT, of_time}, {"PERSIST-TIMEOUT", 16, OF_T_STICKY, of_time}, {"SERVERGROUP", ILB_SGNAME_SZ, OF_STR_SGNAME, of_str}, {"VIP", IPv4_FIELDWIDTH, OF_IP_VIP, of_rl_ip}, {"SERVERS", 20, 0, of_rl_srvlist}, {NULL, 0, 0, NULL} }; static ofmt_field_t rfields_v6[] = { {"RULENAME", ILB_NAMESZ, OF_STR_RNAME, of_str}, {"STATUS", ILB_STATUSFIELD_LEN, 0, of_rstatus}, {"PORT", 10, OF_PORT, of_rport}, {"PROTOCOL", 5, 0, of_proto}, {"LBALG", 12, 0, of_algo}, {"TYPE", 8, 0, of_topo}, {"PROXY-SRC", IPv6_FIELDWIDTH, OF_IP_PROXYSRC, of_rl_ip}, {"PMASK", 6, OF_IP_STICKYMASK, of_rl_mask}, {"HC-NAME", ILB_NAMESZ, OF_STR_HCNAME, of_str}, {"HC-PORT", 8, OF_HCPORT, of_rport}, {"CONN-DRAIN", 11, OF_T_CONN, of_time}, {"NAT-TIMEOUT", 12, OF_T_NAT, of_time}, {"PERSIST-TIMEOUT", 16, OF_T_STICKY, of_time}, {"SERVERGROUP", ILB_SGNAME_SZ, OF_STR_SGNAME, of_str}, {"VIP", IPv6_FIELDWIDTH, OF_IP_VIP, of_rl_ip}, {"SERVERS", 20, 0, of_rl_srvlist}, {NULL, 0, 0, NULL} }; static ofmt_field_t ssfields_v4[] = { {"SERVERID", ILB_NAMESZ, OF_SRV_ID, of_srv2str}, {"ADDRESS", IPv4_FIELDWIDTH, OF_SRV_ADDR, of_srv2str}, {"PORT", 5, OF_SRV_PORT, of_srv2str}, {"RULENAME", ILB_NAMESZ, OF_SRV_RNAME, of_srv2str}, {"STATUS", ILB_STATUSFIELD_LEN, OF_SRV_STATUS, of_srv2str}, {"SERVERGROUP", ILB_SGNAME_SZ, OF_SRV_SGNAME, of_srv2str}, {"HOSTNAME", ILB_HOSTNAMELEN, OF_SRV_HOSTNAME, of_srv2str}, {NULL, 0, 0, NULL} }; static ofmt_field_t ssfields_v6[] = { {"SERVERID", ILB_NAMESZ, OF_SRV_ID, of_srv2str}, {"ADDRESS", IPv6_FIELDWIDTH, OF_SRV_ADDR, of_srv2str}, {"PORT", 5, OF_SRV_PORT, of_srv2str}, {"RULENAME", ILB_NAMESZ, OF_SRV_RNAME, of_srv2str}, {"STATUS", ILB_STATUSFIELD_LEN, OF_SRV_STATUS, of_srv2str}, {"SERVERGROUP", ILB_SGNAME_SZ, OF_SRV_SGNAME, of_srv2str}, {"HOSTNAME", ILB_HOSTNAMELEN, OF_SRV_HOSTNAME, of_srv2str}, {NULL, 0, 0, NULL} }; extern int optind, optopt, opterr; extern char *optarg; extern ilbadm_val_type_t algo_types[]; extern ilbadm_val_type_t topo_types[]; static char * i_key_to_opt(ilbadm_key_name_t *n, ilbadm_key_code_t k) { int i; for (i = 0; n[i].k_key != ILB_KEY_BAD; i++) if (n[i].k_key == k) break; return (n[i].k_name); } char * ilbadm_key_to_opt(ilbadm_key_code_t k) { char *name; int i; for (i = 0; all_keys[i] != NULL; i++) { name = i_key_to_opt(all_keys[i], k); if (*name != '\0') return (name); } return (NULL); } /* * ports are in HOST byte order */ static void ports2str(short port1, short port2, char *buf, const int sz) { if (port2 <= port1) (void) snprintf(buf, sz, "port=%d", port1); else (void) snprintf(buf, sz, "port=%d-%d", port1, port2); } static void proto2str(short proto, char *buf, int sz) { struct protoent *pe; pe = getprotobynumber((int)proto); if (pe != NULL) (void) snprintf(buf, sz, "protocol=%s", pe->p_name); else (void) sprintf(buf, "(bad proto %d)", proto); } static void algo2str(ilb_algo_t algo, char *buf, int sz) { char *s = i_str_from_val((int)algo, &algo_types[0]); (void) snprintf(buf, sz, "lbalg=%s", (s && *s) ? s : "(bad algo)"); } static int algo2bare_str(ilb_algo_t algo, char *buf, int sz) { char *s = i_str_from_val((int)algo, &algo_types[0]); return (snprintf(buf, sz, "%s", (s && *s) ? s : "")); } static void topo2str(ilb_topo_t topo, char *buf, int sz) { char *s = i_str_from_val((int)topo, &topo_types[0]); (void) snprintf(buf, sz, "type=%s", (s && *s) ? s : "(bad type)"); } static int topo2bare_str(ilb_topo_t topo, char *buf, int sz) { char *s = i_str_from_val((int)topo, &topo_types[0]); return (snprintf(buf, sz, "%s", (s && *s) ? s : "")); } static boolean_t of_str(ofmt_arg_t *of_arg, char *buf, uint_t bufsize) { ilbadm_rl_list_arg_t *ra = (ilbadm_rl_list_arg_t *)of_arg->ofmt_cbarg; ilb_rule_data_t *rd = (ilb_rule_data_t *)ra->rd; switch (of_arg->ofmt_id) { case OF_STR_RNAME: (void) strlcpy(buf, rd->r_name, bufsize); break; case OF_STR_SGNAME: (void) strlcpy(buf, rd->r_sgname, bufsize); break; case OF_STR_HCNAME: if (*(rd->r_hcname) != '\0') (void) strlcpy(buf, rd->r_hcname, bufsize); break; } return (B_TRUE); } /* ARGSUSED */ static boolean_t of_proto(ofmt_arg_t *of_arg, char *buf, uint_t bufsize) { ilbadm_rl_list_arg_t *ra = (ilbadm_rl_list_arg_t *)of_arg->ofmt_cbarg; ilb_rule_data_t *rd = (ilb_rule_data_t *)ra->rd; if (rd->r_proto == IPPROTO_TCP) (void) strlcpy(buf, "TCP", bufsize); else if (rd->r_proto == IPPROTO_UDP) (void) strlcpy(buf, "UDP", bufsize); else return (B_FALSE); return (B_TRUE); } static boolean_t of_rl_ip(ofmt_arg_t *of_arg, char *buf, uint_t bufsize) { ilbadm_rl_list_arg_t *ra = (ilbadm_rl_list_arg_t *)of_arg->ofmt_cbarg; ilb_rule_data_t *rd = (ilb_rule_data_t *)ra->rd; ilb_ip_addr_t *ip = NULL, *ip2 = NULL; switch (of_arg->ofmt_id) { case OF_IP_VIP: ip = &rd->r_vip; break; case OF_IP_PROXYSRC: ip = &rd->r_nat_src_start; ip2 = &rd->r_nat_src_end; break; case OF_IP_STICKYMASK: ip = &rd->r_stickymask; break; } /* only print something valid */ if (ip != NULL && (ip->ia_af == AF_INET || ip->ia_af == AF_INET6)) ip2str(ip, buf, bufsize, V6_ADDRONLY); if (ip2 != NULL && (ip2->ia_af == AF_INET || ip2->ia_af == AF_INET6) && buf[0] != '\0') { int sl = strlen(buf); buf += sl; bufsize -= sl; *buf++ = '-'; bufsize--; ip2str(ip2, buf, bufsize, V6_ADDRONLY); } return (B_TRUE); } static boolean_t of_rl_mask(ofmt_arg_t *of_arg, char *buf, uint_t bufsize) { ilbadm_rl_list_arg_t *ra = (ilbadm_rl_list_arg_t *)of_arg->ofmt_cbarg; ilb_rule_data_t *rd = (ilb_rule_data_t *)ra->rd; ilb_ip_addr_t *ip = NULL; assert(of_arg->ofmt_id == OF_IP_STICKYMASK); if (!(rd->r_flags & ILB_FLAGS_RULE_STICKY)) return (B_TRUE); ip = &rd->r_stickymask; (void) snprintf(buf, bufsize, "/%d", ilbadm_mask_to_prefixlen(ip)); return (B_TRUE); } static void hcport_print(ilb_rule_data_t *rd, char *buf, uint_t bufsize) { if (rd->r_hcport != 0) (void) snprintf(buf, bufsize, "%d", ntohs(rd->r_hcport)); else if (rd->r_hcpflag == ILB_HCI_PROBE_ANY) (void) snprintf(buf, bufsize, "ANY"); else buf[0] = '\0'; } static boolean_t of_rport(ofmt_arg_t *of_arg, char *buf, uint_t bufsize) { ilbadm_rl_list_arg_t *ra = (ilbadm_rl_list_arg_t *)of_arg->ofmt_cbarg; ilb_rule_data_t *rd = (ilb_rule_data_t *)ra->rd; if (of_arg->ofmt_id == OF_PORT) return (of_port2str(rd->r_minport, rd->r_maxport, buf, bufsize)); /* only print a hcport if there's a hc name as well */ if (of_arg->ofmt_id == OF_HCPORT && rd->r_hcname[0] != '\0') hcport_print(rd, buf, bufsize); return (B_TRUE); } /* ARGSUSED */ static boolean_t of_rstatus(ofmt_arg_t *of_arg, char *buf, uint_t bufsize) { ilbadm_rl_list_arg_t *ra = (ilbadm_rl_list_arg_t *)of_arg->ofmt_cbarg; ilb_rule_data_t *rd = (ilb_rule_data_t *)ra->rd; if ((rd->r_flags & ILB_FLAGS_RULE_ENABLED) == ILB_FLAGS_RULE_ENABLED) buf[0] = 'E'; else buf[0] = 'D'; buf[1] = '\0'; return (B_TRUE); } static boolean_t of_algo(ofmt_arg_t *of_arg, char *buf, uint_t bufsize) { ilbadm_rl_list_arg_t *ra = (ilbadm_rl_list_arg_t *)of_arg->ofmt_cbarg; ilb_rule_data_t *rd = (ilb_rule_data_t *)ra->rd; if (algo2bare_str(rd->r_algo, buf, bufsize) == 0) return (B_FALSE); return (B_TRUE); } static boolean_t of_topo(ofmt_arg_t *of_arg, char *buf, uint_t bufsize) { ilbadm_rl_list_arg_t *ra = (ilbadm_rl_list_arg_t *)of_arg->ofmt_cbarg; ilb_rule_data_t *rd = (ilb_rule_data_t *)ra->rd; if (topo2bare_str(rd->r_topo, buf, bufsize) == 0) return (B_FALSE); return (B_TRUE); } static boolean_t of_time(ofmt_arg_t *of_arg, char *buf, uint_t bufsize) { ilbadm_rl_list_arg_t *ra = (ilbadm_rl_list_arg_t *)of_arg->ofmt_cbarg; ilb_rule_data_t *rd = (ilb_rule_data_t *)ra->rd; switch (of_arg->ofmt_id) { case OF_T_CONN: (void) snprintf(buf, bufsize, "%u", rd->r_conndrain); break; case OF_T_NAT: (void) snprintf(buf, bufsize, "%u", rd->r_nat_timeout); break; case OF_T_STICKY: (void) snprintf(buf, bufsize, "%u", rd->r_sticky_timeout); break; } return (B_TRUE); } typedef struct rl_showlist_arg { char *buf; uint_t bufsize; } rl_showlist_arg_t; /* ARGSUSED */ /* called by ilb_walk_servers(), cannot get rid of unused args */ static ilb_status_t srv2srvID(ilb_handle_t h, ilb_server_data_t *sd, const char *sgname, void *arg) { rl_showlist_arg_t *sla = (rl_showlist_arg_t *)arg; int len; (void) snprintf(sla->buf, sla->bufsize, "%s,", sd->sd_srvID); len = strlen(sd->sd_srvID) + 1; sla->buf += len; sla->bufsize -= len; return (ILB_STATUS_OK); } static boolean_t of_rl_srvlist(ofmt_arg_t *of_arg, char *buf, uint_t bufsize) { ilbadm_rl_list_arg_t *ra = (ilbadm_rl_list_arg_t *)of_arg->ofmt_cbarg; ilb_rule_data_t *rd = (ilb_rule_data_t *)ra->rd; rl_showlist_arg_t sla; sla.buf = buf; sla.bufsize = bufsize; (void) ilb_walk_servers(ra->h, srv2srvID, rd->r_sgname, (void *)&sla); /* we're trailing a ',' which we need to remove */ *--sla.buf = '\0'; return (B_TRUE); } #define RMAXCOLS 120 /* enough? */ #define SERVER_WIDTH (ILB_NAMESZ+1) /* 1st guess */ static boolean_t of_port2str(in_port_t minport, in_port_t maxport, char *buf, uint_t bufsize) { in_port_t h_min, h_max; int len; h_min = ntohs(minport); h_max = ntohs(maxport); if (h_min == 0) return (B_FALSE); /* print "unspec" == "all ports" */ len = snprintf(buf, bufsize, "%d", h_min); if (h_max > h_min) (void) snprintf(buf + len, bufsize - len, "-%d", h_max); return (B_TRUE); } static ilbadm_status_t ip2hostname(ilb_ip_addr_t *ip, char *buf, uint_t bufsize) { int ret; struct hostent *he; switch (ip->ia_af) { case AF_INET: he = getipnodebyaddr((char *)&ip->ia_v4, sizeof (ip->ia_v4), ip->ia_af, &ret); break; case AF_INET6: he = getipnodebyaddr((char *)&ip->ia_v6, sizeof (ip->ia_v6), ip->ia_af, &ret); break; default: return (ILBADM_INVAL_AF); } /* if we can't resolve this, just return an empty name */ if (he == NULL) buf[0] = '\0'; else (void) strlcpy(buf, he->h_name, bufsize); return (ILBADM_OK); } /* ARGSUSED */ /* * Since this function is used by libilb routine ilb_walk_rules() * it must return libilb errors */ static ilb_status_t ilbadm_show_onerule(ilb_handle_t h, ilb_rule_data_t *rd, void *arg) { ilbadm_sh_rl_arg_t *larg = (ilbadm_sh_rl_arg_t *)arg; ofmt_status_t oerr; int oflags = 0; int ocols = RMAXCOLS; ilbadm_rl_list_arg_t ra; static ofmt_handle_t oh = (ofmt_handle_t)NULL; ofmt_field_t *fields; boolean_t r_enabled = rd->r_flags & ILB_FLAGS_RULE_ENABLED; if (larg->o_str == NULL) { ilbadm_err(gettext("internal error")); return (ILB_STATUS_GENERIC); } /* * only print rules (enabled/dis-) we're asked to * note: both LIST_**ABLED flags can be set at the same time, * whereas a rule has one state only. therefore the complicated * statement. */ if (!((r_enabled && (larg->flags & ILBADM_LIST_ENABLED)) || (!r_enabled && (larg->flags & ILBADM_LIST_DISABLED)))) return (ILB_STATUS_OK); if (larg->flags & ILBADM_LIST_PARSE) oflags |= OFMT_PARSABLE; if (larg->flags & ILBADM_LIST_FULL) oflags |= OFMT_MULTILINE; bzero(&ra, sizeof (ra)); ra.rd = rd; ra.h = h; if (oh == NULL) { if (rd->r_vip.ia_af == AF_INET) fields = rfields_v4; else fields = rfields_v6; oerr = ofmt_open(larg->o_str, fields, oflags, ocols, &oh); if (oerr != OFMT_SUCCESS) { char e[80]; ilbadm_err(gettext("ofmt_open failed: %s"), ofmt_strerror(oh, oerr, e, sizeof (e))); return (ILB_STATUS_GENERIC); } } ofmt_print(oh, &ra); return (ILB_STATUS_OK); } static char *full_list_rule_hdrs = "RULENAME,STATUS,PORT,PROTOCOL,LBALG,TYPE,PROXY-SRC,PMASK," "HC-NAME,HC-PORT,CONN-DRAIN,NAT-TIMEOUT," "PERSIST-TIMEOUT,SERVERGROUP,VIP,SERVERS"; static char *def_list_rule_hdrs = "RULENAME,STATUS,LBALG,TYPE,PROTOCOL,VIP,PORT"; /* ARGSUSED */ ilbadm_status_t ilbadm_show_rules(int argc, char *argv[]) { ilb_handle_t h = ILB_INVALID_HANDLE; int c; ilb_status_t rclib = ILB_STATUS_OK; ilbadm_status_t rc = ILBADM_OK; boolean_t o_opt = B_FALSE, p_opt = B_FALSE; boolean_t f_opt = B_FALSE; ilbadm_sh_rl_arg_t larg = {0, NULL, NULL, NULL}; larg.flags = ILBADM_LIST_ENABLED | ILBADM_LIST_DISABLED; while ((c = getopt(argc, argv, ":fpedo:")) != -1) { switch ((char)c) { case 'f': larg.flags |= ILBADM_LIST_FULL; larg.o_str = full_list_rule_hdrs; f_opt = B_TRUE; break; case 'p': larg.flags |= ILBADM_LIST_PARSE; p_opt = B_TRUE; break; case 'o': larg.o_str = optarg; o_opt = B_TRUE; break; /* -e and -d may be repeated - make sure the last one wins */ case 'e': larg.flags &= ILBADM_LIST_NODISABLED; larg.flags |= ILBADM_LIST_ENABLED; break; case 'd': larg.flags &= ILBADM_LIST_NOENABLED; larg.flags |= ILBADM_LIST_DISABLED; break; case ':': ilbadm_err(gettext("missing option argument for %c"), (char)optopt); rc = ILBADM_LIBERR; goto out; case '?': default: unknown_opt(argv, optind-1); /* not reached */ break; } } if (f_opt && o_opt) { ilbadm_err(gettext("options -o and -f are mutually" " exclusive")); exit(1); } if (p_opt && !o_opt) { ilbadm_err(gettext("option -p requires -o")); exit(1); } if (p_opt && larg.o_str != NULL && (strcasecmp(larg.o_str, "all") == 0)) { ilbadm_err(gettext("option -p requires explicit field" " names for -o")); exit(1); } /* no -o option, so we use std. fields */ if (!o_opt && !f_opt) larg.o_str = def_list_rule_hdrs; rclib = ilb_open(&h); if (rclib != ILB_STATUS_OK) goto out; if (optind >= argc) { rclib = ilb_walk_rules(h, ilbadm_show_onerule, NULL, (void*)&larg); } else { while (optind < argc) { rclib = ilb_walk_rules(h, ilbadm_show_onerule, argv[optind++], (void*)&larg); if (rclib != ILB_STATUS_OK) break; } } out: if (h != ILB_INVALID_HANDLE) (void) ilb_close(h); if (rclib != ILB_STATUS_OK) { /* * The show function returns ILB_STATUS_GENERIC after printing * out an error message. So we don't need to print it again. */ if (rclib != ILB_STATUS_GENERIC) ilbadm_err(ilb_errstr(rclib)); rc = ILBADM_LIBERR; } return (rc); } static boolean_t of_srv2str(ofmt_arg_t *of_arg, char *buf, uint_t bufsize) { ilbadm_rl_srvlist_arg_t *larg = (ilbadm_rl_srvlist_arg_t *)of_arg->ofmt_cbarg; ilb_server_data_t *sd = larg->sd; uint_t op = of_arg->ofmt_id; boolean_t ret = B_TRUE; ilbadm_status_t rc; if (sd == NULL) return (B_FALSE); switch (op) { case OF_SRV_ID: (void) strlcpy(buf, sd->sd_srvID, bufsize); break; case OF_SRV_STATUS: if (ILB_IS_SRV_ENABLED(sd->sd_flags)) buf[0] = 'E'; else buf[0] = 'D'; buf[1] = '\0'; break; case OF_SRV_RNAME: (void) strlcpy(buf, larg->rd->r_name, bufsize); break; case OF_SRV_SGNAME: (void) strlcpy(buf, larg->sgname, bufsize); break; case OF_SRV_HOSTNAME: rc = ip2hostname(&sd->sd_addr, buf, bufsize); if (rc != ILBADM_OK) { buf[0] = '\0'; ret = B_FALSE; } break; case OF_SRV_PORT: ret = of_port2str(sd->sd_minport, sd->sd_maxport, buf, bufsize); break; case OF_SRV_ADDR: ip2str(&sd->sd_addr, buf, bufsize, V6_ADDRONLY); break; } return (ret); } /* ARGSUSED */ static ilb_status_t i_show_rl_srv(ilb_handle_t h, ilb_server_data_t *sd, const char *sgname, void *arg) { ilbadm_rl_srvlist_arg_t *larg = (ilbadm_rl_srvlist_arg_t *)arg; larg->sd = sd; ofmt_print(larg->oh, larg); return (ILB_STATUS_OK); } /* ARGSUSED */ /* * Since this function is used by libilb routine ilb_walk_rules() * it must return libilb errors */ ilb_status_t ilbadm_show_rl_servers(ilb_handle_t h, ilb_rule_data_t *rd, void *arg) { ofmt_status_t oerr; int oflags = 0; int ocols = RMAXCOLS; ofmt_field_t *fields; static ofmt_handle_t oh = (ofmt_handle_t)NULL; ilbadm_rl_srvlist_arg_t *larg = (ilbadm_rl_srvlist_arg_t *)arg; /* * in full mode, we currently re-open ofmt() for every rule; we use * a variable number of lines, as we print one for every server * attached to a rule. */ if (larg->o_str == NULL) { ilbadm_err(gettext("internal error")); return (ILB_STATUS_GENERIC); } if (larg->flags & ILBADM_LIST_PARSE) oflags |= OFMT_PARSABLE; if (rd->r_vip.ia_af == AF_INET) fields = ssfields_v4; else fields = ssfields_v6; if (oh == NULL) { oerr = ofmt_open(larg->o_str, fields, oflags, ocols, &oh); if (oerr != OFMT_SUCCESS) { char e[80]; ilbadm_err(gettext("ofmt_open failed: %s"), ofmt_strerror(oh, oerr, e, sizeof (e))); return (ILB_STATUS_GENERIC); } larg->oh = oh; } larg->rd = rd; larg->sgname = rd->r_sgname; return (ilb_walk_servers(h, i_show_rl_srv, rd->r_sgname, (void *)larg)); } static char *def_show_srv_hdrs = "SERVERID,ADDRESS,PORT,RULENAME,STATUS,SERVERGROUP"; /* ARGSUSED */ ilbadm_status_t ilbadm_show_server(int argc, char *argv[]) { ilb_handle_t h = ILB_INVALID_HANDLE; int c; ilb_status_t rclib = ILB_STATUS_OK; ilbadm_status_t rc = ILBADM_OK; boolean_t o_opt = B_FALSE, p_opt = B_FALSE; ilbadm_rl_srvlist_arg_t larg; bzero(&larg, sizeof (larg)); while ((c = getopt(argc, argv, ":po:")) != -1) { switch ((char)c) { case 'p': larg.flags |= ILBADM_LIST_PARSE; p_opt = B_TRUE; break; case 'o': larg.o_str = optarg; o_opt = B_TRUE; break; case ':': ilbadm_err(gettext("missing option argument for %c"), (char)optopt); rc = ILBADM_LIBERR; goto out; case '?': default: unknown_opt(argv, optind-1); /* not reached */ break; } } if (p_opt && !o_opt) { ilbadm_err(gettext("option -p requires -o")); exit(1); } if (p_opt && larg.o_str != NULL && (strcasecmp(larg.o_str, "all") == 0)) { ilbadm_err(gettext("option -p requires explicit" " field names for -o")); exit(1); } /* no -o option, so we use default fields */ if (!o_opt) larg.o_str = def_show_srv_hdrs; rclib = ilb_open(&h); if (rclib != ILB_STATUS_OK) goto out; if (optind >= argc) { rclib = ilb_walk_rules(h, ilbadm_show_rl_servers, NULL, (void*)&larg); } else { while (optind < argc) { rclib = ilb_walk_rules(h, ilbadm_show_rl_servers, argv[optind++], (void*)&larg); if (rclib != ILB_STATUS_OK) break; } } out: if (h != ILB_INVALID_HANDLE) (void) ilb_close(h); if (rclib != ILB_STATUS_OK) { /* * The show function returns ILB_STATUS_GENERIC after printing * out an error message. So we don't need to print it again. */ if (rclib != ILB_STATUS_GENERIC) ilbadm_err(ilb_errstr(rclib)); rc = ILBADM_LIBERR; } return (rc); } static ilbadm_status_t i_parse_rl_arg(char *arg, ilb_rule_data_t *rd, ilbadm_key_name_t *keylist) { ilbadm_status_t rc; rc = i_parse_optstring(arg, (void *) rd, keylist, OPT_PORTS, NULL); return (rc); } static void i_ilbadm_alloc_rule(ilb_rule_data_t **rdp) { ilb_rule_data_t *rd; *rdp = rd = (ilb_rule_data_t *)calloc(sizeof (*rd), 1); if (rd == NULL) return; rd->r_proto = IPPROTO_TCP; } static void i_ilbadm_free_rule(ilb_rule_data_t *rd) { free(rd); } /* ARGSUSED */ ilbadm_status_t ilbadm_destroy_rule(int argc, char *argv[]) { ilb_handle_t h = ILB_INVALID_HANDLE; ilbadm_status_t rc = ILBADM_OK; ilb_status_t rclib = ILB_STATUS_OK; boolean_t all_rules = B_FALSE; int c, i; while ((c = getopt(argc, argv, ":a")) != -1) { switch ((char)c) { case 'a': all_rules = B_TRUE; break; case '?': default: unknown_opt(argv, optind-1); /* not reached */ break; } } if (optind >= argc && !all_rules) { ilbadm_err(gettext("usage: delete-rule -a | name")); return (ILBADM_LIBERR); } /* either "-a" or rulename, not both */ if (optind < argc && all_rules) { rc = ILBADM_INVAL_ARGS; goto out; } rclib = ilb_open(&h); if (rclib != ILB_STATUS_OK) goto out; if (all_rules) { rclib = ilb_destroy_rule(h, NULL); goto out; } for (i = optind; i < argc && rclib == ILB_STATUS_OK; i++) rclib = ilb_destroy_rule(h, argv[i]); out: if (h != ILB_INVALID_HANDLE) (void) ilb_close(h); /* This prints the specific errors */ if (rclib != ILB_STATUS_OK) { ilbadm_err(ilb_errstr(rclib)); rc = ILBADM_LIBERR; } /* This prints the generic errors */ if ((rc != ILBADM_OK) && (rc != ILBADM_LIBERR)) ilbadm_err(ilbadm_errstr(rc)); return (rc); } /* ARGSUSED */ static ilbadm_status_t ilbadm_Xable_rule(int argc, char *argv[], ilbadm_cmd_t cmd) { ilb_handle_t h = ILB_INVALID_HANDLE; ilb_status_t rclib = ILB_STATUS_OK; ilbadm_status_t rc = ILBADM_OK; int i; rclib = ilb_open(&h); if (rclib != ILB_STATUS_OK) goto out; /* * by default, en/disable-rule mean "all", and not using * a rule name will cause this behaviour to kick in */ if (argc < 2) { if (cmd == cmd_enable_rule) rclib = ilb_enable_rule(h, NULL); else rclib = ilb_disable_rule(h, NULL); } else { for (i = optind; i < argc && rc == ILBADM_OK; i++) { if (cmd == cmd_enable_rule) rclib = ilb_enable_rule(h, argv[i]); else rclib = ilb_disable_rule(h, argv[i]); } } out: if (h != ILB_INVALID_HANDLE) (void) ilb_close(h); if (rclib != ILB_STATUS_OK) { ilbadm_err(ilb_errstr(rclib)); rc = ILBADM_LIBERR; } return (rc); } ilbadm_status_t ilbadm_enable_rule(int argc, char *argv[]) { return (ilbadm_Xable_rule(argc, argv, cmd_enable_rule)); } ilbadm_status_t ilbadm_disable_rule(int argc, char *argv[]) { return (ilbadm_Xable_rule(argc, argv, cmd_disable_rule)); } /* * parse and create a rule */ ilbadm_status_t ilbadm_create_rule(int argc, char *argv[]) { ilb_handle_t h = ILB_INVALID_HANDLE; int c; ilb_status_t rclib = ILB_STATUS_OK; ilbadm_status_t rc = ILBADM_OK; ilb_rule_data_t *rd; boolean_t p_opt = B_FALSE; i_ilbadm_alloc_rule(&rd); while ((c = getopt(argc, argv, ":ei:m:o:t:h:p")) != -1) { switch ((char)c) { case 'e': rd->r_flags |= ILB_FLAGS_RULE_ENABLED; break; case 'h': /* * Default value of of r_hcpflag means that if there * is a port range, probe any port. If there is only * one port, probe that port. */ rd->r_hcpflag = ILB_HCI_PROBE_ANY; rc = i_parse_rl_arg(optarg, rd, &rl_healthchk_keys[0]); break; case 'o': rc = i_parse_rl_arg(optarg, rd, &rl_outgoing_keys[0]); break; case 'm': rc = i_parse_rl_arg(optarg, rd, &rl_method_keys[0]); break; case 't': rc = i_parse_rl_arg(optarg, rd, &rl_timer_keys[0]); break; case 'i': rc = i_parse_rl_arg(optarg, rd, &rl_incoming_keys[0]); break; case 'p': p_opt = B_TRUE; break; case ':': ilbadm_err(gettext("missing option-argument" " for %c"), (char)optopt); rc = ILBADM_LIBERR; break; case '?': default: unknown_opt(argv, optind-1); /* not reached */ break; } if (rc != ILBADM_OK) goto out; } if (optind >= argc) { ilbadm_err(gettext("missing mandatory arguments - please refer" " to 'ilbadm create-rule' subcommand description in" " ilbadm(8)")); rc = ILBADM_LIBERR; goto out; } if (p_opt) { /* * if user hasn't specified a mask, apply default */ if ((rd->r_flags & ILB_FLAGS_RULE_STICKY) == 0) { char *maskstr; switch (rd->r_vip.ia_af) { case AF_INET: maskstr = "32"; break; case AF_INET6: maskstr = "128"; break; } rc = ilbadm_set_netmask(maskstr, &rd->r_stickymask, rd->r_vip.ia_af); if (rc != ILBADM_OK) { ilbadm_err(gettext("trouble seting default" " persistence mask")); rc = ILBADM_LIBERR; goto out; } } } else { /* use of sticky mask currently mandates "-p" */ if ((rd->r_flags & ILB_FLAGS_RULE_STICKY) != 0) { ilbadm_err(gettext("use of stickymask requires" " -p option")); rc = ILBADM_LIBERR; goto out; } } if (strlen(argv[optind]) > ILBD_NAMESZ -1) { ilbadm_err(gettext("rule name %s is too long -" " must not exceed %d chars"), argv[optind], ILBD_NAMESZ - 1); rc = ILBADM_LIBERR; goto out; } (void) strlcpy(rd->r_name, argv[optind], sizeof (rd->r_name)); rc = i_check_rule_spec(rd); if (rc != ILBADM_OK) goto out; rclib = ilb_open(&h); if (rclib != ILB_STATUS_OK) goto out; rclib = ilb_create_rule(h, rd); out: i_ilbadm_free_rule(rd); if (h != ILB_INVALID_HANDLE) (void) ilb_close(h); if (rclib != ILB_STATUS_OK) { ilbadm_err(ilb_errstr(rclib)); rc = ILBADM_LIBERR; } if ((rc != ILBADM_OK) && (rc != ILBADM_LIBERR)) ilbadm_err(ilbadm_errstr(rc)); return (rc); } /* ARGSUSED */ /* * Since this function is used by libilb function, ilb_walk_rules() * it must return libilb errors */ static ilb_status_t ilbadm_export_rl(ilb_handle_t h, ilb_rule_data_t *rd, void *arg) { char linebuf[128]; /* should be enough */ int sz = sizeof (linebuf); FILE *fp = ((ilbadm_rl_exp_arg_t *)arg)->fp; uint32_t conndrain, nat_timeout, sticky_timeout; (void) fprintf(fp, "create-rule "); if (rd->r_flags & ILB_FLAGS_RULE_ENABLED) (void) fprintf(fp, "-e "); if (rd->r_flags & ILB_FLAGS_RULE_STICKY) (void) fprintf(fp, "-p "); ip2str(&rd->r_vip, linebuf, sz, V6_ADDRONLY); (void) fprintf(fp, "-i vip=%s,", linebuf); (void) ports2str(ntohs(rd->r_minport), ntohs(rd->r_maxport), linebuf, sz); (void) fprintf(fp, "%s,", linebuf); proto2str(rd->r_proto, linebuf, sz); (void) fprintf(fp, "%s ", linebuf); algo2str(rd->r_algo, linebuf, sz); (void) fprintf(fp, "-m %s,", linebuf); topo2str(rd->r_topo, linebuf, sz); (void) fprintf(fp, "%s", linebuf); if (rd->r_nat_src_start.ia_af != AF_UNSPEC) { ip2str(&rd->r_nat_src_start, linebuf, sz, V6_ADDRONLY); /* if the address is unspecified, skip it */ if (linebuf[0] != '\0') { (void) fprintf(fp, ",proxy-src=%s", linebuf); ip2str(&rd->r_nat_src_end, linebuf, sz, V6_ADDRONLY); (void) fprintf(fp, "-%s", linebuf); } } if (rd->r_flags & ILB_FLAGS_RULE_STICKY) { (void) fprintf(fp, ",pmask=/%d", ilbadm_mask_to_prefixlen(&rd->r_stickymask)); } (void) fprintf(fp, " "); if (*rd->r_hcname != '\0') { (void) fprintf(fp, "-h hc-name=%s", rd->r_hcname); hcport_print(rd, linebuf, sizeof (linebuf)); if (linebuf[0] != '\0') (void) fprintf(fp, ",hc-port=%s", linebuf); (void) fprintf(fp, " "); } conndrain = rd->r_conndrain; nat_timeout = rd->r_nat_timeout; sticky_timeout = rd->r_sticky_timeout; if (conndrain != 0 || nat_timeout != 0 || sticky_timeout != 0) { int cnt = 0; (void) fprintf(fp, "-t "); if (conndrain != 0) { cnt++; (void) fprintf(fp, "conn-drain=%u", conndrain); } if (nat_timeout != 0) { if (cnt > 0) (void) fprintf(fp, ","); cnt++; (void) fprintf(fp, "nat-timeout=%u", nat_timeout); } if (sticky_timeout != 0) { if (cnt > 0) (void) fprintf(fp, ","); (void) fprintf(fp, "persist-timeout=%u", sticky_timeout); } (void) fprintf(fp, " "); } if (fprintf(fp, "-o servergroup=%s %s\n", rd->r_sgname, rd->r_name) < 0 || fflush(fp) == EOF) return (ILB_STATUS_WRITE); return (ILB_STATUS_OK); } ilbadm_status_t ilbadm_export_rules(ilb_handle_t h, FILE *fp) { ilb_status_t rclib; ilbadm_status_t rc = ILBADM_OK; ilbadm_rl_exp_arg_t arg; arg.fp = fp; rclib = ilb_walk_rules(h, ilbadm_export_rl, NULL, (void *)&arg); if (rclib != ILB_STATUS_OK) rc = ILBADM_LIBERR; return (rc); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * Copyright 2012 Milan Jurik. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ilbadm.h" static ilbadm_key_name_t servrange_keys[] = { {ILB_KEY_SERVER, "server", "servers"}, {ILB_KEY_SERVRANGE, "server", "servers"}, {ILB_KEY_BAD, "", ""} }; static ilbadm_key_name_t serverID_keys[] = { {ILB_KEY_SERVERID, "server", ""}, {ILB_KEY_BAD, "", ""} }; typedef struct sg_export_arg { FILE *fp; ilbadm_sgroup_t *sg; } sg_export_arg_t; typedef struct arg_struct { int flags; char *o_str; ofmt_field_t *o_fields; ofmt_handle_t oh; } list_arg_t; typedef struct sg_srv_o_struct { char *sgname; ilb_server_data_t *sd; } sg_srv_o_arg_t; static ofmt_cb_t of_sgname; static ofmt_cb_t of_srvID; static ofmt_cb_t of_port; static ofmt_cb_t of_ip; static ofmt_field_t sgfields_v4[] = { {"SGNAME", ILB_SGNAME_SZ, 0, of_sgname}, {"SERVERID", ILB_NAMESZ, 0, of_srvID}, {"MINPORT", 8, 0, of_port}, {"MAXPORT", 8, 1, of_port}, {"IP_ADDRESS", 15, 0, of_ip}, {NULL, 0, 0, NULL} }; static ofmt_field_t sgfields_v6[] = { {"SGNAME", ILB_SGNAME_SZ, 0, of_sgname}, {"SERVERID", ILB_NAMESZ, 0, of_srvID}, {"MINPORT", 8, 0, of_port}, {"MAXPORT", 8, 1, of_port}, {"IP_ADDRESS", 39, 0, of_ip}, {NULL, 0, 0, NULL} }; #define MAXCOLS 80 /* make flexible? */ extern int optind, optopt, opterr; extern char *optarg; static boolean_t of_sgname(ofmt_arg_t *of_arg, char *buf, uint_t bufsize) { sg_srv_o_arg_t *l = (sg_srv_o_arg_t *)of_arg->ofmt_cbarg; (void) strlcpy(buf, l->sgname, bufsize); return (B_TRUE); } static boolean_t of_srvID(ofmt_arg_t *of_arg, char *buf, uint_t bufsize) { sg_srv_o_arg_t *l = (sg_srv_o_arg_t *)of_arg->ofmt_cbarg; (void) strlcpy(buf, l->sd->sd_srvID, bufsize); return (B_TRUE); } static boolean_t of_port(ofmt_arg_t *of_arg, char *buf, uint_t bufsize) { sg_srv_o_arg_t *l = (sg_srv_o_arg_t *)of_arg->ofmt_cbarg; int port; if (of_arg->ofmt_id == 0) { port = ntohs(l->sd->sd_minport); if (port == 0) *buf = '\0'; else (void) snprintf(buf, bufsize, "%d", port); } else { port = ntohs(l->sd->sd_maxport); if (port == 0) *buf = '\0'; else (void) snprintf(buf, bufsize, "%d", port); } return (B_TRUE); } static boolean_t of_ip(ofmt_arg_t *of_arg, char *buf, uint_t bufsize) { sg_srv_o_arg_t *l = (sg_srv_o_arg_t *)of_arg->ofmt_cbarg; ip2str(&l->sd->sd_addr, buf, bufsize, V6_ADDRONLY); return (B_TRUE); } ilbadm_status_t i_list_sg_srv_ofmt(char *sgname, ilb_server_data_t *sd, void *arg) { list_arg_t *larg = (list_arg_t *)arg; sg_srv_o_arg_t line_arg; line_arg.sgname = sgname; line_arg.sd = sd; ofmt_print(larg->oh, &line_arg); return (ILBADM_OK); } /* * This function is always called via ilb_walk_servergroups() * and so must return libilb errors. * That's why we need to retain currently unused "h" argument */ /* ARGSUSED */ static ilb_status_t ilbadm_list_sg_srv(ilb_handle_t h, ilb_server_data_t *sd, const char *sgname, void *arg) { char ip_str[2*INET6_ADDRSTRLEN + 3] = ""; char port_str[INET6_ADDRSTRLEN]; list_arg_t *larg = (list_arg_t *)arg; ofmt_status_t oerr; int oflags = 0; int ocols = MAXCOLS; int h_minport, h_maxport; static ofmt_handle_t oh = (ofmt_handle_t)NULL; ofmt_field_t *ofp; if (larg->o_str != NULL) { if (oh == NULL) { if (sd->sd_addr.ia_af == AF_INET) ofp = sgfields_v6; else ofp = sgfields_v4; if (larg->flags & ILBADM_LIST_PARSE) oflags |= OFMT_PARSABLE; oerr = ofmt_open(larg->o_str, ofp, oflags, ocols, &oh); if (oerr != OFMT_SUCCESS) { char e[80]; ilbadm_err(gettext("ofmt_open failed: %s"), ofmt_strerror(oh, oerr, e, sizeof (e))); return (ILB_STATUS_GENERIC); } larg->oh = oh; } (void) i_list_sg_srv_ofmt((char *)sgname, sd, arg); return (ILB_STATUS_OK); } ip2str(&sd->sd_addr, ip_str, sizeof (ip_str), 0); h_minport = ntohs(sd->sd_minport); h_maxport = ntohs(sd->sd_maxport); if (h_minport == 0) *port_str = '\0'; else if (h_maxport > h_minport) (void) sprintf(port_str, ":%d-%d", h_minport, h_maxport); else (void) sprintf(port_str, ":%d", h_minport); (void) printf("%s: id:%s %s%s\n", sgname, sd->sd_srvID, ip_str, port_str); return (ILB_STATUS_OK); } ilb_status_t ilbadm_list_sg(ilb_handle_t h, ilb_sg_data_t *sg, void *arg) { if (sg->sgd_srvcount == 0) { ilb_server_data_t tmp_srv; bzero(&tmp_srv, sizeof (tmp_srv)); return (ilbadm_list_sg_srv(h, &tmp_srv, sg->sgd_name, arg)); } return (ilb_walk_servers(h, ilbadm_list_sg_srv, sg->sgd_name, arg)); } static char *def_fields = "SGNAME,SERVERID,MINPORT,MAXPORT,IP_ADDRESS"; /* ARGSUSED */ ilbadm_status_t ilbadm_show_servergroups(int argc, char *argv[]) { ilb_handle_t h = ILB_INVALID_HANDLE; ilb_status_t rclib = ILB_STATUS_OK; ilbadm_status_t rc = ILBADM_OK; int c; char optstr[] = ":po:"; boolean_t o_opt = B_FALSE, p_opt = B_FALSE; list_arg_t larg = {0, def_fields, NULL, NULL}; while ((c = getopt(argc, argv, optstr)) != -1) { switch ((char)c) { case 'p': p_opt = B_TRUE; larg.flags |= ILBADM_LIST_PARSE; break; case 'o': larg.o_str = optarg; o_opt = B_TRUE; break; case ':': ilbadm_err(gettext("missing option argument" " for %c"), (char)optopt); rc = ILBADM_LIBERR; goto out; default: unknown_opt(argv, optind-1); /* not reached */ break; } } if (p_opt && !o_opt) { ilbadm_err(gettext("option -p requires -o")); exit(1); } if (p_opt && larg.o_str != NULL && (strcasecmp(larg.o_str, "all") == 0)) { ilbadm_err(gettext("option -p requires explicit field" " names for -o")); exit(1); } rclib = ilb_open(&h); if (rclib != ILB_STATUS_OK) goto out; if (optind >= argc) { rclib = ilb_walk_servergroups(h, ilbadm_list_sg, NULL, (void*)&larg); if (rclib != ILB_STATUS_OK) rc = ILBADM_LIBERR; } else { while (optind < argc) { rclib = ilb_walk_servergroups(h, ilbadm_list_sg, argv[optind++], (void*)&larg); if (rclib != ILB_STATUS_OK) { rc = ILBADM_LIBERR; break; } } } if (larg.oh != NULL) ofmt_close(larg.oh); out: if (h != ILB_INVALID_HANDLE) (void) ilb_close(h); if (rclib != ILB_STATUS_OK) { /* * The show function returns ILB_STATUS_GENERIC after printing * out an error message. So we don't need to print it again. */ if (rclib != ILB_STATUS_GENERIC) ilbadm_err(ilb_errstr(rclib)); rc = ILBADM_LIBERR; } return (rc); } ilbadm_servnode_t * i_new_sg_elem(ilbadm_sgroup_t *sgp) { ilbadm_servnode_t *s; s = (ilbadm_servnode_t *)calloc(sizeof (*s), 1); if (s != NULL) { list_insert_tail(&sgp->sg_serv_list, s); sgp->sg_count++; } return (s); } static ilbadm_status_t i_parse_servrange_list(char *arg, ilbadm_sgroup_t *sgp) { ilbadm_status_t rc; int count; rc = i_parse_optstring(arg, (void *) sgp, servrange_keys, OPT_VALUE_LIST|OPT_IP_RANGE|OPT_PORTS, &count); return (rc); } static ilbadm_status_t i_parse_serverIDs(char *arg, ilbadm_sgroup_t *sgp) { ilbadm_status_t rc; int count; rc = i_parse_optstring(arg, (void *) sgp, serverID_keys, OPT_VALUE_LIST|OPT_PORTS, &count); return (rc); } static ilbadm_status_t i_mod_sg(ilb_handle_t h, ilbadm_sgroup_t *sgp, ilbadm_cmd_t cmd, int flags) { ilbadm_servnode_t *sn; ilb_server_data_t *srv; ilb_status_t rclib = ILB_STATUS_OK; ilbadm_status_t rc = ILBADM_OK; if (h == ILB_INVALID_HANDLE && cmd != cmd_enable_server && cmd != cmd_disable_server) return (ILBADM_LIBERR); sn = list_head(&sgp->sg_serv_list); while (sn != NULL) { srv = &sn->s_spec; srv->sd_flags |= flags; if (cmd == cmd_create_sg || cmd == cmd_add_srv) { rclib = ilb_add_server_to_group(h, sgp->sg_name, srv); if (rclib != ILB_STATUS_OK) { char buf[INET6_ADDRSTRLEN + 1]; rc = ILBADM_LIBERR; ip2str(&srv->sd_addr, buf, sizeof (buf), V6_ADDRONLY); ilbadm_err(gettext("cannot add %s to %s: %s"), buf, sgp->sg_name, ilb_errstr(rclib)); /* if we created the SG, we bail out */ if (cmd == cmd_create_sg) return (rc); } } else { assert(cmd == cmd_rem_srv); rclib = ilb_rem_server_from_group(h, sgp->sg_name, srv); /* if we fail, we tell user and continue */ if (rclib != ILB_STATUS_OK) { rc = ILBADM_LIBERR; ilbadm_err( gettext("cannot remove %s from %s: %s"), srv->sd_srvID, sgp->sg_name, ilb_errstr(rclib)); } } /* * list_next returns NULL instead of cycling back to head * so we don't have to check for list_head explicitly. */ sn = list_next(&sgp->sg_serv_list, sn); }; return (rc); } static void i_ilbadm_alloc_sgroup(ilbadm_sgroup_t **sgp) { ilbadm_sgroup_t *sg; *sgp = sg = (ilbadm_sgroup_t *)calloc(sizeof (*sg), 1); if (sg == NULL) return; list_create(&sg->sg_serv_list, sizeof (ilbadm_servnode_t), offsetof(ilbadm_servnode_t, s_link)); } static void i_ilbadm_free_sgroup(ilbadm_sgroup_t *sg) { ilbadm_servnode_t *s; while ((s = list_remove_head(&sg->sg_serv_list)) != NULL) free(s); list_destroy(&sg->sg_serv_list); } ilbadm_status_t ilbadm_create_servergroup(int argc, char *argv[]) { ilb_handle_t h = ILB_INVALID_HANDLE; ilb_status_t rclib = ILB_STATUS_OK; ilbadm_status_t rc = ILBADM_OK; ilbadm_sgroup_t *sg; int c; int flags = 0; i_ilbadm_alloc_sgroup(&sg); while ((c = getopt(argc, argv, ":s:")) != -1) { switch ((char)c) { case 's': rc = i_parse_servrange_list(optarg, sg); break; case ':': ilbadm_err(gettext("missing option-argument for" " %c"), (char)optopt); rc = ILBADM_LIBERR; break; case '?': default: unknown_opt(argv, optind-1); /* not reached */ break; } if (rc != ILBADM_OK) goto out; } if (optind >= argc) { ilbadm_err(gettext("missing mandatory arguments - please refer" " to 'create-servergroup' subcommand" " description in ilbadm(8)")); rc = ILBADM_LIBERR; goto out; } if (strlen(argv[optind]) > ILB_SGNAME_SZ - 1) { ilbadm_err(gettext("servergroup name %s is too long -" " must not exceed %d chars"), argv[optind], ILB_SGNAME_SZ - 1); rc = ILBADM_LIBERR; goto out; } sg->sg_name = argv[optind]; rclib = ilb_open(&h); if (rclib != ILB_STATUS_OK) goto out; rclib = ilb_create_servergroup(h, sg->sg_name); if (rclib != ILB_STATUS_OK) goto out; /* we create a servergroup with all servers enabled */ ILB_SET_ENABLED(flags); rc = i_mod_sg(h, sg, cmd_create_sg, flags); if (rc != ILBADM_OK) (void) ilb_destroy_servergroup(h, sg->sg_name); out: i_ilbadm_free_sgroup(sg); if (h != ILB_INVALID_HANDLE) (void) ilb_close(h); if (rclib != ILB_STATUS_OK) { ilbadm_err(ilb_errstr(rclib)); rc = ILBADM_LIBERR; } if ((rc != ILBADM_OK) && (rc != ILBADM_LIBERR)) ilbadm_err(ilbadm_errstr(rc)); return (rc); } ilbadm_status_t ilbadm_add_server_to_group(int argc, char **argv) { ilb_handle_t h = ILB_INVALID_HANDLE; ilb_status_t rclib = ILB_STATUS_OK; ilbadm_status_t rc = ILBADM_OK; ilbadm_sgroup_t *sg; int c; int flags = 0; i_ilbadm_alloc_sgroup(&sg); while ((c = getopt(argc, argv, ":s:")) != -1) { switch ((char)c) { case 's': rc = i_parse_servrange_list(optarg, sg); break; case ':': ilbadm_err(gettext("missing option-argument for" " %c"), (char)optopt); rc = ILBADM_LIBERR; break; case '?': default: unknown_opt(argv, optind-1); /* not reached */ break; } if (rc != ILBADM_OK) goto out; } if (optind >= argc) { ilbadm_err(gettext("missing mandatory arguments - please refer" " to 'add-server' subcommand description in ilbadm(8)")); rc = ILBADM_LIBERR; goto out; } sg->sg_name = argv[optind]; rclib = ilb_open(&h); if (rclib != ILB_STATUS_OK) goto out; /* A server is added enabled */ ILB_SET_ENABLED(flags); rc = i_mod_sg(h, sg, cmd_add_srv, flags); out: i_ilbadm_free_sgroup(sg); if (h != ILB_INVALID_HANDLE) (void) ilb_close(h); if ((rc != ILBADM_OK) && (rc != ILBADM_LIBERR)) ilbadm_err(ilbadm_errstr(rc)); return (rc); } /* ARGSUSED */ static ilbadm_status_t ilbadm_Xable_server(int argc, char *argv[], ilbadm_cmd_t cmd) { ilb_handle_t h = ILB_INVALID_HANDLE; ilbadm_status_t rc = ILBADM_OK; ilb_status_t rclib = ILB_STATUS_OK; int i; if (argc < 2) { ilbadm_err(gettext("missing required argument" " (server specification)")); rc = ILBADM_LIBERR; goto out; } rclib = ilb_open(&h); if (rclib != ILB_STATUS_OK) goto out; /* enable-server and disable-server only accepts serverids */ for (i = 1; i < argc && rclib == ILB_STATUS_OK; i++) { ilb_server_data_t srv; if (argv[i][0] != ILB_SRVID_PREFIX) { rc = ILBADM_INVAL_SRVID; goto out; } bzero(&srv, sizeof (srv)); /* to do: check length */ (void) strlcpy(srv.sd_srvID, argv[i], sizeof (srv.sd_srvID)); switch (cmd) { case cmd_enable_server: rclib = ilb_enable_server(h, &srv, NULL); break; case cmd_disable_server: rclib = ilb_disable_server(h, &srv, NULL); break; } /* if we can't find a given server ID, just plough on */ if (rclib == ILB_STATUS_ENOENT) { const char *msg = ilb_errstr(rclib); rc = ILBADM_LIBERR; ilbadm_err("%s: %s", msg, argv[i]); rclib = ILB_STATUS_OK; continue; } if (rclib != ILB_STATUS_OK) break; } out: if (h != ILB_INVALID_HANDLE) (void) ilb_close(h); if (rclib != ILB_STATUS_OK) { ilbadm_err(ilb_errstr(rclib)); rc = ILBADM_LIBERR; } if ((rc != ILBADM_OK) && (rc != ILBADM_LIBERR)) ilbadm_err(ilbadm_errstr(rc)); return (rc); } ilbadm_status_t ilbadm_disable_server(int argc, char *argv[]) { return (ilbadm_Xable_server(argc, argv, cmd_disable_server)); } ilbadm_status_t ilbadm_enable_server(int argc, char *argv[]) { return (ilbadm_Xable_server(argc, argv, cmd_enable_server)); } /* ARGSUSED */ ilbadm_status_t ilbadm_rem_server_from_group(int argc, char *argv[]) { ilb_handle_t h = ILB_INVALID_HANDLE; ilb_status_t rclib = ILB_STATUS_OK; ilbadm_status_t rc = ILBADM_OK; ilbadm_sgroup_t *sg; int c; i_ilbadm_alloc_sgroup(&sg); while ((c = getopt(argc, argv, ":s:")) != -1) { switch ((char)c) { case 's': rc = i_parse_serverIDs(optarg, sg); break; case ':': ilbadm_err(gettext("missing option-argument for" " %c"), (char)optopt); rc = ILBADM_LIBERR; break; case '?': default: unknown_opt(argv, optind-1); /* not reached */ break; } if (rc != ILBADM_OK) goto out; } /* we need servergroup name and at least one serverID to remove */ if (optind >= argc || sg->sg_count == 0) { rc = ILBADM_ENOOPTION; goto out; } sg->sg_name = argv[optind]; rclib = ilb_open(&h); if (rclib != ILB_STATUS_OK) goto out; rc = i_mod_sg(h, sg, cmd_rem_srv, 0); out: i_ilbadm_free_sgroup(sg); if (h != ILB_INVALID_HANDLE) (void) ilb_close(h); if ((rc != ILBADM_OK) && (rc != ILBADM_LIBERR)) ilbadm_err(ilbadm_errstr(rc)); return (rc); } ilbadm_status_t ilbadm_destroy_servergroup(int argc, char *argv[]) { ilb_handle_t h = ILB_INVALID_HANDLE; ilb_status_t rclib = ILB_STATUS_OK; ilbadm_status_t rc = ILBADM_OK; char *sgname; if (argc != 2) { ilbadm_err(gettext("usage:ilbadm" " delete-servergroup groupname")); rc = ILBADM_LIBERR; goto out; } sgname = argv[1]; rclib = ilb_open(&h); if (rclib != ILB_STATUS_OK) goto out; rclib = ilb_destroy_servergroup(h, sgname); out: if (h != ILB_INVALID_HANDLE) (void) ilb_close(h); if (rclib != ILB_STATUS_OK) { ilbadm_err(ilb_errstr(rclib)); rc = ILBADM_LIBERR; } return (rc); } #define BUFSZ 1024 static int export_srv_spec(ilb_server_data_t *srv, char *buf, const int bufsize) { int len = 0, bufsz = (int)bufsize; ip2str(&srv->sd_addr, buf, bufsz, 0); len += strlen(buf); bufsz -= len; if (srv->sd_minport != 0) { in_port_t h_min, h_max; int inc; h_min = ntohs(srv->sd_minport); h_max = ntohs(srv->sd_maxport); /* to do: if service name was given, print that, not number */ if (h_max <= h_min) inc = snprintf(buf+len, bufsz, ":%d", h_min); else inc = snprintf(buf+len, bufsz, ":%d-%d", h_min, h_max); if (inc > bufsz) /* too little space */ return (-1); len += inc; } return (len); } /* * this is called by ilb_walk_servers(), therefore we return ilb_status_t * not ilbadm_status, and retain an unused function argument */ /* ARGSUSED */ ilb_status_t ilbadm_export_a_srv(ilb_handle_t h, ilb_server_data_t *srv, const char *sgname, void *arg) { sg_export_arg_t *larg = (sg_export_arg_t *)arg; FILE *fp = larg->fp; char linebuf[BUFSZ]; /* XXXms make that dynamic */ int sz = BUFSZ; if (export_srv_spec(srv, linebuf, sz) == -1) return (ILB_STATUS_OK); (void) fprintf(fp, "add-server -s server="); (void) fprintf(fp, "%s %s\n", linebuf, sgname); return (ILB_STATUS_OK); } ilb_status_t ilbadm_export_sg(ilb_handle_t h, ilb_sg_data_t *sg, void *arg) { ilb_status_t rc = ILB_STATUS_OK; sg_export_arg_t *larg = (sg_export_arg_t *)arg; FILE *fp = larg->fp; (void) fprintf(fp, "create-servergroup %s\n", sg->sgd_name); if (sg->sgd_srvcount == 0) return (ILB_STATUS_OK); rc = ilb_walk_servers(h, ilbadm_export_a_srv, sg->sgd_name, arg); if (rc != ILB_STATUS_OK) goto out; if (fflush(fp) == EOF) rc = ILB_STATUS_WRITE; out: return (rc); } ilbadm_status_t ilbadm_export_servergroups(ilb_handle_t h, FILE *fp) { ilb_status_t rclib = ILB_STATUS_OK; ilbadm_status_t rc = ILBADM_OK; sg_export_arg_t arg; arg.fp = fp; arg.sg = NULL; rclib = ilb_walk_servergroups(h, ilbadm_export_sg, NULL, (void *)&arg); if (rclib != ILB_STATUS_OK) { ilbadm_err(ilb_errstr(rclib)); rc = ILBADM_LIBERR; } return (rc); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * Copyright 2012 Milan Jurik. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ilbadm.h" #define ILBST_TIMESTAMP_HEADER 0x01 /* a timestamp w. every header */ #define ILBST_DELTA_INTERVAL 0x02 /* delta over specified interval */ #define ILBST_ABS_NUMBERS 0x04 /* print absolute numbers, no d's */ #define ILBST_ITEMIZE 0x08 /* itemize */ #define ILBST_VERBOSE 0x10 /* verbose error info */ #define ILBST_OLD_VALUES 0x20 /* for internal processing */ #define ILBST_RULES_CHANGED 0x40 typedef struct { char is_name[KSTAT_STRLEN]; uint64_t is_value; } ilbst_stat_t; static ilbst_stat_t rulestats[] = { {"num_servers", 0}, {"bytes_not_processed", 0}, {"pkt_not_processed", 0}, {"bytes_dropped", 0}, {"pkt_dropped", 0}, {"nomem_bytes_dropped", 0}, {"nomem_pkt_dropped", 0}, {"noport_bytes_dropped", 0}, {"noport_pkt_dropped", 0}, {"icmp_echo_processed", 0}, {"icmp_dropped", 0}, {"icmp_too_big_processed", 0}, {"icmp_too_big_dropped", 0} }; /* indices into array above, to avoid searching */ #define RLSTA_NUM_SRV 0 #define RLSTA_BYTES_U 1 #define RLSTA_PKT_U 2 #define RLSTA_BYTES_D 3 #define RLSTA_PKT_D 4 #define RLSTA_NOMEMBYTES_D 5 #define RLSTA_NOMEMPKT_D 6 #define RLSTA_NOPORTBYTES_D 7 #define RLSTA_NOPORTPKT_D 8 #define RLSTA_ICMP_P 9 #define RLSTA_ICMP_D 10 #define RLSTA_ICMP2BIG_P 11 #define RLSTA_ICMP2BIG_D 12 static ilbst_stat_t servstats[] = { {"bytes_processed", 0}, {"pkt_processed", 0} }; /* indices into array above, to avoid searching */ #define SRVST_BYTES_P 0 #define SRVST_PKT_P 1 /* values used for of_* commands as id */ #define ILBST_PKT_P 0 #define ILBST_BYTES_P 1 #define ILBST_PKT_U 2 #define ILBST_BYTES_U 3 #define ILBST_PKT_D 4 #define ILBST_BYTES_D 5 #define ILBST_ICMP_P 6 #define ILBST_ICMP_D 7 #define ILBST_ICMP2BIG_P 8 #define ILBST_ICMP2BIG_D 9 #define ILBST_NOMEMP_D 10 #define ILBST_NOPORTP_D 11 #define ILBST_NOMEMB_D 12 #define ILBST_NOPORTB_D 13 #define ILBST_ITEMIZE_SNAME 97 #define ILBST_ITEMIZE_RNAME 98 #define ILBST_TIMESTAMP 99 /* approx field widths */ #define ILBST_PKTCTR_W 8 #define ILBST_BYTECTR_W 10 #define ILBST_TIME_W 15 static boolean_t of_rule_stats(ofmt_arg_t *, char *, uint_t); static boolean_t of_server_stats(ofmt_arg_t *, char *, uint_t); static boolean_t of_itemize_stats(ofmt_arg_t *, char *, uint_t); static boolean_t of_timestamp(ofmt_arg_t *, char *, uint_t); static ofmt_field_t stat_itemize_fields[] = { {"RULENAME", ILB_NAMESZ, ILBST_ITEMIZE_RNAME, of_itemize_stats}, {"SERVERNAME", ILB_NAMESZ, ILBST_ITEMIZE_SNAME, of_itemize_stats}, {"PKT_P", ILBST_PKTCTR_W, ILBST_PKT_P, of_itemize_stats}, {"BYTES_P", ILBST_BYTECTR_W, ILBST_BYTES_P, of_itemize_stats}, {"TIME", ILBST_TIME_W, ILBST_TIMESTAMP, of_timestamp}, {NULL, 0, 0, NULL} }; static ofmt_field_t stat_stdfields[] = { {"PKT_P", ILBST_PKTCTR_W, ILBST_PKT_P, of_server_stats}, {"BYTES_P", ILBST_BYTECTR_W, ILBST_BYTES_P, of_server_stats}, {"PKT_U", ILBST_PKTCTR_W, ILBST_PKT_U, of_rule_stats}, {"BYTES_U", ILBST_BYTECTR_W, ILBST_BYTES_U, of_rule_stats}, {"PKT_D", ILBST_PKTCTR_W, ILBST_PKT_D, of_rule_stats}, {"BYTES_D", ILBST_BYTECTR_W, ILBST_BYTES_D, of_rule_stats}, {"ICMP_P", ILBST_PKTCTR_W, ILBST_ICMP_P, of_rule_stats}, {"ICMP_D", ILBST_PKTCTR_W, ILBST_ICMP_D, of_rule_stats}, {"ICMP2BIG_P", 11, ILBST_ICMP2BIG_P, of_rule_stats}, {"ICMP2BIG_D", 11, ILBST_ICMP2BIG_D, of_rule_stats}, {"NOMEMP_D", ILBST_PKTCTR_W, ILBST_NOMEMP_D, of_rule_stats}, {"NOPORTP_D", ILBST_PKTCTR_W, ILBST_NOPORTP_D, of_rule_stats}, {"NOMEMB_D", ILBST_PKTCTR_W, ILBST_NOMEMB_D, of_rule_stats}, {"NOPORTB_D", ILBST_PKTCTR_W, ILBST_NOPORTB_D, of_rule_stats}, {"TIME", ILBST_TIME_W, ILBST_TIMESTAMP, of_timestamp}, {NULL, 0, 0, NULL} }; static char stat_stdhdrs[] = "PKT_P,BYTES_P,PKT_U,BYTES_U,PKT_D,BYTES_D"; static char stat_stdv_hdrs[] = "PKT_P,BYTES_P,PKT_U,BYTES_U,PKT_D,BYTES_D," "ICMP_P,ICMP_D,ICMP2BIG_P,ICMP2BIG_D,NOMEMP_D,NOPORTP_D"; static char stat_itemize_rule_hdrs[] = "SERVERNAME,PKT_P,BYTES_P"; static char stat_itemize_server_hdrs[] = "RULENAME,PKT_P,BYTES_P"; #define RSTAT_SZ (sizeof (rulestats)/sizeof (rulestats[0])) #define SSTAT_SZ (sizeof (servstats)/sizeof (servstats[0])) typedef struct { char isd_servername[KSTAT_STRLEN]; /* serverID */ ilbst_stat_t isd_serverstats[SSTAT_SZ]; hrtime_t isd_crtime; /* save for comparison purpose */ } ilbst_srv_desc_t; /* * this data structure stores statistics for a rule - both an old set * and a current/new set. we use pointers to the actual stores and switch * the pointers for every round. old_is_old in ilbst_arg_t indicates * which pointer points to the "old" data struct (ie, if true, _o pointer * points to old) */ typedef struct { char ird_rulename[KSTAT_STRLEN]; int ird_num_servers; int ird_num_servers_o; int ird_srv_ind; hrtime_t ird_crtime; /* save for comparison */ hrtime_t ird_crtime_o; /* save for comparison */ ilbst_srv_desc_t *ird_srvlist; ilbst_srv_desc_t *ird_srvlist_o; ilbst_stat_t ird_rstats[RSTAT_SZ]; ilbst_stat_t ird_rstats_o[RSTAT_SZ]; ilbst_stat_t *ird_rulestats; ilbst_stat_t *ird_rulestats_o; } ilbst_rule_desc_t; /* * overall "container" for information pertaining to statistics, and * how to display them. */ typedef struct { int ilbst_flags; /* fields representing user input */ char *ilbst_rulename; /* optional */ char *ilbst_server; /* optional */ int ilbst_interval; int ilbst_count; /* "internal" fields for data and data presentation */ ofmt_handle_t ilbst_oh; boolean_t ilbst_old_is_old; ilbst_rule_desc_t *ilbst_rlist; int ilbst_rcount; /* current list count */ int ilbst_rcount_prev; /* prev (different) count */ int ilbst_rlist_sz; /* number of alloc'ed rules */ int ilbst_rule_index; /* for itemizes display */ } ilbst_arg_t; /* ARGSUSED */ static boolean_t of_timestamp(ofmt_arg_t *of_arg, char *buf, uint_t bufsize) { time_t now; struct tm *now_tm; now = time(NULL); now_tm = localtime(&now); (void) strftime(buf, bufsize, "%F:%H.%M.%S", now_tm); return (B_TRUE); } static boolean_t i_sum_per_rule_processed(ilbst_rule_desc_t *rp, uint64_t *resp, int index, int flags) { int i, num_servers; ilbst_srv_desc_t *srv, *o_srv, *n_srv; uint64_t res = 0; boolean_t valid = B_TRUE; boolean_t old = flags & ILBST_OLD_VALUES; boolean_t check_valid; /* if we do abs. numbers, we never look at the _o fields */ assert((old && (flags & ILBST_ABS_NUMBERS)) == B_FALSE); /* we only check for validity under certain conditions */ check_valid = !(old || (flags & ILBST_ABS_NUMBERS)); if (check_valid && rp->ird_num_servers != rp->ird_num_servers_o) valid = B_FALSE; num_servers = old ? rp->ird_num_servers_o : rp->ird_num_servers; for (i = 0; i < num_servers; i++) { n_srv = &rp->ird_srvlist[i]; o_srv = &rp->ird_srvlist_o[i]; if (old) srv = o_srv; else srv = n_srv; res += srv->isd_serverstats[index].is_value; /* * if creation times don't match, comparison is wrong; if * if we already know something is invalid, we don't * need to compare again. */ if (check_valid && valid == B_TRUE && o_srv->isd_crtime != n_srv->isd_crtime) { valid = B_FALSE; break; } } /* * save the result even though it may be imprecise - let the * caller decide what to do */ *resp = res; return (valid); } typedef boolean_t (*sumfunc_t)(ilbst_rule_desc_t *, uint64_t *, int); static boolean_t i_sum_per_rule_pkt_p(ilbst_rule_desc_t *rp, uint64_t *resp, int flags) { return (i_sum_per_rule_processed(rp, resp, SRVST_PKT_P, flags)); } static boolean_t i_sum_per_rule_bytes_p(ilbst_rule_desc_t *rp, uint64_t *resp, int flags) { return (i_sum_per_rule_processed(rp, resp, SRVST_BYTES_P, flags)); } static boolean_t of_server_stats(ofmt_arg_t *of_arg, char *buf, uint_t bufsize) { ilbst_arg_t *sta = (ilbst_arg_t *)of_arg->ofmt_cbarg; uint64_t count = 0, val; int i; boolean_t valid = B_TRUE; sumfunc_t sumfunc; switch (of_arg->ofmt_id) { case ILBST_PKT_P: sumfunc = i_sum_per_rule_pkt_p; break; case ILBST_BYTES_P: sumfunc = i_sum_per_rule_bytes_p; break; } for (i = 0; i < sta->ilbst_rcount; i++) { valid = sumfunc(&sta->ilbst_rlist[i], &val, sta->ilbst_flags); if (!valid) return (valid); count += val; } if ((sta->ilbst_flags & ILBST_ABS_NUMBERS) != 0) goto out; for (i = 0; i < sta->ilbst_rcount; i++) { (void) sumfunc(&sta->ilbst_rlist[i], &val, sta->ilbst_flags | ILBST_OLD_VALUES); count -= val; } out: /* * normally, we print "change per second", which we calculate * here. otherwise, we print "change over interval" */ if ((sta->ilbst_flags & (ILBST_DELTA_INTERVAL|ILBST_ABS_NUMBERS)) == 0) count /= sta->ilbst_interval; (void) snprintf(buf, bufsize, "%llu", count); return (B_TRUE); } /* * this function is called when user wants itemized stats of every * server for a named rule, or vice vera. * i_do_print sets sta->rule_index and the proper ird_srv_ind so * we don't have to differentiate between these two cases here. */ static boolean_t of_itemize_stats(ofmt_arg_t *of_arg, char *buf, uint_t bufsize) { ilbst_arg_t *sta = (ilbst_arg_t *)of_arg->ofmt_cbarg; int stat_ind; uint64_t count; int rule_index = sta->ilbst_rule_index; int srv_ind = sta->ilbst_rlist[rule_index].ird_srv_ind; boolean_t ret = B_TRUE; ilbst_srv_desc_t *srv, *osrv; srv = &sta->ilbst_rlist[rule_index].ird_srvlist[srv_ind]; switch (of_arg->ofmt_id) { case ILBST_PKT_P: stat_ind = SRVST_PKT_P; break; case ILBST_BYTES_P: stat_ind = SRVST_BYTES_P; break; case ILBST_ITEMIZE_RNAME: (void) snprintf(buf, bufsize, "%s", sta->ilbst_rlist[rule_index].ird_rulename); return (B_TRUE); case ILBST_ITEMIZE_SNAME: (void) snprintf(buf, bufsize, "%s", srv->isd_servername); return (B_TRUE); } count = srv->isd_serverstats[stat_ind].is_value; if ((sta->ilbst_flags & ILBST_ABS_NUMBERS) != 0) goto out; osrv = &sta->ilbst_rlist[rule_index].ird_srvlist_o[srv_ind]; if (srv->isd_crtime != osrv->isd_crtime) ret = B_FALSE; count -= osrv->isd_serverstats[stat_ind].is_value; out: /* * normally, we print "change per second", which we calculate * here. otherwise, we print "change over interval" or absolute * values. */ if ((sta->ilbst_flags & (ILBST_DELTA_INTERVAL|ILBST_ABS_NUMBERS)) == 0) count /= sta->ilbst_interval; (void) snprintf(buf, bufsize, "%llu", count); return (ret); } static boolean_t of_rule_stats(ofmt_arg_t *of_arg, char *buf, uint_t bufsize) { ilbst_arg_t *sta = (ilbst_arg_t *)of_arg->ofmt_cbarg; int i, ind; uint64_t count = 0; switch (of_arg->ofmt_id) { case ILBST_PKT_U: ind = RLSTA_PKT_U; break; case ILBST_BYTES_U: ind = RLSTA_BYTES_U; break; case ILBST_PKT_D: ind = RLSTA_PKT_D; break; case ILBST_BYTES_D: ind = RLSTA_BYTES_D; break; case ILBST_ICMP_P: ind = RLSTA_ICMP_P; break; case ILBST_ICMP_D: ind = RLSTA_ICMP_D; break; case ILBST_ICMP2BIG_P: ind = RLSTA_ICMP2BIG_P; break; case ILBST_ICMP2BIG_D: ind = RLSTA_ICMP2BIG_D; break; case ILBST_NOMEMP_D: ind = RLSTA_NOMEMPKT_D; break; case ILBST_NOPORTP_D: ind = RLSTA_NOPORTPKT_D; break; case ILBST_NOMEMB_D: ind = RLSTA_NOMEMBYTES_D; break; case ILBST_NOPORTB_D: ind = RLSTA_NOPORTBYTES_D; break; } for (i = 0; i < sta->ilbst_rcount; i++) count += sta->ilbst_rlist[i].ird_rulestats[ind].is_value; if ((sta->ilbst_flags & ILBST_ABS_NUMBERS) != 0) goto out; /* * the purist approach: if we can't say 100% that what we * calculate is correct, don't. */ if (sta->ilbst_flags & ILBST_RULES_CHANGED) return (B_FALSE); for (i = 0; i < sta->ilbst_rcount; i++) { if (sta->ilbst_rlist[i].ird_crtime_o != 0 && sta->ilbst_rlist[i].ird_crtime != sta->ilbst_rlist[i].ird_crtime_o) return (B_FALSE); count -= sta->ilbst_rlist[i].ird_rulestats_o[ind].is_value; } out: /* * normally, we print "change per second", which we calculate * here. otherwise, we print "change over interval" */ if ((sta->ilbst_flags & (ILBST_DELTA_INTERVAL|ILBST_ABS_NUMBERS)) == 0) count /= sta->ilbst_interval; (void) snprintf(buf, bufsize, "%llu", count); return (B_TRUE); } /* * Get the number of kstat instances. Note that when rules are being * drained the number of kstats instances may be different than the * kstat counter num_rules (ilb:0:global:num_rules"). * * Also there can be multiple instances of a rule in the following * scenario: * * A rule named rule A has been deleted but remains in kstats because * its undergoing connection draining. During this time, the user adds * a new rule with the same name(rule A). In this case, there would * be two kstats instances for rule A. Currently ilbadm's aggregate * results will include data from both instances of rule A. In, * future we should have ilbadm stats only consider the latest instance * of the rule (ie only consider the the instance that corresponds * to the rule that was just added). * */ static int i_get_num_kinstances(kstat_ctl_t *kctl) { kstat_t *kp; int num_instances = 0; /* nothing found, 0 rules */ for (kp = kctl->kc_chain; kp != NULL; kp = kp->ks_next) { if (strncmp("rulestat", kp->ks_class, 8) == 0 && strncmp("ilb", kp->ks_module, 3) == 0) { num_instances++; } } return (num_instances); } /* * since server stat's classname is made up of -sstat, * we walk the rule list to construct the comparison * Return: pointer to rule whose name matches the class * NULL if no match */ static ilbst_rule_desc_t * match_2_rnames(char *class, ilbst_rule_desc_t *rlist, int rcount) { int i; char classname[KSTAT_STRLEN]; for (i = 0; i < rcount; i++) { (void) snprintf(classname, sizeof (classname), "%s-sstat", rlist[i].ird_rulename); if (strncmp(classname, class, sizeof (classname)) == 0) return (&rlist[i]); } return (NULL); } static int i_stat_index(kstat_named_t *knp, ilbst_stat_t *stats, int count) { int i; for (i = 0; i < count; i++) { if (strcasecmp(stats[i].is_name, knp->name) == 0) return (i); } return (-1); } static void i_copy_sstats(ilbst_srv_desc_t *sp, kstat_t *kp) { kstat_named_t *knp; int i, ind; knp = KSTAT_NAMED_PTR(kp); for (i = 0; i < kp->ks_ndata; i++, knp++) { ind = i_stat_index(knp, servstats, SSTAT_SZ); if (ind == -1) continue; (void) strlcpy(sp->isd_serverstats[ind].is_name, knp->name, sizeof (sp->isd_serverstats[ind].is_name)); sp->isd_serverstats[ind].is_value = knp->value.ui64; sp->isd_crtime = kp->ks_crtime; } } static ilbadm_status_t i_get_server_descs(ilbst_arg_t *sta, kstat_ctl_t *kctl) { ilbadm_status_t rc = ILBADM_OK; kstat_t *kp; int i = -1; ilbst_rule_desc_t *rp; ilbst_rule_desc_t *rlist = sta->ilbst_rlist; int rcount = sta->ilbst_rcount; /* * find all "server" kstats, or the one specified in * sta->server */ for (kp = kctl->kc_chain; kp != NULL; kp = kp->ks_next) { if (strncmp("ilb", kp->ks_module, 3) != 0) continue; if (sta->ilbst_server != NULL && strcasecmp(sta->ilbst_server, kp->ks_name) != 0) continue; rp = match_2_rnames(kp->ks_class, rlist, rcount); if (rp == NULL) continue; (void) kstat_read(kctl, kp, NULL); i = rp->ird_srv_ind++; rc = ILBADM_OK; /* * This means that a server is added after we check last * time... Just make the array bigger. */ if (i+1 > rp->ird_num_servers) { ilbst_srv_desc_t *srvlist; if ((srvlist = realloc(rp->ird_srvlist, (i+1) * sizeof (*srvlist))) == NULL) { rc = ILBADM_ENOMEM; break; } rp->ird_srvlist = srvlist; rp->ird_num_servers = i; } (void) strlcpy(rp->ird_srvlist[i].isd_servername, kp->ks_name, sizeof (rp->ird_srvlist[i].isd_servername)); i_copy_sstats(&rp->ird_srvlist[i], kp); } for (i = 0; i < rcount; i++) rlist[i].ird_srv_ind = 0; if (sta->ilbst_server != NULL && i == -1) rc = ILBADM_ENOSERVER; return (rc); } static void i_copy_rstats(ilbst_rule_desc_t *rp, kstat_t *kp) { kstat_named_t *knp; int i, ind; knp = KSTAT_NAMED_PTR(kp); for (i = 0; i < kp->ks_ndata; i++, knp++) { ind = i_stat_index(knp, rulestats, RSTAT_SZ); if (ind == -1) continue; (void) strlcpy(rp->ird_rulestats[ind].is_name, knp->name, sizeof (rp->ird_rulestats[ind].is_name)); rp->ird_rulestats[ind].is_value = knp->value.ui64; } } static void i_set_rlstats_ptr(ilbst_rule_desc_t *rp, boolean_t old_is_old) { if (old_is_old) { rp->ird_rulestats = rp->ird_rstats; rp->ird_rulestats_o = rp->ird_rstats_o; } else { rp->ird_rulestats = rp->ird_rstats_o; rp->ird_rulestats_o = rp->ird_rstats; } } /* * this function walks the array of rules and switches pointer to old * and new stats as well as serverlists. */ static void i_swap_rl_pointers(ilbst_arg_t *sta, int rcount) { int i, tmp_num; ilbst_rule_desc_t *rlist = sta->ilbst_rlist; ilbst_srv_desc_t *tmp_srv; for (i = 0; i < rcount; i++) { /* swap srvlist pointers */ tmp_srv = rlist[i].ird_srvlist; rlist[i].ird_srvlist = rlist[i].ird_srvlist_o; rlist[i].ird_srvlist_o = tmp_srv; /* * swap server counts - we need the old one to * save reallocation calls */ tmp_num = rlist[i].ird_num_servers_o; rlist[i].ird_num_servers_o = rlist[i].ird_num_servers; rlist[i].ird_num_servers = tmp_num; /* preserve creation time */ rlist[i].ird_crtime_o = rlist[i].ird_crtime; i_set_rlstats_ptr(&rlist[i], sta->ilbst_old_is_old); rlist[i].ird_srv_ind = 0; } } static void i_init_rulelist(ilbst_arg_t *sta, int rcount) { int i; ilbst_rule_desc_t *rlist = sta->ilbst_rlist; for (i = 0; i < rcount; i++) { rlist[i].ird_rulestats = rlist[i].ird_rstats; rlist[i].ird_rulestats_o = rlist[i].ird_rstats_o; rlist[i].ird_srv_ind = 0; } } /* * this function searches for kstats describing individual rules and * saves name, # of servers, and the kstat_t * describing them (this is * for sta->rulename == NULL); * if sta->rulename != NULL, it names the rule we're looking for * and this function will fill in the other data (like the all_rules case) * Returns: ILBADM_ENORULE named rule not found * ILBADM_ENOMEM no mem. available */ static ilbadm_status_t i_get_rule_descs(ilbst_arg_t *sta, kstat_ctl_t *kctl) { ilbadm_status_t rc = ILBADM_OK; kstat_t *kp; kstat_named_t *knp; int i; int num_servers; ilbst_rule_desc_t *rlist = sta->ilbst_rlist; int rcount = sta->ilbst_rcount; /* * find all "rule" kstats, or the one specified in * sta->ilbst_rulename. */ for (i = 0, kp = kctl->kc_chain; i < rcount && kp != NULL; kp = kp->ks_next) { if (strncmp("rulestat", kp->ks_class, 8) != 0 || strncmp("ilb", kp->ks_module, 3) != 0) continue; (void) kstat_read(kctl, kp, NULL); knp = kstat_data_lookup(kp, "num_servers"); if (knp == NULL) { ilbadm_err(gettext("kstat_data_lookup() failed: %s"), strerror(errno)); rc = ILBADM_LIBERR; break; } if (sta->ilbst_rulename != NULL) { if (strcasecmp(kp->ks_name, sta->ilbst_rulename) != 0) continue; } (void) strlcpy(rlist[i].ird_rulename, kp->ks_name, sizeof (rlist[i].ird_rulename)); /* only alloc the space we need, set counter here ... */ if (sta->ilbst_server != NULL) num_servers = 1; else num_servers = (int)knp->value.ui64; /* ... furthermore, only reallocate if necessary */ if (num_servers != rlist[i].ird_num_servers) { ilbst_srv_desc_t *srvlist; rlist[i].ird_num_servers = num_servers; if (rlist[i].ird_srvlist == NULL) srvlist = calloc(num_servers, sizeof (*srvlist)); else srvlist = realloc(rlist[i].ird_srvlist, sizeof (*srvlist) * num_servers); if (srvlist == NULL) { rc = ILBADM_ENOMEM; break; } rlist[i].ird_srvlist = srvlist; } rlist[i].ird_srv_ind = 0; rlist[i].ird_crtime = kp->ks_crtime; i_copy_rstats(&rlist[i], kp); i++; /* if we know we're done, return */ if (sta->ilbst_rulename != NULL || i == rcount) { rc = ILBADM_OK; break; } } if (sta->ilbst_rulename != NULL && i == 0) rc = ILBADM_ENORULE; return (rc); } static void i_do_print(ilbst_arg_t *sta) { int i; /* non-itemized display can go right ahead */ if ((sta->ilbst_flags & ILBST_ITEMIZE) == 0) { ofmt_print(sta->ilbst_oh, sta); return; } /* * rulename is given, list a line per server * here's how we do it: * the _ITEMIZE flag indicates to the print function (called * from ofmt_print()) to look at server [ird_srv_ind] only. */ if (sta->ilbst_rulename != NULL) { sta->ilbst_rule_index = 0; for (i = 0; i < sta->ilbst_rlist->ird_num_servers; i++) { sta->ilbst_rlist->ird_srv_ind = i; ofmt_print(sta->ilbst_oh, sta); } sta->ilbst_rlist->ird_srv_ind = 0; return; } /* list one line for every rule for a given server */ for (i = 0; i < sta->ilbst_rcount; i++) { /* * if a rule doesn't contain a given server, there's no * need to print it. Luckily, we can check that * fairly easily */ if (sta->ilbst_rlist[i].ird_srvlist[0].isd_servername[0] == '\0') continue; sta->ilbst_rule_index = i; sta->ilbst_rlist[i].ird_srv_ind = 0; ofmt_print(sta->ilbst_oh, sta); } sta->ilbst_rule_index = 0; } static ilbadm_status_t i_do_show_stats(ilbst_arg_t *sta) { kstat_ctl_t *kctl; kid_t nkid; int rcount = 1, i; ilbadm_status_t rc = ILBADM_OK; ilbst_rule_desc_t *rlist, *rp; boolean_t pseudo_abs = B_FALSE; /* for first pass */ if ((kctl = kstat_open()) == NULL) { ilbadm_err(gettext("kstat_open() failed: %s"), strerror(errno)); return (ILBADM_LIBERR); } if (sta->ilbst_rulename == NULL) rcount = i_get_num_kinstances(kctl); rlist = calloc(sizeof (*rlist), rcount); if (rlist == NULL) { rc = ILBADM_ENOMEM; goto out; } sta->ilbst_old_is_old = B_TRUE; sta->ilbst_rlist = rlist; sta->ilbst_rcount = sta->ilbst_rcount_prev = rcount; sta->ilbst_rlist_sz = rcount; /* * in the first pass, we always print absolute numbers. We * need to remember whether we wanted abs. numbers for * other samples as well */ if ((sta->ilbst_flags & ILBST_ABS_NUMBERS) == 0) { sta->ilbst_flags |= ILBST_ABS_NUMBERS; pseudo_abs = B_TRUE; } i_init_rulelist(sta, rcount); do { rc = i_get_rule_descs(sta, kctl); if (rc != ILBADM_OK) goto out; rc = i_get_server_descs(sta, kctl); if (rc != ILBADM_OK) goto out; i_do_print(sta); if (sta->ilbst_count == -1 || --(sta->ilbst_count) > 0) (void) sleep(sta->ilbst_interval); else break; nkid = kstat_chain_update(kctl); sta->ilbst_flags &= ~ILBST_RULES_CHANGED; /* * we only need to continue with most of the rest of this if * the kstat chain id has changed */ if (nkid == 0) goto swap_old_new; if (nkid == -1) { ilbadm_err(gettext("kstat_chain_update() failed: %s"), strerror(errno)); rc = ILBADM_LIBERR; break; } /* * find out whether the number of rules has changed. * if so, adjust rcount and _o; if number has increased, * expand array to hold all rules. * we only shrink if rlist_sz is larger than both rcount and * rcount_prev; */ if (sta->ilbst_rulename == NULL) rcount = i_get_num_kinstances(kctl); if (rcount != sta->ilbst_rcount) { sta->ilbst_flags |= ILBST_RULES_CHANGED; sta->ilbst_rcount_prev = sta->ilbst_rcount; sta->ilbst_rcount = rcount; if (rcount > sta->ilbst_rcount_prev) { rlist = realloc(sta->ilbst_rlist, sizeof (*sta->ilbst_rlist) * rcount); if (rlist == NULL) { rc = ILBADM_ENOMEM; break; } sta->ilbst_rlist = rlist; /* realloc doesn't zero out memory */ for (i = sta->ilbst_rcount_prev; i < rcount; i++) { rp = &sta->ilbst_rlist[i]; bzero(rp, sizeof (*rp)); i_set_rlstats_ptr(rp, sta->ilbst_old_is_old); } /* * even if rlist_sz was > rcount, it's now * shrunk to rcount */ sta->ilbst_rlist_sz = sta->ilbst_rcount; } } /* * we may need to shrink the allocated slots down to the * actually required number - we need to make sure we * don't delete old or new stats. */ if (sta->ilbst_rlist_sz > MAX(sta->ilbst_rcount, sta->ilbst_rcount_prev)) { sta->ilbst_rlist_sz = MAX(sta->ilbst_rcount, sta->ilbst_rcount_prev); rlist = realloc(sta->ilbst_rlist, sizeof (*sta->ilbst_rlist) * sta->ilbst_rlist_sz); if (rlist == NULL) { rc = ILBADM_ENOMEM; break; } sta->ilbst_rlist = rlist; } /* * move pointers around so what used to point to "old" * stats now points to new, and vice versa * if we're printing absolute numbers, this rigmarole is * not necessary. */ swap_old_new: if (pseudo_abs) sta->ilbst_flags &= ~ILBST_ABS_NUMBERS; if ((sta->ilbst_flags & ILBST_ABS_NUMBERS) == 0) { sta->ilbst_old_is_old = !sta->ilbst_old_is_old; i_swap_rl_pointers(sta, rcount); } _NOTE(CONSTCOND) } while (B_TRUE); out: (void) kstat_close(kctl); if ((rc != ILBADM_OK) && (rc != ILBADM_LIBERR)) ilbadm_err(ilbadm_errstr(rc)); if (sta->ilbst_rlist != NULL) free(sta->ilbst_rlist); return (rc); } /* * read ilb's kernel statistics and (periodically) display * them. */ /* ARGSUSED */ ilbadm_status_t ilbadm_show_stats(int argc, char *argv[]) { ilbadm_status_t rc; int c; ilbst_arg_t sta; int oflags = 0; char *fieldnames = stat_stdhdrs; ofmt_field_t *fields = stat_stdfields; boolean_t r_opt = B_FALSE, s_opt = B_FALSE, i_opt = B_FALSE; boolean_t o_opt = B_FALSE, p_opt = B_FALSE, t_opt = B_FALSE; boolean_t v_opt = B_FALSE, A_opt = B_FALSE, d_opt = B_FALSE; ofmt_status_t oerr; ofmt_handle_t oh = NULL; bzero(&sta, sizeof (sta)); sta.ilbst_interval = 1; sta.ilbst_count = 1; while ((c = getopt(argc, argv, ":tdAr:s:ivo:p")) != -1) { switch ((char)c) { case 't': sta.ilbst_flags |= ILBST_TIMESTAMP_HEADER; t_opt = B_TRUE; break; case 'd': sta.ilbst_flags |= ILBST_DELTA_INTERVAL; d_opt = B_TRUE; break; case 'A': sta.ilbst_flags |= ILBST_ABS_NUMBERS; A_opt = B_TRUE; break; case 'r': sta.ilbst_rulename = optarg; r_opt = B_TRUE; break; case 's': sta.ilbst_server = optarg; s_opt = B_TRUE; break; case 'i': sta.ilbst_flags |= ILBST_ITEMIZE; i_opt = B_TRUE; break; case 'o': fieldnames = optarg; o_opt = B_TRUE; break; case 'p': oflags |= OFMT_PARSABLE; p_opt = B_TRUE; break; case 'v': sta.ilbst_flags |= ILBST_VERBOSE; v_opt = B_TRUE; fieldnames = stat_stdv_hdrs; break; case ':': ilbadm_err(gettext("missing option-argument" " detected for %c"), (char)optopt); exit(1); /* not reached */ break; case '?': /* fallthrough */ default: unknown_opt(argv, optind-1); /* not reached */ break; } } if (s_opt && r_opt) { ilbadm_err(gettext("options -s and -r are mutually exclusive")); exit(1); } if (i_opt) { if (!(s_opt || r_opt)) { ilbadm_err(gettext("option -i requires" " either -r or -s")); exit(1); } if (v_opt) { ilbadm_err(gettext("option -i and -v are mutually" " exclusive")); exit(1); } /* only use "std" headers if none are specified */ if (!o_opt) if (r_opt) fieldnames = stat_itemize_rule_hdrs; else /* must be s_opt */ fieldnames = stat_itemize_server_hdrs; fields = stat_itemize_fields; } if (p_opt) { if (!o_opt) { ilbadm_err(gettext("option -p requires -o")); exit(1); } if (v_opt) { ilbadm_err(gettext("option -o and -v are mutually" " exclusive")); exit(1); } if (strcasecmp(fieldnames, "all") == 0) { ilbadm_err(gettext("option -p requires" " explicit field names")); exit(1); } } if (t_opt) { if (v_opt) { fieldnames = "all"; } else { int len = strlen(fieldnames) + 6; char *fnames; fnames = malloc(len); if (fnames == NULL) { rc = ILBADM_ENOMEM; return (rc); } (void) snprintf(fnames, len, "%s,TIME", fieldnames); fieldnames = fnames; } } if (A_opt && d_opt) { ilbadm_err(gettext("options -d and -A are mutually exclusive")); exit(1); } /* find and parse interval and count arguments if present */ if (optind < argc) { sta.ilbst_interval = atoi(argv[optind]); if (sta.ilbst_interval < 1) { ilbadm_err(gettext("illegal interval spec %s"), argv[optind]); exit(1); } sta.ilbst_count = -1; if (++optind < argc) { sta.ilbst_count = atoi(argv[optind]); if (sta.ilbst_count < 1) { ilbadm_err(gettext("illegal count spec %s"), argv[optind]); exit(1); } } } oerr = ofmt_open(fieldnames, fields, oflags, 80, &oh); if (oerr != OFMT_SUCCESS) { char e[80]; ilbadm_err(gettext("ofmt_open failed: %s"), ofmt_strerror(oh, oerr, e, sizeof (e))); return (ILBADM_LIBERR); } sta.ilbst_oh = oh; rc = i_do_show_stats(&sta); ofmt_close(oh); return (rc); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * Copyright 2012 Milan Jurik. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ilbadm.h" #define PORT_SEP ':' typedef enum { numeric = 1, non_numeric } addr_type_t; ilbadm_val_type_t algo_types[] = { {(int)ILB_ALG_ROUNDROBIN, "roundrobin", "rr"}, {(int)ILB_ALG_HASH_IP, "hash-ip", "hip"}, {(int)ILB_ALG_HASH_IP_SPORT, "hash-ip-port", "hipp"}, {(int)ILB_ALG_HASH_IP_VIP, "hash-ip-vip", "hipv"}, {ILBD_BAD_VAL, 0, 0} }; ilbadm_val_type_t topo_types[] = { {(int)ILB_TOPO_DSR, "DSR", "d"}, {(int)ILB_TOPO_NAT, "NAT", "n"}, {(int)ILB_TOPO_HALF_NAT, "HALF-NAT", "h"}, {ILBD_BAD_VAL, 0, 0} }; void ip2str(ilb_ip_addr_t *ip, char *buf, size_t sz, int flags) { int len; switch (ip->ia_af) { case AF_INET: if (*(uint32_t *)&ip->ia_v4 == 0) buf[0] = '\0'; else (void) inet_ntop(AF_INET, (void *)&ip->ia_v4, buf, sz); break; case AF_INET6: if (IN6_IS_ADDR_UNSPECIFIED(&ip->ia_v6)) { buf[0] = '\0'; break; } if (!(flags & V6_ADDRONLY)) *buf++ = '['; sz--; (void) inet_ntop(ip->ia_af, (void *)&ip->ia_v6, buf, sz); if (!(flags & V6_ADDRONLY)) { len = strlen(buf); buf[len] = ']'; buf[++len] = '\0'; } break; default: buf[0] = '\0'; } } char * i_str_from_val(int val, ilbadm_val_type_t *types) { ilbadm_val_type_t *v; for (v = types; v->v_type != ILBD_BAD_VAL; v++) { if (v->v_type == val) break; } /* we return this in all cases */ return (v->v_name); } int i_val_from_str(char *name, ilbadm_val_type_t *types) { ilbadm_val_type_t *v; for (v = types; v->v_type != ILBD_BAD_VAL; v++) { if (strncasecmp(name, v->v_name, sizeof (v->v_name)) == 0 || strncasecmp(name, v->v_alias, sizeof (v->v_alias)) == 0) break; } /* we return this in all cases */ return (v->v_type); } ilbadm_key_code_t i_match_key(char *key, ilbadm_key_name_t *keylist) { ilbadm_key_name_t *t_key; for (t_key = keylist; t_key->k_key != ILB_KEY_BAD; t_key++) { if (strncasecmp(key, t_key->k_name, sizeof (t_key->k_name)) == 0 || strncasecmp(key, t_key->k_alias, sizeof (t_key->k_alias)) == 0) break; } return (t_key->k_key); } /* * try to match: * 1) IPv4 address * 2) IPv6 address * 3) a hostname */ static ilbadm_status_t i_match_onehost(const char *val, ilb_ip_addr_t *ip, addr_type_t *a_type) { struct addrinfo *ai = NULL; struct addrinfo hints; addr_type_t at = numeric; (void) memset((void *)&hints, 0, sizeof (hints)); hints.ai_flags |= AI_NUMERICHOST; /* * if *a_type == numeric, we only want to check whether this * is a (valid) numeric IP address. If we do and it is NOT, * we return _ENOENT. */ if (getaddrinfo(val, NULL, &hints, &ai) != 0) { if (a_type != NULL && (*a_type == numeric)) return (ILBADM_INVAL_ADDR); at = non_numeric; if (getaddrinfo(val, NULL, NULL, &ai) != 0) return (ILBADM_INVAL_ADDR); } ip->ia_af = ai->ai_family; switch (ip->ia_af) { case AF_INET: { struct sockaddr_in sa; assert(ai->ai_addrlen == sizeof (sa)); (void) memcpy(&sa, ai->ai_addr, sizeof (sa)); ip->ia_v4 = sa.sin_addr; break; } case AF_INET6: { struct sockaddr_in6 sa; assert(ai->ai_addrlen == sizeof (sa)); (void) memcpy(&sa, ai->ai_addr, sizeof (sa)); ip->ia_v6 = sa.sin6_addr; break; } default: return (ILBADM_INVAL_AF); } if (a_type != NULL) *a_type = at; return (ILBADM_OK); } static ilbadm_status_t i_store_serverID(void *store, char *val) { ilbadm_servnode_t *s = (ilbadm_servnode_t *)store; ilb_server_data_t *sn = &s->s_spec; /* * we shouldn't need to check for length here, as a name that's * too long won't exist in the system anyway. */ (void) strlcpy(sn->sd_srvID, val, sizeof (sn->sd_srvID)); return (ILBADM_OK); } static struct in_addr i_next_in_addr(struct in_addr *a, int dir) { struct in_addr new_in; uint32_t iah; iah = ntohl(a->s_addr); if (dir == 1) iah++; else iah--; new_in.s_addr = htonl(iah); return (new_in); } static ilbadm_status_t i_expand_ipv4range(ilbadm_sgroup_t *sg, ilb_server_data_t *srv, ilb_ip_addr_t *ip1, ilb_ip_addr_t *ip2) { struct in_addr *a1; ilbadm_servnode_t *sn_new; ilb_ip_addr_t new_ip; a1 = &ip1->ia_v4; new_ip.ia_af = AF_INET; new_ip.ia_v4 = i_next_in_addr(a1, 1); while (ilb_cmp_ipaddr(&new_ip, ip2, NULL) < 1) { sn_new = i_new_sg_elem(sg); sn_new->s_spec.sd_addr = new_ip; sn_new->s_spec.sd_minport = srv->sd_minport; sn_new->s_spec.sd_maxport = srv->sd_maxport; new_ip.ia_v4 = i_next_in_addr(&new_ip.ia_v4, 1); } return (ILBADM_OK); } static struct in6_addr i_next_in6_addr(struct in6_addr *a, int dir) { struct in6_addr ia6; uint64_t al, ah; ah = INV6_N2H_MSB64(a); al = INV6_N2H_LSB64(a); if (dir == 1) { /* overflow */ if (++al == 0) ah++; } else { /* underflow */ if (--al == 0xffffffff) ah--; } INV6_H2N_MSB64(&ia6, ah); INV6_H2N_LSB64(&ia6, al); return (ia6); } static ilbadm_status_t i_expand_ipv6range(ilbadm_sgroup_t *sg, ilb_server_data_t *srv, ilb_ip_addr_t *ip1, ilb_ip_addr_t *ip2) { struct in6_addr *a1; ilbadm_servnode_t *sn_new; ilb_ip_addr_t new_ip; a1 = &ip1->ia_v6; new_ip.ia_af = AF_INET6; new_ip.ia_v6 = i_next_in6_addr(a1, 1); while (ilb_cmp_ipaddr(&new_ip, ip2, NULL) < 1) { sn_new = i_new_sg_elem(sg); sn_new->s_spec.sd_addr = new_ip; sn_new->s_spec.sd_minport = srv->sd_minport; sn_new->s_spec.sd_maxport = srv->sd_maxport; new_ip.ia_v6 = i_next_in6_addr(&new_ip.ia_v6, 1); } return (ILBADM_OK); } /* * we create a list node in the servergroup for every ip address * in the range [ip1, ip2], where we interpret the ip addresses as * numbers * the first ip address is already stored in "sn" */ static ilbadm_status_t i_expand_iprange(ilbadm_sgroup_t *sg, ilb_server_data_t *sr, ilb_ip_addr_t *ip1, ilb_ip_addr_t *ip2) { int cmp; int64_t delta; if (ip2->ia_af == 0) return (ILBADM_OK); if (ip1->ia_af != ip2->ia_af) { ilbadm_err(gettext("IP address mismatch")); return (ILBADM_LIBERR); } /* if ip addresses are the same, we're done */ if ((cmp = ilb_cmp_ipaddr(ip1, ip2, &delta)) == 0) return (ILBADM_OK); if (cmp == 1) { ilbadm_err(gettext("starting IP address is must be less" " than ending ip address in ip range specification")); return (ILBADM_LIBERR); } /* if the implicit number of IPs is too large, stop */ if (abs((int)delta) > MAX_IP_SPREAD) return (ILBADM_TOOMANYIPADDR); switch (ip1->ia_af) { case AF_INET: return (i_expand_ipv4range(sg, sr, ip1, ip2)); case AF_INET6: return (i_expand_ipv6range(sg, sr, ip1, ip2)); } return (ILBADM_INVAL_AF); } /* * parse a port spec (number or by service name) and * return the numeric port in *host* byte order * * Upon return, *flags contains ILB_FLAGS_SRV_PORTNAME if a service name matches */ static int i_parseport(char *port, char *proto, int *flags) { struct servent *se; /* assumption: port names start with a non-digit */ if (isdigit(port[0])) { if (flags != NULL) *flags &= ~ILB_FLAGS_SRV_PORTNAME; return ((int)strtol(port, NULL, 10)); } se = getservbyname(port, proto); if (se == NULL) return (-1); if (flags != NULL) *flags |= ILB_FLAGS_SRV_PORTNAME; /* * we need to convert to host byte order to be in sync with * numerical ports. since result needs to be compared, this * is preferred to returning NW byte order */ return ((int)(ntohs(se->s_port))); } /* * matches one hostname or IP address and stores it in "store". * space must have been pre-allocated to accept data * "sg" != NULL only for cases where ip ranges may be coming in. */ static ilbadm_status_t i_match_hostorip(void *store, ilbadm_sgroup_t *sg, char *val, int flags, ilbadm_key_code_t keyword) { boolean_t is_ip_range_ok = flags & OPT_IP_RANGE; boolean_t is_addr_numeric = flags & OPT_NUMERIC_ONLY; boolean_t is_ports_ok = flags & OPT_PORTS; boolean_t ports_only = flags & OPT_PORTS_ONLY; boolean_t is_nat_src = flags & OPT_NAT; char *port_pref, *dash; char *port1p, *port2p, *host2p, *host1p; char *close1, *close2; ilb_ip_addr_t ip2store; ilb_ip_addr_t *ip1, *ip2; int p1, p2; ilb_server_data_t *s = NULL; ilbadm_status_t rc = ILBADM_OK; int af = AF_INET; addr_type_t at = 0; int p_flg; struct in6_addr v6nameaddr; port1p = port2p = host2p = host1p = NULL; port_pref = dash = NULL; close1 = close2 = NULL; errno = 0; if (is_nat_src) { ilb_rule_data_t *rd = (ilb_rule_data_t *)store; ip1 = &rd->r_nat_src_start; ip2 = &rd->r_nat_src_end; } else { ilbadm_servnode_t *sn = (ilbadm_servnode_t *)store; s = &sn->s_spec; ip1 = &s->sd_addr; ip2 = &ip2store; bzero(ip2, sizeof (*ip2)); } if (ports_only) { is_ports_ok = B_TRUE; port_pref = val - 1; /* we increment again later on */ goto ports; } /* * we parse the syntax ip[-ip][:port[-port]] * since IPv6 addresses contain ':'s as well, they need to be * enclosed in "[]" to be distinct from a potential port spec. * therefore, we need to first check whether we're dealing with * IPv6 addresses before we can go search for the port seperator * and ipv6 range could look like this: [ff::0]-[ff::255]:80 */ if ((keyword == ILB_KEY_SERVER) && (strchr(val, ':') != NULL) && (*val != '[') && ((inet_pton(AF_INET6, val, &v6nameaddr)) != 0)) { /* * V6 addresses must be enclosed within * brackets when specifying server addresses */ rc = ILBADM_INVAL_SYNTAX; goto err_out; } if (*val == '[') { af = AF_INET6; val++; host1p = val; close1 = strchr(val, (int)']'); if (close1 == NULL) { rc = ILBADM_INVAL_SYNTAX; goto err_out; } *close1 = '\0'; at = 0; rc = i_match_onehost(host1p, ip1, &at); if (rc != ILBADM_OK) goto err_out; if (at != numeric) { rc = ILBADM_INVAL_ADDR; goto err_out; } if (ip1->ia_af != af) { rc = ILBADM_INVAL_AF; goto err_out; } val = close1 + 1; if (*val == PORT_SEP) { port_pref = val; goto ports; } if (*val == '-') { dash = val; if (!is_ip_range_ok) { ilbadm_err(gettext("port ranges not allowed")); rc = ILBADM_LIBERR; goto err_out; } val++; if (*val != '[') { rc = ILBADM_INVAL_SYNTAX; goto err_out; } val++; close2 = strchr(val, (int)']'); if (close2 == NULL) { rc = ILBADM_INVAL_SYNTAX; goto err_out; } *close2 = '\0'; host2p = val; at = 0; rc = i_match_onehost(host2p, ip2, &at); if (rc != ILBADM_OK) goto err_out; if (at != numeric) { rc = ILBADM_INVAL_ADDR; goto err_out; } if (ip2->ia_af != af) { rc = ILBADM_INVAL_AF; goto err_out; } val = close2+1; } } /* ports always potentially allow ranges - XXXms: check? */ port_pref = strchr(val, (int)PORT_SEP); ports: if (port_pref != NULL && is_ports_ok) { port1p = port_pref + 1; *port_pref = '\0'; dash = strchr(port1p, (int)'-'); if (dash != NULL) { port2p = dash + 1; *dash = '\0'; } if (port1p != NULL) { p1 = i_parseport(port1p, NULL, &p_flg); if (p1 == -1 || p1 == 0 || p1 > ILB_MAX_PORT) { ilbadm_err(gettext("invalid port value %s" " specified"), port1p); rc = ILBADM_LIBERR; goto err_out; } s->sd_minport = htons((in_port_t)p1); if (p_flg & ILB_FLAGS_SRV_PORTNAME) s->sd_flags |= ILB_FLAGS_SRV_PORTNAME; } if (port2p != NULL) { /* ranges are only allowed for numeric ports */ if (p_flg & ILB_FLAGS_SRV_PORTNAME) { ilbadm_err(gettext("ranges are only allowed" " for numeric ports")); rc = ILBADM_LIBERR; goto err_out; } p2 = i_parseport(port2p, NULL, &p_flg); if (p2 == -1 || p2 <= p1 || p2 > ILB_MAX_PORT || (p_flg & ILB_FLAGS_SRV_PORTNAME) == ILB_FLAGS_SRV_PORTNAME) { ilbadm_err(gettext("invalid port value %s" " specified"), port2p); rc = ILBADM_LIBERR; goto err_out; } s->sd_maxport = htons((in_port_t)p2); } /* * we fill the '-' back in, but not the port seperator, * as the \0 in its place terminates the ip address(es) */ if (dash != NULL) *dash = '-'; if (ports_only) goto out; } if (af == AF_INET6) goto out; /* * we need to handle these situations for hosts: * a. ip address * b. ip address range (ip1-ip2) * c. a hostname (may include '-' or start with a digit) * * We want to do hostname lookup only if we're quite sure that * we actually are looking at neither a single IP address nor a * range of same, as this can hang if name service is not set up * (sth. likely in a LB environment). * * here's how we proceed: * 1. try to match numeric only. If that succeeds, we're done. * (getaddrinfo, which we call in i_match_onehost(), fails if * it encounters a '-') * 2. search for a '-'; if we find one, try numeric match for * both sides. if this fails: * 3. re-insert '-' and try for a legal hostname. */ /* 1. */ at = numeric; rc = i_match_onehost(val, ip1, &at); if (rc == ILBADM_OK) goto out; /* 2. */ dash = strchr(val, (int)'-'); if (dash != NULL && is_ip_range_ok) { host2p = dash + 1; *dash = '\0'; at = numeric; rc = i_match_onehost(host2p, ip2, &at); if (rc != ILBADM_OK || at != numeric) { *dash = '-'; dash = NULL; bzero(ip2, sizeof (*ip2)); goto hostname; } /* * if the RHS of '-' is an IP but LHS is not, we might * have a hostname of form x-y where y is just a number * (this seems a valid IPv4 address), so we need to * try a complete hostname */ rc = i_match_onehost(val, ip1, &at); if (rc != ILBADM_OK || at != numeric) { *dash = '-'; dash = NULL; goto hostname; } goto out; } hostname: /* 3. */ if (is_addr_numeric) at = numeric; else at = 0; rc = i_match_onehost(val, ip1, &at); if (rc != ILBADM_OK) { goto out; } if (s != NULL) { s->sd_flags |= ILB_FLAGS_SRV_HOSTNAME; /* XXX: todo: save hostname for re-display for admin */ } out: if (dash != NULL && !is_nat_src) { rc = i_expand_iprange(sg, s, ip1, ip2); if (rc != ILBADM_OK) goto err_out; } if (is_nat_src && host2p == NULL) *ip2 = *ip1; err_out: /* * we re-insert what we overwrote, especially in the error case */ if (close2 != NULL) *close2 = ']'; if (close1 != NULL) *close1 = '['; if (dash != NULL) *dash = '-'; if (port_pref != NULL && !ports_only) *port_pref = PORT_SEP; return (rc); } /* * type-agnostic helper function to return a pointer to a * pristine (and maybe freshly allocated) piece of storage * ready for something fitting "key" */ static void * i_new_storep(void *store, ilbadm_key_code_t key) { void *res; switch (key) { case ILB_KEY_SERVER: case ILB_KEY_SERVRANGE: case ILB_KEY_SERVERID: res = (void *) i_new_sg_elem(store); break; default: res = NULL; break; } return (res); } /* * make sure everything that needs to be there is there */ ilbadm_status_t i_check_rule_spec(ilb_rule_data_t *rd) { int32_t vip_af = rd->r_vip.ia_af; ilb_ip_addr_t *prxy_src; if (vip_af != AF_INET && vip_af != AF_INET6) return (ILBADM_INVAL_AF); if (*rd->r_sgname == '\0') return (ILBADM_ENOSGNAME); if (rd->r_algo == 0 || rd->r_topo == 0) { ilbadm_err(gettext("lbalg or type is unspecified")); return (ILBADM_LIBERR); } if (rd->r_topo == ILB_TOPO_NAT) { prxy_src = &rd->r_nat_src_start; if (prxy_src->ia_af != vip_af) { ilbadm_err(gettext("proxy-src is either missing" " or its address family does not" " match that of the VIP address")); return (ILBADM_LIBERR); } } /* extend as necessary */ return (ILBADM_OK); } /* * in parameter "sz" describes size (in bytes) of mask */ static int mask_to_prefixlen(const uchar_t *mask, const int sz) { uchar_t c; int i, j; int len = 0; int tmask; /* * for every byte in the mask, we start with most significant * bit and work our way down to the least significant bit; as * long as we find the bit set, we add 1 to the length. the * first unset bit we encounter terminates this process */ for (i = 0; i < sz; i++) { c = mask[i]; tmask = 1 << 7; for (j = 7; j >= 0; j--) { if ((c & tmask) == 0) return (len); len++; tmask >>= 1; } } return (len); } int ilbadm_mask_to_prefixlen(ilb_ip_addr_t *ip) { int af = ip->ia_af; int len = 0; assert(af == AF_INET || af == AF_INET6); switch (af) { case AF_INET: len = mask_to_prefixlen((uchar_t *)&ip->ia_v4.s_addr, sizeof (ip->ia_v4)); break; case AF_INET6: len = mask_to_prefixlen((uchar_t *)&ip->ia_v6.s6_addr, sizeof (ip->ia_v6)); break; } return (len); } /* copied from ifconfig.c, changed to return symbolic constants */ /* * Convert a prefix length to a mask. * Returns 1 if ok. 0 otherwise. * Assumes the mask array is zero'ed by the caller. */ static boolean_t in_prefixlentomask(int prefixlen, int maxlen, uchar_t *mask) { if (prefixlen < 0 || prefixlen > maxlen) return (B_FALSE); while (prefixlen > 0) { if (prefixlen >= 8) { *mask++ = 0xFF; prefixlen -= 8; continue; } *mask |= 1 << (8 - prefixlen); prefixlen--; } return (B_TRUE); } ilbadm_status_t ilbadm_set_netmask(char *val, ilb_ip_addr_t *ip, int af) { int prefixlen, maxval; boolean_t r; char *end; assert(af == AF_INET || af == AF_INET6); maxval = (af == AF_INET) ? 32 : 128; if (*val == '/') val++; prefixlen = strtol(val, &end, 10); if ((val == end) || (*end != '\0')) { ilbadm_err(gettext("invalid pmask provided")); return (ILBADM_LIBERR); } if (prefixlen < 1 || prefixlen > maxval) { ilbadm_err(gettext("invalid pmask provided (AF mismatch?)")); return (ILBADM_LIBERR); } switch (af) { case AF_INET: r = in_prefixlentomask(prefixlen, maxval, (uchar_t *)&ip->ia_v4.s_addr); break; case AF_INET6: r = in_prefixlentomask(prefixlen, maxval, (uchar_t *)&ip->ia_v6.s6_addr); break; } if (r != B_TRUE) { ilbadm_err(gettext("cannot convert %s to a netmask"), val); return (ILBADM_LIBERR); } ip->ia_af = af; return (ILBADM_OK); } static ilbadm_status_t i_store_val(char *val, void *store, ilbadm_key_code_t keyword) { ilbadm_status_t rc = ILBADM_OK; void *storep = store; ilb_rule_data_t *rd = NULL; ilbadm_sgroup_t *sg = NULL; ilb_hc_info_t *hc_info = NULL; struct protoent *pe; int64_t tmp_val; if (*val == '\0') return (ILBADM_NOKEYWORD_VAL); /* some types need new storage, others don't */ switch (keyword) { case ILB_KEY_SERVER: case ILB_KEY_SERVERID: sg = (ilbadm_sgroup_t *)store; storep = i_new_storep(store, keyword); break; case ILB_KEY_HEALTHCHECK: case ILB_KEY_SERVERGROUP: rd = (ilb_rule_data_t *)store; break; case ILB_KEY_VIP: /* fallthrough */ case ILB_KEY_PORT: /* fallthrough */ case ILB_KEY_HCPORT: /* fallthrough */ case ILB_KEY_CONNDRAIN: /* fallthrough */ case ILB_KEY_NAT_TO: /* fallthrough */ case ILB_KEY_STICKY_TO: /* fallthrough */ case ILB_KEY_PROTOCOL: /* fallthrough */ case ILB_KEY_ALGORITHM: /* fallthrough */ case ILB_KEY_STICKY: /* fallthrough */ case ILB_KEY_TYPE: /* fallthrough */ case ILB_KEY_SRC: /* fallthrough */ rd = (ilb_rule_data_t *)store; break; case ILB_KEY_HC_TEST: case ILB_KEY_HC_COUNT: case ILB_KEY_HC_INTERVAL: case ILB_KEY_HC_TIMEOUT: hc_info = (ilb_hc_info_t *)store; default: /* do nothing */ ; } switch (keyword) { case ILB_KEY_SRC: /* * the proxy-src keyword is only valid for full NAT topology * the value is either a single or a range of IP addresses. */ if (rd->r_topo != ILB_TOPO_NAT) { rc = ILBADM_INVAL_PROXY; break; } rc = i_match_hostorip(storep, sg, val, OPT_NUMERIC_ONLY | OPT_IP_RANGE | OPT_NAT, ILB_KEY_SRC); break; case ILB_KEY_SERVER: rc = i_match_hostorip(storep, sg, val, OPT_IP_RANGE | OPT_PORTS, ILB_KEY_SERVER); break; case ILB_KEY_SERVERID: if (val[0] != ILB_SRVID_PREFIX) rc = ILBADM_INVAL_SRVID; else rc = i_store_serverID(storep, val); break; case ILB_KEY_VIP: { ilb_ip_addr_t *vip = &rd->r_vip; addr_type_t at = numeric; char *close = NULL; /* * we duplicate some functionality of i_match_hostorip * here; that function is geared to mandate '[]' for IPv6 * addresses, which we want to relax here, so as not to * make i_match_hostorip even longer, we do what we need * here. */ if (*val == '[') { val++; if ((close = strchr(val, (int)']')) == NULL) { rc = ILBADM_INVAL_SYNTAX; break; } *close = '\0'; } rc = i_match_onehost(val, vip, &at); /* re-assemble string as we found it */ if (close != NULL) { *close = ']'; if (rc == ILBADM_OK && vip->ia_af != AF_INET6) { ilbadm_err(gettext("use of '[]' only valid" " with IPv6 addresses")); rc = ILBADM_LIBERR; } } break; } case ILB_KEY_CONNDRAIN: tmp_val = strtoll(val, NULL, 10); if (tmp_val <= 0 || tmp_val > UINT_MAX) { rc = ILBADM_EINVAL; break; } rd->r_conndrain = tmp_val; break; case ILB_KEY_NAT_TO: tmp_val = strtoll(val, NULL, 10); if (tmp_val < 0 || tmp_val > UINT_MAX) { rc = ILBADM_EINVAL; break; } rd->r_nat_timeout = tmp_val; break; case ILB_KEY_STICKY_TO: tmp_val = strtoll(val, NULL, 10); if (tmp_val <= 0 || tmp_val > UINT_MAX) { rc = ILBADM_EINVAL; break; } rd->r_sticky_timeout = tmp_val; break; case ILB_KEY_PORT: if (isdigit(*val)) { ilbadm_servnode_t sn; bzero(&sn, sizeof (sn)); rc = i_match_hostorip((void *)&sn, sg, val, OPT_PORTS_ONLY, ILB_KEY_PORT); if (rc != ILBADM_OK) break; rd->r_minport = sn.s_spec.sd_minport; rd->r_maxport = sn.s_spec.sd_maxport; } else { struct servent *se; se = getservbyname(val, NULL); if (se == NULL) { rc = ILBADM_ENOSERVICE; break; } rd->r_minport = se->s_port; rd->r_maxport = 0; } break; case ILB_KEY_HCPORT: if (isdigit(*val)) { int hcport = atoi(val); if (hcport < 1 || hcport > 65535) { ilbadm_err(gettext("illegal number for" " hcport %s"), val); rc = ILBADM_LIBERR; break; } rd->r_hcport = htons(hcport); rd->r_hcpflag = ILB_HCI_PROBE_FIX; } else if (strcasecmp(val, "ANY") == 0) { rd->r_hcport = 0; rd->r_hcpflag = ILB_HCI_PROBE_ANY; } else { return (ILBADM_EINVAL); } break; case ILB_KEY_PROTOCOL: pe = getprotobyname(val); if (pe == NULL) rc = ILBADM_ENOPROTO; else rd->r_proto = pe->p_proto; break; case ILB_KEY_ALGORITHM: rd->r_algo = i_val_from_str(val, &algo_types[0]); if (rd->r_algo == ILBD_BAD_VAL) rc = ILBADM_INVAL_ALG; break; case ILB_KEY_STICKY: rd->r_flags |= ILB_FLAGS_RULE_STICKY; /* * CAVEAT: the use of r_vip.ia_af implies that the VIP * *must* be specified on the commandline *before* * the sticky mask. */ if (AF_UNSPEC == rd->r_vip.ia_af) { ilbadm_err(gettext("option '%s' requires that VIP be " "specified first"), ilbadm_key_to_opt(keyword)); rc = ILBADM_LIBERR; break; } rc = ilbadm_set_netmask(val, &rd->r_stickymask, rd->r_vip.ia_af); break; case ILB_KEY_TYPE: rd->r_topo = i_val_from_str(val, &topo_types[0]); if (rd->r_topo == ILBD_BAD_VAL) rc = ILBADM_INVAL_OPER; break; case ILB_KEY_SERVERGROUP: (void) strlcpy(rd->r_sgname, (char *)val, sizeof (rd->r_sgname)); break; case ILB_KEY_HEALTHCHECK: (void) strlcpy(rd->r_hcname, (char *)val, sizeof (rd->r_hcname)); break; case ILB_KEY_HC_TEST: (void) strlcpy(hc_info->hci_test, (char *)val, sizeof (hc_info->hci_test)); break; case ILB_KEY_HC_COUNT: if (isdigit(*val)) hc_info->hci_count = atoi(val); else return (ILBADM_EINVAL); break; case ILB_KEY_HC_INTERVAL: if (isdigit(*val)) hc_info->hci_interval = atoi(val); else return (ILBADM_EINVAL); break; case ILB_KEY_HC_TIMEOUT: if (isdigit(*val)) hc_info->hci_timeout = atoi(val); else return (ILBADM_EINVAL); break; default: rc = ILBADM_INVAL_KEYWORD; break; } return (rc); } /* * generic parsing function. * parses "key=value[,value]" strings in "arg". keylist determines the * list of valid keys in the LHS. keycode determines interpretation and * storage in store * XXXms: looks like "key=value[,value]" violates spec. needs a fix */ ilbadm_status_t i_parse_optstring(char *arg, void *store, ilbadm_key_name_t *keylist, int flags, int *count) { ilbadm_status_t rc = ILBADM_OK; char *comma = NULL, *equals = NULL; char *key, *nextkey, *val; ilbadm_key_code_t keyword; boolean_t is_value_list = flags & OPT_VALUE_LIST; boolean_t assign_seen = B_FALSE; int n; key = arg; n = 1; /* * Algorithm: * 1. find any commas indicating and seperating current value * from a following value * 2. if we're expecting a list of values (seperated by commas) * and have already seen the assignment, then * get the next "value" * 3. else (we're looking at the first element of the RHS) * 4. find the '=' * 5. match the keyword to the list we were passed in * 6. store the value. */ while (key != NULL && *key != '\0') { comma = equals = NULL; /* 2 */ nextkey = strchr(key, (int)','); if (nextkey != NULL) { comma = nextkey++; *comma = '\0'; } /* 3a */ if (is_value_list && assign_seen) { val = key; /* 3b */ } else { /* 4 */ equals = strchr(key, (int)'='); if (equals == NULL) { ilbadm_err("%s: %s", key, ilbadm_errstr(ILBADM_ASSIGNREQ)); rc = ILBADM_LIBERR; goto out; } val = equals + 1; *equals = '\0'; assign_seen = B_TRUE; /* 5 */ keyword = i_match_key(key, keylist); if (keyword == ILB_KEY_BAD) { ilbadm_err(gettext("bad keyword %s"), key); rc = ILBADM_LIBERR; goto out; } } /* 6 */ rc = i_store_val(val, store, keyword); if (rc != ILBADM_OK) { ilbadm_err("%s: %s", key, ilbadm_errstr(rc)); /* Change to ILBADM_ILBERR to avoid more err msgs. */ rc = ILBADM_LIBERR; goto out; } key = nextkey; n++; } out: if (comma != NULL) *comma = ','; if (equals != NULL) *equals = '='; if (count != NULL) *count = n; return (rc); }