/* * ipvsadm - IP Virtual Server ADMinistration program * for IPVS NetFilter Module in kernel 2.4 * * Version: $Id$ * * Authors: Wensong Zhang * Peter Kese * * This program is based on ippfvsadm. * * Changes: * Wensong Zhang : added the editting service & destination support * Wensong Zhang : added the feature to specify persistent port * Jacob Rief : found the bug that masquerading dest of * different vport and dport cannot be deleted. * Wensong Zhang : fixed it and changed some cosmetic things * Wensong Zhang : added the timeout setting for persistent service * Wensong Zhang : added specifying the dest weight zero * Wensong Zhang : fixed the -E and -e options * Wensong Zhang : added the long options * Wensong Zhang : added the hostname and portname input * Wensong Zhang : added the hostname and portname output * Lars Marowsky-Brée : added persistence granularity support * Julian Anastasov : fixed the (null) print for unknown services * Wensong Zhang : added the port_to_anyname function * Horms : added option to read commands from stdin * Horms : modified usage function so it prints to * : stdout if an exit value of 0 is used and * : stdout otherwise. Program is then terminated * : with the supplied exit value. * Horms : updated manpage and usage funtion so * : the reflect the options available * Wensong Zhang : added option to write rules to stdout * Horms : added ability to specify a fwmark * : instead of a server and port for * : a virtual service * Horms : tightened up checking of services * : in parse_service * Horms : ensure that a -r is passed when needed * Wensong Zhang : fixed the output of fwmark rules * Horms : added kernel version verification * Horms : Specifying command and option options * (e.g. -Ln or -At) in one short option * with popt problem fixed. * Wensong Zhang : split the process_options and make * two versions of parse_options. * Horms : attempting to save or restore when * compiled against getopt_long now results * in an informative error message rather * than the usage information * Horms : added -v option * Wensong Zhang : rewrite most code of parsing options and * processing options. * Alexandre Cassen : added ipvs_syncd SyncdID support to filter * incoming sync messages. * Guy Waugh & Ratz : added --exact option and spelling cleanup * vbusam@google.com : added IPv6 support * * * ippfvsadm - Port Fowarding & Virtual Server ADMinistration program * * Copyright (c) 1998 Wensong Zhang * All rights reserved. * * Author: Wensong Zhang * * This ippfvsadm is derived from Steven Clarke's ipportfw program. * * portfw - Port Forwarding Table Editing v1.1 * * Copyright (c) 1997 Steven Clarke * All rights reserved. * * Author: Steven Clarke * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #undef __KERNEL__ /* Makefile lazyness ;) */ #include #include #include #include #include #include #include #include #include #include #include #include #include /* For waitpid */ #include #include #include #include #include #include "popt.h" #define IPVS_OPTION_PROCESSING "popt" #include "config_stream.h" #include "libipvs/libipvs.h" #define IPVSADM_VERSION_NO "v" VERSION #define IPVSADM_VERSION_DATE "2019/12/24" #define IPVSADM_VERSION IPVSADM_VERSION_NO " " IPVSADM_VERSION_DATE #define MAX_TIMEOUT (86400*31) /* 31 days */ #define CMD_NONE 0 #define CMD_ADD (CMD_NONE+1) #define CMD_EDIT (CMD_NONE+2) #define CMD_DEL (CMD_NONE+3) #define CMD_FLUSH (CMD_NONE+4) #define CMD_LIST (CMD_NONE+5) #define CMD_ADDDEST (CMD_NONE+6) #define CMD_DELDEST (CMD_NONE+7) #define CMD_EDITDEST (CMD_NONE+8) #define CMD_TIMEOUT (CMD_NONE+9) #define CMD_STARTDAEMON (CMD_NONE+10) #define CMD_STOPDAEMON (CMD_NONE+11) #define CMD_RESTORE (CMD_NONE+12) #define CMD_SAVE (CMD_NONE+13) #define CMD_ZERO (CMD_NONE+14) #define CMD_MAX CMD_ZERO #define NUMBER_OF_CMD (CMD_MAX - CMD_NONE) static const char* cmdnames[] = { "add-service", "edit-service", "delete-service", "flush", "list", "add-server", "delete-server", "edit-server", "set", "start-daemon", "stop-daemon", "restore", "save", "zero", }; #define OPT_NONE 0x000000 #define OPT_NUMERIC 0x000001 #define OPT_CONNECTION 0x000002 #define OPT_SERVICE 0x000004 #define OPT_SCHEDULER 0x000008 #define OPT_PERSISTENT 0x000010 #define OPT_NETMASK 0x000020 #define OPT_SERVER 0x000040 #define OPT_FORWARD 0x000080 #define OPT_WEIGHT 0x000100 #define OPT_UTHRESHOLD 0x000200 #define OPT_LTHRESHOLD 0x000400 #define OPT_MCAST 0x000800 #define OPT_TIMEOUT 0x001000 #define OPT_DAEMON 0x002000 #define OPT_STATS 0x004000 #define OPT_RATE 0x008000 #define OPT_THRESHOLDS 0x010000 #define OPT_PERSISTENTCONN 0x020000 #define OPT_NOSORT 0x040000 #define OPT_SYNCID 0x080000 #define OPT_EXACT 0x100000 #define OPT_ONEPACKET 0x200000 #define OPT_PERSISTENCE_ENGINE 0x400000 #define OPT_SCHED_FLAGS 0x800000 #define OPT_MCAST_GROUP 0x01000000 #define OPT_MCAST_PORT 0x02000000 #define OPT_MCAST_TTL 0x04000000 #define OPT_SYNC_MAXLEN 0x08000000 #define OPT_TUN_INFO 0x10000000 #define OPT_TUN_TYPE 0x20000000 #define OPT_TUN_PORT 0x40000000 #define OPT_TUN_NOCSUM 0x80000000 #define OPT_TUN_CSUM 0x100000000 #define OPT_TUN_REMCSUM 0x200000000 #define NUMBER_OF_OPT 34 #define OPTC_NUMERIC 0 #define OPTC_CONNECTION 1 #define OPTC_SERVICE 2 #define OPTC_SCHEDULER 3 #define OPTC_PERSISTENT 4 #define OPTC_NETMASK 5 #define OPTC_SERVER 6 #define OPTC_FORWARD 7 #define OPTC_WEIGHT 8 #define OPTC_UTHRESHOLD 9 #define OPTC_LTHRESHOLD 10 #define OPTC_MCAST 11 #define OPTC_TIMEOUT 12 #define OPTC_DAEMON 13 #define OPTC_STATS 14 #define OPTC_RATE 15 #define OPTC_THRESHOLDS 16 #define OPTC_PERSISTENTCONN 17 #define OPTC_NOSORT 18 #define OPTC_SYNCID 19 #define OPTC_EXACT 20 #define OPTC_ONEPACKET 21 #define OPTC_PERSISTENCE_ENGINE 22 #define OPTC_SCHED_FLAGS 23 #define OPTC_MCAST_GROUP 24 #define OPTC_MCAST_PORT 25 #define OPTC_MCAST_TTL 26 #define OPTC_SYNC_MAXLEN 27 #define OPTC_TUN_INFO 28 #define OPTC_TUN_TYPE 29 #define OPTC_TUN_PORT 30 #define OPTC_TUN_NOCSUM 31 #define OPTC_TUN_CSUM 32 #define OPTC_TUN_REMCSUM 33 static const char* optnames[] = { "numeric", "connection", "service-address", "scheduler", "persistent", "netmask", "real-server", "forwarding-method", "weight", "u-threshold", "l-threshold", "mcast-interface", "timeout", "daemon", "stats", "rate", "thresholds", "persistent-conn", "nosort", "syncid", "exact", "ops", "pe", "sched-flags", "mcast-group", "mcast-port", "mcast-ttl", "sync-maxlen", "tun-info", "tun-type", "tun-port", "tun-nocsum", "tun-csum", "tun-remcsum", }; /* * Table of legal combinations of commands and options. * Key: * '+' compulsory * 'x' illegal * '1' exclusive (only one '1' option can be supplied) * ' ' optional */ static const char commands_v_options[NUMBER_OF_CMD][NUMBER_OF_OPT] = { /* -n -c svc -s -p -M -r fwd -w -x -y -mc tot dmn -st -rt thr -pc srt sid -ex ops -pe -b grp port ttl size tinf type tprt nocs csum remc */ /*ADD*/ {'x', 'x', '+', ' ', ' ', ' ', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', ' ', ' ', ' ', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x'}, /*EDIT*/ {'x', 'x', '+', ' ', ' ', ' ', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', ' ', ' ', ' ', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x'}, /*DEL*/ {'x', 'x', '+', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x'}, /*FLUSH*/ {'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x'}, /*LIST*/ {' ', '1', '1', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', '1', '1', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'x', 'x', 'x', 'x', 'x', 'x', 'x', ' ', 'x', 'x', 'x', 'x', 'x'}, /*ADDSRV*/ {'x', 'x', '+', 'x', 'x', 'x', '+', ' ', ' ', ' ', ' ', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', ' ', ' ', ' ', ' ', ' '}, /*DELSRV*/ {'x', 'x', '+', 'x', 'x', 'x', '+', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x'}, /*EDITSRV*/ {'x', 'x', '+', 'x', 'x', 'x', '+', ' ', ' ', ' ', ' ', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', ' ', ' ', ' ', ' ', ' '}, /*TIMEOUT*/ {'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x'}, /*STARTD*/ {'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', ' ', 'x', 'x', 'x', 'x', 'x', 'x', 'x', ' ', 'x', 'x', 'x', 'x', ' ', ' ', ' ', ' ', 'x', 'x', 'x', 'x', 'x', 'x'}, /*STOPD*/ {'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', ' ', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x'}, /*RESTORE*/ {'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x'}, /*SAVE*/ {' ', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x'}, /*ZERO*/ {'x', 'x', ' ', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x'}, }; static const char * const tunnames[] = { "ipip", "gue", "gre", }; static const char * const tunflags[] = { "-c", /* without checksum */ "+c", /* with checksum */ "r+c", /* with remote checksum */ }; static const char * const tun_flags_opts[] = { "--tun-nocsum", "--tun-csum", "--tun-remcsum", }; static const int tunopts[] = { OPTC_TUN_PORT, OPTC_TUN_NOCSUM, OPTC_TUN_CSUM, OPTC_TUN_REMCSUM, }; #define NUMBER_OF_TUN_OPT 4 #define NA "n/a" /* * Table of legal combinations of tunnel types and options. * Key: * '+' compulsory * 'x' illegal * '1' exclusive (only one '1' option can be supplied) * ' ' optional */ static const char tunnel_types_v_options[IP_VS_CONN_F_TUNNEL_TYPE_MAX][NUMBER_OF_TUN_OPT] = { /* tprt nocs csum remc */ /* ipip */ {'x', 'x', 'x', 'x'}, /* gue */ {'+', '1', '1', '1'}, /* gre */ {'x', '1', '1', 'x'}, }; /* printing format flags */ #define FMT_NONE 0x0000 #define FMT_NUMERIC 0x0001 #define FMT_RULE 0x0002 #define FMT_STATS 0x0004 #define FMT_RATE 0x0008 #define FMT_THRESHOLDS 0x0010 #define FMT_PERSISTENTCONN 0x0020 #define FMT_NOSORT 0x0040 #define FMT_EXACT 0x0080 #define FMT_TUN_INFO 0x0100 #define SERVICE_NONE 0x0000 #define SERVICE_ADDR 0x0001 #define SERVICE_PORT 0x0002 /* default scheduler */ #define DEF_SCHED "wlc" /* default tunnel type */ #define DEF_TUNNEL_TYPE "ipip" /* default multicast interface name */ #define DEF_MCAST_IFN "eth0" #define CONN_PROC_FILE "/proc/net/ip_vs_conn" struct ipvs_command_entry { int cmd; ipvs_service_t svc; ipvs_dest_t dest; ipvs_timeout_t timeout; ipvs_daemon_t daemon; }; /* Use values outside ASCII range so that if an option has * a short name it can be used as the tag */ enum { TAG_SET = 128, TAG_START_DAEMON, TAG_STOP_DAEMON , TAG_MCAST_INTERFACE, TAG_TIMEOUT, TAG_DAEMON, TAG_STATS, TAG_RATE, TAG_THRESHOLDS, TAG_PERSISTENTCONN, TAG_SORT, TAG_NO_SORT, TAG_PERSISTENCE_ENGINE, TAG_SCTP_SERVICE, TAG_MCAST_GROUP, TAG_MCAST_PORT, TAG_MCAST_TTL, TAG_SYNC_MAXLEN, TAG_TUN_INFO, TAG_TUN_TYPE, TAG_TUN_PORT, TAG_TUN_NOCSUM, TAG_TUN_CSUM, TAG_TUN_REMCSUM, }; /* various parsing helpers & parsing functions */ static int str_is_digit(const char *str); static int string_to_number(const char *s, int min, int max); static int host_to_addr(const char *name, struct in_addr *addr); static char * addr_to_host(int af, const void *addr); static char * addr_to_anyname(int af, const void *addr); static int service_to_port(const char *name, unsigned short proto); static char * port_to_service(unsigned short port, unsigned short proto); static char * port_to_anyname(unsigned short port, unsigned short proto); static char * addrport_to_anyname(int af, const void *addr, unsigned short port, unsigned short proto, unsigned int format); static int parse_service(char *buf, ipvs_service_t *svc); static int parse_netmask(char *buf, u_int32_t *addr); static int parse_timeout(char *buf, int min, int max); static unsigned int parse_fwmark(char *buf); static unsigned int parse_sched_flags(const char *sched, char *optarg); static int parse_tun_type(const char *name); /* check the options based on the commands_v_options table */ static void generic_opt_check(int command, unsigned long long options); static void set_command(int *cmd, const int newcmd); static void set_option(unsigned long long *options, int optc); /* check the options based on the tunnel_types_v_options table */ static void tunnel_opt_check(int tun_type, unsigned long long options); static void tryhelp_exit(const char *program, const int exit_status); static void usage_exit(const char *program, const int exit_status); static void version_exit(int exit_status); static void version(FILE *stream); static void fail(int err, char *msg, ...); /* various listing functions */ static void list_conn(unsigned int format); static void list_service(ipvs_service_t *svc, unsigned int format); static void list_all(unsigned int format); static void list_timeout(void); static void list_daemon(void); static int modprobe_ipvs(void); static void check_ipvs_version(void); static int process_options(int argc, char **argv, int reading_stdin); int main(int argc, char **argv) { int result; if (ipvs_init()) { /* try to insmod the ip_vs module if ipvs_init failed */ if (modprobe_ipvs() || ipvs_init()) fail(2, "Can't initialize ipvs: %s\n" "Are you sure that IP Virtual Server is " "built in the kernel or as module?", ipvs_strerror(errno)); } /* warn the user if the IPVS version is out of date */ check_ipvs_version(); /* list the table if there is no other arguement */ if (argc == 1){ list_all(FMT_NONE); ipvs_close(); return 0; } /* process command line arguments */ result = process_options(argc, argv, 0); ipvs_close(); return result; } static int option_to_protocol(int opt) { switch (opt) { case 't': return IPPROTO_TCP; case 'u': return IPPROTO_UDP; case TAG_SCTP_SERVICE: return IPPROTO_SCTP; default: return IPPROTO_IP; } } static char *option_from_protocol(int proto) { switch (proto) { case IPPROTO_TCP: return "-t"; case IPPROTO_UDP: return "-u"; case IPPROTO_SCTP: return "--sctp-service"; default: return NULL; } } static char *protocol_name(int proto) { switch (proto) { case IPPROTO_TCP: return "TCP"; case IPPROTO_UDP: return "UDP"; case IPPROTO_SCTP: return "SCTP"; default: return "?"; } } static int parse_options(int argc, char **argv, struct ipvs_command_entry *ce, unsigned long long *options, unsigned int *format) { int c, parse; poptContext context; char *optarg = NULL, sched_flags_arg[128]; struct poptOption options_table[] = { { "add-service", 'A', POPT_ARG_NONE, NULL, 'A', NULL, NULL }, { "edit-service", 'E', POPT_ARG_NONE, NULL, 'E', NULL, NULL }, { "delete-service", 'D', POPT_ARG_NONE, NULL, 'D', NULL, NULL }, { "clear", 'C', POPT_ARG_NONE, NULL, 'C', NULL, NULL }, { "list", 'L', POPT_ARG_NONE, NULL, 'L', NULL, NULL }, { "list", 'l', POPT_ARG_NONE, NULL, 'l', NULL, NULL }, { "zero", 'Z', POPT_ARG_NONE, NULL, 'Z', NULL, NULL }, { "add-server", 'a', POPT_ARG_NONE, NULL, 'a', NULL, NULL }, { "edit-server", 'e', POPT_ARG_NONE, NULL, 'e', NULL, NULL }, { "delete-server", 'd', POPT_ARG_NONE, NULL, 'd', NULL, NULL }, { "set", '\0', POPT_ARG_NONE, NULL, TAG_SET, NULL, NULL }, { "help", 'h', POPT_ARG_NONE, NULL, 'h', NULL, NULL }, { "version", 'v', POPT_ARG_NONE, NULL, 'v', NULL, NULL }, { "restore", 'R', POPT_ARG_NONE, NULL, 'R', NULL, NULL }, { "save", 'S', POPT_ARG_NONE, NULL, 'S', NULL, NULL }, { "start-daemon", '\0', POPT_ARG_STRING, &optarg, TAG_START_DAEMON, NULL, NULL }, { "stop-daemon", '\0', POPT_ARG_STRING, &optarg, TAG_STOP_DAEMON, NULL, NULL }, { "tcp-service", 't', POPT_ARG_STRING, &optarg, 't', NULL, NULL }, { "udp-service", 'u', POPT_ARG_STRING, &optarg, 'u', NULL, NULL }, { "sctp-service", '\0', POPT_ARG_STRING, &optarg, TAG_SCTP_SERVICE, NULL, NULL }, { "fwmark-service", 'f', POPT_ARG_STRING, &optarg, 'f', NULL, NULL }, { "scheduler", 's', POPT_ARG_STRING, &optarg, 's', NULL, NULL }, { "persistent", 'p', POPT_ARG_STRING|POPT_ARGFLAG_OPTIONAL, &optarg, 'p', NULL, NULL }, { "netmask", 'M', POPT_ARG_STRING, &optarg, 'M', NULL, NULL }, { "real-server", 'r', POPT_ARG_STRING, &optarg, 'r', NULL, NULL }, { "masquerading", 'm', POPT_ARG_NONE, NULL, 'm', NULL, NULL }, { "ipip", 'i', POPT_ARG_NONE, NULL, 'i', NULL, NULL }, { "gatewaying", 'g', POPT_ARG_NONE, NULL, 'g', NULL, NULL }, { "weight", 'w', POPT_ARG_STRING, &optarg, 'w', NULL, NULL }, { "u-threshold", 'x', POPT_ARG_STRING, &optarg, 'x', NULL, NULL }, { "l-threshold", 'y', POPT_ARG_STRING, &optarg, 'y', NULL, NULL }, { "numeric", 'n', POPT_ARG_NONE, NULL, 'n', NULL, NULL }, { "connection", 'c', POPT_ARG_NONE, NULL, 'c', NULL, NULL }, { "mcast-interface", '\0', POPT_ARG_STRING, &optarg, TAG_MCAST_INTERFACE, NULL, NULL }, { "syncid", '\0', POPT_ARG_STRING, &optarg, 'I', NULL, NULL }, { "timeout", '\0', POPT_ARG_NONE, NULL, TAG_TIMEOUT, NULL, NULL }, { "daemon", '\0', POPT_ARG_NONE, NULL, TAG_DAEMON, NULL, NULL }, { "stats", '\0', POPT_ARG_NONE, NULL, TAG_STATS, NULL, NULL }, { "rate", '\0', POPT_ARG_NONE, NULL, TAG_RATE, NULL, NULL }, { "thresholds", '\0', POPT_ARG_NONE, NULL, TAG_THRESHOLDS, NULL, NULL }, { "persistent-conn", '\0', POPT_ARG_NONE, NULL, TAG_PERSISTENTCONN, NULL, NULL }, { "nosort", '\0', POPT_ARG_NONE, NULL, TAG_NO_SORT, NULL, NULL }, { "sort", '\0', POPT_ARG_NONE, NULL, TAG_SORT, NULL, NULL }, { "exact", 'X', POPT_ARG_NONE, NULL, 'X', NULL, NULL }, { "ipv6", '6', POPT_ARG_NONE, NULL, '6', NULL, NULL }, { "ops", 'o', POPT_ARG_NONE, NULL, 'o', NULL, NULL }, { "pe", '\0', POPT_ARG_STRING, &optarg, TAG_PERSISTENCE_ENGINE, NULL, NULL }, { "sched-flags", 'b', POPT_ARG_STRING, &optarg, 'b', NULL, NULL }, { "mcast-group", '\0', POPT_ARG_STRING, &optarg, TAG_MCAST_GROUP, NULL, NULL }, { "mcast-port", '\0', POPT_ARG_STRING, &optarg, TAG_MCAST_PORT, NULL, NULL }, { "mcast-ttl", '\0', POPT_ARG_STRING, &optarg, TAG_MCAST_TTL, NULL, NULL }, { "sync-maxlen", '\0', POPT_ARG_STRING, &optarg, TAG_SYNC_MAXLEN, NULL, NULL }, { "tun-info", '\0', POPT_ARG_NONE, NULL, TAG_TUN_INFO, NULL, NULL }, { "tun-type", '\0', POPT_ARG_STRING, &optarg, TAG_TUN_TYPE, NULL, NULL }, { "tun-port", '\0', POPT_ARG_STRING, &optarg, TAG_TUN_PORT, NULL, NULL }, { "tun-nocsum", '\0', POPT_ARG_NONE, NULL, TAG_TUN_NOCSUM, NULL, NULL }, { "tun-csum", '\0', POPT_ARG_NONE, NULL, TAG_TUN_CSUM, NULL, NULL }, { "tun-remcsum", '\0', POPT_ARG_NONE, NULL, TAG_TUN_REMCSUM, NULL, NULL }, { NULL, 0, 0, NULL, 0, NULL, NULL } }; sched_flags_arg[0] = '\0'; context = poptGetContext("ipvsadm", argc, (const char **)argv, options_table, 0); if ((c = poptGetNextOpt(context)) < 0) tryhelp_exit(argv[0], -1); switch (c) { case 'A': set_command(&ce->cmd, CMD_ADD); break; case 'E': set_command(&ce->cmd, CMD_EDIT); break; case 'D': set_command(&ce->cmd, CMD_DEL); break; case 'a': set_command(&ce->cmd, CMD_ADDDEST); break; case 'e': set_command(&ce->cmd, CMD_EDITDEST); break; case 'd': set_command(&ce->cmd, CMD_DELDEST); break; case 'C': set_command(&ce->cmd, CMD_FLUSH); break; case 'L': case 'l': set_command(&ce->cmd, CMD_LIST); break; case 'Z': set_command(&ce->cmd, CMD_ZERO); break; case TAG_SET: set_command(&ce->cmd, CMD_TIMEOUT); break; case 'R': set_command(&ce->cmd, CMD_RESTORE); break; case 'S': set_command(&ce->cmd, CMD_SAVE); break; case TAG_START_DAEMON: set_command(&ce->cmd, CMD_STARTDAEMON); if (!strcmp(optarg, "master")) ce->daemon.state = IP_VS_STATE_MASTER; else if (!strcmp(optarg, "backup")) ce->daemon.state = IP_VS_STATE_BACKUP; else fail(2, "illegal start-daemon parameter specified"); break; case TAG_STOP_DAEMON: set_command(&ce->cmd, CMD_STOPDAEMON); if (!strcmp(optarg, "master")) ce->daemon.state = IP_VS_STATE_MASTER; else if (!strcmp(optarg, "backup")) ce->daemon.state = IP_VS_STATE_BACKUP; else fail(2, "illegal start_daemon specified"); break; case 'h': usage_exit(argv[0], 0); break; case 'v': version_exit(0); break; default: tryhelp_exit(argv[0], -1); } while ((c=poptGetNextOpt(context)) >= 0){ switch (c) { case 't': case 'u': case TAG_SCTP_SERVICE: set_option(options, OPTC_SERVICE); ce->svc.protocol = option_to_protocol(c); parse = parse_service(optarg, &ce->svc); if (!(parse & SERVICE_ADDR)) fail(2, "illegal virtual server " "address[:port] specified"); break; case 'f': set_option(options, OPTC_SERVICE); /* * Set protocol to a sane values, even * though it is not used */ ce->svc.af = AF_INET; ce->svc.protocol = IPPROTO_TCP; ce->svc.fwmark = parse_fwmark(optarg); break; case 's': set_option(options, OPTC_SCHEDULER); strncpy(ce->svc.sched_name, optarg, IP_VS_SCHEDNAME_MAXLEN - 1); break; case 'p': set_option(options, OPTC_PERSISTENT); ce->svc.flags |= IP_VS_SVC_F_PERSISTENT; ce->svc.timeout = parse_timeout(optarg, 1, MAX_TIMEOUT); break; case 'M': set_option(options, OPTC_NETMASK); if (ce->svc.af != AF_INET6) { parse = parse_netmask(optarg, &ce->svc.netmask); if (parse != 1) fail(2, "illegal virtual server " "persistent mask specified"); } else { ce->svc.netmask = atoi(optarg); if ((ce->svc.netmask < 1) || (ce->svc.netmask > 128)) fail(2, "illegal ipv6 netmask specified"); } break; case 'r': set_option(options, OPTC_SERVER); ipvs_service_t t_dest = ce->svc; parse = parse_service(optarg, &t_dest); ce->dest.af = t_dest.af; ce->dest.addr = t_dest.addr; ce->dest.port = t_dest.port; if (!(parse & SERVICE_ADDR)) fail(2, "illegal real server " "address[:port] specified"); /* copy vport to dport if not specified */ if (parse == 1) ce->dest.port = ce->svc.port; break; case 'i': set_option(options, OPTC_FORWARD); ce->dest.conn_flags = IP_VS_CONN_F_TUNNEL; break; case 'g': set_option(options, OPTC_FORWARD); ce->dest.conn_flags = IP_VS_CONN_F_DROUTE; break; case 'm': set_option(options, OPTC_FORWARD); ce->dest.conn_flags = IP_VS_CONN_F_MASQ; break; case 'w': set_option(options, OPTC_WEIGHT); if ((ce->dest.weight = string_to_number(optarg, 0, 65535)) == -1) fail(2, "illegal weight specified"); break; case 'x': set_option(options, OPTC_UTHRESHOLD); if ((ce->dest.u_threshold = string_to_number(optarg, 0, INT_MAX)) == -1) fail(2, "illegal u_threshold specified"); break; case 'y': set_option(options, OPTC_LTHRESHOLD); if ((ce->dest.l_threshold = string_to_number(optarg, 0, INT_MAX)) == -1) fail(2, "illegal l_threshold specified"); break; case 'c': set_option(options, OPTC_CONNECTION); break; case 'n': set_option(options, OPTC_NUMERIC); *format |= FMT_NUMERIC; break; case TAG_MCAST_INTERFACE: set_option(options, OPTC_MCAST); strncpy(ce->daemon.mcast_ifn, optarg, IP_VS_IFNAME_MAXLEN - 1); break; case 'I': set_option(options, OPTC_SYNCID); if ((ce->daemon.syncid = string_to_number(optarg, 0, 255)) == -1) fail(2, "illegal syncid specified"); break; case TAG_TIMEOUT: set_option(options, OPTC_TIMEOUT); break; case TAG_DAEMON: set_option(options, OPTC_DAEMON); break; case TAG_STATS: set_option(options, OPTC_STATS); *format |= FMT_STATS; break; case TAG_RATE: set_option(options, OPTC_RATE); *format |= FMT_RATE; break; case TAG_THRESHOLDS: set_option(options, OPTC_THRESHOLDS); *format |= FMT_THRESHOLDS; break; case TAG_PERSISTENTCONN: set_option(options, OPTC_PERSISTENTCONN); *format |= FMT_PERSISTENTCONN; break; case TAG_NO_SORT: set_option(options, OPTC_NOSORT); *format |= FMT_NOSORT; break; case TAG_SORT: /* Sort is the default, this is a no-op for compatibility */ break; case 'X': set_option(options, OPTC_EXACT); *format |= FMT_EXACT; break; case '6': if (ce->svc.fwmark) { ce->svc.af = AF_INET6; ce->svc.netmask = 128; } else { fail(2, "-6 used before -f\n"); } break; case 'o': set_option(options, OPTC_ONEPACKET); ce->svc.flags |= IP_VS_SVC_F_ONEPACKET; break; case TAG_PERSISTENCE_ENGINE: set_option(options, OPTC_PERSISTENCE_ENGINE); strncpy(ce->svc.pe_name, optarg, IP_VS_PENAME_MAXLEN); break; case 'b': set_option(options, OPTC_SCHED_FLAGS); snprintf(sched_flags_arg, sizeof(sched_flags_arg), "%s", optarg); break; case TAG_MCAST_GROUP: set_option(options, OPTC_MCAST_GROUP); if (strchr(optarg, ':')) { if (inet_pton(AF_INET6, optarg, &ce->daemon.mcast_group) <= 0 || !IN6_IS_ADDR_MULTICAST( &ce->daemon.mcast_group.in6)) fail(2, "invalid IPv6 mcast-group `%s'", optarg); ce->daemon.mcast_af = AF_INET6; } else { if (inet_pton(AF_INET, optarg, &ce->daemon.mcast_group) <= 0 || !IN_MULTICAST(ntohl( ce->daemon.mcast_group.ip))) fail(2, "invalid IPv4 mcast-group `%s'", optarg); ce->daemon.mcast_af = AF_INET; } break; case TAG_MCAST_PORT: set_option(options, OPTC_MCAST_PORT); parse = string_to_number(optarg, 1, 65535); if (parse == -1) fail(2, "illegal mcast-port specified"); ce->daemon.mcast_port = parse; break; case TAG_MCAST_TTL: set_option(options, OPTC_MCAST_TTL); parse = string_to_number(optarg, 1, 255); if (parse == -1) fail(2, "illegal mcast-ttl specified"); ce->daemon.mcast_ttl = parse; break; case TAG_SYNC_MAXLEN: set_option(options, OPTC_SYNC_MAXLEN); parse = string_to_number(optarg, 1, 65535 - 20 - 8); if (parse == -1) fail(2, "illegal sync-maxlen specified"); ce->daemon.sync_maxlen = parse; break; case TAG_TUN_INFO: set_option(options, OPTC_TUN_INFO); *format |= FMT_TUN_INFO; break; case TAG_TUN_TYPE: set_option(options, OPTC_TUN_TYPE); parse = parse_tun_type(optarg); if (parse == -1) fail(2, "illegal tunnel type specified"); ce->dest.tun_type = parse; break; case TAG_TUN_PORT: set_option(options, OPTC_TUN_PORT); parse = string_to_number(optarg, 1, 65535); if (parse == -1) fail(2, "illegal tunnel port specified"); ce->dest.tun_port = htons(parse); break; case TAG_TUN_NOCSUM: set_option(options, OPTC_TUN_NOCSUM); ce->dest.tun_flags |= IP_VS_TUNNEL_ENCAP_FLAG_NOCSUM; break; case TAG_TUN_CSUM: set_option(options, OPTC_TUN_CSUM); ce->dest.tun_flags |= IP_VS_TUNNEL_ENCAP_FLAG_CSUM; break; case TAG_TUN_REMCSUM: set_option(options, OPTC_TUN_REMCSUM); ce->dest.tun_flags |= IP_VS_TUNNEL_ENCAP_FLAG_REMCSUM; break; default: fail(2, "invalid option `%s'", poptBadOption(context, POPT_BADOPTION_NOALIAS)); } } if (c < -1) { /* an error occurred during option processing */ fprintf(stderr, "%s: %s\n", poptBadOption(context, POPT_BADOPTION_NOALIAS), poptStrerror(c)); poptFreeContext(context); return -1; } if (ce->cmd == CMD_TIMEOUT) { char *optarg1, *optarg2; if ((optarg=(char *)poptGetArg(context)) && (optarg1=(char *)poptGetArg(context)) && (optarg2=(char *)poptGetArg(context))) { ce->timeout.tcp_timeout = parse_timeout(optarg, 0, MAX_TIMEOUT); ce->timeout.tcp_fin_timeout = parse_timeout(optarg1, 0, MAX_TIMEOUT); ce->timeout.udp_timeout = parse_timeout(optarg2, 0, MAX_TIMEOUT); } else fail(2, "--set option requires 3 timeout values"); } if ((optarg=(char *)poptGetArg(context))) fail(2, "unexpected argument %s", optarg); if (sched_flags_arg[0]) { ce->svc.flags &= ~(IP_VS_SVC_F_SCHED1 | IP_VS_SVC_F_SCHED2 | IP_VS_SVC_F_SCHED3); ce->svc.flags |= parse_sched_flags(ce->svc.sched_name, sched_flags_arg); } poptFreeContext(context); return 0; } static int restore_table(int argc, char **argv, int reading_stdin) { int result = 0; dynamic_array_t *a; /* avoid infinite loop */ if (reading_stdin != 0) tryhelp_exit(argv[0], -1); while ((a = config_stream_read(stdin, argv[0])) != NULL) { int i; if ((i = (int)dynamic_array_get_count(a)) > 1) { char **strv = dynamic_array_get_vector(a); result = process_options(i, strv, 1); } dynamic_array_destroy(a, DESTROY_STR); } return result; } static int process_options(int argc, char **argv, int reading_stdin) { struct ipvs_command_entry ce; unsigned long long options = OPT_NONE; unsigned int format = FMT_NONE; unsigned int fwd_method; int result = 0; memset(&ce, 0, sizeof(struct ipvs_command_entry)); ce.cmd = CMD_NONE; /* Set the default weight 1 */ ce.dest.weight = 1; /* Set the default tunnel type 0(ipip) */ ce.dest.tun_type = 0; /* Set the default tunnel port 0(n/a) */ ce.dest.tun_port = 0; /* Set the default tunnel flags 0(nocsum) */ ce.dest.tun_flags = 0; /* Set direct routing as default forwarding method */ ce.dest.conn_flags = IP_VS_CONN_F_DROUTE; /* Set the default persistent granularity to /32 mask */ ce.svc.netmask = ((u_int32_t) 0xffffffff); if (parse_options(argc, argv, &ce, &options, &format)) return -1; generic_opt_check(ce.cmd, options); if (ce.cmd == CMD_ADD || ce.cmd == CMD_EDIT) { /* Make sure that port zero service is persistent */ if (!ce.svc.fwmark && !ce.svc.port && !(ce.svc.flags & IP_VS_SVC_F_PERSISTENT)) fail(2, "Zero port specified " "for non-persistent service"); if (ce.svc.flags & IP_VS_SVC_F_ONEPACKET && !ce.svc.fwmark && ce.svc.protocol != IPPROTO_UDP) fail(2, "One-Packet Scheduling is only " "for UDP virtual services"); /* Set the default scheduling algorithm if not specified */ if (strlen(ce.svc.sched_name) == 0) strcpy(ce.svc.sched_name, DEF_SCHED); } if (ce.cmd == CMD_STARTDAEMON && strlen(ce.daemon.mcast_ifn) == 0) strcpy(ce.daemon.mcast_ifn, DEF_MCAST_IFN); fwd_method = ce.dest.conn_flags & IP_VS_CONN_F_FWD_MASK; if (ce.cmd == CMD_ADDDEST || ce.cmd == CMD_EDITDEST) { /* * The destination port must be equal to the service port * if the IP_VS_CONN_F_TUNNEL or IP_VS_CONN_F_DROUTE is set. * Don't worry about this if fwmark is used. */ if (!ce.svc.fwmark && (fwd_method == IP_VS_CONN_F_TUNNEL || fwd_method == IP_VS_CONN_F_DROUTE)) ce.dest.port = ce.svc.port; /* Tunneling allows different address family */ if (ce.dest.af != ce.svc.af && fwd_method != IP_VS_CONN_F_TUNNEL) fail(2, "Different address family is allowed only " "for tunneling servers"); /* Only tunneling allows tunnel options */ if (((options & (OPT_TUN_TYPE | OPT_TUN_PORT)) || (options & (OPT_TUN_NOCSUM | OPT_TUN_CSUM)) || (options & OPT_TUN_REMCSUM)) && fwd_method != IP_VS_CONN_F_TUNNEL) fail(2, "Tunnel options conflict with forward method"); tunnel_opt_check(ce.dest.tun_type, options); } switch (ce.cmd) { case CMD_LIST: if ((options & (OPT_CONNECTION|OPT_TIMEOUT|OPT_DAEMON) && options & (OPT_STATS|OPT_RATE|OPT_THRESHOLDS)) || (options & (OPT_TIMEOUT|OPT_DAEMON) && options & OPT_PERSISTENTCONN)) fail(2, "options conflicts in the list command"); if (options & OPT_CONNECTION) list_conn(format); else if (options & OPT_SERVICE) list_service(&ce.svc, format); else if (options & OPT_TIMEOUT) list_timeout(); else if (options & OPT_DAEMON) list_daemon(); else list_all(format); return 0; case CMD_RESTORE: return restore_table(argc, argv, reading_stdin); case CMD_SAVE: format |= FMT_RULE; list_all(format); return 0; case CMD_FLUSH: result = ipvs_flush(); break; case CMD_ADD: result = ipvs_add_service(&ce.svc); break; case CMD_EDIT: result = ipvs_update_service(&ce.svc); break; case CMD_DEL: result = ipvs_del_service(&ce.svc); break; case CMD_ZERO: result = ipvs_zero_service(&ce.svc); break; case CMD_ADDDEST: result = ipvs_add_dest(&ce.svc, &ce.dest); break; case CMD_EDITDEST: result = ipvs_update_dest(&ce.svc, &ce.dest); break; case CMD_DELDEST: result = ipvs_del_dest(&ce.svc, &ce.dest); break; case CMD_TIMEOUT: result = ipvs_set_timeout(&ce.timeout); break; case CMD_STARTDAEMON: result = ipvs_start_daemon(&ce.daemon); break; case CMD_STOPDAEMON: result = ipvs_stop_daemon(&ce.daemon); } if (result) fprintf(stderr, "%s\n", ipvs_strerror(errno)); return result; } static int string_to_number(const char *s, int min, int max) { long number; char *end; errno = 0; number = strtol(s, &end, 10); if (*end == '\0' && end != s) { /* We parsed a number, let's see if we want this. */ if (errno != ERANGE && min <= number && number <= max) return number; } return -1; } /* * Parse the timeout value. */ static int parse_timeout(char *buf, int min, int max) { int i; /* it is just for parsing timeout of persistent service */ if (buf == NULL) return IPVS_SVC_PERSISTENT_TIMEOUT; if ((i=string_to_number(buf, min, max)) == -1) fail(2, "invalid timeout value `%s' specified", buf); return i; } /* * Parse IP fwmark from the argument. */ static unsigned int parse_fwmark(char *buf) { unsigned long l; char *end; errno = 0; l = strtol(buf, &end, 10); if (*end != '\0' || end == buf || errno == ERANGE || l <= 0 || l > UINT_MAX) fail(2, "invalid fwmark value `%s' specified", buf); return l; } /* * Get netmask. * Return 0 if failed, * 1 if addr read */ static int parse_netmask(char *buf, u_int32_t *addr) { struct in_addr inaddr; if(buf == NULL) return 0; if (inet_aton(buf, &inaddr) != 0) *addr = inaddr.s_addr; else if (host_to_addr(buf, &inaddr) != -1) *addr = inaddr.s_addr; else return 0; return 1; } /* * Get IP address and port from the argument. * Result is a logical or of * SERVICE_NONE: no service elements set/error * SERVICE_ADDR: addr set * SERVICE_PORT: port set */ static int parse_service(char *buf, ipvs_service_t *svc) { char *portp = NULL; long portn; int result=SERVICE_NONE; struct in_addr inaddr; struct in6_addr inaddr6; if (buf == NULL || str_is_digit(buf)) return SERVICE_NONE; if (buf[0] == '[') { buf++; portp = strchr(buf, ']'); if (portp == NULL) return SERVICE_NONE; *portp = '\0'; portp++; if (*portp == ':') *portp = '\0'; else return SERVICE_NONE; } if (inet_pton(AF_INET6, buf, &inaddr6) > 0) { svc->addr.in6 = inaddr6; svc->af = AF_INET6; svc->netmask = 128; } else { portp = strrchr(buf, ':'); if (portp != NULL) *portp = '\0'; if (inet_aton(buf, &inaddr) != 0) { svc->addr.ip = inaddr.s_addr; svc->af = AF_INET; } else if (host_to_addr(buf, &inaddr) != -1) { svc->addr.ip = inaddr.s_addr; svc->af = AF_INET; } else return SERVICE_NONE; } result |= SERVICE_ADDR; if (portp != NULL) { result |= SERVICE_PORT; if ((portn = string_to_number(portp+1, 0, 65535)) != -1) svc->port = htons(portn); else if ((portn = service_to_port(portp+1, svc->protocol)) != -1) svc->port = htons(portn); else return SERVICE_NONE; } return result; } static unsigned int parse_sched_flags(const char *sched, char *optarg) { unsigned int flags = 0; char *flag; sched = (sched && *sched) ? sched : DEF_SCHED; flag = strtok(optarg, ","); do { if (!strcmp(flag, "flag-1")) { flags |= IP_VS_SVC_F_SCHED1; } else if (!strcmp(flag, "flag-2")) { flags |= IP_VS_SVC_F_SCHED2; } else if (!strcmp(flag, "flag-3")) { flags |= IP_VS_SVC_F_SCHED3; } else if (!strcmp(flag, "sh-fallback")) { flags |= IP_VS_SVC_F_SCHED_SH_FALLBACK; if (strcmp(sched, "sh")) fail(2, "incompatible scheduler flag `%s'", flag); } else if (!strcmp(flag, "sh-port")) { flags |= IP_VS_SVC_F_SCHED_SH_PORT; if (strcmp(sched, "sh")) fail(2, "incompatible scheduler flag `%s'", flag); } else if (!strcmp(flag, "mh-fallback")) { flags |= IP_VS_SVC_F_SCHED_MH_FALLBACK; if (strcmp(sched, "mh")) fail(2, "incompatible scheduler flag `%s'", flag); } else if (!strcmp(flag, "mh-port")) { flags |= IP_VS_SVC_F_SCHED_MH_PORT; if (strcmp(sched, "mh")) fail(2, "incompatible scheduler flag `%s'", flag); } else { fail(2, "invalid scheduler flag `%s'", flag); } } while ((flag = strtok(NULL, ",")) != NULL); return flags; } static int parse_tun_type(const char *tun_type) { int type = -1; if (!strcmp(tun_type, "ipip")) type = IP_VS_CONN_F_TUNNEL_TYPE_IPIP; else if (!strcmp(tun_type, "gue")) type = IP_VS_CONN_F_TUNNEL_TYPE_GUE; else if (!strcmp(tun_type, "gre")) type = IP_VS_CONN_F_TUNNEL_TYPE_GRE; else type = -1; return type; } static void generic_opt_check(int command, unsigned long long options) { int i, j; int last = 0, count = 0; /* Check that commands are valid with options. */ i = command - CMD_NONE -1; for (j = 0; j < NUMBER_OF_OPT; j++) { if (!(options & (1ULL << j))) { if (commands_v_options[i][j] == '+') fail(2, "You need to supply the '%s' " "option for the '%s' command", optnames[j], cmdnames[i]); } else { if (commands_v_options[i][j] == 'x') fail(2, "Illegal '%s' option with " "the '%s' command", optnames[j], cmdnames[i]); if (commands_v_options[i][j] == '1') { count++; if (count == 1) { last = j; continue; } fail(2, "The option '%s' conflicts with the " "'%s' option in the '%s' command", optnames[j], optnames[last], cmdnames[i]); } } } } static void tunnel_opt_check(int tun_type, unsigned long long options) { int i, j, k; int last = 0, count = 0; /* Check that tunnel types are valid with options. */ i = tun_type; for (j = 0; j < NUMBER_OF_TUN_OPT; j++) { k = tunopts[j]; if (!(options & (1ULL << k))) { if (tunnel_types_v_options[i][j] == '+') fail(2, "You need to supply the '%s' " "option for the '%s' tunnel type", optnames[k], tunnames[i]); } else { if (tunnel_types_v_options[i][j] == 'x') fail(2, "Illegal '%s' option with " "the '%s' tunnel type", optnames[k], tunnames[i]); if (tunnel_types_v_options[i][j] == '1') { count++; if (count == 1) { last = k; continue; } fail(2, "The option '%s' conflicts with the " "'%s' option in the '%s' tunnel type", optnames[k], optnames[last], tunnames[i]); } } } } static void set_command(int *cmd, const int newcmd) { if (*cmd != CMD_NONE) fail(2, "multiple commands specified"); *cmd = newcmd; } static void set_option(unsigned long long *options, int optc) { unsigned long long option = 1ULL << optc; if (*options & option) fail(2, "multiple '%s' options specified", optnames[optc]); *options |= option; } static void tryhelp_exit(const char *program, const int exit_status) { fprintf(stderr, "Try `%s -h' or '%s --help' for more information.\n", program, program); exit(exit_status); } static void usage_exit(const char *program, const int exit_status) { FILE *stream; if (exit_status != 0) stream = stderr; else stream = stdout; version(stream); fprintf(stream, "Usage:\n" " %s -A|E virtual-service [-s scheduler] [-p [timeout]] [-M netmask] [--pe persistence_engine] [-b sched-flags]\n" " %s -D virtual-service\n" " %s -C\n" " %s -R\n" " %s -S [-n]\n" " %s -a|e virtual-service -r server-address [options]\n" " %s -d virtual-service -r server-address\n" " %s -L|l [virtual-service] [options]\n" " %s -Z [virtual-service]\n" " %s --set tcp tcpfin udp\n" " %s --start-daemon {master|backup} [daemon-options]\n" " %s --stop-daemon {master|backup}\n" " %s -h\n\n", program, program, program, program, program, program, program, program, program, program, program, program, program); fprintf(stream, "Commands:\n" "Either long or short options are allowed.\n" " --add-service -A add virtual service with options\n" " --edit-service -E edit virtual service with options\n" " --delete-service -D delete virtual service\n" " --clear -C clear the whole table\n" " --restore -R restore rules from stdin\n" " --save -S save rules to stdout\n" " --add-server -a add real server with options\n" " --edit-server -e edit real server with options\n" " --delete-server -d delete real server\n" " --list -L|-l list the table\n" " --zero -Z zero counters in a service or all services\n" " --set tcp tcpfin udp set connection timeout values\n" " --start-daemon start connection sync daemon\n" " --stop-daemon stop connection sync daemon\n" " --help -h display this help message\n\n" ); fprintf(stream, "virtual-service:\n" " --tcp-service|-t service-address service-address is host[:port]\n" " --udp-service|-u service-address service-address is host[:port]\n" " --sctp-service service-address service-address is host[:port]\n" " --fwmark-service|-f fwmark fwmark is an integer greater than zero\n" "\n"); fprintf(stream, "Options:\n" " --ipv6 -6 fwmark entry uses IPv6\n" " --scheduler -s scheduler one of " SCHEDULERS ",\n" " the default scheduler is %s.\n" " --pe engine alternate persistence engine may be " PE_LIST ",\n" " not set by default.\n" " --persistent -p [timeout] persistent service\n" " --netmask -M netmask persistent granularity mask\n" " --real-server -r server-address server-address is host (and port)\n" " --gatewaying -g gatewaying (direct routing) (default)\n" " --ipip -i ipip encapsulation (tunneling)\n" " --masquerading -m masquerading (NAT)\n" " --tun-type type one of ipip|gue|gre,\n" " the default tunnel type is %s.\n" " --tun-port port tunnel destination port\n" " --tun-nocsum tunnel encapsulation without checksum\n" " --tun-csum tunnel encapsulation with checksum\n" " --tun-remcsum tunnel encapsulation with remote checksum\n" " --weight -w weight capacity of real server\n" " --u-threshold -x uthreshold upper threshold of connections\n" " --l-threshold -y lthreshold lower threshold of connections\n" " --connection -c output of current IPVS connections\n" " --timeout output of timeout (tcp tcpfin udp)\n" " --daemon output of daemon information\n" " --stats output of statistics information\n" " --rate output of rate information\n" " --exact expand numbers (display exact values)\n" " --thresholds output of thresholds information\n" " --persistent-conn output of persistent connection info\n" " --tun-info output of tunnel information\n" " --nosort disable sorting output of service/server entries\n" " --sort does nothing, for backwards compatibility\n" " --ops -o one-packet scheduling\n" " --numeric -n numeric output of addresses and ports\n" " --sched-flags -b flags scheduler flags (comma-separated)\n", DEF_SCHED, DEF_TUNNEL_TYPE); fprintf(stream, "Daemon Options:\n" " --syncid sid syncid for connection sync (default=255)\n" " --sync-maxlen length Max sync message length (default=1472)\n" " --mcast-interface interface multicast interface for connection sync\n" " --mcast-group address IPv4/IPv6 group (default=224.0.0.81)\n" " --mcast-port port UDP port (default=8848)\n" " --mcast-ttl ttl Multicast TTL (default=1)\n" ); exit(exit_status); } static void version_exit(const int exit_status) { FILE *stream; if (exit_status != 0) stream = stderr; else stream = stdout; version(stream); exit(exit_status); } static void version(FILE *stream) { fprintf(stream, "ipvsadm " IPVSADM_VERSION " (compiled with " IPVS_OPTION_PROCESSING " and IPVS v%d.%d.%d)\n", NVERSION(IP_VS_VERSION_CODE)); } static void fail(int err, char *msg, ...) { va_list args; va_start(args, msg); vfprintf(stderr, msg, args); va_end(args); fprintf(stderr, "\n"); exit(err); } static int modprobe_ipvs(void) { char *argv[] = { "/sbin/modprobe", "--", "ip_vs", NULL }; int child; int status; int rc; if (!(child = fork())) { execv(argv[0], argv); exit(1); } rc = waitpid(child, &status, 0); if (rc == -1 || !WIFEXITED(status) || WEXITSTATUS(status)) { return 1; } return 0; } static void check_ipvs_version(void) { /* verify the IPVS version */ if (ipvs_info.version < IPVS_VERSION(MINIMUM_IPVS_VERSION_MAJOR, MINIMUM_IPVS_VERSION_MINOR, MINIMUM_IPVS_VERSION_PATCH)) { fprintf(stderr, "Warning: IPVS version mismatch: \n" " Kernel compiled with IPVS version %d.%d.%d\n" " ipvsadm " IPVSADM_VERSION_NO " requires minimum IPVS version %d.%d.%d\n\n", NVERSION(ipvs_info.version), MINIMUM_IPVS_VERSION_MAJOR, MINIMUM_IPVS_VERSION_MINOR, MINIMUM_IPVS_VERSION_PATCH); } } static void print_conn(char *buf, unsigned int format) { char protocol[8]; unsigned short proto; union nf_inet_addr caddr; unsigned short cport; union nf_inet_addr vaddr; unsigned short vport; union nf_inet_addr daddr; unsigned short dport; char state[16]; unsigned int expires; unsigned short af = AF_INET; unsigned short daf = AF_INET; char pe_name[IP_VS_PENAME_MAXLEN + 1]; char pe_data[IP_VS_PEDATA_MAXLEN + 1]; int n; char temp1[INET6_ADDRSTRLEN], temp2[INET6_ADDRSTRLEN], temp3[INET6_ADDRSTRLEN]; char *cname, *vname, *dname; unsigned int minutes, seconds; char expire_str[12]; if ((n = sscanf(buf, "%s %s %hX %s %hX %s %hX %s %d %s %s", protocol, temp1, &cport, temp2, &vport, temp3, &dport, state, &expires, pe_name, pe_data)) == -1) exit(1); if (strcmp(protocol, "TCP") == 0) proto = IPPROTO_TCP; else if (strcmp(protocol, "UDP") == 0) proto = IPPROTO_UDP; else if (strcmp(protocol, "SCTP") == 0) proto = IPPROTO_SCTP; else proto = 0; if (inet_pton(AF_INET6, temp1, &caddr.in6) > 0) { inet_pton(AF_INET6, temp2, &vaddr.in6); af = AF_INET6; } else if (inet_pton(AF_INET, temp1, &caddr.ip) > 0) { inet_pton(AF_INET, temp2, &vaddr.ip); } else { caddr.ip = (__u32) htonl(strtoul(temp1, NULL, 16)); vaddr.ip = (__u32) htonl(strtoul(temp2, NULL, 16)); } if (inet_pton(AF_INET6, temp3, &daddr.in6) > 0) daf = AF_INET6; else if (inet_pton(AF_INET, temp3, &daddr.ip) <= 0) daddr.ip = (__u32) htonl(strtoul(temp3, NULL, 16)); if (!(cname = addrport_to_anyname(af, &caddr, cport, proto, format))) exit(1); if (!(vname = addrport_to_anyname(af, &vaddr, vport, proto, format))) exit(1); if (!(dname = addrport_to_anyname(daf, &daddr, dport, proto, format))) exit(1); seconds = expires % 60; minutes = expires / 60; sprintf(expire_str, "%02d:%02d", minutes, seconds); if (format & FMT_PERSISTENTCONN && n == 11) printf("%-3s %-6s %-11s %-18s %-18s %-16s %-18s %s\n", protocol, expire_str, state, cname, vname, dname, pe_name, pe_data); else printf("%-3s %-6s %-11s %-18s %-18s %s\n", protocol, expire_str, state, cname, vname, dname); free(cname); free(vname); free(dname); } void list_conn(unsigned int format) { static char buffer[256]; FILE *handle; handle = fopen(CONN_PROC_FILE, "r"); if (!handle) { fprintf(stderr, "cannot open file %s\n", CONN_PROC_FILE); exit(1); } /* read the first line */ if (fgets(buffer, sizeof(buffer), handle) == NULL) { fprintf(stderr, "unexpected input from %s\n", CONN_PROC_FILE); exit(1); } printf("IPVS connection entries\n"); if (format & FMT_PERSISTENTCONN) printf("pro expire %-11s %-18s %-18s %-18s %-16s %s\n", "state", "source", "virtual", "destination", "pe name", "pe_data"); else printf("pro expire %-11s %-18s %-18s %s\n", "state", "source", "virtual", "destination"); /* * Print the VS information according to the format */ while (!feof(handle)) { if (fgets(buffer, sizeof(buffer), handle)) print_conn(buffer, format); } fclose(handle); } static inline char *fwd_name(unsigned flags) { char *fwd = NULL; switch (flags & IP_VS_CONN_F_FWD_MASK) { case IP_VS_CONN_F_MASQ: fwd = "Masq"; break; case IP_VS_CONN_F_LOCALNODE: fwd = "Local"; break; case IP_VS_CONN_F_TUNNEL: fwd = "Tunnel"; break; case IP_VS_CONN_F_DROUTE: fwd = "Route"; break; } return fwd; } static inline char *fwd_switch(unsigned flags) { char *swt = NULL; switch (flags & IP_VS_CONN_F_FWD_MASK) { case IP_VS_CONN_F_MASQ: swt = "-m"; break; case IP_VS_CONN_F_TUNNEL: swt = "-i"; break; case IP_VS_CONN_F_LOCALNODE: case IP_VS_CONN_F_DROUTE: swt = "-g"; break; } return swt; } static inline char *fwd_tun_info(ipvs_dest_entry_t *e) { char *info = malloc(16); if (!info) return NULL; switch (e->conn_flags & IP_VS_CONN_F_FWD_MASK) { case IP_VS_CONN_F_TUNNEL: switch (e->tun_type) { case IP_VS_CONN_F_TUNNEL_TYPE_IPIP: snprintf(info, 16, "%s", tunnames[e->tun_type]); break; case IP_VS_CONN_F_TUNNEL_TYPE_GUE: snprintf(info, 16, "%s:%d:%s", tunnames[e->tun_type], ntohs(e->tun_port), tunflags[e->tun_flags]); break; case IP_VS_CONN_F_TUNNEL_TYPE_GRE: snprintf(info, 16, "%s:%s", tunnames[e->tun_type], tunflags[e->tun_flags]); break; default: free(info); return NULL; } break; default: free(info); return NULL; } return info; } static void print_largenum(unsigned long long i, unsigned int format) { if (format & FMT_EXACT) { char mytmp[32]; int len; len = snprintf(mytmp, 32, "%llu", i); printf("%*llu", len <= 8 ? 9 : len + 1, i); return; } if (i < 100000000) /* less than 100 million */ printf("%9llu", i); else if (i < 1000000000) /* less than 1 billion */ printf("%8lluK", i / 1000); else if (i < 100000000000ULL) /* less than 100 billion */ printf("%8lluM", i / 1000000); else if (i < 100000000000000ULL) /* less than 100 trillion */ printf("%8lluG", i / 1000000000ULL); else printf("%8lluT", i / 1000000000000ULL); } static void print_sched_flags(ipvs_service_entry_t *se) { char flags[64]; flags[0] = '\0'; if (!strcmp(se->sched_name, "sh")) { if (se->flags & IP_VS_SVC_F_SCHED_SH_FALLBACK) strcat(flags, "sh-fallback,"); if (se->flags & IP_VS_SVC_F_SCHED_SH_PORT) strcat(flags, "sh-port,"); } else if (!strcmp(se->sched_name, "mh")) { if (se->flags & IP_VS_SVC_F_SCHED_MH_FALLBACK) strcat(flags, "mh-fallback,"); if (se->flags & IP_VS_SVC_F_SCHED_MH_PORT) strcat(flags, "mh-port,"); } else { if (se->flags & IP_VS_SVC_F_SCHED1) strcat(flags, "flag-1,"); if (se->flags & IP_VS_SVC_F_SCHED2) strcat(flags, "flag-2,"); if (se->flags & IP_VS_SVC_F_SCHED3) strcat(flags, "flag-3,"); } if (flags[0]) { flags[strlen(flags)-1] = '\0'; printf("%s", flags); } } static void print_title(unsigned int format) { if (format & FMT_STATS) printf("%-33s %8s %8s %8s %8s %8s\n" " -> RemoteAddress:Port\n", "Prot LocalAddress:Port", "Conns", "InPkts", "OutPkts", "InBytes", "OutBytes"); else if (format & FMT_RATE) printf("%-33s %8s %8s %8s %8s %8s\n" " -> RemoteAddress:Port\n", "Prot LocalAddress:Port", "CPS", "InPPS", "OutPPS", "InBPS", "OutBPS"); else if (format & FMT_THRESHOLDS) printf("%-33s %-10s %-10s %-10s %-10s\n" " -> RemoteAddress:Port\n", "Prot LocalAddress:Port", "Uthreshold", "Lthreshold", "ActiveConn", "InActConn"); else if (format & FMT_PERSISTENTCONN) printf("%-33s %-9s %-11s %-10s %-10s\n" " -> RemoteAddress:Port\n", "Prot LocalAddress:Port", "Weight", "PersistConn", "ActiveConn", "InActConn"); else if ((format & FMT_TUN_INFO)) printf("Prot LocalAddress:Port Scheduler Flags\n" " -> RemoteAddress:Port Forward TunnelInfo Weight ActiveConn InActConn\n"); else if (!(format & FMT_RULE)) printf("Prot LocalAddress:Port Scheduler Flags\n" " -> RemoteAddress:Port Forward Weight ActiveConn InActConn\n"); } static inline void print_tunnel_rule(char *svc_name, char *dname, ipvs_dest_entry_t *e) { switch (e->tun_type) { case IP_VS_CONN_F_TUNNEL_TYPE_GRE: printf("-a %s -r %s %s -w %d --tun-type %s %s\n", svc_name, dname, fwd_switch(e->conn_flags), e->weight, tunnames[e->tun_type], tun_flags_opts[e->tun_flags]); break; case IP_VS_CONN_F_TUNNEL_TYPE_GUE: printf("-a %s -r %s %s -w %d --tun-type %s --tun-port %d %s\n", svc_name, dname, fwd_switch(e->conn_flags), e->weight, tunnames[e->tun_type], ntohs(e->tun_port), tun_flags_opts[e->tun_flags]); break; case IP_VS_CONN_F_TUNNEL_TYPE_IPIP: printf("-a %s -r %s %s -w %d --tun-type %s\n", svc_name, dname, fwd_switch(e->conn_flags), e->weight, tunnames[e->tun_type]); break; default: printf("-a %s -r %s %s -w %d\n", svc_name, dname, fwd_switch(e->conn_flags), e->weight); break; } } static void print_service_entry(ipvs_service_entry_t *se, unsigned int format) { struct ip_vs_get_dests *d; char svc_name[1024]; int i; if (!(d = ipvs_get_dests(se))) { fprintf(stderr, "%s\n", ipvs_strerror(errno)); exit(1); } if (se->fwmark) { if (format & FMT_RULE) if (se->af == AF_INET6) sprintf(svc_name, "-f %u -6", se->fwmark); else sprintf(svc_name, "-f %u", se->fwmark); else if (se->af == AF_INET6) sprintf(svc_name, "FWM %u IPv6", se->fwmark); else sprintf(svc_name, "FWM %u", se->fwmark); } else { char *vname; if (!(vname = addrport_to_anyname(se->af, &se->addr, ntohs(se->port), se->protocol, format))) fail(2, "addrport_to_anyname: %s", strerror(errno)); if (format & FMT_RULE) { char *stype = option_from_protocol(se->protocol) ? : "--xxx-service"; snprintf(svc_name, sizeof(svc_name), "%s %s", stype, vname); } else { char *stype = protocol_name(se->protocol); snprintf(svc_name, sizeof(svc_name), "%-4s %s", stype, vname); if (se->af != AF_INET6) svc_name[33] = '\0'; } free(vname); } /* print virtual service info */ if (format & FMT_RULE) { printf("-A %s -s %s", svc_name, se->sched_name); if (se->flags & IP_VS_SVC_F_PERSISTENT) { printf(" -p %u", se->timeout); if (se->af == AF_INET) if (se->netmask != (unsigned long int) 0xffffffff) { struct in_addr mask; mask.s_addr = se->netmask; printf(" -M %s", inet_ntoa(mask)); } if (se->af == AF_INET6) if (se->netmask != 128) { printf(" -M %i", se->netmask); } } if (se->pe_name[0]) printf(" --pe %s", se->pe_name); if (se->flags & IP_VS_SVC_F_ONEPACKET) printf(" -o"); if (se->flags & (IP_VS_SVC_F_SCHED1 | IP_VS_SVC_F_SCHED2 | IP_VS_SVC_F_SCHED3)) { printf(" -b "); print_sched_flags(se); } } else if (format & FMT_STATS) { printf("%-33s", svc_name); print_largenum(se->stats64.conns, format); print_largenum(se->stats64.inpkts, format); print_largenum(se->stats64.outpkts, format); print_largenum(se->stats64.inbytes, format); print_largenum(se->stats64.outbytes, format); } else if (format & FMT_RATE) { printf("%-33s", svc_name); print_largenum(se->stats64.cps, format); print_largenum(se->stats64.inpps, format); print_largenum(se->stats64.outpps, format); print_largenum(se->stats64.inbps, format); print_largenum(se->stats64.outbps, format); } else { printf("%s %s", svc_name, se->sched_name); if (se->flags & (IP_VS_SVC_F_SCHED1 | IP_VS_SVC_F_SCHED2 | IP_VS_SVC_F_SCHED3)) { printf(" ("); print_sched_flags(se); printf(")"); } if (se->flags & IP_VS_SVC_F_PERSISTENT) { printf(" persistent %u", se->timeout); if (se->af == AF_INET) if (se->netmask != (unsigned long int) 0xffffffff) { struct in_addr mask; mask.s_addr = se->netmask; printf(" mask %s", inet_ntoa(mask)); } if (se->af == AF_INET6) if (se->netmask != 128) printf(" mask %i", se->netmask); if (se->pe_name[0]) printf(" pe %s", se->pe_name); } if (se->flags & IP_VS_SVC_F_ONEPACKET) printf(" ops"); } printf("\n"); /* print all the destination entries */ if (!(format & FMT_NOSORT)) ipvs_sort_dests(d, ipvs_cmp_dests); for (i = 0; i < d->num_dests; i++) { char *dname; ipvs_dest_entry_t *e = &d->entrytable[i]; unsigned int fwd_method = e->conn_flags & IP_VS_CONN_F_FWD_MASK; if (!(dname = addrport_to_anyname(e->af, &(e->addr), ntohs(e->port), se->protocol, format))) { fprintf(stderr, "addrport_to_anyname fails\n"); exit(1); } if (!(format & FMT_RULE) && (e->af != AF_INET6)) dname[28] = '\0'; if (format & FMT_RULE) { if (fwd_method == IP_VS_CONN_F_TUNNEL) { print_tunnel_rule(svc_name, dname, e); } else { printf("-a %s -r %s %s -w %d\n", svc_name, dname, fwd_switch(e->conn_flags), e->weight); } } else if (format & FMT_STATS) { printf(" -> %-28s", dname); print_largenum(e->stats64.conns, format); print_largenum(e->stats64.inpkts, format); print_largenum(e->stats64.outpkts, format); print_largenum(e->stats64.inbytes, format); print_largenum(e->stats64.outbytes, format); printf("\n"); } else if (format & FMT_RATE) { printf(" -> %-28s %8llu %8llu %8llu", dname, e->stats64.cps, e->stats64.inpps, e->stats64.outpps); print_largenum(e->stats64.inbps, format); print_largenum(e->stats64.outbps, format); printf("\n"); } else if (format & FMT_THRESHOLDS) { printf(" -> %-28s %-10u %-10u %-10u %-10u\n", dname, e->u_threshold, e->l_threshold, e->activeconns, e->inactconns); } else if (format & FMT_PERSISTENTCONN) { printf(" -> %-28s %-9u %-11u %-10u %-10u\n", dname, e->weight, e->persistconns, e->activeconns, e->inactconns); } else if (format & FMT_TUN_INFO) { char *ti = fwd_tun_info(e); printf(" -> %-28s %-7s %-13s %-6d %-10u %-10u\n", dname, fwd_name(e->conn_flags), ti ? : NA, e->weight, e->activeconns, e->inactconns); free(ti); } else printf(" -> %-28s %-7s %-6d %-10u %-10u\n", dname, fwd_name(e->conn_flags), e->weight, e->activeconns, e->inactconns); free(dname); } free(d); } static void list_service(ipvs_service_t *svc, unsigned int format) { ipvs_service_entry_t *entry; if (!(entry = ipvs_get_service(svc->fwmark, svc->af, svc->protocol, svc->addr, svc->port))) { fprintf(stderr, "%s\n", ipvs_strerror(errno)); exit(1); } print_title(format); print_service_entry(entry, format); free(entry); } static void list_all(unsigned int format) { struct ip_vs_get_services *get; int i; if (!(format & FMT_RULE)) printf("IP Virtual Server version %d.%d.%d (size=%d)\n", NVERSION(ipvs_info.version), ipvs_info.size); if (!(get = ipvs_get_services())) { fprintf(stderr, "%s\n", ipvs_strerror(errno)); exit(1); } if (!(format & FMT_NOSORT)) ipvs_sort_services(get, ipvs_cmp_services); print_title(format); for (i = 0; i < get->num_services; i++) print_service_entry(&get->entrytable[i], format); free(get); } void list_timeout(void) { ipvs_timeout_t *u; if (!(u = ipvs_get_timeout())) exit(1); printf("Timeout (tcp tcpfin udp): %d %d %d\n", u->tcp_timeout, u->tcp_fin_timeout, u->udp_timeout); free(u); } static void list_daemon(void) { ipvs_daemon_t *u; int i; if (!(u = ipvs_get_daemon())) exit(1); for (i = 0; i < 2; i++) { char *type; if (u[i].state & IP_VS_STATE_MASTER) type = "master"; else if (u[i].state & IP_VS_STATE_BACKUP) type = "backup"; else continue; printf("%s sync daemon (mcast=%s, syncid=%d", type, u[i].mcast_ifn, u[i].syncid); if (u[i].sync_maxlen) printf(", maxlen=%u", u[i].sync_maxlen); if (u[i].mcast_af != AF_UNSPEC) { char addr[INET6_ADDRSTRLEN]; if (inet_ntop(u[i].mcast_af, &u[i].mcast_group, addr, sizeof(addr))) printf(", group=%s", addr); } if (u[i].mcast_port) printf(", port=%u", u[i].mcast_port); if (u[i].mcast_ttl) printf(", ttl=%u", u[i].mcast_ttl); printf(")\n"); } free(u); } int host_to_addr(const char *name, struct in_addr *addr) { struct hostent *host; if ((host = gethostbyname(name)) != NULL) { if (host->h_addrtype != AF_INET || host->h_length != sizeof(struct in_addr)) return -1; /* warning: we just handle h_addr_list[0] here */ memcpy(addr, host->h_addr_list[0], sizeof(struct in_addr)); return 0; } return -1; } static char * addr_to_host(int af, const void *addr) { struct hostent *host; if (af == AF_INET) host = gethostbyaddr((char *) addr, sizeof(struct in_addr), af); else host = gethostbyaddr((char *) addr, sizeof(struct in6_addr), af); if (host != NULL) return (char *) host->h_name; else return (char *) NULL; } static char * addr_to_anyname(int af, const void *addr) { char *name; static char buf[INET6_ADDRSTRLEN]; if ((name = addr_to_host(af, addr)) != NULL) return name; inet_ntop(af, addr, buf, sizeof(buf)); return buf; } int service_to_port(const char *name, unsigned short proto) { struct servent *service; if (proto == IPPROTO_TCP && (service = getservbyname(name, "tcp")) != NULL) return ntohs((unsigned short) service->s_port); else if (proto == IPPROTO_UDP && (service = getservbyname(name, "udp")) != NULL) return ntohs((unsigned short) service->s_port); else if (proto == IPPROTO_SCTP && (service = getservbyname(name, "sctp")) != NULL) return ntohs((unsigned short) service->s_port); else return -1; } static char * port_to_service(unsigned short port, unsigned short proto) { struct servent *service; if (proto == IPPROTO_TCP && (service = getservbyport(htons(port), "tcp")) != NULL) return service->s_name; else if (proto == IPPROTO_UDP && (service = getservbyport(htons(port), "udp")) != NULL) return service->s_name; else if (proto == IPPROTO_SCTP && (service = getservbyport(htons(port), "sctp")) != NULL) return service->s_name; else return (char *) NULL; } static char * port_to_anyname(unsigned short port, unsigned short proto) { char *name; static char buf[10]; if ((name = port_to_service(port, proto)) != NULL) return name; else { sprintf(buf, "%u", port); return buf; } } static char * addrport_to_anyname(int af, const void *addr, unsigned short port, unsigned short proto, unsigned int format) { char *buf, pbuf[INET6_ADDRSTRLEN]; if (!(buf=malloc(60))) return NULL; if (format & FMT_NUMERIC) { snprintf(buf, 60, "%s%s%s:%u", af == AF_INET ? "" : "[", inet_ntop(af, addr, pbuf, sizeof(pbuf)), af == AF_INET ? "" : "]", port); } else { snprintf(buf, 60, "%s%s%s:%s", af == AF_INET ? "" : "[", addr_to_anyname(af, addr), af == AF_INET ? "" : "]", port_to_anyname(port, proto)); } return buf; } static int str_is_digit(const char *str) { size_t offset; size_t top; top = strlen(str); for (offset=0; offset