/*
* 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: Routing table names parser/reader. Place into the dynamic
* data structure representation the table names and ids.
*
* 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 <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <linux/rtnetlink.h>
#include "list.h"
#include "memory.h"
#include "logger.h"
#include "parser.h"
#include "rttables.h"
#define IPROUTE2_DIR "/etc/iproute2/"
#ifdef _HAVE_FIB_ROUTING_
#define RT_TABLES_FILE IPROUTE2_DIR "rt_tables"
#define RT_DSFIELD_FILE IPROUTE2_DIR "rt_dsfield"
#define RT_REALMS_FILE IPROUTE2_DIR "rt_realms"
#define RT_PROTOS_FILE IPROUTE2_DIR "rt_protos"
#if HAVE_DECL_FRA_SUPPRESS_IFGROUP
#define RT_GROUPS_FILE IPROUTE2_DIR "group"
#endif
#endif
#define RT_SCOPES_FILE IPROUTE2_DIR "rt_scopes"
struct rt_entry {
unsigned int id;
char *name;
} ;
typedef struct rt_entry rt_entry_t;
#ifdef _HAVE_FIB_ROUTING_
static rt_entry_t rtntypes[] = {
{ RTN_LOCAL, "local"},
{ RTN_NAT, "nat"},
{ RTN_BROADCAST, "broadcast"},
{ RTN_BROADCAST, "brd"},
{ RTN_ANYCAST, "anycast"},
{ RTN_MULTICAST, "multicast"},
{ RTN_PROHIBIT, "prohibit"},
{ RTN_UNREACHABLE, "unreachable"},
{ RTN_BLACKHOLE, "blackhole"},
{ RTN_XRESOLVE, "xresolve"},
{ RTN_UNICAST, "unicast"},
{ RTN_THROW, "throw"},
{ 0, NULL},
};
static rt_entry_t rtprot_default[] = {
{ RTPROT_UNSPEC, "none"},
{ RTPROT_REDIRECT, "redirect"},
{ RTPROT_KERNEL, "kernel"},
{ RTPROT_BOOT, "boot"},
{ RTPROT_STATIC, "static"},
{ RTPROT_GATED, "gated"},
{ RTPROT_RA, "ra"},
{ RTPROT_MRT, "mrt"},
{ RTPROT_ZEBRA, "zebra"},
{ RTPROT_BIRD, "bird"},
#ifdef RTPROT_BABEL /* Since Linux 3.19 */
{ RTPROT_BABEL, "babel"},
#endif
{ RTPROT_DNROUTED, "dnrouted"},
{ RTPROT_XORP, "xorp"},
{ RTPROT_NTK, "ntk"},
{ RTPROT_DHCP, "dhcp"},
{ 0, NULL},
};
static rt_entry_t rttable_default[] = {
{ RT_TABLE_DEFAULT, "default"},
{ RT_TABLE_MAIN, "main"},
{ RT_TABLE_LOCAL, "local"},
{ 0, NULL},
};
#endif
static rt_entry_t rtscope_default[] = {
{ RT_SCOPE_UNIVERSE, "global"},
{ RT_SCOPE_NOWHERE, "nowhere"},
{ RT_SCOPE_HOST, "host"},
{ RT_SCOPE_LINK, "link"},
{ RT_SCOPE_SITE, "site"},
{ 0, NULL},
};
#define MAX_RT_BUF 128
#ifdef _HAVE_FIB_ROUTING_
static list rt_tables;
static list rt_dsfields;
#if HAVE_DECL_FRA_SUPPRESS_IFGROUP
static list rt_groups;
#endif
static list rt_realms;
static list rt_protos;
#endif
static list rt_scopes;
static char ret_buf[11]; /* uint32_t in decimal */
static void
read_file(const char* file_name, list *l, uint32_t max)
{
FILE *fp;
rt_entry_t *rte;
vector_t *strvec = NULL;
char buf[MAX_RT_BUF];
unsigned long id;
char *number;
char *endptr;
fp = fopen(file_name, "r");
if (!fp)
return;
while (fgets(buf, MAX_RT_BUF, fp)) {
strvec = alloc_strvec(buf);
if (!strvec)
continue;
if (vector_size(strvec) != 2) {
free_strvec(strvec);
continue;
}
rte = MALLOC(sizeof(rt_entry_t));
if (!rte) {
free_strvec(strvec);
goto err;
}
number = strvec_slot(strvec, 0);
number += strspn(number, " \t");
id = strtoul(number, &endptr, 0);
if (*number == '-' || number == endptr || *endptr || id > max) {
FREE(rte);
free_strvec(strvec);
continue;
}
rte->id = (unsigned)id;
rte->name = MALLOC(strlen(FMT_STR_VSLOT(strvec, 1)) + 1);
if (!rte->name) {
FREE(rte);
free_strvec(strvec);
goto err;
}
strcpy(rte->name, FMT_STR_VSLOT(strvec, 1));
list_add(*l, rte);
free_strvec(strvec);
}
fclose(fp);
return;
err:
fclose(fp);
if (strvec)
free_strvec(strvec);
free_list(l);
return;
}
void
clear_rt_names(void)
{
#ifdef _HAVE_FIB_ROUTING_
free_list(&rt_tables);
free_list(&rt_dsfields);
#if HAVE_DECL_FRA_SUPPRESS_IFGROUP
free_list(&rt_groups);
#endif
free_list(&rt_realms);
free_list(&rt_protos);
#endif
free_list(&rt_scopes);
}
static void
free_rt_entry(void *e)
{
rt_entry_t *rte = (rt_entry_t*)e;
if (rte->name)
FREE(rte->name);
FREE(rte);
}
static void
dump_rt_entry(FILE *fp, void *e)
{
rt_entry_t *rte = (rt_entry_t *)e;
conf_write(fp, "rt_table %u, name %s", rte->id, rte->name);
}
static void
add_default(list *l, const struct rt_entry* default_list)
{
bool found;
rt_entry_t *rte;
element e;
for (;default_list->name; default_list++) {
for (e = LIST_HEAD(*l), found = false; e; ELEMENT_NEXT(e)) {
rte = ELEMENT_DATA(e);
if (rte->id == default_list->id) {
found = true;
break;
}
}
if (found)
continue;
rte = MALLOC(sizeof(rt_entry_t));
rte->name = MALLOC(strlen(default_list->name) + 1);
if (!rte->name) {
FREE(rte);
return;
}
strcpy(rte->name, default_list->name);
rte->id = default_list->id;
list_add(*l, rte);
}
}
static void
initialise_list(list *l, const char *file_name, const struct rt_entry *default_list, uint32_t max)
{
if (*l)
return;
*l = alloc_list(free_rt_entry, dump_rt_entry);
if (!*l)
return;
read_file(file_name, l, max);
if (default_list)
add_default(l, default_list);
}
static bool
find_entry(const char *name, unsigned int *id, list *l, const char* file_name, const struct rt_entry* default_list, uint32_t max)
{
element e;
char *endptr;
unsigned long l_id;
l_id = strtoul(name, &endptr, 0);
*id = (unsigned int)l_id;
if (endptr != name && *endptr == '\0')
return (*id <= max);
if (!(*l))
initialise_list(l, file_name, default_list, max);
if (LIST_ISEMPTY(*l))
return false;
for (e = LIST_HEAD(*l); e; ELEMENT_NEXT(e)) {
rt_entry_t *rte = ELEMENT_DATA(e);
if (!strcmp(rte->name, name)) {
*id = rte->id;
return true;
}
}
return false;
}
#ifdef _HAVE_FIB_ROUTING_
bool
find_rttables_table(const char *name, uint32_t *id)
{
return find_entry(name, id, &rt_tables, RT_TABLES_FILE, rttable_default, RT_TABLE_MAX);
}
bool
find_rttables_dsfield(const char *name, uint8_t *id)
{
uint32_t val;
bool ret;
ret = find_entry(name, &val, &rt_dsfields, RT_DSFIELD_FILE, NULL, 255);
*id = val & 0xff;
return ret;
}
#if HAVE_DECL_FRA_SUPPRESS_IFGROUP
bool
find_rttables_group(const char *name, uint32_t *id)
{
return find_entry(name, id, &rt_groups, RT_GROUPS_FILE, NULL, INT32_MAX);
}
#endif
bool
find_rttables_realms(const char *name, uint32_t *id)
{
return find_entry(name, id, &rt_realms, RT_REALMS_FILE, NULL, 255);
}
bool
find_rttables_proto(const char *name, uint8_t *id)
{
uint32_t val;
bool ret;
ret = find_entry(name, &val, &rt_protos, RT_PROTOS_FILE, rtprot_default, 255);
*id = val & 0xff;
return ret;
}
bool
find_rttables_rtntype(const char *str, uint8_t *id)
{
char *end;
unsigned long res;
int i;
for (i = 0; rtntypes[i].name; i++) {
if (!strcmp(str, rtntypes[i].name)) {
*id = (uint8_t)rtntypes[i].id;
return true;
}
}
res = strtoul(str, &end, 0);
if (*end || res > 255 || str[0] == '-')
return false;
*id = (uint8_t)res;
return true;
}
#endif
static const char *
get_entry(unsigned int id, list* l, const char* file_name, const struct rt_entry* default_list, uint32_t max)
{
element e;
if (!(*l))
initialise_list(l, file_name, default_list, max);
if (!LIST_ISEMPTY(*l)) {
for (e = LIST_HEAD(*l); e; ELEMENT_NEXT(e)) {
rt_entry_t *rte = ELEMENT_DATA(e);
if (rte->id == id)
return rte->name;
}
}
snprintf(ret_buf, sizeof(ret_buf), "%u", id);
return ret_buf;
}
#ifdef _HAVE_FIB_ROUTING_
#if HAVE_DECL_FRA_SUPPRESS_IFGROUP
const char *
get_rttables_group(uint32_t id)
{
return get_entry(id, &rt_groups, RT_GROUPS_FILE, NULL, INT32_MAX);
}
#endif
const char *
get_rttables_rtntype(uint8_t val)
{
int i;
for (i = 0; rtntypes[i].name; i++) {
if (val == rtntypes[i].id)
return rtntypes[i].name;
}
snprintf(ret_buf, sizeof(ret_buf), "%u", val);
return ret_buf;
}
#endif
bool
find_rttables_scope(const char *name, uint8_t *id)
{
uint32_t val;
bool ret;
ret = find_entry(name, &val, &rt_scopes, RT_SCOPES_FILE, rtscope_default, 255);
*id = val & 0xff;
return ret;
}
const char *
get_rttables_scope(uint32_t id)
{
return get_entry(id, &rt_scopes, RT_SCOPES_FILE, rtscope_default, 255);
}