/*
* 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: Healthcheckers dynamic data structure definition.
*
* 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 <stdint.h>
#include <stdio.h>
#include "check_data.h"
#include "check_api.h"
#include "check_misc.h"
#include "check_daemon.h"
#include "global_data.h"
#include "check_ssl.h"
#include "logger.h"
#include "utils.h"
#include "ipwrapper.h"
#include "parser.h"
#include "libipvs.h"
#include "keepalived_magic.h"
#ifdef _WITH_BFD_
#include "check_bfd.h"
#endif
/* global vars */
check_data_t *check_data = NULL;
check_data_t *old_check_data = NULL;
/* SSL facility functions */
ssl_data_t *
alloc_ssl(void)
{
ssl_data_t *ssl = (ssl_data_t *) MALLOC(sizeof(ssl_data_t));
return ssl;
}
void
free_ssl(void)
{
ssl_data_t *ssl;
if (!check_data || !check_data->ssl)
return;
ssl = check_data->ssl;
clear_ssl(ssl);
FREE_PTR(ssl->password);
FREE_PTR(ssl->cafile);
FREE_PTR(ssl->certfile);
FREE_PTR(ssl->keyfile);
FREE(ssl);
check_data->ssl = NULL;
}
static void
dump_ssl(FILE *fp)
{
ssl_data_t *ssl = check_data->ssl;
if (!ssl->password && !ssl->cafile && !ssl->certfile && !ssl->keyfile) {
conf_write(fp, " Using autogen SSL context");
return;
}
if (ssl->password)
conf_write(fp, " Password : %s", ssl->password);
if (ssl->cafile)
conf_write(fp, " CA-file : %s", ssl->cafile);
if (ssl->certfile)
conf_write(fp, " Certificate file : %s", ssl->certfile);
if (ssl->keyfile)
conf_write(fp, " Key file : %s", ssl->keyfile);
}
/* Virtual server group facility functions */
static void
free_vsg(void *data)
{
virtual_server_group_t *vsg = data;
FREE_PTR(vsg->gname);
free_list(&vsg->addr_range);
free_list(&vsg->vfwmark);
FREE(vsg);
}
static void
dump_vsg(FILE *fp, void *data)
{
virtual_server_group_t *vsg = data;
conf_write(fp, " ------< Virtual server group >------");
conf_write(fp, " Virtual Server Group = %s", vsg->gname);
dump_list(fp, vsg->addr_range);
dump_list(fp, vsg->vfwmark);
}
static void
free_vsg_entry(void *data)
{
FREE(data);
}
static void
dump_vsg_entry(FILE *fp, void *data)
{
virtual_server_group_entry_t *vsg_entry = data;
uint16_t start;
if (vsg_entry->is_fwmark)
conf_write(fp, " FWMARK = %u", vsg_entry->vfwmark);
else if (vsg_entry->range) {
start = vsg_entry->addr.ss_family == AF_INET ?
ntohl(((struct sockaddr_in*)&vsg_entry->addr)->sin_addr.s_addr) & 0xFF :
ntohs(((struct sockaddr_in6*)&vsg_entry->addr)->sin6_addr.s6_addr16[7]);
conf_write(fp,
vsg_entry->addr.ss_family == AF_INET ?
" VIP Range = %s-%d, VPORT = %d" :
" VIP Range = %s-%x, VPORT = %d",
inet_sockaddrtos(&vsg_entry->addr),
start + vsg_entry->range,
ntohs(inet_sockaddrport(&vsg_entry->addr)));
} else
conf_write(fp, " VIP = %s, VPORT = %d"
, inet_sockaddrtos(&vsg_entry->addr)
, ntohs(inet_sockaddrport(&vsg_entry->addr)));
}
void
alloc_vsg(char *gname)
{
size_t size = strlen(gname);
virtual_server_group_t *new;
new = (virtual_server_group_t *) MALLOC(sizeof(virtual_server_group_t));
new->gname = (char *) MALLOC(size + 1);
memcpy(new->gname, gname, size);
new->addr_range = alloc_list(free_vsg_entry, dump_vsg_entry);
new->vfwmark = alloc_list(free_vsg_entry, dump_vsg_entry);
list_add(check_data->vs_group, new);
}
void
alloc_vsg_entry(vector_t *strvec)
{
virtual_server_group_t *vsg = LIST_TAIL_DATA(check_data->vs_group);
virtual_server_group_entry_t *new;
virtual_server_group_entry_t *old;
uint32_t start;
element e;
char *port_str;
uint32_t range;
unsigned fwmark;
new = (virtual_server_group_entry_t *) MALLOC(sizeof(virtual_server_group_entry_t));
if (!strcmp(strvec_slot(strvec, 0), "fwmark")) {
if (!read_unsigned_strvec(strvec, 1, &fwmark, 0, UINT32_MAX, true)) {
report_config_error(CONFIG_GENERAL_ERROR, "(%s): fwmark '%s' must be in [0, %u] - ignoring", vsg->gname, FMT_STR_VSLOT(strvec, 1), UINT32_MAX);
FREE(new);
return;
}
new->vfwmark = fwmark;
new->is_fwmark = true;
list_add(vsg->vfwmark, new);
} else {
if (!inet_stor(strvec_slot(strvec, 0), &range)) {
FREE(new);
return;
}
new->range = (uint32_t)range;
if (vector_size(strvec) >= 2) {
/* Don't pass a port number of 0. This was added v2.0.7 to support legacy
* configuration since previously having no port wasn't allowed. */
port_str = strvec_slot(strvec, 1);
if (!port_str[strspn(port_str, "0")])
port_str = NULL;
}
else
port_str = NULL;
if (inet_stosockaddr(strvec_slot(strvec, 0), port_str, &new->addr)) {
report_config_error(CONFIG_GENERAL_ERROR, "Invalid virtual server group IP address%s %s%s%s - skipping", FMT_STR_VSLOT(strvec, 0),
port_str ? "/port" : "", port_str ? "/" : "", port_str ? port_str : "");
FREE(new);
return;
}
#ifndef LIBIPVS_USE_NL
if (new->addr.ss_family != AF_INET) {
report_config_error(CONFIG_GENERAL_ERROR, "IPVS does not support IPv6 in this build - skipping %s", FMT_STR_VSLOT(strvec, 0));
FREE(new);
return;
}
#endif
/* Ensure the address family matches any previously configured addresses */
if (!LIST_ISEMPTY(vsg->addr_range)) {
e = LIST_HEAD(vsg->addr_range);
old = ELEMENT_DATA(e);
if (old->addr.ss_family != new->addr.ss_family) {
report_config_error(CONFIG_GENERAL_ERROR, "Cannot mix IPv4 and IPv6 in virtual server group - %s", vsg->gname);
FREE(new);
return;
}
}
/* If no range specified, new->range == UINT32_MAX */
if (new->range == UINT32_MAX)
new->range = 0;
else {
if (new->addr.ss_family == AF_INET)
start = ntohl(((struct sockaddr_in *)&new->addr)->sin_addr.s_addr) & 0xFF;
else
start = ntohs(((struct sockaddr_in6 *)&new->addr)->sin6_addr.s6_addr16[7]);
if (start >= new->range) {
report_config_error(CONFIG_GENERAL_ERROR, "Address range end is not greater than address range start - %s - skipping", FMT_STR_VSLOT(strvec, 0));
FREE(new);
return;
}
new->range -= start;
}
new->is_fwmark = false;
list_add(vsg->addr_range, new);
}
}
/* Virtual server facility functions */
static void
free_vs(void *data)
{
virtual_server_t *vs = data;
FREE_PTR(vs->vsgname);
FREE_PTR(vs->virtualhost);
FREE_PTR(vs->s_svr);
free_list(&vs->rs);
free_notify_script(&vs->notify_quorum_up);
free_notify_script(&vs->notify_quorum_down);
FREE(vs);
}
static void
dump_vs(FILE *fp, void *data)
{
virtual_server_t *vs = data;
conf_write(fp, " ------< Virtual server >------");
if (vs->vsgname)
conf_write(fp, " VS GROUP = %s", FMT_VS(vs));
else if (vs->vfwmark)
conf_write(fp, " VS FWMARK = %u", vs->vfwmark);
else
conf_write(fp, " VS VIP = %s, VPORT = %d"
, inet_sockaddrtos(&vs->addr), ntohs(inet_sockaddrport(&vs->addr)));
if (vs->virtualhost)
conf_write(fp, " VirtualHost = %s", vs->virtualhost);
if (vs->af != AF_UNSPEC)
conf_write(fp, " Address family = inet%s", vs->af == AF_INET ? "" : "6");
conf_write(fp, " delay_loop = %lu, lvs_sched = %s", vs->delay_loop / TIMER_HZ, vs->sched);
conf_write(fp, " Hashed = %sabled", vs->flags & IP_VS_SVC_F_HASHED ? "en" : "dis");
#ifdef IP_VS_SVC_F_SCHED1
if (!strcmp(vs->sched, "sh"))
{
conf_write(fp, " sh-port = %sabled", vs->flags & IP_VS_SVC_F_SCHED_SH_PORT ? "en" : "dis");
conf_write(fp, " sh-fallback = %sabled", vs->flags & IP_VS_SVC_F_SCHED_SH_FALLBACK ? "en" : "dis");
}
else if (!strcmp(vs->sched, "mh"))
{
conf_write(fp, " mh-port = %sabled", vs->flags & IP_VS_SVC_F_SCHED_MH_PORT ? "en" : "dis");
conf_write(fp, " mh-fallback = %sabled", vs->flags & IP_VS_SVC_F_SCHED_MH_FALLBACK ? "en" : "dis");
}
else
{
conf_write(fp, " flag-1 = %sabled", vs->flags & IP_VS_SVC_F_SCHED1 ? "en" : "dis");
conf_write(fp, " flag-2 = %sabled", vs->flags & IP_VS_SVC_F_SCHED2 ? "en" : "dis");
conf_write(fp, " flag-3 = %sabled", vs->flags & IP_VS_SVC_F_SCHED3 ? "en" : "dis");
}
#endif
#ifdef IP_VS_SVC_F_ONEPACKET
conf_write(fp, " One packet scheduling = %sabled%s",
(vs->flags & IP_VS_SVC_F_ONEPACKET) ? "en" : "dis",
((vs->flags & IP_VS_SVC_F_ONEPACKET) && vs->service_type != IPPROTO_UDP) ? " (inactive due to not UDP)" : "");
#endif
if (vs->persistence_timeout)
conf_write(fp, " persistence timeout = %u", vs->persistence_timeout);
if (vs->persistence_granularity) {
if (vs->af == AF_INET6)
conf_write(fp, " persistence granularity = %d",
vs->persistence_granularity);
else
conf_write(fp, " persistence granularity = %s",
inet_ntop2(vs->persistence_granularity));
}
if (vs->service_type == IPPROTO_TCP)
conf_write(fp, " protocol = TCP");
else if (vs->service_type == IPPROTO_UDP)
conf_write(fp, " protocol = UDP");
else if (vs->service_type == IPPROTO_SCTP)
conf_write(fp, " protocol = SCTP");
else if (vs->service_type == 0)
conf_write(fp, " protocol = none");
else
conf_write(fp, " protocol = %d", vs->service_type);
conf_write(fp, " alpha is %s, omega is %s",
vs->alpha ? "ON" : "OFF", vs->omega ? "ON" : "OFF");
if (vs->retry != UINT_MAX)
conf_write(fp, " Retry count = %u" , vs->retry);
if (vs->delay_before_retry != ULONG_MAX)
conf_write(fp, " Retry delay = %lu" , vs->delay_before_retry / TIMER_HZ);
if (vs->warmup != ULONG_MAX)
conf_write(fp, " Warmup = %lu", vs->warmup / TIMER_HZ);
conf_write(fp, " Inhibit on failure is %s", vs->inhibit ? "ON" : "OFF");
conf_write(fp, " quorum = %u, hysteresis = %u", vs->quorum, vs->hysteresis);
if (vs->notify_quorum_up)
conf_write(fp, " Quorum up notify script = %s, uid:gid %d:%d",
cmd_str(vs->notify_quorum_up), vs->notify_quorum_up->uid, vs->notify_quorum_up->gid);
if (vs->notify_quorum_down)
conf_write(fp, " Quorum down notify script = %s, uid:gid %d:%d",
cmd_str(vs->notify_quorum_down), vs->notify_quorum_down->uid, vs->notify_quorum_down->gid);
if (vs->ha_suspend)
conf_write(fp, " Using HA suspend");
conf_write(fp, " Using smtp notification = %s", vs->smtp_alert ? "yes" : "no");
switch (vs->forwarding_method) {
case IP_VS_CONN_F_MASQ:
conf_write(fp, " default forwarding method = NAT");
break;
case IP_VS_CONN_F_DROUTE:
conf_write(fp, " default forwarding method = DR");
break;
case IP_VS_CONN_F_TUNNEL:
conf_write(fp, " default forwarding method = TUN");
break;
}
if (vs->s_svr) {
conf_write(fp, " sorry server = %s"
, FMT_RS(vs->s_svr, vs));
switch (vs->s_svr->forwarding_method) {
case IP_VS_CONN_F_MASQ:
conf_write(fp, " sorry server forwarding method = NAT");
break;
case IP_VS_CONN_F_DROUTE:
conf_write(fp, " sorry server forwarding method = DR");
break;
case IP_VS_CONN_F_TUNNEL:
conf_write(fp, " sorry server forwarding method = TUN");
break;
}
}
dump_list(fp, vs->rs);
}
void
alloc_vs(char *param1, char *param2)
{
size_t size;
virtual_server_t *new;
char *port_str;
unsigned fwmark;
new = (virtual_server_t *) MALLOC(sizeof(virtual_server_t));
new->af = AF_UNSPEC;
if (!strcmp(param1, "group")) {
size = strlen(param2);
new->vsgname = (char *) MALLOC(size + 1);
memcpy(new->vsgname, param2, size);
} else if (!strcmp(param1, "fwmark")) {
if (!read_unsigned(param2, &fwmark, 0, UINT32_MAX, true)) {
report_config_error(CONFIG_GENERAL_ERROR, "virtual server fwmark '%s' must be in [0, %u] - ignoring", param2, UINT32_MAX);
skip_block(true);
FREE(new);
return;
}
new->vfwmark = fwmark;
} else {
/* Don't pass a zero for port number to inet_stosockaddr. This was added in v2.0.7
* to support legacy configuration since previously having no port wasn't allowed. */
port_str = (param2 && param2[strspn(param2, "0")]) ? param2 : NULL;
if (inet_stosockaddr(param1, port_str, &new->addr)) {
report_config_error(CONFIG_GENERAL_ERROR, "Invalid virtual server IP address%s %s%s%s - skipping",
port_str ? "/port" : "", param1, port_str ? "/" : "", port_str ? port_str : "");
skip_block(true);
FREE(new);
return;
}
new->af = new->addr.ss_family;
#ifndef LIBIPVS_USE_NL
if (new->af != AF_INET) {
report_config_error(CONFIG_GENERAL_ERROR, "IPVS with IPv6 is not supported by this build");
FREE(new);
skip_block(true);
return;
}
#endif
}
new->virtualhost = NULL;
new->alpha = false;
new->omega = false;
new->notify_quorum_up = NULL;
new->notify_quorum_down = NULL;
new->quorum = 1;
new->hysteresis = 0;
new->quorum_state_up = true;
new->flags = 0;
new->forwarding_method = IP_VS_CONN_F_FWD_MASK; /* So we can detect if it has been set */
new->delay_loop = KEEPALIVED_DEFAULT_DELAY;
new->warmup = ULONG_MAX;
new->retry = UINT_MAX;
new->delay_before_retry = ULONG_MAX;
new->weight = 1;
new->smtp_alert = -1;
list_add(check_data->vs, new);
}
/* Sorry server facility functions */
void
alloc_ssvr(char *ip, char *port)
{
virtual_server_t *vs = LIST_TAIL_DATA(check_data->vs);
vs->s_svr = (real_server_t *) MALLOC(sizeof(real_server_t));
vs->s_svr->weight = 1;
vs->s_svr->iweight = 1;
vs->s_svr->forwarding_method = vs->forwarding_method;
if (inet_stosockaddr(ip, port, &vs->s_svr->addr)) {
report_config_error(CONFIG_GENERAL_ERROR, "Invalid sorry server IP address %s - skipping", ip);
FREE(vs->s_svr);
vs->s_svr = NULL;
return;
}
}
/* Real server facility functions */
static void
free_rs(void *data)
{
real_server_t *rs = data;
free_notify_script(&rs->notify_up);
free_notify_script(&rs->notify_down);
#ifdef _WITH_BFD_
free_list(&rs->tracked_bfds);
#endif
FREE_PTR(rs->virtualhost);
FREE(rs);
}
static void
dump_rs(FILE *fp, void *data)
{
real_server_t *rs = data;
conf_write(fp, " ------< Real server >------");
conf_write(fp, " RIP = %s, RPORT = %d, WEIGHT = %d"
, inet_sockaddrtos(&rs->addr)
, ntohs(inet_sockaddrport(&rs->addr))
, rs->weight);
switch (rs->forwarding_method) {
case IP_VS_CONN_F_MASQ:
conf_write(fp, " Forwarding method = NAT");
break;
case IP_VS_CONN_F_DROUTE:
conf_write(fp, " Forwarding method = DR");
break;
case IP_VS_CONN_F_TUNNEL:
conf_write(fp, " Forwarding method = TUN");
break;
}
conf_write(fp, " Alpha is %s", rs->alpha ? "ON" : "OFF");
conf_write(fp, " Delay loop = %lu" , rs->delay_loop / TIMER_HZ);
if (rs->retry != UINT_MAX)
conf_write(fp, " Retry count = %u" , rs->retry);
if (rs->delay_before_retry != ULONG_MAX)
conf_write(fp, " Retry delay = %lu" , rs->delay_before_retry / TIMER_HZ);
if (rs->warmup != ULONG_MAX)
conf_write(fp, " Warmup = %lu", rs->warmup / TIMER_HZ);
conf_write(fp, " Inhibit on failure is %s", rs->inhibit ? "ON" : "OFF");
if (rs->notify_up)
conf_write(fp, " RS up notify script = %s, uid:gid %d:%d",
cmd_str(rs->notify_up), rs->notify_up->uid, rs->notify_up->gid);
if (rs->notify_down)
conf_write(fp, " RS down notify script = %s, uid:gid %d:%d",
cmd_str(rs->notify_down), rs->notify_down->uid, rs->notify_down->gid);
if (rs->virtualhost)
conf_write(fp, " VirtualHost = %s", rs->virtualhost);
conf_write(fp, " Using smtp notification = %s", rs->smtp_alert ? "yes" : "no");
}
void
alloc_rs(char *ip, char *port)
{
virtual_server_t *vs = LIST_TAIL_DATA(check_data->vs);
real_server_t *new;
new = (real_server_t *) MALLOC(sizeof(real_server_t));
if (inet_stosockaddr(ip, port, &new->addr)) {
report_config_error(CONFIG_GENERAL_ERROR, "Invalid real server ip address/port %s/%s - skipping", ip, port);
skip_block(true);
FREE(new);
return;
}
#ifndef LIBIPVS_USE_NL
if (new->addr.ss_family != AF_INET) {
report_config_error(CONFIG_GENERAL_ERROR, "IPVS does not support IPv6 in this build - skipping %s/%s", ip, port);
skip_block(true);
FREE(new);
return;
}
#else
#if !HAVE_DECL_IPVS_DEST_ATTR_ADDR_FAMILY
if (vs->af != AF_UNSPEC && new->addr.ss_family != vs->af) {
report_config_error(CONFIG_GENERAL_ERROR, "Your kernel doesn't support mixed IPv4/IPv6 for virtual/real servers");
skip_block(true);
FREE(new);
return;
}
#endif
#endif
new->weight = INT_MAX;
new->forwarding_method = vs->forwarding_method;
new->alpha = -1;
new->delay_loop = ULONG_MAX;
new->warmup = ULONG_MAX;
new->retry = UINT_MAX;
new->delay_before_retry = ULONG_MAX;
new->virtualhost = NULL;
new->smtp_alert = -1;
// ??? alloc list in alloc_vs
if (!LIST_EXISTS(vs->rs))
vs->rs = alloc_list(free_rs, dump_rs);
list_add(vs->rs, new);
clear_dynamic_misc_check_flag();
}
#ifdef _WITH_BFD_
/* Track bfd dump */
static void
dump_checker_bfd(FILE *fp, void *track_data)
{
checker_tracked_bfd_t *cbfd = track_data;
conf_write(fp, " Checker Track BFD = %s", cbfd->bname);
// conf_write(fp, " Weight = %d", cbfd->weight);
conf_write(fp, " Tracking RS = %d", cbfd->tracking_rs ? LIST_SIZE(cbfd->tracking_rs) : 0);
if (cbfd->tracking_rs)
dump_list(fp, cbfd->tracking_rs);
}
static void
free_checker_bfd(void *track_data)
{
checker_tracked_bfd_t *cbfd = track_data;
FREE(cbfd->bname);
free_list(&cbfd->tracking_rs);
FREE(track_data);
}
#endif
/* data facility functions */
check_data_t *
alloc_check_data(void)
{
check_data_t *new;
new = (check_data_t *) MALLOC(sizeof(check_data_t));
new->vs = alloc_list(free_vs, dump_vs);
new->vs_group = alloc_list(free_vsg, dump_vsg);
#ifdef _WITH_BFD_
new->track_bfds = alloc_list(free_checker_bfd, dump_checker_bfd);
#endif
return new;
}
void
free_check_data(check_data_t *data)
{
free_list(&data->vs);
free_list(&data->vs_group);
#ifdef _WITH_BFD_
free_list(&data->track_bfds);
#endif
FREE(data);
}
void
dump_check_data(FILE *fp, check_data_t *data)
{
if (data->ssl) {
conf_write(fp, "------< SSL definitions >------");
dump_ssl(fp);
}
if (!LIST_ISEMPTY(data->vs)) {
conf_write(fp, "------< LVS Topology >------");
conf_write(fp, " System is compiled with LVS v%d.%d.%d",
NVERSION(IP_VS_VERSION_CODE));
if (!LIST_ISEMPTY(data->vs_group))
dump_list(fp, data->vs_group);
dump_list(fp, data->vs);
}
dump_checkers_queue(fp);
#ifdef _WITH_BFD_
if (!LIST_ISEMPTY(data->track_bfds)) {
conf_write(fp, "------< Checker track BFDs >------");
dump_list(fp, data->track_bfds);
}
#endif
}
char *
format_vs (virtual_server_t *vs)
{
/* alloc large buffer because of unknown length of vs->vsgname */
static char ret[512];
if (vs->vsgname)
snprintf (ret, sizeof (ret) - 1, "[%s]:%d"
, vs->vsgname
, ntohs(inet_sockaddrport(&vs->addr)));
else if (vs->vfwmark)
snprintf (ret, sizeof (ret) - 1, "FWM %u", vs->vfwmark);
else
snprintf(ret, sizeof(ret) - 1, "%s"
, inet_sockaddrtotrio(&vs->addr, vs->service_type));
return ret;
}
static void
check_check_script_security(void)
{
element e, e1;
virtual_server_t *vs;
real_server_t *rs;
int script_flags;
magic_t magic;
if (LIST_ISEMPTY(check_data->vs))
return;
magic = ka_magic_open();
script_flags = check_misc_script_security(magic);
for (e = LIST_HEAD(check_data->vs); e; ELEMENT_NEXT(e)) {
vs = ELEMENT_DATA(e);
script_flags |= check_notify_script_secure(&vs->notify_quorum_up, magic);
script_flags |= check_notify_script_secure(&vs->notify_quorum_down, magic);
for (e1 = LIST_HEAD(vs->rs); e1; ELEMENT_NEXT(e1)) {
rs = ELEMENT_DATA(e1);
script_flags |= check_notify_script_secure(&rs->notify_up, magic);
script_flags |= check_notify_script_secure(&rs->notify_down, magic);
}
}
if (global_data->notify_fifo.script)
script_flags |= check_notify_script_secure(&global_data->notify_fifo.script, magic);
if (global_data->lvs_notify_fifo.script)
script_flags |= check_notify_script_secure(&global_data->lvs_notify_fifo.script, magic);
if (!script_security && script_flags & SC_ISSCRIPT) {
report_config_error(CONFIG_SECURITY_ERROR, "SECURITY VIOLATION - check scripts are being executed but script_security not enabled.%s",
script_flags & SC_INSECURE ? " There are insecure scripts." : "");
}
if (magic)
ka_magic_close(magic);
}
bool validate_check_config(void)
{
element e, e1;
virtual_server_t *vs;
virtual_server_group_entry_t *vsge;
real_server_t *rs;
checker_t *checker;
element next;
using_ha_suspend = false;
LIST_FOREACH_NEXT(check_data->vs, vs, e, next) {
if (!vs->rs || LIST_ISEMPTY(vs->rs)) {
report_config_error(CONFIG_GENERAL_ERROR, "Virtual server %s has no real servers - ignoring", FMT_VS(vs));
free_list_element(check_data->vs, e);
continue;
}
/* Check that the quorum isn't higher than the number of real servers,
* otherwise we will never be able to come up. */
if (vs->quorum > LIST_SIZE(vs->rs)) {
report_config_error(CONFIG_GENERAL_ERROR, "Warning - quorum %1$d for %2$s exceeds number of real servers %3$d, reducing quorum to %3$d", vs->quorum, FMT_VS(vs), LIST_SIZE(vs->rs));
vs->quorum = LIST_SIZE(vs->rs);
}
/* Ensure that no virtual server hysteresis >= quorum */
if (vs->hysteresis >= vs->quorum) {
report_config_error(CONFIG_GENERAL_ERROR, "Virtual server %s: hysteresis %u >= quorum %u; setting hysteresis to %u",
FMT_VS(vs), vs->hysteresis, vs->quorum, vs->quorum -1);
vs->hysteresis = vs->quorum - 1;
}
/* Ensure that ha_suspend is not set for any virtual server using fwmarks */
if (vs->ha_suspend &&
(vs->vfwmark || (vs->vsg && !LIST_ISEMPTY(vs->vsg->vfwmark)))) {
report_config_error(CONFIG_GENERAL_ERROR, "Virtual server %s: cannot use ha_suspend with fwmarks - clearing ha_suspend", FMT_VS(vs));
vs->ha_suspend = false;
}
if (vs->ha_suspend)
using_ha_suspend = true;
/* If the virtual server is specified by address (rather than fwmark), make some further checks */
if ((vs->vsg && !LIST_ISEMPTY(vs->vsg->addr_range)) ||
(!vs->vsg && !vs->vfwmark)) {
/* Check protocol set */
if (!vs->service_type) {
/* If the protocol is 0, the kernel defaults to UDP, so set it explicitly */
log_message(LOG_INFO, "Virtual server %s: no protocol set - defaulting to UDP", FMT_VS(vs));
vs->service_type = IPPROTO_UDP;
}
#ifdef IP_VS_SVC_F_ONEPACKET
/* Check OPS not set for TCP or SCTP */
if (vs->flags & IP_VS_SVC_F_ONEPACKET &&
vs->service_type != IPPROTO_UDP) {
/* OPS is only valid for UDP, or with a firewall mark */
report_config_error(CONFIG_GENERAL_ERROR, "Virtual server %s: one packet scheduling requires UDP - resetting", FMT_VS(vs));
vs->flags &= ~(unsigned)IP_VS_SVC_F_ONEPACKET;
}
#endif
/* Check port specified for udp/tcp/sctp unless persistent */
if (!vs->persistence_timeout &&
!vs->vsg &&
((vs->addr.ss_family == AF_INET6 && !((struct sockaddr_in6 *)&vs->addr)->sin6_port) ||
(vs->addr.ss_family == AF_INET && !((struct sockaddr_in *)&vs->addr)->sin_port))) {
report_config_error(CONFIG_GENERAL_ERROR, "Virtual server %s: zero port only valid for persistent services - setting", FMT_VS(vs));
vs->persistence_timeout = IPVS_SVC_PERSISTENT_TIMEOUT;
}
}
/* If a virtual server group with addresses has persistence not set,
* make sure all the address blocks have a port, otherwise set
* persistence. */
if (!vs->persistence_timeout && vs->vsg) {
LIST_FOREACH(vs->vsg->addr_range, vsge, e1) {
if ((vsge->addr.ss_family == AF_INET6 && !((struct sockaddr_in6 *)&vsge->addr)->sin6_port) ||
(vsge->addr.ss_family == AF_INET && !((struct sockaddr_in *)&vsge->addr)->sin_port)) {
report_config_error(CONFIG_GENERAL_ERROR, "Virtual server %s: zero port only valid for persistent services - setting", FMT_VS(vs));
vs->persistence_timeout = IPVS_SVC_PERSISTENT_TIMEOUT;
break;
}
}
}
/* A virtual server using fwmarks will ignore any protocol setting, so warn if one is set */
if (vs->service_type &&
((vs->vsg && LIST_ISEMPTY(vs->vsg->addr_range) && LIST_ISEMPTY(vs->vsg->addr_range)) ||
(!vs->vsg && vs->vfwmark)))
report_config_error(CONFIG_GENERAL_ERROR, "Warning: Virtual server %s: protocol specified for fwmark - protocol will be ignored", FMT_VS(vs));
/* Check scheduler set */
if (!vs->sched[0]) {
log_message(LOG_INFO, "Virtual server %s: no scheduler set, setting default '%s'", FMT_VS(vs), IPVS_DEF_SCHED);
strcpy(vs->sched, IPVS_DEF_SCHED);
}
/* Set default values */
if (vs->smtp_alert == -1) {
if (global_data->smtp_alert_checker != -1)
vs->smtp_alert = global_data->smtp_alert_checker;
else if (global_data->smtp_alert != -1)
vs->smtp_alert = global_data->smtp_alert;
else
vs->smtp_alert = false;
}
/* Spin through all the real servers */
LIST_FOREACH(vs->rs, rs, e1) {
/* Set the forwarding method if necessary */
if (rs->forwarding_method == IP_VS_CONN_F_FWD_MASK) {
if (vs->forwarding_method == IP_VS_CONN_F_FWD_MASK) {
log_message(LOG_INFO, "Virtual server %s: no forwarding method set, setting default NAT", FMT_VS(vs));
vs->forwarding_method = IP_VS_CONN_F_MASQ;
}
rs->forwarding_method = vs->forwarding_method;
}
/* Take default values from virtual server */
if (rs->alpha == -1)
rs->alpha = vs->alpha;
if (rs->inhibit == -1)
rs->inhibit = vs->inhibit;
if (rs->retry == UINT_MAX)
rs->retry = vs->retry;
if (rs->delay_loop == ULONG_MAX)
rs->delay_loop = vs->delay_loop;
if (rs->warmup == ULONG_MAX)
rs->warmup = vs->warmup;
if (rs->delay_before_retry == ULONG_MAX)
rs->delay_before_retry = vs->delay_before_retry;
if (rs->weight == INT_MAX) {
rs->weight = vs->weight;
rs->iweight = rs->weight;
}
if (rs->smtp_alert == -1) {
if (global_data->smtp_alert_checker != -1)
rs->smtp_alert = global_data->smtp_alert_checker;
else if (global_data->smtp_alert != -1)
rs->smtp_alert = global_data->smtp_alert;
else {
/* This is inconsistent with the defaults for other smtp_alerts
* in order to maintain backwards compatibility */
rs->smtp_alert = true;
}
}
}
}
LIST_FOREACH(checkers_queue, checker, e)
{
/* Ensure any checkers that don't have ha_suspend set are enabled */
if (!checker->vs->ha_suspend)
checker->enabled = true;
/* Take default values from real server */
if (checker->alpha == -1)
checker->alpha = checker->rs->alpha;
if (checker->launch) {
if (checker->retry == UINT_MAX)
checker->retry = checker->rs->retry != UINT_MAX ? checker->rs->retry : checker->default_retry;
if (checker->delay_loop == ULONG_MAX)
checker->delay_loop = checker->rs->delay_loop;
if (checker->warmup == ULONG_MAX)
checker->warmup = checker->rs->warmup != ULONG_MAX ? checker->rs->warmup : checker->delay_loop;
if (checker->delay_before_retry == ULONG_MAX) {
checker->delay_before_retry =
checker->rs->delay_before_retry != ULONG_MAX ?
checker->rs->delay_before_retry :
checker->default_delay_before_retry ?
checker->default_delay_before_retry :
checker->delay_loop;
}
}
/* In Alpha mode also mark the checker as failed, unless we are reloading and it has already run. */
if (!checker->has_run) {
if (checker->alpha) {
set_checker_state(checker, false);
UNSET_ALIVE(checker->rs);
} else {
/* For non alpha mode, one failure is enough initially */
checker->retry_it = checker->retry;
}
}
}
/* Add the FIFO name to the end of the parameter list */
if (global_data->notify_fifo.script)
add_script_param(global_data->notify_fifo.script, global_data->notify_fifo.name);
if (global_data->lvs_notify_fifo.script)
add_script_param(global_data->lvs_notify_fifo.script, global_data->lvs_notify_fifo.name);
// ??? This should probably be done in check_daemon after clear_diff_services()
set_quorum_states();
check_check_script_security();
return true;
}