This adds the --pe [engine] option to the -A and -E commands which allows a persistence engine to be associated with a virtual service. The absence of --pe sets no persistence engine. The --pe option only works when ipvsadm is compiled to use netlink for user-space/kernel communication. This patch also allows the --persistent-conn option to be given to the -L command, which will list persistence engine data, if any is present, when listing connections (and persistence templates). At this time the only (proposed) persistence engine is sip. Signed-off-by: Simon Horman <horms@xxxxxxxxxxxx> v0.4 * Fix indentation of --pe help text --- Makefile | 3 ++- PERSISTENCE_ENGINES | 1 + ipvsadm.8 | 4 ++++ ipvsadm.c | 51 ++++++++++++++++++++++++++++++++++++++++----------- libipvs/ip_vs.h | 8 ++++++++ libipvs/libipvs.c | 44 +++++++++++++++++++++++++++++++------------- 6 files changed, 86 insertions(+), 25 deletions(-) create mode 100644 PERSISTENCE_ENGINES diff --git a/Makefile b/Makefile index 46300ef..23274e5 100644 --- a/Makefile +++ b/Makefile @@ -29,6 +29,7 @@ NAME = ipvsadm VERSION = $(shell cat VERSION) RELEASE = 1 SCHEDULERS = "$(shell cat SCHEDULERS)" +PE_LIST = "$(shell cat PERSISTENCE_ENGINES)" PROGROOT = $(shell basename `pwd`) ARCH = $(shell uname -m) RPMSOURCEDIR = $(shell rpm --eval '%_sourcedir') @@ -83,7 +84,7 @@ ifneq (0,$(HAVE_NL)) LIBS += -lnl endif DEFINES = -DVERSION=\"$(VERSION)\" -DSCHEDULERS=\"$(SCHEDULERS)\" \ - $(POPT_DEFINE) + -DPE_LIST=\"$(PE_LIST)\" $(POPT_DEFINE) DEFINES += $(shell if [ ! -f ../ip_vs.h ]; then \ echo "-DHAVE_NET_IP_VS_H"; fi;) diff --git a/PERSISTENCE_ENGINES b/PERSISTENCE_ENGINES new file mode 100644 index 0000000..641cf83 --- /dev/null +++ b/PERSISTENCE_ENGINES @@ -0,0 +1 @@ +sip diff --git a/ipvsadm.8 b/ipvsadm.8 index 816e307..001ae74 100644 --- a/ipvsadm.8 +++ b/ipvsadm.8 @@ -391,6 +391,10 @@ with this option will display the persistent connection counter information of each server in service listing. The persistent connection is used to forward the actual connections from the same client/network to the same server. +.sp +The \fIlist\fP command with the -c, --connection option and this option +will include persistence engine data, if any is present, when listing +connections. .TP .B --sort Sort the list of virtual services and real servers. The virtual diff --git a/ipvsadm.c b/ipvsadm.c index 1ac6c7f..0332b22 100644 --- a/ipvsadm.c +++ b/ipvsadm.c @@ -181,13 +181,15 @@ static const char* cmdnames[] = { #define OPT_SYNCID 0x080000 #define OPT_EXACT 0x100000 #define OPT_ONEPACKET 0x200000 -#define NUMBER_OF_OPT 22 +#define OPT_PERSISTENCE_ENGINE 0x400000 +#define NUMBER_OF_OPT 23 static const char* optnames[] = { "numeric", "connection", "service-address", "scheduler", + "pe", "persistent", "netmask", "real-server", @@ -282,6 +284,7 @@ enum { TAG_PERSISTENTCONN, TAG_SORT, TAG_NO_SORT, + TAG_PERSISTENCE_ENGINE, }; /* various parsing helpers & parsing functions */ @@ -421,6 +424,8 @@ parse_options(int argc, char **argv, struct ipvs_command_entry *ce, { "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 }, { NULL, 0, 0, NULL, 0, NULL, NULL } }; @@ -647,6 +652,10 @@ parse_options(int argc, char **argv, struct ipvs_command_entry *ce, set_option(options, OPT_ONEPACKET); ce->svc.flags |= IP_VS_SVC_F_ONEPACKET; break; + case TAG_PERSISTENCE_ENGINE: + set_option(options, OPT_PERSISTENCE_ENGINE); + strncpy(ce->svc.pe_name, optarg, IP_VS_PENAME_MAXLEN); + break; default: fail(2, "invalid option `%s'", poptBadOption(context, POPT_BADOPTION_NOALIAS)); @@ -763,9 +772,10 @@ static int process_options(int argc, char **argv, int reading_stdin) switch (ce.cmd) { case CMD_LIST: - if (options & (OPT_CONNECTION|OPT_TIMEOUT|OPT_DAEMON) && - options & (OPT_STATS|OPT_PERSISTENTCONN| - OPT_RATE|OPT_THRESHOLDS)) + 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) @@ -1060,7 +1070,7 @@ static void usage_exit(const char *program, const int exit_status) version(stream); fprintf(stream, "Usage:\n" - " %s -A|E -t|u|f service-address [-s scheduler] [-p [timeout]] [-M netmask]\n" + " %s -A|E -t|u|f service-address [-s scheduler] [-p [timeout]] [-M netmask] [--pe persistence_engine]\n" " %s -D -t|u|f service-address\n" " %s -C\n" " %s -R\n" @@ -1105,6 +1115,8 @@ static void usage_exit(const char *program, const int exit_status) " --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" @@ -1225,6 +1237,8 @@ static void print_conn(char *buf, unsigned int format) char state[16]; unsigned int expires; unsigned short af = AF_INET; + char pe_name[IP_VS_PENAME_MAXLEN]; + char pe_data[IP_VS_PEDATA_MAXLEN]; int n; char temp1[INET6_ADDRSTRLEN], temp2[INET6_ADDRSTRLEN], temp3[INET6_ADDRSTRLEN]; @@ -1232,9 +1246,10 @@ static void print_conn(char *buf, unsigned int format) unsigned int minutes, seconds; char expire_str[12]; - if ((n = sscanf(buf, "%s %s %hX %s %hX %s %hX %s %d", + if ((n = sscanf(buf, "%s %s %hX %s %hX %s %hX %s %d %s %s", protocol, temp1, &cport, temp2, &vport, - temp3, &dport, state, &expires)) == -1) + temp3, &dport, state, &expires, + pe_name, pe_data)) == -1) exit(1); if (strcmp(protocol, "TCP") == 0) @@ -1268,8 +1283,13 @@ static void print_conn(char *buf, unsigned int format) minutes = expires / 60; sprintf(expire_str, "%02d:%02d", minutes, seconds); - printf("%-3s %-6s %-11s %-18s %-18s %s\n", - protocol, expire_str, state, cname, vname, dname); + if (format & FMT_PERSISTENTCONN) + 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); @@ -1295,8 +1315,13 @@ void list_conn(unsigned int format) exit(1); } printf("IPVS connection entries\n"); - printf("pro expire %-11s %-18s %-18s %s\n", - "state", "source", "virtual", "destination"); + if (format & FMT_PERSISTENTCONN) + printf("pro expire %-11s %-18s %-18s %-18s %-16s %s\n", + "state", "source", "virtual", "destination", + "pe name", "pe_data"); + else + printf("pro expire %-11s %-18s %-18s %s\n", + "state", "source", "virtual", "destination"); /* * Print the VS information according to the format @@ -1459,6 +1484,8 @@ print_service_entry(ipvs_service_entry_t *se, unsigned int format) 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(" ops"); } else if (format & FMT_STATS) { @@ -1488,6 +1515,8 @@ print_service_entry(ipvs_service_entry_t *se, unsigned int format) 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"); } diff --git a/libipvs/ip_vs.h b/libipvs/ip_vs.h index 843c51a..9726a17 100644 --- a/libipvs/ip_vs.h +++ b/libipvs/ip_vs.h @@ -92,8 +92,11 @@ #define IP_VS_CONN_F_ONE_PACKET 0x2000 /* forward only one packet */ #define IP_VS_SCHEDNAME_MAXLEN 16 +#define IP_VS_PENAME_MAXLEN 16 #define IP_VS_IFNAME_MAXLEN 16 +#define IP_VS_PEDATA_MAXLEN 255 + union nf_inet_addr { __u32 all[4]; __be32 ip; @@ -134,6 +137,7 @@ struct ip_vs_service_user { __be32 netmask; /* persistent netmask */ u_int16_t af; union nf_inet_addr addr; + char pe_name[IP_VS_PENAME_MAXLEN]; }; struct ip_vs_dest_kern { @@ -240,6 +244,7 @@ struct ip_vs_service_entry { u_int16_t af; union nf_inet_addr addr; + char pe_name[IP_VS_PENAME_MAXLEN]; }; @@ -429,6 +434,9 @@ enum { IPVS_SVC_ATTR_NETMASK, /* persistent netmask */ IPVS_SVC_ATTR_STATS, /* nested attribute for service stats */ + + IPVS_SVC_ATTR_PE_NAME, /* name of scheduler */ + __IPVS_SVC_ATTR_MAX, }; diff --git a/libipvs/libipvs.c b/libipvs/libipvs.c index 979d5bd..a157e18 100644 --- a/libipvs/libipvs.c +++ b/libipvs/libipvs.c @@ -40,6 +40,15 @@ static int family, try_nl = 1; { errno = EAFNOSUPPORT; return ret; } \ s->__addr_v4 = s->addr.ip; \ +#define CHECK_PE(s, ret) if (s->pe_name) \ + { errno = EAFNOSUPPORT; return ret; } + +#define CHECK_COMPAT_DEST(s, ret) CHECK_IPV4(s, ret) + +#define CHECK_COMPAT_SVC(s, ret) \ + CHECK_IPV4(s, ret); \ + CHECK_PE(s, ret); + #ifdef LIBIPVS_USE_NL struct nl_msg *ipvs_nl_message(int cmd, int flags) { @@ -218,6 +227,8 @@ static int ipvs_nl_fill_service_attr(struct nl_msg *msg, ipvs_service_t *svc) } NLA_PUT_STRING(msg, IPVS_SVC_ATTR_SCHED_NAME, svc->sched_name); + if (svc->pe_name) + NLA_PUT_STRING(msg, IPVS_SVC_ATTR_PE_NAME, svc->pe_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); @@ -245,7 +256,7 @@ int ipvs_add_service(ipvs_service_t *svc) } #endif - CHECK_IPV4(svc, -1); + CHECK_COMPAT_SVC(svc, -1); return setsockopt(sockfd, IPPROTO_IP, IP_VS_SO_SET_ADD, (char *)svc, sizeof(struct ip_vs_service_kern)); } @@ -265,7 +276,7 @@ int ipvs_update_service(ipvs_service_t *svc) return ipvs_nl_send_message(msg, ipvs_nl_noop_cb, NULL); } #endif - CHECK_IPV4(svc, -1); + CHECK_COMPAT_SVC(svc, -1); return setsockopt(sockfd, IPPROTO_IP, IP_VS_SO_SET_EDIT, (char *)svc, sizeof(struct ip_vs_service_kern)); } @@ -285,7 +296,7 @@ int ipvs_del_service(ipvs_service_t *svc) return ipvs_nl_send_message(msg, ipvs_nl_noop_cb, NULL); } #endif - CHECK_IPV4(svc, -1); + CHECK_COMPAT_SVC(svc, -1); return setsockopt(sockfd, IPPROTO_IP, IP_VS_SO_SET_DEL, (char *)svc, sizeof(struct ip_vs_service_kern)); } @@ -310,7 +321,7 @@ int ipvs_zero_service(ipvs_service_t *svc) return ipvs_nl_send_message(msg, ipvs_nl_noop_cb, NULL); } #endif - CHECK_IPV4(svc, -1); + CHECK_COMPAT_SVC(svc, -1); return setsockopt(sockfd, IPPROTO_IP, IP_VS_SO_SET_ZERO, (char *)svc, sizeof(struct ip_vs_service_kern)); } @@ -360,8 +371,8 @@ nla_put_failure: } #endif - CHECK_IPV4(svc, -1); - CHECK_IPV4(dest, -1); + CHECK_COMPAT_SVC(svc, -1); + CHECK_COMPAT_DEST(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, @@ -389,8 +400,8 @@ nla_put_failure: return -1; } #endif - CHECK_IPV4(svc, -1); - CHECK_IPV4(dest, -1); + CHECK_COMPAT_SVC(svc, -1); + CHECK_COMPAT_DEST(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, @@ -419,8 +430,8 @@ nla_put_failure: } #endif - CHECK_IPV4(svc, -1); - CHECK_IPV4(dest, -1); + CHECK_COMPAT_SVC(svc, -1); + CHECK_COMPAT_DEST(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, @@ -593,6 +604,11 @@ static int ipvs_services_parse_cb(struct nl_msg *msg, void *arg) nla_get_string(svc_attrs[IPVS_SVC_ATTR_SCHED_NAME]), IP_VS_SCHEDNAME_MAXLEN); + if (svc_attrs[IPVS_SVC_ATTR_PE_NAME]) + strncpy(get->entrytable[i].pe_name, + nla_get_string(svc_attrs[IPVS_SVC_ATTR_PE_NAME]), + IP_VS_PENAME_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)); @@ -937,7 +953,8 @@ ipvs_get_service_err2: } #endif - CHECK_IPV4(svc, NULL); + CHECK_COMPAT_SVC(svc, NULL); + CHECK_PE(svc, NULL); if (getsockopt(sockfd, IPPROTO_IP, IP_VS_SO_GET_SERVICE, (char *)svc, &len)) { free(svc); @@ -945,6 +962,7 @@ ipvs_get_service_err2: } svc->af = AF_INET; svc->addr.ip = svc->__addr_v4; + svc->pe_name[0] = '\0'; return svc; } @@ -1086,9 +1104,9 @@ const char *ipvs_strerror(int err) const char *message; } table [] = { { ipvs_add_service, EEXIST, "Service already exists" }, - { ipvs_add_service, ENOENT, "Scheduler not found" }, + { ipvs_add_service, ENOENT, "Scheduler or persistence engine not found" }, { ipvs_update_service, ESRCH, "No such service" }, - { ipvs_update_service, ENOENT, "Scheduler not found" }, + { ipvs_update_service, ENOENT, "Scheduler or persistence engine not found" }, { ipvs_del_service, ESRCH, "No such service" }, { ipvs_zero_service, ESRCH, "No such service" }, { ipvs_add_dest, ESRCH, "Service not defined" }, -- 1.7.1 -- To unsubscribe from this list: send the line "unsubscribe netfilter" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html