| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| #undef __KERNEL__ |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <netdb.h> |
| #include <string.h> |
| #include <unistd.h> |
| #include <errno.h> |
| #include <ctype.h> |
| #include <stdarg.h> |
| #include <netinet/in.h> |
| #include <sys/socket.h> |
| #include <sys/types.h> |
| #include <sys/param.h> |
| #include <sys/wait.h> /* For waitpid */ |
| #include <arpa/inet.h> |
| |
| #include <net/if.h> |
| #include <netinet/ip_icmp.h> |
| #include <netinet/udp.h> |
| #include <netinet/tcp.h> |
| |
| #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) |
| |
| #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", |
| }; |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| static const char commands_v_options[NUMBER_OF_CMD][NUMBER_OF_OPT] = |
| { |
| |
| {'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', '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', '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', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x'}, |
| {' ', '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'}, |
| {'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', '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', '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', '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', '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', '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', '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', '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', 'x'}, |
| }; |
| |
| static const char * const tunnames[] = { |
| "ipip", |
| "gue", |
| "gre", |
| }; |
| |
| static const char * const tunflags[] = { |
| "-c", |
| "+c", |
| "r+c", |
| }; |
| |
| 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" |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| static const char |
| tunnel_types_v_options[IP_VS_CONN_F_TUNNEL_TYPE_MAX][NUMBER_OF_TUN_OPT] = { |
| |
| {'x', 'x', 'x', 'x'}, |
| {'+', '1', '1', '1'}, |
| {'x', '1', '1', 'x'}, |
| }; |
| |
| |
| #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 |
| |
| |
| #define DEF_SCHED "wlc" |
| |
| |
| #define DEF_TUNNEL_TYPE "ipip" |
| |
| |
| #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; |
| }; |
| |
| |
| |
| |
| 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, |
| }; |
| |
| |
| 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); |
| |
| |
| 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); |
| |
| |
| 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, ...); |
| |
| |
| 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()) { |
| |
| 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)); |
| } |
| |
| |
| check_ipvs_version(); |
| |
| |
| if (argc == 1){ |
| list_all(FMT_NONE); |
| ipvs_close(); |
| return 0; |
| } |
| |
| |
| 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); |
| |
| |
| |
| |
| 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"); |
| |
| 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: |
| |
| 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) { |
| |
| 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; |
| |
| |
| 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; |
| |
| ce.dest.weight = 1; |
| |
| ce.dest.tun_type = 0; |
| |
| ce.dest.tun_port = 0; |
| |
| ce.dest.tun_flags = 0; |
| |
| ce.dest.conn_flags = IP_VS_CONN_F_DROUTE; |
| |
| 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) { |
| |
| 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"); |
| |
| |
| 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) { |
| |
| |
| |
| |
| |
| if (!ce.svc.fwmark && |
| (fwd_method == IP_VS_CONN_F_TUNNEL || |
| fwd_method == IP_VS_CONN_F_DROUTE)) |
| ce.dest.port = ce.svc.port; |
| |
| |
| 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"); |
| |
| |
| 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) { |
| |
| if (errno != ERANGE && min <= number && number <= max) |
| return number; |
| } |
| return -1; |
| } |
| |
| |
| |
| |
| |
| static int parse_timeout(char *buf, int min, int max) |
| { |
| int i; |
| |
| |
| 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; |
| } |
| |
| |
| |
| |
| |
| 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; |
| } |
| |
| |
| |
| |
| |
| |
| |
| 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; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| 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; |
| |
| |
| 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; |
| |
| |
| 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) |
| { |
| |
| 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); |
| } |
| |
| |
| 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"); |
| |
| |
| |
| |
| 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) |
| printf("%9llu", i); |
| else if (i < 1000000000) |
| printf("%8lluK", i / 1000); |
| else if (i < 100000000000ULL) |
| printf("%8lluM", i / 1000000); |
| else if (i < 100000000000000ULL) |
| 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 %d -6", se->fwmark); |
| else |
| sprintf(svc_name, "-f %d", se->fwmark); |
| else |
| if (se->af == AF_INET6) |
| sprintf(svc_name, "FWM %d IPv6", se->fwmark); |
| else |
| sprintf(svc_name, "FWM %d", 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); |
| } |
| |
| |
| 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"); |
| |
| |
| 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; |
| |
| 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<top; offset++) { |
| if (!isdigit((int)*(str+offset))) { |
| break; |
| } |
| } |
| |
| return (offset<top)?0:1; |
| } |