aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorvince <vince@36f5d8da-7431-0410-8ca5-ec586ed2521a>2008-09-16 20:05:13 +0000
committerSimon Horman <horms@verge.net.au>2013-05-22 15:04:54 +0900
commitaa67eed57bf3edd79ed795b605ba7524e6c7de32 (patch)
tree6b0cdaf55c36c70eb8e8b32fe255d0faedcf9051
parent0244758bed14cf8e4ab344d625f54b03766287b0 (diff)
downloadipvsadm-aa67eed57bf3edd79ed795b605ba7524e6c7de32.tar.gz
More files to bring release up to 1.24 with IPv6 changes.
git-svn-id: http://svn.linuxvirtualserver.org/repos/ipvsadm/trunk@57 36f5d8da-7431-0410-8ca5-ec586ed2521a
-rw-r--r--contrib/popt-optional.diff25
-rw-r--r--libipvs/Makefile32
-rw-r--r--libipvs/ip_vs.h532
-rw-r--r--libipvs/ip_vs_nl_policy.c70
-rw-r--r--libipvs/libipvs.c1115
-rw-r--r--libipvs/libipvs.h128
6 files changed, 1902 insertions, 0 deletions
diff --git a/contrib/popt-optional.diff b/contrib/popt-optional.diff
new file mode 100644
index 0000000..00a085a
--- /dev/null
+++ b/contrib/popt-optional.diff
@@ -0,0 +1,25 @@
+--- rpm-4.0.4/popt/popt.c~ Sat Jan 19 07:28:30 2002
++++ rpm-4.0.4/popt/popt.c Fri Aug 9 17:08:15 2002
+@@ -840,12 +840,17 @@
+ canstrip) {
+ poptStripArg(con, con->os->next);
+ }
+-
++
+ if (con->os->argv != NULL) { /* XXX can't happen */
+- /* XXX watchout: subtle side-effects live here. */
+- longArg = con->os->argv[con->os->next++];
+- longArg = expandNextArg(con, longArg);
+- con->os->nextArg = longArg;
++ if (opt->argInfo & POPT_ARGFLAG_OPTIONAL &&
++ con->os->argv[con->os->next][0] == '-') {
++ con->os->nextArg = NULL;
++ } else {
++ /* XXX watchout: subtle side-effects live here. */
++ longArg = con->os->argv[con->os->next++];
++ longArg = expandNextArg(con, longArg);
++ con->os->nextArg = longArg;
++ }
+ }
+ }
+ }
diff --git a/libipvs/Makefile b/libipvs/Makefile
new file mode 100644
index 0000000..ce4999d
--- /dev/null
+++ b/libipvs/Makefile
@@ -0,0 +1,32 @@
+# Makefile for libipvs
+
+CC = gcc
+CFLAGS = -Wall -Wunused -Wstrict-prototypes -g -fPIC
+ifneq (0,$(HAVE_NL))
+CFLAGS += -DLIBIPVS_USE_NL
+endif
+
+INCLUDE += $(shell if [ -f ../../ip_vs.h ]; then \
+ echo "-I../../."; fi;)
+DEFINES = $(shell if [ ! -f ../../ip_vs.h ]; then \
+ echo "-DHAVE_NET_IP_VS_H"; fi;)
+
+.PHONY = all clean install dist distclean rpm rpms
+STATIC_LIB = libipvs.a
+SHARED_LIB = libipvs.so
+
+all: $(STATIC_LIB) $(SHARED_LIB)
+
+$(STATIC_LIB): libipvs.o ip_vs_nl_policy.o
+ ar rv $@ $^
+
+$(SHARED_LIB): libipvs.o ip_vs_nl_policy.o
+ $(CC) -shared -Wl,-soname,$@ -o $@ $^
+
+%.o: %.c
+ $(CC) $(CFLAGS) $(INCLUDE) $(DEFINES) -c -o $@ $<
+
+clean:
+ rm -f *.[ao] *~ *.orig *.rej core *.so
+
+distclean: clean
diff --git a/libipvs/ip_vs.h b/libipvs/ip_vs.h
new file mode 100644
index 0000000..e174424
--- /dev/null
+++ b/libipvs/ip_vs.h
@@ -0,0 +1,532 @@
+/*
+ * IP Virtual Server
+ * data structure and functionality definitions
+ */
+
+#ifndef _IP_VS_H
+#define _IP_VS_H
+
+#include <asm/types.h> /* For __uXX types */
+#include <linux/types.h> /* For __beXX types in userland */
+
+#include <netinet/in.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+#ifdef LIBIPVS_USE_NL
+#include <netlink/netlink.h>
+#include <netlink/genl/genl.h>
+#include <netlink/genl/ctrl.h>
+#endif
+
+#define IP_VS_VERSION_CODE 0x010201
+#define NVERSION(version) \
+ (version >> 16) & 0xFF, \
+ (version >> 8) & 0xFF, \
+ version & 0xFF
+
+/*
+ * Virtual Service Flags
+ */
+#define IP_VS_SVC_F_PERSISTENT 0x0001 /* persistent port */
+#define IP_VS_SVC_F_HASHED 0x0002 /* hashed entry */
+
+/*
+ * Destination Server Flags
+ */
+#define IP_VS_DEST_F_AVAILABLE 0x0001 /* server is available */
+#define IP_VS_DEST_F_OVERLOAD 0x0002 /* server is overloaded */
+
+/*
+ * IPVS sync daemon states
+ */
+#define IP_VS_STATE_NONE 0x0000 /* daemon is stopped */
+#define IP_VS_STATE_MASTER 0x0001 /* started as master */
+#define IP_VS_STATE_BACKUP 0x0002 /* started as backup */
+
+/*
+ * IPVS socket options
+ */
+#define IP_VS_BASE_CTL (64+1024+64) /* base */
+
+#define IP_VS_SO_SET_NONE IP_VS_BASE_CTL /* just peek */
+#define IP_VS_SO_SET_INSERT (IP_VS_BASE_CTL+1)
+#define IP_VS_SO_SET_ADD (IP_VS_BASE_CTL+2)
+#define IP_VS_SO_SET_EDIT (IP_VS_BASE_CTL+3)
+#define IP_VS_SO_SET_DEL (IP_VS_BASE_CTL+4)
+#define IP_VS_SO_SET_FLUSH (IP_VS_BASE_CTL+5)
+#define IP_VS_SO_SET_LIST (IP_VS_BASE_CTL+6)
+#define IP_VS_SO_SET_ADDDEST (IP_VS_BASE_CTL+7)
+#define IP_VS_SO_SET_DELDEST (IP_VS_BASE_CTL+8)
+#define IP_VS_SO_SET_EDITDEST (IP_VS_BASE_CTL+9)
+#define IP_VS_SO_SET_TIMEOUT (IP_VS_BASE_CTL+10)
+#define IP_VS_SO_SET_STARTDAEMON (IP_VS_BASE_CTL+11)
+#define IP_VS_SO_SET_STOPDAEMON (IP_VS_BASE_CTL+12)
+#define IP_VS_SO_SET_RESTORE (IP_VS_BASE_CTL+13)
+#define IP_VS_SO_SET_SAVE (IP_VS_BASE_CTL+14)
+#define IP_VS_SO_SET_ZERO (IP_VS_BASE_CTL+15)
+#define IP_VS_SO_SET_MAX IP_VS_SO_SET_ZERO
+
+#define IP_VS_SO_GET_VERSION IP_VS_BASE_CTL
+#define IP_VS_SO_GET_INFO (IP_VS_BASE_CTL+1)
+#define IP_VS_SO_GET_SERVICES (IP_VS_BASE_CTL+2)
+#define IP_VS_SO_GET_SERVICE (IP_VS_BASE_CTL+3)
+#define IP_VS_SO_GET_DESTS (IP_VS_BASE_CTL+4)
+#define IP_VS_SO_GET_DEST (IP_VS_BASE_CTL+5) /* not used now */
+#define IP_VS_SO_GET_TIMEOUT (IP_VS_BASE_CTL+6)
+#define IP_VS_SO_GET_DAEMON (IP_VS_BASE_CTL+7)
+#define IP_VS_SO_GET_MAX IP_VS_SO_GET_DAEMON
+
+
+/*
+ * IPVS Connection Flags
+ */
+#define IP_VS_CONN_F_FWD_MASK 0x0007 /* mask for the fwd methods */
+#define IP_VS_CONN_F_MASQ 0x0000 /* masquerading/NAT */
+#define IP_VS_CONN_F_LOCALNODE 0x0001 /* local node */
+#define IP_VS_CONN_F_TUNNEL 0x0002 /* tunneling */
+#define IP_VS_CONN_F_DROUTE 0x0003 /* direct routing */
+#define IP_VS_CONN_F_BYPASS 0x0004 /* cache bypass */
+#define IP_VS_CONN_F_SYNC 0x0020 /* entry created by sync */
+#define IP_VS_CONN_F_HASHED 0x0040 /* hashed entry */
+#define IP_VS_CONN_F_NOOUTPUT 0x0080 /* no output packets */
+#define IP_VS_CONN_F_INACTIVE 0x0100 /* not established */
+#define IP_VS_CONN_F_OUT_SEQ 0x0200 /* must do output seq adjust */
+#define IP_VS_CONN_F_IN_SEQ 0x0400 /* must do input seq adjust */
+#define IP_VS_CONN_F_SEQ_MASK 0x0600 /* in/out sequence mask */
+#define IP_VS_CONN_F_NO_CPORT 0x0800 /* no client port set yet */
+#define IP_VS_CONN_F_TEMPLATE 0x1000 /* template, not connection */
+
+/* Move it to better place one day, for now keep it unique */
+#define NFC_IPVS_PROPERTY 0x10000
+
+#define IP_VS_SCHEDNAME_MAXLEN 16
+#define IP_VS_IFNAME_MAXLEN 16
+
+union nf_inet_addr {
+ __u32 all[4];
+ __be32 ip;
+ __be32 ip6[4];
+ struct in_addr in;
+ struct in6_addr in6;
+};
+
+/*
+ * The struct ip_vs_service_user and struct ip_vs_dest_user are
+ * used to set IPVS rules through setsockopt.
+ */
+struct ip_vs_service_kern {
+ /* virtual service addresses */
+ u_int16_t protocol;
+ __be32 addr; /* virtual ip address */
+ __be16 port;
+ u_int32_t fwmark; /* firwall mark of service */
+
+ /* virtual service options */
+ char sched_name[IP_VS_SCHEDNAME_MAXLEN];
+ unsigned flags; /* virtual service flags */
+ unsigned timeout; /* persistent timeout in sec */
+ __be32 netmask; /* persistent netmask */
+};
+
+struct ip_vs_service_user {
+ /* virtual service addresses */
+ u_int16_t protocol;
+ __be32 __addr_v4; /* virtual ip address - internal use only */
+ __be16 port;
+ u_int32_t fwmark; /* firwall mark of service */
+
+ /* virtual service options */
+ char sched_name[IP_VS_SCHEDNAME_MAXLEN];
+ unsigned flags; /* virtual service flags */
+ unsigned timeout; /* persistent timeout in sec */
+ __be32 netmask; /* persistent netmask */
+ u_int16_t af;
+ union nf_inet_addr addr;
+};
+
+struct ip_vs_dest_kern {
+ /* destination server address */
+ __be32 addr;
+ __be16 port;
+
+ /* real server options */
+ unsigned conn_flags; /* connection flags */
+ int weight; /* destination weight */
+
+ /* thresholds for active connections */
+ u_int32_t u_threshold; /* upper threshold */
+ u_int32_t l_threshold; /* lower threshold */
+};
+
+struct ip_vs_dest_user {
+ /* destination server address */
+ __be32 __addr_v4; /* internal use only */
+ __be16 port;
+
+ /* real server options */
+ unsigned conn_flags; /* connection flags */
+ int weight; /* destination weight */
+
+ /* thresholds for active connections */
+ u_int32_t u_threshold; /* upper threshold */
+ u_int32_t l_threshold; /* lower threshold */
+ u_int16_t af;
+ union nf_inet_addr addr;
+};
+
+/*
+ * IPVS statistics object (for user space)
+ */
+struct ip_vs_stats_user
+{
+ __u32 conns; /* connections scheduled */
+ __u32 inpkts; /* incoming packets */
+ __u32 outpkts; /* outgoing packets */
+ __u64 inbytes; /* incoming bytes */
+ __u64 outbytes; /* outgoing bytes */
+
+ __u32 cps; /* current connection rate */
+ __u32 inpps; /* current in packet rate */
+ __u32 outpps; /* current out packet rate */
+ __u32 inbps; /* current in byte rate */
+ __u32 outbps; /* current out byte rate */
+};
+
+
+/* The argument to IP_VS_SO_GET_INFO */
+struct ip_vs_getinfo {
+ /* version number */
+ unsigned int version;
+
+ /* size of connection hash table */
+ unsigned int size;
+
+ /* number of virtual services */
+ unsigned int num_services;
+};
+
+
+/* The argument to IP_VS_SO_GET_SERVICE */
+struct ip_vs_service_entry_kern {
+ /* which service: user fills in these */
+ u_int16_t protocol;
+ __be32 addr; /* virtual address */
+ __be16 port;
+ u_int32_t fwmark; /* firwall mark of service */
+
+ /* service options */
+ char sched_name[IP_VS_SCHEDNAME_MAXLEN];
+ unsigned flags; /* virtual service flags */
+ unsigned timeout; /* persistent timeout */
+ __be32 netmask; /* persistent netmask */
+
+ /* number of real servers */
+ unsigned int num_dests;
+
+ /* statistics */
+ struct ip_vs_stats_user stats;
+};
+
+struct ip_vs_service_entry {
+ /* which service: user fills in these */
+ u_int16_t protocol;
+ __be32 __addr_v4; /* virtual address - internal use only*/
+ __be16 port;
+ u_int32_t fwmark; /* firwall mark of service */
+
+ /* service options */
+ char sched_name[IP_VS_SCHEDNAME_MAXLEN];
+ unsigned flags; /* virtual service flags */
+ unsigned timeout; /* persistent timeout */
+ __be32 netmask; /* persistent netmask */
+
+ /* number of real servers */
+ unsigned int num_dests;
+
+ /* statistics */
+ struct ip_vs_stats_user stats;
+
+ u_int16_t af;
+ union nf_inet_addr addr;
+
+};
+
+struct ip_vs_dest_entry_kern {
+ __be32 addr; /* destination address */
+ __be16 port;
+ unsigned conn_flags; /* connection flags */
+ int weight; /* destination weight */
+
+ u_int32_t u_threshold; /* upper threshold */
+ u_int32_t l_threshold; /* lower threshold */
+
+ u_int32_t activeconns; /* active connections */
+ u_int32_t inactconns; /* inactive connections */
+ u_int32_t persistconns; /* persistent connections */
+
+ /* statistics */
+ struct ip_vs_stats_user stats;
+};
+
+struct ip_vs_dest_entry {
+ __be32 __addr_v4; /* destination address - internal use only */
+ __be16 port;
+ unsigned conn_flags; /* connection flags */
+ int weight; /* destination weight */
+
+ u_int32_t u_threshold; /* upper threshold */
+ u_int32_t l_threshold; /* lower threshold */
+
+ u_int32_t activeconns; /* active connections */
+ u_int32_t inactconns; /* inactive connections */
+ u_int32_t persistconns; /* persistent connections */
+
+ /* statistics */
+ struct ip_vs_stats_user stats;
+ u_int16_t af;
+ union nf_inet_addr addr;
+};
+
+/* The argument to IP_VS_SO_GET_DESTS */
+struct ip_vs_get_dests_kern {
+ /* which service: user fills in these */
+ u_int16_t protocol;
+ __be32 addr; /* virtual address - internal use only */
+ __be16 port;
+ u_int32_t fwmark; /* firwall mark of service */
+
+ /* number of real servers */
+ unsigned int num_dests;
+
+ /* the real servers */
+ struct ip_vs_dest_entry_kern entrytable[0];
+};
+
+struct ip_vs_get_dests {
+ /* which service: user fills in these */
+ u_int16_t protocol;
+ __be32 __addr_v4; /* virtual address - internal use only */
+ __be16 port;
+ u_int32_t fwmark; /* firwall mark of service */
+
+ /* number of real servers */
+ unsigned int num_dests;
+ u_int16_t af;
+ union nf_inet_addr addr;
+
+ /* the real servers */
+ struct ip_vs_dest_entry entrytable[0];
+};
+
+/* The argument to IP_VS_SO_GET_SERVICES */
+struct ip_vs_get_services {
+ /* number of virtual services */
+ unsigned int num_services;
+
+ /* service table */
+ struct ip_vs_service_entry entrytable[0];
+};
+
+struct ip_vs_get_services_kern {
+ /* number of virtual services */
+ unsigned int num_services;
+
+ /* service table */
+ struct ip_vs_service_entry_kern entrytable[0];
+};
+
+/* The argument to IP_VS_SO_GET_TIMEOUT */
+struct ip_vs_timeout_user {
+ int tcp_timeout;
+ int tcp_fin_timeout;
+ int udp_timeout;
+};
+
+
+/* The argument to IP_VS_SO_GET_DAEMON */
+struct ip_vs_daemon_user {
+ /* sync daemon state (master/backup) */
+ int state;
+
+ /* multicast interface name */
+ char mcast_ifn[IP_VS_IFNAME_MAXLEN];
+
+ /* SyncID we belong to */
+ int syncid;
+};
+
+
+/*
+ *
+ * IPVS Generic Netlink interface definitions
+ *
+ */
+
+/* Generic Netlink family info */
+
+#define IPVS_GENL_NAME "IPVS"
+#define IPVS_GENL_VERSION 0x1
+
+struct ip_vs_flags {
+ __be32 flags;
+ __be32 mask;
+};
+
+/* Generic Netlink command attributes */
+enum {
+ IPVS_CMD_UNSPEC = 0,
+
+ IPVS_CMD_NEW_SERVICE, /* add service */
+ IPVS_CMD_SET_SERVICE, /* modify service */
+ IPVS_CMD_DEL_SERVICE, /* delete service */
+ IPVS_CMD_GET_SERVICE, /* get info about specific service */
+
+ IPVS_CMD_NEW_DEST, /* add destination */
+ IPVS_CMD_SET_DEST, /* modify destination */
+ IPVS_CMD_DEL_DEST, /* delete destination */
+ IPVS_CMD_GET_DEST, /* get list of all service dests */
+
+ IPVS_CMD_NEW_DAEMON, /* start sync daemon */
+ IPVS_CMD_DEL_DAEMON, /* stop sync daemon */
+ IPVS_CMD_GET_DAEMON, /* get sync daemon status */
+
+ IPVS_CMD_SET_TIMEOUT, /* set TCP and UDP timeouts */
+ IPVS_CMD_GET_TIMEOUT, /* get TCP and UDP timeouts */
+
+ IPVS_CMD_SET_INFO, /* only used in GET_INFO reply */
+ IPVS_CMD_GET_INFO, /* get general IPVS info */
+
+ IPVS_CMD_ZERO, /* zero all counters and stats */
+ IPVS_CMD_FLUSH, /* flush services and dests */
+
+ __IPVS_CMD_MAX,
+};
+
+#define IPVS_CMD_MAX (__IPVS_CMD_MAX - 1)
+
+/* Attributes used in the first level of commands */
+enum {
+ IPVS_CMD_ATTR_UNSPEC = 0,
+ IPVS_CMD_ATTR_SERVICE, /* nested service attribute */
+ IPVS_CMD_ATTR_DEST, /* nested destination attribute */
+ IPVS_CMD_ATTR_DAEMON, /* nested sync daemon attribute */
+ IPVS_CMD_ATTR_TIMEOUT_TCP, /* TCP connection timeout */
+ IPVS_CMD_ATTR_TIMEOUT_TCP_FIN, /* TCP FIN wait timeout */
+ IPVS_CMD_ATTR_TIMEOUT_UDP, /* UDP timeout */
+ __IPVS_CMD_ATTR_MAX,
+};
+
+#define IPVS_CMD_ATTR_MAX (__IPVS_SVC_ATTR_MAX - 1)
+
+/*
+ * Attributes used to describe a service
+ *
+ * Used inside nested attribute IPVS_CMD_ATTR_SERVICE
+ */
+enum {
+ IPVS_SVC_ATTR_UNSPEC = 0,
+ IPVS_SVC_ATTR_AF, /* address family */
+ IPVS_SVC_ATTR_PROTOCOL, /* virtual service protocol */
+ IPVS_SVC_ATTR_ADDR, /* virtual service address */
+ IPVS_SVC_ATTR_PORT, /* virtual service port */
+ IPVS_SVC_ATTR_FWMARK, /* firewall mark of service */
+
+ IPVS_SVC_ATTR_SCHED_NAME, /* name of scheduler */
+ IPVS_SVC_ATTR_FLAGS, /* virtual service flags */
+ IPVS_SVC_ATTR_TIMEOUT, /* persistent timeout */
+ IPVS_SVC_ATTR_NETMASK, /* persistent netmask */
+
+ IPVS_SVC_ATTR_STATS, /* nested attribute for service stats */
+ __IPVS_SVC_ATTR_MAX,
+};
+
+#define IPVS_SVC_ATTR_MAX (__IPVS_SVC_ATTR_MAX - 1)
+
+/*
+ * Attributes used to describe a destination (real server)
+ *
+ * Used inside nested attribute IPVS_CMD_ATTR_DEST
+ */
+enum {
+ IPVS_DEST_ATTR_UNSPEC = 0,
+ IPVS_DEST_ATTR_ADDR, /* real server address */
+ IPVS_DEST_ATTR_PORT, /* real server port */
+
+ IPVS_DEST_ATTR_FWD_METHOD, /* forwarding method */
+ IPVS_DEST_ATTR_WEIGHT, /* destination weight */
+
+ IPVS_DEST_ATTR_U_THRESH, /* upper threshold */
+ IPVS_DEST_ATTR_L_THRESH, /* lower threshold */
+
+ IPVS_DEST_ATTR_ACTIVE_CONNS, /* active connections */
+ IPVS_DEST_ATTR_INACT_CONNS, /* inactive connections */
+ IPVS_DEST_ATTR_PERSIST_CONNS, /* persistent connections */
+
+ IPVS_DEST_ATTR_STATS, /* nested attribute for dest stats */
+ __IPVS_DEST_ATTR_MAX,
+};
+
+#define IPVS_DEST_ATTR_MAX (__IPVS_DEST_ATTR_MAX - 1)
+
+/*
+ * Attributes describing a sync daemon
+ *
+ * Used inside nested attribute IPVS_CMD_ATTR_DAEMON
+ */
+enum {
+ IPVS_DAEMON_ATTR_UNSPEC = 0,
+ IPVS_DAEMON_ATTR_STATE, /* sync daemon state (master/backup) */
+ IPVS_DAEMON_ATTR_MCAST_IFN, /* multicast interface name */
+ IPVS_DAEMON_ATTR_SYNC_ID, /* SyncID we belong to */
+ __IPVS_DAEMON_ATTR_MAX,
+};
+
+#define IPVS_DAEMON_ATTR_MAX (__IPVS_DAEMON_ATTR_MAX - 1)
+
+/*
+ * Attributes used to describe service or destination entry statistics
+ *
+ * Used inside nested attributes IPVS_SVC_ATTR_STATS and IPVS_DEST_ATTR_STATS
+ */
+enum {
+ IPVS_STATS_ATTR_UNSPEC = 0,
+ IPVS_STATS_ATTR_CONNS, /* connections scheduled */
+ IPVS_STATS_ATTR_INPKTS, /* incoming packets */
+ IPVS_STATS_ATTR_OUTPKTS, /* outgoing packets */
+ IPVS_STATS_ATTR_INBYTES, /* incoming bytes */
+ IPVS_STATS_ATTR_OUTBYTES, /* outgoing bytes */
+
+ IPVS_STATS_ATTR_CPS, /* current connection rate */
+ IPVS_STATS_ATTR_INPPS, /* current in packet rate */
+ IPVS_STATS_ATTR_OUTPPS, /* current out packet rate */
+ IPVS_STATS_ATTR_INBPS, /* current in byte rate */
+ IPVS_STATS_ATTR_OUTBPS, /* current out byte rate */
+ __IPVS_STATS_ATTR_MAX,
+};
+
+#define IPVS_STATS_ATTR_MAX (__IPVS_STATS_ATTR_MAX - 1)
+
+/* Attributes used in response to IPVS_CMD_GET_INFO command */
+enum {
+ IPVS_INFO_ATTR_UNSPEC = 0,
+ IPVS_INFO_ATTR_VERSION, /* IPVS version number */
+ IPVS_INFO_ATTR_CONN_TAB_SIZE, /* size of connection hash table */
+ __IPVS_INFO_ATTR_MAX,
+};
+
+#define IPVS_INFO_ATTR_MAX (__IPVS_INFO_ATTR_MAX - 1)
+
+#ifdef LIBIPVS_USE_NL
+extern struct nla_policy ipvs_cmd_policy[IPVS_CMD_ATTR_MAX + 1];
+extern struct nla_policy ipvs_service_policy[IPVS_SVC_ATTR_MAX + 1];
+extern struct nla_policy ipvs_dest_policy[IPVS_DEST_ATTR_MAX + 1];
+extern struct nla_policy ipvs_stats_policy[IPVS_STATS_ATTR_MAX + 1];
+extern struct nla_policy ipvs_info_policy[IPVS_INFO_ATTR_MAX + 1];
+extern struct nla_policy ipvs_daemon_policy[IPVS_DAEMON_ATTR_MAX + 1];
+#endif
+
+/* End of Generic Netlink interface definitions */
+
+#endif /* _IP_VS_H */
diff --git a/libipvs/ip_vs_nl_policy.c b/libipvs/ip_vs_nl_policy.c
new file mode 100644
index 0000000..c80083e
--- /dev/null
+++ b/libipvs/ip_vs_nl_policy.c
@@ -0,0 +1,70 @@
+#include "libipvs.h"
+
+#ifdef LIBIPVS_USE_NL
+/* Policy definitions */
+struct nla_policy ipvs_cmd_policy[IPVS_CMD_ATTR_MAX + 1] = {
+ [IPVS_CMD_ATTR_SERVICE] = { .type = NLA_NESTED },
+ [IPVS_CMD_ATTR_DEST] = { .type = NLA_NESTED },
+ [IPVS_CMD_ATTR_DAEMON] = { .type = NLA_NESTED },
+ [IPVS_CMD_ATTR_TIMEOUT_TCP] = { .type = NLA_U32 },
+ [IPVS_CMD_ATTR_TIMEOUT_TCP_FIN] = { .type = NLA_U32 },
+ [IPVS_CMD_ATTR_TIMEOUT_UDP] = { .type = NLA_U32 },
+};
+
+struct nla_policy ipvs_service_policy[IPVS_SVC_ATTR_MAX + 1] = {
+ [IPVS_SVC_ATTR_AF] = { .type = NLA_U16 },
+ [IPVS_SVC_ATTR_PROTOCOL] = { .type = NLA_U16 },
+ [IPVS_SVC_ATTR_ADDR] = { .type = NLA_UNSPEC,
+ .maxlen = sizeof(struct in6_addr) },
+ [IPVS_SVC_ATTR_PORT] = { .type = NLA_U16 },
+ [IPVS_SVC_ATTR_FWMARK] = { .type = NLA_U32 },
+ [IPVS_SVC_ATTR_SCHED_NAME] = { .type = NLA_STRING,
+ .maxlen = IP_VS_SCHEDNAME_MAXLEN },
+ [IPVS_SVC_ATTR_FLAGS] = { .type = NLA_UNSPEC,
+ .minlen = sizeof(struct ip_vs_flags),
+ .maxlen = sizeof(struct ip_vs_flags) },
+ [IPVS_SVC_ATTR_TIMEOUT] = { .type = NLA_U32 },
+ [IPVS_SVC_ATTR_NETMASK] = { .type = NLA_U32 },
+ [IPVS_SVC_ATTR_STATS] = { .type = NLA_NESTED },
+};
+
+struct nla_policy ipvs_dest_policy[IPVS_DEST_ATTR_MAX + 1] = {
+ [IPVS_DEST_ATTR_ADDR] = { .type = NLA_UNSPEC,
+ .maxlen = sizeof(struct in6_addr) },
+ [IPVS_DEST_ATTR_PORT] = { .type = NLA_U16 },
+ [IPVS_DEST_ATTR_FWD_METHOD] = { .type = NLA_U32 },
+ [IPVS_DEST_ATTR_WEIGHT] = { .type = NLA_U32 },
+ [IPVS_DEST_ATTR_U_THRESH] = { .type = NLA_U32 },
+ [IPVS_DEST_ATTR_L_THRESH] = { .type = NLA_U32 },
+ [IPVS_DEST_ATTR_ACTIVE_CONNS] = { .type = NLA_U32 },
+ [IPVS_DEST_ATTR_INACT_CONNS] = { .type = NLA_U32 },
+ [IPVS_DEST_ATTR_PERSIST_CONNS] = { .type = NLA_U32 },
+ [IPVS_DEST_ATTR_STATS] = { .type = NLA_NESTED },
+};
+
+struct nla_policy ipvs_stats_policy[IPVS_STATS_ATTR_MAX + 1] = {
+ [IPVS_STATS_ATTR_CONNS] = { .type = NLA_U32 },
+ [IPVS_STATS_ATTR_INPKTS] = { .type = NLA_U32 },
+ [IPVS_STATS_ATTR_OUTPKTS] = { .type = NLA_U32 },
+ [IPVS_STATS_ATTR_INBYTES] = { .type = NLA_U64 },
+ [IPVS_STATS_ATTR_OUTBYTES] = { .type = NLA_U64 },
+ [IPVS_STATS_ATTR_CPS] = { .type = NLA_U32 },
+ [IPVS_STATS_ATTR_INPPS] = { .type = NLA_U32 },
+ [IPVS_STATS_ATTR_OUTPPS] = { .type = NLA_U32 },
+ [IPVS_STATS_ATTR_INBPS] = { .type = NLA_U32 },
+ [IPVS_STATS_ATTR_OUTBPS] = { .type = NLA_U32 },
+};
+
+struct nla_policy ipvs_info_policy[IPVS_INFO_ATTR_MAX + 1] = {
+ [IPVS_INFO_ATTR_VERSION] = { .type = NLA_U32 },
+ [IPVS_INFO_ATTR_CONN_TAB_SIZE] = { .type = NLA_U32 },
+};
+
+struct nla_policy ipvs_daemon_policy[IPVS_DAEMON_ATTR_MAX + 1] = {
+ [IPVS_DAEMON_ATTR_STATE] = { .type = NLA_U32 },
+ [IPVS_DAEMON_ATTR_MCAST_IFN] = { .type = NLA_STRING,
+ .maxlen = IP_VS_IFNAME_MAXLEN },
+ [IPVS_DAEMON_ATTR_SYNC_ID] = { .type = NLA_U32 },
+};
+
+#endif /* LIBIPVS_USE_NL */
diff --git a/libipvs/libipvs.c b/libipvs/libipvs.c
new file mode 100644
index 0000000..4f8944a
--- /dev/null
+++ b/libipvs/libipvs.c
@@ -0,0 +1,1115 @@
+/*
+ * libipvs: Library for manipulating IPVS through [gs]etsockopt
+ *
+ * Version: $Id: libipvs.c,v 1.7 2003/06/08 09:31:39 wensong Exp $
+ *
+ * Authors: Wensong Zhang <wensong@linuxvirtualserver.org>
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+#include "libipvs.h"
+
+typedef struct ipvs_servicedest_s {
+ struct ip_vs_service_kern svc;
+ struct ip_vs_dest_kern dest;
+} ipvs_servicedest_t;
+
+static int sockfd = -1;
+static void* ipvs_func = NULL;
+struct ip_vs_getinfo ipvs_info;
+
+#ifdef LIBIPVS_USE_NL
+struct nl_handle *sock = NULL;
+int family, try_nl = 1;
+#endif
+
+#define CHECK_IPV4(s, ret) if (s->af && s->af != AF_INET) \
+ { errno = EAFNOSUPPORT; return ret; } \
+ s->__addr_v4 = s->addr.ip; \
+
+#ifdef LIBIPVS_USE_NL
+struct nl_msg *ipvs_nl_message(int cmd, int flags) {
+ struct nl_msg *msg;
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ return NULL;
+
+ genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, family, 0, flags,
+ cmd, IPVS_GENL_VERSION);
+
+ return msg;
+}
+
+static int ipvs_nl_noop_cb(struct nl_msg *msg, void *arg) {
+ return NL_OK;
+}
+
+int ipvs_nl_send_message(struct nl_msg *msg, nl_recvmsg_msg_cb_t func, void *arg) {
+ int err = EINVAL;
+
+ sock = nl_handle_alloc();
+ if (!sock) {
+ nlmsg_free(msg);
+ return -1;
+ }
+
+ if (genl_connect(sock) < 0)
+ goto fail_genl;
+
+ family = genl_ctrl_resolve(sock, IPVS_GENL_NAME);
+ if (family < 0)
+ goto fail_genl;
+
+ /* To test connections and set the family */
+ if (msg == NULL) {
+ nl_handle_destroy(sock);
+ sock = NULL;
+ return 0;
+ }
+
+ if (nl_socket_modify_cb(sock, NL_CB_VALID, NL_CB_CUSTOM, func, arg) != 0)
+ goto fail_genl;
+
+ if (nl_send_auto_complete(sock, msg) < 0)
+ goto fail_genl;
+
+ if ((err = -nl_recvmsgs_default(sock)) > 0)
+ goto fail_genl;
+
+ nlmsg_free(msg);
+
+ nl_handle_destroy(sock);
+
+ return 0;
+
+fail_genl:
+ nl_handle_destroy(sock);
+ sock = NULL;
+ nlmsg_free(msg);
+ errno = err;
+ return -1;
+}
+#endif
+
+int ipvs_init(void)
+{
+ socklen_t len;
+
+ ipvs_func = ipvs_init;
+
+#ifdef LIBIPVS_USE_NL
+ if (ipvs_nl_send_message(NULL, NULL, NULL) == 0) {
+ return ipvs_getinfo();
+ }
+
+ try_nl = 0;
+#endif
+
+ len = sizeof(ipvs_info);
+ if ((sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) == -1)
+ return -1;
+
+ if (getsockopt(sockfd, IPPROTO_IP, IP_VS_SO_GET_INFO,
+ (char *)&ipvs_info, &len))
+ return -1;
+
+ return 0;
+}
+
+#ifdef LIBIPVS_USE_NL
+static int ipvs_getinfo_parse_cb(struct nl_msg *msg, void *arg)
+{
+ struct nlmsghdr *nlh = nlmsg_hdr(msg);
+ struct nlattr *attrs[IPVS_INFO_ATTR_MAX + 1];
+
+ if (genlmsg_parse(nlh, 0, attrs, IPVS_INFO_ATTR_MAX, ipvs_info_policy) != 0)
+ return -1;
+
+ if (!(attrs[IPVS_INFO_ATTR_VERSION] &&
+ attrs[IPVS_INFO_ATTR_CONN_TAB_SIZE]))
+ return -1;
+
+ ipvs_info.version = nla_get_u32(attrs[IPVS_INFO_ATTR_VERSION]);
+ ipvs_info.size = nla_get_u32(attrs[IPVS_INFO_ATTR_CONN_TAB_SIZE]);
+
+ return NL_OK;
+}
+#endif
+
+int ipvs_getinfo(void)
+{
+ socklen_t len;
+
+#ifdef LIBIPVS_USE_NL
+ if (try_nl) {
+ struct nl_msg *msg;
+ msg = ipvs_nl_message(IPVS_CMD_GET_INFO, 0);
+ if (msg)
+ return ipvs_nl_send_message(msg, ipvs_getinfo_parse_cb,
+ NULL);
+ return -1;
+ }
+#endif
+
+ ipvs_func = ipvs_getinfo;
+ len = sizeof(ipvs_info);
+ return getsockopt(sockfd, IPPROTO_IP, IP_VS_SO_GET_INFO,
+ (char *)&ipvs_info, &len);
+}
+
+
+unsigned int ipvs_version(void)
+{
+ return ipvs_info.version;
+}
+
+
+int ipvs_flush(void)
+{
+#ifdef LIBIPVS_USE_NL
+ if (try_nl) {
+ struct nl_msg *msg = ipvs_nl_message(IPVS_CMD_FLUSH, 0);
+ if (msg && (ipvs_nl_send_message(msg, ipvs_nl_noop_cb, NULL) == 0))
+ return 0;
+
+ return -1;
+ }
+#endif
+ return setsockopt(sockfd, IPPROTO_IP, IP_VS_SO_SET_FLUSH, NULL, 0);
+}
+
+#ifdef LIBIPVS_USE_NL
+static int ipvs_nl_fill_service_attr(struct nl_msg *msg, ipvs_service_t *svc) {
+ struct nlattr *nl_service;
+ struct ip_vs_flags flags = { .flags = svc->flags,
+ .mask = ~0 };
+
+ nl_service = nla_nest_start(msg, IPVS_CMD_ATTR_SERVICE);
+ if (!nl_service)
+ return -1;
+
+ NLA_PUT_U16(msg, IPVS_SVC_ATTR_AF, svc->af);
+
+ if (svc->fwmark) {
+ NLA_PUT_U32(msg, IPVS_SVC_ATTR_FWMARK, svc->fwmark);
+ } else {
+ NLA_PUT_U16(msg, IPVS_SVC_ATTR_PROTOCOL, svc->protocol);
+ NLA_PUT(msg, IPVS_SVC_ATTR_ADDR, sizeof(svc->addr), &(svc->addr));
+ NLA_PUT_U16(msg, IPVS_SVC_ATTR_PORT, svc->port);
+ }
+
+ NLA_PUT_STRING(msg, IPVS_SVC_ATTR_SCHED_NAME, svc->sched_name);
+ NLA_PUT(msg, IPVS_SVC_ATTR_FLAGS, sizeof(flags), &flags);
+ NLA_PUT_U32(msg, IPVS_SVC_ATTR_TIMEOUT, svc->timeout);
+ NLA_PUT_U32(msg, IPVS_SVC_ATTR_NETMASK, svc->netmask);
+
+ nla_nest_end(msg, nl_service);
+ return 0;
+
+nla_put_failure:
+ return -1;
+}
+#endif
+
+int ipvs_add_service(ipvs_service_t *svc)
+{
+ ipvs_func = ipvs_add_service;
+#ifdef LIBIPVS_USE_NL
+ if (try_nl) {
+ struct nl_msg *msg = ipvs_nl_message(IPVS_CMD_NEW_SERVICE, 0);
+ if (!msg) return -1;
+ if (ipvs_nl_fill_service_attr(msg, svc)) {
+ nlmsg_free(msg);
+ return -1;
+ }
+ return ipvs_nl_send_message(msg, ipvs_nl_noop_cb, NULL);
+ }
+#endif
+
+ CHECK_IPV4(svc, -1);
+ return setsockopt(sockfd, IPPROTO_IP, IP_VS_SO_SET_ADD, (char *)svc,
+ sizeof(struct ip_vs_service_kern));
+}
+
+
+int ipvs_update_service(ipvs_service_t *svc)
+{
+ ipvs_func = ipvs_update_service;
+#ifdef LIBIPVS_USE_NL
+ if (try_nl) {
+ struct nl_msg *msg = ipvs_nl_message(IPVS_CMD_SET_SERVICE, 0);
+ if (!msg) return -1;
+ if (ipvs_nl_fill_service_attr(msg, svc)) {
+ nlmsg_free(msg);
+ return -1;
+ }
+ return ipvs_nl_send_message(msg, ipvs_nl_noop_cb, NULL);
+ }
+#endif
+ CHECK_IPV4(svc, -1);
+ return setsockopt(sockfd, IPPROTO_IP, IP_VS_SO_SET_EDIT, (char *)svc,
+ sizeof(struct ip_vs_service_kern));
+}
+
+
+int ipvs_del_service(ipvs_service_t *svc)
+{
+ ipvs_func = ipvs_del_service;
+#ifdef LIBIPVS_USE_NL
+ if (try_nl) {
+ struct nl_msg *msg = ipvs_nl_message(IPVS_CMD_DEL_SERVICE, 0);
+ if (!msg) return -1;
+ if (ipvs_nl_fill_service_attr(msg, svc)) {
+ nlmsg_free(msg);
+ return -1;
+ }
+ return ipvs_nl_send_message(msg, ipvs_nl_noop_cb, NULL);
+ }
+#endif
+ CHECK_IPV4(svc, -1);
+ return setsockopt(sockfd, IPPROTO_IP, IP_VS_SO_SET_DEL, (char *)svc,
+ sizeof(struct ip_vs_service_kern));
+}
+
+
+int ipvs_zero_service(ipvs_service_t *svc)
+{
+ ipvs_func = ipvs_zero_service;
+#ifdef LIBIPVS_USE_NL
+ if (try_nl) {
+ struct nl_msg *msg = ipvs_nl_message(IPVS_CMD_ZERO, 0);
+ if (!msg) return -1;
+
+ if (svc->fwmark
+ || memcmp(&in6addr_any, &svc->addr.in6, sizeof(struct in6_addr))
+ || svc->port) {
+ if (ipvs_nl_fill_service_attr(msg, svc)) {
+ nlmsg_free(msg);
+ return -1;
+ }
+ }
+ return ipvs_nl_send_message(msg, ipvs_nl_noop_cb, NULL);
+ }
+#endif
+ CHECK_IPV4(svc, -1);
+ return setsockopt(sockfd, IPPROTO_IP, IP_VS_SO_SET_ZERO, (char *)svc,
+ sizeof(struct ip_vs_service_kern));
+}
+
+#ifdef LIBIPVS_USE_NL
+static int ipvs_nl_fill_dest_attr(struct nl_msg *msg, ipvs_dest_t *dst) {
+ struct nlattr *nl_dest;
+
+ nl_dest = nla_nest_start(msg, IPVS_CMD_ATTR_DEST);
+ if (!nl_dest)
+ return -1;
+
+ NLA_PUT(msg, IPVS_DEST_ATTR_ADDR, sizeof(dst->addr), &(dst->addr));
+ NLA_PUT_U16(msg, IPVS_DEST_ATTR_PORT, dst->port);
+ NLA_PUT_U32(msg, IPVS_DEST_ATTR_FWD_METHOD, dst->conn_flags & IP_VS_CONN_F_FWD_MASK);
+ NLA_PUT_U32(msg, IPVS_DEST_ATTR_WEIGHT, dst->weight);
+ NLA_PUT_U32(msg, IPVS_DEST_ATTR_U_THRESH, dst->u_threshold);
+ NLA_PUT_U32(msg, IPVS_DEST_ATTR_L_THRESH, dst->l_threshold);
+
+ nla_nest_end(msg, nl_dest);
+ return 0;
+
+nla_put_failure:
+ return -1;
+}
+#endif
+
+int ipvs_add_dest(ipvs_service_t *svc, ipvs_dest_t *dest)
+{
+ ipvs_servicedest_t svcdest;
+
+#ifdef LIBIPVS_USE_NL
+ ipvs_func = ipvs_add_dest;
+ if (try_nl) {
+ struct nl_msg *msg = ipvs_nl_message(IPVS_CMD_NEW_DEST, 0);
+ if (!msg) return -1;
+ if (ipvs_nl_fill_service_attr(msg, svc))
+ goto nla_put_failure;
+ if (ipvs_nl_fill_dest_attr(msg, dest))
+ goto nla_put_failure;
+ return ipvs_nl_send_message(msg, ipvs_nl_noop_cb, NULL);
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return -1;
+ }
+#endif
+
+ CHECK_IPV4(svc, -1);
+ CHECK_IPV4(dest, -1);
+ memcpy(&svcdest.svc, svc, sizeof(svcdest.svc));
+ memcpy(&svcdest.dest, dest, sizeof(svcdest.dest));
+ return setsockopt(sockfd, IPPROTO_IP, IP_VS_SO_SET_ADDDEST,
+ (char *)&svcdest, sizeof(svcdest));
+}
+
+
+int ipvs_update_dest(ipvs_service_t *svc, ipvs_dest_t *dest)
+{
+ ipvs_servicedest_t svcdest;
+
+ ipvs_func = ipvs_update_dest;
+#ifdef LIBIPVS_USE_NL
+ if (try_nl) {
+ struct nl_msg *msg = ipvs_nl_message(IPVS_CMD_SET_DEST, 0);
+ if (!msg) return -1;
+ if (ipvs_nl_fill_service_attr(msg, svc))
+ goto nla_put_failure;
+ if (ipvs_nl_fill_dest_attr(msg, dest))
+ goto nla_put_failure;
+ return ipvs_nl_send_message(msg, ipvs_nl_noop_cb, NULL);
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return -1;
+ }
+#endif
+ CHECK_IPV4(svc, -1);
+ CHECK_IPV4(dest, -1);
+ memcpy(&svcdest.svc, svc, sizeof(svcdest.svc));
+ memcpy(&svcdest.dest, dest, sizeof(svcdest.dest));
+ return setsockopt(sockfd, IPPROTO_IP, IP_VS_SO_SET_EDITDEST,
+ (char *)&svcdest, sizeof(svcdest));
+}
+
+
+int ipvs_del_dest(ipvs_service_t *svc, ipvs_dest_t *dest)
+{
+ ipvs_servicedest_t svcdest;
+
+ ipvs_func = ipvs_del_dest;
+#ifdef LIBIPVS_USE_NL
+ if (try_nl) {
+ struct nl_msg *msg = ipvs_nl_message(IPVS_CMD_DEL_DEST, 0);
+ if (!msg) return -1;
+ if (ipvs_nl_fill_service_attr(msg, svc))
+ goto nla_put_failure;
+ if (ipvs_nl_fill_dest_attr(msg, dest))
+ goto nla_put_failure;
+ return ipvs_nl_send_message(msg, ipvs_nl_noop_cb, NULL);
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return -1;
+ }
+#endif
+
+ CHECK_IPV4(svc, -1);
+ CHECK_IPV4(dest, -1);
+ memcpy(&svcdest.svc, svc, sizeof(svcdest.svc));
+ memcpy(&svcdest.dest, dest, sizeof(svcdest.dest));
+ return setsockopt(sockfd, IPPROTO_IP, IP_VS_SO_SET_DELDEST,
+ (char *)&svcdest, sizeof(svcdest));
+}
+
+
+int ipvs_set_timeout(ipvs_timeout_t *to)
+{
+ ipvs_func = ipvs_set_timeout;
+#ifdef LIBIPVS_USE_NL
+ if (try_nl) {
+ struct nl_msg *msg = ipvs_nl_message(IPVS_CMD_SET_TIMEOUT, 0);
+ if (!msg) return -1;
+ NLA_PUT_U32(msg, IPVS_CMD_ATTR_TIMEOUT_TCP, to->tcp_timeout);
+ NLA_PUT_U32(msg, IPVS_CMD_ATTR_TIMEOUT_TCP_FIN, to->tcp_fin_timeout);
+ NLA_PUT_U32(msg, IPVS_CMD_ATTR_TIMEOUT_UDP, to->udp_timeout);
+ return ipvs_nl_send_message(msg, ipvs_nl_noop_cb, NULL);
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return -1;
+ }
+#endif
+ return setsockopt(sockfd, IPPROTO_IP, IP_VS_SO_SET_TIMEOUT, (char *)to,
+ sizeof(*to));
+}
+
+
+int ipvs_start_daemon(ipvs_daemon_t *dm)
+{
+ ipvs_func = ipvs_start_daemon;
+#ifdef LIBIPVS_USE_NL
+ if (try_nl) {
+ struct nlattr *nl_daemon;
+ struct nl_msg *msg = ipvs_nl_message(IPVS_CMD_NEW_DAEMON, 0);
+ if (!msg) return -1;
+
+ nl_daemon = nla_nest_start(msg, IPVS_CMD_ATTR_DAEMON);
+ if (!nl_daemon)
+ goto nla_put_failure;
+
+ NLA_PUT_U32(msg, IPVS_DAEMON_ATTR_STATE, dm->state);
+ NLA_PUT_STRING(msg, IPVS_DAEMON_ATTR_MCAST_IFN, dm->mcast_ifn);
+ NLA_PUT_U32(msg, IPVS_DAEMON_ATTR_SYNC_ID, dm->syncid);
+
+ nla_nest_end(msg, nl_daemon);
+
+ return ipvs_nl_send_message(msg, ipvs_nl_noop_cb, NULL);
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return -1;
+ }
+#endif
+ return setsockopt(sockfd, IPPROTO_IP, IP_VS_SO_SET_STARTDAEMON,
+ (char *)dm, sizeof(*dm));
+}
+
+
+int ipvs_stop_daemon(ipvs_daemon_t *dm)
+{
+ ipvs_func = ipvs_stop_daemon;
+#ifdef LIBIPVS_USE_NL
+ if (try_nl) {
+ struct nlattr *nl_daemon;
+ struct nl_msg *msg = ipvs_nl_message(IPVS_CMD_DEL_DAEMON, 0);
+ if (!msg) return -1;
+
+ nl_daemon = nla_nest_start(msg, IPVS_CMD_ATTR_DAEMON);
+ if (!nl_daemon)
+ goto nla_put_failure;
+
+ NLA_PUT_U32(msg, IPVS_DAEMON_ATTR_STATE, dm->state);
+ NLA_PUT_STRING(msg, IPVS_DAEMON_ATTR_MCAST_IFN, dm->mcast_ifn);
+ NLA_PUT_U32(msg, IPVS_DAEMON_ATTR_SYNC_ID, dm->syncid);
+
+ nla_nest_end(msg, nl_daemon);
+
+ return ipvs_nl_send_message(msg, ipvs_nl_noop_cb, NULL);
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return -1;
+ }
+#endif
+ return setsockopt(sockfd, IPPROTO_IP, IP_VS_SO_SET_STOPDAEMON,
+ (char *)dm, sizeof(*dm));
+}
+
+#ifdef LIBIPVS_USE_NL
+static int ipvs_parse_stats(struct ip_vs_stats_user *stats, struct nlattr *nla) {
+ struct nlattr *attrs[IPVS_STATS_ATTR_MAX + 1];
+
+ if (nla_parse_nested(attrs, IPVS_STATS_ATTR_MAX, nla, ipvs_stats_policy))
+ return -1;
+
+ if (!(attrs[IPVS_STATS_ATTR_CONNS] &&
+ attrs[IPVS_STATS_ATTR_INPKTS] &&
+ attrs[IPVS_STATS_ATTR_OUTPKTS] &&
+ attrs[IPVS_STATS_ATTR_INBYTES] &&
+ attrs[IPVS_STATS_ATTR_OUTBYTES] &&
+ attrs[IPVS_STATS_ATTR_CPS] &&
+ attrs[IPVS_STATS_ATTR_INPPS] &&
+ attrs[IPVS_STATS_ATTR_OUTPPS] &&
+ attrs[IPVS_STATS_ATTR_INBPS] &&
+ attrs[IPVS_STATS_ATTR_OUTBPS]))
+ return -1;
+
+ stats->conns = nla_get_u32(attrs[IPVS_STATS_ATTR_CONNS]);
+ stats->inpkts = nla_get_u32(attrs[IPVS_STATS_ATTR_INPKTS]);
+ stats->outpkts = nla_get_u32(attrs[IPVS_STATS_ATTR_OUTPKTS]);
+ stats->inbytes = nla_get_u64(attrs[IPVS_STATS_ATTR_INBYTES]);
+ stats->outbytes = nla_get_u64(attrs[IPVS_STATS_ATTR_OUTBYTES]);
+ stats->cps = nla_get_u32(attrs[IPVS_STATS_ATTR_CPS]);
+ stats->inpps = nla_get_u32(attrs[IPVS_STATS_ATTR_INPPS]);
+ stats->outpps = nla_get_u32(attrs[IPVS_STATS_ATTR_OUTPPS]);
+ stats->inbps = nla_get_u32(attrs[IPVS_STATS_ATTR_INBPS]);
+ stats->outbps = nla_get_u32(attrs[IPVS_STATS_ATTR_OUTBPS]);
+
+ return 0;
+
+}
+
+static int ipvs_services_parse_cb(struct nl_msg *msg, void *arg)
+{
+ struct nlmsghdr *nlh = nlmsg_hdr(msg);
+ struct nlattr *attrs[IPVS_CMD_ATTR_MAX + 1];
+ struct nlattr *svc_attrs[IPVS_SVC_ATTR_MAX + 1];
+ struct ip_vs_get_services **getp = (struct ip_vs_get_services **)arg;
+ struct ip_vs_get_services *get = (struct ip_vs_get_services *)*getp;
+ struct ip_vs_flags flags;
+ int i = get->num_services;
+
+ if (genlmsg_parse(nlh, 0, attrs, IPVS_CMD_ATTR_MAX, ipvs_cmd_policy) != 0)
+ return -1;
+
+ if (!attrs[IPVS_CMD_ATTR_SERVICE])
+ return -1;
+
+ if (nla_parse_nested(svc_attrs, IPVS_SVC_ATTR_MAX, attrs[IPVS_CMD_ATTR_SERVICE], ipvs_service_policy))
+ return -1;
+
+ memset(&(get->entrytable[i]), 0, sizeof(get->entrytable[i]));
+
+ if (!(svc_attrs[IPVS_SVC_ATTR_AF] &&
+ (svc_attrs[IPVS_SVC_ATTR_FWMARK] ||
+ (svc_attrs[IPVS_SVC_ATTR_PROTOCOL] &&
+ svc_attrs[IPVS_SVC_ATTR_ADDR] &&
+ svc_attrs[IPVS_SVC_ATTR_PORT])) &&
+ svc_attrs[IPVS_SVC_ATTR_SCHED_NAME] &&
+ svc_attrs[IPVS_SVC_ATTR_NETMASK] &&
+ svc_attrs[IPVS_SVC_ATTR_TIMEOUT] &&
+ svc_attrs[IPVS_SVC_ATTR_FLAGS]))
+ return -1;
+
+ get->entrytable[i].af = nla_get_u16(svc_attrs[IPVS_SVC_ATTR_AF]);
+
+ if (svc_attrs[IPVS_SVC_ATTR_FWMARK])
+ get->entrytable[i].fwmark = nla_get_u32(svc_attrs[IPVS_SVC_ATTR_FWMARK]);
+ else {
+ get->entrytable[i].protocol = nla_get_u16(svc_attrs[IPVS_SVC_ATTR_PROTOCOL]);
+ memcpy(&(get->entrytable[i].addr), nla_data(svc_attrs[IPVS_SVC_ATTR_ADDR]),
+ sizeof(get->entrytable[i].addr));
+ get->entrytable[i].port = nla_get_u16(svc_attrs[IPVS_SVC_ATTR_PORT]);
+ }
+
+ strncpy(get->entrytable[i].sched_name,
+ nla_get_string(svc_attrs[IPVS_SVC_ATTR_SCHED_NAME]),
+ IP_VS_SCHEDNAME_MAXLEN);
+
+ get->entrytable[i].netmask = nla_get_u32(svc_attrs[IPVS_SVC_ATTR_NETMASK]);
+ get->entrytable[i].timeout = nla_get_u32(svc_attrs[IPVS_SVC_ATTR_TIMEOUT]);
+ nla_memcpy(&flags, svc_attrs[IPVS_SVC_ATTR_FLAGS], sizeof(flags));
+ get->entrytable[i].flags = flags.flags & flags.mask;
+
+ if (ipvs_parse_stats(&(get->entrytable[i].stats),
+ svc_attrs[IPVS_SVC_ATTR_STATS]) != 0)
+ return -1;
+
+ get->entrytable[i].num_dests = 0;
+
+ i++;
+
+ get->num_services = i;
+ get = realloc(get, sizeof(*get)
+ + sizeof(ipvs_service_entry_t) * (get->num_services + 1));
+ *getp = get;
+ return 0;
+}
+#endif
+
+struct ip_vs_get_services *ipvs_get_services(void)
+{
+ struct ip_vs_get_services *get;
+ struct ip_vs_get_services_kern *getk;
+ socklen_t len;
+ int i;
+
+#ifdef LIBIPVS_USE_NL
+ if (try_nl) {
+ struct nl_msg *msg;
+ len = sizeof(*get) +
+ sizeof(ipvs_service_entry_t);
+ if (!(get = malloc(len)))
+ return NULL;
+ get->num_services = 0;
+
+ msg = ipvs_nl_message(IPVS_CMD_GET_SERVICE, NLM_F_DUMP);
+ if (msg && (ipvs_nl_send_message(msg, ipvs_services_parse_cb, &get) == 0))
+ return get;
+
+ free(get);
+ return NULL;
+ }
+#endif
+
+ len = sizeof(*get) +
+ sizeof(ipvs_service_entry_t) * ipvs_info.num_services;
+ if (!(get = malloc(len)))
+ return NULL;
+ len = sizeof(*getk) +
+ sizeof(struct ip_vs_service_entry_kern) * ipvs_info.num_services;
+ if (!(getk = malloc(len)))
+ return NULL;
+
+ ipvs_func = ipvs_get_services;
+ getk->num_services = ipvs_info.num_services;
+ if (getsockopt(sockfd, IPPROTO_IP,
+ IP_VS_SO_GET_SERVICES, getk, &len) < 0) {
+ free(get);
+ free(getk);
+ return NULL;
+ }
+ memcpy(get, getk, sizeof(struct ip_vs_get_services));
+ for (i = 0; i < getk->num_services; i++) {
+ memcpy(&get->entrytable[i], &getk->entrytable[i],
+ sizeof(struct ip_vs_service_entry_kern));
+ get->entrytable[i].af = AF_INET;
+ get->entrytable[i].addr.ip = get->entrytable[i].__addr_v4;
+ }
+ free(getk);
+ return get;
+}
+
+
+typedef int (*qsort_cmp_t)(const void *, const void *);
+
+int
+ipvs_cmp_services(ipvs_service_entry_t *s1, ipvs_service_entry_t *s2)
+{
+ int r, i;
+
+ r = s1->fwmark - s2->fwmark;
+ if (r != 0)
+ return r;
+
+ r = s1->af - s2->af;
+ if (r != 0)
+ return r;
+
+ r = s1->protocol - s2->protocol;
+ if (r != 0)
+ return r;
+
+ if (s1->af == AF_INET6)
+ for (i = 0; !r && (i < 4); i++)
+ r = ntohl(s1->addr.in6.s6_addr32[i]) - ntohl(s2->addr.in6.s6_addr32[i]);
+ else
+ r = ntohl(s1->addr.ip) - ntohl(s2->addr.ip);
+ if (r != 0)
+ return r;
+
+ return ntohs(s1->port) - ntohs(s2->port);
+}
+
+
+void
+ipvs_sort_services(struct ip_vs_get_services *s, ipvs_service_cmp_t f)
+{
+ qsort(s->entrytable, s->num_services,
+ sizeof(ipvs_service_entry_t), (qsort_cmp_t)f);
+}
+
+#ifdef LIBIPVS_USE_NL
+static int ipvs_dests_parse_cb(struct nl_msg *msg, void *arg)
+{
+ struct nlmsghdr *nlh = nlmsg_hdr(msg);
+ struct nlattr *attrs[IPVS_DEST_ATTR_MAX + 1];
+ struct nlattr *dest_attrs[IPVS_SVC_ATTR_MAX + 1];
+ struct ip_vs_get_dests **dp = (struct ip_vs_get_dests **)arg;
+ struct ip_vs_get_dests *d = (struct ip_vs_get_dests *)*dp;
+ int i = d->num_dests;
+
+ if (genlmsg_parse(nlh, 0, attrs, IPVS_CMD_ATTR_MAX, ipvs_cmd_policy) != 0)
+ return -1;
+
+ if (!attrs[IPVS_CMD_ATTR_DEST])
+ return -1;
+
+ if (nla_parse_nested(dest_attrs, IPVS_DEST_ATTR_MAX, attrs[IPVS_CMD_ATTR_DEST], ipvs_dest_policy))
+ return -1;
+
+ memset(&(d->entrytable[i]), 0, sizeof(d->entrytable[i]));
+
+ if (!(dest_attrs[IPVS_DEST_ATTR_ADDR] &&
+ dest_attrs[IPVS_DEST_ATTR_PORT] &&
+ dest_attrs[IPVS_DEST_ATTR_FWD_METHOD] &&
+ dest_attrs[IPVS_DEST_ATTR_WEIGHT] &&
+ dest_attrs[IPVS_DEST_ATTR_U_THRESH] &&
+ dest_attrs[IPVS_DEST_ATTR_L_THRESH] &&
+ dest_attrs[IPVS_DEST_ATTR_ACTIVE_CONNS] &&
+ dest_attrs[IPVS_DEST_ATTR_INACT_CONNS] &&
+ dest_attrs[IPVS_DEST_ATTR_PERSIST_CONNS]))
+ return -1;
+
+ memcpy(&(d->entrytable[i].addr),
+ nla_data(dest_attrs[IPVS_DEST_ATTR_ADDR]),
+ sizeof(d->entrytable[i].addr));
+ d->entrytable[i].port = nla_get_u16(dest_attrs[IPVS_DEST_ATTR_PORT]);
+ d->entrytable[i].conn_flags = nla_get_u32(dest_attrs[IPVS_DEST_ATTR_FWD_METHOD]);
+ d->entrytable[i].weight = nla_get_u32(dest_attrs[IPVS_DEST_ATTR_WEIGHT]);
+ d->entrytable[i].u_threshold = nla_get_u32(dest_attrs[IPVS_DEST_ATTR_U_THRESH]);
+ d->entrytable[i].l_threshold = nla_get_u32(dest_attrs[IPVS_DEST_ATTR_L_THRESH]);
+ d->entrytable[i].activeconns = nla_get_u32(dest_attrs[IPVS_DEST_ATTR_ACTIVE_CONNS]);
+ d->entrytable[i].inactconns = nla_get_u32(dest_attrs[IPVS_DEST_ATTR_INACT_CONNS]);
+ d->entrytable[i].activeconns = nla_get_u32(dest_attrs[IPVS_DEST_ATTR_PERSIST_CONNS]);
+ d->entrytable[i].af = d->af;
+
+ if (ipvs_parse_stats(&(d->entrytable[i].stats),
+ dest_attrs[IPVS_DEST_ATTR_STATS]) != 0)
+ return -1;
+
+ i++;
+
+ d->num_dests = i;
+ d = realloc(d, sizeof(*d) + sizeof(ipvs_dest_entry_t) * (d->num_dests + 1));
+ *dp = d;
+ return 0;
+}
+#endif
+
+struct ip_vs_get_dests *ipvs_get_dests(ipvs_service_entry_t *svc)
+{
+ struct ip_vs_get_dests *d;
+ struct ip_vs_get_dests_kern *dk;
+ socklen_t len;
+ int i;
+
+ len = sizeof(*d) + sizeof(ipvs_dest_entry_t) * svc->num_dests;
+ if (!(d = malloc(len)))
+ return NULL;
+
+ ipvs_func = ipvs_get_dests;
+
+#ifdef LIBIPVS_USE_NL
+ if (try_nl) {
+ struct nl_msg *msg;
+ struct nlattr *nl_service;
+ if (svc->num_dests == 0)
+ d = realloc(d,sizeof(*d) + sizeof(ipvs_dest_entry_t));
+ d->fwmark = svc->fwmark;
+ d->protocol = svc->protocol;
+ d->addr = svc->addr;
+ d->port = svc->port;
+ d->num_dests = svc->num_dests;
+ d->af = svc->af;
+
+ msg = ipvs_nl_message(IPVS_CMD_GET_DEST, NLM_F_DUMP);
+ if (!msg)
+ goto ipvs_nl_dest_failure;
+
+ nl_service = nla_nest_start(msg, IPVS_CMD_ATTR_SERVICE);
+ if (!nl_service)
+ goto nla_put_failure;
+
+ NLA_PUT_U16(msg, IPVS_SVC_ATTR_AF, svc->af);
+
+ if (svc->fwmark) {
+ NLA_PUT_U32(msg, IPVS_SVC_ATTR_FWMARK, svc->fwmark);
+ } else {
+ NLA_PUT_U16(msg, IPVS_SVC_ATTR_PROTOCOL, svc->protocol);
+ NLA_PUT(msg, IPVS_SVC_ATTR_ADDR, sizeof(svc->addr),
+ &svc->addr);
+ NLA_PUT_U16(msg, IPVS_SVC_ATTR_PORT, svc->port);
+ }
+
+ nla_nest_end(msg, nl_service);
+ if (ipvs_nl_send_message(msg, ipvs_dests_parse_cb, &d))
+ goto ipvs_nl_dest_failure;
+
+ return d;
+
+nla_put_failure:
+ nlmsg_free(msg);
+ipvs_nl_dest_failure:
+ free(d);
+ return NULL;
+ }
+#endif
+
+ if (svc->af != AF_INET) {
+ errno = EAFNOSUPPORT;
+ free(d);
+ return NULL;
+ }
+
+ len = sizeof(*dk) + sizeof(struct ip_vs_dest_entry_kern) * svc->num_dests;
+ if (!(dk = malloc(len)))
+ return NULL;
+
+ dk->fwmark = svc->fwmark;
+ dk->protocol = svc->protocol;
+ dk->addr = svc->addr.ip;
+ dk->port = svc->port;
+ dk->num_dests = svc->num_dests;
+
+ if (getsockopt(sockfd, IPPROTO_IP,
+ IP_VS_SO_GET_DESTS, dk, &len) < 0) {
+ free(d);
+ free(dk);
+ return NULL;
+ }
+ memcpy(d, dk, sizeof(struct ip_vs_get_dests_kern));
+ d->af = AF_INET;
+ d->addr.ip = d->__addr_v4;
+ for (i = 0; i < dk->num_dests; i++) {
+ memcpy(&d->entrytable[i], &dk->entrytable[i],
+ sizeof(struct ip_vs_dest_entry_kern));
+ d->entrytable[i].af = AF_INET;
+ d->entrytable[i].addr.ip = d->entrytable[i].__addr_v4;
+ }
+ free(dk);
+ return d;
+}
+
+
+int ipvs_cmp_dests(ipvs_dest_entry_t *d1, ipvs_dest_entry_t *d2)
+{
+ int r = 0, i;
+
+ if (d1->af == AF_INET6)
+ for (i = 0; !r && (i < 4); i++)
+ r = ntohl(d1->addr.in6.s6_addr32[i]) -
+ ntohl(d2->addr.in6.s6_addr32[i]);
+ else
+ r = ntohl(d1->addr.ip) - ntohl(d2->addr.ip);
+ if (r != 0)
+ return r;
+
+ return ntohs(d1->port) - ntohs(d2->port);
+}
+
+
+void ipvs_sort_dests(struct ip_vs_get_dests *d, ipvs_dest_cmp_t f)
+{
+ qsort(d->entrytable, d->num_dests,
+ sizeof(ipvs_dest_entry_t), (qsort_cmp_t)f);
+}
+
+
+ipvs_service_entry_t *
+ipvs_get_service(__u32 fwmark, __u16 af, __u16 protocol, union nf_inet_addr addr, __u16 port)
+{
+ ipvs_service_entry_t *svc;
+ socklen_t len;
+
+ len = sizeof(*svc);
+ if (!(svc = malloc(len)))
+ return NULL;
+
+ ipvs_func = ipvs_get_service;
+
+ svc->fwmark = fwmark;
+ svc->af = af;
+ svc->protocol = protocol;
+ svc->addr = addr;
+ svc->port = port;
+#ifdef LIBIPVS_USE_NL
+ if (try_nl) {
+ struct ip_vs_get_services *get;
+ struct nl_msg *msg;
+ ipvs_service_t tsvc;
+ tsvc.fwmark = fwmark;
+ tsvc.af = af;
+ tsvc.protocol= protocol;
+ tsvc.addr = addr;
+ tsvc.port = port;
+
+ if (!(get = malloc(sizeof(*get) + sizeof(ipvs_service_entry_t))))
+ goto ipvs_get_service_err2;
+
+ get->num_services = 0;
+
+ msg = ipvs_nl_message(IPVS_CMD_GET_SERVICE, 0);
+ if (!msg) goto ipvs_get_service_err;
+ if (ipvs_nl_fill_service_attr(msg, &tsvc))
+ goto nla_put_failure;
+ if (ipvs_nl_send_message(msg, ipvs_services_parse_cb, &get))
+ goto ipvs_get_service_err;
+
+ memcpy(svc, &(get->entrytable[0]), sizeof(*svc));
+ free(get);
+ return svc;
+
+nla_put_failure:
+ nlmsg_free(msg);
+ipvs_get_service_err:
+ free(get);
+ipvs_get_service_err2:
+ free(svc);
+ return NULL;
+ }
+#endif
+
+ CHECK_IPV4(svc, NULL);
+ if (getsockopt(sockfd, IPPROTO_IP, IP_VS_SO_GET_SERVICE,
+ (char *)svc, &len)) {
+ free(svc);
+ return NULL;
+ }
+ svc->af = AF_INET;
+ svc->addr.ip = svc->__addr_v4;
+ return svc;
+}
+
+#ifdef LIBIPVS_USE_NL
+static int ipvs_timeout_parse_cb(struct nl_msg *msg, void *arg)
+{
+ struct nlmsghdr *nlh = nlmsg_hdr(msg);
+ struct nlattr *attrs[IPVS_CMD_ATTR_MAX + 1];
+ ipvs_timeout_t *u = (ipvs_timeout_t *)arg;
+
+ if (genlmsg_parse(nlh, 0, attrs, IPVS_CMD_ATTR_MAX, ipvs_cmd_policy) != 0)
+ return -1;
+
+ if (attrs[IPVS_CMD_ATTR_TIMEOUT_TCP])
+ u->tcp_timeout = nla_get_u32(attrs[IPVS_CMD_ATTR_TIMEOUT_TCP]);
+ if (attrs[IPVS_CMD_ATTR_TIMEOUT_TCP_FIN])
+ u->tcp_fin_timeout = nla_get_u32(attrs[IPVS_CMD_ATTR_TIMEOUT_TCP_FIN]);
+ if (attrs[IPVS_CMD_ATTR_TIMEOUT_UDP])
+ u->udp_timeout = nla_get_u32(attrs[IPVS_CMD_ATTR_TIMEOUT_UDP]);
+
+ return NL_OK;
+}
+#endif
+
+ipvs_timeout_t *ipvs_get_timeout(void)
+{
+ ipvs_timeout_t *u;
+ socklen_t len;
+
+ len = sizeof(*u);
+ if (!(u = malloc(len)))
+ return NULL;
+
+ ipvs_func = ipvs_get_timeout;
+#ifdef LIBIPVS_USE_NL
+ if (try_nl) {
+ struct nl_msg *msg;
+ memset(u, 0, sizeof(*u));
+ msg = ipvs_nl_message(IPVS_CMD_GET_TIMEOUT, 0);
+ if (msg && (ipvs_nl_send_message(msg, ipvs_timeout_parse_cb, u) == 0))
+ return u;
+
+ free(u);
+ return NULL;
+ }
+#endif
+ if (getsockopt(sockfd, IPPROTO_IP, IP_VS_SO_GET_TIMEOUT,
+ (char *)u, &len)) {
+ free(u);
+ return NULL;
+ }
+ return u;
+}
+
+#ifdef LIBIPVS_USE_NL
+static int ipvs_daemon_parse_cb(struct nl_msg *msg, void *arg)
+{
+ struct nlmsghdr *nlh = nlmsg_hdr(msg);
+ struct nlattr *attrs[IPVS_CMD_ATTR_MAX + 1];
+ struct nlattr *daemon_attrs[IPVS_DAEMON_ATTR_MAX + 1];
+ ipvs_daemon_t *u = (ipvs_daemon_t *)arg;
+ int i = 0;
+
+ /* We may get two daemons. If we've already got one, this is the second */
+ if (u[0].state)
+ i = 1;
+
+ if (genlmsg_parse(nlh, 0, attrs, IPVS_CMD_ATTR_MAX, ipvs_cmd_policy) != 0)
+ return -1;
+
+ if (nla_parse_nested(daemon_attrs, IPVS_DAEMON_ATTR_MAX,
+ attrs[IPVS_CMD_ATTR_DAEMON], ipvs_daemon_policy))
+ return -1;
+
+ if (!(daemon_attrs[IPVS_DAEMON_ATTR_STATE] &&
+ daemon_attrs[IPVS_DAEMON_ATTR_MCAST_IFN] &&
+ daemon_attrs[IPVS_DAEMON_ATTR_SYNC_ID]))
+ return -1;
+
+ u[i].state = nla_get_u32(daemon_attrs[IPVS_DAEMON_ATTR_STATE]);
+ strncpy(u[i].mcast_ifn,
+ nla_get_string(daemon_attrs[IPVS_DAEMON_ATTR_MCAST_IFN]),
+ IP_VS_IFNAME_MAXLEN);
+ u[i].syncid = nla_get_u32(daemon_attrs[IPVS_DAEMON_ATTR_SYNC_ID]);
+
+ return NL_OK;
+}
+#endif
+
+ipvs_daemon_t *ipvs_get_daemon(void)
+{
+ ipvs_daemon_t *u;
+ socklen_t len;
+
+ /* note that we need to get the info about two possible
+ daemons, master and backup. */
+ len = sizeof(*u) * 2;
+ if (!(u = malloc(len)))
+ return NULL;
+
+ ipvs_func = ipvs_get_daemon;
+#ifdef LIBIPVS_USE_NL
+ if (try_nl) {
+ struct nl_msg *msg;
+ memset(u, 0, len);
+ msg = ipvs_nl_message(IPVS_CMD_GET_DAEMON, NLM_F_DUMP);
+ if (msg && (ipvs_nl_send_message(msg, ipvs_daemon_parse_cb, u) == 0))
+ return u;
+
+ free(u);
+ return NULL;
+ }
+#endif
+ if (getsockopt(sockfd, IPPROTO_IP, IP_VS_SO_GET_DAEMON, (char *)u, &len)) {
+ free(u);
+ return NULL;
+ }
+ return u;
+}
+
+
+void ipvs_close(void)
+{
+#ifdef LIBIPVS_USE_NL
+ if (try_nl) {
+ return;
+ }
+#endif
+ close(sockfd);
+}
+
+
+const char *ipvs_strerror(int err)
+{
+ unsigned int i;
+ struct table_struct {
+ void *func;
+ int err;
+ const char *message;
+ } table [] = {
+ { ipvs_add_service, EEXIST, "Service already exists" },
+ { ipvs_add_service, ENOENT, "Scheduler not found" },
+ { ipvs_update_service, ESRCH, "No such service" },
+ { ipvs_update_service, ENOENT, "Scheduler not found" },
+ { ipvs_del_service, ESRCH, "No such service" },
+ { ipvs_zero_service, ESRCH, "No such service" },
+ { ipvs_add_dest, ESRCH, "Service not defined" },
+ { ipvs_add_dest, EEXIST, "Destination already exists" },
+ { ipvs_update_dest, ESRCH, "Service not defined" },
+ { ipvs_update_dest, ENOENT, "No such destination" },
+ { ipvs_del_dest, ESRCH, "Service not defined" },
+ { ipvs_del_dest, ENOENT, "No such destination" },
+ { ipvs_start_daemon, EEXIST, "Daemon has already run" },
+ { ipvs_stop_daemon, ESRCH, "No daemon is running" },
+ { ipvs_get_services, ESRCH, "No such service" },
+ { ipvs_get_dests, ESRCH, "No such service" },
+ { ipvs_get_service, ESRCH, "No such service" },
+ { 0, EPERM, "Permission denied (you must be root)" },
+ { 0, EINVAL, "Invalid operation. Possibly wrong module version, address not unicast, ..." },
+ { 0, ENOPROTOOPT, "Protocol not available" },
+ { 0, ENOMEM, "Memory allocation problem" },
+ { 0, EOPNOTSUPP, "Operation not supported with IPv6" },
+ { 0, EAFNOSUPPORT, "Operation not supported with specified address family" },
+ { 0, EMSGSIZE, "Module is wrong version" },
+ };
+
+ for (i = 0; i < sizeof(table)/sizeof(struct table_struct); i++) {
+ if ((!table[i].func || table[i].func == ipvs_func)
+ && table[i].err == err)
+ return table[i].message;
+ }
+
+ return strerror(err);
+}
diff --git a/libipvs/libipvs.h b/libipvs/libipvs.h
new file mode 100644
index 0000000..15b7d5d
--- /dev/null
+++ b/libipvs/libipvs.h
@@ -0,0 +1,128 @@
+/*
+ * libipvs.h: header file for the library ipvs
+ *
+ * Version: $Id: libipvs.h,v 1.7 2003/06/08 09:31:39 wensong Exp $
+ *
+ * Authors: Wensong Zhang <wensong@linuxvirtualserver.org>
+ *
+ */
+
+#ifndef _LIBIPVS_H
+#define _LIBIPVS_H
+
+#include "ip_vs.h"
+
+
+#define MINIMUM_IPVS_VERSION_MAJOR 1
+#define MINIMUM_IPVS_VERSION_MINOR 1
+#define MINIMUM_IPVS_VERSION_PATCH 4
+
+#ifndef IPVS_VERSION
+#define IPVS_VERSION(x,y,z) (((x)<<16)+((y)<<8)+(z))
+#endif
+
+/*
+ * The default IPVS_SVC_PERSISTENT_TIMEOUT is a little larger than average
+ * connection time plus IPVS TCP FIN timeout (2*60 seconds). Because the
+ * connection template won't be released until its controlled connection
+ * entries are expired.
+ * If IPVS_SVC_PERSISTENT_TIMEOUT is too less, the template will expire
+ * soon and will be put in expire again and again, which causes additional
+ * overhead. If it is too large, the same will always visit the same
+ * server, which may make dynamic load imbalance worse.
+ */
+#define IPVS_SVC_PERSISTENT_TIMEOUT (6*60)
+
+
+typedef struct ip_vs_service_user ipvs_service_t;
+typedef struct ip_vs_dest_user ipvs_dest_t;
+typedef struct ip_vs_timeout_user ipvs_timeout_t;
+typedef struct ip_vs_daemon_user ipvs_daemon_t;
+typedef struct ip_vs_service_entry ipvs_service_entry_t;
+typedef struct ip_vs_dest_entry ipvs_dest_entry_t;
+
+
+/* ipvs info variable */
+extern struct ip_vs_getinfo ipvs_info;
+
+/* init socket and get ipvs info */
+extern int ipvs_init(void);
+
+/* get ipvs info separately */
+extern int ipvs_getinfo(void);
+
+/* get the version number */
+extern unsigned int ipvs_version(void);
+
+/* flush all the rules */
+extern int ipvs_flush(void);
+
+/* add a virtual service */
+extern int ipvs_add_service(ipvs_service_t *svc);
+
+/* update a virtual service with new options */
+extern int ipvs_update_service(ipvs_service_t *svc);
+
+/* delete a virtual service */
+extern int ipvs_del_service(ipvs_service_t *svc);
+
+/* zero the counters of a service or all */
+extern int ipvs_zero_service(ipvs_service_t *svc);
+
+/* add a destination server into a service */
+extern int ipvs_add_dest(ipvs_service_t *svc, ipvs_dest_t *dest);
+
+/* update a destination server with new options */
+extern int ipvs_update_dest(ipvs_service_t *svc, ipvs_dest_t *dest);
+
+/* remove a destination server from a service */
+extern int ipvs_del_dest(ipvs_service_t *svc, ipvs_dest_t *dest);
+
+/* set timeout */
+extern int ipvs_set_timeout(ipvs_timeout_t *to);
+
+/* start a connection synchronizaiton daemon (master/backup) */
+extern int ipvs_start_daemon(ipvs_daemon_t *dm);
+
+/* stop a connection synchronizaiton daemon (master/backup) */
+extern int ipvs_stop_daemon(ipvs_daemon_t *dm);
+
+
+/* get all the ipvs services */
+extern struct ip_vs_get_services *ipvs_get_services(void);
+
+/* sort the service entries */
+typedef int (*ipvs_service_cmp_t)(ipvs_service_entry_t *,
+ ipvs_service_entry_t *);
+extern int ipvs_cmp_services(ipvs_service_entry_t *s1,
+ ipvs_service_entry_t *s2);
+extern void ipvs_sort_services(struct ip_vs_get_services *s,
+ ipvs_service_cmp_t f);
+
+/* get the destination array of the specified service */
+extern struct ip_vs_get_dests *ipvs_get_dests(ipvs_service_entry_t *svc);
+
+/* sort the destination entries */
+typedef int (*ipvs_dest_cmp_t)(ipvs_dest_entry_t *,
+ ipvs_dest_entry_t *);
+extern int ipvs_cmp_dests(ipvs_dest_entry_t *d1,
+ ipvs_dest_entry_t *d2);
+extern void ipvs_sort_dests(struct ip_vs_get_dests *d,
+ ipvs_dest_cmp_t f);
+
+/* get an ipvs service entry */
+extern ipvs_service_entry_t *
+ipvs_get_service(__u32 fwmark, __u16 af, __u16 protocol, union nf_inet_addr addr, __u16 port);
+
+/* get ipvs timeout */
+extern ipvs_timeout_t *ipvs_get_timeout(void);
+
+/* get ipvs daemon information */
+extern ipvs_daemon_t *ipvs_get_daemon(void);
+
+/* close the socket */
+extern void ipvs_close(void);
+
+extern const char *ipvs_strerror(int err);
+
+#endif /* _LIBIPVS_H */