/*
* Soft: Vrrpd is an implementation of VRRPv2 as specified in rfc2338.
* VRRP is a protocol which elect a master server on a LAN. If the
* master fails, a backup server takes over.
* The original implementation has been made by jerome etienne.
*
* Part: Output running VRRP state information in JSON format
*
* Author: Alexandre Cassen, <acassen@gmail.com>
* Damien Clabaut, <Damien.Clabaut@corp.ovh.com>
*
* 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-2019 Alexandre Cassen, <acassen@gmail.com>
*/
#include "config.h"
#include "vrrp_json.h"
#include <errno.h>
#include <stdio.h>
#include "vrrp.h"
#include "vrrp_track.h"
#include "list_head.h"
#include "vrrp_data.h"
#include "vrrp_ipaddress.h"
#include "vrrp_iproute.h"
#include "vrrp_iprule.h"
#include "logger.h"
#include "timer.h"
#include "utils.h"
#include "json_writer.h"
static inline double
timeval_to_double(const timeval_t *t)
{
/* The casts are necessary to avoid conversion warnings */
return t->tv_sec + t->tv_usec / TIMER_HZ_DOUBLE;
}
static int
vrrp_json_script_dump(json_writer_t *wr, const char *prop, notify_script_t *script)
{
if (!script)
return -1;
jsonw_string_field(wr, prop, cmd_str(script));
return 0;
}
static int
vrrp_json_ip_dump(json_writer_t *wr, list_head_t *e)
{
ip_address_t *ipaddr = list_entry(e, ip_address_t, e_list);
char buf[256];
format_ipaddress(ipaddr, buf, sizeof(buf));
jsonw_string(wr, buf);
return 0;
}
#ifdef _HAVE_FIB_ROUTING_
static int
vrrp_json_vroute_dump(json_writer_t *wr, list_head_t *e)
{
ip_route_t *iproute = list_entry(e, ip_route_t, e_list);
char buf[256];
format_iproute(iproute, buf, sizeof(buf));
jsonw_string(wr, buf);
return 0;
}
static int
vrrp_json_vrule_dump(json_writer_t *wr, list_head_t *e)
{
ip_rule_t *iprule = list_entry(e, ip_rule_t, e_list);
char buf[256];
format_iprule(iprule, buf, sizeof(buf));
jsonw_string(wr, buf);
return 0;
}
#endif
static int
vrrp_json_track_ifp_dump(json_writer_t *wr, list_head_t *e)
{
tracked_if_t *tip = list_entry(e, tracked_if_t, e_list);
interface_t *ifp = tip->ifp;
jsonw_string(wr, ifp->ifname);
return 0;
}
static int
vrrp_json_track_script_dump(json_writer_t *wr, list_head_t *e)
{
tracked_sc_t *tsc = list_entry(e, tracked_sc_t, e_list);
vrrp_script_t *vscript = tsc->scr;
jsonw_string(wr, cmd_str(&vscript->script));
return 0;
}
static int
vrrp_json_array_dump(json_writer_t *wr, const char *prop, list_head_t *l,
int (*func) (json_writer_t *, list_head_t *))
{
list_head_t *e;
if (list_empty(l))
return -1;
jsonw_name(wr, prop);
jsonw_start_array(wr);
list_for_each(e, l)
(*func) (wr, e);
jsonw_end_array(wr);
return 0;
}
static int
vrrp_json_auth_dump(json_writer_t *wr, const char *prop, vrrp_t *vrrp)
{
char buf[256];
if (!vrrp->auth_type)
return -1;
memcpy(buf, vrrp->auth_data, sizeof(vrrp->auth_data));
buf[sizeof(vrrp->auth_data)] = 0;
jsonw_string_field(wr, prop, buf);
return 0;
}
static int
vrrp_json_data_dump(json_writer_t *wr, vrrp_t *vrrp)
{
/* data object */
jsonw_name(wr, "data");
jsonw_start_object(wr);
/* Global instance related */
jsonw_string_field(wr, "iname", vrrp->iname);
jsonw_uint_field(wr, "dont_track_primary", vrrp->dont_track_primary);
jsonw_uint_field(wr, "skip_check_adv_addr", vrrp->skip_check_adv_addr);
jsonw_uint_field(wr, "strict_mode", vrrp->strict_mode);
#ifdef _HAVE_VRRP_VMAC_
jsonw_string_field(wr, "vmac_ifname", vrrp->vmac_ifname);
#endif
if (vrrp->ifp)
jsonw_string_field(wr, "ifp_ifname", vrrp->ifp->ifname);
jsonw_uint_field(wr, "master_priority", vrrp->master_priority);
jsonw_float_field_fmt(wr, "last_transition", "%f", timeval_to_double(&vrrp->last_transition));
jsonw_float_field(wr, "garp_delay", vrrp->garp_delay / TIMER_HZ_FLOAT);
jsonw_uint_field(wr, "garp_refresh", vrrp->garp_refresh.tv_sec);
jsonw_uint_field(wr, "garp_rep", vrrp->garp_rep);
jsonw_uint_field(wr, "garp_refresh_rep", vrrp->garp_refresh_rep);
jsonw_uint_field(wr, "garp_lower_prio_delay", vrrp->garp_lower_prio_delay / TIMER_HZ);
jsonw_uint_field(wr, "garp_lower_prio_rep", vrrp->garp_lower_prio_rep);
jsonw_uint_field(wr, "lower_prio_no_advert", vrrp->lower_prio_no_advert);
jsonw_uint_field(wr, "higher_prio_send_advert", vrrp->higher_prio_send_advert);
jsonw_uint_field(wr, "vrid", vrrp->vrid);
jsonw_uint_field(wr, "base_priority", vrrp->base_priority);
jsonw_uint_field(wr, "effective_priority", vrrp->effective_priority);
jsonw_bool_field(wr, "vipset", vrrp->vipset);
jsonw_bool_field(wr, "promote_secondaries", vrrp->promote_secondaries);
jsonw_float_field(wr, "adver_int", vrrp->adver_int / TIMER_HZ_FLOAT);
jsonw_float_field(wr, "master_adver_int", vrrp->master_adver_int / TIMER_HZ_FLOAT);
#ifdef _WITH_FIREWALL_
jsonw_uint_field(wr, "accept", vrrp->accept);
#endif
jsonw_bool_field(wr, "nopreempt", vrrp->nopreempt);
jsonw_uint_field(wr, "preempt_delay", vrrp->preempt_delay / TIMER_HZ);
jsonw_uint_field(wr, "state", vrrp->state);
jsonw_uint_field(wr, "wantstate", vrrp->wantstate);
jsonw_uint_field(wr, "version", vrrp->version);
jsonw_bool_field(wr, "smtp_alert", vrrp->smtp_alert);
jsonw_bool_field(wr, "notify_deleted", vrrp->notify_deleted);
/* Script related */
vrrp_json_script_dump(wr, "script_backup", vrrp->script_backup);
vrrp_json_script_dump(wr, "script_master", vrrp->script_master);
vrrp_json_script_dump(wr, "script_fault", vrrp->script_fault);
vrrp_json_script_dump(wr, "script_stop", vrrp->script_stop);
vrrp_json_script_dump(wr, "script_deleted", vrrp->script_deleted);
vrrp_json_script_dump(wr, "script", vrrp->script);
vrrp_json_script_dump(wr, "script_master_rx_lower_pri"
, vrrp->script_master_rx_lower_pri);
/* Virtual related */
vrrp_json_array_dump(wr, "vips", &vrrp->vip, vrrp_json_ip_dump);
vrrp_json_array_dump(wr, "evips", &vrrp->evip, vrrp_json_ip_dump);
#ifdef _HAVE_FIB_ROUTING_
vrrp_json_array_dump(wr, "vroutes", &vrrp->vroutes, vrrp_json_vroute_dump);
vrrp_json_array_dump(wr, "vrules", &vrrp->vrules, vrrp_json_vrule_dump);
#endif
/* Tracking related */
vrrp_json_array_dump(wr, "track_ifp", &vrrp->track_ifp, vrrp_json_track_ifp_dump);
vrrp_json_array_dump(wr, "track_script", &vrrp->track_script, vrrp_json_track_script_dump);
#ifdef _WITH_VRRP_AUTH_
jsonw_uint_field(wr, "auth_type", vrrp->auth_type);
vrrp_json_auth_dump(wr, "auth_data", vrrp);
#endif
jsonw_end_object(wr);
return 0;
}
static int
vrrp_json_stats_dump(json_writer_t *wr, vrrp_t *vrrp)
{
vrrp_stats *stats = vrrp->stats;
if (!stats)
return -1;
/* data object */
jsonw_name(wr, "stats");
jsonw_start_object(wr);
jsonw_uint_field(wr, "advert_rcvd", stats->advert_rcvd);
jsonw_uint_field(wr, "advert_sent", stats->advert_sent);
jsonw_uint_field(wr, "become_master", stats->become_master);
jsonw_uint_field(wr, "release_master", stats->release_master);
jsonw_uint_field(wr, "packet_len_err", stats->packet_len_err);
jsonw_uint_field(wr, "advert_interval_err", stats->advert_interval_err);
jsonw_uint_field(wr, "ip_ttl_err", stats->ip_ttl_err);
jsonw_uint_field(wr, "invalid_type_rcvd", stats->invalid_type_rcvd);
jsonw_uint_field(wr, "addr_list_err", stats->addr_list_err);
jsonw_uint_field(wr, "invalid_authtype", stats->invalid_authtype);
#ifdef _WITH_VRRP_AUTH_
jsonw_uint_field(wr, "authtype_mismatch", stats->authtype_mismatch);
jsonw_uint_field(wr, "auth_failure", stats->auth_failure);
#endif
jsonw_uint_field(wr, "pri_zero_rcvd", stats->pri_zero_rcvd);
jsonw_uint_field(wr, "pri_zero_sent", stats->pri_zero_sent);
jsonw_end_object(wr);
return 0;
}
/*
* Split dump function for future purpose
* this offer generic integration for mapping
* socket fd to a FILE stream.
*/
static int
vrrp_json_dump(FILE *fp)
{
json_writer_t *wr;
vrrp_t *vrrp;
wr = jsonw_new(fp);
jsonw_start_array(wr);
list_for_each_entry(vrrp, &vrrp_data->vrrp, e_list) {
jsonw_start_object(wr);
vrrp_json_data_dump(wr, vrrp);
vrrp_json_stats_dump(wr, vrrp);
jsonw_end_object(wr);
}
jsonw_end_array(wr);
jsonw_destroy(&wr);
return 0;
}
void
vrrp_print_json(void)
{
FILE *fp;
if (list_empty(&vrrp_data->vrrp))
return;
fp = fopen_safe("/tmp/keepalived.json", "w");
if (!fp) {
log_message(LOG_INFO, "Can't open /tmp/keepalived.json (%d: %m)", errno);
return;
}
vrrp_json_dump(fp);
fclose(fp);
}