diff --git a/configure.ac b/configure.ac index db67fb0c14..916900cf7b 100644 --- a/configure.ac +++ b/configure.ac @@ -234,6 +234,12 @@ AC_ARG_WITH(run-dir, AC_ARG_WITH(tmp-dir, [AS_HELP_STRING([--with-tmp-dir=PATH_TO_TMP], [specify directory where run-time temporary files are to be located])], [TMP_DIR_SPECIFIED=Y], [TMP_DIR_SPECIFIED=N]) +AC_ARG_WITH(iproute-usr-dir, + [AS_HELP_STRING([--with-iproute-usr-dir=PATH_TO_CONFIG_FILES], [specify usr directory iproute2 uses for config files])]) +AC_ARG_WITH(iproute-etc-dir, + [AS_HELP_STRING([--with-iproute-etc-dir=PATH_TO_CONFIG_FILES], [specify etc directory iproute2 uses for config files])]) +AC_ARG_ENABLE(update-rt-addrprotos-file, + [AS_HELP_STRING([--enable-update-rt-addrprotos-file], [update iproute rt_addrprotos file if no entry for keepalived])]) AC_ARG_ENABLE(strict-config-checks, [AS_HELP_STRING([--enable-strict-config-checks], [build with strict configuration checking])]) AC_ARG_ENABLE(hardening, @@ -1732,6 +1738,15 @@ for flag in IFA_FLAGS; do fi done +dnl - Introduced in Linux 5.18 +AC_CHECK_DECLS([IFA_PROTO], [], [], [[#include ]]) +for flag in IFA_PROTO; do + AS_VAR_COPY([decl_var], [ac_cv_have_decl_$flag]) + if test ${decl_var} = yes; then + add_system_opt[${flag}] + fi +done + dnl -- RedHat backported ENCAP_IP and ENCAP_IP6 without MPLS and ILA AS_IF([test $ac_cv_have_decl_RTA_ENCAP = yes], [ @@ -2726,6 +2741,68 @@ fi AC_DEFINE_UNQUOTED([KA_TMP_DIR], [ "${KA_TMP_DIR}" ], [Location for temporary files]) AC_SUBST([KA_TMP_DIR], [${KA_TMP_DIR}]) +dnl -- iproute2 can have directories other than /usr/share/iproute2 and /etc/iproute2 configured +dnl -- for config files if options for the paths are specified. Otherwise try to find the paths +dnl -- in the ip-route man page, and if that fails, try finding the strings in the executable. +dnl -- The latter two options are not very nice, but I can't think of a better way. +dnl -- Finally, there are runtime configuration options to override these defaults. +AS_IF([test .${with_iproute_usr_dir} != . ], [iproute_usr_dir="$with_iproute_usr_dir"]) +AS_IF([test .${with_iproute_etc_dir} != . ], [iproute_etc_dir="$with_iproute_etc_dir"]) +AS_IF([test .${with_iproute_usr_dir} = . && test .${with_iproute_etc_dir} = . ], + [ + dnl -- try the man page + man ip-route | grep -q /rt_tables + AS_IF([test $? = 0], + [ + IPROUTE_DIRS=$(man ip route | tr " \t" "\n\n" | $SED -e "s/[[\.,;:?\!]]*//g" | grep "/rt_tables$" | $SED -e "s:/rt_tables$::") + set $(<<<$IPROUTE_DIRS sort -u) + second_route=$2 + set $IPROUTE_DIRS + AS_IF([test .$second_route = .], + [ + iproute_etc_dir=$1 + iproute_usr_dir= + ], + [ + iproute_usr_dir=$1 + iproute_etc_dir=$2 + ]) + ], + [ + # try finding the paths in the executable + set $(strings $(type -p ip) | grep /rt_tables | $SED -e "s:/rt_tables::") + for i in $1 $2; do + grep -q /etc <<<$i + AS_IF([test $? -eq 0], [iproute_etc_dir=$i]) + grep -q /usr <<<$i + AS_IF([test $? -eq 0], [iproute_usr_dir=$i]) + done + ]) + AS_IF([test .${iproute_etc_dir}${iproute_usr_dir} = . ], + [ + iproute_etc_dir=/etc/iproute2 + iproute_usr_dir=/usr/share/iproute2 + ]) + ]) +AS_IF([test .${iproute_etc_dir} != . ], + [ + AC_DEFINE_UNQUOTED([IPROUTE_ETC_DIR], ["$iproute_etc_dir"], [etc path to iproute2 config files]) + add_config_opt([IPROUTE_ETC_DIR=$iproute_etc_dir]) + ]) +AS_IF([test .${iproute_usr_dir} != . ], + [ + AC_DEFINE_UNQUOTED([IPROUTE_USR_DIR], ["$iproute_usr_dir"], [usr path to iproute2 config files]) + add_config_opt([IPROUTE_USR_DIR=$iproute_usr_dir]) + ]) + +AS_IF([test .${enable_update_rt_addrprotos_file} = ."yes"], + [ + AC_DEFINE([UPDATE_RT_ADDRPROTOS_FILE], [ 1 ], [update iproute2 rt_addrprotos file]) + echo You probably do not want to use this option\; it is better to create an + echo entry in /etc/iproute2/rt_addrprotos, or add keepalived.conf with a relevant + echo entry in /etc/iproute2/rt_addrprotos.d if your version of iproute2 is \>= 6.12. + ]) + dnl - Check type of rlim_t for printf() - this check needs to be late on dnl - since _FILE_OFFSET_BITS (set when using netsnmp) alters sizeof(rlim_t) SAV_CFLAGS="$CFLAGS" @@ -3454,6 +3531,11 @@ echo "init type : ${INIT_TYPE}" echo "systemd notify : ${USE_SYSTEMD_NOTIFY}" echo "Strict config checks : ${STRICT_CONFIG}" echo "Build documentation : ${HAVE_SPHINX_BUILD}" +echo "iproute usr directory : ${iproute_usr_dir}" +echo "iproute etc directory : ${iproute_etc_dir}" +if test .${enable_update_rt_addrprotos_file} = .yes; then + echo "update rt_addrprotos : Yes" +fi if test ${ENABLE_STACKTRACE} = Yes; then echo "Stacktrace support : Yes" fi diff --git a/keepalived/core/global_data.c b/keepalived/core/global_data.c index fc2a6759bc..679b2bef95 100644 --- a/keepalived/core/global_data.c +++ b/keepalived/core/global_data.c @@ -421,6 +421,15 @@ init_global_data(data_t * data, data_t *prev_global_data, bool copy_unchangeable data->snmp_rs_stats_update_interval = data->snmp_vs_stats_update_interval; #endif #endif + +#ifdef _WITH_VRRP_ +#ifdef IPROUTE_USR_DIR + if (!data->iproute_usr_dir && IPROUTE_USR_DIR[0]) + data->iproute_usr_dir = STRDUP(IPROUTE_USR_DIR); +#endif + if (!data->iproute_etc_dir && IPROUTE_ETC_DIR[0]) + data->iproute_etc_dir = STRDUP(IPROUTE_ETC_DIR); +#endif } void @@ -503,6 +512,10 @@ free_global_data(data_t **datap) FREE_CONST_PTR(data->reload_time_file); #endif FREE_CONST_PTR(data->config_directory); +#ifdef _WITH_VRRP_ + FREE_CONST_PTR(data->iproute_usr_dir); + FREE_CONST_PTR(data->iproute_etc_dir); +#endif FREE(data); *datap = NULL; @@ -924,4 +937,8 @@ dump_global_data(FILE *fp, data_t * data) #ifdef _WITH_JSON_ conf_write(fp, " json_version %u", global_data->json_version); #endif +#ifdef _WITH_VRRP_ + conf_write(fp, " iproute usr directory %s", global_data->iproute_usr_dir ? global_data->iproute_usr_dir : "(none)"); + conf_write(fp, " iproute etc directory %s", global_data->iproute_etc_dir ? global_data->iproute_etc_dir : "(none)"); +#endif } diff --git a/keepalived/core/global_parser.c b/keepalived/core/global_parser.c index 2b6e3ba64a..0efbb6bba6 100644 --- a/keepalived/core/global_parser.c +++ b/keepalived/core/global_parser.c @@ -1071,6 +1071,7 @@ vrrp_higher_prio_send_advert_handler(const vector_t *strvec) else global_data->vrrp_higher_prio_send_advert = true; } +#endif #if defined _WITH_IPTABLES_ || defined _WITH_NFTABLES_ static bool @@ -1090,6 +1091,7 @@ check_valid_iptables_ipset_nftables_name(const vector_t *strvec, unsigned entry, } #endif +#ifdef _WITH_VRRP_ #ifdef _WITH_IPTABLES_ static bool check_valid_iptables_chain_name(const vector_t *strvec, unsigned entry, const char *log_name) @@ -1275,15 +1277,16 @@ vrrp_iptables_handler(__attribute__((unused)) const vector_t *strvec) global_data->vrrp_nf_chain_priority = -1; } #endif +#endif #ifdef _WITH_NFTABLES_ -#ifdef _WITH_VRRP_ static bool check_valid_nftables_chain_name(const vector_t *strvec, unsigned entry, const char *log_name) { return check_valid_iptables_ipset_nftables_name(strvec, entry, NFT_TABLE_MAXNAMELEN, "nftables", log_name); } +#ifdef _WITH_VRRP_ static void vrrp_nftables_handler(__attribute__((unused)) const vector_t *strvec) { @@ -1376,6 +1379,8 @@ nftables_counters_handler(__attribute__((unused)) const vector_t *strvec) global_data->nf_counters = true; } #endif + +#ifdef _WITH_VRRP_ static void vrrp_version_handler(const vector_t *strvec) { @@ -2427,7 +2432,7 @@ json_version_handler(const vector_t *strvec) unsigned version = true; if (vector_size(strvec) < 2) { - report_config_error(CONFIG_GENERAL_ERROR, "%s requires version", strvec_slot(strvec, 1)); + report_config_error(CONFIG_GENERAL_ERROR, "%s requires version", strvec_slot(strvec, 0)); return; } @@ -2440,6 +2445,30 @@ json_version_handler(const vector_t *strvec) } #endif +#ifdef _WITH_VRRP_ +static void +iproute_usr_handler(const vector_t *strvec) +{ + if (vector_size(strvec) != 2) { + report_config_error(CONFIG_GENERAL_ERROR, "%s requires path", strvec_slot(strvec, 0)); + return; + } + + global_data->iproute_usr_dir = STRDUP(strvec_slot(strvec, 1)); +} + +static void +iproute_etc_handler(const vector_t *strvec) +{ + if (vector_size(strvec) != 2) { + report_config_error(CONFIG_GENERAL_ERROR, "%s requires path", strvec_slot(strvec, 0)); + return; + } + + global_data->iproute_etc_dir = STRDUP(strvec_slot(strvec, 1)); +} +#endif + void init_global_keywords(bool global_active) { @@ -2533,17 +2562,9 @@ init_global_keywords(bool global_active) #endif #endif #ifdef _WITH_NFTABLES_ -#ifdef _WITH_VRRP_ install_keyword("nftables", &vrrp_nftables_handler); install_keyword("nftables_priority", &vrrp_nftables_priority_handler); install_keyword("nftables_ifindex", &vrrp_nftables_ifindex_handler); -#endif -#ifdef _WITH_LVS_ - install_keyword("nftables_ipvs", &ipvs_nftables_handler); - install_keyword("nftables_ipvs_priority", &ipvs_nftables_priority_handler); - install_keyword("nftables_ipvs_start_fwmark", &ipvs_nftables_start_fwmark_handler); -#endif - install_keyword("nftables_counters", &nftables_counters_handler); #endif install_keyword("vrrp_check_unicast_src", &vrrp_check_unicast_src_handler); install_keyword("vrrp_skip_check_adv_addr", &vrrp_check_adv_addr_handler); @@ -2554,6 +2575,16 @@ init_global_keywords(bool global_active) install_keyword("vrrp_cpu_affinity", &vrrp_cpu_affinity_handler); install_keyword("vrrp_rlimit_rttime", &vrrp_rt_rlimit_handler); install_keyword("vrrp_rlimit_rtime", &vrrp_rt_rlimit_handler); /* Deprecated 02/02/2020 */ +#endif +#ifdef _WITH_NFTABLES_ +#ifdef _WITH_LVS_ + install_keyword("nftables_ipvs", &ipvs_nftables_handler); + install_keyword("nftables_ipvs_priority", &ipvs_nftables_priority_handler); + install_keyword("nftables_ipvs_start_fwmark", &ipvs_nftables_start_fwmark_handler); +#endif +#if defined _WITH_VRRP_ || defined _WITH_LVS_ + install_keyword("nftables_counters", &nftables_counters_handler); +#endif #endif install_keyword("notify_fifo", &global_notify_fifo); install_keyword_quoted("notify_fifo_script", &global_notify_fifo_script); @@ -2653,4 +2684,8 @@ init_global_keywords(bool global_active) #ifdef _WITH_JSON_ install_keyword("json_version", &json_version_handler); #endif +#ifdef _WITH_VRRP_ + install_keyword("iproute_usr_dir", &iproute_usr_handler); + install_keyword("iproute_etc_dir", &iproute_etc_handler); +#endif } diff --git a/keepalived/include/global_data.h b/keepalived/include/global_data.h index 72701e051b..7024eb6a3f 100644 --- a/keepalived/include/global_data.h +++ b/keepalived/include/global_data.h @@ -294,6 +294,10 @@ typedef struct _data { #ifdef _WITH_JSON_ unsigned json_version; #endif +#ifdef _WITH_VRRP_ + const char *iproute_usr_dir; + const char *iproute_etc_dir; +#endif } data_t; /* Global vars exported */ diff --git a/keepalived/include/vrrp_ipaddress.h b/keepalived/include/vrrp_ipaddress.h index a0b65ea14e..e4cba44d58 100644 --- a/keepalived/include/vrrp_ipaddress.h +++ b/keepalived/include/vrrp_ipaddress.h @@ -117,5 +117,6 @@ extern void get_diff_address(vrrp_t *, vrrp_t *, list_head_t *); extern void clear_address_list(list_head_t *, bool); extern void clear_diff_static_addresses(void); extern void reinstate_static_address(ip_address_t *); +extern void set_addrproto(void); #endif diff --git a/keepalived/vrrp/vrrp_daemon.c b/keepalived/vrrp/vrrp_daemon.c index 1f0fd69287..c47b0822a8 100644 --- a/keepalived/vrrp/vrrp_daemon.c +++ b/keepalived/vrrp/vrrp_daemon.c @@ -318,6 +318,10 @@ vrrp_terminate_phase2(int exit_status) clear_rt_names(); +#if HAVE_DECL_IFA_PROTO && defined UPDATE_RT_ADDRPROTOS_FILE + remove_created_addrprotos_file(); +#endif + if (global_data->vrrp_notify_fifo.fd != -1) notify_fifo_close(&global_data->notify_fifo, &global_data->vrrp_notify_fifo); @@ -1126,6 +1130,9 @@ start_vrrp_child(void) systemd_unset_notify(); #endif + /* Set the protocol for ip addresses we add */ + set_addrproto(); + #ifdef _VRRP_FD_DEBUG_ if (do_vrrp_fd_debug) set_extra_threads_debug(dump_vrrp_fd); diff --git a/keepalived/vrrp/vrrp_ipaddress.c b/keepalived/vrrp/vrrp_ipaddress.c index 116fca63dc..249b25b4fb 100644 --- a/keepalived/vrrp/vrrp_ipaddress.c +++ b/keepalived/vrrp/vrrp_ipaddress.c @@ -46,6 +46,10 @@ #define INFINITY_LIFE_TIME 0xFFFFFFFF +#if HAVE_DECL_IFA_PROTO +static uint8_t address_protocol; +#endif + const char * ipaddresstos(char *buf, const ip_address_t *ip_addr) { @@ -212,6 +216,10 @@ netlink_ipaddress(ip_address_t *ip_addr, int cmd) if (ip_addr->have_peer) addattr_l(&req.n, sizeof(req), IFA_ADDRESS, &ip_addr->peer, req.ifa.ifa_family == AF_INET6 ? 16 : 4); + +#if HAVE_DECL_IFA_PROTO // introduced in Linux v5.18 + addattr8(&req.n, sizeof(req), IFA_PROTO, address_protocol); +#endif } /* If the state of the interface or its parent is down, it might be because the interface @@ -838,3 +846,12 @@ void reinstate_static_address(ip_address_t *ip_addr) format_ipaddress(ip_addr, buf, sizeof(buf)); log_message(LOG_INFO, "Restoring deleted static address %s", buf); } + +void +set_addrproto(void) +{ +#if HAVE_DECL_IFA_PROTO + if (!find_rttables_addrproto("keepalived", &address_protocol)) + create_rttables_addrproto("keepalived", &address_protocol); +#endif +} diff --git a/lib/rttables.c b/lib/rttables.c index e90cdd6913..1fbcac3414 100644 --- a/lib/rttables.c +++ b/lib/rttables.c @@ -18,8 +18,46 @@ * 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, + * Copyright (C) 2001-2024 Alexandre Cassen, */ + +/* + iproute has been moving the location of its config files around recently, + and not so recently. + + Version Commit Date Change + 3.3 fb72129 01/03/12 use CONFDIR for path for config files - edit Makefile to change + 4.1 06ec903 13/04/15 Allow CONFDIR to be set in environment to make (default /etc/iproute2) + 4.4 13ada95 24/11/15 add support for rt_tables.d + 4.10 719e331 09/01/17 add support for rt_protos.d + 5.15 cee0cf8 14/10/21 adds --libdir option to configure + 6.3 bdb8d85 27/03/23 add support for IFA_PROT + 6.5 0a0a8f1 26/07/23 read from /usr/lib/iproute2/FOO unless /etc/iproute2/FOO exists - both specifiable to make + 6.6 946753a 15/09/23 ensure CONF_USR_DIR honours configure lib path - uses $(LIBDIR) + deb66ac 06/11/23 revert 946753a4 + 6.7 9626923 15/11/23 change using /usr/lib/iproute2 to /usr/share/iproute2 + 6.12 b43f84a 14/10/24 add rt_addrprotos.d subdirectories + + Debian, Ubuntu, RHEL and openSUSE moved from /etc/iproute2 to /usr/share/iproute2 + Mint, Gentoo and Archlinux currently use /etc/iproute2 + Alpine by default uses busybox which doesn't support these files + If iproute2 is installed it uses /usr/share/iproute2 + Fedora is potentially a problem. Up to Fedora 39 it used /etc/iproute2. + The initial version of iproute2 in Fedora 40 was v6.5 and it used + /usr/lib/iproute2 or /usr/lib64/iproute2. When iproute2 was upgraded + to v6.7 it moved to using /usr/share/iproute2. + Fedora 41 uses /usr/share/iproute2. + Since Fedora 40 upgraded to iproute2 v6.7 in February 2024, it is reasonable + to assume that if Fedora 40 upgrades to this version of keepalived, i.e. + November 2024 or later, that iproute2 will have already been upgraded to v6.7 + (or later). We therefore do not need to support /usr/lib{,64}/iproute2. + + I have been unable to find any distro that uses CONFDIR/CONF_{USR,ETC}_DIR or --libdir, + but if there is one, they should set --with-iproute-usr-dir and --with-iproute-etc-dir + configure options for keepalived (if the man pages for iproute2 or /usr/bin/ip are + installed in the build environment, configure should be able to work out the paths itself). +*/ + #include "config.h" #include @@ -27,6 +65,15 @@ #include #include #include +#include +#include +#include +#if HAVE_DECL_IFA_PROTO && defined UPDATE_RT_ADDRPROTOS_FILE +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif #include "list_head.h" #include "memory.h" @@ -34,16 +81,22 @@ #include "parser.h" #include "rttables.h" -#define IPROUTE2_DIR "/etc/iproute2/" -#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 !defined IPROUTE_USR_DIR && !defined IPROUTE_ETC_DIR +#define IPROUTE_ETC_DIR "/etc/iproute2" +#endif + +#define RT_TABLES_FILE "rt_tables" +#define RT_DSFIELD_FILE "rt_dsfield" +#define RT_REALMS_FILE "rt_realms" +#define RT_PROTOS_FILE "rt_protos" #if HAVE_DECL_FRA_SUPPRESS_IFGROUP -#define RT_GROUPS_FILE IPROUTE2_DIR "group" +#define RT_GROUPS_FILE "group" +#endif +#define RT_SCOPES_FILE "rt_scopes" +#if HAVE_DECL_IFA_PROTO +#define RT_ADDRPROTOS_FILE "rt_addrprotos" #endif -#define RT_SCOPES_FILE IPROUTE2_DIR "rt_scopes" typedef struct _rt_entry { unsigned int id; @@ -107,6 +160,16 @@ static rt_entry_t const rtscope_default[] = { { 0, NULL, {0}}, }; +#if HAVE_DECL_IFA_PROTO +static rt_entry_t const rtaddrproto_default[] = { + { IFAPROT_UNSPEC, "unspecified", {0}}, + { IFAPROT_KERNEL_LO, "kernel_lo", {0}}, + { IFAPROT_KERNEL_RA, "kernel_ra", {0}}, + { IFAPROT_KERNEL_LL, "kernel_ll", {0}}, + { 0, NULL, {0}}, +}; +#endif + #define MAX_RT_BUF 128 static LIST_HEAD_INITIALIZE(rt_tables); @@ -116,10 +179,19 @@ static LIST_HEAD_INITIALIZE(rt_groups); #endif static LIST_HEAD_INITIALIZE(rt_realms); static LIST_HEAD_INITIALIZE(rt_protos); +#if HAVE_DECL_IFA_PROTO +static LIST_HEAD_INITIALIZE(rt_addrprotos); +#endif static LIST_HEAD_INITIALIZE(rt_scopes); static char ret_buf[11]; /* uint32_t in decimal */ +#if HAVE_DECL_IFA_PROTO && defined UPDATE_RT_ADDRPROTOS_FILE +static const char *created_addrprotos_file; +static const char *created_addrprotos_dir; +bool updated_addrprotos_file; +#endif + static void free_rt_entry(rt_entry_t *rte) { @@ -128,6 +200,7 @@ free_rt_entry(rt_entry_t *rte) FREE_CONST(rte->name); FREE(rte); } + static void free_rt_entry_list(list_head_t *l) { @@ -163,6 +236,9 @@ clear_rt_names(void) #endif free_rt_entry_list(&rt_realms); free_rt_entry_list(&rt_protos); +#if HAVE_DECL_IFA_PROTO + free_rt_entry_list(&rt_addrprotos); +#endif free_rt_entry_list(&rt_scopes); } @@ -181,7 +257,7 @@ read_file(const char *file_name, list_head_t *l, uint32_t max) if (!fp) return; - while (fgets(buf, MAX_RT_BUF, fp)) { + while (fgets(buf, sizeof(buf), fp)) { /* Remove comments */ if ((endptr = strchr(buf, '#'))) *endptr = '\0'; @@ -265,14 +341,103 @@ add_default(list_head_t *l, const rt_entry_t *default_list) } } +static bool +wanted_file(char *file_path, const char *path, const char *dir, const char *name) +{ + struct stat statbuf; + size_t len; + + /* Skip hidden files and . and .. */ + if (name[0] == '.') + return false; + + /* We only want filenames ending '.conf' */ + len = strlen(name); + if (len <= 5 || strcmp(name + len - 5, ".conf")) + return false; + + /* Ensure what we have is a regular file */ + snprintf(file_path, PATH_MAX, "%s/%s.d/%s", path, dir, name); + if (stat(file_path, &statbuf) || (statbuf.st_mode & S_IFMT) != S_IFREG) + return false; + + return true; +} + static void initialise_list(list_head_t *l, const char *file_name, const rt_entry_t *default_list, uint32_t max) { + char *path; +#ifdef IPROUTE_USR_DIR + char *etc_path; +#endif + struct stat statbuf; + struct dirent *ent; + DIR *dir; if (!list_empty(l)) return; - read_file(file_name, l, max); + path = MALLOC(PATH_MAX); +#ifdef IPROUTE_USR_DIR + etc_path = MALLOC(PATH_MAX); +#endif + + /* The default location is IPROUTE_USR_DIR, but it is overridden + * if the file exists in IPROUTE_USR_DIR. */ + snprintf(path, PATH_MAX, "%s/%s", IPROUTE_ETC_DIR, file_name); + if (!stat(path, &statbuf) && (statbuf.st_mode & S_IFMT) == S_IFREG) + read_file(path, l, max); +#ifdef IPROUTE_USR_DIR + else { + snprintf(path, PATH_MAX, "%s/%s", IPROUTE_USR_DIR, file_name); + if (!stat(path, &statbuf) && (statbuf.st_mode & S_IFMT) == S_IFREG) + read_file(path, l, max); + } +#endif + + /* iproute2 uses subdirectories for rt_protos, rt_addrprotos, rt_tables + * (and protodown_reasons) as at v6.11. + * To futureproof our code, we will read subdirectories for all files, + * in case iproute2 introduces support for them in the future. + * We need to check all files ending .conf under IPROUTE_USR_DIR and read + * them unless the matching file exists under IPROUTE_ETC_DIR. We then read + * all relevant files under IPROUTE_ETC_DIR. */ +#ifdef IPROUTE_USR_DIR + snprintf(path, PATH_MAX, "%s/%s.d", IPROUTE_USR_DIR, file_name); + if ((dir = opendir(path))) { + while ((ent = readdir(dir))) { + if (!wanted_file(path, IPROUTE_USR_DIR, file_name, ent->d_name)) + continue; + + /* Check if the file exists in IPROUTE_ETC_DIR. We just check if there is a matching + * entry, and don't care what type the entry is */ + snprintf(etc_path, PATH_MAX, "%s/%s.d/%s", IPROUTE_ETC_DIR, file_name, ent->d_name); + if (!stat(etc_path, &statbuf)) + continue; + + read_file(path, l, max); + } + } + closedir(dir); +#endif + + /* Now read the entries in the IPROUTE_ETC_DIR subdirectory */ + snprintf(path, PATH_MAX, "%s/%s.d", IPROUTE_ETC_DIR, file_name); + if ((dir = opendir(path))) { + while ((ent = readdir(dir))) { + if (!wanted_file(path, IPROUTE_ETC_DIR, file_name, ent->d_name)) + continue; + + read_file(path, l, max); + } + } + closedir(dir); + + FREE_PTR(path); +#ifdef IPROUTE_USR_DIR + FREE_PTR(etc_path); +#endif if (default_list) add_default(l, default_list); @@ -285,6 +450,7 @@ find_entry(const char *name, unsigned int *id, list_head_t *l, const char* file_ unsigned long l_id; rt_entry_t *rte; + /* If the name is numeric, return its value */ l_id = strtoul(name, &endptr, 0); *id = (unsigned int)l_id; if (endptr != name && *endptr == '\0') @@ -423,3 +589,155 @@ get_rttables_scope(uint32_t id) { return get_entry(id, &rt_scopes, RT_SCOPES_FILE, rtscope_default, 255); } + +#if HAVE_DECL_IFA_PROTO +static const char * +get_rttables_addrproto(uint32_t id) +{ + return get_entry(id, &rt_scopes, RT_ADDRPROTOS_FILE, rtscope_default, 255); +} + +#ifdef UPDATE_RT_ADDRPROTOS_FILE +static void +write_addrproto_config(const char *name, uint32_t val) +{ + char buf[256]; + FILE *fp; + char *v, *e; + int ver_maj, ver_min, ver_rel; + char *res; + const char *path, *dir = NULL; + bool file_exists = false; + struct stat statbuf; + + fp = popen("ip -V", "re"); + res = fgets(buf, sizeof(buf), fp); + pclose(fp); + + if (!res) + return; + + /* Format is: + * ip utility, iproute2-5.10.0 + * or + * ip utility, iproute2-6.7.0, libbpf 1.2.3 + */ + if (!(v = strchr(buf, '-'))) + return; + + v++; + if ((e = strchr(v, ','))) + *e = '\0'; + sscanf(v, "%d.%d.%d", &ver_maj, &ver_min, &ver_rel); + if (ver_maj >= 7 || (ver_maj == 6 && ver_min >= 12)) { + dir = IPROUTE_ETC_DIR "/" RT_ADDRPROTOS_FILE ".d"; + path = IPROUTE_ETC_DIR "/" RT_ADDRPROTOS_FILE ".d/keepalived_private.conf" ; + } else if (ver_maj == 6 && ver_min >= 3) { + path = IPROUTE_ETC_DIR "/" RT_ADDRPROTOS_FILE; + } else + return; + + stat(IPROUTE_ETC_DIR, &statbuf); + if (dir) { + if (!mkdir(dir, statbuf.st_mode & ~S_IFMT)) { // This may fail if the directory already exists + created_addrprotos_dir = dir; + chmod(dir, statbuf.st_mode & ~S_IFMT); + } + } else { + /* Check if rt_addrprotos file exists */ + file_exists = !stat(path, &statbuf); + } + + if (!(fp = fopen(path, "a"))) + return; + + if (!file_exists) + chmod(path, statbuf.st_mode & ~S_IFMT & ~(S_IXUSR | S_IXGRP | S_IXOTH)); + + if (dir || !file_exists) { + fputs("# File created by keepalived - feel free to remove it\n", fp); + fprintf(fp, "%u\t%s\n", val, name); + } else + fprintf(fp, "%u\t%s\t# entry added by keepalived - feel free to remove it\n", val, name); + + fclose(fp); + + if (!file_exists) + created_addrprotos_file = path; + else + updated_addrprotos_file = true; +} +#endif + +bool +create_rttables_addrproto(const char *name, uint8_t *id) +{ + unsigned val; + rt_entry_t *rte; + + /* We need to find a free value - try RTPROT_KEEPALIVED first */ + val = RTPROT_KEEPALIVED; + if (!get_rttables_addrproto(val)) { + for (val = 0; val <= 255; val++) { + if (get_rttables_addrproto(val)) + break; + } + } + + if (val > 255) + return false; + + *id = val & 0xff; + + /* Add the entry so other configuration can use it */ + PMALLOC(rte); + if (!rte) + return false; + + rte->id = val; + rte->name = STRDUP(name); + if (!rte->name) { + FREE(rte); + return false; + } + + list_add_tail(&rte->e_list, &rt_addrprotos); + +#ifdef UPDATE_RT_ADDRPROTOS_FILE + /* Save the entry so iproute can use it */ + write_addrproto_config(name, *id); +#endif + + return true; +} + +#ifdef UPDATE_RT_ADDRPROTOS_FILE +void +remove_created_addrprotos_file(void) +{ + if (created_addrprotos_file) { + unlink(created_addrprotos_file); + + if (created_addrprotos_dir) + rmdir(created_addrprotos_dir); + } else if (updated_addrprotos_file) { + if (system("sed -i -e '/keepalived/d' " IPROUTE_ETC_DIR "/" RT_ADDRPROTOS_FILE)) { + /* Dummy to aviod unused result warning */ + } + } +} +#endif + +bool +find_rttables_addrproto(const char *name, uint8_t *id) +{ + uint32_t val; + + if (!find_entry(name, &val, &rt_addrprotos, RT_ADDRPROTOS_FILE, rtaddrproto_default, 255)) + return false; + + *id = val & 0xff; + + return true; +} +#endif diff --git a/lib/rttables.h b/lib/rttables.h index 5b97514e3e..981b49bc16 100644 --- a/lib/rttables.h +++ b/lib/rttables.h @@ -35,6 +35,10 @@ extern bool find_rttables_realms(const char *, uint32_t *); extern bool find_rttables_group(const char *, uint32_t *); #endif extern bool find_rttables_proto(const char *, uint8_t *); +#if HAVE_DECL_IFA_PROTO +extern bool create_rttables_addrproto(const char *, uint8_t *); +extern bool find_rttables_addrproto(const char *, uint8_t *); +#endif extern bool find_rttables_rtntype(const char *, uint8_t *); extern bool find_rttables_scope(const char *, uint8_t *); @@ -43,5 +47,8 @@ extern const char *get_rttables_scope(uint32_t); extern const char *get_rttables_group(uint32_t); #endif extern const char *get_rttables_rtntype(uint8_t); +#if HAVE_DECL_IFA_PROTO && defined UPDATE_RT_ADDRPROTOS_FILE +extern void remove_created_addrprotos_file(void); +#endif #endif