/*
* Soft: Keepalived is a failover program for the LVS project
* <www.linuxvirtualserver.org>. It monitor & manipulate
* a loadbalanced server pool using multi-layer checks.
*
* Part: Configuration file parser/reader. Place into the dynamic
* data structure representation the conf file representing
* the loadbalanced server pool.
*
* Author: Alexandre Cassen, <acassen@linux-vs.org>
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* 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.
*
* Copyright (C) 2001-2017 Alexandre Cassen, <acassen@gmail.com>
*/
#include "config.h"
#include <errno.h>
#include <stdint.h>
#include <arpa/inet.h>
#include "check_parser.h"
#include "check_data.h"
#include "check_api.h"
#include "global_data.h"
#include "global_parser.h"
#include "main.h"
#include "logger.h"
#include "parser.h"
#include "utils.h"
#include "ipwrapper.h"
#if defined _WITH_VRRP_
#include "vrrp_parser.h"
#endif
#if defined _WITH_BFD_
#include "bfd_parser.h"
#endif
#include "libipvs.h"
#include "track_file.h"
/* List of valid schedulers */
static const char *lvs_schedulers[] =
{"rr", "wrr", "lc", "wlc", "lblc", "sh", "mh", "dh", "fo", "ovf", "lblcr", "sed", "nq", NULL};
/* SSL handlers */
static void
ssl_handler(const vector_t *strvec)
{
if (!strvec)
return;
if (check_data->ssl) {
free_ssl();
report_config_error(CONFIG_GENERAL_ERROR, "SSL context already specified - replacing");
}
check_data->ssl = alloc_ssl();
}
static void
sslpass_handler(const vector_t *strvec)
{
if (vector_size(strvec) < 2) {
report_config_error(CONFIG_GENERAL_ERROR, "SSL password missing");
return;
}
if (check_data->ssl->password) {
report_config_error(CONFIG_GENERAL_ERROR, "SSL password already specified - replacing");
FREE_CONST(check_data->ssl->password);
}
check_data->ssl->password = set_value(strvec);
}
static void
sslca_handler(const vector_t *strvec)
{
if (vector_size(strvec) < 2) {
report_config_error(CONFIG_GENERAL_ERROR, "SSL cafile missing");
return;
}
if (check_data->ssl->cafile) {
report_config_error(CONFIG_GENERAL_ERROR, "SSL cafile already specified - replacing");
FREE_CONST(check_data->ssl->cafile);
}
check_data->ssl->cafile = set_value(strvec);
}
static void
sslcert_handler(const vector_t *strvec)
{
if (vector_size(strvec) < 2) {
report_config_error(CONFIG_GENERAL_ERROR, "SSL certfile missing");
return;
}
if (check_data->ssl->certfile) {
report_config_error(CONFIG_GENERAL_ERROR, "SSL certfile already specified - replacing");
FREE_CONST(check_data->ssl->certfile);
}
check_data->ssl->certfile = set_value(strvec);
}
static void
sslkey_handler(const vector_t *strvec)
{
if (vector_size(strvec) < 2) {
report_config_error(CONFIG_GENERAL_ERROR, "SSL keyfile missing");
return;
}
if (check_data->ssl->keyfile) {
report_config_error(CONFIG_GENERAL_ERROR, "SSL keyfile already specified - replacing");
FREE_CONST(check_data->ssl->keyfile);
}
check_data->ssl->keyfile = set_value(strvec);
}
/* Virtual Servers handlers */
static void
vsg_handler(const vector_t *strvec)
{
virtual_server_group_t *vsg;
vector_t *my_strvec;
ptr_hack_t word;
unsigned i;
if (!strvec)
return;
/* Fetch queued vsg */
alloc_vsg(strvec_slot(strvec, 1));
/* alloc_value_block() does not expect a name after the first word,
* so contruct another vector omitting the VSG name */
my_strvec = vector_alloc();
vector_alloc_slot(my_strvec);
vector_set_slot(my_strvec, 0);
for (i = 2; i < vector_size(strvec); i++) {
vector_alloc_slot(my_strvec);
word.cp = strvec_slot(strvec, i);
vector_set_slot(my_strvec, word.p);
}
alloc_value_block(alloc_vsg_entry, my_strvec);
vector_free(my_strvec);
/* Ensure the virtual server group has some configuration */
vsg = list_last_entry(&check_data->vs_group, virtual_server_group_t, e_list);
if (list_empty(&vsg->vfwmark) && list_empty(&vsg->addr_range)) {
report_config_error(CONFIG_GENERAL_ERROR, "virtual server group %s has no entries - removing"
, vsg->gname);
free_vsg(vsg);
} else if (vsg->have_ipv4 && vsg->have_ipv6 && vsg->fwmark_no_family) {
report_config_error(CONFIG_GENERAL_ERROR, "virtual server group %s cannot have IPv4, IPv6"
" and fwmark without family - removing"
, vsg->gname);
free_vsg(vsg);
}
}
static void
vs_handler(const vector_t *strvec)
{
global_data->have_checker_config = true;
/* If we are not in the checker process, we don't want any more info */
if (!strvec)
return;
alloc_vs(strvec_slot(strvec, 1), vector_size(strvec) >= 3 ? strvec_slot(strvec, 2) : NULL);
}
static void
vs_end_handler(void)
{
virtual_server_t *vs = list_last_entry(&check_data->vs, virtual_server_t, e_list);
real_server_t *rs;
bool mixed_af;
/* If the real (sorry) server uses tunnel forwarding, the address family
* does not have to match the address family of the virtual server */
if (vs->s_svr
#if HAVE_DECL_IPVS_DEST_ATTR_ADDR_FAMILY
&& vs->s_svr->forwarding_method != IP_VS_CONN_F_TUNNEL
#endif
)
{
if (vs->af == AF_UNSPEC)
vs->af = vs->s_svr->addr.ss_family;
else if (vs->af != vs->s_svr->addr.ss_family) {
report_config_error(CONFIG_GENERAL_ERROR, "Address family of virtual server and sorry server %s don't match - skipping sorry server.", inet_sockaddrtos(&vs->s_svr->addr));
FREE(vs->s_svr);
vs->s_svr = NULL;
}
}
if (vs->af == AF_UNSPEC && !vs->vsgname) {
/* This only occurs if the virtual server uses a fwmark, all the
* real/sorry servers are tunnelled, and the address family has not
* been specified.
*
* Maintain backward compatibility. Prior to the commit following 17fa4a3c
* the address family of the virtual server was set from any of its
* real or sorry servers, even if they were tunnelled. However, all the real
* and sorry servers had to be the same address family, even if tunnelled,
* so only set the address family from the tunnelled real/sorry servers
* if all the real/sorry servers are of the same address family. */
mixed_af = false;
if (vs->s_svr)
vs->af = vs->s_svr->addr.ss_family;
list_for_each_entry(rs, &vs->rs, e_list) {
if (vs->af == AF_UNSPEC)
vs->af = rs->addr.ss_family;
else if (vs->af != rs->addr.ss_family) {
mixed_af = true;
break;
}
}
if (mixed_af || vs->af == AF_UNSPEC) {
/* We have a mixture of IPv4 and IPv6 tunnelled real/sorry servers.
* Default to IPv4. */
vs->af = AF_INET;
}
}
}
static void
ip_family_handler(const vector_t *strvec)
{
virtual_server_t *vs = list_last_entry(&check_data->vs, virtual_server_t, e_list);
uint16_t af;
if (!strcmp(strvec_slot(strvec, 1), "inet"))
af = AF_INET;
else if (!strcmp(strvec_slot(strvec, 1), "inet6")) {
#ifndef LIBIPVS_USE_NL
report_config_error(CONFIG_GENERAL_ERROR, "IPVS with IPv6 is not supported by this build");
skip_block(false);
return;
#endif
af = AF_INET6;
}
else {
report_config_error(CONFIG_GENERAL_ERROR, "unknown address family %s", strvec_slot(strvec, 1));
return;
}
if (vs->af != AF_UNSPEC &&
af != vs->af) {
report_config_error(CONFIG_GENERAL_ERROR, "Virtual server specified family %s conflicts with server family", strvec_slot(strvec, 1));
return;
}
vs->af = af;
}
static void
vs_co_timeout_handler(const vector_t *strvec)
{
virtual_server_t *vs = list_last_entry(&check_data->vs, virtual_server_t, e_list);
unsigned long timer;
if (!read_timer(strvec, 1, &timer, 1, UINT_MAX, true)) {
report_config_error(CONFIG_GENERAL_ERROR, "virtual server connect_timeout %s invalid - ignoring", strvec_slot(strvec, 1));
return;
}
vs->connection_to = timer;
}
static void
vs_delay_handler(const vector_t *strvec)
{
virtual_server_t *vs = list_last_entry(&check_data->vs, virtual_server_t, e_list);
unsigned long delay;
if (read_timer(strvec, 1, &delay, 1, 0, true))
vs->delay_loop = delay;
else
report_config_error(CONFIG_GENERAL_ERROR, "virtual server delay loop '%s' invalid - ignoring", strvec_slot(strvec, 1));
}
static void
vs_delay_before_retry_handler(const vector_t *strvec)
{
virtual_server_t *vs = list_last_entry(&check_data->vs, virtual_server_t, e_list);
unsigned long delay;
if (read_timer(strvec, 1, &delay, 0, 0, true))
vs->delay_before_retry = delay;
else
report_config_error(CONFIG_GENERAL_ERROR, "virtual server delay before retry '%s' invalid - ignoring", strvec_slot(strvec, 1));
}
static void
vs_retry_handler(const vector_t *strvec)
{
virtual_server_t *vs = list_last_entry(&check_data->vs, virtual_server_t, e_list);
unsigned retry;
if (!read_unsigned_strvec(strvec, 1, &retry, 1, UINT32_MAX, false)) {
report_config_error(CONFIG_GENERAL_ERROR, "retry value invalid - %s", strvec_slot(strvec, 1));
return;
}
vs->retry = retry;
}
static void
vs_warmup_handler(const vector_t *strvec)
{
virtual_server_t *vs = list_last_entry(&check_data->vs, virtual_server_t, e_list);
unsigned long delay;
if (read_timer(strvec, 1, &delay, 0, 0, true))
vs->warmup = delay;
else
report_config_error(CONFIG_GENERAL_ERROR, "virtual server warmup '%s' invalid - ignoring", strvec_slot(strvec, 1));
}
static void
lbalgo_handler(const vector_t *strvec)
{
virtual_server_t *vs = list_last_entry(&check_data->vs, virtual_server_t, e_list);
const char *str = strvec_slot(strvec, 1);
int i;
/* Check valid scheduler name */
for (i = 0; lvs_schedulers[i] && strcmp(str, lvs_schedulers[i]); i++);
if (!lvs_schedulers[i] || strlen(str) >= sizeof(vs->sched)) {
report_config_error(CONFIG_GENERAL_ERROR, "Invalid lvs_scheduler '%s' - ignoring", strvec_slot(strvec, 1));
return;
}
strcpy(vs->sched, str);
}
static void
lbflags_handler(const vector_t *strvec)
{
virtual_server_t *vs = list_last_entry(&check_data->vs, virtual_server_t, e_list);
const char *str = strvec_slot(strvec, 0);
if (false) {}
#ifdef IP_VS_SVC_F_ONEPACKET
else if (!strcmp(str, "ops"))
vs->flags |= IP_VS_SVC_F_ONEPACKET;
#endif
#ifdef IP_VS_SVC_F_SCHED1 /* From Linux 3.11 */
else if (!strcmp(str, "flag-1"))
vs->flags |= IP_VS_SVC_F_SCHED1;
else if (!strcmp(str, "flag-2"))
vs->flags |= IP_VS_SVC_F_SCHED2;
else if (!strcmp(str, "flag-3"))
vs->flags |= IP_VS_SVC_F_SCHED3;
else if (!strcmp(vs->sched , "sh") )
{
/* sh-port and sh-fallback flags are relevant for sh scheduler only */
if (!strcmp(str, "sh-port") )
vs->flags |= IP_VS_SVC_F_SCHED_SH_PORT;
if (!strcmp(str, "sh-fallback"))
vs->flags |= IP_VS_SVC_F_SCHED_SH_FALLBACK;
}
else if (!strcmp(vs->sched , "mh") )
{
/* mh-port and mh-fallback flags are relevant for mh scheduler only */
if (!strcmp(str, "mh-port") )
vs->flags |= IP_VS_SVC_F_SCHED_MH_PORT;
if (!strcmp(str, "mh-fallback"))
vs->flags |= IP_VS_SVC_F_SCHED_MH_FALLBACK;
}
else
report_config_error(CONFIG_GENERAL_ERROR, "%s only applies to sh scheduler - ignoring", str);
#endif
}
static void
svr_forwarding_handler(real_server_t *rs, const vector_t *strvec, const char *s_type)
{
const char *str = strvec_slot(strvec, 1);
#ifdef _HAVE_IPVS_TUN_TYPE_
size_t i;
int tun_type = IP_VS_CONN_F_TUNNEL_TYPE_IPIP;
unsigned port = 0;
#ifdef _HAVE_IPVS_TUN_CSUM_
int csum = IP_VS_TUNNEL_ENCAP_FLAG_NOCSUM;
#endif
#endif
if (!strcmp(str, "NAT"))
rs->forwarding_method = IP_VS_CONN_F_MASQ;
else if (!strcmp(str, "DR"))
rs->forwarding_method = IP_VS_CONN_F_DROUTE;
else if (!strcmp(str, "TUN"))
rs->forwarding_method = IP_VS_CONN_F_TUNNEL;
else {
report_config_error(CONFIG_GENERAL_ERROR, "PARSER : unknown [%s] routing method for %s server.", str, s_type);
return;
}
#ifdef _HAVE_IPVS_TUN_TYPE_
for (i = 2; i < vector_size(strvec); i++) {
if (!strcmp(strvec_slot(strvec, i), "type")) {
if (vector_size(strvec) == i + 1) {
report_config_error(CONFIG_GENERAL_ERROR, "Missing tunnel type for %s server.", s_type);
return;
}
if (!strcmp(strvec_slot(strvec, i + 1), "ipip"))
tun_type = IP_VS_CONN_F_TUNNEL_TYPE_IPIP;
else if (!strcmp(strvec_slot(strvec, i + 1), "gue"))
tun_type = IP_VS_CONN_F_TUNNEL_TYPE_GUE;
#ifdef _HAVE_IPVS_TUN_GRE_
else if (!strcmp(strvec_slot(strvec, i + 1), "gre"))
tun_type = IP_VS_CONN_F_TUNNEL_TYPE_GRE;
#endif
else {
report_config_error(CONFIG_GENERAL_ERROR, "Unknown tunnel type %s for %s server.", strvec_slot(strvec, i + 1), s_type);
return;
}
i++;
} else if (!strcmp(strvec_slot(strvec, i), "port")) {
if (vector_size(strvec) == i + 1) {
report_config_error(CONFIG_GENERAL_ERROR, "Missing port for %s server gue tunnel.", s_type);
return;
}
if (!read_unsigned_strvec(strvec, i + 1, &port, 1, 65535, false)) {
report_config_error(CONFIG_GENERAL_ERROR, "Invalid gue tunnel port %s for %s server.", strvec_slot(strvec, i + 1), s_type);
return;
}
i++;
}
#ifdef _HAVE_IPVS_TUN_CSUM_
else if (!strcmp(strvec_slot(strvec, i), "nocsum"))
csum = IP_VS_TUNNEL_ENCAP_FLAG_NOCSUM;
else if (!strcmp(strvec_slot(strvec, i), "csum"))
csum = IP_VS_TUNNEL_ENCAP_FLAG_CSUM;
else if (!strcmp(strvec_slot(strvec, i), "remcsum"))
csum = IP_VS_TUNNEL_ENCAP_FLAG_REMCSUM;
#endif
else {
report_config_error(CONFIG_GENERAL_ERROR, "Invalid tunnel option %s for %s server.", strvec_slot(strvec, i), s_type);
return;
}
}
if ((tun_type == IP_VS_CONN_F_TUNNEL_TYPE_GUE) != (port != 0)) {
report_config_error(CONFIG_GENERAL_ERROR, "gue tunnels require port, otherwise cannot have port.");
return;
}
#ifdef _HAVE_IPVS_TUN_CSUM_
if (tun_type == IP_VS_CONN_F_TUNNEL_TYPE_IPIP && csum != IP_VS_TUNNEL_ENCAP_FLAG_NOCSUM) {
report_config_error(CONFIG_GENERAL_ERROR, "ipip tunnels do not support checksum option.");
return;
}
#endif
#ifdef _HAVE_IPVS_TUN_GRE_
if (tun_type == IP_VS_CONN_F_TUNNEL_TYPE_GRE && csum == IP_VS_TUNNEL_ENCAP_FLAG_REMCSUM) {
report_config_error(CONFIG_GENERAL_ERROR, "gre tunnels do not support remote checksum option.");
return;
}
#endif
#ifdef _HAVE_IPVS_TUN_TYPE_
rs->tun_type = tun_type;
rs->tun_port = htons(port);
#ifdef _HAVE_IPVS_TUN_CSUM_
rs->tun_flags = csum;
#endif
#endif
#endif
}
static void
vs_forwarding_handler(const vector_t *strvec)
{
virtual_server_t *vs = list_last_entry(&check_data->vs, virtual_server_t, e_list);
real_server_t rs = { // dummy for setting parameters. Ensure previous values are preserved
#ifdef _HAVE_IPVS_TUN_TYPE_
.tun_type = vs->tun_type,
.tun_port = vs->tun_port,
#ifdef _HAVE_IPVS_TUN_CSUM_
.tun_flags = vs->tun_flags,
#endif
#endif
.forwarding_method = vs->forwarding_method };
svr_forwarding_handler(&rs, strvec, "virtual");
vs->forwarding_method = rs.forwarding_method;
#ifdef _HAVE_IPVS_TUN_TYPE_
vs->tun_type = rs.tun_type;
vs->tun_port = rs.tun_port;
#ifdef _HAVE_IPVS_TUN_CSUM_
vs->tun_flags = rs.tun_flags;
#endif
#endif
}
static void
pto_handler(const vector_t *strvec)
{
virtual_server_t *vs = list_last_entry(&check_data->vs, virtual_server_t, e_list);
unsigned timeout;
if (vector_size(strvec) < 2) {
vs->persistence_timeout = IPVS_SVC_PERSISTENT_TIMEOUT;
return;
}
if (!read_unsigned_strvec(strvec, 1, &timeout, 1, LVS_MAX_TIMEOUT, false)) {
report_config_error(CONFIG_GENERAL_ERROR, "persistence_timeout invalid");
return;
}
vs->persistence_timeout = (uint32_t)timeout;
}
#ifdef _HAVE_PE_NAME_
static void
pengine_handler(const vector_t *strvec)
{
virtual_server_t *vs = list_last_entry(&check_data->vs, virtual_server_t, e_list);
const char *str = strvec_slot(strvec, 1);
size_t size = sizeof (vs->pe_name);
if (strlen(str) > size - 1)
report_config_error(CONFIG_GENERAL_ERROR, "persistence_name too long, truncating - %s", strvec_slot(strvec, 1));
strncpy(vs->pe_name, str, size - 1);
vs->pe_name[size - 1] = '\0';
}
#endif
static void
pgr_handler(const vector_t *strvec)
{
virtual_server_t *vs = list_last_entry(&check_data->vs, virtual_server_t, e_list);
struct in_addr addr;
uint16_t af = vs->af;
unsigned granularity;
if (af == AF_UNSPEC)
af = strchr(strvec_slot(strvec, 1), '.') ? AF_INET : AF_INET6;
if (af == AF_INET6) {
if (!read_unsigned_strvec(strvec, 1, &granularity, 1, 128, false)) {
report_config_error(CONFIG_GENERAL_ERROR, "Invalid IPv6 persistence_granularity specified - %s", strvec_slot(strvec, 1));
return;
}
vs->persistence_granularity = granularity;
} else {
if (!inet_aton(strvec_slot(strvec, 1), &addr)) {
report_config_error(CONFIG_GENERAL_ERROR, "Invalid IPv4 persistence_granularity specified - %s", strvec_slot(strvec, 1));
return;
}
/* It would seem sensible to force a solid netmask, but ipvsadm doesn't
and the kernel doesn't require it either. */
#if 0
/* Ensure the netmask is solid */
uint32_t haddr = ntohl(addr.s_addr);
while (!(haddr & 1))
haddr = (haddr >> 1) | 0x80000000;
if (haddr != 0xffffffff) {
report_config_error(CONFIG_GENERAL_ERROR, "IPv4 persistence_granularity netmask is not solid - %s", strvec_slot(strvec, 1));
return;
}
#endif
vs->persistence_granularity = addr.s_addr;
}
if (vs->af == AF_UNSPEC)
vs->af = af;
if (!vs->persistence_timeout)
vs->persistence_timeout = IPVS_SVC_PERSISTENT_TIMEOUT;
}
static void
proto_handler(const vector_t *strvec)
{
virtual_server_t *vs = list_last_entry(&check_data->vs, virtual_server_t, e_list);
const char *str = strvec_slot(strvec, 1);
if (!strcasecmp(str, "TCP"))
vs->service_type = IPPROTO_TCP;
else if (!strcasecmp(str, "SCTP"))
vs->service_type = IPPROTO_SCTP;
else if (!strcasecmp(str, "UDP"))
vs->service_type = IPPROTO_UDP;
else
report_config_error(CONFIG_GENERAL_ERROR, "Unknown protocol %s - ignoring", str);
}
static void
hasuspend_handler(__attribute__((unused)) const vector_t *strvec)
{
virtual_server_t *vs = list_last_entry(&check_data->vs, virtual_server_t, e_list);
vs->ha_suspend = true;
}
static void
vs_smtp_alert_handler(const vector_t *strvec)
{
virtual_server_t *vs = list_last_entry(&check_data->vs, virtual_server_t, e_list);
int res = true;
if (vector_size(strvec) >= 2) {
res = check_true_false(strvec_slot(strvec, 1));
if (res == -1) {
report_config_error(CONFIG_GENERAL_ERROR, "Invalid virtual_server smtp_alert parameter %s", strvec_slot(strvec, 1));
return;
}
}
vs->smtp_alert = res;
check_data->num_smtp_alert++;
}
static void
vs_virtualhost_handler(const vector_t *strvec)
{
virtual_server_t *vs = list_last_entry(&check_data->vs, virtual_server_t, e_list);
if (vector_size(strvec) < 2) {
report_config_error(CONFIG_GENERAL_ERROR, "virtual server virtualhost missing");
return;
}
vs->virtualhost = set_value(strvec);
}
/* Sorry Servers handlers */
static void
ssvr_handler(const vector_t *strvec)
{
alloc_ssvr(strvec_slot(strvec, 1), vector_size(strvec) >= 3 ? strvec_slot(strvec, 2) : NULL);
}
static void
ssvri_handler(__attribute__((unused)) const vector_t *strvec)
{
virtual_server_t *vs = list_last_entry(&check_data->vs, virtual_server_t, e_list);
if (vs->s_svr)
vs->s_svr->inhibit = true;
else
report_config_error(CONFIG_GENERAL_ERROR, "Ignoring sorry_server inhibit used before or without sorry_server");
}
static void
ss_forwarding_handler(const vector_t *strvec)
{
virtual_server_t *vs = list_last_entry(&check_data->vs, virtual_server_t, e_list);
if (vs->s_svr)
svr_forwarding_handler(vs->s_svr, strvec, "sorry");
else
report_config_error(CONFIG_GENERAL_ERROR, "sorry_server forwarding used without sorry_server");
}
/* Real Servers handlers */
static void
rs_handler(const vector_t *strvec)
{
alloc_rs(strvec_slot(strvec, 1), vector_size(strvec) >= 3 ? strvec_slot(strvec, 2) : NULL);
}
static void
rs_end_handler(void)
{
virtual_server_t *vs;
real_server_t *rs;
if (list_empty(&check_data->vs))
return;
vs = list_last_entry(&check_data->vs, virtual_server_t, e_list);
if (list_empty(&vs->rs))
return;
rs = list_last_entry(&vs->rs, real_server_t, e_list);
/* For tunnelled forwarding, the address families don't have to be the same, so
* long as the kernel supports IPVS_DEST_ATTR_ADDR_FAMILY */
#if HAVE_DECL_IPVS_DEST_ATTR_ADDR_FAMILY
if (rs->forwarding_method != IP_VS_CONN_F_TUNNEL)
#endif
{
if (vs->af == AF_UNSPEC)
vs->af = rs->addr.ss_family;
else if (vs->af != rs->addr.ss_family) {
report_config_error(CONFIG_GENERAL_ERROR, "Address family of virtual server and real server %s don't match - skipping real server.", inet_sockaddrtos(&rs->addr));
list_del_init(&rs->e_list);
FREE(rs);
}
}
}
static void
rs_weight_handler(const vector_t *strvec)
{
virtual_server_t *vs = list_last_entry(&check_data->vs, virtual_server_t, e_list);
real_server_t *rs = list_last_entry(&vs->rs, real_server_t, e_list);
unsigned weight;
if (!read_unsigned_strvec(strvec, 1, &weight, 0, IPVS_WEIGHT_MAX, true)) {
report_config_error(CONFIG_GENERAL_ERROR, "Real server weight %s is outside range 0-%d", strvec_slot(strvec, 1), IPVS_WEIGHT_MAX);
return;
}
rs->weight = weight;
rs->iweight = weight;
}
static void
rs_forwarding_handler(const vector_t *strvec)
{
virtual_server_t *vs = list_last_entry(&check_data->vs, virtual_server_t, e_list);
real_server_t *rs = list_last_entry(&vs->rs, real_server_t, e_list);
svr_forwarding_handler(rs, strvec, "real");
}
static void
uthreshold_handler(const vector_t *strvec)
{
virtual_server_t *vs = list_last_entry(&check_data->vs, virtual_server_t, e_list);
real_server_t *rs = list_last_entry(&vs->rs, real_server_t, e_list);
unsigned threshold;
if (!read_unsigned_strvec(strvec, 1, &threshold, 0, UINT_MAX, true)) {
report_config_error(CONFIG_GENERAL_ERROR, "Invalid real_server uthreshold '%s' - ignoring", strvec_slot(strvec, 1));
return;
}
rs->u_threshold = threshold;
}
static void
lthreshold_handler(const vector_t *strvec)
{
virtual_server_t *vs = list_last_entry(&check_data->vs, virtual_server_t, e_list);
real_server_t *rs = list_last_entry(&vs->rs, real_server_t, e_list);
unsigned threshold;
if (!read_unsigned_strvec(strvec, 1, &threshold, 0, UINT_MAX, true)) {
report_config_error(CONFIG_GENERAL_ERROR, "Invalid real_server lthreshold '%s' - ignoring", strvec_slot(strvec, 1));
return;
}
rs->l_threshold = threshold;
}
static void
vs_inhibit_handler(__attribute__((unused)) const vector_t *strvec)
{
virtual_server_t *vs = list_last_entry(&check_data->vs, virtual_server_t, e_list);
vs->inhibit = true;
}
static inline notify_script_t*
set_check_notify_script(__attribute__((unused)) const vector_t *strvec, const char *type)
{
return notify_script_init(0, type);
}
static void
notify_up_handler(const vector_t *strvec)
{
virtual_server_t *vs = list_last_entry(&check_data->vs, virtual_server_t, e_list);
real_server_t *rs = list_last_entry(&vs->rs, real_server_t, e_list);
if (rs->notify_up) {
report_config_error(CONFIG_GENERAL_ERROR, "(%s) notify_up script already specified - ignoring %s", vs->vsgname, strvec_slot(strvec,1));
return;
}
rs->notify_up = set_check_notify_script(strvec, "notify");
}
static void
notify_down_handler(const vector_t *strvec)
{
virtual_server_t *vs = list_last_entry(&check_data->vs, virtual_server_t, e_list);
real_server_t *rs = list_last_entry(&vs->rs, real_server_t, e_list);
if (rs->notify_down) {
report_config_error(CONFIG_GENERAL_ERROR, "(%s) notify_down script already specified - ignoring %s", vs->vsgname, strvec_slot(strvec,1));
return;
}
rs->notify_down = set_check_notify_script(strvec, "notify");
}
static void
rs_co_timeout_handler(const vector_t *strvec)
{
virtual_server_t *vs = list_last_entry(&check_data->vs, virtual_server_t, e_list);
real_server_t *rs = list_last_entry(&vs->rs, real_server_t, e_list);
unsigned long timer;
if (!read_timer(strvec, 1, &timer, 1, UINT_MAX, true)) {
report_config_error(CONFIG_GENERAL_ERROR, "real server connect_timeout %s invalid - ignoring", strvec_slot(strvec, 1));
return;
}
rs->connection_to = timer;
}
static void
rs_delay_handler(const vector_t *strvec)
{
virtual_server_t *vs = list_last_entry(&check_data->vs, virtual_server_t, e_list);
real_server_t *rs = list_last_entry(&vs->rs, real_server_t, e_list);
unsigned long delay;
if (read_timer(strvec, 1, &delay, 1, 0, true))
rs->delay_loop = delay;
else
report_config_error(CONFIG_GENERAL_ERROR, "real server delay_loop '%s' invalid - ignoring", strvec_slot(strvec, 1));
}
static void
rs_delay_before_retry_handler(const vector_t *strvec)
{
virtual_server_t *vs = list_last_entry(&check_data->vs, virtual_server_t, e_list);
real_server_t *rs = list_last_entry(&vs->rs, real_server_t, e_list);
unsigned long delay;
if (read_timer(strvec, 1, &delay, 0, 0, true))
rs->delay_before_retry = delay;
else
report_config_error(CONFIG_GENERAL_ERROR, "real server delay_before_retry '%s' invalid - ignoring", strvec_slot(strvec, 1));
}
static void
rs_retry_handler(const vector_t *strvec)
{
virtual_server_t *vs = list_last_entry(&check_data->vs, virtual_server_t, e_list);
real_server_t *rs = list_last_entry(&vs->rs, real_server_t, e_list);
unsigned retry;
if (!read_unsigned_strvec(strvec, 1, &retry, 1, UINT32_MAX, false)) {
report_config_error(CONFIG_GENERAL_ERROR, "retry value invalid - %s", strvec_slot(strvec, 1));
return;
}
rs->retry = (unsigned)retry;
}
static void
rs_warmup_handler(const vector_t *strvec)
{
virtual_server_t *vs = list_last_entry(&check_data->vs, virtual_server_t, e_list);
real_server_t *rs = list_last_entry(&vs->rs, real_server_t, e_list);
unsigned long delay;
if (read_timer(strvec, 1, &delay, 0, 0, true))
rs->warmup = delay;
else
report_config_error(CONFIG_GENERAL_ERROR, "real server warmup '%s' invalid - ignoring", strvec_slot(strvec, 1));
}
static void
rs_inhibit_handler(const vector_t *strvec)
{
virtual_server_t *vs = list_last_entry(&check_data->vs, virtual_server_t, e_list);
real_server_t *rs = list_last_entry(&vs->rs, real_server_t, e_list);
int res = true;
if (vector_size(strvec) >= 2) {
res = check_true_false(strvec_slot(strvec, 1));
if (res == -1) {
report_config_error(CONFIG_GENERAL_ERROR, "Invalid inhibit_on_failure parameter %s", strvec_slot(strvec, 1));
return;
}
}
rs->inhibit = res;
}
static void
rs_alpha_handler(const vector_t *strvec)
{
virtual_server_t *vs = list_last_entry(&check_data->vs, virtual_server_t, e_list);
real_server_t *rs = list_last_entry(&vs->rs, real_server_t, e_list);
int res = true;
if (vector_size(strvec) >= 2) {
res = check_true_false(strvec_slot(strvec, 1));
if (res == -1) {
report_config_error(CONFIG_GENERAL_ERROR, "Invalid alpha parameter %s", strvec_slot(strvec, 1));
return;
}
}
rs->alpha = res;
}
static void
rs_smtp_alert_handler(const vector_t *strvec)
{
virtual_server_t *vs = list_last_entry(&check_data->vs, virtual_server_t, e_list);
real_server_t *rs = list_last_entry(&vs->rs, real_server_t, e_list);
int res = true;
if (vector_size(strvec) >= 2) {
res = check_true_false(strvec_slot(strvec, 1));
if (res == -1) {
report_config_error(CONFIG_GENERAL_ERROR, "Invalid real_server smtp_alert parameter %s", strvec_slot(strvec, 1));
return;
}
}
rs->smtp_alert = res;
check_data->num_smtp_alert++;
}
static void
rs_virtualhost_handler(const vector_t *strvec)
{
virtual_server_t *vs = list_last_entry(&check_data->vs, virtual_server_t, e_list);
real_server_t *rs = list_last_entry(&vs->rs, real_server_t, e_list);
if (vector_size(strvec) < 2) {
report_config_error(CONFIG_GENERAL_ERROR, "real server virtualhost missing");
return;
}
rs->virtualhost = set_value(strvec);
}
static void
vs_alpha_handler(__attribute__((unused)) const vector_t *strvec)
{
virtual_server_t *vs = list_last_entry(&check_data->vs, virtual_server_t, e_list);
vs->alpha = true;
}
static void
omega_handler(__attribute__((unused)) const vector_t *strvec)
{
virtual_server_t *vs = list_last_entry(&check_data->vs, virtual_server_t, e_list);
vs->omega = true;
}
static void
quorum_up_handler(const vector_t *strvec)
{
virtual_server_t *vs = list_last_entry(&check_data->vs, virtual_server_t, e_list);
if (vs->notify_quorum_up) {
report_config_error(CONFIG_GENERAL_ERROR, "(%s) quorum_up script already specified - ignoring %s", vs->vsgname, strvec_slot(strvec,1));
return;
}
vs->notify_quorum_up = set_check_notify_script(strvec, "quorum");
}
static void
quorum_down_handler(const vector_t *strvec)
{
virtual_server_t *vs = list_last_entry(&check_data->vs, virtual_server_t, e_list);
if (vs->notify_quorum_down) {
report_config_error(CONFIG_GENERAL_ERROR, "(%s) quorum_down script already specified - ignoring %s", vs->vsgname, strvec_slot(strvec,1));
return;
}
vs->notify_quorum_down = set_check_notify_script(strvec, "quorum");
}
static void
quorum_handler(const vector_t *strvec)
{
virtual_server_t *vs = list_last_entry(&check_data->vs, virtual_server_t, e_list);
unsigned quorum;
if (!read_unsigned_strvec(strvec, 1, &quorum, 1, UINT_MAX, true)) {
report_config_error(CONFIG_GENERAL_ERROR, "Quorum %s must be in [1, %u]. Setting to 1.", strvec_slot(strvec, 1), UINT_MAX);
quorum = 1;
}
vs->quorum = quorum;
}
static void
hysteresis_handler(const vector_t *strvec)
{
virtual_server_t *vs = list_last_entry(&check_data->vs, virtual_server_t, e_list);
unsigned hysteresis;
if (!read_unsigned_strvec(strvec, 1, &hysteresis, 0, UINT_MAX, true)) {
report_config_error(CONFIG_GENERAL_ERROR, "Hysteresis %s must be in [0, %u] - ignoring", strvec_slot(strvec, 1), UINT_MAX);
return;
}
vs->hysteresis = hysteresis;
}
static void
vs_weight_handler(const vector_t *strvec)
{
virtual_server_t *vs = list_last_entry(&check_data->vs, virtual_server_t, e_list);
unsigned weight;
if (!read_unsigned_strvec(strvec, 1, &weight, 1, IPVS_WEIGHT_MAX, true)) {
report_config_error(CONFIG_GENERAL_ERROR, "Virtual server weight %s is outside range 1-%d", strvec_slot(strvec, 1), IPVS_WEIGHT_MAX);
return;
}
vs->weight = weight;
}
void
init_check_keywords(bool active)
{
/* SSL mapping */
install_keyword_root("SSL", &ssl_handler, active);
install_keyword("password", &sslpass_handler);
install_keyword("ca", &sslca_handler);
install_keyword("certificate", &sslcert_handler);
install_keyword("key", &sslkey_handler);
/* Virtual server mapping */
install_keyword_root("virtual_server_group", &vsg_handler, active);
install_keyword_root("virtual_server", &vs_handler, active);
install_root_end_handler(&vs_end_handler);
install_keyword("ip_family", &ip_family_handler);
install_keyword("retry", &vs_retry_handler);
install_keyword("delay_before_retry", &vs_delay_before_retry_handler);
install_keyword("warmup", &vs_warmup_handler);
install_keyword("connect_timeout", &vs_co_timeout_handler);
install_keyword("delay_loop", &vs_delay_handler);
install_keyword("inhibit_on_failure", &vs_inhibit_handler);
install_keyword("lb_algo", &lbalgo_handler);
install_keyword("lvs_sched", &lbalgo_handler);
install_keyword("hashed", &lbflags_handler);
#ifdef IP_VS_SVC_F_ONEPACKET
install_keyword("ops", &lbflags_handler);
#endif
#ifdef IP_VS_SVC_F_SCHED1
install_keyword("flag-1", &lbflags_handler);
install_keyword("flag-2", &lbflags_handler);
install_keyword("flag-3", &lbflags_handler);
install_keyword("sh-port", &lbflags_handler);
install_keyword("sh-fallback", &lbflags_handler);
install_keyword("mh-port", &lbflags_handler);
install_keyword("mh-fallback", &lbflags_handler);
#endif
install_keyword("lb_kind", &vs_forwarding_handler);
install_keyword("lvs_method", &vs_forwarding_handler);
#ifdef _HAVE_PE_NAME_
install_keyword("persistence_engine", &pengine_handler);
#endif
install_keyword("persistence_timeout", &pto_handler);
install_keyword("persistence_granularity", &pgr_handler);
install_keyword("protocol", &proto_handler);
install_keyword("ha_suspend", &hasuspend_handler);
install_keyword("smtp_alert", &vs_smtp_alert_handler);
install_keyword("virtualhost", &vs_virtualhost_handler);
/* Pool regression detection and handling. */
install_keyword("alpha", &vs_alpha_handler);
install_keyword("omega", &omega_handler);
install_keyword("quorum_up", &quorum_up_handler);
install_keyword("quorum_down", &quorum_down_handler);
install_keyword("quorum", &quorum_handler);
install_keyword("hysteresis", &hysteresis_handler);
install_keyword("weight", &vs_weight_handler);
/* Real server mapping */
install_keyword("sorry_server", &ssvr_handler);
install_keyword("sorry_server_inhibit", &ssvri_handler);
install_keyword("sorry_server_lvs_method", &ss_forwarding_handler);
install_keyword("real_server", &rs_handler);
install_sublevel();
install_keyword("weight", &rs_weight_handler);
install_keyword("lvs_method", &rs_forwarding_handler);
install_keyword("uthreshold", &uthreshold_handler);
install_keyword("lthreshold", <hreshold_handler);
install_keyword("inhibit_on_failure", &rs_inhibit_handler);
install_keyword("notify_up", ¬ify_up_handler);
install_keyword("notify_down", ¬ify_down_handler);
install_keyword("alpha", &rs_alpha_handler);
install_keyword("retry", &rs_retry_handler);
install_keyword("delay_before_retry", &rs_delay_before_retry_handler);
install_keyword("warmup", &rs_warmup_handler);
install_keyword("connect_timeout", &rs_co_timeout_handler);
install_keyword("delay_loop", &rs_delay_handler);
install_keyword("smtp_alert", &rs_smtp_alert_handler);
install_keyword("virtualhost", &rs_virtualhost_handler);
install_sublevel_end_handler(&rs_end_handler);
/* Checkers mapping */
install_checkers_keyword();
install_sublevel_end();
}
const vector_t *
check_init_keywords(void)
{
/* global definitions mapping */
init_global_keywords(reload);
init_check_keywords(true);
#ifdef _WITH_VRRP_
init_vrrp_keywords(false);
#endif
#ifdef _WITH_BFD_
init_bfd_keywords(true);
#endif
add_track_file_keywords(true);
return keywords;
}